新增手机号登录ui

This commit is contained in:
不做码农 2023-09-07 18:42:24 +08:00
parent aef0b436c4
commit 740061502e
13 changed files with 254 additions and 98 deletions

View File

@ -15,22 +15,23 @@
background: #ffffff;
border-radius: 6px;
width: var(--base-login-width);
position: relative;
}
}
.title {
margin: 0px auto 30px auto;
margin: 10px auto 30px auto;
text-align: center;
// color: #fff;
}
.login-form {
padding: 35px 15px 5px 15px;
padding: 15px 25px 5px 25px;
position: relative;
height: 300px;
height: 210px;
.input-icon {
height: 39px;
height: 30px;
width: 14px;
margin-left: 0px;
}
@ -115,3 +116,6 @@
align-items: center;
justify-content: space-around;
}
@media screen and (max-width: 500px) {
}

View File

@ -20,7 +20,7 @@ $panGreen: #30b08f;
--base-tags-height: 34px;
--base-header-height: 50px;
//登录框宽度
--base-login-width: 320px;
--base-login-width: 360px;
}
/***侧边栏深色配置***/

View File

@ -7,17 +7,17 @@ export default defineComponent({
name: {
type: String,
required: true,
default: '',
default: ''
},
className: {
type: String,
default: '',
default: ''
},
// svg
color: {
type: String,
default: '',
},
default: ''
}
},
setup(props) {
if (props.name?.startsWith('ele')) {
@ -25,9 +25,9 @@ export default defineComponent({
h(
'i',
{
class: 'el-icon',
class: 'el-icon'
},
[h(resolveComponent(props.name.replace('ele-', '')))],
[h(resolveComponent(props.name.replace('ele-', '')))]
)
} else if (props.name != undefined && props.name != '') {
return () =>
@ -38,16 +38,17 @@ export default defineComponent({
'aria-hidden': true,
style: `color: ${props.color}`,
class: `svg-icon ${props.className}`,
'shape-rendering': 'geometricPrecision'
},
h('use', {
'xlink:href': `#icon-${props.name}`,
fill: `${props.color}`,
}),
fill: `${props.color}`
})
)
} else {
return () => h('i')
}
},
}
})
</script>

View File

@ -90,7 +90,7 @@
"topNav": "顶部导航",
"commonFuncs": "常用功能",
"openWatermark": "开启水印",
"workTime": "今日工作时长(分)",
"workTime": "今日工作时长",
"onlineClientNum": "在线设备数"
},
"common": {

View File

@ -11,6 +11,13 @@
"invalidSession": "Invalid session, or session has expired, please log in again.",
"otherLoginWay": "Other",
"register": "Sign up now",
"forgotPwd": "forget"
"forgotPwd": "forget",
"loginway1": "account",
"loginway2": "Phone",
"loginway3": "Scan code",
"phoneCode": "Please enter SMS verification code",
"sendPhoneCode": "Send",
"input_phoneNum": "Please enter phone number",
"tip_scan_code": "Please use the mobile app to scan the code to log in"
}
}

View File

@ -11,6 +11,13 @@
"invalidSession": "无效的会话,或者会话已过期,请重新登录。",
"otherLoginWay": "其他登录方式",
"register": "立即注册",
"forgotPwd": "忘记密码"
"forgotPwd": "忘记密码",
"phoneCode": "请输入短信验证码",
"sendPhoneCode": "发送验证码",
"loginway1": "账号密码",
"loginway2": "手机号",
"loginway3": "扫码登录",
"input_phoneNum": "请输入手机号",
"tip_scan_code": "请使用移动端app扫码登录"
}
}

View File

@ -11,6 +11,13 @@
"invalidSession": "無效的會話,或者會話已過期,請重新登錄。",
"otherLoginWay": "其他登錄方式",
"register": "註冊",
"forgotPwd": "忘記密碼"
"forgotPwd": "忘記密碼",
"loginway1": "賬號密碼",
"loginway2": "手機號",
"loginway3": "掃碼登錄",
"phoneCode": "請輸入短信驗證碼",
"sendPhoneCode": "發送驗證碼",
"input_phoneNum": "請輸入手機號",
"tip_scan_code": "請使用移動端app掃碼登錄"
}
}

View File

@ -71,13 +71,13 @@ export default {
/**
* 是否显示其他登录
*/
showOtherLogin: false,
showOtherLogin: true,
/**
* 默认大小
*/
defaultSize: 'default',
/**
* 默认语言
*/
defaultLang: 'zh-cn'
defaultSize: 'default',
/**
* 默认语言
*/
defaultLang: 'zh-cn'
}

