├── .env.development
├── .env.production
├── .gitignore
├── README.md
├── dist
├── assets
│ ├── element-icons.9c88a535.woff
│ ├── element-icons.de5eb258.ttf
│ ├── home.155038bb.svg
│ ├── index.0282d2bf.js
│ ├── index.0282d2bf.js.gz
│ ├── index.1cb7eec7.css
│ ├── index.1cb7eec7.css.gz
│ ├── index.3d6b04f8.css
│ ├── index.3d6b04f8.css.gz
│ ├── index.710eea4b.js
│ ├── index.710eea4b.js.gz
│ ├── index.92421829.css
│ ├── index.92421829.css.gz
│ ├── index.930d31c9.js
│ ├── index.930d31c9.js.gz
│ ├── index.b06ca65d.css
│ ├── index.b06ca65d.css.gz
│ ├── index.cc670981.js
│ ├── index.d41203df.js
│ ├── index.d41203df.js.gz
│ ├── index.e46c323d.js
│ ├── index.efa6b091.css
│ ├── index.efa6b091.css.gz
│ ├── index.ffebf55c.js
│ ├── index.ffebf55c.js.gz
│ ├── login.a35c9f52.svg
│ ├── logo.61ee5eb1.png
│ ├── useTableData.6da28c31.js
│ ├── useTableData.6da28c31.js.gz
│ ├── vendor.f21cd016.js
│ └── vendor.f21cd016.js.gz
├── favicon.ico
└── index.html
├── index.html
├── package-lock.json
├── package.json
├── preview
├── logo.png
├── 个人信息.png
├── 右键菜单.png
├── 搜索.png
├── 更换主题色1.png
├── 标签切换.png
├── 注册.png
├── 用户管理.png
├── 登录.png
├── 移动端界面1.png
├── 移动端菜单.png
├── 移动端设置.png
├── 设置.png
├── 通知.png
├── 面包屑.png
└── 首页.png
├── public
└── favicon.ico
├── src
├── App.vue
├── assets
│ └── images
│ │ ├── arrow-down.svg
│ │ ├── arrow-up.svg
│ │ ├── empty-tips.svg
│ │ ├── home.svg
│ │ ├── login.svg
│ │ ├── logo.png
│ │ └── office-desk.svg
├── components
│ └── CountDownBtn
│ │ └── index.vue
├── hooks
│ ├── useTableData.js
│ └── useWindowSize.js
├── http
│ ├── apis
│ │ ├── role-management.js
│ │ ├── user-management.js
│ │ └── user.js
│ ├── axios.js
│ └── index.js
├── layouts
│ ├── AdminLayout.vue
│ ├── AppHeader
│ │ └── index.vue
│ ├── AppSideBar
│ │ ├── MenuItem.vue
│ │ └── index.vue
│ ├── Breadcrumb
│ │ └── index.vue
│ ├── MobileAppHeader
│ │ └── index.vue
│ ├── MobileAppSideBar
│ │ └── index.vue
│ └── TagsViewSwitcher
│ │ └── index.vue
├── main.js
├── mock
│ ├── apiController
│ │ ├── role-management.js
│ │ ├── user-management.js
│ │ └── user.js
│ ├── index.js
│ └── mock.js
├── others
│ ├── options.js
│ ├── utils.js
│ └── validator.js
├── pages
│ ├── 404
│ │ └── index.vue
│ ├── home
│ │ └── index.vue
│ ├── login
│ │ └── index.vue
│ ├── role-management
│ │ ├── components
│ │ │ └── RoleEdit.vue
│ │ └── index.vue
│ └── user-management
│ │ ├── components
│ │ └── UserEdit.vue
│ │ └── index.vue
├── router
│ └── index.js
├── store
│ ├── index.js
│ └── modules
│ │ ├── app.js
│ │ └── tags-view.js
└── styles
│ ├── animation.scss
│ ├── color.scss
│ ├── custom-default-browser-style.scss
│ ├── element-variables.scss
│ ├── global.scss
│ └── mixin.scss
├── stats.html
└── vite.config.js
/.env.development:
--------------------------------------------------------------------------------
1 | NODE_ENV=development
2 | VITE_SERVER_HOST=""
3 |
4 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | NODE_ENV=production
2 | VITE_SERVER_HOST=""
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist-ssr
4 | *.local
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
6 |
7 |
8 | ## vue3-vite-elementplus-admin
管理系统快速开发模板
9 |
10 | 基于Vue3.0前端开发框架和 ElementPlus UI框架,并使用由Vue 作者尤雨溪的前端工程化工具Vite进行构建。与Webpack相比,Vite极大的缩短了开发打包和热更新的效率。
此模板可用于现代化管理系统的初始开发模板,可节省一些重复性的工程初始化搭建工作。
11 |
12 |
13 | ## 特性
14 |
15 | - 包含简易的登录注册页、首页、404 提示页、用户管理页、角色权限页。
16 | - 封装好的经典管理端布局(包含头部组件、侧边栏菜单组件、面包屑组件、标签切换器组件)。
17 | - 默认使用`axios`请求数据和`mockjs`来模拟数据。
18 | - 封装的公共组件:倒计时按钮组件。
19 | - 使用`nprogress`插件来作为单页面切换的加载指示器。
20 | - 使用`VueRouter`动态生成不同角色菜单的路由权限。
21 | - 基本的兼容性提醒,由于vite和vue3不兼容IE浏览器,因此如检测为IE浏览器则提醒用户切换使用其他浏览器。
22 | - 界面设计简约,可根据自己的实际开发情况增加删除工具库或修改源码。
23 |
24 |
25 | ## 预览
26 | + github地址
27 | > https://github.com/FEZIRO/vue3-vite-elementplus-admin
28 |
29 | + 模板下载地址(template分支)
30 | > https://github.com/FEZIRO/vue3-vite-elementplus-admin/tree/template
31 |
32 | + 在线预览(账号admin,密码admin)
33 | > https://feziro.github.io/vue3-vite-elementplus-admin/
34 |
35 |
36 |
37 |
38 |

39 |
登录
40 |
41 |
42 |

43 |
注册
44 |
45 |
46 |

47 |
首页
48 |
49 |
50 |

51 |
用户管理
52 |
53 |
54 |
55 |

56 |
移动端界面
57 |
58 |
59 |
60 |

