gyyg-components 0.1.6

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.
@@ -0,0 +1,327 @@
1
+ /** * 公共模态框组件 author:lijihao Date:2024.11 支持关闭时,el-form表单清空 **/
2
+ <template>
3
+ <div class="gyyg-modal">
4
+ <el-dialog
5
+ :class="dialogClass"
6
+ v-el-drag-dialog
7
+ :type="type"
8
+ :width="width"
9
+ :custom-class="customClass"
10
+ :fullscreen="fullscreen"
11
+ :title="title"
12
+ :close-on-click-modal="closeOnClickModal"
13
+ :append-to-body="appendToBody"
14
+ :visible.sync="visible"
15
+ :before-close="beforeClose"
16
+ :show-close="showClose"
17
+ v-if="visible"
18
+ @closed="closed"
19
+ @close="closeMethod"
20
+ :destroy-on-close="destroyOnClose"
21
+ :modal="modal"
22
+ >
23
+ <div>
24
+ <slot name="body" class="gyyg-modal-body" />
25
+ </div>
26
+
27
+ <div v-if="footer" slot="footer" class="dialog-footer">
28
+ <slot name="footer" />
29
+ </div>
30
+ </el-dialog>
31
+ </div>
32
+ </template>
33
+
34
+ <script>
35
+ import elDragDialog from '@/directive/el-drag-dialog' // 引入拖拽指令
36
+ export default {
37
+ name: 'GyygModal',
38
+ directives: { elDragDialog },
39
+ props: {
40
+ // 是否嵌套
41
+ appendToBody: {
42
+ type: Boolean,
43
+ default: false,
44
+ },
45
+ // 是否可以点击关闭
46
+ closeOnClickModal: {
47
+ type: Boolean,
48
+ default: false,
49
+ },
50
+ // 是否显示底部
51
+ footer: {
52
+ type: Boolean,
53
+ default: true,
54
+ },
55
+ // title
56
+ title: {
57
+ type: String,
58
+ default: '',
59
+ },
60
+ type: {
61
+ type: [String,Number],
62
+ default: "",
63
+ }, // 对话框类型:1.基础表单[gyyg-modal-form] 2.表格[gyyg-modal-table] 3.全屏 [gyyg-modal-fullscreen]
64
+ // 对话框宽度(默认50%)
65
+ width: {
66
+ type: [String,Number],
67
+ default: '',
68
+ },
69
+ //对话框高度(默认90%)
70
+ height: {
71
+ type: [String,Number],
72
+ default: '',
73
+ },
74
+ //关闭后销毁
75
+ destroyOnClose: {
76
+ type: Boolean,
77
+ default: true,
78
+ },
79
+ // 关闭回调函数
80
+ beforeClose: Function,
81
+ //弹框class
82
+ dialogClass: {
83
+ type: String,
84
+ default: '',
85
+ },
86
+ // 是否显示叉号
87
+ showClose: {
88
+ default: true,
89
+ type: Boolean,
90
+ },
91
+ //是否需要遮罩层
92
+ modal: {
93
+ default: true,
94
+ type: Boolean,
95
+ },
96
+ },
97
+ data() {
98
+ return {
99
+ visible: false,
100
+ fullscreen: false,
101
+ }
102
+ },
103
+ computed: {
104
+ customClass() {
105
+ let className = "";
106
+ switch (this.type) {
107
+ case "form":
108
+ className = "gyyg-modal-form";
109
+ break;
110
+ case "table":
111
+ className = "gyyg-modal-table";
112
+ break;
113
+ case "fullscreen":
114
+ className = "gyyg-modal-fullscreen";
115
+ break;
116
+ }
117
+ return className;
118
+ },
119
+ },
120
+ created() {
121
+ this.type === "fullscreen" && (this.fullscreen = true);
122
+ },
123
+ methods: {
124
+ //弹框打开
125
+ open() {
126
+ this.visible = true
127
+ if (this.height) {
128
+ this.$nextTick(() => {
129
+ document.getElementsByClassName('gyyg-modal')[0].getElementsByClassName('el-dialog')[0].style.height = this.height
130
+ })
131
+ }
132
+ },
133
+ close() {
134
+ let db = document.getElementsByClassName('el-dialog__body')
135
+ //表单清空显示
136
+ if (db && db[0] && db[0].getElementsByClassName('el-form') && db[0].getElementsByClassName('el-form').length > 0) {
137
+ let arr = Array.prototype.slice.call(db[0].getElementsByClassName('el-form'))
138
+ arr.forEach(item => {
139
+ if (item.__vue__) {
140
+ let obj = item.__vue__.$options.propsData.model
141
+ for (let key in obj) {
142
+ if (obj.hasOwnProperty(key)) {
143
+ if (obj[key] instanceof Array) {
144
+ obj[key] = []
145
+ } else if (obj[key] instanceof Object) {
146
+ obj[key] = {}
147
+ } else {
148
+ obj[key] = ''
149
+ }
150
+ }
151
+ }
152
+ }
153
+ })
154
+ }
155
+ //组件清空显示(组件里必须要加class='subcomponents',才能找到,清空)
156
+ if (db && db[0] && db[0].getElementsByClassName('subcomponents') && db[0].getElementsByClassName('subcomponents').length > 0) {
157
+ let arr = Array.prototype.slice.call(db[0].getElementsByClassName('subcomponents'))
158
+ arr.forEach(item => {
159
+ if (item.__vue__) {
160
+ //组件清空数据的方法
161
+ if (item.__vue__.clearHandle) {
162
+ item.__vue__.clearHandle()
163
+ } else if (item.__vue__.clearInput) {
164
+ item.__vue__.clearInput()
165
+ }
166
+ }
167
+ })
168
+ }
169
+ this.visible = false
170
+ },
171
+ closed() {
172
+ this.close()
173
+ this.$emit('closed')
174
+ },
175
+ //弹框关闭
176
+ closeMethod() {
177
+ this.close()
178
+ this.$emit('close')
179
+ },
180
+ },
181
+ }
182
+ </script>
183
+
184
+ <style lang="less">
185
+ .el-dialog__wrapper {
186
+ overflow: hidden;
187
+ display: flex;
188
+ justify-content: center;
189
+ align-items: center;
190
+ .el-dialog {
191
+ display: flex;
192
+ flex-direction: column;
193
+ border-radius: 8px;
194
+ margin: 0;
195
+ margin-top: 0 !important;
196
+ max-height: 95%;
197
+ .el-dialog__header {
198
+ text-align: left;
199
+ background-color: #efefef;
200
+ height: 48px;
201
+ line-height: 48px;
202
+ padding: 0 20px;
203
+ color: #fff;
204
+ border-top-left-radius: 8px;
205
+ border-top-right-radius: 8px;
206
+ font-weight: bold;
207
+
208
+ .el-dialog__title {
209
+ font-size: 16px;
210
+ }
211
+
212
+ .el-dialog__headerbtn {
213
+ top: 12px;
214
+ }
215
+ }
216
+
217
+ .el-dialog__body {
218
+ flex: 1;
219
+ overflow: auto;
220
+ }
221
+
222
+ .el-dialog__footer {
223
+ padding: 10px;
224
+ border: none;
225
+ background-color: #efefef;
226
+ border-bottom-left-radius: 8px;
227
+ border-bottom-right-radius: 8px;
228
+ }
229
+ }
230
+
231
+ .gyyg-modal-form {
232
+ height: auto;
233
+
234
+ .el-dialog__body {
235
+ padding: 20px 20px 0 20px;
236
+ }
237
+
238
+ .el-dialog__footer {
239
+ border: none;
240
+ background-color: #efefef;
241
+ border-bottom-left-radius: 8px;
242
+ border-bottom-right-radius: 8px;
243
+ }
244
+
245
+ .custom-table {
246
+ // 取消表格下边线
247
+ tbody tr:last-child td {
248
+ border-bottom: none !important;
249
+ }
250
+ }
251
+ }
252
+
253
+ .gyyg-modal-table {
254
+ height: 90vh;
255
+ margin-top: 5vh !important;
256
+ }
257
+
258
+ .gyyg-modal-fullscreen {
259
+ height: 100vh;
260
+ width: 100vw;
261
+
262
+ .el-dialog__body {
263
+ padding: 10px;
264
+ }
265
+ .el-dialog__footer {
266
+ padding: 10px;
267
+ border: none;
268
+ background-color: #efefef;
269
+ border-bottom-left-radius: 8px;
270
+ border-bottom-right-radius: 8px;
271
+ }
272
+ }
273
+ }
274
+
275
+ .gyyg-modal {
276
+ text-align: left;
277
+ }
278
+ ::v-deep .gyyg-modal .el-dialog__wrapper {
279
+ overflow: hidden;
280
+ display: flex !important;
281
+ justify-content: center;
282
+ align-items: center;
283
+ .el-dialog {
284
+ display: flex;
285
+ flex-direction: column;
286
+ border-radius: 8px;
287
+ margin: 0;
288
+ margin-top: 0 !important;
289
+ .el-dialog__header {
290
+ text-align: left;
291
+ background-color: #efefef;
292
+ height: 48px;
293
+ line-height: 48px;
294
+ padding: 0 20px;
295
+ color: #fff;
296
+ border-top-left-radius: 8px;
297
+ border-top-right-radius: 8px;
298
+ font-weight: bold;
299
+
300
+ .el-dialog__title {
301
+ font-size: 16px;
302
+ // font-weight: bold;
303
+ }
304
+
305
+ .el-dialog__headerbtn {
306
+ top: 12px;
307
+ }
308
+ }
309
+
310
+ .el-dialog__body {
311
+ flex: 1;
312
+ overflow: auto;
313
+ }
314
+
315
+ .el-dialog__footer {
316
+ flex-shrink: 0;
317
+ box-sizing: content-box;
318
+ height: 32px;
319
+ padding: 10px;
320
+ border: none;
321
+ background-color: #efefef;
322
+ border-bottom-left-radius: 8px;
323
+ border-bottom-right-radius: 8px;
324
+ }
325
+ }
326
+ }
327
+ </style>
@@ -0,0 +1,5 @@
1
+ import GyygTable from "./GyygTable.vue";
2
+
3
+ GyygTable.install = Vue => Vue.component(GyygTable.name, GyygTable); //注册组件
4
+
5
+ export default GyygTable;
@@ -0,0 +1,78 @@
1
+ /** el-table组件 */
2
+ <template>
3
+ <el-table class='simpleTableClass' ref="simpleTableRef" :data="data" :row-key="rowKey" :row-style="selectedRowStyle" highlight-current-row element-loading-text="加载中" @selection-change="handleSelectionChange" :height='height' @row-click="clickOne" border style="width: 100%">
4
+ <slot name="content" />
5
+ </el-table>
6
+ </template>
7
+
8
+ <script>
9
+ export default {
10
+ name:'GyygTable',
11
+ data() {
12
+ return {
13
+ simpleMultipleSelection:this.multipleSelection
14
+ }
15
+ },
16
+ props: {
17
+ //列表数据
18
+ data: {
19
+ type: Array,
20
+ default: () =>{
21
+ return []
22
+ },
23
+ },
24
+ //行的唯一标识
25
+ rowKey: {
26
+ type: [String,Number],
27
+ default: 'id',
28
+ },
29
+ //选择的行
30
+ multipleSelection: {
31
+ type: Array,
32
+ default: () =>{
33
+ return []
34
+ },
35
+ },
36
+ // 高度
37
+ height: {
38
+ type: [String,Number],
39
+ default: '100%',
40
+ },
41
+ // 点击单行,是否只选中当前行
42
+ toggleRow: {
43
+ type: Boolean,
44
+ default: true,
45
+ }
46
+ },
47
+ methods: {
48
+ // 列表选中加粗
49
+ selectedRowStyle({ row }) {
50
+ const idArr = this.simpleMultipleSelection.map(row => row[this.rowKey])
51
+ if (idArr.includes(row[this.rowKey])) {
52
+ return { 'font-weight': '700' }
53
+ }
54
+ },
55
+ // 列表选中的当前行
56
+ handleSelectionChange(row) {
57
+ this.simpleMultipleSelection = row
58
+ this.$emit('update:multipleSelection',this.simpleMultipleSelection)
59
+ },
60
+ // 选中单条数据
61
+ clickOne(row) {
62
+ if(this.toggleRow){
63
+ this.$refs.simpleTableRef.clearSelection()
64
+ this.$refs.simpleTableRef.toggleRowSelection(row)
65
+ }
66
+ },
67
+ },
68
+ }
69
+ </script>
70
+
71
+ <style>
72
+ .simpleTableClass thead tr th {
73
+ background-color:#F5F7FA !important;
74
+ }
75
+ .simpleTableClass td.el-table__cell{
76
+ border-right:none;
77
+ }
78
+ </style>
@@ -0,0 +1,64 @@
1
+ export default {
2
+ bind(el, binding, vnode) {
3
+ const dialogHeaderEl = el.querySelector('.el-dialog__header')
4
+ const dragDom = el.querySelector('.el-dialog')
5
+ dialogHeaderEl.style.cssText += ';cursor:move;'
6
+
7
+ // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
8
+ const getStyle = (function() {
9
+ if (window.document.currentStyle) {
10
+ return (dom, attr) => dom.currentStyle[attr]
11
+ } else {
12
+ return (dom, attr) => getComputedStyle(dom, false)[attr]
13
+ }
14
+ })()
15
+
16
+ dialogHeaderEl.onmousedown = (e) => {
17
+ // 鼠标按下,计算当前元素距离可视区的距离
18
+ const disX = e.clientX - dialogHeaderEl.offsetLeft
19
+ const disY = e.clientY - dialogHeaderEl.offsetTop
20
+
21
+ // 获取到的值带px 正则匹配替换
22
+ let styL = getStyle(dragDom, 'left')
23
+ let styT = getStyle(dragDom, 'top')
24
+
25
+ if (styL.includes('%')) {
26
+ styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
27
+ styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
28
+ } else {
29
+ styL = +styL.replace(/\px/g, '')
30
+ styT = +styT.replace(/\px/g, '')
31
+ }
32
+
33
+ document.onmousemove = function(e) {
34
+ // 通过事件委托,计算移动的距离
35
+ let left = e.clientX - disX
36
+ let top = e.clientY - disY
37
+
38
+ // 边界处理
39
+ // if (-(left) > minDragDomLeft) {
40
+ // left = -minDragDomLeft
41
+ // } else if (left > maxDragDomLeft) {
42
+ // left = maxDragDomLeft
43
+ // }
44
+
45
+ // if (-(top) > minDragDomTop) {
46
+ // top = -minDragDomTop
47
+ // } else if (top > maxDragDomTop) {
48
+ // top = maxDragDomTop
49
+ // }
50
+
51
+ // 移动当前元素
52
+ dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
53
+
54
+ // emit onDrag event
55
+ vnode.child.$emit('dragDialog')
56
+ }
57
+
58
+ document.onmouseup = function(e) {
59
+ document.onmousemove = null
60
+ document.onmouseup = null
61
+ }
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,13 @@
1
+ import drag from "./drag";
2
+
3
+ const install = function (Vue) {
4
+ Vue.directive("el-drag-dialog", drag);
5
+ };
6
+
7
+ if (window.Vue) {
8
+ window["el-drag-dialog"] = drag;
9
+ Vue.use(install); // eslint-disable-line
10
+ }
11
+
12
+ drag.install = install;
13
+ export default drag;
@@ -0,0 +1,48 @@
1
+ // input金额指令(数字,且保留两位小数,blur自动增加.00)
2
+ import Vue from 'vue'
3
+
4
+ Vue.directive('feeInput', {
5
+ bind(el, binding, vnode) {
6
+ const input = el.getElementsByTagName('input')[0]
7
+ input.addEventListener('compositionstart', () => {
8
+ vnode.inputLocking = true
9
+ })
10
+ input.addEventListener('compositionend', () => {
11
+ vnode.inputLocking = false
12
+ input.dispatchEvent(new Event('input'))
13
+ })
14
+ input.addEventListener('input', () => {
15
+ if (vnode.inputLocking) {
16
+ return
17
+ }
18
+ const oldValue = input.value
19
+ let newValue = input.value
20
+ newValue = newValue.replace(/[^\d.]/g, '')
21
+ newValue = newValue.replace(/^\./g, '')
22
+ newValue = newValue.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.')
23
+ newValue = newValue.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3')
24
+ if (newValue) {
25
+ const arr = newValue.split('.')
26
+ newValue = Number(arr[0]) + (arr[1] === undefined ? '' : '.' + arr[1]) // 去掉开头多余的0
27
+ }
28
+ // 判断是否需要更新,避免进入死循环
29
+ if (newValue !== oldValue) {
30
+ input.value = newValue
31
+ input.dispatchEvent(new Event('input')) // 通知v-model更新
32
+ }
33
+ })
34
+ // input 事件无法处理小数点后面全是零的情况 因为无法确定用户输入的0是否真的应该清除,如3.02。放在blur中去处理
35
+ input.addEventListener('blur', () => {
36
+ const oldValue = input.value
37
+ let newValue = input.value
38
+ if (newValue) {
39
+ newValue = Number(newValue).toFixed(2)
40
+ }
41
+ // 判断是否需要更新,避免进入死循环
42
+ if (newValue !== oldValue) {
43
+ input.value = newValue
44
+ input.dispatchEvent(new Event('input')) // 通知v-model更新
45
+ }
46
+ })
47
+ }
48
+ })
@@ -0,0 +1,32 @@
1
+ // 数量指令(数字)
2
+ import Vue from 'vue'
3
+
4
+ Vue.directive('numberInput', {
5
+ bind(el, binding, vnode) {
6
+ const input = el.getElementsByTagName('input')[0]
7
+ input.addEventListener('compositionstart', () => {
8
+ vnode.inputLocking = true
9
+ })
10
+ input.addEventListener('compositionend', () => {
11
+ vnode.inputLocking = false
12
+ input.dispatchEvent(new Event('input'))
13
+ })
14
+ input.addEventListener('input', () => {
15
+ if (vnode.inputLocking) {
16
+ return
17
+ }
18
+ const oldValue = input.value
19
+ let newValue = input.value
20
+ newValue = newValue.replace(/[^\d]/g,'').replace(/^00*(\d)([0-9]*)/g,'$1$2')
21
+ if (newValue) {
22
+ const arr = newValue.split('.')
23
+ newValue = Number(arr[0]) + (arr[1] === undefined ? '' : '.' + arr[1]) // 去掉开头多余的0
24
+ }
25
+ // 判断是否需要更新,避免进入死循环
26
+ if (newValue !== oldValue) {
27
+ input.value = newValue
28
+ input.dispatchEvent(new Event('input')) // 通知v-model更新
29
+ }
30
+ })
31
+ }
32
+ })
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
3
+ <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
4
+ <use :xlink:href="iconName" />
5
+ </svg>
6
+ </template>
7
+
8
+ <script>
9
+ import { isExternal } from '@/utils/common.js'
10
+
11
+ export default {
12
+ name: 'SvgIcon',
13
+ props: {
14
+ iconClass: {
15
+ type: String,
16
+ required: true
17
+ },
18
+ className: {
19
+ type: String,
20
+ default: ''
21
+ }
22
+ },
23
+ computed: {
24
+ isExternal() {
25
+ return isExternal(this.iconClass)
26
+ },
27
+ iconName() {
28
+ return `#icon-${this.iconClass}`
29
+ },
30
+ svgClass() {
31
+ if (this.className) {
32
+ return 'svg-icon ' + this.className
33
+ } else {
34
+ return 'svg-icon'
35
+ }
36
+ },
37
+ styleExternalIcon() {
38
+ return {
39
+ mask: `url(${this.iconClass}) no-repeat 50% 50%`,
40
+ '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
41
+ }
42
+ }
43
+ }
44
+ }
45
+ </script>
46
+
47
+ <style scoped>
48
+ .svg-icon {
49
+ width: 1em;
50
+ height: 1em;
51
+ vertical-align: -0.15em;
52
+ fill: currentColor;
53
+ overflow: hidden;
54
+ }
55
+
56
+ .svg-external-icon {
57
+ background-color: currentColor;
58
+ mask-size: cover!important;
59
+ display: inline-block;
60
+ }
61
+ </style>
@@ -0,0 +1,9 @@
1
+ import Vue from 'vue'
2
+ import SvgIcon from './SvgIcon.vue'// svg component
3
+
4
+ // register globally
5
+ Vue.component('svg-icon', SvgIcon)
6
+
7
+ const req = require.context('./svg', false, /\.svg$/)
8
+ const requireAll = requireContext => requireContext.keys().map(requireContext)
9
+ requireAll(req)
@@ -0,0 +1 @@
1
+ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1678944914954" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3728" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M726.62 470.62L545.94 651.31c-18.72 18.72-49.16 18.72-67.88 0L297.38 470.62c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L512 594.75l169.38-169.38c12.5-12.5 32.75-12.5 45.25 0 6.25 6.25 9.38 14.44 9.38 22.62s-3.13 16.39-9.39 22.63zM523.31 606.38v-0.31 0.31z" fill="#409EFF" p-id="3729"></path><path d="M128 512c0-211.74 172.26-384 384-384s384 172.26 384 384-172.26 384-384 384-384-172.26-384-384m-64 0c0 247.42 200.58 448 448 448s448-200.58 448-448S759.42 64 512 64 64 264.58 64 512z" fill="#409EFF" p-id="3730"></path></svg>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1678945148529" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4763" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M297.38 553.38l180.69-180.69c18.72-18.72 49.16-18.72 67.88 0l180.69 180.69c12.5 12.5 12.5 32.75 0 45.25s-32.75 12.5-45.25 0L512 429.25 342.62 598.62c-12.5 12.5-32.75 12.5-45.25 0-6.25-6.25-9.38-14.44-9.38-22.62s3.13-16.38 9.39-22.62z m203.31-135.76v0.31-0.31z" fill="#409EFF" p-id="4764"></path><path d="M896 512c0 211.74-172.26 384-384 384S128 723.74 128 512s172.26-384 384-384 384 172.26 384 384m64 0c0-247.42-200.58-448-448-448S64 264.58 64 512s200.58 448 448 448 448-200.58 448-448z" fill="#409EFF" p-id="4765"></path></svg>
@@ -0,0 +1,22 @@
1
+ # replace default config
2
+
3
+ # multipass: true
4
+ # full: true
5
+
6
+ plugins:
7
+
8
+ # - name
9
+ #
10
+ # or:
11
+ # - name: false
12
+ # - name: true
13
+ #
14
+ # or:
15
+ # - name:
16
+ # param1: 1
17
+ # param2: 2
18
+
19
+ - removeAttrs:
20
+ attrs:
21
+ - 'fill'
22
+ - 'fill-rule'