
diff --git a/src/api/model/auth.ts b/src/api/model/auth.ts
index ed73e0d..ddb3a7f 100644
--- a/src/api/model/auth.ts
+++ b/src/api/model/auth.ts
@@ -1,13 +1,19 @@
import http from "@/utils/request"
export default {
+ captcha: async function (data = {}) {
+ return await http.get("v1/captcha", data);
+ },
login: async function (data = {}) {
return await http.post("v1/login", data);
},
+ info: async function () {
+ return await http.get("v1/info");
+ },
menu: async function (data = {}) {
return await http.get("v1/menu", data)
},
- captcha: async function(data = {}){
- return await http.get("v1/captcha", data);
+ logout: async function(){
+ return await http.get("v1/logout");
},
}
diff --git a/src/assets/icons/Brush.vue b/src/assets/icons/Brush.vue
new file mode 100644
index 0000000..0d0350c
--- /dev/null
+++ b/src/assets/icons/Brush.vue
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/icons/Task.vue b/src/assets/icons/Task.vue
new file mode 100644
index 0000000..058bae9
--- /dev/null
+++ b/src/assets/icons/Task.vue
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/icons/index.js b/src/assets/icons/index.js
new file mode 100644
index 0000000..d451041
--- /dev/null
+++ b/src/assets/icons/index.js
@@ -0,0 +1,17 @@
+// 导入当前目录下的所有图标组件
+const files = import.meta.glob('./*.vue', {
+ eager: true,
+ import: 'default' // 可选,指定要导入的 export
+})
+const modules = {}
+
+for (const path in files) {
+ // 获取组件名,可以根据需要修改这里的逻辑
+ const componentName = path.replace(/^\.\/(.*)\.\w+$/, '$1');
+ // 全局注册组件
+ modules[componentName] = files[path];
+}
+
+export default {
+ ...modules
+}
diff --git a/src/config/index.ts b/src/config/index.ts
index cdde218..6343ab7 100644
--- a/src/config/index.ts
+++ b/src/config/index.ts
@@ -1,8 +1,10 @@
export default {
- //
+ // 应用名称
APP_NAME: import.meta.env.VITE_APP_TITLE,
//接口地址
API_URL: import.meta.env.VITE_API_BASE,
+ // websocket
+ WS_URL: import.meta.env.VITE_WS_URL,
//请求超时
TIMEOUT: 10000,
//请求是否开启缓存
diff --git a/src/layout/404.vue b/src/layout/404.vue
index 432cf07..23e3059 100644
--- a/src/layout/404.vue
+++ b/src/layout/404.vue
@@ -1,31 +1,31 @@
-

+
无权限或找不到页面
当前页面无权限访问或者打开了一个不存在的链接,请检查当前账户权限和链接的可访问性。
-
返回首页
-
重新登录
-
返回上一页
+
返回首页
+
重新登录
+
返回上一页
-
diff --git a/src/layout/components/NavMenu.vue b/src/layout/components/NavMenu.vue
new file mode 100644
index 0000000..8d4d083
--- /dev/null
+++ b/src/layout/components/NavMenu.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ {}'>
+
+
+ {{navMenu.meta.title}}
+
+
+
+
+
+
+ {{navMenu.meta.title}}
+
+
+
+
+
+
+
+
diff --git a/src/layout/components/iframeView.vue b/src/layout/components/iframeView.vue
new file mode 100644
index 0000000..0c7e4f6
--- /dev/null
+++ b/src/layout/components/iframeView.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/layout/components/message.vue b/src/layout/components/message.vue
new file mode 100644
index 0000000..664194c
--- /dev/null
+++ b/src/layout/components/message.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+ 消息中心
+ 全部已读
+
+
+
+
+
+
+
diff --git a/src/layout/components/search.vue b/src/layout/components/search.vue
new file mode 100644
index 0000000..2c61081
--- /dev/null
+++ b/src/layout/components/search.vue
@@ -0,0 +1,142 @@
+
+
+
+
+ {{item}}
+
+
+
暂无搜索结果
+
+
+ -
+
+ {{ item.breadcrumb }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layout/components/setting.vue b/src/layout/components/setting.vue
new file mode 100644
index 0000000..fba4cd3
--- /dev/null
+++ b/src/layout/components/setting.vue
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layout/components/sideM.vue b/src/layout/components/sideM.vue
new file mode 100644
index 0000000..5762f0f
--- /dev/null
+++ b/src/layout/components/sideM.vue
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
{{ $CONFIG.APP_NAME }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layout/components/tags.vue b/src/layout/components/tags.vue
new file mode 100644
index 0000000..72312f1
--- /dev/null
+++ b/src/layout/components/tags.vue
@@ -0,0 +1,355 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layout/components/tasks.vue b/src/layout/components/tasks.vue
new file mode 100644
index 0000000..70cdbd8
--- /dev/null
+++ b/src/layout/components/tasks.vue
@@ -0,0 +1,83 @@
+
+
+
+
+
+ 没有正在执行的任务
+
+
+
+
+
+
+
+
+
+
{{ task.task_name }}
+
创建
+
+
+
+ 待处理
+ 处理中
+ 完成
+ 失败
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layout/components/topbar.vue b/src/layout/components/topbar.vue
new file mode 100644
index 0000000..8bb1434
--- /dev/null
+++ b/src/layout/components/topbar.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+ {{item.meta.title}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layout/components/userbar.vue b/src/layout/components/userbar.vue
new file mode 100644
index 0000000..e365fd8
--- /dev/null
+++ b/src/layout/components/userbar.vue
@@ -0,0 +1,297 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ realnameF }}
+
+
+
+
+
+
+
+ 帐号信息
+ 清除缓存
+ 退出登录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layout/index.vue b/src/layout/index.vue
index 39f41e0..3047390 100644
--- a/src/layout/index.vue
+++ b/src/layout/index.vue
@@ -1,11 +1,291 @@
-
+
+
+
+
+
+
+
{{ pmenu.meta.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ pmenu.meta.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
\ No newline at end of file
diff --git a/src/pi.ts b/src/pi.ts
index ec78539..040ec75 100644
--- a/src/pi.ts
+++ b/src/pi.ts
@@ -1,5 +1,7 @@
import * as elIcons from '@element-plus/icons-vue'
import {App} from "vue";
+import * as piIcons from '@assets/icons'
+import errorHandler from "@/utils/errorHandler";
export default {
install(app: App) {
@@ -7,5 +9,12 @@ export default {
for (let icon in elIcons) {
app.component(`ElIcon${icon}`, elIcons[icon])
}
+ //统一注册sc-icon图标
+ for(let icon in piIcons.default){
+ app.component(`PiIcon${icon}`, piIcons.default[icon])
+ }
+
+ //全局代码错误捕捉
+ app.config.errorHandler = errorHandler
}
}
diff --git a/src/router/index.ts b/src/router/index.ts
index cc4b51a..6976654 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -6,6 +6,7 @@ import 'nprogress/nprogress.css'
import tools from '@/utils/tools';
import api from "@/api";
import sRouter from './system';
+import {treeFilter, filterAsyncRouter, flatAsyncRoutes} from '@/utils/route'
import {beforeEach, afterEach} from '@/utils/route';
//系统路由
@@ -68,20 +69,19 @@ router.beforeEach(async (to, from, next) => {
//加载动态/静态路由
if (!isGetRouter) {
// 动态加载菜单
- // const [res, err] = await tools.go(api.auth.menu())
- // console.log(res)
- // if (err) {
- // return false
- // }
- // tools.data.set("MENU", tools.makeMenu(res.data.menus, 0))
- // tools.data.set("PERMISSIONS", res.data.buttons)
- // tools.data.set("ROLE", res.data.roles)
+ /* @ts-ignore */
+ const res = await api.auth.menu()
+
+ tools.data.set("MENU", tools.makeMenu(res.data.menus, 0))
+ tools.data.set("PERMISSIONS", res.data.buttons)
+ tools.data.set("ROLE", res.data.roles)
let apiMenu = tools.data.get("MENU") || []
let userInfo = tools.data.get("USER_INFO")
let userMenu = treeFilter([], node => {
return node.meta.role ? node.meta.role.filter(item => userInfo.role.indexOf(item) > -1).length > 0 : true
})
+
let menu = [...userMenu, ...apiMenu]
var menuRouter = filterAsyncRouter(menu)
menuRouter = flatAsyncRoutes(menuRouter)
@@ -111,82 +111,4 @@ router.onError((error) => {
});
});
-//入侵追加自定义方法、对象
-// router.sc_getMenu = () => {
-// var apiMenu = tools.data.get("MENU") || []
-// let userInfo = tools.data.get("USER_INFO")
-// let userMenu = treeFilter([], node => {
-// return node.meta.role ? node.meta.role.filter(item=>userInfo.role.indexOf(item)>-1).length > 0 : true
-// })
-// var menu = [...userMenu, ...apiMenu]
-// return menu
-// }
-
-//转换
-function filterAsyncRouter(routerMap) {
- const accessedRouters = []
- routerMap.forEach(item => {
- item.meta = item.meta ? item.meta : {};
- //处理外部链接特殊路由
- if (item.meta.type == 'iframe') {
- item.meta.url = item.path;
- item.path = `/i/${item.name}`;
- }
- //MAP转路由对象
- var route = {
- path: item.path,
- name: item.name,
- meta: item.meta,
- redirect: item.redirect,
- children: item.children ? filterAsyncRouter(item.children) : null,
- component: loadComponent(item.component)
- }
- accessedRouters.push(route)
- })
- return accessedRouters
-}
-
-function loadComponent(component: string) {
- if (component) {
- return () => import(/* @vite-ignore */`@/views/${component}`)
- } else {
- return () => import(`@/layout/empty`)
- }
-
-}
-
-//路由扁平化
-function flatAsyncRoutes(routes, breadcrumb = []) {
- let res = []
- routes.forEach(route => {
- const tmp = {...route}
- if (tmp.children) {
- let childrenBreadcrumb = [...breadcrumb]
- childrenBreadcrumb.push(route)
- let tmpRoute = {...route}
- tmpRoute.meta.breadcrumb = childrenBreadcrumb
- delete tmpRoute.children
- res.push(tmpRoute)
- let childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb)
- childrenRoutes.map(item => {
- res.push(item)
- })
- } else {
- let tmpBreadcrumb = [...breadcrumb]
- tmpBreadcrumb.push(tmp)
- tmp.meta.breadcrumb = tmpBreadcrumb
- res.push(tmp)
- }
- })
- return res
-}
-
-//过滤树
-function treeFilter(tree, func) {
- return tree.map(node => ({...node})).filter(node => {
- node.children = node.children && treeFilter(node.children, func)
- return func(node) || (node.children && node.children.length)
- })
-}
-
export default router
diff --git a/src/store/model/global.ts b/src/store/model/global.ts
new file mode 100644
index 0000000..b07ea6a
--- /dev/null
+++ b/src/store/model/global.ts
@@ -0,0 +1,28 @@
+import tools from "@/utils/tools";
+
+export default {
+ state: {
+ //移动端布局
+ ismobile: false,
+ //布局
+ layout: tools.data.get('APP_LAYOUT') || 'default',
+ //菜单是否折叠 toggle
+ menuIsCollapse: false,
+ //多标签栏
+ layoutTags: true
+ },
+ mutations: {
+ SET_ismobile(state, key){
+ state.ismobile = key
+ },
+ SET_layout(state, key){
+ state.layout = key
+ },
+ TOGGLE_menuIsCollapse(state){
+ state.menuIsCollapse = !state.menuIsCollapse
+ },
+ TOGGLE_layoutTags(state){
+ state.layoutTags = !state.layoutTags
+ }
+ }
+}
diff --git a/src/store/model/iframe.ts b/src/store/model/iframe.ts
new file mode 100644
index 0000000..f139578
--- /dev/null
+++ b/src/store/model/iframe.ts
@@ -0,0 +1,38 @@
+export default {
+ state: {
+ iframeList: []
+ },
+ mutations: {
+ setIframeList(state, route){
+ state.iframeList = []
+ state.iframeList.push(route)
+ },
+ pushIframeList(state, route){
+ let target = state.iframeList.find((item) => item.path === route.path)
+ if(!target){
+ state.iframeList.push(route)
+ }
+ },
+ removeIframeList(state, route){
+ state.iframeList.forEach((item, index) => {
+ if (item.path === route.path){
+ state.iframeList.splice(index, 1)
+ }
+ })
+ },
+ refreshIframe(state, route){
+ state.iframeList.forEach((item) => {
+ if (item.path == route.path){
+ var url = route.meta.url;
+ item.meta.url = '';
+ setTimeout(function() {
+ item.meta.url = url
+ }, 200);
+ }
+ })
+ },
+ clearIframeList(state){
+ state.iframeList = []
+ }
+ }
+}
diff --git a/src/store/model/keepAlive.ts b/src/store/model/keepAlive.ts
new file mode 100644
index 0000000..c143cf5
--- /dev/null
+++ b/src/store/model/keepAlive.ts
@@ -0,0 +1,34 @@
+export default {
+ state: {
+ keepLiveRoute: [],
+ routeKey: null,
+ routeShow: true
+ },
+ mutations: {
+ pushKeepLive(state, component){
+ if(!state.keepLiveRoute.includes(component)){
+ state.keepLiveRoute.push(component)
+ }
+ },
+ removeKeepLive(state, component){
+ var index = state.keepLiveRoute.indexOf(component);
+ if(index !== -1){
+ state.keepLiveRoute.splice(index, 1);
+ }
+ },
+ clearKeepLive(state){
+ state.keepLiveRoute = []
+ },
+ setRouteKey(state, key){
+ state.routeKey = key
+ },
+ setRouteShow(state, key){
+ state.routeShow = key
+ }
+ },
+ actions: {
+ setRouteKey({ commit }, key) {
+ commit('setRouteKey', key);
+ }
+ }
+}
diff --git a/src/store/model/views.ts b/src/store/model/viewTags.ts
similarity index 89%
rename from src/store/model/views.ts
rename to src/store/model/viewTags.ts
index 5946559..91dee57 100644
--- a/src/store/model/views.ts
+++ b/src/store/model/viewTags.ts
@@ -25,6 +25,10 @@ export default {
})
},
updateViewTags(state, route){
+ // state.viewTags = state.viewTags.map(item =>
+ // item.fullPath === route.fullPath ? { ...item, ...route } : item
+ // );
+
state.viewTags.forEach((item) => {
if (item.fullPath == route.fullPath){
item = Object.assign(item, route)
diff --git a/src/style/app.scss b/src/style/app.scss
index a064b80..8d45f2d 100644
--- a/src/style/app.scss
+++ b/src/style/app.scss
@@ -8,8 +8,8 @@ a,button,input,textarea{-webkit-tap-highlight-color:rgba(0,0,0,0);box-sizing: bo
* {margin: 0;padding: 0;box-sizing: border-box;outline: none;}
/* 大布局样式 */
-.aminui {display: flex;flex-flow: column;}
-.aminui-wrapper {display: flex;flex:1;overflow: auto;}
+.pi {display: flex;flex-flow: column;}
+.pi-wrapper {display: flex;flex:1;overflow: auto;}
/* 全局滚动条样式 */
.scrollable {-webkit-overflow-scrolling: touch;}
@@ -24,80 +24,80 @@ a,button,input,textarea{-webkit-tap-highlight-color:rgba(0,0,0,0);box-sizing: bo
.layout-setting i {font-size: 18px;color: #fff;}
/* 头部 */
-.adminui-header {height: 58px;background: #222b45;color: #fff;display: flex;justify-content:space-between;}
-.adminui-header-left {display: flex;align-items: center;padding-left:20px;}
-.adminui-header-right {display: flex;align-items: center;}
-.adminui-header .logo-bar {font-size: 20px;font-weight: bold;display: flex;align-items: center;}
-.adminui-header .logo-bar .logo {margin-right: 10px;width: 35px;height: 35px;}
-.adminui-header .nav {display: flex;height: 100%;margin-left: 40px;}
-.adminui-header .nav li {padding:0 10px;margin: 0 10px 0 0;font-size: 14px;color: rgba(255, 255, 255, 0.6);list-style: none;height: 100%;display: flex;align-items: center;cursor: pointer;}
-.adminui-header .nav li i {margin-right: 5px;}
-.adminui-header .nav li:hover {color: #fff;}
-.adminui-header .nav li.active {background: rgba(255, 255, 255, 0.1);color: #fff;}
-.adminui-header .user-bar .panel-item:hover {background: rgba(255, 255, 255, 0.1)!important;}
-.adminui-header .user-bar .user label{color: #fff;}
+.pi-header {height: 58px;background: #222b45;color: #fff;display: flex;justify-content:space-between;}
+.pi-header-left {display: flex;align-items: center;padding-left:20px;}
+.pi-header-right {display: flex;align-items: center;}
+.pi-header .logo-bar {font-size: 20px;font-weight: bold;display: flex;align-items: center;}
+.pi-header .logo-bar .logo {margin-right: 10px;width: 35px;height: 35px;}
+.pi-header .nav {display: flex;height: 100%;margin-left: 40px;}
+.pi-header .nav li {padding:0 10px;margin: 0 10px 0 0;font-size: 14px;color: rgba(255, 255, 255, 0.6);list-style: none;height: 100%;display: flex;align-items: center;cursor: pointer;}
+.pi-header .nav li i {margin-right: 5px;}
+.pi-header .nav li:hover {color: #fff;}
+.pi-header .nav li.active {background: rgba(255, 255, 255, 0.1);color: #fff;}
+.pi-header .user-bar .panel-item:hover {background: rgba(255, 255, 255, 0.1)!important;}
+.pi-header .user-bar .user label{color: #fff;}
/* 左侧菜单 */
-.aminui-side-split {width:65px;flex-shrink:0;background: #222b45;display: flex;flex-flow: column;}
-.aminui-side-split-top {height: 49px;}
-.aminui-side-split-top a {display: inline-block;width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;}
-.aminui-side-split-top .logo {height:30px;vertical-align: bottom;}
-.adminui-side-split-scroll {overflow: auto;overflow-x:hidden;height: 100%;flex: 1;}
-.aminui-side-split li {cursor: pointer;width: 65px;height: 65px;color: #fff;text-align: center;display: flex;flex-direction: column;align-items: center;justify-content: center;}
-.aminui-side-split li i {font-size: 18px;}
-.aminui-side-split li p {margin-top:5px;}
-.aminui-side-split li:hover {background: rgba(255, 255, 255, 0.1);}
-.aminui-side-split li.active {background: #409EFF;}
+.pi-side-split {width:65px;flex-shrink:0;background: #222b45;display: flex;flex-flow: column;}
+.pi-side-split-top {height: 49px;}
+.pi-side-split-top a {display: inline-block;width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;}
+.pi-side-split-top .logo {height:30px;vertical-align: bottom;}
+.pi-side-split-scroll {overflow: auto;overflow-x:hidden;height: 100%;flex: 1;}
+.pi-side-split li {cursor: pointer;width: 65px;height: 65px;color: #fff;text-align: center;display: flex;flex-direction: column;align-items: center;justify-content: center;}
+.pi-side-split li i {font-size: 18px;}
+.pi-side-split li p {margin-top:5px;}
+.pi-side-split li:hover {background: rgba(255, 255, 255, 0.1);}
+.pi-side-split li.active {background: #409EFF;}
-.adminui-side-split-scroll::-webkit-scrollbar-thumb {background-color: rgba(255, 255, 255, 0.4);border-radius:5px;}
-.adminui-side-split-scroll::-webkit-scrollbar-thumb:hover {background-color: rgba(255, 255, 255, 0.5);}
-.adminui-side-split-scroll::-webkit-scrollbar-track {background-color: rgba(255, 255, 255, 0);}
-.adminui-side-split-scroll::-webkit-scrollbar-track:hover {background-color: rgba(255, 255, 255, 0);}
+.pi-side-split-scroll::-webkit-scrollbar-thumb {background-color: rgba(255, 255, 255, 0.4);border-radius:5px;}
+.pi-side-split-scroll::-webkit-scrollbar-thumb:hover {background-color: rgba(255, 255, 255, 0.5);}
+.pi-side-split-scroll::-webkit-scrollbar-track {background-color: rgba(255, 255, 255, 0);}
+.pi-side-split-scroll::-webkit-scrollbar-track:hover {background-color: rgba(255, 255, 255, 0);}
-.aminui-side {display: flex;flex-flow: column;flex-shrink:0;width:210px;background: #fff;box-shadow: 2px 0 8px 0 rgba(29,35,41,.05);border-right: 1px solid #e6e6e6;transition:width 0.3s;}
-.adminui-side-top {border-bottom: 1px solid #ebeef5;height:50px;line-height: 50px;}
-.adminui-side-top h2 {padding:0 20px;font-size: 17px;color: #3c4a54;}
-.adminui-side-scroll {overflow: auto;overflow-x:hidden;flex: 1;}
-.adminui-side-bottom {border-top: 1px solid #ebeef5;height:51px;cursor: pointer;display: flex;align-items: center;justify-content: center;}
-.adminui-side-bottom i {font-size: 16px;}
-.adminui-side-bottom:hover {color: var(--el-color-primary);}
-.aminui-side.isCollapse {width: 65px;}
+.pi-side {display: flex;flex-flow: column;flex-shrink:0;width:210px;background: #fff;box-shadow: 2px 0 8px 0 rgba(29,35,41,.05);border-right: 1px solid #e6e6e6;transition:width 0.3s;}
+.pi-side-top {border-bottom: 1px solid #ebeef5;height:50px;line-height: 50px;}
+.pi-side-top h2 {padding:0 20px;font-size: 17px;color: #3c4a54;}
+.pi-side-scroll {overflow: auto;overflow-x:hidden;flex: 1;}
+.pi-side-bottom {border-top: 1px solid #ebeef5;height:51px;cursor: pointer;display: flex;align-items: center;justify-content: center;}
+.pi-side-bottom i {font-size: 16px;}
+.pi-side-bottom:hover {color: var(--el-color-primary);}
+.pi-side.isCollapse {width: 65px;}
.el-menu .menu-tag {position: absolute;height: 18px;line-height: 18px;background: var(--el-color-danger);font-size: 12px;color: #fff;right: 20px;border-radius:18px;padding:0 6px;}
.el-menu .el-sub-menu__title .menu-tag {right: 40px;}
.el-menu--horizontal > li .menu-tag {display: none;}
/* 右侧内容 */
-.aminui-body {flex: 1;display: flex;flex-flow: column;}
+.pi-body {flex: 1;display: flex;flex-flow: column;}
-.adminui-topbar {height: 50px;border-bottom: 1px solid #ebeef5;background: #fff;box-shadow: 0 1px 4px rgba(0,21,41,.08);display: flex;justify-content:space-between;}
-.adminui-topbar .left-panel {display: flex;align-items: center;}
-.adminui-topbar .right-panel {display: flex;align-items: center;}
+.pi-topbar {height: 50px;border-bottom: 1px solid #ebeef5;background: #fff;box-shadow: 0 1px 4px rgba(0,21,41,.08);display: flex;justify-content:space-between;}
+.pi-topbar .left-panel {display: flex;align-items: center;}
+.pi-topbar .right-panel {display: flex;align-items: center;}
.right-panel-search {display: flex;align-items: center;}
.right-panel-search > * + * {margin-left:10px;}
-.adminui-tags {height:35px;background: #fff;border-bottom: 1px solid #e6e6e6;}
-.adminui-tags ul {display: flex;overflow: hidden;}
-.adminui-tags li {cursor: pointer;display: inline-block;float: left;height:34px;line-height: 34px;position: relative;flex-shrink: 0;}
-.adminui-tags li::after {content: " ";width:1px;height:100%;position: absolute;right:0px;background-image: linear-gradient(#fff, #e6e6e6);}
-.adminui-tags li a {display: inline-block;padding:0 10px;width:100%;height:100%;color: #999;text-decoration:none;display: flex;align-items: center;}
-.adminui-tags li i {margin-left:10px;border-radius: 3px;width:18px;height:18px;display: flex;align-items: center;justify-content: center;}
-.adminui-tags li i:hover {background: rgba(0,0,0,.2);color: #fff;}
-.adminui-tags li:hover {background: #ecf5ff;}
-.adminui-tags li.active {background: #409EFF;}
-.adminui-tags li.active a {color: #fff;}
-.adminui-tags li.sortable-ghost {opacity: 0;}
+.pi-tags {height:35px;background: #fff;border-bottom: 1px solid #e6e6e6;}
+.pi-tags ul {display: flex;overflow: hidden;}
+.pi-tags li {cursor: pointer;display: inline-block;float: left;height:34px;line-height: 34px;position: relative;flex-shrink: 0;}
+.pi-tags li::after {content: " ";width:1px;height:100%;position: absolute;right:0px;background-image: linear-gradient(#fff, #e6e6e6);}
+.pi-tags li a {display: inline-block;padding:0 10px;width:100%;height:100%;color: #999;text-decoration:none;display: flex;align-items: center;}
+.pi-tags li i {margin-left:10px;border-radius: 3px;width:18px;height:18px;display: flex;align-items: center;justify-content: center;}
+.pi-tags li i:hover {background: rgba(0,0,0,.2);color: #fff;}
+.pi-tags li:hover {background: #ecf5ff;}
+.pi-tags li.active {background: #409EFF;}
+.pi-tags li.active a {color: #fff;}
+.pi-tags li.sortable-ghost {opacity: 0;}
-.adminui-main {overflow: auto;background-color: #f6f8f9;flex: 1;}
+.pi-main {overflow: auto;background-color: #f6f8f9;flex: 1;}
/*页面最大化*/
-.aminui.main-maximize {
+.pi.main-maximize {
.main-maximize-exit {display: block;}
- .aminui-side-split, .aminui-side, .adminui-header, .adminui-topbar, .adminui-tags {display: none;}
+ .pi-side-split, .pi-side, .pi-header, .pi-topbar, .pi-tags {display: none;}
}
.main-maximize-exit {display: none;position: fixed;z-index: 3000;top:-20px;left:50%;margin-left: -20px;border-radius: 50%;width: 40px;height: 40px;cursor: pointer;background: rgba(0,0,0,0.2);text-align: center;}
.main-maximize-exit i {font-size: 14px;margin-top: 22px;color: #fff;}
.main-maximize-exit:hover {background: rgba(0,0,0,0.4);}
/*定宽页面*/
-.sc-page {width: 1230px;margin: 0 auto;}
+.pi-page {width: 1230px;margin: 0 auto;}
diff --git a/src/style/dark.scss b/src/style/dark.scss
index 1ffe791..b6d65ae 100644
--- a/src/style/dark.scss
+++ b/src/style/dark.scss
@@ -17,16 +17,16 @@ html.dark {
.login_bg {background: var(--el-bg-color);}
//框架
- .adminui-header {background: var(--el-bg-color-overlay);border-bottom: 1px solid var(--el-border-color-light);height:59px;}
- .aminui-side-split {background: var(--el-bg-color);}
- .aminui-side-split li {color: var(--el-text-color-primary);}
- .aminui-side {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
- .adminui-side-top, .adminui-side-bottom {border-color: var(--el-border-color-light);}
- .adminui-side-top h2 {color: var(--el-text-color-primary);}
- .adminui-topbar, .adminui-tags {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
- .adminui-main {background: var(--el-bg-color);}
+ .pi-header {background: var(--el-bg-color-overlay);border-bottom: 1px solid var(--el-border-color-light);height:59px;}
+ .pi-side-split {background: var(--el-bg-color);}
+ .pi-side-split li {color: var(--el-text-color-primary);}
+ .pi-side {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
+ .pi-side-top, .pi-side-bottom {border-color: var(--el-border-color-light);}
+ .pi-side-top h2 {color: var(--el-text-color-primary);}
+ .pi-topbar, .pi-tags {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
+ .pi-main {background: var(--el-bg-color);}
.drawerBG {background: var(--el-bg-color);}
- .adminui-header-menu .el-menu {--el-menu-bg-color:var(--el-bg-color-overlay) !important;--el-menu-hover-bg-color: #171819 !important;}
+ .pi-header-menu .el-menu {--el-menu-bg-color:var(--el-bg-color-overlay) !important;--el-menu-hover-bg-color: #171819 !important;}
//组件
.el-header, .el-main.nopadding, .el-footer {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
diff --git a/src/style/fix.scss b/src/style/fix.scss
index 589757b..aa93e28 100644
--- a/src/style/fix.scss
+++ b/src/style/fix.scss
@@ -74,9 +74,9 @@
.el-dropdown-link {cursor: pointer;color: var(--el-color-primary);line-height: 22px;font-size: 12px;}
-.aminui-side-split li.active {background-color: var(--el-color-primary);}
-.adminui-tags li:hover {background-color: var(--el-color-primary-light-9);}
-.adminui-tags li.active {background-color: var(--el-color-primary)!important;}
+.pi-side-split li.active {background-color: var(--el-color-primary);}
+.pi-tags li:hover {background-color: var(--el-color-primary-light-9);}
+.pi-tags li.active {background-color: var(--el-color-primary)!important;}
.contextmenu li:hover {background-color: var(--el-color-primary-light-9)!important;color: var(--el-color-primary-light-2)!important;}
.data-box .item-background {background-color: var(--el-color-primary)!important;}
.layout-setting,.diy-grid-setting {background-color: var(--el-color-primary)!important;}
diff --git a/src/style/media.scss b/src/style/media.scss
index 01cd3e1..a5a104a 100644
--- a/src/style/media.scss
+++ b/src/style/media.scss
@@ -7,7 +7,7 @@
.el-drawer.rtl {width: 90%!important;}
.el-form-item__content {margin-left: 0px!important;}
- .adminui-main {
+ .pi-main {
>.el-container {display: block;height:auto;}
>.el-container > .el-aside {width: 100%!important;border: 0}
}
@@ -29,21 +29,21 @@
.right-panel .right-panel-search {display: block;}
.right-panel .right-panel-search >* {width: 100%;margin: 0;margin-top: 15px;}
}
- .adminui-main > .el-container >*:first-child:not(.el-aside):not(.el-header) {border: 0;margin-top: 0;}
- .adminui-main > .el-container >*:first-child:not(.el-aside):not(.el-header) + .el-aside {margin-top: 0;}
- .adminui-main > .el-container > .el-aside {border-bottom: 1px solid var(--el-border-color-light)!important;}
- .adminui-main > .el-container > .el-container {border-top: 1px solid var(--el-border-color-light);border-bottom: 1px solid var(--el-border-color-light);margin-top: 15px;}
- .adminui-main > .el-container > .el-container + .el-aside {border-top: 1px solid var(--el-border-color-light);margin-top: 15px;}
- .adminui-main > .el-container > .el-header {@extend .headerPublic;}
- .adminui-main > .el-container > .el-main.nopadding {border-top: 1px solid var(--el-border-color-light);border-bottom: 1px solid var(--el-border-color-light);margin-top: 15px;}
- .adminui-main > .el-container > .el-main + .el-aside {border-left: 0!important;border-top: 1px solid var(--el-border-color-light);margin-top: 15px;}
- .adminui-main > .el-container > .el-footer {margin-top: 15px;border-bottom: 1px solid var(--el-border-color-light);}
- .adminui-main > .el-container > .el-container > .el-header {@extend .headerPublic}
- .adminui-main > .el-container > .el-container > .el-header .left-panel {display: block;}
- .adminui-main > .el-container > .el-container > .el-header .right-panel {display: block;margin-top: 15px;}
+ .pi-main > .el-container >*:first-child:not(.el-aside):not(.el-header) {border: 0;margin-top: 0;}
+ .pi-main > .el-container >*:first-child:not(.el-aside):not(.el-header) + .el-aside {margin-top: 0;}
+ .pi-main > .el-container > .el-aside {border-bottom: 1px solid var(--el-border-color-light)!important;}
+ .pi-main > .el-container > .el-container {border-top: 1px solid var(--el-border-color-light);border-bottom: 1px solid var(--el-border-color-light);margin-top: 15px;}
+ .pi-main > .el-container > .el-container + .el-aside {border-top: 1px solid var(--el-border-color-light);margin-top: 15px;}
+ .pi-main > .el-container > .el-header {@extend .headerPublic;}
+ .pi-main > .el-container > .el-main.nopadding {border-top: 1px solid var(--el-border-color-light);border-bottom: 1px solid var(--el-border-color-light);margin-top: 15px;}
+ .pi-main > .el-container > .el-main + .el-aside {border-left: 0!important;border-top: 1px solid var(--el-border-color-light);margin-top: 15px;}
+ .pi-main > .el-container > .el-footer {margin-top: 15px;border-bottom: 1px solid var(--el-border-color-light);}
+ .pi-main > .el-container > .el-container > .el-header {@extend .headerPublic}
+ .pi-main > .el-container > .el-container > .el-header .left-panel {display: block;}
+ .pi-main > .el-container > .el-container > .el-header .right-panel {display: block;margin-top: 15px;}
.sc-page {width: 100%;margin: 0;}
-
+
.common-main .el-form {width: 100% !important;}
.common-header-logo label {display: none;}
.common-header-title {display: none;}
diff --git a/src/utils/color.ts b/src/utils/color.ts
new file mode 100644
index 0000000..295d011
--- /dev/null
+++ b/src/utils/color.ts
@@ -0,0 +1,29 @@
+export default {
+ //hex颜色转rgb颜色
+ HexToRgb(str) {
+ str = str.replace("#", "")
+ var hxs = str.match(/../g)
+ for (var i = 0; i < 3; i++) hxs[i] = parseInt(hxs[i], 16)
+ return hxs
+ },
+ //rgb颜色转hex颜色
+ RgbToHex(a, b, c) {
+ var hexs = [a.toString(16), b.toString(16), c.toString(16)]
+ for (var i = 0; i < 3; i++) {
+ if (hexs[i].length == 1) hexs[i] = "0" + hexs[i]
+ }
+ return "#" + hexs.join("");
+ },
+ //加深
+ darken(color, level) {
+ var rgbc = this.HexToRgb(color)
+ for (var i = 0; i < 3; i++) rgbc[i] = Math.floor(rgbc[i] * (1 - level))
+ return this.RgbToHex(rgbc[0], rgbc[1], rgbc[2])
+ },
+ //变淡
+ lighten(color, level) {
+ var rgbc = this.HexToRgb(color)
+ for (var i = 0; i < 3; i++) rgbc[i] = Math.floor((255 - rgbc[i]) * level + rgbc[i])
+ return this.RgbToHex(rgbc[0], rgbc[1], rgbc[2])
+ }
+}
diff --git a/src/utils/errorHandler.ts b/src/utils/errorHandler.ts
new file mode 100644
index 0000000..c7fa90c
--- /dev/null
+++ b/src/utils/errorHandler.ts
@@ -0,0 +1,34 @@
+/**
+ * 全局代码错误捕捉
+ * 比如 null.length 就会被捕捉到
+ */
+import {nextTick} from "vue";
+
+export default (error, vm)=>{
+ //过滤HTTP请求错误
+ if(error.status || error.status==0){
+ return false
+ }
+
+ // var errorMap = {
+ // InternalError: "Javascript引擎内部错误",
+ // ReferenceError: "未找到对象",
+ // TypeError: "使用了错误的类型或对象",
+ // RangeError: "使用内置对象时,参数超范围",
+ // SyntaxError: "语法错误",
+ // EvalError: "错误的使用了Eval",
+ // URIError: "URI错误"
+ // }
+ // var errorName = errorMap[error.name] || "未知错误"
+
+ console.warn(`[PI error]: ${error}`);
+ console.error(error);
+ //throw error;
+
+ vm.$nextTick(() => {
+ // vm.$notify.error({
+ // title: errorName,
+ // message: error
+ // });
+ })
+}
diff --git a/src/utils/route.ts b/src/utils/route.ts
index 9200cd0..75d23ed 100644
--- a/src/utils/route.ts
+++ b/src/utils/route.ts
@@ -1,16 +1,17 @@
-// import store from '@/store'
+import store from '@/store'
import {nextTick} from 'vue'
import {RouteLocationNormalized, RouteLocationNormalizedLoaded} from "vue-router";
+import tools from "@/utils/tools";
export function beforeEach(to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded) {
const adminMain = document.querySelector('#pi-main');
if (!adminMain) {
return false
}
- // store.commit("updateViewTags", {
- // fullPath: from.fullPath,
- // scrollTop: adminMain.scrollTop
- // })
+ store.commit("updateViewTags", {
+ fullPath: from.fullPath,
+ scrollTop: adminMain.scrollTop
+ })
}
export function afterEach(to: RouteLocationNormalized) {
@@ -18,10 +19,87 @@ export function afterEach(to: RouteLocationNormalized) {
if (!adminMain) {
return false
}
- // nextTick(() => {
- // const beforeRoute = store.state.views.viewTags.filter(v => v.fullPath == to.fullPath)[0];
- // if (beforeRoute) {
- // adminMain.scrollTop = beforeRoute.scrollTop || 0
- // }
- // }).then(r => {})
+ nextTick(() => {
+ // @ts-ignore
+ const beforeRoute = store.state.viewTags.viewTags.filter(v => v.fullPath == to.fullPath)[0];
+ if (beforeRoute) {
+ adminMain.scrollTop = beforeRoute.scrollTop || 0
+ }
+ }).then(r => {
+ })
+}
+
+//转换
+export function filterAsyncRouter(routerMap) {
+ const accessedRouters = []
+ routerMap.forEach(item => {
+ item.meta = item.meta ? item.meta : {};
+ //处理外部链接特殊路由
+ if (item.meta.type == 'iframe') {
+ item.meta.url = item.path;
+ item.path = `/i/${item.name}`;
+ }
+ //MAP转路由对象
+ var route = {
+ path: item.path,
+ name: item.name,
+ meta: item.meta,
+ redirect: item.redirect,
+ children: item.children ? filterAsyncRouter(item.children) : null,
+ component: loadComponent(item.component)
+ }
+ accessedRouters.push(route)
+ })
+ return accessedRouters
+}
+
+export function loadComponent(component: string) {
+ if (component) {
+ return () => import(/* @vite-ignore */`/src/views/${component}`)
+ } else {
+ return () => import(`@/layout/empty`)
+ }
+}
+
+//路由扁平化
+export function flatAsyncRoutes(routes, breadcrumb = []) {
+ let res = []
+ routes.forEach(route => {
+ const tmp = {...route}
+ if (tmp.children) {
+ let childrenBreadcrumb = [...breadcrumb]
+ childrenBreadcrumb.push(route)
+ let tmpRoute = {...route}
+ tmpRoute.meta.breadcrumb = childrenBreadcrumb
+ delete tmpRoute.children
+ res.push(tmpRoute)
+ let childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb)
+ childrenRoutes.map(item => {
+ res.push(item)
+ })
+ } else {
+ let tmpBreadcrumb = [...breadcrumb]
+ tmpBreadcrumb.push(tmp)
+ tmp.meta.breadcrumb = tmpBreadcrumb
+ res.push(tmp)
+ }
+ })
+ return res
+}
+
+//过滤树
+export function treeFilter(tree, func) {
+ return tree.map(node => ({...node})).filter(node => {
+ node.children = node.children && treeFilter(node.children, func)
+ return func(node) || (node.children && node.children.length)
+ })
+}
+
+export function getMenu() {
+ const apiMenu = tools.data.get("MENU") || [];
+ let userInfo = tools.data.get("USER_INFO")
+ let userMenu = treeFilter([], node => {
+ return node.meta.role ? node.meta.role.filter(item => userInfo.role.indexOf(item) > -1).length > 0 : true
+ })
+ return [...userMenu, ...apiMenu]
}
diff --git a/src/utils/tools.ts b/src/utils/tools.ts
index 88d510e..83c9a60 100644
--- a/src/utils/tools.ts
+++ b/src/utils/tools.ts
@@ -2,14 +2,14 @@ import CryptoJS from 'crypto-js';
const tools = {
data: {
- set(cacheKey, data, expireIn = 0) {
+ set(cacheKey: string, data: any, expireIn: number = 0) {
let cacheValue = {
content: data,
expireIn: expireIn === 0 ? 0 : new Date().getTime() + expireIn * 1000
}
return localStorage.setItem(cacheKey, tools.base64.encrypt(JSON.stringify(cacheValue)))
},
- get(cacheKey) {
+ get(cacheKey: string) {
try {
const cacheValue = JSON.parse(tools.base64.decrypt(localStorage.getItem(cacheKey)))
if (cacheValue) {
@@ -25,7 +25,7 @@ const tools = {
return null
}
},
- remove(cacheKey) {
+ remove(cacheKey: string) {
return localStorage.removeItem(cacheKey)
},
clear() {
@@ -33,10 +33,10 @@ const tools = {
}
},
base64: {
- encrypt(data) {
+ encrypt(data: string) {
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(data))
},
- decrypt(cipher) {
+ decrypt(cipher: string) {
return CryptoJS.enc.Base64.parse(cipher).toString(CryptoJS.enc.Utf8)
}
},
@@ -50,9 +50,58 @@ const tools = {
},
crypto: {
//MD5加密
- MD5(data){
+ MD5(data: string) {
return CryptoJS.MD5(data).toString()
},
+ },
+ makeMenu: function (menus, parent_id = 0) {
+ const arr = [];
+ for (let item of menus) {
+ if (item.parent_id === parent_id) {
+ // 数据格式处理
+ const tmp = {
+ name: item['name'],
+ path: '/' + item['path'],
+ component: item['path'],
+ meta: {
+ 'title': item['title'],
+ 'icon': item['icon'],
+ 'hidden': item['hidden'],
+ 'type': 'menu'
+ }
+ };
+ const children = this.makeMenu(menus, item.menu_id);
+ if (children.length > 0) {
+ tmp['children'] = children
+ }
+ arr.push(tmp)
+ }
+ }
+ return arr
+ },
+ screen: function (element) {
+ var isFull = !!(document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement || document.fullscreenElement);
+ if (isFull) {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen();
+ } else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen();
+ }
+ } else {
+ if (element.requestFullscreen) {
+ element.requestFullscreen();
+ } else if (element.msRequestFullscreen) {
+ element.msRequestFullscreen();
+ } else if (element.mozRequestFullScreen) {
+ element.mozRequestFullScreen();
+ } else if (element.webkitRequestFullscreen) {
+ element.webkitRequestFullscreen();
+ }
+ }
}
}
diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts
new file mode 100644
index 0000000..7e45c1a
--- /dev/null
+++ b/src/utils/websocket.ts
@@ -0,0 +1,31 @@
+import sysConfig from "@/config";
+import tools from "@/utils/tools";
+
+const ws = {
+ websocket: null,
+ timer: null,
+ init: function () {
+ let token = tools.data.get("TOKEN");
+ ws.websocket = new WebSocket(sysConfig.WS_URL + "?Authorization=" + token);
+ ws.websocket.onopen = ws.open;
+ return ws
+ },
+ open: function () {
+ if (ws.timer) {
+ clearInterval(ws.timer); // 停止心跳
+ }
+ ws.startBeat()
+ },
+ startBeat: function (){
+ ws.timer = setInterval(() => {
+ if (ws.websocket && ws.websocket.readyState === WebSocket.OPEN) {
+ ws.websocket.send("ping");
+ }
+ }, 30000);
+ },
+ send: function (message){
+ ws.websocket.send(JSON.stringify(message))
+ }
+}
+
+export default ws;
diff --git a/src/views/dashboard/index.vue b/src/views/dashboard/index.vue
new file mode 100644
index 0000000..6921659
--- /dev/null
+++ b/src/views/dashboard/index.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dashboard/message/index.vue b/src/views/dashboard/message/index.vue
new file mode 100644
index 0000000..85af715
--- /dev/null
+++ b/src/views/dashboard/message/index.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/src/views/system/login/index.vue b/src/views/system/login/index.vue
index fb3a274..0308ffa 100644
--- a/src/views/system/login/index.vue
+++ b/src/views/system/login/index.vue
@@ -31,7 +31,7 @@
@@ -71,7 +71,7 @@
-