vue2-client 1.18.21 → 1.18.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +112 -112
- package/src/base-client/components/common/HIS/HButtons/HButtons.vue +1 -1
- package/src/base-client/components/his/HChart/HChart.vue +311 -16
- package/src/components/FileImageItem/FileItem.vue +320 -320
- package/src/components/FileImageItem/FileItemGroup.vue +297 -297
- package/src/pages/WorkflowDetail/WorkflowDetail.vue +404 -404
- package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowHandle.vue +1815 -1815
- package/src/pages/WorkflowDetail/WorkflowPageDetail/worklog.vue +98 -98
- package/src/pages/userInfoDetailManage/ApplyRecordQuery/index.vue +64 -64
- package/src/pages/userInfoDetailManage/SafeCheckPaperV3RecordQuery/index.vue +64 -64
- package/src/pages/userInfoDetailManage/TelephoneV3RecordQuery/index.vue +64 -64
- package/src/pages/userInfoDetailManage/userInfoDetailQueryTabs.vue +155 -155
package/package.json
CHANGED
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "vue2-client",
|
|
3
|
-
"version": "1.18.
|
|
4
|
-
"private": false,
|
|
5
|
-
"scripts": {
|
|
6
|
-
"serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint",
|
|
7
|
-
"serve:gaslink": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode gaslink",
|
|
8
|
-
"serve:revenue": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode revenue",
|
|
9
|
-
"serve:liuli": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode liuli",
|
|
10
|
-
"serve:scada": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode scada",
|
|
11
|
-
"serve:iot": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode iot",
|
|
12
|
-
"serve:his": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode his",
|
|
13
|
-
"serve:runtime": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode runtime",
|
|
14
|
-
"serve:message": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode message",
|
|
15
|
-
"serve:apply": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode apply",
|
|
16
|
-
"mac-serve": "vue-cli-service serve --no-eslint --mode his",
|
|
17
|
-
"build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build",
|
|
18
|
-
"test:unit": "vue-cli-service test:unit",
|
|
19
|
-
"lint": "vue-cli-service lint",
|
|
20
|
-
"build:preview": "vue-cli-service build --mode preview",
|
|
21
|
-
"lint:nofix": "vue-cli-service lint --no-fix",
|
|
22
|
-
"test": "jest"
|
|
23
|
-
},
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"@afwenming123/vue-easy-tree": "^1.0.1",
|
|
26
|
-
"@afwenming123/vue-plugin-hiprint": "^0.0.70",
|
|
27
|
-
"@amap/amap-jsapi-loader": "^1.0.1",
|
|
28
|
-
"@antv/data-set": "^0.11.8",
|
|
29
|
-
"@antv/g2plot": "^2.4.31",
|
|
30
|
-
"@hufe921/canvas-editor": "^0.9.49",
|
|
31
|
-
"@microsoft/fetch-event-source": "^2.0.1",
|
|
32
|
-
"@vue/babel-preset-jsx": "^1.4.0",
|
|
33
|
-
"animate.css": "^4.1.1",
|
|
34
|
-
"ant-design-vue": "^1.7.8",
|
|
35
|
-
"axios": "^0.27.2",
|
|
36
|
-
"clipboard": "^2.0.11",
|
|
37
|
-
"core-js": "^3.33.0",
|
|
38
|
-
"crypto-js": "^4.1.1",
|
|
39
|
-
"date-fns": "^2.29.3",
|
|
40
|
-
"default-passive-events": "^2.0.0",
|
|
41
|
-
"dotenv": "^16.3.1",
|
|
42
|
-
"echarts": "^5.5.0",
|
|
43
|
-
"enquire.js": "^2.1.6",
|
|
44
|
-
"file-saver": "^2.0.5",
|
|
45
|
-
"highlight.js": "^11.7.0",
|
|
46
|
-
"html2canvas": "^1.4.1",
|
|
47
|
-
"js-base64": "^3.7.5",
|
|
48
|
-
"js-cookie": "^2.2.1",
|
|
49
|
-
"jsencrypt": "^3.3.2",
|
|
50
|
-
"jspdf": "^2.5.1",
|
|
51
|
-
"lodash.clonedeep": "^4.5.0",
|
|
52
|
-
"lodash.debounce": "^4",
|
|
53
|
-
"lodash.get": "^4.4.2",
|
|
54
|
-
"marked": "^4",
|
|
55
|
-
"mockjs": "^1.1.0",
|
|
56
|
-
"nprogress": "^0.2.0",
|
|
57
|
-
"qs": "^6.11.2",
|
|
58
|
-
"regenerator-runtime": "^0.14.0",
|
|
59
|
-
"splitpanes": "^2.4.1",
|
|
60
|
-
"videojs-contrib-hls": "^5.15.0",
|
|
61
|
-
"viser-vue": "^2.4.8",
|
|
62
|
-
"vue": "^2.7.14",
|
|
63
|
-
"vue-codemirror": "4.0.6",
|
|
64
|
-
"vue-color": "2.7.0",
|
|
65
|
-
"vue-draggable-resizable": "^2.3.0",
|
|
66
|
-
"vue-i18n": "^8.28.2",
|
|
67
|
-
"vue-json-viewer": "^2.2.22",
|
|
68
|
-
"vue-router": "^3.6.5",
|
|
69
|
-
"vue-video-player": "^5.0.2",
|
|
70
|
-
"vue-virtual-scroller": "^1.1.2",
|
|
71
|
-
"vuedraggable": "^2.24.3",
|
|
72
|
-
"vuex": "^3.6.2",
|
|
73
|
-
"xlsx": "0.18.5"
|
|
74
|
-
},
|
|
75
|
-
"devDependencies": {
|
|
76
|
-
"@ant-design/colors": "^7.0.0",
|
|
77
|
-
"@babel/core": "^7.22.20",
|
|
78
|
-
"@babel/eslint-parser": "^7.22.15",
|
|
79
|
-
"@babel/preset-env": "^7.22.20",
|
|
80
|
-
"@vue/cli-plugin-babel": "^5.0.8",
|
|
81
|
-
"@vue/cli-plugin-eslint": "^5.0.8",
|
|
82
|
-
"@vue/cli-service": "^5.0.8",
|
|
83
|
-
"@vue/eslint-config-standard": "^8.0.1",
|
|
84
|
-
"@vue/test-utils": "^1.3.6",
|
|
85
|
-
"babel-plugin-transform-remove-console": "^6.9.4",
|
|
86
|
-
"compression-webpack-plugin": "^10.0.0",
|
|
87
|
-
"css-minimizer-webpack-plugin": "^5.0.1",
|
|
88
|
-
"deepmerge": "^4.3.1",
|
|
89
|
-
"eslint": "^8.51.0",
|
|
90
|
-
"eslint-plugin-vue": "^9.17.0",
|
|
91
|
-
"fast-deep-equal": "^3.1.3",
|
|
92
|
-
"ignore-loader": "^0.1.2",
|
|
93
|
-
"jest": "^29.7.0",
|
|
94
|
-
"jest-environment-jsdom": "^29.7.0",
|
|
95
|
-
"jest-transform-stub": "^2.0.0",
|
|
96
|
-
"less-loader": "^6.2.0",
|
|
97
|
-
"script-loader": "^0.7.2",
|
|
98
|
-
"style-resources-loader": "^1.5.0",
|
|
99
|
-
"vue-cli-plugin-style-resources-loader": "^0.1.5",
|
|
100
|
-
"vue-jest": "^4.0.1",
|
|
101
|
-
"vue-template-compiler": "^2.7.14",
|
|
102
|
-
"webpack": "^5.88.2",
|
|
103
|
-
"webpack-theme-color-replacer": "^1.4.7",
|
|
104
|
-
"whatwg-fetch": "^3.6.19"
|
|
105
|
-
},
|
|
106
|
-
"browserslist": [
|
|
107
|
-
"> 1%",
|
|
108
|
-
"last 2 versions",
|
|
109
|
-
"not dead",
|
|
110
|
-
"not ie 11"
|
|
111
|
-
]
|
|
112
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "vue2-client",
|
|
3
|
+
"version": "1.18.23",
|
|
4
|
+
"private": false,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint",
|
|
7
|
+
"serve:gaslink": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode gaslink",
|
|
8
|
+
"serve:revenue": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode revenue",
|
|
9
|
+
"serve:liuli": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode liuli",
|
|
10
|
+
"serve:scada": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode scada",
|
|
11
|
+
"serve:iot": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode iot",
|
|
12
|
+
"serve:his": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode his",
|
|
13
|
+
"serve:runtime": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode runtime",
|
|
14
|
+
"serve:message": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode message",
|
|
15
|
+
"serve:apply": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode apply",
|
|
16
|
+
"mac-serve": "vue-cli-service serve --no-eslint --mode his",
|
|
17
|
+
"build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build",
|
|
18
|
+
"test:unit": "vue-cli-service test:unit",
|
|
19
|
+
"lint": "vue-cli-service lint",
|
|
20
|
+
"build:preview": "vue-cli-service build --mode preview",
|
|
21
|
+
"lint:nofix": "vue-cli-service lint --no-fix",
|
|
22
|
+
"test": "jest"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@afwenming123/vue-easy-tree": "^1.0.1",
|
|
26
|
+
"@afwenming123/vue-plugin-hiprint": "^0.0.70",
|
|
27
|
+
"@amap/amap-jsapi-loader": "^1.0.1",
|
|
28
|
+
"@antv/data-set": "^0.11.8",
|
|
29
|
+
"@antv/g2plot": "^2.4.31",
|
|
30
|
+
"@hufe921/canvas-editor": "^0.9.49",
|
|
31
|
+
"@microsoft/fetch-event-source": "^2.0.1",
|
|
32
|
+
"@vue/babel-preset-jsx": "^1.4.0",
|
|
33
|
+
"animate.css": "^4.1.1",
|
|
34
|
+
"ant-design-vue": "^1.7.8",
|
|
35
|
+
"axios": "^0.27.2",
|
|
36
|
+
"clipboard": "^2.0.11",
|
|
37
|
+
"core-js": "^3.33.0",
|
|
38
|
+
"crypto-js": "^4.1.1",
|
|
39
|
+
"date-fns": "^2.29.3",
|
|
40
|
+
"default-passive-events": "^2.0.0",
|
|
41
|
+
"dotenv": "^16.3.1",
|
|
42
|
+
"echarts": "^5.5.0",
|
|
43
|
+
"enquire.js": "^2.1.6",
|
|
44
|
+
"file-saver": "^2.0.5",
|
|
45
|
+
"highlight.js": "^11.7.0",
|
|
46
|
+
"html2canvas": "^1.4.1",
|
|
47
|
+
"js-base64": "^3.7.5",
|
|
48
|
+
"js-cookie": "^2.2.1",
|
|
49
|
+
"jsencrypt": "^3.3.2",
|
|
50
|
+
"jspdf": "^2.5.1",
|
|
51
|
+
"lodash.clonedeep": "^4.5.0",
|
|
52
|
+
"lodash.debounce": "^4",
|
|
53
|
+
"lodash.get": "^4.4.2",
|
|
54
|
+
"marked": "^4",
|
|
55
|
+
"mockjs": "^1.1.0",
|
|
56
|
+
"nprogress": "^0.2.0",
|
|
57
|
+
"qs": "^6.11.2",
|
|
58
|
+
"regenerator-runtime": "^0.14.0",
|
|
59
|
+
"splitpanes": "^2.4.1",
|
|
60
|
+
"videojs-contrib-hls": "^5.15.0",
|
|
61
|
+
"viser-vue": "^2.4.8",
|
|
62
|
+
"vue": "^2.7.14",
|
|
63
|
+
"vue-codemirror": "4.0.6",
|
|
64
|
+
"vue-color": "2.7.0",
|
|
65
|
+
"vue-draggable-resizable": "^2.3.0",
|
|
66
|
+
"vue-i18n": "^8.28.2",
|
|
67
|
+
"vue-json-viewer": "^2.2.22",
|
|
68
|
+
"vue-router": "^3.6.5",
|
|
69
|
+
"vue-video-player": "^5.0.2",
|
|
70
|
+
"vue-virtual-scroller": "^1.1.2",
|
|
71
|
+
"vuedraggable": "^2.24.3",
|
|
72
|
+
"vuex": "^3.6.2",
|
|
73
|
+
"xlsx": "0.18.5"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@ant-design/colors": "^7.0.0",
|
|
77
|
+
"@babel/core": "^7.22.20",
|
|
78
|
+
"@babel/eslint-parser": "^7.22.15",
|
|
79
|
+
"@babel/preset-env": "^7.22.20",
|
|
80
|
+
"@vue/cli-plugin-babel": "^5.0.8",
|
|
81
|
+
"@vue/cli-plugin-eslint": "^5.0.8",
|
|
82
|
+
"@vue/cli-service": "^5.0.8",
|
|
83
|
+
"@vue/eslint-config-standard": "^8.0.1",
|
|
84
|
+
"@vue/test-utils": "^1.3.6",
|
|
85
|
+
"babel-plugin-transform-remove-console": "^6.9.4",
|
|
86
|
+
"compression-webpack-plugin": "^10.0.0",
|
|
87
|
+
"css-minimizer-webpack-plugin": "^5.0.1",
|
|
88
|
+
"deepmerge": "^4.3.1",
|
|
89
|
+
"eslint": "^8.51.0",
|
|
90
|
+
"eslint-plugin-vue": "^9.17.0",
|
|
91
|
+
"fast-deep-equal": "^3.1.3",
|
|
92
|
+
"ignore-loader": "^0.1.2",
|
|
93
|
+
"jest": "^29.7.0",
|
|
94
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
95
|
+
"jest-transform-stub": "^2.0.0",
|
|
96
|
+
"less-loader": "^6.2.0",
|
|
97
|
+
"script-loader": "^0.7.2",
|
|
98
|
+
"style-resources-loader": "^1.5.0",
|
|
99
|
+
"vue-cli-plugin-style-resources-loader": "^0.1.5",
|
|
100
|
+
"vue-jest": "^4.0.1",
|
|
101
|
+
"vue-template-compiler": "^2.7.14",
|
|
102
|
+
"webpack": "^5.88.2",
|
|
103
|
+
"webpack-theme-color-replacer": "^1.4.7",
|
|
104
|
+
"whatwg-fetch": "^3.6.19"
|
|
105
|
+
},
|
|
106
|
+
"browserslist": [
|
|
107
|
+
"> 1%",
|
|
108
|
+
"last 2 versions",
|
|
109
|
+
"not dead",
|
|
110
|
+
"not ie 11"
|
|
111
|
+
]
|
|
112
|
+
}
|
|
@@ -340,7 +340,7 @@ defineExpose({
|
|
|
340
340
|
.ant-btn {
|
|
341
341
|
border: none; /* 移除边框 */
|
|
342
342
|
color: #FFFFFF; /* 字体颜色 */
|
|
343
|
-
background-color: #
|
|
343
|
+
background-color: #0057FE; /* 背景色 */
|
|
344
344
|
box-sizing: border-box; /* 盒模型 */
|
|
345
345
|
left: 12px; /* 左偏移 */
|
|
346
346
|
width: 280px; /* 宽度 */
|
|
@@ -19,6 +19,36 @@
|
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
21
21
|
<div class="chart-main">
|
|
22
|
+
<div v-if="showCascadeList" class="chart-cascade-panel">
|
|
23
|
+
<div v-if="sideListLabel" class="cascade-panel-label">{{ sideListLabel }}</div>
|
|
24
|
+
<div class="cascade-columns">
|
|
25
|
+
<div
|
|
26
|
+
v-for="column in cascadeColumns"
|
|
27
|
+
:key="column.field"
|
|
28
|
+
class="cascade-column"
|
|
29
|
+
:class="{ 'is-disabled': !column.options.length }"
|
|
30
|
+
>
|
|
31
|
+
<div class="cascade-column-header">
|
|
32
|
+
<span class="column-title">{{ column.label }}</span>
|
|
33
|
+
<span v-if="column.subtitle" class="column-subtitle">{{ column.subtitle }}</span>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="cascade-option-list">
|
|
36
|
+
<button
|
|
37
|
+
v-for="option in column.options"
|
|
38
|
+
:key="option.value"
|
|
39
|
+
type="button"
|
|
40
|
+
class="cascade-option"
|
|
41
|
+
:class="{ 'is-active': option.value === selectedCascadeValues[column.field] }"
|
|
42
|
+
:disabled="!column.options.length"
|
|
43
|
+
@click="handleCascadeChange(column.field, option.value)"
|
|
44
|
+
>
|
|
45
|
+
<span class="option-label">{{ option.label }}</span>
|
|
46
|
+
<span v-if="option.badge" class="option-badge">{{ option.badge }}</span>
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
22
52
|
<div v-if="showRadioFilter" class="chart-toolbar">
|
|
23
53
|
<span v-if="radioFilterLabel" class="toolbar-label">{{ radioFilterLabel }}</span>
|
|
24
54
|
<div class="toolbar-radio-group">
|
|
@@ -90,6 +120,7 @@ const activePointIndex = ref(-1)
|
|
|
90
120
|
const activeBarIndex = ref(-1)
|
|
91
121
|
const selectedFilterValue = ref('')
|
|
92
122
|
const selectedSideListValue = ref('')
|
|
123
|
+
const selectedCascadeValues = ref({})
|
|
93
124
|
|
|
94
125
|
// 记录上一次的 queryParamsName,用于比较变化
|
|
95
126
|
const lastQueryParamsName = ref('')
|
|
@@ -124,19 +155,85 @@ const filterQueryParams = computed(() => {
|
|
|
124
155
|
return { [filterFieldName.value]: optionValue }
|
|
125
156
|
})
|
|
126
157
|
const sideListConfig = computed(() => props.config?.sideList || chartConfig.value?.sideList || null)
|
|
158
|
+
const isCascadeMode = computed(() => sideListConfig.value?.mode === 'cascade')
|
|
127
159
|
const sideListOptions = computed(() => {
|
|
128
160
|
const options = sideListConfig.value?.options
|
|
129
161
|
return Array.isArray(options) ? options : []
|
|
130
162
|
})
|
|
131
163
|
const sideListLabel = computed(() => sideListConfig.value?.label || sideListConfig.value?.title || '')
|
|
132
|
-
const
|
|
164
|
+
const cascadeLevels = computed(() => {
|
|
165
|
+
if (!isCascadeMode.value) return []
|
|
166
|
+
const levels = sideListConfig.value?.levels
|
|
167
|
+
return Array.isArray(levels) ? levels : []
|
|
168
|
+
})
|
|
169
|
+
const pickFirstArrayFromObject = (source) => {
|
|
170
|
+
if (!source || typeof source !== 'object') return []
|
|
171
|
+
for (const value of Object.values(source)) {
|
|
172
|
+
if (Array.isArray(value)) return value
|
|
173
|
+
}
|
|
174
|
+
return []
|
|
175
|
+
}
|
|
176
|
+
const normalizeOptionList = (options) => {
|
|
177
|
+
if (!options) return []
|
|
178
|
+
if (Array.isArray(options)) return options
|
|
179
|
+
return []
|
|
180
|
+
}
|
|
181
|
+
const cascadeColumns = computed(() => {
|
|
182
|
+
if (!isCascadeMode.value) return []
|
|
183
|
+
const columns = []
|
|
184
|
+
cascadeLevels.value.forEach((level, idx) => {
|
|
185
|
+
const field = level?.field || level?.prop || `cascade-field-${idx}`
|
|
186
|
+
const optionsSource = level?.options
|
|
187
|
+
let scopedOptions = normalizeOptionList(optionsSource)
|
|
188
|
+
if (!Array.isArray(optionsSource) && optionsSource && typeof optionsSource === 'object') {
|
|
189
|
+
if (idx === 0) {
|
|
190
|
+
const firstArray = optionsSource.default || optionsSource.list || pickFirstArrayFromObject(optionsSource)
|
|
191
|
+
scopedOptions = normalizeOptionList(firstArray)
|
|
192
|
+
} else {
|
|
193
|
+
const parentField = columns[idx - 1]?.field
|
|
194
|
+
const parentValue = parentField ? selectedCascadeValues.value[parentField] : undefined
|
|
195
|
+
const mappedOptions = (parentValue && optionsSource[parentValue]) || optionsSource.default || pickFirstArrayFromObject(optionsSource)
|
|
196
|
+
scopedOptions = normalizeOptionList(mappedOptions)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
columns.push({
|
|
200
|
+
label: level?.label || level?.title || '',
|
|
201
|
+
subtitle: level?.subtitle || '',
|
|
202
|
+
field,
|
|
203
|
+
defaultValue: level?.defaultValue,
|
|
204
|
+
options: scopedOptions
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
return columns
|
|
208
|
+
})
|
|
209
|
+
const showSideList = computed(() => !isCascadeMode.value && sideListOptions.value.length > 0)
|
|
210
|
+
const showCascadeList = computed(() => isCascadeMode.value && cascadeColumns.value.some(column => column.options.length))
|
|
133
211
|
const sideListFieldName = computed(() => sideListConfig.value?.field || sideListConfig.value?.prop || 'category')
|
|
212
|
+
const cascadeQueryParams = computed(() => {
|
|
213
|
+
if (!isCascadeMode.value) return {}
|
|
214
|
+
return cascadeColumns.value.reduce((params, column) => {
|
|
215
|
+
const field = column.field
|
|
216
|
+
if (!field) return params
|
|
217
|
+
const value = selectedCascadeValues.value[field]
|
|
218
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
219
|
+
params[field] = value
|
|
220
|
+
}
|
|
221
|
+
return params
|
|
222
|
+
}, {})
|
|
223
|
+
})
|
|
134
224
|
const listQueryParams = computed(() => {
|
|
225
|
+
if (isCascadeMode.value) return cascadeQueryParams.value
|
|
135
226
|
if (!showSideList.value) return {}
|
|
136
227
|
const optionValue = selectedSideListValue.value
|
|
137
228
|
if (optionValue === undefined || optionValue === null || optionValue === '') return {}
|
|
138
229
|
return { [sideListFieldName.value]: optionValue }
|
|
139
230
|
})
|
|
231
|
+
const cascadeSelectedKeys = computed(() => {
|
|
232
|
+
if (!isCascadeMode.value) return []
|
|
233
|
+
return cascadeColumns.value
|
|
234
|
+
.map(column => column.field && selectedCascadeValues.value[column.field])
|
|
235
|
+
.filter(value => value !== undefined && value !== null && value !== '')
|
|
236
|
+
})
|
|
140
237
|
const resolveStaticDataset = (config) => {
|
|
141
238
|
const dataset = config?.dataset
|
|
142
239
|
if (Array.isArray(dataset)) return dataset
|
|
@@ -163,15 +260,37 @@ const resolveStaticDataset = (config) => {
|
|
|
163
260
|
}
|
|
164
261
|
return null
|
|
165
262
|
}
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
263
|
+
const pickNestedByKeys = (source, keys) => {
|
|
264
|
+
if (!source || typeof source !== 'object' || !keys.length) return null
|
|
265
|
+
let scoped = source
|
|
266
|
+
for (const key of keys) {
|
|
267
|
+
if (!key) continue
|
|
268
|
+
if (!scoped || typeof scoped !== 'object') return null
|
|
269
|
+
if (!(key in scoped)) return null
|
|
270
|
+
scoped = scoped[key]
|
|
271
|
+
}
|
|
272
|
+
return scoped
|
|
273
|
+
}
|
|
170
274
|
let scopedDataset = dataset
|
|
171
|
-
if (
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
275
|
+
if (isCascadeMode.value) {
|
|
276
|
+
const cascadeKeys = cascadeColumns.value
|
|
277
|
+
.map(column => column.field && selectedCascadeValues.value[column.field])
|
|
278
|
+
.filter(value => value !== undefined && value !== null && value !== '')
|
|
279
|
+
if (cascadeKeys.length) {
|
|
280
|
+
const cascadeScoped = pickNestedByKeys(dataset, cascadeKeys)
|
|
281
|
+
if (Array.isArray(cascadeScoped)) return cascadeScoped
|
|
282
|
+
if (cascadeScoped && typeof cascadeScoped === 'object') scopedDataset = cascadeScoped
|
|
283
|
+
}
|
|
284
|
+
} else {
|
|
285
|
+
const listKeys = [
|
|
286
|
+
selectedSideListValue.value,
|
|
287
|
+
sideListConfig.value?.defaultValue
|
|
288
|
+
].filter(Boolean)
|
|
289
|
+
if (listKeys.length) {
|
|
290
|
+
const listScoped = pickByKeys(dataset, listKeys, true)
|
|
291
|
+
if (Array.isArray(listScoped)) return listScoped
|
|
292
|
+
if (listScoped && typeof listScoped === 'object') scopedDataset = listScoped
|
|
293
|
+
}
|
|
175
294
|
}
|
|
176
295
|
if (Array.isArray(scopedDataset)) return scopedDataset
|
|
177
296
|
const filterKeys = [
|
|
@@ -233,13 +352,70 @@ const ensureListSelection = () => {
|
|
|
233
352
|
const fallback = sideListConfig.value?.defaultValue ?? options[0]?.value ?? ''
|
|
234
353
|
selectedSideListValue.value = fallback
|
|
235
354
|
}
|
|
355
|
+
const ensureCascadeSelections = () => {
|
|
356
|
+
if (!isCascadeMode.value) {
|
|
357
|
+
if (Object.keys(selectedCascadeValues.value).length) {
|
|
358
|
+
selectedCascadeValues.value = {}
|
|
359
|
+
}
|
|
360
|
+
return
|
|
361
|
+
}
|
|
362
|
+
const nextSelections = { ...selectedCascadeValues.value }
|
|
363
|
+
let changed = false
|
|
364
|
+
cascadeColumns.value.forEach(column => {
|
|
365
|
+
const field = column.field
|
|
366
|
+
if (!field) return
|
|
367
|
+
const options = column.options || []
|
|
368
|
+
if (!options.length) {
|
|
369
|
+
if (nextSelections[field]) {
|
|
370
|
+
delete nextSelections[field]
|
|
371
|
+
changed = true
|
|
372
|
+
}
|
|
373
|
+
return
|
|
374
|
+
}
|
|
375
|
+
const exists = options.some(option => option.value === nextSelections[field])
|
|
376
|
+
if (exists) return
|
|
377
|
+
const fallback = column.defaultValue ?? options[0]?.value ?? ''
|
|
378
|
+
if (fallback !== undefined && fallback !== null && fallback !== '') {
|
|
379
|
+
if (nextSelections[field] !== fallback) {
|
|
380
|
+
nextSelections[field] = fallback
|
|
381
|
+
changed = true
|
|
382
|
+
}
|
|
383
|
+
} else if (nextSelections[field]) {
|
|
384
|
+
delete nextSelections[field]
|
|
385
|
+
changed = true
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
if (changed) {
|
|
389
|
+
selectedCascadeValues.value = { ...nextSelections }
|
|
390
|
+
}
|
|
391
|
+
}
|
|
236
392
|
|
|
237
393
|
watch([radioFilterOptions, () => radioFilterConfig.value?.defaultValue], () => {
|
|
238
394
|
ensureFilterSelection()
|
|
239
395
|
}, { immediate: true })
|
|
240
|
-
watch(
|
|
241
|
-
|
|
242
|
-
|
|
396
|
+
watch(
|
|
397
|
+
() => ({
|
|
398
|
+
options: sideListOptions.value,
|
|
399
|
+
defaultValue: sideListConfig.value?.defaultValue,
|
|
400
|
+
cascadeMode: isCascadeMode.value
|
|
401
|
+
}),
|
|
402
|
+
() => {
|
|
403
|
+
if (isCascadeMode.value) return
|
|
404
|
+
ensureListSelection()
|
|
405
|
+
},
|
|
406
|
+
{ immediate: true, deep: true }
|
|
407
|
+
)
|
|
408
|
+
watch(
|
|
409
|
+
() => cascadeColumns.value.map(column => ({
|
|
410
|
+
field: column.field,
|
|
411
|
+
defaultValue: column.defaultValue,
|
|
412
|
+
optionValues: column.options?.map(option => option.value)
|
|
413
|
+
})),
|
|
414
|
+
() => {
|
|
415
|
+
ensureCascadeSelections()
|
|
416
|
+
},
|
|
417
|
+
{ immediate: true, deep: true }
|
|
418
|
+
)
|
|
243
419
|
|
|
244
420
|
const handleFilterChange = (value) => {
|
|
245
421
|
if (value === selectedFilterValue.value) return
|
|
@@ -257,6 +433,26 @@ const handleListChange = (value) => {
|
|
|
257
433
|
loadChartData(config)
|
|
258
434
|
}
|
|
259
435
|
}
|
|
436
|
+
const handleCascadeChange = (field, value) => {
|
|
437
|
+
if (!isCascadeMode.value || !field) return
|
|
438
|
+
if (selectedCascadeValues.value[field] === value) return
|
|
439
|
+
const updatedSelections = { ...selectedCascadeValues.value, [field]: value }
|
|
440
|
+
const startIndex = cascadeColumns.value.findIndex(column => column.field === field)
|
|
441
|
+
if (startIndex >= 0) {
|
|
442
|
+
for (let idx = startIndex + 1; idx < cascadeColumns.value.length; idx++) {
|
|
443
|
+
const childField = cascadeColumns.value[idx].field
|
|
444
|
+
if (childField && updatedSelections[childField] !== undefined) {
|
|
445
|
+
delete updatedSelections[childField]
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
selectedCascadeValues.value = updatedSelections
|
|
450
|
+
ensureCascadeSelections()
|
|
451
|
+
const config = chartConfig.value || props.config
|
|
452
|
+
if (config) {
|
|
453
|
+
loadChartData(config)
|
|
454
|
+
}
|
|
455
|
+
}
|
|
260
456
|
|
|
261
457
|
// 常用图表预设,统一处理 dataset → ECharts option 的映射
|
|
262
458
|
const presetResolvers = {
|
|
@@ -494,17 +690,18 @@ const presetResolvers = {
|
|
|
494
690
|
type: 'pie',
|
|
495
691
|
radius: '55%',
|
|
496
692
|
startAngle: 180,
|
|
693
|
+
hoverOffset: 1,
|
|
497
694
|
data: dataset.map(item => ({ name: item.label, value: item.value })),
|
|
498
695
|
itemStyle: {
|
|
499
696
|
borderColor: '#fff',
|
|
500
|
-
borderWidth:
|
|
697
|
+
borderWidth: 1.5
|
|
501
698
|
},
|
|
502
699
|
emphasis: {
|
|
503
700
|
scale: true,
|
|
504
|
-
scaleSize:
|
|
701
|
+
scaleSize: 1,
|
|
505
702
|
itemStyle: {
|
|
506
|
-
shadowBlur:
|
|
507
|
-
shadowColor: 'rgba(0, 0, 0, 0.
|
|
703
|
+
shadowBlur: 12,
|
|
704
|
+
shadowColor: 'rgba(0, 0, 0, 0.22)'
|
|
508
705
|
},
|
|
509
706
|
label: {
|
|
510
707
|
show: true,
|
|
@@ -666,6 +863,7 @@ const fetchConfigAndData = async () => {
|
|
|
666
863
|
const loadChartData = async (config) => {
|
|
667
864
|
if (!config) return
|
|
668
865
|
ensureListSelection()
|
|
866
|
+
ensureCascadeSelections()
|
|
669
867
|
ensureFilterSelection()
|
|
670
868
|
|
|
671
869
|
try {
|
|
@@ -978,6 +1176,103 @@ onBeforeUnmount(() => {
|
|
|
978
1176
|
flex-direction: column;
|
|
979
1177
|
}
|
|
980
1178
|
|
|
1179
|
+
.chart-cascade-panel {
|
|
1180
|
+
display: flex;
|
|
1181
|
+
flex-direction: column;
|
|
1182
|
+
gap: 8px;
|
|
1183
|
+
margin-top: 4px;
|
|
1184
|
+
width: 100%;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
.cascade-panel-label {
|
|
1188
|
+
font-size: 14px;
|
|
1189
|
+
color: #4A5875;
|
|
1190
|
+
font-weight: 600;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
.cascade-columns {
|
|
1194
|
+
display: flex;
|
|
1195
|
+
gap: 16px;
|
|
1196
|
+
width: 100%;
|
|
1197
|
+
flex-wrap: wrap;
|
|
1198
|
+
overflow-x: auto;
|
|
1199
|
+
padding-bottom: 4px;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
.cascade-column {
|
|
1203
|
+
flex: 1 1 180px;
|
|
1204
|
+
min-width: 180px;
|
|
1205
|
+
border-right: 1px solid #ECF0F7;
|
|
1206
|
+
padding-right: 12px;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
.cascade-column:last-child {
|
|
1210
|
+
border-right: none;
|
|
1211
|
+
padding-right: 0;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
.cascade-column.is-disabled {
|
|
1215
|
+
opacity: 0.6;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
.cascade-column-header {
|
|
1219
|
+
display: flex;
|
|
1220
|
+
justify-content: space-between;
|
|
1221
|
+
align-items: center;
|
|
1222
|
+
font-size: 13px;
|
|
1223
|
+
color: #5C6C8C;
|
|
1224
|
+
font-weight: 600;
|
|
1225
|
+
margin-bottom: 6px;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
.column-subtitle {
|
|
1229
|
+
font-size: 12px;
|
|
1230
|
+
color: #9AA7C5;
|
|
1231
|
+
font-weight: 400;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
.cascade-option-list {
|
|
1235
|
+
display: flex;
|
|
1236
|
+
flex-direction: column;
|
|
1237
|
+
gap: 6px;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
.cascade-option {
|
|
1241
|
+
width: 100%;
|
|
1242
|
+
text-align: left;
|
|
1243
|
+
border: 0;
|
|
1244
|
+
background: transparent;
|
|
1245
|
+
padding: 8px 10px;
|
|
1246
|
+
border-radius: 8px;
|
|
1247
|
+
font-size: 13px;
|
|
1248
|
+
color: #4A5875;
|
|
1249
|
+
cursor: pointer;
|
|
1250
|
+
transition: all 0.15s ease;
|
|
1251
|
+
display: flex;
|
|
1252
|
+
justify-content: space-between;
|
|
1253
|
+
align-items: center;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.cascade-option .option-label {
|
|
1257
|
+
flex: 1;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
.cascade-option .option-badge {
|
|
1261
|
+
font-size: 12px;
|
|
1262
|
+
color: #9AA7C5;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
.cascade-option.is-active {
|
|
1266
|
+
background: rgba(31, 91, 255, 0.08);
|
|
1267
|
+
color: #1F5BFF;
|
|
1268
|
+
font-weight: 600;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
.cascade-option:disabled {
|
|
1272
|
+
cursor: not-allowed;
|
|
1273
|
+
opacity: 0.6;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
981
1276
|
.chart-toolbar {
|
|
982
1277
|
display: flex;
|
|
983
1278
|
align-items: center;
|