sh-view 2.0.4 → 2.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh-view",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "基于vxe-table二次封装",
5
5
  "main": "packages/index.js",
6
6
  "scripts": {
@@ -0,0 +1,304 @@
1
+ <template>
2
+ <div class="sh-menu" :class="classes">
3
+ <div v-clickout="handleOut" class="sh-menu-inner">
4
+ <template v-for="(item, index) in options" :key="index">
5
+ <menuGroupContent :menu-item="item" />
6
+ </template>
7
+ </div>
8
+ </div>
9
+ </template>
10
+
11
+ <script>
12
+ import menuGroupContent from './menu-group-content.vue'
13
+ export default {
14
+ name: 'ShMenu',
15
+ components: {
16
+ menuGroupContent
17
+ },
18
+ provide() {
19
+ return {
20
+ MenuInstance: this
21
+ }
22
+ },
23
+ props: {
24
+ modelValue: {
25
+ type: String
26
+ },
27
+ mode: {
28
+ type: String,
29
+ default: 'vertical' // horizontal
30
+ },
31
+ indent: {
32
+ type: Number,
33
+ default: 20
34
+ },
35
+ accordion: {
36
+ type: Boolean,
37
+ default: true
38
+ },
39
+ theme: {
40
+ type: Boolean
41
+ },
42
+ fix: {
43
+ type: Boolean
44
+ },
45
+ collapsed: {
46
+ type: Boolean
47
+ },
48
+ options: {
49
+ type: Array,
50
+ default() {
51
+ return []
52
+ }
53
+ },
54
+ labelField: {
55
+ type: String,
56
+ default: 'label'
57
+ },
58
+ labelFormat: {
59
+ type: Function
60
+ }
61
+ },
62
+ emits: ['update:modelValue', 'change', 'expand'],
63
+ data() {
64
+ return {
65
+ activeName: this.modelValue,
66
+ activeNames: [],
67
+ openedNames: []
68
+ }
69
+ },
70
+ computed: {
71
+ classes() {
72
+ return {
73
+ theme: this.theme,
74
+ fix: this.fix,
75
+ collapsed: this.collapsed,
76
+ [`${this.mode}`]: this.mode
77
+ }
78
+ }
79
+ },
80
+ watch: {
81
+ modelValue(to, from) {
82
+ this.resetMenu()
83
+ }
84
+ },
85
+ mounted() {
86
+ this.resetMenu()
87
+ },
88
+ methods: {
89
+ // 重新渲染菜单
90
+ resetMenu() {
91
+ this.activeName = this.modelValue
92
+ this.resetExpand(this.activeName, true)
93
+ },
94
+ resetExpand(menuName, mount) {
95
+ let activeNames = []
96
+ let parentTree = this.$vUtils.searchTree(this.options, item => item.name === menuName)
97
+ this.$vUtils.eachTree(parentTree, item => {
98
+ activeNames.push(item.name)
99
+ })
100
+ if (mount) {
101
+ this.activeNames = activeNames
102
+ if (this.collapsed) return
103
+ }
104
+ // 过滤掉所有子节点
105
+ let menuIndex = activeNames.findIndex(item => item === menuName)
106
+ let parentNames = activeNames.filter((item, itemIndex) => menuIndex >= itemIndex)
107
+ if (!this.accordion) parentNames = Array.from(new Set(this.openedNames.concat(parentNames)))
108
+ this.openedNames = parentNames
109
+ },
110
+ onMenuSelect(menu) {
111
+ this.handleOut()
112
+ this.$emit('change', menu)
113
+ },
114
+ onMenuExpand(expand, menu) {
115
+ this.$emit('expand', expand, menu)
116
+ if (!expand) {
117
+ this.openedNames = this.openedNames.filter(name => name !== menu.name)
118
+ return
119
+ }
120
+ this.resetExpand(menu.name)
121
+ },
122
+ handleOut() {
123
+ if (!this.collapsed && this.mode === 'vertical') return
124
+ this.openedNames = []
125
+ }
126
+ }
127
+ }
128
+ </script>
129
+
130
+ <style lang="scss">
131
+ $textColor: var(--vxe-font-color);
132
+ $textActive: var(--vxe-primary-color);
133
+ $mainBgColor: var(--vxe-table-body-background-color);
134
+ $expandBgColor: var(--vxe-table-header-background-color);
135
+
136
+ $textDkColor: hsla(0, 0%, 100%, 0.6);
137
+ $textDkActive: #fff;
138
+ $mainDkBgColor: var(--vxe-primary-color);
139
+ $expandDkBgColor: var(--vxe-primary-darken-color);
140
+ .sh-menu {
141
+ position: relative;
142
+ background-color: $mainBgColor;
143
+ &.theme {
144
+ background-color: $mainDkBgColor;
145
+ .sh-menu-group {
146
+ &.is__expand {
147
+ background: $expandDkBgColor;
148
+ & > .sh-menu-item {
149
+ color: $textDkActive;
150
+ }
151
+ }
152
+ &.is__active {
153
+ & > .sh-menu-item {
154
+ color: $textDkActive;
155
+ }
156
+ }
157
+ & > .sh-menu-group-dropdown,
158
+ & > .sh-menu-group-dropright {
159
+ background-color: $mainDkBgColor;
160
+ }
161
+ }
162
+ .sh-menu-item {
163
+ color: $textDkColor;
164
+ &:hover {
165
+ color: $textDkActive;
166
+ background: var(--vxe-primary-lighten-color) !important;
167
+ }
168
+ &.sh-menu-item-active {
169
+ color: $textDkActive;
170
+ background: var(--vxe-primary-lighten-color);
171
+ &:after {
172
+ background: $textDkColor;
173
+ }
174
+ }
175
+ }
176
+ }
177
+ &.fix {
178
+ position: fixed;
179
+ top: 0;
180
+ left: 0;
181
+ right: 0;
182
+ }
183
+ &.horizontal {
184
+ .sh-menu-inner {
185
+ display: flex;
186
+ align-items: center;
187
+ height: 100%;
188
+ }
189
+ .sh-menu-item {
190
+ height: 100%;
191
+ &.sh-menu-item-active {
192
+ &:after {
193
+ width: 100%;
194
+ height: 3px;
195
+ top: auto;
196
+ bottom: 0;
197
+ }
198
+ }
199
+ &.sh-menu-item-level {
200
+ .sh-menu-item-arrow {
201
+ transform: rotate(90deg);
202
+ transform-origin: center;
203
+ }
204
+ }
205
+ }
206
+ .sh-menu-group {
207
+ height: 100%;
208
+ &.is__expand {
209
+ & > .sh-menu-item-level {
210
+ .sh-menu-item-arrow {
211
+ transform: rotate(-90deg);
212
+ transform-origin: center;
213
+ }
214
+ }
215
+ }
216
+ }
217
+ }
218
+ &.collapsed {
219
+ .sh-menu-item.sh-menu-item-level {
220
+ flex-wrap: wrap;
221
+ text-align: center;
222
+ padding: 5px;
223
+ &.sh-menu-item-active {
224
+ &:after {
225
+ display: none;
226
+ }
227
+ }
228
+ .sh-menu-item-icon {
229
+ margin-right: 0;
230
+ margin-bottom: 3px;
231
+ flex: 1;
232
+ }
233
+ .sh-menu-item-title {
234
+ min-width: 100%;
235
+ }
236
+ }
237
+ }
238
+ .sh-menu-group {
239
+ position: relative;
240
+ &.is__expand {
241
+ background: $expandBgColor;
242
+ & > .sh-menu-item {
243
+ color: $textActive;
244
+ .sh-menu-item-arrow {
245
+ transform: rotate(90deg);
246
+ transform-origin: center;
247
+ }
248
+ }
249
+ }
250
+ &.is__active {
251
+ & > .sh-menu-item {
252
+ color: $textActive;
253
+ }
254
+ }
255
+ & > .sh-menu-group-dropdown,
256
+ & > .sh-menu-group-dropright {
257
+ position: fixed;
258
+ line-height: 1.5;
259
+ background-color: $mainBgColor;
260
+ box-shadow: var(--box-shadow);
261
+ overflow: auto;
262
+ z-index: 1000;
263
+ }
264
+ }
265
+ .sh-menu-item {
266
+ padding: 10px 12px;
267
+ display: flex;
268
+ align-items: center;
269
+ justify-content: flex-start;
270
+ cursor: pointer;
271
+ position: relative;
272
+ user-select: none;
273
+ color: $textColor;
274
+ text-align: left;
275
+ &:hover {
276
+ color: $textActive;
277
+ }
278
+ &.sh-menu-item-active {
279
+ color: $textActive;
280
+ background: var(--primary-weak-color);
281
+ &:after {
282
+ width: 3px;
283
+ content: '';
284
+ position: absolute;
285
+ top: 0;
286
+ right: 0;
287
+ height: 100%;
288
+ background: var(--vxe-primary-color);
289
+ }
290
+ }
291
+ .sh-menu-item-icon {
292
+ margin-right: 8px;
293
+ }
294
+ .sh-menu-item-title {
295
+ flex: 1;
296
+ white-space: nowrap;
297
+ }
298
+ .sh-menu-item-arrow {
299
+ margin-left: 5px;
300
+ transition: 0.1s all ease-in-out;
301
+ }
302
+ }
303
+ }
304
+ </style>
@@ -0,0 +1,122 @@
1
+ <template>
2
+ <div v-if="showChildren(menuItem)" class="sh-menu-group" :class="groupClass">
3
+ <menuItemContent ref="groupItem" :menu-item="menuItem" :menu-level="menuLevel" :arrow="isArrow" @click="onMenuExpand(!expand)" />
4
+ <div v-show="expand" ref="groupList" class="sh-menu-group-list" :class="groupListClass" :style="groupListStyle">
5
+ <template v-for="(item, index) in menuItem.children" :key="`sh-menu-${menuLevel}-${index}`">
6
+ <menuGroupContent :menu-item="item" :menu-level="menuNextLevel" />
7
+ </template>
8
+ </div>
9
+ </div>
10
+ <template v-else>
11
+ <menuItemContent :menu-item="menuItem" :menu-level="menuLevel" @click.stop="onMenuClick" />
12
+ </template>
13
+ </template>
14
+
15
+ <script>
16
+ import menuItemContent from './menu-item-content.vue'
17
+ export default {
18
+ name: 'MenuGroupContent',
19
+ components: {
20
+ menuItemContent
21
+ },
22
+ inject: {
23
+ MenuInstance: {
24
+ default: null
25
+ }
26
+ },
27
+ props: {
28
+ menuLevel: {
29
+ type: Number,
30
+ default: 0
31
+ },
32
+ menuItem: {
33
+ type: Object,
34
+ default() {
35
+ return {}
36
+ }
37
+ }
38
+ },
39
+ data() {
40
+ return {
41
+ expand: false,
42
+ groupListStyle: {}
43
+ }
44
+ },
45
+ computed: {
46
+ menuNextLevel() {
47
+ return this.menuLevel + 1
48
+ },
49
+ isArrow() {
50
+ return this.MenuInstance.collapsed ? !!this.menuLevel : true
51
+ },
52
+ groupClass() {
53
+ return {
54
+ is__level: !this.menuLevel,
55
+ is__active: this.MenuInstance.activeNames.includes(this.menuItem.name),
56
+ is__expand: this.expand
57
+ }
58
+ },
59
+ groupListClass() {
60
+ const { mode, collapsed } = this.MenuInstance
61
+ return {
62
+ 'sh-menu-group-dropdown': mode === 'horizontal',
63
+ 'sh-menu-group-dropright': mode === 'vertical' && collapsed
64
+ }
65
+ }
66
+ },
67
+ watch: {
68
+ 'MenuInstance.openedNames'(names) {
69
+ this.setMenuExpand()
70
+ }
71
+ },
72
+ mounted() {
73
+ this.setMenuExpand()
74
+ },
75
+ methods: {
76
+ setMenuExpand() {
77
+ let names = this.MenuInstance.openedNames
78
+ this.expand = names.includes(this.menuItem.name)
79
+ const { mode, collapsed } = this.MenuInstance
80
+ if (this.expand && this.$refs.groupItem && ((mode === 'vertical' && collapsed) || mode === 'horizontal')) {
81
+ this.$nextTick(() => {
82
+ const { left, right, top, bottom } = this.$refs.groupItem.$refs?.shMenuItem.getBoundingClientRect()
83
+ const { offsetHeight, offsetWidth } = this.$refs.groupList
84
+ const winWidth = window.innerWidth
85
+ const winHeight = window.innerHeight
86
+ if (mode === 'vertical') {
87
+ let groupStyle = { left: `${right}px` }
88
+ if (offsetHeight >= winHeight) {
89
+ groupStyle.top = '10px'
90
+ groupStyle.bottom = '10px'
91
+ } else if (top + offsetHeight >= winHeight) {
92
+ groupStyle.bottom = '10px'
93
+ } else {
94
+ groupStyle.top = `${top}px`
95
+ }
96
+ this.groupListStyle = groupStyle
97
+ } else {
98
+ let groupStyle = { top: `${this.menuLevel ? top : bottom}px` }
99
+ if (right + offsetWidth >= winWidth) {
100
+ groupStyle.right = '10px'
101
+ } else {
102
+ groupStyle.left = `${this.menuLevel ? right : left}px`
103
+ }
104
+ this.groupListStyle = groupStyle
105
+ }
106
+ })
107
+ }
108
+ },
109
+ showChildren(item) {
110
+ return item.children && item.children.length > 0
111
+ },
112
+ onMenuClick() {
113
+ this.MenuInstance.onMenuSelect(this.menuItem)
114
+ },
115
+ onMenuExpand(value) {
116
+ this.MenuInstance.onMenuExpand(value, this.menuItem)
117
+ }
118
+ }
119
+ }
120
+ </script>
121
+
122
+ <style scoped></style>
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <div ref="shMenuItem" class="sh-menu-item" :class="itemClass" :style="itemStyle">
3
+ <sh-icon v-if="iconType" class="sh-menu-item-icon" :type="iconType"></sh-icon>
4
+ <span class="sh-menu-item-title">{{ formatTitle(menuItem) }}</span>
5
+ <sh-icon v-if="arrow" class="sh-menu-item-arrow" type="ios-arrow-forward"></sh-icon>
6
+ </div>
7
+ </template>
8
+
9
+ <script>
10
+ export default {
11
+ name: 'MenuItemBox',
12
+ inject: {
13
+ MenuInstance: {
14
+ default: null
15
+ }
16
+ },
17
+ props: {
18
+ arrow: Boolean,
19
+ menuLevel: {
20
+ type: Number
21
+ },
22
+ menuItem: {
23
+ type: Object,
24
+ default() {
25
+ return {}
26
+ }
27
+ }
28
+ },
29
+ computed: {
30
+ defaultIcon() {
31
+ if (!this.menuLevel || this.menuLevel < 2) return 'md-folder'
32
+ else if (this.menuLevel === 2) return 'md-bookmark'
33
+ else return ''
34
+ },
35
+ iconType() {
36
+ return this.menuItem.meta?.icon || this.menuItem.icon || this.defaultIcon
37
+ },
38
+ itemClass() {
39
+ return {
40
+ 'sh-menu-item-level': !this.menuLevel,
41
+ 'sh-menu-item-active': this.MenuInstance.activeName === this.menuItem.name
42
+ }
43
+ },
44
+ itemStyle() {
45
+ let result = {}
46
+ const { mode, collapsed, indent } = this.MenuInstance
47
+ if (mode === 'vertical' && !collapsed && this.menuLevel) {
48
+ result.paddingLeft = `${indent * this.menuLevel}px`
49
+ }
50
+ return result
51
+ },
52
+ itemTitle() {
53
+ const { labelField, labelFormat } = this.MenuInstance
54
+ return labelFormat ? labelFormat(this.menuItem) : this.$vUtils.get(this.menuItem, labelField)
55
+ }
56
+ },
57
+ methods: {}
58
+ }
59
+ </script>
60
+
61
+ <style scoped></style>