登录完成

This commit is contained in:
2023-01-14 22:55:34 +08:00
parent 7b22656d34
commit 017af8b64a
13 changed files with 313 additions and 138 deletions

View File

@@ -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"

View File

@@ -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>

View File

@@ -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";
}

View 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>

View File

@@ -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>

View File

@@ -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')
})

View File

@@ -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
View 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

View File

@@ -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}
]
}
})

View File

@@ -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
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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()]
})