11
This commit is contained in:
parent
3db326afa9
commit
eaa2a85c80
11
.eslintrc.cjs
Normal file
11
.eslintrc.cjs
Normal file
@ -0,0 +1,11 @@
|
||||
/* eslint-env node */
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
6
.vscode/extensions.json
vendored
Normal file
6
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"dbaeumer.vscode-eslint"
|
||||
]
|
||||
}
|
21
index.html
Normal file
21
index.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>demo</title>
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<!-- 这里是你的单页面应用内容 -->
|
||||
</div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
<script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
12
jsconfig.json
Normal file
12
jsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
3554
package-lock.json
generated
Normal file
3554
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
33
package.json
Normal file
33
package.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "fat-vue3",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/fetch-event-source": "^2.0.1",
|
||||
"axios": "^1.6.8",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts-stat": "^1.2.0",
|
||||
"element-plus": "^2.6.3",
|
||||
"md-editor-v3": "^4.17.4",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0",
|
||||
"vue3-scroll-seamless": "^1.0.6",
|
||||
"vue3-seamless-scroll": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"echarts": "^5.5.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"sass": "^1.75.0",
|
||||
"vite": "^5.2.8"
|
||||
}
|
||||
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
41
src/App.vue
Normal file
41
src/App.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<script setup>
|
||||
import router from '@/router/index'
|
||||
import {ref,onMounted} from 'vue'
|
||||
|
||||
|
||||
|
||||
const isLogging=()=>{
|
||||
const user=localStorage.getItem('userInfo')
|
||||
if(user){
|
||||
console.log('登录中')
|
||||
if(!JSON.parse(user).token){
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
console.log(isLogging())
|
||||
if(isLogging()){
|
||||
router.replace({ path: "/index" })
|
||||
}else{
|
||||
router.replace({ path: "/login" })
|
||||
}
|
||||
|
||||
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div> <RouterView /></div>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
29
src/apis/loginApi.ts
Normal file
29
src/apis/loginApi.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import http from '@/utils/http'
|
||||
|
||||
//用户登录
|
||||
export function loginAPI({ account, password}) {
|
||||
|
||||
return http({
|
||||
url: 'admin/login',
|
||||
method: 'POST',
|
||||
data: {
|
||||
username: account,
|
||||
password: password
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//账密注册
|
||||
export function registerAPI({ account, password,password2}) {
|
||||
return http({
|
||||
url: 'user/register',
|
||||
method: 'POST',
|
||||
data: {
|
||||
account: account,
|
||||
password: password,
|
||||
password2:password2
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
86
src/assets/base.css
Normal file
86
src/assets/base.css
Normal file
@ -0,0 +1,86 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
BIN
src/assets/loading.gif
Normal file
BIN
src/assets/loading.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
1
src/assets/logo.svg
Normal file
1
src/assets/logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
After Width: | Height: | Size: 276 B |
35
src/assets/main.css
Normal file
35
src/assets/main.css
Normal file
@ -0,0 +1,35 @@
|
||||
@import './base.css';
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
BIN
src/assets/speech.gif
Normal file
BIN
src/assets/speech.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
BIN
src/assets/think.gif
Normal file
BIN
src/assets/think.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
BIN
src/assets/头像.png
Normal file
BIN
src/assets/头像.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
35
src/main.js
Normal file
35
src/main.js
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
import router from '@/router/index'
|
||||
import ElementPlus from 'element-plus'
|
||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||||
import 'element-plus/dist/index.css'
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
|
||||
// **main.js**
|
||||
import {vue3ScrollSeamless} from "vue3-scroll-seamless";
|
||||
|
||||
|
||||
|
||||
// 引入初始化样式文件
|
||||
import '@/styles/common.scss'
|
||||
// 数据可视化
|
||||
import * as echarts from 'echarts'
|
||||
const pinia = createPinia()
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
app.use(pinia)
|
||||
app.use(echarts)
|
||||
|
||||
app.component('vue3ScrollSeamless',vue3ScrollSeamless)
|
||||
|
||||
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component)
|
||||
}
|
||||
app.use(ElementPlus, {
|
||||
locale: zhCn,
|
||||
})
|
||||
app.mount('#app')
|
31
src/router/index.js
Normal file
31
src/router/index.js
Normal file
@ -0,0 +1,31 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
// import Container from '@/views/container.vue'
|
||||
import Layout from '@/views/layout/Layout.vue'
|
||||
import Index from '@/views/pages/index/Index.vue'
|
||||
import Line from '@/views/pages/line/Line.vue'
|
||||
import Login from '@/views/pages/login/Login.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'container',
|
||||
component: Layout,
|
||||
children:[
|
||||
{path:'/',redirect:"/index"},
|
||||
{path:'/index',component:Index},
|
||||
{path:'/line',component:Line},
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: Login
|
||||
}
|
||||
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
73
src/stores/user.js
Normal file
73
src/stores/user.js
Normal file
@ -0,0 +1,73 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import router from '@/router/index'
|
||||
import { loginAPI,registerAPI} from '@/apis/loginApi'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
// 定义管理用户数据的state
|
||||
const userInfo = ref({
|
||||
name:'',
|
||||
token:''
|
||||
})
|
||||
|
||||
// 从本地存储中加载数据
|
||||
const loadUserInfoFromLocalStorage = () => {
|
||||
const savedUserInfo = JSON.parse(localStorage.getItem('userInfo'))
|
||||
if (savedUserInfo) {
|
||||
console.log('用户信息本地存在!')
|
||||
userInfo.value = savedUserInfo
|
||||
return
|
||||
}
|
||||
// localStorage.removeItem('userInfo')
|
||||
// sessionStorage.clear()
|
||||
// if(router.currentRoute.value.path==='/login'){
|
||||
// return
|
||||
// }
|
||||
// ElMessage({ type: 'error', message: '登录状态丢失,请重新登录!' })
|
||||
// router.replace('/login')
|
||||
}
|
||||
|
||||
// 在 store 创建时加载数据
|
||||
loadUserInfoFromLocalStorage()
|
||||
|
||||
//账密登录
|
||||
const getUserInfo = async ({ account, password }) => {
|
||||
console.log('本地没有用户信息,开始请求接口')
|
||||
const res = await loginAPI({ account: account, password: password })
|
||||
if(res.code===0){
|
||||
userInfo.value = res.data
|
||||
// 保存数据到本地存储
|
||||
localStorage.setItem('userInfo', JSON.stringify(res.data))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// 注册并登录
|
||||
const registerAndLogin = async ({ account, password, password2 }) => {
|
||||
const res = await registerAPI({ account: account, password: password, password2: password2 })
|
||||
if(res.code===0){
|
||||
userInfo.value = res.data
|
||||
// 保存数据到本地存储
|
||||
localStorage.setItem('userInfo', JSON.stringify(res.data))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 清除用户信息
|
||||
const clearUserInfo = () => {
|
||||
userInfo.value = {}
|
||||
localStorage.removeItem('userInfo')
|
||||
}
|
||||
|
||||
// 返回state和actions
|
||||
return {
|
||||
userInfo,
|
||||
getUserInfo,
|
||||
clearUserInfo,
|
||||
registerAndLogin
|
||||
}
|
||||
})
|
86
src/styles/common.scss
Normal file
86
src/styles/common.scss
Normal file
@ -0,0 +1,86 @@
|
||||
// 重置样式
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
font-size: 14px;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 0px; /* 设置滚动条的宽度 */
|
||||
height: 0px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #ffffff; /* 设置滚动条轨道的背景颜色 */
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #00144d; /* 设置滚动条滑块的背景颜色 */
|
||||
}
|
||||
body {
|
||||
// transform: scale(0.8);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
// color: #ffffff;
|
||||
background-color: #ffffff;
|
||||
/* 隐藏水平滚动条 */
|
||||
overflow-x:auto;
|
||||
min-width: 100vw;
|
||||
font: 1em/1.4 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI',
|
||||
'Hiragino Sans GB', 'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei',
|
||||
sans-serif;
|
||||
}
|
||||
body,
|
||||
ul,
|
||||
h1,
|
||||
h3,
|
||||
h4,
|
||||
p,
|
||||
dl,
|
||||
dd {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
outline: none;
|
||||
}
|
||||
i {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
vertical-align: middle;
|
||||
// background: #ebebeb url('@/assets/images/200.png') no-repeat center / contain;
|
||||
//当图片未获取到的默认图片
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// display: flex;
|
||||
// min-height: 100%;
|
||||
// user-select: none;//禁止复制
|
||||
// background: #f7b8b8;
|
||||
|
||||
}
|
||||
|
||||
.common-layout{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
.el-link:hover {
|
||||
color: #ff6600;
|
||||
}
|
||||
.el-link:hover .el-icon {
|
||||
color: #ff6600;
|
||||
}
|
||||
|
65
src/utils/http.js
Normal file
65
src/utils/http.js
Normal file
@ -0,0 +1,65 @@
|
||||
//axios基础的封装
|
||||
import axios from "axios"
|
||||
import { ElMessage } from "element-plus"
|
||||
import "element-plus/theme-chalk/el-message.css"
|
||||
import { useUserStore } from "@/stores/user"
|
||||
// import router from "@/router/index"
|
||||
|
||||
//axios基础封装1(默认)
|
||||
const httpInstance1 = axios.create({
|
||||
//配置基地址
|
||||
baseURL: "http://192.168.1.6:8080/",
|
||||
// baseURL: "http://www.mi9688.top/api/",
|
||||
|
||||
//配置超时时间
|
||||
timeout: 20000,
|
||||
})
|
||||
|
||||
// axios请求拦截器
|
||||
httpInstance1.interceptors.request.use(
|
||||
(config) => {
|
||||
const userStore = useUserStore();
|
||||
const token = userStore.userInfo.token;
|
||||
// console.log("请求拦截器得到token:" + token);
|
||||
config.headers["Content-Type"] = "application/json";
|
||||
if (token) {
|
||||
config.headers.Authorization = "Bearer " + token;
|
||||
}
|
||||
eval()
|
||||
return config;
|
||||
},
|
||||
(e) => Promise.reject(e)
|
||||
)
|
||||
|
||||
// axios响应式拦截器
|
||||
httpInstance1.interceptors.response.use(
|
||||
(res) => res.data,
|
||||
(e) => {
|
||||
console.log("响应拦截器:")
|
||||
console.log(e.response)
|
||||
//统一错误提示
|
||||
|
||||
|
||||
//服务器错误处理
|
||||
if (e.response.status === 500) {
|
||||
ElMessage({
|
||||
type: "error",
|
||||
message: "服务器错误!",
|
||||
})
|
||||
}
|
||||
//业务异常处理
|
||||
if(e.response.data.code===0){
|
||||
ElMessage({
|
||||
type: "error",
|
||||
message: e.response.data.message,
|
||||
})
|
||||
}
|
||||
ElMessage({
|
||||
type: "error",
|
||||
message: "网络波动异常!",
|
||||
})
|
||||
return Promise.reject(e)
|
||||
}
|
||||
);
|
||||
|
||||
export default httpInstance1
|
90
src/views/container.vue
Normal file
90
src/views/container.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="common-layout">
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ElContainer, ElHeader, ElMain } from 'element-plus'
|
||||
import LayoutHeader from '@/views/layout/LayoutHeader.vue'
|
||||
import User from '@/views/layout/User.vue'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
/* background-color: bisque; */
|
||||
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: rgb(0, 0, 0, 0.3);
|
||||
/* box-shadow: 0px 0px 1px 1px rgb(255, 255, 255); */
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
/* height: 100%; */
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
width: 100%;
|
||||
/* height: 10%; */
|
||||
display: inline-flex;
|
||||
background-color: antiquewhite;
|
||||
background-image: url('../assets/layout-bg.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
}
|
||||
|
||||
.header-title {
|
||||
width: 300px;
|
||||
height: 40px;
|
||||
/* background-color: aqua; */
|
||||
font-size: 25px;
|
||||
color: rgb(255, 255, 255);
|
||||
font-family: Arial, sans-serif;
|
||||
position: relative;
|
||||
right: 405px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.user {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 10px
|
||||
}
|
||||
|
||||
.main {
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
.canvas {
|
||||
z-index: 0;
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.zoom-content {
|
||||
position: absolute;
|
||||
min-width: 100vw;
|
||||
min-height: 100vh;
|
||||
background-color: rgb(255, 255, 255, 0.01);
|
||||
}
|
||||
|
||||
.all-content {
|
||||
background-color: rgb(161, 202, 202, 0.1);
|
||||
}
|
||||
</style>
|
47
src/views/echarts/temp.vue
Normal file
47
src/views/echarts/temp.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="card-container">
|
||||
<div class="title-container">肥胖者年龄与性别分布</div>
|
||||
<div class="chart" id="ping"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref ,onMounted} from 'vue'
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
|
||||
|
||||
const init=()=>{
|
||||
|
||||
}
|
||||
|
||||
|
||||
onMounted(()=>{
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-container{
|
||||
width: 400px;
|
||||
height: 350px;
|
||||
|
||||
}
|
||||
.title-container{
|
||||
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 18px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
top:10px;
|
||||
|
||||
}
|
||||
.chart{
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
62
src/views/layout/Layout.vue
Normal file
62
src/views/layout/Layout.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-container class="layout-container-demo" style="height: 100vh">
|
||||
<el-aside>
|
||||
<div class="aside" id="left-aside">
|
||||
<the-aside></the-aside>
|
||||
</div>
|
||||
</el-aside>
|
||||
|
||||
<el-container>
|
||||
<el-header>
|
||||
<the-header></the-header>
|
||||
</el-header>
|
||||
|
||||
<el-main>
|
||||
<el-scrollbar>
|
||||
<router-view></router-view>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
|
||||
</el-container>
|
||||
</el-container>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref,onMounted} from 'vue'
|
||||
|
||||
import TheAside from '@/views/layout/TheAside.vue'
|
||||
import TheHeader from '@/views/layout/TheHeader.vue'
|
||||
|
||||
|
||||
onMounted(()=>{
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-container-demo .el-header {
|
||||
position: relative;
|
||||
background-color: #ffffff;
|
||||
color: #ffffff;
|
||||
transition: background-color 1s ease;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.layout-container-demo .el-aside {
|
||||
color: #ffffff;
|
||||
background-color: #5183e0;
|
||||
// border-right: 2px solid #007bff;
|
||||
transition: background-color 1s ease;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.layout-container-demo .el-main {
|
||||
padding: 0;
|
||||
background-color: #ececec;
|
||||
transition: background-color 1s ease;
|
||||
// margin: 5px;
|
||||
}
|
||||
</style>
|
99
src/views/layout/LayoutHeader.vue
Normal file
99
src/views/layout/LayoutHeader.vue
Normal file
@ -0,0 +1,99 @@
|
||||
<script lang="ts" setup>
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
const userStore = useUserStore()
|
||||
import { ref } from 'vue';
|
||||
import router from '../../router/index'
|
||||
|
||||
|
||||
|
||||
|
||||
//搜索框输入内容
|
||||
const input2 = ref('');
|
||||
//一级功能列表
|
||||
const f1List=ref([
|
||||
{id:1,name:'首页',path:'/index',icon:'House',color:'white'},
|
||||
{id:3,name:'在线预测',path:'/predict',icon:'Aim',color:'white'},
|
||||
{id:2,name:'指标分析',path:'/metrics',icon:'Aim',color:'white'},
|
||||
{id:5,name:'智能问答',path:'/qanda',icon:'Aim',color:'white'},
|
||||
{id:6,name:'菜品识别',path:'/dishRecords',icon:'Aim',color:'white'},
|
||||
{id:4,name:'我的记录',path:'/records',icon:'Aim',color:'white'},
|
||||
|
||||
|
||||
])
|
||||
const selecedFunction = ref(0)
|
||||
const seleced = (index) => {
|
||||
//重置
|
||||
sessionStorage.removeItem('selectedFunction1')
|
||||
//持久化选中功能
|
||||
sessionStorage.setItem('selectedFunction', index)
|
||||
selecedFunction.value = index
|
||||
}
|
||||
|
||||
//重载恢复状态
|
||||
const selectedVal = sessionStorage.getItem('selectedFunction')
|
||||
if (selectedVal !== null) {
|
||||
selecedFunction.value = parseInt(selectedVal)
|
||||
} else {
|
||||
if (userStore.userInfo.token) {
|
||||
router.replace('/index')
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class='content-item'>
|
||||
<div class="flist">
|
||||
<ul class="ul">
|
||||
<li class="f1" v-for="(item, index) in f1List" :key="item.id" :class="{ 'seleced': index === selecedFunction }">
|
||||
<RouterLink :to="item.path" @click="seleced(index)">
|
||||
<div >
|
||||
<div class="text">{{ item.name }}</div>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped lang='scss'>
|
||||
.content-item {
|
||||
width:1100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 55px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
.flist{
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ul {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
li{
|
||||
|
||||
|
||||
margin: 10px;
|
||||
display: block;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
}
|
||||
.text{
|
||||
color: rgb(17, 193, 232);
|
||||
font-size: 15px
|
||||
}
|
||||
|
||||
.seleced {
|
||||
color: chartreuse;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
21
src/views/layout/TheAside.vue
Normal file
21
src/views/layout/TheAside.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div>
|
||||
<TheLogo></TheLogo>
|
||||
</div>
|
||||
<el-scrollbar max-height="1000px">
|
||||
<div class="the-menu">
|
||||
<TheMenu></TheMenu>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import TheMenu from '@/views/layout/TheMenu.vue';
|
||||
import TheLogo from '@/views/layout/TheLogo.vue';
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
89
src/views/layout/TheHeader.vue
Normal file
89
src/views/layout/TheHeader.vue
Normal file
@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="toolbar">
|
||||
<div style="position: absolute;right: 20px;"><User /></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import User from './User.vue';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.re {
|
||||
color: var(--txt-color);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: inline-flex;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
right: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.theme {
|
||||
position: absolute;
|
||||
right: 110px;
|
||||
|
||||
.theme-img {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--prominent-color);
|
||||
border-radius: 10px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.user {
|
||||
display: inline-flex;
|
||||
position: absolute;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
right: 0%;
|
||||
width: 120px;
|
||||
height: 40px;
|
||||
/* background-color: rgb(236, 144, 24); */
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-left: 3px;
|
||||
width: 4.3em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.nologin {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* background-color: aqua; */
|
||||
right: 0%;
|
||||
width: 80px;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
58
src/views/layout/TheLogo.vue
Normal file
58
src/views/layout/TheLogo.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="logo-wrapper vertical-text" id="logo-wrapper">
|
||||
<div class="logp-image-container"><img class="logo" src="../../assets/logo.svg" alt=""> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss" >
|
||||
.container {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color 1s ease;
|
||||
|
||||
}
|
||||
|
||||
.logo-wrapper {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
.vertical-text {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-size: var(--logo-txt-size);
|
||||
color: transparent;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.logp-image-container{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
}
|
||||
.logo{
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-image: url('../../assets/logo.svg');
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.divider{
|
||||
margin: auto;
|
||||
margin-top: 20px;
|
||||
width: 60%;
|
||||
border-bottom: 0.5px solid var(--txt-color);
|
||||
opacity: 0.2;
|
||||
}
|
||||
</style>
|
138
src/views/layout/TheMenu.vue
Normal file
138
src/views/layout/TheMenu.vue
Normal file
@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div class="navigation">
|
||||
<ul>
|
||||
<li class="list">
|
||||
<el-tooltip placement="right" content="item1">
|
||||
<RouterLink :to="'index'">
|
||||
<a href="#">
|
||||
<span class="icon"><el-icon size="18">
|
||||
<House />
|
||||
</el-icon></span>
|
||||
</a>
|
||||
</RouterLink>
|
||||
</el-tooltip>
|
||||
</li>
|
||||
<li class="list ">
|
||||
<el-tooltip placement="right" content="item2">
|
||||
<RouterLink :to="'line'">
|
||||
<a href="#">
|
||||
<span class="icon"><el-icon size="18">
|
||||
<EditPen />
|
||||
</el-icon></span>
|
||||
</a>
|
||||
</RouterLink>
|
||||
</el-tooltip>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
onMounted(() => {
|
||||
const list = document.querySelectorAll('.list');
|
||||
|
||||
function activelink() {
|
||||
list.forEach((item) =>
|
||||
item.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
// 保存当前选中菜单的索引到localStorage
|
||||
localStorage.setItem('selectedMenuIndex', Array.from(list).indexOf(this));
|
||||
}
|
||||
|
||||
list.forEach((item) =>
|
||||
item.addEventListener('click', activelink));
|
||||
|
||||
// 读取localStorage中保存的选中菜单的索引,设置选中状态
|
||||
const selectedMenuIndex = localStorage.getItem('selectedMenuIndex');
|
||||
if (selectedMenuIndex) {
|
||||
list[selectedMenuIndex].classList.add('active');
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.navigation {
|
||||
|
||||
position: relative;
|
||||
margin: auto;
|
||||
min-height: 500px;
|
||||
width: 80%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
box-sizing: initial;
|
||||
background-color: var(--bg2);
|
||||
transition: background-color 1s ease;
|
||||
overflow-x: hidden;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.navigation ul {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.navigation ul li {
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
border-radius: 18px;
|
||||
|
||||
}
|
||||
|
||||
.navigation ul li.active {
|
||||
background: var(--active-color);
|
||||
|
||||
}
|
||||
|
||||
.navigation ul li a {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
color: var(--txt-color);
|
||||
}
|
||||
|
||||
.navigation ul li.active a {
|
||||
color: var(--prominent-txt-color);
|
||||
}
|
||||
|
||||
.navigation ul li a .icon {
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.navigation ul li a .icon ion-icon {
|
||||
position: relative;
|
||||
font-size: 1.5em;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.navigation ul li a .left-menu-item-title {
|
||||
position: relative;
|
||||
right: 25px;
|
||||
display: block;
|
||||
padding-left: 10px;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
64
src/views/layout/User.vue
Normal file
64
src/views/layout/User.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<script setup lang="ts">
|
||||
import { ref} from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
//退出登录
|
||||
const exit = () => {
|
||||
localStorage.removeItem('userInfo')
|
||||
sessionStorage.clear()
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div v-if="userStore.userInfo.token" class="avatar">
|
||||
<el-dropdown>
|
||||
<img class="avatar-img" src="@/assets/头像.png" width="30px" alt="">
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
|
||||
<el-dropdown-item @click="exit">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="" v-else>
|
||||
<div class="line">|</div>
|
||||
<RouterLink to="/login">
|
||||
<div class="login">登录</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<style>
|
||||
.avatar {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.login {
|
||||
color: rgb(0, 0, 0);
|
||||
font-size: 16px;
|
||||
/* position: relative;
|
||||
top: 10px; */
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.line {
|
||||
color: white;
|
||||
position: relative;
|
||||
/* top: 10px; */
|
||||
right: 10px;
|
||||
display: inline-flex;
|
||||
}
|
||||
</style>
|
181
src/views/layout/UserInfoEdit.vue
Normal file
181
src/views/layout/UserInfoEdit.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog v-model="dialogVisible" width="500" draggable modal :before-close="() => { goBack() }">
|
||||
<div class="title-container">
|
||||
<div class="title">基本信息</div>
|
||||
</div>
|
||||
<div class="user-info-container">
|
||||
<div class="center">
|
||||
<el-form ref="ruleFormRef" :model="userForm" :rules="rules">
|
||||
<el-form-item label="" prop="username">
|
||||
<span slot="label">
|
||||
<span>用户昵称</span>
|
||||
<span style="color: red">*</span>
|
||||
</span>
|
||||
|
||||
<el-input v-model="userForm.username" placeholder="请填写你的用户名称"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="" prop="account">
|
||||
<span slot="label">
|
||||
<span>账号</span>
|
||||
<span style="color: red">*</span>
|
||||
</span>
|
||||
<el-input v-model="userForm.account" placeholder="请填写你的账号" disabled></el-input>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="" prop="password">
|
||||
<span slot="label">
|
||||
<span>密码</span>
|
||||
<span style="color: red">*</span>
|
||||
<el-input v-model="userForm.password" placeholder="请填写你的密码" type="password" show-password></el-input>
|
||||
</span>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
|
||||
</el-form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="proposal-container">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button icon="Close" @click="goBack()">取消</el-button>
|
||||
<el-button type="primary" icon="SuccessFilled" color="gold" @click="saveEdit(ruleFormRef)">
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted, defineProps } from 'vue'
|
||||
import { ElDialog, ElForm, ElFormItem,ElMessage } from 'element-plus'
|
||||
import { getUserInfoAPI, updateUserInfoAPI } from '@/apis/userApi'
|
||||
import type { ComponentSize, FormInstance, FormRules } from 'element-plus'
|
||||
import { el } from 'element-plus/es/locale';
|
||||
|
||||
interface userForm {
|
||||
username: string,
|
||||
account: string,
|
||||
password: string,
|
||||
|
||||
}
|
||||
|
||||
|
||||
const userForm = reactive<userForm>({
|
||||
username: '',
|
||||
account: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules<userForm>>({
|
||||
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
message: '用户名不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
min: 1,
|
||||
max: 20,
|
||||
message: '用户名长度超过限制',
|
||||
trigger: 'blur',
|
||||
}
|
||||
],
|
||||
password:[
|
||||
{
|
||||
required:true,
|
||||
message:'密码不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
min: 6,
|
||||
max: 16,
|
||||
message: '密码长度必须在6~16位之间',
|
||||
trigger: 'blur',
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
})
|
||||
const ruleFormRef = ref<FormInstance>()
|
||||
//关闭事件
|
||||
const emit = defineEmits(["exitEditPage", "flushListPage"])
|
||||
function goBack() {
|
||||
emit("exitEditPage");
|
||||
}
|
||||
const dialogVisible = ref(true)
|
||||
//获取用户基本信息
|
||||
const getUserInfo = async () => {
|
||||
const res = await getUserInfoAPI()
|
||||
if (res.code == 0) {
|
||||
userForm.username = res.data.user_name
|
||||
userForm.account = res.data.user_account
|
||||
userForm.password = res.data.password
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//修改用户基本信息
|
||||
|
||||
const saveEdit = (formEl: FormInstance | undefined) => {
|
||||
console.log('submit!')
|
||||
if (!formEl) return
|
||||
formEl.validate(async (valid, fields) => {
|
||||
if (valid) {
|
||||
|
||||
const res=await updateUserInfoAPI(userForm)
|
||||
if(res.code===0){
|
||||
ElMessage({type:'success',message:'修改成功!'})
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log('error submit!', fields)
|
||||
ElMessage({ type: 'warning', message: '请完整填写表单!' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getUserInfo()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
// height: 32px;
|
||||
// background-color: aqua;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
19
src/views/pages/index/Index.vue
Normal file
19
src/views/pages/index/Index.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div >
|
||||
<h1>index1111111111111111111111111111111111</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref ,onMounted} from 'vue'
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
19
src/views/pages/line/Line.vue
Normal file
19
src/views/pages/line/Line.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div >
|
||||
<h1>line</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref ,onMounted} from 'vue'
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
471
src/views/pages/login/Login.vue
Normal file
471
src/views/pages/login/Login.vue
Normal file
@ -0,0 +1,471 @@
|
||||
<template>
|
||||
<div class="login-root">
|
||||
<div class="box-root flex-flex flex-direction--column" style="min-height: 100vh;flex-grow: 1;">
|
||||
<div class="loginbackground box-background--white padding-top--64">
|
||||
<div class="loginbackground-gridContainer">
|
||||
<div class="box-root flex-flex" style="grid-area: top / start / 8 / end;">
|
||||
<div class="box-root"
|
||||
style="background-image: linear-gradient(white 0%, rgb(247, 250, 252) 33%); flex-grow: 1;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-root flex-flex" style="grid-area: 4 / 2 / auto / 5;">
|
||||
<div class="box-root box-divider--light-all-2 animationLeftRight tans3s" style="flex-grow: 1;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-root flex-flex" style="grid-area: 6 / start / auto / 2;">
|
||||
<div class="box-root box-background--blue800" style="flex-grow: 1;"></div>
|
||||
</div>
|
||||
<div class="box-root flex-flex" style="grid-area: 7 / start / auto / 4;">
|
||||
<div class="box-root box-background--blue animationLeftRight" style="flex-grow: 1;"></div>
|
||||
</div>
|
||||
<div class="box-root flex-flex" style="grid-area: 8 / 4 / auto / 6;">
|
||||
<div class="box-root box-background--gray100 animationLeftRight tans3s" style="flex-grow: 1;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-root flex-flex" style="grid-area: 2 / 15 / auto / end;">
|
||||
<div class="box-root box-background--cyan200 animationRightLeft tans4s" style="flex-grow: 1;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-root flex-flex" style="grid-area: 3 / 14 / auto / end;">
|
||||
<div class="box-root box-background--blue animationRightLeft" style="flex-grow: 1;"></div>
|
||||
</div>
|
||||
<div class="box-root flex-flex" style="grid-area: 4 / 17 / auto / 20;">
|
||||
<div class="box-root box-background--gray100 animationRightLeft tans4s" style="flex-grow: 1;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-root flex-flex" style="grid-area: 5 / 14 / auto / 17;">
|
||||
<div class="box-root box-divider--light-all-2 animationRightLeft tans3s" style="flex-grow: 1;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-root padding-top--24 flex-flex flex-direction--column" style="flex-grow: 1; z-index: 9;">
|
||||
<div class="box-root padding-top--48 padding-bottom--24 flex-flex flex-justifyContent--center">
|
||||
<h1 v-if="page === 'login'"><a href="http://blog.stackfindover.com/" rel="dofollow">管理员登录</a></h1>
|
||||
</div>
|
||||
<div class="formbg-outer">
|
||||
<div class="formbg">
|
||||
<div class="formbg-inner padding-horizontal--48">
|
||||
|
||||
|
||||
<form id="stripe-login" v-show="page === 'login'">
|
||||
<div class="field padding-bottom--24">
|
||||
<label for="account">账号</label>
|
||||
<input id="account" name="account" pattern=".{5,18}" title="账号长度必须为5到18位" placeholder="输入账号"
|
||||
required>
|
||||
</div>
|
||||
<div class="field padding-bottom--24">
|
||||
<div class="grid--50-50">
|
||||
<label for="password">密码</label>
|
||||
<div class="reset-pass">
|
||||
<!-- <a href="#">忘记密码?</a> -->
|
||||
</div>
|
||||
</div>
|
||||
<input id="password" type="password" name="password" pattern=".{6,16}"
|
||||
title="密码长度必须为6到16位" placeholder="输入密码" required>
|
||||
</div>
|
||||
<div class="field field-checkbox padding-bottom--24 flex-flex align-center">
|
||||
<label for="checkbox">
|
||||
<input id="savePassword" type="checkbox"> 记住密码
|
||||
</label>
|
||||
</div>
|
||||
<div class="field padding-bottom--24">
|
||||
<input type="submit" name="submit" value="登录">
|
||||
</div>
|
||||
<div class="field">
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-link padding-top--24">
|
||||
|
||||
<div class="listing padding-top--24 padding-bottom--24 flex-flex center-center">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { registerAPI, loginAPI } from '@/apis/loginApi'
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { ElMain, ElMessage ,ElNotification} from 'element-plus';
|
||||
import router from '@/router/index'
|
||||
import CryptoJS from "crypto-js";
|
||||
const userStore = useUserStore();
|
||||
const page = ref('login')
|
||||
|
||||
|
||||
//记住密码
|
||||
const savaPassword = () => {
|
||||
var rememberPasswordCheckbox = document.getElementById("savePassword");
|
||||
if (rememberPasswordCheckbox.checked) {
|
||||
console.log("记住密码已勾选:"+document.getElementById("account").value);
|
||||
localStorage.setItem('account', document.getElementById("account").value)
|
||||
localStorage.setItem('password', CryptoJS.AES.encrypt(document.getElementById("password").value, 'mijiu').toString())
|
||||
} else {
|
||||
console.log("记住密码未勾选");
|
||||
}
|
||||
}
|
||||
//加载账密
|
||||
const loadcp = () => {
|
||||
var account = localStorage.getItem("account") ? localStorage.getItem("account") : null;
|
||||
var password = localStorage.getItem("password") ? CryptoJS.AES.decrypt(localStorage.getItem("password"), 'mijiu').toString(CryptoJS.enc.Utf8) : null;
|
||||
document.getElementById("account").value = account
|
||||
document.getElementById("password").value = password
|
||||
document.getElementById("savePassword").checked = true
|
||||
|
||||
};
|
||||
|
||||
//用户登录
|
||||
const login = async () => {
|
||||
|
||||
document.getElementById("stripe-login").addEventListener("submit", async function (event) {
|
||||
event.preventDefault();
|
||||
var account = document.getElementById("account").value;
|
||||
var password = document.getElementById("password").value;
|
||||
const res = await userStore.getUserInfo({ account, password })
|
||||
if (res.code === 0) {
|
||||
// ElMessage({ type: 'success', message: '登录成功' })
|
||||
ElNotification({
|
||||
title: '登录成功!',
|
||||
message: '欢迎您登录系统!',
|
||||
type: 'success',
|
||||
})
|
||||
savaPassword()
|
||||
document.getElementById("account").value=''
|
||||
document.getElementById("password").value=''
|
||||
router.replace({ path: "/index" })
|
||||
}
|
||||
else{
|
||||
ElMessage({ type: 'error', message: res.message })
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
//用户注册
|
||||
// const register = async () => {
|
||||
// document.getElementById("stripe-register").addEventListener("submit", async function (event) {
|
||||
// event.preventDefault();
|
||||
// var account = document.getElementById("account_r").value;
|
||||
// var password = document.getElementById("password_r").value;
|
||||
// var password2 = document.getElementById("password2").value;
|
||||
// //校验两次密码是否一致
|
||||
// if (password !== password2) {
|
||||
// ElMessage({ type: 'error', message: '两次密码输入不一致!' })
|
||||
// return
|
||||
// }
|
||||
// const res = await userStore.registerAndLogin({ account: account, password: password, password2: password2 })
|
||||
|
||||
// if (res.code === 0) {
|
||||
|
||||
// ElNotification({
|
||||
// title: '登录成功!',
|
||||
// message: '欢迎您登录该平台',
|
||||
// type: 'success',
|
||||
// })
|
||||
// savaPassword()
|
||||
// document.getElementById("account_r").value=''
|
||||
// document.getElementById("password_r").value=''
|
||||
// document.getElementById("password2").value=''
|
||||
// router.replace({ path: "/index" })
|
||||
|
||||
// }else{
|
||||
// ElMessage({ type: 'error', message: res.message })
|
||||
// }
|
||||
|
||||
// })
|
||||
// }
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
login()
|
||||
// register()
|
||||
loadcp()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: #1a1f36;
|
||||
box-sizing: border-box;
|
||||
word-wrap: break-word;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Ubuntu, sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #5469d4;
|
||||
text-decoration: unset;
|
||||
}
|
||||
|
||||
.login-root {
|
||||
background-color: #212d63;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.loginbackground {
|
||||
min-height: 692px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.flex-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.center-center {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.box-root {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.flex-direction--column {
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.loginbackground-gridContainer {
|
||||
display: -ms-grid;
|
||||
display: grid;
|
||||
-ms-grid-columns: [start] 1fr [left-gutter] (86.6px)[16] [left-gutter] 1fr [end];
|
||||
grid-template-columns: [start] 1fr [left-gutter] repeat(16, 86.6px) [left-gutter] 1fr [end];
|
||||
-ms-grid-rows: [top] 1fr [top-gutter] (64px)[8] [bottom-gutter] 1fr [bottom];
|
||||
grid-template-rows: [top] 1fr [top-gutter] repeat(8, 64px) [bottom-gutter] 1fr [bottom];
|
||||
justify-content: center;
|
||||
margin: 0 -2%;
|
||||
transform: rotate(-12deg) skew(-12deg);
|
||||
}
|
||||
|
||||
.box-divider--light-all-2 {
|
||||
box-shadow: inset 0 0 0 2px #e3e8ee;
|
||||
}
|
||||
|
||||
.box-background--blue {
|
||||
background-color: #5469d4;
|
||||
}
|
||||
|
||||
.box-background--white {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.box-background--blue800 {
|
||||
background-color: #212d63;
|
||||
}
|
||||
|
||||
.box-background--gray100 {
|
||||
background-color: #e3e8ee;
|
||||
}
|
||||
|
||||
.box-background--cyan200 {
|
||||
background-color: #7fd3ed;
|
||||
}
|
||||
|
||||
.padding-top--64 {
|
||||
padding-top: 64px;
|
||||
}
|
||||
|
||||
.padding-top--24 {
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.padding-top--48 {
|
||||
padding-top: 48px;
|
||||
}
|
||||
|
||||
.padding-bottom--24 {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.padding-horizontal--48 {
|
||||
padding: 48px;
|
||||
}
|
||||
|
||||
.padding-bottom--15 {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
|
||||
.flex-justifyContent--center {
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.formbg {
|
||||
margin: 0px auto;
|
||||
width: 100%;
|
||||
max-width: 448px;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: rgba(60, 66, 87, 0.12) 0px 7px 14px 0px, rgba(0, 0, 0, 0.12) 0px 3px 6px 0px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
color: #1a1f36;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.reset-pass a,
|
||||
label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.reset-pass>a {
|
||||
text-align: right;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.grid--50-50 {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.field input {
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
padding: 8px 16px;
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
border: unset;
|
||||
border-radius: 4px;
|
||||
outline-color: rgb(84 105 212 / 0.5);
|
||||
background-color: rgb(255, 255, 255);
|
||||
box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(60, 66, 87, 0.16) 0px 0px 0px 1px,
|
||||
rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(0, 0, 0, 0) 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
background-color: rgb(84, 105, 212);
|
||||
box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(0, 0, 0, 0.12) 0px 1px 1px 0px,
|
||||
rgb(84, 105, 212) 0px 0px 0px 1px,
|
||||
rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
||||
rgba(60, 66, 87, 0.08) 0px 2px 5px 0px;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.field-checkbox input {
|
||||
width: 20px;
|
||||
height: 15px;
|
||||
margin-right: 5px;
|
||||
box-shadow: unset;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
.field-checkbox label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a.ssolink {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.footer-link span {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.listing a {
|
||||
color: #697386;
|
||||
font-weight: 600;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.animationRightLeft {
|
||||
animation: animationRightLeft 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animationLeftRight {
|
||||
animation: animationLeftRight 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.tans3s {
|
||||
animation: animationLeftRight 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.tans4s {
|
||||
animation: animationLeftRight 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes animationLeftRight {
|
||||
0% {
|
||||
transform: translateX(0px);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateX(1000px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(0px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animationRightLeft {
|
||||
0% {
|
||||
transform: translateX(0px);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateX(-1000px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(0px);
|
||||
}
|
||||
}
|
||||
</style>
|
28
src/views/pages/util/Loading.vue
Normal file
28
src/views/pages/util/Loading.vue
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="speechIng">
|
||||
<img src="@/assets/loading.gif" alt="加载中···">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.speechIng{
|
||||
display: flex;
|
||||
position: fixed;
|
||||
left: 45%;
|
||||
top:40%;
|
||||
z-index: 9999;
|
||||
// background-color: rgb(23, 21, 19);
|
||||
}
|
||||
|
||||
</style>
|
65
src/views/pages/util/limit.vue
Normal file
65
src/views/pages/util/limit.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref,onBeforeUpdate} from 'vue'
|
||||
import {ElPagination} from 'element-plus'
|
||||
|
||||
const emit = defineEmits(['updateItemList']);
|
||||
// 接收父组件传递的数据()
|
||||
const props = defineProps({
|
||||
itemList: {//数据列表
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
count: {//有值则分页功能后端实现
|
||||
type: Number,
|
||||
default: null
|
||||
|
||||
},
|
||||
|
||||
})
|
||||
//--------------------------------------------------------数据分页------------------------------------------------------------------
|
||||
const itemList = ref([])//待分页全部数据
|
||||
const limitItemList = ref([])//分页后数据
|
||||
const currentPage = ref(1); // 当前页码默认
|
||||
const pageSize = ref(10); // 每页显示数量
|
||||
//全部数据按每页展示数量、页码截取
|
||||
const displayedItemList = () => {
|
||||
const startIndex = (currentPage.value - 1) * pageSize.value;
|
||||
const endIndex = startIndex + pageSize.value;
|
||||
limitItemList.value = itemList.value.slice(startIndex, endIndex);
|
||||
};
|
||||
|
||||
//选中页设置为当前页
|
||||
function changePage(page:number) {
|
||||
currentPage.value = page
|
||||
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
//预处理
|
||||
onBeforeUpdate(() => {
|
||||
if (props.count != null&&props.itemList==null) {
|
||||
emit('updateItemList', currentPage.value)
|
||||
return
|
||||
}
|
||||
itemList.value = props.itemList
|
||||
displayedItemList()
|
||||
emit('updateItemList', limitItemList.value)
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div style="display: flex;justify-content: center;margin-top: 10px;">
|
||||
<el-pagination layout="prev, pager, next"
|
||||
:total="props.count == null ? itemList.length : props.count"
|
||||
:current-page.sync="currentPage"
|
||||
:page-size="pageSize"
|
||||
@current-change="changePage"
|
||||
prev-text="上一页"
|
||||
next-text="下一页">
|
||||
</el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
</style>
|
16
vite.config.js
Normal file
16
vite.config.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user