vue3-components-plus 3.0.19 → 3.0.21

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 (73) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +22 -176
  3. package/dist/ComponentDemo/DialogDemo.vue +2 -2
  4. package/dist/ComponentDemo/FormDemo.vue +133 -6
  5. package/dist/ComponentDemo/NsTableDemo/index.vue +29 -31
  6. package/dist/api/types.d.ts +116 -116
  7. package/dist/vue3-components-plus.css +1 -1
  8. package/dist/vue3-components-plus.d.ts +0 -2
  9. package/dist/vue3-components-plus.js +457 -2231
  10. package/dist/vue3-components-plus.umd.cjs +1 -1
  11. package/package.json +1 -1
  12. package/vue3-components-plus.d.ts +0 -2
  13. package/dist/ComponentDemo/DynamicFormCascadeAsyncDemo.vue +0 -337
  14. package/dist/ComponentDemo/DynamicFormCascadeDemo.vue +0 -263
  15. package/dist/ComponentDemo/DynamicFormPlusDemo.vue +0 -176
  16. package/dist/ComponentDemo/FormDemo copy.vue +0 -714
  17. package/dist/ComponentDemo/TestFormConfig.js +0 -129
  18. package/dist/ComponentDemo/VideoDemo.vue +0 -303
  19. package/dist/cdn/ezuikit/ezuikit.js +0 -27
  20. package/dist/cdn/ezuikit/ezuikit_static/PlayCtrlWasm/playCtrl1/HasSIMD/Decoder.js +0 -168
  21. package/dist/cdn/ezuikit/ezuikit_static/PlayCtrlWasm/playCtrl1/NoSIMD/Decoder.js +0 -168
  22. package/dist/cdn/ezuikit/ezuikit_static/PlayCtrlWasm/playCtrl3/hasWorker/HasSIMD/Decoder.js +0 -21
  23. package/dist/cdn/ezuikit/ezuikit_static/PlayCtrlWasm/playCtrl3/hasWorker/HasSIMD/Decoder.wasm +0 -0
  24. package/dist/cdn/ezuikit/ezuikit_static/PlayCtrlWasm/playCtrl3/hasWorker/HasSIMD/Decoder.worker.js +0 -1
  25. package/dist/cdn/ezuikit/ezuikit_static/PlayCtrlWasm/playCtrl3/hasWorker/NoSIMD/Decoder.js +0 -21
  26. package/dist/cdn/ezuikit/ezuikit_static/PlayCtrlWasm/playCtrl3/hasWorker/NoSIMD/Decoder.wasm +0 -0
  27. package/dist/cdn/ezuikit/ezuikit_static/PlayCtrlWasm/playCtrl3/hasWorker/NoSIMD/Decoder.worker.js +0 -1
  28. package/dist/cdn/ezuikit/ezuikit_static/PlayCtrlWasm/playCtrl3/noWorker/Decoder.js +0 -21
  29. package/dist/cdn/ezuikit/ezuikit_static/PlayCtrlWasm/playCtrl3/noWorker/Decoder.wasm +0 -0
  30. package/dist/cdn/ezuikit/ezuikit_static/css/component.css +0 -1257
  31. package/dist/cdn/ezuikit/ezuikit_static/css/inspectTheme.css +0 -354
  32. package/dist/cdn/ezuikit/ezuikit_static/css/theme copy.css +0 -126
  33. package/dist/cdn/ezuikit/ezuikit_static/css/theme.css +0 -140
  34. package/dist/cdn/ezuikit/ezuikit_static/imgs/bg.png +0 -0
  35. package/dist/cdn/ezuikit/ezuikit_static/imgs/bg.svg +0 -33
  36. package/dist/cdn/ezuikit/ezuikit_static/imgs/empty.png +0 -0
  37. package/dist/cdn/ezuikit/ezuikit_static/imgs/end.png +0 -0
  38. package/dist/cdn/ezuikit/ezuikit_static/imgs/fallback.svg +0 -52
  39. package/dist/cdn/ezuikit/ezuikit_static/imgs/start.png +0 -0
  40. package/dist/cdn/ezuikit/ezuikit_static/rec/datepicker.js +0 -1522
  41. package/dist/cdn/ezuikit/ezuikit_static/rec/datepicker.min.css +0 -36
  42. package/dist/cdn/ezuikit/ezuikit_static/rec/datepicker.zh-CN.js +0 -19
  43. package/dist/cdn/ezuikit/ezuikit_static/rec/jquery.min.js +0 -2
  44. package/dist/cdn/ezuikit/ezuikit_static/speed/speed.css +0 -145
  45. package/dist/cdn/ezuikit/ezuikit_static/talk/adapeter.js +0 -5497
  46. package/dist/cdn/ezuikit/ezuikit_static/talk/janus.js +0 -3507
  47. package/dist/cdn/ezuikit/ezuikit_static/talk/tts-v4.js +0 -343
  48. package/dist/cdn/ezuikit.js +0 -27
  49. package/dist/cdn/h5player/h5player.min.js +0 -313
  50. package/dist/cdn/h5player/playctrl1/DecodeWorker.js +0 -642
  51. package/dist/cdn/h5player/playctrl1/Decoder.js +0 -1
  52. package/dist/cdn/h5player/playctrl1simd/DecodeWorker.js +0 -642
  53. package/dist/cdn/h5player/playctrl1simd/Decoder.js +0 -1
  54. package/dist/cdn/h5player/playctrl2/Decoder.js +0 -21
  55. package/dist/cdn/h5player/playctrl2/Decoder.wasm +0 -0
  56. package/dist/cdn/h5player/playctrl2/Decoder.worker.js +0 -1
  57. package/dist/cdn/h5player/playctrl3/Decoder.js +0 -21
  58. package/dist/cdn/h5player/playctrl3/Decoder.wasm +0 -0
  59. package/dist/cdn/h5player/playctrl3/Decoder.worker.js +0 -1
  60. package/dist/cdn/h5player/talk/AudioInterCom.js +0 -21
  61. package/dist/cdn/h5player/talk/AudioInterCom.wasm +0 -0
  62. package/dist/cdn/h5player/talkW/AudioInterCom.js +0 -21
  63. package/dist/cdn/h5player/talkW/AudioInterCom.wasm +0 -0
  64. package/dist/cdn/h5player/talkW/AudioInterCom.worker.js +0 -1
  65. package/dist/cdn/h5player/transform/libSystemTransform.js +0 -6525
  66. package/dist/cdn/h5player/transform/libSystemTransform.wasm +0 -0
  67. package/dist/cdn/h5player/transform/systemTransform-worker.js +0 -120
  68. package/dist/cdn/md5.js +0 -254
  69. package/dist/js/EasyPlayer-decode.js +0 -1
  70. package/dist/js/EasyPlayer-lib.js +0 -1
  71. package/dist/js/EasyPlayer-pro.js +0 -1
  72. package/dist/js/EasyPlayer-pro.wasm +0 -0
  73. package/dist/js/EasyPlayer-snap.wasm +0 -0
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 75535596
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 75535596
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -10,10 +10,6 @@ vue3-components-plus 是一个功能丰富的 Vue 3 企业级组件库,提供
10
10
 
