@done-coding/admin-core 0.21.0 → 0.22.0
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.
- package/es/components/app-layout/AppLayout.vue.mjs +1 -1
- package/es/components/app-layout/AppLayout.vue2.mjs +51 -6
- package/es/components/app-layout/AppPage.vue.mjs +1 -1
- package/es/components/app-layout/AppPage.vue2.mjs +16 -9
- package/es/components/display/ShadowClone.vue.mjs +32 -0
- package/es/components/display/ShadowClone.vue2.mjs +4 -0
- package/es/components/display/use-shadow-target.mjs +76 -0
- package/es/components/form/FormActiveFilter.vue.mjs +7 -0
- package/es/components/form/FormActiveFilter.vue2.mjs +62 -0
- package/es/components/form/FormInput.vue.mjs +27 -0
- package/es/components/form/FormInput.vue2.mjs +4 -0
- package/es/components/form/FormInputShadowClone.vue.mjs +47 -0
- package/es/components/form/FormInputShadowClone.vue2.mjs +4 -0
- package/es/components/form/FormMain.vue.mjs +1 -1
- package/es/components/form/FormMain.vue2.mjs +5 -25
- package/es/components/form/FormSelectShadowClone.vue.mjs +57 -0
- package/es/components/form/FormSelectShadowClone.vue2.mjs +4 -0
- package/es/components/form/FormShadowCloneDispatch.vue.mjs +7 -0
- package/es/components/form/FormShadowCloneDispatch.vue2.mjs +74 -0
- package/es/components/form/FormSortSwitch.vue.mjs +7 -0
- package/es/components/form/FormSortSwitch.vue2.mjs +188 -0
- package/es/components/form/FormSortSwitchGroup.vue.mjs +7 -0
- package/es/components/form/FormSortSwitchGroup.vue2.mjs +68 -0
- package/es/components/form/FormUpload.vue.mjs +8 -0
- package/es/components/form/FormUpload.vue2.mjs +112 -0
- package/es/components/form/FormUploadAudio.vue.mjs +7 -0
- package/es/components/form/FormUploadAudio.vue2.mjs +88 -0
- package/es/components/form/FormUploadCard.vue.mjs +7 -0
- package/es/components/form/FormUploadCard.vue2.mjs +56 -0
- package/es/components/form/FormUploadFileInfo.vue.mjs +7 -0
- package/es/components/form/FormUploadFileInfo.vue2.mjs +36 -0
- package/es/components/form/FormUploadImage.vue.mjs +7 -0
- package/es/components/form/FormUploadImage.vue2.mjs +77 -0
- package/es/components/form/FormUploadTrigger.vue.mjs +7 -0
- package/es/components/form/FormUploadTrigger.vue2.mjs +34 -0
- package/es/components/form/FormUploadVideo.vue.mjs +7 -0
- package/es/components/form/FormUploadVideo.vue2.mjs +74 -0
- package/es/components/form/use-password-autofill.mjs +34 -0
- package/es/components/form/use-upload-state.mjs +98 -0
- package/es/components/list-layout/ListLayout.vue.mjs +1 -1
- package/es/components/list-layout/ListLayout.vue2.mjs +36 -9
- package/es/components/list-layout/use-active-filter.mjs +133 -0
- package/es/components/modal/ImagePreviewTrigger.vue.mjs +34 -0
- package/es/components/modal/ImagePreviewTrigger.vue2.mjs +4 -0
- package/es/components/modal/ModalImagePreview.vue.mjs +52 -0
- package/es/components/modal/ModalImagePreview.vue2.mjs +4 -0
- package/es/components/modal/ModalPorter.vue.mjs +49 -2
- package/es/components/modal/ModalShelf.vue.mjs +13 -4
- package/es/components/modal/ModalVideoPreview.vue.mjs +8 -0
- package/es/components/modal/ModalVideoPreview.vue2.mjs +93 -0
- package/es/components/modal/VideoPreviewTrigger.vue.mjs +7 -0
- package/es/components/modal/VideoPreviewTrigger.vue2.mjs +96 -0
- package/es/components/modal/modal-global-keys.mjs +11 -0
- package/es/components/modal/modal-global-symbol.mjs +6 -0
- package/es/components/modal/modal-shelf-core.mjs +53 -4
- package/es/components/table/TableEmpty.vue.mjs +36 -0
- package/es/components/table/TableEmpty.vue2.mjs +4 -0
- package/es/components/table/TableMain.vue.mjs +1 -1
- package/es/components/table/TableMain.vue2.mjs +21 -8
- package/es/components/view-layout/ViewLayout.vue.mjs +1 -1
- package/es/components/view-layout/ViewLayout.vue2.mjs +1 -1
- package/es/hooks/menus-dispatch.mjs +8 -3
- package/es/index.mjs +127 -91
- package/es/style.css +412 -33
- package/package.json +2 -2
- package/src/components/app-layout/docs/README-AppLayout.md +48 -0
- package/src/components/app-layout/docs/README-AppPage.md +40 -2
- package/src/components/display/README.md +9 -3
- package/src/components/display/docs/README-ShadowClone.md +75 -0
- package/src/components/form/README.md +15 -2
- package/src/components/form/docs/README-FormActiveFilter.md +57 -0
- package/src/components/form/docs/README-FormInput.md +66 -0
- package/src/components/form/docs/README-FormSelect.md +77 -0
- package/src/components/form/docs/README-FormShadowCloneDispatch.md +65 -0
- package/src/components/form/docs/README-FormSortSwitchGroup.md +131 -0
- package/src/components/form/docs/README-FormUpload.md +108 -0
- package/src/components/form/docs/README-FormUploadAudio.md +80 -0
- package/src/components/form/docs/README-FormUploadImage.md +83 -0
- package/src/components/form/docs/README-FormUploadVideo.md +82 -0
- package/src/components/list-layout/README.md +6 -2
- package/src/components/list-layout/docs/README-ListLayout.md +1 -0
- package/src/components/modal/README.md +7 -2
- package/src/components/modal/docs/README-ImagePreviewTrigger.md +78 -0
- package/src/components/modal/docs/README-ModalImagePreview.md +78 -0
- package/src/components/modal/docs/README-ModalPorter.md +10 -0
- package/src/components/modal/docs/README-ModalShelf.md +11 -2
- package/src/components/modal/docs/README-ModalVideoPreview.md +89 -0
- package/src/components/modal/docs/README-VideoPreviewTrigger.md +91 -0
- package/src/components/table/docs/README-TableMain.md +3 -0
- package/types/components/app-layout/AppLayout.vue.d.ts +31 -1
- package/types/components/app-layout/AppPage.vue.d.ts +7 -2
- package/types/components/app-layout/types.d.ts +25 -0
- package/types/components/display/ShadowClone.vue.d.ts +20 -0
- package/types/components/display/TabsTile.vue.d.ts +2 -2
- package/types/components/display/index.d.ts +8 -1
- package/types/components/display/types.d.ts +7 -0
- package/types/components/display/use-shadow-target.d.ts +9 -0
- package/types/components/form/FormActiveFilter.vue.d.ts +9 -0
- package/types/components/form/FormInput.vue.d.ts +3 -0
- package/types/components/form/FormInputShadowClone.vue.d.ts +7 -0
- package/types/components/form/FormSearch.vue.d.ts +1 -1
- package/types/components/form/FormSelectShadowClone.vue.d.ts +39 -0
- package/types/components/form/FormShadowCloneDispatch.vue.d.ts +44 -0
- package/types/components/form/FormSortSwitch.vue.d.ts +17 -0
- package/types/components/form/FormSortSwitchGroup.vue.d.ts +15 -0
- package/types/components/form/FormUpload.vue.d.ts +57 -0
- package/types/components/form/FormUploadAudio.vue.d.ts +30 -0
- package/types/components/form/FormUploadCard.vue.d.ts +24 -0
- package/types/components/form/FormUploadFileInfo.vue.d.ts +10 -0
- package/types/components/form/FormUploadImage.vue.d.ts +34 -0
- package/types/components/form/FormUploadTrigger.vue.d.ts +11 -0
- package/types/components/form/FormUploadVideo.vue.d.ts +34 -0
- package/types/components/form/index.d.ts +12 -1
- package/types/components/form/types.d.ts +158 -0
- package/types/components/form/use-password-autofill.d.ts +11 -0
- package/types/components/form/use-upload-state.d.ts +29 -0
- package/types/components/list-layout/ListLayout.vue.d.ts +3 -0
- package/types/components/list-layout/types.d.ts +16 -0
- package/types/components/list-layout/use-active-filter.d.ts +27 -0
- package/types/components/modal/ImagePreviewTrigger.vue.d.ts +41 -0
- package/types/components/modal/ModalConfirm.vue.d.ts +1 -1
- package/types/components/modal/ModalImagePreview.vue.d.ts +31 -0
- package/types/components/modal/ModalPorter.vue.d.ts +7 -0
- package/types/components/modal/ModalShelf.vue.d.ts +12 -3
- package/types/components/modal/ModalVideoPreview.vue.d.ts +40 -0
- package/types/components/modal/VideoPreviewTrigger.vue.d.ts +43 -0
- package/types/components/modal/index.d.ts +6 -1
- package/types/components/modal/modal-global-keys.d.ts +25 -0
- package/types/components/modal/modal-global-symbol.d.ts +8 -0
- package/types/components/modal/modal-shelf-core.d.ts +11 -3
- package/types/components/modal/modal-shelf-types.d.ts +50 -0
- package/types/components/table/TableEmpty.vue.d.ts +11 -0
- package/types/components/table/types.d.ts +11 -0
- package/types/components/view-layout/types.d.ts +1 -1
- package/types/injectInfo.json.d.ts +1 -1
- package/types/store/app.d.ts +1 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# FormUpload(上传原语)
|
|
2
|
+
|
|
3
|
+
> 基于 ElUpload 封装的上传字段:**先上传拿 URL,表单只提交 URL**(业界主流 + 本框架 JSON 提交链路结构必然);`uploadFn` 注入上传接口(不焊后端);上传中经 `FORM_ITEM_CHANGE_LOADING` 拦截提交;进度走 ElUpload 内置。
|
|
4
|
+
> 对齐表单配置项契约(form 族:`render: FormUpload` + `props` 注入,值 = `string | string[]`)。
|
|
5
|
+
> 范式页:`apps/reference/src/pages/form/upload/showcase/`
|
|
6
|
+
|
|
7
|
+
## 定位
|
|
8
|
+
|
|
9
|
+
- **何时用**:任意「表单内文件上传」场景——头像/附件/素材,值 = 已上传文件的 URL
|
|
10
|
+
- **何时不用**:非表单命令式上传(无字段值语义)→ 直接用 ElUpload;需要倍速/字幕等播放能力 → 播放器组件
|
|
11
|
+
- **模式裁定(只做「先上传再提交 url」)**:业界两模式——「文件 + 字段一次 multipart 提交」属整体表单体系(服务端 multipart 接口 + 表单引擎非 JSON),且组件库先例(ElUpload / antd Upload)均为 URL 入值;本框架 FormMain 提交链路 = validate → stringify → JSON,**先传拿 URL 是结构必然**(URL 是字符串,天然融入现有 JSON 契约)。**随表单直传不做**——需表单引擎 multipart 化,与配置化契约分叉、非主流
|
|
12
|
+
- 职责分工:**数据职责注入式**(`uploadFn` 注入上传接口,不焊后端)/ **呈现职责声明式**(`showProgress` 开关 + 内置进度条)
|
|
13
|
+
- 关键机制:`modelValue` = URL(单文件)/ URL 数组(多文件)——与 `uploadFn` 返回一致,表单 stringify 即提交 URL
|
|
14
|
+
|
|
15
|
+
## 快速上手(最小可用)
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// 表单配置项(render 契约:modelValue v-model + props 注入)
|
|
19
|
+
{
|
|
20
|
+
key: "avatar",
|
|
21
|
+
label: "头像",
|
|
22
|
+
render: FormUpload,
|
|
23
|
+
props: {
|
|
24
|
+
uploadFn: (file) => uploadFile(file), // 上传接口:入参 File,返回 Promise<URL>
|
|
25
|
+
loadingKey: "avatar", // 表单字段 key——上传期间拦提交(见「拦截机制」)
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**要点**:
|
|
31
|
+
|
|
32
|
+
- `uploadFn` 必填:入参 `File`、返回 `Promise<string>`(文件 URL)——**只约定数据契约,不焊任何后端**
|
|
33
|
+
- `loadingKey` = 本表单项的 `key`:上传期间提交被拦截(`FORM_ITEM_CHANGE_LOADING` 链路),上传完成自动放行
|
|
34
|
+
- 值类型随 `multiple`:`false`(默认)= `string`;`true` = `string[]`
|
|
35
|
+
- 进度条默认显示(ElUpload 内置),`showProgress=false` 关闭
|
|
36
|
+
|
|
37
|
+
## 拦截机制(上传中禁提交)
|
|
38
|
+
|
|
39
|
+
- **链路(FormMain validate 闸)**:FormUpload 上传在途数 0→1 时调 `changeLoading({ key: loadingKey, message: "上传中" })` → FormMain `loadingList` 记录 → **`validate()` 开头直接 reject**(错误文案 = `label+message`「xx上传中」)→ **提交被拦截**,错误经 reject 由提交方(FormSubmitBtn `@fail` / ElMessage 等)展示;在途数 →0 时 `changeLoading({ key: loadingKey })`(message 空)移除,恢复可提交
|
|
40
|
+
- **[MUST NOT] 走 el-form 规则注入拦提交**:规则增删会触发 EP el-form-item `doValidateField` 的 `resetField`(validateState=error 且无 error 消息 → 字段重置为初始值)——上传完成移除规则时会把刚写入的 URL 重置清空(历史坑两次踩,FormMain 已收敛为 validate 闸,字段级错误提示不再由规则注入产生)
|
|
41
|
+
- `loadingKey` 未传(裸用/非 FormMain 环境)→ `inject(FORM_ITEM_CHANGE_LOADING)` 未命中 no-op,**上传照常不拦**(组件不崩)
|
|
42
|
+
- 判定在途 = **内部 `uploadingCount`**(起止对称计数,覆盖并发多文件)——ElUpload `v-model:file-list` 不回传 uploading 状态,不依赖它
|
|
43
|
+
|
|
44
|
+
## 能力边界 / 按需使用
|
|
45
|
+
|
|
46
|
+
- **默认即够用:单文件 + URL 值 + 进度条 + 上传中拦提交**——绝大多数场景只配 `uploadFn` + `loadingKey`。进度条由 `uploadFn` 可选第二参 `onProgress(percent)` 驱动(真实后端 XHR 转发 `upload.onprogress`;不调则进度条静态)。
|
|
47
|
+
- `multiple` + `limit`:多文件上传(值变 `string[]`)+ 数量上限(默认按形态:单文件 1 / 多文件不限);**满上限触发区隐藏**(`list.length < maxCount` 才显示——业界主流 maxCount 满即无上传入口,单文件上传后触发区消失,替换 = 删旧再传;自定义 default slot 同样隐藏)。
|
|
48
|
+
- **布局 = 列表在前、触发在后**(text/picture 模式 flex order 重排):每上传一个文件,文件项都在拖拽/点击二合一触发模块前面;picture-card 模式触发卡天然在网格末尾。
|
|
49
|
+
- `accept` / `drag` / `disabled`:文件类型过滤 / 拖拽上传 / 禁用,透传 ElUpload。
|
|
50
|
+
- `showProgress`:默认开(ElUpload 内置进度条:`on-progress` → `event.percent` → 自动更新显示);**无需自造进度方法**——接口支持 XHR 上传进度即自动可用;仅自定义进度呈现时才需自行从 `on-progress` 拿 `event.percent`(本组件不提供进度插槽,YAGNI)。
|
|
51
|
+
- **完整能力演示**:`apps/reference/src/pages/form/upload/showcase/`——能力展示,非推荐默认。
|
|
52
|
+
|
|
53
|
+
## API
|
|
54
|
+
> ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
|
|
55
|
+
|
|
56
|
+
### Props(内联 defineProps,无 types.ts)
|
|
57
|
+
|
|
58
|
+
| name | type | 默认 | 语义 |
|
|
59
|
+
| --- | --- | --- | --- |
|
|
60
|
+
| `modelValue` | `string \| string[] \| undefined`(必传) | — | v-model:已上传文件 URL;`multiple` 时为 URL 数组 |
|
|
61
|
+
| `uploadFn` | `(file: File) => Promise<string>`(必传) | — | 上传接口注入:入参 File、返回 URL(数据职责,不焊后端) |
|
|
62
|
+
| `loadingKey` | `string` | — | 表单字段 key——上传期间经 `FORM_ITEM_CHANGE_LOADING` 拦提交(FormMain 配置化场景传;裸用可不传) |
|
|
63
|
+
| `maxCount` | `number` | `1` | **文件数量上限 + 多文件判定一体**(`>1` = 多文件追加兼上限;`1` = 单文件替换语义——业界主流 maxCount 即数量限制);v-model 恒 `string[]`,配置表单提交转换经 stringify/parse |
|
|
64
|
+
| `maxSize` | `number`(MB) | — | **单文件大小上限**(EP 无原生 size 限制,上传前校验 reject + ElMessage 提示;超限值不入) |
|
|
65
|
+
| `accept` | `string` | — | 文件类型过滤(透传 ElUpload) |
|
|
66
|
+
| `drag` | `boolean` | `false` | 拖拽上传(透传 ElUpload) |
|
|
67
|
+
| `disabled` | `boolean` | `false` | 禁用(透传 ElUpload) |
|
|
68
|
+
| `showProgress` | `boolean` | `true` | 上传进度条(ElUpload 内置 `show-progress`) |
|
|
69
|
+
| `placeholder` | `string` | — | 空态提示文字(render 契约字段) |
|
|
70
|
+
|
|
71
|
+
> **ElUpload UI 类属性全量透传**:`$attrs` 直达 ElUpload——`autoUpload` / `listType`(picture-card 等)/ `on-success` / `on-error` / `on-exceed` 等均可用。**网络类属性(`action` / `headers` / `data` / `with-credentials` / `http-request`)不适用**——上传行为已由 `uploadFn` 内部接管(`http-request` 内部固定走 uploadFn),外部传了被忽略。
|
|
72
|
+
|
|
73
|
+
### Emits
|
|
74
|
+
|
|
75
|
+
| 事件 | 载荷 | 语义 |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| `update:modelValue` | `string \| string[]` | v-model 同步 |
|
|
78
|
+
| `uploadSuccess` | `{ file: UploadFile, url: string }` | 单文件上传成功(URL 已入值) |
|
|
79
|
+
| `uploadError` | `{ file: UploadFile, error: Error }` | 单文件上传失败(值不变,错误可展示) |
|
|
80
|
+
| `triggerChangeLink` | `(value, attachInfo?)` | 联动透传(配置项 beLink 场景) |
|
|
81
|
+
|
|
82
|
+
### Slots
|
|
83
|
+
|
|
84
|
+
| 槽 | scope | 语义 |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| 默认 | ElUpload 触发区 scope | 自定义触发区(透传 ElUpload);**缺省 = 内置触发区**(上传图标 + 「点击上传」/drag 时「点击或拖拽上传」——无内容时 el-upload 触发区坍缩 0 尺寸不可点,兜底保证点击上传入口恒在) |
|
|
87
|
+
| `#tip` | 无 | 提示区(透传 ElUpload) |
|
|
88
|
+
| `#file` | `{ file, index, remove }` | 文件列表自定义渲染(透传 ElUpload 实际 slot scope;**`remove` = 删除当前项**,内部同步 v-model) |
|
|
89
|
+
|
|
90
|
+
### Expose
|
|
91
|
+
|
|
92
|
+
无
|
|
93
|
+
|
|
94
|
+
## 反模式 / 注意
|
|
95
|
+
|
|
96
|
+
- **值 = URL 不是 File**:`modelValue` 语义是「已上传文件的 URL」——外部塞 File 对象进值是错误用法(表单 stringify 提交的是 URL);上传动作经 `uploadFn` 发生
|
|
97
|
+
- **`loadingKey` 不传则上传中不拦提交**:裸用可接受(无 FormMain 环境);FormMain 配置化场景忘传 = 上传中能提交(半成品 URL 进库)——配置时 [MUST] 传
|
|
98
|
+
- **进度不自造**:ElUpload 内置进度条(on-progress → percent → 自动显示)——「把 onprogress 数据传给 getProgress 方法」是自造轮子;只有自定义进度呈现才需要自己拿 `event.percent`
|
|
99
|
+
- **网络类属性无效**:`action` / `headers` / `http-request` 等传了被忽略(上传走 uploadFn)——别把后端上传配置塞 ElUpload 网络 props
|
|
100
|
+
- **外部清空联动**:外部把值清空(`undefined` / 空数组)→ 文件列表同步清空——受控语义,别惊讶
|
|
101
|
+
- **多文件去重**:同 URL 重复上传不做客户端去重(服务端职责),`limit` 只限数量
|
|
102
|
+
|
|
103
|
+
## 关联
|
|
104
|
+
|
|
105
|
+
- form 族:`FormMain`(宿主:validate 拦截闸 / stringify 序列化)、`FormSubmitPanel`(FormMain 容器 + 取消/重置/提交操作区,直接消费——上传中拦提交经内置提交管线照样生效,见范式页 ⓔ)、`FormItem`(render 契约宿主:modelValue + props 注入)
|
|
106
|
+
- 拦截机制:`FORM_ITEM_CHANGE_LOADING`(FormMain 256 行 provide;loadingList → 恒失败规则 → validate reject)
|
|
107
|
+
- 底层:`ElUpload`(element-plus;UI/进度/列表内置能力)
|
|
108
|
+
- 范式页:`apps/reference/src/pages/form/upload/showcase/`
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# FormUploadAudio(音频上传组件)
|
|
2
|
+
|
|
3
|
+
> 基于 `FormUpload` 的**音频专属薄封装**:类型限制(`accept="audio/*"`)+ 列表项体验优化(内联播放条即时试听)。
|
|
4
|
+
> 与 `FormUploadImage` / `FormUploadVideo` 同族(媒体上传三件套)。
|
|
5
|
+
> 范式页:`apps/reference/src/pages/form/guide/`(FormMain 配置化)、`apps/reference/src/pages/form/submit-panel/showcase/`(FormSubmitPanel 配置化)
|
|
6
|
+
|
|
7
|
+
## 定位
|
|
8
|
+
|
|
9
|
+
- **何时用**:任意「上传音频」场景——语音、配乐、素材;与 `FormUpload` 同契约(先上传拿 URL),只是类型限制 + 列表项按音频形态优化
|
|
10
|
+
- **何时不用**:上传任意文件 → `FormUpload` 本体;图片 / 视频 → 同族 `FormUploadImage` / `FormUploadVideo`
|
|
11
|
+
- 基座关系:**薄封装不改契约**——全部 props / emits / v-model 透传 `FormUpload`;仅两处覆写:ⓐ `accept` 默认 `"audio/*"`;ⓑ 默认 `#file` 列表项 = 行式(文件名/大小/进度 + **内联 `<audio controls>` 播放条** + 删除)
|
|
12
|
+
- **音频不建弹窗(业界结论)**:音频语义是「试听」不是「观看」——业界主流(blueimp / Vue File Agent / 微信 / 播客产品)均为**内联播放条**,无「音频 lightbox」形态;故不新增 `AudioPreviewTrigger` / `ModalAudioPreview`,播放条直接内联列表项(上传中 blob URL 即时可试听)
|
|
13
|
+
|
|
14
|
+
## 快速上手(最小可用)
|
|
15
|
+
|
|
16
|
+
```vue
|
|
17
|
+
<FormUploadAudio
|
|
18
|
+
v-model="audio"
|
|
19
|
+
:upload-fn="uploadFn"
|
|
20
|
+
loading-key="audio"
|
|
21
|
+
placeholder="点击上传音频(单文件 · 限音频)"
|
|
22
|
+
/>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**要点**:
|
|
26
|
+
|
|
27
|
+
- `uploadFn` 必填(同 `FormUpload`);`loadingKey` = 本表单项 `key`(上传期间拦提交)
|
|
28
|
+
- 单文件默认(`maxCount` 1 替换语义);`maxCount` > 1 = 多文件追加(列表行)
|
|
29
|
+
- 必传/非必传 = 表单配置项 `required`,与组件无关
|
|
30
|
+
|
|
31
|
+
## 能力边界 / 按需使用
|
|
32
|
+
|
|
33
|
+
- **默认即够用:行式列表 + 内联播放条**——绝大多数音频上传只传 `uploadFn` / `loadingKey`。
|
|
34
|
+
- `maxCount` / `maxSize` / `drag`:多文件+数量上限 / 大小上限 / 拖拽(透传 FormUpload)。
|
|
35
|
+
- `#file` 插槽(scope 含 `remove`):完全自定义列表项(建议保留播放与删除)。
|
|
36
|
+
- **完整能力演示**:范式页——单文件必传 / 多文件非必传组合见 FormMain 与 FormSubmitPanel reference。
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
> ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
|
|
40
|
+
|
|
41
|
+
### Props(继承 FormUpload 全部 + 覆写默认值)
|
|
42
|
+
|
|
43
|
+
| name | 默认 | 语义 |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| `uploadFn` | (必填) | 上传接口注入 `(file: File) => Promise<string>`(透传 FormUpload) |
|
|
46
|
+
| `loadingKey` | — | 本表单项 key,上传期间拦提交(透传 FormUpload) |
|
|
47
|
+
| `accept` | `"audio/*"` | 文件类型过滤(**覆写默认**,可再覆盖) |
|
|
48
|
+
| 其余 | 同 FormUpload | `maxCount`(默认 1,>1 多文件)/ `maxSize`(MB 大小上限)/ `drag` / `disabled` / `showProgress` / `placeholder` 等全透传 |
|
|
49
|
+
|
|
50
|
+
> **ElUpload 属性经 `$attrs` 全量透传**,网络类属性被 FormUpload 忽略(上传行为由 uploadFn 接管)。
|
|
51
|
+
|
|
52
|
+
### Emits
|
|
53
|
+
|
|
54
|
+
同 `FormUpload`(`update:modelValue` / `uploadSuccess` / `uploadError` / `triggerChangeLink`)。
|
|
55
|
+
|
|
56
|
+
### Slots
|
|
57
|
+
|
|
58
|
+
| 槽 | scope | 语义 |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| `#default` | 无 | 上传触发区(默认「点击/拖拽上传」观感) |
|
|
61
|
+
| `#file` | `{ file, index, remove }` | 列表项自定义;**默认 = 文件名/大小/进度 + 内联 `<audio controls>` 播放条 + 删除**;`remove` = 删除当前项 |
|
|
62
|
+
| `#tip` | 无 | 列表下方提示(透传 FormUpload) |
|
|
63
|
+
|
|
64
|
+
### Expose
|
|
65
|
+
|
|
66
|
+
无
|
|
67
|
+
|
|
68
|
+
## 反模式 / 注意
|
|
69
|
+
|
|
70
|
+
- **[MUST NOT] 为音频造弹窗**:业界无「音频 lightbox」形态——试听语义用内联播放条(成本最低、交互最自然),弹窗沉浸是视频专利
|
|
71
|
+
- **`accept` 只是过滤不是校验**:服务端仍须校验(同 FormUpload)
|
|
72
|
+
- **`remove` 走基座值同步**:自定义 `#file` 删除须用 scope 的 `remove`,[MUST NOT] 直接改 fileList
|
|
73
|
+
- **播放条直用原生 `<audio controls>`**:薄壳不内置波形/播放器增强(需要时消费方自拼,见反模式)
|
|
74
|
+
|
|
75
|
+
## 关联
|
|
76
|
+
|
|
77
|
+
- 基座:`FormUpload`(上传原语:uploadFn 注入 / validate 拦提交 / #file slot scope)
|
|
78
|
+
- 同族:`FormUploadImage`(方形缩略图卡片 + 点击直达放大)、`FormUploadVideo`(封面占位 + 播放图标 + 弹窗决策链)
|
|
79
|
+
- 拦截机制:`FORM_ITEM_CHANGE_LOADING`(FormMain validate 闸)
|
|
80
|
+
- 范式页:`apps/reference/src/pages/form/guide/`、`apps/reference/src/pages/form/submit-panel/showcase/`
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# FormUploadImage(图片上传组件)
|
|
2
|
+
|
|
3
|
+
> 基于 `FormUpload` 的**图片专属薄封装**:类型限制(`accept="image/*"`)+ 列表项体验优化(方形缩略图卡片 + 点击放大预览 + 悬停删除)。
|
|
4
|
+
> 与 `FormUploadVideo` / `FormUploadAudio` 同族(媒体上传三件套)。
|
|
5
|
+
> 范式页:`apps/reference/src/pages/form/guide/`(FormMain 配置化)、`apps/reference/src/pages/form/submit-panel/showcase/`(FormSubmitPanel 配置化)
|
|
6
|
+
|
|
7
|
+
## 定位
|
|
8
|
+
|
|
9
|
+
- **何时用**:任意「上传图片」场景——头像、封面、图库;与 `FormUpload` 同契约(先上传拿 URL,表单只提交 URL),只是对上传类型做了进一步限制 + 列表项按图片形态优化
|
|
10
|
+
- **何时不用**:上传任意文件类型 → `FormUpload` 本体;上传视频 / 音频 → 同族 `FormUploadVideo` / `FormUploadAudio`
|
|
11
|
+
- 基座关系:**薄封装不改契约**——全部 props / emits / v-model 透传 `FormUpload`;仅两处覆写:ⓐ `accept` 默认 `"image/*"`;ⓑ 默认 `#file` 列表项 = 方形缩略图卡片(点击经 `ImagePreviewTrigger` 直达 el-image-viewer 放大;悬停删除角标)
|
|
12
|
+
- 列表形态:默认 `list-type="picture-card"`(EP 方形卡片网格),消费方可经 attrs 覆写
|
|
13
|
+
- 预览链路:列表项 = `ImagePreviewTrigger`(`src=file.url`)——**点击直达 ElImage 原生预览**(零弹窗零决策链,与预览族图片约定一致);上传中 blob URL 同样可预览(本地即时核对)
|
|
14
|
+
|
|
15
|
+
## 快速上手(最小可用)
|
|
16
|
+
|
|
17
|
+
```vue
|
|
18
|
+
<FormUploadImage
|
|
19
|
+
v-model="cover"
|
|
20
|
+
:upload-fn="uploadFn"
|
|
21
|
+
loading-key="cover"
|
|
22
|
+
placeholder="点击上传封面(单文件 · 限图片)"
|
|
23
|
+
/>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**要点**:
|
|
27
|
+
|
|
28
|
+
- `uploadFn` 必填(数据职责注入,同 `FormUpload`);`loadingKey` = 本表单项 `key`(上传期间拦提交)
|
|
29
|
+
- 单文件默认(`maxCount` 1 替换语义);`maxCount` > 1 = 多文件追加(卡片网格)
|
|
30
|
+
- 必传/非必传 = 表单配置项 `required`(FormMain 校验),与组件无关
|
|
31
|
+
|
|
32
|
+
## 能力边界 / 按需使用
|
|
33
|
+
|
|
34
|
+
- **默认即够用:方形卡片 + 点击放大 + 悬停删除**——绝大多数图片上传只传 `uploadFn` / `loadingKey`。
|
|
35
|
+
- `maxCount` / `maxSize` / `drag`:多文件+数量上限 / 大小上限 / 拖拽(透传 FormUpload)。
|
|
36
|
+
- `#file` 插槽(scope 含 `remove`):完全自定义卡片内容(仍建议保留预览与删除能力)。
|
|
37
|
+
- **完整能力演示**:范式页——单文件必传 / 多文件非必传组合见 FormMain 与 FormSubmitPanel reference。
|
|
38
|
+
|
|
39
|
+
## API
|
|
40
|
+
> ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
|
|
41
|
+
|
|
42
|
+
### Props(继承 FormUpload 全部 + 覆写默认值)
|
|
43
|
+
|
|
44
|
+
| name | 默认 | 语义 |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| `uploadFn` | (必填) | 上传接口注入 `(file: File) => Promise<string>`(透传 FormUpload) |
|
|
47
|
+
| `loadingKey` | — | 本表单项 key,上传期间拦提交(透传 FormUpload) |
|
|
48
|
+
| `accept` | `"image/*"` | 文件类型过滤(**覆写默认**,可再覆盖) |
|
|
49
|
+
| `listType` | `"picture-card"` | 列表形态(**覆写默认**为方形卡片网格,透传 ElUpload) |
|
|
50
|
+
| 其余 | 同 FormUpload | `maxCount`(默认 1,>1 多文件)/ `maxSize`(MB 大小上限)/ `drag` / `disabled` / `showProgress` / `placeholder` 等全透传 |
|
|
51
|
+
|
|
52
|
+
> **ElUpload 属性经 `$attrs` 全量透传**(`autoUpload` / `on-exceed` 等),网络类属性(action/headers/data/http-request)被 FormUpload 忽略(上传行为由 uploadFn 接管)。
|
|
53
|
+
|
|
54
|
+
### Emits
|
|
55
|
+
|
|
56
|
+
同 `FormUpload`(`update:modelValue` / `uploadSuccess` / `uploadError` / `triggerChangeLink`)。
|
|
57
|
+
|
|
58
|
+
### Slots
|
|
59
|
+
|
|
60
|
+
| 槽 | scope | 语义 |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| `#default` | 无 | 上传触发区(默认「点击/拖拽上传」观感) |
|
|
63
|
+
| `#file` | `{ file, index, remove }` | 列表项自定义;**默认 = 方形缩略图卡片 + 点击放大 + 悬停删除**;`remove` = 删除当前项(基座 slot scope 拓展) |
|
|
64
|
+
| `#tip` | 无 | 列表下方提示(透传 FormUpload) |
|
|
65
|
+
|
|
66
|
+
### Expose
|
|
67
|
+
|
|
68
|
+
无
|
|
69
|
+
|
|
70
|
+
## 反模式 / 注意
|
|
71
|
+
|
|
72
|
+
- **别在 `#file` 里塞自绘弹窗**:预览终点 = el-image-viewer(`ImagePreviewTrigger` 点击直达),[MUST NOT] 再包弹窗(两层嵌套已废弃)
|
|
73
|
+
- **`accept` 只是过滤不是校验**:服务端仍须按类型/大小校验(同 FormUpload)
|
|
74
|
+
- **`remove` 走基座值同步**:自定义 `#file` 删除须用 scope 的 `remove`(内部 handleRemove 同步 v-model),[MUST NOT] 直接改 fileList
|
|
75
|
+
- **进度条**:上传中卡片下方有 EP 内置进度(`showProgress` 开关)
|
|
76
|
+
|
|
77
|
+
## 关联
|
|
78
|
+
|
|
79
|
+
- 基座:`FormUpload`(上传原语:uploadFn 注入 / validate 拦提交 / #file slot scope)
|
|
80
|
+
- 同族:`FormUploadVideo`(视频,封面占位 + 播放图标 + VideoPreviewTrigger 决策链)、`FormUploadAudio`(音频,内联播放条)
|
|
81
|
+
- 预览:`ImagePreviewTrigger`(点击直达 ElImage 原生放大)
|
|
82
|
+
- 拦截机制:`FORM_ITEM_CHANGE_LOADING`(FormMain validate 闸)
|
|
83
|
+
- 范式页:`apps/reference/src/pages/form/guide/`、`apps/reference/src/pages/form/submit-panel/showcase/`
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# FormUploadVideo(视频上传组件)
|
|
2
|
+
|
|
3
|
+
> 基于 `FormUpload` 的**视频专属薄封装**:类型限制(`accept="video/*"`)+ **交互对齐图片上传**(picture-card 卡片网格——唯一区别 = 卡片内容为视频预览)。
|
|
4
|
+
> 与 `FormUploadImage` / `FormUploadAudio` 同族(媒体上传三件套)。
|
|
5
|
+
> 范式页:`apps/reference/src/pages/form/guide/`(FormMain 配置化)、`apps/reference/src/pages/form/submit-panel/showcase/`(FormSubmitPanel 配置化)
|
|
6
|
+
|
|
7
|
+
## 定位
|
|
8
|
+
|
|
9
|
+
- **何时用**:任意「上传视频」场景——素材库、课程视频;与 `FormUpload` 同契约(先上传拿 URL),只是类型限制 + 列表项按视频形态优化
|
|
10
|
+
- **何时不用**:上传任意文件 → `FormUpload` 本体;图片 / 音频 → 同族 `FormUploadImage` / `FormUploadAudio`
|
|
11
|
+
- 基座关系:**薄封装不改契约**——全部 props / emits / v-model 透传 `FormUpload`;仅三处覆写:ⓐ `accept` 默认 `"video/*"`;ⓑ `listType` 默认 `"picture-card"`(**卡片网格,交互对齐图片上传**);ⓒ 默认 `#file` 列表项 = 媒体卡片(`VideoPreviewTrigger` 铺满 + 上传中遮罩 + 悬停删除)
|
|
12
|
+
- **卡片内容 = 视频预览**:封面图(有 coverUrl)/ 深色占位 + 播放图标(无封面——业界主流:上传视频大多无封面,YouTube/剪映素材库同此形态);点击走弹窗决策链(全局遥控 `APP_GLOBAL_VIDEO_PREVIEW_KEY` 命中 → 全局唤起;否则自带弹窗兜底),上传中 blob URL 同样可播放(本地即时核对)
|
|
13
|
+
|
|
14
|
+
## 快速上手(最小可用)
|
|
15
|
+
|
|
16
|
+
```vue
|
|
17
|
+
<FormUploadVideo
|
|
18
|
+
v-model="video"
|
|
19
|
+
:upload-fn="uploadFn"
|
|
20
|
+
loading-key="video"
|
|
21
|
+
placeholder="点击上传视频(单文件 · 限视频)"
|
|
22
|
+
/>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**要点**:
|
|
26
|
+
|
|
27
|
+
- `uploadFn` 必填(同 `FormUpload`);`loadingKey` = 本表单项 `key`(上传期间拦提交)
|
|
28
|
+
- 单文件默认(`maxCount` 1 替换语义);`maxCount` > 1 = 多文件追加(列表行)
|
|
29
|
+
- 必传/非必传 = 表单配置项 `required`,与组件无关
|
|
30
|
+
|
|
31
|
+
## 能力边界 / 按需使用
|
|
32
|
+
|
|
33
|
+
- **默认即够用:卡片网格 + 播放图标 + 点击播放**——绝大多数视频上传只传 `uploadFn` / `loadingKey`。
|
|
34
|
+
- `maxCount` / `maxSize` / `drag`:多文件+数量上限 / 大小上限 / 拖拽(透传 FormUpload);**满上限(`maxCount` 达)触发区隐藏**(单文件上传后消失,替换 = 删旧再传)。
|
|
35
|
+
- `#file` 插槽(scope 含 `remove`):完全自定义列表项(建议保留预览与删除)。
|
|
36
|
+
- **完整能力演示**:范式页——单文件必传 / 多文件非必传组合见 FormMain 与 FormSubmitPanel reference。
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
> ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
|
|
40
|
+
|
|
41
|
+
### Props(继承 FormUpload 全部 + 覆写默认值)
|
|
42
|
+
|
|
43
|
+
| name | 默认 | 语义 |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| `uploadFn` | (必填) | 上传接口注入 `(file: File) => Promise<string>`(透传 FormUpload) |
|
|
46
|
+
| `loadingKey` | — | 本表单项 key,上传期间拦提交(透传 FormUpload) |
|
|
47
|
+
| `accept` | `"video/*"` | 文件类型过滤(**覆写默认**,可再覆盖) |
|
|
48
|
+
| `listType` | `"picture-card"` | 列表形态(**覆写默认**为卡片网格,交互对齐图片上传;text 等可覆写) |
|
|
49
|
+
| 其余 | 同 FormUpload | `maxCount`(默认 1,>1 多文件)/ `maxSize`(MB 大小上限)/ `drag` / `disabled` / `showProgress` / `placeholder` 等全透传 |
|
|
50
|
+
|
|
51
|
+
> **ElUpload 属性经 `$attrs` 全量透传**,网络类属性被 FormUpload 忽略(上传行为由 uploadFn 接管)。
|
|
52
|
+
|
|
53
|
+
### Emits
|
|
54
|
+
|
|
55
|
+
同 `FormUpload`(`update:modelValue` / `uploadSuccess` / `uploadError` / `triggerChangeLink`)。
|
|
56
|
+
|
|
57
|
+
### Slots
|
|
58
|
+
|
|
59
|
+
| 槽 | scope | 语义 |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| `#default` | 无 | 上传触发区(默认「点击/拖拽上传」观感) |
|
|
62
|
+
| `#file` | `{ file, index, remove }` | 列表项自定义;**默认 = 媒体卡片(`VideoPreviewTrigger` 铺满:封面/占位 + 播放图标,点击播放)+ 上传中遮罩 + 悬停删除**;`remove` = 删除当前项 |
|
|
63
|
+
| `#tip` | 无 | 列表下方提示(透传 FormUpload) |
|
|
64
|
+
|
|
65
|
+
### Expose
|
|
66
|
+
|
|
67
|
+
无
|
|
68
|
+
|
|
69
|
+
## 反模式 / 注意
|
|
70
|
+
|
|
71
|
+
- **列表项不做 `#t=0.01` 抠首帧**:无封面就深色占位 + 播放图标(业界主流,播放按钮即语义;首帧由服务端生成封面是上传后增强,非前端必须)——首帧兜底只在弹窗端 `ModalVideoPreview`(播放器场景才需要「不黑屏」)
|
|
72
|
+
- **`accept` 只是过滤不是校验**:服务端仍须校验(同 FormUpload)
|
|
73
|
+
- **`remove` 走基座值同步**:自定义 `#file` 删除须用 scope 的 `remove`,[MUST NOT] 直接改 fileList
|
|
74
|
+
- **弹窗端视频属性(autoplay/muted 等)在 `ModalVideoPreview` 上配**,[MUST NOT] 经本组件透传
|
|
75
|
+
|
|
76
|
+
## 关联
|
|
77
|
+
|
|
78
|
+
- 基座:`FormUpload`(上传原语:uploadFn 注入 / validate 拦提交 / #file slot scope)
|
|
79
|
+
- 同族:`FormUploadImage`(方形缩略图卡片 + 点击直达放大)、`FormUploadAudio`(内联播放条)
|
|
80
|
+
- 播放端:`VideoPreviewTrigger` → `ModalVideoPreview`(黑底 lightbox,决策链:全局遥控优先 / 自带弹窗兜底)
|
|
81
|
+
- 拦截机制:`FORM_ITEM_CHANGE_LOADING`(FormMain validate 闸)
|
|
82
|
+
- 范式页:`apps/reference/src/pages/form/guide/`、`apps/reference/src/pages/form/submit-panel/showcase/`
|
|
@@ -9,8 +9,12 @@
|
|
|
9
9
|
## 关键类型
|
|
10
10
|
|
|
11
11
|
- `ListLayoutProps` / `ListLayoutInstance`(= FormSearchInstance + TableMainInstance + update)/ `ListLayoutInnerInjectInfo`(列插槽注入:triggerSearch / triggerReset / update)
|
|
12
|
-
- 内部不导出:`use-sticky
|
|
12
|
+
- 内部不导出:`use-sticky`、`use-active-filter`(生效条件条投影逻辑:query 投影 + value→label + 表单值同步清除)
|
|
13
|
+
|
|
14
|
+
## 新能力(2026-08-04)
|
|
15
|
+
|
|
16
|
+
- **生效筛选条件条**:`formActiveFilter` prop(默认关)开启后,表单卡片顶部一行显示已生效查询条件(query 投影,≠ 表单输入值;枚举字段经 config props.options 显示 label,取不到原值兜底);单项 × = 移除 query 字段 + 同步清表单值 + 重查;`allowClear`(默认关)额外显示「清除全部」。UI 组件 = form 族 `FormActiveFilter`(纯 UI,数据/事件上抛)
|
|
13
17
|
|
|
14
18
|
## 范式页(apps/reference)
|
|
15
19
|
|
|
16
|
-
- `/list-layout/guide`(最佳实践:搜索 + 弹窗 CRUD + 分离侧栏)/ `/list-layout/sticky`(三件套粘性)/ `/list-layout/module-demo`(keepAlive
|
|
20
|
+
- `/list-layout/guide`(最佳实践:搜索 + 弹窗 CRUD + 分离侧栏)/ `/list-layout/sticky`(三件套粘性)/ `/list-layout/module-demo`(keepAlive 模块组合)/ `/list-layout/active-filter`(生效筛选条件条)
|
|
@@ -95,6 +95,7 @@ const { listPageConfig } = useSpaceConfig({
|
|
|
95
95
|
| `separateSearchConfig` | `Pick<SlotLayoutFlowAsideProps, "type"\|"gap"\|"asideWidth">` | 未设 | 分离侧栏布局 |
|
|
96
96
|
| `searchSticky` / `toolbarSticky` / `pagerSticky` | `boolean` | 均 `false` | 三件套粘性(分离态下 searchSticky 被无视) |
|
|
97
97
|
| `headerObserveResize` / `operationObserveResize` | `boolean` | 均 `false` | header/操作槽量高 ResizeObserver |
|
|
98
|
+
| `initialSearch` | `boolean` | `true` | 挂载后是否立即触发初始化查询(默认查);关闭后表格空态显示「欢迎触发查询」、KeepAlive 激活不自动刷新(TableMain 经「已发生过查询」自判,见 table docs) |
|
|
98
99
|
|
|
99
100
|
**v-model**:`isAutoRefresh`、`refreshInterval`、`customView`(配合 `#custom-view-item` 卡片渲染)
|
|
100
101
|
|
|
@@ -7,15 +7,20 @@
|
|
|
7
7
|
- [ModalConfirm](./docs/README-ModalConfirm.md) — 确认弹窗/抽屉原语(onXxx props 契约)
|
|
8
8
|
- [ModalForm](./docs/README-ModalForm.md) — 弹窗 + 表单 + 校验提交
|
|
9
9
|
- [ModalDetail](./docs/README-ModalDetail.md) — CRUD 预设薄壳(add / edit / read)
|
|
10
|
+
- [ModalImagePreview](./docs/README-ModalImagePreview.md) — 图片预览弹窗·命令式入口(ElImageViewer 薄适配 + 弹窗配置化契约;点击路径不走它——ElImage 原生直达)
|
|
11
|
+
- [ModalVideoPreview](./docs/README-ModalVideoPreview.md) — 视频预览弹窗·lightbox 形态(ElDialog 直配黑底沉浸,业界 Video Lightbox;url/coverUrl,封面空走 `#t=0.01` 首帧)
|
|
12
|
+
- [ImagePreviewTrigger](./docs/README-ImagePreviewTrigger.md) — 图片触发(ElImage 落地 + 懒加载/占位兜底 + previewSrc 原图,**点击直达 ElImage 原生预览**——零弹窗)
|
|
13
|
+
- [VideoPreviewTrigger](./docs/README-VideoPreviewTrigger.md) — 视频触发(封面吃懒加载 / 无封面占位,**两形态统一播放图标**;视频无 el 原生预览 → 弹窗决策链保留)
|
|
10
14
|
- [ModalShelf](./docs/README-ModalShelf.md) — 弹窗架子(level: app/page/custom + 层级断言)
|
|
11
15
|
- [ModalPorter](./docs/README-ModalPorter.md) — 弹窗搬运(modalList 注册表 + evoke + 卸载自动清理)
|
|
12
16
|
|
|
13
17
|
## 关键类型与机制
|
|
14
18
|
|
|
15
|
-
- 类型:`ModalLevel` / `ModalSameKeyStrategy` / `ModalShelfMap` / `ModalInjectedProps` / `EvokeHandle` / `RegisterEntry` / `ShelfController`(经 `./types` + `./modal-shelf-types` 导出)
|
|
19
|
+
- 类型:`ModalLevel` / `ModalSameKeyStrategy` / `ModalShelfMap` / `ModalInjectedProps` / `EvokeHandle` / `RegisterEntry` / `ShelfController` / `GlobalModalRemote`(含 **`modalKey: string[]`** 已注册 key 清单)/ `GlobalModalControl`(经 `./types` + `./modal-shelf-types` 导出)
|
|
20
|
+
- 全局遥控:`GLOBAL_MODAL_REMOTE_KEY`(core 导出注入 key,触发组件等消费端 inject)+ 预置 key 常量 `APP_GLOBAL_IMAGE_PREVIEW_KEY` / `APP_GLOBAL_VIDEO_PREVIEW_KEY`(AppLayout `enableGlobal*Preview` 开关开启即注册全局预览弹窗)
|
|
16
21
|
- ⚠️ deprecated 兼容别名:`ConfirmModal` / `FormModal` / `DetailModal`(指向新名,后续大版本移除)——**新代码 [MUST NOT] 用旧名**,仅存量迁移期过渡(readme API 表与范式页均用新名)
|
|
17
22
|
- 内置挂载:AppLayout 内置 `level="app"`、AppPage 内置 `level="page"`(业务 [MUST NOT] 自声明)
|
|
18
23
|
|
|
19
24
|
## 范式页(apps/reference)
|
|
20
25
|
|
|
21
|
-
- `/modal/guide`(对接内置 page 架子)/ `/modal/porter/showcase`(三层自定义架子 + 5 场景)/ `/modal/confirm/showcase`(modal/drawer 双形态)/ `/modal/form-modal/showcase
|
|
26
|
+
- `/modal/guide`(对接内置 page 架子)/ `/modal/porter/showcase`(三层自定义架子 + 5 场景)/ `/modal/confirm/showcase`(modal/drawer 双形态)/ `/modal/form-modal/showcase`(独立原语用法)/ `/modal/preview/showcase`(预览族:图片/视频弹窗 + 触发组件 + 全局遥控交叉验证)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# ImagePreviewTrigger(图片预览触发组件)
|
|
2
|
+
|
|
3
|
+
> 图片触发原语:`ElImage` 落地(img 原生属性对齐)+ 懒加载 + 加载中/失败占位兜底;**点击直接走 ElImage 自带预览(el-image-viewer 放大)**——点图即放大,一层到位,不经弹窗。
|
|
4
|
+
> 与 `VideoPreviewTrigger` 对等成对(视频触发端仍需弹窗;图片预览终点是 el 原生查看器)。
|
|
5
|
+
> 范式页:`apps/reference/src/pages/modal/preview/showcase/`
|
|
6
|
+
|
|
7
|
+
## 定位
|
|
8
|
+
|
|
9
|
+
- **何时用**:任意「缩略图可点开看大图」场景——上传文件列表、附件卡片、图片墙
|
|
10
|
+
- **何时不用**:仅展示不点击 → 直接用 `ElImage`;命令式开图(按钮/编排,不经过点击)→ `ModalImagePreview` + 全局遥控器 evoke
|
|
11
|
+
- 关键机制:落地 **`ElImage`**(非裸 `<img>`)——原生 img 属性照常 `$attrs` 透传对齐,同时白拿组件库三件套:**`lazy`(默认开)** + **`#placeholder`(加载中占位)** + **`#error`(加载失败兜底图)** + **`preview-src-list`(点击放大)**
|
|
12
|
+
- **点击直达(零弹窗)**:`preview` 开启时内部给 ElImage 绑定 `preview-src-list`(= `[previewSrc ?? src]`)+ `preview-teleported`——点击图片即打开 **el-image-viewer 全屏放大**(缩放/旋转/切换/ESC/遮罩关闭全原生)——**一层到位**
|
|
13
|
+
- **命令式与点击两入口各司其职**:点击 → ElImage 原生预览(本组件);命令式 → `ModalImagePreview`(ElImageViewer 薄适配,全局遥控器 evoke)——触发组件 [MUST NOT] 再走弹窗(两层预览嵌套已废弃)
|
|
14
|
+
|
|
15
|
+
## 快速上手(最小可用)
|
|
16
|
+
|
|
17
|
+
```vue
|
|
18
|
+
<ImagePreviewTrigger src="https://example.com/thumb.png" />
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**要点**:
|
|
22
|
+
|
|
23
|
+
- `src` 必传(缩略图路径);零配置即带原生懒加载 + 占位兜底 + **点击放大**
|
|
24
|
+
- 大图与原图分离:`previewSrc` 传原图(点击放大打开的大图),**缺省降级为 `src`**(缩略图即原图时零配置)
|
|
25
|
+
- 点击后:el-image-viewer 全屏放大(ep 原生)——无环境依赖、无决策链、无弹窗
|
|
26
|
+
|
|
27
|
+
## 能力边界 / 按需使用
|
|
28
|
+
|
|
29
|
+
- **默认即够用:缩略图 + 懒加载 + 点击放大**——绝大多数场景只传 `src`。
|
|
30
|
+
- `previewSrc`:仅当缩略图/原图分离(如压缩图列表)时传;缺省 = `src`。
|
|
31
|
+
- `#placeholder` / `#error` 插槽:仅当需要自定义加载中/失败兜底图(小图 / base64)时传;缺省 EP 默认观感(灰底加载中 + 失败图标)。
|
|
32
|
+
- `lazy={false}`:仅当图片必须立即加载(首屏无滚动场景)时关。
|
|
33
|
+
- `preview={false}`:关闭点击放大(纯展示缩略图)——仍保留懒加载 + 占位兜底。
|
|
34
|
+
- **完整能力演示**:`apps/reference/src/pages/modal/preview/showcase/`——能力展示,非推荐默认。
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
> ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
|
|
38
|
+
|
|
39
|
+
### Props(内联 defineProps,无 types.ts)
|
|
40
|
+
|
|
41
|
+
| name | type | 默认 | 语义 |
|
|
42
|
+
| --- | --- | --- | --- |
|
|
43
|
+
| `src` | `string`(必传) | — | 缩略图路径(ElImage.src) |
|
|
44
|
+
| `previewSrc` | `string` | — | 原图路径(点击放大打开的大图);**缺省降级为 `src`** |
|
|
45
|
+
| `lazy` | `boolean` | `true` | 图片懒加载(ElImage.lazy,原生 loading 优先) |
|
|
46
|
+
| `preview` | `boolean` | `true` | 是否可点击放大(内部绑定 `preview-src-list`;false = 纯展示缩略图) |
|
|
47
|
+
|
|
48
|
+
> **ElImage / img 原生属性全量透传**:`$attrs` 直达自身 ElImage——`alt` / `fit` / `loading` / `draggable` / `scroll-container` 等原生属性与 EP 图片属性均可用。
|
|
49
|
+
> ⚠ **`width` / `height` 无效**:EP 2.13 的 ElImage 未声明 width/height props,透传会落根 div 的 HTML attribute(对 div 无渲染效果)。尺寸控制请用外层 CSS / `style`(配合 `fit="cover"` 居中裁切)。
|
|
50
|
+
|
|
51
|
+
### Emits
|
|
52
|
+
|
|
53
|
+
无
|
|
54
|
+
|
|
55
|
+
### Slots
|
|
56
|
+
|
|
57
|
+
| 槽 | scope | 语义 |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| `#placeholder` | 无 | 加载中占位(透传 ElImage;缺省 EP 灰底观感) |
|
|
60
|
+
| `#error` | 无 | 加载失败兜底图(透传 ElImage;缺省 EP 失败图标)——**插槽非属性**,传兜底图走插槽内放 `<img>` |
|
|
61
|
+
|
|
62
|
+
### Expose
|
|
63
|
+
|
|
64
|
+
无
|
|
65
|
+
|
|
66
|
+
## 反模式 / 注意
|
|
67
|
+
|
|
68
|
+
- **[MUST NOT] 点击走弹窗**:图片预览终点 = el-image-viewer(ElImage 原生)——弹窗 → 弹窗内再点放大 = 两层嵌套(脱裤子放屁,已废弃);命令式开图走 `ModalImagePreview`,与点击路径分离
|
|
69
|
+
- **`previewSrc` 才是预览路径**:`src` 是展示路径——缩略图/原图分离时别把大图塞 `src`(懒加载白拉全尺寸图)
|
|
70
|
+
- **`#error` 是插槽不是属性**:传字符串路径是 EP 常见误区(加载失败显示空白)——兜底图放插槽内 `<img :src>`(或 base64)
|
|
71
|
+
- **`preview=false` 只关放大**:仍渲染缩略图 + 懒加载 + 占位兜底——「图片展示」能力不随「预览」关掉
|
|
72
|
+
|
|
73
|
+
## 关联
|
|
74
|
+
|
|
75
|
+
- 命令式弹窗端:`ModalImagePreview`(ElImageViewer 薄适配 + 弹窗配置化契约;全局遥控器 `evoke(APP_GLOBAL_IMAGE_PREVIEW_KEY, { src })`)
|
|
76
|
+
- 对等组件:`VideoPreviewTrigger`(视频触发端;封面/占位 + 播放图标两形态统一,仍需弹窗——视频无 el 原生预览)
|
|
77
|
+
- 命令式弹窗体系:`ModalShelf`(架子 + level 层级)+ `ModalPorter`(modalList 注册表)+ 全局遥控器(`globalModalRemote`)
|
|
78
|
+
- 范式页:`apps/reference/src/pages/modal/preview/showcase/`
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# ModalImagePreview(图片预览弹窗·命令式入口)
|
|
2
|
+
|
|
3
|
+
> 图片预览原语:**ElImageViewer 薄层适配**(el 图片预览组件本体 = 全屏 lightbox)+ **弹窗配置化契约包装**(`v-model:show` / 注册形态注入 / 双输入源)。
|
|
4
|
+
> 注册 key 建议 `modalImagePreview`(ModalShelf/Porter 注册项);全局预置 key = `APP_GLOBAL_IMAGE_PREVIEW_KEY`。
|
|
5
|
+
> 范式页:`apps/reference/src/pages/modal/preview/showcase/`
|
|
6
|
+
|
|
7
|
+
## 定位
|
|
8
|
+
|
|
9
|
+
- **何时用**:**命令式 / 配置化唤起图片预览**——业务按钮、编排逻辑、全局遥控器 `evoke` 开图(el 图片预览无命令式 API,本组件是唯一桥梁)
|
|
10
|
+
- **何时不用**:**点击图片看大图 → 直接用 `ElImage` 的 `preview-src-list`**(el 原生放大查看器,零开发);触发组件 `ImagePreviewTrigger` 点击即直达 ElImage 预览,**不经本组件**——本组件只服务「不经过点击」的命令式入口
|
|
11
|
+
- **为什么是薄适配**:el 图片预览组件(`ElImageViewer`)本身就是全屏 lightbox 弹层(遮罩 + 缩放旋转 + ESC/遮罩关闭)——**直接以它为底座**,只补一层弹窗配置化契约;[MUST NOT] 再包 `ModalConfirm`(两层嵌套:弹窗内再点才放大——脱裤子放屁,已废弃)
|
|
12
|
+
- 关键机制:`defineModel("show")` → 内部映射 ElImageViewer `v-model:visible`;关闭(ESC / 遮罩 / 关闭按钮)→ `update:visible` → 映射 `update:show` → 架子 `@update:show` 收尾关闭
|
|
13
|
+
- **注册形态(ModalShelf/Porter)**:架子渲染只传 `show`/`payload`/`closeFn`/`removeFn`——业务值走 payload,双输入源:`src` = `props.src ?? payload.src`(原语用法 prop 直传、注册用法 payload 承载)
|
|
14
|
+
- **全局预置**:core 预置 `APP_GLOBAL_IMAGE_PREVIEW_KEY`——AppLayout `enableGlobalImagePreview` 开启后自动注册进全局弹窗(见 README-AppLayout);命令式 `globalModalRemote.evoke(APP_GLOBAL_IMAGE_PREVIEW_KEY, { src })` 即开图
|
|
15
|
+
|
|
16
|
+
## 快速上手(最小可用)
|
|
17
|
+
|
|
18
|
+
```vue
|
|
19
|
+
<ModalImagePreview v-model:show="show" src="https://example.com/a.png" />
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**要点**:
|
|
23
|
+
|
|
24
|
+
- `show` 必传(`v-model:show`);`src` 必传(图片地址)
|
|
25
|
+
- 打开即全屏 lightbox(el 原生:缩放 / 旋转 / 左右切换 / ESC / 遮罩关闭)
|
|
26
|
+
- 命令式:`globalModalRemote.evoke(APP_GLOBAL_IMAGE_PREVIEW_KEY, { src })`
|
|
27
|
+
|
|
28
|
+
## 能力边界 / 按需使用
|
|
29
|
+
|
|
30
|
+
- **默认即够用:`show` + `src` = 全屏图片预览**——绝大多数命令式场景只传这两项。
|
|
31
|
+
- `url-list` / `initial-index`:多图切换(`$attrs` 透传 ElImageViewer)。
|
|
32
|
+
- `hide-on-click-modal` / `close-on-press-escape` / `show-close`:关闭通道控制(透传 ElImageViewer)。
|
|
33
|
+
- 点图放大的「点击路径」不归本组件:`ImagePreviewTrigger` / 任意 `ElImage` 的 `preview-src-list` 直达。
|
|
34
|
+
- **完整能力演示**:`apps/reference/src/pages/modal/preview/showcase/`——能力展示,非推荐默认。
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
> ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
|
|
38
|
+
|
|
39
|
+
### Props(内联 defineProps,无 types.ts)
|
|
40
|
+
|
|
41
|
+
| name | type | 默认 | 语义 |
|
|
42
|
+
| --- | --- | --- | --- |
|
|
43
|
+
| `show` | `boolean`(必传) | — | 可见性(v-model:show → ElImageViewer visible) |
|
|
44
|
+
| `src` | `string`(必传) | — | 图片地址(双输入源 `props.src ?? payload.src`) |
|
|
45
|
+
| `payload` | `{ src?: string }` | — | 注册形态注入(架子渲染只传 payload;原语用法无需传) |
|
|
46
|
+
| `closeFn` / `removeFn` | `() => void` | — | 注册形态注入的关闭/移除出口(声明避免泄漏进 `$attrs`) |
|
|
47
|
+
| `onClose` | `(status?: boolean) => void` | — | 关闭回调(ElImageViewer @close 触发) |
|
|
48
|
+
|
|
49
|
+
> **ElImageViewer 属性全量透传**:`$attrs` 直达——`url-list` / `initial-index` / `hide-on-click-modal` / `close-on-press-escape` / `show-close` / `teleported` / `z-index` 等均可用。
|
|
50
|
+
|
|
51
|
+
### Emits
|
|
52
|
+
|
|
53
|
+
| 事件 | 载荷 | 语义 |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `update:show` | `boolean` | 可见性同步(v-model;ElImageViewer 关闭 → `update:visible` 映射) |
|
|
56
|
+
|
|
57
|
+
### Slots
|
|
58
|
+
|
|
59
|
+
无(ElImageViewer 为全屏查看器,无内容插槽语义)
|
|
60
|
+
|
|
61
|
+
### Expose
|
|
62
|
+
|
|
63
|
+
无
|
|
64
|
+
|
|
65
|
+
## 反模式 / 注意
|
|
66
|
+
|
|
67
|
+
- **[MUST NOT] 包 `ModalConfirm` 底座**:两层预览嵌套已废弃(弹窗内再点放大 = 脱裤子放屁)——本组件 = ElImageViewer + 契约,薄层不叠加
|
|
68
|
+
- **点击路径不走本组件**:点图看大图用 `ElImage` `preview-src-list` / `ImagePreviewTrigger` 直达——本组件只服务命令式入口,两路径各司其职
|
|
69
|
+
- **无标题 / 无按钮语义**:lightbox 沉浸形态不配 title/footer/确认取消——需要「弹窗 + 图片 + 操作按钮」的形态用 `ModalConfirm` + `ElImage` 消费方自拼
|
|
70
|
+
- **关闭映射链**:ElImageViewer 关闭 → `update:visible` → `update:show` → 架子收尾——[MUST NOT] 另接关闭逻辑造成双通道
|
|
71
|
+
|
|
72
|
+
## 关联
|
|
73
|
+
|
|
74
|
+
- 弹层族:`ModalConfirm`(确认/表单弹窗体系——图片预览不再用它做底座)、`ModalForm`、`ModalDetail`
|
|
75
|
+
- 预览族:`ModalVideoPreview`(视频预览,同为 lightbox 形态;视频无 el 原生组件,ElDialog 直配黑底)
|
|
76
|
+
- 触发端:`ImagePreviewTrigger`(ElImage + 懒加载 + 占位兜底,**点击直达 ElImage 原生预览**,不经本组件)
|
|
77
|
+
- 命令式弹窗体系:`ModalShelf`(架子 + level 层级)+ `ModalPorter`(modalList 注册表)+ 全局遥控器(`globalModalRemote.evoke(APP_GLOBAL_IMAGE_PREVIEW_KEY, { src })`)
|
|
78
|
+
- 范式页:`apps/reference/src/pages/modal/preview/showcase/`
|
|
@@ -43,6 +43,7 @@ const modalList: RegisterEntry[] = [
|
|
|
43
43
|
| name | type | 默认 | 语义 |
|
|
44
44
|
| --- | --- | --- | --- |
|
|
45
45
|
| `modalList` | `RegisterEntry[]`(必传) | — | 弹窗注册表(key → component/懒工厂 → shelf → sameKeyStrategy) |
|
|
46
|
+
| `globalOnly` | `symbol`(core 内部专用) | 无默认 | **专用模式标识**:仅 AppLayout 内部调用时传(`APP_GLOBAL_MODAL_PORTER_SYMBOL`,core 内部不导出)——只接受 `shelf:"app"` 条目推送,非 app 条目 `console.error` + 过滤;外部 [MUST NOT] 使用(Symbol 拿不到即无法误用) |
|
|
46
47
|
|
|
47
48
|
### Slots
|
|
48
49
|
|
|
@@ -70,6 +71,14 @@ const modalList: RegisterEntry[] = [
|
|
|
70
71
|
|
|
71
72
|
失活/卸载时自动 remove 本 porter 注册的全部 nsKey(useActivated 统一收口)——Porter 销毁即弹窗实例随架清理,**[MUST NOT]** 手动补偿清理。
|
|
72
73
|
|
|
74
|
+
### 专用模式(`globalOnly`:AppLayout 内部全局弹窗 Porter)
|
|
75
|
+
|
|
76
|
+
AppLayout 在调用内置 app 级 ModalShelf 的**子元素最前**位置调用一个打 `globalOnly` 标识的专用 Porter,配置数据直接来自 AppLayout 的 `globalModalConfig` prop——**全局弹窗的唯一注册入口**。专用模式下:
|
|
77
|
+
|
|
78
|
+
- 只接受 `shelf:"app"` 条目;**非 app 条目(page/custom)`console.error` + 过滤**——page 级弹窗注册不得逃逸到全局面(page 级作用域由路由层保证);
|
|
79
|
+
- 推送的配置落到 app 级架子 → 挂载进 `globalModalRemote`(经 AppLayout `v-model:globalModalRemote` → 内置架子 `v-model:remote`),应用根即可遥控;
|
|
80
|
+
- 专用 Porter 生命周期与 AppLayout 同寿——**规避页面级 Porter 配 app 级弹窗的孤儿弹窗反模式**(页面卸载后弹窗还挂在常驻 app 架子上、句柄丢失)。
|
|
81
|
+
|
|
73
82
|
### 注入给弹窗的 props(`ModalInjectedProps<P>`)
|
|
74
83
|
|
|
75
84
|
架子把 `{show, closeFn, removeFn, payload: P}` 注入弹窗组件(见 ModalShelf 文档)。
|
|
@@ -80,6 +89,7 @@ const modalList: RegisterEntry[] = [
|
|
|
80
89
|
- **shelf 字符串只认 "app"/"page"**:custom 架子必须传 `ShelfInstance`(ref 实例),传字符串进 custom 寻址不通
|
|
81
90
|
- **key 类型必须声明合并**:evoke 的 key 泛型约束 `keyof ModalShelfMap`——业务 [MUST] 在 `.d.ts` 里 declare module 扩展 `ModalShelfMap`(见 ModalShelf 文档),否则 key 不在 map 上 TS 直接拦截
|
|
82
91
|
- **自动清理别补偿**:Porter 失活/卸载已自动 remove 全部 nsKey,手写 `onUnmounted` 清理是重复劳动(也可能提前清掉活跃弹窗)
|
|
92
|
+
- **页面级 Porter [MUST NOT] 配 `shelf:"app"` 条目**:页面卸载后弹窗仍挂在常驻 app 架子上 → 孤儿弹窗(唤起句柄已死、只剩弹窗自身 closeFn);app 级弹窗唯一正道 = AppLayout `globalModalConfig` + 内部专用 Porter + `globalModalRemote` 遥控
|
|
83
93
|
- **弹窗 UI 不在 Porter 插槽里写**:Porter 只是注册表宿主,弹窗经 evoke 落在架子上渲染——插槽只放页面内容
|
|
84
94
|
|
|
85
95
|
## 关联
|