mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-24 08:03:53 +08:00
Manager web (#227)
* update:增加前端设计图 * 前端代码优化 * update:底部信息纠正 * 增加:flyio * update:去除org.quartz * update:登陆功能 --------- Co-authored-by: hrz <1710360675@qq.com> Co-authored-by: CGD <3030332422@qq.com>
This commit is contained in:
Executable
+22
@@ -0,0 +1,22 @@
|
||||
// 引入各个模块的请求
|
||||
import user from './module/user.js'
|
||||
/**
|
||||
* 接口地址
|
||||
* 在开发阶段,如果地址写的是相对路径,请与vue.config.js的devServer配置相结合,方便跨域请求
|
||||
*
|
||||
*/
|
||||
const DEV_API_SERVICE = 'https://apifoxmock.com/m1/5931378-5618560-default'
|
||||
|
||||
/**
|
||||
* 根据开发环境返回接口url
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getServiceUrl () {
|
||||
return DEV_API_SERVICE
|
||||
}
|
||||
|
||||
/** request服务封装 */
|
||||
export default {
|
||||
getServiceUrl,
|
||||
user
|
||||
}
|
||||
Executable
+132
@@ -0,0 +1,132 @@
|
||||
import {goToPage, showDanger, showWarning} from '../utils/index'
|
||||
import Constant from '../utils/constant'
|
||||
import Fly from 'flyio/dist/npm/fly';
|
||||
|
||||
const fly = new Fly()
|
||||
// 设置超时
|
||||
fly.config.timeout = 30000
|
||||
|
||||
/**
|
||||
* Request服务封装
|
||||
*/
|
||||
export default {
|
||||
sendRequest,
|
||||
reAjaxFun,
|
||||
clearRequestTime
|
||||
}
|
||||
|
||||
function sendRequest() {
|
||||
return {
|
||||
_sucCallback: null,
|
||||
_failCallback: null,
|
||||
_method: 'GET',
|
||||
_data: {},
|
||||
_header: {'content-type': 'application/json; charset=utf-8'},
|
||||
_url: '',
|
||||
'send'() {
|
||||
this._header.token = localStorage.getItem(Constant.STORAGE_KEY.TOKEN)
|
||||
fly.request(this._url, this._data, {
|
||||
method: this._method,
|
||||
headers: this._header
|
||||
}).then((res) => {
|
||||
const error = httpHandlerError(res, this._failCallback)
|
||||
if (error) {
|
||||
return
|
||||
}
|
||||
if (this._sucCallback) {
|
||||
this._sucCallback(res)
|
||||
}
|
||||
}).catch((res) => {
|
||||
console.log(1111, res)
|
||||
httpHandlerError(res, this._failCallback)
|
||||
})
|
||||
return this
|
||||
},
|
||||
'success'(callback) {
|
||||
this._sucCallback = callback
|
||||
return this
|
||||
},
|
||||
'fail'(callback) {
|
||||
this._failCallback = callback
|
||||
return this
|
||||
},
|
||||
'url'(url) {
|
||||
if (url) {
|
||||
url = url.replaceAll('$', '/')
|
||||
}
|
||||
this._url = url
|
||||
return this
|
||||
},
|
||||
'data'(data) {
|
||||
this._data = data
|
||||
return this
|
||||
},
|
||||
'method'(method) {
|
||||
this._method = method
|
||||
return this
|
||||
},
|
||||
'header'(header) {
|
||||
this._header = header
|
||||
return this
|
||||
},
|
||||
'showLoading'(showLoading) {
|
||||
this._showLoading = showLoading
|
||||
return this
|
||||
},
|
||||
'async'(flag) {
|
||||
this.async = flag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Info 请求完成后返回信息
|
||||
* callBack 回调函数
|
||||
* errTip 自定义错误信息
|
||||
*/
|
||||
function httpHandlerError(info, callBack) {
|
||||
/** 请求成功,退出该函数 可以根据项目需求来判断是否请求成功。这里判断的是status为200的时候是成功 */
|
||||
let networkError = false
|
||||
if (info.status === 200) {
|
||||
if (info.data.code === 'success' || info.data.code === 0) {
|
||||
return networkError
|
||||
} else if (info.data.code === 401) {
|
||||
goToPage(Constant.PAGE.LOGIN, true)
|
||||
return true
|
||||
} else {
|
||||
showDanger(info.data.msg)
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (callBack) {
|
||||
callBack(info)
|
||||
} else {
|
||||
showDanger(`网络请求出现了错误【${info.status}】`)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
let requestTime = 0
|
||||
let reAjaxSec = 2
|
||||
|
||||
function reAjaxFun(fn) {
|
||||
let nowTimeSec = new Date().getTime() / 1000
|
||||
if (requestTime === 0) {
|
||||
requestTime = nowTimeSec
|
||||
}
|
||||
let ajaxIndex = parseInt((nowTimeSec - requestTime) / reAjaxSec)
|
||||
if (ajaxIndex > 10) {
|
||||
showWarning('似乎无法连接服务器')
|
||||
} else {
|
||||
showWarning('正在连接服务器(' + ajaxIndex + ')')
|
||||
}
|
||||
if (fn) {
|
||||
setTimeout(() => {
|
||||
fn()
|
||||
}, reAjaxSec * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
function clearRequestTime() {
|
||||
requestTime = 0
|
||||
}
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
import RequestService from '../httpRequest'
|
||||
import {getServiceUrl} from '../api'
|
||||
|
||||
|
||||
export default {
|
||||
// 登录
|
||||
login(loginForm, callback) {
|
||||
RequestService.sendRequest().url(`${getServiceUrl()}/api/v1/user/login`).method('POST')
|
||||
.data(loginForm)
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(res)
|
||||
})
|
||||
.fail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.login(loginForm, callback)
|
||||
})
|
||||
}).send()
|
||||
}
|
||||
}
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
const HAVE_NO_RESULT = '暂无'
|
||||
export default {
|
||||
HAVE_NO_RESULT, // 项目的配置信息
|
||||
STORAGE_KEY: {
|
||||
TOKEN: 'TOKEN',
|
||||
PUBLIC_KEY: 'PUBLIC_KEY',
|
||||
USER_TYPE: 'USER_TYPE'
|
||||
},
|
||||
Lang: {
|
||||
'zh_cn': 'zh_cn', 'zh_tw': 'zh_tw', 'en': 'en'
|
||||
},
|
||||
FONT_SIZE: {
|
||||
'big': 'big',
|
||||
'normal': 'normal',
|
||||
}, // 获取map中的某key
|
||||
get(map, key) {
|
||||
return map[key] || HAVE_NO_RESULT
|
||||
}
|
||||
}
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
export const toDate = (date) => {
|
||||
return isDate(date) ? new Date(date) : null
|
||||
}
|
||||
|
||||
export const isDate = (date) => {
|
||||
if (date === null || date === undefined) return false
|
||||
if (isNaN(new Date(date).getTime())) return false
|
||||
return true
|
||||
}
|
||||
|
||||
export const isDateObject = (val) => {
|
||||
return val instanceof Date
|
||||
}
|
||||
|
||||
export const formatAddDate = (date, format, addDay) => {
|
||||
date = toDate(date)
|
||||
if (!date) {
|
||||
return ''
|
||||
}
|
||||
if (!addDay) {
|
||||
date.setDate(date.getDate() + addDay)
|
||||
}
|
||||
return formatDateTool(date, format || 'yyyy-MM-dd HH:mm:ss')
|
||||
}
|
||||
|
||||
export const formatDate = (date, format) => {
|
||||
date = toDate(date)
|
||||
if (!date) return ''
|
||||
return formatDateTool(date, format || 'yyyy-MM-dd HH:mm:ss')
|
||||
}
|
||||
|
||||
function formatDateTool(date, fmt) {
|
||||
if (/(y+)/.test(fmt)) {
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
|
||||
)
|
||||
}
|
||||
const o = {
|
||||
'M+': date.getMonth() + 1,
|
||||
'd+': date.getDate(),
|
||||
'h+': date.getHours(),
|
||||
'm+': date.getMinutes(),
|
||||
's+': date.getSeconds()
|
||||
}
|
||||
for (const k in o) {
|
||||
if (new RegExp(`(${k})`).test(fmt)) {
|
||||
const str = o[k] + ''
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
RegExp.$1.length === 1 ? str : padLeftZero(str)
|
||||
)
|
||||
}
|
||||
}
|
||||
return fmt
|
||||
}
|
||||
|
||||
function padLeftZero(str) {
|
||||
return ('00' + str).substr(str.length)
|
||||
}
|
||||
Executable
+135
@@ -0,0 +1,135 @@
|
||||
import router from '../router'
|
||||
import Constant from '../utils/constant'
|
||||
import { Message } from 'element-ui'
|
||||
|
||||
/**
|
||||
* 判断用户是否登录
|
||||
*/
|
||||
export function checkUserLogin(fn) {
|
||||
let token = localStorage.getItem(Constant.STORAGE_KEY.TOKEN)
|
||||
let userType = localStorage.getItem(Constant.STORAGE_KEY.USER_TYPE)
|
||||
if (isNull(token) || isNull(userType)) {
|
||||
goToPage('console', true)
|
||||
return
|
||||
}
|
||||
if (fn) {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为空
|
||||
* @param data
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isNull(data) {
|
||||
if (data === undefined) {
|
||||
return true
|
||||
} else if (data === null) {
|
||||
return true
|
||||
} else if (typeof data === 'string' && (data.length === 0 || data === '' || data === 'undefined' || data === 'null')) {
|
||||
return true
|
||||
} else if ((data instanceof Array) && data.length === 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断不为空
|
||||
* @param data
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isNotNull(data) {
|
||||
return !isNull(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示顶部红色通知
|
||||
* @param msg
|
||||
*/
|
||||
export function showDanger(msg) {
|
||||
if (isNull(msg)) {
|
||||
return
|
||||
}
|
||||
Message({
|
||||
message: msg,
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示顶部橙色通知
|
||||
* @param msg
|
||||
*/
|
||||
export function showWarning(msg) {
|
||||
if (isNull(msg)) {
|
||||
return
|
||||
}
|
||||
Message({
|
||||
message: msg,
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 显示顶部绿色通知
|
||||
* @param msg
|
||||
*/
|
||||
export function showSuccess(msg) {
|
||||
Message({
|
||||
message: msg,
|
||||
type: 'success'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 页面跳转
|
||||
* @param path
|
||||
* @param isRepalce
|
||||
*/
|
||||
export function goToPage(path, isRepalce) {
|
||||
if (isRepalce) {
|
||||
router.replace(path)
|
||||
} else {
|
||||
router.push(path)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前vue页面名称
|
||||
* @param path
|
||||
* @param isRepalce
|
||||
*/
|
||||
export function getCurrentPage() {
|
||||
let hash = location.hash.replace('#', '')
|
||||
if (hash.indexOf('?') > 0) {
|
||||
hash = hash.substring(0, hash.indexOf('?'))
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成从[min,max]的随机数
|
||||
* @param min
|
||||
* @param max
|
||||
* @returns {number}
|
||||
*/
|
||||
export function randomNum(min, max) {
|
||||
return Math.round(Math.random() * (max - min) + min)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取uuid
|
||||
*/
|
||||
export function getUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
||||
return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</div>
|
||||
<div style="display: flex;align-items: center;gap: 8px;">
|
||||
<div class="serach-box">
|
||||
<el-input placeholder="输入名称搜索.." v-model="serach" />
|
||||
<el-input placeholder="输入名称搜索.." v-model="serach" style="border: none; background: transparent;" />
|
||||
<img src="@/assets/home/search.png" alt=""
|
||||
style="width: 12px;height: 12px;margin-right: 11px;cursor: pointer;" />
|
||||
</div>
|
||||
@@ -87,20 +87,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="settingDevice" style="border-radius: 20px;background: #fafcfe;">
|
||||
<div v-show="settingDevice" style="border-radius: 18px;background: #fafcfe;">
|
||||
<div
|
||||
style="padding: 19px 30px;font-weight: 700;font-size: 24px;text-align: left;color: #3d4566;display: flex;gap: 16px;align-items: center;">
|
||||
style="padding: 17px 27px;font-weight: 700;font-size: 21px;text-align: left;color: #3d4566;display: flex;gap: 14px;align-items: center;">
|
||||
<div
|
||||
style="width: 46px;height: 46px;background: #5778ff;border-radius: 50%;display: flex;align-items: center;justify-content: center;">
|
||||
<img src="@/assets/home/setting-user.png" alt="" style="width: 24px;height: 24px;" />
|
||||
style="width: 41px;height: 41px;background: #5778ff;border-radius: 50%;display: flex;align-items: center;justify-content: center;">
|
||||
<img src="@/assets/home/setting-user.png" alt="" style="width: 21px;height: 21px;" />
|
||||
</div>
|
||||
CC:ba:97:11:a6:ac
|
||||
</div>
|
||||
<div style="height: 1px;background: #e8f0ff;" />
|
||||
<el-form ref="form" :model="form" label-width="90px">
|
||||
<div style="padding: 20px 30px;max-width: 990px;">
|
||||
<el-form ref="form" :model="form" label-width="81px">
|
||||
<div style="padding: 18px 28px;max-width: 890px;">
|
||||
<el-form-item label="助手昵称:">
|
||||
<div class="input-46">
|
||||
<div class="input-46" style="width: 57.5%;">
|
||||
<el-input v-model="form.name" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -119,7 +119,7 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色音色:">
|
||||
<div style="display: flex;gap: 10px;align-items: center;">
|
||||
<div style="display: flex;gap: 9px;align-items: center;">
|
||||
<div class="input-46" style="flex:1.4;">
|
||||
<el-select v-model="form.timbre" placeholder="请选择" style="width: 100%;">
|
||||
<el-option v-for="item in options" :key="item.value" :label="item.label"
|
||||
@@ -135,28 +135,28 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="角色介绍:">
|
||||
<div class="textarea-box">
|
||||
<el-input type="textarea" rows="6" resize="none" placeholder="请输入内容"
|
||||
<el-input type="textarea" rows="5.4" resize="none" placeholder="请输入内容"
|
||||
v-model="form.introduction" maxlength="2000" show-word-limit />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="记忆体:">
|
||||
<div class="textarea-box">
|
||||
<el-input type="textarea" rows="6" resize="none" placeholder="请输入内容"
|
||||
<el-input type="textarea" rows="5.4" resize="none" placeholder="请输入内容"
|
||||
v-model="form.prompt" maxlength="1000" />
|
||||
<div class="prompt-bottom">
|
||||
<div style="display: flex;gap: 10px;align-items: center;">
|
||||
<div style="color: #979db1;font-size: 14px;">当前记忆(每次对话后重新生成)</div>
|
||||
<div style="color: #979db1;font-size: 12px;">当前记忆(每次对话后重新生成)</div>
|
||||
<div class="clear-btn">
|
||||
<i class="el-icon-delete-solid" style="font-size: 14px;" />
|
||||
<i class="el-icon-delete-solid" style="font-size: 12px;" />
|
||||
清除
|
||||
</div>
|
||||
</div>
|
||||
<div style="color: #979db1;font-size:14px;">{{form.prompt.length}}/1000</div>
|
||||
<div style="color: #979db1;font-size:12px;">{{form.prompt.length}}/1000</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="语言模型(内测):" class="lh-form-item">
|
||||
<div style="display: flex;gap: 10px;">
|
||||
<div style="display: flex;gap: 9px;">
|
||||
<div class="input-46" style="width: 100%;">
|
||||
<el-select v-model="form.model" placeholder="请选择" style="width: 100%;">
|
||||
<el-option v-for="item in options" :key="item.value" :label="item.label"
|
||||
@@ -172,13 +172,13 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<div style="display: flex;padding: 20px;gap: 10px;align-items: center;">
|
||||
<div style="display: flex;padding: 18px;gap: 9px;align-items: center;">
|
||||
<div class="save-btn">
|
||||
保存配置</div>
|
||||
<div class="reset-btn">
|
||||
重制</div>
|
||||
<div class="clear-text">
|
||||
<img src="@/assets/home/red-info.png" alt="" style="width: 24px;height: 24px;" />
|
||||
<img src="@/assets/home/red-info.png" alt="" style="width: 21px;height: 21px;" />
|
||||
保存配置后,需要重启设备,新的配置才会生效。
|
||||
</div>
|
||||
</div>
|
||||
@@ -188,7 +188,7 @@
|
||||
©2025 xiaozhi-esp32-server</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
<el-dialog :visible.sync="addDeviceDialogVisible" width="480px" center>
|
||||
<el-dialog :visible.sync="addDeviceDialogVisible" width="400px" center>
|
||||
<div
|
||||
style="margin: 0 20px 20px;display: flex;align-items: center;gap: 10px;font-weight: 700;font-size: 20px;text-align: left;color: #3d4566;;">
|
||||
<div
|
||||
@@ -356,11 +356,14 @@ export default {
|
||||
}
|
||||
.serach-box {
|
||||
display: flex;
|
||||
width: 230px;
|
||||
width: 250px;
|
||||
height: 30px;
|
||||
border-radius: 15px;
|
||||
background-color: #e2f5f7;
|
||||
background-color: #f6fcfe66;
|
||||
border: 1px solid #e4e6ef;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.user-info {
|
||||
font-weight: 600;
|
||||
@@ -389,7 +392,7 @@ export default {
|
||||
}
|
||||
.template-item {
|
||||
height: 35px;
|
||||
width: 75px;
|
||||
width: 85px;
|
||||
border-radius: 8px;
|
||||
background: #e6ebff;
|
||||
line-height: 35px;
|
||||
@@ -461,7 +464,7 @@ export default {
|
||||
}
|
||||
}
|
||||
.device-item {
|
||||
width: 341px;
|
||||
width: 350px;
|
||||
border-radius: 15px;
|
||||
background: #fafcfe;
|
||||
padding: 22px;
|
||||
@@ -526,6 +529,14 @@ audio::-webkit-media-controls-panel {
|
||||
border: none !important;
|
||||
padding: 15px;
|
||||
}
|
||||
// 搜索输入框的样式调整
|
||||
.serach-box .el-input__inner {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
padding: 0 5px 0 15px;
|
||||
font-size: 12px;
|
||||
color: #3d4566;
|
||||
}
|
||||
.el-textarea .el-input__count {
|
||||
color: #979db1;
|
||||
font-size: 11px;
|
||||
@@ -533,13 +544,14 @@ audio::-webkit-media-controls-panel {
|
||||
background-color: transparent;
|
||||
}
|
||||
.el-input__inner {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
//border: none;
|
||||
//background-color: transparent;
|
||||
padding: 0 5px 0 15px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.input-46 .el-input__inner {
|
||||
padding: 0 15px;
|
||||
height: 46px;
|
||||
height: 38px;
|
||||
}
|
||||
.lh-form-item {
|
||||
.el-form-item__label {
|
||||
@@ -596,3 +608,5 @@ audio::-webkit-media-controls-panel {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -3,97 +3,91 @@
|
||||
<el-container style="height: 100%;">
|
||||
<el-header>
|
||||
<div
|
||||
style="display: flex;align-items: center;margin-top: 15px;margin-left: 10px;gap: 10px;">
|
||||
<img src="@/assets/xiaozhi-logo.png" alt="" style="width: 45px;height: 45px;" />
|
||||
<img src="@/assets/xiaozhi-ai.png" alt="" style="width: 70px;height: 13px;" />
|
||||
style="display: flex;align-items: center;margin-top: 15px;margin-left: 10px;gap: 10px;">
|
||||
<img src="@/assets/xiaozhi-logo.png" alt="" style="width: 45px;height: 45px;"/>
|
||||
<img src="@/assets/xiaozhi-ai.png" alt="" style="width: 70px;height: 13px;"/>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main style="position: relative;">
|
||||
<div class="login-box">
|
||||
<div
|
||||
style="display: flex;align-items: center;gap: 20px;margin-bottom: 39px;padding: 0 30px;">
|
||||
<img src="@/assets/login/hi.png" alt="" style="width: 34px;height: 34px;" />
|
||||
style="display: flex;align-items: center;gap: 20px;margin-bottom: 39px;padding: 0 30px;">
|
||||
<img src="@/assets/login/hi.png" alt="" style="width: 34px;height: 34px;"/>
|
||||
<div class="login-text">登录</div>
|
||||
<div class="login-welcome">
|
||||
WELCOME TO LOG IN</div>
|
||||
WELCOME TO LOGIN
|
||||
</div>
|
||||
</div>
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="用户名" name="username">
|
||||
<div style="padding: 0 30px;">
|
||||
<div class="input-box">
|
||||
<img src="@/assets/login/username.png" alt="" class="input-icon" />
|
||||
<el-input v-model="form.username" placeholder="请输入用户名" />
|
||||
</div>
|
||||
<div class="input-box">
|
||||
<img src="@/assets/login/password.png" alt="" class="input-icon" />
|
||||
<el-input v-model="form.password" placeholder="请输入密码" />
|
||||
</div>
|
||||
<div style="font-weight: 400;font-size: 14px;text-align: left;color: #5778ff;display: flex;justify-content: space-between;margin-top: 20px;">
|
||||
<div style="cursor: pointer;">新用户注册</div>
|
||||
<div style="cursor: pointer;">忘记密码?</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="手机号" name="phone">
|
||||
<div style="padding: 0 30px;">
|
||||
<div class="input-box">
|
||||
<img src="@/assets/login/phone.png" alt="" class="input-icon" />
|
||||
<el-input v-model="form.phoneNumber" placeholder="请输入手机号" />
|
||||
<div style="width: 120px;flex-shrink: 0;">
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
+86 中国大陆<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item>黄金糕</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 0 12px 0 30px;margin-top: 20px;" class="input-box">
|
||||
<img src="@/assets/login/shield.png" alt="" class="input-icon" />
|
||||
<el-input v-model="form.phoneCode" placeholder="请输入验证码" />
|
||||
<div class="code-send">
|
||||
发送
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="login-btn">登陆</div>
|
||||
<div style="padding: 0 30px;">
|
||||
<div class="input-box">
|
||||
<img src="@/assets/login/username.png" alt="" class="input-icon"/>
|
||||
<el-input v-model="form.username" placeholder="请输入用户名"/>
|
||||
</div>
|
||||
<div class="input-box">
|
||||
<img src="@/assets/login/password.png" alt="" class="input-icon"/>
|
||||
<el-input v-model="form.password" placeholder="请输入密码"/>
|
||||
</div>
|
||||
<div class="input-box">
|
||||
<img src="@/assets/login/shield.png" alt="" class="input-icon"/>
|
||||
<el-input v-model="form.captcha" placeholder="请输入验证码"/>
|
||||
</div>
|
||||
<div
|
||||
style="font-weight: 400;font-size: 14px;text-align: left;color: #5778ff;display: flex;justify-content: space-between;margin-top: 20px;">
|
||||
<div style="cursor: pointer;">新用户注册</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-btn" @click="login">登陆</div>
|
||||
<div style="font-size: 14px;color: #979db1;">
|
||||
登录即同意<div style="display: inline-block;color: #5778FF;cursor: pointer;">《用户协议》</div>和
|
||||
登录即同意
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">《用户协议》</div>
|
||||
和
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">《隐私政策》</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
<el-footer>
|
||||
<div style="font-size: 12px;font-weight: 400;color: #979db1;">
|
||||
©2024 小智Al控制面板2.0粤ICP备2022121736号-2</div>
|
||||
©2025 xiaozhi-esp32-server
|
||||
</div>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ is an alias to /src
|
||||
import Api from '@/apis/api'
|
||||
import {isNull, showDanger, showSuccess, goToPage} from '@/utils'
|
||||
|
||||
export default {
|
||||
name: 'login',
|
||||
data() {
|
||||
return {
|
||||
activeName: "username",
|
||||
form:{
|
||||
phoneNumber:'',
|
||||
phoneCode:'',
|
||||
username:'',
|
||||
password:''
|
||||
form: {
|
||||
username: '',
|
||||
password: '',
|
||||
captcha: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
handleClick(){
|
||||
|
||||
methods: {
|
||||
login() {
|
||||
if (isNull(this.form.username)) {
|
||||
showDanger('用户名不能为空')
|
||||
return
|
||||
}
|
||||
if (isNull(this.form.password)) {
|
||||
showDanger('密码不能为空')
|
||||
return
|
||||
}
|
||||
if (isNull(this.form.captcha)) {
|
||||
showDanger('验证码不能为空')
|
||||
return
|
||||
}
|
||||
Api.user.login(this.form, ({data}) => {
|
||||
showSuccess('登陆成功!')
|
||||
goToPage('/home')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,12 +107,14 @@ export default {
|
||||
-o-background-size: cover;
|
||||
/* 兼容老版本Opera浏览器 */
|
||||
}
|
||||
|
||||
.login-text {
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
text-align: left;
|
||||
color: #3d4566;
|
||||
}
|
||||
|
||||
.login-welcome {
|
||||
font-weight: 400;
|
||||
font-size: 9px;
|
||||
@@ -127,6 +123,7 @@ export default {
|
||||
align-self: flex-end;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
.login-box {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
@@ -138,17 +135,20 @@ export default {
|
||||
width: 450px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.el-dropdown-link {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
color: #979db1;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
width: 19px;
|
||||
height: 22px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
height: 35px;
|
||||
background: #5778ff;
|
||||
@@ -160,6 +160,7 @@ export default {
|
||||
line-height: 35px;
|
||||
margin: 35px 15px 15px;
|
||||
}
|
||||
|
||||
.code-send {
|
||||
width: 70px;
|
||||
height: 32px;
|
||||
@@ -172,6 +173,7 @@ export default {
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.input-box {
|
||||
display: flex;
|
||||
margin-top: 20px;
|
||||
@@ -183,10 +185,12 @@ export default {
|
||||
padding: 0 15px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
::v-deep {
|
||||
.el-tabs__nav-wrap::after {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.el-tabs__nav-wrap::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
@@ -197,18 +201,22 @@ export default {
|
||||
background-color: #e4e7ed;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.el-tabs__item {
|
||||
height: 65px;
|
||||
line-height: 65px;
|
||||
font-weight: 700;
|
||||
color: #3d4566;
|
||||
}
|
||||
|
||||
.el-tabs__item.is-active {
|
||||
color: #5778ff;
|
||||
}
|
||||
|
||||
.el-tabs__nav-scroll {
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
.el-input__inner {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
|
||||
Reference in New Issue
Block a user