sohelp-eleplus 1.1.26 → 1.1.28

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 (205) hide show
  1. package/components.js +1 -0
  2. package/icons/flag/README.md +2 -2
  3. package/icons/flag/index.js +0 -1
  4. package/package.json +1 -1
  5. package/sohelp-ace-editor/README.md +32 -42
  6. package/sohelp-ace-editor/index.vue +166 -156
  7. package/sohelp-api-doc/README.md +36 -0
  8. package/sohelp-api-doc/index.vue +160 -0
  9. package/sohelp-application-select/README.md +9 -7
  10. package/sohelp-application-select/index.vue +10 -13
  11. package/sohelp-autocode/README.md +14 -26
  12. package/sohelp-calendar-view/README.md +9 -7
  13. package/sohelp-calendar-view/index.vue +10 -9
  14. package/sohelp-card/README.md +15 -17
  15. package/sohelp-card/index.vue +1 -1
  16. package/sohelp-card-view/README.md +9 -7
  17. package/sohelp-card-view/index.vue +10 -9
  18. package/sohelp-condition/README.md +29 -49
  19. package/sohelp-condition/index.vue +2 -1
  20. package/sohelp-country-select/README.md +15 -123
  21. package/sohelp-country-select/index.vue +1 -1
  22. package/sohelp-cry-input/README.md +19 -195
  23. package/sohelp-cry-input/index.vue +6 -2
  24. package/sohelp-date/README.md +12 -15
  25. package/sohelp-datetime/README.md +15 -17
  26. package/sohelp-datetime-picker/README.md +18 -18
  27. package/sohelp-datetime-picker/index.vue +11 -4
  28. package/sohelp-datetime-range/README.md +20 -23
  29. package/sohelp-demo-block/README.md +43 -0
  30. package/sohelp-demo-block/index.vue +229 -0
  31. package/sohelp-dict/README.md +28 -23
  32. package/sohelp-dict/index.vue +17 -17
  33. package/sohelp-drawer/README.md +28 -26
  34. package/sohelp-drop-card/README.md +29 -28
  35. package/sohelp-drop-card/index.vue +0 -1
  36. package/sohelp-dyn-select/README.md +31 -16
  37. package/sohelp-dyn-select/index.vue +65 -67
  38. package/sohelp-dyn-tree/README.md +26 -20
  39. package/sohelp-dyn-tree/index.vue +2 -2
  40. package/sohelp-dyn-tree-select/README.md +28 -19
  41. package/sohelp-dyn-tree-select/index.vue +23 -7
  42. package/sohelp-entity-form/README.md +65 -30
  43. package/sohelp-entity-form/index.vue +3 -2
  44. package/sohelp-entity-grid/README.md +13 -11
  45. package/sohelp-entity-grid/index.vue +1 -1
  46. package/sohelp-file-upload/README.md +25 -27
  47. package/sohelp-file-upload/index.vue +1 -1
  48. package/sohelp-filter-scheme/README.md +34 -30
  49. package/sohelp-filter-scheme/index.vue +2 -2
  50. package/sohelp-grid/README.md +52 -33
  51. package/sohelp-grid/index.vue +41 -39
  52. package/sohelp-grid/js/SohelpGridConfig.js +3 -3
  53. package/sohelp-grid/js/useSohelpGridConfig.js +4 -6
  54. package/sohelp-grid-select/README.md +32 -27
  55. package/sohelp-grid-select/index.vue +6 -5
  56. package/sohelp-grid-view/README.md +51 -22
  57. package/sohelp-grid-view-select/README.md +33 -25
  58. package/sohelp-grid-view-select/index.vue +2 -1
  59. package/sohelp-group-view/README.md +14 -4
  60. package/sohelp-group-view/index.vue +10 -9
  61. package/sohelp-icon-select/README.md +14 -12
  62. package/sohelp-image-upload/README.md +27 -27
  63. package/sohelp-image-upload/index.vue +2 -2
  64. package/sohelp-import/README.md +25 -24
  65. package/sohelp-input/README.md +27 -11
  66. package/sohelp-input/index.vue +15 -2
  67. package/sohelp-input-tag/README.md +53 -0
  68. package/sohelp-input-tag/index.vue +337 -0
  69. package/sohelp-modal/README.md +42 -16
  70. package/sohelp-modal/index.vue +21 -4
  71. package/sohelp-modal-select/README.md +745 -0
  72. package/sohelp-modal-select/index.vue +705 -0
  73. package/sohelp-module/README.md +24 -13
  74. package/sohelp-number-input/README.md +15 -8
  75. package/sohelp-number-input/index.vue +3 -0
  76. package/sohelp-number-range/README.md +22 -12
  77. package/sohelp-number-range/index.vue +3 -11
  78. package/sohelp-org-modal-select/README.md +47 -0
  79. package/sohelp-org-modal-select/index.vue +411 -0
  80. package/sohelp-org-select/README.md +23 -10
  81. package/sohelp-org-select/index.vue +41 -24
  82. package/sohelp-org-tree/README.md +19 -7
  83. package/sohelp-org-tree/index.vue +1 -2
  84. package/sohelp-org-tree-select/README.md +22 -11
  85. package/sohelp-org-tree-select/index.vue +1 -2
  86. package/sohelp-org-user-tree/README.md +19 -7
  87. package/sohelp-org-user-tree/index.vue +1 -2
  88. package/sohelp-org-user-tree-select/README.md +8 -3
  89. package/sohelp-org-user-tree-select/index.vue +8 -7
  90. package/sohelp-page/README.md +20 -11
  91. package/sohelp-page/index.vue +1 -1
  92. package/sohelp-pagination/README.md +14 -6
  93. package/sohelp-pagination/index.vue +1 -1
  94. package/sohelp-power/README.md +34 -19
  95. package/sohelp-power/index.vue +2 -2
  96. package/sohelp-pro-form/README.md +64 -21
  97. package/sohelp-pro-layout/README.md +10 -4
  98. package/sohelp-pro-layout/index.vue +8 -7
  99. package/sohelp-pro-table/README.md +30 -6
  100. package/sohelp-process/README.md +26 -13
  101. package/sohelp-process/index.vue +6 -6
  102. package/sohelp-rate/README.md +24 -12
  103. package/sohelp-rate/index.vue +5 -1
  104. package/sohelp-relation/README.md +10 -4
  105. package/sohelp-relation/index.vue +8 -7
  106. package/sohelp-relation-modal-select/README.md +41 -0
  107. package/sohelp-relation-modal-select/index.vue +70 -0
  108. package/sohelp-rich-text/README.md +29 -11
  109. package/sohelp-rich-text/index.vue +21 -20
  110. package/sohelp-richtext/README.md +12 -2
  111. package/sohelp-richtext/index.vue +8 -6
  112. package/sohelp-role-modal-select/README.md +45 -0
  113. package/sohelp-role-modal-select/index.vue +111 -0
  114. package/sohelp-role-select/README.md +18 -10
  115. package/sohelp-role-select/index.vue +36 -27
  116. package/sohelp-search/README.md +12 -4
  117. package/sohelp-search/index.vue +2 -2
  118. package/sohelp-search-pro-form/README.md +15 -1
  119. package/sohelp-search-pro-form/index.vue +2 -1
  120. package/sohelp-select/README.md +31 -30
  121. package/sohelp-select/index.vue +79 -84
  122. package/sohelp-split-panel/README.md +17 -18
  123. package/sohelp-switch/README.md +21 -19
  124. package/sohelp-switch/index.vue +34 -33
  125. package/sohelp-table/README.md +35 -27
  126. package/sohelp-table/index.vue +110 -109
  127. package/sohelp-table-select/README.md +55 -0
  128. package/sohelp-tenant-select/README.md +19 -18
  129. package/sohelp-tenant-select/index.vue +105 -109
  130. package/sohelp-text/README.md +16 -10
  131. package/sohelp-text/index.vue +5 -5
  132. package/sohelp-textarea-input/README.md +19 -12
  133. package/sohelp-time/README.md +11 -10
  134. package/sohelp-tree/README.md +24 -19
  135. package/sohelp-tree/index.vue +21 -23
  136. package/sohelp-tree-select/README.md +23 -10
  137. package/sohelp-user-modal-select/README.md +739 -0
  138. package/sohelp-user-modal-select/index.vue +87 -0
  139. package/sohelp-user-select/README.md +26 -15
  140. package/sohelp-user-select/index.vue +6 -2
  141. package/sohelp-user-tag/README.md +5 -7
  142. package/sohelp-user-tag/index.vue +8 -8
  143. package/sohelp-user-tree/README.md +5 -8
  144. package/sohelp-user-tree/index.vue +8 -7
  145. package/sohelp-vform-drawer/README.md +36 -18
  146. package/sohelp-vform-drawer/index.vue +2 -2
  147. package/sohelp-vform-eleplus/README.md +33 -31
  148. package/sohelp-vform-eleplus/index.vue +2 -2
  149. package/sohelp-vform-eleplus/tinymce/langs/zh_CN.js +461 -461
  150. package/sohelp-vform-eleplus/tinymce/langs/zh_TW.js +418 -418
  151. package/sohelp-vform-eleplus/tinymce/skins/content/dark/content.css +72 -72
  152. package/sohelp-vform-eleplus/tinymce/skins/content/dark/content.min.css +7 -7
  153. package/sohelp-vform-eleplus/tinymce/skins/content/default/content.css +67 -67
  154. package/sohelp-vform-eleplus/tinymce/skins/content/default/content.min.css +7 -7
  155. package/sohelp-vform-eleplus/tinymce/skins/content/document/content.css +72 -72
  156. package/sohelp-vform-eleplus/tinymce/skins/content/document/content.min.css +7 -7
  157. package/sohelp-vform-eleplus/tinymce/skins/content/writer/content.css +68 -68
  158. package/sohelp-vform-eleplus/tinymce/skins/content/writer/content.min.css +7 -7
  159. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/content.css +732 -732
  160. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/content.inline.css +726 -726
  161. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/content.inline.min.css +7 -7
  162. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/content.min.css +7 -7
  163. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/content.mobile.css +29 -29
  164. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/content.mobile.min.css +7 -7
  165. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/skin.css +3047 -3047
  166. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/skin.min.css +7 -7
  167. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/skin.mobile.css +673 -673
  168. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/skin.mobile.min.css +7 -7
  169. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/skin.shadowdom.css +37 -37
  170. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide/skin.shadowdom.min.css +7 -7
  171. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/content.css +714 -714
  172. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/content.inline.css +726 -726
  173. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/content.inline.min.css +7 -7
  174. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/content.min.css +7 -7
  175. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/content.mobile.css +29 -29
  176. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/content.mobile.min.css +7 -7
  177. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/skin.css +3047 -3047
  178. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/skin.min.css +7 -7
  179. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/skin.mobile.css +673 -673
  180. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/skin.mobile.min.css +7 -7
  181. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/skin.shadowdom.css +37 -37
  182. package/sohelp-vform-eleplus/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css +7 -7
  183. package/sohelp-vform-modal/README.md +36 -18
  184. package/sohelp-vform-modal/index.vue +2 -2
  185. package/sohelp-vform-select/README.md +9 -7
  186. package/sohelp-vform-select/index.vue +8 -7
  187. package/sohelp-vxe-grid/DefaultGridOptions.js +5 -3
  188. package/sohelp-vxe-grid/DefaultProps.js +0 -1
  189. package/sohelp-vxe-grid/README.md +540 -35
  190. package/sohelp-vxe-grid/SohelpGridConfig.js +8 -6
  191. package/sohelp-vxe-grid/index.vue +141 -94
  192. package/sohelp-vxe-grid-select/README.md +41 -26
  193. package/sohelp-vxe-table/README.md +23 -20
  194. package/sohelp-vxe-table/index.vue +5 -4
  195. package/sohelp-workflow/README.md +21 -17
  196. package/sohelp-workflow/index.vue +25 -22
  197. package/sohelp-workflow-drawer/README.md +41 -28
  198. package/sohelp-workflow-drawer/components/table.vue +7 -1
  199. package/sohelp-workflow-drawer/index.vue +86 -71
  200. package/sohelp-workflow-drawer/js/index.js +15 -13
  201. package/style/index.scss +0 -0
  202. package/utils/safe-eval.js +89 -0
  203. package/sohelp-dyn-select/props.js +0 -67
  204. package/sohelp-user-select/index.vue~ +0 -53
  205. package/sohelp-user-select/props.js +0 -71