View File

@ -0,0 +1,43 @@
<template>
<div class="other-login" v-if="defaultSettings.showOtherLogin">
<div class="other-tip">{{ $t('login.otherLoginWay') }}</div>
<span @click="onAuth('GITHUB')" title="github"><svg-icon name="github" className="login-icon"></svg-icon></span>
<span @click="onAuth('GITEE')" title="gitee"><svg-icon name="gitee" className="login-icon"></svg-icon></span>
</div>
</template>
<script setup>
import defaultSettings from '@/settings'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
function onAuth(type) {
userStore.setAuthSource(type)
switch (type) {
default:
// window.location.href = import.meta.env.VITE_APP_BASE_API + '/auth/Authorization?authSource=' + type
proxy.$modal.msg('请看文档怎么接入')
break
}
}
</script>
<style lang="scss" scoped>
.other-login {
padding: 0px 20px 10px;
.other-tip {
text-align: center;
color: #ccc;
font-size: 13px;
margin-top: 10px;
}
.login-icon {
width: 25px;
height: 25px;
margin-right: 20px;
cursor: pointer;
}
}
</style>

View File

@ -0,0 +1,106 @@
<template>
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form" v-show="loginType == 1">
<el-form-item prop="phoneNum">
<el-input v-model="loginForm.phoneNum" type="phone" :maxlength="11" auto-complete="off" :placeholder="$t('login.input_phoneNum')">
<template #prefix>
<svg-icon name="phone" class="input-icon" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="phoneCode">
<el-input v-model="loginForm.phoneCode" type="number" auto-complete="off" :placeholder="$t('login.phoneCode')" @keyup.enter="handleLogin">
<template #prefix>
<svg-icon name="validCode" class="input-icon" />
</template>
<template #append>
<el-button @click="handleSendCode" v-if="!showCounddown">{{ $t('login.sendPhoneCode') }}</el-button>
<el-countdown :value="countdownValue" format="mm:ss" @finish="handleFinish" v-else />
</template>
</el-input>
</el-form-item>
<el-form-item prop="code" v-if="captchaOnOff != 'off'">
<el-input v-model="loginForm.code" auto-complete="off" :placeholder="$t('login.captcha')" style="width: 63%" @keyup.enter="handleLogin">
<template #prefix>
<svg-icon name="validCode" class="input-icon" />
</template>
</el-input>
<div class="login-code">
<el-image :src="codeUrl" @click="getCode" class="login-code-img" />
</div>
</el-form-item>
<el-form-item style="width: 100%" :style="{ 'margin-top': captchaOnOff == 'off' ? '40px' : '' }">
<el-button :loading="loading" size="default" round type="primary" style="width: 100%" @click.prevent="handleLogin">
<span v-if="!loading">{{ $t('login.btnLogin') }}</span>
<span v-else> 中...</span>
</el-button>
</el-form-item>
</el-form>
</template>
<script setup name="phonelogin">
import { getCodeImg } from '@/api/system/login'
import useUserStore from '@/store/modules/user'
const route = useRoute()
const { proxy } = getCurrentInstance()
const loginForm = ref({
password: '',
rememberMe: false,
code: '',
uuid: '',
phoneCode: '',
phoneNum: ''
})
const loginRules = {
phoneNum: [{ required: true, trigger: 'blur', message: '请输入手机号码', pattern: /^1\d{10}$/ }],
phoneCode: [{ required: true, trigger: 'blur', message: '请输入短信验证码' }],
code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
}
const loginType = ref(1)
const codeUrl = ref('')
const loading = ref(false)
//
const captchaOnOff = ref('')
const redirect = ref()
redirect.value = route.query.redirect
function handleLogin() {
proxy.$refs.loginRef.validate((valid) => {
if (valid) {
loading.value = true
// cookie
proxy.$modal.msg('敬请期待')
}
})
}
function getCode() {
getCodeImg().then((res) => {
codeUrl.value = 'data:image/gif;base64,' + res.data.img
loginForm.value.uuid = res.data.uuid
captchaOnOff.value = res.data.captchaOff
})
}
const showCounddown = ref(false)
const countdownValue = ref(0)
function handleSendCode() {
showCounddown.value = true
countdownValue.value = Date.now() + 1000 * 60
}
function handleFinish() {
showCounddown.value = false
}
getCode()
</script>
<style lang="scss" scoped>
@import '@/assets/styles/login.scss';
.forget-pwd {
color: #ccc;
margin-right: 10px;
cursor: pointer;
}
</style>

