@lambo-design/pro-layout 1.0.0-beta.43 → 1.0.0-beta.431

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.
Files changed (39) hide show
  1. package/package.json +11 -4
  2. package/src/components/pro-layout-header/index.vue +218 -0
  3. package/src/components/pro-layout-header/pro-layout-logo/index.vue +206 -0
  4. package/src/components/pro-layout-header/pro-layout-nav/components/pro-layout-nav-slide-menu.vue +360 -0
  5. package/src/components/pro-layout-header/pro-layout-nav/index-slide.vue +225 -0
  6. package/src/components/pro-layout-header/pro-layout-nav/index.vue +526 -0
  7. package/src/components/pro-layout-header/pro-layout-slogan/index.vue +40 -0
  8. package/src/components/pro-layout-header/pro-layout-tools/components/pro-layout-tools-quick-collect.vue +79 -0
  9. package/src/components/pro-layout-header/pro-layout-tools/components/pro-layout-tools-quick-document.vue +20 -0
  10. package/src/components/pro-layout-header/pro-layout-tools/components/pro-layout-tools-quick-fullscreen.vue +144 -0
  11. package/src/components/pro-layout-header/pro-layout-tools/components/pro-layout-tools-quick-icons.vue +99 -0
  12. package/src/components/pro-layout-header/pro-layout-tools/components/pro-layout-tools-quick-intl.vue +91 -0
  13. package/src/components/pro-layout-header/pro-layout-tools/components/pro-layout-tools-quick-notice.vue +133 -0
  14. package/src/components/pro-layout-header/pro-layout-tools/components/pro-layout-tools-quick-search.vue +305 -0
  15. package/src/components/pro-layout-header/pro-layout-tools/components/pro-layout-tools-quick-todo.vue +145 -0
  16. package/src/components/pro-layout-header/pro-layout-tools/components/pro-layout-tools-user.vue +64 -0
  17. package/src/components/pro-layout-header/pro-layout-tools/index.vue +38 -0
  18. package/src/components/pro-layout-header/pro-layout-trigger/index.vue +84 -0
  19. package/src/components/{pro-layout-sider-collapsed-menu.vue → pro-layout-sider/components/pro-layout-sider-collapsed-menu.vue} +30 -11
  20. package/src/components/{pro-layout-sider-icon.vue → pro-layout-sider/components/pro-layout-sider-icon.vue} +2 -2
  21. package/src/components/pro-layout-sider/components/pro-layout-sider-menu-item.vue +137 -0
  22. package/src/components/pro-layout-sider/components/pro-layout-sider-other-menu.vue +140 -0
  23. package/src/components/pro-layout-sider/components/pro-layout-sider-search.vue +345 -0
  24. package/src/components/pro-layout-sider/index.vue +477 -0
  25. package/src/components/{pro-layout-tabs.vue → pro-layout-tabs/index.vue} +85 -13
  26. package/src/index.vue +296 -37
  27. package/src/styles/color.less +267 -0
  28. package/src/styles/images/xiaoxitongzhi.png +0 -0
  29. package/src/styles/other-menu.less +111 -0
  30. package/src/utils/menuItem.js +10 -0
  31. package/src/utils/sider.js +16 -1
  32. package/src/components/pro-layout-header.vue +0 -52
  33. package/src/components/pro-layout-logo.vue +0 -79
  34. package/src/components/pro-layout-nav.vue +0 -150
  35. package/src/components/pro-layout-sider-menu-item.vue +0 -37
  36. package/src/components/pro-layout-sider.vue +0 -240
  37. package/src/components/pro-layout-tools-user.vue +0 -84
  38. package/src/components/pro-layout-tools.vue +0 -21
  39. package/src/components/pro-layout-trigger.vue +0 -48
