leisure-core 0.6.91 → 0.6.93
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/PROJECT_GUIDE.md +52 -23
- package/le-home/src/LeHeaderMenu.vue +60 -0
- package/le-home/src/main.vue +641 -134
- package/package.json +1 -1
- package/le-theme/common/var.scss +0 -17
package/PROJECT_GUIDE.md
CHANGED
|
@@ -47,14 +47,13 @@ leisure-core/
|
|
|
47
47
|
│ └── directives/src/ # (预留指令目录,当前为空)
|
|
48
48
|
├── le-theme/ # 主题资源 + 一键换肤服务
|
|
49
49
|
│ ├── common/
|
|
50
|
-
│ │ ├── var.scss # SCSS 变量(编译期用)
|
|
51
50
|
│ │ └── vars.css # CSS 自定义属性层(运行时换肤用)+ Element UI primary 覆盖
|
|
52
51
|
│ ├── iconfont/ # 字体图标(css + ttf/woff/woff2)
|
|
53
52
|
│ ├── pages/index.css # 页面样式
|
|
54
53
|
│ └── index.js # LeTheme 主题服务(set/register/init)
|
|
55
54
|
└── le-*/ # 各功能组件(共 36 个,见下文)
|
|
56
55
|
├── index.js # 组件注册入口(标准 install 模式)
|
|
57
|
-
└── src/main.vue # 组件实现(部分含 rmixins.js 等辅助文件)
|
|
56
|
+
└── src/main.vue # 组件实现(部分含 LeHeaderMenu.vue / rmixins.js 等辅助文件)
|
|
58
57
|
```
|
|
59
58
|
|
|
60
59
|
## 4. 架构约定
|
|
@@ -152,7 +151,6 @@ export default LeXxx;
|
|
|
152
151
|
|
|
153
152
|
| 文件 | 作用 |
|
|
154
153
|
| ----------------- | ----------------------------------------------------------------------------------------- |
|
|
155
|
-
| `common/var.scss` | SCSS 变量(编译期用) |
|
|
156
154
|
| `common/vars.css` | **CSS 自定义属性层**(运行时换肤的核心),定义全部语义变量 + 覆盖 Element UI primary 色族 |
|
|
157
155
|
| `pages/index.css` | 页面级样式 |
|
|
158
156
|
| `iconfont/` | 字体图标 |
|
|
@@ -160,32 +158,40 @@ export default LeXxx;
|
|
|
160
158
|
|
|
161
159
|
### 6.2 CSS 变量层(`common/vars.css`)
|
|
162
160
|
|
|
163
|
-
在 `:root` 上定义约
|
|
161
|
+
在 `:root` 上定义约 40 个语义化变量,分为 6 组:
|
|
164
162
|
|
|
165
163
|
**品牌色族**
|
|
166
164
|
|
|
167
|
-
- `--le-color-primary`、`--le-color-primary-light`、`--le-color-primary-
|
|
165
|
+
- `--le-color-primary`、`--le-color-primary-2`、`--le-color-primary-light`、`--le-color-primary-light-1`、`--le-color-primary-light-9`
|
|
166
|
+
- `--le-color-primary-shadow`、`--le-color-primary-shadow-strong`、`--le-color-primary-shadow-soft`
|
|
168
167
|
|
|
169
|
-
|
|
168
|
+
**功能色**
|
|
170
169
|
|
|
171
|
-
- `--le-color-success`、`--le-color-
|
|
170
|
+
- `--le-color-success`、`--le-color-success-2`、`--le-color-success-shadow`
|
|
172
171
|
|
|
173
|
-
|
|
172
|
+
**文本**
|
|
174
173
|
|
|
175
|
-
- `--le-color-text-
|
|
176
|
-
- `--le-color-
|
|
174
|
+
- `--le-color-text-strong`、`--le-color-text-regular`、`--le-color-text-muted`
|
|
175
|
+
- `--le-color-text-form`、`--le-color-input-text`、`--le-color-on-dark`
|
|
176
|
+
|
|
177
|
+
**边框 / 容器 / 卡片 / 装饰**
|
|
178
|
+
|
|
179
|
+
- `--le-color-border`、`--le-color-border-hover`、`--le-color-border-dark`
|
|
180
|
+
- `--le-color-card-bg`、`--le-color-card-border`、`--le-color-card-shadow`
|
|
181
|
+
- `--le-color-input-bg`、`--le-color-placeholder`
|
|
182
|
+
- `--le-color-section-bg-from`、`--le-color-section-bg-to`
|
|
177
183
|
|
|
178
184
|
**le-home 框架色**(一键换肤必覆盖)
|
|
179
185
|
|
|
180
186
|
- `--le-color-header-bg`、`--le-color-aside-bg`、`--le-color-menu-bg`
|
|
181
187
|
- `--le-color-aside-text`、`--le-color-aside-active`
|
|
182
188
|
- `--le-color-aside-handle-hover`、`--le-color-aside-handle-active`
|
|
183
|
-
- `--le-color-aside-toggle-bg`、`--le-color-aside-toggle-
|
|
184
|
-
- `--le-color-contextmenu-bg`、`--le-color-contextmenu-color`、`--le-color-contextmenu-hover`
|
|
189
|
+
- `--le-color-aside-toggle-bg`、`--le-color-aside-toggle-hover`
|
|
190
|
+
- `--le-color-contextmenu-bg`、`--le-color-contextmenu-border`、`--le-color-contextmenu-text`、`--le-color-contextmenu-hover`、`--le-color-contextmenu-shadow`
|
|
185
191
|
|
|
186
192
|
**Element UI primary 覆盖**(让 `type="primary"` 按钮跟随换肤)
|
|
187
193
|
|
|
188
|
-
- `.el-button--primary`、`.el-tag--primary`、`.el-checkbox`、`.el-radio`、`.el-switch`、`.el-pagination`、`.el-tabs`、`.el-link` 等选择器的 `color/background/border` 均绑定到 `--le-color-primary*` 变量
|
|
194
|
+
- `.el-button--primary`、`.el-tag--primary`、`.el-checkbox`、`.el-radio`、`.el-switch`、`.el-slider`、`.el-pagination`、`.el-tabs`、`.el-link`、`.el-loading-spinner` 等选择器的 `color/background/border` 均绑定到 `--le-color-primary*` 变量
|
|
189
195
|
|
|
190
196
|
### 6.3 LeTheme 服务(`index.js`)
|
|
191
197
|
|
|
@@ -193,11 +199,11 @@ export default LeXxx;
|
|
|
193
199
|
|
|
194
200
|
**预置皮肤**
|
|
195
201
|
|
|
196
|
-
| 皮肤名 | 主色
|
|
197
|
-
| --------- |
|
|
198
|
-
| `default` |
|
|
199
|
-
| `dark` |
|
|
200
|
-
| `green` |
|
|
202
|
+
| 皮肤名 | 主色 | 用途 |
|
|
203
|
+
| --------- | ----------------- | ----------------------------------------- |
|
|
204
|
+
| `default` | `#409eff`(蓝色) | 当前默认(依赖 `:root` 变量,无额外覆盖) |
|
|
205
|
+
| `dark` | `#3b82f6`(深蓝) | 暗色模式 |
|
|
206
|
+
| `green` | `#00b96b`(翠绿) | 绿色主色 |
|
|
201
207
|
|
|
202
208
|
**API**
|
|
203
209
|
|
|
@@ -230,16 +236,27 @@ this.$leTheme.current; // "dark"
|
|
|
230
236
|
|
|
231
237
|
```js
|
|
232
238
|
// 方式 A:CSS 直接消费(静态样式)
|
|
233
|
-
.hello { color: var(--le-color-text-
|
|
239
|
+
.hello { color: var(--le-color-text-strong); }
|
|
234
240
|
|
|
235
241
|
// 方式 B:el-menu 等需要 prop 传值的场景
|
|
242
|
+
// getCssVar 必须定义在 methods 里(Vue 2 computed 不支持形参)
|
|
243
|
+
methods: {
|
|
244
|
+
getCssVar(name, fallback) {
|
|
245
|
+
try {
|
|
246
|
+
const v = getComputedStyle(document.documentElement)
|
|
247
|
+
.getPropertyValue(name).trim();
|
|
248
|
+
return v || fallback;
|
|
249
|
+
} catch (e) { return fallback; }
|
|
250
|
+
}
|
|
251
|
+
},
|
|
236
252
|
computed: {
|
|
237
253
|
menuBackgroundColor() {
|
|
238
254
|
return this.getCssVar('--le-color-menu-bg', '#3a3a3a');
|
|
239
255
|
}
|
|
240
256
|
},
|
|
241
257
|
mounted() {
|
|
242
|
-
|
|
258
|
+
this._themeChangeHandler = () => this.$forceUpdate();
|
|
259
|
+
window.addEventListener('le-theme-change', this._themeChangeHandler);
|
|
243
260
|
}
|
|
244
261
|
```
|
|
245
262
|
|
|
@@ -432,13 +449,25 @@ mounted() {
|
|
|
432
449
|
|
|
433
450
|
### 8.21 le-home
|
|
434
451
|
|
|
435
|
-
- **Props**:`extraDropdownItems`、`companyInfo`、`showClose`
|
|
436
|
-
- **Slots**:`otherContainer`
|
|
452
|
+
- **Props**:`extraDropdownItems`、`companyInfo`、`showClose`、`headerMenu`、`showHeaderMenu`
|
|
453
|
+
- **Slots**:`otherContainer`、`rightComment`
|
|
437
454
|
- **Events**:`homeInit`、`command(command)`(修改密码/退出之外的命令)、`userChangePwd(params, callback)`
|
|
455
|
+
- **子组件**:`LeHeaderMenu`(`src/LeHeaderMenu.vue`,递归菜单组件,支持任意深度嵌套)
|
|
438
456
|
- **行为**:挂载后注册键盘/鼠标事件,内置修改密码弹窗
|
|
457
|
+
- **Header 布局**:
|
|
458
|
+
- 三栏结构:左(品牌名)/ 中(横向菜单,可选)/ 右(用户下拉)
|
|
459
|
+
- 有 headerMenu 时 span 为 `4:12:8`,无时为 `8:0:16`
|
|
460
|
+
- 通过 `showHeaderMenu` prop 显式控制是否显示横向菜单
|
|
461
|
+
- **headerMenu 数据结构**:`[{ id, name, title, icon, children: [...] }]`,支持任意深度嵌套
|
|
462
|
+
- **关键方法**:
|
|
463
|
+
- `_filterHiddenMenus(list)`:递归过滤 `ishide === 1` 的菜单项
|
|
464
|
+
- `handleHeaderMenuSelect(item)`:复用侧边栏导航逻辑
|
|
465
|
+
- `getCssVar(name, fallback)`:读 CSS 变量的 helper(定义在 methods 里)
|
|
466
|
+
- **关键 computed**:`effectiveHeaderMenu`、`headerMenuBgColor`、`headerMenuTextColor`、`headerMenuActiveTextColor`
|
|
439
467
|
- **样式换肤支持**:
|
|
440
468
|
- 顶部 header 背景 / aside 背景 / 拖动条 / aside_open_close / contextmenu 全部使用 `var(--le-color-*)`
|
|
441
|
-
-
|
|
469
|
+
- el-menu 通过 `background-color` / `text-color` / `active-text-color` prop 动态绑定 CSS 变量(computed + `getCssVar`)
|
|
470
|
+
- 横向菜单弹出层通过全局 `.le-header-menu-popper` 样式绑定 header 背景色
|
|
442
471
|
- 监听 `window` 级 `le-theme-change` 事件,回调里 `$forceUpdate` 强制刷新 el-menu 的 prop
|
|
443
472
|
|
|
444
473
|
### 8.22 le-area
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- 有子项:渲染为 submenu(一级横向 / 下级下拉均支持) -->
|
|
3
|
+
<el-submenu
|
|
4
|
+
v-if="hasChildren"
|
|
5
|
+
:index="item.name"
|
|
6
|
+
popper-class="le-header-menu-popper"
|
|
7
|
+
>
|
|
8
|
+
<template slot="title">
|
|
9
|
+
<i v-if="item.icon" :class="item.icon"></i>
|
|
10
|
+
<span>{{ item.title }}</span>
|
|
11
|
+
</template>
|
|
12
|
+
<!-- 递归渲染子节点 -->
|
|
13
|
+
<le-header-menu
|
|
14
|
+
v-for="child in visibleChildren"
|
|
15
|
+
:key="child.id || child.name"
|
|
16
|
+
:item="child"
|
|
17
|
+
:level="level + 1"
|
|
18
|
+
:on-select="onSelect"
|
|
19
|
+
></le-header-menu>
|
|
20
|
+
</el-submenu>
|
|
21
|
+
<!-- 叶子节点:渲染为 menu-item -->
|
|
22
|
+
<el-menu-item v-else :index="item.name" @click="handleClick">
|
|
23
|
+
<i v-if="item.icon" :class="item.icon"></i>
|
|
24
|
+
<span>{{ item.title }}</span>
|
|
25
|
+
</el-menu-item>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script>
|
|
29
|
+
export default {
|
|
30
|
+
name: "LeHeaderMenu",
|
|
31
|
+
props: {
|
|
32
|
+
item: {
|
|
33
|
+
type: Object,
|
|
34
|
+
required: true,
|
|
35
|
+
},
|
|
36
|
+
level: {
|
|
37
|
+
type: Number,
|
|
38
|
+
default: 0,
|
|
39
|
+
},
|
|
40
|
+
onSelect: {
|
|
41
|
+
type: Function,
|
|
42
|
+
default: () => {},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
computed: {
|
|
46
|
+
hasChildren() {
|
|
47
|
+
return Array.isArray(this.item.children) && this.item.children.length > 0;
|
|
48
|
+
},
|
|
49
|
+
visibleChildren() {
|
|
50
|
+
if (!this.item.children) return [];
|
|
51
|
+
return this.item.children.filter((child) => child.ishide !== 1);
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
methods: {
|
|
55
|
+
handleClick() {
|
|
56
|
+
this.onSelect(this.item);
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
</script>
|
package/le-home/src/main.vue
CHANGED
|
@@ -1,15 +1,41 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<el-container class="le-home-cp-container">
|
|
3
3
|
<el-header class="header">
|
|
4
|
-
<el-row>
|
|
5
|
-
<el-col :span="
|
|
4
|
+
<el-row type="flex" align="middle" class="header-row">
|
|
5
|
+
<el-col :span="effectiveHeaderMenu.length ? 4 : 8" class="leftsection">
|
|
6
6
|
<div class="grid-content bg-purple">
|
|
7
7
|
<span class="userinfo-inner" @click="clickLeftTop">{{
|
|
8
8
|
companyInfo.name
|
|
9
9
|
}}</span>
|
|
10
10
|
</div>
|
|
11
11
|
</el-col>
|
|
12
|
-
<el-col
|
|
12
|
+
<el-col
|
|
13
|
+
v-if="effectiveHeaderMenu.length"
|
|
14
|
+
:span="12"
|
|
15
|
+
class="middlesection"
|
|
16
|
+
>
|
|
17
|
+
<el-menu
|
|
18
|
+
mode="horizontal"
|
|
19
|
+
class="header-menu"
|
|
20
|
+
:background-color="headerMenuBgColor"
|
|
21
|
+
:text-color="headerMenuTextColor"
|
|
22
|
+
:active-text-color="headerMenuActiveTextColor"
|
|
23
|
+
:unique-opened="true"
|
|
24
|
+
popper-class="le-header-menu-popper"
|
|
25
|
+
>
|
|
26
|
+
<le-header-menu
|
|
27
|
+
v-for="item in effectiveHeaderMenu"
|
|
28
|
+
:key="item.id || item.name"
|
|
29
|
+
:item="item"
|
|
30
|
+
:level="0"
|
|
31
|
+
:on-select="handleHeaderMenuSelect"
|
|
32
|
+
></le-header-menu>
|
|
33
|
+
</el-menu>
|
|
34
|
+
</el-col>
|
|
35
|
+
<el-col
|
|
36
|
+
:span="effectiveHeaderMenu.length ? 8 : 16"
|
|
37
|
+
class="rightsection"
|
|
38
|
+
>
|
|
13
39
|
<div class="grid-content bg-purple-light">
|
|
14
40
|
<slot name="rightComment"> </slot>
|
|
15
41
|
<el-dropdown size="medium" trigger="click" @command="handleCommand">
|
|
@@ -19,8 +45,6 @@
|
|
|
19
45
|
<i class="el-icon-arrow-down el-icon--right"></i>
|
|
20
46
|
</span>
|
|
21
47
|
<el-dropdown-menu slot="dropdown">
|
|
22
|
-
<el-dropdown-item command="1">修改密码</el-dropdown-item>
|
|
23
|
-
<!-- <el-dropdown-item command="3">个人信息</el-dropdown-item> -->
|
|
24
48
|
<el-dropdown-item
|
|
25
49
|
v-for="item in extraDropdownItems"
|
|
26
50
|
:key="item.command"
|
|
@@ -29,7 +53,12 @@
|
|
|
29
53
|
>
|
|
30
54
|
{{ item.label }}
|
|
31
55
|
</el-dropdown-item>
|
|
32
|
-
<el-dropdown-item command="
|
|
56
|
+
<el-dropdown-item command="openSysSettings"
|
|
57
|
+
>系统设置</el-dropdown-item
|
|
58
|
+
>
|
|
59
|
+
<el-dropdown-item command="2" divided
|
|
60
|
+
>退出系统</el-dropdown-item
|
|
61
|
+
>
|
|
33
62
|
</el-dropdown-menu>
|
|
34
63
|
</el-dropdown>
|
|
35
64
|
</div>
|
|
@@ -44,6 +73,7 @@
|
|
|
44
73
|
ref="asideRef"
|
|
45
74
|
>
|
|
46
75
|
<el-menu
|
|
76
|
+
ref="sideMenu"
|
|
47
77
|
:unique-opened="true"
|
|
48
78
|
class="menu"
|
|
49
79
|
:background-color="menuBackgroundColor"
|
|
@@ -108,7 +138,7 @@
|
|
|
108
138
|
</div>
|
|
109
139
|
</el-main>
|
|
110
140
|
</el-container>
|
|
111
|
-
<el-dialog
|
|
141
|
+
<!-- <el-dialog
|
|
112
142
|
title="修改密码"
|
|
113
143
|
ref="elDialog"
|
|
114
144
|
:visible.sync="showChangePwd"
|
|
@@ -161,8 +191,8 @@
|
|
|
161
191
|
>确认修改</el-button
|
|
162
192
|
>
|
|
163
193
|
</div>
|
|
164
|
-
</el-dialog>
|
|
165
|
-
<le-dialog-container :showDialog="showPerInfo" title="个人信息" width="30%">
|
|
194
|
+
</el-dialog> -->
|
|
195
|
+
<!-- <le-dialog-container :showDialog="showPerInfo" title="个人信息" width="30%">
|
|
166
196
|
<le-list-form
|
|
167
197
|
:formColumns="formColumns"
|
|
168
198
|
:formData="perInfo"
|
|
@@ -171,7 +201,119 @@
|
|
|
171
201
|
@closeDialog="cancelChangePerInfo"
|
|
172
202
|
@saveData="savePerInfo"
|
|
173
203
|
></le-list-form>
|
|
174
|
-
</le-dialog-container>
|
|
204
|
+
</le-dialog-container> -->
|
|
205
|
+
|
|
206
|
+
<!-- 系统设置对话框 -->
|
|
207
|
+
<el-dialog
|
|
208
|
+
:visible.sync="sysSettingsVisible"
|
|
209
|
+
title="系统设置"
|
|
210
|
+
width="560px"
|
|
211
|
+
:close-on-click-modal="false"
|
|
212
|
+
custom-class="rounded-dialog"
|
|
213
|
+
append-to-body
|
|
214
|
+
>
|
|
215
|
+
<el-tabs v-model="sysSettingsTab" type="border-card">
|
|
216
|
+
<!-- 修改密码 -->
|
|
217
|
+
<el-tab-pane label="修改密码" name="pwd">
|
|
218
|
+
<el-form
|
|
219
|
+
ref="sysPwdForm"
|
|
220
|
+
:model="sysPwdForm"
|
|
221
|
+
:rules="sysPwdRules"
|
|
222
|
+
label-width="100px"
|
|
223
|
+
style="padding: 20px 0"
|
|
224
|
+
>
|
|
225
|
+
<el-form-item label="原密码" prop="oldPwd">
|
|
226
|
+
<el-input
|
|
227
|
+
v-model="sysPwdForm.oldPwd"
|
|
228
|
+
type="password"
|
|
229
|
+
placeholder="请输入原密码"
|
|
230
|
+
show-password
|
|
231
|
+
></el-input>
|
|
232
|
+
</el-form-item>
|
|
233
|
+
<el-form-item label="新密码" prop="newPwd">
|
|
234
|
+
<el-input
|
|
235
|
+
v-model="sysPwdForm.newPwd"
|
|
236
|
+
type="password"
|
|
237
|
+
placeholder="请输入新密码"
|
|
238
|
+
show-password
|
|
239
|
+
></el-input>
|
|
240
|
+
</el-form-item>
|
|
241
|
+
<el-form-item label="确认密码" prop="confirmPwd">
|
|
242
|
+
<el-input
|
|
243
|
+
v-model="sysPwdForm.confirmPwd"
|
|
244
|
+
type="password"
|
|
245
|
+
placeholder="请再次输入新密码"
|
|
246
|
+
show-password
|
|
247
|
+
></el-input>
|
|
248
|
+
</el-form-item>
|
|
249
|
+
</el-form>
|
|
250
|
+
</el-tab-pane>
|
|
251
|
+
|
|
252
|
+
<!-- 修改个人信息 -->
|
|
253
|
+
<el-tab-pane label="修改个人信息" name="person">
|
|
254
|
+
<el-form
|
|
255
|
+
ref="sysPerForm"
|
|
256
|
+
:model="sysPerForm"
|
|
257
|
+
:rules="sysPerRules"
|
|
258
|
+
label-width="100px"
|
|
259
|
+
style="padding: 20px 0"
|
|
260
|
+
>
|
|
261
|
+
<el-form-item label="昵称" prop="nick">
|
|
262
|
+
<el-input
|
|
263
|
+
v-model="sysPerForm.nick"
|
|
264
|
+
placeholder="请输入昵称"
|
|
265
|
+
></el-input>
|
|
266
|
+
</el-form-item>
|
|
267
|
+
<el-form-item label="手机号" prop="phone">
|
|
268
|
+
<el-input
|
|
269
|
+
v-model="sysPerForm.phone"
|
|
270
|
+
placeholder="请输入手机号"
|
|
271
|
+
maxlength="11"
|
|
272
|
+
></el-input>
|
|
273
|
+
</el-form-item>
|
|
274
|
+
</el-form>
|
|
275
|
+
</el-tab-pane>
|
|
276
|
+
|
|
277
|
+
<!-- 设置皮肤 -->
|
|
278
|
+
<el-tab-pane label="设置皮肤" name="skin">
|
|
279
|
+
<div class="skin-picker">
|
|
280
|
+
<div
|
|
281
|
+
v-for="skin in availableSkins"
|
|
282
|
+
:key="skin.name"
|
|
283
|
+
:class="['skin-item', { active: currentSkin === skin.name }]"
|
|
284
|
+
@click="selectSkin(skin.name)"
|
|
285
|
+
>
|
|
286
|
+
<div class="skin-color" :style="{ background: skin.color }"></div>
|
|
287
|
+
<div class="skin-info">
|
|
288
|
+
<div class="skin-name">{{ skin.label }}</div>
|
|
289
|
+
<div class="skin-desc" v-if="skin.name === currentSkin">
|
|
290
|
+
当前使用
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
<i
|
|
294
|
+
v-if="currentSkin === skin.name"
|
|
295
|
+
class="el-icon-check skin-check"
|
|
296
|
+
></i>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
</el-tab-pane>
|
|
300
|
+
</el-tabs>
|
|
301
|
+
<!-- 对话框底部按钮:根据当前 tab 显示不同按钮 -->
|
|
302
|
+
<div slot="footer">
|
|
303
|
+
<template v-if="sysSettingsTab === 'pwd'">
|
|
304
|
+
<el-button @click="sysSettingsVisible = false">取消</el-button>
|
|
305
|
+
<el-button type="primary" @click="submitSysPwd">确定</el-button>
|
|
306
|
+
</template>
|
|
307
|
+
<template v-else-if="sysSettingsTab === 'person'">
|
|
308
|
+
<el-button @click="sysSettingsVisible = false">取消</el-button>
|
|
309
|
+
<el-button type="primary" @click="submitSysPer">确定</el-button>
|
|
310
|
+
</template>
|
|
311
|
+
<template v-else-if="sysSettingsTab === 'skin'">
|
|
312
|
+
<el-button @click="sysSettingsVisible = false">关闭</el-button>
|
|
313
|
+
</template>
|
|
314
|
+
</div>
|
|
315
|
+
</el-dialog>
|
|
316
|
+
|
|
175
317
|
<div v-show="contextMenuVisible">
|
|
176
318
|
<ul
|
|
177
319
|
:style="{ left: menuLeft + 'px', top: menuTop + 'px' }"
|
|
@@ -203,8 +345,10 @@
|
|
|
203
345
|
<script>
|
|
204
346
|
import { cloneDeep } from "lodash";
|
|
205
347
|
import { throttle } from "lodash"; // 可选,用于性能优化
|
|
348
|
+
import LeHeaderMenu from "./LeHeaderMenu.vue";
|
|
206
349
|
export default {
|
|
207
350
|
name: "le-home",
|
|
351
|
+
components: { LeHeaderMenu },
|
|
208
352
|
props: {
|
|
209
353
|
extraDropdownItems: {
|
|
210
354
|
type: Array,
|
|
@@ -218,32 +362,45 @@ export default {
|
|
|
218
362
|
type: Boolean,
|
|
219
363
|
default: true,
|
|
220
364
|
},
|
|
365
|
+
// 顶部横向菜单数据,支持多级嵌套
|
|
366
|
+
// 数据结构: [{ id, name, title, icon, children: [...] }]
|
|
367
|
+
headerMenu: {
|
|
368
|
+
type: Array,
|
|
369
|
+
default: () => [],
|
|
370
|
+
},
|
|
371
|
+
// 是否显示顶部横向菜单
|
|
372
|
+
// true: 只要 headerMenu 有数据就显示
|
|
373
|
+
// false: 即使 headerMenu 有数据也不显示
|
|
374
|
+
showHeaderMenu: {
|
|
375
|
+
type: Boolean,
|
|
376
|
+
default: false,
|
|
377
|
+
},
|
|
221
378
|
},
|
|
222
379
|
|
|
223
380
|
data() {
|
|
224
|
-
var validatePass = (rule, value, callback) => {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
};
|
|
238
|
-
var validatePass2 = (rule, value, callback) => {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
};
|
|
381
|
+
// var validatePass = (rule, value, callback) => {
|
|
382
|
+
// if (value === "") {
|
|
383
|
+
// callback(new Error("请输入密码"));
|
|
384
|
+
// } else {
|
|
385
|
+
// if (value.length < 8) {
|
|
386
|
+
// callback(new Error("密码至少8位"));
|
|
387
|
+
// } else {
|
|
388
|
+
// if (this.ruleForm.confirmPwd !== "") {
|
|
389
|
+
// this.$refs.ruleForm.validateField("confirmPwd ");
|
|
390
|
+
// }
|
|
391
|
+
// callback();
|
|
392
|
+
// }
|
|
393
|
+
// }
|
|
394
|
+
// };
|
|
395
|
+
// var validatePass2 = (rule, value, callback) => {
|
|
396
|
+
// if (value === "") {
|
|
397
|
+
// callback(new Error("请再次输入密码"));
|
|
398
|
+
// } else if (value !== this.ruleForm.pwd) {
|
|
399
|
+
// callback(new Error("两次输入密码不一致!"));
|
|
400
|
+
// } else {
|
|
401
|
+
// callback();
|
|
402
|
+
// }
|
|
403
|
+
// };
|
|
247
404
|
return {
|
|
248
405
|
style: {
|
|
249
406
|
display: "block",
|
|
@@ -253,29 +410,29 @@ export default {
|
|
|
253
410
|
aside_open_close: true,
|
|
254
411
|
menuData: [],
|
|
255
412
|
title: "",
|
|
256
|
-
cacheView: [],
|
|
257
|
-
showChangePwd: false,
|
|
258
|
-
showPerInfo: false,
|
|
413
|
+
// cacheView: [],
|
|
414
|
+
// showChangePwd: false,
|
|
415
|
+
// showPerInfo: false,
|
|
259
416
|
username: "",
|
|
260
|
-
changeUid: "",
|
|
417
|
+
// changeUid: "",
|
|
261
418
|
tabNameCurrent: "",
|
|
262
419
|
tabsAll: [],
|
|
263
|
-
ruleForm: {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
},
|
|
267
|
-
formColumns: [
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
],
|
|
271
|
-
perInfo: {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
},
|
|
275
|
-
rules: {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
},
|
|
420
|
+
// ruleForm: {
|
|
421
|
+
// pwd: "",
|
|
422
|
+
// confirmPwd: "",
|
|
423
|
+
// },
|
|
424
|
+
// formColumns: [
|
|
425
|
+
// { prop: "nick", label: "昵称" },
|
|
426
|
+
// { prop: "phone", label: "手机号" },
|
|
427
|
+
// ],
|
|
428
|
+
// perInfo: {
|
|
429
|
+
// nick: this.$store.getters.userinfo.nick,
|
|
430
|
+
// phone: this.$store.getters.userinfo.phone,
|
|
431
|
+
// },
|
|
432
|
+
// rules: {
|
|
433
|
+
// pwd: [{ validator: validatePass, trigger: "blur" }],
|
|
434
|
+
// confirmPwd: [{ validator: validatePass2, trigger: "blur" }],
|
|
435
|
+
// },
|
|
279
436
|
//右键菜单
|
|
280
437
|
contextMenuVisible: false,
|
|
281
438
|
menuLeft: 0,
|
|
@@ -286,16 +443,66 @@ export default {
|
|
|
286
443
|
isDragging: false,
|
|
287
444
|
asideWidth: 200, // 默认宽度,与原宽度一致
|
|
288
445
|
dragStartInfo: null, // {startX, startWidth}
|
|
446
|
+
// 系统设置对话框
|
|
447
|
+
sysSettingsVisible: false,
|
|
448
|
+
sysSettingsTab: "pwd",
|
|
449
|
+
sysPwdForm: {
|
|
450
|
+
oldPwd: "",
|
|
451
|
+
newPwd: "",
|
|
452
|
+
confirmPwd: "",
|
|
453
|
+
},
|
|
454
|
+
sysPwdRules: {
|
|
455
|
+
oldPwd: [{ required: true, message: "请输入原密码", trigger: "blur" }],
|
|
456
|
+
newPwd: [
|
|
457
|
+
{ required: true, message: "请输入新密码", trigger: "blur" },
|
|
458
|
+
{ min: 8, message: "密码至少8位", trigger: "blur" },
|
|
459
|
+
],
|
|
460
|
+
confirmPwd: [
|
|
461
|
+
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
|
462
|
+
{
|
|
463
|
+
validator: (rule, value, callback) => {
|
|
464
|
+
if (value !== this.sysPwdForm.newPwd) {
|
|
465
|
+
callback(new Error("两次输入密码不一致"));
|
|
466
|
+
} else {
|
|
467
|
+
callback();
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
trigger: "blur",
|
|
471
|
+
},
|
|
472
|
+
],
|
|
473
|
+
},
|
|
474
|
+
sysPerForm: {
|
|
475
|
+
nick: "",
|
|
476
|
+
phone: "",
|
|
477
|
+
},
|
|
478
|
+
sysPerRules: {
|
|
479
|
+
nick: [{ required: true, message: "请输入昵称", trigger: "blur" }],
|
|
480
|
+
phone: [
|
|
481
|
+
{ required: true, message: "请输入手机号", trigger: "blur" },
|
|
482
|
+
{
|
|
483
|
+
pattern: /^1[3-9]\d{9}$/,
|
|
484
|
+
message: "请输入正确的手机号",
|
|
485
|
+
trigger: "blur",
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
},
|
|
489
|
+
// 当前皮肤名(响应式,供皮肤选择器和 UI 刷新使用)
|
|
490
|
+
currentSkin: "default",
|
|
289
491
|
};
|
|
290
492
|
},
|
|
291
493
|
computed: {
|
|
292
494
|
asideStyle() {
|
|
495
|
+
// 依赖 currentSkin 让 Vue 知道需要重算(getCssVar 读取 DOM 不是响应式的)
|
|
496
|
+
if (this.currentSkin) {
|
|
497
|
+
// 触发访问,建立依赖关系
|
|
498
|
+
}
|
|
293
499
|
if (this.isHideMenu) {
|
|
294
500
|
return {
|
|
295
501
|
width: "0px",
|
|
296
502
|
minWidth: "0px",
|
|
297
503
|
maxWidth: "0px",
|
|
298
504
|
flex: "0 0 0px",
|
|
505
|
+
backgroundColor: this.getCssVar("--le-color-aside-bg", "#3A3A3A"),
|
|
299
506
|
};
|
|
300
507
|
}
|
|
301
508
|
return {
|
|
@@ -304,19 +511,48 @@ export default {
|
|
|
304
511
|
maxWidth: `${this.asideWidth}px`,
|
|
305
512
|
flex: `0 0 ${this.asideWidth}px`,
|
|
306
513
|
transition: this.isDragging ? "none" : "width 0.3s",
|
|
514
|
+
backgroundColor: this.getCssVar("--le-color-aside-bg", "#3A3A3A"),
|
|
307
515
|
};
|
|
308
516
|
},
|
|
309
|
-
// 读取 CSS 变量的 helper,返回指定变量值(回退到 fallback)
|
|
310
|
-
// 注意:此函数必须放在 methods 里,不能放在 computed 里,否则会被当成无参 computed 而非方法
|
|
311
517
|
// el-menu 的三个颜色 prop,绑定到 CSS 变量,实现主题切换
|
|
312
518
|
menuBackgroundColor() {
|
|
313
|
-
return this.getCssVar("--le-color-menu-bg", "#3A3A3A");
|
|
519
|
+
return this.currentSkin, this.getCssVar("--le-color-menu-bg", "#3A3A3A");
|
|
314
520
|
},
|
|
315
521
|
menuTextColor() {
|
|
316
|
-
return this.getCssVar("--le-color-aside-text", "#fff");
|
|
522
|
+
return this.currentSkin, this.getCssVar("--le-color-aside-text", "#fff");
|
|
317
523
|
},
|
|
318
524
|
menuActiveTextColor() {
|
|
319
|
-
return
|
|
525
|
+
return (
|
|
526
|
+
this.currentSkin, this.getCssVar("--le-color-aside-active", "#ffd04b")
|
|
527
|
+
);
|
|
528
|
+
},
|
|
529
|
+
// header 横向菜单:综合判断 showHeaderMenu + headerMenu 数据 + 过滤隐藏项
|
|
530
|
+
effectiveHeaderMenu() {
|
|
531
|
+
if (!this.showHeaderMenu) return [];
|
|
532
|
+
if (!this.headerMenu || !this.headerMenu.length) return [];
|
|
533
|
+
return this._filterHiddenMenus(this.headerMenu);
|
|
534
|
+
},
|
|
535
|
+
// header 横向菜单颜色(跟随主题)
|
|
536
|
+
headerMenuBgColor() {
|
|
537
|
+
return (
|
|
538
|
+
this.currentSkin, this.getCssVar("--le-color-header-bg", "#212121")
|
|
539
|
+
);
|
|
540
|
+
},
|
|
541
|
+
headerMenuTextColor() {
|
|
542
|
+
return this.currentSkin, this.getCssVar("--le-color-aside-text", "#fff");
|
|
543
|
+
},
|
|
544
|
+
headerMenuActiveTextColor() {
|
|
545
|
+
return (
|
|
546
|
+
this.currentSkin, this.getCssVar("--le-color-aside-active", "#ffd04b")
|
|
547
|
+
);
|
|
548
|
+
},
|
|
549
|
+
// 皮肤列表(供下拉子菜单渲染)
|
|
550
|
+
availableSkins() {
|
|
551
|
+
return [
|
|
552
|
+
{ name: "default", label: "默认蓝", color: "#409eff" },
|
|
553
|
+
{ name: "dark", label: "深色", color: "#0f172a" },
|
|
554
|
+
{ name: "green", label: "翠绿", color: "#00b96b" },
|
|
555
|
+
];
|
|
320
556
|
},
|
|
321
557
|
},
|
|
322
558
|
mounted() {
|
|
@@ -332,18 +568,18 @@ export default {
|
|
|
332
568
|
this.$store.commit("setCname", this.companyInfo.name);
|
|
333
569
|
}
|
|
334
570
|
|
|
335
|
-
let dict = [
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
];
|
|
571
|
+
// let dict = [
|
|
572
|
+
// {
|
|
573
|
+
// title: "是",
|
|
574
|
+
// value: 1,
|
|
575
|
+
// },
|
|
576
|
+
// {
|
|
577
|
+
// title: "否",
|
|
578
|
+
// value: 0,
|
|
579
|
+
// },
|
|
580
|
+
// ];
|
|
345
581
|
|
|
346
|
-
this.$store.commit("setDict", dict);
|
|
582
|
+
// this.$store.commit("setDict", dict);
|
|
347
583
|
|
|
348
584
|
const menus = cloneDeep(this.$store.getters.menu);
|
|
349
585
|
this.menuData = menus.filter(function (item) {
|
|
@@ -371,12 +607,20 @@ export default {
|
|
|
371
607
|
document.addEventListener("mousemove", this.handleDrag);
|
|
372
608
|
document.addEventListener("mouseup", this.stopDrag);
|
|
373
609
|
|
|
374
|
-
// 2026-08-26
|
|
375
|
-
this._themeChangeHandler = () => {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
610
|
+
// 2026-08-26:监听主题切换事件,更新响应式皮肤名触发 computed 重算
|
|
611
|
+
this._themeChangeHandler = (e) => {
|
|
612
|
+
const skinName =
|
|
613
|
+
(e && e.detail) ||
|
|
614
|
+
(this.$leTheme && this.$leTheme.current) ||
|
|
615
|
+
"default";
|
|
616
|
+
// 更新 currentSkin 后,所有依赖 currentSkin 的 computed 会自动重算
|
|
617
|
+
// 包括 asideStyle, menuBackgroundColor, menuTextColor, headerMenuBgColor 等
|
|
618
|
+
this.currentSkin = skinName;
|
|
379
619
|
};
|
|
620
|
+
// 初始化当前皮肤名
|
|
621
|
+
if (this.$leTheme) {
|
|
622
|
+
this.currentSkin = this.$leTheme.current;
|
|
623
|
+
}
|
|
380
624
|
if (typeof window !== "undefined") {
|
|
381
625
|
window.addEventListener("le-theme-change", this._themeChangeHandler);
|
|
382
626
|
}
|
|
@@ -401,6 +645,21 @@ export default {
|
|
|
401
645
|
this.stopDrag();
|
|
402
646
|
},
|
|
403
647
|
methods: {
|
|
648
|
+
// 递归过滤隐藏菜单项(ishide === 1 的项被过滤,其子项也一并丢弃)
|
|
649
|
+
_filterHiddenMenus(list) {
|
|
650
|
+
if (!Array.isArray(list)) return [];
|
|
651
|
+
return list
|
|
652
|
+
.filter((item) => item.ishide !== 1)
|
|
653
|
+
.map((item) => {
|
|
654
|
+
if (item.children && item.children.length) {
|
|
655
|
+
return {
|
|
656
|
+
...item,
|
|
657
|
+
children: this._filterHiddenMenus(item.children),
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
return item;
|
|
661
|
+
});
|
|
662
|
+
},
|
|
404
663
|
// 2026-08-26:读取 CSS 变量的 helper,返回指定变量值(回退到 fallback)
|
|
405
664
|
getCssVar(name, fallback) {
|
|
406
665
|
try {
|
|
@@ -412,6 +671,10 @@ export default {
|
|
|
412
671
|
return fallback;
|
|
413
672
|
}
|
|
414
673
|
},
|
|
674
|
+
// header 横向菜单项点击处理(复用 clickMenuItem 的导航逻辑)
|
|
675
|
+
handleHeaderMenuSelect(item) {
|
|
676
|
+
this.clickMenuItem(item);
|
|
677
|
+
},
|
|
415
678
|
handleKeyDown(e) {
|
|
416
679
|
// F5 键的 keyCode 是 116
|
|
417
680
|
if (e.keyCode === 116) {
|
|
@@ -456,76 +719,128 @@ export default {
|
|
|
456
719
|
}
|
|
457
720
|
},
|
|
458
721
|
handleCommand(command) {
|
|
459
|
-
if (command === "
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
this.perInfo.phone = this.$store.getters.userinfo.phone;
|
|
474
|
-
this.showPerInfo = true;
|
|
475
|
-
},
|
|
476
|
-
validatePhone(phone) {
|
|
477
|
-
const phoneRegex = /^1[3-9]\d{9}$/;
|
|
478
|
-
return phoneRegex.test(phone);
|
|
722
|
+
if (command === "openSysSettings") {
|
|
723
|
+
// 打开系统设置对话框,默认密码 tab
|
|
724
|
+
this.sysSettingsTab = "pwd";
|
|
725
|
+
this.sysPwdForm = { oldPwd: "", newPwd: "", confirmPwd: "" };
|
|
726
|
+
this.sysPerForm = {
|
|
727
|
+
nick: this.$store.getters.userinfo.nick || "",
|
|
728
|
+
phone: this.$store.getters.userinfo.phone || "",
|
|
729
|
+
};
|
|
730
|
+
this.sysSettingsVisible = true;
|
|
731
|
+
} else if (command === "2") {
|
|
732
|
+
this.signout();
|
|
733
|
+
} else {
|
|
734
|
+
this.$emit("command", command); // 抛出外部命令
|
|
735
|
+
}
|
|
479
736
|
},
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
this.
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
savePerInfo(param) {
|
|
492
|
-
if (param.phone) {
|
|
493
|
-
if (!this.validatePhone(param.phone)) {
|
|
494
|
-
this.$message.error("请输入有效的手机号");
|
|
495
|
-
return;
|
|
737
|
+
// 系统设置 - 提交修改密码
|
|
738
|
+
submitSysPwd() {
|
|
739
|
+
this.$refs.sysPwdForm.validate((valid) => {
|
|
740
|
+
if (valid) {
|
|
741
|
+
const payload = {
|
|
742
|
+
uid: this.$store.getters.userinfo.id,
|
|
743
|
+
pwd: this.sysPwdForm.newPwd,
|
|
744
|
+
confirmPwd: this.sysPwdForm.confirmPwd,
|
|
745
|
+
};
|
|
746
|
+
// 方案一:emit 给宿主,由宿主决定如何调用接口
|
|
747
|
+
this.$emit("userChangePwd", payload, this.changePwdAfter);
|
|
496
748
|
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
if (!param.nick && !param.phone) {
|
|
500
|
-
this.$message.error("昵称和手机号至少填写一项");
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
let params = {
|
|
505
|
-
uid: this.$store.getters.userinfo.id,
|
|
506
|
-
nick: param.nick,
|
|
507
|
-
phone: param.phone,
|
|
508
|
-
};
|
|
509
|
-
this.$emit("savePerInfo", params);
|
|
749
|
+
});
|
|
510
750
|
},
|
|
511
|
-
|
|
512
|
-
|
|
751
|
+
// 系统设置 - 提交修改个人信息
|
|
752
|
+
submitSysPer() {
|
|
753
|
+
this.$refs.sysPerForm.validate((valid) => {
|
|
513
754
|
if (valid) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
755
|
+
const payload = {
|
|
756
|
+
uid: this.$store.getters.userinfo.id,
|
|
757
|
+
nick: this.sysPerForm.nick,
|
|
758
|
+
phone: this.sysPerForm.phone,
|
|
518
759
|
};
|
|
519
|
-
this.$emit("
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
760
|
+
this.$emit("savePerInfo", payload);
|
|
761
|
+
this.sysSettingsVisible = false;
|
|
762
|
+
try {
|
|
763
|
+
let userinfo = this.$store.getters.userinfo;
|
|
764
|
+
if (userinfo) {
|
|
765
|
+
userinfo.nick = payload.nick;
|
|
766
|
+
userinfo.phone = payload.phone;
|
|
767
|
+
this.$store.commit("setUserInfo", userinfo);
|
|
768
|
+
}
|
|
769
|
+
} catch (e) {}
|
|
523
770
|
}
|
|
524
771
|
});
|
|
525
772
|
},
|
|
773
|
+
// 系统设置 - 选择皮肤
|
|
774
|
+
selectSkin(skinName) {
|
|
775
|
+
this.$leTheme.set(skinName);
|
|
776
|
+
this.$message.success("皮肤切换成功");
|
|
777
|
+
},
|
|
778
|
+
// changPwd() {
|
|
779
|
+
// this.changeUid = this.$store.getters.userinfo.id;
|
|
780
|
+
// this.ruleForm.pwd = "";
|
|
781
|
+
// this.ruleForm.confirmPwd = "";
|
|
782
|
+
// this.showChangePwd = true;
|
|
783
|
+
// },
|
|
784
|
+
// changPerson() {
|
|
785
|
+
// this.changeUid = this.$store.getters.userinfo.id;
|
|
786
|
+
// this.perInfo.nick = this.$store.getters.userinfo.nick;
|
|
787
|
+
// this.perInfo.phone = this.$store.getters.userinfo.phone;
|
|
788
|
+
// this.showPerInfo = true;
|
|
789
|
+
// },
|
|
790
|
+
validatePhone(phone) {
|
|
791
|
+
const phoneRegex = /^1[3-9]\d{9}$/;
|
|
792
|
+
return phoneRegex.test(phone);
|
|
793
|
+
},
|
|
794
|
+
// cancelChangePwd() {
|
|
795
|
+
// this.showChangePwd = false;
|
|
796
|
+
// this.changeUid = "";
|
|
797
|
+
// this.ruleForm.pwd = "";
|
|
798
|
+
// this.ruleForm.confirmPwd = "";
|
|
799
|
+
// },
|
|
800
|
+
// cancelChangePerInfo() {
|
|
801
|
+
// this.showPerInfo = false;
|
|
802
|
+
// this.perInfo.nick = "";
|
|
803
|
+
// this.perInfo.phone = "";
|
|
804
|
+
// },
|
|
805
|
+
// savePerInfo(param) {
|
|
806
|
+
// if (param.phone) {
|
|
807
|
+
// if (!this.validatePhone(param.phone)) {
|
|
808
|
+
// this.$message.error("请输入有效的手机号");
|
|
809
|
+
// return;
|
|
810
|
+
// }
|
|
811
|
+
// }
|
|
812
|
+
|
|
813
|
+
// if (!param.nick && !param.phone) {
|
|
814
|
+
// this.$message.error("昵称和手机号至少填写一项");
|
|
815
|
+
// return;
|
|
816
|
+
// }
|
|
817
|
+
|
|
818
|
+
// let params = {
|
|
819
|
+
// uid: this.$store.getters.userinfo.id,
|
|
820
|
+
// nick: param.nick,
|
|
821
|
+
// phone: param.phone,
|
|
822
|
+
// };
|
|
823
|
+
// this.$emit("savePerInfo", params);
|
|
824
|
+
// },
|
|
825
|
+
// saveChangePwd(formName) {
|
|
826
|
+
// this.$refs[formName].validate((valid) => {
|
|
827
|
+
// if (valid) {
|
|
828
|
+
// let params = {
|
|
829
|
+
// pwd: this.ruleForm.pwd,
|
|
830
|
+
// confirmPwd: this.ruleForm.confirmPwd,
|
|
831
|
+
// uid: this.changeUid,
|
|
832
|
+
// };
|
|
833
|
+
// this.$emit("userChangePwd", params, this.changePwdAfter);
|
|
834
|
+
// } else {
|
|
835
|
+
// console.log("error submit!!");
|
|
836
|
+
// return false;
|
|
837
|
+
// }
|
|
838
|
+
// });
|
|
839
|
+
// },
|
|
526
840
|
changePwdAfter(res) {
|
|
527
841
|
if (res && res.data && res.data.code == "10000") {
|
|
528
842
|
this.$message.success("更新成功");
|
|
843
|
+
this.sysSettingsVisible = false;
|
|
529
844
|
localStorage.clear();
|
|
530
845
|
this.$store.commit("setUserInfo", null);
|
|
531
846
|
this.$router.replace({ path: "/" });
|
|
@@ -731,9 +1046,91 @@ export default {
|
|
|
731
1046
|
font-size: 15px;
|
|
732
1047
|
|
|
733
1048
|
.header {
|
|
734
|
-
background: var(--le-color-header-bg);
|
|
1049
|
+
background-color: var(--le-color-header-bg) !important;
|
|
735
1050
|
color: #fff;
|
|
736
1051
|
height: 60px;
|
|
1052
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
1053
|
+
|
|
1054
|
+
/* 清除 Element UI el-row / el-col 默认背景 */
|
|
1055
|
+
.el-row,
|
|
1056
|
+
.el-col {
|
|
1057
|
+
background-color: transparent !important;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/* 清除 grid-content / bg-purple 等示例类的默认背景 */
|
|
1061
|
+
.grid-content,
|
|
1062
|
+
.bg-purple,
|
|
1063
|
+
.bg-purple-light {
|
|
1064
|
+
background-color: transparent !important;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
.header-row {
|
|
1068
|
+
height: 60px;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
.leftsection,
|
|
1072
|
+
.middlesection,
|
|
1073
|
+
.rightsection {
|
|
1074
|
+
height: 60px;
|
|
1075
|
+
line-height: 60px;
|
|
1076
|
+
display: flex;
|
|
1077
|
+
align-items: center;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
.leftsection {
|
|
1081
|
+
.userinfo-inner {
|
|
1082
|
+
font-size: 18px;
|
|
1083
|
+
font-weight: bold;
|
|
1084
|
+
cursor: pointer;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
.middlesection {
|
|
1089
|
+
flex: 1;
|
|
1090
|
+
display: flex;
|
|
1091
|
+
align-items: center;
|
|
1092
|
+
padding: 0 16px;
|
|
1093
|
+
|
|
1094
|
+
.header-menu {
|
|
1095
|
+
border-bottom: none;
|
|
1096
|
+
height: 60px;
|
|
1097
|
+
line-height: 60px;
|
|
1098
|
+
width: 100%;
|
|
1099
|
+
|
|
1100
|
+
/* 菜单项悬停/激活色 */
|
|
1101
|
+
.el-menu-item,
|
|
1102
|
+
.el-submenu__title {
|
|
1103
|
+
height: 60px;
|
|
1104
|
+
line-height: 60px;
|
|
1105
|
+
border-bottom: none;
|
|
1106
|
+
font-size: 15px;
|
|
1107
|
+
min-width: 100px;
|
|
1108
|
+
text-align: center;
|
|
1109
|
+
|
|
1110
|
+
&:hover {
|
|
1111
|
+
background-color: rgba(255, 255, 255, 0.1) !important;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
.el-submenu__icon-arrow {
|
|
1116
|
+
font-size: 12px;
|
|
1117
|
+
margin-left: 4px;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
.el-menu-item.is-active {
|
|
1121
|
+
border-bottom: 2px solid var(--le-color-aside-active);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
.rightsection {
|
|
1127
|
+
justify-content: flex-end;
|
|
1128
|
+
|
|
1129
|
+
.el-dropdown-link {
|
|
1130
|
+
cursor: pointer;
|
|
1131
|
+
color: #fff;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
737
1134
|
}
|
|
738
1135
|
|
|
739
1136
|
/* 修改 .aside 样式 - 移除固定宽度 !important */
|
|
@@ -955,4 +1352,114 @@ export default {
|
|
|
955
1352
|
border-radius: 12px !important;
|
|
956
1353
|
overflow: hidden;
|
|
957
1354
|
}
|
|
1355
|
+
|
|
1356
|
+
/* 皮肤选择器样式 */
|
|
1357
|
+
.skin-picker {
|
|
1358
|
+
display: flex;
|
|
1359
|
+
flex-direction: column;
|
|
1360
|
+
gap: 12px;
|
|
1361
|
+
padding: 16px 0;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
.skin-item {
|
|
1365
|
+
display: flex;
|
|
1366
|
+
align-items: center;
|
|
1367
|
+
padding: 12px 16px;
|
|
1368
|
+
border: 2px solid var(--le-color-border);
|
|
1369
|
+
border-radius: 8px;
|
|
1370
|
+
cursor: pointer;
|
|
1371
|
+
transition: all 0.2s ease;
|
|
1372
|
+
|
|
1373
|
+
&:hover {
|
|
1374
|
+
border-color: var(--le-color-primary);
|
|
1375
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
&.active {
|
|
1379
|
+
border-color: var(--le-color-primary);
|
|
1380
|
+
background-color: var(--le-color-primary-light-9);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
.skin-color {
|
|
1385
|
+
width: 32px;
|
|
1386
|
+
height: 32px;
|
|
1387
|
+
border-radius: 50%;
|
|
1388
|
+
margin-right: 16px;
|
|
1389
|
+
flex-shrink: 0;
|
|
1390
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
.skin-info {
|
|
1394
|
+
flex: 1;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
.skin-name {
|
|
1398
|
+
font-size: 15px;
|
|
1399
|
+
font-weight: 600;
|
|
1400
|
+
color: var(--le-color-text-strong);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
.skin-desc {
|
|
1404
|
+
font-size: 12px;
|
|
1405
|
+
color: var(--le-color-primary);
|
|
1406
|
+
margin-top: 2px;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
.skin-check {
|
|
1410
|
+
font-size: 20px;
|
|
1411
|
+
color: var(--le-color-primary);
|
|
1412
|
+
}
|
|
1413
|
+
</style>
|
|
1414
|
+
<style lang="scss">
|
|
1415
|
+
/* header 菜单弹出层样式(挂在 body 上,需全局样式)
|
|
1416
|
+
弹出层不受 el-menu prop 控制,需手动设置背景色与 header 一致 */
|
|
1417
|
+
.le-header-menu-popper {
|
|
1418
|
+
background-color: var(--le-color-header-bg) !important;
|
|
1419
|
+
|
|
1420
|
+
.el-menu {
|
|
1421
|
+
border-right: none;
|
|
1422
|
+
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.3);
|
|
1423
|
+
min-width: 160px;
|
|
1424
|
+
background-color: var(--le-color-header-bg) !important;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
.el-menu-item,
|
|
1428
|
+
.el-submenu__title {
|
|
1429
|
+
height: 40px;
|
|
1430
|
+
line-height: 40px;
|
|
1431
|
+
border-bottom: none;
|
|
1432
|
+
padding: 0 20px;
|
|
1433
|
+
min-width: 160px;
|
|
1434
|
+
font-size: 14px;
|
|
1435
|
+
background-color: var(--le-color-header-bg) !important;
|
|
1436
|
+
color: var(--le-color-aside-text) !important;
|
|
1437
|
+
|
|
1438
|
+
&:hover {
|
|
1439
|
+
background-color: rgba(255, 255, 255, 0.1) !important;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
.el-menu-item.is-active {
|
|
1444
|
+
color: var(--le-color-aside-active) !important;
|
|
1445
|
+
background-color: rgba(255, 255, 255, 0.1) !important;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
.el-submenu__icon-arrow {
|
|
1449
|
+
float: right;
|
|
1450
|
+
margin-right: 0;
|
|
1451
|
+
color: var(--le-color-aside-text) !important;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
/* 用户下拉菜单(系统设置/皮肤选择)全局样式 */
|
|
1456
|
+
.el-dropdown-menu {
|
|
1457
|
+
.el-dropdown-menu__item {
|
|
1458
|
+
position: relative;
|
|
1459
|
+
|
|
1460
|
+
&.skin-active {
|
|
1461
|
+
color: var(--le-color-aside-active);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
958
1465
|
</style>
|
package/package.json
CHANGED
package/le-theme/common/var.scss
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
-------------------------- */
|
|
2
|
-
$--all-transition: all .3s cubic-bezier(.645,.045,.355,1) !default;
|
|
3
|
-
$--fade-transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
|
|
4
|
-
$--fade-linear-transition: opacity 200ms linear !default;
|
|
5
|
-
$--md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
|
|
6
|
-
$--border-transition-base: border-color .2s cubic-bezier(.645,.045,.355,1) !default;
|
|
7
|
-
$--color-transition-base: color .2s cubic-bezier(.645,.045,.355,1) !default;
|
|
8
|
-
|
|
9
|
-
/* Color
|
|
10
|
-
-------------------------- */
|
|
11
|
-
/// color|1|Brand Color|0
|
|
12
|
-
$--color-primary: #409EFF !default;
|
|
13
|
-
/// color|1|Background Color|4
|
|
14
|
-
$--color-white: #FFFFFF !default;
|
|
15
|
-
/// color|1|Background Color|4
|
|
16
|
-
$--color-black: #000000 !default;
|
|
17
|
-
$--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */
|