@houaoran/designer 1.0.0

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.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/dist/components.es.js +11424 -0
  4. package/dist/components.umd.js +878 -0
  5. package/dist/index.es.js +39113 -0
  6. package/dist/index.umd.js +1187 -0
  7. package/package.json +96 -0
  8. package/src/components/DragBox.vue +49 -0
  9. package/src/components/DragTool.vue +235 -0
  10. package/src/components/EventConfig.vue +557 -0
  11. package/src/components/FcDesigner.vue +2569 -0
  12. package/src/components/FcTitle.vue +69 -0
  13. package/src/components/FetchConfig.vue +415 -0
  14. package/src/components/FieldInput.vue +371 -0
  15. package/src/components/FnConfig.vue +315 -0
  16. package/src/components/FnEditor.vue +327 -0
  17. package/src/components/FnInput.vue +103 -0
  18. package/src/components/FormLabel.vue +47 -0
  19. package/src/components/HtmlEditor.vue +125 -0
  20. package/src/components/JsonPreview.vue +146 -0
  21. package/src/components/OptionsTextInput.vue +151 -0
  22. package/src/components/PropsInput.vue +72 -0
  23. package/src/components/Required.vue +75 -0
  24. package/src/components/Row.vue +26 -0
  25. package/src/components/SignaturePad.vue +176 -0
  26. package/src/components/Struct.vue +153 -0
  27. package/src/components/StructEditor.vue +121 -0
  28. package/src/components/StructTree.vue +209 -0
  29. package/src/components/TableOptions.vue +164 -0
  30. package/src/components/TreeOptions.vue +167 -0
  31. package/src/components/TypeSelect.vue +144 -0
  32. package/src/components/Validate.vue +302 -0
  33. package/src/components/ValueInput.vue +89 -0
  34. package/src/components/Warning.vue +46 -0
  35. package/src/components/ai/AiPanel.vue +1122 -0
  36. package/src/components/ai/MarkdownRenderer.vue +548 -0
  37. package/src/components/language/LanguageConfig.vue +174 -0
  38. package/src/components/language/LanguageInput.vue +191 -0
  39. package/src/components/style/BackgroundInput.vue +315 -0
  40. package/src/components/style/BorderInput.vue +242 -0
  41. package/src/components/style/BoxSizeInput.vue +166 -0
  42. package/src/components/style/BoxSpaceInput.vue +269 -0
  43. package/src/components/style/ColorInput.vue +90 -0
  44. package/src/components/style/ConfigItem.vue +118 -0
  45. package/src/components/style/FontInput.vue +197 -0
  46. package/src/components/style/PositionInput.vue +146 -0
  47. package/src/components/style/RadiusInput.vue +164 -0
  48. package/src/components/style/ShadowContent.vue +335 -0
  49. package/src/components/style/ShadowInput.vue +91 -0
  50. package/src/components/style/SizeInput.vue +118 -0
  51. package/src/components/style/StyleConfig.vue +307 -0
  52. package/src/components/table/Table.vue +252 -0
  53. package/src/components/table/TableView.vue +1058 -0
  54. package/src/components/tableForm/TableForm.vue +471 -0
  55. package/src/components/tableForm/TableFormColumnView.vue +103 -0
  56. package/src/components/tableForm/TableFormView.vue +46 -0
  57. package/src/components/tree/FcTree.vue +713 -0
  58. package/src/components/tree/FcTreeNode.vue +216 -0
  59. package/src/config/base/field.js +43 -0
  60. package/src/config/base/form.js +132 -0
  61. package/src/config/base/style.js +26 -0
  62. package/src/config/base/validate.js +15 -0
  63. package/src/config/index.js +70 -0
  64. package/src/config/menu.js +24 -0
  65. package/src/config/rule/alert.js +45 -0
  66. package/src/config/rule/button.js +49 -0
  67. package/src/config/rule/card.js +40 -0
  68. package/src/config/rule/cascader.js +121 -0
  69. package/src/config/rule/checkbox.js +68 -0
  70. package/src/config/rule/col.js +86 -0
  71. package/src/config/rule/collapse.js +30 -0
  72. package/src/config/rule/collapseItem.js +36 -0
  73. package/src/config/rule/color.js +53 -0
  74. package/src/config/rule/date.js +66 -0
  75. package/src/config/rule/dateRange.js +60 -0
  76. package/src/config/rule/divider.js +31 -0
  77. package/src/config/rule/editor.js +31 -0
  78. package/src/config/rule/group.js +86 -0
  79. package/src/config/rule/html.js +43 -0
  80. package/src/config/rule/image.js +32 -0
  81. package/src/config/rule/input.js +62 -0
  82. package/src/config/rule/number.js +49 -0
  83. package/src/config/rule/password.js +52 -0
  84. package/src/config/rule/radio.js +43 -0
  85. package/src/config/rule/rate.js +44 -0
  86. package/src/config/rule/row.js +46 -0
  87. package/src/config/rule/select.js +70 -0
  88. package/src/config/rule/signaturePad.js +59 -0
  89. package/src/config/rule/slider.js +53 -0
  90. package/src/config/rule/space.js +44 -0
  91. package/src/config/rule/subForm.js +47 -0
  92. package/src/config/rule/switch.js +46 -0
  93. package/src/config/rule/tabPane.js +29 -0
  94. package/src/config/rule/table.js +37 -0
  95. package/src/config/rule/tableForm.js +115 -0
  96. package/src/config/rule/tableFormColumn.js +55 -0
  97. package/src/config/rule/tabs.js +38 -0
  98. package/src/config/rule/tag.js +69 -0
  99. package/src/config/rule/text.js +41 -0
  100. package/src/config/rule/textarea.js +63 -0
  101. package/src/config/rule/time.js +58 -0
  102. package/src/config/rule/timeRange.js +49 -0
  103. package/src/config/rule/title.js +37 -0
  104. package/src/config/rule/transfer.js +59 -0
  105. package/src/config/rule/tree.js +70 -0
  106. package/src/config/rule/treeSelect.js +77 -0
  107. package/src/config/rule/upload.js +107 -0
  108. package/src/form/index.js +19 -0
  109. package/src/index.js +173 -0
  110. package/src/locale/en.js +981 -0
  111. package/src/locale/zh-cn.js +983 -0
  112. package/src/style/fonts/fc-icons.woff +0 -0
  113. package/src/style/icon.css +1052 -0
  114. package/src/style/index.css +836 -0
  115. package/src/utils/form.js +9 -0
  116. package/src/utils/highlight/highlight.min.js +307 -0
  117. package/src/utils/highlight/javascript.min.js +80 -0
  118. package/src/utils/highlight/style.css +1 -0
  119. package/src/utils/highlight/xml.min.js +29 -0
  120. package/src/utils/hintStubs.js +120 -0
  121. package/src/utils/index.js +544 -0
  122. package/src/utils/jsonDiff.js +173 -0
  123. package/src/utils/locale.js +23 -0
  124. package/src/utils/message.js +19 -0
  125. package/src/utils/template.js +105 -0
  126. package/types/index.d.ts +575 -0