@@ -0,0 +1,360 @@
1
+ <template>
2
+ <div class="menu-list" ref="menuList">
3
+ <ul class="top-menu" :style="layoutSize === 'default' ? {height: '64px'} : {height: '50px'}" ref="topNav">
4
+ <template v-for="(item,index) in topMenList" >
5
+ <li class="top-menu-item"
6
+ :class="{ 'active': notHomeIndex ? activeName === item.appId : 'backHome' === item.appId}"
7
+ :key="item.appId"
8
+ @click="selectApp(item.appId)">
9
+ <div class="menu-item" :style="layoutSize === 'default' ? {paddingTop: '10px'} : {paddingTop: '2px'}">
10
+ <p class="menu-icon" v-show="systemInfo.navLogo==='1'"><Icon :type="item.icon" :size="20"></Icon></p>
11
+ <p class="menu-txt" :title="item.name">{{ item.name }}</p>
12
+ </div>
13
+ </li>
14
+ </template>
15
+ </ul>
16
+ </div>
17
+ </template>
18
+
19
+ <script>
20
+ import Bus from '@lambo-design/shared/utils/bus'
21
+ import { deepCopy } from '@lambo-design/shared/utils/assist'
22
+ import _ from "lodash";
23
+
24
+ export default {
25
+ props: {
26
+ acceptInt: {
27
+ type: Number,
28
+ default: 0
29
+ },
30
+ topMenListNum: {
31
+ type: Number,
32
+ default: 0
33
+ },
34
+ availableWidth: {
35
+ type: Number,
36
+ default: 800
37
+ },
38
+ notHomeIndex: {
39
+ type: Boolean,
40
+ default: true
41
+ }
42
+ },
43
+ data() {
44
+ return {
45
+ systemInfo:{},
46
+ pointer:0,
47
+ arrowFlag: true,
48
+ acceptAppId: '',
49
+ navList: [],
50
+ topMenList: [],
51
+ topTqmMenList:[],
52
+ otherList: [],
53
+ activeName: '',
54
+ topMenuNum: 7,
55
+ displayMenuNum: 7, // 实际显示的菜单数量
56
+ lastTopMenuNum:-1,
57
+ originMenuList: [],
58
+ layoutSize:"default",
59
+ // menuItemWidths: [], // 存储每个菜单项的宽度
60
+ resizeObserver: null,
61
+ }
62
+ },
63
+ methods: {
64
+ initListener() {
65
+ Bus.$on('system-info',(data) => {
66
+ this.initSystemInfo(data);
67
+ })
68
+ Bus.$on('nav-list', (data) => {
69
+ this.initNav(data)
70
+ })
71
+ Bus.$on('change-app', ({ appId, appInfo }) => {
72
+ this.changeApp(appId, appInfo)
73
+ })
74
+ },
75
+ destroyListener() {
76
+ Bus.$off('system-info')
77
+ Bus.$off('nav-list')
78
+ Bus.$off('change-app')
79
+ },
80
+ initSystemInfo(data) {
81
+ if (data) {
82
+ this.systemInfo = data
83
+ this.layoutSize = data.layoutSize ? data.layoutSize : 'default';
84
+ this.topMenuNum = Number.isInteger(data.topMenu) ? data.topMenu : 4;
85
+ this.acceptAppId = data.acceptAppId ? data.acceptAppId : '';
86
+ if (!this.activeName && this.notHomeIndex) {
87
+ this.activeName = this.acceptAppId
88
+ }
89
+ this.initNav(this.navList)
90
+ }
91
+ },
92
+ initNav(data) {
93
+ // 过滤掉首页项,获取真实的业务数据
94
+ const realData = data.filter(item => item.appId !== 'backHome');
95
+ if (realData.toString() === this.navList.toString() && this.topMenuNum === this.lastTopMenuNum) {
96
+ return
97
+ }
98
+ this.navList = realData
99
+ this.lastTopMenuNum = this.topMenuNum
100
+
101
+ let dataClone = _.cloneDeep(realData);
102
+ // 如果配置了显示首页且显示样式是应用式,在列表最前面添加首页项
103
+ if (dataClone && dataClone.length > 0 && this.systemInfo.backToHome && this.systemInfo.backToHomeStyle === "app") {
104
+ const homeItem = {
105
+ appId: 'backHome',
106
+ name: this.systemInfo.backToHomeText || '首页',
107
+ icon: 'ios-home',
108
+ selected: false
109
+ };
110
+ dataClone.unshift(homeItem);
111
+ }
112
+
113
+ this.topMenList = dataClone
114
+ this.$emit('topMen-list', this.topMenList);
115
+
116
+ // 计算可以显示的菜单数量
117
+ this.calculateDisplayMenuNum();
118
+
119
+ if (this.topMenList.length > 0) {
120
+ let appId = this.topMenList[0].appId
121
+ if (this.systemInfo.backToHome && this.systemInfo.backToHomeStyle === "app") {
122
+ appId = this.topMenList[1].appId;
123
+ }
124
+ for (let i = 0; i < this.topMenList.length; i++) {
125
+ if (this.topMenList[i].selected == true) {
126
+ appId = this.topMenList[i].appId
127
+ }
128
+ }
129
+ if (this.activeName) {
130
+ appId = this.activeName
131
+ }
132
+ if (this.notHomeIndex) {
133
+ this.selectApp(appId)
134
+ } else if (this.systemInfo.backToHome === "1" && this.systemInfo.backToHomeStyle === "app") {
135
+ this.activeName = 'backHome'
136
+ }
137
+ }
138
+ },
139
+ // 计算可以显示的菜单数量
140
+ calculateDisplayMenuNum() {
141
+ this.$nextTick(() => {
142
+ if (!this.$refs.menuList) return;
143
+
144
+ // 获取单个菜单项的平均宽度
145
+ // const menuItems = this.$refs.menuList.querySelectorAll('.top-menu-item');
146
+ // if (menuItems.length === 0) return;
147
+
148
+ let totalWidth = 0;
149
+ let itemWidths = [];
150
+
151
+ // 临时显示所有菜单项以测量宽度
152
+ // menuItems.forEach((item, index) => {
153
+ // item.style.display = 'block';
154
+ // const width = item.offsetWidth;
155
+ // itemWidths.push(width);
156
+ // totalWidth += width;
157
+ // });
158
+
159
+ // this.menuItemWidths = itemWidths;
160
+
161
+ // 计算可以显示的菜单数量
162
+ let displayCount = 0;
163
+ let accumulatedWidth = 0;
164
+ const reservedWidth = 40; // 预留一些边距
165
+
166
+ this.topMenList.forEach((item, index) => {
167
+ const estimatedWidth = this.estimateMenuItemWidth(item);
168
+ if (accumulatedWidth + estimatedWidth <= this.availableWidth - reservedWidth) {
169
+ accumulatedWidth += estimatedWidth;
170
+ displayCount++;
171
+ }
172
+ })
173
+ //
174
+ // for (let i = 0; i < itemWidths.length; i++) {
175
+ // if (accumulatedWidth + itemWidths[i] <= this.availableWidth - reservedWidth) {
176
+ // accumulatedWidth += itemWidths[i];
177
+ // displayCount++;
178
+ // } else {
179
+ // break;
180
+ // }
181
+ // }
182
+
183
+ // 确保至少显示一个菜单项
184
+ let displayMenuNum = Math.max(1, displayCount);
185
+
186
+ // 如果配置的数量更小则依赖配置
187
+ if(this.topMenuNum < displayMenuNum){
188
+ this.displayMenuNum = this.topMenuNum
189
+ }else{
190
+ this.displayMenuNum = displayMenuNum
191
+ }
192
+
193
+ // this.displayMenuNum = Math.max(1, displayCount);
194
+ // 判断是否需要显示箭头
195
+ const needArrows = this.topMenList.length > this.displayMenuNum && this.displayMenuNum > 0;
196
+ this.$emit('topMen-num', this.displayMenuNum);
197
+ this.$emit('topMen-true', needArrows);
198
+
199
+ // 向父组件报告实际宽度
200
+ this.$emit('menu-width-change', totalWidth);
201
+
202
+ //重新调整样式
203
+ // menuItems.forEach((item, index) => {
204
+ // if(!(this.pointer <= index && index < (this.pointer + this.displayMenuNum))){
205
+ // item.style.display = 'none';
206
+ // }else{
207
+ // item.style.display = 'block'
208
+ // }
209
+ // });
210
+ });
211
+ },
212
+ estimateMenuItemWidth(item) {
213
+ // 基础宽度:内边距 + 边框
214
+ let baseWidth = 30;
215
+
216
+ // 图标宽度
217
+ if (item.icon && this.systemInfo.navLogo === '1') {
218
+ baseWidth += 30;
219
+ }
220
+
221
+ // 文字宽度估算(每个字符约14px)
222
+
223
+ const textWidth = (item.name || '').length * 14;
224
+ return baseWidth + textWidth;
225
+ },
226
+
227
+ selectApp(appId) {
228
+ if (appId) {
229
+ if (appId === 'backHome') {
230
+ Bus.$emit("change-app", {
231
+ appId,
232
+ appInfo: {}
233
+ });
234
+ return;
235
+ }
236
+ this.activeName = appId
237
+ let res = this.navList.filter(app => app.appId == appId)
238
+ Bus.$emit('change-app', { appId, appInfo: res[0] })
239
+ }
240
+ },
241
+ changeApp(appId, appInfo){
242
+ if (appId) {
243
+ this.activeName = appId
244
+ }
245
+ },
246
+ // 监听菜单容器大小变化
247
+ observeResize() {
248
+ if (window.ResizeObserver) {
249
+ this.resizeObserver = new ResizeObserver(() => {
250
+ this.calculateDisplayMenuNum();
251
+ this.controlVisable()
252
+ });
253
+
254
+ if (this.$refs.menuList) {
255
+ this.resizeObserver.observe(this.$refs.menuList);
256
+ }
257
+ }
258
+ },
259
+ controlVisable(){
260
+ //重新调整样式
261
+ const menuItems = this.$refs.menuList.querySelectorAll('.top-menu-item');
262
+ menuItems.forEach((item, index) => {
263
+ if(!(this.pointer <= index && index < (this.pointer + this.displayMenuNum))){
264
+ item.style.display = 'none';
265
+ }else{
266
+ item.style.display = 'block';
267
+ }
268
+ });
269
+ }
270
+ },
271
+ watch: {
272
+ acceptInt(val){
273
+ this.pointer = val;
274
+ this.controlVisable()
275
+ },
276
+ availableWidth(newVal, oldVal) {
277
+ if (newVal !== oldVal) {
278
+ this.calculateDisplayMenuNum();
279
+ }
280
+ },
281
+ topMenList() {
282
+ this.calculateDisplayMenuNum();
283
+ }
284
+ },
285
+ mounted() {
286
+ this.calculateDisplayMenuNum();
287
+ this.$nextTick(() => {
288
+ this.observeResize();
289
+ });
290
+ },
291
+ created() {
292
+ this.initListener()
293
+ },
294
+ beforeDestroy() {
295
+ this.destroyListener();
296
+ if (this.resizeObserver) {
297
+ this.resizeObserver.disconnect();
298
+ }
299
+ }
300
+ }
301
+ </script>
302
+
303
+ <style scoped lang="less">
304
+ @import '@lambo-design/core/src/styles/default.less';
305
+ .menu-list {
306
+ height: 100%;
307
+ line-height: 24px;
308
+ cursor: pointer;
309
+ font-size: 16px;
310
+ margin-left: 15px;
311
+ overflow: hidden;
312
+
313
+ .top-menu {
314
+ overflow: hidden;
315
+ white-space: nowrap;
316
+
317
+ .top-menu-item {
318
+ padding-left: 15px;
319
+ padding-right: 15px;
320
+ position: relative;
321
+ height: 100%;
322
+ list-style: none;
323
+ float: left;
324
+ transition: all 0.3s ease;
325
+
326
+ &:hover {
327
+ .menu-item {
328
+ // hover styles
329
+ }
330
+ }
331
+ &.active {
332
+ .menu-item {
333
+ // active styles
334
+ }
335
+ }
336
+ .menu-item {
337
+ display: flex;
338
+ margin-top: 10px;
339
+ text-align: center;
340
+ }
341
+ .menu-icon {
342
+ height: 20px;
343
+ line-height: 20px;
344
+ text-align: center;
345
+ margin-right: 10px;
346
+ margin-top: 3px;
347
+ }
348
+ .menu-txt {
349
+ text-align: center;
350
+ font-size: 14px;
351
+ line-height: 2;
352
+ white-space: nowrap;
353
+ overflow: hidden;
354
+ text-overflow: ellipsis;
355
+ max-width: 120px; // 限制最大宽度
356
+ }
357
+ }
358
+ }
359
+ }
360
+ </style>
@@ -0,0 +1,225 @@
1
+ <template>
2
+ <div class="pro-layout-nav-slide-wrapper" ref="slideWrapper">
3
+ <div class="nav-box-slide" ref="navBox" :style="{ width: availableWidth + 'px' }">
4
+ <LamboProNavSlideMenu
5
+ :not-home-index="notHomeIndex"
6
+ :accept-int="pointer"
7
+ :available-width="availableWidth"
8
+ @topMen-list="handleCustomEvent"
9
+ @topMen-num="topMen"
10
+ @topMen-true="topMenTrue"
11
+ @menu-width-change="handleMenuWidthChange"
12
+ ></LamboProNavSlideMenu>
13
+ </div>
14
+ <!--slide按钮-->
15
+ <div class="tools-box-slide" v-if="shouldShowArrows">
16
+ <div style="margin-right: 50px;">
17
+ <Icon
18
+ class="more-menu"
19
+ style="margin-right: 10px"
20
+ type="md-arrow-dropleft-circle"
21
+ v-if="arrowFlag"
22
+ @click="moveMenu('left')"
23
+ :style="{ opacity: canMoveLeft ? 1 : 0.3 }"
24
+ />
25
+ <Icon
26
+ type="md-arrow-dropright-circle"
27
+ class="more-menu"
28
+ v-if="arrowFlag"
29
+ @click="moveMenu('right')"
30
+ :style="{ opacity: canMoveRight ? 1 : 0.3 }"
31
+ />
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </template>
36
+
37
+ <script>
38
+ import LamboProNavSlideMenu from './components/pro-layout-nav-slide-menu'
39
+
40
+ export default {
41
+ props: {
42
+ availableWidth: {
43
+ type: Number,
44
+ default: 800
45
+ },
46
+ autoCalculateWidth: {
47
+ type: Boolean,
48
+ default: true
49
+ },
50
+ notHomeIndex: {
51
+ type: Boolean,
52
+ default: true
53
+ }
54
+ },
55
+ data(){
56
+ return {
57
+ pointer: 0,
58
+ topList: [],
59
+ topNum: 0,
60
+ arrowFlag: false,
61
+ menuActualWidth: 0,
62
+ shouldShowArrows: false,
63
+ canMoveLeft: false,
64
+ canMoveRight: false,
65
+ resizeObserver: null,
66
+ }
67
+ },
68
+ components: {
69
+ LamboProNavSlideMenu
70
+ },
71
+ methods:{
72
+ handleCustomEvent(data) {
73
+ // 接收子组件传递的数据
74
+ this.topList = data;
75
+ this.checkWidthAndArrows();
76
+ },
77
+ topMen(data){
78
+ this.topNum = data;
79
+ this.checkWidthAndArrows();
80
+ },
81
+ topMenTrue(data){
82
+ this.arrowFlag = data;
83
+ this.checkWidthAndArrows();
84
+ },
85
+ // 处理菜单宽度变化
86
+ handleMenuWidthChange(width) {
87
+ this.menuActualWidth = width;
88
+ this.checkWidthAndArrows();
89
+ },
90
+ // 检查宽度并决定是否显示箭头
91
+ checkWidthAndArrows() {
92
+ this.$nextTick(() => {
93
+ if (!this.autoCalculateWidth) {
94
+ this.shouldShowArrows = this.arrowFlag;
95
+ return;
96
+ }
97
+
98
+ // 计算实际菜单宽度
99
+ const navBox = this.$refs.navBox;
100
+ if (navBox) {
101
+ const menuContainer = navBox.querySelector('.menu-list');
102
+ if (menuContainer) {
103
+ this.menuActualWidth = menuContainer.scrollWidth;
104
+ }
105
+ }
106
+
107
+ // 预留箭头按钮空间
108
+ const arrowSpace = 80;
109
+ const effectiveWidth = this.availableWidth - arrowSpace;
110
+
111
+ // 判断是否需要显示箭头
112
+ this.shouldShowArrows = this.menuActualWidth > effectiveWidth;
113
+
114
+ if (this.shouldShowArrows) {
115
+ this.updateArrowStates();
116
+ }
117
+
118
+ // 触发宽度变化事件
119
+ this.$emit('width-change', {
120
+ available: this.availableWidth,
121
+ actual: this.menuActualWidth,
122
+ needArrows: this.shouldShowArrows
123
+ });
124
+
125
+ });
126
+ },
127
+ // 更新箭头状态
128
+ updateArrowStates() {
129
+ if (!this.shouldShowArrows) {
130
+ this.canMoveLeft = false;
131
+ this.canMoveRight = false;
132
+ return;
133
+ }
134
+
135
+ // 计算最大可移动位置
136
+ const maxPointer = Math.max(0, this.topList.length - this.topNum);
137
+
138
+ this.canMoveLeft = this.pointer > 0;
139
+ this.canMoveRight = this.pointer < maxPointer;
140
+ },
141
+ moveMenu: function (direction) {
142
+ if (!this.shouldShowArrows) return;
143
+
144
+ const maxPointer = Math.max(0, this.topList.length - this.topNum);
145
+
146
+ if (direction === "right") {
147
+ if (this.pointer < maxPointer) {
148
+ this.pointer++;
149
+ }
150
+ } else {
151
+ if (this.pointer > 0) {
152
+ this.pointer--;
153
+ }
154
+ }
155
+
156
+ this.updateArrowStates();
157
+
158
+ this.flag = false;
159
+ let self = this;
160
+ setTimeout(() => {
161
+ self.flag = true;
162
+ }, 0);
163
+ },
164
+ // 监听容器大小变化
165
+ observeResize() {
166
+ if (window.ResizeObserver) {
167
+ this.resizeObserver = new ResizeObserver(() => {
168
+ this.checkWidthAndArrows();
169
+ });
170
+
171
+ if (this.$refs.slideWrapper) {
172
+ this.resizeObserver.observe(this.$refs.slideWrapper);
173
+ }
174
+ }
175
+ },
176
+ },
177
+ watch: {
178
+ availableWidth(newVal) {
179
+ this.checkWidthAndArrows();
180
+ },
181
+ pointer() {
182
+ this.updateArrowStates();
183
+ }
184
+ },
185
+ mounted() {
186
+ this.$nextTick(() => {
187
+ this.checkWidthAndArrows();
188
+ this.observeResize();
189
+ });
190
+ },
191
+ beforeDestroy() {
192
+ if (this.resizeObserver) {
193
+ this.resizeObserver.disconnect();
194
+ }
195
+ }
196
+ }
197
+ </script>
198
+
199
+ <style scoped lang="less">
200
+ .pro-layout-nav-slide-wrapper {
201
+ display: flex;
202
+ align-items: center;
203
+ height: 100%;
204
+ width: 100%;
205
+
206
+ .nav-box-slide{
207
+ overflow: hidden;
208
+ flex: 1;
209
+ }
210
+
211
+ .tools-box-slide{
212
+ flex-shrink: 0;
213
+ }
214
+ }
215
+
216
+ .more-menu {
217
+ font-size: 22px;
218
+ cursor: pointer;
219
+ transition: opacity 0.3s ease;
220
+
221
+ &:hover {
222
+ opacity: 0.8 !important;
223
+ }
224
+ }
225
+ </style>