feat: 项目初始化

This commit is contained in:
inc904 2023-03-17 11:59:52 +08:00
parent eae44feb59
commit c94d2e5737
112 changed files with 2886 additions and 2659 deletions

View File

@ -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
} }
] ]
} }
}; }

View File

@ -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'
}

View File

@ -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",

View File

@ -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.

View File

@ -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: 敬请期待。

View File

@ -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: [

View File

@ -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>

View File

@ -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')
}; }

View File

@ -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 })
}; }

View File

@ -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

View File

@ -1,5 +1,5 @@
import auth from "./src/auth"; import auth from './src/auth'
const Auth = auth; const Auth = auth
export { Auth }; export { Auth }

View File

@ -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
}; }
} }
}); })

View File

@ -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 }

View File

@ -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
}); })
} }
}); })
} }
} }

View File

@ -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
}); })
} }
} }
}); })

View File

@ -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: () => []
} }
); )
} }
}); })

View File

@ -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: () => []
} }
); )
} }
}); })

View File

@ -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)

View File

@ -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
} }

View File

@ -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 }

View File

@ -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']\"")
} }
} }
}; }

View File

@ -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)
} }
}; }

View File

@ -1,2 +1,2 @@
export * from "./auth"; export * from './auth'
export * from "./elResizeDetector"; export * from './elResizeDetector'

View File

@ -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>

View File

@ -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>

View File

@ -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'
} }
] ]
} }
]; ]

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -1,3 +1,3 @@
import SearchModal from "./SearchModal.vue"; import SearchModal from './SearchModal.vue'
export { SearchModal }; export { SearchModal }

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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.resolvepath.resolve windows使electron // 使path.posix.resolvepath.resolve windows使electron
return path.posix.resolve(props.basePath, routePath); return path.posix.resolve(props.basePath, routePath)
} }
} }
</script> </script>

View File

@ -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>

View File

@ -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>

View File

@ -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')
// tagtag // tagtag
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>

View File

@ -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>

View File

@ -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
}; }
} }

View File

@ -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
}; }
} }

View File

@ -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
}; }
} }

View File

@ -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
}; }
} }

View File

@ -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
}; }
} }

View File

@ -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
}; }
} }

View File

@ -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;
} }

View File

@ -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>

View File

@ -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
}; }

View File

@ -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
}; }
} }

View File

@ -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')
}); })

View File

@ -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)
} }

View File

@ -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

View File

@ -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)
}); })
} }

View File

@ -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)
} }

View File

@ -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

View 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')
}
}
]
}

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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
}; }

View File

@ -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 }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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
}; }

View File

@ -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)
} }

View File

@ -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 {

View File

@ -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;
} }

View File

@ -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 {

View File

@ -1,6 +1,6 @@
@mixin clearfix { @mixin clearfix {
&::after { &::after {
content: ""; content: '';
display: table; display: table;
clear: both; clear: both;
} }

View File

@ -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;
} }

View File

@ -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);

View File

@ -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()
} }
/** 格式化tokenjwt格式 */ /** 格式化tokenjwt格式 */
export const formatToken = (token: string): string => { export const formatToken = (token: string): string => {
return "Bearer " + token; return 'Bearer ' + token
}; }

View File

@ -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 {}

View File

@ -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()

View File

@ -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>;
} }

View File

@ -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 }

View File

@ -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>()

View File

@ -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

View File

@ -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

View File

@ -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
}); })
} }
} }

View File

@ -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 })
}; }

View File

@ -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
} }
})(); })()

View File

@ -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为1children并自动组建唯一uniqueId * @description children的length为1children并自动组建唯一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
}; }

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
defineOptions({
// name name
name: 'GameData'
})
</script>
<template>
<h1>牌局数据</h1>
</template>

View 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>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
defineOptions({
// name name
name: 'ClubData'
})
</script>
<template>
<h1>俱乐部数据</h1>
</template>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
defineOptions({
// name name
name: 'QueryClub'
})
</script>
<template>
<h1>搜索俱乐部</h1>
</template>

View 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