@@ -0,0 +1,745 @@
1
+ # SohelpModalSelect 弹窗选择器
2
+
3
+ 基于 `sohelp-modal` 和 `sohelp-vxe-grid` 封装的弹窗选择组件,点击输入框弹出表格列表进行单选/多选,选中后以 Tag 形式回显。
4
+
5
+ ## ✨ 特性
6
+
7
+ - 🎯 **单选/多选** - 支持灵活的选模式切换
8
+ - 🔍 **分页搜索** - 内置分页和关键字搜索功能
9
+ - 📊 **实时预览** - 多选模式下实时显示已选数据
10
+ - 🎨 **自定义模板** - 支持自定义显示格式
11
+ - ⚙️ **灵活配置** - 完全自定义弹窗和表格配置
12
+ - 🔄 **双向绑定** - v-model 和 v-model:data 完整支持
13
+ - 🪝 **钩子函数** - 打开前/确认前的回调控制
14
+ - 📱 **响应式布局** - 高度自适应,无抖动
15
+ - 🛡️ **数据一致性** - 取消操作后数据自动回显
16
+
17
+ ## 📖 基础用法
18
+
19
+ ### 基础多选
20
+
21
+ ```vue
22
+ <template>
23
+ <sohelp-modal-select
24
+ v-model="selectedIds"
25
+ v-model:data="selectedData"
26
+ :gridProps="{
27
+ url: '/api/user/page',
28
+ gridOptions: {
29
+ columns: [
30
+ { field: 'name', title: '姓名', width: 120 },
31
+ { field: 'code', title: '编码', width: 120 }
32
+ ]
33
+ }
34
+ }"
35
+ labelField="name"
36
+ valueField="id"
37
+ multiple
38
+ />
39
+ </template>
40
+
41
+ <script setup>
42
+ import { ref } from 'vue';
43
+
44
+ const selectedIds = ref([]);
45
+ const selectedData = ref([]);
46
+ </script>
47
+ ```
48
+
49
+ ### 单选模式
50
+
51
+ ```vue
52
+ <template>
53
+ <sohelp-modal-select
54
+ v-model="selectedId"
55
+ v-model:data="selectedData"
56
+ :gridProps="{
57
+ url: '/api/user/page',
58
+ gridOptions: {
59
+ columns: [
60
+ { field: 'name', title: '姓名' },
61
+ { field: 'code', title: '编码' }
62
+ ]
63
+ }
64
+ }"
65
+ :multiple="false"
66
+ labelField="name"
67
+ valueField="id"
68
+ placeholder="请选择用户"
69
+ />
70
+ </template>
71
+
72
+ <script setup>
73
+ import { ref } from 'vue';
74
+
75
+ const selectedId = ref('');
76
+ const selectedData = ref({});
77
+ </script>
78
+ ```
79
+
80
+ ### 自定义显示模板
81
+
82
+ 使用 `selectionTemplate` 属性自定义已选列表的显示格式,支持 `${field}` 占位符:
83
+
84
+ ```vue
85
+ <template>
86
+ <sohelp-modal-select
87
+ v-model="selectedIds"
88
+ v-model:data="selectedData"
89
+ :gridProps="{
90
+ url: '/api/user/page',
91
+ gridOptions: {
92
+ columns: [
93
+ { field: 'user_name', title: '姓名' },
94
+ { field: 'login_name', title: '登录名' }
95
+ ]
96
+ }
97
+ }"
98
+ labelField="user_name"
99
+ valueField="id"
100
+ selectionTemplate="${user_name} (${login_name})"
101
+ multiple
102
+ />
103
+ </template>
104
+
105
+ <script setup>
106
+ import { ref } from 'vue';
107
+
108
+ const selectedIds = ref([]);
109
+ const selectedData = ref([]);
110
+ </script>
111
+ ```
112
+
113
+ ### 自定义弹窗配置
114
+
115
+ ```vue
116
+ <template>
117
+ <sohelp-modal-select
118
+ v-model="selectedIds"
119
+ v-model:data="selectedData"
120
+ :modalProps="{
121
+ title: '选择用户',
122
+ width: '80%',
123
+ minWidth: 800,
124
+ minHeight: 600
125
+ }"
126
+ :gridProps="{
127
+ url: '/api/user/page',
128
+ gridOptions: {
129
+ columns: [
130
+ { field: 'user_name', title: '姓名' },
131
+ { field: 'login_name', title: '登录名' }
132
+ ]
133
+ }
134
+ }"
135
+ labelField="user_name"
136
+ valueField="id"
137
+ />
138
+ </template>
139
+ ```
140
+
141
+ ### 使用钩子函数
142
+
143
+ ```vue
144
+ <template>
145
+ <sohelp-modal-select
146
+ v-model="selectedIds"
147
+ v-model:data="selectedData"
148
+ :gridProps="gridProps"
149
+ labelField="name"
150
+ valueField="id"
151
+ :beforeOpen="handleBeforeOpen"
152
+ @confirm="handleConfirm"
153
+ />
154
+ </template>
155
+
156
+ <script setup>
157
+ import { ref } from 'vue';
158
+ import { ElMessage } from 'element-plus';
159
+
160
+ const selectedIds = ref([]);
161
+ const selectedData = ref([]);
162
+
163
+ const gridProps = {
164
+ url: '/api/user/page',
165
+ gridOptions: {
166
+ columns: [
167
+ { field: 'name', title: '姓名' },
168
+ { field: 'code', title: '编码' }
169
+ ]
170
+ }
171
+ };
172
+
173
+ // 打开前钩子
174
+ const handleBeforeOpen = (modelValue, data) => {
175
+ console.log('当前选中值:', modelValue);
176
+ console.log('当前选中数据:', data);
177
+ // 返回 false 可以阻止弹窗打开
178
+ return true;
179
+ };
180
+
181
+ // 确认事件
182
+ const handleConfirm = (selectedIds, selectedData) => {
183
+ console.log('选中的 ID:', selectedIds);
184
+ console.log('选中的数据:', selectedData);
185
+ ElMessage.success('已选择 ' + selectedIds.length + ' 条数据');
186
+ };
187
+ </script>
188
+ ```
189
+
190
+ ### 表单验证场景
191
+
192
+ ```vue
193
+ <template>
194
+ <el-form :model="form" :rules="rules" ref="formRef">
195
+ <el-form-item label="用户" prop="userIds">
196
+ <sohelp-modal-select
197
+ v-model="form.userIds"
198
+ v-model:data="form.userData"
199
+ :gridProps="{
200
+ url: '/api/user/page',
201
+ gridOptions: {
202
+ columns: [
203
+ { field: 'user_name', title: '姓名' },
204
+ { field: 'login_name', title: '登录名' }
205
+ ]
206
+ }
207
+ }"
208
+ labelField="user_name"
209
+ valueField="id"
210
+ placeholder="请选择用户"
211
+ />
212
+ </el-form-item>
213
+
214
+ <el-form-item>
215
+ <el-button type="primary" @click="submitForm">提交表单</el-button>
216
+ <el-button @click="resetForm">重置</el-button>
217
+ </el-form-item>
218
+ </el-form>
219
+ </template>
220
+
221
+ <script setup>
222
+ import { ref } from 'vue';
223
+
224
+ const formRef = ref(null);
225
+ const form = ref({
226
+ userIds: [],
227
+ userData: []
228
+ });
229
+
230
+ const rules = {
231
+ userIds: [
232
+ { required: true, message: '请选择用户', trigger: 'change' }
233
+ ]
234
+ };
235
+
236
+ const submitForm = async () => {
237
+ await formRef.value?.validate();
238
+ console.log('提交表单:', form.value);
239
+ // 提交到后端...
240
+ };
241
+
242
+ const resetForm = () => {
243
+ formRef.value?.resetFields();
244
+ };
245
+ </script>
246
+ ```
247
+
248
+ ### 自定义触发区域
249
+
250
+ ```vue
251
+ <template>
252
+ <sohelp-modal-select
253
+ v-model="selectedIds"
254
+ v-model:data="selectedData"
255
+ :gridProps="gridProps"
256
+ labelField="name"
257
+ valueField="id"
258
+ >
259
+ <template #header="{ data }">
260
+ <el-button type="primary" @click.stop>
261
+ 已选择 {{ data.length }} 人
262
+ </el-button>
263
+ </template>
264
+ </sohelp-modal-select>
265
+ </template>
266
+
267
+ <script setup>
268
+ import { ref } from 'vue';
269
+
270
+ const selectedIds = ref([]);
271
+ const selectedData = ref([]);
272
+
273
+ const gridProps = {
274
+ url: '/api/user/page',
275
+ gridOptions: {
276
+ columns: [
277
+ { field: 'name', title: '姓名' },
278
+ { field: 'code', title: '编码' }
279
+ ]
280
+ }
281
+ };
282
+ </script>
283
+ ```
284
+
285
+ ### 禁用/只读状态
286
+
287
+ ```vue
288
+ <template>
289
+ <div>
290
+ <!-- 禁用 -->
291
+ <sohelp-modal-select
292
+ v-model="selectedIds"
293
+ v-model:data="selectedData"
294
+ :gridProps="gridProps"
295
+ labelField="name"
296
+ valueField="id"
297
+ disabled
298
+ />
299
+
300
+ <!-- 只读 -->
301
+ <sohelp-modal-select
302
+ v-model="selectedIds"
303
+ v-model:data="selectedData"
304
+ :gridProps="gridProps"
305
+ labelField="name"
306
+ valueField="id"
307
+ readonly
308
+ />
309
+ </div>
310
+ </template>
311
+ ```
312
+
313
+ ### 数据回显场景
314
+
315
+ ```vue
316
+ <template>
317
+ <sohelp-modal-select
318
+ v-model="form.userIds"
319
+ v-model:data="form.userData"
320
+ :gridProps="{
321
+ url: '/api/user/page',
322
+ gridOptions: {
323
+ columns: [
324
+ { field: 'user_name', title: '姓名' },
325
+ { field: 'login_name', title: '登录名' }
326
+ ]
327
+ }
328
+ }"
329
+ labelField="user_name"
330
+ valueField="id"
331
+ />
332
+ </template>
333
+
334
+ <script setup>
335
+ import { ref, onMounted } from 'vue';
336
+
337
+ const form = ref({
338
+ userIds: [],
339
+ userData: []
340
+ });
341
+
342
+ // 模拟从后端加载数据
343
+ onMounted(async () => {
344
+ // 这里假设从后端获取数据
345
+ const response = await fetch('/api/user/detail');
346
+ const data = await response.json();
347
+
348
+ form.value.userIds = data.userIds || [];
349
+ form.value.userData = data.userData || [];
350
+
351
+ // 组件会自动回显数据
352
+ });
353
+ </script>
354
+ ```
355
+
356
+ ## 📋 属性 (Props)
357
+
358
+ | 属性名 | 类型 | 默认值 | 必填 | 说明 |
359
+ | --- | --- | --- | --- | --- |
360
+ | modelValue (v-model) | String / Array | `''` | 否 | 选中项的值(valueField 对应的值),多选时为数组 |
361
+ | data (v-model:data) | Array / Object | `[]` | 否 | 选中项的完整数据对象,多选时为数组 |
362
+ | multiple | Boolean | `true` | 否 | 是否多选模式 |
363
+ | labelField | String | `'label'` | 否 | 显示字段名(Tag 显示) |
364
+ | valueField | String | `'value'` | 否 | 值字段名(用于 v-model) |
365
+ | placeholder | String | `'请选择'` | 否 | 未选择时的占位文本 |
366
+ | title | String | `'选择'` | 否 | 弹窗标题(可被 modalProps.title 覆盖) |
367
+ | disabled | Boolean | `false` | 否 | 是否禁用 |
368
+ | readonly | Boolean | `false` | 否 | 是否只读 |
369
+ | clearable | Boolean | `true` | 否 | 是否可清空 |
370
+ | modalProps | Object | `{ title: '选择数据', width: '100%' }` | 否 | 传递给 sohelp-modal 的属性 |
371
+ | gridProps | Object | `{ url: '/admin/system/user/page', autoLoad: true, gridOptions: { columns: [] } }` | 否 | 传递给 sohelp-vxe-grid 的属性 |
372
+ | beforeOpen | Function | `() => true` | 否 | 打开前的回调函数 |
373
+ | selectionTemplate | String | `''` | 否 | 已选列表的显示模板 |
374
+
375
+ ### modalProps 常用配置
376
+
377
+ | 属性 | 类型 | 默认值 | 说明 |
378
+ | --- | --- | --- | --- |
379
+ | title | String | `'选择数据'` | 弹窗标题 |
380
+ | width | String | `'50%'` | 弹窗宽度 |
381
+ | minWidth | Number | `500` | 弹窗最小宽度 |
382
+ | minHeight | Number | `500` | 弹窗最小高度 |
383
+ | closeOnClickModal | Boolean | `true` | 点击遮罩是否关闭 |
384
+
385
+ ### gridProps 常用配置
386
+
387
+ | 属性 | 类型 | 默认值 | 说明 |
388
+ | --- | --- | --- | --- |
389
+ | url | String | `'/admin/system/user/page'` | 数据接口地址 |
390
+ | autoLoad | Boolean | `true` | 是否自动加载数据 |
391
+ | gridOptions | Object | - | 表格配置对象 |
392
+ | gridOptions.columns | Array | `[]` | 表格列配置 |
393
+ | gridOptions.pagerConfig | Object | `{ enabled: true, pageSize: 50 }` | 分页配置 |
394
+
395
+ ## 🔄 双向绑定 (v-model)
396
+
397
+ | 属性名 | 类型 | 说明 |
398
+ | --- | --- | --- |
399
+ | modelValue | String / Array | 选中项的值(valueField 对应的值),多选时为数组,单选时为字符串 |
400
+ | data | Array / Object | 选中项的完整数据对象,多选时为数组,单选时为对象 |
401
+
402
+ ## 🎯 事件 (Events)
403
+
404
+ | 事件名 | 回调参数 | 说明 |
405
+ | --- | --- | --- |
406
+ | update:modelValue | `(selectedIds)` | 选中值变化时触发 |
407
+ | confirm | `(selectedIds, selectedData)` | 点击确认后触发,返回选中的 ID 数组和完整数据 |
408
+ | page-change | `(pageInfo)` | 分页变化时触发 |
409
+
410
+ ## 🎨 插槽 (Slots)
411
+
412
+ | 插槽名 | 作用域参数 | 说明 |
413
+ | --- | --- | --- |
414
+ | header | `{ data }` | 自定义触发区域,替换默认的 Tag 输入框 |
415
+ | default | - | 自定义弹窗内容区域,替换默认的表格 |
416
+
417
+ ## 🔧 方法 (Methods)
418
+
419
+ 通过 `ref` 可以调用以下方法:
420
+
421
+ | 方法名 | 参数 | 返回值 | 说明 |
422
+ | --- | --- | --- | --- |
423
+ | clearAll | - | - | 清空所有选中数据 |
424
+
425
+ ```vue
426
+ <template>
427
+ <sohelp-modal-select
428
+ ref="selectRef"
429
+ v-model="selectedIds"
430
+ v-model:data="selectedData"
431
+ :gridProps="gridProps"
432
+ />
433
+ <el-button @click="clearSelection">清空选中</el-button>
434
+ </template>
435
+
436
+ <script setup>
437
+ import { ref } from 'vue';
438
+
439
+ const selectRef = ref(null);
440
+ const selectedIds = ref([]);
441
+ const selectedData = ref([]);
442
+
443
+ const clearSelection = () => {
444
+ selectRef.value?.clearAll();
445
+ };
446
+ </script>
447
+ ```
448
+
449
+ ## 📊 数据流说明
450
+
451
+ 组件的数据流设计如下:
452
+
453
+ ```
454
+ ┌─────────────┐
455
+ │ 打开弹窗 │
456
+ └──────┬──────┘
457
+
458
+
459
+ ┌─────────────────────────────────────┐
460
+ │ 根据 modelValue 和 data 回显数据 │
461
+ │ - selections = data.value │
462
+ │ - 表格选中状态 = selections │
463
+ └──────┬──────────────────────────────┘
464
+
465
+
466
+ ┌─────────────┐
467
+ │ 用户操作 │
468
+ │ - 添加选中 │
469
+ │ - 删除选中 │
470
+ └──────┬──────┘
471
+
472
+
473
+ ┌─────────────────────────────────────┐
474
+ │ selections 临时保存选中状态 │
475
+ │ (不影响 modelValue 和 data) │
476
+ └──────┬──────────────────────────────┘
477
+
478
+
479
+ ┌─────────────┐ ┌─────────────┐
480
+ │ 点击确认 │ │ 点击取消 │
481
+ └──────┬──────┘ └──────┬──────┘
482
+ │ │
483
+ ▼ ▼
484
+ ┌─────────────┐ ┌─────────────────────┐
485
+ │ 更新: │ │ 不更新: │
486
+ │ - modelValue │ │ - modelValue 保持不变 │
487
+ │ - data │ │ - data 保持不变 │
488
+ └─────────────┘ └─────────────────────┘
489
+ ```
490
+
491
+ 1. **v-model(modelValue)**:保存选中项的 ID 值(或 ID 数组)
492
+ 2. **v-model:data(data)**:保存选中项的完整数据对象
493
+ 3. **弹窗打开时**:根据 `modelValue` 和 `data` 自动回显已选数据
494
+ 4. **弹窗操作时**:`selections` 临时保存弹窗内的选中状态
495
+ 5. **点击确认时**:将 `selections` 同步到 `modelValue` 和 `data`
496
+ 6. **点击取消/关闭时**:不修改 `modelValue` 和 `data`,下次打开时重新回显
497
+
498
+ ## 🎯 最佳实践
499
+
500
+ ### 1. 数据回显
501
+
502
+ ```vue
503
+ <template>
504
+ <sohelp-modal-select
505
+ v-model="form.userIds"
506
+ v-model:data="form.userData"
507
+ :gridProps="{
508
+ url: '/api/user/page',
509
+ gridOptions: {
510
+ columns: [
511
+ { field: 'user_name', title: '姓名' },
512
+ { field: 'login_name', title: '登录名' }
513
+ ]
514
+ }
515
+ }"
516
+ labelField="user_name"
517
+ valueField="id"
518
+ />
519
+ </template>
520
+
521
+ <script setup>
522
+ import { ref } from 'vue';
523
+
524
+ const form = ref({
525
+ userIds: ['1', '2', '3'],
526
+ userData: [
527
+ { id: '1', user_name: '张三', login_name: 'zhangsan' },
528
+ { id: '2', user_name: '李四', login_name: 'lisi' },
529
+ { id: '3', user_name: '王五', login_name: 'wangwu' }
530
+ ]
531
+ });
532
+
533
+ // 组件会自动回显这些数据
534
+ </script>
535
+ ```
536
+
537
+ ### 2. 表单验证
538
+
539
+ ```vue
540
+ <template>
541
+ <el-form :model="form" :rules="rules">
542
+ <el-form-item label="用户" prop="userIds">
543
+ <sohelp-modal-select
544
+ v-model="form.userIds"
545
+ v-model:data="form.userData"
546
+ :gridProps="gridProps"
547
+ />
548
+ </el-form-item>
549
+ </el-form>
550
+ </template>
551
+
552
+ <script setup>
553
+ import { ref } from 'vue';
554
+
555
+ const form = ref({
556
+ userIds: [],
557
+ userData: []
558
+ });
559
+
560
+ const rules = {
561
+ userIds: [
562
+ { required: true, message: '请选择用户', trigger: 'change' }
563
+ ]
564
+ };
565
+ </script>
566
+ ```
567
+
568
+ ### 3. 动态过滤数据
569
+
570
+ ```vue
571
+ <template>
572
+ <div>
573
+ <el-select v-model="deptId" placeholder="请选择部门">
574
+ <el-option label="技术部" value="1" />
575
+ <el-option label="市场部" value="2" />
576
+ </el-select>
577
+
578
+ <sohelp-modal-select
579
+ v-model="selectedIds"
580
+ v-model:data="selectedData"
581
+ :gridProps="dynamicGridProps"
582
+ labelField="user_name"
583
+ valueField="id"
584
+ />
585
+ </div>
586
+ </template>
587
+
588
+ <script setup>
589
+ import { ref, computed } from 'vue';
590
+
591
+ const deptId = ref('1');
592
+ const selectedIds = ref([]);
593
+ const selectedData = ref([]);
594
+
595
+ const dynamicGridProps = computed(() => ({
596
+ url: `/api/user/page?deptId=${deptId.value}`,
597
+ gridOptions: {
598
+ columns: [
599
+ { field: 'user_name', title: '姓名' },
600
+ { field: 'login_name', title: '登录名' }
601
+ ]
602
+ }
603
+ }));
604
+ </script>
605
+ ```
606
+
607
+ ### 4. 级联选择
608
+
609
+ ```vue
610
+ <template>
611
+ <div>
612
+ <sohelp-modal-select
613
+ v-model="form.deptId"
614
+ v-model:data="form.deptData"
615
+ :gridProps="deptGridProps"
616
+ labelField="name"
617
+ valueField="id"
618
+ placeholder="请选择部门"
619
+ @confirm="handleDeptChange"
620
+ />
621
+
622
+ <sohelp-modal-select
623
+ v-if="form.deptId"
624
+ v-model="form.userId"
625
+ v-model:data="form.userData"
626
+ :gridProps="userGridProps"
627
+ labelField="user_name"
628
+ valueField="id"
629
+ placeholder="请选择用户"
630
+ />
631
+ </div>
632
+ </template>
633
+
634
+ <script setup>
635
+ import { ref } from 'vue';
636
+
637
+ const form = ref({
638
+ deptId: '',
639
+ deptData: {},
640
+ userId: '',
641
+ userData: {}
642
+ });
643
+
644
+ const deptGridProps = {
645
+ url: '/api/dept/page',
646
+ gridOptions: {
647
+ columns: [
648
+ { field: 'name', title: '部门名称' },
649
+ { field: 'code', title: '部门编码' }
650
+ ]
651
+ }
652
+ };
653
+
654
+ const userGridProps = ref({
655
+ url: '',
656
+ gridOptions: {
657
+ columns: [
658
+ { field: 'user_name', title: '姓名' },
659
+ { field: 'login_name', title: '登录名' }
660
+ ]
661
+ }
662
+ });
663
+
664
+ const handleDeptChange = (deptIds) => {
665
+ if (deptIds.length > 0) {
666
+ // 根据选择的部门加载用户
667
+ userGridProps.value.url = `/api/user/page?deptId=${deptIds[0]}`;
668
+ }
669
+ };
670
+ </script>
671
+ ```
672
+
673
+ ## ❓ 常见问题
674
+
675
+ ### Q1: 为什么需要同时绑定 v-model 和 v-model:data?
676
+
677
+ **A**:
678
+ - `v-model` 保存 ID 值,用于提交到后端
679
+ - `v-model:data` 保存完整数据,用于界面显示和数据处理
680
+ - 这样设计可以避免重复请求接口获取完整数据
681
+
682
+ ### Q2: 弹窗取消后再次打开,为什么还能看到之前的选中状态?
683
+
684
+ **A**: 这是正常设计。组件每次打开弹窗时,会根据 `v-model` 和 `v-model:data` 的值重新回显数据,确保数据一致性。只有在点击确认时才会更新这两个值。
685
+
686
+ ### Q3: 如何实现级联选择?
687
+
688
+ **A**: 可以在 `beforeOpen` 钩子中动态修改 `gridProps.url`,或使用 `:gridProps="dynamicGridProps"` 计算属性。参考上面的"级联选择"示例。
689
+
690
+ ### Q4: 表格高度抖动怎么办?
691
+
692
+ **A**: 组件已优化布局,使用了 flex 布局的 `min-height: 0` 技巧。如果仍有问题,请检查:
693
+
694
+ 1. 确保 `gridProps.gridOptions.height` 不与容器高度冲突
695
+ 2. 确保弹窗容器有明确的高度设置
696
+ 3. 检查是否有其他 CSS 样式影响布局
697
+
698
+ ### Q5: 选中数据后再次打开弹窗,取消部分选中,点取消,为什么数据不对?
699
+
700
+ **A**: 这是已修复的问题。组件现在每次打开弹窗时,都会根据 `modelValue` 和 `data` 重新初始化 `selections`,确保不受上次弹窗操作的影响。
701
+
702
+ ### Q6: 确认后 data.value 中的数据只有部分字段?
703
+
704
+ **A**: 组件会根据 `gridOptions.columns` 中的字段配置补充数据。如果需要保留所有字段,请确保 `columns` 中包含所需的所有字段,或确保原始数据中已包含所有字段。
705
+
706
+ ### Q7: 如何实现默认选中某些数据?
707
+
708
+ **A**: 直接设置 `v-model` 和 `v-model:data` 的初始值即可:
709
+
710
+ ```vue
711
+ <script setup>
712
+ const selectedIds = ref(['1', '2', '3']);
713
+ const selectedData = ref([
714
+ { id: '1', name: '张三' },
715
+ { id: '2', name: '李四' },
716
+ { id: '3', name: '王五' }
717
+ ]);
718
+ </script>
719
+ ```
720
+
721
+ ## 📝 更新日志
722
+
723
+ ### v1.3.0
724
+ - 🐛 修复选中数据后再次打开弹窗,取消部分选中导致数据丢失的问题
725
+ - 🐛 修复确认后 data.value 中数据字段丢失的问题
726
+ - ✨ 优化弹窗布局,修复高度抖动问题
727
+ - ✨ 改进数据回显逻辑,确保取消操作后数据一致性
728
+
729
+ ### v1.2.0
730
+ - ✨ 新增 `selectionTemplate` 属性,支持自定义显示模板
731
+ - ✨ 新增 `beforeOpen` 钩子函数
732
+ - ✨ 支持单选/多选模式切换
733
+
734
+ ### v1.1.0
735
+ - ✨ 优化选中区域显示效果
736
+ - ✨ 支持自定义弹窗内容区域
737
+
738
+ ### v1.0.0
739
+ - 🎉 初始版本
740
+
741
+ ## 🔗 相关组件
742
+
743
+ - [sohelp-vxe-grid](../sohelp-vxe-grid/README.md) - 数据网格组件
744
+ - [sohelp-modal](../sohelp-modal/README.md) - 弹窗组件
745
+ - [sohelp-user-modal-select](../sohelp-user-modal-select/README.md) - 用户选择器