前端目录调整

This commit is contained in:
molong committed 2022-05-12 21:23:18 +08:00
1 parent fe38e19924
commit a37870c93b
145 files changed
+3090 -7244

No files matched your search

+13 -59
View File
@@ -1,65 +1,19 @@
# thinkvue-admin
<div align="center">
## Project setup
```
npm install
```
![logo](https://lolicode.gitee.io/scui-doc/logo.png)
<p align="center">
<a href="https://v3.vuejs.org/" target="_blank">
<img src="https://img.shields.io/badge/vue.js-3.x-green" alt="vue">
</a>
<a href="https://element-plus.gitee.io/#/zh-CN/component/changelog" target="_blank">
<img src="https://img.shields.io/badge/element--plus-latest-blue" alt="element plus">
</a>
</p>
<h1>SCUI Admin</h1>
</div>
## 介绍
SCUI 是一个中后台前端解决方案,基于VUE3和elementPlus实现。
使用最新的前端技术栈,提供各类实用的组件方便在业务开发时的调用,并且持续性的提供丰富的业务模板帮助你快速搭建企业级中后台前端任务。
SCUI的宗旨是 让一切复杂的东西傻瓜化。
![logo](https://lolicode.gitee.io/scui-doc/g_1.jpg)
## 特点
- **组件** 多个独家组件、业务模板和代码生成器
- **权限** 完整的鉴权体系和高精度权限控制
- **布局** 提供多套布局模式,满足各种视觉需求
- **API** 完善的API管理,使用真实网络MOCK
- **配置** 统一的全局配置和组件配置,支持build后配置热更新
- **性能** 在减少带宽请求和前端算力上多次优化,并且持续着
- **其他** 多功能视图标签、动态权限菜单、控制台组态化、统一异常处理等等
## 演示和文档
- <a href="https://lolicode.gitee.io/scui-doc/demo/#/login" target="_blank">演示</a>
- <a href="https://lolicode.gitee.io/scui-doc/" target="_blank">文档</a>
## 部分截图
![logo](https://lolicode.gitee.io/scui-doc/g_2.jpg)
## 安装教程
``` sh
# 克隆项目
git clone https://gitee.com/lolicode/scui.git
# 进入项目目录
cd scui
# 安装依赖
npm i
# 启动项目(开发模式)
### Compiles and hot-reloads for development
```
npm run serve
```
启动完成后浏览器访问 http://localhost:2800
## 感谢
![fastmock](https://www.fastmock.site/resource/images/logo.png)
### Compiles and minifies for production
```
npm run build
```
## 支持
如果觉得本项目还不错或在工作中有所启发,请在Gitee(码云)帮开发者点亮星星,这是对开发者最大的支持和鼓励!
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "scui",
"name": "thinkvue-admin",
"version": "1.5.0",
"private": true,
"scripts": {
+2 -2
View File
@@ -4,8 +4,8 @@
const APP_CONFIG = {
//标题
APP_NAME: "SCUI",
// APP_NAME: "SCUI",
//接口地址,如遇跨域需使用nginx代理
API_URL: "https://www.fastmock.site/mock/5039c4361c39a7e3252c5b55971f1bd3/api"
// API_URL: "https://www.fastmock.site/mock/5039c4361c39a7e3252c5b55971f1bd3/api"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

+17 -17
View File
@@ -1,29 +1,29 @@
<template>
<el-config-provider :locale="$i18n.messages[$i18n.locale].el" :button="{autoInsertSpace: false}">
<router-view></router-view>
</el-config-provider>
<el-config-provider :locale="$i18n.messages[$i18n.locale].el" :button="{autoInsertSpace: false}">
<router-view></router-view>
</el-config-provider>
</template>
<script>
import colorTool from '@/utils/color'
import colorTool from '@/utils/color'
export default {
name: 'App',
created() {
//设置主题颜色
const app_color = this.$CONFIG.COLOR || this.$TOOL.data.get('APP_COLOR')
if(app_color){
document.documentElement.style.setProperty('--el-color-primary', app_color);
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, colorTool.lighten(app_color,i/10));
}
document.documentElement.style.setProperty(`--el-color-primary-darken-1`, colorTool.darken(app_color,0.1));
export default {
name: 'App',
created() {
//设置主题颜色
const app_color = this.$CONFIG.COLOR || this.$TOOL.data.get('APP_COLOR')
if(app_color){
document.documentElement.style.setProperty('--el-color-primary', app_color);
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, colorTool.lighten(app_color,i/10));
}
document.documentElement.style.setProperty(`--el-color-primary-darken-1`, colorTool.darken(app_color,0.1));
}
}
}
</script>
<style lang="scss">
@import '@/style/style.scss';
@import '@/style/theme/dark.scss';
@import '@/static/style/style.scss';
@import '@/static/style/theme/dark.scss';
</style>
+9 -2
View File
@@ -2,11 +2,18 @@ import config from "@/config"
import http from "@/utils/request"
export default {
token: {
url: `${config.API_URL}/token`,
login: {
url: `${config.API_URL}auth/login`,
name: "登录获取TOKEN",
post: async function(data={}){
return await http.post(this.url, data);
}
},
logout:{
url: `${config.API_URL}auth/logout`,
name: "登出",
get: async function(data={}){
return await http.get(this.url, data);
}
}
}
-37
View File
@@ -1,37 +0,0 @@
import config from "@/config"
import http from "@/utils/request"
export default {
ver: {
url: `${config.API_URL}/demo/ver`,
name: "获取最新版本号",
get: async function(){
return await http.get(this.url);
}
},
post: {
url: `${config.API_URL}/demo/post`,
name: "分页列表",
post: async function(data){
return await http.post(this.url, data, {
headers: {
//'response-status': 401
}
});
}
},
page: {
url: `${config.API_URL}/demo/page`,
name: "分页列表",
get: async function(params){
return await http.get(this.url, params);
}
},
menu: {
url: `${config.API_URL}/demo/menu`,
name: "普通用户菜单",
get: async function(){
return await http.get(this.url);
}
}
}
+92 -24
View File
@@ -2,62 +2,130 @@ import config from "@/config"
import http from "@/utils/request"
export default {
version:{
url: `${config.API_URL}system/index/version`,
name: "获取最新版本号",
get: async function(){
return await http.get(this.url);
}
},
setting:{
list: {
url: `${config.API_URL}system/index/setting`,
name: "获取最新版本号",
get: function(){
return http.get(this.url);
}
}
},
menu: {
myMenus: {
url: `${config.API_URL}/system/menu/my/1.5.0`,
url: `${config.API_URL}system/menu/my/1.5.0`,
name: "获取我的菜单",
get: async function(){
return await http.get(this.url);
}
},
list: {
url: `${config.API_URL}/system/menu/list`,
url: `${config.API_URL}system/menu/index`,
name: "获取菜单",
get: async function(){
return await http.get(this.url);
}
},
add: {
url: `${config.API_URL}/system/menu/add`,
name: "添加菜单",
post: async function(data){
return await http.post(this.url, data);
}
},
edit: {
url: `${config.API_URL}/system/menu/edit`,
name: "编辑菜单",
post: async function(data){
return await http.post(this.url, data);
}
},
delete: {
url: `${config.API_URL}/system/menu/delete`,
name: "删除菜单",
post: async function(data){
return await http.post(this.url, data);
}
}
},
dic: {
tree: {
url: `${config.API_URL}/system/dic/tree`,
dictionary: {
category: {
url: `${config.API_URL}/system/dictionary/category`,
name: "获取字典树",
get: async function(){
return await http.get(this.url);
}
},
editcate:{
url: `${config.API_URL}/system/dictionary/editcate`,
name: "编辑字典树",
post: async function(data = {}){
return await http.post(this.url, data);
}
},
addcate:{
url: `${config.API_URL}/system/dictionary/addcate`,
name: "添加字典树",
post: async function(data = {}){
return await http.post(this.url, data);
}
},
delCate:{
url: `${config.API_URL}/system/dictionary/delcate`,
name: "删除字典树",
post: async function(data = {}){
return await http.post(this.url, data);
}
},
list: {
url: `${config.API_URL}/system/dic/list`,
url: `${config.API_URL}/system/dictionary/lists`,
name: "字典明细",
get: async function(params){
return await http.get(this.url, params);
}
},
get: {
url: `${config.API_URL}/system/dic/get`,
url: `${config.API_URL}/system/dictionary/detail`,
name: "获取字典数据",
get: async function(params){
return await http.get(this.url, params);
}
}
},
role: {
list: {
url: `${config.API_URL}/system/role/list`,
name: "获取角色列表",
},
edit:{
url: `${config.API_URL}/system/dictionary/edit`,
name: "编辑字典明细",
post: async function(data = {}){
return await http.post(this.url, data);
}
},
add:{
url: `${config.API_URL}/system/dictionary/add`,
name: "添加字典明细",
post: async function(data = {}){
return await http.post(this.url, data);
}
},
delete:{
url: `${config.API_URL}/system/dictionary/delete`,
name: "删除字典明细",
post: async function(data = {}){
return await http.post(this.url, data);
}
},
detail: {
url: `${config.API_URL}/system/dictionary/detail`,
name: "字典明细",
get: async function(params){
return await http.get(this.url, params);
}
}
},
user: {
list: {
url: `${config.API_URL}/system/user/list`,
name: "获取用户列表",
get: async function(params){
return await http.get(this.url, params);
}
}
},
},
app: {
list: {
@@ -70,7 +138,7 @@ export default {
},
log: {
list: {
url: `${config.API_URL}/system/log/list`,
url: `${config.API_URL}/system/log/index`,
name: "日志列表",
get: async function(params){
return await http.get(this.url, params);
+65
View File
@@ -0,0 +1,65 @@
import config from "@/config"
import http from "@/utils/request"
export default {
list: {
url: `${config.API_URL}/user/user/index`,
name: "获得用户列表",
get: async function(params){
return await http.get(this.url, params);
}
},
add: {
url: `${config.API_URL}/user/user/add`,
name: "添加用户",
post: async function(params){
return await http.post(this.url, params);
}
},
edit: {
url: `${config.API_URL}/user/user/edit`,
name: "编辑用户",
post: async function(params){
return await http.post(this.url, params);
}
},
role: {
list: {
url: `${config.API_URL}/user/role/index`,
name: "获得角色列表",
get: async function(params){
return await http.get(this.url, params);
}
},
edit: {
url: `${config.API_URL}/user/role/edit`,
name: "编辑角色",
post: async function(params){
return await http.post(this.url, params);
}
}
},
department: {
list: {
url: `${config.API_URL}/user/department/index`,
name: "获得部门列表",
get: async function(params){
return await http.get(this.url, params);
}
},
edit: {
url: `${config.API_URL}/user/department/edit`,
name: "编辑部门",
post: async function(params){
return await http.post(this.url, params);
}
}
},
userinfo:{
url: `${config.API_URL}/user/user/info`,
name: "获得部门列表",
get: async function(params){
return await http.get(this.url, params);
}
}
}
-32
View File
@@ -1,32 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
+5 -6
View File
@@ -1,6 +1,6 @@
const DEFAULT_CONFIG = {
//标题
APP_NAME: "SCUI",
APP_NAME: "管理系统",
//首页地址
DASHBOARD_URL: "/dashboard",
@@ -12,7 +12,7 @@ const DEFAULT_CONFIG = {
CORE_VER: "1.5.0",
//接口地址
API_URL: "/api",
API_URL: "http://localhost:8000/",
//请求超时
TIMEOUT: 10000,
@@ -51,12 +51,11 @@ const DEFAULT_CONFIG = {
//控制台首页默认布局
DEFAULT_GRID: {
//默认分栏数量和宽度 例如 [24] [18,6] [8,8,8] [6,12,6]
layout: [12, 6, 6],
layout: [12, 12],
//小组件分布,com取值:views/home/components 文件名
copmsList: [
['welcome'],
['about', 'ver'],
['time', 'progress']
['time', 'version'],
['welcome']
]
}
}
+3 -3
View File
@@ -3,7 +3,7 @@ import API from "@/api";
//字典选择器配置
export default {
dicApiObj: API.system.dic.get, //获取字典接口对象
dicApiObj: API.system.dictionary.detail, //获取字典接口对象
parseData: function (res) {
return {
data: res.data, //分析行数据字段结构
@@ -15,7 +15,7 @@ export default {
name: 'name' //规定搜索字段
},
props: {
label: 'label', //映射label显示字段
value: 'value', //映射value值字段
label: 'name', //映射label显示字段
value: 'key', //映射value值字段
}
}
+3 -3
View File
@@ -3,15 +3,15 @@
import tool from '@/utils/tool'
export default {
successCode: 200, //请求完成代码
successCode: 1, //请求完成代码
pageSize: 20, //表格每一页条数
parseData: function (res) { //数据分析
return {
data: res.data, //分析无分页的数据字段结构
rows: res.data.rows, //分析行数据字段结构
rows: res.data.data, //分析行数据字段结构
total: res.data.total, //分析总数字段结构
summary: res.data.summary, //分析合计行字段结构
msg: res.message, //分析描述字段结构
msg: res.msg, //分析描述字段结构
code: res.code //分析状态字段结构
}
},
+1 -1
View File
@@ -4,7 +4,7 @@ import API from "@/api";
export default {
apiObj: API.common.upload, //上传请求API对象
successCode: 200, //请求完成代码
successCode: 1, //请求完成代码
maxSize: 10, //最大文件大小 默认10MB
parseData: function (res) {
return {
+2 -2
View File
@@ -26,11 +26,11 @@ export default {
},
//配置用户
user: {
apiObj: API.demo.page,
apiObj: API.system.page,
pageSize: 20,
parseData: function (res) {
return {
rows: res.data.rows,
rows: res.data.data,
total: res.data.total,
msg: res.message,
code: res.code
+2 -30
View File
@@ -63,40 +63,12 @@
userName: "",
userNameF: "",
msg: false,
msgList: [
{
id: 1,
type: 'user',
avatar: "img/avatar.jpg",
title: "Skuya",
describe: "如果喜欢就点个星星支持一下哦",
link: "https://gitee.com/lolicode/scui",
time: "5分钟前"
},
{
id: 2,
type: 'user',
avatar: "img/avatar2.gif",
title: "Lolowan",
describe: "点进去Gitee获取最新开源版本",
link: "https://gitee.com/lolicode/scui",
time: "14分钟前"
},
{
id: 3,
type: 'system',
avatar: "img/logo.png",
title: "感谢登录SCUI Admin",
describe: "Vue 3.0 + Vue-Router 4.0 + ElementPlus + Axios 后台管理系统。",
link: "https://gitee.com/lolicode/scui",
time: "2020年7月24日"
}
]
msgList: []
}
},
created() {
var userInfo = this.$TOOL.data.get("USER_INFO");
this.userName = userInfo.userName;
this.userName = userInfo.username;
this.userNameF = this.userName.substring(0,1);
},
methods: {
+2 -2
View File
@@ -1,7 +1,7 @@
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/display.css'
import scui from './scui'
import sent from './sent'
import i18n from './locales'
import router from './router'
import store from './store'
@@ -14,7 +14,7 @@ app.use(store);
app.use(router);
app.use(ElementPlus, {size: 'default'});
app.use(i18n);
app.use(scui);
app.use(sent);
//挂载app
app.mount('#app');
+48
View File
@@ -0,0 +1,48 @@
<template>
<div v-if="pageLoading">
<el-main>
<el-card shadow="never">
<el-skeleton :rows="1"></el-skeleton>
</el-card>
<el-card shadow="never" style="margin-top: 15px;">
<el-skeleton></el-skeleton>
</el-card>
</el-main>
</div>
<work v-if="dashboard=='1'" @on-mounted="onMounted"></work>
<widgets v-else @on-mounted="onMounted"></widgets>
</template>
<script>
import { defineAsyncComponent } from 'vue';
const work = defineAsyncComponent(() => import('./work'));
const widgets = defineAsyncComponent(() => import('./widgets'));
export default {
name: "dashboard",
components: {
work,
widgets
},
data(){
return {
pageLoading: true,
dashboard: '1'
}
},
created(){
this.dashboard = this.$TOOL.data.get("USER_INFO").dashboard || '1';
},
mounted(){
},
methods: {
onMounted(){
this.pageLoading = false
}
}
}
</script>
<style>
</style>
@@ -0,0 +1,41 @@
<template>
<el-card shadow="hover" header="时钟" class="item-background">
<div class="time">
<h2>{{ time }}</h2>
<p>{{ day }}</p>
</div>
</el-card>
</template>
<script>
export default {
title: "时钟",
icon: "el-icon-clock",
description: "演示部件效果",
data() {
return {
time: '',
day: ''
}
},
mounted() {
this.showTime()
setInterval(()=>{
this.showTime()
},1000)
},
methods: {
showTime(){
this.time = this.$TOOL.dateFormat(new Date(), 'hh:mm:ss')
this.day = this.$TOOL.dateFormat(new Date(), 'yyyy年MM月dd日')
}
}
}
</script>
<style scoped>
.item-background {background: linear-gradient(to right, #8E54E9, #4776E6);color: #fff;}
.time{display: flex; justify-content: space-between;}
.time h2 {font-size: 40px;}
.time p {font-size: 14px;margin-top: 13px;opacity: 0.7;}
</style>
@@ -0,0 +1,37 @@
<template>
<el-card shadow="hover" header="版本信息">
<div style="height: 200px;text-align: center;">
<img src="img/ver.svg" style="height:130px"/>
<h2 style="margin-top: 15px;">电子通行证 {{$CONFIG.CORE_VER}}</h2>
<p style="margin-top: 5px;">最新版本 {{ver.version}}</p>
</div>
</el-card>
</template>
<script>
export default {
title: "版本信息",
icon: "el-icon-monitor",
description: "当前项目版本信息",
data() {
return {
ver: 'loading...'
}
},
mounted() {
this.getVer()
},
methods: {
async getVer(){
const ver = await this.$API.system.version.get()
this.ver = ver.data
},
golog(){
window.open("https://gitee.com/lolicode/scui/releases")
},
gogit(){
window.open("https://gitee.com/lolicode/scui")
}
}
}
</script>
@@ -3,7 +3,7 @@
<div class="welcome">
<div class="logo">
<img src="img/logo.png">
<h2>欢迎体验 SCUI</h2>
<h2>南昌工程学院电子通行证管理系统</h2>
</div>
<div class="tips">
<div class="tips-item">
@@ -19,9 +19,6 @@
<div class="tips-item-message">项目目的:让前端工作更快乐</div>
</div>
</div>
<div class="actions">
<el-button type="primary" icon="el-icon-check" size="large" @click="godoc">文档</el-button>
</div>
</div>
</el-card>
</template>
File renamed without changes.
+48
View File
@@ -0,0 +1,48 @@
<template>
<el-main>
<el-row :gutter="15">
<el-col :lg="24">
<el-card shadow="never" header="我的常用">
<myapp></myapp>
</el-card>
</el-col>
</el-row>
<el-row :gutter="15">
<el-col :lg="12">
<el-card header="门岗出校码">
<div style="margin: 30px; text-align: center;"><sc-qr-code text="http://dztxz.nit.edu.cn:81/h5/#/pages/pass/index?type=out" size="300"></sc-qr-code></div>
</el-card>
</el-col>
<el-col :lg="12">
<el-card header="门岗进校码">
<div style="margin: 30px; text-align: center;"><sc-qr-code text="http://dztxz.nit.edu.cn:81/h5/#/pages/pass/index?type=in" size="300"></sc-qr-code></div>
</el-card>
</el-col>
</el-row>
</el-main>
</template>
<script>
import myapp from './components/myapp';
export default {
components: {
myapp
},
data() {
return {
}
},
mounted(){
this.$emit('on-mounted')
},
methods: {
}
}
</script>
<style scoped>
</style>
File renamed without changes.
File renamed without changes.
+146
View File
@@ -0,0 +1,146 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add"></el-button>
<el-button type="danger" plain icon="el-icon-delete" :disabled="selection.length==0" @click="batch_del"></el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<el-input v-model="search.keyword" placeholder="部门名称" clearable></el-input>
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="apiObj" row-key="id" @selection-change="selectionChange" hidePagination>
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="#" type="index" width="50"></el-table-column>
<el-table-column label="部门名称" prop="title" width="350"></el-table-column>
<el-table-column label="排序" prop="sort" width="150"></el-table-column>
<el-table-column label="操作" fixed="right" align="right" width="140">
<template #default="scope">
<el-button type="text" size="small" @click="table_show(scope.row, scope.$index)">查看</el-button>
<el-divider direction="vertical"></el-divider>
<el-button type="text" size="small" @click="table_edit(scope.row, scope.$index)">编辑</el-button>
<el-divider direction="vertical"></el-divider>
<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
<template #reference>
<el-button type="text" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<save-dialog v-if="dialog.save" ref="saveDialog" @success="handleSaveSuccess" @closed="dialog.save=false"></save-dialog>
<permission-dialog v-if="dialog.permission" ref="permissionDialog" @closed="dialog.permission=false"></permission-dialog>
</template>
<script>
import saveDialog from './save'
export default {
name: 'role',
components: {
saveDialog
},
data() {
return {
dialog: {
save: false,
permission: false
},
apiObj: this.$API.user.department.list,
selection: [],
search: {
keyword: null
}
}
},
methods: {
//添加
add(){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open()
})
},
//编辑
table_edit(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('edit').setData(row)
})
},
//查看
table_show(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('show').setData(row)
})
},
//删除
async table_del(row){
var reqData = {id: row.id}
var res = await this.$API.demo.post.post(reqData);
if(res.code == 200){
this.$refs.table.refresh()
this.$message.success("删除成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
},
//批量删除
async batch_del(){
this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?如果删除项中含有子集将会被一并删除`, '提示', {
type: 'warning'
}).then(() => {
const loading = this.$loading();
this.$refs.table.refresh()
loading.close();
this.$message.success("操作成功")
}).catch(() => {
})
},
//表格选择后回调事件
selectionChange(selection){
this.selection = selection;
},
//搜索
upsearch(){
this.$refs.table.upData(this.search)
},
//根据ID获取树结构
filterTree(id){
var target = null;
function filter(tree){
tree.forEach(item => {
if(item.id == id){
target = item
}
if(item.children){
filter(item.children)
}
})
}
filter(this.$refs.table.tableData)
return target
},
//本地更新数据
handleSaveSuccess(data, mode){
if(mode=='add'){
this.$refs.table.refresh()
}else if(mode=='edit'){
this.$refs.table.refresh()
}
}
}
}
</script>
+118
View File
@@ -0,0 +1,118 @@
<template>
<el-dialog :title="titleMap[mode]" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
<el-form :model="form" :rules="rules" :disabled="mode=='show'" ref="dialogForm" label-width="100px" label-position="left">
<el-form-item label="上级部门" prop="parent_id">
<el-cascader v-model="form.parent_id" :options="groups" :props="groupsProps" :show-all-levels="false" clearable style="width: 100%;"></el-cascader>
</el-form-item>
<el-form-item label="部门名称" prop="title">
<el-input v-model="form.title" clearable></el-input>
</el-form-item>
<el-form-item label="部门别名" prop="identify">
<el-input v-model="form.identify" clearable></el-input>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" :min="1" style="width: 100%;"></el-input-number>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible=false" >取 消</el-button>
<el-button v-if="mode!='show'" type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
emits: ['success', 'closed'],
data() {
return {
mode: "add",
titleMap: {
add: '新增',
edit: '编辑',
show: '查看'
},
visible: false,
isSaveing: false,
//表单数据
form: {
id:"",
title: "",
identify: "",
sort: 1,
parent_id: ""
},
//验证规则
rules: {
sort: [
{required: true, message: '请输入排序', trigger: 'change'}
],
label: [
{required: true, message: '请输入角色名称'}
],
alias: [
{required: true, message: '请输入角色别名'}
]
},
//所需数据选项
groups: [],
groupsProps: {
value: "id",
label: "title",
emitPath: false,
checkStrictly: true
}
}
},
mounted() {
this.getGroup()
},
methods: {
//显示
open(mode='add'){
this.mode = mode;
this.visible = true;
return this
},
//加载树数据
async getGroup(){
var res = await this.$API.user.department.list.get();
this.groups = res.data;
},
//表单提交方法
submit(){
this.$refs.dialogForm.validate(async (valid) => {
if (valid) {
this.isSaveing = true;
this.form.parent_id = this.form.parent_id ? this.form.parent_id : 0;
var res = {}
if(this.mode == 'add'){
res = await this.$API.user.department.add.post(this.form);
}else{
res = await this.$API.user.department.edit.post(this.form);
}
this.isSaveing = false;
if(res.code == 1){
this.$emit('success', this.form, this.mode)
this.visible = false;
this.$message.success("操作成功")
}else{
this.$alert(res.msg, "提示", {type: 'error'})
}
}
})
},
//表单注入数据
setData(data){
this.form.id = data.id
this.form.title = data.title
this.form.identify = data.identify
this.form.sort = data.sort
this.form.parent_id = data.parent_id
//可以和上面一样单个注入,也可以像下面一样直接合并进去
//Object.assign(this.form, data)
}
}
}
</script>
@@ -7,8 +7,8 @@
<el-form-item label="字典名称" prop="name">
<el-input v-model="form.name" clearable placeholder="字典显示名称"></el-input>
</el-form-item>
<el-form-item label="父路径" prop="parentId">
<el-cascader v-model="form.parentId" :options="dic" :props="dicProps" :show-all-levels="false" clearable></el-cascader>
<el-form-item label="父路径" prop="parent_id">
<el-cascader v-model="form.parent_id" :options="dic" :props="dicProps" :show-all-levels="false" clearable></el-cascader>
</el-form-item>
</el-form>
<template #footer>
@@ -25,8 +25,8 @@
return {
mode: "add",
titleMap: {
add: '新增字典',
edit: '编辑字典'
add: '新增字典分类',
edit: '编辑字典分类'
},
visible: false,
isSaveing: false,
@@ -34,7 +34,7 @@
id:"",
name: "",
code: "",
parentId: ""
parent_id: ""
},
rules: {
code: [
@@ -65,7 +65,7 @@
},
//获取字典列表
async getDic(){
var res = await this.$API.system.dic.tree.get();
var res = await this.$API.system.dictionary.category.get();
this.dic = res.data;
},
//表单提交方法
@@ -73,14 +73,21 @@
this.$refs.dialogForm.validate(async (valid) => {
if (valid) {
this.isSaveing = true;
var res = await this.$API.demo.post.post(this.form);
var res = {};
this.form.parent_id = this.form.parent_id ? this.form.parent_id : 0;
if(this.mode == 'add'){
res = await this.$API.system.dictionary.addcate.post(this.form);
}else{
res = await this.$API.system.dictionary.editcate.post(this.form);
}
this.isSaveing = false;
if(res.code == 200){
if(res.code == 1){
this.$emit('success', this.form, this.mode)
this.visible = false;
this.$message.success("操作成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
this.$alert(res.msg, "提示", {type: 'error'})
}
}
})
@@ -90,10 +97,7 @@
this.form.id = data.id
this.form.name = data.name
this.form.code = data.code
this.form.parentId = data.parentId
//可以和上面一样单个注入,也可以像下面一样直接合并进去
//Object.assign(this.form, data)
this.form.parent_id = data.parent_id
}
}
}
+317
View File
@@ -0,0 +1,317 @@
<template>
<el-container>
<el-aside width="300px" v-loading="showDicloading">
<el-container>
<el-header>
<el-input placeholder="输入关键字进行过滤" v-model="dicFilterText" clearable></el-input>
</el-header>
<el-main class="nopadding">
<el-tree ref="dic" class="menu" node-key="id" :data="dicList" :props="dicProps" :highlight-current="true" :expand-on-click-node="false" :filter-node-method="dicFilterNode" @node-click="dicClick">
<template #default="{node, data}">
<span class="custom-tree-node">
<span class="label">{{ node.label }}</span>
<span class="code">{{ data.code }}</span>
<span class="do">
<el-icon :size="26" @click.stop="dicEdit(data)"><el-icon-edit /></el-icon>
<el-icon :size="26" @click.stop="dicDel(node, data)"><el-icon-delete /></el-icon>
</span>
</span>
</template>
</el-tree>
</el-main>
<el-footer style="height:51px;">
<el-button type="primary" size="small" icon="el-icon-plus" style="width: 100%;" @click="addDic">字典分类</el-button>
</el-footer>
</el-container>
</el-aside>
<el-container class="is-vertical">
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="addInfo"></el-button>
<el-button type="danger" plain icon="el-icon-delete" :disabled="selection.length==0" @click="batch_del"></el-button>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="listApi" row-key="id" :params="listApiParams" @selection-change="selectionChange" stripe :paginationLayout="'prev, pager, next'">
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="" width="60">
<template #default>
<el-tag class="move" style="cursor: move;"><el-icon-d-caret style="width: 1em; height: 1em;"/></el-tag>
</template>
</el-table-column>
<el-table-column label="名称" prop="name" width="150"></el-table-column>
<el-table-column label="键值" prop="key" width="150"></el-table-column>
<el-table-column label="是否有效" prop="yx" width="100">
<template #default="scope">
<el-switch v-model="scope.row.status" @change="changeSwitch($event, scope.row)" :loading="scope.row.$switch_status" active-value="1" inactive-value="0"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" align="right" width="140">
<template #default="scope">
<el-button type="text" size="small" @click="table_edit(scope.row, scope.$index)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
<template #reference>
<el-button type="text" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
</el-container>
<dic-dialog v-if="dialog.dic" ref="dicDialog" @success="handleDicSuccess" @closed="dialog.dic=false"></dic-dialog>
<list-dialog v-if="dialog.list" ref="listDialog" @success="handleListSuccess" @closed="dialog.list=false"></list-dialog>
</template>
<script>
import dicDialog from './dic'
import listDialog from './list'
import Sortable from 'sortablejs'
export default {
name: 'dic',
components: {
dicDialog,
listDialog
},
data() {
return {
dialog: {
dic: false,
info: false
},
showDicloading: true,
dicList: [],
dicFilterText: '',
dicProps: {
label: 'name'
},
listApi: this.$API.system.dictionary.list,
listApiParams: {},
selection: []
}
},
watch: {
dicFilterText(val) {
this.$refs.dic.filter(val);
}
},
mounted() {
this.getDic()
this.rowDrop()
},
methods: {
//加载树数据
async getDic(){
var res = await this.$API.system.dictionary.category.get();
this.showDicloading = false;
this.dicList = res.data;
//获取第一个节点,设置选中 & 加载明细列表
var firstNode = this.dicList[0];
if(firstNode){
this.$nextTick(() => {
this.$refs.dic.setCurrentKey(firstNode.id)
})
this.listApiParams = {
code: firstNode.code
}
this.$refs.table.reload(this.listApiParams);
}
},
//树过滤
dicFilterNode(value, data){
if (!value) return true;
var targetText = data.name + data.code;
return targetText.indexOf(value) !== -1;
},
//树增加
addDic(){
this.dialog.dic = true
this.$nextTick(() => {
this.$refs.dicDialog.open()
})
},
//编辑树
dicEdit(data){
this.dialog.dic = true
this.$nextTick(() => {
var editNode = this.$refs.dic.getNode(data.id);
var editNodeParentId = editNode.level==1?undefined:editNode.parent.data.id
data.parent_id = editNodeParentId
this.$refs.dicDialog.open('edit').setData(data)
})
},
//树点击事件
dicClick(data){
this.$refs.table.reload({
code: data.code
})
},
//删除树
dicDel(node, data){
this.$confirm(`确定删除 ${data.name} 项吗?`, '提示', {
type: 'warning'
}).then(() => {
this.showDicloading = true;
this.$API.system.dictionary.delCate.post({id: data.id});
//删除节点是否为高亮当前 是的话 设置第一个节点高亮
var dicCurrentKey = this.$refs.dic.getCurrentKey();
this.$refs.dic.remove(data.id)
if(dicCurrentKey == data.id){
var firstNode = this.dicList[0];
if(firstNode){
this.$refs.dic.setCurrentKey(firstNode.id);
this.$refs.table.upData({
code: firstNode.code
})
}else{
this.listApi = null;
this.$refs.table.tableData = []
}
}
this.showDicloading = false;
this.$message.success("操作成功")
}).catch(() => {
})
},
//行拖拽
rowDrop(){
const _this = this
const tbody = this.$refs.table.$el.querySelector('.el-table__body-wrapper tbody')
Sortable.create(tbody, {
handle: ".move",
animation: 300,
ghostClass: "ghost",
onEnd({ newIndex, oldIndex }) {
const tableData = _this.$refs.table.tableData
const currRow = tableData.splice(oldIndex, 1)[0]
tableData.splice(newIndex, 0, currRow)
_this.$message.success("排序成功")
}
})
},
//添加明细
addInfo(){
this.dialog.list = true
this.$nextTick(() => {
var dicCurrentKey = this.$refs.dic.getCurrentKey();
const data = {
dic: dicCurrentKey
}
this.$refs.listDialog.open().setData(data)
})
},
//编辑明细
table_edit(row){
this.dialog.list = true
this.$nextTick(() => {
this.$refs.listDialog.open('edit').setData(row)
})
},
//删除明细
async table_del(row, index){
var reqData = {id: row.id}
var res = await this.$API.system.dictionary.delete.post(reqData);
if(res.code == 200){
this.$refs.table.tableData.splice(index, 1);
this.$message.success("删除成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
},
//批量删除
async batch_del(){
this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
type: 'warning'
}).then(() => {
const loading = this.$loading();
this.selection.forEach(item => {
this.$refs.table.tableData.forEach((itemI, indexI) => {
if (item.id === itemI.id) {
this.$refs.table.tableData.splice(indexI, 1)
}
})
})
loading.close();
this.$message.success("操作成功")
}).catch(() => {
})
},
//提交明细
saveList(){
this.$refs.listDialog.submit(async (formData) => {
this.isListSaveing = true;
var res = await this.$API.system.dictionary.post.post(formData);
this.isListSaveing = false;
if(res.code == 1){
//这里选择刷新整个表格 OR 插入/编辑现有表格数据
this.listDialogVisible = false;
this.$message.success("操作成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
})
},
//表格选择后回调事件
selectionChange(selection){
this.selection = selection;
},
//表格内开关事件
changeSwitch(val, row){
this.$API.system.dictionary.edit.post({id: row.id, status: val});
},
//本地更新数据
handleDicSuccess(data, mode){
if(mode=='add'){
if(this.dicList.length > 0){
this.$refs.table.upData({
code: data.code
})
}else{
this.listApiParams = {
code: data.code
}
this.listApi = this.$API.system.dictionary.list;
}
this.$refs.dic.append(data, data.parent_id)
this.$refs.dic.setCurrentKey(data.id)
}else if(mode=='edit'){
var editNode = this.$refs.dic.getNode(data.id);
//判断是否移动?
var editNodeParentId = editNode.level==1 ? undefined : editNode.parent.data.id
console.log(editNodeParentId)
if(editNodeParentId != data.parent_id){
var obj = editNode.data;
this.$refs.dic.remove(data.id)
this.$refs.dic.append(obj, data.parent_id)
}
Object.assign(editNode.data, data)
this.$refs.table.refresh()
}
},
//本地更新数据
handleListSuccess(){
this.$refs.table.refresh()
}
}
}
</script>
<style scoped>
.custom-tree-node {display: flex;flex: 1;align-items: center;justify-content: space-between;font-size: 14px;padding-right: 24px;height:100%;}
.custom-tree-node .code {font-size: 12px;color: #999;}
.custom-tree-node .do {display: none;}
.custom-tree-node .do i {margin-left:5px;color: #999;padding:5px;}
.custom-tree-node .do i:hover {color: #333;}
.custom-tree-node:hover .code {display: none;}
.custom-tree-node:hover .do {display: inline-block;}
</style>
+117
View File
@@ -0,0 +1,117 @@
<template>
<el-dialog :title="titleMap[mode]" v-model="visible" :width="400" destroy-on-close @closed="$emit('closed')">
<el-form :model="form" :rules="rules" ref="dialogForm" label-width="100px" label-position="left">
<el-form-item label="所属字典" prop="dic_type">
<el-cascader v-model="form.dic_type" :options="dic" :props="dicProps" :show-all-levels="false" clearable></el-cascader>
</el-form-item>
<el-form-item label="项名称" prop="name">
<el-input v-model="form.name" clearable></el-input>
</el-form-item>
<el-form-item label="键值" prop="key">
<el-input v-model="form.key" clearable></el-input>
</el-form-item>
<el-form-item label="是否有效" prop="status">
<el-switch v-model="form.status" active-value="1" inactive-value="0"></el-switch>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible=false" >取 消</el-button>
<el-button type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
emits: ['success', 'closed'],
data() {
return {
mode: "add",
titleMap: {
add: '新增项',
edit: '编辑项'
},
visible: false,
isSaveing: false,
form: {
id: "",
dic_type: "",
name: "",
key: "",
status: "1"
},
rules: {
dic_type: [
{required: true, message: '请选择所属字典'}
],
name: [
{required: true, message: '请输入项名称'}
],
key: [
{required: true, message: '请输入键值'}
]
},
dic: [],
dicProps: {
value: "code",
label: "name",
emitPath: false,
checkStrictly: true
}
}
},
mounted() {
if(this.params){
this.form.dic = this.params.code
}
this.getDic()
},
methods: {
//显示
open(mode='add'){
this.mode = mode;
this.visible = true;
return this;
},
//获取字典列表
async getDic(){
var res = await this.$API.system.dictionary.category.get();
this.dic = res.data;
},
//表单提交方法
submit(){
this.$refs.dialogForm.validate(async (valid) => {
if (valid) {
this.isSaveing = true;
var res;
if(this.mode == 'add'){
res = await this.$API.system.dictionary.add.post(this.form);
}else{
res = await this.$API.system.dictionary.edit.post(this.form);
}
this.isSaveing = false;
if(res.code == 1){
this.$emit('success', this.form, this.mode)
this.visible = false;
this.$message.success("操作成功")
}else{
this.$alert(res.msg, "提示", {type: 'error'})
}
}
})
},
//表单注入数据
setData(data){
this.form.id = data.id
this.form.name = data.name
this.form.key = data.key
this.form.status = data.status
this.form.dic_type = data.dic_type
return this;
}
}
}
</script>
<style>
</style>
+103
View File
@@ -0,0 +1,103 @@
<template>
<el-container>
<el-aside width="220px">
<el-tree ref="category" class="menu" node-key="label" :data="category" :default-expanded-keys="['系统日志']" current-node-key="系统日志" :highlight-current="true" :expand-on-click-node="false" @node-click="groupClick">
</el-tree>
</el-aside>
<el-container>
<el-main class="nopadding">
<el-container>
<el-header>
<div class="left-panel">
<el-date-picker v-model="date" type="datetimerange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</div>
<div class="right-panel">
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="apiObj" stripe highlightCurrentRow @row-click="rowClick">
<el-table-column label="级别" prop="code" width="60">
<template #default="scope">
<el-icon v-if="scope.row.code=='500'" style="color: #F56C6C;"><el-icon-circle-close-filled /></el-icon>
<el-icon v-if="scope.row.code!='200' && scope.row.code!='500'" style="color: #E6A23C;"><el-icon-warning-filled /></el-icon>
<el-icon v-if="scope.row.code=='200'" style="color: #409EFF;"><el-icon-info-filled /></el-icon>
</template>
</el-table-column>
<el-table-column label="ID" prop="id" width="100"></el-table-column>
<el-table-column label="日志名" prop="title" width="150"></el-table-column>
<el-table-column label="请求接口" prop="route" width="220"></el-table-column>
<el-table-column label="请求方法" prop="method" width="150"></el-table-column>
<el-table-column label="用户" prop="nickname" width="150"></el-table-column>
<el-table-column label="客户端IP" prop="client_ip" width="220"></el-table-column>
<el-table-column label="日志时间" prop="create_time" width="170"></el-table-column>
</scTable>
</el-main>
</el-container>
</el-main>
</el-container>
</el-container>
<el-drawer v-model="infoDrawer" title="日志详情" :size="600" destroy-on-close>
<info ref="info"></info>
</el-drawer>
</template>
<script>
import info from './info'
export default {
name: 'log',
components: {
info
},
data() {
return {
infoDrawer: false,
category: [
{
label: '系统日志',
children: [
{label: 'GET', name: 'get'},
{label: 'POST', name: 'post'},
{label: 'DELETE', name: 'delete'},
{label: 'PUT', name: 'put'}
]
}
// ,{
// label: '应用日志',
// children: [
// {label: 'selfHelp'},
// {label: 'WechatApp'}
// ]
// }
],
date: [],
apiObj: this.$API.system.log.list,
search: {
keyword: ""
}
}
},
methods: {
upsearch(){
},
rowClick(row){
this.infoDrawer = true
this.$nextTick(() => {
this.$refs.info.setData(row)
})
},
groupClick(data){
var params = {
method: data.name || 0
}
this.$refs.table.reload(params)
}
}
}
</script>
<style>
</style>
+47
View File
@@ -0,0 +1,47 @@
<template>
<el-main style="padding:0 20px;">
<el-descriptions :column="1" border size="small">
<el-descriptions-item label="请求接口">{{data.route}}</el-descriptions-item>
<el-descriptions-item label="请求方法">{{data.method}}</el-descriptions-item>
<el-descriptions-item label="状态代码">{{data.code}}</el-descriptions-item>
<el-descriptions-item label="日志名">{{data.title}}</el-descriptions-item>
<el-descriptions-item label="日志时间">{{data.create_time}}</el-descriptions-item>
</el-descriptions>
<el-collapse v-model="activeNames" style="margin-top: 20px;">
<el-collapse-item title="请求参数" name="1">
<div class="code">{{data.params}}</div>
</el-collapse-item>
<el-collapse-item title="详细" name="2">
<div class="code">
{{data.browser}}
</div>
</el-collapse-item>
</el-collapse>
</el-main>
</template>
<script>
export default {
data() {
return {
data: {},
activeNames: ['1'],
typeMap: {
'info': "info",
'warn': "warning",
'error': "error"
}
}
},
methods: {
setData(data){
this.data = data
}
}
}
</script>
<style scoped>
.code {background: #848484;padding:15px;color: #fff;font-size: 12px;border-radius: 4px;}
</style>
+162
View File
@@ -0,0 +1,162 @@
<template>
<el-container>
<el-aside width="300px" v-loading="menuloading">
<el-container>
<el-header>
<el-input placeholder="输入关键字进行过滤" v-model="menuFilterText" clearable></el-input>
</el-header>
<el-main class="nopadding">
<el-tree ref="menu" class="menu" node-key="id" :data="menuList" :props="menuProps" draggable highlight-current :expand-on-click-node="false" check-strictly show-checkbox :filter-node-method="menuFilterNode" @node-click="menuClick" @node-drop="nodeDrop">
<template #default="{node, data}">
<span class="custom-tree-node el-tree-node__label">
<span class="label">
{{ node.label }}
</span>
<span class="do">
<el-icon @click.stop="add(node, data)"><el-icon-plus /></el-icon>
</span>
</span>
</template>
</el-tree>
</el-main>
<el-footer style="height:51px;">
<el-button type="primary" size="small" icon="el-icon-plus" @click="add()"></el-button>
<el-button type="danger" size="small" plain icon="el-icon-delete" @click="delMenu"></el-button>
</el-footer>
</el-container>
</el-aside>
<el-container>
<el-main class="nopadding" style="padding:20px;" ref="main">
<save ref="save" :menu="menuList"></save>
</el-main>
</el-container>
</el-container>
</template>
<script>
let newMenuIndex = 1;
import save from './save'
export default {
name: "settingMenu",
components: {
save
},
data(){
return {
menuloading: false,
menuList: [],
menuProps: {
label: (data)=>{
return data.title
}
},
menuFilterText: ""
}
},
watch: {
menuFilterText(val){
this.$refs.menu.filter(val);
}
},
mounted() {
this.getMenu();
},
methods: {
//加载树数据
async getMenu(){
this.menuloading = true
var res = await this.$API.system.menu.list.get();
this.menuloading = false
this.menuList = res.data;
},
//树点击
menuClick(data, node){
var pid = node.level==1?undefined:node.parent.data.id;
this.$refs.save.setData(data, pid)
this.$refs.main.$el.scrollTop = 0
},
//树过滤
menuFilterNode(value, data){
if (!value) return true;
var targetText = data.meta.title;
return targetText.indexOf(value) !== -1;
},
//树拖拽
nodeDrop(draggingNode, dropNode, dropType){
this.$refs.save.setData({})
this.$message(`拖拽对象:${draggingNode.data.meta.title}, 释放对象:${dropNode.data.meta.title}, 释放对象的位置:${dropType}`)
},
//增加
async add(node, data){
var newMenuName = "新菜单" + newMenuIndex++;
var newMenuData = {
parent_id: data ? data.id : "",
name: newMenuName,
path: "",
component: "",
title: newMenuName,
type: "menu"
}
this.menuloading = true
var res = await this.$API.system.menu.add.post(newMenuData)
this.menuloading = false
newMenuData.id = res.data.id
this.$refs.menu.append(newMenuData, node)
this.$refs.menu.setCurrentKey(newMenuData.id)
var pid = node ? node.data.id : ""
this.$refs.save.setData(newMenuData, pid)
},
//删除菜单
async delMenu(){
var CheckedNodes = this.$refs.menu.getCheckedNodes()
if(CheckedNodes.length == 0){
this.$message.warning("请选择需要删除的项")
return false;
}
var confirm = await this.$confirm('确认删除已选择的菜单吗?','提示', {
type: 'warning',
confirmButtonText: '删除',
confirmButtonClass: 'el-button--danger'
}).catch(() => {})
if(confirm != 'confirm'){
return false
}
this.menuloading = true
var reqData = {
ids: CheckedNodes.map(item => item.id)
}
var res = await this.$API.system.menu.delete.post(reqData)
this.menuloading = false
if(res.code == 1){
CheckedNodes.forEach(item => {
var node = this.$refs.menu.getNode(item)
if(node.isCurrent){
this.$refs.save.setData({})
}
this.$refs.menu.remove(item)
})
}else{
this.$message.warning(res.msg)
}
}
}
}
</script>
<style scoped>
.custom-tree-node {display: flex;flex: 1;align-items: center;justify-content: space-between;font-size: 14px;padding-right: 24px;height:100%;}
.custom-tree-node .label {display: flex;align-items: center;;height: 100%;}
.custom-tree-node .label .el-tag {margin-left: 5px;}
.custom-tree-node .do {display: none;}
.custom-tree-node .do i {margin-left:5px;color: #999;}
.custom-tree-node .do i:hover {color: #333;}
.custom-tree-node:hover .do {display: inline-block;}
</style>
@@ -7,14 +7,14 @@
<el-col :lg="12">
<h2>{{form.meta.title || "新增菜单"}}</h2>
<el-form :model="form" :rules="rules" ref="dialogForm" label-width="80px" label-position="left">
<el-form-item label="显示名称" prop="meta.title">
<el-input v-model="form.meta.title" clearable placeholder="菜单显示名字"></el-input>
<el-form-item label="显示名称" prop="title">
<el-input v-model="form.title" clearable placeholder="菜单显示名字"></el-input>
</el-form-item>
<el-form-item label="上级菜单" prop="parentId">
<el-cascader v-model="form.parentId" :options="menuOptions" :props="menuProps" :show-all-levels="false" placeholder="顶级菜单" clearable disabled></el-cascader>
</el-form-item>
<el-form-item label="类型" prop="meta.type">
<el-radio-group v-model="form.meta.type">
<el-form-item label="类型" prop="type">
<el-radio-group v-model="form.type">
<el-radio-button label="menu">菜单</el-radio-button>
<el-radio-button label="iframe">Iframe</el-radio-button>
<el-radio-button label="link">外链</el-radio-button>
@@ -25,8 +25,8 @@
<el-input v-model="form.name" clearable placeholder="菜单别名"></el-input>
<div class="el-form-item-msg">系统唯一且与内置组件名一致,否则导致缓存失效。如类型为Iframe的菜单,别名将代替源地址显示在地址栏</div>
</el-form-item>
<el-form-item label="菜单图标" prop="meta.icon">
<sc-icon-select v-model="form.meta.icon" clearable></sc-icon-select>
<el-form-item label="菜单图标" prop="icon">
<sc-icon-select v-model="form.icon" clearable></sc-icon-select>
</el-form-item>
<el-form-item label="路由地址" prop="path">
<el-input v-model="form.path" clearable placeholder=""></el-input>
@@ -34,25 +34,32 @@
<el-form-item label="重定向" prop="redirect">
<el-input v-model="form.redirect" clearable placeholder=""></el-input>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" class="mx-4" :min="0" :max="100" controls-position="right" clearable />
</el-form-item>
<el-form-item label="菜单高亮" prop="active">
<el-input v-model="form.active" clearable placeholder=""></el-input>
<div class="el-form-item-msg">子节点或详情页需要高亮的上级菜单路由地址</div>
</el-form-item>
<el-form-item label="视图" prop="component">
<el-input v-model="form.component" clearable placeholder="">
<template #prepend>views/</template>
<template #prepend>pages/</template>
</el-input>
<div class="el-form-item-msg">如父节点、链接或Iframe等没有视图的菜单不需要填写</div>
</el-form-item>
<el-form-item label="颜色" prop="color">
<el-color-picker v-model="form.meta.color" :predefine="predefineColors"></el-color-picker>
<el-color-picker v-model="form.color" :predefine="predefineColors"></el-color-picker>
</el-form-item>
<el-form-item label="是否隐藏" prop="meta.hidden">
<el-checkbox v-model="form.meta.hidden">隐藏菜单</el-checkbox>
<el-checkbox v-model="form.meta.hiddenBreadcrumb">隐藏面包屑</el-checkbox>
<el-form-item label="是否隐藏" prop="hidden">
<el-checkbox v-model="form.hidden">隐藏菜单</el-checkbox>
<el-checkbox v-model="form.hiddenBreadcrumb">隐藏面包屑</el-checkbox>
<div class="el-form-item-msg">菜单不显示在导航中,但用户依然可以访问,例如详情页</div>
</el-form-item>
<el-form-item label="是否固定" prop="affix">
<el-switch v-model="form.affix" />
<div class="el-form-item-msg">是否固定,类似首页控制台在标签中是没有关闭按钮的</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="save" :loading="loading">保 存</el-button>
</el-form-item>
@@ -98,13 +105,13 @@
path: "",
component: "",
redirect: "",
meta:{
title: "",
icon: "",
active: "",
color: "",
type: "menu"
},
sort: 0,
title: "",
icon: "",
active: "",
color: "",
type: "menu",
affix: false,
apiList: []
},
menuOptions: [],
@@ -149,7 +156,7 @@
var obj = {
id: item.id,
parentId: item.parentId,
title: item.meta.title,
title: item.title,
children: item.children&&item.children.length>0 ? this.treeToMap(item.children) : null
}
map.push(obj)
@@ -159,19 +166,27 @@
//保存
async save(){
this.loading = true
var res = await this.$API.demo.post.post(this.form)
this.loading = false
if(res.code == 200){
this.$message.success("保存成功")
let res = {};
this.form.parent_id = this.form.parent_id ? this.form.parent_id : 0;
if(this.form.id){
res = await this.$API.system.menu.edit.post(this.form)
}else{
this.$message.warning(res.message)
res = await this.$API.system.menu.add.post(this.form)
}
this.loading = false
if(res.code == 1){
this.$message.success("保存成功")
this.$TOOL.data.set("MENU", res.data)
}else{
this.$message.warning(res.msg)
}
},
//表单注入数据
setData(data, pid){
this.form = data
this.form.apiList = data.apiList || []
this.form.parentId = pid
this.form.parent_id = pid
}
}
}
+160
View File
@@ -0,0 +1,160 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add"></el-button>
<el-button type="danger" plain icon="el-icon-delete" :disabled="selection.length==0" @click="batch_del"></el-button>
<el-button type="primary" plain :disabled="selection.length!=1" @click="permission">权限设置</el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<el-input v-model="search.keyword" placeholder="角色名称" clearable></el-input>
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="apiObj" row-key="id" @selection-change="selectionChange" hidePagination>
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="#" type="index" width="50"></el-table-column>
<el-table-column label="角色名称" prop="title" width="250"></el-table-column>
<el-table-column label="别名" prop="identify" width="150"></el-table-column>
<el-table-column label="排序" prop="sort" width="150"></el-table-column>
<el-table-column label="操作" fixed="right" align="right" width="140">
<template #default="scope">
<el-button type="text" size="small" @click="table_show(scope.row, scope.$index)">查看</el-button>
<el-divider direction="vertical"></el-divider>
<el-button type="text" size="small" @click="table_edit(scope.row, scope.$index)">编辑</el-button>
<el-divider direction="vertical"></el-divider>
<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
<template #reference>
<el-button type="text" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<save-dialog v-if="dialog.save" ref="saveDialog" @success="handleSaveSuccess" @closed="dialog.save=false"></save-dialog>
<permission-dialog v-if="dialog.permission" ref="permissionDialog" @closed="dialog.permission=false" @success="permissionSuccess"></permission-dialog>
</template>
<script>
import saveDialog from './save'
import permissionDialog from './permission'
export default {
name: 'role',
components: {
saveDialog,
permissionDialog
},
data() {
return {
dialog: {
save: false,
permission: false
},
apiObj: this.$API.user.role.list,
selection: [],
search: {
keyword: null
}
}
},
methods: {
//添加
add(){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open()
})
},
//编辑
table_edit(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('edit').setData(row)
})
},
//查看
table_show(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('show').setData(row)
})
},
//权限设置
permission(){
this.dialog.permission = true
this.$nextTick(() => {
this.$refs.permissionDialog.open().setData(this.selection)
})
},
//删除
async table_del(row){
var reqData = {id: row.id}
var res = await this.$API.user.role.delete.post(reqData);
if(res.code == 1){
this.$refs.table.refresh()
this.$message.success("删除成功")
}else{
this.$alert(res.msg, "提示", {type: 'error'})
}
},
//批量删除
async batch_del(){
this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?如果删除项中含有子集将会被一并删除`, '提示', {
type: 'warning'
}).then(() => {
const loading = this.$loading();
this.$refs.table.refresh()
loading.close();
this.$message.success("操作成功")
}).catch(() => {
})
},
//表格选择后回调事件
selectionChange(selection){
this.selection = selection;
},
//搜索
upsearch(){
this.$refs.table.upData(this.search)
},
//根据ID获取树结构
filterTree(id){
var target = null;
function filter(tree){
tree.forEach(item => {
if(item.id == id){
target = item
}
if(item.children){
filter(item.children)
}
})
}
filter(this.$refs.table.tableData)
return target
},
//本地更新数据
handleSaveSuccess(data, mode){
if(mode=='add'){
this.$refs.table.refresh()
}else if(mode=='edit'){
this.$refs.table.refresh()
}
},
permissionSuccess(){
this.$refs.table.refresh()
}
}
}
</script>
+144
View File
@@ -0,0 +1,144 @@
<template>
<el-dialog title="角色权限设置" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
<el-tabs tab-position="top">
<el-tab-pane label="菜单权限">
<div class="treeMain">
<el-tree ref="menu" node-key="id" :data="menu.list" :default-checked-keys="menu.checked" :props="menu.props" check-strictly show-checkbox></el-tree>
</div>
</el-tab-pane>
<el-tab-pane label="数据权限">
<el-form label-width="100px" label-position="left">
<el-form-item label="数据权限">
<sc-select dic="data_auth" v-model="form.data_range"></sc-select>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="移动端功能">
<el-form label-width="100px" label-position="left">
<el-form-item label="移动端功能">
<el-select v-model="form.mobile_module" multiple placeholder="请选择移动端模块" >
<el-option v-for="item in mobileOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="控制台">
<el-form label-width="100px" label-position="left">
<el-form-item label="控制台视图">
<el-select v-model="form.dashboard" placeholder="请选择">
<el-option v-for="item in dashboardOptions" :key="item.value" :label="item.label" :value="item.value">
<span style="float: left">{{ item.label }}</span>
<span style="float: right; color: #8492a6; font-size: 12px">{{ item.views }}</span>
</el-option>
</el-select>
<div class="el-form-item-msg">用于控制角色登录后控制台的视图</div>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="visible=false" >取 消</el-button>
<el-button type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
emits: ['success', 'closed'],
data() {
return {
visible: false,
isSaveing: false,
menu: {
list: [],
checked: [],
props: {
label: (data)=>{
return data.title
}
}
},
group: {
list: [],
checked: [],
props: {}
},
type: {
list: [],
checked: [],
props: {}
},
form: {
role_id: 0,
auth: [],
data_range: "",
dashboard: "work",
},
mobileOptions: [
{value: 'leave', label: '请假申请'},
{value: 'visit', label: '来访提交'},
{value: 'permit', label: '通行设置'},
{value: 'stats', label: '数据统计'}
],
dashboardOptions: [
{
value: '0',
label: '数据统计',
views: 'stats'
},
{
value: '1',
label: '工作台',
views: 'work'
},
]
}
},
mounted() {
this.getMenu();
},
methods: {
open(){
this.visible = true;
return this;
},
async submit(){
let authSelect = this.$refs.menu.getCheckedNodes();
this.form.auth = [];
authSelect.map(item => {
this.form.auth.push(item.id);
})
this.isSaveing = true;
var res = await this.$API.user.role.auth.post(this.form);
this.isSaveing = false;
if(res.code == 1){
this.$emit('success', this.form)
this.visible = false;
this.$message.success("操作成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
},
async getMenu(){
var res = await this.$API.system.menu.list.get();
this.menu.list = res.data;
},
//表单注入数据
setData(data){
data.map(item => {
this.form.role_id = item.id;
this.form.data_range = parseInt(item.data_range);
this.form.dashboard = item.dashboard;
this.form.mobile_module = item.mobile_module;
this.menu.checked = item.permission_id;
})
}
}
}
</script>
<style scoped>
.treeMain {height:280px;overflow: auto;border: 1px solid #dcdfe6;margin-bottom: 10px;}
</style>
@@ -1,14 +1,14 @@
<template>
<el-dialog :title="titleMap[mode]" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
<el-form :model="form" :rules="rules" :disabled="mode=='show'" ref="dialogForm" label-width="100px" label-position="left">
<el-form-item label="上级角色" prop="parentId">
<el-cascader v-model="form.parentId" :options="groups" :props="groupsProps" :show-all-levels="false" clearable style="width: 100%;"></el-cascader>
<el-form-item label="上级角色" prop="parent_id">
<el-cascader v-model="form.parent_id" :options="groups" :props="groupsProps" :show-all-levels="false" clearable style="width: 100%;"></el-cascader>
</el-form-item>
<el-form-item label="角色名称" prop="label">
<el-input v-model="form.label" clearable></el-input>
<el-form-item label="角色名称" prop="title">
<el-input v-model="form.title" clearable></el-input>
</el-form-item>
<el-form-item label="角色别名" prop="alias">
<el-input v-model="form.alias" clearable></el-input>
<el-form-item label="角色别名" prop="identify">
<el-input v-model="form.identify" clearable></el-input>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" :min="1" style="width: 100%;"></el-input-number>
@@ -37,10 +37,10 @@
//表单数据
form: {
id:"",
label: "",
alias: "",
title: "",
identify: "",
sort: 1,
parentId: ""
parent_id: 0
},
//验证规则
rules: {
@@ -58,6 +58,7 @@
groups: [],
groupsProps: {
value: "id",
label: "title",
emitPath: false,
checkStrictly: true
}
@@ -75,7 +76,7 @@
},
//加载树数据
async getGroup(){
var res = await this.$API.system.role.list.get();
var res = await this.$API.user.role.list.get();
this.groups = res.data;
},
//表单提交方法
@@ -83,14 +84,20 @@
this.$refs.dialogForm.validate(async (valid) => {
if (valid) {
this.isSaveing = true;
var res = await this.$API.demo.post.post(this.form);
var res = {}
this.form.parent_id = this.form.parent_id ? this.form.parent_id : 0;
if(this.mode == 'add'){
res = await this.$API.user.role.add.post(this.form);
}else{
res = await this.$API.user.role.edit.post(this.form);
}
this.isSaveing = false;
if(res.code == 200){
if(res.code == 1){
this.$emit('success', this.form, this.mode)
this.visible = false;
this.$message.success("操作成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
this.$alert(res.msg, "提示", {type: 'error'})
}
}
})
@@ -98,10 +105,10 @@
//表单注入数据
setData(data){
this.form.id = data.id
this.form.label = data.label
this.form.alias = data.alias
this.form.title = data.title
this.form.identify = data.identify
this.form.sort = data.sort
this.form.parentId = data.parentId
this.form.parent_id = data.parent_id
//可以和上面一样单个注入,也可以像下面一样直接合并进去
//Object.assign(this.form, data)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+200
View File
@@ -0,0 +1,200 @@
<template>
<el-container>
<el-aside width="200px" v-loading="showGrouploading">
<el-container>
<el-header>
<el-input placeholder="输入关键字进行过滤" v-model="groupFilterText" clearable></el-input>
</el-header>
<el-main class="nopadding">
<el-tree ref="group" class="menu" node-key="id" :data="group" :props="{label: 'title'}" :current-node-key="''" :highlight-current="true" :expand-on-click-node="false" :filter-node-method="groupFilterNode" @node-click="groupClick"></el-tree>
</el-main>
</el-container>
</el-aside>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add"></el-button>
<el-button type="danger" plain icon="el-icon-delete" :disabled="selection.length==0" @click="batch_del"></el-button>
<el-button type="primary" plain :disabled="selection.length!=1" @click="roleSet">分配角色</el-button>
<el-button type="primary" plain @click="insertData" v-if="false">数据导入</el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<el-input v-model="search.name" placeholder="登录账号 / 姓名" clearable></el-input>
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="apiObj" @selection-change="selectionChange" stripe remoteSort remoteFilter>
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="头像" width="80" column-key="avatar" :filters="[{text: '已上传', value: '1'}, {text: '未上传', value: '0'}]">
<template #default="scope">
<el-avatar :src="scope.row.avatar" shape="square" size="small"></el-avatar>
</template>
</el-table-column>
<el-table-column label="UID" prop="uid" width="150" sortable='custom'></el-table-column>
<el-table-column label="登录账号" prop="username" width="150" sortable='custom' column-key="username" :filters="[{text: '系统账号', value: '1'}, {text: '普通账号', value: '0'}]"></el-table-column>
<el-table-column label="姓名" prop="nickname" width="150" sortable='custom'></el-table-column>
<el-table-column label="所属角色" prop="roleName" width="200" sortable='custom'></el-table-column>
<el-table-column label="加入时间" prop="create_time" width="150" sortable='custom'></el-table-column>
<el-table-column label="操作" fixed="right" align="right" width="140">
<template #default="scope">
<el-button type="text" size="small" @click="table_show(scope.row, scope.$index)">查看</el-button>
<el-button type="text" size="small" @click="table_edit(scope.row, scope.$index)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
<template #reference>
<el-button type="text" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
</el-container>
<save-dialog v-if="dialog.save" ref="saveDialog" @success="handleSuccess" @closed="dialog.save=false"></save-dialog>
<role-dialog v-if="dialog.role" ref="roleDialog" @success="handleSuccess" @closed="dialog.role=false"></role-dialog>
</template>
<script>
import saveDialog from './save'
import roleDialog from './role'
export default {
name: 'user',
components: {
saveDialog,
roleDialog
},
data() {
return {
dialog: {
save: false
},
showGrouploading: false,
groupFilterText: '',
group: [],
apiObj: this.$API.user.list,
selection: [],
search: {
name: null
}
}
},
watch: {
groupFilterText(val) {
this.$refs.group.filter(val);
}
},
mounted() {
this.getGroup()
},
methods: {
//添加
add(){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open()
})
},
//编辑
table_edit(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('edit').setData(row)
})
},
//查看
table_show(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('show').setData(row)
})
},
//删除
async table_del(row, index){
var reqData = {id: row.uid}
var res = await this.$API.user.delete.post(reqData);
if(res.code == 200){
//这里选择刷新整个表格 OR 插入/编辑现有表格数据
this.$refs.table.tableData.splice(index, 1);
this.$message.success("删除成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
},
//批量删除
async batch_del(){
this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
type: 'warning'
}).then(() => {
const loading = this.$loading();
this.selection.forEach(item => {
this.$refs.table.tableData.forEach((itemI, indexI) => {
if (item.id === itemI.id) {
this.$refs.table.tableData.splice(indexI, 1)
}
})
})
loading.close();
this.$message.success("操作成功")
}).catch(() => {
})
},
//权限设置
roleSet(){
this.dialog.role = true
this.$nextTick(() => {
this.$refs.roleDialog.open().setData(this.selection)
})
},
insertData(){
this.dialog.insert = true
this.$nextTick(() => {
this.$refs.insertDialog.open()
})
},
//表格选择后回调事件
selectionChange(selection){
this.selection = selection;
},
//加载树数据
async getGroup(){
this.showGrouploading = true;
var res = await this.$API.user.department.list.get();
this.showGrouploading = false;
var allNode ={id: '', title: '所有'}
res.data.unshift(allNode);
this.group = res.data;
},
//树过滤
groupFilterNode(value, data){
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
//树点击事件
groupClick(data){
var params = {
department_id: data.id
}
this.$refs.table.reload(params)
},
//搜索
upsearch(){
this.$refs.table.upData(this.search)
},
//本地更新数据
handleSuccess(){
this.$refs.table.refresh()
}
}
}
</script>
<style>
</style>
+81
View File
@@ -0,0 +1,81 @@
<template>
<el-dialog title="角色设置" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
<el-tabs tab-position="top">
<el-tab-pane label="角色选择">
<div class="treeMain">
<el-tree ref="role" node-key="id" :data="role.list" :default-checked-keys="role.checked" :props="role.props" check-strictly default-expand-all show-checkbox></el-tree>
</div>
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="visible=false" >取 消</el-button>
<el-button type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
data(){
return {
visible: false,
isSaveing: false,
role: {
list: [],
checked: [],
props: {
label: (data)=>{
return data.title
}
}
},
form: {
uid: '',
role: []
}
}
},
mounted(){
this.getRole();
},
methods:{
open(){
this.visible = true;
return this;
},
async submit(){
this.isSaveing = true;
let role = this.$refs.role.getCheckedNodes();
this.form.role = [];
role.map(item => {
this.form.role.push(item.id);
})
this.isSaveing = true;
var res = await this.$API.user.uprole.post(this.form);
this.isSaveing = false;
if(res.code == 1){
this.$emit('success', this.form)
this.visible = false;
this.$message.success("操作成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
},
async getRole(){
let res = await this.$API.user.role.list.get();
this.role.list = res.data;
},
setData(data){
data.map(item => {
this.role.checked = item.role_id;
this.form.uid = item.uid;
})
}
}
}
</script>
<style scoped>
.treeMain {height:280px;overflow: auto;border: 1px solid #dcdfe6;margin-bottom: 10px;}
</style>
@@ -2,13 +2,13 @@
<el-dialog :title="titleMap[mode]" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
<el-form :model="form" :rules="rules" :disabled="mode=='show'" ref="dialogForm" label-width="100px" label-position="left">
<el-form-item label="头像" prop="avatar">
<sc-upload v-model="form.avatar" title="上传头像"></sc-upload>
<sc-upload v-model="form.avatar" :cropper="true" :aspectRatio="1" title="上传头像"></sc-upload>
</el-form-item>
<el-form-item label="登录账号" prop="userName">
<el-input v-model="form.userName" placeholder="用于登录系统" clearable></el-input>
<el-form-item label="登录账号" prop="username">
<el-input v-model="form.username" placeholder="用于登录系统" clearable></el-input>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入完整的真实姓名" clearable></el-input>
<el-form-item label="姓名" prop="nickname">
<el-input v-model="form.nickname" placeholder="请输入完整的真实姓名" clearable></el-input>
</el-form-item>
<template v-if="mode=='add'">
<el-form-item label="登录密码" prop="password">
@@ -18,8 +18,11 @@
<el-input type="password" v-model="form.password2" clearable show-password></el-input>
</el-form-item>
</template>
<el-form-item label="所属部门" prop="group">
<el-cascader v-model="form.department_id" :options="department" :props="departmentProps" :show-all-levels="false" clearable style="width: 100%;"></el-cascader>
</el-form-item>
<el-form-item label="所属角色" prop="group">
<el-cascader v-model="form.group" :options="groups" :props="groupsProps" :show-all-levels="false" clearable style="width: 100%;"></el-cascader>
<el-cascader v-model="form.role_id" :options="groups" :props="groupsProps" :show-all-levels="false" clearable style="width: 100%;"></el-cascader>
</el-form-item>
</el-form>
<template #footer>
@@ -48,17 +51,18 @@
userName: "",
avatar: "",
name: "",
group: ""
department_id: 0,
role_id: []
},
//验证规则
rules: {
avatar:[
{required: true, message: '请上传头像'}
],
userName: [
// avatar:[
// {required: true, message: '请上传头像'}
// ],
username: [
{required: true, message: '请输入登录账号'}
],
name: [
nickname: [
{required: true, message: '请输入真实姓名'}
],
password: [
@@ -80,14 +84,22 @@
}
}}
],
group: [
role_id: [
{required: true, message: '请选择所属角色'}
]
},
//所需数据选项
department: [],
departmentProps: {
value: "id",
label: "title",
multiple: false,
checkStrictly: true
},
groups: [],
groupsProps: {
value: "id",
label: "title",
multiple: true,
checkStrictly: true
}
@@ -95,6 +107,7 @@
},
mounted() {
this.getGroup()
this.getDepartment()
},
methods: {
//显示
@@ -105,17 +118,27 @@
},
//加载树数据
async getGroup(){
var res = await this.$API.system.role.list.get();
var res = await this.$API.user.role.list.get();
this.groups = res.data;
},
async getDepartment(){
var res = await this.$API.user.department.list.get();
this.department = res.data;
},
//表单提交方法
submit(){
this.$refs.dialogForm.validate(async (valid) => {
if (valid) {
this.isSaveing = true;
var res = await this.$API.demo.post.post(this.form);
var res = {};
if(this.mode == 'add'){
res = await this.$API.user.add.post(this.form);
}else{
res = await this.$API.user.edit.post(this.form);
}
this.isSaveing = false;
if(res.code == 200){
if(res.code == 1){
this.$emit('success', this.form, this.mode)
this.visible = false;
this.$message.success("操作成功")
@@ -129,14 +152,14 @@
},
//表单注入数据
setData(data){
this.form.id = data.id
this.form.userName = data.userName
this.form.avatar = data.avatar
this.form.name = data.name
this.form.group = data.group
// this.form.id = data.uid
// this.form.userName = data.username
// this.form.avatar = data.avatar
// this.form.name = data.nickname
// this.form.role_id = data.role_id
//可以和上面一样单个注入,也可以像下面一样直接合并进去
//Object.assign(this.form, data)
Object.assign(this.form, data)
}
}
}
+169
View File
@@ -0,0 +1,169 @@
<template>
<el-main>
<el-row :gutter="15">
<el-col :lg="8">
<el-card shadow="never">
<div class="user-info">
<div class="user-info-top">
<el-avatar :size="80" :src="userInfo.avatar"></el-avatar>
<h2>{{ userInfo.nickname||'-' }}</h2>
<p>{{ userInfo.about||'无签名' }}</p>
<el-button type="primary" round icon="el-icon-collection-tag" size="large">{{userInfo.username}}</el-button>
</div>
<div class="user-info-main">
<ul>
<li><label><el-icon><el-icon-user /></el-icon></label><span>{{userInfo.email||'暂无邮箱'}}</span></li>
<li><label><el-icon><el-icon-male /></el-icon></label><span>{{userInfo.email||'未知'}}</span></li>
<li><label><el-icon><el-icon-office-building /></el-icon></label><span>{{userInfo.department_name||'暂无部门'}}</span></li>
</ul>
</div>
<div class="user-info-bottom">
<h2>当前账号权限</h2>
<el-space wrap>
<el-tag v-auth="'user.add'">user.add</el-tag>
<el-tag v-auth="'user.edit'">user.edit</el-tag>
<el-tag v-if="$AUTH('user.delete')">user.delete</el-tag>
<el-tag v-if="$AUTH('list.add')">list.add</el-tag>
<el-tag v-if="$AUTH('list.edit')">list.edit</el-tag>
<el-tag v-if="$AUTH('list.delete')">list.delete</el-tag>
</el-space>
</div>
</div>
</el-card>
</el-col>
<el-col :lg="16">
<el-card shadow="never">
<el-tabs tab-position="top">
<el-tab-pane :label="$t('user.dynamic')">
<el-timeline style="margin-top:20px;padding-left:10px;">
<el-timeline-item v-for="(activity, index) in activities" :key="index" :timestamp="activity.timestamp" placement="top">
<div class="activity-item">
<el-avatar class="avatar" :size="24" src="img/avatar.jpg"></el-avatar>
<label>{{activity.operate}}</label><el-tag v-if="activity.mod">{{activity.mod}}</el-tag>{{activity.describe}}
</div>
</el-timeline-item>
</el-timeline>
</el-tab-pane>
<el-tab-pane :label="$t('user.info')">
<el-form ref="form" :model="userInfo" label-width="80px" style="width: 460px;margin-top:20px;">
<el-form-item label="账号">
<el-input v-model="userInfo.username" disabled></el-input>
<div class="el-form-item-msg">账号信息用于登录,系统不允许修改</div>
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="userInfo.nickname"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="userInfo.sex" placeholder="请选择">
<el-option label="保密" value="0"></el-option>
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="个性签名">
<el-input v-model="userInfo.about" type="textarea"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary">保存</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane :label="$t('user.settings')">
<el-form ref="form" :model="form" label-width="120px" style="margin-top:20px;">
<el-form-item :label="$t('user.nightmode')">
<el-switch v-model="config.theme" active-value="dark" inactive-value="default" inline-prompt active-icon="el-icon-moon" inactive-icon="el-icon-sunny"></el-switch>
<div class="el-form-item-msg">{{ $t('user.nightmode_msg') }}</div>
</el-form-item>
<el-form-item label="主题颜色">
<el-color-picker v-model="config.colorPrimary" :predefine="colorList">></el-color-picker>
</el-form-item>
<el-form-item :label="$t('user.language')">
<el-select v-model="config.lang">
<el-option label="简体中文" value="zh-cn"></el-option>
<el-option label="English" value="en"></el-option>
<el-option label="日本語" value="ja"></el-option>
</el-select>
<div class="el-form-item-msg">{{ $t('user.language_msg') }}</div>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
</el-row>
</el-main>
</template>
<script>
import colorTool from '@/utils/color'
export default {
name: 'userCenter',
data() {
return {
activities: [
{
operate: '更改了',
mod: '系统配置',
describe: 'systemName 为 SCUI',
type: 'edit',
timestamp: '刚刚'
}
],
userInfo: this.$TOOL.data.get("USER_INFO"),
colorList: ['#409EFF', '#009688', '#536dfe', '#ff5c93', '#c62f2f', '#fd726d'],
config: {
lang: this.$TOOL.data.get('APP_LANG') || this.$CONFIG.LANG,
theme: this.$TOOL.data.get('APP_THEME') || 'default',
colorPrimary: this.$TOOL.data.get('APP_COLOR') || this.$CONFIG.COLOR || '#409EFF'
}
}
},
watch:{
'config.theme'(val){
document.body.setAttribute('data-theme', val)
this.$TOOL.data.set("APP_THEME", val);
},
'config.lang'(val){
this.$i18n.locale = val
this.$TOOL.data.set("APP_LANG", val);
},
'config.colorPrimary'(val){
document.documentElement.style.setProperty('--el-color-primary', val);
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, colorTool.lighten(val,i/10));
}
document.documentElement.style.setProperty(`--el-color-primary-darken-1`, colorTool.darken(val,0.1));
this.$TOOL.data.set("APP_COLOR", val);
}
},
//路由跳转进来 判断from是否有特殊标识做特殊处理
beforeRouteEnter (to, from, next){
next((vm)=>{
if(from.is){
//删除特殊标识,防止标签刷新重复执行
delete from.is
//执行特殊方法
vm.$alert('路由跳转过来后含有特殊标识,做特殊处理', '提示', {
type: 'success',
center: true
}).then(() => {}).catch(() => {})
}
})
},
methods: {
}
}
</script>
<style scoped>
.el-card {margin-bottom:15px;}
.activity-item {font-size: 13px;color: #999;display: flex;align-items: center;}
.activity-item label {color: #333;margin-right:10px;}
.activity-item .el-avatar {margin-right:10px;}
.activity-item .el-tag {margin-right:10px;}
[data-theme='dark'] .user-info-bottom {border-color: var(--el-border-color-base);}
[data-theme='dark'] .activity-item label {color: #999;}
</style>
+247
View File
@@ -0,0 +1,247 @@
<template>
<div class="login_bg">
<div class="login_adv" style="background-image: url(img/auth_banner.jpg);">
<div class="login_adv__title">
<h2>ThinkVue</h2>
<h4>{{ $t('login.slogan') }}</h4>
<p>{{ $t('login.describe') }}</p>
<div>
<span>
<el-icon><sc-icon-vue /></el-icon>
</span>
<span>
<el-icon class="add"><el-icon-plus /></el-icon>
</span>
<span>
<el-icon><el-icon-eleme-filled /></el-icon>
</span>
</div>
</div>
<div class="login_adv__bottom">
© {{$CONFIG.APP_NAME}} {{$CONFIG.APP_VER}}
</div>
</div>
<div class="login_main">
<div class="login_config">
<el-button :icon="config.theme=='dark'?'el-icon-sunny':'el-icon-moon'" circle type="info" @click="configTheme"></el-button>
<el-dropdown trigger="click" placement="bottom-end" @command="configLang">
<el-button circle>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 512 512"><path d="M478.33 433.6l-90-218a22 22 0 0 0-40.67 0l-90 218a22 22 0 1 0 40.67 16.79L316.66 406h102.67l18.33 44.39A22 22 0 0 0 458 464a22 22 0 0 0 20.32-30.4zM334.83 362L368 281.65L401.17 362z" fill="currentColor"></path><path d="M267.84 342.92a22 22 0 0 0-4.89-30.7c-.2-.15-15-11.13-36.49-34.73c39.65-53.68 62.11-114.75 71.27-143.49H330a22 22 0 0 0 0-44H214V70a22 22 0 0 0-44 0v20H54a22 22 0 0 0 0 44h197.25c-9.52 26.95-27.05 69.5-53.79 108.36c-31.41-41.68-43.08-68.65-43.17-68.87a22 22 0 0 0-40.58 17c.58 1.38 14.55 34.23 52.86 83.93c.92 1.19 1.83 2.35 2.74 3.51c-39.24 44.35-77.74 71.86-93.85 80.74a22 22 0 1 0 21.07 38.63c2.16-1.18 48.6-26.89 101.63-85.59c22.52 24.08 38 35.44 38.93 36.1a22 22 0 0 0 30.75-4.9z" fill="currentColor"></path></svg>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in lang" :key="item.value" :command="item" :class="{'selected':config.lang==item.value}">{{item.name}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div class="login-form">
<div class="login-header">
<div class="logo">
<img :alt="$CONFIG.APP_NAME" src="img/logo.png">
<label>{{$CONFIG.APP_NAME}}</label>
</div>
<h2>{{ $t('login.signInTitle') }}</h2>
</div>
<el-form ref="loginForm" :model="ruleForm" :rules="rules" label-width="0" size="large">
<el-form-item prop="user">
<el-input v-model="ruleForm.user" prefix-icon="el-icon-user" clearable :placeholder="$t('login.userPlaceholder')"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="ruleForm.password" prefix-icon="el-icon-lock" clearable show-password :placeholder="$t('login.PWPlaceholder')"></el-input>
</el-form-item>
<el-form-item style="margin-bottom: 10px;">
<el-row>
<el-col :span="12">
<el-checkbox :label="$t('login.rememberMe')" v-model="ruleForm.autologin"></el-checkbox>
</el-col>
<el-col :span="12" style="text-align: right;">
<el-button type="text">{{ $t('login.forgetPassword') }}?</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-button type="primary" style="width: 100%;" :loading="islogin" round @click="login">{{ $t('login.signIn') }}</el-button>
</el-form-item>
</el-form>
<el-divider>{{ $t('login.signInOther') }}</el-divider>
<div class="login-oauth">
<el-button size="small" type="success" icon="sc-icon-wechat" circle></el-button>
</div>
</div>
</div>
</div>
</template>
<script>
import {mapMutations} from 'vuex'
export default {
data() {
return {
userType: 'admin',
ruleForm: {
user: "admin",
password: "admin888",
autologin: false
},
rules: {
user: [
{required: true, message: this.$t('login.userError'), trigger: 'blur'}
],
password: [
{required: true, message: this.$t('login.PWError'), trigger: 'blur'}
]
},
islogin: false,
config: {
lang: this.$TOOL.data.get('APP_LANG') || this.$CONFIG.LANG,
theme: this.$TOOL.data.get('APP_THEME') || 'default'
},
lang: [
{
name: '简体中文',
value: 'zh-cn',
},
{
name: 'English',
value: 'en',
},
{
name: '日本語',
value: 'ja',
}
]
}
},
watch:{
userType(val){
if(val == 'admin'){
this.ruleForm.user = 'molong@tensent.cn'
this.ruleForm.password = 'password'
}else if(val == 'user'){
this.ruleForm.user = 'user'
this.ruleForm.password = 'user'
}
},
'config.theme'(val){
document.body.setAttribute('data-theme', val)
this.$TOOL.data.set("APP_THEME", val);
},
'config.lang'(val){
this.$i18n.locale = val
this.$TOOL.data.set("APP_LANG", val);
}
},
created: function() {
this.$TOOL.data.remove("TOKEN")
this.$TOOL.data.remove("USER_INFO")
this.$TOOL.data.remove("MENU")
this.$TOOL.data.remove("PERMISSIONS")
this.$TOOL.data.remove("grid")
this.$store.commit("clearViewTags")
this.$store.commit("clearKeepLive")
this.$store.commit("clearIframeList")
},
methods: {
...mapMutations('account', ['setUser', 'setPermissions', 'setRoles']),
async login(){
var validate = await this.$refs.loginForm.validate().catch(()=>{})
if(!validate){ return false }
this.islogin = true
var data = {
username: this.ruleForm.user,
// password: this.$TOOL.crypto.BASE64.encrypt(this.ruleForm.password)
password: this.ruleForm.password
}
//获取token
var user = await this.$API.auth.login.post(data)
if(user.code == 1){
this.$TOOL.data.set("TOKEN", user.data.token)
this.$TOOL.data.set("USER_INFO", user.data)
}else{
this.islogin = false
this.$message.warning(user.message)
return false
}
this.afterLogin();
},
async afterLogin(){
//获取菜单
var menu = null
if(this.ruleForm.user == 'admin'){
menu = await this.$API.system.menu.myMenus.get()
}else{
menu = await this.$API.system.menu.get()
}
if(menu.code == 1){
if(menu.data.menu.length==0){
this.islogin = false
this.$alert("当前用户无任何菜单权限,请联系系统管理员", "无权限访问", {
type: 'error',
center: true
})
return false
}
this.$TOOL.data.set("MENU", menu.data.menu)
this.$TOOL.data.set("PERMISSIONS", menu.data.permissions)
}else{
this.islogin = false
this.$message.warning(menu.message)
return false
}
this.$router.replace({
path: '/'
})
this.$message.success("Login Success 登录成功")
this.islogin = false
},
configTheme(){
this.config.theme = this.config.theme=='default'?'dark':'default'
},
configLang(command){
this.config.lang = command.value
}
}
}
</script>
<style scoped>
.login_bg {width: 100%;height: 100%;background: #fff;display: flex;}
.login_adv {width: 33.33333%;background-color: #555;background-size: cover;background-position: center center;background-repeat: no-repeat;position: relative;}
.login_adv__title {color: #fff;padding: 40px;}
.login_adv__title h2 {font-size: 40px;}
.login_adv__title h4 {font-size: 18px;margin-top: 10px;font-weight: normal;}
.login_adv__title p {font-size: 14px;margin-top:10px;line-height: 1.8;color: rgba(255,255,255,0.6);}
.login_adv__title div {margin-top: 10px;display: flex;align-items: center;}
.login_adv__title div span {margin-right: 15px;}
.login_adv__title div i {font-size: 40px;}
.login_adv__title div i.add {font-size: 20px;color: rgba(255,255,255,0.6);}
.login_adv__bottom {position: absolute;left:0px;right: 0px;bottom: 0px;color: #fff;padding: 40px;background-image:linear-gradient(transparent, #000);}
.login_main {flex: 1;overflow: auto;display:flex;}
.login-form {width: 400px;margin: auto;padding:20px 0;}
.login-header {margin-bottom: 20px;}
.login-header .logo {display: flex;align-items: center;}
.login-header .logo img {width: 35px;height: 35px;vertical-align: bottom;margin-right: 10px;}
.login-header .logo label {font-size: 24px;}
.login-header h2 {font-size: 24px;font-weight: bold;margin-top: 50px;}
.login-oauth {display: flex;justify-content:space-around;}
.login-form .el-divider {margin-top:40px;}
.login_config {position: absolute;top:20px;right: 20px;}
.el-dropdown-menu__item.selected {color: var(--el-color-primary);}
@media (max-width: 1200px){
.login-form {width: 340px;}
}
@media (max-width: 1000px){
.login_main {display: block;}
.login-form {width:100%;padding:20px 40px;}
.login_adv {display: none;}
}
</style>
+1 -1
View File
@@ -131,7 +131,7 @@ function filterAsyncRouter(routerMap) {
}
function loadComponent(component){
if(component){
return () => import(/* webpackChunkName: "[request]" */ `@/views/${component}`)
return () => import(/* webpackChunkName: "[request]" */ `@/pages/${component}`)
}else{
return () => import(`@/layout/other/empty`)
}
+1 -1
View File
@@ -11,7 +11,7 @@ const routes = [
},
{
path: "/login",
component: () => import(/* webpackChunkName: "login" */ '@/views/userCenter/login'),
component: () => import(/* webpackChunkName: "login" */ '@/pages/ucenter/login'),
meta: {
title: "登录"
}
+1 -1
View File
@@ -28,7 +28,7 @@ import copy from './directives/copy'
import errorHandler from './utils/errorHandler'
import * as elIcons from '@element-plus/icons'
import * as scIcons from './assets/icons'
import * as scIcons from './static/icons'
export default {
install(app) {
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+33
View File
@@ -0,0 +1,33 @@
import tool from '@/utils/tool';
export default {
state: {
user: undefined,
permissions: null,
roles: null,
routesConfig: null,
userMenu: tool.data.get("MENU") || []
},
mutations: {
setUser (state, user) {
state.user = user
localStorage.setItem(process.env.VUE_APP_USER_KEY, JSON.stringify(user))
},
setPermissions(state, permissions) {
state.permissions = permissions
localStorage.setItem(process.env.VUE_APP_PERMISSIONS_KEY, JSON.stringify(permissions))
},
setRoles(state, roles) {
state.roles = roles
localStorage.setItem(process.env.VUE_APP_ROLES_KEY, JSON.stringify(roles))
},
setRoutesConfig(state, routesConfig) {
state.routesConfig = routesConfig
localStorage.setItem(process.env.VUE_APP_ROUTES_KEY, JSON.stringify(routesConfig))
},
setUserMenu (state, menu){
state.userMenu = menu;
tool.data.set("MENU", menu)
}
}
}
+11 -2
View File
@@ -4,8 +4,7 @@ import sysConfig from "@/config";
import tool from '@/utils/tool';
import router from '@/router';
axios.defaults.baseURL = ''
axios.defaults.withCredentials = false
axios.defaults.timeout = sysConfig.TIMEOUT
// HTTP request 拦截器
@@ -30,6 +29,16 @@ axios.interceptors.request.use(
// HTTP response 拦截器
axios.interceptors.response.use(
(response) => {
if(response.data.code == 2000 || response.data.code == 2001){
ElMessageBox.confirm('当前用户已被登出或无权限访问当前资源,请尝试重新登录后再操作。', '无权限访问', {
type: 'error',
closeOnClickModal: false,
center: true,
confirmButtonText: '重新登录'
}).then(() => {
router.replace({path: '/login'});
}).catch(() => {})
}
return response;
},
(error) => {
-48
View File
@@ -1,48 +0,0 @@
<template>
<div v-if="pageLoading">
<el-main>
<el-card shadow="never">
<el-skeleton :rows="1"></el-skeleton>
</el-card>
<el-card shadow="never" style="margin-top: 15px;">
<el-skeleton></el-skeleton>
</el-card>
</el-main>
</div>
<work v-if="dashboard=='1'" @on-mounted="onMounted"></work>
<widgets v-else @on-mounted="onMounted"></widgets>
</template>
<script>
import { defineAsyncComponent } from 'vue';
const work = defineAsyncComponent(() => import('./work'));
const widgets = defineAsyncComponent(() => import('./widgets'));
export default {
name: "dashboard",
components: {
work,
widgets
},
data(){
return {
pageLoading: true,
dashboard: '0'
}
},
created(){
this.dashboard = this.$TOOL.data.get("USER_INFO").dashboard || '0';
},
mounted(){
},
methods: {
onMounted(){
this.pageLoading = false
}
}
}
</script>
<style>
</style>
@@ -1,40 +0,0 @@
<template>
<el-card shadow="hover" header="时钟" class="item-background">
<div class="time">
<h2>{{ time }}</h2>
<p>{{ day }}</p>
</div>
</el-card>
</template>
<script>
export default {
title: "时钟",
icon: "el-icon-clock",
description: "演示部件效果",
data() {
return {
time: '',
day: ''
}
},
mounted() {
this.showTime()
setInterval(()=>{
this.showTime()
},1000)
},
methods: {
showTime(){
this.time = this.$TOOL.dateFormat(new Date(), 'hh:mm:ss')
this.day = this.$TOOL.dateFormat(new Date(), 'yyyy年MM月dd日')
}
}
}
</script>
<style scoped>
.item-background {background: linear-gradient(to right, #8E54E9, #4776E6);color: #fff;}
.time h2 {font-size: 40px;}
.time p {font-size: 14px;margin-top: 13px;opacity: 0.7;}
</style>
@@ -1,41 +0,0 @@
<template>
<el-card shadow="hover" header="版本信息">
<div style="height: 210px;text-align: center;">
<img src="img/ver.svg" style="height:140px"/>
<h2 style="margin-top: 15px;">SCUI {{$CONFIG.CORE_VER}}</h2>
<p style="margin-top: 5px;">最新版本 {{ver}}</p>
</div>
<div style="margin-top: 20px;">
<el-button type="primary" plain round @click="golog">更新日志</el-button>
<el-button type="primary" plain round @click="gogit">gitee</el-button>
</div>
</el-card>
</template>
<script>
export default {
title: "版本信息",
icon: "el-icon-monitor",
description: "当前项目版本信息",
data() {
return {
ver: 'loading...'
}
},
mounted() {
this.getVer()
},
methods: {
async getVer(){
const ver = await this.$API.demo.ver.get()
this.ver = ver.data
},
golog(){
window.open("https://gitee.com/lolicode/scui/releases")
},
gogit(){
window.open("https://gitee.com/lolicode/scui")
}
}
}
</script>
-37
View File
@@ -1,37 +0,0 @@
<template>
<el-main>
<el-alert title="根据角色配置,可让不同角色访问不同的控制台视图,参数值在登录成功后返回 dashboard:{type}" type="success" style="margin-bottom:20px;"></el-alert>
<el-row :gutter="15">
<el-col :lg="24">
<el-card shadow="never" header="我的常用">
<myapp></myapp>
</el-card>
</el-col>
</el-row>
</el-main>
</template>
<script>
import myapp from './components/myapp';
export default {
components: {
myapp
},
data() {
return {
}
},
mounted(){
this.$emit('on-mounted')
},
methods: {
}
}
</script>
<style scoped>
</style>
-52
View File
@@ -1,52 +0,0 @@
<template>
<el-main>
<el-row :gutter="15">
<el-col :lg="24">
<el-card shadow="never" class="aboutTop">
<div class="aboutTop-info">
<img src="img/logo.png">
<h2>{{data.name}}</h2>
<p>{{data.version}}</p>
</div>
</el-card>
<el-card shadow="never" header="dependencies">
<el-descriptions border :column="3">
<el-descriptions-item v-for="(value, key) in data.dependencies" :key="key" :label="key">{{value}}</el-descriptions-item>
</el-descriptions>
</el-card>
<el-card shadow="never" header="devDependencies">
<el-descriptions border :column="3">
<el-descriptions-item v-for="(value, key) in data.devDependencies" :key="key" :label="key">{{value}}</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
</el-row>
</el-main>
</template>
<script>
import packageJson from '../../../package.json'
export default {
name: 'about',
data() {
return {
data: packageJson
}
},
mounted() {
},
methods: {
}
}
</script>
<style scoped>
.aboutTop {border:0;background: linear-gradient(to right, #8E54E9, #4776E6);color: #fff;}
.aboutTop-info {text-align: center}
.aboutTop-info img {width: 100px;}
.aboutTop-info h2 {font-size: 26px;margin-top: 15px;}
.aboutTop-info p {font-size: 16px;margin-top: 10px;}
</style>
-54
View File
@@ -1,54 +0,0 @@
<template>
<el-main>
<el-card shadow="never" header="v-auth 高精度权限控制">
<el-button v-auth="'user.add'" type="primary">v-auth="'user.add'"</el-button>
<el-button v-auth="['user.no','user.add']" type="primary">v-auth="['user.no','user.add']"</el-button>
<el-alert title="v-auth指令 是$AUTH的语法糖, 原先需要使用v-if来判断是否有权限, 使用指令将减少代码冗余. 并且支持传入数组,有一项满足就判断有权限" style="margin-top: 20px;"></el-alert>
</el-card>
<el-card shadow="never" header="v-role 角色权限控制" style="margin-top: 15px;">
<el-button v-role="'admin'" type="primary">v-role="'admin'"</el-button>
<el-button v-role="['SA','admin']" type="primary">v-role="['SA','admin']"</el-button>
<el-alert title="v-role指令 是$ROLE的语法糖, 原理是判断是否含有用户所在的角色别名" style="margin-top: 20px;"></el-alert>
</el-card>
<el-card shadow="never" header="v-time 时间转换" style="margin-top: 15px;">
<p>
<el-tag v-time="1630117968295" format="yyyy-MM-dd hh:mm:ss"></el-tag>
</p>
<p style="margin-top: 15px;">
<el-tag v-time.tip="time1"></el-tag>
</p>
<p style="margin-top: 15px;">
<el-tag v-time.tip="time2"></el-tag>
</p>
<p style="margin-top: 15px;">
<el-tag v-time.tip="time3"></el-tag>
</p>
<el-alert title="指令方式日期时间转换,如设置'tip'修饰符将会转换成相对时间,并且每60秒自动更新" style="margin-top: 20px;"></el-alert>
</el-card>
<el-card shadow="never" header="v-copy 一键复制" style="margin-top: 15px;">
<el-input type="textarea" :rows="2" placeholder="请输入内容" v-model="copyText"></el-input>
<el-button v-copy="copyText" type="primary" style="margin-top: 15px;">复制</el-button>
<el-alert title="点击复制按钮会将文本框绑定的值复制到剪切板, 试着粘贴到其他地方看看效果" style="margin-top: 20px;"></el-alert>
</el-card>
</el-main>
</template>
<script>
export default {
name: 'directive',
data() {
return {
time1: new Date(),
time2: new Date().setMinutes(new Date().getMinutes()-1),
time3: new Date().setMinutes(new Date().getMinutes()-120),
copyText: '测试复制内容'
}
},
created() {
}
}
</script>
<style>
</style>
-32
View File
@@ -1,32 +0,0 @@
<template>
<el-container>
<el-header>
<el-page-header content="FullPage" @back="goBack" />
</el-header>
<el-main>
<el-empty description="FullPageMain"></el-empty>
</el-main>
</el-container>
</template>
<script>
export default {
name: 'fullpage',
data() {
return {
}
},
mounted() {
},
methods: {
goBack(){
this.$router.go(-1)
}
}
}
</script>
<style>
</style>
-82
View File
@@ -1,82 +0,0 @@
<template>
<el-main>
<el-card shadow="never" header="打开">
<el-button type="primary" plain @click="open1">打开个人信息</el-button>
<el-button type="primary" plain @click="open2">打开后执行</el-button>
<el-alert title="打开后执行原理: 路由push时,在当前路由对象中插入一个特殊标识, 在目标视图中beforeRouteEnter获取判断是否需要执行特殊方法" style="margin-top: 20px;"></el-alert>
</el-card>
<el-card shadow="never" header="刷新" style="margin-top: 15px;">
<el-button type="primary" plain @click="refresh1">刷新当前</el-button>
</el-card>
<el-card shadow="never" header="关闭" style="margin-top: 15px;">
<el-button type="primary" plain @click="close1">关闭当前</el-button>
<el-button type="primary" plain @click="close2">关闭其他</el-button>
<el-button type="primary" plain @click="close3">关闭后执行</el-button>
</el-card>
<el-card shadow="never" header="设置" style="margin-top: 15px;">
<el-form :inline="true">
<el-form-item>
<el-input v-model="input" placeholder="请输入内容"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" plain @click="set1">设置标题</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card shadow="never" header="整页路由" style="margin-top: 15px;">
<el-button type="primary" plain @click="fullpage">fullpage</el-button>
<el-alert title="变更路由的层级关系,向上推至顶级达到在layout视图中显示. 只需要在路由中设置 meta.fullpage 即可" style="margin-top: 20px;"></el-alert>
</el-card>
</el-main>
</template>
<script>
import useTabs from '@/utils/useTabs'
export default {
name: 'viewTags',
data() {
return {
input: "newTabName"
}
},
mounted() {
},
methods: {
open1(){
this.$router.push('/usercenter')
},
open2(){
this.$router.push('/usercenter')
this.$route.is = true
},
refresh1(){
useTabs.refresh()
},
close1(){
useTabs.close()
},
close2(){
useTabs.closeOther()
},
close3(){
useTabs.closeNext((tags)=>{
//回调返回所有标签的数组,这里其实是需要判断是否含有'/usercenter',含有再操作的,这里为了演示就直接打开了。
console.log(tags)
this.$router.push('/usercenter')
this.$route.is = true
})
},
set1(){
useTabs.setTitle(this.input)
},
fullpage(){
this.$router.push('/other/fullpage')
}
}
}
</script>
<style>
</style>
-330
View File
@@ -1,330 +0,0 @@
<template>
<el-container>
<el-aside width="300px" v-loading="showDicloading">
<el-container>
<el-header>
<el-input placeholder="输入关键字进行过滤" v-model="dicFilterText" clearable></el-input>
</el-header>
<el-main class="nopadding">
<el-tree ref="dic" class="menu" node-key="id" :data="dicList" :props="dicProps" :highlight-current="true" :expand-on-click-node="false" :filter-node-method="dicFilterNode" @node-click="dicClick">
<template #default="{node, data}">
<span class="custom-tree-node">
<span class="label">{{ node.label }}</span>
<span class="code">{{ data.code }}</span>
<span class="do">
<el-icon @click.stop="dicEdit(data)"><el-icon-edit /></el-icon>
<el-icon @click.stop="dicDel(node, data)"><el-icon-delete /></el-icon>
</span>
</span>
</template>
</el-tree>
</el-main>
<el-footer style="height:51px;">
<el-button type="primary" size="small" icon="el-icon-plus" style="width: 100%;" @click="addDic">字典分类</el-button>
</el-footer>
</el-container>
</el-aside>
<el-container class="is-vertical">
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="addInfo"></el-button>
<el-button type="danger" plain icon="el-icon-delete" :disabled="selection.length==0" @click="batch_del"></el-button>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="listApi" row-key="id" :params="listApiParams" @selection-change="selectionChange" stripe :paginationLayout="'prev, pager, next'">
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="" width="60">
<template #default>
<el-tag class="move" style="cursor: move;"><el-icon-d-caret style="width: 1em; height: 1em;"/></el-tag>
</template>
</el-table-column>
<el-table-column label="名称" prop="name" width="150"></el-table-column>
<el-table-column label="键值" prop="key" width="150"></el-table-column>
<el-table-column label="是否有效" prop="yx" width="100">
<template #default="scope">
<el-switch v-model="scope.row.yx" @change="changeSwitch($event, scope.row)" :loading="scope.row.$switch_yx" active-value="1" inactive-value="0"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" align="right" width="140">
<template #default="scope">
<el-button type="text" size="small" @click="table_edit(scope.row, scope.$index)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
<template #reference>
<el-button type="text" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
</el-container>
<dic-dialog v-if="dialog.dic" ref="dicDialog" @success="handleDicSuccess" @closed="dialog.dic=false"></dic-dialog>
<list-dialog v-if="dialog.list" ref="listDialog" @success="handleListSuccess" @closed="dialog.list=false"></list-dialog>
</template>
<script>
import dicDialog from './dic'
import listDialog from './list'
import Sortable from 'sortablejs'
export default {
name: 'dic',
components: {
dicDialog,
listDialog
},
data() {
return {
dialog: {
dic: false,
info: false
},
showDicloading: true,
dicList: [],
dicFilterText: '',
dicProps: {
label: 'name'
},
listApi: null,
listApiParams: {},
selection: []
}
},
watch: {
dicFilterText(val) {
this.$refs.dic.filter(val);
}
},
mounted() {
this.getDic()
this.rowDrop()
},
methods: {
//加载树数据
async getDic(){
var res = await this.$API.system.dic.tree.get();
this.showDicloading = false;
this.dicList = res.data;
//获取第一个节点,设置选中 & 加载明细列表
var firstNode = this.dicList[0];
if(firstNode){
this.$nextTick(() => {
this.$refs.dic.setCurrentKey(firstNode.id)
})
this.listApiParams = {
code: firstNode.code
}
this.listApi = this.$API.system.dic.list;
}
},
//树过滤
dicFilterNode(value, data){
if (!value) return true;
var targetText = data.name + data.code;
return targetText.indexOf(value) !== -1;
},
//树增加
addDic(){
this.dialog.dic = true
this.$nextTick(() => {
this.$refs.dicDialog.open()
})
},
//编辑树
dicEdit(data){
this.dialog.dic = true
this.$nextTick(() => {
var editNode = this.$refs.dic.getNode(data.id);
var editNodeParentId = editNode.level==1?undefined:editNode.parent.data.id
data.parentId = editNodeParentId
this.$refs.dicDialog.open('edit').setData(data)
})
},
//树点击事件
dicClick(data){
this.$refs.table.reload({
code: data.code
})
},
//删除树
dicDel(node, data){
this.$confirm(`确定删除 ${data.name} 项吗?`, '提示', {
type: 'warning'
}).then(() => {
this.showDicloading = true;
//删除节点是否为高亮当前 是的话 设置第一个节点高亮
var dicCurrentKey = this.$refs.dic.getCurrentKey();
this.$refs.dic.remove(data.id)
if(dicCurrentKey == data.id){
var firstNode = this.dicList[0];
if(firstNode){
this.$refs.dic.setCurrentKey(firstNode.id);
this.$refs.table.upData({
code: firstNode.code
})
}else{
this.listApi = null;
this.$refs.table.tableData = []
}
}
this.showDicloading = false;
this.$message.success("操作成功")
}).catch(() => {
})
},
//行拖拽
rowDrop(){
const _this = this
const tbody = this.$refs.table.$el.querySelector('.el-table__body-wrapper tbody')
Sortable.create(tbody, {
handle: ".move",
animation: 300,
ghostClass: "ghost",
onEnd({ newIndex, oldIndex }) {
const tableData = _this.$refs.table.tableData
const currRow = tableData.splice(oldIndex, 1)[0]
tableData.splice(newIndex, 0, currRow)
_this.$message.success("排序成功")
}
})
},
//添加明细
addInfo(){
this.dialog.list = true
this.$nextTick(() => {
var dicCurrentKey = this.$refs.dic.getCurrentKey();
const data = {
dic: dicCurrentKey
}
this.$refs.listDialog.open().setData(data)
})
},
//编辑明细
table_edit(row){
this.dialog.list = true
this.$nextTick(() => {
this.$refs.listDialog.open('edit').setData(row)
})
},
//删除明细
async table_del(row, index){
var reqData = {id: row.id}
var res = await this.$API.demo.post.post(reqData);
if(res.code == 200){
this.$refs.table.tableData.splice(index, 1);
this.$message.success("删除成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
},
//批量删除
async batch_del(){
this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
type: 'warning'
}).then(() => {
const loading = this.$loading();
this.selection.forEach(item => {
this.$refs.table.tableData.forEach((itemI, indexI) => {
if (item.id === itemI.id) {
this.$refs.table.tableData.splice(indexI, 1)
}
})
})
loading.close();
this.$message.success("操作成功")
}).catch(() => {
})
},
//提交明细
saveList(){
this.$refs.listDialog.submit(async (formData) => {
this.isListSaveing = true;
var res = await this.$API.demo.post.post(formData);
this.isListSaveing = false;
if(res.code == 200){
//这里选择刷新整个表格 OR 插入/编辑现有表格数据
this.listDialogVisible = false;
this.$message.success("操作成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
})
},
//表格选择后回调事件
selectionChange(selection){
this.selection = selection;
},
//表格内开关事件
changeSwitch(val, row){
//1.还原数据
row.yx = row.yx == '1'?'0':'1'
//2.执行加载
row.$switch_yx = true;
//3.等待接口返回后改变值
setTimeout(()=>{
delete row.$switch_yx;
row.yx = val;
this.$message.success(`操作成功id:${row.id} val:${val}`)
}, 500)
},
//本地更新数据
handleDicSuccess(data, mode){
if(mode=='add'){
data.id = new Date().getTime()
if(this.dicList.length > 0){
this.$refs.table.upData({
code: data.code
})
}else{
this.listApiParams = {
code: data.code
}
this.listApi = this.$API.dic.info;
}
this.$refs.dic.append(data, data.parentId[0])
this.$refs.dic.setCurrentKey(data.id)
}else if(mode=='edit'){
var editNode = this.$refs.dic.getNode(data.id);
//判断是否移动?
var editNodeParentId = editNode.level==1?undefined:editNode.parent.data.id
if(editNodeParentId != data.parentId){
var obj = editNode.data;
this.$refs.dic.remove(data.id)
this.$refs.dic.append(obj, data.parentId[0])
}
Object.assign(editNode.data, data)
}
},
//本地更新数据
handleListSuccess(data, mode){
if(mode=='add'){
data.id = new Date().getTime()
this.$refs.table.tableData.push(data)
}else if(mode=='edit'){
this.$refs.table.tableData.filter(item => item.id===data.id ).forEach(item => {
Object.assign(item, data)
})
}
}
}
}
</script>
<style scoped>
.custom-tree-node {display: flex;flex: 1;align-items: center;justify-content: space-between;font-size: 14px;padding-right: 24px;height:100%;}
.custom-tree-node .code {font-size: 12px;color: #999;}
.custom-tree-node .do {display: none;}
.custom-tree-node .do i {margin-left:5px;color: #999;padding:5px;}
.custom-tree-node .do i:hover {color: #333;}
.custom-tree-node:hover .code {display: none;}
.custom-tree-node:hover .do {display: inline-block;}
</style>
-111
View File
@@ -1,111 +0,0 @@
<template>
<el-dialog :title="titleMap[mode]" v-model="visible" :width="400" destroy-on-close @closed="$emit('closed')">
<el-form :model="form" :rules="rules" ref="dialogForm" label-width="100px" label-position="left">
<el-form-item label="所属字典" prop="dic">
<el-cascader v-model="form.dic" :options="dic" :props="dicProps" :show-all-levels="false" clearable></el-cascader>
</el-form-item>
<el-form-item label="项名称" prop="name">
<el-input v-model="form.name" clearable></el-input>
</el-form-item>
<el-form-item label="键值" prop="key">
<el-input v-model="form.key" clearable></el-input>
</el-form-item>
<el-form-item label="是否有效" prop="yx">
<el-switch v-model="form.yx" active-value="1" inactive-value="0"></el-switch>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible=false" >取 消</el-button>
<el-button type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
emits: ['success', 'closed'],
data() {
return {
mode: "add",
titleMap: {
add: '新增项',
edit: '编辑项'
},
visible: false,
isSaveing: false,
form: {
id: "",
dic: "",
name: "",
key: "",
yx: "1"
},
rules: {
dic: [
{required: true, message: '请选择所属字典'}
],
name: [
{required: true, message: '请输入项名称'}
],
key: [
{required: true, message: '请输入键值'}
]
},
dic: [],
dicProps: {
value: "id",
label: "name",
emitPath: false,
checkStrictly: true
}
}
},
mounted() {
if(this.params){
this.form.dic = this.params.code
}
this.getDic()
},
methods: {
//显示
open(mode='add'){
this.mode = mode;
this.visible = true;
return this;
},
//获取字典列表
async getDic(){
var res = await this.$API.system.dic.tree.get();
this.dic = res.data;
},
//表单提交方法
submit(){
this.$refs.dialogForm.validate(async (valid) => {
if (valid) {
this.isSaveing = true;
var res = await this.$API.demo.post.post(this.form);
this.isSaveing = false;
if(res.code == 200){
this.$emit('success', this.form, this.mode)
this.visible = false;
this.$message.success("操作成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
}
})
},
//表单注入数据
setData(data){
this.form.id = data.id
this.form.name = data.name
this.form.key = data.key
this.form.yx = data.yx
this.form.dic = data.dic
}
}
}
</script>
<style>
</style>
-142
View File
@@ -1,142 +0,0 @@
<template>
<el-container>
<el-aside width="220px">
<el-tree ref="category" class="menu" node-key="label" :data="category" :default-expanded-keys="['系统日志']" current-node-key="系统日志" :highlight-current="true" :expand-on-click-node="false">
</el-tree>
</el-aside>
<el-container>
<el-main class="nopadding">
<el-container>
<el-header>
<div class="left-panel">
<el-date-picker v-model="date" type="datetimerange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</div>
<div class="right-panel">
</div>
</el-header>
<el-header style="height:150px;">
<scEcharts height="100%" :option="logsChartOption"></scEcharts>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="apiObj" stripe highlightCurrentRow @row-click="rowClick">
<el-table-column label="级别" prop="level" width="60">
<template #default="scope">
<el-icon v-if="scope.row.level=='error'" style="color: #F56C6C;"><el-icon-circle-close-filled /></el-icon>
<el-icon v-if="scope.row.level=='warn'" style="color: #E6A23C;"><el-icon-warning-filled /></el-icon>
<el-icon v-if="scope.row.level=='info'" style="color: #409EFF;"><el-icon-info-filled /></el-icon>
</template>
</el-table-column>
<el-table-column label="ID" prop="id" width="180"></el-table-column>
<el-table-column label="日志名" prop="name" width="150"></el-table-column>
<el-table-column label="请求接口" prop="url" width="150"></el-table-column>
<el-table-column label="请求方法" prop="type" width="150"></el-table-column>
<el-table-column label="用户" prop="user" width="150"></el-table-column>
<el-table-column label="客户端IP" prop="cip" width="150"></el-table-column>
<el-table-column label="日志时间" prop="time" width="170"></el-table-column>
</scTable>
</el-main>
</el-container>
</el-main>
</el-container>
</el-container>
<el-drawer v-model="infoDrawer" title="日志详情" :size="600" destroy-on-close>
<info ref="info"></info>
</el-drawer>
</template>
<script>
import info from './info'
import scEcharts from '@/components/scEcharts'
export default {
name: 'log',
components: {
info,
scEcharts
},
data() {
return {
infoDrawer: false,
logsChartOption: {
color: ['#409eff','#e6a23c','#f56c6c'],
grid: {
top: '0px',
left: '10px',
right: '10px',
bottom: '0px'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['2021-07-01', '2021-07-02', '2021-07-03', '2021-07-04', '2021-07-05', '2021-07-06', '2021-07-07', '2021-07-08', '2021-07-09', '2021-07-10', '2021-07-11', '2021-07-12', '2021-07-13', '2021-07-14', '2021-07-15']
},
yAxis: {
show: false,
type: 'value'
},
series: [{
data: [120, 200, 150, 80, 70, 110, 130, 120, 200, 150, 80, 70, 110, 130, 70, 110],
type: 'bar',
stack: 'log',
barWidth: '15px'
},
{
data: [15, 26, 7, 12, 13, 9, 21, 15, 26, 7, 12, 13, 9, 21, 12, 3],
type: 'bar',
stack: 'log',
barWidth: '15px'
},
{
data: [0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
type: 'bar',
stack: 'log',
barWidth: '15px'
}]
},
category: [
{
label: '系统日志',
children: [
{label: 'debug'},
{label: 'info'},
{label: 'warn'},
{label: 'error'},
{label: 'fatal'}
]
},
{
label: '应用日志',
children: [
{label: 'selfHelp'},
{label: 'WechatApp'}
]
}
],
date: [],
apiObj: this.$API.system.log.list,
search: {
keyword: ""
}
}
},
methods: {
upsearch(){
},
rowClick(row){
this.infoDrawer = true
this.$nextTick(() => {
this.$refs.info.setData(row)
})
}
}
}
</script>
<style>
</style>
-54
View File
@@ -1,54 +0,0 @@
<template>
<el-main style="padding:0 20px;">
<el-descriptions :column="1" border size="small">
<el-descriptions-item label="请求接口">{{data.url}}</el-descriptions-item>
<el-descriptions-item label="请求方法">{{data.type}}</el-descriptions-item>
<el-descriptions-item label="状态代码">{{data.code}}</el-descriptions-item>
<el-descriptions-item label="日志名">{{data.name}}</el-descriptions-item>
<el-descriptions-item label="日志时间">{{data.time}}</el-descriptions-item>
</el-descriptions>
<el-collapse v-model="activeNames" style="margin-top: 20px;">
<el-collapse-item title="常规" name="1">
<el-alert title="在没有配置的 DNS 服务器响应之后,名称 update-khd.2345.cc 的名称解析超时。" :type="typeMap[data.level]" :closable="false"></el-alert>
</el-collapse-item>
<el-collapse-item title="详细" name="2">
<div class="code">
Request: {
User-Agent: "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
},
Response: {
Content-Type: "application/json; charset=utf-8",
Date: "Fri, 25 Jun 2021 03:02:14 GMT",
Server: "nginx/1.17.8"
}
</div>
</el-collapse-item>
</el-collapse>
</el-main>
</template>
<script>
export default {
data() {
return {
data: {},
activeNames: ['1'],
typeMap: {
'info': "info",
'warn': "warning",
'error': "error"
}
}
},
methods: {
setData(data){
this.data = data
}
}
}
</script>
<style scoped>
.code {background: #848484;padding:15px;color: #fff;font-size: 12px;border-radius: 4px;}
</style>
-164
View File
@@ -1,164 +0,0 @@
<template>
<el-container>
<el-aside width="300px" v-loading="menuloading">
<el-container>
<el-header>
<el-input placeholder="输入关键字进行过滤" v-model="menuFilterText" clearable></el-input>
</el-header>
<el-main class="nopadding">
<el-tree ref="menu" class="menu" node-key="id" :data="menuList" :props="menuProps" draggable highlight-current :expand-on-click-node="false" check-strictly show-checkbox :filter-node-method="menuFilterNode" @node-click="menuClick" @node-drop="nodeDrop">
<template #default="{node, data}">
<span class="custom-tree-node el-tree-node__label">
<span class="label">
{{ node.label }}
</span>
<span class="do">
<el-icon @click.stop="add(node, data)"><el-icon-plus /></el-icon>
</span>
</span>
</template>
</el-tree>
</el-main>
<el-footer style="height:51px;">
<el-button type="primary" size="small" icon="el-icon-plus" @click="add()"></el-button>
<el-button type="danger" size="small" plain icon="el-icon-delete" @click="delMenu"></el-button>
</el-footer>
</el-container>
</el-aside>
<el-container>
<el-main class="nopadding" style="padding:20px;" ref="main">
<save ref="save" :menu="menuList"></save>
</el-main>
</el-container>
</el-container>
</template>
<script>
let newMenuIndex = 1;
import save from './save'
export default {
name: "settingMenu",
components: {
save
},
data(){
return {
menuloading: false,
menuList: [],
menuProps: {
label: (data)=>{
return data.meta.title
}
},
menuFilterText: ""
}
},
watch: {
menuFilterText(val){
this.$refs.menu.filter(val);
}
},
mounted() {
this.getMenu();
},
methods: {
//加载树数据
async getMenu(){
this.menuloading = true
var res = await this.$API.system.menu.list.get();
this.menuloading = false
this.menuList = res.data;
},
//树点击
menuClick(data, node){
var pid = node.level==1?undefined:node.parent.data.id;
this.$refs.save.setData(data, pid)
this.$refs.main.$el.scrollTop = 0
},
//树过滤
menuFilterNode(value, data){
if (!value) return true;
var targetText = data.meta.title;
return targetText.indexOf(value) !== -1;
},
//树拖拽
nodeDrop(draggingNode, dropNode, dropType){
this.$refs.save.setData({})
this.$message(`拖拽对象:${draggingNode.data.meta.title}, 释放对象:${dropNode.data.meta.title}, 释放对象的位置:${dropType}`)
},
//增加
async add(node, data){
var newMenuName = "未命名" + newMenuIndex++;
var newMenuData = {
parentId: data ? data.id : "",
name: newMenuName,
path: "",
component: "",
meta:{
title: newMenuName,
type: "menu"
}
}
this.menuloading = true
var res = await this.$API.demo.post.post(newMenuData)
this.menuloading = false
newMenuData.id = res.data
this.$refs.menu.append(newMenuData, node)
this.$refs.menu.setCurrentKey(newMenuData.id)
var pid = node ? node.data.id : ""
this.$refs.save.setData(newMenuData, pid)
},
//删除菜单
async delMenu(){
var CheckedNodes = this.$refs.menu.getCheckedNodes()
if(CheckedNodes.length == 0){
this.$message.warning("请选择需要删除的项")
return false;
}
var confirm = await this.$confirm('确认删除已选择的菜单吗?','提示', {
type: 'warning',
confirmButtonText: '删除',
confirmButtonClass: 'el-button--danger'
}).catch(() => {})
if(confirm != 'confirm'){
return false
}
this.menuloading = true
var reqData = {
ids: CheckedNodes.map(item => item.id)
}
var res = await this.$API.demo.post.post(reqData)
this.menuloading = false
if(res.code == 200){
CheckedNodes.forEach(item => {
var node = this.$refs.menu.getNode(item)
if(node.isCurrent){
this.$refs.save.setData({})
}
this.$refs.menu.remove(item)
})
}else{
this.$message.warning(res.message)
}
}
}
}
</script>
<style scoped>
.custom-tree-node {display: flex;flex: 1;align-items: center;justify-content: space-between;font-size: 14px;padding-right: 24px;height:100%;}
.custom-tree-node .label {display: flex;align-items: center;;height: 100%;}
.custom-tree-node .label .el-tag {margin-left: 5px;}
.custom-tree-node .do {display: none;}
.custom-tree-node .do i {margin-left:5px;color: #999;}
.custom-tree-node .do i:hover {color: #333;}
.custom-tree-node:hover .do {display: inline-block;}
</style>
-160
View File
@@ -1,160 +0,0 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add"></el-button>
<el-button type="danger" plain icon="el-icon-delete" :disabled="selection.length==0" @click="batch_del"></el-button>
<el-button type="primary" plain :disabled="selection.length!=1" @click="permission">权限设置</el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<el-input v-model="search.keyword" placeholder="角色名称" clearable></el-input>
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="apiObj" row-key="id" @selection-change="selectionChange" hidePagination>
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="#" type="index" width="50"></el-table-column>
<el-table-column label="角色名称" prop="label" width="250"></el-table-column>
<el-table-column label="别名" prop="alias" width="150"></el-table-column>
<el-table-column label="排序" prop="sort" width="150"></el-table-column>
<el-table-column label="操作" fixed="right" align="right" width="140">
<template #default="scope">
<el-button type="text" size="small" @click="table_show(scope.row, scope.$index)">查看</el-button>
<el-divider direction="vertical"></el-divider>
<el-button type="text" size="small" @click="table_edit(scope.row, scope.$index)">编辑</el-button>
<el-divider direction="vertical"></el-divider>
<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
<template #reference>
<el-button type="text" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<save-dialog v-if="dialog.save" ref="saveDialog" @success="handleSaveSuccess" @closed="dialog.save=false"></save-dialog>
<permission-dialog v-if="dialog.permission" ref="permissionDialog" @closed="dialog.permission=false"></permission-dialog>
</template>
<script>
import saveDialog from './save'
import permissionDialog from './permission'
export default {
name: 'role',
components: {
saveDialog,
permissionDialog
},
data() {
return {
dialog: {
save: false,
permission: false
},
apiObj: this.$API.system.role.list,
selection: [],
search: {
keyword: null
}
}
},
methods: {
//添加
add(){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open()
})
},
//编辑
table_edit(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('edit').setData(row)
})
},
//查看
table_show(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('show').setData(row)
})
},
//权限设置
permission(){
this.dialog.permission = true
this.$nextTick(() => {
this.$refs.permissionDialog.open()
})
},
//删除
async table_del(row){
var reqData = {id: row.id}
var res = await this.$API.demo.post.post(reqData);
if(res.code == 200){
this.$refs.table.refresh()
this.$message.success("删除成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
},
//批量删除
async batch_del(){
this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?如果删除项中含有子集将会被一并删除`, '提示', {
type: 'warning'
}).then(() => {
const loading = this.$loading();
this.$refs.table.refresh()
loading.close();
this.$message.success("操作成功")
}).catch(() => {
})
},
//表格选择后回调事件
selectionChange(selection){
this.selection = selection;
},
//搜索
upsearch(){
},
//根据ID获取树结构
filterTree(id){
var target = null;
function filter(tree){
tree.forEach(item => {
if(item.id == id){
target = item
}
if(item.children){
filter(item.children)
}
})
}
filter(this.$refs.table.tableData)
return target
},
//本地更新数据
handleSaveSuccess(data, mode){
if(mode=='add'){
this.$refs.table.refresh()
}else if(mode=='edit'){
this.$refs.table.refresh()
}
}
}
}
</script>
<style>
</style>
-131
View File
@@ -1,131 +0,0 @@
<template>
<el-dialog title="角色权限设置" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
<el-tabs tab-position="top">
<el-tab-pane label="菜单权限">
<div class="treeMain">
<el-tree ref="menu" node-key="name" :data="menu.list" :default-checked-keys="menu.checked" :props="menu.props" show-checkbox></el-tree>
</div>
</el-tab-pane>
<el-tab-pane label="部门权限">
<div class="treeMain">
<el-tree ref="group" node-key="name" :data="group.list" :default-checked-keys="group.checked" :props="group.props" show-checkbox></el-tree>
</div>
</el-tab-pane>
<el-tab-pane label="类型权限">
<div class="treeMain">
<el-tree ref="type" node-key="name" :data="type.list" :default-checked-keys="type.checked" :props="type.props" show-checkbox></el-tree>
</div>
</el-tab-pane>
<el-tab-pane label="控制台">
<el-form label-width="100px" label-position="left">
<el-form-item label="控制台视图">
<el-select v-model="dashboard" placeholder="请选择">
<el-option v-for="item in dashboardOptions" :key="item.value" :label="item.label" :value="item.value">
<span style="float: left">{{ item.label }}</span>
<span style="float: right; color: #8492a6; font-size: 12px">{{ item.views }}</span>
</el-option>
</el-select>
<div class="el-form-item-msg">用于控制角色登录后控制台的视图</div>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="visible=false" >取 消</el-button>
<el-button type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
emits: ['success', 'closed'],
data() {
return {
visible: false,
isSaveing: false,
menu: {
list: [],
checked: ["test", "system", "user", "role"],
props: {
label: (data)=>{
return data.meta.title
}
}
},
group: {
list: [],
checked: [],
props: {}
},
type: {
list: [],
checked: [],
props: {}
},
dashboard: "0",
dashboardOptions: [
{
value: '0',
label: '数据统计',
views: 'stats'
},
{
value: '1',
label: '工作台',
views: 'work'
},
]
}
},
mounted() {
this.getMenu();
this.getGroup();
this.getType();
},
methods: {
open(){
this.visible = true;
},
submit(){
this.isSaveing = true;
setTimeout(()=>{
this.isSaveing = false;
this.visible = false;
this.$message.success("操作成功")
this.$emit('success')
},1000)
},
async getMenu(){
var res = await this.$API.system.menu.list.get();
this.menu.list = res.data;
},
getGroup(){
this.group.list = [
{label: 'JL00'},
{label: 'LP01'},
{label: 'LP07'},
{label: 'SL01'},
{label: 'TL06'},
{label: 'TL09'},
{label: 'YP07'}
];
},
getType(){
this.type.list = [
{label: '原料采购'},
{label: '厂内互供'},
{label: '炼销订单'},
{label: '化工统销订单'},
{label: '移库单'},
{label: '自销订单'},
];
}
}
}
</script>
<style scoped>
.treeMain {height:280px;overflow: auto;border: 1px solid #dcdfe6;margin-bottom: 10px;}
</style>
-191
View File
@@ -1,191 +0,0 @@
<template>
<el-container>
<el-aside width="200px" v-loading="showGrouploading">
<el-container>
<el-header>
<el-input placeholder="输入关键字进行过滤" v-model="groupFilterText" clearable></el-input>
</el-header>
<el-main class="nopadding">
<el-tree ref="group" class="menu" node-key="id" :data="group" :current-node-key="''" :highlight-current="true" :expand-on-click-node="false" :filter-node-method="groupFilterNode" @node-click="groupClick"></el-tree>
</el-main>
</el-container>
</el-aside>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add"></el-button>
<el-button type="danger" plain icon="el-icon-delete" :disabled="selection.length==0" @click="batch_del"></el-button>
<el-button type="primary" plain :disabled="selection.length==0">分配角色</el-button>
<el-button type="primary" plain :disabled="selection.length==0">密码重置</el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<el-input v-model="search.name" placeholder="登录账号 / 姓名" clearable></el-input>
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="apiObj" @selection-change="selectionChange" stripe remoteSort remoteFilter>
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="ID" prop="id" width="80" sortable='custom'></el-table-column>
<el-table-column label="头像" width="80" column-key="filterAvatar" :filters="[{text: '已上传', value: '1'}, {text: '未上传', value: '0'}]">
<template #default="scope">
<el-avatar :src="scope.row.avatar" size="small"></el-avatar>
</template>
</el-table-column>
<el-table-column label="登录账号" prop="userName" width="150" sortable='custom' column-key="filterUserName" :filters="[{text: '系统账号', value: '1'}, {text: '普通账号', value: '0'}]"></el-table-column>
<el-table-column label="姓名" prop="name" width="150" sortable='custom'></el-table-column>
<el-table-column label="所属角色" prop="groupName" width="200" sortable='custom'></el-table-column>
<el-table-column label="加入时间" prop="date" width="170" sortable='custom'></el-table-column>
<el-table-column label="操作" fixed="right" align="right" width="140">
<template #default="scope">
<el-button type="text" size="small" @click="table_show(scope.row, scope.$index)">查看</el-button>
<el-button type="text" size="small" @click="table_edit(scope.row, scope.$index)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
<template #reference>
<el-button type="text" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
</el-container>
<save-dialog v-if="dialog.save" ref="saveDialog" @success="handleSuccess" @closed="dialog.save=false"></save-dialog>
</template>
<script>
import saveDialog from './save'
export default {
name: 'user',
components: {
saveDialog
},
data() {
return {
dialog: {
save: false
},
showGrouploading: false,
groupFilterText: '',
group: [],
apiObj: this.$API.system.user.list,
selection: [],
search: {
name: null
}
}
},
watch: {
groupFilterText(val) {
this.$refs.group.filter(val);
}
},
mounted() {
this.getGroup()
},
methods: {
//添加
add(){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open()
})
},
//编辑
table_edit(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('edit').setData(row)
})
},
//查看
table_show(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('show').setData(row)
})
},
//删除
async table_del(row, index){
var reqData = {id: row.id}
var res = await this.$API.demo.post.post(reqData);
if(res.code == 200){
//这里选择刷新整个表格 OR 插入/编辑现有表格数据
this.$refs.table.tableData.splice(index, 1);
this.$message.success("删除成功")
}else{
this.$alert(res.message, "提示", {type: 'error'})
}
},
//批量删除
async batch_del(){
this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
type: 'warning'
}).then(() => {
const loading = this.$loading();
this.selection.forEach(item => {
this.$refs.table.tableData.forEach((itemI, indexI) => {
if (item.id === itemI.id) {
this.$refs.table.tableData.splice(indexI, 1)
}
})
})
loading.close();
this.$message.success("操作成功")
}).catch(() => {
})
},
//表格选择后回调事件
selectionChange(selection){
this.selection = selection;
},
//加载树数据
async getGroup(){
this.showGrouploading = true;
var res = await this.$API.system.role.list.get();
this.showGrouploading = false;
var allNode ={id: '', label: '所有'}
res.data.unshift(allNode);
this.group = res.data;
},
//树过滤
groupFilterNode(value, data){
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
//树点击事件
groupClick(data){
var params = {
groupId: data.id
}
this.$refs.table.reload(params)
},
//搜索
upsearch(){
this.$refs.table.upData(this.search)
},
//本地更新数据
handleSuccess(data, mode){
if(mode=='add'){
data.id = new Date().getTime()
this.$refs.table.tableData.unshift(data)
}else if(mode=='edit'){
this.$refs.table.tableData.filter(item => item.id===data.id ).forEach(item => {
Object.assign(item, data)
})
}
}
}
}
</script>
<style>
</style>
-17
View File
@@ -1,17 +0,0 @@
<template>
<el-empty :image-size="200" :description="title"></el-empty>
</template>
<script>
export default {
name: 'blank',
data() {
return {
title: "BLANK PAGE :)"
}
}
}
</script>
<style>
</style>
-154
View File
@@ -1,154 +0,0 @@
<template>
<el-container>
<el-main class="nopadding">
<el-calendar v-model="toDay">
<template #dateCell="{data}">
<div class="calendar-item">
<h2>{{ data.day.split('-')[2] }}</h2>
<div v-if="getData(data.day) && data.type=='current-month'" class="calendar-item-info">
<p><el-progress :percentage="getData(data.day).plan" :show-text="false"></el-progress></p>
<p><el-progress :percentage="getData(data.day).complete" status="success" :show-text="false"></el-progress></p>
</div>
</div>
</template>
</el-calendar>
</el-main>
<el-aside style="width: 400px;border-left: 1px solid #e6e6e6;">
<el-container>
<el-header>
<h2 class="dayTitle"><el-icon><el-icon-calendar /></el-icon>{{day}}</h2>
</el-header>
<el-main>
<div class="task-list">
<template v-if="dayItem">
<el-card shadow="hover" v-for="task in dayItem.tasks" :key="task.id" :class="stateMap[task.state]">
<h2>{{task.title}}</h2>
<div class="task-bottom">
<div class="tags">
<el-tag type="info" size="small">#{{task.id}}</el-tag>
<el-tag v-if="task.state=='open'" type="info" size="small">{{task.state}}</el-tag>
<el-tag v-if="task.state=='complete'" type="success" size="small">{{task.state}}</el-tag>
<el-tag v-if="task.state=='timeout'" type="danger" size="small">{{task.state}}</el-tag>
</div>
<el-avatar :size="20" :src="task.avatar"></el-avatar>
</div>
</el-card>
</template>
<template v-else>
<el-empty description="无工作任务" :image-size="100"></el-empty>
</template>
</div>
</el-main>
</el-container>
</el-aside>
</el-container>
</template>
<script>
export default {
name: 'calendar',
data(){
return {
stateMap: {
open: "open",
complete: "complete",
timeout: "timeout"
},
toDay: new Date(this.demoDay()),
data: {
[this.demoDay()]: {
plan: 80,
complete: 66,
tasks: [
{
id: "3601",
title: "vite2 on demand loading popconfirm style missing",
avatar: "img/avatar.jpg",
state: "complete"
},
{
id: "3602",
title: "Switch prevent switching invalid",
avatar: "img/avatar.jpg",
state: "complete"
},
{
id: "3603",
title: "The use of paging in vue3 is inconsistent with the official UI display",
avatar: "img/avatar.jpg",
state: "timeout"
}
]
},
[this.demoDay(1)]: {
plan: 80,
complete: 0,
tasks: [
{
id: "3604",
title: "el-switch @change is auto triggered when page load",
avatar: "img/avatar.jpg",
state: "open"
}
]
},
[this.demoDay(2)]: {
plan: 80,
complete: 0,
tasks: [
{
id: "3605",
title: "locale plugin problem happen in production mode when install element-plus on demand",
avatar: "img/avatar.jpg",
state: "open"
},
{
id: "3606",
title: "table uses fixed=right, which leads to the dislocation of hover style",
avatar: "img/avatar.jpg",
state: "open"
}
]
}
}
}
},
computed: {
day(){
return this.$TOOL.dateFormat(this.toDay,"yyyy-MM-dd");
},
dayItem(){
return this.getData(this.day)
}
},
methods: {
getData(date){
return this.data[date]
},
demoDay(n=0){
var curDate = new Date()
var oneDayTime = 24*60*60*1000
var rDate = new Date(curDate.getTime() + (oneDayTime*n) )
return this.$TOOL.dateFormat(rDate, "yyyy-MM-dd");
}
}
}
</script>
<style scoped>
.calendar-item h2 {font-size: 14px;}
.calendar-item-info {margin-top: 15px;}
.calendar-item-info p {margin-top: 5px;}
.task-list .el-card {margin-bottom: 15px;border-left: 5px solid #ddd;cursor: pointer;}
.task-list .el-card.open {border-color: #ddd;}
.task-list .el-card.complete {border-color: #67C23A;}
.task-list .el-card.timeout {border-color: #f56c6c;}
.task-list h2 {font-size: 14px;font-weight: normal;}
.task-bottom {display: flex;justify-content: space-between;align-items: center;margin-top: 10px;}
.task-bottom .tags .el-tag {margin-right: 5px;}
.dayTitle {font-size: 14px;display: flex;align-items: center;}
.dayTitle i {color: #999;margin-right: 10px;}
</style>
-222
View File
@@ -1,222 +0,0 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-radio-group v-model="dateType" style="margin-right: 15px;">
<el-radio-button label="今天"></el-radio-button>
<el-radio-button label="昨天"></el-radio-button>
<el-radio-button label="最近7天"></el-radio-button>
<el-radio-button label="最近30天"></el-radio-button>
</el-radio-group>
<el-date-picker v-model="date" type="datetimerange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</div>
</el-header>
<el-main>
<el-card shadow="never">
<div class="number-data">
<div class="item">
<h2>浏览量(PV)
<el-tooltip effect="light">
<template #content>
<div style="width: 200px;line-height: 2;">
即通常说的Page View(PV),用户每打开一个网站页面就被记录1次。用户多次打开同一页面,浏览量值累计。
</div>
</template>
<el-icon><el-icon-question-filled /></el-icon>
</el-tooltip>
</h2>
<p>65,715</p>
</div>
<div class="item">
<h2>访客数(UV)
<el-tooltip effect="light">
<template #content>
<div style="width: 200px;line-height: 2;">
一天之内您网站的独立访客数(以Cookie为依据),一天内同一访客多次访问您网站只计算1个访客。
</div>
</template>
<el-icon><el-icon-question-filled /></el-icon>
</el-tooltip>
</h2>
<p>8,936</p>
</div>
<div class="item">
<h2>IP数
<el-tooltip effect="light">
<template #content>
<div style="width: 200px;line-height: 2;">
一天之内您网站的独立访问ip数。
</div>
</template>
<el-icon><el-icon-question-filled /></el-icon>
</el-tooltip>
</h2>
<p>10,279</p>
</div>
<div class="item">
<h2>跳出率
<el-tooltip effect="light">
<template #content>
<div style="width: 200px;line-height: 2;">
只浏览了一个页面便离开了网站的访问次数占总的访问次数的百分比。
</div>
</template>
<el-icon><el-icon-question-filled /></el-icon>
</el-tooltip>
</h2>
<p>27.92%</p>
</div>
<div class="item">
<h2>平均访问时长
<el-tooltip effect="light">
<template #content>
<div style="width: 200px;line-height: 2;">
访客在一次访问中,平均打开网站的时长。即每次访问中,打开第一个页面到关闭最后一个页面的平均值,打开一个页面时计算打开关闭的时间差。
</div>
</template>
<el-icon><el-icon-question-filled /></el-icon>
</el-tooltip>
</h2>
<p>00:19:05</p>
</div>
</div>
<div class="chart">
<el-row>
<el-col :span="8">
<scEcharts height="250px" :option="pie"></scEcharts>
</el-col>
<el-col :span="16">
<scEcharts height="250px" :option="option"></scEcharts>
</el-col>
</el-row>
</div>
</el-card>
<el-card shadow="never">
<scTable ref="table" :data="data" show-summary height="auto">
<el-table-column label="来源类型" prop="type"></el-table-column>
<el-table-column label="网站基础指标" align="center">
<el-table-column label="访客数(UV)" prop="uv" width="150"></el-table-column>
<el-table-column label="IP数" prop="ip" width="150"></el-table-column>
</el-table-column>
<el-table-column label="流量质量指标" align="center">
<el-table-column label="跳出率" prop="out" width="150"></el-table-column>
<el-table-column label="平均访问时长" prop="time" width="150"></el-table-column>
</el-table-column>
</scTable>
</el-card>
</el-main>
</el-container>
</template>
<script>
import scEcharts from '@/components/scEcharts';
export default {
name: 'chartlist',
components: {
scEcharts
},
data(){
return {
dateType: "今天",
date: [new Date(2000, 10, 10, 10, 10), new Date(2000, 10, 11, 10, 10)],
data: [
{
type: "直接访问",
pv: "57,847",
uv: "7,129",
ip: "8,330",
out: "26.38%",
time: "00:20:35"
},
{
type: "搜索引擎",
pv: "5,942",
uv: "1,338",
ip: "1,449",
out: "33.49%",
time: "00:11:31"
},
{
type: "外部链接",
pv: "1,926",
uv: "469",
ip: "500",
out: "44.53%",
time: "00:08:49"
}
],
pie: {
tooltip: {
trigger: 'item'
},
series: [
{
name: '访问来源',
type: 'pie',
radius: ['55%', '70%'],
itemStyle: {
borderRadius: 5,
borderColor: '#fff',
borderWidth: 1
},
data: [
{value: 1048, name: '搜索引擎'},
{value: 235, name: '直接访问'},
{value: 180, name: '外部链接'}
]
}
]
},
option: {
legend: {
data: ['直接访问', '搜索引擎', '外部链接']
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: ['08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00'],
boundaryGap: false,
},
yAxis: {
type: 'value'
},
series: [{
name: '直接访问',
data: [120, 210, 150, 80, 70, 110, 130],
type: 'line',
},
{
name: '搜索引擎',
data: [110, 180, 120, 120, 60, 90, 110],
type: 'line',
},
{
name: '外部链接',
data: [50, 90, 60, 60, 30, 40, 50],
type: 'line',
}]
}
}
}
}
</script>
<style scoped>
.el-card {margin-bottom: 15px;}
.number-data {display: flex;}
.number-data .item {flex:1;border-right: 1px solid #f0f0f0;padding:0 20px;}
.number-data .item:last-child {border: 0;}
.number-data .item h2 {font-size: 12px;color: #787a7d;font-weight: normal;display: flex;align-items: center;}
.number-data .item h2 i {margin-left: 5px;color: #8cc5ff;cursor: pointer;}
.number-data .item p {font-size: 20px;color: #121315;margin-top: 10px;}
.chart {border-top: 1px solid #f0f0f0;margin-top: 20px;padding-top: 20px;}
[data-theme='dark'] .number-data .item {border-color: var(--el-border-color-base);}
[data-theme='dark'] .number-data .item p {color: #d0d0d0;}
[data-theme='dark'] .chart {border-color: var(--el-border-color-base);}
</style>
-249
View File
@@ -1,249 +0,0 @@
<!--
* @Descripttion: 详情列表模板
* @version: 1.1
* @Author: sakuya
* @Date: 2021年6月16日15:05:15
* @LastEditors: sakuya
* @LastEditTime: 2021年7月22日12:18:50
-->
<template>
<el-container>
<el-header style="display: block;height:auto;">
<el-row :gutter="15">
<el-col :lg="6">
<sc-statistic title="数量" value="12" suffix="项"></sc-statistic>
</el-col>
<el-col :lg="6">
<sc-statistic title="总进度" value="70.0" suffix="%"></sc-statistic>
</el-col>
<el-col :lg="6">
<sc-statistic title="收入" value="0.0" prefix="¥" groupSeparator></sc-statistic>
</el-col>
<el-col :lg="6">
<sc-statistic title="支出" value="200" prefix="¥" groupSeparator></sc-statistic>
</el-col>
</el-row>
</el-header>
<el-header class="header-tabs">
<el-tabs type="border-card">
<el-tab-pane label="所有"></el-tab-pane>
<el-tab-pane label="未完成 (2)"></el-tab-pane>
<el-tab-pane label="弃坑"></el-tab-pane>
<el-tab-pane label="其他"></el-tab-pane>
</el-tabs>
</el-header>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add"></el-button>
<el-button v-if="selection.length>0" type="danger" plain icon="el-icon-delete"></el-button>
<el-button v-if="selection.length>0">变更状态</el-button>
<el-button v-if="selection.length>0">推送至队列</el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<scFilterBar :options="options" @change="change"></scFilterBar>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable tableName="templateList" ref="table" :data="list" :column="column" @selection-change="selectionChange" stripe>
<!-- 各列自定义template -->
<template #state="scope">
<sc-status-indicator v-if="scope.row.state=='1'" type="primary"></sc-status-indicator>
<sc-status-indicator v-if="scope.row.state=='2'" pulse type="danger"></sc-status-indicator>
</template>
<template #name="scope">
<h4>{{scope.row.name}}</h4>
<p>{{scope.row.subtitle}}</p>
</template>
<template #type="scope">
<el-tag>{{scope.row.type}}</el-tag>
</template>
<template #progress="scope">
<el-progress v-if="scope.row.state=='1'" :percentage="scope.row.progress"></el-progress>
<el-progress v-if="scope.row.state=='2'" :percentage="scope.row.progress" status="exception"></el-progress>
</template>
<!-- 固定列-选择列 -->
<el-table-column type="selection" width="50" fixed></el-table-column>
<!-- 固定列-操作列 -->
<el-table-column label="操作" fixed="right" align="right" width="120">
<template #default="scope">
<el-button type="text" size="small" @click="table_show(scope.row, scope.$index)">查看</el-button>
<el-divider direction="vertical"></el-divider>
<el-dropdown>
<el-button type="text" size="small">更多<el-icon class="el-icon--right"><el-icon-arrow-down /></el-icon></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="table_edit(scope.row, scope.$index)">编辑</el-dropdown-item>
<el-dropdown-item>推送至队列</el-dropdown-item>
<el-dropdown-item divided>删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<el-drawer v-model="info" :size="800" custom-class="drawerBG" direction="rtl" destroy-on-close>
<info></info>
</el-drawer>
</template>
<script>
import scStatistic from '@/components/scStatistic';
import scFilterBar from '@/components/scFilterBar';
import info from './info'
export default {
name: 'list',
components: {
scStatistic,
scFilterBar,
info
},
data() {
return {
options: [
{
label: '名称',
value: 'name',
type: 'text'
},
{
label: '类型',
value: 'type',
type: 'select',
extend: {
multiple: true,
data:[
{
label: "数据",
value: "1"
},
{
label: "表单",
value: "2"
}
]
}
},
{
label: '创建时间',
value: 'date',
type: 'daterange',
}
],
column: [
{
label: "ID",
prop: "id",
width: "100",
sortable: true,
hide: true,
},
{
label: "名称",
prop: "name",
width: "300"
},
{
label: "状态",
prop: "state",
width: "100",
filters: [{text: '正常', value: '1'}, {text: '异常', value: '2'}]
},
{
label: "类型",
prop: "type",
width: "100",
filters: [{text: '数据', value: '数据'}, {text: '表单', value: '表单'}]
},
{
label: "负责人",
prop: "user",
width: "100"
},
{
label: "进度",
prop: "progress",
width: "250"
},
{
label: "创建时间",
prop: "time",
width: "150",
sortable: true
},
],
group: "0",
selection: [],
list: [],
info: false
}
},
mounted() {
this.list = [
{
id: "5001",
name: "scEcharts",
subtitle: "重新封装的Echarts,暴露源对象",
state: "1",
type: "数据",
progress: 70,
user: "Sakuya",
time: "2010-10-10"
},
{
id: "5002",
name: "scEditor",
subtitle: "Tinymce封装的富文本编辑器",
state: "2",
type: "表单",
progress: 40,
user: "Sakuya",
time: "2010-10-10"
}
]
},
methods: {
//表格选择后回调事件
selectionChange(selection){
this.selection = selection;
},
add(){
this.$router.push({
path: `/template/list/save`
});
},
table_show(row, index){
console.log(row, index);
this.info = true;
},
table_edit(row){
this.$router.push({
path: `/template/list/save`,
query: {
id: row.id
}
});
},
filterHandler(value, row, column){
const property = column.property;
return row[property] === value;
},
change(data){
console.log(data);
}
}
}
</script>
<style></style>
-153
View File
@@ -1,153 +0,0 @@
<template>
<el-main style="padding:0 20px;">
<el-steps :active="3" finish-status="success" align-center style="margin-bottom:40px;">
<el-step title="立项"></el-step>
<el-step title="调研"></el-step>
<el-step title="设计"></el-step>
<el-step title="开发"></el-step>
<el-step title="完成"></el-step>
</el-steps>
<div class="make">
<div class="make-left">
<el-button type="primary">主要操作</el-button>
<el-button>次要操作</el-button>
<el-button>次要操作</el-button>
<el-dropdown>
<el-button icon="el-icon-arrow-down"></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>更改状态</el-dropdown-item>
<el-dropdown-item>发送队列</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div class="make-right">
<el-button type="danger" plain icon="el-icon-delete"></el-button>
</div>
</div>
<el-card shadow="never" header="统计">
<sc-echarts ref="chart" height="150px" :option="option"></sc-echarts>
</el-card>
<el-card shadow="never">
<el-tabs>
<el-tab-pane label="基础信息">
<el-descriptions :column="2" border>
<el-descriptions-item label="ID">5001</el-descriptions-item>
<el-descriptions-item label="名称">scEcharts</el-descriptions-item>
<el-descriptions-item label="参与者">
<el-tag size="small">Sakuya</el-tag>
<el-tag size="small">Lolowan</el-tag>
</el-descriptions-item>
<el-descriptions-item label="类型">数据</el-descriptions-item>
<el-descriptions-item label="创建时间">2010-10-10</el-descriptions-item>
<el-descriptions-item label="进度">70%</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="详细信息">
<el-empty description="没有详细信息" :image-size="80"></el-empty>
</el-tab-pane>
</el-tabs>
</el-card>
<el-card shadow="never" header="日志">
<el-timeline style="padding-left: 10px;padding-top: 10px;">
<el-timeline-item v-for="(item, index) in logs" :key="index" :timestamp="item.timestamp" :color="index==0?'#409EFF':''">
<div class="log-item">
<el-avatar class="avatar" :size="24" :src="item.icon"></el-avatar>
<span class="user">{{item.user}}</span>
<p>{{item.content}}</p>
</div>
<div v-if="item.msg" class="log-msg">
<el-alert :title="item.msg" :closable="false"></el-alert>
</div>
</el-timeline-item>
</el-timeline>
</el-card>
</el-main>
</template>
<script>
import scEcharts from '@/components/scEcharts';
export default {
components: {
scEcharts
},
data() {
return {
option: {
grid: {
top: '10px'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'bar',
barWidth: '15px',
itemStyle: {
borderRadius:[15, 15, 0, 0]
}
},
{
data: [110, 180, 120, 120, 60, 90, 110],
type: 'line',
smooth: true
}]
},
logs: [
{
user: 'Lolowan',
icon: 'img/avatar2.gif',
content: '完成设计',
msg: '@Sakuya 设计完成了,赶紧开发吧',
timestamp: '2021-10-13 13:41:19'
},
{
user: 'Sakuya',
icon: 'img/avatar.jpg',
content: '完成调研',
msg: '',
timestamp: '2021-10-11 22:08:30'
},
{
user: 'Sakuya',
icon: 'img/avatar.jpg',
content: '创建立项',
msg: '',
timestamp: '2021-10-10 08:31:23'
},
]
}
},
mounted() {
},
methods: {
}
}
</script>
<style scoped>
.el-card {margin-bottom: 15px;}
.make {margin-bottom: 15px;display: flex;justify-content: space-between;align-items: center;}
.log-item {display: flex;align-items: center;font-size: 13px;}
.log-item .user {color: #333;margin: 0 10px;display: inline-block;}
.log-item p {color: #666;}
.log-msg {margin-top: 10px;}
</style>
-73
View File
@@ -1,73 +0,0 @@
<!--
* @Descripttion: 多路由复用一个路由组件DEMO文件
* @version: 1.0
* @Author: sakuya
* @Date: 2021年7月20日09:28:19
* @LastEditors:
* @LastEditTime:
-->
<template>
<sc-page-header :title="id?'ID:'+id:'页面标题'" description="可用于非常复杂的表单提交,如一些较为简单的表单提交应使用dialog或者drawer更合适" icon="el-icon-burger"></sc-page-header>
<el-main>
<el-alert title="因为keep-alive只接受组件name,导致多路由共用组件时,关闭或刷新一个标签导致其他同一组件的页面缓存失效,后续还在寻找完美的解决方案." type="error" style="margin-bottom: 15px;"></el-alert>
<el-card shadow="never">
<el-form :model="form" :rules="rules" ref="form" label-width="100px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" clearable></el-input>
<div class="el-form-item-msg" data-v-b33b3cf8="">组件名字以sc开头,并坚持以“小驼峰”命名规则</div>
</el-form-item>
<el-form-item label="缩略图" prop="img">
<sc-upload v-model="form.img" title="上传缩略图"></sc-upload>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-radio-group v-model="form.type">
<el-radio-button label="1">数据</el-radio-button>
<el-radio-button label="2">表单</el-radio-button>
<el-radio-button label="3">样式</el-radio-button>
<el-radio-button label="4">其他</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="负责人" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" clearable></el-input>
</el-form-item>
<el-form-item label="进度" prop="img">
<el-input v-model="form.name" placeholder="请输入名称" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="save">保 存</el-button>
</el-form-item>
</el-form>
</el-card>
</el-main>
</template>
<script>
export default {
name: 'list-save',
data() {
return {
id: this.$route.query.id,
form: {
name: "",
type: "1"
},
rules: {
}
}
},
mounted() {
if(this.id){
//更改tag标签
this.$store.commit("updateViewTagsTitle", `详情ID:${this.id}`)
}
},
methods: {
save(){
console.log(this.$store);
}
}
}
</script>
-615
View File
@@ -1,615 +0,0 @@
<template>
<el-container>
<el-aside width="220px">
<el-container>
<el-header>
<el-input placeholder="输入关键字进行过滤" v-model="groupFilterText" clearable></el-input>
</el-header>
<el-main class="nopadding">
<el-tree ref="group" class="menu" :data="group" node-key="code" :current-node-key="group[0].code" highlight-current :filter-node-method="groupFilterNode"></el-tree>
</el-main>
</el-container>
</el-aside>
<el-main style="padding:0;">
<el-tabs class="tabs-pages" stretch>
<el-tab-pane label="服务器概况" lazy>
<el-main>
<el-card shadow="never" header="SQL Server">
<el-row>
<el-col :span="8">
<div class="server-top-item">
<el-progress type="dashboard" :percentage="25" :stroke-width="8" :width="150"
:color="colors">
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}<em>%</em></span>
<span class="percentage-label">CPU</span>
</template>
</el-progress>
</div>
</el-col>
<el-col :span="8">
<div class="server-top-item">
<el-progress type="dashboard" :percentage="33.2" :stroke-width="8" :width="150"
:color="colors">
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}<em>%</em></span>
<span class="percentage-label">Memory</span>
</template>
</el-progress>
</div>
</el-col>
<el-col :span="8">
<div class="server-top-item">
<el-progress type="dashboard" :percentage="81.0" :stroke-width="8" :width="150"
:color="colors">
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}<em>%</em></span>
<span class="percentage-label">Disk</span>
</template>
</el-progress>
</div>
</el-col>
</el-row>
<el-tabs style="margin-top: 20px;" @tab-click="tabClick">
<el-tab-pane label="CPU" lazy>
<div class="server-item-info">
<div class="title">
<label>CPU</label>
<span>Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz</span>
</div>
<div class="chart" style="border-color: #409EFF;background: rgba(64,158,255,0.05);">
<scEcharts ref="cpu" height="100%" :option="option.cpu"></scEcharts>
</div>
<div class="data">
<el-descriptions :column="2" size="small" border title="Information">
<el-descriptions-item label="利用率">35%</el-descriptions-item>
<el-descriptions-item label="速度">2.67 GHz</el-descriptions-item>
<el-descriptions-item label="内核">4</el-descriptions-item>
<el-descriptions-item label="逻辑处理器">8</el-descriptions-item>
<el-descriptions-item label="正常运行时间">0:06:06:04</el-descriptions-item>
</el-descriptions>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="Memory" lazy>
<div class="server-item-info">
<div class="title">
<label>Memory</label>
<span>16.0 GB</span>
</div>
<div class="chart" style="border-color: #8b12ae;background: rgba(139,18,174,0.05);">
<scEcharts ref="memory" height="100%" :option="option.memory"></scEcharts>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="Disk" lazy>
<div class="server-item-info">
<div class="title">
<label>Disk</label>
<span>SAMSUNG MZVLB512HBJQ-000L7</span>
</div>
<div class="disk-list">
<el-row :gutter="20">
<el-col :span="6">
<div class="disk-list-item">
<h2>本地磁盘(C)</h2>
<el-progress :stroke-width="10" :percentage="50" :color="colors" />
<p>66.2 GB 可用,共 175 GB</p>
</div>
</el-col>
<el-col :span="6">
<div class="disk-list-item">
<h2>本地磁盘(D)</h2>
<el-progress :stroke-width="10" :percentage="60" :color="colors" />
<p>66.2 GB 可用,共 175 GB</p>
</div>
</el-col>
<el-col :span="6">
<div class="disk-list-item">
<h2>本地磁盘(E)</h2>
<el-progress :stroke-width="10" :percentage="81" :color="colors" />
<p>66.2 GB 可用,共 175 GB</p>
</div>
</el-col>
<el-col :span="6">
<div class="disk-list-item">
<h2>本地磁盘(F)</h2>
<el-progress :stroke-width="10" :percentage="15.5" :color="colors" />
<p>66.2 GB 可用,共 175 GB</p>
</div>
</el-col>
</el-row>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="Network" lazy>
<div class="server-item-info">
<div class="title">
<label>Network</label>
<span>Intel(R) Ethernet Connection (6) 1219-V</span>
</div>
<div class="chart" style="border-color: #a74f01;background: rgba(167,79,1,0.05);">
<scEcharts ref="network" height="100%" :option="option.network"></scEcharts>
</div>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</el-main>
</el-tab-pane>
<el-tab-pane label="报警管理" lazy>
<el-main>
<el-card shadow="never" header="Event">
<scTable ref="table" :data="event" stripe height="auto">
<el-table-column label="#" type="index" width="50"></el-table-column>
<el-table-column label="事件" prop="title" width="150"></el-table-column>
<el-table-column label="等级" prop="type" width="150"></el-table-column>
<el-table-column label="通知" prop="push" ></el-table-column>
<el-table-column label="触发时间" prop="time" width="180"></el-table-column>
</scTable>
</el-card>
<el-card shadow="never" header="Rule" style="margin-top: 15px;">
<el-table :data="rule" stripe>
<el-table-column label="规则名称" prop="name" width="150"></el-table-column>
<el-table-column label="描述" prop="title" width="150"></el-table-column>
<el-table-column label="告警类型" prop="type" width="150"></el-table-column>
<el-table-column label="通知" prop="push" width="150"></el-table-column>
<el-table-column></el-table-column>
<el-table-column label="操作" width="150" align="right">
<el-dropdown>
<el-button type="text" size="small" icon="el-icon-more"></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>编辑</el-dropdown-item>
<el-dropdown-item divided>删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-table-column>
</el-table>
<el-button type="primary" icon="el-icon-plus" style="margin-top: 20px;"></el-button>
</el-card>
</el-main>
</el-tab-pane>
</el-tabs>
</el-main>
</el-container>
</template>
<script>
import scEcharts from '@/components/scEcharts';
export default {
name: 'server',
components: {
scEcharts
},
data() {
return {
groupFilterText: '',
group: [
{
label: "SQL Server",
icon: "el-icon-coin",
code: "1",
}, {
label: "Web Server",
icon: "el-icon-monitor",
code: "2",
}, {
label: "Api Server",
icon: "el-icon-monitor",
code: "3",
}, {
label: "Nginx Server",
icon: "el-icon-guide",
code: "4",
}
],
colors: [{
color: '#409EFF',
percentage: 60
},
{
color: '#E6A23C',
percentage: 80
},
{
color: '#F56C6C',
percentage: 100
}
],
event: [
{
title: 'Disk 大于 90%',
type: '告警事件',
push: '短信',
time: '2021-10-09 21:25:19'
},
{
title: 'Disk 大于 90%',
type: '致命事件',
push: '短信',
time: '2021-10-09 21:25:19'
}
],
rule: [
{
name: 'test_cpu',
title: '测试CPU报警',
type: 'CPU',
push: '短信'
},
{
name: 'test_cpu',
title: '测试CPU报警',
type: 'CPU',
push: '短信'
}
],
option: {
cpu: {
tooltip: {
trigger: 'axis'
},
grid: {
top: '0',
bottom: '0',
left: '0',
right: '0'
},
animation: false,
xAxis: {
type: 'category',
boundaryGap: false,
data: (function() {
var now = new Date();
var res = [];
var len = 60;
while (len--) {
res.unshift(now.toLocaleTimeString().replace(/^\D*/, ''));
now = new Date(now - 2000);
}
return res;
})(),
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false
}
},
yAxis: {
type: 'value',
min: 0,
max: 100,
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false
}
},
series: [{
data: (function() {
var res = [];
var len = 60;
while (len--) {
res.push(Math.round(Math.random() * 0));
}
return res;
})(),
type: 'line',
symbol: 'none',
lineStyle: {
width: 1,
color: '#409EFF'
},
areaStyle: {
opacity: 0.1,
color: '#409EFF'
},
}]
},
memory: {
tooltip: {
trigger: 'axis'
},
grid: {
top: '0',
bottom: '0',
left: '0',
right: '0'
},
animation: false,
xAxis: {
type: 'category',
boundaryGap: false,
data: (function() {
var now = new Date();
var res = [];
var len = 60;
while (len--) {
res.unshift(now.toLocaleTimeString().replace(/^\D*/, ''));
now = new Date(now - 2000);
}
return res;
})(),
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false
}
},
yAxis: {
type: 'value',
min: 0,
max: 100,
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false
}
},
series: [{
data: (function() {
var res = [];
var len = 60;
while (len--) {
res.push(Math.round(Math.random() * 0));
}
return res;
})(),
type: 'line',
symbol: 'none',
lineStyle: {
width: 1,
color: '#8b12ae'
},
areaStyle: {
opacity: 0.1,
color: '#8b12ae'
},
}]
},
network: {
tooltip: {
trigger: 'axis'
},
grid: {
top: '0',
bottom: '0',
left: '0',
right: '0'
},
animation: false,
legend: {
data: ['接收 Kbps', '发送 Kbps'],
top: '10px',
right: '10px',
itemStyle: {
opacity: 0
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: (function() {
var now = new Date();
var res = [];
var len = 60;
while (len--) {
res.unshift(now.toLocaleTimeString().replace(/^\D*/, ''));
now = new Date(now - 2000);
}
return res;
})(),
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false
}
},
yAxis: {
type: 'value',
min: 0,
max: 100,
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false
}
},
series: [{
name: '接收 Kbps',
data: (function() {
var res = [];
var len = 60;
while (len--) {
res.push(Math.round(Math.random() * 0));
}
return res;
})(),
type: 'line',
symbol: 'none',
lineStyle: {
width: 1,
color: '#a74f01'
},
areaStyle: {
opacity: 0.1,
color: '#a74f01'
},
}, {
name: '发送 Kbps',
data: (function() {
var res = [];
var len = 60;
while (len--) {
res.push(Math.round(Math.random() * 0));
}
return res;
})(),
type: 'line',
smooth: false,
symbol: 'none',
lineStyle: {
width: 1,
type: 'dashed',
color: '#a74f01'
}
}]
}
}
}
},
watch: {
groupFilterText(val) {
this.$refs.group.filter(val);
}
},
mounted() {
setInterval(() => {
this.option.cpu.series[0].data.shift()
this.option.cpu.series[0].data.push(Math.round(Math.random() * 100));
this.option.cpu.xAxis.data.shift();
this.option.cpu.xAxis.data.push((new Date()).toLocaleTimeString().replace(/^\D*/, ''));
this.option.memory.series[0].data.shift()
this.option.memory.series[0].data.push(Math.round(Math.random() * 50));
this.option.memory.xAxis.data.shift();
this.option.memory.xAxis.data.push((new Date()).toLocaleTimeString().replace(/^\D*/, ''));
this.option.network.series[0].data.shift()
this.option.network.series[0].data.push(Math.round(Math.random() * 20));
this.option.network.series[1].data.shift()
this.option.network.series[1].data.push(Math.round(Math.random() * 10));
this.option.network.xAxis.data.shift();
this.option.network.xAxis.data.push((new Date()).toLocaleTimeString().replace(/^\D*/, ''));
}, 1000)
},
methods: {
//树过滤
groupFilterNode(value, data){
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
tabClick(tab) {
this.$nextTick(() => {
if(tab.props.label == 'CPU' && this.$refs.cpu.myChart){
this.$refs.cpu.myChart.resize()
}
if(tab.props.label == 'Memory' && this.$refs.memory.myChart){
this.$refs.memory.myChart.resize()
}
if(tab.props.label == 'Network' && this.$refs.network.myChart){
this.$refs.network.myChart.resize()
}
})
}
}
}
</script>
<style>
.tabs-pages {display: flex;flex-flow: column;flex-shrink: 0;height: 100%;}
.tabs-pages > .el-tabs__header {background: #fff;margin: 0;}
.tabs-pages > .el-tabs__header .el-tabs__nav-wrap {display: flex;justify-content:center;}
.tabs-pages > .el-tabs__header .el-tabs__item {height:60px;line-height: 60px;font-size: 14px;}
.tabs-pages > .el-tabs__content {overflow-x: hidden;overflow: auto;}
[data-theme="dark"] .tabs-pages > .el-tabs__header {background: #2b2b2b;}
</style>
<style scoped>
.server-top-item {
text-align: center;
}
.server-top-item .percentage-value {
display: block;
font-size: 24px;
font-weight: bold;
}
.server-top-item .percentage-value em {
font-size: 14px;
font-style: normal;
margin-left: 5px;
font-weight: normal;
}
.server-top-item .percentage-label {
display: block;
font-size: 12px;
margin-top: 10px;
}
.server-item-info {}
.server-item-info .title {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.server-item-info .title label {
font-size: 26px;
}
.server-item-info .title span {
font-size: 14px;
}
.server-item-info .chart {
height: 250px;
border: 1px solid #eee;
}
.server-item-info .data {
margin-top: 20px;
}
.disk-list-item {
margin: 10px 0;
}
.disk-list-item h2 {
font-weight: normal;
font-size: 12px;
}
.disk-list-item .el-progress {
margin: 5px 0;
}
.disk-list-item p {
color: #999;
font-size: 12px;
}
</style>
-150
View File
@@ -1,150 +0,0 @@
<template>
<el-main>
<el-card shadow="never" header="分步表单">
<el-steps :active="active" align-center style="margin-bottom: 20px;">
<el-step title="填写转账信息"></el-step>
<el-step title="确认转账信息"></el-step>
<el-step title="完成"></el-step>
</el-steps>
<el-row>
<el-col :lg="{span: 8, offset: 8}">
<el-form v-if="active==0" ref="stepForm_0" :model="form" :rules="rules" label-position="top">
<el-form-item label="付款账户" prop="paymentAccount">
<el-select v-model="form.paymentAccount" clearable>
<el-option label="sakuya@scui.com" value="sakuya@scui.com"></el-option>
<el-option label="lolicode@scui.com" value="lolicode@scui.com"></el-option>
</el-select>
</el-form-item>
<el-form-item label="收款账户" prop="collectionAccount">
<el-input v-model="form.collectionAccount" clearable></el-input>
</el-form-item>
<el-form-item label="收款人姓名" prop="collectionName">
<el-input v-model="form.collectionName" clearable></el-input>
</el-form-item>
<el-form-item label="转账金额" prop="amount">
<el-input v-model="form.amount"></el-input>
</el-form-item>
</el-form>
<el-form v-if="active==1" ref="stepForm_1" :model="form" :rules="rules" label-position="top">
<el-alert title="确认转账后,资金将直接打入对方账户,无法退回。" type="warning" show-icon style="margin-bottom: 15px;"/>
<el-descriptions :column="1" border>
<el-descriptions-item label="付款账户">{{form.paymentAccount}}</el-descriptions-item>
<el-descriptions-item label="收款账户">{{form.collectionAccount}}</el-descriptions-item>
<el-descriptions-item label="收款人姓名">{{form.collectionName}}</el-descriptions-item>
<el-descriptions-item label="转账金额">{{form.amount}} 元</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-form-item label="支付密码" prop="payPassword">
<el-input v-model="form.payPassword" show-password placeholder="请输入"></el-input>
</el-form-item>
</el-form>
<div v-if="active==2">
<el-result icon="success" title="操作成功" sub-title="预计两小时内到账">
<template #extra>
<el-button type="primary" @click="again">再转一笔</el-button>
<el-button>查看账单</el-button>
</template>
</el-result>
</div>
<el-button v-if="active>0 && active<2" @click="pre" :disabled="submitLoading">上一步</el-button>
<el-button v-if="active<1" type="primary" @click="next">下一步</el-button>
<el-button v-if="active==1" type="primary" @click="submit" :loading="submitLoading">提交</el-button>
</el-col>
</el-row>
</el-card>
</el-main>
</template>
<script>
export default {
name: 'stepform',
data() {
return {
active: 0,
submitLoading: false,
form: {
paymentAccount: 'lolicode@scui.com',
collectionAccount: 'test@example.com',
collectionName: 'Lolowan',
amount: '100',
payPassword: ''
},
rules: {
paymentAccount: [
{
required: true,
message: '请选择付款账户',
}
],
collectionAccount: [
{
required: true,
message: '请输入收款账户',
}
],
collectionName: [
{
required: true,
message: '请输入收款人姓名',
}
],
amount: [
{
required: true,
message: '请输入转账金额',
}
],
payPassword: [
{
required: true,
message: '需要支付密码才能进行支付',
}
]
}
}
},
mounted() {
},
methods: {
//下一步
next(){
const formName = `stepForm_${this.active}`
this.$refs[formName].validate((valid) => {
if (valid) {
this.active += 1
}else{
return false
}
})
},
//上一步
pre(){
this.active -= 1
},
//提交
submit(){
const formName = `stepForm_${this.active}`
this.$refs[formName].validate((valid) => {
if (valid) {
this.submitLoading = true
setTimeout(()=>{
this.submitLoading = false
this.active += 1
},1000)
}else{
return false
}
})
},
//再来一次
again(){
this.active = 0
}
}
}
</script>
<style scoped>
.el-steps:deep(.is-finish) .el-step__line {background: var(--el-color-primary);}
</style>
-148
View File
@@ -1,148 +0,0 @@
<!--
* @Descripttion: SVG MAP
* @version: 2.0
* @Author: sakuya
* @Date: 2021年6月16日15:05:15
* @LastEditors: sakuya
* @LastEditTime: 2021年8月23日13:42:58
-->
<template>
<el-container>
<el-main class="nopadding" style="background: #f6f8f9;" v-loading="mapLoading">
<scEcharts ref="map" :option="option"></scEcharts>
</el-main>
<el-aside width="340px" style="border-left: 1px solid #e6e6e6;border-right: 0;padding:15px;">
<el-descriptions title="Shanghai China" :column="1" border>
<el-descriptions-item label="region">Shanghai</el-descriptions-item>
<el-descriptions-item label="area">6340.5 km2</el-descriptions-item>
<el-descriptions-item label="state"><em class="state state-1 status-processing"></em></el-descriptions-item>
</el-descriptions>
<el-collapse style="margin-top: 15px;">
<el-collapse-item title="video monitor" name="1">
<div class="screen" style="background: #000;height:180px;color: #999;text-align: center;">
<el-icon style="font-size: 40px;margin-top: 50px;"><el-icon-video-play /></el-icon>
<p>camera</p>
</div>
</el-collapse-item>
</el-collapse>
<el-alert title="地图数据来自阿里项目 DATAV.GeoAtlas" type="info" style="margin-top: 15px;"></el-alert>
</el-aside>
</el-container>
</template>
<script>
import scEcharts from '@/components/scEcharts';
export default {
name: 'svgmap',
components: {
scEcharts
},
data() {
return {
mapLoading: false,
option: {}
}
},
mounted() {
this.getSvg()
},
methods: {
async getSvg(){
this.mapLoading = true;
var map = await this.$HTTP.get('img/shanghai.json')
this.mapLoading = false;
scEcharts.registerMap('shanghai', map);
this.option = {
title: {
text: 'Map Demo',
subtext: '可用于展示GeoJson/SVG的地图或者其它图形',
left: '20',
top: '20'
},
tooltip: {
},
geo: {
map: 'shanghai',
zoom: 1,
roam: true,
selectedMode: 'single',
itemStyle: {
areaColor: 'rgba(128, 128, 128, 0.1)',
borderColor: 'rgba(0, 0, 0, 0.2)',
borderWidth: 1
},
select: {
itemStyle: {
color: 'rgba(0, 153, 255, 0.8)'
},
label: {
show: false,
}
},
emphasis: {
itemStyle: {
color: null
},
label: {
show: false
}
}
},
series: {
name: '告警',
type: 'effectScatter',
symbolSize: 20,
coordinateSystem: 'geo',
geoIndex: 0,
encode: {
tooltip: 2
},
data: [
[121.3154759073276, 30.819428360452587]
]
}
}
var myChart = this.$refs.map.myChart
myChart.on('geoselectchanged', function (params) {
var selectedNames = params.allSelected[0].name;
console.log('selected', selectedNames);
});
myChart.getZr().on('click', function (params) {
var pixelPoint = [params.offsetX, params.offsetY];
var dataPoint = myChart.convertFromPixel({ geoIndex: 0 }, pixelPoint);
// 在 SVG 上点击时,坐标会被打印。
// 这些坐标可以在 `series.data` 里使用。
console.log(dataPoint);
});
},
}
}
</script>
<style scoped>
.state {width:8px;height:8px;background: #ddd;display: inline-block;border-radius: 50%;vertical-align: middle;}
.state-1 {background: #409EFF;}
.state-2 {background: #F56C6C;}
.status-processing {position: relative;}
.status-processing:after {position: absolute;top:0px;left:0px;width: 100%;height: 100%;border-radius: 50%;background: inherit;content: '';animation: warn 1.2s ease-in-out infinite;}
@keyframes warn {
0% {
transform: scale(0.5);
opacity: 1;
}
30% {
opacity: 1;
}
100% {
transform: scale(2);
opacity: 0;
}
}
</style>
-214
View File
@@ -1,214 +0,0 @@
<!--
* @Descripttion: 分栏明细模板
* @version: 1.0
* @Author: sakuya
* @Date: 2021年7月26日08:59:14
* @LastEditors:
* @LastEditTime:
-->
<template>
<el-container>
<el-main ref="printMain">
<div class="innerPage">
<el-row :gutter="15">
<el-col :lg="8">
<el-card shadow="never" class="top-state">
<h2>OID<b>2000832</b><span>复制</span></h2>
<p>当前状态:物流配送中</p>
</el-card>
</el-col>
<el-col :lg="16">
<el-card shadow="never">
<el-row :gutter="15">
<el-col :lg="8">
<sc-statistic title="订单量" value="6400" suffix="Kg" groupSeparator></sc-statistic>
</el-col>
<el-col :lg="8">
<sc-statistic title="发货量" value="1568.873" suffix="Kg" groupSeparator></sc-statistic>
</el-col>
<el-col :lg="8">
<sc-statistic title="订单金额" value="1920000" prefix="¥" groupSeparator></sc-statistic>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
<el-card shadow="never" header="进程">
<el-steps :active="2" finish-status="success">
<el-step title="创建" description="创建人 Sakuya"></el-step>
<el-step title="分配" description="承运商 SCUI logistics"></el-step>
<el-step title="配送" description="3 条物流记录"></el-step>
<el-step title="完成"></el-step>
</el-steps>
</el-card>
<el-card shadow="never" style="margin-top: 15px;">
<el-tabs>
<el-tab-pane label="基础信息">
<el-descriptions :column="3" border size="small">
<el-descriptions-item label="订单号">2000832</el-descriptions-item>
<el-descriptions-item label="创建人">sakuya</el-descriptions-item>
<el-descriptions-item label="创建时间">2021-07-21 13:10:00</el-descriptions-item>
<el-descriptions-item label="客户编号">0000017</el-descriptions-item>
<el-descriptions-item label="客户名称">上海红谷物流有限公司</el-descriptions-item>
<el-descriptions-item label="客户评级">
<el-rate v-model="rate" disabled>
</el-rate>
</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="物流记录" lazy>
<scTable ref="table" :data="list" stripe hideDo>
<el-table-column label="ID" prop="id" width="80" sortable></el-table-column>
<el-table-column label="载具" prop="vehicle" width="120"></el-table-column>
<el-table-column label="驾驶员" prop="driver" width="120"></el-table-column>
<el-table-column label="状态" prop="state" width="120"></el-table-column>
<el-table-column label="载量" prop="quantity" width="120" sortable></el-table-column>
</scTable>
</el-tab-pane>
<el-tab-pane label="操作日志" lazy>
<el-timeline style="padding:15px;">
<el-timeline-item v-for="(item, index) in logs" :key="index" :timestamp="item.timestamp">
{{item.content}}
</el-timeline-item>
</el-timeline>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</el-main>
<el-aside width="300px" style="border-left: 1px solid #e6e6e6;border-right: 0;padding:15px;">
<div class="side-item">
<div class="header">操作</div>
<div class="content">
<el-button type="primary" size="small" @click="print">打印</el-button>
<el-button size="small">次要操作</el-button>
</div>
</div>
<div class="side-item">
<div class="header">状态</div>
<div class="content">
<p>进行中</p>
</div>
</div>
<div class="side-item">
<div class="header">标记</div>
<div class="content">
<el-tag>重要</el-tag>
<el-tag type="danger">紧急</el-tag>
</div>
</div>
<div class="side-item">
<div class="header">指数</div>
<div class="content">
<scEcharts height="200px" :option="radarsOption"></scEcharts>
</div>
</div>
</el-aside>
</el-container>
</template>
<script>
import scStatistic from '@/components/scStatistic'
import scEcharts from '@/components/scEcharts'
import print from '@/utils/print'
export default {
name: 'tabinfo',
components: {
scStatistic,
scEcharts
},
data() {
return {
rate: 3.7,
logs: [
{
content: 'sakuya 创建了物流记录 1',
timestamp: '2018-04-17'
},
{
content: 'sakuya 维护了客户信息',
timestamp: '2018-04-15'
},
{
content: 'sakuya 创建订单',
timestamp: '2018-04-15'
}
],
list: [
{
id: 1,
vehicle: '沪A88808',
driver: '刘翔',
state: '0',
quantity: '29.654'
},
{
id: 2,
vehicle: '沪A88808',
driver: '刘翔',
state: '0',
quantity: '28.850'
},
{
id: 3,
vehicle: '沪A3B88D',
driver: '刘翔',
state: '0',
quantity: '28.159'
}
],
radarsOption: {
radar: {
radius: 60,
center: ['50%', '50%'],
indicator: [
{ name: '人员', max: 100},
{ name: '设备', max: 100},
{ name: '产品', max: 100},
{ name: '服务', max: 100},
{ name: '耗时', max: 100}
]
},
series: [{
name: "SCUI",
type: 'radar',
areaStyle: {},
data: [
{
value: [50, 28, 89, 70, 10],
}
]
}]
}
}
},
mounted() {
},
methods: {
print(){
print(this.$refs.printMain)
}
}
}
</script>
<style scoped>
.innerPage {width: 1000px;margin:0 auto;}
.top-state {background: #409EFF;color: #fff;}
.top-state h2 {font-size: 17px;font-weight: normal;}
.top-state h2 b {margin-left: 10px;}
.top-state h2 span {font-size: 12px;margin-left: 15px;}
.top-state p {opacity: 0.8;margin-top: 15px;}
.side-item {border-bottom: 1px solid #eee;margin-bottom: 30px;}
.side-item .header {font-size: 14px;margin-bottom: 15px;font-weight: bold;}
.side-item .content {padding-bottom:15px;}
@media (max-width: 1610px){
.innerPage {width: 100%;}
}
</style>
Loaded 100 of 145 files, more files were not shown because too many files have changed in this diff. Show more