View File

@ -31,7 +31,7 @@
<el-card style="height: 100%">
<div class="text-warning mb10">{{ currentTime }} {{ weekName }}</div>
<div class="work-wrap">
<el-statistic :title="$t('layout.workTime')" :value="Math.ceil(onlineInfo.todayOnlineTime, 2)" />
<el-statistic :title="$t('layout.workTime')" :formatter="workTimeFormatter" :value="onlineInfo.todayOnlineTime" />
<el-statistic :title="$t('layout.onlineClientNum')" :value="onlineInfo.clientNum" />
</div>
</el-card>
@ -98,6 +98,10 @@ import PieChart from './dashboard/PieChart'
import BarChart from './dashboard/BarChart'
// import WordCloudChat from './dashboard/WordCloud.vue'
import CommonMenu from './components/CommonMenu'
import dayjs from 'dayjs'
//
import duration from 'dayjs/plugin/duration'
dayjs.extend(duration)
import useUserStore from '@/store/modules/user'
import useSocketStore from '@/store/modules/socket'
@ -144,6 +148,9 @@ handleSetLineChartData('newVisitis')
function handleAdd() {
proxy.$modal.msg('请通过搜索添加')
}
function workTimeFormatter(val) {
return dayjs.duration(val * 60, 'second').format('HH时mm分')
}
</script>
<style lang="scss" scoped>

View File

