Compare commits
72 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
6a7019ec1a | |
![]() |
7375986a8e | |
![]() |
251936dcdc | |
![]() |
921c38cea2 | |
![]() |
718f255817 | |
![]() |
433b60e720 | |
![]() |
b9a4402431 | |
![]() |
daaf393872 | |
![]() |
c8cae1b200 | |
![]() |
f70c93715b | |
![]() |
60a293514d | |
![]() |
8312ba1d6c | |
![]() |
eea5e368a9 | |
![]() |
759beaf940 | |
![]() |
9237de9b89 | |
![]() |
995ab69b1e | |
![]() |
a072752609 | |
![]() |
d28f504a80 | |
![]() |
e82a457a2c | |
![]() |
256b3354ca | |
![]() |
be39b534c1 | |
![]() |
274198a7bd | |
![]() |
439d48f8a3 | |
![]() |
8aeb8b006e | |
![]() |
039c9c3da4 | |
![]() |
9bc43e2efc | |
![]() |
4dd1b4aae6 | |
![]() |
3358fd7942 | |
![]() |
221aa3a5b9 | |
![]() |
9e7ec0cea7 | |
![]() |
98261e3eba | |
![]() |
1ae7fc8613 | |
![]() |
2874f01c34 | |
![]() |
96be359eab | |
![]() |
0f641c0f08 | |
![]() |
31f40a5e15 | |
![]() |
e8d1bfcad9 | |
![]() |
6b73235ba3 | |
![]() |
ce56f86f0d | |
![]() |
03f67de289 | |
![]() |
5173cac28f | |
![]() |
c5c018ac0f | |
![]() |
3cfc7032bc | |
![]() |
838a03086e | |
![]() |
2ebf10e9ba | |
![]() |
60eeaaf3eb | |
![]() |
5b294b781b | |
![]() |
3022814f8a | |
![]() |
21efb45431 | |
![]() |
f7fb309680 | |
![]() |
d45bdbeeda | |
![]() |
ed014e9304 | |
![]() |
289f817180 | |
![]() |
a7b561baf5 | |
![]() |
49f8a69675 | |
![]() |
3c2f4e891d | |
![]() |
c442dcfeb3 | |
![]() |
2cb246ec3e | |
![]() |
3dd0b2fd8a | |
![]() |
ba74b86448 | |
![]() |
0ee9965612 | |
![]() |
285ea37245 | |
![]() |
53d067f0d1 | |
![]() |
a905850345 | |
![]() |
eb36dad968 | |
![]() |
d553a75d09 | |
![]() |
38a5bd4bd0 | |
![]() |
bee36e1260 | |
![]() |
5ac78cfd18 | |
![]() |
ce21d13774 | |
![]() |
dc3405876e | |
![]() |
8c33d1b5b3 |
|
@ -1,3 +0,0 @@
|
|||
> 1%
|
||||
last 2 versions
|
||||
not ie <= 8
|
|
@ -0,0 +1,12 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://lin-xin.github.io/images/weixin.jpg
|
|
@ -1,22 +1,23 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
example.html
|
||||
favicon.ico
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw*
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
|
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016 vue-manage-system
|
||||
Copyright (c) 2016-2023 vue-manage-system
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
189
README.md
|
@ -1,179 +1,80 @@
|
|||
# vue-manage-system #
|
||||
<a href="https://github.com/vuejs/vue">
|
||||
<img src="https://img.shields.io/badge/vue-2.6.10-brightgreen.svg" alt="vue">
|
||||
</a>
|
||||
<a href="https://github.com/ElemeFE/element">
|
||||
<img src="https://img.shields.io/badge/element--ui-2.8.2-brightgreen.svg" alt="element-ui">
|
||||
</a>
|
||||
<a href="https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
|
||||
</a>
|
||||
# vue-manage-system
|
||||
|
||||
<a href="https://github.com/lin-xin/vue-manage-system/releases">
|
||||
<img src="https://img.shields.io/github/release/lin-xin/vue-manage-system.svg" alt="GitHub release">
|
||||
</a>
|
||||
<a href="http://blog.gdfengshuo.com/example/work/#/donate">
|
||||
<img src="https://img.shields.io/badge/%24-donate-ff69b4.svg" alt="donate">
|
||||
<a href="https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
|
||||
</a>
|
||||
|
||||
基于Vue.js + Element UI 的后台管理系统解决方案。[线上地址](http://blog.gdfengshuo.com/example/work/)
|
||||
基于 Vue3 + pinia + Element Plus 的后台管理系统解决方案。[线上演示](https://lin-xin.github.io/example/vue-manage-system/)
|
||||
|
||||
(本项目基于vue-cli3构建,如果是vue-cli2的请下载[旧版本V3.2.0](https://github.com/lin-xin/vue-manage-system/releases/tag/V3.2.0))
|
||||
> Vue2 版本请看 [tag-V4.2.0](https://github.com/lin-xin/vue-manage-system/tree/V4.2.0),带后台功能请看 [tsrpc-manage-system](https://github.com/lin-xin/tsrpc-manage-system)
|
||||
|
||||
[文档地址](https://lin-xin.github.io/example/vuems-doc/)
|
||||
[English document](https://github.com/lin-xin/manage-system/blob/master/README_EN.md)
|
||||
|
||||
## 项目截图 ##
|
||||
### 登录
|
||||

|
||||
## 赞助商
|
||||
|
||||
### 默认皮肤 ###
|
||||

|
||||
### 好问
|
||||
|
||||
### 浅绿色皮肤 ###
|
||||

|
||||
[<img src="https://static.bestqa.net/logo/bestqa_haowen.png" width="220" height="100">](https://www.bestqa.net/home/index.html)
|
||||
|
||||
专业问卷服务,一对一客服,按需定制
|
||||
|
||||
## 支持作者
|
||||
|
||||
## 赞赏
|
||||
请作者喝杯咖啡吧!(微信号:linxin_20)
|
||||
|
||||

|
||||

|
||||
|
||||
## 前言 ##
|
||||
之前在公司用了Vue + Element组件库做了个后台管理系统,基本很多组件可以直接引用组件库的,但是也有一些需求无法满足。像图片裁剪上传、富文本编辑器、图表等这些在后台管理系统中很常见的功能,就需要引用其他的组件才能完成。从寻找组件,到使用组件的过程中,遇到了很多问题,也积累了宝贵的经验。所以我就把开发这个后台管理系统的经验,总结成这个后台管理系统解决方案。
|
||||
## 前言
|
||||
|
||||
该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统(Web Management System)开发。基于vue.js,使用vue-cli@3.2.3脚手架快速生成项目目录,引用Element UI组件库,方便开发快速简洁好看的组件。分离颜色样式,支持手动切换主题色,而且很方便使用自定义主题色。
|
||||
(已经升级到 vue-cli@3.2.3,请更新依赖)
|
||||
该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统开发。基于 Vue3 + pinia + typescript,引用 Element Plus 组件库,方便开发。实现逻辑简单,适合外包项目,快速交付。
|
||||
|
||||
## 功能 ##
|
||||
- [x] Element UI
|
||||
- [x] 登录/注销
|
||||
- [x] Dashboard
|
||||
- [x] 表格
|
||||
- [x] Tab选项卡
|
||||
- [x] 表单
|
||||
- [x] 图表 :bar_chart:
|
||||
- [x] 富文本编辑器
|
||||
- [x] markdown编辑器
|
||||
- [x] 图片拖拽/裁剪上传
|
||||
- [x] 支持切换主题色 :sparkles:
|
||||
- [x] 列表拖拽排序
|
||||
- [x] 权限测试
|
||||
- [x] 404 / 403
|
||||
- [x] 三级菜单
|
||||
- [x] 自定义图标
|
||||
- [x] 可拖拽弹窗
|
||||
- [x] 国际化
|
||||
## 功能
|
||||
|
||||
- [x] Element Plus
|
||||
- [x] vite 3
|
||||
- [x] pinia
|
||||
- [x] typescript
|
||||
- [x] 登录/注册
|
||||
- [x] Dashboard
|
||||
- [x] 表格/表单
|
||||
- [x] 图表 :bar_chart:
|
||||
- [x] 富文本/markdown 编辑器
|
||||
- [x] 图片拖拽/裁剪上传
|
||||
- [x] 权限管理
|
||||
- [x] 三级菜单
|
||||
- [x] 自定义图标
|
||||
- [x] 主题切换
|
||||
|
||||
## 安装步骤
|
||||
|
||||
> 因为使用 vite3,node 版本需要 14.18+
|
||||
|
||||
## 安装步骤 ##
|
||||
```
|
||||
git clone https://github.com/lin-xin/vue-manage-system.git // 把模板下载到本地
|
||||
cd vue-manage-system // 进入模板目录
|
||||
npm install // 安装项目依赖,等待安装完成之后,安装失败可用 cnpm 或 yarn
|
||||
|
||||
// 开启服务器,浏览器访问 http://localhost:8080
|
||||
npm run serve
|
||||
// 运行
|
||||
npm run dev
|
||||
|
||||
// 执行构建命令,生成的dist文件夹放在服务器下即可访问
|
||||
npm run build
|
||||
```
|
||||
## 组件使用说明与演示 ##
|
||||
|
||||
### vue-schart ###
|
||||
vue.js封装sChart.js的图表组件。访问地址:[vue-schart](https://github.com/linxin/vue-schart)
|
||||
<p><a href="https://www.npmjs.com/package/vue-schart"><img src="https://img.shields.io/npm/dm/vue-schart.svg" alt="Downloads"></a></p>
|
||||
## 项目截图
|
||||
|
||||
```html
|
||||
<template>
|
||||
<div>
|
||||
<schart class="wrapper"
|
||||
:canvasId="canvasId"
|
||||
:type="type"
|
||||
:data="data"
|
||||
:options="options"
|
||||
></schart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Schart from 'vue-schart'; // 导入Schart组件
|
||||
export default {
|
||||
data: function(){
|
||||
return {
|
||||
canvasId: 'myCanvas', // canvas的id
|
||||
type: 'bar', // 图表类型
|
||||
data: [
|
||||
{name: '2014', value: 1342},
|
||||
{name: '2015', value: 2123},
|
||||
{name: '2016', value: 1654},
|
||||
{name: '2017', value: 1795},
|
||||
],
|
||||
options: { // 图表可选参数
|
||||
title: 'Total sales of stores in recent years'
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Schart
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.wrapper{
|
||||
width: 7rem;
|
||||
height: 5rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
### 首页
|
||||
|
||||
## 其他注意事项 ##
|
||||
### 一、如果我不想用到上面的某些组件呢,那我怎么在模板中删除掉不影响到其他功能呢? ###
|
||||

|
||||
|
||||
举个栗子,我不想用 Vue-Quill-Editor 这个组件,那我需要分四步走。
|
||||
### 登录
|
||||
|
||||
第一步:删除该组件的路由,在目录 src/router/index.js 中,找到引入改组件的路由,删除下面这段代码。
|
||||
|
||||
```JavaScript
|
||||
{
|
||||
// 富文本编辑器组件
|
||||
path: '/editor',
|
||||
component: resolve => require(['../components/page/VueEditor.vue'], resolve)
|
||||
},
|
||||
```
|
||||
|
||||
第二步:删除引入该组件的文件。在目录 src/components/page/ 删除 VueEditor.vue 文件。
|
||||
|
||||
第三步:删除该页面的入口。在目录 src/components/common/Sidebar.vue 中,找到该入口,删除下面这段代码。
|
||||
|
||||
```js
|
||||
{
|
||||
index: 'editor',
|
||||
title: '富文本编辑器'
|
||||
},
|
||||
```
|
||||
|
||||
第四步:卸载该组件。执行以下命令:
|
||||
|
||||
npm un vue-quill-editor -S
|
||||
|
||||
完成。
|
||||
|
||||
### 二、如何切换主题色呢? ###
|
||||
|
||||
第一步:打开 src/main.js 文件,找到引入 element 样式的地方,换成浅绿色主题。
|
||||
|
||||
```javascript
|
||||
import 'element-ui/lib/theme-default/index.css'; // 默认主题
|
||||
// import '../static/css/theme-green/index.css'; // 浅绿色主题
|
||||
```
|
||||
|
||||
第二步:打开 src/App.vue 文件,找到 style 标签引入样式的地方,切换成浅绿色主题。
|
||||
|
||||
```javascript
|
||||
@import "../static/css/main.css";
|
||||
@import "../static/css/color-dark.css"; /*深色主题*/
|
||||
/*@import "../static/css/theme-green/color-green.css"; !*浅绿色主题*!*/
|
||||
```
|
||||
|
||||
第三步:打开 src/components/common/Sidebar.vue 文件,找到 el-menu 标签,把 background-color/text-color/active-text-color 属性去掉即可。
|
||||

|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)
|
||||
[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)
|
||||
|
|
223
README_EN.md
|
@ -1,164 +1,119 @@
|
|||
# vue-manage-system #
|
||||
The web management system solution based on Vue2 and Element-UI。[live demo](http://blog.gdfengshuo.com/example/work/)
|
||||
# vue-manage-system
|
||||
|
||||
<a href="https://github.com/vuejs/vue">
|
||||
<img src="https://img.shields.io/badge/vue-2.6.10-brightgreen.svg" alt="vue">
|
||||
</a>
|
||||
<a href="https://github.com/ElemeFE/element">
|
||||
<img src="https://img.shields.io/badge/element--ui-2.8.2-brightgreen.svg" alt="element-ui">
|
||||
</a>
|
||||
<a href="https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
|
||||
</a>
|
||||
<a href="https://github.com/lin-xin/vue-manage-system/releases">
|
||||
<img src="https://img.shields.io/github/release/lin-xin/vue-manage-system.svg" alt="GitHub release">
|
||||
</a>
|
||||
<a href="https://lin-xin.gitee.io/example/work/#/donate">
|
||||
<img src="https://img.shields.io/badge/%24-donate-ff69b4.svg" alt="donate">
|
||||
</a>
|
||||
|
||||
The web management system solution based on Vue3 and ElementPlus。[live demo](https://lin-xin.gitee.io/example/work/)
|
||||
|
||||
Please check the version of vue2 in [tag V4.2.0](https://github.com/lin-xin/vue-manage-system/tree/V4.2.0)
|
||||
|
||||
## Donation
|
||||

|
||||
|
||||
## Preface ##
|
||||
The scheme as a set of multi-function background frame templates, suitable for most of the WEB management system development. Convenient development fast simple good components based on Vue2 and Element-UI. Color separation of color style, support manual switch themes, and it is convenient to use a custom theme color.
|
||||

|
||||
|
||||
## Function ##
|
||||
- [x] Element-UI
|
||||
- [x] Login/Logout
|
||||
- [x] Dashboard
|
||||
- [x] Table
|
||||
- [x] Tabs
|
||||
- [x] From
|
||||
- [x] Chart :bar_chart:
|
||||
- [x] Editor
|
||||
- [x] Markdown
|
||||
- [x] Upload pictures by clipping or dragging
|
||||
- [x] Support manual switch themes :sparkles:
|
||||
- [x] List drag sort
|
||||
- [x] Permission
|
||||
- [x] 404 / 403
|
||||
- [x] Three level menu
|
||||
- [x] Custom icon
|
||||
## Preface
|
||||
|
||||
The scheme as a set of multi-function background frame templates, suitable for most of the WEB management system development. Convenient development fast simple good components based on Vue3 and ElementPlus. Color separation of color style, support manual switch themes, and it is convenient to use a custom theme color.
|
||||
|
||||
## Installation steps ##
|
||||
## Function
|
||||
|
||||
git clone https://github.com/lin-xin/vue-manage-system.git // Clone templates
|
||||
cd vue-manage-system // Enter template directory
|
||||
npm install // Installation dependency
|
||||
- [x] Element-UI
|
||||
- [x] Login/Logout
|
||||
- [x] Dashboard
|
||||
- [x] Table
|
||||
- [x] Tabs
|
||||
- [x] From
|
||||
- [x] Chart :bar_chart:
|
||||
- [x] Editor
|
||||
- [x] Markdown
|
||||
- [x] Upload pictures by clipping or dragging
|
||||
- [x] Permission
|
||||
- [x] Three level menu
|
||||
- [x] Custom icon
|
||||
|
||||
## Local development ##
|
||||
## Installation steps
|
||||
|
||||
// Open server and access http://localhost:8080 in browser
|
||||
npm run serve
|
||||
git clone https://github.com/lin-xin/vue-manage-system.git // Clone templates
|
||||
cd vue-manage-system // Enter template directory
|
||||
npm install // Installation dependency
|
||||
|
||||
## Constructing production ##
|
||||
## Local development
|
||||
|
||||
// Constructing project
|
||||
npm run build
|
||||
npm run dev
|
||||
|
||||
## Component description and presentation ##
|
||||
## Constructing production
|
||||
|
||||
### vue-schart ###
|
||||
Vue.js wrapper for sChart.js. Github : [vue-schart](https://github.com/linxin/vue-schart)
|
||||
// Constructing project
|
||||
npm run build
|
||||
|
||||
```JavaScript
|
||||
## Component description and presentation
|
||||
|
||||
### vue-schart
|
||||
|
||||
Vue.js wrapper for sChart.js. Github : [vue-schart](https://github.com/lin-xin/vue-schart#/)
|
||||
|
||||
```html
|
||||
<template>
|
||||
<div>
|
||||
<schart :canvasId="canvasId"
|
||||
:type="type"
|
||||
:width="width"
|
||||
:height="height"
|
||||
:data="data"
|
||||
:options="options"
|
||||
></schart>
|
||||
<schart class="wrapper" canvasId="myCanvas" :options="options"></schart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Schart from 'vue-schart';
|
||||
export default {
|
||||
data: function(){
|
||||
return {
|
||||
canvasId: 'myCanvas',
|
||||
type: 'bar',
|
||||
width: 500,
|
||||
height: 400,
|
||||
data: [
|
||||
{name: '2014', value: 1342},
|
||||
{name: '2015', value: 2123},
|
||||
{name: '2016', value: 1654},
|
||||
{name: '2017', value: 1795},
|
||||
],
|
||||
options: {
|
||||
title: 'Total sales of stores in recent years'
|
||||
}
|
||||
}
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import Schart from "vue-schart"; // 导入Schart组件
|
||||
const options = ref({
|
||||
type: "bar",
|
||||
title: {
|
||||
text: "最近一周各品类销售图",
|
||||
},
|
||||
labels: ["周一", "周二", "周三", "周四", "周五"],
|
||||
datasets: [
|
||||
{
|
||||
label: "家电",
|
||||
data: [234, 278, 270, 190, 230],
|
||||
},
|
||||
components: {
|
||||
Schart
|
||||
}
|
||||
}
|
||||
{
|
||||
label: "百货",
|
||||
data: [164, 178, 190, 135, 160],
|
||||
},
|
||||
{
|
||||
label: "食品",
|
||||
data: [144, 198, 150, 235, 120],
|
||||
},
|
||||
],
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
.wrapper {
|
||||
width: 7rem;
|
||||
height: 5rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### element-ui ###
|
||||
A desktop component library based on vue.js2.0 . Github : [element](http://element.eleme.io/#/zh-CN/component/layout)
|
||||
## Screenshot
|
||||
|
||||
### Vue-Quill-Editor ###
|
||||
Quill editor component for Vue2. Github : [vue-quill-editor](https://github.com/surmon-china/vue-quill-editor)
|
||||
|
||||
### mavonEditor ###
|
||||
A markdown editor based on Vue that supports a variety of personalized features. Github: [mavonEditor](https://github.com/hinesboy/mavonEditor)
|
||||
|
||||
### vue-cropperjs ###
|
||||
A Vue wrapper component for cropperjs. Github: [vue-cropperjs](https://github.com/Agontuk/vue-cropperjs)
|
||||
|
||||
|
||||
## Notice ##
|
||||
### 一、If I don't want to use some components, how can I delete it? ###
|
||||
|
||||
For example, I don't want to use the Vue-Quill-Editor component, I need to take four steps.
|
||||
|
||||
The first step to remove the component of the routing. Enter 'src/router/index.js' and delete the code below.
|
||||
|
||||
```JavaScript
|
||||
{
|
||||
path: '/editor',
|
||||
component: resolve => require(['../components/page/VueEditor.vue'], resolve)
|
||||
},
|
||||
```
|
||||
|
||||
Second,delete the component files. Enter 'src/components/page/' and delete 'VueEditor.vue' file.
|
||||
|
||||
The third step is to delete the entry. Enter 'src/components/common/Sidebar.vue' and delete the code below.
|
||||
|
||||
```js
|
||||
{
|
||||
index: 'editor',
|
||||
title: '富文本编辑器'
|
||||
},
|
||||
```
|
||||
|
||||
Finally, uninstall this component.
|
||||
|
||||
npm un vue-quill-editor -S
|
||||
|
||||
Complete!
|
||||
|
||||
### 二、How to switch themes? ###
|
||||
|
||||
The first step to enter 'src/main.js' and change into green theme.
|
||||
|
||||
```javascript
|
||||
import 'element-ui/lib/theme-default/index.css'; // default theme
|
||||
// import '../static/css/theme-green/index.css'; // green theme
|
||||
```
|
||||
|
||||
The second step to enter 'src/App.vue' and change into green theme.
|
||||
|
||||
```javascript
|
||||
@import "../static/css/main.css";
|
||||
@import "../static/css/color-dark.css"; /*深色主题*/
|
||||
/*@import "../static/css/theme-green/color-green.css"; !*浅绿色主题*!*/
|
||||
```
|
||||
|
||||
Finally,enter 'src/components/common/Sidebar.vue' and find el-menu Tags,delete 'background-color/text-color/active-text-color'。
|
||||
|
||||
## Screenshot ##
|
||||
### Default theme ###
|
||||
### Default theme
|
||||
|
||||

|
||||
|
||||
### Green theme ###
|
||||
### Login
|
||||
|
||||

|
||||

|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)
|
||||
[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
// Generated by 'unplugin-auto-import'
|
||||
export {}
|
||||
declare global {
|
||||
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
]
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
// generated by unplugin-vue-components
|
||||
// We suggest you to commit this file into source control
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
Countup: typeof import('./src/components/countup.vue')['default']
|
||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCalendar: typeof import('element-plus/es')['ElCalendar']
|
||||
ElCard: typeof import('element-plus/es')['ElCard']
|
||||
ElCarousel: typeof import('element-plus/es')['ElCarousel']
|
||||
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
|
||||
ElCascader: typeof import('element-plus/es')['ElCascader']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
|
||||
ElCountdown: typeof import('element-plus/es')['ElCountdown']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElImage: typeof import('element-plus/es')['ElImage']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||
ElLink: typeof import('element-plus/es')['ElLink']
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElRate: typeof import('element-plus/es')['ElRate']
|
||||
ElResult: typeof import('element-plus/es')['ElResult']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSlider: typeof import('element-plus/es')['ElSlider']
|
||||
ElSpace: typeof import('element-plus/es')['ElSpace']
|
||||
ElStatistic: typeof import('element-plus/es')['ElStatistic']
|
||||
ElStep: typeof import('element-plus/es')['ElStep']
|
||||
ElSteps: typeof import('element-plus/es')['ElSteps']
|
||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ElTimeline: typeof import('element-plus/es')['ElTimeline']
|
||||
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
|
||||
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
ElTour: typeof import('element-plus/es')['ElTour']
|
||||
ElTourStep: typeof import('element-plus/es')['ElTourStep']
|
||||
ElTransfer: typeof import('element-plus/es')['ElTransfer']
|
||||
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||
ElWatermark: typeof import('element-plus/es')['ElWatermark']
|
||||
Header: typeof import('./src/components/header.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
Sidebar: typeof import('./src/components/sidebar.vue')['default']
|
||||
TableCustom: typeof import('./src/components/table-custom.vue')['default']
|
||||
TableDetail: typeof import('./src/components/table-detail.vue')['default']
|
||||
TableEdit: typeof import('./src/components/table-edit.vue')['default']
|
||||
TableSearch: typeof import('./src/components/table-search.vue')['default']
|
||||
Tabs: typeof import('./src/components/tabs.vue')['default']
|
||||
}
|
||||
}
|
BIN
favicon.ico
Before Width: | Height: | Size: 914 B |
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>vue-manage-system后台管理系统</title>
|
||||
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_830376_92o68tc95je.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
|
||||
Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
|
||||
</html>
|
72
package.json
|
@ -1,28 +1,44 @@
|
|||
{
|
||||
"name": "vue-manage-system",
|
||||
"version": "4.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run serve",
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"element-ui": "^2.8.2",
|
||||
"mavon-editor": "^2.6.17",
|
||||
"vue": "^2.6.10",
|
||||
"vue-cropperjs": "^3.0.0",
|
||||
"vue-i18n": "^8.10.0",
|
||||
"vue-quill-editor": "^3.0.6",
|
||||
"vue-router": "^3.0.1",
|
||||
"vue-schart": "^1.0.0",
|
||||
"vuedraggable": "^2.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^3.2.0",
|
||||
"@vue/cli-service": "^3.2.0",
|
||||
"vue-template-compiler": "^2.5.21"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "vue-manage-system",
|
||||
"version": "5.5.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "*",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"axios": "^1.6.3",
|
||||
"countup.js": "^2.8.0",
|
||||
"echarts": "^5.5.0",
|
||||
"echarts-wordcloud": "^2.1.0",
|
||||
"element-plus": "^2.6.3",
|
||||
"md-editor-v3": "^2.11.2",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.5",
|
||||
"vue-cropper": "1.1.1",
|
||||
"vue-echarts": "^6.6.9",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue-schart": "^2.0.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^3.0.0",
|
||||
"@vue/compiler-sfc": "^3.1.2",
|
||||
"typescript": "^4.6.4",
|
||||
"unplugin-auto-import": "^0.11.2",
|
||||
"unplugin-vue-components": "^0.22.4",
|
||||
"vite": "^3.0.0",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||
"vue-tsc": "^0.38.4"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
|
||||
<link rel="stylesheet" href="//at.alicdn.com/t/font_830376_qzecyukz0s.css">
|
||||
<title>vue-manage-system</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but vms doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"list": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "管理员",
|
||||
"key": "admin",
|
||||
"status": true,
|
||||
"permiss": [
|
||||
"0",
|
||||
"1",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"2",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"3",
|
||||
"31",
|
||||
"32",
|
||||
"33",
|
||||
"331",
|
||||
"332",
|
||||
"4",
|
||||
"41",
|
||||
"42",
|
||||
"5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "普通用户",
|
||||
"key": "user",
|
||||
"status": true,
|
||||
"permiss": [
|
||||
"0",
|
||||
"1",
|
||||
"11",
|
||||
"12",
|
||||
"13"
|
||||
]
|
||||
}
|
||||
],
|
||||
"pageTotal": 2
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"list": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "张三",
|
||||
"money": 123,
|
||||
"address": "广东省东莞市长安镇",
|
||||
"state": true,
|
||||
"date": "2019-11-1",
|
||||
"thumb": "https://lin-xin.gitee.io/images/post/wms.png"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "李四",
|
||||
"money": 456,
|
||||
"address": "广东省广州市白云区",
|
||||
"state": true,
|
||||
"date": "2019-10-11",
|
||||
"thumb": "https://lin-xin.gitee.io/images/post/node3.png"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "王五",
|
||||
"money": 789,
|
||||
"address": "湖南省长沙市",
|
||||
"state": false,
|
||||
"date": "2019-11-11",
|
||||
"thumb": "https://lin-xin.gitee.io/images/post/parcel.png"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "赵六",
|
||||
"money": 1011,
|
||||
"address": "福建省厦门市鼓浪屿",
|
||||
"state": true,
|
||||
"date": "2019-10-20",
|
||||
"thumb": "https://lin-xin.gitee.io/images/post/notice.png"
|
||||
}
|
||||
],
|
||||
"pageTotal": 4
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"list": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "张三",
|
||||
"password": "123",
|
||||
"email": "123@qq.com",
|
||||
"phone": "12345678944",
|
||||
"date": "2024-01-01",
|
||||
"role": "管理员"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "李四",
|
||||
"password": "123",
|
||||
"email": "1234@qq.com",
|
||||
"phone": "12345678945",
|
||||
"date": "2024-01-01",
|
||||
"role": "普通用户"
|
||||
}
|
||||
],
|
||||
"pageTotal": 2
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"list": [{
|
||||
"date": "1997-11-11",
|
||||
"name": "林丽",
|
||||
"address": "吉林省 辽源市 龙山区"
|
||||
}, {
|
||||
"date": "1987-09-24",
|
||||
"name": "文敏",
|
||||
"address": "江西省 萍乡市 芦溪县"
|
||||
}, {
|
||||
"date": "1996-08-08",
|
||||
"name": "杨秀兰",
|
||||
"address": "黑龙江省 黑河市 五大连池市"
|
||||
}, {
|
||||
"date": "1978-06-18",
|
||||
"name": "魏强",
|
||||
"address": "广东省 韶关市 始兴县"
|
||||
}, {
|
||||
"date": "1977-07-09",
|
||||
"name": "石秀兰",
|
||||
"address": "江苏省 宿迁市 宿豫区"
|
||||
}, {
|
||||
"date": "1994-09-20",
|
||||
"name": "朱洋",
|
||||
"address": "海外 海外 -"
|
||||
}, {
|
||||
"date": "1980-01-22",
|
||||
"name": "傅敏",
|
||||
"address": "海外 海外 -"
|
||||
}, {
|
||||
"date": "1985-10-10",
|
||||
"name": "毛明",
|
||||
"address": "内蒙古自治区 包头市 九原区"
|
||||
}, {
|
||||
"date": "1975-09-08",
|
||||
"name": "何静",
|
||||
"address": "西藏自治区 阿里地区 普兰县"
|
||||
}, {
|
||||
"date": "1970-06-07",
|
||||
"name": "郭秀英",
|
||||
"address": "四川省 巴中市 恩阳区"
|
||||
}]
|
||||
}
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 425 KiB |
27
src/App.vue
|
@ -1,10 +1,17 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
@import "./assets/css/main.css";
|
||||
@import "./assets/css/color-dark.css"; /*深色主题*/
|
||||
/*@import "./assets/css/theme-green/color-green.css"; 浅绿色主题*/
|
||||
</style>
|
||||
<template>
|
||||
<el-config-provider :locale="zhCn">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElConfigProvider } from 'element-plus';
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||
import { useThemeStore } from './store/theme';
|
||||
|
||||
const theme = useThemeStore();
|
||||
theme.initTheme();
|
||||
</script>
|
||||
<style>
|
||||
@import './assets/css/main.css';
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import request from '../utils/request';
|
||||
|
||||
export const fetchData = () => {
|
||||
return request({
|
||||
url: './mock/table.json',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchUserData = () => {
|
||||
return request({
|
||||
url: './mock/user.json',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchRoleData = () => {
|
||||
return request({
|
||||
url: './mock/role.json',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
.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);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
[class*=" el-icon-lx"], [class^=el-icon-lx] {
|
||||
font-family: lx-iconfont!important;
|
||||
}
|
||||
[class*=" el-icon-lx"],
|
||||
[class^=el-icon-lx] {
|
||||
font-family: lx-iconfont !important;
|
||||
}
|
|
@ -1,173 +1,87 @@
|
|||
* {
|
||||
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;
|
||||
}
|
||||
|
||||
.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 .5s;
|
||||
}
|
||||
|
||||
.move-enter,
|
||||
.move-leave {
|
||||
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;
|
||||
}
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, 'microsoft yahei', arial, STHeiTi, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
i {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 30px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.el-table th {
|
||||
background-color: #f5f7fa !important;
|
||||
}
|
||||
|
||||
.plugins-tips {
|
||||
padding: 20px 10px;
|
||||
margin-bottom: 20px;
|
||||
background: #eef1f6;
|
||||
}
|
||||
|
||||
.plugins-tips a {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-button + .el-tooltip {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.mgb20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.mgb10 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.mr10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.move-enter-active,
|
||||
.move-leave-active {
|
||||
transition: opacity 0.1s ease;
|
||||
}
|
||||
|
||||
.move-enter-from,
|
||||
.move-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:root {
|
||||
--header-bg-color: #242f42;
|
||||
--header-text-color: #fff;
|
||||
--active-color: var(--el-color-primary);
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
.header{
|
||||
background-color: #07c4a8;
|
||||
}
|
||||
.login-wrap{
|
||||
background: rgba(56, 157, 170, 0.82);;
|
||||
}
|
||||
.plugins-tips{
|
||||
background: #f2f2f2;
|
||||
}
|
||||
.plugins-tips a{
|
||||
color: #00d1b2;
|
||||
}
|
||||
.el-upload--text em {
|
||||
color: #00d1b2;
|
||||
}
|
||||
.pure-button{
|
||||
background: #00d1b2;
|
||||
}
|
||||
.pagination > .active > a, .pagination > .active > a:hover, .pagination > .active > a:focus, .pagination > .active > span, .pagination > .active > span:hover, .pagination > .active > span:focus {
|
||||
background-color: #00d1b2 !important;
|
||||
border-color: #00d1b2 !important;
|
||||
}
|
||||
.tags-li.active {
|
||||
border: 1px solid #00d1b2;
|
||||
background-color: #00d1b2;
|
||||
}
|
||||
.collapse-btn:hover{
|
||||
background: #00d1b2;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144.08 128.61"><title>资源 82</title><path d="M72.23 128.61c-7.1-.23-11.51-3.72-14.76-9.36C48 102.87 38.43 86.59 29.1 70.16a36 36 0 0 1-4.47-11.35A14.61 14.61 0 0 1 34 42.51c7.49-2.71 15.71-.21 19.67 6.43 7.52 12.56 14.77 25.27 22.12 37.92 3 5.17 5.89 10.43 9 15.51 5 8 3.45 18-4.22 23.31-2.3 1.62-5.52 1.99-8.34 2.93z" fill="#2ef2e9"/><path d="M72.66.33c6-.57 10.39 2.6 13.51 8C95.61 24.69 105 41.1 114.52 57.4c3.9 6.65-.28 17.13-6.39 20.44-8.93 4.83-17.88 1.28-21.86-5.62C76.82 55.86 67.14 39.62 58.11 23 52.06 12 59.61.24 72.66.33z" fill="#fa6663"/><path d="M144.08 15.83c-.58 8.62-6.73 15.57-15.51 15.66-9.31.09-16.87-7-16.95-15.62S119 0 127.87 0c9.13.09 16.22 7 16.21 15.83z" fill="#fbb355"/><path d="M16.24 31.5C7 31.33-.19 24.42 0 15.8.19 7.5 7.19-.06 14.64 0c10.53.08 18.27 6.73 17.61 15.9-.64 8.96-6.25 15.28-16.01 15.6z" fill="#8a56c2"/></svg>
|
After Width: | Height: | Size: 918 B |
After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 6.7 KiB |
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"list": [{
|
||||
"date": "1997-11-11",
|
||||
"name": "林丽",
|
||||
"address": "吉林省 辽源市 龙山区"
|
||||
}, {
|
||||
"date": "1987-09-24",
|
||||
"name": "文敏",
|
||||
"address": "江西省 萍乡市 芦溪县"
|
||||
}, {
|
||||
"date": "1996-08-08",
|
||||
"name": "杨秀兰",
|
||||
"address": "黑龙江省 黑河市 五大连池市"
|
||||
}, {
|
||||
"date": "1978-06-18",
|
||||
"name": "魏强",
|
||||
"address": "广东省 韶关市 始兴县"
|
||||
}, {
|
||||
"date": "1977-07-09",
|
||||
"name": "石秀兰",
|
||||
"address": "江苏省 宿迁市 宿豫区"
|
||||
}, {
|
||||
"date": "1994-09-20",
|
||||
"name": "朱洋",
|
||||
"address": "海外 海外 -"
|
||||
}, {
|
||||
"date": "1980-01-22",
|
||||
"name": "傅敏",
|
||||
"address": "海外 海外 -"
|
||||
}, {
|
||||
"date": "1985-10-10",
|
||||
"name": "毛明",
|
||||
"address": "内蒙古自治区 包头市 九原区"
|
||||
}, {
|
||||
"date": "1975-09-08",
|
||||
"name": "何静",
|
||||
"address": "西藏自治区 阿里地区 普兰县"
|
||||
}, {
|
||||
"date": "1970-06-07",
|
||||
"name": "郭秀英",
|
||||
"address": "四川省 巴中市 恩阳区"
|
||||
}]
|
||||
}
|
|
@ -1,185 +0,0 @@
|
|||
<template>
|
||||
<div class="header">
|
||||
<!-- 折叠按钮 -->
|
||||
<div class="collapse-btn" @click="collapseChage">
|
||||
<i class="el-icon-menu"></i>
|
||||
</div>
|
||||
<div class="logo">后台管理系统</div>
|
||||
<div class="header-right">
|
||||
<div class="header-user-con">
|
||||
<!-- 全屏显示 -->
|
||||
<div class="btn-fullscreen" @click="handleFullScreen">
|
||||
<el-tooltip effect="dark" :content="fullscreen?`取消全屏`:`全屏`" placement="bottom">
|
||||
<i class="el-icon-rank"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<!-- 消息中心 -->
|
||||
<div class="btn-bell">
|
||||
<el-tooltip effect="dark" :content="message?`有${message}条未读消息`:`消息中心`" placement="bottom">
|
||||
<router-link to="/tabs">
|
||||
<i class="el-icon-bell"></i>
|
||||
</router-link>
|
||||
</el-tooltip>
|
||||
<span class="btn-bell-badge" v-if="message"></span>
|
||||
</div>
|
||||
<!-- 用户头像 -->
|
||||
<div class="user-avator"><img src="../../assets/img/img.jpg"></div>
|
||||
<!-- 用户名下拉菜单 -->
|
||||
<el-dropdown class="user-name" trigger="click" @command="handleCommand">
|
||||
<span class="el-dropdown-link">
|
||||
{{username}} <i class="el-icon-caret-bottom"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<a href="http://blog.gdfengshuo.com/about/" target="_blank">
|
||||
<el-dropdown-item>关于作者</el-dropdown-item>
|
||||
</a>
|
||||
<a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
|
||||
<el-dropdown-item>项目仓库</el-dropdown-item>
|
||||
</a>
|
||||
<el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import bus from '../common/bus';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
collapse: false,
|
||||
fullscreen: false,
|
||||
name: 'linxin',
|
||||
message: 2
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
username(){
|
||||
let username = localStorage.getItem('ms_username');
|
||||
return username ? username : this.name;
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
// 用户名下拉菜单选择事件
|
||||
handleCommand(command) {
|
||||
if(command == 'loginout'){
|
||||
localStorage.removeItem('ms_username')
|
||||
this.$router.push('/login');
|
||||
}
|
||||
},
|
||||
// 侧边栏折叠
|
||||
collapseChage(){
|
||||
this.collapse = !this.collapse;
|
||||
bus.$emit('collapse', this.collapse);
|
||||
},
|
||||
// 全屏事件
|
||||
handleFullScreen(){
|
||||
let element = document.documentElement;
|
||||
if (this.fullscreen) {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.webkitCancelFullScreen) {
|
||||
document.webkitCancelFullScreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
} else {
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen();
|
||||
} else if (element.webkitRequestFullScreen) {
|
||||
element.webkitRequestFullScreen();
|
||||
} else if (element.mozRequestFullScreen) {
|
||||
element.mozRequestFullScreen();
|
||||
} else if (element.msRequestFullscreen) {
|
||||
// IE11
|
||||
element.msRequestFullscreen();
|
||||
}
|
||||
}
|
||||
this.fullscreen = !this.fullscreen;
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
if(document.body.clientWidth < 1500){
|
||||
this.collapseChage();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.header {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
}
|
||||
.collapse-btn{
|
||||
float: left;
|
||||
padding: 0 21px;
|
||||
cursor: pointer;
|
||||
line-height: 70px;
|
||||
}
|
||||
.header .logo{
|
||||
float: left;
|
||||
width:250px;
|
||||
line-height: 70px;
|
||||
}
|
||||
.header-right{
|
||||
float: right;
|
||||
padding-right: 50px;
|
||||
}
|
||||
.header-user-con{
|
||||
display: flex;
|
||||
height: 70px;
|
||||
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>
|
|
@ -1,48 +0,0 @@
|
|||
<template>
|
||||
<div class="wrapper">
|
||||
<v-head></v-head>
|
||||
<v-sidebar></v-sidebar>
|
||||
<div class="content-box" :class="{'content-collapse':collapse}">
|
||||
<v-tags></v-tags>
|
||||
<div class="content">
|
||||
<transition name="move" mode="out-in">
|
||||
<keep-alive :include="tagsList">
|
||||
<router-view></router-view>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import vHead from './Header.vue';
|
||||
import vSidebar from './Sidebar.vue';
|
||||
import vTags from './Tags.vue';
|
||||
import bus from './bus';
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
tagsList: [],
|
||||
collapse: false
|
||||
}
|
||||
},
|
||||
components:{
|
||||
vHead, vSidebar, vTags
|
||||
},
|
||||
created(){
|
||||
bus.$on('collapse', msg => {
|
||||
this.collapse = msg;
|
||||
})
|
||||
|
||||
// 只有在标签页列表里的页面才使用keep-alive,即关闭标签之后就不保存到内存中了。
|
||||
bus.$on('tags', msg => {
|
||||
let arr = [];
|
||||
for(let i = 0, len = msg.length; i < len; i ++){
|
||||
msg[i].name && arr.push(msg[i].name);
|
||||
}
|
||||
this.tagsList = arr;
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,165 +0,0 @@
|
|||
<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-submenu :index="item.index" :key="item.index">
|
||||
<template slot="title">
|
||||
<i :class="item.icon"></i><span slot="title">{{ item.title }}</span>
|
||||
</template>
|
||||
<template v-for="subItem in item.subs">
|
||||
<el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
|
||||
<template slot="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-submenu>
|
||||
<el-menu-item v-else :index="subItem.index" :key="subItem.index">
|
||||
{{ subItem.title }}
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-submenu>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-menu-item :index="item.index" :key="item.index">
|
||||
<i :class="item.icon"></i><span slot="title">{{ item.title }}</span>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</el-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import bus from '../common/bus';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
collapse: false,
|
||||
items: [
|
||||
{
|
||||
icon: 'el-icon-lx-home',
|
||||
index: 'dashboard',
|
||||
title: '系统首页'
|
||||
},
|
||||
{
|
||||
icon: 'el-icon-lx-cascades',
|
||||
index: 'table',
|
||||
title: '基础表格'
|
||||
},
|
||||
{
|
||||
icon: 'el-icon-lx-copy',
|
||||
index: 'tabs',
|
||||
title: 'tab选项卡'
|
||||
},
|
||||
{
|
||||
icon: 'el-icon-lx-calendar',
|
||||
index: '3',
|
||||
title: '表单相关',
|
||||
subs: [
|
||||
{
|
||||
index: 'form',
|
||||
title: '基本表单'
|
||||
},
|
||||
{
|
||||
index: '3-2',
|
||||
title: '三级菜单',
|
||||
subs: [
|
||||
{
|
||||
index: 'editor',
|
||||
title: '富文本编辑器'
|
||||
},
|
||||
{
|
||||
index: 'markdown',
|
||||
title: 'markdown编辑器'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
index: 'upload',
|
||||
title: '文件上传'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
icon: 'el-icon-lx-emoji',
|
||||
index: 'icon',
|
||||
title: '自定义图标'
|
||||
},
|
||||
{
|
||||
icon: 'el-icon-pie-chart',
|
||||
index: 'charts',
|
||||
title: 'schart图表'
|
||||
},
|
||||
{
|
||||
icon: 'el-icon-rank',
|
||||
index: '6',
|
||||
title: '拖拽组件',
|
||||
subs: [
|
||||
{
|
||||
index: 'drag',
|
||||
title: '拖拽列表',
|
||||
},
|
||||
{
|
||||
index: 'dialog',
|
||||
title: '拖拽弹框',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
icon: 'el-icon-lx-global',
|
||||
index: 'i18n',
|
||||
title: '国际化功能'
|
||||
},
|
||||
{
|
||||
icon: 'el-icon-lx-warn',
|
||||
index: '7',
|
||||
title: '错误处理',
|
||||
subs: [
|
||||
{
|
||||
index: 'permission',
|
||||
title: '权限测试'
|
||||
},
|
||||
{
|
||||
index: '404',
|
||||
title: '404页面'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
onRoutes(){
|
||||
return this.$route.path.replace('/','');
|
||||
}
|
||||
},
|
||||
created(){
|
||||
// 通过 Event Bus 进行组件间通信,来折叠侧边栏
|
||||
bus.$on('collapse', msg => {
|
||||
this.collapse = msg;
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sidebar{
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 70px;
|
||||
bottom:0;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.sidebar::-webkit-scrollbar{
|
||||
width: 0;
|
||||
}
|
||||
.sidebar-el-menu:not(.el-menu--collapse){
|
||||
width: 250px;
|
||||
}
|
||||
.sidebar > ul {
|
||||
height:100%;
|
||||
}
|
||||
</style>
|
|
@ -1,186 +0,0 @@
|
|||
<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>
|
||||
<el-dropdown-menu size="small" slot="dropdown">
|
||||
<el-dropdown-item command="other">关闭其他</el-dropdown-item>
|
||||
<el-dropdown-item command="all">关闭所有</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import bus from './bus';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tagsList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isActive(path) {
|
||||
return path === this.$route.fullPath;
|
||||
},
|
||||
// 关闭单个标签
|
||||
closeTags(index) {
|
||||
const delItem = this.tagsList.splice(index, 1)[0];
|
||||
const item = this.tagsList[index] ? this.tagsList[index] : this.tagsList[index - 1];
|
||||
if (item) {
|
||||
delItem.path === this.$route.fullPath && this.$router.push(item.path);
|
||||
}else{
|
||||
this.$router.push('/');
|
||||
}
|
||||
},
|
||||
// 关闭全部标签
|
||||
closeAll(){
|
||||
this.tagsList = [];
|
||||
this.$router.push('/');
|
||||
},
|
||||
// 关闭其他标签
|
||||
closeOther(){
|
||||
const curItem = this.tagsList.filter(item => {
|
||||
return item.path === this.$route.fullPath;
|
||||
})
|
||||
this.tagsList = curItem;
|
||||
},
|
||||
// 设置标签
|
||||
setTags(route){
|
||||
const isExist = this.tagsList.some(item => {
|
||||
return item.path === route.fullPath;
|
||||
})
|
||||
if(!isExist){
|
||||
if(this.tagsList.length >= 8){
|
||||
this.tagsList.shift();
|
||||
}
|
||||
this.tagsList.push({
|
||||
title: route.meta.title,
|
||||
path: route.fullPath,
|
||||
name: route.matched[1].components.default.name
|
||||
})
|
||||
}
|
||||
bus.$emit('tags', this.tagsList);
|
||||
},
|
||||
handleTags(command){
|
||||
command === 'other' ? this.closeOther() : this.closeAll();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showTags() {
|
||||
return this.tagsList.length > 0;
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
$route(newValue, oldValue){
|
||||
this.setTags(newValue);
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.setTags(this.$route);
|
||||
// 监听关闭当前页面的标签页
|
||||
bus.$on('close_current_tags', () => {
|
||||
for (let i = 0, len = this.tagsList.length; i < len; i++) {
|
||||
const item = this.tagsList[i];
|
||||
if(item.path === this.$route.fullPath){
|
||||
if(i < len - 1){
|
||||
this.$router.push(this.tagsList[i+1].path);
|
||||
}else if(i > 0){
|
||||
this.$router.push(this.tagsList[i-1].path);
|
||||
}else{
|
||||
this.$router.push('/');
|
||||
}
|
||||
this.tagsList.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
</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 .3s ease-in;
|
||||
-moz-transition: all .3s ease-in;
|
||||
transition: all .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, .1);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,6 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
|
||||
// 使用 Event Bus
|
||||
const bus = new Vue();
|
||||
|
||||
export default bus;
|
|
@ -1,80 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
|
||||
// v-dialogDrag: 弹窗拖拽属性
|
||||
Vue.directive('dialogDrag', {
|
||||
bind(el, binding, vnode, oldVnode) {
|
||||
const dialogHeaderEl = el.querySelector('.el-dialog__header');
|
||||
const dragDom = el.querySelector('.el-dialog');
|
||||
|
||||
dialogHeaderEl.style.cssText += ';cursor:move;'
|
||||
dragDom.style.cssText += ';top:0px;'
|
||||
|
||||
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
|
||||
const sty = (() => {
|
||||
if (window.document.currentStyle) {
|
||||
return (dom, attr) => dom.currentStyle[attr];
|
||||
} else {
|
||||
return (dom, attr) => getComputedStyle(dom, false)[attr];
|
||||
}
|
||||
})()
|
||||
|
||||
dialogHeaderEl.onmousedown = (e) => {
|
||||
// 鼠标按下,计算当前元素距离可视区的距离
|
||||
const disX = e.clientX - dialogHeaderEl.offsetLeft;
|
||||
const disY = e.clientY - dialogHeaderEl.offsetTop;
|
||||
|
||||
const screenWidth = document.body.clientWidth; // body当前宽度
|
||||
const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取)
|
||||
|
||||
const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
|
||||
const dragDomheight = dragDom.offsetHeight; // 对话框高度
|
||||
|
||||
const minDragDomLeft = dragDom.offsetLeft;
|
||||
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
|
||||
|
||||
const minDragDomTop = dragDom.offsetTop;
|
||||
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
|
||||
|
||||
|
||||
// 获取到的值带px 正则匹配替换
|
||||
let styL = sty(dragDom, 'left');
|
||||
let styT = sty(dragDom, 'top');
|
||||
|
||||
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
||||
if (styL.includes('%')) {
|
||||
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
|
||||
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
|
||||
} else {
|
||||
styL = +styL.replace(/\px/g, '');
|
||||
styT = +styT.replace(/\px/g, '');
|
||||
};
|
||||
|
||||
document.onmousemove = function (e) {
|
||||
// 通过事件委托,计算移动的距离
|
||||
let left = e.clientX - disX;
|
||||
let top = e.clientY - disY;
|
||||
|
||||
// 边界处理
|
||||
if (-(left) > minDragDomLeft) {
|
||||
left = -(minDragDomLeft);
|
||||
} else if (left > maxDragDomLeft) {
|
||||
left = maxDragDomLeft;
|
||||
}
|
||||
|
||||
if (-(top) > minDragDomTop) {
|
||||
top = -(minDragDomTop);
|
||||
} else if (top > maxDragDomTop) {
|
||||
top = maxDragDomTop;
|
||||
}
|
||||
|
||||
// 移动当前元素
|
||||
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
|
||||
};
|
||||
|
||||
document.onmouseup = function (e) {
|
||||
document.onmousemove = null;
|
||||
document.onmouseup = null;
|
||||
};
|
||||
}
|
||||
}
|
||||
})
|
|
@ -1,30 +0,0 @@
|
|||
export const messages = {
|
||||
'zh': {
|
||||
i18n: {
|
||||
breadcrumb: '国际化产品',
|
||||
tips: '通过切换语言按钮,来改变当前内容的语言。',
|
||||
btn: '切换英文',
|
||||
title1: '常用用法',
|
||||
p1: '要是你把你的秘密告诉了风,那就别怪风把它带给树。',
|
||||
p2: '没有什么比信念更能支撑我们度过艰难的时光了。',
|
||||
p3: '只要能把自己的事做好,并让自己快乐,你就领先于大多数人了。',
|
||||
title2: '组件插值',
|
||||
info: 'Element组件需要国际化,请参考 {action}。',
|
||||
value: '文档'
|
||||
}
|
||||
},
|
||||
'en': {
|
||||
i18n: {
|
||||
breadcrumb: 'International Products',
|
||||
tips: 'Click on the button to change the current language. ',
|
||||
btn: 'Switch Chinese',
|
||||
title1: 'Common usage',
|
||||
p1: "If you reveal your secrets to the wind you should not blame the wind for revealing them to the trees.",
|
||||
p2: "Nothing can help us endure dark times better than our faith. ",
|
||||
p3: "If you can do what you do best and be happy, you're further along in life than most people.",
|
||||
title2: 'Component interpolation',
|
||||
info: 'The default language of Element is Chinese. If you wish to use another language, please refer to the {action}.',
|
||||
value: 'documentation'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<template>
|
||||
<span ref="countRef"></span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { CountUp } from 'countup.js';
|
||||
|
||||
const props = defineProps({
|
||||
end: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
required: false,
|
||||
},
|
||||
});
|
||||
|
||||
const countRef = ref<any>(null);
|
||||
let countUp: any;
|
||||
onMounted(() => {
|
||||
countUp = new CountUp(countRef.value, props.end, props.options);
|
||||
if (countUp.error) {
|
||||
console.error(countUp.error);
|
||||
return;
|
||||
}
|
||||
countUp.start();
|
||||
});
|
||||
|
||||
watch(() => props.end, (newVal) => {
|
||||
if (countUp) {
|
||||
countUp.update(newVal);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
|
@ -0,0 +1,204 @@
|
|||
<template>
|
||||
<div class="header">
|
||||
<!-- 折叠按钮 -->
|
||||
<div class="header-left">
|
||||
<img class="logo" src="../assets/img/logo.svg" alt="" />
|
||||
<div class="web-title">后台管理系统</div>
|
||||
<div class="collapse-btn" @click="collapseChage">
|
||||
<el-icon v-if="sidebar.collapse">
|
||||
<Expand />
|
||||
</el-icon>
|
||||
<el-icon v-else>
|
||||
<Fold />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="header-user-con">
|
||||
<div class="btn-icon" @click="router.push('/theme')">
|
||||
<el-tooltip effect="dark" content="设置主题" placement="bottom">
|
||||
<i class="el-icon-lx-skin"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="btn-icon" @click="router.push('/ucenter')">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="message ? `有${message}条未读消息` : `消息中心`"
|
||||
placement="bottom"
|
||||
>
|
||||
<i class="el-icon-lx-notice"></i>
|
||||
</el-tooltip>
|
||||
<span class="btn-bell-badge" v-if="message"></span>
|
||||
</div>
|
||||
<div class="btn-icon" @click="setFullScreen">
|
||||
<el-tooltip effect="dark" content="全屏" placement="bottom">
|
||||
<i class="el-icon-lx-full"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<!-- 用户头像 -->
|
||||
<el-avatar class="user-avator" :size="30" :src="imgurl" />
|
||||
<!-- 用户名下拉菜单 -->
|
||||
<el-dropdown class="user-name" trigger="click" @command="handleCommand">
|
||||
<span class="el-dropdown-link">
|
||||
{{ username }}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
|
||||
<el-dropdown-item>项目仓库</el-dropdown-item>
|
||||
</a>
|
||||
<a href="https://lin-xin.gitee.io/example/vuems-doc/" target="_blank">
|
||||
<el-dropdown-item>官方文档</el-dropdown-item>
|
||||
</a>
|
||||
<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 setup lang="ts">
|
||||
import { onMounted } from 'vue';
|
||||
import { useSidebarStore } from '../store/sidebar';
|
||||
import { useRouter } from 'vue-router';
|
||||
import imgurl from '../assets/img/img.jpg';
|
||||
|
||||
const username: string | null = localStorage.getItem('vuems_name');
|
||||
const message: number = 2;
|
||||
|
||||
const sidebar = useSidebarStore();
|
||||
// 侧边栏折叠
|
||||
const collapseChage = () => {
|
||||
sidebar.handleCollapse();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (document.body.clientWidth < 1500) {
|
||||
collapseChage();
|
||||
}
|
||||
});
|
||||
|
||||
// 用户名下拉菜单选择事件
|
||||
const router = useRouter();
|
||||
const handleCommand = (command: string) => {
|
||||
if (command == 'loginout') {
|
||||
localStorage.removeItem('vuems_name');
|
||||
router.push('/login');
|
||||
} else if (command == 'user') {
|
||||
router.push('/ucenter');
|
||||
}
|
||||
};
|
||||
|
||||
const setFullScreen = () => {
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
} else {
|
||||
document.body.requestFullscreen.call(document.body);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
color: var(--header-text-color);
|
||||
background-color: var(--header-bg-color);
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 35px;
|
||||
}
|
||||
|
||||
.web-title {
|
||||
margin: 0 40px 0 10px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.collapse-btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.collapse-btn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
float: right;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.header-user-con {
|
||||
display: flex;
|
||||
height: 70px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-fullscreen {
|
||||
transform: rotate(45deg);
|
||||
margin-right: 5px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
position: relative;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--header-text-color);
|
||||
margin: 0 5px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.btn-bell-badge {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 0px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
background: #f56c6c;
|
||||
color: var(--header-text-color);
|
||||
}
|
||||
|
||||
.user-avator {
|
||||
margin: 0 10px 0 20px;
|
||||
}
|
||||
|
||||
.el-dropdown-link {
|
||||
color: var(--header-text-color);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,221 @@
|
|||
import { Menus } from '@/types/menu';
|
||||
|
||||
export const menuData: Menus[] = [
|
||||
{
|
||||
id: '0',
|
||||
title: '系统首页',
|
||||
index: '/dashboard',
|
||||
icon: 'Odometer',
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
title: '系统管理',
|
||||
index: '1',
|
||||
icon: 'HomeFilled',
|
||||
children: [
|
||||
{
|
||||
id: '11',
|
||||
pid: '1',
|
||||
index: '/system-user',
|
||||
title: '用户管理',
|
||||
},
|
||||
{
|
||||
id: '12',
|
||||
pid: '1',
|
||||
index: '/system-role',
|
||||
title: '角色管理',
|
||||
},
|
||||
{
|
||||
id: '13',
|
||||
pid: '1',
|
||||
index: '/system-menu',
|
||||
title: '菜单管理',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '组件',
|
||||
index: '2-1',
|
||||
icon: 'Calendar',
|
||||
children: [
|
||||
{
|
||||
id: '21',
|
||||
pid: '3',
|
||||
index: '/form',
|
||||
title: '表单',
|
||||
},
|
||||
{
|
||||
id: '22',
|
||||
pid: '3',
|
||||
index: '/upload',
|
||||
title: '上传',
|
||||
},
|
||||
{
|
||||
id: '23',
|
||||
pid: '2',
|
||||
index: '/carousel',
|
||||
title: '走马灯',
|
||||
},
|
||||
{
|
||||
id: '24',
|
||||
pid: '2',
|
||||
index: '/calendar',
|
||||
title: '日历',
|
||||
},
|
||||
{
|
||||
id: '25',
|
||||
pid: '2',
|
||||
index: '/watermark',
|
||||
title: '水印',
|
||||
},
|
||||
{
|
||||
id: '26',
|
||||
pid: '2',
|
||||
index: '/tour',
|
||||
title: '分布引导',
|
||||
},
|
||||
{
|
||||
id: '27',
|
||||
pid: '2',
|
||||
index: '/steps',
|
||||
title: '步骤条',
|
||||
},
|
||||
{
|
||||
id: '28',
|
||||
pid: '2',
|
||||
index: '/statistic',
|
||||
title: '统计',
|
||||
},
|
||||
{
|
||||
id: '29',
|
||||
pid: '3',
|
||||
index: '29',
|
||||
title: '三级菜单',
|
||||
children: [
|
||||
{
|
||||
id: '291',
|
||||
pid: '29',
|
||||
index: '/editor',
|
||||
title: '富文本编辑器',
|
||||
},
|
||||
{
|
||||
id: '292',
|
||||
pid: '29',
|
||||
index: '/markdown',
|
||||
title: 'markdown编辑器',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: '表格',
|
||||
index: '3',
|
||||
icon: 'Calendar',
|
||||
children: [
|
||||
{
|
||||
id: '31',
|
||||
pid: '3',
|
||||
index: '/table',
|
||||
title: '基础表格',
|
||||
},
|
||||
{
|
||||
id: '32',
|
||||
pid: '3',
|
||||
index: '/table-editor',
|
||||
title: '可编辑表格',
|
||||
},
|
||||
{
|
||||
id: '33',
|
||||
pid: '3',
|
||||
index: '/import',
|
||||
title: '导入Excel',
|
||||
},
|
||||
{
|
||||
id: '34',
|
||||
pid: '3',
|
||||
index: '/export',
|
||||
title: '导出Excel',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
icon: 'PieChart',
|
||||
index: '4',
|
||||
title: '图表',
|
||||
children: [
|
||||
{
|
||||
id: '41',
|
||||
pid: '4',
|
||||
index: '/schart',
|
||||
title: 'schart图表',
|
||||
},
|
||||
{
|
||||
id: '42',
|
||||
pid: '4',
|
||||
index: '/echarts',
|
||||
title: 'echarts图表',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
icon: 'Guide',
|
||||
index: '/icon',
|
||||
title: '图标',
|
||||
permiss: '5',
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
icon: 'Brush',
|
||||
index: '/theme',
|
||||
title: '主题',
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
icon: 'DocumentAdd',
|
||||
index: '6',
|
||||
title: '附加页面',
|
||||
children: [
|
||||
{
|
||||
id: '61',
|
||||
pid: '6',
|
||||
index: '/ucenter',
|
||||
title: '个人中心',
|
||||
},
|
||||
{
|
||||
id: '62',
|
||||
pid: '6',
|
||||
index: '/login',
|
||||
title: '登录',
|
||||
},
|
||||
{
|
||||
id: '63',
|
||||
pid: '6',
|
||||
index: '/register',
|
||||
title: '注册',
|
||||
},
|
||||
{
|
||||
id: '64',
|
||||
pid: '6',
|
||||
index: '/reset-pwd',
|
||||
title: '重设密码',
|
||||
},
|
||||
{
|
||||
id: '65',
|
||||
pid: '6',
|
||||
index: '/403',
|
||||
title: '403',
|
||||
},
|
||||
{
|
||||
id: '66',
|
||||
pid: '6',
|
||||
index: '/404',
|
||||
title: '404',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -1,56 +0,0 @@
|
|||
<template>
|
||||
<div class="error-page">
|
||||
<div class="error-code">4<span>0</span>3</div>
|
||||
<div class="error-desc">啊哦~ 你没有权限访问该页面哦</div>
|
||||
<div class="error-handle">
|
||||
<router-link to="/">
|
||||
<el-button type="primary" size="large">返回首页</el-button>
|
||||
</router-link>
|
||||
<el-button class="error-btn" type="primary" size="large" @click="goBack">返回上一页</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
goBack(){
|
||||
this.$router.go(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.error-page{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f3f3f3;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.error-code{
|
||||
line-height: 1;
|
||||
font-size: 250px;
|
||||
font-weight: bolder;
|
||||
color: #f02d2d;
|
||||
}
|
||||
.error-code span{
|
||||
color: #00a854;
|
||||
}
|
||||
.error-desc{
|
||||
font-size: 30px;
|
||||
color: #777;
|
||||
}
|
||||
.error-handle{
|
||||
margin-top: 30px;
|
||||
padding-bottom: 200px;
|
||||
}
|
||||
.error-btn{
|
||||
margin-left: 100px;
|
||||
}
|
||||
</style>
|
|
@ -1,56 +0,0 @@
|
|||
<template>
|
||||
<div class="error-page">
|
||||
<div class="error-code">4<span>0</span>4</div>
|
||||
<div class="error-desc">啊哦~ 你所访问的页面不存在</div>
|
||||
<div class="error-handle">
|
||||
<router-link to="/">
|
||||
<el-button type="primary" size="large">返回首页</el-button>
|
||||
</router-link>
|
||||
<el-button class="error-btn" type="primary" size="large" @click="goBack">返回上一页</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
goBack(){
|
||||
this.$router.go(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.error-page{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f3f3f3;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.error-code{
|
||||
line-height: 1;
|
||||
font-size: 250px;
|
||||
font-weight: bolder;
|
||||
color: #2d8cf0;
|
||||
}
|
||||
.error-code span{
|
||||
color: #00a854;
|
||||
}
|
||||
.error-desc{
|
||||
font-size: 30px;
|
||||
color: #777;
|
||||
}
|
||||
.error-handle{
|
||||
margin-top: 30px;
|
||||
padding-bottom: 200px;
|
||||
}
|
||||
.error-btn{
|
||||
margin-left: 100px;
|
||||
}
|
||||
</style>
|
|
@ -1,109 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-pie-chart"></i> schart图表</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="plugins-tips">
|
||||
vue-schart:vue.js封装sChart.js的图表组件。
|
||||
访问地址:<a href="https://github.com/lin-xin/vue-schart" target="_blank">vue-schart</a>
|
||||
</div>
|
||||
<div class="schart-box">
|
||||
<div class="content-title">柱状图</div>
|
||||
<schart class="schart" canvasId="bar" :data="data1" type="bar" :options="options1"></schart>
|
||||
</div>
|
||||
<div class="schart-box">
|
||||
<div class="content-title">折线图</div>
|
||||
<schart class="schart" canvasId="line" :data="data1" type="line" :options="options2"></schart>
|
||||
</div>
|
||||
<div class="schart-box">
|
||||
<div class="content-title">饼状图</div>
|
||||
<schart class="schart" canvasId="pie" :data="data2" type="pie" :options="options3"></schart>
|
||||
</div>
|
||||
<div class="schart-box">
|
||||
<div class="content-title">环形图</div>
|
||||
<schart class="schart" canvasId="ring" :data="data2" type="ring" :options="options4"></schart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Schart from 'vue-schart';
|
||||
export default {
|
||||
name: 'basecharts',
|
||||
components: {
|
||||
Schart
|
||||
},
|
||||
data: () => ({
|
||||
data1:[
|
||||
{name:'2012',value:1141},
|
||||
{name:'2013',value:1499},
|
||||
{name:'2014',value:2260},
|
||||
{name:'2015',value:1170},
|
||||
{name:'2016',value:970},
|
||||
{name:'2017',value:1450}
|
||||
],
|
||||
data2 : [
|
||||
{name:'短袖',value:1200},
|
||||
{name:'休闲裤',value:1222},
|
||||
{name:'连衣裙',value:1283},
|
||||
{name:'外套',value:1314},
|
||||
{name:'羽绒服',value:2314}
|
||||
],
|
||||
options1: {
|
||||
title: '某商店近年营业总额',
|
||||
autoWidth: true, // 设置宽高自适应
|
||||
showValue: false,
|
||||
bgColor: '#F9EFCC',
|
||||
fillColor: '#00887C',
|
||||
contentColor: 'rgba(46,199,201,0.3)',
|
||||
yEqual: 7
|
||||
},
|
||||
options2: {
|
||||
title: '某商店近年营业总额',
|
||||
bgColor: '#D5E4EB',
|
||||
titleColor: '#00887C',
|
||||
fillColor: 'red',
|
||||
contentColor: 'rgba(46,199,201,0.3)'
|
||||
},
|
||||
options3: {
|
||||
title: '某商店各商品年度销量',
|
||||
bgColor: '#829dca',
|
||||
titleColor: '#ffffff',
|
||||
legendColor: '#ffffff',
|
||||
radius: 120
|
||||
},
|
||||
options4: {
|
||||
title: '某商店各商品年度销量',
|
||||
bgColor: '#829daa',
|
||||
titleColor: '#ffffff',
|
||||
legendColor: '#ffffff',
|
||||
radius: 120,
|
||||
innerRadius:80
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.schart-box{
|
||||
display: inline-block;
|
||||
margin: 20px;
|
||||
}
|
||||
.schart{
|
||||
width: 500px;
|
||||
height: 400px;
|
||||
}
|
||||
.content-title{
|
||||
clear: both;
|
||||
font-weight: 400;
|
||||
line-height: 50px;
|
||||
margin: 10px 0;
|
||||
font-size: 22px;
|
||||
color: #1f2f3d;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,141 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-lx-calendar"></i> 表单</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>基本表单</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="form-box">
|
||||
<el-form ref="form" :model="form" label-width="80px">
|
||||
<el-form-item label="表单名称">
|
||||
<el-input v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择器">
|
||||
<el-select v-model="form.region" placeholder="请选择">
|
||||
<el-option key="bbk" label="步步高" value="bbk"></el-option>
|
||||
<el-option key="xtc" label="小天才" value="xtc"></el-option>
|
||||
<el-option key="imoo" label="imoo" value="imoo"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="日期时间">
|
||||
<el-col :span="11">
|
||||
<el-date-picker type="date" placeholder="选择日期" v-model="form.date1" style="width: 100%;"></el-date-picker>
|
||||
</el-col>
|
||||
<el-col class="line" :span="2">-</el-col>
|
||||
<el-col :span="11">
|
||||
<el-time-picker placeholder="选择时间" v-model="form.date2" style="width: 100%;"></el-time-picker>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item label="城市级联">
|
||||
<el-cascader :options="options" v-model="form.options"></el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择开关">
|
||||
<el-switch v-model="form.delivery"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="多选框">
|
||||
<el-checkbox-group v-model="form.type">
|
||||
<el-checkbox label="步步高" name="type"></el-checkbox>
|
||||
<el-checkbox label="小天才" name="type"></el-checkbox>
|
||||
<el-checkbox label="imoo" name="type"></el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="单选框">
|
||||
<el-radio-group v-model="form.resource">
|
||||
<el-radio label="步步高"></el-radio>
|
||||
<el-radio label="小天才"></el-radio>
|
||||
<el-radio label="imoo"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="文本框">
|
||||
<el-input type="textarea" rows="5" v-model="form.desc"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">表单提交</el-button>
|
||||
<el-button>取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'baseform',
|
||||
data: function(){
|
||||
return {
|
||||
options:[
|
||||
{
|
||||
value: 'guangdong',
|
||||
label: '广东省',
|
||||
children: [
|
||||
{
|
||||
value: 'guangzhou',
|
||||
label: '广州市',
|
||||
children: [
|
||||
{
|
||||
value: 'tianhe',
|
||||
label: '天河区'
|
||||
},
|
||||
{
|
||||
value: 'haizhu',
|
||||
label: '海珠区'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
value: 'dongguan',
|
||||
label: '东莞市',
|
||||
children: [
|
||||
{
|
||||
value: 'changan',
|
||||
label: '长安镇'
|
||||
},
|
||||
{
|
||||
value: 'humen',
|
||||
label: '虎门镇'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
value: 'hunan',
|
||||
label: '湖南省',
|
||||
children: [
|
||||
{
|
||||
value: 'changsha',
|
||||
label: '长沙市',
|
||||
children: [
|
||||
{
|
||||
value: 'yuelu',
|
||||
label: '岳麓区'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
form: {
|
||||
name: '',
|
||||
region: '',
|
||||
date1: '',
|
||||
date2: '',
|
||||
delivery: true,
|
||||
type: ['步步高'],
|
||||
resource: '小天才',
|
||||
desc: '',
|
||||
options: []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
this.$message.success('提交成功!');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,215 +0,0 @@
|
|||
<template>
|
||||
<div class="table">
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-lx-cascades"></i> 基础表格</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="handle-box">
|
||||
<el-button type="primary" icon="delete" class="handle-del mr10" @click="delAll">批量删除</el-button>
|
||||
<el-select v-model="select_cate" 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="select_word" placeholder="筛选关键词" class="handle-input mr10"></el-input>
|
||||
<el-button type="primary" icon="search" @click="search">搜索</el-button>
|
||||
</div>
|
||||
<el-table :data="data" border class="table" ref="multipleTable" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center"></el-table-column>
|
||||
<el-table-column prop="date" label="日期" sortable width="150">
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="姓名" width="120">
|
||||
</el-table-column>
|
||||
<el-table-column prop="address" label="地址" :formatter="formatter">
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" align="center">
|
||||
<template slot-scope="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 @current-change="handleCurrentChange" layout="prev, pager, next" :total="1000">
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑弹出框 -->
|
||||
<el-dialog title="编辑" :visible.sync="editVisible" width="30%">
|
||||
<el-form ref="form" :model="form" label-width="50px">
|
||||
<el-form-item label="日期">
|
||||
<el-date-picker type="date" placeholder="选择日期" v-model="form.date" value-format="yyyy-MM-dd" style="width: 100%;"></el-date-picker>
|
||||
</el-form-item>
|
||||
<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>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="saveEdit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 删除提示框 -->
|
||||
<el-dialog title="提示" :visible.sync="delVisible" width="300px" center>
|
||||
<div class="del-dialog-cnt">删除不可恢复,是否确定删除?</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="delVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="deleteRow">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'basetable',
|
||||
data() {
|
||||
return {
|
||||
url: './vuetable.json',
|
||||
tableData: [],
|
||||
cur_page: 1,
|
||||
multipleSelection: [],
|
||||
select_cate: '',
|
||||
select_word: '',
|
||||
del_list: [],
|
||||
is_search: false,
|
||||
editVisible: false,
|
||||
delVisible: false,
|
||||
form: {
|
||||
name: '',
|
||||
date: '',
|
||||
address: ''
|
||||
},
|
||||
idx: -1
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getData();
|
||||
},
|
||||
computed: {
|
||||
data() {
|
||||
return this.tableData.filter((d) => {
|
||||
let is_del = false;
|
||||
for (let i = 0; i < this.del_list.length; i++) {
|
||||
if (d.name === this.del_list[i].name) {
|
||||
is_del = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_del) {
|
||||
if (d.address.indexOf(this.select_cate) > -1 &&
|
||||
(d.name.indexOf(this.select_word) > -1 ||
|
||||
d.address.indexOf(this.select_word) > -1)
|
||||
) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 分页导航
|
||||
handleCurrentChange(val) {
|
||||
this.cur_page = val;
|
||||
this.getData();
|
||||
},
|
||||
// 获取 easy-mock 的模拟数据
|
||||
getData() {
|
||||
// 开发环境使用 easy-mock 数据,正式环境使用 json 文件
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
this.url = '/ms/table/list';
|
||||
};
|
||||
this.$axios.post(this.url, {
|
||||
page: this.cur_page
|
||||
}).then((res) => {
|
||||
this.tableData = res.data.list;
|
||||
})
|
||||
},
|
||||
search() {
|
||||
this.is_search = true;
|
||||
},
|
||||
formatter(row, column) {
|
||||
return row.address;
|
||||
},
|
||||
filterTag(value, row) {
|
||||
return row.tag === value;
|
||||
},
|
||||
handleEdit(index, row) {
|
||||
this.idx = index;
|
||||
const item = this.tableData[index];
|
||||
this.form = {
|
||||
name: item.name,
|
||||
date: item.date,
|
||||
address: item.address
|
||||
}
|
||||
this.editVisible = true;
|
||||
},
|
||||
handleDelete(index, row) {
|
||||
this.idx = index;
|
||||
this.delVisible = true;
|
||||
},
|
||||
delAll() {
|
||||
const length = this.multipleSelection.length;
|
||||
let str = '';
|
||||
this.del_list = this.del_list.concat(this.multipleSelection);
|
||||
for (let i = 0; i < length; i++) {
|
||||
str += this.multipleSelection[i].name + ' ';
|
||||
}
|
||||
this.$message.error('删除了' + str);
|
||||
this.multipleSelection = [];
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val;
|
||||
},
|
||||
// 保存编辑
|
||||
saveEdit() {
|
||||
this.$set(this.tableData, this.idx, this.form);
|
||||
this.editVisible = false;
|
||||
this.$message.success(`修改第 ${this.idx+1} 行成功`);
|
||||
},
|
||||
// 确定删除
|
||||
deleteRow(){
|
||||
this.tableData.splice(this.idx, 1);
|
||||
this.$message.success('删除成功');
|
||||
this.delVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.handle-box {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.handle-select {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.handle-input {
|
||||
width: 300px;
|
||||
display: inline-block;
|
||||
}
|
||||
.del-dialog-cnt{
|
||||
font-size: 16px;
|
||||
text-align: center
|
||||
}
|
||||
.table{
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
.red{
|
||||
color: #ff0000;
|
||||
}
|
||||
.mr10{
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
|
@ -1,343 +0,0 @@
|
|||
<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>2018-01-01</span></div>
|
||||
<div class="user-info-list">上次登录地点:<span>东莞</span></div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" style="height:252px;">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>语言详情</span>
|
||||
</div>
|
||||
Vue
|
||||
<el-progress :percentage="71.3" color="#42b983"></el-progress>
|
||||
JavaScript
|
||||
<el-progress :percentage="24.1" color="#f1e05a"></el-progress>
|
||||
CSS
|
||||
<el-progress :percentage="3.7"></el-progress>
|
||||
HTML
|
||||
<el-progress :percentage="0.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-lx-people 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-lx-notice 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-lx-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;">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>待办事项</span>
|
||||
<el-button style="float: right; padding: 3px 0" type="text">添加</el-button>
|
||||
</div>
|
||||
<el-table :data="todoList" :show-header="false" height="304" style="width: 100%;font-size:14px;">
|
||||
<el-table-column width="40">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.status"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column>
|
||||
<template slot-scope="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 slot-scope="scope">
|
||||
<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" :data="data" type="bar" :options="options"></schart>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card shadow="hover">
|
||||
<schart ref="line" class="schart" canvasId="line" :data="data" type="line" :options="options2"></schart>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Schart from 'vue-schart';
|
||||
import bus from '../common/bus';
|
||||
export default {
|
||||
name: 'dashboard',
|
||||
data() {
|
||||
return {
|
||||
name: localStorage.getItem('ms_username'),
|
||||
todoList: [{
|
||||
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,
|
||||
}
|
||||
],
|
||||
data: [{
|
||||
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
|
||||
}
|
||||
],
|
||||
options: {
|
||||
title: '最近七天每天的用户访问量',
|
||||
showValue: false,
|
||||
fillColor: 'rgb(45, 140, 240)',
|
||||
bottomPadding: 30,
|
||||
topPadding: 30
|
||||
},
|
||||
options2: {
|
||||
title: '最近七天用户访问趋势',
|
||||
fillColor: '#FC6FA1',
|
||||
axisColor: '#008ACD',
|
||||
contentColor: '#EEEEEE',
|
||||
bgColor: '#F5F8FD',
|
||||
bottomPadding: 30,
|
||||
topPadding: 30
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Schart
|
||||
},
|
||||
computed: {
|
||||
role() {
|
||||
return this.name === 'admin' ? '超级管理员' : '普通用户';
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.handleListener();
|
||||
this.changeDate();
|
||||
},
|
||||
activated(){
|
||||
this.handleListener();
|
||||
},
|
||||
deactivated(){
|
||||
window.removeEventListener('resize', this.renderChart);
|
||||
bus.$off('collapse', this.handleBus);
|
||||
},
|
||||
methods: {
|
||||
changeDate(){
|
||||
const now = new Date().getTime();
|
||||
this.data.forEach((item, index) => {
|
||||
const date = new Date(now - (6 - index) * 86400000);
|
||||
item.name = `${date.getFullYear()}/${date.getMonth()+1}/${date.getDate()}`
|
||||
})
|
||||
},
|
||||
handleListener(){
|
||||
bus.$on('collapse', this.handleBus);
|
||||
// 调用renderChart方法对图表进行重新渲染
|
||||
window.addEventListener('resize', this.renderChart)
|
||||
},
|
||||
handleBus(msg){
|
||||
setTimeout(() => {
|
||||
this.renderChart()
|
||||
}, 300);
|
||||
},
|
||||
renderChart(){
|
||||
this.$refs.bar.renderChart();
|
||||
this.$refs.line.renderChart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</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>
|
|
@ -1,36 +0,0 @@
|
|||
<template>
|
||||
<section class="main">
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-rank"></i> 拖拽组件</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>拖拽弹框</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<p>通过指令 v-dialogDrag 使 Dialog 对话框具有可拖拽的功能。</p>
|
||||
<br>
|
||||
<el-button type="primary" @click="visible = true;">点我弹框</el-button>
|
||||
</div>
|
||||
<el-dialog v-dialogDrag title="拖拽弹框" center :visible.sync="visible" width="30%">
|
||||
我是一个可以拖拽的对话框!
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="visible = false">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
|
@ -1,174 +0,0 @@
|
|||
<template>
|
||||
<section class="main">
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-rank"></i> 拖拽组件</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>拖拽排序</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="plugins-tips">
|
||||
Vue.Draggable:基于 Sortable.js 的 Vue 拖拽组件。
|
||||
访问地址:<a href="https://github.com/SortableJS/Vue.Draggable" target="_blank">Vue.Draggable</a>
|
||||
</div>
|
||||
<div class="drag-box">
|
||||
<div class="drag-box-item">
|
||||
<div class="item-title">todo</div>
|
||||
<draggable v-model="todo" @remove="removeHandle" :options="dragOptions">
|
||||
<transition-group tag="div" id="todo" class="item-ul">
|
||||
<div v-for="item in todo" class="drag-list" :key="item.id">
|
||||
{{item.content}}
|
||||
</div>
|
||||
</transition-group>
|
||||
</draggable>
|
||||
</div>
|
||||
<div class="drag-box-item">
|
||||
<div class="item-title">doing</div>
|
||||
<draggable v-model="doing" @remove="removeHandle" :options="dragOptions">
|
||||
<transition-group tag="div" id="doing" class="item-ul">
|
||||
<div v-for="item in doing" class="drag-list" :key="item.id">
|
||||
{{item.content}}
|
||||
</div>
|
||||
</transition-group>
|
||||
</draggable>
|
||||
</div>
|
||||
<div class="drag-box-item">
|
||||
<div class="item-title">done</div>
|
||||
<draggable v-model="done" @remove="removeHandle" :options="dragOptions">
|
||||
<transition-group tag="div" id="done" class="item-ul">
|
||||
<div v-for="item in done" class="drag-list" :key="item.id">
|
||||
{{item.content}}
|
||||
</div>
|
||||
</transition-group>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
export default {
|
||||
name: 'draglist',
|
||||
data() {
|
||||
return {
|
||||
dragOptions:{
|
||||
animation: 120,
|
||||
scroll: true,
|
||||
group: 'sortlist',
|
||||
ghostClass: 'ghost-style'
|
||||
},
|
||||
todo: [
|
||||
{
|
||||
id: 1,
|
||||
content: '开发图表组件'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
content: '开发拖拽组件'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
content: '开发权限测试组件'
|
||||
}
|
||||
],
|
||||
doing: [
|
||||
{
|
||||
id: 1,
|
||||
content: '开发登录注册页面'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
content: '开发头部组件'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
content: '开发表格相关组件'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
content: '开发表单相关组件'
|
||||
}
|
||||
],
|
||||
done:[
|
||||
{
|
||||
id: 1,
|
||||
content: '初始化项目,生成工程目录,完成相关配置'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
content: '开发项目整体框架'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
components:{
|
||||
draggable
|
||||
},
|
||||
methods: {
|
||||
removeHandle(event){
|
||||
console.log(event);
|
||||
this.$message.success(`从 ${event.from.id} 移动到 ${event.to.id} `);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.drag-box{
|
||||
display: flex;
|
||||
user-select: none;
|
||||
}
|
||||
.drag-box-item {
|
||||
flex: 1;
|
||||
max-width: 330px;
|
||||
min-width: 300px;
|
||||
background-color: #eff1f5;
|
||||
margin-right: 16px;
|
||||
border-radius: 6px;
|
||||
border: 1px #e1e4e8 solid;
|
||||
}
|
||||
.item-title{
|
||||
padding: 8px 8px 8px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #24292e;
|
||||
font-weight: 600;
|
||||
}
|
||||
.item-ul{
|
||||
padding: 0 8px 8px;
|
||||
height: 500px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.item-ul::-webkit-scrollbar{
|
||||
width: 0;
|
||||
}
|
||||
.drag-list {
|
||||
border: 1px #e1e4e8 solid;
|
||||
padding: 10px;
|
||||
margin: 5px 0 10px;
|
||||
list-style: none;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
-webkit-transition: border .3s ease-in;
|
||||
transition: border .3s ease-in;
|
||||
}
|
||||
.drag-list:hover {
|
||||
border: 1px solid #20a0ff;
|
||||
}
|
||||
.drag-title {
|
||||
font-weight: 400;
|
||||
line-height: 25px;
|
||||
margin: 10px 0;
|
||||
font-size: 22px;
|
||||
color: #1f2f3d;
|
||||
}
|
||||
.ghost-style{
|
||||
display: block;
|
||||
color: transparent;
|
||||
border-style: dashed
|
||||
}
|
||||
</style>
|
|
@ -1,46 +0,0 @@
|
|||
<template>
|
||||
<section class="main">
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-lx-global"></i> {{$t('i18n.breadcrumb')}}</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<span>{{$t('i18n.tips')}}</span>
|
||||
<el-button type="primary" @click="$i18n.locale = $i18n.locale === 'zh'?'en':'zh';">{{$t('i18n.btn')}}</el-button>
|
||||
<div class="list">
|
||||
<h2>{{$t('i18n.title1')}}</h2>
|
||||
<p>{{$t('i18n.p1')}}</p>
|
||||
<p>{{$t('i18n.p2')}}</p>
|
||||
<p>{{$t('i18n.p3')}}</p>
|
||||
</div>
|
||||
<h2>{{$t('i18n.title2')}}</h2>
|
||||
<div>
|
||||
<i18n path="i18n.info" tag="p">
|
||||
<a place="action" href="https://element.eleme.cn/2.0/#/zh-CN/component/i18n">{{ $t('i18n.value') }}</a>
|
||||
</i18n>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.list{
|
||||
padding: 30px 0;
|
||||
}
|
||||
.list p{
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
a{
|
||||
color: #409eff;
|
||||
}
|
||||
</style>
|
|
@ -1,225 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-lx-emoji"></i> 自定义图标</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h2>使用方法</h2>
|
||||
<p style="line-height: 50px;">
|
||||
直接通过设置类名为 el-icon-lx-iconName 来使用即可。例如:(共{{iconList.length}}个图标)
|
||||
</p>
|
||||
<p class="example-p">
|
||||
<i class="el-icon-lx-redpacket_fill" style="font-size: 30px;color: #ff5900"></i>
|
||||
<span><i class="el-icon-lx-redpacket_fill"></i></span>
|
||||
</p>
|
||||
<p class="example-p">
|
||||
<i class="el-icon-lx-weibo" style="font-size: 30px;color:#fd5656"></i>
|
||||
<span><i class="el-icon-lx-weibo"></i></span>
|
||||
</p>
|
||||
<p class="example-p">
|
||||
<i class="el-icon-lx-emojifill" style="font-size: 30px;color: #ffc300"></i>
|
||||
<span><i class="el-icon-lx-emojifill"></i></span>
|
||||
</p>
|
||||
<br>
|
||||
<h2>图标</h2>
|
||||
<div class="search-box">
|
||||
<el-input class="search" size="large" v-model="keyword" clearable placeholder="请输入图标名称"></el-input>
|
||||
</div>
|
||||
<ul>
|
||||
<li class="icon-li" v-for="(item,index) in list" :key="index">
|
||||
<div class="icon-li-content">
|
||||
<i :class="`el-icon-lx-${item}`"></i>
|
||||
<span>{{item}}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: function(){
|
||||
return {
|
||||
keyword: '',
|
||||
iconList: [
|
||||
'attentionforbid',
|
||||
'attentionforbidfill',
|
||||
'attention',
|
||||
'attentionfill',
|
||||
'tag',
|
||||
'tagfill',
|
||||
'people',
|
||||
'peoplefill',
|
||||
'notice',
|
||||
'noticefill',
|
||||
'mobile',
|
||||
'mobilefill',
|
||||
'voice',
|
||||
'voicefill',
|
||||
'unlock',
|
||||
'lock',
|
||||
'home',
|
||||
'homefill',
|
||||
'delete',
|
||||
'deletefill',
|
||||
'notification',
|
||||
'notificationfill',
|
||||
'notificationforbidfill',
|
||||
'like',
|
||||
'likefill',
|
||||
'comment',
|
||||
'commentfill',
|
||||
'camera',
|
||||
'camerafill',
|
||||
'warn',
|
||||
'warnfill',
|
||||
'time',
|
||||
'timefill',
|
||||
'location',
|
||||
'locationfill',
|
||||
'favor',
|
||||
'favorfill',
|
||||
'skin',
|
||||
'skinfill',
|
||||
'news',
|
||||
'newsfill',
|
||||
'record',
|
||||
'recordfill',
|
||||
'emoji',
|
||||
'emojifill',
|
||||
'message',
|
||||
'messagefill',
|
||||
'goods',
|
||||
'goodsfill',
|
||||
'crown',
|
||||
'crownfill',
|
||||
'move',
|
||||
'add',
|
||||
'hot',
|
||||
'hotfill',
|
||||
'service',
|
||||
'servicefill',
|
||||
'present',
|
||||
'presentfill',
|
||||
'pic',
|
||||
'picfill',
|
||||
'rank',
|
||||
'rankfill',
|
||||
'male',
|
||||
'female',
|
||||
'down',
|
||||
'top',
|
||||
'recharge',
|
||||
'rechargefill',
|
||||
'forward',
|
||||
'forwardfill',
|
||||
'info',
|
||||
'infofill',
|
||||
'redpacket',
|
||||
'redpacket_fill',
|
||||
'roundadd',
|
||||
'roundaddfill',
|
||||
'friendadd',
|
||||
'friendaddfill',
|
||||
'cart',
|
||||
'cartfill',
|
||||
'more',
|
||||
'moreandroid',
|
||||
'back',
|
||||
'right',
|
||||
'shop',
|
||||
'shopfill',
|
||||
'question',
|
||||
'questionfill',
|
||||
'roundclose',
|
||||
'roundclosefill',
|
||||
'roundcheck',
|
||||
'roundcheckfill',
|
||||
'global',
|
||||
'mail',
|
||||
'punch',
|
||||
'exit',
|
||||
'upload',
|
||||
'read',
|
||||
'file',
|
||||
'link',
|
||||
'full',
|
||||
'group',
|
||||
'friend',
|
||||
'profile',
|
||||
'addressbook',
|
||||
'calendar',
|
||||
'text',
|
||||
'copy',
|
||||
'share',
|
||||
'wifi',
|
||||
'vipcard',
|
||||
'weibo',
|
||||
'remind',
|
||||
'refresh',
|
||||
'filter',
|
||||
'settings',
|
||||
'scan',
|
||||
'qrcode',
|
||||
'cascades',
|
||||
'apps',
|
||||
'sort',
|
||||
'searchlist',
|
||||
'search',
|
||||
'edit'
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
list(){
|
||||
return this.iconList.filter((item) => {
|
||||
return item.indexOf(this.keyword) !== -1;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.example-p{
|
||||
height: 45px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.search-box{
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.search{
|
||||
width: 300px;
|
||||
}
|
||||
ul,li{
|
||||
list-style: none;
|
||||
}
|
||||
.icon-li{
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
.icon-li-content{
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.icon-li-content i{
|
||||
font-size: 36px;
|
||||
color: #606266;
|
||||
}
|
||||
.icon-li-content span{
|
||||
margin-top: 10px;
|
||||
color: #787878;
|
||||
}
|
||||
</style>
|
|
@ -1,101 +0,0 @@
|
|||
<template>
|
||||
<div class="login-wrap">
|
||||
<div class="ms-login">
|
||||
<div class="ms-title">后台管理系统</div>
|
||||
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="0px" class="ms-content">
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="ruleForm.username" placeholder="username">
|
||||
<el-button slot="prepend" icon="el-icon-lx-people"></el-button>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input type="password" placeholder="password" v-model="ruleForm.password" @keyup.enter.native="submitForm('ruleForm')">
|
||||
<el-button slot="prepend" icon="el-icon-lx-lock"></el-button>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<div class="login-btn">
|
||||
<el-button type="primary" @click="submitForm('ruleForm')">登录</el-button>
|
||||
</div>
|
||||
<p class="login-tips">Tips : 用户名和密码随便填。</p>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: function(){
|
||||
return {
|
||||
ruleForm: {
|
||||
username: 'admin',
|
||||
password: '123123'
|
||||
},
|
||||
rules: {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submitForm(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
localStorage.setItem('ms_username',this.ruleForm.username);
|
||||
this.$router.push('/');
|
||||
} else {
|
||||
console.log('error submit!!');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</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>
|
|
@ -1,67 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-lx-calendar"></i> 表单</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>markdown编辑器</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="plugins-tips">
|
||||
mavonEditor:基于Vue的markdown编辑器。
|
||||
访问地址:<a href="https://github.com/hinesboy/mavonEditor" target="_blank">mavonEditor</a>
|
||||
</div>
|
||||
<mavon-editor v-model="content" ref="md" @imgAdd="$imgAdd" @change="change" style="min-height: 600px"/>
|
||||
<el-button class="editor-btn" type="primary" @click="submit">提交</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mavonEditor } from 'mavon-editor'
|
||||
import 'mavon-editor/dist/css/index.css'
|
||||
export default {
|
||||
name: 'markdown',
|
||||
data: function(){
|
||||
return {
|
||||
content:'',
|
||||
html:'',
|
||||
configs: {
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
mavonEditor
|
||||
},
|
||||
methods: {
|
||||
// 将图片上传到服务器,返回地址替换到md中
|
||||
$imgAdd(pos, $file){
|
||||
var formdata = new FormData();
|
||||
formdata.append('file', $file);
|
||||
// 这里没有服务器供大家尝试,可将下面上传接口替换为你自己的服务器接口
|
||||
this.$axios({
|
||||
url: '/common/upload',
|
||||
method: 'post',
|
||||
data: formdata,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
}).then((url) => {
|
||||
this.$refs.md.$img2Url(pos, url);
|
||||
})
|
||||
},
|
||||
change(value, render){
|
||||
// render 为 markdown 解析后的结果
|
||||
this.html = render;
|
||||
},
|
||||
submit(){
|
||||
console.log(this.content);
|
||||
console.log(this.html);
|
||||
this.$message.success('提交成功!');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.editor-btn{
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
|
@ -1,38 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-lx-warn"></i> 权限测试</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1>管理员权限页面</h1>
|
||||
<p>只有用 admin 账号登录的才拥有管理员权限,才能进到这个页面,其他账号想进来都会跳到403页面,重新用管理员账号登录才有权限。</p>
|
||||
<p>想尝试一下,请<router-link to="/login" class="logout">退出登录</router-link>,随便输入个账号名,再进来试试看。</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: function(){
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1{
|
||||
text-align: center;
|
||||
margin: 30px 0;
|
||||
}
|
||||
p{
|
||||
line-height: 30px;
|
||||
margin-bottom: 10px;
|
||||
text-indent: 2em;
|
||||
}
|
||||
.logout{
|
||||
color: #409EFF;
|
||||
}
|
||||
</style>
|
|
@ -1,129 +0,0 @@
|
|||
<template>
|
||||
<div class="">
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-lx-copy"></i> tab选项卡</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<el-tabs v-model="message">
|
||||
<el-tab-pane :label="`未读消息(${unread.length})`" name="first">
|
||||
<el-table :data="unread" :show-header="false" style="width: 100%">
|
||||
<el-table-column>
|
||||
<template slot-scope="scope">
|
||||
<span class="message-title">{{scope.row.title}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="date" width="180"></el-table-column>
|
||||
<el-table-column width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="small" @click="handleRead(scope.$index)">标为已读</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="handle-row">
|
||||
<el-button type="primary">全部标为已读</el-button>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="`已读消息(${read.length})`" name="second">
|
||||
<template v-if="message === 'second'">
|
||||
<el-table :data="read" :show-header="false" style="width: 100%">
|
||||
<el-table-column>
|
||||
<template slot-scope="scope">
|
||||
<span class="message-title">{{scope.row.title}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="date" width="150"></el-table-column>
|
||||
<el-table-column width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="danger" @click="handleDel(scope.$index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="handle-row">
|
||||
<el-button type="danger">删除全部</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="`回收站(${recycle.length})`" name="third">
|
||||
<template v-if="message === 'third'">
|
||||
<el-table :data="recycle" :show-header="false" style="width: 100%">
|
||||
<el-table-column>
|
||||
<template slot-scope="scope">
|
||||
<span class="message-title">{{scope.row.title}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="date" width="150"></el-table-column>
|
||||
<el-table-column width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-button @click="handleRestore(scope.$index)">还原</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="handle-row">
|
||||
<el-button type="danger">清空回收站</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'tabs',
|
||||
data() {
|
||||
return {
|
||||
message: 'first',
|
||||
showHeader: false,
|
||||
unread: [{
|
||||
date: '2018-04-19 20:00:00',
|
||||
title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护',
|
||||
},{
|
||||
date: '2018-04-19 21:00:00',
|
||||
title: '今晚12点整发大红包,先到先得',
|
||||
}],
|
||||
read: [{
|
||||
date: '2018-04-19 20:00:00',
|
||||
title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'
|
||||
}],
|
||||
recycle: [{
|
||||
date: '2018-04-19 20:00:00',
|
||||
title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'
|
||||
}]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleRead(index) {
|
||||
const item = this.unread.splice(index, 1);
|
||||
console.log(item);
|
||||
this.read = item.concat(this.read);
|
||||
},
|
||||
handleDel(index) {
|
||||
const item = this.read.splice(index, 1);
|
||||
this.recycle = item.concat(this.recycle);
|
||||
},
|
||||
handleRestore(index) {
|
||||
const item = this.recycle.splice(index, 1);
|
||||
this.read = item.concat(this.read);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
unreadNum(){
|
||||
return this.unread.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.message-title{
|
||||
cursor: pointer;
|
||||
}
|
||||
.handle-row{
|
||||
margin-top: 30px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-lx-calendar"></i> 表单</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>图片上传</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="content-title">支持拖拽</div>
|
||||
<div class="plugins-tips">
|
||||
Element UI自带上传组件。
|
||||
访问地址:<a href="http://element.eleme.io/#/zh-CN/component/upload" target="_blank">Element UI Upload</a>
|
||||
</div>
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
drag
|
||||
action="/api/posts/"
|
||||
multiple>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
|
||||
</el-upload>
|
||||
<div class="content-title">支持裁剪</div>
|
||||
<div class="plugins-tips">
|
||||
vue-cropperjs:一个封装了 cropperjs 的 Vue 组件。
|
||||
访问地址:<a href="https://github.com/Agontuk/vue-cropperjs" target="_blank">vue-cropperjs</a>
|
||||
</div>
|
||||
<div class="crop-demo">
|
||||
<img :src="cropImg" class="pre-img">
|
||||
<div class="crop-demo-btn">选择图片
|
||||
<input class="crop-input" type="file" name="image" accept="image/*" @change="setImage"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog title="裁剪图片" :visible.sync="dialogVisible" width="30%">
|
||||
<vue-cropper ref='cropper' :src="imgSrc" :ready="cropImage" :zoom="cropImage" :cropmove="cropImage" style="width:100%;height:300px;"></vue-cropper>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="cancelCrop">取 消</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueCropper from 'vue-cropperjs';
|
||||
export default {
|
||||
name: 'upload',
|
||||
data: function(){
|
||||
return {
|
||||
defaultSrc: require('../../assets/img/img.jpg'),
|
||||
fileList: [],
|
||||
imgSrc: '',
|
||||
cropImg: '',
|
||||
dialogVisible: false,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
VueCropper
|
||||
},
|
||||
methods:{
|
||||
setImage(e){
|
||||
const file = e.target.files[0];
|
||||
if (!file.type.includes('image/')) {
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
this.dialogVisible = true;
|
||||
this.imgSrc = event.target.result;
|
||||
this.$refs.cropper && this.$refs.cropper.replace(event.target.result);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
},
|
||||
cropImage () {
|
||||
this.cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL();
|
||||
},
|
||||
cancelCrop(){
|
||||
this.dialogVisible = false;
|
||||
this.cropImg = this.defaultSrc;
|
||||
},
|
||||
imageuploaded(res) {
|
||||
console.log(res)
|
||||
},
|
||||
handleError(){
|
||||
this.$notify.error({
|
||||
title: '上传失败',
|
||||
message: '图片上传接口上传失败,可更改为自己的服务器接口'
|
||||
});
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.cropImg = this.defaultSrc;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content-title{
|
||||
font-weight: 400;
|
||||
line-height: 50px;
|
||||
margin: 10px 0;
|
||||
font-size: 22px;
|
||||
color: #1f2f3d;
|
||||
}
|
||||
.pre-img{
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: #f8f8f8;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.crop-demo{
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.crop-demo-btn{
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 0 20px;
|
||||
margin-left: 30px;
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.crop-input{
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
|
@ -1,53 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="crumbs">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-lx-calendar"></i> 表单</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>编辑器</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="plugins-tips">
|
||||
Vue-Quill-Editor:基于Quill、适用于Vue2的富文本编辑器。
|
||||
访问地址:<a href="https://github.com/surmon-china/vue-quill-editor" target="_blank">vue-quill-editor</a>
|
||||
</div>
|
||||
<quill-editor ref="myTextEditor" v-model="content" :options="editorOption"></quill-editor>
|
||||
<el-button class="editor-btn" type="primary" @click="submit">提交</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import 'quill/dist/quill.core.css';
|
||||
import 'quill/dist/quill.snow.css';
|
||||
import 'quill/dist/quill.bubble.css';
|
||||
import { quillEditor } from 'vue-quill-editor';
|
||||
export default {
|
||||
name: 'editor',
|
||||
data: function(){
|
||||
return {
|
||||
content: '',
|
||||
editorOption: {
|
||||
placeholder: 'Hello World'
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
quillEditor
|
||||
},
|
||||
methods: {
|
||||
onEditorChange({ editor, html, text }) {
|
||||
this.content = html;
|
||||
},
|
||||
submit(){
|
||||
console.log(this.content);
|
||||
this.$message.success('提交成功!');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.editor-btn{
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<div class="sidebar">
|
||||
<el-menu
|
||||
class="sidebar-el-menu"
|
||||
:default-active="onRoutes"
|
||||
:collapse="sidebar.collapse"
|
||||
:background-color="sidebar.bgColor"
|
||||
:text-color="sidebar.textColor"
|
||||
router
|
||||
>
|
||||
<template v-for="item in menuData">
|
||||
<template v-if="item.children">
|
||||
<el-sub-menu :index="item.index" :key="item.index" v-permiss="item.id">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<component :is="item.icon"></component>
|
||||
</el-icon>
|
||||
<span>{{ item.title }}</span>
|
||||
</template>
|
||||
<template v-for="subItem in item.children">
|
||||
<el-sub-menu
|
||||
v-if="subItem.children"
|
||||
:index="subItem.index"
|
||||
:key="subItem.index"
|
||||
v-permiss="item.id"
|
||||
>
|
||||
<template #title>{{ subItem.title }}</template>
|
||||
<el-menu-item
|
||||
v-for="(threeItem, i) in subItem.children"
|
||||
:key="i"
|
||||
:index="threeItem.index"
|
||||
>
|
||||
{{ threeItem.title }}
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else :index="subItem.index" v-permiss="item.id">
|
||||
{{ subItem.title }}
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-menu-item :index="item.index" :key="item.index" v-permiss="item.id">
|
||||
<el-icon>
|
||||
<component :is="item.icon"></component>
|
||||
</el-icon>
|
||||
<template #title>{{ item.title }}</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</el-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useSidebarStore } from '../store/sidebar';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { menuData } from '@/components/menu';
|
||||
|
||||
const route = useRoute();
|
||||
const onRoutes = computed(() => {
|
||||
return route.path;
|
||||
});
|
||||
|
||||
const sidebar = useSidebarStore();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sidebar {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 70px;
|
||||
bottom: 0;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.sidebar-el-menu:not(.el-menu--collapse) {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.sidebar-el-menu {
|
||||
min-height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,211 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="table-toolbar" v-if="hasToolbar">
|
||||
<div class="table-toolbar-left">
|
||||
<slot name="toolbarBtn"></slot>
|
||||
</div>
|
||||
<div class="table-toolbar-right flex-center">
|
||||
<template v-if="multipleSelection.length > 0">
|
||||
<el-tooltip effect="dark" content="删除选中" placement="top">
|
||||
<el-icon class="columns-setting-icon" @click="delSelection(multipleSelection)">
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
<el-divider direction="vertical" />
|
||||
</template>
|
||||
<el-tooltip effect="dark" content="刷新" placement="top">
|
||||
<el-icon class="columns-setting-icon" @click="refresh">
|
||||
<Refresh />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
<el-divider direction="vertical" />
|
||||
<el-tooltip effect="dark" content="列设置" placement="top">
|
||||
<el-dropdown :hide-on-click="false" size="small" trigger="click">
|
||||
<el-icon class="columns-setting-icon">
|
||||
<Setting />
|
||||
</el-icon>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="c in columns">
|
||||
<el-checkbox v-model="c.visible" :label="c.label" />
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<el-table class="mgb20" :style="{ width: '100%' }" border :data="tableData" :row-key="rowKey"
|
||||
@selection-change="handleSelectionChange" table-layout="auto">
|
||||
<template v-for="item in columns" :key="item.prop">
|
||||
<el-table-column v-if="item.visible" :prop="item.prop" :label="item.label" :width="item.width"
|
||||
:type="item.type" :align="item.align || 'center'">
|
||||
|
||||
<template #default="{ row, column, $index }" v-if="item.type === 'index'">
|
||||
{{ getIndex($index) }}
|
||||
</template>
|
||||
<template #default="{ row, column, $index }" v-if="!item.type">
|
||||
<slot :name="item.prop" :rows="row" :index="$index">
|
||||
<template v-if="item.prop == 'operator'">
|
||||
<el-button type="warning" size="small" :icon="View" @click="viewFunc(row)">
|
||||
查看
|
||||
</el-button>
|
||||
<el-button type="primary" size="small" :icon="Edit" @click="editFunc(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" size="small" :icon="Delete" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
<span v-else-if="item.formatter">
|
||||
{{ item.formatter(row[item.prop]) }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ row[item.prop] }}
|
||||
</span>
|
||||
</slot>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
</el-table>
|
||||
<el-pagination v-if="hasPagination" :current-page="currentPage" :page-size="pageSize" :background="true"
|
||||
:layout="layout" :total="total" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { toRefs, PropType, ref } from 'vue'
|
||||
import { Delete, Edit, View, Refresh } from '@element-plus/icons-vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
// 表格相关
|
||||
tableData: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
columns: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: []
|
||||
},
|
||||
rowKey: {
|
||||
type: String,
|
||||
default: 'id'
|
||||
},
|
||||
hasToolbar: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 分页相关
|
||||
hasPagination: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
currentPage: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
pageSize: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'total, prev, pager, next'
|
||||
},
|
||||
delFunc: {
|
||||
type: Function,
|
||||
default: () => { }
|
||||
},
|
||||
viewFunc: {
|
||||
type: Function,
|
||||
default: () => { }
|
||||
},
|
||||
editFunc: {
|
||||
type: Function,
|
||||
default: () => { }
|
||||
},
|
||||
delSelection: {
|
||||
type: Function,
|
||||
default: () => { }
|
||||
},
|
||||
refresh: {
|
||||
type: Function,
|
||||
default: () => { }
|
||||
},
|
||||
changePage: {
|
||||
type: Function,
|
||||
default: () => { }
|
||||
}
|
||||
})
|
||||
|
||||
let {
|
||||
tableData,
|
||||
columns,
|
||||
rowKey,
|
||||
hasToolbar,
|
||||
hasPagination,
|
||||
total,
|
||||
currentPage,
|
||||
pageSize,
|
||||
layout,
|
||||
} = toRefs(props)
|
||||
|
||||
columns.value.forEach((item) => {
|
||||
if (item.visible === undefined) {
|
||||
item.visible = true
|
||||
}
|
||||
})
|
||||
|
||||
// 当选择项发生变化时会触发该事件
|
||||
const multipleSelection = ref([])
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
multipleSelection.value = selection
|
||||
}
|
||||
|
||||
// 当前页码变化的事件
|
||||
const handleCurrentChange = (val: number) => {
|
||||
props.changePage(val)
|
||||
}
|
||||
|
||||
const handleDelete = (row) => {
|
||||
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
props.delFunc(row);
|
||||
})
|
||||
.catch(() => { });
|
||||
};
|
||||
|
||||
const getIndex = (index: number) => {
|
||||
return index + 1 + (currentPage.value - 1) * pageSize.value
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.table-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.columns-setting-icon {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
color: #676767;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.table-header .cell {
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<el-descriptions :title="title" :column="column" border>
|
||||
<el-descriptions-item v-for="item in list" :span="item.span">
|
||||
<template #label> {{ item.label }} </template>
|
||||
<slot :name="item.prop" :rows="row">
|
||||
{{ item.value || row[item.prop] }}
|
||||
</slot>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
}
|
||||
});
|
||||
const { row, title, column = 2, list } = props.data;
|
||||
|
||||
</script>
|
|
@ -0,0 +1,111 @@
|
|||
<template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" :label-width="options.labelWidth">
|
||||
<el-row>
|
||||
<el-col :span="options.span" v-for="item in options.list">
|
||||
<el-form-item :label="item.label" :prop="item.prop">
|
||||
<!-- 文本框、数字框、下拉框、日期框、开关、上传 -->
|
||||
<el-input v-if="item.type === 'input'" v-model="form[item.prop]" :disabled="item.disabled"
|
||||
:placeholder="item.placeholder" clearable></el-input>
|
||||
<el-input-number v-else-if="item.type === 'number'" v-model="form[item.prop]"
|
||||
:disabled="item.disabled" controls-position="right"></el-input-number>
|
||||
<el-select v-else-if="item.type === 'select'" v-model="form[item.prop]" :disabled="item.disabled"
|
||||
:placeholder="item.placeholder" clearable>
|
||||
<el-option v-for="opt in item.opts" :label="opt.label" :value="opt.value"></el-option>
|
||||
</el-select>
|
||||
<el-date-picker v-else-if="item.type === 'date'" type="date" v-model="form[item.prop]"
|
||||
:value-format="item.format"></el-date-picker>
|
||||
<el-switch v-else-if="item.type === 'switch'" v-model="form[item.prop]"
|
||||
:active-value="item.activeValue" :inactive-value="item.inactiveValue"
|
||||
:active-text="item.activeText" :inactive-text="item.inactiveText"></el-switch>
|
||||
<el-upload v-else-if="item.type === 'upload'" class="avatar-uploader" action="#"
|
||||
:show-file-list="false" :on-success="handleAvatarSuccess">
|
||||
<img v-if="form[item.prop]" :src="form[item.prop]" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
<slot :name="item.prop" v-else>
|
||||
|
||||
</slot>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="saveEdit(formRef)">保 存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { FormOption } from '@/types/form-option';
|
||||
import { FormInstance, FormRules, UploadProps } from 'element-plus';
|
||||
import { PropType, ref } from 'vue';
|
||||
|
||||
const { options, formData, edit, update } = defineProps({
|
||||
options: {
|
||||
type: Object as PropType<FormOption>,
|
||||
required: true
|
||||
},
|
||||
formData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
edit: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
update: {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const form = ref({ ...(edit ? formData : {}) });
|
||||
|
||||
const rules: FormRules = options.list.map(item => {
|
||||
if (item.required) {
|
||||
return { [item.prop]: [{ required: true, message: `${item.label}不能为空`, trigger: 'blur' }] };
|
||||
}
|
||||
return {};
|
||||
}).reduce((acc, cur) => ({ ...acc, ...cur }), {});
|
||||
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const saveEdit = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(valid => {
|
||||
if (!valid) return false;
|
||||
update(form.value);
|
||||
});
|
||||
};
|
||||
|
||||
const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
|
||||
form.value.thumb = URL.createObjectURL(uploadFile.raw!);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div class="search-container">
|
||||
<el-form ref="searchRef" :model="query" :inline="true">
|
||||
<el-form-item :label="item.label" :prop="item.prop" v-for="item in options">
|
||||
<!-- 文本框、下拉框、日期框 -->
|
||||
<el-input v-if="item.type === 'input'" v-model="query[item.prop]" :disabled="item.disabled"
|
||||
:placeholder="item.placeholder" clearable></el-input>
|
||||
<el-select v-else-if="item.type === 'select'" v-model="query[item.prop]" :disabled="item.disabled"
|
||||
:placeholder="item.placeholder" clearable>
|
||||
<el-option v-for="opt in item.opts" :label="opt.label" :value="opt.value"></el-option>
|
||||
</el-select>
|
||||
<el-date-picker v-else-if="item.type === 'date'" type="date" v-model="query[item.prop]"
|
||||
:value-format="item.format"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :icon="Search" @click="search">搜索</el-button>
|
||||
<el-button :icon="Refresh" @click="resetForm(searchRef)">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { Search, Refresh } from '@element-plus/icons-vue';
|
||||
import { PropType, ref } from 'vue';
|
||||
import { FormOptionList } from '@/types/form-option';
|
||||
|
||||
const props = defineProps({
|
||||
query: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
options: {
|
||||
type: Array as PropType<Array<FormOptionList>>,
|
||||
required: true
|
||||
},
|
||||
search: {
|
||||
type: Function,
|
||||
default: () => { }
|
||||
}
|
||||
});
|
||||
|
||||
const searchRef = ref<FormInstance>();
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
props.search();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-container {
|
||||
padding: 20px 30px 0;
|
||||
background-color: #fff;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,148 @@
|
|||
<template>
|
||||
<div class="tabs-container">
|
||||
<el-tabs v-model="activePath" class="tabs" type="card" closable @tab-click="clickTabls" @tab-remove="closeTabs">
|
||||
<el-tab-pane
|
||||
v-for="item in tabs.list"
|
||||
:key="item.path"
|
||||
:label="item.title"
|
||||
:name="item.path"
|
||||
@click="setTags(item)"
|
||||
></el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="Tabs-close-box">
|
||||
<el-dropdown @command="handleTags">
|
||||
<el-button size="small" type="primary" plain>
|
||||
标签选项
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu size="small">
|
||||
<el-dropdown-item command="other">关闭其他</el-dropdown-item>
|
||||
<el-dropdown-item command="current">关闭当前</el-dropdown-item>
|
||||
<el-dropdown-item command="all">关闭所有</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { useTabsStore } from '../store/tabs';
|
||||
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const activePath = ref(route.fullPath);
|
||||
const tabs = useTabsStore();
|
||||
// 设置标签
|
||||
const setTags = (route: any) => {
|
||||
const isExist = tabs.list.some((item) => {
|
||||
return item.path === route.fullPath;
|
||||
});
|
||||
if (!isExist) {
|
||||
tabs.setTabsItem({
|
||||
name: route.name,
|
||||
title: route.meta.title,
|
||||
path: route.fullPath,
|
||||
});
|
||||
}
|
||||
};
|
||||
setTags(route);
|
||||
onBeforeRouteUpdate((to) => {
|
||||
setTags(to);
|
||||
});
|
||||
|
||||
// 关闭全部标签
|
||||
const closeAll = () => {
|
||||
tabs.clearTabs();
|
||||
router.push('/');
|
||||
};
|
||||
// 关闭其他标签
|
||||
const closeOther = () => {
|
||||
const curItem = tabs.list.filter((item) => {
|
||||
return item.path === route.fullPath;
|
||||
});
|
||||
tabs.closeTabsOther(curItem);
|
||||
};
|
||||
const handleTags = (command: string) => {
|
||||
switch (command) {
|
||||
case 'current':
|
||||
// 关闭当前页面的标签页
|
||||
tabs.closeCurrentTag({
|
||||
$router: router,
|
||||
$route: route,
|
||||
});
|
||||
break;
|
||||
case 'all':
|
||||
closeAll();
|
||||
break;
|
||||
|
||||
case 'other':
|
||||
closeOther();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const clickTabls = (item: any) => {
|
||||
router.push(item.props.name);
|
||||
};
|
||||
const closeTabs = (path: string) => {
|
||||
const index = tabs.list.findIndex((item) => item.path === path);
|
||||
tabs.delTabsItem(index);
|
||||
const item = tabs.list[index] || tabs.list[index - 1];
|
||||
router.push(item ? item.path : '/');
|
||||
};
|
||||
|
||||
watch(
|
||||
() => route.fullPath,
|
||||
(newVal, oldVal) => {
|
||||
activePath.value = newVal;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scss>
|
||||
.tabs-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
padding: 2px 120px 0 0;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
.el-tabs__header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.el-tabs__nav {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.el-tabs__nav-next,
|
||||
.el-tabs__nav-prev {
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
&.el-tabs {
|
||||
--el-tabs-header-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.Tabs-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>
|
51
src/main.js
|
@ -1,51 +0,0 @@
|
|||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import axios from 'axios';
|
||||
import ElementUI from 'element-ui';
|
||||
import VueI18n from 'vue-i18n';
|
||||
import { messages } from './components/common/i18n';
|
||||
import 'element-ui/lib/theme-chalk/index.css'; // 默认主题
|
||||
// import '../static/css/theme-green/index.css'; // 浅绿色主题
|
||||
import './assets/css/icon.css';
|
||||
import './components/common/directives';
|
||||
import "babel-polyfill";
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.use(VueI18n);
|
||||
Vue.use(ElementUI, {
|
||||
size: 'small'
|
||||
});
|
||||
Vue.prototype.$axios = axios;
|
||||
|
||||
const i18n = new VueI18n({
|
||||
locale: 'zh',
|
||||
messages
|
||||
})
|
||||
|
||||
//使用钩子函数对路由进行权限跳转
|
||||
router.beforeEach((to, from, next) => {
|
||||
const role = localStorage.getItem('ms_username');
|
||||
if (!role && to.path !== '/login') {
|
||||
next('/login');
|
||||
} else if (to.meta.permission) {
|
||||
// 如果是管理员权限则可进入,这里只是简单的模拟管理员权限而已
|
||||
role === 'admin' ? next() : next('/403');
|
||||
} else {
|
||||
// 简单的判断IE10及以下不进入富文本编辑器,该组件不兼容
|
||||
if (navigator.userAgent.indexOf('MSIE') > -1 && to.path === '/editor') {
|
||||
Vue.prototype.$alert('vue-quill-editor组件不兼容IE10及以下浏览器,请使用更高版本的浏览器查看', '浏览器不兼容通知', {
|
||||
confirmButtonText: '确定'
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
i18n,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
|
@ -0,0 +1,28 @@
|
|||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import { usePermissStore } from './store/permiss';
|
||||
import 'element-plus/dist/index.css';
|
||||
import './assets/css/icon.css';
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(createPinia());
|
||||
app.use(router);
|
||||
|
||||
// 注册elementplus图标
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component);
|
||||
}
|
||||
// 自定义权限指令
|
||||
const permiss = usePermissStore();
|
||||
app.directive('permiss', {
|
||||
mounted(el, binding) {
|
||||
if (binding.value && !permiss.key.includes(String(binding.value))) {
|
||||
el['hidden'] = true;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
app.mount('#app');
|
|
@ -1,111 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import Router from 'vue-router';
|
||||
|
||||
Vue.use(Router);
|
||||
|
||||
export default new Router({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/dashboard'
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: resolve => require(['../components/common/Home.vue'], resolve),
|
||||
meta: { title: '自述文件' },
|
||||
children:[
|
||||
{
|
||||
path: '/dashboard',
|
||||
component: resolve => require(['../components/page/Dashboard.vue'], resolve),
|
||||
meta: { title: '系统首页' }
|
||||
},
|
||||
{
|
||||
path: '/icon',
|
||||
component: resolve => require(['../components/page/Icon.vue'], resolve),
|
||||
meta: { title: '自定义图标' }
|
||||
},
|
||||
{
|
||||
path: '/table',
|
||||
component: resolve => require(['../components/page/BaseTable.vue'], resolve),
|
||||
meta: { title: '基础表格' }
|
||||
},
|
||||
{
|
||||
path: '/tabs',
|
||||
component: resolve => require(['../components/page/Tabs.vue'], resolve),
|
||||
meta: { title: 'tab选项卡' }
|
||||
},
|
||||
{
|
||||
path: '/form',
|
||||
component: resolve => require(['../components/page/BaseForm.vue'], resolve),
|
||||
meta: { title: '基本表单' }
|
||||
},
|
||||
{
|
||||
// 富文本编辑器组件
|
||||
path: '/editor',
|
||||
component: resolve => require(['../components/page/VueEditor.vue'], resolve),
|
||||
meta: { title: '富文本编辑器' }
|
||||
},
|
||||
{
|
||||
// markdown组件
|
||||
path: '/markdown',
|
||||
component: resolve => require(['../components/page/Markdown.vue'], resolve),
|
||||
meta: { title: 'markdown编辑器' }
|
||||
},
|
||||
{
|
||||
// 图片上传组件
|
||||
path: '/upload',
|
||||
component: resolve => require(['../components/page/Upload.vue'], resolve),
|
||||
meta: { title: '文件上传' }
|
||||
},
|
||||
{
|
||||
// vue-schart组件
|
||||
path: '/charts',
|
||||
component: resolve => require(['../components/page/BaseCharts.vue'], resolve),
|
||||
meta: { title: 'schart图表' }
|
||||
},
|
||||
{
|
||||
// 拖拽列表组件
|
||||
path: '/drag',
|
||||
component: resolve => require(['../components/page/DragList.vue'], resolve),
|
||||
meta: { title: '拖拽列表' }
|
||||
},
|
||||
{
|
||||
// 拖拽Dialog组件
|
||||
path: '/dialog',
|
||||
component: resolve => require(['../components/page/DragDialog.vue'], resolve),
|
||||
meta: { title: '拖拽弹框' }
|
||||
},
|
||||
{
|
||||
// 国际化组件
|
||||
path: '/i18n',
|
||||
component: resolve => require(['../components/page/I18n.vue'], resolve),
|
||||
meta: { title: '国际化' }
|
||||
},
|
||||
{
|
||||
// 权限页面
|
||||
path: '/permission',
|
||||
component: resolve => require(['../components/page/Permission.vue'], resolve),
|
||||
meta: { title: '权限测试', permission: true }
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
component: resolve => require(['../components/page/404.vue'], resolve),
|
||||
meta: { title: '404' }
|
||||
},
|
||||
{
|
||||
path: '/403',
|
||||
component: resolve => require(['../components/page/403.vue'], resolve),
|
||||
meta: { title: '403' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
component: resolve => require(['../components/page/Login.vue'], resolve)
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
redirect: '/404'
|
||||
}
|
||||
]
|
||||
})
|
|
@ -0,0 +1,293 @@
|
|||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
||||
import { usePermissStore } from '../store/permiss';
|
||||
import Home from '../views/home.vue';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/dashboard',
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home,
|
||||
children: [
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'dashboard',
|
||||
meta: {
|
||||
title: '系统首页',
|
||||
noAuth: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "dashboard" */ '../views/dashboard.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system-user',
|
||||
name: 'system-user',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
permiss: '11',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "system-user" */ '../views/system/user.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system-role',
|
||||
name: 'system-role',
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
permiss: '12',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "system-role" */ '../views/system/role.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system-menu',
|
||||
name: 'system-menu',
|
||||
meta: {
|
||||
title: '菜单管理',
|
||||
permiss: '13',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "system-menu" */ '../views/system/menu.vue'),
|
||||
},
|
||||
{
|
||||
path: '/table',
|
||||
name: 'basetable',
|
||||
meta: {
|
||||
title: '基础表格',
|
||||
permiss: '31',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "table" */ '../views/table/basetable.vue'),
|
||||
},
|
||||
{
|
||||
path: '/table-editor',
|
||||
name: 'table-editor',
|
||||
meta: {
|
||||
title: '可编辑表格',
|
||||
permiss: '32',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "table-editor" */ '../views/table/table-editor.vue'),
|
||||
},
|
||||
{
|
||||
path: '/schart',
|
||||
name: 'schart',
|
||||
meta: {
|
||||
title: 'schart图表',
|
||||
permiss: '41',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "schart" */ '../views/chart/schart.vue'),
|
||||
},
|
||||
{
|
||||
path: '/echarts',
|
||||
name: 'echarts',
|
||||
meta: {
|
||||
title: 'echarts图表',
|
||||
permiss: '42',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "echarts" */ '../views/chart/echarts.vue'),
|
||||
},
|
||||
|
||||
{
|
||||
path: '/icon',
|
||||
name: 'icon',
|
||||
meta: {
|
||||
title: '图标',
|
||||
permiss: '5',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "icon" */ '../views/pages/icon.vue'),
|
||||
},
|
||||
{
|
||||
path: '/ucenter',
|
||||
name: 'ucenter',
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "ucenter" */ '../views/pages/ucenter.vue'),
|
||||
},
|
||||
{
|
||||
path: '/editor',
|
||||
name: 'editor',
|
||||
meta: {
|
||||
title: '富文本编辑器',
|
||||
permiss: '291',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "editor" */ '../views/pages/editor.vue'),
|
||||
},
|
||||
{
|
||||
path: '/markdown',
|
||||
name: 'markdown',
|
||||
meta: {
|
||||
title: 'markdown编辑器',
|
||||
permiss: '292',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "markdown" */ '../views/pages/markdown.vue'),
|
||||
},
|
||||
{
|
||||
path: '/export',
|
||||
name: 'export',
|
||||
meta: {
|
||||
title: '导出Excel',
|
||||
permiss: '34',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "export" */ '../views/table/export.vue'),
|
||||
},
|
||||
{
|
||||
path: '/import',
|
||||
name: 'import',
|
||||
meta: {
|
||||
title: '导入Excel',
|
||||
permiss: '33',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "import" */ '../views/table/import.vue'),
|
||||
},
|
||||
{
|
||||
path: '/theme',
|
||||
name: 'theme',
|
||||
meta: {
|
||||
title: '主题设置',
|
||||
permiss: '7',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "theme" */ '../views/pages/theme.vue'),
|
||||
},
|
||||
{
|
||||
path: '/calendar',
|
||||
name: 'calendar',
|
||||
meta: {
|
||||
title: '日历',
|
||||
permiss: '24',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "calendar" */ '../views/element/calendar.vue'),
|
||||
},
|
||||
{
|
||||
path: '/watermark',
|
||||
name: 'watermark',
|
||||
meta: {
|
||||
title: '水印',
|
||||
permiss: '25',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "watermark" */ '../views/element/watermark.vue'),
|
||||
},
|
||||
{
|
||||
path: '/carousel',
|
||||
name: 'carousel',
|
||||
meta: {
|
||||
title: '走马灯',
|
||||
permiss: '23',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "carousel" */ '../views/element/carousel.vue'),
|
||||
},
|
||||
{
|
||||
path: '/tour',
|
||||
name: 'tour',
|
||||
meta: {
|
||||
title: '分步引导',
|
||||
permiss: '26',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "tour" */ '../views/element/tour.vue'),
|
||||
},
|
||||
{
|
||||
path: '/steps',
|
||||
name: 'steps',
|
||||
meta: {
|
||||
title: '步骤条',
|
||||
permiss: '27',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "steps" */ '../views/element/steps.vue'),
|
||||
},
|
||||
{
|
||||
path: '/form',
|
||||
name: 'forms',
|
||||
meta: {
|
||||
title: '表单',
|
||||
permiss: '21',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "form" */ '../views/element/form.vue'),
|
||||
},
|
||||
{
|
||||
path: '/upload',
|
||||
name: 'upload',
|
||||
meta: {
|
||||
title: '上传',
|
||||
permiss: '22',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "upload" */ '../views/element/upload.vue'),
|
||||
},
|
||||
{
|
||||
path: '/statistic',
|
||||
name: 'statistic',
|
||||
meta: {
|
||||
title: '统计',
|
||||
permiss: '28',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "statistic" */ '../views/element/statistic.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
meta: {
|
||||
title: '登录',
|
||||
noAuth: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "login" */ '../views/pages/login.vue'),
|
||||
},
|
||||
{
|
||||
path: '/register',
|
||||
meta: {
|
||||
title: '注册',
|
||||
noAuth: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "register" */ '../views/pages/register.vue'),
|
||||
},
|
||||
{
|
||||
path: '/reset-pwd',
|
||||
meta: {
|
||||
title: '重置密码',
|
||||
noAuth: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "reset-pwd" */ '../views/pages/reset-pwd.vue'),
|
||||
},
|
||||
{
|
||||
path: '/403',
|
||||
meta: {
|
||||
title: '没有权限',
|
||||
noAuth: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "403" */ '../views/pages/403.vue'),
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
meta: {
|
||||
title: '找不到页面',
|
||||
noAuth: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "404" */ '../views/pages/404.vue'),
|
||||
},
|
||||
{ path: '/:path(.*)', redirect: '/404' },
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes,
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start();
|
||||
const role = localStorage.getItem('vuems_name');
|
||||
const permiss = usePermissStore();
|
||||
|
||||
if (!role && to.meta.noAuth !== true) {
|
||||
next('/login');
|
||||
} else if (typeof to.meta.permiss == 'string' && !permiss.key.includes(to.meta.permiss)) {
|
||||
// 如果没有权限,则进入403
|
||||
next('/403');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
router.afterEach(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -0,0 +1,60 @@
|
|||
import { defineStore } from 'pinia';
|
||||
|
||||
interface ObjectList {
|
||||
[key: string]: string[];
|
||||
}
|
||||
|
||||
export const usePermissStore = defineStore('permiss', {
|
||||
state: () => {
|
||||
const defaultList: ObjectList = {
|
||||
admin: [
|
||||
'0',
|
||||
'1',
|
||||
'11',
|
||||
'12',
|
||||
'13',
|
||||
'2',
|
||||
'21',
|
||||
'22',
|
||||
'23',
|
||||
'24',
|
||||
'25',
|
||||
'26',
|
||||
'27',
|
||||
'28',
|
||||
'29',
|
||||
'291',
|
||||
'292',
|
||||
'3',
|
||||
'31',
|
||||
'32',
|
||||
'33',
|
||||
'34',
|
||||
'4',
|
||||
'41',
|
||||
'42',
|
||||
'5',
|
||||
'7',
|
||||
'6',
|
||||
'61',
|
||||
'62',
|
||||
'63',
|
||||
'64',
|
||||
'65',
|
||||
'66',
|
||||
],
|
||||
user: ['0', '1', '11', '12', '13'],
|
||||
};
|
||||
const username = localStorage.getItem('vuems_name');
|
||||
console.log(username);
|
||||
return {
|
||||
key: (username == 'admin' ? defaultList.admin : defaultList.user) as string[],
|
||||
defaultList,
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
handleSet(val: string[]) {
|
||||
this.key = val;
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useSidebarStore = defineStore('sidebar', {
|
||||
state: () => {
|
||||
return {
|
||||
collapse: false,
|
||||
bgColor: localStorage.getItem('sidebar-bg-color') || '#324157',
|
||||
textColor: localStorage.getItem('sidebar-text-color') || '#bfcbd9'
|
||||
};
|
||||
},
|
||||
getters: {},
|
||||
actions: {
|
||||
handleCollapse() {
|
||||
this.collapse = !this.collapse;
|
||||
},
|
||||
setBgColor(color: string) {
|
||||
this.bgColor = color;
|
||||
localStorage.setItem('sidebar-bg-color', color);
|
||||
},
|
||||
setTextColor(color: string) {
|
||||
this.textColor = color;
|
||||
localStorage.setItem('sidebar-text-color', color);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,53 @@
|
|||
import { defineStore } from 'pinia';
|
||||
|
||||
interface ListItem {
|
||||
name: string;
|
||||
path: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const useTabsStore = defineStore('tabs', {
|
||||
state: () => {
|
||||
return {
|
||||
list: <ListItem[]>[]
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
show: state => {
|
||||
return state.list.length > 0;
|
||||
},
|
||||
nameList: state => {
|
||||
return state.list.map(item => item.name);
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
delTabsItem(index: number) {
|
||||
this.list.splice(index, 1);
|
||||
},
|
||||
setTabsItem(data: ListItem) {
|
||||
this.list.push(data);
|
||||
},
|
||||
clearTabs() {
|
||||
this.list = [];
|
||||
},
|
||||
closeTabsOther(data: ListItem[]) {
|
||||
this.list = data;
|
||||
},
|
||||
closeCurrentTag(data: any) {
|
||||
for (let i = 0, len = this.list.length; i < len; i++) {
|
||||
const item = this.list[i];
|
||||
if (item.path === data.$route.fullPath) {
|
||||
if (i < len - 1) {
|
||||
data.$router.push(this.list[i + 1].path);
|
||||
} else if (i > 0) {
|
||||
data.$router.push(this.list[i - 1].path);
|
||||
} else {
|
||||
data.$router.push('/');
|
||||
}
|
||||
this.list.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
import { mix, setProperty } from '@/utils';
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useThemeStore = defineStore('theme', {
|
||||
state: () => {
|
||||
return {
|
||||
primary: '',
|
||||
success: '',
|
||||
warning: '',
|
||||
danger: '',
|
||||
info: '',
|
||||
headerBgColor: '#242f42',
|
||||
headerTextColor: '#fff',
|
||||
};
|
||||
},
|
||||
getters: {},
|
||||
actions: {
|
||||
initTheme() {
|
||||
['primary', 'success', 'warning', 'danger', 'info'].forEach((type) => {
|
||||
const color = localStorage.getItem(`theme-${type}`) || '';
|
||||
if (color) {
|
||||
this.setPropertyColor(color, type); // 设置主题色
|
||||
}
|
||||
});
|
||||
const headerBgColor = localStorage.getItem('header-bg-color');
|
||||
headerBgColor && this.setHeaderBgColor(headerBgColor);
|
||||
const headerTextColor = localStorage.getItem('header-text-color');
|
||||
headerTextColor && this.setHeaderTextColor(headerTextColor);
|
||||
},
|
||||
resetTheme() {
|
||||
['primary', 'success', 'warning', 'danger', 'info'].forEach((type) => {
|
||||
this.setPropertyColor('', type); // 重置主题色
|
||||
});
|
||||
},
|
||||
setPropertyColor(color: string, type: string = 'primary') {
|
||||
this[type] = color;
|
||||
setProperty(`--el-color-${type}`, color);
|
||||
localStorage.setItem(`theme-${type}`, color);
|
||||
this.setThemeLight(type);
|
||||
},
|
||||
setThemeLight(type: string = 'primary') {
|
||||
[3, 5, 7, 8, 9].forEach((v) => {
|
||||
setProperty(`--el-color-${type}-light-${v}`, mix('#ffffff', this[type], v / 10));
|
||||
});
|
||||
setProperty(`--el-color-${type}-dark-2`, mix('#ffffff', this[type], 0.2));
|
||||
},
|
||||
setHeaderBgColor(color: string) {
|
||||
this.headerBgColor = color;
|
||||
setProperty('--header-bg-color', color);
|
||||
localStorage.setItem(`header-bg-color`, color);
|
||||
},
|
||||
setHeaderTextColor(color: string) {
|
||||
this.headerTextColor = color;
|
||||
setProperty('--header-text-color', color);
|
||||
localStorage.setItem(`header-text-color`, color);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
export interface FormOption {
|
||||
list: FormOptionList[];
|
||||
labelWidth?: number | string;
|
||||
span?: number;
|
||||
|
||||
}
|
||||
|
||||
export interface FormOptionList {
|
||||
prop: string;
|
||||
label: string;
|
||||
type: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
opts?: any[];
|
||||
format?: string;
|
||||
activeValue?: any;
|
||||
inactiveValue?: any;
|
||||
activeText?: string;
|
||||
inactiveText?: string;
|
||||
required?: boolean;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
export interface Menus {
|
||||
id: string;
|
||||
pid?: string;
|
||||
icon?: string;
|
||||
index: string;
|
||||
title: string;
|
||||
permiss?: string;
|
||||
children?: Menus[];
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
export interface Role {
|
||||
id: number;
|
||||
name: string;
|
||||
key: string;
|
||||
status: boolean;
|
||||
permiss: string[]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
export interface TableItem {
|
||||
id: number;
|
||||
name: string;
|
||||
thumb: string;
|
||||
money: number;
|
||||
state: string;
|
||||
date: string;
|
||||
address: string;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
export interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
password: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
role: string;
|
||||
date: string;
|
||||
}
|
||||
|
||||
export interface Register {
|
||||
username: string;
|
||||
password: string;
|
||||
email: string;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
export const setProperty = (prop: string, val: any, dom = document.documentElement) => {
|
||||
dom.style.setProperty(prop, val);
|
||||
};
|
||||
|
||||
export const mix = (color1: string, color2: string, weight: number = 0.5): string => {
|
||||
let color = '#';
|
||||
for (let i = 0; i <= 2; i++) {
|
||||
const c1 = parseInt(color1.substring(1 + i * 2, 3 + i * 2), 16);
|
||||
const c2 = parseInt(color2.substring(1 + i * 2, 3 + i * 2), 16);
|
||||
const c = Math.round(c1 * weight + c2 * (1 - weight));
|
||||
color += c.toString(16).padStart(2, '0');
|
||||
}
|
||||
return color;
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
import axios, { AxiosInstance, AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||
|
||||
const service: AxiosInstance = axios.create({
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
service.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
return config;
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.log(error);
|
||||
return Promise.reject();
|
||||
}
|
||||
);
|
||||
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
if (response.status === 200) {
|
||||
return response;
|
||||
} else {
|
||||
Promise.reject();
|
||||
}
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.log(error);
|
||||
return Promise.reject();
|
||||
}
|
||||
);
|
||||
|
||||
export default service;
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<div class="plugins-tips">
|
||||
vue-echarts:Apache ECharts™ 的 Vue.js 组件。 访问地址:
|
||||
<a href="https://github.com/ecomfe/vue-echarts" target="_blank">vue-echarts</a>
|
||||
</div>
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>
|
||||
<div class="content-title">柱状图</div>
|
||||
</template>
|
||||
<v-chart class="schart" :option="barOptions" />
|
||||
</el-card>
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>
|
||||
<div class="content-title">折线图</div>
|
||||
</template>
|
||||
<v-chart class="schart" :option="lineOptions" />
|
||||
</el-card>
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>
|
||||
<div class="content-title">饼状图</div>
|
||||
</template>
|
||||
<v-chart class="schart" :option="pieOptions" />
|
||||
</el-card>
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>
|
||||
<div class="content-title">环形图</div>
|
||||
</template>
|
||||
<v-chart class="schart" :option="ringOptions" />
|
||||
</el-card>
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>
|
||||
<div class="content-title">词云图</div>
|
||||
</template>
|
||||
<v-chart class="schart" :option="wordOptions" />
|
||||
</el-card>
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>
|
||||
<div class="content-title">地图</div>
|
||||
</template>
|
||||
<v-chart class="schart" :option="mapOptions" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="echarts">
|
||||
import { registerMap, use } from 'echarts/core';
|
||||
import { BarChart, LineChart, PieChart, MapChart } from 'echarts/charts';
|
||||
import {
|
||||
GridComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
TitleComponent,
|
||||
VisualMapComponent,
|
||||
} from 'echarts/components';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import VChart from 'vue-echarts';
|
||||
import 'echarts-wordcloud';
|
||||
import { barOptions, lineOptions, pieOptions, ringOptions, wordOptions, mapOptions } from './options';
|
||||
import chinaMap from '@/utils/china';
|
||||
use([
|
||||
CanvasRenderer,
|
||||
BarChart,
|
||||
GridComponent,
|
||||
LineChart,
|
||||
PieChart,
|
||||
MapChart,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
TitleComponent,
|
||||
VisualMapComponent,
|
||||
]);
|
||||
registerMap('china', chinaMap);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.schart {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
font-weight: 400;
|
||||
font-size: 22px;
|
||||
color: #1f2f3d;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,345 @@
|
|||
import { graphic } from 'echarts/core';
|
||||
export const barOptions = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
color: ['#009688', '#f44336'],
|
||||
series: [
|
||||
{
|
||||
data: [120, 200, 150, 80, 70, 110, 130],
|
||||
type: 'bar',
|
||||
},
|
||||
{
|
||||
data: [180, 230, 190, 120, 110, 230, 235],
|
||||
type: 'bar',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const lineOptions = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
color: ['#009688', '#f44336'],
|
||||
series: [
|
||||
{
|
||||
name: 'Email',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
areaStyle: {},
|
||||
smooth: true,
|
||||
data: [120, 132, 101, 134, 90, 230, 210],
|
||||
},
|
||||
{
|
||||
name: 'Union Ads',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
areaStyle: {},
|
||||
smooth: true,
|
||||
data: [220, 182, 191, 234, 290, 330, 310],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const pieOptions = {
|
||||
title: {
|
||||
text: 'Referer of a Website',
|
||||
subtext: 'Fake Data',
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Access From',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: [
|
||||
{ value: 1048, name: 'Search Engine' },
|
||||
{ value: 735, name: 'Direct' },
|
||||
{ value: 580, name: 'Email' },
|
||||
{ value: 484, name: 'Union Ads' },
|
||||
{ value: 300, name: 'Video Ads' },
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const wordOptions = {
|
||||
series: [
|
||||
{
|
||||
type: 'wordCloud',
|
||||
rotationRange: [0, 0],
|
||||
autoSize: {
|
||||
enable: true,
|
||||
minSize: 14,
|
||||
},
|
||||
textStyle: {
|
||||
fontFamily: '微软雅黑,sans-serif',
|
||||
color: function () {
|
||||
return (
|
||||
'rgb(' +
|
||||
[
|
||||
Math.round(Math.random() * 160),
|
||||
Math.round(Math.random() * 160),
|
||||
Math.round(Math.random() * 160),
|
||||
].join(',') +
|
||||
')'
|
||||
);
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{
|
||||
name: 'Vue',
|
||||
value: 10000,
|
||||
},
|
||||
{
|
||||
name: 'React',
|
||||
value: 9000,
|
||||
},
|
||||
{
|
||||
name: '图表',
|
||||
value: 4000,
|
||||
},
|
||||
{
|
||||
name: '产品',
|
||||
value: 7000,
|
||||
},
|
||||
{
|
||||
name: 'vue-manage-system',
|
||||
value: 2000,
|
||||
},
|
||||
{
|
||||
name: 'element-plus',
|
||||
value: 6000,
|
||||
},
|
||||
{
|
||||
name: '管理系统',
|
||||
value: 5000,
|
||||
},
|
||||
{
|
||||
name: '前端',
|
||||
value: 4000,
|
||||
},
|
||||
{
|
||||
name: '测试',
|
||||
value: 3000,
|
||||
},
|
||||
{
|
||||
name: '后端',
|
||||
value: 8000,
|
||||
},
|
||||
{
|
||||
name: '软件开发',
|
||||
value: 6000,
|
||||
},
|
||||
{
|
||||
name: '程序员',
|
||||
value: 4000,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const ringOptions = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
top: '5%',
|
||||
left: 'center',
|
||||
},
|
||||
|
||||
series: [
|
||||
{
|
||||
name: 'Access From',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center',
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 40,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
data: [
|
||||
{ value: 1048, name: 'Search Engine' },
|
||||
{ value: 735, name: 'Direct' },
|
||||
{ value: 580, name: 'Email' },
|
||||
{ value: 484, name: 'Union Ads' },
|
||||
{ value: 300, name: 'Video Ads' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const dashOpt1 = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
grid: {
|
||||
top: '2%',
|
||||
left: '2%',
|
||||
right: '3%',
|
||||
bottom: '2%',
|
||||
containLabel: true,
|
||||
},
|
||||
color: ['#009688', '#f44336'],
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 150, 136,0.8)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(0, 150, 136,0.2)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
smooth: true,
|
||||
data: [120, 132, 301, 134, 90, 230, 210],
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: [220, 122, 191, 234, 190, 130, 310],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const dashOpt2 = {
|
||||
legend: {
|
||||
bottom: '1%',
|
||||
left: 'center',
|
||||
},
|
||||
color: ['#3f51b5', '#009688', '#f44336', '#00bcd4', '#1ABC9C'],
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
},
|
||||
data: [
|
||||
{ value: 1048, name: '数码' },
|
||||
{ value: 735, name: '食品' },
|
||||
{ value: 580, name: '母婴' },
|
||||
{ value: 484, name: '家电' },
|
||||
{ value: 300, name: '运动' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const mapOptions = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
geo: {
|
||||
map: 'china',
|
||||
roam: false,
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
visualMap: {
|
||||
show: false,
|
||||
min: 0,
|
||||
max: 100,
|
||||
realtime: false,
|
||||
calculable: false,
|
||||
inRange: {
|
||||
color: ['#d2e0f5', '#71A9FF'],
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
geoIndex: 0,
|
||||
name: '地域分布',
|
||||
type: 'map',
|
||||
coordinateSystem: 'geo',
|
||||
map: 'china',
|
||||
data: [
|
||||
{ name: '北京', value: 100 },
|
||||
{ name: '上海', value: 100 },
|
||||
{ name: '广东', value: 100 },
|
||||
{ name: '浙江', value: 90 },
|
||||
{ name: '江西', value: 80 },
|
||||
{ name: '山东', value: 70 },
|
||||
{ name: '广西', value: 60 },
|
||||
{ name: '河南', value: 50 },
|
||||
{ name: '河南', value: 40 },
|
||||
{ name: '青海', value: 70 },
|
||||
{ name: '河南', value: 30 },
|
||||
{ name: '黑龙江', value: 20 },
|
||||
{ name: '新疆', value: 20 },
|
||||
{ name: '云南', value: 20 },
|
||||
{ name: '甘肃', value: 20 },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,129 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<div class="plugins-tips">
|
||||
vue-schart:vue.js封装sChart.js的图表组件。 访问地址:
|
||||
<a href="https://github.com/lin-xin/vue-schart" target="_blank">vue-schart</a>
|
||||
</div>
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>
|
||||
<div class="content-title">柱状图</div>
|
||||
</template>
|
||||
<schart class="schart" canvasId="bar" :options="options1"></schart>
|
||||
</el-card>
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>
|
||||
<div class="content-title">折线图</div>
|
||||
</template>
|
||||
<schart class="schart" canvasId="line" :options="options2"></schart>
|
||||
</el-card>
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>
|
||||
<div class="content-title">饼状图</div>
|
||||
</template>
|
||||
<schart class="schart" canvasId="pie" :options="options3"></schart>
|
||||
</el-card>
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>
|
||||
<div class="content-title">环形图</div>
|
||||
</template>
|
||||
<schart class="schart" canvasId="ring" :options="options4"></schart>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="schart">
|
||||
import Schart from 'vue-schart';
|
||||
|
||||
const options1 = {
|
||||
type: 'bar',
|
||||
title: {
|
||||
text: '最近一周各品类销售图'
|
||||
},
|
||||
colorList: ["#3f51b5", "#009688", "#f44336", "#00bcd4", "#1ABC9C"],
|
||||
labels: ['周一', '周二', '周三', '周四', '周五'],
|
||||
datasets: [
|
||||
{
|
||||
label: '家电',
|
||||
// fillColor: 'rgba(241, 49, 74, 0.5)',
|
||||
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: '最近几个月各品类销售趋势图'
|
||||
},
|
||||
colorList: ["#3f51b5", "#009688", "#f44336", "#00bcd4", "#1ABC9C"],
|
||||
labels: ['6月', '7月', '8月', '9月', '10月'],
|
||||
datasets: [
|
||||
{
|
||||
label: '家电',
|
||||
data: [234, 278, 270, 190, 230]
|
||||
},
|
||||
{
|
||||
label: '百货',
|
||||
data: [164, 178, 150, 135, 160]
|
||||
},
|
||||
{
|
||||
label: '食品',
|
||||
data: [114, 138, 200, 235, 190]
|
||||
}
|
||||
]
|
||||
};
|
||||
const options3 = {
|
||||
type: 'pie',
|
||||
title: {
|
||||
text: '服装品类销售饼状图'
|
||||
},
|
||||
legend: {
|
||||
position: 'left'
|
||||
},
|
||||
colorList: ["#2196f3", '#673ab7', "#009688", "#1ABC9C", "#3f51b5", "#f44336", "#00bcd4"],
|
||||
labels: ['T恤', '牛仔裤', '连衣裙', '毛衣', '七分裤', '短裙', '羽绒服'],
|
||||
datasets: [
|
||||
{
|
||||
data: [334, 278, 190, 235, 260, 200, 141]
|
||||
}
|
||||
]
|
||||
};
|
||||
const options4 = {
|
||||
type: 'ring',
|
||||
title: {
|
||||
text: '环形三等分'
|
||||
},
|
||||
showValue: false,
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
bottom: 40
|
||||
},
|
||||
colorList: ["#3f51b5", "#009688", "#f44336", "#00bcd4", "#1ABC9C"],
|
||||
labels: ['vue', 'react', 'angular'],
|
||||
datasets: [
|
||||
{
|
||||
data: [500, 500, 500]
|
||||
}
|
||||
]
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.schart {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
font-weight: 400;
|
||||
font-size: 22px;
|
||||
color: #1f2f3d;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,357 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20" class="mgb20">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon bg1">
|
||||
<User />
|
||||
</el-icon>
|
||||
<div class="card-content">
|
||||
<countup class="card-num color1" :end="6666" />
|
||||
<div>用户访问量</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon bg2">
|
||||
<ChatDotRound />
|
||||
</el-icon>
|
||||
<div class="card-content">
|
||||
<countup class="card-num color2" :end="168" />
|
||||
<div>系统消息</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon bg3">
|
||||
<Goods />
|
||||
</el-icon>
|
||||
<div class="card-content">
|
||||
<countup class="card-num color3" :end="8888" />
|
||||
<div>商品数量</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon bg4">
|
||||
<ShoppingCartFull />
|
||||
</el-icon>
|
||||
<div class="card-content">
|
||||
<countup class="card-num color4" :end="568" />
|
||||
<div>今日订单量</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="mgb20">
|
||||
<el-col :span="18">
|
||||
<el-card shadow="hover">
|
||||
<div class="card-header">
|
||||
<p class="card-header-title">订单动态</p>
|
||||
<p class="card-header-desc">最近一周订单状态,包括订单成交量和订单退货量</p>
|
||||
</div>
|
||||
<v-chart class="chart" :option="dashOpt1" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<div class="card-header">
|
||||
<p class="card-header-title">品类分布</p>
|
||||
<p class="card-header-desc">最近一个月销售商品的品类情况</p>
|
||||
</div>
|
||||
<v-chart class="chart" :option="dashOpt2" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="7">
|
||||
<el-card shadow="hover" :body-style="{ height: '400px' }">
|
||||
<div class="card-header">
|
||||
<p class="card-header-title">时间线</p>
|
||||
<p class="card-header-desc">最新的销售动态和活动信息</p>
|
||||
</div>
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(activity, index) in activities" :key="index" :color="activity.color">
|
||||
<div class="timeline-item">
|
||||
<div>
|
||||
<p>{{ activity.content }}</p>
|
||||
<p class="timeline-desc">{{ activity.description }}</p>
|
||||
</div>
|
||||
<div class="timeline-time">{{ activity.timestamp }}</div>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-card shadow="hover" :body-style="{ height: '400px' }">
|
||||
<div class="card-header">
|
||||
<p class="card-header-title">渠道统计</p>
|
||||
<p class="card-header-desc">最近一个月的订单来源统计</p>
|
||||
</div>
|
||||
<v-chart class="map-chart" :option="mapOptions" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-card shadow="hover" :body-style="{ height: '400px' }">
|
||||
<div class="card-header">
|
||||
<p class="card-header-title">排行榜</p>
|
||||
<p class="card-header-desc">销售商品的热门榜单Top5</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="rank-item" v-for="(rank, index) in ranks">
|
||||
<div class="rank-item-avatar">{{ index + 1 }}</div>
|
||||
<div class="rank-item-content">
|
||||
<div class="rank-item-top">
|
||||
<div class="rank-item-title">{{ rank.title }}</div>
|
||||
<div class="rank-item-desc">销量:{{ rank.value }}</div>
|
||||
</div>
|
||||
<el-progress
|
||||
:show-text="false"
|
||||
striped
|
||||
:stroke-width="10"
|
||||
:percentage="rank.percent"
|
||||
:color="rank.color"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="dashboard">
|
||||
import countup from '@/components/countup.vue';
|
||||
import { use, registerMap } from 'echarts/core';
|
||||
import { BarChart, LineChart, PieChart, MapChart } from 'echarts/charts';
|
||||
import {
|
||||
GridComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
TitleComponent,
|
||||
VisualMapComponent,
|
||||
} from 'echarts/components';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import VChart from 'vue-echarts';
|
||||
import { dashOpt1, dashOpt2, mapOptions } from './chart/options';
|
||||
import chinaMap from '@/utils/china';
|
||||
use([
|
||||
CanvasRenderer,
|
||||
BarChart,
|
||||
GridComponent,
|
||||
LineChart,
|
||||
PieChart,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
TitleComponent,
|
||||
VisualMapComponent,
|
||||
MapChart,
|
||||
]);
|
||||
registerMap('china', chinaMap);
|
||||
const activities = [
|
||||
{
|
||||
content: '收藏商品',
|
||||
description: 'xxx收藏了你的商品,就是不买',
|
||||
timestamp: '30分钟前',
|
||||
color: '#00bcd4',
|
||||
},
|
||||
{
|
||||
content: '用户评价',
|
||||
description: 'xxx给了某某商品一个差评,吐血啊',
|
||||
timestamp: '55分钟前',
|
||||
color: '#1ABC9C',
|
||||
},
|
||||
{
|
||||
content: '订单提交',
|
||||
description: 'xxx提交了订单,快去收钱吧',
|
||||
timestamp: '1小时前',
|
||||
color: '#3f51b5',
|
||||
},
|
||||
{
|
||||
content: '退款申请',
|
||||
description: 'xxx申请了仅退款,又要亏钱了',
|
||||
timestamp: '15小时前',
|
||||
color: '#f44336',
|
||||
},
|
||||
{
|
||||
content: '商品上架',
|
||||
description: '运营专员瞒着你上架了一辆飞机',
|
||||
timestamp: '1天前',
|
||||
color: '#009688',
|
||||
},
|
||||
];
|
||||
|
||||
const ranks = [
|
||||
{
|
||||
title: '手机',
|
||||
value: 10000,
|
||||
percent: 80,
|
||||
color: '#f25e43',
|
||||
},
|
||||
{
|
||||
title: '电脑',
|
||||
value: 8000,
|
||||
percent: 70,
|
||||
color: '#00bcd4',
|
||||
},
|
||||
{
|
||||
title: '相机',
|
||||
value: 6000,
|
||||
percent: 60,
|
||||
color: '#64d572',
|
||||
},
|
||||
{
|
||||
title: '衣服',
|
||||
value: 5000,
|
||||
percent: 55,
|
||||
color: '#e9a745',
|
||||
},
|
||||
{
|
||||
title: '书籍',
|
||||
value: 4000,
|
||||
percent: 50,
|
||||
color: '#009688',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.card-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100px;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
.card-content {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.card-num {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 50px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.bg1 {
|
||||
background: #2d8cf0;
|
||||
}
|
||||
|
||||
.bg2 {
|
||||
background: #64d572;
|
||||
}
|
||||
|
||||
.bg3 {
|
||||
background: #f25e43;
|
||||
}
|
||||
|
||||
.bg4 {
|
||||
background: #e9a745;
|
||||
}
|
||||
|
||||
.color1 {
|
||||
color: #2d8cf0;
|
||||
}
|
||||
|
||||
.color2 {
|
||||
color: #64d572;
|
||||
}
|
||||
|
||||
.color3 {
|
||||
color: #f25e43;
|
||||
}
|
||||
|
||||
.color4 {
|
||||
color: #e9a745;
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding-left: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.card-header-desc {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.timeline-time,
|
||||
.timeline-desc {
|
||||
font-size: 12px;
|
||||
color: #787878;
|
||||
}
|
||||
|
||||
.rank-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.rank-item-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: #f2f2f2;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.rank-item-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.rank-item-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #343434;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.rank-item-desc {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
.map-chart {
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<el-calendar v-model="value">
|
||||
<template #date-cell="{ data }">
|
||||
<div>{{ data.date.getDate() }}</div>
|
||||
<div class="notes-container" v-if="notes[data.day.toString()]">
|
||||
<div class="notes" v-for="note in notes[data.day.toString()]">
|
||||
<span :class="note.status === 1 ? 'text-success' : 'text-danger'"></span>
|
||||
<div class="note-title">{{ note.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-calendar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const today = new Date();
|
||||
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
|
||||
const value = ref(today);
|
||||
|
||||
const todayDate = today.toISOString().slice(0, 10);
|
||||
const yesterdayDate = yesterday.toISOString().slice(0, 10);
|
||||
|
||||
const notes: any = {
|
||||
[todayDate]: [
|
||||
{ title: '吃饭', status: 1 },
|
||||
{ title: '睡觉', status: 0 },
|
||||
{ title: '吃饭', status: 1 },
|
||||
{ title: '睡觉', status: 0 },
|
||||
{ title: '吃饭', status: 1 },
|
||||
{ title: '睡觉', status: 0 },
|
||||
],
|
||||
[yesterdayDate]: [{ title: '参加会议', status: 0 }],
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.notes-container {
|
||||
height: 60px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.notes-container::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.notes {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.notes:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.note-title {
|
||||
flex: 1;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.notes span {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
background-color: #5cb85c;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
background-color: #d9534f;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,66 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-card class="mgb20">
|
||||
<template #header>基础用法</template>
|
||||
<el-carousel height="400px">
|
||||
<el-carousel-item v-for="item in 4" :key="item">
|
||||
<h3>{{ item }}</h3>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-card>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-card class="mgb20">
|
||||
<template #header>轮播图</template>
|
||||
<el-carousel height="300px">
|
||||
<el-carousel-item v-for="item in imgs" :key="item">
|
||||
<el-image class="carousel-img" :src="item" fit="cover" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card class="mgb20">
|
||||
<template #header>卡片模式</template>
|
||||
<el-carousel height="300px" type="card">
|
||||
<el-carousel-item v-for="item in imgs" :key="item">
|
||||
<el-image class="carousel-img" :src="item" fit="cover" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const imgs = [
|
||||
'https://cdn.pixabay.com/photo/2017/08/07/08/23/sea-2601374_640.jpg',
|
||||
'https://cdn.pixabay.com/photo/2020/02/11/10/24/lake-4839058_640.jpg',
|
||||
'https://cdn.pixabay.com/photo/2024/02/21/08/06/coast-8587004_640.jpg',
|
||||
'https://cdn.pixabay.com/photo/2023/07/29/10/21/grasshopper-8156626_640.jpg',
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-carousel__item h3 {
|
||||
color: #475669;
|
||||
line-height: 400px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-carousel__item:nth-child(2n) {
|
||||
background-color: #99a9bf;
|
||||
}
|
||||
|
||||
.el-carousel__item:nth-child(2n + 1) {
|
||||
background-color: #d3dce6;
|
||||
}
|
||||
|
||||
.carousel-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,189 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<el-radio-group class="mgb20" v-model="labelPosition">
|
||||
<el-radio-button value="left">Left</el-radio-button>
|
||||
<el-radio-button value="right">Right</el-radio-button>
|
||||
<el-radio-button value="top">Top</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-form ref="formRef" :rules="rules" :model="form" label-width="120px" :label-position="labelPosition">
|
||||
<el-row :gutter="50">
|
||||
<el-col :span="10">
|
||||
<el-form-item label="文本框" prop="name">
|
||||
<el-input v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="数字框" prop="num">
|
||||
<el-input-number v-model="form.num" :min="1" :max="10" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日期选择" prop="date">
|
||||
<el-date-picker type="date" placeholder="选择日期" v-model="form.date"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间选择" prop="time">
|
||||
<el-time-picker placeholder="选择时间" v-model="form.time">
|
||||
</el-time-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择器" prop="region">
|
||||
<el-select v-model="form.region" placeholder="请选择">
|
||||
<el-option key="小明" label="小明" value="小明"></el-option>
|
||||
<el-option key="小红" label="小红" value="小红"></el-option>
|
||||
<el-option key="小白" label="小白" value="小白"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="城市级联" prop="options">
|
||||
<el-cascader :options="options" v-model="form.options"></el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="文本框" prop="desc">
|
||||
<el-input type="textarea" rows="5" v-model="form.desc"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="评分" prop="rate">
|
||||
<el-rate v-model="form.rate" allow-half />
|
||||
</el-form-item>
|
||||
<el-form-item label="滑块" prop="num">
|
||||
<el-slider v-model="form.num" :step="1" show-stops :max="10" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开关" prop="delivery">
|
||||
<el-switch v-model="form.delivery"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="颜色选择" prop="color">
|
||||
<el-color-picker v-model="form.color" />
|
||||
</el-form-item>
|
||||
<el-form-item label="多选框" prop="type">
|
||||
<el-checkbox-group v-model="form.type">
|
||||
<el-checkbox label="小明" value="小明" name="type"></el-checkbox>
|
||||
<el-checkbox label="小红" value="小红" name="type"></el-checkbox>
|
||||
<el-checkbox label="小白" value="小白" name="type"></el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="单选框" prop="resource">
|
||||
<el-radio-group v-model="form.resource">
|
||||
<el-radio label="小明" value="小明"></el-radio>
|
||||
<el-radio label="小红" value="小红"></el-radio>
|
||||
<el-radio label="小白" value="小白"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="穿梭框" prop="transfer">
|
||||
<el-transfer v-model="form.transfer" :data="transferData" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit(formRef)">表单提交</el-button>
|
||||
<el-button @click="onReset(formRef)">重置表单</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="forms">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import type { FormInstance, FormProps, FormRules } from 'element-plus';
|
||||
const labelPosition = ref<FormProps['labelPosition']>('right')
|
||||
const options = [
|
||||
{
|
||||
value: 'guangdong',
|
||||
label: '广东省',
|
||||
children: [
|
||||
{
|
||||
value: 'guangzhou',
|
||||
label: '广州市',
|
||||
children: [
|
||||
{
|
||||
value: 'tianhe',
|
||||
label: '天河区',
|
||||
},
|
||||
{
|
||||
value: 'haizhu',
|
||||
label: '海珠区',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'dongguan',
|
||||
label: '东莞市',
|
||||
children: [
|
||||
{
|
||||
value: 'changan',
|
||||
label: '长安镇',
|
||||
},
|
||||
{
|
||||
value: 'humen',
|
||||
label: '虎门镇',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'hunan',
|
||||
label: '湖南省',
|
||||
children: [
|
||||
{
|
||||
value: 'changsha',
|
||||
label: '长沙市',
|
||||
children: [
|
||||
{
|
||||
value: 'yuelu',
|
||||
label: '岳麓区',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const rules: FormRules = {
|
||||
name: [{ required: true, message: '请输入表单名称', trigger: 'blur' }],
|
||||
};
|
||||
const formRef = ref<FormInstance>();
|
||||
const form = reactive({
|
||||
name: '',
|
||||
region: '',
|
||||
date: '',
|
||||
time: '',
|
||||
delivery: true,
|
||||
type: ['小明'],
|
||||
resource: '小红',
|
||||
desc: '',
|
||||
options: [],
|
||||
color: '',
|
||||
num: 1,
|
||||
rate: 0,
|
||||
transfer: [],
|
||||
|
||||
});
|
||||
const generateData = () => {
|
||||
const data = []
|
||||
for (let i = 1; i <= 15; i++) {
|
||||
data.push({
|
||||
key: i,
|
||||
label: `Option ${i}`,
|
||||
disabled: i % 4 === 0,
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
const transferData = ref(generateData())
|
||||
// 提交
|
||||
const onSubmit = (formEl: FormInstance | undefined) => {
|
||||
// 表单校验
|
||||
if (!formEl) return;
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
console.log(form);
|
||||
ElMessage.success('提交成功!');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
// 重置
|
||||
const onReset = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,340 @@
|
|||
<template>
|
||||
<div>
|
||||
|
||||
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>基础用法</template>
|
||||
<el-row>
|
||||
<el-col :span="6" style="text-align: center">
|
||||
<el-statistic title="Daily active users" :value="268500" />
|
||||
</el-col>
|
||||
<el-col :span="6" style="text-align: center">
|
||||
<el-statistic :value="138">
|
||||
<template #title>
|
||||
<div style="display: inline-flex; align-items: center">
|
||||
Ratio of men to women
|
||||
</div>
|
||||
</template>
|
||||
<template #suffix>/100</template>
|
||||
</el-statistic>
|
||||
</el-col>
|
||||
<el-col :span="6" style="text-align: center">
|
||||
<el-statistic title="数字滚动" :value="outputValue" />
|
||||
</el-col>
|
||||
<el-col :span="6" style="text-align: center">
|
||||
<el-countdown title="倒计时" :value="value" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mgb20" shadow="hover">
|
||||
<template #header>CountUp.js</template>
|
||||
<div class="plugins-tips">
|
||||
countup.js:用于快速创建以更有趣的方式显示数字数据的动画。 访问地址:
|
||||
<a href="https://github.com/inorganik/countUp.js" target="_blank">countUp.js</a>
|
||||
</div>
|
||||
<el-row>
|
||||
<el-col :span="8" style="text-align: center">
|
||||
<p>基础用法</p>
|
||||
<countup class="countup" :end="6666" />
|
||||
</el-col>
|
||||
<el-col :span="8" style="text-align: center">
|
||||
<p>具体配置</p>
|
||||
<countup class="countup" :end="8888.5" :options="options" />
|
||||
</el-col>
|
||||
<el-col :span="8" style="text-align: center">
|
||||
<p>更新数值</p>
|
||||
<countup class="countup" :end="value1" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card class="mgb20" shadow="never">
|
||||
<template #header>统计卡片</template>
|
||||
|
||||
<el-row :gutter="20" class="mgb20">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon color1">
|
||||
<User />
|
||||
</el-icon>
|
||||
<div class="card-content text-right">
|
||||
<el-statistic title="日活跃用户量" :value="268500" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon color2">
|
||||
<ChatDotRound />
|
||||
</el-icon>
|
||||
<div class="card-content text-right">
|
||||
<el-statistic title="系统消息" :value="16800" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon color3">
|
||||
<Goods />
|
||||
</el-icon>
|
||||
<div class="card-content text-right">
|
||||
<el-statistic title="商品数量" :value="8888" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon color4">
|
||||
<ShoppingCartFull />
|
||||
</el-icon>
|
||||
<div class="card-content text-right">
|
||||
<el-statistic title="今日订单量" :value="56888" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" class="mgb20">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<div class="card-content text-left">
|
||||
<el-statistic :value-style="{ color: '#2d8cf0' }" title="日活跃用户量" :value="268500" />
|
||||
</div>
|
||||
<el-icon class="card-icon color1">
|
||||
<User />
|
||||
</el-icon>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<div class="card-content text-left">
|
||||
<el-statistic :value-style="{ color: '#64d572' }" title="系统消息" :value="16800" />
|
||||
</div>
|
||||
<el-icon class="card-icon color2">
|
||||
<ChatDotRound />
|
||||
</el-icon>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<div class="card-content text-left">
|
||||
<el-statistic :value-style="{ color: '#f25e43' }" title="商品数量" :value="8888" />
|
||||
</div>
|
||||
<el-icon class="card-icon color3">
|
||||
<Goods />
|
||||
</el-icon>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<div class="card-content text-left">
|
||||
<el-statistic :value-style="{ color: '#e9a745' }" title="今日订单量" :value="56888" />
|
||||
</div>
|
||||
<el-icon class="card-icon color4">
|
||||
<ShoppingCartFull />
|
||||
</el-icon>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" class="mgb20">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon bg1">
|
||||
<User />
|
||||
</el-icon>
|
||||
<div class="card-content">
|
||||
<countup class="card-num color1" :end="6666" />
|
||||
<div>用户访问量</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon bg2">
|
||||
<ChatDotRound />
|
||||
</el-icon>
|
||||
<div class="card-content">
|
||||
<countup class="card-num color2" :end="168" />
|
||||
<div>系统消息</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon bg3">
|
||||
<Goods />
|
||||
</el-icon>
|
||||
<div class="card-content">
|
||||
<countup class="card-num color3" :end="8888" />
|
||||
<div>商品数量</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body">
|
||||
<el-icon class="card-icon bg4">
|
||||
<ShoppingCartFull />
|
||||
</el-icon>
|
||||
<div class="card-content">
|
||||
<countup class="card-num color4" :end="568" />
|
||||
<div>今日订单量</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" class="mgb20">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body bg1">
|
||||
<el-icon class="card-icon ">
|
||||
<User />
|
||||
</el-icon>
|
||||
<div class="card-content color0">
|
||||
<countup class="card-num" :end="6666" />
|
||||
<div>用户访问量</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body bg2">
|
||||
<el-icon class="card-icon">
|
||||
<ChatDotRound />
|
||||
</el-icon>
|
||||
<div class="card-content color0">
|
||||
<countup class="card-num" :end="168" />
|
||||
<div>系统消息</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body bg3">
|
||||
<el-icon class="card-icon">
|
||||
<Goods />
|
||||
</el-icon>
|
||||
<div class="card-content color0">
|
||||
<countup class="card-num " :end="8888" />
|
||||
<div>商品数量</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" body-class="card-body bg4">
|
||||
<el-icon class="card-icon">
|
||||
<ShoppingCartFull />
|
||||
</el-icon>
|
||||
<div class="card-content color0">
|
||||
<countup class="card-num " :end="568" />
|
||||
<div>今日订单量</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useTransition } from '@vueuse/core'
|
||||
import countup from '@/components/countup.vue';
|
||||
|
||||
const source = ref(0)
|
||||
const outputValue = useTransition(source, {
|
||||
duration: 1500,
|
||||
})
|
||||
source.value = 172000
|
||||
|
||||
const value = ref(Date.now() + 1000 * 60 * 60 * 7)
|
||||
const value1 = ref(1000);
|
||||
setTimeout(() => {
|
||||
value1.value = 8000;
|
||||
}, 5000);
|
||||
const options = {
|
||||
startVal: 1000,
|
||||
decimalPlaces: 2,
|
||||
duration: 5,
|
||||
useGrouping: false,
|
||||
prefix: '$',
|
||||
separator: ',',
|
||||
decimal: '.',
|
||||
suffix: '',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.card-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.bg1 {
|
||||
background: #2d8cf0;
|
||||
}
|
||||
|
||||
.bg2 {
|
||||
background: #64d572;
|
||||
}
|
||||
|
||||
.bg3 {
|
||||
background: #f25e43;
|
||||
}
|
||||
|
||||
.bg4 {
|
||||
background: #e9a745;
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
.countup {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.card-num {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 50px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
||||
.color0 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.color1 {
|
||||
color: #2d8cf0;
|
||||
}
|
||||
|
||||
.color2 {
|
||||
color: #64d572;
|
||||
}
|
||||
|
||||
.color3 {
|
||||
color: #f25e43;
|
||||
}
|
||||
|
||||
.color4 {
|
||||
color: #e9a745;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<div class="step-div" v-if="step === 0">
|
||||
<p>输入注册时的邮箱,我们会发送验证码到您的邮箱</p>
|
||||
<el-input placeholder="请输入邮箱"></el-input>
|
||||
<el-button class="step-btn" type="primary" @click="step++">下一步</el-button>
|
||||
</div>
|
||||
<div class="step-div" v-else-if="step === 1">
|
||||
<p>验证码已发送至您的邮箱,请输入验证码</p>
|
||||
<el-input placeholder="请输入验证码"></el-input>
|
||||
<el-button class="step-btn" type="primary" @click="step++">下一步</el-button>
|
||||
</div>
|
||||
|
||||
<div class="step-div" v-else-if="step === 2">
|
||||
<p>请输入6位以上密码</p>
|
||||
<el-input placeholder="请输入新密码"></el-input>
|
||||
<el-button class="step-btn" type="primary" @click="step++">保存</el-button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-result icon="success" title="保存成功" sub-title="请退出后重新登录"></el-result>
|
||||
</div>
|
||||
<el-steps class="step-style" :active="step" align-center finish-status="success">
|
||||
<el-step title="Step 1" description="填写邮箱" />
|
||||
<el-step title="Step 2" description="填写验证码" />
|
||||
<el-step title="Step 3" description="修改密码" />
|
||||
</el-steps>
|
||||
<el-steps class="step-style" :active="step" finish-status="success" simple>
|
||||
<el-step title="填写邮箱" />
|
||||
<el-step title="填写验证码" />
|
||||
<el-step title="修改密码" />
|
||||
</el-steps>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
const step = ref(0)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.step-div {
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.step-div p {
|
||||
margin-bottom: 20px;
|
||||
color: #787878;
|
||||
}
|
||||
|
||||
.step-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.step-style {
|
||||
max-width: 800px;
|
||||
margin: 40px auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<el-tabs v-model="message" type="card">
|
||||
<el-tab-pane :label="`未读消息(${state.unread.length})`" name="first">
|
||||
<el-table :data="state.unread" :show-header="false" style="width: 100%">
|
||||
<el-table-column>
|
||||
<template #default="scope">
|
||||
<span class="message-title">{{ scope.row.title }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="date" width="180"></el-table-column>
|
||||
<el-table-column width="120">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="handleRead(scope.$index)">标为已读</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="handle-row">
|
||||
<el-button type="primary">全部标为已读</el-button>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="`已读消息(${state.read.length})`" name="second">
|
||||
<template v-if="message === 'second'">
|
||||
<el-table :data="state.read" :show-header="false" style="width: 100%">
|
||||
<el-table-column>
|
||||
<template #default="scope">
|
||||
<span class="message-title">{{ scope.row.title }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="date" width="180"></el-table-column>
|
||||
<el-table-column width="120">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" size="small" @click="handleDel(scope.$index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="handle-row">
|
||||
<el-button type="danger">删除全部</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="`回收站(${state.recycle.length})`" name="third">
|
||||
<template v-if="message === 'third'">
|
||||
<el-table :data="state.recycle" :show-header="false" style="width: 100%">
|
||||
<el-table-column>
|
||||
<template #default="scope">
|
||||
<span class="message-title">{{ scope.row.title }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="date" width="180"></el-table-column>
|
||||
<el-table-column width="120">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="handleRestore(scope.$index)">还原</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="handle-row">
|
||||
<el-button type="danger">清空回收站</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="tabs">
|
||||
import { ref, reactive } from 'vue';
|
||||
|
||||
const message = ref('first');
|
||||
const state = reactive({
|
||||
unread: [
|
||||
{
|
||||
date: '2018-04-19 20:00:00',
|
||||
title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'
|
||||
},
|
||||
{
|
||||
date: '2018-04-19 21:00:00',
|
||||
title: '今晚12点整发大红包,先到先得'
|
||||
}
|
||||
],
|
||||
read: [
|
||||
{
|
||||
date: '2018-04-19 20:00:00',
|
||||
title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'
|
||||
}
|
||||
],
|
||||
recycle: [
|
||||
{
|
||||
date: '2018-04-19 20:00:00',
|
||||
title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const handleRead = (index: number) => {
|
||||
const item = state.unread.splice(index, 1);
|
||||
state.read = item.concat(state.read);
|
||||
};
|
||||
const handleDel = (index: number) => {
|
||||
const item = state.read.splice(index, 1);
|
||||
state.recycle = item.concat(state.recycle);
|
||||
};
|
||||
const handleRestore = (index: number) => {
|
||||
const item = state.recycle.splice(index, 1);
|
||||
state.read = item.concat(state.read);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.message-title {
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.handle-row {
|
||||
margin-top: 30px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<el-button type="primary" @click="open = true">开始引导</el-button>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<el-space>
|
||||
<el-button ref="ref1">上传</el-button>
|
||||
<el-button ref="ref2" type="primary">保存</el-button>
|
||||
<el-button ref="ref3" :icon="MoreFilled" />
|
||||
</el-space>
|
||||
|
||||
<el-tour v-model="open">
|
||||
<el-tour-step :target="ref1?.$el" title="上传文件">
|
||||
<img style="width: 120px" src="../../assets/img/img.jpg" alt="tour.png" />
|
||||
<div>点击这里选择文件</div>
|
||||
</el-tour-step>
|
||||
<el-tour-step :target="ref2?.$el" title="保存" description="点击进行上传" />
|
||||
<el-tour-step :target="ref3?.$el" title="更多操作" description="点击查看更多操作" />
|
||||
</el-tour>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { MoreFilled } from '@element-plus/icons-vue'
|
||||
|
||||
const ref1 = ref()
|
||||
const ref2 = ref()
|
||||
const ref3 = ref()
|
||||
|
||||
const open = ref(false)
|
||||
</script>
|
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<div class="content-title">支持拖拽</div>
|
||||
<div class="plugins-tips">
|
||||
Element Plus自带上传组件。 访问地址:
|
||||
<a href="https://element-plus.org/zh-CN/component/upload.html" target="_blank">Element Plus Upload</a>
|
||||
</div>
|
||||
<el-upload class="upload-demo" drag action="http://jsonplaceholder.typicode.com/api/posts/" multiple
|
||||
:on-change="handle">
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或
|
||||
<em>点击上传</em>
|
||||
</div>
|
||||
</el-upload>
|
||||
|
||||
<div class="content-title">支持裁剪</div>
|
||||
<div class="plugins-tips">
|
||||
vue-cropper:一个简单的vue图片裁剪插件。 访问地址:
|
||||
<a href="https://github.com/xyxiao001/vue-cropper" target="_blank">vue-cropper</a>。 示例请查看
|
||||
<router-link to="/ucenter">个人中心-我的头像</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const handle = (rawFile: any) => {
|
||||
console.log(rawFile);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content-title {
|
||||
font-weight: 400;
|
||||
line-height: 50px;
|
||||
margin: 10px 0;
|
||||
font-size: 22px;
|
||||
color: #1f2f3d;
|
||||
}
|
||||
|
||||
.upload-demo {
|
||||
width: 360px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="18">
|
||||
<el-watermark :content="config.content" :font="config.font" :z-index="config.zIndex"
|
||||
:rotate="config.rotate" :gap="config.gap" :offset="config.offset">
|
||||
<div style="height: 600px" />
|
||||
</el-watermark>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form class="form" :model="config" label-position="top" label-width="50px">
|
||||
<el-form-item label="Content">
|
||||
<el-input v-model="config.content" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Color">
|
||||
<el-color-picker v-model="config.font.color" show-alpha />
|
||||
</el-form-item>
|
||||
<el-form-item label="FontSize">
|
||||
<el-slider v-model="config.font.fontSize" />
|
||||
</el-form-item>
|
||||
<el-form-item label="zIndex">
|
||||
<el-slider v-model="config.zIndex" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Rotate">
|
||||
<el-slider v-model="config.rotate" :min="-180" :max="180" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Gap">
|
||||
<el-space>
|
||||
<el-input-number v-model="config.gap[0]" controls-position="right" />
|
||||
<el-input-number v-model="config.gap[1]" controls-position="right" />
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
<el-form-item label="Offset">
|
||||
<el-space>
|
||||
<el-input-number v-model="config.offset[0]" placeholder="offsetLeft"
|
||||
controls-position="right" />
|
||||
<el-input-number v-model="config.offset[1]" placeholder="offsetTop"
|
||||
controls-position="right" />
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const config = reactive({
|
||||
content: 'vue-manage-system',
|
||||
font: {
|
||||
fontSize: 16,
|
||||
color: 'rgba(0, 0, 0, 0.15)',
|
||||
},
|
||||
zIndex: -1,
|
||||
rotate: -22,
|
||||
gap: [100, 100] as [number, number],
|
||||
offset: [] as unknown as [number, number],
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,63 @@
|
|||
<template>
|
||||
<div class="wrapper">
|
||||
<v-header />
|
||||
<v-sidebar />
|
||||
<div class="content-box" :class="{ 'content-collapse': sidebar.collapse }">
|
||||
<v-tabs></v-tabs>
|
||||
<div class="content">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="move" mode="out-in">
|
||||
<keep-alive :include="tabs.nameList">
|
||||
<component :is="Component"></component>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useSidebarStore } from '@/store/sidebar';
|
||||
import { useTabsStore } from '@/store/tabs';
|
||||
import vHeader from '@/components/header.vue';
|
||||
import vSidebar from '@/components/sidebar.vue';
|
||||
import vTabs from '@/components/tabs.vue';
|
||||
|
||||
const sidebar = useSidebarStore();
|
||||
const tabs = useTabsStore();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content-box {
|
||||
position: absolute;
|
||||
left: 250px;
|
||||
right: 0;
|
||||
top: 70px;
|
||||
bottom: 0;
|
||||
padding-bottom: 30px;
|
||||
-webkit-transition: left 0.3s ease-in-out;
|
||||
transition: left 0.3s ease-in-out;
|
||||
background: #eef0fc;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
overflow-y: scroll;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.content::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.content-collapse {
|
||||
left: 65px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<div class="error-page">
|
||||
<div class="error-box">
|
||||
<div class="error-code">403</div>
|
||||
<div class="error-desc">啊哦~ 你没有权限访问该页面哦</div>
|
||||
<div class="error-handle">
|
||||
<router-link to="/">
|
||||
<el-button type="primary" size="large">返回首页</el-button>
|
||||
</router-link>
|
||||
<el-button class="error-btn" size="large" @click="goBack">返回上一页</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="403">
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const goBack = () => {
|
||||
router.go(-2);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.error-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #eef0fc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.error-box {
|
||||
width: 400px;
|
||||
background-color: #fff;
|
||||
padding: 80px 50px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.error-code {
|
||||
line-height: 1;
|
||||
font-size: 100px;
|
||||
font-weight: bold;
|
||||
color: var(--el-color-primary);
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-desc {
|
||||
font-size: 20px;
|
||||
color: #777;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-handle {
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-btn {
|
||||
margin-left: 100px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<div class="error-page">
|
||||
<div class="error-box">
|
||||
<div class="error-code">404</div>
|
||||
<div class="error-desc">啊哦~ 你所访问的页面不存在</div>
|
||||
<div class="error-handle">
|
||||
<router-link to="/">
|
||||
<el-button type="primary" size="large">返回首页</el-button>
|
||||
</router-link>
|
||||
<el-button class="error-btn" size="large" @click="goBack">返回上一页</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="404">
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const goBack = () => {
|
||||
router.go(-1);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.error-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #eef0fc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.error-box {
|
||||
width: 400px;
|
||||
background-color: #fff;
|
||||
padding: 80px 50px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.error-code {
|
||||
line-height: 1;
|
||||
font-size: 100px;
|
||||
font-weight: bold;
|
||||
color: var(--el-color-primary);
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-desc {
|
||||
font-size: 20px;
|
||||
color: #777;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-handle {
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-btn {
|
||||
margin-left: 100px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<div class="plugins-tips">
|
||||
wangEditor:轻量级 web 富文本编辑器,配置方便,使用简单。 访问地址:
|
||||
<a href="https://www.wangeditor.com/doc/" target="_blank">wangEditor</a>
|
||||
</div>
|
||||
<div style="border: 1px solid #ccc; margin-bottom: 10px">
|
||||
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" />
|
||||
<Editor
|
||||
style="height: 500px; overflow-y: hidden"
|
||||
v-model="valueHtml"
|
||||
:defaultConfig="editorConfig"
|
||||
@onCreated="handleCreated"
|
||||
/>
|
||||
</div>
|
||||
<el-button type="primary" @click="syncHTML">提交</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="editor">
|
||||
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
|
||||
import { onBeforeUnmount, ref, reactive, shallowRef, onMounted } from 'vue';
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef();
|
||||
|
||||
// 内容 HTML
|
||||
const valueHtml = ref('<p>hello</p>');
|
||||
|
||||
// 模拟 ajax 异步获取内容
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>';
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
const toolbarConfig = {};
|
||||
const editorConfig = { placeholder: '请输入内容...' };
|
||||
|
||||
// 组件销毁时,也及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value;
|
||||
if (editor == null) return;
|
||||
editor.destroy();
|
||||
});
|
||||
|
||||
const handleCreated = (editor: any) => {
|
||||
editorRef.value = editor; // 记录 editor 实例,重要!
|
||||
};
|
||||
const syncHTML = () => {
|
||||
console.log(valueHtml.value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|