vue2-components-plus 1.0.28 → 1.0.32
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/dist/ComponentDemo/DialogDemo.md +327 -0
- package/dist/ComponentDemo/{DialogSetupDemo.vue → DialogDemo.vue} +132 -14
- package/dist/ComponentDemo/DirectivesDemo.md +297 -0
- package/dist/ComponentDemo/DirectivesDemo.vue +0 -2
- package/dist/ComponentDemo/FormDemo.md +432 -0
- package/dist/ComponentDemo/{FormSetupDemo.vue → FormDemo.vue} +75 -1
- package/dist/ComponentDemo/TableDemo.md +573 -0
- package/dist/ComponentDemo/TableDemo.vue +980 -0
- package/dist/vue2-components-plus.css +1 -1
- package/dist/vue2-components-plus.es5.js +36 -10
- package/dist/vue2-components-plus.js +23 -9
- package/dist/vue2-components-plus.umd.cjs +1 -1
- package/package.json +1 -1
- package/dist/ComponentDemo/NsTableDemo/SetupDemo.vue +0 -546
- /package/dist/ComponentDemo/{NsTableDemo/mockData.js → mockData.js} +0 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# NsDialog 使用说明(面向 AI 代码生成)
|
|
2
|
+
|
|
3
|
+
本文档严格对应当前仓库实现,目标不是介绍“理想 API”,而是让 AI 和开发者基于现有能力直接生成可运行代码。
|
|
4
|
+
|
|
5
|
+
## 1. 组件定位
|
|
6
|
+
|
|
7
|
+
- `NsDialog` 是函数式弹窗工厂,不是通过 `<NsDialog />` 标签直接使用的常规组件。
|
|
8
|
+
- 入口文件:`packages/components/NsDialog/index.js`
|
|
9
|
+
- 视图实现:`packages/components/NsDialog/NsDialog.vue`
|
|
10
|
+
- 调用方式:`window.NsDialog(config, modal?, appendTo?)`
|
|
11
|
+
- 支持多实例;所有实例会记录到 `window.__dialogInstances`
|
|
12
|
+
- 返回值是实例对象,不是 Promise
|
|
13
|
+
|
|
14
|
+
## 2. 最小可运行示例
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import FormDemo from '@/views/FormDemo.vue'
|
|
18
|
+
|
|
19
|
+
const instance = window.NsDialog(
|
|
20
|
+
{
|
|
21
|
+
title: '示例弹窗',
|
|
22
|
+
dom: FormDemo,
|
|
23
|
+
option: {
|
|
24
|
+
readOnly: false
|
|
25
|
+
},
|
|
26
|
+
events: {
|
|
27
|
+
btnClick(payload) {
|
|
28
|
+
console.log('收到内容组件事件', payload)
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
true,
|
|
33
|
+
'#app',
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 3. 调用签名
|
|
38
|
+
|
|
39
|
+
### 3.1 `NsDialog(config, modal, appendTo)`
|
|
40
|
+
|
|
41
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| `config` | `Object` | - | 弹窗配置对象,必须至少包含 `dom` |
|
|
44
|
+
| `modal` | `Boolean` | `true` | 是否显示遮罩 |
|
|
45
|
+
| `appendTo` | `String` | `'#app'` | 挂载容器选择器,找不到时回退到 `document.body` |
|
|
46
|
+
|
|
47
|
+
### 3.2 返回值
|
|
48
|
+
|
|
49
|
+
如果 `config` 缺失或 `config.dom` 不存在,返回 `false`。正常情况下返回实例对象。
|
|
50
|
+
|
|
51
|
+
## 4. `config` 字段总表
|
|
52
|
+
|
|
53
|
+
下表为当前实现实际支持的字段,来源于 `NsDialog.vue` 的 `props` 和 `index.js` 工厂增强逻辑。
|
|
54
|
+
|
|
55
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `id` | `String` | 自动生成 | 实例 id |
|
|
58
|
+
| `class` | `String` | `''` | 最终映射为内部 `className` |
|
|
59
|
+
| `title` | `String` | `''` | 弹窗标题 |
|
|
60
|
+
| `width` | `Number \| String` | `500` | 弹窗宽度,数字会补 `px` |
|
|
61
|
+
| `height` | `Number \| String` | `''` | 弹窗高度;设置后弹窗 body 使用纵向自适应布局 |
|
|
62
|
+
| `dialogPadding` | `Number \| String \| Array` | `-1` | body 内边距;`-1` 表示默认 `16px 20px` |
|
|
63
|
+
| `modalColor` | `String` | `rgba(0, 0, 0, 0.45)` | 遮罩颜色 |
|
|
64
|
+
| `closeOnClickModal` | `Boolean` | `true` | 点击遮罩是否关闭 |
|
|
65
|
+
| `dom` | `Object \| Function` | `null` | 主体内容组件,必填 |
|
|
66
|
+
| `option` | `Object` | `{}` | 透传给 `dom` 的 props |
|
|
67
|
+
| `events` | `Object` | `{}` | 透传给 `dom` 的事件 |
|
|
68
|
+
| `domCompleted` | `Function` | `null` | 内容组件 ref 可用后的回调,参数为内容 ref |
|
|
69
|
+
| `headerDom` | `Object \| Function` | `null` | 自定义头部组件 |
|
|
70
|
+
| `headerOption` | `Object` | `{}` | 透传给 `headerDom` 的 props |
|
|
71
|
+
| `headerEvents` | `Object` | `{}` | 透传给 `headerDom` 的事件 |
|
|
72
|
+
| `showFooter` | `Boolean` | `true` | 是否显示底部 |
|
|
73
|
+
| `footerDom` | `Object \| Function` | `null` | 自定义底部组件 |
|
|
74
|
+
| `footerOption` | `Object` | `{}` | 透传给 `footerDom` 的 props |
|
|
75
|
+
| `footerTitle` | `Object` | `{ close: '取消', confirm: '确定' }` | 默认底部按钮文案 |
|
|
76
|
+
| `footerEvents` | `Object` | `{}` | 透传给 `footerDom` 的事件 |
|
|
77
|
+
| `footerButtonReverse` | `Boolean` | `false` | 组件默认值为 `false`,但通过工厂创建时当前实现会强制改成 `true` |
|
|
78
|
+
| `footerCloseOnly` | `Boolean` | `false` | 仅对默认底部生效;设为 `true` 时只显示关闭按钮,不显示确认按钮 |
|
|
79
|
+
| `immediately` | `Boolean` | `false` | 点击确认后是否先关闭再执行 `confirm` |
|
|
80
|
+
| `close` | `Function` | `null` | `el-dialog` 的 `close` 阶段回调 |
|
|
81
|
+
| `closed` | `Function` | `null` | `el-dialog` 的 `closed` 阶段回调;工厂会在这里销毁实例 |
|
|
82
|
+
| `draggable` | `Boolean` | `false` | 是否允许拖拽标题栏 |
|
|
83
|
+
| `confirm` | `Function` | `null` | 点击默认确认按钮时的回调 |
|
|
84
|
+
| `x` | `Number \| String` | `null` | 固定定位 `left` |
|
|
85
|
+
| `y` | `Number \| String` | `null` | 固定定位 `top` |
|
|
86
|
+
| `maxSize` | `Function` | `null` | 存在时显示最大化按钮,函数返回最大化后的宽高坐标 |
|
|
87
|
+
| `store` | `Object` | `null` | 注入给动态实例的 Vuex store |
|
|
88
|
+
| `pinia` | `Object` | `null` | 注入给动态实例的 Pinia 实例 |
|
|
89
|
+
|
|
90
|
+
## 5. 核心行为
|
|
91
|
+
|
|
92
|
+
### 5.1 内容组件如何接收数据
|
|
93
|
+
|
|
94
|
+
- `option` 会以 `v-bind="currentOption"` 的方式透传给内容组件
|
|
95
|
+
- `events` 会以 `v-on="mergeEvents(events)"` 的方式透传给内容组件
|
|
96
|
+
- `mergeEvents` 会额外注入一个 `close` 事件,因此内容组件内部可以直接 `this.$emit('close')`
|
|
97
|
+
|
|
98
|
+
### 5.2 头部和底部如何自定义
|
|
99
|
+
|
|
100
|
+
- `headerDom` / `footerDom` 接收组件对象或异步组件
|
|
101
|
+
- `headerOption` / `footerOption` 作为 props 透传
|
|
102
|
+
- `headerEvents` / `footerEvents` 作为事件透传
|
|
103
|
+
- 这两类事件同样会额外合并 `close`
|
|
104
|
+
|
|
105
|
+
### 5.3 默认确认按钮行为
|
|
106
|
+
|
|
107
|
+
- 只有在 `showFooter=true` 且未传 `footerDom` 时,才会显示默认确认/取消按钮
|
|
108
|
+
- 若 `footerCloseOnly=true`,默认底部只显示关闭按钮
|
|
109
|
+
- 点击默认确认按钮后会执行 `dealConfirm`
|
|
110
|
+
- 若未配置 `confirm`,按钮只会短暂进入 loading 后立即结束,不会自动关闭
|
|
111
|
+
- 若 `confirm` 中调用了 `closeFn()`,组件会关闭,并自动弹出一次 `操作成功`
|
|
112
|
+
|
|
113
|
+
### 5.4 `immediately=true` 的真实行为
|
|
114
|
+
|
|
115
|
+
这一点是 AI 生成代码最容易写错的地方。
|
|
116
|
+
|
|
117
|
+
- 点击确认后会先关闭弹窗
|
|
118
|
+
- 然后调用 `confirm(null, contentRef)`
|
|
119
|
+
- 当前实现不会传入第三个 `loadingProxy`
|
|
120
|
+
- 因为弹窗已经先关闭,所以 `immediately=true` 更适合“无需等待结果、触发即关”的场景
|
|
121
|
+
|
|
122
|
+
### 5.5 拖拽、定位、最大化
|
|
123
|
+
|
|
124
|
+
- `draggable=true` 时,只有标题栏可拖拽
|
|
125
|
+
- 只要传了 `x` 或 `y`,弹窗就改为 `position: fixed`
|
|
126
|
+
- 最大化按钮是否显示,不取决于单独开关,而是取决于 `maxSize` 是否为函数
|
|
127
|
+
- `maxSize()` 返回的对象支持 `width`、`height`、`x`、`y`
|
|
128
|
+
|
|
129
|
+
### 5.6 回车行为
|
|
130
|
+
|
|
131
|
+
- 当 `visible=true`、`showFooter=true`、`footerDom` 不存在时,按回车会触发默认确认逻辑
|
|
132
|
+
- 当 `footerCloseOnly=true` 时,即使使用默认底部,回车也不会触发确认
|
|
133
|
+
- 如果用了自定义底部组件,回车不会自动触发 `footerEvents.confirm`
|
|
134
|
+
|
|
135
|
+
## 6. `confirm` 回调签名
|
|
136
|
+
|
|
137
|
+
### 6.1 常规模式
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
confirm(closeFn, contentRef, loadingProxy) {
|
|
141
|
+
return Promise.resolve(contentRef.getFormData()).then((data) => {
|
|
142
|
+
if (!data) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
closeFn()
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 6.2 参数含义
|
|
151
|
+
|
|
152
|
+
| 参数 | 说明 |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `closeFn` | 调用后关闭弹窗,仅在 `immediately=false` 时有意义 |
|
|
155
|
+
| `contentRef` | 内容组件实例,可调用其公开方法 |
|
|
156
|
+
| `loadingProxy` | 当前实现意图上用于控制确认按钮 loading,但只有常规模式下才会传入 |
|
|
157
|
+
|
|
158
|
+
### 6.3 更稳妥的生成策略
|
|
159
|
+
|
|
160
|
+
- 如果需要异步校验后再关闭,用 `immediately=false`
|
|
161
|
+
- 内容组件应暴露公开方法,例如 `getFormData`
|
|
162
|
+
- 让 `confirm` 只负责校验、提交和决定是否调用 `closeFn`
|
|
163
|
+
- 不要假设 `confirm` 返回 Promise 后会被组件自动等待;当前实现不会自动处理返回值
|
|
164
|
+
|
|
165
|
+
## 7. 实例对象能力
|
|
166
|
+
|
|
167
|
+
`NsDialog(...)` 返回的实例对象结构如下。
|
|
168
|
+
|
|
169
|
+
| 字段/方法 | 说明 |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `id` | 实例 id |
|
|
172
|
+
| `domRef` | 内容组件 ref,渲染完成后可用 |
|
|
173
|
+
| `close()` | 主动关闭弹窗 |
|
|
174
|
+
| `updateOption(partial)` | 动态更新配置 |
|
|
175
|
+
| `callMethod(name, ...args)` | 调用内容组件实例方法 |
|
|
176
|
+
|
|
177
|
+
### 7.1 `updateOption` 的真实更新范围
|
|
178
|
+
|
|
179
|
+
`updateOption(partial)` 会对以下字段做特殊处理:
|
|
180
|
+
|
|
181
|
+
- `title`
|
|
182
|
+
- `width`
|
|
183
|
+
- `height`
|
|
184
|
+
- `x`
|
|
185
|
+
- `y`
|
|
186
|
+
|
|
187
|
+
除此之外,其余字段不会回写到顶层配置,而是会并入 `currentOption`,等价于继续给内容组件追加 props。
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
const instance = window.NsDialog({
|
|
191
|
+
title: '用户编辑',
|
|
192
|
+
dom: FormDemo,
|
|
193
|
+
option: { readOnly: false },
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
instance.updateOption({
|
|
197
|
+
title: '用户详情',
|
|
198
|
+
width: '960px',
|
|
199
|
+
readOnly: true,
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const data = await instance.callMethod('getFormData')
|
|
203
|
+
instance.close()
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## 8. 全局能力
|
|
207
|
+
|
|
208
|
+
| 方法 | 位置 | 说明 |
|
|
209
|
+
|---|---|---|
|
|
210
|
+
| `closeAllNsDialog()` | `packages/components/NsDialog/index.js` | 关闭当前所有实例 |
|
|
211
|
+
| `setExternalApp(app, options)` | `packages/components/NsDialog/index.js` | 解析外部 Vue / store / pinia 上下文 |
|
|
212
|
+
|
|
213
|
+
### 8.1 何时使用 `setExternalApp`
|
|
214
|
+
|
|
215
|
+
- 在非当前 Vue 根实例上下文里调用 `window.NsDialog`
|
|
216
|
+
- 希望动态弹窗也能访问外部 store 或 pinia
|
|
217
|
+
- 希望用指定的 Vue 构造器创建实例
|
|
218
|
+
|
|
219
|
+
## 9. Demo 功能映射
|
|
220
|
+
|
|
221
|
+
`src/views/DialogDemo.vue` 当前已验证以下组合模式:
|
|
222
|
+
|
|
223
|
+
| 场景 | 对应能力 |
|
|
224
|
+
|---|---|
|
|
225
|
+
| 打开普通弹窗 | `dom + option + events` |
|
|
226
|
+
| 打开只读弹窗 | `option.readOnly` |
|
|
227
|
+
| 仅显示关闭按钮 | `footerCloseOnly=true` |
|
|
228
|
+
| 多开错位显示 | `x / y` 动态偏移 |
|
|
229
|
+
| 自定义头部 | `headerDom + headerOption` |
|
|
230
|
+
| 自定义底部 | `footerDom + footerOption + footerEvents` |
|
|
231
|
+
| 禁止点击遮罩关闭 | `closeOnClickModal=false` |
|
|
232
|
+
| 蓝色遮罩 | `modalColor` |
|
|
233
|
+
| 最大化 | `maxSize` |
|
|
234
|
+
| 动态切换标题和内容 props | `instance.updateOption()` |
|
|
235
|
+
| 调用内容方法 | `instance.callMethod()` |
|
|
236
|
+
| 全量关闭 | `this.$closeAllNsDialog()` 或 `window.closeAllNsDialog()` |
|
|
237
|
+
|
|
238
|
+
## 10. AI 生成代码约束
|
|
239
|
+
|
|
240
|
+
- 必须通过 `window.NsDialog(...)` 调用,不要写成 `<NsDialog />`
|
|
241
|
+
- `dom` 必填
|
|
242
|
+
- 需要被外层调用的方法必须在内容组件上暴露为实例方法
|
|
243
|
+
- `updateOption()` 适合改标题、宽高、坐标和内容 props,不适合更新所有顶层行为配置
|
|
244
|
+
- 自定义底部时,关闭动作要么触发 `$emit('close')`,要么由外层自己维护逻辑
|
|
245
|
+
- 若要保留多个弹窗,业务侧应保存返回实例数组
|
|
246
|
+
|
|
247
|
+
## 11. 推荐 Prompt
|
|
248
|
+
|
|
249
|
+
```text
|
|
250
|
+
请基于当前项目的 NsDialog 工厂生成 Vue2.7 页面,要求:
|
|
251
|
+
1) 只能通过 window.NsDialog(config, modal, '#app') 打开弹窗;
|
|
252
|
+
2) 演示普通弹窗、只读弹窗、自定义 headerDom/footerDom 弹窗;
|
|
253
|
+
3) 内容组件通过 option 接收 props,通过 events 向外派发事件;
|
|
254
|
+
4) 演示 instance.updateOption 和 instance.callMethod;
|
|
255
|
+
5) 演示 draggable、x/y 定位、maxSize 最大化;
|
|
256
|
+
6) 演示 closeAllNsDialog;
|
|
257
|
+
7) 代码风格与当前仓库一致,不使用 TS,不虚构不存在的 props。
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## 12. 标准模板
|
|
261
|
+
|
|
262
|
+
```vue
|
|
263
|
+
<template>
|
|
264
|
+
<div>
|
|
265
|
+
<el-button type="primary" @click="openDialog">打开弹窗</el-button>
|
|
266
|
+
<el-button :disabled="!dialogInstance" @click="toggleReadonly">切换只读</el-button>
|
|
267
|
+
<el-button :disabled="!dialogInstance" @click="callInnerMethod">调用内容方法</el-button>
|
|
268
|
+
</div>
|
|
269
|
+
</template>
|
|
270
|
+
|
|
271
|
+
<script setup>
|
|
272
|
+
import { ref } from 'vue'
|
|
273
|
+
import FormDemo from '@/views/FormDemo.vue'
|
|
274
|
+
|
|
275
|
+
const dialogInstance = ref(null)
|
|
276
|
+
const readonly = ref(false)
|
|
277
|
+
|
|
278
|
+
const openDialog = () => {
|
|
279
|
+
dialogInstance.value = window.NsDialog(
|
|
280
|
+
{
|
|
281
|
+
title: '用户编辑',
|
|
282
|
+
width: '960px',
|
|
283
|
+
height: '620px',
|
|
284
|
+
dom: FormDemo,
|
|
285
|
+
draggable: true,
|
|
286
|
+
x: 'calc(50% - 480px)',
|
|
287
|
+
y: 'calc(50% - 310px)',
|
|
288
|
+
option: {
|
|
289
|
+
readOnly: readonly.value
|
|
290
|
+
},
|
|
291
|
+
events: {
|
|
292
|
+
btnClick(payload) {
|
|
293
|
+
console.log('内容组件事件', payload)
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
confirm: async (closeFn, contentRef) => {
|
|
297
|
+
const result = await contentRef.getFormData()
|
|
298
|
+
if (!result) {
|
|
299
|
+
return
|
|
300
|
+
}
|
|
301
|
+
closeFn()
|
|
302
|
+
},
|
|
303
|
+
closed: () => {
|
|
304
|
+
dialogInstance.value = null
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
true,
|
|
308
|
+
'#app',
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const toggleReadonly = () => {
|
|
313
|
+
if (!dialogInstance.value) return
|
|
314
|
+
readonly.value = !readonly.value
|
|
315
|
+
dialogInstance.value.updateOption({
|
|
316
|
+
title: readonly.value ? '用户详情' : '用户编辑',
|
|
317
|
+
readOnly: readonly.value,
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const callInnerMethod = async () => {
|
|
322
|
+
if (!dialogInstance.value) return
|
|
323
|
+
const data = await dialogInstance.value.callMethod('getFormData')
|
|
324
|
+
console.log(data)
|
|
325
|
+
}
|
|
326
|
+
</script>
|
|
327
|
+
```
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
<el-card shadow="never" class="dialog-demo__card">
|
|
4
4
|
<div slot="header" class="dialog-demo__header">
|
|
5
5
|
<div>
|
|
6
|
-
<div class="dialog-demo__title"
|
|
7
|
-
<div class="dialog-demo__desc">Vue2.7 + <script setup>
|
|
6
|
+
<div class="dialog-demo__title">多实例与方法调用面板</div>
|
|
7
|
+
<div class="dialog-demo__desc">Vue2.7 + <script setup>:演示多开、更新配置、调用内部方法、关闭单个与全部弹窗。</div>
|
|
8
8
|
|
|
9
9
|
</div>
|
|
10
10
|
<el-tag size="small" type="success">当前 {{ dialogInstances.length }} 个实例</el-tag>
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
<div class="dialog-demo__actions">
|
|
14
14
|
<el-button type="primary" @click="openDialog()">打开弹窗</el-button>
|
|
15
15
|
<el-button @click="openReadonlyDialog">打开只读弹窗</el-button>
|
|
16
|
+
<el-button @click="openCloseOnlyDialog">打开仅关闭按钮弹窗</el-button>
|
|
17
|
+
<el-button @click="openCustomShellDialog">打开自定义头底部弹窗</el-button>
|
|
16
18
|
<el-button @click="updateDialogOption">更新最后一个弹窗</el-button>
|
|
17
19
|
<el-button @click="callDialogMethod">调用最后一个弹窗内容方法</el-button>
|
|
18
20
|
<el-button type="danger" plain :disabled="!dialogInstances.length" @click="closeAllDialogs">关闭全部</el-button>
|
|
@@ -41,8 +43,9 @@
|
|
|
41
43
|
<el-col :span="14">
|
|
42
44
|
<el-card shadow="never" class="dialog-demo__card">
|
|
43
45
|
<div slot="header">能力说明</div>
|
|
44
|
-
<el-steps direction="vertical" :active="
|
|
46
|
+
<el-steps direction="vertical" :active="5" finish-status="success">
|
|
45
47
|
<el-step title="打开弹窗" description="每次打开会创建独立实例,并做错位展示。" />
|
|
48
|
+
<el-step title="头尾插槽" description="支持通过 headerDom / footerDom 渲染自定义头部和底部。" />
|
|
46
49
|
<el-step title="更新配置" description="可更新标题、宽高、位置以及传入内容组件的 props。" />
|
|
47
50
|
<el-step title="调用方法" description="通过实例调用弹窗内部组件的公开方法,例如获取表单数据。" />
|
|
48
51
|
<el-step title="关闭管理" description="支持关闭指定实例与一键关闭全部实例。" />
|
|
@@ -55,13 +58,76 @@
|
|
|
55
58
|
|
|
56
59
|
<script setup>
|
|
57
60
|
import { ref, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue'
|
|
58
|
-
import FormDemo from '@/views/
|
|
61
|
+
import FormDemo from '@/views/FormDemo.vue'
|
|
59
62
|
|
|
60
63
|
const dialogInstances = ref([])
|
|
61
64
|
const openIndex = ref(0)
|
|
62
65
|
const lastReadOnly = ref(false)
|
|
63
66
|
const { proxy } = getCurrentInstance()
|
|
64
67
|
|
|
68
|
+
const DialogHeaderBadge = {
|
|
69
|
+
name: 'DialogHeaderBadge',
|
|
70
|
+
props: {
|
|
71
|
+
title: {
|
|
72
|
+
type: String,
|
|
73
|
+
default: '自定义头部',
|
|
74
|
+
},
|
|
75
|
+
tagText: {
|
|
76
|
+
type: String,
|
|
77
|
+
default: '高级',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
render(h) {
|
|
81
|
+
return h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, [
|
|
82
|
+
h('span', { style: { fontWeight: 600 } }, this.title),
|
|
83
|
+
h(
|
|
84
|
+
'el-tag',
|
|
85
|
+
{
|
|
86
|
+
props: { size: 'mini', type: 'warning' },
|
|
87
|
+
},
|
|
88
|
+
[this.tagText],
|
|
89
|
+
),
|
|
90
|
+
])
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const DialogFooterActions = {
|
|
95
|
+
name: 'DialogFooterActions',
|
|
96
|
+
props: {
|
|
97
|
+
confirmText: {
|
|
98
|
+
type: String,
|
|
99
|
+
default: '自定义确认',
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
methods: {
|
|
103
|
+
emitConfirm() {
|
|
104
|
+
this.$emit('confirm')
|
|
105
|
+
},
|
|
106
|
+
emitClose() {
|
|
107
|
+
this.$emit('close')
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
render(h) {
|
|
111
|
+
return h('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: '8px' } }, [
|
|
112
|
+
h(
|
|
113
|
+
'el-button',
|
|
114
|
+
{
|
|
115
|
+
on: { click: this.emitClose },
|
|
116
|
+
},
|
|
117
|
+
['关闭'],
|
|
118
|
+
),
|
|
119
|
+
h(
|
|
120
|
+
'el-button',
|
|
121
|
+
{
|
|
122
|
+
props: { type: 'primary' },
|
|
123
|
+
on: { click: this.emitConfirm },
|
|
124
|
+
},
|
|
125
|
+
[this.confirmText],
|
|
126
|
+
),
|
|
127
|
+
])
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
|
|
65
131
|
const refreshInstances = () => {
|
|
66
132
|
setTimeout(() => {
|
|
67
133
|
dialogInstances.value = Array.isArray(window.__dialogInstances)
|
|
@@ -77,20 +143,27 @@ const openDialog = (options = {}) => {
|
|
|
77
143
|
}
|
|
78
144
|
const offset = openIndex.value * 24
|
|
79
145
|
const readOnly = !!options.readOnly
|
|
80
|
-
|
|
146
|
+
const resolvedOption = Object.assign(
|
|
147
|
+
{
|
|
148
|
+
readOnly,
|
|
149
|
+
insideDialog: true,
|
|
150
|
+
hintText: readOnly ? '当前是只读弹窗内容。' : '可以在弹窗中直接编辑表单并触发事件。',
|
|
151
|
+
},
|
|
152
|
+
options.option || {},
|
|
153
|
+
)
|
|
154
|
+
const baseEvents = Object.assign(
|
|
155
|
+
{
|
|
156
|
+
btnClick: handleInnerButtonClick,
|
|
157
|
+
},
|
|
158
|
+
options.events || {},
|
|
159
|
+
)
|
|
160
|
+
const dialogConfig = Object.assign(
|
|
81
161
|
{
|
|
82
162
|
title: options.title || 'NsDialog 示例(setup)',
|
|
83
163
|
class: 'dialog-demo-instance',
|
|
84
164
|
dom: FormDemo,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
readOnly,
|
|
88
|
-
insideDialog: true,
|
|
89
|
-
hintText: readOnly ? '当前是只读弹窗内容。' : '可以在弹窗中直接编辑表单并触发事件。',
|
|
90
|
-
},
|
|
91
|
-
events: {
|
|
92
|
-
btnClick: handleInnerButtonClick,
|
|
93
|
-
},
|
|
165
|
+
option: resolvedOption,
|
|
166
|
+
events: baseEvents,
|
|
94
167
|
width: options.width || '960px',
|
|
95
168
|
height: options.height || '620px',
|
|
96
169
|
dialogPadding: [10, 10],
|
|
@@ -122,6 +195,10 @@ const openDialog = (options = {}) => {
|
|
|
122
195
|
close: refreshInstances,
|
|
123
196
|
closed: refreshInstances,
|
|
124
197
|
},
|
|
198
|
+
options.extraConfig || {},
|
|
199
|
+
)
|
|
200
|
+
window.NsDialog(
|
|
201
|
+
dialogConfig,
|
|
125
202
|
true,
|
|
126
203
|
'#app',
|
|
127
204
|
)
|
|
@@ -137,6 +214,47 @@ const openReadonlyDialog = () => {
|
|
|
137
214
|
})
|
|
138
215
|
}
|
|
139
216
|
|
|
217
|
+
const openCloseOnlyDialog = () => {
|
|
218
|
+
openDialog({
|
|
219
|
+
title: '仅关闭按钮弹窗',
|
|
220
|
+
option: {
|
|
221
|
+
hintText: '底部仅保留关闭按钮,回车不会触发确认逻辑。',
|
|
222
|
+
},
|
|
223
|
+
extraConfig: {
|
|
224
|
+
footerCloseOnly: true,
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const openCustomShellDialog = () => {
|
|
230
|
+
openDialog({
|
|
231
|
+
title: '自定义头底部弹窗',
|
|
232
|
+
modal: true,
|
|
233
|
+
option: {
|
|
234
|
+
hintText: '当前演示 headerDom / footerDom / footerEvents / modalColor / closeOnClickModal',
|
|
235
|
+
},
|
|
236
|
+
extraConfig: {
|
|
237
|
+
modalColor: 'rgba(64, 158, 255, 0.2)',
|
|
238
|
+
closeOnClickModal: false,
|
|
239
|
+
immediately: true,
|
|
240
|
+
headerDom: DialogHeaderBadge,
|
|
241
|
+
headerOption: {
|
|
242
|
+
title: '高级弹窗头部',
|
|
243
|
+
tagText: 'Custom',
|
|
244
|
+
},
|
|
245
|
+
footerDom: DialogFooterActions,
|
|
246
|
+
footerOption: {
|
|
247
|
+
confirmText: '提交',
|
|
248
|
+
},
|
|
249
|
+
footerEvents: {
|
|
250
|
+
confirm: () => {
|
|
251
|
+
proxy.$message.success('触发了自定义底部 confirm 事件')
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
140
258
|
const updateDialogOption = () => {
|
|
141
259
|
if (!dialogInstances.value.length) {
|
|
142
260
|
proxy.$message.warning('请先打开一个弹窗')
|