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
@@ -1,24 +1,35 @@
1
- # SohelpModule 模块弹窗管理器
1
+ # SohelpModule 动态模块容器
2
2
 
3
- 用于统一管理和渲染动态弹窗(Modal)和抽屉(Drawer)的组件,通常用于低代码场景或需要动态打开表单的场景。
3
+ 动态渲染多个模态弹窗和抽屉的容器组件,内部集成 `sohelp-modal`、`sohelp-drawer` 和 `sohelp-vform`,通过 `useModalManager` 统一管理弹窗/抽屉的打开与关闭。
4
4
 
5
5
  ## 基础用法
6
6
 
7
- 通常在应用的根组件或布局组件中引入一次即可。
7
+ ```vue
8
+ <template>
9
+ <sohelp-module ref="moduleRef" />
10
+ </template>
8
11
 
9
- ```html
10
- <sohelp-module ref="moduleRef" />
12
+ <script setup>
13
+ import { ref } from 'vue';
14
+ const moduleRef = ref(null);
15
+
16
+ // 打开一个模态弹窗
17
+ moduleRef.value?.openModal('form1', { title: '新增', width: '600px', params: { id: 1 } });
18
+
19
+ // 打开一个抽屉
20
+ moduleRef.value?.openDrawer('detail1', { title: '详情', size: '40%', params: { id: 1 } });
21
+ </script>
11
22
  ```
12
23
 
13
- ## 功能特性
24
+ ## 属性 (Props)
14
25
 
15
- - **动态渲染**: 根据状态动态渲染多个 `sohelp-modal` 或 `sohelp-drawer`。
16
- - **集成表单**: 内部默认集成 `sohelp-vform-eleplus`,用于加载动态表单。
17
- - **状态管理**: 使用 `useModalManager` Hook 管理弹窗的打开和关闭状态。
26
+ 该组件无自定义 Props。
18
27
 
19
28
  ## 公开方法 (Expose)
20
29
 
21
- - `openModal(id, params)`: 打开指定 ID 的模态框。
22
- - `closeModal(id)`: 关闭指定 ID 的模态框。
23
- - `openDrawer(id, params)`: 打开指定 ID 的抽屉。
24
- - `closeDrawer(id)`: 关闭指定 ID 的抽屉。
30
+ | 方法名 | 参数 | 说明 |
31
+ | --- | --- | --- |
32
+ | openModal(id, options) | `id`: 唯一标识, `options`: 弹窗配置 | 打开一个模态弹窗并加载 vform |
33
+ | closeModal(id) | `id`: 唯一标识 | 关闭指定模态弹窗 |
34
+ | openDrawer(id, options) | `id`: 唯一标识, `options`: 抽屉配置 | 打开一个抽屉并加载 vform |
35
+ | closeDrawer(id) | `id`: 唯一标识 | 关闭指定抽屉 |
@@ -1,18 +1,25 @@
1
1
  # SohelpNumberInput 数字输入框
2
2
 
3
- 基于 `el-input-number` 的封装,增加了自动聚焦功能。
3
+ 基于 `el-input-number` 封装的数字输入框组件,默认控制按钮靠右显示,支持自动聚焦。
4
4
 
5
5
  ## 基础用法
6
6
 
7
- ```html
8
- <sohelp-number-input v-model="num" :auto-focus="true" />
7
+ ```vue
8
+ <template>
9
+ <sohelp-number-input v-model="num" />
10
+ </template>
11
+
12
+ <script setup>
13
+ import { ref } from 'vue';
14
+ const num = ref(0);
15
+ </script>
9
16
  ```
10
17
 
11
18
  ## 属性 (Props)
12
19
 
13
- | 属性名 | 类型 | 默认值 | 说明 |
14
- | autoFocus | Boolean | false | 是否自动获取焦点 |
15
-
16
- ## 其他
20
+ | 属性名 | 类型 | 默认值 | 必填 | 说明 |
21
+ | :--- | :--- | :--- | :--- | :--- |
22
+ | modelValue | Number | - | 否 | 绑定值(支持 v-model) |
23
+ | autoFocus | Boolean | `false` | 否 | 组件挂载后是否自动获取焦点 |
17
24
 
18
- 继承 `el-input-number` 的所有属性和事件,默认 `controls-position="right"`。
25
+ > 支持通过 `v-bind="$attrs"` 透传所有 `el-input-number` 原生属性(如 `min`、`max`、`step`、`precision`、`disabled`、`readonly` 等)。默认 `controls-position="right"`。
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <el-input-number
3
+ v-model="modelValue"
3
4
  v-bind="$attrs"
