完成管理页面的结构
This commit is contained in:
parent
bd903154ab
commit
07230156e6
@ -1,19 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<button @click="showMessage">显示消息</button>
|
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
// 主要实现应用的初始化
|
||||||
import message from "./components/message";
|
import message from "./components/message";
|
||||||
|
import {useUserStore} from "./service/store";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
|
useUserStore().updateInfo();
|
||||||
|
// TODO 进行系统数据的初始化
|
||||||
return {
|
return {
|
||||||
|
// login->index->具体某个业务页面
|
||||||
showMessage() {
|
showMessage() {
|
||||||
message.toast('test display')
|
message.toast('test display')
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,44 @@
|
|||||||
:root {
|
:root {
|
||||||
--primary-color: #fff;
|
--primary-color: #1890ff;
|
||||||
|
--primary-color-hover: #40a9ff;
|
||||||
|
--primary-color-active: #096dd9;
|
||||||
|
--primary-color-outline: rgba(24, 144, 255, .2);
|
||||||
|
--primary-1: #e6f7ff;
|
||||||
|
--primary-2: #bae7ff;
|
||||||
|
--primary-3: #91d5ff;
|
||||||
|
--primary-4: #69c0ff;
|
||||||
|
--primary-5: #40a9ff;
|
||||||
|
--primary-6: #1890ff;
|
||||||
|
--primary-7: #096dd9;
|
||||||
|
--primary-color-deprecated-l-35: #cbe6ff;
|
||||||
|
--primary-color-deprecated-l-20: #7ec1ff;
|
||||||
|
--primary-color-deprecated-t-20: #46a6ff;
|
||||||
|
--primary-color-deprecated-t-50: #8cc8ff;
|
||||||
|
--primary-color-deprecated-f-12: rgba(24, 144, 255, .12);
|
||||||
|
--primary-color-active-deprecated-f-30: rgba(230, 247, 255, .3);
|
||||||
|
--primary-color-active-deprecated-d-02: #dcf4ff;
|
||||||
|
--success-color: #52c41a;
|
||||||
|
--success-color-hover: #73d13d;
|
||||||
|
--success-color-active: #389e0d;
|
||||||
|
--success-color-outline: rgba(82, 196, 26, .2);
|
||||||
|
--success-color-deprecated-bg: #f6ffed;
|
||||||
|
--success-color-deprecated-border: #b7eb8f;
|
||||||
|
--error-color: #ff4d4f;
|
||||||
|
--error-color-hover: #ff7875;
|
||||||
|
--error-color-active: #d9363e;
|
||||||
|
--error-color-outline: rgba(255, 77, 79, .2);
|
||||||
|
--error-color-deprecated-bg: #fff2f0;
|
||||||
|
--error-color-deprecated-border: #ffccc7;
|
||||||
|
--warning-color: #faad14;
|
||||||
|
--warning-color-hover: #ffc53d;
|
||||||
|
--warning-color-active: #d48806;
|
||||||
|
--warning-color-outline: rgba(250, 173, 20, .2);
|
||||||
|
--warning-color-deprecated-bg: #fffbe6;
|
||||||
|
--warning-color-deprecated-border: #ffe58f;
|
||||||
|
--info-color: #1890ff;
|
||||||
|
--info-color-deprecated-bg: #e6f7ff;
|
||||||
|
--info-color-deprecated-border: #91d5ff;
|
||||||
|
|
||||||
--primary-color-text: #333;
|
--primary-color-text: #333;
|
||||||
--font-size: 14px;
|
--font-size: 14px;
|
||||||
--font-size-small: calc(var(--font-size) - 2px);
|
--font-size-small: calc(var(--font-size) - 2px);
|
||||||
@ -8,12 +47,16 @@
|
|||||||
--border-radius: 1px;
|
--border-radius: 1px;
|
||||||
--border-radius-middle: calc(var(--border-radius) + 2px);
|
--border-radius-middle: calc(var(--border-radius) + 2px);
|
||||||
--border-radius-large: calc(var(--border-radius) + 3px);
|
--border-radius-large: calc(var(--border-radius) + 3px);
|
||||||
|
|
||||||
|
--header-height: 80px;
|
||||||
|
--left-menu-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
*, *:before, *:after {
|
*, *:before, *:after {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
@ -21,10 +64,12 @@
|
|||||||
input[type=text], input[type=password], input[type=number], textarea {
|
input[type=text], input[type=password], input[type=number], textarea {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
[class^=p-]::-ms-clear,[class*=p-]::-ms-clear,[class^=p-] input::-ms-clear,
|
|
||||||
[class*=p-] input::-ms-clear,[class^=p-] input::-ms-reveal,[class*=p-] input::-ms-reveal {
|
[class^=p-]::-ms-clear, [class*=p-]::-ms-clear, [class^=p-] input::-ms-clear,
|
||||||
|
[class*=p-] input::-ms-clear, [class^=p-] input::-ms-reveal, [class*=p-] input::-ms-reveal {
|
||||||
display: none
|
display: none
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 'Source Han Sans SC', 'Microsoft YaHei', 'Microsoft YaHei UI', "Helvetica Neue", sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 'Source Han Sans SC', 'Microsoft YaHei', 'Microsoft YaHei UI', "Helvetica Neue", sans-serif;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
@ -68,8 +113,9 @@ body {
|
|||||||
pointer-events: all
|
pointer-events: all
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-svg{
|
.icon-svg {
|
||||||
}
|
}
|
||||||
.pointer-cursor{
|
|
||||||
|
.pointer-cursor {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
3
admin-fe/src/components/input/index.ts
Normal file
3
admin-fe/src/components/input/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import PInput from './input.vue'
|
||||||
|
|
||||||
|
export default PInput
|
@ -12,4 +12,5 @@ const app = createApp(App)
|
|||||||
// 使用路由
|
// 使用路由
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(createPinia());
|
app.use(createPinia());
|
||||||
|
// 将应用实例挂载到 模板中
|
||||||
app.mount('#vue-root-app')
|
app.mount('#vue-root-app')
|
@ -3,22 +3,29 @@ import Home from '../views/admin/Home.vue'
|
|||||||
import Login from '../views/Login.vue'
|
import Login from '../views/Login.vue'
|
||||||
import NotFound from '../views/NotFound.vue'
|
import NotFound from '../views/NotFound.vue'
|
||||||
import Test from '../views/Test.vue'
|
import Test from '../views/Test.vue'
|
||||||
|
import AdminLayout from '../views/layout/AdminLayout.vue'
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: Test
|
component: AdminLayout,
|
||||||
},
|
children: [
|
||||||
{
|
{
|
||||||
path: '/home',
|
path: 'home',
|
||||||
component: Home
|
component: Home
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'test',
|
||||||
|
component: Test
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
component: Login
|
component: Login
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path:'/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
component: NotFound
|
component: NotFound
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -16,7 +16,8 @@ export const useTestStore = defineStore('test-store', {
|
|||||||
},
|
},
|
||||||
actions: {}
|
actions: {}
|
||||||
})
|
})
|
||||||
|
// 保存数据的key
|
||||||
|
const TOKEN_KEY = "user-login-token";
|
||||||
export const useUserStore = defineStore('user-store', () => {
|
export const useUserStore = defineStore('user-store', () => {
|
||||||
const userinfo = ref<AdminLoginModel>()
|
const userinfo = ref<AdminLoginModel>()
|
||||||
|
|
||||||
@ -25,11 +26,16 @@ export const useUserStore = defineStore('user-store', () => {
|
|||||||
userinfo.value = data
|
userinfo.value = data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function token() {
|
||||||
|
if (userinfo.value && userinfo.value.token) return userinfo.value.token;
|
||||||
|
return localStorage.getItem(TOKEN_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
async function login(params: any) {
|
async function login(params: any) {
|
||||||
try {
|
try {
|
||||||
const data = await http.post<AdminLoginModel>('/admin/user/login', params)
|
const data = await http.post<AdminLoginModel>('/admin/user/login', params)
|
||||||
userinfo.value = data
|
userinfo.value = data
|
||||||
localStorage.setItem("user-login-token", data.token)
|
localStorage.setItem(TOKEN_KEY, data.token)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
message.toast('登录失败:' + e.message)
|
message.toast('登录失败:' + e.message)
|
||||||
throw e;
|
throw e;
|
||||||
@ -38,10 +44,10 @@ export const useUserStore = defineStore('user-store', () => {
|
|||||||
|
|
||||||
async function logout() {
|
async function logout() {
|
||||||
const data = await http.get<AdminLoginModel>('/admin/user/info')
|
const data = await http.get<AdminLoginModel>('/admin/user/info')
|
||||||
localStorage.removeItem('user-login-token')
|
localStorage.removeItem(TOKEN_KEY)
|
||||||
userinfo.value = null
|
userinfo.value = null
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {userinfo, login, logout}
|
return {userinfo, login, logout, updateInfo, token}
|
||||||
})
|
})
|
@ -1,4 +1,6 @@
|
|||||||
import {toast} from "../components/message";
|
import {toast} from "../components/message";
|
||||||
|
import {useUserStore} from "../service/store";
|
||||||
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
|
||||||
export type HttpMethod = 'get' | 'post' | 'delete' | 'put'
|
export type HttpMethod = 'get' | 'post' | 'delete' | 'put'
|
||||||
|
|
||||||
@ -69,12 +71,16 @@ class Http {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const headers: any = {
|
||||||
|
'Content-Type': contentType
|
||||||
|
}
|
||||||
|
const token = useUserStore().token();
|
||||||
|
if (token) headers.token = token
|
||||||
|
const r = useRoute(), router = useRouter();
|
||||||
fetch(httpConfig.baseURL + url, {
|
fetch(httpConfig.baseURL + url, {
|
||||||
method,
|
method,
|
||||||
body: data,
|
body: data,
|
||||||
headers: {
|
headers,
|
||||||
'Content-Type': contentType
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.then(res => res.json()) // 只要json的响应数据
|
.then(res => res.json()) // 只要json的响应数据
|
||||||
.then((res: ResponseModel<T>) => {
|
.then((res: ResponseModel<T>) => {
|
||||||
@ -84,6 +90,9 @@ class Http {
|
|||||||
// 需要统一处理数据
|
// 需要统一处理数据
|
||||||
if (code === 403) {
|
if (code === 403) {
|
||||||
toast("登录凭证无效或者已过期")
|
toast("登录凭证无效或者已过期")
|
||||||
|
if (r.fullPath != '/login') {
|
||||||
|
router.replace('/login');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
} else if (err) {
|
} else if (err) {
|
||||||
toast(httpConfig.globalErrorHandler[code])
|
toast(httpConfig.globalErrorHandler[code])
|
||||||
|
@ -1,66 +1,69 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login-wrapper">
|
<div class="login-wrapper">
|
||||||
<form @submit="onLogin">
|
<form @submit="onLogin">
|
||||||
<p>
|
<p>
|
||||||
<input placeholder="请输入账号" type="text" v-model="data.account">
|
<PInput placeholder="请输入密码" type="text" v-model="data.account"/>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<input placeholder="请输入密码" type="text" v-model="data.password">
|
<PInput placeholder="请输入密码" type="password" v-model="data.password"/>
|
||||||
</p>
|
</p>
|
||||||
<button :disabled="loading" :type="loading?'button':'submit'">{{ loading ? '正在登录' : '登录' }}</button>
|
<button :disabled="loading" :type="loading?'button':'submit'">{{ loading ? '正在登录' : '登录' }}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {reactive, ref} from "vue";
|
import {reactive, ref} from "vue";
|
||||||
import http from "../util/http";
|
import PInput from './../components/input'
|
||||||
import {useUserStore} from "../service/store";
|
import {useUserStore} from "../service/store";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "User",
|
name: "User",
|
||||||
setup() {
|
components: {PInput},
|
||||||
// vue 数据双向绑定(响应式原理)
|
setup() {
|
||||||
// vue2 Object.definedProperty
|
// vue 数据双向绑定(响应式原理)
|
||||||
// vue3 Proxy
|
// vue2 Object.definedProperty
|
||||||
const data = reactive({
|
// vue3 Proxy
|
||||||
account: '',
|
const data = reactive({
|
||||||
password: ''
|
account: '',
|
||||||
|
password: ''
|
||||||
|
})
|
||||||
|
const store = useUserStore()
|
||||||
|
const r = useRouter()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
function onLogin(e: Event) {
|
||||||
|
e.preventDefault()
|
||||||
|
loading.value = true
|
||||||
|
store.login(data)
|
||||||
|
.then(() => {
|
||||||
|
r.replace('/home').then(() => console.log('success')).catch(e => console.log(e))
|
||||||
})
|
})
|
||||||
const store = useUserStore()
|
.catch(e => {
|
||||||
const r = useRouter()
|
console.log(e)
|
||||||
|
}).finally(() => {
|
||||||
const loading = ref(false)
|
loading.value = false
|
||||||
|
})
|
||||||
function onLogin(e: Event) {
|
|
||||||
e.preventDefault()
|
|
||||||
loading.value = true
|
|
||||||
store.login(data)
|
|
||||||
.then(() => {
|
|
||||||
r.replace('/home').then(() => console.log('success')).catch(e => console.log(e))
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
console.log(e)
|
|
||||||
}).finally(() => {
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
data, onLogin, loading
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data, onLogin, loading
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.error {
|
.error {
|
||||||
color: red;
|
color: red;
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-wrapper {
|
.login-wrapper {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
margin: 50px auto;
|
margin: 50px auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -20,7 +20,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import PInput from "../components/input/index.vue";
|
import PInput from "../components/input";
|
||||||
import {reactive} from "vue";
|
import {reactive} from "vue";
|
||||||
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<h1>Home</h1>
|
<h1> Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home</h1>
|
||||||
|
<h1> Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home</h1>
|
||||||
|
<h1> Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home</h1>
|
||||||
|
<h1> Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home</h1>
|
||||||
|
<h1> Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home Home</h1>
|
||||||
<router-link to="/user">User</router-link>
|
<router-link to="/user">User</router-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
71
admin-fe/src/views/layout/AdminLayout.vue
Normal file
71
admin-fe/src/views/layout/AdminLayout.vue
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-admin-layout">
|
||||||
|
<div class="header">
|
||||||
|
<!-- 头部 -->
|
||||||
|
<div class="user-info">
|
||||||
|
<span>
|
||||||
|
{{ userStore.userinfo?.account }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="main">
|
||||||
|
<div class="left-menu">左侧的菜单
|
||||||
|
<div>
|
||||||
|
<router-link to="/test">TEST</router-link>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<router-link to="/home">HOME</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<!-- 内容区域-->
|
||||||
|
<router-view/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import {useUserStore} from "../../service/store";
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.app-admin-layout {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
line-height: 80px;
|
||||||
|
padding: 0 20px;
|
||||||
|
text-align: right;
|
||||||
|
position: fixed;
|
||||||
|
height: 80px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
background-color: #eee;
|
||||||
|
padding-top: var(--header-height);
|
||||||
|
padding-left: var(--left-menu-width);
|
||||||
|
|
||||||
|
.left-menu {
|
||||||
|
width: 200px;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -2,6 +2,7 @@ import vue from '@vitejs/plugin-vue'
|
|||||||
import vueJsxPlugin from "@vitejs/plugin-vue-jsx";
|
import vueJsxPlugin from "@vitejs/plugin-vue-jsx";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
// 开发服务信息
|
||||||
server: {
|
server: {
|
||||||
host:'::'
|
host:'::'
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user