mirror of
https://github.com/pure-admin/pure-admin-thin.git
synced 2025-04-25 07:57:18 +08:00
feat: 项目初始化
This commit is contained in:
parent
eae44feb59
commit
c94d2e5737
152
.eslintrc.js
152
.eslintrc.js
@ -5,116 +5,128 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
// Ref sugar (take 2)
|
// Ref sugar (take 2)
|
||||||
$: "readonly",
|
$: 'readonly',
|
||||||
$$: "readonly",
|
$$: 'readonly',
|
||||||
$ref: "readonly",
|
$ref: 'readonly',
|
||||||
$shallowRef: "readonly",
|
$shallowRef: 'readonly',
|
||||||
$computed: "readonly",
|
$computed: 'readonly',
|
||||||
|
|
||||||
// index.d.ts
|
// index.d.ts
|
||||||
// global.d.ts
|
// global.d.ts
|
||||||
Fn: "readonly",
|
Fn: 'readonly',
|
||||||
PromiseFn: "readonly",
|
PromiseFn: 'readonly',
|
||||||
RefType: "readonly",
|
RefType: 'readonly',
|
||||||
LabelValueOptions: "readonly",
|
LabelValueOptions: 'readonly',
|
||||||
EmitType: "readonly",
|
EmitType: 'readonly',
|
||||||
TargetContext: "readonly",
|
TargetContext: 'readonly',
|
||||||
ComponentElRef: "readonly",
|
ComponentElRef: 'readonly',
|
||||||
ComponentRef: "readonly",
|
ComponentRef: 'readonly',
|
||||||
ElRef: "readonly",
|
ElRef: 'readonly',
|
||||||
global: "readonly",
|
global: 'readonly',
|
||||||
ForDataType: "readonly",
|
ForDataType: 'readonly',
|
||||||
ComponentRoutes: "readonly",
|
ComponentRoutes: 'readonly',
|
||||||
|
|
||||||
// script setup
|
// script setup
|
||||||
defineProps: "readonly",
|
defineProps: 'readonly',
|
||||||
defineEmits: "readonly",
|
defineEmits: 'readonly',
|
||||||
defineExpose: "readonly",
|
defineExpose: 'readonly',
|
||||||
withDefaults: "readonly"
|
withDefaults: 'readonly'
|
||||||
},
|
},
|
||||||
extends: [
|
extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/typescript/recommended', '@vue/prettier', '@vue/eslint-config-typescript'],
|
||||||
"plugin:vue/vue3-essential",
|
parser: 'vue-eslint-parser',
|
||||||
"eslint:recommended",
|
|
||||||
"@vue/typescript/recommended",
|
|
||||||
"@vue/prettier",
|
|
||||||
"@vue/eslint-config-typescript"
|
|
||||||
],
|
|
||||||
parser: "vue-eslint-parser",
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: "@typescript-eslint/parser",
|
parser: '@typescript-eslint/parser',
|
||||||
ecmaVersion: 2020,
|
ecmaVersion: 2020,
|
||||||
sourceType: "module",
|
sourceType: 'module',
|
||||||
jsxPragma: "React",
|
jsxPragma: 'React',
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
jsx: true
|
jsx: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ["*.ts", "*.vue"],
|
files: ['*.ts', '*.vue'],
|
||||||
rules: {
|
rules: {
|
||||||
"no-undef": "off"
|
'no-undef': 'off'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ["*.vue"],
|
files: ['*.vue'],
|
||||||
parser: "vue-eslint-parser",
|
parser: 'vue-eslint-parser',
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: "@typescript-eslint/parser",
|
parser: '@typescript-eslint/parser',
|
||||||
extraFileExtensions: [".vue"],
|
extraFileExtensions: ['.vue'],
|
||||||
ecmaVersion: "latest",
|
ecmaVersion: 'latest',
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
jsx: true
|
jsx: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
"no-undef": "off"
|
'no-undef': 'off'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
"vue/no-v-html": "off",
|
'vue/no-v-html': 'off',
|
||||||
"vue/require-default-prop": "off",
|
'vue/require-default-prop': 'off',
|
||||||
"vue/require-explicit-emits": "off",
|
'vue/require-explicit-emits': 'off',
|
||||||
"vue/multi-word-component-names": "off",
|
'vue/multi-word-component-names': 'off',
|
||||||
"@typescript-eslint/no-explicit-any": "off", // any
|
'@typescript-eslint/no-explicit-any': 'off', // any
|
||||||
"no-debugger": "off",
|
'no-debugger': 'off',
|
||||||
"@typescript-eslint/explicit-module-boundary-types": "off", // setup()
|
'@typescript-eslint/explicit-module-boundary-types': 'off', // setup()
|
||||||
"@typescript-eslint/ban-types": "off",
|
'@typescript-eslint/ban-types': 'off',
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
"@typescript-eslint/no-empty-function": "off",
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
"vue/html-self-closing": [
|
quotes: [
|
||||||
"error",
|
2,
|
||||||
|
'single',
|
||||||
|
{
|
||||||
|
avoidEscape: true,
|
||||||
|
allowTemplateLiterals: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
semi: [2, 'never'],
|
||||||
|
'semi-spacing': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
before: false,
|
||||||
|
after: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'vue/html-self-closing': [
|
||||||
|
'error',
|
||||||
{
|
{
|
||||||
html: {
|
html: {
|
||||||
void: "always",
|
void: 'always',
|
||||||
normal: "always",
|
normal: 'always',
|
||||||
component: "always"
|
component: 'always'
|
||||||
},
|
},
|
||||||
svg: "always",
|
svg: 'always',
|
||||||
math: "always"
|
math: 'always'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"@typescript-eslint/no-unused-vars": [
|
'@typescript-eslint/no-unused-vars': [
|
||||||
"error",
|
'error',
|
||||||
{
|
{
|
||||||
argsIgnorePattern: "^_",
|
argsIgnorePattern: '^_',
|
||||||
varsIgnorePattern: "^_"
|
varsIgnorePattern: '^_'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"no-unused-vars": [
|
'no-unused-vars': [
|
||||||
"error",
|
'error',
|
||||||
{
|
{
|
||||||
argsIgnorePattern: "^_",
|
argsIgnorePattern: '^_',
|
||||||
varsIgnorePattern: "^_"
|
varsIgnorePattern: '^_'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"prettier/prettier": [
|
'prettier/prettier': [
|
||||||
"error",
|
'error',
|
||||||
{
|
{
|
||||||
endOfLine: "auto"
|
endOfLine: 'auto',
|
||||||
|
singleQuote: true,
|
||||||
|
semi: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
bracketSpacing: true,
|
// bracketSpacing: true,
|
||||||
singleQuote: false,
|
singleQuote: true,
|
||||||
arrowParens: "avoid",
|
semi: false,
|
||||||
trailingComma: "none"
|
arrowParens: 'avoid',
|
||||||
};
|
trailingComma: 'none'
|
||||||
|
}
|
||||||
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -7,7 +7,7 @@
|
|||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
"editor.formatOnPaste": true,
|
"editor.formatOnPaste": true,
|
||||||
"editor.guides.bracketPairs": "active",
|
"editor.guides.bracketPairs": "active",
|
||||||
"files.autoSave": "afterDelay",
|
"files.autoSave": "onFocusChange",
|
||||||
"git.confirmSync": false,
|
"git.confirmSync": false,
|
||||||
"workbench.startupEditor": "newUntitledFile",
|
"workbench.startupEditor": "newUntitledFile",
|
||||||
"editor.suggestSelection": "first",
|
"editor.suggestSelection": "first",
|
||||||
|
@ -29,6 +29,13 @@ menus:
|
|||||||
hsfourZeroFour: "404"
|
hsfourZeroFour: "404"
|
||||||
hsfourZeroOne: "403"
|
hsfourZeroOne: "403"
|
||||||
hsFive: "500"
|
hsFive: "500"
|
||||||
|
basicData: Basic Data
|
||||||
|
liveData: Live Data
|
||||||
|
userData: User Data
|
||||||
|
searchUser: Search User
|
||||||
|
gameData: Game Data
|
||||||
|
clubData: Club Data
|
||||||
|
searchClub: Search Club
|
||||||
permission: Permission Manage
|
permission: Permission Manage
|
||||||
permissionPage: Page Permission
|
permissionPage: Page Permission
|
||||||
permissionButton: Button Permission
|
permissionButton: Button Permission
|
||||||
@ -41,3 +48,6 @@ login:
|
|||||||
usernameReg: Please enter username
|
usernameReg: Please enter username
|
||||||
passwordReg: Please enter password
|
passwordReg: Please enter password
|
||||||
passwordRuleReg: The password format should be any combination of 8-18 digits
|
passwordRuleReg: The password format should be any combination of 8-18 digits
|
||||||
|
others:
|
||||||
|
backHome: Back Home
|
||||||
|
comingSoon: Coming soon.
|
||||||
|
@ -29,6 +29,13 @@ menus:
|
|||||||
hsfourZeroFour: "404"
|
hsfourZeroFour: "404"
|
||||||
hsfourZeroOne: "403"
|
hsfourZeroOne: "403"
|
||||||
hsFive: "500"
|
hsFive: "500"
|
||||||
|
basicData: "基础数据"
|
||||||
|
liveData: "实时数据"
|
||||||
|
userData: "用户数据"
|
||||||
|
searchUser: "搜索用户"
|
||||||
|
gameData: "牌局数据"
|
||||||
|
clubData: "俱乐部数据"
|
||||||
|
searchClub: "搜索俱乐部"
|
||||||
permission: 权限管理
|
permission: 权限管理
|
||||||
permissionPage: 页面权限
|
permissionPage: 页面权限
|
||||||
permissionButton: 按钮权限
|
permissionButton: 按钮权限
|
||||||
@ -41,3 +48,7 @@ login:
|
|||||||
usernameReg: 请输入账号
|
usernameReg: 请输入账号
|
||||||
passwordReg: 请输入密码
|
passwordReg: 请输入密码
|
||||||
passwordRuleReg: 密码格式应为8-18位数字、字母、符号的任意两种组合
|
passwordRuleReg: 密码格式应为8-18位数字、字母、符号的任意两种组合
|
||||||
|
others:
|
||||||
|
backHome: 返回首页
|
||||||
|
comingSoon: 敬请期待。
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ const permissionRouter = {
|
|||||||
path: "/permission",
|
path: "/permission",
|
||||||
meta: {
|
meta: {
|
||||||
title: "menus.permission",
|
title: "menus.permission",
|
||||||
icon: "lollipop",
|
icon: "informationLine",
|
||||||
rank: 10
|
rank: 10
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
14
src/App.vue
14
src/App.vue
@ -5,19 +5,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from 'vue'
|
||||||
import { ElConfigProvider } from "element-plus";
|
import { ElConfigProvider } from 'element-plus'
|
||||||
import zhCn from "element-plus/lib/locale/lang/zh-cn";
|
import zhCn from 'element-plus/lib/locale/lang/zh-cn'
|
||||||
import en from "element-plus/lib/locale/lang/en";
|
import en from 'element-plus/lib/locale/lang/en'
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "app",
|
name: 'app',
|
||||||
components: {
|
components: {
|
||||||
[ElConfigProvider.name]: ElConfigProvider
|
[ElConfigProvider.name]: ElConfigProvider
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
currentLocale() {
|
currentLocale() {
|
||||||
return this.$storage.locale?.locale === "zh" ? zhCn : en;
|
return this.$storage.locale?.locale === 'zh' ? zhCn : en
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { http } from "@/utils/http";
|
import { http } from '@/utils/http'
|
||||||
|
|
||||||
type Result = {
|
type Result = {
|
||||||
success: boolean;
|
success: boolean
|
||||||
data: Array<any>;
|
data: Array<any>
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getAsyncRoutes = () => {
|
export const getAsyncRoutes = () => {
|
||||||
return http.request<Result>("get", "/getAsyncRoutes");
|
return http.request<Result>('get', '/getAsyncRoutes')
|
||||||
};
|
}
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
import { http } from "@/utils/http";
|
import { http } from '@/utils/http'
|
||||||
|
|
||||||
export type UserResult = {
|
export type UserResult = {
|
||||||
success: boolean;
|
success: boolean
|
||||||
data: {
|
data: {
|
||||||
/** 用户名 */
|
/** 用户名 */
|
||||||
username: string;
|
username: string
|
||||||
/** 当前登陆用户的角色 */
|
/** 当前登陆用户的角色 */
|
||||||
roles: Array<string>;
|
roles: Array<string>
|
||||||
/** `token` */
|
/** `token` */
|
||||||
accessToken: string;
|
accessToken: string
|
||||||
/** 用于调用刷新`accessToken`的接口时所需的`token` */
|
/** 用于调用刷新`accessToken`的接口时所需的`token` */
|
||||||
refreshToken: string;
|
refreshToken: string
|
||||||
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
|
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
|
||||||
expires: Date;
|
expires: Date
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export type RefreshTokenResult = {
|
export type RefreshTokenResult = {
|
||||||
success: boolean;
|
success: boolean
|
||||||
data: {
|
data: {
|
||||||
/** `token` */
|
/** `token` */
|
||||||
accessToken: string;
|
accessToken: string
|
||||||
/** 用于调用刷新`accessToken`的接口时所需的`token` */
|
/** 用于调用刷新`accessToken`的接口时所需的`token` */
|
||||||
refreshToken: string;
|
refreshToken: string
|
||||||
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
|
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
|
||||||
expires: Date;
|
expires: Date
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 登录 */
|
/** 登录 */
|
||||||
export const getLogin = (data?: object) => {
|
export const getLogin = (data?: object) => {
|
||||||
return http.request<UserResult>("post", "/login", { data });
|
return http.request<UserResult>('post', '/login', { data })
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 刷新token */
|
/** 刷新token */
|
||||||
export const refreshTokenApi = (data?: object) => {
|
export const refreshTokenApi = (data?: object) => {
|
||||||
return http.request<RefreshTokenResult>("post", "/refreshToken", { data });
|
return http.request<RefreshTokenResult>('post', '/refreshToken', { data })
|
||||||
};
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 2208059 */
|
font-family: 'iconfont'; /* Project id 2208059 */
|
||||||
src: url("iconfont.woff2?t=1671895108120") format("woff2"),
|
src: url('iconfont.woff2?t=1671895108120') format('woff2'),
|
||||||
url("iconfont.woff?t=1671895108120") format("woff"),
|
url('iconfont.woff?t=1671895108120') format('woff'),
|
||||||
url("iconfont.ttf?t=1671895108120") format("truetype");
|
url('iconfont.ttf?t=1671895108120') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
font-family: "iconfont" !important;
|
font-family: 'iconfont' !important;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
@ -14,13 +14,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pure-iconfont-tabs:before {
|
.pure-iconfont-tabs:before {
|
||||||
content: "\e63e";
|
content: '\e63e';
|
||||||
}
|
}
|
||||||
|
|
||||||
.pure-iconfont-logo:before {
|
.pure-iconfont-logo:before {
|
||||||
content: "\e620";
|
content: '\e620';
|
||||||
}
|
}
|
||||||
|
|
||||||
.pure-iconfont-new:before {
|
.pure-iconfont-new:before {
|
||||||
content: "\e615";
|
content: '\e615';
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
|||||||
import auth from "./src/auth";
|
import auth from './src/auth'
|
||||||
|
|
||||||
const Auth = auth;
|
const Auth = auth
|
||||||
|
|
||||||
export { Auth };
|
export { Auth }
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { defineComponent, Fragment } from "vue";
|
import { defineComponent, Fragment } from 'vue'
|
||||||
import { hasAuth } from "@/router/utils";
|
import { hasAuth } from '@/router/utils'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "Auth",
|
name: 'Auth',
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: undefined,
|
type: undefined,
|
||||||
@ -11,10 +11,10 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
return () => {
|
return () => {
|
||||||
if (!slots) return null;
|
if (!slots) return null
|
||||||
return hasAuth(props.value) ? (
|
return hasAuth(props.value) ? (
|
||||||
<Fragment>{slots.default?.()}</Fragment>
|
<Fragment>{slots.default?.()}</Fragment>
|
||||||
) : null;
|
) : null
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import iconifyIconOffline from "./src/iconifyIconOffline";
|
import iconifyIconOffline from './src/iconifyIconOffline'
|
||||||
import iconifyIconOnline from "./src/iconifyIconOnline";
|
import iconifyIconOnline from './src/iconifyIconOnline'
|
||||||
import fontIcon from "./src/iconfont";
|
import fontIcon from './src/iconfont'
|
||||||
|
|
||||||
/** 本地图标组件 */
|
/** 本地图标组件 */
|
||||||
const IconifyIconOffline = iconifyIconOffline;
|
const IconifyIconOffline = iconifyIconOffline
|
||||||
/** 在线图标组件 */
|
/** 在线图标组件 */
|
||||||
const IconifyIconOnline = iconifyIconOnline;
|
const IconifyIconOnline = iconifyIconOnline
|
||||||
/** iconfont组件 */
|
/** iconfont组件 */
|
||||||
const FontIcon = fontIcon;
|
const FontIcon = fontIcon
|
||||||
|
|
||||||
export { IconifyIconOffline, IconifyIconOnline, FontIcon };
|
export { IconifyIconOffline, IconifyIconOnline, FontIcon }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { iconType } from "./types";
|
import { iconType } from './types'
|
||||||
import { h, defineComponent, Component } from "vue";
|
import { h, defineComponent, Component } from 'vue'
|
||||||
import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index";
|
import { IconifyIconOnline, IconifyIconOffline, FontIcon } from '../index'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支持 `iconfont`、自定义 `svg` 以及 `iconify` 中所有的图标
|
* 支持 `iconfont`、自定义 `svg` 以及 `iconify` 中所有的图标
|
||||||
@ -11,51 +11,51 @@ import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index";
|
|||||||
*/
|
*/
|
||||||
export function useRenderIcon(icon: any, attrs?: iconType): Component {
|
export function useRenderIcon(icon: any, attrs?: iconType): Component {
|
||||||
// iconfont
|
// iconfont
|
||||||
const ifReg = /^IF-/;
|
const ifReg = /^IF-/
|
||||||
// typeof icon === "function" 属于SVG
|
// typeof icon === "function" 属于SVG
|
||||||
if (ifReg.test(icon)) {
|
if (ifReg.test(icon)) {
|
||||||
// iconfont
|
// iconfont
|
||||||
const name = icon.split(ifReg)[1];
|
const name = icon.split(ifReg)[1]
|
||||||
const iconName = name.slice(
|
const iconName = name.slice(
|
||||||
0,
|
0,
|
||||||
name.indexOf(" ") == -1 ? name.length : name.indexOf(" ")
|
name.indexOf(' ') == -1 ? name.length : name.indexOf(' ')
|
||||||
);
|
)
|
||||||
const iconType = name.slice(name.indexOf(" ") + 1, name.length);
|
const iconType = name.slice(name.indexOf(' ') + 1, name.length)
|
||||||
return defineComponent({
|
return defineComponent({
|
||||||
name: "FontIcon",
|
name: 'FontIcon',
|
||||||
render() {
|
render() {
|
||||||
return h(FontIcon, {
|
return h(FontIcon, {
|
||||||
icon: iconName,
|
icon: iconName,
|
||||||
iconType,
|
iconType,
|
||||||
...attrs
|
...attrs
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
} else if (typeof icon === "function" || typeof icon?.render === "function") {
|
} else if (typeof icon === 'function' || typeof icon?.render === 'function') {
|
||||||
// svg
|
// svg
|
||||||
return icon;
|
return icon
|
||||||
} else if (typeof icon === "object") {
|
} else if (typeof icon === 'object') {
|
||||||
return defineComponent({
|
return defineComponent({
|
||||||
name: "OfflineIcon",
|
name: 'OfflineIcon',
|
||||||
render() {
|
render() {
|
||||||
return h(IconifyIconOffline, {
|
return h(IconifyIconOffline, {
|
||||||
icon: icon,
|
icon: icon,
|
||||||
...attrs
|
...attrs
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
// 通过是否存在 : 符号来判断是在线还是本地图标,存在即是在线图标,反之
|
// 通过是否存在 : 符号来判断是在线还是本地图标,存在即是在线图标,反之
|
||||||
return defineComponent({
|
return defineComponent({
|
||||||
name: "Icon",
|
name: 'Icon',
|
||||||
render() {
|
render() {
|
||||||
const IconifyIcon =
|
const IconifyIcon =
|
||||||
icon && icon.includes(":") ? IconifyIconOnline : IconifyIconOffline;
|
icon && icon.includes(':') ? IconifyIconOnline : IconifyIconOffline
|
||||||
return h(IconifyIcon, {
|
return h(IconifyIcon, {
|
||||||
icon: icon,
|
icon: icon,
|
||||||
...attrs
|
...attrs
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,48 @@
|
|||||||
import { h, defineComponent } from "vue";
|
import { h, defineComponent } from 'vue'
|
||||||
|
|
||||||
// 封装iconfont组件,默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 (https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code)
|
// 封装iconfont组件,默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 (https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code)
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "FontIcon",
|
name: 'FontIcon',
|
||||||
props: {
|
props: {
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ""
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const attrs = this.$attrs;
|
const attrs = this.$attrs
|
||||||
if (Object.keys(attrs).includes("uni") || attrs?.iconType === "uni") {
|
if (Object.keys(attrs).includes('uni') || attrs?.iconType === 'uni') {
|
||||||
return h(
|
return h(
|
||||||
"i",
|
'i',
|
||||||
{
|
{
|
||||||
class: "iconfont",
|
class: 'iconfont',
|
||||||
...attrs
|
...attrs
|
||||||
},
|
},
|
||||||
this.icon
|
this.icon
|
||||||
);
|
)
|
||||||
} else if (
|
} else if (
|
||||||
Object.keys(attrs).includes("svg") ||
|
Object.keys(attrs).includes('svg') ||
|
||||||
attrs?.iconType === "svg"
|
attrs?.iconType === 'svg'
|
||||||
) {
|
) {
|
||||||
return h(
|
return h(
|
||||||
"svg",
|
'svg',
|
||||||
{
|
{
|
||||||
class: "icon-svg",
|
class: 'icon-svg',
|
||||||
"aria-hidden": true
|
'aria-hidden': true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
default: () => [
|
default: () => [
|
||||||
h("use", {
|
h('use', {
|
||||||
"xlink:href": `#${this.icon}`
|
'xlink:href': `#${this.icon}`
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
return h("i", {
|
return h('i', {
|
||||||
class: `iconfont ${this.icon}`,
|
class: `iconfont ${this.icon}`,
|
||||||
...attrs
|
...attrs
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { h, defineComponent } from "vue";
|
import { h, defineComponent } from 'vue'
|
||||||
import { Icon as IconifyIcon, addIcon } from "@iconify/vue/dist/offline";
|
import { Icon as IconifyIcon, addIcon } from '@iconify/vue/dist/offline'
|
||||||
|
|
||||||
// Iconify Icon在Vue里本地使用(用于内网环境)https://docs.iconify.design/icon-components/vue/offline.html
|
// Iconify Icon在Vue里本地使用(用于内网环境)https://docs.iconify.design/icon-components/vue/offline.html
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "IconifyIconOffline",
|
name: 'IconifyIconOffline',
|
||||||
components: { IconifyIcon },
|
components: { IconifyIcon },
|
||||||
props: {
|
props: {
|
||||||
icon: {
|
icon: {
|
||||||
@ -11,20 +11,20 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
if (typeof this.icon === "object") addIcon(this.icon, this.icon);
|
if (typeof this.icon === 'object') addIcon(this.icon, this.icon)
|
||||||
const attrs = this.$attrs;
|
const attrs = this.$attrs
|
||||||
return h(
|
return h(
|
||||||
IconifyIcon,
|
IconifyIcon,
|
||||||
{
|
{
|
||||||
icon: this.icon,
|
icon: this.icon,
|
||||||
style: attrs?.style
|
style: attrs?.style
|
||||||
? Object.assign(attrs.style, { outline: "none" })
|
? Object.assign(attrs.style, { outline: 'none' })
|
||||||
: { outline: "none" },
|
: { outline: 'none' },
|
||||||
...attrs
|
...attrs
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
import { h, defineComponent } from "vue";
|
import { h, defineComponent } from 'vue'
|
||||||
import { Icon as IconifyIcon } from "@iconify/vue";
|
import { Icon as IconifyIcon } from '@iconify/vue'
|
||||||
|
|
||||||
// Iconify Icon在Vue里在线使用(用于外网环境)
|
// Iconify Icon在Vue里在线使用(用于外网环境)
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "IconifyIconOnline",
|
name: 'IconifyIconOnline',
|
||||||
components: { IconifyIcon },
|
components: { IconifyIcon },
|
||||||
props: {
|
props: {
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ""
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const attrs = this.$attrs;
|
const attrs = this.$attrs
|
||||||
return h(
|
return h(
|
||||||
IconifyIcon,
|
IconifyIcon,
|
||||||
{
|
{
|
||||||
icon: `${this.icon}`,
|
icon: `${this.icon}`,
|
||||||
style: attrs?.style
|
style: attrs?.style
|
||||||
? Object.assign(attrs.style, { outline: "none" })
|
? Object.assign(attrs.style, { outline: 'none' })
|
||||||
: { outline: "none" },
|
: { outline: 'none' },
|
||||||
...attrs
|
...attrs
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
@ -1,14 +1,26 @@
|
|||||||
import { addIcon } from "@iconify/vue/dist/offline";
|
import { addIcon } from '@iconify/vue/dist/offline'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 这里存放本地图标,在 src/layout/index.vue 文件中加载,避免在首启动加载
|
* 这里存放本地图标,在 src/layout/index.vue 文件中加载,避免在首启动加载
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 本地菜单图标,后端在路由的icon中返回对应的图标字符串并且前端在此处使用addIcon添加即可渲染菜单图标
|
// 本地菜单图标,后端在路由的icon中返回对应的图标字符串并且前端在此处使用addIcon添加即可渲染菜单图标
|
||||||
import HomeFilled from "@iconify-icons/ep/home-filled";
|
import homeSmileLine from '@iconify-icons/ri/home-smile-line'
|
||||||
import InformationLine from "@iconify-icons/ri/information-line";
|
import InformationLine from '@iconify-icons/ri/information-line'
|
||||||
import Lollipop from "@iconify-icons/ep/lollipop";
|
import signalWifiErrorLine from '@iconify-icons/ri/signal-wifi-error-line'
|
||||||
|
import lineChartLine from '@iconify-icons/ri/line-chart-line'
|
||||||
|
import barChart2Line from '@iconify-icons/ri/bar-chart-2-line'
|
||||||
|
import volumeUpLine from '@iconify-icons/ri/volume-up-line'
|
||||||
|
import userFollowLine from '@iconify-icons/ri/user-follow-line'
|
||||||
|
import arrowUpCircleLine from '@iconify-icons/ri/arrow-up-circle-line'
|
||||||
|
import settings4Line from '@iconify-icons/ri/settings-4-line'
|
||||||
|
|
||||||
addIcon("homeFilled", HomeFilled);
|
addIcon('homeSmileLine', homeSmileLine)
|
||||||
addIcon("informationLine", InformationLine);
|
addIcon('informationLine', InformationLine)
|
||||||
addIcon("lollipop", Lollipop);
|
addIcon('signalWifiErrorLine', signalWifiErrorLine)
|
||||||
|
addIcon('lineChartLine', lineChartLine)
|
||||||
|
addIcon('barChart2Line', barChart2Line)
|
||||||
|
addIcon('volumeUpLine', volumeUpLine)
|
||||||
|
addIcon('userFollowLine', userFollowLine)
|
||||||
|
addIcon('arrowUpCircleLine', arrowUpCircleLine)
|
||||||
|
addIcon('settings4Line', settings4Line)
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
export interface iconType {
|
export interface iconType {
|
||||||
// iconify (https://docs.iconify.design/icon-components/vue/#properties)
|
// iconify (https://docs.iconify.design/icon-components/vue/#properties)
|
||||||
inline?: boolean;
|
inline?: boolean
|
||||||
width?: string | number;
|
width?: string | number
|
||||||
height?: string | number;
|
height?: string | number
|
||||||
horizontalFlip?: boolean;
|
horizontalFlip?: boolean
|
||||||
verticalFlip?: boolean;
|
verticalFlip?: boolean
|
||||||
flip?: string;
|
flip?: string
|
||||||
rotate?: number | string;
|
rotate?: number | string
|
||||||
color?: string;
|
color?: string
|
||||||
horizontalAlign?: boolean;
|
horizontalAlign?: boolean
|
||||||
verticalAlign?: boolean;
|
verticalAlign?: boolean
|
||||||
align?: string;
|
align?: string
|
||||||
onLoad?: Function;
|
onLoad?: Function
|
||||||
includes?: Function;
|
includes?: Function
|
||||||
|
|
||||||
// all icon
|
// all icon
|
||||||
style?: object;
|
style?: object
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,52 @@
|
|||||||
import { App } from "vue";
|
import { App } from 'vue'
|
||||||
import axios from "axios";
|
import axios from 'axios'
|
||||||
|
|
||||||
let config: object = {};
|
let config: object = {}
|
||||||
const { VITE_PUBLIC_PATH } = import.meta.env;
|
const { VITE_PUBLIC_PATH } = import.meta.env
|
||||||
|
|
||||||
const setConfig = (cfg?: unknown) => {
|
const setConfig = (cfg?: unknown) => {
|
||||||
config = Object.assign(config, cfg);
|
config = Object.assign(config, cfg)
|
||||||
};
|
}
|
||||||
|
|
||||||
const getConfig = (key?: string): ServerConfigs => {
|
const getConfig = (key?: string): ServerConfigs => {
|
||||||
if (typeof key === "string") {
|
if (typeof key === 'string') {
|
||||||
const arr = key.split(".");
|
const arr = key.split('.')
|
||||||
if (arr && arr.length) {
|
if (arr && arr.length) {
|
||||||
let data = config;
|
let data = config
|
||||||
arr.forEach(v => {
|
arr.forEach(v => {
|
||||||
if (data && typeof data[v] !== "undefined") {
|
if (data && typeof data[v] !== 'undefined') {
|
||||||
data = data[v];
|
data = data[v]
|
||||||
} else {
|
} else {
|
||||||
data = null;
|
data = null
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return data;
|
return data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return config;
|
return config
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 获取项目动态全局配置 */
|
/** 获取项目动态全局配置 */
|
||||||
export const getServerConfig = async (app: App): Promise<undefined> => {
|
export const getServerConfig = async (app: App): Promise<undefined> => {
|
||||||
app.config.globalProperties.$config = getConfig();
|
app.config.globalProperties.$config = getConfig()
|
||||||
return axios({
|
return axios({
|
||||||
method: "get",
|
method: 'get',
|
||||||
url: `${VITE_PUBLIC_PATH}serverConfig.json`
|
url: `${VITE_PUBLIC_PATH}serverConfig.json`
|
||||||
})
|
})
|
||||||
.then(({ data: config }) => {
|
.then(({ data: config }) => {
|
||||||
let $config = app.config.globalProperties.$config;
|
let $config = app.config.globalProperties.$config
|
||||||
// 自动注入项目配置
|
// 自动注入项目配置
|
||||||
if (app && $config && typeof config === "object") {
|
if (app && $config && typeof config === 'object') {
|
||||||
$config = Object.assign($config, config);
|
$config = Object.assign($config, config)
|
||||||
app.config.globalProperties.$config = $config;
|
app.config.globalProperties.$config = $config
|
||||||
// 设置全局配置
|
// 设置全局配置
|
||||||
setConfig($config);
|
setConfig($config)
|
||||||
}
|
}
|
||||||
return $config;
|
return $config
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw "请在public文件夹下添加serverConfig.json配置文件";
|
throw '请在public文件夹下添加serverConfig.json配置文件'
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
export { getConfig, setConfig };
|
export { getConfig, setConfig }
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { hasAuth } from "@/router/utils";
|
import { hasAuth } from '@/router/utils'
|
||||||
import { Directive, type DirectiveBinding } from "vue";
|
import { Directive, type DirectiveBinding } from 'vue'
|
||||||
|
|
||||||
export const auth: Directive = {
|
export const auth: Directive = {
|
||||||
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||||
const { value } = binding;
|
const { value } = binding
|
||||||
if (value) {
|
if (value) {
|
||||||
!hasAuth(value) && el.parentNode?.removeChild(el);
|
!hasAuth(value) && el.parentNode?.removeChild(el)
|
||||||
} else {
|
} else {
|
||||||
throw new Error("need auths! Like v-auth=\"['btn.add','btn.edit']\"");
|
throw new Error("need auths! Like v-auth=\"['btn.add','btn.edit']\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
import { Directive, type DirectiveBinding, type VNode } from "vue";
|
import { Directive, type DirectiveBinding, type VNode } from 'vue'
|
||||||
import elementResizeDetectorMaker from "element-resize-detector";
|
import elementResizeDetectorMaker from 'element-resize-detector'
|
||||||
import type { Erd } from "element-resize-detector";
|
import type { Erd } from 'element-resize-detector'
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from '@/utils/mitt'
|
||||||
|
|
||||||
const erd: Erd = elementResizeDetectorMaker({
|
const erd: Erd = elementResizeDetectorMaker({
|
||||||
strategy: "scroll"
|
strategy: 'scroll'
|
||||||
});
|
})
|
||||||
|
|
||||||
export const resize: Directive = {
|
export const resize: Directive = {
|
||||||
mounted(el: HTMLElement, binding?: DirectiveBinding, vnode?: VNode) {
|
mounted(el: HTMLElement, binding?: DirectiveBinding, vnode?: VNode) {
|
||||||
erd.listenTo(el, elem => {
|
erd.listenTo(el, elem => {
|
||||||
const width = elem.offsetWidth;
|
const width = elem.offsetWidth
|
||||||
const height = elem.offsetHeight;
|
const height = elem.offsetHeight
|
||||||
if (binding?.instance) {
|
if (binding?.instance) {
|
||||||
emitter.emit("resize", { detail: { width, height } });
|
emitter.emit('resize', { detail: { width, height } })
|
||||||
} else {
|
} else {
|
||||||
vnode.el.dispatchEvent(
|
vnode.el.dispatchEvent(
|
||||||
new CustomEvent("resize", { detail: { width, height } })
|
new CustomEvent('resize', { detail: { width, height } })
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
unmounted(el: HTMLElement) {
|
unmounted(el: HTMLElement) {
|
||||||
erd.uninstall(el);
|
erd.uninstall(el)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
export * from "./auth";
|
export * from './auth'
|
||||||
export * from "./elResizeDetector";
|
export * from './elResizeDetector'
|
||||||
|
@ -1,42 +1,42 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useGlobal } from "@pureadmin/utils";
|
import { useGlobal } from '@pureadmin/utils'
|
||||||
import backTop from "@/assets/svg/back_top.svg?component";
|
import backTop from '@/assets/svg/back_top.svg?component'
|
||||||
import { h, computed, Transition, defineComponent } from "vue";
|
import { h, computed, Transition, defineComponent } from 'vue'
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from '@/store/modules/permission'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
fixedHeader: Boolean
|
fixedHeader: Boolean
|
||||||
});
|
})
|
||||||
|
|
||||||
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
|
const { $storage, $config } = useGlobal<GlobalPropertiesApi>()
|
||||||
|
|
||||||
const keepAlive = computed(() => {
|
const keepAlive = computed(() => {
|
||||||
return $config?.KeepAlive;
|
return $config?.KeepAlive
|
||||||
});
|
})
|
||||||
|
|
||||||
const transitions = computed(() => {
|
const transitions = computed(() => {
|
||||||
return route => {
|
return route => {
|
||||||
return route.meta.transition;
|
return route.meta.transition
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const hideTabs = computed(() => {
|
const hideTabs = computed(() => {
|
||||||
return $storage?.configure.hideTabs;
|
return $storage?.configure.hideTabs
|
||||||
});
|
})
|
||||||
|
|
||||||
const layout = computed(() => {
|
const layout = computed(() => {
|
||||||
return $storage?.layout.layout === "vertical";
|
return $storage?.layout.layout === 'vertical'
|
||||||
});
|
})
|
||||||
|
|
||||||
const getSectionStyle = computed(() => {
|
const getSectionStyle = computed(() => {
|
||||||
return [
|
return [
|
||||||
hideTabs.value && layout ? "padding-top: 48px;" : "",
|
hideTabs.value && layout ? 'padding-top: 48px;' : '',
|
||||||
!hideTabs.value && layout ? "padding-top: 85px;" : "",
|
!hideTabs.value && layout ? 'padding-top: 85px;' : '',
|
||||||
hideTabs.value && !layout.value ? "padding-top: 48px" : "",
|
hideTabs.value && !layout.value ? 'padding-top: 48px' : '',
|
||||||
!hideTabs.value && !layout.value ? "padding-top: 85px;" : "",
|
!hideTabs.value && !layout.value ? 'padding-top: 85px;' : '',
|
||||||
props.fixedHeader ? "" : "padding-top: 0;"
|
props.fixedHeader ? '' : 'padding-top: 0;'
|
||||||
];
|
]
|
||||||
});
|
})
|
||||||
|
|
||||||
const transitionMain = defineComponent({
|
const transitionMain = defineComponent({
|
||||||
render() {
|
render() {
|
||||||
@ -46,23 +46,23 @@ const transitionMain = defineComponent({
|
|||||||
name:
|
name:
|
||||||
transitions.value(this.route) &&
|
transitions.value(this.route) &&
|
||||||
this.route.meta.transition.enterTransition
|
this.route.meta.transition.enterTransition
|
||||||
? "pure-classes-transition"
|
? 'pure-classes-transition'
|
||||||
: (transitions.value(this.route) &&
|
: (transitions.value(this.route) &&
|
||||||
this.route.meta.transition.name) ||
|
this.route.meta.transition.name) ||
|
||||||
"fade-transform",
|
'fade-transform',
|
||||||
enterActiveClass:
|
enterActiveClass:
|
||||||
transitions.value(this.route) &&
|
transitions.value(this.route) &&
|
||||||
`animate__animated ${this.route.meta.transition.enterTransition}`,
|
`animate__animated ${this.route.meta.transition.enterTransition}`,
|
||||||
leaveActiveClass:
|
leaveActiveClass:
|
||||||
transitions.value(this.route) &&
|
transitions.value(this.route) &&
|
||||||
`animate__animated ${this.route.meta.transition.leaveTransition}`,
|
`animate__animated ${this.route.meta.transition.leaveTransition}`,
|
||||||
mode: "out-in",
|
mode: 'out-in',
|
||||||
appear: true
|
appear: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
default: () => [this.$slots.default()]
|
default: () => [this.$slots.default()]
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
route: {
|
route: {
|
||||||
@ -70,7 +70,7 @@ const transitionMain = defineComponent({
|
|||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Search from "./search/index.vue";
|
import Search from './search/index.vue'
|
||||||
import Notice from "./notice/index.vue";
|
import Notice from './notice/index.vue'
|
||||||
import mixNav from "./sidebar/mixNav.vue";
|
import mixNav from './sidebar/mixNav.vue'
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from '@/layout/hooks/useNav'
|
||||||
import Breadcrumb from "./sidebar/breadCrumb.vue";
|
import Breadcrumb from './sidebar/breadCrumb.vue'
|
||||||
import topCollapse from "./sidebar/topCollapse.vue";
|
import topCollapse from './sidebar/topCollapse.vue'
|
||||||
import { useTranslationLang } from "../hooks/useTranslationLang";
|
import { useTranslationLang } from '../hooks/useTranslationLang'
|
||||||
import globalization from "@/assets/svg/globalization.svg?component";
|
import globalization from '@/assets/svg/globalization.svg?component'
|
||||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
import LogoutCircleRLine from '@iconify-icons/ri/logout-circle-r-line'
|
||||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
import Setting from '@iconify-icons/ri/settings-3-line'
|
||||||
import Check from "@iconify-icons/ep/check";
|
import Check from '@iconify-icons/ep/check'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
layout,
|
layout,
|
||||||
@ -22,9 +22,9 @@ const {
|
|||||||
toggleSideBar,
|
toggleSideBar,
|
||||||
getDropdownItemStyle,
|
getDropdownItemStyle,
|
||||||
getDropdownItemClass
|
getDropdownItemClass
|
||||||
} = useNav();
|
} = useNav()
|
||||||
|
|
||||||
const { t, locale, translationCh, translationEn } = useTranslationLang();
|
const { t, locale, translationCh, translationEn } = useTranslationLang()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -98,7 +98,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
|
|||||||
:icon="LogoutCircleRLine"
|
:icon="LogoutCircleRLine"
|
||||||
style="margin: 5px"
|
style="margin: 5px"
|
||||||
/>
|
/>
|
||||||
{{ t("buttons.hsLoginOut") }}
|
{{ t('buttons.hsLoginOut') }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,146 +1,146 @@
|
|||||||
export interface ListItem {
|
export interface ListItem {
|
||||||
avatar: string;
|
avatar: string
|
||||||
title: string;
|
title: string
|
||||||
datetime: string;
|
datetime: string
|
||||||
type: string;
|
type: string
|
||||||
description: string;
|
description: string
|
||||||
status?: "" | "success" | "warning" | "info" | "danger";
|
status?: '' | 'success' | 'warning' | 'info' | 'danger'
|
||||||
extra?: string;
|
extra?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TabItem {
|
export interface TabItem {
|
||||||
key: string;
|
key: string
|
||||||
name: string;
|
name: string
|
||||||
list: ListItem[];
|
list: ListItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const noticesData: TabItem[] = [
|
export const noticesData: TabItem[] = [
|
||||||
{
|
{
|
||||||
key: "1",
|
key: '1',
|
||||||
name: "通知",
|
name: '通知',
|
||||||
list: [
|
list: [
|
||||||
{
|
{
|
||||||
avatar:
|
avatar:
|
||||||
"https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
|
||||||
title: "你收到了 12 份新周报",
|
title: '你收到了 12 份新周报',
|
||||||
datetime: "一年前",
|
datetime: '一年前',
|
||||||
description: "",
|
description: '',
|
||||||
type: "1"
|
type: '1'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
avatar:
|
avatar:
|
||||||
"https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
|
'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
|
||||||
title: "你推荐的 前端高手 已通过第三轮面试",
|
title: '你推荐的 前端高手 已通过第三轮面试',
|
||||||
datetime: "一年前",
|
datetime: '一年前',
|
||||||
description: "",
|
description: '',
|
||||||
type: "1"
|
type: '1'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
avatar:
|
avatar:
|
||||||
"https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png",
|
'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
|
||||||
title: "这种模板可以区分多种通知类型",
|
title: '这种模板可以区分多种通知类型',
|
||||||
datetime: "一年前",
|
datetime: '一年前',
|
||||||
description: "",
|
description: '',
|
||||||
type: "1"
|
type: '1'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
avatar:
|
avatar:
|
||||||
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
|
||||||
title:
|
title:
|
||||||
"展示标题内容超过一行后的处理方式,如果内容超过1行将自动截断并支持tooltip显示完整标题。",
|
'展示标题内容超过一行后的处理方式,如果内容超过1行将自动截断并支持tooltip显示完整标题。',
|
||||||
datetime: "一年前",
|
datetime: '一年前',
|
||||||
description: "",
|
description: '',
|
||||||
type: "1"
|
type: '1'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
avatar:
|
avatar:
|
||||||
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
|
||||||
title: "左侧图标用于区分不同的类型",
|
title: '左侧图标用于区分不同的类型',
|
||||||
datetime: "一年前",
|
datetime: '一年前',
|
||||||
description: "",
|
description: '',
|
||||||
type: "1"
|
type: '1'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
avatar:
|
avatar:
|
||||||
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
|
||||||
title: "左侧图标用于区分不同的类型",
|
title: '左侧图标用于区分不同的类型',
|
||||||
datetime: "一年前",
|
datetime: '一年前',
|
||||||
description: "",
|
description: '',
|
||||||
type: "1"
|
type: '1'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "2",
|
key: '2',
|
||||||
name: "消息",
|
name: '消息',
|
||||||
list: [
|
list: [
|
||||||
{
|
{
|
||||||
avatar:
|
avatar:
|
||||||
"https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
|
'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
|
||||||
title: "李白 评论了你",
|
title: '李白 评论了你',
|
||||||
description: "长风破浪会有时,直挂云帆济沧海",
|
description: '长风破浪会有时,直挂云帆济沧海',
|
||||||
datetime: "一年前",
|
datetime: '一年前',
|
||||||
type: "2"
|
type: '2'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
avatar:
|
avatar:
|
||||||
"https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
|
'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
|
||||||
title: "李白 回复了你",
|
title: '李白 回复了你',
|
||||||
description: "行路难,行路难,多歧路,今安在。",
|
description: '行路难,行路难,多歧路,今安在。',
|
||||||
datetime: "一年前",
|
datetime: '一年前',
|
||||||
type: "2"
|
type: '2'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
avatar:
|
avatar:
|
||||||
"https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
|
'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
|
||||||
title: "标题",
|
title: '标题',
|
||||||
description:
|
description:
|
||||||
"请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容",
|
'请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容',
|
||||||
datetime: "一年前",
|
datetime: '一年前',
|
||||||
type: "2"
|
type: '2'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "3",
|
key: '3',
|
||||||
name: "代办",
|
name: '代办',
|
||||||
list: [
|
list: [
|
||||||
{
|
{
|
||||||
avatar: "",
|
avatar: '',
|
||||||
title: "任务名称",
|
title: '任务名称',
|
||||||
description: "任务需要在 2022-11-16 20:00 前启动",
|
description: '任务需要在 2022-11-16 20:00 前启动',
|
||||||
datetime: "",
|
datetime: '',
|
||||||
extra: "未开始",
|
extra: '未开始',
|
||||||
status: "info",
|
status: 'info',
|
||||||
type: "3"
|
type: '3'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
avatar: "",
|
avatar: '',
|
||||||
title: "第三方紧急代码变更",
|
title: '第三方紧急代码变更',
|
||||||
description:
|
description:
|
||||||
"一拳提交于 2022-11-16,需在 2022-11-18 前完成代码变更任务",
|
'一拳提交于 2022-11-16,需在 2022-11-18 前完成代码变更任务',
|
||||||
datetime: "",
|
datetime: '',
|
||||||
extra: "马上到期",
|
extra: '马上到期',
|
||||||
status: "danger",
|
status: 'danger',
|
||||||
type: "3"
|
type: '3'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
avatar: "",
|
avatar: '',
|
||||||
title: "信息安全考试",
|
title: '信息安全考试',
|
||||||
description: "指派小仙于 2022-12-12 前完成更新并发布",
|
description: '指派小仙于 2022-12-12 前完成更新并发布',
|
||||||
datetime: "",
|
datetime: '',
|
||||||
extra: "已耗时 8 天",
|
extra: '已耗时 8 天',
|
||||||
status: "warning",
|
status: 'warning',
|
||||||
type: "3"
|
type: '3'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
avatar: "",
|
avatar: '',
|
||||||
title: "vue-pure-admin 版本发布",
|
title: 'vue-pure-admin 版本发布',
|
||||||
description: "vue-pure-admin 版本发布",
|
description: 'vue-pure-admin 版本发布',
|
||||||
datetime: "",
|
datetime: '',
|
||||||
extra: "进行中",
|
extra: '进行中',
|
||||||
type: "3"
|
type: '3'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
import { noticesData } from "./data";
|
import { noticesData } from './data'
|
||||||
import NoticeList from "./noticeList.vue";
|
import NoticeList from './noticeList.vue'
|
||||||
import Bell from "@iconify-icons/ep/bell";
|
import Bell from '@iconify-icons/ep/bell'
|
||||||
|
|
||||||
const noticesNum = ref(0);
|
const noticesNum = ref(0)
|
||||||
const notices = ref(noticesData);
|
const notices = ref(noticesData)
|
||||||
const activeKey = ref(noticesData[0].key);
|
const activeKey = ref(noticesData[0].key)
|
||||||
|
|
||||||
notices.value.map(v => (noticesNum.value += v.list.length));
|
notices.value.map(v => (noticesNum.value += v.list.length))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,49 +1,49 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ListItem } from "./data";
|
import { ListItem } from './data'
|
||||||
import { ref, PropType, nextTick } from "vue";
|
import { ref, PropType, nextTick } from 'vue'
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from '@/layout/hooks/useNav'
|
||||||
import { deviceDetection } from "@pureadmin/utils";
|
import { deviceDetection } from '@pureadmin/utils'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
noticeItem: {
|
noticeItem: {
|
||||||
type: Object as PropType<ListItem>,
|
type: Object as PropType<ListItem>,
|
||||||
default: () => {}
|
default: () => {}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const titleRef = ref(null);
|
const titleRef = ref(null)
|
||||||
const titleTooltip = ref(false);
|
const titleTooltip = ref(false)
|
||||||
const descriptionRef = ref(null);
|
const descriptionRef = ref(null)
|
||||||
const descriptionTooltip = ref(false);
|
const descriptionTooltip = ref(false)
|
||||||
const { tooltipEffect } = useNav();
|
const { tooltipEffect } = useNav()
|
||||||
const isMobile = deviceDetection();
|
const isMobile = deviceDetection()
|
||||||
|
|
||||||
function hoverTitle() {
|
function hoverTitle() {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
titleRef.value?.scrollWidth > titleRef.value?.clientWidth
|
titleRef.value?.scrollWidth > titleRef.value?.clientWidth
|
||||||
? (titleTooltip.value = true)
|
? (titleTooltip.value = true)
|
||||||
: (titleTooltip.value = false);
|
: (titleTooltip.value = false)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function hoverDescription(event, description) {
|
function hoverDescription(event, description) {
|
||||||
// currentWidth 为文本在页面中所占的宽度,创建标签,加入到页面,获取currentWidth ,最后在移除
|
// currentWidth 为文本在页面中所占的宽度,创建标签,加入到页面,获取currentWidth ,最后在移除
|
||||||
const tempTag = document.createElement("span");
|
const tempTag = document.createElement('span')
|
||||||
tempTag.innerText = description;
|
tempTag.innerText = description
|
||||||
tempTag.className = "getDescriptionWidth";
|
tempTag.className = 'getDescriptionWidth'
|
||||||
document.querySelector("body").appendChild(tempTag);
|
document.querySelector('body').appendChild(tempTag)
|
||||||
const currentWidth = (
|
const currentWidth = (
|
||||||
document.querySelector(".getDescriptionWidth") as HTMLSpanElement
|
document.querySelector('.getDescriptionWidth') as HTMLSpanElement
|
||||||
).offsetWidth;
|
).offsetWidth
|
||||||
document.querySelector(".getDescriptionWidth").remove();
|
document.querySelector('.getDescriptionWidth').remove()
|
||||||
|
|
||||||
// cellWidth为容器的宽度
|
// cellWidth为容器的宽度
|
||||||
const cellWidth = event.target.offsetWidth;
|
const cellWidth = event.target.offsetWidth
|
||||||
|
|
||||||
// 当文本宽度大于容器宽度两倍时,代表文本显示超过两行
|
// 当文本宽度大于容器宽度两倍时,代表文本显示超过两行
|
||||||
currentWidth > 2 * cellWidth
|
currentWidth > 2 * cellWidth
|
||||||
? (descriptionTooltip.value = true)
|
? (descriptionTooltip.value = true)
|
||||||
: (descriptionTooltip.value = false);
|
: (descriptionTooltip.value = false)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PropType } from "vue";
|
import { PropType } from 'vue'
|
||||||
import { ListItem } from "./data";
|
import { ListItem } from './data'
|
||||||
import NoticeItem from "./noticeItem.vue";
|
import NoticeItem from './noticeItem.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
list: {
|
list: {
|
||||||
type: Array as PropType<Array<ListItem>>,
|
type: Array as PropType<Array<ListItem>>,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed } from 'vue'
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from '@/utils/mitt'
|
||||||
import { onClickOutside } from "@vueuse/core";
|
import { onClickOutside } from '@vueuse/core'
|
||||||
import Close from "@iconify-icons/ep/close";
|
import Close from '@iconify-icons/ep/close'
|
||||||
|
|
||||||
const target = ref(null);
|
const target = ref(null)
|
||||||
const show = ref<Boolean>(false);
|
const show = ref<Boolean>(false)
|
||||||
|
|
||||||
const iconClass = computed(() => {
|
const iconClass = computed(() => {
|
||||||
return [
|
return [
|
||||||
"mr-[20px]",
|
'mr-[20px]',
|
||||||
"outline-none",
|
'outline-none',
|
||||||
"width-[20px]",
|
'width-[20px]',
|
||||||
"height-[20px]",
|
'height-[20px]',
|
||||||
"rounded-[4px]",
|
'rounded-[4px]',
|
||||||
"cursor-pointer",
|
'cursor-pointer',
|
||||||
"transition-colors",
|
'transition-colors',
|
||||||
"hover:bg-[#0000000f]",
|
'hover:bg-[#0000000f]',
|
||||||
"dark:hover:bg-[#ffffff1f]",
|
'dark:hover:bg-[#ffffff1f]',
|
||||||
"dark:hover:text-[#ffffffd9]"
|
'dark:hover:text-[#ffffffd9]'
|
||||||
];
|
]
|
||||||
});
|
})
|
||||||
|
|
||||||
onClickOutside(target, (event: any) => {
|
onClickOutside(target, (event: any) => {
|
||||||
if (event.clientX > target.value.offsetLeft) return;
|
if (event.clientX > target.value.offsetLeft) return
|
||||||
show.value = false;
|
show.value = false
|
||||||
});
|
})
|
||||||
|
|
||||||
emitter.on("openPanel", () => {
|
emitter.on('openPanel', () => {
|
||||||
show.value = true;
|
show.value = true
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ArrowUpLine from "@iconify-icons/ri/arrow-up-line";
|
import ArrowUpLine from '@iconify-icons/ri/arrow-up-line'
|
||||||
import ArrowDownLine from "@iconify-icons/ri/arrow-down-line";
|
import ArrowDownLine from '@iconify-icons/ri/arrow-down-line'
|
||||||
import mdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
|
import mdiKeyboardEsc from '@/assets/svg/keyboard_esc.svg?component'
|
||||||
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
|
import enterOutlined from '@/assets/svg/enter_outlined.svg?component'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -1,127 +1,127 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from 'vue-router'
|
||||||
import { cloneDeep } from "@pureadmin/utils";
|
import { cloneDeep } from '@pureadmin/utils'
|
||||||
import SearchResult from "./SearchResult.vue";
|
import SearchResult from './SearchResult.vue'
|
||||||
import SearchFooter from "./SearchFooter.vue";
|
import SearchFooter from './SearchFooter.vue'
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from '@/layout/hooks/useNav'
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
import { transformI18n } from '@/plugins/i18n'
|
||||||
import { ref, computed, shallowRef } from "vue";
|
import { ref, computed, shallowRef } from 'vue'
|
||||||
import { useDebounceFn, onKeyStroke } from "@vueuse/core";
|
import { useDebounceFn, onKeyStroke } from '@vueuse/core'
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from '@/store/modules/permission'
|
||||||
import Search from "@iconify-icons/ep/search";
|
import Search from '@iconify-icons/ep/search'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/** 弹窗显隐 */
|
/** 弹窗显隐 */
|
||||||
value: boolean;
|
value: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: "update:value", val: boolean): void;
|
(e: 'update:value', val: boolean): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const { device } = useNav();
|
const { device } = useNav()
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>()
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {})
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
|
|
||||||
const keyword = ref("");
|
const keyword = ref('')
|
||||||
const activePath = ref("");
|
const activePath = ref('')
|
||||||
const inputRef = ref<HTMLInputElement | null>(null);
|
const inputRef = ref<HTMLInputElement | null>(null)
|
||||||
const resultOptions = shallowRef([]);
|
const resultOptions = shallowRef([])
|
||||||
const handleSearch = useDebounceFn(search, 300);
|
const handleSearch = useDebounceFn(search, 300)
|
||||||
|
|
||||||
/** 菜单树形结构 */
|
/** 菜单树形结构 */
|
||||||
const menusData = computed(() => {
|
const menusData = computed(() => {
|
||||||
return cloneDeep(usePermissionStoreHook().wholeMenus);
|
return cloneDeep(usePermissionStoreHook().wholeMenus)
|
||||||
});
|
})
|
||||||
|
|
||||||
const show = computed({
|
const show = computed({
|
||||||
get() {
|
get() {
|
||||||
return props.value;
|
return props.value
|
||||||
},
|
},
|
||||||
set(val: boolean) {
|
set(val: boolean) {
|
||||||
emit("update:value", val);
|
emit('update:value', val)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
/** 将菜单树形结构扁平化为一维数组,用于菜单查询 */
|
/** 将菜单树形结构扁平化为一维数组,用于菜单查询 */
|
||||||
function flatTree(arr) {
|
function flatTree(arr) {
|
||||||
const res = [];
|
const res = []
|
||||||
function deep(arr) {
|
function deep(arr) {
|
||||||
arr.forEach(item => {
|
arr.forEach(item => {
|
||||||
res.push(item);
|
res.push(item)
|
||||||
item.children && deep(item.children);
|
item.children && deep(item.children)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
deep(arr);
|
deep(arr)
|
||||||
return res;
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 查询 */
|
/** 查询 */
|
||||||
function search() {
|
function search() {
|
||||||
const flatMenusData = flatTree(menusData.value);
|
const flatMenusData = flatTree(menusData.value)
|
||||||
resultOptions.value = flatMenusData.filter(
|
resultOptions.value = flatMenusData.filter(
|
||||||
menu =>
|
menu =>
|
||||||
keyword.value &&
|
keyword.value &&
|
||||||
transformI18n(menu.meta?.title)
|
transformI18n(menu.meta?.title)
|
||||||
.toLocaleLowerCase()
|
.toLocaleLowerCase()
|
||||||
.includes(keyword.value.toLocaleLowerCase().trim())
|
.includes(keyword.value.toLocaleLowerCase().trim())
|
||||||
);
|
)
|
||||||
if (resultOptions.value?.length > 0) {
|
if (resultOptions.value?.length > 0) {
|
||||||
activePath.value = resultOptions.value[0].path;
|
activePath.value = resultOptions.value[0].path
|
||||||
} else {
|
} else {
|
||||||
activePath.value = "";
|
activePath.value = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
show.value = false;
|
show.value = false
|
||||||
/** 延时处理防止用户看到某些操作 */
|
/** 延时处理防止用户看到某些操作 */
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resultOptions.value = [];
|
resultOptions.value = []
|
||||||
keyword.value = "";
|
keyword.value = ''
|
||||||
}, 200);
|
}, 200)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** key up */
|
/** key up */
|
||||||
function handleUp() {
|
function handleUp() {
|
||||||
const { length } = resultOptions.value;
|
const { length } = resultOptions.value
|
||||||
if (length === 0) return;
|
if (length === 0) return
|
||||||
const index = resultOptions.value.findIndex(
|
const index = resultOptions.value.findIndex(
|
||||||
item => item.path === activePath.value
|
item => item.path === activePath.value
|
||||||
);
|
)
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
activePath.value = resultOptions.value[length - 1].path;
|
activePath.value = resultOptions.value[length - 1].path
|
||||||
} else {
|
} else {
|
||||||
activePath.value = resultOptions.value[index - 1].path;
|
activePath.value = resultOptions.value[index - 1].path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** key down */
|
/** key down */
|
||||||
function handleDown() {
|
function handleDown() {
|
||||||
const { length } = resultOptions.value;
|
const { length } = resultOptions.value
|
||||||
if (length === 0) return;
|
if (length === 0) return
|
||||||
const index = resultOptions.value.findIndex(
|
const index = resultOptions.value.findIndex(
|
||||||
item => item.path === activePath.value
|
item => item.path === activePath.value
|
||||||
);
|
)
|
||||||
if (index + 1 === length) {
|
if (index + 1 === length) {
|
||||||
activePath.value = resultOptions.value[0].path;
|
activePath.value = resultOptions.value[0].path
|
||||||
} else {
|
} else {
|
||||||
activePath.value = resultOptions.value[index + 1].path;
|
activePath.value = resultOptions.value[index + 1].path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** key enter */
|
/** key enter */
|
||||||
function handleEnter() {
|
function handleEnter() {
|
||||||
const { length } = resultOptions.value;
|
const { length } = resultOptions.value
|
||||||
if (length === 0 || activePath.value === "") return;
|
if (length === 0 || activePath.value === '') return
|
||||||
router.push(activePath.value);
|
router.push(activePath.value)
|
||||||
handleClose();
|
handleClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeyStroke("Enter", handleEnter);
|
onKeyStroke('Enter', handleEnter)
|
||||||
onKeyStroke("ArrowUp", handleUp);
|
onKeyStroke('ArrowUp', handleUp)
|
||||||
onKeyStroke("ArrowDown", handleDown);
|
onKeyStroke('ArrowDown', handleDown)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,61 +1,61 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from "vue";
|
import { computed } from 'vue'
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
import { useEpThemeStoreHook } from '@/store/modules/epTheme'
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from '@/components/ReIcon/src/hooks'
|
||||||
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
|
import enterOutlined from '@/assets/svg/enter_outlined.svg?component'
|
||||||
import Bookmark2Line from "@iconify-icons/ri/bookmark-2-line";
|
import Bookmark2Line from '@iconify-icons/ri/bookmark-2-line'
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n()
|
||||||
|
|
||||||
interface optionsItem {
|
interface optionsItem {
|
||||||
path: string;
|
path: string
|
||||||
meta?: {
|
meta?: {
|
||||||
icon?: string;
|
icon?: string
|
||||||
title?: string;
|
title?: string
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value: string;
|
value: string
|
||||||
options: Array<optionsItem>;
|
options: Array<optionsItem>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: "update:value", val: string): void;
|
(e: 'update:value', val: string): void
|
||||||
(e: "enter"): void;
|
(e: 'enter'): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {})
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
const itemStyle = computed(() => {
|
const itemStyle = computed(() => {
|
||||||
return item => {
|
return item => {
|
||||||
return {
|
return {
|
||||||
background:
|
background:
|
||||||
item?.path === active.value ? useEpThemeStoreHook().epThemeColor : "",
|
item?.path === active.value ? useEpThemeStoreHook().epThemeColor : '',
|
||||||
color: item.path === active.value ? "#fff" : "",
|
color: item.path === active.value ? '#fff' : '',
|
||||||
fontSize: item.path === active.value ? "16px" : "14px"
|
fontSize: item.path === active.value ? '16px' : '14px'
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const active = computed({
|
const active = computed({
|
||||||
get() {
|
get() {
|
||||||
return props.value;
|
return props.value
|
||||||
},
|
},
|
||||||
set(val: string) {
|
set(val: string) {
|
||||||
emit("update:value", val);
|
emit('update:value', val)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
/** 鼠标移入 */
|
/** 鼠标移入 */
|
||||||
async function handleMouse(item) {
|
async function handleMouse(item) {
|
||||||
active.value = item.path;
|
active.value = item.path
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTo() {
|
function handleTo() {
|
||||||
emit("enter");
|
emit('enter')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import SearchModal from "./SearchModal.vue";
|
import SearchModal from './SearchModal.vue'
|
||||||
|
|
||||||
export { SearchModal };
|
export { SearchModal }
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { SearchModal } from "./components";
|
import { SearchModal } from './components'
|
||||||
import { useBoolean } from "../../hooks/useBoolean";
|
import { useBoolean } from '../../hooks/useBoolean'
|
||||||
import Search from "@iconify-icons/ep/search";
|
import Search from '@iconify-icons/ep/search'
|
||||||
|
|
||||||
const { bool: show, toggle } = useBoolean();
|
const { bool: show, toggle } = useBoolean()
|
||||||
function handleSearch() {
|
function handleSearch() {
|
||||||
toggle();
|
toggle()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -7,40 +7,40 @@ import {
|
|||||||
computed,
|
computed,
|
||||||
nextTick,
|
nextTick,
|
||||||
onBeforeMount
|
onBeforeMount
|
||||||
} from "vue";
|
} from 'vue'
|
||||||
import {
|
import {
|
||||||
useDark,
|
useDark,
|
||||||
debounce,
|
debounce,
|
||||||
useGlobal,
|
useGlobal,
|
||||||
storageLocal,
|
storageLocal,
|
||||||
storageSession
|
storageSession
|
||||||
} from "@pureadmin/utils";
|
} from '@pureadmin/utils'
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from '@/config'
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from 'vue-router'
|
||||||
import panel from "../panel/index.vue";
|
import panel from '../panel/index.vue'
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from '@/utils/mitt'
|
||||||
import { resetRouter } from "@/router";
|
import { resetRouter } from '@/router'
|
||||||
import { removeToken } from "@/utils/auth";
|
import { removeToken } from '@/utils/auth'
|
||||||
import { routerArrays } from "@/layout/types";
|
import { routerArrays } from '@/layout/types'
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from '@/layout/hooks/useNav'
|
||||||
import { useAppStoreHook } from "@/store/modules/app";
|
import { useAppStoreHook } from '@/store/modules/app'
|
||||||
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
|
import { toggleTheme } from '@pureadmin/theme/dist/browser-utils'
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from '@/store/modules/multiTags'
|
||||||
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
|
import { useDataThemeChange } from '@/layout/hooks/useDataThemeChange'
|
||||||
|
|
||||||
import dayIcon from "@/assets/svg/day.svg?component";
|
import dayIcon from '@/assets/svg/day.svg?component'
|
||||||
import darkIcon from "@/assets/svg/dark.svg?component";
|
import darkIcon from '@/assets/svg/dark.svg?component'
|
||||||
import Check from "@iconify-icons/ep/check";
|
import Check from '@iconify-icons/ep/check'
|
||||||
import Logout from "@iconify-icons/ri/logout-circle-r-line";
|
import Logout from '@iconify-icons/ri/logout-circle-r-line'
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const { isDark } = useDark();
|
const { isDark } = useDark()
|
||||||
const { device, tooltipEffect } = useNav();
|
const { device, tooltipEffect } = useNav()
|
||||||
const { $storage } = useGlobal<GlobalPropertiesApi>();
|
const { $storage } = useGlobal<GlobalPropertiesApi>()
|
||||||
|
|
||||||
const mixRef = ref();
|
const mixRef = ref()
|
||||||
const verticalRef = ref();
|
const verticalRef = ref()
|
||||||
const horizontalRef = ref();
|
const horizontalRef = ref()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
dataTheme,
|
dataTheme,
|
||||||
@ -49,22 +49,22 @@ const {
|
|||||||
dataThemeChange,
|
dataThemeChange,
|
||||||
setEpThemeColor,
|
setEpThemeColor,
|
||||||
setLayoutThemeColor
|
setLayoutThemeColor
|
||||||
} = useDataThemeChange();
|
} = useDataThemeChange()
|
||||||
|
|
||||||
/* body添加layout属性,作用于src/style/sidebar.scss */
|
/* body添加layout属性,作用于src/style/sidebar.scss */
|
||||||
if (unref(layoutTheme)) {
|
if (unref(layoutTheme)) {
|
||||||
const layout = unref(layoutTheme).layout;
|
const layout = unref(layoutTheme).layout
|
||||||
const theme = unref(layoutTheme).theme;
|
const theme = unref(layoutTheme).theme
|
||||||
toggleTheme({
|
toggleTheme({
|
||||||
scopeName: `layout-theme-${theme}`
|
scopeName: `layout-theme-${theme}`
|
||||||
});
|
})
|
||||||
setLayoutModel(layout);
|
setLayoutModel(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 默认灵动模式 */
|
/** 默认灵动模式 */
|
||||||
const markValue = ref($storage.configure?.showModel ?? "smart");
|
const markValue = ref($storage.configure?.showModel ?? 'smart')
|
||||||
|
|
||||||
const logoVal = ref($storage.configure?.showLogo ?? true);
|
const logoVal = ref($storage.configure?.showLogo ?? true)
|
||||||
|
|
||||||
const settings = reactive({
|
const settings = reactive({
|
||||||
greyVal: $storage.configure.grey,
|
greyVal: $storage.configure.grey,
|
||||||
@ -73,95 +73,91 @@ const settings = reactive({
|
|||||||
showLogo: $storage.configure.showLogo,
|
showLogo: $storage.configure.showLogo,
|
||||||
showModel: $storage.configure.showModel,
|
showModel: $storage.configure.showModel,
|
||||||
multiTagsCache: $storage.configure.multiTagsCache
|
multiTagsCache: $storage.configure.multiTagsCache
|
||||||
});
|
})
|
||||||
|
|
||||||
const getThemeColorStyle = computed(() => {
|
const getThemeColorStyle = computed(() => {
|
||||||
return color => {
|
return color => {
|
||||||
return { background: color };
|
return { background: color }
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
/** 当网页为暗黑模式时不显示亮白色切换选项 */
|
/** 当网页为暗黑模式时不显示亮白色切换选项 */
|
||||||
const showThemeColors = computed(() => {
|
const showThemeColors = computed(() => {
|
||||||
return themeColor => {
|
return themeColor => {
|
||||||
return themeColor === "light" && isDark.value ? false : true;
|
return themeColor === 'light' && isDark.value ? false : true
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
function storageConfigureChange<T>(key: string, val: T): void {
|
function storageConfigureChange<T>(key: string, val: T): void {
|
||||||
const storageConfigure = $storage.configure;
|
const storageConfigure = $storage.configure
|
||||||
storageConfigure[key] = val;
|
storageConfigure[key] = val
|
||||||
$storage.configure = storageConfigure;
|
$storage.configure = storageConfigure
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
|
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
|
||||||
const targetEl = target || document.body;
|
const targetEl = target || document.body
|
||||||
let { className } = targetEl;
|
let { className } = targetEl
|
||||||
className = className.replace(clsName, "").trim();
|
className = className.replace(clsName, '').trim()
|
||||||
targetEl.className = flag ? `${className} ${clsName} ` : className;
|
targetEl.className = flag ? `${className} ${clsName} ` : className
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 灰色模式设置 */
|
/** 灰色模式设置 */
|
||||||
const greyChange = (value): void => {
|
const greyChange = (value): void => {
|
||||||
toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
|
toggleClass(settings.greyVal, 'html-grey', document.querySelector('html'))
|
||||||
storageConfigureChange("grey", value);
|
storageConfigureChange('grey', value)
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 色弱模式设置 */
|
/** 色弱模式设置 */
|
||||||
const weekChange = (value): void => {
|
const weekChange = (value): void => {
|
||||||
toggleClass(
|
toggleClass(settings.weakVal, 'html-weakness', document.querySelector('html'))
|
||||||
settings.weakVal,
|
storageConfigureChange('weak', value)
|
||||||
"html-weakness",
|
}
|
||||||
document.querySelector("html")
|
|
||||||
);
|
|
||||||
storageConfigureChange("weak", value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const tagsChange = () => {
|
const tagsChange = () => {
|
||||||
const showVal = settings.tabsVal;
|
const showVal = settings.tabsVal
|
||||||
storageConfigureChange("hideTabs", showVal);
|
storageConfigureChange('hideTabs', showVal)
|
||||||
emitter.emit("tagViewsChange", showVal as unknown as string);
|
emitter.emit('tagViewsChange', showVal as unknown as string)
|
||||||
};
|
}
|
||||||
|
|
||||||
const multiTagsCacheChange = () => {
|
const multiTagsCacheChange = () => {
|
||||||
const multiTagsCache = settings.multiTagsCache;
|
const multiTagsCache = settings.multiTagsCache
|
||||||
storageConfigureChange("multiTagsCache", multiTagsCache);
|
storageConfigureChange('multiTagsCache', multiTagsCache)
|
||||||
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
|
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache)
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 清空缓存并返回登录页 */
|
/** 清空缓存并返回登录页 */
|
||||||
function onReset() {
|
function onReset() {
|
||||||
removeToken();
|
removeToken()
|
||||||
storageLocal().clear();
|
storageLocal().clear()
|
||||||
storageSession().clear();
|
storageSession().clear()
|
||||||
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
|
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig()
|
||||||
useAppStoreHook().setLayout(Layout);
|
useAppStoreHook().setLayout(Layout)
|
||||||
setEpThemeColor(EpThemeColor);
|
setEpThemeColor(EpThemeColor)
|
||||||
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
|
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache)
|
||||||
toggleClass(Grey, "html-grey", document.querySelector("html"));
|
toggleClass(Grey, 'html-grey', document.querySelector('html'))
|
||||||
toggleClass(Weak, "html-weakness", document.querySelector("html"));
|
toggleClass(Weak, 'html-weakness', document.querySelector('html'))
|
||||||
router.push("/login");
|
router.push('/login')
|
||||||
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
|
useMultiTagsStoreHook().handleTags('equal', [...routerArrays])
|
||||||
resetRouter();
|
resetRouter()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(label) {
|
function onChange(label) {
|
||||||
storageConfigureChange("showModel", label);
|
storageConfigureChange('showModel', label)
|
||||||
emitter.emit("tagViewsShowModel", label);
|
emitter.emit('tagViewsShowModel', label)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 侧边栏Logo */
|
/** 侧边栏Logo */
|
||||||
function logoChange() {
|
function logoChange() {
|
||||||
unref(logoVal)
|
unref(logoVal)
|
||||||
? storageConfigureChange("showLogo", true)
|
? storageConfigureChange('showLogo', true)
|
||||||
: storageConfigureChange("showLogo", false);
|
: storageConfigureChange('showLogo', false)
|
||||||
emitter.emit("logoChange", unref(logoVal));
|
emitter.emit('logoChange', unref(logoVal))
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFalse(Doms): any {
|
function setFalse(Doms): any {
|
||||||
Doms.forEach(v => {
|
Doms.forEach(v => {
|
||||||
toggleClass(false, "is-select", unref(v));
|
toggleClass(false, 'is-select', unref(v))
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 主题色 激活选择项 */
|
/** 主题色 激活选择项 */
|
||||||
@ -169,65 +165,65 @@ const getThemeColor = computed(() => {
|
|||||||
return current => {
|
return current => {
|
||||||
if (
|
if (
|
||||||
current === layoutTheme.value.theme &&
|
current === layoutTheme.value.theme &&
|
||||||
layoutTheme.value.theme !== "light"
|
layoutTheme.value.theme !== 'light'
|
||||||
) {
|
) {
|
||||||
return "#fff";
|
return '#fff'
|
||||||
} else if (
|
} else if (
|
||||||
current === layoutTheme.value.theme &&
|
current === layoutTheme.value.theme &&
|
||||||
layoutTheme.value.theme === "light"
|
layoutTheme.value.theme === 'light'
|
||||||
) {
|
) {
|
||||||
return "#1d2b45";
|
return '#1d2b45'
|
||||||
} else {
|
} else {
|
||||||
return "transparent";
|
return 'transparent'
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
/** 设置导航模式 */
|
/** 设置导航模式 */
|
||||||
function setLayoutModel(layout: string) {
|
function setLayoutModel(layout: string) {
|
||||||
layoutTheme.value.layout = layout;
|
layoutTheme.value.layout = layout
|
||||||
window.document.body.setAttribute("layout", layout);
|
window.document.body.setAttribute('layout', layout)
|
||||||
$storage.layout = {
|
$storage.layout = {
|
||||||
layout,
|
layout,
|
||||||
theme: layoutTheme.value.theme,
|
theme: layoutTheme.value.theme,
|
||||||
darkMode: $storage.layout?.darkMode,
|
darkMode: $storage.layout?.darkMode,
|
||||||
sidebarStatus: $storage.layout?.sidebarStatus,
|
sidebarStatus: $storage.layout?.sidebarStatus,
|
||||||
epThemeColor: $storage.layout?.epThemeColor
|
epThemeColor: $storage.layout?.epThemeColor
|
||||||
};
|
}
|
||||||
useAppStoreHook().setLayout(layout);
|
useAppStoreHook().setLayout(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
watch($storage, ({ layout }) => {
|
watch($storage, ({ layout }) => {
|
||||||
switch (layout["layout"]) {
|
switch (layout['layout']) {
|
||||||
case "vertical":
|
case 'vertical':
|
||||||
toggleClass(true, "is-select", unref(verticalRef));
|
toggleClass(true, 'is-select', unref(verticalRef))
|
||||||
debounce(setFalse([horizontalRef]), 50);
|
debounce(setFalse([horizontalRef]), 50)
|
||||||
debounce(setFalse([mixRef]), 50);
|
debounce(setFalse([mixRef]), 50)
|
||||||
break;
|
break
|
||||||
case "horizontal":
|
case 'horizontal':
|
||||||
toggleClass(true, "is-select", unref(horizontalRef));
|
toggleClass(true, 'is-select', unref(horizontalRef))
|
||||||
debounce(setFalse([verticalRef]), 50);
|
debounce(setFalse([verticalRef]), 50)
|
||||||
debounce(setFalse([mixRef]), 50);
|
debounce(setFalse([mixRef]), 50)
|
||||||
break;
|
break
|
||||||
case "mix":
|
case 'mix':
|
||||||
toggleClass(true, "is-select", unref(mixRef));
|
toggleClass(true, 'is-select', unref(mixRef))
|
||||||
debounce(setFalse([verticalRef]), 50);
|
debounce(setFalse([verticalRef]), 50)
|
||||||
debounce(setFalse([horizontalRef]), 50);
|
debounce(setFalse([horizontalRef]), 50)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
dataThemeChange();
|
dataThemeChange()
|
||||||
/* 初始化项目配置 */
|
/* 初始化项目配置 */
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
settings.greyVal &&
|
settings.greyVal &&
|
||||||
document.querySelector("html")?.setAttribute("class", "html-grey");
|
document.querySelector('html')?.setAttribute('class', 'html-grey')
|
||||||
settings.weakVal &&
|
settings.weakVal &&
|
||||||
document.querySelector("html")?.setAttribute("class", "html-weakness");
|
document.querySelector('html')?.setAttribute('class', 'html-weakness')
|
||||||
settings.tabsVal && tagsChange();
|
settings.tabsVal && tagsChange()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,98 +1,98 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { isEqual } from "@pureadmin/utils";
|
import { isEqual } from '@pureadmin/utils'
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
import { transformI18n } from '@/plugins/i18n'
|
||||||
import { ref, watch, onMounted, toRaw } from "vue";
|
import { ref, watch, onMounted, toRaw } from 'vue'
|
||||||
import { getParentPaths, findRouteByPath } from "@/router/utils";
|
import { getParentPaths, findRouteByPath } from '@/router/utils'
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from '@/store/modules/multiTags'
|
||||||
import { useRoute, useRouter, RouteLocationMatched } from "vue-router";
|
import { useRoute, useRouter, RouteLocationMatched } from 'vue-router'
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const levelList = ref([]);
|
const levelList = ref([])
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const routes: any = router.options.routes;
|
const routes: any = router.options.routes
|
||||||
const multiTags: any = useMultiTagsStoreHook().multiTags;
|
const multiTags: any = useMultiTagsStoreHook().multiTags
|
||||||
|
|
||||||
const isDashboard = (route: RouteLocationMatched): boolean | string => {
|
const isDashboard = (route: RouteLocationMatched): boolean | string => {
|
||||||
const name = route && (route.name as string);
|
const name = route && (route.name as string)
|
||||||
if (!name) return false;
|
if (!name) return false
|
||||||
return name.trim().toLocaleLowerCase() === "Welcome".toLocaleLowerCase();
|
return name.trim().toLocaleLowerCase() === 'Welcome'.toLocaleLowerCase()
|
||||||
};
|
}
|
||||||
|
|
||||||
const getBreadcrumb = (): void => {
|
const getBreadcrumb = (): void => {
|
||||||
// 当前路由信息
|
// 当前路由信息
|
||||||
let currentRoute;
|
let currentRoute
|
||||||
|
|
||||||
if (Object.keys(route.query).length > 0) {
|
if (Object.keys(route.query).length > 0) {
|
||||||
multiTags.forEach(item => {
|
multiTags.forEach(item => {
|
||||||
if (isEqual(route.query, item?.query)) {
|
if (isEqual(route.query, item?.query)) {
|
||||||
currentRoute = toRaw(item);
|
currentRoute = toRaw(item)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
} else if (Object.keys(route.params).length > 0) {
|
} else if (Object.keys(route.params).length > 0) {
|
||||||
multiTags.forEach(item => {
|
multiTags.forEach(item => {
|
||||||
if (isEqual(route.params, item?.params)) {
|
if (isEqual(route.params, item?.params)) {
|
||||||
currentRoute = toRaw(item);
|
currentRoute = toRaw(item)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
currentRoute = findRouteByPath(router.currentRoute.value.path, multiTags);
|
currentRoute = findRouteByPath(router.currentRoute.value.path, multiTags)
|
||||||
}
|
}
|
||||||
// 当前路由的父级路径组成的数组
|
// 当前路由的父级路径组成的数组
|
||||||
const parentRoutes = getParentPaths(router.currentRoute.value.path, routes);
|
const parentRoutes = getParentPaths(router.currentRoute.value.path, routes)
|
||||||
// 存放组成面包屑的数组
|
// 存放组成面包屑的数组
|
||||||
let matched = [];
|
let matched = []
|
||||||
// 获取每个父级路径对应的路由信息
|
// 获取每个父级路径对应的路由信息
|
||||||
parentRoutes.forEach(path => {
|
parentRoutes.forEach(path => {
|
||||||
if (path !== "/") matched.push(findRouteByPath(path, routes));
|
if (path !== '/') matched.push(findRouteByPath(path, routes))
|
||||||
});
|
})
|
||||||
|
|
||||||
if (currentRoute?.path !== "/welcome") matched.push(currentRoute);
|
if (currentRoute?.path !== '/welcome') matched.push(currentRoute)
|
||||||
|
|
||||||
if (!isDashboard(matched[0])) {
|
if (!isDashboard(matched[0])) {
|
||||||
matched = [
|
matched = [
|
||||||
{
|
{
|
||||||
path: "/welcome",
|
path: '/welcome',
|
||||||
parentPath: "/",
|
parentPath: '/',
|
||||||
meta: { title: "menus.hshome" }
|
meta: { title: 'menus.hshome' }
|
||||||
} as unknown as RouteLocationMatched
|
} as unknown as RouteLocationMatched
|
||||||
].concat(matched);
|
].concat(matched)
|
||||||
}
|
}
|
||||||
|
|
||||||
matched.forEach((item, index) => {
|
matched.forEach((item, index) => {
|
||||||
if (currentRoute?.query || currentRoute?.params) return;
|
if (currentRoute?.query || currentRoute?.params) return
|
||||||
if (item?.children) {
|
if (item?.children) {
|
||||||
item.children.forEach(v => {
|
item.children.forEach(v => {
|
||||||
if (v?.meta?.title === item?.meta?.title) {
|
if (v?.meta?.title === item?.meta?.title) {
|
||||||
matched.splice(index, 1);
|
matched.splice(index, 1)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
levelList.value = matched.filter(
|
levelList.value = matched.filter(
|
||||||
item => item?.meta && item?.meta.title !== false
|
item => item?.meta && item?.meta.title !== false
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleLink = (item: RouteLocationMatched): void => {
|
const handleLink = (item: RouteLocationMatched): void => {
|
||||||
const { redirect, path } = item;
|
const { redirect, path } = item
|
||||||
if (redirect) {
|
if (redirect) {
|
||||||
router.push(redirect as any);
|
router.push(redirect as any)
|
||||||
} else {
|
} else {
|
||||||
router.push(path);
|
router.push(path)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getBreadcrumb();
|
getBreadcrumb()
|
||||||
});
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
() => {
|
() => {
|
||||||
getBreadcrumb();
|
getBreadcrumb()
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { toRaw } from "vue";
|
import { toRaw } from 'vue'
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from '@/components/ReIcon/src/hooks'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
extraIcon: {
|
extraIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ""
|
default: ''
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Search from "../search/index.vue";
|
import Search from '../search/index.vue'
|
||||||
import Notice from "../notice/index.vue";
|
import Notice from '../notice/index.vue'
|
||||||
import { ref, watch, nextTick } from "vue";
|
import { ref, watch, nextTick } from 'vue'
|
||||||
import SidebarItem from "./sidebarItem.vue";
|
import SidebarItem from './sidebarItem.vue'
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from '@/layout/hooks/useNav'
|
||||||
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
import { useTranslationLang } from '../../hooks/useTranslationLang'
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from '@/store/modules/permission'
|
||||||
import globalization from "@/assets/svg/globalization.svg?component";
|
import globalization from '@/assets/svg/globalization.svg?component'
|
||||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
import LogoutCircleRLine from '@iconify-icons/ri/logout-circle-r-line'
|
||||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
import Setting from '@iconify-icons/ri/settings-3-line'
|
||||||
import Check from "@iconify-icons/ep/check";
|
import Check from '@iconify-icons/ep/check'
|
||||||
|
|
||||||
const menuRef = ref();
|
const menuRef = ref()
|
||||||
|
|
||||||
const { t, route, locale, translationCh, translationEn } =
|
const { t, route, locale, translationCh, translationEn } =
|
||||||
useTranslationLang(menuRef);
|
useTranslationLang(menuRef)
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
routers,
|
routers,
|
||||||
@ -26,18 +26,18 @@ const {
|
|||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
getDropdownItemStyle,
|
getDropdownItemStyle,
|
||||||
getDropdownItemClass
|
getDropdownItemClass
|
||||||
} = useNav();
|
} = useNav()
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
menuRef.value?.handleResize();
|
menuRef.value?.handleResize()
|
||||||
});
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
() => {
|
() => {
|
||||||
menuSelect(route.path, routers);
|
menuSelect(route.path, routers)
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -115,7 +115,7 @@ watch(
|
|||||||
:icon="LogoutCircleRLine"
|
:icon="LogoutCircleRLine"
|
||||||
style="margin: 5px"
|
style="margin: 5px"
|
||||||
/>
|
/>
|
||||||
{{ t("buttons.hsLoginOut") }}
|
{{ t('buttons.hsLoginOut') }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,42 +1,42 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed } from 'vue'
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from '@/layout/hooks/useNav'
|
||||||
import MenuFold from "@iconify-icons/ri/menu-fold-fill";
|
import MenuFold from '@iconify-icons/ri/menu-fold-fill'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isActive: boolean;
|
isActive: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
isActive: false
|
isActive: false
|
||||||
});
|
})
|
||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false)
|
||||||
const { tooltipEffect } = useNav();
|
const { tooltipEffect } = useNav()
|
||||||
|
|
||||||
const iconClass = computed(() => {
|
const iconClass = computed(() => {
|
||||||
return [
|
return [
|
||||||
"ml-4",
|
'ml-4',
|
||||||
"mb-1",
|
'mb-1',
|
||||||
"w-[16px]",
|
'w-[16px]',
|
||||||
"h-[16px]",
|
'h-[16px]',
|
||||||
"inline-block",
|
'inline-block',
|
||||||
"align-middle",
|
'align-middle',
|
||||||
"text-primary",
|
'text-primary',
|
||||||
"cursor-pointer",
|
'cursor-pointer',
|
||||||
"duration-[100ms]",
|
'duration-[100ms]',
|
||||||
"hover:text-primary",
|
'hover:text-primary',
|
||||||
"dark:hover:!text-white"
|
'dark:hover:!text-white'
|
||||||
];
|
]
|
||||||
});
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "toggleClick"): void;
|
(e: 'toggleClick'): void
|
||||||
}>();
|
}>()
|
||||||
|
|
||||||
const toggleClick = () => {
|
const toggleClick = () => {
|
||||||
emit("toggleClick");
|
emit('toggleClick')
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from '@/layout/hooks/useNav'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
collapse: Boolean
|
collapse: Boolean
|
||||||
});
|
})
|
||||||
|
|
||||||
const { title } = useNav();
|
const { title } = useNav()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import extraIcon from "./extraIcon.vue";
|
import extraIcon from './extraIcon.vue'
|
||||||
import Search from "../search/index.vue";
|
import Search from '../search/index.vue'
|
||||||
import Notice from "../notice/index.vue";
|
import Notice from '../notice/index.vue'
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from '@/layout/hooks/useNav'
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
import { transformI18n } from '@/plugins/i18n'
|
||||||
import { ref, toRaw, watch, onMounted, nextTick } from "vue";
|
import { ref, toRaw, watch, onMounted, nextTick } from 'vue'
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from '@/components/ReIcon/src/hooks'
|
||||||
import { getParentPaths, findRouteByPath } from "@/router/utils";
|
import { getParentPaths, findRouteByPath } from '@/router/utils'
|
||||||
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
import { useTranslationLang } from '../../hooks/useTranslationLang'
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from '@/store/modules/permission'
|
||||||
import globalization from "@/assets/svg/globalization.svg?component";
|
import globalization from '@/assets/svg/globalization.svg?component'
|
||||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
import LogoutCircleRLine from '@iconify-icons/ri/logout-circle-r-line'
|
||||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
import Setting from '@iconify-icons/ri/settings-3-line'
|
||||||
import Check from "@iconify-icons/ep/check";
|
import Check from '@iconify-icons/ep/check'
|
||||||
|
|
||||||
const menuRef = ref();
|
const menuRef = ref()
|
||||||
const defaultActive = ref(null);
|
const defaultActive = ref(null)
|
||||||
|
|
||||||
const { t, route, locale, translationCh, translationEn } =
|
const { t, route, locale, translationCh, translationEn } =
|
||||||
useTranslationLang(menuRef);
|
useTranslationLang(menuRef)
|
||||||
const {
|
const {
|
||||||
device,
|
device,
|
||||||
routers,
|
routers,
|
||||||
@ -31,32 +31,32 @@ const {
|
|||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
getDropdownItemStyle,
|
getDropdownItemStyle,
|
||||||
getDropdownItemClass
|
getDropdownItemClass
|
||||||
} = useNav();
|
} = useNav()
|
||||||
|
|
||||||
function getDefaultActive(routePath) {
|
function getDefaultActive(routePath) {
|
||||||
const wholeMenus = usePermissionStoreHook().wholeMenus;
|
const wholeMenus = usePermissionStoreHook().wholeMenus
|
||||||
/** 当前路由的父级路径 */
|
/** 当前路由的父级路径 */
|
||||||
const parentRoutes = getParentPaths(routePath, wholeMenus)[0];
|
const parentRoutes = getParentPaths(routePath, wholeMenus)[0]
|
||||||
defaultActive.value = findRouteByPath(
|
defaultActive.value = findRouteByPath(
|
||||||
parentRoutes,
|
parentRoutes,
|
||||||
wholeMenus
|
wholeMenus
|
||||||
)?.children[0]?.path;
|
)?.children[0]?.path
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDefaultActive(route.path);
|
getDefaultActive(route.path)
|
||||||
});
|
})
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
menuRef.value?.handleResize();
|
menuRef.value?.handleResize()
|
||||||
});
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [route.path, usePermissionStoreHook().wholeMenus],
|
() => [route.path, usePermissionStoreHook().wholeMenus],
|
||||||
() => {
|
() => {
|
||||||
getDefaultActive(route.path);
|
getDefaultActive(route.path)
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -147,7 +147,7 @@ watch(
|
|||||||
:icon="LogoutCircleRLine"
|
:icon="LogoutCircleRLine"
|
||||||
style="margin: 5px"
|
style="margin: 5px"
|
||||||
/>
|
/>
|
||||||
{{ t("buttons.hsLoginOut") }}
|
{{ t('buttons.hsLoginOut') }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import path from "path";
|
import path from 'path'
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from '@/config'
|
||||||
import extraIcon from "./extraIcon.vue";
|
import extraIcon from './extraIcon.vue'
|
||||||
import { childrenType } from "../../types";
|
import { childrenType } from '../../types'
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from '@/layout/hooks/useNav'
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
import { transformI18n } from '@/plugins/i18n'
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from '@/components/ReIcon/src/hooks'
|
||||||
import { ref, toRaw, PropType, nextTick, computed, CSSProperties } from "vue";
|
import { ref, toRaw, PropType, nextTick, computed, CSSProperties } from 'vue'
|
||||||
|
|
||||||
import ArrowUp from "@iconify-icons/ep/arrow-up-bold";
|
import ArrowUp from '@iconify-icons/ep/arrow-up-bold'
|
||||||
import EpArrowDown from "@iconify-icons/ep/arrow-down-bold";
|
import EpArrowDown from '@iconify-icons/ep/arrow-down-bold'
|
||||||
import ArrowLeft from "@iconify-icons/ep/arrow-left-bold";
|
import ArrowLeft from '@iconify-icons/ep/arrow-left-bold'
|
||||||
import ArrowRight from "@iconify-icons/ep/arrow-right-bold";
|
import ArrowRight from '@iconify-icons/ep/arrow-right-bold'
|
||||||
|
|
||||||
const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
|
const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
item: {
|
item: {
|
||||||
@ -25,102 +25,102 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
basePath: {
|
basePath: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ""
|
default: ''
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const getSpanStyle = computed((): CSSProperties => {
|
const getSpanStyle = computed((): CSSProperties => {
|
||||||
return {
|
return {
|
||||||
width: "100%",
|
width: '100%',
|
||||||
textAlign: "center"
|
textAlign: 'center'
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const getNoDropdownStyle = computed((): CSSProperties => {
|
const getNoDropdownStyle = computed((): CSSProperties => {
|
||||||
return {
|
return {
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center"
|
alignItems: 'center'
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const getMenuTextStyle = computed(() => {
|
const getMenuTextStyle = computed(() => {
|
||||||
return {
|
return {
|
||||||
overflow: "hidden",
|
overflow: 'hidden',
|
||||||
textOverflow: "ellipsis",
|
textOverflow: 'ellipsis',
|
||||||
outline: "none"
|
outline: 'none'
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const getsubMenuIconStyle = computed((): CSSProperties => {
|
const getsubMenuIconStyle = computed((): CSSProperties => {
|
||||||
return {
|
return {
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
margin:
|
margin:
|
||||||
layout.value === "horizontal"
|
layout.value === 'horizontal'
|
||||||
? "0 5px 0 0"
|
? '0 5px 0 0'
|
||||||
: isCollapse.value
|
: isCollapse.value
|
||||||
? "0 auto"
|
? '0 auto'
|
||||||
: "0 5px 0 0"
|
: '0 5px 0 0'
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const getSubTextStyle = computed((): CSSProperties => {
|
const getSubTextStyle = computed((): CSSProperties => {
|
||||||
if (!isCollapse.value) {
|
if (!isCollapse.value) {
|
||||||
return {
|
return {
|
||||||
width: "210px",
|
width: '210px',
|
||||||
display: "inline-block",
|
display: 'inline-block',
|
||||||
overflow: "hidden",
|
overflow: 'hidden',
|
||||||
textOverflow: "ellipsis"
|
textOverflow: 'ellipsis'
|
||||||
};
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
width: ""
|
width: ''
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const getSubMenuDivStyle = computed((): any => {
|
const getSubMenuDivStyle = computed((): any => {
|
||||||
return item => {
|
return item => {
|
||||||
return !isCollapse.value
|
return !isCollapse.value
|
||||||
? {
|
? {
|
||||||
width: "100%",
|
width: '100%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
justifyContent: "space-between",
|
justifyContent: 'space-between',
|
||||||
overflow: "hidden"
|
overflow: 'hidden'
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
width: "100%",
|
width: '100%',
|
||||||
textAlign:
|
textAlign:
|
||||||
item?.parentId === null
|
item?.parentId === null
|
||||||
? "center"
|
? 'center'
|
||||||
: layout.value === "mix" && item?.pathList?.length === 2
|
: layout.value === 'mix' && item?.pathList?.length === 2
|
||||||
? "center"
|
? 'center'
|
||||||
: ""
|
: ''
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const expandCloseIcon = computed(() => {
|
const expandCloseIcon = computed(() => {
|
||||||
if (!getConfig()?.MenuArrowIconNoTransition) return "";
|
if (!getConfig()?.MenuArrowIconNoTransition) return ''
|
||||||
return {
|
return {
|
||||||
"expand-close-icon": useRenderIcon(EpArrowDown),
|
'expand-close-icon': useRenderIcon(EpArrowDown),
|
||||||
"expand-open-icon": useRenderIcon(ArrowUp),
|
'expand-open-icon': useRenderIcon(ArrowUp),
|
||||||
"collapse-close-icon": useRenderIcon(ArrowRight),
|
'collapse-close-icon': useRenderIcon(ArrowRight),
|
||||||
"collapse-open-icon": useRenderIcon(ArrowLeft)
|
'collapse-open-icon': useRenderIcon(ArrowLeft)
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const onlyOneChild: childrenType = ref(null);
|
const onlyOneChild: childrenType = ref(null)
|
||||||
// 存放菜单是否存在showTooltip属性标识
|
// 存放菜单是否存在showTooltip属性标识
|
||||||
const hoverMenuMap = new WeakMap();
|
const hoverMenuMap = new WeakMap()
|
||||||
// 存储菜单文本dom元素
|
// 存储菜单文本dom元素
|
||||||
const menuTextRef = ref(null);
|
const menuTextRef = ref(null)
|
||||||
|
|
||||||
function hoverMenu(key) {
|
function hoverMenu(key) {
|
||||||
// 如果当前菜单showTooltip属性已存在,退出计算
|
// 如果当前菜单showTooltip属性已存在,退出计算
|
||||||
if (hoverMenuMap.get(key)) return;
|
if (hoverMenuMap.get(key)) return
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 如果文本内容的整体宽度大于其可视宽度,则文本溢出
|
// 如果文本内容的整体宽度大于其可视宽度,则文本溢出
|
||||||
@ -130,23 +130,23 @@ function hoverMenu(key) {
|
|||||||
})
|
})
|
||||||
: Object.assign(key, {
|
: Object.assign(key, {
|
||||||
showTooltip: false
|
showTooltip: false
|
||||||
});
|
})
|
||||||
hoverMenuMap.set(key, true);
|
hoverMenuMap.set(key, true)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 左侧菜单折叠后,当菜单没有图标时只显示第一个文字并加上省略号
|
// 左侧菜单折叠后,当菜单没有图标时只显示第一个文字并加上省略号
|
||||||
function overflowSlice(text, item?: any) {
|
function overflowSlice(text, item?: any) {
|
||||||
const newText =
|
const newText =
|
||||||
(text?.length > 1 ? text.toString().slice(0, 1) : text) + "...";
|
(text?.length > 1 ? text.toString().slice(0, 1) : text) + '...'
|
||||||
if (item && !(isCollapse.value && item?.parentId === null)) {
|
if (item && !(isCollapse.value && item?.parentId === null)) {
|
||||||
return layout.value === "mix" &&
|
return layout.value === 'mix' &&
|
||||||
item?.pathList?.length === 2 &&
|
item?.pathList?.length === 2 &&
|
||||||
isCollapse.value
|
isCollapse.value
|
||||||
? newText
|
? newText
|
||||||
: text;
|
: text
|
||||||
}
|
}
|
||||||
return newText;
|
return newText
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasOneShowingChild(
|
function hasOneShowingChild(
|
||||||
@ -154,32 +154,32 @@ function hasOneShowingChild(
|
|||||||
parent: childrenType
|
parent: childrenType
|
||||||
) {
|
) {
|
||||||
const showingChildren = children.filter((item: any) => {
|
const showingChildren = children.filter((item: any) => {
|
||||||
onlyOneChild.value = item;
|
onlyOneChild.value = item
|
||||||
return true;
|
return true
|
||||||
});
|
})
|
||||||
|
|
||||||
if (showingChildren[0]?.meta?.showParent) {
|
if (showingChildren[0]?.meta?.showParent) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showingChildren.length === 1) {
|
if (showingChildren.length === 1) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showingChildren.length === 0) {
|
if (showingChildren.length === 0) {
|
||||||
onlyOneChild.value = { ...parent, path: "", noShowingChildren: true };
|
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolvePath(routePath) {
|
function resolvePath(routePath) {
|
||||||
const httpReg = /^http(s?):\/\//;
|
const httpReg = /^http(s?):\/\//
|
||||||
if (httpReg.test(routePath) || httpReg.test(props.basePath)) {
|
if (httpReg.test(routePath) || httpReg.test(props.basePath)) {
|
||||||
return routePath || props.basePath;
|
return routePath || props.basePath
|
||||||
} else {
|
} else {
|
||||||
// 使用path.posix.resolve替代path.resolve 避免windows环境下使用electron出现盘符问题
|
// 使用path.posix.resolve替代path.resolve 避免windows环境下使用electron出现盘符问题
|
||||||
return path.posix.resolve(props.basePath, routePath);
|
return path.posix.resolve(props.basePath, routePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import MenuFold from "@iconify-icons/ri/menu-fold-fill";
|
import MenuFold from '@iconify-icons/ri/menu-fold-fill'
|
||||||
import MenuUnfold from "@iconify-icons/ri/menu-unfold-fill";
|
import MenuUnfold from '@iconify-icons/ri/menu-unfold-fill'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isActive: boolean;
|
isActive: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
isActive: false
|
isActive: false
|
||||||
});
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "toggleClick"): void;
|
(e: 'toggleClick'): void
|
||||||
}>();
|
}>()
|
||||||
|
|
||||||
const toggleClick = () => {
|
const toggleClick = () => {
|
||||||
emit("toggleClick");
|
emit('toggleClick')
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,62 +1,62 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Logo from "./logo.vue";
|
import Logo from './logo.vue'
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from 'vue-router'
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from '@/utils/mitt'
|
||||||
import SidebarItem from "./sidebarItem.vue";
|
import SidebarItem from './sidebarItem.vue'
|
||||||
import leftCollapse from "./leftCollapse.vue";
|
import leftCollapse from './leftCollapse.vue'
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from '@/layout/hooks/useNav'
|
||||||
import { storageLocal } from "@pureadmin/utils";
|
import { storageLocal } from '@pureadmin/utils'
|
||||||
import { ref, computed, watch, onBeforeMount } from "vue";
|
import { ref, computed, watch, onBeforeMount } from 'vue'
|
||||||
import { findRouteByPath, getParentPaths } from "@/router/utils";
|
import { findRouteByPath, getParentPaths } from '@/router/utils'
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from '@/store/modules/permission'
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const showLogo = ref(
|
const showLogo = ref(
|
||||||
storageLocal().getItem<StorageConfigs>("responsive-configure")?.showLogo ??
|
storageLocal().getItem<StorageConfigs>('responsive-configure')?.showLogo ??
|
||||||
true
|
true
|
||||||
);
|
)
|
||||||
|
|
||||||
const { routers, device, pureApp, isCollapse, menuSelect, toggleSideBar } =
|
const { routers, device, pureApp, isCollapse, menuSelect, toggleSideBar } =
|
||||||
useNav();
|
useNav()
|
||||||
|
|
||||||
const subMenuData = ref([]);
|
const subMenuData = ref([])
|
||||||
|
|
||||||
const menuData = computed(() => {
|
const menuData = computed(() => {
|
||||||
return pureApp.layout === "mix" && device.value !== "mobile"
|
return pureApp.layout === 'mix' && device.value !== 'mobile'
|
||||||
? subMenuData.value
|
? subMenuData.value
|
||||||
: usePermissionStoreHook().wholeMenus;
|
: usePermissionStoreHook().wholeMenus
|
||||||
});
|
})
|
||||||
|
|
||||||
function getSubMenuData(path: string) {
|
function getSubMenuData(path: string) {
|
||||||
// path的上级路由组成的数组
|
// path的上级路由组成的数组
|
||||||
const parentPathArr = getParentPaths(
|
const parentPathArr = getParentPaths(
|
||||||
path,
|
path,
|
||||||
usePermissionStoreHook().wholeMenus
|
usePermissionStoreHook().wholeMenus
|
||||||
);
|
)
|
||||||
// 当前路由的父级路由信息
|
// 当前路由的父级路由信息
|
||||||
const parenetRoute = findRouteByPath(
|
const parenetRoute = findRouteByPath(
|
||||||
parentPathArr[0] || path,
|
parentPathArr[0] || path,
|
||||||
usePermissionStoreHook().wholeMenus
|
usePermissionStoreHook().wholeMenus
|
||||||
);
|
)
|
||||||
if (!parenetRoute?.children) return;
|
if (!parenetRoute?.children) return
|
||||||
subMenuData.value = parenetRoute?.children;
|
subMenuData.value = parenetRoute?.children
|
||||||
}
|
}
|
||||||
|
|
||||||
getSubMenuData(route.path);
|
getSubMenuData(route.path)
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
emitter.on("logoChange", key => {
|
emitter.on('logoChange', key => {
|
||||||
showLogo.value = key;
|
showLogo.value = key
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [route.path, usePermissionStoreHook().wholeMenus],
|
() => [route.path, usePermissionStoreHook().wholeMenus],
|
||||||
() => {
|
() => {
|
||||||
getSubMenuData(route.path);
|
getSubMenuData(route.path)
|
||||||
menuSelect(route.path, routers);
|
menuSelect(route.path, routers)
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { $t } from "@/plugins/i18n";
|
import { $t } from '@/plugins/i18n'
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from '@/utils/mitt'
|
||||||
import { RouteConfigs } from "../../types";
|
import { RouteConfigs } from '../../types'
|
||||||
import { useTags } from "../../hooks/useTag";
|
import { useTags } from '../../hooks/useTag'
|
||||||
import { routerArrays } from "@/layout/types";
|
import { routerArrays } from '@/layout/types'
|
||||||
import { isEqual, isAllEmpty } from "@pureadmin/utils";
|
import { isEqual, isAllEmpty } from '@pureadmin/utils'
|
||||||
import { useSettingStoreHook } from "@/store/modules/settings";
|
import { useSettingStoreHook } from '@/store/modules/settings'
|
||||||
import { ref, watch, unref, nextTick, onBeforeMount } from "vue";
|
import { ref, watch, unref, nextTick, onBeforeMount } from 'vue'
|
||||||
import { handleAliveRoute, delAliveRoutes } from "@/router/utils";
|
import { handleAliveRoute, delAliveRoutes } from '@/router/utils'
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from '@/store/modules/multiTags'
|
||||||
import { useResizeObserver, useDebounceFn, useFullscreen } from "@vueuse/core";
|
import { useResizeObserver, useDebounceFn, useFullscreen } from '@vueuse/core'
|
||||||
|
|
||||||
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
|
import ExitFullscreen from '@iconify-icons/ri/fullscreen-exit-fill'
|
||||||
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
|
import Fullscreen from '@iconify-icons/ri/fullscreen-fill'
|
||||||
import ArrowDown from "@iconify-icons/ri/arrow-down-s-line";
|
import ArrowDown from '@iconify-icons/ri/arrow-down-s-line'
|
||||||
import ArrowRightSLine from "@iconify-icons/ri/arrow-right-s-line";
|
import ArrowRightSLine from '@iconify-icons/ri/arrow-right-s-line'
|
||||||
import ArrowLeftSLine from "@iconify-icons/ri/arrow-left-s-line";
|
import ArrowLeftSLine from '@iconify-icons/ri/arrow-left-s-line'
|
||||||
import CloseBold from "@iconify-icons/ep/close-bold";
|
import CloseBold from '@iconify-icons/ep/close-bold'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
route,
|
route,
|
||||||
@ -44,52 +44,52 @@ const {
|
|||||||
onMouseleave,
|
onMouseleave,
|
||||||
transformI18n,
|
transformI18n,
|
||||||
onContentFullScreen
|
onContentFullScreen
|
||||||
} = useTags();
|
} = useTags()
|
||||||
|
|
||||||
const tabDom = ref();
|
const tabDom = ref()
|
||||||
const containerDom = ref();
|
const containerDom = ref()
|
||||||
const scrollbarDom = ref();
|
const scrollbarDom = ref()
|
||||||
const isShowArrow = ref(false);
|
const isShowArrow = ref(false)
|
||||||
const { isFullscreen, toggle } = useFullscreen();
|
const { isFullscreen, toggle } = useFullscreen()
|
||||||
|
|
||||||
const dynamicTagView = () => {
|
const dynamicTagView = () => {
|
||||||
const index = multiTags.value.findIndex(item => {
|
const index = multiTags.value.findIndex(item => {
|
||||||
if (item.query) {
|
if (item.query) {
|
||||||
return isEqual(route.query, item.query);
|
return isEqual(route.query, item.query)
|
||||||
} else if (item.params) {
|
} else if (item.params) {
|
||||||
return isEqual(route.params, item.params);
|
return isEqual(route.params, item.params)
|
||||||
} else {
|
} else {
|
||||||
return item.path === route.path;
|
return item.path === route.path
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
moveToView(index);
|
moveToView(index)
|
||||||
};
|
}
|
||||||
|
|
||||||
const moveToView = async (index: number): Promise<void> => {
|
const moveToView = async (index: number): Promise<void> => {
|
||||||
const tabNavPadding = 10;
|
const tabNavPadding = 10
|
||||||
if (!instance.refs["dynamic" + index]) return;
|
if (!instance.refs['dynamic' + index]) return
|
||||||
const tabItemEl = instance.refs["dynamic" + index][0];
|
const tabItemEl = instance.refs['dynamic' + index][0]
|
||||||
const tabItemElOffsetLeft = (tabItemEl as HTMLElement)?.offsetLeft;
|
const tabItemElOffsetLeft = (tabItemEl as HTMLElement)?.offsetLeft
|
||||||
const tabItemOffsetWidth = (tabItemEl as HTMLElement)?.offsetWidth;
|
const tabItemOffsetWidth = (tabItemEl as HTMLElement)?.offsetWidth
|
||||||
// 标签页导航栏可视长度(不包含溢出部分)
|
// 标签页导航栏可视长度(不包含溢出部分)
|
||||||
const scrollbarDomWidth = scrollbarDom.value
|
const scrollbarDomWidth = scrollbarDom.value
|
||||||
? scrollbarDom.value?.offsetWidth
|
? scrollbarDom.value?.offsetWidth
|
||||||
: 0;
|
: 0
|
||||||
|
|
||||||
// 获取视图更新后dom
|
// 获取视图更新后dom
|
||||||
await nextTick();
|
await nextTick()
|
||||||
|
|
||||||
// 已有标签页总长度(包含溢出部分)
|
// 已有标签页总长度(包含溢出部分)
|
||||||
const tabDomWidth = tabDom.value ? tabDom.value?.offsetWidth : 0;
|
const tabDomWidth = tabDom.value ? tabDom.value?.offsetWidth : 0
|
||||||
|
|
||||||
scrollbarDomWidth <= tabDomWidth
|
scrollbarDomWidth <= tabDomWidth
|
||||||
? (isShowArrow.value = true)
|
? (isShowArrow.value = true)
|
||||||
: (isShowArrow.value = false);
|
: (isShowArrow.value = false)
|
||||||
if (tabDomWidth < scrollbarDomWidth || tabItemElOffsetLeft === 0) {
|
if (tabDomWidth < scrollbarDomWidth || tabItemElOffsetLeft === 0) {
|
||||||
translateX.value = 0;
|
translateX.value = 0
|
||||||
} else if (tabItemElOffsetLeft < -translateX.value) {
|
} else if (tabItemElOffsetLeft < -translateX.value) {
|
||||||
// 标签在可视区域左侧
|
// 标签在可视区域左侧
|
||||||
translateX.value = -tabItemElOffsetLeft + tabNavPadding;
|
translateX.value = -tabItemElOffsetLeft + tabNavPadding
|
||||||
} else if (
|
} else if (
|
||||||
tabItemElOffsetLeft > -translateX.value &&
|
tabItemElOffsetLeft > -translateX.value &&
|
||||||
tabItemElOffsetLeft + tabItemOffsetWidth <
|
tabItemElOffsetLeft + tabItemOffsetWidth <
|
||||||
@ -102,89 +102,89 @@ const moveToView = async (index: number): Promise<void> => {
|
|||||||
tabItemOffsetWidth -
|
tabItemOffsetWidth -
|
||||||
tabItemElOffsetLeft -
|
tabItemElOffsetLeft -
|
||||||
tabNavPadding
|
tabNavPadding
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
// 标签在可视区域右侧
|
// 标签在可视区域右侧
|
||||||
translateX.value = -(
|
translateX.value = -(
|
||||||
tabItemElOffsetLeft -
|
tabItemElOffsetLeft -
|
||||||
(scrollbarDomWidth - tabNavPadding - tabItemOffsetWidth)
|
(scrollbarDomWidth - tabNavPadding - tabItemOffsetWidth)
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleScroll = (offset: number): void => {
|
const handleScroll = (offset: number): void => {
|
||||||
const scrollbarDomWidth = scrollbarDom.value
|
const scrollbarDomWidth = scrollbarDom.value
|
||||||
? scrollbarDom.value?.offsetWidth
|
? scrollbarDom.value?.offsetWidth
|
||||||
: 0;
|
: 0
|
||||||
const tabDomWidth = tabDom.value ? tabDom.value.offsetWidth : 0;
|
const tabDomWidth = tabDom.value ? tabDom.value.offsetWidth : 0
|
||||||
if (offset > 0) {
|
if (offset > 0) {
|
||||||
translateX.value = Math.min(0, translateX.value + offset);
|
translateX.value = Math.min(0, translateX.value + offset)
|
||||||
} else {
|
} else {
|
||||||
if (scrollbarDomWidth < tabDomWidth) {
|
if (scrollbarDomWidth < tabDomWidth) {
|
||||||
if (translateX.value >= -(tabDomWidth - scrollbarDomWidth)) {
|
if (translateX.value >= -(tabDomWidth - scrollbarDomWidth)) {
|
||||||
translateX.value = Math.max(
|
translateX.value = Math.max(
|
||||||
translateX.value + offset,
|
translateX.value + offset,
|
||||||
scrollbarDomWidth - tabDomWidth
|
scrollbarDomWidth - tabDomWidth
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
translateX.value = 0;
|
translateX.value = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function dynamicRouteTag(value: string, parentPath: string): void {
|
function dynamicRouteTag(value: string, parentPath: string): void {
|
||||||
const hasValue = multiTags.value.some(item => {
|
const hasValue = multiTags.value.some(item => {
|
||||||
return item.path === value;
|
return item.path === value
|
||||||
});
|
})
|
||||||
|
|
||||||
function concatPath(arr: object[], value: string, parentPath: string) {
|
function concatPath(arr: object[], value: string, parentPath: string) {
|
||||||
if (!hasValue) {
|
if (!hasValue) {
|
||||||
arr.forEach((arrItem: any) => {
|
arr.forEach((arrItem: any) => {
|
||||||
const pathConcat = parentPath + arrItem.path;
|
const pathConcat = parentPath + arrItem.path
|
||||||
if (arrItem.path === value || pathConcat === value) {
|
if (arrItem.path === value || pathConcat === value) {
|
||||||
useMultiTagsStoreHook().handleTags("push", {
|
useMultiTagsStoreHook().handleTags('push', {
|
||||||
path: value,
|
path: value,
|
||||||
parentPath: `/${parentPath.split("/")[1]}`,
|
parentPath: `/${parentPath.split('/')[1]}`,
|
||||||
meta: arrItem.meta,
|
meta: arrItem.meta,
|
||||||
name: arrItem.name
|
name: arrItem.name
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
if (arrItem.children && arrItem.children.length > 0) {
|
if (arrItem.children && arrItem.children.length > 0) {
|
||||||
concatPath(arrItem.children, value, parentPath);
|
concatPath(arrItem.children, value, parentPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
concatPath(router.options.routes as any, value, parentPath);
|
concatPath(router.options.routes as any, value, parentPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 刷新路由 */
|
/** 刷新路由 */
|
||||||
function onFresh() {
|
function onFresh() {
|
||||||
const { fullPath, query } = unref(route);
|
const { fullPath, query } = unref(route)
|
||||||
router.replace({
|
router.replace({
|
||||||
path: "/redirect" + fullPath,
|
path: '/redirect' + fullPath,
|
||||||
query: query
|
query: query
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||||
// 存放被删除的缓存路由
|
// 存放被删除的缓存路由
|
||||||
let delAliveRouteList = [];
|
let delAliveRouteList = []
|
||||||
const valueIndex: number = multiTags.value.findIndex((item: any) => {
|
const valueIndex: number = multiTags.value.findIndex((item: any) => {
|
||||||
if (item.query) {
|
if (item.query) {
|
||||||
if (item.path === obj.path) {
|
if (item.path === obj.path) {
|
||||||
return item.query === obj.query;
|
return item.query === obj.query
|
||||||
}
|
}
|
||||||
} else if (item.params) {
|
} else if (item.params) {
|
||||||
if (item.path === obj.path) {
|
if (item.path === obj.path) {
|
||||||
return item.params === obj.params;
|
return item.params === obj.params
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return item.path === obj.path;
|
return item.path === obj.path
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const spliceRoute = (
|
const spliceRoute = (
|
||||||
startIndex?: number,
|
startIndex?: number,
|
||||||
@ -192,64 +192,64 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
|||||||
other?: boolean
|
other?: boolean
|
||||||
): void => {
|
): void => {
|
||||||
if (other) {
|
if (other) {
|
||||||
useMultiTagsStoreHook().handleTags("equal", [routerArrays[0], obj]);
|
useMultiTagsStoreHook().handleTags('equal', [routerArrays[0], obj])
|
||||||
} else {
|
} else {
|
||||||
delAliveRouteList = useMultiTagsStoreHook().handleTags("splice", "", {
|
delAliveRouteList = useMultiTagsStoreHook().handleTags('splice', '', {
|
||||||
startIndex,
|
startIndex,
|
||||||
length
|
length
|
||||||
}) as any;
|
}) as any
|
||||||
}
|
}
|
||||||
dynamicTagView();
|
dynamicTagView()
|
||||||
};
|
}
|
||||||
|
|
||||||
if (tag === "other") {
|
if (tag === 'other') {
|
||||||
spliceRoute(1, 1, true);
|
spliceRoute(1, 1, true)
|
||||||
} else if (tag === "left") {
|
} else if (tag === 'left') {
|
||||||
spliceRoute(1, valueIndex - 1);
|
spliceRoute(1, valueIndex - 1)
|
||||||
} else if (tag === "right") {
|
} else if (tag === 'right') {
|
||||||
spliceRoute(valueIndex + 1, multiTags.value.length);
|
spliceRoute(valueIndex + 1, multiTags.value.length)
|
||||||
} else {
|
} else {
|
||||||
// 从当前匹配到的路径中删除
|
// 从当前匹配到的路径中删除
|
||||||
spliceRoute(valueIndex, 1);
|
spliceRoute(valueIndex, 1)
|
||||||
}
|
}
|
||||||
const newRoute = useMultiTagsStoreHook().handleTags("slice");
|
const newRoute = useMultiTagsStoreHook().handleTags('slice')
|
||||||
if (current === route.path) {
|
if (current === route.path) {
|
||||||
// 删除缓存路由
|
// 删除缓存路由
|
||||||
tag
|
tag
|
||||||
? delAliveRoutes(delAliveRouteList)
|
? delAliveRoutes(delAliveRouteList)
|
||||||
: handleAliveRoute(route.matched, "delete");
|
: handleAliveRoute(route.matched, 'delete')
|
||||||
// 如果删除当前激活tag就自动切换到最后一个tag
|
// 如果删除当前激活tag就自动切换到最后一个tag
|
||||||
if (tag === "left") return;
|
if (tag === 'left') return
|
||||||
if (newRoute[0]?.query) {
|
if (newRoute[0]?.query) {
|
||||||
router.push({ name: newRoute[0].name, query: newRoute[0].query });
|
router.push({ name: newRoute[0].name, query: newRoute[0].query })
|
||||||
} else if (newRoute[0]?.params) {
|
} else if (newRoute[0]?.params) {
|
||||||
router.push({ name: newRoute[0].name, params: newRoute[0].params });
|
router.push({ name: newRoute[0].name, params: newRoute[0].params })
|
||||||
} else {
|
} else {
|
||||||
router.push({ path: newRoute[0].path });
|
router.push({ path: newRoute[0].path })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 删除缓存路由
|
// 删除缓存路由
|
||||||
tag ? delAliveRoutes(delAliveRouteList) : delAliveRoutes([obj]);
|
tag ? delAliveRoutes(delAliveRouteList) : delAliveRoutes([obj])
|
||||||
if (!multiTags.value.length) return;
|
if (!multiTags.value.length) return
|
||||||
if (multiTags.value.some(item => item.path === route.path)) return;
|
if (multiTags.value.some(item => item.path === route.path)) return
|
||||||
if (newRoute[0]?.query) {
|
if (newRoute[0]?.query) {
|
||||||
router.push({ name: newRoute[0].name, query: newRoute[0].query });
|
router.push({ name: newRoute[0].name, query: newRoute[0].query })
|
||||||
} else if (newRoute[0]?.params) {
|
} else if (newRoute[0]?.params) {
|
||||||
router.push({ name: newRoute[0].name, params: newRoute[0].params });
|
router.push({ name: newRoute[0].name, params: newRoute[0].params })
|
||||||
} else {
|
} else {
|
||||||
router.push({ path: newRoute[0].path });
|
router.push({ path: newRoute[0].path })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteMenu(item, tag?: string) {
|
function deleteMenu(item, tag?: string) {
|
||||||
deleteDynamicTag(item, item.path, tag);
|
deleteDynamicTag(item, item.path, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClickDrop(key, item, selectRoute?: RouteConfigs) {
|
function onClickDrop(key, item, selectRoute?: RouteConfigs) {
|
||||||
if (item && item.disabled) return;
|
if (item && item.disabled) return
|
||||||
|
|
||||||
let selectTagRoute;
|
let selectTagRoute
|
||||||
if (selectRoute) {
|
if (selectRoute) {
|
||||||
selectTagRoute = {
|
selectTagRoute = {
|
||||||
path: selectRoute.path,
|
path: selectRoute.path,
|
||||||
@ -257,93 +257,93 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
|
|||||||
name: selectRoute.name,
|
name: selectRoute.name,
|
||||||
query: selectRoute?.query,
|
query: selectRoute?.query,
|
||||||
params: selectRoute?.params
|
params: selectRoute?.params
|
||||||
};
|
}
|
||||||
} else {
|
} else {
|
||||||
selectTagRoute = { path: route.path, meta: route.meta };
|
selectTagRoute = { path: route.path, meta: route.meta }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前路由信息
|
// 当前路由信息
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 0:
|
case 0:
|
||||||
// 刷新路由
|
// 刷新路由
|
||||||
onFresh();
|
onFresh()
|
||||||
break;
|
break
|
||||||
case 1:
|
case 1:
|
||||||
// 关闭当前标签页
|
// 关闭当前标签页
|
||||||
deleteMenu(selectTagRoute);
|
deleteMenu(selectTagRoute)
|
||||||
break;
|
break
|
||||||
case 2:
|
case 2:
|
||||||
// 关闭左侧标签页
|
// 关闭左侧标签页
|
||||||
deleteMenu(selectTagRoute, "left");
|
deleteMenu(selectTagRoute, 'left')
|
||||||
break;
|
break
|
||||||
case 3:
|
case 3:
|
||||||
// 关闭右侧标签页
|
// 关闭右侧标签页
|
||||||
deleteMenu(selectTagRoute, "right");
|
deleteMenu(selectTagRoute, 'right')
|
||||||
break;
|
break
|
||||||
case 4:
|
case 4:
|
||||||
// 关闭其他标签页
|
// 关闭其他标签页
|
||||||
deleteMenu(selectTagRoute, "other");
|
deleteMenu(selectTagRoute, 'other')
|
||||||
break;
|
break
|
||||||
case 5:
|
case 5:
|
||||||
// 关闭全部标签页
|
// 关闭全部标签页
|
||||||
useMultiTagsStoreHook().handleTags("splice", "", {
|
useMultiTagsStoreHook().handleTags('splice', '', {
|
||||||
startIndex: 1,
|
startIndex: 1,
|
||||||
length: multiTags.value.length
|
length: multiTags.value.length
|
||||||
});
|
})
|
||||||
router.push("/welcome");
|
router.push('/welcome')
|
||||||
break;
|
break
|
||||||
case 6:
|
case 6:
|
||||||
// 整体页面全屏
|
// 整体页面全屏
|
||||||
toggle();
|
toggle()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (isFullscreen.value) {
|
if (isFullscreen.value) {
|
||||||
tagsViews[6].icon = ExitFullscreen;
|
tagsViews[6].icon = ExitFullscreen
|
||||||
tagsViews[6].text = $t("buttons.hswholeExitFullScreen");
|
tagsViews[6].text = $t('buttons.hswholeExitFullScreen')
|
||||||
} else {
|
} else {
|
||||||
tagsViews[6].icon = Fullscreen;
|
tagsViews[6].icon = Fullscreen
|
||||||
tagsViews[6].text = $t("buttons.hswholeFullScreen");
|
tagsViews[6].text = $t('buttons.hswholeFullScreen')
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100)
|
||||||
break;
|
break
|
||||||
case 7:
|
case 7:
|
||||||
// 内容区全屏
|
// 内容区全屏
|
||||||
onContentFullScreen();
|
onContentFullScreen()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (pureSetting.hiddenSideBar) {
|
if (pureSetting.hiddenSideBar) {
|
||||||
tagsViews[7].icon = ExitFullscreen;
|
tagsViews[7].icon = ExitFullscreen
|
||||||
tagsViews[7].text = $t("buttons.hscontentExitFullScreen");
|
tagsViews[7].text = $t('buttons.hscontentExitFullScreen')
|
||||||
} else {
|
} else {
|
||||||
tagsViews[7].icon = Fullscreen;
|
tagsViews[7].icon = Fullscreen
|
||||||
tagsViews[7].text = $t("buttons.hscontentFullScreen");
|
tagsViews[7].text = $t('buttons.hscontentFullScreen')
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showMenuModel(route.fullPath, route.query);
|
showMenuModel(route.fullPath, route.query)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCommand(command: any) {
|
function handleCommand(command: any) {
|
||||||
const { key, item } = command;
|
const { key, item } = command
|
||||||
onClickDrop(key, item);
|
onClickDrop(key, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 触发右键中菜单的点击事件 */
|
/** 触发右键中菜单的点击事件 */
|
||||||
function selectTag(key, item) {
|
function selectTag(key, item) {
|
||||||
onClickDrop(key, item, currentSelect.value);
|
onClickDrop(key, item, currentSelect.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMenus(value: boolean) {
|
function showMenus(value: boolean) {
|
||||||
Array.of(1, 2, 3, 4, 5).forEach(v => {
|
Array.of(1, 2, 3, 4, 5).forEach(v => {
|
||||||
tagsViews[v].show = value;
|
tagsViews[v].show = value
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function disabledMenus(value: boolean) {
|
function disabledMenus(value: boolean) {
|
||||||
Array.of(1, 2, 3, 4, 5).forEach(v => {
|
Array.of(1, 2, 3, 4, 5).forEach(v => {
|
||||||
tagsViews[v].disabled = value;
|
tagsViews[v].disabled = value
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是首页,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
|
/** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是首页,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
|
||||||
@ -352,19 +352,19 @@ function showMenuModel(
|
|||||||
query: object = {},
|
query: object = {},
|
||||||
refresh = false
|
refresh = false
|
||||||
) {
|
) {
|
||||||
const allRoute = multiTags.value;
|
const allRoute = multiTags.value
|
||||||
const routeLength = multiTags.value.length;
|
const routeLength = multiTags.value.length
|
||||||
let currentIndex = -1;
|
let currentIndex = -1
|
||||||
if (isAllEmpty(query)) {
|
if (isAllEmpty(query)) {
|
||||||
currentIndex = allRoute.findIndex(v => v.path === currentPath);
|
currentIndex = allRoute.findIndex(v => v.path === currentPath)
|
||||||
} else {
|
} else {
|
||||||
currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
|
currentIndex = allRoute.findIndex(v => isEqual(v.query, query))
|
||||||
}
|
}
|
||||||
|
|
||||||
showMenus(true);
|
showMenus(true)
|
||||||
|
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
tagsViews[0].show = true;
|
tagsViews[0].show = true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -373,142 +373,142 @@ function showMenuModel(
|
|||||||
*/
|
*/
|
||||||
if (currentIndex === 1 && routeLength !== 2) {
|
if (currentIndex === 1 && routeLength !== 2) {
|
||||||
// 左侧的菜单是首页,右侧存在别的菜单
|
// 左侧的菜单是首页,右侧存在别的菜单
|
||||||
tagsViews[2].show = false;
|
tagsViews[2].show = false
|
||||||
Array.of(1, 3, 4, 5).forEach(v => {
|
Array.of(1, 3, 4, 5).forEach(v => {
|
||||||
tagsViews[v].disabled = false;
|
tagsViews[v].disabled = false
|
||||||
});
|
})
|
||||||
tagsViews[2].disabled = true;
|
tagsViews[2].disabled = true
|
||||||
} else if (currentIndex === 1 && routeLength === 2) {
|
} else if (currentIndex === 1 && routeLength === 2) {
|
||||||
disabledMenus(false);
|
disabledMenus(false)
|
||||||
// 左侧的菜单是首页,右侧不存在别的菜单
|
// 左侧的菜单是首页,右侧不存在别的菜单
|
||||||
Array.of(2, 3, 4).forEach(v => {
|
Array.of(2, 3, 4).forEach(v => {
|
||||||
tagsViews[v].show = false;
|
tagsViews[v].show = false
|
||||||
tagsViews[v].disabled = true;
|
tagsViews[v].disabled = true
|
||||||
});
|
})
|
||||||
} else if (routeLength - 1 === currentIndex && currentIndex !== 0) {
|
} else if (routeLength - 1 === currentIndex && currentIndex !== 0) {
|
||||||
// 当前路由是所有路由中的最后一个
|
// 当前路由是所有路由中的最后一个
|
||||||
tagsViews[3].show = false;
|
tagsViews[3].show = false
|
||||||
Array.of(1, 2, 4, 5).forEach(v => {
|
Array.of(1, 2, 4, 5).forEach(v => {
|
||||||
tagsViews[v].disabled = false;
|
tagsViews[v].disabled = false
|
||||||
});
|
})
|
||||||
tagsViews[3].disabled = true;
|
tagsViews[3].disabled = true
|
||||||
} else if (currentIndex === 0 || currentPath === "/redirect/welcome") {
|
} else if (currentIndex === 0 || currentPath === '/redirect/welcome') {
|
||||||
// 当前路由为首页
|
// 当前路由为首页
|
||||||
disabledMenus(true);
|
disabledMenus(true)
|
||||||
} else {
|
} else {
|
||||||
disabledMenus(false);
|
disabledMenus(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openMenu(tag, e) {
|
function openMenu(tag, e) {
|
||||||
closeMenu();
|
closeMenu()
|
||||||
if (tag.path === "/welcome") {
|
if (tag.path === '/welcome') {
|
||||||
// 右键菜单为首页,只显示刷新
|
// 右键菜单为首页,只显示刷新
|
||||||
showMenus(false);
|
showMenus(false)
|
||||||
tagsViews[0].show = true;
|
tagsViews[0].show = true
|
||||||
} else if (route.path !== tag.path && route.name !== tag.name) {
|
} else if (route.path !== tag.path && route.name !== tag.name) {
|
||||||
// 右键菜单不匹配当前路由,隐藏刷新
|
// 右键菜单不匹配当前路由,隐藏刷新
|
||||||
tagsViews[0].show = false;
|
tagsViews[0].show = false
|
||||||
showMenuModel(tag.path, tag.query);
|
showMenuModel(tag.path, tag.query)
|
||||||
} else if (
|
} else if (
|
||||||
// eslint-disable-next-line no-dupe-else-if
|
// eslint-disable-next-line no-dupe-else-if
|
||||||
multiTags.value.length === 2 &&
|
multiTags.value.length === 2 &&
|
||||||
route.path !== tag.path
|
route.path !== tag.path
|
||||||
) {
|
) {
|
||||||
showMenus(true);
|
showMenus(true)
|
||||||
// 只有两个标签时不显示关闭其他标签页
|
// 只有两个标签时不显示关闭其他标签页
|
||||||
tagsViews[4].show = false;
|
tagsViews[4].show = false
|
||||||
} else if (route.path === tag.path) {
|
} else if (route.path === tag.path) {
|
||||||
// 右键当前激活的菜单
|
// 右键当前激活的菜单
|
||||||
showMenuModel(tag.path, tag.query, true);
|
showMenuModel(tag.path, tag.query, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSelect.value = tag;
|
currentSelect.value = tag
|
||||||
const menuMinWidth = 105;
|
const menuMinWidth = 105
|
||||||
const offsetLeft = unref(containerDom).getBoundingClientRect().left;
|
const offsetLeft = unref(containerDom).getBoundingClientRect().left
|
||||||
const offsetWidth = unref(containerDom).offsetWidth;
|
const offsetWidth = unref(containerDom).offsetWidth
|
||||||
const maxLeft = offsetWidth - menuMinWidth;
|
const maxLeft = offsetWidth - menuMinWidth
|
||||||
const left = e.clientX - offsetLeft + 5;
|
const left = e.clientX - offsetLeft + 5
|
||||||
if (left > maxLeft) {
|
if (left > maxLeft) {
|
||||||
buttonLeft.value = maxLeft;
|
buttonLeft.value = maxLeft
|
||||||
} else {
|
} else {
|
||||||
buttonLeft.value = left;
|
buttonLeft.value = left
|
||||||
}
|
}
|
||||||
useSettingStoreHook().hiddenSideBar
|
useSettingStoreHook().hiddenSideBar
|
||||||
? (buttonTop.value = e.clientY)
|
? (buttonTop.value = e.clientY)
|
||||||
: (buttonTop.value = e.clientY - 40);
|
: (buttonTop.value = e.clientY - 40)
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
visible.value = true;
|
visible.value = true
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 触发tags标签切换 */
|
/** 触发tags标签切换 */
|
||||||
function tagOnClick(item) {
|
function tagOnClick(item) {
|
||||||
const { name, path } = item;
|
const { name, path } = item
|
||||||
if (name) {
|
if (name) {
|
||||||
if (item.query) {
|
if (item.query) {
|
||||||
router.push({
|
router.push({
|
||||||
name,
|
name,
|
||||||
query: item.query
|
query: item.query
|
||||||
});
|
})
|
||||||
} else if (item.params) {
|
} else if (item.params) {
|
||||||
router.push({
|
router.push({
|
||||||
name,
|
name,
|
||||||
params: item.params
|
params: item.params
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
router.push({ name });
|
router.push({ name })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
router.push({ path });
|
router.push({ path })
|
||||||
}
|
}
|
||||||
// showMenuModel(item?.path, item?.query);
|
// showMenuModel(item?.path, item?.query);
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
if (!instance) return;
|
if (!instance) return
|
||||||
|
|
||||||
// 根据当前路由初始化操作标签页的禁用状态
|
// 根据当前路由初始化操作标签页的禁用状态
|
||||||
showMenuModel(route.fullPath);
|
showMenuModel(route.fullPath)
|
||||||
|
|
||||||
// 触发隐藏标签页
|
// 触发隐藏标签页
|
||||||
emitter.on("tagViewsChange", (key: any) => {
|
emitter.on('tagViewsChange', (key: any) => {
|
||||||
if (unref(showTags as any) === key) return;
|
if (unref(showTags as any) === key) return
|
||||||
(showTags as any).value = key;
|
;(showTags as any).value = key
|
||||||
});
|
})
|
||||||
|
|
||||||
// 改变标签风格
|
// 改变标签风格
|
||||||
emitter.on("tagViewsShowModel", key => {
|
emitter.on('tagViewsShowModel', key => {
|
||||||
showModel.value = key;
|
showModel.value = key
|
||||||
});
|
})
|
||||||
|
|
||||||
// 接收侧边栏切换传递过来的参数
|
// 接收侧边栏切换传递过来的参数
|
||||||
emitter.on("changLayoutRoute", ({ indexPath, parentPath }) => {
|
emitter.on('changLayoutRoute', ({ indexPath, parentPath }) => {
|
||||||
dynamicRouteTag(indexPath, parentPath);
|
dynamicRouteTag(indexPath, parentPath)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showMenuModel(indexPath);
|
showMenuModel(indexPath)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
watch([route], () => {
|
watch([route], () => {
|
||||||
activeIndex.value = -1;
|
activeIndex.value = -1
|
||||||
dynamicTagView();
|
dynamicTagView()
|
||||||
});
|
})
|
||||||
|
|
||||||
watch(isFullscreen, () => {
|
watch(isFullscreen, () => {
|
||||||
tagsViews[6].icon = Fullscreen;
|
tagsViews[6].icon = Fullscreen
|
||||||
tagsViews[6].text = $t("buttons.hswholeFullScreen");
|
tagsViews[6].text = $t('buttons.hswholeFullScreen')
|
||||||
});
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
useResizeObserver(
|
useResizeObserver(
|
||||||
scrollbarDom,
|
scrollbarDom,
|
||||||
useDebounceFn(() => {
|
useDebounceFn(() => {
|
||||||
dynamicTagView();
|
dynamicTagView()
|
||||||
}, 200)
|
}, 200)
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -609,5 +609,5 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "./index.scss";
|
@import './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,47 +1,47 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from 'vue-router'
|
||||||
import { ref, unref, onMounted, nextTick } from "vue";
|
import { ref, unref, onMounted, nextTick } from 'vue'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "FrameView"
|
name: 'FrameView'
|
||||||
});
|
})
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n()
|
||||||
const loading = ref(true);
|
const loading = ref(true)
|
||||||
const currentRoute = useRoute();
|
const currentRoute = useRoute()
|
||||||
const frameSrc = ref<string>("");
|
const frameSrc = ref<string>('')
|
||||||
const frameRef = ref<HTMLElement | null>(null);
|
const frameRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
if (unref(currentRoute.meta)?.frameSrc) {
|
if (unref(currentRoute.meta)?.frameSrc) {
|
||||||
frameSrc.value = unref(currentRoute.meta)?.frameSrc as string;
|
frameSrc.value = unref(currentRoute.meta)?.frameSrc as string
|
||||||
}
|
}
|
||||||
unref(currentRoute.meta)?.frameLoading === false && hideLoading();
|
unref(currentRoute.meta)?.frameLoading === false && hideLoading()
|
||||||
|
|
||||||
function hideLoading() {
|
function hideLoading() {
|
||||||
loading.value = false;
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const iframe = unref(frameRef);
|
const iframe = unref(frameRef)
|
||||||
if (!iframe) return;
|
if (!iframe) return
|
||||||
const _frame = iframe as any;
|
const _frame = iframe as any
|
||||||
if (_frame.attachEvent) {
|
if (_frame.attachEvent) {
|
||||||
_frame.attachEvent("onload", () => {
|
_frame.attachEvent('onload', () => {
|
||||||
hideLoading();
|
hideLoading()
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
iframe.onload = () => {
|
iframe.onload = () => {
|
||||||
hideLoading();
|
hideLoading()
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
init();
|
init()
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
|
|
||||||
export function useBoolean(initValue = false) {
|
export function useBoolean(initValue = false) {
|
||||||
const bool = ref(initValue);
|
const bool = ref(initValue)
|
||||||
|
|
||||||
function setBool(value: boolean) {
|
function setBool(value: boolean) {
|
||||||
bool.value = value;
|
bool.value = value
|
||||||
}
|
}
|
||||||
function setTrue() {
|
function setTrue() {
|
||||||
setBool(true);
|
setBool(true)
|
||||||
}
|
}
|
||||||
function setFalse() {
|
function setFalse() {
|
||||||
setBool(false);
|
setBool(false)
|
||||||
}
|
}
|
||||||
function toggle() {
|
function toggle() {
|
||||||
setBool(!bool.value);
|
setBool(!bool.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -22,5 +22,5 @@ export function useBoolean(initValue = false) {
|
|||||||
setTrue,
|
setTrue,
|
||||||
setFalse,
|
setFalse,
|
||||||
toggle
|
toggle
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,61 +1,61 @@
|
|||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from '@/config'
|
||||||
import { useLayout } from "./useLayout";
|
import { useLayout } from './useLayout'
|
||||||
import { themeColorsType } from "../types";
|
import { themeColorsType } from '../types'
|
||||||
import { useGlobal } from "@pureadmin/utils";
|
import { useGlobal } from '@pureadmin/utils'
|
||||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
import { useEpThemeStoreHook } from '@/store/modules/epTheme'
|
||||||
import {
|
import {
|
||||||
darken,
|
darken,
|
||||||
lighten,
|
lighten,
|
||||||
toggleTheme
|
toggleTheme
|
||||||
} from "@pureadmin/theme/dist/browser-utils";
|
} from '@pureadmin/theme/dist/browser-utils'
|
||||||
|
|
||||||
export function useDataThemeChange() {
|
export function useDataThemeChange() {
|
||||||
const { layoutTheme, layout } = useLayout();
|
const { layoutTheme, layout } = useLayout()
|
||||||
const themeColors = ref<Array<themeColorsType>>([
|
const themeColors = ref<Array<themeColorsType>>([
|
||||||
/* 道奇蓝(默认) */
|
/* 道奇蓝(默认) */
|
||||||
{ color: "#1b2a47", themeColor: "default" },
|
{ color: '#1b2a47', themeColor: 'default' },
|
||||||
/* 亮白色 */
|
/* 亮白色 */
|
||||||
{ color: "#ffffff", themeColor: "light" },
|
{ color: '#ffffff', themeColor: 'light' },
|
||||||
/* 猩红色 */
|
/* 猩红色 */
|
||||||
{ color: "#f5222d", themeColor: "dusk" },
|
{ color: '#f5222d', themeColor: 'dusk' },
|
||||||
/* 橙红色 */
|
/* 橙红色 */
|
||||||
{ color: "#fa541c", themeColor: "volcano" },
|
{ color: '#fa541c', themeColor: 'volcano' },
|
||||||
/* 金色 */
|
/* 金色 */
|
||||||
{ color: "#fadb14", themeColor: "yellow" },
|
{ color: '#fadb14', themeColor: 'yellow' },
|
||||||
/* 绿宝石 */
|
/* 绿宝石 */
|
||||||
{ color: "#13c2c2", themeColor: "mingQing" },
|
{ color: '#13c2c2', themeColor: 'mingQing' },
|
||||||
/* 酸橙绿 */
|
/* 酸橙绿 */
|
||||||
{ color: "#52c41a", themeColor: "auroraGreen" },
|
{ color: '#52c41a', themeColor: 'auroraGreen' },
|
||||||
/* 深粉色 */
|
/* 深粉色 */
|
||||||
{ color: "#eb2f96", themeColor: "pink" },
|
{ color: '#eb2f96', themeColor: 'pink' },
|
||||||
/* 深紫罗兰色 */
|
/* 深紫罗兰色 */
|
||||||
{ color: "#722ed1", themeColor: "saucePurple" }
|
{ color: '#722ed1', themeColor: 'saucePurple' }
|
||||||
]);
|
])
|
||||||
|
|
||||||
const { $storage } = useGlobal<GlobalPropertiesApi>();
|
const { $storage } = useGlobal<GlobalPropertiesApi>()
|
||||||
const dataTheme = ref<boolean>($storage?.layout?.darkMode);
|
const dataTheme = ref<boolean>($storage?.layout?.darkMode)
|
||||||
const body = document.documentElement as HTMLElement;
|
const body = document.documentElement as HTMLElement
|
||||||
|
|
||||||
/** 设置导航主题色 */
|
/** 设置导航主题色 */
|
||||||
function setLayoutThemeColor(theme = getConfig().Theme ?? "default") {
|
function setLayoutThemeColor(theme = getConfig().Theme ?? 'default') {
|
||||||
layoutTheme.value.theme = theme;
|
layoutTheme.value.theme = theme
|
||||||
toggleTheme({
|
toggleTheme({
|
||||||
scopeName: `layout-theme-${theme}`
|
scopeName: `layout-theme-${theme}`
|
||||||
});
|
})
|
||||||
$storage.layout = {
|
$storage.layout = {
|
||||||
layout: layout.value,
|
layout: layout.value,
|
||||||
theme,
|
theme,
|
||||||
darkMode: dataTheme.value,
|
darkMode: dataTheme.value,
|
||||||
sidebarStatus: $storage.layout?.sidebarStatus,
|
sidebarStatus: $storage.layout?.sidebarStatus,
|
||||||
epThemeColor: $storage.layout?.epThemeColor
|
epThemeColor: $storage.layout?.epThemeColor
|
||||||
};
|
}
|
||||||
|
|
||||||
if (theme === "default" || theme === "light") {
|
if (theme === 'default' || theme === 'light') {
|
||||||
setEpThemeColor(getConfig().EpThemeColor);
|
setEpThemeColor(getConfig().EpThemeColor)
|
||||||
} else {
|
} else {
|
||||||
const colors = themeColors.value.find(v => v.themeColor === theme);
|
const colors = themeColors.value.find(v => v.themeColor === theme)
|
||||||
setEpThemeColor(colors.color);
|
setEpThemeColor(colors.color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,34 +63,34 @@ export function useDataThemeChange() {
|
|||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
`--el-color-primary-${mode}-${i}`,
|
`--el-color-primary-${mode}-${i}`,
|
||||||
dataTheme.value ? darken(color, i / 10) : lighten(color, i / 10)
|
dataTheme.value ? darken(color, i / 10) : lighten(color, i / 10)
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 设置 `element-plus` 主题色 */
|
/** 设置 `element-plus` 主题色 */
|
||||||
const setEpThemeColor = (color: string) => {
|
const setEpThemeColor = (color: string) => {
|
||||||
useEpThemeStoreHook().setEpThemeColor(color);
|
useEpThemeStoreHook().setEpThemeColor(color)
|
||||||
document.documentElement.style.setProperty("--el-color-primary", color);
|
document.documentElement.style.setProperty('--el-color-primary', color)
|
||||||
for (let i = 1; i <= 2; i++) {
|
for (let i = 1; i <= 2; i++) {
|
||||||
setPropertyPrimary("dark", i, color);
|
setPropertyPrimary('dark', i, color)
|
||||||
}
|
}
|
||||||
for (let i = 1; i <= 9; i++) {
|
for (let i = 1; i <= 9; i++) {
|
||||||
setPropertyPrimary("light", i, color);
|
setPropertyPrimary('light', i, color)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 日间、夜间主题切换 */
|
/** 日间、夜间主题切换 */
|
||||||
function dataThemeChange() {
|
function dataThemeChange() {
|
||||||
/* 如果当前是light夜间主题,默认切换到default主题 */
|
/* 如果当前是light夜间主题,默认切换到default主题 */
|
||||||
if (useEpThemeStoreHook().epTheme === "light" && dataTheme.value) {
|
if (useEpThemeStoreHook().epTheme === 'light' && dataTheme.value) {
|
||||||
setLayoutThemeColor("default");
|
setLayoutThemeColor('default')
|
||||||
} else {
|
} else {
|
||||||
setLayoutThemeColor(useEpThemeStoreHook().epTheme);
|
setLayoutThemeColor(useEpThemeStoreHook().epTheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataTheme.value) {
|
if (dataTheme.value) {
|
||||||
document.documentElement.classList.add("dark");
|
document.documentElement.classList.add('dark')
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove("dark");
|
document.documentElement.classList.remove('dark')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,5 +102,5 @@ export function useDataThemeChange() {
|
|||||||
dataThemeChange,
|
dataThemeChange,
|
||||||
setEpThemeColor,
|
setEpThemeColor,
|
||||||
setLayoutThemeColor
|
setLayoutThemeColor
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { computed } from "vue";
|
import { computed } from 'vue'
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from 'vue-i18n'
|
||||||
import { routerArrays } from "../types";
|
import { routerArrays } from '../types'
|
||||||
import { useGlobal } from "@pureadmin/utils";
|
import { useGlobal } from '@pureadmin/utils'
|
||||||
import { useMultiTagsStore } from "@/store/modules/multiTags";
|
import { useMultiTagsStore } from '@/store/modules/multiTags'
|
||||||
|
|
||||||
export function useLayout() {
|
export function useLayout() {
|
||||||
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
|
const { $storage, $config } = useGlobal<GlobalPropertiesApi>()
|
||||||
|
|
||||||
const initStorage = () => {
|
const initStorage = () => {
|
||||||
/** 路由 */
|
/** 路由 */
|
||||||
@ -13,22 +13,22 @@ export function useLayout() {
|
|||||||
useMultiTagsStore().multiTagsCache &&
|
useMultiTagsStore().multiTagsCache &&
|
||||||
(!$storage.tags || $storage.tags.length === 0)
|
(!$storage.tags || $storage.tags.length === 0)
|
||||||
) {
|
) {
|
||||||
$storage.tags = routerArrays;
|
$storage.tags = routerArrays
|
||||||
}
|
}
|
||||||
/** 国际化 */
|
/** 国际化 */
|
||||||
if (!$storage.locale) {
|
if (!$storage.locale) {
|
||||||
$storage.locale = { locale: $config?.Locale ?? "zh" };
|
$storage.locale = { locale: $config?.Locale ?? 'zh' }
|
||||||
useI18n().locale.value = $config?.Locale ?? "zh";
|
useI18n().locale.value = $config?.Locale ?? 'zh'
|
||||||
}
|
}
|
||||||
/** 导航 */
|
/** 导航 */
|
||||||
if (!$storage.layout) {
|
if (!$storage.layout) {
|
||||||
$storage.layout = {
|
$storage.layout = {
|
||||||
layout: $config?.Layout ?? "vertical",
|
layout: $config?.Layout ?? 'vertical',
|
||||||
theme: $config?.Theme ?? "default",
|
theme: $config?.Theme ?? 'default',
|
||||||
darkMode: $config?.DarkMode ?? false,
|
darkMode: $config?.DarkMode ?? false,
|
||||||
sidebarStatus: $config?.SidebarStatus ?? true,
|
sidebarStatus: $config?.SidebarStatus ?? true,
|
||||||
epThemeColor: $config?.EpThemeColor ?? "#409EFF"
|
epThemeColor: $config?.EpThemeColor ?? '#409EFF'
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
/** 灰色模式、色弱模式、隐藏标签页 */
|
/** 灰色模式、色弱模式、隐藏标签页 */
|
||||||
if (!$storage.configure) {
|
if (!$storage.configure) {
|
||||||
@ -37,24 +37,24 @@ export function useLayout() {
|
|||||||
weak: $config?.Weak ?? false,
|
weak: $config?.Weak ?? false,
|
||||||
hideTabs: $config?.HideTabs ?? false,
|
hideTabs: $config?.HideTabs ?? false,
|
||||||
showLogo: $config?.ShowLogo ?? true,
|
showLogo: $config?.ShowLogo ?? true,
|
||||||
showModel: $config?.ShowModel ?? "smart",
|
showModel: $config?.ShowModel ?? 'smart',
|
||||||
multiTagsCache: $config?.MultiTagsCache ?? false
|
multiTagsCache: $config?.MultiTagsCache ?? false
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 清空缓存后从serverConfig.json读取默认配置并赋值到storage中 */
|
/** 清空缓存后从serverConfig.json读取默认配置并赋值到storage中 */
|
||||||
const layout = computed(() => {
|
const layout = computed(() => {
|
||||||
return $storage?.layout.layout;
|
return $storage?.layout.layout
|
||||||
});
|
})
|
||||||
|
|
||||||
const layoutTheme = computed(() => {
|
const layoutTheme = computed(() => {
|
||||||
return $storage.layout;
|
return $storage.layout
|
||||||
});
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
layout,
|
layout,
|
||||||
layoutTheme,
|
layoutTheme,
|
||||||
initStorage
|
initStorage
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,150 +1,150 @@
|
|||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from 'pinia'
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from '@/config'
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from 'vue-router'
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from '@/utils/mitt'
|
||||||
import { routeMetaType } from "../types";
|
import { routeMetaType } from '../types'
|
||||||
import { useGlobal } from "@pureadmin/utils";
|
import { useGlobal } from '@pureadmin/utils'
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
import { transformI18n } from '@/plugins/i18n'
|
||||||
import { router, remainingPaths } from "@/router";
|
import { router, remainingPaths } from '@/router'
|
||||||
import { computed, type CSSProperties } from "vue";
|
import { computed, type CSSProperties } from 'vue'
|
||||||
import { useAppStoreHook } from "@/store/modules/app";
|
import { useAppStoreHook } from '@/store/modules/app'
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
import { useUserStoreHook } from '@/store/modules/user'
|
||||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
import { useEpThemeStoreHook } from '@/store/modules/epTheme'
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from '@/store/modules/permission'
|
||||||
|
|
||||||
const errorInfo = "当前路由配置不正确,请检查配置";
|
const errorInfo = '当前路由配置不正确,请检查配置'
|
||||||
|
|
||||||
export function useNav() {
|
export function useNav() {
|
||||||
const pureApp = useAppStoreHook();
|
const pureApp = useAppStoreHook()
|
||||||
const routers = useRouter().options.routes;
|
const routers = useRouter().options.routes
|
||||||
const { wholeMenus } = storeToRefs(usePermissionStoreHook());
|
const { wholeMenus } = storeToRefs(usePermissionStoreHook())
|
||||||
/** 平台`layout`中所有`el-tooltip`的`effect`配置,默认`light` */
|
/** 平台`layout`中所有`el-tooltip`的`effect`配置,默认`light` */
|
||||||
const tooltipEffect = getConfig()?.TooltipEffect ?? "light";
|
const tooltipEffect = getConfig()?.TooltipEffect ?? 'light'
|
||||||
|
|
||||||
const getDivStyle = computed((): CSSProperties => {
|
const getDivStyle = computed((): CSSProperties => {
|
||||||
return {
|
return {
|
||||||
width: "100%",
|
width: '100%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
justifyContent: "space-between",
|
justifyContent: 'space-between',
|
||||||
overflow: "hidden"
|
overflow: 'hidden'
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
/** 用户名 */
|
/** 用户名 */
|
||||||
const username = computed(() => {
|
const username = computed(() => {
|
||||||
return useUserStoreHook()?.username;
|
return useUserStoreHook()?.username
|
||||||
});
|
})
|
||||||
|
|
||||||
/** 设置国际化选中后的样式 */
|
/** 设置国际化选中后的样式 */
|
||||||
const getDropdownItemStyle = computed(() => {
|
const getDropdownItemStyle = computed(() => {
|
||||||
return (locale, t) => {
|
return (locale, t) => {
|
||||||
return {
|
return {
|
||||||
background: locale === t ? useEpThemeStoreHook().epThemeColor : "",
|
background: locale === t ? useEpThemeStoreHook().epThemeColor : '',
|
||||||
color: locale === t ? "#f4f4f5" : "#000"
|
color: locale === t ? '#f4f4f5' : '#000'
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const getDropdownItemClass = computed(() => {
|
const getDropdownItemClass = computed(() => {
|
||||||
return (locale, t) => {
|
return (locale, t) => {
|
||||||
return locale === t ? "" : "dark:hover:!text-primary";
|
return locale === t ? '' : 'dark:hover:!text-primary'
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const avatarsStyle = computed(() => {
|
const avatarsStyle = computed(() => {
|
||||||
return username.value ? { marginRight: "10px" } : "";
|
return username.value ? { marginRight: '10px' } : ''
|
||||||
});
|
})
|
||||||
|
|
||||||
const isCollapse = computed(() => {
|
const isCollapse = computed(() => {
|
||||||
return !pureApp.getSidebarStatus;
|
return !pureApp.getSidebarStatus
|
||||||
});
|
})
|
||||||
|
|
||||||
const device = computed(() => {
|
const device = computed(() => {
|
||||||
return pureApp.getDevice;
|
return pureApp.getDevice
|
||||||
});
|
})
|
||||||
|
|
||||||
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
|
const { $storage, $config } = useGlobal<GlobalPropertiesApi>()
|
||||||
const layout = computed(() => {
|
const layout = computed(() => {
|
||||||
return $storage?.layout?.layout;
|
return $storage?.layout?.layout
|
||||||
});
|
})
|
||||||
|
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
return $config.Title;
|
return $config.Title
|
||||||
});
|
})
|
||||||
|
|
||||||
/** 动态title */
|
/** 动态title */
|
||||||
function changeTitle(meta: routeMetaType) {
|
function changeTitle(meta: routeMetaType) {
|
||||||
const Title = getConfig().Title;
|
const Title = getConfig().Title
|
||||||
if (Title) document.title = `${transformI18n(meta.title)} | ${Title}`;
|
if (Title) document.title = `${transformI18n(meta.title)} | ${Title}`
|
||||||
else document.title = transformI18n(meta.title);
|
else document.title = transformI18n(meta.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 退出登录 */
|
/** 退出登录 */
|
||||||
function logout() {
|
function logout() {
|
||||||
useUserStoreHook().logOut();
|
useUserStoreHook().logOut()
|
||||||
}
|
}
|
||||||
|
|
||||||
function backHome() {
|
function backHome() {
|
||||||
router.push("/welcome");
|
router.push('/welcome')
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPanel() {
|
function onPanel() {
|
||||||
emitter.emit("openPanel");
|
emitter.emit('openPanel')
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSideBar() {
|
function toggleSideBar() {
|
||||||
pureApp.toggleSideBar();
|
pureApp.toggleSideBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleResize(menuRef) {
|
function handleResize(menuRef) {
|
||||||
menuRef?.handleResize();
|
menuRef?.handleResize()
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolvePath(route) {
|
function resolvePath(route) {
|
||||||
if (!route.children) return console.error(errorInfo);
|
if (!route.children) return console.error(errorInfo)
|
||||||
const httpReg = /^http(s?):\/\//;
|
const httpReg = /^http(s?):\/\//
|
||||||
const routeChildPath = route.children[0]?.path;
|
const routeChildPath = route.children[0]?.path
|
||||||
if (httpReg.test(routeChildPath)) {
|
if (httpReg.test(routeChildPath)) {
|
||||||
return route.path + "/" + routeChildPath;
|
return route.path + '/' + routeChildPath
|
||||||
} else {
|
} else {
|
||||||
return routeChildPath;
|
return routeChildPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function menuSelect(indexPath: string, routers): void {
|
function menuSelect(indexPath: string, routers): void {
|
||||||
if (wholeMenus.value.length === 0) return;
|
if (wholeMenus.value.length === 0) return
|
||||||
if (isRemaining(indexPath)) return;
|
if (isRemaining(indexPath)) return
|
||||||
let parentPath = "";
|
let parentPath = ''
|
||||||
const parentPathIndex = indexPath.lastIndexOf("/");
|
const parentPathIndex = indexPath.lastIndexOf('/')
|
||||||
if (parentPathIndex > 0) {
|
if (parentPathIndex > 0) {
|
||||||
parentPath = indexPath.slice(0, parentPathIndex);
|
parentPath = indexPath.slice(0, parentPathIndex)
|
||||||
}
|
}
|
||||||
/** 找到当前路由的信息 */
|
/** 找到当前路由的信息 */
|
||||||
function findCurrentRoute(indexPath: string, routes) {
|
function findCurrentRoute(indexPath: string, routes) {
|
||||||
if (!routes) return console.error(errorInfo);
|
if (!routes) return console.error(errorInfo)
|
||||||
return routes.map(item => {
|
return routes.map(item => {
|
||||||
if (item.path === indexPath) {
|
if (item.path === indexPath) {
|
||||||
if (item.redirect) {
|
if (item.redirect) {
|
||||||
findCurrentRoute(item.redirect, item.children);
|
findCurrentRoute(item.redirect, item.children)
|
||||||
} else {
|
} else {
|
||||||
/** 切换左侧菜单 通知标签页 */
|
/** 切换左侧菜单 通知标签页 */
|
||||||
emitter.emit("changLayoutRoute", {
|
emitter.emit('changLayoutRoute', {
|
||||||
indexPath,
|
indexPath,
|
||||||
parentPath
|
parentPath
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (item.children) findCurrentRoute(indexPath, item.children);
|
if (item.children) findCurrentRoute(indexPath, item.children)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
findCurrentRoute(indexPath, routers);
|
findCurrentRoute(indexPath, routers)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 判断路径是否参与菜单 */
|
/** 判断路径是否参与菜单 */
|
||||||
function isRemaining(path: string): boolean {
|
function isRemaining(path: string): boolean {
|
||||||
return remainingPaths.includes(path);
|
return remainingPaths.includes(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -169,5 +169,5 @@ export function useNav() {
|
|||||||
tooltipEffect,
|
tooltipEffect,
|
||||||
getDropdownItemStyle,
|
getDropdownItemStyle,
|
||||||
getDropdownItemClass
|
getDropdownItemClass
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,213 +7,213 @@ import {
|
|||||||
onMounted,
|
onMounted,
|
||||||
CSSProperties,
|
CSSProperties,
|
||||||
getCurrentInstance
|
getCurrentInstance
|
||||||
} from "vue";
|
} from 'vue'
|
||||||
import { tagsViewsType } from "../types";
|
import { tagsViewsType } from '../types'
|
||||||
import { useEventListener } from "@vueuse/core";
|
import { useEventListener } from '@vueuse/core'
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { transformI18n, $t } from "@/plugins/i18n";
|
import { transformI18n, $t } from '@/plugins/i18n'
|
||||||
import { useSettingStoreHook } from "@/store/modules/settings";
|
import { useSettingStoreHook } from '@/store/modules/settings'
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from '@/store/modules/multiTags'
|
||||||
import {
|
import {
|
||||||
isEqual,
|
isEqual,
|
||||||
isBoolean,
|
isBoolean,
|
||||||
storageLocal,
|
storageLocal,
|
||||||
toggleClass,
|
toggleClass,
|
||||||
hasClass
|
hasClass
|
||||||
} from "@pureadmin/utils";
|
} from '@pureadmin/utils'
|
||||||
|
|
||||||
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
|
import Fullscreen from '@iconify-icons/ri/fullscreen-fill'
|
||||||
import CloseAllTags from "@iconify-icons/ri/subtract-line";
|
import CloseAllTags from '@iconify-icons/ri/subtract-line'
|
||||||
import CloseOtherTags from "@iconify-icons/ri/text-spacing";
|
import CloseOtherTags from '@iconify-icons/ri/text-spacing'
|
||||||
import CloseRightTags from "@iconify-icons/ri/text-direction-l";
|
import CloseRightTags from '@iconify-icons/ri/text-direction-l'
|
||||||
import CloseLeftTags from "@iconify-icons/ri/text-direction-r";
|
import CloseLeftTags from '@iconify-icons/ri/text-direction-r'
|
||||||
import RefreshRight from "@iconify-icons/ep/refresh-right";
|
import RefreshRight from '@iconify-icons/ep/refresh-right'
|
||||||
import Close from "@iconify-icons/ep/close";
|
import Close from '@iconify-icons/ep/close'
|
||||||
|
|
||||||
export function useTags() {
|
export function useTags() {
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const instance = getCurrentInstance();
|
const instance = getCurrentInstance()
|
||||||
const pureSetting = useSettingStoreHook();
|
const pureSetting = useSettingStoreHook()
|
||||||
|
|
||||||
const buttonTop = ref(0);
|
const buttonTop = ref(0)
|
||||||
const buttonLeft = ref(0);
|
const buttonLeft = ref(0)
|
||||||
const translateX = ref(0);
|
const translateX = ref(0)
|
||||||
const visible = ref(false);
|
const visible = ref(false)
|
||||||
const activeIndex = ref(-1);
|
const activeIndex = ref(-1)
|
||||||
// 当前右键选中的路由信息
|
// 当前右键选中的路由信息
|
||||||
const currentSelect = ref({});
|
const currentSelect = ref({})
|
||||||
|
|
||||||
/** 显示模式,默认灵动模式 */
|
/** 显示模式,默认灵动模式 */
|
||||||
const showModel = ref(
|
const showModel = ref(
|
||||||
storageLocal().getItem<StorageConfigs>("responsive-configure")?.showModel ||
|
storageLocal().getItem<StorageConfigs>('responsive-configure')?.showModel ||
|
||||||
"smart"
|
'smart'
|
||||||
);
|
)
|
||||||
/** 是否隐藏标签页,默认显示 */
|
/** 是否隐藏标签页,默认显示 */
|
||||||
const showTags =
|
const showTags =
|
||||||
ref(
|
ref(
|
||||||
storageLocal().getItem<StorageConfigs>("responsive-configure").hideTabs
|
storageLocal().getItem<StorageConfigs>('responsive-configure').hideTabs
|
||||||
) ?? ref("false");
|
) ?? ref('false')
|
||||||
const multiTags: any = computed(() => {
|
const multiTags: any = computed(() => {
|
||||||
return useMultiTagsStoreHook().multiTags;
|
return useMultiTagsStoreHook().multiTags
|
||||||
});
|
})
|
||||||
|
|
||||||
const tagsViews = reactive<Array<tagsViewsType>>([
|
const tagsViews = reactive<Array<tagsViewsType>>([
|
||||||
{
|
{
|
||||||
icon: RefreshRight,
|
icon: RefreshRight,
|
||||||
text: $t("buttons.hsreload"),
|
text: $t('buttons.hsreload'),
|
||||||
divided: false,
|
divided: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: Close,
|
icon: Close,
|
||||||
text: $t("buttons.hscloseCurrentTab"),
|
text: $t('buttons.hscloseCurrentTab'),
|
||||||
divided: false,
|
divided: false,
|
||||||
disabled: multiTags.value.length > 1 ? false : true,
|
disabled: multiTags.value.length > 1 ? false : true,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CloseLeftTags,
|
icon: CloseLeftTags,
|
||||||
text: $t("buttons.hscloseLeftTabs"),
|
text: $t('buttons.hscloseLeftTabs'),
|
||||||
divided: true,
|
divided: true,
|
||||||
disabled: multiTags.value.length > 1 ? false : true,
|
disabled: multiTags.value.length > 1 ? false : true,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CloseRightTags,
|
icon: CloseRightTags,
|
||||||
text: $t("buttons.hscloseRightTabs"),
|
text: $t('buttons.hscloseRightTabs'),
|
||||||
divided: false,
|
divided: false,
|
||||||
disabled: multiTags.value.length > 1 ? false : true,
|
disabled: multiTags.value.length > 1 ? false : true,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CloseOtherTags,
|
icon: CloseOtherTags,
|
||||||
text: $t("buttons.hscloseOtherTabs"),
|
text: $t('buttons.hscloseOtherTabs'),
|
||||||
divided: true,
|
divided: true,
|
||||||
disabled: multiTags.value.length > 2 ? false : true,
|
disabled: multiTags.value.length > 2 ? false : true,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CloseAllTags,
|
icon: CloseAllTags,
|
||||||
text: $t("buttons.hscloseAllTabs"),
|
text: $t('buttons.hscloseAllTabs'),
|
||||||
divided: false,
|
divided: false,
|
||||||
disabled: multiTags.value.length > 1 ? false : true,
|
disabled: multiTags.value.length > 1 ? false : true,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: Fullscreen,
|
icon: Fullscreen,
|
||||||
text: $t("buttons.hswholeFullScreen"),
|
text: $t('buttons.hswholeFullScreen'),
|
||||||
divided: true,
|
divided: true,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: Fullscreen,
|
icon: Fullscreen,
|
||||||
text: $t("buttons.hscontentFullScreen"),
|
text: $t('buttons.hscontentFullScreen'),
|
||||||
divided: false,
|
divided: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
show: true
|
show: true
|
||||||
}
|
}
|
||||||
]);
|
])
|
||||||
|
|
||||||
function conditionHandle(item, previous, next) {
|
function conditionHandle(item, previous, next) {
|
||||||
if (isBoolean(route?.meta?.showLink) && route?.meta?.showLink === false) {
|
if (isBoolean(route?.meta?.showLink) && route?.meta?.showLink === false) {
|
||||||
if (Object.keys(route.query).length > 0) {
|
if (Object.keys(route.query).length > 0) {
|
||||||
return isEqual(route.query, item.query) ? previous : next;
|
return isEqual(route.query, item.query) ? previous : next
|
||||||
} else {
|
} else {
|
||||||
return isEqual(route.params, item.params) ? previous : next;
|
return isEqual(route.params, item.params) ? previous : next
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return route.path === item.path ? previous : next;
|
return route.path === item.path ? previous : next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const iconIsActive = computed(() => {
|
const iconIsActive = computed(() => {
|
||||||
return (item, index) => {
|
return (item, index) => {
|
||||||
if (index === 0) return;
|
if (index === 0) return
|
||||||
return conditionHandle(item, true, false);
|
return conditionHandle(item, true, false)
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const linkIsActive = computed(() => {
|
const linkIsActive = computed(() => {
|
||||||
return item => {
|
return item => {
|
||||||
return conditionHandle(item, "is-active", "");
|
return conditionHandle(item, 'is-active', '')
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const scheduleIsActive = computed(() => {
|
const scheduleIsActive = computed(() => {
|
||||||
return item => {
|
return item => {
|
||||||
return conditionHandle(item, "schedule-active", "");
|
return conditionHandle(item, 'schedule-active', '')
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const getTabStyle = computed((): CSSProperties => {
|
const getTabStyle = computed((): CSSProperties => {
|
||||||
return {
|
return {
|
||||||
transform: `translateX(${translateX.value}px)`
|
transform: `translateX(${translateX.value}px)`
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const getContextMenuStyle = computed((): CSSProperties => {
|
const getContextMenuStyle = computed((): CSSProperties => {
|
||||||
return { left: buttonLeft.value + "px", top: buttonTop.value + "px" };
|
return { left: buttonLeft.value + 'px', top: buttonTop.value + 'px' }
|
||||||
});
|
})
|
||||||
|
|
||||||
const closeMenu = () => {
|
const closeMenu = () => {
|
||||||
visible.value = false;
|
visible.value = false
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 鼠标移入添加激活样式 */
|
/** 鼠标移入添加激活样式 */
|
||||||
function onMouseenter(index) {
|
function onMouseenter(index) {
|
||||||
if (index) activeIndex.value = index;
|
if (index) activeIndex.value = index
|
||||||
if (unref(showModel) === "smart") {
|
if (unref(showModel) === 'smart') {
|
||||||
if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
|
if (hasClass(instance.refs['schedule' + index][0], 'schedule-active'))
|
||||||
return;
|
return
|
||||||
toggleClass(true, "schedule-in", instance.refs["schedule" + index][0]);
|
toggleClass(true, 'schedule-in', instance.refs['schedule' + index][0])
|
||||||
toggleClass(false, "schedule-out", instance.refs["schedule" + index][0]);
|
toggleClass(false, 'schedule-out', instance.refs['schedule' + index][0])
|
||||||
} else {
|
} else {
|
||||||
if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
|
if (hasClass(instance.refs['dynamic' + index][0], 'card-active')) return
|
||||||
toggleClass(true, "card-in", instance.refs["dynamic" + index][0]);
|
toggleClass(true, 'card-in', instance.refs['dynamic' + index][0])
|
||||||
toggleClass(false, "card-out", instance.refs["dynamic" + index][0]);
|
toggleClass(false, 'card-out', instance.refs['dynamic' + index][0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 鼠标移出恢复默认样式 */
|
/** 鼠标移出恢复默认样式 */
|
||||||
function onMouseleave(index) {
|
function onMouseleave(index) {
|
||||||
activeIndex.value = -1;
|
activeIndex.value = -1
|
||||||
if (unref(showModel) === "smart") {
|
if (unref(showModel) === 'smart') {
|
||||||
if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
|
if (hasClass(instance.refs['schedule' + index][0], 'schedule-active'))
|
||||||
return;
|
return
|
||||||
toggleClass(false, "schedule-in", instance.refs["schedule" + index][0]);
|
toggleClass(false, 'schedule-in', instance.refs['schedule' + index][0])
|
||||||
toggleClass(true, "schedule-out", instance.refs["schedule" + index][0]);
|
toggleClass(true, 'schedule-out', instance.refs['schedule' + index][0])
|
||||||
} else {
|
} else {
|
||||||
if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
|
if (hasClass(instance.refs['dynamic' + index][0], 'card-active')) return
|
||||||
toggleClass(false, "card-in", instance.refs["dynamic" + index][0]);
|
toggleClass(false, 'card-in', instance.refs['dynamic' + index][0])
|
||||||
toggleClass(true, "card-out", instance.refs["dynamic" + index][0]);
|
toggleClass(true, 'card-out', instance.refs['dynamic' + index][0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onContentFullScreen() {
|
function onContentFullScreen() {
|
||||||
pureSetting.hiddenSideBar
|
pureSetting.hiddenSideBar
|
||||||
? pureSetting.changeSetting({ key: "hiddenSideBar", value: false })
|
? pureSetting.changeSetting({ key: 'hiddenSideBar', value: false })
|
||||||
: pureSetting.changeSetting({ key: "hiddenSideBar", value: true });
|
: pureSetting.changeSetting({ key: 'hiddenSideBar', value: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!showModel.value) {
|
if (!showModel.value) {
|
||||||
const configure = storageLocal().getItem<StorageConfigs>(
|
const configure = storageLocal().getItem<StorageConfigs>(
|
||||||
"responsive-configure"
|
'responsive-configure'
|
||||||
);
|
)
|
||||||
configure.showModel = "card";
|
configure.showModel = 'card'
|
||||||
storageLocal().setItem("responsive-configure", configure);
|
storageLocal().setItem('responsive-configure', configure)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => visible.value,
|
() => visible.value,
|
||||||
() => {
|
() => {
|
||||||
useEventListener(document, "click", closeMenu);
|
useEventListener(document, 'click', closeMenu)
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
route,
|
route,
|
||||||
@ -242,5 +242,5 @@ export function useTags() {
|
|||||||
onMouseleave,
|
onMouseleave,
|
||||||
transformI18n,
|
transformI18n,
|
||||||
onContentFullScreen
|
onContentFullScreen
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
import { useNav } from "./useNav";
|
import { useNav } from './useNav'
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from 'vue-router'
|
||||||
import { watch, type Ref } from "vue";
|
import { watch, type Ref } from 'vue'
|
||||||
|
|
||||||
export function useTranslationLang(ref?: Ref) {
|
export function useTranslationLang(ref?: Ref) {
|
||||||
const { $storage, changeTitle, handleResize } = useNav();
|
const { $storage, changeTitle, handleResize } = useNav()
|
||||||
const { locale, t } = useI18n();
|
const { locale, t } = useI18n()
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
|
|
||||||
function translationCh() {
|
function translationCh() {
|
||||||
$storage.locale = { locale: "zh" };
|
$storage.locale = { locale: 'zh' }
|
||||||
locale.value = "zh";
|
locale.value = 'zh'
|
||||||
ref && handleResize(ref.value);
|
ref && handleResize(ref.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function translationEn() {
|
function translationEn() {
|
||||||
$storage.locale = { locale: "en" };
|
$storage.locale = { locale: 'en' }
|
||||||
locale.value = "en";
|
locale.value = 'en'
|
||||||
ref && handleResize(ref.value);
|
ref && handleResize(ref.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => locale.value,
|
() => locale.value,
|
||||||
() => {
|
() => {
|
||||||
changeTitle(route.meta);
|
changeTitle(route.meta)
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
t,
|
t,
|
||||||
@ -33,5 +33,5 @@ export function useTranslationLang(ref?: Ref) {
|
|||||||
locale,
|
locale,
|
||||||
translationCh,
|
translationCh,
|
||||||
translationEn
|
translationEn
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import "animate.css";
|
import 'animate.css'
|
||||||
// 引入 src/components/ReIcon/src/offlineIcon.ts 文件中所有使用addIcon添加过的本地图标
|
// 引入 src/components/ReIcon/src/offlineIcon.ts 文件中所有使用addIcon添加过的本地图标
|
||||||
import "@/components/ReIcon/src/offlineIcon";
|
import '@/components/ReIcon/src/offlineIcon'
|
||||||
import { setType } from "./types";
|
import { setType } from './types'
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from '@/utils/mitt'
|
||||||
import { useLayout } from "./hooks/useLayout";
|
import { useLayout } from './hooks/useLayout'
|
||||||
import { useAppStoreHook } from "@/store/modules/app";
|
import { useAppStoreHook } from '@/store/modules/app'
|
||||||
import { useSettingStoreHook } from "@/store/modules/settings";
|
import { useSettingStoreHook } from '@/store/modules/settings'
|
||||||
import { deviceDetection, useDark, useGlobal } from "@pureadmin/utils";
|
import { deviceDetection, useDark, useGlobal } from '@pureadmin/utils'
|
||||||
import { h, reactive, computed, onMounted, defineComponent } from "vue";
|
import { h, reactive, computed, onMounted, defineComponent } from 'vue'
|
||||||
|
|
||||||
import navbar from "./components/navbar.vue";
|
import navbar from './components/navbar.vue'
|
||||||
import tag from "./components/tag/index.vue";
|
import tag from './components/tag/index.vue'
|
||||||
import appMain from "./components/appMain.vue";
|
import appMain from './components/appMain.vue'
|
||||||
import setting from "./components/setting/index.vue";
|
import setting from './components/setting/index.vue'
|
||||||
import Vertical from "./components/sidebar/vertical.vue";
|
import Vertical from './components/sidebar/vertical.vue'
|
||||||
import Horizontal from "./components/sidebar/horizontal.vue";
|
import Horizontal from './components/sidebar/horizontal.vue'
|
||||||
import backTop from "@/assets/svg/back_top.svg?component";
|
import backTop from '@/assets/svg/back_top.svg?component'
|
||||||
|
|
||||||
const { isDark } = useDark();
|
const { isDark } = useDark()
|
||||||
const { layout } = useLayout();
|
const { layout } = useLayout()
|
||||||
const isMobile = deviceDetection();
|
const isMobile = deviceDetection()
|
||||||
const pureSetting = useSettingStoreHook();
|
const pureSetting = useSettingStoreHook()
|
||||||
const { $storage } = useGlobal<GlobalPropertiesApi>();
|
const { $storage } = useGlobal<GlobalPropertiesApi>()
|
||||||
|
|
||||||
const set: setType = reactive({
|
const set: setType = reactive({
|
||||||
sidebar: computed(() => {
|
sidebar: computed(() => {
|
||||||
return useAppStoreHook().sidebar;
|
return useAppStoreHook().sidebar
|
||||||
}),
|
}),
|
||||||
|
|
||||||
device: computed(() => {
|
device: computed(() => {
|
||||||
return useAppStoreHook().device;
|
return useAppStoreHook().device
|
||||||
}),
|
}),
|
||||||
|
|
||||||
fixedHeader: computed(() => {
|
fixedHeader: computed(() => {
|
||||||
return pureSetting.fixedHeader;
|
return pureSetting.fixedHeader
|
||||||
}),
|
}),
|
||||||
|
|
||||||
classes: computed(() => {
|
classes: computed(() => {
|
||||||
@ -42,95 +42,95 @@ const set: setType = reactive({
|
|||||||
hideSidebar: !set.sidebar.opened,
|
hideSidebar: !set.sidebar.opened,
|
||||||
openSidebar: set.sidebar.opened,
|
openSidebar: set.sidebar.opened,
|
||||||
withoutAnimation: set.sidebar.withoutAnimation,
|
withoutAnimation: set.sidebar.withoutAnimation,
|
||||||
mobile: set.device === "mobile"
|
mobile: set.device === 'mobile'
|
||||||
};
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
hideTabs: computed(() => {
|
hideTabs: computed(() => {
|
||||||
return $storage?.configure.hideTabs;
|
return $storage?.configure.hideTabs
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
function setTheme(layoutModel: string) {
|
function setTheme(layoutModel: string) {
|
||||||
window.document.body.setAttribute("layout", layoutModel);
|
window.document.body.setAttribute('layout', layoutModel)
|
||||||
$storage.layout = {
|
$storage.layout = {
|
||||||
layout: `${layoutModel}`,
|
layout: `${layoutModel}`,
|
||||||
theme: $storage.layout?.theme,
|
theme: $storage.layout?.theme,
|
||||||
darkMode: $storage.layout?.darkMode,
|
darkMode: $storage.layout?.darkMode,
|
||||||
sidebarStatus: $storage.layout?.sidebarStatus,
|
sidebarStatus: $storage.layout?.sidebarStatus,
|
||||||
epThemeColor: $storage.layout?.epThemeColor
|
epThemeColor: $storage.layout?.epThemeColor
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle(device: string, bool: boolean) {
|
function toggle(device: string, bool: boolean) {
|
||||||
useAppStoreHook().toggleDevice(device);
|
useAppStoreHook().toggleDevice(device)
|
||||||
useAppStoreHook().toggleSideBar(bool, "resize");
|
useAppStoreHook().toggleSideBar(bool, 'resize')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否可自动关闭菜单栏
|
// 判断是否可自动关闭菜单栏
|
||||||
let isAutoCloseSidebar = true;
|
let isAutoCloseSidebar = true
|
||||||
|
|
||||||
// 监听容器
|
// 监听容器
|
||||||
emitter.on("resize", ({ detail }) => {
|
emitter.on('resize', ({ detail }) => {
|
||||||
if (isMobile) return;
|
if (isMobile) return
|
||||||
const { width } = detail;
|
const { width } = detail
|
||||||
width <= 760 ? setTheme("vertical") : setTheme(useAppStoreHook().layout);
|
width <= 760 ? setTheme('vertical') : setTheme(useAppStoreHook().layout)
|
||||||
/** width app-wrapper类容器宽度
|
/** width app-wrapper类容器宽度
|
||||||
* 0 < width <= 760 隐藏侧边栏
|
* 0 < width <= 760 隐藏侧边栏
|
||||||
* 760 < width <= 990 折叠侧边栏
|
* 760 < width <= 990 折叠侧边栏
|
||||||
* width > 990 展开侧边栏
|
* width > 990 展开侧边栏
|
||||||
*/
|
*/
|
||||||
if (width > 0 && width <= 760) {
|
if (width > 0 && width <= 760) {
|
||||||
toggle("mobile", false);
|
toggle('mobile', false)
|
||||||
isAutoCloseSidebar = true;
|
isAutoCloseSidebar = true
|
||||||
} else if (width > 760 && width <= 990) {
|
} else if (width > 760 && width <= 990) {
|
||||||
if (isAutoCloseSidebar) {
|
if (isAutoCloseSidebar) {
|
||||||
toggle("desktop", false);
|
toggle('desktop', false)
|
||||||
isAutoCloseSidebar = false;
|
isAutoCloseSidebar = false
|
||||||
}
|
}
|
||||||
} else if (width > 990) {
|
} else if (width > 990) {
|
||||||
if (!set.sidebar.isClickCollapse) {
|
if (!set.sidebar.isClickCollapse) {
|
||||||
toggle("desktop", true);
|
toggle('desktop', true)
|
||||||
isAutoCloseSidebar = true;
|
isAutoCloseSidebar = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
toggle("mobile", false);
|
toggle('mobile', false)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const layoutHeader = defineComponent({
|
const layoutHeader = defineComponent({
|
||||||
render() {
|
render() {
|
||||||
return h(
|
return h(
|
||||||
"div",
|
'div',
|
||||||
{
|
{
|
||||||
class: { "fixed-header": set.fixedHeader },
|
class: { 'fixed-header': set.fixedHeader },
|
||||||
style: [
|
style: [
|
||||||
set.hideTabs && layout.value.includes("horizontal")
|
set.hideTabs && layout.value.includes('horizontal')
|
||||||
? isDark.value
|
? isDark.value
|
||||||
? "box-shadow: 0 1px 4px #0d0d0d"
|
? 'box-shadow: 0 1px 4px #0d0d0d'
|
||||||
: "box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08)"
|
: 'box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08)'
|
||||||
: ""
|
: ''
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
default: () => [
|
default: () => [
|
||||||
!pureSetting.hiddenSideBar &&
|
!pureSetting.hiddenSideBar &&
|
||||||
(layout.value.includes("vertical") || layout.value.includes("mix"))
|
(layout.value.includes('vertical') || layout.value.includes('mix'))
|
||||||
? h(navbar)
|
? h(navbar)
|
||||||
: null,
|
: null,
|
||||||
!pureSetting.hiddenSideBar && layout.value.includes("horizontal")
|
!pureSetting.hiddenSideBar && layout.value.includes('horizontal')
|
||||||
? h(Horizontal)
|
? h(Horizontal)
|
||||||
: null,
|
: null,
|
||||||
h(tag)
|
h(tag)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -181,7 +181,7 @@ const layoutHeader = defineComponent({
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@mixin clearfix {
|
@mixin clearfix {
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: '';
|
||||||
display: table;
|
display: table;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { unref } from "vue";
|
import { unref } from 'vue'
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "Redirect"
|
name: 'Redirect'
|
||||||
});
|
})
|
||||||
|
|
||||||
const { currentRoute, replace } = useRouter();
|
const { currentRoute, replace } = useRouter()
|
||||||
|
|
||||||
const { params, query } = unref(currentRoute);
|
const { params, query } = unref(currentRoute)
|
||||||
const { path } = params;
|
const { path } = params
|
||||||
|
|
||||||
const _path = Array.isArray(path) ? path.join("/") : path;
|
const _path = Array.isArray(path) ? path.join('/') : path
|
||||||
|
|
||||||
replace({
|
replace({
|
||||||
path: "/" + _path,
|
path: '/' + _path,
|
||||||
query
|
query
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -2,116 +2,116 @@
|
|||||||
* @description ⚠️:此文件仅供主题插件使用,请不要在此文件中导出别的工具函数(仅在页面加载前运行)
|
* @description ⚠️:此文件仅供主题插件使用,请不要在此文件中导出别的工具函数(仅在页面加载前运行)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type multipleScopeVarsOptions } from "@pureadmin/theme";
|
import { type multipleScopeVarsOptions } from '@pureadmin/theme'
|
||||||
|
|
||||||
/** 预设主题色 */
|
/** 预设主题色 */
|
||||||
const themeColors = {
|
const themeColors = {
|
||||||
default: {
|
default: {
|
||||||
subMenuActiveText: "#fff",
|
subMenuActiveText: '#fff',
|
||||||
menuBg: "#001529",
|
menuBg: '#001529',
|
||||||
menuHover: "#4091f7",
|
menuHover: '#4091f7',
|
||||||
subMenuBg: "#0f0303",
|
subMenuBg: '#0f0303',
|
||||||
subMenuActiveBg: "#4091f7",
|
subMenuActiveBg: '#4091f7',
|
||||||
menuText: "rgb(254 254 254 / 65%)",
|
menuText: 'rgb(254 254 254 / 65%)',
|
||||||
sidebarLogo: "#002140",
|
sidebarLogo: '#002140',
|
||||||
menuTitleHover: "#fff",
|
menuTitleHover: '#fff',
|
||||||
menuActiveBefore: "#4091f7"
|
menuActiveBefore: '#4091f7'
|
||||||
},
|
},
|
||||||
light: {
|
light: {
|
||||||
subMenuActiveText: "#409eff",
|
subMenuActiveText: '#409eff',
|
||||||
menuBg: "#fff",
|
menuBg: '#fff',
|
||||||
menuHover: "#e0ebf6",
|
menuHover: '#e0ebf6',
|
||||||
subMenuBg: "#fff",
|
subMenuBg: '#fff',
|
||||||
subMenuActiveBg: "#e0ebf6",
|
subMenuActiveBg: '#e0ebf6',
|
||||||
menuText: "#7a80b4",
|
menuText: '#7a80b4',
|
||||||
sidebarLogo: "#fff",
|
sidebarLogo: '#fff',
|
||||||
menuTitleHover: "#000",
|
menuTitleHover: '#000',
|
||||||
menuActiveBefore: "#4091f7"
|
menuActiveBefore: '#4091f7'
|
||||||
},
|
},
|
||||||
dusk: {
|
dusk: {
|
||||||
subMenuActiveText: "#fff",
|
subMenuActiveText: '#fff',
|
||||||
menuBg: "#2a0608",
|
menuBg: '#2a0608',
|
||||||
menuHover: "#e13c39",
|
menuHover: '#e13c39',
|
||||||
subMenuBg: "#000",
|
subMenuBg: '#000',
|
||||||
subMenuActiveBg: "#e13c39",
|
subMenuActiveBg: '#e13c39',
|
||||||
menuText: "rgb(254 254 254 / 65.1%)",
|
menuText: 'rgb(254 254 254 / 65.1%)',
|
||||||
sidebarLogo: "#42090c",
|
sidebarLogo: '#42090c',
|
||||||
menuTitleHover: "#fff",
|
menuTitleHover: '#fff',
|
||||||
menuActiveBefore: "#e13c39"
|
menuActiveBefore: '#e13c39'
|
||||||
},
|
},
|
||||||
volcano: {
|
volcano: {
|
||||||
subMenuActiveText: "#fff",
|
subMenuActiveText: '#fff',
|
||||||
menuBg: "#2b0e05",
|
menuBg: '#2b0e05',
|
||||||
menuHover: "#e85f33",
|
menuHover: '#e85f33',
|
||||||
subMenuBg: "#0f0603",
|
subMenuBg: '#0f0603',
|
||||||
subMenuActiveBg: "#e85f33",
|
subMenuActiveBg: '#e85f33',
|
||||||
menuText: "rgb(254 254 254 / 65%)",
|
menuText: 'rgb(254 254 254 / 65%)',
|
||||||
sidebarLogo: "#441708",
|
sidebarLogo: '#441708',
|
||||||
menuTitleHover: "#fff",
|
menuTitleHover: '#fff',
|
||||||
menuActiveBefore: "#e85f33"
|
menuActiveBefore: '#e85f33'
|
||||||
},
|
},
|
||||||
yellow: {
|
yellow: {
|
||||||
subMenuActiveText: "#d25f00",
|
subMenuActiveText: '#d25f00',
|
||||||
menuBg: "#2b2503",
|
menuBg: '#2b2503',
|
||||||
menuHover: "#f6da4d",
|
menuHover: '#f6da4d',
|
||||||
subMenuBg: "#0f0603",
|
subMenuBg: '#0f0603',
|
||||||
subMenuActiveBg: "#f6da4d",
|
subMenuActiveBg: '#f6da4d',
|
||||||
menuText: "rgb(254 254 254 / 65%)",
|
menuText: 'rgb(254 254 254 / 65%)',
|
||||||
sidebarLogo: "#443b05",
|
sidebarLogo: '#443b05',
|
||||||
menuTitleHover: "#fff",
|
menuTitleHover: '#fff',
|
||||||
menuActiveBefore: "#f6da4d"
|
menuActiveBefore: '#f6da4d'
|
||||||
},
|
},
|
||||||
mingQing: {
|
mingQing: {
|
||||||
subMenuActiveText: "#fff",
|
subMenuActiveText: '#fff',
|
||||||
menuBg: "#032121",
|
menuBg: '#032121',
|
||||||
menuHover: "#59bfc1",
|
menuHover: '#59bfc1',
|
||||||
subMenuBg: "#000",
|
subMenuBg: '#000',
|
||||||
subMenuActiveBg: "#59bfc1",
|
subMenuActiveBg: '#59bfc1',
|
||||||
menuText: "#7a80b4",
|
menuText: '#7a80b4',
|
||||||
sidebarLogo: "#053434",
|
sidebarLogo: '#053434',
|
||||||
menuTitleHover: "#fff",
|
menuTitleHover: '#fff',
|
||||||
menuActiveBefore: "#59bfc1"
|
menuActiveBefore: '#59bfc1'
|
||||||
},
|
},
|
||||||
auroraGreen: {
|
auroraGreen: {
|
||||||
subMenuActiveText: "#fff",
|
subMenuActiveText: '#fff',
|
||||||
menuBg: "#0b1e15",
|
menuBg: '#0b1e15',
|
||||||
menuHover: "#60ac80",
|
menuHover: '#60ac80',
|
||||||
subMenuBg: "#000",
|
subMenuBg: '#000',
|
||||||
subMenuActiveBg: "#60ac80",
|
subMenuActiveBg: '#60ac80',
|
||||||
menuText: "#7a80b4",
|
menuText: '#7a80b4',
|
||||||
sidebarLogo: "#112f21",
|
sidebarLogo: '#112f21',
|
||||||
menuTitleHover: "#fff",
|
menuTitleHover: '#fff',
|
||||||
menuActiveBefore: "#60ac80"
|
menuActiveBefore: '#60ac80'
|
||||||
},
|
},
|
||||||
pink: {
|
pink: {
|
||||||
subMenuActiveText: "#fff",
|
subMenuActiveText: '#fff',
|
||||||
menuBg: "#28081a",
|
menuBg: '#28081a',
|
||||||
menuHover: "#d84493",
|
menuHover: '#d84493',
|
||||||
subMenuBg: "#000",
|
subMenuBg: '#000',
|
||||||
subMenuActiveBg: "#d84493",
|
subMenuActiveBg: '#d84493',
|
||||||
menuText: "#7a80b4",
|
menuText: '#7a80b4',
|
||||||
sidebarLogo: "#3f0d29",
|
sidebarLogo: '#3f0d29',
|
||||||
menuTitleHover: "#fff",
|
menuTitleHover: '#fff',
|
||||||
menuActiveBefore: "#d84493"
|
menuActiveBefore: '#d84493'
|
||||||
},
|
},
|
||||||
saucePurple: {
|
saucePurple: {
|
||||||
subMenuActiveText: "#fff",
|
subMenuActiveText: '#fff',
|
||||||
menuBg: "#130824",
|
menuBg: '#130824',
|
||||||
menuHover: "#693ac9",
|
menuHover: '#693ac9',
|
||||||
subMenuBg: "#000",
|
subMenuBg: '#000',
|
||||||
subMenuActiveBg: "#693ac9",
|
subMenuActiveBg: '#693ac9',
|
||||||
menuText: "#7a80b4",
|
menuText: '#7a80b4',
|
||||||
sidebarLogo: "#1f0c38",
|
sidebarLogo: '#1f0c38',
|
||||||
menuTitleHover: "#fff",
|
menuTitleHover: '#fff',
|
||||||
menuActiveBefore: "#693ac9"
|
menuActiveBefore: '#693ac9'
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 将预设主题色处理成主题插件所需格式
|
* @description 将预设主题色处理成主题插件所需格式
|
||||||
*/
|
*/
|
||||||
export const genScssMultipleScopeVars = (): multipleScopeVarsOptions[] => {
|
export const genScssMultipleScopeVars = (): multipleScopeVarsOptions[] => {
|
||||||
const result = [] as multipleScopeVarsOptions[];
|
const result = [] as multipleScopeVarsOptions[]
|
||||||
Object.keys(themeColors).forEach(key => {
|
Object.keys(themeColors).forEach(key => {
|
||||||
result.push({
|
result.push({
|
||||||
scopeName: `layout-theme-${key}`,
|
scopeName: `layout-theme-${key}`,
|
||||||
@ -126,7 +126,7 @@ export const genScssMultipleScopeVars = (): multipleScopeVarsOptions[] => {
|
|||||||
$menuTitleHover: ${themeColors[key].menuTitleHover} !default;
|
$menuTitleHover: ${themeColors[key].menuTitleHover} !default;
|
||||||
$menuActiveBefore: ${themeColors[key].menuActiveBefore} !default;
|
$menuActiveBefore: ${themeColors[key].menuActiveBefore} !default;
|
||||||
`
|
`
|
||||||
} as multipleScopeVarsOptions);
|
} as multipleScopeVarsOptions)
|
||||||
});
|
})
|
||||||
return result;
|
return result
|
||||||
};
|
}
|
||||||
|
@ -1,86 +1,86 @@
|
|||||||
import type { IconifyIcon } from "@iconify/vue";
|
import type { IconifyIcon } from '@iconify/vue'
|
||||||
|
|
||||||
export const routerArrays: Array<RouteConfigs> = [
|
export const routerArrays: Array<RouteConfigs> = [
|
||||||
{
|
{
|
||||||
path: "/welcome",
|
path: '/welcome',
|
||||||
parentPath: "/",
|
parentPath: '/',
|
||||||
meta: {
|
meta: {
|
||||||
title: "menus.hshome",
|
title: 'menus.hshome',
|
||||||
icon: "homeFilled"
|
icon: 'homeFilled'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
|
||||||
export type routeMetaType = {
|
export type routeMetaType = {
|
||||||
title?: string;
|
title?: string
|
||||||
icon?: string | IconifyIcon;
|
icon?: string | IconifyIcon
|
||||||
showLink?: boolean;
|
showLink?: boolean
|
||||||
savedPosition?: boolean;
|
savedPosition?: boolean
|
||||||
auths?: Array<string>;
|
auths?: Array<string>
|
||||||
};
|
}
|
||||||
|
|
||||||
export type RouteConfigs = {
|
export type RouteConfigs = {
|
||||||
path?: string;
|
path?: string
|
||||||
parentPath?: string;
|
parentPath?: string
|
||||||
query?: object;
|
query?: object
|
||||||
params?: object;
|
params?: object
|
||||||
meta?: routeMetaType;
|
meta?: routeMetaType
|
||||||
children?: RouteConfigs[];
|
children?: RouteConfigs[]
|
||||||
name?: string;
|
name?: string
|
||||||
};
|
}
|
||||||
|
|
||||||
export type multiTagsType = {
|
export type multiTagsType = {
|
||||||
tags: Array<RouteConfigs>;
|
tags: Array<RouteConfigs>
|
||||||
};
|
}
|
||||||
|
|
||||||
export type tagsViewsType = {
|
export type tagsViewsType = {
|
||||||
icon: string | IconifyIcon;
|
icon: string | IconifyIcon
|
||||||
text: string;
|
text: string
|
||||||
divided: boolean;
|
divided: boolean
|
||||||
disabled: boolean;
|
disabled: boolean
|
||||||
show: boolean;
|
show: boolean
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface setType {
|
export interface setType {
|
||||||
sidebar: {
|
sidebar: {
|
||||||
opened: boolean;
|
opened: boolean
|
||||||
withoutAnimation: boolean;
|
withoutAnimation: boolean
|
||||||
isClickCollapse: boolean;
|
isClickCollapse: boolean
|
||||||
};
|
}
|
||||||
device: string;
|
device: string
|
||||||
fixedHeader: boolean;
|
fixedHeader: boolean
|
||||||
classes: {
|
classes: {
|
||||||
hideSidebar: boolean;
|
hideSidebar: boolean
|
||||||
openSidebar: boolean;
|
openSidebar: boolean
|
||||||
withoutAnimation: boolean;
|
withoutAnimation: boolean
|
||||||
mobile: boolean;
|
mobile: boolean
|
||||||
};
|
}
|
||||||
hideTabs: boolean;
|
hideTabs: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type childrenType = {
|
export type childrenType = {
|
||||||
path?: string;
|
path?: string
|
||||||
noShowingChildren?: boolean;
|
noShowingChildren?: boolean
|
||||||
children?: childrenType[];
|
children?: childrenType[]
|
||||||
value: unknown;
|
value: unknown
|
||||||
meta?: {
|
meta?: {
|
||||||
icon?: string;
|
icon?: string
|
||||||
title?: string;
|
title?: string
|
||||||
showParent?: boolean;
|
showParent?: boolean
|
||||||
extraIcon?: string;
|
extraIcon?: string
|
||||||
};
|
}
|
||||||
showTooltip?: boolean;
|
showTooltip?: boolean
|
||||||
parentId?: number;
|
parentId?: number
|
||||||
pathList?: number[];
|
pathList?: number[]
|
||||||
};
|
}
|
||||||
|
|
||||||
export type themeColorsType = {
|
export type themeColorsType = {
|
||||||
color: string;
|
color: string
|
||||||
themeColor: string;
|
themeColor: string
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface scrollbarDomType extends HTMLElement {
|
export interface scrollbarDomType extends HTMLElement {
|
||||||
wrap?: {
|
wrap?: {
|
||||||
offsetWidth: number;
|
offsetWidth: number
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
64
src/main.ts
64
src/main.ts
@ -1,58 +1,58 @@
|
|||||||
import App from "./App.vue";
|
import App from './App.vue'
|
||||||
import router from "./router";
|
import router from './router'
|
||||||
import { setupStore } from "@/store";
|
import { setupStore } from '@/store'
|
||||||
import ElementPlus from "element-plus";
|
import ElementPlus from 'element-plus'
|
||||||
import { useI18n } from "@/plugins/i18n";
|
import { useI18n } from '@/plugins/i18n'
|
||||||
import { getServerConfig } from "./config";
|
import { getServerConfig } from './config'
|
||||||
import { createApp, Directive } from "vue";
|
import { createApp, Directive } from 'vue'
|
||||||
import { MotionPlugin } from "@vueuse/motion";
|
import { MotionPlugin } from '@vueuse/motion'
|
||||||
// import { useEcharts } from "@/plugins/echarts";
|
// import { useEcharts } from "@/plugins/echarts";
|
||||||
import { injectResponsiveStorage } from "@/utils/responsive";
|
import { injectResponsiveStorage } from '@/utils/responsive'
|
||||||
|
|
||||||
// import Table from "@pureadmin/table";
|
// import Table from "@pureadmin/table";
|
||||||
// import PureDescriptions from "@pureadmin/descriptions";
|
// import PureDescriptions from "@pureadmin/descriptions";
|
||||||
|
|
||||||
// 引入重置样式
|
// 引入重置样式
|
||||||
import "./style/reset.scss";
|
import './style/reset.scss'
|
||||||
// 导入公共样式
|
// 导入公共样式
|
||||||
import "./style/index.scss";
|
import './style/index.scss'
|
||||||
// 一定要在main.ts中导入tailwind.css,防止vite每次hmr都会请求src/style/index.scss整体css文件导致热更新慢的问题
|
// 一定要在main.ts中导入tailwind.css,防止vite每次hmr都会请求src/style/index.scss整体css文件导致热更新慢的问题
|
||||||
import "./style/tailwind.css";
|
import './style/tailwind.css'
|
||||||
import "element-plus/dist/index.css";
|
import 'element-plus/dist/index.css'
|
||||||
// 导入字体图标
|
// 导入字体图标
|
||||||
import "./assets/iconfont/iconfont.js";
|
import './assets/iconfont/iconfont.js'
|
||||||
import "./assets/iconfont/iconfont.css";
|
import './assets/iconfont/iconfont.css'
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App)
|
||||||
|
|
||||||
// 自定义指令
|
// 自定义指令
|
||||||
import * as directives from "@/directives";
|
import * as directives from '@/directives'
|
||||||
Object.keys(directives).forEach(key => {
|
Object.keys(directives).forEach(key => {
|
||||||
app.directive(key, (directives as { [key: string]: Directive })[key]);
|
app.directive(key, (directives as { [key: string]: Directive })[key])
|
||||||
});
|
})
|
||||||
|
|
||||||
// 全局注册`@iconify/vue`图标库
|
// 全局注册`@iconify/vue`图标库
|
||||||
import {
|
import {
|
||||||
IconifyIconOffline,
|
IconifyIconOffline,
|
||||||
IconifyIconOnline,
|
IconifyIconOnline,
|
||||||
FontIcon
|
FontIcon
|
||||||
} from "./components/ReIcon";
|
} from './components/ReIcon'
|
||||||
app.component("IconifyIconOffline", IconifyIconOffline);
|
app.component('IconifyIconOffline', IconifyIconOffline)
|
||||||
app.component("IconifyIconOnline", IconifyIconOnline);
|
app.component('IconifyIconOnline', IconifyIconOnline)
|
||||||
app.component("FontIcon", FontIcon);
|
app.component('FontIcon', FontIcon)
|
||||||
|
|
||||||
// 全局注册按钮级别权限组件
|
// 全局注册按钮级别权限组件
|
||||||
import { Auth } from "@/components/ReAuth";
|
import { Auth } from '@/components/ReAuth'
|
||||||
app.component("Auth", Auth);
|
app.component('Auth', Auth)
|
||||||
|
|
||||||
getServerConfig(app).then(async config => {
|
getServerConfig(app).then(async config => {
|
||||||
app.use(router);
|
app.use(router)
|
||||||
await router.isReady();
|
await router.isReady()
|
||||||
injectResponsiveStorage(app, config);
|
injectResponsiveStorage(app, config)
|
||||||
setupStore(app);
|
setupStore(app)
|
||||||
app.use(MotionPlugin).use(useI18n).use(ElementPlus);
|
app.use(MotionPlugin).use(useI18n).use(ElementPlus)
|
||||||
// .use(useEcharts);
|
// .use(useEcharts);
|
||||||
// .use(Table);
|
// .use(Table);
|
||||||
// .use(PureDescriptions);
|
// .use(PureDescriptions);
|
||||||
app.mount("#app");
|
app.mount('#app')
|
||||||
});
|
})
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { createProdMockServer } from "vite-plugin-mock/es/createProdMockServer";
|
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
|
||||||
|
|
||||||
const modules: Record<string, any> = import.meta.glob("../mock/*.ts", {
|
const modules: Record<string, any> = import.meta.glob('../mock/*.ts', {
|
||||||
eager: true
|
eager: true
|
||||||
});
|
})
|
||||||
const mockModules = [];
|
const mockModules = []
|
||||||
|
|
||||||
Object.keys(modules).forEach(key => {
|
Object.keys(modules).forEach(key => {
|
||||||
mockModules.push(...modules[key].default);
|
mockModules.push(...modules[key].default)
|
||||||
});
|
})
|
||||||
|
|
||||||
export function setupProdMockServer() {
|
export function setupProdMockServer() {
|
||||||
createProdMockServer(mockModules);
|
createProdMockServer(mockModules)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { App } from "vue";
|
import type { App } from 'vue'
|
||||||
import * as echarts from "echarts/core";
|
import * as echarts from 'echarts/core'
|
||||||
import { CanvasRenderer } from "echarts/renderers";
|
import { CanvasRenderer } from 'echarts/renderers'
|
||||||
import { PieChart, BarChart, LineChart } from "echarts/charts";
|
import { PieChart, BarChart, LineChart } from 'echarts/charts'
|
||||||
import {
|
import {
|
||||||
GridComponent,
|
GridComponent,
|
||||||
TitleComponent,
|
TitleComponent,
|
||||||
@ -11,9 +11,9 @@ import {
|
|||||||
TooltipComponent,
|
TooltipComponent,
|
||||||
DataZoomComponent,
|
DataZoomComponent,
|
||||||
VisualMapComponent
|
VisualMapComponent
|
||||||
} from "echarts/components";
|
} from 'echarts/components'
|
||||||
|
|
||||||
const { use } = echarts;
|
const { use } = echarts
|
||||||
|
|
||||||
use([
|
use([
|
||||||
PieChart,
|
PieChart,
|
||||||
@ -28,7 +28,7 @@ use([
|
|||||||
TooltipComponent,
|
TooltipComponent,
|
||||||
DataZoomComponent,
|
DataZoomComponent,
|
||||||
VisualMapComponent
|
VisualMapComponent
|
||||||
]);
|
])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 按需引入echarts
|
* @description 按需引入echarts
|
||||||
@ -36,7 +36,7 @@ use([
|
|||||||
* @see 温馨提示:必须将 `$echarts` 添加到全局 `globalProperties` ,为了配合 https://pure-admin-utils.netlify.app/hooks/useEcharts/useEcharts.html 使用
|
* @see 温馨提示:必须将 `$echarts` 添加到全局 `globalProperties` ,为了配合 https://pure-admin-utils.netlify.app/hooks/useEcharts/useEcharts.html 使用
|
||||||
*/
|
*/
|
||||||
export function useEcharts(app: App) {
|
export function useEcharts(app: App) {
|
||||||
app.config.globalProperties.$echarts = echarts;
|
app.config.globalProperties.$echarts = echarts
|
||||||
}
|
}
|
||||||
|
|
||||||
export default echarts;
|
export default echarts
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { App, Component } from "vue";
|
import { App, Component } from 'vue'
|
||||||
import {
|
import {
|
||||||
ElTag,
|
ElTag,
|
||||||
ElAffix,
|
ElAffix,
|
||||||
@ -62,10 +62,10 @@ import {
|
|||||||
// 指令
|
// 指令
|
||||||
ElLoading,
|
ElLoading,
|
||||||
ElInfiniteScroll
|
ElInfiniteScroll
|
||||||
} from "element-plus";
|
} from 'element-plus'
|
||||||
|
|
||||||
// Directives
|
// Directives
|
||||||
const plugins = [ElLoading, ElInfiniteScroll];
|
const plugins = [ElLoading, ElInfiniteScroll]
|
||||||
|
|
||||||
const components = [
|
const components = [
|
||||||
ElTag,
|
ElTag,
|
||||||
@ -127,15 +127,15 @@ const components = [
|
|||||||
ElResult,
|
ElResult,
|
||||||
ElSteps,
|
ElSteps,
|
||||||
ElStep
|
ElStep
|
||||||
];
|
]
|
||||||
|
|
||||||
export function useElementPlus(app: App) {
|
export function useElementPlus(app: App) {
|
||||||
// 注册组件
|
// 注册组件
|
||||||
components.forEach((component: Component) => {
|
components.forEach((component: Component) => {
|
||||||
app.component(component.name, component);
|
app.component(component.name, component)
|
||||||
});
|
})
|
||||||
// 注册指令
|
// 注册指令
|
||||||
plugins.forEach(plugin => {
|
plugins.forEach(plugin => {
|
||||||
app.use(plugin);
|
app.use(plugin)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,73 @@
|
|||||||
// 多组件库的国际化和本地项目国际化兼容
|
// 多组件库的国际化和本地项目国际化兼容
|
||||||
import { App, WritableComputedRef } from "vue";
|
import { App, WritableComputedRef } from 'vue'
|
||||||
import { storageLocal } from "@pureadmin/utils";
|
import { storageLocal } from '@pureadmin/utils'
|
||||||
import { type I18n, createI18n } from "vue-i18n";
|
import { type I18n, createI18n } from 'vue-i18n'
|
||||||
|
|
||||||
// element-plus国际化
|
// element-plus国际化
|
||||||
import enLocale from "element-plus/lib/locale/lang/en";
|
import enLocale from 'element-plus/lib/locale/lang/en'
|
||||||
import zhLocale from "element-plus/lib/locale/lang/zh-cn";
|
import zhLocale from 'element-plus/lib/locale/lang/zh-cn'
|
||||||
|
|
||||||
function siphonI18n(prefix = "zh-CN") {
|
function siphonI18n(prefix = 'zh-CN') {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(
|
Object.entries(
|
||||||
import.meta.glob("../../locales/*.y(a)?ml", { eager: true })
|
import.meta.glob('../../locales/*.y(a)?ml', { eager: true })
|
||||||
).map(([key, value]: any) => {
|
).map(([key, value]: any) => {
|
||||||
const matched = key.match(/([A-Za-z0-9-_]+)\./i)[1];
|
const matched = key.match(/([A-Za-z0-9-_]+)\./i)[1]
|
||||||
return [matched, value.default];
|
return [matched, value.default]
|
||||||
})
|
})
|
||||||
)[prefix];
|
)[prefix]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const localesConfigs = {
|
export const localesConfigs = {
|
||||||
zh: {
|
zh: {
|
||||||
...siphonI18n("zh-CN"),
|
...siphonI18n('zh-CN'),
|
||||||
...zhLocale
|
...zhLocale
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
...siphonI18n("en"),
|
...siphonI18n('en'),
|
||||||
...enLocale
|
...enLocale
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际化转换工具函数(自动读取根目录locales文件夹下文件进行国际化匹配)
|
* 国际化转换工具函数(自动读取根目录locales文件夹下文件进行国际化匹配)
|
||||||
* @param message message
|
* @param message message
|
||||||
* @returns 转化后的message
|
* @returns 转化后的message
|
||||||
*/
|
*/
|
||||||
export function transformI18n(message: any = "") {
|
export function transformI18n(message: any = '') {
|
||||||
if (!message) {
|
if (!message) {
|
||||||
return "";
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理存储动态路由的title,格式 {zh:"",en:""}
|
// 处理存储动态路由的title,格式 {zh:"",en:""}
|
||||||
if (typeof message === "object") {
|
if (typeof message === 'object') {
|
||||||
const locale: string | WritableComputedRef<string> | any =
|
const locale: string | WritableComputedRef<string> | any =
|
||||||
i18n.global.locale;
|
i18n.global.locale
|
||||||
return message[locale?.value];
|
return message[locale?.value]
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = message.match(/(\S*)\./)?.[1];
|
const key = message.match(/(\S*)\./)?.[1]
|
||||||
if (key && Object.keys(siphonI18n("zh-CN")).includes(key)) {
|
if (key && Object.keys(siphonI18n('zh-CN')).includes(key)) {
|
||||||
return i18n.global.t.call(i18n.global.locale, message);
|
return i18n.global.t.call(i18n.global.locale, message)
|
||||||
} else if (!key && Object.keys(siphonI18n("zh-CN")).includes(message)) {
|
} else if (!key && Object.keys(siphonI18n('zh-CN')).includes(message)) {
|
||||||
// 兼容非嵌套形式的国际化写法
|
// 兼容非嵌套形式的国际化写法
|
||||||
return i18n.global.t.call(i18n.global.locale, message);
|
return i18n.global.t.call(i18n.global.locale, message)
|
||||||
} else {
|
} else {
|
||||||
return message;
|
return message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 此函数只是配合i18n Ally插件来进行国际化智能提示,并无实际意义(只对提示起作用),如果不需要国际化可删除 */
|
/** 此函数只是配合i18n Ally插件来进行国际化智能提示,并无实际意义(只对提示起作用),如果不需要国际化可删除 */
|
||||||
export const $t = (key: string) => key;
|
export const $t = (key: string) => key
|
||||||
|
|
||||||
export const i18n: I18n = createI18n({
|
export const i18n: I18n = createI18n({
|
||||||
legacy: false,
|
legacy: false,
|
||||||
locale:
|
locale:
|
||||||
storageLocal().getItem<StorageConfigs>("responsive-locale")?.locale ?? "zh",
|
storageLocal().getItem<StorageConfigs>('responsive-locale')?.locale ?? 'zh',
|
||||||
fallbackLocale: "en",
|
fallbackLocale: 'en',
|
||||||
messages: localesConfigs
|
messages: localesConfigs
|
||||||
});
|
})
|
||||||
|
|
||||||
export function useI18n(app: App) {
|
export function useI18n(app: App) {
|
||||||
app.use(i18n);
|
app.use(i18n)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
// import "@/utils/sso";
|
// import "@/utils/sso";
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from '@/config'
|
||||||
import NProgress from "@/utils/progress";
|
import NProgress from '@/utils/progress'
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
import { transformI18n } from '@/plugins/i18n'
|
||||||
import { sessionKey, type DataInfo } from "@/utils/auth";
|
import { sessionKey, type DataInfo } from '@/utils/auth'
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from '@/store/modules/multiTags'
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from '@/store/modules/permission'
|
||||||
import {
|
import {
|
||||||
Router,
|
Router,
|
||||||
createRouter,
|
createRouter,
|
||||||
RouteRecordRaw,
|
RouteRecordRaw,
|
||||||
RouteComponent
|
RouteComponent
|
||||||
} from "vue-router";
|
} from 'vue-router'
|
||||||
import {
|
import {
|
||||||
ascending,
|
ascending,
|
||||||
initRouter,
|
initRouter,
|
||||||
@ -20,44 +20,44 @@ import {
|
|||||||
handleAliveRoute,
|
handleAliveRoute,
|
||||||
formatTwoStageRoutes,
|
formatTwoStageRoutes,
|
||||||
formatFlatteningRoutes
|
formatFlatteningRoutes
|
||||||
} from "./utils";
|
} from './utils'
|
||||||
import { buildHierarchyTree } from "@/utils/tree";
|
import { buildHierarchyTree } from '@/utils/tree'
|
||||||
import { isUrl, openLink, storageSession } from "@pureadmin/utils";
|
import { isUrl, openLink, storageSession } from '@pureadmin/utils'
|
||||||
|
|
||||||
import remainingRouter from "./modules/remaining";
|
import remainingRouter from './modules/remaining'
|
||||||
|
|
||||||
/** 自动导入全部静态路由,无需再手动引入!匹配 src/router/modules 目录(任何嵌套级别)中具有 .ts 扩展名的所有文件,除了 remaining.ts 文件
|
/** 自动导入全部静态路由,无需再手动引入!匹配 src/router/modules 目录(任何嵌套级别)中具有 .ts 扩展名的所有文件,除了 remaining.ts 文件
|
||||||
* 如何匹配所有文件请看:https://github.com/mrmlnc/fast-glob#basic-syntax
|
* 如何匹配所有文件请看:https://github.com/mrmlnc/fast-glob#basic-syntax
|
||||||
* 如何排除文件请看:https://cn.vitejs.dev/guide/features.html#negative-patterns
|
* 如何排除文件请看:https://cn.vitejs.dev/guide/features.html#negative-patterns
|
||||||
*/
|
*/
|
||||||
const modules: Record<string, any> = import.meta.glob(
|
const modules: Record<string, any> = import.meta.glob(
|
||||||
["./modules/**/*.ts", "!./modules/**/remaining.ts"],
|
['./modules/**/*.ts', '!./modules/**/remaining.ts'],
|
||||||
{
|
{
|
||||||
eager: true
|
eager: true
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
/** 原始静态路由(未做任何处理) */
|
/** 原始静态路由(未做任何处理) */
|
||||||
const routes = [];
|
const routes = []
|
||||||
|
|
||||||
Object.keys(modules).forEach(key => {
|
Object.keys(modules).forEach(key => {
|
||||||
routes.push(modules[key].default);
|
routes.push(modules[key].default)
|
||||||
});
|
})
|
||||||
|
|
||||||
/** 导出处理后的静态路由(三级及以上的路由全部拍成二级) */
|
/** 导出处理后的静态路由(三级及以上的路由全部拍成二级) */
|
||||||
export const constantRoutes: Array<RouteRecordRaw> = formatTwoStageRoutes(
|
export const constantRoutes: Array<RouteRecordRaw> = formatTwoStageRoutes(
|
||||||
formatFlatteningRoutes(buildHierarchyTree(ascending(routes)))
|
formatFlatteningRoutes(buildHierarchyTree(ascending(routes)))
|
||||||
);
|
)
|
||||||
|
|
||||||
/** 用于渲染菜单,保持原始层级 */
|
/** 用于渲染菜单,保持原始层级 */
|
||||||
export const constantMenus: Array<RouteComponent> = ascending(routes).concat(
|
export const constantMenus: Array<RouteComponent> = ascending(routes).concat(
|
||||||
...remainingRouter
|
...remainingRouter
|
||||||
);
|
)
|
||||||
|
|
||||||
/** 不参与菜单的路由 */
|
/** 不参与菜单的路由 */
|
||||||
export const remainingPaths = Object.keys(remainingRouter).map(v => {
|
export const remainingPaths = Object.keys(remainingRouter).map(v => {
|
||||||
return remainingRouter[v].path;
|
return remainingRouter[v].path
|
||||||
});
|
})
|
||||||
|
|
||||||
/** 创建路由实例 */
|
/** 创建路由实例 */
|
||||||
export const router: Router = createRouter({
|
export const router: Router = createRouter({
|
||||||
@ -67,115 +67,114 @@ export const router: Router = createRouter({
|
|||||||
scrollBehavior(to, from, savedPosition) {
|
scrollBehavior(to, from, savedPosition) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (savedPosition) {
|
if (savedPosition) {
|
||||||
return savedPosition;
|
return savedPosition
|
||||||
} else {
|
} else {
|
||||||
if (from.meta.saveSrollTop) {
|
if (from.meta.saveSrollTop) {
|
||||||
const top: number =
|
const top: number =
|
||||||
document.documentElement.scrollTop || document.body.scrollTop;
|
document.documentElement.scrollTop || document.body.scrollTop
|
||||||
resolve({ left: 0, top });
|
resolve({ left: 0, top })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
/** 重置路由 */
|
/** 重置路由 */
|
||||||
export function resetRouter() {
|
export function resetRouter() {
|
||||||
router.getRoutes().forEach(route => {
|
router.getRoutes().forEach(route => {
|
||||||
const { name, meta } = route;
|
const { name, meta } = route
|
||||||
if (name && router.hasRoute(name) && meta?.backstage) {
|
if (name && router.hasRoute(name) && meta?.backstage) {
|
||||||
router.removeRoute(name);
|
router.removeRoute(name)
|
||||||
router.options.routes = formatTwoStageRoutes(
|
router.options.routes = formatTwoStageRoutes(
|
||||||
formatFlatteningRoutes(buildHierarchyTree(ascending(routes)))
|
formatFlatteningRoutes(buildHierarchyTree(ascending(routes)))
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
usePermissionStoreHook().clearAllCachePage();
|
usePermissionStoreHook().clearAllCachePage()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 路由白名单 */
|
/** 路由白名单 */
|
||||||
const whiteList = ["/login"];
|
const whiteList = ['/login']
|
||||||
|
|
||||||
router.beforeEach((to: toRouteType, _from, next) => {
|
router.beforeEach((to: toRouteType, _from, next) => {
|
||||||
if (to.meta?.keepAlive) {
|
if (to.meta?.keepAlive) {
|
||||||
const newMatched = to.matched;
|
const newMatched = to.matched
|
||||||
handleAliveRoute(newMatched, "add");
|
handleAliveRoute(newMatched, 'add')
|
||||||
// 页面整体刷新和点击标签页刷新
|
// 页面整体刷新和点击标签页刷新
|
||||||
if (_from.name === undefined || _from.name === "Redirect") {
|
if (_from.name === undefined || _from.name === 'Redirect') {
|
||||||
handleAliveRoute(newMatched);
|
handleAliveRoute(newMatched)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const userInfo = storageSession().getItem<DataInfo<number>>(sessionKey);
|
const userInfo = storageSession().getItem<DataInfo<number>>(sessionKey)
|
||||||
NProgress.start();
|
NProgress.start()
|
||||||
const externalLink = isUrl(to?.name as string);
|
const externalLink = isUrl(to?.name as string)
|
||||||
if (!externalLink) {
|
if (!externalLink) {
|
||||||
to.matched.some(item => {
|
to.matched.some(item => {
|
||||||
if (!item.meta.title) return "";
|
if (!item.meta.title) return ''
|
||||||
const Title = getConfig().Title;
|
const Title = getConfig().Title
|
||||||
if (Title)
|
if (Title) document.title = `${transformI18n(item.meta.title)} | ${Title}`
|
||||||
document.title = `${transformI18n(item.meta.title)} | ${Title}`;
|
else document.title = transformI18n(item.meta.title)
|
||||||
else document.title = transformI18n(item.meta.title);
|
})
|
||||||
});
|
|
||||||
}
|
}
|
||||||
/** 如果已经登录并存在登录信息后不能跳转到路由白名单,而是继续保持在当前页面 */
|
/** 如果已经登录并存在登录信息后不能跳转到路由白名单,而是继续保持在当前页面 */
|
||||||
function toCorrectRoute() {
|
function toCorrectRoute() {
|
||||||
whiteList.includes(to.fullPath) ? next(_from.fullPath) : next();
|
whiteList.includes(to.fullPath) ? next(_from.fullPath) : next()
|
||||||
}
|
}
|
||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
// 无权限跳转403页面
|
// 无权限跳转403页面
|
||||||
if (to.meta?.roles && !isOneOfArray(to.meta?.roles, userInfo?.roles)) {
|
if (to.meta?.roles && !isOneOfArray(to.meta?.roles, userInfo?.roles)) {
|
||||||
next({ path: "/error/403" });
|
next({ path: '/error/403' })
|
||||||
}
|
}
|
||||||
if (_from?.name) {
|
if (_from?.name) {
|
||||||
// name为超链接
|
// name为超链接
|
||||||
if (externalLink) {
|
if (externalLink) {
|
||||||
openLink(to?.name as string);
|
openLink(to?.name as string)
|
||||||
NProgress.done();
|
NProgress.done()
|
||||||
} else {
|
} else {
|
||||||
toCorrectRoute();
|
toCorrectRoute()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 刷新
|
// 刷新
|
||||||
if (
|
if (
|
||||||
usePermissionStoreHook().wholeMenus.length === 0 &&
|
usePermissionStoreHook().wholeMenus.length === 0 &&
|
||||||
to.path !== "/login"
|
to.path !== '/login'
|
||||||
) {
|
) {
|
||||||
initRouter().then((router: Router) => {
|
initRouter().then((router: Router) => {
|
||||||
if (!useMultiTagsStoreHook().getMultiTagsCache) {
|
if (!useMultiTagsStoreHook().getMultiTagsCache) {
|
||||||
const { path } = to;
|
const { path } = to
|
||||||
const route = findRouteByPath(
|
const route = findRouteByPath(
|
||||||
path,
|
path,
|
||||||
router.options.routes[0].children
|
router.options.routes[0].children
|
||||||
);
|
)
|
||||||
// query、params模式路由传参数的标签页不在此处处理
|
// query、params模式路由传参数的标签页不在此处处理
|
||||||
if (route && route.meta?.title) {
|
if (route && route.meta?.title) {
|
||||||
useMultiTagsStoreHook().handleTags("push", {
|
useMultiTagsStoreHook().handleTags('push', {
|
||||||
path: route.path,
|
path: route.path,
|
||||||
name: route.name,
|
name: route.name,
|
||||||
meta: route.meta
|
meta: route.meta
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
router.push(to.fullPath);
|
router.push(to.fullPath)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
toCorrectRoute();
|
toCorrectRoute()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (to.path !== "/login") {
|
if (to.path !== '/login') {
|
||||||
if (whiteList.indexOf(to.path) !== -1) {
|
if (whiteList.indexOf(to.path) !== -1) {
|
||||||
next();
|
next()
|
||||||
} else {
|
} else {
|
||||||
next({ path: "/login" });
|
next({ path: '/login' })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
next();
|
next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
router.afterEach(() => {
|
router.afterEach(() => {
|
||||||
NProgress.done();
|
NProgress.done()
|
||||||
});
|
})
|
||||||
|
|
||||||
export default router;
|
export default router
|
||||||
|
62
src/router/modules/basicData.ts
Normal file
62
src/router/modules/basicData.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { $t } from '@/plugins/i18n'
|
||||||
|
|
||||||
|
// 最简代码,也就是这些字段必须有
|
||||||
|
export default {
|
||||||
|
path: '/basic',
|
||||||
|
redirect: '/basic/liveData',
|
||||||
|
meta: {
|
||||||
|
title: $t('menus.basicData'),
|
||||||
|
icon: 'lineChartLine'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/basic/liveData',
|
||||||
|
name: 'LiveData',
|
||||||
|
component: () => import('@/views/basicData/LiveData.vue'),
|
||||||
|
meta: {
|
||||||
|
title: $t('menus.liveData'),
|
||||||
|
roles: ['admin']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/basic/userData',
|
||||||
|
name: 'UserData',
|
||||||
|
component: () => import('@/views/basicData/queryUser/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: $t('menus.userData')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/basic/queryUser',
|
||||||
|
name: 'QueryUser',
|
||||||
|
component: () => import('@/views/basicData/queryUser/queryUser.vue'),
|
||||||
|
meta: {
|
||||||
|
title: $t('menus.searchUser')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/basic/gameData',
|
||||||
|
name: 'GameData',
|
||||||
|
component: () => import('@/views/basicData/GameData.vue'),
|
||||||
|
meta: {
|
||||||
|
title: $t('menus.gameData')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/basic/clubData',
|
||||||
|
name: 'ClubData',
|
||||||
|
component: () => import('@/views/basicData/queryClub/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: $t('menus.clubData')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/basic/queryClub',
|
||||||
|
name: 'QueryData',
|
||||||
|
component: () => import('@/views/basicData/queryClub/queryClub.vue'),
|
||||||
|
meta: {
|
||||||
|
title: $t('menus.searchClub')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,38 +1,38 @@
|
|||||||
import { $t } from "@/plugins/i18n";
|
import { $t } from '@/plugins/i18n'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
path: "/error",
|
path: '/error',
|
||||||
redirect: "/error/403",
|
redirect: '/error/403',
|
||||||
meta: {
|
meta: {
|
||||||
icon: "informationLine",
|
icon: 'signalWifiErrorLine',
|
||||||
title: $t("menus.hsabnormal"),
|
title: $t('menus.hsabnormal'),
|
||||||
// showLink: false,
|
// showLink: false,
|
||||||
rank: 9
|
rank: 9
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/error/403",
|
path: '/error/403',
|
||||||
name: "403",
|
name: '403',
|
||||||
component: () => import("@/views/error/403.vue"),
|
component: () => import('@/views/error/403.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("menus.hsfourZeroOne")
|
title: $t('menus.hsfourZeroOne')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/error/404",
|
path: '/error/404',
|
||||||
name: "404",
|
name: '404',
|
||||||
component: () => import("@/views/error/404.vue"),
|
component: () => import('@/views/error/404.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("menus.hsfourZeroFour")
|
title: $t('menus.hsfourZeroFour')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/error/500",
|
path: '/error/500',
|
||||||
name: "500",
|
name: '500',
|
||||||
component: () => import("@/views/error/500.vue"),
|
component: () => import('@/views/error/500.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("menus.hsFive")
|
title: $t('menus.hsFive')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
} as RouteConfigsTable;
|
} as RouteConfigsTable
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
import { $t } from "@/plugins/i18n";
|
import { $t } from '@/plugins/i18n'
|
||||||
const Layout = () => import("@/layout/index.vue");
|
const Layout = () => import('@/layout/index.vue')
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
path: "/",
|
path: '/',
|
||||||
name: "Home",
|
name: 'Home',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: "/welcome",
|
redirect: '/welcome',
|
||||||
meta: {
|
meta: {
|
||||||
icon: "homeFilled",
|
icon: 'homeSmileLine',
|
||||||
title: $t("menus.hshome"),
|
title: $t('menus.hshome'),
|
||||||
rank: 0
|
rank: 0
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/welcome",
|
path: '/welcome',
|
||||||
name: "Welcome",
|
name: 'Welcome',
|
||||||
component: () => import("@/views/welcome/index.vue"),
|
component: () => import('@/views/welcome/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("menus.hshome")
|
title: $t('menus.hshome')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
} as RouteConfigsTable;
|
} as RouteConfigsTable
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
import { $t } from "@/plugins/i18n";
|
import { $t } from '@/plugins/i18n'
|
||||||
const Layout = () => import("@/layout/index.vue");
|
const Layout = () => import('@/layout/index.vue')
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
path: "/login",
|
path: '/login',
|
||||||
name: "Login",
|
name: 'Login',
|
||||||
component: () => import("@/views/login/index.vue"),
|
component: () => import('@/views/login/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("menus.hslogin"),
|
title: $t('menus.hslogin'),
|
||||||
showLink: false,
|
showLink: false,
|
||||||
rank: 101
|
rank: 101
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/redirect",
|
path: '/redirect',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
meta: {
|
meta: {
|
||||||
icon: "homeFilled",
|
icon: 'homeFilled',
|
||||||
title: $t("menus.hshome"),
|
title: $t('menus.hshome'),
|
||||||
showLink: false,
|
showLink: false,
|
||||||
rank: 102
|
rank: 102
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/redirect/:path(.*)",
|
path: '/redirect/:path(.*)',
|
||||||
name: "Redirect",
|
name: 'Redirect',
|
||||||
component: () => import("@/layout/redirect.vue")
|
component: () => import('@/layout/redirect.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
] as Array<RouteConfigsTable>;
|
] as Array<RouteConfigsTable>
|
||||||
|
@ -5,11 +5,11 @@ import {
|
|||||||
createWebHistory,
|
createWebHistory,
|
||||||
createWebHashHistory,
|
createWebHashHistory,
|
||||||
RouteRecordNormalized
|
RouteRecordNormalized
|
||||||
} from "vue-router";
|
} from 'vue-router'
|
||||||
import { router } from "./index";
|
import { router } from './index'
|
||||||
import { isProxy, toRaw } from "vue";
|
import { isProxy, toRaw } from 'vue'
|
||||||
import { useTimeoutFn } from "@vueuse/core";
|
import { useTimeoutFn } from '@vueuse/core'
|
||||||
import { RouteConfigs } from "@/layout/types";
|
import { RouteConfigs } from '@/layout/types'
|
||||||
import {
|
import {
|
||||||
isString,
|
isString,
|
||||||
cloneDeep,
|
cloneDeep,
|
||||||
@ -17,59 +17,59 @@ import {
|
|||||||
intersection,
|
intersection,
|
||||||
storageSession,
|
storageSession,
|
||||||
isIncludeAllChildren
|
isIncludeAllChildren
|
||||||
} from "@pureadmin/utils";
|
} from '@pureadmin/utils'
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from '@/config'
|
||||||
import { buildHierarchyTree } from "@/utils/tree";
|
import { buildHierarchyTree } from '@/utils/tree'
|
||||||
import { sessionKey, type DataInfo } from "@/utils/auth";
|
import { sessionKey, type DataInfo } from '@/utils/auth'
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from '@/store/modules/permission'
|
||||||
const IFrame = () => import("@/layout/frameView.vue");
|
const IFrame = () => import('@/layout/frameView.vue')
|
||||||
// https://cn.vitejs.dev/guide/features.html#glob-import
|
// https://cn.vitejs.dev/guide/features.html#glob-import
|
||||||
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
|
const modulesRoutes = import.meta.glob('/src/views/**/*.{vue,tsx}')
|
||||||
|
|
||||||
// 动态路由
|
// 动态路由
|
||||||
import { getAsyncRoutes } from "@/api/routes";
|
import { getAsyncRoutes } from '@/api/routes'
|
||||||
|
|
||||||
function handRank(routeInfo: any) {
|
function handRank(routeInfo: any) {
|
||||||
const { name, path, parentId, meta } = routeInfo;
|
const { name, path, parentId, meta } = routeInfo
|
||||||
return isAllEmpty(parentId)
|
return isAllEmpty(parentId)
|
||||||
? isAllEmpty(meta?.rank) ||
|
? isAllEmpty(meta?.rank) ||
|
||||||
(meta?.rank === 0 && name !== "Home" && path !== "/")
|
(meta?.rank === 0 && name !== 'Home' && path !== '/')
|
||||||
? true
|
? true
|
||||||
: false
|
: false
|
||||||
: false;
|
: false
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 按照路由中meta下的rank等级升序来排序路由 */
|
/** 按照路由中meta下的rank等级升序来排序路由 */
|
||||||
function ascending(arr: any[]) {
|
function ascending(arr: any[]) {
|
||||||
arr.forEach((v, index) => {
|
arr.forEach((v, index) => {
|
||||||
// 当rank不存在时,根据顺序自动创建,首页路由永远在第一位
|
// 当rank不存在时,根据顺序自动创建,首页路由永远在第一位
|
||||||
if (handRank(v)) v.meta.rank = index + 2;
|
if (handRank(v)) v.meta.rank = index + 2
|
||||||
});
|
})
|
||||||
return arr.sort(
|
return arr.sort(
|
||||||
(a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
|
(a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
|
||||||
return a?.meta.rank - b?.meta.rank;
|
return a?.meta.rank - b?.meta.rank
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 过滤meta中showLink为false的菜单 */
|
/** 过滤meta中showLink为false的菜单 */
|
||||||
function filterTree(data: RouteComponent[]) {
|
function filterTree(data: RouteComponent[]) {
|
||||||
const newTree = cloneDeep(data).filter(
|
const newTree = cloneDeep(data).filter(
|
||||||
(v: { meta: { showLink: boolean } }) => v.meta?.showLink !== false
|
(v: { meta: { showLink: boolean } }) => v.meta?.showLink !== false
|
||||||
);
|
)
|
||||||
newTree.forEach(
|
newTree.forEach(
|
||||||
(v: { children }) => v.children && (v.children = filterTree(v.children))
|
(v: { children }) => v.children && (v.children = filterTree(v.children))
|
||||||
);
|
)
|
||||||
return newTree;
|
return newTree
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 过滤children长度为0的的目录,当目录下没有菜单时,会过滤此目录,目录没有赋予roles权限,当目录下只要有一个菜单有显示权限,那么此目录就会显示 */
|
/** 过滤children长度为0的的目录,当目录下没有菜单时,会过滤此目录,目录没有赋予roles权限,当目录下只要有一个菜单有显示权限,那么此目录就会显示 */
|
||||||
function filterChildrenTree(data: RouteComponent[]) {
|
function filterChildrenTree(data: RouteComponent[]) {
|
||||||
const newTree = cloneDeep(data).filter((v: any) => v?.children?.length !== 0);
|
const newTree = cloneDeep(data).filter((v: any) => v?.children?.length !== 0)
|
||||||
newTree.forEach(
|
newTree.forEach(
|
||||||
(v: { children }) => v.children && (v.children = filterTree(v.children))
|
(v: { children }) => v.children && (v.children = filterTree(v.children))
|
||||||
);
|
)
|
||||||
return newTree;
|
return newTree
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 判断两个数组彼此是否存在相同值 */
|
/** 判断两个数组彼此是否存在相同值 */
|
||||||
@ -78,30 +78,30 @@ function isOneOfArray(a: Array<string>, b: Array<string>) {
|
|||||||
? intersection(a, b).length > 0
|
? intersection(a, b).length > 0
|
||||||
? true
|
? true
|
||||||
: false
|
: false
|
||||||
: true;
|
: true
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 从sessionStorage里取出当前登陆用户的角色roles,过滤无权限的菜单 */
|
/** 从sessionStorage里取出当前登陆用户的角色roles,过滤无权限的菜单 */
|
||||||
function filterNoPermissionTree(data: RouteComponent[]) {
|
function filterNoPermissionTree(data: RouteComponent[]) {
|
||||||
const currentRoles =
|
const currentRoles =
|
||||||
storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? [];
|
storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? []
|
||||||
const newTree = cloneDeep(data).filter((v: any) =>
|
const newTree = cloneDeep(data).filter((v: any) =>
|
||||||
isOneOfArray(v.meta?.roles, currentRoles)
|
isOneOfArray(v.meta?.roles, currentRoles)
|
||||||
);
|
)
|
||||||
newTree.forEach(
|
newTree.forEach(
|
||||||
(v: any) => v.children && (v.children = filterNoPermissionTree(v.children))
|
(v: any) => v.children && (v.children = filterNoPermissionTree(v.children))
|
||||||
);
|
)
|
||||||
return filterChildrenTree(newTree);
|
return filterChildrenTree(newTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 批量删除缓存路由(keepalive) */
|
/** 批量删除缓存路由(keepalive) */
|
||||||
function delAliveRoutes(delAliveRouteList: Array<RouteConfigs>) {
|
function delAliveRoutes(delAliveRouteList: Array<RouteConfigs>) {
|
||||||
delAliveRouteList.forEach(route => {
|
delAliveRouteList.forEach(route => {
|
||||||
usePermissionStoreHook().cacheOperate({
|
usePermissionStoreHook().cacheOperate({
|
||||||
mode: "delete",
|
mode: 'delete',
|
||||||
name: route?.name
|
name: route?.name
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 通过path获取父级路径 */
|
/** 通过path获取父级路径 */
|
||||||
@ -109,60 +109,60 @@ function getParentPaths(path: string, routes: RouteRecordRaw[]) {
|
|||||||
// 深度遍历查找
|
// 深度遍历查找
|
||||||
function dfs(routes: RouteRecordRaw[], path: string, parents: string[]) {
|
function dfs(routes: RouteRecordRaw[], path: string, parents: string[]) {
|
||||||
for (let i = 0; i < routes.length; i++) {
|
for (let i = 0; i < routes.length; i++) {
|
||||||
const item = routes[i];
|
const item = routes[i]
|
||||||
// 找到path则返回父级path
|
// 找到path则返回父级path
|
||||||
if (item.path === path) return parents;
|
if (item.path === path) return parents
|
||||||
// children不存在或为空则不递归
|
// children不存在或为空则不递归
|
||||||
if (!item.children || !item.children.length) continue;
|
if (!item.children || !item.children.length) continue
|
||||||
// 往下查找时将当前path入栈
|
// 往下查找时将当前path入栈
|
||||||
parents.push(item.path);
|
parents.push(item.path)
|
||||||
|
|
||||||
if (dfs(item.children, path, parents).length) return parents;
|
if (dfs(item.children, path, parents).length) return parents
|
||||||
// 深度遍历查找未找到时当前path 出栈
|
// 深度遍历查找未找到时当前path 出栈
|
||||||
parents.pop();
|
parents.pop()
|
||||||
}
|
}
|
||||||
// 未找到时返回空数组
|
// 未找到时返回空数组
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return dfs(routes, path, []);
|
return dfs(routes, path, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 查找对应path的路由信息 */
|
/** 查找对应path的路由信息 */
|
||||||
function findRouteByPath(path: string, routes: RouteRecordRaw[]) {
|
function findRouteByPath(path: string, routes: RouteRecordRaw[]) {
|
||||||
let res = routes.find((item: { path: string }) => item.path == path);
|
let res = routes.find((item: { path: string }) => item.path == path)
|
||||||
if (res) {
|
if (res) {
|
||||||
return isProxy(res) ? toRaw(res) : res;
|
return isProxy(res) ? toRaw(res) : res
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < routes.length; i++) {
|
for (let i = 0; i < routes.length; i++) {
|
||||||
if (
|
if (
|
||||||
routes[i].children instanceof Array &&
|
routes[i].children instanceof Array &&
|
||||||
routes[i].children.length > 0
|
routes[i].children.length > 0
|
||||||
) {
|
) {
|
||||||
res = findRouteByPath(path, routes[i].children);
|
res = findRouteByPath(path, routes[i].children)
|
||||||
if (res) {
|
if (res) {
|
||||||
return isProxy(res) ? toRaw(res) : res;
|
return isProxy(res) ? toRaw(res) : res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPathMatch() {
|
function addPathMatch() {
|
||||||
if (!router.hasRoute("pathMatch")) {
|
if (!router.hasRoute('pathMatch')) {
|
||||||
router.addRoute({
|
router.addRoute({
|
||||||
path: "/:pathMatch(.*)",
|
path: '/:pathMatch(.*)',
|
||||||
name: "pathMatch",
|
name: 'pathMatch',
|
||||||
redirect: "/error/404"
|
redirect: '/error/404'
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理动态路由(后端返回的路由) */
|
/** 处理动态路由(后端返回的路由) */
|
||||||
function handleAsyncRoutes(routeList) {
|
function handleAsyncRoutes(routeList) {
|
||||||
if (routeList.length === 0) {
|
if (routeList.length === 0) {
|
||||||
usePermissionStoreHook().handleWholeMenus(routeList);
|
usePermissionStoreHook().handleWholeMenus(routeList)
|
||||||
} else {
|
} else {
|
||||||
formatFlatteningRoutes(addAsyncRoutes(routeList)).map(
|
formatFlatteningRoutes(addAsyncRoutes(routeList)).map(
|
||||||
(v: RouteRecordRaw) => {
|
(v: RouteRecordRaw) => {
|
||||||
@ -172,52 +172,52 @@ function handleAsyncRoutes(routeList) {
|
|||||||
value => value.path === v.path
|
value => value.path === v.path
|
||||||
) !== -1
|
) !== -1
|
||||||
) {
|
) {
|
||||||
return;
|
return
|
||||||
} else {
|
} else {
|
||||||
// 切记将路由push到routes后还需要使用addRoute,这样路由才能正常跳转
|
// 切记将路由push到routes后还需要使用addRoute,这样路由才能正常跳转
|
||||||
router.options.routes[0].children.push(v);
|
router.options.routes[0].children.push(v)
|
||||||
// 最终路由进行升序
|
// 最终路由进行升序
|
||||||
ascending(router.options.routes[0].children);
|
ascending(router.options.routes[0].children)
|
||||||
if (!router.hasRoute(v?.name)) router.addRoute(v);
|
if (!router.hasRoute(v?.name)) router.addRoute(v)
|
||||||
const flattenRouters: any = router
|
const flattenRouters: any = router
|
||||||
.getRoutes()
|
.getRoutes()
|
||||||
.find(n => n.path === "/");
|
.find(n => n.path === '/')
|
||||||
router.addRoute(flattenRouters);
|
router.addRoute(flattenRouters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
usePermissionStoreHook().handleWholeMenus(routeList);
|
usePermissionStoreHook().handleWholeMenus(routeList)
|
||||||
}
|
}
|
||||||
addPathMatch();
|
addPathMatch()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 初始化路由(`new Promise` 写法防止在异步请求中造成无限循环)*/
|
/** 初始化路由(`new Promise` 写法防止在异步请求中造成无限循环)*/
|
||||||
function initRouter() {
|
function initRouter() {
|
||||||
if (getConfig()?.CachingAsyncRoutes) {
|
if (getConfig()?.CachingAsyncRoutes) {
|
||||||
// 开启动态路由缓存本地sessionStorage
|
// 开启动态路由缓存本地sessionStorage
|
||||||
const key = "async-routes";
|
const key = 'async-routes'
|
||||||
const asyncRouteList = storageSession().getItem(key) as any;
|
const asyncRouteList = storageSession().getItem(key) as any
|
||||||
if (asyncRouteList && asyncRouteList?.length > 0) {
|
if (asyncRouteList && asyncRouteList?.length > 0) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
handleAsyncRoutes(asyncRouteList);
|
handleAsyncRoutes(asyncRouteList)
|
||||||
resolve(router);
|
resolve(router)
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
getAsyncRoutes().then(({ data }) => {
|
getAsyncRoutes().then(({ data }) => {
|
||||||
handleAsyncRoutes(cloneDeep(data));
|
handleAsyncRoutes(cloneDeep(data))
|
||||||
storageSession().setItem(key, data);
|
storageSession().setItem(key, data)
|
||||||
resolve(router);
|
resolve(router)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
getAsyncRoutes().then(({ data }) => {
|
getAsyncRoutes().then(({ data }) => {
|
||||||
handleAsyncRoutes(cloneDeep(data));
|
handleAsyncRoutes(cloneDeep(data))
|
||||||
resolve(router);
|
resolve(router)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,16 +227,16 @@ function initRouter() {
|
|||||||
* @returns 返回处理后的一维路由
|
* @returns 返回处理后的一维路由
|
||||||
*/
|
*/
|
||||||
function formatFlatteningRoutes(routesList: RouteRecordRaw[]) {
|
function formatFlatteningRoutes(routesList: RouteRecordRaw[]) {
|
||||||
if (routesList.length === 0) return routesList;
|
if (routesList.length === 0) return routesList
|
||||||
let hierarchyList = buildHierarchyTree(routesList);
|
let hierarchyList = buildHierarchyTree(routesList)
|
||||||
for (let i = 0; i < hierarchyList.length; i++) {
|
for (let i = 0; i < hierarchyList.length; i++) {
|
||||||
if (hierarchyList[i].children) {
|
if (hierarchyList[i].children) {
|
||||||
hierarchyList = hierarchyList
|
hierarchyList = hierarchyList
|
||||||
.slice(0, i + 1)
|
.slice(0, i + 1)
|
||||||
.concat(hierarchyList[i].children, hierarchyList.slice(i + 1));
|
.concat(hierarchyList[i].children, hierarchyList.slice(i + 1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hierarchyList;
|
return hierarchyList
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,10 +246,10 @@ function formatFlatteningRoutes(routesList: RouteRecordRaw[]) {
|
|||||||
* @returns 返回将一维数组重新处理成规定路由的格式
|
* @returns 返回将一维数组重新处理成规定路由的格式
|
||||||
*/
|
*/
|
||||||
function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
|
function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
|
||||||
if (routesList.length === 0) return routesList;
|
if (routesList.length === 0) return routesList
|
||||||
const newRoutesList: RouteRecordRaw[] = [];
|
const newRoutesList: RouteRecordRaw[] = []
|
||||||
routesList.forEach((v: RouteRecordRaw) => {
|
routesList.forEach((v: RouteRecordRaw) => {
|
||||||
if (v.path === "/") {
|
if (v.path === '/') {
|
||||||
newRoutesList.push({
|
newRoutesList.push({
|
||||||
component: v.component,
|
component: v.component,
|
||||||
name: v.name,
|
name: v.name,
|
||||||
@ -257,108 +257,108 @@ function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
|
|||||||
redirect: v.redirect,
|
redirect: v.redirect,
|
||||||
meta: v.meta,
|
meta: v.meta,
|
||||||
children: []
|
children: []
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
newRoutesList[0]?.children.push({ ...v });
|
newRoutesList[0]?.children.push({ ...v })
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return newRoutesList;
|
return newRoutesList
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理缓存路由(添加、删除、刷新) */
|
/** 处理缓存路由(添加、删除、刷新) */
|
||||||
function handleAliveRoute(matched: RouteRecordNormalized[], mode?: string) {
|
function handleAliveRoute(matched: RouteRecordNormalized[], mode?: string) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "add":
|
case 'add':
|
||||||
matched.forEach(v => {
|
matched.forEach(v => {
|
||||||
usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
|
usePermissionStoreHook().cacheOperate({ mode: 'add', name: v.name })
|
||||||
});
|
})
|
||||||
break;
|
break
|
||||||
case "delete":
|
case 'delete':
|
||||||
usePermissionStoreHook().cacheOperate({
|
usePermissionStoreHook().cacheOperate({
|
||||||
mode: "delete",
|
mode: 'delete',
|
||||||
name: matched[matched.length - 1].name
|
name: matched[matched.length - 1].name
|
||||||
});
|
})
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
usePermissionStoreHook().cacheOperate({
|
usePermissionStoreHook().cacheOperate({
|
||||||
mode: "delete",
|
mode: 'delete',
|
||||||
name: matched[matched.length - 1].name
|
name: matched[matched.length - 1].name
|
||||||
});
|
})
|
||||||
useTimeoutFn(() => {
|
useTimeoutFn(() => {
|
||||||
matched.forEach(v => {
|
matched.forEach(v => {
|
||||||
usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
|
usePermissionStoreHook().cacheOperate({ mode: 'add', name: v.name })
|
||||||
});
|
})
|
||||||
}, 100);
|
}, 100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 过滤后端传来的动态路由 重新生成规范路由 */
|
/** 过滤后端传来的动态路由 重新生成规范路由 */
|
||||||
function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
|
function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
|
||||||
if (!arrRoutes || !arrRoutes.length) return;
|
if (!arrRoutes || !arrRoutes.length) return
|
||||||
const modulesRoutesKeys = Object.keys(modulesRoutes);
|
const modulesRoutesKeys = Object.keys(modulesRoutes)
|
||||||
arrRoutes.forEach((v: RouteRecordRaw) => {
|
arrRoutes.forEach((v: RouteRecordRaw) => {
|
||||||
// 将backstage属性加入meta,标识此路由为后端返回路由
|
// 将backstage属性加入meta,标识此路由为后端返回路由
|
||||||
v.meta.backstage = true;
|
v.meta.backstage = true
|
||||||
// 父级的redirect属性取值:如果子级存在且父级的redirect属性不存在,默认取第一个子级的path;如果子级存在且父级的redirect属性存在,取存在的redirect属性,会覆盖默认值
|
// 父级的redirect属性取值:如果子级存在且父级的redirect属性不存在,默认取第一个子级的path;如果子级存在且父级的redirect属性存在,取存在的redirect属性,会覆盖默认值
|
||||||
if (v?.children && v.children.length && !v.redirect)
|
if (v?.children && v.children.length && !v.redirect)
|
||||||
v.redirect = v.children[0].path;
|
v.redirect = v.children[0].path
|
||||||
// 父级的name属性取值:如果子级存在且父级的name属性不存在,默认取第一个子级的name;如果子级存在且父级的name属性存在,取存在的name属性,会覆盖默认值(注意:测试中发现父级的name不能和子级name重复,如果重复会造成重定向无效(跳转404),所以这里给父级的name起名的时候后面会自动加上`Parent`,避免重复)
|
// 父级的name属性取值:如果子级存在且父级的name属性不存在,默认取第一个子级的name;如果子级存在且父级的name属性存在,取存在的name属性,会覆盖默认值(注意:测试中发现父级的name不能和子级name重复,如果重复会造成重定向无效(跳转404),所以这里给父级的name起名的时候后面会自动加上`Parent`,避免重复)
|
||||||
if (v?.children && v.children.length && !v.name)
|
if (v?.children && v.children.length && !v.name)
|
||||||
v.name = (v.children[0].name as string) + "Parent";
|
v.name = (v.children[0].name as string) + 'Parent'
|
||||||
if (v.meta?.frameSrc) {
|
if (v.meta?.frameSrc) {
|
||||||
v.component = IFrame;
|
v.component = IFrame
|
||||||
} else {
|
} else {
|
||||||
// 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会跟path保持一致)
|
// 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会跟path保持一致)
|
||||||
const index = v?.component
|
const index = v?.component
|
||||||
? modulesRoutesKeys.findIndex(ev => ev.includes(v.component as any))
|
? modulesRoutesKeys.findIndex(ev => ev.includes(v.component as any))
|
||||||
: modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
|
: modulesRoutesKeys.findIndex(ev => ev.includes(v.path))
|
||||||
v.component = modulesRoutes[modulesRoutesKeys[index]];
|
v.component = modulesRoutes[modulesRoutesKeys[index]]
|
||||||
}
|
}
|
||||||
if (v?.children && v.children.length) {
|
if (v?.children && v.children.length) {
|
||||||
addAsyncRoutes(v.children);
|
addAsyncRoutes(v.children)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return arrRoutes;
|
return arrRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */
|
/** 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */
|
||||||
function getHistoryMode(routerHistory): RouterHistory {
|
function getHistoryMode(routerHistory): RouterHistory {
|
||||||
// len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
|
// len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
|
||||||
const historyMode = routerHistory.split(",");
|
const historyMode = routerHistory.split(',')
|
||||||
const leftMode = historyMode[0];
|
const leftMode = historyMode[0]
|
||||||
const rightMode = historyMode[1];
|
const rightMode = historyMode[1]
|
||||||
// no param
|
// no param
|
||||||
if (historyMode.length === 1) {
|
if (historyMode.length === 1) {
|
||||||
if (leftMode === "hash") {
|
if (leftMode === 'hash') {
|
||||||
return createWebHashHistory("");
|
return createWebHashHistory('')
|
||||||
} else if (leftMode === "h5") {
|
} else if (leftMode === 'h5') {
|
||||||
return createWebHistory("");
|
return createWebHistory('')
|
||||||
}
|
}
|
||||||
} //has param
|
} //has param
|
||||||
else if (historyMode.length === 2) {
|
else if (historyMode.length === 2) {
|
||||||
if (leftMode === "hash") {
|
if (leftMode === 'hash') {
|
||||||
return createWebHashHistory(rightMode);
|
return createWebHashHistory(rightMode)
|
||||||
} else if (leftMode === "h5") {
|
} else if (leftMode === 'h5') {
|
||||||
return createWebHistory(rightMode);
|
return createWebHistory(rightMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取当前页面按钮级别的权限 */
|
/** 获取当前页面按钮级别的权限 */
|
||||||
function getAuths(): Array<string> {
|
function getAuths(): Array<string> {
|
||||||
return router.currentRoute.value.meta.auths as Array<string>;
|
return router.currentRoute.value.meta.auths as Array<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 是否有按钮级别的权限 */
|
/** 是否有按钮级别的权限 */
|
||||||
function hasAuth(value: string | Array<string>): boolean {
|
function hasAuth(value: string | Array<string>): boolean {
|
||||||
if (!value) return false;
|
if (!value) return false
|
||||||
/** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */
|
/** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */
|
||||||
const metaAuths = getAuths();
|
const metaAuths = getAuths()
|
||||||
if (!metaAuths) return false;
|
if (!metaAuths) return false
|
||||||
const isAuths = isString(value)
|
const isAuths = isString(value)
|
||||||
? metaAuths.includes(value)
|
? metaAuths.includes(value)
|
||||||
: isIncludeAllChildren(value, metaAuths);
|
: isIncludeAllChildren(value, metaAuths)
|
||||||
return isAuths ? true : false;
|
return isAuths ? true : false
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -378,4 +378,4 @@ export {
|
|||||||
formatTwoStageRoutes,
|
formatTwoStageRoutes,
|
||||||
formatFlatteningRoutes,
|
formatFlatteningRoutes,
|
||||||
filterNoPermissionTree
|
filterNoPermissionTree
|
||||||
};
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import type { App } from "vue";
|
import type { App } from 'vue'
|
||||||
import { createPinia } from "pinia";
|
import { createPinia } from 'pinia'
|
||||||
const store = createPinia();
|
const store = createPinia()
|
||||||
|
|
||||||
export function setupStore(app: App<Element>) {
|
export function setupStore(app: App<Element>) {
|
||||||
app.use(store);
|
app.use(store)
|
||||||
}
|
}
|
||||||
|
|
||||||
export { store };
|
export { store }
|
||||||
|
@ -1,65 +1,64 @@
|
|||||||
import { store } from "@/store";
|
import { store } from '@/store'
|
||||||
import { appType } from "./types";
|
import { appType } from './types'
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from 'pinia'
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from '@/config'
|
||||||
import { deviceDetection, storageLocal } from "@pureadmin/utils";
|
import { deviceDetection, storageLocal } from '@pureadmin/utils'
|
||||||
|
|
||||||
export const useAppStore = defineStore({
|
export const useAppStore = defineStore({
|
||||||
id: "pure-app",
|
id: 'pure-app',
|
||||||
state: (): appType => ({
|
state: (): appType => ({
|
||||||
sidebar: {
|
sidebar: {
|
||||||
opened:
|
opened:
|
||||||
storageLocal().getItem<StorageConfigs>("responsive-layout")
|
storageLocal().getItem<StorageConfigs>('responsive-layout')
|
||||||
?.sidebarStatus ?? getConfig().SidebarStatus,
|
?.sidebarStatus ?? getConfig().SidebarStatus,
|
||||||
withoutAnimation: false,
|
withoutAnimation: false,
|
||||||
isClickCollapse: false
|
isClickCollapse: false
|
||||||
},
|
},
|
||||||
// 这里的layout用于监听容器拖拉后恢复对应的导航模式
|
// 这里的layout用于监听容器拖拉后恢复对应的导航模式
|
||||||
layout:
|
layout:
|
||||||
storageLocal().getItem<StorageConfigs>("responsive-layout")?.layout ??
|
storageLocal().getItem<StorageConfigs>('responsive-layout')?.layout ??
|
||||||
getConfig().Layout,
|
getConfig().Layout,
|
||||||
device: deviceDetection() ? "mobile" : "desktop"
|
device: deviceDetection() ? 'mobile' : 'desktop'
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getSidebarStatus() {
|
getSidebarStatus() {
|
||||||
return this.sidebar.opened;
|
return this.sidebar.opened
|
||||||
},
|
},
|
||||||
getDevice() {
|
getDevice() {
|
||||||
return this.device;
|
return this.device
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
TOGGLE_SIDEBAR(opened?: boolean, resize?: string) {
|
TOGGLE_SIDEBAR(opened?: boolean, resize?: string) {
|
||||||
const layout =
|
const layout = storageLocal().getItem<StorageConfigs>('responsive-layout')
|
||||||
storageLocal().getItem<StorageConfigs>("responsive-layout");
|
|
||||||
if (opened && resize) {
|
if (opened && resize) {
|
||||||
this.sidebar.withoutAnimation = true;
|
this.sidebar.withoutAnimation = true
|
||||||
this.sidebar.opened = true;
|
this.sidebar.opened = true
|
||||||
layout.sidebarStatus = true;
|
layout.sidebarStatus = true
|
||||||
} else if (!opened && resize) {
|
} else if (!opened && resize) {
|
||||||
this.sidebar.withoutAnimation = true;
|
this.sidebar.withoutAnimation = true
|
||||||
this.sidebar.opened = false;
|
this.sidebar.opened = false
|
||||||
layout.sidebarStatus = false;
|
layout.sidebarStatus = false
|
||||||
} else if (!opened && !resize) {
|
} else if (!opened && !resize) {
|
||||||
this.sidebar.withoutAnimation = false;
|
this.sidebar.withoutAnimation = false
|
||||||
this.sidebar.opened = !this.sidebar.opened;
|
this.sidebar.opened = !this.sidebar.opened
|
||||||
this.sidebar.isClickCollapse = !this.sidebar.opened;
|
this.sidebar.isClickCollapse = !this.sidebar.opened
|
||||||
layout.sidebarStatus = this.sidebar.opened;
|
layout.sidebarStatus = this.sidebar.opened
|
||||||
}
|
}
|
||||||
storageLocal().setItem("responsive-layout", layout);
|
storageLocal().setItem('responsive-layout', layout)
|
||||||
},
|
},
|
||||||
async toggleSideBar(opened?: boolean, resize?: string) {
|
async toggleSideBar(opened?: boolean, resize?: string) {
|
||||||
await this.TOGGLE_SIDEBAR(opened, resize);
|
await this.TOGGLE_SIDEBAR(opened, resize)
|
||||||
},
|
},
|
||||||
toggleDevice(device: string) {
|
toggleDevice(device: string) {
|
||||||
this.device = device;
|
this.device = device
|
||||||
},
|
},
|
||||||
setLayout(layout) {
|
setLayout(layout) {
|
||||||
this.layout = layout;
|
this.layout = layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export function useAppStoreHook() {
|
export function useAppStoreHook() {
|
||||||
return useAppStore(store);
|
return useAppStore(store)
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,45 @@
|
|||||||
import { store } from "@/store";
|
import { store } from '@/store'
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from 'pinia'
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from '@/config'
|
||||||
import { storageLocal } from "@pureadmin/utils";
|
import { storageLocal } from '@pureadmin/utils'
|
||||||
|
|
||||||
export const useEpThemeStore = defineStore({
|
export const useEpThemeStore = defineStore({
|
||||||
id: "pure-epTheme",
|
id: 'pure-epTheme',
|
||||||
state: () => ({
|
state: () => ({
|
||||||
epThemeColor:
|
epThemeColor:
|
||||||
storageLocal().getItem<StorageConfigs>("responsive-layout")
|
storageLocal().getItem<StorageConfigs>('responsive-layout')
|
||||||
?.epThemeColor ?? getConfig().EpThemeColor,
|
?.epThemeColor ?? getConfig().EpThemeColor,
|
||||||
epTheme:
|
epTheme:
|
||||||
storageLocal().getItem<StorageConfigs>("responsive-layout")?.theme ??
|
storageLocal().getItem<StorageConfigs>('responsive-layout')?.theme ??
|
||||||
getConfig().Theme
|
getConfig().Theme
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getEpThemeColor() {
|
getEpThemeColor() {
|
||||||
return this.epThemeColor;
|
return this.epThemeColor
|
||||||
},
|
},
|
||||||
/** 用于mix导航模式下hamburger-svg的fill属性 */
|
/** 用于mix导航模式下hamburger-svg的fill属性 */
|
||||||
fill() {
|
fill() {
|
||||||
if (this.epTheme === "light") {
|
if (this.epTheme === 'light') {
|
||||||
return "#409eff";
|
return '#409eff'
|
||||||
} else if (this.epTheme === "yellow") {
|
} else if (this.epTheme === 'yellow') {
|
||||||
return "#d25f00";
|
return '#d25f00'
|
||||||
} else {
|
} else {
|
||||||
return "#fff";
|
return '#fff'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setEpThemeColor(newColor: string): void {
|
setEpThemeColor(newColor: string): void {
|
||||||
const layout =
|
const layout = storageLocal().getItem<StorageConfigs>('responsive-layout')
|
||||||
storageLocal().getItem<StorageConfigs>("responsive-layout");
|
this.epTheme = layout?.theme
|
||||||
this.epTheme = layout?.theme;
|
this.epThemeColor = newColor
|
||||||
this.epThemeColor = newColor;
|
if (!layout) return
|
||||||
if (!layout) return;
|
layout.epThemeColor = newColor
|
||||||
layout.epThemeColor = newColor;
|
storageLocal().setItem('responsive-layout', layout)
|
||||||
storageLocal().setItem("responsive-layout", layout);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export function useEpThemeStoreHook() {
|
export function useEpThemeStoreHook() {
|
||||||
return useEpThemeStore(store);
|
return useEpThemeStore(store)
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from 'pinia'
|
||||||
import { store } from "@/store";
|
import { store } from '@/store'
|
||||||
import { routerArrays } from "@/layout/types";
|
import { routerArrays } from '@/layout/types'
|
||||||
import { multiType, positionType } from "./types";
|
import { multiType, positionType } from './types'
|
||||||
import { isEqual, isBoolean, isUrl, storageLocal } from "@pureadmin/utils";
|
import { isEqual, isBoolean, isUrl, storageLocal } from '@pureadmin/utils'
|
||||||
|
|
||||||
export const useMultiTagsStore = defineStore({
|
export const useMultiTagsStore = defineStore({
|
||||||
id: "pure-multiTags",
|
id: 'pure-multiTags',
|
||||||
state: () => ({
|
state: () => ({
|
||||||
// 存储标签页信息(路由信息)
|
// 存储标签页信息(路由信息)
|
||||||
multiTags: storageLocal().getItem<StorageConfigs>("responsive-configure")
|
multiTags: storageLocal().getItem<StorageConfigs>('responsive-configure')
|
||||||
?.multiTagsCache
|
?.multiTagsCache
|
||||||
? storageLocal().getItem<StorageConfigs>("responsive-tags")
|
? storageLocal().getItem<StorageConfigs>('responsive-tags')
|
||||||
: [...routerArrays],
|
: [...routerArrays],
|
||||||
multiTagsCache: storageLocal().getItem<StorageConfigs>(
|
multiTagsCache: storageLocal().getItem<StorageConfigs>(
|
||||||
"responsive-configure"
|
'responsive-configure'
|
||||||
)?.multiTagsCache
|
)?.multiTagsCache
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getMultiTagsCache() {
|
getMultiTagsCache() {
|
||||||
return this.multiTagsCache;
|
return this.multiTagsCache
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
multiTagsCacheChange(multiTagsCache: boolean) {
|
multiTagsCacheChange(multiTagsCache: boolean) {
|
||||||
this.multiTagsCache = multiTagsCache;
|
this.multiTagsCache = multiTagsCache
|
||||||
if (multiTagsCache) {
|
if (multiTagsCache) {
|
||||||
storageLocal().setItem("responsive-tags", this.multiTags);
|
storageLocal().setItem('responsive-tags', this.multiTags)
|
||||||
} else {
|
} else {
|
||||||
storageLocal().removeItem("responsive-tags");
|
storageLocal().removeItem('responsive-tags')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tagsCache(multiTags) {
|
tagsCache(multiTags) {
|
||||||
this.getMultiTagsCache &&
|
this.getMultiTagsCache &&
|
||||||
storageLocal().setItem("responsive-tags", multiTags);
|
storageLocal().setItem('responsive-tags', multiTags)
|
||||||
},
|
},
|
||||||
handleTags<T>(
|
handleTags<T>(
|
||||||
mode: string,
|
mode: string,
|
||||||
@ -40,42 +40,42 @@ export const useMultiTagsStore = defineStore({
|
|||||||
position?: positionType
|
position?: positionType
|
||||||
): T {
|
): T {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "equal":
|
case 'equal':
|
||||||
this.multiTags = value;
|
this.multiTags = value
|
||||||
this.tagsCache(this.multiTags);
|
this.tagsCache(this.multiTags)
|
||||||
break;
|
break
|
||||||
case "push":
|
case 'push':
|
||||||
{
|
{
|
||||||
const tagVal = value as multiType;
|
const tagVal = value as multiType
|
||||||
// 不添加到标签页
|
// 不添加到标签页
|
||||||
if (tagVal?.meta?.hiddenTag) return;
|
if (tagVal?.meta?.hiddenTag) return
|
||||||
// 如果是外链无需添加信息到标签页
|
// 如果是外链无需添加信息到标签页
|
||||||
if (isUrl(tagVal?.name)) return;
|
if (isUrl(tagVal?.name)) return
|
||||||
// 如果title为空拒绝添加空信息到标签页
|
// 如果title为空拒绝添加空信息到标签页
|
||||||
if (tagVal?.meta?.title.length === 0) return;
|
if (tagVal?.meta?.title.length === 0) return
|
||||||
// showLink:false 不添加到标签页
|
// showLink:false 不添加到标签页
|
||||||
if (isBoolean(tagVal?.meta?.showLink) && !tagVal?.meta?.showLink)
|
if (isBoolean(tagVal?.meta?.showLink) && !tagVal?.meta?.showLink)
|
||||||
return;
|
return
|
||||||
const tagPath = tagVal.path;
|
const tagPath = tagVal.path
|
||||||
// 判断tag是否已存在
|
// 判断tag是否已存在
|
||||||
const tagHasExits = this.multiTags.some(tag => {
|
const tagHasExits = this.multiTags.some(tag => {
|
||||||
return tag.path === tagPath;
|
return tag.path === tagPath
|
||||||
});
|
})
|
||||||
|
|
||||||
// 判断tag中的query键值是否相等
|
// 判断tag中的query键值是否相等
|
||||||
const tagQueryHasExits = this.multiTags.some(tag => {
|
const tagQueryHasExits = this.multiTags.some(tag => {
|
||||||
return isEqual(tag?.query, tagVal?.query);
|
return isEqual(tag?.query, tagVal?.query)
|
||||||
});
|
})
|
||||||
|
|
||||||
// 判断tag中的params键值是否相等
|
// 判断tag中的params键值是否相等
|
||||||
const tagParamsHasExits = this.multiTags.some(tag => {
|
const tagParamsHasExits = this.multiTags.some(tag => {
|
||||||
return isEqual(tag?.params, tagVal?.params);
|
return isEqual(tag?.params, tagVal?.params)
|
||||||
});
|
})
|
||||||
|
|
||||||
if (tagHasExits && tagQueryHasExits && tagParamsHasExits) return;
|
if (tagHasExits && tagQueryHasExits && tagParamsHasExits) return
|
||||||
|
|
||||||
// 动态路由可打开的最大数量
|
// 动态路由可打开的最大数量
|
||||||
const dynamicLevel = tagVal?.meta?.dynamicLevel ?? -1;
|
const dynamicLevel = tagVal?.meta?.dynamicLevel ?? -1
|
||||||
if (dynamicLevel > 0) {
|
if (dynamicLevel > 0) {
|
||||||
if (
|
if (
|
||||||
this.multiTags.filter(e => e?.path === tagPath).length >=
|
this.multiTags.filter(e => e?.path === tagPath).length >=
|
||||||
@ -84,31 +84,31 @@ export const useMultiTagsStore = defineStore({
|
|||||||
// 如果当前已打开的动态路由数大于dynamicLevel,替换第一个动态路由标签
|
// 如果当前已打开的动态路由数大于dynamicLevel,替换第一个动态路由标签
|
||||||
const index = this.multiTags.findIndex(
|
const index = this.multiTags.findIndex(
|
||||||
item => item?.path === tagPath
|
item => item?.path === tagPath
|
||||||
);
|
)
|
||||||
index !== -1 && this.multiTags.splice(index, 1);
|
index !== -1 && this.multiTags.splice(index, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.multiTags.push(value);
|
this.multiTags.push(value)
|
||||||
this.tagsCache(this.multiTags);
|
this.tagsCache(this.multiTags)
|
||||||
}
|
}
|
||||||
break;
|
break
|
||||||
case "splice":
|
case 'splice':
|
||||||
if (!position) {
|
if (!position) {
|
||||||
const index = this.multiTags.findIndex(v => v.path === value);
|
const index = this.multiTags.findIndex(v => v.path === value)
|
||||||
if (index === -1) return;
|
if (index === -1) return
|
||||||
this.multiTags.splice(index, 1);
|
this.multiTags.splice(index, 1)
|
||||||
} else {
|
} else {
|
||||||
this.multiTags.splice(position?.startIndex, position?.length);
|
this.multiTags.splice(position?.startIndex, position?.length)
|
||||||
}
|
}
|
||||||
this.tagsCache(this.multiTags);
|
this.tagsCache(this.multiTags)
|
||||||
return this.multiTags;
|
return this.multiTags
|
||||||
case "slice":
|
case 'slice':
|
||||||
return this.multiTags.slice(-1);
|
return this.multiTags.slice(-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export function useMultiTagsStoreHook() {
|
export function useMultiTagsStoreHook() {
|
||||||
return useMultiTagsStore(store);
|
return useMultiTagsStore(store)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from 'pinia'
|
||||||
import { store } from "@/store";
|
import { store } from '@/store'
|
||||||
import { cacheType } from "./types";
|
import { cacheType } from './types'
|
||||||
import { constantMenus } from "@/router";
|
import { constantMenus } from '@/router'
|
||||||
import { ascending, filterTree, filterNoPermissionTree } from "@/router/utils";
|
import { ascending, filterTree, filterNoPermissionTree } from '@/router/utils'
|
||||||
|
|
||||||
export const usePermissionStore = defineStore({
|
export const usePermissionStore = defineStore({
|
||||||
id: "pure-permission",
|
id: 'pure-permission',
|
||||||
state: () => ({
|
state: () => ({
|
||||||
// 静态路由生成的菜单
|
// 静态路由生成的菜单
|
||||||
constantMenus,
|
constantMenus,
|
||||||
@ -19,29 +19,29 @@ export const usePermissionStore = defineStore({
|
|||||||
handleWholeMenus(routes: any[]) {
|
handleWholeMenus(routes: any[]) {
|
||||||
this.wholeMenus = filterNoPermissionTree(
|
this.wholeMenus = filterNoPermissionTree(
|
||||||
filterTree(ascending(this.constantMenus.concat(routes)))
|
filterTree(ascending(this.constantMenus.concat(routes)))
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
cacheOperate({ mode, name }: cacheType) {
|
cacheOperate({ mode, name }: cacheType) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "add":
|
case 'add':
|
||||||
this.cachePageList.push(name);
|
this.cachePageList.push(name)
|
||||||
this.cachePageList = [...new Set(this.cachePageList)];
|
this.cachePageList = [...new Set(this.cachePageList)]
|
||||||
break;
|
break
|
||||||
case "delete":
|
case 'delete':
|
||||||
// eslint-disable-next-line no-case-declarations
|
// eslint-disable-next-line no-case-declarations
|
||||||
const delIndex = this.cachePageList.findIndex(v => v === name);
|
const delIndex = this.cachePageList.findIndex(v => v === name)
|
||||||
delIndex !== -1 && this.cachePageList.splice(delIndex, 1);
|
delIndex !== -1 && this.cachePageList.splice(delIndex, 1)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/** 清空缓存页面 */
|
/** 清空缓存页面 */
|
||||||
clearAllCachePage() {
|
clearAllCachePage() {
|
||||||
this.wholeMenus = [];
|
this.wholeMenus = []
|
||||||
this.cachePageList = [];
|
this.cachePageList = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export function usePermissionStoreHook() {
|
export function usePermissionStoreHook() {
|
||||||
return usePermissionStore(store);
|
return usePermissionStore(store)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from 'pinia'
|
||||||
import { store } from "@/store";
|
import { store } from '@/store'
|
||||||
import { setType } from "./types";
|
import { setType } from './types'
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from '@/config'
|
||||||
|
|
||||||
export const useSettingStore = defineStore({
|
export const useSettingStore = defineStore({
|
||||||
id: "pure-setting",
|
id: 'pure-setting',
|
||||||
state: (): setType => ({
|
state: (): setType => ({
|
||||||
title: getConfig().Title,
|
title: getConfig().Title,
|
||||||
fixedHeader: getConfig().FixedHeader,
|
fixedHeader: getConfig().FixedHeader,
|
||||||
@ -12,28 +12,28 @@ export const useSettingStore = defineStore({
|
|||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getTitle() {
|
getTitle() {
|
||||||
return this.title;
|
return this.title
|
||||||
},
|
},
|
||||||
getFixedHeader() {
|
getFixedHeader() {
|
||||||
return this.fixedHeader;
|
return this.fixedHeader
|
||||||
},
|
},
|
||||||
getHiddenSideBar() {
|
getHiddenSideBar() {
|
||||||
return this.HiddenSideBar;
|
return this.HiddenSideBar
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
CHANGE_SETTING({ key, value }) {
|
CHANGE_SETTING({ key, value }) {
|
||||||
// eslint-disable-next-line no-prototype-builtins
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
if (this.hasOwnProperty(key)) {
|
if (this.hasOwnProperty(key)) {
|
||||||
this[key] = value;
|
this[key] = value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeSetting(data) {
|
changeSetting(data) {
|
||||||
this.CHANGE_SETTING(data);
|
this.CHANGE_SETTING(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export function useSettingStoreHook() {
|
export function useSettingStoreHook() {
|
||||||
return useSettingStore(store);
|
return useSettingStore(store)
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,44 @@
|
|||||||
import { RouteRecordName } from "vue-router";
|
import { RouteRecordName } from 'vue-router'
|
||||||
|
|
||||||
export type cacheType = {
|
export type cacheType = {
|
||||||
mode: string;
|
mode: string
|
||||||
name?: RouteRecordName;
|
name?: RouteRecordName
|
||||||
};
|
}
|
||||||
|
|
||||||
export type positionType = {
|
export type positionType = {
|
||||||
startIndex?: number;
|
startIndex?: number
|
||||||
length?: number;
|
length?: number
|
||||||
};
|
}
|
||||||
|
|
||||||
export type appType = {
|
export type appType = {
|
||||||
sidebar: {
|
sidebar: {
|
||||||
opened: boolean;
|
opened: boolean
|
||||||
withoutAnimation: boolean;
|
withoutAnimation: boolean
|
||||||
// 判断是否手动点击Collapse
|
// 判断是否手动点击Collapse
|
||||||
isClickCollapse: boolean;
|
isClickCollapse: boolean
|
||||||
};
|
}
|
||||||
layout: string;
|
layout: string
|
||||||
device: string;
|
device: string
|
||||||
};
|
}
|
||||||
|
|
||||||
export type multiType = {
|
export type multiType = {
|
||||||
path: string;
|
path: string
|
||||||
parentPath: string;
|
parentPath: string
|
||||||
name: string;
|
name: string
|
||||||
meta: any;
|
meta: any
|
||||||
query?: object;
|
query?: object
|
||||||
params?: object;
|
params?: object
|
||||||
};
|
}
|
||||||
|
|
||||||
export type setType = {
|
export type setType = {
|
||||||
title: string;
|
title: string
|
||||||
fixedHeader: boolean;
|
fixedHeader: boolean
|
||||||
hiddenSideBar: boolean;
|
hiddenSideBar: boolean
|
||||||
};
|
}
|
||||||
|
|
||||||
export type userType = {
|
export type userType = {
|
||||||
username?: string;
|
username?: string
|
||||||
roles?: Array<string>;
|
roles?: Array<string>
|
||||||
verifyCode?: string;
|
verifyCode?: string
|
||||||
currentPage?: number;
|
currentPage?: number
|
||||||
};
|
}
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from 'pinia'
|
||||||
import { store } from "@/store";
|
import { store } from '@/store'
|
||||||
import { userType } from "./types";
|
import { userType } from './types'
|
||||||
import { routerArrays } from "@/layout/types";
|
import { routerArrays } from '@/layout/types'
|
||||||
import { router, resetRouter } from "@/router";
|
import { router, resetRouter } from '@/router'
|
||||||
import { storageSession } from "@pureadmin/utils";
|
import { storageSession } from '@pureadmin/utils'
|
||||||
import { getLogin, refreshTokenApi } from "@/api/user";
|
import { getLogin, refreshTokenApi } from '@/api/user'
|
||||||
import { UserResult, RefreshTokenResult } from "@/api/user";
|
import { UserResult, RefreshTokenResult } from '@/api/user'
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from '@/store/modules/multiTags'
|
||||||
import { type DataInfo, setToken, removeToken, sessionKey } from "@/utils/auth";
|
import { type DataInfo, setToken, removeToken, sessionKey } from '@/utils/auth'
|
||||||
|
|
||||||
export const useUserStore = defineStore({
|
export const useUserStore = defineStore({
|
||||||
id: "pure-user",
|
id: 'pure-user',
|
||||||
state: (): userType => ({
|
state: (): userType => ({
|
||||||
// 用户名
|
// 用户名
|
||||||
username:
|
username:
|
||||||
storageSession().getItem<DataInfo<number>>(sessionKey)?.username ?? "",
|
storageSession().getItem<DataInfo<number>>(sessionKey)?.username ?? '',
|
||||||
// 页面级别权限
|
// 页面级别权限
|
||||||
roles: storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? []
|
roles: storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? []
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
/** 存储用户名 */
|
/** 存储用户名 */
|
||||||
SET_USERNAME(username: string) {
|
SET_USERNAME(username: string) {
|
||||||
this.username = username;
|
this.username = username
|
||||||
},
|
},
|
||||||
/** 存储角色 */
|
/** 存储角色 */
|
||||||
SET_ROLES(roles: Array<string>) {
|
SET_ROLES(roles: Array<string>) {
|
||||||
this.roles = roles;
|
this.roles = roles
|
||||||
},
|
},
|
||||||
/** 登入 */
|
/** 登入 */
|
||||||
async loginByUsername(data) {
|
async loginByUsername(data) {
|
||||||
@ -33,23 +33,23 @@ export const useUserStore = defineStore({
|
|||||||
getLogin(data)
|
getLogin(data)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
setToken(data.data);
|
setToken(data.data)
|
||||||
resolve(data);
|
resolve(data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
reject(error);
|
reject(error)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
/** 前端登出(不调用接口) */
|
/** 前端登出(不调用接口) */
|
||||||
logOut() {
|
logOut() {
|
||||||
this.username = "";
|
this.username = ''
|
||||||
this.roles = [];
|
this.roles = []
|
||||||
removeToken();
|
removeToken()
|
||||||
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
|
useMultiTagsStoreHook().handleTags('equal', [...routerArrays])
|
||||||
resetRouter();
|
resetRouter()
|
||||||
router.push("/login");
|
router.push('/login')
|
||||||
},
|
},
|
||||||
/** 刷新`token` */
|
/** 刷新`token` */
|
||||||
async handRefreshToken(data) {
|
async handRefreshToken(data) {
|
||||||
@ -57,18 +57,18 @@ export const useUserStore = defineStore({
|
|||||||
refreshTokenApi(data)
|
refreshTokenApi(data)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
setToken(data.data);
|
setToken(data.data)
|
||||||
resolve(data);
|
resolve(data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
reject(error);
|
reject(error)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export function useUserStoreHook() {
|
export function useUserStoreHook() {
|
||||||
return useUserStore(store);
|
return useUserStore(store)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use "element-plus/theme-chalk/src/dark/css-vars.scss" as *;
|
@use 'element-plus/theme-chalk/src/dark/css-vars.scss' as *;
|
||||||
|
|
||||||
/* 暗黑模式适配 */
|
/* 暗黑模式适配 */
|
||||||
html.dark {
|
html.dark {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-upload {
|
.el-upload {
|
||||||
input[type="file"] {
|
input[type='file'] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 重置 el-button 中 icon 的 margin */
|
/* 重置 el-button 中 icon 的 margin */
|
||||||
.reset-margin [class*="el-icon"] + span {
|
.reset-margin [class*='el-icon'] + span {
|
||||||
margin-left: 2px !important;
|
margin-left: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
@import "./mixin.scss";
|
@import './mixin.scss';
|
||||||
@import "./transition.scss";
|
@import './transition.scss';
|
||||||
@import "./element-plus.scss";
|
@import './element-plus.scss';
|
||||||
@import "./sidebar.scss";
|
@import './sidebar.scss';
|
||||||
@import "./dark.scss";
|
@import './dark.scss';
|
||||||
|
|
||||||
/* 自定义全局 CssVar */
|
/* 自定义全局 CssVar */
|
||||||
:root {
|
:root {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@mixin clearfix {
|
@mixin clearfix {
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: '';
|
||||||
display: table;
|
display: table;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,8 @@ body {
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
text-rendering: optimizelegibility;
|
text-rendering: optimizelegibility;
|
||||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
|
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
|
||||||
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
@ -59,7 +59,7 @@ kbd,
|
|||||||
samp,
|
samp,
|
||||||
pre {
|
pre {
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||||
"Liberation Mono", "Courier New", monospace;
|
'Liberation Mono', 'Courier New', monospace;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,9 +108,9 @@ select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
button,
|
button,
|
||||||
[type="button"],
|
[type='button'],
|
||||||
[type="reset"],
|
[type='reset'],
|
||||||
[type="submit"] {
|
[type='submit'] {
|
||||||
background-image: none;
|
background-image: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ progress {
|
|||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
[type="search"] {
|
[type='search'] {
|
||||||
outline-offset: -2px;
|
outline-offset: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ textarea::placeholder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
button,
|
button,
|
||||||
[role="button"] {
|
[role='button'] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +252,7 @@ div:focus {
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
content: " ";
|
content: ' ';
|
||||||
clear: both;
|
clear: both;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-menu-item.is-active.nest-menu::before {
|
.el-menu-item.is-active.nest-menu::before {
|
||||||
content: "";
|
content: '';
|
||||||
clear: both;
|
clear: both;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 8px;
|
left: 8px;
|
||||||
@ -197,14 +197,14 @@
|
|||||||
width: 2px;
|
width: 2px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: $menuActiveBefore;
|
background-color: $menuActiveBefore;
|
||||||
content: "";
|
content: '';
|
||||||
clear: both;
|
clear: both;
|
||||||
transition: all var(--pure-transition-duration) ease-in-out;
|
transition: all var(--pure-transition-duration) ease-in-out;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-menu--collapse .outer-most.el-sub-menu > .el-sub-menu__title::before {
|
.el-menu--collapse .outer-most.el-sub-menu > .el-sub-menu__title::before {
|
||||||
content: "";
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 0;
|
height: 0;
|
||||||
@ -219,7 +219,7 @@
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.is-active.submenu-title-noDropdown.outer-most::before {
|
.is-active.submenu-title-noDropdown.outer-most::before {
|
||||||
content: "";
|
content: '';
|
||||||
clear: both;
|
clear: both;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 8px;
|
left: 8px;
|
||||||
@ -289,7 +289,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-menu-item.is-active.nest-menu::before {
|
.el-menu-item.is-active.nest-menu::before {
|
||||||
content: "";
|
content: '';
|
||||||
clear: both;
|
clear: both;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 8px;
|
left: 8px;
|
||||||
@ -381,7 +381,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-menu-item.is-active.nest-menu::before {
|
.el-menu-item.is-active.nest-menu::before {
|
||||||
content: "";
|
content: '';
|
||||||
clear: both;
|
clear: both;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
@ -567,7 +567,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body[layout="vertical"] {
|
body[layout='vertical'] {
|
||||||
$sideBarWidth: 210px;
|
$sideBarWidth: 210px;
|
||||||
@include merge-style($sideBarWidth);
|
@include merge-style($sideBarWidth);
|
||||||
|
|
||||||
@ -641,7 +641,7 @@ body[layout="vertical"] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body[layout="horizontal"] {
|
body[layout='horizontal'] {
|
||||||
$sideBarWidth: 0;
|
$sideBarWidth: 0;
|
||||||
@include merge-style($sideBarWidth);
|
@include merge-style($sideBarWidth);
|
||||||
|
|
||||||
@ -655,7 +655,7 @@ body[layout="horizontal"] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body[layout="mix"] {
|
body[layout='mix'] {
|
||||||
$sideBarWidth: 210px;
|
$sideBarWidth: 210px;
|
||||||
@include merge-style($sideBarWidth);
|
@include merge-style($sideBarWidth);
|
||||||
|
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
import Cookies from "js-cookie";
|
import Cookies from 'js-cookie'
|
||||||
import { storageSession } from "@pureadmin/utils";
|
import { storageSession } from '@pureadmin/utils'
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
import { useUserStoreHook } from '@/store/modules/user'
|
||||||
|
|
||||||
export interface DataInfo<T> {
|
export interface DataInfo<T> {
|
||||||
/** token */
|
/** token */
|
||||||
accessToken: string;
|
accessToken: string
|
||||||
/** `accessToken`的过期时间(时间戳) */
|
/** `accessToken`的过期时间(时间戳) */
|
||||||
expires: T;
|
expires: T
|
||||||
/** 用于调用刷新accessToken的接口时所需的token */
|
/** 用于调用刷新accessToken的接口时所需的token */
|
||||||
refreshToken: string;
|
refreshToken: string
|
||||||
/** 用户名 */
|
/** 用户名 */
|
||||||
username?: string;
|
username?: string
|
||||||
/** 当前登陆用户的角色 */
|
/** 当前登陆用户的角色 */
|
||||||
roles?: Array<string>;
|
roles?: Array<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sessionKey = "user-info";
|
export const sessionKey = 'user-info'
|
||||||
export const TokenKey = "authorized-token";
|
export const TokenKey = 'authorized-token'
|
||||||
|
|
||||||
/** 获取`token` */
|
/** 获取`token` */
|
||||||
export function getToken(): DataInfo<number> {
|
export function getToken(): DataInfo<number> {
|
||||||
// 此处与`TokenKey`相同,此写法解决初始化时`Cookies`中不存在`TokenKey`报错
|
// 此处与`TokenKey`相同,此写法解决初始化时`Cookies`中不存在`TokenKey`报错
|
||||||
return Cookies.get(TokenKey)
|
return Cookies.get(TokenKey)
|
||||||
? JSON.parse(Cookies.get(TokenKey))
|
? JSON.parse(Cookies.get(TokenKey))
|
||||||
: storageSession().getItem(sessionKey);
|
: storageSession().getItem(sessionKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,47 +33,47 @@ export function getToken(): DataInfo<number> {
|
|||||||
* 将`username`、`roles`、`refreshToken`、`expires`这四条信息放在key值为`user-info`的sessionStorage里(浏览器关闭自动销毁)
|
* 将`username`、`roles`、`refreshToken`、`expires`这四条信息放在key值为`user-info`的sessionStorage里(浏览器关闭自动销毁)
|
||||||
*/
|
*/
|
||||||
export function setToken(data: DataInfo<Date>) {
|
export function setToken(data: DataInfo<Date>) {
|
||||||
let expires = 0;
|
let expires = 0
|
||||||
const { accessToken, refreshToken } = data;
|
const { accessToken, refreshToken } = data
|
||||||
expires = new Date(data.expires).getTime(); // 如果后端直接设置时间戳,将此处代码改为expires = data.expires,然后把上面的DataInfo<Date>改成DataInfo<number>即可
|
expires = new Date(data.expires).getTime() // 如果后端直接设置时间戳,将此处代码改为expires = data.expires,然后把上面的DataInfo<Date>改成DataInfo<number>即可
|
||||||
const cookieString = JSON.stringify({ accessToken, expires });
|
const cookieString = JSON.stringify({ accessToken, expires })
|
||||||
|
|
||||||
expires > 0
|
expires > 0
|
||||||
? Cookies.set(TokenKey, cookieString, {
|
? Cookies.set(TokenKey, cookieString, {
|
||||||
expires: (expires - Date.now()) / 86400000
|
expires: (expires - Date.now()) / 86400000
|
||||||
})
|
})
|
||||||
: Cookies.set(TokenKey, cookieString);
|
: Cookies.set(TokenKey, cookieString)
|
||||||
|
|
||||||
function setSessionKey(username: string, roles: Array<string>) {
|
function setSessionKey(username: string, roles: Array<string>) {
|
||||||
useUserStoreHook().SET_USERNAME(username);
|
useUserStoreHook().SET_USERNAME(username)
|
||||||
useUserStoreHook().SET_ROLES(roles);
|
useUserStoreHook().SET_ROLES(roles)
|
||||||
storageSession().setItem(sessionKey, {
|
storageSession().setItem(sessionKey, {
|
||||||
refreshToken,
|
refreshToken,
|
||||||
expires,
|
expires,
|
||||||
username,
|
username,
|
||||||
roles
|
roles
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.username && data.roles) {
|
if (data.username && data.roles) {
|
||||||
const { username, roles } = data;
|
const { username, roles } = data
|
||||||
setSessionKey(username, roles);
|
setSessionKey(username, roles)
|
||||||
} else {
|
} else {
|
||||||
const username =
|
const username =
|
||||||
storageSession().getItem<DataInfo<number>>(sessionKey)?.username ?? "";
|
storageSession().getItem<DataInfo<number>>(sessionKey)?.username ?? ''
|
||||||
const roles =
|
const roles =
|
||||||
storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? [];
|
storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? []
|
||||||
setSessionKey(username, roles);
|
setSessionKey(username, roles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除`token`以及key值为`user-info`的session信息 */
|
/** 删除`token`以及key值为`user-info`的session信息 */
|
||||||
export function removeToken() {
|
export function removeToken() {
|
||||||
Cookies.remove(TokenKey);
|
Cookies.remove(TokenKey)
|
||||||
sessionStorage.clear();
|
sessionStorage.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 格式化token(jwt格式) */
|
/** 格式化token(jwt格式) */
|
||||||
export const formatToken = (token: string): string => {
|
export const formatToken = (token: string): string => {
|
||||||
return "Bearer " + token;
|
return 'Bearer ' + token
|
||||||
};
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// 如果项目出现 `global is not defined` 报错,可能是您引入某个库的问题,比如 aws-sdk-js https://github.com/aws/aws-sdk-js
|
// 如果项目出现 `global is not defined` 报错,可能是您引入某个库的问题,比如 aws-sdk-js https://github.com/aws/aws-sdk-js
|
||||||
// 解决办法就是将该文件引入 src/main.ts 即可 import "@/utils/globalPolyfills";
|
// 解决办法就是将该文件引入 src/main.ts 即可 import "@/utils/globalPolyfills";
|
||||||
if (typeof (window as any).global === "undefined") {
|
if (typeof (window as any).global === 'undefined') {
|
||||||
(window as any).global = window;
|
;(window as any).global = window
|
||||||
}
|
}
|
||||||
|
|
||||||
export {};
|
export {}
|
||||||
|
@ -2,59 +2,59 @@ import Axios, {
|
|||||||
AxiosInstance,
|
AxiosInstance,
|
||||||
AxiosRequestConfig,
|
AxiosRequestConfig,
|
||||||
CustomParamsSerializer
|
CustomParamsSerializer
|
||||||
} from "axios";
|
} from 'axios'
|
||||||
import {
|
import {
|
||||||
PureHttpError,
|
PureHttpError,
|
||||||
RequestMethods,
|
RequestMethods,
|
||||||
PureHttpResponse,
|
PureHttpResponse,
|
||||||
PureHttpRequestConfig
|
PureHttpRequestConfig
|
||||||
} from "./types.d";
|
} from './types.d'
|
||||||
import { stringify } from "qs";
|
import { stringify } from 'qs'
|
||||||
import NProgress from "../progress";
|
import NProgress from '../progress'
|
||||||
import { getToken, formatToken } from "@/utils/auth";
|
import { getToken, formatToken } from '@/utils/auth'
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
import { useUserStoreHook } from '@/store/modules/user'
|
||||||
|
|
||||||
// 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
|
// 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
|
||||||
const defaultConfig: AxiosRequestConfig = {
|
const defaultConfig: AxiosRequestConfig = {
|
||||||
// 请求超时时间
|
// 请求超时时间
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json, text/plain, */*",
|
Accept: 'application/json, text/plain, */*',
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
"X-Requested-With": "XMLHttpRequest"
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
},
|
},
|
||||||
// 数组格式参数序列化(https://github.com/axios/axios/issues/5142)
|
// 数组格式参数序列化(https://github.com/axios/axios/issues/5142)
|
||||||
paramsSerializer: {
|
paramsSerializer: {
|
||||||
serialize: stringify as unknown as CustomParamsSerializer
|
serialize: stringify as unknown as CustomParamsSerializer
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
class PureHttp {
|
class PureHttp {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.httpInterceptorsRequest();
|
this.httpInterceptorsRequest()
|
||||||
this.httpInterceptorsResponse();
|
this.httpInterceptorsResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** token过期后,暂存待执行的请求 */
|
/** token过期后,暂存待执行的请求 */
|
||||||
private static requests = [];
|
private static requests = []
|
||||||
|
|
||||||
/** 防止重复刷新token */
|
/** 防止重复刷新token */
|
||||||
private static isRefreshing = false;
|
private static isRefreshing = false
|
||||||
|
|
||||||
/** 初始化配置对象 */
|
/** 初始化配置对象 */
|
||||||
private static initConfig: PureHttpRequestConfig = {};
|
private static initConfig: PureHttpRequestConfig = {}
|
||||||
|
|
||||||
/** 保存当前Axios实例对象 */
|
/** 保存当前Axios实例对象 */
|
||||||
private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);
|
private static axiosInstance: AxiosInstance = Axios.create(defaultConfig)
|
||||||
|
|
||||||
/** 重连原始请求 */
|
/** 重连原始请求 */
|
||||||
private static retryOriginalRequest(config: PureHttpRequestConfig) {
|
private static retryOriginalRequest(config: PureHttpRequestConfig) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
PureHttp.requests.push((token: string) => {
|
PureHttp.requests.push((token: string) => {
|
||||||
config.headers["Authorization"] = formatToken(token);
|
config.headers['Authorization'] = formatToken(token)
|
||||||
resolve(config);
|
resolve(config)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 请求拦截 */
|
/** 请求拦截 */
|
||||||
@ -62,87 +62,87 @@ class PureHttp {
|
|||||||
PureHttp.axiosInstance.interceptors.request.use(
|
PureHttp.axiosInstance.interceptors.request.use(
|
||||||
async (config: PureHttpRequestConfig) => {
|
async (config: PureHttpRequestConfig) => {
|
||||||
// 开启进度条动画
|
// 开启进度条动画
|
||||||
NProgress.start();
|
NProgress.start()
|
||||||
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
||||||
if (typeof config.beforeRequestCallback === "function") {
|
if (typeof config.beforeRequestCallback === 'function') {
|
||||||
config.beforeRequestCallback(config);
|
config.beforeRequestCallback(config)
|
||||||
return config;
|
return config
|
||||||
}
|
}
|
||||||
if (PureHttp.initConfig.beforeRequestCallback) {
|
if (PureHttp.initConfig.beforeRequestCallback) {
|
||||||
PureHttp.initConfig.beforeRequestCallback(config);
|
PureHttp.initConfig.beforeRequestCallback(config)
|
||||||
return config;
|
return config
|
||||||
}
|
}
|
||||||
/** 请求白名单,放置一些不需要token的接口(通过设置请求白名单,防止token过期后再请求造成的死循环问题) */
|
/** 请求白名单,放置一些不需要token的接口(通过设置请求白名单,防止token过期后再请求造成的死循环问题) */
|
||||||
const whiteList = ["/refreshToken", "/login"];
|
const whiteList = ['/refreshToken', '/login']
|
||||||
return whiteList.some(v => config.url.indexOf(v) > -1)
|
return whiteList.some(v => config.url.indexOf(v) > -1)
|
||||||
? config
|
? config
|
||||||
: new Promise(resolve => {
|
: new Promise(resolve => {
|
||||||
const data = getToken();
|
const data = getToken()
|
||||||
if (data) {
|
if (data) {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime()
|
||||||
const expired = parseInt(data.expires) - now <= 0;
|
const expired = parseInt(data.expires) - now <= 0
|
||||||
if (expired) {
|
if (expired) {
|
||||||
if (!PureHttp.isRefreshing) {
|
if (!PureHttp.isRefreshing) {
|
||||||
PureHttp.isRefreshing = true;
|
PureHttp.isRefreshing = true
|
||||||
// token过期刷新
|
// token过期刷新
|
||||||
useUserStoreHook()
|
useUserStoreHook()
|
||||||
.handRefreshToken({ refreshToken: data.refreshToken })
|
.handRefreshToken({ refreshToken: data.refreshToken })
|
||||||
.then(res => {
|
.then(res => {
|
||||||
const token = res.data.accessToken;
|
const token = res.data.accessToken
|
||||||
config.headers["Authorization"] = formatToken(token);
|
config.headers['Authorization'] = formatToken(token)
|
||||||
PureHttp.requests.forEach(cb => cb(token));
|
PureHttp.requests.forEach(cb => cb(token))
|
||||||
PureHttp.requests = [];
|
PureHttp.requests = []
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
PureHttp.isRefreshing = false;
|
PureHttp.isRefreshing = false
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
resolve(PureHttp.retryOriginalRequest(config));
|
resolve(PureHttp.retryOriginalRequest(config))
|
||||||
} else {
|
} else {
|
||||||
config.headers["Authorization"] = formatToken(
|
config.headers['Authorization'] = formatToken(
|
||||||
data.accessToken
|
data.accessToken
|
||||||
);
|
)
|
||||||
resolve(config);
|
resolve(config)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resolve(config);
|
resolve(config)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
return Promise.reject(error);
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 响应拦截 */
|
/** 响应拦截 */
|
||||||
private httpInterceptorsResponse(): void {
|
private httpInterceptorsResponse(): void {
|
||||||
const instance = PureHttp.axiosInstance;
|
const instance = PureHttp.axiosInstance
|
||||||
instance.interceptors.response.use(
|
instance.interceptors.response.use(
|
||||||
(response: PureHttpResponse) => {
|
(response: PureHttpResponse) => {
|
||||||
const $config = response.config;
|
const $config = response.config
|
||||||
// 关闭进度条动画
|
// 关闭进度条动画
|
||||||
NProgress.done();
|
NProgress.done()
|
||||||
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
||||||
if (typeof $config.beforeResponseCallback === "function") {
|
if (typeof $config.beforeResponseCallback === 'function') {
|
||||||
$config.beforeResponseCallback(response);
|
$config.beforeResponseCallback(response)
|
||||||
return response.data;
|
return response.data
|
||||||
}
|
}
|
||||||
if (PureHttp.initConfig.beforeResponseCallback) {
|
if (PureHttp.initConfig.beforeResponseCallback) {
|
||||||
PureHttp.initConfig.beforeResponseCallback(response);
|
PureHttp.initConfig.beforeResponseCallback(response)
|
||||||
return response.data;
|
return response.data
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data
|
||||||
},
|
},
|
||||||
(error: PureHttpError) => {
|
(error: PureHttpError) => {
|
||||||
const $error = error;
|
const $error = error
|
||||||
$error.isCancelRequest = Axios.isCancel($error);
|
$error.isCancelRequest = Axios.isCancel($error)
|
||||||
// 关闭进度条动画
|
// 关闭进度条动画
|
||||||
NProgress.done();
|
NProgress.done()
|
||||||
// 所有的响应异常 区分来源为取消请求/非取消请求
|
// 所有的响应异常 区分来源为取消请求/非取消请求
|
||||||
return Promise.reject($error);
|
return Promise.reject($error)
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 通用请求工具函数 */
|
/** 通用请求工具函数 */
|
||||||
@ -157,19 +157,19 @@ class PureHttp {
|
|||||||
url,
|
url,
|
||||||
...param,
|
...param,
|
||||||
...axiosConfig
|
...axiosConfig
|
||||||
} as PureHttpRequestConfig;
|
} as PureHttpRequestConfig
|
||||||
|
|
||||||
// 单独处理自定义请求/响应回掉
|
// 单独处理自定义请求/响应回掉
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
PureHttp.axiosInstance
|
PureHttp.axiosInstance
|
||||||
.request(config)
|
.request(config)
|
||||||
.then((response: undefined) => {
|
.then((response: undefined) => {
|
||||||
resolve(response);
|
resolve(response)
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
reject(error);
|
reject(error)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 单独抽离的post工具函数 */
|
/** 单独抽离的post工具函数 */
|
||||||
@ -178,7 +178,7 @@ class PureHttp {
|
|||||||
params?: AxiosRequestConfig<T>,
|
params?: AxiosRequestConfig<T>,
|
||||||
config?: PureHttpRequestConfig
|
config?: PureHttpRequestConfig
|
||||||
): Promise<P> {
|
): Promise<P> {
|
||||||
return this.request<P>("post", url, params, config);
|
return this.request<P>('post', url, params, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 单独抽离的get工具函数 */
|
/** 单独抽离的get工具函数 */
|
||||||
@ -187,8 +187,8 @@ class PureHttp {
|
|||||||
params?: AxiosRequestConfig<T>,
|
params?: AxiosRequestConfig<T>,
|
||||||
config?: PureHttpRequestConfig
|
config?: PureHttpRequestConfig
|
||||||
): Promise<P> {
|
): Promise<P> {
|
||||||
return this.request<P>("get", url, params, config);
|
return this.request<P>('get', url, params, config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const http = new PureHttp();
|
export const http = new PureHttp()
|
||||||
|
28
src/utils/http/types.d.ts
vendored
28
src/utils/http/types.d.ts
vendored
@ -3,28 +3,28 @@ import Axios, {
|
|||||||
AxiosError,
|
AxiosError,
|
||||||
AxiosResponse,
|
AxiosResponse,
|
||||||
AxiosRequestConfig
|
AxiosRequestConfig
|
||||||
} from "axios";
|
} from 'axios'
|
||||||
|
|
||||||
export type resultType = {
|
export type resultType = {
|
||||||
accessToken?: string;
|
accessToken?: string
|
||||||
};
|
}
|
||||||
|
|
||||||
export type RequestMethods = Extract<
|
export type RequestMethods = Extract<
|
||||||
Method,
|
Method,
|
||||||
"get" | "post" | "put" | "delete" | "patch" | "option" | "head"
|
'get' | 'post' | 'put' | 'delete' | 'patch' | 'option' | 'head'
|
||||||
>;
|
>
|
||||||
|
|
||||||
export interface PureHttpError extends AxiosError {
|
export interface PureHttpError extends AxiosError {
|
||||||
isCancelRequest?: boolean;
|
isCancelRequest?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PureHttpResponse extends AxiosResponse {
|
export interface PureHttpResponse extends AxiosResponse {
|
||||||
config: PureHttpRequestConfig;
|
config: PureHttpRequestConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PureHttpRequestConfig extends AxiosRequestConfig {
|
export interface PureHttpRequestConfig extends AxiosRequestConfig {
|
||||||
beforeRequestCallback?: (request: PureHttpRequestConfig) => void;
|
beforeRequestCallback?: (request: PureHttpRequestConfig) => void
|
||||||
beforeResponseCallback?: (response: PureHttpResponse) => void;
|
beforeResponseCallback?: (response: PureHttpResponse) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PureHttp {
|
export default class PureHttp {
|
||||||
@ -33,15 +33,11 @@ export default class PureHttp {
|
|||||||
url: string,
|
url: string,
|
||||||
param?: AxiosRequestConfig,
|
param?: AxiosRequestConfig,
|
||||||
axiosConfig?: PureHttpRequestConfig
|
axiosConfig?: PureHttpRequestConfig
|
||||||
): Promise<T>;
|
): Promise<T>
|
||||||
post<T, P>(
|
post<T, P>(
|
||||||
url: string,
|
url: string,
|
||||||
params?: T,
|
params?: T,
|
||||||
config?: PureHttpRequestConfig
|
config?: PureHttpRequestConfig
|
||||||
): Promise<P>;
|
): Promise<P>
|
||||||
get<T, P>(
|
get<T, P>(url: string, params?: T, config?: PureHttpRequestConfig): Promise<P>
|
||||||
url: string,
|
|
||||||
params?: T,
|
|
||||||
config?: PureHttpRequestConfig
|
|
||||||
): Promise<P>;
|
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
import { type VNode } from "vue";
|
import { type VNode } from 'vue'
|
||||||
import { isFunction } from "@pureadmin/utils";
|
import { isFunction } from '@pureadmin/utils'
|
||||||
import { type MessageHandler, ElMessage } from "element-plus";
|
import { type MessageHandler, ElMessage } from 'element-plus'
|
||||||
|
|
||||||
type messageStyle = "el" | "antd";
|
type messageStyle = 'el' | 'antd'
|
||||||
type messageTypes = "info" | "success" | "warning" | "error";
|
type messageTypes = 'info' | 'success' | 'warning' | 'error'
|
||||||
|
|
||||||
interface MessageParams {
|
interface MessageParams {
|
||||||
/** 消息类型,可选 `info` 、`success` 、`warning` 、`error` ,默认 `info` */
|
/** 消息类型,可选 `info` 、`success` 、`warning` 、`error` ,默认 `info` */
|
||||||
type?: messageTypes;
|
type?: messageTypes
|
||||||
/** 自定义图标,该属性会覆盖 `type` 的图标 */
|
/** 自定义图标,该属性会覆盖 `type` 的图标 */
|
||||||
icon?: any;
|
icon?: any
|
||||||
/** 是否将 `message` 属性作为 `HTML` 片段处理,默认 `false` */
|
/** 是否将 `message` 属性作为 `HTML` 片段处理,默认 `false` */
|
||||||
dangerouslyUseHTMLString?: boolean;
|
dangerouslyUseHTMLString?: boolean
|
||||||
/** 消息风格,可选 `el` 、`antd` ,默认 `antd` */
|
/** 消息风格,可选 `el` 、`antd` ,默认 `antd` */
|
||||||
customClass?: messageStyle;
|
customClass?: messageStyle
|
||||||
/** 显示时间,单位为毫秒。设为 `0` 则不会自动关闭,`element-plus` 默认是 `3000` ,平台改成默认 `2000` */
|
/** 显示时间,单位为毫秒。设为 `0` 则不会自动关闭,`element-plus` 默认是 `3000` ,平台改成默认 `2000` */
|
||||||
duration?: number;
|
duration?: number
|
||||||
/** 是否显示关闭按钮,默认值 `false` */
|
/** 是否显示关闭按钮,默认值 `false` */
|
||||||
showClose?: boolean;
|
showClose?: boolean
|
||||||
/** 文字是否居中,默认值 `false` */
|
/** 文字是否居中,默认值 `false` */
|
||||||
center?: boolean;
|
center?: boolean
|
||||||
/** `Message` 距离窗口顶部的偏移量,默认 `20` */
|
/** `Message` 距离窗口顶部的偏移量,默认 `20` */
|
||||||
offset?: number;
|
offset?: number
|
||||||
/** 设置组件的根元素,默认 `document.body` */
|
/** 设置组件的根元素,默认 `document.body` */
|
||||||
appendTo?: string | HTMLElement;
|
appendTo?: string | HTMLElement
|
||||||
/** 合并内容相同的消息,不支持 `VNode` 类型的消息,默认值 `false` */
|
/** 合并内容相同的消息,不支持 `VNode` 类型的消息,默认值 `false` */
|
||||||
grouping?: boolean;
|
grouping?: boolean
|
||||||
/** 关闭时的回调函数, 参数为被关闭的 `message` 实例 */
|
/** 关闭时的回调函数, 参数为被关闭的 `message` 实例 */
|
||||||
onClose?: Function | null;
|
onClose?: Function | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 用法非常简单,参考 src/views/components/message/index.vue 文件 */
|
/** 用法非常简单,参考 src/views/components/message/index.vue 文件 */
|
||||||
@ -42,14 +42,14 @@ const message = (
|
|||||||
if (!params) {
|
if (!params) {
|
||||||
return ElMessage({
|
return ElMessage({
|
||||||
message,
|
message,
|
||||||
customClass: "pure-message"
|
customClass: 'pure-message'
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
const {
|
const {
|
||||||
icon,
|
icon,
|
||||||
type = "info",
|
type = 'info',
|
||||||
dangerouslyUseHTMLString = false,
|
dangerouslyUseHTMLString = false,
|
||||||
customClass = "antd",
|
customClass = 'antd',
|
||||||
duration = 2000,
|
duration = 2000,
|
||||||
showClose = false,
|
showClose = false,
|
||||||
center = false,
|
center = false,
|
||||||
@ -57,7 +57,7 @@ const message = (
|
|||||||
appendTo = document.body,
|
appendTo = document.body,
|
||||||
grouping = false,
|
grouping = false,
|
||||||
onClose
|
onClose
|
||||||
} = params;
|
} = params
|
||||||
|
|
||||||
return ElMessage({
|
return ElMessage({
|
||||||
message,
|
message,
|
||||||
@ -71,15 +71,15 @@ const message = (
|
|||||||
appendTo,
|
appendTo,
|
||||||
grouping,
|
grouping,
|
||||||
// 全局搜 pure-message 即可知道该类的样式位置
|
// 全局搜 pure-message 即可知道该类的样式位置
|
||||||
customClass: customClass === "antd" ? "pure-message" : "",
|
customClass: customClass === 'antd' ? 'pure-message' : '',
|
||||||
onClose: () => (isFunction(onClose) ? onClose() : null)
|
onClose: () => (isFunction(onClose) ? onClose() : null)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭所有 `Message` 消息提示函数
|
* 关闭所有 `Message` 消息提示函数
|
||||||
*/
|
*/
|
||||||
const closeAllMessage = (): void => ElMessage.closeAll();
|
const closeAllMessage = (): void => ElMessage.closeAll()
|
||||||
|
|
||||||
export { message, closeAllMessage };
|
export { message, closeAllMessage }
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import type { Emitter } from "mitt";
|
import type { Emitter } from 'mitt'
|
||||||
import mitt from "mitt";
|
import mitt from 'mitt'
|
||||||
|
|
||||||
type Events = {
|
type Events = {
|
||||||
resize: {
|
resize: {
|
||||||
detail: {
|
detail: {
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
openPanel: string;
|
openPanel: string
|
||||||
tagViewsChange: string;
|
tagViewsChange: string
|
||||||
tagViewsShowModel: string;
|
tagViewsShowModel: string
|
||||||
logoChange: boolean;
|
logoChange: boolean
|
||||||
changLayoutRoute: {
|
changLayoutRoute: {
|
||||||
indexPath: string;
|
indexPath: string
|
||||||
parentPath: string;
|
parentPath: string
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const emitter: Emitter<Events> = mitt<Events>();
|
export const emitter: Emitter<Events> = mitt<Events>()
|
||||||
|
@ -1,47 +1,47 @@
|
|||||||
interface PrintFunction {
|
interface PrintFunction {
|
||||||
extendOptions: Function;
|
extendOptions: Function
|
||||||
getStyle: Function;
|
getStyle: Function
|
||||||
setDomHeight: Function;
|
setDomHeight: Function
|
||||||
toPrint: Function;
|
toPrint: Function
|
||||||
}
|
}
|
||||||
|
|
||||||
const Print = function (dom, options?: object): PrintFunction {
|
const Print = function (dom, options?: object): PrintFunction {
|
||||||
options = options || {};
|
options = options || {}
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
if (!(this instanceof Print)) return new Print(dom, options);
|
if (!(this instanceof Print)) return new Print(dom, options)
|
||||||
this.conf = {
|
this.conf = {
|
||||||
styleStr: "",
|
styleStr: '',
|
||||||
// Elements that need to dynamically get and set the height
|
// Elements that need to dynamically get and set the height
|
||||||
setDomHeightArr: [],
|
setDomHeightArr: [],
|
||||||
// Callback before printing
|
// Callback before printing
|
||||||
printBeforeFn: null,
|
printBeforeFn: null,
|
||||||
// Callback after printing
|
// Callback after printing
|
||||||
printDoneCallBack: null
|
printDoneCallBack: null
|
||||||
};
|
}
|
||||||
for (const key in this.conf) {
|
for (const key in this.conf) {
|
||||||
// eslint-disable-next-line no-prototype-builtins
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
if (key && options.hasOwnProperty(key)) {
|
if (key && options.hasOwnProperty(key)) {
|
||||||
this.conf[key] = options[key];
|
this.conf[key] = options[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof dom === "string") {
|
if (typeof dom === 'string') {
|
||||||
this.dom = document.querySelector(dom);
|
this.dom = document.querySelector(dom)
|
||||||
} else {
|
} else {
|
||||||
this.dom = this.isDOM(dom) ? dom : dom.$el;
|
this.dom = this.isDOM(dom) ? dom : dom.$el
|
||||||
}
|
}
|
||||||
if (this.conf.setDomHeightArr && this.conf.setDomHeightArr.length) {
|
if (this.conf.setDomHeightArr && this.conf.setDomHeightArr.length) {
|
||||||
this.setDomHeight(this.conf.setDomHeightArr);
|
this.setDomHeight(this.conf.setDomHeightArr)
|
||||||
}
|
}
|
||||||
this.init();
|
this.init()
|
||||||
};
|
}
|
||||||
|
|
||||||
Print.prototype = {
|
Print.prototype = {
|
||||||
/**
|
/**
|
||||||
* init
|
* init
|
||||||
*/
|
*/
|
||||||
init: function (): void {
|
init: function (): void {
|
||||||
const content = this.getStyle() + this.getHtml();
|
const content = this.getStyle() + this.getHtml()
|
||||||
this.writeIframe(content);
|
this.writeIframe(content)
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Configuration property extension
|
* Configuration property extension
|
||||||
@ -50,58 +50,58 @@ Print.prototype = {
|
|||||||
*/
|
*/
|
||||||
extendOptions: function <T>(obj, obj2: T): T {
|
extendOptions: function <T>(obj, obj2: T): T {
|
||||||
for (const k in obj2) {
|
for (const k in obj2) {
|
||||||
obj[k] = obj2[k];
|
obj[k] = obj2[k]
|
||||||
}
|
}
|
||||||
return obj;
|
return obj
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
Copy all styles of the original page
|
Copy all styles of the original page
|
||||||
*/
|
*/
|
||||||
getStyle: function (): string {
|
getStyle: function (): string {
|
||||||
let str = "";
|
let str = ''
|
||||||
const styles: NodeListOf<Element> = document.querySelectorAll("style,link");
|
const styles: NodeListOf<Element> = document.querySelectorAll('style,link')
|
||||||
for (let i = 0; i < styles.length; i++) {
|
for (let i = 0; i < styles.length; i++) {
|
||||||
str += styles[i].outerHTML;
|
str += styles[i].outerHTML
|
||||||
}
|
}
|
||||||
str += `<style>.no-print{display:none;}${this.conf.styleStr}</style>`;
|
str += `<style>.no-print{display:none;}${this.conf.styleStr}</style>`
|
||||||
return str;
|
return str
|
||||||
},
|
},
|
||||||
// form assignment
|
// form assignment
|
||||||
getHtml: function (): Element {
|
getHtml: function (): Element {
|
||||||
const inputs = document.querySelectorAll("input");
|
const inputs = document.querySelectorAll('input')
|
||||||
const selects = document.querySelectorAll("select");
|
const selects = document.querySelectorAll('select')
|
||||||
const textareas = document.querySelectorAll("textarea");
|
const textareas = document.querySelectorAll('textarea')
|
||||||
const canvass = document.querySelectorAll("canvas");
|
const canvass = document.querySelectorAll('canvas')
|
||||||
|
|
||||||
for (let k = 0; k < inputs.length; k++) {
|
for (let k = 0; k < inputs.length; k++) {
|
||||||
if (inputs[k].type == "checkbox" || inputs[k].type == "radio") {
|
if (inputs[k].type == 'checkbox' || inputs[k].type == 'radio') {
|
||||||
if (inputs[k].checked == true) {
|
if (inputs[k].checked == true) {
|
||||||
inputs[k].setAttribute("checked", "checked");
|
inputs[k].setAttribute('checked', 'checked')
|
||||||
} else {
|
} else {
|
||||||
inputs[k].removeAttribute("checked");
|
inputs[k].removeAttribute('checked')
|
||||||
}
|
}
|
||||||
} else if (inputs[k].type == "text") {
|
} else if (inputs[k].type == 'text') {
|
||||||
inputs[k].setAttribute("value", inputs[k].value);
|
inputs[k].setAttribute('value', inputs[k].value)
|
||||||
} else {
|
} else {
|
||||||
inputs[k].setAttribute("value", inputs[k].value);
|
inputs[k].setAttribute('value', inputs[k].value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let k2 = 0; k2 < textareas.length; k2++) {
|
for (let k2 = 0; k2 < textareas.length; k2++) {
|
||||||
if (textareas[k2].type == "textarea") {
|
if (textareas[k2].type == 'textarea') {
|
||||||
textareas[k2].innerHTML = textareas[k2].value;
|
textareas[k2].innerHTML = textareas[k2].value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let k3 = 0; k3 < selects.length; k3++) {
|
for (let k3 = 0; k3 < selects.length; k3++) {
|
||||||
if (selects[k3].type == "select-one") {
|
if (selects[k3].type == 'select-one') {
|
||||||
const child = selects[k3].children;
|
const child = selects[k3].children
|
||||||
for (const i in child) {
|
for (const i in child) {
|
||||||
if (child[i].tagName == "OPTION") {
|
if (child[i].tagName == 'OPTION') {
|
||||||
if ((child[i] as any).selected == true) {
|
if ((child[i] as any).selected == true) {
|
||||||
child[i].setAttribute("selected", "selected");
|
child[i].setAttribute('selected', 'selected')
|
||||||
} else {
|
} else {
|
||||||
child[i].removeAttribute("selected");
|
child[i].removeAttribute('selected')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,58 +109,58 @@ Print.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let k4 = 0; k4 < canvass.length; k4++) {
|
for (let k4 = 0; k4 < canvass.length; k4++) {
|
||||||
const imageURL = canvass[k4].toDataURL("image/png");
|
const imageURL = canvass[k4].toDataURL('image/png')
|
||||||
const img = document.createElement("img");
|
const img = document.createElement('img')
|
||||||
img.src = imageURL;
|
img.src = imageURL
|
||||||
img.setAttribute("style", "max-width: 100%;");
|
img.setAttribute('style', 'max-width: 100%;')
|
||||||
img.className = "isNeedRemove";
|
img.className = 'isNeedRemove'
|
||||||
canvass[k4].parentNode.insertBefore(img, canvass[k4].nextElementSibling);
|
canvass[k4].parentNode.insertBefore(img, canvass[k4].nextElementSibling)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.dom.outerHTML;
|
return this.dom.outerHTML
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
create iframe
|
create iframe
|
||||||
*/
|
*/
|
||||||
writeIframe: function (content) {
|
writeIframe: function (content) {
|
||||||
let w: Document | Window;
|
let w: Document | Window
|
||||||
let doc: Document;
|
let doc: Document
|
||||||
const iframe: HTMLIFrameElement = document.createElement("iframe");
|
const iframe: HTMLIFrameElement = document.createElement('iframe')
|
||||||
const f: HTMLIFrameElement = document.body.appendChild(iframe);
|
const f: HTMLIFrameElement = document.body.appendChild(iframe)
|
||||||
iframe.id = "myIframe";
|
iframe.id = 'myIframe'
|
||||||
iframe.setAttribute(
|
iframe.setAttribute(
|
||||||
"style",
|
'style',
|
||||||
"position:absolute;width:0;height:0;top:-10px;left:-10px;"
|
'position:absolute;width:0;height:0;top:-10px;left:-10px;'
|
||||||
);
|
)
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
w = f.contentWindow || f.contentDocument;
|
w = f.contentWindow || f.contentDocument
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
doc = f.contentDocument || f.contentWindow.document;
|
doc = f.contentDocument || f.contentWindow.document
|
||||||
doc.open();
|
doc.open()
|
||||||
doc.write(content);
|
doc.write(content)
|
||||||
doc.close();
|
doc.close()
|
||||||
|
|
||||||
const removes = document.querySelectorAll(".isNeedRemove");
|
const removes = document.querySelectorAll('.isNeedRemove')
|
||||||
for (let k = 0; k < removes.length; k++) {
|
for (let k = 0; k < removes.length; k++) {
|
||||||
removes[k].parentNode.removeChild(removes[k]);
|
removes[k].parentNode.removeChild(removes[k])
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
const _this = this;
|
const _this = this
|
||||||
iframe.onload = function (): void {
|
iframe.onload = function (): void {
|
||||||
// Before popping, callback
|
// Before popping, callback
|
||||||
if (_this.conf.printBeforeFn) {
|
if (_this.conf.printBeforeFn) {
|
||||||
_this.conf.printBeforeFn({ doc });
|
_this.conf.printBeforeFn({ doc })
|
||||||
}
|
}
|
||||||
_this.toPrint(w);
|
_this.toPrint(w)
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
document.body.removeChild(iframe);
|
document.body.removeChild(iframe)
|
||||||
// After popup, callback
|
// After popup, callback
|
||||||
if (_this.conf.printDoneCallBack) {
|
if (_this.conf.printDoneCallBack) {
|
||||||
_this.conf.printDoneCallBack();
|
_this.conf.printDoneCallBack()
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100)
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
Print
|
Print
|
||||||
@ -168,32 +168,32 @@ Print.prototype = {
|
|||||||
toPrint: function (frameWindow): void {
|
toPrint: function (frameWindow): void {
|
||||||
try {
|
try {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
frameWindow.focus();
|
frameWindow.focus()
|
||||||
try {
|
try {
|
||||||
if (!frameWindow.document.execCommand("print", false, null)) {
|
if (!frameWindow.document.execCommand('print', false, null)) {
|
||||||
frameWindow.print();
|
frameWindow.print()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
frameWindow.print();
|
frameWindow.print()
|
||||||
}
|
}
|
||||||
frameWindow.close();
|
frameWindow.close()
|
||||||
}, 10);
|
}, 10)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isDOM:
|
isDOM:
|
||||||
typeof HTMLElement === "object"
|
typeof HTMLElement === 'object'
|
||||||
? function (obj) {
|
? function (obj) {
|
||||||
return obj instanceof HTMLElement;
|
return obj instanceof HTMLElement
|
||||||
}
|
}
|
||||||
: function (obj) {
|
: function (obj) {
|
||||||
return (
|
return (
|
||||||
obj &&
|
obj &&
|
||||||
typeof obj === "object" &&
|
typeof obj === 'object' &&
|
||||||
obj.nodeType === 1 &&
|
obj.nodeType === 1 &&
|
||||||
typeof obj.nodeName === "string"
|
typeof obj.nodeName === 'string'
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Set the height of the specified dom element by getting the existing height of the dom element and setting
|
* Set the height of the specified dom element by getting the existing height of the dom element and setting
|
||||||
@ -202,13 +202,13 @@ Print.prototype = {
|
|||||||
setDomHeight(arr) {
|
setDomHeight(arr) {
|
||||||
if (arr && arr.length) {
|
if (arr && arr.length) {
|
||||||
arr.forEach(name => {
|
arr.forEach(name => {
|
||||||
const domArr = document.querySelectorAll(name);
|
const domArr = document.querySelectorAll(name)
|
||||||
domArr.forEach(dom => {
|
domArr.forEach(dom => {
|
||||||
dom.style.height = dom.offsetHeight + "px";
|
dom.style.height = dom.offsetHeight + 'px'
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Print;
|
export default Print
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import NProgress from "nprogress";
|
import NProgress from 'nprogress'
|
||||||
import "nprogress/nprogress.css";
|
import 'nprogress/nprogress.css'
|
||||||
|
|
||||||
NProgress.configure({
|
NProgress.configure({
|
||||||
// 动画方式
|
// 动画方式
|
||||||
easing: "ease",
|
easing: 'ease',
|
||||||
// 递增进度条的速度
|
// 递增进度条的速度
|
||||||
speed: 500,
|
speed: 500,
|
||||||
// 是否显示加载ico
|
// 是否显示加载ico
|
||||||
@ -12,6 +12,6 @@ NProgress.configure({
|
|||||||
trickleSpeed: 200,
|
trickleSpeed: 200,
|
||||||
// 初始化时的最小百分比
|
// 初始化时的最小百分比
|
||||||
minimum: 0.3
|
minimum: 0.3
|
||||||
});
|
})
|
||||||
|
|
||||||
export default NProgress;
|
export default NProgress
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import type { CSSProperties, VNodeChild } from "vue";
|
import type { CSSProperties, VNodeChild } from 'vue'
|
||||||
import {
|
import {
|
||||||
createTypes,
|
createTypes,
|
||||||
toValidableType,
|
toValidableType,
|
||||||
VueTypesInterface,
|
VueTypesInterface,
|
||||||
VueTypeValidableDef
|
VueTypeValidableDef
|
||||||
} from "vue-types";
|
} from 'vue-types'
|
||||||
|
|
||||||
export type VueNode = VNodeChild | JSX.Element;
|
export type VueNode = VNodeChild | JSX.Element
|
||||||
|
|
||||||
type PropTypes = VueTypesInterface & {
|
type PropTypes = VueTypesInterface & {
|
||||||
readonly style: VueTypeValidableDef<CSSProperties>;
|
readonly style: VueTypeValidableDef<CSSProperties>
|
||||||
readonly VNodeChild: VueTypeValidableDef<VueNode>;
|
readonly VNodeChild: VueTypeValidableDef<VueNode>
|
||||||
};
|
}
|
||||||
|
|
||||||
const newPropTypes = createTypes({
|
const newPropTypes = createTypes({
|
||||||
func: undefined,
|
func: undefined,
|
||||||
@ -20,20 +20,20 @@ const newPropTypes = createTypes({
|
|||||||
number: undefined,
|
number: undefined,
|
||||||
object: undefined,
|
object: undefined,
|
||||||
integer: undefined
|
integer: undefined
|
||||||
}) as PropTypes;
|
}) as PropTypes
|
||||||
|
|
||||||
// 从 vue-types v5.0 开始,extend()方法已经废弃,当前已改为官方推荐的ES6+方法 https://dwightjack.github.io/vue-types/advanced/extending-vue-types.html#the-extend-method
|
// 从 vue-types v5.0 开始,extend()方法已经废弃,当前已改为官方推荐的ES6+方法 https://dwightjack.github.io/vue-types/advanced/extending-vue-types.html#the-extend-method
|
||||||
export default class propTypes extends newPropTypes {
|
export default class propTypes extends newPropTypes {
|
||||||
// a native-like validator that supports the `.validable` method
|
// a native-like validator that supports the `.validable` method
|
||||||
static get style() {
|
static get style() {
|
||||||
return toValidableType("style", {
|
return toValidableType('style', {
|
||||||
type: [String, Object]
|
type: [String, Object]
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static get VNodeChild() {
|
static get VNodeChild() {
|
||||||
return toValidableType("VNodeChild", {
|
return toValidableType('VNodeChild', {
|
||||||
type: undefined
|
type: undefined
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
// 响应式storage
|
// 响应式storage
|
||||||
import { App } from "vue";
|
import { App } from 'vue'
|
||||||
import Storage from "responsive-storage";
|
import Storage from 'responsive-storage'
|
||||||
import { routerArrays } from "@/layout/types";
|
import { routerArrays } from '@/layout/types'
|
||||||
|
|
||||||
const nameSpace = "responsive-";
|
const nameSpace = 'responsive-'
|
||||||
|
|
||||||
export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
|
export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
|
||||||
const configObj = Object.assign(
|
const configObj = Object.assign(
|
||||||
{
|
{
|
||||||
// 国际化 默认中文zh
|
// 国际化 默认中文zh
|
||||||
locale: Storage.getData("locale", nameSpace) ?? {
|
locale: Storage.getData('locale', nameSpace) ?? {
|
||||||
locale: config.Locale ?? "zh"
|
locale: config.Locale ?? 'zh'
|
||||||
},
|
},
|
||||||
// layout模式以及主题
|
// layout模式以及主题
|
||||||
layout: Storage.getData("layout", nameSpace) ?? {
|
layout: Storage.getData('layout', nameSpace) ?? {
|
||||||
layout: config.Layout ?? "vertical",
|
layout: config.Layout ?? 'vertical',
|
||||||
theme: config.Theme ?? "default",
|
theme: config.Theme ?? 'default',
|
||||||
darkMode: config.DarkMode ?? false,
|
darkMode: config.DarkMode ?? false,
|
||||||
sidebarStatus: config.SidebarStatus ?? true,
|
sidebarStatus: config.SidebarStatus ?? true,
|
||||||
epThemeColor: config.EpThemeColor ?? "#409EFF"
|
epThemeColor: config.EpThemeColor ?? '#409EFF'
|
||||||
},
|
},
|
||||||
configure: Storage.getData("configure", nameSpace) ?? {
|
configure: Storage.getData('configure', nameSpace) ?? {
|
||||||
grey: config.Grey ?? false,
|
grey: config.Grey ?? false,
|
||||||
weak: config.Weak ?? false,
|
weak: config.Weak ?? false,
|
||||||
hideTabs: config.HideTabs ?? false,
|
hideTabs: config.HideTabs ?? false,
|
||||||
showLogo: config.ShowLogo ?? true,
|
showLogo: config.ShowLogo ?? true,
|
||||||
showModel: config.ShowModel ?? "smart",
|
showModel: config.ShowModel ?? 'smart',
|
||||||
multiTagsCache: config.MultiTagsCache ?? false
|
multiTagsCache: config.MultiTagsCache ?? false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
config.MultiTagsCache
|
config.MultiTagsCache
|
||||||
? {
|
? {
|
||||||
// 默认显示首页tag
|
// 默认显示首页tag
|
||||||
tags: Storage.getData("tags", nameSpace) ?? routerArrays
|
tags: Storage.getData('tags', nameSpace) ?? routerArrays
|
||||||
}
|
}
|
||||||
: {}
|
: {}
|
||||||
);
|
)
|
||||||
|
|
||||||
app.use(Storage, { nameSpace, memory: configObj });
|
app.use(Storage, { nameSpace, memory: configObj })
|
||||||
};
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { removeToken, setToken, type DataInfo } from "./auth";
|
import { removeToken, setToken, type DataInfo } from './auth'
|
||||||
import { subBefore, getQueryMap } from "@pureadmin/utils";
|
import { subBefore, getQueryMap } from '@pureadmin/utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 简版前端单点登录,根据实际业务自行编写
|
* 简版前端单点登录,根据实际业务自行编写
|
||||||
@ -10,50 +10,50 @@ import { subBefore, getQueryMap } from "@pureadmin/utils";
|
|||||||
* 3.删除不需要显示在 url 的参数
|
* 3.删除不需要显示在 url 的参数
|
||||||
* 4.使用 window.location.replace 跳转正确页面
|
* 4.使用 window.location.replace 跳转正确页面
|
||||||
*/
|
*/
|
||||||
(function () {
|
;(function () {
|
||||||
// 获取 url 中的参数
|
// 获取 url 中的参数
|
||||||
const params = getQueryMap(location.href) as DataInfo<Date>;
|
const params = getQueryMap(location.href) as DataInfo<Date>
|
||||||
const must = ["username", "roles", "accessToken"];
|
const must = ['username', 'roles', 'accessToken']
|
||||||
const mustLength = must.length;
|
const mustLength = must.length
|
||||||
if (Object.keys(params).length !== mustLength) return;
|
if (Object.keys(params).length !== mustLength) return
|
||||||
|
|
||||||
// url 参数满足 must 里的全部值,才判定为单点登录,避免非单点登录时刷新页面无限循环
|
// url 参数满足 must 里的全部值,才判定为单点登录,避免非单点登录时刷新页面无限循环
|
||||||
let sso = [];
|
let sso = []
|
||||||
let start = 0;
|
let start = 0
|
||||||
|
|
||||||
while (start < mustLength) {
|
while (start < mustLength) {
|
||||||
if (Object.keys(params).includes(must[start]) && sso.length <= mustLength) {
|
if (Object.keys(params).includes(must[start]) && sso.length <= mustLength) {
|
||||||
sso.push(must[start]);
|
sso.push(must[start])
|
||||||
} else {
|
} else {
|
||||||
sso = [];
|
sso = []
|
||||||
}
|
}
|
||||||
start++;
|
start++
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sso.length === mustLength) {
|
if (sso.length === mustLength) {
|
||||||
// 判定为单点登录
|
// 判定为单点登录
|
||||||
|
|
||||||
// 清空本地旧信息
|
// 清空本地旧信息
|
||||||
removeToken();
|
removeToken()
|
||||||
|
|
||||||
// 保存新信息到本地
|
// 保存新信息到本地
|
||||||
setToken(params);
|
setToken(params)
|
||||||
|
|
||||||
// 删除不需要显示在 url 的参数
|
// 删除不需要显示在 url 的参数
|
||||||
delete params["roles"];
|
delete params['roles']
|
||||||
delete params["accessToken"];
|
delete params['accessToken']
|
||||||
|
|
||||||
const newUrl = `${location.origin}${location.pathname}${subBefore(
|
const newUrl = `${location.origin}${location.pathname}${subBefore(
|
||||||
location.hash,
|
location.hash,
|
||||||
"?"
|
'?'
|
||||||
)}?${JSON.stringify(params)
|
)}?${JSON.stringify(params)
|
||||||
.replace(/["{}]/g, "")
|
.replace(/["{}]/g, '')
|
||||||
.replace(/:/g, "=")
|
.replace(/:/g, '=')
|
||||||
.replace(/,/g, "&")}`;
|
.replace(/,/g, '&')}`
|
||||||
|
|
||||||
// 替换历史记录项
|
// 替换历史记录项
|
||||||
window.location.replace(newUrl);
|
window.location.replace(newUrl)
|
||||||
} else {
|
} else {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
})();
|
})()
|
||||||
|
@ -5,20 +5,20 @@
|
|||||||
*/
|
*/
|
||||||
export const extractPathList = (tree: any[]): any => {
|
export const extractPathList = (tree: any[]): any => {
|
||||||
if (!Array.isArray(tree)) {
|
if (!Array.isArray(tree)) {
|
||||||
console.warn("tree must be an array");
|
console.warn('tree must be an array')
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
if (!tree || tree.length === 0) return [];
|
if (!tree || tree.length === 0) return []
|
||||||
const expandedPaths: Array<number | string> = [];
|
const expandedPaths: Array<number | string> = []
|
||||||
for (const node of tree) {
|
for (const node of tree) {
|
||||||
const hasChildren = node.children && node.children.length > 0;
|
const hasChildren = node.children && node.children.length > 0
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
extractPathList(node.children);
|
extractPathList(node.children)
|
||||||
}
|
}
|
||||||
expandedPaths.push(node.uniqueId);
|
expandedPaths.push(node.uniqueId)
|
||||||
}
|
}
|
||||||
return expandedPaths;
|
return expandedPaths
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 如果父级下children的length为1,删除children并自动组建唯一uniqueId
|
* @description 如果父级下children的length为1,删除children并自动组建唯一uniqueId
|
||||||
@ -28,24 +28,24 @@ export const extractPathList = (tree: any[]): any => {
|
|||||||
*/
|
*/
|
||||||
export const deleteChildren = (tree: any[], pathList = []): any => {
|
export const deleteChildren = (tree: any[], pathList = []): any => {
|
||||||
if (!Array.isArray(tree)) {
|
if (!Array.isArray(tree)) {
|
||||||
console.warn("menuTree must be an array");
|
console.warn('menuTree must be an array')
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
if (!tree || tree.length === 0) return [];
|
if (!tree || tree.length === 0) return []
|
||||||
for (const [key, node] of tree.entries()) {
|
for (const [key, node] of tree.entries()) {
|
||||||
if (node.children && node.children.length === 1) delete node.children;
|
if (node.children && node.children.length === 1) delete node.children
|
||||||
node.id = key;
|
node.id = key
|
||||||
node.parentId = pathList.length ? pathList[pathList.length - 1] : null;
|
node.parentId = pathList.length ? pathList[pathList.length - 1] : null
|
||||||
node.pathList = [...pathList, node.id];
|
node.pathList = [...pathList, node.id]
|
||||||
node.uniqueId =
|
node.uniqueId =
|
||||||
node.pathList.length > 1 ? node.pathList.join("-") : node.pathList[0];
|
node.pathList.length > 1 ? node.pathList.join('-') : node.pathList[0]
|
||||||
const hasChildren = node.children && node.children.length > 0;
|
const hasChildren = node.children && node.children.length > 0
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
deleteChildren(node.children, node.pathList);
|
deleteChildren(node.children, node.pathList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tree;
|
return tree
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 创建层级关系
|
* @description 创建层级关系
|
||||||
@ -55,21 +55,21 @@ export const deleteChildren = (tree: any[], pathList = []): any => {
|
|||||||
*/
|
*/
|
||||||
export const buildHierarchyTree = (tree: any[], pathList = []): any => {
|
export const buildHierarchyTree = (tree: any[], pathList = []): any => {
|
||||||
if (!Array.isArray(tree)) {
|
if (!Array.isArray(tree)) {
|
||||||
console.warn("tree must be an array");
|
console.warn('tree must be an array')
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
if (!tree || tree.length === 0) return [];
|
if (!tree || tree.length === 0) return []
|
||||||
for (const [key, node] of tree.entries()) {
|
for (const [key, node] of tree.entries()) {
|
||||||
node.id = key;
|
node.id = key
|
||||||
node.parentId = pathList.length ? pathList[pathList.length - 1] : null;
|
node.parentId = pathList.length ? pathList[pathList.length - 1] : null
|
||||||
node.pathList = [...pathList, node.id];
|
node.pathList = [...pathList, node.id]
|
||||||
const hasChildren = node.children && node.children.length > 0;
|
const hasChildren = node.children && node.children.length > 0
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
buildHierarchyTree(node.children, node.pathList);
|
buildHierarchyTree(node.children, node.pathList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tree;
|
return tree
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 广度优先遍历,根据唯一uniqueId找当前节点信息
|
* @description 广度优先遍历,根据唯一uniqueId找当前节点信息
|
||||||
@ -82,18 +82,18 @@ export const getNodeByUniqueId = (
|
|||||||
uniqueId: number | string
|
uniqueId: number | string
|
||||||
): any => {
|
): any => {
|
||||||
if (!Array.isArray(tree)) {
|
if (!Array.isArray(tree)) {
|
||||||
console.warn("menuTree must be an array");
|
console.warn('menuTree must be an array')
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
if (!tree || tree.length === 0) return [];
|
if (!tree || tree.length === 0) return []
|
||||||
const item = tree.find(node => node.uniqueId === uniqueId);
|
const item = tree.find(node => node.uniqueId === uniqueId)
|
||||||
if (item) return item;
|
if (item) return item
|
||||||
const childrenList = tree
|
const childrenList = tree
|
||||||
.filter(node => node.children)
|
.filter(node => node.children)
|
||||||
.map(i => i.children)
|
.map(i => i.children)
|
||||||
.flat(1) as unknown;
|
.flat(1) as unknown
|
||||||
return getNodeByUniqueId(childrenList as any[], uniqueId);
|
return getNodeByUniqueId(childrenList as any[], uniqueId)
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 向当前唯一uniqueId节点中追加字段
|
* @description 向当前唯一uniqueId节点中追加字段
|
||||||
@ -108,23 +108,23 @@ export const appendFieldByUniqueId = (
|
|||||||
fields: object
|
fields: object
|
||||||
): any => {
|
): any => {
|
||||||
if (!Array.isArray(tree)) {
|
if (!Array.isArray(tree)) {
|
||||||
console.warn("menuTree must be an array");
|
console.warn('menuTree must be an array')
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
if (!tree || tree.length === 0) return [];
|
if (!tree || tree.length === 0) return []
|
||||||
for (const node of tree) {
|
for (const node of tree) {
|
||||||
const hasChildren = node.children && node.children.length > 0;
|
const hasChildren = node.children && node.children.length > 0
|
||||||
if (
|
if (
|
||||||
node.uniqueId === uniqueId &&
|
node.uniqueId === uniqueId &&
|
||||||
Object.prototype.toString.call(fields) === "[object Object]"
|
Object.prototype.toString.call(fields) === '[object Object]'
|
||||||
)
|
)
|
||||||
Object.assign(node, fields);
|
Object.assign(node, fields)
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
appendFieldByUniqueId(node.children, uniqueId, fields);
|
appendFieldByUniqueId(node.children, uniqueId, fields)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tree;
|
return tree
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 构造树型结构数据
|
* @description 构造树型结构数据
|
||||||
@ -141,48 +141,48 @@ export const handleTree = (
|
|||||||
children?: string
|
children?: string
|
||||||
): any => {
|
): any => {
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
console.warn("data must be an array");
|
console.warn('data must be an array')
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
const config = {
|
const config = {
|
||||||
id: id || "id",
|
id: id || 'id',
|
||||||
parentId: parentId || "parentId",
|
parentId: parentId || 'parentId',
|
||||||
childrenList: children || "children"
|
childrenList: children || 'children'
|
||||||
};
|
}
|
||||||
|
|
||||||
const childrenListMap: any = {};
|
const childrenListMap: any = {}
|
||||||
const nodeIds: any = {};
|
const nodeIds: any = {}
|
||||||
const tree = [];
|
const tree = []
|
||||||
|
|
||||||
for (const d of data) {
|
for (const d of data) {
|
||||||
const parentId = d[config.parentId];
|
const parentId = d[config.parentId]
|
||||||
if (childrenListMap[parentId] == null) {
|
if (childrenListMap[parentId] == null) {
|
||||||
childrenListMap[parentId] = [];
|
childrenListMap[parentId] = []
|
||||||
}
|
}
|
||||||
nodeIds[d[config.id]] = d;
|
nodeIds[d[config.id]] = d
|
||||||
childrenListMap[parentId].push(d);
|
childrenListMap[parentId].push(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const d of data) {
|
for (const d of data) {
|
||||||
const parentId = d[config.parentId];
|
const parentId = d[config.parentId]
|
||||||
if (nodeIds[parentId] == null) {
|
if (nodeIds[parentId] == null) {
|
||||||
tree.push(d);
|
tree.push(d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const t of tree) {
|
for (const t of tree) {
|
||||||
adaptToChildrenList(t);
|
adaptToChildrenList(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
function adaptToChildrenList(o: Record<string, any>) {
|
function adaptToChildrenList(o: Record<string, any>) {
|
||||||
if (childrenListMap[o[config.id]] !== null) {
|
if (childrenListMap[o[config.id]] !== null) {
|
||||||
o[config.childrenList] = childrenListMap[o[config.id]];
|
o[config.childrenList] = childrenListMap[o[config.id]]
|
||||||
}
|
}
|
||||||
if (o[config.childrenList]) {
|
if (o[config.childrenList]) {
|
||||||
for (const c of o[config.childrenList]) {
|
for (const c of o[config.childrenList]) {
|
||||||
adaptToChildrenList(c);
|
adaptToChildrenList(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tree;
|
return tree
|
||||||
};
|
}
|
||||||
|
10
src/views/basicData/GameData.vue
Normal file
10
src/views/basicData/GameData.vue
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineOptions({
|
||||||
|
// name 作为一种规范最好必须写上并且和路由的name保持一致
|
||||||
|
name: 'GameData'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>牌局数据</h1>
|
||||||
|
</template>
|
14
src/views/basicData/LiveData.vue
Normal file
14
src/views/basicData/LiveData.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import ComingSoon from '@/views/error/fight.vue'
|
||||||
|
defineOptions({
|
||||||
|
// name 作为一种规范最好必须写上并且和路由的name保持一致
|
||||||
|
name: 'ComingSoon'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>实时数据</h1>
|
||||||
|
<ComingSoon />
|
||||||
|
</div>
|
||||||
|
</template>
|
10
src/views/basicData/queryClub/index.vue
Normal file
10
src/views/basicData/queryClub/index.vue
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineOptions({
|
||||||
|
// name 作为一种规范最好必须写上并且和路由的name保持一致
|
||||||
|
name: 'ClubData'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>俱乐部数据</h1>
|
||||||
|
</template>
|
10
src/views/basicData/queryClub/queryClub.vue
Normal file
10
src/views/basicData/queryClub/queryClub.vue
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineOptions({
|
||||||
|
// name 作为一种规范最好必须写上并且和路由的name保持一致
|
||||||
|
name: 'QueryClub'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>搜索俱乐部</h1>
|
||||||
|
</template>
|
10
src/views/basicData/queryUser/index.vue
Normal file
10
src/views/basicData/queryUser/index.vue
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineOptions({
|
||||||
|
// name 作为一种规范最好必须写上并且和路由的name保持一致
|
||||||
|
name: 'UserData'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>用户数据</h1>
|
||||||
|
</template>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user