@@ -0,0 +1,153 @@
1
+ <template>
2
+ <div class="_fd-struct">
3
+ <el-badge type="warning" is-dot :hidden="!configured">
4
+ <div @click="visible=true">
5
+ <slot>
6
+ <el-button class="_fd-plain-button" plain size="small">
7
+ {{ title || t('struct.title') }}
8
+ </el-button>
9
+ </slot>
10
+ </div>
11
+ </el-badge>
12
+ <el-dialog class="_fd-struct-con" :title="title || t('struct.title')" v-model="visible" destroy-on-close
13
+ :close-on-click-modal="false"
14
+ append-to-body width="800px">
15
+ <div ref="editor" v-if="visible"></div>
16
+ <template #footer>
17
+ <div>
18
+ <el-button @click="visible = false" size="default">{{ t('props.cancel') }}</el-button>
19
+ <el-button type="primary" @click="onOk" size="default" color="#2f73ff">{{ t('props.ok') }}</el-button>
20
+ </div>
21
+ </template>
22
+ </el-dialog>
23
+ </div>
24
+ </template>
25
+
26
+ <script>
27
+ import 'codemirror/lib/codemirror.css';
28
+ import CodeMirror from 'codemirror/lib/codemirror';
29
+ import 'codemirror/mode/javascript/javascript';
30
+ import 'codemirror/addon/display/placeholder';
31
+ import {deepParseFn, toJSON} from '../utils/index';
32
+ import {deepCopy} from '@form-create/utils/lib/deepextend';
33
+ import {defineComponent, markRaw} from 'vue';
34
+ import is from '@form-create/utils/lib/type';
35
+ import errorMessage from '../utils/message';
36
+ import beautify from 'js-beautify';
37
+
38
+ export default defineComponent({
39
+ name: 'Struct',
40
+ emits: ['update:modelValue'],
41
+ props: {
42
+ modelValue: [Object, Array, Function, String],
43
+ title: String,
44
+ placeholder: String,
45
+ defaultValue: {
46
+ require: false
47
+ },
48
+ validate: Function,
49
+ text: Boolean,
50
+ },
51
+ inject: ['designer'],
52
+ computed: {
53
+ t() {
54
+ return this.designer.setupState.t;
55
+ },
56
+ configured() {
57
+ return !is.empty(this.modelValue) && Object.keys(this.modelValue).length > 0;
58
+ },
59
+ },
60
+ data() {
61
+ return {
62
+ editor: null,
63
+ visible: false,
64
+ oldVal: null,
65
+ };
66
+ },
67
+ watch: {
68
+ modelValue() {
69
+ this.load();
70
+ },
71
+ visible(n) {
72
+ if (n) {
73
+ this.load();
74
+ }
75
+ },
76
+ },
77
+ methods: {
78
+ load() {
79
+ const val = this.text
80
+ ? this.modelValue || this.defaultValue
81
+ : toJSON(deepParseFn(this.modelValue ? deepCopy(this.modelValue) : this.defaultValue));
82
+ const placeholder = this.placeholder || '';
83
+ this.oldVal = val;
84
+ this.$nextTick(() => {
85
+ this.editor = markRaw(CodeMirror(this.$refs.editor, {
86
+ lineNumbers: true,
87
+ mode: this.text ? 'null' : 'javascript',
88
+ lint: true,
89
+ line: true,
90
+ tabSize: 2,
91
+ lineWrapping: true,
92
+ placeholder,
93
+ value: val && !this.text ? beautify.js(val, {
94
+ indent_size: '2',
95
+ indent_char: ' ',
96
+ max_preserve_newlines: '5',
97
+ indent_scripts: 'separate',
98
+ }) : this.text ? (val || '') : '',
99
+ }));
100
+ });
101
+ },
102
+ onOk() {
103
+ const str = (this.editor.getValue() || '').trim();
104
+ let val;
105
+ if (this.text) {
106
+ val = str;
107
+ } else {
108
+ try {
109
+ val = (new Function('return ' + str))();
110
+ } catch (e) {
111
+ console.error(e);
112
+ errorMessage(this.t('struct.errorMsg'));
113
+ return false;
114
+ }
115
+ }
116
+ if (this.validate && false === this.validate(val)) {
117
+ errorMessage(this.t('struct.errorMsg'));
118
+ return false;
119
+ }
120
+ this.visible = false;
121
+ if (toJSON(val, null, 2) !== this.oldVal) {
122
+ this.$emit('update:modelValue', val);
123
+ }
124
+ return true;
125
+ },
126
+ }
127
+ });
128
+ </script>
129
+
130
+ <style>
131
+ ._fd-struct {
132
+ width: 100%;
133
+ }
134
+
135
+ ._fd-struct .el-badge {
136
+ width: 100%;
137
+ }
138
+
139
+ ._fd-struct .el-button {
140
+ font-weight: 400;
141
+ width: 100%;
142
+ border-color: #2E73FF;
143
+ color: #2E73FF;
144
+ }
145
+
146
+ ._fd-struct-con .CodeMirror {
147
+ height: 500px;
148
+ }
149
+
150
+ ._fd-struct-con .el-dialog__body {
151
+ padding: 0px;
152
+ }
153
+ </style>
@@ -0,0 +1,121 @@
1
+ <template>
2
+ <div class="_fd-struct-editor">
3
+ <div ref="editor"></div>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ import 'codemirror/lib/codemirror.css';
9
+ import CodeMirror from 'codemirror/lib/codemirror';
10
+ import 'codemirror/mode/javascript/javascript';
11
+ import {toJSON} from '../utils/index';
12
+ import {defineComponent, markRaw} from 'vue';
13
+ import errorMessage from '../utils/message';
14
+ import {designerForm} from '../utils/form';
15
+ import beautify from 'js-beautify';
16
+
17
+ export default defineComponent({
18
+ name: 'StructEditor',
19
+ props: {
20
+ modelValue: [Object, Array, Function, String],
21
+ format: Boolean,
22
+ defaultValue: {
23
+ require: false
24
+ }
25
+ },
26
+ emits: ['blur', 'focus', 'update:modelValue'],
27
+ inject: ['designer'],
28
+ data() {
29
+ return {
30
+ editor: null,
31
+ visible: false,
32
+ err: false,
33
+ oldVal: null,
34
+ };
35
+ },
36
+ computed: {
37
+ t() {
38
+ return this.designer.setupState.t;
39
+ },
40
+ },
41
+ watch: {
42
+ modelValue(n) {
43
+ if (this.editor) {
44
+ const val = n ? this.toJson(n) : '';
45
+ this.oldVal = val;
46
+ const scrollInfo = this.editor.getScrollInfo();
47
+ const scrollTop = scrollInfo.top;
48
+ this.editor.setValue(val);
49
+ this.editor.scrollTo(0, scrollTop);
50
+ }
51
+ }
52
+ },
53
+ mounted() {
54
+ this.$nextTick(() => {
55
+ this.load();
56
+ });
57
+ },
58
+ methods: {
59
+ toJson(val) {
60
+ return this.format ? designerForm.toJson(val, 2) : toJSON(val);
61
+ },
62
+ load() {
63
+ const val = this.modelValue ? this.toJson(this.modelValue) : '';
64
+ this.oldVal = val;
65
+ this.$nextTick(() => {
66
+ this.editor = markRaw(CodeMirror(this.$refs.editor, {
67
+ lineNumbers: true,
68
+ mode: 'javascript',
69
+ lint: true,
70
+ line: true,
71
+ tabSize: 2,
72
+ lineWrapping: true,
73
+ value: val ? beautify.js(val, {
74
+ indent_size: '2',
75
+ indent_char: ' ',
76
+ max_preserve_newlines: '5',
77
+ indent_scripts: 'separate',
78
+ }) : '',
79
+ }));
80
+ this.editor.on('blur', () => {
81
+ this.$emit('blur');
82
+ });
83
+ this.editor.on('focus', () => {
84
+ this.$emit('focus');
85
+ });
86
+ });
87
+ },
88
+ save() {
89
+ const str = (this.editor.getValue() || '').trim();
90
+ let val;
91
+ try {
92
+ val = (new Function('return ' + str))();
93
+ } catch (e) {
94
+ console.error(e);
95
+ errorMessage(this.t('struct.errorMsg'));
96
+ return false;
97
+ }
98
+ if (this.validate && false === this.validate(val)) {
99
+ this.err = true;
100
+ return false;
101
+ }
102
+ this.visible = false;
103
+ if (this.toJson(val) !== this.oldVal) {
104
+ this.$emit('update:modelValue', val);
105
+ }
106
+ return true;
107
+ },
108
+ }
109
+ });
110
+ </script>
111
+
112
+ <style>
113
+ ._fd-struct-editor {
114
+ flex: 1;
115
+ width: 100%;
116
+ }
117
+
118
+ ._fd-struct-editor > div {
119
+ height: 100%;
120
+ }
121
+ </style>
@@ -0,0 +1,209 @@
1
+ <template>
2
+ <FcTree
3
+ class="_fd-struct-tree"
4
+ ref="tree"
5
+ :data="treeData"
6
+ :indent="10"
7
+ default-expand-all
8
+ :placeholder="treePlaceholder"
9
+ :filter-node-method="treeFilterNode"
10
+ :get-key="getKey"
11
+ :get-children="getChildren"
12
+ draggable
13
+ :can-inner-drop="treeCanInnerDrop"
14
+ :before-drag="treeBeforeDrag"
15
+ :before-drop="treeBeforeDrop"
16
+ ensure-children
17
+ @select="select"
18
+ @drop="onDrop"
19
+ >
20
+ <template #default="{ node, data }">
21
+ <slot :node="node" :data="data" />
22
+ </template>
23
+ </FcTree>
24
+ </template>
25
+
26
+ <script>
27
+ import FcTree from './tree/FcTree.vue';
28
+
29
+ function defaultGetKey(item) {
30
+ return item?.id ?? item?.rule?._fc_id ?? item?.rule?.field ?? item?.rule?.name ?? item;
31
+ }
32
+
33
+ export default {
34
+ name: 'StructTree',
35
+ components: { FcTree },
36
+ inject: {
37
+ designer: { default: null },
38
+ },
39
+ props: {
40
+ getKey: { type: Function, default: defaultGetKey },
41
+ // 通用树 children 适配(默认使用 item.children)
42
+ getChildren: { type: Function, default: item => item?.children },
43
+ },
44
+ computed: {
45
+ designerProxy() {
46
+ return this.designer.setupState;
47
+ },
48
+ treeData() {
49
+ return Array.isArray(this.designerProxy.treeInfo) ? this.designerProxy.treeInfo : [];
50
+ },
51
+ treePlaceholder() {
52
+ return this.designerProxy.t('designer.search');
53
+ },
54
+ },
55
+ methods: {
56
+ filter(val) {
57
+ this.$refs.tree && this.$refs.tree.filter && this.$refs.tree.filter(val);
58
+ },
59
+ select(item) {
60
+ // 这里是 StructTree 的唯一使用点(树面板),选中即激活组件
61
+ this.designerProxy.triggerActive(item?.rule);
62
+ },
63
+ treeFilterNode(val, data) {
64
+ if (!val) return true;
65
+ return (
66
+ data.id?.indexOf(val) > -1 ||
67
+ data.rule?.field?.indexOf(val) > -1 ||
68
+ data.rule?.name?.indexOf(val) > -1 ||
69
+ data.rule?.title?.indexOf(val) > -1 ||
70
+ data.rule.__fc__?.refRule?.__$title?.value?.indexOf(val) > -1 ||
71
+ (typeof data.rule?.children?.[0] === 'string' && data.rule?.children?.[0].indexOf(val) > -1)
72
+ );
73
+ },
74
+ treeCanInnerDrop(item) {
75
+ const rule = item?.rule;
76
+ if (!rule) return false;
77
+ const menu = rule._menu;
78
+ if (!menu) return true;
79
+ if (menu.drag === true) return true;
80
+ if (menu.children) return true;
81
+ return false;
82
+ },
83
+ treeBeforeDrag({ dragItem }) {
84
+ const rule = dragItem?.rule;
85
+ if (!rule) return false;
86
+ const d = this.designerProxy;
87
+ const hiddenDragBtn = d.hiddenDragBtn;
88
+ const hidden = typeof hiddenDragBtn === 'object' && hiddenDragBtn ? hiddenDragBtn.value : hiddenDragBtn;
89
+ const permission = d.getPermission(rule);
90
+ return !(permission?.move === false || hidden);
91
+ },
92
+ treeBeforeDrop({ dragItem, toParent }) {
93
+ const dragRule = dragItem?._menu ? dragItem : dragItem?.rule;
94
+ const toParentRule = toParent?._menu ? toParent : toParent?.rule;
95
+ if (!dragRule || !toParentRule) return !!dragRule._menu.menu;
96
+ const menu = toParentRule._menu;
97
+ if (menu) {
98
+ if (!menu.children && menu.drag !== true) return false;
99
+ if (menu.children && menu.children !== menu.name && menu.children !== dragRule._menu?.name) return false;
100
+ if (!dragRule._menu.menu) {
101
+ if (dragRule.__fc__.parent.rule.type === 'DragTool') {
102
+ return dragRule.__fc__.parent.parent.rule === toParentRule;
103
+ } else {
104
+ return dragRule.__fc__.parent.rule === toParentRule;
105
+ }
106
+ }
107
+ }
108
+ return true;
109
+ },
110
+ getMovableRule(rule) {
111
+ if (!rule) return null;
112
+ if (rule._menu?.inside) {
113
+ return rule;
114
+ }
115
+ return rule.__fc__?.parent?.rule || rule;
116
+ },
117
+ getRuleTop(rule) {
118
+ let parent = rule?.__fc__?.parent?.rule;
119
+ let current = rule;
120
+ const config = parent?._menu;
121
+ if (config && config.inside) {
122
+ current = parent;
123
+ parent = parent.__fc__?.parent?.rule;
124
+ }
125
+ return { root: parent, parent: current };
126
+ },
127
+ getRuleParentInfo(rule) {
128
+ const moveRule = this.getMovableRule(rule);
129
+ const parentRule = moveRule?.__fc__?.parent?.rule || null;
130
+ return {
131
+ moveRule,
132
+ parentRule,
133
+ list: parentRule?.children || [],
134
+ };
135
+ },
136
+ getRootRuleList(fallbackRule) {
137
+ const firstRoot = (this.treeData || [])[0];
138
+ if (firstRoot?.rule) {
139
+ const { list } = this.getRuleParentInfo(firstRoot.rule);
140
+ if (Array.isArray(list) && list.length > 0) {
141
+ return list;
142
+ }
143
+ const mr = this.getMovableRule(firstRoot.rule);
144
+ if (mr && Array.isArray(mr.children)) {
145
+ return mr.children;
146
+ }
147
+ return list || [];
148
+ }
149
+ if (fallbackRule) {
150
+ return this.getRuleTop(fallbackRule).root?.children || [];
151
+ }
152
+ return [];
153
+ },
154
+ onDrop(payload) {
155
+ // payload 来自 FcTree:dragItem/dropItem 是树节点对象(非 rule)
156
+ const dragItem = payload?.dragItem;
157
+ if (!dragItem) return false;
158
+
159
+ const sourceInfo = this.getRuleParentInfo(dragItem.rule);
160
+ if (!sourceInfo.moveRule || !sourceInfo.list) return false;
161
+
162
+ // beforeDrop/canDrop 已在 FcTree 里判断过,这里只做最终业务移动
163
+ let targetList;
164
+ let targetIndex = payload.toIndex;
165
+ if (payload.dropType === 'inner') {
166
+ const targetItem = payload.toParent;
167
+ if (!targetItem) return false;
168
+ if (!Array.isArray(targetItem.rule.children)) targetItem.rule.children = [];
169
+ targetList = targetItem.rule.children;
170
+ } else if (payload.toKey === '__root__') {
171
+ targetList = this.getRootRuleList(dragItem.rule);
172
+ } else {
173
+ const targetItem = payload.dropItem;
174
+ const targetInfo = targetItem ? this.getRuleParentInfo(targetItem.rule) : null;
175
+ targetList = targetInfo?.list || null;
176
+ }
177
+ if (!targetList) return false;
178
+
179
+ const oldIndex = sourceInfo.list.indexOf(sourceInfo.moveRule);
180
+ if (oldIndex < 0) return false;
181
+
182
+ // 同一列表内拖拽时,resolvedIndex 是“包含自身”的插入位;若最终还是原位,直接视为无操作
183
+ if (sourceInfo.list === targetList) {
184
+ let normalizedIndex = targetIndex;
185
+ if (oldIndex < normalizedIndex) normalizedIndex -= 1;
186
+ if (normalizedIndex === oldIndex) return false;
187
+ }
188
+ delete sourceInfo.moveRule.slot;
189
+ this.designerProxy.handleSortBefore();
190
+
191
+ sourceInfo.list.splice(oldIndex, 1);
192
+ if (sourceInfo.list === targetList && oldIndex < targetIndex) {
193
+ targetIndex -= 1;
194
+ }
195
+ if (targetIndex < 0) targetIndex = 0;
196
+ if (targetIndex > targetList.length) targetIndex = targetList.length;
197
+ targetList.splice(targetIndex, 0, sourceInfo.moveRule);
198
+ this.designerProxy.handleSortAfter();
199
+ return true;
200
+ },
201
+ },
202
+ };
203
+ </script>
204
+
205
+ <style scoped>
206
+ ._fd-struct-tree {
207
+ padding: 8px 12px;
208
+ }
209
+ </style>
@@ -0,0 +1,164 @@
1
+ <template>
2
+ <div class="_td-table-opt">
3
+ <el-table
4
+ :data="value"
5
+ border
6
+ :size="size || 'small'"
7
+ style="width: 100%">
8
+ <template v-for="(col,idx) in column" :key="col.label + idx">
9
+ <el-table-column :label="col.label">
10
+ <template #default="scope">
11
+ <template v-if="col.value">
12
+ <ValueInput :size="size || 'small'" :modelValue="scope.row[col.key]" :disabled="disabled"
13
+ @update:modelValue="(n)=>(scope.row[col.key] = n)"
14
+ @blur="onInput(scope.row)" @change-type="onInput(scope.row)"></ValueInput>
15
+ </template>
16
+ <template v-else>
17
+ <el-input :size="size || 'small'" :disabled="disabled" :modelValue="scope.row[col.key] || ''"
18
+ @Update:modelValue="(n)=>(scope.row[col.key] = n)"
19
+ @blur="onInput(scope.row)"
20
+ @keydown.enter="onInput(scope.row)"></el-input>
21
+ </template>
22
+ </template>
23
+ </el-table-column>
24
+ </template>
25
+ <el-table-column width="45" align="center" fixed="right" v-if="!disabled">
26
+ <template #default="scope">
27
+ <i class="fc-icon icon-delete" @click="del(scope.$index)"></i>
28
+ </template>
29
+ </el-table-column>
30
+ </el-table>
31
+ <div class="_td-table-opt-handle">
32
+ <el-button link type="primary" @click="add" v-if="!max || max > value.length">
33
+ <i class="fc-icon icon-add"></i> {{ t('tableOptions.add') }}
34
+ </el-button>
35
+ </div>
36
+
37
+ </div>
38
+ </template>
39
+
40
+ <script>
41
+ import {defineComponent} from 'vue';
42
+ import {copy} from '@form-create/utils/lib/extend';
43
+ import ValueInput from './ValueInput.vue';
44
+
45
+ export default defineComponent({
46
+ name: 'TableOptions',
47
+ emits: ['update:modelValue', 'change'],
48
+ components: {
49
+ ValueInput
50
+ },
51
+ props: {
52
+ modelValue: [Array, Object],
53
+ column: {
54
+ type: Array,
55
+ default: () => [{label: 'label', key: 'label'}, {label: 'value', key: 'value'}]
56
+ },
57
+ valueType: String,
58
+ max: Number,
59
+ size: String,
60
+ disabled: Boolean,
61
+ },
62
+ inject: ['designer'],
63
+ watch: {
64
+ modelValue() {
65
+ this.value = this.tidyModelValue();
66
+ }
67
+ },
68
+ computed: {
69
+ t() {
70
+ return this.designer.setupState.t;
71
+ },
72
+ },
73
+ data() {
74
+ return {
75
+ value: this.tidyModelValue(),
76
+ };
77
+ },
78
+ methods: {
79
+ tidyModelValue() {
80
+ const modelValue = this.modelValue;
81
+ if (this.valueType === 'string') {
82
+ return (modelValue || []).map(value => {
83
+ return {value: '' + value}
84
+ })
85
+ } else if (this.valueType === 'object') {
86
+ return Object.keys((modelValue || {})).map(label => {
87
+ return {label, value: modelValue[label]}
88
+ })
89
+ } else {
90
+ return [...modelValue || []].map(v => {
91
+ return copy(v);
92
+ });
93
+ }
94
+ },
95
+ tidyValue() {
96
+ if (this.valueType === 'object') {
97
+ const obj = {};
98
+ this.value.forEach(v => {
99
+ if (v.label && v.value) {
100
+ obj[v.label] = v.value;
101
+ }
102
+ })
103
+ return obj;
104
+ } else {
105
+ return this.value.map(v => {
106
+ if (this.valueType === 'string') {
107
+ return v.value;
108
+ }
109
+ return {...v}
110
+ });
111
+ }
112
+ },
113
+ onInput(item) {
114
+ if (this.column.length === 1 && '' === item[this.column[0].key]) {
115
+ return;
116
+ }
117
+ const flag = this.column.every(v => {
118
+ if (v.required === false) {
119
+ return true;
120
+ }
121
+ if (['object', 'string'].indexOf(this.valueType) > -1) {
122
+ return item[v.key] !== undefined && item[v.key] !== '' && item[v.key] !== null;
123
+ }
124
+ return item[v.key] !== undefined;
125
+ })
126
+ if (flag) {
127
+ this.input();
128
+ }
129
+ },
130
+ input() {
131
+ const value = this.tidyValue();
132
+ this.$emit('update:modelValue', value);
133
+ this.$emit('change', value);
134
+ },
135
+ add() {
136
+ this.value.push(this.column.reduce((initial, v) => {
137
+ initial[v.key] = '';
138
+ return initial;
139
+ }, {}));
140
+ },
141
+ del(idx) {
142
+ this.value.splice(idx, 1);
143
+ this.input();
144
+ }
145
+ }
146
+ });
147
+ </script>
148
+
149
+ <style scoped>
150
+ ._td-table-opt {
151
+ width: 100%;
152
+ }
153
+
154
+ ._td-table-opt .el-table {
155
+ z-index: 1;
156
+ }
157
+
158
+ ._td-table-opt-handle {
159
+ display: flex;
160
+ justify-content: space-between;
161
+ align-items: center;
162
+ padding-right: 5px;
163
+ }
164
+ </style>