61 |
移动端菜单
62 |
63 |
64 |
65 | ## 使用库/工具
66 |
67 | - [Vue 3](https://v3.cn.vuejs.org/)
68 | - [VueRouter 4.x](https://next.router.vuejs.org/zh/)(有兼容`vue3`语法的`hook`写法)
69 | - [Vuex 4.x](https://next.vuex.vuejs.org/zh/index.html) (有兼容`vue3`语法的`hook`写法)
70 | - [Vite 2.x](https://cn.vitejs.dev/)
71 | - [ElementPlus 1.0.2-beta.55](https://element-plus.org/#/zh-CN/component/space)(整个库采用`vue3`语法编写)
72 | - [dayjs](https://github.com/iamkun/dayjs)(一个轻便的时间处理库)
73 | - [axios](http://www.axios-js.com/) (好用的 http 请求库)
74 | - [mockjs](http://mockjs.com/) (模拟请求)
75 | - [reset-css](https://www.npmjs.com/package/reset-css) (重置浏览器默认样式)
76 | - [nprogress](https://ricostacruz.com/nprogress/) (顶部加载指示器)
77 | - vite-plugin-compression(打包压缩 gzip 插件)
78 | - @rollup/plugin-strip (打包去除调试语句插件)
79 | - visualizer (打包文件大小占比分析可视化插件)
80 |
81 |
82 | ## 模板主要结构
83 |
84 | #### vite-vue3-elementplus-admin
85 |
86 | ├─ `public`(public 文件夹)
87 | │ └─ `favicon.ico` 浏览器标签前缀图标
88 | │
89 | ├─ `src`(根目录 src-文件夹)
90 | │ ├─ `assets`(资源-文件夹)
91 | │ │ └─ `images`(图片-文件夹)
92 | │ │ ├─ `arrow-down.svg`
93 | │ │ ├─ `arrow-up.svg`
94 | │ │ ├─ `empty-tips.svg `
95 | │ │ ├─ `home.svg`
96 | │ │ ├─ `login.svg`
97 | │ │ ├─ `logo.png`
98 | │ │ └─ `office-desk.svg`
99 | │ │
100 | │ ├─ `components`(全局公共-文件夹)
101 | │ │ ├─ `CountDownBtn` (倒计时按钮组件)
102 | │ │ │ └─ `index.vue`
103 | │ │
104 | │ ├─ `hooks`(自定义 hooks-文件夹)
105 | │ │ ├─ `useTableData.js`
106 | │ │ └─ `useWindowRect.js`
107 | │ │
108 | │ ├─ `http`(http 请求-文件夹)
109 | │ │ ├─ `apis`(api 管理-文件夹)
110 | │ │ │ ├─ `role-management.js`
111 | │ │ │ ├─ `user-management.js`
112 | │ │ │ └─ `user.js`
113 | │ │ ├─ `axios.js` (axios 配置文件)
114 | │ │ └─ `index.js` (http 请求主入口文件)
115 | │ │
116 | │ ├─ `layouts` (布局文件夹)
117 | │ │ ├─ `AppHeader`(头部组件-文件夹)
118 | │ │ │ └─ `index.vue`
119 | │ │ ├─ `MobileAppHeader`(移动端头部组件-文件夹)
120 | │ │ │ └─ `index.vue`
121 | │ │ ├─ `AppSideBar`(侧边栏菜单组件-文件夹)
122 | │ │ │ ├─ `index.vue`
123 | │ │ ├─ `MobileAppSideBar`(移动端侧边栏菜单组件-文件夹)
124 | │ │ │ ├─ `index.vue`
125 | │ │ │ └─ `MenuItem.vue`
126 | │ │ ├─ `Breadcrumb`(面包屑组件-文件夹)
127 | │ │ │ └─ `index.vue`
128 | │ │ ├─`TagsViewSwitcher`(标签切换组件-文件夹)
129 | │ │ │ └─ `index.vue`
130 | │ │ └─ `AdminLayout.vue` (管理端页面布局组件)
131 | │ │
132 | │ ├─ `mock`(mock 模拟数据-文件夹)
133 | │ │ ├─ `apiController`(controller-文件夹)
134 | │ │ │ ├─ `role-management.js`
135 | │ │ │ ├─ `user-management.js`
136 | │ │ │ └─ `user.js`
137 | │ │ ├─ `index.js` (mock 主入口文件)
138 | │ │ └─ `mock.js` (mock 配置文件)
139 | │ │
140 | │ ├─ `pages`
141 | │ │ ├─ `404`(404 页面-文件夹)
142 | │ │ │ └─ `index.vue`
143 | │ │ ├─ `home`(首页-文件夹)
144 | │ │ │ └─ `index.vue `
145 | │ │ ├─ `login` (登录注册页面-文件夹)
146 | │ │ │ └─ `index.vue`
147 | │ │ ├─ `role-management`(角色管理页-文件夹)
148 | │ │ │ ├─ `components`(页面子组件-文件夹)
149 | │ │ │ │ └─ `RoleEdit.vue`
150 | │ │ │ └─ `index.vue`
151 | │ │ └─` user-management`(用户管理页-文件夹)
152 | │ │ ├─ `components` (页面子组件-文件夹)
153 | │ │ │ └─ `UserEdit.vue`
154 | │ │ └─ `index.vue`
155 | │ │
156 | │ ├─ `router`(路由文件夹)
157 | │ │ └─ `index.js` vueRouter 主入口
158 | │ │
159 | │ ├─ `store`(文件夹)
160 | │ │ ├─ `modules`(store 模块文件夹)
161 | │ │ │ ├─ `app.js` (app 配置模块)
162 | │ │ │ ├─ `options.js` (全局下拉值选项模块)
163 | │ │ │ └─ `tags-view.js` (页面标签切换模块)
164 | │ │ └─ `index.js` (store 主入口)
165 | │ │
166 | │ ├─ `styles`(全局样式-文件夹)
167 | │ │ ├─ `animation.scss`(css 动画样式表)
168 | │ │ ├─ `color.scss`(颜色变量样式表)
169 | │ │ ├─ `custom-default-browser-style.scss` (自定义浏览器默认样式表)
170 | │ │ ├─ `elementui-variables.scss`(自定义 elementui 默认主题和样式表)
171 | │ │ ├─ `global.scss`(全局样式表)
172 | │ │ └─ `mixin.scss`(scss 的 mixin 样式表)
173 | │ │
174 | │ ├─ `others`(其他-文件夹)
175 | │ │ ├─ `utils.js` (工具函数文件)
176 | │ │ ├─ `options.js` (下拉值文件)
177 | │ │ └─ `validator.js` (值校验函数文件)
178 | │ │
179 | │ ├─ `App.vue` (项目主组件)
180 | │ └─ `main.js` (项目主入口)
181 | │
182 | ├─ `index.html ` (页面挂载文件)
183 | ├─ `package.json` (npm 配置文件)
184 | ├─ `README.md` (项目文档)
185 | └─ `vite.config.js` (vite 配置文件)
186 |
187 |
188 | ## 关键点介绍
189 |
190 | - ##### 自定义 ElementUI 组件样式
191 | (修改主题方法可能会随着element版本而变化,此方法适用于<=ElementPlus1.0.2-beta.55版本)
192 | 本模板已修改 elementUI 的默认主题色和部分表单组件样式,详细请参考`style`文件夹下的`elementui-variables.scss`样式文件。。
193 |
194 | ```
195 | //修改 $--color-primary 变量可更换element主题色
196 | $--color-primary: #80cbc4;
197 | ```
198 |
199 |

200 |
主题色1
201 |
202 |
203 |

204 |
主题色2
205 |
206 |
207 | - ##### 自定义 ElementUI 语言
208 | (修改语言方法可能会随着element版本而变化,此方法适用于<=ElementPlus1.0.2-beta.55版本)
209 | ```
210 | // main.js
211 | import locale from "element-plus/lib/locale/lang/zh-cn";
212 | app.use(ElementPlus, { locale });
213 | ```
214 |
215 | - ##### 重置浏览器默认样式
216 |
217 | 请参考`style`文件夹下的`custom-default-browser-style.scss`文件,本文件修改浏览器默认滚动条样式。
218 | 更多的浏览器样式重置则使用了 npm 插件`reset-css`,在项目主入口文件`main.js`中引入。
219 |
220 |
221 | - ##### useTableData
222 |
223 | 自定义 hook,用于封装表格查询的可复用逻辑和操作,在需要表格查询操作的页面或组件中引入即可。详细请查看`hooks`文件夹下的`useTableData.js`文件。
224 |
225 | ```
226 | //使用举例
227 | import useTableData from "@/hooks/useTableData.js";
228 |
229 | //传入以Promise封装的http请求api
230 | const table = useTableData(fetchTableDataApi);
231 |
232 | //设置参数,发起请求
233 | table.setParams({
234 | keyword: keyword,
235 | ...
236 | });
237 |
238 | //重置请求
239 | table.reset();
240 |
241 | //刷新请求
242 | table.refresh()
243 |
244 | //表格请求参数(默认字段 currentPage,pageSize )
245 | console.log(table.tableParams);
246 |
247 | //表格分页器分页组配置(默认值 [20, 50, 100] )
248 | console.log(table.PAGE_SIZES);
249 |
250 | //表格数据(默认字段 list,totalCount,isLoading )
251 | console.log(table.tableData);
252 | ```
253 |
254 | - ##### useWindowSize 与动态表格高度
255 |
256 | 当固定表头时需要限制 elementUI 表格高度,数据则会表现为溢出滚动,如页面缩放的时候需要占满屏幕,则可监测浏览器高度`clientHeight`动态赋值表格高度,以达到缩放浏览器窗口表格大小不变的效果。
257 | `useWindowRect`封装了监听浏览器宽高的可复用逻辑,此 hook 将动态的`clientHeight`和`clientWidth`值设置存储到 vuex 的`store/modules/app.js`文件内的`windowRect`字段,因此每个页面可引入动态的宽高值来设置表格高度。
258 |
259 | - ##### 菜单权限和动态路由
260 | 如管理系统的权限是根据菜单管理页面和权限配置页面来设置的并且通过后端返回菜单区分账号权限,则前端可在登录时根据返回的菜单渲染侧边栏菜单和使用`VueRouter`的`router.addRoute`API 来动态添加路由。
261 | 动态添加路由会在浏览器刷新时丢失,可把菜单用`localStorage`缓存,每次刷新浏览器都动态添加一次路由即可解决。
262 | 退出登录时需要清除动态添加的路由,可使用 resetRouter 进行重置路由。
263 | ```
264 | function resetRouter() {
265 | const newRouter = createRouter({
266 | routes: constantRoute,
267 | history: createWebHashHistory(),
268 | });
269 | router.matcher = newRouter.matcher;
270 | }
271 | ```
272 |
273 | - ##### 菜单检索
274 |
275 | 根据后端返回的菜单结构的页面名称进行递归检索。
276 |
277 |
278 |

279 |
检索菜单
280 |
281 |
282 | - ##### 面包屑
283 |
284 | 最简单的处理方式就是根据后端返回的菜单设置个`breadcrumb`数组字段处理即可,如有其它需求可根据实际数据情况而定。
285 |
286 | ```
287 | {
288 | id: "1",
289 | name: "系统管理",
290 | icon: "el-icon-setting",
291 | url: "/system",
292 | breadcrumb: ["系统管理"],
293 | type: "group",
294 | children: [{
295 | id: "1-1",
296 | name: "用户管理",
297 | icon: "",
298 | type: "page",
299 | url: "/system/user-management",
300 | children: null,
301 | breadcrumb: ["系统管理", "用户管理"],
302 | }]
303 | }
304 | ```
305 |
306 |
307 |

308 |
面包屑
309 |
310 |
311 | - ##### 移动端适配
312 |
313 | 使用 CSS 媒体查询、Javascript的`clientWidth`客户端宽度检测来区分移动端或桌面端。管理端类型界面不建议在小屏设备进行操作。
314 |
全局设备类型字段(`/store/modules/app`中的`device`字段):
315 |
316 | - desktop:大于 750px(大屏设备)
317 | - mobile:小于 750px(小屏设备)
318 |
319 | 如有更多断点检测需求可自行在`app.vue`中设置。
320 |
321 | - ##### 系统设置
322 | 可配置化或用户自行选择。
323 |
324 | - 页面指示:
325 | 可选`标签切换`/`面包屑`
326 |
327 | - 页面状态缓存:
328 | 当页面指示为`标签切换`时可选页面切换时是否缓存页面状态,使用``组件缓存。
329 |
330 | - 菜单栏折叠。
331 |
332 |
333 |
334 |

335 |
系统设置
336 |
337 |
338 | - ##### 右键自定义菜单与Teleport
339 | 当指示器为`标签切换`时,标签切换栏区域点击右键出现自定义菜单,可删除全部已打开的标签;
340 | 自定义右键菜单采用Vue3新特性`teleport`的方式加载到`body`元素里面,防止父元素定位干扰。
341 |
342 | ```
343 | 右键自定义菜单
344 |
345 |
348 |
349 | ```
350 |
351 |
352 |

353 |
系统设置
354 |
355 |
356 |
357 | - ##### http请求与axios全局请求配置
358 | 全局请求配置文件在`/src/http/axios.js`下配置;请求接口按页面模块进行分类,页面名称对应api接口文件名称,存放在`/src/http/apis/`文件夹下,再通过`/src/http/index.js`文件进行统一导出。
359 | ```
360 | // http接口请求使用
361 | import http from "@/http";
362 | http.[页面模块名称].xxx()
363 |
364 | 如 http.roleManagement.tableList()
365 | ```
366 |
367 |
368 | - ##### Mockjs模拟数据
369 | 所有数据均来自本地mock模拟数据,位于`/src/mock`文件夹下。
370 | `/src/mock/mock.js`为mock全局配置
371 | `/src/mock/apiController`文件夹为所有模拟接口请求处理
372 |
373 | 在`/src/main.js`下引入mock文件即可使用接口,如`import "./mock/index";`
374 |
375 | - ##### 环境切换
376 | 根据项目的根目录下的`.env.xxx.`文件区分环境,以`VITE_`开头的字段可在`import.meta.env`中获取。如有更多需求可自行添加。
377 | + `.env.development`开发环境
378 | + `.env.production`正式环境
379 |
380 | - ##### Vite基本命令
381 | ```
382 | // package.json
383 | "scripts": {
384 | "dev": "vite", //开启热更新服务器
385 | "build": "vite build", //打包项目
386 | "preview/dist": "vite preview" //本地预览打包的项目
387 | }
388 | ```
389 |
390 | - ##### Vite基本配置
391 | ```
392 | // vite.config.js配置文件
393 |
394 | import vue from "@vitejs/plugin-vue";
395 | import { visualizer } from "rollup-plugin-visualizer";
396 | import strip from "@rollup/plugin-strip";
397 | import viteCompression from "vite-plugin-compression";
398 | const path = require("path");
399 |
400 | export default {
401 | base: './',
402 | plugins: [
403 | vue(),
404 | //正式环境打包查看各文件大小占比
405 | visualizer({
406 | open: true,
407 | gzipSize: true,
408 | brotliSize: true,
409 | }),
410 | //正式环境打包去除调试语句
411 | {
412 | ...strip({
413 | include: ["**/*.js", "**/*.vue", "**/*.ts", "**/*.jsx"],
414 | }),
415 | apply: "build",
416 | },
417 | //打包开启gzip压缩
418 | viteCompression(),
419 | ],
420 | resolve: {
421 | alias: {
422 | // 键必须以斜线开始和结束
423 | "@": path.resolve(__dirname, "./src"),
424 | },
425 | },
426 | css: {
427 | preprocessorOptions: {
428 | scss: {
429 | //添加scss全局变量样式
430 | additionalData: "@import './src/styles/global.scss';",
431 | },
432 | },
433 | },
434 | server: {
435 | // 配置调试服务器主机名,如果允许外部访问,可设置为"0.0.0.0"
436 | host: "0.0.0.0",
437 | port: 3000, // 服务器端口号
438 | open: true, // 是否自动打开浏览器
439 | },
440 | };
441 | ```
442 |
443 | ## 注意
444 |
445 | - Vite 工具打包不兼容任何版本的 IE 浏览器(和 Webpack 打包机制不同)。
446 | - Vue 3 不兼容IE浏览器。
447 | - 本模板的ElementPlus使用的是1.0.2-beta.55测试版,由于是测试版缘故,功能设置随时会变,故当更新到某个新版本时某些设置可能会不正确不生效,一切以Element官方文档为准!
448 |
449 |
450 | By FEZIRO
451 |
--------------------------------------------------------------------------------
/dist/assets/element-icons.9c88a535.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/element-icons.9c88a535.woff
--------------------------------------------------------------------------------
/dist/assets/element-icons.de5eb258.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/element-icons.de5eb258.ttf
--------------------------------------------------------------------------------
/dist/assets/index.0282d2bf.js:
--------------------------------------------------------------------------------
1 | var e=Object.defineProperty,a=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,t=Object.prototype.propertyIsEnumerable,o=(a,l,t)=>l in a?e(a,l,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[l]=t;import{r as n,b as i,e as r,f as d,h as s,g as m,m as c,A as p,u,w as f,o as b,v as g,c as h,C,D as y,E as k,G as w,J as v,z}from"./vendor.f21cd016.js";import{h as _}from"./index.ffebf55c.js";import{u as T}from"./useTableData.6da28c31.js";import"./index.710eea4b.js";const M={name:"RoleTableEdit",emits:["confirm"],setup(e,i){const r=n({type:"",title:"",visible:!1,confirmBtnLoading:!1}),d=n({form:{name:"",remark:"",permission:""}}),s=()=>{r.visible=!1,d.form={name:"",remark:"",permission:""}};return{modalConfig:r,data:d,menuTree:[{id:2,label:"系统管理",children:[{id:5,label:"用户管理"},{id:6,label:"角色管理"}]},{id:3,label:"数据统计"}],menuTreeDefaultProps:{children:"children",label:"label"},hideModal:s,showModal:(e={type:"add",title:"",data:{}})=>{r.type=e.type,r.visible=!0,"add"===e.type&&(r.title="添加"),"edit"===e.type&&(r.title="编辑"),r.title=e.title||"",e.data&&(d.form=((e,n)=>{for(var i in n||(n={}))l.call(n,i)&&o(e,i,n[i]);if(a)for(var i of a(n))t.call(n,i)&&o(e,i,n[i]);return e})({},e.data))},onModalConfirmClick:()=>{s(),i.emit("confirm")},onModalCloseClick:()=>{s()}}}},S={key:0,class:"edit-page"},P={class:"confirm-btn-group"},x=p("关闭"),E=p("保存");M.render=function(e,a,l,t,o,n){const p=i("el-page-header"),u=i("el-input"),f=i("el-form-item"),b=i("el-col"),g=i("el-tree"),h=i("el-button"),C=i("el-row"),y=i("el-form");return t.modalConfig.visible?(r(),d("div",S,[s(p,{onBack:t.onModalCloseClick,content:"添加角色"},null,8,["onBack"]),s(y,{size:"medium",model:t.data.form,"label-position":"top",style:{width:"50%",margin:"0 auto","margin-top":"20px"}},{default:m((()=>[s(C,{gutter:20},{default:m((()=>[s(b,{span:24},{default:m((()=>[s(f,{label:"角色名称:"},{default:m((()=>[s(u,{size:"medium",modelValue:t.data.form.name,"onUpdate:modelValue":a[1]||(a[1]=e=>t.data.form.name=e),placeholder:"请输入",clearable:"",style:{width:"100%"}},null,8,["modelValue"])])),_:1})])),_:1}),s(b,{span:24},{default:m((()=>[s(f,{label:"备注:"},{default:m((()=>[s(u,{type:"textarea",size:"medium",modelValue:t.data.form.reamrk,"onUpdate:modelValue":a[2]||(a[2]=e=>t.data.form.reamrk=e),placeholder:"请输入",clearable:"",style:{width:"100%"}},null,8,["modelValue"])])),_:1})])),_:1}),s(b,{span:24},{default:m((()=>[s(f,{label:"页面权限:"},{default:m((()=>[s(g,{data:t.menuTree,"show-checkbox":"","node-key":"id",props:t.menuTreeDefaultProps},null,8,["data","props"])])),_:1})])),_:1}),s(b,{span:24},{default:m((()=>[s("div",P,[s(h,{size:"medium",type:"danger",onClick:t.onModalCloseClick},{default:m((()=>[x])),_:1},8,["onClick"]),s(h,{size:"medium",type:"primary",onClick:t.onModalConfirmClick},{default:m((()=>[E])),_:1},8,["onClick"])])])),_:1})])),_:1})])),_:1},8,["model"])])):c("",!0)};const D={name:"role-management",components:{RoleEdit:M},setup(){const e=T(_.roleManagement.list),a=u();let l=n({keyword:""});f((()=>l.keyword),(()=>{e.setParams({keyword:l.keyword})}));b((()=>{e.setParams({keyword:l.keyword})}));const t=g([]),o=g(null);return{roleManagementTable:e,filterForm:l,tableSelection:t,roleEdit:o,onTableAddClick:()=>{o.value.showModal({type:"add",title:"添加"})},onTableEditClick:e=>{o.value.showModal({type:"edit",title:"添加",data:{id:e.id,name:e.roleName,remark:e.remark}})},onTableDeleteClick:()=>{},onTableSelectionChange:e=>{t.value=e},onSizeChange:a=>{e.setParams({pageSize:a})},onCurrentChange:a=>{e.setParams({currentPage:a})},windowRect:h((()=>a.state.app.windowRect)),device:h((()=>a.state.app.device))}}},V=z();C("data-v-6275459d");const j={class:"role-management-page","element-loading-text":"拼命加载中"},O=p("添加"),R=p("删除"),A=p("编辑"),B={class:"table-pager"};y();const F=V(((e,a,l,t,o,n)=>{const m=i("el-input"),c=i("el-button"),p=i("el-space"),u=i("el-table-column"),f=i("el-empty"),b=i("el-table"),g=i("el-pagination"),h=i("RoleEdit"),C=k("loading");return w((r(),d("div",j,[s(p,{wrap:"",style:{width:"100%"}},{default:V((()=>[s(m,{modelValue:t.filterForm.keyword,"onUpdate:modelValue":a[1]||(a[1]=e=>t.filterForm.keyword=e),class:"search-input","prefix-icon":"el-icon-search",size:"small",placeholder:"角色名称"},null,8,["modelValue"]),s(c,{type:"primary",size:"small",icon:"el-icon-circle-plus-outline",onClick:t.onTableAddClick},{default:V((()=>[O])),_:1},8,["onClick"]),s(c,{type:"danger",size:"small",icon:"el-icon-delete",disabled:0==t.tableSelection.length,onClick:t.onTableDeleteClick},{default:V((()=>[R])),_:1},8,["disabled","onClick"])])),_:1}),s(b,{data:t.roleManagementTable.tableData.list,"tooltip-effect":"dark",height:t.windowRect.clientHeight-250,"row-style":{height:"65px"},onSelectionChange:t.onTableSelectionChange},{empty:V((()=>[s(f,{description:"暂无数据"})])),default:V((()=>[s(u,{type:"selection",width:"50","show-overflow-tooltip":"","header-align":"center",align:"center"}),s(u,{prop:"roleName",label:"角色名称","min-width":"120","show-overflow-tooltip":""}),s(u,{prop:"remark",label:"备注","show-overflow-tooltip":""}),s(u,{label:"操作","show-overflow-tooltip":"","min-width":"95",fixed:"right"},{default:V((e=>[s(c,{type:"warning",plain:"",size:"small",onClick:a=>t.onTableEditClick(e.row)},{default:V((()=>[A])),_:2},1032,["onClick"])])),_:1})])),_:1},8,["data","height","onSelectionChange"]),s("div",B,[s(g,{onSizeChange:t.onSizeChange,onCurrentChange:t.onCurrentChange,"page-size":t.roleManagementTable.tableParams.pageSize,"page-sizes":t.roleManagementTable.PAGE_SIZES,layout:"total,prev,pager,next,"+("mobile"===t.device?"":"sizes"),total:t.roleManagementTable.tableData.totalCount,small:"mobile"===t.device},null,8,["onSizeChange","onCurrentChange","page-size","page-sizes","layout","total","small"])]),s(v,{name:"fade"},{default:V((()=>[s(h,{ref:"roleEdit"},null,512)])),_:1})],512)),[[C,t.roleManagementTable.tableData.isPageLoading]])}));D.render=F,D.__scopeId="data-v-6275459d";export{D as default};
2 |
--------------------------------------------------------------------------------
/dist/assets/index.0282d2bf.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/index.0282d2bf.js.gz
--------------------------------------------------------------------------------
/dist/assets/index.1cb7eec7.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";*{scrollbar-color:rgba(0,0,0,0.3) transparent;scrollbar-width:thin}:focus{outline:0}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background-color:rgba(153,153,153,.5);background-clip:padding-box;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;transition:all 1s;cursor:pointer}::-webkit-scrollbar-thumb:hover{background-color:#999}.fade-enter-active,.fade-leave-active{transition:all .3s ease}.fade-enter-from,.fade-leave-to{opacity:0;transform:translateY(-1000px)}@keyframes slideUp{0%{opacity:0;transform:translateY(60px)}100%{opacity:1;transform:translateY(0)}}@keyframes upAndDown{0%{transform:translateY(0)}50%{transform:translateY(10px)}100%{transform:translateY(0)}}@keyframes sideBarSlipIn{0%{transform:translateX(0)}100%{transform:translateX(-500px)}}@keyframes sideBarSlipOut{0%{transform:translateX(-500px)}100%{transform:translateX(0)}}#nprogress .bar{background:#36ad6a;height:3px}body{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#000;width:100%;height:100%}.login-page{width:100%;height:100vh;background:#f6f7f9;display:flex;justify-content:center;align-items:center}.login-page .panel-container{position:relative;display:flex;justify-content:center;align-items:center;width:auto}.login-page .panel-container .login-intro{width:500px;height:500px;flex-shrink:0;background-color:#36ad6a;padding:50px;color:#fff!important;box-sizing:border-box;padding-right:60px;border-top-left-radius:6px;border-bottom-left-radius:6px}@media screen and (max-width:750px){.login-page .panel-container .login-intro{display:none}}.login-page .panel-container .login-intro>img{width:100%;height:100%}.login-page .panel-container .login-intro .intro-subtitle,.login-page .panel-container .login-intro .intro-title{text-align:left}.login-page .panel-container .login-intro .intro-title{font-weight:700;font-size:22px}.login-page .panel-container .login-intro .intro-subtitle{font-size:14px;margin-top:10px}.login-page .panel-container .login-panel{width:400px;min-height:500px;padding:50px;background-color:#fff;box-sizing:border-box;transition:all .5s;flex-shrink:0;border-top-right-radius:6px;border-bottom-right-radius:6px}@media screen and (max-width:750px){.login-page .panel-container .login-panel{width:90vw;padding:50px 30px}}.login-page .panel-container .login-panel h1{text-align:center;font-size:24px;width:100%;margin:0 auto}.login-page .panel-container .login-panel .login-form{margin-top:40px}.login-page .panel-container .login-panel .login-form .login-btn{width:100%;height:40px;margin-top:30px}.login-page .panel-container .login-panel .login-form .form-bottom{margin-top:10px;display:flex;justify-content:space-between;align-items:center}.login-page .panel-container .code-by{position:absolute;bottom:-50px;left:50%;transform:translateX(-50%);color:#ccc}
--------------------------------------------------------------------------------
/dist/assets/index.1cb7eec7.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/index.1cb7eec7.css.gz
--------------------------------------------------------------------------------
/dist/assets/index.3d6b04f8.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";*{scrollbar-color:rgba(0,0,0,0.3) transparent;scrollbar-width:thin}:focus{outline:0}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background-color:rgba(153,153,153,.5);background-clip:padding-box;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;transition:all 1s;cursor:pointer}::-webkit-scrollbar-thumb:hover{background-color:#999}.fade-enter-active,.fade-leave-active{transition:all .3s ease}.fade-enter-from,.fade-leave-to{opacity:0;transform:translateY(-1000px)}@keyframes slideUp{0%{opacity:0;transform:translateY(60px)}100%{opacity:1;transform:translateY(0)}}@keyframes upAndDown{0%{transform:translateY(0)}50%{transform:translateY(10px)}100%{transform:translateY(0)}}@keyframes sideBarSlipIn{0%{transform:translateX(0)}100%{transform:translateX(-500px)}}@keyframes sideBarSlipOut{0%{transform:translateX(-500px)}100%{transform:translateX(0)}}#nprogress .bar{background:#36ad6a;height:3px}body{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#000;width:100%;height:100%}.edit-page{background:#fff;position:absolute;left:0;right:0;bottom:0;top:0;z-index:10;overflow:auto;box-sizing:border-box;padding:20px}.edit-page .confirm-btn-group{margin-top:100px;text-align:right}.edit-page .el-form-item__label{width:100%;font-weight:700}[data-v-6275459d]{scrollbar-color:rgba(0,0,0,0.3) transparent;scrollbar-width:thin}[data-v-6275459d]:focus{outline:0}[data-v-6275459d]::-webkit-scrollbar{width:8px;height:8px}[data-v-6275459d]::-webkit-scrollbar-track{background:0 0}[data-v-6275459d]::-webkit-scrollbar-thumb{background-color:rgba(153,153,153,.5);background-clip:padding-box;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;transition:all 1s;cursor:pointer}[data-v-6275459d]::-webkit-scrollbar-thumb:hover{background-color:#999}.fade-enter-active[data-v-6275459d],.fade-leave-active[data-v-6275459d]{transition:all .3s ease}.fade-enter-from[data-v-6275459d],.fade-leave-to[data-v-6275459d]{opacity:0;transform:translateY(-1000px)}@keyframes slideUp-6275459d{0%{opacity:0;transform:translateY(60px)}100%{opacity:1;transform:translateY(0)}}@keyframes upAndDown-6275459d{0%{transform:translateY(0)}50%{transform:translateY(10px)}100%{transform:translateY(0)}}@keyframes sideBarSlipIn-6275459d{0%{transform:translateX(0)}100%{transform:translateX(-500px)}}@keyframes sideBarSlipOut-6275459d{0%{transform:translateX(-500px)}100%{transform:translateX(0)}}#nprogress .bar[data-v-6275459d]{background:#36ad6a;height:3px}body[data-v-6275459d]{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#000;width:100%;height:100%}.role-management-page[data-v-6275459d]{width:100%;box-sizing:border-box;padding:10px;background:#fff;overflow:auto;position:relative}.role-management-page .table-pager[data-v-6275459d]{width:100%;display:flex;justify-content:center;align-items:center;padding:20px 0;box-sizing:border-box}
--------------------------------------------------------------------------------
/dist/assets/index.3d6b04f8.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/index.3d6b04f8.css.gz
--------------------------------------------------------------------------------
/dist/assets/index.710eea4b.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/index.710eea4b.js.gz
--------------------------------------------------------------------------------
/dist/assets/index.92421829.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/index.92421829.css.gz
--------------------------------------------------------------------------------
/dist/assets/index.930d31c9.js:
--------------------------------------------------------------------------------
1 | import{d as e,v as l,a as o,b as a,e as t,f as n,g as s,A as i,y as u,q as r,u as d,r as c,c as m,I as p,h as g,G as f,L as C,m as v}from"./vendor.f21cd016.js";import{h as b}from"./index.ffebf55c.js";import{c as y}from"./index.710eea4b.js";const h=e({name:"CountDownBtn",props:{defaultTime:{type:Number,default:10},defaultText:{type:String,default:"获取验证码"}},setup(e,a){let t=l(0);t.value=e.defaultTime;let n=l(!1),s=l("");s.value=e.defaultText;let i=null;return o((()=>{i&&clearInterval(i)})),{onGetCodeClick:()=>{a.emit("getCode"),n.value=!0,s.value=t.value+"s后获取",i=setInterval((()=>{--t.value,s.value=t.value+"s后获取",0==t.value&&(s.value=e.defaultText,t.value=e.defaultTime,n.value=!1,clearInterval(i))}),1e3)},waitingCodeTips:s,disableBtn:n}}});h.render=function(e,l,o,r,d,c){const m=a("el-button");return t(),n(m,{type:"primary",disabled:e.disableBtn,onClick:e.onGetCodeClick},{default:s((()=>[i(u(e.waitingCodeTips),1)])),_:1},8,["disabled","onClick"])};const k={name:"login",components:{CountDownBtn:h},setup(){const e=r(),o=d(),a=l(!0),t=c({account:"",password:""});let n=l(!1);const s=c({phone:"",password:"",code:""});return{loginForm:t,loginImg:"./assets/login.a35c9f52.svg",loginStatus:a,loginBtnLoading:n,registerForm:s,onRegisterClick:()=>{},onLoginClick:async()=>{n.value=!0;let l=await b.user.login({username:t.account,password:t.password}).catch((()=>{n.value=!1}));n.value=!1,l?(l.menu.unshift({name:"首页",icon:"el-icon-s-home",type:"page",url:"/home",children:null,path:[],tagsViewAffix:!0,breadcrumb:["首页"]}),localStorage.setItem("userInfo",JSON.stringify(l.userInfo)),localStorage.setItem("menu",JSON.stringify(l.menu)),y(l.menu),p.success("登录成功!"),e.push("/home")):p.error("登录失败!")},onGetCodeClick:()=>{},onTogglePanelStatus:()=>{a.value=!a.value},appName:m((()=>o.state.app.appName)),device:m((()=>o.state.app.device))}}},w={class:"login-page"},_={class:"panel-container"},V={class:"login-intro"},F={class:"intro-title"},S=g("p",{class:"intro-subtitle"},"欢迎登录!",-1),x={class:"login-panel"},T={key:0,style:{"margin-bottom":"10px"}},I=i(" 登录 "),B={class:"form-bottom"},G=i(" 注册 "),N=i(" 第三方登录 "),L=i(" 注册 "),U={class:"form-bottom"},j=i(" 密码登录 "),D=i(" 第三方登录 "),O=g("span",{class:"code-by"},"Code By FEZIRO",-1);k.render=function(e,l,o,i,r,d){const c=a("el-input"),m=a("el-form-item"),p=a("el-button"),b=a("el-form"),y=a("CountDownBtn"),h=a("el-space");return t(),n("div",w,[g("div",_,[f(g("section",V,[g("h1",F,u(i.appName),1),S,g("img",{src:i.loginImg,alt:"login"},null,8,["src"])],512),[[C,i.loginStatus]]),g("section",x,["mobile"==i.device?(t(),n("h1",T,u(i.appName),1)):v("",!0),g("h1",null,u(i.loginStatus?"登录":"注册"),1),f(g(b,{model:i.loginForm,class:"login-form","label-position":"top"},{default:s((()=>[g(m,{label:"账号"},{default:s((()=>[g(c,{placeholder:"请输入账号",modelValue:i.loginForm.account,"onUpdate:modelValue":l[1]||(l[1]=e=>i.loginForm.account=e)},null,8,["modelValue"])])),_:1}),g(m,{label:"密码"},{default:s((()=>[g(c,{type:"password",placeholder:"请输入密码",modelValue:i.loginForm.password,"onUpdate:modelValue":l[2]||(l[2]=e=>i.loginForm.password=e)},null,8,["modelValue"])])),_:1}),g(m,null,{default:s((()=>[g(p,{type:"primary",class:"login-btn",onClick:i.onLoginClick,loading:i.loginBtnLoading},{default:s((()=>[I])),_:1},8,["onClick","loading"]),g("div",B,[g(p,{type:"text",onClick:i.onTogglePanelStatus},{default:s((()=>[G])),_:1},8,["onClick"]),g(p,{type:"text"},{default:s((()=>[N])),_:1})])])),_:1})])),_:1},8,["model"]),[[C,i.loginStatus]]),f(g(b,{model:i.registerForm,class:"login-form","label-position":"top"},{default:s((()=>[g(m,{label:"手机号码",name:"phone"},{default:s((()=>[g(c,{placeholder:"请输入手机号码",modelValue:i.registerForm.phone,"onUpdate:modelValue":l[3]||(l[3]=e=>i.registerForm.phone=e)},null,8,["modelValue"])])),_:1}),g(m,{label:"密码"},{default:s((()=>[g(c,{type:"password",placeholder:"请设置密码",modelValue:i.registerForm.password,"onUpdate:modelValue":l[4]||(l[4]=e=>i.registerForm.password=e)},null,8,["modelValue"])])),_:1}),g(m,{label:"验证码"},{default:s((()=>[g(h,null,{default:s((()=>[g(c,{placeholder:"请输入验证码",modelValue:i.registerForm.code,"onUpdate:modelValue":l[5]||(l[5]=e=>i.registerForm.code=e)},null,8,["modelValue"]),g(y,{defaultTime:4,onGetCode:i.onGetCodeClick},null,8,["onGetCode"])])),_:1})])),_:1}),g(m,null,{default:s((()=>[g(p,{type:"primary",class:"login-btn",onClick:i.onRegisterClick},{default:s((()=>[L])),_:1},8,["onClick"]),g("div",U,[g(p,{type:"text",onClick:i.onTogglePanelStatus},{default:s((()=>[j])),_:1},8,["onClick"]),g(p,{type:"text"},{default:s((()=>[D])),_:1})])])),_:1})])),_:1},8,["model"]),[[C,!i.loginStatus]])]),O])])};export{k as default};
2 |
--------------------------------------------------------------------------------
/dist/assets/index.930d31c9.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/index.930d31c9.js.gz
--------------------------------------------------------------------------------
/dist/assets/index.b06ca65d.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";[data-v-4898c2bf]{scrollbar-color:rgba(0,0,0,0.3) transparent;scrollbar-width:thin}[data-v-4898c2bf]:focus{outline:0}[data-v-4898c2bf]::-webkit-scrollbar{width:8px;height:8px}[data-v-4898c2bf]::-webkit-scrollbar-track{background:0 0}[data-v-4898c2bf]::-webkit-scrollbar-thumb{background-color:rgba(153,153,153,.5);background-clip:padding-box;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;transition:all 1s;cursor:pointer}[data-v-4898c2bf]::-webkit-scrollbar-thumb:hover{background-color:#999}.fade-enter-active[data-v-4898c2bf],.fade-leave-active[data-v-4898c2bf]{transition:all .3s ease}.fade-enter-from[data-v-4898c2bf],.fade-leave-to[data-v-4898c2bf]{opacity:0;transform:translateY(-1000px)}@keyframes slideUp-4898c2bf{0%{opacity:0;transform:translateY(60px)}100%{opacity:1;transform:translateY(0)}}@keyframes upAndDown-4898c2bf{0%{transform:translateY(0)}50%{transform:translateY(10px)}100%{transform:translateY(0)}}@keyframes sideBarSlipIn-4898c2bf{0%{transform:translateX(0)}100%{transform:translateX(-500px)}}@keyframes sideBarSlipOut-4898c2bf{0%{transform:translateX(-500px)}100%{transform:translateX(0)}}#nprogress .bar[data-v-4898c2bf]{background:#36ad6a;height:3px}body[data-v-4898c2bf]{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#000;width:100%;height:100%}.avatar-uploader[data-v-4898c2bf]{margin-bottom:30px;text-align:center}.avatar-uploader .avatar-upload-wrap[data-v-4898c2bf]{position:relative;height:100px;border-radius:100%;overflow:hidden}.avatar-uploader .avatar-upload-wrap .uploader-bottom[data-v-4898c2bf]{width:100%;padding:4px 0;background:rgba(0,0,0,.4);position:absolute;z-index:99;bottom:0;left:50%;transform:translateX(-50%);text-align:center}.avatar-uploader .avatar-upload-wrap .uploader-bottom .icon-upload[data-v-4898c2bf]{font-size:16px;color:#fff}[data-v-0e832da3]{scrollbar-color:rgba(0,0,0,0.3) transparent;scrollbar-width:thin}[data-v-0e832da3]:focus{outline:0}[data-v-0e832da3]::-webkit-scrollbar{width:8px;height:8px}[data-v-0e832da3]::-webkit-scrollbar-track{background:0 0}[data-v-0e832da3]::-webkit-scrollbar-thumb{background-color:rgba(153,153,153,.5);background-clip:padding-box;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;transition:all 1s;cursor:pointer}[data-v-0e832da3]::-webkit-scrollbar-thumb:hover{background-color:#999}.fade-enter-active[data-v-0e832da3],.fade-leave-active[data-v-0e832da3]{transition:all .3s ease}.fade-enter-from[data-v-0e832da3],.fade-leave-to[data-v-0e832da3]{opacity:0;transform:translateY(-1000px)}@keyframes slideUp-0e832da3{0%{opacity:0;transform:translateY(60px)}100%{opacity:1;transform:translateY(0)}}@keyframes upAndDown-0e832da3{0%{transform:translateY(0)}50%{transform:translateY(10px)}100%{transform:translateY(0)}}@keyframes sideBarSlipIn-0e832da3{0%{transform:translateX(0)}100%{transform:translateX(-500px)}}@keyframes sideBarSlipOut-0e832da3{0%{transform:translateX(-500px)}100%{transform:translateX(0)}}#nprogress .bar[data-v-0e832da3]{background:#36ad6a;height:3px}body[data-v-0e832da3]{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#000;width:100%;height:100%}.user-management-page[data-v-0e832da3]{width:100%;box-sizing:border-box;padding:10px;background:#fff;overflow:auto;position:relative}.user-management-page .table-pager[data-v-0e832da3]{width:100%;display:flex;justify-content:center;align-items:center;padding:20px 0;box-sizing:border-box}
--------------------------------------------------------------------------------
/dist/assets/index.b06ca65d.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/index.b06ca65d.css.gz
--------------------------------------------------------------------------------
/dist/assets/index.cc670981.js:
--------------------------------------------------------------------------------
1 | import{u as s,C as a,D as e,b as t,e as p,f as c,h as o,y as i,z as d}from"./vendor.f21cd016.js";const m={name:"home",setup:()=>({appName:s().state.app.appName})},r=d();a("data-v-657f9024");const l=o("div",{class:"home-img"},[o("img",{src:"./assets/home.155038bb.svg"})],-1),n={class:"tips"},v=o("div",{class:"tips-title"},"欢迎登陆!",-1),f={class:"tips-subtitle"};e();const u=r(((s,a,e,d,m,u)=>{const h=t("el-space");return p(),c(h,{wrap:"",class:"home-container"},{default:r((()=>[l,o("div",n,[v,o("div",f,i(d.appName),1)])])),_:1})}));m.render=u,m.__scopeId="data-v-657f9024";export{m as default};
2 |
--------------------------------------------------------------------------------
/dist/assets/index.d41203df.js:
--------------------------------------------------------------------------------
1 | var e=Object.defineProperty,l=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,t=(l,a,o)=>a in l?e(l,a,{enumerable:!0,configurable:!0,writable:!0,value:o}):l[a]=o;import{u as i}from"./useTableData.6da28c31.js";import{C as n,D as s,u as d,r,b as m,e as u,f as p,h as c,F as f,x as b,z as h,A as g,w,o as y,v as C,c as v,E as k,G as _,y as x}from"./vendor.f21cd016.js";import{a as z}from"./index.710eea4b.js";import{h as V}from"./index.ffebf55c.js";const T=[{id:1,name:"普通人员"},{id:2,name:"管理员"}];z(T,{keyName:"id"});const M=[{id:1,name:"男"},{id:2,name:"女"}];z(M,{keyName:"id"});const S={name:"UserEdit",emits:["confirm"],setup(e,i){d();const n=r({type:"",title:"",visible:!1,confirmBtnLoading:!1}),s=r({form:{userName:"",sex:"",phone:"",role:""}}),m=()=>{n.visible=!1,s.form={userName:"",sex:"",phone:"",role:""}};return{modalConfig:n,data:s,sexOptions:M,roleOptions:T,hideModal:m,showModal:(e={type:"add",title:"",data:{}})=>{n.type=e.type,n.visible=!0,"add"===e.type&&(n.title="添加"),"edit"===e.type&&(n.title="编辑"),n.title=e.title||"",e.data&&(s.form=((e,i)=>{for(var n in i||(i={}))a.call(i,n)&&t(e,n,i[n]);if(l)for(var n of l(i))o.call(i,n)&&t(e,n,i[n]);return e})({},e.data))},onModalConfirmClick:()=>{m(),i.emit("confirm")},onModalCloseClick:()=>{m()}}}},E=h();n("data-v-4898c2bf");const O={class:"avatar-upload-wrap"},P=c("div",{class:"uploader-bottom"},[c("i",{class:"icon-upload el-icon-camera"})],-1),U=g("取 消"),j=g("确 定");s();const N=E(((e,l,a,o,t,i)=>{const n=m("el-avatar"),s=m("el-upload"),d=m("el-col"),r=m("el-input"),h=m("el-form-item"),g=m("el-option"),w=m("el-select"),y=m("el-row"),C=m("el-form"),v=m("el-button"),k=m("el-dialog");return u(),p(k,{title:o.modalConfig.title,modelValue:o.modalConfig.visible,"onUpdate:modelValue":l[5]||(l[5]=e=>o.modalConfig.visible=e),width:"40%","close-on-click-modal":!1},{footer:E((()=>[c(v,{onClick:o.onModalCloseClick,size:"medium"},{default:E((()=>[U])),_:1},8,["onClick"]),c(v,{type:"primary",onClick:o.onModalConfirmClick,size:"medium",loading:o.modalConfig.confirmBtnLoading},{default:E((()=>[j])),_:1},8,["onClick","loading"])])),default:E((()=>[c(C,{size:"medium",style:{"min-height":"200px"},"label-width":"100px","label-position":"top"},{default:E((()=>[c(y,{gutter:20},{default:E((()=>[c(d,{span:24},{default:E((()=>[c(s,{class:"avatar-uploader",action:"https://jsonplaceholder.typicode.com/posts/","show-file-list":!1},{default:E((()=>[c("div",O,[c(n,{size:100,style:{"margin-bottom":"30px"},src:"https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png"}),P])])),_:1})])),_:1}),c(d,{span:12},{default:E((()=>[c(h,{label:"用户名:"},{default:E((()=>[c(r,{size:"medium",modelValue:o.data.form.userName,"onUpdate:modelValue":l[1]||(l[1]=e=>o.data.form.userName=e),placeholder:"请输入用户名",clearable:"",style:{width:"100%"}},null,8,["modelValue"])])),_:1})])),_:1}),c(d,{span:12},{default:E((()=>[c(h,{label:"性别:"},{default:E((()=>[c(w,{modelValue:o.data.form.sex,"onUpdate:modelValue":l[2]||(l[2]=e=>o.data.form.sex=e),placeholder:"请选择性别",clearable:"",size:"medium",style:{width:"100%"}},{default:E((()=>[(u(!0),p(f,null,b(o.sexOptions,(e=>(u(),p(g,{key:e.id,label:e.name,value:e.id},null,8,["label","value"])))),128))])),_:1},8,["modelValue"])])),_:1})])),_:1}),c(d,{span:12},{default:E((()=>[c(h,{label:"联系电话:"},{default:E((()=>[c(r,{modelValue:o.data.form.phone,"onUpdate:modelValue":l[3]||(l[3]=e=>o.data.form.phone=e),placeholder:"请输入联系电话",size:"medium",style:{width:"100%"}},null,8,["modelValue"])])),_:1})])),_:1}),c(d,{span:12},{default:E((()=>[c(h,{label:"角色:"},{default:E((()=>[c(w,{modelValue:o.data.form.role,"onUpdate:modelValue":l[4]||(l[4]=e=>o.data.form.role=e),placeholder:"请选择角色",size:"medium",style:{width:"100%"}},{default:E((()=>[(u(!0),p(f,null,b(o.roleOptions,(e=>(u(),p(g,{key:e.id,label:e.name,value:e.id},null,8,["label","value"])))),128))])),_:1},8,["modelValue"])])),_:1})])),_:1})])),_:1})])),_:1})])),_:1},8,["title","modelValue"])}));S.render=N,S.__scopeId="data-v-4898c2bf";const D={name:"user-management",components:{UserEdit:S},setup(){const e=d(),l=i(V.userManagement.list);let a=r({keyword:"",sex:""});w([()=>a.keyword,()=>a.sex],(()=>{l.setParams({keyword:a.keyword,sex:a.sex})}));y((()=>{l.setParams({keyword:a.keyword})}));const o=C([]),t=C(null);return{filterForm:a,userManagementTable:l,tableSelection:o,userTableEdit:t,onTableAddClick:()=>{t.value.showModal({type:"add",title:"添加"})},onTableEditClick:e=>{t.value.showModal({type:"edit",title:"添加",data:{userName:e.userName,sex:e.sex,phone:e.phone,role:e.role}})},onTableDeleteClick:()=>{},onTableSelectionChange:e=>{o.value=e},onSizeChange:e=>{l.setParams({pageSize:e})},onCurrentChange:e=>{l.setParams({currentPage:e})},windowRect:v((()=>e.state.app.windowRect)),device:v((()=>e.state.app.device))}}},F=h();n("data-v-0e832da3");const A={class:"user-management-page","element-loading-text":"拼命加载中"},I=g("添加"),L=g("删除"),R=g("编辑"),B=g(" 暂无数据 "),G={class:"table-pager"};s();const H=F(((e,l,a,o,t,i)=>{const n=m("el-input"),s=m("el-option"),d=m("el-select"),r=m("el-button"),f=m("el-space"),b=m("el-table-column"),h=m("el-avatar"),w=m("el-tag"),y=m("el-table"),C=m("el-pagination"),v=m("UserEdit"),z=k("loading");return _((u(),p("div",A,[c(f,{wrap:"",style:{width:"100%"}},{default:F((()=>[c(n,{modelValue:o.filterForm.keyword,"onUpdate:modelValue":l[1]||(l[1]=e=>o.filterForm.keyword=e),class:"search-input","prefix-icon":"el-icon-search",size:"small",placeholder:"用户名/姓名",clearable:"",style:{width:"200px"}},null,8,["modelValue"]),c(d,{style:{width:"100px"},modelValue:o.filterForm.sex,"onUpdate:modelValue":l[2]||(l[2]=e=>o.filterForm.sex=e),class:"search-input","prefix-icon":"el-icon-search",size:"small",placeholder:"性别",clearable:""},{default:F((()=>[c(s,{label:"男",value:"1"}),c(s,{label:"女",value:"2"})])),_:1},8,["modelValue"]),c(r,{type:"primary",size:"small",icon:"el-icon-circle-plus-outline",onClick:o.onTableAddClick},{default:F((()=>[I])),_:1},8,["onClick"]),c(r,{type:"danger",size:"small",icon:"el-icon-delete",disabled:0==o.tableSelection.length,onClick:o.onTableDeleteClick},{default:F((()=>[L])),_:1},8,["disabled","onClick"])])),_:1}),c(y,{data:o.userManagementTable.tableData.list,"tooltip-effect":"dark",height:o.windowRect.clientHeight-240,"row-style":{height:"65px"},onSelectionChange:o.onTableSelectionChange},{empty:F((()=>[B])),default:F((()=>[c(b,{type:"selection",width:"50","show-overflow-tooltip":"","header-align":"center",align:"center"}),c(b,{label:"头像",width:"80","show-overflow-tooltip":""},{default:F((e=>[c(h,{size:32,src:e.row.img},null,8,["src"])])),_:1}),c(b,{prop:"userName",label:"用户名","show-overflow-tooltip":""}),c(b,{prop:"sex",label:"性别","show-overflow-tooltip":""}),c(b,{label:"角色","show-overflow-tooltip":""},{default:F((e=>[c(w,{effect:"dark",size:"small"},{default:F((()=>[g(x(e.row.role),1)])),_:2},1024)])),_:1}),c(b,{prop:"phone",label:"联系电话","min-width":"120","show-overflow-tooltip":""}),c(b,{prop:"loginTime",label:"登录次数","show-overflow-tooltip":""}),c(b,{prop:"lastLoginTime",label:"最后登录时间","min-width":"200","show-overflow-tooltip":""}),c(b,{label:"操作","show-overflow-tooltip":"","min-width":"95",fixed:"right"},{default:F((e=>[c(r,{type:"warning",size:"small",onClick:l=>o.onTableEditClick(e.row)},{default:F((()=>[R])),_:2},1032,["onClick"])])),_:1})])),_:1},8,["data","height","onSelectionChange"]),c("div",G,[c(C,{onSizeChange:o.onSizeChange,onCurrentChange:o.onCurrentChange,"page-size":o.userManagementTable.tableParams.pageSize,"page-sizes":o.userManagementTable.PAGE_SIZES,layout:"total,prev,pager,next,"+("mobile"===o.device?"":"sizes"),total:o.userManagementTable.tableData.totalCount,small:"mobile"===o.device},null,8,["onSizeChange","onCurrentChange","page-size","page-sizes","layout","total","small"])]),c(v,{ref:"userTableEdit",onConfirm:o.userManagementTable.refresh},null,8,["onConfirm"])],512)),[[z,o.userManagementTable.tableData.isLoading]])}));D.render=H,D.__scopeId="data-v-0e832da3";export{D as default};
2 |
--------------------------------------------------------------------------------
/dist/assets/index.d41203df.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/index.d41203df.js.gz
--------------------------------------------------------------------------------
/dist/assets/index.e46c323d.js:
--------------------------------------------------------------------------------
1 | import{d as e,q as t,b as a,e as s,f as r,g as n,h as o,A as i}from"./vendor.f21cd016.js";const c=e({name:"404",setup(){const e=t();return{onNavigateBack:()=>{e.back()}}}}),l=i("返回");c.render=function(e,t,i,c,u,d){const f=a("el-button"),m=a("el-result");return s(),r(m,{icon:"error",title:"404",subTitle:"页面不存在!",style:{width:"100%",height:"100vh"}},{extra:n((()=>[o(f,{type:"primary",size:"medium",onClick:e.onNavigateBack},{default:n((()=>[l])),_:1},8,["onClick"])])),_:1})};export{c as default};
2 |
--------------------------------------------------------------------------------
/dist/assets/index.efa6b091.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";[data-v-657f9024]{scrollbar-color:rgba(0,0,0,0.3) transparent;scrollbar-width:thin}[data-v-657f9024]:focus{outline:0}[data-v-657f9024]::-webkit-scrollbar{width:8px;height:8px}[data-v-657f9024]::-webkit-scrollbar-track{background:0 0}[data-v-657f9024]::-webkit-scrollbar-thumb{background-color:rgba(153,153,153,.5);background-clip:padding-box;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;transition:all 1s;cursor:pointer}[data-v-657f9024]::-webkit-scrollbar-thumb:hover{background-color:#999}.fade-enter-active[data-v-657f9024],.fade-leave-active[data-v-657f9024]{transition:all .3s ease}.fade-enter-from[data-v-657f9024],.fade-leave-to[data-v-657f9024]{opacity:0;transform:translateY(-1000px)}@keyframes slideUp-657f9024{0%{opacity:0;transform:translateY(60px)}100%{opacity:1;transform:translateY(0)}}@keyframes upAndDown-657f9024{0%{transform:translateY(0)}50%{transform:translateY(10px)}100%{transform:translateY(0)}}@keyframes sideBarSlipIn-657f9024{0%{transform:translateX(0)}100%{transform:translateX(-500px)}}@keyframes sideBarSlipOut-657f9024{0%{transform:translateX(-500px)}100%{transform:translateX(0)}}#nprogress .bar[data-v-657f9024]{background:#36ad6a;height:3px}body[data-v-657f9024]{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#000;width:100%;height:100%}.home-container[data-v-657f9024]{display:flex;align-items:center;justify-content:center;box-sizing:border-box;flex-wrap:wrap;height:100%;width:100%;background:#fff}.home-container .home-img[data-v-657f9024]{width:400px;height:auto;padding:0 50px;box-sizing:border-box;animation:upAndDown-657f9024 2s infinite linear}.home-container .home-img>img[data-v-657f9024]{transform:rotateY(180deg)}.home-container .tips[data-v-657f9024]{position:relative;overflow:hidden;display:flex;flex-direction:column;text-align:center;margin:0 50px}.home-container .tips .tips-title[data-v-657f9024]{margin-bottom:30px;font-size:40px;font-weight:700;line-height:40px;color:#000;opacity:0;animation-name:slideUp-657f9024;animation-duration:.5s;animation-fill-mode:forwards}.home-container .tips .tips-subtitle[data-v-657f9024]{margin-bottom:30px;font-size:25px;line-height:36px;color:rgba(0,0,0,.8);opacity:0;animation-name:slideUp-657f9024;animation-duration:.5s;animation-delay:.2s;animation-fill-mode:forwards}
--------------------------------------------------------------------------------
/dist/assets/index.efa6b091.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/index.efa6b091.css.gz
--------------------------------------------------------------------------------
/dist/assets/index.ffebf55c.js:
--------------------------------------------------------------------------------
1 | var e={exports:{}},t=function(e,t){return function(){for(var r=new Array(arguments.length),n=0;n=0)return;o[t]="set-cookie"===t?(o[t]?o[t]:[]).concat([r]):o[t]?o[t]+", "+r:r}})),o):o},_=A,D=S,F=function(e){return new Promise((function(t,r){var n=e.data,o=e.headers;U.isFormData(n)&&delete o["Content-Type"];var s=new XMLHttpRequest;if(e.auth){var a=e.auth.username||"",i=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";o.Authorization="Basic "+btoa(a+":"+i)}var u=k(e.baseURL,e.url);if(s.open(e.method.toUpperCase(),B(u,e.params,e.paramsSerializer),!0),s.timeout=e.timeout,s.onreadystatechange=function(){if(s&&4===s.readyState&&(0!==s.status||s.responseURL&&0===s.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in s?q(s.getAllResponseHeaders()):null,o={data:e.responseType&&"text"!==e.responseType?s.response:s.responseText,status:s.status,statusText:s.statusText,headers:n,config:e,request:s};L(t,r,o),s=null}},s.onabort=function(){s&&(r(D("Request aborted",e,"ECONNABORTED",s)),s=null)},s.onerror=function(){r(D("Network Error",e,null,s)),s=null},s.ontimeout=function(){var t="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),r(D(t,e,"ECONNABORTED",s)),s=null},U.isStandardBrowserEnv()){var c=(e.withCredentials||_(u))&&e.xsrfCookieName?N.read(e.xsrfCookieName):void 0;c&&(o[e.xsrfHeaderName]=c)}if("setRequestHeader"in s&&U.forEach(o,(function(e,t){void 0===n&&"content-type"===t.toLowerCase()?delete o[t]:s.setRequestHeader(t,e)})),U.isUndefined(e.withCredentials)||(s.withCredentials=!!e.withCredentials),e.responseType)try{s.responseType=e.responseType}catch(f){if("json"!==e.responseType)throw f}"function"==typeof e.onDownloadProgress&&s.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&s.upload&&s.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then((function(e){s&&(s.abort(),r(e),s=null)})),n||(n=null),s.send(n)}))},M=f,z=function(e,t){w.forEach(e,(function(r,n){n!==t&&n.toUpperCase()===t.toUpperCase()&&(e[t]=r,delete e[n])}))},H={"Content-Type":"application/x-www-form-urlencoded"};function I(e,t){!M.isUndefined(e)&&M.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}var X,$={adapter:(("undefined"!=typeof XMLHttpRequest||"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process))&&(X=F),X),transformRequest:[function(e,t){return z(t,"Accept"),z(t,"Content-Type"),M.isFormData(e)||M.isArrayBuffer(e)||M.isBuffer(e)||M.isStream(e)||M.isFile(e)||M.isBlob(e)?e:M.isArrayBufferView(e)?e.buffer:M.isURLSearchParams(e)?(I(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):M.isObject(e)?(I(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,validateStatus:function(e){return e>=200&&e<300}};$.headers={common:{Accept:"application/json, text/plain, */*"}},M.forEach(["delete","get","head"],(function(e){$.headers[e]={}})),M.forEach(["post","put","patch"],(function(e){$.headers[e]=M.merge(H)}));var J=$,V=f,K=function(e,t,r){return y.forEach(r,(function(r){e=r(e,t)})),e},G=v,Q=J;function W(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var Y=f,Z=function(e,t){t=t||{};var r={},n=["url","method","data"],o=["headers","auth","proxy","params"],s=["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","timeoutMessage","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","decompress","maxContentLength","maxBodyLength","maxRedirects","transport","httpAgent","httpsAgent","cancelToken","socketPath","responseEncoding"],a=["validateStatus"];function i(e,t){return Y.isPlainObject(e)&&Y.isPlainObject(t)?Y.merge(e,t):Y.isPlainObject(t)?Y.merge({},t):Y.isArray(t)?t.slice():t}function u(n){Y.isUndefined(t[n])?Y.isUndefined(e[n])||(r[n]=i(void 0,e[n])):r[n]=i(e[n],t[n])}Y.forEach(n,(function(e){Y.isUndefined(t[e])||(r[e]=i(void 0,t[e]))})),Y.forEach(o,u),Y.forEach(s,(function(n){Y.isUndefined(t[n])?Y.isUndefined(e[n])||(r[n]=i(void 0,e[n])):r[n]=i(void 0,t[n])})),Y.forEach(a,(function(n){n in t?r[n]=i(e[n],t[n]):n in e&&(r[n]=i(void 0,e[n]))}));var c=n.concat(o).concat(s).concat(a),f=Object.keys(e).concat(Object.keys(t)).filter((function(e){return-1===c.indexOf(e)}));return Y.forEach(f,u),r},ee=f,te=p,re=g,ne=function(e){return W(e),e.headers=e.headers||{},e.data=K(e.data,e.headers,e.transformRequest),e.headers=V.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),V.forEach(["delete","get","head","post","put","patch","common"],(function(t){delete e.headers[t]})),(e.adapter||Q.adapter)(e).then((function(t){return W(e),t.data=K(t.data,t.headers,e.transformResponse),t}),(function(t){return G(t)||(W(e),t&&t.response&&(t.response.data=K(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))},oe=Z;function se(e){this.defaults=e,this.interceptors={request:new re,response:new re}}se.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{}).url=arguments[0]:e=e||{},(e=oe(this.defaults,e)).method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[ne,void 0],r=Promise.resolve(e);for(this.interceptors.request.forEach((function(e){t.unshift(e.fulfilled,e.rejected)})),this.interceptors.response.forEach((function(e){t.push(e.fulfilled,e.rejected)}));t.length;)r=r.then(t.shift(),t.shift());return r},se.prototype.getUri=function(e){return e=oe(this.defaults,e),te(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},ee.forEach(["delete","get","head","options"],(function(e){se.prototype[e]=function(t,r){return this.request(oe(r||{},{method:e,url:t,data:(r||{}).data}))}})),ee.forEach(["post","put","patch"],(function(e){se.prototype[e]=function(t,r,n){return this.request(oe(n||{},{method:e,url:t,data:r}))}}));var ae=se;function ie(e){this.message=e}ie.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},ie.prototype.__CANCEL__=!0;var ue=ie,ce=ue;function fe(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var r=this;e((function(e){r.reason||(r.reason=new ce(e),t(r.reason))}))}fe.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},fe.source=function(){var e;return{token:new fe((function(t){e=t})),cancel:e}};var de=fe,le=f,pe=t,he=ae,me=Z;function ge(e){var t=new he(e),r=pe(he.prototype.request,t);return le.extend(r,he.prototype,t),le.extend(r,t),r}var ye=ge(J);ye.Axios=he,ye.create=function(e){return ge(me(ye.defaults,e))},ye.Cancel=ue,ye.CancelToken=de,ye.isCancel=v,ye.all=function(e){return Promise.all(e)},ye.spread=function(e){return function(t){return e.apply(null,t)}},ye.isAxiosError=function(e){return"object"==typeof e&&!0===e.isAxiosError},e.exports=ye,e.exports.default=ye;var ve=e.exports;ve.defaults.timeout=36e6,ve.interceptors.request.use((e=>e),(e=>Promise.reject(e))),ve.interceptors.response.use((e=>{if(e.data.success)return e.data.data;throw new Error(e.data.msg)}),(e=>{if(e&&e.response)switch(e.response.status){case 400:return e.message="请求出错(400)",Promise.reject(e);case 401:return e.message="未授权(401)",Promise.reject(e);case 403:e.message="禁止访问(403)";break;case 404:return e.message="资源未找到(404)",Promise.reject(e);case 500:return Promise.reject(e);case 501:return e.message="服务未实现(501)",Promise.reject(e);case 502:return e.message="网络错误(502)",Promise.reject(e);case 503:return e.message="服务不可用(503)",Promise.reject(e);case 504:return e.message="网络超时(504)",Promise.reject(e);case 505:return e.message="HTTP版本不受支持(505)",Promise.reject(e);default:return e.message=`请求出错(${e.response.status})!`,Promise.reject(e)}else e.message="连接服务器失败!"}));var we={user:Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",login:e=>ve({url:"/login",method:"POST",data:e})}),roleManagement:Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",list:e=>ve({url:"/roleList",method:"POST",data:e}),addUser:e=>ve({url:"/roleList/add",method:"POST",data:e}),deleteUser:e=>ve({url:"/roleList/delete",method:"POST",data:e}),editUser:e=>ve({url:"/roleList/edit",method:"POST",data:e})}),userManagement:Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",list:e=>ve({url:"/userList",method:"POST",data:e}),addUser:e=>ve({url:"/userList/add",method:"POST",data:e}),deleteUser:e=>ve({url:"/userList/delete",method:"POST",data:e}),editUser:e=>ve({url:"/userList/edit",method:"POST",data:e})})};export{we as h};
2 |
--------------------------------------------------------------------------------
/dist/assets/index.ffebf55c.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/index.ffebf55c.js.gz
--------------------------------------------------------------------------------
/dist/assets/logo.61ee5eb1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/logo.61ee5eb1.png
--------------------------------------------------------------------------------
/dist/assets/useTableData.6da28c31.js:
--------------------------------------------------------------------------------
1 | var t=Object.defineProperty,e=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable,o=(e,a,r)=>a in e?t(e,a,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[a]=r,i=(t,i)=>{for(var n in i||(i={}))a.call(i,n)&&o(t,n,i[n]);if(e)for(var n of e(i))r.call(i,n)&&o(t,n,i[n]);return t};import{g as n}from"./index.710eea4b.js";import{r as s,I as l}from"./vendor.f21cd016.js";function g(t){if("function"!=typeof t&&"Promise"!==n(t))throw new Error("请传入Promise类型的api请求");const e=[20,50,100],a={list:[],totalCount:0,isLoading:!1,currentPage:1,pageSize:e[0]};let r=s({list:a.list,totalCount:a.totalCount,isLoading:a.isLoading}),o={currentPage:a.currentPage,pageSize:a.pageSize};const g=async()=>{r.isLoading=!0;let e=await t(o).catch((t=>{r.isLoading=!1}));e?(r.isLoading=!1,r.list=e.list,r.totalCount=e.totalCount):l.error("数据获取失败!")};return{PAGE_SIZES:e,tableData:r,tableParams:o,setParams:(t={})=>{o=i(i({},o),t),g()},reset:()=>{r.list=a.list,r.totalCount=a.totalCount,r.isLoading=a.isLoading,o={currentPage:a.currentPage,pageSize:a.pageSize},g()},refresh:()=>{g()}}}export{g as u};
2 |
--------------------------------------------------------------------------------
/dist/assets/useTableData.6da28c31.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/useTableData.6da28c31.js.gz
--------------------------------------------------------------------------------
/dist/assets/vendor.f21cd016.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/assets/vendor.f21cd016.js.gz
--------------------------------------------------------------------------------
/dist/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/dist/favicon.ico
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 | Vite-Vue3-Elementplus-Admin
11 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 | Vite-Vue3-Elementplus-Admin
11 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-vue3-elementplus-admin",
3 | "version": "1.0.0",
4 | "author": "FEZIRO",
5 | "email": "FEZIRO@foxmail.com",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview/dist": "vite preview"
10 | },
11 | "dependencies": {
12 | "axios": "^0.21.1",
13 | "dayjs": "^1.10.6",
14 | "element-plus": "^1.0.2-beta.55",
15 | "mockjs": "^1.1.0",
16 | "nprogress": "^0.2.0",
17 | "reset-css": "^5.0.1",
18 | "vue": "^3.1.4",
19 | "vue-router": "^4.0.10",
20 | "vuex": "^4.0.2"
21 | },
22 | "devDependencies": {
23 | "@rollup/plugin-strip": "^2.1.0",
24 | "@vitejs/plugin-vue": "^1.4.0",
25 | "@vue/compiler-sfc": "^3.1.5",
26 | "babel-plugin-component": "^1.1.1",
27 | "rollup-plugin-visualizer": "^5.5.2",
28 | "sass": "^1.37.5",
29 | "vite": "^2.4.4",
30 | "vite-plugin-compression": "^0.3.3"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/preview/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/logo.png
--------------------------------------------------------------------------------
/preview/个人信息.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/个人信息.png
--------------------------------------------------------------------------------
/preview/右键菜单.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/右键菜单.png
--------------------------------------------------------------------------------
/preview/搜索.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/搜索.png
--------------------------------------------------------------------------------
/preview/更换主题色1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/更换主题色1.png
--------------------------------------------------------------------------------
/preview/标签切换.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/标签切换.png
--------------------------------------------------------------------------------
/preview/注册.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/注册.png
--------------------------------------------------------------------------------
/preview/用户管理.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/用户管理.png
--------------------------------------------------------------------------------
/preview/登录.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/登录.png
--------------------------------------------------------------------------------
/preview/移动端界面1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/移动端界面1.png
--------------------------------------------------------------------------------
/preview/移动端菜单.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/移动端菜单.png
--------------------------------------------------------------------------------
/preview/移动端设置.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/移动端设置.png
--------------------------------------------------------------------------------
/preview/设置.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/设置.png
--------------------------------------------------------------------------------
/preview/通知.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/通知.png
--------------------------------------------------------------------------------
/preview/面包屑.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/面包屑.png
--------------------------------------------------------------------------------
/preview/首页.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/preview/首页.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/public/favicon.ico
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
44 |
45 |
--------------------------------------------------------------------------------
/src/assets/images/arrow-down.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/arrow-up.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/empty-tips.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
98 |
--------------------------------------------------------------------------------
/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEZIRO/vue3-vite-elementplus-admin/092b3a8d46bb68809770feda8c7589893da885ab/src/assets/images/logo.png
--------------------------------------------------------------------------------
/src/components/CountDownBtn/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ waitingCodeTips }}
4 |
5 |
6 |
7 |
57 |
58 |
--------------------------------------------------------------------------------
/src/hooks/useTableData.js:
--------------------------------------------------------------------------------
1 | import { reactive } from "vue";
2 | import { getType } from "@/others/utils";
3 | import { ElMessage } from "element-plus";
4 |
5 | export default function useTableData(api) {
6 | if (typeof api !== "function" && getType(api) !== "Promise") {
7 | throw new Error("请传入Promise类型的api请求");
8 | }
9 |
10 | //分页器每页个数配置组
11 | const PAGE_SIZES = [20, 50, 100];
12 |
13 | //初始化数据
14 | const DEFAULT_TABLE_DATA = {
15 | list: [],
16 | totalCount: 0,
17 | isLoading: false,
18 | currentPage: 1,
19 | pageSize: PAGE_SIZES[0],
20 | };
21 |
22 | //数据表格
23 | let tableData = reactive({
24 | list: DEFAULT_TABLE_DATA.list,
25 | totalCount: DEFAULT_TABLE_DATA.totalCount,
26 | isLoading: DEFAULT_TABLE_DATA.isLoading,
27 | });
28 |
29 | //数据表格基本请求参数
30 | let tableParams = {
31 | currentPage: DEFAULT_TABLE_DATA.currentPage,
32 | pageSize: DEFAULT_TABLE_DATA.pageSize,
33 | };
34 |
35 | //获取数据
36 | const fetchData = async () => {
37 | tableData.isLoading = true;
38 | let res = await api(tableParams).catch(err => {
39 | tableData.isLoading = false;
40 | });
41 | if (res) {
42 | console.log(api.toString() + "请求成功\n", res);
43 | tableData.isLoading = false;
44 | tableData.list = res.list;
45 | tableData.totalCount = res.totalCount;
46 | } else {
47 | ElMessage.error("数据获取失败!");
48 | }
49 | };
50 |
51 | //重置请求
52 | const reset = () => {
53 | tableData.list = DEFAULT_TABLE_DATA.list;
54 | tableData.totalCount = DEFAULT_TABLE_DATA.totalCount;
55 | tableData.isLoading = DEFAULT_TABLE_DATA.isLoading;
56 | tableParams = {
57 | currentPage: DEFAULT_TABLE_DATA.currentPage,
58 | pageSize: DEFAULT_TABLE_DATA.pageSize,
59 | };
60 | fetchData();
61 | };
62 |
63 | //刷新
64 | const refresh = () => {
65 | fetchData();
66 | };
67 |
68 | //设置查询参数
69 | const setParams = (params = {}) => {
70 | tableParams = { ...tableParams, ...params };
71 | fetchData();
72 | };
73 |
74 | return {
75 | PAGE_SIZES,
76 | tableData,
77 | tableParams,
78 | setParams,
79 | reset,
80 | refresh,
81 | };
82 | }
83 |
--------------------------------------------------------------------------------
/src/hooks/useWindowSize.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 实时监听获取浏览器窗口宽度、高度
3 | */
4 |
5 | import { toRefs, onMounted, onBeforeUnmount, reactive } from "vue";
6 | import { debounce, getClientRect } from "@/others/utils";
7 |
8 | export default function useWindowSize() {
9 | const windowRect = reactive({
10 | clientHeight: getClientRect().clientHeight,
11 | clientWidth: getClientRect().clientWidth,
12 | });
13 |
14 | const onWindowsResize = debounce(() => {
15 | windowRect.clientHeight = getClientRect().clientHeight;
16 | windowRect.clientWidth = getClientRect().clientWidth;
17 | }, 300);
18 |
19 | onMounted(() => {
20 | window.addEventListener("resize", onWindowsResize);
21 | });
22 |
23 | onBeforeUnmount(() => {
24 | window.removeEventListener("resize", onWindowsResize);
25 | });
26 | return {
27 | ...toRefs(windowRect),
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/src/http/apis/role-management.js:
--------------------------------------------------------------------------------
1 | import axios from "../axios";
2 |
3 | export const list = data => axios({ url: "/roleList", method: "POST", data });
4 |
5 | export const addUser = data =>
6 | axios({ url: "/roleList/add", method: "POST", data });
7 |
8 | export const deleteUser = data =>
9 | axios({ url: "/roleList/delete", method: "POST", data });
10 |
11 | export const editUser = data =>
12 | axios({ url: "/roleList/edit", method: "POST", data });
13 |
--------------------------------------------------------------------------------
/src/http/apis/user-management.js:
--------------------------------------------------------------------------------
1 | import axios from "../axios";
2 |
3 | export const list = data => axios({ url: "/userList", method: "POST", data });
4 |
5 | export const addUser = data =>
6 | axios({ url: "/userList/add", method: "POST", data });
7 |
8 | export const deleteUser = data =>
9 | axios({ url: "/userList/delete", method: "POST", data });
10 |
11 | export const editUser = data =>
12 | axios({ url: "/userList/edit", method: "POST", data });
13 |
--------------------------------------------------------------------------------
/src/http/apis/user.js:
--------------------------------------------------------------------------------
1 | import axios from "../axios";
2 |
3 | //登录操作
4 | export const login = data => axios({ url: "/login", method: "POST", data });
5 |
--------------------------------------------------------------------------------
/src/http/axios.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @desc:axios请求库全局配置
3 | */
4 | import axios from "axios";
5 |
6 | //根据.env文件切换环境
7 | // axios.defaults.baseURL = process.env.VITE_APP_SERVER_HOST;
8 |
9 | axios.defaults.timeout = 36000000;
10 | axios.interceptors.request.use(
11 | config => {
12 | // let token = localStorage.getItem("userInfo").token;
13 | // config.headers["accessToken"] = token;
14 | return config;
15 | },
16 | error => {
17 | return Promise.reject(error);
18 | }
19 | );
20 |
21 | axios.interceptors.response.use(
22 | response => {
23 | // console.log("response", response);
24 |
25 | if (response.data.success) {
26 | return response.data.data;
27 | } else {
28 | // message.error(response.data.msg);
29 | throw new Error(response.data.msg);
30 | }
31 | },
32 | error => {
33 | console.error("请求error", error);
34 |
35 | if (error && error.response) {
36 | switch (error.response.status) {
37 | case 400:
38 | error.message = "请求出错(400)";
39 | return Promise.reject(error);
40 | case 401:
41 | error.message = "未授权(401)";
42 | //跳登录页
43 | return Promise.reject(error);
44 | case 403:
45 | error.message = "禁止访问(403)";
46 | break;
47 | case 404:
48 | error.message = "资源未找到(404)";
49 | return Promise.reject(error);
50 | case 500:
51 | return Promise.reject(error);
52 | case 501:
53 | error.message = "服务未实现(501)";
54 | return Promise.reject(error);
55 | case 502:
56 | error.message = "网络错误(502)";
57 | return Promise.reject(error);
58 | case 503:
59 | error.message = "服务不可用(503)";
60 | return Promise.reject(error);
61 | case 504:
62 | error.message = "网络超时(504)";
63 | return Promise.reject(error);
64 | case 505:
65 | error.message = "HTTP版本不受支持(505)";
66 | return Promise.reject(error);
67 | default:
68 | error.message = `请求出错(${error.response.status})!`;
69 | return Promise.reject(error);
70 | }
71 | } else {
72 | error.message = "连接服务器失败!";
73 | }
74 | }
75 | );
76 |
77 | export default axios;
78 |
--------------------------------------------------------------------------------
/src/http/index.js:
--------------------------------------------------------------------------------
1 | import * as user from "./apis/user.js";
2 | import * as roleManagement from "./apis/role-management";
3 | import * as userManagement from "./apis/user-management";
4 |
5 | export default {
6 | user,
7 | roleManagement,
8 | userManagement,
9 | };
10 |
--------------------------------------------------------------------------------
/src/layouts/AdminLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
54 |
55 |
--------------------------------------------------------------------------------
/src/layouts/AppHeader/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
181 |
182 |
183 |
336 |
337 |
544 |
--------------------------------------------------------------------------------
/src/layouts/AppSideBar/MenuItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 | {{ item.name }}
12 |
13 |
14 |
15 |
22 |
23 | {{ item.name }}
24 |
25 |
26 |
27 |
28 |
58 |
--------------------------------------------------------------------------------
/src/layouts/AppSideBar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
50 |
51 |
139 |
--------------------------------------------------------------------------------
/src/layouts/Breadcrumb/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ item }}
5 |
6 |
7 |
8 |
9 |
29 |
--------------------------------------------------------------------------------
/src/layouts/MobileAppHeader/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
89 |
90 |
91 |
177 |
178 |
354 |
--------------------------------------------------------------------------------
/src/layouts/MobileAppSideBar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
80 |
81 |
82 |
83 |
178 |
179 |
294 |
--------------------------------------------------------------------------------
/src/layouts/TagsViewSwitcher/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
14 |
21 |
22 |
23 |
33 |
34 |
35 |
36 |
37 |
164 |
165 |
250 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 | import ElementPlus from "element-plus";
3 | import "element-plus/lib/theme-chalk/index.css";
4 | import locale from "element-plus/lib/locale/lang/zh-cn";
5 | import App from "./App.vue";
6 | import router from "@/router";
7 | import "reset-css";
8 | import store from "@/store";
9 | import "./mock/index";
10 | import "./styles/element-variables.scss";
11 |
12 | //全局注册Nprogress加载指示器
13 | import NProgress from "nprogress";
14 | import "nprogress/nprogress.css";
15 | NProgress.configure({ showSpinner: false, easing: "ease", speed: 500 });
16 |
17 | const app = createApp(App);
18 | console.warn("环境", import.meta.env);
19 |
20 | // 开发环境开启devtool
21 | if (import.meta.env.MODE === "development") {
22 | app.config.devtools = true;
23 | }
24 |
25 | app.config.errorHandler = (err, vm, info) => {
26 | console.error("Vue错误:", err, vm, info);
27 | };
28 |
29 | app.config.warnHandler = function (msg, vm, trace) {
30 | console.warn("Vue警告:", msg, vm, trace);
31 | };
32 | app.config.performance = true;
33 |
34 | //全局注册组件
35 | import AdminLayout from "@/layouts/AdminLayout.vue";
36 | app.component("AdminLayout", AdminLayout);
37 |
38 | import AppTagsViewSwitcher from "@/layouts/TagsViewSwitcher/index.vue";
39 | app.component("AppTagsViewSwitcher", AppTagsViewSwitcher);
40 |
41 | import AppSideBar from "@/layouts/AppSideBar/index.vue";
42 | app.component("AppSideBar", AppSideBar);
43 |
44 | import AppBreadcrumb from "@/layouts/Breadcrumb/index.vue";
45 | app.component("AppBreadcrumb", AppBreadcrumb);
46 |
47 | import AppHeader from "@/layouts/AppHeader/index.vue";
48 | app.component("AppHeader", AppHeader);
49 |
50 | import MobileAppHeader from "@/layouts/MobileAppHeader/index.vue";
51 | app.component("MobileAppHeader", MobileAppHeader);
52 |
53 | import MobileAppSideBar from "@/layouts/MobileAppSideBar/index.vue";
54 | app.component("MobileAppSideBar", MobileAppSideBar);
55 |
56 | app.use(ElementPlus, { locale });
57 | app.use(router);
58 | app.use(store);
59 | app.mount("#app");
60 |
--------------------------------------------------------------------------------
/src/mock/apiController/role-management.js:
--------------------------------------------------------------------------------
1 | import Mock from "../mock";
2 | let roleList = [
3 | {
4 | id: 1,
5 | roleName: "管理员",
6 | remark: "--",
7 | },
8 | {
9 | id: 2,
10 | roleName: "超级管理员",
11 | remark: "--",
12 | },
13 | {
14 | id: 3,
15 | roleName: "普通用户",
16 | remark: "--",
17 | },
18 | ];
19 |
20 | const pager = (list, pageSize) => {
21 | let newlist = [];
22 | for (let i = 0; i < list.length; i += pageSize) {
23 | newlist.push(list.slice(i, i + pageSize));
24 | }
25 | return newlist;
26 | };
27 |
28 | const parserBody = bodyStr => {
29 | if (bodyStr) {
30 | return JSON.parse(bodyStr);
31 | } else {
32 | return {};
33 | }
34 | };
35 |
36 | Mock.mock("/roleList", "post", config => {
37 | let body = parserBody(config.body);
38 | console.log("/roleList 请求", config);
39 | const pageSize = body.pageSize;
40 | const currentPage = body.currentPage;
41 | const keyword = body.keyword;
42 |
43 | let filterList = roleList.filter(item => new RegExp(keyword).test(item.name));
44 | return {
45 | success: true,
46 | msg: "",
47 | data: {
48 | list: pager(filterList, pageSize)[currentPage - 1],
49 | currentPage: currentPage,
50 | totalCount: filterList.length,
51 | totalPage: pager(filterList, pageSize).length,
52 | pageSize: pageSize,
53 | },
54 | };
55 | });
56 |
57 | Mock.mock("/roleList/add", "post", config => {
58 | let body = parserBody(config.body);
59 | console.log("/roleList/add 请求", config);
60 | roleList.unshift({
61 | id: Math.random(),
62 | name: body.name,
63 | });
64 | return {
65 | success: true,
66 | msg: "",
67 | data: {},
68 | };
69 | });
70 |
71 | Mock.mock("/roleList/delete", "post", config => {
72 | let body = parserBody(config.body);
73 | console.log("/roleList/delete 请求", config);
74 | roleList = roleList.filter(item => item.id !== body.id);
75 | return {
76 | success: true,
77 | msg: "",
78 | data: {},
79 | };
80 | });
81 |
82 | // Mock.mock("/roleList/edit", "post", config => {
83 | // let body = parserBody(config.body);
84 | // console.log("/roleList/add 请求", config);
85 | // roleList = roleList.filter(item => item.id !== body.id);
86 | // return {
87 | // success: true,
88 | // msg: "",
89 | // data: {},
90 | // };
91 | // });
92 |
--------------------------------------------------------------------------------
/src/mock/apiController/user-management.js:
--------------------------------------------------------------------------------
1 | import Mock from "../mock";
2 | let userList = new Array(100)
3 | .fill({
4 | id: "",
5 | name: "",
6 | })
7 | .map((item, index) => {
8 | return {
9 | id: index,
10 | img: "https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3305298991,2024211813&fm=26&gp=0.jpg",
11 | userName: "张三" + index,
12 | sex: "男",
13 | role: "管理员",
14 | phone: "13323131321",
15 | loginTime: "32",
16 | lastLoginTime: "2020-12-12 01:00:00",
17 | };
18 | });
19 |
20 | const pager = (list, pageSize) => {
21 | let newlist = [];
22 | for (let i = 0; i < list.length; i += pageSize) {
23 | newlist.push(list.slice(i, i + pageSize));
24 | }
25 | return newlist;
26 | };
27 |
28 | const parserBody = bodyStr => {
29 | if (bodyStr) {
30 | return JSON.parse(bodyStr);
31 | } else {
32 | return {};
33 | }
34 | };
35 |
36 | Mock.mock("/userList", "post", config => {
37 | let body = parserBody(config.body);
38 | console.log("/userList 请求", config);
39 | const pageSize = body.pageSize;
40 | const currentPage = body.currentPage;
41 | const keyword = body.keyword;
42 |
43 | let filterList = userList.filter(item =>
44 | new RegExp(keyword).test(item.userName)
45 | );
46 | return {
47 | success: true,
48 | msg: "",
49 | data: {
50 | list: pager(filterList, pageSize)[currentPage - 1],
51 | currentPage: currentPage,
52 | totalCount: filterList.length,
53 | totalPage: pager(filterList, pageSize).length,
54 | pageSize: pageSize,
55 | },
56 | };
57 | });
58 |
59 | Mock.mock("/userList/add", "post", config => {
60 | let body = parserBody(config.body);
61 | console.log("/userList/add 请求", config);
62 | userList.unshift({
63 | id: Math.random(),
64 | name: body.username,
65 | });
66 | return {
67 | success: true,
68 | msg: "",
69 | data: {},
70 | };
71 | });
72 |
73 | Mock.mock("/userList/delete", "post", config => {
74 | let body = parserBody(config.body);
75 | console.log("/userList/delete 请求", config);
76 | userList = userList.filter(item => item.id !== body.id);
77 | return {
78 | success: true,
79 | msg: "",
80 | data: {},
81 | };
82 | });
83 |
84 | // Mock.mock("/userList/edit", "post", config => {
85 | // let body = parserBody(config.body);
86 | // console.log("/userList/add 请求", config);
87 | // userList = userList.filter(item => item.id !== body.id);
88 | // return {
89 | // success: true,
90 | // msg: "",
91 | // data: {},
92 | // };
93 | // });
94 |
--------------------------------------------------------------------------------
/src/mock/apiController/user.js:
--------------------------------------------------------------------------------
1 | import Mock from "../mock";
2 |
3 | Mock.mock("/login", "post", config => {
4 | console.log("mockconfig", config);
5 | let body = config.body && JSON.parse(config.body);
6 | console.log("body", body);
7 |
8 | if (body.username === "admin" && body.password === "admin") {
9 | return {
10 | success: true,
11 | msg: "登录成功",
12 | data: {
13 | userInfo: {
14 | id: "32hnhszj22872hwkjae",
15 | name: "系统管理员",
16 | role: "管理员",
17 | avatar:
18 | "https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3305298991,2024211813&fm=26&gp=0.jpg",
19 | },
20 | menu: [
21 | {
22 | id: "1",
23 | name: "系统管理",
24 | icon: "el-icon-setting",
25 | url: "/system",
26 | breadcrumb: ["系统管理"],
27 | type: "group",
28 | children: [
29 | {
30 | id: "1-1",
31 | name: "用户管理",
32 | icon: "",
33 | type: "page",
34 | url: "/system/user-management",
35 | children: null,
36 | breadcrumb: ["系统管理", "用户管理"],
37 | },
38 | {
39 | id: "1-2",
40 | name: "角色管理",
41 | icon: "",
42 | type: "page",
43 | url: "/system/role-management",
44 | children: null,
45 | breadcrumb: ["系统管理", "角色管理"],
46 | },
47 | ],
48 | },
49 | ],
50 | },
51 | };
52 | }
53 | if (body.username === "2" && body.password === "2") {
54 | return {
55 | success: true,
56 | msg: "登录成功",
57 | data: {
58 | userInfo: {
59 | id: "ashwh2872dkjsahhwkjae",
60 | name: "张三",
61 | role: "普通用户",
62 | avatar:
63 | "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2448425926,3379370176&fm=11&gp=0.jpg",
64 | },
65 | menu: [
66 | {
67 | id: "2",
68 | name: "统计",
69 | icon: "",
70 | type: "page",
71 | url: "/chart",
72 | children: null,
73 | breadcrumb: [
74 | {
75 | name: "统计",
76 | url: "/chart",
77 | },
78 | ],
79 | },
80 | ],
81 | },
82 | };
83 | }
84 |
85 | return {
86 | success: false,
87 | msg: "密码错误!",
88 | data: {},
89 | };
90 | });
91 |
--------------------------------------------------------------------------------
/src/mock/index.js:
--------------------------------------------------------------------------------
1 | import "./apiController/user";
2 | import "./apiController/user-management";
3 | import "./apiController/role-management";
4 |
--------------------------------------------------------------------------------
/src/mock/mock.js:
--------------------------------------------------------------------------------
1 | import Mock from "mockjs";
2 |
3 | Mock.setup({
4 | timeout: 500, // 设置延迟响应,模拟向后端请求数据
5 | });
6 |
7 |
8 | export default Mock;
--------------------------------------------------------------------------------
/src/others/options.js:
--------------------------------------------------------------------------------
1 | import { arrayToObject } from "@/others/utils.js";
2 |
3 | /**
4 | * 全局下拉值选项配置
5 | */
6 |
7 | // 用户类型选项
8 | export const userTypeOptions = [
9 | {
10 | id: 1,
11 | name: "普通人员",
12 | },
13 | {
14 | id: 2,
15 | name: "管理员",
16 | },
17 | ];
18 |
19 | // 用户类型映射(可用于表格类型回显)
20 | // [{ id: 1, name: "张三" }] => { 1 : { id: 1, name: "张三" } }
21 | export const userTypeOptionsMap = arrayToObject(userTypeOptions, {
22 | keyName: "id",
23 | });
24 |
25 |
26 | //性别类型选项
27 | export const sexOptions = [
28 | {
29 | id: 1,
30 | name: "男",
31 | },
32 | {
33 | id: 2,
34 | name: "女",
35 | },
36 | ];
37 | // 性别类型选项映射
38 | export const sexOptionsMap = arrayToObject(sexOptions, {
39 | keyName: "id",
40 | });
41 |
--------------------------------------------------------------------------------
/src/others/utils.js:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | /**
4 | * 对象数组映射成对象Map结构
5 | * @param {Array}
6 | * @return {Object}
7 | */
8 | export function arrayToObject(
9 | data = [],
10 | format = { keyName: "id", valueName: "name" }
11 | ) {
12 | let map = {};
13 | if (data.length == 0) return;
14 | if (format && format.keyName && format.valueName) {
15 | // 例如以id为key名称,以name为value名称
16 | // [{ id: "xx", name: "yy" }] => { xx: yy }
17 | data.forEach(item => {
18 | map[item[format.keyName]] = item[format.valueName];
19 | });
20 | return map;
21 | } else if (format && format.keyName) {
22 | // 例如以id为key名称
23 | // [{ id: "xx", name: "yy" }] => { xx: { id: "xx", name: "yy" } }
24 | data.forEach(item => {
25 | map[item[format.keyName]] = item;
26 | });
27 | return map;
28 | } else {
29 | console.error("请输入keyName或valueName");
30 | return;
31 | }
32 | }
33 |
34 | /**
35 | * 时间格式化
36 | * @param {Number} num
37 | * @return {Date}
38 | */
39 | export function timeFormat(time, formatStr = "YYYY-MM-DD HH:mm") {
40 | if (!time) return "--";
41 | return time ? dayjs(time).format(formatStr) : "";
42 | }
43 |
44 | /**
45 | * 类型检测
46 | * @param {Any} value
47 | * @return {String}
48 | * 返回类型字符如: Array String Number
49 | */
50 | export function getType(value) {
51 | let type = Object.prototype.toString.call(value);
52 | type = type.replace(/^\[object\s/, "");
53 | type = type.replace(/\]$/, "");
54 | return type;
55 | }
56 |
57 | /**
58 | * 遍历树节点
59 | * @param {Array} data
60 | * @param {Function} callBack
61 | * @return {null}
62 | * [{
63 | * id:1,
64 | * name:"1",
65 | * children:[{
66 | * id:1,
67 | * name:"1",
68 | * }]
69 | * }]
70 | * **/
71 | export function traverseArrayTree(
72 | data = [],
73 | childNodeKey = "children",
74 | callBack
75 | ) {
76 | if (Array.isArray(data)) {
77 | data.forEach(item => {
78 | typeof callBack == "function" && callBack(item);
79 | if (item[childNodeKey] && Array.isArray(item[childNodeKey])) {
80 | traverseArrayTree(item[childNodeKey], childNodeKey, callBack);
81 | }
82 | });
83 | } else {
84 | throw new error("请传入数组");
85 | }
86 | }
87 |
88 | /**
89 | * 函数节流
90 | * @param {Function} fun
91 | * @param {Number} wait
92 | * @return {Function}
93 | */
94 | export function throttle(fn, wait) {
95 | var pre = Date.now();
96 | return function () {
97 | var context = this;
98 | var args = arguments;
99 | var now = Date.now();
100 | if (now - pre >= wait) {
101 | fn.apply(context, args);
102 | pre = Date.now();
103 | }
104 | };
105 | }
106 |
107 | /**
108 | * 函数防抖
109 | * @param {Function} fun
110 | * @param {Number} delay
111 | * @return {Function}
112 | */
113 | export function debounce(fn, delay) {
114 | // 记录上一次的延时器
115 | var timer = null;
116 | return function () {
117 | // 清除上一次延时器
118 | var context = this;
119 | var args = arguments;
120 | clearTimeout(timer);
121 | timer = setTimeout(function () {
122 | fn.apply(context, args);
123 | }, delay);
124 | };
125 | }
126 |
127 | /**
128 | * 获取客户端浏览器内容窗口宽高
129 | * @param null
130 | * @return {Object}
131 | */
132 | export const getClientRect = () => {
133 | let currentClientHeight =
134 | document.documentElement.clientHeight || document.body.clientHeight;
135 | let currentClientWidth =
136 | document.documentElement.clientWidth || document.body.clientWidth;
137 |
138 | return {
139 | clientWidth: currentClientWidth,
140 | clientHeight: currentClientHeight,
141 | };
142 | };
143 |
--------------------------------------------------------------------------------
/src/others/validator.js:
--------------------------------------------------------------------------------
1 | import { Message } from "element-ui";
2 | /**
3 | * 数据正则验证器
4 | */
5 |
6 | /**
7 | * 验证电话号码
8 | * @param {Number/String} phone
9 | * @return {Boolean}
10 | */
11 | export const validPhone = (phone, errMsg = "") => {
12 | const phoneRule =
13 | /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-7|9])|(?:5[0-3|5-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[1|8|9]))\d{8}$/;
14 | let status = phoneRule.test(phone);
15 | if (!status) {
16 | if (errMsg) Message({ type: "error", message: errMsg });
17 | }
18 |
19 | return status;
20 | };
21 |
22 | /**
23 | * 验证邮箱
24 | * @param {String} email
25 | * @return {Boolean}
26 | */
27 | export const validEmail = (email, errMsg = "") => {
28 | const emailRule =
29 | /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
30 | let status = emailRule.test(email);
31 | if (!status) {
32 | if (errMsg) Message({ type: "error", message: errMsg });
33 | }
34 |
35 | return status;
36 | };
37 |
38 | /**
39 | * 验证内容不为空
40 | * @param {String} content
41 | * @return {Boolean}
42 | */
43 | export const validNotEmpty = (content, errMsg = "") => {
44 | if (content == null) {
45 | if (errMsg) Message({ type: "error", message: errMsg });
46 | return false;
47 | }
48 | const notEmptyRule = /\S/;
49 | let status = notEmptyRule.test(content);
50 | if (!status) {
51 | if (errMsg) Message({ type: "error", message: errMsg });
52 | }
53 |
54 | return status;
55 | };
56 |
57 | /**
58 | * 验证数组不为空
59 | * @param {Array} contentList
60 | * @return {Boolean}
61 | */
62 | export const validArrayNotEmpty = (contentList = [], errMsg = "") => {
63 | let status = contentList.length == 0 ? false : true;
64 | if (!status) {
65 | if (errMsg) Message({ type: "error", message: errMsg });
66 | }
67 |
68 | return status;
69 | };
70 |
--------------------------------------------------------------------------------
/src/pages/404/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 | 返回
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/pages/home/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
6 |
7 |
欢迎登陆!
8 |
{{ appName }}
9 |
10 |
11 |
15 |
16 |
17 |
29 |
30 |
31 |
91 |
--------------------------------------------------------------------------------
/src/pages/login/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ appName }}
6 | 欢迎登录!
7 |
8 |
9 |
10 |
11 | {{ appName }}
12 |
13 | {{ loginStatus ? "登录" : "注册" }}
14 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
31 |
37 | 登录
38 |
39 |
40 |
41 | 注册
42 |
43 | 第三方登录
44 |
45 |
46 |
47 |
53 |
54 |
58 |
59 |
60 |
65 |
66 |
67 |
68 |
72 |
76 |
77 |
78 |
79 |
84 | 注册
85 |
86 |
87 |
88 | 密码登录
89 |
90 | 第三方登录
91 |
92 |
93 |
94 |
95 |
Code By FEZIRO
96 |
97 |
98 |
99 |
100 |
101 |
197 |
198 |
296 |
297 |
298 |
--------------------------------------------------------------------------------
/src/pages/role-management/components/RoleEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
14 |
15 |
16 |
17 |
24 |
25 |
26 |
27 |
28 |
29 |
37 |
38 |
39 |
40 |
41 |
42 |
48 |
49 |
50 |
51 |
52 |
53 | 关闭
56 | 保存
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
166 |
167 |
--------------------------------------------------------------------------------
/src/pages/role-management/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 | 添加
22 | 删除
30 |
31 |
38 |
45 |
46 |
52 |
53 |
54 |
55 |
61 |
62 | 编辑
69 |
70 |
71 |
72 |
73 |
74 |
75 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
189 |
190 |
--------------------------------------------------------------------------------
/src/pages/user-management/components/UserEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
14 |
15 |
16 |
21 |
22 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
43 |
44 |
45 |
46 |
47 |
48 |
55 |
61 |
62 |
63 |
64 |
65 |
66 |
72 |
73 |
74 |
75 |
76 |
82 |
88 |
89 |
90 |
91 |
92 |
93 |
103 |
104 |
105 |
106 |
189 |
190 |
--------------------------------------------------------------------------------
/src/pages/user-management/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
26 |
27 |
28 |
29 | 添加
36 | 删除
44 |
45 |
52 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | {{ scope.row.role }}
73 |
74 |
75 |
76 |
82 |
83 |
84 |
85 |
91 |
92 |
98 |
99 | 编辑
105 |
106 |
107 | 暂无数据
108 |
109 |
121 |
125 |
126 |
127 |
128 |
222 |
223 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHashHistory } from "vue-router";
2 | import NProgress from "nprogress";
3 |
4 | //路径映射页面组件
5 | const pageUrlMap = {
6 | "/home": () => import("@/pages/home/index.vue"),
7 | "/system/user-management": () => import("@/pages/user-management/index.vue"),
8 | "/system/role-management": () => import("@/pages/role-management/index.vue"),
9 | };
10 |
11 | //静态路由
12 | const constantRoute = [
13 | {
14 | name: "登录",
15 | path: "/login",
16 | component: () => import("@/pages/login/index.vue"),
17 | meta: {
18 | title: "登录",
19 | auth: false,
20 | tagsView: false,
21 | tagsViewAffix: false,
22 | },
23 | },
24 | {
25 | name: "404",
26 | path: "/404",
27 | component: () => import("@/pages/404/index.vue"),
28 | meta: {
29 | title: "404页面未找到",
30 | auth: false,
31 | tagsView: false,
32 | tagsViewAffix: false,
33 | },
34 | },
35 | {
36 | path: "/:patchAll(\\S+)",
37 | redirect: "/404",
38 | },
39 | ];
40 |
41 | const router = createRouter({
42 | routes: constantRoute,
43 | history: createWebHashHistory(),
44 | });
45 |
46 | //路由拦截
47 | router.beforeEach((to, from, next) => {
48 | NProgress.start();
49 | if (to.meta && to.meta.title) {
50 | document.title = to.meta.title;
51 | }
52 | let userInfo = localStorage.getItem("userInfo") || null;
53 |
54 | if (to.fullPath == "/" && !userInfo) {
55 | router.replace("/login");
56 | return;
57 | }
58 |
59 | if (to.meta.auth) {
60 | if (!userInfo) {
61 | router.push({ path: "/login" });
62 | } else {
63 | next();
64 | }
65 | } else {
66 | next();
67 | }
68 | });
69 |
70 | router.afterEach(() => {
71 | NProgress.done();
72 | });
73 |
74 | //动态路由
75 | export function createRoute(menu) {
76 | if (!menu || menu.length == 0) return;
77 | for (let i = 0; i < menu.length; i++) {
78 | let menuItem = menu[i];
79 | if (menuItem.type === "group") {
80 | createRoute(menuItem.children);
81 | } else {
82 | router.addRoute({
83 | name: menuItem.name,
84 | path: menuItem.url || "",
85 | component: menuItem.url ? pageUrlMap[menuItem.url] : "",
86 | meta: {
87 | title: menuItem.name || "",
88 | auth: true,
89 | layout: menuItem.layout || "admin",
90 | tagsView: menuItem.tagsView || true,
91 | tagsViewAffix: menuItem.tagsViewAffix || false,
92 | breadcrumb: menuItem.breadcrumb || [],
93 | },
94 | });
95 | }
96 | }
97 | router.addRoute({
98 | path: "/",
99 | redirect: "/home",
100 | });
101 | }
102 | //刷新时浏览器路由不丢失
103 | let menu = localStorage.getItem("menu");
104 | menu && createRoute(JSON.parse(menu));
105 |
106 | //重置路由(用于退出登录时重置路由)
107 | export function resetRouter() {
108 | const newRouter = createRouter({
109 | routes: constantRoute,
110 | history: createWebHashHistory(),
111 | });
112 | router.matcher = newRouter.matcher;
113 | }
114 |
115 | export default router;
116 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from "vuex";
2 | import app from "@/store/modules/app.js";
3 | import tagsView from "@/store/modules/tags-view.js";
4 |
5 | export default createStore({
6 | modules: {
7 | app,
8 | tagsView,
9 | },
10 | devtools: true,
11 | });
12 |
--------------------------------------------------------------------------------
/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * app全局配置
3 | */
4 |
5 | import logo from "../../assets/images/logo.png";
6 | import { getClientRect } from "@/others/utils";
7 | const getCachePageIndicator = () => {
8 | let val = localStorage.getItem("pageIndicator");
9 | return val;
10 | };
11 | const getCacheMenuCollapse = () => {
12 | let val = localStorage.getItem("menuCollapse");
13 | return val;
14 | };
15 | const getCachePageKeepAlive = () => {
16 | let val = localStorage.getItem("getCachePageKeepAlive");
17 | return val;
18 | };
19 |
20 | export default {
21 | namespaced: true,
22 | state: () => ({
23 | //平台名称
24 | appName: "Vue3-Element+管理系统",
25 | //平台logo
26 | appLogo: logo,
27 | //端标识(移动端"mobile"/ 桌面端"desktop")
28 | device: "desktop",
29 | //页面指示器("面包屑"/"标签切换")
30 | pageIndicator: getCachePageIndicator() || "面包屑",
31 | //菜单折叠
32 | menuCollapse: getCacheMenuCollapse() || 0,
33 | //页面缓存
34 | pageKeepAlive: getCachePageKeepAlive() || 0,
35 | //客户端窗口尺寸宽高(resize动态赋值)
36 | windowRect: {
37 | clientHeight: getClientRect().clientHeight,
38 | clientWidth: getClientRect().clientWidth,
39 | },
40 | }),
41 |
42 | getters: {
43 | userInfo() {
44 | let userInfo = localStorage.getItem("userInfo");
45 | return userInfo ? JSON.parse(userInfo) : {};
46 | },
47 | menu() {
48 | let menu = localStorage.getItem("menu");
49 | return menu ? JSON.parse(menu) : [];
50 | },
51 | },
52 |
53 | //用于同步操作state
54 | mutations: {
55 | SET_DEVICE(state, device) {
56 | state.device = device;
57 | if (device === "mobile") this.commit("app/TOGGLE_MENU_COLLAPSE", 1);
58 | if (device === "desktop") this.commit("app/TOGGLE_MENU_COLLAPSE", 0);
59 | },
60 |
61 | TOGGLE_MENU_COLLAPSE(state, collapse) {
62 | if (state.menuCollapse === collapse) return;
63 | state.menuCollapse = collapse;
64 | localStorage.setItem("menuCollapse", state.menuCollapse);
65 | },
66 |
67 | SET_MENU_TAG_SWITCHER(state, val = "面包屑") {
68 | state.pageIndicator = val;
69 | localStorage.setItem("pageIndicator", state.pageIndicator);
70 | },
71 |
72 | SET_PAGE_KEEP_ALIVE(state, val = 0) {
73 | state.pageKeepAlive = val;
74 | localStorage.setItem("pageKeepAlive", state.pageKeepAlive);
75 | },
76 |
77 | SET_WINDOW_RECT(state, val) {
78 | state.windowRect = {
79 | clientHeight: val.clientHeight || 0,
80 | clientWidth: val.clientWidth || 0,
81 | };
82 | },
83 | },
84 |
85 | //用于异步操作state
86 | actions: {},
87 | };
88 |
--------------------------------------------------------------------------------
/src/store/modules/tags-view.js:
--------------------------------------------------------------------------------
1 | /**
2 | * tagsView标签切换页面
3 | *
4 | */
5 |
6 | // visitedRoutes本地缓存(localStorage)
7 | const getCacheVisitedRoutes = () => {
8 | let visitedRoutes = localStorage.getItem("visitedRoutes");
9 | return visitedRoutes ? JSON.parse(visitedRoutes) : [];
10 | };
11 | const setCacheVisitedRoutes = visitedRoutes => {
12 | localStorage.setItem("visitedRoutes", JSON.stringify(visitedRoutes));
13 | };
14 |
15 | export default {
16 | namespaced: true,
17 | state: () => ({
18 | visitedRoutes: getCacheVisitedRoutes(),
19 | }),
20 | mutations: {
21 | //添加
22 | ADD_VISITED_ROUTE(state, route) {
23 | let target = state.visitedRoutes.find(item => item.path === route.path);
24 | if (target) {
25 | if (route.fullPath !== target.fullPath) Object.assign(target, route);
26 | return;
27 | }
28 | state.visitedRoutes.push({ ...target, ...route });
29 | setCacheVisitedRoutes(state.visitedRoutes);
30 | },
31 | //删除
32 | DELETE_VISITED_ROUTE(state, route) {
33 | state.visitedRoutes.forEach((item, index) => {
34 | if (item.path === route.path) state.visitedRoutes.splice(index, 1);
35 | });
36 | setCacheVisitedRoutes(state.visitedRoutes);
37 | },
38 | //删除除了本身以外的
39 | DELETE_OTHERS_VISITED_ROUTE(state, route) {
40 | state.visitedRoutes = state.visitedRoutes.filter(
41 | item => item.path == route.path
42 | );
43 | setCacheVisitedRoutes(state.visitedRoutes);
44 | console.log("state", state);
45 | },
46 | //更新当前
47 | UPDATE_VISITED_ROUTE(state, route) {
48 | state.visitedRoutes.forEach(item => {
49 | if (item.path === route.path) {
50 | console.log("refresh", item);
51 | item = Object.assign(item, route);
52 | }
53 | });
54 | },
55 | },
56 | };
57 |
--------------------------------------------------------------------------------
/src/styles/animation.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 动画
3 | */
4 |
5 | /************** Vue 组件动画 ***************/
6 |
7 | .fade {
8 |
9 | &-enter-active,
10 | &-leave-active {
11 | transition: all 0.3s ease;
12 | }
13 |
14 | &-enter-from,
15 | &-leave-to {
16 | opacity: 0;
17 | transform: translateY(-1000px);
18 | }
19 | }
20 |
21 |
22 |
23 | /************** Css动画 ***************/
24 |
25 | //上划出
26 | @keyframes slideUp {
27 | 0% {
28 | opacity: 0;
29 | transform: translateY(60px);
30 | }
31 |
32 | 100% {
33 | opacity: 1;
34 | transform: translateY(0);
35 | }
36 | }
37 |
38 |
39 | //上下循环跳动
40 | @keyframes upAndDown {
41 | 0% {
42 | transform: translateY(0);
43 | }
44 |
45 | 50% {
46 | transform: translateY(10px);
47 | }
48 |
49 | 100% {
50 | transform: translateY(0px);
51 | }
52 | }
53 |
54 | //移动端侧边栏滑入
55 | @keyframes sideBarSlipIn {
56 | 0% {
57 | transform: translateX(0px);
58 | }
59 |
60 | 100% {
61 | transform: translateX(-500px);
62 | }
63 | }
64 |
65 | //移动端侧边栏滑出
66 | @keyframes sideBarSlipOut {
67 | 0% {
68 | transform: translateX(-500px);
69 | }
70 |
71 | 100% {
72 | transform: translateX(0px);
73 | }
74 | }
--------------------------------------------------------------------------------
/src/styles/color.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 颜色
3 | */
4 |
5 | $primaryColor:#36ad6a;
6 | $secondaryColor:rgb($primaryColor, 0.1);
7 | $bgColor:#f6f7f9;
8 |
9 | $blue:#409EFF;
10 | $red: #F56C6C;
11 | $green:#67C23A;
12 | $yellow:#E6A23C;
13 |
14 | //app背景色
15 | $appBgColor:#fff;
16 |
--------------------------------------------------------------------------------
/src/styles/custom-default-browser-style.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 自定义浏览器默认样式
3 | */
4 |
5 | //优化滚动条样式
6 | * {
7 | /* for Firefox: */
8 | scrollbar-color: rgba(#000, 0.3) transparent;
9 | scrollbar-width: thin;
10 |
11 | &:focus {
12 | outline: none;
13 | }
14 |
15 | }
16 |
17 |
18 | ::-webkit-scrollbar {
19 | width: 8px;
20 | height: 8px;
21 | }
22 |
23 | //滚动条轨道
24 | ::-webkit-scrollbar-track {
25 | background: transparent;
26 | }
27 |
28 | //滚动条
29 | ::-webkit-scrollbar-thumb {
30 | background-color: rgba(#999, 0.5);
31 | background-clip: padding-box;
32 | -webkit-border-radius: 2px;
33 | -moz-border-radius: 2px;
34 | border-radius: 2px;
35 | transition: all 1s;
36 | cursor: pointer;
37 | }
38 |
39 | //滚动条鼠标悬停
40 | ::-webkit-scrollbar-thumb:hover {
41 | background-color: rgba(#999, 1);
42 | }
--------------------------------------------------------------------------------
/src/styles/element-variables.scss:
--------------------------------------------------------------------------------
1 | @import "./color.scss";
2 |
3 | /**
4 | * 自定义elementUI主题色
5 | */
6 |
7 | $--color-primary:$primaryColor;
8 | /* 改变 icon 字体路径变量,必需 */
9 | $--font-path: 'element-plus/lib/theme-chalk/fonts';
10 |
11 | @import "element-plus/packages/theme-chalk/src/index";
12 |
13 |
14 | /********************重置elementUI组件某些样式******************/
15 |
16 | //全局设置tab的细线
17 | .el-tabs__nav-wrap::after {
18 | height: 1px;
19 | }
20 |
21 |
22 | .el-overlay {
23 | display: flex;
24 | justify-content: center;
25 | align-items: center;
26 | }
27 |
28 | //全局弹窗居中, 内容溢出滚动
29 |
30 | .el-dialog {
31 | display: flex;
32 | flex-direction: column;
33 | margin: 0 !important;
34 | max-height: calc(100% - 30px);
35 | max-width: calc(100% - 30px);
36 |
37 | @media screen and (max-width:1024px) {
38 | width: 90% !important;
39 | }
40 | }
41 |
42 | .el-dialog__wrapper {
43 | transition-duration: 0.3s;
44 | }
45 |
46 | .el-dialog .el-dialog__body {
47 | flex: 1;
48 | overflow: auto;
49 | padding-top: 10px;
50 | padding-bottom: 10px;
51 | }
52 |
53 | .el-dialog__header {
54 | text-align: left;
55 | }
56 |
57 | .el-dialog__wrapper {
58 | background: rgba(#000, 0.3);
59 | }
60 |
61 | .el-radio-group {
62 | white-space: nowrap;
63 | }
64 |
65 | .el-image-viewer__mask {
66 | opacity: 0.8;
67 | }
68 |
69 |
70 | .el-image {
71 | .el-icon-circle-close::before {
72 | color: #fff;
73 | }
74 | }
75 |
76 | .el-tree-node__content {
77 | padding: 4px 0;
78 | }
79 |
80 | .el-cascader__tags {
81 | input {
82 | &::placeholder {
83 | color: transparent;
84 | }
85 | }
86 | }
87 |
88 | .el-textarea__inner,
89 | .el-input__inner {
90 | border-radius: 2px;
91 | background-color: rgba(#000, 0.03);
92 | border: 1px solid transparent;
93 | transition: all 0.5s;
94 | box-sizing: border-box;
95 | }
96 |
97 | .el-range-input {
98 | background: transparent;
99 | }
100 |
101 | .el-input-group__append {
102 | border: none;
103 | }
104 |
105 | .el-tag {
106 | border-radius: 2px;
107 | }
108 |
109 |
110 | .el-button {
111 | border-radius: 2px;
112 | }
113 |
114 | .el-button--default {
115 | border: none;
116 | outline: none;
117 | background-color: rgba(#000, 0.05);
118 | }
119 |
120 | .el-menu.el-menu--horizontal {
121 | border-bottom: 0;
122 | }
123 |
124 | .el-form--label-top .el-form-item__label {
125 | padding-bottom: 0 !important;
126 | }
127 |
128 | .el-form-item {
129 | margin-bottom: 15px !important;
130 | }
131 |
132 | .el-message {
133 | border: none;
134 | box-shadow: 0 5px 5px 0 rgba(#000, 0.02);
135 |
136 | @media screen and (max-width:750px) {
137 | min-width: 80% !important;
138 | }
139 | }
--------------------------------------------------------------------------------
/src/styles/global.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 全局样式表
3 | */
4 |
5 | @import "./color.scss";
6 | @import "./custom-default-browser-style.scss";
7 | @import "./mixin.scss";
8 | @import "./animation.scss";
9 |
10 |
11 | //头部导航栏高度
12 | $appHeaderHeight:62px;
13 | //侧边导航栏宽度
14 | $sideMenuWidth:220px;
15 |
16 |
17 | //管理端面包屑高度
18 | $breadcrumbHeight:50px;
19 | //管理端主内容高度
20 | $adminContentHeight:calc(100vh - #{$appHeaderHeight} - #{$breadcrumbHeight} - 2px);
21 |
22 |
23 | #nprogress .bar {
24 | background: $primaryColor;
25 | height: 3px;
26 | }
27 |
28 |
29 | body {
30 | font-family: "Avenir", Helvetica, Arial, sans-serif;
31 | -webkit-font-smoothing: antialiased;
32 | -moz-osx-font-smoothing: grayscale;
33 | color: #000;
34 | width: 100%;
35 | height: 100%;
36 | }
--------------------------------------------------------------------------------
/src/styles/mixin.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 全局scss的mixin
3 | */
4 | @mixin flex-row-center-center {
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | }
9 |
10 | @mixin flex-col-center-center {
11 | display: flex;
12 | flex-direction: column;
13 | justify-content: center;
14 | align-items: center;
15 | }
16 |
17 |
18 | //单行溢出
19 | @mixin ellipsis-singe-line {
20 | white-space: nowrap;
21 | overflow: hidden;
22 | text-overflow: ellipsis;
23 | }
24 |
25 | //多行溢出
26 | @mixin ellipsis-multi-line($line) {
27 | display: -webkit-box;
28 | -webkit-box-orient: vertical;
29 | -webkit-line-clamp: $line;
30 | overflow: hidden;
31 | }
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import vue from "@vitejs/plugin-vue";
2 | import { visualizer } from "rollup-plugin-visualizer";
3 | import strip from "@rollup/plugin-strip";
4 | import viteCompression from "vite-plugin-compression";
5 | const path = require("path");
6 |
7 | export default {
8 | base: './',
9 | plugins: [
10 | vue(),
11 | //正式环境打包查看各文件大小占比
12 | visualizer({
13 | open: true,
14 | gzipSize: true,
15 | brotliSize: true,
16 | }),
17 | //正式环境打包去除调试语句
18 | {
19 | ...strip({
20 | include: ["**/*.js", "**/*.vue", "**/*.ts", "**/*.jsx"],
21 | }),
22 | apply: "build",
23 | },
24 | //打包开启gzip压缩
25 | viteCompression(),
26 | ],
27 | resolve: {
28 | alias: {
29 | // 键必须以斜线开始和结束
30 | "@": path.resolve(__dirname, "./src"),
31 | },
32 | },
33 | css: {
34 | preprocessorOptions: {
35 | scss: {
36 | //添加scss全局变量样式
37 | additionalData: "@import './src/styles/global.scss';",
38 | },
39 | },
40 | },
41 | server: {
42 | // 配置调试服务器主机名,如果允许外部访问,可设置为"0.0.0.0"
43 | host: "0.0.0.0",
44 | port: 3000, // 服务器端口号
45 | open: true, // 是否自动打开浏览器
46 | },
47 | };
48 |
--------------------------------------------------------------------------------