前言: 作為後臺程序猿介入前端開發,雖然有一定的經驗,但實際操作起來,也會碰到不少問題。我個人認為最有效的方式,就是動手,動手,動手,然後看文檔。下面就隨著我一起來進入vue-element-admin的開發之路吧。
這裡我們不會從 0 開始,本項目是選擇依託開源的vue-element-admin作為起步的框架,結合自身後臺開發的專長,從而理解一些常用的方法。
目錄結構├── build # 構建相關
├── plop-templates # 基本模板
├── public # 靜態資源
│ │── favicon.ico # favicon圖標
│ └── index.html # html模板
├── src # 原始碼
│ ├── api # 所有請求
│ ├── assets # 主題 字體等靜態資源
│ ├── components # 全局公用組件
│ ├── directive # 全局指令
│ ├── filters # 全局 filter
│ ├── icons # 項目所有 svg icons
│ ├── lang # 國際化 language
│ ├── layout # 全局 layout
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── styles # 全局樣式
│ ├── utils # 全局公用方法
│ ├── vendor # 公用vendor
│ ├── views # views 所有頁面
│ ├── App.vue # 入口頁面
│ ├── main.js # 入口文件 加載組件 初始化等
│ └── permission.js # 權限管理
└── settings.js # 頁面自定義顯示配置按鈕
├── tests # 測試
├── .env.xxx # 環境變量配置
├── .eslintrc.js # eslint 配置項
├── .babelrc # babel-loader 配置
├── .travis.yml # 自動化CI配置
├── vue.config.js # vue-cli 配置
├── postcss.config.js # postcss 配置
└── package.json # package.json這裡去掉了原始項目的 mock 模擬數據目錄,採用的是後臺的數據接口。
登錄登錄是首先要做的事情,涉及到要考慮的問題還是比較多。我們這裡從登錄先講起,這裡要講到兩種登錄,密碼登錄、簡訊驗證碼登錄。
帳戶登錄界面簡訊登錄界面
對應的代碼
<div class="title-container">
<h3 class="title">系 統 登 錄</h3>
<div class="login_header">
<a
@click="loginForm.login_type = 0"
:class="{ active: loginForm.login_type == 0 }"
>密碼登錄</a
>
<a
@click="loginForm.login_type = 1"
:class="{ active: loginForm.login_type == 1 }"
>簡訊登錄</a
>
</div>
</div>【代碼解讀】
這裡使用<a>標籤配合@click點擊事件,此點擊事件十分簡單,就是給login_type賦值。1. 當'login_type == 0'表示密碼登錄
2. 當'login_type == 1'表示簡訊登錄為了更明確當前的面板指向,加上:class動態樣式,通過active樣式的值來實現文章下劃線的效果。
激活下劃線樣式
.active樣式如下
.active {
color: #fff;
padding-bottom: 10px;
border-bottom: 3px solid #fff;
}
密碼登錄先來看看密碼登錄的代碼
<!-- 在login_type==0時此板塊顯示 其他時候此板塊不顯示 -->
<div v-if="loginForm.login_type == 0" class="Cbody_item">
<el-form-item prop="loginname">
<span class="svg-container">
<svg-icon icon-class="user" />
</span>
<el-input
ref="loginname"
v-model="loginForm.loginname"
placeholder="工號/用戶名/手機號"
name="loginname"
type="text"
tabindex="1"
autocomplete="on"
/>
</el-form-item>
<el-tooltip
v-model="capsTooltip"
content="大寫鎖定已開啟"
placement="right"
manual
>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password" />
</span>
<el-input
:key="passwordType"
ref="password"
v-model="loginForm.password"
:type="passwordType"
placeholder="登錄密碼(初始密碼123456)"
name="password"
tabindex="2"
autocomplete="on"
@keyup.native="checkCapslock"
@blur="capsTooltip = false"
@keyup.enter.native="handleLogin"
/>
<span class="show-pwd" @click="showPwd">
<svg-icon
:icon-class="passwordType === 'password' ? 'eye' : 'eye-open'"
/>
</span>
</el-form-item>
</el-tooltip>
</div>這裡使用的是element-ui:表單,官方文檔已經描寫的相當清楚了,這裡就不繼續贅述。
在表單裡面使用了<svg>標籤引入 icon 圖標,網上關於<svg>此類的文章也挺多,這裡就稍微演示一下如何使用。
icon圖標哪裡找,阿里巴巴這裡有。
例如我們這裡需要用到微信的圖標,搜索一下:搜索
從上找到你看中的 icon,點擊下載。
下載
複製 SVG 代碼
<svg t="1611990623145" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14306" width="200" height="200"><path d="M826.2 823.3C887.9 785.4 928 723.9 928 654.4c0-111.8-104.1-203.1-234.7-208.2-14.1-132.3-141.7-235.8-297-235.8C231.5 210.4 98 326.9 98 470.5c0 85.8 47.7 161.9 121.1 209.3 8.2 5.3-27.3 80-18.5 84.6 14.9 7.7 74.7-55.6 91-50.2 32.5 10.6 67.8 16.4 104.6 16.4 18.4 0 36.5-1.5 54-4.3 34.6 79.6 125.1 136.5 231.3 136.5 29 0 56.9-4.2 82.7-12 8.5-2.6 65.9 48.4 74 45.1 13.1-5.3-23.7-65.5-12-72.6zM590.8 603c-14.3 0-25.9-11.7-25.9-26.2s11.6-26.2 25.9-26.2c14.3 0 25.9 11.7 25.9 26.2S605.1 603 590.8 603z m-155.6 51.4c0 7.7 0.5 15.3 1.5 22.8-13.1 1.8-26.6 2.8-40.4 2.8-34.4 0-67.2-6-96.9-16.7-3-1.1-13.5-4.3-19.3 0C267.3 672.9 250 693 250 693s6.3-14.6 8.4-35.9c1-9.7-13.8-16.6-16.7-18.6-56-38.2-91.9-101.4-91.9-167.1 0-115.2 110.3-208.6 246.4-208.6 127.1 0 231.7 81.4 245 186-116.8 16.3-206 102.1-206 205.6zM746.4 603c-14.3 0-25.9-11.7-25.9-26.2s11.6-26.2 25.9-26.2c14.3 0 25.9 11.7 25.9 26.2 0.1 14.5-11.5 26.2-25.9 26.2z" p-id="14307"></path><path d="M500 419.8c21.5 0 38.9-17.6 38.9-39.3 0-21.7-17.4-39.3-38.9-39.3s-38.9 17.6-38.9 39.3c0 21.7 17.4 39.3 38.9 39.3zM292.5 419.8c21.5 0 38.9-17.6 38.9-39.3 0-21.7-17.4-39.3-38.9-39.3s-38.9 17.6-38.9 39.3c0 21.7 17.4 39.3 38.9 39.3z" p-id="14308"></path></svg>然後在 src\icons\svg 目錄下新建一個 svg 文件,例如這裡叫icon-wechat.svg。然後在頁面中就可以通過<svg-icon icon-class="icon-wechat" />來使用了。可以使用font-size ,color等標籤來控制其樣式。
說明:從iconfont複製的svg代碼需要刪除裡面的 fill="#",
這樣它才會使用當前的顏色`currentColor`icon-wechat下面簡單解讀一下其工作原理。
在 src\components 組件下創建了一個 SvgIcon 組件
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>向外暴露了兩個屬性,其中icon-class是必填項,class-name是自定義樣式名稱。
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},通過 computed 監控 icon 的名字和其自定義的樣式,當沒有指定自定義樣式時候,會採用默認樣式,否則會再加上自定義 class
computed: {
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
}下面是固定寫法,默認的樣式。
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>在 src\icons 中的 index.js 中引入 svg 組件
import IconSvg from '@/components/IconSvg'
使用全局註冊 icon-svg
Vue.component('icon-svg', IconSvg)
這樣就可以在項目中任意地方使用。
為了便於集中管理圖標,所有圖標均放在 @/icons/svg。const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)require.context 有三個參數:
useSubdirectories:是否檢索子目錄這樣就可自行添加或者刪除圖標,所有圖標都會被自動導入,無需手動操作。
最後在@/main.js中引入import './icons'這樣在任意頁面就可以成功使用組件了<svg-icon icon-class="icon-wechat" class-name="wechat" />
下面我們再看看代碼中一些其它屬性ref: ref 被用來給元素或子組件註冊引用信息。引用信息將會註冊在父組件的 $refs 對象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子組件上,引用就指向組件。refs: refs 直接在實例裡面獲取 ref 在上面綁定的元素。所以後面在對 form 表單或單個 input 使用校驗,就可以採取this.$refs.loginForm.validate或者this.$refs.loginForm.validateField。tabindex: 讓普通 dom 元素變為可聚焦的元素;讓普通 dom 元素可以參與順序鍵盤導航(通常使用 Tab 鍵,因此得名)。它接受一個整數作為值,具有不同的結果,具體取決於整數的值:
autocomplete: 默認為 on,其含義代表是否讓瀏覽器自動記錄輸入的值。
tabindex=負值 (通常是 tabindex=「-1」),表示元素是可聚焦的,但是不能通過鍵盤導航來訪問到該元素,用 JS 做頁面小組件內部鍵盤導航的時候非常有用。
tabindex="0" ,表示元素是可聚焦的,並且可以通過鍵盤導航來聚焦到該元素,它的相對順序是當前處於的 DOM 結構來決定的。
tabindex=正值,表示元素是可聚焦的,並且可以通過鍵盤導航來訪問到該元素;它的相對順序按照 tabindex 的數值遞增而滯後獲焦。如果多個元素擁有相同的 tabindex,它們的相對順序按照他們在當前 DOM 中的先後順序決定。@keyup.native="checkCapslock" 和 @blur="capsTooltip = false":用於判斷用戶輸入大寫提示
checkCapslock(e) {
const { key } = e
this.capsTooltip = key && key.length === 1 && key >= 'A' && key <= 'Z'
},@keyup.enter.native="handleLogin":當點擊鍵盤enter鍵時,觸發handleLogin事件<el-tooltip>: 查看官方文檔Tooltip 文字提示@click.native.prevent: 給vue組件綁定事件時候,必須加上native ,否則會認為監聽的是來自Item組件自定義的事件。prevent 是用來阻止默認的 ,相當於原生的event.preventDefault()簡訊登錄
這裡在獲取驗證碼的時候,對手機號進行了校驗,避免不必要的簡訊發送。
簡訊發送成功後,發送簡訊按鈕置灰並且倒計時一分鐘,刷新瀏覽器倒計時依舊存在,除非清除瀏覽器緩存(因為存儲在Cookie中了)。
<!-- 在login_type==1時此板塊顯示 其他時候此板塊不顯示 -->
<div v-if="loginForm.login_type == 1" class="Cbody_item">
<el-form-item prop="loginname">
<span class="svg-container">
<!-- <svg-icon icon-class="el-icon-phone" /> -->
<i class="el-icon-phone" />
</span>
<el-input
ref="loginname"
v-model="loginForm.loginname"
placeholder="請輸入手機號碼"
name="loginname"
type="text"
tabindex="1"
autocomplete="on"
/>
</el-form-item>
<el-tooltip
v-model="capsTooltip"
content="大寫鎖定已開啟"
placement="right"
manual
>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password" />
</span>
<el-input
ref="password"
v-model="loginForm.password"
type="text"
placeholder="請輸入驗證碼"
name="password"
tabindex="2"
maxlength="6"
auto-complete="on"
@keyup.native="checkCapslock"
@blur="capsTooltip = false"
@keyup.enter.native="handleLogin"
/>
<span class="show-pwd">
<el-button
:loading="sending"
:disabled="sendDisabled"
size="small"
@click.native.prevent="onSendSms"
>{{ sendButtonText }}</el-button
>
</span>
</el-form-item>
</el-tooltip>
</div>發送簡訊代碼如下:
async onSendSms() {
this.$refs.loginForm.validateField('loginname', err => {
if (!err) {
// 先校驗手機號是否存在
this.sendDisabled = true
this.sending = true
const tel = this.loginForm.loginname
checkTel(tel)
.then(res => {
if (!res.flag) {
// 手機號不存在
this.$message.error(resTel.message)
this.sending = true
return false
} else {
sendSms(tel)
.then(() => {
this.$message.success('簡訊發送成功,請注意查收')
Cookie.set('last-send-time', new Date().toISOString())
this.timer = 60
this.sending = true
})
.catch(e => {
this.$message.error('網絡異常')
this.sendDisabled = false
console.log(e)
})
.finally(() => (this.sending = false))
}
})
.catch(err => {
this.sending = false
this.sendDisabled = false
console.log(err)
return false
})
} else {
console.log('error submit!!')
return false
}
})
},倒計時功能代碼如下:
watch: {
$route: {
handler: function(route) {
const query = route.query
if (query) {
this.redirect = query.redirect
this.otherQuery = this.getOtherQuery(query)
}
},
immediate: true
},
timer: {
handler(num) {
if (num > 0) {
setTimeout(() => {
this.timer = --num
this.sendDisabled = this.timer == 0 ? false : true
}, 1000)
}
}
},
'loginForm.login_type': {
handler(newVal) {
this.loginForm.loginname = ''
this.loginForm.password = ''
},
deep: true
}
},其中的timer對應的是監控倒計時timer的值,當其值>0時,使用定時器實現倒計時功能,當timer==0時,將發送簡訊按鈕變為可用狀態this.sendDisabled =false
順便將watch裡面的另外兩處代碼解讀一下:
$route:獲取登錄後重定向的path路徑,例如:redirect: "/dashboard"。this.getOtherQuery(query):getOtherQuery(query) {
Object.keys:獲取所有的請求對象的KEY組成一個數組。reduce:ES6中的語法
return Object.keys(query).reduce((acc, cur) => {
if (cur !== 'redirect') {
acc[cur] = query[cur]
}
return acc
}, {})
}reduce 為數組中的每一個元素依次執行回調函數,不包括數組中被刪除或從未被賦值的元素,接受四個參數:初始值(或者上一次回調函數的返回值),當前元素值,當前索引,調用 reduce 的數組。
當數組中的key不等於redirect時候,就將其存入對象中,最終返回acc,其中acc初始化值為{}對象。例如{page:1,size:10}
loginForm.login_type:目的是在切換登錄方式後,將表單清空登錄功能實現
這樣就獲得了跳轉的路徑以及查詢的參數
immediate:true 代表如果在 wacth 裡聲明了之後,就會立即先去執行裡面的handler方法handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
this.$store
.dispatch('user/login', this.loginForm)
.then(() => {
this.$router.push({
path: this.redirect || '/',
query: this.otherQuery
})
this.loading = false
})
.catch(() => {
this.loading = false
})
} else {
return false
}
})
},當登錄路徑中的path不存在時,進行初始化path: this.redirect || '/',再經過permission.js中的router.beforeEach攔截,在沒有token的情況下,都會跳轉到登錄頁
next(`/login?redirect=${to.path}`)登錄成功後,重定向到/即:router中定義的首頁
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
component: () => import('@/views/dashboard/index'),
name: 'Dashboard',
meta: { title: '首頁', icon: 'dashboard', affix: true }
}
]
},
login.vue完整代碼<template>
<div class="login-container">
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginForm.login_type == 0 ? loginRules : loginRulesTel"
class="login-form"
autocomplete="on"
label-position="left"
>
<div class="title-container">
<h3 class="title">系 統 登 錄</h3>
<div class="login_header">
<a
@click="loginForm.login_type = 0"
:class="{ active: loginForm.login_type == 0 }"
>密碼登錄</a
>
<a
@click="loginForm.login_type = 1"
:class="{ active: loginForm.login_type == 1 }"
>簡訊登錄</a
>
</div>
</div>
<div class="login_content">
<!-- 在login_type==0時此板塊顯示 其他時候此板塊不顯示 -->
<div v-if="loginForm.login_type == 0" class="Cbody_item">
<el-form-item prop="loginname">
<span class="svg-container">
<svg-icon icon-class="user" />
</span>
<el-input
ref="loginname"
v-model="loginForm.loginname"
placeholder="工號/用戶名/手機號"
name="loginname"
type="text"
tabindex="1"
autocomplete="on"
/>
</el-form-item>
<el-tooltip
v-model="capsTooltip"
content="大寫鎖定已開啟"
placement="right"
manual
>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password" />
</span>
<el-input
:key="passwordType"
ref="password"
v-model="loginForm.password"
:type="passwordType"
placeholder="登錄密碼(初始密碼123456)"
name="password"
tabindex="2"
autocomplete="on"
@keyup.native="checkCapslock"
@blur="capsTooltip = false"
@keyup.enter.native="handleLogin"
/>
<span class="show-pwd" @click="showPwd">
<svg-icon
:icon-class="passwordType === 'password' ? 'eye' : 'eye-open'"
/>
</span>
</el-form-item>
</el-tooltip>
</div>
<!-- 在login_type==1時此板塊顯示 其他時候此板塊不顯示 -->
<div v-if="loginForm.login_type == 1" class="Cbody_item">
<el-form-item prop="loginname">
<span class="svg-container">
<!-- <svg-icon icon-class="el-icon-phone" /> -->
<i class="el-icon-phone" />
</span>
<el-input
ref="loginname"
v-model="loginForm.loginname"
placeholder="請輸入手機號碼"
name="loginname"
type="text"
tabindex="1"
autocomplete="on"
/>
</el-form-item>
<el-tooltip
v-model="capsTooltip"
content="大寫鎖定已開啟"
placement="right"
manual
>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password" />
</span>
<el-input
ref="password"
v-model="loginForm.password"
type="text"
placeholder="請輸入驗證碼"
name="password"
tabindex="2"
maxlength="6"
auto-complete="on"
@keyup.native="checkCapslock"
@blur="capsTooltip = false"
@keyup.enter.native="handleLogin"
/>
<span class="show-pwd">
<el-button
:loading="sending"
:disabled="sendDisabled"
size="small"
@click.native.prevent="onSendSms"
>{{ sendButtonText }}</el-button
>
</span>
</el-form-item>
</el-tooltip>
</div>
</div>
<el-button
:loading="loading"
type="primary"
style="width: 100%; margin-bottom: 30px"
@click.native.prevent="handleLogin"
>登錄</el-button
>
</el-form>
</div>
</template>
<script>
import { sendSms, checkTel } from '@/api/user'
import Cookie from 'js-cookie'
export default {
name: 'Login',
data() {
const validateUsername = (rule, value, callback) => {
if (value == null || value.trim() == '') {
callback(new Error('用戶名不能為空'))
} else {
callback()
}
}
const validatePassword = (rule, value, callback) => {
if (value == null || value.trim() == '') {
callback(new Error('密碼不能為空'))
} else {
callback()
}
}
const validateCode = (rule, value, callback) => {
if (value == null || value.trim() == '') {
callback(new Error('驗證碼不能為空'))
} else {
callback()
}
}
const validateTel = (rule, value, callback) => {
let reg = /^((13[0-9])|(17[0-1,6-8])|(15[^4,\\D])|(18[0-9]))\d{8}$/
value = value != null ? value.replace(/\s*/g, '') : value
if (value == null || value.length < 1) {
callback(new Error('請輸入手機號'))
} else if (!reg.test(value)) {
callback(new Error('手機號輸入不合法'))
} else {
callback()
}
}
return {
loginForm: {
loginname: '',
password: '',
login_type: 0 //0:密碼登錄 ; 1:簡訊登錄
},
loginRules: {
loginname: [
{
required: true,
trigger: 'blur',
validator: validateUsername
}
],
password: [
{
required: true,
trigger: 'blur',
validator: validatePassword
}
]
},
loginRulesTel: {
loginname: [
{
required: true,
// trigger: 'blur',
validator: validateTel
}
],
password: [
{
required: true,
trigger: 'blur',
validator: validateCode
}
]
},
sending: false,
sendDisabled: false,
timer: 0,
passwordType: 'password',
capsTooltip: false,
loading: false,
showDialog: false,
redirect: undefined,
otherQuery: {}
}
},
computed: {
sendButtonText() {
if (this.timer === 0) {
return '發送驗證碼'
} else {
return `${this.timer}秒後重發`
}
}
},
watch: {
$route: {
handler: function(route) {
const query = route.query
if (query) {
this.redirect = query.redirect
this.otherQuery = this.getOtherQuery(query)
}
},
immediate: true
},
timer: {
handler(num) {
if (num > 0) {
setTimeout(() => {
this.timer = --num
this.sendDisabled = this.timer == 0 ? false : true
}, 1000)
}
}
},
'loginForm.login_type': {
handler(newVal) {
this.loginForm.loginname = ''
this.loginForm.password = ''
},
deep: true
}
},
created() {
const lastSendTime = Cookie.get('last-send-time')
if (lastSendTime) {
const timer =
60 - this.$moment.utc().diff(this.$moment(lastSendTime), 'seconds')
this.timer = timer > 0 ? timer : 0
}
},
mounted() {
if (this.loginForm.loginname === '') {
this.$refs.loginname.focus()
} else if (this.loginForm.password === '') {
this.$refs.password.focus()
}
},
destroyed() {
// window.removeEventListener('storage', this.afterQRScan)
},
methods: {
async onSendSms() {
this.$refs.loginForm.validateField('loginname', err => {
if (!err) {
// 先校驗手機號是否存在
this.sendDisabled = true
this.sending = true
const tel = this.loginForm.loginname
checkTel(tel)
.then(res => {
if (!res.flag) {
// 手機號不存在
this.$message.error(resTel.message)
this.sending = true
return false
} else {
sendSms(tel)
.then(() => {
this.$message.success('簡訊發送成功,請注意查收')
Cookie.set('last-send-time', new Date().toISOString())
this.timer = 60
this.sending = true
})
.catch(e => {
this.$message.error('網絡異常')
this.sendDisabled = false
console.log(e)
})
.finally(() => (this.sending = false))
}
})
.catch(err => {
this.sending = false
this.sendDisabled = false
console.log(err)
return false
})
} else {
console.log('error submit!!')
return false
}
})
},
checkCapslock(e) {
const { key } = e
this.capsTooltip = key && key.length === 1 && key >= 'A' && key <= 'Z'
},
showPwd() {
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.password.focus()
})
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
this.$store
.dispatch('user/login', this.loginForm)
.then(() => {
this.$router.push({
path: this.redirect || '/',
query: this.otherQuery
})
this.loading = false
})
.catch(() => {
this.loading = false
})
} else {
return false
}
})
},
getOtherQuery(query) {
return Object.keys(query).reduce((acc, cur) => {
if (cur !== 'redirect') {
acc[cur] = query[cur]
}
return acc
}, {})
}
}
}
</script>
<style lang="scss">
/* 修復input 背景不協調 和光標變色 */
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
$bg: #283443;
$light_gray: #fff;
$cursor: #fff;
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
.login-container .el-input input {
color: $cursor;
}
}
/* reset element-ui css */
.login-container {
.el-input {
display: inline-block;
height: 47px;
width: 85%;
input {
background: transparent;
border: 0px;
-webkit-appearance: none;
border-radius: 0px;
padding: 12px 5px 12px 15px;
color: $light_gray;
height: 47px;
caret-color: $cursor;
&:-webkit-autofill {
box-shadow: 0 0 0px 1000px $bg inset !important;
-webkit-text-fill-color: $cursor !important;
}
}
}
.el-form-item {
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.1);
border-radius: 5px;
color: #454545;
}
}
</style>
<style lang="scss" scoped>
a {
color: #fff;
margin: 50px;
}
.login_header {
margin-bottom: 30px;
text-align: center;
}
.login_header span {
margin-right: 20px;
cursor: pointer;
}
.Cbody_item {
border: 0px solid #999;
overflow: hidden;
}
.active {
color: #fff;
padding-bottom: 10px;
border-bottom: 3px solid #fff;
}
</style>
<style lang="scss" scoped>
$bg: #2d3a4b;
$dark_gray: #889aa4;
$light_gray: #eee;
.login-container {
min-height: 100%;
width: 100%;
background-color: $bg;
overflow: hidden;
.wechat {
font-size: 1.5em;
color: yellow;
}
.login-form {
position: relative;
width: 520px;
max-width: 100%;
padding: 160px 35px 0;
margin: 0 auto;
overflow: hidden;
}
.tips {
font-size: 14px;
color: #fff;
margin-bottom: 10px;
span {
&:first-of-type {
margin-right: 16px;
}
}
}
.svg-container {
padding: 6px 5px 6px 15px;
color: $dark_gray;
vertical-align: middle;
width: 30px;
display: inline-block;
}
.title-container {
position: relative;
.title {
font-size: 26px;
color: $light_gray;
margin: 0px auto 40px auto;
text-align: center;
font-weight: bold;
}
}
.show-pwd {
position: absolute;
right: 10px;
top: 7px;
font-size: 16px;
color: $dark_gray;
cursor: pointer;
user-select: none;
}
.thirdparty-button {
position: absolute;
right: 0;
bottom: 6px;
}
@media only screen and (max-width: 470px) {
.thirdparty-button {
display: none;
}
}
}
</style>佔坑專用:下篇我們來了解一下登錄後的token鑑權操作