@dhccmobile/vue3-lo-form 2.0.3 → 2.0.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhccmobile/vue3-lo-form",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "VUE3动态表单",
5
5
  "private": false,
6
6
  "main": "dist/vue3-lo-form.umd.js",
@@ -42,18 +42,18 @@
42
42
  "@types/moment": "^2.13.0",
43
43
  "@typescript-eslint/eslint-plugin": "^5.4.0",
44
44
  "@typescript-eslint/parser": "^5.4.0",
45
- "@vue/cli-plugin-babel": "~5.0.0",
46
- "@vue/cli-plugin-eslint": "~5.0.0",
47
- "@vue/cli-plugin-router": "~5.0.0",
48
- "@vue/cli-plugin-typescript": "~5.0.0",
49
- "@vue/cli-plugin-vuex": "~5.0.0",
50
- "@vue/cli-service": "~5.0.0",
45
+ "@vue/cli-plugin-babel": "~4.5.10",
46
+ "@vue/cli-plugin-eslint": "~4.5.10",
47
+ "@vue/cli-plugin-router": "~4.5.10",
48
+ "@vue/cli-plugin-typescript": "~4.5.17",
49
+ "@vue/cli-plugin-vuex": "~4.5.10",
50
+ "@vue/cli-service": "~4.5.10",
51
51
  "@vue/compiler-sfc": "3.4.35",
52
52
  "@vue/eslint-config-prettier": "^6.0.0",
53
53
  "@vue/eslint-config-typescript": "^9.1.0",
54
54
  "eslint": "^7.1.0",
55
55
  "eslint-plugin-prettier": "^3.3.1",
56
- "eslint-plugin-vue": "^8.0.1",
56
+ "eslint-plugin-vue": "^7.0.0",
57
57
  "less": "^4.2.0",
58
58
  "less-loader": "^11.0.0",
59
59
  "prettier": "^2.2.1",
package/src/App.vue CHANGED
@@ -102,7 +102,7 @@
102
102
  </div>
103
103
  <div class="lo-input">
104
104
  <!--styleMode="credit-rd" :formId="'773549593606574080'"-->
105
- <dy-form v-if="showForm" :formId="'984034993956851712'" v-model:edit="formApi.edit" v-model:formApi="formApi" @change="onChange($event)" @click="onClick($event)" @focus="onFocus($event)" @blur="onBlur($event)" @addon-before="onAddonBeforeHandler($event)" @addon-after="onAddonAfterHandler($event)" @add-item="selectAddItem($event)">
105
+ <dy-form v-if="showForm" :formId="'984119606230257664'" v-model:edit="formApi.edit" v-model:formApi="formApi" @change="onChange($event)" @click="onClick($event)" @focus="onFocus($event)" @blur="onBlur($event)" @addon-before="onAddonBeforeHandler($event)" @addon-after="onAddonAfterHandler($event)" @add-item="selectAddItem($event)">
106
106
  <template #combinationSelector="{ control, onChange }">
107
107
  <div class="lo-custom-control-box">
108
108
  <a-input v-model:value="control.formControl.value" @input="onChange(control.formControl)" v-if="control && control.formControl" />
@@ -12,7 +12,7 @@
12
12
  import DvFormLayout from "./DvFormLayout.vue";
13
13
  import { Options, Prop, Provide, Watch, Vue } from "vue-property-decorator";
14
14
  import { DesForm } from "@/domain";
15
- import { FormFieldClass } from "@/constants";
15
+ import { FormFieldClass, DynamicOptionType, OptionType, ValidateRules } from "@/constants";
16
16
  import { formToolsService } from "@/services";
17
17
  import { Control } from "@/domain";
18
18
  import { DesFormControl } from "@/domain";
@@ -25,7 +25,7 @@ import { ProvideInjectData } from "@/domain/ProvideInjectData";
25
25
  import { validateGeneratorService } from "@/services";
26
26
  import { CustomFormat, FieldChangeHistory } from "../../domain";
27
27
  import { extractOptions } from "@/filtres/extract-options.filter";
