mirror of
https://github.com/jekip/naive-ui-admin.git
synced 2026-02-04 13:42:27 +08:00
fix Bug or esLink formatting
This commit is contained in:
74
.eslintrc.js
74
.eslintrc.js
@@ -1,9 +1,11 @@
|
||||
module.exports = {
|
||||
// @ts-check
|
||||
const { defineConfig } = require('eslint-define-config');
|
||||
module.exports = defineConfig({
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true
|
||||
es6: true,
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
@@ -12,21 +14,46 @@ module.exports = {
|
||||
sourceType: 'module',
|
||||
jsxPragma: 'React',
|
||||
ecmaFeatures: {
|
||||
jsx: true
|
||||
}
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'plugin:prettier/recommended'
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:jest/recommended',
|
||||
],
|
||||
rules: {
|
||||
'vue/no-unused-components': 'off',
|
||||
'vue/no-unused-vars': 'off',
|
||||
'vue/no-v-for-template-key-on-child': 'off',
|
||||
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'vue/custom-event-name-casing': 'off',
|
||||
// 'vue/attributes-order': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'space-before-function-paren': 'off',
|
||||
|
||||
'vue/attributes-order': 'off',
|
||||
'vue/one-component-per-file': 'off',
|
||||
'vue/html-closing-bracket-newline': 'off',
|
||||
'vue/max-attributes-per-line': 'off',
|
||||
@@ -34,34 +61,17 @@ module.exports = {
|
||||
'vue/singleline-html-element-content-newline': 'off',
|
||||
'vue/attribute-hyphenation': 'off',
|
||||
'vue/require-default-prop': 'off',
|
||||
|
||||
'space-before-function-paren': 'off',
|
||||
|
||||
'@typescript-eslint/camelcase': 'off',
|
||||
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'vue/html-self-closing': [
|
||||
'error',
|
||||
{
|
||||
html: {
|
||||
void: 'always',
|
||||
normal: 'never',
|
||||
component: 'always'
|
||||
component: 'always',
|
||||
},
|
||||
svg: 'always',
|
||||
math: 'always'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
math: 'always',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Mock from 'mockjs'
|
||||
import Mock from 'mockjs';
|
||||
|
||||
export function resultSuccess(result, { message = 'ok' } = {}) {
|
||||
return Mock.mock({
|
||||
@@ -50,10 +50,10 @@ export function pagination<T = any>(pageNo: number, pageSize: number, array: T[]
|
||||
* @param {Number} times 回调函数需要执行的次数
|
||||
* @param {Function} callback 回调函数
|
||||
*/
|
||||
export function doCustomTimes (times:number, callback:any) {
|
||||
let i = -1
|
||||
export function doCustomTimes(times: number, callback: any) {
|
||||
let i = -1;
|
||||
while (++i < times) {
|
||||
callback(i)
|
||||
callback(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
import { Random } from 'mockjs'
|
||||
import { resultSuccess } from '../_util'
|
||||
import { Random } from 'mockjs';
|
||||
import { resultSuccess } from '../_util';
|
||||
|
||||
const consoleInfo = {
|
||||
//访问量
|
||||
visits:{
|
||||
dayVisits:Random.float(10000,99999,2,2),
|
||||
rise:Random.float(10,99),
|
||||
decline:Random.float(10,99),
|
||||
amount:Random.float(99999,999999,3,5),
|
||||
visits: {
|
||||
dayVisits: Random.float(10000, 99999, 2, 2),
|
||||
rise: Random.float(10, 99),
|
||||
decline: Random.float(10, 99),
|
||||
amount: Random.float(99999, 999999, 3, 5),
|
||||
},
|
||||
//销售额
|
||||
saleroom:{
|
||||
weekSaleroom:Random.float(10000,99999,2,2),
|
||||
amount:Random.float(99999,999999,2,2),
|
||||
degree:Random.float(10,99)
|
||||
saleroom: {
|
||||
weekSaleroom: Random.float(10000, 99999, 2, 2),
|
||||
amount: Random.float(99999, 999999, 2, 2),
|
||||
degree: Random.float(10, 99),
|
||||
},
|
||||
//订单量
|
||||
orderLarge:{
|
||||
weekLarge:Random.float(10000,99999,2,2),
|
||||
rise:Random.float(10,99),
|
||||
decline:Random.float(10,99),
|
||||
amount:Random.float(99999,999999,2,2),
|
||||
orderLarge: {
|
||||
weekLarge: Random.float(10000, 99999, 2, 2),
|
||||
rise: Random.float(10, 99),
|
||||
decline: Random.float(10, 99),
|
||||
amount: Random.float(99999, 999999, 2, 2),
|
||||
},
|
||||
//成交额度
|
||||
volume:{
|
||||
weekLarge:Random.float(10000,99999,2,2),
|
||||
rise:Random.float(10,99),
|
||||
decline:Random.float(10,99),
|
||||
amount:Random.float(99999,999999,2,2)
|
||||
volume: {
|
||||
weekLarge: Random.float(10000, 99999, 2, 2),
|
||||
rise: Random.float(10, 99),
|
||||
decline: Random.float(10, 99),
|
||||
amount: Random.float(99999, 999999, 2, 2),
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default [
|
||||
//主控台 卡片数据
|
||||
@@ -41,7 +40,5 @@ export default [
|
||||
response: () => {
|
||||
return resultSuccess(consoleInfo);
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,79 +1,78 @@
|
||||
import { resultSuccess } from '../_util'
|
||||
import { resultSuccess } from '../_util';
|
||||
|
||||
const menuList = (() => {
|
||||
const menuList = () => {
|
||||
const result: any[] = [
|
||||
{
|
||||
label: 'Dashboard',
|
||||
key: 'dashboard',
|
||||
type: 1,
|
||||
subtitle:'dashboard',
|
||||
openType:1,
|
||||
auth:'dashboard',
|
||||
path:'/dashboard',
|
||||
subtitle: 'dashboard',
|
||||
openType: 1,
|
||||
auth: 'dashboard',
|
||||
path: '/dashboard',
|
||||
children: [
|
||||
{
|
||||
label: '主控台',
|
||||
key: 'console',
|
||||
type: 1,
|
||||
subtitle:'console',
|
||||
openType:1,
|
||||
auth:'console',
|
||||
path:'/dashboard/console',
|
||||
subtitle: 'console',
|
||||
openType: 1,
|
||||
auth: 'console',
|
||||
path: '/dashboard/console',
|
||||
},
|
||||
{
|
||||
label: '工作台',
|
||||
key: 'workplace',
|
||||
type: 1,
|
||||
subtitle:'workplace',
|
||||
openType:1,
|
||||
auth:'workplace',
|
||||
path:'/dashboard/workplace',
|
||||
}
|
||||
]
|
||||
subtitle: 'workplace',
|
||||
openType: 1,
|
||||
auth: 'workplace',
|
||||
path: '/dashboard/workplace',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '表单管理',
|
||||
key: 'form',
|
||||
type: 1,
|
||||
subtitle:'form',
|
||||
openType:1,
|
||||
auth:'form',
|
||||
path:'/form',
|
||||
subtitle: 'form',
|
||||
openType: 1,
|
||||
auth: 'form',
|
||||
path: '/form',
|
||||
children: [
|
||||
{
|
||||
label: '基础表单',
|
||||
key: 'basic-form',
|
||||
type: 1,
|
||||
subtitle:'basic-form',
|
||||
openType:1,
|
||||
auth:'basic-form',
|
||||
path:'/form/basic-form',
|
||||
subtitle: 'basic-form',
|
||||
openType: 1,
|
||||
auth: 'basic-form',
|
||||
path: '/form/basic-form',
|
||||
},
|
||||
{
|
||||
label: '分步表单',
|
||||
key: 'step-form',
|
||||
type: 1,
|
||||
subtitle:'step-form',
|
||||
openType:1,
|
||||
auth:'step-form',
|
||||
path:'/form/step-form',
|
||||
subtitle: 'step-form',
|
||||
openType: 1,
|
||||
auth: 'step-form',
|
||||
path: '/form/step-form',
|
||||
},
|
||||
{
|
||||
label: '表单详情',
|
||||
key: 'detail',
|
||||
type: 1,
|
||||
subtitle:'detail',
|
||||
openType:1,
|
||||
auth:'detail',
|
||||
path:'/form/detail',
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
return result
|
||||
});
|
||||
subtitle: 'detail',
|
||||
openType: 1,
|
||||
auth: 'detail',
|
||||
path: '/form/detail',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
@@ -81,13 +80,10 @@ export default [
|
||||
timeout: 1000,
|
||||
method: 'get',
|
||||
response: () => {
|
||||
const list = menuList()
|
||||
const list = menuList();
|
||||
return resultSuccess({
|
||||
list
|
||||
}
|
||||
);
|
||||
list,
|
||||
});
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { resultSuccess, doCustomTimes } from '../_util'
|
||||
|
||||
import { resultSuccess, doCustomTimes } from '../_util';
|
||||
|
||||
function getMenuKeys() {
|
||||
let keys = ['dashboard', 'console', 'workplace', 'basic-form', 'step-form', 'detail']
|
||||
let newKeys = []
|
||||
doCustomTimes(parseInt(Math.random()*6), () => {
|
||||
let key = keys[Math.floor(Math.random() * keys.length)];
|
||||
newKeys.push(key)
|
||||
})
|
||||
const keys = ['dashboard', 'console', 'workplace', 'basic-form', 'step-form', 'detail'];
|
||||
const newKeys = [];
|
||||
doCustomTimes(parseInt(Math.random() * 6), () => {
|
||||
const key = keys[Math.floor(Math.random() * keys.length)];
|
||||
newKeys.push(key);
|
||||
});
|
||||
return Array.from(new Set(newKeys));
|
||||
}
|
||||
|
||||
const roleList = ((pageSize) => {
|
||||
const result: any[] = []
|
||||
const roleList = (pageSize) => {
|
||||
const result: any[] = [];
|
||||
doCustomTimes(pageSize, () => {
|
||||
result.push({
|
||||
id: '@integer(10,100)',
|
||||
@@ -23,10 +22,9 @@ const roleList = ((pageSize) => {
|
||||
create_date: `@date('yyyy-MM-dd hh:mm:ss')`,
|
||||
'status|1': ['normal', 'enable', 'disable'],
|
||||
});
|
||||
})
|
||||
return result
|
||||
});
|
||||
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
@@ -35,16 +33,13 @@ export default [
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
const { page = 1, pageSize = 10 } = query;
|
||||
const list = roleList(Number(pageSize))
|
||||
const list = roleList(Number(pageSize));
|
||||
return resultSuccess({
|
||||
page: Number(page),
|
||||
pageSize: Number(pageSize),
|
||||
pageCount: 60,
|
||||
list
|
||||
}
|
||||
);
|
||||
list,
|
||||
});
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Random } from 'mockjs'
|
||||
import { resultSuccess, doCustomTimes, resultPageSuccess } from '../_util'
|
||||
import { Random } from 'mockjs';
|
||||
import { resultSuccess, doCustomTimes } from '../_util';
|
||||
|
||||
const tableList = ((pageSize) => {
|
||||
const result:any[] = []
|
||||
doCustomTimes(pageSize,()=> {
|
||||
const tableList = (pageSize) => {
|
||||
const result: any[] = [];
|
||||
doCustomTimes(pageSize, () => {
|
||||
result.push({
|
||||
id: '@integer(10,100)',
|
||||
beginTime: '@datetime',
|
||||
@@ -16,10 +16,9 @@ const tableList = ((pageSize) => {
|
||||
'no|100000-10000000': 100000,
|
||||
'status|1': ['normal', 'enable', 'disable'],
|
||||
});
|
||||
})
|
||||
return result
|
||||
});
|
||||
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
export default [
|
||||
//表格数据列表
|
||||
@@ -29,16 +28,13 @@ export default [
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
const { page = 1, pageSize = 10 } = query;
|
||||
const list = tableList(Number(pageSize))
|
||||
const list = tableList(Number(pageSize));
|
||||
return resultSuccess({
|
||||
page:Number(page),
|
||||
pageSize:Number(pageSize),
|
||||
page: Number(page),
|
||||
pageSize: Number(pageSize),
|
||||
pageCount: 60,
|
||||
list
|
||||
}
|
||||
);
|
||||
list,
|
||||
});
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { MockMethod } from 'vite-plugin-mock'
|
||||
import { resultSuccess, getRequestToken } from '../_util'
|
||||
import { resultSuccess } from '../_util';
|
||||
|
||||
const menusList = [
|
||||
{
|
||||
@@ -18,7 +17,7 @@ const menusList = [
|
||||
component: 'DashboardConsole',
|
||||
meta: {
|
||||
title: '主控台',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'monitor',
|
||||
@@ -26,7 +25,7 @@ const menusList = [
|
||||
component: 'DashboardMonitor',
|
||||
meta: {
|
||||
title: '监控页',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'workplace',
|
||||
@@ -35,11 +34,11 @@ const menusList = [
|
||||
meta: {
|
||||
hidden: true,
|
||||
title: '工作台',
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default [
|
||||
{
|
||||
@@ -49,5 +48,5 @@ export default [
|
||||
response: () => {
|
||||
return resultSuccess(menusList);
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import Mock from 'mockjs'
|
||||
import { resultSuccess, getRequestToken } from '../_util'
|
||||
import Mock from 'mockjs';
|
||||
import { resultSuccess } from '../_util';
|
||||
|
||||
const Random = Mock.Random
|
||||
const Random = Mock.Random;
|
||||
|
||||
const token = Random.string('upper', 32, 32)
|
||||
const token = Random.string('upper', 32, 32);
|
||||
|
||||
const adminInfo = {
|
||||
userId: '1',
|
||||
@@ -33,9 +33,9 @@ const adminInfo = {
|
||||
{
|
||||
roleName: '基础列表删除',
|
||||
value: 'basic_list_delete',
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
@@ -56,4 +56,4 @@ export default [
|
||||
return resultSuccess(adminInfo);
|
||||
},
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
@@ -62,11 +62,14 @@
|
||||
"dotenv": "^10.0.0",
|
||||
"eslint": "^7.28.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-define-config": "^1.0.9",
|
||||
"eslint-plugin-jest": "^24.4.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-vue": "^7.11.1",
|
||||
"esno": "^0.7.3",
|
||||
"gh-pages": "^3.2.0",
|
||||
"husky": "^6.0.0",
|
||||
"jest": "^27.0.6",
|
||||
"less": "^4.1.1",
|
||||
"less-loader": "^9.0.0",
|
||||
"lint-staged": "^11.0.0",
|
||||
|
||||
94
src/App.vue
94
src/App.vue
@@ -7,33 +7,33 @@
|
||||
:date-locale="dateZhCN"
|
||||
>
|
||||
<AppProvider>
|
||||
<RouterView/>
|
||||
<RouterView />
|
||||
</AppProvider>
|
||||
</NConfigProvider>
|
||||
|
||||
<transition v-if="isLock && $route.name != 'login'" name="slide-up">
|
||||
<LockScreen/>
|
||||
<LockScreen />
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { zhCN, dateZhCN, createTheme, inputDark, datePickerDark, darkTheme } from 'naive-ui'
|
||||
import { LockScreen } from '@/components/Lockscreen'
|
||||
import { AppProvider } from '@/components/Application'
|
||||
import { useLockscreenStore } from '@/store/modules/lockscreen'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||
import { defineComponent, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { zhCN, dateZhCN, createTheme, inputDark, datePickerDark, darkTheme } from 'naive-ui';
|
||||
import { LockScreen } from '@/components/Lockscreen';
|
||||
import { AppProvider } from '@/components/Application';
|
||||
import { useLockscreenStore } from '@/store/modules/lockscreen';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
components: { LockScreen, AppProvider },
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const useLockscreen = useLockscreenStore()
|
||||
const designStore = useDesignSettingStore()
|
||||
const isLock = computed(() => useLockscreen.isLock)
|
||||
const lockTime = computed(() => useLockscreen.lockTime)
|
||||
const route = useRoute();
|
||||
const useLockscreen = useLockscreenStore();
|
||||
const designStore = useDesignSettingStore();
|
||||
const isLock = computed(() => useLockscreen.isLock);
|
||||
const lockTime = computed(() => useLockscreen.lockTime);
|
||||
/**
|
||||
* @type import('naive-ui').GlobalThemeOverrides
|
||||
*/
|
||||
@@ -41,40 +41,40 @@ export default defineComponent({
|
||||
return {
|
||||
common: {
|
||||
primaryColor: designStore.appTheme,
|
||||
primaryColorHover: '#57a3f3'
|
||||
}
|
||||
}
|
||||
})
|
||||
primaryColorHover: '#57a3f3',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const getDarkTheme = computed(() => (designStore.darkTheme ? darkTheme : undefined))
|
||||
const getDarkTheme = computed(() => (designStore.darkTheme ? darkTheme : undefined));
|
||||
|
||||
let timer
|
||||
let timer;
|
||||
|
||||
const timekeeping = () => {
|
||||
clearInterval(timer)
|
||||
if (route.name == 'login' || isLock.value) return
|
||||
clearInterval(timer);
|
||||
if (route.name == 'login' || isLock.value) return;
|
||||
// 设置不锁屏
|
||||
useLockscreen.setLock(false)
|
||||
useLockscreen.setLock(false);
|
||||
// 重置锁屏时间
|
||||
useLockscreen.setLockTime()
|
||||
useLockscreen.setLockTime();
|
||||
timer = setInterval(() => {
|
||||
// 锁屏倒计时递减
|
||||
useLockscreen.setLockTime(lockTime.value - 1)
|
||||
useLockscreen.setLockTime(lockTime.value - 1);
|
||||
if (lockTime.value <= 0) {
|
||||
// 设置锁屏
|
||||
useLockscreen.setLock(true)
|
||||
return clearInterval(timer)
|
||||
}
|
||||
}, 1000)
|
||||
useLockscreen.setLock(true);
|
||||
return clearInterval(timer);
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('mousedown', timekeeping)
|
||||
})
|
||||
document.addEventListener('mousedown', timekeeping);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('mousedown', timekeeping)
|
||||
})
|
||||
document.removeEventListener('mousedown', timekeeping);
|
||||
});
|
||||
|
||||
return {
|
||||
darkTheme: createTheme([inputDark, datePickerDark]),
|
||||
@@ -82,24 +82,24 @@ export default defineComponent({
|
||||
zhCN,
|
||||
dateZhCN,
|
||||
isLock,
|
||||
getThemeOverrides
|
||||
}
|
||||
}
|
||||
})
|
||||
getThemeOverrides,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import 'styles/global.less';
|
||||
@import 'styles/common.less';
|
||||
@import 'styles/override.less';
|
||||
@import 'styles/global.less';
|
||||
@import 'styles/common.less';
|
||||
@import 'styles/override.less';
|
||||
|
||||
.slide-up-enter-active,
|
||||
.slide-up-leave-active {
|
||||
.slide-up-enter-active,
|
||||
.slide-up-leave-active {
|
||||
transition: transform 0.35s ease-in;
|
||||
}
|
||||
}
|
||||
|
||||
.slide-up-enter-form,
|
||||
.slide-up-leave-to {
|
||||
.slide-up-enter-form,
|
||||
.slide-up-leave-to {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import http from '@/utils/http/axios'
|
||||
import http from '@/utils/http/axios';
|
||||
|
||||
//获取主控台信息
|
||||
export function getConsoleInfo() {
|
||||
return http.request(
|
||||
{
|
||||
return http.request({
|
||||
url: '/dashboard/console',
|
||||
method: 'get'
|
||||
}
|
||||
)
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import http from '@/utils/http/axios'
|
||||
import http from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 根据用户id获取用户菜单
|
||||
@@ -6,11 +6,10 @@ import http from '@/utils/http/axios'
|
||||
export function adminMenus() {
|
||||
return http.request({
|
||||
url: '/menus',
|
||||
method: 'GET'
|
||||
})
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取tree菜单列表
|
||||
* @param params
|
||||
@@ -19,6 +18,6 @@ export function getMenuList(params) {
|
||||
return http.request({
|
||||
url: '/menu/list',
|
||||
method: 'GET',
|
||||
params
|
||||
})
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import http from '@/utils/http/axios'
|
||||
import http from '@/utils/http/axios';
|
||||
|
||||
/**
|
||||
* @description: 角色列表
|
||||
@@ -6,6 +6,6 @@ import http from '@/utils/http/axios'
|
||||
export function getRoleList() {
|
||||
return http.request({
|
||||
url: '/role/list',
|
||||
method: 'GET'
|
||||
})
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
@@ -1,27 +1,25 @@
|
||||
import http from '@/utils/http/axios'
|
||||
import http from '@/utils/http/axios';
|
||||
|
||||
export interface BasicResponseModel<T = any> {
|
||||
code: number
|
||||
message: string
|
||||
result: T
|
||||
code: number;
|
||||
message: string;
|
||||
result: T;
|
||||
}
|
||||
|
||||
export interface BasicPageParams {
|
||||
pageNumber: number
|
||||
pageSize: number
|
||||
total: number
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取用户信息
|
||||
*/
|
||||
export function getUserInfo() {
|
||||
return http.request(
|
||||
{
|
||||
return http.request({
|
||||
url: '/admin_info',
|
||||
method: 'get'
|
||||
}
|
||||
)
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,12 +30,12 @@ export function login(params) {
|
||||
{
|
||||
url: '/login',
|
||||
method: 'POST',
|
||||
params
|
||||
params,
|
||||
},
|
||||
{
|
||||
isTransformRequestResult: false
|
||||
isTransformRequestResult: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,14 +44,14 @@ export function login(params) {
|
||||
export function changePassword(params, uid) {
|
||||
return http.request(
|
||||
{
|
||||
url: `/user/u${ uid }/changepw`,
|
||||
url: `/user/u${uid}/changepw`,
|
||||
method: 'POST',
|
||||
params
|
||||
params,
|
||||
},
|
||||
{
|
||||
isTransformRequestResult: false
|
||||
isTransformRequestResult: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +61,6 @@ export function logout(params) {
|
||||
return http.request({
|
||||
url: '/login/logout',
|
||||
method: 'POST',
|
||||
params
|
||||
})
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import http from '@/utils/http/axios'
|
||||
import http from '@/utils/http/axios';
|
||||
|
||||
//获取table
|
||||
export function getTableList(params) {
|
||||
return http.request(
|
||||
{
|
||||
return http.request({
|
||||
url: '/table/list',
|
||||
method: 'get',
|
||||
params
|
||||
}
|
||||
)
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<n-dialog-provider>
|
||||
<DialogContent/>
|
||||
<DialogContent />
|
||||
<n-notification-provider>
|
||||
<n-message-provider>
|
||||
<MessageContent/>
|
||||
<MessageContent />
|
||||
<slot slot="default"></slot>
|
||||
</n-message-provider>
|
||||
</n-notification-provider>
|
||||
@@ -11,15 +11,15 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { MessageContent } from '@/components/MessageContent'
|
||||
import { DialogContent } from '@/components/DialogContent'
|
||||
import { defineComponent } from 'vue';
|
||||
import { MessageContent } from '@/components/MessageContent';
|
||||
import { DialogContent } from '@/components/DialogContent';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'Application',
|
||||
components: { MessageContent, DialogContent },
|
||||
setup() {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
return {};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import AppProvider from './Application.vue'
|
||||
import AppProvider from './Application.vue';
|
||||
|
||||
export { AppProvider }
|
||||
export { AppProvider };
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, watchEffect, unref, onMounted, watch } from 'vue';
|
||||
import { useTransition, TransitionPresets } from '@vueuse/core';
|
||||
import { isNumber } from '@/utils/is';
|
||||
import { defineComponent, ref, computed, watchEffect, unref, onMounted, watch } from 'vue';
|
||||
import { useTransition, TransitionPresets } from '@vueuse/core';
|
||||
import { isNumber } from '@/utils/is';
|
||||
|
||||
const props = {
|
||||
const props = {
|
||||
startVal: { type: Number, default: 0 },
|
||||
endVal: { type: Number, default: 2021 },
|
||||
duration: { type: Number, default: 1500 },
|
||||
@@ -36,9 +36,9 @@ const props = {
|
||||
* Digital animation
|
||||
*/
|
||||
transition: { type: String, default: 'linear' },
|
||||
};
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'CountTo',
|
||||
props,
|
||||
emits: ['onStarted', 'onFinished'],
|
||||
@@ -106,5 +106,5 @@ export default defineComponent({
|
||||
|
||||
return { value, start, reset };
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import DialogContent from './index.vue'
|
||||
import DialogContent from './index.vue';
|
||||
|
||||
export { DialogContent }
|
||||
export { DialogContent };
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template></template>
|
||||
<script lang="ts">
|
||||
import { useDialog } from 'naive-ui'
|
||||
import { useDialog } from 'naive-ui';
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'DialogContent',
|
||||
setup() {
|
||||
//挂载在 window 方便与在js中使用
|
||||
window.$dialog = useDialog()
|
||||
}
|
||||
}
|
||||
window['$dialog'] = useDialog();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -7,23 +7,21 @@
|
||||
@contextmenu.prevent
|
||||
>
|
||||
<template v-if="!showLogin">
|
||||
|
||||
<div class="lock-box">
|
||||
<div class="lock">
|
||||
<span class="lock-icon" title="解锁屏幕" @click="onLockLogin(true)">
|
||||
<n-icon>
|
||||
<lock-outlined/>
|
||||
<lock-outlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!--充电-->
|
||||
<recharge
|
||||
:battery="battery"
|
||||
:battery-status="batteryStatus"
|
||||
:calc-discharging-time="calcDischargingTime"
|
||||
></recharge>
|
||||
/>
|
||||
|
||||
<div class="local-time">
|
||||
<div class="time">{{ hour }}:{{ minute }}</div>
|
||||
@@ -31,9 +29,9 @@
|
||||
</div>
|
||||
<div class="computer-status">
|
||||
<span :class="{ offline: !online }" class="network">
|
||||
<wifi-outlined class="network"/>
|
||||
<wifi-outlined class="network" />
|
||||
</span>
|
||||
<api-outlined/>
|
||||
<api-outlined />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -42,7 +40,7 @@
|
||||
<div class="login-box">
|
||||
<n-avatar :size="128">
|
||||
<n-icon>
|
||||
<user-outlined/>
|
||||
<user-outlined />
|
||||
</n-icon>
|
||||
</n-avatar>
|
||||
<div class="username">{{ loginParams.username }}</div>
|
||||
@@ -50,11 +48,12 @@
|
||||
type="password"
|
||||
autofocus
|
||||
v-model:value="loginParams.password"
|
||||
placeholder="请输入登录密码">
|
||||
placeholder="请输入登录密码"
|
||||
>
|
||||
<template #suffix>
|
||||
<n-icon @click="onLogin" style="cursor: pointer;">
|
||||
<LoadingOutlined v-if="loginLoading"/>
|
||||
<arrow-right-outlined v-else/>
|
||||
<n-icon @click="onLogin" style="cursor: pointer">
|
||||
<LoadingOutlined v-if="loginLoading" />
|
||||
<arrow-right-outlined v-else />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-input>
|
||||
@@ -64,62 +63,60 @@
|
||||
</div>
|
||||
|
||||
<div class="w-full mt-1 flex justify-around">
|
||||
<div><a @click="showLogin=false">返回</a></div>
|
||||
<div><a @click="showLogin = false">返回</a></div>
|
||||
<div><a @click="goLogin">重新登录</a></div>
|
||||
<div><a @click="onLogin">进入系统</a></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, reactive, toRefs, computed } from 'vue'
|
||||
import { ResultEnum } from '@/enums/httpEnum'
|
||||
import recharge from './Recharge.vue'
|
||||
import {
|
||||
import { defineComponent, reactive, toRefs } from 'vue';
|
||||
import { ResultEnum } from '@/enums/httpEnum';
|
||||
import recharge from './Recharge.vue';
|
||||
import {
|
||||
LockOutlined,
|
||||
LoadingOutlined,
|
||||
UnlockOutlined,
|
||||
UserOutlined,
|
||||
ApiOutlined,
|
||||
ArrowRightOutlined,
|
||||
WifiOutlined,
|
||||
} from '@vicons/antd'
|
||||
} from '@vicons/antd';
|
||||
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useOnline } from '@/hooks/useOnline'
|
||||
import { useTime } from '@/hooks/useTime'
|
||||
import { useBattery } from '@/hooks/useBattery'
|
||||
import { useLockscreenStore } from '@/store/modules/lockscreen'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useOnline } from '@/hooks/useOnline';
|
||||
import { useTime } from '@/hooks/useTime';
|
||||
import { useBattery } from '@/hooks/useBattery';
|
||||
import { useLockscreenStore } from '@/store/modules/lockscreen';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'Lockscreen',
|
||||
components: {
|
||||
LockOutlined,
|
||||
LoadingOutlined,
|
||||
UnlockOutlined,
|
||||
UserOutlined,
|
||||
ArrowRightOutlined,
|
||||
ApiOutlined,
|
||||
WifiOutlined,
|
||||
recharge,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const useLockscreen = useLockscreenStore()
|
||||
setup() {
|
||||
const useLockscreen = useLockscreenStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 获取时间
|
||||
const { month, day, hour, minute, second, week } = useTime()
|
||||
const { online } = useOnline()
|
||||
const { month, day, hour, minute, second, week } = useTime();
|
||||
const { online } = useOnline();
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const { battery, batteryStatus, calcDischargingTime } = useBattery()
|
||||
const { username } = userStore.getUserInfo || {}
|
||||
const { battery, batteryStatus, calcDischargingTime } = useBattery();
|
||||
const userInfo: object = userStore.getUserInfo || {};
|
||||
const username = userInfo['username'] || '';
|
||||
const state = reactive({
|
||||
showLogin: false,
|
||||
loginLoading: false, // 正在登录
|
||||
@@ -127,45 +124,45 @@ export default defineComponent({
|
||||
errorMsg: '密码错误',
|
||||
loginParams: {
|
||||
username: username || '',
|
||||
password: ''
|
||||
}
|
||||
})
|
||||
password: '',
|
||||
},
|
||||
});
|
||||
|
||||
// 解锁登录
|
||||
const onLockLogin = (value: boolean) => (state.showLogin = value)
|
||||
const onLockLogin = (value: boolean) => (state.showLogin = value);
|
||||
|
||||
// 登录
|
||||
const onLogin = async () => {
|
||||
if (!state.loginParams.password.trim()) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
isLock: true,
|
||||
...state.loginParams
|
||||
}
|
||||
state.loginLoading = true
|
||||
const { code, result, message } = await userStore.login(params)
|
||||
...state.loginParams,
|
||||
};
|
||||
state.loginLoading = true;
|
||||
const { code, message } = await userStore.login(params);
|
||||
if (code === ResultEnum.SUCCESS) {
|
||||
onLockLogin(false)
|
||||
useLockscreen.setLock(false)
|
||||
onLockLogin(false);
|
||||
useLockscreen.setLock(false);
|
||||
} else {
|
||||
state.errorMsg = message
|
||||
state.isLoginError = true
|
||||
}
|
||||
state.loginLoading = false
|
||||
state.errorMsg = message;
|
||||
state.isLoginError = true;
|
||||
}
|
||||
state.loginLoading = false;
|
||||
};
|
||||
|
||||
//重新登录
|
||||
const goLogin = () => {
|
||||
onLockLogin(false)
|
||||
useLockscreen.setLock(false)
|
||||
onLockLogin(false);
|
||||
useLockscreen.setLock(false);
|
||||
router.replace({
|
||||
path: '/login',
|
||||
query: {
|
||||
redirect: route.fullPath
|
||||
}
|
||||
})
|
||||
}
|
||||
redirect: route.fullPath,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
@@ -181,14 +178,14 @@ export default defineComponent({
|
||||
calcDischargingTime,
|
||||
onLockLogin,
|
||||
onLogin,
|
||||
goLogin
|
||||
}
|
||||
}
|
||||
})
|
||||
goLogin,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.lockscreen {
|
||||
.lockscreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -300,5 +297,5 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,33 +20,33 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'HuaweiCharge',
|
||||
// props: ['batteryStatus', 'battery', 'calcDischargingTime'],
|
||||
props: {
|
||||
battery: {
|
||||
// 电池对象
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
default: () => ({}),
|
||||
},
|
||||
calcDischargingTime: {
|
||||
// 电池剩余时间可用时间
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
batteryStatus: {
|
||||
// 电池状态
|
||||
type: String,
|
||||
validator: (val: string) => ['充电中', '已充满', '已断开电源'].includes(val)
|
||||
}
|
||||
}
|
||||
})
|
||||
validator: (val: string) => ['充电中', '已充满', '已断开电源'].includes(val),
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
.container {
|
||||
position: absolute;
|
||||
bottom: 20vh;
|
||||
left: 50vw;
|
||||
@@ -129,11 +129,11 @@ export default defineComponent({
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@width: ~`Math.round(Math.random() * 100)` px;
|
||||
@left: calc(15px + `Math.round(Math.random(70))`);
|
||||
each(range(15), {
|
||||
@width: ~`Math.round(Math.random() * 100) ` px;
|
||||
@left: calc(15px + `Math.round(Math.random(70)) `);
|
||||
each(range(15), {
|
||||
.xiaoma-@{value} {
|
||||
height: (@value * 50px);
|
||||
}
|
||||
@@ -147,8 +147,7 @@ each(range(15), {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@keyframes rotate {
|
||||
@keyframes rotate {
|
||||
50% {
|
||||
border-radius: 45% / 42% 38% 58% 49%;
|
||||
}
|
||||
@@ -156,9 +155,9 @@ each(range(15), {
|
||||
100% {
|
||||
transform: translate(-50%, -50%) rotate(720deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes moveToTop {
|
||||
@keyframes moveToTop {
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -167,11 +166,11 @@ each(range(15), {
|
||||
opacity: 0.1;
|
||||
transform: translate(-50%, -180px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes hueRotate {
|
||||
@keyframes hueRotate {
|
||||
100% {
|
||||
filter: contrast(15) hue-rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import LockScreen from './Lockscreen.vue'
|
||||
import LockScreen from './Lockscreen.vue';
|
||||
|
||||
export { LockScreen }
|
||||
export { LockScreen };
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import MessageContent from './index.vue'
|
||||
import MessageContent from './index.vue';
|
||||
|
||||
export { MessageContent }
|
||||
export { MessageContent };
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template></template>
|
||||
<script lang="ts">
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { useMessage } from 'naive-ui';
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'MessageContent',
|
||||
setup() {
|
||||
//挂载在 window 方便与在js中使用
|
||||
window.$message = useMessage()
|
||||
}
|
||||
}
|
||||
window['$message'] = useMessage();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -2,4 +2,3 @@ export { default as BasicTable } from './src/Table.vue';
|
||||
export { default as TableAction } from './src/components/TableAction.vue';
|
||||
export * from './src/types/table';
|
||||
export * from './src/types/tableAction';
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
<template>
|
||||
<div class="table-toolbar">
|
||||
|
||||
<!--顶部左侧区域-->
|
||||
<div class="flex items-center table-toolbar-left ">
|
||||
<div class="flex items-center table-toolbar-left">
|
||||
<template v-if="title">
|
||||
<div class="table-toolbar-left-title">
|
||||
{{ title }}
|
||||
<n-tooltip trigger="hover" v-if="titleTooltip">
|
||||
<template #trigger>
|
||||
<n-icon size="18" class="ml-1 cursor-pointer text-gray-400">
|
||||
<QuestionCircleOutlined/>
|
||||
<QuestionCircleOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
{{ titleTooltip }}
|
||||
@@ -20,7 +19,6 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center table-toolbar-right">
|
||||
|
||||
<!--顶部右侧区域-->
|
||||
<slot name="toolbar"></slot>
|
||||
|
||||
@@ -29,7 +27,7 @@
|
||||
<template #trigger>
|
||||
<div class="table-toolbar-right-icon" @click="reload">
|
||||
<n-icon size="18">
|
||||
<ReloadOutlined/>
|
||||
<ReloadOutlined />
|
||||
</n-icon>
|
||||
</div>
|
||||
</template>
|
||||
@@ -40,9 +38,14 @@
|
||||
<n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<div class="table-toolbar-right-icon">
|
||||
<n-dropdown @select="densitySelect" trigger="click" :options="densityOptions" v-model:value="tableSize">
|
||||
<n-dropdown
|
||||
@select="densitySelect"
|
||||
trigger="click"
|
||||
:options="densityOptions"
|
||||
v-model:value="tableSize"
|
||||
>
|
||||
<n-icon size="18">
|
||||
<ColumnHeightOutlined/>
|
||||
<ColumnHeightOutlined />
|
||||
</n-icon>
|
||||
</n-dropdown>
|
||||
</div>
|
||||
@@ -51,10 +54,8 @@
|
||||
</n-tooltip>
|
||||
|
||||
<!--表格设置单独抽离成组件-->
|
||||
<ColumnSetting></ColumnSetting>
|
||||
|
||||
<ColumnSetting />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="s-table">
|
||||
<n-data-table
|
||||
@@ -71,56 +72,53 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { NDataTable } from 'naive-ui'
|
||||
import { ref, defineComponent, reactive, unref, onMounted, toRaw, onBeforeMount, computed, toRefs, watch } from "vue"
|
||||
import { ReloadOutlined, ColumnHeightOutlined, SettingOutlined, DragOutlined, QuestionCircleOutlined } from '@vicons/antd'
|
||||
import { createTableContext } from './hooks/useTableContext';
|
||||
import { NDataTable } from 'naive-ui';
|
||||
import { ref, defineComponent, reactive, unref, toRaw, computed, toRefs } from 'vue';
|
||||
import { ReloadOutlined, ColumnHeightOutlined, QuestionCircleOutlined } from '@vicons/antd';
|
||||
import { createTableContext } from './hooks/useTableContext';
|
||||
|
||||
import ColumnSetting from './components/settings/ColumnSetting.vue'
|
||||
import ColumnSetting from './components/settings/ColumnSetting.vue';
|
||||
|
||||
import { useLoading } from './hooks/useLoading';
|
||||
import { useColumns } from './hooks/useColumns';
|
||||
import { useDataSource } from './hooks/useDataSource';
|
||||
import { usePagination } from './hooks/usePagination';
|
||||
import { useLoading } from './hooks/useLoading';
|
||||
import { useColumns } from './hooks/useColumns';
|
||||
import { useDataSource } from './hooks/useDataSource';
|
||||
import { usePagination } from './hooks/usePagination';
|
||||
|
||||
import { basicProps } from './props'
|
||||
import { basicProps } from './props';
|
||||
|
||||
import { BasicTableProps } from './types/table'
|
||||
import { BasicTableProps } from './types/table';
|
||||
|
||||
|
||||
const densityOptions = [
|
||||
const densityOptions = [
|
||||
{
|
||||
type: "menu",
|
||||
type: 'menu',
|
||||
label: '紧凑',
|
||||
key: 'small',
|
||||
},
|
||||
{
|
||||
type: "menu",
|
||||
type: 'menu',
|
||||
label: '默认',
|
||||
key: "medium"
|
||||
key: 'medium',
|
||||
},
|
||||
{
|
||||
type: "menu",
|
||||
type: 'menu',
|
||||
label: '宽松',
|
||||
key: 'large'
|
||||
}
|
||||
]
|
||||
key: 'large',
|
||||
},
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ReloadOutlined, ColumnHeightOutlined, SettingOutlined, DragOutlined, ColumnSetting, QuestionCircleOutlined
|
||||
ReloadOutlined,
|
||||
ColumnHeightOutlined,
|
||||
ColumnSetting,
|
||||
QuestionCircleOutlined,
|
||||
},
|
||||
props: {
|
||||
...NDataTable.props, // 这里继承原 UI 组件的 props
|
||||
...basicProps
|
||||
...basicProps,
|
||||
},
|
||||
emits: [
|
||||
'fetch-success',
|
||||
'fetch-error',
|
||||
'update:checked-row-keys'
|
||||
],
|
||||
emits: ['fetch-success', 'fetch-error', 'update:checked-row-keys'],
|
||||
setup(props, { emit }) {
|
||||
|
||||
const wrapRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
|
||||
const tableData = ref<Recordable[]>([]);
|
||||
@@ -132,68 +130,58 @@ export default defineComponent({
|
||||
|
||||
const { getLoading, setLoading } = useLoading(getProps);
|
||||
|
||||
const {
|
||||
getPaginationInfo,
|
||||
getPagination,
|
||||
setPagination,
|
||||
setShowPagination,
|
||||
getShowPagination,
|
||||
} = usePagination(getProps)
|
||||
const { getPaginationInfo, setPagination } = usePagination(getProps);
|
||||
|
||||
const { getDataSourceRef, getRowKey, getDataSource, setDataSource, reload } = useDataSource(
|
||||
getProps, {
|
||||
const { getDataSourceRef, getRowKey, reload } = useDataSource(
|
||||
getProps,
|
||||
{
|
||||
getPaginationInfo,
|
||||
setPagination,
|
||||
tableData,
|
||||
setLoading
|
||||
}, emit
|
||||
)
|
||||
setLoading,
|
||||
},
|
||||
emit
|
||||
);
|
||||
|
||||
const {
|
||||
getPageColumns,
|
||||
setColumns,
|
||||
getColumns,
|
||||
getCacheColumns,
|
||||
setCacheColumnsField,
|
||||
getColumnsRef
|
||||
} = useColumns(getProps)
|
||||
const { getPageColumns, setColumns, getColumns, getCacheColumns, setCacheColumnsField } =
|
||||
useColumns(getProps);
|
||||
|
||||
const state = reactive({
|
||||
tableSize: 'medium',
|
||||
isColumnSetting: false
|
||||
})
|
||||
isColumnSetting: false,
|
||||
});
|
||||
|
||||
//页码切换
|
||||
function updatePage(page) {
|
||||
setPagination({ page: page, });
|
||||
reload()
|
||||
setPagination({ page: page });
|
||||
reload();
|
||||
}
|
||||
|
||||
//分页数量切换
|
||||
function updatePageSize(size) {
|
||||
setPagination({ page: 1, pageSize: size, });
|
||||
reload()
|
||||
setPagination({ page: 1, pageSize: size });
|
||||
reload();
|
||||
}
|
||||
|
||||
//密度切换
|
||||
function densitySelect(e) {
|
||||
state.tableSize = e
|
||||
state.tableSize = e;
|
||||
}
|
||||
|
||||
//选中行
|
||||
function updateCheckedRowKeys(rowKeys) {
|
||||
emit('update:checked-row-keys', rowKeys)
|
||||
emit('update:checked-row-keys', rowKeys);
|
||||
}
|
||||
|
||||
//重置 Columns
|
||||
const resetColumns = () => {
|
||||
columns.map(item => {
|
||||
item.isShow = true
|
||||
})
|
||||
}
|
||||
columns.map((item) => {
|
||||
item.isShow = true;
|
||||
});
|
||||
};
|
||||
|
||||
//获取表格大小
|
||||
const getTableSize = computed(() => state.tableSize)
|
||||
const getTableSize = computed(() => state.tableSize);
|
||||
|
||||
//组装表格信息
|
||||
const getBindValues = computed(() => {
|
||||
@@ -205,13 +193,13 @@ export default defineComponent({
|
||||
rowKey: unref(getRowKey),
|
||||
data: tableData,
|
||||
size: unref(getTableSize),
|
||||
remote: true
|
||||
}
|
||||
return propsData
|
||||
})
|
||||
remote: true,
|
||||
};
|
||||
return propsData;
|
||||
});
|
||||
|
||||
//获取分页信息
|
||||
const pagination = computed(() => toRaw(unref(getPaginationInfo)))
|
||||
const pagination = computed(() => toRaw(unref(getPaginationInfo)));
|
||||
|
||||
function setProps(props: Partial<BasicTableProps>) {
|
||||
innerPropsRef.value = { ...unref(innerPropsRef), ...props };
|
||||
@@ -245,14 +233,13 @@ export default defineComponent({
|
||||
updateCheckedRowKeys,
|
||||
pagination,
|
||||
resetColumns,
|
||||
tableAction
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
tableAction,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang='less' scoped>
|
||||
.table-toolbar {
|
||||
<style lang="less" scoped>
|
||||
.table-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 0 16px 0;
|
||||
@@ -288,9 +275,9 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-toolbar-inner-popover-title {
|
||||
.table-toolbar-inner-popover-title {
|
||||
padding: 2px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,11 +12,10 @@
|
||||
>
|
||||
<slot name="more"></slot>
|
||||
<n-button v-bind="getMoreProps" class="mx-2" v-if="!$slots.more" icon-placement="right">
|
||||
|
||||
<div class="flex items-center">
|
||||
<span>更多</span>
|
||||
<n-icon size="14" class="ml-1">
|
||||
<DownOutlined/>
|
||||
<DownOutlined />
|
||||
</n-icon>
|
||||
</div>
|
||||
<!-- <template #icon>-->
|
||||
@@ -29,13 +28,13 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed, toRaw } from 'vue';
|
||||
import { ActionItem } from '@/components/Table';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
import { isString, isBoolean, isFunction } from "@/utils/is";
|
||||
import { DownOutlined } from '@vicons/antd'
|
||||
import { defineComponent, PropType, computed, toRaw } from 'vue';
|
||||
import { ActionItem } from '@/components/Table';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
import { isBoolean, isFunction } from '@/utils/is';
|
||||
import { DownOutlined } from '@vicons/antd';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'TableAction',
|
||||
components: { DownOutlined },
|
||||
props: {
|
||||
@@ -49,44 +48,36 @@ export default defineComponent({
|
||||
},
|
||||
style: {
|
||||
type: String as PropType<String>,
|
||||
default: 'button'
|
||||
default: 'button',
|
||||
},
|
||||
select:{
|
||||
select: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () =>{ }
|
||||
}
|
||||
default: () => {},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
},
|
||||
setup(props) {
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const getTooltip = computed(() => {
|
||||
return (data: string | TooltipProps): TooltipProps => {
|
||||
if (isString(data)) {
|
||||
return { title: data, placement: 'bottom' };
|
||||
} else {
|
||||
return Object.assign({ placement: 'bottom' }, data);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const actionType = props.style === 'button' ? 'default' : props.style === 'text' ? 'primary' : 'default'
|
||||
const actionText = props.style === 'button' ? undefined : props.style === 'text' ? true : undefined
|
||||
const actionType =
|
||||
props.style === 'button' ? 'default' : props.style === 'text' ? 'primary' : 'default';
|
||||
const actionText =
|
||||
props.style === 'button' ? undefined : props.style === 'text' ? true : undefined;
|
||||
|
||||
const getMoreProps = computed(() => {
|
||||
return {
|
||||
text: actionText,
|
||||
type: actionType,
|
||||
size: "small"
|
||||
}
|
||||
})
|
||||
size: 'small',
|
||||
};
|
||||
});
|
||||
|
||||
const getDropdownList = computed(() => {
|
||||
return (toRaw(props.dropDownActions) || [])
|
||||
.filter((action) => {
|
||||
return hasPermission(action.auth) && isIfShow(action);
|
||||
})
|
||||
.map((action, index) => {
|
||||
const { label, popConfirm } = action;
|
||||
.map((action) => {
|
||||
const { popConfirm } = action;
|
||||
return {
|
||||
size: 'small',
|
||||
text: actionText,
|
||||
@@ -94,7 +85,7 @@ export default defineComponent({
|
||||
...action,
|
||||
...popConfirm,
|
||||
onConfirm: popConfirm?.confirm,
|
||||
onCancel: popConfirm?.cancel
|
||||
onCancel: popConfirm?.cancel,
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -137,9 +128,8 @@ export default defineComponent({
|
||||
return {
|
||||
getActions,
|
||||
getDropdownList,
|
||||
getTooltip,
|
||||
getMoreProps
|
||||
}
|
||||
}
|
||||
})
|
||||
getMoreProps,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -5,46 +5,62 @@
|
||||
<n-popover trigger="click" :width="230" class="toolbar-popover" placement="bottom-end">
|
||||
<template #trigger>
|
||||
<n-icon size="18">
|
||||
<SettingOutlined/>
|
||||
<SettingOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
<template #header>
|
||||
<div class="table-toolbar-inner-popover-title">
|
||||
<n-space>
|
||||
<n-checkbox v-model:checked="checkAll" @update:checked="onCheckAll">列展示</n-checkbox>
|
||||
<n-checkbox v-model:checked="selection" @update:checked="onSelection">勾选列</n-checkbox>
|
||||
<n-button text type="info" size="small" class="mt-1" @click="resetColumns">重置</n-button>
|
||||
<n-checkbox v-model:checked="checkAll" @update:checked="onCheckAll"
|
||||
>列展示</n-checkbox
|
||||
>
|
||||
<n-checkbox v-model:checked="selection" @update:checked="onSelection"
|
||||
>勾选列</n-checkbox
|
||||
>
|
||||
<n-button text type="info" size="small" class="mt-1" @click="resetColumns"
|
||||
>重置</n-button
|
||||
>
|
||||
</n-space>
|
||||
</div>
|
||||
</template>
|
||||
<div class="table-toolbar-inner">
|
||||
<n-checkbox-group v-model:value="checkList" @update:value="onChange">
|
||||
<Draggable v-model="columnsList" animation="300" item-key="key" @end="draggableEnd">
|
||||
<template #item="{element, index}">
|
||||
<div class="table-toolbar-inner-checkbox"
|
||||
:class="{'table-toolbar-inner-checkbox-dark':getDarkTheme === true}">
|
||||
<template #item="{ element }">
|
||||
<div
|
||||
class="table-toolbar-inner-checkbox"
|
||||
:class="{ 'table-toolbar-inner-checkbox-dark': getDarkTheme === true }"
|
||||
>
|
||||
<span class="drag-icon">
|
||||
<n-icon size="18">
|
||||
<DragOutlined/>
|
||||
<DragOutlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
<n-checkbox :value="element.key" :label="element.title"/>
|
||||
<n-checkbox :value="element.key" :label="element.title" />
|
||||
<div class="fixed-item">
|
||||
<n-tooltip trigger="hover" placement="bottom">
|
||||
<template #trigger>
|
||||
<n-icon size="18" :color="element.fixed === 'left' ? '#2080f0':undefined"
|
||||
class="cursor-pointer" @click="fixedColumn(element,'left')">
|
||||
<VerticalRightOutlined/>
|
||||
<n-icon
|
||||
size="18"
|
||||
:color="element.fixed === 'left' ? '#2080f0' : undefined"
|
||||
class="cursor-pointer"
|
||||
@click="fixedColumn(element, 'left')"
|
||||
>
|
||||
<VerticalRightOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>固定到左侧</span>
|
||||
</n-tooltip>
|
||||
<n-divider vertical/>
|
||||
<n-divider vertical />
|
||||
<n-tooltip trigger="hover" placement="bottom">
|
||||
<template #trigger>
|
||||
<n-icon size="18" :color="element.fixed === 'right' ? '#2080f0':undefined"
|
||||
class="cursor-pointer" @click="fixedColumn(element,'right')">
|
||||
<VerticalLeftOutlined/>
|
||||
<n-icon
|
||||
size="18"
|
||||
:color="element.fixed === 'right' ? '#2080f0' : undefined"
|
||||
class="cursor-pointer"
|
||||
@click="fixedColumn(element, 'right')"
|
||||
>
|
||||
<VerticalLeftOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>固定到右侧</span>
|
||||
@@ -63,26 +79,34 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, defineComponent, reactive, unref, toRaw, computed, toRefs, watchEffect } from "vue"
|
||||
import { useTableContext } from '../../hooks/useTableContext';
|
||||
import { ReloadOutlined, ColumnHeightOutlined, SettingOutlined, DragOutlined, VerticalRightOutlined, VerticalLeftOutlined } from '@vicons/antd'
|
||||
import Draggable from 'vuedraggable/src/vuedraggable'
|
||||
import { useDesignSetting } from "@/hooks/setting/useDesignSetting";
|
||||
import { ref, defineComponent, reactive, unref, toRaw, computed, toRefs, watchEffect } from 'vue';
|
||||
import { useTableContext } from '../../hooks/useTableContext';
|
||||
import {
|
||||
SettingOutlined,
|
||||
DragOutlined,
|
||||
VerticalRightOutlined,
|
||||
VerticalLeftOutlined,
|
||||
} from '@vicons/antd';
|
||||
import Draggable from 'vuedraggable/src/vuedraggable';
|
||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
||||
|
||||
interface Options {
|
||||
interface Options {
|
||||
title: string;
|
||||
key: string;
|
||||
fixed?: boolean | 'left' | 'right';
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'ColumnSetting',
|
||||
components: {
|
||||
ReloadOutlined, ColumnHeightOutlined, SettingOutlined, DragOutlined, Draggable,
|
||||
VerticalRightOutlined, VerticalLeftOutlined
|
||||
SettingOutlined,
|
||||
DragOutlined,
|
||||
Draggable,
|
||||
VerticalRightOutlined,
|
||||
VerticalLeftOutlined,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const { getDarkTheme } = useDesignSetting()
|
||||
setup() {
|
||||
const { getDarkTheme } = useDesignSetting();
|
||||
const table = useTableContext();
|
||||
const columnsList = ref<Options[]>([]);
|
||||
const cacheColumnsList = ref<Options[]>([]);
|
||||
@@ -91,12 +115,12 @@ export default defineComponent({
|
||||
selection: false,
|
||||
checkAll: true,
|
||||
checkList: [],
|
||||
defaultCheckList: []
|
||||
})
|
||||
defaultCheckList: [],
|
||||
});
|
||||
|
||||
const getSelection = computed(() => {
|
||||
return state.selection
|
||||
})
|
||||
return state.selection;
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
const columns = table.getColumns();
|
||||
@@ -107,96 +131,93 @@ export default defineComponent({
|
||||
|
||||
//初始化
|
||||
function init() {
|
||||
const columns = getColumns();
|
||||
const checkList = columns.map(item => item.key)
|
||||
state.checkList = checkList
|
||||
state.defaultCheckList = checkList
|
||||
columnsList.value = columns
|
||||
cacheColumnsList.value = columns
|
||||
const columns: any[] = getColumns();
|
||||
const checkList: any = columns.map((item) => item.key);
|
||||
state.checkList = checkList;
|
||||
state.defaultCheckList = checkList;
|
||||
columnsList.value = columns;
|
||||
cacheColumnsList.value = columns;
|
||||
}
|
||||
|
||||
//切换
|
||||
function onChange(checkList) {
|
||||
if (state.selection) {
|
||||
checkList.unshift('selection')
|
||||
checkList.unshift('selection');
|
||||
}
|
||||
setColumns(checkList)
|
||||
setColumns(checkList);
|
||||
}
|
||||
|
||||
//设置
|
||||
function setColumns(columns) {
|
||||
table.setColumns(columns)
|
||||
table.setColumns(columns);
|
||||
}
|
||||
|
||||
//获取
|
||||
function getColumns() {
|
||||
let newRet = []
|
||||
table.getColumns().forEach(item => {
|
||||
newRet.push({ ...item })
|
||||
})
|
||||
return newRet
|
||||
let newRet = [];
|
||||
table.getColumns().forEach((item) => {
|
||||
newRet.push({ ...item });
|
||||
});
|
||||
return newRet;
|
||||
}
|
||||
|
||||
//重置
|
||||
function resetColumns() {
|
||||
state.checkList = [...state.defaultCheckList]
|
||||
state.checkList = [...state.defaultCheckList];
|
||||
state.checkAll = true;
|
||||
let cacheColumnsKeys: any[] = table.getCacheColumns()
|
||||
let newColumns = cacheColumnsKeys.map(item => {
|
||||
let cacheColumnsKeys: any[] = table.getCacheColumns();
|
||||
let newColumns = cacheColumnsKeys.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
fixed: undefined
|
||||
}
|
||||
})
|
||||
fixed: undefined,
|
||||
};
|
||||
});
|
||||
setColumns(newColumns);
|
||||
columnsList.value = newColumns
|
||||
columnsList.value = newColumns;
|
||||
}
|
||||
|
||||
//全选
|
||||
function onCheckAll(e) {
|
||||
let checkList = table.getCacheColumns(true)
|
||||
let checkList = table.getCacheColumns(true);
|
||||
if (e) {
|
||||
setColumns(checkList);
|
||||
state.checkList = checkList
|
||||
state.checkList = checkList;
|
||||
} else {
|
||||
setColumns([]);
|
||||
state.checkList = []
|
||||
state.checkList = [];
|
||||
}
|
||||
}
|
||||
|
||||
//拖拽排序
|
||||
function draggableEnd() {
|
||||
const newColumns = toRaw(unref(columnsList))
|
||||
columnsList.value = newColumns
|
||||
const newColumns = toRaw(unref(columnsList));
|
||||
columnsList.value = newColumns;
|
||||
setColumns(newColumns);
|
||||
}
|
||||
|
||||
//勾选列
|
||||
function onSelection(e) {
|
||||
let checkList = table.getCacheColumns()
|
||||
let checkList = table.getCacheColumns();
|
||||
if (e) {
|
||||
checkList.unshift({ type: 'selection', key: 'selection' })
|
||||
checkList.unshift({ type: 'selection', key: 'selection' });
|
||||
setColumns(checkList);
|
||||
} else {
|
||||
checkList.splice(0, 1)
|
||||
checkList.splice(0, 1);
|
||||
setColumns(checkList);
|
||||
}
|
||||
}
|
||||
|
||||
//固定
|
||||
function fixedColumn(item, fixed) {
|
||||
console.log('item:', item)
|
||||
if (!state.checkList.includes(item.key)) return;
|
||||
let columns = getColumns();
|
||||
const isFixed = item.fixed === fixed ? undefined : fixed
|
||||
let index = columns.findIndex(res => res.key === item.key)
|
||||
console.log('index:', index)
|
||||
const isFixed = item.fixed === fixed ? undefined : fixed;
|
||||
let index = columns.findIndex((res) => res.key === item.key);
|
||||
if (index !== -1) {
|
||||
columns[index].fixed = isFixed;
|
||||
}
|
||||
table.setCacheColumnsField(item.key, { fixed: isFixed })
|
||||
columnsList.value[index].fixed = isFixed
|
||||
console.log('columnsList:', columnsList.value)
|
||||
table.setCacheColumnsField(item.key, { fixed: isFixed });
|
||||
columnsList.value[index].fixed = isFixed;
|
||||
setColumns(columns);
|
||||
}
|
||||
|
||||
@@ -210,14 +231,14 @@ export default defineComponent({
|
||||
resetColumns,
|
||||
fixedColumn,
|
||||
draggableEnd,
|
||||
getSelection
|
||||
}
|
||||
}
|
||||
})
|
||||
getSelection,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.table-toolbar {
|
||||
.table-toolbar {
|
||||
&-inner-popover-title {
|
||||
padding: 3px 0;
|
||||
}
|
||||
@@ -234,9 +255,9 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-toolbar-inner {
|
||||
.table-toolbar-inner {
|
||||
&-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -270,14 +291,14 @@ export default defineComponent({
|
||||
|
||||
&-checkbox-dark {
|
||||
&:hover {
|
||||
background: hsla(0, 0%, 100%, .08);
|
||||
background: hsla(0, 0%, 100%, 0.08);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-popover {
|
||||
.toolbar-popover {
|
||||
.n-popover__content {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import componentSetting from '@/settings/componentSetting'
|
||||
import componentSetting from '@/settings/componentSetting';
|
||||
|
||||
const { table } = componentSetting
|
||||
const { table } = componentSetting;
|
||||
|
||||
const { apiSetting, defaultPageSize, pageSizes } = table;
|
||||
|
||||
@@ -9,7 +9,3 @@ export const DEFAULTPAGESIZE = defaultPageSize;
|
||||
export const APISETTING = apiSetting;
|
||||
|
||||
export const PAGESIZES = pageSizes;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { ref, Ref, ComputedRef, unref, computed, watch, toRaw } from 'vue';
|
||||
import type { BasicColumn, BasicTableProps } from '../types/table';
|
||||
import { isEqual, cloneDeep } from 'lodash-es';
|
||||
import { isArray, isString } from '@/utils/is';
|
||||
import { isArray, isString, isBoolean, isFunction } from '@/utils/is';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
import { isString, isBoolean, isFunction } from "@/utils/is";
|
||||
import { ActionItem } from "@/components/Table";
|
||||
import { ActionItem } from '@/components/Table';
|
||||
|
||||
export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||
const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
|
||||
@@ -16,7 +15,7 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||
handleActionColumn(propsRef, columns);
|
||||
if (!columns) return [];
|
||||
return columns;
|
||||
})
|
||||
});
|
||||
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
@@ -38,9 +37,10 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||
const pageColumns = unref(getColumnsRef);
|
||||
const columns = cloneDeep(pageColumns);
|
||||
return columns.filter((column) => {
|
||||
// @ts-ignore
|
||||
return hasPermission(column.auth) && isIfShow(column);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => unref(propsRef).columns,
|
||||
@@ -53,8 +53,9 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||
function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: BasicColumn[]) {
|
||||
const { actionColumn } = unref(propsRef);
|
||||
if (!actionColumn) return;
|
||||
// @ts-ignore
|
||||
columns.push({
|
||||
...actionColumn
|
||||
...actionColumn,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -72,34 +73,32 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||
if (!isString(columns[0])) {
|
||||
columnsRef.value = columns;
|
||||
} else {
|
||||
const newColumns: any[] = []
|
||||
cacheColumns.forEach(item => {
|
||||
const newColumns: any[] = [];
|
||||
cacheColumns.forEach((item) => {
|
||||
if (columnList.includes(item.key)) {
|
||||
newColumns.push({ ...item })
|
||||
newColumns.push({ ...item });
|
||||
}
|
||||
})
|
||||
});
|
||||
if (!isEqual(cacheKeys, columns)) {
|
||||
newColumns.sort((prev, next) => {
|
||||
return (
|
||||
cacheKeys.indexOf(prev.key) - cacheKeys.indexOf(next.key)
|
||||
);
|
||||
return cacheKeys.indexOf(prev.key) - cacheKeys.indexOf(next.key);
|
||||
});
|
||||
}
|
||||
columnsRef.value = newColumns
|
||||
columnsRef.value = newColumns;
|
||||
}
|
||||
}
|
||||
|
||||
//获取
|
||||
function getColumns() {
|
||||
let columns = toRaw(unref(getColumnsRef));
|
||||
return columns.map(item => {
|
||||
return { ...item, title: item.title, key: item.key, fixed: item.fixed || undefined }
|
||||
})
|
||||
const columns = toRaw(unref(getColumnsRef));
|
||||
return columns.map((item) => {
|
||||
return { ...item, title: item.title, key: item.key, fixed: item.fixed || undefined };
|
||||
});
|
||||
}
|
||||
|
||||
//获取原始
|
||||
function getCacheColumns(isKey?: boolean): any[] {
|
||||
return isKey ? cacheColumns.map(item => item.key) : cacheColumns;
|
||||
return isKey ? cacheColumns.map((item) => item.key) : cacheColumns;
|
||||
}
|
||||
|
||||
//更新原始数据单个字段
|
||||
@@ -121,6 +120,6 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||
setCacheColumnsField,
|
||||
setColumns,
|
||||
getColumns,
|
||||
getPageColumns
|
||||
getPageColumns,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,12 +6,7 @@ import { APISETTING } from '../const';
|
||||
|
||||
export function useDataSource(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
{
|
||||
getPaginationInfo,
|
||||
setPagination,
|
||||
setLoading,
|
||||
tableData
|
||||
},
|
||||
{ getPaginationInfo, setPagination, setLoading, tableData },
|
||||
emit
|
||||
) {
|
||||
const dataSourceRef = ref([]);
|
||||
@@ -33,8 +28,10 @@ export function useDataSource(
|
||||
|
||||
const getRowKey = computed(() => {
|
||||
const { rowKey }: any = unref(propsRef);
|
||||
return rowKey ? rowKey : () => {
|
||||
return 'key'
|
||||
return rowKey
|
||||
? rowKey
|
||||
: () => {
|
||||
return 'key';
|
||||
};
|
||||
});
|
||||
|
||||
@@ -52,10 +49,10 @@ export function useDataSource(
|
||||
const { request, pagination }: any = unref(propsRef);
|
||||
|
||||
//组装分页信息
|
||||
const pageField = APISETTING.pageField
|
||||
const sizeField = APISETTING.sizeField
|
||||
const totalField = APISETTING.totalField
|
||||
const listField = APISETTING.listField
|
||||
const pageField = APISETTING.pageField;
|
||||
const sizeField = APISETTING.sizeField;
|
||||
const totalField = APISETTING.totalField;
|
||||
const listField = APISETTING.listField;
|
||||
|
||||
let pageParams = {};
|
||||
const { page = 1, pageSize = 10 } = unref(getPaginationInfo) as PaginationProps;
|
||||
@@ -67,13 +64,13 @@ export function useDataSource(
|
||||
pageParams[sizeField] = pageSize;
|
||||
}
|
||||
|
||||
let params = {
|
||||
const params = {
|
||||
...pageParams,
|
||||
}
|
||||
};
|
||||
const res = await request(params);
|
||||
|
||||
const resultTotal = res[totalField] || 0
|
||||
const currentPage = res[pageField]
|
||||
const resultTotal = res[totalField] || 0;
|
||||
const currentPage = res[pageField];
|
||||
|
||||
// 如果数据异常,需获取正确的页码再次执行
|
||||
if (resultTotal) {
|
||||
@@ -85,7 +82,7 @@ export function useDataSource(
|
||||
fetch(opt);
|
||||
}
|
||||
}
|
||||
let resultInfo = res[listField] ? res[listField] : []
|
||||
const resultInfo = res[listField] ? res[listField] : [];
|
||||
dataSourceRef.value = resultInfo;
|
||||
setPagination({
|
||||
[pageField]: currentPage,
|
||||
@@ -98,10 +95,10 @@ export function useDataSource(
|
||||
}
|
||||
emit('fetch-success', {
|
||||
items: unref(resultInfo),
|
||||
resultTotal
|
||||
resultTotal,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
console.error(error);
|
||||
emit('fetch-error', error);
|
||||
dataSourceRef.value = [];
|
||||
// setPagination({
|
||||
@@ -115,7 +112,7 @@ export function useDataSource(
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
fetch();
|
||||
}, 16)
|
||||
}, 16);
|
||||
});
|
||||
|
||||
function setTableData(values) {
|
||||
@@ -136,6 +133,6 @@ export function useDataSource(
|
||||
getDataSourceRef,
|
||||
getDataSource,
|
||||
setTableData,
|
||||
reload
|
||||
}
|
||||
reload,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PropType } from 'vue'
|
||||
import { BasicColumn } from './types/table'
|
||||
import type { PropType } from 'vue';
|
||||
import { BasicColumn } from './types/table';
|
||||
|
||||
export const basicProps = {
|
||||
title: {
|
||||
@@ -16,8 +16,7 @@ export const basicProps = {
|
||||
},
|
||||
tableData: {
|
||||
type: [Object],
|
||||
default: () => {
|
||||
},
|
||||
default: () => {},
|
||||
},
|
||||
columns: {
|
||||
type: [Array] as PropType<BasicColumn[]>,
|
||||
@@ -27,7 +26,7 @@ export const basicProps = {
|
||||
request: {
|
||||
type: Function as PropType<(...arg: any[]) => Promise<any>>,
|
||||
default: null,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
rowKey: {
|
||||
type: [String, Function] as PropType<string | ((record) => string)>,
|
||||
@@ -35,15 +34,14 @@ export const basicProps = {
|
||||
},
|
||||
pagination: {
|
||||
type: [Object, Boolean],
|
||||
default: () => {
|
||||
}
|
||||
default: () => {},
|
||||
},
|
||||
showPagination: {
|
||||
type: [String, Boolean],
|
||||
default: 'auto'
|
||||
default: 'auto',
|
||||
},
|
||||
actionColumn: {
|
||||
type: Object as PropType<BasicColumn>,
|
||||
default: null,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import Pagination from 'naive-ui/lib/pagination';
|
||||
import { VNodeChild } from 'vue';
|
||||
|
||||
export interface PaginationProps {
|
||||
page?: number;
|
||||
pageCount?: number,
|
||||
pageSize?: number,
|
||||
pageSizes?: number[],
|
||||
showSizePicker?: boolean,
|
||||
showQuickJumper?: boolean,
|
||||
pageCount?: number;
|
||||
pageSize?: number;
|
||||
pageSizes?: number[];
|
||||
showSizePicker?: boolean;
|
||||
showQuickJumper?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import type {
|
||||
TableBaseColumn,
|
||||
} from 'naive-ui/lib/data-table/src/interface';
|
||||
import type { TableBaseColumn } from 'naive-ui/lib/data-table/src/interface';
|
||||
|
||||
export interface BasicColumn extends TableBaseColumn {
|
||||
|
||||
}
|
||||
export type BasicColumn = TableBaseColumn;
|
||||
|
||||
export interface TableActionType {
|
||||
reload: (opt) => Promise<void>;
|
||||
emit?: any;
|
||||
getColumns: (opt) => BasicColumn[];
|
||||
getColumns: (opt?) => BasicColumn[];
|
||||
setColumns: (columns: BasicColumn[] | string[]) => void;
|
||||
}
|
||||
|
||||
export interface BasicTableProps<T = any> {
|
||||
title?: string,
|
||||
dataSource: Function,
|
||||
columns: any[],
|
||||
pagination: object,
|
||||
showPagination: boolean
|
||||
export interface BasicTableProps {
|
||||
title?: string;
|
||||
dataSource: Function;
|
||||
columns: any[];
|
||||
pagination: object;
|
||||
showPagination: boolean;
|
||||
actionColumn: any[];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NButton, NTooltip } from 'naive-ui';
|
||||
import { RoleEnum } from '/@/enums/roleEnum';
|
||||
|
||||
// @ts-ignore
|
||||
import { NButton } from 'naive-ui';
|
||||
import { RoleEnum } from '@/enums/roleEnum';
|
||||
// @ts-ignore
|
||||
export interface ActionItem extends NButton.props {
|
||||
onClick?: Fn;
|
||||
label?: string;
|
||||
@@ -13,7 +14,6 @@ export interface ActionItem extends NButton.props {
|
||||
auth?: RoleEnum | RoleEnum[] | string | string[];
|
||||
// 业务控制是否显示
|
||||
ifShow?: boolean | ((action: ActionItem) => boolean);
|
||||
tooltip?: string | TooltipProps;
|
||||
}
|
||||
|
||||
export interface PopConfirm {
|
||||
|
||||
@@ -2,42 +2,48 @@
|
||||
<div class="w-full">
|
||||
<div class="upload">
|
||||
<div class="upload-card">
|
||||
|
||||
<!--图片列表-->
|
||||
<div class="upload-card-item" :style="getCSSProperties" v-for="(item,index) in imgList">
|
||||
<div
|
||||
class="upload-card-item"
|
||||
:style="getCSSProperties"
|
||||
v-for="(item, index) in imgList"
|
||||
:key="`img_${index}`"
|
||||
>
|
||||
<div class="upload-card-item-info">
|
||||
<div class="img-box">
|
||||
<img :src="item"/>
|
||||
<img :src="item" />
|
||||
</div>
|
||||
<div class="img-box-actions">
|
||||
<n-icon size="18" class="action-icon mx-2" @click="preview(item)">
|
||||
<EyeOutlined/>
|
||||
<EyeOutlined />
|
||||
</n-icon>
|
||||
<n-icon size="18" class="action-icon mx-2" @click="remove(index)">
|
||||
<DeleteOutlined/>
|
||||
<DeleteOutlined />
|
||||
</n-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--上传图片-->
|
||||
<div class="upload-card-item upload-card-item-select-picture" :style="getCSSProperties"
|
||||
v-if="imgList.length < maxNumber">
|
||||
<div
|
||||
class="upload-card-item upload-card-item-select-picture"
|
||||
:style="getCSSProperties"
|
||||
v-if="imgList.length < maxNumber"
|
||||
>
|
||||
<n-upload
|
||||
v-bind="$props"
|
||||
:file-list-style="{ display:'none'}"
|
||||
:file-list-style="{ display: 'none' }"
|
||||
@before-upload="beforeUpload"
|
||||
@finish="finish"
|
||||
>
|
||||
<div class="flex justify-center flex-col">
|
||||
<n-icon size="18" class="m-auto">
|
||||
<PlusOutlined/>
|
||||
<PlusOutlined />
|
||||
</n-icon>
|
||||
<span class="upload-title">上传图片</span>
|
||||
</div>
|
||||
</n-upload>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,7 +53,6 @@
|
||||
{{ helpText }}
|
||||
</n-alert>
|
||||
</n-space>
|
||||
|
||||
</div>
|
||||
|
||||
<!--预览图片-->
|
||||
@@ -56,65 +61,63 @@
|
||||
preset="card"
|
||||
title="预览"
|
||||
:bordered="false"
|
||||
:style="{width: '520px'}"
|
||||
:style="{ width: '520px' }"
|
||||
>
|
||||
<img :src="previewUrl"/>
|
||||
<img :src="previewUrl" />
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, reactive, computed } from "vue";
|
||||
import { EyeOutlined, DeleteOutlined, PlusOutlined } from "@vicons/antd";
|
||||
import { NUpload } from 'naive-ui'
|
||||
import { basicProps } from "./props";
|
||||
import { useMessage, useDialog } from 'naive-ui'
|
||||
import { ResultEnum } from '@/enums/httpEnum'
|
||||
import componentSetting from '@/settings/componentSetting'
|
||||
import { useGlobSetting } from '@/hooks/setting'
|
||||
import { isString } from '@/utils/is'
|
||||
import { defineComponent, toRefs, reactive, computed } from 'vue';
|
||||
import { EyeOutlined, DeleteOutlined, PlusOutlined } from '@vicons/antd';
|
||||
import { NUpload } from 'naive-ui';
|
||||
import { basicProps } from './props';
|
||||
import { useMessage, useDialog } from 'naive-ui';
|
||||
import { ResultEnum } from '@/enums/httpEnum';
|
||||
import componentSetting from '@/settings/componentSetting';
|
||||
import { useGlobSetting } from '@/hooks/setting';
|
||||
import { isString } from '@/utils/is';
|
||||
|
||||
const globSetting = useGlobSetting()
|
||||
const globSetting = useGlobSetting();
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'BasicUpload',
|
||||
|
||||
components: { EyeOutlined, DeleteOutlined, PlusOutlined },
|
||||
props: {
|
||||
...NUpload.props, // 这里继承原 UI 组件的 props
|
||||
...basicProps
|
||||
...basicProps,
|
||||
},
|
||||
emits: ['uploadChange', 'delete'],
|
||||
setup(props, { emit }) {
|
||||
const { value, width, height } = props
|
||||
|
||||
const getCSSProperties = computed(() => {
|
||||
return {
|
||||
width: `${ width }px`,
|
||||
height: `${ height }px`,
|
||||
}
|
||||
})
|
||||
width: `${props.width}px`,
|
||||
height: `${props.height}px`,
|
||||
};
|
||||
});
|
||||
|
||||
const message = useMessage()
|
||||
const dialog = useDialog()
|
||||
const message = useMessage();
|
||||
const dialog = useDialog();
|
||||
|
||||
const state = reactive({
|
||||
showModal: false,
|
||||
previewUrl: '',
|
||||
originalImgList: [],
|
||||
imgList: []
|
||||
})
|
||||
imgList: [],
|
||||
});
|
||||
|
||||
//赋值默认图片显示
|
||||
if (value.length) {
|
||||
state.imgList = value.map(item => {
|
||||
return getImgUrl(item)
|
||||
})
|
||||
if (props.value.length) {
|
||||
state.imgList = props.value.map((item) => {
|
||||
return getImgUrl(item);
|
||||
});
|
||||
}
|
||||
|
||||
//预览
|
||||
function preview(url: string) {
|
||||
state.showModal = true
|
||||
state.previewUrl = url
|
||||
state.showModal = true;
|
||||
state.previewUrl = url;
|
||||
}
|
||||
|
||||
//删除
|
||||
@@ -125,64 +128,61 @@ export default defineComponent({
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
state.imgList.splice(index, 1)
|
||||
state.originalImgList.splice(index, 1)
|
||||
emit('uploadChange', state.originalImgList)
|
||||
emit('delete', state.originalImgList)
|
||||
state.imgList.splice(index, 1);
|
||||
state.originalImgList.splice(index, 1);
|
||||
emit('uploadChange', state.originalImgList);
|
||||
emit('delete', state.originalImgList);
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
|
||||
}
|
||||
})
|
||||
onNegativeClick: () => {},
|
||||
});
|
||||
}
|
||||
|
||||
//组装完整图片地址
|
||||
function getImgUrl(url: string) {
|
||||
const { imgUrl } = globSetting
|
||||
return (/(^http|https:\/\/)/g).test(url) ? url : `${ imgUrl }${ url }`
|
||||
function getImgUrl(url: string): string {
|
||||
const { imgUrl } = globSetting;
|
||||
return /(^http|https:\/\/)/g.test(url) ? url : `${imgUrl}${url}`;
|
||||
}
|
||||
|
||||
|
||||
function checkFileType(fileType: string) {
|
||||
return componentSetting.upload.fileType.includes(fileType)
|
||||
return componentSetting.upload.fileType.includes(fileType);
|
||||
}
|
||||
|
||||
//上传之前
|
||||
function beforeUpload({ file, fileList }) {
|
||||
function beforeUpload({ file }) {
|
||||
const fileInfo = file.file;
|
||||
const { maxSize, accept, maxNumber } = props;
|
||||
const { maxSize, accept } = props;
|
||||
const acceptRef = (isString(accept) && accept.split(',')) || [];
|
||||
|
||||
// 设置最大值,则判断
|
||||
if (maxSize && fileInfo.size / 1024 / 1024 >= maxSize) {
|
||||
message.error(`上传文件最大值不能超过${ maxSize }M`);
|
||||
message.error(`上传文件最大值不能超过${maxSize}M`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置类型,则判断
|
||||
const fileType = componentSetting.upload.fileType
|
||||
const fileType = componentSetting.upload.fileType;
|
||||
if (acceptRef.length > 0 && !checkFileType(fileInfo.type)) {
|
||||
message.error(`只能上传文件类型为${ fileType.join(',') }`);
|
||||
message.error(`只能上传文件类型为${fileType.join(',')}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
//上传结束
|
||||
function finish({ event: Event }) {
|
||||
const res = eval('(' + Event.target.response + ')');
|
||||
const infoField = componentSetting.upload.apiSetting.infoField
|
||||
const { code } = res
|
||||
const message = (res.msg || res.message) || '上传失败'
|
||||
const result = res[infoField]
|
||||
const infoField = componentSetting.upload.apiSetting.infoField;
|
||||
const { code } = res;
|
||||
const message = res.msg || res.message || '上传失败';
|
||||
const result = res[infoField];
|
||||
//成功
|
||||
if (code === ResultEnum.SUCCESS) {
|
||||
let imgUrl: string = getImgUrl(result.photo)
|
||||
state.imgList.push(imgUrl)
|
||||
state.originalImgList.push(result.photo)
|
||||
emit('uploadChange', state.originalImgList)
|
||||
} else message.error(message)
|
||||
let imgUrl = getImgUrl(result.photo);
|
||||
state.imgList.push(imgUrl);
|
||||
state.originalImgList.push(result.photo);
|
||||
emit('uploadChange', state.originalImgList);
|
||||
} else message.error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -191,15 +191,14 @@ export default defineComponent({
|
||||
preview,
|
||||
remove,
|
||||
beforeUpload,
|
||||
getCSSProperties
|
||||
}
|
||||
}
|
||||
})
|
||||
getCSSProperties,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
.upload {
|
||||
.upload {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -223,9 +222,12 @@ export default defineComponent({
|
||||
|
||||
&:hover {
|
||||
background: 0 0;
|
||||
.upload-card-item-info::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&-info::before {
|
||||
opacity: 1
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,9 +248,9 @@ export default defineComponent({
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0;
|
||||
transition: all .3s;
|
||||
transition: all 0.3s;
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
@@ -267,7 +269,7 @@ export default defineComponent({
|
||||
white-space: nowrap;
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 0;
|
||||
transition: all .3s;
|
||||
transition: all 0.3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -277,16 +279,15 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
color: rgba(255, 255, 255, .85);
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: #fff
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-item-select-picture {
|
||||
@@ -300,14 +301,6 @@ export default defineComponent({
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&-item:hover {
|
||||
background: 0 0;
|
||||
|
||||
.upload-card-item-info::before {
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PropType } from 'vue'
|
||||
import { NUpload } from 'naive-ui'
|
||||
import type { PropType } from 'vue';
|
||||
import { NUpload } from 'naive-ui';
|
||||
|
||||
export const basicProps = {
|
||||
...NUpload.props,
|
||||
@@ -13,22 +13,22 @@ export const basicProps = {
|
||||
},
|
||||
maxSize: {
|
||||
type: Number as PropType<number>,
|
||||
default: 2
|
||||
default: 2,
|
||||
},
|
||||
maxNumber: {
|
||||
type: Number as PropType<number>,
|
||||
default: Infinity
|
||||
default: Infinity,
|
||||
},
|
||||
value: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
width: {
|
||||
type: Number as PropType<number>,
|
||||
default: 104
|
||||
default: 104,
|
||||
},
|
||||
height: {
|
||||
type: Number as PropType<number>,
|
||||
default: 104 //建议不小于这个尺寸 太小页面可能显示有异常
|
||||
default: 104, //建议不小于这个尺寸 太小页面可能显示有异常
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export interface BasicProps<T = any> {
|
||||
title?: string,
|
||||
dataSource: Function,
|
||||
columns: any[],
|
||||
pagination: object,
|
||||
showPagination: boolean
|
||||
export interface BasicProps {
|
||||
title?: string;
|
||||
dataSource: Function;
|
||||
columns: any[];
|
||||
pagination: object;
|
||||
showPagination: boolean;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { ObjectDirective } from 'vue'
|
||||
import { usePermission } from "@/hooks/web/usePermission";
|
||||
import { ObjectDirective } from 'vue';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
|
||||
export const permission: ObjectDirective = {
|
||||
mounted(el: HTMLButtonElement, binding, vnode) {
|
||||
if (binding.value == undefined) return
|
||||
const { action, effect } = binding.value
|
||||
const { hasPermission } = usePermission()
|
||||
mounted(el: HTMLButtonElement, binding) {
|
||||
if (binding.value == undefined) return;
|
||||
const { action, effect } = binding.value;
|
||||
const { hasPermission } = usePermission();
|
||||
if (!hasPermission(action)) {
|
||||
if (effect == 'disabled') {
|
||||
el.disabled = true
|
||||
el.style["disabled"] = 'disabled'
|
||||
el.classList.add("n-button--disabled")
|
||||
el.disabled = true;
|
||||
el.style['disabled'] = 'disabled';
|
||||
el.classList.add('n-button--disabled');
|
||||
} else {
|
||||
el.remove()
|
||||
el.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
// token key
|
||||
export const TOKEN_KEY = 'TOKEN'
|
||||
export const TOKEN_KEY = 'TOKEN';
|
||||
|
||||
// user info key
|
||||
export const USER_INFO_KEY = 'USER__INFO__'
|
||||
export const USER_INFO_KEY = 'USER__INFO__';
|
||||
|
||||
// role info key
|
||||
export const ROLES_KEY = 'ROLES__KEY__'
|
||||
export const ROLES_KEY = 'ROLES__KEY__';
|
||||
|
||||
// project config key
|
||||
export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__'
|
||||
export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__';
|
||||
|
||||
// lock info
|
||||
export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__'
|
||||
export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__';
|
||||
|
||||
// base global local key
|
||||
export const BASE_LOCAL_CACHE_KEY = 'LOCAL__CACHE__KEY__'
|
||||
export const BASE_LOCAL_CACHE_KEY = 'LOCAL__CACHE__KEY__';
|
||||
|
||||
// base global session key
|
||||
export const BASE_SESSION_CACHE_KEY = 'SESSION__CACHE__KEY__'
|
||||
export const BASE_SESSION_CACHE_KEY = 'SESSION__CACHE__KEY__';
|
||||
|
||||
@@ -5,7 +5,7 @@ export enum ResultEnum {
|
||||
SUCCESS = 200,
|
||||
ERROR = -1,
|
||||
TIMEOUT = 10042,
|
||||
TYPE = 'success'
|
||||
TYPE = 'success',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,7 @@ export enum RequestEnum {
|
||||
POST = 'POST',
|
||||
PATCH = 'PATCH',
|
||||
PUT = 'PUT',
|
||||
DELETE = 'DELETE'
|
||||
DELETE = 'DELETE',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,5 +30,5 @@ export enum ContentTypeEnum {
|
||||
// form-data 一般配合qs
|
||||
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||
// form-data 上传
|
||||
FORM_DATA = 'multipart/form-data;charset=UTF-8'
|
||||
FORM_DATA = 'multipart/form-data;charset=UTF-8',
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ export enum RoleEnum {
|
||||
ADMIN = 'admin',
|
||||
|
||||
// 普通用户
|
||||
NORMAL = 'normal'
|
||||
NORMAL = 'normal',
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export function useEventListener({
|
||||
autoRemove = true,
|
||||
isDebounce = true,
|
||||
wait = 80,
|
||||
}: UseEventParams): { removeEvent: RemoveEventFn } {
|
||||
}: UseEventParams): { removeEvent: RemoveEventFn } {
|
||||
/* eslint-disable-next-line */
|
||||
let remove: RemoveEventFn = () => {
|
||||
};
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { useAsync } from './use-async'
|
||||
import { useAsync } from './use-async';
|
||||
|
||||
export { useAsync }
|
||||
export { useAsync };
|
||||
|
||||
@@ -11,7 +11,7 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
|
||||
VITE_GLOB_API_URL_PREFIX,
|
||||
VITE_GLOB_UPLOAD_URL,
|
||||
VITE_GLOB_PROD_MOCK,
|
||||
VITE_GLOB_IMG_URL
|
||||
VITE_GLOB_IMG_URL,
|
||||
} = getAppEnvConfig();
|
||||
|
||||
if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
||||
@@ -28,8 +28,7 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
|
||||
urlPrefix: VITE_GLOB_API_URL_PREFIX,
|
||||
uploadUrl: VITE_GLOB_UPLOAD_URL,
|
||||
prodMock: VITE_GLOB_PROD_MOCK,
|
||||
imgUrl: VITE_GLOB_IMG_URL
|
||||
|
||||
imgUrl: VITE_GLOB_IMG_URL,
|
||||
};
|
||||
return glob as Readonly<GlobConfig>;
|
||||
};
|
||||
|
||||
@@ -13,7 +13,6 @@ export function useDesignSetting() {
|
||||
return {
|
||||
getDarkTheme,
|
||||
getAppTheme,
|
||||
getAppThemeList
|
||||
}
|
||||
|
||||
getAppThemeList,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { computed } from 'vue';
|
||||
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||
|
||||
export function useProjectSetting() {
|
||||
|
||||
const projectStore = useProjectSettingStore();
|
||||
|
||||
const getNavMode = computed(() => projectStore.navMode);
|
||||
@@ -29,6 +28,6 @@ export function useProjectSetting() {
|
||||
getMenuSetting,
|
||||
getCrumbsSetting,
|
||||
getPermissionMode,
|
||||
getShowFooter
|
||||
}
|
||||
getShowFooter,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Ref, isReactive, isRef } from 'vue'
|
||||
import { isReactive, isRef } from 'vue';
|
||||
|
||||
function setLoading(loading, val) {
|
||||
if (loading != undefined && isRef(loading)) {
|
||||
loading.value = val
|
||||
loading.value = val;
|
||||
} else if (loading != undefined && isReactive(loading)) {
|
||||
loading.loading = val
|
||||
loading.loading = val;
|
||||
}
|
||||
}
|
||||
|
||||
export const useAsync = async (func: Promise<any>, loading: any): Promise<any> => {
|
||||
setLoading(loading, true)
|
||||
setLoading(loading, true);
|
||||
|
||||
return await func.finally(() => setLoading(loading, false))
|
||||
}
|
||||
return await func.finally(() => setLoading(loading, false));
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { computed, onMounted, reactive, toRefs } from 'vue'
|
||||
import { computed, onMounted, reactive, toRefs } from 'vue';
|
||||
|
||||
interface Battery {
|
||||
charging: boolean // 当前电池是否正在充电
|
||||
chargingTime: number // 距离充电完毕还需多少秒,如果为0则充电完毕
|
||||
dischargingTime: number // 代表距离电池耗电至空且挂起需要多少秒
|
||||
level: number // 代表电量的放大等级,这个值在 0.0 至 1.0 之间
|
||||
[key: string]: any
|
||||
charging: boolean; // 当前电池是否正在充电
|
||||
chargingTime: number; // 距离充电完毕还需多少秒,如果为0则充电完毕
|
||||
dischargingTime: number; // 代表距离电池耗电至空且挂起需要多少秒
|
||||
level: number; // 代表电量的放大等级,这个值在 0.0 至 1.0 之间
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export const useBattery = () => {
|
||||
@@ -14,56 +14,56 @@ export const useBattery = () => {
|
||||
charging: false,
|
||||
chargingTime: 0,
|
||||
dischargingTime: 0,
|
||||
level: 100
|
||||
}
|
||||
})
|
||||
level: 100,
|
||||
},
|
||||
});
|
||||
|
||||
// 更新电池使用状态
|
||||
const updateBattery = (target) => {
|
||||
for (const key in state.battery) {
|
||||
state.battery[key] = target[key]
|
||||
}
|
||||
state.battery.level = state.battery.level * 100
|
||||
state.battery[key] = target[key];
|
||||
}
|
||||
state.battery.level = state.battery.level * 100;
|
||||
};
|
||||
|
||||
// 计算电池剩余可用时间
|
||||
const calcDischargingTime = computed(() => {
|
||||
const hour = state.battery.dischargingTime / 3600
|
||||
const minute = (state.battery.dischargingTime / 60) % 60
|
||||
return `${ ~~hour }小时${ ~~minute }分钟`
|
||||
})
|
||||
const hour = state.battery.dischargingTime / 3600;
|
||||
const minute = (state.battery.dischargingTime / 60) % 60;
|
||||
return `${~~hour}小时${~~minute}分钟`;
|
||||
});
|
||||
|
||||
// 电池状态
|
||||
const batteryStatus = computed(() => {
|
||||
if (state.battery.charging && state.battery.level >= 100) {
|
||||
return '已充满'
|
||||
return '已充满';
|
||||
} else if (state.battery.charging) {
|
||||
return '充电中'
|
||||
return '充电中';
|
||||
} else {
|
||||
return '已断开电源'
|
||||
return '已断开电源';
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const BatteryManager: Battery = await (window.navigator as any).getBattery()
|
||||
updateBattery(BatteryManager)
|
||||
const BatteryManager: Battery = await (window.navigator as any).getBattery();
|
||||
updateBattery(BatteryManager);
|
||||
|
||||
// 电池充电状态更新时被调用
|
||||
BatteryManager.onchargingchange = ({ target }) => {
|
||||
updateBattery(target)
|
||||
}
|
||||
updateBattery(target);
|
||||
};
|
||||
// 电池充电时间更新时被调用
|
||||
BatteryManager.onchargingtimechange = ({ target }) => {
|
||||
updateBattery(target)
|
||||
}
|
||||
updateBattery(target);
|
||||
};
|
||||
// 电池断开充电时间更新时被调用
|
||||
BatteryManager.ondischargingtimechange = ({ target }) => {
|
||||
updateBattery(target)
|
||||
}
|
||||
updateBattery(target);
|
||||
};
|
||||
// 电池电量更新时被调用
|
||||
BatteryManager.onlevelchange = ({ target }) => {
|
||||
updateBattery(target)
|
||||
}
|
||||
updateBattery(target);
|
||||
};
|
||||
|
||||
// new Intl.DateTimeFormat('zh', {
|
||||
// year: 'numeric',
|
||||
@@ -74,11 +74,11 @@ export const useBattery = () => {
|
||||
// second: '2-digit',
|
||||
// hour12: false
|
||||
// }).format(new Date())
|
||||
})
|
||||
});
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
batteryStatus,
|
||||
calcDischargingTime
|
||||
}
|
||||
}
|
||||
calcDischargingTime,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { debounce } from 'lodash'
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
/**
|
||||
* description: 获取页面宽度
|
||||
*/
|
||||
|
||||
export function useDomWidth() {
|
||||
const domWidth = ref(window.innerWidth)
|
||||
const domWidth = ref(window.innerWidth);
|
||||
|
||||
function resize() {
|
||||
domWidth.value = document.body.clientWidth
|
||||
domWidth.value = document.body.clientWidth;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', debounce(resize, 80))
|
||||
})
|
||||
window.addEventListener('resize', debounce(resize, 80));
|
||||
});
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', resize)
|
||||
})
|
||||
window.removeEventListener('resize', resize);
|
||||
});
|
||||
|
||||
return domWidth
|
||||
return domWidth;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
/**
|
||||
* @description 用户网络是否可用
|
||||
* */
|
||||
export function useOnline() {
|
||||
const online = ref(true)
|
||||
const online = ref(true);
|
||||
|
||||
const showStatus = (val) => {
|
||||
online.value = typeof val == 'boolean' ? val : val.target.online
|
||||
}
|
||||
online.value = typeof val == 'boolean' ? val : val.target.online;
|
||||
};
|
||||
|
||||
// 在页面加载后,设置正确的网络状态
|
||||
navigator.onLine ? showStatus(true) : showStatus(false)
|
||||
navigator.onLine ? showStatus(true) : showStatus(false);
|
||||
|
||||
onMounted(() => {
|
||||
// 开始监听网络状态的变化
|
||||
window.addEventListener('online', showStatus)
|
||||
window.addEventListener('online', showStatus);
|
||||
|
||||
window.addEventListener('offline', showStatus)
|
||||
})
|
||||
window.addEventListener('offline', showStatus);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
// 移除监听网络状态的变化
|
||||
window.removeEventListener('online', showStatus)
|
||||
window.removeEventListener('online', showStatus);
|
||||
|
||||
window.removeEventListener('offline', showStatus)
|
||||
})
|
||||
window.removeEventListener('offline', showStatus);
|
||||
});
|
||||
|
||||
return { online }
|
||||
return { online };
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
/**
|
||||
* @description 获取本地时间
|
||||
*/
|
||||
export function useTime() {
|
||||
let timer // 定时器
|
||||
const year = ref(0) // 年份
|
||||
const month = ref(0) // 月份
|
||||
const week = ref('') // 星期几
|
||||
const day = ref(0) // 天数
|
||||
const hour = ref<number | string>(0) // 小时
|
||||
const minute = ref<number | string>(0) // 分钟
|
||||
const second = ref(0) // 秒
|
||||
let timer; // 定时器
|
||||
const year = ref(0); // 年份
|
||||
const month = ref(0); // 月份
|
||||
const week = ref(''); // 星期几
|
||||
const day = ref(0); // 天数
|
||||
const hour = ref<number | string>(0); // 小时
|
||||
const minute = ref<number | string>(0); // 分钟
|
||||
const second = ref(0); // 秒
|
||||
|
||||
// 更新时间
|
||||
const updateTime = () => {
|
||||
const date = new Date()
|
||||
year.value = date.getFullYear()
|
||||
month.value = date.getMonth() + 1
|
||||
week.value = '日一二三四五六'.charAt(date.getDay())
|
||||
day.value = date.getDate()
|
||||
const date = new Date();
|
||||
year.value = date.getFullYear();
|
||||
month.value = date.getMonth() + 1;
|
||||
week.value = '日一二三四五六'.charAt(date.getDay());
|
||||
day.value = date.getDate();
|
||||
hour.value =
|
||||
(date.getHours() + '')?.padStart(2, '0') ||
|
||||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getHours())
|
||||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getHours());
|
||||
minute.value =
|
||||
(date.getMinutes() + '')?.padStart(2, '0') ||
|
||||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getMinutes())
|
||||
second.value = date.getSeconds()
|
||||
}
|
||||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getMinutes());
|
||||
second.value = date.getSeconds();
|
||||
};
|
||||
|
||||
// 原生时间格式化
|
||||
// new Intl.DateTimeFormat('zh', {
|
||||
@@ -40,16 +40,16 @@ export function useTime() {
|
||||
// hour12: false
|
||||
// }).format(new Date())
|
||||
|
||||
updateTime()
|
||||
updateTime();
|
||||
|
||||
onMounted(() => {
|
||||
clearInterval(timer)
|
||||
timer = setInterval(() => updateTime(), 1000)
|
||||
})
|
||||
clearInterval(timer);
|
||||
timer = setInterval(() => updateTime(), 1000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(timer)
|
||||
})
|
||||
clearInterval(timer);
|
||||
});
|
||||
|
||||
return { month, day, hour, minute, second, week }
|
||||
return { month, day, hour, minute, second, week };
|
||||
}
|
||||
|
||||
@@ -12,18 +12,16 @@ import echarts from '@/utils/lib/echarts';
|
||||
|
||||
// import { useRootSetting } from '@/hooks/setting/useRootSetting';
|
||||
|
||||
|
||||
export function useECharts(
|
||||
elRef: Ref<HTMLDivElement>,
|
||||
theme: 'light' | 'dark' | 'default' = 'light'
|
||||
) {
|
||||
// const { getDarkMode } = useRootSetting();
|
||||
const getDarkMode = 'light'
|
||||
const getDarkMode = 'light';
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
let resizeFn: Fn = resize;
|
||||
const cacheOptions = ref<EChartsOption>({});
|
||||
let removeResizeFn: Fn = () => {
|
||||
};
|
||||
let removeResizeFn: Fn = () => {};
|
||||
|
||||
resizeFn = useDebounceFn(resize, 200);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
export function usePermission() {
|
||||
const userStore = useUserStore();
|
||||
@@ -8,10 +8,10 @@ export function usePermission() {
|
||||
* @param accesses
|
||||
*/
|
||||
function _someRoles(accesses: string[]) {
|
||||
return userStore.getRoles.some(item => {
|
||||
const { value }: any = item
|
||||
return accesses.includes(value)
|
||||
})
|
||||
return userStore.getRoles.some((item) => {
|
||||
const { value }: any = item;
|
||||
return accesses.includes(value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -19,8 +19,8 @@ export function usePermission() {
|
||||
* 可用于 v-if 显示逻辑
|
||||
* */
|
||||
function hasPermission(accesses: string[]): boolean {
|
||||
if (!accesses ||!accesses.length) return true
|
||||
return _someRoles(accesses)
|
||||
if (!accesses || !accesses.length) return true;
|
||||
return _someRoles(accesses);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,11 +28,11 @@ export function usePermission() {
|
||||
* @param accesses
|
||||
*/
|
||||
function hasEveryPermission(accesses: string[]): boolean {
|
||||
const rolesList = userStore.getRoles
|
||||
const rolesList = userStore.getRoles;
|
||||
if (Array.isArray(accesses)) {
|
||||
return accesses.every((access) => !!rolesList[access])
|
||||
return accesses.every((access) => !!rolesList[access]);
|
||||
}
|
||||
throw new Error(`[hasEveryPermission]: ${ accesses } should be a array !`)
|
||||
throw new Error(`[hasEveryPermission]: ${accesses} should be a array !`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,11 +41,11 @@ export function usePermission() {
|
||||
* @param accessMap
|
||||
*/
|
||||
function hasSomePermission(accesses: string[]): boolean {
|
||||
const rolesList = userStore.getRoles
|
||||
const rolesList = userStore.getRoles;
|
||||
if (Array.isArray(accesses)) {
|
||||
return accesses.some((access) => !!rolesList[access])
|
||||
return accesses.some((access) => !!rolesList[access]);
|
||||
}
|
||||
throw new Error(`[hasSomePermission]: ${ accesses } should be a array !`)
|
||||
throw new Error(`[hasSomePermission]: ${accesses} should be a array !`);
|
||||
}
|
||||
|
||||
return { hasPermission, hasEveryPermission, hasSomePermission };
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import PageFooter from './index.vue'
|
||||
import PageFooter from './index.vue';
|
||||
|
||||
export { PageFooter }
|
||||
export { PageFooter };
|
||||
|
||||
@@ -1,39 +1,28 @@
|
||||
<template>
|
||||
<div class="page-footer">
|
||||
<div class="page-footer-link">
|
||||
<a href="https://github.com/jekip/naive-ui-admin" target="_blank">
|
||||
官网
|
||||
</a>
|
||||
<a href="https://github.com/jekip/naive-ui-admin" target="_blank">
|
||||
社区
|
||||
</a>
|
||||
<a href="https://github.com/jekip/naive-ui-admin/issues" target="_blank">
|
||||
交流
|
||||
</a>
|
||||
<a href="https://github.com/jekip/naive-ui-admin" target="_blank"> 官网 </a>
|
||||
<a href="https://github.com/jekip/naive-ui-admin" target="_blank"> 社区 </a>
|
||||
<a href="https://github.com/jekip/naive-ui-admin/issues" target="_blank"> 交流 </a>
|
||||
</div>
|
||||
<div class="copyright">
|
||||
naive-ui-admin 1.4 · Made by Ah jung
|
||||
<div class="copyright"> naive-ui-admin 1.4 · Made by Ah jung </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GithubOutlined, CopyrightOutlined } from '@vicons/antd'
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'PageFooter',
|
||||
components: { GithubOutlined, CopyrightOutlined },
|
||||
components: {},
|
||||
props: {
|
||||
collapsed: {
|
||||
type: Boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-footer {
|
||||
.page-footer {
|
||||
margin: 48px 0 24px 0;
|
||||
padding: 0 16px;
|
||||
text-align: center;
|
||||
@@ -41,8 +30,8 @@ export default {
|
||||
a {
|
||||
font-size: 14px;
|
||||
color: #808695;
|
||||
-webkit-transition: all .2s ease-in-out;
|
||||
transition: all .2s ease-in-out;
|
||||
-webkit-transition: all 0.2s ease-in-out;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: #515a6e;
|
||||
@@ -63,5 +52,5 @@ export default {
|
||||
color: #808695;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,24 +7,24 @@
|
||||
<div class="drawer-setting-item justify-center dark-switch">
|
||||
<n-tooltip placement="bottom">
|
||||
<template #trigger>
|
||||
<n-switch v-model:value="designStore.darkTheme"/>
|
||||
<n-switch v-model:value="designStore.darkTheme" />
|
||||
</template>
|
||||
<span>深色主题</span>
|
||||
</n-tooltip>
|
||||
|
||||
</div>
|
||||
|
||||
<n-divider title-placement="center">系统主题</n-divider>
|
||||
|
||||
<div class="drawer-setting-item align-items-top">
|
||||
<span class="theme-item"
|
||||
<span
|
||||
class="theme-item"
|
||||
v-for="(item, index) in appThemeList"
|
||||
:key="index"
|
||||
:style="{'background-color':item}"
|
||||
:style="{ 'background-color': item }"
|
||||
@click="togTheme(item)"
|
||||
>
|
||||
<n-icon size="12" v-if="item === designStore.appTheme">
|
||||
<CheckOutlined/>
|
||||
<CheckOutlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
</div>
|
||||
@@ -35,46 +35,45 @@
|
||||
<div class="drawer-setting-item-style align-items-top">
|
||||
<n-tooltip placement="top">
|
||||
<template #trigger>
|
||||
<img src="~@/assets/images/nav-theme-dark.svg" @click="togNavMode('vertical')"/>
|
||||
<img src="~@/assets/images/nav-theme-dark.svg" @click="togNavMode('vertical')" />
|
||||
</template>
|
||||
<span>左侧菜单模式</span>
|
||||
</n-tooltip>
|
||||
<n-badge dot color="#19be6b" v-show="settingStore.navMode === 'vertical'"/>
|
||||
<n-badge dot color="#19be6b" v-show="settingStore.navMode === 'vertical'" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-setting-item-style">
|
||||
<n-tooltip placement="top">
|
||||
<template #trigger>
|
||||
<img src="~@/assets/images/nav-horizontal.svg" @click="togNavMode('horizontal')"/>
|
||||
<img src="~@/assets/images/nav-horizontal.svg" @click="togNavMode('horizontal')" />
|
||||
</template>
|
||||
<span>顶部菜单模式</span>
|
||||
</n-tooltip>
|
||||
<n-badge dot color="#19be6b" v-show="settingStore.navMode === 'horizontal'"/>
|
||||
<n-badge dot color="#19be6b" v-show="settingStore.navMode === 'horizontal'" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<n-divider title-placement="center">导航栏风格</n-divider>
|
||||
|
||||
<div class="drawer-setting-item align-items-top">
|
||||
<div class="drawer-setting-item-style align-items-top">
|
||||
<n-tooltip placement="top">
|
||||
<template #trigger>
|
||||
<img src="~@/assets/images/nav-theme-dark.svg" @click="togNavTheme('dark')"/>
|
||||
<img src="~@/assets/images/nav-theme-dark.svg" @click="togNavTheme('dark')" />
|
||||
</template>
|
||||
<span>暗色侧边栏</span>
|
||||
</n-tooltip>
|
||||
<n-badge dot color="#19be6b" v-if="settingStore.navTheme === 'dark'"/>
|
||||
<n-badge dot color="#19be6b" v-if="settingStore.navTheme === 'dark'" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-setting-item-style">
|
||||
<n-tooltip placement="top">
|
||||
<template #trigger>
|
||||
<img src="~@/assets/images/nav-theme-light.svg" @click="togNavTheme('light')"/>
|
||||
<img src="~@/assets/images/nav-theme-light.svg" @click="togNavTheme('light')" />
|
||||
</template>
|
||||
<span>白色侧边栏</span>
|
||||
</n-tooltip>
|
||||
<n-badge dot color="#19be6b" v-if="settingStore.navTheme === 'light'"/>
|
||||
<n-badge dot color="#19be6b" v-if="settingStore.navTheme === 'light'" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -82,22 +81,23 @@
|
||||
<div class="drawer-setting-item-style">
|
||||
<n-tooltip placement="top">
|
||||
<template #trigger>
|
||||
<img src="~@/assets/images/header-theme-dark.svg" @click="togNavTheme('header-dark')"/>
|
||||
<img
|
||||
src="~@/assets/images/header-theme-dark.svg"
|
||||
@click="togNavTheme('header-dark')"
|
||||
/>
|
||||
</template>
|
||||
<span>暗色顶栏</span>
|
||||
</n-tooltip>
|
||||
<n-badge dot color="#19be6b" v-if="settingStore.navTheme === 'header-dark'"/>
|
||||
<n-badge dot color="#19be6b" v-if="settingStore.navTheme === 'header-dark'" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-divider title-placement="center">界面功能</n-divider>
|
||||
|
||||
<div class="drawer-setting-item">
|
||||
<div class="drawer-setting-item-title">
|
||||
固定顶栏
|
||||
</div>
|
||||
<div class="drawer-setting-item-title"> 固定顶栏 </div>
|
||||
<div class="drawer-setting-item-action">
|
||||
<n-switch v-model:value="settingStore.headerSetting.fixed"/>
|
||||
<n-switch v-model:value="settingStore.headerSetting.fixed" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -111,138 +111,124 @@
|
||||
<!-- </div>-->
|
||||
|
||||
<div class="drawer-setting-item">
|
||||
<div class="drawer-setting-item-title">
|
||||
固定多页签
|
||||
</div>
|
||||
<div class="drawer-setting-item-title"> 固定多页签 </div>
|
||||
<div class="drawer-setting-item-action">
|
||||
<n-switch v-model:value="settingStore.multiTabsSetting.fixed"/>
|
||||
<n-switch v-model:value="settingStore.multiTabsSetting.fixed" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-divider title-placement="center">界面显示</n-divider>
|
||||
|
||||
<div class="drawer-setting-item">
|
||||
<div class="drawer-setting-item-title">
|
||||
显示重载页面按钮
|
||||
</div>
|
||||
<div class="drawer-setting-item-title"> 显示重载页面按钮 </div>
|
||||
<div class="drawer-setting-item-action">
|
||||
<n-switch v-model:value="settingStore.headerSetting.isReload"/>
|
||||
<n-switch v-model:value="settingStore.headerSetting.isReload" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-setting-item">
|
||||
<div class="drawer-setting-item-title">
|
||||
显示面包屑导航
|
||||
</div>
|
||||
<div class="drawer-setting-item-title"> 显示面包屑导航 </div>
|
||||
<div class="drawer-setting-item-action">
|
||||
<n-switch v-model:value="settingStore.crumbsSetting.show"/>
|
||||
<n-switch v-model:value="settingStore.crumbsSetting.show" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-setting-item">
|
||||
<div class="drawer-setting-item-title">
|
||||
显示面包屑显示图标
|
||||
</div>
|
||||
<div class="drawer-setting-item-title"> 显示面包屑显示图标 </div>
|
||||
<div class="drawer-setting-item-action">
|
||||
<n-switch v-model:value="settingStore.crumbsSetting.showIcon"/>
|
||||
<n-switch v-model:value="settingStore.crumbsSetting.showIcon" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-setting-item">
|
||||
<div class="drawer-setting-item-title">
|
||||
显示多页签
|
||||
</div>
|
||||
<div class="drawer-setting-item-title"> 显示多页签 </div>
|
||||
<div class="drawer-setting-item-action">
|
||||
<n-switch v-model:value="settingStore.multiTabsSetting.show"/>
|
||||
<n-switch v-model:value="settingStore.multiTabsSetting.show" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-setting-item">
|
||||
<div class="drawer-setting-item-title">
|
||||
显示页脚
|
||||
</div>
|
||||
<div class="drawer-setting-item-title"> 显示页脚 </div>
|
||||
<div class="drawer-setting-item-action">
|
||||
<n-switch v-model:value="settingStore.showFooter"/>
|
||||
<n-switch v-model:value="settingStore.showFooter" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="drawer-setting-item">
|
||||
<n-alert type="warning" :showIcon="false">
|
||||
<p>{{ alertText }}</p>
|
||||
</n-alert>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs, watch, createVNode, computed, unref } from 'vue'
|
||||
import { useProjectSettingStore } from "@/store/modules/projectSetting";
|
||||
import { useDesignSettingStore } from "@/store/modules/designSetting";
|
||||
import { CheckOutlined } from '@vicons/antd'
|
||||
import { darkTheme } from 'naive-ui'
|
||||
import { defineComponent, reactive, toRefs, watch } from 'vue';
|
||||
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
||||
import { CheckOutlined } from '@vicons/antd';
|
||||
import { darkTheme } from 'naive-ui';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'ProjectSetting',
|
||||
components: { CheckOutlined },
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '项目配置'
|
||||
default: '项目配置',
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 280
|
||||
default: 280,
|
||||
},
|
||||
},
|
||||
components: { CheckOutlined },
|
||||
setup(props, { emit }) {
|
||||
const settingStore = useProjectSettingStore()
|
||||
const designStore = useDesignSettingStore()
|
||||
const { width, title } = props
|
||||
setup(props) {
|
||||
const settingStore = useProjectSettingStore();
|
||||
const designStore = useDesignSettingStore();
|
||||
const state = reactive({
|
||||
width,
|
||||
title,
|
||||
width: props.width,
|
||||
title: props.title,
|
||||
isDrawer: false,
|
||||
placement: "right",
|
||||
alertText: '该功能主要实时预览各种布局效果,更多完整配置在 projectSetting.ts 中设置,建议在生产环境关闭该布局预览功能。',
|
||||
appThemeList: designStore.appThemeList
|
||||
})
|
||||
placement: 'right',
|
||||
alertText:
|
||||
'该功能主要实时预览各种布局效果,更多完整配置在 projectSetting.ts 中设置,建议在生产环境关闭该布局预览功能。',
|
||||
appThemeList: designStore.appThemeList,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => designStore.darkTheme,
|
||||
(to) => {
|
||||
settingStore.navTheme = to ? 'header-dark' : 'dark'
|
||||
settingStore.navTheme = to ? 'header-dark' : 'dark';
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
function openDrawer(isDrawer) {
|
||||
state.isDrawer = true
|
||||
function openDrawer() {
|
||||
state.isDrawer = true;
|
||||
}
|
||||
|
||||
function closeDrawer() {
|
||||
state.isDrawer = false
|
||||
state.isDrawer = false;
|
||||
}
|
||||
|
||||
function togNavTheme(theme) {
|
||||
settingStore.navTheme = theme
|
||||
settingStore.navTheme = theme;
|
||||
if (settingStore.navMode === 'horizontal' && theme === 'light') {
|
||||
designStore.navTheme = 'dark'
|
||||
settingStore.navTheme = 'dark';
|
||||
}
|
||||
}
|
||||
|
||||
function togTheme(color) {
|
||||
designStore.appTheme = color
|
||||
designStore.appTheme = color;
|
||||
}
|
||||
|
||||
function togNavMode(mode) {
|
||||
settingStore.navMode = mode
|
||||
settingStore.navMode = mode;
|
||||
if (mode === 'horizontal') {
|
||||
settingStore.setNavTheme('light')
|
||||
settingStore.setNavTheme('light');
|
||||
} else {
|
||||
settingStore.setNavTheme('dark')
|
||||
settingStore.setNavTheme('dark');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,13 +242,13 @@ export default defineComponent({
|
||||
darkTheme,
|
||||
openDrawer,
|
||||
closeDrawer,
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.drawer {
|
||||
.drawer {
|
||||
.n-divider:not(.n-divider--vertical) {
|
||||
margin: 10px 0;
|
||||
}
|
||||
@@ -301,7 +287,7 @@ export default defineComponent({
|
||||
text-align: center;
|
||||
|
||||
.n-icon {
|
||||
color: #fff
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,5 +306,5 @@ export default defineComponent({
|
||||
background-color: #000e1c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
ReloadOutlined,
|
||||
LogoutOutlined,
|
||||
UserOutlined,
|
||||
CheckOutlined
|
||||
} from '@vicons/antd'
|
||||
CheckOutlined,
|
||||
} from '@vicons/antd';
|
||||
|
||||
export default {
|
||||
SettingOutlined,
|
||||
@@ -27,5 +27,5 @@ export default {
|
||||
ReloadOutlined,
|
||||
LogoutOutlined,
|
||||
UserOutlined,
|
||||
CheckOutlined
|
||||
}
|
||||
CheckOutlined,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import PageHeader from './index.vue'
|
||||
import PageHeader from './index.vue';
|
||||
|
||||
export { PageHeader }
|
||||
export { PageHeader };
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
<template>
|
||||
<div class="layout-header" :class="{'layout-header-light':!(navTheme == 'header-dark')}">
|
||||
<div class="layout-header" :class="{ 'layout-header-light': !(navTheme == 'header-dark') }">
|
||||
<!--顶部菜单-->
|
||||
<div class="layout-header-left" v-if="navMode==='horizontal'">
|
||||
<AsideMenu v-model:collapsed="collapsed" mode="horizontal" class="n-menu-horizontal-light"/>
|
||||
<div class="layout-header-left" v-if="navMode === 'horizontal'">
|
||||
<AsideMenu v-model:collapsed="collapsed" mode="horizontal" class="n-menu-horizontal-light" />
|
||||
</div>
|
||||
<!--左侧菜单-->
|
||||
<div class="layout-header-left" v-else>
|
||||
<!-- 菜单收起 -->
|
||||
<div class="ml-1 layout-header-trigger layout-header-trigger-min"
|
||||
@click="() => $emit('update:collapsed', !collapsed)">
|
||||
<div
|
||||
class="ml-1 layout-header-trigger layout-header-trigger-min"
|
||||
@click="() => $emit('update:collapsed', !collapsed)"
|
||||
>
|
||||
<n-icon size="18" v-if="collapsed">
|
||||
<MenuUnfoldOutlined/>
|
||||
<MenuUnfoldOutlined />
|
||||
</n-icon>
|
||||
<n-icon size="18" v-else>
|
||||
<MenuFoldOutlined/>
|
||||
<MenuFoldOutlined />
|
||||
</n-icon>
|
||||
</div>
|
||||
<!-- 刷新 -->
|
||||
<div class="mr-1 layout-header-trigger layout-header-trigger-min" v-if="headerSetting.isReload"
|
||||
@click="reloadPage">
|
||||
<div
|
||||
class="mr-1 layout-header-trigger layout-header-trigger-min"
|
||||
v-if="headerSetting.isReload"
|
||||
@click="reloadPage"
|
||||
>
|
||||
<n-icon size="18">
|
||||
<ReloadOutlined/>
|
||||
<ReloadOutlined />
|
||||
</n-icon>
|
||||
</div>
|
||||
<!-- 面包屑 -->
|
||||
@@ -33,12 +38,18 @@
|
||||
@select="dropdownSelect"
|
||||
>
|
||||
<span class="link-text">
|
||||
<component v-if="crumbsSetting.showIcon && routeItem.meta.icon" :is="routeItem.meta.icon"></component>
|
||||
<component
|
||||
v-if="crumbsSetting.showIcon && routeItem.meta.icon"
|
||||
:is="routeItem.meta.icon"
|
||||
/>
|
||||
{{ routeItem.meta.title }}
|
||||
</span>
|
||||
</n-dropdown>
|
||||
<span class="link-text" v-else>
|
||||
<component v-if="crumbsSetting.showIcon && routeItem.meta.icon" :is="routeItem.meta.icon"></component>
|
||||
<component
|
||||
v-if="crumbsSetting.showIcon && routeItem.meta.icon"
|
||||
:is="routeItem.meta.icon"
|
||||
/>
|
||||
{{ routeItem.meta.title }}
|
||||
</span>
|
||||
</n-breadcrumb-item>
|
||||
@@ -46,11 +57,15 @@
|
||||
</n-breadcrumb>
|
||||
</div>
|
||||
<div class="layout-header-right">
|
||||
<div class="layout-header-trigger layout-header-trigger-min" v-for="item in iconList" :key="item.icon.name">
|
||||
<div
|
||||
class="layout-header-trigger layout-header-trigger-min"
|
||||
v-for="item in iconList"
|
||||
:key="item.icon.name"
|
||||
>
|
||||
<n-tooltip placement="bottom">
|
||||
<template #trigger>
|
||||
<n-icon size="18">
|
||||
<component :is="item.icon" v-on="item.eventObject || {}"/>
|
||||
<component :is="item.icon" v-on="item.eventObject || {}" />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>{{ item.tips }}</span>
|
||||
@@ -61,7 +76,7 @@
|
||||
<n-tooltip placement="bottom">
|
||||
<template #trigger>
|
||||
<n-icon size="18">
|
||||
<component :is="fullscreenIcon" @click="toggleFullScreen"/>
|
||||
<component :is="fullscreenIcon" @click="toggleFullScreen" />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>全屏</span>
|
||||
@@ -74,7 +89,7 @@
|
||||
<n-avatar>
|
||||
{{ username }}
|
||||
<template #icon>
|
||||
<UserOutlined/>
|
||||
<UserOutlined />
|
||||
</template>
|
||||
</n-avatar>
|
||||
</div>
|
||||
@@ -85,7 +100,7 @@
|
||||
<n-tooltip placement="bottom-end">
|
||||
<template #trigger>
|
||||
<n-icon size="18" style="font-weight: bold">
|
||||
<SettingOutlined/>
|
||||
<SettingOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>项目配置</span>
|
||||
@@ -94,46 +109,40 @@
|
||||
</div>
|
||||
</div>
|
||||
<!--项目配置-->
|
||||
<ProjectSetting ref="drawerSetting"/>
|
||||
<ProjectSetting ref="drawerSetting" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs, ref, computed, unref } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import components from './components'
|
||||
import { NDialogProvider, useDialog, useMessage, useNotification } from 'naive-ui'
|
||||
import { TABS_ROUTES } from '@/store/mutation-types'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useLockscreenStore } from '@/store/modules/lockscreen'
|
||||
import ProjectSetting from './ProjectSetting.vue'
|
||||
import { AsideMenu } from '@/layout/components/Menu'
|
||||
import { useProjectSetting } from "@/hooks/setting/useProjectSetting";
|
||||
import { defineComponent, reactive, toRefs, ref, computed, unref } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import components from './components';
|
||||
import { NDialogProvider, useDialog, useMessage } from 'naive-ui';
|
||||
import { TABS_ROUTES } from '@/store/mutation-types';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useLockscreenStore } from '@/store/modules/lockscreen';
|
||||
import ProjectSetting from './ProjectSetting.vue';
|
||||
import { AsideMenu } from '@/layout/components/Menu';
|
||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'PageHeader',
|
||||
components: { ...components, NDialogProvider, ProjectSetting, AsideMenu },
|
||||
props: {
|
||||
collapsed: {
|
||||
type: Boolean
|
||||
}
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const userStore = useUserStore()
|
||||
const useLockscreen = useLockscreenStore()
|
||||
const message = useMessage()
|
||||
const notification = useNotification()
|
||||
const dialog = useDialog()
|
||||
const {
|
||||
getNavMode,
|
||||
getNavTheme,
|
||||
getHeaderSetting,
|
||||
getMenuSetting,
|
||||
getCrumbsSetting
|
||||
} = useProjectSetting()
|
||||
const userStore = useUserStore();
|
||||
const useLockscreen = useLockscreenStore();
|
||||
const message = useMessage();
|
||||
const dialog = useDialog();
|
||||
const { getNavMode, getNavTheme, getHeaderSetting, getMenuSetting, getCrumbsSetting } =
|
||||
useProjectSetting();
|
||||
|
||||
const { username } = userStore?.info || {}
|
||||
const { username } = userStore?.info || {};
|
||||
|
||||
const drawerSetting = ref()
|
||||
const drawerSetting = ref();
|
||||
|
||||
const state = reactive({
|
||||
username: username || '',
|
||||
@@ -142,51 +151,51 @@ export default defineComponent({
|
||||
navTheme: getNavTheme,
|
||||
headerSetting: getHeaderSetting,
|
||||
crumbsSetting: getCrumbsSetting,
|
||||
})
|
||||
});
|
||||
|
||||
const getChangeStyle = computed(() => {
|
||||
const { collapsed } = props
|
||||
const { minMenuWidth, menuWidth }: any = unref(getMenuSetting)
|
||||
const { collapsed } = props;
|
||||
const { minMenuWidth, menuWidth }: any = unref(getMenuSetting);
|
||||
return {
|
||||
'left': collapsed ? `${ minMenuWidth }px` : `${ menuWidth }px`,
|
||||
'width': `calc(100% - ${ collapsed ? `${ minMenuWidth }px` : `${ menuWidth }px` })`
|
||||
}
|
||||
})
|
||||
left: collapsed ? `${minMenuWidth}px` : `${menuWidth}px`,
|
||||
width: `calc(100% - ${collapsed ? `${minMenuWidth}px` : `${menuWidth}px`})`,
|
||||
};
|
||||
});
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const generator: any = (routerMap, parent) => {
|
||||
return routerMap.map((item, key) => {
|
||||
const generator: any = (routerMap) => {
|
||||
return routerMap.map((item) => {
|
||||
const currentMenu = {
|
||||
...item,
|
||||
label: item.meta.title,
|
||||
key: item.name,
|
||||
disabled: item.path === '/',
|
||||
}
|
||||
};
|
||||
// 是否有子菜单,并递归处理
|
||||
if (item.children && item.children.length > 0) {
|
||||
// Recursion
|
||||
currentMenu.children = generator(item.children, currentMenu)
|
||||
}
|
||||
return currentMenu
|
||||
})
|
||||
currentMenu.children = generator(item.children, currentMenu);
|
||||
}
|
||||
return currentMenu;
|
||||
});
|
||||
};
|
||||
|
||||
const breadcrumbList = computed(() => {
|
||||
return generator(route.matched)
|
||||
})
|
||||
return generator(route.matched);
|
||||
});
|
||||
|
||||
const dropdownSelect = (key) => {
|
||||
router.push({ name: key })
|
||||
}
|
||||
router.push({ name: key });
|
||||
};
|
||||
|
||||
// 刷新页面
|
||||
const reloadPage = () => {
|
||||
router.push({
|
||||
path: '/redirect' + unref(route).fullPath
|
||||
})
|
||||
}
|
||||
path: '/redirect' + unref(route).fullPath,
|
||||
});
|
||||
};
|
||||
|
||||
// 退出登录
|
||||
const doLogout = () => {
|
||||
@@ -196,92 +205,90 @@ export default defineComponent({
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
userStore.logout().then((res) => {
|
||||
message.success('成功退出登录')
|
||||
userStore.logout().then(() => {
|
||||
message.success('成功退出登录');
|
||||
// 移除标签页
|
||||
localStorage.removeItem(TABS_ROUTES)
|
||||
localStorage.removeItem(TABS_ROUTES);
|
||||
router
|
||||
.replace({
|
||||
name: 'Login',
|
||||
query: {
|
||||
redirect: route.fullPath
|
||||
}
|
||||
})
|
||||
.finally(() => location.reload())
|
||||
})
|
||||
redirect: route.fullPath,
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
.finally(() => location.reload());
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {},
|
||||
});
|
||||
};
|
||||
|
||||
// 切换全屏图标
|
||||
const toggleFullscreenIcon = () =>
|
||||
(state.fullscreenIcon =
|
||||
document.fullscreenElement !== null ? 'FullscreenExitOutlined' : 'FullscreenOutlined')
|
||||
document.fullscreenElement !== null ? 'FullscreenExitOutlined' : 'FullscreenOutlined');
|
||||
|
||||
// 监听全屏切换事件
|
||||
document.addEventListener('fullscreenchange', toggleFullscreenIcon)
|
||||
document.addEventListener('fullscreenchange', toggleFullscreenIcon);
|
||||
|
||||
// 全屏切换
|
||||
const toggleFullScreen = () => {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen()
|
||||
document.documentElement.requestFullscreen();
|
||||
} else {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen()
|
||||
}
|
||||
document.exitFullscreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 图标列表
|
||||
const iconList = [
|
||||
{
|
||||
icon: 'SearchOutlined',
|
||||
tips: '搜索'
|
||||
tips: '搜索',
|
||||
},
|
||||
{
|
||||
icon: 'GithubOutlined',
|
||||
tips: 'github',
|
||||
eventObject: {
|
||||
click: () => window.open('https://github.com/jekip/naive-ui-admin')
|
||||
}
|
||||
click: () => window.open('https://github.com/jekip/naive-ui-admin'),
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: 'LockOutlined',
|
||||
tips: '锁屏',
|
||||
eventObject: {
|
||||
click: () => useLockscreen.setLock(true)
|
||||
}
|
||||
}
|
||||
]
|
||||
click: () => useLockscreen.setLock(true),
|
||||
},
|
||||
},
|
||||
];
|
||||
const avatarOptions = [
|
||||
{
|
||||
label: '个人设置',
|
||||
key: 1
|
||||
key: 1,
|
||||
},
|
||||
{
|
||||
label: '退出登录',
|
||||
key: 2
|
||||
key: 2,
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
//头像下拉菜单
|
||||
const avatarSelect = (key) => {
|
||||
switch (key) {
|
||||
case 1:
|
||||
router.push({ name: 'Setting' })
|
||||
router.push({ name: 'Setting' });
|
||||
break;
|
||||
case 2:
|
||||
doLogout()
|
||||
doLogout();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function openSetting() {
|
||||
const { openDrawer } = drawerSetting.value
|
||||
openDrawer()
|
||||
const { openDrawer } = drawerSetting.value;
|
||||
openDrawer();
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -298,20 +305,20 @@ export default defineComponent({
|
||||
reloadPage,
|
||||
drawerSetting,
|
||||
openSetting,
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.layout-header {
|
||||
.layout-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
height: @header-height;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
transition: all .2s ease-in-out;
|
||||
transition: all 0.2s ease-in-out;
|
||||
width: 100%;
|
||||
z-index: 11;
|
||||
//color: #fff;
|
||||
@@ -369,7 +376,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: hsla(0, 0%, 100%, .08);
|
||||
background: hsla(0, 0%, 100%, 0.08);
|
||||
}
|
||||
|
||||
.anticon {
|
||||
@@ -382,14 +389,14 @@ export default defineComponent({
|
||||
width: auto;
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-header-light {
|
||||
.layout-header-light {
|
||||
background: #fff;
|
||||
color: #515a6e;
|
||||
|
||||
.n-icon {
|
||||
color: #515a6e
|
||||
color: #515a6e;
|
||||
}
|
||||
|
||||
.layout-header-left {
|
||||
@@ -403,21 +410,21 @@ export default defineComponent({
|
||||
background: #f8f8f9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-header-fix {
|
||||
.layout-header-fix {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 200px;
|
||||
z-index: 11;
|
||||
}
|
||||
}
|
||||
|
||||
//::v-deep(.menu-router-link) {
|
||||
// color: #515a6e;
|
||||
//
|
||||
// &:hover {
|
||||
// color: #1890ff;
|
||||
// }
|
||||
//}
|
||||
//::v-deep(.menu-router-link) {
|
||||
// color: #515a6e;
|
||||
//
|
||||
// &:hover {
|
||||
// color: #1890ff;
|
||||
// }
|
||||
//}
|
||||
</style>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import Logo from './index.vue'
|
||||
import Logo from './index.vue';
|
||||
|
||||
export { Logo }
|
||||
export { Logo };
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<template>
|
||||
<div class="logo">
|
||||
<img src="~@/assets/images/logo.png" alt=""/>
|
||||
<img src="~@/assets/images/logo.png" alt="" />
|
||||
<h2 v-show="!collapsed" class="title"> NaiveUiAdmin</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
export default {
|
||||
name: 'Index',
|
||||
props: {
|
||||
collapsed: {
|
||||
type: Boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.logo {
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -35,5 +35,5 @@ export default {
|
||||
color: white;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import MainView from './index.vue'
|
||||
import MainView from './index.vue';
|
||||
|
||||
export { MainView }
|
||||
export { MainView };
|
||||
|
||||
@@ -3,41 +3,40 @@
|
||||
<template #default="{ Component, route }">
|
||||
<transition name="zoom-fade" mode="out-in" appear>
|
||||
<keep-alive v-if="keepAliveComponents" :include="keepAliveComponents">
|
||||
<component :is="Component" :key="route.fullPath"/>
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</keep-alive>
|
||||
<component v-else :is="Component" :key="route.fullPath"/>
|
||||
<component v-else :is="Component" :key="route.fullPath" />
|
||||
</transition>
|
||||
</template>
|
||||
</RouterView>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute'
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'MainView',
|
||||
components: {},
|
||||
props: {
|
||||
notNeedKey: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
animate: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const asyncRouteStore = useAsyncRouteStore()
|
||||
const asyncRouteStore = useAsyncRouteStore();
|
||||
// 需要缓存的路由组件
|
||||
const keepAliveComponents = computed(() => asyncRouteStore.keepAliveComponents)
|
||||
const keepAliveComponents = computed(() => asyncRouteStore.keepAliveComponents);
|
||||
return {
|
||||
keepAliveComponents
|
||||
}
|
||||
}
|
||||
})
|
||||
keepAliveComponents,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
||||
<style lang="less" scoped></style>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import AsideMenu from './index.vue'
|
||||
import AsideMenu from './index.vue';
|
||||
|
||||
export { AsideMenu }
|
||||
export { AsideMenu };
|
||||
|
||||
@@ -10,102 +10,99 @@
|
||||
v-model:value="selectedKeys"
|
||||
@update:value="clickMenuItem"
|
||||
@update:expanded-keys="menuExpanded"
|
||||
>
|
||||
</NMenu>
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, computed, watch, toRefs, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute'
|
||||
import { generatorMenu } from '@/utils/index'
|
||||
import { useProjectSettingStore } from "@/store/modules/projectSetting";
|
||||
import { defineComponent, reactive, computed, watch, toRefs } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||
import { generatorMenu } from '@/utils/index';
|
||||
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'Menu',
|
||||
components: {},
|
||||
props: {
|
||||
mode: {
|
||||
// 菜单模式
|
||||
type: String,
|
||||
default: 'vertical'
|
||||
default: 'vertical',
|
||||
},
|
||||
collapsed: {
|
||||
// 侧边栏菜单是否收起
|
||||
type: Boolean
|
||||
}
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
// 当前路由
|
||||
const currentRoute = useRoute()
|
||||
const router = useRouter()
|
||||
const asyncRouteStore = useAsyncRouteStore()
|
||||
const settingStore = useProjectSettingStore()
|
||||
const { mode } = props
|
||||
const currentRoute = useRoute();
|
||||
const router = useRouter();
|
||||
const asyncRouteStore = useAsyncRouteStore();
|
||||
const settingStore = useProjectSettingStore();
|
||||
|
||||
// 获取当前打开的子菜单
|
||||
const matched = currentRoute.matched
|
||||
const matched = currentRoute.matched;
|
||||
|
||||
const getOpenKeys = matched && matched.length ? [matched[0]?.name] : []
|
||||
const getOpenKeys = matched && matched.length ? [matched[0]?.name] : [];
|
||||
|
||||
const state = reactive({
|
||||
openKeys: getOpenKeys,
|
||||
selectedKeys: currentRoute.name,
|
||||
})
|
||||
});
|
||||
|
||||
const inverted = computed(() => {
|
||||
return ['dark', 'header-dark'].includes(settingStore.navTheme)
|
||||
})
|
||||
return ['dark', 'header-dark'].includes(settingStore.navTheme);
|
||||
});
|
||||
|
||||
const menus = computed(() => {
|
||||
return generatorMenu(asyncRouteStore.getMenus)
|
||||
})
|
||||
return generatorMenu(asyncRouteStore.getMenus);
|
||||
});
|
||||
|
||||
// 监听菜单收缩状态
|
||||
watch(
|
||||
() => props.collapsed,
|
||||
(newVal) => {
|
||||
state.openKeys = newVal ? [] : getOpenKeys
|
||||
state.selectedKeys = currentRoute.name
|
||||
state.openKeys = newVal ? [] : getOpenKeys;
|
||||
state.selectedKeys = currentRoute.name;
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// 跟随页面路由变化,切换菜单选中状态
|
||||
watch(
|
||||
() => currentRoute.fullPath,
|
||||
() => {
|
||||
const matched = currentRoute.matched
|
||||
const getOpenKeys = matched && matched.length ? [matched[0]?.name] : []
|
||||
state.openKeys = getOpenKeys
|
||||
state.selectedKeys = currentRoute.name
|
||||
const matched = currentRoute.matched;
|
||||
const getOpenKeys = matched && matched.length ? [matched[0]?.name] : [];
|
||||
state.openKeys = getOpenKeys;
|
||||
state.selectedKeys = currentRoute.name;
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// 点击菜单
|
||||
function clickMenuItem(key: string) {
|
||||
if (/http(s)?:/.test(key)) {
|
||||
window.open(key)
|
||||
window.open(key);
|
||||
} else {
|
||||
router.push({ name: key })
|
||||
router.push({ name: key });
|
||||
}
|
||||
}
|
||||
|
||||
//展开菜单
|
||||
function menuExpanded(openKeys: string[]) {
|
||||
console.log(openKeys)
|
||||
if (!openKeys) return
|
||||
console.log(openKeys);
|
||||
if (!openKeys) return;
|
||||
const latestOpenKey = openKeys.pop();
|
||||
state.openKeys = latestOpenKey ? [latestOpenKey] : []
|
||||
state.openKeys = latestOpenKey ? [latestOpenKey] : [];
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
inverted,
|
||||
menus,
|
||||
mode,
|
||||
clickMenuItem,
|
||||
menuExpanded
|
||||
}
|
||||
}
|
||||
})
|
||||
menuExpanded,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
VerticalRightOutlined,
|
||||
VerticalLeftOutlined,
|
||||
ColumnWidthOutlined,
|
||||
MinusOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { Dropdown, Tabs, Card } from 'ant-design-vue'
|
||||
MinusOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { Dropdown, Tabs, Card } from 'ant-design-vue';
|
||||
|
||||
export default {
|
||||
[Tabs.name]: Tabs,
|
||||
@@ -20,5 +20,5 @@ export default {
|
||||
CloseOutlined,
|
||||
VerticalRightOutlined,
|
||||
VerticalLeftOutlined,
|
||||
ColumnWidthOutlined
|
||||
}
|
||||
ColumnWidthOutlined,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import TabsView from './index.vue'
|
||||
import TabsView from './index.vue';
|
||||
|
||||
export { TabsView }
|
||||
export { TabsView };
|
||||
|
||||
@@ -1,33 +1,49 @@
|
||||
<template>
|
||||
<div class="tabs-view"
|
||||
<div
|
||||
class="tabs-view"
|
||||
:class="{
|
||||
'tabs-view-fix':multiTabsSetting.fixed,
|
||||
'tabs-view-fixed-header':isMultiHeaderFixed,
|
||||
'tabs-view-default-background':getDarkTheme === false
|
||||
'tabs-view-fix': multiTabsSetting.fixed,
|
||||
'tabs-view-fixed-header': isMultiHeaderFixed,
|
||||
'tabs-view-default-background': getDarkTheme === false,
|
||||
}"
|
||||
:style="getChangeStyle">
|
||||
:style="getChangeStyle"
|
||||
>
|
||||
<div class="tabs-view-main">
|
||||
<div ref="navWrap" class="tabs-card" :class="{'tabs-card-scrollable': scrollable }">
|
||||
<span class="tabs-card-prev" :class="{'tabs-card-prev-hide': !scrollable }" @click="scrollPrev">
|
||||
<div ref="navWrap" class="tabs-card" :class="{ 'tabs-card-scrollable': scrollable }">
|
||||
<span
|
||||
class="tabs-card-prev"
|
||||
:class="{ 'tabs-card-prev-hide': !scrollable }"
|
||||
@click="scrollPrev"
|
||||
>
|
||||
<n-icon size="16" color="#515a6e">
|
||||
<LeftOutlined/>
|
||||
<LeftOutlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
<span class="tabs-card-next" :class="{'tabs-card-next-hide': !scrollable }" @click="scrollNext">
|
||||
<span
|
||||
class="tabs-card-next"
|
||||
:class="{ 'tabs-card-next-hide': !scrollable }"
|
||||
@click="scrollNext"
|
||||
>
|
||||
<n-icon size="16" color="#515a6e">
|
||||
<RightOutlined/>
|
||||
<RightOutlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
<div ref="navScroll" class="tabs-card-scroll">
|
||||
<div ref="navRef" class="tabs-card-nav" :style="getNavStyle">
|
||||
<Draggable :list="tabsList" animation="300" item-key="fullPath" class="flex">
|
||||
<template #item="{element}">
|
||||
<div class="tabs-card-scroll-item"
|
||||
:class="{'active-item':activeKey === element.path }"
|
||||
<template #item="{ element }">
|
||||
<div
|
||||
class="tabs-card-scroll-item"
|
||||
:class="{ 'active-item': activeKey === element.path }"
|
||||
@click.stop="goPage(element)"
|
||||
@contextmenu="handleContextMenu($event,element)">
|
||||
@contextmenu="handleContextMenu($event, element)"
|
||||
>
|
||||
<span>{{ element.meta.title }}</span>
|
||||
<n-icon size="14" @click.stop="closeTabItem(element)" v-if="element.path != baseHome">
|
||||
<n-icon
|
||||
size="14"
|
||||
@click.stop="closeTabItem(element)"
|
||||
v-if="element.path != baseHome"
|
||||
>
|
||||
<CloseOutlined />
|
||||
</n-icon>
|
||||
</div>
|
||||
@@ -37,22 +53,34 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs-close">
|
||||
<n-dropdown trigger="hover" @select="closeHandleSelect" placement="bottom-end" :options="TabsMenuOptions">
|
||||
<n-dropdown
|
||||
trigger="hover"
|
||||
@select="closeHandleSelect"
|
||||
placement="bottom-end"
|
||||
:options="TabsMenuOptions"
|
||||
>
|
||||
<div class="tabs-close-btn" @click.prevent>
|
||||
<n-icon size="16" color="#515a6e">
|
||||
<DownOutlined/>
|
||||
<DownOutlined />
|
||||
</n-icon>
|
||||
</div>
|
||||
</n-dropdown>
|
||||
</div>
|
||||
<n-dropdown :show="showDropdown" :x="dropdownX" :y="dropdownY" @clickoutside="onClickOutside"
|
||||
placement="bottom-start" @select="closeHandleSelect" :options="TabsMenuOptions"/>
|
||||
<n-dropdown
|
||||
:show="showDropdown"
|
||||
:x="dropdownX"
|
||||
:y="dropdownY"
|
||||
@clickoutside="onClickOutside"
|
||||
placement="bottom-start"
|
||||
@select="closeHandleSelect"
|
||||
:options="TabsMenuOptions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
import {
|
||||
defineComponent,
|
||||
reactive,
|
||||
computed,
|
||||
@@ -63,35 +91,20 @@ import {
|
||||
provide,
|
||||
watch,
|
||||
onMounted,
|
||||
nextTick
|
||||
} from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { storage } from '@/utils/Storage'
|
||||
import { TABS_ROUTES } from '@/store/mutation-types'
|
||||
import { useTabsViewStore } from '@/store/modules/tabsView'
|
||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute'
|
||||
import { RouteItem } from '@/store/modules/tabsView'
|
||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting'
|
||||
import { useMessage } from 'naive-ui'
|
||||
// @ts-ignore
|
||||
import Draggable from 'vuedraggable/src/vuedraggable'
|
||||
import { PageEnum } from '@/enums/pageEnum'
|
||||
import {
|
||||
DownOutlined,
|
||||
ReloadOutlined,
|
||||
CloseOutlined,
|
||||
ColumnWidthOutlined,
|
||||
MinusOutlined,
|
||||
LeftOutlined,
|
||||
RightOutlined
|
||||
} from '@vicons/antd'
|
||||
import { renderIcon } from '@/utils/index'
|
||||
import elementResizeDetectorMaker from 'element-resize-detector'
|
||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TabsView',
|
||||
components: {
|
||||
nextTick,
|
||||
} from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { storage } from '@/utils/Storage';
|
||||
import { TABS_ROUTES } from '@/store/mutation-types';
|
||||
import { useTabsViewStore } from '@/store/modules/tabsView';
|
||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||
import { RouteItem } from '@/store/modules/tabsView';
|
||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||
import { useMessage } from 'naive-ui';
|
||||
// @ts-ignore
|
||||
import Draggable from 'vuedraggable/src/vuedraggable';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
import {
|
||||
DownOutlined,
|
||||
ReloadOutlined,
|
||||
CloseOutlined,
|
||||
@@ -99,325 +112,346 @@ export default defineComponent({
|
||||
MinusOutlined,
|
||||
LeftOutlined,
|
||||
RightOutlined,
|
||||
Draggable
|
||||
} from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
import elementResizeDetectorMaker from 'element-resize-detector';
|
||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TabsView',
|
||||
components: {
|
||||
DownOutlined,
|
||||
CloseOutlined,
|
||||
LeftOutlined,
|
||||
RightOutlined,
|
||||
Draggable,
|
||||
},
|
||||
props: {
|
||||
collapsed: {
|
||||
type: Boolean
|
||||
}
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { getDarkTheme } = useDesignSetting()
|
||||
const { getNavMode, getHeaderSetting, getMenuSetting, getMultiTabsSetting } = useProjectSetting()
|
||||
const { getDarkTheme } = useDesignSetting();
|
||||
const { getNavMode, getHeaderSetting, getMenuSetting, getMultiTabsSetting } =
|
||||
useProjectSetting();
|
||||
|
||||
const message = useMessage()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const tabsViewStore = useTabsViewStore()
|
||||
const asyncRouteStore = useAsyncRouteStore()
|
||||
const navRef: any = ref(null)
|
||||
const navScroll: any = ref(null)
|
||||
const navWrap: any = ref(null)
|
||||
const isCurrent = ref(false)
|
||||
const message = useMessage();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const tabsViewStore = useTabsViewStore();
|
||||
const asyncRouteStore = useAsyncRouteStore();
|
||||
const navRef: any = ref(null);
|
||||
const navScroll: any = ref(null);
|
||||
const navWrap: any = ref(null);
|
||||
const isCurrent = ref(false);
|
||||
|
||||
const state = reactive({
|
||||
activeKey: route.fullPath,
|
||||
scrollable: false,
|
||||
navStyle: {
|
||||
transform: ''
|
||||
transform: '',
|
||||
},
|
||||
dropdownX: 0,
|
||||
dropdownY: 0,
|
||||
showDropdown: false,
|
||||
isMultiHeaderFixed: false,
|
||||
multiTabsSetting: getMultiTabsSetting,
|
||||
})
|
||||
});
|
||||
|
||||
// 获取简易的路由对象
|
||||
const getSimpleRoute = (route): RouteItem => {
|
||||
const { fullPath, hash, meta, name, params, path, query } = route
|
||||
return { fullPath, hash, meta, name, params, path, query }
|
||||
}
|
||||
const { fullPath, hash, meta, name, params, path, query } = route;
|
||||
return { fullPath, hash, meta, name, params, path, query };
|
||||
};
|
||||
|
||||
//动态组装样式 菜单缩进
|
||||
const getChangeStyle = computed(() => {
|
||||
const { collapsed } = props
|
||||
const navMode = unref(getNavMode)
|
||||
const { minMenuWidth, menuWidth }: any = unref(getMenuSetting)
|
||||
const { fixed }: any = unref(getMultiTabsSetting)
|
||||
let lenNum = navMode === 'horizontal' ? '0px' : collapsed ? `${ minMenuWidth }px` : `${ menuWidth }px`
|
||||
const { collapsed } = props;
|
||||
const navMode = unref(getNavMode);
|
||||
const { minMenuWidth, menuWidth }: any = unref(getMenuSetting);
|
||||
const { fixed }: any = unref(getMultiTabsSetting);
|
||||
let lenNum =
|
||||
navMode === 'horizontal' ? '0px' : collapsed ? `${minMenuWidth}px` : `${menuWidth}px`;
|
||||
return {
|
||||
left: lenNum,
|
||||
width: `calc(100% - ${ !fixed ? '0px' : lenNum })`
|
||||
}
|
||||
})
|
||||
width: `calc(100% - ${!fixed ? '0px' : lenNum})`,
|
||||
};
|
||||
});
|
||||
|
||||
//tags 右侧下拉菜单
|
||||
const TabsMenuOptions = computed(() => {
|
||||
const isDisabled = unref(tabsList).length <= 1 ? true : false
|
||||
const isDisabled = unref(tabsList).length <= 1 ? true : false;
|
||||
return [
|
||||
{
|
||||
label: '刷新当前',
|
||||
key: '1',
|
||||
icon: renderIcon(ReloadOutlined)
|
||||
icon: renderIcon(ReloadOutlined),
|
||||
},
|
||||
{
|
||||
label: `关闭当前`,
|
||||
key: '2',
|
||||
disabled: unref(isCurrent) || isDisabled,
|
||||
icon: renderIcon(CloseOutlined)
|
||||
icon: renderIcon(CloseOutlined),
|
||||
},
|
||||
{
|
||||
label: '关闭其他',
|
||||
key: '3',
|
||||
disabled:isDisabled,
|
||||
icon: renderIcon(ColumnWidthOutlined)
|
||||
disabled: isDisabled,
|
||||
icon: renderIcon(ColumnWidthOutlined),
|
||||
},
|
||||
{
|
||||
label: '关闭全部',
|
||||
key: '4',
|
||||
disabled:isDisabled,
|
||||
icon: renderIcon(MinusOutlined)
|
||||
}
|
||||
]
|
||||
})
|
||||
disabled: isDisabled,
|
||||
icon: renderIcon(MinusOutlined),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
let routes: RouteItem[] = []
|
||||
let routes: RouteItem[] = [];
|
||||
|
||||
try {
|
||||
const routesStr = storage.get(TABS_ROUTES) as string | null | undefined
|
||||
routes = routesStr ? JSON.parse(routesStr) : [getSimpleRoute(route)]
|
||||
const routesStr = storage.get(TABS_ROUTES) as string | null | undefined;
|
||||
routes = routesStr ? JSON.parse(routesStr) : [getSimpleRoute(route)];
|
||||
} catch (e) {
|
||||
routes = [getSimpleRoute(route)]
|
||||
routes = [getSimpleRoute(route)];
|
||||
}
|
||||
|
||||
// 初始化标签页
|
||||
tabsViewStore.initTabs(routes)
|
||||
|
||||
tabsViewStore.initTabs(routes);
|
||||
|
||||
//监听滚动条
|
||||
function onScroll(e) {
|
||||
let scrollTop = e.target.scrollTop || (document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop); // 滚动条偏移量
|
||||
let scrollTop =
|
||||
e.target.scrollTop ||
|
||||
document.documentElement.scrollTop ||
|
||||
window.pageYOffset ||
|
||||
document.body.scrollTop; // 滚动条偏移量
|
||||
if (!getHeaderSetting.fixed && getMultiTabsSetting.fixed && scrollTop >= 64) {
|
||||
state.isMultiHeaderFixed = true
|
||||
state.isMultiHeaderFixed = true;
|
||||
} else {
|
||||
state.isMultiHeaderFixed = false
|
||||
state.isMultiHeaderFixed = false;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', onScroll, true)
|
||||
window.addEventListener('scroll', onScroll, true);
|
||||
|
||||
// 移除缓存组件名称
|
||||
const delKeepAliveCompName = () => {
|
||||
if (route.meta.keepAlive) {
|
||||
const name = router.currentRoute.value.matched.find((item) => item.name == route.name)
|
||||
?.components?.default.name
|
||||
?.components?.default.name;
|
||||
if (name) {
|
||||
asyncRouteStore.keepAliveComponents = asyncRouteStore.keepAliveComponents.filter(
|
||||
(item) => item != name
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 标签页列表
|
||||
const tabsList: any = computed(() => tabsViewStore.tabsList)
|
||||
const whiteList: string[] = [PageEnum.BASE_LOGIN_NAME, PageEnum.REDIRECT_NAME, PageEnum.ERROR_PAGE_NAME]
|
||||
const tabsList: any = computed(() => tabsViewStore.tabsList);
|
||||
const whiteList: string[] = [
|
||||
PageEnum.BASE_LOGIN_NAME,
|
||||
PageEnum.REDIRECT_NAME,
|
||||
PageEnum.ERROR_PAGE_NAME,
|
||||
];
|
||||
|
||||
watch(
|
||||
() => route.fullPath,
|
||||
(to) => {
|
||||
if (whiteList.includes(route.name as string)) return
|
||||
state.activeKey = to
|
||||
tabsViewStore.addTabs(getSimpleRoute(route))
|
||||
updateNavScroll()
|
||||
if (whiteList.includes(route.name as string)) return;
|
||||
state.activeKey = to;
|
||||
tabsViewStore.addTabs(getSimpleRoute(route));
|
||||
updateNavScroll();
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
);
|
||||
|
||||
// 在页面关闭或刷新之前,保存数据
|
||||
window.addEventListener('beforeunload', () => {
|
||||
storage.set(TABS_ROUTES, JSON.stringify(tabsList.value))
|
||||
})
|
||||
storage.set(TABS_ROUTES, JSON.stringify(tabsList.value));
|
||||
});
|
||||
|
||||
// 关闭当前页面
|
||||
const removeTab = (route) => {
|
||||
if (tabsList.value.length === 1) {
|
||||
return message.warning('这已经是最后一页,不能再关闭了!')
|
||||
return message.warning('这已经是最后一页,不能再关闭了!');
|
||||
}
|
||||
delKeepAliveCompName()
|
||||
tabsViewStore.closeCurrentTab(route)
|
||||
delKeepAliveCompName();
|
||||
tabsViewStore.closeCurrentTab(route);
|
||||
// 如果关闭的是当前页
|
||||
if (state.activeKey === route.fullPath) {
|
||||
const currentRoute = tabsList.value[Math.max(0, tabsList.value.length - 1)]
|
||||
state.activeKey = currentRoute.fullPath
|
||||
router.push(currentRoute)
|
||||
}
|
||||
updateNavScroll()
|
||||
const currentRoute = tabsList.value[Math.max(0, tabsList.value.length - 1)];
|
||||
state.activeKey = currentRoute.fullPath;
|
||||
router.push(currentRoute);
|
||||
}
|
||||
updateNavScroll();
|
||||
};
|
||||
|
||||
// 刷新页面
|
||||
const reloadPage = () => {
|
||||
delKeepAliveCompName()
|
||||
delKeepAliveCompName();
|
||||
router.push({
|
||||
path: '/redirect' + unref(route).fullPath
|
||||
})
|
||||
}
|
||||
path: '/redirect' + unref(route).fullPath,
|
||||
});
|
||||
};
|
||||
|
||||
// 注入刷新页面方法
|
||||
provide('reloadPage', reloadPage)
|
||||
provide('reloadPage', reloadPage);
|
||||
|
||||
// 关闭左侧
|
||||
const closeLeft = (route) => {
|
||||
tabsViewStore.closeLeftTabs(route)
|
||||
state.activeKey = route.fullPath
|
||||
router.replace(route.fullPath)
|
||||
updateNavScroll()
|
||||
}
|
||||
tabsViewStore.closeLeftTabs(route);
|
||||
state.activeKey = route.fullPath;
|
||||
router.replace(route.fullPath);
|
||||
updateNavScroll();
|
||||
};
|
||||
|
||||
// 关闭右侧
|
||||
const closeRight = (route) => {
|
||||
tabsViewStore.closeRightTabs(route)
|
||||
state.activeKey = route.fullPath
|
||||
router.replace(route.fullPath)
|
||||
updateNavScroll()
|
||||
}
|
||||
tabsViewStore.closeRightTabs(route);
|
||||
state.activeKey = route.fullPath;
|
||||
router.replace(route.fullPath);
|
||||
updateNavScroll();
|
||||
};
|
||||
|
||||
// 关闭其他
|
||||
const closeOther = (route) => {
|
||||
tabsViewStore.closeOtherTabs(route)
|
||||
state.activeKey = route.fullPath
|
||||
router.replace(route.fullPath)
|
||||
updateNavScroll()
|
||||
}
|
||||
tabsViewStore.closeOtherTabs(route);
|
||||
state.activeKey = route.fullPath;
|
||||
router.replace(route.fullPath);
|
||||
updateNavScroll();
|
||||
};
|
||||
|
||||
// 关闭全部
|
||||
const closeAll = () => {
|
||||
localStorage.removeItem('routes')
|
||||
tabsViewStore.closeAllTabs()
|
||||
router.replace(PageEnum.BASE_HOME)
|
||||
updateNavScroll()
|
||||
}
|
||||
localStorage.removeItem('routes');
|
||||
tabsViewStore.closeAllTabs();
|
||||
router.replace(PageEnum.BASE_HOME);
|
||||
updateNavScroll();
|
||||
};
|
||||
|
||||
//tab 操作
|
||||
const closeHandleSelect = (key) => {
|
||||
switch (key) {
|
||||
//刷新
|
||||
case '1':
|
||||
reloadPage()
|
||||
break
|
||||
reloadPage();
|
||||
break;
|
||||
//关闭
|
||||
case '2':
|
||||
removeTab(route)
|
||||
break
|
||||
removeTab(route);
|
||||
break;
|
||||
//关闭其他
|
||||
case '3':
|
||||
closeOther(route)
|
||||
break
|
||||
closeOther(route);
|
||||
break;
|
||||
//关闭所有
|
||||
case '4':
|
||||
closeAll()
|
||||
break
|
||||
}
|
||||
updateNavScroll()
|
||||
closeAll();
|
||||
break;
|
||||
}
|
||||
updateNavScroll();
|
||||
};
|
||||
|
||||
function getCurrentScrollOffset() {
|
||||
const { navStyle } = state
|
||||
const transform: any = toRaw(navStyle.transform)
|
||||
return transform ? Number(transform.match(/translateX\(-(\d+(\.\d+)*)px\)/)[1]) : 0
|
||||
const { navStyle } = state;
|
||||
const transform: any = toRaw(navStyle.transform);
|
||||
return transform ? Number(transform.match(/translateX\(-(\d+(\.\d+)*)px\)/)[1]) : 0;
|
||||
}
|
||||
|
||||
function setOffset(value) {
|
||||
state.navStyle.transform = `translateX(-${ value }px)`
|
||||
state.navStyle.transform = `translateX(-${value}px)`;
|
||||
}
|
||||
|
||||
function scrollPrev() {
|
||||
const containerWidth = navScroll.value.offsetWidth
|
||||
const currentOffset = getCurrentScrollOffset()
|
||||
if (!currentOffset) return
|
||||
let newOffset = currentOffset > containerWidth ? currentOffset - containerWidth : 0
|
||||
setOffset(newOffset)
|
||||
const containerWidth = navScroll.value.offsetWidth;
|
||||
const currentOffset = getCurrentScrollOffset();
|
||||
if (!currentOffset) return;
|
||||
let newOffset = currentOffset > containerWidth ? currentOffset - containerWidth : 0;
|
||||
setOffset(newOffset);
|
||||
}
|
||||
|
||||
function scrollNext() {
|
||||
const navWidth = navRef.value.scrollWidth
|
||||
const containerWidth = navScroll.value.offsetWidth
|
||||
const currentOffset = getCurrentScrollOffset()
|
||||
if (navWidth - currentOffset <= containerWidth) return
|
||||
const navWidth = navRef.value.scrollWidth;
|
||||
const containerWidth = navScroll.value.offsetWidth;
|
||||
const currentOffset = getCurrentScrollOffset();
|
||||
if (navWidth - currentOffset <= containerWidth) return;
|
||||
|
||||
let newOffset =
|
||||
navWidth - currentOffset > containerWidth * 2
|
||||
? currentOffset + containerWidth
|
||||
: navWidth - containerWidth
|
||||
: navWidth - containerWidth;
|
||||
|
||||
setOffset(newOffset)
|
||||
setOffset(newOffset);
|
||||
}
|
||||
|
||||
function updateNavScroll() {
|
||||
if (!navRef.value) return
|
||||
let navWidth = navRef.value.scrollWidth
|
||||
let containerWidth = navScroll.value.offsetWidth
|
||||
const currentOffset = getCurrentScrollOffset()
|
||||
if (!navRef.value) return;
|
||||
let navWidth = navRef.value.scrollWidth;
|
||||
let containerWidth = navScroll.value.offsetWidth;
|
||||
const currentOffset = getCurrentScrollOffset();
|
||||
if (containerWidth < navWidth) {
|
||||
state.scrollable = true
|
||||
state.scrollable = true;
|
||||
if (navWidth - currentOffset < containerWidth) {
|
||||
setOffset(navWidth - containerWidth)
|
||||
setOffset(navWidth - containerWidth);
|
||||
}
|
||||
} else {
|
||||
state.scrollable = false
|
||||
state.scrollable = false;
|
||||
if (currentOffset > 0) {
|
||||
setOffset(0)
|
||||
setOffset(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleResize() {
|
||||
updateNavScroll()
|
||||
updateNavScroll();
|
||||
}
|
||||
|
||||
const getNavStyle = computed(() => {
|
||||
return state.navStyle
|
||||
})
|
||||
return state.navStyle;
|
||||
});
|
||||
|
||||
function handleContextMenu(e,item) {
|
||||
e.preventDefault()
|
||||
isCurrent.value = PageEnum.BASE_HOME_REDIRECT === item.path
|
||||
state.showDropdown = false
|
||||
function handleContextMenu(e, item) {
|
||||
e.preventDefault();
|
||||
isCurrent.value = PageEnum.BASE_HOME_REDIRECT === item.path;
|
||||
state.showDropdown = false;
|
||||
nextTick().then(() => {
|
||||
state.showDropdown = true
|
||||
state.dropdownX = e.clientX
|
||||
state.dropdownY = e.clientY
|
||||
})
|
||||
state.showDropdown = true;
|
||||
state.dropdownX = e.clientX;
|
||||
state.dropdownY = e.clientY;
|
||||
});
|
||||
}
|
||||
|
||||
function onClickOutside() {
|
||||
state.showDropdown = false
|
||||
state.showDropdown = false;
|
||||
}
|
||||
|
||||
//tags 跳转页面
|
||||
function goPage(e) {
|
||||
const { fullPath } = e
|
||||
if (fullPath === route.fullPath) return
|
||||
state.activeKey = fullPath
|
||||
router.push({ path: fullPath })
|
||||
const { fullPath } = e;
|
||||
if (fullPath === route.fullPath) return;
|
||||
state.activeKey = fullPath;
|
||||
router.push({ path: fullPath });
|
||||
}
|
||||
|
||||
//删除tab
|
||||
function closeTabItem(e) {
|
||||
const { fullPath } = e
|
||||
const routeInfo = tabsList.value.find((item) => item.fullPath == fullPath)
|
||||
removeTab(routeInfo)
|
||||
const { fullPath } = e;
|
||||
const routeInfo = tabsList.value.find((item) => item.fullPath == fullPath);
|
||||
removeTab(routeInfo);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onElementResize()
|
||||
})
|
||||
onElementResize();
|
||||
});
|
||||
|
||||
function onElementResize() {
|
||||
let observer
|
||||
observer = elementResizeDetectorMaker()
|
||||
observer.listenTo(navWrap.value, handleResize)
|
||||
let observer;
|
||||
observer = elementResizeDetectorMaker();
|
||||
observer.listenTo(navWrap.value, handleResize);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -427,7 +461,7 @@ export default defineComponent({
|
||||
navScroll,
|
||||
route,
|
||||
tabsList,
|
||||
baseHome:PageEnum.BASE_HOME_REDIRECT,
|
||||
baseHome: PageEnum.BASE_HOME_REDIRECT,
|
||||
goPage,
|
||||
closeTabItem,
|
||||
closeLeft,
|
||||
@@ -443,14 +477,14 @@ export default defineComponent({
|
||||
getNavStyle,
|
||||
handleContextMenu,
|
||||
onClickOutside,
|
||||
getDarkTheme
|
||||
}
|
||||
}
|
||||
})
|
||||
getDarkTheme,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.tabs-view {
|
||||
.tabs-view {
|
||||
width: 100%;
|
||||
padding: 6px 0;
|
||||
display: flex;
|
||||
@@ -554,7 +588,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
.active-item {
|
||||
color: #2d8cf0
|
||||
color: #2d8cf0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -584,20 +618,20 @@ export default defineComponent({
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-view-default-background {
|
||||
.tabs-view-default-background {
|
||||
background: #f5f7f9;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-view-fix {
|
||||
.tabs-view-fix {
|
||||
position: fixed;
|
||||
z-index: 5;
|
||||
padding: 6px 19px 6px 10px;
|
||||
left: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-view-fixed-header {
|
||||
.tabs-view-fixed-header {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<template>
|
||||
<NLayout class="layout" :position="fixedMenu" has-sider>
|
||||
|
||||
<NLayoutSider
|
||||
v-if="navMode ==='vertical'"
|
||||
v-if="navMode === 'vertical'"
|
||||
show-trigger
|
||||
@collapse="collapsed = true"
|
||||
:position="fixedMenu"
|
||||
@@ -12,50 +11,62 @@
|
||||
:collapsed-width="64"
|
||||
:width="leftMenuWidth"
|
||||
:native-scrollbar="false"
|
||||
:inverted="inverted" class="layout-sider">
|
||||
<Logo :collapsed="collapsed"/>
|
||||
<AsideMenu v-model:collapsed="collapsed"/>
|
||||
:inverted="inverted"
|
||||
class="layout-sider"
|
||||
>
|
||||
<Logo :collapsed="collapsed" />
|
||||
<AsideMenu v-model:collapsed="collapsed" />
|
||||
</NLayoutSider>
|
||||
|
||||
<NLayout :inverted="inverted">
|
||||
|
||||
<NLayoutHeader :inverted="inverted" :position="fixedHeader">
|
||||
<PageHeader v-model:collapsed="collapsed"/>
|
||||
<PageHeader v-model:collapsed="collapsed" />
|
||||
</NLayoutHeader>
|
||||
|
||||
<NLayoutContent class="layout-content" :class="{'layout-default-background':getDarkTheme === false}">
|
||||
|
||||
<div class="layout-content-main"
|
||||
:class="{'layout-content-main-fix':fixedMulti,'fluid-header':fixedHeader === 'static'}">
|
||||
<TabsView v-if="isMultiTabs" v-model:collapsed="collapsed"/>
|
||||
<div class="main-view" :class="{'main-view-fix':fixedMulti,'noMultiTabs':!isMultiTabs,'mt-3':!isMultiTabs}">
|
||||
<MainView/>
|
||||
<NLayoutContent
|
||||
class="layout-content"
|
||||
:class="{ 'layout-default-background': getDarkTheme === false }"
|
||||
>
|
||||
<div
|
||||
class="layout-content-main"
|
||||
:class="{
|
||||
'layout-content-main-fix': fixedMulti,
|
||||
'fluid-header': fixedHeader === 'static',
|
||||
}"
|
||||
>
|
||||
<TabsView v-if="isMultiTabs" v-model:collapsed="collapsed" />
|
||||
<div
|
||||
class="main-view"
|
||||
:class="{
|
||||
'main-view-fix': fixedMulti,
|
||||
noMultiTabs: !isMultiTabs,
|
||||
'mt-3': !isMultiTabs,
|
||||
}"
|
||||
>
|
||||
<MainView />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NLayoutFooter v-if="getShowFooter">
|
||||
<PageFooter/>
|
||||
<PageFooter />
|
||||
</NLayoutFooter>
|
||||
|
||||
</NLayoutContent>
|
||||
|
||||
</NLayout>
|
||||
|
||||
</NLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, unref, h, watch, computed, onMounted } from 'vue'
|
||||
import { Logo } from './components/Logo'
|
||||
import { TabsView } from './components/TagsView'
|
||||
import { MainView } from './components/Main'
|
||||
import { AsideMenu } from './components/Menu'
|
||||
import { PageHeader } from './components/Header'
|
||||
import { PageFooter } from './components/Footer'
|
||||
import { useProjectSetting } from "@/hooks/setting/useProjectSetting";
|
||||
import { useDesignSetting } from "@/hooks/setting/useDesignSetting";
|
||||
import { defineComponent, ref, unref, computed, onMounted } from 'vue';
|
||||
import { Logo } from './components/Logo';
|
||||
import { TabsView } from './components/TagsView';
|
||||
import { MainView } from './components/Main';
|
||||
import { AsideMenu } from './components/Menu';
|
||||
import { PageHeader } from './components/Header';
|
||||
import { PageFooter } from './components/Footer';
|
||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'Layout',
|
||||
components: {
|
||||
TabsView,
|
||||
@@ -63,10 +74,10 @@ export default defineComponent({
|
||||
PageHeader,
|
||||
AsideMenu,
|
||||
Logo,
|
||||
PageFooter
|
||||
PageFooter,
|
||||
},
|
||||
setup() {
|
||||
const { getDarkTheme } = useDesignSetting()
|
||||
const { getDarkTheme } = useDesignSetting();
|
||||
|
||||
const {
|
||||
getShowFooter,
|
||||
@@ -74,57 +85,57 @@ export default defineComponent({
|
||||
getNavTheme,
|
||||
getHeaderSetting,
|
||||
getMenuSetting,
|
||||
getMultiTabsSetting
|
||||
} = useProjectSetting()
|
||||
getMultiTabsSetting,
|
||||
} = useProjectSetting();
|
||||
|
||||
const navMode = getNavMode
|
||||
const navMode = getNavMode;
|
||||
|
||||
const collapsed = ref<boolean>(false)
|
||||
const collapsed = ref<boolean>(false);
|
||||
|
||||
const fixedHeader = computed(() => {
|
||||
const { fixed } = unref(getHeaderSetting)
|
||||
return fixed ? 'absolute' : 'static'
|
||||
})
|
||||
const { fixed } = unref(getHeaderSetting);
|
||||
return fixed ? 'absolute' : 'static';
|
||||
});
|
||||
|
||||
const fixedMenu = computed(() => {
|
||||
const { fixed } = unref(getHeaderSetting)
|
||||
return fixed ? 'absolute' : 'static'
|
||||
})
|
||||
const { fixed } = unref(getHeaderSetting);
|
||||
return fixed ? 'absolute' : 'static';
|
||||
});
|
||||
|
||||
const isMultiTabs = computed(() => {
|
||||
return unref(getMultiTabsSetting).show
|
||||
})
|
||||
return unref(getMultiTabsSetting).show;
|
||||
});
|
||||
|
||||
const fixedMulti = computed(() => {
|
||||
return unref(getMultiTabsSetting).fixed
|
||||
})
|
||||
return unref(getMultiTabsSetting).fixed;
|
||||
});
|
||||
|
||||
const inverted = computed(() => {
|
||||
return ['dark', 'header-dark'].includes(unref(getNavTheme))
|
||||
})
|
||||
return ['dark', 'header-dark'].includes(unref(getNavTheme));
|
||||
});
|
||||
|
||||
const leftMenuWidth = computed(() => {
|
||||
const { minMenuWidth, menuWidth } = unref(getMenuSetting)
|
||||
return collapsed.value ? minMenuWidth : menuWidth
|
||||
})
|
||||
const { minMenuWidth, menuWidth } = unref(getMenuSetting);
|
||||
return collapsed.value ? minMenuWidth : menuWidth;
|
||||
});
|
||||
|
||||
const getChangeStyle = computed(() => {
|
||||
const { minMenuWidth, menuWidth } = unref(getMenuSetting)
|
||||
const { minMenuWidth, menuWidth } = unref(getMenuSetting);
|
||||
return {
|
||||
'padding-left': collapsed.value ? `${ minMenuWidth }px` : `${ menuWidth }px`
|
||||
}
|
||||
})
|
||||
'padding-left': collapsed.value ? `${minMenuWidth}px` : `${menuWidth}px`,
|
||||
};
|
||||
});
|
||||
|
||||
function watchWidth() {
|
||||
const Width = document.body.clientWidth
|
||||
const Width = document.body.clientWidth;
|
||||
if (Width <= 950) {
|
||||
collapsed.value = true
|
||||
} else collapsed.value = false
|
||||
collapsed.value = true;
|
||||
} else collapsed.value = false;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', watchWidth);
|
||||
})
|
||||
});
|
||||
|
||||
return {
|
||||
fixedMenu,
|
||||
@@ -137,14 +148,14 @@ export default defineComponent({
|
||||
getChangeStyle,
|
||||
navMode,
|
||||
getShowFooter,
|
||||
getDarkTheme
|
||||
}
|
||||
}
|
||||
})
|
||||
getDarkTheme,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.layout {
|
||||
.layout {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: auto;
|
||||
@@ -158,7 +169,7 @@ export default defineComponent({
|
||||
box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);
|
||||
position: relative;
|
||||
z-index: 13;
|
||||
transition: all .2s ease-in-out;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.layout-sider-fix {
|
||||
@@ -190,28 +201,27 @@ export default defineComponent({
|
||||
.n-layout-footer {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.layout-content-main {
|
||||
.layout-content-main {
|
||||
margin: 0 10px 10px;
|
||||
position: relative;
|
||||
padding-top: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-content-main-fix {
|
||||
.layout-content-main-fix {
|
||||
padding-top: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
.fluid-header {
|
||||
.fluid-header {
|
||||
padding-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.main-view-fix {
|
||||
.main-view-fix {
|
||||
padding-top: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.noMultiTabs {
|
||||
.noMultiTabs {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
34
src/main.ts
34
src/main.ts
@@ -1,34 +1,34 @@
|
||||
import './styles/tailwind.css'
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router, { setupRouter } from './router'
|
||||
import { setupStore } from '@/store'
|
||||
import MakeitCaptcha from 'makeit-captcha'
|
||||
import 'makeit-captcha/dist/captcha.min.css'
|
||||
import { setupNaive, setupDirectives, setupGlobalMethods, setupCustomComponents } from '@/plugins'
|
||||
import './styles/tailwind.css';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import router, { setupRouter } from './router';
|
||||
import { setupStore } from '@/store';
|
||||
import MakeitCaptcha from 'makeit-captcha';
|
||||
import 'makeit-captcha/dist/captcha.min.css';
|
||||
import { setupNaive, setupDirectives, setupGlobalMethods, setupCustomComponents } from '@/plugins';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = createApp(App)
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(MakeitCaptcha)
|
||||
app.use(MakeitCaptcha);
|
||||
|
||||
// 注册全局常用的 naive-ui 组件
|
||||
setupNaive(app)
|
||||
setupNaive(app);
|
||||
|
||||
// 注册全局自定义组件,如:<svg-icon />
|
||||
setupCustomComponents(app)
|
||||
setupCustomComponents(app);
|
||||
|
||||
// 注册全局自定义指令,如:v-permission权限指令
|
||||
setupDirectives(app)
|
||||
setupDirectives(app);
|
||||
|
||||
// 注册全局方法,如:app.config.globalProperties.$message = message
|
||||
setupGlobalMethods(app)
|
||||
setupGlobalMethods(app);
|
||||
|
||||
// 挂载状态管理
|
||||
setupStore(app)
|
||||
setupStore(app);
|
||||
|
||||
// 挂载路由
|
||||
await setupRouter(app)
|
||||
await setupRouter(app);
|
||||
|
||||
// 路由准备就绪后挂载APP实例
|
||||
await router.isReady();
|
||||
@@ -36,4 +36,4 @@ async function bootstrap() {
|
||||
app.mount('#app', true);
|
||||
}
|
||||
|
||||
void bootstrap()
|
||||
void bootstrap();
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { App } from 'vue'
|
||||
|
||||
/**
|
||||
* 全局注册自定义组件 待完善
|
||||
* @param app
|
||||
*/
|
||||
export function setupCustomComponents(app: App) {
|
||||
export function setupCustomComponents() {
|
||||
// app.component()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { App } from 'vue'
|
||||
import { App } from 'vue';
|
||||
|
||||
import { permission } from '@/directives/permission'
|
||||
import { permission } from '@/directives/permission';
|
||||
|
||||
/**
|
||||
* 注册全局自定义指令
|
||||
@@ -8,5 +8,5 @@ import { permission } from '@/directives/permission'
|
||||
*/
|
||||
export function setupDirectives(app: App) {
|
||||
// 权限控制指令(演示)
|
||||
app.directive('permission', permission)
|
||||
app.directive('permission', permission);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { App } from 'vue'
|
||||
|
||||
/**
|
||||
* 注册全局方法 待完善
|
||||
* @param app
|
||||
*/
|
||||
export function setupGlobalMethods(app: App) {
|
||||
|
||||
}
|
||||
export function setupGlobalMethods() {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { setupNaive } from '@/plugins/naive'
|
||||
export { setupDirectives } from '@/plugins/directives'
|
||||
export { setupCustomComponents } from '@/plugins/customComponents'
|
||||
export { setupGlobalMethods } from '@/plugins/globalMethods'
|
||||
export { setupNaive } from '@/plugins/naive';
|
||||
export { setupDirectives } from '@/plugins/directives';
|
||||
export { setupCustomComponents } from '@/plugins/customComponents';
|
||||
export { setupGlobalMethods } from '@/plugins/globalMethods';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { App } from 'vue'
|
||||
import type { App } from 'vue';
|
||||
import {
|
||||
create,
|
||||
NConfigProvider,
|
||||
@@ -62,8 +62,8 @@ import {
|
||||
NModal,
|
||||
NUpload,
|
||||
NTree,
|
||||
NSpin
|
||||
} from 'naive-ui'
|
||||
NSpin,
|
||||
} from 'naive-ui';
|
||||
|
||||
const naive = create({
|
||||
components: [
|
||||
@@ -128,10 +128,10 @@ const naive = create({
|
||||
NModal,
|
||||
NUpload,
|
||||
NTree,
|
||||
NSpin
|
||||
]
|
||||
})
|
||||
NSpin,
|
||||
],
|
||||
});
|
||||
|
||||
export function setupNaive(app: App<Element>) {
|
||||
app.use(naive)
|
||||
app.use(naive);
|
||||
}
|
||||
|
||||
@@ -3,4 +3,3 @@ export const RedirectName = 'Redirect';
|
||||
export const ErrorPage = () => import('@/views/exception/404.vue');
|
||||
|
||||
export const Layout = () => import('@/layout/index.vue');
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { RouterView } from 'vue-router'
|
||||
import { renderIcon } from '@/utils/index'
|
||||
import { DashboardOutlined } from '@vicons/antd'
|
||||
import { renderIcon } from '@/utils/index';
|
||||
import { DashboardOutlined } from '@vicons/antd';
|
||||
// import { RouterTransition } from '@/components/transition'
|
||||
|
||||
//前端路由映射表
|
||||
export const constantRouterComponents = {
|
||||
'Layout': () => import('@/layout/index.vue'), //布局
|
||||
'DashboardConsole': () => import('@/views/dashboard/console/console.vue'), // 主控台
|
||||
'DashboardMonitor': () => import('@/views/dashboard/monitor/monitor.vue'), // 监控页
|
||||
'DashboardWorkplace': () => import('@/views/dashboard/workplace/workplace.vue'), // 工作台
|
||||
}
|
||||
Layout: () => import('@/layout/index.vue'), //布局
|
||||
DashboardConsole: () => import('@/views/dashboard/console/console.vue'), // 主控台
|
||||
DashboardMonitor: () => import('@/views/dashboard/monitor/monitor.vue'), // 监控页
|
||||
DashboardWorkplace: () => import('@/views/dashboard/workplace/workplace.vue'), // 工作台
|
||||
};
|
||||
|
||||
//前端路由图标映射表
|
||||
export const constantRouterIcon = {
|
||||
'DashboardOutlined': renderIcon(DashboardOutlined)
|
||||
}
|
||||
DashboardOutlined: renderIcon(DashboardOutlined),
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { adminMenus } from '@/api/system/menu'
|
||||
import { constantRouterComponents, constantRouterIcon } from './constantRouterComponents'
|
||||
import router from '@/router/index'
|
||||
import { constantRouter } from '@/router/index'
|
||||
import { RouteRecordRaw } from 'vue-router'
|
||||
import { adminMenus } from '@/api/system/menu';
|
||||
import { constantRouterComponents, constantRouterIcon } from './constantRouterComponents';
|
||||
import router from '@/router/index';
|
||||
import { constantRouter } from '@/router/index';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
/**
|
||||
* 格式化 后端 结构信息并递归生成层级路由表
|
||||
@@ -12,10 +12,10 @@ import { RouteRecordRaw } from 'vue-router'
|
||||
* @returns {*}
|
||||
*/
|
||||
export const routerGenerator = (routerMap, parent?): any[] => {
|
||||
return routerMap.map(item => {
|
||||
return routerMap.map((item) => {
|
||||
const currentRouter: any = {
|
||||
// 路由地址 动态拼接生成如 /dashboard/workplace
|
||||
path: `${ parent && parent.path || '' }/${ item.path }`,
|
||||
path: `${(parent && parent.path) || ''}/${item.path}`,
|
||||
// 路由名称,建议唯一
|
||||
name: item.name || '',
|
||||
// 该路由对应页面的 组件
|
||||
@@ -25,22 +25,21 @@ export const routerGenerator = (routerMap, parent?): any[] => {
|
||||
...item.meta,
|
||||
label: item.meta.title,
|
||||
icon: constantRouterIcon[item.meta.icon] || null,
|
||||
permission: item.meta.permission || null
|
||||
}
|
||||
}
|
||||
permission: item.meta.permission || null,
|
||||
},
|
||||
};
|
||||
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
|
||||
currentRouter.path = currentRouter.path.replace('//', '/')
|
||||
currentRouter.path = currentRouter.path.replace('//', '/');
|
||||
// 重定向
|
||||
item.redirect && (currentRouter.redirect = item.redirect)
|
||||
item.redirect && (currentRouter.redirect = item.redirect);
|
||||
// 是否有子菜单,并递归处理
|
||||
if (item.children && item.children.length > 0) {
|
||||
// Recursion
|
||||
currentRouter.children = routerGenerator(item.children, currentRouter)
|
||||
currentRouter.children = routerGenerator(item.children, currentRouter);
|
||||
}
|
||||
return currentRouter
|
||||
})
|
||||
}
|
||||
|
||||
return currentRouter;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 动态生成菜单
|
||||
@@ -51,15 +50,15 @@ export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
adminMenus()
|
||||
.then((result) => {
|
||||
const routeList = routerGenerator(result)
|
||||
const asyncRoutesList = [...constantRouter, ...routeList]
|
||||
asyncRoutesList.forEach(item => {
|
||||
router.addRoute(item)
|
||||
})
|
||||
resolve(asyncRoutesList)
|
||||
const routeList = routerGenerator(result);
|
||||
const asyncRoutesList = [...constantRouter, ...routeList];
|
||||
asyncRoutesList.forEach((item) => {
|
||||
router.addRoute(item);
|
||||
});
|
||||
resolve(asyncRoutesList);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { App } from 'vue'
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
|
||||
import { App } from 'vue';
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
||||
import { ErrorPageRoute, RedirectRoute } from '@/router/base';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
import { createRouterGuards } from './router-guards'
|
||||
import 'nprogress/css/nprogress.css' // 进度条样式
|
||||
import { createRouterGuards } from './router-guards';
|
||||
import 'nprogress/css/nprogress.css'; // 进度条样式
|
||||
|
||||
// @ts-ignore
|
||||
const modules = import.meta.globEager('./modules/**/*.ts');
|
||||
@@ -17,10 +17,10 @@ Object.keys(modules).forEach((key) => {
|
||||
});
|
||||
|
||||
function sortRoute(a, b) {
|
||||
return (a.meta.sort || 0) - (b.meta.sort || 0)
|
||||
return (a.meta.sort || 0) - (b.meta.sort || 0);
|
||||
}
|
||||
|
||||
routeModuleList.sort(sortRoute)
|
||||
routeModuleList.sort(sortRoute);
|
||||
|
||||
export const RootRoute: RouteRecordRaw = {
|
||||
path: '/',
|
||||
@@ -43,21 +43,20 @@ export const LoginRoute: RouteRecordRaw = {
|
||||
//需要验证权限
|
||||
export const asyncRoutes = [ErrorPageRoute, ...routeModuleList];
|
||||
|
||||
|
||||
//普通路由 无需验证权限
|
||||
export const constantRouter:any[] = [LoginRoute, RootRoute, RedirectRoute]
|
||||
export const constantRouter: any[] = [LoginRoute, RootRoute, RedirectRoute];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(''),
|
||||
routes: constantRouter,
|
||||
strict: true,
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
})
|
||||
});
|
||||
|
||||
export function setupRouter(app: App) {
|
||||
app.use(router)
|
||||
app.use(router);
|
||||
// 创建路由守卫
|
||||
createRouterGuards(router)
|
||||
createRouterGuards(router);
|
||||
}
|
||||
|
||||
export default router
|
||||
export default router;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { RouteRecordRaw } from 'vue-router'
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { WalletOutlined } from '@vicons/antd'
|
||||
import { renderIcon } from '@/utils/index'
|
||||
import { WalletOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
|
||||
const routeName = 'comp'
|
||||
const routeName = 'comp';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
@@ -26,28 +25,27 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '组件示例',
|
||||
icon: renderIcon(WalletOutlined),
|
||||
sort: 8
|
||||
sort: 8,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'table',
|
||||
name: `${ routeName }_table`,
|
||||
name: `${routeName}_table`,
|
||||
meta: {
|
||||
title: '表格',
|
||||
},
|
||||
component: () => import('@/views/comp/table/list.vue')
|
||||
component: () => import('@/views/comp/table/list.vue'),
|
||||
},
|
||||
{
|
||||
|
||||
path: 'upload',
|
||||
name: `${ routeName }_upload`,
|
||||
name: `${routeName}_upload`,
|
||||
meta: {
|
||||
title: '上传',
|
||||
},
|
||||
component: () => import('@/views/comp/upload/index.vue')
|
||||
}
|
||||
component: () => import('@/views/comp/upload/index.vue'),
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default routes
|
||||
export default routes;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { RouteRecordRaw } from 'vue-router'
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { DashboardOutlined } from '@vicons/antd'
|
||||
import { renderIcon } from '@/utils/index'
|
||||
import { DashboardOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
const routeName = 'dashboard'
|
||||
const routeName = 'dashboard';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
@@ -25,17 +25,17 @@ const routes: Array<RouteRecordRaw> = [
|
||||
title: 'Dashboard',
|
||||
icon: renderIcon(DashboardOutlined),
|
||||
permission: ['dashboard_console', 'dashboard_console', 'dashboard_workplace'],
|
||||
sort: 0
|
||||
sort: 0,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'console',
|
||||
name: `${ routeName }_console`,
|
||||
name: `${routeName}_console`,
|
||||
meta: {
|
||||
title: '主控台',
|
||||
permission: ['dashboard_console']
|
||||
permission: ['dashboard_console'],
|
||||
},
|
||||
component: () => import('@/views/dashboard/console/console.vue')
|
||||
component: () => import('@/views/dashboard/console/console.vue'),
|
||||
},
|
||||
// {
|
||||
// path: 'monitor',
|
||||
@@ -48,16 +48,16 @@ const routes: Array<RouteRecordRaw> = [
|
||||
// },
|
||||
{
|
||||
path: 'workplace',
|
||||
name: `${ routeName }_workplace`,
|
||||
name: `${routeName}_workplace`,
|
||||
meta: {
|
||||
title: '工作台',
|
||||
keepAlive: true,
|
||||
permission: ['dashboard_workplace']
|
||||
permission: ['dashboard_workplace'],
|
||||
},
|
||||
component: () => import('@/views/dashboard/workplace/workplace.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
component: () => import('@/views/dashboard/workplace/workplace.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes
|
||||
export default routes;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RouteRecordRaw } from 'vue-router'
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { ExclamationCircleOutlined } from '@vicons/antd'
|
||||
import { renderIcon } from '@/utils/index'
|
||||
import { ExclamationCircleOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
@@ -23,7 +23,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '异常页面',
|
||||
icon: renderIcon(ExclamationCircleOutlined),
|
||||
sort: 3
|
||||
sort: 3,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -32,7 +32,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '403',
|
||||
},
|
||||
component: () => import('@/views/exception/403.vue')
|
||||
component: () => import('@/views/exception/403.vue'),
|
||||
},
|
||||
{
|
||||
path: '404',
|
||||
@@ -40,7 +40,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '404',
|
||||
},
|
||||
component: () => import('@/views/exception/404.vue')
|
||||
component: () => import('@/views/exception/404.vue'),
|
||||
},
|
||||
{
|
||||
path: '500',
|
||||
@@ -48,10 +48,10 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '500',
|
||||
},
|
||||
component: () => import('@/views/exception/500.vue')
|
||||
component: () => import('@/views/exception/500.vue'),
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default routes
|
||||
export default routes;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RouteRecordRaw } from 'vue-router'
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { ProfileOutlined } from '@vicons/antd'
|
||||
import { renderIcon } from '@/utils/index'
|
||||
import { ProfileOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
@@ -23,7 +23,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '表单页面',
|
||||
icon: renderIcon(ProfileOutlined),
|
||||
sort: 1
|
||||
sort: 3,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -32,7 +32,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '基础表单',
|
||||
},
|
||||
component: () => import('@/views/form/basicForm/index.vue')
|
||||
component: () => import('@/views/form/basicForm/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'step-form',
|
||||
@@ -40,7 +40,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '分步表单',
|
||||
},
|
||||
component: () => import('@/views/form/stepForm/stepForm.vue')
|
||||
component: () => import('@/views/form/stepForm/stepForm.vue'),
|
||||
},
|
||||
{
|
||||
path: 'detail',
|
||||
@@ -48,11 +48,10 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '表单详情',
|
||||
},
|
||||
component: () => import('@/views/form/detail/index.vue')
|
||||
component: () => import('@/views/form/detail/index.vue'),
|
||||
},
|
||||
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default routes
|
||||
export default routes;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RouteRecordRaw } from 'vue-router'
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { TableOutlined } from '@vicons/antd'
|
||||
import { renderIcon } from '@/utils/index'
|
||||
import { TableOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
@@ -23,7 +23,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '列表页面',
|
||||
icon: renderIcon(TableOutlined),
|
||||
sort: 1
|
||||
sort: 2,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -32,19 +32,19 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '基础列表',
|
||||
},
|
||||
component: () => import('@/views/list/basicList/index.vue')
|
||||
component: () => import('@/views/list/basicList/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'basic-info/:id?',
|
||||
name: 'basic-info',
|
||||
meta: {
|
||||
title: '基础详情',
|
||||
hidden:true
|
||||
hidden: true,
|
||||
},
|
||||
component: () => import('@/views/list/basicList/info.vue'),
|
||||
},
|
||||
component: () => import('@/views/list/basicList/info.vue')
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default routes
|
||||
export default routes;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RouteRecordRaw } from 'vue-router'
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { CheckCircleOutlined } from '@vicons/antd'
|
||||
import { renderIcon } from '@/utils/index'
|
||||
import { CheckCircleOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
@@ -23,7 +23,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '结果页面',
|
||||
icon: renderIcon(CheckCircleOutlined),
|
||||
sort: 4
|
||||
sort: 4,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -32,7 +32,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '成功页',
|
||||
},
|
||||
component: () => import('@/views/result/success.vue')
|
||||
component: () => import('@/views/result/success.vue'),
|
||||
},
|
||||
{
|
||||
path: 'fail',
|
||||
@@ -40,7 +40,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '失败页',
|
||||
},
|
||||
component: () => import('@/views/result/fail.vue')
|
||||
component: () => import('@/views/result/fail.vue'),
|
||||
},
|
||||
{
|
||||
path: 'info',
|
||||
@@ -48,10 +48,10 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '信息页',
|
||||
},
|
||||
component: () => import('@/views/result/info.vue')
|
||||
component: () => import('@/views/result/info.vue'),
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default routes
|
||||
export default routes;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RouteRecordRaw } from 'vue-router'
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { SettingOutlined } from '@vicons/antd'
|
||||
import { renderIcon } from '@/utils/index'
|
||||
import { SettingOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
@@ -23,7 +23,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '设置页面',
|
||||
icon: renderIcon(SettingOutlined),
|
||||
sort: 5
|
||||
sort: 5,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -32,7 +32,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '个人设置',
|
||||
},
|
||||
component: () => import('@/views/setting/account/account.vue')
|
||||
component: () => import('@/views/setting/account/account.vue'),
|
||||
},
|
||||
{
|
||||
path: 'system',
|
||||
@@ -40,10 +40,10 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '系统设置',
|
||||
},
|
||||
component: () => import('@/views/setting/system/system.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
component: () => import('@/views/setting/system/system.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes
|
||||
export default routes;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { RouteRecordRaw } from 'vue-router'
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { ToolOutlined } from '@vicons/antd'
|
||||
import { OptionsSharp } from '@vicons/ionicons5'
|
||||
import { renderIcon } from '@/utils/index'
|
||||
import { OptionsSharp } from '@vicons/ionicons5';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
@@ -24,7 +23,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '系统设置',
|
||||
icon: renderIcon(OptionsSharp),
|
||||
sort: 1
|
||||
sort: 1,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -33,7 +32,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '菜单权限管理',
|
||||
},
|
||||
component: () => import('@/views/system/menu/menu.vue')
|
||||
component: () => import('@/views/system/menu/menu.vue'),
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
@@ -41,10 +40,10 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '角色权限管理',
|
||||
},
|
||||
component: () => import('@/views/system/role/role.vue')
|
||||
}
|
||||
component: () => import('@/views/system/role/role.vue'),
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default routes
|
||||
export default routes;
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import { isNavigationFailure, Router } from 'vue-router'
|
||||
import { useUserStoreWidthOut } from '@/store/modules/user'
|
||||
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute'
|
||||
import NProgress from 'nprogress' // progress bar
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||
import { storage } from '@/utils/Storage'
|
||||
import { PageEnum } from '@/enums/pageEnum'
|
||||
import { isNavigationFailure, Router } from 'vue-router';
|
||||
import { useUserStoreWidthOut } from '@/store/modules/user';
|
||||
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute';
|
||||
import NProgress from 'nprogress'; // progress bar
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types';
|
||||
import { storage } from '@/utils/Storage';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
|
||||
NProgress.configure({ showSpinner: false }); // NProgress Configuration
|
||||
|
||||
NProgress.configure({ showSpinner: false }) // NProgress Configuration
|
||||
const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
||||
|
||||
const LOGIN_PATH = PageEnum.BASE_LOGIN
|
||||
|
||||
const whitePathList = [LOGIN_PATH] // no redirect whitelist
|
||||
const whitePathList = [LOGIN_PATH]; // no redirect whitelist
|
||||
|
||||
export function createRouterGuards(router: Router) {
|
||||
const userStore = useUserStoreWidthOut();
|
||||
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
NProgress.start()
|
||||
NProgress.start();
|
||||
if (from.path === LOGIN_PATH && to.name === 'errorPage') {
|
||||
next(PageEnum.BASE_HOME);
|
||||
return;
|
||||
@@ -29,10 +28,9 @@ export function createRouterGuards(router: Router) {
|
||||
return;
|
||||
}
|
||||
|
||||
const token = storage.get(ACCESS_TOKEN)
|
||||
const token = storage.get(ACCESS_TOKEN);
|
||||
|
||||
if (!token) {
|
||||
|
||||
// You can access without permission. You need to set the routing meta.ignoreAuth to true
|
||||
if (to.meta.ignoreAuth) {
|
||||
next();
|
||||
@@ -58,13 +56,13 @@ export function createRouterGuards(router: Router) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userInfo = await userStore.GetInfo()
|
||||
const userInfo = await userStore.GetInfo();
|
||||
|
||||
const routes = await asyncRouteStore.generateRoutes(userInfo)
|
||||
const routes = await asyncRouteStore.generateRoutes(userInfo);
|
||||
|
||||
// 动态添加可访问路由表
|
||||
routes.forEach((item) => {
|
||||
router.addRoute(item)
|
||||
router.addRoute(item);
|
||||
});
|
||||
|
||||
const redirectPath = (from.query.redirect || to.path) as string;
|
||||
@@ -72,35 +70,33 @@ export function createRouterGuards(router: Router) {
|
||||
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
|
||||
asyncRouteStore.setDynamicAddedRoute(true);
|
||||
next(nextData);
|
||||
NProgress.done()
|
||||
})
|
||||
NProgress.done();
|
||||
});
|
||||
|
||||
router.afterEach((to, _, failure) => {
|
||||
document.title = (to?.meta?.title as string) || document.title
|
||||
document.title = (to?.meta?.title as string) || document.title;
|
||||
if (isNavigationFailure(failure)) {
|
||||
//console.log('failed navigation', failure)
|
||||
}
|
||||
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
||||
// 在这里设置需要缓存的组件名称
|
||||
const keepAliveComponents = asyncRouteStore.keepAliveComponents
|
||||
const currentComName: any = to.matched.find((item) => item.name == to.name)?.name
|
||||
const keepAliveComponents = asyncRouteStore.keepAliveComponents;
|
||||
const currentComName: any = to.matched.find((item) => item.name == to.name)?.name;
|
||||
if (currentComName && !keepAliveComponents.includes(currentComName) && to.meta?.keepAlive) {
|
||||
// 需要缓存的组件
|
||||
keepAliveComponents.push(currentComName)
|
||||
keepAliveComponents.push(currentComName);
|
||||
} else if (!to.meta?.keepAlive || to.name == 'Redirect') {
|
||||
// 不需要缓存的组件
|
||||
const index = asyncRouteStore.keepAliveComponents.findIndex(
|
||||
(name) => name == currentComName
|
||||
)
|
||||
const index = asyncRouteStore.keepAliveComponents.findIndex((name) => name == currentComName);
|
||||
if (index != -1) {
|
||||
keepAliveComponents.splice(index, 1)
|
||||
keepAliveComponents.splice(index, 1);
|
||||
}
|
||||
}
|
||||
asyncRouteStore.setKeepAliveComponents(keepAliveComponents)
|
||||
NProgress.done() // finish progress bar
|
||||
})
|
||||
asyncRouteStore.setKeepAliveComponents(keepAliveComponents);
|
||||
NProgress.done(); // finish progress bar
|
||||
});
|
||||
|
||||
router.onError((error) => {
|
||||
console.log(error, '路由错误')
|
||||
})
|
||||
console.log(error, '路由错误');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { RouteRecordRaw, RouteMeta } from 'vue-router';
|
||||
import { defineComponent } from 'vue';
|
||||
import { RoleEnum } from '@/enums/roleEnum'
|
||||
import { RoleEnum } from '@/enums/roleEnum';
|
||||
|
||||
export type Component<T extends any = any> =
|
||||
| ReturnType<typeof defineComponent>
|
||||
@@ -20,18 +20,18 @@ export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
|
||||
|
||||
export interface Meta {
|
||||
// 名称
|
||||
title: string
|
||||
title: string;
|
||||
// 是否忽略权限
|
||||
ignoreAuth?: boolean
|
||||
roles?: RoleEnum[]
|
||||
ignoreAuth?: boolean;
|
||||
roles?: RoleEnum[];
|
||||
// 是否不缓存
|
||||
noKeepAlive?: boolean
|
||||
noKeepAlive?: boolean;
|
||||
// 是否固定在tab上
|
||||
affix?: boolean
|
||||
affix?: boolean;
|
||||
// tab上的图标
|
||||
icon?: string
|
||||
icon?: string;
|
||||
// 跳转地址
|
||||
frameSrc?: string
|
||||
frameSrc?: string;
|
||||
// 外链跳转地址
|
||||
externalLink?: string
|
||||
externalLink?: string;
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ export default {
|
||||
// 集合字段名
|
||||
infoField: 'data',
|
||||
// 图片地址字段名
|
||||
imgField: 'photo'
|
||||
imgField: 'photo',
|
||||
},
|
||||
//最大上传图片大小
|
||||
maxSize: 2,
|
||||
//图片上传类型
|
||||
fileType: ['image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/svg+xml'],
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ const setting = {
|
||||
//系统主题色
|
||||
appTheme: '#2d8cf0',
|
||||
//系统内置主题色列表
|
||||
appThemeList
|
||||
}
|
||||
appThemeList,
|
||||
};
|
||||
|
||||
export default setting;
|
||||
|
||||
@@ -10,7 +10,7 @@ const setting = {
|
||||
//固定顶部
|
||||
fixed: true,
|
||||
//显示重载按钮
|
||||
isReload: true
|
||||
isReload: true,
|
||||
},
|
||||
//页脚
|
||||
showFooter: true,
|
||||
@@ -30,7 +30,7 @@ const setting = {
|
||||
//菜单宽度
|
||||
menuWidth: 200,
|
||||
//固定菜单
|
||||
fixed: true
|
||||
fixed: true,
|
||||
},
|
||||
//面包屑
|
||||
crumbsSetting: {
|
||||
@@ -41,5 +41,5 @@ const setting = {
|
||||
},
|
||||
//菜单权限模式 ROLE 前端固定角色 BACK 动态获取
|
||||
permissionMode: 'ROLE',
|
||||
}
|
||||
};
|
||||
export default setting;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { toRaw, unref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { RouteRecordRaw } from 'vue-router'
|
||||
import { store } from '@/store'
|
||||
import { asyncRoutes, constantRouter } from '@/router/index'
|
||||
import { generatorDynamicRouter } from '@/router/generator-routers'
|
||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting'
|
||||
import { toRaw, unref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { store } from '@/store';
|
||||
import { asyncRoutes, constantRouter } from '@/router/index';
|
||||
import { generatorDynamicRouter } from '@/router/generator-routers';
|
||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||
|
||||
interface TreeHelperConfig {
|
||||
id: string;
|
||||
@@ -21,14 +21,13 @@ const DEFAULT_CONFIG: TreeHelperConfig = {
|
||||
const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config);
|
||||
|
||||
interface AsyncRouteState {
|
||||
menus: RouteRecordRaw[],
|
||||
routers: any[],
|
||||
addRouters: any[],
|
||||
keepAliveComponents: string[],
|
||||
isDynamicAddedRoute: boolean
|
||||
menus: RouteRecordRaw[];
|
||||
routers: any[];
|
||||
addRouters: any[];
|
||||
keepAliveComponents: string[];
|
||||
isDynamicAddedRoute: boolean;
|
||||
}
|
||||
|
||||
|
||||
function filter<T = any>(
|
||||
tree: T[],
|
||||
func: (n: T) => boolean,
|
||||
@@ -61,7 +60,7 @@ export const useAsyncRouteStore = defineStore({
|
||||
}),
|
||||
getters: {
|
||||
getMenus(): RouteRecordRaw[] {
|
||||
return this.menus
|
||||
return this.menus;
|
||||
},
|
||||
getIsDynamicAddedRoute(): boolean {
|
||||
return this.isDynamicAddedRoute;
|
||||
@@ -69,57 +68,57 @@ export const useAsyncRouteStore = defineStore({
|
||||
},
|
||||
actions: {
|
||||
getRouters() {
|
||||
return toRaw(this.addRouters)
|
||||
return toRaw(this.addRouters);
|
||||
},
|
||||
setDynamicAddedRoute(added: boolean) {
|
||||
this.isDynamicAddedRoute = added;
|
||||
},
|
||||
// 设置动态路由
|
||||
setRouters(routers) {
|
||||
this.addRouters = routers
|
||||
this.routers = constantRouter.concat(routers)
|
||||
this.addRouters = routers;
|
||||
this.routers = constantRouter.concat(routers);
|
||||
},
|
||||
setMenus(menus) {
|
||||
// 设置动态路由
|
||||
this.menus = menus
|
||||
this.menus = menus;
|
||||
},
|
||||
setKeepAliveComponents(compNames) {
|
||||
// 设置需要缓存的组件
|
||||
this.keepAliveComponents = compNames
|
||||
this.keepAliveComponents = compNames;
|
||||
},
|
||||
async generateRoutes(data) {
|
||||
let accessedRouters
|
||||
const roleList = data.roles || []
|
||||
let accessedRouters;
|
||||
const roleList = data.roles || [];
|
||||
const routeFilter = (route) => {
|
||||
const { meta } = route;
|
||||
const { permission } = meta || {};
|
||||
if (!permission) return true;
|
||||
return roleList.some((role) => permission.includes(role.value));
|
||||
};
|
||||
const { getPermissionMode } = useProjectSetting()
|
||||
const permissionMode = unref(getPermissionMode)
|
||||
const { getPermissionMode } = useProjectSetting();
|
||||
const permissionMode = unref(getPermissionMode);
|
||||
if (permissionMode === 'BACK') {
|
||||
// 动态获取菜单
|
||||
try {
|
||||
accessedRouters = await generatorDynamicRouter()
|
||||
accessedRouters = await generatorDynamicRouter();
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
//过滤账户是否拥有某一个权限,并将菜单从加载列表移除
|
||||
accessedRouters = filter([...asyncRoutes, ...constantRouter], routeFilter);
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
accessedRouters = accessedRouters.filter(routeFilter);
|
||||
this.setRouters(accessedRouters)
|
||||
this.setMenus(accessedRouters)
|
||||
return toRaw(accessedRouters)
|
||||
}
|
||||
}
|
||||
})
|
||||
this.setRouters(accessedRouters);
|
||||
this.setMenus(accessedRouters);
|
||||
return toRaw(accessedRouters);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Need to be used outside the setup
|
||||
export function useAsyncRouteStoreWidthOut() {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { store } from "@/store"
|
||||
import designSetting from '@/settings/designSetting'
|
||||
import { defineStore } from 'pinia';
|
||||
import { store } from '@/store';
|
||||
import designSetting from '@/settings/designSetting';
|
||||
|
||||
const { darkTheme, appTheme, appThemeList } = designSetting
|
||||
const { darkTheme, appTheme, appThemeList } = designSetting;
|
||||
|
||||
interface DesignSettingState {
|
||||
//深色主题
|
||||
darkTheme: boolean,
|
||||
darkTheme: boolean;
|
||||
//系统风格
|
||||
appTheme: string,
|
||||
appTheme: string;
|
||||
//系统内置风格
|
||||
appThemeList: string[],
|
||||
appThemeList: string[];
|
||||
}
|
||||
|
||||
export const useDesignSettingStore = defineStore({
|
||||
@@ -18,21 +18,21 @@ export const useDesignSettingStore = defineStore({
|
||||
state: (): DesignSettingState => ({
|
||||
darkTheme,
|
||||
appTheme,
|
||||
appThemeList
|
||||
appThemeList,
|
||||
}),
|
||||
getters: {
|
||||
getDarkTheme(): boolean {
|
||||
return this.darkTheme
|
||||
return this.darkTheme;
|
||||
},
|
||||
getAppTheme(): string {
|
||||
return this.appTheme
|
||||
return this.appTheme;
|
||||
},
|
||||
getAppThemeList(): string[] {
|
||||
return this.appThemeList
|
||||
return this.appThemeList;
|
||||
},
|
||||
},
|
||||
actions: {}
|
||||
})
|
||||
actions: {},
|
||||
});
|
||||
|
||||
// Need to be used outside the setup
|
||||
export function useDesignSettingWithOut() {
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
const allModules = import.meta.globEager('./*/index.ts')
|
||||
const modules = {} as any
|
||||
const allModules = import.meta.globEager('./*/index.ts');
|
||||
const modules = {} as any;
|
||||
Object.keys(allModules).forEach((path) => {
|
||||
const fileName = path.split('/')[1]
|
||||
modules[fileName] = allModules[path][fileName] || allModules[path].default || allModules[path]
|
||||
})
|
||||
const fileName = path.split('/')[1];
|
||||
modules[fileName] = allModules[path][fileName] || allModules[path].default || allModules[path];
|
||||
});
|
||||
|
||||
// export default modules
|
||||
import asyncRoute from './async-route'
|
||||
import user from './user'
|
||||
import tabsView from './tabs-view'
|
||||
import lockscreen from './lockscreen'
|
||||
import asyncRoute from './async-route';
|
||||
import user from './user';
|
||||
import tabsView from './tabs-view';
|
||||
import lockscreen from './lockscreen';
|
||||
|
||||
export default {
|
||||
asyncRoute,
|
||||
user,
|
||||
tabsView,
|
||||
lockscreen
|
||||
}
|
||||
lockscreen,
|
||||
};
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { IS_LOCKSCREEN } from '@/store/mutation-types'
|
||||
import { storage } from '@/utils/Storage'
|
||||
import { defineStore } from 'pinia';
|
||||
import { IS_LOCKSCREEN } from '@/store/mutation-types';
|
||||
import { storage } from '@/utils/Storage';
|
||||
|
||||
// 长时间不操作默认锁屏时间
|
||||
const initTime = 60 * 60
|
||||
const initTime = 60 * 60;
|
||||
|
||||
const isLock = storage.get(IS_LOCKSCREEN, false)
|
||||
const isLock = storage.get(IS_LOCKSCREEN, false);
|
||||
|
||||
export type ILockscreenState = {
|
||||
isLock: boolean // 是否锁屏
|
||||
lockTime: number
|
||||
}
|
||||
isLock: boolean; // 是否锁屏
|
||||
lockTime: number;
|
||||
};
|
||||
|
||||
export const useLockscreenStore = defineStore({
|
||||
id: 'app-lockscreen',
|
||||
state: (): ILockscreenState => ({
|
||||
isLock: isLock === true, // 是否锁屏
|
||||
lockTime: isLock == 'true' ? initTime : 0
|
||||
lockTime: isLock == 'true' ? initTime : 0,
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
setLock(payload) {
|
||||
this.isLock = payload
|
||||
storage.set(IS_LOCKSCREEN, this.isLock)
|
||||
this.isLock = payload;
|
||||
storage.set(IS_LOCKSCREEN, this.isLock);
|
||||
},
|
||||
setLockTime(payload = initTime) {
|
||||
this.lockTime = payload
|
||||
}
|
||||
}
|
||||
})
|
||||
this.lockTime = payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { store } from "@/store"
|
||||
import projectSetting from '@/settings/projectSetting'
|
||||
import { defineStore } from 'pinia';
|
||||
import { store } from '@/store';
|
||||
import projectSetting from '@/settings/projectSetting';
|
||||
|
||||
const {
|
||||
navMode,
|
||||
@@ -10,18 +10,18 @@ const {
|
||||
menuSetting,
|
||||
multiTabsSetting,
|
||||
crumbsSetting,
|
||||
permissionMode
|
||||
} = projectSetting
|
||||
permissionMode,
|
||||
} = projectSetting;
|
||||
|
||||
interface ProjectSettingState {
|
||||
navMode: string,//导航模式
|
||||
navTheme: string,//导航风格
|
||||
headerSetting: object,//顶部设置
|
||||
showFooter: boolean, //页脚
|
||||
menuSetting: object, //多标签
|
||||
multiTabsSetting: object,//多标签
|
||||
crumbsSetting: object,//面包屑
|
||||
permissionMode: string//权限模式
|
||||
navMode: string; //导航模式
|
||||
navTheme: string; //导航风格
|
||||
headerSetting: object; //顶部设置
|
||||
showFooter: boolean; //页脚
|
||||
menuSetting: object; //多标签
|
||||
multiTabsSetting: object; //多标签
|
||||
crumbsSetting: object; //面包屑
|
||||
permissionMode: string; //权限模式
|
||||
}
|
||||
|
||||
export const useProjectSettingStore = defineStore({
|
||||
@@ -34,7 +34,7 @@ export const useProjectSettingStore = defineStore({
|
||||
menuSetting,
|
||||
multiTabsSetting,
|
||||
crumbsSetting,
|
||||
permissionMode
|
||||
permissionMode,
|
||||
}),
|
||||
getters: {
|
||||
getNavMode(): string {
|
||||
@@ -44,30 +44,30 @@ export const useProjectSettingStore = defineStore({
|
||||
return this.navTheme;
|
||||
},
|
||||
getHeaderSetting(): object {
|
||||
return this.headerSetting
|
||||
return this.headerSetting;
|
||||
},
|
||||
getShowFooter(): boolean {
|
||||
return this.showFooter
|
||||
return this.showFooter;
|
||||
},
|
||||
getMenuSetting(): object {
|
||||
return this.menuSetting
|
||||
return this.menuSetting;
|
||||
},
|
||||
getMultiTabsSetting(): object {
|
||||
return this.multiTabsSetting
|
||||
return this.multiTabsSetting;
|
||||
},
|
||||
getCrumbsSetting(): object {
|
||||
return this.multiTabsSetting
|
||||
return this.multiTabsSetting;
|
||||
},
|
||||
getPermissionMode(): string {
|
||||
return this.permissionMode
|
||||
}
|
||||
return this.permissionMode;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setNavTheme(value: string): void {
|
||||
this.navTheme = value
|
||||
this.navTheme = value;
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
// Need to be used outside the setup
|
||||
export function useProjectSettingStoreWithOut() {
|
||||
|
||||
@@ -1,62 +1,62 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { RouteLocationNormalized } from 'vue-router'
|
||||
import { TABS_ROUTES } from '../mutation-types'
|
||||
import { defineStore } from 'pinia';
|
||||
import { RouteLocationNormalized } from 'vue-router';
|
||||
import { TABS_ROUTES } from '../mutation-types';
|
||||
|
||||
// 不需要出现在标签页中的路由
|
||||
const whiteList = ['Redirect', 'login']
|
||||
const whiteList = ['Redirect', 'login'];
|
||||
|
||||
export type RouteItem = Partial<RouteLocationNormalized> & {
|
||||
fullPath: string
|
||||
name: string
|
||||
}
|
||||
fullPath: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type ITabsViewState = {
|
||||
tabsList: RouteItem[] // 标签页
|
||||
}
|
||||
tabsList: RouteItem[]; // 标签页
|
||||
};
|
||||
|
||||
export const useTabsViewStore = defineStore({
|
||||
id: 'app-tabs-view',
|
||||
state: (): ITabsViewState => ({
|
||||
tabsList: []
|
||||
tabsList: [],
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
initTabs(routes) {
|
||||
// 初始化标签页
|
||||
this.tabsList = routes
|
||||
this.tabsList = routes;
|
||||
},
|
||||
addTabs(route): boolean {
|
||||
// 添加标签页
|
||||
if (whiteList.includes(route.name)) return false
|
||||
const isExists = this.tabsList.some((item) => item.fullPath == route.fullPath)
|
||||
if (whiteList.includes(route.name)) return false;
|
||||
const isExists = this.tabsList.some((item) => item.fullPath == route.fullPath);
|
||||
if (!isExists) {
|
||||
this.tabsList.push(route)
|
||||
this.tabsList.push(route);
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
},
|
||||
closeLeftTabs(route) {
|
||||
// 关闭左侧
|
||||
const index = this.tabsList.findIndex((item) => item.fullPath == route.fullPath)
|
||||
this.tabsList.splice(0, index)
|
||||
const index = this.tabsList.findIndex((item) => item.fullPath == route.fullPath);
|
||||
this.tabsList.splice(0, index);
|
||||
},
|
||||
closeRightTabs(route) {
|
||||
// 关闭右侧
|
||||
const index = this.tabsList.findIndex((item) => item.fullPath == route.fullPath)
|
||||
this.tabsList.splice(index + 1)
|
||||
const index = this.tabsList.findIndex((item) => item.fullPath == route.fullPath);
|
||||
this.tabsList.splice(index + 1);
|
||||
},
|
||||
closeOtherTabs(route) {
|
||||
// 关闭其他
|
||||
this.tabsList = this.tabsList.filter((item) => item.fullPath == route.fullPath)
|
||||
this.tabsList = this.tabsList.filter((item) => item.fullPath == route.fullPath);
|
||||
},
|
||||
closeCurrentTab(route) {
|
||||
// 关闭当前页
|
||||
const index = this.tabsList.findIndex((item) => item.fullPath == route.fullPath)
|
||||
this.tabsList.splice(index, 1)
|
||||
const index = this.tabsList.findIndex((item) => item.fullPath == route.fullPath);
|
||||
this.tabsList.splice(index, 1);
|
||||
},
|
||||
closeAllTabs() {
|
||||
// 关闭全部
|
||||
this.tabsList = []
|
||||
localStorage.removeItem(TABS_ROUTES)
|
||||
}
|
||||
}
|
||||
})
|
||||
this.tabsList = [];
|
||||
localStorage.removeItem(TABS_ROUTES);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user