[新增功能](el-admin v2.5): v2.5 beta

详情 https://www.ydyno.com/archives/1225.html
This commit is contained in:
ZhengJie
2020-05-05 21:20:36 +08:00
parent a07ac18235
commit 3975e782e7
31 changed files with 687 additions and 482 deletions

View File

@@ -38,12 +38,13 @@
"clipboard": "2.0.4",
"codemirror": "^5.49.2",
"connect": "3.6.6",
"echarts": "4.2.1",
"echarts": "^4.2.1",
"echarts-gl": "^1.1.1",
"echarts-wordcloud": "^1.1.3",
"element-ui": "^2.12.0",
"file-saver": "1.3.8",
"fuse.js": "3.4.4",
"js-beautify": "^1.10.2",
"js-cookie": "2.2.0",
"jsencrypt": "^3.0.0-rc.1",
"jszip": "3.1.5",
@@ -53,18 +54,18 @@
"path-to-regexp": "2.4.0",
"qs": "^6.9.1",
"screenfull": "4.2.0",
"sortablejs": "1.8.4",
"vue": "2.6.10",
"vue-count-to": "1.0.13",
"vue-cropper": "0.4.9",
"vue-echarts": "^5.0.0-beta.0",
"vue-highlightjs": "^1.3.3",
"vue-router": "3.0.2",
"vue-splitpane": "1.0.4",
"vuedraggable": "2.20.0",
"vuex": "3.1.0",
"wangeditor": ">=3.0.0",
"xlsx": "^0.11.16",
"js-beautify": "^1.10.2",
"sortablejs": "1.8.4",
"vuedraggable": "2.20.0"
"xlsx": "^0.11.16"
},
"devDependencies": {
"@babel/core": "7.0.0",

View File

@@ -1,27 +0,0 @@
import request from '@/utils/request'
export function add(data) {
return request({
url: 'api/server',
method: 'post',
data
})
}
export function del(ids) {
return request({
url: 'api/server',
method: 'delete',
data: ids
})
}
export function edit(data) {
return request({
url: 'api/server',
method: 'put',
data
})
}
export default { add, edit, del }

View File

@@ -1,22 +0,0 @@
import request from '@/utils/request'
export function count() {
return request({
url: 'api/visits',
method: 'post'
})
}
export function get() {
return request({
url: 'api/visits',
method: 'get'
})
}
export function getChartData() {
return request({
url: 'api/visits/chartData',
method: 'get'
})
}

View File

@@ -2,9 +2,8 @@ import request from '@/utils/request'
export function resetEmail(data) {
return request({
url: 'api/code/resetEmail',
method: 'post',
data
url: 'api/code/resetEmail?email=' + data,
method: 'post'
})
}

View File

@@ -1,8 +1,7 @@
import request from '@/utils/request'
export function getAllJob(deptId) {
export function getAllJob() {
const params = {
deptId,
page: 0,
size: 9999
}

View File

@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.122 127.892v-28.68H7.513V87.274h46.609v-12.4H7.513v-12.86h38.003L.099 0h22.6l32.556 45.07c3.617 5.144 6.44 9.611 8.487 13.385 1.788-3.05 4.89-7.779 9.301-14.186L103.93 0h24.01L82.385 62.013h38.34v12.862h-46.41v12.4h46.41v11.937h-46.41v28.68H54.123z"/></svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M42.913 101.36c1.642 0 3.198.332 4.667.996a12.28 12.28 0 0 1 3.89 2.772c1.123 1.184 1.987 2.582 2.592 4.193.605 1.612.908 3.318.908 5.118 0 1.8-.303 3.507-.908 5.118-.605 1.611-1.469 3.01-2.593 4.194a13.3 13.3 0 0 1-3.889 2.843 10.582 10.582 0 0 1-4.667 1.066c-1.729 0-3.306-.355-4.732-1.066a13.604 13.604 0 0 1-3.825-2.843c-1.123-1.185-1.988-2.583-2.593-4.194a14.437 14.437 0 0 1-.907-5.118c0-1.8.302-3.506.907-5.118.605-1.61 1.47-3.009 2.593-4.193a12.515 12.515 0 0 1 3.825-2.772c1.426-.664 3.003-.996 4.732-.996zm53.932.285c1.643 0 3.22.331 4.733.995a11.386 11.386 0 0 1 3.889 2.772c1.08 1.185 1.945 2.583 2.593 4.194.648 1.61.972 3.317.972 5.118 0 1.8-.324 3.506-.972 5.117-.648 1.611-1.513 3.01-2.593 4.194a12.253 12.253 0 0 1-3.89 2.843 11 11 0 0 1-4.732 1.066 10.58 10.58 0 0 1-4.667-1.066 12.478 12.478 0 0 1-3.824-2.843c-1.08-1.185-1.945-2.583-2.593-4.194a13.581 13.581 0 0 1-.973-5.117c0-1.801.325-3.507.973-5.118.648-1.611 1.512-3.01 2.593-4.194a11.559 11.559 0 0 1 3.824-2.772 11.212 11.212 0 0 1 4.667-.995zm21.781-80.747c2.42 0 4.3.355 5.64 1.066 1.34.71 2.29 1.587 2.852 2.63a6.427 6.427 0 0 1 .778 3.34c-.044 1.185-.195 2.204-.454 3.057-.26.853-.8 2.606-1.62 5.26a589.268 589.268 0 0 1-2.788 8.743 1236.373 1236.373 0 0 0-3.047 9.453c-.994 3.128-1.75 5.592-2.269 7.393-1.123 3.79-2.55 6.42-4.278 7.89-1.728 1.469-3.846 2.203-6.352 2.203H39.023l1.945 12.795h65.342c4.148 0 6.223 1.943 6.223 5.828 0 1.896-.41 3.53-1.232 4.905-.821 1.374-2.442 2.061-4.862 2.061H38.505c-1.729 0-3.176-.426-4.343-1.28-1.167-.852-2.14-1.966-2.917-3.34a21.277 21.277 0 0 1-1.88-4.478 44.128 44.128 0 0 1-1.102-4.55c-.087-.568-.324-1.942-.713-4.122-.39-2.18-.865-4.904-1.426-8.174l-1.88-10.947c-.692-4.027-1.383-8.079-2.075-12.154-1.642-9.572-3.5-20.234-5.574-31.986H6.87c-1.296 0-2.377-.356-3.24-1.067a9.024 9.024 0 0 1-2.14-2.558 10.416 10.416 0 0 1-1.167-3.2C.108 8.53 0 7.488 0 6.54c0-1.896.583-3.46 1.75-4.69C2.917.615 4.494 0 6.482 0h13.095c1.728 0 3.111.284 4.148.853 1.037.569 1.858 1.28 2.463 2.132a8.548 8.548 0 0 1 1.297 2.701c.26.948.475 1.754.648 2.417.173.758.346 1.825.519 3.199.173 1.374.345 2.772.518 4.193.26 1.706.519 3.507.778 5.403h88.678z"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1588584086939" class="icon" viewBox="0 0 1084 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1156" xmlns:xlink="http://www.w3.org/1999/xlink" width="211.71875" height="200"><defs><style type="text/css"></style></defs><path d="M1080.096 434.5c-4.216-23.731-26.924-47.945-50.596-53.185l-17.648-4.096a175.94 175.94 0 0 1-101.613-80.832 177.807 177.807 0 0 1-18.732-129.802l5.541-16.685c7.108-23.13-2.108-54.992-20.6-70.833 0 0-16.624-14.095-63.244-41.2-46.8-26.984-66.858-34.513-66.858-34.513-22.768-8.373-54.632-0.362-71.256 17.407l-12.287 13.251a173.47 173.47 0 0 1-120.466 48.066 174.133 174.133 0 0 1-121.008-48.487l-11.745-12.83C393.14 2.992 361.096-4.899 338.268 3.354c0 0-20.359 7.529-67.1 34.513-46.8 27.346-63.244 41.44-63.244 41.44-18.431 15.661-27.647 47.223-20.54 70.593l5.06 16.866a178.048 178.048 0 0 1-18.672 129.62A174.916 174.916 0 0 1 71.496 377.46l-17.045 3.855c-23.31 5.421-46.26 29.334-50.596 53.186 0 0-3.855 21.382-3.855 75.712s3.855 75.713 3.855 75.713C8.07 609.9 30.779 633.872 54.45 639.112l16.624 3.855A174.254 174.254 0 0 1 173.47 724.28c23.31 40.838 28.911 87.338 18.732 129.802l-4.818 16.444c-7.108 23.129 2.108 54.992 20.6 70.833 0 0 16.623 14.095 63.244 41.2 46.8 27.105 66.918 34.513 66.918 34.513 22.708 8.373 54.632 0.362 71.256-17.407l11.625-12.589a175.097 175.097 0 0 1 242.257 0.12l11.624 12.65c16.384 17.708 48.428 25.599 71.256 17.347 0 0 20.359-7.53 67.16-34.514 46.74-27.105 63.124-41.2 63.124-41.2 18.491-15.6 27.707-47.463 20.6-70.833l-5.06-17.106A176.723 176.723 0 0 1 910.66 724.4a176.06 176.06 0 0 1 102.396-81.314l16.684-3.855c23.31-5.42 46.26-29.333 50.596-53.185 0 0 3.855-21.383 3.855-75.713-0.241-54.33-4.096-75.833-4.096-75.833z m-537.82 293.335c-119.26 0-216.175-97.336-216.175-217.622a216.658 216.658 0 0 1 216.236-217.32c119.2 0 216.115 97.276 216.115 217.561-0.24 120.045-96.974 217.32-216.175 217.32z" p-id="1157"></path></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -20,7 +20,7 @@
vertical-align: middle;
margin-bottom: 10px;
height: 30.5px !important;
width: 223px !important;
width: 230px !important;
}
}
.el-avatar {

View File

@@ -490,9 +490,6 @@ function CRUD(options) {
Vue.set(crud.props, name, value)
},
getDataId(data) {
if (!data.hasOwnProperty(this.idField)) {
console.error('[CRUD error]: no property [%s] in %o', this.idField, data)
}
return data[this.idField]
},
attchTable() {

View File

@@ -1,16 +0,0 @@
<template>
<div>
<svg-icon icon-class="download" @click="click" />
</div>
</template>
<script>
export default {
name: 'Github',
methods: {
click() {
window.open('https://github.com/elunez/eladmin', '_blank')
}
}
}
</script>

View File

@@ -0,0 +1,54 @@
<template>
<a href="https://github.com/elunez/eladmin" target="_blank" class="github-corner" aria-label="View source on Github">
<svg
width="80"
height="80"
viewBox="0 0 250 250"
style="fill:#40c9c6; color:#fff;"
aria-hidden="true"
>
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
<path
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor"
style="transform-origin: 130px 106px;"
class="octo-arm"
/>
<path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor"
class="octo-body"
/>
</svg>
</a>
</template>
<style scoped>
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out
}
@keyframes octocat-wave {
0%,
100% {
transform: rotate(0)
}
20%,
60% {
transform: rotate(-25deg)
}
40%,
80% {
transform: rotate(10deg)
}
}
@media (max-width:500px) {
.github-corner:hover .octo-arm {
animation: none
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out
}
}
</style>

View File

@@ -8,10 +8,6 @@
<template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" />
<el-tooltip content="源码下载" effect="dark" placement="bottom">
<Github class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip content="项目文档" effect="dark" placement="bottom">
<Doc class="right-menu-item hover-effect" />
</el-tooltip>
@@ -28,7 +24,7 @@
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
<div class="avatar-wrapper">
<img :src="user.avatar ? baseApi + '/avatar/' + user.avatar : Avatar" class="user-avatar">
<img :src="user.avatarName ? baseApi + '/avatar/' + user.avatarName : Avatar" class="user-avatar">
<i class="el-icon-caret-bottom" />
</div>
<el-dropdown-menu slot="dropdown">
@@ -57,7 +53,6 @@
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
import Github from '@/components/Github'
import Doc from '@/components/Doc'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
@@ -71,7 +66,6 @@ export default {
Screenfull,
SizeSelect,
Search,
Github,
Doc
},
data() {

View File

@@ -28,6 +28,7 @@ import router from './router/routers'
import './assets/icons' // icon
import './router/index' // permission control
import 'echarts-gl'
Vue.use(VueHighlightJS)
Vue.use(mavonEditor)

View File

@@ -5,10 +5,10 @@
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
import { getChartData } from '@/api/monitor/visits'
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
@@ -25,62 +25,46 @@ export default {
autoResize: {
type: Boolean,
default: true
},
chartData: {
type: Object,
required: true
}
},
data() {
return {
chart: null,
sidebarElm: null,
chartData: {
visitsData: [],
ipData: []
chart: null
}
},
weekDays: []
watch: {
chartData: {
deep: true,
handler(val) {
this.setOptions(val)
}
}
},
mounted() {
getChartData().then(res => {
this.chartData.visitsData = res.visitsData
this.chartData.ipData = res.ipData
this.weekDays = res.weekDays
this.$nextTick(() => {
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()
}
initChart() {
this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(this.chartData)
},
setOptions({ visitsData, ipData } = {}) {
setOptions({ expectedData, actualData } = {}) {
this.chart.setOption({
xAxis: {
data: this.weekDays,
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
boundaryGap: false,
axisTick: {
show: false
@@ -106,10 +90,10 @@ export default {
}
},
legend: {
data: ['pv', 'ip']
data: ['expected', 'actual']
},
series: [{
name: 'pv', itemStyle: {
name: 'expected', itemStyle: {
normal: {
color: '#FF005A',
lineStyle: {
@@ -120,12 +104,12 @@ export default {
},
smooth: true,
type: 'line',
data: visitsData,
data: expectedData,
animationDuration: 2800,
animationEasing: 'cubicInOut'
},
{
name: 'ip',
name: 'actual',
smooth: true,
type: 'line',
itemStyle: {
@@ -140,15 +124,11 @@ export default {
}
}
},
data: ipData,
data: actualData,
animationDuration: 2800,
animationEasing: 'quadraticOut'
}]
})
},
initChart() {
this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(this.chartData)
}
}
}

View File

@@ -1,46 +1,54 @@
<template>
<el-row :gutter="40" class="panel-group">
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel" @click="handleSetLineChartData('newVisitis')">
<div class="card-panel-icon-wrapper icon-people">
<svg-icon icon-class="visits" class-name="card-panel-icon" />
<svg-icon icon-class="peoples" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">日流量</div>
<count-to :start-val="0" :end-val="count.newVisits" :duration="2600" class="card-panel-num" />
<div class="card-panel-text">
New Visits
</div>
<count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel" @click="handleSetLineChartData('messages')">
<div class="card-panel-icon-wrapper icon-message">
<svg-icon icon-class="ipvisits" class-name="card-panel-icon" />
<svg-icon icon-class="message" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">日IP量</div>
<count-to :start-val="0" :end-val="count.newIp" :duration="3000" class="card-panel-num" />
<div class="card-panel-text">
Messages
</div>
<count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel" @click="handleSetLineChartData('purchases')">
<div class="card-panel-icon-wrapper icon-money">
<svg-icon icon-class="visits" class-name="card-panel-icon" />
<svg-icon icon-class="money" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">周流量</div>
<count-to :start-val="0" :end-val="count.recentVisits" :duration="3200" class="card-panel-num" />
<div class="card-panel-text">
Purchases
</div>
<count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel" @click="handleSetLineChartData('shoppings')">
<div class="card-panel-icon-wrapper icon-shopping">
<svg-icon icon-class="ipvisits" class-name="card-panel-icon" />
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">周IP量</div>
<count-to :start-val="0" :end-val="count.recentIp" :duration="3600" class="card-panel-num" />
<div class="card-panel-text">
Shoppings
</div>
<count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" />
</div>
</div>
</el-col>
@@ -49,35 +57,30 @@
<script>
import CountTo from 'vue-count-to'
import { get } from '@/api/monitor/visits'
export default {
components: {
CountTo
},
data() {
return {
count: { newIp: 0, newVisits: 0, recentIp: 0, recentVisits: 0 }
methods: {
handleSetLineChartData(type) {
this.$emit('handleSetLineChartData', type)
}
},
mounted() {
get().then(res => {
this.count.newIp = res.newIp
this.count.newVisits = res.newVisits
this.count.recentIp = res.recentIp
this.count.recentVisits = res.recentVisits
})
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.panel-group {
margin-top: 18px;
.card-panel-col{
.card-panel-col {
margin-bottom: 32px;
}
.card-panel {
height: 108px;
cursor: pointer;
font-size: 12px;
position: relative;
overflow: hidden;
@@ -85,18 +88,45 @@ export default {
background: #fff;
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
border-color: rgba(0, 0, 0, .05);
&:hover {
.card-panel-icon-wrapper {
color: #fff;
}
.icon-people {
background: #40c9c6;
}
.icon-message {
background: #36a3f7;
}
.icon-money {
background: #f4516c;
}
.icon-shopping {
background: #34bfa3
}
}
.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;
@@ -104,25 +134,48 @@ export default {
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;
}
}
}
</style>

View File

@@ -0,0 +1,55 @@
import { debounce } from '@/utils'
export default {
data() {
return {
$_sidebarElm: null,
$_resizeHandler: null
}
},
mounted() {
this.$_resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
this.$_initResizeEvent()
this.$_initSidebarResizeEvent()
},
beforeDestroy() {
this.$_destroyResizeEvent()
this.$_destroySidebarResizeEvent()
},
// to fixed bug when cached by keep-alive
// https://github.com/PanJiaChen/vue-element-admin/issues/2116
activated() {
this.$_initResizeEvent()
this.$_initSidebarResizeEvent()
},
deactivated() {
this.$_destroyResizeEvent()
this.$_destroySidebarResizeEvent()
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_initResizeEvent() {
window.addEventListener('resize', this.$_resizeHandler)
},
$_destroyResizeEvent() {
window.removeEventListener('resize', this.$_resizeHandler)
},
$_sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.$_resizeHandler()
}
},
$_initSidebarResizeEvent() {
this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
},
$_destroySidebarResizeEvent() {
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
}
}
}

View File

@@ -1,9 +1,12 @@
<template>
<div class="dashboard-container">
<div class="dashboard-editor-container">
<panel-group />
<github-corner class="github-corner" />
<panel-group @handleSetLineChartData="handleSetLineChartData" />
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
<line-chart />
<line-chart :chart-data="lineChartData" />
</el-row>
<el-row :gutter="32">
<el-col :xs="24" :sm="24" :lg="8">
@@ -27,39 +30,78 @@
</template>
<script>
import GithubCorner from '@/components/GithubCorner'
import PanelGroup from './dashboard/PanelGroup'
import LineChart from './dashboard/LineChart'
import RadarChart from '@/components/Echarts/RadarChart'
import PieChart from '@/components/Echarts/PieChart'
import BarChart from '@/components/Echarts/BarChart'
import { count } from '@/api/monitor/visits'
/**
* 记录访问,只有页面刷新或者第一次加载才会记录
*/
count().then(res => {
})
const lineChartData = {
newVisitis: {
expectedData: [100, 120, 161, 134, 105, 160, 165],
actualData: [120, 82, 91, 154, 162, 140, 145]
},
messages: {
expectedData: [200, 192, 120, 144, 160, 130, 140],
actualData: [180, 160, 151, 106, 145, 150, 130]
},
purchases: {
expectedData: [80, 100, 121, 104, 105, 90, 100],
actualData: [120, 90, 100, 138, 142, 130, 130]
},
shoppings: {
expectedData: [130, 140, 141, 142, 145, 150, 160],
actualData: [120, 82, 91, 154, 162, 140, 130]
}
}
export default {
name: 'Dashboard',
components: {
GithubCorner,
PanelGroup,
LineChart,
RadarChart,
PieChart,
BarChart
},
data() {
return {
lineChartData: lineChartData.newVisitis
}
},
methods: {
handleSetLineChartData(type) {
this.lineChartData = lineChartData[type]
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.dashboard-editor-container {
padding: 18px 22px 22px 22px;
padding: 32px;
background-color: rgb(240, 242, 245);
position: relative;
.github-corner {
position: absolute;
top: 0;
border: 0;
right: 0;
}
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
@media (max-width:1024px) {
.chart-wrapper {
padding: 8px;
}
}
</style>

View File

@@ -25,7 +25,7 @@
<el-table-column type="selection" width="55" />
<el-table-column prop="userName" label="用户名" />
<el-table-column prop="nickName" label="用户昵称" />
<el-table-column prop="job" label="岗位" />
<el-table-column prop="dept" label="部门" />
<el-table-column prop="ip" label="登录IP" />
<el-table-column :show-overflow-tooltip="true" prop="address" label="登录地点" />
<el-table-column prop="browser" label="浏览器" />
@@ -34,7 +34,7 @@
<span>{{ parseTime(scope.row.loginTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100px" fixed="right">
<el-table-column label="操作" width="70px" fixed="right">
<template slot-scope="scope">
<el-popover
:ref="scope.$index"

View File

@@ -1,194 +1,281 @@
<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="crud.toQuery" />
<rrOperation />
<div v-loading="!show" element-loading-text="数据加载中..." :style="!show ? 'height: 500px' : 'height: 100%'" class="app-container">
<div v-if="show">
<el-card class="box-card">
<div style="color: #666;font-size: 13px;">
<svg-icon icon-class="system" style="margin-right: 5px" />
<span>
系统{{ data.sys.os }}
</span>
<span>
IP{{ data.sys.ip }}
</span>
<span>
项目已不间断运行{{ data.sys.day }}
</span>
<i class="el-icon-refresh" style="margin-left: 40px" @click="init" />
</div>
<crudOperation :permission="permission" />
</el-card>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span style="font-weight: bold;color: #666;font-size: 15px">状态</span>
</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" :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: 370px;" />
</el-form-item>
<el-form-item label="地址" prop="address">
<el-input v-model="form.address" style="width: 370px;" />
</el-form-item>
<el-form-item label="端口" prop="port">
<el-input-number v-model.number="form.port" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model.number="form.sort" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
<div>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">CPU使用率</div>
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px;">
{{ data.cpu.name }}
</div>
</el-dialog>
<!--表格渲染-->
<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 prop="state" label="状态" width="50px">
<template slot-scope="scope">
<el-tag
:type="scope.row.state === '1' ? 'success' : 'info'"
disable-transitions
>
<i v-if="scope.row.state === '1'" class="el-icon-success" />
<i v-if="scope.row.state === '0'" class="el-icon-error" />
</el-tag>
</template>
</el-table-column>
<el-table-column prop="name" label="名称" />
<el-table-column prop="address" label="地址" />
<el-table-column prop="port" label="端口" width="80px" align="center" />
<el-table-column :formatter="formatCpuRate" prop="cpuRate" label="CPU使用率" width="100px" align="center" />
<el-table-column prop="cpuCore" label="CPU内核数" width="100px" align="center" />
<el-table-column prop="memTotal" label="物理内存" align="center">
<template slot-scope="scope">
<el-row>
<el-col :span="24">{{ formatMem(scope.row) }}</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-progress :percentage="percentNumber(scope.row.memUsed,scope.row.memTotal)" :status="percentStatus(scope.row.memUsed,scope.row.memTotal)" :show-text="false" />
<div style="padding: 3px">
{{ data.cpu.package }}
</div>
<div style="padding: 3px">
{{ data.cpu.core }}
</div>
<div style="padding: 3px">
{{ data.cpu.logic }}
</div>
</div>
<div class="content">
<el-progress type="circle" :percentage="parseFloat(data.cpu.used)" />
</div>
</el-tooltip>
<div class="footer">{{ data.cpu.coreNumber }} 核心</div>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">内存使用率</div>
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px;">
总量{{ data.memory.total }}
</div>
<div style="padding: 3px">
已使用{{ data.memory.used }}
</div>
<div style="padding: 3px">
空闲{{ data.memory.available }}
</div>
</div>
<div class="content">
<el-progress type="circle" :percentage="parseFloat(data.memory.usageRate)" />
</div>
</el-tooltip>
<div class="footer">{{ data.memory.used }} / {{ data.memory.total }}</div>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">交换区使用率</div>
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px;">
总量{{ data.swap.total }}
</div>
<div style="padding: 3px">
已使用{{ data.swap.used }}
</div>
<div style="padding: 3px">
空闲{{ data.swap.available }}
</div>
</div>
<div class="content">
<el-progress type="circle" :percentage="parseFloat(data.swap.usageRate)" />
</div>
</el-tooltip>
<div class="footer">{{ data.swap.used }} / {{ data.swap.total }}</div>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">磁盘使用率</div>
<div class="content">
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px;">
读取{{ data.disk.reads }}
</div>
<div style="padding: 3px">
写入{{ data.disk.writes }}
</div>
<div style="padding: 3px">
空闲{{ data.disk.available }}
</div>
</div>
<div class="content">
<el-progress type="circle" :percentage="parseFloat(data.disk.usageRate)" />
</div>
</el-tooltip>
</div>
<div class="footer">{{ data.disk.used }} / {{ data.disk.total }}</div>
</el-col>
</div>
</el-card>
<div>
<el-row :gutter="6">
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span style="font-weight: bold;color: #666;font-size: 15px">CPU使用率监控</span>
</div>
<div>
<v-chart :options="cpuInfo" />
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span style="font-weight: bold;color: #666;font-size: 15px">内存使用率监控</span>
</div>
<div>
<v-chart :options="memoryInfo" />
</div>
</el-card>
</el-col>
</el-row>
</template>
</el-table-column>
<el-table-column prop="diskTotal" :formatter="formatDisk" label="磁盘使用情况" align="center">
<template slot-scope="scope">
<el-row>
<el-col :span="24">{{ formatDisk(scope.row) }}</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-progress :percentage="percentNumber(scope.row.diskUsed,scope.row.diskTotal)" :status="percentStatus(scope.row.diskUsed,scope.row.diskTotal)" :show-text="false" />
</el-col>
</el-row>
</template>
</el-table-column>
<el-table-column prop="swapTotal" label="交换空间" align="center">
<template slot-scope="scope">
<el-row>
<el-col :span="24">{{ formatSwap(scope.row) }}</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-progress :percentage="percentNumber(scope.row.swapUsed,scope.row.swapTotal)" :status="percentStatus(scope.row.swapUsed,scope.row.swapTotal)" :show-text="false" />
</el-col>
</el-row>
</template>
</el-table-column>
<el-table-column v-permission="['admin','server:edit','server:del']" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</div>
</template>
<script>
import crudServer from '@/api/monitor/server'
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'
const defaultForm = { id: null, address: 'localhost', name: null, ip: null, port: 8777, state: null, cpuRate: null, cpuCore: null, memTotal: null, memUsed: null, diskTotal: null, diskUsed: null, swapTotal: null, swapUsed: null, sort: 999 }
import ECharts from 'vue-echarts'
import 'echarts/lib/chart/line'
import 'echarts/lib/component/polar'
import { initData } from '@/api/data'
export default {
name: 'ServerMonitor',
components: { pagination, crudOperation, rrOperation, udOperation },
cruds() {
return CRUD({ title: '监控', url: 'api/server', sort: 'sort,asc', crudMethod: { ...crudServer }})
components: {
'v-chart': ECharts
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
permission: {
add: ['admin', 'server:add'],
edit: ['admin', 'server:edit'],
del: ['admin', 'server:del']
show: false,
monitor: null,
url: 'api/monitor',
data: {},
cpuInfo: {
tooltip: {
trigger: 'axis'
},
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
address: [
{ required: true, message: '请输入IP', trigger: 'blur' }
],
port: [
{ required: true, message: '请输入访问端口', trigger: 'blur', type: 'number' }
]
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value',
min: 0,
max: 100,
interval: 20
},
series: [{
data: [],
type: 'line',
areaStyle: {
normal: {
color: 'rgb(32, 160, 255)' // 改变区域颜色
}
},
itemStyle: {
normal: {
color: '#6fbae1',
lineStyle: {
color: '#6fbae1' // 改变折线颜色
}
}
}
}]
},
memoryInfo: {
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value',
min: 0,
max: 100,
interval: 20
},
series: [{
data: [],
type: 'line',
areaStyle: {
normal: {
color: 'rgb(32, 160, 255)' // 改变区域颜色
}
},
itemStyle: {
normal: {
color: '#6fbae1',
lineStyle: {
color: '#6fbae1' // 改变折线颜色
}
}
}
}]
}
}
},
created() {
this.crud.optShow.download = false
this.init()
this.monitor = window.setInterval(() => {
setTimeout(() => {
this.init()
}, 2)
}, 3500)
},
destroyed() {
clearInterval(this.monitor)
},
methods: {
formatCpuRate(row, column) {
const value = row.cpuRate
if (!value) {
return 0
}
return (Math.floor(value * 10000) / 100) + '%'
},
percentNumber(value, total) {
if (!value || !total) {
return 0
}
return value / total * 100
},
percentStatus(value, total) {
const percent = this.percentNumber(value, total)
if (percent < 60) {
return 'success'
} else if (percent < 90) {
return 'warning'
} else {
return 'exception'
}
},
convertToGb(num) {
if (!num) {
return '-'
}
let unit = 'G'
if (num > 1024) {
num = num / 1024
unit = 'T'
}
num = Math.floor(num * 100) / 100
return num + unit
},
formatMem(row, column) {
return this.convertToGb(row.memUsed) + ' / ' + this.convertToGb(row.memTotal)
},
formatDisk(row, column) {
return this.convertToGb(row.diskUsed) + ' / ' + this.convertToGb(row.diskTotal)
},
formatSwap(row, column) {
return this.convertToGb(row.swapUsed) + ' / ' + this.convertToGb(row.swapTotal)
init() {
initData(this.url, {}).then(data => {
this.data = data
this.show = true
this.cpuInfo.xAxis.data.push(data.time)
this.memoryInfo.xAxis.data.push(data.time)
this.cpuInfo.series[0].data.push(parseFloat(data.memory.used))
this.memoryInfo.series[0].data.push(parseFloat(data.memory.usageRate))
})
}
}
}
</script>
<style scoped>
.el-col {
<style rel="stylesheet/scss" lang="scss" scoped>
/deep/ .box-card {
margin-bottom: 5px;
span {
margin-right: 28px;
}
.el-icon-refresh {
margin-right: 10px;
float: right;
cursor:pointer;
}
}
.cpu, .memory, .swap, .disk {
width: 20%;
float: left;
padding-bottom: 20px;
margin-right: 5%;
}
.title, .footer {
text-align: center;
font-size: 15px;
font-weight: 500;
color: #999;
height: 25px;
line-height: 25px;
}
.content {
text-align: center;
margin-top: 5px;
margin-bottom: 5px;
}
</style>

View File

@@ -21,8 +21,8 @@
<el-form-item label="字典值" prop="value">
<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 label="排序" prop="dictSort">
<el-input-number v-model.number="form.dictSort" :min="0" :max="999" controls-position="right" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -37,7 +37,7 @@
</el-table-column>
<el-table-column prop="label" label="字典标签" />
<el-table-column prop="value" label="字典值" />
<el-table-column prop="sort" label="排序" />
<el-table-column prop="dictSort" label="排序" />
<el-table-column v-permission="['admin','dict:edit','dict:del']" label="操作" width="130px" align="center" fixed="right">
<template slot-scope="scope">
<udOperation
@@ -61,13 +61,13 @@ import pagination from '@crud/Pagination'
import rrOperation from '@crud/RR.operation'
import udOperation from '@crud/UD.operation'
const defaultForm = { id: null, label: null, value: null, sort: 999 }
const defaultForm = { id: null, label: null, value: null, dictSort: 999 }
export default {
components: { pagination, rrOperation, udOperation },
cruds() {
return [
CRUD({ title: '字典详情', url: 'api/dictDetail', query: { dictName: '' }, sort: ['sort,asc', 'id,desc'],
CRUD({ title: '字典详情', url: 'api/dictDetail', query: { dictName: '' }, sort: ['dictSort,asc', 'id,desc'],
crudMethod: { ...crudDictDetail },
optShow: { add: true,
edit: true,
@@ -94,7 +94,7 @@ export default {
value: [
{ required: true, message: '请输入字典值', trigger: 'blur' }
],
sort: [
dictSort: [
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
]
},

View File

@@ -7,7 +7,7 @@
<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-input v-model="form.description" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -32,7 +32,7 @@
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
<el-table-column type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" prop="name" label="名称" />
<el-table-column :show-overflow-tooltip="true" prop="remark" label="描述" />
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
<el-table-column v-permission="['admin','dict:edit','dict:del']" label="操作" width="130px" align="center" fixed="right">
<template slot-scope="scope">
<udOperation
@@ -79,7 +79,7 @@ import pagination from '@crud/Pagination'
import rrOperation from '@crud/RR.operation'
import udOperation from '@crud/UD.operation'
const defaultForm = { id: null, name: null, remark: null }
const defaultForm = { id: null, name: null, description: null }
export default {
name: 'Dict',
@@ -94,7 +94,7 @@ export default {
return {
queryTypeOptions: [
{ key: 'name', display_name: '字典名称' },
{ key: 'remark', display_name: '描述' }
{ key: 'description', display_name: '描述' }
],
rules: {
name: [

View File

@@ -9,14 +9,9 @@
<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 prop="name" label="名称" />
<el-table-column prop="dept" label="所属部门">
<el-table-column prop="jobSort" label="排序">
<template slot-scope="scope">
<div>{{ scope.row.deptSuperiorName ? scope.row.deptSuperiorName + ' / ' : '' }}{{ scope.row.dept.name }}</div>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序">
<template slot-scope="scope">
{{ scope.row.sort }}
{{ scope.row.jobSort }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" align="center">
@@ -73,7 +68,7 @@ export default {
return CRUD({
title: '岗位',
url: 'api/job',
sort: ['sort,asc', 'id,desc'],
sort: ['jobSort,asc', 'id,desc'],
crudMethod: { ...crudJob }
})
},
@@ -98,9 +93,9 @@ export default {
type: 'warning'
}).then(() => {
// eslint-disable-next-line no-undef
crud.crudMethod.edit(data).then(() => {
crudJob.edit(data).then(() => {
// eslint-disable-next-line no-undef
crud.notify(this.dict.label.job_status[val] + '成功', 'success')
this.crud.notify(this.dict.label.job_status[val] + '成功', 'success')
}).catch(err => {
data.enabled = !data.enabled
console.log(err.data.message)

View File

@@ -25,10 +25,10 @@
</el-form-item>
<el-form-item
label="排序"
prop="sort"
prop="jobSort"
>
<el-input-number
v-model.number="form.sort"
v-model.number="form.jobSort"
:min="0"
:max="999"
controls-position="right"
@@ -49,18 +49,6 @@
{{ 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"
@@ -84,22 +72,15 @@
</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'
import { form } from '@crud/crud'
const defaultForm = {
id: null,
name: '',
sort: 999,
enabled: true,
dept: {
id: 1
}
jobSort: 999,
enabled: true
}
export default {
components: { Treeselect },
mixins: [form(defaultForm)],
props: {
jobStatus: {
@@ -109,35 +90,15 @@ export default {
},
data() {
return {
depts: [],
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
sort: [
jobSort: [
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
],
dept: { required: true, message: '所属部门不能为空', trigger: 'select' }
]
}
}
},
methods: {
[CRUD.HOOK.beforeToCU]() {
getDepts({ enabled: true }).then(res => {
this.depts = res.content
})
},
// 提交前的验证
[CRUD.HOOK.afterValidateCU]() {
if (!this.form.dept.id) {
this.$notify({
title: '所属部门不能为空',
type: 'warning'
})
return false
}
return true
}
}
}
</script>

View File

@@ -63,7 +63,7 @@
</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-input v-model="form.title" :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;" />
@@ -71,11 +71,11 @@
<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-if="form.type.toString() !== '2'" label="路由地址" prop="path">
<el-input v-model="form.path" placeholder="路由地址" style="width: 178px;" />
<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 label="菜单排序" prop="menuSort">
<el-input-number v-model.number="form.menuSort" :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字段" />
@@ -95,15 +95,15 @@
<!--表格渲染-->
<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 :show-overflow-tooltip="true" label="菜单名称" width="125px" prop="name" />
<el-table-column :show-overflow-tooltip="true" label="菜单标题" width="125px" prop="title" />
<el-table-column prop="icon" label="图标" align="center" width="60px">
<template slot-scope="scope">
<svg-icon :icon-class="scope.row.icon ? scope.row.icon : ''" />
</template>
</el-table-column>
<el-table-column prop="sort" align="center" label="排序">
<el-table-column prop="menuSort" align="center" label="排序">
<template slot-scope="scope">
{{ scope.row.sort }}
{{ scope.row.menuSort }}
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="path" label="路由地址" />
@@ -156,7 +156,7 @@ import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
// crud交由presenter持有
const defaultForm = { id: null, name: null, sort: 999, path: null, component: null, componentName: null, iframe: false, roles: [], pid: 0, icon: null, cache: false, hidden: false, type: 0, permission: null }
const defaultForm = { id: null, title: null, menuSort: 999, path: null, component: null, componentName: null, iframe: false, roles: [], pid: 0, icon: null, cache: false, hidden: false, type: 0, permission: null }
export default {
name: 'Menu',
components: { Treeselect, IconSelect, crudOperation, rrOperation, udOperation },
@@ -173,8 +173,8 @@ export default {
del: ['admin', 'menu:del']
},
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
title: [
{ required: true, message: '请输入标题', trigger: 'blur' }
],
path: [
{ required: true, message: '请输入地址', trigger: 'blur' }

View File

@@ -24,13 +24,13 @@
<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-input v-model="form.name" style="width: 380px;" />
</el-form-item>
<el-form-item label="角色权限" prop="permission">
<el-input v-model="form.permission" style="width: 145px;" />
<el-form-item label="角色级别" prop="level">
<el-input-number v-model.number="form.level" :min="1" controls-position="right" style="width: 145px;" />
</el-form-item>
<el-form-item label="数据范围" prop="dataScope">
<el-select v-model="form.dataScope" style="width: 145px" placeholder="请选择数据范围" @change="changeScope">
<el-select v-model="form.dataScope" style="width: 140px" placeholder="请选择数据范围" @change="changeScope">
<el-option
v-for="item in dateScopes"
:key="item"
@@ -39,14 +39,11 @@
/>
</el-select>
</el-form-item>
<el-form-item label="角色级别" prop="level">
<el-input-number v-model.number="form.level" :min="1" controls-position="right" 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 label="描述信息" prop="description">
<el-input v-model="form.description" style="width: 380px;" rows="5" type="textarea" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -65,9 +62,8 @@
<el-table-column :selectable="checkboxT" type="selection" width="55" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="dataScope" label="数据权限" />
<el-table-column prop="permission" label="角色权限" />
<el-table-column prop="level" label="角色级别" />
<el-table-column :show-overflow-tooltip="true" prop="remark" label="描述" />
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
<el-table-column :show-overflow-tooltip="true" width="135px" prop="createTime" label="创建日期">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
@@ -133,7 +129,7 @@ import pagination from '@crud/Pagination'
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
const defaultForm = { id: null, name: null, depts: [], remark: null, dataScope: '全部', level: 3, permission: null }
const defaultForm = { id: null, name: null, depts: [], description: null, dataScope: '全部', level: 3 }
export default {
name: 'Role',
components: { Treeselect, pagination, crudOperation, rrOperation, udOperation },
@@ -167,9 +163,6 @@ export default {
crudRoles.getLevel().then(data => {
this.level = data.level
})
this.$nextTick(() => {
this.crud.toQuery()
})
},
methods: {
[CRUD.HOOK.afterRefresh]() {

View File

@@ -32,29 +32,46 @@
<Log ref="log" />
</div>
<!--Form表单-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" append-to-body width="600px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="100px">
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" append-to-body width="730px">
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="100px">
<el-form-item label="任务名称" prop="jobName">
<el-input v-model="form.jobName" style="width: 460px;" />
<el-input v-model="form.jobName" style="width: 220px;" />
</el-form-item>
<el-form-item label="任务描述" prop="description">
<el-input v-model="form.description" style="width: 220px;" />
</el-form-item>
<el-form-item label="Bean名称" prop="beanName">
<el-input v-model="form.beanName" style="width: 460px;" />
<el-input v-model="form.beanName" style="width: 220px;" />
</el-form-item>
<el-form-item label="执行方法" prop="methodName">
<el-input v-model="form.methodName" style="width: 460px;" />
</el-form-item>
<el-form-item label="参数内容">
<el-input v-model="form.params" style="width: 460px;" />
<el-input v-model="form.methodName" style="width: 220px;" />
</el-form-item>
<el-form-item label="Cron表达式" prop="cronExpression">
<el-input v-model="form.cronExpression" style="width: 460px;" />
<el-input v-model="form.cronExpression" style="width: 220px;" />
</el-form-item>
<el-form-item label="子任务ID">
<el-input v-model="form.subTask" placeholder="多个用逗号隔开按顺序执行" style="width: 220px;" />
</el-form-item>
<el-form-item label="任务负责人" prop="personInCharge">
<el-input v-model="form.personInCharge" style="width: 220px;" />
</el-form-item>
<el-form-item label="告警邮箱" prop="email">
<el-input v-model="form.email" placeholder="多个邮箱用逗号隔开" style="width: 220px;" />
</el-form-item>
<el-form-item label="失败后暂停">
<el-radio-group v-model="form.pauseAfterFailure" style="width: 220px">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="任务状态">
<el-radio v-model="form.isPause" :label="false">启用</el-radio>
<el-radio v-model="form.isPause" :label="true">暂停</el-radio>
<el-radio-group v-model="form.isPause" style="width: 220px">
<el-radio :label="false">启用</el-radio>
<el-radio :label="true">暂停</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="任务描述">
<el-input v-model="form.remark" style="width: 460px;" rows="2" type="textarea" />
<el-form-item label="参数内容">
<el-input v-model="form.params" style="width: 556px;" rows="4" type="textarea" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -65,23 +82,24 @@
<!--表格渲染-->
<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 :show-overflow-tooltip="true" prop="jobName" width="100px" label="任务名称" />
<el-table-column :show-overflow-tooltip="true" prop="id" label="任务ID" />
<el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
<el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
<el-table-column :show-overflow-tooltip="true" prop="methodName" width="90px" label="执行方法" />
<el-table-column :show-overflow-tooltip="true" prop="params" width="80px" label="参数" />
<el-table-column :show-overflow-tooltip="true" prop="cronExpression" width="100px" label="cron表达式" />
<el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
<el-table-column :show-overflow-tooltip="true" prop="params" label="参数" />
<el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
<el-table-column :show-overflow-tooltip="true" prop="isPause" width="90px" label="状态">
<template slot-scope="scope">
<el-tag :type="scope.row.isPause ? 'warning' : 'success'">{{ scope.row.isPause ? '已暂停' : '运行中' }}</el-tag>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="remark" label="描述" />
<el-table-column :show-overflow-tooltip="true" prop="createTime" label="创建日期">
<el-table-column :show-overflow-tooltip="true" prop="description" width="150px" label="描述" />
<el-table-column :show-overflow-tooltip="true" prop="createTime" width="136px" label="创建日期">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column v-permission="['admin','timing:edit','timing:del']" label="操作" width="180px" align="center" fixed="right">
<el-table-column v-permission="['admin','timing:edit','timing:del']" label="操作" width="170px" align="center" fixed="right">
<template slot-scope="scope">
<el-button v-permission="['admin','timing:edit']" size="mini" style="margin-right: 3px;" type="text" @click="crud.toEdit(scope.row)">编辑</el-button>
<el-button v-permission="['admin','timing:edit']" style="margin-left: -2px" type="text" size="mini" @click="execute(scope.row.id)">执行</el-button>
@@ -117,7 +135,7 @@ import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, jobName: null, beanName: null, methodName: null, params: null, cronExpression: null, isPause: false, remark: null }
const defaultForm = { id: null, jobName: null, subTask: null, beanName: null, methodName: null, params: null, cronExpression: null, pauseAfterFailure: true, isPause: false, personInCharge: null, email: null, description: null }
export default {
name: 'Timing',
components: { Log, pagination, crudOperation, rrOperation },
@@ -137,6 +155,9 @@ export default {
jobName: [
{ required: true, message: '请输入任务名称', trigger: 'blur' }
],
description: [
{ required: true, message: '请输入任务描述', trigger: 'blur' }
],
beanName: [
{ required: true, message: '请输入Bean名称', trigger: 'blur' }
],
@@ -145,6 +166,9 @@ export default {
],
cronExpression: [
{ required: true, message: '请输入Cron表达式', trigger: 'blur' }
],
personInCharge: [
{ required: true, message: '请输入负责人名称', trigger: 'blur' }
]
}
}
@@ -160,6 +184,9 @@ export default {
},
// 改变状态
updateStatus(id, status) {
if (status === '恢复') {
this.updateParams(id)
}
crudJob.updateIsPause(id).then(res => {
this.crud.toQuery()
this.crud.notify(status + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
@@ -167,6 +194,9 @@ export default {
console.log(err.response.data.message)
})
},
updateParams(id) {
console.log(id)
},
delMethod(id) {
this.delLoading = true
crudJob.del([id]).then(() => {

View File

@@ -16,15 +16,15 @@
:action="updateAvatarApi"
class="avatar-uploader"
>
<img :src="user.avatar ? baseApi + '/avatar/' + user.avatar : Avatar" title="点击上传头像" class="avatar">
<img :src="user.avatarName ? baseApi + '/avatar/' + user.avatarName : 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="dept" /> 所属部门 <div class="user-right"> {{ user.dept.name }}</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="dept" /> 所属部门 <div class="user-right"> {{ user.dept.name }} / {{ user.job.name }}</div></li>
<li>
<svg-icon icon-class="anq" /> 安全设置
<div class="user-right">
@@ -51,7 +51,7 @@
<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-group v-model="form.gender" style="width: 178px">
<el-radio label="男"></el-radio>
<el-radio label="女"></el-radio>
</el-radio-group>
@@ -158,7 +158,7 @@ export default {
])
},
created() {
this.form = { id: this.user.id, nickName: this.user.nickName, sex: this.user.sex, phone: this.user.phone }
this.form = { id: this.user.id, nickName: this.user.nickName, gender: this.user.gender, phone: this.user.phone }
store.dispatch('GetInfo').then(() => {})
},
methods: {

View File

@@ -48,7 +48,6 @@ export default {
return {
loading: false, dialog: false, title: '修改邮箱', form: { pass: '', email: '', code: '' },
user: { email: '', password: '' }, codeLoading: false,
codeData: { type: 'email', value: '' },
buttonName: '获取验证码', isDisabled: false, time: 60,
rules: {
pass: [
@@ -71,9 +70,8 @@ export default {
if (this.form.email && this.form.email !== this.email) {
this.codeLoading = true
this.buttonName = '验证码发送中'
this.codeData.value = this.form.email
const _this = this
resetEmail(this.codeData).then(res => {
resetEmail(this.form.email).then(res => {
this.$message({
showClose: true,
message: '发送成功验证码有效期5分钟',

View File

@@ -89,21 +89,27 @@
:options="depts"
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-form-item label="岗位" prop="jobs">
<el-select
v-model="form.jobs"
style="width: 178px"
multiple
placeholder="请选择"
@remove-tag="deleteTag"
@change="changeJob"
>
<el-option
v-for="(item, index) in jobs"
:key="item.name + index"
v-for="item in jobs"
:key="item.name"
: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-group v-model="form.gender" style="width: 178px">
<el-radio label="">男</el-radio>
<el-radio label="">女</el-radio>
</el-radio-group>
@@ -146,12 +152,12 @@
<el-table-column :selectable="checkboxT" type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" prop="username" label="用户名" />
<el-table-column :show-overflow-tooltip="true" prop="nickName" label="昵称" />
<el-table-column prop="sex" label="性别" />
<el-table-column prop="gender" label="性别" />
<el-table-column :show-overflow-tooltip="true" prop="phone" width="100" label="电话" />
<el-table-column :show-overflow-tooltip="true" width="125" prop="email" label="邮箱" />
<el-table-column :show-overflow-tooltip="true" width="110" prop="dept" label="部门 / 岗位">
<el-table-column :show-overflow-tooltip="true" width="135" prop="email" label="邮箱" />
<el-table-column :show-overflow-tooltip="true" prop="dept" label="部门">
<template slot-scope="scope">
<div>{{ scope.row.dept.name }} / {{ scope.row.job.name }}</div>
<div>{{ scope.row.dept.name }}</div>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="enabled">
@@ -209,7 +215,8 @@ import { mapGetters } from 'vuex'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
let userRoles = []
const defaultForm = { id: null, username: null, nickName: null, sex: '男', email: null, enabled: 'false', roles: [], job: { id: null }, dept: { id: null }, phone: null }
let userJobs = []
const defaultForm = { id: null, username: null, nickName: null, gender: '男', email: null, enabled: 'false', roles: [], jobs: [], dept: { id: null }, phone: null }
export default {
name: 'User',
components: { Treeselect, crudOperation, rrOperation, udOperation, pagination },
@@ -270,7 +277,6 @@ export default {
created() {
this.$nextTick(() => {
this.getDeptDatas()
this.crud.toQuery()
this.crud.msg.add = '新增成功默认密码123456'
})
},
@@ -288,6 +294,13 @@ export default {
userRoles.push(role)
})
},
changeJob(value) {
userJobs = []
value.forEach(function(data, index) {
const job = { id: data }
userJobs.push(job)
})
},
[CRUD.HOOK.afterAddError](crud) {
this.afterErrorMethod(crud)
},
@@ -297,10 +310,15 @@ export default {
afterErrorMethod(crud) {
// 恢复select
const initRoles = []
const initJobs = []
userRoles.forEach(function(role, index) {
initRoles.push(role.id)
})
userJobs.forEach(function(job, index) {
initJobs.push(job.id)
})
crud.form.roles = initRoles
crud.form.jobs = initJobs
},
deleteTag(value) {
userRoles.forEach(function(data, index) {
@@ -314,20 +332,30 @@ export default {
this.getDepts()
this.getRoles()
this.getRoleLevel()
this.getJobs()
form.enabled = form.enabled.toString()
},
// 打开编辑弹窗前做的操作
[CRUD.HOOK.beforeToEdit](crud, form) {
this.getJobs(this.form.dept.id)
userRoles = []
userJobs = []
const roles = []
const jobs = []
form.roles.forEach(function(role, index) {
roles.push(role.id)
// 初始化编辑时候的角色
const rol = { id: role.id }
userRoles.push(rol)
})
form.jobs.forEach(function(job, index) {
jobs.push(job.id)
// 初始化编辑时候的岗位
const data = { id: job.id }
userJobs.push(data)
})
form.roles = roles
form.jobs = jobs
},
// 提交前做的操作
[CRUD.HOOK.afterValidateCU](crud) {
@@ -337,13 +365,13 @@ export default {
type: 'warning'
})
return false
} else if (!crud.form.job.id) {
} else if (crud.form.jobs.length === 0) {
this.$message({
message: '岗位不能为空',
type: 'warning'
})
return false
} else if (this.roles.length === 0) {
} else if (crud.form.roles.length === 0) {
this.$message({
message: '角色不能为空',
type: 'warning'
@@ -351,6 +379,7 @@ export default {
return false
}
crud.form.roles = userRoles
crud.form.jobs = userJobs
return true
},
// 获取左侧部门数据
@@ -400,16 +429,11 @@ export default {
}).catch(() => { })
},
// 获取弹窗内岗位数据
getJobs(id) {
getAllJob(id).then(res => {
getJobs() {
getAllJob().then(res => {
this.jobs = res.content
}).catch(() => { })
},
// 点击部门搜索对应的岗位
selectFun(node, instanceId) {
this.getJobs(node.id)
this.form.job.id = null
},
// 获取权限级别
getRoleLevel() {
getLevel().then(res => {

View File

@@ -127,5 +127,9 @@ module.exports = {
config.optimization.runtimeChunk('single')
}
)
}
},
transpileDependencies: [
'vue-echarts',
'resize-detector'
]
}