11
11
  ## 📸 部分组件预览
12
12
 
13
- ### NsVideo
14
-
15
- ![emh1eWluZw==](https://tc-cdn.processon.com/po/605c2da663768970077b1422-695db61dbffe264705f9b033)
16
-
17
13
  ### NsDialog
18
14
 
19
15
  ![emh1eWluZw==](https://tc-cdn.processon.com/po/605c2da663768970077b1422-695db64084aa6b1f690b64aa)
@@ -52,7 +48,6 @@ vue3-components-plus 是一个功能丰富的 Vue 3 企业级组件库,提供
52
48
 
53
49
  - ✅ **NsOffice**: 办公文档统一预览(Excel/PDF/Word)
54
50
  - ✅ **NsForm**: 动态表单生成和管理
55
- - ✅ **NsVideo**: 专业视频播放器
56
51
  - ✅ **NsDialog**: 灵活弹窗对话框
57
52
  - ✅ **NsPdf**: PDF文档预览和搜索
58
53
  - ✅ **NsExcel**: Excel预览和编辑
@@ -235,7 +230,7 @@ app.use(NsComponents)
235
230
 
236
231
  ## 📋 组件列表
237
232
 
238
- ### 1. NsOffice - 办公文档统一预览组件
233
+ ### NsOffice - 办公文档统一预览组件
239
234
 
240
235
  支持格式:Excel(.xlsx/.xls)、PDF(.pdf)、Word(.docx/.doc)
241
236
 
@@ -268,7 +263,7 @@ officeRef.value?.refresh()
268
263
  </script>
269
264
  ```
270
265
 
271
- ### 2. NsForm - 动态表单组件
266
+ ### NsForm - 动态表单组件
272
267
 
273
268
  ```vue
274
269
  <template>
@@ -306,164 +301,7 @@ const isValid = formRef.value?.validate()
306
301
  </script>
307
302
  ```
308
303
 
309
- ### 3. NsVideo - 视频播放组件
310
-
311
- ```vue
312
- <template>
313
- <NsVideo
314
- ref="nsVideoRef"
315
- v-bind="videoData"
316
- v-on="videoEvent"
317
- @changeSplit='changeSplitHandler'
318
- >
319
- <!-- 自定义插槽 -->
320
- <template #video-tree><span>左侧树-自定义插槽</span></template>
321
- <template #video-player-head><span>播放区域头部-自定义插槽</span></template>
322
- <template #video-player-view><span>播放区域主体-自定义插槽</span></template>
323
- <template #video-player-foot><span>播放区域底部控制按钮-自定义插槽</span></template>
324
- <template #video-player-cover><span>播放器canvas画布区域(除head+foot+tree区域)</span></template>
325
- </NsVideo>
326
- </template>
327
-
328
- <script setup>
329
- import { ref } from 'vue'
330
- import { NsVideo } from 'vue3-components-plus'
331
-
332
- const nsVideoRef = ref()
333
-
334
- // 视频配置数据
335
- const videoData = {
336
- // 显示视频关闭按钮
337
- showClose: true,
338
- // 是否支持全屏
339
- hasFullScreen: true,
340
- // 显示树
341
- showTree: true,
342
- // 树数据
343
- treeData: [
344
- {
345
- id: '1',
346
- label: '分组1',
347
- children: [
348
- {
349
- videoModel: 'easyplayer',
350
- id: '111',
351
- label: '视频A',
352
- url: 'ws://example.com/rtp/video.live.flv',
353
- deviceId: 'a1',
354
- channelId: 'a11',
355
- icontype: 'on',
356
- }
357
- ]
358
- }
359
- ],
360
- // 树节点对应的key
361
- treeNodeKey: 'id',
362
- // 树节点展开的key
363
- treeExpandedKeys: ['11'],
364
- // 树节点属性
365
- treeOptions: {
366
- icontype: 'icontype',
367
- background: 'background',
368
- videoUrlKey: 'url',
369
- children: 'children',
370
- label: 'label',
371
- },
372
- // 获取、设置打开的播放视频信息
373
- videoInfos: [
374
- {
375
- index: 0,
376
- url: 'https://example.com/video.flv',
377
- info: {
378
- videoModel: 'easyplayer',
379
- id: 'video1',
380
- url: 'https://example.com/video.flv',
381
- deviceId: 'c1',
382
- channelId: 'c11',
383
- },
384
- }
385
- ],
386
- // 单点播放
387
- videoSingleUrl: false,
388
- // 已经在播放的是否关闭后再点击打开
389
- videoSingleClose: false,
390
- // 播放模式: 1: 单击,2: 双击
391
- videoPlayModel: 1,
392
- // 分屏模式: 1: 单屏, 2: 四屏, 3: 九屏
393
- videoSplitType: 1,
394
- // 显示分屏按钮
395
- showVideoSplit: true,
396
- // 显示方向控制按钮
397
- showVideoCtrls: false,
398
- // 禁止控制按钮默认请求行为
399
- stopVideoCtrlMethods: true,
400
- // 单个视频错误最大次数
401
- videoErrorMaxCount: 3,
402
- // easyplayer配置
403
- videoConfig: {
404
- MSE: true,
405
- WCS: true,
406
- WASM: true,
407
- WASMSIMD: true,
408
- isLive: true,
409
- hasAudio: false,
410
- stretch: false
411
- },
412
- // 事件回调
413
- treeClick: () => { console.log('点击树节点') },
414
- treeDBClick: () => { console.log('双击树节点') },
415
- treeRightMenu: () => { console.log('右键树菜单') },
416
- treeExpand: () => { console.log('展开树节点') },
417
- videoError: () => { console.log('视频错误') },
418
- }
419
-
420
- // 视频操作事件
421
- const videoEvent = {
422
- videoOriginalInfo: (info) => { console.log('视频原始信息:', info) },
423
- up: () => { console.log('向上移动') },
424
- down: () => { console.log('向下移动') },
425
- left: () => { console.log('向左移动') },
426
- right: () => { console.log('向右移动') },
427
- zoomin: () => { console.log('放大') },
428
- zoomout: () => { console.log('缩小') },
429
- stop: () => { console.log('停止') },
430
- speed: () => { console.log('设置速度') },
431
- speak: () => { console.log('开始说话') },
432
- scan: () => { console.log('扫描') },
433
- cruise: () => { console.log('巡航') },
434
- call: () => { console.log('调用') },
435
- }
436
-
437
- // 获取原始视频信息
438
- const videoInfo = nsVideoRef.value?.getOriginalInfo()
439
-
440
- // 设置视频URL
441
- nsVideoRef.value?.setVideoUrl('ws://example.com/rtp/video.live.flv', false, 1, {
442
- videoModel: 'easyplayer'
443
- })
444
-
445
- // 移除视频
446
- nsVideoRef.value?.removeVideo(1, true)
447
-
448
- // 分屏变化回调
449
- function changeSplitHandler(num) {
450
- console.log('分屏模式:', num)
451
- }
452
- </script>
453
- ```
454
-
455
- **前置准备:**
456
-
457
- 1. 复制播放器资源:`js文件夹` 到 `public/` 目录中
458
- 2. 复制播放器资源:`cdn文件夹` 到 `public/` 目录中
459
- 3. 在 `index.html` 中引入:
460
-
461
- ```html
462
- <script src="./js/EasyPlayer-pro.js"></script>
463
- <script src="./cdn/h5player/h5player.min.js"></script>
464
- ```
465
-
466
- ### 4. NsDialog - 弹出框组件
304
+ ### NsDialog - 弹出框组件
467
305
 
468
306
  ```javascript
469
307
  import { NsDialog, closeAllNsDialog } from 'vue3-components-plus'
@@ -533,7 +371,7 @@ NsDialog({
533
371
  closeAllNsDialog()
534
372
  ```
535
373
 
536
- ### 5. NsPdf - PDF预览组件
374
+ ### NsPdf - PDF预览组件
537
375
 
538
376
  ```vue
539
377
  <template>
@@ -553,7 +391,7 @@ function searchKeyword() {
553
391
  </script>
554
392
  ```
555
393
 
556
- ### 6. NsExcel - Excel预览/编辑组件
394
+ ### NsExcel - Excel预览/编辑组件
557
395
 
558
396
  ```vue
559
397
  <template>
@@ -561,7 +399,7 @@ function searchKeyword() {
561
399
  </template>
562
400
  ```
563
401
 
564
- ### 7. NsWord - Word预览组件
402
+ ### NsWord - Word预览组件
565
403
 
566
404
  ```vue
567
405
  <template>
@@ -569,7 +407,7 @@ function searchKeyword() {
569
407
  </template>
570
408
  ```
571
409
 
572
- ### 8. NsImage - 图片预览组件
410
+ ### NsImage - 图片预览组件
573
411
 
574
412
  ```vue
575
413
  <template>
@@ -577,7 +415,7 @@ function searchKeyword() {
577
415
  </template>
578
416
  ```
579
417
 
580
- ### 9. NsMD - Markdown编辑器组件
418
+ ### NsMD - Markdown编辑器组件
581
419
 
582
420
  ```vue
583
421
  <template>
@@ -585,7 +423,7 @@ function searchKeyword() {
585
423
  </template>
586
424
  ```
587
425
 
588
- ### 10. NsSaturationline - 饱和度线组件
426
+ ### NsSaturationline - 饱和度线组件
589
427
 
590
428
  ```vue
591
429
  <template>
@@ -593,7 +431,7 @@ function searchKeyword() {
593
431
  </template>
594
432
  ```
595
433
 
596
- ### 11. NsAutoScreen - 自适应屏幕工具
434
+ ### NsAutoScreen - 自适应屏幕工具
597
435
 
598
436
  ```javascript
599
437
  import { autoScaleInit, sacle_x, sacle_y } from 'vue3-components-plus'
@@ -999,12 +837,11 @@ app.mount('#app')
999
837
 
1000
838
  ```javascript
1001
839
  // 按需引入组件和函数
1002
- import { NsForm, NsVideo, get, post, autoScaleInit } from 'vue3-components-plus'
840
+ import { NsForm, get, post, autoScaleInit } from 'vue3-components-plus'
1003
841
 
1004
842
  // 在组件中使用
1005
843
  const app = createApp(App)
1006
844
  app.use(NsForm)
1007
- app.use(NsVideo)
1008
845
 
1009
846
  autoScaleInit(document.querySelector('body'), {
1010
847
  designWidth: 1920,
@@ -1016,10 +853,19 @@ autoScaleInit(document.querySelector('body'), {
1016
853
  ## 更新日志
1017
854
 
1018
855
  ```text
1019
- version: 3.0.18
856
+ version: 3.0.21
857
+ 日期: 2026-03-17
858
+ 更新内容:
859
+ 1. NsTableContainer刷新后数据偶尔不更新的bug
860
+ ```
861
+
862
+
863
+ ```text
864
+ version: 3.0.20
1020
865
  日期: 2026-03-17
1021
866
  更新内容:
1022
- 1. NsTableContainer按钮禁用颜色修改
867
+ 1. NsTableContainer增加插槽#page-content,替换表格区域
868
+ 2. NsTableContainer按钮禁用颜色修改
1023
869
  ```
1024
870
 
1025
871
  ```text
@@ -13,7 +13,7 @@
13
13
  </div>
14
14
  </template>
15
15
  <script setup lang="ts">
16
- import VideoDemo from '@/views/VideoDemo.vue'
16
+ import FormDemo from '@/views/FormDemo.vue'
17
17
  import { onMounted, onUnmounted, ref } from 'vue'
18
18
 
19
19
  // 扩展Window接口
@@ -37,7 +37,7 @@ function openDialog(data = {}) {
37
37
  class: 'xxx',
38
38
  title: '测试',
39
39
  // 任何组件添加 $emit('close') 时,会触发关闭弹出框事件
40
- dom: VideoDemo, // 也可以通过异步方式:import("@/views/VideoDemo.vue") 和 () => import("@/views/VideoDemo.vue")
40
+ dom: FormDemo, // 也可以通过异步方式:import("@/views/FormDemo.vue") 和 () => import("@/views/FormDemo.vue")
41
41
  domCompleted: (domRef: any)=>{
42
42
  // dom加载完成或触发函数,domRef为dom实例可以执行defineExpose暴露出的函数
43
43
  console.log('组件加载完成,domRef:', domRef)
@@ -66,6 +66,20 @@
66
66
  gapV="10px"
67
67
  ></NsForm>
68
68
  </NsFormTitle>
69
+ <NsFormTitle title="文件上传">
70
+ <NsForm
71
+ ref="rowUploadRef"
72
+ :readOnly="state.readOnly"
73
+ backgroundColor="#fff"
74
+ :model="state.model"
75
+ :rows="state.rowsUpload"
76
+ formPropKey="rowsUpload"
77
+ labelColor="#606266"
78
+ labelWidth="150"
79
+ gapH="20px"
80
+ gapV="10px"
81
+ ></NsForm>
82
+ </NsFormTitle>
69
83
  </el-form>
70
84
  </div>
71
85
  </div>
@@ -73,9 +87,9 @@
73
87
 
74
88
  <script setup lang="ts">
75
89
  import { h, onMounted, reactive, ref } from 'vue'
76
- import { cloneDeep } from 'lodash-es'
77
- import { nextTick } from 'vue'
78
90
  import { ElMessage } from 'element-plus'
91
+ import type { UploadRequestOptions, UploadFile, UploadFiles } from 'element-plus'
92
+ import { useFileUpload } from '../../packages/components/NsForm/uploadHook'
79
93
 
80
94
  // ------------全局的函数(不需要引入直接使用)------------
81
95
  // import { getAllFormKvData, getAllFormNodeByKey, getAllFormNodeRefByKey } from "vue3-components-plus";
@@ -115,8 +129,9 @@ const row1Ref = ref()
115
129
  const row2Ref = ref()
116
130
  const row3Ref = ref()
117
131
  const row4Ref = ref()
132
+ const rowUploadRef = ref()
118
133
 
119
- const state = reactive({
134
+ const state = reactive<any>({
120
135
  formData: {},
121
136
  readOnly: props.readOnly,
122
137
  model: props.readOnly ? '' : 'vertical',
@@ -481,10 +496,100 @@ const state = reactive({
481
496
  },
482
497
  ],
483
498
  ],
499
+ uploadFileList: [] as UploadFiles,
500
+ rowsUpload: [
501
+ [
502
+ {
503
+ key: 'upload_file',
504
+ label: '上传模型文件',
505
+ value: [],
506
+ component: 'ElUpload',
507
+ params: {
508
+ drag: true,
509
+ multiple: true,
510
+ limit: 2,
511
+ action: '#',
512
+ accept: '.txt,.md,.json,.jpg,.png,.pdf',
513
+ disabled: props.readOnly,
514
+ fileList: [],
515
+ httpRequest: mockUploadRequest,
516
+ rules: [
517
+ {
518
+ required: true,
519
+ message: '请上传模型或文档',
520
+ trigger: 'change',
521
+ },
522
+ ],
523
+ },
524
+ slots: {
525
+ default: () =>
526
+ h('div', { class: 'upload-trigger' }, [
527
+ h('p', { class: 'upload-title' }, '点击或拖拽上传'),
528
+ h('p', { class: 'upload-sub' }, '自动模拟上传成功,最多2个文件'),
529
+ ]),
530
+ tip: () => h('div', { class: 'el-upload__tip' }, '仅演示,文件信息会写入表单数据'),
531
+ },
532
+ events: {
533
+ success: handleUploadSuccess,
534
+ change: handleUploadChange,
535
+ remove: handleUploadRemove,
536
+ },
537
+ },
538
+ ],
539
+ ],
484
540
  })
485
541
 
486
- function changeHandler(v) {
487
- ElMessage.info(v)
542
+ state.rowsUpload[0][0].params.fileList = state.uploadFileList
543
+
544
+ const uploadFieldKey = 'upload_file'
545
+ const uploadFormKey = 'rowsUpload'
546
+ const { handleRemoveFile, handleFileSuccessFile, handleCheckFileRequire } = useFileUpload(state)
547
+
548
+ function mockUploadRequest(options: UploadRequestOptions) {
549
+ const { file, onSuccess, onError } = options
550
+ return new Promise((resolve, reject) => {
551
+ const timer = setTimeout(() => {
552
+ const response = {
553
+ code: 0,
554
+ data: {
555
+ fileName: file.name,
556
+ filePath: URL.createObjectURL(file),
557
+ fileSize: (file as any).size || (file as any).raw?.size || 0,
558
+ },
559
+ message: 'ok',
560
+ }
561
+ onSuccess?.(response as any)
562
+ resolve(response)
563
+ }, 400)
564
+
565
+ ;(options as any).abort = () => {
566
+ clearTimeout(timer)
567
+ const error = new Error('已取消') as any
568
+ onError?.(error)
569
+ reject(error)
570
+ }
571
+ })
572
+ }
573
+
574
+ function handleUploadSuccess(response: unknown, file: UploadFile, fileList: UploadFiles) {
575
+ state.uploadFileList.splice(0, state.uploadFileList.length, ...(fileList || []))
576
+ handleFileSuccessFile(response as any, file as any, fileList as any, uploadFieldKey, state.rowsUpload)
577
+ handleCheckFileRequire(state.rowsUpload, uploadFieldKey, formRef, uploadFormKey)
578
+ }
579
+
580
+ function handleUploadChange(_file: UploadFile, fileList: UploadFiles) {
581
+ state.uploadFileList.splice(0, state.uploadFileList.length, ...(fileList || []))
582
+ handleCheckFileRequire(state.rowsUpload, uploadFieldKey, formRef, uploadFormKey)
583
+ }
584
+
585
+ function handleUploadRemove(file: UploadFile, fileList: UploadFiles) {
586
+ state.uploadFileList.splice(0, state.uploadFileList.length, ...(fileList || []))
587
+ handleRemoveFile(file as any, fileList as any, uploadFieldKey, state.rowsUpload)
588
+ handleCheckFileRequire(state.rowsUpload, uploadFieldKey, formRef, uploadFormKey)
589
+ }
590
+
591
+ function changeHandler(v: boolean) {
592
+ ElMessage.info(v ? '启用' : '禁用')
488
593
  }
489
594
 
490
595
  function detAreaModeChange(value: any) {
@@ -527,7 +632,8 @@ async function getFormData() {
527
632
  const data2 = row2Ref.value?.getFormKvData?.()
528
633
  const data3 = row3Ref.value?.getFormKvData?.()
529
634
  const data4 = row4Ref.value?.getFormKvData?.()
530
- const data = { ...data1, ...data2, ...data3, ...data4 }
635
+ const dataUpload = rowUploadRef.value?.getFormKvData?.()
636
+ const data = { ...data1, ...data2, ...data3, ...data4, ...dataUpload }
531
637
  state.formData = data
532
638
  ElMessage.success('表单校验成功')
533
639
  return data
@@ -542,6 +648,8 @@ async function resetFormData() {
542
648
  row2Ref.value?.resetForm?.()
543
649
  row3Ref.value?.resetForm?.()
544
650
  row4Ref.value?.resetForm?.()
651
+ rowUploadRef.value?.resetForm?.()
652
+ state.uploadFileList.splice(0, state.uploadFileList.length)
545
653
  setTimeout(() => {
546
654
  // 重置表单验证状态
547
655
  formRef.value?.clearValidate?.()
@@ -567,16 +675,20 @@ async function getDetail() {
567
675
  region: 'haidian,pudong', //['beijing', 'haidian'],
568
676
  department: ['company', 'tech', 'frontend'],
569
677
  single_level_cascader: 'shanghai',
678
+ upload_file: [],
570
679
  }
571
680
  row1Ref.value?.resetForm()
572
681
  row2Ref.value?.resetForm()
573
682
  row3Ref.value?.resetForm()
574
683
  row4Ref.value?.resetForm()
684
+ rowUploadRef.value?.resetForm()
685
+ state.uploadFileList.splice(0, state.uploadFileList.length)
575
686
  setTimeout(() => {
576
687
  row1Ref.value?.setFormData?.(res)
577
688
  row2Ref.value?.setFormData?.(res)
578
689
  row3Ref.value?.setFormData?.(res)
579
690
  row4Ref.value?.setFormData?.(res)
691
+ rowUploadRef.value?.setFormData?.(res)
580
692
  }, 10)
581
693
  return
582
694
  // 特殊处理
@@ -700,6 +812,21 @@ onMounted(() => {
700
812
  }
701
813
  }
702
814
 
815
+ .upload-trigger {
816
+ padding: 12px 8px;
817
+ text-align: center;
818
+ color: #606266;
819
+ .upload-title {
820
+ margin: 0 0 4px;
821
+ font-weight: 600;
822
+ }
823
+ .upload-sub {
824
+ margin: 0;
825
+ font-size: 12px;
826
+ color: #909399;
827
+ }
828
+ }
829
+
703
830
  @media (max-width: 768px) {
704
831
  .config-select {
705
832
  width: 100% !important;
@@ -27,6 +27,10 @@
27
27
  @add="handleAdd"
28
28
  @selection-change="handleSelectionChange"
29
29
  >
30
+ <!-- 替代表格插槽 -->
31
+ <!-- <template #page-content>
32
+ <div>替代表格插槽</div>
33
+ </template> -->
30
34
  <!-- 自定义状态列 -->
31
35
  <template #status="{ row }">
32
36
  <el-tag :type="getStatusType(row.status)">
@@ -84,7 +88,15 @@
84
88
 
85
89
  <script setup lang="ts">
86
90
  import { Delete, Edit, View } from '@element-plus/icons-vue'
87
- import { ElDatePicker, ElInput, ElMessage, ElMessageBox, ElPopconfirm, ElSelect, ElSwitch } from 'element-plus'
91
+ import {
92
+ ElDatePicker,
93
+ ElInput,
94
+ ElMessage,
95
+ ElMessageBox,
96
+ ElPopconfirm,
97
+ ElSelect,
98
+ ElSwitch,
99
+ } from 'element-plus'
88
100
  import { onMounted, ref } from 'vue'
89
101
  import { fetchDepartmentOptions, fetchStatusOptions, filterUsers, mockUsers } from './mockData.js'
90
102
 
@@ -104,37 +116,23 @@ const tableData = ref([])
104
116
  const total = ref(0)
105
117
 
106
118
  const searchItems = ref([
107
- {
108
- prop: "mounth",
109
- label: "归属月",
119
+ {
120
+ prop: 'mounth',
121
+ label: '归属月',
110
122
  span: 6,
111
123
  component: ElSelect,
112
124
  attrs: {
113
- placeholder: "请选择归属月",
125
+ placeholder: '请选择归属月',
114
126
  clearable: true,
115
- options: [
116
- {
117
- value: "Option1",
118
- label: "Option1"
119
- },
120
- {
121
- value: "Option2",
122
- label: "Option2",
123
- },
124
- {
125
- value: "Option3",
126
- label: "Option3"
127
- },
128
- {
129
- value: "Option4",
130
- label: "Option4",
131
- },
132
- {
133
- value: "Option5",
134
- label: "Option5"
135
- }
136
- ]
137
- }
127
+ type: 'month',
128
+ },
129
+ children: Array.from({ length: 12 }, (_, i) => {
130
+ return {
131
+ label: `${i + 1}月`,
132
+ value: String(i + 1),
133
+ }
134
+ }),
135
+ defaultValue: '3', // 默认选中3月
138
136
  },
139
137
  {
140
138
  prop: 'username',
@@ -279,7 +277,7 @@ const columns = ref([
279
277
  'min-width': 100, // 最小宽度
280
278
  },
281
279
  { prop: 'gender', label: '性别', slot: 'gender', width: 80 },
282
- ]
280
+ ],
283
281
  },
284
282
  // 多级表头示例:组织信息
285
283
  {
@@ -287,7 +285,7 @@ const columns = ref([
287
285
  children: [
288
286
  { prop: 'department', label: '部门', slot: 'department', width: 120 },
289
287
  { prop: 'status', label: '状态', slot: 'status', width: 100 },
290
- ]
288
+ ],
291
289
  },
292
290
  // 多级表头示例:联系方式
293
291
  {
@@ -313,7 +311,7 @@ const columns = ref([
313
311
  return row.email.includes(value)
314
312
  },
315
313
  },
316
- ]
314
+ ],
317
315
  },
318
316
  {
319
317
  prop: 'createTime',