@ebiz/designer-components 0.2.0 → 0.2.1

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 (230) hide show
  1. package/README.md +29 -29
  2. package/dist/designer-components.css +1 -1
  3. package/dist/index.mjs +22815 -22828
  4. package/package.json +1 -1
  5. package/src/App.vue +26 -26
  6. package/src/apiService/SIMPLE_DATA_SERVICE.md +284 -284
  7. package/src/apiService/mockDataService.js +115 -115
  8. package/src/apiService/simpleDataService.js +312 -312
  9. package/src/assets/base.css +86 -86
  10. package/src/assets/logo.svg +1 -1
  11. package/src/components/Button.vue +149 -149
  12. package/src/components/DataContainer.vue +40 -40
  13. package/src/components/EbizApproval.vue +747 -747
  14. package/src/components/EbizApprovalDetail.vue +169 -169
  15. package/src/components/EbizApprovalForm.vue +510 -510
  16. package/src/components/EbizApprovalV2.vue +747 -747
  17. package/src/components/EbizAutoForm.vue +596 -596
  18. package/src/components/EbizAvatar.vue +115 -115
  19. package/src/components/EbizCheckbox.vue +93 -93
  20. package/src/components/EbizCheckboxGroup.vue +69 -69
  21. package/src/components/EbizDepartmentSelector.vue +148 -148
  22. package/src/components/EbizDescriptions.vue +340 -340
  23. package/src/components/EbizDescriptionsItem.vue +47 -47
  24. package/src/components/EbizDetailBlock.vue +81 -81
  25. package/src/components/EbizDetailItem.vue +559 -559
  26. package/src/components/EbizDetailView.md +438 -438
  27. package/src/components/EbizDetailView.vue +356 -356
  28. package/src/components/EbizDialog.vue +260 -260
  29. package/src/components/EbizDictionarySelect.vue +229 -229
  30. package/src/components/EbizDiv.vue +40 -40
  31. package/src/components/EbizDivider.vue +96 -96
  32. package/src/components/EbizDormDashboard.vue +314 -314
  33. package/src/components/EbizDropdown.vue +135 -135
  34. package/src/components/EbizDropdownItem.vue +85 -85
  35. package/src/components/EbizEmployeeInfo.vue +144 -144
  36. package/src/components/EbizEmployeeSelector.vue +1160 -1160
  37. package/src/components/EbizFileList.vue +545 -545
  38. package/src/components/EbizMap.vue +541 -541
  39. package/src/components/EbizMeetingRoomSelector.vue +1149 -1149
  40. package/src/components/EbizMobileMeetingRoomSelector.vue +727 -727
  41. package/src/components/EbizOkrTree.vue +99 -99
  42. package/src/components/EbizPageHeader.vue +98 -98
  43. package/src/components/EbizPagination.vue +162 -162
  44. package/src/components/EbizParkingDashboard.vue +152 -152
  45. package/src/components/EbizPdfViewer.vue +540 -540
  46. package/src/components/EbizPopconfirm.vue +47 -47
  47. package/src/components/EbizQrCode.vue +167 -167
  48. package/src/components/EbizRadio.vue +86 -86
  49. package/src/components/EbizRadioGroup.vue +83 -83
  50. package/src/components/EbizRemoteSelect.vue +232 -232
  51. package/src/components/EbizRichTextEditor.vue +338 -338
  52. package/src/components/EbizRouteBreadcrumb.vue +46 -46
  53. package/src/components/EbizSApprovalProcess.vue +1456 -1456
  54. package/src/components/EbizSelect.vue +85 -85
  55. package/src/components/EbizSpace.vue +100 -100
  56. package/src/components/EbizStatistic.vue +149 -149
  57. package/src/components/EbizStatisticChart.vue +373 -298
  58. package/src/components/EbizStatsCard.vue +113 -113
  59. package/src/components/EbizSwiper.vue +113 -113
  60. package/src/components/EbizSwiperItem.vue +13 -13
  61. package/src/components/EbizSwitch.vue +85 -85
  62. package/src/components/EbizTabHeader.vue +132 -132
  63. package/src/components/EbizTabPanel.vue +22 -22
  64. package/src/components/EbizTable.vue +469 -469
  65. package/src/components/EbizTableColumn.vue +116 -116
  66. package/src/components/EbizTableSort.vue +179 -179
  67. package/src/components/EbizTabs.vue +142 -142
  68. package/src/components/EbizTdesignButtonDialog.vue +332 -332
  69. package/src/components/EbizTdesignLoading.vue +107 -107
  70. package/src/components/EbizTimePicker.vue +143 -143
  71. package/src/components/EbizTitle.vue +91 -91
  72. package/src/components/EbizTree.vue +141 -141
  73. package/src/components/EbizTreeMergeTable.vue +1494 -1494
  74. package/src/components/EbizTreeSelector.vue +469 -469
  75. package/src/components/EbizVideo.vue +553 -553
  76. package/src/components/EbizVxeTable.vue +290 -290
  77. package/src/components/Form.vue +28 -28
  78. package/src/components/Home.vue +7 -7
  79. package/src/components/LaunchInterview.vue +526 -526
  80. package/src/components/MyComponent.vue +39 -39
  81. package/src/components/Table.vue +45 -45
  82. package/src/components/TdesignAlert.vue +115 -115
  83. package/src/components/TdesignButton.vue +135 -135
  84. package/src/components/TdesignCalendar/index.vue +145 -145
  85. package/src/components/TdesignCard.vue +195 -195
  86. package/src/components/TdesignCol.vue +101 -101
  87. package/src/components/TdesignCollapse.vue +142 -142
  88. package/src/components/TdesignCollapsePanel.vue +79 -79
  89. package/src/components/TdesignDatePicker.vue +124 -124
  90. package/src/components/TdesignDescriptions.vue +74 -74
  91. package/src/components/TdesignDescriptionsItem.vue +50 -50
  92. package/src/components/TdesignDialog.vue +225 -225
  93. package/src/components/TdesignForm.vue +138 -138
  94. package/src/components/TdesignFormItem.vue +105 -105
  95. package/src/components/TdesignGrid.vue +55 -55
  96. package/src/components/TdesignIcon.vue +67 -67
  97. package/src/components/TdesignImage.vue +162 -162
  98. package/src/components/TdesignImageViewer.vue +200 -200
  99. package/src/components/TdesignInput.vue +242 -242
  100. package/src/components/TdesignSelect.vue +446 -446
  101. package/src/components/TdesignTag.vue +117 -117
  102. package/src/components/TdesignTextarea.vue +142 -142
  103. package/src/components/TdesignTimeline.vue +58 -58
  104. package/src/components/TdesignTimelineItem.vue +71 -71
  105. package/src/components/TdesignUpload.vue +414 -414
  106. package/src/components/TdesignWatermark.vue +107 -107
  107. package/src/components/ebiz-form/components/cascader.vue +61 -61
  108. package/src/components/ebiz-form/components/checkbox.vue +37 -37
  109. package/src/components/ebiz-form/components/city.vue +137 -137
  110. package/src/components/ebiz-form/components/date-panel.vue +52 -52
  111. package/src/components/ebiz-form/components/date-range-panel.vue +52 -52
  112. package/src/components/ebiz-form/components/date-range.vue +56 -56
  113. package/src/components/ebiz-form/components/date.vue +52 -52
  114. package/src/components/ebiz-form/components/editor-multi-language.vue +47 -47
  115. package/src/components/ebiz-form/components/editor.vue +78 -78
  116. package/src/components/ebiz-form/components/file-multi-language.vue +52 -52
  117. package/src/components/ebiz-form/components/file.vue +149 -149
  118. package/src/components/ebiz-form/components/images-multi-language.vue +52 -52
  119. package/src/components/ebiz-form/components/images.vue +129 -129
  120. package/src/components/ebiz-form/components/img-multi-language.vue +51 -51
  121. package/src/components/ebiz-form/components/img.vue +129 -129
  122. package/src/components/ebiz-form/components/number.vue +50 -50
  123. package/src/components/ebiz-form/components/radio.vue +28 -28
  124. package/src/components/ebiz-form/components/select.vue +119 -119
  125. package/src/components/ebiz-form/components/switch.vue +23 -23
  126. package/src/components/ebiz-form/components/text-multi-language.vue +47 -47
  127. package/src/components/ebiz-form/components/text.vue +52 -52
  128. package/src/components/ebiz-form/components/textarea-multi-language.vue +48 -48
  129. package/src/components/ebiz-form/components/textarea.vue +29 -29
  130. package/src/components/ebiz-form/components/video-multi-language.vue +51 -51
  131. package/src/components/ebiz-form/components/video.vue +97 -97
  132. package/src/components/ebiz-form/index.vue +157 -157
  133. package/src/components/examples/PopconfirmExample.vue +149 -149
  134. package/src/components/icons/IconCommunity.vue +7 -7
  135. package/src/components/icons/IconDocumentation.vue +7 -7
  136. package/src/components/icons/IconEcosystem.vue +7 -7
  137. package/src/components/icons/IconSupport.vue +7 -7
  138. package/src/components/icons/IconTooling.vue +19 -19
  139. package/src/components/mItems/UserInfo.vue +349 -349
  140. package/src/components/senior/EbizApprovalList/ApprovalList.vue +127 -127
  141. package/src/components/senior/EbizSData/index.vue +280 -280
  142. package/src/components/senior/EbizSDialog/index.vue +776 -776
  143. package/src/components/senior/EbizSForm/README.md +157 -157
  144. package/src/components/senior/EbizSForm/index.vue +667 -667
  145. package/src/components/senior/EbizSForm/item.vue +1011 -1011
  146. package/src/components/senior/EbizSForm/mItems/DateTimePicker.vue +51 -51
  147. package/src/components/senior/EbizSForm/mItems/Picker.vue +63 -63
  148. package/src/index.js +330 -330
  149. package/src/main.js +55 -55
  150. package/src/router/index.js +436 -436
  151. package/src/utils/formatCode.js +24 -24
  152. package/src/utils/generateImportStatement.js +52 -52
  153. package/src/utils/hasJsx.js +25 -25
  154. package/src/utils/index.js +166 -166
  155. package/src/utils/mergeOptions.js +29 -29
  156. package/src/utils/parseRequiredBlocks.js +18 -18
  157. package/src/utils/vue-sfc-validator.js +155 -155
  158. package/src/views/Button.vue +23 -23
  159. package/src/views/CheckboxDemo.vue +104 -104
  160. package/src/views/DataContainer.vue +19 -19
  161. package/src/views/DialogDemo.vue +125 -125
  162. package/src/views/EbizApprovalDemo.vue +87 -87
  163. package/src/views/EbizApprovalFormDemo.vue +207 -207
  164. package/src/views/EbizAutoFormDemo.vue +129 -129
  165. package/src/views/EbizAvatar.vue +223 -223
  166. package/src/views/EbizDepartmentSelectorDemo.vue +169 -169
  167. package/src/views/EbizDetailBlockDemo.vue +30 -30
  168. package/src/views/EbizDetailViewDemo.vue +412 -412
  169. package/src/views/EbizDormDashboardDemo.vue +87 -87
  170. package/src/views/EbizEmployeeInfo.vue +249 -249
  171. package/src/views/EbizEmployeeSelector.vue +85 -85
  172. package/src/views/EbizFileListDemo.vue +339 -339
  173. package/src/views/EbizMap.vue +201 -201
  174. package/src/views/EbizMeetingRoomSelectorDemo.vue +293 -293
  175. package/src/views/EbizMobileMeetingRoomSelectorDemo.vue +566 -566
  176. package/src/views/EbizParkingDashboardDemo.vue +28 -28
  177. package/src/views/EbizRadioDemo.vue +151 -151
  178. package/src/views/EbizSDataDemo.vue +136 -136
  179. package/src/views/EbizSDialogDemo.vue +303 -303
  180. package/src/views/EbizSForm/index.vue +352 -352
  181. package/src/views/EbizSFormDemo.vue +420 -420
  182. package/src/views/EbizSpace.vue +185 -185
  183. package/src/views/EbizSwiper.vue +157 -157
  184. package/src/views/EbizTdesignButtonDialogExample.vue +437 -437
  185. package/src/views/Form.vue +19 -19
  186. package/src/views/GridDemo.vue +238 -238
  187. package/src/views/Home.vue +156 -156
  188. package/src/views/LaunchInterviewDemo.vue +111 -111
  189. package/src/views/Mindmap.vue +17 -17
  190. package/src/views/MyComponent.vue +19 -19
  191. package/src/views/OkrTree.vue +19 -19
  192. package/src/views/PageHeaderDemo.vue +104 -104
  193. package/src/views/PaginationDemo.vue +96 -96
  194. package/src/views/PdfViewerDemo.vue +433 -433
  195. package/src/views/PermissionBoxDemo.vue +85 -85
  196. package/src/views/PopconfirmDemo.vue +80 -80
  197. package/src/views/RemoteSelect.vue +350 -350
  198. package/src/views/StatisticDemo.vue +190 -190
  199. package/src/views/SwitchDemo.vue +79 -79
  200. package/src/views/Table.vue +19 -19
  201. package/src/views/TableDemo.vue +334 -334
  202. package/src/views/TableSortDemo.vue +143 -143
  203. package/src/views/TableView.vue +68 -68
  204. package/src/views/TabsDemo.vue +282 -282
  205. package/src/views/TagDemo.vue +101 -101
  206. package/src/views/TdesignAlert.vue +98 -98
  207. package/src/views/TdesignButton.vue +190 -190
  208. package/src/views/TdesignCalendar.vue +94 -94
  209. package/src/views/TdesignCard.vue +296 -296
  210. package/src/views/TdesignCollapse.vue +293 -293
  211. package/src/views/TdesignDatePicker.vue +187 -187
  212. package/src/views/TdesignDescriptions.vue +101 -101
  213. package/src/views/TdesignForm.vue +248 -248
  214. package/src/views/TdesignIcon.vue +203 -203
  215. package/src/views/TdesignImage.vue +215 -215
  216. package/src/views/TdesignImageViewer.vue +198 -198
  217. package/src/views/TdesignInput.vue +252 -252
  218. package/src/views/TdesignSelect.vue +473 -473
  219. package/src/views/TdesignSwiper.vue +157 -157
  220. package/src/views/TextareaDemo.vue +93 -93
  221. package/src/views/TimePickerDemo.vue +146 -146
  222. package/src/views/TimelineDemo.vue +160 -160
  223. package/src/views/Title.vue +19 -19
  224. package/src/views/TreeDemo.vue +254 -254
  225. package/src/views/TreeMergeTableDemo.vue +239 -239
  226. package/src/views/TreeSelectorDemo.vue +245 -245
  227. package/src/views/UploadDemo.vue +128 -128
  228. package/src/views/VideoDemo.vue +245 -245
  229. package/src/views/VxeTableDemo.vue +279 -279
  230. 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>