28
- import { DynamicOptionType, OptionType } from "../../constants/enum";
28
+ // import { DynamicOptionType, OptionType } from "../../constants/enum";
29
29
  import { NzTreeNodeOptions } from "@/services";
30
30
 
31
31
  export type NfFormMode = "manual" | "auto";
@@ -62,6 +62,7 @@ export default class DyForm extends Vue {
62
62
  @Prop({ default: false }) widthCompatibleMode!: boolean; // 宽度计算模式
63
63
  @Prop({ type: String }) formTheme!: NfFormTheme; // 表单主题
64
64
  @Prop({ type: Array }) linkList: any | undefined; // 需要添加超链接点击事件数组 例:['cifNo', 'prodBaseNo']
65
+ @Prop({ type: Array }) formLimits!: any[]; // 表单权限
65
66
 
66
67
  private metaRefresh: any = new Date().getTime(); // 刷新标识
67
68
  private refreshCheckFeedbackFlag = new Date().getTime(); // 刷新校验标识
@@ -98,7 +99,7 @@ export default class DyForm extends Vue {
98
99
  * @date 2022/4/6 14:17
99
100
  */
100
101
  @Watch("formValidateStateResetMark")
101
- onPropFormValidateStateResetHandler(newVal: any): void {
102
+ onPropFormValidateStateResetHandler(): void {
102
103
  if ((this as any)._provided) {
103
104
  ((this as any)._provided as any).provideInjectData.formValidateStateResetMark = this.formValidateStateResetMark;
104
105
  }
@@ -115,7 +116,7 @@ export default class DyForm extends Vue {
115
116
  @Watch("metaRefresh")
116
117
  @Watch("refreshCheckFeedbackFlag")
117
118
  @Watch("widthCompatibleMode")
118
- onPropHandler(newVal: any): void {
119
+ onPropHandler(): void {
119
120
  if (this) {
120
121
  this.provideInjectData.edit = this.edit;
121
122
  this.provideInjectData.labelSpan = this.labelSpan;
@@ -128,10 +129,20 @@ export default class DyForm extends Vue {
128
129
 
129
130
  get loFormTheme(): string {
130
131
  const win: any = window;
131
- const themes = ["default", "gray"];
132
+ // 默认, 灰度, 某北某方,label换行
133
+ const themes = ["default", "gray", "nitic", "labelWrap"];
132
134
  const formTheme = themes.indexOf(this.formTheme) > -1 ? this.formTheme : win.$loFormTheme;
133
135
  return themes.indexOf(formTheme) > -1 ? formTheme : themes[0];
134
136
  }
137
+
138
+ get loFormLimit(): any {
139
+ const formLimitObj: any = {};
140
+ this.formLimits?.forEach((item: any) => {
141
+ formLimitObj[item.id] = item;
142
+ });
143
+ return formLimitObj;
144
+ }
145
+
135
146
  /**
136
147
  * @Description 表单数据反显回调(data:Object { key: 元素码值, value: 元素值 })
137
148
  * @Author JiangTao
@@ -452,9 +463,25 @@ export default class DyForm extends Vue {
452
463
  const propertys: string[] = boundProperty.split(".");
453
464
  if (propertys.length > 1 && formGroup != null) {
454
465
  const formControl: FormControl = this.findFormControlByProperty(propertys.slice(1, propertys.length), formGroup);
466
+ const controlAttr: DesFormControl = control.controlAttr as any;
467
+ // console.log("接受到的表单权限信息", this.loFormLimit);
468
+ const property = propertys[propertys.length - 1];
469
+ const formLimit = this.loFormLimit[property];
470
+ if (formLimit) {
471
+ controlAttr.isHideControl = formLimit.isHide === "1";
472
+ controlAttr.isNotEdit = formLimit.isNotEdit === "1";
473
+ const requiredIndex = controlAttr.verificationRules?.findIndex((item: any) => item.ruleType === ValidateRules.Required.code);
474
+ if (formLimit.isMustFill == "1" && requiredIndex == -1) {
475
+ // 添加必输校验
476
+ controlAttr.verificationRules.push({ ruleType: ValidateRules.Required.code });
477
+ } else if (formLimit.isMustFill != "1" && requiredIndex > -1) {
478
+ // 移除必输校验
479
+ controlAttr.verificationRules.splice(requiredIndex, 1);
480
+ }
481
+ }
455
482
  formControl.code = control.code;
456
- formControl.controlAttr = control.controlAttr;
457
- validateGeneratorService.fillCheckRule(formControl, control.controlAttr as DesFormControl);
483
+ formControl.controlAttr = controlAttr;
484
+ validateGeneratorService.fillCheckRule(formControl, controlAttr);
458
485
  control["formControl"] = formControl;
459
486
  }
460
487
  }
@@ -16,7 +16,7 @@
16
16
  :class="{
17
17
  ['form-model-item-' + controlAttr.boundProperty]: true,
18
18
  ['form-model-item-' + control.code]: true,
19
- 'lo-form-model-item-textarea-box': control.code === formFieldType.Textarea.code,
19
+ 'lo-form-model-item-textarea-box lo-theme-textarea-box': control.code === formFieldType.Textarea.code,
20
20
  'lo-form-item-mobile': isMobileBrowser(),
21
21
  }"
22
22
  >
@@ -302,7 +302,9 @@
302
302
  </a-upload>
303
303
  </template>
304
304
  <template v-if="control.code === formFieldType.Custom.code && controlAttr.customFieldName">
305
- <slot :control="control" :onChange="onChange" :name="controlAttr.customFieldName"></slot>
305
+ <div class="custom-border">
306
+ <slot :control="control" :onChange="onChange" :name="controlAttr.customFieldName"></slot>
307
+ </div>
306
308
  </template>
307
309
  </template>
308
310
  <template v-else>
@@ -782,7 +784,7 @@ export default class DvFormLayout extends Vue {
782
784
  } else if (this.provideInjectData.controlSpan) {
783
785
  return this.provideInjectData.controlSpan;
784
786
  } else {
785
- return 0;
787
+ return "none";
786
788
  }
787
789
  }
788
790
 
@@ -957,6 +959,7 @@ export default class DvFormLayout extends Vue {
957
959
  for (let i = 0; i < index; i++) {
958
960
  data += "0";
959
961
  }
962
+ data = this.countDecimalPlaces(data, controlAttr.decimalPlaces);
960
963
  }
961
964
  // 金额后缀不依赖于是否设置了小数位
962
965
  if (controlAttr.zoomType !== ZoomType.automatic.code && data != null && data != "" && controlAttr.unit) {
@@ -1033,6 +1036,39 @@ export default class DvFormLayout extends Vue {
1033
1036
  return data;
1034
1037
  }
1035
1038
 
1039
+ /**
1040
+ * 数字小数位数计算
1041
+ * @author JiangTao
1042
+ * @since 2026-04-16 16:40:23
1043
+ */
1044
+ countDecimalPlaces(data: string | number, decimalPlaces: number | undefined): string {
1045
+ // 1. 安全处理
1046
+ const digits = decimalPlaces ?? 0;
1047
+ const str = String(data).trim();
1048
+ if (!str) return digits > 0 ? `0.${"0".repeat(digits)}` : "0";
1049
+
1050
+ // 2. 判断是否有小数
1051
+ const hasDot = str.includes(".");
1052
+ const intPart = hasDot ? str.split(".")[0] : str;
1053
+ const decimalPart = hasDot ? str.split(".")[1] : "";
1054
+
1055
+ // 3. 拼接成纯数字用于四舍五入计算
1056
+ const pureNumStr = intPart.replace(/,/g, "") + (hasDot ? "." + decimalPart : "");
1057
+ const num = parseFloat(pureNumStr);
1058
+ if (isNaN(num)) return intPart + (digits > 0 ? `.${"0".repeat(digits)}` : "");
1059
+
1060
+ // 4. 四舍五入保留 digits 位
1061
+ const factor = Math.pow(10, digits);
1062
+ const rounded = (Math.round(num * factor) / factor).toFixed(digits);
1063
+
1064
+ // 5. 拆分四舍五入后的整数与小数
1065
+ const [, roundedDecimal = ""] = rounded.split(".");
1066
+
1067
+ // 6. 最终结果:原始整数部分 + 处理后的小数部分
1068
+ if (digits <= 0) return intPart;
1069
+ return `${intPart}.${roundedDecimal}`;
1070
+ }
1071
+
1036
1072
  /**
1037
1073
  * @description: 支持的上传类型
1038
1074
  * @author ChenRui
@@ -28,5 +28,7 @@ const SubmittedType = {
28
28
  BS_26: { code: "26", name: "集团司库资金系统投融资数据报送" },
29
29
  BS_27: { code: "27", name: "金控客户协同" },
30
30
  BS_28: { code: "28", name: "金控大额交易" },
31
+ BS_29: { code: "29", name: "五篇大文章" },
32
+ BS_30: { code: "30", name: "金控重大风险预警及关注与不良项目情况表" },
31
33
  };
32
34
  export { SubmittedType };
package/src/main.ts CHANGED
@@ -15,3 +15,5 @@ app
15
15
  loadMode: "dynamic",
16
16
  })
17
17
  .mount("#app");
18
+ const win: any = window;
19
+ win.$loFormTheme = "labelWrap"; // 指定主题
@@ -0,0 +1,371 @@
1
+ .ant-form-horizontal {
2
+ .lo-control-container {
3
+ /**非编辑状态下的样式*/
4
+ .is-disable-input {
5
+ :deep(.ant-form-item-control) {
6
+ border-radius: 5px;
7
+ border: rgba(229,229,229, 0.7) 1px solid;
8
+ }
9
+ }
10
+ .lo-control-box {
11
+ .ant-form-item {
12
+ display: flex;
13
+ flex-direction: row;
14
+ height: 100%;
15
+ :deep {
16
+ .ant-form-item-control {
17
+ min-height: 40px;
18
+ //box-shadow: 0 0 0 1px rgba(229,229,229, 0.5);
19
+ margin-top: 1px;
20
+ }
21
+ .ant-form-item-label {
22
+ position: relative;
23
+ //box-shadow: 0 0 0 1px #fff;
24
+ background-color: transparent;
25
+ z-index: 1;
26
+ label {
27
+ display: block;
28
+ width: 100%;
29
+ height: auto;
30
+ white-space: pre-wrap;
31
+ word-break: break-all;
32
+ line-height: normal;
33
+ padding: 8px 0;
34
+ }
35
+ }
36
+ .ant-input,
37
+ .ant-input-affix-wrapper,
38
+ .ant-select-selector,
39
+ .ant-picker,
40
+ .ant-btn,
41
+ .ant-input-number {
42
+ border-radius: .5rem;
43
+ min-height: 40px;
44
+ max-height: 40px;
45
+ padding: 0 6px;
46
+ height: 100%;
47
+ color: #333333;
48
+ }
49
+ .ant-input-number-input, input[type=search] {
50
+ min-height: 40px;
51
+ padding: 0;
52
+ height: 100%;
53
+ color: #333333;
54
+ }
55
+ .ant-input-number-group,
56
+ .ant-input-number-group-wrapper,
57
+ .ant-input-number-input-wrap {
58
+ width: 100%;
59
+ height: 100%;
60
+ }
61
+ .ant-picker-input > input {
62
+ color: #333333;
63
+ }
64
+ .ant-radio-group {
65
+ padding: 0 6px;
66
+ margin: auto 0;
67
+ }
68
+ .ant-checkbox-group {
69
+ padding: 8px 6px;
70
+ }
71
+ .ant-picker-suffix, .ant-select-arrow {
72
+ width: 40px;
73
+ }
74
+ .ant-picker-suffix {
75
+ text-align: center;
76
+ margin-right: -6px;
77
+ }
78
+ .ant-select-arrow {
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ height: 100%;
83
+ top: 5px;
84
+ right: 0;
85
+ z-index: 10;
86
+ }
87
+ .ant-form-item-control-input-content {
88
+ display: flex;
89
+ align-items: center;
90
+ }
91
+ .ant-form-item-control-input, .ant-form-item-control-input-content {
92
+ height: 100%;
93
+ align-items: center;
94
+ }
95
+ .ant-input:focus,
96
+ .ant-input-number-focused,
97
+ .ant-picker-focused,
98
+ .ant-select-focused:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input) .ant-select-selector {
99
+ box-shadow: 0 0 0 1px #4094f3;
100
+ z-index: 10;
101
+ }
102
+ .ant-select-disabled.ant-select:not(.ant-select-customize-input) .ant-select-selector,
103
+ .ant-picker-input > input[disabled],
104
+ .ant-input-number-disabled,
105
+ .ant-input[disabled] {
106
+ color: #666666;
107
+ background-color: #fbfbfb;
108
+ }
109
+ .ant-picker-disabled {
110
+ background-color: #fbfbfb;
111
+ }
112
+ .ant-form-item-explain-error {
113
+ line-height: 24px;
114
+ color: #ff2d4b;
115
+ font-size: 12px;
116
+ padding: 0 6px;
117
+ background: rgba(255, 45, 75, 0.2)
118
+ }
119
+ .ant-select-selection-item {
120
+ display: inline-flex;
121
+ align-items: center;
122
+ }
123
+ .ant-select-selection-overflow {
124
+ padding-right: 40px;
125
+ }
126
+ .ant-select-multiple {
127
+ .ant-select-selection-search {
128
+ margin-start: 0;
129
+ margin-inline-start: 0;
130
+ }
131
+ .ant-select-selection-placeholder {
132
+ left: 6px;
133
+ right: 40px;
134
+ }
135
+ }
136
+ .ant-select-single {
137
+ .ant-select-selection-item,
138
+ .ant-select-selection-search {
139
+ position: absolute;
140
+ left: 6px;
141
+ right: 40px;
142
+ top: 0;
143
+ bottom: 0;
144
+ }
145
+ }
146
+ }
147
+ }
148
+ :deep .ant-form-item-has-error {
149
+ .ant-input:focus,
150
+ .ant-input-number-focused,
151
+ .ant-picker-focused,
152
+ .ant-select-focused:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input) .ant-select-selector {
153
+ box-shadow: 0 0 0 1px #ff194c;
154
+ color: #ff194c;
155
+ z-index: 10;
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ .lo-control-container {
162
+ box-sizing: border-box;
163
+ margin-bottom: 1.5rem;
164
+ .lo-control-box {
165
+ height: 100%;
166
+ .ant-form-item {
167
+ margin-bottom: 0;
168
+ .lo-select {
169
+ :deep {
170
+ .ant-select-selection-selected-value {
171
+ width: 100%;
172
+ white-space: nowrap;
173
+ text-overflow: ellipsis;
174
+ overflow: hidden;
175
+ word-break: break-all;
176
+ }
177
+ }
178
+ &.lo-select-multiple-suffix-icon {
179
+ :deep {
180
+ .ant-select-arrow {
181
+ margin: 0;
182
+ height: 100%;
183
+ top: 0;
184
+ right: 0;
185
+ width: fit-content;
186
+ .ant-select-arrow-icon {
187
+ height: 100%;
188
+ display: flex;
189
+ align-items: center;
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ .lo-label-sub-type-icon {
196
+ color: #3fb5eb;
197
+ cursor: pointer;
198
+ margin-left: 4px;
199
+ margin-bottom: 3px;
200
+ display: inline-block;
201
+ :deep {
202
+ svg {
203
+ display: inline-block;
204
+ margin-bottom: -2.5px;
205
+ }
206
+ }
207
+ }
208
+ .lo-label-history-icon {
209
+ display: inline-block;
210
+ line-height: 0;
211
+ margin-left: 4px;
212
+ margin-bottom: 3px;
213
+ cursor: pointer;
214
+ font-size: 14px;
215
+ }
216
+ .lo-label-tips-icon {
217
+ color: #4094f3;
218
+ cursor: pointer;
219
+ font-size: 14px !important;
220
+ margin-left: 4px;
221
+ }
222
+ :deep {
223
+ .ant-form-item-label {
224
+ label {
225
+ color: #333333;
226
+ }
227
+ }
228
+ .ant-form-item-control-wrapper {
229
+ flex-grow: 1;
230
+ color: #333333;
231
+ }
232
+ }
233
+ :deep {
234
+ .ant-input-group-addon, .ant-input-number-group-addon {
235
+ width: 40px;
236
+ min-height: 38px;
237
+ position: absolute;
238
+ right: 2px;
239
+ top: 1px;
240
+ border: none;
241
+ z-index: 999;
242
+ &:active {
243
+ opacity: .7;
244
+ }
245
+ }
246
+ }
247
+ .addon-icon {
248
+ display: flex;
249
+ align-items: center;
250
+ justify-content: center;
251
+ width: 100%;
252
+ height: 100%;
253
+ font-size: 15px;
254
+ cursor: pointer;
255
+ background-color: #fff;
256
+ }
257
+ .addon-inner {
258
+ display: flex;
259
+ align-items: center;
260
+ justify-content: center;
261
+ position: absolute;
262
+ width: 100%;
263
+ height: 100%;
264
+ top: 0;
265
+ left: 0;
266
+ cursor: pointer;
267
+ color: #999999;
268
+ background-color: #f2f2f2;
269
+ }
270
+ .addon-inner:active {
271
+ background: rgba(240, 240, 240, 0.3);
272
+ }
273
+ }
274
+ .lo-textarea-count {
275
+ position: absolute;
276
+ line-height: 10px;
277
+ right: 0;
278
+ color: rgba(0, 0, 0, 0.45);
279
+ }
280
+ .lo-input-number-box {
281
+ width: 100%;
282
+ :deep {
283
+ .ant-input-number {
284
+ width: 100%;
285
+ }
286
+ }
287
+ &.lo-with-unit {
288
+ :deep {
289
+ .ant-input-number {
290
+ border-top-right-radius: 0;
291
+ border-bottom-right-radius: 0;
292
+ border-right: 0;
293
+ .ant-input-number-handler-wrap {
294
+ border-top-right-radius: 0;
295
+ border-bottom-right-radius: 0;
296
+ opacity: 1;
297
+ }
298
+ }
299
+ .ant-dropdown-trigger {
300
+ border-top-left-radius: 0;
301
+ border-bottom-left-radius: 0;
302
+ }
303
+ }
304
+ &.lo-unit-1 {
305
+ :deep {
306
+ .ant-input-number {
307
+ width: calc(100% - 50px);
308
+ }
309
+ .ant-dropdown-trigger {
310
+ min-width: 50px;
311
+ }
312
+ }
313
+ }
314
+ &.lo-unit-2 {
315
+ :deep {
316
+ .ant-input-number {
317
+ width: calc(100% - 80px);
318
+ }
319
+ .ant-dropdown-trigger {
320
+ min-width: 80px;
321
+ }
322
+ }
323
+ }
324
+ &.lo-unit-3 {
325
+ :deep {
326
+ .ant-input-number {
327
+ width: calc(100% - 110px);
328
+ }
329
+ .ant-dropdown-trigger {
330
+ min-width: 110px;
331
+ }
332
+ }
333
+ }
334
+ }
335
+ }
336
+ .lo-input-content {
337
+ align-items: center;
338
+ overflow: hidden;
339
+ text-overflow: ellipsis;
340
+ color: #333333;
341
+ font-size: 14px;
342
+ padding: 0 6px;
343
+ }
344
+ .lo-control-text {
345
+ white-space: pre-wrap;
346
+ word-break: break-all;
347
+ line-height: 22px;
348
+ }
349
+ }
350
+
351
+ :deep .ant-select-multiple .ant-select-selection-item {
352
+ height: 40px !important;
353
+ margin: 0 4px 0 0 !important;
354
+ }
355
+ }
356
+ .lo-empty-layout-container {
357
+ box-sizing: border-box;
358
+ display: grid;
359
+ border: none;
360
+ }
361
+ .lo-general-layout-container {
362
+ box-sizing: border-box;
363
+ display: grid;
364
+ grid-column-start: 1;
365
+ }
366
+ :deep .ant-form-item-label > label.ant-form-item-no-colon::after {
367
+ display: none;
368
+ }
369
+ .ant-input-textarea {
370
+ width: 100%;
371
+ }