cy-element-ui 1.1.26 → 1.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,277 +1,315 @@
1
- <template>
2
- <div class="cy-tab-dialog">
3
- <div
4
- v-show="visible && modal"
5
- contenteditable="false"
6
- class="cy-tab-dialog-modal"
7
- :style="{ zIndex: modelZIndex, height: modelHeight ? modelHeight : '100%' }"
8
- @click="closeOnClickModal && handleClose"
9
- ></div>
10
-
11
- <el-dialog
12
- :visible="visible"
13
- :title="title"
14
- :width="width"
15
- :fullscreen="fullscreen"
16
- :modal="false"
17
- :modal-append-to-body="false"
18
- :append-to-body="false"
19
- :lock-scroll="true"
20
- :custom-class="customClass"
21
- :close-on-click-modal="appendToTagsView ? false : closeOnClickModal"
22
- :close-on-press-escape="appendToTagsView ? false : closeOnPressEscape"
23
- :show-close="showClose"
24
- :center="center"
25
- :destroy-on-close="destroyOnClose"
26
- contenteditable="false"
27
- v-dialog-drag="isDrag"
28
- v-dialog-drag-width="isDrag"
29
- v-dialog-drag-height="isDrag"
30
- :before-close="beforeClose"
31
- @open="handleOpen"
32
- @opened="handleOpened"
33
- @close="handleClose"
34
- @closed="handleClosed"
35
- >
36
- <template v-if="$slots.title" slot="title">
37
- <slot name="title"></slot>
38
- </template>
39
-
40
- <slot></slot>
41
-
42
- <template v-if="$slots.footer" slot="footer">
43
- <slot name="footer"></slot>
44
- </template>
45
- </el-dialog>
46
- </div>
47
- </template>
48
-
49
- <script>
50
- import ElDialog from '../../../dialog';
51
- import Vue from 'vue';
52
-
53
- export default {
54
- name: 'CyTabDialog',
55
- components: {
56
- ElDialog
57
- },
58
- props: {
59
- // 是否显示 Dialog,支持 .sync 修饰符
60
- visible: {
61
- type: Boolean,
62
- default: false
63
- },
64
- // Dialog 的标题,也可通过具名 slot
65
- title: {
66
- type: String,
67
- default: ''
68
- },
69
- // Dialog 的宽度
70
- width: {
71
- type: String,
72
- default: '50%'
73
- },
74
- // 是否为全屏 Dialog
75
- fullscreen: {
76
- type: Boolean,
77
- default: false
78
- },
79
- // Dialog CSS 中的 margin-top 值
80
- top: {
81
- type: String,
82
- default: '6vh'
83
- },
84
- // 是否需要遮罩层
85
- modal: {
86
- type: Boolean,
87
- default: true
88
- },
89
- // 遮罩层是否插入至 body 元素上,若为 false,则遮罩层会插入至 Dialog 的父元素上
90
- appendToBody: {
91
- type: Boolean,
92
- default: false
93
- },
94
- // 是否在 Dialog 出现时将 body或上层 滚动锁定
95
- lockScroll: {
96
- type: Boolean,
97
- default: true
98
- },
99
- // Dialog 的自定义类名
100
- customClass: {
101
- type: String,
102
- default: ''
103
- },
104
- // 是否可以通过点击 modal 关闭 Dialog
105
- closeOnClickModal: {
106
- type: Boolean,
107
- default: false
108
- },
109
- // 是否可以通过按下 ESC 关闭 Dialog
110
- closeOnPressEscape: {
111
- type: Boolean,
112
- default: true
113
- },
114
- // 是否显示关闭按钮
115
- showClose: {
116
- type: Boolean,
117
- default: true
118
- },
119
- // 是否对头部和底部采用居中布局
120
- center: {
121
- type: Boolean,
122
- default: false
123
- },
124
- // 关闭时销毁 Dialog 中的元素
125
- destroyOnClose: {
126
- type: Boolean,
127
- default: false
128
- },
129
- // 是否放到tagsView中,即当前app-views下面,此属性true时,appendToBody无效
130
- appendToTagsView: {
131
- type: Boolean,
132
- default: false
133
- },
134
- // 对话框是否可拖拽
135
- isDrag: {
136
- type: Boolean,
137
- default: true
138
- },
139
- // 关闭前的回调,会暂停 Dialog 的关闭
140
- beforeClose: {
141
- type: Function
142
- }
143
- },
144
- data() {
145
- return {
146
- // 判断dialog是否已经放到对应的div下
147
- rendered: false,
148
- modelZIndex: 1000,
149
- modelHeight: 0,
150
- // 当前app-views的dom元素
151
- parentDom: null,
152
- // 当前el-dialog的dom元素
153
- elDialogDom: null
154
- };
155
- },
156
- watch: {
157
- visible: {
158
- immediate: true,
159
- handler(val) {
160
- if (val) {
161
- this.$emit('open');
162
- Vue.nextTick(() => {
163
- const dom = this.$el;
164
- // 首次打开
165
- if (!this.rendered) {
166
- this.rendered = true;
167
- if (this.appendToTagsView) {
168
- // 将当前dialog挪到当前app-views里面 app-container
169
- this.parentDom = dom.closest('.app-container');
170
- if (!this.parentDom) this.parentDom = document.body;
171
- this.parentDom.append(dom);
172
- } else if (this.appendToBody) {
173
- // 将当前dialog挪到当前body里面
174
- this.parentDom = document.body;
175
- this.parentDom.append(dom);
176
- }
177
- // 添加键盘监听事件
178
- if (this.closeOnPressEscape) {
179
- document.addEventListener('keydown', this.escKeyDown);
180
- }
181
- }
182
-
183
- setTimeout(() => {
184
- if (this.parentDom) this.modelHeight = this.parentDom.scrollHeight + 'px';
185
- // 当前VDialog不在body中时
186
- if (!this.parentDom || this.parentDom.tagName.toUpperCase() !== 'BODY') {
187
- // 获取el-dialog的宽度,如果大于app-views的宽度,则el-dialog不居中显示
188
- let elDialogWidth = dom.getElementsByClassName('el-dialog')[0].clientWidth;
189
- if (!this.parentDom || elDialogWidth < this.parentDom.clientWidth) {
190
- dom.getElementsByClassName('el-dialog')[0].style.left = '50%';
191
- dom.getElementsByClassName('el-dialog')[0].style.transform = 'translateX(-50%)';
192
- } else {
193
- dom.getElementsByClassName('el-dialog')[0].style.left = '0';
194
- }
195
- }
196
- dom.getElementsByClassName('el-dialog')[0].style.marginTop = this.top;
197
- }, 50);
198
-
199
- // 获取el-dialog的z-index,赋值给model,用于处理model的层级问题
200
- this.elDialogDom = dom.children[1];
201
- setTimeout(() => {
202
- this.modelZIndex = this.elDialogDom.style.zIndex;
203
- }, 50);
204
- });
205
- } else {
206
- // immediate=true时,首次打开不需要触发close事件
207
- if (this.rendered) this.$emit('close');
208
- }
209
- }
210
- }
211
- },
212
- methods: {
213
- handleOpen() {
214
- this.$emit('open');
215
- },
216
-
217
- handleOpened() {
218
- this.$emit('opened');
219
- },
220
-
221
- handleClose() {
222
- this.$emit('update:visible', false);
223
- this.$emit('close');
224
- },
225
-
226
- handleClosed() {
227
- this.$emit('closed');
228
- },
229
-
230
- /**
231
- * 监听esc键
232
- */
233
- escKeyDown(event) {
234
- if (event.keyCode === 27) {
235
- // 获取body下的直接子节点中的已打开的vDialog
236
- let bodyVDialogDomArr = [];
237
- let bodyChildrenArr = document.body.childNodes;
238
- for (let i = 0; i < bodyChildrenArr.length; i++) {
239
- let item = bodyChildrenArr[i];
240
- if (item.tagName && item.tagName.toUpperCase() === 'DIV' && item.className && item.className.split(' ').includes('cy-tab-dialog-opened')) {
241
- bodyVDialogDomArr.push(item);
242
- }
243
- }
244
- // 判断body中是否有已打开的vDialog,如果有,优先关闭body下的vDialog
245
- if (bodyVDialogDomArr.length > 0) {
246
- // 获取当前cy-tab-dialog在当前body中的下标
247
- let curDomIndex = [].indexOf.call(bodyVDialogDomArr, this.$el);
248
- if (curDomIndex === bodyVDialogDomArr.length - 1) {
249
- this.handleClose();
250
- }
251
- } else if (document.getElementById('tags-view-container') && this.parentDom.parentElement.getAttribute('include_name')) {
252
- // 获取当前app-views对应的include_name值
253
- let includeName = this.parentDom.parentElement.getAttribute('include_name');
254
- // 根据includeName获取tagsView中的tag标签页
255
- let tagDom = document.getElementById('tags-view-container').querySelectorAll(`[include_name=${ includeName }]`);
256
- if (tagDom && tagDom[0]) {
257
- // 判断此标签页是否为选中状态,只有选中状态中的vDialog才可以进行关闭
258
- if (tagDom[0].className.split(' ').includes('active')) {
259
- let vDialogList = this.parentDom.getElementsByClassName('cy-tab-dialog-opened');
260
- // 获取当前cy-tab-dialog在当前app-views中的下标
261
- let curDomIndex = [].indexOf.call(vDialogList, this.$el);
262
- if (curDomIndex === vDialogList.length - 1) {
263
- this.handleClose();
264
- }
265
- }
266
- }
267
- } else {
268
- this.handleClose();
269
- }
270
- }
271
- }
272
- },
273
- beforeDestroy() {
274
- document.removeEventListener('keydown', this.escKeyDown);
275
- }
276
- };
277
- </script>
1
+ <template>
2
+ <!-- 组件根容器 -->
3
+ <div class="cy-tab-dialog">
4
+ <!-- 独立遮罩层:仅当 visible modal 为 true 时显示,点击可关闭 -->
5
+ <div
6
+ v-show="visible && modal"
7
+ contenteditable="false"
8
+ class="cy-tab-dialog-modal"
9
+ :style="{ zIndex: modelZIndex, height: modelHeight ? modelHeight : '100%' }"
10
+ @click="closeOnClickModal && handleClose"
11
+ ></div>
12
+
13
+ <!-- 基础对话框:禁用自带遮罩/滚动锁定,改用上方自管遮罩 -->
14
+ <el-dialog
15
+ :visible="visible"
16
+ :title="title"
17
+ :width="width"
18
+ :fullscreen="fullscreen"
19
+ :modal="false"
20
+ :modal-append-to-body="false"
21
+ :append-to-body="false"
22
+ :lock-scroll="true"
23
+ :custom-class="customClass"
24
+ :close-on-click-modal="appendToTagsView ? false : closeOnClickModal"
25
+ :close-on-press-escape="appendToTagsView ? false : closeOnPressEscape"
26
+ :show-close="showClose"
27
+ :center="center"
28
+ :destroy-on-close="destroyOnClose"
29
+ contenteditable="false"
30
+ v-dialog-drag="isDrag"
31
+ v-dialog-drag-width="isDrag"
32
+ v-dialog-drag-height="isDrag"
33
+ :before-close="beforeClose"
34
+ @open="handleOpen"
35
+ @opened="handleOpened"
36
+ @close="handleClose"
37
+ @closed="handleClosed"
38
+ >
39
+ <!-- 自定义标题插槽 -->
40
+ <template v-if="$slots.title" slot="title">
41
+ <slot name="title"></slot>
42
+ </template>
43
+
44
+ <!-- 默认插槽:对话框主体内容 -->
45
+ <slot></slot>
46
+
47
+ <!-- 自定义底部操作区插槽 -->
48
+ <template v-if="$slots.footer" slot="footer">
49
+ <slot name="footer"></slot>
50
+ </template>
51
+ </el-dialog>
52
+ </div>
53
+ </template>
54
+
55
+ <script>
56
+ // 引入 Element UI 底层 Dialog 组件(直接引用源码而非全量注册)
57
+ import ElDialog from '../../../dialog';
58
+ import Vue from 'vue';
59
+
60
+ export default {
61
+ name: 'CyTabDialog',
62
+ components: {
63
+ ElDialog
64
+ },
65
+ props: {
66
+ // 是否显示 Dialog,支持 .sync 修饰符
67
+ visible: {
68
+ type: Boolean,
69
+ default: false
70
+ },
71
+ // Dialog 的标题,也可通过具名 slot 自定义
72
+ title: {
73
+ type: String,
74
+ default: ''
75
+ },
76
+ // Dialog 的宽度
77
+ width: {
78
+ type: String,
79
+ default: '50%'
80
+ },
81
+ // 是否为全屏 Dialog
82
+ fullscreen: {
83
+ type: Boolean,
84
+ default: false
85
+ },
86
+ // Dialog CSS 中的 margin-top 值
87
+ top: {
88
+ type: String,
89
+ default: '6vh'
90
+ },
91
+ // 是否需要遮罩层
92
+ modal: {
93
+ type: Boolean,
94
+ default: true
95
+ },
96
+ // 遮罩层是否插入至 body 元素上,若为 false,则遮罩层会插入至 Dialog 的父元素上
97
+ appendToBody: {
98
+ type: Boolean,
99
+ default: false
100
+ },
101
+ // 是否在 Dialog 出现时将 body 或上层滚动锁定
102
+ lockScroll: {
103
+ type: Boolean,
104
+ default: true
105
+ },
106
+ // Dialog 的自定义类名
107
+ customClass: {
108
+ type: String,
109
+ default: ''
110
+ },
111
+ // 是否可以通过点击 modal 关闭 Dialog
112
+ closeOnClickModal: {
113
+ type: Boolean,
114
+ default: false
115
+ },
116
+ // 是否可以通过按下 ESC 关闭 Dialog
117
+ closeOnPressEscape: {
118
+ type: Boolean,
119
+ default: true
120
+ },
121
+ // 是否显示关闭按钮
122
+ showClose: {
123
+ type: Boolean,
124
+ default: true
125
+ },
126
+ // 是否对头部和底部采用居中布局
127
+ center: {
128
+ type: Boolean,
129
+ default: false
130
+ },
131
+ // 关闭时销毁 Dialog 中的元素
132
+ destroyOnClose: {
133
+ type: Boolean,
134
+ default: false
135
+ },
136
+ // 是否放到 tagsView 中(即当前 app-views 下面);此属性为 true 时 appendToBody 无效
137
+ appendToTagsView: {
138
+ type: Boolean,
139
+ default: false
140
+ },
141
+ // 对话框是否可拖拽
142
+ isDrag: {
143
+ type: Boolean,
144
+ default: true
145
+ },
146
+ // 关闭前的回调,会暂停 Dialog 的关闭
147
+ beforeClose: {
148
+ type: Function
149
+ }
150
+ },
151
+ data() {
152
+ return {
153
+ // 判断 dialog 是否已经放到对应的 div 下
154
+ rendered: false,
155
+ // 自管遮罩层的 z-index
156
+ modelZIndex: 1000,
157
+ // 自管遮罩层的高度(根据父容器动态计算)
158
+ modelHeight: 0,
159
+ // 当前 app-views 的 dom 元素
160
+ parentDom: null,
161
+ // 当前 el-dialog 的 dom 元素
162
+ elDialogDom: null
163
+ };
164
+ },
165
+ watch: {
166
+ // 监听 visible:控制 dialog 显示/隐藏、挂载位置、键盘事件、遮罩层级
167
+ visible: {
168
+ immediate: true,
169
+ handler(val) {
170
+ if (val) {
171
+ this.$emit('open');
172
+ Vue.nextTick(() => {
173
+ const dom = this.$el;
174
+ // 首次打开时处理挂载位置
175
+ if (!this.rendered) {
176
+ this.rendered = true;
177
+ if (this.appendToTagsView) {
178
+ // 将当前 dialog 挪到当前 app-views 里面的 app-container
179
+ this.parentDom = dom.closest('.app-container');
180
+ if (!this.parentDom) this.parentDom = document.body;
181
+ this.parentDom.append(dom);
182
+ } else if (this.appendToBody) {
183
+ // 将当前 dialog 挪到当前 body 里面
184
+ this.parentDom = document.body;
185
+ this.parentDom.append(dom);
186
+ }
187
+ // 添加键盘监听事件(用于 ESC 关闭)
188
+ if (this.closeOnPressEscape) {
189
+ document.addEventListener('keydown', this.escKeyDown);
190
+ }
191
+ }
192
+
193
+ setTimeout(() => {
194
+ if (this.parentDom) this.modelHeight = this.parentDom.scrollHeight + 'px';
195
+ // 当前 VDialog 不在 body 中时
196
+ if (!this.parentDom || this.parentDom.tagName.toUpperCase() !== 'BODY') {
197
+ // 获取 el-dialog 的宽度,如果大于 app-views 的宽度,则 el-dialog 不居中显示
198
+ let elDialogWidth = dom.getElementsByClassName('el-dialog')[0].clientWidth;
199
+ if (!this.parentDom || elDialogWidth < this.parentDom.clientWidth) {
200
+ dom.getElementsByClassName('el-dialog')[0].style.left = '50%';
201
+ dom.getElementsByClassName('el-dialog')[0].style.transform = 'translateX(-50%)';
202
+ } else {
203
+ dom.getElementsByClassName('el-dialog')[0].style.left = '0';
204
+ }
205
+ }
206
+ dom.getElementsByClassName('el-dialog')[0].style.marginTop = this.top;
207
+ }, 50);
208
+
209
+ // 获取 el-dialog 的 z-index,赋值给 model,用于处理 model 的层级问题
210
+ this.elDialogDom = dom.children[1];
211
+ setTimeout(() => {
212
+ this.modelZIndex = this.elDialogDom.style.zIndex;
213
+ }, 50);
214
+ });
215
+ } else {
216
+ // immediate=true 时,首次打开不需要触发 close 事件
217
+ if (this.rendered) this.$emit('close');
218
+ }
219
+ }
220
+ }
221
+ },
222
+ methods: {
223
+ /**
224
+ * 打开回调,透传 open 事件
225
+ * @fires {Event} open
226
+ * @returns {void}
227
+ */
228
+ handleOpen() {
229
+ this.$emit('open');
230
+ },
231
+
232
+ /**
233
+ * 打开动画结束回调,透传 opened 事件
234
+ * @fires {Event} opened
235
+ * @returns {void}
236
+ */
237
+ handleOpened() {
238
+ this.$emit('opened');
239
+ },
240
+
241
+ /**
242
+ * 关闭回调:同步 visible 并透传 close 事件
243
+ * @fires {Event} update:visible
244
+ * @fires {Event} close
245
+ * @returns {void}
246
+ */
247
+ handleClose() {
248
+ this.$emit('update:visible', false);
249
+ this.$emit('close');
250
+ },
251
+
252
+ /**
253
+ * 关闭动画结束回调,透传 closed 事件
254
+ * @fires {Event} closed
255
+ * @returns {void}
256
+ */
257
+ handleClosed() {
258
+ this.$emit('closed');
259
+ },
260
+
261
+ /**
262
+ * 监听 esc 键,按层级优先级关闭最上层 dialog
263
+ * @param {KeyboardEvent} event - 键盘事件对象
264
+ * @returns {void}
265
+ */
266
+ escKeyDown(event) {
267
+ if (event.keyCode === 27) {
268
+ // 获取 body 下的直接子节点中的已打开的 VDialog
269
+ let bodyVDialogDomArr = [];
270
+ let bodyChildrenArr = document.body.childNodes;
271
+ for (let i = 0; i < bodyChildrenArr.length; i++) {
272
+ let item = bodyChildrenArr[i];
273
+ if (item.tagName && item.tagName.toUpperCase() === 'DIV' && item.className && item.className.split(' ').includes('cy-tab-dialog-opened')) {
274
+ bodyVDialogDomArr.push(item);
275
+ }
276
+ }
277
+ // 判断 body 中是否有已打开的 VDialog,如果有,优先关闭 body 下的 VDialog
278
+ if (bodyVDialogDomArr.length > 0) {
279
+ // 获取当前 cy-tab-dialog 在当前 body 中的下标
280
+ let curDomIndex = [].indexOf.call(bodyVDialogDomArr, this.$el);
281
+ if (curDomIndex === bodyVDialogDomArr.length - 1) {
282
+ this.handleClose();
283
+ }
284
+ } else if (document.getElementById('tags-view-container') && this.parentDom.parentElement.getAttribute('include_name')) {
285
+ // 获取当前 app-views 对应的 include_name 值
286
+ let includeName = this.parentDom.parentElement.getAttribute('include_name');
287
+ // 根据 includeName 获取 tagsView 中的 tag 标签页
288
+ let tagDom = document.getElementById('tags-view-container').querySelectorAll(`[include_name=${ includeName }]`);
289
+ if (tagDom && tagDom[0]) {
290
+ // 判断此标签页是否为选中状态,只有选中状态中的 VDialog 才可以进行关闭
291
+ if (tagDom[0].className.split(' ').includes('active')) {
292
+ let vDialogList = this.parentDom.getElementsByClassName('cy-tab-dialog-opened');
293
+ // 获取当前 cy-tab-dialog 在当前 app-views 中的下标
294
+ let curDomIndex = [].indexOf.call(vDialogList, this.$el);
295
+ if (curDomIndex === vDialogList.length - 1) {
296
+ this.handleClose();
297
+ }
298
+ }
299
+ }
300
+ } else {
301
+ this.handleClose();
302
+ }
303
+ }
304
+ }
305
+ },
306
+
307
+ /**
308
+ * 组件销毁前移除键盘监听,避免内存泄漏
309
+ * @returns {void}
310
+ */
311
+ beforeDestroy() {
312
+ document.removeEventListener('keydown', this.escKeyDown);
313
+ }
314
+ };
315
+ </script>