@ -2,14 +2,19 @@
<starBackground></starBackground>
<div class="login-wrap">
<div class="login">
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form" v-if="!showQrLogin">
<h3 class="title">{{ defaultSettings.title }}</h3>
<h3 class="title">{{ defaultSettings.title }}</h3>
<div class="scan-wrap" @click="handleShowQrLogin()">
<svg-icon name="qr" class="icon" />
<div class="scan-delta"></div>
</div>
<LangSelect title="多语言设置" class="langSet" />
<LangSelect title="多语言设置" class="langSet" />
<div style="padding: 0 25px 5px 25px">
<el-tabs v-model="loginType" @tab-click="handleLoginType">
<el-tab-pane :label="$t('login.loginway1')" :name="1"></el-tab-pane>
<el-tab-pane :label="$t('login.loginway2')" :name="2"></el-tab-pane>
<el-tab-pane :label="$t('login.loginway3')" :name="3"></el-tab-pane>
</el-tabs>
</div>
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form" v-show="loginType == 1">
<el-form-item prop="username">
<el-input v-model="loginForm.username" type="text" auto-complete="off" :placeholder="$t('login.account')">
<template #prefix>
@ -43,34 +48,24 @@
</span>
</div>
<el-form-item style="width: 100%" :style="{ 'margin-top': captchaOnOff == 'off' ? '40px' : '' }">
<el-button :loading="loading" size="default" type="primary" style="width: 100%" @click.prevent="handleLogin">
<el-form-item style="width: 100%" :style="{ 'margin-top': captchaOnOff == 'off' ? '20px' : '' }">
<el-button :loading="loading" size="default" round type="primary" style="width: 100%" @click.prevent="handleLogin">
<span v-if="!loading">{{ $t('login.btnLogin') }}</span>
<span v-else> 中...</span>
</el-button>
</el-form-item>
</el-form>
<div class="qr-wrap login-form" v-else>
<h3 class="title">移动端扫码登录</h3>
<div class="scan-wrap" @click="handleShowQrLogin()">
<svg-icon name="pc" class="icon" />
<div class="scan-delta"></div>
</div>
<div class="qr-wrap login-form" v-show="loginType == 3">
<div class="login-scan-container">
<div ref="imgContainerRef" id="imgContainer" class="qrCode"></div>
<div class="mt10 text-muted">请使用移动端app扫码登录</div>
<div class="mt10 text-muted">{{ $t('login.tip_scan_code') }}</div>
</div>
</div>
<div class="other-login" v-if="defaultSettings.showOtherLogin">
<el-divider>{{ $t('login.otherLoginWay') }}</el-divider>
<span @click="onAuth('GITHUB')" title="github"><svg-icon name="github" className="login-icon"></svg-icon></span>
<span @click="onAuth('GITEE')" title="gitee"><svg-icon name="gitee" className="login-icon"></svg-icon></span>
</div>
<phoneLogin v-show="loginType == 2"></phoneLogin>
<oauthLogin></oauthLogin>
</div>
<!-- 底部 -->
<div class="el-login-footer">
<div v-html="defaultSettings.copyright"></div>
</div>
@ -87,6 +82,9 @@ import LangSelect from '@/components/LangSelect/index.vue'
import useUserStore from '@/store/modules/user'
import QRCode from 'qrcodejs2-fixes'
import { verifyScan, generateQrcode } from '@/api/system/login'
import oauthLogin from './components/Login/oauthLogin.vue'
import phoneLogin from './components/Login/phoneLogin.vue'
var visitorId = ''
const fpPromise = import('https://openfpcdn.io/fingerprintjs/v3').then((FingerprintJS) => FingerprintJS.load())
@ -108,7 +106,7 @@ const loginRules = {
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
}
const loginType = ref(1)
const codeUrl = ref('')
const loading = ref(false)
//
@ -178,15 +176,6 @@ function getCookie() {
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
}
}
function onAuth(type) {
userStore.setAuthSource(type)
switch (type) {
default:
window.location.href = import.meta.env.VITE_APP_BASE_API + '/auth/Authorization?authSource=' + type
break
}
}
function handleForgetPwd() {
proxy.$modal.msg('请联系管理员')
}
@ -194,15 +183,11 @@ function handleForgetPwd() {
const interval = ref(null)
const showQrLogin = ref(false)
function handleShowQrLogin() {
showQrLogin.value = !showQrLogin.value
// showQrLogin.value = !showQrLogin.value
if (showQrLogin.value) {
nextTick(() => {
generateCode()
})
} else {
clearQr()
}
nextTick(() => {
generateCode()
})
}
//
function generateCode() {
@ -259,6 +244,15 @@ function getUuid() {
URL.revokeObjectURL(temp_url)
return uuid.substr(uuid.lastIndexOf('/') + 1)
}
function handleLoginType(t) {
const val = t.paneName
if (val == 3) {
handleShowQrLogin()
} else {
clearQr()
}
}
getCode()
getCookie()
</script>
@ -270,15 +264,6 @@ getCookie()
margin-right: 10px;
cursor: pointer;
}
.login-icon {
width: 30px;
height: 30px;
margin-right: 20px;
cursor: pointer;
}
.other-login {
padding: 0px 10px 5px;
}
.qrCode {
width: 160px;
height: 160px;

View File

@ -36,19 +36,20 @@
</div>
</el-form-item>
<el-form-item style="width: 100%">
<el-button :loading="loading" type="primary" size="default" style="width: 100%" @click.prevent="handleRegister">
<span v-if="!loading"> </span>
<el-button :loading="loading" type="primary" size="default" round style="width: 100%" @click.prevent="handleRegister">
<span v-if="!loading">{{ $t('login.register') }}</span>
<span v-else> 中...</span>
</el-button>
<div style="text-align: center">
<router-link class="link-type" :to="'/login'">使用已有账户登录</router-link>
</div>
</el-form-item>
<div style="text-align: center">
<router-link class="link-type" :to="'/login'">使用已有账户登录</router-link>
</div>
</el-form>
<oauthLogin></oauthLogin>
</div>
<!-- 底部 -->
<div class="el-register-footer">
<div v-html="defaultSettings.copyright"></div>
<div v-html="copyRight"></div>
</div>
</div>
</template>
@ -58,6 +59,7 @@ import starBackground from '@/views/components/starBackground.vue'
import { getCodeImg, register } from '@/api/system/login'
import defaultSettings from '@/settings'
import { ElMessageBox } from 'element-plus'
import oauthLogin from './components/Login/oauthLogin.vue'
const { proxy } = getCurrentInstance()
const router = useRouter()
const codeUrl = ref('')
@ -159,28 +161,15 @@ getCode()
flex-direction: column;
background: radial-gradient(220% 105% at top center, #1b2947 10%, #4b76a7 40%, #81acae 65%, #f7f7b6);
}
.login-form {
height: 320px;
}
.title {
margin: 0px auto 30px auto;
text-align: center;
// color: #fff;
}
.register-form {
background: #fff;
width: var(--base-login-width);
padding: 35px 15px 5px 15px;
.el-input {
height: 38px;
input {
height: 38px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
}
.register-tip {
font-size: 13px;
text-align: center;