登录完成
This commit is contained in:
@@ -10,8 +10,12 @@
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^1.1.4",
|
||||
"axios": "^0.26.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"element-plus": "^2.1.9",
|
||||
"md5": "^2.3.0",
|
||||
"pinia": "^2.0.13",
|
||||
"pinia-plugin-persist": "^1.0.0",
|
||||
"query-string": "^8.1.0",
|
||||
"sass": "^1.50.0",
|
||||
"vue": "^3.2.25",
|
||||
"vue-router": "^4.0.14"
|
||||
|
||||
@@ -16,21 +16,91 @@ body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@each $padMar, $pm in (mar:margin, pad:padding) {
|
||||
@each $v in (8, 10, 16, 20, 32, 48, 56, 64, 80) {
|
||||
@each $pos, $p in (l:left, r:right, t:top, b:bottom) {
|
||||
.#{$padMar}-#{$pos+$v} {
|
||||
#{$pm}-#{$p}: #{$v}px
|
||||
}
|
||||
}
|
||||
.#{$padMar}-#{$v} {
|
||||
#{$pm}: #{$v}px
|
||||
}
|
||||
//纵向
|
||||
.#{$padMar}-v#{$v} {
|
||||
#{$pm}-top: #{$v}px;
|
||||
#{$pm}-bottom: #{$v}px;
|
||||
}
|
||||
//横向
|
||||
.#{$padMar}-h#{$v} {
|
||||
#{$pm}-left: #{$v}px;
|
||||
#{$pm}-right: #{$v}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@each $where in (sticky, fixed) {
|
||||
@each $pos, $p in (l:left, r:right, t:top, b:bottom) {
|
||||
.#{$where}-#{$pos} {
|
||||
position: fixed;
|
||||
#{$p}: 0;
|
||||
z-index: 202301031019;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
|
||||
&.spb {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&.wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&.start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&.center {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.fill {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.color-a2v1c5 {
|
||||
color: #A2B1C5;
|
||||
.shrink {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mar-r30 {
|
||||
margin-right: 30px;
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
&.circle {
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.pad-w10 {
|
||||
padding: 0 10px;
|
||||
.rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.sub {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3335619 */
|
||||
src: url('//at.alicdn.com/t/font_3335619_ki0rpko4inh.woff2?t=1650462455599') format('woff2'),
|
||||
url('//at.alicdn.com/t/font_3335619_ki0rpko4inh.woff?t=1650462455599') format('woff'),
|
||||
url('//at.alicdn.com/t/font_3335619_ki0rpko4inh.ttf?t=1650462455599') format('truetype');
|
||||
font-family: "iconfont"; /* Project id 3861072 */
|
||||
src: url('//at.alicdn.com/t/c/font_3861072_hjuv05l9b3b.woff2?t=1673690356713') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3861072_hjuv05l9b3b.woff?t=1673690356713') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3861072_hjuv05l9b3b.ttf?t=1673690356713') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,102 +13,18 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-tishi:before {
|
||||
content: "\e74c";
|
||||
.icon-WeChat:before {
|
||||
content: "\e64c";
|
||||
}
|
||||
|
||||
.icon-fenxiang:before {
|
||||
content: "\e74d";
|
||||
.icon-cart:before {
|
||||
content: "\e880";
|
||||
}
|
||||
|
||||
.icon-shujuyuanguanli:before {
|
||||
content: "\e74b";
|
||||
.icon-mine:before {
|
||||
content: "\eb2a";
|
||||
}
|
||||
|
||||
.icon-dapingguanli:before {
|
||||
content: "\e74a";
|
||||
}
|
||||
|
||||
.icon-dangzuzhi:before {
|
||||
content: "\e749";
|
||||
}
|
||||
|
||||
.icon-fanhuishangyiye:before {
|
||||
content: "\e748";
|
||||
}
|
||||
|
||||
.icon-jiediancaozuo:before {
|
||||
content: "\e747";
|
||||
}
|
||||
|
||||
.icon-shujujicheng:before {
|
||||
content: "\e745";
|
||||
}
|
||||
|
||||
.icon-shujuqingxi:before {
|
||||
content: "\e746";
|
||||
}
|
||||
|
||||
.icon-shang:before {
|
||||
content: "\e743";
|
||||
}
|
||||
|
||||
.icon-xia:before {
|
||||
content: "\e744";
|
||||
}
|
||||
|
||||
.icon-toubutouxiang:before {
|
||||
content: "\e742";
|
||||
}
|
||||
|
||||
.icon-anniusousuo:before {
|
||||
content: "\e741";
|
||||
}
|
||||
|
||||
.icon-sousuo:before {
|
||||
content: "\e740";
|
||||
}
|
||||
|
||||
.icon-daochu:before {
|
||||
content: "\e73e";
|
||||
}
|
||||
|
||||
.icon-shanchu:before {
|
||||
content: "\e73f";
|
||||
}
|
||||
|
||||
.icon-zhuantishujuku:before {
|
||||
content: "\e73c";
|
||||
}
|
||||
|
||||
.icon-jichushujuku:before {
|
||||
content: "\e73d";
|
||||
}
|
||||
|
||||
.icon-ruanjianxinxicaiji:before {
|
||||
content: "\e73a";
|
||||
}
|
||||
|
||||
.icon-yingjianxinxicaiji:before {
|
||||
content: "\e73b";
|
||||
}
|
||||
|
||||
.icon-quanxianguanli:before {
|
||||
content: "\e733";
|
||||
}
|
||||
|
||||
.icon-dangqianweizhi:before {
|
||||
content: "\e734";
|
||||
}
|
||||
|
||||
.icon-tuichu:before {
|
||||
content: "\e735";
|
||||
}
|
||||
|
||||
.icon-zuzhiguanli:before {
|
||||
content: "\e736";
|
||||
}
|
||||
|
||||
.icon-zhanghuguanli:before {
|
||||
content: "\e737";
|
||||
.icon-store:before {
|
||||
content: "\e83d";
|
||||
}
|
||||
|
||||
37
web/src/components/KuIcon.vue
Normal file
37
web/src/components/KuIcon.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="KuIcon">
|
||||
<div class="iconfont" :class="icon" v-text="text" :style="{color}"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import "../assets/iconfont.css";
|
||||
|
||||
export default {
|
||||
name: "KuIcon",
|
||||
props: {
|
||||
icon: {type: String, required: true},
|
||||
color: String,
|
||||
text: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.KuIcon {
|
||||
box-sizing: border-box;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
.iconfont {
|
||||
font-size: inherit;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,16 +2,10 @@
|
||||
<section class="KuLogin">
|
||||
<div class="panel" :class="{border,changePwd}">
|
||||
<h2 v-text="title"/>
|
||||
<div class="color-a2v1c5 subTitle" v-text="`请输入您的用户名及密码`"/>
|
||||
<el-form :model="form" :rules="rules" ref="LoginForm" size="large">
|
||||
<el-form-item prop="name">
|
||||
<el-input placeholder="用户名或手机号" v-model="form.name" clearable>
|
||||
<template slot="prefix">
|
||||
<el-icon :size="20" color="var(--el-color-primary)" class="inputIcon">
|
||||
<user-filled/>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<div class="sub subTitle" v-text="`管理端使用`"/>
|
||||
<el-form :model="form" :rules="rules" ref="LoginForm">
|
||||
<el-form-item prop="phone">
|
||||
<el-input placeholder="用户名或手机号" v-model="form.phone" clearable/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input placeholder="请输入密码" type="password" v-model="form.password" clearable show-password>
|
||||
@@ -23,12 +17,18 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="btn">登录</div>
|
||||
<div class="btn" @click="handleLogin">登录</div>
|
||||
<el-row justify="end">
|
||||
<el-button class="rightBtn" type="text" @click="changePwd=true">修改密码</el-button>
|
||||
<el-link class="rightBtn" @click="changePwd=true" :underline="false">修改密码</el-link>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="divider" v-text="`第三方账号登录`"/>
|
||||
<el-row class="tpLogin" align="middle">
|
||||
<el-tooltip v-for="(tp,i) in tpLoginList" :key="i" :content="tp.label" placement="top">
|
||||
<ku-icon class="item" v-bind="tp" @click.native.stop="tp.click"/>
|
||||
</el-tooltip>
|
||||
</el-row>
|
||||
</div>
|
||||
<change-pwd v-bind="$props" @back="changePwd=false"/>
|
||||
</section>
|
||||
@@ -36,12 +36,17 @@
|
||||
|
||||
<script>
|
||||
|
||||
import {Lock, UserFilled} from "@element-plus/icons-vue";
|
||||
import {Lock} from "@element-plus/icons-vue";
|
||||
import ChangePwd from "./changePwd";
|
||||
import KuIcon from "../KuIcon";
|
||||
import qs from "query-string"
|
||||
import md5 from "md5"
|
||||
import {mapActions} from "pinia/dist/pinia";
|
||||
import {mainStore} from "../../utils/store";
|
||||
|
||||
export default {
|
||||
name: "KuLogin",
|
||||
components: {ChangePwd, UserFilled, Lock},
|
||||
components: {KuIcon, ChangePwd, Lock},
|
||||
props: {
|
||||
title: {default: "登录组件"},
|
||||
border: Boolean
|
||||
@@ -49,8 +54,38 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
rules: {},
|
||||
changePwd: false
|
||||
rules: {
|
||||
password: {required: true, message: "请输入密码"},
|
||||
phone: {required: true, message: "请输入手机号"},
|
||||
},
|
||||
changePwd: false,
|
||||
tpLoginList: [
|
||||
{
|
||||
label: "微信登录", icon: "icon-WeChat", color: "#2BA245", click: () => {
|
||||
location.href = qs.stringifyUrl({
|
||||
url: "https://open.weixin.qq.com/connect/qrconnect",
|
||||
query: {
|
||||
appid: "wx68ef6cfaa104652a",
|
||||
redirect_uri: encodeURIComponent(location.href),
|
||||
response_type: "code",
|
||||
scope: "snsapi_login"
|
||||
},
|
||||
fragmentIdentifier: "wechat_redirect"
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(mainStore, ['getToken']),
|
||||
handleLogin() {
|
||||
this.$refs.LoginForm.validate(v => {
|
||||
if (v) {
|
||||
const password = md5(this.form.password)
|
||||
this.getToken({...this.form, password}).then(() => this.$emit("login"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +93,7 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.KuLogin {
|
||||
width: 630px;
|
||||
width: 400px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -89,6 +124,7 @@ export default {
|
||||
font-size: 32px;
|
||||
font-family: PingFang SC-Bold, PingFang SC;
|
||||
line-height: 38px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
@@ -97,8 +133,8 @@ export default {
|
||||
|
||||
.btn {
|
||||
text-align: center;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
background: var(--el-color-primary);
|
||||
@@ -112,9 +148,8 @@ export default {
|
||||
|
||||
.el-input {
|
||||
.el-input__inner {
|
||||
line-height: 60px;
|
||||
height: 60px;
|
||||
padding-left: 44px !important;
|
||||
line-height: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.el-input__prefix-inner {
|
||||
@@ -130,6 +165,36 @@ export default {
|
||||
.el-row {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.divider {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
margin: 12px 0;
|
||||
|
||||
&:before, &:after {
|
||||
content: "——";
|
||||
margin: 0 11px;
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
.tpLogin {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.item {
|
||||
cursor: pointer;
|
||||
margin: 8px;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
font-size: 28px;
|
||||
border-radius: 50%;
|
||||
line-height: 48px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,18 +7,21 @@ import tools from "./utils/tools";
|
||||
import 'element-plus/dist/index.css'
|
||||
import eui from 'element-plus'
|
||||
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
||||
import persist from "pinia-plugin-persist"
|
||||
import "./assets/iconfont.css"
|
||||
|
||||
const app = createApp(App)
|
||||
app.config.globalProperties.$http = axios
|
||||
app.config.globalProperties.$sys = {
|
||||
title: "综合大数据平台中心"
|
||||
title: "综合业务管理平台"
|
||||
}
|
||||
Object.keys(tools).map(e => app.config.globalProperties[e] = tools[e])
|
||||
router.then(r => {
|
||||
app.use(eui, {locale: zhCn})
|
||||
app.use(r)
|
||||
app.use(createPinia())
|
||||
const store = createPinia()
|
||||
store.use(persist)
|
||||
app.use(store)
|
||||
app.mount('#app')
|
||||
})
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import axios from "axios";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {getToken} from "./tools";
|
||||
import {mainStore} from "./store";
|
||||
|
||||
|
||||
let ins = axios.create({
|
||||
baseURL: process.env.NODE_ENV == "production" ? "/" : "/dev", timeout: 600000, withCredentials: true,
|
||||
})
|
||||
const ins = axios.create({baseURL: "/", timeout: 600000, withCredentials: true})
|
||||
|
||||
ins.interceptors.request.use(config => {
|
||||
if (!config.withoutToken && !!getToken()) {
|
||||
@@ -23,13 +21,20 @@ ins.interceptors.request.use(config => {
|
||||
ins.interceptors.response.use(res => {
|
||||
if (res?.data?.code == 0) {
|
||||
return res.data
|
||||
} else if (res?.data?.code == 1) {
|
||||
!res.config?.customReturn && res.data?.msg && ElMessage.error(res.data.msg)
|
||||
return res.data
|
||||
} else if (res?.data?.code == 401) {
|
||||
console.error("安全令牌验证无法通过!")
|
||||
mainStore().logout()
|
||||
location.replace("/")
|
||||
} else if (res?.data) {
|
||||
return res.data
|
||||
} else {
|
||||
ElMessage.error("服务器异常,请联系管理员!")
|
||||
console.error("服务器异常,请联系管理员!")
|
||||
}
|
||||
}, ({response}) => {
|
||||
if (response?.data?.error) ElMessage.error(response.data.error)
|
||||
})
|
||||
export default ins
|
||||
35
web/src/utils/dayjs.js
Normal file
35
web/src/utils/dayjs.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import moment from 'dayjs'
|
||||
import duration from 'dayjs/plugin/duration'
|
||||
import updateLocale from 'dayjs/plugin/updateLocale'
|
||||
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
||||
import 'dayjs/locale/zh-cn'
|
||||
|
||||
moment.locale('zh-cn')
|
||||
moment.extend(updateLocale)
|
||||
moment.extend(customParseFormat)
|
||||
moment.extend(duration)
|
||||
moment.updateLocale('zh-cn', {
|
||||
weekdays: "星期日|星期一|星期二|星期三|星期四|星期五|星期六".split("|"),
|
||||
meridiem(hour) {
|
||||
let word = ""
|
||||
if (hour < 6) {
|
||||
word = "凌晨"
|
||||
} else if (hour < 9) {
|
||||
word = "早上"
|
||||
} else if (hour < 12) {
|
||||
word = "上午"
|
||||
} else if (hour < 14) {
|
||||
word = "中午"
|
||||
} else if (hour < 17) {
|
||||
word = "下午"
|
||||
} else if (hour < 19) {
|
||||
word = "傍晚"
|
||||
} else if (hour < 22) {
|
||||
word = "晚上"
|
||||
} else {
|
||||
word = "夜里"
|
||||
}
|
||||
return word;
|
||||
}
|
||||
})
|
||||
export default moment
|
||||
@@ -1,7 +1,37 @@
|
||||
import {defineStore} from "pinia/dist/pinia";
|
||||
import http from "./axios";
|
||||
|
||||
export const mainStore = defineStore('main', {
|
||||
state: () => ({
|
||||
user: {}
|
||||
})
|
||||
user: {},
|
||||
token: ""
|
||||
}),
|
||||
actions: {
|
||||
getToken(params) {
|
||||
return http.post("/api/auth/token", null, {
|
||||
params: {...params}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
return this.token = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
getUserInfo() {
|
||||
http.post("/api/user/info").then(res => {
|
||||
if (res?.data) {
|
||||
this.user = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
logout() {
|
||||
this.user = {}
|
||||
this.token = ""
|
||||
}
|
||||
},
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{storage: localStorage}
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -1,7 +1,9 @@
|
||||
export const getToken = () => localStorage.getItem("token")
|
||||
export const $confirm = ()=>{
|
||||
import {mainStore} from "./store";
|
||||
|
||||
export const getToken = () => mainStore()?.token
|
||||
export const $confirm = () => {
|
||||
|
||||
}
|
||||
export default {
|
||||
getToken,$confirm
|
||||
getToken, $confirm
|
||||
}
|
||||
@@ -32,7 +32,7 @@
|
||||
<router-view class="fill"/>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<div class="copyright color-a2v1c5" v-text="`copyright @2022 ${$sys.title}`"/>
|
||||
<div class="copyright sub" v-text="`copyright @2022 ${$sys.title}`"/>
|
||||
</div>
|
||||
</el-row>
|
||||
</section>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<ku-login :title="$sys.title" border/>
|
||||
<div class="copyright color-a2v1c5" v-text="`copyright @2022 ${$sys.title}`"/>
|
||||
<ku-login :title="$sys.title" border @login="$message.success('登录成功!')"/>
|
||||
<div class="copyright sub" v-text="`copyright @2022 ${$sys.title}`"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -6,5 +6,13 @@ export default defineConfig({
|
||||
resolve: {
|
||||
extensions: ['.vue', '.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']
|
||||
},
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
port: 3000,
|
||||
open: true,
|
||||
proxy: {
|
||||
'/api': 'http://localhost:7001'
|
||||
}
|
||||
},
|
||||
plugins: [vue()]
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user