Commit be0092176455ad2f3963ca24b0ec1b876304bb96
1 parent
090611a7
处理,通知公告,任务中心
Showing
44 changed files
with
3934 additions
and
117 deletions
src/components/Charts/Keyboard.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div :id="id" :class="className" :style="{height:height,width:width}" /> | |
| 3 | +</template> | |
| 4 | + | |
| 5 | +<script> | |
| 6 | +import echarts from 'echarts' | |
| 7 | +import resize from './mixins/resize' | |
| 8 | + | |
| 9 | +export default { | |
| 10 | + mixins: [resize], | |
| 11 | + props: { | |
| 12 | + className: { | |
| 13 | + type: String, | |
| 14 | + default: 'chart' | |
| 15 | + }, | |
| 16 | + id: { | |
| 17 | + type: String, | |
| 18 | + default: 'chart' | |
| 19 | + }, | |
| 20 | + width: { | |
| 21 | + type: String, | |
| 22 | + default: '200px' | |
| 23 | + }, | |
| 24 | + height: { | |
| 25 | + type: String, | |
| 26 | + default: '200px' | |
| 27 | + } | |
| 28 | + }, | |
| 29 | + data() { | |
| 30 | + return { | |
| 31 | + chart: null | |
| 32 | + } | |
| 33 | + }, | |
| 34 | + mounted() { | |
| 35 | + this.initChart() | |
| 36 | + }, | |
| 37 | + beforeDestroy() { | |
| 38 | + if (!this.chart) { | |
| 39 | + return | |
| 40 | + } | |
| 41 | + this.chart.dispose() | |
| 42 | + this.chart = null | |
| 43 | + }, | |
| 44 | + methods: { | |
| 45 | + initChart() { | |
| 46 | + this.chart = echarts.init(document.getElementById(this.id)) | |
| 47 | + | |
| 48 | + const xAxisData = [] | |
| 49 | + const data = [] | |
| 50 | + const data2 = [] | |
| 51 | + for (let i = 0; i < 50; i++) { | |
| 52 | + xAxisData.push(i) | |
| 53 | + data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5) | |
| 54 | + data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3) | |
| 55 | + } | |
| 56 | + this.chart.setOption({ | |
| 57 | + backgroundColor: '#08263a', | |
| 58 | + grid: { | |
| 59 | + left: '5%', | |
| 60 | + right: '5%' | |
| 61 | + }, | |
| 62 | + xAxis: [{ | |
| 63 | + show: false, | |
| 64 | + data: xAxisData | |
| 65 | + }, { | |
| 66 | + show: false, | |
| 67 | + data: xAxisData | |
| 68 | + }], | |
| 69 | + visualMap: { | |
| 70 | + show: false, | |
| 71 | + min: 0, | |
| 72 | + max: 50, | |
| 73 | + dimension: 0, | |
| 74 | + inRange: { | |
| 75 | + color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055'] | |
| 76 | + } | |
| 77 | + }, | |
| 78 | + yAxis: { | |
| 79 | + axisLine: { | |
| 80 | + show: false | |
| 81 | + }, | |
| 82 | + axisLabel: { | |
| 83 | + textStyle: { | |
| 84 | + color: '#4a657a' | |
| 85 | + } | |
| 86 | + }, | |
| 87 | + splitLine: { | |
| 88 | + show: true, | |
| 89 | + lineStyle: { | |
| 90 | + color: '#08263f' | |
| 91 | + } | |
| 92 | + }, | |
| 93 | + axisTick: { | |
| 94 | + show: false | |
| 95 | + } | |
| 96 | + }, | |
| 97 | + series: [{ | |
| 98 | + name: 'back', | |
| 99 | + type: 'bar', | |
| 100 | + data: data2, | |
| 101 | + z: 1, | |
| 102 | + itemStyle: { | |
| 103 | + normal: { | |
| 104 | + opacity: 0.4, | |
| 105 | + barBorderRadius: 5, | |
| 106 | + shadowBlur: 3, | |
| 107 | + shadowColor: '#111' | |
| 108 | + } | |
| 109 | + } | |
| 110 | + }, { | |
| 111 | + name: 'Simulate Shadow', | |
| 112 | + type: 'line', | |
| 113 | + data, | |
| 114 | + z: 2, | |
| 115 | + showSymbol: false, | |
| 116 | + animationDelay: 0, | |
| 117 | + animationEasing: 'linear', | |
| 118 | + animationDuration: 1200, | |
| 119 | + lineStyle: { | |
| 120 | + normal: { | |
| 121 | + color: 'transparent' | |
| 122 | + } | |
| 123 | + }, | |
| 124 | + areaStyle: { | |
| 125 | + normal: { | |
| 126 | + color: '#08263a', | |
| 127 | + shadowBlur: 50, | |
| 128 | + shadowColor: '#000' | |
| 129 | + } | |
| 130 | + } | |
| 131 | + }, { | |
| 132 | + name: 'front', | |
| 133 | + type: 'bar', | |
| 134 | + data, | |
| 135 | + xAxisIndex: 1, | |
| 136 | + z: 3, | |
| 137 | + itemStyle: { | |
| 138 | + normal: { | |
| 139 | + barBorderRadius: 5 | |
| 140 | + } | |
| 141 | + } | |
| 142 | + }], | |
| 143 | + animationEasing: 'elasticOut', | |
| 144 | + animationEasingUpdate: 'elasticOut', | |
| 145 | + animationDelay(idx) { | |
| 146 | + return idx * 20 | |
| 147 | + }, | |
| 148 | + animationDelayUpdate(idx) { | |
| 149 | + return idx * 20 | |
| 150 | + } | |
| 151 | + }) | |
| 152 | + } | |
| 153 | + } | |
| 154 | +} | |
| 155 | +</script> | ... | ... |
src/components/Charts/LineMarker.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div :id="id" :class="className" :style="{height:height,width:width}" /> | |
| 3 | +</template> | |
| 4 | + | |
| 5 | +<script> | |
| 6 | +import echarts from 'echarts' | |
| 7 | +import resize from './mixins/resize' | |
| 8 | + | |
| 9 | +export default { | |
| 10 | + mixins: [resize], | |
| 11 | + props: { | |
| 12 | + className: { | |
| 13 | + type: String, | |
| 14 | + default: 'chart' | |
| 15 | + }, | |
| 16 | + id: { | |
| 17 | + type: String, | |
| 18 | + default: 'chart' | |
| 19 | + }, | |
| 20 | + width: { | |
| 21 | + type: String, | |
| 22 | + default: '200px' | |
| 23 | + }, | |
| 24 | + height: { | |
| 25 | + type: String, | |
| 26 | + default: '200px' | |
| 27 | + } | |
| 28 | + }, | |
| 29 | + data() { | |
| 30 | + return { | |
| 31 | + chart: null | |
| 32 | + } | |
| 33 | + }, | |
| 34 | + mounted() { | |
| 35 | + this.initChart() | |
| 36 | + }, | |
| 37 | + beforeDestroy() { | |
| 38 | + if (!this.chart) { | |
| 39 | + return | |
| 40 | + } | |
| 41 | + this.chart.dispose() | |
| 42 | + this.chart = null | |
| 43 | + }, | |
| 44 | + methods: { | |
| 45 | + initChart() { | |
| 46 | + this.chart = echarts.init(document.getElementById(this.id)) | |
| 47 | + | |
| 48 | + this.chart.setOption({ | |
| 49 | + backgroundColor: '#394056', | |
| 50 | + title: { | |
| 51 | + top: 20, | |
| 52 | + text: 'Requests', | |
| 53 | + textStyle: { | |
| 54 | + fontWeight: 'normal', | |
| 55 | + fontSize: 16, | |
| 56 | + color: '#F1F1F3' | |
| 57 | + }, | |
| 58 | + left: '1%' | |
| 59 | + }, | |
| 60 | + tooltip: { | |
| 61 | + trigger: 'axis', | |
| 62 | + axisPointer: { | |
| 63 | + lineStyle: { | |
| 64 | + color: '#57617B' | |
| 65 | + } | |
| 66 | + } | |
| 67 | + }, | |
| 68 | + legend: { | |
| 69 | + top: 20, | |
| 70 | + icon: 'rect', | |
| 71 | + itemWidth: 14, | |
| 72 | + itemHeight: 5, | |
| 73 | + itemGap: 13, | |
| 74 | + data: ['CMCC', 'CTCC', 'CUCC'], | |
| 75 | + right: '4%', | |
| 76 | + textStyle: { | |
| 77 | + fontSize: 12, | |
| 78 | + color: '#F1F1F3' | |
| 79 | + } | |
| 80 | + }, | |
| 81 | + grid: { | |
| 82 | + top: 100, | |
| 83 | + left: '2%', | |
| 84 | + right: '2%', | |
| 85 | + bottom: '2%', | |
| 86 | + containLabel: true | |
| 87 | + }, | |
| 88 | + xAxis: [{ | |
| 89 | + type: 'category', | |
| 90 | + boundaryGap: false, | |
| 91 | + axisLine: { | |
| 92 | + lineStyle: { | |
| 93 | + color: '#57617B' | |
| 94 | + } | |
| 95 | + }, | |
| 96 | + data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55'] | |
| 97 | + }], | |
| 98 | + yAxis: [{ | |
| 99 | + type: 'value', | |
| 100 | + name: '(%)', | |
| 101 | + axisTick: { | |
| 102 | + show: false | |
| 103 | + }, | |
| 104 | + axisLine: { | |
| 105 | + lineStyle: { | |
| 106 | + color: '#57617B' | |
| 107 | + } | |
| 108 | + }, | |
| 109 | + axisLabel: { | |
| 110 | + margin: 10, | |
| 111 | + textStyle: { | |
| 112 | + fontSize: 14 | |
| 113 | + } | |
| 114 | + }, | |
| 115 | + splitLine: { | |
| 116 | + lineStyle: { | |
| 117 | + color: '#57617B' | |
| 118 | + } | |
| 119 | + } | |
| 120 | + }], | |
| 121 | + series: [{ | |
| 122 | + name: 'CMCC', | |
| 123 | + type: 'line', | |
| 124 | + smooth: true, | |
| 125 | + symbol: 'circle', | |
| 126 | + symbolSize: 5, | |
| 127 | + showSymbol: false, | |
| 128 | + lineStyle: { | |
| 129 | + normal: { | |
| 130 | + width: 1 | |
| 131 | + } | |
| 132 | + }, | |
| 133 | + areaStyle: { | |
| 134 | + normal: { | |
| 135 | + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ | |
| 136 | + offset: 0, | |
| 137 | + color: 'rgba(137, 189, 27, 0.3)' | |
| 138 | + }, { | |
| 139 | + offset: 0.8, | |
| 140 | + color: 'rgba(137, 189, 27, 0)' | |
| 141 | + }], false), | |
| 142 | + shadowColor: 'rgba(0, 0, 0, 0.1)', | |
| 143 | + shadowBlur: 10 | |
| 144 | + } | |
| 145 | + }, | |
| 146 | + itemStyle: { | |
| 147 | + normal: { | |
| 148 | + color: 'rgb(137,189,27)', | |
| 149 | + borderColor: 'rgba(137,189,2,0.27)', | |
| 150 | + borderWidth: 12 | |
| 151 | + | |
| 152 | + } | |
| 153 | + }, | |
| 154 | + data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122] | |
| 155 | + }, { | |
| 156 | + name: 'CTCC', | |
| 157 | + type: 'line', | |
| 158 | + smooth: true, | |
| 159 | + symbol: 'circle', | |
| 160 | + symbolSize: 5, | |
| 161 | + showSymbol: false, | |
| 162 | + lineStyle: { | |
| 163 | + normal: { | |
| 164 | + width: 1 | |
| 165 | + } | |
| 166 | + }, | |
| 167 | + areaStyle: { | |
| 168 | + normal: { | |
| 169 | + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ | |
| 170 | + offset: 0, | |
| 171 | + color: 'rgba(0, 136, 212, 0.3)' | |
| 172 | + }, { | |
| 173 | + offset: 0.8, | |
| 174 | + color: 'rgba(0, 136, 212, 0)' | |
| 175 | + }], false), | |
| 176 | + shadowColor: 'rgba(0, 0, 0, 0.1)', | |
| 177 | + shadowBlur: 10 | |
| 178 | + } | |
| 179 | + }, | |
| 180 | + itemStyle: { | |
| 181 | + normal: { | |
| 182 | + color: 'rgb(0,136,212)', | |
| 183 | + borderColor: 'rgba(0,136,212,0.2)', | |
| 184 | + borderWidth: 12 | |
| 185 | + | |
| 186 | + } | |
| 187 | + }, | |
| 188 | + data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150] | |
| 189 | + }, { | |
| 190 | + name: 'CUCC', | |
| 191 | + type: 'line', | |
| 192 | + smooth: true, | |
| 193 | + symbol: 'circle', | |
| 194 | + symbolSize: 5, | |
| 195 | + showSymbol: false, | |
| 196 | + lineStyle: { | |
| 197 | + normal: { | |
| 198 | + width: 1 | |
| 199 | + } | |
| 200 | + }, | |
| 201 | + areaStyle: { | |
| 202 | + normal: { | |
| 203 | + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ | |
| 204 | + offset: 0, | |
| 205 | + color: 'rgba(219, 50, 51, 0.3)' | |
| 206 | + }, { | |
| 207 | + offset: 0.8, | |
| 208 | + color: 'rgba(219, 50, 51, 0)' | |
| 209 | + }], false), | |
| 210 | + shadowColor: 'rgba(0, 0, 0, 0.1)', | |
| 211 | + shadowBlur: 10 | |
| 212 | + } | |
| 213 | + }, | |
| 214 | + itemStyle: { | |
| 215 | + normal: { | |
| 216 | + color: 'rgb(219,50,51)', | |
| 217 | + borderColor: 'rgba(219,50,51,0.2)', | |
| 218 | + borderWidth: 12 | |
| 219 | + } | |
| 220 | + }, | |
| 221 | + data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122] | |
| 222 | + }] | |
| 223 | + }) | |
| 224 | + } | |
| 225 | + } | |
| 226 | +} | |
| 227 | +</script> | ... | ... |
src/components/Charts/MixChart.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div :id="id" :class="className" :style="{height:height,width:width}" /> | |
| 3 | +</template> | |
| 4 | + | |
| 5 | +<script> | |
| 6 | +import echarts from 'echarts' | |
| 7 | +import resize from './mixins/resize' | |
| 8 | + | |
| 9 | +export default { | |
| 10 | + mixins: [resize], | |
| 11 | + props: { | |
| 12 | + className: { | |
| 13 | + type: String, | |
| 14 | + default: 'chart' | |
| 15 | + }, | |
| 16 | + id: { | |
| 17 | + type: String, | |
| 18 | + default: 'chart' | |
| 19 | + }, | |
| 20 | + width: { | |
| 21 | + type: String, | |
| 22 | + default: '200px' | |
| 23 | + }, | |
| 24 | + height: { | |
| 25 | + type: String, | |
| 26 | + default: '200px' | |
| 27 | + } | |
| 28 | + }, | |
| 29 | + data() { | |
| 30 | + return { | |
| 31 | + chart: null | |
| 32 | + } | |
| 33 | + }, | |
| 34 | + mounted() { | |
| 35 | + this.initChart() | |
| 36 | + }, | |
| 37 | + beforeDestroy() { | |
| 38 | + if (!this.chart) { | |
| 39 | + return | |
| 40 | + } | |
| 41 | + this.chart.dispose() | |
| 42 | + this.chart = null | |
| 43 | + }, | |
| 44 | + methods: { | |
| 45 | + initChart() { | |
| 46 | + this.chart = echarts.init(document.getElementById(this.id)) | |
| 47 | + const xData = (function() { | |
| 48 | + const data = [] | |
| 49 | + for (let i = 1; i < 13; i++) { | |
| 50 | + data.push(i + 'month') | |
| 51 | + } | |
| 52 | + return data | |
| 53 | + }()) | |
| 54 | + this.chart.setOption({ | |
| 55 | + backgroundColor: '#344b58', | |
| 56 | + title: { | |
| 57 | + text: 'statistics', | |
| 58 | + x: '20', | |
| 59 | + top: '20', | |
| 60 | + textStyle: { | |
| 61 | + color: '#fff', | |
| 62 | + fontSize: '22' | |
| 63 | + }, | |
| 64 | + subtextStyle: { | |
| 65 | + color: '#90979c', | |
| 66 | + fontSize: '16' | |
| 67 | + } | |
| 68 | + }, | |
| 69 | + tooltip: { | |
| 70 | + trigger: 'axis', | |
| 71 | + axisPointer: { | |
| 72 | + textStyle: { | |
| 73 | + color: '#fff' | |
| 74 | + } | |
| 75 | + } | |
| 76 | + }, | |
| 77 | + grid: { | |
| 78 | + left: '5%', | |
| 79 | + right: '5%', | |
| 80 | + borderWidth: 0, | |
| 81 | + top: 150, | |
| 82 | + bottom: 95, | |
| 83 | + textStyle: { | |
| 84 | + color: '#fff' | |
| 85 | + } | |
| 86 | + }, | |
| 87 | + legend: { | |
| 88 | + x: '5%', | |
| 89 | + top: '10%', | |
| 90 | + textStyle: { | |
| 91 | + color: '#90979c' | |
| 92 | + }, | |
| 93 | + data: ['female', 'male', 'average'] | |
| 94 | + }, | |
| 95 | + calculable: true, | |
| 96 | + xAxis: [{ | |
| 97 | + type: 'category', | |
| 98 | + axisLine: { | |
| 99 | + lineStyle: { | |
| 100 | + color: '#90979c' | |
| 101 | + } | |
| 102 | + }, | |
| 103 | + splitLine: { | |
| 104 | + show: false | |
| 105 | + }, | |
| 106 | + axisTick: { | |
| 107 | + show: false | |
| 108 | + }, | |
| 109 | + splitArea: { | |
| 110 | + show: false | |
| 111 | + }, | |
| 112 | + axisLabel: { | |
| 113 | + interval: 0 | |
| 114 | + | |
| 115 | + }, | |
| 116 | + data: xData | |
| 117 | + }], | |
| 118 | + yAxis: [{ | |
| 119 | + type: 'value', | |
| 120 | + splitLine: { | |
| 121 | + show: false | |
| 122 | + }, | |
| 123 | + axisLine: { | |
| 124 | + lineStyle: { | |
| 125 | + color: '#90979c' | |
| 126 | + } | |
| 127 | + }, | |
| 128 | + axisTick: { | |
| 129 | + show: false | |
| 130 | + }, | |
| 131 | + axisLabel: { | |
| 132 | + interval: 0 | |
| 133 | + }, | |
| 134 | + splitArea: { | |
| 135 | + show: false | |
| 136 | + } | |
| 137 | + }], | |
| 138 | + dataZoom: [{ | |
| 139 | + show: true, | |
| 140 | + height: 30, | |
| 141 | + xAxisIndex: [ | |
| 142 | + 0 | |
| 143 | + ], | |
| 144 | + bottom: 30, | |
| 145 | + start: 10, | |
| 146 | + end: 80, | |
| 147 | + handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z', | |
| 148 | + handleSize: '110%', | |
| 149 | + handleStyle: { | |
| 150 | + color: '#d3dee5' | |
| 151 | + | |
| 152 | + }, | |
| 153 | + textStyle: { | |
| 154 | + color: '#fff' }, | |
| 155 | + borderColor: '#90979c' | |
| 156 | + | |
| 157 | + }, { | |
| 158 | + type: 'inside', | |
| 159 | + show: true, | |
| 160 | + height: 15, | |
| 161 | + start: 1, | |
| 162 | + end: 35 | |
| 163 | + }], | |
| 164 | + series: [{ | |
| 165 | + name: 'female', | |
| 166 | + type: 'bar', | |
| 167 | + stack: 'total', | |
| 168 | + barMaxWidth: 35, | |
| 169 | + barGap: '10%', | |
| 170 | + itemStyle: { | |
| 171 | + normal: { | |
| 172 | + color: 'rgba(255,144,128,1)', | |
| 173 | + label: { | |
| 174 | + show: true, | |
| 175 | + textStyle: { | |
| 176 | + color: '#fff' | |
| 177 | + }, | |
| 178 | + position: 'insideTop', | |
| 179 | + formatter(p) { | |
| 180 | + return p.value > 0 ? p.value : '' | |
| 181 | + } | |
| 182 | + } | |
| 183 | + } | |
| 184 | + }, | |
| 185 | + data: [ | |
| 186 | + 709, | |
| 187 | + 1917, | |
| 188 | + 2455, | |
| 189 | + 2610, | |
| 190 | + 1719, | |
| 191 | + 1433, | |
| 192 | + 1544, | |
| 193 | + 3285, | |
| 194 | + 5208, | |
| 195 | + 3372, | |
| 196 | + 2484, | |
| 197 | + 4078 | |
| 198 | + ] | |
| 199 | + }, | |
| 200 | + | |
| 201 | + { | |
| 202 | + name: 'male', | |
| 203 | + type: 'bar', | |
| 204 | + stack: 'total', | |
| 205 | + itemStyle: { | |
| 206 | + normal: { | |
| 207 | + color: 'rgba(0,191,183,1)', | |
| 208 | + barBorderRadius: 0, | |
| 209 | + label: { | |
| 210 | + show: true, | |
| 211 | + position: 'top', | |
| 212 | + formatter(p) { | |
| 213 | + return p.value > 0 ? p.value : '' | |
| 214 | + } | |
| 215 | + } | |
| 216 | + } | |
| 217 | + }, | |
| 218 | + data: [ | |
| 219 | + 327, | |
| 220 | + 1776, | |
| 221 | + 507, | |
| 222 | + 1200, | |
| 223 | + 800, | |
| 224 | + 482, | |
| 225 | + 204, | |
| 226 | + 1390, | |
| 227 | + 1001, | |
| 228 | + 951, | |
| 229 | + 381, | |
| 230 | + 220 | |
| 231 | + ] | |
| 232 | + }, { | |
| 233 | + name: 'average', | |
| 234 | + type: 'line', | |
| 235 | + stack: 'total', | |
| 236 | + symbolSize: 10, | |
| 237 | + symbol: 'circle', | |
| 238 | + itemStyle: { | |
| 239 | + normal: { | |
| 240 | + color: 'rgba(252,230,48,1)', | |
| 241 | + barBorderRadius: 0, | |
| 242 | + label: { | |
| 243 | + show: true, | |
| 244 | + position: 'top', | |
| 245 | + formatter(p) { | |
| 246 | + return p.value > 0 ? p.value : '' | |
| 247 | + } | |
| 248 | + } | |
| 249 | + } | |
| 250 | + }, | |
| 251 | + data: [ | |
| 252 | + 1036, | |
| 253 | + 3693, | |
| 254 | + 2962, | |
| 255 | + 3810, | |
| 256 | + 2519, | |
| 257 | + 1915, | |
| 258 | + 1748, | |
| 259 | + 4675, | |
| 260 | + 6209, | |
| 261 | + 4323, | |
| 262 | + 2865, | |
| 263 | + 4298 | |
| 264 | + ] | |
| 265 | + } | |
| 266 | + ] | |
| 267 | + }) | |
| 268 | + } | |
| 269 | + } | |
| 270 | +} | |
| 271 | +</script> | ... | ... |
src/components/Charts/Normal.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div :class="className" :style="{height: height,width: width}" ref="echarts"/> | |
| 3 | +</template> | |
| 4 | + | |
| 5 | +<script> | |
| 6 | + import echarts from 'echarts' | |
| 7 | + require('echarts/theme/macarons') // echarts theme | |
| 8 | + import resize from './mixins/resize' | |
| 9 | + | |
| 10 | + export default { | |
| 11 | + name: 'chart', | |
| 12 | + mixins: [resize], | |
| 13 | + props: { | |
| 14 | + className: { | |
| 15 | + type: String, | |
| 16 | + default: 'chart' | |
| 17 | + }, | |
| 18 | + width: { | |
| 19 | + type: String, | |
| 20 | + default: '100%' | |
| 21 | + }, | |
| 22 | + height: { | |
| 23 | + type: String, | |
| 24 | + default: '350px' | |
| 25 | + }, | |
| 26 | + autoResize: { | |
| 27 | + type: Boolean, | |
| 28 | + default: true | |
| 29 | + }, | |
| 30 | + options: { | |
| 31 | + type: Object, | |
| 32 | + default: {}, | |
| 33 | + required: true | |
| 34 | + } | |
| 35 | + }, | |
| 36 | + data() { | |
| 37 | + return { | |
| 38 | + chart: null | |
| 39 | + } | |
| 40 | + }, | |
| 41 | + watch: { | |
| 42 | + options: { | |
| 43 | + deep: true, | |
| 44 | + handler(options) { | |
| 45 | + if (!this.chart && options) { | |
| 46 | + this.initChart() | |
| 47 | + } else { | |
| 48 | + this.chart.setOption(this.options, true) | |
| 49 | + } | |
| 50 | + } | |
| 51 | + } | |
| 52 | + }, | |
| 53 | + mounted() { | |
| 54 | + this.$nextTick(() => { | |
| 55 | + this.initChart() | |
| 56 | + }) | |
| 57 | + }, | |
| 58 | + beforeDestroy() { | |
| 59 | + if (!this.chart) { | |
| 60 | + return | |
| 61 | + } | |
| 62 | + this.chart.dispose() | |
| 63 | + this.chart = null | |
| 64 | + }, | |
| 65 | + methods: { | |
| 66 | + initChart() { | |
| 67 | + this.chart = echarts.init(this.$refs.echarts, 'macarons') | |
| 68 | + this.chart.setOption(this.options) | |
| 69 | + } | |
| 70 | + } | |
| 71 | + } | |
| 72 | +</script> | ... | ... |
src/components/Charts/mixins/resize.js
0 → 100644
| 1 | +import { debounce } from '@/utils' | |
| 2 | + | |
| 3 | +export default { | |
| 4 | + data() { | |
| 5 | + return { | |
| 6 | + $_sidebarElm: null, | |
| 7 | + $_resizeHandler: null | |
| 8 | + } | |
| 9 | + }, | |
| 10 | + mounted() { | |
| 11 | + this.initListener() | |
| 12 | + }, | |
| 13 | + activated() { | |
| 14 | + if (!this.$_resizeHandler) { | |
| 15 | + // avoid duplication init | |
| 16 | + this.initListener() | |
| 17 | + } | |
| 18 | + | |
| 19 | + // when keep-alive chart activated, auto resize | |
| 20 | + this.resize() | |
| 21 | + }, | |
| 22 | + beforeDestroy() { | |
| 23 | + this.destroyListener() | |
| 24 | + }, | |
| 25 | + deactivated() { | |
| 26 | + this.destroyListener() | |
| 27 | + }, | |
| 28 | + methods: { | |
| 29 | + // use $_ for mixins properties | |
| 30 | + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential | |
| 31 | + $_sidebarResizeHandler(e) { | |
| 32 | + if (e.propertyName === 'width') { | |
| 33 | + this.$_resizeHandler() | |
| 34 | + } | |
| 35 | + }, | |
| 36 | + initListener() { | |
| 37 | + this.$_resizeHandler = debounce(() => { | |
| 38 | + this.resize() | |
| 39 | + }, 100) | |
| 40 | + window.addEventListener('resize', this.$_resizeHandler) | |
| 41 | + | |
| 42 | + this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] | |
| 43 | + this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) | |
| 44 | + }, | |
| 45 | + destroyListener() { | |
| 46 | + window.removeEventListener('resize', this.$_resizeHandler) | |
| 47 | + this.$_resizeHandler = null | |
| 48 | + | |
| 49 | + this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) | |
| 50 | + }, | |
| 51 | + resize() { | |
| 52 | + const { chart } = this | |
| 53 | + chart && chart.resize() | |
| 54 | + } | |
| 55 | + } | |
| 56 | +} | ... | ... |
src/components/ColumnSettings/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-tooltip :content="$t('common.columnSettings')" placement="top"> | |
| 3 | + <el-popover width="200" popper-class="columnSetting-popover"> | |
| 4 | + <el-link icon="icon-ym icon-ym-options NCC-common-head-icon" :underline="false" | |
| 5 | + slot="reference" /> | |
| 6 | + <NCC-table :data="data" class="columnTable" :hasNO="false" size="mini" ref="columnTable" | |
| 7 | + @selection-change="columnSelectionChange" @row-click="handleRowClick"> | |
| 8 | + <el-table-column prop="label" label="列表字段" /> | |
| 9 | + <el-table-column type="selection" width="50" align="center" /> | |
| 10 | + </NCC-table> | |
| 11 | + </el-popover> | |
| 12 | + </el-tooltip> | |
| 13 | +</template> | |
| 14 | + | |
| 15 | +<script> | |
| 16 | +export default { | |
| 17 | + name: 'ColumnSettings', | |
| 18 | + model: { | |
| 19 | + prop: 'value', | |
| 20 | + event: 'change' | |
| 21 | + }, | |
| 22 | + props: { | |
| 23 | + value: Array, | |
| 24 | + data: { | |
| 25 | + type: Array, | |
| 26 | + default: () => [] | |
| 27 | + } | |
| 28 | + }, | |
| 29 | + mounted() { | |
| 30 | + this.$refs.columnTable.$refs.NCCTable.toggleAllSelection() | |
| 31 | + }, | |
| 32 | + methods: { | |
| 33 | + handleRowClick(row) { | |
| 34 | + this.$refs.columnTable.$refs.NCCTable.toggleRowSelection(row) | |
| 35 | + }, | |
| 36 | + columnSelectionChange(val) { | |
| 37 | + this.$emit('change', val) | |
| 38 | + } | |
| 39 | + } | |
| 40 | +} | |
| 41 | +</script> | |
| 42 | +<style lang="scss"> | |
| 43 | +.columnSetting-popover { | |
| 44 | + padding: 0 !important; | |
| 45 | +} | |
| 46 | +</style> | |
| 0 | 47 | \ No newline at end of file | ... | ... |
src/components/CompanyForm/index.vue
| ... | ... | @@ -66,7 +66,7 @@ |
| 66 | 66 | </el-form> |
| 67 | 67 | <span slot="footer" class="dialog-footer"> |
| 68 | 68 | <el-button @click="close">取消</el-button> |
| 69 | - <el-button type="primary" @click="confirm">确认</el-button> | |
| 69 | + <el-button type="primary" @click="confirm" :loading="btnLoading">确认</el-button> | |
| 70 | 70 | </span> |
| 71 | 71 | </el-dialog> |
| 72 | 72 | </div> |
| ... | ... | @@ -79,35 +79,36 @@ export default { |
| 79 | 79 | props: {}, |
| 80 | 80 | data() { |
| 81 | 81 | return { |
| 82 | - visible: false, | |
| 83 | - loading: false, | |
| 84 | - dataForm: { | |
| 85 | - id: '', | |
| 86 | - id: undefined, | |
| 87 | - companyName: undefined, | |
| 88 | - socialCreditAgency: undefined, | |
| 89 | - legalPerson: undefined, | |
| 90 | - address: undefined, | |
| 91 | - contactInformation: undefined, | |
| 92 | - qualificationCertificate: [], | |
| 93 | - otherInfo: undefined, | |
| 94 | - }, | |
| 95 | - rules: { | |
| 96 | - companyName: [ | |
| 97 | - { | |
| 98 | - required: true, | |
| 99 | - message: '请输入公司名称', | |
| 100 | - trigger: 'blur' | |
| 101 | - }, | |
| 102 | - ], | |
| 103 | - contactInformation: [ | |
| 104 | - { | |
| 105 | - pattern: /^1[3456789]\d{9}$|^0\d{2,3}-?\d{7,8}$/, | |
| 106 | - message: '请输入正确的联系方式', | |
| 107 | - trigger: 'blur' | |
| 108 | - }, | |
| 109 | - ], | |
| 110 | - }, | |
| 82 | + visible: false, | |
| 83 | + loading: false, | |
| 84 | + dataForm: { | |
| 85 | + id: '', | |
| 86 | + id: undefined, | |
| 87 | + companyName: undefined, | |
| 88 | + socialCreditAgency: undefined, | |
| 89 | + legalPerson: undefined, | |
| 90 | + address: undefined, | |
| 91 | + contactInformation: undefined, | |
| 92 | + qualificationCertificate: [], | |
| 93 | + otherInfo: undefined, | |
| 94 | + }, | |
| 95 | + rules: { | |
| 96 | + companyName: [ | |
| 97 | + { | |
| 98 | + required: true, | |
| 99 | + message: '请输入公司名称', | |
| 100 | + trigger: 'blur' | |
| 101 | + }, | |
| 102 | + ], | |
| 103 | + contactInformation: [ | |
| 104 | + { | |
| 105 | + pattern: /^1[3456789]\d{9}$|^0\d{2,3}-?\d{7,8}$/, | |
| 106 | + message: '请输入正确的联系方式', | |
| 107 | + trigger: 'blur' | |
| 108 | + }, | |
| 109 | + ], | |
| 110 | + }, | |
| 111 | + btnLoading: false | |
| 111 | 112 | }; |
| 112 | 113 | }, |
| 113 | 114 | watch: {}, |
| ... | ... | @@ -122,25 +123,27 @@ export default { |
| 122 | 123 | this.$refs['elForm'].resetFields(); |
| 123 | 124 | }, |
| 124 | 125 | async confirm() { |
| 125 | - this.$refs['elForm'].validate((valid) => { | |
| 126 | - if (valid) { | |
| 127 | - request({ | |
| 128 | - url: `/Extend/BaseComapnyInfo`, | |
| 129 | - method: 'post', | |
| 130 | - data: this.dataForm, | |
| 131 | - }).then((res) => { | |
| 132 | - this.$message({ | |
| 133 | - message: res.msg, | |
| 134 | - type: 'success', | |
| 135 | - duration: 1000, | |
| 136 | - onClose: () => { | |
| 137 | - this.visible = false, | |
| 138 | - this.$emit('refresh', true) | |
| 139 | - } | |
| 140 | - }) | |
| 141 | - }) | |
| 142 | - }}) | |
| 143 | - | |
| 126 | + this.$refs['elForm'].validate((valid) => { | |
| 127 | + if (valid) { | |
| 128 | + this.btnLoading = true; | |
| 129 | + request({ | |
| 130 | + url: `/Extend/BaseComapnyInfo`, | |
| 131 | + method: 'post', | |
| 132 | + data: this.dataForm, | |
| 133 | + }).then((res) => { | |
| 134 | + this.btnLoading = false; | |
| 135 | + this.$message({ | |
| 136 | + message: res.msg, | |
| 137 | + type: 'success', | |
| 138 | + duration: 1000, | |
| 139 | + onClose: () => { | |
| 140 | + this.visible = false, | |
| 141 | + this.$emit('refresh', true) | |
| 142 | + } | |
| 143 | + }) | |
| 144 | + }).catch(() => {this.btnLoading = false}) | |
| 145 | + } | |
| 146 | + }) | |
| 144 | 147 | }, |
| 145 | 148 | }, |
| 146 | 149 | }; | ... | ... |
src/components/Hamburger/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div style="padding: 0 15px;" @click="toggleClick"> | |
| 3 | + <i class="icon-ym icon-ym-header-collapse1" v-if='isActive'></i> | |
| 4 | + <i class="icon-ym icon-ym-header-expand" v-else></i> | |
| 5 | + </div> | |
| 6 | +</template> | |
| 7 | + | |
| 8 | +<script> | |
| 9 | +export default { | |
| 10 | + name: 'Hamburger', | |
| 11 | + props: { | |
| 12 | + isActive: { | |
| 13 | + type: Boolean, | |
| 14 | + default: false | |
| 15 | + } | |
| 16 | + }, | |
| 17 | + methods: { | |
| 18 | + toggleClick() { | |
| 19 | + this.$emit('toggleClick') | |
| 20 | + } | |
| 21 | + } | |
| 22 | +} | |
| 23 | +</script> | |
| 24 | + | |
| 25 | +<style scoped> | |
| 26 | +.icon-ym { | |
| 27 | + font-size: 20px; | |
| 28 | + color: #666666; | |
| 29 | +} | |
| 30 | +</style> | |
| 0 | 31 | \ No newline at end of file | ... | ... |
src/components/HeaderSearch/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div :class="{'show':show}" class="header-search"> | |
| 3 | + <i class="el-icon-search search-icon" @click.stop="click"></i> | |
| 4 | + <el-select ref="headerSearchSelect" v-model="search" :remote-method="querySearch" filterable | |
| 5 | + default-first-option remote placeholder="搜索:导航菜单" class="header-search-select" | |
| 6 | + @change="change"> | |
| 7 | + <el-option v-for="item in options" :key="item.path" :value="item" | |
| 8 | + :label="item.title.join(' > ')" /> | |
| 9 | + </el-select> | |
| 10 | + </div> | |
| 11 | +</template> | |
| 12 | + | |
| 13 | +<script> | |
| 14 | +// fuse is a lightweight fuzzy-search module | |
| 15 | +// make search results more in line with expectations | |
| 16 | +import Fuse from 'fuse.js' | |
| 17 | +import path from 'path' | |
| 18 | +import i18n from '@/lang' | |
| 19 | + | |
| 20 | +export default { | |
| 21 | + name: 'HeaderSearch', | |
| 22 | + data() { | |
| 23 | + return { | |
| 24 | + search: '', | |
| 25 | + options: [], | |
| 26 | + searchPool: [], | |
| 27 | + show: false, | |
| 28 | + fuse: undefined | |
| 29 | + } | |
| 30 | + }, | |
| 31 | + computed: { | |
| 32 | + routes() { | |
| 33 | + return this.$store.getters.permission_routes | |
| 34 | + }, | |
| 35 | + lang() { | |
| 36 | + return this.$store.getters.language | |
| 37 | + } | |
| 38 | + }, | |
| 39 | + watch: { | |
| 40 | + lang() { | |
| 41 | + this.searchPool = this.generateRoutes(this.routes) | |
| 42 | + }, | |
| 43 | + routes() { | |
| 44 | + this.searchPool = this.generateRoutes(this.routes) | |
| 45 | + }, | |
| 46 | + searchPool(list) { | |
| 47 | + this.initFuse(list) | |
| 48 | + }, | |
| 49 | + show(value) { | |
| 50 | + if (value) { | |
| 51 | + document.body.addEventListener('click', this.close) | |
| 52 | + } else { | |
| 53 | + document.body.removeEventListener('click', this.close) | |
| 54 | + } | |
| 55 | + } | |
| 56 | + }, | |
| 57 | + mounted() { | |
| 58 | + this.searchPool = this.generateRoutes(this.routes) | |
| 59 | + }, | |
| 60 | + methods: { | |
| 61 | + click() { | |
| 62 | + this.show = !this.show | |
| 63 | + if (this.show) { | |
| 64 | + this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus() | |
| 65 | + } | |
| 66 | + }, | |
| 67 | + close() { | |
| 68 | + this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur() | |
| 69 | + this.options = [] | |
| 70 | + this.show = false | |
| 71 | + }, | |
| 72 | + change(val) { | |
| 73 | + this.$router.push(val.path) | |
| 74 | + this.search = '' | |
| 75 | + this.options = [] | |
| 76 | + this.$nextTick(() => { | |
| 77 | + this.show = false | |
| 78 | + }) | |
| 79 | + }, | |
| 80 | + initFuse(list) { | |
| 81 | + this.fuse = new Fuse(list, { | |
| 82 | + shouldSort: true, | |
| 83 | + threshold: 0.4, | |
| 84 | + location: 0, | |
| 85 | + distance: 100, | |
| 86 | + maxPatternLength: 32, | |
| 87 | + minMatchCharLength: 1, | |
| 88 | + keys: [{ | |
| 89 | + name: 'title', | |
| 90 | + weight: 0.7 | |
| 91 | + }, { | |
| 92 | + name: 'path', | |
| 93 | + weight: 0.3 | |
| 94 | + }] | |
| 95 | + }) | |
| 96 | + }, | |
| 97 | + // Filter out the routes that can be displayed in the sidebar | |
| 98 | + // And generate the internationalized title | |
| 99 | + generateRoutes(routes, basePath = '/', prefixTitle = []) { | |
| 100 | + let res = [] | |
| 101 | + | |
| 102 | + for (const router of routes) { | |
| 103 | + // console.log(router); | |
| 104 | + | |
| 105 | + // skip hidden router | |
| 106 | + if (router.hidden) { continue } | |
| 107 | + | |
| 108 | + const data = { | |
| 109 | + path: path.resolve(basePath, router.path), | |
| 110 | + title: [...prefixTitle] | |
| 111 | + } | |
| 112 | + | |
| 113 | + if (router.meta && router.meta.title) { | |
| 114 | + // generate internationalized title | |
| 115 | + const i18ntitle = i18n.t(`route.${router.meta.title}`) | |
| 116 | + | |
| 117 | + let title = i18ntitle | |
| 118 | + if (i18ntitle.indexOf('route.') > -1) title = router.meta.zhTitle | |
| 119 | + data.title = [...data.title, title] | |
| 120 | + | |
| 121 | + if (router.redirect !== 'noRedirect') { | |
| 122 | + // only push the routes with title | |
| 123 | + // special case: need to exclude parent router without redirect | |
| 124 | + res.push(data) | |
| 125 | + } | |
| 126 | + } | |
| 127 | + | |
| 128 | + // recursive child routes | |
| 129 | + if (router.children) { | |
| 130 | + const tempRoutes = this.generateRoutes(router.children, data.path, data.title) | |
| 131 | + if (tempRoutes.length >= 1) { | |
| 132 | + res = [...res, ...tempRoutes] | |
| 133 | + } | |
| 134 | + } | |
| 135 | + } | |
| 136 | + return res | |
| 137 | + }, | |
| 138 | + querySearch(query) { | |
| 139 | + if (query !== '') { | |
| 140 | + this.options = this.fuse.search(query) | |
| 141 | + } else { | |
| 142 | + this.options = [] | |
| 143 | + } | |
| 144 | + } | |
| 145 | + } | |
| 146 | +} | |
| 147 | +</script> | |
| 148 | + | |
| 149 | +<style lang="scss" scoped> | |
| 150 | +.header-search { | |
| 151 | + font-size: 0 !important; | |
| 152 | + | |
| 153 | + .search-icon { | |
| 154 | + cursor: pointer; | |
| 155 | + font-size: 16px; | |
| 156 | + vertical-align: middle; | |
| 157 | + } | |
| 158 | + | |
| 159 | + .header-search-select { | |
| 160 | + font-size: 20px; | |
| 161 | + transition: width 0.2s; | |
| 162 | + width: 0; | |
| 163 | + overflow: hidden; | |
| 164 | + background: transparent; | |
| 165 | + border-radius: 0; | |
| 166 | + display: inline-block; | |
| 167 | + vertical-align: middle; | |
| 168 | + | |
| 169 | + >>> .el-input__inner { | |
| 170 | + border-radius: 0; | |
| 171 | + border: 0; | |
| 172 | + // padding-left: 0; | |
| 173 | + // padding-right: 0; | |
| 174 | + box-shadow: none !important; | |
| 175 | + border-bottom: 1px solid #d9d9d9; | |
| 176 | + vertical-align: middle; | |
| 177 | + } | |
| 178 | + } | |
| 179 | + | |
| 180 | + &.show { | |
| 181 | + .header-search-select { | |
| 182 | + width: 210px; | |
| 183 | + margin-left: 10px; | |
| 184 | + } | |
| 185 | + } | |
| 186 | +} | |
| 187 | +</style> | ... | ... |
src/components/InfoForm/index.vue
| ... | ... | @@ -220,7 +220,7 @@ |
| 220 | 220 | </el-form> |
| 221 | 221 | <span slot="footer" class="dialog-footer"> |
| 222 | 222 | <el-button @click="close">取消</el-button> |
| 223 | - <el-button type="primary" @click="confirm">确认</el-button> | |
| 223 | + <el-button type="primary" @click="confirm" :loading="btnLoading">确认</el-button> | |
| 224 | 224 | </span> |
| 225 | 225 | </el-dialog> |
| 226 | 226 | </div> |
| ... | ... | @@ -312,6 +312,7 @@ export default { |
| 312 | 312 | systemTypeOptions: [], |
| 313 | 313 | systemClassOptions: [], |
| 314 | 314 | areaOptions: [], |
| 315 | + btnLoading: false, | |
| 315 | 316 | }; |
| 316 | 317 | }, |
| 317 | 318 | watch: {}, |
| ... | ... | @@ -388,6 +389,7 @@ export default { |
| 388 | 389 | async confirm() { |
| 389 | 390 | this.$refs["infoForm"].validate((valid) => { |
| 390 | 391 | if (valid) { |
| 392 | + this.btnLoading = true; | |
| 391 | 393 | switch (this.type) { |
| 392 | 394 | case "add": |
| 393 | 395 | addSystem(this.infoForm).then((res) => { |
| ... | ... | @@ -395,9 +397,10 @@ export default { |
| 395 | 397 | message: "恭喜你,新增成功", |
| 396 | 398 | type: "success", |
| 397 | 399 | }); |
| 400 | + this.btnLoading = false; | |
| 398 | 401 | this.$emit('reInit'); |
| 399 | 402 | this.close(); |
| 400 | - }); | |
| 403 | + }).catch(() => { this.btnLoading = false }); | |
| 401 | 404 | break; |
| 402 | 405 | case "edit": |
| 403 | 406 | this.infoForm.id = this.systemId; |
| ... | ... | @@ -406,9 +409,10 @@ export default { |
| 406 | 409 | message: "恭喜你,修改成功", |
| 407 | 410 | type: "success", |
| 408 | 411 | }); |
| 412 | + this.btnLoading = false; | |
| 409 | 413 | this.$emit('reInit'); |
| 410 | 414 | this.close(); |
| 411 | - }); | |
| 415 | + }).catch(() => { this.btnLoading = false });; | |
| 412 | 416 | break; |
| 413 | 417 | |
| 414 | 418 | default: | ... | ... |
src/components/LangSelect/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dropdown class="international" @command="handleSetLanguage"> | |
| 3 | + <div> | |
| 4 | + <i class="icon-ym icon-ym-header-language"></i> | |
| 5 | + </div> | |
| 6 | + <el-dropdown-menu slot="dropdown"> | |
| 7 | + <el-dropdown-item :disabled="language==='zh'" command="zh"> | |
| 8 | + 简体中文 | |
| 9 | + </el-dropdown-item> | |
| 10 | + <el-dropdown-item :disabled="language==='zhtw'" command="zhtw"> | |
| 11 | + 繁体中文 | |
| 12 | + </el-dropdown-item> | |
| 13 | + <el-dropdown-item :disabled="language==='en'" command="en"> | |
| 14 | + English | |
| 15 | + </el-dropdown-item> | |
| 16 | + </el-dropdown-menu> | |
| 17 | + </el-dropdown> | |
| 18 | +</template> | |
| 19 | + | |
| 20 | +<script> | |
| 21 | +import { UpdateLanguage } from '@/api/permission/userSetting' | |
| 22 | +import getPageTitle from '@/utils/get-page-title' | |
| 23 | +export default { | |
| 24 | + computed: { | |
| 25 | + language() { | |
| 26 | + return this.$store.getters.language | |
| 27 | + } | |
| 28 | + }, | |
| 29 | + methods: { | |
| 30 | + handleSetLanguage(lang) { | |
| 31 | + UpdateLanguage({ language: lang }).then(res => { }) | |
| 32 | + this.$i18n.locale = lang | |
| 33 | + this.$store.dispatch('app/setLanguage', lang) | |
| 34 | + let text = '切换成功' | |
| 35 | + if (lang === 'en') text = 'Switch Language Success' | |
| 36 | + if (lang === 'zhtw') text = '切換成功' | |
| 37 | + document.title = getPageTitle(this.$route.meta.title, this.$route.meta.zhTitle) | |
| 38 | + this.$message({ | |
| 39 | + message: text, | |
| 40 | + type: 'success' | |
| 41 | + }) | |
| 42 | + } | |
| 43 | + } | |
| 44 | +} | |
| 45 | +</script> | ... | ... |
src/components/MarkdownEditor/default-options.js
0 → 100644
| 1 | +// doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor | |
| 2 | +export default { | |
| 3 | + minHeight: '200px', | |
| 4 | + previewStyle: 'vertical', | |
| 5 | + useCommandShortcut: true, | |
| 6 | + useDefaultHTMLSanitizer: true, | |
| 7 | + usageStatistics: false, | |
| 8 | + hideModeSwitch: false, | |
| 9 | + toolbarItems: [ | |
| 10 | + 'heading', | |
| 11 | + 'bold', | |
| 12 | + 'italic', | |
| 13 | + 'strike', | |
| 14 | + 'divider', | |
| 15 | + 'hr', | |
| 16 | + 'quote', | |
| 17 | + 'divider', | |
| 18 | + 'ul', | |
| 19 | + 'ol', | |
| 20 | + 'task', | |
| 21 | + 'indent', | |
| 22 | + 'outdent', | |
| 23 | + 'divider', | |
| 24 | + 'table', | |
| 25 | + 'image', | |
| 26 | + 'link', | |
| 27 | + 'divider', | |
| 28 | + 'code', | |
| 29 | + 'codeblock' | |
| 30 | + ] | |
| 31 | +} | ... | ... |
src/components/MarkdownEditor/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div :id="id" /> | |
| 3 | +</template> | |
| 4 | + | |
| 5 | +<script> | |
| 6 | +// deps for editor | |
| 7 | +import 'codemirror/lib/codemirror.css' // codemirror | |
| 8 | +import 'tui-editor/dist/tui-editor.css' // editor ui | |
| 9 | +import 'tui-editor/dist/tui-editor-contents.css' // editor content | |
| 10 | + | |
| 11 | +import Editor from 'tui-editor' | |
| 12 | +import defaultOptions from './default-options' | |
| 13 | + | |
| 14 | +export default { | |
| 15 | + name: 'MarkdownEditor', | |
| 16 | + props: { | |
| 17 | + value: { | |
| 18 | + type: String, | |
| 19 | + default: '' | |
| 20 | + }, | |
| 21 | + id: { | |
| 22 | + type: String, | |
| 23 | + required: false, | |
| 24 | + default() { | |
| 25 | + return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '') | |
| 26 | + } | |
| 27 | + }, | |
| 28 | + options: { | |
| 29 | + type: Object, | |
| 30 | + default() { | |
| 31 | + return defaultOptions | |
| 32 | + } | |
| 33 | + }, | |
| 34 | + mode: { | |
| 35 | + type: String, | |
| 36 | + default: 'markdown' | |
| 37 | + }, | |
| 38 | + height: { | |
| 39 | + type: String, | |
| 40 | + required: false, | |
| 41 | + default: '300px' | |
| 42 | + }, | |
| 43 | + language: { | |
| 44 | + type: String, | |
| 45 | + required: false, | |
| 46 | + default: 'en_US' // https://github.com/nhnent/tui.editor/tree/master/src/js/langs | |
| 47 | + } | |
| 48 | + }, | |
| 49 | + data() { | |
| 50 | + return { | |
| 51 | + editor: null | |
| 52 | + } | |
| 53 | + }, | |
| 54 | + computed: { | |
| 55 | + editorOptions() { | |
| 56 | + const options = Object.assign({}, defaultOptions, this.options) | |
| 57 | + options.initialEditType = this.mode | |
| 58 | + options.height = this.height | |
| 59 | + options.language = this.language | |
| 60 | + return options | |
| 61 | + } | |
| 62 | + }, | |
| 63 | + watch: { | |
| 64 | + value(newValue, preValue) { | |
| 65 | + if (newValue !== preValue && newValue !== this.editor.getValue()) { | |
| 66 | + this.editor.setValue(newValue) | |
| 67 | + } | |
| 68 | + }, | |
| 69 | + language(val) { | |
| 70 | + this.destroyEditor() | |
| 71 | + this.initEditor() | |
| 72 | + }, | |
| 73 | + height(newValue) { | |
| 74 | + this.editor.height(newValue) | |
| 75 | + }, | |
| 76 | + mode(newValue) { | |
| 77 | + this.editor.changeMode(newValue) | |
| 78 | + } | |
| 79 | + }, | |
| 80 | + mounted() { | |
| 81 | + this.initEditor() | |
| 82 | + }, | |
| 83 | + destroyed() { | |
| 84 | + this.destroyEditor() | |
| 85 | + }, | |
| 86 | + methods: { | |
| 87 | + initEditor() { | |
| 88 | + this.editor = new Editor({ | |
| 89 | + el: document.getElementById(this.id), | |
| 90 | + ...this.editorOptions | |
| 91 | + }) | |
| 92 | + if (this.value) { | |
| 93 | + this.editor.setValue(this.value) | |
| 94 | + } | |
| 95 | + this.editor.on('change', () => { | |
| 96 | + this.$emit('input', this.editor.getValue()) | |
| 97 | + }) | |
| 98 | + }, | |
| 99 | + destroyEditor() { | |
| 100 | + if (!this.editor) return | |
| 101 | + this.editor.off('change') | |
| 102 | + this.editor.remove() | |
| 103 | + }, | |
| 104 | + setValue(value) { | |
| 105 | + this.editor.setValue(value) | |
| 106 | + }, | |
| 107 | + getValue() { | |
| 108 | + return this.editor.getValue() | |
| 109 | + }, | |
| 110 | + setHtml(value) { | |
| 111 | + this.editor.setHtml(value) | |
| 112 | + }, | |
| 113 | + getHtml() { | |
| 114 | + return this.editor.getHtml() | |
| 115 | + } | |
| 116 | + } | |
| 117 | +} | |
| 118 | +</script> | ... | ... |
src/components/NCC-TreeTransfer/array.js
0 → 100644
| 1 | +/** | |
| 2 | + * auth: weilan | |
| 3 | + * github: https://github.com/hql7 | |
| 4 | + * description: 一个数组操作函数库 | |
| 5 | + */ | |
| 6 | + | |
| 7 | +// 从树形数据中递归筛选目标值 | |
| 8 | +function valInDeep(arr = [], val, id, childs) { | |
| 9 | + return arr.reduce((flat, item) => { | |
| 10 | + return flat.concat( | |
| 11 | + item[id] == val ? item : valInDeep(item[childs] || [], val, id, childs) | |
| 12 | + ); | |
| 13 | + }, []); | |
| 14 | +} | |
| 15 | + | |
| 16 | +// 将树形数据向下递归为一维数组 | |
| 17 | +function flattenDeep(arr = [], childs) { | |
| 18 | + return arr.reduce((flat, item) => { | |
| 19 | + return flat.concat( | |
| 20 | + item, | |
| 21 | + item[childs] ? flattenDeep(item[childs], childs) : [] | |
| 22 | + ); | |
| 23 | + }, []); | |
| 24 | +} | |
| 25 | + | |
| 26 | +// 将树形数据向上将此支线递归为一维数组 | |
| 27 | +function flattenDeepParents(arr, parent) { | |
| 28 | + return arr.reduce((flat, item) => { | |
| 29 | + return flat.concat( | |
| 30 | + item[parent] || [], | |
| 31 | + item[parent] ? flattenDeepParents([item[parent]], parent) : [] | |
| 32 | + ); | |
| 33 | + }, []); | |
| 34 | +} | |
| 35 | + | |
| 36 | +// 根据条件递归祖先元素 | |
| 37 | +function regDeepParents(row, parent, reg) { | |
| 38 | + if (row[parent]) { | |
| 39 | + reg && reg(row[parent]); | |
| 40 | + regDeepParents(row[parent], parent, reg); | |
| 41 | + } | |
| 42 | +} | |
| 43 | + | |
| 44 | +// 将数组转化成树结构 | |
| 45 | +function arrayToTree( | |
| 46 | + array = [], | |
| 47 | + options = { id: "id", pid: "pid", children: "children" } | |
| 48 | +) { | |
| 49 | + let array_ = []; // 创建储存剔除叶子节点后的骨架节点数组 | |
| 50 | + let unique = {}; // 创建盒子辅助本轮children合并去重 | |
| 51 | + array.forEach(item => { | |
| 52 | + // 适应el-tree-transfer 将根节点pid重置为 0 | |
| 53 | + let root = ["undefined", undefined, null].includes(item[options.pid]); | |
| 54 | + if (root) item[options.pid] = 0; | |
| 55 | + // 筛选可以插入当前节点的所有子节点 | |
| 56 | + let children_array = array.filter( | |
| 57 | + it => it[options.pid] === item[options.id] | |
| 58 | + ); | |
| 59 | + if (item.children && item.children instanceof Array) { | |
| 60 | + // 去重合并数组 | |
| 61 | + item.children.map(i => (unique[i[options.id]] = 1)); | |
| 62 | + item.children.push( | |
| 63 | + ...children_array.filter(i => unique[i[options.id]] !== 1) | |
| 64 | + ); | |
| 65 | + } else { | |
| 66 | + item.children = children_array; | |
| 67 | + } | |
| 68 | + // 当children_array有数据时插入下一轮array_,当无数据时将最后留下来的根节点树形插入数组 | |
| 69 | + let has_children = children_array.length > 0; | |
| 70 | + if ( | |
| 71 | + has_children || | |
| 72 | + (!has_children && [0, "0"].includes(item[options.pid])) | |
| 73 | + ) { | |
| 74 | + array_.push(item); | |
| 75 | + } | |
| 76 | + }); | |
| 77 | + // 当数组内仅有根节点时退出,否组继续处理 最终递归深度次 | |
| 78 | + if (!array_.every(item => [0, "0"].includes(item[options.pid]))) { | |
| 79 | + return arrayToTree(array_, options); | |
| 80 | + } else { | |
| 81 | + return array_; | |
| 82 | + } | |
| 83 | +} | |
| 84 | + | |
| 85 | +export { | |
| 86 | + valInDeep, | |
| 87 | + flattenDeep, | |
| 88 | + flattenDeepParents, | |
| 89 | + regDeepParents, | |
| 90 | + arrayToTree | |
| 91 | +}; | ... | ... |
src/components/NCC-TreeTransfer/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="transfer" :style="{ width, height }"> | |
| 3 | + <template> | |
| 4 | + <!-- 左侧穿梭框 原料框 --> | |
| 5 | + <div class="transfer-left"> | |
| 6 | + <h3 class="transfer-title"> | |
| 7 | + <el-checkbox :indeterminate="from_is_indeterminate" v-model="from_check_all" | |
| 8 | + @change="fromAllBoxChange" /> | |
| 9 | + <span>{{ fromTitle }}</span> | |
| 10 | + <slot name="title-left"></slot> | |
| 11 | + </h3> | |
| 12 | + <!-- 内容区 --> | |
| 13 | + <div class="transfer-main"> | |
| 14 | + <slot name="from"></slot> | |
| 15 | + <el-input v-if="filter" :placeholder="placeholder" v-model="filterFrom" | |
| 16 | + class="filter-tree" clearable /> | |
| 17 | + <el-tree ref="from-tree" show-checkbox :lazy="lazy" :node-key="node_key" | |
| 18 | + :load="leftloadNode" :props="defaultProps" :data="self_from_data" | |
| 19 | + :default-expand-all="openAll" :highlight-current="highLight" | |
| 20 | + :render-content="renderContentLeft" :filter-node-method="filterNodeFrom" | |
| 21 | + :default-checked-keys="defaultCheckedKeys" :default-expanded-keys="from_expanded_keys" | |
| 22 | + @check="fromTreeChecked"> | |
| 23 | + <div slot-scope="{ node, data }"> | |
| 24 | + <i :class="data.icon" /> | |
| 25 | + <span style="padding-left: 4px;">{{ node.label }}</span> | |
| 26 | + </div> | |
| 27 | + </el-tree> | |
| 28 | + <slot name="left-footer"></slot> | |
| 29 | + </div> | |
| 30 | + </div> | |
| 31 | + <!-- 穿梭区 按钮框 --> | |
| 32 | + <div class="transfer-center"> | |
| 33 | + <template v-if="button_text"> | |
| 34 | + <p class="transfer-center-item"> | |
| 35 | + <el-button type="primary" @click="addToAims(true)" :disabled="from_disabled"> | |
| 36 | + {{ fromButton || "添加" }} | |
| 37 | + <i class="el-icon-arrow-right"></i> | |
| 38 | + </el-button> | |
| 39 | + </p> | |
| 40 | + <p class="transfer-center-item"> | |
| 41 | + <el-button type="primary" @click="removeToSource" :disabled="to_disabled" | |
| 42 | + icon="el-icon-arrow-left">{{ toButton || "移除" }}</el-button> | |
| 43 | + </p> | |
| 44 | + </template> | |
| 45 | + <template v-else> | |
| 46 | + <p class="transfer-center-item"> | |
| 47 | + <el-button type="primary" @click="addToAims(true)" icon="el-icon-arrow-right" | |
| 48 | + :disabled="from_disabled" /> | |
| 49 | + </p> | |
| 50 | + <p class="transfer-center-item"> | |
| 51 | + <el-button type="primary" @click="removeToSource" :disabled="to_disabled" | |
| 52 | + icon="el-icon-arrow-left" /> | |
| 53 | + </p> | |
| 54 | + </template> | |
| 55 | + </div> | |
| 56 | + <!-- 右侧穿梭框 目标框 --> | |
| 57 | + <div class="transfer-right"> | |
| 58 | + <h3 class="transfer-title"> | |
| 59 | + <el-checkbox :indeterminate="to_is_indeterminate" v-model="to_check_all" | |
| 60 | + @change="toAllBoxChange" /> | |
| 61 | + <span>{{ toTitle }}</span> | |
| 62 | + <slot name="title-right"></slot> | |
| 63 | + </h3> | |
| 64 | + <!-- 内容区 --> | |
| 65 | + <div class="transfer-main"> | |
| 66 | + <slot name="to"></slot> | |
| 67 | + <el-input v-if="filter" :placeholder="placeholder" v-model="filterTo" size="small" | |
| 68 | + class="filter-tree" clearable /> | |
| 69 | + <el-tree slot="to" ref="to-tree" show-checkbox :lazy="lazyRight" :data="self_to_data" | |
| 70 | + :node-key="node_key" :props="defaultProps" :load="rightloadNode" | |
| 71 | + :default-expand-all="openAll" :highlight-current="highLight" | |
| 72 | + :render-content="renderContentRight" :filter-node-method="filterNodeTo" | |
| 73 | + :default-expanded-keys="to_expanded_keys" @check="toTreeChecked"> | |
| 74 | + <div slot-scope="{ node, data }"> | |
| 75 | + <i :class="data.icon" /> | |
| 76 | + <span style="padding-left: 4px;">{{ node.label }}</span> | |
| 77 | + </div> | |
| 78 | + </el-tree> | |
| 79 | + <slot name="right-footer"></slot> | |
| 80 | + </div> | |
| 81 | + </div> | |
| 82 | + </template> | |
| 83 | + </div> | |
| 84 | +</template> | |
| 85 | + | |
| 86 | +<script> | |
| 87 | +import { arrayToTree } from './array'; | |
| 88 | +export default { | |
| 89 | + name: 'NCC-tree-transfer', | |
| 90 | + data() { | |
| 91 | + return { | |
| 92 | + from_is_indeterminate: false, // 源数据是否半选 | |
| 93 | + from_check_all: false, // 源数据是否全选 | |
| 94 | + to_is_indeterminate: false, // 目标数据是否半选 | |
| 95 | + to_check_all: false, // 目标数据是否全选 | |
| 96 | + from_expanded_keys: [], // 源数据展开节点 | |
| 97 | + to_expanded_keys: [], // 目标数据展开节点 | |
| 98 | + from_disabled: true, // 添加按钮是否禁用 | |
| 99 | + to_disabled: true, // 移除按钮是否禁用 | |
| 100 | + from_check_keys: [], // 源数据选中key数组 以此属性关联穿梭按钮,总全选、半选状态 | |
| 101 | + to_check_keys: [], // 目标数据选中key数组 以此属性关联穿梭按钮,总全选、半选状态 | |
| 102 | + filterFrom: "", // 源数据筛选 | |
| 103 | + filterTo: "", // 目标数据筛选 | |
| 104 | + } | |
| 105 | + }, | |
| 106 | + props: { | |
| 107 | + // 宽度 | |
| 108 | + width: { | |
| 109 | + type: String, | |
| 110 | + default: "100%" | |
| 111 | + }, | |
| 112 | + // 高度 | |
| 113 | + height: { | |
| 114 | + type: String, | |
| 115 | + default: "320px" | |
| 116 | + }, | |
| 117 | + // 标题 | |
| 118 | + title: { | |
| 119 | + type: Array, | |
| 120 | + default: () => ["源列表", "目标列表"] | |
| 121 | + }, | |
| 122 | + // 穿梭按钮名字 | |
| 123 | + button_text: Array, | |
| 124 | + // 源数据 | |
| 125 | + from_data: { | |
| 126 | + type: Array, | |
| 127 | + default: () => [] | |
| 128 | + }, | |
| 129 | + // 选中数据 | |
| 130 | + to_data: { | |
| 131 | + type: Array, | |
| 132 | + default: () => [] | |
| 133 | + }, | |
| 134 | + // el-tree 配置项 | |
| 135 | + defaultProps: { | |
| 136 | + type: Object, | |
| 137 | + default: () => { | |
| 138 | + return { label: "fullName", children: "children" }; | |
| 139 | + } | |
| 140 | + }, | |
| 141 | + // el-tree node-key 必须唯一 | |
| 142 | + node_key: { | |
| 143 | + type: String, | |
| 144 | + default: "id" | |
| 145 | + }, | |
| 146 | + // 自定义 pid参数名 | |
| 147 | + pid: { | |
| 148 | + type: String, | |
| 149 | + default: "parentId" | |
| 150 | + }, | |
| 151 | + // 是否启用筛选 | |
| 152 | + filter: { | |
| 153 | + type: Boolean, | |
| 154 | + default: false | |
| 155 | + }, | |
| 156 | + // 是否展开所有节点 | |
| 157 | + openAll: { | |
| 158 | + type: Boolean, | |
| 159 | + default: false | |
| 160 | + }, | |
| 161 | + // 左侧自定义树节点 | |
| 162 | + renderContentLeft: Function, | |
| 163 | + // 右侧自定义树节点 | |
| 164 | + renderContentRight: Function, | |
| 165 | + // 是否展开节点 | |
| 166 | + transferOpenNode: { | |
| 167 | + type: Boolean, | |
| 168 | + default: true | |
| 169 | + }, | |
| 170 | + // 源数据 默认选中节点 | |
| 171 | + defaultCheckedKeys: { | |
| 172 | + type: Array, | |
| 173 | + default: () => [] | |
| 174 | + }, | |
| 175 | + // 源数据 默认展开节点 | |
| 176 | + defaultExpandedKeys: { | |
| 177 | + type: Array, | |
| 178 | + default: () => [] | |
| 179 | + }, | |
| 180 | + // 筛选placeholder | |
| 181 | + placeholder: { | |
| 182 | + type: String, | |
| 183 | + default: "输入关键词" | |
| 184 | + }, | |
| 185 | + // 自定义筛选函数 | |
| 186 | + filterNode: Function, | |
| 187 | + // 默认穿梭一次默认选中数据 | |
| 188 | + defaultTransfer: { | |
| 189 | + type: Boolean, | |
| 190 | + default: false | |
| 191 | + }, | |
| 192 | + // 是否开启arrayToTree | |
| 193 | + arrayToTree: { | |
| 194 | + type: Boolean, | |
| 195 | + default: false | |
| 196 | + }, | |
| 197 | + // 是否启用懒加载 | |
| 198 | + lazy: { | |
| 199 | + type: Boolean, | |
| 200 | + default: false | |
| 201 | + }, | |
| 202 | + // 右侧是否启用懒加载 | |
| 203 | + lazyRight: { | |
| 204 | + type: Boolean, | |
| 205 | + default: false | |
| 206 | + }, | |
| 207 | + // 懒加载的回调函数 | |
| 208 | + lazyFn: Function, | |
| 209 | + // 是否高亮当前选中节点,默认值是 false。 | |
| 210 | + highLight: { | |
| 211 | + type: Boolean, | |
| 212 | + default: false | |
| 213 | + } | |
| 214 | + }, | |
| 215 | + created() { | |
| 216 | + this.from_check_keys = this.defaultCheckedKeys; | |
| 217 | + this.from_expanded_keys = this.defaultExpandedKeys; | |
| 218 | + this.to_expanded_keys = this.defaultExpandedKeys; | |
| 219 | + if (this.defaultTransfer && this.defaultCheckedKeys.length > 0) { | |
| 220 | + this.$nextTick(() => { | |
| 221 | + this.addToAims(false); | |
| 222 | + }); | |
| 223 | + } | |
| 224 | + }, | |
| 225 | + methods: { | |
| 226 | + // -------------------------------提供输出函数--------------------- | |
| 227 | + /** | |
| 228 | + * 清空选中节点 | |
| 229 | + * type:string left左边 right右边 all全部 默认all | |
| 230 | + */ | |
| 231 | + clearChecked(type = "all") { | |
| 232 | + if (type === "left") { | |
| 233 | + this.$refs["from-tree"].setCheckedKeys([]); | |
| 234 | + this.from_is_indeterminate = false; | |
| 235 | + this.from_check_all = false; | |
| 236 | + } else if (type === "right") { | |
| 237 | + this.$refs["to-tree"].setCheckedKeys([]); | |
| 238 | + this.to_is_indeterminate = false; | |
| 239 | + this.to_check_all = false; | |
| 240 | + } else { | |
| 241 | + this.$refs["from-tree"].setCheckedKeys([]); | |
| 242 | + this.$refs["to-tree"].setCheckedKeys([]); | |
| 243 | + this.from_is_indeterminate = false; | |
| 244 | + this.from_check_all = false; | |
| 245 | + this.to_is_indeterminate = false; | |
| 246 | + this.to_check_all = false; | |
| 247 | + } | |
| 248 | + }, | |
| 249 | + // 添加按钮 | |
| 250 | + addToAims(emit) { | |
| 251 | + // 获取选中通过穿梭框的keys - 仅用于传送纯净的id数组到父组件同后台通信 | |
| 252 | + let keys = this.$refs["from-tree"].getCheckedKeys(); | |
| 253 | + // 获取半选通过穿梭框的keys - 仅用于传送纯净的id数组到父组件同后台通信 | |
| 254 | + let harfKeys = this.$refs["from-tree"].getHalfCheckedKeys(); | |
| 255 | + // 选中节点数据 | |
| 256 | + let arrayCheckedNodes = this.$refs["from-tree"].getCheckedNodes(); | |
| 257 | + // 获取选中通过穿梭框的nodes - 仅用于传送选中节点数组到父组件同后台通信需求 | |
| 258 | + let nodes = JSON.parse(JSON.stringify(arrayCheckedNodes)); | |
| 259 | + // 半选中节点数据 | |
| 260 | + let arrayHalfCheckedNodes = this.$refs["from-tree"].getHalfCheckedNodes(); | |
| 261 | + // 获取半选通过穿梭框的nodes - 仅用于传送选中节点数组到父组件同后台通信需求 | |
| 262 | + let halfNodes = JSON.parse(JSON.stringify(arrayHalfCheckedNodes)); | |
| 263 | + | |
| 264 | + // 自定义参数读取设置 | |
| 265 | + let children__ = this.defaultProps.children || "children"; | |
| 266 | + let pid__ = this.pid || "parentId"; | |
| 267 | + let id__ = this["node_key"] || "id"; | |
| 268 | + | |
| 269 | + /* | |
| 270 | + * 先整合目标树没有父节点的叶子节点选中,需要整理出来此叶子节点的父节点直到根节点路径 - 此时所有骨架节点已有 | |
| 271 | + * 再将所有末端叶子节点根据pid直接推入目标树即可 | |
| 272 | + * 声明新盒子将所有半选节点的子节点清除 - 只保留骨架 因为排序是先父后子 因此不存在子元素处理好插入时父元素还没处理的情况 | |
| 273 | + * 下面一二步是为了搭建出来目标树没有根节点躯干节点时的叶子选中,给此叶子搭建出根节点和躯干节点 | |
| 274 | + */ | |
| 275 | + | |
| 276 | + // let不存在状态提升 因此在函数调用之前赋值 并递归为以为数组! | |
| 277 | + let self_to_data = JSON.stringify(this.self_to_data); | |
| 278 | + // 第一步 | |
| 279 | + let skeletonHalfCheckedNodes = JSON.parse( | |
| 280 | + JSON.stringify(arrayHalfCheckedNodes) | |
| 281 | + ); // 深拷贝数据 - 半选节点 | |
| 282 | + // 筛选目标树不存在的骨架节点 - 半选内的节点 | |
| 283 | + let newSkeletonHalfCheckedNodes = []; | |
| 284 | + skeletonHalfCheckedNodes.forEach(item => { | |
| 285 | + if (!inquireIsExist(item)) { | |
| 286 | + newSkeletonHalfCheckedNodes.push(item); | |
| 287 | + } | |
| 288 | + }); | |
| 289 | + // 筛选到目标树不存在的骨架后在处理每个骨架节点-非末端叶子节点 - 半选节点 | |
| 290 | + newSkeletonHalfCheckedNodes.forEach(item => { | |
| 291 | + item[children__] = []; | |
| 292 | + [0, "-1"].includes(item[pid__]) | |
| 293 | + ? this.$refs["to-tree"].append(item) | |
| 294 | + : this.$refs["to-tree"].append(item, item[pid__]); | |
| 295 | + }); | |
| 296 | + | |
| 297 | + // 第二步 | |
| 298 | + // 筛选目标树不存在的骨架节点 - 全选内的节点 | |
| 299 | + let newSkeletonCheckedNodes = []; | |
| 300 | + nodes.forEach(item => { | |
| 301 | + if (!inquireIsExist(item)) { | |
| 302 | + newSkeletonCheckedNodes.push(item); | |
| 303 | + } | |
| 304 | + }); | |
| 305 | + // 筛选到目标树不存在的骨架后在处理每个骨架节点-非末端叶子节点 - 全选节点 | |
| 306 | + newSkeletonCheckedNodes.forEach(item => { | |
| 307 | + if (item[children__] && item[children__].length > 0) { | |
| 308 | + item[children__] = []; | |
| 309 | + [0, "-1"].includes(item[pid__]) | |
| 310 | + ? this.$refs["to-tree"].append(item) | |
| 311 | + : this.$refs["to-tree"].append(item, item[pid__]); | |
| 312 | + } | |
| 313 | + }); | |
| 314 | + | |
| 315 | + // 第三步 处理末端叶子元素 - 声明新盒子筛选出所有末端叶子节点 | |
| 316 | + let leafCheckedNodes = arrayCheckedNodes.filter( | |
| 317 | + item => !item[children__] || item[children__].length === 0 | |
| 318 | + ); | |
| 319 | + // 末端叶子插入目标树 | |
| 320 | + leafCheckedNodes.forEach(item => { | |
| 321 | + if (!inquireIsExist(item)) { | |
| 322 | + this.$refs["to-tree"].append(item, item[pid__]); | |
| 323 | + } | |
| 324 | + }); | |
| 325 | + | |
| 326 | + // 递归查询data内是否存在item函数 | |
| 327 | + function inquireIsExist(item, strData = self_to_data) { | |
| 328 | + // 将树形数据格式化成一维字符串 然后通过匹配来判断是否已存在 | |
| 329 | + let strItem = | |
| 330 | + typeof item[id__] == "number" | |
| 331 | + ? `"${id__}":${item[id__]},` | |
| 332 | + : `"${id__}":"${item[id__]}"`; | |
| 333 | + let reg = RegExp(strItem); | |
| 334 | + let existed = reg.test(strData); | |
| 335 | + return existed; | |
| 336 | + } | |
| 337 | + | |
| 338 | + // 左侧删掉选中数据 | |
| 339 | + arrayCheckedNodes.map(item => this.$refs["from-tree"].remove(item)); | |
| 340 | + | |
| 341 | + // 处理完毕按钮恢复禁用状态 | |
| 342 | + this.from_check_keys = []; | |
| 343 | + | |
| 344 | + // 目标数据节点展开 | |
| 345 | + if (this.transferOpenNode && !this.lazy) { | |
| 346 | + this.to_expanded_keys = keys; | |
| 347 | + } | |
| 348 | + | |
| 349 | + // 传递信息给父组件 | |
| 350 | + emit && | |
| 351 | + this.$emit("addBtn", this.self_from_data, this.self_to_data, { | |
| 352 | + keys, | |
| 353 | + nodes, | |
| 354 | + harfKeys, | |
| 355 | + halfNodes | |
| 356 | + }); | |
| 357 | + | |
| 358 | + // 处理完毕取消选中 | |
| 359 | + this.$refs["from-tree"].setCheckedKeys([]); | |
| 360 | + }, | |
| 361 | + // 移除按钮 | |
| 362 | + removeToSource() { | |
| 363 | + // 获取选中通过穿梭框的keys - 仅用于传送纯净的id数组到父组件同后台通信 | |
| 364 | + let keys = this.$refs["to-tree"].getCheckedKeys(); | |
| 365 | + // 获取半选通过穿梭框的keys - 仅用于传送纯净的id数组到父组件同后台通信 | |
| 366 | + let harfKeys = this.$refs["to-tree"].getHalfCheckedKeys(); | |
| 367 | + // 获取选中通过穿梭框的nodes 选中节点数据 | |
| 368 | + let arrayCheckedNodes = this.$refs["to-tree"].getCheckedNodes(); | |
| 369 | + // 获取选中通过穿梭框的nodes - 仅用于传送选中节点数组到父组件同后台通信需求 | |
| 370 | + let nodes = JSON.parse(JSON.stringify(arrayCheckedNodes)); | |
| 371 | + // 半选中节点数据 | |
| 372 | + let arrayHalfCheckedNodes = this.$refs["to-tree"].getHalfCheckedNodes(); | |
| 373 | + // 获取半选通过穿梭框的nodes - 仅用于传送选中节点数组到父组件同后台通信需求 | |
| 374 | + let halfNodes = JSON.parse(JSON.stringify(arrayHalfCheckedNodes)); | |
| 375 | + | |
| 376 | + // 自定义参数读取设置 | |
| 377 | + let children__ = this.defaultProps.children || "children"; | |
| 378 | + let pid__ = this.pid || "parentId"; | |
| 379 | + let id__ = this["node_key"] || "id"; | |
| 380 | + | |
| 381 | + /* | |
| 382 | + * 先整合目标树没有父节点的叶子节点选中,需要整理出来此叶子节点的父节点直到根节点路径 - 此时所有骨架节点已有 | |
| 383 | + * 再将所有末端叶子节点根据pid直接推入目标树即可 | |
| 384 | + * 声明新盒子将所有半选节点的子节点清除 - 只保留骨架 因为排序是先父后子 因此不存在子元素处理好插入时父元素还没处理的情况 | |
| 385 | + * 下面一二步是为了搭建出来目标树没有根节点躯干节点时的叶子选中,给此叶子搭建出根节点和躯干节点 | |
| 386 | + */ | |
| 387 | + | |
| 388 | + // let不存在状态提升 因此在函数调用之前赋值 并递归为以为数组! | |
| 389 | + let self_from_data = JSON.stringify(this.self_from_data); | |
| 390 | + // 第一步 | |
| 391 | + let skeletonHalfCheckedNodes = JSON.parse( | |
| 392 | + JSON.stringify(arrayHalfCheckedNodes) | |
| 393 | + ); // 深拷贝数据 - 半选节点 | |
| 394 | + // 筛选目标树不存在的骨架节点 - 半选内的节点 | |
| 395 | + let newSkeletonHalfCheckedNodes = []; | |
| 396 | + skeletonHalfCheckedNodes.forEach(item => { | |
| 397 | + if (!inquireIsExist(item)) { | |
| 398 | + newSkeletonHalfCheckedNodes.push(item); | |
| 399 | + } | |
| 400 | + }); | |
| 401 | + // 筛选到目标树不存在的骨架后在处理每个骨架节点-非末端叶子节点 - 半选节点 | |
| 402 | + newSkeletonHalfCheckedNodes.forEach(item => { | |
| 403 | + item[children__] = []; | |
| 404 | + [0, "-1"].includes(item[pid__]) | |
| 405 | + ? this.$refs["from-tree"].append(item) | |
| 406 | + : this.$refs["from-tree"].append(item, item[pid__]); | |
| 407 | + }); | |
| 408 | + | |
| 409 | + // 第二步 | |
| 410 | + // 筛选目标树不存在的骨架节点 - 全选内的节点 | |
| 411 | + let newSkeletonCheckedNodes = []; | |
| 412 | + nodes.forEach(item => { | |
| 413 | + if (!inquireIsExist(item)) { | |
| 414 | + newSkeletonCheckedNodes.push(item); | |
| 415 | + } | |
| 416 | + }); | |
| 417 | + // 筛选到目标树不存在的骨架后在处理每个骨架节点-非末端叶子节点 - 全选节点 | |
| 418 | + newSkeletonCheckedNodes.forEach(item => { | |
| 419 | + if (item[children__] && item[children__].length > 0) { | |
| 420 | + item[children__] = []; | |
| 421 | + [0, "-1"].includes(item[pid__]) | |
| 422 | + ? this.$refs["from-tree"].append(item) | |
| 423 | + : this.$refs["from-tree"].append(item, item[pid__]); | |
| 424 | + } | |
| 425 | + }); | |
| 426 | + | |
| 427 | + // 第三步 处理末端叶子元素 - 声明新盒子筛选出所有末端叶子节点 | |
| 428 | + let leafCheckedNodes = arrayCheckedNodes.filter( | |
| 429 | + item => !item[children__] || item[children__].length == 0 | |
| 430 | + ); | |
| 431 | + // 末端叶子插入目标树 | |
| 432 | + leafCheckedNodes.forEach(item => { | |
| 433 | + if (!inquireIsExist(item)) { | |
| 434 | + this.$refs["from-tree"].append(item, item[pid__]); | |
| 435 | + } | |
| 436 | + }); | |
| 437 | + | |
| 438 | + // 递归查询data内是否存在item函数 | |
| 439 | + function inquireIsExist(item, strData = self_from_data) { | |
| 440 | + // 将树形数据格式化成一维字符串 然后通过匹配来判断是否已存在 | |
| 441 | + let strItem = | |
| 442 | + typeof item[id__] == "number" | |
| 443 | + ? `"${id__}":${item[id__]},` | |
| 444 | + : `"${id__}":"${item[id__]}"`; | |
| 445 | + let reg = RegExp(strItem); | |
| 446 | + let existed = reg.test(strData); | |
| 447 | + return existed; | |
| 448 | + } | |
| 449 | + | |
| 450 | + // 右侧删掉选中数据 | |
| 451 | + arrayCheckedNodes.map(item => this.$refs["to-tree"].remove(item)); | |
| 452 | + | |
| 453 | + // 处理完毕按钮恢复禁用状态 | |
| 454 | + this.to_check_keys = []; | |
| 455 | + | |
| 456 | + // 目标数据节点展开 | |
| 457 | + if (this.transferOpenNode && !this.lazy) { | |
| 458 | + this.from_expanded_keys = keys; | |
| 459 | + } | |
| 460 | + | |
| 461 | + // 传递信息给父组件 | |
| 462 | + this.$emit("removeBtn", this.self_from_data, this.self_to_data, { | |
| 463 | + keys, | |
| 464 | + nodes, | |
| 465 | + harfKeys, | |
| 466 | + halfNodes | |
| 467 | + }); | |
| 468 | + // 处理完毕取消选中 | |
| 469 | + this.$refs["to-tree"].setCheckedKeys([]); | |
| 470 | + }, | |
| 471 | + // 异步加载左侧 | |
| 472 | + leftloadNode(node, resolve) { | |
| 473 | + if (node.level === 0) { | |
| 474 | + return resolve(this.self_from_data); | |
| 475 | + } | |
| 476 | + this.lazyFn && this.lazyFn(node, resolve, "left"); | |
| 477 | + }, | |
| 478 | + // 异步加载右侧 | |
| 479 | + rightloadNode(node, resolve) { | |
| 480 | + if (node.level === 0) { | |
| 481 | + return resolve(this.self_to_data); | |
| 482 | + } | |
| 483 | + | |
| 484 | + this.lazyFn && this.lazyFn(node, resolve, "right"); | |
| 485 | + }, | |
| 486 | + // 源树选中事件 - 是否禁用穿梭按钮 | |
| 487 | + fromTreeChecked(nodeObj, treeObj) { | |
| 488 | + this.from_check_keys = treeObj.checkedNodes | |
| 489 | + this.$nextTick(() => { | |
| 490 | + | |
| 491 | + this.$emit("left-check-change", nodeObj, treeObj, this.from_check_all) | |
| 492 | + }) | |
| 493 | + }, | |
| 494 | + // 目标树选中事件 - 是否禁用穿梭按钮 | |
| 495 | + toTreeChecked(nodeObj, treeObj) { | |
| 496 | + this.to_check_keys = treeObj.checkedNodes; | |
| 497 | + this.$nextTick(() => { | |
| 498 | + this.$emit("right-check-change", nodeObj, treeObj, this.to_check_all); | |
| 499 | + }); | |
| 500 | + }, | |
| 501 | + // 源数据 总全选checkbox | |
| 502 | + fromAllBoxChange(val) { | |
| 503 | + if (this.self_from_data.length === 0) { | |
| 504 | + return; | |
| 505 | + } | |
| 506 | + if (val) { | |
| 507 | + this.from_check_keys = this.self_from_data; | |
| 508 | + this.$refs["from-tree"].setCheckedNodes(this.self_from_data); | |
| 509 | + } else { | |
| 510 | + this.$refs["from-tree"].setCheckedNodes([]); | |
| 511 | + this.from_check_keys = []; | |
| 512 | + } | |
| 513 | + this.$emit("left-check-change", null, null, this.from_check_all); | |
| 514 | + }, | |
| 515 | + // 目标数据 总全选checkbox | |
| 516 | + toAllBoxChange(val) { | |
| 517 | + if (this.self_to_data.length === 0) { | |
| 518 | + return; | |
| 519 | + } | |
| 520 | + if (val) { | |
| 521 | + this.to_check_keys = this.self_to_data; | |
| 522 | + this.$refs["to-tree"].setCheckedNodes(this.self_to_data); | |
| 523 | + } else { | |
| 524 | + this.$refs["to-tree"].setCheckedNodes([]); | |
| 525 | + this.to_check_keys = []; | |
| 526 | + } | |
| 527 | + this.$emit("right-check-change", null, null, this.to_check_all); | |
| 528 | + }, | |
| 529 | + // 源数据 筛选 | |
| 530 | + filterNodeFrom(value, data) { | |
| 531 | + if (this.filterNode) { | |
| 532 | + return this.filterNode(value, data, "form"); | |
| 533 | + } | |
| 534 | + if (!value) return true; | |
| 535 | + return data[this.defaultProps.label].indexOf(value) !== -1; | |
| 536 | + }, | |
| 537 | + // 目标数据筛选 | |
| 538 | + filterNodeTo(value, data) { | |
| 539 | + if (this.filterNode) { | |
| 540 | + return this.filterNode(value, data, "to"); | |
| 541 | + } | |
| 542 | + if (!value) return true; | |
| 543 | + return data[this.defaultProps.label].indexOf(value) !== -1; | |
| 544 | + }, | |
| 545 | + }, | |
| 546 | + computed: { | |
| 547 | + // 左侧数据 | |
| 548 | + self_from_data() { | |
| 549 | + let from_array = [...this.from_data]; | |
| 550 | + if (!this.arrayToTree) { | |
| 551 | + from_array.forEach(item => { | |
| 552 | + item[this.pid] = 0; | |
| 553 | + }); | |
| 554 | + return from_array; | |
| 555 | + } else { | |
| 556 | + return arrayToTree(from_array, { | |
| 557 | + id: this.node_key, | |
| 558 | + pid: this.pid, | |
| 559 | + children: this.defaultProps.children | |
| 560 | + }); | |
| 561 | + } | |
| 562 | + }, | |
| 563 | + // 右侧数据 | |
| 564 | + self_to_data() { | |
| 565 | + let to_array = [...this.to_data]; | |
| 566 | + if (!this.arrayToTree) { | |
| 567 | + to_array.forEach(item => { | |
| 568 | + item[this.pid] = 0; | |
| 569 | + }); | |
| 570 | + return to_array; | |
| 571 | + } else { | |
| 572 | + return arrayToTree(to_array, { | |
| 573 | + id: this.node_key, | |
| 574 | + pid: this.pid, | |
| 575 | + children: this.defaultProps.children | |
| 576 | + }); | |
| 577 | + } | |
| 578 | + }, | |
| 579 | + // 左侧菜单名 | |
| 580 | + fromTitle() { | |
| 581 | + let [text] = this.title; | |
| 582 | + return text; | |
| 583 | + }, | |
| 584 | + // 右侧菜单名 | |
| 585 | + toTitle() { | |
| 586 | + let [, text] = this.title; | |
| 587 | + return text; | |
| 588 | + }, | |
| 589 | + // 右侧菜单名2 | |
| 590 | + toTitleSecond() { | |
| 591 | + let [, , text] = this.title; | |
| 592 | + return text; | |
| 593 | + }, | |
| 594 | + // 右侧菜单名3 | |
| 595 | + toTitleThird() { | |
| 596 | + let [, , , text] = this.title; | |
| 597 | + return text; | |
| 598 | + }, | |
| 599 | + // 上部按钮名 | |
| 600 | + fromButton() { | |
| 601 | + if (this.button_text == undefined) { | |
| 602 | + return; | |
| 603 | + } | |
| 604 | + | |
| 605 | + let [text] = this.button_text; | |
| 606 | + return text; | |
| 607 | + }, | |
| 608 | + // 下部按钮名 | |
| 609 | + toButton() { | |
| 610 | + if (this.button_text == undefined) { | |
| 611 | + return; | |
| 612 | + } | |
| 613 | + let [, text] = this.button_text; | |
| 614 | + return text; | |
| 615 | + } | |
| 616 | + }, | |
| 617 | + watch: { | |
| 618 | + // 左侧 状态监测 | |
| 619 | + from_check_keys(val) { | |
| 620 | + if (val.length > 0) { | |
| 621 | + // 穿梭按钮是否禁用 | |
| 622 | + this.from_disabled = false; | |
| 623 | + // 总半选是否开启 | |
| 624 | + this.from_is_indeterminate = true; | |
| 625 | + | |
| 626 | + // 总全选是否开启 - 根据选中节点中为根节点的数量是否和源数据长度相等 | |
| 627 | + let allCheck = val.filter(item => item[this.pid] === 0); | |
| 628 | + if (allCheck.length == this.self_from_data.length) { | |
| 629 | + // 关闭半选 开启全选 | |
| 630 | + this.from_is_indeterminate = false; | |
| 631 | + this.from_check_all = true; | |
| 632 | + } else { | |
| 633 | + this.from_is_indeterminate = true; | |
| 634 | + this.from_check_all = false; | |
| 635 | + } | |
| 636 | + } else { | |
| 637 | + this.from_disabled = true; | |
| 638 | + this.from_is_indeterminate = false; | |
| 639 | + this.from_check_all = false; | |
| 640 | + } | |
| 641 | + }, | |
| 642 | + // 右侧 状态监测 | |
| 643 | + to_check_keys(val) { | |
| 644 | + if (val.length > 0) { | |
| 645 | + // 穿梭按钮是否禁用 | |
| 646 | + this.to_disabled = false; | |
| 647 | + // 总半选是否开启 | |
| 648 | + this.to_is_indeterminate = true; | |
| 649 | + | |
| 650 | + // 总全选是否开启 - 根据选中节点中为根节点的数量是否和源数据长度相等 | |
| 651 | + let allCheck = val.filter(item => item[this.pid] === 0); | |
| 652 | + if (allCheck.length == this.self_to_data.length) { | |
| 653 | + // 关闭半选 开启全选 | |
| 654 | + this.to_is_indeterminate = false; | |
| 655 | + this.to_check_all = true; | |
| 656 | + } else { | |
| 657 | + this.to_is_indeterminate = true; | |
| 658 | + this.to_check_all = false; | |
| 659 | + } | |
| 660 | + } else { | |
| 661 | + this.to_disabled = true; | |
| 662 | + this.to_is_indeterminate = false; | |
| 663 | + this.to_check_all = false; | |
| 664 | + } | |
| 665 | + }, | |
| 666 | + // 左侧 数据筛选 | |
| 667 | + filterFrom(val) { | |
| 668 | + this.$refs["from-tree"].filter(val); | |
| 669 | + }, | |
| 670 | + // 右侧 数据筛选 | |
| 671 | + filterTo(val) { | |
| 672 | + this.$refs["to-tree"].filter(val); | |
| 673 | + }, | |
| 674 | + // 监视默认选中 | |
| 675 | + defaultCheckedKeys(val) { | |
| 676 | + if (this.defaultTransfer && val.length > 0) { | |
| 677 | + this.$nextTick(() => { | |
| 678 | + this.addToAims(false); | |
| 679 | + }); | |
| 680 | + } | |
| 681 | + }, | |
| 682 | + // 监视默认展开 | |
| 683 | + defaultExpandedKeys(val) { | |
| 684 | + let _form = new Set(this.from_expanded_keys.concat(val)); | |
| 685 | + this.from_expanded_keys = [..._form]; | |
| 686 | + let _to = new Set(this.to_expanded_keys.concat(val)); | |
| 687 | + this.to_expanded_keys = [..._to]; | |
| 688 | + } | |
| 689 | + } | |
| 690 | +}; | |
| 691 | +</script> | |
| 692 | + | |
| 693 | +<style lang="scss" scoped> | |
| 694 | +.el-tree { | |
| 695 | + min-width: 100%; | |
| 696 | + display: inline-block !important; | |
| 697 | +} | |
| 698 | + | |
| 699 | +.transfer { | |
| 700 | + position: relative; | |
| 701 | + overflow: hidden; | |
| 702 | +} | |
| 703 | + | |
| 704 | +.transfer-left { | |
| 705 | + position: absolute; | |
| 706 | + top: 0; | |
| 707 | + left: 0; | |
| 708 | +} | |
| 709 | + | |
| 710 | +.transfer-right { | |
| 711 | + position: absolute; | |
| 712 | + top: 0; | |
| 713 | + right: 0; | |
| 714 | +} | |
| 715 | + | |
| 716 | +.transfer-right-item { | |
| 717 | + height: calc((100% - 41px) / 2); | |
| 718 | +} | |
| 719 | + | |
| 720 | +.transfer-right-small { | |
| 721 | + height: 41px; | |
| 722 | +} | |
| 723 | + | |
| 724 | +.transfer-right-only { | |
| 725 | + height: 100%; | |
| 726 | +} | |
| 727 | + | |
| 728 | +.transfer-main { | |
| 729 | + padding: 10px; | |
| 730 | + // height: calc(100% - 41px); | |
| 731 | + box-sizing: border-box; | |
| 732 | + overflow: hidden; | |
| 733 | + .el-tree { | |
| 734 | + height: calc(100vh - 590px); | |
| 735 | + overflow: auto; | |
| 736 | + } | |
| 737 | +} | |
| 738 | + | |
| 739 | +.transfer-left, | |
| 740 | +.transfer-right { | |
| 741 | + border: 1px solid #ebeef5; | |
| 742 | + width: 40%; | |
| 743 | + height: 100%; | |
| 744 | + box-sizing: border-box; | |
| 745 | + border-radius: 5px; | |
| 746 | + vertical-align: middle; | |
| 747 | +} | |
| 748 | + | |
| 749 | +.transfer-center { | |
| 750 | + position: absolute; | |
| 751 | + top: 50%; | |
| 752 | + left: 40%; | |
| 753 | + width: 20%; | |
| 754 | + transform: translateY(-50%); | |
| 755 | + text-align: center; | |
| 756 | +} | |
| 757 | + | |
| 758 | +.transfer-center-item { | |
| 759 | + padding: 10px; | |
| 760 | + overflow: hidden; | |
| 761 | +} | |
| 762 | + | |
| 763 | +.address-list-center { | |
| 764 | + height: 100%; | |
| 765 | +} | |
| 766 | + | |
| 767 | +.address-list-center > .transfer-center-item { | |
| 768 | + height: 50%; | |
| 769 | + padding: 70px 10px 0; | |
| 770 | + box-sizing: border-box; | |
| 771 | + overflow: hidden; | |
| 772 | +} | |
| 773 | + | |
| 774 | +.address-list-center > .address-only-item { | |
| 775 | + height: 100%; | |
| 776 | + position: relative; | |
| 777 | +} | |
| 778 | + | |
| 779 | +.address-only-item > .address-first-btn { | |
| 780 | + position: absolute; | |
| 781 | + top: 50%; | |
| 782 | + left: 50%; | |
| 783 | + transform: translate(-50%, -50%); | |
| 784 | +} | |
| 785 | + | |
| 786 | +.transfer-title { | |
| 787 | + border-bottom: 1px solid #ebeef5; | |
| 788 | + padding: 0 15px; | |
| 789 | + height: 40px; | |
| 790 | + line-height: 40px; | |
| 791 | + color: #333; | |
| 792 | + font-size: 16px; | |
| 793 | + background-color: #f5f7fa; | |
| 794 | + font-weight: normal; | |
| 795 | +} | |
| 796 | + | |
| 797 | +.transfer-title .el-checkbox { | |
| 798 | + margin-right: 10px; | |
| 799 | +} | |
| 800 | + | |
| 801 | +.filter-tree { | |
| 802 | + margin-bottom: 10px; | |
| 803 | +} | |
| 804 | + | |
| 805 | +.u-clear { | |
| 806 | + float: right; | |
| 807 | + color: #67c23a; | |
| 808 | + font-size: 14px; | |
| 809 | + cursor: pointer; | |
| 810 | +} | |
| 811 | +</style> | ... | ... |
src/components/NCC-enlarge/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="enlarge-main"> | |
| 3 | + <img :src="img" class="enlarge-img" @click="dialogVisible = true" title="点击查看放大" /> | |
| 4 | + <el-dialog :visible.sync="dialogVisible" append-to-body width="600px" | |
| 5 | + class="NCC-dialog NCC-dialog_center enlarge-dialog"> | |
| 6 | + <img width="100%" :src="img" alt /> | |
| 7 | + </el-dialog> | |
| 8 | + </div> | |
| 9 | +</template> | |
| 10 | + | |
| 11 | +<script> | |
| 12 | +export default { | |
| 13 | + props: ["img"], | |
| 14 | + data() { | |
| 15 | + return { | |
| 16 | + dialogVisible: false | |
| 17 | + } | |
| 18 | + } | |
| 19 | +} | |
| 20 | +</script> | |
| 21 | + | |
| 22 | +<style lang="scss" scoped> | |
| 23 | +.enlarge-main { | |
| 24 | + cursor: pointer; | |
| 25 | + display: inline-block; | |
| 26 | + width: 120px; | |
| 27 | + height: 120px; | |
| 28 | + overflow: hidden; | |
| 29 | + margin: 0 8px 8px 0; | |
| 30 | + border: 1px solid #c0ccda; | |
| 31 | + border-radius: 6px; | |
| 32 | + -webkit-box-sizing: border-box; | |
| 33 | + box-sizing: border-box; | |
| 34 | + .enlarge-img { | |
| 35 | + width: 120px; | |
| 36 | + height: 120px; | |
| 37 | + object-fit: contain; | |
| 38 | + } | |
| 39 | +} | |
| 40 | +</style> | |
| 0 | 41 | \ No newline at end of file | ... | ... |
src/components/NCC-tableOperation/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="ncc-table-opts"> | |
| 3 | + <!--左侧插槽--> | |
| 4 | + <slot name="left" /> | |
| 5 | + <template v-if="isJudgePer"> | |
| 6 | + <el-button size="mini" type="text" @click="edit()" v-if="hasEdit" :disabled="editDisabled" | |
| 7 | + v-has="editPerCode">{{ editText === '编辑' ? $t(`common.editBtn`) : editText }}</el-button> | |
| 8 | + </template> | |
| 9 | + <template v-else> | |
| 10 | + <el-button size="mini" type="text" @click="edit()" v-if="hasEdit" :disabled="editDisabled"> | |
| 11 | + {{ editText === '编辑' ? $t(`common.editBtn`) : editText }}</el-button> | |
| 12 | + </template> | |
| 13 | + <!-- 中间插槽 --> | |
| 14 | + <slot name="center" /> | |
| 15 | + <template v-if="isJudgePer"> | |
| 16 | + <el-button size="mini" type="text" class="NCC-table-delBtn" @click="del()" v-if="hasDel" | |
| 17 | + :disabled="delDisabled" v-has="delPerCode"> | |
| 18 | + {{ delText === '删除' ? $t(`common.delBtn`) : delText }}</el-button> | |
| 19 | + </template> | |
| 20 | + <template v-else> | |
| 21 | + <el-button size="mini" type="text" class="NCC-table-delBtn" @click="del()" v-if="hasDel" | |
| 22 | + :disabled="delDisabled">{{ delText === '删除' ? $t(`common.delBtn`) : delText }}</el-button> | |
| 23 | + </template> | |
| 24 | + <!-- 默认右侧插槽 --> | |
| 25 | + <slot /> | |
| 26 | + </div> | |
| 27 | +</template> | |
| 28 | +<script> | |
| 29 | +export default { | |
| 30 | + props: { | |
| 31 | + delText: { | |
| 32 | + type: String, | |
| 33 | + default: '删除' | |
| 34 | + }, | |
| 35 | + editText: { | |
| 36 | + type: String, | |
| 37 | + default: '编辑' | |
| 38 | + }, | |
| 39 | + // 是否展示编辑按钮 | |
| 40 | + hasEdit: { | |
| 41 | + type: Boolean, | |
| 42 | + default: true | |
| 43 | + }, | |
| 44 | + // 是否展示删除按钮 | |
| 45 | + hasDel: { | |
| 46 | + type: Boolean, | |
| 47 | + default: true | |
| 48 | + }, | |
| 49 | + editDisabled: { | |
| 50 | + type: Boolean, | |
| 51 | + default: false | |
| 52 | + }, | |
| 53 | + delDisabled: { | |
| 54 | + type: Boolean, | |
| 55 | + default: false | |
| 56 | + }, | |
| 57 | + // 编辑按钮权限标识 | |
| 58 | + editPerCode: { | |
| 59 | + type: String, | |
| 60 | + default: 'btn_edit' | |
| 61 | + }, | |
| 62 | + // 删除按钮权限标识 | |
| 63 | + delPerCode: { | |
| 64 | + type: String, | |
| 65 | + default: 'btn_remove' | |
| 66 | + }, | |
| 67 | + // 是否开启权限判断 | |
| 68 | + isJudgePer: { | |
| 69 | + type: Boolean, | |
| 70 | + default: false | |
| 71 | + }, | |
| 72 | + }, | |
| 73 | + data() { | |
| 74 | + return {} | |
| 75 | + }, | |
| 76 | + methods: { | |
| 77 | + del() { | |
| 78 | + this.$emit('del') | |
| 79 | + }, | |
| 80 | + edit() { | |
| 81 | + this.$emit('edit') | |
| 82 | + } | |
| 83 | + } | |
| 84 | +} | |
| 85 | +</script> | |
| 0 | 86 | \ No newline at end of file | ... | ... |
src/components/NCC-topOperation/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="ncc-opts"> | |
| 3 | + <!--左侧插槽--> | |
| 4 | + <slot name="left" /> | |
| 5 | + <template v-if="isJudgePer"> | |
| 6 | + <el-button type="primary" @click="add" icon="el-icon-plus" v-has="addPerCode"> | |
| 7 | + {{ addText === '新建' ? $t(`common.addBtn`) : addText }} | |
| 8 | + </el-button> | |
| 9 | + </template> | |
| 10 | + <template v-else> | |
| 11 | + <el-button type="primary" @click="add" icon="el-icon-plus"> | |
| 12 | + {{ addText === '新建' ? $t(`common.addBtn`) : addText }} | |
| 13 | + </el-button> | |
| 14 | + </template> | |
| 15 | + <!-- 默认右侧插槽 --> | |
| 16 | + <slot /> | |
| 17 | + </div> | |
| 18 | +</template> | |
| 19 | +<script> | |
| 20 | +export default { | |
| 21 | + props: { | |
| 22 | + refreshText: { | |
| 23 | + type: String, | |
| 24 | + default: '刷新' | |
| 25 | + }, | |
| 26 | + addText: { | |
| 27 | + type: String, | |
| 28 | + default: '新建' | |
| 29 | + }, | |
| 30 | + // 刷新加载状态 | |
| 31 | + loading: { | |
| 32 | + type: Boolean, | |
| 33 | + default: false | |
| 34 | + }, | |
| 35 | + // 是否展示刷新按钮 | |
| 36 | + hasRefresh: { | |
| 37 | + type: Boolean, | |
| 38 | + default: true | |
| 39 | + }, | |
| 40 | + // 新增按钮权限标识 | |
| 41 | + addPerCode: { | |
| 42 | + type: String, | |
| 43 | + default: 'btn_add' | |
| 44 | + }, | |
| 45 | + // 是否开启权限判断 | |
| 46 | + isJudgePer: { | |
| 47 | + type: Boolean, | |
| 48 | + default: false | |
| 49 | + } | |
| 50 | + }, | |
| 51 | + data() { | |
| 52 | + return {} | |
| 53 | + }, | |
| 54 | + methods: { | |
| 55 | + refresh() { | |
| 56 | + this.$emit('refresh') | |
| 57 | + }, | |
| 58 | + add() { | |
| 59 | + this.$emit('add') | |
| 60 | + } | |
| 61 | + } | |
| 62 | +} | |
| 63 | +</script> | |
| 64 | +<style lang="scss" scoped> | |
| 65 | +.ncc-opts { | |
| 66 | + display: inline-block; | |
| 67 | +} | |
| 68 | +</style> | |
| 0 | 69 | \ No newline at end of file | ... | ... |
src/components/NCC-uploadBtn/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-upload :action="define.comUrl+url" :headers="{ Authorization: $store.getters.token}" | |
| 3 | + :on-success="handleSuccess" :before-upload="beforeUpload" :show-file-list="false" | |
| 4 | + class="upload-btn"> | |
| 5 | + <el-button :type="buttonType" icon="el-icon-upload2" :loading="loading">{{buttonText}} | |
| 6 | + </el-button> | |
| 7 | + </el-upload> | |
| 8 | +</template> | |
| 9 | + | |
| 10 | +<script> | |
| 11 | +export default { | |
| 12 | + name: 'NCC-uploadBtn', | |
| 13 | + data() { | |
| 14 | + return { | |
| 15 | + loading: false | |
| 16 | + } | |
| 17 | + }, | |
| 18 | + props: { | |
| 19 | + url: { | |
| 20 | + type: String, | |
| 21 | + default: "" | |
| 22 | + }, | |
| 23 | + buttonText: { | |
| 24 | + type: String, | |
| 25 | + default: '导入' | |
| 26 | + }, | |
| 27 | + buttonType: { | |
| 28 | + type: String, | |
| 29 | + default: 'text' | |
| 30 | + } | |
| 31 | + }, | |
| 32 | + methods: { | |
| 33 | + beforeUpload() { | |
| 34 | + this.loading = true | |
| 35 | + }, | |
| 36 | + handleSuccess(res) { | |
| 37 | + this.loading = false | |
| 38 | + if (res.code == 200) { | |
| 39 | + this.$message({ | |
| 40 | + message: res.msg, | |
| 41 | + type: 'success', | |
| 42 | + duration: 1000 | |
| 43 | + }) | |
| 44 | + this.$emit('on-success') | |
| 45 | + } else { | |
| 46 | + this.$message({ | |
| 47 | + message: res.msg, | |
| 48 | + type: 'error', | |
| 49 | + duration: 1000 | |
| 50 | + }) | |
| 51 | + } | |
| 52 | + } | |
| 53 | + } | |
| 54 | +}; | |
| 55 | +</script> | |
| 56 | +<style lang="scss" scoped> | |
| 57 | +.upload-btn { | |
| 58 | + display: inline-block; | |
| 59 | + margin: 0 10px; | |
| 60 | +} | |
| 61 | +</style> | |
| 0 | 62 | \ No newline at end of file | ... | ... |
src/components/NCC-userBox/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="'选择'+title" :close-on-click-modal="false" :visible.sync="visible" | |
| 3 | + class="NCC-dialog NCC-dialog_center NCC-dialog-tree" lock-scroll append-to-body | |
| 4 | + width='450px'> | |
| 5 | + <el-input placeholder="输入姓名或者编号进行过滤" v-model="keyword" clearable @keyup.enter.native="getList"> | |
| 6 | + <el-button slot="append" icon="el-icon-search" @click="getList"></el-button> | |
| 7 | + </el-input> | |
| 8 | + <el-tree :data="treeData" :props="props" highlight-current :expand-on-click-node="false" | |
| 9 | + check-on-click-node @node-click="handleNodeClick" class="NCC-common-el-tree" node-key="id" | |
| 10 | + v-loading="loading" lazy :load="loadNode"> | |
| 11 | + <span class="custom-tree-node" slot-scope="{ node, data }"> | |
| 12 | + <i :class="data.icon"></i> | |
| 13 | + <span class="text">{{node.label}}</span> | |
| 14 | + </span> | |
| 15 | + </el-tree> | |
| 16 | + <span slot="footer" class="dialog-footer"> | |
| 17 | + <el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button> | |
| 18 | + <el-button type="primary" @click="dataFormSubmit()">{{$t('common.confirmButton')}}</el-button> | |
| 19 | + </span> | |
| 20 | + </el-dialog> | |
| 21 | +</template> | |
| 22 | + | |
| 23 | +<script> | |
| 24 | +import { getImUserSelector } from '@/api/permission/user' | |
| 25 | +export default { | |
| 26 | + name: 'UserBox', | |
| 27 | + props: { | |
| 28 | + title: { | |
| 29 | + type: String, | |
| 30 | + default: '审批人' | |
| 31 | + } | |
| 32 | + }, | |
| 33 | + data() { | |
| 34 | + return { | |
| 35 | + visible: false, | |
| 36 | + id: '', | |
| 37 | + nodeId: '0', | |
| 38 | + props: { | |
| 39 | + children: 'children', | |
| 40 | + label: 'fullName', | |
| 41 | + value: 'id', | |
| 42 | + isLeaf: 'isLeaf' | |
| 43 | + }, | |
| 44 | + treeData: [], | |
| 45 | + loading: false, | |
| 46 | + keyword: '' | |
| 47 | + } | |
| 48 | + }, | |
| 49 | + methods: { | |
| 50 | + init() { | |
| 51 | + this.visible = true | |
| 52 | + this.keyword = '' | |
| 53 | + this.nodeId = '0' | |
| 54 | + this.getList() | |
| 55 | + }, | |
| 56 | + getList() { | |
| 57 | + this.loading = true | |
| 58 | + if (this.keyword) this.nodeId = '0' | |
| 59 | + getImUserSelector(this.nodeId, this.keyword).then(res => { | |
| 60 | + this.treeData = res.data.list | |
| 61 | + this.loading = false | |
| 62 | + }) | |
| 63 | + }, | |
| 64 | + loadNode(node, resolve) { | |
| 65 | + if (node.level === 0) { | |
| 66 | + this.nodeId = '0' | |
| 67 | + return resolve(this.treeData) | |
| 68 | + } | |
| 69 | + this.nodeId = node.data.id | |
| 70 | + getImUserSelector(this.nodeId).then(res => { | |
| 71 | + resolve(res.data.list) | |
| 72 | + }) | |
| 73 | + }, | |
| 74 | + handleNodeClick(data) { | |
| 75 | + if (data.type !== 'user') return | |
| 76 | + this.id = data.id | |
| 77 | + }, | |
| 78 | + dataFormSubmit() { | |
| 79 | + if (!this.id) { | |
| 80 | + this.$message({ | |
| 81 | + message: `请选择${this.title}`, | |
| 82 | + type: 'error', | |
| 83 | + duration: 1000, | |
| 84 | + }) | |
| 85 | + return | |
| 86 | + } | |
| 87 | + this.visible = false | |
| 88 | + this.$emit('submit', this.id) | |
| 89 | + }, | |
| 90 | + } | |
| 91 | +} | |
| 92 | +</script> | |
| 0 | 93 | \ No newline at end of file | ... | ... |
src/components/NCC-userSelect/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="userSelect-container"> | |
| 3 | + <div class="userSelect-input" @click="openDialog"> | |
| 4 | + <el-input :placeholder="placeholder" v-model="innerValue" readonly :disabled="disabled"> | |
| 5 | + <i slot="suffix" class="el-input__icon el-icon-circle-close" @click.stop="clear" | |
| 6 | + v-if="clearable"></i> | |
| 7 | + <i slot="suffix" class="el-input__icon el-icon-arrow-down" | |
| 8 | + :class="{'clearable':clearable}"></i> | |
| 9 | + </el-input> | |
| 10 | + </div> | |
| 11 | + <el-dialog title="选择用户" :close-on-click-modal="false" :visible.sync="visible" | |
| 12 | + class="NCC-dialog NCC-dialog_center transfer-dialog" lock-scroll append-to-body | |
| 13 | + width="800px" :modal-append-to-body="false"> | |
| 14 | + <div class="transfer__body" :element-loading-text="$t('common.loadingText')"> | |
| 15 | + <div class="transfer-pane"> | |
| 16 | + <div class="transfer-pane__tools"> | |
| 17 | + <el-input placeholder="输入关键词进行搜索" v-model="keyword" @keyup.enter.native="getList"> | |
| 18 | + <el-button slot="append" icon="el-icon-search" @click="getList"></el-button> | |
| 19 | + </el-input> | |
| 20 | + </div> | |
| 21 | + <div class="transfer-pane__body"> | |
| 22 | + <el-tree :data="treeData" :props="props" highlight-current :expand-on-click-node="false" | |
| 23 | + check-on-click-node @node-click="handleNodeClick" class="NCC-common-el-tree" | |
| 24 | + node-key="id" v-loading="loading" lazy :load="loadNode"> | |
| 25 | + <span class="custom-tree-node" slot-scope="{ node, data }"> | |
| 26 | + <i :class="data.icon"></i> | |
| 27 | + <span class="text">{{node.label}}</span> | |
| 28 | + </span> | |
| 29 | + </el-tree> | |
| 30 | + </div> | |
| 31 | + </div> | |
| 32 | + <div class="transfer-pane"> | |
| 33 | + <div class="transfer-pane__tools"> | |
| 34 | + <span>已选</span> | |
| 35 | + <el-button @click="removeAll" type="text">清空列表</el-button> | |
| 36 | + </div> | |
| 37 | + <div class="transfer-pane__body shadow right-pane"> | |
| 38 | + <template> | |
| 39 | + <div v-for="(item, index) in selectedData" :key=" index" class="selected-item"> | |
| 40 | + <span>{{ item.fullName}}</span> | |
| 41 | + <i class="el-icon-delete" @click="removeData(index)"></i> | |
| 42 | + </div> | |
| 43 | + </template> | |
| 44 | + </div> | |
| 45 | + </div> | |
| 46 | + </div> | |
| 47 | + <span slot="footer" class="dialog-footer"> | |
| 48 | + <el-button @click="visible=false">{{$t('common.cancelButton')}}</el-button> | |
| 49 | + <el-button type="primary" @click="confirm">{{$t('common.confirmButton')}}</el-button> | |
| 50 | + </span> | |
| 51 | + </el-dialog> | |
| 52 | + </div> | |
| 53 | +</template> | |
| 54 | + | |
| 55 | +<script> | |
| 56 | +import { getImUserSelector, getUserInfoList } from '@/api/permission/user' | |
| 57 | +export default { | |
| 58 | + name: 'userSelect', | |
| 59 | + props: { | |
| 60 | + value: { | |
| 61 | + default: '' | |
| 62 | + }, | |
| 63 | + interfaceId: { | |
| 64 | + type: String, | |
| 65 | + default: '' | |
| 66 | + }, | |
| 67 | + placeholder: { | |
| 68 | + type: String, | |
| 69 | + default: '请选择' | |
| 70 | + }, | |
| 71 | + disabled: { | |
| 72 | + type: Boolean, | |
| 73 | + default: false | |
| 74 | + }, | |
| 75 | + multiple: { | |
| 76 | + type: Boolean, | |
| 77 | + default: false | |
| 78 | + }, | |
| 79 | + clearable: { | |
| 80 | + type: Boolean, | |
| 81 | + default: true | |
| 82 | + } | |
| 83 | + }, | |
| 84 | + data() { | |
| 85 | + return { | |
| 86 | + visible: false, | |
| 87 | + keyword: '', | |
| 88 | + nodeId: '', | |
| 89 | + innerValue: '', | |
| 90 | + loading: false, | |
| 91 | + props: { | |
| 92 | + children: 'children', | |
| 93 | + label: 'fullName', | |
| 94 | + isLeaf: 'isLeaf' | |
| 95 | + }, | |
| 96 | + treeData: [], | |
| 97 | + selectedData: [], | |
| 98 | + } | |
| 99 | + }, | |
| 100 | + watch: { | |
| 101 | + value(val) { | |
| 102 | + this.setDefault() | |
| 103 | + } | |
| 104 | + }, | |
| 105 | + created() { | |
| 106 | + this.setDefault() | |
| 107 | + }, | |
| 108 | + methods: { | |
| 109 | + clear() { | |
| 110 | + if (this.disabled) return | |
| 111 | + this.innerValue = '' | |
| 112 | + this.selectedData = [] | |
| 113 | + this.$emit('input', '') | |
| 114 | + this.$emit('change', '', '') | |
| 115 | + }, | |
| 116 | + openDialog() { | |
| 117 | + if (this.disabled) return | |
| 118 | + this.visible = true | |
| 119 | + this.keyword = '' | |
| 120 | + this.nodeId = '0' | |
| 121 | + this.getList() | |
| 122 | + }, | |
| 123 | + confirm() { | |
| 124 | + let txt = '', ids = '' | |
| 125 | + for (let i = 0; i < this.selectedData.length; i++) { | |
| 126 | + txt += (i ? ',' : '') + this.selectedData[i].fullName | |
| 127 | + ids += (i ? ',' : '') + this.selectedData[i].id | |
| 128 | + } | |
| 129 | + this.innerValue = txt | |
| 130 | + this.$emit('input', ids) | |
| 131 | + this.$emit('change', ids, this.selectedData) | |
| 132 | + this.visible = false | |
| 133 | + }, | |
| 134 | + setDefault() { | |
| 135 | + this.selectedData = [] | |
| 136 | + if (!this.value) return this.innerValue = '' | |
| 137 | + const arr = this.value.split(',') | |
| 138 | + getUserInfoList(arr).then(res => { | |
| 139 | + const list = res.data.list | |
| 140 | + this.selectedData = list | |
| 141 | + let txt = '' | |
| 142 | + for (let i = 0; i < list.length; i++) { | |
| 143 | + txt += (i ? ',' : '') + list[i].fullName | |
| 144 | + } | |
| 145 | + this.innerValue = txt | |
| 146 | + }) | |
| 147 | + }, | |
| 148 | + getList() { | |
| 149 | + this.loading = true | |
| 150 | + if (this.keyword) this.nodeId = '0' | |
| 151 | + getImUserSelector(this.nodeId, this.keyword).then(res => { | |
| 152 | + this.treeData = res.data.list | |
| 153 | + this.loading = false | |
| 154 | + }) | |
| 155 | + }, | |
| 156 | + loadNode(node, resolve) { | |
| 157 | + if (node.level === 0) { | |
| 158 | + this.nodeId = '0' | |
| 159 | + return resolve(this.treeData) | |
| 160 | + } | |
| 161 | + this.nodeId = node.data.id | |
| 162 | + getImUserSelector(this.nodeId).then(res => { | |
| 163 | + resolve(res.data.list) | |
| 164 | + }) | |
| 165 | + }, | |
| 166 | + handleNodeClick(data) { | |
| 167 | + if (data.type !== 'user') return | |
| 168 | + const boo = this.selectedData.some(o => o.id === data.id) | |
| 169 | + if (boo) return | |
| 170 | + const item = { | |
| 171 | + id: data.id, | |
| 172 | + fullName: data.fullName | |
| 173 | + } | |
| 174 | + this.multiple ? this.selectedData.push(item) : this.selectedData = [item] | |
| 175 | + }, | |
| 176 | + removeAll() { | |
| 177 | + this.selectedData = [] | |
| 178 | + }, | |
| 179 | + removeData(index) { | |
| 180 | + this.selectedData.splice(index, 1) | |
| 181 | + }, | |
| 182 | + }, | |
| 183 | +} | |
| 184 | +</script> | |
| 185 | +<style lang="scss" scoped> | |
| 186 | +.userSelect-container { | |
| 187 | + width: 100%; | |
| 188 | + .userSelect-input { | |
| 189 | + width: 100%; | |
| 190 | + cursor: pointer; | |
| 191 | + .is-disabled { | |
| 192 | + &:hover { | |
| 193 | + .el-icon-circle-close { | |
| 194 | + display: none; | |
| 195 | + } | |
| 196 | + .el-icon-arrow-down { | |
| 197 | + display: inline-block; | |
| 198 | + } | |
| 199 | + } | |
| 200 | + >>> input { | |
| 201 | + cursor: not-allowed; | |
| 202 | + } | |
| 203 | + } | |
| 204 | + >>> input { | |
| 205 | + cursor: pointer; | |
| 206 | + } | |
| 207 | + .el-icon-circle-close { | |
| 208 | + display: none; | |
| 209 | + } | |
| 210 | + &:hover { | |
| 211 | + .el-icon-circle-close { | |
| 212 | + display: block; | |
| 213 | + } | |
| 214 | + .el-icon-arrow-down.clearable { | |
| 215 | + display: none; | |
| 216 | + } | |
| 217 | + } | |
| 218 | + } | |
| 219 | +} | |
| 220 | +</style> | |
| 0 | 221 | \ No newline at end of file | ... | ... |
src/components/NCCEditor/quill.vue
| 1 | 1 | <template> |
| 2 | 2 | <div :class="prefixCls"> |
| 3 | - <quill-editor v-model="content" ref="myQuillEditor" :content="value" :options="editorOption" | |
| 3 | + <quill-editor v-model="content" ref="myQuillEditor" :content="value" :options="editorOption" :disabled="disabled" | |
| 4 | 4 | @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @ready="onEditorReady($event)" |
| 5 | 5 | @change="onEditorChange($event)"> |
| 6 | 6 | </quill-editor> |
| ... | ... | @@ -29,6 +29,10 @@ export default { |
| 29 | 29 | placeholder: { |
| 30 | 30 | type: String, |
| 31 | 31 | default: '请输入内容...' |
| 32 | + }, | |
| 33 | + disabled: { | |
| 34 | + type: Boolean, | |
| 35 | + default: false | |
| 32 | 36 | } |
| 33 | 37 | }, |
| 34 | 38 | data() { |
| ... | ... | @@ -47,11 +51,11 @@ export default { |
| 47 | 51 | [{ align: [] }], |
| 48 | 52 | [{ direction: "rtl" }], |
| 49 | 53 | ["clean"], |
| 50 | - ["link", "image"], | |
| 54 | + // ["link", "image"], | |
| 51 | 55 | ] |
| 52 | 56 | }, |
| 53 | 57 | theme: 'snow', |
| 54 | - placeholder: this.placeholder | |
| 58 | + placeholder: this.placeholder, | |
| 55 | 59 | } |
| 56 | 60 | } |
| 57 | 61 | }, | ... | ... |
src/components/SizeSelect/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dropdown trigger="click" @command="handleSetSize"> | |
| 3 | + <div> | |
| 4 | + <i class="ym-custom ym-custom-format-size" /> | |
| 5 | + </div> | |
| 6 | + <el-dropdown-menu slot="dropdown"> | |
| 7 | + <el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" | |
| 8 | + :command="item.value"> | |
| 9 | + {{ | |
| 10 | + item.label }} | |
| 11 | + </el-dropdown-item> | |
| 12 | + </el-dropdown-menu> | |
| 13 | + </el-dropdown> | |
| 14 | +</template> | |
| 15 | + | |
| 16 | +<script> | |
| 17 | +export default { | |
| 18 | + data() { | |
| 19 | + return { | |
| 20 | + sizeOptions: [ | |
| 21 | + { label: 'Default', value: 'default' }, | |
| 22 | + { label: 'Medium', value: 'medium' }, | |
| 23 | + { label: 'Small', value: 'small' }, | |
| 24 | + { label: 'Mini', value: 'mini' } | |
| 25 | + ] | |
| 26 | + } | |
| 27 | + }, | |
| 28 | + computed: { | |
| 29 | + size() { | |
| 30 | + return this.$store.getters.size | |
| 31 | + } | |
| 32 | + }, | |
| 33 | + methods: { | |
| 34 | + handleSetSize(size) { | |
| 35 | + this.$ELEMENT.size = size | |
| 36 | + this.$store.dispatch('app/setSize', size) | |
| 37 | + this.refreshView() | |
| 38 | + this.$message({ | |
| 39 | + message: 'Switch Size Success', | |
| 40 | + type: 'success' | |
| 41 | + }) | |
| 42 | + }, | |
| 43 | + refreshView() { | |
| 44 | + // In order to make the cached page re-rendered | |
| 45 | + this.$store.dispatch('tagsView/delAllCachedViews', this.$route) | |
| 46 | + | |
| 47 | + const { fullPath } = this.$route | |
| 48 | + | |
| 49 | + this.$nextTick(() => { | |
| 50 | + this.$router.replace({ | |
| 51 | + path: '/redirect' + fullPath | |
| 52 | + }) | |
| 53 | + }) | |
| 54 | + } | |
| 55 | + } | |
| 56 | + | |
| 57 | +} | |
| 58 | +</script> | ... | ... |
src/components/VisualPortal/CommonFunc/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="commonFunc-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="commonFunc-box-body"> | |
| 7 | + <router-link class="item" :to="'/'+item.urlAddress" v-for="(item,i) in menuList" :key="i"> | |
| 8 | + <i :class="item.icon" :style="{color:item.iconBackgroundColor||'#1890FF'}"></i> | |
| 9 | + <p class="name">{{item.fullName}}</p> | |
| 10 | + </router-link> | |
| 11 | + </div> | |
| 12 | + </el-card> | |
| 13 | +</template> | |
| 14 | +<script> | |
| 15 | +export default { | |
| 16 | + props: { | |
| 17 | + title: { type: String, default: '' }, | |
| 18 | + list: { type: Array, default: () => [] } | |
| 19 | + }, | |
| 20 | + data() { | |
| 21 | + return { | |
| 22 | + menuList: [] | |
| 23 | + } | |
| 24 | + }, | |
| 25 | + created() { | |
| 26 | + this.menuList = this.list.filter(o => o.id) | |
| 27 | + }, | |
| 28 | + watch: { | |
| 29 | + list: { | |
| 30 | + handler(val) { | |
| 31 | + this.menuList = val.filter(o => o.id) | |
| 32 | + }, | |
| 33 | + deep: true | |
| 34 | + } | |
| 35 | + } | |
| 36 | +} | |
| 37 | +</script> | |
| 38 | +<style lang="scss" scoped> | |
| 39 | +.commonFunc-box { | |
| 40 | + >>> .el-card__body { | |
| 41 | + width: 100%; | |
| 42 | + height: calc(100% - 55px); | |
| 43 | + } | |
| 44 | + .commonFunc-box-body { | |
| 45 | + padding: 0 30px; | |
| 46 | + height: 100%; | |
| 47 | + display: flex; | |
| 48 | + justify-content: space-between; | |
| 49 | + align-items: center; | |
| 50 | + .item { | |
| 51 | + display: block; | |
| 52 | + text-align: center; | |
| 53 | + i { | |
| 54 | + display: inline-block; | |
| 55 | + height: 40px; | |
| 56 | + font-size: 40px; | |
| 57 | + margin-bottom: 10px; | |
| 58 | + } | |
| 59 | + .name { | |
| 60 | + font-size: 14px; | |
| 61 | + line-height: 20px; | |
| 62 | + } | |
| 63 | + } | |
| 64 | + } | |
| 65 | +} | |
| 66 | +</style> | |
| 0 | 67 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/DataBoard/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-row :gutter="10" class="dataBoard"> | |
| 3 | + <el-col :span="6" class="dataBoard-item" v-for="(item,i) in menuList" :key="i"> | |
| 4 | + <el-card shadow="never"> | |
| 5 | + <div class="dataBoard-body"> | |
| 6 | + <i :class="item.icon+' dataBoard-body-item dataBoard-body-item'+(i+1)"></i> | |
| 7 | + <div class="text"> | |
| 8 | + <p class="num">{{item.num}}</p> | |
| 9 | + <p class="name">{{item.fullName}}</p> | |
| 10 | + </div> | |
| 11 | + </div> | |
| 12 | + </el-card> | |
| 13 | + </el-col> | |
| 14 | + </el-row> | |
| 15 | +</template> | |
| 16 | +<script> | |
| 17 | +import { getCountData } from '@/api/home' | |
| 18 | +export default { | |
| 19 | + props: { | |
| 20 | + title: { type: String, default: '' }, | |
| 21 | + // list: { type: Array, default: () => [] } | |
| 22 | + }, | |
| 23 | + data() { | |
| 24 | + return { | |
| 25 | + menuList: [] | |
| 26 | + } | |
| 27 | + }, | |
| 28 | + created() { | |
| 29 | + this.getData() | |
| 30 | + }, | |
| 31 | + methods: { | |
| 32 | + getData() { | |
| 33 | + getCountData().then(res => { | |
| 34 | + this.menuList = res.data.list | |
| 35 | + }) | |
| 36 | + } | |
| 37 | + }, | |
| 38 | + watch: { | |
| 39 | + list: { | |
| 40 | + handler(val) { | |
| 41 | + this.menuList = val | |
| 42 | + }, | |
| 43 | + deep: true | |
| 44 | + } | |
| 45 | + } | |
| 46 | +} | |
| 47 | +</script> | |
| 48 | +<style lang="scss" scoped> | |
| 49 | +.dataBoard { | |
| 50 | + height: 100%; | |
| 51 | + overflow: hidden; | |
| 52 | + .dataBoard-item { | |
| 53 | + height: 100%; | |
| 54 | + } | |
| 55 | + >>> .el-card { | |
| 56 | + width: 100%; | |
| 57 | + height: 100%; | |
| 58 | + .el-card__body { | |
| 59 | + padding: 0; | |
| 60 | + height: 100%; | |
| 61 | + } | |
| 62 | + } | |
| 63 | + .dataBoard-body { | |
| 64 | + padding-left: 30px; | |
| 65 | + height: 100%; | |
| 66 | + display: flex; | |
| 67 | + align-items: center; | |
| 68 | + .dataBoard-body-item { | |
| 69 | + width: 66px; | |
| 70 | + height: 66px; | |
| 71 | + margin-right: 16px; | |
| 72 | + border-radius: 50%; | |
| 73 | + text-align: center; | |
| 74 | + line-height: 66px; | |
| 75 | + font-size: 36px; | |
| 76 | + flex-shrink: 0; | |
| 77 | + &.dataBoard-body-item1 { | |
| 78 | + background: #f2ebfb; | |
| 79 | + color: #7b1ae1; | |
| 80 | + } | |
| 81 | + &.dataBoard-body-item2 { | |
| 82 | + background: #edf8fe; | |
| 83 | + color: #4ab8ff; | |
| 84 | + } | |
| 85 | + &.dataBoard-body-item3 { | |
| 86 | + background: #fff7e4; | |
| 87 | + color: #ff8b58; | |
| 88 | + } | |
| 89 | + &.dataBoard-body-item4 { | |
| 90 | + background: #fff2f5; | |
| 91 | + color: #fc5b87; | |
| 92 | + } | |
| 93 | + } | |
| 94 | + .text { | |
| 95 | + display: inline-block; | |
| 96 | + height: 56px; | |
| 97 | + .num { | |
| 98 | + font-size: 20px; | |
| 99 | + line-height: 36px; | |
| 100 | + } | |
| 101 | + .name { | |
| 102 | + font-size: 14px; | |
| 103 | + color: #666; | |
| 104 | + } | |
| 105 | + } | |
| 106 | + } | |
| 107 | +} | |
| 108 | +</style> | |
| 0 | 109 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/HAnnularChart/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="portal-eChart-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="eChart-box-body"> | |
| 7 | + <div ref="chart" id="chart" v-show="!isEmpty"></div> | |
| 8 | + <div class="portal-common-noData portal-common-noData-eChart" v-show="isEmpty"> | |
| 9 | + <img src="@/assets/images/portal-nodata.png" alt="" class="noData-img"> | |
| 10 | + <p class="noData-txt">暂无数据</p> | |
| 11 | + </div> | |
| 12 | + </div> | |
| 13 | + </el-card> | |
| 14 | +</template> | |
| 15 | +<script> | |
| 16 | +import echartMixin from '@/components/VisualPortal/mixins' | |
| 17 | +export default { | |
| 18 | + mixins: [echartMixin] | |
| 19 | +} | |
| 20 | +</script> | ... | ... |
src/components/VisualPortal/HAreaChart/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="portal-eChart-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="eChart-box-body"> | |
| 7 | + <div ref="chart" id="chart" v-show="!isEmpty"></div> | |
| 8 | + <div class="portal-common-noData portal-common-noData-eChart" v-show="isEmpty"> | |
| 9 | + <img src="@/assets/images/portal-nodata.png" alt="" class="noData-img"> | |
| 10 | + <p class="noData-txt">暂无数据</p> | |
| 11 | + </div> | |
| 12 | + </div> | |
| 13 | + </el-card> | |
| 14 | +</template> | |
| 15 | +<script> | |
| 16 | +import echartMixin from '@/components/VisualPortal/mixins' | |
| 17 | +export default { | |
| 18 | + mixins: [echartMixin] | |
| 19 | +} | |
| 20 | +</script> | ... | ... |
src/components/VisualPortal/HBarChart/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="portal-eChart-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="eChart-box-body"> | |
| 7 | + <div ref="chart" id="chart" v-show="!isEmpty"></div> | |
| 8 | + <div class="portal-common-noData portal-common-noData-eChart" v-show="isEmpty"> | |
| 9 | + <img src="@/assets/images/portal-nodata.png" alt="" class="noData-img"> | |
| 10 | + <p class="noData-txt">暂无数据</p> | |
| 11 | + </div> | |
| 12 | + </div> | |
| 13 | + </el-card> | |
| 14 | +</template> | |
| 15 | +<script> | |
| 16 | +import { getProjectMonthLine } from "@/api/home"; | |
| 17 | +import echarts from 'echarts' | |
| 18 | +import resize from '@/components/Charts/mixins/resize' | |
| 19 | +export default { | |
| 20 | + mixins: [resize], | |
| 21 | + props: { | |
| 22 | + title: { type: String, default: '' }, | |
| 23 | + option: { type: Object, default: () => { } } | |
| 24 | + }, | |
| 25 | + data() { | |
| 26 | + return { | |
| 27 | + chart: null, | |
| 28 | + currOption: {}, | |
| 29 | + isEmpty: false | |
| 30 | + } | |
| 31 | + }, | |
| 32 | + created() { | |
| 33 | + this.getData(); | |
| 34 | + }, | |
| 35 | + methods: { | |
| 36 | + getData() { | |
| 37 | + getProjectMonthLine().then((res) => { | |
| 38 | + window.console.log("柱状图:" + res.data.chartdata); | |
| 39 | + this.currOption = res.data.chartdata; | |
| 40 | + this.initChart(); | |
| 41 | + }); | |
| 42 | + }, | |
| 43 | + initChart() { | |
| 44 | + this.chart = echarts.init(this.$refs.chart); | |
| 45 | + this.chart.setOption(this.currOption); | |
| 46 | + setTimeout(() => { | |
| 47 | + this.$nextTick(() => { | |
| 48 | + this.chart.resize(); | |
| 49 | + }); | |
| 50 | + }, 50); | |
| 51 | + }, | |
| 52 | + }, | |
| 53 | + beforeDestroy() { | |
| 54 | + if (!this.chart) return; | |
| 55 | + this.chart.dispose(); | |
| 56 | + this.chart = null; | |
| 57 | + }, | |
| 58 | +} | |
| 59 | +</script> | |
| 0 | 60 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/HEmail/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="portal-todoList-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="portal-todoList-box-body"> | |
| 7 | + <template v-if="list.length"> | |
| 8 | + <router-link class="item" to="/extend/email" v-for="(item, i) in list" :key="i"> | |
| 9 | + <span class="name">{{item.fullName}}</span> | |
| 10 | + <span class="time">{{item.creatorTime | toDateText()}}</span> | |
| 11 | + </router-link> | |
| 12 | + </template> | |
| 13 | + <div class="portal-common-noData" v-else> | |
| 14 | + <img src="@/assets/images/portal-nodata.png" alt="" class="noData-img"> | |
| 15 | + <p class="noData-txt">暂无数据</p> | |
| 16 | + </div> | |
| 17 | + </div> | |
| 18 | + </el-card> | |
| 19 | +</template> | |
| 20 | +<script> | |
| 21 | +import { getEmail } from '@/api/home' | |
| 22 | +export default { | |
| 23 | + props: { | |
| 24 | + title: { type: String, default: '' } | |
| 25 | + }, | |
| 26 | + data() { | |
| 27 | + return { | |
| 28 | + list: [] | |
| 29 | + } | |
| 30 | + }, | |
| 31 | + created() { | |
| 32 | + this.getData() | |
| 33 | + }, | |
| 34 | + methods: { | |
| 35 | + getData() { | |
| 36 | + getEmail().then(res => { | |
| 37 | + this.list = res.data.list.slice(0, 7) | |
| 38 | + }) | |
| 39 | + } | |
| 40 | + } | |
| 41 | +} | |
| 42 | +</script> | |
| 0 | 43 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/HLineChart/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="portal-eChart-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="eChart-box-body"> | |
| 7 | + <div ref="chart" id="chart" v-show="!isEmpty"></div> | |
| 8 | + <div class="portal-common-noData portal-common-noData-eChart" v-show="isEmpty"> | |
| 9 | + <img src="@/assets/images/portal-nodata.png" alt="" class="noData-img"> | |
| 10 | + <p class="noData-txt">暂无数据</p> | |
| 11 | + </div> | |
| 12 | + </div> | |
| 13 | + </el-card> | |
| 14 | +</template> | |
| 15 | +<script> | |
| 16 | +import echartMixin from '@/components/VisualPortal/mixins' | |
| 17 | +export default { | |
| 18 | + mixins: [echartMixin] | |
| 19 | +} | |
| 20 | +</script> | |
| 0 | 21 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/HNotice/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="portal-todoList-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="portal-todoList-box-body"> | |
| 7 | + <template v-if="list.length"> | |
| 8 | + <router-link class="item" to="/messageRecord" v-for="(item, i) in list" :key="i"> | |
| 9 | + <span class="name">{{item.fullName}}</span> | |
| 10 | + <span class="time">{{item.creatorTime | toDate('yyyy-MM-dd')}}</span> | |
| 11 | + </router-link> | |
| 12 | + </template> | |
| 13 | + <div class="portal-common-noData" v-else> | |
| 14 | + <img src="@/assets/images/portal-nodata.png" alt="" class="noData-img"> | |
| 15 | + <p class="noData-txt">暂无数据</p> | |
| 16 | + </div> | |
| 17 | + </div> | |
| 18 | + </el-card> | |
| 19 | +</template> | |
| 20 | +<script> | |
| 21 | +import { getArticle } from '@/api/home' | |
| 22 | +export default { | |
| 23 | + props: { | |
| 24 | + title: { type: String, default: '' } | |
| 25 | + }, | |
| 26 | + data() { | |
| 27 | + return { | |
| 28 | + list: [] | |
| 29 | + } | |
| 30 | + }, | |
| 31 | + created() { | |
| 32 | + this.getData() | |
| 33 | + }, | |
| 34 | + methods: { | |
| 35 | + getData() { | |
| 36 | + getArticle().then(res => { | |
| 37 | + this.list = res.data.list.slice(0, 7) | |
| 38 | + }) | |
| 39 | + } | |
| 40 | + } | |
| 41 | +} | |
| 42 | +</script> | |
| 0 | 43 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/HPieChart/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="portal-eChart-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="eChart-box-body"> | |
| 7 | + <div ref="chart" id="chart" v-show="!isEmpty"></div> | |
| 8 | + <div class="portal-common-noData portal-common-noData-eChart" v-show="isEmpty"> | |
| 9 | + <img src="@/assets/images/portal-nodata.png" alt="" class="noData-img"> | |
| 10 | + <p class="noData-txt">暂无数据</p> | |
| 11 | + </div> | |
| 12 | + </div> | |
| 13 | + </el-card> | |
| 14 | +</template> | |
| 15 | +<script> | |
| 16 | +import echartMixin from '@/components/VisualPortal/mixins' | |
| 17 | +export default { | |
| 18 | + mixins: [echartMixin] | |
| 19 | +} | |
| 20 | +</script> | |
| 0 | 21 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/HRadarChart/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="portal-eChart-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="eChart-box-body"> | |
| 7 | + <div ref="chart" id="chart" v-show="!isEmpty"></div> | |
| 8 | + <div class="portal-common-noData portal-common-noData-eChart" v-show="isEmpty"> | |
| 9 | + <img src="@/assets/images/portal-nodata.png" alt="" class="noData-img"> | |
| 10 | + <p class="noData-txt">暂无数据</p> | |
| 11 | + </div> | |
| 12 | + </div> | |
| 13 | + </el-card> | |
| 14 | +</template> | |
| 15 | +<script> | |
| 16 | +import echartMixin from '@/components/VisualPortal/mixins' | |
| 17 | +export default { | |
| 18 | + mixins: [echartMixin] | |
| 19 | +} | |
| 20 | +</script> | |
| 0 | 21 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/Layout/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-scrollbar class="layout-area"> | |
| 3 | + <template v-if="layout.length"> | |
| 4 | + <grid-layout :layout.sync="layout" :row-height="40" :is-draggable="false" | |
| 5 | + :is-resizable="false"> | |
| 6 | + <grid-item v-for="item in layout" :x="item.x" :y="item.y" :w="item.w" :h="item.h" | |
| 7 | + :i="item.i" :key="item.i" static> | |
| 8 | + <Todo v-if="item.nccKey==='todo'" :title="item.title" /> | |
| 9 | + <CommonFunc v-if="item.nccKey==='commonFunc'" :title="item.title" :list="item.list" /> | |
| 10 | + <TodoList v-if="item.nccKey==='todoList'" :title="item.title" /> | |
| 11 | + <HNotice v-if="item.nccKey==='notice'" :title="item.title" /> | |
| 12 | + <HEmail v-if="item.nccKey==='email'" :title="item.title" /> | |
| 13 | + <DataBoard v-if="item.nccKey==='dataBoard'" :title="item.title" :list="item.list" /> | |
| 14 | + <HBarChart v-if="item.nccKey==='barChart'" :title="item.title" :option="item.option" | |
| 15 | + :dataType="item.dataType" :propsApi="item.propsApi" /> | |
| 16 | + <HAnnularChart v-if="item.nccKey==='annularChart'" :title="item.title" | |
| 17 | + :option=" item.option" :dataType="item.dataType" :propsApi="item.propsApi" /> | |
| 18 | + <HAreaChart v-if="item.nccKey==='areaChart'" :title="item.title" :option="item.option" | |
| 19 | + :dataType="item.dataType" :propsApi="item.propsApi" /> | |
| 20 | + <HLineChart v-if="item.nccKey==='lineChart'" :title="item.title" :option="item.option" | |
| 21 | + :dataType="item.dataType" :propsApi="item.propsApi" /> | |
| 22 | + <HPieChart v-if="item.nccKey==='pieChart'" :title="item.title" :option="item.option" | |
| 23 | + :dataType="item.dataType" :propsApi="item.propsApi" /> | |
| 24 | + <HRadarChart v-if="item.nccKey==='radarChart'" :title="item.title" :option="item.option" | |
| 25 | + :dataType="item.dataType" :propsApi="item.propsApi" /> | |
| 26 | + <div class="mask" v-if="mask"></div> | |
| 27 | + </grid-item> | |
| 28 | + </grid-layout> | |
| 29 | + </template> | |
| 30 | + <div class="portal-layout-nodata" v-else> | |
| 31 | + <img src="@/assets/images/dashboard-nodata.png" alt="" class="layout-nodata-img"> | |
| 32 | + <p class="layout-nodata-txt">暂无数据</p> | |
| 33 | + </div> | |
| 34 | + </el-scrollbar> | |
| 35 | +</template> | |
| 36 | + | |
| 37 | +<script> | |
| 38 | +import { | |
| 39 | + Todo, | |
| 40 | + CommonFunc, | |
| 41 | + TodoList, | |
| 42 | + HNotice, | |
| 43 | + HEmail, | |
| 44 | + DataBoard, | |
| 45 | + HBarChart, | |
| 46 | + HAnnularChart, | |
| 47 | + HAreaChart, | |
| 48 | + HLineChart, | |
| 49 | + HPieChart, | |
| 50 | + HRadarChart | |
| 51 | +} from "@/components/VisualPortal" | |
| 52 | +import VueGridLayout from 'vue-grid-layout' | |
| 53 | +export default { | |
| 54 | + props: { | |
| 55 | + layout: { type: Array, default: () => [] }, | |
| 56 | + mask: { type: Boolean, default: false }, | |
| 57 | + }, | |
| 58 | + components: { | |
| 59 | + GridLayout: VueGridLayout.GridLayout, | |
| 60 | + GridItem: VueGridLayout.GridItem, | |
| 61 | + Todo, | |
| 62 | + CommonFunc, | |
| 63 | + TodoList, | |
| 64 | + HNotice, | |
| 65 | + HEmail, | |
| 66 | + DataBoard, | |
| 67 | + HBarChart, | |
| 68 | + HAnnularChart, | |
| 69 | + HAreaChart, | |
| 70 | + HLineChart, | |
| 71 | + HPieChart, | |
| 72 | + HRadarChart | |
| 73 | + }, | |
| 74 | +} | |
| 75 | +</script> | |
| 76 | +<style lang="scss" scoped> | |
| 77 | +.layout-area { | |
| 78 | + height: 100%; | |
| 79 | + overflow: hidden; | |
| 80 | + >>> .el-scrollbar__wrap { | |
| 81 | + margin-bottom: 0 !important; | |
| 82 | + overflow-x: auto; | |
| 83 | + } | |
| 84 | + >>> .el-scrollbar__bar.is-horizontal > div { | |
| 85 | + display: none; | |
| 86 | + } | |
| 87 | + >>> .el-card { | |
| 88 | + width: 100%; | |
| 89 | + height: 100%; | |
| 90 | + .el-card__body { | |
| 91 | + padding: 0; | |
| 92 | + } | |
| 93 | + } | |
| 94 | + .vue-grid-item { | |
| 95 | + position: relative; | |
| 96 | + .mask { | |
| 97 | + position: absolute; | |
| 98 | + top: 0; | |
| 99 | + left: 0; | |
| 100 | + width: 100%; | |
| 101 | + height: 100%; | |
| 102 | + z-index: 1; | |
| 103 | + } | |
| 104 | + } | |
| 105 | +} | |
| 106 | +</style> | |
| 0 | 107 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/Todo/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="todo-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="todo-box-body"> | |
| 7 | + <router-link class="item" to="/workFlow/flowTodo"> | |
| 8 | + <i class="icon-ym icon-ym-flowTodo"></i> | |
| 9 | + <div class="text"> | |
| 10 | + <p class="num">{{toBeReviewed}}</p> | |
| 11 | + <p class="name">待我审核</p> | |
| 12 | + </div> | |
| 13 | + </router-link> | |
| 14 | + <router-link class="item" to="/workFlow/entrust"> | |
| 15 | + <i class="icon-ym icon-ym-flowEntrust"></i> | |
| 16 | + <div class="text"> | |
| 17 | + <p class="num">{{entrust}}</p> | |
| 18 | + <p class="name">流程委托</p> | |
| 19 | + </div> | |
| 20 | + </router-link> | |
| 21 | + <router-link class="item" to="/workFlow/flowDone"> | |
| 22 | + <i class="icon-ym icon-ym-flowDone"></i> | |
| 23 | + <div class="text"> | |
| 24 | + <p class="num">{{flowDone}}</p> | |
| 25 | + <p class="name">已办事宜</p> | |
| 26 | + </div> | |
| 27 | + </router-link> | |
| 28 | + </div> | |
| 29 | + </el-card> | |
| 30 | +</template> | |
| 31 | +<script> | |
| 32 | +import { getFlowTodoCount } from '@/api/home' | |
| 33 | +export default { | |
| 34 | + props: { | |
| 35 | + title: { type: String, default: '' } | |
| 36 | + }, | |
| 37 | + data() { | |
| 38 | + return { | |
| 39 | + entrust: 0, | |
| 40 | + flowDone: 0, | |
| 41 | + toBeReviewed: 0 | |
| 42 | + } | |
| 43 | + }, | |
| 44 | + created() { | |
| 45 | + this.getData() | |
| 46 | + }, | |
| 47 | + methods: { | |
| 48 | + getData() { | |
| 49 | + getFlowTodoCount().then(res => { | |
| 50 | + this.entrust = res.data.entrust || 0 | |
| 51 | + this.flowDone = res.data.flowDone || 0 | |
| 52 | + this.toBeReviewed = res.data.toBeReviewed || 0 | |
| 53 | + }) | |
| 54 | + } | |
| 55 | + } | |
| 56 | +} | |
| 57 | +</script> | |
| 58 | +<style lang="scss" scoped> | |
| 59 | +.todo-box { | |
| 60 | + >>> .el-card__body { | |
| 61 | + width: 100%; | |
| 62 | + height: calc(100% - 55px); | |
| 63 | + } | |
| 64 | + .todo-box-body { | |
| 65 | + padding: 0 30px; | |
| 66 | + height: 100%; | |
| 67 | + display: flex; | |
| 68 | + justify-content: space-between; | |
| 69 | + align-items: center; | |
| 70 | + .item { | |
| 71 | + height: 56px; | |
| 72 | + display: block; | |
| 73 | + i { | |
| 74 | + width: 56px; | |
| 75 | + height: 56px; | |
| 76 | + margin-right: 14px; | |
| 77 | + border-radius: 50%; | |
| 78 | + color: #fff; | |
| 79 | + display: inline-block; | |
| 80 | + vertical-align: top; | |
| 81 | + text-align: center; | |
| 82 | + line-height: 56px; | |
| 83 | + font-size: 30px; | |
| 84 | + &.icon-ym-flowTodo { | |
| 85 | + background: #f68900; | |
| 86 | + } | |
| 87 | + &.icon-ym-flowEntrust { | |
| 88 | + background: #1890ff; | |
| 89 | + } | |
| 90 | + &.icon-ym-flowDone { | |
| 91 | + background: #7b1ae1; | |
| 92 | + } | |
| 93 | + } | |
| 94 | + .text { | |
| 95 | + display: inline-block; | |
| 96 | + height: 56px; | |
| 97 | + .num { | |
| 98 | + font-size: 20px; | |
| 99 | + line-height: 36px; | |
| 100 | + } | |
| 101 | + .name { | |
| 102 | + font-size: 14px; | |
| 103 | + color: #666; | |
| 104 | + } | |
| 105 | + } | |
| 106 | + } | |
| 107 | + } | |
| 108 | +} | |
| 109 | +</style> | |
| 0 | 110 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/TodoList/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-card shadow="never" class="portal-todoList-box"> | |
| 3 | + <div slot="header" class="portal-common-title"> | |
| 4 | + <span>{{title}}</span> | |
| 5 | + </div> | |
| 6 | + <div class="portal-todoList-box-body"> | |
| 7 | + <template v-if="list.length"> | |
| 8 | + <router-link class="item" to="/workFlow/flowTodo" v-for="(item, i) in list" :key="i"> | |
| 9 | + <span class="name">{{item.fullName}}</span> | |
| 10 | + <span class="time">{{item.creatorTime | toDate('yyyy-MM-dd')}}</span> | |
| 11 | + </router-link> | |
| 12 | + </template> | |
| 13 | + <div class="portal-common-noData" v-else> | |
| 14 | + <img src="@/assets/images/portal-nodata.png" alt="" class="noData-img"> | |
| 15 | + <p class="noData-txt">暂无数据</p> | |
| 16 | + </div> | |
| 17 | + </div> | |
| 18 | + </el-card> | |
| 19 | +</template> | |
| 20 | +<script> | |
| 21 | +import { getFlowTodo } from '@/api/home' | |
| 22 | +export default { | |
| 23 | + props: { | |
| 24 | + title: { type: String, default: '' } | |
| 25 | + }, | |
| 26 | + data() { | |
| 27 | + return { | |
| 28 | + list: [] | |
| 29 | + } | |
| 30 | + }, | |
| 31 | + created() { | |
| 32 | + this.getData() | |
| 33 | + }, | |
| 34 | + methods: { | |
| 35 | + getData() { | |
| 36 | + getFlowTodo().then(res => { | |
| 37 | + this.list = res.data.list.slice(0, 7) | |
| 38 | + }) | |
| 39 | + } | |
| 40 | + } | |
| 41 | +} | |
| 42 | +</script> | |
| 0 | 43 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/index.js
0 → 100644
| 1 | +// 门户设计专供 | |
| 2 | +export { default as Todo } from '@/components/VisualPortal/Todo' | |
| 3 | +export { default as CommonFunc } from '@/components/VisualPortal/CommonFunc' | |
| 4 | +export { default as TodoList } from '@/components/VisualPortal/TodoList' | |
| 5 | +export { default as HNotice } from '@/components/VisualPortal/HNotice' | |
| 6 | +export { default as HEmail } from '@/components/VisualPortal/HEmail' | |
| 7 | +export { default as DataBoard } from '@/components/VisualPortal/DataBoard' | |
| 8 | +export { default as HBarChart } from '@/components/VisualPortal/HBarChart' | |
| 9 | +export { default as HAnnularChart } from '@/components/VisualPortal/HAnnularChart' | |
| 10 | +export { default as HAreaChart } from '@/components/VisualPortal/HAreaChart' | |
| 11 | +export { default as HLineChart } from '@/components/VisualPortal/HLineChart' | |
| 12 | +export { default as HPieChart } from '@/components/VisualPortal/HPieChart' | |
| 13 | +export { default as HRadarChart } from '@/components/VisualPortal/HRadarChart' | |
| 0 | 14 | \ No newline at end of file | ... | ... |
src/components/VisualPortal/mixins/index.js
0 → 100644
| 1 | +import echarts from 'echarts' | |
| 2 | +import resize from '@/components/Charts/mixins/resize' | |
| 3 | +import { previewDataInterface } from '@/api/systemData/dataInterface' | |
| 4 | + | |
| 5 | +export default { | |
| 6 | + mixins: [resize], | |
| 7 | + props: { | |
| 8 | + title: { type: String, default: '' }, | |
| 9 | + dataType: { type: String, default: 'static' }, | |
| 10 | + propsApi: { type: String, default: '' }, | |
| 11 | + option: { type: Object, default: () => {} } | |
| 12 | + }, | |
| 13 | + data() { | |
| 14 | + return { | |
| 15 | + chart: null, | |
| 16 | + currOption: {}, | |
| 17 | + isEmpty: false | |
| 18 | + } | |
| 19 | + }, | |
| 20 | + mounted() { | |
| 21 | + if (this.dataType === 'dynamic') { | |
| 22 | + if (!this.propsApi) return | |
| 23 | + previewDataInterface(this.propsApi).then(res => { | |
| 24 | + this.currOption = res.data | |
| 25 | + this.resetChart() | |
| 26 | + }) | |
| 27 | + } else { | |
| 28 | + this.currOption = this.option | |
| 29 | + this.initChart() | |
| 30 | + } | |
| 31 | + }, | |
| 32 | + watch: { | |
| 33 | + option: { | |
| 34 | + handler(val) { | |
| 35 | + this.currOption = val | |
| 36 | + this.resetChart() | |
| 37 | + }, | |
| 38 | + deep: true | |
| 39 | + }, | |
| 40 | + dataType(val) { | |
| 41 | + if (val !== 'dynamic') { | |
| 42 | + this.currOption = this.option | |
| 43 | + this.resetChart() | |
| 44 | + } | |
| 45 | + }, | |
| 46 | + propsApi(val) { | |
| 47 | + if (this.dataType === 'static') return | |
| 48 | + if (!val) return | |
| 49 | + previewDataInterface(val).then(res => { | |
| 50 | + this.currOption = res.data | |
| 51 | + this.resetChart() | |
| 52 | + }) | |
| 53 | + } | |
| 54 | + }, | |
| 55 | + methods: { | |
| 56 | + initChart() { | |
| 57 | + this.chart = echarts.init(this.$refs.chart) | |
| 58 | + this.chart.setOption(this.currOption) | |
| 59 | + setTimeout(() => { | |
| 60 | + this.$nextTick(() => { | |
| 61 | + this.chart.resize(); | |
| 62 | + }) | |
| 63 | + }, 50); | |
| 64 | + }, | |
| 65 | + resetChart() { | |
| 66 | + this.isEmpty = JSON.stringify(this.currOption) === "{}" | |
| 67 | + this.chart && this.chart.dispose() | |
| 68 | + this.chart = null | |
| 69 | + if (!this.isEmpty) this.initChart() | |
| 70 | + } | |
| 71 | + }, | |
| 72 | + beforeDestroy() { | |
| 73 | + if (!this.chart) return | |
| 74 | + this.chart.dispose() | |
| 75 | + this.chart = null | |
| 76 | + } | |
| 77 | +} | |
| 0 | 78 | \ No newline at end of file | ... | ... |
src/components/index.js
| ... | ... | @@ -12,6 +12,28 @@ import Screenfull from '@/components/Screenfull' |
| 12 | 12 | import NCCTreeSelect from '@/components/NCC-treeSelect' |
| 13 | 13 | import NCCAddress from '@/components/Generator/components/Address' |
| 14 | 14 | import SelsctLoad from '@/components/SelsctLoad' |
| 15 | +import topOperation from '@/components/NCC-topOperation/index' | |
| 16 | +import tableOperation from '@/components/NCC-tableOperation' | |
| 17 | +import UserBox from '@/components/NCC-userBox' | |
| 18 | +import ColumnSettings from '@/components/ColumnSettings' | |
| 19 | +import UserSelect from '@/components/NCC-userSelect' | |
| 20 | +import uploadBtn from '@/components/NCC-uploadBtn' | |
| 21 | +// 代码生成器专供 | |
| 22 | +import NCCText from '@/components/Generator/components/NCCText' | |
| 23 | +import NCCUploadImg from '@/components/Generator/components/Upload/UploadImg' | |
| 24 | +import PopupSelect from '@/components/Generator/components/PopupSelect' | |
| 25 | +import NumRange from '@/components/Generator/components/NumRange' | |
| 26 | +import ComSelect from '@/components/Generator/components/ComSelect' | |
| 27 | +import PosSelect from '@/components/Generator/components/PosSelect' | |
| 28 | +import DicSelect from '@/components/Generator/components/DicSelect' | |
| 29 | +import BillRule from '@/components/Generator/components/BillRule' | |
| 30 | +import NCCInputTable from '@/components/Generator/components/InputTable' | |
| 31 | +import GroupTitle from '@/components/Generator/components/GroupTitle' | |
| 32 | +import RelationForm from '@/components/Generator/components/RelationForm' | |
| 33 | +import RelationFormAttr from '@/components/Generator/components/RelationFormAttr' | |
| 34 | +import RelationFlow from '@/components/Generator/components/RelationFlow' | |
| 35 | +import RelationFlowAttr from '@/components/Generator/components/RelationFlowAttr' | |
| 36 | +import Calculate from '@/components/Generator/components/Calculate' | |
| 15 | 37 | |
| 16 | 38 | export default { |
| 17 | 39 | install(Vue, options) { |
| ... | ... | @@ -22,13 +44,34 @@ export default { |
| 22 | 44 | Vue.component('InfoEditRecord', InfoEditRecord) |
| 23 | 45 | Vue.component('Pagination', Pagination) |
| 24 | 46 | Vue.component('NCCTable', NCCTable) |
| 47 | + Vue.component('SelsctLoad', SelsctLoad) | |
| 48 | + Vue.component('NCCTreeSelect', NCCTreeSelect) | |
| 49 | + Vue.component('topOpts', topOperation) | |
| 50 | + Vue.component('tableOpts', tableOperation) | |
| 51 | + Vue.component('uploadBtn', uploadBtn) | |
| 52 | + Vue.component('UserBox', UserBox) | |
| 53 | + Vue.component('NCCText', NCCText) | |
| 25 | 54 | Vue.component('NCCUploadFz', NCCUploadFz) |
| 26 | - Vue.component('NCCQuill', NCCQuill) | |
| 55 | + Vue.component('NCCUploadImg', NCCUploadImg) | |
| 56 | + Vue.component('PopupSelect', PopupSelect) | |
| 57 | + Vue.component('NumRange', NumRange) | |
| 58 | + Vue.component('ComSelect', ComSelect) | |
| 27 | 59 | Vue.component('DepSelect', DepSelect) |
| 28 | - Vue.component('NCCTreeSelect', NCCTreeSelect) | |
| 29 | - Vue.component('Screenfull', Screenfull) | |
| 60 | + Vue.component('PosSelect', PosSelect) | |
| 61 | + Vue.component('UserSelect', UserSelect) | |
| 62 | + Vue.component('DicSelect', DicSelect) | |
| 63 | + Vue.component('BillRule', BillRule) | |
| 64 | + Vue.component('NCCInputTable', NCCInputTable) | |
| 30 | 65 | Vue.component('NCCAddress', NCCAddress) |
| 31 | - Vue.component('SelsctLoad', SelsctLoad) | |
| 66 | + Vue.component('GroupTitle', GroupTitle) | |
| 67 | + Vue.component('RelationForm', RelationForm) | |
| 68 | + Vue.component('RelationFormAttr', RelationFormAttr) | |
| 69 | + Vue.component('RelationFlow', RelationFlow) | |
| 70 | + Vue.component('RelationFlowAttr', RelationFlowAttr) | |
| 71 | + Vue.component('Calculate', Calculate) | |
| 72 | + Vue.component('NCCQuill', NCCQuill) | |
| 73 | + Vue.component('Screenfull', Screenfull) | |
| 74 | + Vue.component('ColumnSettings', ColumnSettings) | |
| 32 | 75 | |
| 33 | 76 | } |
| 34 | 77 | } |
| 35 | 78 | \ No newline at end of file | ... | ... |
src/views/baseSpecialAction/dynamicModel/list/Form.vue
| ... | ... | @@ -51,6 +51,7 @@ import Parser from '@/components/Generator/parser/Parser' |
| 51 | 51 | import ParserMixin from '@/components/Generator/parser/mixin' |
| 52 | 52 | import PrintBrowse from '@/components/PrintBrowse' |
| 53 | 53 | import { deepClone } from '@/utils' |
| 54 | +import request from "@/utils/request"; | |
| 54 | 55 | export default { |
| 55 | 56 | components: { Parser, PrintBrowse }, |
| 56 | 57 | mixins: [ParserMixin], |
| ... | ... | @@ -69,6 +70,7 @@ export default { |
| 69 | 70 | useFormPermission: false, |
| 70 | 71 | printBrowseVisible: false, |
| 71 | 72 | formOperates: [], |
| 73 | + taskId: '', | |
| 72 | 74 | } |
| 73 | 75 | }, |
| 74 | 76 | methods: { |
| ... | ... | @@ -79,12 +81,13 @@ export default { |
| 79 | 81 | if (this.isPreview) return this.$message({ message: '功能预览不支持打印', type: 'warning' }) |
| 80 | 82 | this.printBrowseVisible = true |
| 81 | 83 | }, |
| 82 | - init(formConf, modelId, id, isPreview, useFormPermission) { | |
| 84 | + init(formConf, modelId, id, isPreview, useFormPermission, taskId) { | |
| 83 | 85 | this.formConf = deepClone(JSON.parse(formConf)) |
| 84 | 86 | this.modelId = modelId |
| 85 | 87 | this.isPreview = isPreview |
| 86 | 88 | this.useFormPermission = useFormPermission |
| 87 | 89 | this.dataForm.id = id || '' |
| 90 | + this.taskId = taskId | |
| 88 | 91 | this.getFormOperates() |
| 89 | 92 | this.loading = true |
| 90 | 93 | this.$nextTick(() => { |
| ... | ... | @@ -150,20 +153,33 @@ export default { |
| 150 | 153 | const formMethod = this.dataForm.id ? updateModel : createModel |
| 151 | 154 | formMethod(this.modelId, this.dataForm).then(res => { |
| 152 | 155 | console.info(res); |
| 153 | - alert(res.data.Id); | |
| 156 | + let itemId = !this.dataForm.id ? res.data.Id : ''; | |
| 154 | 157 | this.$message({ |
| 155 | 158 | message: res.msg, |
| 156 | 159 | type: 'success', |
| 157 | 160 | duration: 1500, |
| 158 | - onClose: () => { | |
| 161 | + onClose: async () => { | |
| 159 | 162 | if (callback && typeof callback === "function") callback() |
| 160 | 163 | this.visible = false |
| 161 | 164 | this.btnLoading = false |
| 165 | + !this.dataForm.id && this.toSaveItemid(itemId) | |
| 162 | 166 | this.$emit('refreshDataList', true) |
| 163 | 167 | } |
| 164 | 168 | }) |
| 165 | 169 | }).catch(() => { this.btnLoading = false }) |
| 166 | 170 | }, |
| 171 | + async toSaveItemid(itemId) { | |
| 172 | + console.log(11); | |
| 173 | + let res = await request({ | |
| 174 | + url: '/Extend/BaseSpecialActionInfo/UpdateState', | |
| 175 | + method: "PUT", | |
| 176 | + data: { | |
| 177 | + id: this.taskId, | |
| 178 | + itemId | |
| 179 | + }, | |
| 180 | + }) | |
| 181 | + return res | |
| 182 | + }, | |
| 167 | 183 | dataFormSubmit() { |
| 168 | 184 | if (this.isPreview) return this.$message({ message: '功能预览不支持数据保存', type: 'warning' }) |
| 169 | 185 | this.$refs.dynamicForm && this.$refs.dynamicForm.submitForm() | ... | ... |
src/views/baseSpecialAction/index.vue
| ... | ... | @@ -49,10 +49,10 @@ |
| 49 | 49 | </el-table-column> |
| 50 | 50 | <el-table-column prop="deadline" label="截止日期" align="left" :formatter="ncc.tableDateFormat" show-overflow-tooltip/> |
| 51 | 51 | <el-table-column prop="creatorUserId" show-overflow-tooltip label="创建用户" align="left"/> |
| 52 | - <el-table-column label="操作" fixed="right" width="130"> | |
| 52 | + <el-table-column label="操作" fixed="right" width="100"> | |
| 53 | 53 | <template slot-scope="scope"> |
| 54 | - <el-button type="text" @click="addOrUpdateHandle(scope.row.id)">发布任务</el-button> | |
| 55 | - <el-button type="text" @click="toDetail(scope.row)" v-if="scope.row.state == '已发布'">行动情况</el-button> | |
| 54 | + <el-button type="text" @click="addOrUpdateHandle(scope.row.id)" v-if="scope.row.state == '未发布'">发布任务</el-button> | |
| 55 | + <el-button type="text" @click="toDetail(scope.row)" v-else>处理情况</el-button> | |
| 56 | 56 | </template> |
| 57 | 57 | </el-table-column> |
| 58 | 58 | </NCC-table> | ... | ... |
src/views/baseSpecialhandle/index.vue
| ... | ... | @@ -39,7 +39,8 @@ |
| 39 | 39 | show-overflow-tooltip /> |
| 40 | 40 | <el-table-column label="操作" fixed="right" width="130"> |
| 41 | 41 | <template slot-scope="scope"> |
| 42 | - <el-button type="text" @click="toFillForm(scope.row)">处理</el-button> | |
| 42 | + <el-button type="text" @click="toFillForm(scope.row)">{{scope.row.state == '已填写' ? '修改' : '处理'}}</el-button> | |
| 43 | + <!-- <el-button type="text" @click="toFillForm(scope.row)" v-if="scope.row.state == '已填写'">详情</el-button> --> | |
| 43 | 44 | </template> |
| 44 | 45 | </el-table-column> |
| 45 | 46 | </NCC-table> |
| ... | ... | @@ -49,7 +50,7 @@ |
| 49 | 50 | </div> |
| 50 | 51 | </div> |
| 51 | 52 | </div> |
| 52 | - <NCCForm v-if="dialogVisible" ref="NCCForm" @refresh="refresh" /> | |
| 53 | + <NCCForm v-if="dialogVisible" ref="NCCForm" @refreshDataList="refresh" /> | |
| 53 | 54 | <!-- <el-dialog title="专项行动处理" :visible.sync="dialogVisible" fullscreen :modal="false"> |
| 54 | 55 | <iframe |
| 55 | 56 | :src="nestedPageUrl" |
| ... | ... | @@ -86,6 +87,7 @@ export default { |
| 86 | 87 | dialogVisible: false, |
| 87 | 88 | nestedPageUrl: '', |
| 88 | 89 | viewportHeight: 0, |
| 90 | + taskId: '', | |
| 89 | 91 | }; |
| 90 | 92 | }, |
| 91 | 93 | created() { |
| ... | ... | @@ -135,9 +137,9 @@ export default { |
| 135 | 137 | }, |
| 136 | 138 | toFillForm(row) { |
| 137 | 139 | this.dialogVisible = true; |
| 138 | - var Itemid= "588014226493146373";//数据id,没有的话就是新增 ,有的话就是修改 | |
| 139 | - Itemid=""; | |
| 140 | - var modelId=row.formId;//关联的表单id | |
| 140 | + var Itemid = row.itemId || '';//数据id,没有的话就是新增 ,有的话就是修改 | |
| 141 | + var modelId = row.formId;//关联的表单id | |
| 142 | + var taskId = row.id; // 当前专项行动id | |
| 141 | 143 | var isPreview = false;//固定死,值不变 |
| 142 | 144 | var useFormPermission = false;//固定死,值不变 |
| 143 | 145 | var formData = []; |
| ... | ... | @@ -147,7 +149,8 @@ export default { |
| 147 | 149 | params:null |
| 148 | 150 | }).then(res => { |
| 149 | 151 | formData = res.data.formData; |
| 150 | - this.$refs.NCCForm.init(formData, modelId, Itemid, isPreview, useFormPermission) | |
| 152 | + console.log(res, formData, modelId, Itemid, isPreview, useFormPermission); | |
| 153 | + this.$refs.NCCForm.init(formData, modelId, Itemid, isPreview, useFormPermission, taskId) | |
| 151 | 154 | }); |
| 152 | 155 | |
| 153 | 156 | // this.nestedPageUrl = `http://8.130.38.56:8043/old/#/previewModel?isPreview=1&id=${row.originId}`; |
| ... | ... | @@ -195,6 +198,7 @@ export default { |
| 195 | 198 | this.formVisible = false; |
| 196 | 199 | if (isrRefresh) this.reset(); |
| 197 | 200 | }, |
| 201 | + | |
| 198 | 202 | reset() { |
| 199 | 203 | for (let key in this.query) { |
| 200 | 204 | this.query[key] = undefined; | ... | ... |
src/views/overView/Overview.vue
| ... | ... | @@ -3,10 +3,18 @@ |
| 3 | 3 | <el-row :gutter="20"> |
| 4 | 4 | <el-col :span="16"> |
| 5 | 5 | <div class="item-box todo"> |
| 6 | - <div class="item-title">事件中心</div> | |
| 6 | + <div class="item-title"> | |
| 7 | + <div class="left">任务中心</div> | |
| 8 | + <div class="right"> | |
| 9 | + <!-- <el-button type="success" size="mini" style="margin-right: 10px;" v-if="isSHILevel" @click="announceMsg">发布</el-button> --> | |
| 10 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 11 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" @click="resetTask"/> | |
| 12 | + </el-tooltip> | |
| 13 | + </div> | |
| 14 | + </div> | |
| 7 | 15 | <div class="item-body"> |
| 8 | 16 | <template> |
| 9 | - <el-table :data="todoTableData" style="width: 100%" stripe> | |
| 17 | + <el-table :data="taskList" style="width: 100%" stripe v-loading="taskLoading"> | |
| 10 | 18 | <el-table-column type="index" width="50"> </el-table-column> |
| 11 | 19 | <el-table-column |
| 12 | 20 | prop="teskName" |
| ... | ... | @@ -44,6 +52,7 @@ |
| 44 | 52 | </template> |
| 45 | 53 | </el-table-column> |
| 46 | 54 | </el-table> |
| 55 | + <pagination :total="taskTotal" :page.sync="taskListQuery.currentPage" :limit.sync="taskListQuery.pageSize" @pagination="getAllTaskList" /> | |
| 47 | 56 | </template> |
| 48 | 57 | </div> |
| 49 | 58 | </div> |
| ... | ... | @@ -111,60 +120,74 @@ |
| 111 | 120 | <div class="item-box msg"> |
| 112 | 121 | <div class="item-title"> |
| 113 | 122 | <div class="left">通知公告</div> |
| 114 | - <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 115 | - <el-link | |
| 116 | - icon="icon-ym icon-ym-Refresh NCC-common-head-icon" | |
| 117 | - :underline="false" | |
| 118 | - @click="resetMsg()" | |
| 119 | - /> | |
| 120 | - </el-tooltip> | |
| 123 | + <div class="right"> | |
| 124 | + <el-button type="success" size="mini" style="margin-right: 10px;" v-if="isSHILevel" @click="announceMsg">发布</el-button> | |
| 125 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 126 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" @click="resetMsg"/> | |
| 127 | + </el-tooltip> | |
| 128 | + </div> | |
| 121 | 129 | </div> |
| 122 | 130 | <div class="item-body"> |
| 123 | 131 | <template> |
| 124 | - <el-table :data="msgList" style="width: 100%" stripe> | |
| 125 | - <el-table-column | |
| 126 | - prop="teskName" | |
| 127 | - label="内容" | |
| 128 | - show-overflow-tooltip | |
| 129 | - > | |
| 132 | + <el-table :data="msgList" style="width: 100%" stripe v-loading="msgLoading"> | |
| 133 | + <el-table-column prop="title" label="标题" show-overflow-tooltip> | |
| 130 | 134 | </el-table-column> |
| 131 | - <el-table-column | |
| 132 | - prop="teskCode" | |
| 133 | - label="事件" | |
| 134 | - show-overflow-tooltip | |
| 135 | - > | |
| 135 | + <el-table-column label="操作" fixed="right" width="80"> | |
| 136 | + <template slot-scope="scope"> | |
| 137 | + <el-button type="text" @click="checkDetail(scope.row)">详情</el-button> | |
| 138 | + </template> | |
| 136 | 139 | </el-table-column> |
| 137 | 140 | </el-table> |
| 141 | + <pagination :total="msgTotal" :page.sync="msgListQuery.currentPage" :limit.sync="msgListQuery.pageSize" @pagination="getAllMsgList" /> | |
| 138 | 142 | </template> |
| 139 | 143 | </div> |
| 140 | 144 | </div> |
| 141 | 145 | </el-col> |
| 142 | 146 | </el-row> |
| 147 | + <MsgForm v-if="MsgFormVisible" ref="MsgForm" @refreshDataList="msgRefresh"/> | |
| 143 | 148 | </div> |
| 144 | 149 | </template> |
| 145 | 150 | |
| 146 | 151 | <script> |
| 147 | -import { | |
| 148 | - todoObj, | |
| 149 | - tipObj, | |
| 150 | - areaObj, | |
| 151 | - chObj, | |
| 152 | -} from "@/assets/mockdata/demodata.json"; | |
| 153 | -import { computed } from "vue"; | |
| 152 | +import request from "@/utils/request"; | |
| 153 | +import MsgForm from "./msgForm.vue" | |
| 154 | 154 | export default { |
| 155 | 155 | name: "Overview", |
| 156 | + components: { MsgForm }, | |
| 156 | 157 | data() { |
| 157 | 158 | return { |
| 158 | 159 | todoTableData: [], |
| 159 | - msgList: [], | |
| 160 | 160 | tipTableData: [], |
| 161 | 161 | aimList: [], |
| 162 | + | |
| 163 | + msgLoading: false, | |
| 164 | + msgListQuery: { | |
| 165 | + currentPage: 1, | |
| 166 | + pageSize: 20, | |
| 167 | + }, | |
| 168 | + msgQuery: { | |
| 169 | + keyword: '' | |
| 170 | + }, | |
| 171 | + msgList: [], | |
| 172 | + msgTotal: 0, | |
| 173 | + MsgFormVisible: false, | |
| 174 | + | |
| 175 | + taskLoading: false, | |
| 176 | + taskListQuery: { | |
| 177 | + currentPage: 1, | |
| 178 | + pageSize: 20, | |
| 179 | + }, | |
| 180 | + taskQuery: { | |
| 181 | + keyword: '' | |
| 182 | + }, | |
| 183 | + taskList: [], | |
| 184 | + taskTotal: 0, | |
| 185 | + TaskFormVisible: false | |
| 162 | 186 | }; |
| 163 | 187 | }, |
| 164 | 188 | created() { |
| 165 | - this.getMsgList(); | |
| 166 | - this.getTipList(); | |
| 167 | - this.getAimList(); | |
| 189 | + this.getAllMsgList(); | |
| 190 | + this.getAllTaskList(); | |
| 168 | 191 | }, |
| 169 | 192 | computed: { |
| 170 | 193 | isSHILevel() { |
| ... | ... | @@ -173,29 +196,90 @@ export default { |
| 173 | 196 | }, |
| 174 | 197 | }, |
| 175 | 198 | methods: { |
| 176 | - getMsgList() { | |
| 177 | - for (let index = 0; index < 10; index++) { | |
| 178 | - this.msgList.push(todoObj); | |
| 179 | - this.todoTableData.push(todoObj); | |
| 199 | + // 通知公告 | |
| 200 | + getAllMsgList() { | |
| 201 | + this.msgLoading = true; | |
| 202 | + let _query = { | |
| 203 | + ...this.msgListQuery, | |
| 204 | + ...this.msgQuery, | |
| 205 | + }; | |
| 206 | + let query = {}; | |
| 207 | + for (let key in _query) { | |
| 208 | + if (Array.isArray(_query[key])) { | |
| 209 | + query[key] = _query[key].join(); | |
| 210 | + } else { | |
| 211 | + query[key] = _query[key]; | |
| 212 | + } | |
| 180 | 213 | } |
| 214 | + request({ | |
| 215 | + url: '/SubDev/ZyOaArticle', | |
| 216 | + method: "GET", | |
| 217 | + params: query, | |
| 218 | + }).then((res) => { | |
| 219 | + this.msgList = res.data.list; | |
| 220 | + this.msgTotal = res.data.pagination.total; | |
| 221 | + this.msgLoading = false; | |
| 222 | + }); | |
| 181 | 223 | }, |
| 182 | 224 | resetMsg() { |
| 183 | 225 | this.msgList = []; |
| 184 | - this.getMsgList(); | |
| 226 | + this.msgListQuery = { | |
| 227 | + currentPage: 1, | |
| 228 | + pageSize: 20, | |
| 229 | + }; | |
| 230 | + this.getAllMsgList(); | |
| 185 | 231 | }, |
| 186 | - getTipList() { | |
| 187 | - for (let index = 0; index < 10; index++) { | |
| 188 | - this.tipTableData.push(tipObj); | |
| 189 | - } | |
| 232 | + msgRefresh(val) { | |
| 233 | + this.MsgFormVisible = false; | |
| 234 | + val && this.getAllMsgList(); | |
| 235 | + }, | |
| 236 | + checkDetail(row) { | |
| 237 | + console.log(row); | |
| 238 | + this.MsgFormVisible = true; | |
| 239 | + this.$nextTick(() => { | |
| 240 | + this.$refs.MsgForm.init(row.id, true); | |
| 241 | + }) | |
| 190 | 242 | }, |
| 191 | - getAimList() { | |
| 192 | - for (let index = 0; index < 12; index++) { | |
| 193 | - if (Math.random() > 0.5) { | |
| 194 | - this.aimList.push(areaObj); | |
| 243 | + // 发布消息通知 | |
| 244 | + announceMsg() { | |
| 245 | + this.MsgFormVisible = true; | |
| 246 | + this.$nextTick(() => { | |
| 247 | + this.$refs.MsgForm.init(); | |
| 248 | + }) | |
| 249 | + }, | |
| 250 | + | |
| 251 | + // 任务中心 | |
| 252 | + getAllTaskList() { | |
| 253 | + this.taskLoading = true; | |
| 254 | + let _query = { | |
| 255 | + ...this.taskListQuery, | |
| 256 | + ...this.taskQuery, | |
| 257 | + }; | |
| 258 | + let query = {}; | |
| 259 | + for (let key in _query) { | |
| 260 | + if (Array.isArray(_query[key])) { | |
| 261 | + query[key] = _query[key].join(); | |
| 195 | 262 | } else { |
| 196 | - this.aimList.push(chObj); | |
| 263 | + query[key] = _query[key]; | |
| 197 | 264 | } |
| 198 | 265 | } |
| 266 | + request({ | |
| 267 | + url: '/Extend/BaseTaskCenter/GetListByCurretUser', | |
| 268 | + method: "GET", | |
| 269 | + params: query, | |
| 270 | + }).then((res) => { | |
| 271 | + this.taskList = res.data.list; | |
| 272 | + this.taskTotal = res.data.pagination.total; | |
| 273 | + this.taskLoading = false; | |
| 274 | + }); | |
| 275 | + }, | |
| 276 | + resetTask() { | |
| 277 | + this.taskList = []; | |
| 278 | + this.taskListQuery = { | |
| 279 | + currentPage: 1, | |
| 280 | + pageSize: 20, | |
| 281 | + }; | |
| 282 | + this.getAllTaskList(); | |
| 199 | 283 | }, |
| 200 | 284 | }, |
| 201 | 285 | }; |
| ... | ... | @@ -234,7 +318,7 @@ export default { |
| 234 | 318 | } |
| 235 | 319 | } |
| 236 | 320 | .item-body { |
| 237 | - height: calc(100% - 68px); | |
| 321 | + height: calc(100% - 95px); | |
| 238 | 322 | margin: 0 13px; |
| 239 | 323 | // padding-bottom: 28px; |
| 240 | 324 | .el-table { | ... | ... |
src/views/overView/msgForm.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + :title="isDetail ? '通知详情' : '发布通知'" | |
| 4 | + :close-on-click-modal="false" | |
| 5 | + :visible.sync="visible" | |
| 6 | + class="NCC-dialog NCC-dialog_center msg-dialog" | |
| 7 | + lock-scroll | |
| 8 | + width="50%" | |
| 9 | + > | |
| 10 | + <el-row :gutter="15" style="padding-top: 10px"> | |
| 11 | + <el-form | |
| 12 | + ref="elForm" | |
| 13 | + :model="dataForm" | |
| 14 | + size="small" | |
| 15 | + label-width="100px" | |
| 16 | + label-position="right" | |
| 17 | + :disabled="!!isDetail" | |
| 18 | + :rules="rules"> | |
| 19 | + <el-col :span="24"> | |
| 20 | + <el-form-item label="通知标题" prop="title"> | |
| 21 | + <el-input v-model="dataForm.title"></el-input> | |
| 22 | + </el-form-item> | |
| 23 | + </el-col> | |
| 24 | + <el-col :span="24"> | |
| 25 | + <el-form-item label="内容" prop="bodyContent"> | |
| 26 | + <NCC-Quill v-model="dataForm.bodyContent" placeholder="请输入内容..." :disabled="!!isDetail" /> | |
| 27 | + </el-form-item> | |
| 28 | + </el-col> | |
| 29 | + <el-col :span="24"> | |
| 30 | + <el-form-item label="图片上传" prop="imgUrl"> | |
| 31 | + <NCC-UploadFz v-model="dataForm.imgUrl" :fileSize="5" sizeUnit="MB" :limit="9" buttonText="点击上传" /> | |
| 32 | + </el-form-item> | |
| 33 | + </el-col> | |
| 34 | + </el-form> | |
| 35 | + </el-row> | |
| 36 | + <span slot="footer" class="dialog-footer"> | |
| 37 | + <el-button @click="visible = false">取 消</el-button> | |
| 38 | + <el-button type="primary" @click="dataFormSubmit()" v-if="!isDetail">确 定</el-button> | |
| 39 | + </span> | |
| 40 | + </el-dialog> | |
| 41 | + </template> | |
| 42 | + <script> | |
| 43 | + import request from "@/utils/request"; | |
| 44 | + export default { | |
| 45 | + components: {}, | |
| 46 | + props: [], | |
| 47 | + data() { | |
| 48 | + return { | |
| 49 | + loading: false, | |
| 50 | + visible: false, | |
| 51 | + isDetail: false, | |
| 52 | + dataForm: { | |
| 53 | + id: '', | |
| 54 | + title: '', | |
| 55 | + bodyContent: '', | |
| 56 | + imgUrl: [], | |
| 57 | + categoryId: 'cc225c68421644f79037aaf624ccccc0', | |
| 58 | + isType: '0', | |
| 59 | + }, | |
| 60 | + rules: { | |
| 61 | + title: [ | |
| 62 | + { required: true, message: '请输入标题', trigger: 'blur' }, | |
| 63 | + ], | |
| 64 | + bodyContent: [ | |
| 65 | + { required: true, message: '请输入内容', trigger: 'blur' }, | |
| 66 | + ], | |
| 67 | + }, | |
| 68 | + }; | |
| 69 | + }, | |
| 70 | + computed: {}, | |
| 71 | + watch: {}, | |
| 72 | + created() { | |
| 73 | + | |
| 74 | + }, | |
| 75 | + mounted() {}, | |
| 76 | + beforeDestroy() { | |
| 77 | + }, | |
| 78 | + methods: { | |
| 79 | + | |
| 80 | + goBack() { | |
| 81 | + this.$emit("refresh"); | |
| 82 | + }, | |
| 83 | + init(id, isDetail) { | |
| 84 | + this.dataForm.id = id || 0; | |
| 85 | + this.visible = true; | |
| 86 | + this.isDetail = isDetail || false; | |
| 87 | + this.$nextTick(() => { | |
| 88 | + this.$refs["elForm"].resetFields(); | |
| 89 | + if(this.dataForm.id) { | |
| 90 | + request({ | |
| 91 | + url: `/SubDev/ZyOaArticle/${this.dataForm.id}`, | |
| 92 | + method: "GET", | |
| 93 | + }).then((res) => { | |
| 94 | + this.dataForm = res.data; | |
| 95 | + console.log(this.dataForm); | |
| 96 | + }); | |
| 97 | + } | |
| 98 | + }); | |
| 99 | + }, | |
| 100 | + dataFormSubmit() { | |
| 101 | + this.$refs['elForm'].validate((valid) => { | |
| 102 | + if (valid) { | |
| 103 | + request({ | |
| 104 | + url: `/SubDev/ZyOaArticle`, | |
| 105 | + method: 'post', | |
| 106 | + data: this.dataForm, | |
| 107 | + }).then((res) => { | |
| 108 | + this.$message({ | |
| 109 | + message: res.msg, | |
| 110 | + type: 'success', | |
| 111 | + duration: 1000, | |
| 112 | + onClose: () => { | |
| 113 | + this.visible = false, this.$emit('refresh', true) | |
| 114 | + } | |
| 115 | + }) | |
| 116 | + }) | |
| 117 | + } | |
| 118 | + }) | |
| 119 | + }, | |
| 120 | + }, | |
| 121 | + }; | |
| 122 | + </script> | |
| 123 | + <style lang="scss" scoped> | |
| 124 | + .msg-dialog { | |
| 125 | + :deep(.el-dialog__body) { | |
| 126 | + max-height: 50vh; | |
| 127 | + overflow-y: scroll; | |
| 128 | + } | |
| 129 | + } | |
| 130 | + </style> | |
| 131 | + | |
| 0 | 132 | \ No newline at end of file | ... | ... |