中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

詳情頁制作網(wǎng)站seo交互論壇

詳情頁制作網(wǎng)站,seo交互論壇,女孩子讀電子商務(wù)好就業(yè)嗎,免費ui設(shè)計網(wǎng)站這里寫目錄標(biāo)題 1. 注冊頁面1.1 注冊/登錄頁面——接口請求1.2 Vue開發(fā)中Element UI的樣式穿透1.2.1 ::v-deep的使用1.2.2 elementUI Dialog內(nèi)容區(qū)域顯示滾動條 1.3 注冊頁面——步驟條和表單聯(lián)動 stepsform1.4 注冊頁面——滑動拼圖驗證1.5 注冊頁面——element-ui組件Popover…

這里寫目錄標(biāo)題

  • 1. 注冊頁面
    • 1.1 注冊/登錄頁面——接口請求
    • 1.2 Vue開發(fā)中Element UI的樣式穿透
      • 1.2.1 ::v-deep的使用
      • 1.2.2 elementUI Dialog內(nèi)容區(qū)域顯示滾動條
    • 1.3 注冊頁面——步驟條和表單聯(lián)動 steps+form
    • 1.4 注冊頁面——滑動拼圖驗證
    • 1.5 注冊頁面——element-ui組件Popover 彈出框 條件控制顯示和隱藏 滑塊驗證碼
    • 1.6 注冊頁面——獲取手機(jī)驗證碼,進(jìn)行手機(jī)號校驗、驗證碼CD60秒
    • 1.7 注冊頁面——element-ui組件el-autocomplete帶輸入建議 自動補(bǔ)全后綴(郵箱地址)
    • 1.8 注冊頁面——用戶、密碼強(qiáng)弱校驗+密碼自定義規(guī)則校驗、提交驗證
  • 2. 登錄頁面
    • 2.1 登錄頁面——接口
    • 2.2 登錄——token
    • 2.3 登錄頁面——退出登錄
    • 2.4 登錄頁面——全局導(dǎo)航守衛(wèi)
    • 2.5 路由獨享守衛(wèi)
    • 2.6 組件導(dǎo)航守衛(wèi)
    • 2.7 全局封裝API
  • 3. 二維碼生成
  • 4. 二級路由拆分(個人中心)
  • 5. 圖片懶加載
  • 6. 路由懶加載
  • 7. 項目上線
    • 7.1 打包
    • 7.2 購買云服務(wù)器

1. 注冊頁面

1.1 注冊/登錄頁面——接口請求

api/index.js

/*--------- 用戶注冊登錄 ---------*///獲取驗證碼
export const reqGetCode = (phone) => requests({url: `/user/passport/sendCode/${phone}`, method: 'get'})//用戶注冊
export const reqUserRegister = (data) => requests({url: '/user/passport/register', method: 'post', data: data})

store/user.js

import { reqGetCode, reqUserRegister } from "@/api"//home模塊的Vuex模塊
const state = {//state中數(shù)據(jù)默認(rèn)初始值別瞎寫,根據(jù)接口的返回值進(jìn)行初始化code: '',
}
const mutations = {GET_CODE(state, code) {state.code = code},
}
const actions = {//獲取驗證碼async getCode({commit}, phone){//獲取驗證碼的這個接口,把驗證碼返回,正常情況下,后臺把驗證碼發(fā)到用戶手機(jī)上let result  = await reqGetCode(phone)// console.log("result",result) if(result.code == 200){commit('GET_CODE',result.data)return 'ok'} else {return Promise.reject(new Error("fail"))}},//用戶注冊async userRegister({commit}, userFrom) {let result = await reqUserRegister(userFrom) if(result.code == 200) {return 'ok'} else {return Promise.reject(new Error(result.message))}},
}export default{state,getters,mutations,actions
}

1.2 Vue開發(fā)中Element UI的樣式穿透

1.2.1 ::v-deep的使用

參考::v-deep的使用

在 vue 項目的開發(fā)過程,使用了 ElementUI 組件且樣式 style 使用了 scoped 屬性,當(dāng)想要修改組件樣式,發(fā)現(xiàn)直接修改不了,需去掉 scoped 屬性或者使用深度選擇器才能修改成功。去掉scoped的話又會影響全局樣式,針對這種情況,可以使用深度作用選擇器(即樣式穿透)

1、當(dāng)項目中使用的 css 原生樣式 ,需要使用 >>> 深度選擇器來修改 外用第三方組件的樣式

<style lang="css" scoped>.el-button >>> span{color: '#f00'}
</style>

2、當(dāng)項目中使用的 css 擴(kuò)展語言是 less, 需要使用 /deep/ 或者 ::v-deep 深度選擇器來修改 外用第三方組件的樣式

<style lang="less" scoped>/deep/.el-button{span{color: '#f00'}}.el-button::v-deep{span{color: '#f00'}}
</style>

3、當(dāng)項目中使用的 css 擴(kuò)展語言是 node-sass, 需要使用 /deep/ 或者 ::v-deep 深度選擇器來修改 外用第三方組件的樣式

<style lang="scss" scoped>.el-button::v-deep{span{color: '#f00'}}/deep/.el-button{span{color: '#f00'}}
</style>

4、當(dāng)項目中使用的 css 擴(kuò)展語言是 dart-sass, 需要使用 ::v-deep 深度選擇器來修改 外用第三方組件的樣式,dart-sass不支持 /deep/ 和 >>> 的寫法

<style lang="scss" scoped>.el-button::v-deep{span{color: '#f00'}}
</style>

注意:
① 操作符 >>> 可能會因為無法編譯而報錯,可以使用 /deep/
② vue3.0 中使用 /deep/ 會報錯,更推薦使用 ::v-deep
③ 對于使用了 css 預(yù)處理器(scss 、sass、 less)時,深度選擇器 ::v-deep 比較通用

1.2.2 elementUI Dialog內(nèi)容區(qū)域顯示滾動條

所以在項目,我要使對話框的內(nèi)容區(qū)域顯示滾動條,同時去掉對話框原有的外部滾動條,使用樣式穿透,代碼如下:

<el-dialog title="Dialog" class="roll-dialog"> </el-dialog>
.rolling-dialog {overflow: hidden;::v-deep .el-dialog .el-dialog__body {overflow-y: scroll;height: 400px;}
}

