@ebiz/designer-components 0.1.136 → 0.1.138

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 (231) hide show
  1. package/README.md +29 -29
  2. package/dist/designer-components.css +1 -1
  3. package/dist/index.mjs +69289 -67213
  4. package/dist/{pdf-C9oIcL2N.js → pdf-KomaE0u5.js} +891 -891
  5. package/package.json +1 -1
  6. package/src/App.vue +26 -26
  7. package/src/apiService/SIMPLE_DATA_SERVICE.md +284 -284
  8. package/src/apiService/mockDataService.js +115 -115
  9. package/src/apiService/simpleDataService.js +312 -312
  10. package/src/assets/base.css +86 -86
  11. package/src/assets/logo.svg +1 -1
  12. package/src/components/Button.vue +149 -149
  13. package/src/components/DataContainer.vue +40 -40
  14. package/src/components/EbizApproval.vue +747 -747
  15. package/src/components/EbizApprovalDetail.vue +169 -169
  16. package/src/components/EbizApprovalForm.vue +510 -510
  17. package/src/components/EbizApprovalV2.vue +747 -747
  18. package/src/components/EbizAutoForm.vue +596 -596
  19. package/src/components/EbizAvatar.vue +115 -115
  20. package/src/components/EbizCheckbox.vue +93 -93
  21. package/src/components/EbizCheckboxGroup.vue +69 -69
  22. package/src/components/EbizDepartmentSelector.vue +148 -148
  23. package/src/components/EbizDescriptions.vue +340 -340
  24. package/src/components/EbizDescriptionsItem.vue +47 -47
  25. package/src/components/EbizDetailBlock.vue +81 -81
  26. package/src/components/EbizDetailItem.vue +559 -559
  27. package/src/components/EbizDetailView.md +438 -438
  28. package/src/components/EbizDetailView.vue +356 -356
  29. package/src/components/EbizDialog.vue +260 -260
  30. package/src/components/EbizDictionarySelect.vue +229 -229
  31. package/src/components/EbizDiv.vue +40 -40
  32. package/src/components/EbizDivider.vue +96 -96
  33. package/src/components/EbizDormDashboard.vue +314 -314
  34. package/src/components/EbizDropdown.vue +135 -135
  35. package/src/components/EbizDropdownItem.vue +85 -85
  36. package/src/components/EbizEmployeeInfo.vue +144 -144
  37. package/src/components/EbizEmployeeSelector.vue +1160 -1160
  38. package/src/components/EbizFileList.vue +502 -502
  39. package/src/components/EbizMap.vue +541 -541
  40. package/src/components/EbizMeetingRoomSelector.vue +1149 -1149
  41. package/src/components/EbizMobileMeetingRoomSelector.vue +727 -727
  42. package/src/components/EbizOkrTree.vue +99 -99
  43. package/src/components/EbizPageHeader.vue +98 -98
  44. package/src/components/EbizPagination.vue +162 -162
  45. package/src/components/EbizParkingDashboard.vue +152 -152
  46. package/src/components/EbizPdfViewer.vue +540 -540
  47. package/src/components/EbizPopconfirm.vue +47 -47
  48. package/src/components/EbizQrCode.vue +167 -167
  49. package/src/components/EbizRadio.vue +86 -86
  50. package/src/components/EbizRadioGroup.vue +83 -83
  51. package/src/components/EbizRemoteSelect.vue +232 -232
  52. package/src/components/EbizRichTextEditor.vue +338 -338
  53. package/src/components/EbizRouteBreadcrumb.vue +46 -46
  54. package/src/components/EbizSApprovalProcess.vue +1456 -1456
  55. package/src/components/EbizSelect.vue +85 -85
  56. package/src/components/EbizSpace.vue +100 -100
  57. package/src/components/EbizStatistic.vue +149 -149
  58. package/src/components/EbizStatisticChart.vue +213 -213
  59. package/src/components/EbizStatsCard.vue +113 -113
  60. package/src/components/EbizSwiper.vue +113 -113
  61. package/src/components/EbizSwiperItem.vue +13 -13
  62. package/src/components/EbizSwitch.vue +85 -85
  63. package/src/components/EbizTabHeader.vue +132 -132
  64. package/src/components/EbizTabPanel.vue +22 -22
  65. package/src/components/EbizTable.vue +469 -469
  66. package/src/components/EbizTableColumn.vue +116 -116
  67. package/src/components/EbizTableSort.vue +179 -179
  68. package/src/components/EbizTabs.vue +142 -142
  69. package/src/components/EbizTdesignButtonDialog.vue +332 -332
  70. package/src/components/EbizTdesignLoading.vue +107 -107
  71. package/src/components/EbizTimePicker.vue +143 -143
  72. package/src/components/EbizTitle.vue +91 -91
  73. package/src/components/EbizTree.vue +141 -141
  74. package/src/components/EbizTreeMergeTable.vue +1494 -1494
  75. package/src/components/EbizTreeSelector.vue +469 -469
  76. package/src/components/EbizVideo.vue +553 -553
  77. package/src/components/EbizVxeTable.vue +290 -290
  78. package/src/components/Form.vue +28 -28
  79. package/src/components/Home.vue +7 -7
  80. package/src/components/LaunchInterview.vue +526 -526
  81. package/src/components/MyComponent.vue +39 -39
  82. package/src/components/Table.vue +45 -45
  83. package/src/components/TdesignAlert.vue +115 -115
  84. package/src/components/TdesignButton.vue +135 -135
  85. package/src/components/TdesignCalendar/index.vue +145 -145
  86. package/src/components/TdesignCard.vue +195 -195
  87. package/src/components/TdesignCol.vue +101 -101
  88. package/src/components/TdesignCollapse.vue +142 -142
  89. package/src/components/TdesignCollapsePanel.vue +79 -79
  90. package/src/components/TdesignDatePicker.vue +124 -124
  91. package/src/components/TdesignDescriptions.vue +74 -74
  92. package/src/components/TdesignDescriptionsItem.vue +50 -50
  93. package/src/components/TdesignDialog.vue +225 -225
  94. package/src/components/TdesignForm.vue +138 -138
  95. package/src/components/TdesignFormItem.vue +105 -105
  96. package/src/components/TdesignGrid.vue +55 -55
  97. package/src/components/TdesignIcon.vue +67 -67
  98. package/src/components/TdesignImage.vue +162 -162
  99. package/src/components/TdesignImageViewer.vue +200 -200
  100. package/src/components/TdesignInput.vue +242 -242
  101. package/src/components/TdesignSelect.vue +446 -446
  102. package/src/components/TdesignTag.vue +117 -117
  103. package/src/components/TdesignTextarea.vue +142 -142
  104. package/src/components/TdesignTimeline.vue +58 -58
  105. package/src/components/TdesignTimelineItem.vue +71 -71
  106. package/src/components/TdesignUpload.vue +414 -414
  107. package/src/components/TdesignWatermark.vue +107 -107
  108. package/src/components/ebiz-form/components/cascader.vue +61 -61
  109. package/src/components/ebiz-form/components/checkbox.vue +37 -37
  110. package/src/components/ebiz-form/components/city.vue +137 -137
  111. package/src/components/ebiz-form/components/date-panel.vue +52 -52
  112. package/src/components/ebiz-form/components/date-range-panel.vue +52 -52
  113. package/src/components/ebiz-form/components/date-range.vue +56 -56
  114. package/src/components/ebiz-form/components/date.vue +52 -52
  115. package/src/components/ebiz-form/components/editor-multi-language.vue +47 -47
  116. package/src/components/ebiz-form/components/editor.vue +78 -78
  117. package/src/components/ebiz-form/components/file-multi-language.vue +52 -52
  118. package/src/components/ebiz-form/components/file.vue +149 -149
  119. package/src/components/ebiz-form/components/images-multi-language.vue +52 -52
  120. package/src/components/ebiz-form/components/images.vue +129 -129
  121. package/src/components/ebiz-form/components/img-multi-language.vue +51 -51
  122. package/src/components/ebiz-form/components/img.vue +129 -129
  123. package/src/components/ebiz-form/components/number.vue +50 -50
  124. package/src/components/ebiz-form/components/radio.vue +28 -28
  125. package/src/components/ebiz-form/components/select.vue +119 -119
  126. package/src/components/ebiz-form/components/switch.vue +23 -23
  127. package/src/components/ebiz-form/components/text-multi-language.vue +47 -47
  128. package/src/components/ebiz-form/components/text.vue +52 -52
  129. package/src/components/ebiz-form/components/textarea-multi-language.vue +48 -48
  130. package/src/components/ebiz-form/components/textarea.vue +29 -29
  131. package/src/components/ebiz-form/components/video-multi-language.vue +51 -51
  132. package/src/components/ebiz-form/components/video.vue +97 -97
  133. package/src/components/ebiz-form/index.vue +157 -157
  134. package/src/components/examples/PopconfirmExample.vue +149 -149
  135. package/src/components/icons/IconCommunity.vue +7 -7
  136. package/src/components/icons/IconDocumentation.vue +7 -7
  137. package/src/components/icons/IconEcosystem.vue +7 -7
  138. package/src/components/icons/IconSupport.vue +7 -7
  139. package/src/components/icons/IconTooling.vue +19 -19
  140. package/src/components/mItems/UserInfo.vue +349 -349
  141. package/src/components/senior/EbizApprovalList/ApprovalList.vue +127 -127
  142. package/src/components/senior/EbizSData/index.vue +280 -280
  143. package/src/components/senior/EbizSDialog/index.vue +776 -776
  144. package/src/components/senior/EbizSForm/README.md +157 -157
  145. package/src/components/senior/EbizSForm/index.vue +667 -667
  146. package/src/components/senior/EbizSForm/item.vue +1011 -1011
  147. package/src/components/senior/EbizSForm/mItems/DateTimePicker.vue +51 -51
  148. package/src/components/senior/EbizSForm/mItems/Picker.vue +63 -63
  149. package/src/index.js +330 -330
  150. package/src/main.js +55 -55
  151. package/src/router/index.js +436 -436
  152. package/src/utils/formatCode.js +24 -24
  153. package/src/utils/generateImportStatement.js +52 -52
  154. package/src/utils/hasJsx.js +25 -25
  155. package/src/utils/index.js +166 -166
  156. package/src/utils/mergeOptions.js +29 -29
  157. package/src/utils/parseRequiredBlocks.js +18 -18
  158. package/src/utils/vue-sfc-validator.js +155 -155
  159. package/src/views/Button.vue +23 -23
  160. package/src/views/CheckboxDemo.vue +104 -104
  161. package/src/views/DataContainer.vue +19 -19
  162. package/src/views/DialogDemo.vue +125 -125
  163. package/src/views/EbizApprovalDemo.vue +87 -87
  164. package/src/views/EbizApprovalFormDemo.vue +207 -207
  165. package/src/views/EbizAutoFormDemo.vue +129 -129
  166. package/src/views/EbizAvatar.vue +223 -223
  167. package/src/views/EbizDepartmentSelectorDemo.vue +169 -169
  168. package/src/views/EbizDetailBlockDemo.vue +30 -30
  169. package/src/views/EbizDetailViewDemo.vue +412 -412
  170. package/src/views/EbizDormDashboardDemo.vue +87 -87
  171. package/src/views/EbizEmployeeInfo.vue +249 -249
  172. package/src/views/EbizEmployeeSelector.vue +85 -85
  173. package/src/views/EbizFileListDemo.vue +339 -339
  174. package/src/views/EbizMap.vue +201 -201
  175. package/src/views/EbizMeetingRoomSelectorDemo.vue +293 -293
  176. package/src/views/EbizMobileMeetingRoomSelectorDemo.vue +566 -566
  177. package/src/views/EbizParkingDashboardDemo.vue +28 -28
  178. package/src/views/EbizRadioDemo.vue +151 -151
  179. package/src/views/EbizSDataDemo.vue +136 -136
  180. package/src/views/EbizSDialogDemo.vue +303 -303
  181. package/src/views/EbizSForm/index.vue +352 -352
  182. package/src/views/EbizSFormDemo.vue +420 -420
  183. package/src/views/EbizSpace.vue +185 -185
  184. package/src/views/EbizSwiper.vue +157 -157
  185. package/src/views/EbizTdesignButtonDialogExample.vue +437 -437
  186. package/src/views/Form.vue +19 -19
  187. package/src/views/GridDemo.vue +238 -238
  188. package/src/views/Home.vue +156 -156
  189. package/src/views/LaunchInterviewDemo.vue +111 -111
  190. package/src/views/Mindmap.vue +17 -17
  191. package/src/views/MyComponent.vue +19 -19
  192. package/src/views/OkrTree.vue +19 -19
  193. package/src/views/PageHeaderDemo.vue +104 -104
  194. package/src/views/PaginationDemo.vue +96 -96
  195. package/src/views/PdfViewerDemo.vue +433 -433
  196. package/src/views/PermissionBoxDemo.vue +85 -85
  197. package/src/views/PopconfirmDemo.vue +80 -80
  198. package/src/views/RemoteSelect.vue +350 -350
  199. package/src/views/StatisticDemo.vue +190 -190
  200. package/src/views/SwitchDemo.vue +79 -79
  201. package/src/views/Table.vue +19 -19
  202. package/src/views/TableDemo.vue +334 -334
  203. package/src/views/TableSortDemo.vue +143 -143
  204. package/src/views/TableView.vue +68 -68
  205. package/src/views/TabsDemo.vue +282 -282
  206. package/src/views/TagDemo.vue +101 -101
  207. package/src/views/TdesignAlert.vue +98 -98
  208. package/src/views/TdesignButton.vue +190 -190
  209. package/src/views/TdesignCalendar.vue +94 -94
  210. package/src/views/TdesignCard.vue +296 -296
  211. package/src/views/TdesignCollapse.vue +293 -293
  212. package/src/views/TdesignDatePicker.vue +187 -187
  213. package/src/views/TdesignDescriptions.vue +101 -101
  214. package/src/views/TdesignForm.vue +248 -248
  215. package/src/views/TdesignIcon.vue +203 -203
  216. package/src/views/TdesignImage.vue +215 -215
  217. package/src/views/TdesignImageViewer.vue +198 -198
  218. package/src/views/TdesignInput.vue +252 -252
  219. package/src/views/TdesignSelect.vue +473 -473
  220. package/src/views/TdesignSwiper.vue +157 -157
  221. package/src/views/TextareaDemo.vue +93 -93
  222. package/src/views/TimePickerDemo.vue +146 -146
  223. package/src/views/TimelineDemo.vue +160 -160
  224. package/src/views/Title.vue +19 -19
  225. package/src/views/TreeDemo.vue +254 -254
  226. package/src/views/TreeMergeTableDemo.vue +239 -239
  227. package/src/views/TreeSelectorDemo.vue +245 -245
  228. package/src/views/UploadDemo.vue +128 -128
  229. package/src/views/VideoDemo.vue +245 -245
  230. package/src/views/VxeTableDemo.vue +279 -279
  231. package/src/views/WatermarkDemo.vue +85 -85
