项目初始化
commit
0217700c2c
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["johnsoncodehk.volar"]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
# Vue 3 + Typescript + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 and Typescript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)
|
||||
|
||||
## Type Support For `.vue` Imports in TS
|
||||
|
||||
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's `.vue` type support plugin by running `Volar: Switch TS Plugin on/off` from VSCode command palette.
|
|
@ -0,0 +1,13 @@
|
|||
<!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>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "oa-dxtc-vue",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^1.1.4",
|
||||
"axios": "^0.26.1",
|
||||
"element-plus": "^2.1.5",
|
||||
"qs": "^6.10.3",
|
||||
"vue": "^3.2.25",
|
||||
"vue-router": "^4.0.14",
|
||||
"vue-schart": "^2.0.0",
|
||||
"vuex": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^2.2.0",
|
||||
"typescript": "^4.5.4",
|
||||
"vite": "^2.8.0",
|
||||
"vue-tsc": "^0.29.8"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
|
@ -0,0 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@import "./assets/css/main.css";
|
||||
@import "./assets/css/color-dark.css";
|
||||
</style>
|
|
@ -0,0 +1,4 @@
|
|||
import axios from 'axios'
|
||||
import qs from 'qs'
|
||||
|
||||
export const customerData = query => axios.post('http://oa-dxtc.test/customer', qs.stringify(query));
|
|
@ -0,0 +1,4 @@
|
|||
import axios from 'axios'
|
||||
import qs from 'qs'
|
||||
|
||||
export const loginData = query => axios.post('http://oa-dxtc.test/login', qs.stringify(query));
|
|
@ -0,0 +1,28 @@
|
|||
.header{
|
||||
background-color: #242f42;
|
||||
}
|
||||
.login-wrap{
|
||||
background: #324157;
|
||||
}
|
||||
.plugins-tips{
|
||||
background: #eef1f6;
|
||||
}
|
||||
.plugins-tips a{
|
||||
color: #20a0ff;
|
||||
}
|
||||
.el-upload--text em {
|
||||
color: #20a0ff;
|
||||
}
|
||||
.pure-button{
|
||||
background: #20a0ff;
|
||||
}
|
||||
.tags-li.active {
|
||||
border: 1px solid #409EFF;
|
||||
background-color: #409EFF;
|
||||
}
|
||||
.message-title{
|
||||
color: #20a0ff;
|
||||
}
|
||||
.collapse-btn:hover{
|
||||
background: rgb(40,52,70);
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#app,
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'PingFang SC', "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
|
||||
.content-box {
|
||||
position: absolute;
|
||||
left: 250px;
|
||||
right: 0;
|
||||
top: 70px;
|
||||
bottom: 0;
|
||||
padding-bottom: 30px;
|
||||
-webkit-transition: left .3s ease-in-out;
|
||||
transition: left .3s ease-in-out;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
overflow-y: scroll;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.content-collapse {
|
||||
left: 65px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 30px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.crumbs {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.el-table th {
|
||||
background-color: #f5f7fa !important;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin: 20px 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.plugins-tips {
|
||||
padding: 20px 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.el-button+.el-tooltip {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.el-table tr:hover {
|
||||
background: #f6faff;
|
||||
}
|
||||
|
||||
.mgb20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.move-enter-active,
|
||||
.move-leave-active {
|
||||
transition: opacity .1s ease;
|
||||
}
|
||||
|
||||
.move-enter-from,
|
||||
.move-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/*BaseForm*/
|
||||
|
||||
.form-box {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.form-box .line {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-time-panel__content::after,
|
||||
.el-time-panel__content::before {
|
||||
margin-top: -7px;
|
||||
}
|
||||
|
||||
.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/*Upload*/
|
||||
|
||||
.pure-button {
|
||||
width: 150px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.g-core-image-corp-container .info-aside {
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.el-upload--text {
|
||||
background-color: #fff;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
width: 360px;
|
||||
height: 180px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-upload--text .el-icon-upload {
|
||||
font-size: 67px;
|
||||
color: #97a8be;
|
||||
margin: 40px 0 16px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.el-upload--text {
|
||||
color: #97a8be;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-upload--text em {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/*VueEditor*/
|
||||
|
||||
.ql-container {
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.ql-snow .ql-tooltip {
|
||||
transform: translateX(117.5px) translateY(10px) !important;
|
||||
}
|
||||
|
||||
.editor-btn {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/*markdown*/
|
||||
|
||||
.v-note-wrapper .v-note-panel {
|
||||
min-height: 500px;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
|
@ -0,0 +1,142 @@
|
|||
<template>
|
||||
<div class="sidebar">
|
||||
<el-menu class="sidebar-el-menu" :default-active="onRoutes" :collapse="collapse" background-color="#324157" text-color="#bfcbd9" active-text-color="#20a0ff" unique-opened router>
|
||||
<template v-for="item in items">
|
||||
<template v-if="item.subs">
|
||||
<el-sub-menu :index="item.index" :key="item.index">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<component :is="item.icon" />
|
||||
</el-icon>
|
||||
<span>{{ item.title }}</span>
|
||||
</template>
|
||||
<template v-for="subItem in item.subs">
|
||||
<el-sub-menu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
|
||||
<template #title>{{ subItem.title }}</template>
|
||||
<el-menu-item v-for="(threeItem, i) in subItem.subs" :key="i" :index="threeItem.index">
|
||||
{{ threeItem.title }}
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else :index="subItem.index" :key="subItem.index">{{ subItem.title }}</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-menu-item :index="item.index" :key="item.index">
|
||||
<el-icon>
|
||||
<component :is="item.icon" />
|
||||
</el-icon>
|
||||
<template #title>{{ item.title }}</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</el-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, watch } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
import { useRoute } from "vue-router";
|
||||
export default {
|
||||
setup() {
|
||||
const items = [
|
||||
{
|
||||
icon: "home-filled",
|
||||
index: "/dashboard",
|
||||
title: "系统首页",
|
||||
},
|
||||
{
|
||||
icon: "user",
|
||||
index: "/customer",
|
||||
title: "客户",
|
||||
},
|
||||
{
|
||||
icon: "document-copy",
|
||||
index: "/tabs",
|
||||
title: "tab选项卡",
|
||||
},
|
||||
{
|
||||
icon: "calendar",
|
||||
index: "3",
|
||||
title: "表单相关",
|
||||
subs: [
|
||||
{
|
||||
index: "/form",
|
||||
title: "基本表单",
|
||||
},
|
||||
{
|
||||
index: "/upload",
|
||||
title: "文件上传",
|
||||
},
|
||||
{
|
||||
index: "4",
|
||||
title: "三级菜单",
|
||||
subs: [
|
||||
{
|
||||
index: "/editor",
|
||||
title: "富文本编辑器",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: "pie-chart",
|
||||
index: "/charts",
|
||||
title: "schart图表",
|
||||
},
|
||||
{
|
||||
icon: "warning",
|
||||
index: "7",
|
||||
title: "错误处理",
|
||||
subs: [
|
||||
{
|
||||
index: "/permission",
|
||||
title: "权限测试",
|
||||
},
|
||||
{
|
||||
index: "/404",
|
||||
title: "404页面",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const onRoutes = computed(() => {
|
||||
return route.path;
|
||||
});
|
||||
|
||||
const store = useStore();
|
||||
const collapse = computed(() => store.state.collapse);
|
||||
|
||||
return {
|
||||
items,
|
||||
onRoutes,
|
||||
collapse,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sidebar {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 60px;
|
||||
bottom: 0;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.sidebar::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
.sidebar-el-menu:not(.el-menu--collapse) {
|
||||
width: 200px;
|
||||
}
|
||||
.sidebar > ul {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,147 @@
|
|||
<template>
|
||||
<div class="header">
|
||||
<!-- 折叠按钮 -->
|
||||
<div class="collapse-btn" @click="collapseChage">
|
||||
<el-icon v-if="!collapse" class="el-icon-s-fold"><expand /></el-icon>
|
||||
<el-icon v-else class="el-icon-s-unfold"><fold /></el-icon>
|
||||
</div>
|
||||
<div class="logo">后台管理系统</div>
|
||||
<div class="header-right">
|
||||
<div class="header-user-con">
|
||||
<!-- 用户名下拉菜单 -->
|
||||
|
||||
<el-dropdown class="user-name" trigger="click" @command="handleCommand">
|
||||
<span class="el-dropdown-link">
|
||||
{{user.username}}
|
||||
<el-icon class="el-icon-caret-bottom"><caret-bottom /></el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="user">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, onMounted } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
import { useRouter } from "vue-router";
|
||||
export default {
|
||||
setup() {
|
||||
const user = JSON.parse(localStorage.getItem("user"));
|
||||
const message = 2;
|
||||
|
||||
const store = useStore();
|
||||
const collapse = computed(() => store.state.collapse);
|
||||
// 侧边栏折叠
|
||||
const collapseChage = () => {
|
||||
store.commit("handleCollapse", !collapse.value);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (document.body.clientWidth < 1500) {
|
||||
//collapseChage();
|
||||
}
|
||||
});
|
||||
|
||||
// 用户名下拉菜单选择事件
|
||||
const router = useRouter();
|
||||
const handleCommand = (command) => {
|
||||
if (command == "loginout") {
|
||||
localStorage.removeItem("user");
|
||||
router.push("/login");
|
||||
} else if (command == "user") {
|
||||
router.push("/user");
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
user,
|
||||
message,
|
||||
collapse,
|
||||
collapseChage,
|
||||
handleCommand,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.header {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
}
|
||||
.collapse-btn {
|
||||
float: left;
|
||||
padding: 0 21px;
|
||||
cursor: pointer;
|
||||
line-height: 60px;
|
||||
}
|
||||
.header .logo {
|
||||
float: left;
|
||||
width: 250px;
|
||||
line-height: 60px;
|
||||
}
|
||||
.header-right {
|
||||
float: right;
|
||||
padding-right: 50px;
|
||||
}
|
||||
.header-user-con {
|
||||
display: flex;
|
||||
height: 60px;
|
||||
align-items: center;
|
||||
}
|
||||
.btn-fullscreen {
|
||||
transform: rotate(45deg);
|
||||
margin-right: 5px;
|
||||
font-size: 24px;
|
||||
}
|
||||
.btn-bell,
|
||||
.btn-fullscreen {
|
||||
position: relative;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
border-radius: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-bell-badge {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -2px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
background: #f56c6c;
|
||||
color: #fff;
|
||||
}
|
||||
.btn-bell .el-icon-bell {
|
||||
color: #fff;
|
||||
}
|
||||
.user-name {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.user-avator {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.user-avator img {
|
||||
display: block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.el-dropdown-link {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
.el-dropdown-menu__item {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<div class="tags" v-if="showTags">
|
||||
<ul>
|
||||
<li class="tags-li" v-for="(item,index) in tagsList" :class="{'active': isActive(item.path)}" :key="index">
|
||||
<router-link :to="item.path" class="tags-li-title">{{item.title}}</router-link>
|
||||
<span class="tags-li-icon" @click="closeTags(index)">
|
||||
<i class="el-icon-close"></i>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tags-close-box">
|
||||
<el-dropdown @command="handleTags">
|
||||
<el-button size="mini" type="primary">
|
||||
标签选项
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu size="small">
|
||||
<el-dropdown-item command="other">关闭其他</el-dropdown-item>
|
||||
<el-dropdown-item command="all">关闭所有</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
|
||||
export default {
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const isActive = (path) => {
|
||||
return path === route.fullPath;
|
||||
};
|
||||
|
||||
const store = useStore();
|
||||
const tagsList = computed(() => store.state.tagsList);
|
||||
const showTags = computed(() => tagsList.value.length > 0);
|
||||
|
||||
// 关闭单个标签
|
||||
const closeTags = (index) => {
|
||||
const delItem = tagsList.value[index];
|
||||
store.commit("delTagsItem", { index });
|
||||
const item = tagsList.value[index]
|
||||
? tagsList.value[index]
|
||||
: tagsList.value[index - 1];
|
||||
if (item) {
|
||||
delItem.path === route.fullPath && router.push(item.path);
|
||||
} else {
|
||||
router.push("/");
|
||||
}
|
||||
};
|
||||
|
||||
// 设置标签
|
||||
const setTags = (route) => {
|
||||
const isExist = tagsList.value.some((item) => {
|
||||
return item.path === route.fullPath;
|
||||
});
|
||||
if (!isExist) {
|
||||
if (tagsList.value.length >= 8) {
|
||||
store.commit("delTagsItem", { index: 0 });
|
||||
}
|
||||
store.commit("setTagsItem", {
|
||||
name: route.name,
|
||||
title: route.meta.title,
|
||||
path: route.fullPath,
|
||||
});
|
||||
}
|
||||
};
|
||||
setTags(route);
|
||||
onBeforeRouteUpdate((to) => {
|
||||
setTags(to);
|
||||
});
|
||||
|
||||
// 关闭全部标签
|
||||
const closeAll = () => {
|
||||
store.commit("clearTags");
|
||||
router.push("/");
|
||||
};
|
||||
// 关闭其他标签
|
||||
const closeOther = () => {
|
||||
const curItem = tagsList.value.filter((item) => {
|
||||
return item.path === route.fullPath;
|
||||
});
|
||||
store.commit("closeTagsOther", curItem);
|
||||
};
|
||||
const handleTags = (command) => {
|
||||
command === "other" ? closeOther() : closeAll();
|
||||
};
|
||||
|
||||
// 关闭当前页面的标签页
|
||||
// store.commit("closeCurrentTag", {
|
||||
// $router: router,
|
||||
// $route: route
|
||||
// });
|
||||
|
||||
return {
|
||||
isActive,
|
||||
tagsList,
|
||||
showTags,
|
||||
closeTags,
|
||||
handleTags,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
.tags {
|
||||
position: relative;
|
||||
height: 30px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
padding-right: 120px;
|
||||
box-shadow: 0 5px 10px #ddd;
|
||||
}
|
||||
|
||||
.tags ul {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tags-li {
|
||||
float: left;
|
||||
margin: 3px 5px 2px 3px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
height: 23px;
|
||||
line-height: 23px;
|
||||
border: 1px solid #e9eaec;
|
||||
background: #fff;
|
||||
padding: 0 5px 0 12px;
|
||||
vertical-align: middle;
|
||||
color: #666;
|
||||
-webkit-transition: all 0.3s ease-in;
|
||||
-moz-transition: all 0.3s ease-in;
|
||||
transition: all 0.3s ease-in;
|
||||
}
|
||||
|
||||
.tags-li:not(.active):hover {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.tags-li.active {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tags-li-title {
|
||||
float: left;
|
||||
max-width: 80px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
margin-right: 5px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tags-li.active .tags-li-title {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tags-close-box {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
box-sizing: border-box;
|
||||
padding-top: 1px;
|
||||
text-align: center;
|
||||
width: 110px;
|
||||
height: 30px;
|
||||
background: #fff;
|
||||
box-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1);
|
||||
z-index: 10;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,8 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import { createApp } from 'vue'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
import * as ElIcons from '@element-plus/icons-vue'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
|
||||
const app = createApp(App)
|
||||
for (const name in ElIcons){
|
||||
app.component(name,(ElIcons as any)[name])
|
||||
}
|
||||
app.use(ElementPlus, {
|
||||
locale: zhCn
|
||||
})
|
||||
.use(store)
|
||||
.use(router)
|
||||
.mount('#app')
|
|
@ -0,0 +1,148 @@
|
|||
import {createRouter, createWebHashHistory} from "vue-router";
|
||||
import Home from "../views/Home.vue";
|
||||
import Dashboard from "../views/Dashboard.vue"
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/dashboard'
|
||||
}, {
|
||||
path: "/",
|
||||
name: "Home",
|
||||
component: Home,
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard",
|
||||
name: "dashboard",
|
||||
meta: {
|
||||
title: '系统首页'
|
||||
},
|
||||
//component: () => import ( /* webpackChunkName: "dashboard" */ "../views/Dashboard.vue")
|
||||
component :Dashboard
|
||||
},
|
||||
{
|
||||
path: "/customer",
|
||||
name: "basetable",
|
||||
meta: {
|
||||
title: '客户'
|
||||
},
|
||||
component: () => import ( /* webpackChunkName: "table" */ "../views/Customer.vue")
|
||||
},
|
||||
//{
|
||||
// path: "/charts",
|
||||
// name: "basecharts",
|
||||
// meta: {
|
||||
// title: '图表'
|
||||
// },
|
||||
// component: () => import ( /* webpackChunkName: "charts" */ "../views/BaseCharts.vue")
|
||||
// }, {
|
||||
// path: "/form",
|
||||
// name: "baseform",
|
||||
// meta: {
|
||||
// title: '表单'
|
||||
// },
|
||||
// component: () => import ( /* webpackChunkName: "form" */ "../views/BaseForm.vue")
|
||||
// }, {
|
||||
// path: "/tabs",
|
||||
// name: "tabs",
|
||||
// meta: {
|
||||
// title: 'tab标签'
|
||||
// },
|
||||
// component: () => import ( /* webpackChunkName: "tabs" */ "../views/Tabs.vue")
|
||||
// }, {
|
||||
// path: "/donate",
|
||||
// name: "donate",
|
||||
// meta: {
|
||||
// title: '鼓励作者'
|
||||
// },
|
||||
// component: () => import ( /* webpackChunkName: "donate" */ "../views/Donate.vue")
|
||||
// }, {
|
||||
// path: "/permission",
|
||||
// name: "permission",
|
||||
// meta: {
|
||||
// title: '权限管理',
|
||||
// permission: true
|
||||
// },
|
||||
// component: () => import ( /* webpackChunkName: "permission" */ "../views/Permission.vue")
|
||||
// }, {
|
||||
// path: "/i18n",
|
||||
// name: "i18n",
|
||||
// meta: {
|
||||
// title: '国际化语言'
|
||||
// },
|
||||
// component: () => import ( /* webpackChunkName: "i18n" */ "../views/I18n.vue")
|
||||
// }, {
|
||||
// path: "/upload",
|
||||
// name: "upload",
|
||||
// meta: {
|
||||
// title: '上传插件'
|
||||
// },
|
||||
// component: () => import ( /* webpackChunkName: "upload" */ "../views/Upload.vue")
|
||||
// }, {
|
||||
// path: "/icon",
|
||||
// name: "icon",
|
||||
// meta: {
|
||||
// title: '自定义图标'
|
||||
// },
|
||||
// component: () => import ( /* webpackChunkName: "icon" */ "../views/Icon.vue")
|
||||
// }, {
|
||||
// path: '/404',
|
||||
// name: '404',
|
||||
// meta: {
|
||||
// title: '找不到页面'
|
||||
// },
|
||||
// component: () => import (/* webpackChunkName: "404" */ '../views/404.vue')
|
||||
// }, {
|
||||
// path: '/403',
|
||||
// name: '403',
|
||||
// meta: {
|
||||
// title: '没有权限'
|
||||
// },
|
||||
// component: () => import (/* webpackChunkName: "403" */ '../views/403.vue')
|
||||
// }, {
|
||||
// path: '/user',
|
||||
// name: 'user',
|
||||
// meta: {
|
||||
// title: '个人中心'
|
||||
// },
|
||||
// component: () => import (/* webpackChunkName: "user" */ '../views/User.vue')
|
||||
// }, {
|
||||
// path: '/editor',
|
||||
// name: 'editor',
|
||||
// meta: {
|
||||
// title: '富文本编辑器'
|
||||
// },
|
||||
// component: () => import (/* webpackChunkName: "editor" */ '../views/Editor.vue')
|
||||
// }
|
||||
]
|
||||
}, {
|
||||
path: "/login",
|
||||
name: "Login",
|
||||
meta: {
|
||||
title: '登录'
|
||||
},
|
||||
component: () => import ( /* webpackChunkName: "login" */ "../views/Login.vue")
|
||||
}
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
document.title = `${to.meta.title} | vue-manage-system`;
|
||||
const role = localStorage.getItem('ms_username');
|
||||
if (!role && to.path !== '/login') {
|
||||
next('/login');
|
||||
} else if (to.meta.permission) {
|
||||
// 如果是管理员权限则可进入,这里只是简单的模拟管理员权限而已
|
||||
role === 'admin'
|
||||
? next()
|
||||
: next('/403');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -0,0 +1,56 @@
|
|||
import {createStore} from 'vuex'
|
||||
|
||||
export default createStore({
|
||||
state: {
|
||||
tagsList: [],
|
||||
collapse: false
|
||||
},
|
||||
mutations: {
|
||||
delTagsItem(state, data) {
|
||||
state
|
||||
.tagsList
|
||||
.splice(data.index, 1);
|
||||
},
|
||||
setTagsItem(state, data) {
|
||||
state
|
||||
.tagsList
|
||||
.push(data)
|
||||
},
|
||||
clearTags(state) {
|
||||
state.tagsList = []
|
||||
},
|
||||
closeTagsOther(state, data) {
|
||||
state.tagsList = data;
|
||||
},
|
||||
closeCurrentTag(state, data) {
|
||||
for (let i = 0, len = state.tagsList.length; i < len; i++) {
|
||||
const item = state.tagsList[i];
|
||||
if (item.path === data.$route.fullPath) {
|
||||
if (i < len - 1) {
|
||||
data
|
||||
.$router
|
||||
.push(state.tagsList[i + 1].path);
|
||||
} else if (i > 0) {
|
||||
data
|
||||
.$router
|
||||
.push(state.tagsList[i - 1].path);
|
||||
} else {
|
||||
data
|
||||
.$router
|
||||
.push("/");
|
||||
}
|
||||
state
|
||||
.tagsList
|
||||
.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
// 侧边栏折叠
|
||||
handleCollapse(state, data) {
|
||||
state.collapse = data;
|
||||
}
|
||||
},
|
||||
actions: {},
|
||||
modules: {}
|
||||
})
|
|
@ -0,0 +1,201 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item>
|
||||
<i class="el-icon-lx-cascades"></i> 客户列表
|
||||
</el-breadcrumb-item>
|
||||
<el-button type="primary">
|
||||
<el-icon :size="20"><plus /></el-icon>
|
||||
</el-button>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="handle-box">
|
||||
<el-select v-model="query.address" placeholder="地址" class="handle-select mr10">
|
||||
<el-option key="1" label="广东省" value="广东省"></el-option>
|
||||
<el-option key="2" label="湖南省" value="湖南省"></el-option>
|
||||
</el-select>
|
||||
<el-input v-model="query.name" placeholder="用户名" class="handle-input mr10"></el-input>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="tableData" border class="table" ref="multipleTable" header-cell-class-name="table-header">
|
||||
<el-table-column prop="id" label="ID" width="55" align="center"></el-table-column>
|
||||
<el-table-column prop="name" label="用户名"></el-table-column>
|
||||
<el-table-column label="账户余额">
|
||||
<template #default="scope">¥{{ scope.row.money }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="头像(查看大图)" align="center">
|
||||
<template #default="scope">
|
||||
<el-image class="table-td-thumb" :src="scope.row.thumb" :preview-src-list="[scope.row.thumb]">
|
||||
</el-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="address" label="地址"></el-table-column>
|
||||
<el-table-column label="状态" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="
|
||||
scope.row.state === '成功'
|
||||
? 'success'
|
||||
: scope.row.state === '失败'
|
||||
? 'danger'
|
||||
: ''
|
||||
">{{ scope.row.state }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="date" label="注册时间"></el-table-column>
|
||||
<el-table-column label="操作" width="180" align="center">
|
||||
<template #default="scope">
|
||||
<el-button type="text" icon="el-icon-edit" @click="handleEdit(scope.$index, scope.row)">编辑
|
||||
</el-button>
|
||||
<el-button type="text" icon="el-icon-delete" class="red"
|
||||
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
<el-pagination background layout="total, prev, pager, next" :current-page="query.pageIndex"
|
||||
:page-size="query.pageSize" :total="pageTotal" @current-change="handlePageChange"></el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑弹出框 -->
|
||||
<el-dialog title="编辑" v-model="editVisible" width="30%">
|
||||
<el-form label-width="70px">
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="地址">
|
||||
<el-input v-model="form.address"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="editVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="saveEdit">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { customerData } from "../api/customer";
|
||||
|
||||
export default {
|
||||
name: "basetable",
|
||||
setup() {
|
||||
const query = reactive({
|
||||
address: "",
|
||||
name: "",
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
const tableData = ref([]);
|
||||
const pageTotal = ref(0);
|
||||
// 获取表格数据
|
||||
const getData = () => {
|
||||
customerData(query).then((res) => {
|
||||
console.log(res);
|
||||
//tableData.value = res.data;
|
||||
//pageTotal.value = res.pageTotal || 0;
|
||||
});
|
||||
};
|
||||
getData();
|
||||
|
||||
// 查询操作
|
||||
const handleSearch = () => {
|
||||
query.pageIndex = 1;
|
||||
getData();
|
||||
};
|
||||
// 分页导航
|
||||
const handlePageChange = (val) => {
|
||||
query.pageIndex = val;
|
||||
getData();
|
||||
};
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (index) => {
|
||||
// 二次确认删除
|
||||
ElMessageBox.confirm("确定要删除吗?", "提示", {
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
tableData.value.splice(index, 1);
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 表格编辑时弹窗和保存
|
||||
const editVisible = ref(false);
|
||||
let form = reactive({
|
||||
name: "",
|
||||
address: "",
|
||||
});
|
||||
let idx = -1;
|
||||
const handleEdit = (index, row) => {
|
||||
idx = index;
|
||||
Object.keys(form).forEach((item) => {
|
||||
form[item] = row[item];
|
||||
});
|
||||
editVisible.value = true;
|
||||
};
|
||||
const saveEdit = () => {
|
||||
editVisible.value = false;
|
||||
ElMessage.success(`修改第 ${idx + 1} 行成功`);
|
||||
Object.keys(form).forEach((item) => {
|
||||
tableData.value[idx][item] = form[item];
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
query,
|
||||
tableData,
|
||||
pageTotal,
|
||||
editVisible,
|
||||
form,
|
||||
handleSearch,
|
||||
handlePageChange,
|
||||
handleDelete,
|
||||
handleEdit,
|
||||
saveEdit,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.handle-box {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.handle-select {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.handle-input {
|
||||
width: 300px;
|
||||
display: inline-block;
|
||||
}
|
||||
.table {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
.red {
|
||||
color: #ff0000;
|
||||
}
|
||||
.mr10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.table-td-thumb {
|
||||
display: block;
|
||||
margin: auto;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,348 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" class="mgb20" style="height:252px;">
|
||||
<div class="user-info">
|
||||
<!-- <img src="../assets/img/img.jpg" class="user-avator" alt /> -->
|
||||
<div class="user-info-cont">
|
||||
<div class="user-info-name">{{ name }}</div>
|
||||
<div>{{ role }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-info-list">
|
||||
上次登录时间:
|
||||
<span>2019-11-01</span>
|
||||
</div>
|
||||
<div class="user-info-list">
|
||||
上次登录地点:
|
||||
<span>东莞</span>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" style="height:252px;">
|
||||
<template #header>
|
||||
<div class="clearfix">
|
||||
<span>语言详情</span>
|
||||
</div>
|
||||
</template>
|
||||
Vue
|
||||
<el-progress :percentage="71.3" color="#42b983"></el-progress>JavaScript
|
||||
<el-progress :percentage="24.1" color="#f1e05a"></el-progress>CSS
|
||||
<el-progress :percentage="13.7"></el-progress>HTML
|
||||
<el-progress :percentage="5.9" color="#f56c6c"></el-progress>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-row :gutter="20" class="mgb20">
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }">
|
||||
<div class="grid-content grid-con-1">
|
||||
<i class="el-icon-user-solid grid-con-icon"></i>
|
||||
<div class="grid-cont-right">
|
||||
<div class="grid-num">1234</div>
|
||||
<div>用户访问量</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }">
|
||||
<div class="grid-content grid-con-2">
|
||||
<i class="el-icon-message-solid grid-con-icon"></i>
|
||||
<div class="grid-cont-right">
|
||||
<div class="grid-num">321</div>
|
||||
<div>系统消息</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }">
|
||||
<div class="grid-content grid-con-3">
|
||||
<i class="el-icon-s-goods grid-con-icon"></i>
|
||||
<div class="grid-cont-right">
|
||||
<div class="grid-num">5000</div>
|
||||
<div>数量</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-card shadow="hover" style="height:403px;">
|
||||
<template #header>
|
||||
<div class="clearfix">
|
||||
<span>待办事项</span>
|
||||
<el-button style="float: right; padding: 3px 0" type="text">添加</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table :show-header="false" :data="todoList" style="width:100%;">
|
||||
<el-table-column width="40">
|
||||
<template #default="scope">
|
||||
<el-checkbox v-model="scope.row.status"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column>
|
||||
<template #default="scope">
|
||||
<div class="todo-item" :class="{
|
||||
'todo-item-del': scope.row.status,
|
||||
}">{{ scope.row.title }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="60">
|
||||
<template>
|
||||
<i class="el-icon-edit"></i>
|
||||
<i class="el-icon-delete"></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-card shadow="hover">
|
||||
<!-- <schart ref="bar" class="schart" canvasId="bar" :options="options"></schart> -->
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card shadow="hover">
|
||||
<!-- <schart ref="line" class="schart" canvasId="line" :options="options2"></schart> -->
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
//import Schart from "vue-schart";
|
||||
import { reactive } from "vue";
|
||||
export default {
|
||||
name: "dashboard",
|
||||
//components: { Schart },
|
||||
setup() {
|
||||
const name = localStorage.getItem("ms_username");
|
||||
const role = name === "admin" ? "超级管理员" : "普通用户";
|
||||
|
||||
const data = reactive([
|
||||
{
|
||||
name: "2018/09/04",
|
||||
value: 1083,
|
||||
},
|
||||
{
|
||||
name: "2018/09/05",
|
||||
value: 941,
|
||||
},
|
||||
{
|
||||
name: "2018/09/06",
|
||||
value: 1139,
|
||||
},
|
||||
{
|
||||
name: "2018/09/07",
|
||||
value: 816,
|
||||
},
|
||||
{
|
||||
name: "2018/09/08",
|
||||
value: 327,
|
||||
},
|
||||
{
|
||||
name: "2018/09/09",
|
||||
value: 228,
|
||||
},
|
||||
{
|
||||
name: "2018/09/10",
|
||||
value: 1065,
|
||||
},
|
||||
]);
|
||||
const options = {
|
||||
type: "bar",
|
||||
title: {
|
||||
text: "最近一周各品类销售图",
|
||||
},
|
||||
xRorate: 25,
|
||||
labels: ["周一", "周二", "周三", "周四", "周五"],
|
||||
datasets: [
|
||||
{
|
||||
label: "家电",
|
||||
data: [234, 278, 270, 190, 230],
|
||||
},
|
||||
{
|
||||
label: "百货",
|
||||
data: [164, 178, 190, 135, 160],
|
||||
},
|
||||
{
|
||||
label: "食品",
|
||||
data: [144, 198, 150, 235, 120],
|
||||
},
|
||||
],
|
||||
};
|
||||
const options2 = {
|
||||
type: "line",
|
||||
title: {
|
||||
text: "最近几个月各品类销售趋势图",
|
||||
},
|
||||
labels: ["6月", "7月", "8月", "9月", "10月"],
|
||||
datasets: [
|
||||
{
|
||||
label: "家电",
|
||||
data: [234, 278, 270, 190, 230],
|
||||
},
|
||||
{
|
||||
label: "百货",
|
||||
data: [164, 178, 150, 135, 160],
|
||||
},
|
||||
{
|
||||
label: "食品",
|
||||
data: [74, 118, 200, 235, 90],
|
||||
},
|
||||
],
|
||||
};
|
||||
const todoList = reactive([
|
||||
{
|
||||
title: "今天要修复100个bug",
|
||||
status: false,
|
||||
},
|
||||
{
|
||||
title: "今天要修复100个bug",
|
||||
status: false,
|
||||
},
|
||||
{
|
||||
title: "今天要写100行代码加几个bug吧",
|
||||
status: false,
|
||||
},
|
||||
{
|
||||
title: "今天要修复100个bug",
|
||||
status: false,
|
||||
},
|
||||
{
|
||||
title: "今天要修复100个bug",
|
||||
status: true,
|
||||
},
|
||||
{
|
||||
title: "今天要写100行代码加几个bug吧",
|
||||
status: true,
|
||||
},
|
||||
]);
|
||||
|
||||
return {
|
||||
name,
|
||||
data,
|
||||
options,
|
||||
options2,
|
||||
todoList,
|
||||
role,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.grid-cont-right {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.grid-num {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.grid-con-icon {
|
||||
font-size: 50px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.grid-con-1 .grid-con-icon {
|
||||
background: rgb(45, 140, 240);
|
||||
}
|
||||
|
||||
.grid-con-1 .grid-num {
|
||||
color: rgb(45, 140, 240);
|
||||
}
|
||||
|
||||
.grid-con-2 .grid-con-icon {
|
||||
background: rgb(100, 213, 114);
|
||||
}
|
||||
|
||||
.grid-con-2 .grid-num {
|
||||
color: rgb(45, 140, 240);
|
||||
}
|
||||
|
||||
.grid-con-3 .grid-con-icon {
|
||||
background: rgb(242, 94, 67);
|
||||
}
|
||||
|
||||
.grid-con-3 .grid-num {
|
||||
color: rgb(242, 94, 67);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #ccc;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.user-avator {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.user-info-cont {
|
||||
padding-left: 50px;
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.user-info-cont div:first-child {
|
||||
font-size: 30px;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.user-info-list {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.user-info-list span {
|
||||
margin-left: 70px;
|
||||
}
|
||||
|
||||
.mgb20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.todo-item {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.todo-item-del {
|
||||
text-decoration: line-through;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.schart {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<div class="common-layout" style="border:1px solid blue;">
|
||||
<el-container>
|
||||
<el-header>
|
||||
<v-header />
|
||||
</el-header>
|
||||
<el-container>
|
||||
<el-aside width="200px">
|
||||
<v-aside />
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<v-tags />
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="move" mode="out-in">
|
||||
<keep-alive :include="tagsList">
|
||||
<component :is="Component" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useStore } from "vuex";
|
||||
import vHeader from '../components/Header.vue'
|
||||
import vAside from '../components/Aside.vue'
|
||||
import vTags from "../components/Tags.vue";
|
||||
export default {
|
||||
components:{
|
||||
vHeader,
|
||||
vAside,
|
||||
vTags
|
||||
},
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const tagsList = computed(() =>
|
||||
store.state.tagsList.map((item) => item.name)
|
||||
);
|
||||
const collapse = computed(() => store.state.collapse);
|
||||
return {
|
||||
tagsList,
|
||||
collapse,
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.el-header{
|
||||
--el-header-padding:0
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<div class="login-wrap">
|
||||
<div class="ms-login">
|
||||
<div class="ms-title">后台管理系统</div>
|
||||
<el-form :model="param" :rules="rules" ref="login" label-width="0px" class="ms-content">
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="param.username" placeholder="username">
|
||||
<template #prepend>
|
||||
<el-button icon="el-icon-user"></el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input type="password" placeholder="password" v-model="param.password"
|
||||
@keyup.enter="submitForm()">
|
||||
<template #prepend>
|
||||
<el-button icon="el-icon-lock"></el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<div class="login-btn">
|
||||
<el-button type="primary" @click="submitForm()">登录</el-button>
|
||||
</div>
|
||||
<p class="login-tips">Tips : 用户名和密码随便填。</p>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, reactive } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { loginData } from "../api/login";
|
||||
export default {
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const param = reactive({
|
||||
username: "admin",
|
||||
password: "123456",
|
||||
});
|
||||
|
||||
const rules = {
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入用户名",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: "请输入密码", trigger: "blur" },
|
||||
],
|
||||
};
|
||||
const login = ref(null);
|
||||
const submitForm = () => {
|
||||
login.value.validate((valid) => {
|
||||
if(!valid){
|
||||
ElMessage.error('登录失败');
|
||||
return false;
|
||||
}
|
||||
console.log(router)
|
||||
loginData(param).then((res) => {
|
||||
if(res.data.code == 0){
|
||||
console.log(res.data.data);
|
||||
ElMessage.success("登录成功");
|
||||
localStorage.setItem("user", JSON.stringify(res.data.data));
|
||||
localStorage.setItem('ms_username', res.data.data.username);
|
||||
router.push("/");
|
||||
}else{
|
||||
ElMessage.error(res.data.msg);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const store = useStore();
|
||||
store.commit("clearTags");
|
||||
|
||||
return {
|
||||
param,
|
||||
rules,
|
||||
login,
|
||||
submitForm,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/*background-image: url(../assets/img/login-bg.jpg);*/
|
||||
background-size: 100%;
|
||||
}
|
||||
.ms-title {
|
||||
width: 100%;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.ms-login {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 350px;
|
||||
margin: -190px 0 0 -175px;
|
||||
border-radius: 5px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
overflow: hidden;
|
||||
}
|
||||
.ms-content {
|
||||
padding: 30px 30px;
|
||||
}
|
||||
.login-btn {
|
||||
text-align: center;
|
||||
}
|
||||
.login-btn button {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.login-tips {
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext", "dom"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()]
|
||||
})
|
Loading…
Reference in New Issue