本篇內(nèi)容介紹了“php如何實現(xiàn)小程序登錄功能”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、微信小程序定制開發(fā)、集團企業(yè)網(wǎng)站建設(shè)等服務(wù)項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了濠江免費建站歡迎大家使用!
RequestTask說明
方法 | 說明 |
---|---|
RequestTask.abort() | 中斷請求任務(wù)。 |
RequestTask.onHeadersReceived(function callback) | 監(jiān)聽 HTTP Response Header 事件。會比請求完成事件更早。 |
RequestTask.offHeadersReceived(function callback) | 取消監(jiān)聽 HTTP Response Header 事件。 |
RequestTask.onChunkReceived(function callback) | 監(jiān)聽 Transfer-Encoding Chunk Received 事件。當(dāng)接收到新的chunk時觸發(fā)。 |
RequestTask.offChunkReceived(function callback) | 取消監(jiān)聽 Transfer-Encoding Chunk Received 事件。 |
wx.request(Object object)屬性
此處只列比較常用的屬性,全部屬性請查看鏈接。
屬性 | 類型 | 默認值 | 必填 | 說明 |
---|---|---|---|---|
url | string | 是 | 開發(fā)者服務(wù)器接口地址 | |
data | string/object/ArrayBuffer | 否 | 請求的參數(shù) | |
header | Object | 否 | 設(shè)置請求的 header,header 中不能設(shè)置 Referer。 content-type 默認為 application/json | |
timeout | number | 否 | 超時時間,單位為毫秒 | |
method | string | GET | 否 | HTTP 請求方法 |
success | function | 否 | 接口調(diào)用成功的回調(diào)函數(shù) | |
fail | function | 否 | 接口調(diào)用失敗的回調(diào)函數(shù) | |
complete | function | 否 | 接口調(diào)用結(jié)束的回調(diào)函數(shù)(調(diào)用成功、失敗都會執(zhí)行)哪怕是abort掉的請求! |
總結(jié)一下:所有的小程序接口基本上都有兩個特征:
參數(shù)都是一個對象。便于記憶的同時方便擴展。
都有相同的結(jié)果處理方式:都有success、fail、complete三個回調(diào)屬性。
接口執(zhí)行的各種情況下的errMsg對象介紹。
回調(diào)屬性 | errMsg對象 |
---|---|
success | {errMsg:"request:ok"...} |
fail | {errMsg:"request:fail "...} 有的系統(tǒng)這個fail后面有個空格,所以要使用這個判斷,最好是使用正則表達式。也可以使用indexOf函數(shù),大于-1進行判斷。 |
abort | {errMsg:"request:fail abort"...} |
示例代碼
let reqTask = wx.request({ url: getApp().globalData.api, success(res) { if (res.errMsg === "request:ok") console.log("res", res); }, fail(err) { // if(err.errMsg.indexOf('request:fail')>-1) console.log('err', err); if (/^request:fail/i.test(err.errMsg)) console.log("err", err); }, complete(res) { console.log("resOrErr", res); }, }); const reqTaskOnHeadersReceived = (headers) => { reqTask.offHeadersReceived(reqTaskOnHeadersReceived); console.log("headers", headers); // 由于請求還未完全結(jié)束,所以我們沒辦法獲得請求的狀態(tài)碼,但是我們可以通過返回的requestBody的長度來進行判斷。 // 兩點說明:1. 兩個~~可以把字符串?dāng)?shù)字快速轉(zhuǎn)化為數(shù)字。 // 2. 為什么取小于19,是由于后臺返回沒有權(quán)限的requestBody的時候Content-length為“18”,正常情況下是大于19的。所以具體多少得看一下具體情況。 if (~~headers.header["Content-length"] < 19) reqTask.abort(); }; reqTask.onHeadersReceived(reqTaskOnHeadersReceived);
小程序登錄接口
wx.getUserProfile(Object object)
獲取用戶信息。頁面產(chǎn)生點擊事件(例如 button
上 bindtap
的回調(diào)中)后才可調(diào)用,每次請求都會彈出授權(quán)窗口,用戶同意后返回 userInfo
。該接口用于替換 wx.getUserInfo
,詳見 用戶信息接口調(diào)整說明。
wx.checkSession(Object object)
檢查登錄態(tài)是否過期。 通過 wx.login 接口獲得的用戶登錄態(tài)擁有一定的時效性。用戶越久未使用小程序,用戶登錄態(tài)越有可能失效。反之如果用戶一直在使用小程序,則用戶登錄態(tài)一直保持有效。具體時效邏輯由微信維護,對開發(fā)者透明。開發(fā)者只需要調(diào)用 wx.checkSession 接口檢測當(dāng)前用戶登錄態(tài)是否有效。
登錄態(tài)過期后開發(fā)者可以再調(diào)用 wx.login 獲取新的用戶登錄態(tài)。調(diào)用成功說明當(dāng)前 session_key 未過期,調(diào)用失敗說明 session_key 已過期。更多使用方法詳見 小程序登錄。
wx.login(Object object)
調(diào)用接口獲取登錄憑證(code)。通過憑證進而換取用戶登錄態(tài)信息,包括用戶在當(dāng)前小程序的唯一標(biāo)識(openid)、微信開放平臺帳號下的唯一標(biāo)識(unionid,若當(dāng)前小程序已綁定到微信開放平臺帳號)及本次登錄的會話密鑰(session_key)等。用戶數(shù)據(jù)的加解密通訊需要依賴會話密鑰完成。更多使用方法詳見 小程序登錄。
后端登錄接口代碼實現(xiàn)
后端使用NodeJS,web框架KOA版本^2.13.4,路由框架@koa/router版本^10.1.1,框架request,版本 ^2.88.2,jsonwebtoken用來加密解密token信息,版本^8.5.1
// app.js const Koa = require("koa"); const Router = require("@koa/router"); const WeixinAuth = require("./lib/koa2-weixin-auth"); const jsonwebtoken = require("jsonwebtoken"); const app = new Koa(); // 小程序機票信息 const miniProgramAppId = "*********"; const miniProgramAppSecret = "***********"; const weixinAuth = new WeixinAuth(miniProgramAppId, miniProgramAppSecret); const JWT_SECRET = "JWTSECRET"; // 路由中間件需要安裝@koa/router // 開啟一個帶群組的路由 const router = new Router({ prefix: "/user", }); // 這是正規(guī)的登陸方法 // 添加一個參數(shù),sessionKeyIsValid,代表sessionKey是否還有效 router.post("/weixin-login", async (ctx) => { let { code, userInfo, encryptedData, iv, sessionKeyIsValid } = ctx.request.body; // 解析openid const token = await weixinAuth.getAccessToken(code); userInfo.openid = token.data.openid; // 這里可以自己進行處理,比方說記錄到數(shù)據(jù)庫,處理token等 let authorizationToken = jsonwebtoken.sign( { name: userInfo.nickName }, JWT_SECRET, { expiresIn: "1d" } ); Object.assign(userInfo, { authorizationToken }); ctx.status = 200; ctx.body = { code: 200, msg: "ok", data: userInfo, }; });
// lib/koa2-weixin-auth.js const querystring = require("querystring"); const request = require("request"); const AccessToken = function (data) { if (!(this instanceof AccessToken)) { return new AccessToken(data); } this.data = data; }; /*! * 檢查AccessToken是否有效,檢查規(guī)則為當(dāng)前時間和過期時間進行對比 * * Examples: * ``` * token.isValid(); * ``` */ AccessToken.prototype.isValid = function () { return ( !!this.data.session_key && new Date().getTime() < this.data.create_at + this.data.expires_in * 1000 ); }; /** * 根據(jù)appid和appsecret創(chuàng)建OAuth接口的構(gòu)造函數(shù) * 如需跨進程跨機器進行操作,access token需要進行全局維護 * 使用使用token的優(yōu)先級是: * * 1. 使用當(dāng)前緩存的token對象 * 2. 調(diào)用開發(fā)傳入的獲取token的異步方法,獲得token之后使用(并緩存它)。 * Examples: * ``` * var OAuth = require('oauth'); * var api = new OAuth('appid', 'secret'); * ``` * @param {String} appid 在公眾平臺上申請得到的appid * @param {String} appsecret 在公眾平臺上申請得到的app secret */ const Auth = function (appid, appsecret) { this.appid = appid; this.appsecret = appsecret; this.store = {}; this.getToken = function (openid) { return this.store[openid]; }; this.saveToken = function (openid, token) { this.store[openid] = token; }; }; /** * 獲取授權(quán)頁面的URL地址 * @param {String} redirect 授權(quán)后要跳轉(zhuǎn)的地址 * @param {String} state 開發(fā)者可提供的數(shù)據(jù) * @param {String} scope 作用范圍,值為snsapi_userinfo和snsapi_base,前者用于彈出,后者用于跳轉(zhuǎn) */ Auth.prototype.getAuthorizeURL = function (redirect_uri, scope, state) { return new Promise((resolve, reject) => { const url = "https://open.weixin.qq.com/connect/oauth3/authorize"; let info = { appid: this.appid, redirect_uri: redirect_uri, scope: scope || "snsapi_base", state: state || "", response_type: "code", }; resolve(url + "?" + querystring.stringify(info) + "#wechat_redirect"); }); }; /*! * 處理token,更新過期時間 */ Auth.prototype.processToken = function (data) { data.create_at = new Date().getTime(); // 存儲token this.saveToken(data.openid, data); return AccessToken(data); }; /** * 根據(jù)授權(quán)獲取到的code,換取access token和openid * 獲取openid之后,可以調(diào)用`wechat.API`來獲取更多信息 * @param {String} code 授權(quán)獲取到的code */ Auth.prototype.getAccessToken = function (code) { return new Promise((resolve, reject) => { const url = "https://api.weixin.qq.com/sns/jscode2session"; //由于此框架版本很久沒有更新了,此處地址發(fā)生了變化,需要修改為以上地址,不然會出現(xiàn) //41008錯誤。這也是沒有直接使用框架,引用本地使用的原因。 // const url = "https://api.weixin.qq.com/sns/oauth3/access_token"; const info = { appid: this.appid, secret: this.appsecret, js_code: code, grant_type: "authorization_code", }; request.post(url, { form: info }, (err, res, body) => { if (err) { reject(err); } else { const data = JSON.parse(body); resolve(this.processToken(data)); } }); }); }; /** * 根據(jù)refresh token,刷新access token,調(diào)用getAccessToken后才有效 * @param {String} refreshToken refreshToken */ Auth.prototype.refreshAccessToken = function (refreshToken) { return new Promise((resolve, reject) => { const url = "https://api.weixin.qq.com/sns/oauth3/refresh_token"; var info = { appid: this.appid, grant_type: "refresh_token", refresh_token: refreshToken, }; request.post(url, { form: info }, (err, res, body) => { if (err) { reject(err); } else { const data = JSON.parse(body); resolve(this.processToken(data)); } }); }); }; /** * 根據(jù)openid,獲取用戶信息。 * 當(dāng)access token無效時,自動通過refresh token獲取新的access token。然后再獲取用戶信息 * @param {Object|String} options 傳入openid或者參見Options */ Auth.prototype.getUser = async function (openid) { const data = this.getToken(openid); console.log("getUser", data); if (!data) { var error = new Error( "No token for " + options.openid + ", please authorize first." ); error.name = "NoOAuthTokenError"; throw error; } const token = AccessToken(data); var accessToken; if (token.isValid()) { accessToken = token.data.session_key; } else { var newToken = await this.refreshAccessToken(token.data.refresh_token); accessToken = newToken.data.session_key; } console.log("accessToken", accessToken); return await this._getUser(openid, accessToken); }; Auth.prototype._getUser = function (openid, accessToken, lang) { return new Promise((resolve, reject) => { const url = "https://api.weixin.qq.com/sns/userinfo"; const info = { access_token: accessToken, openid: openid, lang: lang || "zh_CN", }; request.post(url, { form: info }, (err, res, body) => { if (err) { reject(err); } else { resolve(JSON.parse(body)); } }); }); }; /** * 根據(jù)code,獲取用戶信息。 * @param {String} code 授權(quán)獲取到的code */ Auth.prototype.getUserByCode = async function (code) { const token = await this.getAccessToken(code); return await this.getUser(token.data.openid); }; module.exports = Auth;
小程序端登錄代碼實現(xiàn)
<!--pages/index.wxml--> <view class="page-section"> <text class="page-section__title">微信登錄</text> <view class="btn-area"> <button bindtap="getUserProfile" type="primary">登錄</button> </view> </view>
// pages/index.js Page({ /** * 頁面的初始數(shù)據(jù) */ data: {}, // 正確的登錄方式 getUserProfile() { // 推薦使用wx.getUserProfile獲取用戶信息,開發(fā)者每次通過該接口獲取用戶個人信息均需用戶確認 // 開發(fā)者妥善保管用戶快速填寫的頭像昵稱,避免重復(fù)彈窗 wx.getUserProfile({ desc: "用于完善會員資料", // 聲明獲取用戶個人信息后的用途,后續(xù)會展示在彈窗中,請謹慎填寫 success: (res) => { let { userInfo, encryptedData, iv } = res; const requestLoginApi = (code) => { // 發(fā)起網(wǎng)絡(luò)請求 wx.request({ url: "http://localhost:3000/user/weixin-login", method: "POST", header: { "content-type": "application/json", }, data: { code, userInfo, encryptedData, iv, }, success(res) { console.log("請求成功", res.data); let token = res.data.data.authorizationToken; wx.setStorageSync("token", token); onUserLogin(token); console.log("authorization", token); }, fail(err) { console.log("請求異常", err); }, }); }; const onUserLogin = (token) => { getApp().globalData.token = token; wx.showToast({ title: "登錄成功了", }); }; //必須進行session是否過期檢查,不然會出現(xiàn)第一次點擊登錄,服務(wù)器報Illegal Buffer //的錯誤,但是第二次點擊登錄正常。 wx.checkSession({ success: (res) => { // session_key 未過期,并且在本生命周期一直有效 console.log("在登陸中"); let token = wx.getStorageSync("token"); if (token) onUserLogin(token); }, fail: (res) => { // session_key已經(jīng)失效,需要重新執(zhí)行登錄流程 wx.login({ success(res0) { if (res0.code) { requestLoginApi(res0.code); } else { console.log("登錄失敗!" + res0.errMsg); } }, }); }, }); }, }); }, });
針對登錄代碼可以做哪些優(yōu)化?
對于一個軟件,就代碼層面而言,需要追求最基本的幾個方面(遠不止這些,但是先姑且先做個好這些吧):
可維護性(maintainability)
所謂的“維護”無外乎就是修改 bug、修改老的代碼、添加新的代碼之類的工作。所謂“代碼易維護”就是指,在不破壞原有代碼設(shè)計、不引入新的 bug 的情況下,能夠快速地修改或者添加代碼。所謂“代碼不易維護”就是指,修改或者添加代碼需要冒著極大的引入新 bug 的風(fēng)險,并且需要花費很長的時間才能完成。
可讀性(readability)
軟件設(shè)計大師 Martin Fowler 曾經(jīng)說過:“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”翻譯成中文就是:“任何傻瓜都會編寫計算機能理解的代碼。好的程序員能夠編寫人能夠理解的代碼?!盙oogle 內(nèi)部甚至專門有個認證就叫作 Readability。只有拿到這個認證的工程師,才有資格在 code review 的時候,批準(zhǔn)別人提交代碼??梢姶a的可讀性有多重要,畢竟,代碼被閱讀的次數(shù)遠遠超過被編寫和執(zhí)行的次數(shù)。我們需要看代碼是否符合編碼規(guī)范、命名是否達意、注釋是否詳盡、函數(shù)是否長短合適、模塊劃分是否清晰、是否符合高內(nèi)聚低耦合等等。
可擴展性(extensibility)
可擴展性也是一個評價代碼質(zhì)量非常重要的標(biāo)準(zhǔn)。代碼預(yù)留了一些功能擴展點,你可以把新功能代碼,直接插到擴展點上,而不需要因為要添加一個功能而大動干戈,改動大量的原始代碼。
可復(fù)用性(reusability)
代碼的可復(fù)用性可以簡單地理解為,盡量減少重復(fù)代碼的編寫,復(fù)用已有的代碼。
那么接下來就來優(yōu)化一下代碼吧:
模塊化
可以把登錄的代碼模塊化,代碼如下:
// lib/login.js function loginWithCallback(cb) { // 推薦使用wx.getUserProfile獲取用戶信息,開發(fā)者每次通過該接口獲取用戶個人信息均需用戶確認 // 開發(fā)者妥善保管用戶快速填寫的頭像昵稱,避免重復(fù)彈窗 wx.getUserProfile({ desc: "用于完善會員資料", // 聲明獲取用戶個人信息后的用途,后續(xù)會展示在彈窗中,請謹慎填寫 success: (res) => { let { userInfo, encryptedData, iv } = res; const requestLoginApi = (code) => { // 發(fā)起網(wǎng)絡(luò)請求 wx.request({ url: "http://localhost:3000/user/weixin-login", method: "POST", header: { "content-type": "application/json", }, data: { code, userInfo, encryptedData, iv, }, success(res) { console.log("請求成功", res.data); let token = res.data.data.authorizationToken; wx.setStorageSync("token", token); onUserLogin(token); console.log("authorization", token); }, fail(err) { console.log("請求異常", err); }, }); }; const onUserLogin = (token) => { getApp().globalData.token = token; wx.showToast({ title: "登錄成功了", }); if (cb && typeof cb == "function") cb(token); }; wx.checkSession({ success: (res) => { // session_key 未過期,并且在本生命周期一直有效 console.log("在登陸中"); let token = wx.getStorageSync("token"); if (token) onUserLogin(token); }, fail: (res) => { // session_key已經(jīng)失效,需要重新執(zhí)行登錄流程 wx.login({ success(res0) { if (res0.code) { requestLoginApi(res0.code); } else { console.log("登錄失敗!" + res0.errMsg); } }, }); }, }); }, }); } export default loginWithCallback;
Promise化
回調(diào)地獄問題,不利于代碼的閱讀,所以接下來我們基于Promise進行代碼優(yōu)化。有了 Promise 對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調(diào)函數(shù)。此外,Promise 對象提供統(tǒng)一的接口,使得控制異步操作更加容易。
Promise的幾個方法簡介
方法名 | 說明 |
---|---|
Promise.prototype.then | 方法返回的是一個新的 Promise 對象,因此可以采用鏈?zhǔn)綄懛?。這種設(shè)計使得嵌套的異步操作,可以被很容易得改寫,從回調(diào)函數(shù)的"橫向發(fā)展"改為"向下發(fā)展"。 |
Promise.prototype.catch | 是 Promise.prototype.then(null, rejection) 的別名,用于指定發(fā)生錯誤時的回調(diào)函數(shù)。Promise 對象的錯誤具有"冒泡"性質(zhì),會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個 catch 語句捕獲。 |
Promise.prototype.finally | 方法返回一個Promise 。在promise結(jié)束時,無論結(jié)果是fulfilled或者是rejected,都會執(zhí)行指定的回調(diào)函數(shù)。這為在Promise 是否成功完成后都需要執(zhí)行的代碼提供了一種方式。 |
Promise.all | 這避免了同樣的語句需要在then() 和catch() 中各寫一次的情況。Promise.all 方法用于將多個 Promise 實例,包裝成一個新的 Promise 實例。Promise.all 方法接受一個數(shù)組作為參數(shù),var p = Promise.all([p1,p2,p3]); p1、p2、p3 都是 Promise 對象的實例。(Promise.all 方法的參數(shù)不一定是數(shù)組,但是必須具有 iterator 接口,且返回的每個成員都是 Promise 實例。)p 的狀態(tài)由 p1、p2、p3 決定,分成兩種情況。 (1)只有p1、p2、p3的狀態(tài)都變成fulfilled,p的狀態(tài)才會變成fulfilled,此時p1、p2、p3的返回值組成一個數(shù)組,傳遞給p的回調(diào)函數(shù)。 (2)只要p1、p2、p3之中有一個被rejected,p的狀態(tài)就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調(diào)函數(shù)。 |
Promise.race | Promise.race 方法同樣是將多個 Promise 實例,包裝成一個新的 Promise 實例。var p = Promise.race([p1,p2,p3]); 上面代碼中,只要p1、p2、p3之中有一個實例率先改變狀態(tài),p的狀態(tài)就跟著改變。那個率先改變的Promise實例的返回值,就傳遞給p的返回值。 |
Promise.any | 接收一個Promise可迭代對象,只要其中的一個 promise 成功,就返回那個已經(jīng)成功的 promise 。所有子實例都處于rejected狀態(tài),總的promise才處于rejected狀態(tài)。 |
Promise.allSettled | 返回一個在所有給定的promise都已經(jīng)fulfilled 或rejected 后的promise,并帶有一個對象數(shù)組,每個對象表示對應(yīng)的promise結(jié)果。相比之下,Promise.all() 更適合彼此相互依賴或者在其中任何一個reject 時立即結(jié)束。 |
小程序API接口Promise化并且把需要登錄的調(diào)用接口模塊化
1、安裝插件。請先查看npm支持文檔。
npm install --save miniprogram-api-promise
2、在微信開發(fā)者工具右方詳情中勾選使用npm模塊,并在菜單欄工具中點擊構(gòu)建npm。
3、初始化代碼。
// app.js import {promisifyAll} from 'miniprogram-api-promise' import login from "../lib/login"; const wxp ={} promisifyAll(wx,wxp) // 需要token的請求統(tǒng)一處理登錄和設(shè)置header,并且處理錯誤信息 wxp.requestNeedLogin = async function (args) { let token = wx.getStorageSync("token"); if (!token) { token = await loginWithPromise(); } if (!args.header) args.header = {}; args.header["Authorization"] = `Bearer ${token}`; return wxp.request(args).catch(console.error); }; // app.js App({ wxp:wxp, });
4、改寫login.js代碼
// lib/login.js function login() { return new Promise((resolve, reject) => { // 推薦使用wx.getUserProfile獲取用戶信息,開發(fā)者每次通過該接口獲取用戶個人信息均需用戶確認 // 開發(fā)者妥善保管用戶快速填寫的頭像昵稱,避免重復(fù)彈窗 wx.getUserProfile({ desc: "用于完善會員資料", // 聲明獲取用戶個人信息后的用途,后續(xù)會展示在彈窗中,請謹慎填寫 success:async (res0) => { let { userInfo, encryptedData, iv } = res0; const app = getApp(); try { app.wxp.checkSession(); } catch (err) { reject(err); } let token = wx.getStorageSync("token"); if (!token) { let res1 = await app.wxp.login().catch(err => reject(err)); let code = res1.code; let res = await app.wxp.request({ url: "http://localhost:3000/user/weixin-login", method: "POST", header: { "content-type": "application/json", }, data: { code, userInfo, encryptedData, iv, } }).catch(err => reject(err)); token = res.data.data.authorizationToken; wx.setStorageSync("token", token); app.globalData.token = token; wx.showToast({ title: "登錄成功了", }); resolve(token); } }, }); }) } export default login;
5、調(diào)用代碼
<view class="container page-head"> <text class="page-section__title">需要登錄的請求調(diào)用</text> <view class="btn-area"> <button bindtap="request1" type="primary">請求1</button> <button bindtap="request2" type="primary">請求2</button> </view> </view>
// pages/index.js Page({ /** * 頁面的初始數(shù)據(jù) */ data: {}, request1() { getApp().wxp.requestNeedLogin({ url: "http://localhost:3000/user/home?name=andying", }).then(console.log) }, request2() { getApp().wxp.requestNeedLogin({ url: "http://localhost:3000/user/home?name=eva", }).then(console.log) }, });
“php如何實現(xiàn)小程序登錄功能”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
當(dāng)前名稱:php如何實現(xiàn)小程序登錄功能
網(wǎng)頁網(wǎng)址:http://sd-ha.com/article42/jsjdhc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、服務(wù)器托管、品牌網(wǎng)站設(shè)計、網(wǎng)站內(nèi)鏈、網(wǎng)站營銷、網(wǎng)頁設(shè)計公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)