整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          「Access示例」獲取網(wǎng)頁(yè)的Html代碼

          要代碼:

          Dim doc, objhtml As Object

          Dim i As Integer

          Dim strhtml As String

          If Not Me.WebBrowser1.Busy Then

          Set doc = WebBrowser1.Document

          i = 0

          Set objhtml = doc.body.createtextrange()

          If Not IsNull(objhtml) Then

          Text1 = objhtml.htmltext

          End If

          End If

          ?

          示例下載:( 在“了解更多”里下載)

          圖 示:

          在我們已經(jīng)完成了后端,讓我們轉(zhuǎn)到前端。 我將采用單頁(yè)應(yīng)用程序方案。

          ? 來(lái)源:linux.cn ? 作者:Nicolás Parada ? 譯者:XianLei Gao ?

          (本文字?jǐn)?shù):11404,閱讀時(shí)長(zhǎng)大約:12 分鐘)

          本文是該系列的第七篇。

          • 第一篇: 模式
          • 第二篇: OAuth
          • 第三篇: 對(duì)話
          • 第四篇: 消息
          • 第五篇: 實(shí)時(shí)消息
          • 第六篇: 僅用于開(kāi)發(fā)的登錄

          現(xiàn)在我們已經(jīng)完成了后端,讓我們轉(zhuǎn)到前端。 我將采用單頁(yè)應(yīng)用程序方案。

          首先,我們創(chuàng)建一個(gè) static/index.html 文件,內(nèi)容如下。

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="utf-8">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <title>Messenger</title>
              <link rel="shortcut icon" href="data:,">
              <link rel="stylesheet" href="/styles.css">
              <script src="/main.js" type="module"></script>
          </head>
          <body></body>
          </html>
          

          這個(gè) HTML 文件必須為每個(gè) URL 提供服務(wù),并且使用 JavaScript 負(fù)責(zé)呈現(xiàn)正確的頁(yè)面。

          因此,讓我們將注意力轉(zhuǎn)到 main.go 片刻,然后在 main() 函數(shù)中添加以下路由:

          router.Handle("GET", "/...", http.FileServer(SPAFileSystem{http.Dir("static")}))
          
          type SPAFileSystem struct {
              fs http.FileSystem
          }
          
          func (spa SPAFileSystem) Open(name string) (http.File, error) {
              f, err := spa.fs.Open(name)
              if err != nil {
                  return spa.fs.Open("index.html")
              }
              return f, nil
          }
          

          我們使用一個(gè)自定義的文件系統(tǒng),因此它不是為未知的 URL 返回 404 Not Found,而是轉(zhuǎn)到 index.html。

          路由器

          在 index.html 中我們加載了兩個(gè)文件:styles.css 和 main.js。我把樣式留給你自由發(fā)揮。

          讓我們移動(dòng)到 main.js。 創(chuàng)建一個(gè)包含以下內(nèi)容的 static/main.js 文件:

          import { guard } from './auth.js'
          import Router from './router.js'
          
          let currentPage
          const disconnect = new CustomEvent('disconnect')
          const router = new Router()
          
          router.handle('/', guard(view('home'), view('access')))
          router.handle('/callback', view('callback'))
          router.handle(/^\/conversations\/([^\/]+)$/, guard(view('conversation'), view('access')))
          router.handle(/^\//, view('not-found'))
          
          router.install(async result => {
              document.body.innerHTML = ''
              if (currentPage instanceof Node) {
                  currentPage.dispatchEvent(disconnect)
              }
              currentPage = await result
              if (currentPage instanceof Node) {
                  document.body.appendChild(currentPage)
              }
          })
          
          function view(pageName) {
              return (...args) => import(`/pages/${pageName}-page.js`)
                  .then(m => m.default(...args))
          }
          

          如果你是這個(gè)博客的關(guān)注者,你已經(jīng)知道它是如何工作的了。 該路由器就是在 這里 顯示的那個(gè)。 只需從 @nicolasparada/router 下載并保存到 static/router.js 即可。

          我們注冊(cè)了四條路由。 在根路由 / 處,我們展示 home 或 access 頁(yè)面,無(wú)論用戶是否通過(guò)身份驗(yàn)證。 在 /callback 中,我們展示 callback 頁(yè)面。 在 /conversations/{conversationID} 上,我們展示對(duì)話或 access 頁(yè)面,無(wú)論用戶是否通過(guò)驗(yàn)證,對(duì)于其他 URL,我們展示一個(gè) not-found 頁(yè)面。

          我們告訴路由器將結(jié)果渲染為文檔主體,并在離開(kāi)之前向每個(gè)頁(yè)面調(diào)度一個(gè) disconnect 事件。

          我們將每個(gè)頁(yè)面放在不同的文件中,并使用新的動(dòng)態(tài) import() 函數(shù)導(dǎo)入它們。

          身份驗(yàn)證

          guard() 是一個(gè)函數(shù),給它兩個(gè)函數(shù)作為參數(shù),如果用戶通過(guò)了身份驗(yàn)證,則執(zhí)行第一個(gè)函數(shù),否則執(zhí)行第二個(gè)。它來(lái)自 auth.js,所以我們創(chuàng)建一個(gè)包含以下內(nèi)容的 static/auth.js 文件:

          export function isAuthenticated() {
              const token = localStorage.getItem('token')
              const expiresAtItem = localStorage.getItem('expires_at')
              if (token === null || expiresAtItem === null) {
                  return false
              }
          
              const expiresAt = new Date(expiresAtItem)
              if (isNaN(expiresAt.valueOf()) || expiresAt <= new Date()) {
                  return false
              }
          
              return true
          }
          
          export function guard(fn1, fn2) {
              return (...args) => isAuthenticated()
                  ? fn1(...args)
                  : fn2(...args)
          }
          
          export function getAuthUser() {
              if (!isAuthenticated()) {
                  return null
              }
          
              const authUser = localStorage.getItem('auth_user')
              if (authUser === null) {
                  return null
              }
          
              try {
                  return JSON.parse(authUser)
              } catch (_) {
                  return null
              }
          }
          

          isAuthenticated() 檢查 localStorage 中的 token 和 expires_at,以判斷用戶是否已通過(guò)身份驗(yàn)證。getAuthUser() 從 localStorage 中獲取經(jīng)過(guò)身份驗(yàn)證的用戶。

          當(dāng)我們登錄時(shí),我們會(huì)將所有的數(shù)據(jù)保存到 localStorage,這樣才有意義。

          Access 頁(yè)面

          access page screenshot


          讓我們從 access 頁(yè)面開(kāi)始。 創(chuàng)建一個(gè)包含以下內(nèi)容的文件 static/pages/access-page.js:

          const template = document.createElement('template')
          template.innerHTML = `
              <h1>Messenger</h1>
              <a href="/api/oauth/github" onclick="event.stopPropagation()">Access with GitHub</a>
          `
          
          export default function accessPage() {
              return template.content
          }
          

          因?yàn)槁酚善鲿?huì)攔截所有鏈接點(diǎn)擊來(lái)進(jìn)行導(dǎo)航,所以我們必須特別阻止此鏈接的事件傳播。

          單擊該鏈接會(huì)將我們重定向到后端,然后重定向到 GitHub,再重定向到后端,然后再次重定向到前端; 到 callback 頁(yè)面。

          Callback 頁(yè)面

          創(chuàng)建包括以下內(nèi)容的 static/pages/callback-page.js 文件:

          import http from '../http.js'
          import { navigate } from '../router.js'
          
          export default async function callbackPage() {
              const url = new URL(location.toString())
              const token = url.searchParams.get('token')
              const expiresAt = url.searchParams.get('expires_at')
          
              try {
                  if (token === null || expiresAt === null) {
                      throw new Error('Invalid URL')
                  }
          
                  const authUser = await getAuthUser(token)
          
                  localStorage.setItem('auth_user', JSON.stringify(authUser))
                  localStorage.setItem('token', token)
                  localStorage.setItem('expires_at', expiresAt)
              } catch (err) {
                  alert(err.message)
              } finally {
                  navigate('/', true)
              }
          }
          
          function getAuthUser(token) {
              return http.get('/api/auth_user', { authorization: `Bearer ${token}` })
          }
          

          callback 頁(yè)面不呈現(xiàn)任何內(nèi)容。這是一個(gè)異步函數(shù),它使用 URL 查詢字符串中的 token 向 /api/auth_user 發(fā)出 GET 請(qǐng)求,并將所有數(shù)據(jù)保存到 localStorage。 然后重定向到 /。

          HTTP

          這里是一個(gè) HTTP 模塊。 創(chuàng)建一個(gè)包含以下內(nèi)容的 static/http.js 文件:

          import { isAuthenticated } from './auth.js'
          
          async function handleResponse(res) {
              const body = await res.clone().json().catch(() => res.text())
          
              if (res.status === 401) {
                  localStorage.removeItem('auth_user')
                  localStorage.removeItem('token')
                  localStorage.removeItem('expires_at')
              }
          
              if (!res.ok) {
                  const message = typeof body === 'object' && body !== null && 'message' in body
                      ? body.message
                      : typeof body === 'string' && body !== ''
                          ? body
                          : res.statusText
                  throw Object.assign(new Error(message), {
                      url: res.url,
                      statusCode: res.status,
                      statusText: res.statusText,
                      headers: res.headers,
                      body,
                  })
              }
          
              return body
          }
          
          function getAuthHeader() {
              return isAuthenticated()
                  ? { authorization: `Bearer ${localStorage.getItem('token')}` }
                  : {}
          }
          
          export default {
              get(url, headers) {
                  return fetch(url, {
                      headers: Object.assign(getAuthHeader(), headers),
                  }).then(handleResponse)
              },
          
              post(url, body, headers) {
                  const init = {
                      method: 'POST',
                      headers: getAuthHeader(),
                  }
                  if (typeof body === 'object' && body !== null) {
                      init.body = JSON.stringify(body)
                      init.headers['content-type'] = 'application/json; charset=utf-8'
                  }
                  Object.assign(init.headers, headers)
                  return fetch(url, init).then(handleResponse)
              },
          
              subscribe(url, callback) {
                  const urlWithToken = new URL(url, location.origin)
                  if (isAuthenticated()) {
                      urlWithToken.searchParams.set('token', localStorage.getItem('token'))
                  }
                  const eventSource = new EventSource(urlWithToken.toString())
                  eventSource.onmessage = ev => {
                      let data
                      try {
                          data = JSON.parse(ev.data)
                      } catch (err) {
                          console.error('could not parse message data as JSON:', err)
                          return
                      }
                      callback(data)
                  }
                  const unsubscribe = () => {
                      eventSource.close()
                  }
                  return unsubscribe
              },
          }
          

          這個(gè)模塊是 fetch 和 EventSource API 的包裝器。最重要的部分是它將 JSON web 令牌添加到請(qǐng)求中。

          Home 頁(yè)面

          home page screenshot


          因此,當(dāng)用戶登錄時(shí),將顯示 home 頁(yè)。 創(chuàng)建一個(gè)具有以下內(nèi)容的 static/pages/home-page.js 文件:

          import { getAuthUser } from '../auth.js'
          import { avatar } from '../shared.js'
          
          export default function homePage() {
              const authUser = getAuthUser()
              const template = document.createElement('template')
              template.innerHTML = `
                  <div>
                      <div>
                          ${avatar(authUser)}
                          <span>${authUser.username}</span>
                      </div>
                      <button id="logout-button">Logout</button>
                  </div>
                  <!-- conversation form here -->
                  <!-- conversation list here -->
              `
              const page = template.content
              page.getElementById('logout-button').onclick = onLogoutClick
              return page
          }
          
          function onLogoutClick() {
              localStorage.clear()
              location.reload()
          }
          

          對(duì)于這篇文章,這是我們?cè)?home 頁(yè)上呈現(xiàn)的唯一內(nèi)容。我們顯示當(dāng)前經(jīng)過(guò)身份驗(yàn)證的用戶和注銷按鈕。

          當(dāng)用戶單擊注銷時(shí),我們清除 localStorage 中的所有內(nèi)容并重新加載頁(yè)面。

          Avatar

          那個(gè) avatar() 函數(shù)用于顯示用戶的頭像。 由于已在多個(gè)地方使用,因此我將它移到 shared.js 文件中。 創(chuàng)建具有以下內(nèi)容的文件 static/shared.js:

          export function avatar(user) {
              return user.avatarUrl === null
                  ? `<figure class="avatar" data-initial="${user.username[0]}"></figure>`
                  : `<img class="avatar" src="${user.avatarUrl}" alt="${user.username}'s avatar">`
          }
          

          如果頭像網(wǎng)址為 null,我們將使用用戶的姓名首字母作為初始頭像。

          你可以使用 attr() 函數(shù)顯示帶有少量 CSS 樣式的首字母。

          .avatar[data-initial]::after {
              content: attr(data-initial);
          }
          

          僅開(kāi)發(fā)使用的登錄

          access page with login form screenshot


          在上一篇文章中,我們?yōu)榫帉懥艘粋€(gè)登錄代碼。讓我們?cè)?access 頁(yè)面中為此添加一個(gè)表單。 進(jìn)入 static/ages/access-page.js,稍微修改一下。

          import http from '../http.js'
          
          const template = document.createElement('template')
          template.innerHTML = `
              <h1>Messenger</h1>
              <form id="login-form">
                  <input type="text" placeholder="Username" required>
                  <button>Login</button>
              </form>
              <a href="/api/oauth/github" onclick="event.stopPropagation()">Access with GitHub</a>
          `
          
          export default function accessPage() {
              const page = template.content.cloneNode(true)
              page.getElementById('login-form').onsubmit = onLoginSubmit
              return page
          }
          
          async function onLoginSubmit(ev) {
              ev.preventDefault()
          
              const form = ev.currentTarget
              const input = form.querySelector('input')
              const submitButton = form.querySelector('button')
          
              input.disabled = true
              submitButton.disabled = true
          
              try {
                  const payload = await login(input.value)
                  input.value = ''
          
                  localStorage.setItem('auth_user', JSON.stringify(payload.authUser))
                  localStorage.setItem('token', payload.token)
                  localStorage.setItem('expires_at', payload.expiresAt)
          
                  location.reload()
              } catch (err) {
                  alert(err.message)
                  setTimeout(() => {
                      input.focus()
                  }, 0)
              } finally {
                  input.disabled = false
                  submitButton.disabled = false
              }
          }
          
          function login(username) {
              return http.post('/api/login', { username })
          }
          

          我添加了一個(gè)登錄表單。當(dāng)用戶提交表單時(shí)。它使用用戶名對(duì) /api/login 進(jìn)行 POST 請(qǐng)求。將所有數(shù)據(jù)保存到 localStorage 并重新加載頁(yè)面。

          記住在前端完成后刪除此表單。


          這就是這篇文章的全部?jī)?nèi)容。在下一篇文章中,我們將繼續(xù)使用主頁(yè)添加一個(gè)表單來(lái)開(kāi)始對(duì)話,并顯示包含最新對(duì)話的列表。

          • 源代碼

          via: nicolasparada.netlify.com

          作者: Nicolás Parada 選題: lujun9972 譯者: gxlct008 校對(duì): wxy

          本文由 LCTT 原創(chuàng)編譯, Linux中國(guó) 榮譽(yù)推出

          點(diǎn)擊“了解更多”可訪問(wèn)文內(nèi)鏈接

          ccess窗體間的數(shù)據(jù)是如何傳遞的?前面有發(fā)過(guò)子窗體的數(shù)據(jù)傳遞。今天講解3種方法傳遞窗體里的數(shù)據(jù):

          如有窗體“傳遞窗體”和“接收窗體”兩個(gè)窗體,通過(guò)“傳遞窗體”控件的值傳給“接收窗體”的控件。

          方法一:

          打開(kāi)窗體后,直接用打開(kāi)的窗體的控件值等于當(dāng)前窗體的控件值

          Forms("接收窗體").控件= Me.控件

          方法二:

          通過(guò)openform方法的最后一個(gè)參數(shù)openargs將值傳遞過(guò)去。

          openform最后參數(shù)等于文本框的值,打開(kāi)“接收窗體”的open事件:控件值= Me.OpenArgs

          方法三:

          用全局變量。在模塊定義一個(gè)全局變量,這里是“Public strName As String”

          再將“傳遞窗體”的控件值賦給strName . 然后在窗體2的加載事件中將strName 賦值給“接收窗體”的控件。

          傳遞窗體代碼:

          Private Sub Command6_Click()
           DoCmd.OpenForm "接收窗體", , , , , , Me.Text2
           Forms("接收窗體").Text0.Value = Me.Text0
           strName = Me.Text4
          End Sub

          接收窗體代碼:

          Private Sub Form_Load()
           Me.Text8 = strName
          End Sub
          Private Sub Form_Open(Cancel As Integer)
           Me.Text6 = Me.OpenArgs
          End Sub

          模塊代碼

          Public strName As String

          其中“傳遞窗體”的文本框值分別為:“Text0”,“Text2”,“Text4”

          其中“接收窗體”的文本框值分別為:“Text0”,“Text6”,“Text8”

          詳細(xì)源碼:http://www.office-cn.net/access-interface/399.html


          主站蜘蛛池模板: 日韩人妻无码一区二区三区综合部 | 精品视频一区二区三区在线观看| 在线日产精品一区| 国产在线不卡一区二区三区| 麻豆视传媒一区二区三区| 国产精品亚洲专区一区| 精品国产日韩亚洲一区在线| 最美女人体内射精一区二区| 秋霞无码一区二区| 久久精品岛国av一区二区无码| 日韩毛片基地一区二区三区| 成人免费视频一区二区| 亚洲熟妇AV一区二区三区宅男| 狠狠色成人一区二区三区| 怡红院美国分院一区二区| 日韩一区精品视频一区二区| 精品福利一区二区三区精品国产第一国产综合精品 | 99久久精品国产免看国产一区| 在线电影一区二区| 亚洲一区中文字幕在线电影网| 最美女人体内射精一区二区| 日本一区二区三区在线视频观看免费| 亚洲一区二区三区乱码在线欧洲| 中文字幕aⅴ人妻一区二区| 日韩精品电影一区亚洲| 无码日韩精品一区二区免费| 日韩精品一区二区三区中文| 国产在线一区二区在线视频 | 精品人妻中文av一区二区三区| 久久一区二区免费播放| 免费一区二区无码视频在线播放| 爱爱帝国亚洲一区二区三区| 一区二区三区观看| 亚洲中文字幕乱码一区| 国产一区二区四区在线观看| 在线播放偷拍一区精品| 久久久久久一区国产精品| 精品国产一区二区三区在线| 国产AV午夜精品一区二区三区| 狠狠爱无码一区二区三区| 无码福利一区二区三区|