vue2-components-plus 1.0.11 → 1.0.13

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/README.md CHANGED
@@ -173,6 +173,11 @@ NsDialog({
173
173
  closeAllNsDialog()
174
174
  ```
175
175
 
176
+ ### Legacy / ES5 兼容构建
177
+ - **命令**:`pnpm build:es5`(先执行默认 `pnpm build`,随后用 Babel 输出 `dist/vue2-components-plus.es5.js`)。
178
+ - **引入方式**:需要 ES5 时使用 `import Legacy from 'vue2-components-plus/legacy'`。
179
+ - **旧 webpack 配置提示**:若你的项目无法 tree-shake,可在 webpack 中对 `node_modules/vue2-components-plus` 走一次 `babel-loader`(target IE11)以避免语法解析错误。
180
+
176
181
  ## 🔧 自定义指令
177
182
 
178
183
  ### 1. v-permission - 按钮权限控制
@@ -98,6 +98,7 @@ export default {
98
98
  title: config.title || 'NsDialog 示例',
99
99
  class: 'dialog-demo-instance',
100
100
  dom: FormDemo,
101
+ showMaximizeButton: true,
101
102
  option: {
102
103
  readOnly,
103
104
  insideDialog: true,
@@ -110,9 +111,18 @@ export default {
110
111
  height: config.height || '620px',
111
112
  dialogPadding: [10, 10],
112
113
  modal: config.modal !== undefined ? config.modal : false,
114
+ // 拖动
113
115
  draggable: true,
114
- x: 120 + offset,
115
- y: 80 + offset,
116
+ // 最大化方法
117
+ maxSize: () => ({
118
+ width: '100%',
119
+ height: '100%',
120
+ x: 0,
121
+ y: 0
122
+ }),
123
+ // 初始化位置
124
+ x: 'calc(50% - 480px)',
125
+ y: 'calc(50% - 310px)',
116
126
  domCompleted: (domRef) => {
117
127
  if (!config.silent && domRef && typeof domRef.showToast === 'function') {
118
128
  domRef.showToast('弹窗内容已加载完成')
@@ -0,0 +1,277 @@
1
+ <template>
2
+ <div class="dialog-demo">
3
+ <el-card shadow="never" class="dialog-demo__card">
4
+ <div slot="header" class="dialog-demo__header">
5
+ <div>
6
+ <div class="dialog-demo__title">NsDialog 预览(&lt;script setup&gt; 版)</div>
7
+ <div class="dialog-demo__desc">Vue2.7 + &lt;script setup&gt; 写法:演示多开、更新配置、调用内部方法、关闭单个与全部弹窗。</div>
8
+
9
+ </div>
10
+ <el-tag size="small" type="success">当前 {{ dialogInstances.length }} 个实例</el-tag>
11
+ </div>
12
+
13
+ <div class="dialog-demo__actions">
14
+ <el-button type="primary" @click="openDialog()">打开弹窗</el-button>
15
+ <el-button @click="openReadonlyDialog">打开只读弹窗</el-button>
16
+ <el-button @click="updateDialogOption">更新最后一个弹窗</el-button>
17
+ <el-button @click="callDialogMethod">调用最后一个弹窗内容方法</el-button>
18
+ <el-button type="danger" plain :disabled="!dialogInstances.length" @click="closeAllDialogs">关闭全部</el-button>
19
+ </div>
20
+ </el-card>
21
+
22
+ <el-row :gutter="20">
23
+ <el-col :span="10">
24
+ <el-card shadow="never" class="dialog-demo__card dialog-demo__instance-card">
25
+ <div slot="header">弹窗实例列表</div>
26
+ <div v-if="dialogInstances.length" class="instance-list">
27
+ <div v-for="(instance, index) in dialogInstances" :key="instance.id" class="instance-item">
28
+ <div>
29
+ <div class="instance-item__title">实例 {{ index + 1 }}</div>
30
+ <div class="instance-item__meta">{{ instance.id }}</div>
31
+ </div>
32
+ <div class="instance-item__actions">
33
+ <el-button size="mini" @click="closeDialog(instance)">关闭</el-button>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ <el-empty v-else description="暂无已打开弹窗" :image-size="88" />
38
+ </el-card>
39
+ </el-col>
40
+
41
+ <el-col :span="14">
42
+ <el-card shadow="never" class="dialog-demo__card">
43
+ <div slot="header">能力说明</div>
44
+ <el-steps direction="vertical" :active="4" finish-status="success">
45
+ <el-step title="打开弹窗" description="每次打开会创建独立实例,并做错位展示。" />
46
+ <el-step title="更新配置" description="可更新标题、宽高、位置以及传入内容组件的 props。" />
47
+ <el-step title="调用方法" description="通过实例调用弹窗内部组件的公开方法,例如获取表单数据。" />
48
+ <el-step title="关闭管理" description="支持关闭指定实例与一键关闭全部实例。" />
49
+ </el-steps>
50
+ </el-card>
51
+ </el-col>
52
+ </el-row>
53
+ </div>
54
+ </template>
55
+
56
+ <script setup>
57
+ import { ref, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue'
58
+ import FormDemo from '@/views/FormSetupDemo.vue'
59
+
60
+ const dialogInstances = ref([])
61
+ const openIndex = ref(0)
62
+ const lastReadOnly = ref(false)
63
+ const { proxy } = getCurrentInstance()
64
+
65
+ const refreshInstances = () => {
66
+ setTimeout(() => {
67
+ dialogInstances.value = Array.isArray(window.__dialogInstances)
68
+ ? window.__dialogInstances.slice()
69
+ : []
70
+ }, 0)
71
+ }
72
+
73
+ const openDialog = (options = {}) => {
74
+ if (!window.NsDialog) {
75
+ proxy?.$message?.error('NsDialog 尚未挂载到全局')
76
+ return
77
+ }
78
+ const offset = openIndex.value * 24
79
+ const readOnly = !!options.readOnly
80
+ window.NsDialog(
81
+ {
82
+ title: options.title || 'NsDialog 示例(setup)',
83
+ class: 'dialog-demo-instance',
84
+ dom: FormDemo,
85
+ showMaximizeButton: true,
86
+ option: {
87
+ readOnly,
88
+ insideDialog: true,
89
+ hintText: readOnly ? '当前是只读弹窗内容。' : '可以在弹窗中直接编辑表单并触发事件。',
90
+ },
91
+ events: {
92
+ btnClick: handleInnerButtonClick,
93
+ },
94
+ width: options.width || '960px',
95
+ height: options.height || '620px',
96
+ dialogPadding: [10, 10],
97
+ modal: options.modal !== undefined ? options.modal : false,
98
+ draggable: true,
99
+ maxSize: () => ({
100
+ width: '100%',
101
+ height: '100%',
102
+ x: 0,
103
+ y: 0,
104
+ }),
105
+ x: `calc(50% - 480px + ${offset}px)`,
106
+ y: `calc(50% - 310px + ${offset}px)`,
107
+ domCompleted: (domRef) => {
108
+ if (!options.silent && domRef && typeof domRef.showToast === 'function') {
109
+ domRef.showToast('弹窗内容已加载完成')
110
+ }
111
+ },
112
+ confirm: (closeFn, componentRef) => {
113
+ if (componentRef && typeof componentRef.showToast === 'function') {
114
+ componentRef.showToast('点击了弹窗底部确认按钮')
115
+ }
116
+ setTimeout(() => {
117
+ if (typeof closeFn === 'function') {
118
+ closeFn()
119
+ }
120
+ }, 300)
121
+ },
122
+ close: refreshInstances,
123
+ closed: refreshInstances,
124
+ },
125
+ true,
126
+ '#app',
127
+ )
128
+ openIndex.value += 1
129
+ lastReadOnly.value = readOnly
130
+ refreshInstances()
131
+ }
132
+
133
+ const openReadonlyDialog = () => {
134
+ openDialog({
135
+ readOnly: true,
136
+ title: '只读预览弹窗(setup)',
137
+ })
138
+ }
139
+
140
+ const updateDialogOption = () => {
141
+ if (!dialogInstances.value.length) {
142
+ proxy?.$message?.warning('请先打开一个弹窗')
143
+ return
144
+ }
145
+ const lastInstance = dialogInstances.value[dialogInstances.value.length - 1]
146
+ lastReadOnly.value = !lastReadOnly.value
147
+ lastInstance.updateOption({
148
+ title: lastReadOnly.value ? '更新后的只读弹窗' : '更新后的编辑弹窗',
149
+ readOnly: lastReadOnly.value,
150
+ hintText: lastReadOnly.value ? '已通过 updateOption 切换为只读。' : '已通过 updateOption 切换为编辑。',
151
+ width: lastReadOnly.value ? '880px' : '960px',
152
+ })
153
+ proxy?.$message?.success('已更新最后一个弹窗配置')
154
+ }
155
+
156
+ const callDialogMethod = async () => {
157
+ if (!dialogInstances.value.length) {
158
+ proxy?.$message?.warning('请先打开一个弹窗')
159
+ return
160
+ }
161
+ const lastInstance = dialogInstances.value[dialogInstances.value.length - 1]
162
+ const result = await lastInstance.callMethod('getFormData')
163
+ if (result === false) {
164
+ proxy?.$message?.warning('弹窗内表单还未通过校验')
165
+ return
166
+ }
167
+ proxy?.$message?.success('已调用内部组件方法并获取结果')
168
+ }
169
+
170
+ const closeDialog = (instance) => {
171
+ if (!instance || typeof instance.close !== 'function') return
172
+ instance.close()
173
+ refreshInstances()
174
+ }
175
+
176
+ const closeAllDialogs = () => {
177
+ if (typeof proxy?.$closeAllNsDialog === 'function') {
178
+ proxy.$closeAllNsDialog()
179
+ } else if (typeof window.closeAllNsDialog === 'function') {
180
+ window.closeAllNsDialog()
181
+ } else if (Array.isArray(window.__dialogInstances)) {
182
+ window.__dialogInstances.slice().forEach((instance) => {
183
+ instance && typeof instance.close === 'function' && instance.close()
184
+ })
185
+ }
186
+ openIndex.value = 0
187
+ refreshInstances()
188
+ }
189
+
190
+ const handleInnerButtonClick = (payload) => {
191
+ const keys = payload ? Object.keys(payload) : []
192
+ proxy?.$message?.info(`收到弹窗内容事件,共 ${keys.length} 个字段`)
193
+ }
194
+
195
+ onMounted(() => {
196
+ refreshInstances()
197
+ setTimeout(() => {
198
+ openDialog({ silent: true })
199
+ }, 300)
200
+ })
201
+
202
+ onBeforeUnmount(() => {
203
+ closeAllDialogs()
204
+ })
205
+ </script>
206
+
207
+ <style scoped>
208
+ .dialog-demo {
209
+ display: flex;
210
+ flex-direction: column;
211
+ gap: 20px;
212
+ }
213
+
214
+ .dialog-demo__card {
215
+ border-radius: 12px;
216
+ }
217
+
218
+ .dialog-demo__header {
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: space-between;
222
+ }
223
+
224
+ .dialog-demo__title {
225
+ font-size: 18px;
226
+ font-weight: 700;
227
+ color: #303133;
228
+ }
229
+
230
+ .dialog-demo__desc {
231
+ margin-top: 6px;
232
+ font-size: 13px;
233
+ color: #909399;
234
+ }
235
+
236
+ .dialog-demo__actions {
237
+ display: flex;
238
+ flex-wrap: wrap;
239
+ gap: 12px;
240
+ }
241
+
242
+ .instance-list {
243
+ display: flex;
244
+ flex-direction: column;
245
+ gap: 12px;
246
+ }
247
+
248
+ .instance-item {
249
+ display: flex;
250
+ align-items: center;
251
+ justify-content: space-between;
252
+ padding: 14px 16px;
253
+ border: 1px solid #ebeef5;
254
+ border-radius: 10px;
255
+ background: #fafafa;
256
+ }
257
+
258
+ .instance-item__title {
259
+ font-size: 14px;
260
+ font-weight: 600;
261
+ color: #303133;
262
+ }
263
+
264
+ .instance-item__meta {
265
+ margin-top: 4px;
266
+ font-size: 12px;
267
+ color: #909399;
268
+ word-break: break-all;
269
+ }
270
+
271
+ @media (max-width: 960px) {
272
+ .dialog-demo__actions {
273
+ flex-direction: column;
274
+ align-items: stretch;
275
+ }
276
+ }
277
+ </style>