實際開發(fā)中還有對話框內(nèi)容區(qū)域高度自適應(yīng)的要求,可以閱讀這篇 Element UI 彈窗(Dialog)改成自適應(yīng)高度,僅body內(nèi)容部分滾動

效果
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-aIKYH0Br-1693146642040)(C:\Users\crj\AppData\Roaming\Typora\typora-user-images\image-20230715185749623.png)]

1.3 注冊頁面——步驟條和表單聯(lián)動 steps+form

element的步驟條整合表單(steps+form)

場景

image-20230718153429816image-20230718153502570

在vue開發(fā)中,注冊頁面填寫的信息過多,如果全部一起呈現(xiàn),效果不是很好,這時候就可以用到步驟條來分步注冊,這里用到了ElementUI里的steps組件和form

頁面代碼如下:

<template>//第一步:定義出4個步驟
<el-steps :active="active" finish-status="success" align-center :space="200" class="steps"><el-step title="驗證手機(jī)號"></el-step><el-step title="填寫帳號信息"></el-step><el-step title="注冊成功" status="success"></el-step>
</el-steps>//第二步:定義form表單
<el-formref="registerForm":model="registerForm":rules="rules"class="form-body">//第三步:定義3個盒子對象active =>0 到 2
<div v-show="active == 0">//第四步:放置表單項//...<el-form-item class="form-item" prop="phoneNum"><el-input clearable placeholder="建議使用常用手機(jī)號" v-model="registerForm.phoneNum"></el-form-item></div>
<div v-show="active == 1"></div>
<div v-show="active == 2"></div></el-form>//第五步:設(shè)置上一步和下一步的按鈕
<el-button v-if="active < 3" style="margin-top: 12px" @click="next">下一步</el-button>
<el-button v-if="active > 1" style="margin-top: 12px" @click="pre">上一步</el-button></template>

對應(yīng)的屬性和方法

