添加必要组件;优化项目架构
This commit is contained in:
parent
37e4e0d4a0
commit
a258aa05cf
39
index.html
39
index.html
@ -1,13 +1,34 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8"/>
|
||||||
<link rel="icon" type="image/svg+xml" href="/logo.png" />
|
<link rel="icon" type="image/svg+xml" href="/logo.png"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>营养与健康数据管理处理平台</title>
|
<title>营养与健康数据管理处理平台</title>
|
||||||
</head>
|
<style>
|
||||||
<body>
|
.app-init-loading{font-size:14px;line-height:1.5;background-color:#fff;color:#2d8cf0;align-items:center;justify-content:center;text-align:center;display:flex;height:100vh;position:fixed;inset:0}
|
||||||
<div id="app"></div>
|
@keyframes app-init-rotate{100%{transform:rotate(360deg)}
|
||||||
<script type="module" src="/src/main.ts"></script>
|
}
|
||||||
</body>
|
@keyframes app-init-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}
|
||||||
|
50%{stroke-dasharray:89,200;stroke-dashoffset:-35px}
|
||||||
|
100%{stroke-dasharray:89,200;stroke-dashoffset:-124px}
|
||||||
|
}
|
||||||
|
.app-init-loading .circular{width:50px;height:50px;animation:app-init-rotate 2s linear infinite;margin:auto}
|
||||||
|
.app-init-loading .path{stroke-dasharray:1,200;stroke-dashoffset:0;stroke-width:2px;stroke:#2d8cf0;animation:app-init-dash 1.5s ease-in-out infinite}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<div class="app-init-loading">
|
||||||
|
<div class="app-init-inner">
|
||||||
|
<svg class="circular" viewbox="25 25 50 50">
|
||||||
|
<circle class="path" cx="50" cy="50" r="20" fill="none"/>
|
||||||
|
</svg>
|
||||||
|
<div class="description">初始化中...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -9,9 +9,11 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@handsontable/vue3": "^14.0.0",
|
"dayjs": "^1.11.10",
|
||||||
|
"pinia": "^2.1.7",
|
||||||
"view-ui-plus": "^1.3.15",
|
"view-ui-plus": "^1.3.15",
|
||||||
"vue": "^3.4.0"
|
"vue": "^3.4.0",
|
||||||
|
"vue-router": "4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.10.5",
|
"@types/node": "^20.10.5",
|
||||||
|
81
src/App.vue
81
src/App.vue
@ -1,69 +1,32 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {onMounted, ref} from "vue";
|
import PageLoading from "./components/page-loading/index.vue";
|
||||||
|
|
||||||
|
import {useUserStore} from "./service/user-store.ts";
|
||||||
import Login from "./components/login/index.vue";
|
import Login from "./components/login/index.vue";
|
||||||
import DataFields from "./components/data-fields/index.vue";
|
|
||||||
import Product from "./components/product/index.vue";
|
|
||||||
import Field from "./components/field/index.vue";
|
|
||||||
import Result from "./components/result/index.vue";
|
|
||||||
|
|
||||||
import {MenuItem, Menu, Icon} from "view-ui-plus";
|
|
||||||
import {useHash} from "./service/router.ts";
|
|
||||||
|
|
||||||
const page_data = ref({
|
|
||||||
showLogin: false,
|
|
||||||
})
|
|
||||||
// 登录相关
|
// 登录相关
|
||||||
const LOGIN_SESSION_KEY = 'x-yy-js-data-user';
|
|
||||||
const onLoginSuccess = () => {
|
|
||||||
page_data.value.showLogin = false
|
const store = useUserStore()
|
||||||
localStorage.setItem(LOGIN_SESSION_KEY, Date.now().toString(16));
|
//
|
||||||
}
|
// const MENU_LIST = [
|
||||||
onMounted(() => {
|
// {title: '药品管理', name: 'product', icon: 'ios-cube'},
|
||||||
const loginSession = localStorage.getItem(LOGIN_SESSION_KEY);
|
// {title: '数据项管理', name: 'field', icon: 'ios-apps'},
|
||||||
console.log('loginSession', loginSession)
|
// {title: '数据管理', name: 'product_value', icon: 'ios-paper'},
|
||||||
if (!loginSession || (Date.now() - parseInt(loginSession, 16) > 3600 * 24 * 1000)) {
|
// {title: '计算结果', name: 'result', icon: 'ios-calculator'},
|
||||||
page_data.value.showLogin = true
|
// ]
|
||||||
}
|
// const activeMenu = ref(MENU_LIST[0].name)
|
||||||
})
|
|
||||||
const MENU_LIST = [
|
|
||||||
{title: '药品管理', name: 'product', icon: 'ios-cube'},
|
|
||||||
{title: '数据项管理', name: 'field', icon: 'ios-apps'},
|
|
||||||
{title: '数据管理', name: 'product_value', icon: 'ios-paper'},
|
|
||||||
{title: '计算结果', name: 'result', icon: 'ios-calculator'},
|
|
||||||
]
|
|
||||||
const hash = useHash()
|
|
||||||
const activeMenu = ref((hash.value || MENU_LIST[0].name))
|
|
||||||
const onMenuSelect = (name: string) => {
|
|
||||||
activeMenu.value = name
|
|
||||||
window.location.hash = name
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<Login v-if="page_data.showLogin" @login-success="onLoginSuccess"/>
|
<PageLoading :class="{ hideLoading: store.userInit }"/>
|
||||||
<!-- <Layout />-->
|
<Login v-if="store.userInit && (!store.userInfo || store.userInfo.uid < 1) " />
|
||||||
<div class="app-header">
|
<template v-if="store.userInit">
|
||||||
<div class="app-logo">
|
<router-view />
|
||||||
<img class="logo" src="./assets/images/logo.png" alt=""/>
|
</template>
|
||||||
</div>
|
|
||||||
<div class="app-menu">
|
|
||||||
<Menu mode="horizontal" :active-name="activeMenu" @on-select="onMenuSelect">
|
|
||||||
<MenuItem v-for="m in MENU_LIST" :key="m.name" :name="m.name">
|
|
||||||
<Icon :type="m.icon" size="18"/>
|
|
||||||
<span>{{ m.title }}</span>
|
|
||||||
</MenuItem>
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="app-content">
|
|
||||||
<DataFields v-if="activeMenu == 'product_value'"/>
|
|
||||||
<Field v-else-if="activeMenu == 'field'"/>
|
|
||||||
<Product v-else-if="activeMenu == 'product'"/>
|
|
||||||
<Result v-else/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -74,6 +37,12 @@ const onMenuSelect = (name: string) => {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hideLoading {
|
||||||
|
background: rgba(255, 255, 255, 0);
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.app-header {
|
.app-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -114,5 +83,5 @@ body {
|
|||||||
will-change: filter;
|
will-change: filter;
|
||||||
transition: filter 300ms;
|
transition: filter 300ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
./service/api/user.ts
|
||||||
|
@ -77,9 +77,12 @@ const onInputPaste = (e: ClipboardEvent, pIndex: number, fIndex: number) => {
|
|||||||
//border-bottom: solid 1px #eee;
|
//border-bottom: solid 1px #eee;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.item:not(.item-header) {
|
// .item:not(.item-header) {
|
||||||
background: #fafafa;
|
// background: #fafafa;
|
||||||
}
|
// }
|
||||||
|
}
|
||||||
|
&:focus-within{
|
||||||
|
background-color: #fcf2e0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,9 +115,12 @@ const onInputPaste = (e: ClipboardEvent, pIndex: number, fIndex: number) => {
|
|||||||
//transform: translateX(-1px) translateY(-1px);
|
//transform: translateX(-1px) translateY(-1px);
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: 0 0 5px rgba(87, 116, 189, 0.5) inset;
|
// box-shadow: 0 0 5px rgba(87, 116, 189, 0.5) inset;
|
||||||
//box-shadow: 0 0 1px rgb(0, 0, 0) inset;
|
//box-shadow: 0 0 1px rgb(0, 0, 0) inset;
|
||||||
//outline: solid 1px rgb(87, 116, 189);
|
outline: solid 2px rgb(87, 116, 189);
|
||||||
|
position: relative;
|
||||||
|
left:0px;
|
||||||
|
background-color: #fff;
|
||||||
//border-color: #ccc;
|
//border-color: #ccc;
|
||||||
//background: rgba(87,87,189,0.15);
|
//background: rgba(87,87,189,0.15);
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {Login, UserName, Password, Submit, Message} from 'view-ui-plus';
|
import {Login, UserName, Password, Submit, Message} from 'view-ui-plus';
|
||||||
|
import {useUserStore} from "../../service/user-store.ts";
|
||||||
|
import {BizError} from "../../core/errors.ts";
|
||||||
|
|
||||||
type LoginModel = {
|
const handleSubmit = (...e: any) => {
|
||||||
username: string;
|
const [valid, params] = e as [valid: true, params: any];
|
||||||
password: string;
|
const store = useUserStore();
|
||||||
}
|
|
||||||
//const emits = defineEmits(['loginSuccess'])
|
|
||||||
const emits = defineEmits<{
|
|
||||||
(e: 'loginSuccess'): void
|
|
||||||
}>()
|
|
||||||
const handleSubmit = (...e:any) => {
|
|
||||||
const [valid,params] = e as [valid: true, params: LoginModel];
|
|
||||||
if (valid) {
|
if (valid) {
|
||||||
if (params.username == 'admin' && params.password == 'admin') {
|
store.login(params).catch((e: BizError) => Message.info(e.message))
|
||||||
emits('loginSuccess')
|
|
||||||
} else {
|
|
||||||
Message.error('登录信息不正确')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +21,7 @@ const handleSubmit = (...e:any) => {
|
|||||||
</div>
|
</div>
|
||||||
<p class="desc">营养与健康数据管理处理平台</p>
|
<p class="desc">营养与健康数据管理处理平台</p>
|
||||||
<Login @on-submit="handleSubmit">
|
<Login @on-submit="handleSubmit">
|
||||||
<UserName name="username" enter-to-submit/>
|
<UserName name="account" enter-to-submit/>
|
||||||
<Password name="password" enter-to-submit/>
|
<Password name="password" enter-to-submit/>
|
||||||
<div class="submit">
|
<div class="submit">
|
||||||
<Submit/>
|
<Submit/>
|
||||||
@ -48,7 +39,7 @@ const handleSubmit = (...e:any) => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: rgba(0, 0, 0, 0.1);
|
background: rgba(0, 0, 0, 0.3);
|
||||||
z-index: 101;
|
z-index: 101;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
110
src/components/page-loading/index.vue
Normal file
110
src/components/page-loading/index.vue
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Icon, Spin } from 'view-ui-plus'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="page-loading">
|
||||||
|
<Spin fix>
|
||||||
|
<!-- <div class="loader">
|
||||||
|
<svg class="circular" viewBox="25 25 50 50">
|
||||||
|
<circle class="path" cx="50" cy="50" r="20" fill="none" stroke-width="5" stroke-miterlimit="10">
|
||||||
|
</circle>
|
||||||
|
</svg>
|
||||||
|
</div> -->
|
||||||
|
<Icon type="ios-loading" size="40" class="demo-spin-icon-load"></Icon>
|
||||||
|
<div class="description">初始化中...</div>
|
||||||
|
</Spin>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="app-init-loading">-->
|
||||||
|
<!-- <div class="app-init-inner">-->
|
||||||
|
<!-- <svg class="circular" viewbox="25 25 50 50">-->
|
||||||
|
<!-- <circle class="path" cx="50" cy="50" r="20" fill="none"/>-->
|
||||||
|
<!-- </svg>-->
|
||||||
|
<!-- <div class="description">初始化中...</div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.page-loading {
|
||||||
|
height: 100vh;
|
||||||
|
background:#fff;
|
||||||
|
position:fixed;
|
||||||
|
inset:0;
|
||||||
|
z-index:9999;
|
||||||
|
transition: all 0.5s;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-spin-col .circular {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-spin-icon-load {
|
||||||
|
animation: ani-demo-spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
to {
|
||||||
|
-webkit-transform: rotate(1turn);
|
||||||
|
transform: rotate(1turn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dash {
|
||||||
|
0% {
|
||||||
|
stroke-dasharray: 1, 200;
|
||||||
|
stroke-dashoffset: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
stroke-dasharray: 89, 200;
|
||||||
|
stroke-dashoffset: -35
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
stroke-dasharray: 89, 200;
|
||||||
|
stroke-dashoffset: -124
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circular {
|
||||||
|
animation: rotate 2s linear infinite;
|
||||||
|
|
||||||
|
.path {
|
||||||
|
stroke-dasharray: 1, 200;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ani-demo-spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-spin-col {
|
||||||
|
height: 100px;
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
</style>
|
8
src/core/errors.ts
Normal file
8
src/core/errors.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export class BizError extends Error {
|
||||||
|
code: number;
|
||||||
|
|
||||||
|
constructor(code: number, message: string='') {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
}
|
5
src/core/sleep.ts
Normal file
5
src/core/sleep.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export function sleep(time: number) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, time);
|
||||||
|
})
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
// import ViewUIPlus from 'view-ui-plus'
|
|
||||||
import 'view-ui-plus/dist/styles/viewuiplus.css'
|
import 'view-ui-plus/dist/styles/viewuiplus.css'
|
||||||
import './style.css'
|
import { createPinia } from 'pinia'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
import './style.css'
|
||||||
|
|
||||||
createApp(App)
|
createApp(App)
|
||||||
// .use(ViewUIPlus)
|
.use(createPinia())
|
||||||
|
.use(router)
|
||||||
.mount('#app')
|
.mount('#app')
|
||||||
|
26
src/pages/Layout.vue
Normal file
26
src/pages/Layout.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import {routes} from "../router.ts";
|
||||||
|
import {useUserStore} from "../service/user-store.ts";
|
||||||
|
import {computed} from "vue";
|
||||||
|
const store = useUserStore()
|
||||||
|
const currentMenus = computed(()=>{
|
||||||
|
if(!store.userInfo) return []
|
||||||
|
return routes.filter((s)=>{
|
||||||
|
return !s.meta || !s.meta['role'] || store.userInfo?.role == s.meta.role;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="layout">
|
||||||
|
<h1>12312</h1>
|
||||||
|
<div class="menu">
|
||||||
|
<div v-for="r in currentMenus">
|
||||||
|
<router-link :to="(r.path||'/')">{{r.meta?.title}}</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
3
src/pages/datas.vue
Normal file
3
src/pages/datas.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<h1>result</h1>
|
||||||
|
</template>
|
3
src/pages/result.vue
Normal file
3
src/pages/result.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<h1>输出计算</h1>
|
||||||
|
</template>
|
3
src/pages/user.vue
Normal file
3
src/pages/user.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<h1>user</h1>
|
||||||
|
</template>
|
69
src/router.ts
Normal file
69
src/router.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import {onMounted, onUnmounted, ref} from "vue";
|
||||||
|
import {createRouter, createWebHashHistory, RouteRecordRaw} from "vue-router";
|
||||||
|
|
||||||
|
function getHash() {
|
||||||
|
const hash = window.location.hash
|
||||||
|
return hash.length > 0 ? hash.slice(1) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useHash = () => {
|
||||||
|
const hash = ref<string>(getHash())
|
||||||
|
|
||||||
|
const onHashChange = () => {
|
||||||
|
hash.value = getHash()
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('hashchange', onHashChange, false)
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('hashchange', onHashChange, false)
|
||||||
|
})
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
export const routes:RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
name: 'home',
|
||||||
|
meta: {
|
||||||
|
title: '输出计算'
|
||||||
|
},
|
||||||
|
component: () => import('./pages/result.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'data',
|
||||||
|
name: 'data',
|
||||||
|
meta: {
|
||||||
|
title: '数据管理'
|
||||||
|
},
|
||||||
|
component: () => import('./pages/datas.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'user',
|
||||||
|
name: 'user',
|
||||||
|
component: () => import('./pages/user.vue'),
|
||||||
|
meta: {
|
||||||
|
role: 'root',
|
||||||
|
title: '用户管理'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const router = createRouter({
|
||||||
|
routes: [
|
||||||
|
// 路由配置
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: () => import('./pages/Layout.vue'),
|
||||||
|
children: routes
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// path: '/login',
|
||||||
|
// name: 'login',
|
||||||
|
// component: () => import('./pages/login.vue')
|
||||||
|
// }
|
||||||
|
],
|
||||||
|
history: createWebHashHistory()
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
29
src/service/api/user.ts
Normal file
29
src/service/api/user.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import {BizError} from "../../core/errors.ts";
|
||||||
|
|
||||||
|
export type LoginModel = {
|
||||||
|
account: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeUser: UserInfo = {
|
||||||
|
account: "admin",
|
||||||
|
nickname: "管理员",
|
||||||
|
password: "",
|
||||||
|
role: 'root',
|
||||||
|
uid: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function LoginService(params: LoginModel) {
|
||||||
|
if (params.account == 'admin' && params.password == 'admin') {
|
||||||
|
const loginInfo = Date.now().toString(16);
|
||||||
|
return {
|
||||||
|
userinfo: fakeUser,
|
||||||
|
token: loginInfo
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new BizError(1001, '用户名或密码错误');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GetLoginInfo() {
|
||||||
|
return fakeUser;
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
import {onMounted, onUnmounted, ref} from "vue";
|
|
||||||
|
|
||||||
function getHash() {
|
|
||||||
const hash = window.location.hash
|
|
||||||
return hash.length > 0 ? hash.slice(1) : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useHash = () => {
|
|
||||||
const hash = ref<string>(getHash())
|
|
||||||
|
|
||||||
const onHashChange = () => {
|
|
||||||
hash.value = getHash()
|
|
||||||
}
|
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener('hashchange', onHashChange, false)
|
|
||||||
})
|
|
||||||
onUnmounted(() => {
|
|
||||||
window.removeEventListener('hashchange', onHashChange, false)
|
|
||||||
})
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
63
src/service/user-store.ts
Normal file
63
src/service/user-store.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import {defineStore} from "pinia"
|
||||||
|
import {onMounted, ref} from "vue"
|
||||||
|
import {GetLoginInfo, LoginService} from "./api/user";
|
||||||
|
import {BizError} from "../core/errors.ts";
|
||||||
|
import router from "../router.ts";
|
||||||
|
import {sleep} from "../core/sleep.ts";
|
||||||
|
|
||||||
|
type LoginParam = {
|
||||||
|
account: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LOGIN_SESSION_KEY = 'x-yy-js-data-user';
|
||||||
|
|
||||||
|
export const useUserStore = defineStore('counter', () => {
|
||||||
|
const userInfo = ref<UserInfo>()
|
||||||
|
const userInit = ref(false)
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
const login = async (data: LoginParam) => {
|
||||||
|
await sleep(1000);
|
||||||
|
const info = await LoginService(data);
|
||||||
|
userInfo.value = info.userinfo;
|
||||||
|
localStorage.setItem(LOGIN_SESSION_KEY, info.token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登出
|
||||||
|
const logout = async () => {
|
||||||
|
localStorage.removeItem(LOGIN_SESSION_KEY)
|
||||||
|
userInfo.value = undefined;
|
||||||
|
}
|
||||||
|
// 更新用户信息
|
||||||
|
const updateUserInfo = async (info: UserInfo) => {
|
||||||
|
userInfo.value = info
|
||||||
|
}
|
||||||
|
// 获取用户信息
|
||||||
|
const getUserInfo = async () => {
|
||||||
|
const token = localStorage.getItem(LOGIN_SESSION_KEY);
|
||||||
|
if (!token) {
|
||||||
|
throw new BizError(401)
|
||||||
|
}
|
||||||
|
userInfo.value = await GetLoginInfo();
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
getUserInfo().catch((e: BizError) => {
|
||||||
|
if (e.code == 401) {
|
||||||
|
router.replace(`/login?redirect=${router.currentRoute.value.path}`).then(() => console.log('401 show login'))
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
console.log('onMounted inited')
|
||||||
|
userInit.value = true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
userInfo,
|
||||||
|
userInit,
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
updateUserInfo,
|
||||||
|
getUserInfo
|
||||||
|
}
|
||||||
|
})
|
15
src/vite-env.d.ts
vendored
15
src/vite-env.d.ts
vendored
@ -1 +1,16 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
type int = number;
|
||||||
|
type double = number;
|
||||||
|
type bool = boolean;
|
||||||
|
type AccountRole = 'root' | 'admin' | 'user';
|
||||||
|
type UserInfo = {
|
||||||
|
uid: number;
|
||||||
|
nickname: string;
|
||||||
|
account: string;
|
||||||
|
password: string;
|
||||||
|
avatar?: string;
|
||||||
|
lastUpdate?:string;
|
||||||
|
createTime?: string;
|
||||||
|
role: AccountRole;
|
||||||
|
}
|
35
yarn.lock
35
yarn.lock
@ -117,11 +117,6 @@
|
|||||||
resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
|
resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
|
||||||
integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==
|
integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==
|
||||||
|
|
||||||
"@handsontable/vue3@^14.0.0":
|
|
||||||
version "14.0.0"
|
|
||||||
resolved "https://registry.npmmirror.com/@handsontable/vue3/-/vue3-14.0.0.tgz#7c62091cc3393cb556771d9678cced4622095f1c"
|
|
||||||
integrity sha512-4/XuCxXw+8d3fHB3/v0zbb9PYgyjJfU6Tol+dsxRHk0L8FGUFCM8WIDFn3DCb5PHtE2peQBIUl/HvnsCpPpEMg==
|
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.4.15":
|
"@jridgewell/sourcemap-codec@^1.4.15":
|
||||||
version "1.4.15"
|
version "1.4.15"
|
||||||
resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
|
resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
|
||||||
@ -203,6 +198,11 @@
|
|||||||
"@vue/compiler-dom" "3.4.0"
|
"@vue/compiler-dom" "3.4.0"
|
||||||
"@vue/shared" "3.4.0"
|
"@vue/shared" "3.4.0"
|
||||||
|
|
||||||
|
"@vue/devtools-api@^6.5.0":
|
||||||
|
version "6.5.1"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz#7f71f31e40973eeee65b9a64382b13593fdbd697"
|
||||||
|
integrity sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==
|
||||||
|
|
||||||
"@vue/language-core@1.8.27":
|
"@vue/language-core@1.8.27":
|
||||||
version "1.8.27"
|
version "1.8.27"
|
||||||
resolved "https://registry.npmmirror.com/@vue/language-core/-/language-core-1.8.27.tgz"
|
resolved "https://registry.npmmirror.com/@vue/language-core/-/language-core-1.8.27.tgz"
|
||||||
@ -332,6 +332,11 @@ dayjs@^1.11.0:
|
|||||||
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz"
|
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz"
|
||||||
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
|
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
|
||||||
|
|
||||||
|
dayjs@^1.11.10:
|
||||||
|
version "1.11.10"
|
||||||
|
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
|
||||||
|
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
|
||||||
|
|
||||||
de-indent@^1.0.2:
|
de-indent@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz"
|
resolved "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz"
|
||||||
@ -511,6 +516,14 @@ picomatch@^2.0.4, picomatch@^2.2.1:
|
|||||||
resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz"
|
resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz"
|
||||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||||
|
|
||||||
|
pinia@^2.1.7:
|
||||||
|
version "2.1.7"
|
||||||
|
resolved "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz#4cf5420d9324ca00b7b4984d3fbf693222115bbc"
|
||||||
|
integrity sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==
|
||||||
|
dependencies:
|
||||||
|
"@vue/devtools-api" "^6.5.0"
|
||||||
|
vue-demi ">=0.14.5"
|
||||||
|
|
||||||
popper.js@^1.14.6:
|
popper.js@^1.14.6:
|
||||||
version "1.16.1"
|
version "1.16.1"
|
||||||
resolved "https://registry.npmmirror.com/popper.js/-/popper.js-1.16.1.tgz"
|
resolved "https://registry.npmmirror.com/popper.js/-/popper.js-1.16.1.tgz"
|
||||||
@ -622,6 +635,18 @@ vite@^4.3.2:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
vue-demi@>=0.14.5:
|
||||||
|
version "0.14.6"
|
||||||
|
resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.6.tgz#dc706582851dc1cdc17a0054f4fec2eb6df74c92"
|
||||||
|
integrity sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==
|
||||||
|
|
||||||
|
vue-router@4:
|
||||||
|
version "4.2.5"
|
||||||
|
resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.2.5.tgz#b9e3e08f1bd9ea363fdd173032620bc50cf0e98a"
|
||||||
|
integrity sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==
|
||||||
|
dependencies:
|
||||||
|
"@vue/devtools-api" "^6.5.0"
|
||||||
|
|
||||||
vue-template-compiler@^2.7.14:
|
vue-template-compiler@^2.7.14:
|
||||||
version "2.7.16"
|
version "2.7.16"
|
||||||
resolved "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz"
|
resolved "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user