Viviman
2 years ago
40 changed files with 4692 additions and 0 deletions
@ -0,0 +1,11 @@ |
|||||
|
<template> |
||||
|
<div id="app"> |
||||
|
<router-view /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'App' |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,52 @@ |
|||||
|
import Vue from 'vue' |
||||
|
|
||||
|
import Cookies from 'js-cookie' |
||||
|
|
||||
|
import 'normalize.css/normalize.css' |
||||
|
|
||||
|
import Element from 'element-ui' |
||||
|
//
|
||||
|
import mavonEditor from 'mavon-editor' |
||||
|
import 'mavon-editor/dist/css/index.css' |
||||
|
|
||||
|
// 数据字典
|
||||
|
import dict from './components/Dict' |
||||
|
|
||||
|
// 权限指令
|
||||
|
import permission from './components/Permission' |
||||
|
import './assets/styles/element-variables.scss' |
||||
|
// global css
|
||||
|
import './assets/styles/index.scss' |
||||
|
|
||||
|
// 代码高亮
|
||||
|
import VueHighlightJS from 'vue-highlightjs' |
||||
|
import 'highlight.js/styles/atom-one-dark.css' |
||||
|
|
||||
|
import App from './App' |
||||
|
import store from './store' |
||||
|
import router from './router/routers' |
||||
|
|
||||
|
import './assets/icons' // icon
|
||||
|
import './router/index' // permission control
|
||||
|
|
||||
|
import { VueJsonp } from 'vue-jsonp' |
||||
|
// Vue.use(VueJsonp)
|
||||
|
|
||||
|
Vue.use(VueHighlightJS) |
||||
|
Vue.use(mavonEditor) |
||||
|
Vue.use(permission) |
||||
|
Vue.use(dict) |
||||
|
Vue.use(Element, { |
||||
|
size: Cookies.get('size') || 'small' // set element-ui default size
|
||||
|
}) |
||||
|
|
||||
|
Vue.use(VueJsonp) |
||||
|
|
||||
|
Vue.config.productionTip = false |
||||
|
|
||||
|
new Vue({ |
||||
|
el: '#app', |
||||
|
router, |
||||
|
store, |
||||
|
render: h => h(App) |
||||
|
}) |
@ -0,0 +1,50 @@ |
|||||
|
module.exports = { |
||||
|
/** |
||||
|
* @description 网站标题 |
||||
|
*/ |
||||
|
title: 'VIVIB-V1.0', |
||||
|
/** |
||||
|
* @description 是否显示 tagsView |
||||
|
*/ |
||||
|
tagsView: true, |
||||
|
/** |
||||
|
* @description 固定头部 |
||||
|
*/ |
||||
|
fixedHeader: true, |
||||
|
/** |
||||
|
* @description 记住密码状态下的token在Cookie中存储的天数,默认1天 |
||||
|
*/ |
||||
|
tokenCookieExpires: 1, |
||||
|
/** |
||||
|
* @description 记住密码状态下的密码在Cookie中存储的天数,默认1天s |
||||
|
*/ |
||||
|
passCookieExpires: 1, |
||||
|
/** |
||||
|
* @description 是否只保持一个子菜单的展开 |
||||
|
*/ |
||||
|
uniqueOpened: true, |
||||
|
/** |
||||
|
* @description token key |
||||
|
*/ |
||||
|
TokenKey: 'YSHOP-TOEKN', |
||||
|
/** |
||||
|
* @description 请求超时时间,毫秒(默认2分钟) |
||||
|
*/ |
||||
|
timeout: 1200000, |
||||
|
/** |
||||
|
* @description 是否显示logo |
||||
|
*/ |
||||
|
sidebarLogo: true, |
||||
|
/** |
||||
|
* 是否显示设置的底部信息 |
||||
|
*/ |
||||
|
showFooter: true, |
||||
|
/** |
||||
|
* 底部文字,支持html语法 |
||||
|
*/ |
||||
|
footerTxt: '© 2019-2020 YSHOP <a href="http://www.apache.org/licenses/LICENSE-2.0" target="_blank">All Rights Reserved</a>', |
||||
|
/** |
||||
|
* 备案号 |
||||
|
*/ |
||||
|
caseNumber: '豫ICP备17049587-2号' |
||||
|
} |
@ -0,0 +1,124 @@ |
|||||
|
<template> |
||||
|
<div class="dashboard-container"> |
||||
|
<div class="dashboard-editor-container"> |
||||
|
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;"> |
||||
|
<heat-map /> |
||||
|
</el-row> |
||||
|
<el-row :gutter="32"> |
||||
|
<el-col :xs="24" :sm="24" :lg="8"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<radar-chart /> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
<el-col :xs="24" :sm="24" :lg="8"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<sunburst /> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
<el-col :xs="24" :sm="24" :lg="8"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<gauge /> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
<el-row :gutter="12"> |
||||
|
<el-col :span="12"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<rich /> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
<el-col :span="12"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<theme-river /> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
<el-row :gutter="32"> |
||||
|
<el-col :xs="24" :sm="24" :lg="8"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<graph /> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
<el-col :xs="24" :sm="24" :lg="8"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<sankey /> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
<el-col :xs="24" :sm="24" :lg="8"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<line3-d /> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
<el-row :gutter="12"> |
||||
|
<el-col :span="12"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<scatter /> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
<el-col :span="12"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<point /> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<word-cloud /> |
||||
|
</div> |
||||
|
</el-row> |
||||
|
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;"> |
||||
|
<div class="chart-wrapper"> |
||||
|
<category /> |
||||
|
</div> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import RadarChart from '@/components/Echarts/RadarChart' |
||||
|
import HeatMap from '@/components/Echarts/HeatMap' |
||||
|
import Gauge from '@/components/Echarts/Gauge' |
||||
|
import Rich from '@/components/Echarts/Rich' |
||||
|
import ThemeRiver from '@/components/Echarts/ThemeRiver' |
||||
|
import Sunburst from '@/components/Echarts/Sunburst' |
||||
|
import Graph from '@/components/Echarts/Graph' |
||||
|
import Sankey from '@/components/Echarts/Sankey' |
||||
|
import Scatter from '@/components/Echarts/Scatter' |
||||
|
import Line3D from '@/components/Echarts/Line3D' |
||||
|
import Category from '@/components/Echarts/Category' |
||||
|
import Point from '@/components/Echarts/Point' |
||||
|
import WordCloud from '@/components/Echarts/WordCloud' |
||||
|
|
||||
|
export default { |
||||
|
name: 'Echarts', |
||||
|
components: { |
||||
|
Point, |
||||
|
Category, |
||||
|
Graph, |
||||
|
HeatMap, |
||||
|
RadarChart, |
||||
|
Sunburst, |
||||
|
Gauge, |
||||
|
Rich, |
||||
|
ThemeRiver, |
||||
|
Sankey, |
||||
|
Line3D, |
||||
|
Scatter, |
||||
|
WordCloud |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
.dashboard-editor-container { |
||||
|
padding: 18px 22px 22px 22px; |
||||
|
background-color: rgb(240, 242, 245); |
||||
|
.chart-wrapper { |
||||
|
background: #fff; |
||||
|
padding: 16px 16px 0; |
||||
|
margin-bottom: 32px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,73 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<div ref="editor" style="text-align:left;margin: 5px;width: 700px" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapGetters } from 'vuex' |
||||
|
import E from 'wangeditor' |
||||
|
import { getToken } from '@/utils/auth' |
||||
|
export default { |
||||
|
name: 'Editor', |
||||
|
model: { |
||||
|
prop: 'value', |
||||
|
event: 'change' |
||||
|
}, |
||||
|
props: { |
||||
|
value: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
headers: { |
||||
|
'Authorization': getToken() |
||||
|
}, |
||||
|
info: null, |
||||
|
editor: null |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
'uploadApi' |
||||
|
]) |
||||
|
}, |
||||
|
watch: { |
||||
|
value: function(val) { |
||||
|
this.editor.txt.html(val) |
||||
|
// this.editor.txt.html(val) |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.editor = new E(this.$refs.editor) |
||||
|
this.editor.customConfig = this.editor.customConfig ? this.editor.customConfig : this.editor.config |
||||
|
this.editor.customConfig.uploadImgShowBase64 = true // 使用 base64 保存图片 |
||||
|
// 不可修改 |
||||
|
this.editor.customConfig.uploadImgHeaders = this.headers |
||||
|
// 自定义文件名,不可修改,修改后会上传失败 |
||||
|
this.editor.customConfig.uploadFileName = 'file' |
||||
|
this.editor.customConfig.uploadImgServer = this.uploadApi // 上传图片到服务器 |
||||
|
this.editor.customConfig.uploadImgHooks = { |
||||
|
customInsert: function(insertImg, result, editor) { |
||||
|
var url = result.link |
||||
|
insertImg(url) |
||||
|
} |
||||
|
} |
||||
|
this.editor.customConfig.onchange = (html) => { |
||||
|
this.info = html |
||||
|
this.$emit('change', this.info) |
||||
|
this.$emit('input', this.info) |
||||
|
} |
||||
|
this.editor.create() |
||||
|
this.editor.txt.html(this.value) |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.editor-content{ |
||||
|
padding-left: 5px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,44 @@ |
|||||
|
<template> |
||||
|
<div class="app-container"> |
||||
|
<p class="warn-content"> |
||||
|
Markdown 基于 |
||||
|
<el-link type="primary" href="https://github.com/hinesboy/mavonEditor" target="_blank">MavonEditor</el-link> |
||||
|
,图片上传使用 <el-link type="primary" href="https://sm.ms/" target="_blank">SM.MS</el-link> |
||||
|
</p> |
||||
|
<mavon-editor ref="md" :style="'height:' + height" @imgAdd="imgAdd" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { upload } from '@/utils/upload' |
||||
|
import { mapGetters } from 'vuex' |
||||
|
export default { |
||||
|
name: 'Markdown', |
||||
|
data() { |
||||
|
return { |
||||
|
height: document.documentElement.clientHeight - 200 + 'px' |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
'imagesUploadApi' |
||||
|
]) |
||||
|
}, |
||||
|
mounted() { |
||||
|
const that = this |
||||
|
window.onresize = function temp() { |
||||
|
that.height = document.documentElement.clientHeight - 200 + 'px' |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
imgAdd(pos, $file) { |
||||
|
upload(this.imagesUploadApi, $file).then(data => { |
||||
|
this.$refs.md.$img2Url(pos, data.data.url) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
</style> |
@ -0,0 +1,207 @@ |
|||||
|
<template> |
||||
|
<div class="app-container"> |
||||
|
<p class="warn-content"> |
||||
|
Yaml编辑器 基于 |
||||
|
<a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirror</a>, |
||||
|
主题预览地址 <a href="https://codemirror.net/demo/theme.html#idea" target="_blank">Theme</a> |
||||
|
</p> |
||||
|
<Yaml :value="value" :height="height" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import Yaml from '@/components/YamlEdit/index' |
||||
|
export default { |
||||
|
name: 'YamlEdit', |
||||
|
components: { Yaml }, |
||||
|
data() { |
||||
|
return { |
||||
|
height: document.documentElement.clientHeight - 210 + 'px', |
||||
|
value: '# 展示数据,如需更换主题,请在src/components/YamlEdit 目录中搜索原主题名称进行替换\n' + |
||||
|
'\n' + |
||||
|
'# ===================================================================\n' + |
||||
|
'# Spring Boot configuration.\n' + |
||||
|
'#\n' + |
||||
|
'# This configuration will be overridden by the Spring profile you use,\n' + |
||||
|
'# for example application-dev.yml if you use the "dev" profile.\n' + |
||||
|
'#\n' + |
||||
|
'# More information on profiles: https://www.jhipster.tech/profiles/\n' + |
||||
|
'# More information on configuration properties: https://www.jhipster.tech/common-application-properties/\n' + |
||||
|
'# ===================================================================\n' + |
||||
|
'\n' + |
||||
|
'# ===================================================================\n' + |
||||
|
'# Standard Spring Boot properties.\n' + |
||||
|
'# Full reference is available at:\n' + |
||||
|
'# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html\n' + |
||||
|
'# ===================================================================\n' + |
||||
|
'\n' + |
||||
|
'eureka:\n' + |
||||
|
' client:\n' + |
||||
|
' enabled: true\n' + |
||||
|
' healthcheck:\n' + |
||||
|
' enabled: true\n' + |
||||
|
' fetch-registry: true\n' + |
||||
|
' register-with-eureka: true\n' + |
||||
|
' instance-info-replication-interval-seconds: 10\n' + |
||||
|
' registry-fetch-interval-seconds: 10\n' + |
||||
|
' instance:\n' + |
||||
|
' appname: product\n' + |
||||
|
' instanceId: product:${spring.application.instance-id:${random.value}}\n' + |
||||
|
' #instanceId: 127.0.0.1:9080\n' + |
||||
|
' lease-renewal-interval-in-seconds: 5\n' + |
||||
|
' lease-expiration-duration-in-seconds: 10\n' + |
||||
|
' status-page-url-path: ${management.endpoints.web.base-path}/info\n' + |
||||
|
' health-check-url-path: ${management.endpoints.web.base-path}/health\n' + |
||||
|
' metadata-map:\n' + |
||||
|
' zone: primary # This is needed for the load balancer\n' + |
||||
|
' profile: ${spring.profiles.active}\n' + |
||||
|
' version: ${info.project.version:}\n' + |
||||
|
' git-version: ${git.commit.id.describe:}\n' + |
||||
|
' git-commit: ${git.commit.id.abbrev:}\n' + |
||||
|
' git-branch: ${git.branch:}\n' + |
||||
|
'ribbon:\n' + |
||||
|
' ReadTimeout: 120000\n' + |
||||
|
' ConnectTimeout: 300000\n' + |
||||
|
' eureka:\n' + |
||||
|
' enabled: true\n' + |
||||
|
'zuul:\n' + |
||||
|
' host:\n' + |
||||
|
' connect-timeout-millis: 5000\n' + |
||||
|
' max-per-route-connections: 10000\n' + |
||||
|
' max-total-connections: 5000\n' + |
||||
|
' socket-timeout-millis: 60000\n' + |
||||
|
' semaphore:\n' + |
||||
|
' max-semaphores: 500\n' + |
||||
|
'\n' + |
||||
|
'feign:\n' + |
||||
|
' hystrix:\n' + |
||||
|
' enabled: true\n' + |
||||
|
' client:\n' + |
||||
|
' config:\n' + |
||||
|
' default:\n' + |
||||
|
' connectTimeout: 500000\n' + |
||||
|
' readTimeout: 500000\n' + |
||||
|
'\n' + |
||||
|
'# See https://github.com/Netflix/Hystrix/wiki/Configuration\n' + |
||||
|
'hystrix:\n' + |
||||
|
' command:\n' + |
||||
|
' default:\n' + |
||||
|
' circuitBreaker:\n' + |
||||
|
' sleepWindowInMilliseconds: 100000\n' + |
||||
|
' forceClosed: true\n' + |
||||
|
' execution:\n' + |
||||
|
' isolation:\n' + |
||||
|
'# strategy: SEMAPHORE\n' + |
||||
|
'# See https://github.com/spring-cloud/spring-cloud-netflix/issues/1330\n' + |
||||
|
' thread:\n' + |
||||
|
' timeoutInMilliseconds: 60000\n' + |
||||
|
' shareSecurityContext: true\n' + |
||||
|
'\n' + |
||||
|
'management:\n' + |
||||
|
' endpoints:\n' + |
||||
|
' web:\n' + |
||||
|
' base-path: /management\n' + |
||||
|
' exposure:\n' + |
||||
|
' include: ["configprops", "env", "health", "info", "threaddump"]\n' + |
||||
|
' endpoint:\n' + |
||||
|
' health:\n' + |
||||
|
' show-details: when_authorized\n' + |
||||
|
' info:\n' + |
||||
|
' git:\n' + |
||||
|
' mode: full\n' + |
||||
|
' health:\n' + |
||||
|
' mail:\n' + |
||||
|
' enabled: false # When using the MailService, configure an SMTP server and set this to true\n' + |
||||
|
' metrics:\n' + |
||||
|
' enabled: false # http://micrometer.io/ is disabled by default, as we use http://metrics.dropwizard.io/ instead\n' + |
||||
|
'\n' + |
||||
|
'spring:\n' + |
||||
|
' application:\n' + |
||||
|
' name: product\n' + |
||||
|
' jpa:\n' + |
||||
|
' open-in-view: false\n' + |
||||
|
' hibernate:\n' + |
||||
|
' ddl-auto: update\n' + |
||||
|
' naming:\n' + |
||||
|
' physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy\n' + |
||||
|
' implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy\n' + |
||||
|
' messages:\n' + |
||||
|
' basename: i18n/messages\n' + |
||||
|
' mvc:\n' + |
||||
|
' favicon:\n' + |
||||
|
' enabled: false\n' + |
||||
|
' thymeleaf:\n' + |
||||
|
' mode: HTML\n' + |
||||
|
'security:\n' + |
||||
|
' oauth2:\n' + |
||||
|
' resource:\n' + |
||||
|
' filter-order: 3\n' + |
||||
|
'\n' + |
||||
|
'server:\n' + |
||||
|
' servlet:\n' + |
||||
|
' session:\n' + |
||||
|
' cookie:\n' + |
||||
|
' http-only: true\n' + |
||||
|
'\n' + |
||||
|
'# Properties to be exposed on the /info management endpoint\n' + |
||||
|
'info:\n' + |
||||
|
' # Comma separated list of profiles that will trigger the ribbon to show\n' + |
||||
|
' display-ribbon-on-profiles: "dev"\n' + |
||||
|
'\n' + |
||||
|
'# ===================================================================\n' + |
||||
|
'# JHipster specific properties\n' + |
||||
|
'#\n' + |
||||
|
'# Full reference is available at: https://www.jhipster.tech/common-application-properties/\n' + |
||||
|
'# ===================================================================\n' + |
||||
|
'\n' + |
||||
|
'jhipster:\n' + |
||||
|
' async:\n' + |
||||
|
' core-pool-size: 2\n' + |
||||
|
' max-pool-size: 50\n' + |
||||
|
' queue-capacity: 10000\n' + |
||||
|
' # By default CORS is disabled. Uncomment to enable.\n' + |
||||
|
' #cors:\n' + |
||||
|
' #allowed-origins: "*"\n' + |
||||
|
' #allowed-methods: "*"\n' + |
||||
|
' #allowed-headers: "*"\n' + |
||||
|
' #exposed-headers: "Authorization,Link,X-Total-Count"\n' + |
||||
|
' #allow-credentials: true\n' + |
||||
|
' #max-age: 1800\n' + |
||||
|
' mail:\n' + |
||||
|
' from: product@localhost\n' + |
||||
|
' swagger:\n' + |
||||
|
' default-include-pattern: /api/.*\n' + |
||||
|
' title: product API\n' + |
||||
|
' description: product API documentation\n' + |
||||
|
' version: 0.0.1\n' + |
||||
|
' terms-of-service-url:\n' + |
||||
|
' contact-name:\n' + |
||||
|
' contact-url:\n' + |
||||
|
' contact-email:\n' + |
||||
|
' license:\n' + |
||||
|
' license-url:\n' + |
||||
|
'\n' + |
||||
|
'# ===================================================================\n' + |
||||
|
'# Application specific properties\n' + |
||||
|
'# Add your own application properties here, see the ApplicationProperties class\n' + |
||||
|
'# to have type-safe configuration, like in the JHipsterProperties above\n' + |
||||
|
'#\n' + |
||||
|
'# More documentation is available at:\n' + |
||||
|
'# https://www.jhipster.tech/common-application-properties/\n' + |
||||
|
'# ===================================================================\n' + |
||||
|
'\n' + |
||||
|
'# application:\n' |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
const that = this |
||||
|
window.onresize = function temp() { |
||||
|
that.height = document.documentElement.clientHeight - 210 + 'px' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
</style> |
@ -0,0 +1,74 @@ |
|||||
|
const elementIcons = [ |
||||
|
'info', |
||||
|
'error', |
||||
|
'success', |
||||
|
'warning', |
||||
|
'question', |
||||
|
'back', |
||||
|
'arrow-left', |
||||
|
'arrow-down', |
||||
|
'arrow-right', |
||||
|
'arrow-up', |
||||
|
'caret-left', |
||||
|
'caret-bottom', |
||||
|
'caret-top', |
||||
|
'caret-right', |
||||
|
'd-arrow-left', |
||||
|
'd-arrow-right', |
||||
|
'minus', |
||||
|
'plus', |
||||
|
'remove', |
||||
|
'circle-plus', |
||||
|
'remove-outline', |
||||
|
'circle-plus-outline', |
||||
|
'close', |
||||
|
'check', |
||||
|
'circle-close', |
||||
|
'circle-check', |
||||
|
'circle-close-outline', |
||||
|
'circle-check-outline', |
||||
|
'zoom-out', |
||||
|
'zoom-in', |
||||
|
'd-caret', |
||||
|
'sort', |
||||
|
'sort-down', |
||||
|
'sort-up', |
||||
|
'tickets', |
||||
|
'document', |
||||
|
'goods', |
||||
|
'sold-out', |
||||
|
'news', |
||||
|
'message', |
||||
|
'date', |
||||
|
'printer', |
||||
|
'time', |
||||
|
'bell', |
||||
|
'mobile-phone', |
||||
|
'service', |
||||
|
'view', |
||||
|
'menu', |
||||
|
'more', |
||||
|
'more-outline', |
||||
|
'star-on', |
||||
|
'star-off', |
||||
|
'location', |
||||
|
'location-outline', |
||||
|
'phone', |
||||
|
'phone-outline', |
||||
|
'picture', |
||||
|
'picture-outline', |
||||
|
'delete', |
||||
|
'search', |
||||
|
'edit', |
||||
|
'edit-outline', |
||||
|
'rank', |
||||
|
'refresh', |
||||
|
'share', |
||||
|
'setting', |
||||
|
'upload', |
||||
|
'upload2', |
||||
|
'download', |
||||
|
'loading' |
||||
|
] |
||||
|
|
||||
|
export default elementIcons |
@ -0,0 +1,91 @@ |
|||||
|
<template> |
||||
|
<div class="icons-container"> |
||||
|
<aside> |
||||
|
<a href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/icon.html" target="_blank">Add and use |
||||
|
</a> |
||||
|
</aside> |
||||
|
<el-tabs type="border-card"> |
||||
|
<el-tab-pane label="Icons"> |
||||
|
<div v-for="item of svgIcons" :key="item" @click="handleClipboard(generateIconCode(item),$event)"> |
||||
|
<el-tooltip placement="top"> |
||||
|
<div slot="content"> |
||||
|
{{ generateIconCode(item) }} |
||||
|
</div> |
||||
|
<div class="icon-item"> |
||||
|
<svg-icon :icon-class="item" class-name="disabled" /> |
||||
|
<span>{{ item }}</span> |
||||
|
</div> |
||||
|
</el-tooltip> |
||||
|
</div> |
||||
|
</el-tab-pane> |
||||
|
<el-tab-pane label="Element-UI Icons"> |
||||
|
<div v-for="item of elementIcons" :key="item" @click="handleClipboard(generateElementIconCode(item),$event)"> |
||||
|
<el-tooltip placement="top"> |
||||
|
<div slot="content"> |
||||
|
{{ generateElementIconCode(item) }} |
||||
|
</div> |
||||
|
<div class="icon-item"> |
||||
|
<i :class="'el-icon-' + item" /> |
||||
|
<span>{{ item }}</span> |
||||
|
</div> |
||||
|
</el-tooltip> |
||||
|
</div> |
||||
|
</el-tab-pane> |
||||
|
</el-tabs> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import clipboard from '@/utils/clipboard' |
||||
|
import svgIcons from './svg-icons' |
||||
|
import elementIcons from './element-icons' |
||||
|
|
||||
|
export default { |
||||
|
name: 'Icons', |
||||
|
data() { |
||||
|
return { |
||||
|
svgIcons, |
||||
|
elementIcons |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
generateIconCode(symbol) { |
||||
|
return `<svg-icon icon-class="${symbol}" />` |
||||
|
}, |
||||
|
generateElementIconCode(symbol) { |
||||
|
return `<i class="el-icon-${symbol}" />` |
||||
|
}, |
||||
|
handleClipboard(text, event) { |
||||
|
clipboard(text, event) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.icons-container { |
||||
|
margin: 10px 20px 0; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
.icon-item { |
||||
|
margin: 20px; |
||||
|
height: 85px; |
||||
|
text-align: center; |
||||
|
width: 100px; |
||||
|
float: left; |
||||
|
font-size: 30px; |
||||
|
color: #24292e; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
span { |
||||
|
display: block; |
||||
|
font-size: 16px; |
||||
|
margin-top: 10px; |
||||
|
} |
||||
|
|
||||
|
.disabled { |
||||
|
pointer-events: none; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,10 @@ |
|||||
|
const req = require.context('../../../assets/icons/svg', false, /\.svg$/) |
||||
|
const requireAll = requireContext => requireContext.keys() |
||||
|
|
||||
|
const re = /\.\/(.*)\.svg/ |
||||
|
|
||||
|
const svgIcons = requireAll(req).map(i => { |
||||
|
return i.match(re)[1] |
||||
|
}) |
||||
|
|
||||
|
export default svgIcons |
@ -0,0 +1,108 @@ |
|||||
|
<template> |
||||
|
<div :class="className" :style="{height:height,width:width}" /> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import echarts from 'echarts' |
||||
|
require('echarts/theme/macarons') // echarts theme |
||||
|
import { debounce } from '@/utils' |
||||
|
|
||||
|
|
||||
|
const animationDuration = 6000 |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
className: { |
||||
|
type: String, |
||||
|
default: 'chart' |
||||
|
}, |
||||
|
width: { |
||||
|
type: String, |
||||
|
default: '100%' |
||||
|
}, |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: '300px' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
chart: null, |
||||
|
day: [], num: [] |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.initChart() |
||||
|
this.__resizeHandler = debounce(() => { |
||||
|
if (this.chart) { |
||||
|
this.chart.resize() |
||||
|
} |
||||
|
}, 100) |
||||
|
window.addEventListener('resize', this.__resizeHandler) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
if (!this.chart) { |
||||
|
return |
||||
|
} |
||||
|
window.removeEventListener('resize', this.__resizeHandler) |
||||
|
this.chart.dispose() |
||||
|
this.chart = null |
||||
|
}, |
||||
|
methods: { |
||||
|
initChart() { |
||||
|
this.chart = echarts.init(this.$el, 'macarons') |
||||
|
var _infostr = '{"chart":[{"num":3.02,"time":"02-01"},{"num":13.0,"time":"02-02"},{"num":12.0,"time":"02-03"},{"num":506.0,"time":"02-04"},{"num":2.01,"time":"02-06"},{"num":20.01,"time":"02-07"},{"num":5040.0,"time":"02-08"},{"num":11.0,"time":"02-10"},{"num":451.0,"time":"02-14"},{"num":0.0,"time":"02-15"},{"num":7.0,"time":"02-16"}],"chartT":[{"num":3.0,"time":"02-01"},{"num":2.0,"time":"02-02"},{"num":2.0,"time":"02-03"},{"num":1.0,"time":"02-04"},{"num":4.0,"time":"02-06"},{"num":2.0,"time":"02-07"},{"num":5.0,"time":"02-08"},{"num":2.0,"time":"02-10"},{"num":3.0,"time":"02-14"},{"num":1.0,"time":"02-15"},{"num":1.0,"time":"02-16"}]}' |
||||
|
var _info1 = eval('(' + _infostr + ')'); |
||||
|
var _info = _info1.chart |
||||
|
var day = [] |
||||
|
var num = [] |
||||
|
_info.forEach(function(item) { |
||||
|
day.push(item.time) |
||||
|
num.push(item.num) |
||||
|
}) |
||||
|
|
||||
|
console.log(day, num) |
||||
|
|
||||
|
this.chart.setOption({ |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
axisPointer: { // 坐标轴指示器,坐标轴触发有效 |
||||
|
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' |
||||
|
} |
||||
|
}, |
||||
|
grid: { |
||||
|
top: 10, |
||||
|
left: '2%', |
||||
|
right: '2%', |
||||
|
bottom: '3%', |
||||
|
containLabel: true |
||||
|
}, |
||||
|
xAxis: [{ |
||||
|
type: 'category', |
||||
|
data: day, |
||||
|
axisTick: { |
||||
|
alignWithLabel: true |
||||
|
} |
||||
|
}], |
||||
|
yAxis: [{ |
||||
|
type: 'value', |
||||
|
axisTick: { |
||||
|
show: false |
||||
|
} |
||||
|
}], |
||||
|
series: [{ |
||||
|
name: 'pageA', |
||||
|
type: 'line', |
||||
|
stack: 'vistors', |
||||
|
barWidth: '60%', |
||||
|
data: num, |
||||
|
animationDuration |
||||
|
}] |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
console.log('day:' + this.day) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,111 @@ |
|||||
|
<template> |
||||
|
<div :class="className" :style="{height:height,width:width}" /> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import echarts from 'echarts' |
||||
|
require('echarts/theme/macarons') // echarts theme |
||||
|
import { debounce } from '@/utils' |
||||
|
|
||||
|
|
||||
|
const animationDuration = 6000 |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
className: { |
||||
|
type: String, |
||||
|
default: 'chart' |
||||
|
}, |
||||
|
width: { |
||||
|
type: String, |
||||
|
default: '100%' |
||||
|
}, |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: '300px' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
chart: null, |
||||
|
day: [], num: [] |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.initChart() |
||||
|
this.__resizeHandler = debounce(() => { |
||||
|
if (this.chart) { |
||||
|
this.chart.resize() |
||||
|
} |
||||
|
}, 100) |
||||
|
window.addEventListener('resize', this.__resizeHandler) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
if (!this.chart) { |
||||
|
return |
||||
|
} |
||||
|
window.removeEventListener('resize', this.__resizeHandler) |
||||
|
this.chart.dispose() |
||||
|
this.chart = null |
||||
|
}, |
||||
|
methods: { |
||||
|
initChart() { |
||||
|
this.chart = echarts.init(this.$el, 'macarons') |
||||
|
|
||||
|
//chart().then(res => { |
||||
|
//var _info = res.chartT |
||||
|
var _infostr = '{"chart":[{"num":3.02,"time":"02-01"},{"num":13.0,"time":"02-02"},{"num":12.0,"time":"02-03"},{"num":506.0,"time":"02-04"},{"num":2.01,"time":"02-06"},{"num":20.01,"time":"02-07"},{"num":5040.0,"time":"02-08"},{"num":11.0,"time":"02-10"},{"num":451.0,"time":"02-14"},{"num":0.0,"time":"02-15"},{"num":7.0,"time":"02-16"}],"chartT":[{"num":3.0,"time":"02-01"},{"num":2.0,"time":"02-02"},{"num":2.0,"time":"02-03"},{"num":1.0,"time":"02-04"},{"num":4.0,"time":"02-06"},{"num":2.0,"time":"02-07"},{"num":5.0,"time":"02-08"},{"num":2.0,"time":"02-10"},{"num":3.0,"time":"02-14"},{"num":1.0,"time":"02-15"},{"num":1.0,"time":"02-16"}]}' |
||||
|
var _info1 = eval('(' + _infostr + ')'); |
||||
|
var _info = _info1.chartT |
||||
|
var day = [] |
||||
|
var num = [] |
||||
|
_info.forEach(function(item) { |
||||
|
day.push(item.time) |
||||
|
num.push(item.num) |
||||
|
}) |
||||
|
|
||||
|
console.log(day, num) |
||||
|
|
||||
|
this.chart.setOption({ |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
axisPointer: { // 坐标轴指示器,坐标轴触发有效 |
||||
|
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' |
||||
|
} |
||||
|
}, |
||||
|
grid: { |
||||
|
top: 10, |
||||
|
left: '2%', |
||||
|
right: '2%', |
||||
|
bottom: '3%', |
||||
|
containLabel: true |
||||
|
}, |
||||
|
xAxis: [{ |
||||
|
type: 'category', |
||||
|
data: day, |
||||
|
axisTick: { |
||||
|
alignWithLabel: true |
||||
|
} |
||||
|
}], |
||||
|
yAxis: [{ |
||||
|
type: 'value', |
||||
|
axisTick: { |
||||
|
show: false |
||||
|
} |
||||
|
}], |
||||
|
series: [{ |
||||
|
name: 'pageA', |
||||
|
type: 'line', |
||||
|
stack: 'vistors', |
||||
|
barWidth: '60%', |
||||
|
data: num, |
||||
|
animationDuration |
||||
|
}] |
||||
|
}) |
||||
|
// }) |
||||
|
|
||||
|
console.log('day:' + this.day) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,155 @@ |
|||||
|
<template> |
||||
|
<div :class="className" :style="{height:height,width:width}" /> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import echarts from 'echarts' |
||||
|
require('echarts/theme/macarons') // echarts theme |
||||
|
import { debounce } from '@/utils' |
||||
|
|
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
className: { |
||||
|
type: String, |
||||
|
default: 'chart' |
||||
|
}, |
||||
|
width: { |
||||
|
type: String, |
||||
|
default: '100%' |
||||
|
}, |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: '350px' |
||||
|
}, |
||||
|
autoResize: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
chart: null, |
||||
|
sidebarElm: null, |
||||
|
chartData: { |
||||
|
visitsData: [], |
||||
|
ipData: [] |
||||
|
}, |
||||
|
weekDays: [] |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
getChartData().then(res => { |
||||
|
this.chartData.visitsData = res.visitsData |
||||
|
this.chartData.ipData = res.ipData |
||||
|
this.weekDays = res.weekDays |
||||
|
this.initChart() |
||||
|
}) |
||||
|
if (this.autoResize) { |
||||
|
this.__resizeHandler = debounce(() => { |
||||
|
if (this.chart) { |
||||
|
this.chart.resize() |
||||
|
} |
||||
|
}, 100) |
||||
|
window.addEventListener('resize', this.__resizeHandler) |
||||
|
} |
||||
|
|
||||
|
// 监听侧边栏的变化 |
||||
|
this.sidebarElm = document.getElementsByClassName('sidebar-container')[0] |
||||
|
this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
if (!this.chart) { |
||||
|
return |
||||
|
} |
||||
|
if (this.autoResize) { |
||||
|
window.removeEventListener('resize', this.__resizeHandler) |
||||
|
} |
||||
|
|
||||
|
this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler) |
||||
|
|
||||
|
this.chart.dispose() |
||||
|
this.chart = null |
||||
|
}, |
||||
|
methods: { |
||||
|
sidebarResizeHandler(e) { |
||||
|
if (e.propertyName === 'width') { |
||||
|
this.__resizeHandler() |
||||
|
} |
||||
|
}, |
||||
|
setOptions({ visitsData, ipData } = {}) { |
||||
|
this.chart.setOption({ |
||||
|
xAxis: { |
||||
|
data: this.weekDays, |
||||
|
boundaryGap: false, |
||||
|
axisTick: { |
||||
|
show: false |
||||
|
} |
||||
|
}, |
||||
|
grid: { |
||||
|
left: 10, |
||||
|
right: 10, |
||||
|
bottom: 20, |
||||
|
top: 30, |
||||
|
containLabel: true |
||||
|
}, |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
axisPointer: { |
||||
|
type: 'cross' |
||||
|
}, |
||||
|
padding: [5, 10] |
||||
|
}, |
||||
|
yAxis: { |
||||
|
axisTick: { |
||||
|
show: false |
||||
|
} |
||||
|
}, |
||||
|
legend: { |
||||
|
data: ['pv', 'ip'] |
||||
|
}, |
||||
|
series: [{ |
||||
|
name: 'pv', itemStyle: { |
||||
|
normal: { |
||||
|
color: '#FF005A', |
||||
|
lineStyle: { |
||||
|
color: '#FF005A', |
||||
|
width: 2 |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
smooth: true, |
||||
|
type: 'line', |
||||
|
data: visitsData, |
||||
|
animationDuration: 2800, |
||||
|
animationEasing: 'cubicInOut' |
||||
|
}, |
||||
|
{ |
||||
|
name: 'ip', |
||||
|
smooth: true, |
||||
|
type: 'line', |
||||
|
itemStyle: { |
||||
|
normal: { |
||||
|
color: '#3888fa', |
||||
|
lineStyle: { |
||||
|
color: '#3888fa', |
||||
|
width: 2 |
||||
|
}, |
||||
|
areaStyle: { |
||||
|
color: '#f3f8ff' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
data: ipData, |
||||
|
animationDuration: 2800, |
||||
|
animationEasing: 'quadraticOut' |
||||
|
}] |
||||
|
}) |
||||
|
}, |
||||
|
initChart() { |
||||
|
this.chart = echarts.init(this.$el, 'macarons') |
||||
|
this.setOptions(this.chartData) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,121 @@ |
|||||
|
<template> |
||||
|
<div :class="className" ref="chart" :style="{ height: height, width: width }" /> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import echarts from "echarts"; |
||||
|
require("echarts/theme/macarons"); // echarts theme |
||||
|
import { debounce } from "@/utils"; |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
className: { |
||||
|
type: String, |
||||
|
default: "chart", |
||||
|
}, |
||||
|
width: { |
||||
|
type: String, |
||||
|
default: "100%", |
||||
|
}, |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: "300px", |
||||
|
}, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
chart: null, |
||||
|
column: [], |
||||
|
orderCountDatas: [], |
||||
|
}; |
||||
|
}, |
||||
|
mounted() { |
||||
|
var datastr = '{"column":["打印复印","广告印刷"],"orderCountDatas":[{"name":"打印复印","value":672},{"name":"广告印刷","value":1474}]}' |
||||
|
var obj = eval('(' + datastr + ')'); |
||||
|
this.column = obj.column |
||||
|
this.orderCountDatas = obj.orderCountDatas |
||||
|
this.__resizeHandler = debounce(() => { |
||||
|
if (this.chart) { |
||||
|
this.chart.resize(); |
||||
|
} |
||||
|
}, 100); |
||||
|
window.addEventListener("resize", this.__resizeHandler); |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
if (!this.chart) { |
||||
|
return; |
||||
|
} |
||||
|
window.removeEventListener("resize", this.__resizeHandler); |
||||
|
this.chart.dispose(); |
||||
|
this.chart = null; |
||||
|
}, |
||||
|
methods: { |
||||
|
initChart() { |
||||
|
this.chart = echarts.init(this.$refs.chart, "macarons"); |
||||
|
|
||||
|
this.chart.setOption({ |
||||
|
tooltip: { |
||||
|
trigger: "item", |
||||
|
formatter: "{a} <br/>{b} : {c} ({d}%)", |
||||
|
}, |
||||
|
legend: { |
||||
|
left: "center", |
||||
|
bottom: "10", |
||||
|
data: this.column, |
||||
|
}, |
||||
|
calculable: true, |
||||
|
series: [ |
||||
|
{ |
||||
|
name: "商品分类销售占总销售的比例", |
||||
|
type: "pie", |
||||
|
roseType: "radius", |
||||
|
radius: [15, 95], |
||||
|
center: ["50%", "38%"], |
||||
|
data: this.orderCountDatas, |
||||
|
animationEasing: "cubicInOut", |
||||
|
animationDuration: 2600, |
||||
|
}, |
||||
|
], |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.acea-row{ |
||||
|
/deep/.el-avatar--small { |
||||
|
width: 22px; |
||||
|
height: 22px; |
||||
|
line-height: 22px; |
||||
|
} |
||||
|
} |
||||
|
.checkTime{ |
||||
|
/deep/.el-radio__input{ |
||||
|
display: none; |
||||
|
} |
||||
|
} |
||||
|
.ivu-pl-8{ |
||||
|
margin-left: 8px; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
.divBox { |
||||
|
// padding: 0 20px !important; |
||||
|
} |
||||
|
.dashboard-console-visit { |
||||
|
/deep/.el-card__header{ |
||||
|
padding: 14px 20px !important; |
||||
|
} |
||||
|
ul { |
||||
|
li { |
||||
|
list-style-type: none; |
||||
|
margin-top: 12px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.ivu-mb{ |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
</style> |
||||
|
|
@ -0,0 +1,233 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<div class="divBox"> |
||||
|
<el-row :gutter="24" class="baseInfo"> |
||||
|
<el-col :xs="12" :sm="12" :lg="6" class="ivu-mb"> |
||||
|
<el-card :bordered="false" dis-hover :padding="12"> |
||||
|
<div slot="header" class="acea-row row-between-wrapper"> |
||||
|
<span>会员总数</span> |
||||
|
<el-tag type="success">全平台</el-tag> |
||||
|
</div> |
||||
|
<div class="content" v-if="count"> |
||||
|
<span class="content-number spBlock mb15"><count-to :start-val="0" :end-val="count.userCount" :duration="2600" class="card-panel-num" /></span> |
||||
|
<el-divider></el-divider> |
||||
|
<div class="acea-row row-between-wrapper"> |
||||
|
<span class="content-time">今日订单数</span> |
||||
|
<span>{{ count.todayCount }} 单</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :xs="12" :sm="12" :lg="6" class="ivu-mb"> |
||||
|
<el-card :bordered="false" dis-hover :padding="12"> |
||||
|
<div slot="header" class="acea-row row-between-wrapper"> |
||||
|
<span>订单总数</span> |
||||
|
<el-tag type="success">全平台</el-tag> |
||||
|
</div> |
||||
|
<div class="content" v-if="count"> |
||||
|
<span class="content-number spBlock mb15"><count-to :start-val="0" :end-val="count.orderCount" :duration="3000" class="card-panel-num" /></span> |
||||
|
<el-divider></el-divider> |
||||
|
<div class="acea-row row-between-wrapper"> |
||||
|
<span class="content-time">昨日订单数</span> |
||||
|
<span>{{ count.lastWeekCount }} 单</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :xs="12" :sm="12" :lg="6" class="ivu-mb"> |
||||
|
<el-card :bordered="false" dis-hover :padding="12"> |
||||
|
<div slot="header" class="acea-row row-between-wrapper"> |
||||
|
<span>总金额</span> |
||||
|
<el-tag type="success">全平台</el-tag> |
||||
|
</div> |
||||
|
<div class="content" v-if="count"> |
||||
|
<span class="content-number spBlock mb15"><count-to :start-val="0" :end-val="count.priceCount" :duration="3200" class="card-panel-num" /></span> |
||||
|
<el-divider></el-divider> |
||||
|
<div class="acea-row row-between-wrapper"> |
||||
|
<span class="content-time">近七天订单数</span> |
||||
|
<span>{{ count.lastWeekCount }} 单</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :xs="12" :sm="12" :lg="6" class="ivu-mb"> |
||||
|
<el-card :bordered="false" dis-hover :padding="12"> |
||||
|
<div slot="header" class="acea-row row-between-wrapper"> |
||||
|
<span>商品总数</span> |
||||
|
<el-tag type="success">全平台</el-tag> |
||||
|
</div> |
||||
|
<div class="content" v-if="count"> |
||||
|
<span class="content-number spBlock mb15"><count-to :start-val="0" :end-val="count.goodsCount" :duration="3600" class="card-panel-num" /></span> |
||||
|
<el-divider></el-divider> |
||||
|
<div class="acea-row row-between-wrapper"> |
||||
|
<span class="content-time">本月订单数</span> |
||||
|
<span>{{ count.monthCount }} 单</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script> |
||||
|
import CountTo from "vue-count-to"; |
||||
|
export default { |
||||
|
components: { |
||||
|
CountTo, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
count: { |
||||
|
todayPrice: 0, |
||||
|
todayCount: 0, |
||||
|
proPrice: 0, |
||||
|
proCount: 0, |
||||
|
monthPrice: 0, |
||||
|
monthCount: 0, |
||||
|
lastWeekCount: 0, |
||||
|
lastWeekPrice: 0, |
||||
|
userCount: 0, |
||||
|
orderCount: 0, |
||||
|
priceCount: 0, |
||||
|
goodsCount: 0, |
||||
|
}, |
||||
|
|
||||
|
}; |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.count.todayPrice = 99 |
||||
|
this.count.todayCount = 88 |
||||
|
this.count.proCount = 77 |
||||
|
this.count.proPrice = 66 |
||||
|
|
||||
|
this.count.monthPrice = 999 |
||||
|
this.count.monthCount = 99 |
||||
|
this.count.lastWeekCount = 7 |
||||
|
this.count.lastWeekPrice = 6 |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
.panel-group { |
||||
|
margin-top: 18px; |
||||
|
|
||||
|
.card-panel-col { |
||||
|
margin-bottom: 32px; |
||||
|
} |
||||
|
|
||||
|
.card-panel { |
||||
|
height: 108px; |
||||
|
cursor: pointer; |
||||
|
font-size: 12px; |
||||
|
position: relative; |
||||
|
overflow: hidden; |
||||
|
color: #666; |
||||
|
background: #fff; |
||||
|
box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05); |
||||
|
border-color: rgba(0, 0, 0, 0.05); |
||||
|
|
||||
|
.icon-people { |
||||
|
color: #40c9c6; |
||||
|
} |
||||
|
|
||||
|
.icon-message { |
||||
|
color: #36a3f7; |
||||
|
} |
||||
|
|
||||
|
.icon-money { |
||||
|
color: #f4516c; |
||||
|
} |
||||
|
|
||||
|
.icon-shopping { |
||||
|
color: #34bfa3; |
||||
|
} |
||||
|
|
||||
|
.card-panel-icon-wrapper { |
||||
|
float: left; |
||||
|
margin: 14px 0 0 14px; |
||||
|
padding: 16px; |
||||
|
transition: all 0.38s ease-out; |
||||
|
border-radius: 6px; |
||||
|
} |
||||
|
|
||||
|
.card-panel-icon { |
||||
|
float: left; |
||||
|
font-size: 48px; |
||||
|
} |
||||
|
|
||||
|
.card-panel-description { |
||||
|
float: right; |
||||
|
font-weight: bold; |
||||
|
margin: 26px; |
||||
|
margin-left: 0px; |
||||
|
|
||||
|
.card-panel-text { |
||||
|
line-height: 18px; |
||||
|
color: rgba(0, 0, 0, 0.45); |
||||
|
font-size: 16px; |
||||
|
margin-bottom: 12px; |
||||
|
} |
||||
|
|
||||
|
.card-panel-num { |
||||
|
font-size: 20px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@media (max-width: 550px) { |
||||
|
.card-panel-description { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.card-panel-icon-wrapper { |
||||
|
float: none !important; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
margin: 0 !important; |
||||
|
|
||||
|
.svg-icon { |
||||
|
display: block; |
||||
|
margin: 14px auto !important; |
||||
|
float: none !important; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.baseInfo { |
||||
|
/deep/ .el-card__header { |
||||
|
padding: 15px 20px !important; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.ivu-mb { |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
.up, |
||||
|
.el-icon-caret-top { |
||||
|
color: #f5222d; |
||||
|
font-size: 12px; |
||||
|
opacity: 1 !important; |
||||
|
} |
||||
|
|
||||
|
.down, |
||||
|
.el-icon-caret-bottom { |
||||
|
color: #39c15b; |
||||
|
font-size: 12px; |
||||
|
/*opacity: 100% !important;*/ |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
&-number { |
||||
|
font-size: 30px; |
||||
|
} |
||||
|
&-time { |
||||
|
font-size: 14px; |
||||
|
/*color: #8C8C8C;*/ |
||||
|
} |
||||
|
} |
||||
|
.spBlock { |
||||
|
display: block; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,188 @@ |
|||||
|
<template> |
||||
|
<div class="divBox"> |
||||
|
<el-row :gutter="24" class="dashboard-console-grid"> |
||||
|
<el-col v-bind="grid" class="ivu-mb"> |
||||
|
<el-card :bordered="false"> |
||||
|
<router-link :to="{ path: '/shop/cate' }"> |
||||
|
<i class="el-icon-goods" style="color: #69c0ff" /> |
||||
|
<p>商品管理</p> |
||||
|
</router-link> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col v-bind="grid" class="ivu-mb"> |
||||
|
<el-card :bordered="false"> |
||||
|
<router-link :to="{ path: '/member/member' }"> |
||||
|
<i class="el-icon-user" style="color: #95de64" /> |
||||
|
<p>会员管理</p> |
||||
|
</router-link> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col v-bind="grid" class="ivu-mb"> |
||||
|
<el-card :bordered="false"> |
||||
|
<router-link :to="{ path: '/order/order' }"> |
||||
|
<i class="el-icon-s-order" style="color: #ff9c6e" /> |
||||
|
<p>订单管理</p> |
||||
|
</router-link> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col v-bind="grid" class="ivu-mb"> |
||||
|
<el-card :bordered="false"> |
||||
|
<router-link :to="{ path: '/syscoupon/coupon' }"> |
||||
|
<i class="el-icon-s-ticket" style="color: #b37feb" /> |
||||
|
<p>电子券管理</p> |
||||
|
</router-link> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col v-bind="grid" class="ivu-mb"> |
||||
|
<el-card :bordered="false"> |
||||
|
<router-link :to="{ path: '/activity/combination' }"> |
||||
|
<i class="el-icon-postcard" style="color: #ffd666" /> |
||||
|
<p>营销管理</p> |
||||
|
</router-link> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col v-bind="grid" class="ivu-mb"> |
||||
|
<el-card :bordered="false"> |
||||
|
<router-link :to="{ path: '/promoter/promoterconfig' }"> |
||||
|
<i class="el-icon-notebook-1" style="color: #5cdbd3" /> |
||||
|
<p>分销管理</p> |
||||
|
</router-link> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col v-bind="grid" class="ivu-mb"> |
||||
|
<el-card :bordered="false"> |
||||
|
<router-link :to="{ path: '/price/extract' }"> |
||||
|
<i class="el-icon-s-finance" style="color: #ff85c0" /> |
||||
|
<p>财务管理</p> |
||||
|
</router-link> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col v-bind="grid" class="ivu-mb"> |
||||
|
<el-card :bordered="false"> |
||||
|
<router-link :to="{ path: '/store/storeinfo' }"> |
||||
|
<i class="el-icon-office-building" style="color: #ffc069" /> |
||||
|
<p>门店管理</p> |
||||
|
</router-link> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script> |
||||
|
import CountTo from 'vue-count-to' |
||||
|
export default { |
||||
|
components: { |
||||
|
CountTo |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
count: { todayPrice: 0, todayCount: 0, proPrice: 0, proCount: 0, |
||||
|
monthPrice: 0, monthCount: 0, lastWeekCount: 0, lastWeekPrice: 0 }, |
||||
|
grid: { |
||||
|
xl: 3, |
||||
|
lg: 3, |
||||
|
md: 6, |
||||
|
sm: 8, |
||||
|
xs: 8, |
||||
|
}, |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.count.todayPrice = 99 |
||||
|
this.count.todayCount = 88 |
||||
|
this.count.proCount = 77 |
||||
|
this.count.proPrice = 66 |
||||
|
|
||||
|
this.count.monthPrice = 999 |
||||
|
this.count.monthCount = 99 |
||||
|
this.count.lastWeekCount = 7 |
||||
|
this.count.lastWeekPrice = 6 |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
.panel-group { |
||||
|
margin-top: 18px; |
||||
|
.card-panel-col{ |
||||
|
margin-bottom: 32px; |
||||
|
} |
||||
|
.card-panel { |
||||
|
height: 108px; |
||||
|
font-size: 12px; |
||||
|
position: relative; |
||||
|
overflow: hidden; |
||||
|
cursor: pointer; |
||||
|
color: #666; |
||||
|
background: #fff; |
||||
|
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05); |
||||
|
border-color: rgba(0, 0, 0, .05); |
||||
|
|
||||
|
|
||||
|
|
||||
|
.icon-people { |
||||
|
color: #40c9c6; |
||||
|
} |
||||
|
.icon-message { |
||||
|
color: #36a3f7; |
||||
|
} |
||||
|
.icon-money { |
||||
|
color: #f4516c; |
||||
|
} |
||||
|
.icon-shopping { |
||||
|
color: #34bfa3 |
||||
|
} |
||||
|
.card-panel-icon-wrapper { |
||||
|
float: left; |
||||
|
margin: 14px 0 0 14px; |
||||
|
padding: 16px; |
||||
|
transition: all 0.38s ease-out; |
||||
|
border-radius: 6px; |
||||
|
} |
||||
|
.card-panel-icon { |
||||
|
float: left; |
||||
|
font-size: 48px; |
||||
|
} |
||||
|
.card-panel-description { |
||||
|
float: right; |
||||
|
font-weight: bold; |
||||
|
margin: 26px; |
||||
|
margin-left: 0px; |
||||
|
.card-panel-text { |
||||
|
line-height: 18px; |
||||
|
color: rgba(0, 0, 0, 0.45); |
||||
|
font-size: 16px; |
||||
|
margin-bottom: 12px; |
||||
|
} |
||||
|
.card-panel-num { |
||||
|
font-size: 20px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.ivu-mb { |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
.divBox { |
||||
|
// padding: 0 20px !important; |
||||
|
} |
||||
|
|
||||
|
.dashboard-console-grid { |
||||
|
text-align: center; |
||||
|
.ivu-card-body { |
||||
|
padding: 0; |
||||
|
} |
||||
|
i { |
||||
|
font-size: 32px; |
||||
|
} |
||||
|
a { |
||||
|
display: block; |
||||
|
color: inherit; |
||||
|
} |
||||
|
p { |
||||
|
margin-top: 8px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,91 @@ |
|||||
|
<template> |
||||
|
<div :class="className" :style="{height:height,width:width}" /> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import echarts from 'echarts' |
||||
|
require('echarts/theme/macarons') // echarts theme |
||||
|
import { debounce } from '@/utils' |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
className: { |
||||
|
type: String, |
||||
|
default: 'chart' |
||||
|
}, |
||||
|
width: { |
||||
|
type: String, |
||||
|
default: '100%' |
||||
|
}, |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: '300px' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
chart: null, |
||||
|
grid: { |
||||
|
xl: 3, |
||||
|
lg: 6, |
||||
|
md: 6, |
||||
|
sm: 8, |
||||
|
xs: 8, |
||||
|
}, |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.initChart() |
||||
|
this.__resizeHandler = debounce(() => { |
||||
|
if (this.chart) { |
||||
|
this.chart.resize() |
||||
|
} |
||||
|
}, 100) |
||||
|
window.addEventListener('resize', this.__resizeHandler) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
if (!this.chart) { |
||||
|
return |
||||
|
} |
||||
|
window.removeEventListener('resize', this.__resizeHandler) |
||||
|
this.chart.dispose() |
||||
|
this.chart = null |
||||
|
}, |
||||
|
methods: { |
||||
|
initChart() { |
||||
|
this.chart = echarts.init(this.$el, 'macarons') |
||||
|
|
||||
|
this.chart.setOption({ |
||||
|
tooltip: { |
||||
|
trigger: 'item', |
||||
|
formatter: '{a} <br/>{b} : {c} ({d}%)' |
||||
|
}, |
||||
|
legend: { |
||||
|
left: 'center', |
||||
|
bottom: '10', |
||||
|
data: ['Industries', 'Technology', 'Forex', 'Gold', 'Forecasts'] |
||||
|
}, |
||||
|
calculable: true, |
||||
|
series: [ |
||||
|
{ |
||||
|
name: 'WEEKLY WRITE ARTICLES', |
||||
|
type: 'pie', |
||||
|
roseType: 'radius', |
||||
|
radius: [15, 95], |
||||
|
center: ['50%', '38%'], |
||||
|
data: [ |
||||
|
{ value: 320, name: 'Industries' }, |
||||
|
{ value: 240, name: 'Technology' }, |
||||
|
{ value: 149, name: 'Forex' }, |
||||
|
{ value: 100, name: 'Gold' }, |
||||
|
{ value: 59, name: 'Forecasts' } |
||||
|
], |
||||
|
animationEasing: 'cubicInOut', |
||||
|
animationDuration: 2600 |
||||
|
} |
||||
|
] |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,120 @@ |
|||||
|
<template> |
||||
|
<div :class="className" :style="{height:height,width:width}" /> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import echarts from 'echarts' |
||||
|
require('echarts/theme/macarons') // echarts theme |
||||
|
import { debounce } from '@/utils' |
||||
|
|
||||
|
const animationDuration = 3000 |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
className: { |
||||
|
type: String, |
||||
|
default: 'chart' |
||||
|
}, |
||||
|
width: { |
||||
|
type: String, |
||||
|
default: '100%' |
||||
|
}, |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: '300px' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
chart: null |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.initChart() |
||||
|
this.__resizeHandler = debounce(() => { |
||||
|
if (this.chart) { |
||||
|
this.chart.resize() |
||||
|
} |
||||
|
}, 100) |
||||
|
window.addEventListener('resize', this.__resizeHandler) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
if (!this.chart) { |
||||
|
return |
||||
|
} |
||||
|
window.removeEventListener('resize', this.__resizeHandler) |
||||
|
this.chart.dispose() |
||||
|
this.chart = null |
||||
|
}, |
||||
|
methods: { |
||||
|
initChart() { |
||||
|
this.chart = echarts.init(this.$el, 'macarons') |
||||
|
|
||||
|
this.chart.setOption({ |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
axisPointer: { // 坐标轴指示器,坐标轴触发有效 |
||||
|
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' |
||||
|
} |
||||
|
}, |
||||
|
radar: { |
||||
|
radius: '66%', |
||||
|
center: ['50%', '42%'], |
||||
|
splitNumber: 8, |
||||
|
splitArea: { |
||||
|
areaStyle: { |
||||
|
color: 'rgba(127,95,132,.3)', |
||||
|
opacity: 1, |
||||
|
shadowBlur: 45, |
||||
|
shadowColor: 'rgba(0,0,0,.5)', |
||||
|
shadowOffsetX: 0, |
||||
|
shadowOffsetY: 15 |
||||
|
} |
||||
|
}, |
||||
|
indicator: [ |
||||
|
{ name: 'Sales', max: 10000 }, |
||||
|
{ name: 'Administration', max: 20000 }, |
||||
|
{ name: 'Information Techology', max: 20000 }, |
||||
|
{ name: 'Customer Support', max: 20000 }, |
||||
|
{ name: 'Development', max: 20000 }, |
||||
|
{ name: 'Marketing', max: 20000 } |
||||
|
] |
||||
|
}, |
||||
|
legend: { |
||||
|
left: 'center', |
||||
|
bottom: '10', |
||||
|
data: ['Allocated Budget', 'Expected Spending', 'Actual Spending'] |
||||
|
}, |
||||
|
series: [{ |
||||
|
type: 'radar', |
||||
|
symbolSize: 0, |
||||
|
areaStyle: { |
||||
|
normal: { |
||||
|
shadowBlur: 13, |
||||
|
shadowColor: 'rgba(0,0,0,.2)', |
||||
|
shadowOffsetX: 0, |
||||
|
shadowOffsetY: 10, |
||||
|
opacity: 1 |
||||
|
} |
||||
|
}, |
||||
|
data: [ |
||||
|
{ |
||||
|
value: [5000, 7000, 12000, 11000, 15000, 14000], |
||||
|
name: 'Allocated Budget' |
||||
|
}, |
||||
|
{ |
||||
|
value: [4000, 9000, 15000, 15000, 13000, 11000], |
||||
|
name: 'Expected Spending' |
||||
|
}, |
||||
|
{ |
||||
|
value: [5500, 11000, 12000, 15000, 12000, 12000], |
||||
|
name: 'Actual Spending' |
||||
|
} |
||||
|
], |
||||
|
animationDuration: animationDuration |
||||
|
}] |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,107 @@ |
|||||
|
<template> |
||||
|
<el-row :gutter="10" class="panel-group"> |
||||
|
<el-col :xs="16" :sm="16" :lg="24" class="card-panel-col"> |
||||
|
<div class="card-panel"> |
||||
|
<el-card class="box-card"> |
||||
|
<div slot="header" class="clearfix"> |
||||
|
<span style="font-weight: bolder">项目信息</span> |
||||
|
</div> |
||||
|
<div class="text item"> |
||||
|
<span style="font-weight: bolder">VIVIB基于当前流行技术组合的前后端RBAC管理系统:</span> |
||||
|
Go1.15.x+Beego2.x+Jwt+Redis+Mysql8+Vue |
||||
|
的前后端分离系统,权限控制采用 RBAC,支持数据字典与数据权限管理,支持动态路由等 |
||||
|
</div> |
||||
|
<div class="text item"> |
||||
|
<span style="font-weight: bolder">系统功能:</span><br> |
||||
|
1、用户管理:提供用户的相关配置 <br> |
||||
|
2、角色管理:对权限与菜单进行分配,可根据部门设置角色的数据权限 <br> |
||||
|
3、菜单管理:已实现菜单动态路由,后端可配置化 <br> |
||||
|
4、部门管理:可配置系统组织架构,树形表格展示 <br> |
||||
|
5、岗位管理:配置各个部门的职位 <br> |
||||
|
6、字典管理:可维护常用一些固定的数据,如:状态,性别等 <br> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
</el-card> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
|
||||
|
</el-row> |
||||
|
</template> |
||||
|
<script> |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
.text { |
||||
|
font-size: 16px; |
||||
|
} |
||||
|
|
||||
|
.item { |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
.clearfix:before, |
||||
|
.clearfix:after { |
||||
|
display: table; |
||||
|
content: ""; |
||||
|
} |
||||
|
.clearfix:after { |
||||
|
clear: both |
||||
|
} |
||||
|
|
||||
|
.panel-group { |
||||
|
margin-top: 18px; |
||||
|
.card-panel-col{ |
||||
|
margin-bottom: 32px; |
||||
|
} |
||||
|
.card-panel { |
||||
|
height: auto; |
||||
|
font-size: 12px; |
||||
|
position: relative; |
||||
|
overflow: hidden; |
||||
|
color: #666; |
||||
|
background: #fff; |
||||
|
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05); |
||||
|
border-color: rgba(0, 0, 0, .05); |
||||
|
.icon-people { |
||||
|
color: #40c9c6; |
||||
|
} |
||||
|
.icon-message { |
||||
|
color: #36a3f7; |
||||
|
} |
||||
|
.icon-money { |
||||
|
color: #f4516c; |
||||
|
} |
||||
|
.icon-shopping { |
||||
|
color: #34bfa3 |
||||
|
} |
||||
|
.card-panel-icon-wrapper { |
||||
|
float: left; |
||||
|
margin: 14px 0 0 14px; |
||||
|
padding: 16px; |
||||
|
transition: all 0.38s ease-out; |
||||
|
border-radius: 6px; |
||||
|
} |
||||
|
.card-panel-icon { |
||||
|
float: left; |
||||
|
font-size: 48px; |
||||
|
} |
||||
|
.card-panel-description { |
||||
|
float: right; |
||||
|
font-weight: bold; |
||||
|
margin: 26px; |
||||
|
margin-left: 0px; |
||||
|
.card-panel-text { |
||||
|
line-height: 18px; |
||||
|
color: rgba(0, 0, 0, 0.45); |
||||
|
font-size: 16px; |
||||
|
margin-bottom: 12px; |
||||
|
} |
||||
|
.card-panel-num { |
||||
|
font-size: 20px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,89 @@ |
|||||
|
<template> |
||||
|
<div class="errPage-container"> |
||||
|
<el-button icon="arrow-left" class="pan-back-btn" @click="back"> |
||||
|
返回 |
||||
|
</el-button> |
||||
|
<el-row> |
||||
|
<el-col :span="12"> |
||||
|
<h1 class="text-jumbo text-ginormous"> |
||||
|
Oops! |
||||
|
</h1> |
||||
|
<h2>你没有权限去该页面</h2> |
||||
|
<h6>如有不满请联系你领导</h6> |
||||
|
<ul class="list-unstyled"> |
||||
|
<li>或者你可以去:</li> |
||||
|
<li class="link-type"> |
||||
|
<router-link to="/dashboard"> |
||||
|
回首页 |
||||
|
</router-link> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</el-col> |
||||
|
<el-col :span="12"> |
||||
|
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream."> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import errGif from '@/assets/401_images/401.gif' |
||||
|
|
||||
|
export default { |
||||
|
name: 'Page401', |
||||
|
data() { |
||||
|
return { |
||||
|
errGif: errGif + '?' + +new Date() |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
back() { |
||||
|
if (this.$route.query.noGoBack) { |
||||
|
this.$router.push({ path: '/dashboard' }) |
||||
|
} else { |
||||
|
this.$router.go(-1) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.errPage-container { |
||||
|
width: 800px; |
||||
|
max-width: 100%; |
||||
|
margin: 100px auto; |
||||
|
.pan-back-btn { |
||||
|
background: #008489; |
||||
|
color: #fff; |
||||
|
border: none!important; |
||||
|
} |
||||
|
.pan-gif { |
||||
|
margin: 0 auto; |
||||
|
display: block; |
||||
|
} |
||||
|
.pan-img { |
||||
|
display: block; |
||||
|
margin: 0 auto; |
||||
|
width: 100%; |
||||
|
} |
||||
|
.text-jumbo { |
||||
|
font-size: 60px; |
||||
|
font-weight: 700; |
||||
|
color: #484848; |
||||
|
} |
||||
|
.list-unstyled { |
||||
|
font-size: 14px; |
||||
|
li { |
||||
|
padding-bottom: 5px; |
||||
|
} |
||||
|
a { |
||||
|
color: #008489; |
||||
|
text-decoration: none; |
||||
|
&:hover { |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,225 @@ |
|||||
|
<template> |
||||
|
<div class="wscn-http404-container"> |
||||
|
<div class="wscn-http404"> |
||||
|
<div class="pic-404"> |
||||
|
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404"> |
||||
|
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404"> |
||||
|
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404"> |
||||
|
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404"> |
||||
|
</div> |
||||
|
<div class="bullshit"> |
||||
|
<div class="bullshit__oops">OOPS!</div> |
||||
|
<div class="bullshit__headline">{{ message }}</div> |
||||
|
<div class="bullshit__info">请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告</div> |
||||
|
<a href="/" class="bullshit__return-home">返回首页</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
|
||||
|
export default { |
||||
|
name: 'Page404', |
||||
|
computed: { |
||||
|
message() { |
||||
|
return '网管说这个页面你不能进......' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
.wscn-http404-container{ |
||||
|
transform: translate(-50%,-50%); |
||||
|
position: absolute; |
||||
|
top: 40%; |
||||
|
left: 50%; |
||||
|
} |
||||
|
.wscn-http404 { |
||||
|
position: relative; |
||||
|
width: 1200px; |
||||
|
padding: 0 50px; |
||||
|
overflow: hidden; |
||||
|
.pic-404 { |
||||
|
position: relative; |
||||
|
float: left; |
||||
|
width: 600px; |
||||
|
overflow: hidden; |
||||
|
&__parent { |
||||
|
width: 100%; |
||||
|
} |
||||
|
&__child { |
||||
|
position: absolute; |
||||
|
&.left { |
||||
|
width: 80px; |
||||
|
top: 17px; |
||||
|
left: 220px; |
||||
|
opacity: 0; |
||||
|
animation-name: cloudLeft; |
||||
|
animation-duration: 2s; |
||||
|
animation-timing-function: linear; |
||||
|
animation-fill-mode: forwards; |
||||
|
animation-delay: 1s; |
||||
|
} |
||||
|
&.mid { |
||||
|
width: 46px; |
||||
|
top: 10px; |
||||
|
left: 420px; |
||||
|
opacity: 0; |
||||
|
animation-name: cloudMid; |
||||
|
animation-duration: 2s; |
||||
|
animation-timing-function: linear; |
||||
|
animation-fill-mode: forwards; |
||||
|
animation-delay: 1.2s; |
||||
|
} |
||||
|
&.right { |
||||
|
width: 62px; |
||||
|
top: 100px; |
||||
|
left: 500px; |
||||
|
opacity: 0; |
||||
|
animation-name: cloudRight; |
||||
|
animation-duration: 2s; |
||||
|
animation-timing-function: linear; |
||||
|
animation-fill-mode: forwards; |
||||
|
animation-delay: 1s; |
||||
|
} |
||||
|
@keyframes cloudLeft { |
||||
|
0% { |
||||
|
top: 17px; |
||||
|
left: 220px; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
20% { |
||||
|
top: 33px; |
||||
|
left: 188px; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
80% { |
||||
|
top: 81px; |
||||
|
left: 92px; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
100% { |
||||
|
top: 97px; |
||||
|
left: 60px; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
} |
||||
|
@keyframes cloudMid { |
||||
|
0% { |
||||
|
top: 10px; |
||||
|
left: 420px; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
20% { |
||||
|
top: 40px; |
||||
|
left: 360px; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
70% { |
||||
|
top: 130px; |
||||
|
left: 180px; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
100% { |
||||
|
top: 160px; |
||||
|
left: 120px; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
} |
||||
|
@keyframes cloudRight { |
||||
|
0% { |
||||
|
top: 100px; |
||||
|
left: 500px; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
20% { |
||||
|
top: 120px; |
||||
|
left: 460px; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
80% { |
||||
|
top: 180px; |
||||
|
left: 340px; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
100% { |
||||
|
top: 200px; |
||||
|
left: 300px; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.bullshit { |
||||
|
position: relative; |
||||
|
float: left; |
||||
|
width: 300px; |
||||
|
padding: 30px 0; |
||||
|
overflow: hidden; |
||||
|
&__oops { |
||||
|
font-size: 32px; |
||||
|
font-weight: bold; |
||||
|
line-height: 40px; |
||||
|
color: #1482f0; |
||||
|
opacity: 0; |
||||
|
margin-bottom: 20px; |
||||
|
animation-name: slideUp; |
||||
|
animation-duration: 0.5s; |
||||
|
animation-fill-mode: forwards; |
||||
|
} |
||||
|
&__headline { |
||||
|
font-size: 20px; |
||||
|
line-height: 24px; |
||||
|
color: #222; |
||||
|
font-weight: bold; |
||||
|
opacity: 0; |
||||
|
margin-bottom: 10px; |
||||
|
animation-name: slideUp; |
||||
|
animation-duration: 0.5s; |
||||
|
animation-delay: 0.1s; |
||||
|
animation-fill-mode: forwards; |
||||
|
} |
||||
|
&__info { |
||||
|
font-size: 13px; |
||||
|
line-height: 21px; |
||||
|
color: grey; |
||||
|
opacity: 0; |
||||
|
margin-bottom: 30px; |
||||
|
animation-name: slideUp; |
||||
|
animation-duration: 0.5s; |
||||
|
animation-delay: 0.2s; |
||||
|
animation-fill-mode: forwards; |
||||
|
} |
||||
|
&__return-home { |
||||
|
display: block; |
||||
|
float: left; |
||||
|
width: 110px; |
||||
|
height: 36px; |
||||
|
background: #1482f0; |
||||
|
border-radius: 100px; |
||||
|
text-align: center; |
||||
|
color: #ffffff; |
||||
|
opacity: 0; |
||||
|
font-size: 14px; |
||||
|
line-height: 36px; |
||||
|
cursor: pointer; |
||||
|
animation-name: slideUp; |
||||
|
animation-duration: 0.5s; |
||||
|
animation-delay: 0.3s; |
||||
|
animation-fill-mode: forwards; |
||||
|
} |
||||
|
@keyframes slideUp { |
||||
|
0% { |
||||
|
transform: translateY(60px); |
||||
|
opacity: 0; |
||||
|
} |
||||
|
100% { |
||||
|
transform: translateY(0); |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,12 @@ |
|||||
|
<script> |
||||
|
export default { |
||||
|
created() { |
||||
|
const { params, query } = this.$route |
||||
|
const { path } = params |
||||
|
this.$router.replace({ path: '/' + path, query }) |
||||
|
}, |
||||
|
render: function(h) { |
||||
|
return h() // avoid warning message |
||||
|
} |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,138 @@ |
|||||
|
<template> |
||||
|
<div class="dashboard-container"> |
||||
|
<div class="dashboard-editor-container"> |
||||
|
<yshop-info /> |
||||
|
<!-- <panel-group /> --> |
||||
|
|
||||
|
|
||||
|
<div class="divBox"> |
||||
|
<el-card :bordered="false" dis-hover> |
||||
|
<div slot="header"> |
||||
|
<div class="acea-row row-middle"> |
||||
|
<el-avatar |
||||
|
icon="el-icon-s-operation" |
||||
|
size="small" |
||||
|
style="color: #1890ff; background: #e6f7ff; font-size: 13px" |
||||
|
/> |
||||
|
<span class="ivu-pl-8">统计</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<el-row :gutter="24"> |
||||
|
<el-col |
||||
|
:xl="8" |
||||
|
:lg="8" |
||||
|
:md="24" |
||||
|
:sm="24" |
||||
|
:xs="24" |
||||
|
class="ivu-mb mb10 dashboard-console-visit" |
||||
|
> |
||||
|
<span class="ivu-pl-8">商品分析</span> |
||||
|
<order-count></order-count> |
||||
|
</el-col> |
||||
|
<el-col |
||||
|
:xl="8" |
||||
|
:lg="8" |
||||
|
:md="24" |
||||
|
:sm="24" |
||||
|
:xs="24" |
||||
|
class="ivu-mb mb10 dashboard-console-visit" |
||||
|
> |
||||
|
<span class="ivu-pl-8">本月成交额</span> |
||||
|
|
||||
|
<bar-chart /> |
||||
|
</el-col> |
||||
|
<el-col |
||||
|
:xl="8" |
||||
|
:lg="8" |
||||
|
:md="24" |
||||
|
:sm="24" |
||||
|
:xs="24" |
||||
|
class="ivu-mb mb10 dashboard-console-visit" |
||||
|
> |
||||
|
<span class="ivu-pl-8">本月订单数</span> |
||||
|
|
||||
|
<pie-chart /> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapGetters } from "vuex"; |
||||
|
import PanelGroup from "./dashboard/PanelGroup"; |
||||
|
import PanelGroupT from "./dashboard/PanelGroupT"; |
||||
|
import PieChart from "./dashboard/BarChartT"; |
||||
|
import BarChart from "./dashboard/BarChart"; |
||||
|
import YshopInfo from "./dashboard/YshopInfo"; |
||||
|
import OrderCount from "./dashboard/OrderCount"; |
||||
|
|
||||
|
/** |
||||
|
* 记录访问,只有页面刷新或者第一次加载才会记录 |
||||
|
*/ |
||||
|
//count().then((res) => {}); |
||||
|
|
||||
|
export default { |
||||
|
name: "Dashboard", |
||||
|
components: { |
||||
|
PanelGroup, |
||||
|
PanelGroupT, |
||||
|
PieChart, |
||||
|
BarChart, |
||||
|
YshopInfo, |
||||
|
OrderCount, |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters(["roles"]), |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
.dashboard-editor-container { |
||||
|
padding: 18px 22px 22px 22px; |
||||
|
background-color: rgb(240, 242, 245); |
||||
|
|
||||
|
.chart-wrapper { |
||||
|
background: #fff; |
||||
|
padding: 16px 16px 0; |
||||
|
margin-bottom: 32px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.acea-row { |
||||
|
/deep/.el-avatar--small { |
||||
|
width: 22px; |
||||
|
height: 22px; |
||||
|
line-height: 22px; |
||||
|
} |
||||
|
} |
||||
|
.checkTime { |
||||
|
/deep/.el-radio__input { |
||||
|
display: none; |
||||
|
} |
||||
|
} |
||||
|
.ivu-pl-8 { |
||||
|
margin-left: 8px; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
.divBox { |
||||
|
// padding: 0 20px !important; |
||||
|
} |
||||
|
.dashboard-console-visit { |
||||
|
/deep/.el-card__header { |
||||
|
padding: 14px 20px !important; |
||||
|
} |
||||
|
ul { |
||||
|
li { |
||||
|
list-style-type: none; |
||||
|
margin-top: 12px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.ivu-mb { |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,198 @@ |
|||||
|
<template> |
||||
|
<div class="login"> |
||||
|
<div style="display:flex;justify-content:space-between;align-items: center;"> |
||||
|
<div style="padding: 0 60px 0 0"> |
||||
|
<h3 style="color: #ffffff;font-size: 24px;padding: 0;margin-bottom: 10px"> |
||||
|
VIVIB RBAC管理后台系统 |
||||
|
</h3> |
||||
|
<ul style="margin: 0;color: #ffffff;font-size: 15px;line-height: 25px;padding-left: 20px"> |
||||
|
<li>Go1.15.x</li> |
||||
|
<li>Beego2.x</li> |
||||
|
<li>Mysql8</li> |
||||
|
<li>Jwt</li> |
||||
|
<li>Redis</li> |
||||
|
<li>Vue</li> |
||||
|
</ul> |
||||
|
<h3 style="color: #ffffff;"> |
||||
|
后台地址:https://go.yixiang.co |
||||
|
</h3> |
||||
|
</div> |
||||
|
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" label-position="left" label-width="0px" class="login-form"> |
||||
|
<h3 class="title"> |
||||
|
VIVIB后台管理系统-1.0 |
||||
|
</h3> |
||||
|
<el-form-item prop="username"> |
||||
|
<el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号"> |
||||
|
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item prop="password"> |
||||
|
<el-input v-model="loginForm.password" type="password" auto-complete="off" placeholder="密码" @keyup.enter.native="handleLogin"> |
||||
|
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item prop="code"> |
||||
|
<el-input v-model="loginForm.code" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter.native="handleLogin"> |
||||
|
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /> |
||||
|
</el-input> |
||||
|
<div class="login-code"> |
||||
|
<img :src="codeUrl" @click="getCode"> |
||||
|
</div> |
||||
|
</el-form-item> |
||||
|
<el-form-item style="width:100%;"> |
||||
|
<el-button :loading="loading" size="medium" type="primary" style="width:100%;" @click.native.prevent="handleLogin"> |
||||
|
<span v-if="!loading">登 录</span> |
||||
|
<span v-else>登 录 中...</span> |
||||
|
</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
</div> |
||||
|
<!-- 底部 --> |
||||
|
<div v-if="$store.state.settings.showFooter" id="el-login-footer"> |
||||
|
<span v-html="$store.state.settings.footerTxt" /> |
||||
|
<span> ⋅ </span> |
||||
|
<a href="http://www.beian.miit.gov.cn" target="_blank">{{ $store.state.settings.caseNumber }}</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { encrypt } from '@/utils/rsaEncrypt' |
||||
|
import Config from '@/settings' |
||||
|
import { getCodeImg } from '@/api/login' |
||||
|
import Cookies from 'js-cookie' |
||||
|
export default { |
||||
|
name: 'Login', |
||||
|
data() { |
||||
|
return { |
||||
|
codeUrl: '', |
||||
|
cookiePass: '', |
||||
|
loginForm: { |
||||
|
username: 'admin', |
||||
|
password: '123456', |
||||
|
code: '', |
||||
|
uuid: '' |
||||
|
}, |
||||
|
loginRules: { |
||||
|
username: [{ required: true, trigger: 'blur', message: '用户名不能为空' }], |
||||
|
password: [{ required: true, trigger: 'blur', message: '密码不能为空' }], |
||||
|
code: [{ required: true, trigger: 'change', message: '验证码不能为空' }] |
||||
|
}, |
||||
|
loading: false, |
||||
|
redirect: undefined |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
$route: { |
||||
|
handler: function(route) { |
||||
|
this.redirect = route.query && route.query.redirect |
||||
|
}, |
||||
|
immediate: true |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
this.getCode() |
||||
|
this.getCookie() |
||||
|
}, |
||||
|
methods: { |
||||
|
getCode() { |
||||
|
getCodeImg().then(res => { |
||||
|
this.codeUrl = res.data.base_64_blob |
||||
|
this.loginForm.uuid = res.data.id |
||||
|
}) |
||||
|
}, |
||||
|
getCookie() { |
||||
|
const username = Cookies.get('username') |
||||
|
let password = Cookies.get('password') |
||||
|
// 保存cookie里面的加密后的密码 |
||||
|
this.cookiePass = password === undefined ? '' : password |
||||
|
password = password === undefined ? this.loginForm.password : password |
||||
|
this.loginForm = { |
||||
|
username: username === undefined ? this.loginForm.username : username, |
||||
|
password: password, |
||||
|
code: '' |
||||
|
} |
||||
|
}, |
||||
|
handleLogin() { |
||||
|
this.$refs.loginForm.validate(valid => { |
||||
|
const user = { |
||||
|
username: this.loginForm.username, |
||||
|
password: this.loginForm.password, |
||||
|
code: this.loginForm.code, |
||||
|
uuid: this.loginForm.uuid |
||||
|
} |
||||
|
|
||||
|
if (valid) { |
||||
|
this.loading = true |
||||
|
if (user.rememberMe) { |
||||
|
Cookies.set('username', user.username, { expires: Config.passCookieExpires }) |
||||
|
Cookies.set('password', user.password, { expires: Config.passCookieExpires }) |
||||
|
|
||||
|
} else { |
||||
|
Cookies.remove('username') |
||||
|
Cookies.remove('password') |
||||
|
|
||||
|
} |
||||
|
this.$store.dispatch('Login', user).then(() => { |
||||
|
this.loading = false |
||||
|
this.$router.push({ path: this.redirect || '/' }) |
||||
|
}).catch(() => { |
||||
|
this.loading = false |
||||
|
this.getCode() |
||||
|
}) |
||||
|
} else { |
||||
|
console.log('error submit!!') |
||||
|
return false |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss"> |
||||
|
.login { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
height: 100%; |
||||
|
background-image:url("../assets/images/bg.jpg"); |
||||
|
background-size: cover; |
||||
|
} |
||||
|
.title { |
||||
|
margin: 0 auto 30px auto; |
||||
|
text-align: center; |
||||
|
color: #707070; |
||||
|
} |
||||
|
|
||||
|
.login-form { |
||||
|
border-radius: 6px; |
||||
|
background: #ffffff; |
||||
|
width: 385px; |
||||
|
padding: 25px 25px 5px 25px; |
||||
|
.el-input { |
||||
|
height: 38px; |
||||
|
input { |
||||
|
height: 38px; |
||||
|
} |
||||
|
} |
||||
|
.input-icon{ |
||||
|
height: 39px;width: 14px;margin-left: 2px; |
||||
|
} |
||||
|
} |
||||
|
.login-tip { |
||||
|
font-size: 13px; |
||||
|
text-align: center; |
||||
|
color: #bfbfbf; |
||||
|
} |
||||
|
.login-code { |
||||
|
width: 33%; |
||||
|
display: inline-block; |
||||
|
height: 38px; |
||||
|
float: right; |
||||
|
img{ |
||||
|
cursor: pointer; |
||||
|
vertical-align:middle |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,3 @@ |
|||||
|
<template> |
||||
|
<router-view /> |
||||
|
</template> |
@ -0,0 +1,5 @@ |
|||||
|
<template> |
||||
|
<div style="padding:30px;"> |
||||
|
<el-alert :closable="false" title="三级菜单1" type="success" /> |
||||
|
</div> |
||||
|
</template> |
@ -0,0 +1,5 @@ |
|||||
|
<template> |
||||
|
<div style="padding:30px;"> |
||||
|
<el-alert :closable="false" title="三级菜单2" type="success" /> |
||||
|
</div> |
||||
|
</template> |
@ -0,0 +1,5 @@ |
|||||
|
<template> |
||||
|
<div style="padding:30px;"> |
||||
|
<el-alert :closable="false" title="二级菜单" /> |
||||
|
</div> |
||||
|
</template> |
@ -0,0 +1,228 @@ |
|||||
|
<template> |
||||
|
<div class="app-container"> |
||||
|
<!--工具栏--> |
||||
|
<div class="head-container"> |
||||
|
<div v-if="crud.props.searchToggle"> |
||||
|
<!-- 搜索 --> |
||||
|
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery"> |
||||
|
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" /> |
||||
|
</el-select> |
||||
|
<rrOperation :crud="crud" /> |
||||
|
</div> |
||||
|
<crudOperation :permission="permission" /> |
||||
|
</div> |
||||
|
<!--表单组件--> |
||||
|
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px"> |
||||
|
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px"> |
||||
|
<el-form-item label="名称" prop="name"> |
||||
|
<el-input v-model="form.name" style="width: 370px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-if="form.pid !== 0" label="状态" prop="enabled"> |
||||
|
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-if="form.pid !== 0" style="margin-bottom: 0;" label="上级部门" prop="pid"> |
||||
|
<treeselect v-model="form.pid" :options="depts" :normalizer="normalizer" style="width: 370px;" placeholder="选择上级类目" /> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<el-button type="text" @click="crud.cancelCU">取消</el-button> |
||||
|
<el-button :loading="crud.cu === 2" type="primary" @click="crud.submitCU">确认</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
<!--表格渲染--> |
||||
|
<el-table ref="table" v-loading="crud.loading" :tree-props="{children: 'children', hasChildren: 'hasChildren'}" default-expand-all :data="crud.data" row-key="id" @select="crud.selectChange" @select-all="crud.selectAllChange" @selection-change="crud.selectionChangeHandler"> |
||||
|
<el-table-column :selectable="checkboxT" type="selection" width="55" /> |
||||
|
<el-table-column v-if="columns.visible('name')" label="名称" prop="name" /> |
||||
|
<el-table-column v-if="columns.visible('enabled')" label="状态" align="center" prop="enabled"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-switch |
||||
|
v-model="scope.row.enabled" |
||||
|
:disabled="scope.row.id === 1" |
||||
|
active-color="#409EFF" |
||||
|
inactive-color="#F56C6C" |
||||
|
:active-value= "1" |
||||
|
:inactive-value= "0" |
||||
|
@change="changeEnabled(scope.row, scope.row.enabled,)" |
||||
|
/> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('createTime')" prop="createTime" label="创建日期"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span>{{ parseTime(scope.row.createTime) }}</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-permission="['admin','dept:edit','dept:del']" label="操作" width="130px" align="center" fixed="right"> |
||||
|
<template slot-scope="scope"> |
||||
|
<udOperation |
||||
|
:data="scope.row" |
||||
|
:permission="permission" |
||||
|
:disabled-dle="scope.row.id === 1" |
||||
|
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!" |
||||
|
/> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import crudDept from '@/api/system/dept' |
||||
|
import Treeselect from '@riophae/vue-treeselect' |
||||
|
import '@riophae/vue-treeselect/dist/vue-treeselect.css' |
||||
|
import CRUD, { presenter, header, form, crud } from '@crud/crud' |
||||
|
import rrOperation from '@crud/RR.operation' |
||||
|
import crudOperation from '@crud/CRUD.operation' |
||||
|
import udOperation from '@crud/UD.operation' |
||||
|
|
||||
|
// crud交由presenter持有 |
||||
|
const defaultCrud = CRUD({ title: '部门', url: 'admin/dept', crudMethod: { ...crudDept }}) |
||||
|
const defaultForm = { id: null, name: null, pid: 1, enabled: 1 } |
||||
|
export default { |
||||
|
name: 'Dept', |
||||
|
components: { Treeselect, crudOperation, rrOperation, udOperation }, |
||||
|
mixins: [presenter(defaultCrud), header(), form(defaultForm), crud()], |
||||
|
// 设置数据字典 |
||||
|
dicts: ['dept_status'], |
||||
|
data() { |
||||
|
return { |
||||
|
normalizer(node){ |
||||
|
//去掉children=[]的children属性 respectively |
||||
|
if(node.children && !node.children.length){ |
||||
|
delete node.children; |
||||
|
} |
||||
|
}, |
||||
|
depts: [], |
||||
|
rules: { |
||||
|
name: [ |
||||
|
{ required: true, message: '请输入名称', trigger: 'blur' } |
||||
|
] |
||||
|
}, |
||||
|
permission: { |
||||
|
add: ['admin', 'dept:add'], |
||||
|
edit: ['admin', 'dept:edit'], |
||||
|
del: ['admin', 'dept:del'] |
||||
|
}, |
||||
|
enabledTypeOptions: [ |
||||
|
{ key: 1, display_name: '正常' }, |
||||
|
{ key: 0, display_name: '禁用' } |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
getDeptDatas(tree, treeNode, resolve) { |
||||
|
const params = { pid: tree.id } |
||||
|
setTimeout(() => { |
||||
|
crudDept.getDepts(params).then(res => { |
||||
|
resolve(res.content) |
||||
|
}) |
||||
|
}, 100) |
||||
|
}, |
||||
|
// 新增与编辑前做的操作 |
||||
|
[CRUD.HOOK.afterToCU](crud, form) { |
||||
|
if (form.pid !== null) { |
||||
|
form.isTop = '0' |
||||
|
} else if (form.id !== null) { |
||||
|
form.isTop = '1' |
||||
|
} |
||||
|
// form.enabled = `${form.enabled}` |
||||
|
if (form.id != null) { |
||||
|
this.getSupDepts(form.id) |
||||
|
} else { |
||||
|
this.getDepts() |
||||
|
} |
||||
|
}, |
||||
|
getSupDepts(id) { |
||||
|
var data={"pid":id}; |
||||
|
crudDept.getDeptSuperior(data).then(res => { |
||||
|
const date = res.data.content |
||||
|
this.buildDepts(date) |
||||
|
this.depts = date |
||||
|
}) |
||||
|
}, |
||||
|
buildDepts(depts) { |
||||
|
(depts || []).forEach(data => { |
||||
|
if (data.children) { |
||||
|
this.buildDepts(data.children) |
||||
|
} |
||||
|
if (data.hasChildren && !data.children) { |
||||
|
data.children = null |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
getDepts() { |
||||
|
crudDept.getDepts({ enabled: 1 }).then(res => { |
||||
|
this.depts = res.data.content.map(function(obj) { |
||||
|
if (obj.hasChildren) { |
||||
|
obj.children = null |
||||
|
} |
||||
|
return obj |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
// 获取弹窗内部门数据 |
||||
|
loadDepts({ action, parentNode, callback }) { |
||||
|
if (action === LOAD_CHILDREN_OPTIONS) { |
||||
|
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => { |
||||
|
parentNode.children = res.content.map(function(obj) { |
||||
|
if (obj.hasChildren) { |
||||
|
obj.children = null |
||||
|
} |
||||
|
return obj |
||||
|
}) |
||||
|
setTimeout(() => { |
||||
|
callback() |
||||
|
}, 100) |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
// 提交前的验证 |
||||
|
[CRUD.HOOK.afterValidateCU]() { |
||||
|
if (this.form.pid !== null && this.form.pid === this.form.id) { |
||||
|
this.$message({ |
||||
|
message: '上级部门不能为空', |
||||
|
type: 'warning' |
||||
|
}) |
||||
|
return false |
||||
|
} |
||||
|
if (this.form.isTop === '1') { |
||||
|
this.form.pid = null |
||||
|
} |
||||
|
return true |
||||
|
}, |
||||
|
// 改变状态 |
||||
|
changeEnabled(data, val) { |
||||
|
console.log("aa:"+JSON.stringify(this.dict.label.dept_status)) |
||||
|
console.log("aa:"+val) |
||||
|
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
crudDept.edit(data).then(res => { |
||||
|
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS) |
||||
|
}).catch(err => { |
||||
|
data.enabled = !data.enabled |
||||
|
console.log(err.response.data.message) |
||||
|
}) |
||||
|
}).catch(() => { |
||||
|
data.enabled = !data.enabled |
||||
|
}) |
||||
|
}, |
||||
|
checkboxT(row, rowIndex) { |
||||
|
return row.id !== 1 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value { |
||||
|
height: 30px; |
||||
|
line-height: 30px; |
||||
|
} |
||||
|
</style> |
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
::v-deep .el-input-number .el-input__inner { |
||||
|
text-align: left; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,114 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<div v-if="dictName === ''"> |
||||
|
<div class="my-code">点击字典查看详情</div> |
||||
|
</div> |
||||
|
<div v-else> |
||||
|
<!--工具栏--> |
||||
|
<div class="head-container"> |
||||
|
<!-- 搜索 --> |
||||
|
<el-input v-model="query.blurry" clearable size="small" placeholder="输入字典标签查询" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" /> |
||||
|
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button> |
||||
|
</div> |
||||
|
<!--表单组件--> |
||||
|
<el-dialog append-to-body :close-on-click-modal="false" :before-close="cancel" :visible.sync="dialog" :title="getFormTitle()" width="500px"> |
||||
|
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px"> |
||||
|
<el-form-item label="字典标签" prop="label"> |
||||
|
<el-input v-model="form.label" style="width: 370px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="字典值"> |
||||
|
<el-input v-model="form.value" style="width: 370px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="排序" prop="sort"> |
||||
|
<el-input-number v-model.number="form.sort" :min="0" :max="999" controls-position="right" style="width: 370px;" /> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<el-button type="text" @click="cancel">取消</el-button> |
||||
|
<el-button :loading="loading" type="primary" @click="submitMethod">确认</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
<!--表格渲染--> |
||||
|
<el-table v-loading="loading" :data="data" style="width: 100%;"> |
||||
|
<el-table-column label="所属字典"> |
||||
|
{{ dictName }} |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="label" label="字典标签" /> |
||||
|
<el-table-column prop="value" label="字典值" /> |
||||
|
<el-table-column prop="sort" label="排序" /> |
||||
|
<el-table-column v-if="checkPermission(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-button v-permission="['admin','dict:edit']" size="mini" type="primary" icon="el-icon-edit" @click="showEditFormDialog(scope.row)" /> |
||||
|
<el-popover |
||||
|
:ref="scope.row.id" |
||||
|
v-permission="['admin','dict:del']" |
||||
|
placement="top" |
||||
|
width="180" |
||||
|
> |
||||
|
<p>确定删除本条数据吗?</p> |
||||
|
<div style="text-align: right; margin: 0"> |
||||
|
<el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button> |
||||
|
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button> |
||||
|
</div> |
||||
|
<el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" /> |
||||
|
</el-popover> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
<!--分页组件--> |
||||
|
<el-pagination |
||||
|
:total="total" |
||||
|
:current-page="page + 1" |
||||
|
style="margin-top: 8px;" |
||||
|
layout="total, prev, pager, next, sizes" |
||||
|
@size-change="sizeChange" |
||||
|
@current-change="pageChange" |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import crud from '@/mixins/crud' |
||||
|
import crudDictDetail from '@/api/system/dictDetail' |
||||
|
export default { |
||||
|
mixins: [crud], |
||||
|
data() { |
||||
|
return { |
||||
|
title: '字典详情', |
||||
|
sort: ['sort,asc', 'id,desc'], |
||||
|
crudMethod: { ...crudDictDetail }, |
||||
|
dictName: '', |
||||
|
dictId: null, |
||||
|
form: { id: null, label: null, value: null, dictId: null,dictName: null, sort: 999 }, |
||||
|
rules: { |
||||
|
label: [ |
||||
|
{ required: true, message: '请输入字典标签', trigger: 'blur' } |
||||
|
], |
||||
|
sort: [ |
||||
|
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 获取数据前设置好接口地址 |
||||
|
beforeInit() { |
||||
|
this.url = 'admin/dictDetail' |
||||
|
if (this.dictName) { |
||||
|
this.params['dictName'] = this.dictName |
||||
|
} |
||||
|
if(this.form.dictId) { |
||||
|
this.params['dictId'] = this.form.dictId |
||||
|
} |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
/deep/ .el-input-number .el-input__inner { |
||||
|
text-align: left; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,160 @@ |
|||||
|
<template> |
||||
|
<div class="app-container"> |
||||
|
<!--表单组件--> |
||||
|
<el-dialog append-to-body :close-on-click-modal="false" :before-close="cancel" :visible.sync="dialog" :title="getFormTitle()" width="500px"> |
||||
|
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px"> |
||||
|
<el-form-item label="字典名称" prop="name"> |
||||
|
<el-input v-model="form.name" style="width: 370px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="描述"> |
||||
|
<el-input v-model="form.remark" style="width: 370px;" /> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<el-button type="text" @click="cancel">取消</el-button> |
||||
|
<el-button :loading="loading" type="primary" @click="submitMethod">确认</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
<!-- 字典列表 --> |
||||
|
<el-row :gutter="10"> |
||||
|
<el-col :xs="24" :sm="24" :md="10" :lg="10" :xl="10" style="margin-bottom: 10px"> |
||||
|
<el-card class="box-card"> |
||||
|
<div slot="header" class="clearfix"> |
||||
|
<span>字典列表</span> |
||||
|
<!-- 新增 --> |
||||
|
<el-button |
||||
|
v-permission="['admin','dict:add']" |
||||
|
class="filter-item" |
||||
|
size="mini" |
||||
|
style="float: right;padding: 4px 10px" |
||||
|
type="primary" |
||||
|
icon="el-icon-plus" |
||||
|
@click="showAddFormDialog" |
||||
|
>新增</el-button> |
||||
|
</div> |
||||
|
<!--工具栏--> |
||||
|
<div class="head-container"> |
||||
|
<!-- 搜索 --> |
||||
|
<el-input v-model="query.blurry" clearable size="small" placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" /> |
||||
|
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button> |
||||
|
<!-- 导出 --> |
||||
|
<el-button |
||||
|
:loading="downloadLoading" |
||||
|
size="mini" |
||||
|
class="filter-item" |
||||
|
type="warning" |
||||
|
icon="el-icon-download" |
||||
|
@click="downloadMethod" |
||||
|
>导出</el-button> |
||||
|
</div> |
||||
|
<!--表格渲染--> |
||||
|
<el-table v-loading="loading" :data="data" highlight-current-row style="width: 100%;" @current-change="handleCurrentChange"> |
||||
|
<el-table-column :show-overflow-tooltip="true" prop="name" label="名称" /> |
||||
|
<el-table-column :show-overflow-tooltip="true" prop="remark" label="描述" /> |
||||
|
<el-table-column v-if="checkPermission(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-button v-permission="['admin','dict:edit']" size="mini" type="primary" icon="el-icon-edit" @click="showEditFormDialog(scope.row)" /> |
||||
|
<el-popover |
||||
|
:ref="scope.row.id" |
||||
|
v-permission="['admin','dict:del']" |
||||
|
placement="top" |
||||
|
width="180" |
||||
|
> |
||||
|
<p>此操作将删除字典与对应的字典详情,确定要删除吗?</p> |
||||
|
<div style="text-align: right; margin: 0"> |
||||
|
<el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button> |
||||
|
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button> |
||||
|
</div> |
||||
|
<el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" /> |
||||
|
</el-popover> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
<!--分页组件--> |
||||
|
<el-pagination |
||||
|
:total="total" |
||||
|
:current-page="page + 1" |
||||
|
style="margin-top: 8px;" |
||||
|
layout="total, prev, pager, next, sizes" |
||||
|
@size-change="sizeChange" |
||||
|
@current-change="pageChange" |
||||
|
/> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<!-- 字典详情列表 --> |
||||
|
<el-col :xs="24" :sm="24" :md="14" :lg="14" :xl="14"> |
||||
|
<el-card class="box-card"> |
||||
|
<div slot="header" class="clearfix"> |
||||
|
<span>字典详情</span> |
||||
|
<el-button |
||||
|
v-if="checkPermission(['admin','dict:add']) && this.$refs.dictDetail && this.$refs.dictDetail.dictName" |
||||
|
class="filter-item" |
||||
|
size="mini" |
||||
|
style="float: right;padding: 4px 10px" |
||||
|
type="primary" |
||||
|
icon="el-icon-plus" |
||||
|
@click="$refs.dictDetail.showAddFormDialog" |
||||
|
>新增</el-button> |
||||
|
</div> |
||||
|
<dictDetail ref="dictDetail" /> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import crud from '@/mixins/crud' |
||||
|
import dictDetail from './dictDetail' |
||||
|
import crudDict from '@/api/system/dict' |
||||
|
export default { |
||||
|
name: 'Dict', |
||||
|
components: { dictDetail }, |
||||
|
mixins: [crud], |
||||
|
data() { |
||||
|
return { |
||||
|
title: '字典', |
||||
|
crudMethod: { ...crudDict }, |
||||
|
queryTypeOptions: [ |
||||
|
{ key: 'name', display_name: '字典名称' }, |
||||
|
{ key: 'remark', display_name: '描述' } |
||||
|
], |
||||
|
form: { id: null, name: null, remark: null }, |
||||
|
rules: { |
||||
|
name: [ |
||||
|
{ required: true, message: '请输入名称', trigger: 'blur' } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
this.$nextTick(() => { |
||||
|
this.init() |
||||
|
}) |
||||
|
}, |
||||
|
methods: { |
||||
|
// 获取数据前设置好接口地址 |
||||
|
beforeInit() { |
||||
|
this.url = 'admin/dict' |
||||
|
if (this.$refs.dictDetail) { |
||||
|
this.$refs.dictDetail.data = [] |
||||
|
this.$refs.dictDetail.dictName = '' |
||||
|
} |
||||
|
return true |
||||
|
}, |
||||
|
// 选中字典后,设置字典详情数据 |
||||
|
handleCurrentChange(val) { |
||||
|
if (val) { |
||||
|
this.$refs.dictDetail.dictName = val.name |
||||
|
console.log("a:"+val.name) |
||||
|
this.$refs.dictDetail.form.dictName = val.name |
||||
|
this.$refs.dictDetail.form.dictId = val.id |
||||
|
this.$refs.dictDetail.init() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
</style> |
@ -0,0 +1,120 @@ |
|||||
|
<template> |
||||
|
<div class="app-container"> |
||||
|
<!--工具栏--> |
||||
|
<div class="head-container"> |
||||
|
<eHeader :dict="dict" :permission="permission" /> |
||||
|
<crudOperation :permission="permission" /> |
||||
|
</div> |
||||
|
<!--表格渲染--> |
||||
|
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler"> |
||||
|
<el-table-column type="selection" width="55" /> |
||||
|
<el-table-column v-if="columns.visible('name')" prop="name" label="名称" /> |
||||
|
<el-table-column v-if="columns.visible('dept')" prop="dept" label="所属部门"> |
||||
|
<template slot-scope="scope"> |
||||
|
<div>{{ scope.row.deptSuperiorName ? scope.row.deptSuperiorName + ' / ' : '' }}{{ scope.row.dept.name }}</div> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('sort')" prop="sort" label="排序"> |
||||
|
<template slot-scope="scope"> |
||||
|
{{ scope.row.sort }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('status')" prop="status" label="状态" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-switch |
||||
|
v-model="scope.row.enabled" |
||||
|
active-color="#409EFF" |
||||
|
inactive-color="#F56C6C" |
||||
|
:active-value= "1" |
||||
|
:inactive-value= "0" |
||||
|
@change="changeEnabled(scope.row, scope.row.enabled)" |
||||
|
/> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('createTime')" prop="createTime" label="创建日期"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span>{{ parseTime(scope.row.createTime) }}</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<!-- 编辑与删除 --> |
||||
|
<el-table-column |
||||
|
v-permission="['admin','job:edit','job:del']" |
||||
|
label="操作" |
||||
|
width="130px" |
||||
|
align="center" |
||||
|
fixed="right" |
||||
|
> |
||||
|
<template slot-scope="scope"> |
||||
|
<udOperation |
||||
|
:data="scope.row" |
||||
|
:permission="permission" |
||||
|
/> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
<!--分页组件--> |
||||
|
<pagination /> |
||||
|
<!--表单渲染--> |
||||
|
<eForm :job-status="dict.job_status" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import crudJob from '@/api/system/job' |
||||
|
import eHeader from './module/header' |
||||
|
import eForm from './module/form' |
||||
|
import CRUD, { presenter } from '@crud/crud' |
||||
|
import crudOperation from '@crud/CRUD.operation' |
||||
|
import pagination from '@crud/Pagination' |
||||
|
import udOperation from '@crud/UD.operation' |
||||
|
|
||||
|
// crud交由presenter持有 |
||||
|
const crud = CRUD({ |
||||
|
title: '岗位', |
||||
|
url: 'admin/job', |
||||
|
sort: ['sort,asc', 'id,desc'], |
||||
|
crudMethod: { ...crudJob } |
||||
|
}) |
||||
|
|
||||
|
export default { |
||||
|
name: 'Job', |
||||
|
components: { eHeader, eForm, crudOperation, pagination, udOperation }, |
||||
|
mixins: [presenter(crud)], |
||||
|
// 数据字典 |
||||
|
dicts: ['job_status'], |
||||
|
data() { |
||||
|
return { |
||||
|
permission: { |
||||
|
add: ['admin', 'job:add'], |
||||
|
edit: ['admin', 'job:edit'], |
||||
|
del: ['admin', 'job:del'] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 改变状态 |
||||
|
changeEnabled(data, val) { |
||||
|
this.$confirm('此操作将 "' + this.dict.label.job_status[val] + '" ' + data.name + '岗位, 是否继续?', '提示', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
crud.crudMethod.edit(data).then(() => { |
||||
|
crud.notify(this.dict.label.job_status[val] + '成功', 'success') |
||||
|
}).catch(err => { |
||||
|
data.enabled = !data.enabled |
||||
|
console.log(err.data.message) |
||||
|
}) |
||||
|
}).catch(() => { |
||||
|
data.enabled = !data.enabled |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
/deep/ .el-input-number .el-input__inner { |
||||
|
text-align: left; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,149 @@ |
|||||
|
<template> |
||||
|
<el-dialog |
||||
|
append-to-body |
||||
|
:close-on-click-modal="false" |
||||
|
:before-close="crud.cancelCU" |
||||
|
:visible="crud.status.cu > 0" |
||||
|
:title="crud.status.title" |
||||
|
width="500px" |
||||
|
> |
||||
|
<el-form |
||||
|
ref="form" |
||||
|
:model="form" |
||||
|
:rules="rules" |
||||
|
size="small" |
||||
|
label-width="80px" |
||||
|
> |
||||
|
<el-form-item |
||||
|
label="名称" |
||||
|
prop="name" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="form.name" |
||||
|
style="width: 370px;" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
label="排序" |
||||
|
prop="sort" |
||||
|
> |
||||
|
<el-input-number |
||||
|
v-model.number="form.sort" |
||||
|
:min="0" |
||||
|
:max="999" |
||||
|
controls-position="right" |
||||
|
style="width: 370px;" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
v-if="form.pid !== 0" |
||||
|
label="状态" |
||||
|
prop="enabled" |
||||
|
> |
||||
|
<el-radio |
||||
|
v-for="item in jobStatus" |
||||
|
:key="item.id" |
||||
|
v-model="form.enabled" |
||||
|
:label="item.value" |
||||
|
> |
||||
|
{{ item.label }} |
||||
|
</el-radio> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
label="所属部门" |
||||
|
prop="dept.id" |
||||
|
:rules="rules.dept" |
||||
|
> |
||||
|
<treeselect |
||||
|
v-model="form.dept.id" |
||||
|
:options="depts" |
||||
|
style="width: 370px" |
||||
|
placeholder="选择部门" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<div |
||||
|
slot="footer" |
||||
|
class="dialog-footer" |
||||
|
> |
||||
|
<el-button |
||||
|
type="text" |
||||
|
@click="crud.cancelCU" |
||||
|
> |
||||
|
取消 |
||||
|
</el-button> |
||||
|
<el-button |
||||
|
:loading="crud.cu === 2" |
||||
|
type="primary" |
||||
|
@click="crud.submitCU" |
||||
|
> |
||||
|
确认 |
||||
|
</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import CRUD, { form } from '@crud/crud' |
||||
|
import { getDepts } from '@/api/system/dept' |
||||
|
import Treeselect from '@riophae/vue-treeselect' |
||||
|
import '@riophae/vue-treeselect/dist/vue-treeselect.css' |
||||
|
|
||||
|
const defaultForm = { |
||||
|
id: null, |
||||
|
name: '', |
||||
|
sort: 999, |
||||
|
enabled: 1, |
||||
|
dept: { |
||||
|
id: 1 |
||||
|
} |
||||
|
} |
||||
|
export default { |
||||
|
components: { Treeselect }, |
||||
|
mixins: [form(defaultForm)], |
||||
|
props: { |
||||
|
jobStatus: { |
||||
|
type: Array, |
||||
|
required: true |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
depts: [], |
||||
|
rules: { |
||||
|
name: [ |
||||
|
{ required: true, message: '请输入名称', trigger: 'blur' } |
||||
|
], |
||||
|
sort: [ |
||||
|
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' } |
||||
|
], |
||||
|
dept: { required: true, message: '所属部门不能为空', trigger: 'select' } |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
[CRUD.HOOK.beforeToCU]() { |
||||
|
getDepts({ enabled: 1 }).then(res => { |
||||
|
this.depts = res.data.content |
||||
|
}) |
||||
|
}, |
||||
|
// 提交前的验证 |
||||
|
[CRUD.HOOK.afterValidateCU]() { |
||||
|
if (!this.form.dept.id) { |
||||
|
this.$notify({ |
||||
|
title: '所属部门不能为空', |
||||
|
type: 'warning' |
||||
|
}) |
||||
|
return false |
||||
|
} |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
/deep/ .el-input-number .el-input__inner { |
||||
|
text-align: left; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,32 @@ |
|||||
|
<template> |
||||
|
<div |
||||
|
v-if="crud.props.searchToggle" |
||||
|
> |
||||
|
<el-input v-model="query.blurry" clearable size="small" placeholder="输入岗位名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> |
||||
|
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery"> |
||||
|
<el-option v-for="item in dict.dict.job_status" :key="item.value" :label="item.label" :value="item.value" /> |
||||
|
</el-select> |
||||
|
<rrOperation |
||||
|
:crud="crud" |
||||
|
/> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { header } from '@crud/crud' |
||||
|
import rrOperation from '@crud/RR.operation' |
||||
|
export default { |
||||
|
components: { rrOperation }, |
||||
|
mixins: [header()], |
||||
|
props: { |
||||
|
dict: { |
||||
|
type: Object, |
||||
|
required: true |
||||
|
}, |
||||
|
permission: { |
||||
|
type: Object, |
||||
|
required: true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,206 @@ |
|||||
|
<template> |
||||
|
<div class="app-container"> |
||||
|
<!--工具栏--> |
||||
|
<div class="head-container"> |
||||
|
<div v-if="crud.props.searchToggle"> |
||||
|
<!-- 搜索 --> |
||||
|
<el-input v-model="query.blurry" clearable size="small" placeholder="模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" /> |
||||
|
<rrOperation :crud="crud" /> |
||||
|
</div> |
||||
|
<crudOperation :permission="permission" /> |
||||
|
</div> |
||||
|
<!--表单渲染--> |
||||
|
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="580px"> |
||||
|
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px"> |
||||
|
<el-form-item label="菜单类型" prop="type"> |
||||
|
<el-radio-group v-model="form.type" size="mini" style="width: 178px"> |
||||
|
<el-radio-button :label="0">目录</el-radio-button> |
||||
|
<el-radio-button :label="1">菜单</el-radio-button> |
||||
|
<el-radio-button :label="2">按钮</el-radio-button> |
||||
|
</el-radio-group> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="form.type.toString() !== '2'" label="菜单图标" prop="icon"> |
||||
|
<el-popover |
||||
|
placement="bottom-start" |
||||
|
width="450" |
||||
|
trigger="click" |
||||
|
@show="$refs['iconSelect'].reset()" |
||||
|
> |
||||
|
<IconSelect ref="iconSelect" @selected="selected" /> |
||||
|
<el-input slot="reference" v-model="form.icon" style="width: 450px;" placeholder="点击选择图标" readonly> |
||||
|
<svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" style="height: 32px;width: 16px;" /> |
||||
|
<i v-else slot="prefix" class="el-icon-search el-input__icon" /> |
||||
|
</el-input> |
||||
|
</el-popover> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="form.type.toString() !== '2'" label="外链菜单" prop="iframe"> |
||||
|
<el-radio-group v-model="form.iframe" size="mini"> |
||||
|
<el-radio-button :label="1">是</el-radio-button> |
||||
|
<el-radio-button :label="0">否</el-radio-button> |
||||
|
</el-radio-group> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="form.type.toString() === '1'" label="菜单缓存" prop="cache"> |
||||
|
<el-radio-group v-model="form.cache" size="mini"> |
||||
|
<el-radio-button :abel="1">是</el-radio-button> |
||||
|
<el-radio-button :label="0">否</el-radio-button> |
||||
|
</el-radio-group> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="form.type.toString() !== '2'" label="菜单可见" prop="hidden"> |
||||
|
<el-radio-group v-model="form.hidden" size="mini"> |
||||
|
<el-radio-button :label="0">是</el-radio-button> |
||||
|
<el-radio-button :label="1">否</el-radio-button> |
||||
|
</el-radio-group> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="form.type.toString() !== '2'" label="菜单标题" prop="name"> |
||||
|
<el-input v-model="form.name" :style=" form.type.toString() === '0' ? 'width: 450px' : 'width: 178px'" placeholder="菜单标题" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="form.type.toString() === '2'" label="按钮名称" prop="name"> |
||||
|
<el-input v-model="form.name" placeholder="按钮名称" style="width: 178px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="form.type.toString() !== '0'" label="权限标识" prop="permission"> |
||||
|
<el-input v-model="form.permission" :disabled="form.iframe" placeholder="权限标识" style="width: 178px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="form.type.toString() !== '0'" label="操作路由" prop="router"> |
||||
|
<el-input v-model="form.router" :disabled="form.iframe" placeholder="权限标识" style="width: 178px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="form.type.toString() !== '0'" label="路由动作" prop="routerMethod"> |
||||
|
<el-input v-model="form.routerMethod" :disabled="form.iframe" placeholder="权限标识" style="width: 178px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-if="form.type.toString() !== '2'" label="路由地址" prop="path"> |
||||
|
<el-input v-model="form.path" placeholder="路由地址" style="width: 178px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="菜单排序" prop="sort"> |
||||
|
<el-input-number v-model.number="form.sort" :min="0" :max="999" controls-position="right" style="width: 178px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="!form.iframe && form.type.toString() === '1'" label="组件名称" prop="componentName"> |
||||
|
<el-input v-model="form.componentName" style="width: 178px;" placeholder="匹配组件内Name字段" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-show="!form.iframe && form.type.toString() === '1'" label="组件路径" prop="component"> |
||||
|
<el-input v-model="form.component" style="width: 178px;" placeholder="组件路径" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="上级类目" prop="pid"> |
||||
|
<treeselect v-model="form.pid" :options="menus" :normalizer="normalizer" style="width: 450px;" placeholder="选择上级类目" /> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<el-button type="text" @click="crud.cancelCU">取消</el-button> |
||||
|
<el-button :loading="crud.cu === 2" type="primary" @click="crud.submitCU">确认</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
<!--表格渲染--> |
||||
|
<el-table ref="table" v-loading="crud.loading" :data="crud.data" :tree-props="{children: 'children', hasChildren: 'hasChildren'}" row-key="id" @select="crud.selectChange" @select-all="crud.selectAllChange" @selection-change="crud.selectionChangeHandler"> |
||||
|
<el-table-column type="selection" width="55" /> |
||||
|
<el-table-column v-if="columns.visible('name')" :show-overflow-tooltip="true" label="菜单名称" width="125px" prop="name" /> |
||||
|
<el-table-column v-if="columns.visible('icon')" prop="icon" label="图标" align="center" width="60px"> |
||||
|
<template slot-scope="scope"> |
||||
|
<svg-icon :icon-class="scope.row.icon" /> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('sort')" prop="sort" align="center" label="排序"> |
||||
|
<template slot-scope="scope"> |
||||
|
{{ scope.row.sort }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('path')" :show-overflow-tooltip="true" prop="path" label="路由地址" /> |
||||
|
<el-table-column v-if="columns.visible('permission')" :show-overflow-tooltip="true" prop="permission" label="权限标识" /> |
||||
|
<el-table-column v-if="columns.visible('component')" :show-overflow-tooltip="true" prop="component" label="组件路径" /> |
||||
|
<el-table-column v-if="columns.visible('iframe')" prop="iframe" label="外链" width="75px"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span v-if="scope.row.iframe">是</span> |
||||
|
<span v-else>否</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('cache')" prop="cache" label="缓存" width="75px"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span v-if="scope.row.cache">是</span> |
||||
|
<span v-else>否</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('hidden')" prop="hidden" label="可见" width="75px"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span v-if="scope.row.hidden">否</span> |
||||
|
<span v-else>是</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('createTime')" prop="createTime" label="创建日期" width="135px"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span>{{ parseTime(scope.row.createTime) }}</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-permission="['admin','menu:edit','menu:del']" label="操作" width="130px" align="center" fixed="right"> |
||||
|
<template slot-scope="scope"> |
||||
|
<udOperation |
||||
|
:data="scope.row" |
||||
|
:permission="permission" |
||||
|
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!" |
||||
|
/> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import crudMenu from '@/api/system/menu' |
||||
|
import IconSelect from '@/components/IconSelect' |
||||
|
import Treeselect from '@riophae/vue-treeselect' |
||||
|
import '@riophae/vue-treeselect/dist/vue-treeselect.css' |
||||
|
import CRUD, { presenter, header, form, crud } from '@crud/crud' |
||||
|
import rrOperation from '@crud/RR.operation' |
||||
|
import crudOperation from '@crud/CRUD.operation' |
||||
|
import udOperation from '@crud/UD.operation' |
||||
|
|
||||
|
// crud交由presenter持有 |
||||
|
const defaultCrud = CRUD({ title: '菜单', url: 'admin/menu', crudMethod: { ...crudMenu }}) |
||||
|
const defaultForm = { id: null, name: null, sort: 999, path: null, component: null, componentName: null, iframe: 0, roles: [], pid: 0, icon: null, cache: 0, hidden: 0, type: 0, permission: null,router: null,routerMethod:null } |
||||
|
export default { |
||||
|
name: 'Menu', |
||||
|
components: { Treeselect, IconSelect, crudOperation, rrOperation, udOperation }, |
||||
|
mixins: [presenter(defaultCrud), header(), form(defaultForm), crud()], |
||||
|
data() { |
||||
|
return { |
||||
|
normalizer(node){ |
||||
|
//去掉children=[]的children属性 respectively |
||||
|
if(node.children && !node.children.length){ |
||||
|
delete node.children; |
||||
|
} |
||||
|
}, |
||||
|
menus: [], |
||||
|
permission: { |
||||
|
add: ['admin', 'menu:add'], |
||||
|
edit: ['admin', 'menu:edit'], |
||||
|
del: ['admin', 'menu:del'] |
||||
|
}, |
||||
|
rules: { |
||||
|
name: [ |
||||
|
{ required: true, message: '请输入名称', trigger: 'blur' } |
||||
|
], |
||||
|
path: [ |
||||
|
{ required: true, message: '请输入地址', trigger: 'blur' } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 新增与编辑前做的操作 |
||||
|
[CRUD.HOOK.afterToCU](crud, form) { |
||||
|
crudMenu.getMenusTree().then(res => { |
||||
|
this.menus = [] |
||||
|
const menu = { id: 0, label: '顶级类目', children: [] } |
||||
|
menu.children = res.data |
||||
|
this.menus.push(menu) |
||||
|
}) |
||||
|
}, |
||||
|
// 选中图标 |
||||
|
selected(name) { |
||||
|
this.form.icon = name |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
/deep/ .el-input-number .el-input__inner { |
||||
|
text-align: left; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,279 @@ |
|||||
|
<template> |
||||
|
<div class="app-container"> |
||||
|
<!--工具栏--> |
||||
|
<div class="head-container"> |
||||
|
<div v-if="crud.props.searchToggle"> |
||||
|
<!-- 搜索 --> |
||||
|
<el-input v-model="query.blurry" size="small" clearable placeholder="输入名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> |
||||
|
<rrOperation :crud="crud" /> |
||||
|
</div> |
||||
|
<crudOperation :permission="permission" /> |
||||
|
</div> |
||||
|
<!-- 表单渲染 --> |
||||
|
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="520px"> |
||||
|
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px"> |
||||
|
<el-form-item label="角色名称" prop="name"> |
||||
|
<el-input v-model="form.name" style="width: 145px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="角色权限" prop="permission"> |
||||
|
<el-input v-model="form.permission" style="width: 145px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item v-if="form.dataScope === '自定义'" label="数据权限" prop="depts"> |
||||
|
<treeselect v-model="form.depts" :options="depts" multiple style="width: 380px" placeholder="请选择" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="描述信息" prop="remark"> |
||||
|
<el-input v-model="form.remark" style="width: 380px;" rows="5" type="textarea" /> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<el-button type="text" @click="crud.cancelCU">取消</el-button> |
||||
|
<el-button :loading="crud.cu === 2" type="primary" @click="crud.submitCU">确认</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
<el-row :gutter="15"> |
||||
|
<!--角色管理--> |
||||
|
<el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="17" style="margin-bottom: 10px"> |
||||
|
<el-card class="box-card" shadow="never"> |
||||
|
<div slot="header" class="clearfix"> |
||||
|
<span class="role-span">角色列表</span> |
||||
|
</div> |
||||
|
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" :data="crud.data" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange"> |
||||
|
<el-table-column :selectable="checkboxT" type="selection" width="55" /> |
||||
|
<el-table-column v-if="columns.visible('name')" prop="name" label="名称" /> |
||||
|
<el-table-column v-if="columns.visible('permission')" prop="permission" label="角色权限" /> |
||||
|
<el-table-column v-if="columns.visible('remark')" :show-overflow-tooltip="true" prop="remark" label="描述" /> |
||||
|
<el-table-column v-if="columns.visible('createTime')" :show-overflow-tooltip="true" width="135px" prop="createTime" label="创建日期"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span>{{ parseTime(scope.row.createTime) }}</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-permission="['admin','roles:edit','roles:del']" label="操作" width="130px" align="center" fixed="right"> |
||||
|
<template slot-scope="scope"> |
||||
|
<udOperation |
||||
|
:data="scope.row" |
||||
|
:permission="permission" |
||||
|
/> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
<!--分页组件--> |
||||
|
<pagination /> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<!-- 菜单授权 --> |
||||
|
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="7"> |
||||
|
<el-card class="box-card" shadow="never"> |
||||
|
<div slot="header" class="clearfix"> |
||||
|
<el-tooltip class="item" effect="dark" content="选择指定角色分配菜单" placement="top"> |
||||
|
<span class="role-span">菜单分配</span> |
||||
|
</el-tooltip> |
||||
|
<el-button |
||||
|
v-permission="['admin','roles:edit']" |
||||
|
:disabled="!showButton" |
||||
|
:loading="menuLoading" |
||||
|
icon="el-icon-check" |
||||
|
size="mini" |
||||
|
style="float: right; padding: 6px 9px" |
||||
|
type="primary" |
||||
|
@click="saveMenu" |
||||
|
>保存</el-button> |
||||
|
</div> |
||||
|
<el-tree |
||||
|
ref="menu" |
||||
|
:data="menus" |
||||
|
:default-checked-keys="menuIds" |
||||
|
:props="defaultProps" |
||||
|
show-checkbox |
||||
|
node-key="id" |
||||
|
/> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import crudRoles from '@/api/system/role' |
||||
|
import { getDepts } from '@/api/system/dept' |
||||
|
import { getMenusTree } from '@/api/system/menu' |
||||
|
import CRUD, { presenter, header, form, crud } from '@crud/crud' |
||||
|
import rrOperation from '@crud/RR.operation' |
||||
|
import crudOperation from '@crud/CRUD.operation' |
||||
|
import udOperation from '@crud/UD.operation' |
||||
|
import pagination from '@crud/Pagination' |
||||
|
import Treeselect from '@riophae/vue-treeselect' |
||||
|
import '@riophae/vue-treeselect/dist/vue-treeselect.css' |
||||
|
|
||||
|
// crud交由presenter持有 |
||||
|
const defaultCrud = CRUD({ title: '角色', url: 'admin/roles', sort: 'level,asc', crudMethod: { ...crudRoles }}) |
||||
|
const defaultForm = { id: null, name: null, depts: [], remark: null, dataScope: '全部', level: 3, permission: null } |
||||
|
export default { |
||||
|
name: 'Role', |
||||
|
components: { Treeselect, pagination, crudOperation, rrOperation, udOperation }, |
||||
|
mixins: [presenter(defaultCrud), header(), form(defaultForm), crud()], |
||||
|
data() { |
||||
|
return { |
||||
|
defaultProps: { children: 'children', label: 'label' }, |
||||
|
dateScopes: ['全部', '本级', '自定义'], level: 3, |
||||
|
currentId: 0, menuLoading: false, showButton: false, |
||||
|
menus: [], menuIds: [], depts: [], |
||||
|
permission: { |
||||
|
add: ['admin', 'roles:add'], |
||||
|
edit: ['admin', 'roles:edit'], |
||||
|
del: ['admin', 'roles:del'] |
||||
|
}, |
||||
|
rules: { |
||||
|
name: [ |
||||
|
{ required: true, message: '请输入名称', trigger: 'blur' } |
||||
|
], |
||||
|
permission: [ |
||||
|
{ required: true, message: '请输入权限', trigger: 'blur' } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
this.getMenus() |
||||
|
this.$nextTick(() => { |
||||
|
this.crud.toQuery() |
||||
|
}) |
||||
|
}, |
||||
|
methods: { |
||||
|
[CRUD.HOOK.afterRefresh]() { |
||||
|
this.$refs.menu.setCheckedKeys([]) |
||||
|
}, |
||||
|
// 编辑前 |
||||
|
[CRUD.HOOK.beforeToEdit](crud, form) { |
||||
|
if (form.dataScope === '自定义') { |
||||
|
this.getDepts() |
||||
|
} |
||||
|
const depts = [] |
||||
|
form.depts.forEach(function(dept, index) { |
||||
|
depts.push(dept.id) |
||||
|
}) |
||||
|
form.depts = depts |
||||
|
}, |
||||
|
// 提交前做的操作 |
||||
|
[CRUD.HOOK.afterValidateCU](crud) { |
||||
|
if (crud.form.dataScope === '自定义' && crud.form.depts.length === 0) { |
||||
|
this.$message({ |
||||
|
message: '自定义数据权限不能为空', |
||||
|
type: 'warning' |
||||
|
}) |
||||
|
return false |
||||
|
} else if (crud.form.dataScope === '自定义') { |
||||
|
const depts = [] |
||||
|
crud.form.depts.forEach(function(data, index) { |
||||
|
const dept = { id: data } |
||||
|
depts.push(dept) |
||||
|
}) |
||||
|
crud.form.depts = depts |
||||
|
} else { |
||||
|
crud.form.depts = [] |
||||
|
} |
||||
|
return true |
||||
|
}, |
||||
|
[CRUD.HOOK.afterAddError](crud) { |
||||
|
this.afterErrorMethod(crud) |
||||
|
}, |
||||
|
[CRUD.HOOK.afterEditError](crud) { |
||||
|
this.afterErrorMethod(crud) |
||||
|
}, |
||||
|
afterErrorMethod(crud) { |
||||
|
const depts = [] |
||||
|
crud.form.depts.forEach(function(dept, index) { |
||||
|
depts.push(dept.id) |
||||
|
}) |
||||
|
crud.form.depts = depts |
||||
|
}, |
||||
|
// 获取所有菜单 |
||||
|
getMenus() { |
||||
|
getMenusTree().then(res => { |
||||
|
this.menus = res.data |
||||
|
}) |
||||
|
}, |
||||
|
// 触发单选 |
||||
|
handleCurrentChange(val) { |
||||
|
if (val) { |
||||
|
const _this = this |
||||
|
// 清空菜单的选中 |
||||
|
this.$refs.menu.setCheckedKeys([]) |
||||
|
// 保存当前的角色id |
||||
|
this.currentId = val.id |
||||
|
this.showButton = val.level > 0 |
||||
|
// 初始化 |
||||
|
this.menuIds = [] |
||||
|
// 菜单数据需要特殊处理 |
||||
|
val.menus.forEach(function(data, index) { |
||||
|
if(data.pid!=0){ |
||||
|
_this.menuIds.push(data.id) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
// 保存菜单 |
||||
|
saveMenu() { |
||||
|
this.menuLoading = true |
||||
|
const role = { id: this.currentId, menus: [] } |
||||
|
// 得到半选的父节点数据,保存起来 |
||||
|
this.$refs.menu.getHalfCheckedNodes().forEach(function(data, index) { |
||||
|
const menu = { id: data.id } |
||||
|
role.menus.push(menu) |
||||
|
}) |
||||
|
// 得到已选中的 key 值 |
||||
|
this.$refs.menu.getCheckedKeys().forEach(function(data, index) { |
||||
|
const menu = { id: data } |
||||
|
role.menus.push(menu) |
||||
|
}) |
||||
|
crudRoles.editMenu(role).then(res => { |
||||
|
this.crud.notify('保存成功', CRUD.NOTIFICATION_TYPE.SUCCESS) |
||||
|
this.menuLoading = false |
||||
|
this.update() |
||||
|
}).catch(err => { |
||||
|
this.menuLoading = false |
||||
|
console.log(err.response.data.message) |
||||
|
}) |
||||
|
}, |
||||
|
// 改变数据 |
||||
|
update() { |
||||
|
// 无刷新更新 表格数据 |
||||
|
crudRoles.get(this.currentId).then(res => { |
||||
|
for (let i = 0; i < this.crud.data.length; i++) { |
||||
|
if (res.data.id === this.crud.data[i].id) { |
||||
|
this.crud.data[i] = res.data |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
// 获取部门数据 |
||||
|
getDepts() { |
||||
|
getDepts({ enabled: true }).then(res => { |
||||
|
this.depts = res.content |
||||
|
}) |
||||
|
}, |
||||
|
// 如果数据权限为自定义则获取部门数据 |
||||
|
changeScope() { |
||||
|
if (this.form.dataScope === '自定义') { |
||||
|
this.getDepts() |
||||
|
} |
||||
|
}, |
||||
|
checkboxT(row, rowIndex) { |
||||
|
return row.level >= this.level |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss"> |
||||
|
.role-span { |
||||
|
font-weight: bold;color: #303133; |
||||
|
font-size: 15px; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss" scoped> |
||||
|
/deep/ .el-input-number .el-input__inner { |
||||
|
text-align: left; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,218 @@ |
|||||
|
<template> |
||||
|
<div class="app-container"> |
||||
|
<el-row :gutter="20"> |
||||
|
<el-col :xs="24" :sm="24" :md="8" :lg="6" :xl="5" style="margin-bottom: 10px"> |
||||
|
<el-card class="box-card"> |
||||
|
<div slot="header" class="clearfix"> |
||||
|
<span>个人信息</span> |
||||
|
</div> |
||||
|
<div> |
||||
|
<div style="text-align: center"> |
||||
|
<el-upload |
||||
|
:show-file-list="false" |
||||
|
:on-success="handleSuccess" |
||||
|
:on-error="handleError" |
||||
|
|
||||
|
action="" |
||||
|
:http-request="upload" |
||||
|
class="avatar-uploader" |
||||
|
> |
||||
|
<img :src="user.avatar ? user.avatar : Avatar" title="点击上传头像" class="avatar"> |
||||
|
</el-upload> |
||||
|
</div> |
||||
|
<ul class="user-info"> |
||||
|
<li><div style="height: 100%"><svg-icon icon-class="login" /> 登录账号<div class="user-right">{{ user.username }}</div></div></li> |
||||
|
<li><svg-icon icon-class="user1" /> 用户昵称 <div class="user-right">{{ user.nickName }}</div></li> |
||||
|
<li><svg-icon icon-class="phone" /> 手机号码 <div class="user-right">{{ user.phone }}</div></li> |
||||
|
<li><svg-icon icon-class="email" /> 用户邮箱 <div class="user-right">{{ user.email }}</div></li> |
||||
|
<li> |
||||
|
<svg-icon icon-class="anq" /> 安全设置 |
||||
|
<div class="user-right"> |
||||
|
<a @click="$refs.pass.dialog = true">修改密码</a> |
||||
|
</div> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :xs="24" :sm="24" :md="16" :lg="18" :xl="19"> |
||||
|
<!-- 用户资料 --> |
||||
|
<el-card class="box-card"> |
||||
|
<el-tabs v-model="activeName" @tab-click="handleClick"> |
||||
|
<el-tab-pane label="用户资料" name="first"> |
||||
|
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 10px;" size="small" label-width="65px"> |
||||
|
<el-form-item label="昵称" prop="nickName"> |
||||
|
<el-input v-model="form.nickName" style="width: 35%" /> |
||||
|
<span style="color: #C0C0C0;margin-left: 10px;">用户昵称不作为登录使用</span> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="手机号" prop="phone"> |
||||
|
<el-input v-model="form.phone" style="width: 35%;" /> |
||||
|
<span style="color: #C0C0C0;margin-left: 10px;">手机号码不能重复</span> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="性别"> |
||||
|
<el-radio-group v-model="form.sex" style="width: 178px"> |
||||
|
<el-radio label="男">男</el-radio> |
||||
|
<el-radio label="女">女</el-radio> |
||||
|
</el-radio-group> |
||||
|
</el-form-item> |
||||
|
<el-form-item label=""> |
||||
|
<el-button :loading="saveLoading" size="mini" type="primary" @click="doSubmit">保存配置</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
</el-tab-pane> |
||||
|
|
||||
|
</el-tabs> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
<updatePass ref="pass" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapGetters } from 'vuex' |
||||
|
import updatePass from './center/updatePass' |
||||
|
import { getToken } from '@/utils/auth' |
||||
|
import store from '@/store' |
||||
|
import { isvalidPhone } from '@/utils/validate' |
||||
|
import { parseTime } from '@/utils/index' |
||||
|
import crud from '@/mixins/crud' |
||||
|
import { editUser, uploadAvatar } from '@/api/system/user' |
||||
|
import Avatar from '@/assets/images/avatar.png' |
||||
|
import axios from 'axios' |
||||
|
export default { |
||||
|
name: 'Center', |
||||
|
components: { updatePass }, |
||||
|
mixins: [crud], |
||||
|
data() { |
||||
|
// 自定义验证 |
||||
|
const validPhone = (rule, value, callback) => { |
||||
|
if (!value) { |
||||
|
callback(new Error('请输入电话号码')) |
||||
|
} else if (!isvalidPhone(value)) { |
||||
|
callback(new Error('请输入正确的11位手机号码')) |
||||
|
} else { |
||||
|
callback() |
||||
|
} |
||||
|
} |
||||
|
return { |
||||
|
Avatar: Avatar, |
||||
|
activeName: 'first', |
||||
|
saveLoading: false, |
||||
|
headers: { |
||||
|
'Authorization': "Bearer " + getToken() |
||||
|
}, |
||||
|
form: {}, |
||||
|
rules: { |
||||
|
nickName: [ |
||||
|
{ required: true, message: '请输入用户昵称', trigger: 'blur' }, |
||||
|
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' } |
||||
|
], |
||||
|
phone: [ |
||||
|
{ required: true, trigger: 'blur', validator: validPhone } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
'user', |
||||
|
'updateAvatarApi', |
||||
|
'baseApi' |
||||
|
]) |
||||
|
}, |
||||
|
created() { |
||||
|
this.form = { id: this.user.id, nickName: this.user.nickName, sex: this.user.sex, phone: this.user.phone } |
||||
|
store.dispatch('GetInfo').then(() => {}) |
||||
|
}, |
||||
|
methods: { |
||||
|
upload(param) { |
||||
|
const formData = new FormData() |
||||
|
formData.append('file', param.file) |
||||
|
uploadAvatar(formData).then(response => { |
||||
|
console.log(response.data) |
||||
|
if(response.status == 200) { |
||||
|
this.$notify({ |
||||
|
title: '头像修改成功', |
||||
|
type: 'success', |
||||
|
duration: 2500 |
||||
|
}) |
||||
|
store.dispatch('GetInfo').then(() => {}) |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
parseTime, |
||||
|
handleClick(tab, event) { |
||||
|
if (tab.name === 'second') { |
||||
|
this.init() |
||||
|
} |
||||
|
}, |
||||
|
handleSuccess(response, file, fileList) { |
||||
|
this.$notify({ |
||||
|
title: '头像修改成功', |
||||
|
type: 'success', |
||||
|
duration: 2500 |
||||
|
}) |
||||
|
store.dispatch('GetInfo').then(() => {}) |
||||
|
}, |
||||
|
// 监听上传失败 |
||||
|
handleError(e, file, fileList) { |
||||
|
const msg = JSON.parse(e.message) |
||||
|
this.$notify({ |
||||
|
title: msg.message, |
||||
|
type: 'error', |
||||
|
duration: 2500 |
||||
|
}) |
||||
|
}, |
||||
|
doSubmit() { |
||||
|
if (this.$refs['form']) { |
||||
|
this.$refs['form'].validate((valid) => { |
||||
|
if (valid) { |
||||
|
this.saveLoading = true |
||||
|
editUser(this.form).then(() => { |
||||
|
this.editSuccessNotify() |
||||
|
store.dispatch('GetInfo').then(() => {}) |
||||
|
this.saveLoading = false |
||||
|
}).catch(() => { |
||||
|
this.saveLoading = false |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style rel="stylesheet/scss" lang="scss"> |
||||
|
.avatar-uploader-icon { |
||||
|
font-size: 28px; |
||||
|
width: 120px; |
||||
|
height: 120px; |
||||
|
line-height: 120px; |
||||
|
text-align: center |
||||
|
} |
||||
|
|
||||
|
.avatar { |
||||
|
width: 120px; |
||||
|
height: 120px; |
||||
|
display: block; |
||||
|
border-radius: 50% |
||||
|
} |
||||
|
.user-info { |
||||
|
padding-left: 0; |
||||
|
list-style: none; |
||||
|
li{ |
||||
|
border-bottom: 1px solid #F0F3F4; |
||||
|
padding: 11px 0; |
||||
|
font-size: 13px; |
||||
|
} |
||||
|
.user-right { |
||||
|
float: right; |
||||
|
|
||||
|
a{ |
||||
|
color: #317EF3; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,95 @@ |
|||||
|
<template> |
||||
|
<div style="display: inline-block"> |
||||
|
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="500px" @close="cancel"> |
||||
|
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px"> |
||||
|
<el-form-item label="旧密码" prop="oldPass"> |
||||
|
<el-input v-model="form.oldPass" type="password" auto-complete="on" style="width: 370px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="新密码" prop="newPass"> |
||||
|
<el-input v-model="form.newPass" type="password" auto-complete="on" style="width: 370px;" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="确认密码" prop="confirmPass"> |
||||
|
<el-input v-model="form.confirmPass" type="password" auto-complete="on" style="width: 370px;" /> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<el-button type="text" @click="cancel">取消</el-button> |
||||
|
<el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import store from '@/store' |
||||
|
import { updatePass } from '@/api/system/user' |
||||
|
export default { |
||||
|
data() { |
||||
|
const confirmPass = (rule, value, callback) => { |
||||
|
if (value) { |
||||
|
if (this.form.newPass !== value) { |
||||
|
callback(new Error('两次输入的密码不一致')) |
||||
|
} else { |
||||
|
callback() |
||||
|
} |
||||
|
} else { |
||||
|
callback(new Error('请再次输入密码')) |
||||
|
} |
||||
|
} |
||||
|
return { |
||||
|
loading: false, dialog: false, title: '修改密码', form: { oldPass: '', newPass: '', confirmPass: '' }, |
||||
|
rules: { |
||||
|
oldPass: [ |
||||
|
{ required: true, message: '请输入旧密码', trigger: 'blur' } |
||||
|
], |
||||
|
newPass: [ |
||||
|
{ required: true, message: '请输入新密码', trigger: 'blur' }, |
||||
|
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' } |
||||
|
], |
||||
|
confirmPass: [ |
||||
|
{ required: true, validator: confirmPass, trigger: 'blur' } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
cancel() { |
||||
|
this.resetForm() |
||||
|
}, |
||||
|
doSubmit() { |
||||
|
this.$refs['form'].validate((valid) => { |
||||
|
if (valid) { |
||||
|
this.loading = true |
||||
|
updatePass(this.form).then(res => { |
||||
|
this.resetForm() |
||||
|
this.$notify({ |
||||
|
title: '密码修改成功,请重新登录', |
||||
|
type: 'success', |
||||
|
duration: 1500 |
||||
|
}) |
||||
|
setTimeout(() => { |
||||
|
store.dispatch('LogOut').then(() => { |
||||
|
location.reload() // 为了重新实例化vue-router对象 避免bug |
||||
|
}) |
||||
|
}, 1500) |
||||
|
}).catch(err => { |
||||
|
this.loading = false |
||||
|
console.log(err.response.data.message) |
||||
|
}) |
||||
|
} else { |
||||
|
return false |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
resetForm() { |
||||
|
this.dialog = false |
||||
|
this.$refs['form'].resetFields() |
||||
|
this.form = { oldPass: '', newPass: '', confirmPass: '' } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
</style> |
@ -0,0 +1,425 @@ |
|||||
|
<template> |
||||
|
<div class="app-container"> |
||||
|
<el-row :gutter="20"> |
||||
|
<!--侧边部门数据--> |
||||
|
<el-col :xs="9" :sm="6" :md="4" :lg="4" :xl="4"> |
||||
|
<div class="head-container"> |
||||
|
<el-input |
||||
|
v-model="deptName" |
||||
|
clearable |
||||
|
size="small" |
||||
|
placeholder="输入部门名称搜索" |
||||
|
prefix-icon="el-icon-search" |
||||
|
class="filter-item" |
||||
|
@input="getDeptDatas" |
||||
|
/> |
||||
|
</div> |
||||
|
<el-tree |
||||
|
:data="deptDatas" |
||||
|
:props="defaultProps" |
||||
|
:expand-on-click-node="false" |
||||
|
default-expand-all |
||||
|
@node-click="handleNodeClick" |
||||
|
/> |
||||
|
</el-col> |
||||
|
<!--用户数据--> |
||||
|
<el-col :xs="15" :sm="18" :md="20" :lg="20" :xl="20"> |
||||
|
<!--工具栏--> |
||||
|
<div class="head-container"> |
||||
|
<div v-if="crud.props.searchToggle"> |
||||
|
<!-- 搜索 --> |
||||
|
<el-input |
||||
|
v-model="query.blurry" |
||||
|
clearable |
||||
|
size="small" |
||||
|
placeholder="输入名称或者邮箱搜索" |
||||
|
style="width: 200px;" |
||||
|
class="filter-item" |
||||
|
@keyup.enter.native="crud.toQuery" |
||||
|
/> |
||||
|
<el-select |
||||
|
v-model="query.enabled" |
||||
|
clearable |
||||
|
size="small" |
||||
|
placeholder="状态" |
||||
|
class="filter-item" |
||||
|
style="width: 90px" |
||||
|
@change="crud.toQuery" |
||||
|
> |
||||
|
<el-option |
||||
|
v-for="item in enabledTypeOptions" |
||||
|
:key="item.key" |
||||
|
:label="item.display_name" |
||||
|
:value="item.key" |
||||
|
/> |
||||
|
</el-select> |
||||
|
<rrOperation :crud="crud" /> |
||||
|
</div> |
||||
|
<crudOperation show="" :permission="permission" /> |
||||
|
</div> |
||||
|
<!--表单渲染--> |
||||
|
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="570px"> |
||||
|
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="66px"> |
||||
|
<el-form-item label="用户名" prop="username"> |
||||
|
<el-input v-model="form.username" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="电话" prop="phone"> |
||||
|
<el-input v-model="form.phone" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="昵称" prop="nickName"> |
||||
|
<el-input v-model="form.nickName" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="邮箱" prop="email"> |
||||
|
<el-input v-model="form.email" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="部门" prop="dept.id"> |
||||
|
<treeselect |
||||
|
v-model="form.dept.id" |
||||
|
:options="depts" |
||||
|
:normalizer="normalizer" |
||||
|
style="width: 178px" |
||||
|
placeholder="选择部门" |
||||
|
@select="selectFun" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="岗位" prop="job.id"> |
||||
|
<el-select v-model="form.job.id" style="width: 178px" placeholder="请先选择岗位"> |
||||
|
<el-option |
||||
|
v-for="(item, index) in jobs" |
||||
|
:key="item.name + index" |
||||
|
:label="item.name" |
||||
|
:value="item.id" |
||||
|
/> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="性别"> |
||||
|
<el-radio-group v-model="form.sex" style="width: 178px"> |
||||
|
<el-radio label="男">男</el-radio> |
||||
|
<el-radio label="女">女</el-radio> |
||||
|
</el-radio-group> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="状态"> |
||||
|
<el-radio-group v-model="form.enabled" :disabled="form.id === user.id"> |
||||
|
<el-radio |
||||
|
v-for="item in dict.user_status" |
||||
|
:key="item.id" |
||||
|
:label="item.value" |
||||
|
>{{ item.label }}</el-radio> |
||||
|
</el-radio-group> |
||||
|
</el-form-item> |
||||
|
<el-form-item style="margin-bottom: 0;" label="角色" prop="roles"> |
||||
|
<el-select |
||||
|
v-model="form.roleIds" |
||||
|
style="width: 437px" |
||||
|
multiple |
||||
|
placeholder="请选择角色" |
||||
|
@remove-tag="deleteTag" |
||||
|
@change="changeRole" |
||||
|
> |
||||
|
<el-option |
||||
|
v-for="item in roles" |
||||
|
:key="item.name" |
||||
|
:label="item.name" |
||||
|
:value="item.id" |
||||
|
/> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<el-button type="text" @click="crud.cancelCU">取消</el-button> |
||||
|
<el-button :loading="crud.cu === 2" type="primary" @click="crud.submitCU">确认</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
<!--表格渲染--> |
||||
|
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler"> |
||||
|
<el-table-column :selectable="checkboxT" type="selection" width="55" /> |
||||
|
<el-table-column v-if="columns.visible('username')" :show-overflow-tooltip="true" prop="username" label="用户名" /> |
||||
|
<el-table-column v-if="columns.visible('nickName')" :show-overflow-tooltip="true" prop="nickName" label="昵称" /> |
||||
|
<el-table-column v-if="columns.visible('sex')" prop="sex" label="性别" /> |
||||
|
<el-table-column v-if="columns.visible('phone')" :show-overflow-tooltip="true" prop="phone" width="100" label="电话" /> |
||||
|
<el-table-column v-if="columns.visible('email')" :show-overflow-tooltip="true" width="125" prop="email" label="邮箱" /> |
||||
|
<el-table-column v-if="columns.visible('dept')" :show-overflow-tooltip="true" width="110" prop="dept" label="部门 / 岗位"> |
||||
|
<template slot-scope="scope"> |
||||
|
<div>{{ scope.row.dept.name }} / {{ scope.row.job.name }}</div> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('enabled')" label="状态" align="center" prop="enabled"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-switch |
||||
|
v-model="scope.row.enabled" |
||||
|
:disabled="user.id === scope.row.id" |
||||
|
active-color="#409EFF" |
||||
|
inactive-color="#F56C6C" |
||||
|
:active-value= "1" |
||||
|
:inactive-value= "0" |
||||
|
@change="changeEnabled(scope.row, scope.row.enabled)" |
||||
|
/> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column v-if="columns.visible('createTime')" :show-overflow-tooltip="true" prop="createTime" width="140" label="创建日期"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span>{{ parseTime(scope.row.createTime) }}</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column |
||||
|
v-permission="['admin','user:edit','user:del']" |
||||
|
label="操作" |
||||
|
width="125" |
||||
|
align="center" |
||||
|
fixed="right" |
||||
|
> |
||||
|
<template slot-scope="scope"> |
||||
|
<udOperation |
||||
|
:data="scope.row" |
||||
|
:permission="permission" |
||||
|
:disabled-dle="scope.row.id === user.id" |
||||
|
/> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
<!--分页组件--> |
||||
|
<pagination /> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import crudUser from '@/api/system/user' |
||||
|
import { isvalidPhone } from '@/utils/validate' |
||||
|
import { getDepts } from '@/api/system/dept' |
||||
|
import { getAll, getLevel } from '@/api/system/role' |
||||
|
import { getAllJob } from '@/api/system/job' |
||||
|
import CRUD, { presenter, header, form, crud } from '@crud/crud' |
||||
|
import rrOperation from '@crud/RR.operation' |
||||
|
import crudOperation from '@crud/CRUD.operation' |
||||
|
import udOperation from '@crud/UD.operation' |
||||
|
import pagination from '@crud/Pagination' |
||||
|
import Treeselect from '@riophae/vue-treeselect' |
||||
|
import { mapGetters } from 'vuex' |
||||
|
import '@riophae/vue-treeselect/dist/vue-treeselect.css' |
||||
|
|
||||
|
let userRoles = [] |
||||
|
// crud交由presenter持有 |
||||
|
const defaultCrud = CRUD({ title: '用户', url: 'admin/user', crudMethod: { ...crudUser }}) |
||||
|
const defaultForm = { id:null,username: null, nickName: null, sex: '男', email: null, |
||||
|
enabled: 1, roles: [], job: { id: null }, dept: { id: null }, phone: null,roleIds: [] } |
||||
|
export default { |
||||
|
name: 'User', |
||||
|
components: { Treeselect, crudOperation, rrOperation, udOperation, pagination }, |
||||
|
mixins: [presenter(defaultCrud), header(), form(defaultForm), crud()], |
||||
|
// 数据字典 |
||||
|
dicts: ['user_status'], |
||||
|
data() { |
||||
|
// 自定义验证 |
||||
|
const validPhone = (rule, value, callback) => { |
||||
|
if (!value) { |
||||
|
callback(new Error('请输入电话号码')) |
||||
|
} else if (!isvalidPhone(value)) { |
||||
|
callback(new Error('请输入正确的11位手机号码')) |
||||
|
} else { |
||||
|
callback() |
||||
|
} |
||||
|
} |
||||
|
return { |
||||
|
normalizer(node){ |
||||
|
//去掉children=[]的children属性 respectively |
||||
|
if(node.children && !node.children.length){ |
||||
|
delete node.children; |
||||
|
} |
||||
|
}, |
||||
|
height: document.documentElement.clientHeight - 180 + 'px;', |
||||
|
deptName: '', depts: [], deptDatas: [], jobs: [], level: 3, roles: [], |
||||
|
defaultProps: { children: 'children', label: 'name' }, |
||||
|
permission: { |
||||
|
add: ['admin', 'user:add'], |
||||
|
edit: ['admin', 'user:edit'], |
||||
|
del: ['admin', 'user:del'] |
||||
|
}, |
||||
|
enabledTypeOptions: [ |
||||
|
{ key: 1, display_name: '激活' }, |
||||
|
{ key: 0, display_name: '锁定' } |
||||
|
], |
||||
|
rules: { |
||||
|
username: [ |
||||
|
{ required: true, message: '请输入用户名', trigger: 'blur' }, |
||||
|
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' } |
||||
|
], |
||||
|
nickName: [ |
||||
|
{ required: true, message: '请输入用户昵称', trigger: 'blur' }, |
||||
|
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' } |
||||
|
], |
||||
|
email: [ |
||||
|
{ required: true, message: '请输入邮箱地址', trigger: 'blur' }, |
||||
|
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' } |
||||
|
], |
||||
|
roleIds: [ |
||||
|
{ required: true, message: '请选择角色', trigger: 'blur' }, |
||||
|
], |
||||
|
phone: [ |
||||
|
{ required: true, trigger: 'blur', validator: validPhone } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
'user' |
||||
|
]) |
||||
|
}, |
||||
|
created() { |
||||
|
this.$nextTick(() => { |
||||
|
this.getDeptDatas() |
||||
|
this.crud.toQuery() |
||||
|
this.crud.msg.add = '新增成功,默认密码:123456' |
||||
|
}) |
||||
|
}, |
||||
|
mounted: function() { |
||||
|
const that = this |
||||
|
window.onresize = function temp() { |
||||
|
that.height = document.documentElement.clientHeight - 180 + 'px;' |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
changeRole(value) { |
||||
|
userRoles = [] |
||||
|
value.forEach(function(data, index) { |
||||
|
const role = { id: data } |
||||
|
userRoles.push(role) |
||||
|
}) |
||||
|
}, |
||||
|
[CRUD.HOOK.afterAddError](crud) { |
||||
|
this.afterErrorMethod(crud) |
||||
|
}, |
||||
|
[CRUD.HOOK.afterEditError](crud) { |
||||
|
this.afterErrorMethod(crud) |
||||
|
}, |
||||
|
afterErrorMethod(crud) { |
||||
|
// 恢复select |
||||
|
const initRoles = [] |
||||
|
userRoles.forEach(function(role, index) { |
||||
|
initRoles.push(role.id) |
||||
|
}) |
||||
|
crud.form.roles = initRoles |
||||
|
}, |
||||
|
deleteTag(value) { |
||||
|
userRoles.forEach(function(data, index) { |
||||
|
if (data.id === value) { |
||||
|
userRoles.splice(index, value) |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
beforeInit() { |
||||
|
this.url = 'admin/user' |
||||
|
return true |
||||
|
}, |
||||
|
// 新增与编辑前做的操作 |
||||
|
[CRUD.HOOK.afterToCU](crud, form) { |
||||
|
this.getDepts() |
||||
|
this.getRoles() |
||||
|
this.getJobs(null) |
||||
|
}, |
||||
|
// 打开编辑弹窗前做的操作 |
||||
|
[CRUD.HOOK.beforeToEdit](crud, form) { |
||||
|
this.getJobs() |
||||
|
userRoles = [] |
||||
|
const roles = [] |
||||
|
form.roles.forEach(function(role, index) { |
||||
|
roles.push(role.id) |
||||
|
// 初始化编辑时候的角色 |
||||
|
const rol = { id: role.id } |
||||
|
userRoles.push(rol) |
||||
|
}) |
||||
|
form.roleIds = roles |
||||
|
}, |
||||
|
// 提交前做的操作 |
||||
|
[CRUD.HOOK.afterValidateCU](crud) { |
||||
|
if (!crud.form.dept.id) { |
||||
|
this.$message({ |
||||
|
message: '部门不能为空', |
||||
|
type: 'warning' |
||||
|
}) |
||||
|
return false |
||||
|
} else if (!crud.form.job.id) { |
||||
|
this.$message({ |
||||
|
message: '岗位不能为空', |
||||
|
type: 'warning' |
||||
|
}) |
||||
|
return false |
||||
|
} else if (this.roles.length === 0) { |
||||
|
this.$message({ |
||||
|
message: '角色不能为空', |
||||
|
type: 'warning' |
||||
|
}) |
||||
|
return false |
||||
|
} |
||||
|
crud.form.roles = userRoles |
||||
|
return true |
||||
|
}, |
||||
|
// 获取左侧部门数据 |
||||
|
getDeptDatas() { |
||||
|
const sort = 'id,desc' |
||||
|
const params = { sort: sort } |
||||
|
if (this.deptName) { params['name'] = this.deptName } |
||||
|
getDepts(params).then(res => { |
||||
|
this.deptDatas = res.data.content |
||||
|
}) |
||||
|
}, |
||||
|
// 获取弹窗内部门数据 |
||||
|
getDepts() { |
||||
|
getDepts({ enabled: 1 }).then(res => { |
||||
|
this.depts = res.data.content |
||||
|
}) |
||||
|
}, |
||||
|
// 切换部门 |
||||
|
handleNodeClick(data) { |
||||
|
if (data.pid === 0) { |
||||
|
this.query.deptId = null |
||||
|
} else { |
||||
|
this.query.deptId = data.id |
||||
|
} |
||||
|
this.crud.toQuery() |
||||
|
}, |
||||
|
// 改变状态 |
||||
|
changeEnabled(data, val) { |
||||
|
this.$confirm('此操作将 "' + this.dict.label.user_status[val] + '" ' + data.username + ', 是否继续?', '提示', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
crudUser.edit(data).then(res => { |
||||
|
this.crud.notify(this.dict.label.user_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS) |
||||
|
}).catch(() => { |
||||
|
data.enabled = !data.enabled |
||||
|
}) |
||||
|
}).catch(() => { |
||||
|
data.enabled = !data.enabled |
||||
|
}) |
||||
|
}, |
||||
|
// 获取弹窗内角色数据 |
||||
|
getRoles() { |
||||
|
getAll().then(res => { |
||||
|
this.roles = res.data.content |
||||
|
}).catch(() => { }) |
||||
|
}, |
||||
|
// 获取弹窗内岗位数据 |
||||
|
getJobs(id) { |
||||
|
getAllJob(id).then(res => { |
||||
|
this.jobs = res.data.content |
||||
|
}).catch(() => { }) |
||||
|
}, |
||||
|
// 点击部门搜索对应的岗位 |
||||
|
selectFun(node, instanceId) { |
||||
|
this.getJobs(node.id) |
||||
|
this.form.job.id = null |
||||
|
}, |
||||
|
|
||||
|
checkboxT(row, rowIndex) { |
||||
|
return row.id !== this.user.id |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
</style> |
@ -0,0 +1,16 @@ |
|||||
|
<template> |
||||
|
<elFrame :src="swaggerApi" /> |
||||
|
</template> |
||||
|
<script> |
||||
|
import { mapGetters } from 'vuex' |
||||
|
import elFrame from '@/components/Iframe/index' |
||||
|
export default { |
||||
|
name: 'Swagger', |
||||
|
components: { elFrame }, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
'swaggerApi' |
||||
|
]) |
||||
|
} |
||||
|
} |
||||
|
</script> |
Loading…
Reference in new issue