@@ -1,1012 +1,1012 @@
1
- <template>
2
- <t-form-item :class="['ebiz-s-form-item', className]" :for="forAttr" :help="help" :label="label" :name="name"
3
- :style="customStyle" :rules="rules" :label-width="labelWidth" :label-align="labelAlign" :status="status"
4
- @validate="handleValidate" style="margin-bottom: 10px;">
5
-
6
- <slot name="default">
7
- <!-- 员工选择 -->
8
- <template v-if="type === 'employee'">
9
- <ebiz-employee-selector v-model="computedModelValue" :placeholder="placeholder || '请选择员工'"
10
- :disabled="disabled" :clearable="clearable" :single="single" :defaultTab="defaultTab"
11
- :maxCount="maxCount" @change="handleChange" />
12
- </template>
13
-
14
- <!-- 部门选择 -->
15
- <template v-if="type === 'dept'">
16
- <ebiz-department-selector v-model="computedModelValue" :placeholder="placeholder || '请选择部门'"
17
- :disabled="disabled" :clearable="clearable" :filterable="filterable" :apiUrl="apiUrl"
18
- :companyId="companyId" :multiple="multiple" @change="handleChange" />
19
- </template>
20
-
21
- <!-- 输入框 -->
22
- <template v-if="type === 'input'">
23
- <t-input v-model="computedModelValue" :placeholder="placeholder || '请输入'" :disabled="disabled"
24
- :readonly="readonly" :clearable="clearable" :max-length="maxLength" :max-character="maxCharacter"
25
- :show-limit-number="showLimitNumber" @change="handleChange" />
26
- </template>
27
-
28
- <!-- 数字输入框 -->
29
- <template v-else-if="type === 'number'">
30
- <t-input-number v-model="computedModelValue" :placeholder="placeholder || '请输入'" :disabled="disabled"
31
- :readonly="readonly" :max="max" :min="min" :step="step" :format="format" @change="handleChange" />
32
- </template>
33
-
34
- <!-- 文本域 -->
35
- <template v-else-if="type === 'textarea'">
36
- <t-textarea v-model="computedModelValue" :placeholder="placeholder || '请输入'" :disabled="disabled"
37
- :readonly="readonly" :autosize="autosize" :maxlength="maxLength" :max-character="maxCharacter"
38
- :show-limit-number="showLimitNumber" @change="handleChange" />
39
- </template>
40
-
41
- <!-- 选择器 -->
42
- <template v-else-if="type === 'select'">
43
- <template v-if="isRemote">
44
- <ebiz-remote-select v-model="computedModelValue" :queryParams="queryParams"
45
- :placeholder="placeholder || '请选择'" :disabled="disabled" :clearable="clearable"
46
- :filterable="true" :multiple="multiple" :loading="loading" :apiConfig="remoteApiConfig"
47
- @change="handleChange" />
48
- </template>
49
- <template v-else>
50
- <t-select v-model="computedModelValue" :options="options" :placeholder="placeholder || '请选择'"
51
- :disabled="disabled" :clearable="clearable" :filterable="filterable" :multiple="multiple"
52
- :loading="loading" @change="handleChange" />
53
- </template>
54
- </template>
55
-
56
- <!-- 日期选择器 -->
57
- <template v-else-if="type === 'datetime'">
58
- <t-date-picker v-model="computedModelValue" :placeholder="placeholder || '请选择日期时间'" :disabled="disabled"
59
- :clearable="clearable" enable-time-picker format="YYYY-MM-DD HH:mm:ss" @change="handleChange" />
60
- </template>
61
-
62
-
63
- <!-- 日期选择器 -->
64
- <template v-else-if="type === 'date'">
65
- <t-date-picker v-model="computedModelValue" :placeholder="placeholder || '请选择日期'" :disabled="disabled"
66
- :clearable="clearable" :format="dateFormat || 'YYYY-MM-DD'" :mode="dateMode || 'date'"
67
- @change="handleChange" />
68
- </template>
69
-
70
- <!-- 时间选择器 -->
71
- <template v-else-if="type === 'time'">
72
- <t-time-picker v-model="computedModelValue" :placeholder="placeholder || '请选择时间'" :disabled="disabled"
73
- :clearable="clearable" :format="timeFormat || 'HH:mm:ss'" @change="handleChange" />
74
- </template>
75
-
76
- <!-- 范围日期选择器 -->
77
- <template v-else-if="type === 'date-range'">
78
- <t-date-range-picker v-model="computedModelValue" :placeholder="placeholder || '请选择日期范围'" :disabled="disabled"
79
- :clearable="clearable" :format="dateFormat || 'YYYY-MM-DD'" :mode="dateMode || 'date'" @change="handleChange" />
80
- </template>
81
-
82
- <!-- 范围日期时间选择器 -->
83
- <template v-else-if="type === 'datetime-range'">
84
- <t-date-range-picker v-model="computedModelValue" :placeholder="placeholder || '请选择日期时间范围'" :disabled="disabled"
85
- :clearable="clearable" format="YYYY-MM-DD HH:mm:ss" :mode="dateMode || 'date'" enable-time-picker @change="handleChange" />
86
- </template>
87
-
88
- <!-- 单选框组 -->
89
- <template v-else-if="type === 'radio'">
90
- <t-radio-group v-model="computedModelValue" :disabled="disabled" :options="options"
91
- @change="handleChange" />
92
- </template>
93
-
94
- <!-- 复选框组 -->
95
- <template v-else-if="type === 'checkbox'">
96
- <t-checkbox-group v-model="computedModelValue" :disabled="disabled" :options="options" :max="max"
97
- @change="handleChange" />
98
- </template>
99
-
100
- <!-- 开关 -->
101
- <template v-else-if="type === 'switch'">
102
- <t-switch v-model="computedModelValue" :disabled="disabled" @change="handleChange" />
103
- </template>
104
-
105
- <!-- 滑块 -->
106
- <template v-else-if="type === 'slider'">
107
- <t-slider v-model="computedModelValue" :disabled="disabled" :max="max" :min="min" :step="step"
108
- :range="range" :marks="marks" @change="handleChange" />
109
- </template>
110
-
111
- <!-- 上传 -->
112
- <template v-else-if="type === 'upload'">
113
- <ebiz-upload v-model="computedModelValue" :disabled="disabled" :accept="accept" :multiple="multiple"
114
- :max="max" :theme="uploadTheme || 'file'" @change="handleChange" />
115
- </template>
116
-
117
- <!-- 视频上传 -->
118
- <template v-else-if="type === 'video-upload'">
119
- <div>
120
- <ebiz-upload v-if="!computedModelValue" v-model="computedModelValue" :disabled="disabled"
121
- :accept="accept || 'video/*'" :multiple="false" :max="1" :theme="'file'" :action="action"
122
- :headers="headers" :withCredentials="withCredentials" :beforeUpload="beforeUpload"
123
- :formatResponse="formatResponse" :placeholder="placeholder || '请上传视频'" :sizeLimit="sizeLimit"
124
- :tips="tips || '只能上传一个视频文件'" @change="handleChange" />
125
- <div v-else style="display: flex; position: relative;">
126
- <video :src="computedModelValue" controls autoplay muted width="400"></video>
127
- <div style="position: absolute; top: 5px; right: 5px;">
128
- <t-button theme="danger" @click="clearVideoUpload" size="small" :disabled="disabled">
129
- <template #icon>
130
- <t-icon name="delete"></t-icon>
131
- </template>
132
- </t-button>
133
- </div>
134
- </div>
135
- </div>
136
- </template>
137
-
138
- <!-- 级联选择器 -->
139
- <template v-else-if="type === 'cascader'">
140
- <t-cascader v-model="computedModelValue" :disabled="disabled" :options="options"
141
- :placeholder="placeholder || '请选择'" :clearable="clearable" :filterable="filterable"
142
- :multiple="multiple" @change="handleChange" />
143
- </template>
144
-
145
- <!-- 树形选择器 -->
146
- <template v-else-if="type === 'tree-select'">
147
- <t-tree-select v-model="computedModelValue" :disabled="disabled" :data="treeData || options"
148
- :placeholder="placeholder || '请选择'" :clearable="clearable" :filterable="filterable"
149
- :multiple="multiple" @change="handleChange" />
150
- </template>
151
-
152
- <!-- 颜色选择器 -->
153
- <template v-else-if="type === 'color-picker'">
154
- <t-color-picker v-model="computedModelValue" :disabled="disabled" :clearable="clearable"
155
- @change="handleChange" />
156
- </template>
157
-
158
- <!-- 富文本编辑器 -->
159
- <template v-else-if="type === 'rich-text'">
160
- <ebiz-rich-text-editor v-model="computedModelValue" :placeholder="placeholder || '请输入内容'"
161
- :disabled="disabled" :readonly="readonly" :height="editorHeight"
162
- :toolbar-config="editorToolbarConfig" :editor-config="editorConfig" :image-path="editorImagePath"
163
- :video-path="editorVideoPath" :max-length="maxLength" @change="handleChange" />
164
- </template>
165
-
166
- <!-- 子表格 -->
167
- <template v-else-if="type === 'table'">
168
- <div class="ebiz-form-table">
169
- <t-space direction="vertical" style="width: 100%;">
170
- <!-- 添加按钮 -->
171
- <div>
172
- <t-button theme="primary" variant="outline" @click="addTableRow" :disabled="disabled">
173
- <template #icon>
174
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
175
- <path d="M8 1V15M1 8H15" stroke="currentColor" stroke-width="1.5"
176
- stroke-linecap="round" />
177
- </svg>
178
- </template>
179
- {{ addButtonText }}
180
- </t-button>
181
- </div>
182
-
183
- <!-- 表格 -->
184
- <t-table :data="computedModelValue" :columns="computedTableColumns"
185
- row-key="__id" size="small" bordered :empty="'暂无数据'">
186
- <!-- 序号列 -->
187
- <template #index="{ rowIndex }">
188
- {{ rowIndex + 1 }}
189
- </template>
190
-
191
- <!-- 动态渲染每个数据列 -->
192
- <template v-for="col in tableColumns" :key="col.key" #[col.key]="{ row, rowIndex }">
193
- <!-- 输入框 -->
194
- <t-input v-if="!col.type || col.type === 'input'" v-model="row[col.key]"
195
- :disabled="disabled" :placeholder="col.placeholder || `请输入${col.name}`"
196
- :clearable="col.clearable !== false" :maxlength="col.maxlength"
197
- @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
198
-
199
- <!-- 数字输入框 -->
200
- <t-input-number v-else-if="col.type === 'number'" v-model="row[col.key]"
201
- :disabled="disabled" :placeholder="col.placeholder || `请输入${col.name}`"
202
- :min="col.min" :max="col.max" :step="col.step || 1"
203
- :decimal-places="col.decimalPlaces"
204
- @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
205
-
206
- <!-- 选择器 -->
207
- <t-select v-else-if="col.type === 'select' && col.isRemote !== true" v-model="row[col.key]" :disabled="disabled"
208
- :placeholder="col.placeholder || `请选择${col.name}`" :options="col.options || []"
209
- :clearable="col.clearable !== false" :filterable="col.filterable"
210
- :multiple="col.multiple"
211
- @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
212
-
213
- <!-- 远程搜索 -->
214
- <ebiz-remote-select v-else-if="col.type === 'select' && col.isRemote === true" v-model="row[col.key]" :queryParams="col.queryParams"
215
- :placeholder="col.placeholder || `请选择${col.name}`" :disabled="disabled" :clearable="col.clearable !== false"
216
- :filterable="true" :multiple="col.multiple" :apiConfig="col.remoteApiConfig"
217
- @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
218
-
219
- <!-- 日期选择器 -->
220
- <t-date-picker v-else-if="col.type === 'date'" v-model="row[col.key]"
221
- :disabled="disabled" :placeholder="col.placeholder || `请选择${col.name}`"
222
- :format="col.format || 'YYYY-MM-DD'" :clearable="col.clearable !== false"
223
- @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
224
-
225
- <!-- 日期时间选择器 -->
226
- <t-date-picker v-else-if="col.type === 'datetime'" v-model="row[col.key]"
227
- :disabled="disabled" :placeholder="col.placeholder || `请选择${col.name}`"
228
- :format="col.format || 'YYYY-MM-DD HH:mm:ss'" enable-time-picker
229
- :clearable="col.clearable !== false"
230
- @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
231
-
232
- <!-- 开关 -->
233
- <t-switch v-else-if="col.type === 'switch'" v-model="row[col.key]" :disabled="disabled"
234
- @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
235
-
236
- <!-- 文本域 -->
237
- <t-textarea v-else-if="col.type === 'textarea'" v-model="row[col.key]"
238
- :disabled="disabled" :placeholder="col.placeholder || `请输入${col.name}`"
239
- :autosize="col.autosize || { minRows: 2, maxRows: 4 }" :maxlength="col.maxlength"
240
- @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
241
-
242
- <!-- 单选框组 -->
243
- <t-radio-group v-else-if="col.type === 'radio'" v-model="row[col.key]"
244
- :disabled="disabled" :options="col.options || []"
245
- @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
246
-
247
- <!-- 复选框组 -->
248
- <t-checkbox-group v-else-if="col.type === 'checkbox'" v-model="row[col.key]"
249
- :disabled="disabled" :options="col.options || []" :max="col.max"
250
- @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
251
-
252
- <ebiz-employee-selector v-else-if="col.type === 'employee'" v-model="row[col.key]"
253
- :placeholder="col.placeholder || '请选择员工'" :disabled="disabled"
254
- :clearable="col.clearable !== false" :single="col.single" :defaultTab="col.defaultTab"
255
- :maxCount="col.maxCount" @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
256
-
257
- <!-- 默认文本显示 -->
258
- <span v-else>{{ row[col.key] || '' }}</span>
259
- </template>
260
-
261
- <!-- 操作列 -->
262
- <template v-if="showTableActions" #actions="{ rowIndex }">
263
- <t-button theme="danger" variant="text" size="small" :disabled="disabled"
264
- @click="removeTableRow(rowIndex)">
265
- 删除
266
- </t-button>
267
- </template>
268
- </t-table>
269
- </t-space>
270
- </div>
271
- </template>
272
-
273
- <!-- 字典选择器 -->
274
- <template v-else-if="type === 'dictionarySelect'">
275
- <ebiz-dictionary-select v-model="computedModelValue" :dictionary-key="dictionaryKey" :queryParams="queryParams"
276
- :placeholder="placeholder || '请选择'" :disabled="disabled" :clearable="clearable"
277
- :filterable="true" :multiple="multiple" :loading="loading"
278
- @change="handleChange" />
279
- </template>
280
-
281
- <!-- 默认插槽,用于自定义控件 -->
282
- <template v-else>
283
- </template>
284
- </slot>
285
-
286
- </t-form-item>
287
- </template>
288
-
289
- <script>
290
- /**
291
- * @displayName PC端表单项
292
- * @description PC端表单项组件,基于TDesign FormItem封装,支持多种控件类型,包括子表格功能
293
- * @category 表单组件
294
- * @name EbizSFormItem
295
- */
296
- export default {
297
- name: "EbizSFormItem"
298
- }
299
- </script>
300
-
301
- <script setup>
302
- import { defineProps, defineEmits, computed, watch, onMounted } from 'vue';
303
- import {
304
- FormItem as TFormItem,
305
- Input as TInput,
306
- InputNumber as TInputNumber,
307
- Textarea as TTextarea,
308
- Select as TSelect,
309
- DatePicker as TDatePicker,
310
- TimePicker as TTimePicker,
311
- RadioGroup as TRadioGroup,
312
- CheckboxGroup as TCheckboxGroup,
313
- Switch as TSwitch,
314
- Slider as TSlider,
315
- Cascader as TCascader,
316
- TreeSelect as TTreeSelect,
317
- ColorPicker as TColorPicker,
318
- Table as TTable,
319
- Button as TButton,
320
- Space as TSpace,
321
- DateRangePicker as TDateRangePicker
322
- } from 'tdesign-vue-next';
323
- import EbizRemoteSelect from '../../EbizRemoteSelect.vue';
324
- import EbizEmployeeSelector from '../../EbizEmployeeSelector.vue';
325
- import EbizDepartmentSelector from '../../EbizDepartmentSelector.vue';
326
- import { EbizUpload } from '../../../index.js'
327
- import EbizRichTextEditor from '../../EbizRichTextEditor.vue';
328
- import EbizDictionarySelect from '../../EbizDictionarySelect.vue';
329
-
330
- const props = defineProps({
331
- /**
332
- * 组件类型
333
- * @options input|number|textarea|select|date|datetime|time|radio|checkbox|switch|slider|upload|video-upload|cascader|tree-select|color-picker|employee|dept|table
334
- */
335
- type: {
336
- type: String,
337
- default: undefined,
338
- validator: (val) => [
339
- 'input', 'number', 'textarea', 'select', 'date', 'datetime', 'time', 'radio',
340
- 'checkbox', 'switch', 'slider', 'upload', 'video-upload', 'cascader', 'tree-select',
341
- 'color-picker', 'employee', 'dept', 'table', 'rich-text'
342
- ].includes(val)
343
- },
344
-
345
- queryParams: {
346
- type: Object,
347
- default: () => ({})
348
- },
349
- /**
350
- * 绑定的值
351
- */
352
- modelValue: {
353
- type: [String, Number, Boolean, Array, Object],
354
- default: undefined
355
- },
356
- /**
357
- * 表单项标签
358
- */
359
- label: {
360
- type: String,
361
- default: ''
362
- },
363
- /**
364
- * 表单项字段名,唯一标识
365
- */
366
- name: {
367
- type: String,
368
- default: ''
369
- },
370
- /**
371
- * html元素原生属性for
372
- */
373
- forAttr: {
374
- type: String,
375
- default: undefined
376
- },
377
- /**
378
- * 表单项帮助文本
379
- */
380
- help: {
381
- type: [String, Function],
382
- default: ''
383
- },
384
- /**
385
- * 是否禁用
386
- */
387
- disabled: {
388
- type: Boolean,
389
- default: false
390
- },
391
- /**
392
- * 是否只读
393
- */
394
- readonly: {
395
- type: Boolean,
396
- default: false
397
- },
398
- /**
399
- * 表单项规则
400
- */
401
- rules: {
402
- type: [Array, Object],
403
- default: undefined
404
- },
405
- /**
406
- * 自定义样式
407
- */
408
- customStyle: {
409
- type: [String, Object],
410
- default: ''
411
- },
412
- /**
413
- * 自定义类名
414
- */
415
- className: {
416
- type: String,
417
- default: ''
418
- },
419
- /**
420
- * 表单项标签宽度
421
- */
422
- labelWidth: {
423
- type: [String, Number],
424
- default: undefined
425
- },
426
- /**
427
- * 表单项标签对齐方式
428
- * @options left|right|top
429
- */
430
- labelAlign: {
431
- type: String,
432
- default: undefined,
433
- validator: (val) => ['left', 'right', 'top'].includes(val)
434
- },
435
- /**
436
- * 表单项状态
437
- * @options default|success|warning|error
438
- */
439
- status: {
440
- type: String,
441
- default: undefined,
442
- validator: (val) => ['default', 'success', 'warning', 'error'].includes(val)
443
- },
444
- /**
445
- * 占位文本
446
- */
447
- placeholder: {
448
- type: String,
449
- default: ''
450
- },
451
- /**
452
- * 是否可清空
453
- */
454
- clearable: {
455
- type: Boolean,
456
- default: true
457
- },
458
- /**
459
- * 最大长度
460
- */
461
- maxLength: {
462
- type: Number,
463
- default: undefined
464
- },
465
- /**
466
- * 最大字符数
467
- */
468
- maxCharacter: {
469
- type: Number,
470
- default: undefined
471
- },
472
- /**
473
- * 是否显示字数限制
474
- */
475
- showLimitNumber: {
476
- type: Boolean,
477
- default: false
478
- },
479
- /**
480
- * 选项数据,用于select, radio, checkbox等
481
- */
482
- options: {
483
- type: Array,
484
- default: () => []
485
- },
486
- /**
487
- * 树形数据,用于tree-select
488
- */
489
- treeData: {
490
- type: Array,
491
- default: undefined
492
- },
493
- /**
494
- * 是否支持过滤
495
- */
496
- filterable: {
497
- type: Boolean,
498
- default: true
499
- },
500
- /**
501
- * 是否支持多选
502
- */
503
- multiple: {
504
- type: Boolean,
505
- default: false
506
- },
507
- /**
508
- * 加载状态
509
- */
510
- loading: {
511
- type: Boolean,
512
- default: false
513
- },
514
- /**
515
- * 日期格式
516
- */
517
- dateFormat: {
518
- type: String,
519
- default: 'YYYY-MM-DD'
520
- },
521
- /**
522
- * 日期选择器模式
523
- * @options date|week|month|quarter|year
524
- */
525
- dateMode: {
526
- type: String,
527
- default: 'date',
528
- validator: (val) => ['date', 'week', 'month', 'quarter', 'year'].includes(val)
529
- },
530
- /**
531
- * 时间格式
532
- */
533
- timeFormat: {
534
- type: String,
535
- default: 'HH:mm:ss'
536
- },
537
- /**
538
- * 最大值
539
- */
540
- max: {
541
- type: Number,
542
- default: undefined
543
- },
544
- /**
545
- * 最小值
546
- */
547
- min: {
548
- type: Number,
549
- default: undefined
550
- },
551
- /**
552
- * 步长
553
- */
554
- step: {
555
- type: Number,
556
- default: 1
557
- },
558
- /**
559
- * 格式化函数
560
- */
561
- format: {
562
- type: Function,
563
- default: undefined
564
- },
565
- /**
566
- * 文本域自适应高度
567
- */
568
- autosize: {
569
- type: [Boolean, Object],
570
- default: false
571
- },
572
- /**
573
- * 滑块是否为范围选择
574
- */
575
- range: {
576
- type: Boolean,
577
- default: false
578
- },
579
- /**
580
- * 滑块刻度标记
581
- */
582
- marks: {
583
- type: Object,
584
- default: () => ({})
585
- },
586
- /**
587
- * 上传接受的文件类型
588
- */
589
- accept: {
590
- type: String,
591
- default: ''
592
- },
593
- /**
594
- * 上传接口地址
595
- */
596
- action: {
597
- type: String,
598
- default: ''
599
- },
600
- /**
601
- * 上传请求头
602
- */
603
- headers: {
604
- type: Object,
605
- default: () => ({})
606
- },
607
- /**
608
- * 上传是否携带cookie
609
- */
610
- withCredentials: {
611
- type: Boolean,
612
- default: false
613
- },
614
- /**
615
- * 上传前的钩子函数
616
- */
617
- beforeUpload: {
618
- type: Function,
619
- default: undefined
620
- },
621
- /**
622
- * 上传响应格式化函数
623
- */
624
- formatResponse: {
625
- type: Function,
626
- default: undefined
627
- },
628
- /**
629
- * 上传组件主题
630
- * @options file|image|image-flow
631
- */
632
- uploadTheme: {
633
- type: String,
634
- default: 'file',
635
- validator: (val) => ['file', 'image', 'image-flow'].includes(val)
636
- },
637
- /**
638
- * 是否开启远程搜索
639
- */
640
- isRemote: {
641
- type: Boolean,
642
- default: false
643
- },
644
- /**
645
- * 远程搜索配置
646
- */
647
- remoteApiConfig: {
648
- type: Object,
649
- default: () => ({
650
- url: '',
651
- method: 'GET',
652
- params: {},
653
- data: {},
654
- headers: {},
655
- transformResponse: null
656
- })
657
- },
658
- /**
659
- * 员工选择器是否单选
660
- */
661
- single: {
662
- type: Boolean,
663
- default: true
664
- },
665
- /**
666
- * 员工选择器默认标签页
667
- * @options organization|recent
668
- */
669
- defaultTab: {
670
- type: String,
671
- default: 'organization',
672
- validator: (val) => ['organization', 'recent'].includes(val)
673
- },
674
- /**
675
- * 员工选择器最大选择数量
676
- */
677
- maxCount: {
678
- type: Number,
679
- default: undefined
680
- },
681
- /**
682
- * 部门选择器API地址
683
- */
684
- apiUrl: {
685
- type: String,
686
- default: '/api/departments'
687
- },
688
- /**
689
- * 部门选择器所属公司ID
690
- */
691
- companyId: {
692
- type: [String, Number],
693
- default: undefined
694
- },
695
- /**
696
- * 子表格列配置,用于table类型
697
- * 示例:[
698
- * {name:'名称', key:'name', type:'input', width:150},
699
- * {name:'类型', key:'type', type:'select', options:[{label:'选项1', value:'1'}]},
700
- * {name:'数量', key:'num', type:'number', min:0, max:999}
701
- * ]
702
- */
703
- tableColumns: {
704
- type: Array,
705
- default: () => []
706
- },
707
- /**
708
- * 子表格是否显示操作列
709
- */
710
- showTableActions: {
711
- type: Boolean,
712
- default: true
713
- },
714
- /**
715
- * 子表格新增按钮文本
716
- */
717
- addButtonText: {
718
- type: String,
719
- default: '添加数据'
720
- },
721
- /**
722
- * 富文本编辑器高度
723
- */
724
- editorHeight: {
725
- type: [String, Number],
726
- default: 300
727
- },
728
- /**
729
- * 富文本编辑器工具栏配置
730
- */
731
- editorToolbarConfig: {
732
- type: Object,
733
- default: () => ({})
734
- },
735
- /**
736
- * 富文本编辑器配置
737
- */
738
- editorConfig: {
739
- type: Object,
740
- default: () => ({})
741
- },
742
- /**
743
- * 富文本图片上传路径
744
- */
745
- editorImagePath: {
746
- type: String,
747
- default: 'editor'
748
- },
749
- /**
750
- * 富文本视频上传路径
751
- */
752
- editorVideoPath: {
753
- type: String,
754
- default: 'video'
755
- },
756
- /**
757
- * 字典类型
758
- */
759
- dictionaryKey: {
760
- type: String,
761
- default: ''
762
- }
763
- });
764
-
765
- const emit = defineEmits([
766
- 'update:modelValue',
767
- 'change',
768
- 'validate'
769
- ]);
770
-
771
- // 计算属性处理v-model双向绑定
772
- const computedModelValue = computed({
773
- get: () => props.modelValue,
774
- set: (val) => {
775
- emit('update:modelValue', val);
776
- }
777
- });
778
-
779
- // 表格列配置计算属性
780
- const computedTableColumns = computed(() => {
781
- if (props.type !== 'table' || !props.tableColumns?.length) return [];
782
-
783
- const columns = [];
784
-
785
- // 添加序号列
786
- columns.push({
787
- colKey: 'index',
788
- title: '序号',
789
- width: 80,
790
- align: 'center'
791
- });
792
-
793
- // 添加用户配置的列
794
- const userColumns = props.tableColumns.map(col => ({
795
- colKey: col.key,
796
- title: col.name,
797
- width: col.width || undefined,
798
- align: col.align || 'left'
799
- }));
800
-
801
- columns.push(...userColumns);
802
-
803
- // 添加操作列
804
- if (props.showTableActions) {
805
- columns.push({
806
- colKey: 'actions',
807
- title: '操作',
808
- width: 80,
809
- align: 'center'
810
- });
811
- }
812
-
813
- return columns;
814
- });
815
-
816
- /**
817
- * 值变更事件
818
- * @param {*} val 新值
819
- */
820
- const handleChange = (val) => {
821
- emit('change', val);
822
- };
823
-
824
- /**
825
- * 验证事件
826
- * @param {Object} validateResult 验证结果
827
- */
828
- const handleValidate = (validateResult) => {
829
- emit('validate', validateResult);
830
- };
831
-
832
- /**
833
- * 清除视频上传
834
- */
835
- const clearVideoUpload = () => {
836
- computedModelValue.value = null;
837
- emit('change', null);
838
- };
839
-
840
- /**
841
- * 添加表格行
842
- */
843
- const addTableRow = () => {
844
- const currentData = computedModelValue.value || [];
845
- const newRow = {};
846
-
847
- // 根据表格列配置初始化新行数据
848
- props.tableColumns.forEach(col => {
849
- // 根据列类型设置默认值
850
- switch (col.type) {
851
- case 'number':
852
- newRow[col.key] = col.defaultValue || 0;
853
- break;
854
- case 'switch':
855
- newRow[col.key] = col.defaultValue || false;
856
- break;
857
- case 'checkbox':
858
- newRow[col.key] = col.defaultValue || [];
859
- break;
860
- case 'select':
861
- if (col.multiple) {
862
- newRow[col.key] = col.defaultValue || [];
863
- } else {
864
- newRow[col.key] = col.defaultValue || '';
865
- }
866
- break;
867
- default:
868
- newRow[col.key] = col.defaultValue || '';
869
- break;
870
- }
871
- });
872
-
873
- const newData = [...currentData, newRow];
874
- // 只更新computedModelValue,让watch自动处理change事件
875
- computedModelValue.value = JSON.parse(JSON.stringify(newData));
876
- };
877
-
878
- /**
879
- * 删除表格行
880
- * @param {number} rowIndex 行索引
881
- */
882
- const removeTableRow = (rowIndex) => {
883
- if (props.type !== 'table') return;
884
-
885
- const currentData = [...(computedModelValue.value || [])];
886
- currentData.splice(rowIndex, 1);
887
-
888
- // 只更新computedModelValue,让watch自动处理change事件
889
- computedModelValue.value = currentData;
890
- };
891
-
892
- /**
893
- * 更新表格单元格数据
894
- * @param {number} rowIndex 行索引
895
- * @param {string} colKey 列键名
896
- * @param {*} value 新值
897
- */
898
- const updateTableCell = (rowIndex, colKey, value) => {
899
- if (props.type !== 'table') return;
900
-
901
- const currentData = [...(computedModelValue.value || [])];
902
- if (currentData[rowIndex]) {
903
- currentData[rowIndex] = {
904
- ...currentData[rowIndex],
905
- [colKey]: value
906
- };
907
-
908
- // 只更新computedModelValue,让watch自动处理change事件
909
- computedModelValue.value = currentData;
910
- }
911
- };
912
-
913
- // 监听modelValue变化,触发change事件(仅在table类型时)
914
- watch(
915
- () => props.modelValue,
916
- (newVal, oldVal) => {
917
- if (props.type === 'table' && newVal !== oldVal) {
918
- // 只在数据真正变化时触发change事件
919
- const cleanData = Array.isArray(newVal) ? newVal : [];
920
- handleChange(cleanData);
921
- }
922
- },
923
- { deep: true }
924
- );
925
-
926
- // 组件挂载时初始化文件列表
927
- onMounted(() => {
928
- });
929
- </script>
930
-
931
- <style scoped>
932
- .custom-control {
933
- width: 100%;
934
- }
935
-
936
- .ebiz-form-table {
937
- width: 100%;
938
- }
939
-
940
- .ebiz-form-table :deep(.t-table) {
941
- border: 1px solid var(--td-border-level-1-color);
942
- border-radius: var(--td-radius-default);
943
- }
944
-
945
- .ebiz-form-table :deep(.t-table td) {
946
- padding: 8px 12px;
947
- }
948
-
949
- .ebiz-form-table :deep(.t-table th) {
950
- background-color: var(--td-bg-color-secondarycontainer);
951
- font-weight: 500;
952
- }
953
-
954
- .ebiz-form-table :deep(.t-input) {
955
- border: none;
956
- box-shadow: none;
957
- }
958
-
959
- .ebiz-form-table :deep(.t-input:focus-within) {
960
- border: 1px solid var(--td-brand-color);
961
- box-shadow: 0 0 0 2px var(--td-brand-color-focus);
962
- }
963
-
964
- .ebiz-form-table :deep(.t-input-number) {
965
- border: none;
966
- box-shadow: none;
967
- }
968
-
969
- .ebiz-form-table :deep(.t-input-number:focus-within) {
970
- border: 1px solid var(--td-brand-color);
971
- box-shadow: 0 0 0 2px var(--td-brand-color-focus);
972
- }
973
-
974
- .ebiz-form-table :deep(.t-select) {
975
- border: none;
976
- box-shadow: none;
977
- }
978
-
979
- .ebiz-form-table :deep(.t-select:focus-within) {
980
- border: 1px solid var(--td-brand-color);
981
- box-shadow: 0 0 0 2px var(--td-brand-color-focus);
982
- }
983
-
984
- .ebiz-form-table :deep(.t-date-picker) {
985
- border: none;
986
- box-shadow: none;
987
- }
988
-
989
- .ebiz-form-table :deep(.t-date-picker:focus-within) {
990
- border: 1px solid var(--td-brand-color);
991
- box-shadow: 0 0 0 2px var(--td-brand-color-focus);
992
- }
993
-
994
- .ebiz-form-table :deep(.t-textarea) {
995
- border: none;
996
- box-shadow: none;
997
- }
998
-
999
- .ebiz-form-table :deep(.t-textarea:focus-within) {
1000
- border: 1px solid var(--td-brand-color);
1001
- box-shadow: 0 0 0 2px var(--td-brand-color-focus);
1002
- }
1003
-
1004
- .ebiz-form-table :deep(.t-switch) {
1005
- justify-content: center;
1006
- }
1007
-
1008
- .ebiz-form-table :deep(.t-radio-group),
1009
- .ebiz-form-table :deep(.t-checkbox-group) {
1010
- justify-content: center;
1011
- }
1
+ <template>
2
+ <t-form-item :class="['ebiz-s-form-item', className]" :for="forAttr" :help="help" :label="label" :name="name"
3
+ :style="customStyle" :rules="rules" :label-width="labelWidth" :label-align="labelAlign" :status="status"
4
+ @validate="handleValidate" style="margin-bottom: 10px;">
5
+
6
+ <slot name="default">
7
+ <!-- 员工选择 -->
8
+ <template v-if="type === 'employee'">
9
+ <ebiz-employee-selector v-model="computedModelValue" :placeholder="placeholder || '请选择员工'"
10
+ :disabled="disabled" :clearable="clearable" :single="single" :defaultTab="defaultTab"
11
+ :maxCount="maxCount" @change="handleChange" />
12
+ </template>
13
+
14
+ <!-- 部门选择 -->
15
+ <template v-if="type === 'dept'">
16
+ <ebiz-department-selector v-model="computedModelValue" :placeholder="placeholder || '请选择部门'"
17
+ :disabled="disabled" :clearable="clearable" :filterable="filterable" :apiUrl="apiUrl"
18
+ :companyId="companyId" :multiple="multiple" @change="handleChange" />
19
+ </template>
20
+
21
+ <!-- 输入框 -->
22
+ <template v-if="type === 'input'">
23
+ <t-input v-model="computedModelValue" :placeholder="placeholder || '请输入'" :disabled="disabled"
24
+ :readonly="readonly" :clearable="clearable" :max-length="maxLength" :max-character="maxCharacter"
25
+ :show-limit-number="showLimitNumber" @change="handleChange" />
26
+ </template>
27
+
28
+ <!-- 数字输入框 -->
29
+ <template v-else-if="type === 'number'">
30
+ <t-input-number v-model="computedModelValue" :placeholder="placeholder || '请输入'" :disabled="disabled"
31
+ :readonly="readonly" :max="max" :min="min" :step="step" :format="format" @change="handleChange" />
32
+ </template>
33
+
34
+ <!-- 文本域 -->
35
+ <template v-else-if="type === 'textarea'">
36
+ <t-textarea v-model="computedModelValue" :placeholder="placeholder || '请输入'" :disabled="disabled"
37
+ :readonly="readonly" :autosize="autosize" :maxlength="maxLength" :max-character="maxCharacter"
38
+ :show-limit-number="showLimitNumber" @change="handleChange" />
39
+ </template>
40
+
41
+ <!-- 选择器 -->
42
+ <template v-else-if="type === 'select'">
43
+ <template v-if="isRemote">
44
+ <ebiz-remote-select v-model="computedModelValue" :queryParams="queryParams"
45
+ :placeholder="placeholder || '请选择'" :disabled="disabled" :clearable="clearable"
46
+ :filterable="true" :multiple="multiple" :loading="loading" :apiConfig="remoteApiConfig"
47
+ @change="handleChange" />
48
+ </template>
49
+ <template v-else>
50
+ <t-select v-model="computedModelValue" :options="options" :placeholder="placeholder || '请选择'"
51
+ :disabled="disabled" :clearable="clearable" :filterable="filterable" :multiple="multiple"
52
+ :loading="loading" @change="handleChange" />
53
+ </template>
54
+ </template>
55
+
56
+ <!-- 日期选择器 -->
57
+ <template v-else-if="type === 'datetime'">
58
+ <t-date-picker v-model="computedModelValue" :placeholder="placeholder || '请选择日期时间'" :disabled="disabled"
59
+ :clearable="clearable" enable-time-picker format="YYYY-MM-DD HH:mm:ss" @change="handleChange" />
60
+ </template>
61
+
62
+
63
+ <!-- 日期选择器 -->
64
+ <template v-else-if="type === 'date'">
65
+ <t-date-picker v-model="computedModelValue" :placeholder="placeholder || '请选择日期'" :disabled="disabled"
66
+ :clearable="clearable" :format="dateFormat || 'YYYY-MM-DD'" :mode="dateMode || 'date'"
67
+ @change="handleChange" />
68
+ </template>
69
+
70
+ <!-- 时间选择器 -->
71
+ <template v-else-if="type === 'time'">
72
+ <t-time-picker v-model="computedModelValue" :placeholder="placeholder || '请选择时间'" :disabled="disabled"
73
+ :clearable="clearable" :format="timeFormat || 'HH:mm:ss'" @change="handleChange" />
74
+ </template>
75
+
76
+ <!-- 范围日期选择器 -->
77
+ <template v-else-if="type === 'date-range'">
78
+ <t-date-range-picker v-model="computedModelValue" :placeholder="placeholder || '请选择日期范围'" :disabled="disabled"
79
+ :clearable="clearable" :format="dateFormat || 'YYYY-MM-DD'" :mode="dateMode || 'date'" @change="handleChange" />
80
+ </template>
81
+
82
+ <!-- 范围日期时间选择器 -->
83
+ <template v-else-if="type === 'datetime-range'">
84
+ <t-date-range-picker v-model="computedModelValue" :placeholder="placeholder || '请选择日期时间范围'" :disabled="disabled"
85
+ :clearable="clearable" format="YYYY-MM-DD HH:mm:ss" :mode="dateMode || 'date'" enable-time-picker @change="handleChange" />
86
+ </template>
87
+
88
+ <!-- 单选框组 -->
89
+ <template v-else-if="type === 'radio'">
90
+ <t-radio-group v-model="computedModelValue" :disabled="disabled" :options="options"
91
+ @change="handleChange" />
92
+ </template>
93
+
94
+ <!-- 复选框组 -->
95
+ <template v-else-if="type === 'checkbox'">
96
+ <t-checkbox-group v-model="computedModelValue" :disabled="disabled" :options="options" :max="max"
97
+ @change="handleChange" />
98
+ </template>
99
+
100
+ <!-- 开关 -->
101
+ <template v-else-if="type === 'switch'">
102
+ <t-switch v-model="computedModelValue" :disabled="disabled" @change="handleChange" />
103
+ </template>
104
+
105
+ <!-- 滑块 -->
106
+ <template v-else-if="type === 'slider'">
107
+ <t-slider v-model="computedModelValue" :disabled="disabled" :max="max" :min="min" :step="step"
108
+ :range="range" :marks="marks" @change="handleChange" />
109
+ </template>
110
+
111
+ <!-- 上传 -->
112
+ <template v-else-if="type === 'upload'">
113
+ <ebiz-upload v-model="computedModelValue" :disabled="disabled" :accept="accept" :multiple="multiple"
114
+ :max="max" :theme="uploadTheme || 'file'" @change="handleChange" />
115
+ </template>
116
+
117
+ <!-- 视频上传 -->
118
+ <template v-else-if="type === 'video-upload'">
119
+ <div>
120
+ <ebiz-upload v-if="!computedModelValue" v-model="computedModelValue" :disabled="disabled"
121
+ :accept="accept || 'video/*'" :multiple="false" :max="1" :theme="'file'" :action="action"
122
+ :headers="headers" :withCredentials="withCredentials" :beforeUpload="beforeUpload"
123
+ :formatResponse="formatResponse" :placeholder="placeholder || '请上传视频'" :sizeLimit="sizeLimit"
124
+ :tips="tips || '只能上传一个视频文件'" @change="handleChange" />
125
+ <div v-else style="display: flex; position: relative;">
126
+ <video :src="computedModelValue" controls autoplay muted width="400"></video>
127
+ <div style="position: absolute; top: 5px; right: 5px;">
128
+ <t-button theme="danger" @click="clearVideoUpload" size="small" :disabled="disabled">
129
+ <template #icon>
130
+ <t-icon name="delete"></t-icon>
131
+ </template>
132
+ </t-button>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ </template>
137
+
138
+ <!-- 级联选择器 -->
139
+ <template v-else-if="type === 'cascader'">
140
+ <t-cascader v-model="computedModelValue" :disabled="disabled" :options="options"
141
+ :placeholder="placeholder || '请选择'" :clearable="clearable" :filterable="filterable"
142
+ :multiple="multiple" @change="handleChange" />
143
+ </template>
144
+
145
+ <!-- 树形选择器 -->
146
+ <template v-else-if="type === 'tree-select'">
147
+ <t-tree-select v-model="computedModelValue" :disabled="disabled" :data="treeData || options"
148
+ :placeholder="placeholder || '请选择'" :clearable="clearable" :filterable="filterable"
149
+ :multiple="multiple" @change="handleChange" />
150
+ </template>
151
+
152
+ <!-- 颜色选择器 -->
153
+ <template v-else-if="type === 'color-picker'">
154
+ <t-color-picker v-model="computedModelValue" :disabled="disabled" :clearable="clearable"
155
+ @change="handleChange" />
156
+ </template>
157
+
158
+ <!-- 富文本编辑器 -->
159
+ <template v-else-if="type === 'rich-text'">
160
+ <ebiz-rich-text-editor v-model="computedModelValue" :placeholder="placeholder || '请输入内容'"
161
+ :disabled="disabled" :readonly="readonly" :height="editorHeight"
162
+ :toolbar-config="editorToolbarConfig" :editor-config="editorConfig" :image-path="editorImagePath"
163
+ :video-path="editorVideoPath" :max-length="maxLength" @change="handleChange" />
164
+ </template>
165
+
166
+ <!-- 子表格 -->
167
+ <template v-else-if="type === 'table'">
168
+ <div class="ebiz-form-table">
169
+ <t-space direction="vertical" style="width: 100%;">
170
+ <!-- 添加按钮 -->
171
+ <div>
172
+ <t-button theme="primary" variant="outline" @click="addTableRow" :disabled="disabled">
173
+ <template #icon>
174
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
175
+ <path d="M8 1V15M1 8H15" stroke="currentColor" stroke-width="1.5"
176
+ stroke-linecap="round" />
177
+ </svg>
178
+ </template>
179
+ {{ addButtonText }}
180
+ </t-button>
181
+ </div>
182
+
183
+ <!-- 表格 -->
184
+ <t-table :data="computedModelValue" :columns="computedTableColumns"
185
+ row-key="__id" size="small" bordered :empty="'暂无数据'">
186
+ <!-- 序号列 -->
187
+ <template #index="{ rowIndex }">
188
+ {{ rowIndex + 1 }}
189
+ </template>
190
+
191
+ <!-- 动态渲染每个数据列 -->
192
+ <template v-for="col in tableColumns" :key="col.key" #[col.key]="{ row, rowIndex }">
193
+ <!-- 输入框 -->
194
+ <t-input v-if="!col.type || col.type === 'input'" v-model="row[col.key]"
195
+ :disabled="disabled" :placeholder="col.placeholder || `请输入${col.name}`"
196
+ :clearable="col.clearable !== false" :maxlength="col.maxlength"
197
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
198
+
199
+ <!-- 数字输入框 -->
200
+ <t-input-number v-else-if="col.type === 'number'" v-model="row[col.key]"
201
+ :disabled="disabled" :placeholder="col.placeholder || `请输入${col.name}`"
202
+ :min="col.min" :max="col.max" :step="col.step || 1"
203
+ :decimal-places="col.decimalPlaces"
204
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
205
+
206
+ <!-- 选择器 -->
207
+ <t-select v-else-if="col.type === 'select' && col.isRemote !== true" v-model="row[col.key]" :disabled="disabled"
208
+ :placeholder="col.placeholder || `请选择${col.name}`" :options="col.options || []"
209
+ :clearable="col.clearable !== false" :filterable="col.filterable"
210
+ :multiple="col.multiple"
211
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
212
+
213
+ <!-- 远程搜索 -->
214
+ <ebiz-remote-select v-else-if="col.type === 'select' && col.isRemote === true" v-model="row[col.key]" :queryParams="col.queryParams"
215
+ :placeholder="col.placeholder || `请选择${col.name}`" :disabled="disabled" :clearable="col.clearable !== false"
216
+ :filterable="true" :multiple="col.multiple" :apiConfig="col.remoteApiConfig"
217
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
218
+
219
+ <!-- 日期选择器 -->
220
+ <t-date-picker v-else-if="col.type === 'date'" v-model="row[col.key]"
221
+ :disabled="disabled" :placeholder="col.placeholder || `请选择${col.name}`"
222
+ :format="col.format || 'YYYY-MM-DD'" :clearable="col.clearable !== false"
223
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
224
+
225
+ <!-- 日期时间选择器 -->
226
+ <t-date-picker v-else-if="col.type === 'datetime'" v-model="row[col.key]"
227
+ :disabled="disabled" :placeholder="col.placeholder || `请选择${col.name}`"
228
+ :format="col.format || 'YYYY-MM-DD HH:mm:ss'" enable-time-picker
229
+ :clearable="col.clearable !== false"
230
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
231
+
232
+ <!-- 开关 -->
233
+ <t-switch v-else-if="col.type === 'switch'" v-model="row[col.key]" :disabled="disabled"
234
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
235
+
236
+ <!-- 文本域 -->
237
+ <t-textarea v-else-if="col.type === 'textarea'" v-model="row[col.key]"
238
+ :disabled="disabled" :placeholder="col.placeholder || `请输入${col.name}`"
239
+ :autosize="col.autosize || { minRows: 2, maxRows: 4 }" :maxlength="col.maxlength"
240
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
241
+
242
+ <!-- 单选框组 -->
243
+ <t-radio-group v-else-if="col.type === 'radio'" v-model="row[col.key]"
244
+ :disabled="disabled" :options="col.options || []"
245
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
246
+
247
+ <!-- 复选框组 -->
248
+ <t-checkbox-group v-else-if="col.type === 'checkbox'" v-model="row[col.key]"
249
+ :disabled="disabled" :options="col.options || []" :max="col.max"
250
+ @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
251
+
252
+ <ebiz-employee-selector v-else-if="col.type === 'employee'" v-model="row[col.key]"
253
+ :placeholder="col.placeholder || '请选择员工'" :disabled="disabled"
254
+ :clearable="col.clearable !== false" :single="col.single" :defaultTab="col.defaultTab"
255
+ :maxCount="col.maxCount" @update:model-value="(value) => updateTableCell(rowIndex, col.key, value)" />
256
+
257
+ <!-- 默认文本显示 -->
258
+ <span v-else>{{ row[col.key] || '' }}</span>
259
+ </template>
260
+
261
+ <!-- 操作列 -->
262
+ <template v-if="showTableActions" #actions="{ rowIndex }">
263
+ <t-button theme="danger" variant="text" size="small" :disabled="disabled"
264
+ @click="removeTableRow(rowIndex)">
265
+ 删除
266
+ </t-button>
267
+ </template>
268
+ </t-table>
269
+ </t-space>
270
+ </div>
271
+ </template>
272
+
273
+ <!-- 字典选择器 -->
274
+ <template v-else-if="type === 'dictionarySelect'">
275
+ <ebiz-dictionary-select v-model="computedModelValue" :dictionary-key="dictionaryKey" :queryParams="queryParams"
276
+ :placeholder="placeholder || '请选择'" :disabled="disabled" :clearable="clearable"
277
+ :filterable="true" :multiple="multiple" :loading="loading"
278
+ @change="handleChange" />
279
+ </template>
280
+
281
+ <!-- 默认插槽,用于自定义控件 -->
282
+ <template v-else>
283
+ </template>
284
+ </slot>
285
+
286
+ </t-form-item>
287
+ </template>
288
+
289
+ <script>
290
+ /**
291
+ * @displayName PC端表单项
292
+ * @description PC端表单项组件,基于TDesign FormItem封装,支持多种控件类型,包括子表格功能
293
+ * @category 表单组件
294
+ * @name EbizSFormItem
295
+ */
296
+ export default {
297
+ name: "EbizSFormItem"
298
+ }
299
+ </script>
300
+
301
+ <script setup>
302
+ import { defineProps, defineEmits, computed, watch, onMounted } from 'vue';
303
+ import {
304
+ FormItem as TFormItem,
305
+ Input as TInput,
306
+ InputNumber as TInputNumber,
307
+ Textarea as TTextarea,
308
+ Select as TSelect,
309
+ DatePicker as TDatePicker,
310
+ TimePicker as TTimePicker,
311
+ RadioGroup as TRadioGroup,
312
+ CheckboxGroup as TCheckboxGroup,
313
+ Switch as TSwitch,
314
+ Slider as TSlider,
315
+ Cascader as TCascader,
316
+ TreeSelect as TTreeSelect,
317
+ ColorPicker as TColorPicker,
318
+ Table as TTable,
319
+ Button as TButton,
320
+ Space as TSpace,
321
+ DateRangePicker as TDateRangePicker
322
+ } from 'tdesign-vue-next';
323
+ import EbizRemoteSelect from '../../EbizRemoteSelect.vue';
324
+ import EbizEmployeeSelector from '../../EbizEmployeeSelector.vue';
325
+ import EbizDepartmentSelector from '../../EbizDepartmentSelector.vue';
326
+ import { EbizUpload } from '../../../index.js'
327
+ import EbizRichTextEditor from '../../EbizRichTextEditor.vue';
328
+ import EbizDictionarySelect from '../../EbizDictionarySelect.vue';
329
+
330
+ const props = defineProps({
331
+ /**
332
+ * 组件类型
333
+ * @options input|number|textarea|select|date|datetime|time|radio|checkbox|switch|slider|upload|video-upload|cascader|tree-select|color-picker|employee|dept|table
334
+ */
335
+ type: {
336
+ type: String,
337
+ default: undefined,
338
+ validator: (val) => [
339
+ 'input', 'number', 'textarea', 'select', 'date', 'datetime', 'time', 'radio',
340
+ 'checkbox', 'switch', 'slider', 'upload', 'video-upload', 'cascader', 'tree-select',
341
+ 'color-picker', 'employee', 'dept', 'table', 'rich-text'
342
+ ].includes(val)
343
+ },
344
+
345
+ queryParams: {
346
+ type: Object,
347
+ default: () => ({})
348
+ },
349
+ /**
350
+ * 绑定的值
351
+ */
352
+ modelValue: {
353
+ type: [String, Number, Boolean, Array, Object],
354
+ default: undefined
355
+ },
356
+ /**
357
+ * 表单项标签
358
+ */
359
+ label: {
360
+ type: String,
361
+ default: ''
362
+ },
363
+ /**
364
+ * 表单项字段名,唯一标识
365
+ */
366
+ name: {
367
+ type: String,
368
+ default: ''
369
+ },
370
+ /**
371
+ * html元素原生属性for
372
+ */
373
+ forAttr: {
374
+ type: String,
375
+ default: undefined
376
+ },
377
+ /**
378
+ * 表单项帮助文本
379
+ */
380
+ help: {
381
+ type: [String, Function],
382
+ default: ''
383
+ },
384
+ /**
385
+ * 是否禁用
386
+ */
387
+ disabled: {
388
+ type: Boolean,
389
+ default: false
390
+ },
391
+ /**
392
+ * 是否只读
393
+ */
394
+ readonly: {
395
+ type: Boolean,
396
+ default: false
397
+ },
398
+ /**
399
+ * 表单项规则
400
+ */
401
+ rules: {
402
+ type: [Array, Object],
403
+ default: undefined
404
+ },
405
+ /**
406
+ * 自定义样式
407
+ */
408
+ customStyle: {
409
+ type: [String, Object],
410
+ default: ''
411
+ },
412
+ /**
413
+ * 自定义类名
414
+ */
415
+ className: {
416
+ type: String,
417
+ default: ''
418
+ },
419
+ /**
420
+ * 表单项标签宽度
421
+ */
422
+ labelWidth: {
423
+ type: [String, Number],
424
+ default: undefined
425
+ },
426
+ /**
427
+ * 表单项标签对齐方式
428
+ * @options left|right|top
429
+ */
430
+ labelAlign: {
431
+ type: String,
432
+ default: undefined,
433
+ validator: (val) => ['left', 'right', 'top'].includes(val)
434
+ },
435
+ /**
436
+ * 表单项状态
437
+ * @options default|success|warning|error
438
+ */
439
+ status: {
440
+ type: String,
441
+ default: undefined,
442
+ validator: (val) => ['default', 'success', 'warning', 'error'].includes(val)
443
+ },
444
+ /**
445
+ * 占位文本
446
+ */
447
+ placeholder: {
448
+ type: String,
449
+ default: ''
450
+ },
451
+ /**
452
+ * 是否可清空
453
+ */
454
+ clearable: {
455
+ type: Boolean,
456
+ default: true
457
+ },
458
+ /**
459
+ * 最大长度
460
+ */
461
+ maxLength: {
462
+ type: Number,
463
+ default: undefined
464
+ },
465
+ /**
466
+ * 最大字符数
467
+ */
468
+ maxCharacter: {
469
+ type: Number,
470
+ default: undefined
471
+ },
472
+ /**
473
+ * 是否显示字数限制
474
+ */
475
+ showLimitNumber: {
476
+ type: Boolean,
477
+ default: false
478
+ },
479
+ /**
480
+ * 选项数据,用于select, radio, checkbox等
481
+ */
482
+ options: {
483
+ type: Array,
484
+ default: () => []
485
+ },
486
+ /**
487
+ * 树形数据,用于tree-select
488
+ */
489
+ treeData: {
490
+ type: Array,
491
+ default: undefined
492
+ },
493
+ /**
494
+ * 是否支持过滤
495
+ */
496
+ filterable: {
497
+ type: Boolean,
498
+ default: true
499
+ },
500
+ /**
501
+ * 是否支持多选
502
+ */
503
+ multiple: {
504
+ type: Boolean,
505
+ default: false
506
+ },
507
+ /**
508
+ * 加载状态
509
+ */
510
+ loading: {
511
+ type: Boolean,
512
+ default: false
513
+ },
514
+ /**
515
+ * 日期格式
516
+ */
517
+ dateFormat: {
518
+ type: String,
519
+ default: 'YYYY-MM-DD'
520
+ },
521
+ /**
522
+ * 日期选择器模式
523
+ * @options date|week|month|quarter|year
524
+ */
525
+ dateMode: {
526
+ type: String,
527
+ default: 'date',
528
+ validator: (val) => ['date', 'week', 'month', 'quarter', 'year'].includes(val)
529
+ },
530
+ /**
531
+ * 时间格式
532
+ */
533
+ timeFormat: {
534
+ type: String,
535
+ default: 'HH:mm:ss'
536
+ },
537
+ /**
538
+ * 最大值
539
+ */
540
+ max: {
541
+ type: Number,
542
+ default: undefined
543
+ },
544
+ /**
545
+ * 最小值
546
+ */
547
+ min: {
548
+ type: Number,
549
+ default: undefined
550
+ },
551
+ /**
552
+ * 步长
553
+ */
554
+ step: {
555
+ type: Number,
556
+ default: 1
557
+ },
558
+ /**
559
+ * 格式化函数
560
+ */
561
+ format: {
562
+ type: Function,
563
+ default: undefined
564
+ },
565
+ /**
566
+ * 文本域自适应高度
567
+ */
568
+ autosize: {
569
+ type: [Boolean, Object],
570
+ default: false
571
+ },
572
+ /**
573
+ * 滑块是否为范围选择
574
+ */
575
+ range: {
576
+ type: Boolean,
577
+ default: false
578
+ },
579
+ /**
580
+ * 滑块刻度标记
581
+ */
582
+ marks: {
583
+ type: Object,
584
+ default: () => ({})
585
+ },
586
+ /**
587
+ * 上传接受的文件类型
588
+ */
589
+ accept: {
590
+ type: String,
591
+ default: ''
592
+ },
593
+ /**
594
+ * 上传接口地址
595
+ */
596
+ action: {
597
+ type: String,
598
+ default: ''
599
+ },
600
+ /**
601
+ * 上传请求头
602
+ */
603
+ headers: {
604
+ type: Object,
605
+ default: () => ({})
606
+ },
607
+ /**
608
+ * 上传是否携带cookie
609
+ */
610
+ withCredentials: {
611
+ type: Boolean,
612
+ default: false
613
+ },
614
+ /**
615
+ * 上传前的钩子函数
616
+ */
617
+ beforeUpload: {
618
+ type: Function,
619
+ default: undefined
620
+ },
621
+ /**
622
+ * 上传响应格式化函数
623
+ */
624
+ formatResponse: {
625
+ type: Function,
626
+ default: undefined
627
+ },
628
+ /**
629
+ * 上传组件主题
630
+ * @options file|image|image-flow
631
+ */
632
+ uploadTheme: {
633
+ type: String,
634
+ default: 'file',
635
+ validator: (val) => ['file', 'image', 'image-flow'].includes(val)
636
+ },
637
+ /**
638
+ * 是否开启远程搜索
639
+ */
640
+ isRemote: {
641
+ type: Boolean,
642
+ default: false
643
+ },
644
+ /**
645
+ * 远程搜索配置
646
+ */
647
+ remoteApiConfig: {
648
+ type: Object,
649
+ default: () => ({
650
+ url: '',
651
+ method: 'GET',
652
+ params: {},
653
+ data: {},
654
+ headers: {},
655
+ transformResponse: null
656
+ })
657
+ },
658
+ /**
659
+ * 员工选择器是否单选
660
+ */
661
+ single: {
662
+ type: Boolean,
663
+ default: true
664
+ },
665
+ /**
666
+ * 员工选择器默认标签页
667
+ * @options organization|recent
668
+ */
669
+ defaultTab: {
670
+ type: String,
671
+ default: 'organization',
672
+ validator: (val) => ['organization', 'recent'].includes(val)
673
+ },
674
+ /**
675
+ * 员工选择器最大选择数量
676
+ */
677
+ maxCount: {
678
+ type: Number,
679
+ default: undefined
680
+ },
681
+ /**
682
+ * 部门选择器API地址
683
+ */
684
+ apiUrl: {
685
+ type: String,
686
+ default: '/api/departments'
687
+ },
688
+ /**
689
+ * 部门选择器所属公司ID
690
+ */
691
+ companyId: {
692
+ type: [String, Number],
693
+ default: undefined
694
+ },
695
+ /**
696
+ * 子表格列配置,用于table类型
697
+ * 示例:[
698
+ * {name:'名称', key:'name', type:'input', width:150},
699
+ * {name:'类型', key:'type', type:'select', options:[{label:'选项1', value:'1'}]},
700
+ * {name:'数量', key:'num', type:'number', min:0, max:999}
701
+ * ]
702
+ */
703
+ tableColumns: {
704
+ type: Array,
705
+ default: () => []
706
+ },
707
+ /**
708
+ * 子表格是否显示操作列
709
+ */
710
+ showTableActions: {
711
+ type: Boolean,
712
+ default: true
713
+ },
714
+ /**
715
+ * 子表格新增按钮文本
716
+ */
717
+ addButtonText: {
718
+ type: String,
719
+ default: '添加数据'
720
+ },
721
+ /**
722
+ * 富文本编辑器高度
723
+ */
724
+ editorHeight: {
725
+ type: [String, Number],
726
+ default: 300
727
+ },
728
+ /**
729
+ * 富文本编辑器工具栏配置
730
+ */
731
+ editorToolbarConfig: {
732
+ type: Object,
733
+ default: () => ({})
734
+ },
735
+ /**
736
+ * 富文本编辑器配置
737
+ */
738
+ editorConfig: {
739
+ type: Object,
740
+ default: () => ({})
741
+ },
742
+ /**
743
+ * 富文本图片上传路径
744
+ */
745
+ editorImagePath: {
746
+ type: String,
747
+ default: 'editor'
748
+ },
749
+ /**
750
+ * 富文本视频上传路径
751
+ */
752
+ editorVideoPath: {
753
+ type: String,
754
+ default: 'video'
755
+ },
756
+ /**
757
+ * 字典类型
758
+ */
759
+ dictionaryKey: {
760
+ type: String,
761
+ default: ''
762
+ }
763
+ });
764
+
765
+ const emit = defineEmits([
766
+ 'update:modelValue',
767
+ 'change',
768
+ 'validate'
769
+ ]);
770
+
771
+ // 计算属性处理v-model双向绑定
772
+ const computedModelValue = computed({
773
+ get: () => props.modelValue,
774
+ set: (val) => {
775
+ emit('update:modelValue', val);
776
+ }
777
+ });
778
+
779
+ // 表格列配置计算属性
780
+ const computedTableColumns = computed(() => {
781
+ if (props.type !== 'table' || !props.tableColumns?.length) return [];
782
+
783
+ const columns = [];
784
+
785
+ // 添加序号列
786
+ columns.push({
787
+ colKey: 'index',
788
+ title: '序号',
789
+ width: 80,
790
+ align: 'center'
791
+ });
792
+
793
+ // 添加用户配置的列
794
+ const userColumns = props.tableColumns.map(col => ({
795
+ colKey: col.key,
796
+ title: col.name,
797
+ width: col.width || undefined,
798
+ align: col.align || 'left'
799
+ }));
800
+
801
+ columns.push(...userColumns);
802
+
803
+ // 添加操作列
804
+ if (props.showTableActions) {
805
+ columns.push({
806
+ colKey: 'actions',
807
+ title: '操作',
808
+ width: 80,
809
+ align: 'center'
810
+ });
811
+ }
812
+
813
+ return columns;
814
+ });
815
+
816
+ /**
817
+ * 值变更事件
818
+ * @param {*} val 新值
819
+ */
820
+ const handleChange = (val) => {
821
+ emit('change', val);
822
+ };
823
+
824
+ /**
825
+ * 验证事件
826
+ * @param {Object} validateResult 验证结果
827
+ */
828
+ const handleValidate = (validateResult) => {
829
+ emit('validate', validateResult);
830
+ };
831
+
832
+ /**
833
+ * 清除视频上传
834
+ */
835
+ const clearVideoUpload = () => {
836
+ computedModelValue.value = null;
837
+ emit('change', null);
838
+ };
839
+
840
+ /**
841
+ * 添加表格行
842
+ */
843
+ const addTableRow = () => {
844
+ const currentData = computedModelValue.value || [];
845
+ const newRow = {};
846
+
847
+ // 根据表格列配置初始化新行数据
848
+ props.tableColumns.forEach(col => {
849
+ // 根据列类型设置默认值
850
+ switch (col.type) {
851
+ case 'number':
852
+ newRow[col.key] = col.defaultValue || 0;
853
+ break;
854
+ case 'switch':
855
+ newRow[col.key] = col.defaultValue || false;
856
+ break;
857
+ case 'checkbox':
858
+ newRow[col.key] = col.defaultValue || [];
859
+ break;
860
+ case 'select':
861
+ if (col.multiple) {
862
+ newRow[col.key] = col.defaultValue || [];
863
+ } else {
864
+ newRow[col.key] = col.defaultValue || '';
865
+ }
866
+ break;
867
+ default:
868
+ newRow[col.key] = col.defaultValue || '';
869
+ break;
870
+ }
871
+ });
872
+
873
+ const newData = [...currentData, newRow];
874
+ // 只更新computedModelValue,让watch自动处理change事件
875
+ computedModelValue.value = JSON.parse(JSON.stringify(newData));
876
+ };
877
+
878
+ /**
879
+ * 删除表格行
880
+ * @param {number} rowIndex 行索引
881
+ */
882
+ const removeTableRow = (rowIndex) => {
883
+ if (props.type !== 'table') return;
884
+
885
+ const currentData = [...(computedModelValue.value || [])];
886
+ currentData.splice(rowIndex, 1);
887
+
888
+ // 只更新computedModelValue,让watch自动处理change事件
889
+ computedModelValue.value = currentData;
890
+ };
891
+
892
+ /**
893
+ * 更新表格单元格数据
894
+ * @param {number} rowIndex 行索引
895
+ * @param {string} colKey 列键名
896
+ * @param {*} value 新值
897
+ */
898
+ const updateTableCell = (rowIndex, colKey, value) => {
899
+ if (props.type !== 'table') return;
900
+
901
+ const currentData = [...(computedModelValue.value || [])];
902
+ if (currentData[rowIndex]) {
903
+ currentData[rowIndex] = {
904
+ ...currentData[rowIndex],
905
+ [colKey]: value
906
+ };
907
+
908
+ // 只更新computedModelValue,让watch自动处理change事件
909
+ computedModelValue.value = currentData;
910
+ }
911
+ };
912
+
913
+ // 监听modelValue变化,触发change事件(仅在table类型时)
914
+ watch(
915
+ () => props.modelValue,
916
+ (newVal, oldVal) => {
917
+ if (props.type === 'table' && newVal !== oldVal) {
918
+ // 只在数据真正变化时触发change事件
919
+ const cleanData = Array.isArray(newVal) ? newVal : [];
920
+ handleChange(cleanData);
921
+ }
922
+ },
923
+ { deep: true }
924
+ );
925
+
926
+ // 组件挂载时初始化文件列表
927
+ onMounted(() => {
928
+ });
929
+ </script>
930
+
931
+ <style scoped>
932
+ .custom-control {
933
+ width: 100%;
934
+ }
935
+
936
+ .ebiz-form-table {
937
+ width: 100%;
938
+ }
939
+
940
+ .ebiz-form-table :deep(.t-table) {
941
+ border: 1px solid var(--td-border-level-1-color);
942
+ border-radius: var(--td-radius-default);
943
+ }
944
+
945
+ .ebiz-form-table :deep(.t-table td) {
946
+ padding: 8px 12px;
947
+ }
948
+
949
+ .ebiz-form-table :deep(.t-table th) {
950
+ background-color: var(--td-bg-color-secondarycontainer);
951
+ font-weight: 500;
952
+ }
953
+
954
+ .ebiz-form-table :deep(.t-input) {
955
+ border: none;
956
+ box-shadow: none;
957
+ }
958
+
959
+ .ebiz-form-table :deep(.t-input:focus-within) {
960
+ border: 1px solid var(--td-brand-color);
961
+ box-shadow: 0 0 0 2px var(--td-brand-color-focus);
962
+ }
963
+
964
+ .ebiz-form-table :deep(.t-input-number) {
965
+ border: none;
966
+ box-shadow: none;
967
+ }
968
+
969
+ .ebiz-form-table :deep(.t-input-number:focus-within) {
970
+ border: 1px solid var(--td-brand-color);
971
+ box-shadow: 0 0 0 2px var(--td-brand-color-focus);
972
+ }
973
+
974
+ .ebiz-form-table :deep(.t-select) {
975
+ border: none;
976
+ box-shadow: none;
977
+ }
978
+
979
+ .ebiz-form-table :deep(.t-select:focus-within) {
980
+ border: 1px solid var(--td-brand-color);
981
+ box-shadow: 0 0 0 2px var(--td-brand-color-focus);
982
+ }
983
+
984
+ .ebiz-form-table :deep(.t-date-picker) {
985
+ border: none;
986
+ box-shadow: none;
987
+ }
988
+
989
+ .ebiz-form-table :deep(.t-date-picker:focus-within) {
990
+ border: 1px solid var(--td-brand-color);
991
+ box-shadow: 0 0 0 2px var(--td-brand-color-focus);
992
+ }
993
+
994
+ .ebiz-form-table :deep(.t-textarea) {
995
+ border: none;
996
+ box-shadow: none;
997
+ }
998
+
999
+ .ebiz-form-table :deep(.t-textarea:focus-within) {
1000
+ border: 1px solid var(--td-brand-color);
1001
+ box-shadow: 0 0 0 2px var(--td-brand-color-focus);
1002
+ }
1003
+
1004
+ .ebiz-form-table :deep(.t-switch) {
1005
+ justify-content: center;
1006
+ }
1007
+
1008
+ .ebiz-form-table :deep(.t-radio-group),
1009
+ .ebiz-form-table :deep(.t-checkbox-group) {
1010
+ justify-content: center;
1011
+ }
1012
1012
  </style>