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