data() {return {//默認(rèn)第一步active: 0,}
},
methods: {// 步驟條下一步的方法next() {if (this.active++ > 2) this.active = 0},// 步驟條上一步的方法pre() {if (this.active-- < 0) this.active = 0},}

1.4 注冊頁面——滑動拼圖驗證

Vue實現(xiàn)滑塊拼圖驗證,這里使用了vue-monoplasty-slide-verify插件

參考 vue實現(xiàn)登錄滑動拼圖驗證的兩種方法,純前端組件驗證以及前后端同時驗證

1.5 注冊頁面——element-ui組件Popover 彈出框 條件控制顯示和隱藏 滑塊驗證碼

場景:輸入手機(jī)號碼,手機(jī)格式正確,點擊按鈕驗證才可顯示彈出框

image-20230714164134589image-20230714164118737

手動控制el-popver彈窗的顯示與隱藏,給el-popver層綁定一個v-model,值為true或是false,這是官網(wǎng)上給的Attributes。

<el-popover v-model="showPopover">

在這里插入圖片描述

而且顯示根本不用控制,el-popover有一個trigger屬性,trigger可以為click/focus/hover/manual,默認(rèn)值是click,所以單擊就能觸發(fā),主要是彈窗的隱藏問題。

我們使用manul來控制顯示

<el-popover trigger="manual">

實際代碼:

<el-popover placement="top" width="320" trigger="manual" v-model="showSliderVerify"><div class="popper-title"><span>完成拼圖驗證</span><i class="el-icon-close close-icon" @click="showSliderVerify=false"></i></div><slide-verify :l="42" :r="10" :w="310":h="155" slider-text="向右滑動" @success="onSuccess" @fail="onFail" @refresh="onRefresh"></slide-verify><div style="margin-top: 15px">{{ msg }}</div><!--點擊控制彈窗的顯示--><el-button slot="reference" style="width: 400px" @click="openSliderVerify">點擊按鈕進(jìn)行驗證</el-button>
</el-popover>
<script>export default {data() {return {showSliderVerify:false,//v-model默認(rèn)值是false, click激活變成truemsg: ""}},methods: {//滑塊按鈕點擊openSliderVerify() {//只有手機(jī)號碼通過才能顯示滑塊驗證碼this.$refs.registerForm.validateField("phoneNum", async (valid) => {if (!valid) {//手機(jī)號碼格式正確,才可以顯示滑塊驗證碼this.showSliderVerify = true;} else {return false;}});},//滑塊驗證通過onSuccess(times) {this.msg = `success, 耗時${(times / 1000).toFixed(1)}s`;this.showSliderVerify = false;this.showCode = true;//驗證碼一通過,就自動獲取驗證碼this.getCode();},//滑塊驗證失敗onFail() {this.msg = "驗證不通過";},//滑塊驗證刷新onRefresh() {this.msg = "";console.log("點擊了刷新小圖標(biāo)");},//滑塊驗證刷新完成onFulfilled() {this.msg = "重新驗證";},}}
</script>

代碼中我還設(shè)置了樣式,但是樣式在當(dāng)前vue文件下,怎么樣調(diào)整都不動。

后來看來這個blog

原來是要在App.vue下寫css樣式,

<style lang="less">
.popper-title {margin-bottom: 10px;display: flex;justify-content: space-between;font-size: 16px;.close-icon {cursor: pointer;font-size: 20px;color: #4f4f4f;}
}
</style>

原因可以看下面這張圖,你會發(fā)現(xiàn) app 和 el-popover 是平級,又因為我們每個組件的style標(biāo)簽都寫有 scoped 屬性,所以在組件里寫樣式不起效

img

1.6 注冊頁面——獲取手機(jī)驗證碼,進(jìn)行手機(jī)號校驗、驗證碼CD60秒

參考blog

場景

手機(jī)輸入不能為空且必須為正確格式
在這里插入圖片描述

點擊下一步,如果未完成驗證,則不可以進(jìn)行下一步注冊步驟

完成滑塊拼圖驗證后,立即自動發(fā)送驗證碼,60s有效期,
在這里插入圖片描述

由于注冊頁面用到了前面的提到的steps,所以手機(jī)及驗證碼的輸入框是在 <div v-show="active == 0"></div>內(nèi),且只有手機(jī)號碼輸入正確,才可進(jìn)行滑塊拼圖驗證,滑塊拼圖驗證通過之后,立即發(fā)送驗證碼

頁面代碼如下

<el-steps :active="active" finish-status="success" align-center :space="200" class="steps" ><el-step title="驗證手機(jī)號"></el-step><el-step title="填寫帳號信息"></el-step><el-step title="注冊成功"></el-step>
</el-steps>
<el-form ref="registerForm" :model="registerForm" :rules="rules" class="form-body"><!-- 步驟條 active==0 --><div v-show="active == 0"><el-form-item class="form-item" prop="phoneNum"><el-input clearable placeholder="建議使用常用手機(jī)號" v-model="registerForm.phoneNum"><el-selectv-model="registerForm.select"placeholder="中國+86"slot="prepend"style="width: 120px"><el-option label="中國+86" value="中國+86"></el-option><el-option label="+40" value="+40"></el-option><el-option label="+111" value="+111"></el-option></el-select></el-input></el-form-item><el-form-item class="form-item" v-show="!showCode"><el-popover placement="top" width="320" trigger="manual" v-model="showSliderVerify"><div class="popper-title"><span>完成拼圖驗證</span><i class="el-icon-close close-icon" @click="showSliderVerify = false"></i></div><slide-verify :l="42" :r="10" :w="310" :h="155" :imgs="bgimgs"slider-text="向右滑動"@success="onSuccess"@fail="onFail"@refresh="onRefresh"></slide-verify><div style="margin-top: 15px">{{ msg }}</div><el-button slot="reference" class="wd400" @click="openSliderVerify">點擊按鈕進(jìn)行驗證</el-button></el-popover><div v-show="validateCode" class="el-form-item__error">請完成驗證</div></el-form-item><el-form-item prop="code" class="form-item" v-show="showCode"><el-input clearable v-model="registerForm.code" placeholder="請輸入驗證碼"><template slot="prepend">手機(jī)驗證碼</template><el-button slot="append" :disabled="codeCd" size="samll" @click="getCode"><span v-if="codeCd">{{ long }}后重新獲取</span><span v-else>獲取驗證碼</span></el-button></el-input></el-form-item><el-form-item class="form-item"><el-button class="wd400" @click="next">下一步</el-button></el-form-item></div>
</el-form>

對應(yīng)的邏輯代碼如下

export default {name: "Register",data() {//驗證手機(jī)號const validatePhone = (rule, value, callback) => {if (!value) {callback(new Error("手機(jī)號碼不能為空"));}// 使用正則表達(dá)式驗證手機(jī)號碼if (!/^1[3456789]\d{9}$/.test(value)) {callback(new Error("手機(jī)號碼格式不正確"));}//自定義校驗規(guī)則,需要調(diào)用callback()函數(shù)callback();};return {registerForm: {phoneNum: null,code: "",},rules: {phoneNum: [{required: true,validator: validatePhone,trigger: "blur",},],code: [{required: true,message: "驗證碼不能為空!",trigger: "blur",},],},//驗證碼秒數(shù)倒計時long: 60,//驗證碼是否等候codeCd: false,//滑塊拼圖驗證碼msgmsg: "",//滑塊驗證碼背景圖bgimgs: [],//步驟條的activeactive: 0,//是否顯示滑塊拼圖驗證showSliderVerify: false,//是否顯示驗證碼showCode: false,//驗證是否完成拼圖滑塊validateCode: false};},methods: {//滑塊按鈕點擊openSliderVerify() {//只有手機(jī)號碼通過才能顯示滑塊驗證碼this.$refs.registerForm.validateField("phoneNum", async (valid) => {if (!valid) {//手機(jī)號碼格式正確,才可以顯示滑塊驗證碼this.showSliderVerify = true;} else {return false;}});},//步驟條下一步next() {//當(dāng)前表格是否進(jìn)行驗證const { phoneNum, code } = this.registerForm;const { showCode } = this;//驗證手機(jī)號if (!phoneNum) {this.$refs.registerForm.validateField("phoneNum");return;}//驗證滑塊拼圖驗證碼if (!showCode) {//展示提示信息this.validateCode = true       return;} //完成滑塊驗證后,驗證是否填寫驗證碼else if (showCode && !code) {this.$refs.registerForm.validateField("code");return;}//以上都通過才進(jìn)入下一步if (this.active++ > 2) this.active = 0;},//獲取手機(jī)驗證碼getCode() {this.$refs.registerForm.validateField("phoneNum", async (valid) => {// valid是驗證手機(jī)號碼是否通過if (!valid) {// 獲取驗證碼try {//發(fā)送驗證碼this.$store.dispatch("getCode", this.registerForm.phoneNum);//開始計時,60秒倒計時this.codeCd = true;const timer = setInterval(() => {this.long--;if (this.long <= 0) {this.long = 60;this.codeCd = false;clearInterval(timer);}}, 1000);//假設(shè)手動輸入驗證碼this.registerForm.code = this.$store.state.user.code;} catch (error) {alert(error.message);}} else {return false;}});},//滑塊驗證通過onSuccess(times) {this.msg = `success, 耗時${(times / 1000).toFixed(1)}s`;this.showSliderVerify = false;this.showCode = true;//驗證碼一通過,就自動獲取驗證碼this.getCode();},//滑塊驗證失敗onFail() {this.msg = "驗證不通過";},//滑塊驗證刷新onRefresh() {this.msg = "";//console.log("點擊了刷新小圖標(biāo)");},//滑塊驗證刷新完成onFulfilled() {this.msg = "重新驗證";},
  1. 進(jìn)行手機(jī)號校驗關(guān)鍵在對單個手機(jī)號輸入框進(jìn)行校驗,需要使用到validateField對部分表單字段進(jìn)行校驗,valid是校驗完的提示信息,當(dāng)valid為空時代表校驗成功
  2. 讀秒和設(shè)置禁用,在校驗成功時設(shè)置一個60s計時器,讀秒過程禁用按鈕,用了element-ui的按鈕組件,在讀秒過程中給按鈕增加disabled屬性;讀秒過程結(jié)束,解除按鈕禁用

1.7 注冊頁面——element-ui組件el-autocomplete帶輸入建議 自動補(bǔ)全后綴(郵箱地址)

效果:實現(xiàn)輸入數(shù)字,自動補(bǔ)齊郵箱后綴
在這里插入圖片描述

autocomplete 是一個可帶輸入建議的輸入框組件,fetch-suggestions 是一個返回輸入建議的方法屬性,如 querySearch(queryString, cb),在該方法中你可以在你的輸入建議數(shù)據(jù)準(zhǔn)備好時通過 cb(data) 返回到 autocomplete 組件中。

<el-autocompleteclearablev-model="registerForm.email":fetch-suggestions="emailSuffix":trigger-on-focus = 'false'@select="selectEmailSuffix"class="wd400"placeholder="請輸入郵箱"><template slot="prepend">郵箱驗證</template></el-autocomplete>
data(){return {suffix: []}
},
mounted() {this.suffix = this.loadAll()
},
methods: {//郵箱后綴輸入建議emailSuffix(queryString, callback) {console.log(queryString);let suffix = this.suffixlet results = JSON.parse(JSON.stringify(suffix))for(let item in results) {results[item].value = queryString + '' + suffix[item].value}callback(results)},//選擇的哪個值selectEmailSuffix(item) {console.log(item);},loadAll() {return [{"value": "@qq.com"},{"value": "@126.com"},{"value": "@163.com"},{"value": "@sohu.com"},{"value": "@Gmail.com"},{"value": "@Sina.com"}]},
}

1.8 注冊頁面——用戶、密碼強(qiáng)弱校驗+密碼自定義規(guī)則校驗、提交驗證

參考vue3+ts+element-plus密碼強(qiáng)弱校驗+密碼自定義規(guī)則校驗

博客中用的是Vue3,自己項目中用的Vue2,去掉密碼規(guī)則中的"是否包含3個及以上鍵盤連續(xù)字符;(橫向、斜向都包括)"這一項,其他要求都差不多。

修修改改,實現(xiàn)效果如下:
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

頁面代碼

<el-form ref="registerForm" :model="registerForm" :rules="rules" class="form-body"><div v-show="active ==0 "><!-- 驗證手機(jī)號.... --></div><div v-show="active == 1"><el-form-item class="form-item" prop="username"><el-input clearable v-model="registerForm.username" placeholder="賬戶唯一識別,可用來登錄"><template slot="prepend">賬號名</template></el-input></el-form-item><el-form-item class="form-item" prop="password"><el-inputclearablev-model="registerForm.password"show-passwordplaceholder="請輸入包含英文字母大小寫、數(shù)字和特殊符號的 8-16 位組合"><template slot="prepend">設(shè)置密碼</template></el-input><div class="barbox"v-if="registerForm.password !== '' && registerForm.password !== undefined"><div class="strength" :style="{ color: barColor }">{{ strength }} </div><div class="bar" :style="{ background: barColor, width: width + '%' }"></div></div></el-form-item><el-form-item class="form-item" prop="repassword"><el-inputclearablev-model="registerForm.repassword"show-passwordplaceholder="請再次輸入密碼"><template slot="prepend">確認(rèn)密碼</template></el-input></el-form-item><el-form-item class="form-item" prop="email"><el-autocompleteclearablev-model="registerForm.email":fetch-suggestions="emailSuffix":trigger-on-focus="false"@select="selectEmailSuffix"class="wd400"placeholder="請輸入郵箱"><template slot="prepend">郵箱驗證</template></el-autocomplete></el-form-item><el-form-item class="form-item" prop="emailCode"><el-inputclearablev-model="registerForm.emailCode"placeholder="請輸入郵箱驗證碼"><template slot="prepend">郵箱驗證碼</template><el-buttonslot="append":disabled="emailCodeCd"size="samll"@click="getEmailCode"><span v-if="emailCodeCd">{{ emaillong }}后重新獲取</span><span v-else>獲取驗證碼</span></el-button></el-input></el-form-item><el-form-item class="form-item"><el-button class="wd400" @click="submitForm">立即注冊</el-button></el-form-item></div><div v-show="active == 2"><div class="registerOk"><i class="el-icon-time icon"></i><h1>恭喜您 {{ this.registerForm.username }}</h1><span>您已成功注冊為京東用戶,祝您購物愉快~</span><router-link class="btn" to="/home">去購物</router-link></div></div>
</el-form>

css代碼

.barbox {display: flex;align-items: center;height: 26px;.strength {font-size: 13px;color: #271e25;transition: 0.5s all ease;margin-right: 5px;flex-shrink: 0;}.bar {height: 5px;background: red;transition: 0.5s all ease;max-width: 400px;}
}
.registerOk {color: #333;font-size: 14px;display: flex;flex-flow: column;align-items: center;.icon {font-size: 40px;color: green;}h1 {font-size: 40px;margin: 16px 0;}span {margin-bottom: 16px;}.btn {padding: 10px 20px;background-color: #c81623;color: #fff;}
}

對應(yīng)的邏輯代碼

//引入驗證方法
import { checkPasswordRule, level } from "./CheckPassword";
export default {name: "Register",data() {//驗證手機(jī)號const validatePhone = (rule, value, callback) => {if (!value) {callback(new Error("手機(jī)號碼不能為空"));}// 使用正則表達(dá)式驗證手機(jī)號碼if (!/^1[3456789]\d{9}$/.test(value)) {callback(new Error("手機(jī)號碼格式不正確"));}//自定義校驗規(guī)則,需要調(diào)用callback()函數(shù)callback();};//密碼驗證const passwordValidate = (rule, value, callback) => {if (!value) {callback(new Error("密碼不能為空"));} else {let name =this.registerForm.username === "" ? "" : this.registerForm.username;const result = checkPasswordRule(value, name);if (result === "校驗通過") {callback();} else {callback(new Error(result));}}//該部分是只驗證密碼是否滿足reg正則,而不進(jìn)行強(qiáng)弱校驗// const reg = /^(?=.*[A-Za-z])(?=.*[0-9])[A-Za-z0-9]{8,16}$/g;// if (!reg.test(value)) {//   callback(new Error("請輸入包含英文字母、數(shù)字的 8-16 位組合"));// } else {//   callback();// }};//密碼與確認(rèn)密碼不一樣,一定要寫在data里,但不是return里const repeatValidate = (rule, value, callback) => {if (!value) {callback(new Error("請再次輸入密碼"));} else if (value !== this.registerForm.password) {callback(new Error("兩次輸入密碼不一致"));} else {callback();}};return {registerForm: {username: "",phoneNum: null,code: "",email: "",password: "",repassword: "",emailCode: "",},rules: {phoneNum: [{required: true,validator: validatePhone,trigger: "blur",},],code: [{required: true,message: "驗證碼不能為空!",trigger: "blur",},],password: [{required: true,validator: passwordValidate,trigger: "blur",},],repassword: [{required: true,validator: repeatValidate,trigger: "blur",},],username: [{required: true,message: "賬戶名不能為空",trigger: "blur",},{min: 4,max: 30,message: "賬戶名長度在4至30個字符之間",trigger: "blur",},],email: [{required: true,message: "請輸入郵箱地址",trigger: "blur",},{type: "email",message: "請輸入正確的郵箱地址",trigger: ["blur", "change"],},],emailCode: [{required: true,message: "郵箱驗證碼不能為空!",trigger: "blur",},],},//驗證碼秒數(shù)倒計時long: 60,emaillong: 300,//密碼強(qiáng)度背景色barColor: "",//密碼強(qiáng)度長度width: "",//密碼強(qiáng)度strength: "",//驗證碼是否等候codeCd: false,//郵箱驗證碼等候emailCodeCd: false,};},methods: {//提交表單_用戶注冊submitForm() {this.$refs["registerForm"].validate(async(valid)=>{if(valid) {try {const { phoneNum, code, password } = this.registerForm;phoneNum && code && password && (await this.$store.dispatch("userRegister", {phone: phoneNum,code: code,password:password,}));this.active++;// this.$router.push("/login");} catch (error) {alert(error.message);}}else {return false}})},  },watch: {"registerForm.password"(newVal, oldVal) {if (newVal !== "") {const res = level(newVal);this.strength = res;switch (res) {case "非常強(qiáng)":this.barColor = "#1B8EF8";this.width = "100";break;case "強(qiáng)":this.barColor = "green";this.width = "80";break;case "一般":this.barColor = "orange";this.width = "60";break;case "弱":this.barColor = "#ee795c";this.width = "40";break;case "非常弱":this.barColor = "red";this.width = "20";break;}}},},

強(qiáng)弱校驗、規(guī)則校驗 CheckPassword.js:

// 數(shù)字
const REG_NUMBER = '.*\\d+.*'
//大寫字母
const REG_UPPERCASE = '.*[A-Z].*'
//小寫字母
const REG_LOWERCASE = '.*[a-z].*'
//特殊符號
const REG_SYMBOL = ".*[~!@#$%^&*()_+|<>,.?/:;'\\[\\]{}\"]+.*"
/*** 校驗密碼是否符合條件* @param password 密碼* @param username 用戶名*/
export const checkPasswordRule = (password, username) => {if(password === '' ) {return "密碼不能為空"}else if (password.length < 8 || password.length > 20) {return "密碼長度應(yīng)大于8小于20"} if(username && password.indexOf(username) !== -1) {return "請勿包含用戶名"}if(isContinuousChar(password)) {return "請勿包含3個及以上相同或連續(xù)的字符"}let i = 0if(password.match(REG_NUMBER)) i++if(password.match(REG_UPPERCASE)) i++if(password.match(REG_LOWERCASE)) i++if(password.match(REG_SYMBOL)) i++if(i<2) {return "數(shù)字、小寫字母、大寫字母、特殊字符,至少包含兩種";}return "校驗通過"
}/*** 是否包含3個及以上相同或字典連續(xù)字符*/
const isContinuousChar = (password) => {let chars = password.split('')let charCode = []for(let i=0; i<chars.length-2; i++) {charCode[i] = chars[i].charCodeAt(0)}for(let i=0; i<chars.length-2; i++) {let n1 = charCode[i]let n2 = charCode[i+1]let n3 = charCode[i+2]//判斷重復(fù)字符if(n1 == n2 && n2 == n3) {return true}//判斷連續(xù)字符: 正序+倒序if((n1 + 1 == n2 && n2 + 2 == n3) || (n1 - 1 == n2 && n2 - 2 == n3)) {return true}}return false
}/*** 密碼強(qiáng)度校驗*//*** 長度* @param str */
const length = (str) => { if(str.length<5){ return 5;}else if(str.length<8){return 10;}else{return 25;}
}/*** 字母* @param str */
const letters = (str)=> {let count1 = 0, count2 = 0for(let i=0; i<str.length; i++) {if(str.charAt(i) >= 'a' && str.charAt(i) <= 'z'){count1++}if(str.charAt(i) >= 'A' && str.charAt(i) <= 'Z'){count2++}}if(count1==0 && count2==0) {return 0}if(count1!=0 && count2!=0) {return 20}return 10
}/*** 數(shù)字* @param str */
const numbers = (str)=> {let count = 0for(let i=0; i<str.length; i++) {if(str.charAt(i) >= '0' && str.charAt(i) <= '9'){count++}}if(count==0) {return 0}if(count==1) {return 10}return 20
}/*** 符號* @param str */
const symbols = (str)=> {let count = 0for(let i=0; i<str.length; i++) {if(str.charCodeAt(i)>=0x21 && str.charCodeAt(i)<=0x2F ||str.charCodeAt(i)>=0x3A && str.charCodeAt(i)<=0x40 ||str.charCodeAt(i)>=0x5B && str.charCodeAt(i)<=0x60 ||str.charCodeAt(i)>=0x7B && str.charCodeAt(i)<=0x7E ){count++;}}if(count==0) {return 0}if(count==1) {return 10}return 25
}/*** 得分機(jī)制* @param str */
const rewards = (str) => {let letter = letters(str)let number = numbers(str)let symbol = symbols(str)if(letter>0 && number>0 && symbol==0){    //字母和數(shù)字return 2;}if(letter==10 && number>0 && symbol>0){    //字母、數(shù)字和符號return 3;}if(letter==20 && number>0 && symbol>0){   //大小寫字母、數(shù)字和符號return 5;}return 0;
}/*** 最終評分* @param str */
export const level = (str) => {let lengths=length(str);//長度let letter=letters(str);//字母let number=numbers(str);//數(shù)字let symbol=symbols(str);//符號let reward=rewards(str);//獎勵let sum = lengths+letter+number+symbol+rewardif(sum>=80) {return '非常強(qiáng)'}else if (sum>=60) {return "強(qiáng)"}else if(sum>=40) {return '一般'}else if(sum>=25) {return '弱'}else {return "非常弱"}
}

常用的密碼校驗正則和 Regex 正則表達(dá)式

包含英文字母大小寫、數(shù)字和特殊符號的 8-16 位組合

/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[._~!@#$^&*])[A-Za-z0-9._~!@#$^&*]{8,16}$/g

2. 登錄頁面

2.1 登錄頁面——接口

api/index.js

/*--------- 用戶登錄 ---------*///登錄
export const reqUserLogin = (data) => requests({url: '/user/passport/login', method: 'post', data: data})//登陸后獲取用戶信息(需要帶著用戶的token向服務(wù)器要用戶信息)
export const reqUserInfo = () => requests({url: '/user/passport/auth/getUserInfo',method: 'get'})//退出登錄
export const reqLogout = () => requests({url: '/user/passport/logout',method: 'get'})

store/user.js

import { reqUserLogin } from "@/api"//home模塊的Vuex模塊
const state = {//state中數(shù)據(jù)默認(rèn)初始值別瞎寫,根據(jù)接口的返回值進(jìn)行初始化token: localStorage.getItem("TOKEN"),userInfo: {}
}
const mutations = {USER_LOGIN(state, token){state.token = token},USER_INFO(state, data) {state.userInfo = data},USER_CLEAR(state) {//清除本地數(shù)據(jù)state.token = ""state.userInfo = {}localStorage.removeItem("TOKEN")}
}
const actions = {//用戶登錄async userLogin({commit}, userFrom) {let result = await reqUserLogin(userFrom) //服務(wù)器下發(fā)token,用戶唯一標(biāo)識符(uuid)//將來經(jīng)常通過帶token找服務(wù)器要用戶信息進(jìn)行展示if(result.code == 200) {//token存入vuexcommit("USER_LOGIN", result.data.token)//持久化存儲tokenlocalStorage.setItem('TOKEN', result.data.token)return 'ok'} else {return Promise.reject(new Error(result.message))}},//獲取用戶信息async getUserInfo({commit}) {let result = await reqUserInfo()if(result.code == 200) {commit("USER_INFO", result.data)return 'ok'} else {return Promise.reject(new Error(result.message))}},//退出登錄async userLogout({commit}) {let result = await reqLogout()if(result.code == 200) {commit("USER_CLEAR", result.data)return 'ok'} else {return Promise.reject(new Error(result.message))}}
}export default{state,getters,mutations,actions
}

2.2 登錄——token

登錄成功的時候,后臺為了區(qū)分你這個用戶是誰-服務(wù)器下發(fā)token[令牌:唯一標(biāo)識符]

一般登錄成功服務(wù)器會下發(fā)token,前臺持久化存儲token,[帶著token找服務(wù)器要用戶信息進(jìn)行展示]

Vuex不是持久化存儲,如果token存儲在Vuex,頁面一刷新token數(shù)據(jù)就沒有了,所以使用localStorage存儲

登陸組件methods登陸函數(shù)userLogin

methods: {async userLogin() {try {const {phone, password} = thisphone && password && await this.$store.dispatch('userLogin', {phone,password})//登陸成功,有query參數(shù),就跳到query參數(shù)指定的路由,無query參數(shù),跳到home組件//這個query參數(shù)是導(dǎo)航守衛(wèi)設(shè)置的,next("/login?redirect="+toPath),toPath是原路由let toPath = this.$route.query.redirect || '/home'this.$router.push(toPath)} catch (error) {alert(error.message)}}}

登錄成功后跳轉(zhuǎn)到指定路由或是首頁,若是跳轉(zhuǎn)到首頁,在首頁獲取用戶信息,向服務(wù)器請求用戶信息,需要攜帶token

view/home/index.vue

mounted() {
// 觸發(fā)vuex的異步action調(diào)用, 從mock接口請求數(shù)據(jù)到state中
this.$store.dispatch("getFloorList")//獲取用戶信息在首頁展示
this.$store.dispatch("getUserInfo")
},

api/request.js 下的請求攔截器,設(shè)置攜帶token

//配置請求攔截器
requests.interceptors.request.use(config => {//config內(nèi)主要是對請求頭Header配置//比如添加token//1、先判斷uuid_token是否為空if(store.state.detail.uuid_token) {//2、userTempId字段和后端統(tǒng)一config.headers['userTempId'] = store.state.detail.uuid_token}//需要攜帶token帶給服務(wù)器if(store.state.user.token) {config.headers.token = store.state.user.token}//開啟進(jìn)度條nprogress.start()return config;
})

登錄接口返回的token
在這里插入圖片描述

獲取用戶信息接口,攜帶token

在這里插入圖片描述

2.3 登錄頁面——退出登錄

methods: {//退出登錄async logout() {//1.發(fā)請求,通知服務(wù)器退出登錄(清除數(shù)據(jù),如token)//2. 清楚項目當(dāng)中的用戶數(shù)據(jù)(userInfo)try {//退出成功await this.$store.dispatch("userLogout")//回到首頁this.$router.push('/home')} catch (error) {alert(error.message)}}
}

2.4 登錄頁面——全局導(dǎo)航守衛(wèi)

存在的問題:

  1. 多個組件需要展示用戶信息,需要在每一個組件的mounted中觸發(fā) 獲取用戶信息接口函數(shù)
  2. 用戶已經(jīng)登錄,不應(yīng)該再回登陸頁面
  3. 未登錄,不允許跳轉(zhuǎn)到購物車和訂單

流程

在這里插入圖片描述

為什么要判斷name?

因為store中的token是通過localStorage獲取的,token有存放在本地。當(dāng)頁面刷新時,本地token不會消失,所以store中的token也不會消失。但是,store中的其他數(shù)據(jù)(用戶信息等)會清空,此時會出現(xiàn)用戶信息不存在,但是有token,這種情況是不可以訪問其他頁面的,必須先去獲取用戶信息。由于用戶信息是一個對象,所以我們通過它的一個屬性name判斷用戶信息是否存在。
所以不僅要判斷token,還要判斷用戶信息

router/index.js全局前置守衛(wèi)代碼

//設(shè)置全局導(dǎo)航前置守衛(wèi)
router.beforeEach(async(to, from, next) =>  {let token = store.state.user.tokenlet name = store.state.user.userInfo.name//1、有token代表登錄,全部頁面放行if(token){//1.1登陸了,不允許前往登錄頁if(to.path==='/login'){next('/home')} else{//1.2、因為store中的token是通過localStorage獲取的,token有存放在本地// 當(dāng)頁面刷新時,token不會消失,但是store中的其他數(shù)據(jù)會清空,// 所以不僅要判斷token,還要判斷用戶信息//1.2.1、判斷倉庫中是否有用戶信息,有放行,沒有派發(fā)actions獲取信息if(name)next()else{//1.2.2、如果沒有用戶信息,則派發(fā)actions獲取用戶信息try{await store.dispatch('getUserInfo')next()}catch (error){//1.2.3、獲取用戶信息失敗,原因:token過期//清除前后端token,跳轉(zhuǎn)到登陸頁面await store.dispatch('logout')next('/login')}}}}else{//2、未登錄,首頁或者登錄頁可以正常訪問//2. 未登錄,支付頁面、訂單頁let toPath = to.pathif(toPath.indexOf('/pay') !== -1 || toPath.indexOf('/trade')!==-1 || toPath.indexOf('/center')!==-1) {alert("請先登錄")//登錄成功后,回到原頁面,源地址存儲在地址欄中next("/login?redirect="+toPath)} else {// 其他可以正常訪問next()}}
})

2.5 路由獨享守衛(wèi)

全局導(dǎo)航守衛(wèi)已經(jīng)幫助我們限制未登錄的用戶不可以訪問相關(guān)頁面。但是還會有一個問題。
例如:

用戶已經(jīng)登陸,用戶在home頁直接通過地址欄訪問trade結(jié)算頁面,發(fā)現(xiàn)可以成功進(jìn)入該頁面,正常情況,用戶只能通過在shopcart頁面點擊去結(jié)算按鈕才可以到達(dá)trade頁面。我們可以通過路由獨享守衛(wèi)解決該問題

路由獨享的守衛(wèi):只針對一個路由的守衛(wèi),所以該守衛(wèi)會定義在某個路由中。
以上面問題為例,我們可以通過路由獨享的守衛(wèi)解決。
在trade路由信息中加入路由獨享守衛(wèi)

//交易組件{name: 'Trade',path: '/trade',meta: {showFooter: true},component:  () => import('@/views/Trade'),//路由獨享首位beforeEnter: (to, from, next) => {//購物車頁面才可進(jìn)入交易頁面if(from.path ===  '/shopcart' ){next()}else{next(false)}}},//支付組件{path: '/pay',component: () => import('@/views/Pay'),meta: {showFooter: true},//路由獨享守衛(wèi)beforeEnter: (to, from, next) => {if(from.path === '/trade'){next()} else {next(false)}}   },

上面的代碼已經(jīng)實現(xiàn)了trade路由只能從shopcart路由跳轉(zhuǎn)。next(false)指回到from路由。
但是,上面的代碼還會有bug,就是當(dāng)我們在shopcart頁面通過地址欄訪問trade時還是會成功。正常情況應(yīng)該是只有當(dāng)我們點擊去結(jié)算按鈕后才可以進(jìn)入到trade頁面。(這只是我個人觀點)
解決辦法:
在shopcart路由信息meta中加一個flag,初始值為false。當(dāng)點擊去結(jié)算按鈕后,將flag置為true。在trade的獨享路由守衛(wèi)中判斷一下flag是否為true,當(dāng)flag為true時,代表是通過點擊去結(jié)算按鈕跳轉(zhuǎn)的,所以就放行。
shopcart路由信息

 //購物車{path: "/shopcart",component: () => import('@/views/ShopCart'),meta:{showFooter: true,flag: false},},

shopcart組件去結(jié)算按鈕觸發(fā)事件

toTrade(){this.$route.meta.flag = truethis.$router.push('/trade')
}

trade路由信息

{name: 'Trade',path: '/trade',meta: {showFooter: true},component:  () => import('@/views/Trade'),//路由獨享首位beforeEnter: (to, from, next) => {//購物車頁面才可進(jìn)入交易頁面if(from.path ===  '/shopcart'&& from.meta.flag === true){from.meta.flag = falsenext()}else{next(false)}}
},

注意,判斷通過后,在跳轉(zhuǎn)之前一定要將flag置為false。

2.6 組件導(dǎo)航守衛(wèi)

支付成功頁面,設(shè)置只有通過支付頁面后才能訪問

<script>export default {name: 'PaySuccess',//組件內(nèi)守衛(wèi):通過路由規(guī)則,進(jìn)入該組件時被調(diào)用//不能獲取組件實例——this,因為守衛(wèi)執(zhí)行前,組件實例還未被創(chuàng)建beforeRouteEnter (to, from, next) {if(from.path == '/pay') {next()}else {next(false)}},}
</script>

2.7 全局封裝API

推薦:API封裝的具體步驟

若是想在組件里調(diào)用請求接口,而不通過Vuex來調(diào)用請求接口,該如何統(tǒng)一配置api接口,一次調(diào)用即可,而不須一個個引入

api/index.js文件是請求接口

main.js

//統(tǒng)一接口api
import * as api from '@/api'
new Vue({render: (h) => h(App),beforeCreate() {//全局事件總線Vue.prototype.$bus = this;Vue.prototype.$api = api;},
})

組件內(nèi)發(fā)請求

methods: {//提交訂單submitOrder() {console.log(this.$API.reqSubmitOrder());}
}

3. 二維碼生成

qrcode

import QRCode from "qrcode"
//立即支付彈出框
async openMsgBox(){//生成二維碼(地址)let code = await QRCode.toDataURL(this.payInfo.codeUrl)this.$alert(`<img src=${code} />`, '請微信支付', {dangerouslyUseHTMLString: true,center: true,showCancelButton: true,confirmButtonText: '已支付成功',cancelButtonText: '支付遇見問題',showClose: false})
}

4. 二級路由拆分(個人中心)

菜單欄

<dt><i>·</i> 訂單中心</dt><dd><router-link to="/center/myorder">我的訂單</router-link></dd><dd><router-link to="/center/grouporder">團(tuán)購訂單</router-link></dd>
</dt>

個人中心路由

//個人中心
{path: '/center',component:  () => import('@/views/Center'),children: [{//二級路由要么不寫/,要么寫全:'/center/myorder'path: 'myorder',component: () => import('@/views/Center/MyOrder')},{path: 'groupbuy',component: () => import('@/views/Center/GroupOrder'),},//默認(rèn)顯示{path: '',redirect: 'myorder'}]
}

{ path: '', redirect: 'myorder' }表示當(dāng)我們訪問center路由時,center中的router-view部分默認(rèn)顯示myorder二級路由內(nèi)容。
我們的子路由最好放在父路由文件夾下,如下所示。
在這里插入圖片描述

注意:當(dāng)某個路由有子級路由時,父級路由須要一個默認(rèn)的路由,因此父級路由不能定義name屬性

5. 圖片懶加載

在網(wǎng)絡(luò)不好時,每個圖片都有一個基礎(chǔ)的默認(rèn)圖片,即在請求服務(wù)器結(jié)束前 加載默認(rèn)設(shè)置的圖片

懶加載vue-lazyload插件官網(wǎng)
插件的使用直接參考官方教程,很簡單。

npm i vue-lazyload

vue使用插件的步驟,main.js

import VueLazyload from "vue-lazyloadimport loadingImg from '@/assets/images/loading.jpeg'
Vue.use(VueLazyload, {//懶加載默認(rèn)的圖片loading: loadingImg
})

使用懶加載

<img v-lazy="good.defaultImg" />

在使用中報錯 如下圖所示:

img

因為該 模塊 版本問題, 可安裝低版本的 vue-lazyload 來解決該問題:

# 先寫在原有的安裝
npm uninstall vue-lazyload --save# 再安裝低版本的
npm install vue-lazyload@1.3.3 --save

6. 路由懶加載

路由懶加載

當(dāng)打包構(gòu)建應(yīng)用時,JavaScript 包會變得非常大,影響頁面加載。如果我們能把不同路由對應(yīng)的組件分割成不同的代碼塊,然后當(dāng)路由被訪問的時候才加載對應(yīng)組件,這樣就會更加高效。

// 將
// import Center from '@/views/Center'
// 替換成
const Center = () => import('./views/Center')
{path: '/center',component: Center,meta: { showFooter: true },
}

component (和 components) 配置接收一個返回 Promise 組件的函數(shù),Vue Router 只會在第一次進(jìn)入頁面時才會獲取這個函數(shù),然后使用緩存數(shù)據(jù)。

7. 項目上線

7.1 打包

npm run build

會生成dist打包文件。

在這里插入圖片描述

dist就是我們打包好的項目文件

在這里插入圖片描述

dist文件下的js文件存放我們所有的js文件,并且經(jīng)過了加密,并且還會生成對應(yīng)的map文件。

**map文件作用:**因為代碼是經(jīng)過加密的,如果運行時報錯,輸出的錯誤信息無法準(zhǔn)確得知時那里的代碼報錯。有了map就可以像未加密的代碼一樣,準(zhǔn)確的輸出是哪一行那一列有錯。

當(dāng)然map文件也可以去除(map文件大小還是比較大的)
vue.config.js配置productionSourceMap: false即可。
注意:vue.config.js配置改變,需要重啟項目
map

7.2 購買云服務(wù)器

阿里云、騰訊云
記得重置密碼
設(shè)置安全組 開放端口
利用xshell等工具登錄服務(wù)器

nginx
1、如何通過服務(wù)器IP地址直接訪問到項目?
在服務(wù)器上部署dist文件地址:/root/project/dist
2、項目數(shù)據(jù)來自于哪個服務(wù)器
通過nginx從數(shù)據(jù)服務(wù)器拿數(shù)據(jù)
配置Nginx:在etc文件下

cd etc
ls

安裝nginx:yum install nginx

cd nginx

存在文件nginx.conf
編輯vim nginx.conf
解決第一個問題:

location /{
root /root/project/dist;
index index.html;
try_files $uri/ /index.html;
}

解決第二個問題:

location /api{proxy_pass http://39.983123.211;

啟動nginx服務(wù)器:service nginx start

http://www.risenshineclean.com/news/38393.html

相關(guān)文章:

  • 建立一個小程序多少錢小紅書關(guān)鍵詞排名優(yōu)化
  • 去哪找人做網(wǎng)站seo技術(shù)網(wǎng)網(wǎng)
  • 人大網(wǎng)站建設(shè)方案湖南省人民政府
  • 機(jī)械類畢業(yè)設(shè)計代做網(wǎng)站推薦官網(wǎng)seo關(guān)鍵詞排名系統(tǒng)
  • 哪里有做雜志的免費模板下載網(wǎng)站網(wǎng)絡(luò)服務(wù)合同
  • 做網(wǎng)站買空間谷歌廣告開戶
  • 浙江建筑信息監(jiān)管平臺seo建站公司
  • 做網(wǎng)站視頻 上傳到哪兒百度手機(jī)版下載
  • 網(wǎng)站招工費怎么做會計分錄企業(yè)推廣策劃
  • 幫人做網(wǎng)站在徐州被敲詐五萬網(wǎng)站開發(fā)軟件
  • 網(wǎng)站收錄做關(guān)鍵詞排名百度網(wǎng)站優(yōu)化
  • 自助微信網(wǎng)站芭蕉視頻app無限次數(shù)
  • 互聯(lián)網(wǎng)網(wǎng)站建設(shè)情況統(tǒng)計表關(guān)鍵詞資源
  • 哈爾濱微網(wǎng)站建設(shè)太原seo優(yōu)化
  • 哪些網(wǎng)站做批發(fā)衣服電子商務(wù)網(wǎng)站建設(shè)與維護(hù)
  • 南通做網(wǎng)站baidu tg做網(wǎng)站公司排名
  • 電子商城網(wǎng)站開發(fā)教程網(wǎng)絡(luò)推廣引流是做什么工作
  • bootstrap微網(wǎng)站模板下載新聞播報最新
  • 網(wǎng)站域名備案資料seo客服
  • 青島正規(guī)的網(wǎng)站建設(shè)公司沈陽網(wǎng)頁建站模板
  • 企業(yè)宣傳網(wǎng)站制作外鏈系統(tǒng)
  • 做創(chuàng)意ppt網(wǎng)站有哪些網(wǎng)頁在線生成
  • asp汽車租憑網(wǎng)站源碼搜索引擎推廣的關(guān)鍵詞
  • 西安 網(wǎng)站搭建深圳seo網(wǎng)絡(luò)優(yōu)化公司
  • wordpress tag 去掉優(yōu)化公司排行榜
  • 無錫企業(yè)網(wǎng)站的建設(shè)競價廣告是怎么推廣的
  • 引流軟件下載站網(wǎng)推和地推的區(qū)別
  • 網(wǎng)站建設(shè)的日常工作有什么做個公司網(wǎng)站多少錢
  • 購物網(wǎng)站的功能網(wǎng)站建設(shè)策劃書
  • 網(wǎng)站后臺管理系統(tǒng)欄目位置天津疫情最新消息