4
5
  controls-position="right"
5
6
  ref="inputRef"
@@ -9,6 +10,8 @@
9
10
  <script setup>
10
11
  import { nextTick, onMounted, ref } from 'vue';
11
12
 
13
+ const modelValue = defineModel('modelValue', { type: Number });
14
+
12
15
  const props = defineProps({
13
16
  autoFocus: {
14
17
  type: Boolean,
@@ -1,24 +1,34 @@
1
1
  # SohelpNumberRange 数字范围输入
2
2
 
3
- 用于输入一个数值范围(最小值 - 最大值),会自动处理最小值不能大于最大值的逻辑。
3
+ 基于两个 `el-input` 组合的数字范围输入组件,用于输入最小值和最大值,内置数值边界校验与自动纠正逻辑。
4
4
 
5
5
  ## 基础用法
6
6
 
7
- ```html
8
- <sohelp-number-range v-model="range" :min="0" :max="100" />
7
+ ```vue
8
+ <template>
9
+ <sohelp-number-range v-model="range" :min="0" :max="100" />
10
+ </template>
11
+
12
+ <script setup>
13
+ import { ref } from 'vue';
14
+ const range = ref([10, 50]);
15
+ </script>
9
16
  ```
10
17
 
11
18
  ## 属性 (Props)
12
19
 
13
- | 属性名 | 类型 | 默认值 | 说明 |
14
- | modelValue | Array | [] | 范围数组 [min, max] |
15
- | min | Number | -Infinity | 允许的最小值 |
16
- | max | Number | Infinity | 允许的最大值 |
17
- | separator | String | '-' | 分隔符 |
18
- | clearable | Boolean | true | 是否可清空 |
20
+ | 属性名 | 类型 | 默认值 | 必填 | 说明 |
21
+ | :--- | :--- | :--- | :--- | :--- |
22
+ | modelValue | String / Number / Array | - | | 绑定值,数组格式 `[min, max]` |
23
+ | min | Number | `-Infinity` | | 允许输入的最小值 |
24
+ | max | Number | `Infinity` | | 允许输入的最大值 |
25
+ | separator | String | `'-'` | | 两个输入框之间的分隔符文本 |
26
+ | clearable | Boolean | `true` | 否 | 是否可清空 |
27
+ | controlsPosition | String | `'right'` | 否 | 控制按钮位置 |
19
28
 
20
29
  ## 事件 (Events)
21
30
 
22
- | 事件名 | 说明 | 回调参数 |
23
- | update:modelValue | 值变化时触发 | (value: Array) |
24
- | change | 值变化时触发 | (value: Array) |
31
+ | 事件名 | 回调参数 | 说明 |
32
+ | :--- | :--- | :--- |
33
+ | update:modelValue | `(value: Array)` | 值变化时触发 |
34
+ | change | `(value: Array)` | 值变化时触发 |
@@ -1,5 +1,5 @@
1
- <template name="sohelp-number-range">
2
- <view class="sohelp-number-range">
1
+ <template>
2
+ <div class="sohelp-number-range">
3
3
  <el-input
4
4
  v-model="value[0]"
5
5
  v-bind="$attrs"
@@ -19,7 +19,7 @@
19
19
  @change="change"
20
20
  :controlsPosition="controlsPosition"
21
21
  ></el-input>
22
- </view>
22
+ </div>
23
23
  </template>
24
24
  <script setup>
25
25
  import { ref, watch } from 'vue';
@@ -124,12 +124,4 @@
124
124
  :deep(input[type='number']) {
125
125
  -moz-appearance: textfield;
126
126
  }
127
-
128
- :deep(input::-webkit-outer-spin-button),
129
- :deep(input::-webkit-inner-spin-button) {
130
- -webkit-appearance: none;
131
- }
132
- :deep(input[type='number']) {
133
- -moz-appearance: textfield;
134
- }
135
127
  </style>
@@ -0,0 +1,47 @@
1
+ # SohelpOrgModalSelect 组织选择器
2
+
3
+ 基于 `sohelp-modal` 和 `el-tree` 封装的组织树选择组件,点击输入框弹出组织树进行单选/多选,选中后以 Tag 形式回显。
4
+
5
+ ## 基础用法
6
+
7
+ ```vue
8
+ <template>
9
+ <sohelp-org-modal-select
10
+ v-model="selectedOrgs"
11
+ v-model:data="selectedOrgData"
12
+ multiple
13
+ />
14
+ </template>
15
+
16
+ <script setup>
17
+ import { ref } from 'vue';
18
+ const selectedOrgs = ref([]);
19
+ const selectedOrgData = ref([]);
20
+ </script>
21
+ ```
22
+
23
+ ## 属性 (Props)
24
+
25
+ | 属性名 | 类型 | 默认值 | 必填 | 说明 |
26
+ | --- | --- | --- | --- | --- |
27
+ | modelValue (v-model) | String / Array | `''` | 否 | 选中组织的 ID 值 |
28
+ | data (v-model:data) | Array / Object | `[]` | 否 | 选中组织的完整数据 |
29
+ | url | String | `'/engine/web/org/list'` | 否 | 组织数据的请求地址 |
30
+ | datasource | Array / Function / Object | - | 否 | 自定义数据源,优先于 url |
31
+ | valueField | String | `'id'` | 否 | 值字段名 |
32
+ | labelField | String | `'org_name'` | 否 | 显示字段名 |
33
+ | multiple | Boolean | `true` | 否 | 是否多选 |
34
+ | checkStrictly | Boolean | `true` | 否 | 父子节点是否不关联(严格模式) |
35
+ | title | String | `'选择组织'` | 否 | 弹窗标题 |
36
+ | width | String | `'500px'` | 否 | 弹窗宽度 |
37
+ | placeholder | String | `'请选择组织'` | 否 | 占位文本 |
38
+ | disabled | Boolean | `false` | 否 | 是否禁用 |
39
+ | readonly | Boolean | `false` | 否 | 是否只读 |
40
+ | clearable | Boolean | `true` | 否 | 是否可清空 |
41
+
42
+ ## 事件 (Events)
43
+
44
+ | 事件名 | 回调参数 | 说明 |
45
+ | --- | --- | --- |
46
+ | update:modelValue | `(value)` | 选中值变化时触发 |
47
+ | confirm | `(value, data)` | 确认选择后触发,返回选中值和完整数据 |
@@ -0,0 +1,411 @@
1
+ <template>
2
+ <div class="sohelp-org-modal-select">
3
+ <!-- 输入框触发区 -->
4
+ <div
5
+ class="select-trigger"
6
+ :class="[
7
+ globalConfig.size ? 'el-input--' + globalConfig.size : '',
8
+ { 'is-disabled': disabled, 'is-readonly': readonly }
9
+ ]"
10
+ @click.stop="onOpen"
11
+ >
12
+ <div class="tag-input">
13
+ <span class="placeholder-text" v-if="!displayTags.length">{{ placeholder }}</span>
14
+ <template v-else>
15
+ <el-tag
16
+ v-for="item in displayTags"
17
+ :key="item.id"
18
+ class="selection-tag"
19
+ :type="disabled || readonly ? 'info' : 'primary'"
20
+ :closable="clearable && !disabled && !readonly"
21
+ @close.stop="removeTag(item)"
22
+ >
23
+ {{ item.org_name }}
24
+ </el-tag>
25
+ </template>
26
+ </div>
27
+ <div class="append-icon">
28
+ <el-icon
29
+ size="15"
30
+ class="clear-icon"
31
+ v-if="clearable && !disabled && !readonly && displayTags.length > 0"
32
+ @click.stop="clearAll"
33
+ >
34
+ <CircleCloseFilled />
35
+ </el-icon>
36
+ <el-icon size="15"><Search /></el-icon>
37
+ </div>
38
+ </div>
39
+
40
+ <!-- 弹窗 -->
41
+ <sohelp-modal
42
+ v-model="modalVisible"
43
+ :title="title"
44
+ :width="width"
45
+ @confirm="onConfirm"
46
+ >
47
+ <div class="org-tree-container">
48
+ <div class="tree-search">
49
+ <el-input
50
+ v-model="filterText"
51
+ placeholder="输入关键字搜索"
52
+ clearable
53
+ prefix-icon="Search"
54
+ />
55
+ </div>
56
+ <el-scrollbar class="tree-scrollbar">
57
+ <el-tree
58
+ ref="treeRef"
59
+ :data="treeData"
60
+ :props="{ label: 'label', children: 'children' }"
61
+ node-key="id"
62
+ :show-checkbox="multiple"
63
+ :check-strictly="checkStrictly"
64
+ :default-expanded-keys="defaultExpandedKeys"
65
+ :filter-node-method="filterNode"
66
+ highlight-current
67
+ @node-click="onNodeClick"
68
+ @check="onCheck"
69
+ />
70
+ </el-scrollbar>
71
+ </div>
72
+
73
+ <!-- 多选时显示已选列表 -->
74
+ <template v-if="multiple" #footer>
75
+ <div class="modal-footer">
76
+ <div class="selected-info">
77
+ <el-tag type="info" size="small">已选 {{ tempSelected.length }} 个</el-tag>
78
+ <el-button v-if="tempSelected.length > 0" type="danger" link size="small" @click="clearTempSelected">清空</el-button>
79
+ </div>
80
+ <div>
81
+ <el-button @click="modalVisible = false">取消</el-button>
82
+ <el-button type="primary" @click="onConfirm">确认</el-button>
83
+ </div>
84
+ </div>
85
+ </template>
86
+ </sohelp-modal>
87
+ </div>
88
+ </template>
89
+
90
+ <script>
91
+ export default {
92
+ name: 'SohelpOrgModalSelect'
93
+ };
94
+ </script>
95
+
96
+ <script setup>
97
+ import { ref, computed, watch, onMounted, nextTick } from 'vue';
98
+ import { Search, CircleCloseFilled } from '@element-plus/icons-vue';
99
+ import { useGlobalConfig, ElMessage } from 'element-plus';
100
+ import { toTree } from '../utils/core.js';
101
+ import SohelpModal from '../sohelp-modal/index.vue';
102
+
103
+ const props = defineProps({
104
+ url: {
105
+ type: String,
106
+ default: '/engine/web/org/list'
107
+ },
108
+ datasource: {
109
+ type: [Array, Function, Object]
110
+ },
111
+ valueField: {
112
+ type: String,
113
+ default: 'id'
114
+ },
115
+ labelField: {
116
+ type: String,
117
+ default: 'org_name'
118
+ },
119
+ multiple: {
120
+ type: Boolean,
121
+ default: true
122
+ },
123
+ checkStrictly: {
124
+ type: Boolean,
125
+ default: true
126
+ },
127
+ title: {
128
+ type: String,
129
+ default: '选择组织'
130
+ },
131
+ width: {
132
+ type: String,
133
+ default: '500px'
134
+ },
135
+ placeholder: {
136
+ type: String,
137
+ default: '请选择组织'
138
+ },
139
+ disabled: {
140
+ type: Boolean,
141
+ default: false
142
+ },
143
+ readonly: {
144
+ type: Boolean,
145
+ default: false
146
+ },
147
+ clearable: {
148
+ type: Boolean,
149
+ default: true
150
+ }
151
+ });
152
+
153
+ const emits = defineEmits(['confirm', 'update:modelValue']);
154
+
155
+ const modelValue = defineModel('modelValue', {
156
+ type: [String, Array],
157
+ default: ''
158
+ });
159
+
160
+ const data = defineModel('data', {
161
+ type: [Array, Object],
162
+ default: () => []
163
+ });
164
+
165
+ const globalConfig = useGlobalConfig();
166
+ const treeRef = ref(null);
167
+ const modalVisible = ref(false);
168
+ const filterText = ref('');
169
+ const treeData = ref([]);
170
+ const flatData = ref([]);
171
+ const defaultExpandedKeys = ref([]);
172
+
173
+ // 临时选中(弹窗内操作,确认后才同步)
174
+ const tempSelected = ref([]);
175
+
176
+ // 显示 tag
177
+ const displayTags = computed(() => {
178
+ const values = Array.isArray(modelValue.value)
179
+ ? modelValue.value
180
+ : modelValue.value ? [modelValue.value] : [];
181
+ return values.map((v) => flatData.value.find((d) => d[props.valueField] === v)).filter(Boolean);
182
+ });
183
+
184
+ // 搜索过滤
185
+ watch(filterText, (val) => {
186
+ treeRef.value?.filter(val);
187
+ });
188
+
189
+ const filterNode = (value, data) => {
190
+ if (!value) return true;
191
+ return data.label?.includes(value);
192
+ };
193
+
194
+ // 加载数据
195
+ onMounted(async () => {
196
+ let rawData = [];
197
+ if (Array.isArray(props.datasource)) {
198
+ rawData = props.datasource;
199
+ } else if (typeof props.datasource === 'function') {
200
+ rawData = await props.datasource();
201
+ } else if (props.url) {
202
+ try {
203
+ const res = await SohelpHttp.get(props.url, {});
204
+ rawData = res?.data || [];
205
+ } catch (e) {
206
+ ElMessage.error(e?.meta?.message || '加载组织数据失败');
207
+ }
208
+ }
209
+
210
+ flatData.value = rawData;
211
+
212
+ treeData.value = toTree({
213
+ data: rawData.map((m) => ({
214
+ ...m,
215
+ label: m[props.labelField],
216
+ parent_id: m.parent_id
217
+ })),
218
+ idField: 'id',
219
+ parentIdField: 'parent_id'
220
+ });
221
+
222
+ // 默认展开前两级
223
+ if (treeData.value.length > 0) {
224
+ defaultExpandedKeys.value = treeData.value.map((n) => n.id);
225
+ }
226
+ });
227
+
228
+ // 打开弹窗
229
+ const onOpen = () => {
230
+ if (props.disabled || props.readonly) return;
231
+ modalVisible.value = true;
232
+
233
+ nextTick(() => {
234
+ // 回显选中状态
235
+ const values = Array.isArray(modelValue.value)
236
+ ? modelValue.value
237
+ : modelValue.value ? [modelValue.value] : [];
238
+ tempSelected.value = [...values];
239
+
240
+ if (props.multiple) {
241
+ treeRef.value?.setCheckedKeys(values);
242
+ } else if (values.length > 0) {
243
+ treeRef.value?.setCurrentKey(values[0]);
244
+ }
245
+ });
246
+ };
247
+
248
+ // 单选 - 点击节点
249
+ const onNodeClick = (nodeData) => {
250
+ if (props.multiple) return;
251
+ tempSelected.value = [nodeData[props.valueField]];
252
+ // 单选直接确认
253
+ onConfirm();
254
+ };
255
+
256
+ // 多选 - checkbox 变化
257
+ const onCheck = (nodeData, { checkedKeys }) => {
258
+ tempSelected.value = [...checkedKeys];
259
+ };
260
+
261
+ // 确认选择
262
+ const onConfirm = () => {
263
+ const selectedValues = [...tempSelected.value];
264
+ const selectedData = selectedValues
265
+ .map((v) => flatData.value.find((d) => d[props.valueField] === v))
266
+ .filter(Boolean);
267
+
268
+ if (props.multiple) {
269
+ modelValue.value = selectedValues;
270
+ data.value = selectedData;
271
+ } else {
272
+ modelValue.value = selectedValues[0] || '';
273
+ data.value = selectedData[0] || {};
274
+ }
275
+
276
+ emits('confirm', modelValue.value, data.value);
277
+ modalVisible.value = false;
278
+ };
279
+
280
+ // 删除单个 tag
281
+ const removeTag = (item) => {
282
+ const v = item[props.valueField];
283
+ if (Array.isArray(modelValue.value)) {
284
+ modelValue.value = modelValue.value.filter((i) => i !== v);
285
+ data.value = Array.isArray(data.value)
286
+ ? data.value.filter((d) => d[props.valueField] !== v)
287
+ : [];
288
+ } else {
289
+ modelValue.value = '';
290
+ data.value = {};
291
+ }
292
+ };
293
+
294
+ // 清空所有
295
+ const clearAll = () => {
296
+ modelValue.value = props.multiple ? [] : '';
297
+ data.value = props.multiple ? [] : {};
298
+ };
299
+
300
+ // 清空弹窗内临时选中
301
+ const clearTempSelected = () => {
302
+ tempSelected.value = [];
303
+ treeRef.value?.setCheckedKeys([]);
304
+ };
305
+ </script>
306
+
307
+ <style lang="scss" scoped>
308
+ .sohelp-org-modal-select {
309
+ width: 100%;
310
+ }
311
+
312
+ .select-trigger {
313
+ display: inline-flex;
314
+ align-items: center;
315
+ width: 100%;
316
+ min-height: 30px;
317
+ border: 1px solid var(--el-border-color);
318
+ border-radius: var(--el-border-radius-base);
319
+ box-sizing: border-box;
320
+ cursor: pointer;
321
+ transition: border-color 0.2s;
322
+
323
+ &:hover {
324
+ border-color: var(--el-color-primary);
325
+ }
326
+
327
+ &.is-disabled {
328
+ cursor: not-allowed;
329
+ background-color: var(--el-disabled-bg-color);
330
+ border-color: var(--el-disabled-border-color);
331
+ }
332
+
333
+ .tag-input {
334
+ flex: 1;
335
+ display: flex;
336
+ align-items: center;
337
+ gap: 4px;
338
+ flex-wrap: wrap;
339
+ padding: 2px 6px;
340
+ min-height: 18px;
341
+
342
+ .placeholder-text {
343
+ color: var(--el-text-color-placeholder);
344
+ line-height: 18px;
345
+ }
346
+
347
+ .selection-tag {
348
+ flex-shrink: 0;
349
+ margin: 0;
350
+ max-width: 150px;
351
+ border: 0;
352
+
353
+ :deep(.el-tag__content) {
354
+ overflow: hidden;
355
+ text-overflow: ellipsis;
356
+ white-space: nowrap;
357
+ }
358
+ }
359
+ }
360
+
361
+ .append-icon {
362
+ display: flex;
363
+ align-items: center;
364
+ padding: 0 5px;
365
+ color: var(--el-text-color-placeholder);
366
+
367
+ .clear-icon {
368
+ margin-right: 2px;
369
+ cursor: pointer;
370
+ opacity: 0;
371
+ transition: opacity 0.2s;
372
+ color: #c0c4cc;
373
+
374
+ &:hover {
375
+ color: #909399;
376
+ }
377
+ }
378
+ }
379
+
380
+ &:hover .append-icon .clear-icon {
381
+ opacity: 1;
382
+ }
383
+ }
384
+
385
+ .org-tree-container {
386
+ height: 400px;
387
+ display: flex;
388
+ flex-direction: column;
389
+
390
+ .tree-search {
391
+ margin-bottom: 10px;
392
+ }
393
+
394
+ .tree-scrollbar {
395
+ flex: 1;
396
+ }
397
+ }
398
+
399
+ .modal-footer {
400
+ display: flex;
401
+ justify-content: space-between;
402
+ align-items: center;
403
+ width: 100%;
404
+
405
+ .selected-info {
406
+ display: flex;
407
+ align-items: center;
408
+ gap: 8px;
409
+ }
410
+ }
411
+ </style>
@@ -1,20 +1,33 @@
1
- # SohelpOrgSelect 组织机构选择器(下拉框)
1
+ # SohelpOrgSelect 组织机构选择器
2
2
 
3
- 基于 `sohelp-dyn-select` 封装,预设了组织机构列表的接口地址。
3
+ 基于 `sohelp-dyn-select` 封装的组织机构选择组件,预设了组织列表接口地址和字段映射,开箱即用。
4
4
 
5
5
  ## 基础用法
6
6
 
7
- ```html
8
- <sohelp-org-select v-model="orgId" />
7
+ ```vue
8
+ <template>
9
+ <sohelp-org-select v-model="orgId" />
10
+ </template>
11
+
12
+ <script setup>
13
+ import { ref } from 'vue';
14
+
15
+ const orgId = ref('');
16
+ </script>
9
17
  ```
10
18
 
11
19
  ## 属性 (Props)
12
20
 
13
- | 属性名 | 类型 | 默认值 | 说明 |
14
- | url | String | '/engine/web/org/list' | 数据接口地址 |
15
- | labelField | String | 'org_name' | 显示文本字段 |
16
- | valueField | String | 'id' | 值字段 |
21
+ | 属性名 | 类型 | 默认值 | 必填 | 说明 |
22
+ | :--- | :--- | :--- | :--- | :--- |
23
+ | modelValue | String / Number / Array | `''` | | 绑定值(v-model) |
24
+ | datasource | Array / Function / Promise | `undefined` | | 自定义数据源,优先级高于 url |
25
+ | url | String | `'/engine/web/org/list'` | 否 | 组织机构列表接口地址 |
26
+ | labelField | String | `'org_name'` | 否 | 显示文本字段名 |
27
+ | valueField | String | `'id'` | 否 | 值字段名 |
28
+
29
+ > 同时支持所有 `sohelp-dyn-select` 和 `el-select` 原生属性,通过 `v-bind="$attrs"` 透传。
17
30
 
18
- ## 其他
31
+ ## 公开方法 (Expose)
19
32
 
20
- 支持 `sohelp-dyn-select` 的所有属性。
33
+ 组件通过 `defineExpose({})` 暴露,当前无额外公开方法。