vue2-client 1.15.138 → 1.15.141
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 +111 -111
- package/src/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm.vue +411 -0
- package/src/base-client/components/common/HIS/HAddNativeForm/index.js +3 -0
- package/src/base-client/components/common/HIS/HButtons/HButtons.vue +365 -324
- package/src/base-client/components/common/HIS/HFormTable/HFormTable.vue +10 -13
- package/src/base-client/components/common/HIS/HTab/HTab.vue +282 -281
- package/src/base-client/components/common/HIS/demo.vue +54 -46
- package/src/base-client/components/common/XFormTable/XFormTable.vue +6 -0
- package/src/base-client/components/common/XReportGrid/XReportTrGroup.vue +813 -810
- package/src/base-client/components/common/XTable/XTable.vue +5 -0
- package/src/base-client/components/common/XTable/XTableWrapper.vue +1 -0
- package/src/base-client/components/his/XSimpleTable/XSimpleTable.vue +119 -119
- package/src/components/STable/index.js +200 -23
- package/src/router/async/router.map.js +128 -128
- package/src/base-client/components/his/XCharge/XChargeDemo.vue +0 -32
package/package.json
CHANGED
@@ -1,111 +1,111 @@
|
|
1
|
-
{
|
2
|
-
"name": "vue2-client",
|
3
|
-
"version": "1.15.
|
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
|
-
"videojs-contrib-hls": "^5.15.0",
|
60
|
-
"viser-vue": "^2.4.8",
|
61
|
-
"vue": "^2.7.14",
|
62
|
-
"vue-codemirror": "4.0.6",
|
63
|
-
"vue-color": "2.7.0",
|
64
|
-
"vue-draggable-resizable": "^2.3.0",
|
65
|
-
"vue-i18n": "^8.28.2",
|
66
|
-
"vue-json-viewer": "^2.2.22",
|
67
|
-
"vue-router": "^3.6.5",
|
68
|
-
"vue-video-player": "^5.0.2",
|
69
|
-
"vue-virtual-scroller": "^1.1.2",
|
70
|
-
"vuedraggable": "^2.24.3",
|
71
|
-
"vuex": "^3.6.2",
|
72
|
-
"xlsx": "0.18.5"
|
73
|
-
},
|
74
|
-
"devDependencies": {
|
75
|
-
"@ant-design/colors": "^7.0.0",
|
76
|
-
"@babel/core": "^7.22.20",
|
77
|
-
"@babel/eslint-parser": "^7.22.15",
|
78
|
-
"@babel/preset-env": "^7.22.20",
|
79
|
-
"@vue/cli-plugin-babel": "^5.0.8",
|
80
|
-
"@vue/cli-plugin-eslint": "^5.0.8",
|
81
|
-
"@vue/cli-service": "^5.0.8",
|
82
|
-
"@vue/eslint-config-standard": "^8.0.1",
|
83
|
-
"@vue/test-utils": "^1.3.6",
|
84
|
-
"babel-plugin-transform-remove-console": "^6.9.4",
|
85
|
-
"compression-webpack-plugin": "^10.0.0",
|
86
|
-
"css-minimizer-webpack-plugin": "^5.0.1",
|
87
|
-
"deepmerge": "^4.3.1",
|
88
|
-
"eslint": "^8.51.0",
|
89
|
-
"eslint-plugin-vue": "^9.17.0",
|
90
|
-
"fast-deep-equal": "^3.1.3",
|
91
|
-
"ignore-loader": "^0.1.2",
|
92
|
-
"jest": "^29.7.0",
|
93
|
-
"jest-environment-jsdom": "^29.7.0",
|
94
|
-
"jest-transform-stub": "^2.0.0",
|
95
|
-
"less-loader": "^6.2.0",
|
96
|
-
"script-loader": "^0.7.2",
|
97
|
-
"style-resources-loader": "^1.5.0",
|
98
|
-
"vue-cli-plugin-style-resources-loader": "^0.1.5",
|
99
|
-
"vue-jest": "^4.0.1",
|
100
|
-
"vue-template-compiler": "^2.7.14",
|
101
|
-
"webpack": "^5.88.2",
|
102
|
-
"webpack-theme-color-replacer": "^1.4.7",
|
103
|
-
"whatwg-fetch": "^3.6.19"
|
104
|
-
},
|
105
|
-
"browserslist": [
|
106
|
-
"> 1%",
|
107
|
-
"last 2 versions",
|
108
|
-
"not dead",
|
109
|
-
"not ie 11"
|
110
|
-
]
|
111
|
-
}
|
1
|
+
{
|
2
|
+
"name": "vue2-client",
|
3
|
+
"version": "1.15.141",
|
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
|
+
"videojs-contrib-hls": "^5.15.0",
|
60
|
+
"viser-vue": "^2.4.8",
|
61
|
+
"vue": "^2.7.14",
|
62
|
+
"vue-codemirror": "4.0.6",
|
63
|
+
"vue-color": "2.7.0",
|
64
|
+
"vue-draggable-resizable": "^2.3.0",
|
65
|
+
"vue-i18n": "^8.28.2",
|
66
|
+
"vue-json-viewer": "^2.2.22",
|
67
|
+
"vue-router": "^3.6.5",
|
68
|
+
"vue-video-player": "^5.0.2",
|
69
|
+
"vue-virtual-scroller": "^1.1.2",
|
70
|
+
"vuedraggable": "^2.24.3",
|
71
|
+
"vuex": "^3.6.2",
|
72
|
+
"xlsx": "0.18.5"
|
73
|
+
},
|
74
|
+
"devDependencies": {
|
75
|
+
"@ant-design/colors": "^7.0.0",
|
76
|
+
"@babel/core": "^7.22.20",
|
77
|
+
"@babel/eslint-parser": "^7.22.15",
|
78
|
+
"@babel/preset-env": "^7.22.20",
|
79
|
+
"@vue/cli-plugin-babel": "^5.0.8",
|
80
|
+
"@vue/cli-plugin-eslint": "^5.0.8",
|
81
|
+
"@vue/cli-service": "^5.0.8",
|
82
|
+
"@vue/eslint-config-standard": "^8.0.1",
|
83
|
+
"@vue/test-utils": "^1.3.6",
|
84
|
+
"babel-plugin-transform-remove-console": "^6.9.4",
|
85
|
+
"compression-webpack-plugin": "^10.0.0",
|
86
|
+
"css-minimizer-webpack-plugin": "^5.0.1",
|
87
|
+
"deepmerge": "^4.3.1",
|
88
|
+
"eslint": "^8.51.0",
|
89
|
+
"eslint-plugin-vue": "^9.17.0",
|
90
|
+
"fast-deep-equal": "^3.1.3",
|
91
|
+
"ignore-loader": "^0.1.2",
|
92
|
+
"jest": "^29.7.0",
|
93
|
+
"jest-environment-jsdom": "^29.7.0",
|
94
|
+
"jest-transform-stub": "^2.0.0",
|
95
|
+
"less-loader": "^6.2.0",
|
96
|
+
"script-loader": "^0.7.2",
|
97
|
+
"style-resources-loader": "^1.5.0",
|
98
|
+
"vue-cli-plugin-style-resources-loader": "^0.1.5",
|
99
|
+
"vue-jest": "^4.0.1",
|
100
|
+
"vue-template-compiler": "^2.7.14",
|
101
|
+
"webpack": "^5.88.2",
|
102
|
+
"webpack-theme-color-replacer": "^1.4.7",
|
103
|
+
"whatwg-fetch": "^3.6.19"
|
104
|
+
},
|
105
|
+
"browserslist": [
|
106
|
+
"> 1%",
|
107
|
+
"last 2 versions",
|
108
|
+
"not dead",
|
109
|
+
"not ie 11"
|
110
|
+
]
|
111
|
+
}
|
@@ -0,0 +1,411 @@
|
|
1
|
+
<script>
|
2
|
+
import { v4 as uuidv4 } from 'uuid'
|
3
|
+
import {
|
4
|
+
FormModel,
|
5
|
+
Input,
|
6
|
+
Button,
|
7
|
+
Row,
|
8
|
+
Col,
|
9
|
+
Card,
|
10
|
+
Divider,
|
11
|
+
message,
|
12
|
+
Select,
|
13
|
+
Spin
|
14
|
+
} from 'ant-design-vue'
|
15
|
+
import { getConfigByNameAsync, runLogic } from '@vue2-client/services/api/common'
|
16
|
+
import { mapState } from 'vuex'
|
17
|
+
import { debounce } from 'lodash'
|
18
|
+
|
19
|
+
export default {
|
20
|
+
name: 'HAddNativeForm',
|
21
|
+
components: {
|
22
|
+
'a-form-model': FormModel,
|
23
|
+
'a-form-item': FormModel.Item,
|
24
|
+
'a-input': Input,
|
25
|
+
'a-button': Button,
|
26
|
+
'a-row': Row,
|
27
|
+
'a-col': Col,
|
28
|
+
'a-card': Card,
|
29
|
+
'a-divider': Divider,
|
30
|
+
'a-select': Select,
|
31
|
+
'a-select-option': Select.Option,
|
32
|
+
'a-spin': Spin
|
33
|
+
},
|
34
|
+
props: {
|
35
|
+
queryParamsName: {
|
36
|
+
type: String,
|
37
|
+
required: true
|
38
|
+
},
|
39
|
+
serviceName: {
|
40
|
+
type: String,
|
41
|
+
default: process.env.VUE_APP_SYSTEM_NAME
|
42
|
+
},
|
43
|
+
formStyle: {
|
44
|
+
type: String,
|
45
|
+
default: 'defaultForm'
|
46
|
+
}
|
47
|
+
},
|
48
|
+
data () {
|
49
|
+
return {
|
50
|
+
loading: true,
|
51
|
+
config: {},
|
52
|
+
formInstances: []
|
53
|
+
}
|
54
|
+
},
|
55
|
+
computed: {
|
56
|
+
formItemLayoutComputed () {
|
57
|
+
// 如果是 inline 布局,则不绑定 formItemLayout,让其使用 Ant Design Vue 默认的 inline 样式
|
58
|
+
if (this.config.xAddFormLayout === 'inline') {
|
59
|
+
return {}
|
60
|
+
}
|
61
|
+
if (this.config.formItemLayout) {
|
62
|
+
return {
|
63
|
+
labelCol: { span: this.config.formItemLayout.labelCol },
|
64
|
+
wrapperCol: { span: this.config.formItemLayout.wrapperCol }
|
65
|
+
}
|
66
|
+
}
|
67
|
+
return {}
|
68
|
+
},
|
69
|
+
...mapState('account', { currUser: 'user' })
|
70
|
+
},
|
71
|
+
created () {
|
72
|
+
this.debouncedSearch = debounce(this.performSearch, 500)
|
73
|
+
this.fetchConfigAndInitializeForms()
|
74
|
+
},
|
75
|
+
methods: {
|
76
|
+
async fetchConfigAndInitializeForms () {
|
77
|
+
try {
|
78
|
+
this.loading = true
|
79
|
+
const res = await getConfigByNameAsync(this.queryParamsName, this.serviceName)
|
80
|
+
if (res) {
|
81
|
+
this.config = res
|
82
|
+
if (this.config.manyForm) {
|
83
|
+
this.addFormInstance()
|
84
|
+
} else {
|
85
|
+
this.addFormInstance()
|
86
|
+
}
|
87
|
+
} else {
|
88
|
+
console.error('HAddNativeForm: 未能获取到配置内容。')
|
89
|
+
message.error('未能获取到表单配置,请检查配置名称或网络。')
|
90
|
+
}
|
91
|
+
} catch (error) {
|
92
|
+
console.error('HAddNativeForm: 获取配置时发生错误:', error)
|
93
|
+
message.error('获取表单配置时发生错误。')
|
94
|
+
} finally {
|
95
|
+
this.loading = false
|
96
|
+
}
|
97
|
+
},
|
98
|
+
async getOptions (item, formDataContext, searchKeyword = '') {
|
99
|
+
if (!item.keyName) {
|
100
|
+
return
|
101
|
+
}
|
102
|
+
|
103
|
+
this.$set(item, 'loadingOptions', true)
|
104
|
+
let fetchedOptions = []
|
105
|
+
|
106
|
+
try {
|
107
|
+
if (item.keyName.startsWith('logic@')) {
|
108
|
+
const logicName = item.keyName.substring(6)
|
109
|
+
const result = await runLogic(logicName, { searchKeyword: searchKeyword }, this.serviceName)
|
110
|
+
|
111
|
+
if (Array.isArray(result)) {
|
112
|
+
fetchedOptions = result.map(opt => ({
|
113
|
+
label: opt.label || opt.name || opt.text,
|
114
|
+
value: (opt.value || opt.id) + ''
|
115
|
+
}))
|
116
|
+
} else {
|
117
|
+
console.warn(`Logic '${logicName}' did not return an array for options. Result:`, result)
|
118
|
+
}
|
119
|
+
} else if (item.keyName.startsWith('config@')) {
|
120
|
+
const configName = item.keyName.substring(7)
|
121
|
+
const res = await getConfigByNameAsync(configName, this.serviceName)
|
122
|
+
if (res && res.value && Array.isArray(res.value)) {
|
123
|
+
fetchedOptions = res.value.map(opt => ({
|
124
|
+
label: opt.label || opt.name || opt.text,
|
125
|
+
value: (opt.value || opt.id) + ''
|
126
|
+
}))
|
127
|
+
} else {
|
128
|
+
console.warn(`Config '${configName}' did not return a valid 'value' array for options. Result:`, res)
|
129
|
+
}
|
130
|
+
} else {
|
131
|
+
// 全局字典
|
132
|
+
if (this.$appdata && typeof this.$appdata.getDictionaryList === 'function') {
|
133
|
+
const dictionaryList = this.$appdata.getDictionaryList(item.keyName)
|
134
|
+
if (Array.isArray(dictionaryList)) {
|
135
|
+
fetchedOptions = dictionaryList.map(opt => ({
|
136
|
+
label: opt.text,
|
137
|
+
value: opt.value + ''
|
138
|
+
}))
|
139
|
+
} else {
|
140
|
+
console.warn(`Global dictionary '${item.keyName}' did not return an array. Result:`, dictionaryList)
|
141
|
+
}
|
142
|
+
} else {
|
143
|
+
console.error('$appdata.getDictionaryList is not available. Please ensure it\'s globally provided.')
|
144
|
+
}
|
145
|
+
}
|
146
|
+
} catch (error) {
|
147
|
+
console.error(`Error fetching options for item '${item.key}':`, error)
|
148
|
+
fetchedOptions = []
|
149
|
+
} finally {
|
150
|
+
this.$set(item, 'options', fetchedOptions)
|
151
|
+
this.$set(item, 'loadingOptions', false)
|
152
|
+
}
|
153
|
+
},
|
154
|
+
// 搜索处理函数,使用 debounce 优化性能
|
155
|
+
handleSelectSearch (value, item, formDataContext) {
|
156
|
+
// 更新搜索关键字,并触发 debouncedSearch
|
157
|
+
this.$set(item, 'searchKeyword', value)
|
158
|
+
this.debouncedSearch(item, formDataContext)
|
159
|
+
},
|
160
|
+
// 执行实际的搜索操作
|
161
|
+
performSearch (item, formDataContext) {
|
162
|
+
this.getOptions(item, formDataContext, item.searchKeyword)
|
163
|
+
},
|
164
|
+
// 当选择框获得焦点时,重新加载选项(如果没有搜索关键字)
|
165
|
+
handleSelectFocus (item, formDataContext) {
|
166
|
+
if (!item.searchKeyword || item.searchKeyword === '') {
|
167
|
+
this.getOptions(item, formDataContext)
|
168
|
+
}
|
169
|
+
},
|
170
|
+
addFormInstance () {
|
171
|
+
const newId = uuidv4()
|
172
|
+
const newFormData = {}
|
173
|
+
const clonedFormItems = this.config.formItem ? JSON.parse(JSON.stringify(this.config.formItem)) : []
|
174
|
+
|
175
|
+
if (clonedFormItems.length > 0) {
|
176
|
+
for (const item of clonedFormItems) {
|
177
|
+
newFormData[item.key] = undefined
|
178
|
+
if (item.formType === 'select' && item.keyName) {
|
179
|
+
this.$set(item, 'options', [])
|
180
|
+
this.$set(item, 'loadingOptions', false)
|
181
|
+
this.$set(item, 'searchKeyword', '')
|
182
|
+
this.getOptions(item, newFormData)
|
183
|
+
}
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
this.formInstances.push({
|
188
|
+
id: newId,
|
189
|
+
formData: newFormData,
|
190
|
+
formItems: clonedFormItems
|
191
|
+
})
|
192
|
+
},
|
193
|
+
deleteFormInstance (id) {
|
194
|
+
if (this.formInstances.length > 1) {
|
195
|
+
this.formInstances = this.formInstances.filter(instance => instance.id !== id)
|
196
|
+
} else {
|
197
|
+
message.warning('至少需要保留一个表单实例。')
|
198
|
+
}
|
199
|
+
},
|
200
|
+
getRules (item) {
|
201
|
+
const rules = []
|
202
|
+
if (item.rule && (item.rule.required === true || item.rule.required === 'true')) {
|
203
|
+
rules.push({
|
204
|
+
required: true,
|
205
|
+
message: `请输入${item.title}`,
|
206
|
+
trigger: 'blur'
|
207
|
+
})
|
208
|
+
}
|
209
|
+
return rules
|
210
|
+
},
|
211
|
+
async onSubmit () {
|
212
|
+
let allFormsValid = true
|
213
|
+
const allFormsData = []
|
214
|
+
|
215
|
+
for (const instance of this.formInstances) {
|
216
|
+
const formRef = this.$refs['formModel_' + instance.id][0]
|
217
|
+
if (formRef) {
|
218
|
+
try {
|
219
|
+
await formRef.validate()
|
220
|
+
allFormsData.push(instance.formData)
|
221
|
+
} catch (error) {
|
222
|
+
allFormsValid = false
|
223
|
+
break
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
if (allFormsValid) {
|
229
|
+
message.success('表单提交成功!')
|
230
|
+
this.$emit('submit', allFormsData)
|
231
|
+
} else {
|
232
|
+
message.error('部分表单校验失败,请检查。')
|
233
|
+
}
|
234
|
+
},
|
235
|
+
getAllFormsData () {
|
236
|
+
return new Promise((resolve, reject) => {
|
237
|
+
let allFormsValid = true
|
238
|
+
const allFormsData = []
|
239
|
+
let validatedCount = 0
|
240
|
+
|
241
|
+
if (this.formInstances.length === 0) {
|
242
|
+
resolve([])
|
243
|
+
return
|
244
|
+
}
|
245
|
+
|
246
|
+
this.formInstances.forEach((instance, index) => {
|
247
|
+
const formRef = this.$refs['formModel_' + instance.id][0]
|
248
|
+
if (formRef) {
|
249
|
+
formRef.validate(valid => {
|
250
|
+
validatedCount++
|
251
|
+
if (valid) {
|
252
|
+
allFormsData.push(instance.formData)
|
253
|
+
} else {
|
254
|
+
allFormsValid = false
|
255
|
+
}
|
256
|
+
|
257
|
+
if (validatedCount === this.formInstances.length) {
|
258
|
+
if (allFormsValid) {
|
259
|
+
resolve(allFormsData)
|
260
|
+
} else {
|
261
|
+
reject(new Error('部分表单校验失败。'))
|
262
|
+
}
|
263
|
+
}
|
264
|
+
})
|
265
|
+
} else {
|
266
|
+
console.warn(`无法获取表单实例 ${instance.id} 的引用进行校验。`)
|
267
|
+
allFormsValid = false
|
268
|
+
validatedCount++
|
269
|
+
if (validatedCount === this.formInstances.length) {
|
270
|
+
if (allFormsValid) {
|
271
|
+
resolve(allFormsData)
|
272
|
+
} else {
|
273
|
+
reject(new Error('部分表单校验失败。'))
|
274
|
+
}
|
275
|
+
}
|
276
|
+
}
|
277
|
+
})
|
278
|
+
})
|
279
|
+
}
|
280
|
+
}
|
281
|
+
}
|
282
|
+
</script>
|
283
|
+
|
284
|
+
<template>
|
285
|
+
<div class="h-add-native-form-group" :class="[`h-add-native-form-${formStyle}`]">
|
286
|
+
<a-skeleton :loading="loading" :paragraph="{ rows: 4 }" />
|
287
|
+
<div v-if="!loading">
|
288
|
+
<div v-for="(formInstance, formIndex) in formInstances" :key="formInstance.id" class="form-instance-wrapper">
|
289
|
+
<div v-if="config.manyForm" class="form-instance-title">药品 {{ formIndex + 1 }}</div>
|
290
|
+
<a-form-model
|
291
|
+
:ref="'formModel_' + formInstance.id"
|
292
|
+
:model="formInstance.formData"
|
293
|
+
v-bind="formItemLayoutComputed"
|
294
|
+
:layout="config.xAddFormLayout || 'horizontal'"
|
295
|
+
>
|
296
|
+
<a-row :gutter="16">
|
297
|
+
<a-col :span="8" v-for="(item, itemIndex) in formInstance.formItems" :key="itemIndex">
|
298
|
+
<a-form-item
|
299
|
+
:label="item.title"
|
300
|
+
:prop="item.key"
|
301
|
+
:rules="getRules(item)"
|
302
|
+
>
|
303
|
+
<a-input
|
304
|
+
v-if="item.formType === 'input'"
|
305
|
+
v-model="formInstance.formData[item.key]"
|
306
|
+
:placeholder="'请输入' + item.title"
|
307
|
+
/>
|
308
|
+
<a-select
|
309
|
+
v-else-if="item.formType === 'select'"
|
310
|
+
v-model="formInstance.formData[item.key]"
|
311
|
+
:placeholder="item.loadingOptions ? '加载中...' : '请选择' + item.title"
|
312
|
+
:options="item.options"
|
313
|
+
:loading="item.loadingOptions"
|
314
|
+
show-search
|
315
|
+
allowClear
|
316
|
+
:filter-option="false"
|
317
|
+
@search="(value) => handleSelectSearch(value, item, formInstance.formData)"
|
318
|
+
@focus="() => handleSelectFocus(item, formInstance.formData)"
|
319
|
+
:dropdownMatchSelectWidth="false"
|
320
|
+
:dropdownStyle="{ minWidth: '200px' }"
|
321
|
+
>
|
322
|
+
<a-spin v-if="item.loadingOptions" slot="notFoundContent" size="small" />
|
323
|
+
</a-select>
|
324
|
+
<!-- 可以根据需要添加其他表单类型,例如 select, datePicker 等 -->
|
325
|
+
</a-form-item>
|
326
|
+
</a-col>
|
327
|
+
</a-row>
|
328
|
+
</a-form-model>
|
329
|
+
<a-divider v-if="config.manyForm && formIndex < formInstances.length - 1" />
|
330
|
+
</div>
|
331
|
+
|
332
|
+
<!-- 全局添加/删除按钮,只在所有表单实例之后显示 -->
|
333
|
+
<a-row v-if="config.manyForm" type="flex" justify="start" style="margin-top: 20px; gap: 10px;">
|
334
|
+
<a-button v-if="config.showAddButton" class="form-action-button" icon="plus" @click="addFormInstance"></a-button>
|
335
|
+
<a-button v-if="config.showDeleteButton && formInstances.length > 1" class="form-action-button" icon="minus" @click="deleteFormInstance(formInstances[formInstances.length - 1].id)"></a-button>
|
336
|
+
</a-row>
|
337
|
+
|
338
|
+
<a-row v-if="config.showSubmitBtn" type="flex" justify="start" style="margin-top: 20px;">
|
339
|
+
<a-col>
|
340
|
+
<a-button type="primary" @click="onSubmit">{{ config.btnName || '提交' }}</a-button>
|
341
|
+
</a-col>
|
342
|
+
</a-row>
|
343
|
+
</div>
|
344
|
+
</div>
|
345
|
+
</template>
|
346
|
+
|
347
|
+
<style scoped lang="less">
|
348
|
+
.h-add-native-form-group {
|
349
|
+
// 基础样式,对应 defaultForm
|
350
|
+
&.h-add-native-form-defaultForm {
|
351
|
+
padding: 0px;
|
352
|
+
background-color: #fff;
|
353
|
+
border-radius: 8px;
|
354
|
+
}
|
355
|
+
}
|
356
|
+
|
357
|
+
.form-instance-wrapper {
|
358
|
+
margin-bottom: 20px;
|
359
|
+
/* 移除边框和背景色,使其看起来像一个整体 */
|
360
|
+
/* border: 1px solid #e8e8e8;
|
361
|
+
border-radius: 4px;
|
362
|
+
background-color: #fff; */
|
363
|
+
}
|
364
|
+
|
365
|
+
.form-instance-title {
|
366
|
+
font-weight: bold;
|
367
|
+
font-size: 16px;
|
368
|
+
margin-bottom: 10px;
|
369
|
+
padding-left: 10px; /* 与表单项对齐 */
|
370
|
+
color: rgba(0, 0, 0, 0.85);
|
371
|
+
}
|
372
|
+
|
373
|
+
.form-action-button {
|
374
|
+
width: 40px;
|
375
|
+
height: 40px;
|
376
|
+
border-radius: 8px;
|
377
|
+
border: 1px solid #d9d9d9;
|
378
|
+
background-color: #fff;
|
379
|
+
color: rgba(0, 0, 0, 0.65);
|
380
|
+
display: flex;
|
381
|
+
justify-content: center;
|
382
|
+
align-items: center;
|
383
|
+
|
384
|
+
&:hover {
|
385
|
+
border-color: #40a9ff; /* 鼠标悬停时边框颜色 */
|
386
|
+
color: #40a9ff; /* 鼠标悬停时图标颜色 */
|
387
|
+
}
|
388
|
+
}
|
389
|
+
|
390
|
+
:deep(.ant-card-extra) {
|
391
|
+
padding: 0;
|
392
|
+
}
|
393
|
+
|
394
|
+
:deep(.ant-form-item) {
|
395
|
+
margin-bottom: 5px;
|
396
|
+
}
|
397
|
+
|
398
|
+
:deep(.ant-form-inline .ant-form-item) {
|
399
|
+
margin-right: 0;
|
400
|
+
margin-top: 0;
|
401
|
+
margin-bottom: 12px;
|
402
|
+
|
403
|
+
.ant-form-item-control-wrapper {
|
404
|
+
min-width: 0;
|
405
|
+
}
|
406
|
+
}
|
407
|
+
|
408
|
+
// :deep(.ant-form-inline .ant-form-item-label) {
|
409
|
+
// padding-right: 8px; /* 调整标签的右内边距,增加标签和控件的间距 */
|
410
|
+
// }
|
411
|
+
</style>
|