cloud-web-corejs 1.0.54-dev.751 → 1.0.54-dev.754
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 +3 -1
- package/src/App.vue +1 -1
- package/src/components/baseInputNumber/index.vue +9 -9
- package/src/components/errorMsg/index.js +47 -3
- package/src/components/errorMsg/mixins.js +101 -4
- package/src/components/jsonImport/mixins.js +9 -7
- package/src/components/langImport/mixins.js +9 -7
- package/src/components/mobile/file/index.vue +10 -0
- package/src/components/wf/wf.js +20 -2
- package/src/components/xform/form-designer/designer.js +1 -0
- package/src/components/xform/form-designer/form-widget/dialog/importDialogMixin.js +9 -7
- package/src/components/xform/form-designer/indexMixin.js +8 -1
- package/src/components/xform/form-designer/setting-panel/form-setting.vue +33 -2
- package/src/components/xform/form-designer/setting-panel/indexMixin.js +11 -1
- package/src/components/xform/form-designer/toolbar-panel/indexMixin.js +8 -1
- package/src/components/xform/form-designer/widget-panel/indexMixin.js +8 -1
- package/src/components/xform/form-render/container-item/detail-h5-item.vue +1 -0
- package/src/components/xform/form-render/container-item/detail-item.vue +1 -0
- package/src/components/xform/form-render/indexMixin.js +23 -14
- package/src/components/xform/utils/util.js +11 -5
- package/src/directive/LimitNumber/index.js +130 -124
- package/src/index.js +4 -0
- package/src/layout/components/AppMain.vue +9 -7
- package/src/layout/defaultLayout.vue +8 -2
- package/src/microRouter/index.js +37 -20
- package/src/router/modules/customer.js +9 -0
- package/src/utils/aes.js +21 -2
- package/src/utils/global.js +370 -8
- package/src/utils/keepAlive.js +2 -2
- package/src/utils/validate.js +104 -1
- package/src/views/support/form-dialog-demo/demoPickerDialog.vue +72 -0
- package/src/views/support/form-dialog-demo/index.vue +537 -0
- package/src/views/user/home/default2.vue +9 -2
- package/src/views/user/login/indexMixin.js +45 -11
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-dialog
|
|
3
|
+
title="选择供应商(自定义 Vue 弹框)"
|
|
4
|
+
:visible.sync="showDialog"
|
|
5
|
+
width="560px"
|
|
6
|
+
:close-on-click-modal="false"
|
|
7
|
+
:append-to-body="true"
|
|
8
|
+
custom-class="dialog-style"
|
|
9
|
+
v-el-drag-dialog
|
|
10
|
+
@close="handleClose"
|
|
11
|
+
>
|
|
12
|
+
<el-table
|
|
13
|
+
:data="rows"
|
|
14
|
+
border
|
|
15
|
+
highlight-current-row
|
|
16
|
+
@current-change="handleCurrentChange"
|
|
17
|
+
@row-dblclick="handleConfirm"
|
|
18
|
+
>
|
|
19
|
+
<el-table-column prop="id" label="编号" width="100" />
|
|
20
|
+
<el-table-column prop="name" label="名称" />
|
|
21
|
+
<el-table-column prop="contact" label="联系人" width="120" />
|
|
22
|
+
</el-table>
|
|
23
|
+
<span slot="footer" class="dialog-footer">
|
|
24
|
+
<el-button type="primary" plain class="button-sty" @click="showDialog = false"
|
|
25
|
+
><i class="el-icon-close el-icon"></i
|
|
26
|
+
>{{ $t2("取 消", "system.button.cancel2") }}</el-button
|
|
27
|
+
>
|
|
28
|
+
<el-button
|
|
29
|
+
type="primary"
|
|
30
|
+
class="button-sty"
|
|
31
|
+
:disabled="!currentRow"
|
|
32
|
+
@click="handleConfirm"
|
|
33
|
+
><i class="el-icon-check el-icon"></i
|
|
34
|
+
>{{ $t2("确 定", "system.button.confirm2") }}</el-button
|
|
35
|
+
>
|
|
36
|
+
</span>
|
|
37
|
+
</el-dialog>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script>
|
|
41
|
+
export default {
|
|
42
|
+
name: "demoPickerDialog",
|
|
43
|
+
props: ["visiable"],
|
|
44
|
+
data() {
|
|
45
|
+
return {
|
|
46
|
+
showDialog: true,
|
|
47
|
+
currentRow: null,
|
|
48
|
+
rows: [
|
|
49
|
+
{ id: "S001", name: "供应商甲", contact: "张三" },
|
|
50
|
+
{ id: "S002", name: "供应商乙", contact: "李四" },
|
|
51
|
+
{ id: "S003", name: "供应商丙", contact: "王五" },
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
methods: {
|
|
56
|
+
handleCurrentChange(row) {
|
|
57
|
+
this.currentRow = row;
|
|
58
|
+
},
|
|
59
|
+
handleConfirm() {
|
|
60
|
+
if (!this.currentRow) return;
|
|
61
|
+
// 与 formDialog searchComponent 的约定:confirm 事件带选中行数组
|
|
62
|
+
this.$emit("confirm", [this.currentRow]);
|
|
63
|
+
this.showDialog = false;
|
|
64
|
+
},
|
|
65
|
+
handleClose() {
|
|
66
|
+
this.$emit("update:visiable", false);
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<style></style>
|
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="app-container form-dialog-demo">
|
|
3
|
+
<el-card shadow="never">
|
|
4
|
+
<div slot="header">$formDialog 表单弹框示例</div>
|
|
5
|
+
<div class="btns">
|
|
6
|
+
<el-button type="primary" @click="openBasic">1. 基础新增表单</el-button>
|
|
7
|
+
<el-button type="primary" @click="openEdit">2. 编辑回显</el-button>
|
|
8
|
+
<el-button type="primary" @click="openReadonly">3. 只读查看</el-button>
|
|
9
|
+
<el-button type="primary" @click="openAllTypes"
|
|
10
|
+
>4. 全字段类型(两列布局)</el-button
|
|
11
|
+
>
|
|
12
|
+
<el-button type="primary" @click="openBeforeConfirm"
|
|
13
|
+
>5. beforeConfirm 异步保存</el-button
|
|
14
|
+
>
|
|
15
|
+
<el-button type="primary" @click="openCustomRender"
|
|
16
|
+
>6. 自定义 render 控件</el-button
|
|
17
|
+
>
|
|
18
|
+
<el-button type="primary" @click="openNested"
|
|
19
|
+
>7. 叠开两层弹框</el-button
|
|
20
|
+
>
|
|
21
|
+
<el-button type="primary" @click="openDataSource"
|
|
22
|
+
>8. 搜索框 + 下拉数据源(静态/词汇/脚本)</el-button
|
|
23
|
+
>
|
|
24
|
+
<el-button type="primary" @click="openLinkage"
|
|
25
|
+
>9. 字段联动(级联/显隐/禁用/onChange)</el-button
|
|
26
|
+
>
|
|
27
|
+
<el-button type="primary" @click="openXform"
|
|
28
|
+
>10. xform 整表模式(formCode)</el-button
|
|
29
|
+
>
|
|
30
|
+
<el-button type="primary" @click="openXformReadonly"
|
|
31
|
+
>11. xform 整表只读查看</el-button
|
|
32
|
+
>
|
|
33
|
+
</div>
|
|
34
|
+
</el-card>
|
|
35
|
+
|
|
36
|
+
<el-card shadow="never" style="margin-top: 16px">
|
|
37
|
+
<div slot="header">最近一次返回结果(取消/关闭为 null)</div>
|
|
38
|
+
<pre class="result">{{ result }}</pre>
|
|
39
|
+
</el-card>
|
|
40
|
+
</div>
|
|
41
|
+
</template>
|
|
42
|
+
|
|
43
|
+
<script>
|
|
44
|
+
export default {
|
|
45
|
+
name: "formDialogDemo",
|
|
46
|
+
data() {
|
|
47
|
+
return {
|
|
48
|
+
result: "(还未调用)",
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
methods: {
|
|
52
|
+
show(data) {
|
|
53
|
+
this.result = data === null ? "null" : JSON.stringify(data, null, 2);
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
// 1. 基础新增:必填校验 + 常用控件
|
|
57
|
+
async openBasic() {
|
|
58
|
+
const data = await this.$formDialog({
|
|
59
|
+
title: "新增分类",
|
|
60
|
+
fields: [
|
|
61
|
+
{ prop: "name", label: "名称", required: true },
|
|
62
|
+
{
|
|
63
|
+
prop: "type",
|
|
64
|
+
label: "类型",
|
|
65
|
+
type: "select",
|
|
66
|
+
required: true,
|
|
67
|
+
options: [
|
|
68
|
+
{ label: "普通", value: 1 },
|
|
69
|
+
{ label: "重要", value: 2 },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{ prop: "enabled", label: "启用", type: "switch" },
|
|
73
|
+
{ prop: "remark", label: "备注", type: "textarea" },
|
|
74
|
+
],
|
|
75
|
+
});
|
|
76
|
+
this.show(data);
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// 2. 编辑回显:model 传初始值
|
|
80
|
+
async openEdit() {
|
|
81
|
+
const data = await this.$formDialog({
|
|
82
|
+
title: "编辑分类",
|
|
83
|
+
model: {
|
|
84
|
+
name: "季度促销",
|
|
85
|
+
type: 2,
|
|
86
|
+
enabled: true,
|
|
87
|
+
remark: "从列表行数据带入的初始值",
|
|
88
|
+
},
|
|
89
|
+
fields: [
|
|
90
|
+
{ prop: "name", label: "名称", required: true },
|
|
91
|
+
{
|
|
92
|
+
prop: "type",
|
|
93
|
+
label: "类型",
|
|
94
|
+
type: "select",
|
|
95
|
+
options: [
|
|
96
|
+
{ label: "普通", value: 1 },
|
|
97
|
+
{ label: "重要", value: 2 },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
{ prop: "enabled", label: "启用", type: "switch" },
|
|
101
|
+
{ prop: "remark", label: "备注", type: "textarea" },
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
this.show(data);
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// 3. 只读查看:readonly=true,整表单禁用,底部只有关闭
|
|
108
|
+
async openReadonly() {
|
|
109
|
+
const data = await this.$formDialog({
|
|
110
|
+
title: "查看分类",
|
|
111
|
+
readonly: true,
|
|
112
|
+
model: { name: "季度促销", type: 2, enabled: true, remark: "只读模式" },
|
|
113
|
+
fields: [
|
|
114
|
+
{ prop: "name", label: "名称" },
|
|
115
|
+
{
|
|
116
|
+
prop: "type",
|
|
117
|
+
label: "类型",
|
|
118
|
+
type: "select",
|
|
119
|
+
options: [
|
|
120
|
+
{ label: "普通", value: 1 },
|
|
121
|
+
{ label: "重要", value: 2 },
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{ prop: "enabled", label: "启用", type: "switch" },
|
|
125
|
+
{ prop: "remark", label: "备注", type: "textarea" },
|
|
126
|
+
],
|
|
127
|
+
});
|
|
128
|
+
this.show(data);
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
// 4. 全字段类型 + span 两列布局 + 自定义 rules + 控件透传 props
|
|
132
|
+
async openAllTypes() {
|
|
133
|
+
const data = await this.$formDialog({
|
|
134
|
+
title: "全字段类型",
|
|
135
|
+
width: "760px",
|
|
136
|
+
rules: {
|
|
137
|
+
qty: [
|
|
138
|
+
{
|
|
139
|
+
validator: (rule, value, callback) => {
|
|
140
|
+
if (value != null && value % 2 !== 0) {
|
|
141
|
+
callback(new Error("数量必须是偶数(自定义 rules 示例)"));
|
|
142
|
+
} else {
|
|
143
|
+
callback();
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
trigger: "change",
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
fields: [
|
|
151
|
+
{ prop: "name", label: "文本", span: 12, required: true },
|
|
152
|
+
{
|
|
153
|
+
// 数字框走 baseInputNumber:scale 限小数位(0=整数),negative 允许负数
|
|
154
|
+
prop: "qty",
|
|
155
|
+
label: "数量",
|
|
156
|
+
type: "number",
|
|
157
|
+
span: 12,
|
|
158
|
+
scale: 0,
|
|
159
|
+
props: { min: 0, max: 100 },
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
prop: "amount",
|
|
163
|
+
label: "金额",
|
|
164
|
+
type: "number",
|
|
165
|
+
span: 12,
|
|
166
|
+
scale: 2,
|
|
167
|
+
negative: true,
|
|
168
|
+
},
|
|
169
|
+
{ prop: "date", label: "日期", type: "date", span: 12 },
|
|
170
|
+
{ prop: "time", label: "日期时间", type: "datetime", span: 12 },
|
|
171
|
+
{ prop: "range", label: "日期区间", type: "daterange", span: 24 },
|
|
172
|
+
{
|
|
173
|
+
prop: "level",
|
|
174
|
+
label: "单选",
|
|
175
|
+
type: "radio",
|
|
176
|
+
span: 12,
|
|
177
|
+
options: [
|
|
178
|
+
{ label: "低", value: "L" },
|
|
179
|
+
{ label: "中", value: "M" },
|
|
180
|
+
{ label: "高", value: "H" },
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
prop: "tags",
|
|
185
|
+
label: "多选",
|
|
186
|
+
type: "checkbox",
|
|
187
|
+
span: 12,
|
|
188
|
+
options: ["标签A", "标签B", "标签C"],
|
|
189
|
+
},
|
|
190
|
+
{ prop: "enabled", label: "开关", type: "switch", span: 12 },
|
|
191
|
+
{
|
|
192
|
+
prop: "locked",
|
|
193
|
+
label: "禁用控件",
|
|
194
|
+
type: "switch",
|
|
195
|
+
span: 12,
|
|
196
|
+
disabled: true,
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
});
|
|
200
|
+
this.show(data);
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
// 5. beforeConfirm:确认前调接口,失败不关闭弹框
|
|
204
|
+
async openBeforeConfirm() {
|
|
205
|
+
const data = await this.$formDialog({
|
|
206
|
+
title: "异步保存(勾选“模拟失败”体验保存失败不关闭)",
|
|
207
|
+
fields: [
|
|
208
|
+
{ prop: "name", label: "名称", required: true },
|
|
209
|
+
{ prop: "mockFail", label: "模拟失败", type: "switch" },
|
|
210
|
+
],
|
|
211
|
+
beforeConfirm: (formData) =>
|
|
212
|
+
new Promise((resolve, reject) => {
|
|
213
|
+
// 实际业务里这里通常是 this.$http({...}) 保存请求
|
|
214
|
+
setTimeout(() => {
|
|
215
|
+
if (formData.mockFail) {
|
|
216
|
+
this.$baseMessage({
|
|
217
|
+
message: "保存失败,弹框保持打开",
|
|
218
|
+
type: "error",
|
|
219
|
+
});
|
|
220
|
+
reject(new Error("mock fail"));
|
|
221
|
+
} else {
|
|
222
|
+
resolve();
|
|
223
|
+
}
|
|
224
|
+
}, 800);
|
|
225
|
+
}),
|
|
226
|
+
});
|
|
227
|
+
this.show(data);
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
// 6. render 逃生口:内置类型覆盖不了的控件自己渲染
|
|
231
|
+
async openCustomRender() {
|
|
232
|
+
const data = await this.$formDialog({
|
|
233
|
+
title: "自定义 render 控件",
|
|
234
|
+
model: { score: 3, percent: 40 },
|
|
235
|
+
fields: [
|
|
236
|
+
{ prop: "name", label: "名称", required: true },
|
|
237
|
+
{
|
|
238
|
+
prop: "score",
|
|
239
|
+
label: "评分",
|
|
240
|
+
render: (h, model) =>
|
|
241
|
+
h("el-rate", {
|
|
242
|
+
props: { value: model.score },
|
|
243
|
+
style: "margin-top:8px",
|
|
244
|
+
on: {
|
|
245
|
+
input: (val) => {
|
|
246
|
+
model.score = val;
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
}),
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
prop: "percent",
|
|
253
|
+
label: "进度",
|
|
254
|
+
render: (h, model) =>
|
|
255
|
+
h("el-slider", {
|
|
256
|
+
props: { value: model.percent },
|
|
257
|
+
on: {
|
|
258
|
+
input: (val) => {
|
|
259
|
+
model.percent = val;
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
}),
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
});
|
|
266
|
+
this.show(data);
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
// 7. 叠开两层:第一层确认后基于结果打开第二层
|
|
270
|
+
async openNested() {
|
|
271
|
+
const first = await this.$formDialog({
|
|
272
|
+
title: "第一层:选择类型",
|
|
273
|
+
fields: [
|
|
274
|
+
{
|
|
275
|
+
prop: "type",
|
|
276
|
+
label: "类型",
|
|
277
|
+
type: "select",
|
|
278
|
+
required: true,
|
|
279
|
+
options: [
|
|
280
|
+
{ label: "普通", value: 1 },
|
|
281
|
+
{ label: "重要", value: 2 },
|
|
282
|
+
],
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
});
|
|
286
|
+
if (first === null) {
|
|
287
|
+
this.show(null);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const second = await this.$formDialog({
|
|
291
|
+
title: `第二层:填写${first.type === 2 ? "重要" : "普通"}事项`,
|
|
292
|
+
model: { type: first.type },
|
|
293
|
+
fields: [
|
|
294
|
+
{ prop: "name", label: "名称", required: true },
|
|
295
|
+
{ prop: "remark", label: "备注", type: "textarea" },
|
|
296
|
+
],
|
|
297
|
+
});
|
|
298
|
+
this.show(second === null ? null : { ...first, ...second });
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
// 8. 搜索框 + 下拉数据源:静态 options / 词汇 dictCode / 脚本 scriptCode
|
|
302
|
+
async openDataSource() {
|
|
303
|
+
const customers = [
|
|
304
|
+
{ id: 1001, name: "华南贸易有限公司" },
|
|
305
|
+
{ id: 1002, name: "北方物流集团" },
|
|
306
|
+
{ id: 1003, name: "东部电子科技" },
|
|
307
|
+
];
|
|
308
|
+
const data = await this.$formDialog({
|
|
309
|
+
title: "搜索框与下拉数据源",
|
|
310
|
+
labelWidth: "120px",
|
|
311
|
+
fields: [
|
|
312
|
+
{
|
|
313
|
+
prop: "staticType",
|
|
314
|
+
label: "静态下拉",
|
|
315
|
+
type: "select",
|
|
316
|
+
options: [
|
|
317
|
+
{ label: "普通", value: 1 },
|
|
318
|
+
{ label: "重要", value: 2 },
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
// 词汇下拉:显示 value、取值 sn;dictCode 替换为环境中存在的词汇编码
|
|
323
|
+
prop: "dictType",
|
|
324
|
+
label: "词汇下拉",
|
|
325
|
+
type: "select",
|
|
326
|
+
dictCode: "kehuGrade",
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
// 脚本下拉:"formCode/scriptCode",labelField/valueField 按脚本返回结构调整;
|
|
330
|
+
// 也可用 parseOptions: (res) => [{label, value}] 完全自定义解析
|
|
331
|
+
prop: "scriptType",
|
|
332
|
+
label: "脚本下拉",
|
|
333
|
+
type: "select",
|
|
334
|
+
scriptCode: "/intf/listUsers",
|
|
335
|
+
labelField: "nick_name",
|
|
336
|
+
valueField: "id",
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
// 搜索方式1:onSearch 完全自定义(这里用嵌套 formDialog 模拟选择)
|
|
340
|
+
prop: "customerName",
|
|
341
|
+
label: "客户(onSearch)",
|
|
342
|
+
type: "search",
|
|
343
|
+
required: true,
|
|
344
|
+
placeholder: "点击搜索按钮选择",
|
|
345
|
+
onSearch: async (model) => {
|
|
346
|
+
const picked = await this.$formDialog({
|
|
347
|
+
title: "选择客户",
|
|
348
|
+
width: "420px",
|
|
349
|
+
fields: [
|
|
350
|
+
{
|
|
351
|
+
prop: "customerId",
|
|
352
|
+
label: "客户",
|
|
353
|
+
type: "select",
|
|
354
|
+
required: true,
|
|
355
|
+
options: customers.map((item) => ({
|
|
356
|
+
label: item.name,
|
|
357
|
+
value: item.id,
|
|
358
|
+
})),
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
});
|
|
362
|
+
if (picked) {
|
|
363
|
+
const row = customers.find(
|
|
364
|
+
(item) => item.id === picked.customerId
|
|
365
|
+
);
|
|
366
|
+
model.customerId = row.id;
|
|
367
|
+
model.customerName = row.name;
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
// 清除时把关联的 id 一起清掉
|
|
371
|
+
onClear: (model) => {
|
|
372
|
+
model.customerId = null;
|
|
373
|
+
model.customerName = null;
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
// 搜索方式2:searchComponent 打开自定义 Vue 弹框组件,
|
|
378
|
+
// 组件 $emit("confirm", rows) 后按 fieldMap 回写 model
|
|
379
|
+
prop: "supplierName",
|
|
380
|
+
label: "供应商(Vue弹框)",
|
|
381
|
+
type: "search",
|
|
382
|
+
placeholder: "点击搜索按钮选择",
|
|
383
|
+
searchComponent: {
|
|
384
|
+
componentPath: "views/user/user/dialog.vue",
|
|
385
|
+
fieldMap: { supplierId: "id", supplierName: "nickName" },
|
|
386
|
+
},
|
|
387
|
+
onClear: (model) => {
|
|
388
|
+
model.supplierId = null;
|
|
389
|
+
model.supplierName = null;
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
// 搜索方式3:searchDialog 打开 xform 搜索弹框(searchFormDialog),
|
|
394
|
+
// formCode 替换为环境中已配置搜索弹框的表单编码
|
|
395
|
+
prop: "billName",
|
|
396
|
+
label: "单据(xform弹框)",
|
|
397
|
+
type: "search",
|
|
398
|
+
placeholder: "点击搜索按钮选择",
|
|
399
|
+
searchDialog: {
|
|
400
|
+
formCode: "user_dialog",
|
|
401
|
+
multiple: false,
|
|
402
|
+
// queryParam: {...}, // 额外查询参数
|
|
403
|
+
fieldMap: { billId: "id", billName: "nick_name" },
|
|
404
|
+
// onConfirm: (model, rows) => {...} // 或完全自定义回写
|
|
405
|
+
},
|
|
406
|
+
onClear: (model) => {
|
|
407
|
+
model.billId = null;
|
|
408
|
+
model.billName = null;
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
],
|
|
412
|
+
});
|
|
413
|
+
this.show(data);
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
// 9. 字段联动:级联下拉(dependsOn) + 显隐(visible) + 禁用(disabled) + onChange
|
|
417
|
+
async openLinkage() {
|
|
418
|
+
const cityMap = {
|
|
419
|
+
gd: [
|
|
420
|
+
{ label: "广州", value: "gz" },
|
|
421
|
+
{ label: "深圳", value: "sz" },
|
|
422
|
+
],
|
|
423
|
+
hn: [
|
|
424
|
+
{ label: "长沙", value: "cs" },
|
|
425
|
+
{ label: "岳阳", value: "yy" },
|
|
426
|
+
],
|
|
427
|
+
};
|
|
428
|
+
const data = await this.$formDialog({
|
|
429
|
+
title: "字段联动",
|
|
430
|
+
fields: [
|
|
431
|
+
{
|
|
432
|
+
prop: "province",
|
|
433
|
+
label: "省份",
|
|
434
|
+
type: "select",
|
|
435
|
+
required: true,
|
|
436
|
+
options: [
|
|
437
|
+
{ label: "广东", value: "gd" },
|
|
438
|
+
{ label: "湖南", value: "hn" },
|
|
439
|
+
],
|
|
440
|
+
// onChange(value, model, field, dialog):dialog 上有
|
|
441
|
+
// setFieldOptions / reloadFieldOptions 可手动控制其他字段选项
|
|
442
|
+
onChange: (val, model) => {
|
|
443
|
+
model.provinceName = { gd: "广东", hn: "湖南" }[val] || null;
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
// 级联:province 变化时自动清空 city;选项用函数按 model 动态取。
|
|
448
|
+
// 若选项来自 dictCode/scriptCode,dependsOn 还会自动重新拉取
|
|
449
|
+
//(dictData/scriptData 支持函数形式 (model) => ({...}))
|
|
450
|
+
prop: "city",
|
|
451
|
+
label: "城市",
|
|
452
|
+
type: "select",
|
|
453
|
+
required: true,
|
|
454
|
+
dependsOn: ["province"],
|
|
455
|
+
options: (model) => cityMap[model.province] || [],
|
|
456
|
+
},
|
|
457
|
+
{ prop: "needInvoice", label: "需要发票", type: "switch" },
|
|
458
|
+
{
|
|
459
|
+
// 显隐联动:开关打开才显示;隐藏时不渲染也不参与必填校验
|
|
460
|
+
prop: "taxNo",
|
|
461
|
+
label: "税号",
|
|
462
|
+
required: true,
|
|
463
|
+
visible: (model) => model.needInvoice === true,
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
prop: "level",
|
|
467
|
+
label: "等级",
|
|
468
|
+
type: "radio",
|
|
469
|
+
options: [
|
|
470
|
+
{ label: "普通", value: 1 },
|
|
471
|
+
{ label: "特殊", value: 2 },
|
|
472
|
+
],
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
// 禁用联动:等级为"特殊"才允许填原因
|
|
476
|
+
prop: "reason",
|
|
477
|
+
label: "特殊原因",
|
|
478
|
+
type: "textarea",
|
|
479
|
+
disabled: (model) => model.level !== 2,
|
|
480
|
+
},
|
|
481
|
+
],
|
|
482
|
+
});
|
|
483
|
+
this.show(data);
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
// 10. xform 整表模式:formCode 渲染设计器里配置好的表单,确认走 xform 校验
|
|
487
|
+
// formCode 为占位编码,按环境替换为已发布的表单模板编码
|
|
488
|
+
async openXform() {
|
|
489
|
+
const data = await this.$formDialog({
|
|
490
|
+
formCode: "demoForm",
|
|
491
|
+
// title 不传时默认取表单模板名称;width 默认 1000px
|
|
492
|
+
// model: { name: "初始值" }, // 初始数据(结构与 getFormData 一致)
|
|
493
|
+
// optionData / globalDsv 可透传给 VFormRender
|
|
494
|
+
// onReady: (formRef, dialog) => {}, // 表单挂载后回调,可调 setFieldValue 等
|
|
495
|
+
beforeConfirm: (formData) => {
|
|
496
|
+
// 常规用法:这里调保存接口,reject 则弹框不关闭
|
|
497
|
+
console.log("xform 表单数据", formData);
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
this.show(data);
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
// 11. xform 整表只读:readonly=true 走 setReadMode,底部只有关闭
|
|
504
|
+
async openXformReadonly() {
|
|
505
|
+
const data = await this.$formDialog({
|
|
506
|
+
formCode: "demoForm",
|
|
507
|
+
readonly: true,
|
|
508
|
+
model: {},
|
|
509
|
+
});
|
|
510
|
+
this.show(data);
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
};
|
|
514
|
+
</script>
|
|
515
|
+
|
|
516
|
+
<style lang="scss" scoped>
|
|
517
|
+
.form-dialog-demo {
|
|
518
|
+
padding: 16px;
|
|
519
|
+
|
|
520
|
+
.btns {
|
|
521
|
+
display: flex;
|
|
522
|
+
flex-wrap: wrap;
|
|
523
|
+
gap: 8px;
|
|
524
|
+
|
|
525
|
+
.el-button {
|
|
526
|
+
margin-left: 0;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.result {
|
|
531
|
+
margin: 0;
|
|
532
|
+
min-height: 60px;
|
|
533
|
+
white-space: pre-wrap;
|
|
534
|
+
word-break: break-all;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
</style>
|
|
@@ -280,6 +280,10 @@ export default {
|
|
|
280
280
|
},
|
|
281
281
|
beforeDestroy() {
|
|
282
282
|
this.clearTimer();
|
|
283
|
+
if (this.reloadHomeMenuHandler) {
|
|
284
|
+
this.$baseEventBus.$off("reloadHomeMenu", this.reloadHomeMenuHandler);
|
|
285
|
+
this.reloadHomeMenuHandler = null;
|
|
286
|
+
}
|
|
283
287
|
},
|
|
284
288
|
created() {
|
|
285
289
|
this.getUserInfo();
|
|
@@ -520,9 +524,12 @@ export default {
|
|
|
520
524
|
},
|
|
521
525
|
|
|
522
526
|
initMenu() {
|
|
523
|
-
|
|
527
|
+
// handler 存实例属性并在销毁时 $off:home 每次登出/登录重建都会重新注册,
|
|
528
|
+
// 不解绑会累积 handler 并钉住已销毁的 home 实例(内存泄露)
|
|
529
|
+
this.reloadHomeMenuHandler = (menuId) => {
|
|
524
530
|
this.$forceUpdate();
|
|
525
|
-
}
|
|
531
|
+
};
|
|
532
|
+
this.$baseEventBus.$on("reloadHomeMenu", this.reloadHomeMenuHandler);
|
|
526
533
|
},
|
|
527
534
|
filterRoute() {
|
|
528
535
|
let oldRoutes = JSON.parse(JSON.stringify(this.sidebarRouters));
|