@@@@@@
6
git.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
git config --global http.proxy http://127.0.0.1:4780;
|
||||||
|
git config --global https.proxy https://127.0.0.1:4780;
|
||||||
|
|
||||||
|
exec /bin/bash
|
||||||
30
index.html
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta content="IE=edge" http-equiv="X-UA-Compatible">
|
||||||
|
<meta content="webkit" name="renderer"/>
|
||||||
|
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||||
|
<meta content="no-cache, no-store, must-revalidate" http-equiv="Cache-Control">
|
||||||
|
<link href="/favicon.ico" rel="icon" sizes="any">
|
||||||
|
<title>浙大学生公寓</title>
|
||||||
|
<meta content="浙大学生公寓 浙大 浙大公寓 海宁浙大 海宁浙大公寓 公寓 公寓 租房 入住 学生公寓" name="description">
|
||||||
|
<link href="/static/css/loading.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>https://equip.intl.zju.edu.cn/</noscript>
|
||||||
|
<div id="app">
|
||||||
|
<figure>
|
||||||
|
<div class="dot white"></div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
<script src="/src/main.ts" type="module"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
9
library/build/banner/index.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import banner from 'vite-plugin-banner'
|
||||||
|
|
||||||
|
export const createBanner = () => {
|
||||||
|
return [
|
||||||
|
banner(
|
||||||
|
` build: \u0056\u0075\u0065\u0020\u0053\u0068\u006f\u0070\u0020\u0056\u0069\u0074\u0065 \n copyright: \u0068\u0074\u0074\u0070\u0073\u003a\u002f\u002f\u0076\u0075\u0065\u006a\u0073\u002d\u0063\u006f\u0072\u0065\u002e\u0063\u006e\u002f\u0073\u0068\u006f\u0070\u002d\u0076\u0069\u0074\u0065 \n time: ${process.env.VITE_APP_UPDATE_TIME} \n`
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
18
library/build/compress/index.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import compressPlugin from 'vite-plugin-compression'
|
||||||
|
|
||||||
|
export const createCompress = (compress: any) => {
|
||||||
|
if (compress === 'brotli') {
|
||||||
|
return compressPlugin({
|
||||||
|
ext: '.br',
|
||||||
|
algorithm: 'brotliCompress',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compress === 'gzip' || compress) {
|
||||||
|
return compressPlugin({
|
||||||
|
ext: '.gz',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
5
library/build/https/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import basicSsl from '@vitejs/plugin-basic-ssl'
|
||||||
|
|
||||||
|
export const createHttps = () => {
|
||||||
|
return basicSsl()
|
||||||
|
}
|
||||||
71
library/build/index.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||||
|
import chokidar from 'chokidar'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import pc from 'picocolors'
|
||||||
|
import type { Plugin } from 'vite'
|
||||||
|
import { createBanner } from './banner/'
|
||||||
|
import { createCompress } from './compress/'
|
||||||
|
import { createHttps } from './https'
|
||||||
|
import { createMock } from './mock/'
|
||||||
|
import { createProgress } from './progress/'
|
||||||
|
import { createPwa } from './pwa/'
|
||||||
|
import { createSvgIcons } from './svgSprite/'
|
||||||
|
import { createUnPlugin } from './unplugin/'
|
||||||
|
import { createVisualizer } from './visualizer/'
|
||||||
|
import { compress, https, localEnabled, port, prodEnabled, pwa, pwaDev, report } from '/@/config/'
|
||||||
|
|
||||||
|
const viteApp = 'VITE_' + 'APP_'
|
||||||
|
const viteUser = 'VITE_' + 'USER_'
|
||||||
|
|
||||||
|
export const createVitePlugin = (env: Record<string, string>) => {
|
||||||
|
const vitePlugins: (Plugin | Plugin[])[] = [vue()]
|
||||||
|
const userName = env[`${viteApp}GITHUB_USER_NAME`]
|
||||||
|
const secretKey = env[`${viteApp}SECRET_KEY`]
|
||||||
|
const nodeEnv = env[`${viteUser}NODE_ENV`]
|
||||||
|
const isEmpty = (value: any) => {
|
||||||
|
return value == undefined || value == '' || value == null
|
||||||
|
}
|
||||||
|
if (isEmpty(userName) || isEmpty(secretKey)) return
|
||||||
|
if (nodeEnv !== 'development' && (isEmpty(userName) || isEmpty(secretKey))) return
|
||||||
|
vitePlugins.push(
|
||||||
|
vueJsx(),
|
||||||
|
createProgress(env),
|
||||||
|
createUnPlugin(env),
|
||||||
|
createMock(localEnabled, prodEnabled),
|
||||||
|
createSvgIcons(),
|
||||||
|
createBanner()
|
||||||
|
)
|
||||||
|
if (compress) vitePlugins.push(createCompress(compress))
|
||||||
|
if (pwa) vitePlugins.push(createPwa(nodeEnv, pwaDev))
|
||||||
|
if (https) vitePlugins.push(createHttps())
|
||||||
|
if (report) vitePlugins.push(createVisualizer())
|
||||||
|
return vitePlugins
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createWatch = (env: Record<string, string>) => {
|
||||||
|
const userName = env[`${viteApp}GITHUB_USER_NAME`]
|
||||||
|
const secretKey = env[`${viteApp}SECRET_KEY`]
|
||||||
|
const nodeEnv = env[`${viteUser}NODE_ENV`]
|
||||||
|
|
||||||
|
if (nodeEnv === 'production' && (userName === 'test' || secretKey === 'preview')) {
|
||||||
|
console.log(
|
||||||
|
`${pc.red(
|
||||||
|
'key错误'
|
||||||
|
)}`
|
||||||
|
)
|
||||||
|
process.exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeEnv === 'development') {
|
||||||
|
chokidar.watch('./src/views').on('change', (path) => {
|
||||||
|
if (path.endsWith('vue')) {
|
||||||
|
console.log(
|
||||||
|
`\n${pc.gray(dayjs().format('HH:mm:ss'))} ${pc.cyan('[Vue Sh' + 'op Vite]')} ${pc.cyan(`http://localhost:${port}/`)} ${pc.green(
|
||||||
|
'update success'
|
||||||
|
)} `
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
14
library/build/mock/index.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { viteMockServe } from 'vite-plugin-mock'
|
||||||
|
|
||||||
|
export const createMock = (localEnabled: boolean, prodEnabled: boolean) => {
|
||||||
|
return viteMockServe({
|
||||||
|
logger: false,
|
||||||
|
ignore: /^index/,
|
||||||
|
localEnabled,
|
||||||
|
prodEnabled,
|
||||||
|
injectCode: `
|
||||||
|
import { setupProdMockServer } from '/mock/index'
|
||||||
|
setupProdMockServer()
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
}
|
||||||
6
library/build/progress/index.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import progress from 'vite-plugin-vitebar'
|
||||||
|
|
||||||
|
export const createProgress = (env: Record<string, string>) => {
|
||||||
|
const projectName = 'school-apartment'
|
||||||
|
return progress({ env, projectName })
|
||||||
|
}
|
||||||
46
library/build/pwa/index.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
|
|
||||||
|
export const createPwa = (nodeEnv: string, pwaDev: boolean) => {
|
||||||
|
return VitePWA({
|
||||||
|
base: nodeEnv === 'development' && pwaDev ? '/' : './', // ./ 或 /
|
||||||
|
registerType: 'autoUpdate', // promp弹窗提示手动更新、autoUpdate自动更新,建议使用自动更新
|
||||||
|
workbox: {
|
||||||
|
cleanupOutdatedCaches: true,
|
||||||
|
},
|
||||||
|
devOptions: {
|
||||||
|
enabled: pwaDev,
|
||||||
|
},
|
||||||
|
manifest: {
|
||||||
|
lang: 'zh',
|
||||||
|
name: 'school-apartment',
|
||||||
|
short_name: 'school-apartment',
|
||||||
|
description: '迪联科技',
|
||||||
|
background_color: '#ffffff',
|
||||||
|
theme_color: '#ffffff',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: 'pwa-64x64.png',
|
||||||
|
sizes: '64x64',
|
||||||
|
type: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'pwa-192x192.png',
|
||||||
|
sizes: '192x192',
|
||||||
|
type: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'pwa-512x512.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'any',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'maskable-icon-512x512.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'maskable',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
10
library/build/svgSprite/index.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import path from 'node:path'
|
||||||
|
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
||||||
|
|
||||||
|
export const createSvgIcons = () => {
|
||||||
|
return createSvgIconsPlugin({
|
||||||
|
iconDirs: [path.resolve(process.cwd(), 'src/icon')],
|
||||||
|
symbolId: 'vab-icon-[name]',
|
||||||
|
// svgoOptions: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
316
library/build/unplugin/auto-imports.d.ts
vendored
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
const $baseAlert: typeof import('../../../src/hooks/index')['$baseAlert']
|
||||||
|
const $baseConfirm: typeof import('../../../src/hooks/index')['$baseConfirm']
|
||||||
|
const $baseLoading: typeof import('../../../src/hooks/index')['$baseLoading']
|
||||||
|
const $baseMessage: typeof import('../../../src/hooks/index')['$baseMessage']
|
||||||
|
const $baseNotify: typeof import('../../../src/hooks/index')['$baseNotify']
|
||||||
|
const $pub: typeof import('../../../src/hooks/index')['$pub']
|
||||||
|
const $sub: typeof import('../../../src/hooks/index')['$sub']
|
||||||
|
const $unsub: typeof import('../../../src/hooks/index')['$unsub']
|
||||||
|
const EffectScope: typeof import('vue')['EffectScope']
|
||||||
|
const ElMessage: typeof import('element-plus/es')['ElMessage']
|
||||||
|
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
|
||||||
|
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||||
|
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||||
|
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||||
|
const axios: typeof import('axios')['default']
|
||||||
|
const computed: typeof import('vue')['computed']
|
||||||
|
const computedAsync: typeof import('@vueuse/core')['computedAsync']
|
||||||
|
const computedEager: typeof import('@vueuse/core')['computedEager']
|
||||||
|
const computedInject: typeof import('@vueuse/core')['computedInject']
|
||||||
|
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
|
||||||
|
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
|
||||||
|
const controlledRef: typeof import('@vueuse/core')['controlledRef']
|
||||||
|
const createApp: typeof import('vue')['createApp']
|
||||||
|
const createEventHook: typeof import('@vueuse/core')['createEventHook']
|
||||||
|
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
|
||||||
|
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
|
||||||
|
const createPinia: typeof import('pinia')['createPinia']
|
||||||
|
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
|
||||||
|
const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate']
|
||||||
|
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
|
||||||
|
const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise']
|
||||||
|
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
|
||||||
|
const customRef: typeof import('vue')['customRef']
|
||||||
|
const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
|
||||||
|
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
|
||||||
|
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||||
|
const defineComponent: typeof import('vue')['defineComponent']
|
||||||
|
const defineStore: typeof import('pinia')['defineStore']
|
||||||
|
const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
|
||||||
|
const effectScope: typeof import('vue')['effectScope']
|
||||||
|
const extendRef: typeof import('@vueuse/core')['extendRef']
|
||||||
|
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||||
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||||
|
const h: typeof import('vue')['h']
|
||||||
|
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
|
||||||
|
const inject: typeof import('vue')['inject']
|
||||||
|
const injectLocal: typeof import('@vueuse/core')['injectLocal']
|
||||||
|
const isDefined: typeof import('@vueuse/core')['isDefined']
|
||||||
|
const isProxy: typeof import('vue')['isProxy']
|
||||||
|
const isReactive: typeof import('vue')['isReactive']
|
||||||
|
const isReadonly: typeof import('vue')['isReadonly']
|
||||||
|
const isRef: typeof import('vue')['isRef']
|
||||||
|
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
|
||||||
|
const mapActions: typeof import('pinia')['mapActions']
|
||||||
|
const mapGetters: typeof import('pinia')['mapGetters']
|
||||||
|
const mapState: typeof import('pinia')['mapState']
|
||||||
|
const mapStores: typeof import('pinia')['mapStores']
|
||||||
|
const mapWritableState: typeof import('pinia')['mapWritableState']
|
||||||
|
const markRaw: typeof import('vue')['markRaw']
|
||||||
|
const nextTick: typeof import('vue')['nextTick']
|
||||||
|
const onActivated: typeof import('vue')['onActivated']
|
||||||
|
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||||
|
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
|
||||||
|
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
|
||||||
|
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||||
|
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||||
|
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
|
||||||
|
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||||
|
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||||
|
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
|
||||||
|
const onLongPress: typeof import('@vueuse/core')['onLongPress']
|
||||||
|
const onMounted: typeof import('vue')['onMounted']
|
||||||
|
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||||
|
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||||
|
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||||
|
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||||
|
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
|
||||||
|
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||||
|
const onUpdated: typeof import('vue')['onUpdated']
|
||||||
|
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
|
||||||
|
const provide: typeof import('vue')['provide']
|
||||||
|
const provideLocal: typeof import('@vueuse/core')['provideLocal']
|
||||||
|
const reactify: typeof import('@vueuse/core')['reactify']
|
||||||
|
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
|
||||||
|
const reactive: typeof import('vue')['reactive']
|
||||||
|
const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
|
||||||
|
const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
|
||||||
|
const reactivePick: typeof import('@vueuse/core')['reactivePick']
|
||||||
|
const readonly: typeof import('vue')['readonly']
|
||||||
|
const ref: typeof import('vue')['ref']
|
||||||
|
const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
|
||||||
|
const refDebounced: typeof import('@vueuse/core')['refDebounced']
|
||||||
|
const refDefault: typeof import('@vueuse/core')['refDefault']
|
||||||
|
const refThrottled: typeof import('@vueuse/core')['refThrottled']
|
||||||
|
const refWithControl: typeof import('@vueuse/core')['refWithControl']
|
||||||
|
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||||
|
const resolveRef: typeof import('@vueuse/core')['resolveRef']
|
||||||
|
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
|
||||||
|
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||||
|
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||||
|
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||||
|
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||||
|
const shallowRef: typeof import('vue')['shallowRef']
|
||||||
|
const storeToRefs: typeof import('pinia')['storeToRefs']
|
||||||
|
const syncRef: typeof import('@vueuse/core')['syncRef']
|
||||||
|
const syncRefs: typeof import('@vueuse/core')['syncRefs']
|
||||||
|
const templateRef: typeof import('@vueuse/core')['templateRef']
|
||||||
|
const throttledRef: typeof import('@vueuse/core')['throttledRef']
|
||||||
|
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
|
||||||
|
const toRaw: typeof import('vue')['toRaw']
|
||||||
|
const toReactive: typeof import('@vueuse/core')['toReactive']
|
||||||
|
const toRef: typeof import('vue')['toRef']
|
||||||
|
const toRefs: typeof import('vue')['toRefs']
|
||||||
|
const toValue: typeof import('vue')['toValue']
|
||||||
|
const triggerRef: typeof import('vue')['triggerRef']
|
||||||
|
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
|
||||||
|
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
|
||||||
|
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
|
||||||
|
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
|
||||||
|
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
|
||||||
|
const unref: typeof import('vue')['unref']
|
||||||
|
const unrefElement: typeof import('@vueuse/core')['unrefElement']
|
||||||
|
const until: typeof import('@vueuse/core')['until']
|
||||||
|
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
||||||
|
const useAnimate: typeof import('@vueuse/core')['useAnimate']
|
||||||
|
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
|
||||||
|
const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
|
||||||
|
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
|
||||||
|
const useArrayFind: typeof import('@vueuse/core')['useArrayFind']
|
||||||
|
const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex']
|
||||||
|
const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast']
|
||||||
|
const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes']
|
||||||
|
const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin']
|
||||||
|
const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
|
||||||
|
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
|
||||||
|
const useArraySome: typeof import('@vueuse/core')['useArraySome']
|
||||||
|
const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique']
|
||||||
|
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
|
||||||
|
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
|
||||||
|
const useAttrs: typeof import('vue')['useAttrs']
|
||||||
|
const useBase64: typeof import('@vueuse/core')['useBase64']
|
||||||
|
const useBattery: typeof import('@vueuse/core')['useBattery']
|
||||||
|
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
|
||||||
|
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
|
||||||
|
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
|
||||||
|
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
|
||||||
|
const useCached: typeof import('@vueuse/core')['useCached']
|
||||||
|
const useClipboard: typeof import('@vueuse/core')['useClipboard']
|
||||||
|
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
|
||||||
|
const useCloned: typeof import('@vueuse/core')['useCloned']
|
||||||
|
const useColorMode: typeof import('@vueuse/core')['useColorMode']
|
||||||
|
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
|
||||||
|
const useCounter: typeof import('@vueuse/core')['useCounter']
|
||||||
|
const useCssModule: typeof import('vue')['useCssModule']
|
||||||
|
const useCssVar: typeof import('@vueuse/core')['useCssVar']
|
||||||
|
const useCssVars: typeof import('vue')['useCssVars']
|
||||||
|
const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
|
||||||
|
const useCycleList: typeof import('@vueuse/core')['useCycleList']
|
||||||
|
const useDark: typeof import('@vueuse/core')['useDark']
|
||||||
|
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
|
||||||
|
const useDebounce: typeof import('@vueuse/core')['useDebounce']
|
||||||
|
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
|
||||||
|
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
|
||||||
|
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
|
||||||
|
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
|
||||||
|
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
|
||||||
|
const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
|
||||||
|
const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
|
||||||
|
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
|
||||||
|
const useDraggable: typeof import('@vueuse/core')['useDraggable']
|
||||||
|
const useDropZone: typeof import('@vueuse/core')['useDropZone']
|
||||||
|
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
|
||||||
|
const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
|
||||||
|
const useElementHover: typeof import('@vueuse/core')['useElementHover']
|
||||||
|
const useElementSize: typeof import('@vueuse/core')['useElementSize']
|
||||||
|
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
|
||||||
|
const useEventBus: typeof import('@vueuse/core')['useEventBus']
|
||||||
|
const useEventListener: typeof import('@vueuse/core')['useEventListener']
|
||||||
|
const useEventSource: typeof import('@vueuse/core')['useEventSource']
|
||||||
|
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
|
||||||
|
const useFavicon: typeof import('@vueuse/core')['useFavicon']
|
||||||
|
const useFetch: typeof import('@vueuse/core')['useFetch']
|
||||||
|
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
|
||||||
|
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
|
||||||
|
const useFocus: typeof import('@vueuse/core')['useFocus']
|
||||||
|
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
|
||||||
|
const useFps: typeof import('@vueuse/core')['useFps']
|
||||||
|
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
|
||||||
|
const useGamepad: typeof import('@vueuse/core')['useGamepad']
|
||||||
|
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
|
||||||
|
const useI18n: typeof import('vue-i18n')['useI18n']
|
||||||
|
const useIdle: typeof import('@vueuse/core')['useIdle']
|
||||||
|
const useImage: typeof import('@vueuse/core')['useImage']
|
||||||
|
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
|
||||||
|
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
|
||||||
|
const useInterval: typeof import('@vueuse/core')['useInterval']
|
||||||
|
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
|
||||||
|
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
|
||||||
|
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
|
||||||
|
const useLink: typeof import('vue-router')['useLink']
|
||||||
|
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
|
||||||
|
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
|
||||||
|
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
|
||||||
|
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
|
||||||
|
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
|
||||||
|
const useMemoize: typeof import('@vueuse/core')['useMemoize']
|
||||||
|
const useMemory: typeof import('@vueuse/core')['useMemory']
|
||||||
|
const useMounted: typeof import('@vueuse/core')['useMounted']
|
||||||
|
const useMouse: typeof import('@vueuse/core')['useMouse']
|
||||||
|
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
|
||||||
|
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
|
||||||
|
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
|
||||||
|
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
|
||||||
|
const useNetwork: typeof import('@vueuse/core')['useNetwork']
|
||||||
|
const useNow: typeof import('@vueuse/core')['useNow']
|
||||||
|
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
|
||||||
|
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
|
||||||
|
const useOnline: typeof import('@vueuse/core')['useOnline']
|
||||||
|
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
|
||||||
|
const useParallax: typeof import('@vueuse/core')['useParallax']
|
||||||
|
const useParentElement: typeof import('@vueuse/core')['useParentElement']
|
||||||
|
const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver']
|
||||||
|
const usePermission: typeof import('@vueuse/core')['usePermission']
|
||||||
|
const usePointer: typeof import('@vueuse/core')['usePointer']
|
||||||
|
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
|
||||||
|
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
|
||||||
|
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
|
||||||
|
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
|
||||||
|
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
|
||||||
|
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
|
||||||
|
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
|
||||||
|
const usePrevious: typeof import('@vueuse/core')['usePrevious']
|
||||||
|
const useRafFn: typeof import('@vueuse/core')['useRafFn']
|
||||||
|
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
|
||||||
|
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
|
||||||
|
const useRoute: typeof import('vue-router')['useRoute']
|
||||||
|
const useRouter: typeof import('vue-router')['useRouter']
|
||||||
|
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
|
||||||
|
const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
|
||||||
|
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
|
||||||
|
const useScroll: typeof import('@vueuse/core')['useScroll']
|
||||||
|
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
|
||||||
|
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
|
||||||
|
const useShare: typeof import('@vueuse/core')['useShare']
|
||||||
|
const useSlots: typeof import('vue')['useSlots']
|
||||||
|
const useSorted: typeof import('@vueuse/core')['useSorted']
|
||||||
|
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
|
||||||
|
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
|
||||||
|
const useStepper: typeof import('@vueuse/core')['useStepper']
|
||||||
|
const useStorage: typeof import('@vueuse/core')['useStorage']
|
||||||
|
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
|
||||||
|
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
|
||||||
|
const useSupported: typeof import('@vueuse/core')['useSupported']
|
||||||
|
const useSwipe: typeof import('@vueuse/core')['useSwipe']
|
||||||
|
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
|
||||||
|
const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
|
||||||
|
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
|
||||||
|
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
|
||||||
|
const useThrottle: typeof import('@vueuse/core')['useThrottle']
|
||||||
|
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
|
||||||
|
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
|
||||||
|
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
|
||||||
|
const useTimeout: typeof import('@vueuse/core')['useTimeout']
|
||||||
|
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
|
||||||
|
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
|
||||||
|
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
|
||||||
|
const useTitle: typeof import('@vueuse/core')['useTitle']
|
||||||
|
const useToNumber: typeof import('@vueuse/core')['useToNumber']
|
||||||
|
const useToString: typeof import('@vueuse/core')['useToString']
|
||||||
|
const useToggle: typeof import('@vueuse/core')['useToggle']
|
||||||
|
const useTransition: typeof import('@vueuse/core')['useTransition']
|
||||||
|
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
|
||||||
|
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
|
||||||
|
const useVModel: typeof import('@vueuse/core')['useVModel']
|
||||||
|
const useVModels: typeof import('@vueuse/core')['useVModels']
|
||||||
|
const useVibrate: typeof import('@vueuse/core')['useVibrate']
|
||||||
|
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
|
||||||
|
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
|
||||||
|
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
|
||||||
|
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
|
||||||
|
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
|
||||||
|
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
|
||||||
|
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
|
||||||
|
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
|
||||||
|
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
|
||||||
|
const watch: typeof import('vue')['watch']
|
||||||
|
const watchArray: typeof import('@vueuse/core')['watchArray']
|
||||||
|
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
|
||||||
|
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
|
||||||
|
const watchDeep: typeof import('@vueuse/core')['watchDeep']
|
||||||
|
const watchEffect: typeof import('vue')['watchEffect']
|
||||||
|
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
|
||||||
|
const watchImmediate: typeof import('@vueuse/core')['watchImmediate']
|
||||||
|
const watchOnce: typeof import('@vueuse/core')['watchOnce']
|
||||||
|
const watchPausable: typeof import('@vueuse/core')['watchPausable']
|
||||||
|
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||||
|
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||||
|
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
|
||||||
|
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
|
||||||
|
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
|
||||||
|
const whenever: typeof import('@vueuse/core')['whenever']
|
||||||
|
}
|
||||||
|
// for type re-export
|
||||||
|
declare global {
|
||||||
|
// @ts-ignore
|
||||||
|
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||||
|
import('vue')
|
||||||
|
}
|
||||||
144
library/build/unplugin/components.d.ts
vendored
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
export {}
|
||||||
|
|
||||||
|
/* prettier-ignore */
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
Authorization: typeof import('./../../../src/views/index/vabAutoComponents/Authorization.vue')['default']
|
||||||
|
Develop: typeof import('./../../../src/views/index/vabAutoComponents/Develop.vue')['default']
|
||||||
|
ElAlert: typeof import('element-plus/es')['ElAlert']
|
||||||
|
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||||
|
ElBacktop: typeof import('element-plus/es')['ElBacktop']
|
||||||
|
ElBadge: typeof import('element-plus/es')['ElBadge']
|
||||||
|
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
||||||
|
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
||||||
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
|
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
|
||||||
|
ElCalendar: typeof import('element-plus/es')['ElCalendar']
|
||||||
|
ElCard: typeof import('element-plus/es')['ElCard']
|
||||||
|
ElCascader: typeof import('element-plus/es')['ElCascader']
|
||||||
|
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||||
|
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||||
|
ElCol: typeof import('element-plus/es')['ElCol']
|
||||||
|
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
||||||
|
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||||
|
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
|
||||||
|
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
|
||||||
|
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||||
|
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||||
|
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||||
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
|
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||||
|
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||||
|
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||||
|
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||||
|
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||||
|
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
||||||
|
ElForm: typeof import('element-plus/es')['ElForm']
|
||||||
|
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||||
|
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||||
|
ElImage: typeof import('element-plus/es')['ElImage']
|
||||||
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
|
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||||
|
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||||
|
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||||
|
ElOption: typeof import('element-plus/es')['ElOption']
|
||||||
|
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||||
|
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
|
||||||
|
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||||
|
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||||
|
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||||
|
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||||
|
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||||
|
ElRate: typeof import('element-plus/es')['ElRate']
|
||||||
|
ElResult: typeof import('element-plus/es')['ElResult']
|
||||||
|
ElRow: typeof import('element-plus/es')['ElRow']
|
||||||
|
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||||
|
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||||
|
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
|
||||||
|
ElStep: typeof import('element-plus/es')['ElStep']
|
||||||
|
ElSteps: typeof import('element-plus/es')['ElSteps']
|
||||||
|
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||||
|
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||||
|
ElTable: typeof import('element-plus/es')['ElTable']
|
||||||
|
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||||
|
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||||
|
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||||
|
ElTag: typeof import('element-plus/es')['ElTag']
|
||||||
|
ElText: typeof import('element-plus/es')['ElText']
|
||||||
|
ElTimeline: typeof import('element-plus/es')['ElTimeline']
|
||||||
|
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
|
||||||
|
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
|
||||||
|
ElTimeSelect: typeof import('element-plus/es')['ElTimeSelect']
|
||||||
|
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||||
|
ElTransfer: typeof import('element-plus/es')['ElTransfer']
|
||||||
|
ElTree: typeof import('element-plus/es')['ElTree']
|
||||||
|
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
|
||||||
|
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||||
|
ErrorContainer: typeof import('./../../../src/views/error/vabAutoComponents/ErrorContainer.vue')['default']
|
||||||
|
ImageUpload: typeof import('./../../../src/plugins/imageUpload/index.vue')['default']
|
||||||
|
LoginContainer: typeof import('./../../../src/views/login/vabAutoComponents/LoginContainer.vue')['default']
|
||||||
|
Pending: typeof import('./../../../src/views/index/vabAutoComponents/Pending.vue')['default']
|
||||||
|
Recommendation: typeof import('./../../../src/views/index/vabAutoComponents/Recommendation.vue')['default']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
VabAlert: typeof import('./../../components/VabAlert/index.vue')['default']
|
||||||
|
VabApp: typeof import('./../../components/VabApp/index.vue')['default']
|
||||||
|
VabAppMain: typeof import('./../../components/VabAppMain/index.vue')['default']
|
||||||
|
VabAvatar: typeof import('./../../components/VabAvatar/index.vue')['default']
|
||||||
|
VabBreadcrumb: typeof import('./../../components/VabBreadcrumb/index.vue')['default']
|
||||||
|
VabCard: typeof import('./../../components/VabCard/index.vue')['default']
|
||||||
|
VabChart: typeof import('./../../../src/plugins/VabChart/index.vue')['default']
|
||||||
|
VabCity: typeof import('./../../../src/plugins/VabCity/index.vue')['default']
|
||||||
|
VabColorfulCard: typeof import('./../../components/VabColorfulCard/index.vue')['default']
|
||||||
|
VabColorPicker: typeof import('./../../components/VabColorPicker/index.vue')['default']
|
||||||
|
VabColumnBar: typeof import('./../../components/VabColumnBar/index.vue')['default']
|
||||||
|
VabCount: typeof import('./../../../src/plugins/VabCount/index.vue')['default']
|
||||||
|
VabDark: typeof import('./../../components/VabDark/index.vue')['default']
|
||||||
|
VabDialog: typeof import('./../../components/VabDialog/index.vue')['default']
|
||||||
|
VabDivider: typeof import('./../../components/VabDivider/index.vue')['default']
|
||||||
|
VabDot: typeof import('./../../components/VabDot/index.vue')['default']
|
||||||
|
VabEditor: typeof import('./../../../src/plugins/VabEditor/index.vue')['default']
|
||||||
|
VabErrorLog: typeof import('./../../components/VabErrorLog/index.vue')['default']
|
||||||
|
VabErrorLogContent: typeof import('./../../components/VabErrorLog/components/VabErrorLogContent.vue')['default']
|
||||||
|
VabFallBar: typeof import('./../../components/VabFallBar/index.vue')['default']
|
||||||
|
VabFold: typeof import('./../../components/VabFold/index.vue')['default']
|
||||||
|
VabFontSize: typeof import('./../../components/VabFontSize/index.vue')['default']
|
||||||
|
VabFooter: typeof import('./../../components/VabFooter/index.vue')['default']
|
||||||
|
VabFullscreen: typeof import('./../../components/VabFullscreen/index.vue')['default']
|
||||||
|
VabHeader: typeof import('./../../components/VabHeader/index.vue')['default']
|
||||||
|
VabLink: typeof import('./../../components/VabLink/index.vue')['default']
|
||||||
|
VabLock: typeof import('./../../components/VabLock/index.vue')['default']
|
||||||
|
VabLogo: typeof import('./../../components/VabLogo/index.vue')['default']
|
||||||
|
VabMagnifier: typeof import('./../../../src/plugins/VabMagnifier/index.vue')['default']
|
||||||
|
VabMenu: typeof import('./../../components/VabMenu/index.vue')['default']
|
||||||
|
VabMenuItem: typeof import('./../../components/VabMenu/components/VabMenuItem.vue')['default']
|
||||||
|
VabNav: typeof import('./../../components/VabNav/index.vue')['default']
|
||||||
|
VabNotice: typeof import('./../../components/VabNotice/index.vue')['default']
|
||||||
|
VabPagination: typeof import('./../../components/VabPagination/index.vue')['default']
|
||||||
|
VabPaneSplit: typeof import('./../../../src/plugins/VabPaneSplit/index.vue')['default']
|
||||||
|
VabPlayer: typeof import('./../../../src/plugins/VabPlayer/index.vue')['default']
|
||||||
|
VabPlayerHls: typeof import('./../../../src/plugins/VabPlayer/VabPlayerHls.vue')['default']
|
||||||
|
VabQueryForm: typeof import('./../../components/VabQueryForm/index.vue')['default']
|
||||||
|
VabQueryFormBottomPanel: typeof import('./../../components/VabQueryForm/components/VabQueryFormBottomPanel.vue')['default']
|
||||||
|
VabQueryFormLeftPanel: typeof import('./../../components/VabQueryForm/components/VabQueryFormLeftPanel.vue')['default']
|
||||||
|
VabQueryFormRightPanel: typeof import('./../../components/VabQueryForm/components/VabQueryFormRightPanel.vue')['default']
|
||||||
|
VabQueryFormTopPanel: typeof import('./../../components/VabQueryForm/components/VabQueryFormTopPanel.vue')['default']
|
||||||
|
VabRefresh: typeof import('./../../components/VabRefresh/index.vue')['default']
|
||||||
|
VabRightTools: typeof import('./../../components/VabRightTools/index.vue')['default']
|
||||||
|
VabRouterView: typeof import('./../../components/VabRouterView/index.vue')['default']
|
||||||
|
VabSearch: typeof import('./../../components/VabSearch/index.vue')['default']
|
||||||
|
VabSideBar: typeof import('./../../components/VabSideBar/index.vue')['default']
|
||||||
|
VabStatistics: typeof import('./../../components/VabStatistics/index.vue')['default']
|
||||||
|
VabSubMenu: typeof import('./../../components/VabMenu/components/VabSubMenu.vue')['default']
|
||||||
|
VabTabs: typeof import('./../../components/VabTabs/index.vue')['default']
|
||||||
|
VabTabsSetting: typeof import('./../../components/VabTabs/components/VabTabsSetting.vue')['default']
|
||||||
|
VabUpdate: typeof import('./../../../src/plugins/VabUpdate/index.vue')['default']
|
||||||
|
}
|
||||||
|
export interface ComponentCustomProperties {
|
||||||
|
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||||
|
}
|
||||||
|
}
|
||||||
30
library/build/unplugin/index.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @description: 动态导入components
|
||||||
|
* @author sundan
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||||
|
import { unplugin } from 'vite-plugin-unplugin'
|
||||||
|
|
||||||
|
export const createUnPlugin = (env: Record<string, string>) => {
|
||||||
|
return unplugin({
|
||||||
|
env,
|
||||||
|
imports: [
|
||||||
|
'vue',
|
||||||
|
'pinia',
|
||||||
|
'vue-i18n',
|
||||||
|
'vue-router',
|
||||||
|
'@vueuse/core',
|
||||||
|
{
|
||||||
|
axios: [['default', 'axios']],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
resolvers: [
|
||||||
|
ElementPlusResolver({
|
||||||
|
importStyle: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
dirs: [
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
11
library/build/visualizer/index.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
|
||||||
|
export const createVisualizer: any = () => {
|
||||||
|
return visualizer({
|
||||||
|
filename: 'stats.html',
|
||||||
|
title: 'Rollup Stats',
|
||||||
|
gzipSize: true,
|
||||||
|
brotliSize: true,
|
||||||
|
emitFile: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
40
library/components/VabAlert/index.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<el-alert
|
||||||
|
:center="center"
|
||||||
|
:closable="closable"
|
||||||
|
:close-text="closeText"
|
||||||
|
:description="description"
|
||||||
|
:effect="effect"
|
||||||
|
:show-icon="showIcon"
|
||||||
|
:title="title"
|
||||||
|
:type="type"
|
||||||
|
v-bind="$attrs"
|
||||||
|
>
|
||||||
|
<template v-if="title || $slots.title" #title>
|
||||||
|
<slot name="title">
|
||||||
|
{{ title }}
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
<template v-if="$slots.default || description" #default>
|
||||||
|
<slot name="default">
|
||||||
|
{{ description }}
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ElAlert } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabAlert',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
...ElAlert.props,
|
||||||
|
closable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
18
library/components/VabApp/index.vue
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<template>
|
||||||
|
<el-config-provider :button="{ autoInsertSpace: true }" :locale="locale">
|
||||||
|
<router-view />
|
||||||
|
<vab-update v-if="pwa" />
|
||||||
|
</el-config-provider>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { pwa } from '/@/config'
|
||||||
|
import { enLocale, zhLocale } from '/@/i18n'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabApp',
|
||||||
|
})
|
||||||
|
|
||||||
|
const { locale: language } = useI18n()
|
||||||
|
const locale = computed(() => (language.value === 'en' ? enLocale : zhLocale))
|
||||||
|
</script>
|
||||||
30
library/components/VabAppMain/index.vue
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vab-app-main">
|
||||||
|
<section>
|
||||||
|
<vab-router-view />
|
||||||
|
<vab-footer />
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRoutesStore } from '/@/store/modules/routes'
|
||||||
|
import { handleActivePath } from '/@/utils/routes'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabAppMain',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const routesStore = useRoutesStore()
|
||||||
|
const { tab, activeMenu } = storeToRefs(routesStore)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
route,
|
||||||
|
() => {
|
||||||
|
if (tab.value.data !== route.matched[0].name) tab.value.data = route.matched[0].name
|
||||||
|
activeMenu.value.data = handleActivePath(route)
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
200
library/components/VabAvatar/index.vue
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
<template>
|
||||||
|
<el-popover
|
||||||
|
v-model:visible="visible"
|
||||||
|
class="vab-avatar"
|
||||||
|
popper-class="vab-avatar-popper"
|
||||||
|
width="188"
|
||||||
|
@hide="handleShow"
|
||||||
|
@show="handleHide"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<div class="avatar-dropdown">
|
||||||
|
<!-- <el-avatar class="user-avatar" :src="avatar" /> -->
|
||||||
|
<img src="../../../public/logo.png" alt="" style="width: 30px; height: 30px;margin-left: 20px; background: #59809b; border-radius: 50%;">
|
||||||
|
<div class="username">
|
||||||
|
<span class="hidden-xs-only">{{ username }}</span>
|
||||||
|
<vab-icon class="vab-dropdown" :class="{ 'vab-dropdown-active': active }" icon="arrow-down-s-line" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<div class="avatar-dropdown" @click="handleCommand('personalCenter')">
|
||||||
|
<!-- <el-avatar class="user-avatar" :src="avatar" /> -->
|
||||||
|
<img src="../../../public/logo.png"
|
||||||
|
style="width: 30px; height: 30px; margin-left: 20px; background: #59809b; border-radius: 50%;">
|
||||||
|
<div class="username">
|
||||||
|
<div>{{ username }}</div>
|
||||||
|
<div class="personal-center">
|
||||||
|
<el-text size="small" type="info">个人中心</el-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-divider />
|
||||||
|
<ul class="el-dropdown-menu">
|
||||||
|
<!-- <li class="el-dropdown-menu__item" @click="handleCommand('changeLog')">
|
||||||
|
<vab-icon icon="file-word-line" />
|
||||||
|
<span>{{ translate('更新日志') }}</span>
|
||||||
|
<el-tag effect="dark" size="small" type="danger">99+</el-tag>
|
||||||
|
</li> -->
|
||||||
|
<li class="el-dropdown-menu__item" @click="handleCommand('logout')">
|
||||||
|
<vab-icon icon="logout-circle-r-line" />
|
||||||
|
<span>{{ translate('退出登录') }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useUserStore } from '/@/store/modules/user'
|
||||||
|
import { toLoginRoute } from '/@/utils/routes'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabAvatar',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const { avatar, username } = storeToRefs(userStore)
|
||||||
|
const { logout } = userStore
|
||||||
|
const active = ref(false)
|
||||||
|
const visible = ref(false)
|
||||||
|
const handleShow = () => {
|
||||||
|
active.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleHide = () => {
|
||||||
|
active.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCommand = async (command) => {
|
||||||
|
switch (command) {
|
||||||
|
case 'logout': {
|
||||||
|
await logout()
|
||||||
|
await router.push(toLoginRoute(route.fullPath))
|
||||||
|
visible.value = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'personalCenter': {
|
||||||
|
await router.push('/setting/personalCenter')
|
||||||
|
visible.value = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'changeLog': {
|
||||||
|
await router.push('/changeLog')
|
||||||
|
visible.value = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'portal': {
|
||||||
|
await window.open('#/portal')
|
||||||
|
visible.value = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'dataScreen': {
|
||||||
|
await window.open('#/dataScreen')
|
||||||
|
visible.value = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.avatar-dropdown {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
justify-items: center;
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
padding: 8px;
|
||||||
|
margin-left: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
right: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
content: '';
|
||||||
|
background: var(--el-color-success);
|
||||||
|
border: 3px solid var(--el-color-white);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.username {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: max-content;
|
||||||
|
height: 40px;
|
||||||
|
margin-left: 6px;
|
||||||
|
line-height: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
[class*='ri-'] {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
.vab-avatar-popper {
|
||||||
|
padding: 0 !important;
|
||||||
|
|
||||||
|
.avatar-dropdown {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
justify-content: start !important;
|
||||||
|
padding: calc(var(--el-padding) / 1.5);
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
margin-left: calc(var(--el-margin) / 2) calc(var(--el-margin) / 2) calc(var(--el-margin) / 2) 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
line-height: 20px;
|
||||||
|
|
||||||
|
.personal-center {
|
||||||
|
width: 100%;
|
||||||
|
font-size: var(--el-font-size-small);
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown-menu {
|
||||||
|
position: relative;
|
||||||
|
padding: calc(var(--el-padding) / 2);
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
.el-tag {
|
||||||
|
position: absolute;
|
||||||
|
right: 17.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-divider--horizontal {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
49
library/components/VabBreadcrumb/index.vue
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<template>
|
||||||
|
<el-breadcrumb class="vab-breadcrumb" separator="/">
|
||||||
|
<el-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="index" :to="handleTo(item.redirect)">
|
||||||
|
<vab-icon v-if="item.meta && item.meta.icon" :icon="item.meta.icon" />
|
||||||
|
<span>{{ translate(item.meta.title) }}</span>
|
||||||
|
</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useRoutesStore } from '/@/store/modules/routes'
|
||||||
|
import { handleMatched } from '/@/utils/routes'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabBreadcrumb',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const routesStore = useRoutesStore()
|
||||||
|
const { getBreadcrumbRoutes: breadcrumbRoutes } = storeToRefs(routesStore)
|
||||||
|
|
||||||
|
const breadcrumbList = computed(() => {
|
||||||
|
const matchedRoutes = handleMatched(breadcrumbRoutes.value, route.fullPath).filter((item) => !item.meta.breadcrumbHidden)
|
||||||
|
if (matchedRoutes.length > 0) return matchedRoutes
|
||||||
|
else return handleMatched(breadcrumbRoutes.value, route.path).filter((item) => !item.meta.breadcrumbHidden)
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleTo = (path) => {
|
||||||
|
if (path) return { path }
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-breadcrumb {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: var(--el-nav-height);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-breadcrumb__item {
|
||||||
|
.el-breadcrumb__inner {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
76
library/components/VabCard/index.vue
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<el-card :body-class="bodyClass" :body-style="bodyStyle" :shadow="shadow" v-bind="$attrs">
|
||||||
|
<template v-if="$slots.header || title" #header>
|
||||||
|
<slot v-if="$slots.header" name="header"></slot>
|
||||||
|
<template v-else>{{ title }}</template>
|
||||||
|
</template>
|
||||||
|
<el-skeleton v-if="skeleton" animated :loading="skeletonShow" :rows="skeletonRows">
|
||||||
|
<template #default>
|
||||||
|
<slot></slot>
|
||||||
|
</template>
|
||||||
|
</el-skeleton>
|
||||||
|
<slot v-else></slot>
|
||||||
|
<template v-if="$slots.footer" #footer>
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</template>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElCard } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabCard',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
...ElCard.props,
|
||||||
|
shadow: {
|
||||||
|
type: String,
|
||||||
|
default: 'never',
|
||||||
|
},
|
||||||
|
skeleton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
skeletonRows: {
|
||||||
|
type: Number,
|
||||||
|
default: 5, //显示的数量会比传入的数量多 1
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const skeletonShow = ref(true)
|
||||||
|
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
skeletonShow.value = false
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (timer) clearTimeout(timer)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep() {
|
||||||
|
.el-card {
|
||||||
|
.el-card__header {
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
[class*='ri-'] {
|
||||||
|
background-image: linear-gradient(120deg, #bd34fe 30%, var(--el-color-primary));
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-skeleton {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
64
library/components/VabColorPicker/index.vue
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="'technology' != theme.themeName" class="vab-color-picker" style="margin-left: var(--el-margin)">
|
||||||
|
<el-color-picker
|
||||||
|
v-model="theme.color"
|
||||||
|
popper-class="vab-color-picker-popper"
|
||||||
|
:predefine="predefineColors"
|
||||||
|
@active-change="handleChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { color as _color } from '/@/config/'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabColorPicker',
|
||||||
|
})
|
||||||
|
|
||||||
|
const predefineColors = ref([
|
||||||
|
_color,
|
||||||
|
'#1e90ff',
|
||||||
|
'#4e6ef2',
|
||||||
|
'#0052d9',
|
||||||
|
'#3fb884',
|
||||||
|
'#16baa9',
|
||||||
|
'#07c160',
|
||||||
|
'#009688',
|
||||||
|
'#6954f0',
|
||||||
|
'#7b40f2',
|
||||||
|
'#f01414',
|
||||||
|
])
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { updateTheme, saveTheme } = settingsStore
|
||||||
|
const { theme } = storeToRefs(settingsStore)
|
||||||
|
|
||||||
|
const handleChange = (value) => {
|
||||||
|
theme.value.color = value
|
||||||
|
updateTheme()
|
||||||
|
saveTheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
// 还原默认
|
||||||
|
$sub('shop-vite-reset-color', () => {
|
||||||
|
handleChange(_color)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.vab-color-picker-popper {
|
||||||
|
box-sizing: content-box !important;
|
||||||
|
padding: calc(var(--el-padding) / 2);
|
||||||
|
|
||||||
|
.el-color-dropdown__link-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-color-dropdown__btns {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
77
library/components/VabColorfulCard/index.vue
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<el-card
|
||||||
|
:body-style="bodyStyle"
|
||||||
|
class="vab-colorful-card"
|
||||||
|
:shadow="shadow"
|
||||||
|
:style="
|
||||||
|
style
|
||||||
|
? style
|
||||||
|
: {
|
||||||
|
background: `linear-gradient(120deg, ${colorFrom} 10%, ${colorTo})`,
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template v-if="$slots.header" #header>
|
||||||
|
<slot name="header"></slot>
|
||||||
|
</template>
|
||||||
|
<vab-icon v-if="icon" :icon="icon" />
|
||||||
|
<slot></slot>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElCard } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabColorfulCard',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
...ElCard.props,
|
||||||
|
shadow: {
|
||||||
|
type: String,
|
||||||
|
default: 'never',
|
||||||
|
},
|
||||||
|
colorFrom: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
colorTo: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-colorful-card {
|
||||||
|
position: relative;
|
||||||
|
min-height: 120px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-card__header {
|
||||||
|
color: var(--el-color-white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
font-size: 60px;
|
||||||
|
transform: rotate(15deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
440
library/components/VabColumnBar/index.vue
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
<template>
|
||||||
|
<el-scrollbar
|
||||||
|
class="vab-column-bar"
|
||||||
|
:class="{
|
||||||
|
'is-collapse': collapse,
|
||||||
|
['vab-column-bar-' + theme.columnStyle]: true,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<vab-logo style="z-index: 999" />
|
||||||
|
<el-tabs v-model="tab.data" tab-position="left" @tab-click="handleTabClick">
|
||||||
|
<template v-for="(item, index) in routes" :key="index + item.name">
|
||||||
|
<el-tab-pane :name="item.name">
|
||||||
|
<template #label>
|
||||||
|
<div
|
||||||
|
class="vab-column-grid"
|
||||||
|
:class="{
|
||||||
|
['vab-column-grid-' + theme.columnStyle]: true,
|
||||||
|
}"
|
||||||
|
:title="translate(item.meta.title)"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<vab-icon v-if="item.meta.icon" :icon="item.meta.icon" :is-custom-svg="item.meta.isCustomSvg" />
|
||||||
|
<span v-if="translate(item.meta.title).length < 4">
|
||||||
|
{{ translate(item.meta.title) }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="font-size: var(--el-font-size-extra-small); zoom: 0.88">
|
||||||
|
{{ translate(item.meta.title) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-tab-pane>
|
||||||
|
</template>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<el-menu
|
||||||
|
ref="menuRef"
|
||||||
|
background-color="var(--el-menu-background-color-second)"
|
||||||
|
:default-active="activeMenu.data"
|
||||||
|
:default-openeds="defaultOpeneds"
|
||||||
|
mode="vertical"
|
||||||
|
:unique-opened="uniqueOpened"
|
||||||
|
>
|
||||||
|
<vab-menu v-for="item in partialRoutes" :key="item.path" :item="item" />
|
||||||
|
</el-menu>
|
||||||
|
<div class="float-fold">
|
||||||
|
<vab-fold fold="contract-left-line" unfold="contract-right-line" />
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defaultOpeneds, isHashRouterMode, openFirstMenu, uniqueOpened } from '/@/config'
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useRoutesStore } from '/@/store/modules/routes'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
import { isExternal } from '/@/utils/validate'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabColumnBar',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { collapse, device, theme } = storeToRefs(settingsStore)
|
||||||
|
const { foldSideBar, openSideBar } = settingsStore
|
||||||
|
const routesStore = useRoutesStore()
|
||||||
|
const {
|
||||||
|
getTab: tab,
|
||||||
|
getTabMenu: tabMenu,
|
||||||
|
getActiveMenu: activeMenu,
|
||||||
|
getRoutes: routes,
|
||||||
|
getPartialRoutes: partialRoutes,
|
||||||
|
} = storeToRefs(routesStore)
|
||||||
|
const menuRef = ref(null)
|
||||||
|
let timer
|
||||||
|
|
||||||
|
const setDefaultOpeneds = () => {
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
defaultOpeneds.forEach((item) => {
|
||||||
|
try {
|
||||||
|
menuRef.value.open(item)
|
||||||
|
} catch {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTabClick = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (tabMenu.value.meta.target === '_blank') {
|
||||||
|
if (route.path !== tabMenu.value.path) {
|
||||||
|
isHashRouterMode ? window.open(`#${tabMenu.value.path}`) : window.open(tabMenu.value.path)
|
||||||
|
router.push('/redirect')
|
||||||
|
}
|
||||||
|
} else if (isExternal(tabMenu.value.path)) {
|
||||||
|
window.open(tabMenu.value.path)
|
||||||
|
router.push('/redirect')
|
||||||
|
} else if (openFirstMenu) router.push(tabMenu.value.redirect || tabMenu.value)
|
||||||
|
setDefaultOpeneds()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
setDefaultOpeneds()
|
||||||
|
if (theme.value.layout === 'column')
|
||||||
|
watch(
|
||||||
|
route,
|
||||||
|
() => {
|
||||||
|
const foldUnfold = document.querySelector('.left-panel .fold-unfold')
|
||||||
|
const floatFold = document.querySelector('.float-fold')
|
||||||
|
if (route.meta.noColumn && theme.value.layout === 'column') {
|
||||||
|
if (device.value !== 'mobile') foldSideBar()
|
||||||
|
if (foldUnfold) foldUnfold.style = 'opacity:0'
|
||||||
|
if (floatFold) floatFold.style = 'opacity:0'
|
||||||
|
} else {
|
||||||
|
if (device.value !== 'mobile') openSideBar()
|
||||||
|
if (foldUnfold) foldUnfold.style = 'opacity:1'
|
||||||
|
if (floatFold) floatFold.style = 'opacity:1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (timer) clearTimeout(timer)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@mixin active {
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
|
||||||
|
i,
|
||||||
|
svg {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-column-bar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: var(--el-left-menu-width);
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--el-color-white);
|
||||||
|
border-right: 1px solid var(--el-border-color);
|
||||||
|
|
||||||
|
&-vertical,
|
||||||
|
&-card,
|
||||||
|
&-arrow {
|
||||||
|
:deep() {
|
||||||
|
.el-tabs + .el-menu {
|
||||||
|
left: var(--el-left-menu-width-min);
|
||||||
|
width: calc(var(--el-left-menu-width) - var(--el-left-menu-width-min));
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-horizontal,
|
||||||
|
&-semicircle {
|
||||||
|
.float-fold {
|
||||||
|
left: 26.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.vab-logo-column {
|
||||||
|
.logo {
|
||||||
|
width: calc(var(--el-left-menu-width-min) * 1.4) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
left: calc(var(--el-left-menu-width-min) * 1.4) !important;
|
||||||
|
width: calc(var(--el-left-menu-width) - calc(var(--el-left-menu-width-min) * 1.4) - 1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs + .el-menu {
|
||||||
|
left: calc(var(--el-left-menu-width-min) * 1.4);
|
||||||
|
width: calc(var(--el-left-menu-width) - var(--el-left-menu-width-min) * 1.4);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-card {
|
||||||
|
:deep() {
|
||||||
|
.el-tabs {
|
||||||
|
.el-tabs__item {
|
||||||
|
padding: 5px !important;
|
||||||
|
|
||||||
|
.vab-column-grid {
|
||||||
|
width: calc(var(--el-left-menu-width-min) - 12px) !important;
|
||||||
|
height: calc(var(--el-left-menu-width-min) - 10px) !important;
|
||||||
|
margin-left: 2px;
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
.vab-column-grid {
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs + .el-menu {
|
||||||
|
left: calc(var(--el-left-menu-width-min) + 10px);
|
||||||
|
width: calc(var(--el-left-menu-width) - var(--el-left-menu-width-min) - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-sub-menu .el-sub-menu__title,
|
||||||
|
.el-menu-item {
|
||||||
|
min-width: 180px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-arrow {
|
||||||
|
:deep() {
|
||||||
|
.el-tabs {
|
||||||
|
.el-tabs__item {
|
||||||
|
&.is-active {
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
.vab-column-grid {
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
right: -1px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
content: '';
|
||||||
|
border-color: transparent var(--el-color-white) transparent transparent;
|
||||||
|
border-style: solid dashed dashed;
|
||||||
|
border-width: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs + .el-menu {
|
||||||
|
left: calc(var(--el-left-menu-width-min) + 10px);
|
||||||
|
width: calc(var(--el-left-menu-width) - var(--el-left-menu-width-min) - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-sub-menu .el-sub-menu__title,
|
||||||
|
.el-menu-item {
|
||||||
|
min-width: 180px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-semicircle {
|
||||||
|
:deep() {
|
||||||
|
.el-tabs {
|
||||||
|
.el-tabs__item {
|
||||||
|
&.is-active {
|
||||||
|
border-top-left-radius: 99px;
|
||||||
|
border-bottom-left-radius: 99px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-column-grid {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: var(--el-left-menu-width-min);
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&-vertical,
|
||||||
|
&-card,
|
||||||
|
&-arrow {
|
||||||
|
justify-content: center;
|
||||||
|
height: var(--el-left-menu-width-min);
|
||||||
|
|
||||||
|
> div {
|
||||||
|
svg,
|
||||||
|
[class*='ri-'] {
|
||||||
|
display: block;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-horizontal,
|
||||||
|
&-semicircle {
|
||||||
|
justify-content: left;
|
||||||
|
width: calc(var(--el-left-menu-width-min) * 1.4);
|
||||||
|
height: calc(var(--el-left-menu-width-min) / 1.4);
|
||||||
|
padding-left: var(--el-padding);
|
||||||
|
|
||||||
|
svg,
|
||||||
|
[class*='ri-'] {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
* {
|
||||||
|
transition: var(--el-transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-scrollbar__wrap {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 9999;
|
||||||
|
height: calc(var(--vh, 1vh) * 100 - var(--el-logo-height));
|
||||||
|
|
||||||
|
.el-tabs__header.is-left {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
|
||||||
|
.el-tabs__nav-wrap.is-left {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
background: var(--el-menu-background-color);
|
||||||
|
|
||||||
|
.el-tabs__nav-scroll {
|
||||||
|
height: calc(var(--vh, 1vh) * 100 - var(--el-logo-height) * 2);
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__nav {
|
||||||
|
height: calc(var(--vh, 1vh) * 100 - var(--el-logo-height) * 2);
|
||||||
|
background: var(--el-menu-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
height: auto;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--el-color-white);
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
margin-right: -1px;
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
|
||||||
|
> .vab-column-grid {
|
||||||
|
margin-right: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__active-bar.is-left,
|
||||||
|
.el-tabs--left .el-tabs__nav-wrap.is-left::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu {
|
||||||
|
margin-top: 10px;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
.el-menu-item,
|
||||||
|
.el-sub-menu__title {
|
||||||
|
height: var(--el-menu-item-height);
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: var(--el-menu-item-height);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
@include active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-collapse {
|
||||||
|
:deep() {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-fold {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 13px;
|
||||||
|
left: 14px;
|
||||||
|
z-index: 9999;
|
||||||
|
width: 34px;
|
||||||
|
height: 34px;
|
||||||
|
line-height: 34px;
|
||||||
|
text-align: center;
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.fold-unfold,
|
||||||
|
.ri-building-line {
|
||||||
|
font-size: var(--el-font-size-extra-large);
|
||||||
|
color: var(--el-color-white);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
120
library/components/VabDark/index.vue
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<el-switch
|
||||||
|
v-if="'technology' != theme.themeName && 'plain' != theme.themeName && route.path !== '/goods/posterDesign'"
|
||||||
|
v-model="mode"
|
||||||
|
:active-icon="Moon"
|
||||||
|
active-value="dark"
|
||||||
|
class="vab-dark"
|
||||||
|
:inactive-icon="Sunny"
|
||||||
|
inactive-value="light"
|
||||||
|
inline-prompt
|
||||||
|
@click="_toggleDark($event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
//
|
||||||
|
|
||||||
|
import { Moon, Sunny } from '@element-plus/icons-vue'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabDark',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { theme, mode } = storeToRefs(settingsStore)
|
||||||
|
const { updateMode } = settingsStore
|
||||||
|
|
||||||
|
const _toggleDark = async (event) => {
|
||||||
|
if (typeof document.startViewTransition === 'function') {
|
||||||
|
const x = event.clientX
|
||||||
|
const y = event.clientY
|
||||||
|
const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y))
|
||||||
|
let isDark
|
||||||
|
const transition = document.startViewTransition(() => {
|
||||||
|
const root = document.documentElement
|
||||||
|
isDark = root.classList.contains('dark')
|
||||||
|
root.classList.remove(isDark ? 'dark' : 'light')
|
||||||
|
root.classList.add(isDark ? 'light' : 'dark')
|
||||||
|
handleSetScheme(isDark ? 'light' : 'dark')
|
||||||
|
})
|
||||||
|
await transition.ready.then(() => {
|
||||||
|
const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`]
|
||||||
|
document.documentElement.animate(
|
||||||
|
{
|
||||||
|
clipPath: isDark ? [...clipPath].reverse() : clipPath,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
duration: 500,
|
||||||
|
easing: 'ease-in',
|
||||||
|
pseudoElement: isDark ? '::view-transition-old(root)' : '::view-transition-new(root)',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const toggleDark = useToggle(handleUseDark())
|
||||||
|
await toggleDark()
|
||||||
|
}
|
||||||
|
await updateMode(localStorage.getItem('vueuse-color-scheme'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUseDark = () => {
|
||||||
|
return useDark()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleGetScheme = () => {
|
||||||
|
return localStorage.getItem('vueuse-color-scheme')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSetScheme = (value) => {
|
||||||
|
return localStorage.setItem('vueuse-color-scheme', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
// 还原默认
|
||||||
|
$sub('shop-vite-reset-dark', () => {
|
||||||
|
mode.value = handleGetScheme()
|
||||||
|
if (handleGetScheme() === 'dark') {
|
||||||
|
handleSetScheme('light')
|
||||||
|
handleUseDark()
|
||||||
|
mode.value = 'light'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
handleUseDark()
|
||||||
|
if (handleGetScheme() === 'auto') handleSetScheme('light')
|
||||||
|
mode.value = handleGetScheme()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
::view-transition-old(root),
|
||||||
|
::view-transition-new(root) {
|
||||||
|
mix-blend-mode: normal;
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(root) {
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-new(root) {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
&::view-transition-old(root) {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::view-transition-new(root) {
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-dark {
|
||||||
|
margin-left: var(--el-margin);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
120
library/components/VabDialog/index.vue
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:align-center="alignCenter"
|
||||||
|
:append-to="appendTo"
|
||||||
|
:append-to-body="appendToBody"
|
||||||
|
:before-close="beforeClose"
|
||||||
|
:center="center"
|
||||||
|
:class="'vab-dialog-' + theme"
|
||||||
|
:close-on-click-modal="closeOnClickModal"
|
||||||
|
:close-on-press-escape="closeOnPressEscape"
|
||||||
|
:destroy-on-close="destroyOnClose"
|
||||||
|
:draggable="draggable"
|
||||||
|
:fullscreen="isFullscreen"
|
||||||
|
:lock-scroll="lockScroll"
|
||||||
|
:modal="modal"
|
||||||
|
:modal-class="modalClass"
|
||||||
|
:open-delay="openDelay"
|
||||||
|
:overflow="overflow"
|
||||||
|
:show-close="showClose"
|
||||||
|
:style="{
|
||||||
|
transition: animated ? 'all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),transform 0s' : '',
|
||||||
|
}"
|
||||||
|
:top="top"
|
||||||
|
:width="width"
|
||||||
|
v-bind="$attrs"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<slot name="header">
|
||||||
|
<div class="el-dialog__title" @dblclick="setFullscreen">{{ title }}</div>
|
||||||
|
</slot>
|
||||||
|
<button v-if="showClose" class="el-dialog__headerbtn" type="button" @click="closeDialog">
|
||||||
|
<el-icon class="el-dialog__close">
|
||||||
|
<close />
|
||||||
|
</el-icon>
|
||||||
|
</button>
|
||||||
|
<button v-if="showFullscreen" class="el-dialog__headerbtn" style="right: 51px" type="button" @click="setFullscreen">
|
||||||
|
<vab-icon class="el-dialog__close el-dialog__fullscreen" :icon="isFullscreen ? 'fullscreen-exit-fill' : 'fullscreen-fill'" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { Close } from '@element-plus/icons-vue'
|
||||||
|
import { ElDialog } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabDialog',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
...ElDialog.props,
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
showFullscreen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
fullscreen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
animated: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
closeOnClickModal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
closeOnPressEscape: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
default: 'default', //支持default、plain、primary三种
|
||||||
|
},
|
||||||
|
draggable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const dialogVisible = useVModel(props, 'modelValue', emit)
|
||||||
|
const isFullscreen = ref(false)
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
isFullscreen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const setFullscreen = () => {
|
||||||
|
isFullscreen.value = !isFullscreen.value
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
props,
|
||||||
|
() => {
|
||||||
|
isFullscreen.value = props.fullscreen
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
98
library/components/VabDivider/index.vue
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<blockquote
|
||||||
|
v-if="blockquote"
|
||||||
|
class="vab-blockquote"
|
||||||
|
:class="isBorder ? 'vab-blockquote-' + type + ' is-border' : 'vab-blockquote-' + type"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</blockquote>
|
||||||
|
<fieldset v-else-if="fieldset" class="vab-fieldset">
|
||||||
|
<legend>{{ title }}</legend>
|
||||||
|
<slot></slot>
|
||||||
|
</fieldset>
|
||||||
|
<el-divider v-else :border-style="borderStyle" :content-position="contentPosition" :direction="direction">
|
||||||
|
<template #default>
|
||||||
|
<slot></slot>
|
||||||
|
</template>
|
||||||
|
</el-divider>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElDivider } from 'element-plus'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabDivider',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
...ElDivider.props,
|
||||||
|
blockquote: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
fieldset: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'primary', // 类型: primary / success / warning / danger / info
|
||||||
|
},
|
||||||
|
isBorder: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-blockquote {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
line-height: 1.8;
|
||||||
|
background-color: var(--el-background-color);
|
||||||
|
border-left: 5px solid var(--el-color-primary);
|
||||||
|
border-radius: 0 var(--el-border-radius-base) var(--el-border-radius-base) 0;
|
||||||
|
|
||||||
|
&-primary {
|
||||||
|
border-left: 5px solid var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-success {
|
||||||
|
border-left: 5px solid var(--el-color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-warning {
|
||||||
|
border-left: 5px solid var(--el-color-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-danger {
|
||||||
|
border-left: 5px solid var(--el-color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-info {
|
||||||
|
border-left: 5px solid var(--el-color-info);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-border {
|
||||||
|
border-top: 1px solid var(--el-border-color);
|
||||||
|
border-right: 1px solid var(--el-border-color);
|
||||||
|
border-bottom: 1px solid var(--el-border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-fieldset {
|
||||||
|
padding: var(--el-padding);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
|
||||||
|
legend {
|
||||||
|
padding: 0 var(--el-padding) 0 var(--el-padding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
75
library/components/VabDot/index.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<span :class="'vab-dot vab-dot-' + type"></span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabDot',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
type: {
|
||||||
|
values: ['primary', 'success', 'info', 'warning', 'danger'],
|
||||||
|
type: String,
|
||||||
|
default: 'primary',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
/**
|
||||||
|
* @name: vab-dot
|
||||||
|
* @description: vab圆点动画
|
||||||
|
* @author: sundan
|
||||||
|
* @date: 2024-08-05 22:53:00
|
||||||
|
*/
|
||||||
|
.vab-dot {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
margin-right: 3px;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
@keyframes vabDot {
|
||||||
|
0% {
|
||||||
|
opacity: 0.6;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(2.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
content: '';
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: vabDot 1.2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin set-color($color) {
|
||||||
|
&-#{$color} {
|
||||||
|
background: var(--el-color-#{$color});
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background: var(--el-color-#{$color});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include set-color(primary);
|
||||||
|
@include set-color(success);
|
||||||
|
@include set-color(warning);
|
||||||
|
@include set-color(error);
|
||||||
|
@include set-color(danger);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<el-table border :data="errorLogs">
|
||||||
|
<el-table-column label="报错路由">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button :href="row.url" rel="noopener noreferrer" tag="a" target="_blank" text type="success">{{ row.url }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="错误信息">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag type="danger">{{ row.err.message }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tooltip :content="row.err.stack" effect="light">
|
||||||
|
<el-button text type="primary">错误详情</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<a>
|
||||||
|
<el-button
|
||||||
|
v-for="(item, index) in searchList"
|
||||||
|
:key="index"
|
||||||
|
:href="item.url + row.err.message"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
tag="a"
|
||||||
|
target="_blank"
|
||||||
|
text
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
{{ item.title }}
|
||||||
|
</el-button>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty class="vab-data-empty" description="暂无数据" />
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useErrorLogStore } from '/@/store/modules/errorLog'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabErrorLogContent',
|
||||||
|
})
|
||||||
|
|
||||||
|
const errorLogStore = useErrorLogStore()
|
||||||
|
const { errorLogs } = storeToRefs(errorLogStore)
|
||||||
|
|
||||||
|
const searchList = ref<any>([
|
||||||
|
{
|
||||||
|
title: '百度搜索',
|
||||||
|
url: 'https://www.baidu.com/baidu?wd=',
|
||||||
|
icon: 'baidu-line',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '谷歌搜索',
|
||||||
|
url: 'https://www.google.com/search?q=',
|
||||||
|
icon: 'google-line',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
</script>
|
||||||
33
library/components/VabErrorLog/index.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="errorLogs.length > 0">
|
||||||
|
<el-badge type="danger" :value="errorLogs.length" @click="dialogVisible = true">
|
||||||
|
<vab-icon icon="bug-line" />
|
||||||
|
</el-badge>
|
||||||
|
|
||||||
|
<vab-dialog v-model="dialogVisible" append-to-body title="公寓项目异常捕获" width="60%">
|
||||||
|
<vab-error-log-content />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="danger" @click="clearAll">暂不显示</el-button>
|
||||||
|
</template>
|
||||||
|
</vab-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useErrorLogStore } from '/@/store/modules/errorLog'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabErrorLog',
|
||||||
|
})
|
||||||
|
|
||||||
|
const errorLogStore = useErrorLogStore()
|
||||||
|
const { errorLogs } = storeToRefs(errorLogStore)
|
||||||
|
const { clearErrorLog } = errorLogStore
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
|
||||||
|
const clearAll = () => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
clearErrorLog()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
377
library/components/VabFallBar/index.vue
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="vab-fall-bar"
|
||||||
|
:class="{
|
||||||
|
'is-collapse': collapse,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<vab-logo style="z-index: 999" />
|
||||||
|
<fall-menu :data="handleRoutes">
|
||||||
|
<template #level1="{ slotScope }">
|
||||||
|
<a :title="translate(slotScope.meta.title)" @click="handleLink(slotScope)">
|
||||||
|
<vab-icon :icon="slotScope.meta && slotScope.meta.icon" />
|
||||||
|
<span>{{ translate(slotScope.meta.title) }}</span>
|
||||||
|
<vab-icon v-if="slotScope.children" class="fall-icon-right" icon="arrow-right-s-line" />
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<template #level2="{ slotScope }">
|
||||||
|
<span style="cursor: pointer" @click="handleLink(slotScope)">
|
||||||
|
<vab-icon :icon="slotScope.meta && slotScope.meta.icon" />
|
||||||
|
<span>{{ translate(slotScope.meta.title) }}</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #level3="{ slotScope }">
|
||||||
|
<a v-for="(level3, index) in slotScope" :key="index" :href="level3.url" @click="handleLink(level3)">
|
||||||
|
- {{ translate(level3.meta.title) }}
|
||||||
|
<el-tag v-if="level3.meta && level3.meta.badge" effect="dark" size="small" type="danger">
|
||||||
|
{{ level3.meta.badge }}
|
||||||
|
</el-tag>
|
||||||
|
<vab-dot v-if="level3.meta && level3.meta.dot" type="danger" />
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</fall-menu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
/**
|
||||||
|
* @author:zxwk-sundan
|
||||||
|
* @description:瀑布菜单最多支持到三级菜单,不支持左侧点击选中,且非element-plus官方组件,生产环境请谨慎使用
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FallMenu } from '@opentiny/vue'
|
||||||
|
import { isHashRouterMode } from '/@/config'
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useRoutesStore } from '/@/store/modules/routes'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
import { isExternal } from '/@/utils/validate'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabFallBar',
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const routesStore = useRoutesStore()
|
||||||
|
const { getRoutes: routes } = storeToRefs(routesStore)
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const { device, collapse } = storeToRefs(settingsStore)
|
||||||
|
const { foldSideBar } = settingsStore
|
||||||
|
const { enter, exit } = useFullscreen()
|
||||||
|
const mousePosition = ref({ x: 0, y: 0 })
|
||||||
|
|
||||||
|
const handleRoutes = computed(() =>
|
||||||
|
routes.value.flatMap((route) => (route.meta.levelHidden && route.children ? [...route.children] : route))
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleLink = (slotScope) => {
|
||||||
|
nextTick(() => {
|
||||||
|
const routePath = slotScope.path
|
||||||
|
const target = slotScope.meta.target
|
||||||
|
const fullscreen = slotScope.meta.fullscreen
|
||||||
|
|
||||||
|
if (target === '_blank') {
|
||||||
|
if (isExternal(routePath)) {
|
||||||
|
window.open(routePath)
|
||||||
|
router.push('/redirect')
|
||||||
|
} else if (route.path !== routePath) isHashRouterMode ? window.open(`#${routePath}`) : window.open(routePath)
|
||||||
|
router.push('/redirect')
|
||||||
|
} else {
|
||||||
|
if (isExternal(routePath)) window.location.href = routePath
|
||||||
|
else if (route.path === routePath) {
|
||||||
|
$pub('reload-router-view')
|
||||||
|
} else {
|
||||||
|
if (device.value === 'mobile') foldSideBar()
|
||||||
|
if (slotScope.children) router.push(slotScope.redirect)
|
||||||
|
else router.push(slotScope.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (fullscreen) enter()
|
||||||
|
else exit()
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEventListener('mousemove', (e) => {
|
||||||
|
mousePosition.value = {
|
||||||
|
x: e.clientX,
|
||||||
|
y: e.clientY,
|
||||||
|
}
|
||||||
|
if ((mousePosition.value.x < 265 && !collapse.value) || (mousePosition.value.x < 65 && collapse.value)) {
|
||||||
|
const element = document.querySelector('.vab-fall-bar .tiny-fall-menu__box')
|
||||||
|
const base = 60
|
||||||
|
const intervalSize = 48
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const lowerBound = base + i * intervalSize
|
||||||
|
const upperBound = lowerBound + intervalSize
|
||||||
|
if (mousePosition.value.y > lowerBound && mousePosition.value.y < upperBound) {
|
||||||
|
mousePosition.value.y = lowerBound - 60
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
element.style.top = `${mousePosition.value.y}px`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-fall-bar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: var(--el-z-index);
|
||||||
|
width: calc(var(--el-left-menu-width) - 1px);
|
||||||
|
background: var(--el-menu-background-color);
|
||||||
|
border-right: 1px solid var(--el-border-color);
|
||||||
|
|
||||||
|
.fall-icon-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.tiny-fall-menu {
|
||||||
|
--ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
|
||||||
|
--ti-fall-menu-bg-color-hover: var(--el-color-primary);
|
||||||
|
--ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
|
||||||
|
--ti-fall-menu-box-text-color: var(--el-color-white);
|
||||||
|
--ti-fall-menu-slot-text-color: var(--el-color-white);
|
||||||
|
--ti-common-font-size-base: var(--el-font-size-base);
|
||||||
|
--ti-fall-menu-title-font-size: var(--el-font-size-base);
|
||||||
|
--ti-fall-menu-box-width: 560px;
|
||||||
|
|
||||||
|
&__nav {
|
||||||
|
height: calc(var(--vh, 1vh) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__wrap {
|
||||||
|
padding: 0;
|
||||||
|
background: var(--ti-fall-menu-bg-color-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subnav {
|
||||||
|
.icon-slot-left,
|
||||||
|
.icon-slot-right {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
right: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
|
min-width: 100%;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: block;
|
||||||
|
float: none;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
margin: 0 10px 0px 10px !important;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
|
||||||
|
[class*='ri-'] {
|
||||||
|
margin-left: 1.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*='ri-'] + span {
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fall-hide {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__box {
|
||||||
|
top: 5px;
|
||||||
|
left: var(--el-left-menu-width);
|
||||||
|
min-width: var(--ti-fall-menu-box-width);
|
||||||
|
padding: var(--el-padding);
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 0;
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
box-shadow: none;
|
||||||
|
transition:
|
||||||
|
all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
|
||||||
|
top 0.1s !important;
|
||||||
|
|
||||||
|
.cont {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sublist {
|
||||||
|
li {
|
||||||
|
h3.mcate-item-hd {
|
||||||
|
color: var(--ti-fall-menu-box-title-text-color);
|
||||||
|
|
||||||
|
[class*='ri-'] + span {
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.mcate-item-bd {
|
||||||
|
a {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--ti-fall-menu-box-text-color);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 5px 0 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-collapse {
|
||||||
|
width: 65px;
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.tiny-fall-menu {
|
||||||
|
&__list {
|
||||||
|
li {
|
||||||
|
a {
|
||||||
|
[class*='ri-'] + span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fall-hide {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__box {
|
||||||
|
left: calc(var(--el-left-menu-width-min) + 2px);
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.vab-theme-technology {
|
||||||
|
.tiny-fall-menu {
|
||||||
|
--ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
|
||||||
|
--ti-fall-menu-bg-color-hover: var(--el-color-primary);
|
||||||
|
--ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
|
||||||
|
--ti-fall-menu-box-text-color: #fff !important;
|
||||||
|
--ti-fall-menu-slot-text-color: var(--el-color-white);
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
a {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__box {
|
||||||
|
border: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.sublist {
|
||||||
|
li {
|
||||||
|
h3.mcate-item-hd {
|
||||||
|
color: var(--ti-fall-menu-box-title-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.mcate-item-bd {
|
||||||
|
a {
|
||||||
|
color: var(--ti-fall-menu-box-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-theme-plain {
|
||||||
|
.tiny-fall-menu {
|
||||||
|
--ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
|
||||||
|
--ti-fall-menu-bg-color-hover: var(--el-color-primary);
|
||||||
|
--ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
|
||||||
|
--ti-fall-menu-box-text-color: var(--el-color-grey) !important;
|
||||||
|
--ti-fall-menu-slot-text-color: var(--el-color-grey) !important;
|
||||||
|
|
||||||
|
&__box {
|
||||||
|
border: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.sublist {
|
||||||
|
li {
|
||||||
|
h3.mcate-item-hd {
|
||||||
|
color: #515a6e !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.mcate-item-bd {
|
||||||
|
a {
|
||||||
|
color: var(--ti-fall-menu-box-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
a:hover {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
.tiny-fall-menu {
|
||||||
|
--ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
|
||||||
|
--ti-fall-menu-bg-color-hover: var(--el-color-primary);
|
||||||
|
--ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
|
||||||
|
--ti-fall-menu-box-text-color: var(--el-color-grey) !important;
|
||||||
|
--ti-fall-menu-slot-text-color: var(--el-color-grey) !important;
|
||||||
|
|
||||||
|
&__box {
|
||||||
|
border: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.sublist {
|
||||||
|
li {
|
||||||
|
h3.mcate-item-hd {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.mcate-item-bd {
|
||||||
|
a {
|
||||||
|
color: var(--ti-fall-menu-box-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
a:hover {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
32
library/components/VabFold/index.vue
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<vab-icon class="fold-unfold" :icon="collapse ? unfold : fold" @click="toggleCollapse" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabFold',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
unfold: {
|
||||||
|
type: String,
|
||||||
|
default: 'menu-unfold-line',
|
||||||
|
},
|
||||||
|
fold: {
|
||||||
|
type: String,
|
||||||
|
default: 'menu-fold-line',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { collapse } = storeToRefs(settingsStore)
|
||||||
|
const { toggleCollapse } = settingsStore
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.fold-unfold {
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
29
library/components/VabFontSize/index.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<template>
|
||||||
|
<el-dropdown class="vab-language" @command="handleCommand">
|
||||||
|
<vab-icon icon="font-size-2" />
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item v-for="item in fontSizeList" :key="item" :command="item">{{ item }}</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabFontSize',
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { theme } = storeToRefs(settingsStore)
|
||||||
|
const { updateTheme, saveTheme } = settingsStore
|
||||||
|
const fontSizeList = ref(['13px', '13.5px', '14px', '15px', '15.5px', '16px'])
|
||||||
|
|
||||||
|
const handleCommand = (fontSize) => {
|
||||||
|
theme.value.fontSize = fontSize
|
||||||
|
updateTheme()
|
||||||
|
saveTheme()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
63
library/components/VabFooter/index.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<footer v-if="theme?.showFooter" class="vab-footer">
|
||||||
|
Copyright
|
||||||
|
<vab-icon icon="copyright-line" />
|
||||||
|
{{ fullYear }} {{ title }}
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-if="beian"
|
||||||
|
class="hidden-xs-only"
|
||||||
|
href="https://beian.miit.gov.cn/#/Integrated/index"
|
||||||
|
style="margin-left: 3px; color: var(--el-color-grey)"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ beian }}
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabFooter',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const fullYear = new Date().getFullYear()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { title, theme } = storeToRefs(settingsStore)
|
||||||
|
const beian = ref(localStorage.getItem('beian'))
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
if (route.query && route.query.beian) {
|
||||||
|
beian.value = route.query.beian
|
||||||
|
localStorage.setItem('beian', beian.value)
|
||||||
|
} else {
|
||||||
|
// 备案号
|
||||||
|
if (location.hostname.includes('beautiful')) beian.value = ''
|
||||||
|
// 官方站点
|
||||||
|
if (location.hostname.includes('vuejs-core')) beian.value = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: var(--el-footer-height);
|
||||||
|
padding: 0 var(--el-padding) 0 var(--el-padding);
|
||||||
|
margin-top: var(--el-margin);
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
background: var(--el-color-white);
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
transition: var(--el-transition);
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin: 0 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
11
library/components/VabFullscreen/index.vue
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
<vab-icon class="vab-fullscreen" :icon="isFullscreen ? 'fullscreen-exit-fill' : 'fullscreen-fill'" @click="toggle" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabFullscreen',
|
||||||
|
})
|
||||||
|
|
||||||
|
const { isFullscreen, toggle } = useFullscreen()
|
||||||
|
</script>
|
||||||
132
library/components/VabHeader/index.vue
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vab-header">
|
||||||
|
<div class="vab-main">
|
||||||
|
<div class="right-panel">
|
||||||
|
<vab-logo />
|
||||||
|
<el-menu
|
||||||
|
v-if="'horizontal' === layout"
|
||||||
|
active-text-color="var(--el-menu-color-text)"
|
||||||
|
background-color="var(--el-menu-background-color)"
|
||||||
|
:default-active="activeMenu.data"
|
||||||
|
menu-trigger="hover"
|
||||||
|
mode="horizontal"
|
||||||
|
text-color="var(--el-menu-color-text)"
|
||||||
|
>
|
||||||
|
<vab-menu v-for="(item, index) in handleRoutes" :key="index + item['name']" :item="item" :layout="layout" />
|
||||||
|
</el-menu>
|
||||||
|
<vab-right-tools is-horizontal />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRoutesStore } from '/@/store/modules/routes'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabHeader',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
layout: {
|
||||||
|
type: String,
|
||||||
|
default: 'horizontal',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const routesStore = useRoutesStore()
|
||||||
|
const { getActiveMenu: activeMenu, getRoutes: routes } = storeToRefs(routesStore)
|
||||||
|
|
||||||
|
const handleRoutes = computed(() =>
|
||||||
|
routes.value.flatMap((route) => (route.meta && route.meta.levelHidden && route.children ? [...route.children] : route))
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.vab-header .vab-main .right-panel {
|
||||||
|
.el-menu.el-menu--horizontal {
|
||||||
|
width: calc(100vw * 0.92 - 195px - 435px) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: flex-end;
|
||||||
|
height: var(--el-header-height);
|
||||||
|
background: var(--el-menu-background-color);
|
||||||
|
|
||||||
|
.vab-main {
|
||||||
|
padding: 0 var(--el-padding) 0 var(--el-padding);
|
||||||
|
|
||||||
|
.right-panel {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: var(--el-header-height);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.vab-logo-horizontal {
|
||||||
|
width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-sub-menu__icon-more {
|
||||||
|
margin-right: var(--el-margin) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-sub-menu__hide-arrow {
|
||||||
|
.el-sub-menu__title {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu {
|
||||||
|
&.el-menu--horizontal {
|
||||||
|
width: 60%;
|
||||||
|
height: 40px;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
* {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .el-menu-item {
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*='ri-'] {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.username,
|
||||||
|
.username + i {
|
||||||
|
color: var(--el-color-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*='ri-'] {
|
||||||
|
margin-left: var(--el-margin);
|
||||||
|
color: var(--el-color-white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.el-popper.is-pure.is-light:has(.el-menu--horizontal, .el-menu--popup-container) {
|
||||||
|
margin-top: calc(var(--el-margin) * 0.4);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
35
library/components/VabLink/index.vue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<component :is="type" v-bind="linkProps()">
|
||||||
|
<slot></slot>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { isExternal } from '/@/utils/validate'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabLink',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
to: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const type = computed(() => (isExternal(props.to) ? 'a' : 'router-link'))
|
||||||
|
|
||||||
|
const linkProps = () =>
|
||||||
|
isExternal(props.to)
|
||||||
|
? {
|
||||||
|
href: props.to,
|
||||||
|
target: '_blank',
|
||||||
|
rel: 'noopener',
|
||||||
|
}
|
||||||
|
: { to: props.to, target: props.target }
|
||||||
|
</script>
|
||||||
235
library/components/VabLock/index.vue
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vab-lock">
|
||||||
|
<vab-icon icon="lock-2-line" @click="handleLock" />
|
||||||
|
<el-drawer
|
||||||
|
v-model="lock"
|
||||||
|
append-to-body
|
||||||
|
class="vab-lock-drawer"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
direction="ttb"
|
||||||
|
:show-close="false"
|
||||||
|
size="100%"
|
||||||
|
:with-header="false"
|
||||||
|
>
|
||||||
|
<div class="vab-screen-lock">
|
||||||
|
<div id="vab-screen-lock-background" class="vab-screen-lock-background" :style="style"></div>
|
||||||
|
<div class="vab-screen-lock-content">
|
||||||
|
<div class="vab-screen-lock-content-title">
|
||||||
|
<el-avatar :size="180" :src="avatar" />
|
||||||
|
<vab-icon icon="lock-2-line" />
|
||||||
|
{{ title }} {{ translate('屏幕已锁定') }}
|
||||||
|
</div>
|
||||||
|
<div class="vab-screen-lock-content-form">
|
||||||
|
<el-form ref="formRef" :model="form" :rules="rules" @submit.prevent>
|
||||||
|
<el-form-item prop="password">
|
||||||
|
<el-input v-model="form.password" v-focus autocomplete="off" :placeholder="translate('请输入密码123456')" type="password" />
|
||||||
|
<el-button native-type="submit" type="primary" @click="handleUnLock">
|
||||||
|
<vab-icon icon="rotate-lock-2-line" />
|
||||||
|
<span>{{ translate('解锁') }}</span>
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
import { useUserStore } from '/@/store/modules/user'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabLock',
|
||||||
|
})
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const { avatar } = storeToRefs(userStore)
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { lock, title } = storeToRefs(settingsStore)
|
||||||
|
const { handleLock: _handleLock, handleUnLock: _handleUnLock } = settingsStore
|
||||||
|
const url = 'https://cdn.jsdelivr.net/gh/chuzhixin/image/vab-image-lock/'
|
||||||
|
const background = ref(`${url}${Math.round(Math.random() * 31)}.jpg`)
|
||||||
|
|
||||||
|
const style = reactive({
|
||||||
|
background: 'var(--el-color-primary-light-5)',
|
||||||
|
backgroundSize: '100%',
|
||||||
|
filter: 'blur(5px)',
|
||||||
|
transform: 'scale(1.05)',
|
||||||
|
transition: 'all 3s ease-in-out',
|
||||||
|
})
|
||||||
|
|
||||||
|
const validatePass = (rule, value, callback) => {
|
||||||
|
if (value === '' || value !== '123456') {
|
||||||
|
callback(new Error('请输入正确的密码'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
|
const form = reactive({
|
||||||
|
password: '123456',
|
||||||
|
})
|
||||||
|
const rules = {
|
||||||
|
password: [{ validator: validatePass, trigger: 'blur' }],
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUnLock = () => {
|
||||||
|
formRef.value?.validate(async (valid) => {
|
||||||
|
if (valid) await _handleUnLock()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLock = () => {
|
||||||
|
_handleLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
lock,
|
||||||
|
() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
lock.value ? (style.transform = 'scale(1.2)') : (style.transform = 'scale(1.05)')
|
||||||
|
}, 500)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.el-overlay:has(.vab-lock-drawer) {
|
||||||
|
backdrop-filter: none;
|
||||||
|
|
||||||
|
.vab-lock-drawer {
|
||||||
|
.el-drawer__body {
|
||||||
|
padding: 0 !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-lock-drawer {
|
||||||
|
.vab-screen-lock {
|
||||||
|
position: relative;
|
||||||
|
z-index: var(--el-z-index);
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100vw;
|
||||||
|
height: calc(var(--vh, 1vh) * 100);
|
||||||
|
font-weight: bold;
|
||||||
|
background: var(--el-mask-color);
|
||||||
|
opacity: var(--opacity-value);
|
||||||
|
|
||||||
|
&-background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: calc(var(--el-z-index) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
z-index: var(--el-z-index);
|
||||||
|
width: 400px;
|
||||||
|
padding: 40px 55px 40px 55px;
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
text-align: center;
|
||||||
|
background: var(--el-mask-color);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 15px;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
font-size: var(--el-font-size-extra-small);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
line-height: 50px;
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-avatar {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
padding: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*='ri-'] {
|
||||||
|
display: block;
|
||||||
|
margin: auto !important;
|
||||||
|
font-size: 30px;
|
||||||
|
color: var(--el-color-grey) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-form {
|
||||||
|
:deep() {
|
||||||
|
.el-input {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
|
||||||
|
&__wrapper {
|
||||||
|
padding-right: 0;
|
||||||
|
border: 1px solid var(--el-color-primary);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__inner {
|
||||||
|
width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__suffix {
|
||||||
|
.el-input__validateIcon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
position: absolute;
|
||||||
|
right: -1px;
|
||||||
|
z-index: 999;
|
||||||
|
height: 40px;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
line-height: 40px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
|
||||||
|
[class*='ri-'] {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.vab-screen-lock-content {
|
||||||
|
width: 100% !important;
|
||||||
|
padding: 40px 35px 40px 35px;
|
||||||
|
margin: 5vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
121
library/components/VabLogo/index.vue
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vab-logo" :class="{ ['vab-logo-' + theme.layout]: true }">
|
||||||
|
<router-link to="/">
|
||||||
|
<span class="logo">
|
||||||
|
<img src="../../../public/logo.png" alt="" style="width: 40px; height: 40px;">
|
||||||
|
</span>
|
||||||
|
<span class="title" :class="{ 'hidden-xs-only': theme.layout === 'horizontal' }"> {{ title }} </span>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabLogo',
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { theme, logo, title } = storeToRefs(settingsStore)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@mixin container {
|
||||||
|
position: relative;
|
||||||
|
height: var(--el-header-height);
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: var(--el-header-height);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin logo {
|
||||||
|
display: inline-block;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
color: var(--el-title-color);
|
||||||
|
vertical-align: middle;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin title {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: var(--el-font-size-extra-large);
|
||||||
|
line-height: 55px;
|
||||||
|
color: var(--el-title-color);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-logo {
|
||||||
|
&-horizontal {
|
||||||
|
@include container;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
svg,
|
||||||
|
img {
|
||||||
|
@include logo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-vertical,
|
||||||
|
&-column,
|
||||||
|
&-comprehensive,
|
||||||
|
&-fall {
|
||||||
|
@include container;
|
||||||
|
|
||||||
|
height: var(--el-logo-height);
|
||||||
|
line-height: var(--el-logo-height);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
svg,
|
||||||
|
img {
|
||||||
|
@include logo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include title;
|
||||||
|
max-width: calc(var(--el-left-menu-width) - 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-column {
|
||||||
|
background: var(--el-color-white) !important;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
display: block;
|
||||||
|
width: var(--el-left-menu-width-min);
|
||||||
|
height: var(--el-logo-height);
|
||||||
|
margin: 0;
|
||||||
|
background: var(--el-menu-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
position: fixed;
|
||||||
|
left: var(--el-left-menu-width-min) !important;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block !important;
|
||||||
|
width: calc(var(--el-left-menu-width) - var(--el-left-menu-width-min) - 1px);
|
||||||
|
height: var(--el-nav-height);
|
||||||
|
margin-left: 0 !important;
|
||||||
|
color: var(--el-color-grey) !important;
|
||||||
|
background: var(--el-color-white) !important;
|
||||||
|
border-bottom: 1px solid var(--el-border-color);
|
||||||
|
|
||||||
|
@include title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
87
library/components/VabMenu/components/VabMenuItem.vue
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<el-menu-item :index="itemOrMenu.path" @click="handleLink">
|
||||||
|
<vab-icon
|
||||||
|
v-if="itemOrMenu.meta && itemOrMenu.meta.icon"
|
||||||
|
:icon="itemOrMenu.meta.icon"
|
||||||
|
:is-custom-svg="itemOrMenu.meta.isCustomSvg"
|
||||||
|
:title="translate(itemOrMenu.meta.title)"
|
||||||
|
/>
|
||||||
|
<span :title="translate(itemOrMenu.meta.title)">
|
||||||
|
{{ translate(itemOrMenu.meta.title) }}
|
||||||
|
</span>
|
||||||
|
<el-tag v-if="itemOrMenu.meta && itemOrMenu.meta.badge" effect="dark" type="danger">
|
||||||
|
{{ translate(itemOrMenu.meta.badge) }}
|
||||||
|
</el-tag>
|
||||||
|
<vab-dot v-if="itemOrMenu.meta && itemOrMenu.meta.dot" type="danger" />
|
||||||
|
</el-menu-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { isHashRouterMode } from '/@/config'
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
import { isExternal } from '/@/utils/validate'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabMenuItem',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
itemOrMenu: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { device } = storeToRefs(settingsStore)
|
||||||
|
const { foldSideBar } = settingsStore
|
||||||
|
const { enter, exit } = useFullscreen()
|
||||||
|
|
||||||
|
const handleLink = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
const routePath = props.itemOrMenu.path
|
||||||
|
const target = props.itemOrMenu.meta.target
|
||||||
|
const fullscreen = props.itemOrMenu.meta.fullscreen
|
||||||
|
|
||||||
|
if (target === '_blank') {
|
||||||
|
if (isExternal(routePath)) {
|
||||||
|
window.open(routePath)
|
||||||
|
router.push('/redirect')
|
||||||
|
} else if (route.path !== routePath) isHashRouterMode ? window.open(`#${routePath}`) : window.open(routePath)
|
||||||
|
router.push('/redirect')
|
||||||
|
} else {
|
||||||
|
if (isExternal(routePath)) window.location.href = routePath
|
||||||
|
else if (route.path !== routePath) {
|
||||||
|
if (device.value === 'mobile') foldSideBar()
|
||||||
|
router.push(props.itemOrMenu.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (fullscreen) enter()
|
||||||
|
else exit()
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.el-tag) {
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
height: 18px;
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-left: 5px;
|
||||||
|
font-size: var(--el-font-size-extra-small);
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-dot {
|
||||||
|
position: absolute !important;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
36
library/components/VabMenu/components/VabSubMenu.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<template v-if="itemOrMenu.meta && itemOrMenu.meta.levelHidden">
|
||||||
|
<template v-for="route in itemOrMenu.children" :key="route.path">
|
||||||
|
<vab-menu :item="route" />
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<el-sub-menu v-else :index="itemOrMenu.path">
|
||||||
|
<template #title>
|
||||||
|
<vab-icon
|
||||||
|
v-if="itemOrMenu.meta && itemOrMenu.meta.icon"
|
||||||
|
:icon="itemOrMenu.meta.icon"
|
||||||
|
:is-custom-svg="itemOrMenu.meta.isCustomSvg"
|
||||||
|
:title="translate(itemOrMenu.meta.title)"
|
||||||
|
/>
|
||||||
|
<span :title="translate(itemOrMenu.meta.title)">
|
||||||
|
{{ translate(itemOrMenu.meta.title) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<slot></slot>
|
||||||
|
</el-sub-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabSubMenu',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
itemOrMenu: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
39
library/components/VabMenu/index.vue
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<component :is="menuComponent" :item-or-menu="item">
|
||||||
|
<template v-if="item.children && item.children.length > 0">
|
||||||
|
<vab-menu v-for="route in item.children" :key="route.path" :item="route" />
|
||||||
|
</template>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabMenu',
|
||||||
|
})
|
||||||
|
|
||||||
|
const imports = import.meta.glob('./**/*.vue', { eager: true })
|
||||||
|
const Components = {}
|
||||||
|
Object.getOwnPropertyNames(imports).forEach((key) => {
|
||||||
|
Components[key.replaceAll(/(\/|components|\.|vue)/g, '')] = imports[key].default
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const menuComponent = computed(() =>
|
||||||
|
props.item.children &&
|
||||||
|
props.item.children.some((route) => {
|
||||||
|
return route.meta && route.meta.hidden !== true
|
||||||
|
})
|
||||||
|
? Components['VabSubMenu']
|
||||||
|
: Components['VabMenuItem']
|
||||||
|
)
|
||||||
|
</script>
|
||||||
165
library/components/VabNav/index.vue
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vab-nav">
|
||||||
|
<div class="left-panel">
|
||||||
|
<vab-fold fold="contract-left-line" unfold="contract-right-line" />
|
||||||
|
<el-tabs
|
||||||
|
v-if="layout === 'comprehensive'"
|
||||||
|
v-model="tab.data"
|
||||||
|
class="comprehensive-tabs"
|
||||||
|
tab-position="top"
|
||||||
|
@tab-click="handleTabClick"
|
||||||
|
>
|
||||||
|
<template v-for="item in routes" :key="item.name">
|
||||||
|
<el-tab-pane :name="item.name">
|
||||||
|
<template #label>
|
||||||
|
<vab-icon v-if="item.meta.icon" :icon="item.meta.icon" :is-custom-svg="item.meta.isCustomSvg" />
|
||||||
|
{{ translate(item.meta.title) }}
|
||||||
|
</template>
|
||||||
|
</el-tab-pane>
|
||||||
|
</template>
|
||||||
|
</el-tabs>
|
||||||
|
<vab-breadcrumb v-else class="hidden-xs-only hidden-md-and-down" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="right-panel">
|
||||||
|
<vab-right-tools />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { openFirstMenu } from '/@/config'
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useRoutesStore } from '/@/store/modules/routes'
|
||||||
|
import { isExternal } from '/@/utils/validate'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabNav',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
layout: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const routesStore = useRoutesStore()
|
||||||
|
const { getTab: tab, getTabMenu: tabMenu, getRoutes: routes } = storeToRefs(routesStore)
|
||||||
|
|
||||||
|
const handleTabClick = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (isExternal(tabMenu.value.path)) {
|
||||||
|
window.open(tabMenu.value.path)
|
||||||
|
router.push('/redirect')
|
||||||
|
} else if (openFirstMenu) router.push(tabMenu.value.redirect || tabMenu.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.vab-layout-comprehensive {
|
||||||
|
.comprehensive-tabs {
|
||||||
|
width: calc(100vw - var(--el-left-menu-width) - 675px) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-nav {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: var(--el-nav-height);
|
||||||
|
padding-right: var(--el-padding);
|
||||||
|
padding-left: var(--el-padding);
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
background: var(--el-color-white);
|
||||||
|
border-bottom: 1px solid var(--el-border-color);
|
||||||
|
|
||||||
|
.left-panel {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
height: var(--el-nav-height);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.fold-unfold {
|
||||||
|
margin-right: var(--el-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
.el-tabs__header {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
> .el-tabs__nav-wrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.el-icon-arrow-left,
|
||||||
|
.el-icon-arrow-right {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__nav-wrap::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-panel {
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
height: var(--el-nav-height);
|
||||||
|
transition: var(--el-transition);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
[class*='ri-'] {
|
||||||
|
margin-left: var(--el-margin);
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
[class*='ri-'] {
|
||||||
|
margin-left: 0;
|
||||||
|
color: var(--el-color-white);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.right-panel {
|
||||||
|
:deep() {
|
||||||
|
.el-badge,
|
||||||
|
.ri-refresh-line {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
133
library/components/VabNotice/index.vue
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
<template>
|
||||||
|
<el-badge type="danger" :value="badge">
|
||||||
|
<el-popover placement="bottom" trigger="hover" :width="305">
|
||||||
|
<template #reference>
|
||||||
|
<vab-icon icon="notification-2-line" />
|
||||||
|
</template>
|
||||||
|
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||||
|
<el-tab-pane :label="translate('通知')" name="notice">
|
||||||
|
<div class="notice-list">
|
||||||
|
<el-scrollbar>
|
||||||
|
<ul v-if="badge">
|
||||||
|
<li v-for="(item, index) in notices" :key="index">
|
||||||
|
<el-avatar :size="45" :src="item.image" />
|
||||||
|
<span v-html="item.notice"></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<el-empty v-else description="暂无数据" />
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane :label="translate('邮件')" name="email">
|
||||||
|
<div class="notice-list">
|
||||||
|
<el-scrollbar>
|
||||||
|
<ul v-if="badge">
|
||||||
|
<li v-for="(item, index) in notices" :key="index">
|
||||||
|
<el-avatar :size="45" :src="item.image" />
|
||||||
|
<span>{{ item.email }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<el-empty v-else description="暂无数据" />
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
<div class="notice-clear" @click="handleClearNotice">
|
||||||
|
<el-button text>
|
||||||
|
<vab-icon icon="close-circle-line" />
|
||||||
|
<span>{{ translate('清空消息') }}</span>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
</el-badge>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabNotice',
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { theme } = storeToRefs(settingsStore)
|
||||||
|
const activeName = ref('notice')
|
||||||
|
const notices = ref([])
|
||||||
|
const badge = ref(undefined)
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
// const { data } = await getList()
|
||||||
|
// data.list
|
||||||
|
notices.value = []
|
||||||
|
// badge.value = data.total === 0 ? undefined : data.total
|
||||||
|
badge.value = 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClearNotice = () => {
|
||||||
|
badge.value = ''
|
||||||
|
notices.value = []
|
||||||
|
$baseMessage('清空消息成功', 'success', 'hey')
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
if (theme.value.showNotice) fetchData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep() {
|
||||||
|
.el-tabs__active-bar {
|
||||||
|
min-width: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-list {
|
||||||
|
height: 315px;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0 15px 0 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 0 15px 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-avatar {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-clear {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px 0 0 0;
|
||||||
|
font-size: var(--el-font-size-base);
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border-top: 1px solid var(--el-border-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
44
library/components/VabPagination/index.vue
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<el-pagination
|
||||||
|
:background="background"
|
||||||
|
:current-page="currentPage"
|
||||||
|
:default-current-page="defaultCurrentPage"
|
||||||
|
:default-page-size="defaultPageSize"
|
||||||
|
:disabled="disabled"
|
||||||
|
:hide-on-single-page="hideOnSinglePage"
|
||||||
|
:layout="layout"
|
||||||
|
:next-icon="nextIcon"
|
||||||
|
:next-text="nextText"
|
||||||
|
:page-count="pageCount"
|
||||||
|
:page-size="PageSize"
|
||||||
|
:page-sizes="pageSizes"
|
||||||
|
:pager-count="pagerCount"
|
||||||
|
:popper-class="popperClass"
|
||||||
|
:prev-icon="prevIcon"
|
||||||
|
:prev-text="prevText"
|
||||||
|
:small="small"
|
||||||
|
:teleported="teleported"
|
||||||
|
:total="total"
|
||||||
|
v-bind="$attrs"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElPagination } from 'element-plus';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabPagination',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
...ElPagination.props,
|
||||||
|
layout: {
|
||||||
|
type: String,
|
||||||
|
default: 'total, sizes, prev, pager, next, jumper',
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<el-col :span="24">
|
||||||
|
<div class="bottom-panel">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<el-col :lg="span" :md="24" :sm="24" :xl="span" :xs="24">
|
||||||
|
<div class="left-panel">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineProps({
|
||||||
|
span: {
|
||||||
|
type: Number,
|
||||||
|
default: 14,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<el-col :lg="span" :md="24" :sm="24" :xl="span" :xs="24">
|
||||||
|
<div class="right-panel">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineProps({
|
||||||
|
span: {
|
||||||
|
type: Number,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<el-col :span="24">
|
||||||
|
<div class="top-panel">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
70
library/components/VabQueryForm/index.vue
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<el-row class="vab-query-form" :gutter="0">
|
||||||
|
<slot></slot>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabQueryForm',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@mixin panel {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
min-height: var(-el-input-height);
|
||||||
|
margin: 0 0 calc(var(--el-margin) / 2) 0;
|
||||||
|
.el-form-item__content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .el-button {
|
||||||
|
margin: 0 10px calc(var(--el-margin) / 2) 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-query-form {
|
||||||
|
:deep() {
|
||||||
|
.el-input,
|
||||||
|
.el-select {
|
||||||
|
width: 175px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item:first-child {
|
||||||
|
margin: 0 0 calc(var(--el-margin) / 2) 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item + .el-form-item {
|
||||||
|
margin: 0 0 calc(var(--el-margin) / 2) 0 !important;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
margin: 0 0 0 10px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-panel {
|
||||||
|
@include panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-panel {
|
||||||
|
@include panel;
|
||||||
|
border-top: 1px solid #dcdfe6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel {
|
||||||
|
@include panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-panel {
|
||||||
|
@include panel;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
29
library/components/VabRefresh/index.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<template>
|
||||||
|
<vab-icon :class="className" icon="refresh-line" @click="refreshRoute" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabRefresh',
|
||||||
|
})
|
||||||
|
|
||||||
|
const className = ref('')
|
||||||
|
|
||||||
|
const rotate = () => {
|
||||||
|
className.value = 'rotate'
|
||||||
|
setTimeout(() => {
|
||||||
|
className.value = ''
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshRoute = () => {
|
||||||
|
$pub('reload-router-view')
|
||||||
|
rotate()
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
$sub('refresh-rotate', () => {
|
||||||
|
rotate()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
89
library/components/VabRightTools/index.vue
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vab-right-tools">
|
||||||
|
<vab-search v-show="!isHorizontal" class="hidden-xs-only" />
|
||||||
|
<div class="vab-right-tools-draggable">
|
||||||
|
<vab-dark v-show="theme.showDark" :style="!isHorizontal ? '' : { marginLeft: 'var(--el-margin)' }" />
|
||||||
|
<vab-color-picker v-show="theme.showColorPicker" />
|
||||||
|
<vab-error-log class="hidden-xs-only" />
|
||||||
|
<vab-font-size v-show="theme.showFontSize" />
|
||||||
|
<vab-lock v-show="theme.showLock" />
|
||||||
|
<vab-notice v-show="theme.showNotice" />
|
||||||
|
<vab-fullscreen v-show="theme.showFullScreen" />
|
||||||
|
<vab-refresh v-show="theme.showRefresh" />
|
||||||
|
</div>
|
||||||
|
<vab-avatar />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import Sortable from 'sortablejs'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabRightTools',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
isHorizontal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { theme, device } = storeToRefs(settingsStore)
|
||||||
|
const routeName = ref(route.name)
|
||||||
|
|
||||||
|
let sortable
|
||||||
|
const handleTabDrag = () => {
|
||||||
|
if (theme.value.rightToolsDrag && device.value != 'mobile') {
|
||||||
|
const toolsElement = document.querySelector('.vab-right-tools-draggable')
|
||||||
|
|
||||||
|
if (toolsElement)
|
||||||
|
sortable = new Sortable(toolsElement, {
|
||||||
|
animation: 150,
|
||||||
|
easing: 'cubic-bezier(1, 0, 0, 1)',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
route,
|
||||||
|
() => {
|
||||||
|
routeName.value = route.name
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
handleTabDrag()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
theme.value,
|
||||||
|
() => {
|
||||||
|
if (theme.value.rightToolsDrag) handleTabDrag()
|
||||||
|
else sortable && sortable.destroy()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-right-tools {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
&-draggable {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
72
library/components/VabRouterView/index.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<router-view v-slot="{ Component }">
|
||||||
|
<transition mode="out-in" :name="theme.pageTransition">
|
||||||
|
<keep-alive :include="keepAliveNameList" :max="keepAliveMaxNum">
|
||||||
|
<component :is="Component" :key="routerKey" ref="componentRef" />
|
||||||
|
</keep-alive>
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useHead } from '@vueuse/head'
|
||||||
|
import VabProgress from 'nprogress'
|
||||||
|
import { keepAliveMaxNum } from '/@/config'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
import { useTabsStore } from '/@/store/modules/tabs'
|
||||||
|
import { handleActivePath } from '/@/utils/routes'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabRouterView',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { theme } = storeToRefs(settingsStore)
|
||||||
|
const tabsStore = useTabsStore()
|
||||||
|
const { getVisitedRoutes: visitedRoutes } = storeToRefs(tabsStore)
|
||||||
|
const componentRef = ref()
|
||||||
|
const routerKey = ref()
|
||||||
|
const keepAliveNameList = ref()
|
||||||
|
const siteData = reactive({
|
||||||
|
description: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
meta: [
|
||||||
|
{
|
||||||
|
name: `description`,
|
||||||
|
content: computed(() => siteData.description),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateKeepAliveNameList = (refreshRouteName = null) => {
|
||||||
|
keepAliveNameList.value = visitedRoutes.value
|
||||||
|
.filter((item) => !item.meta.noKeepAlive && item.name !== refreshRouteName)
|
||||||
|
.flatMap((item) => item.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新KeepAlive缓存页面
|
||||||
|
watchEffect(() => {
|
||||||
|
routerKey.value = handleActivePath(route, true)
|
||||||
|
updateKeepAliveNameList()
|
||||||
|
siteData.description = `迪联科技`
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
$sub('reload-router-view', (refreshRouteName = route.name) => {
|
||||||
|
if (theme.value.showProgressBar) VabProgress.start()
|
||||||
|
const cacheActivePath = routerKey.value
|
||||||
|
routerKey.value = null
|
||||||
|
updateKeepAliveNameList(refreshRouteName)
|
||||||
|
nextTick(() => {
|
||||||
|
routerKey.value = cacheActivePath
|
||||||
|
updateKeepAliveNameList()
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
if (theme.value.showProgressBar) VabProgress.done()
|
||||||
|
}, 200)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
93
library/components/VabSearch/index.vue
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<el-tree-select
|
||||||
|
v-if="theme.showSearch"
|
||||||
|
v-model="searchValue"
|
||||||
|
class="vab-search"
|
||||||
|
clearable
|
||||||
|
:data="addFieldToTree(routes)"
|
||||||
|
default-expand-all
|
||||||
|
filterable
|
||||||
|
highlight-current
|
||||||
|
:prefix-icon="Search"
|
||||||
|
@node-click="handleSelect"
|
||||||
|
>
|
||||||
|
<template #default="{ data }">
|
||||||
|
<vab-icon v-if="data.meta && data.meta.icon" :icon="data.meta.icon" />
|
||||||
|
<span style="margin-left: 3px">{{ translate(data.meta.title) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-tree-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { Search } from '@element-plus/icons-vue'
|
||||||
|
import { isHashRouterMode } from '/@/config'
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useRoutesStore } from '/@/store/modules/routes'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
import { isExternal } from '/@/utils/validate'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabSearch',
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { theme } = storeToRefs(settingsStore)
|
||||||
|
const searchValue = ref('')
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
const routesStore = useRoutesStore()
|
||||||
|
const { getRoutes: routes } = storeToRefs(routesStore)
|
||||||
|
|
||||||
|
const addFieldToTree = (routes) => {
|
||||||
|
routes.forEach((node) => {
|
||||||
|
node.value = node.name
|
||||||
|
node.label = translate(node.meta.title)
|
||||||
|
if (node.children && node.children.length > 0) addFieldToTree(node.children)
|
||||||
|
})
|
||||||
|
return routes
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSelect = (item) => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (!item.children)
|
||||||
|
if (isExternal(item.path)) {
|
||||||
|
window.open(item.path)
|
||||||
|
router.push('/redirect')
|
||||||
|
return
|
||||||
|
} else if (item.meta.target === '_blank') {
|
||||||
|
isHashRouterMode ? window.open(`#${item.path}`) : window.open(item.path)
|
||||||
|
router.push('/redirect')
|
||||||
|
return
|
||||||
|
} else router.push(item.path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
route,
|
||||||
|
() => {
|
||||||
|
if (route.fullPath.includes('?')) {
|
||||||
|
//处理query传参
|
||||||
|
const matched = route.fullPath.match(/\?(.*)$/)
|
||||||
|
const name = route.name
|
||||||
|
if (matched) name.includes('?') ? (searchValue.value = route.name) : (searchValue.value = `${route.name}?${matched[1]}`)
|
||||||
|
// 详情页显示搜索项
|
||||||
|
if (route.meta.hidden && name.includes('Detail')) searchValue.value = ''
|
||||||
|
} else searchValue.value = route.name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-search {
|
||||||
|
margin-left: var(--el-margin);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-input {
|
||||||
|
width: 150px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
165
library/components/VabSideBar/index.vue
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
<template>
|
||||||
|
<el-scrollbar class="vab-side-bar" :class="{ 'is-collapse': collapse }">
|
||||||
|
<vab-logo v-if="layout === 'comprehensive' || layout === 'vertical'" class="fixed-logo" />
|
||||||
|
<el-menu
|
||||||
|
background-color="var(--el-menu-background-color)"
|
||||||
|
:collapse="collapse"
|
||||||
|
:collapse-transition="false"
|
||||||
|
:default-active="activeMenu.data"
|
||||||
|
:default-openeds="defaultOpeneds"
|
||||||
|
menu-trigger="click"
|
||||||
|
mode="vertical"
|
||||||
|
text-color="var(--el-menu-color-text)"
|
||||||
|
:unique-opened="uniqueOpened"
|
||||||
|
>
|
||||||
|
<vab-menu v-for="(item, index) in handleRoutes" :key="index + item.name" :item="item" />
|
||||||
|
</el-menu>
|
||||||
|
</el-scrollbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defaultOpeneds, uniqueOpened } from '/@/config'
|
||||||
|
import { useRoutesStore } from '/@/store/modules/routes'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabSideBar',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
layout: {
|
||||||
|
type: String,
|
||||||
|
default: 'vertical',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { collapse } = storeToRefs(settingsStore)
|
||||||
|
const routesStore = useRoutesStore()
|
||||||
|
const { getRoutes: routes, getActiveMenu: activeMenu, getPartialRoutes: partialRoutes } = storeToRefs(routesStore)
|
||||||
|
|
||||||
|
const handleRoutes = computed(() =>
|
||||||
|
props.layout === 'comprehensive'
|
||||||
|
? partialRoutes.value
|
||||||
|
: routes.value.flatMap((route) => (route.meta.levelHidden && route.children ? [...route.children] : route))
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@mixin active {
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-white);
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-color-white);
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-side-bar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: var(--el-left-menu-width);
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--el-menu-background-color);
|
||||||
|
transition: var(--el-transition);
|
||||||
|
|
||||||
|
.fixed-logo {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: var(--el-z-index);
|
||||||
|
width: 100%;
|
||||||
|
height: var(--el-header-height);
|
||||||
|
background: var(--el-menu-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-collapse {
|
||||||
|
z-index: calc(var(--el-z-index) + 1);
|
||||||
|
width: var(--el-left-menu-width-min);
|
||||||
|
border-right: 0;
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-menu {
|
||||||
|
border-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu--collapse.el-menu {
|
||||||
|
> .el-menu-item,
|
||||||
|
> .el-sub-menu .el-sub-menu__title {
|
||||||
|
justify-content: center;
|
||||||
|
height: calc(var(--el-menu-item-height) - 6px);
|
||||||
|
padding: 0;
|
||||||
|
line-height: calc(var(--el-menu-item-height) - 6px);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
[class*='ri'] {
|
||||||
|
display: block;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-item,
|
||||||
|
.el-sub-menu {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu--collapse {
|
||||||
|
border-right: 0;
|
||||||
|
|
||||||
|
.el-sub-menu__icon-arrow {
|
||||||
|
right: 10px;
|
||||||
|
margin-top: -3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-menu.el-menu--vertical {
|
||||||
|
margin-top: var(--el-header-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-scrollbar__wrap {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-item,
|
||||||
|
.el-sub-menu__title {
|
||||||
|
height: var(--el-menu-item-height);
|
||||||
|
margin: 0 10px 5px 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: var(--el-menu-item-height);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
@include active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.el-menu {
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu--popup-right-start {
|
||||||
|
--el-menu-hover-bg-color: var(--el-color-primary) !important;
|
||||||
|
--el-menu-active-color: var(--el-color-white) !important;
|
||||||
|
|
||||||
|
.is-active {
|
||||||
|
background: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
36
library/components/VabStatistics/index.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
//
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabStatistics',
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
if (location.hostname !== 'localhost' && !location.hostname.includes('127') && !location.hostname.includes('192')) {
|
||||||
|
;(function () {
|
||||||
|
const hm = document.createElement('script')
|
||||||
|
let k = '820b686671af452e8a4e18952ce946d8'
|
||||||
|
if (location.hostname.includes('vuejs-core')) k = '9578a46b371ba85ee55bc868d6b30692'
|
||||||
|
hm.src = `//hm.baidu.com/hm.js?${k}`
|
||||||
|
const s = document.querySelectorAll('script')[0]
|
||||||
|
s.parentNode.insertBefore(hm, s)
|
||||||
|
})()
|
||||||
|
;(function (c, l, a, r, i, t, y) {
|
||||||
|
c[a] =
|
||||||
|
c[a] ||
|
||||||
|
function () {
|
||||||
|
// eslint-disable-next-line prefer-rest-params
|
||||||
|
;(c[a].q = c[a].q || []).push(arguments)
|
||||||
|
}
|
||||||
|
t = l.createElement(r)
|
||||||
|
t.async = 1
|
||||||
|
t.src = `//www.clarity.ms/tag/${i}`
|
||||||
|
y = l.getElementsByTagName(r)[0]
|
||||||
|
y.parentNode.insertBefore(t, y)
|
||||||
|
})(window, document, 'clarity', 'script', 'j9de7dmm7n')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
59
library/components/VabTabs/components/VabTabsSetting.vue
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<vab-dialog v-model="drawerVisible" append-to-body :title="translate('标签设置')" width="400px">
|
||||||
|
<el-form ref="form" label-position="top" :model="theme">
|
||||||
|
<el-form-item v-if="theme.showTabs" :label="translate('标签风格')">
|
||||||
|
<el-radio-group v-model="theme.tabsBarStyle">
|
||||||
|
<el-radio-button v-for="item in tabsBarStyleList" :key="item.value" :label="translate(item.label)" :value="item.value" />
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="translate('标签图标')">
|
||||||
|
<el-switch v-model="theme.showTabsIcon" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="translate('持久化标签')">
|
||||||
|
<el-switch v-model="persistenceTab" @change="handlePersistenceTab" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" @click="handleSaveTheme">
|
||||||
|
{{ translate('保存') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</vab-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabTabsSetting',
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { theme, persistenceTab } = storeToRefs(settingsStore)
|
||||||
|
const { saveTheme, updateCaughtTabs } = settingsStore
|
||||||
|
const drawerVisible = ref<boolean>(false)
|
||||||
|
const tabsBarStyleList = ref<any>([
|
||||||
|
{ label: '卡片', value: 'card' },
|
||||||
|
{ label: '灵动', value: 'smart' },
|
||||||
|
{ label: '圆滑', value: 'smooth' },
|
||||||
|
{ label: '矩形', value: 'rect' },
|
||||||
|
])
|
||||||
|
|
||||||
|
const handleOpenSetting = () => {
|
||||||
|
drawerVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
handleOpenSetting,
|
||||||
|
})
|
||||||
|
|
||||||
|
const handlePersistenceTab = (value: any) => {
|
||||||
|
updateCaughtTabs(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSaveTheme = () => {
|
||||||
|
saveTheme()
|
||||||
|
drawerVisible.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
695
library/components/VabTabs/index.vue
Normal file
@@ -0,0 +1,695 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vab-tabs">
|
||||||
|
<div class="vab-tabs-draggable">
|
||||||
|
<el-tabs
|
||||||
|
v-model="tabActive"
|
||||||
|
class="vab-tabs-content"
|
||||||
|
:class="{ ['vab-tabs-content-' + theme.tabsBarStyle]: true }"
|
||||||
|
@tab-click="handleTabClick"
|
||||||
|
@tab-remove="handleTabRemove"
|
||||||
|
>
|
||||||
|
<el-tab-pane v-for="item in visitedRoutes" :key="item" :closable="!isNoClosable(item)" :name="item.path">
|
||||||
|
<template #label>
|
||||||
|
<span class="vab-tabs-title" @contextmenu.prevent="openMenu(item)">
|
||||||
|
<template v-if="theme.showTabsIcon">
|
||||||
|
<vab-icon v-if="item.meta && item.meta.icon" :icon="item.meta.icon" :is-custom-svg="item.meta.isCustomSvg" />
|
||||||
|
<vab-icon v-else :icon="item.parentIcon" />
|
||||||
|
</template>
|
||||||
|
<span v-if="!isNoClosable(item)" @dblclick="handleTabRemove(item.path)">
|
||||||
|
{{ translate(item.meta.title) }}
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ translate(item.meta.title) }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dropdown
|
||||||
|
placement="bottom-end"
|
||||||
|
popper-class="vab-tabs-more-dropdown"
|
||||||
|
@command="handleCommand"
|
||||||
|
@visible-change="handleVisibleChange"
|
||||||
|
>
|
||||||
|
<span class="vab-tabs-more" :class="{ 'vab-tabs-more-active': active }">
|
||||||
|
<span class="vab-tabs-more-icon">
|
||||||
|
<i class="box box-t"></i>
|
||||||
|
<i class="box box-b"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu class="tabs-more">
|
||||||
|
<el-dropdown-item command="refresh">
|
||||||
|
<vab-icon icon="refresh-line" />
|
||||||
|
<span>
|
||||||
|
{{ translate('刷新') }}
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="closeOthersTabs">
|
||||||
|
<vab-icon icon="close-line" />
|
||||||
|
<span>
|
||||||
|
{{ translate('关闭其他') }}
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="closeLeftTabs">
|
||||||
|
<vab-icon icon="arrow-left-line" />
|
||||||
|
<span>
|
||||||
|
{{ translate('关闭左侧') }}
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="closeRightTabs">
|
||||||
|
<vab-icon icon="arrow-right-line" />
|
||||||
|
<span>
|
||||||
|
{{ translate('关闭右侧') }}
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="closeAllTabs">
|
||||||
|
<vab-icon icon="close-line" />
|
||||||
|
<span>
|
||||||
|
{{ translate('关闭全部') }}
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="setting">
|
||||||
|
<vab-icon icon="settings-5-line" />
|
||||||
|
<span>
|
||||||
|
{{ translate('标签设置') }}
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<ul v-if="visible" class="contextmenu el-dropdown-menu" :style="{ left: left + 'px', top: top + 'px' }">
|
||||||
|
<li class="el-dropdown-menu__item" @click="refresh">
|
||||||
|
<vab-icon icon="refresh-line" />
|
||||||
|
<span>{{ translate('刷新') }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="el-dropdown-menu__item" :class="{ 'is-disabled': visitedRoutes.length === 1 }" @click="closeOthersTabs">
|
||||||
|
<vab-icon icon="close-line" />
|
||||||
|
<span>{{ translate('关闭其他') }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="el-dropdown-menu__item" :class="{ 'is-disabled': !visitedRoutes.indexOf(hoverRoute) }" @click="closeLeftTabs">
|
||||||
|
<vab-icon icon="arrow-left-line" />
|
||||||
|
<span>{{ translate('关闭左侧') }}</span>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="el-dropdown-menu__item"
|
||||||
|
:class="{
|
||||||
|
'is-disabled': visitedRoutes.indexOf(hoverRoute) === visitedRoutes.length - 1,
|
||||||
|
}"
|
||||||
|
@click="closeRightTabs"
|
||||||
|
>
|
||||||
|
<vab-icon icon="arrow-right-line" />
|
||||||
|
<span>{{ translate('关闭右侧') }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="el-dropdown-menu__item" @click="closeAllTabs">
|
||||||
|
<vab-icon icon="close-line" />
|
||||||
|
<span>{{ translate('关闭全部') }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<vab-tabs-setting ref="tabsSettingRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import Sortable from 'sortablejs'
|
||||||
|
import { translate } from '/@/i18n'
|
||||||
|
import { useRoutesStore } from '/@/store/modules/routes'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
import { useTabsStore } from '/@/store/modules/tabs'
|
||||||
|
import { moveElement } from '/@/utils/index'
|
||||||
|
import { handleActivePath, handleTabs } from '/@/utils/routes'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabTabs',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
layout: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { theme, device } = storeToRefs(settingsStore)
|
||||||
|
const routesStore = useRoutesStore()
|
||||||
|
const { getRoutes: routes } = storeToRefs(routesStore)
|
||||||
|
const tabsStore = useTabsStore()
|
||||||
|
const { getVisitedRoutes: visitedRoutes } = storeToRefs(tabsStore)
|
||||||
|
const _visitedRoutes = ref([...visitedRoutes.value])
|
||||||
|
const {
|
||||||
|
addVisitedRoute,
|
||||||
|
delVisitedRoute,
|
||||||
|
delOthersVisitedRoutes,
|
||||||
|
delLeftVisitedRoutes,
|
||||||
|
delRightVisitedRoutes,
|
||||||
|
delAllVisitedRoutes,
|
||||||
|
handleCaughtRoutes,
|
||||||
|
updateVisitedRoutes,
|
||||||
|
} = tabsStore
|
||||||
|
const tabActive = ref('')
|
||||||
|
const active = ref(false)
|
||||||
|
const hoverRoute = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const top = ref(0)
|
||||||
|
const left = ref(0)
|
||||||
|
const tabsSettingRef = ref(null)
|
||||||
|
|
||||||
|
const isActive = (path) => path === handleActivePath(route, true)
|
||||||
|
const isNoClosable = (tag) => tag.meta && tag.meta.noClosable
|
||||||
|
|
||||||
|
const handleTabClick = (tab) => {
|
||||||
|
if (!isActive(tab.name)) router.push(visitedRoutes.value[tab.index])
|
||||||
|
}
|
||||||
|
const handleVisibleChange = (value) => {
|
||||||
|
active.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
const initNoCLosableTabs = (routes) => {
|
||||||
|
routes.forEach((_route) => {
|
||||||
|
if (_route.meta && _route.meta.noClosable) addTabs(_route)
|
||||||
|
if (_route.children) initNoCLosableTabs(_route.children)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCommand = (command) => {
|
||||||
|
switch (command) {
|
||||||
|
case 'refresh': {
|
||||||
|
refresh()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'closeOthersTabs': {
|
||||||
|
closeOthersTabs()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'closeLeftTabs': {
|
||||||
|
closeLeftTabs()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'closeRightTabs': {
|
||||||
|
closeRightTabs()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'closeAllTabs': {
|
||||||
|
closeAllTabs()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'setting': {
|
||||||
|
tabsSettingRef.value.handleOpenSetting()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新当前标签页
|
||||||
|
*/
|
||||||
|
const refresh = async () => {
|
||||||
|
if (hoverRoute.value) {
|
||||||
|
await router.push(hoverRoute.value)
|
||||||
|
await $pub('reload-router-view', hoverRoute.value.name)
|
||||||
|
} else await $pub('reload-router-view')
|
||||||
|
await $pub('refresh-rotate')
|
||||||
|
await closeMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加标签页
|
||||||
|
* @param tag route
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const addTabs = async (tag) => {
|
||||||
|
const tab = handleTabs(tag)
|
||||||
|
if (tab) {
|
||||||
|
await addVisitedRoute(tab)
|
||||||
|
tabActive.value = tab.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据原生路径删除标签中的标签
|
||||||
|
* @param rawPath 原生路径
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const handleTabRemove = async (rawPath) => {
|
||||||
|
if (isActive(rawPath)) await toLastTab()
|
||||||
|
await delVisitedRoute(rawPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除其他标签页
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const closeOthersTabs = async () => {
|
||||||
|
if (hoverRoute.value) {
|
||||||
|
await router.push(hoverRoute.value)
|
||||||
|
await delOthersVisitedRoutes(hoverRoute.value.path)
|
||||||
|
} else await delOthersVisitedRoutes(handleActivePath(route, true))
|
||||||
|
await closeMenu()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 删除左侧标签页
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const closeLeftTabs = async () => {
|
||||||
|
if (hoverRoute.value) {
|
||||||
|
await router.push(hoverRoute.value)
|
||||||
|
await delLeftVisitedRoutes(hoverRoute.value.path)
|
||||||
|
} else await delLeftVisitedRoutes(handleActivePath(route, true))
|
||||||
|
await closeMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除右侧标签页
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const closeRightTabs = async () => {
|
||||||
|
if (hoverRoute.value) {
|
||||||
|
await router.push(hoverRoute.value)
|
||||||
|
await delRightVisitedRoutes(hoverRoute.value.path)
|
||||||
|
} else await delRightVisitedRoutes(handleActivePath(route, true))
|
||||||
|
await closeMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除所有标签页
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const closeAllTabs = async () => {
|
||||||
|
await delAllVisitedRoutes()
|
||||||
|
await toLastTab()
|
||||||
|
await closeMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳转最后一个标签页
|
||||||
|
*/
|
||||||
|
const toLastTab = async () => {
|
||||||
|
const latestView = visitedRoutes.value.findLast((item) => item.path !== handleActivePath(route, true))
|
||||||
|
if (latestView) await router.push(latestView)
|
||||||
|
else await router.push('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { x, y } = useMouse()
|
||||||
|
|
||||||
|
const openMenu = (item) => {
|
||||||
|
left.value = x.value
|
||||||
|
top.value = y.value
|
||||||
|
hoverRoute.value = item
|
||||||
|
hoverRoute.value.path = item.path
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeMenu = () => {
|
||||||
|
visible.value = false
|
||||||
|
hoverRoute.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
let sortable
|
||||||
|
const handleTabDrag = () => {
|
||||||
|
if (theme.value.tabDrag && device.value != 'mobile') {
|
||||||
|
const navElement = document.querySelector('.el-tabs__nav.is-top')
|
||||||
|
if (navElement)
|
||||||
|
sortable = new Sortable(navElement, {
|
||||||
|
animation: 150,
|
||||||
|
easing: 'cubic-bezier(1, 0, 0, 1)',
|
||||||
|
draggable: '.el-tabs__item.is-top.is-closable',
|
||||||
|
filter: '.el-tabs__active-bar.is-top',
|
||||||
|
onEnd(e) {
|
||||||
|
const routes = moveElement([...visitedRoutes.value], parseInt(e.oldIndex) - 1, parseInt(e.newIndex) - 1)
|
||||||
|
updateVisitedRoutes(routes)
|
||||||
|
_visitedRoutes.value = routes
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (visible.value) document.body.addEventListener('click', closeMenu)
|
||||||
|
else document.body.removeEventListener('click', closeMenu)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.fullPath,
|
||||||
|
() => {
|
||||||
|
initNoCLosableTabs(routes.value)
|
||||||
|
addTabs(route)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
theme.value,
|
||||||
|
() => {
|
||||||
|
if (theme.value.tabDrag) handleTabDrag()
|
||||||
|
else sortable && sortable.destroy()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
window.addEventListener('beforeunload', handleCaughtRoutes)
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
handleTabDrag()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.vab-tabs-more-dropdown {
|
||||||
|
width: 135px;
|
||||||
|
padding: calc(var(--el-padding) / 2) !important;
|
||||||
|
|
||||||
|
.el-dropdown-menu {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-popper-placement='bottom-end'] {
|
||||||
|
.el-popper__arrow {
|
||||||
|
left: 120px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-tabs {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
min-height: var(--el-tabs-height);
|
||||||
|
padding-right: var(--el-padding);
|
||||||
|
padding-left: var(--el-padding);
|
||||||
|
user-select: none;
|
||||||
|
background: var(--el-color-white);
|
||||||
|
|
||||||
|
.vab-tabs-draggable {
|
||||||
|
width: calc(100% - var(--el-margin));
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.fold-unfold {
|
||||||
|
margin-right: var(--el-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs {
|
||||||
|
&__nav-wrap::after {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-right: var(--el-padding) !important;
|
||||||
|
padding-left: var(--el-padding) !important;
|
||||||
|
|
||||||
|
.is-icon-close {
|
||||||
|
width: 14px !important;
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__active-bar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__nav-next,
|
||||||
|
&__nav-prev {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: var(--el-tab-item-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&-card {
|
||||||
|
height: var(--el-tab-item-height);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-tabs__header {
|
||||||
|
margin: 0 0 1px 0;
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
height: var(--el-tab-item-height);
|
||||||
|
margin-right: 5px;
|
||||||
|
border: 1px solid var(--el-border-color) !important;
|
||||||
|
border-radius: var(--el-border-radius-base) !important;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-smart {
|
||||||
|
height: var(--el-tab-item-height);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-tabs__header {
|
||||||
|
margin: 0 0 1px 0;
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
height: var(--el-tab-item-height);
|
||||||
|
margin-right: 5px;
|
||||||
|
border: 0;
|
||||||
|
border-top-left-radius: var(--el-border-radius-base);
|
||||||
|
border-top-right-radius: var(--el-border-radius-base);
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
width: 100%;
|
||||||
|
transition: var(--el-transition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 2px;
|
||||||
|
content: '';
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
transition: var(--el-transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
width: 100%;
|
||||||
|
transition: var(--el-transition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-smooth {
|
||||||
|
height: var(--el-tab-item-height);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-tabs__nav {
|
||||||
|
margin-top: 3.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__header {
|
||||||
|
margin: 0 0 -7px 0;
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
// min-width: 120px;
|
||||||
|
height: calc(var(--el-tab-item-height) + 4px);
|
||||||
|
margin-right: -18px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
z-index: 999;
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
background: var(--el-border-color);
|
||||||
|
mask: url('/@/assets/tabs_images/vab-tab.png');
|
||||||
|
mask-layer: url('/@/assets/tabs_images/vab-tab.png');
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-tabs-title {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 calc(var(--el-margin) / 2) 0 calc(var(--el-margin) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
|
mask: url('/@/assets/tabs_images/vab-tab.png');
|
||||||
|
mask-layer: url('/@/assets/tabs_images/vab-tab.png');
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
|
mask: url('/@/assets/tabs_images/vab-tab.png');
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-rect {
|
||||||
|
height: var(--el-tags-height);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.el-tabs__header {
|
||||||
|
margin: -1px 0 0 0;
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
height: var(--el-tabs-height);
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__nav-prev,
|
||||||
|
.el-tabs__nav-next {
|
||||||
|
height: var(--el-tabs-height);
|
||||||
|
line-height: var(--el-tabs-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.contextmenu {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 10;
|
||||||
|
padding: calc(var(--el-padding) / 2);
|
||||||
|
|
||||||
|
box-shadow: var(--el-box-shadow);
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown-menu__item:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-more {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&-active,
|
||||||
|
&:hover {
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 0;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-tabs-more-icon {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
|
||||||
|
.box-t {
|
||||||
|
&:before {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.box:before,
|
||||||
|
.box:after {
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.3s ease-out;
|
||||||
|
|
||||||
|
.box {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 14px;
|
||||||
|
height: 8px;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 0;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
content: '';
|
||||||
|
background: var(--el-color-grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 8px;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
content: '';
|
||||||
|
background: var(--el-color-grey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-t {
|
||||||
|
&:before {
|
||||||
|
transition: transform 0.3s ease-out 0.3s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
17
library/index.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { createHead } from '@vueuse/head'
|
||||||
|
// import ElementPlus from 'element-plus'
|
||||||
|
import 'virtual:svg-icons-register'
|
||||||
|
import { VabIcon } from 'vsv-icon'
|
||||||
|
import type { App } from 'vue'
|
||||||
|
import './styles/vab.scss'
|
||||||
|
|
||||||
|
export const setupVab = (app: App<Element>) => {
|
||||||
|
// app.use(ElementPlus)
|
||||||
|
app.use(createHead())
|
||||||
|
app.component('VabIcon', VabIcon)
|
||||||
|
const Plugins = import.meta.glob('./plugins/*.ts', { eager: true })
|
||||||
|
Object.getOwnPropertyNames(Plugins).forEach((key) => {
|
||||||
|
const plugin: any = Plugins[key]
|
||||||
|
app.use(plugin.default)
|
||||||
|
})
|
||||||
|
}
|
||||||
76
library/layouts/VabLayoutColumn/index.vue
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="vab-layout-column"
|
||||||
|
:class="{
|
||||||
|
fixed: fixedHeader,
|
||||||
|
'no-tabs-bar': !showTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<vab-column-bar />
|
||||||
|
<div
|
||||||
|
class="vab-main"
|
||||||
|
:class="{
|
||||||
|
['vab-main-' + theme.columnStyle]: true,
|
||||||
|
'is-collapse-main': collapse,
|
||||||
|
'is-no-tabs': !showTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="vab-layout-header"
|
||||||
|
:class="{
|
||||||
|
'fixed-header': fixedHeader,
|
||||||
|
'is-no-tabs': !showTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<vab-nav />
|
||||||
|
<vab-tabs v-show="showTabs" />
|
||||||
|
</div>
|
||||||
|
<vab-app-main />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabLayoutColumn',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
collapse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
fixedHeader: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
showTabs: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { theme } = storeToRefs(settingsStore)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-layout-column {
|
||||||
|
.vab-main {
|
||||||
|
&.is-collapse-main {
|
||||||
|
&.vab-main-horizontal,
|
||||||
|
&.vab-main-semicircle {
|
||||||
|
margin-left: calc(var(--el-left-menu-width-min) * 1.4);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.fixed-header {
|
||||||
|
width: calc(100% - var(--el-left-menu-width-min) * 1.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
70
library/layouts/VabLayoutFall/index.vue
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="vab-layout-fall"
|
||||||
|
:class="{
|
||||||
|
fixed: fixedHeader,
|
||||||
|
'no-tabs-bar': !showTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<vab-fall-bar />
|
||||||
|
<div
|
||||||
|
class="vab-main"
|
||||||
|
:class="{
|
||||||
|
'is-collapse-main': collapse,
|
||||||
|
'is-no-tabs': !showTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="vab-layout-header"
|
||||||
|
:class="{
|
||||||
|
'fixed-header': fixedHeader,
|
||||||
|
'is-no-tabs': !showTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<vab-nav />
|
||||||
|
<vab-tabs v-show="showTabs" />
|
||||||
|
</div>
|
||||||
|
<vab-app-main />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabLayoutFall',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
collapse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
fixedHeader: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
showTabs: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vab-layout-fall {
|
||||||
|
.vab-main {
|
||||||
|
&.is-collapse-main {
|
||||||
|
&.vab-main-horizontal,
|
||||||
|
&.vab-main-semicircle {
|
||||||
|
margin-left: calc(var(--el-left-menu-width-min) * 1.4);
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.fixed-header {
|
||||||
|
width: calc(100% - var(--el-left-menu-width-min) * 1.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
61
library/layouts/VabLayoutVertical/index.vue
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="vab-layout-vertical"
|
||||||
|
:class="{
|
||||||
|
fixed: fixedHeader,
|
||||||
|
'no-tabs-bar': !showTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<vab-side-bar />
|
||||||
|
<div v-if="device === 'mobile' && !collapse" class="vab-modal" @click="foldSideBar"></div>
|
||||||
|
<div
|
||||||
|
class="vab-main"
|
||||||
|
:class="{
|
||||||
|
'is-collapse-main': collapse,
|
||||||
|
'is-no-tabs': !showTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="vab-layout-header"
|
||||||
|
:class="{
|
||||||
|
'fixed-header': fixedHeader,
|
||||||
|
'is-no-tabs': !showTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<vab-nav />
|
||||||
|
<vab-tabs v-show="showTabs" />
|
||||||
|
</div>
|
||||||
|
<vab-app-main />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'VabLayoutVertical',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
collapse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
fixedHeader: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
showTabs: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
device: {
|
||||||
|
type: String,
|
||||||
|
default: 'desktop',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { foldSideBar } = settingsStore
|
||||||
|
</script>
|
||||||
176
library/layouts/index.vue
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
<template>
|
||||||
|
<el-scrollbar ref="scrollbarRef" wrap-class="scroll-wrap">
|
||||||
|
<div class="school-apartment-box" :class="{ mobile }">
|
||||||
|
<component :is="layout" :collapse="collapse" :device="device" :fixed-header="theme.fixedHeader" :show-tabs="theme.showTabs" />
|
||||||
|
</div>
|
||||||
|
<vab-statistics />
|
||||||
|
<el-backtop target="#app .scroll-wrap" />
|
||||||
|
</el-scrollbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ElScrollbar } from 'element-plus'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
import { useUserStore } from '/@/store/modules/user'
|
||||||
|
import { convertToCamelCase } from '/@/utils/convertToCamelCase'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Layout',
|
||||||
|
})
|
||||||
|
|
||||||
|
interface ComponentType {
|
||||||
|
default: Component
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const { username } = storeToRefs(userStore)
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { device, collapse, theme } = storeToRefs(settingsStore)
|
||||||
|
const { toggleDevice, foldSideBar, openSideBar, updateTheme } = settingsStore
|
||||||
|
const mobile = ref(false)
|
||||||
|
let oldLayout = theme.value.layout
|
||||||
|
const visibility = useDocumentVisibility()
|
||||||
|
const imports = import.meta.glob<ComponentType>('./**/*.vue', { eager: true })
|
||||||
|
const Components: Record<string, Component> = {}
|
||||||
|
Object.getOwnPropertyNames(imports).forEach((key: any) => {
|
||||||
|
Components[key.replaceAll(/(\/|\.|index.vue)/g, '')] = imports[key].default
|
||||||
|
})
|
||||||
|
|
||||||
|
const layout = computed(() => Components[convertToCamelCase(`vab-layout-${theme.value.layout}`)])
|
||||||
|
|
||||||
|
const resizeBody = () => {
|
||||||
|
const { width } = useWindowSize()
|
||||||
|
mobile.value = width.value - 1 < 992
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(mobile, (value) => {
|
||||||
|
if (value) {
|
||||||
|
oldLayout = theme.value.layout
|
||||||
|
foldSideBar()
|
||||||
|
} else openSideBar()
|
||||||
|
theme.value.layout = value ? 'vertical' : oldLayout
|
||||||
|
toggleDevice(value ? 'mobile' : 'desktop')
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
resizeBody()
|
||||||
|
window.addEventListener('resize', resizeBody)
|
||||||
|
updateTheme()
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (mobile.value) theme.value.layout = oldLayout
|
||||||
|
window.removeEventListener('resize', resizeBody)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(visibility, (current, previous) => {
|
||||||
|
if (current === 'visible' && previous === 'hidden') $baseNotify(`尊敬的${username.value},欢迎回来`, '', 'success', 'bottom-right')
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
route,
|
||||||
|
() => {
|
||||||
|
nextTick(() => {
|
||||||
|
scrollbarRef.value!.setScrollTop(0)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.el-scrollbar__view.scroll-view {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.school-apartment-box {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
[class*='vab-layout-'] {
|
||||||
|
:deep() {
|
||||||
|
.vab-layout-header {
|
||||||
|
border-bottom: 1px solid var(--el-border-color);
|
||||||
|
|
||||||
|
&.is-no-tabs {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fixed {
|
||||||
|
padding-top: calc(var(--el-nav-height) + var(--el-tabs-height));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fixed.no-tabs-bar {
|
||||||
|
padding-top: var(--el-nav-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.fixed-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: calc(var(--el-z-index) - 1);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-main {
|
||||||
|
position: relative;
|
||||||
|
width: auto;
|
||||||
|
min-height: 100%;
|
||||||
|
margin-left: var(--el-left-menu-width);
|
||||||
|
|
||||||
|
&.is-collapse-main {
|
||||||
|
margin-left: var(--el-left-menu-width-min);
|
||||||
|
|
||||||
|
.fixed-header {
|
||||||
|
width: var(--el-right-content-width-min);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.is-collapse-main) {
|
||||||
|
.fixed-header {
|
||||||
|
width: calc(100% - var(--el-left-menu-width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 手机端开始 */
|
||||||
|
&.mobile {
|
||||||
|
:deep() {
|
||||||
|
.vab-layout-vertical {
|
||||||
|
.el-scrollbar.vab-side-bar {
|
||||||
|
z-index: calc(var(--el-z-index) + 1);
|
||||||
|
|
||||||
|
&.is-collapse {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-main {
|
||||||
|
.fixed-header {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 隐藏分页和页码跳转 */
|
||||||
|
.el-pager,
|
||||||
|
.el-pagination__jump {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 手机端结束 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
65
library/plugins/directive.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { throttle } from 'lodash-es'
|
||||||
|
import type { App, DirectiveBinding } from 'vue'
|
||||||
|
import { devDependencies } from '~/package.json'
|
||||||
|
import { hasPermission } from '/@/utils/permission'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install: (app: App<Element>) => {
|
||||||
|
/**
|
||||||
|
* @description 权限自定义指令v-permissions
|
||||||
|
*/
|
||||||
|
app.directive('permissions', {
|
||||||
|
mounted(el, binding: DirectiveBinding) {
|
||||||
|
const { value } = binding
|
||||||
|
if (value && !hasPermission(value)) el.parentNode && el.parentNode.removeChild(el)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* @description 节流自定义指令v-throttle
|
||||||
|
*/
|
||||||
|
app.directive('throttle', {
|
||||||
|
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||||
|
const { value } = binding
|
||||||
|
const throttledFunction = throttle(value, 2000)
|
||||||
|
el.addEventListener('click', throttledFunction)
|
||||||
|
},
|
||||||
|
beforeUnmount(el, { value }) {
|
||||||
|
el.removeEventListener('click', value)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* @description 防抖自定义指令v-debounce
|
||||||
|
*/
|
||||||
|
app.directive('debounce', {
|
||||||
|
mounted(el, binding) {
|
||||||
|
const { value } = binding
|
||||||
|
let debounceTimer: any
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
if (debounceTimer) clearTimeout(debounceTimer)
|
||||||
|
debounceTimer = setTimeout(() => {
|
||||||
|
value()
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* @description 获取焦点自定义指令v-focus
|
||||||
|
*/
|
||||||
|
app.directive('focus', {
|
||||||
|
mounted(el) {
|
||||||
|
el.querySelector('input').focus()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (import.meta.env.MODE !== 'development') {
|
||||||
|
const _devDependencies: any = devDependencies
|
||||||
|
if (!_devDependencies['vite-plu' + 'gin-vit' + 'ebar'] || !_devDependencies['vite-plu' + 'gin-unpl' + 'ugin']) {
|
||||||
|
const theme = { layout: 'layout' }
|
||||||
|
setInterval(() => {
|
||||||
|
localStorage.setItem('bus-theme', JSON.stringify(theme))
|
||||||
|
localStorage.setItem('shop-vite-token', '')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
23
library/plugins/errorLog.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import type { App } from 'vue'
|
||||||
|
import { errorLog } from '/@/config'
|
||||||
|
import pinia from '/@/store'
|
||||||
|
import { useErrorLogStore } from '/@/store/modules/errorLog'
|
||||||
|
import { isArray } from '/@/utils/validate'
|
||||||
|
|
||||||
|
export const needErrorLog = () => {
|
||||||
|
const errorLogArray = isArray(errorLog) ? [...errorLog] : [errorLog]
|
||||||
|
return errorLogArray.includes(import.meta.env.MODE)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addErrorLog = (err: any) => {
|
||||||
|
if (!err.isRequest) console.error('school-apartment 错误拦截:', err)
|
||||||
|
const url = window.location.href
|
||||||
|
const { addErrorLog } = useErrorLogStore(pinia)
|
||||||
|
addErrorLog({ err, url })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install: (app: App<Element>) => {
|
||||||
|
if (needErrorLog()) app.config.errorHandler = addErrorLog
|
||||||
|
},
|
||||||
|
}
|
||||||
25
library/plugins/support.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { ElMessageBox } from 'element-plus'
|
||||||
|
import pinia from '/@/store'
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install: () => {
|
||||||
|
const { title } = useSettingsStore(pinia)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
if (!!window.ActiveXObject || 'ActiveXObject' in window) {
|
||||||
|
ElMessageBox({
|
||||||
|
title: '温馨提示',
|
||||||
|
message:
|
||||||
|
'检测到您当前浏览器使用的是IE内核,自2015年3月起,微软已宣布弃用IE,且不再对IE提供任何更新维护,请<a target="_blank" style="color:blue" href="https://www.microsoft.com/zh-cn/edge/">点击此处</a>访问微软官网更新浏览器,如果您使用的是双核浏览器,请您切换浏览器内核为极速模式',
|
||||||
|
type: 'warning',
|
||||||
|
showClose: true,
|
||||||
|
showConfirmButton: false,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
closeOnPressEscape: false,
|
||||||
|
closeOnHashChange: false,
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
}).then(() => {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
176
library/plugins/vab.ts
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import { ElLoading, ElMessage, ElMessageBox, ElNotification } from 'element-plus'
|
||||||
|
import { head, toArray } from 'lodash-es'
|
||||||
|
import mitt from 'mitt'
|
||||||
|
import type { App, VNode } from 'vue'
|
||||||
|
import { loadingText, messageDuration } from '/@/config'
|
||||||
|
|
||||||
|
export let gp: Record<string, any>
|
||||||
|
|
||||||
|
export const isCheck = () => {
|
||||||
|
if (
|
||||||
|
import.meta.env.MODE !== '\u0064\u0065\u0076\u0065\u006c\u006f\u0070\u006d\u0065\u006e\u0074' &&
|
||||||
|
import.meta.env['\u0056\u0049\u0054\u0045\u005f\u0041\u0050\u0050\u005f\u0053\u0045\u0043\u0052\u0045\u0054\u005f\u004b\u0045\u0059']
|
||||||
|
.length < 50
|
||||||
|
) {
|
||||||
|
setInterval(() => {
|
||||||
|
localStorage.clear()
|
||||||
|
//@ts-ignore
|
||||||
|
location.reload(true)
|
||||||
|
}, 50)
|
||||||
|
;(() => {
|
||||||
|
function block() {
|
||||||
|
setInterval(() => {
|
||||||
|
;(function () {
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
['constructor']('debugger')
|
||||||
|
['call']()
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
block()
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
return false
|
||||||
|
} else return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install: (app: App<Element>) => {
|
||||||
|
|
||||||
|
const $baseLoading = (text = loadingText, background = 'var(--el-color-white)') => {
|
||||||
|
return ElLoading.service({
|
||||||
|
lock: true,
|
||||||
|
text,
|
||||||
|
background,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const $baseMessage = (
|
||||||
|
message: string | VNode,
|
||||||
|
type: 'success' | 'warning' | 'info' | 'error' = 'info',
|
||||||
|
customClass: string,
|
||||||
|
dangerouslyUseHTMLString: boolean,
|
||||||
|
callback?: any
|
||||||
|
) => {
|
||||||
|
if (customClass == 'hey') customClass = `vab-hey-message-${type}`
|
||||||
|
if (dangerouslyUseHTMLString && typeof dangerouslyUseHTMLString == 'function') {
|
||||||
|
callback = dangerouslyUseHTMLString
|
||||||
|
dangerouslyUseHTMLString = false
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessage({
|
||||||
|
message,
|
||||||
|
type,
|
||||||
|
customClass,
|
||||||
|
duration: messageDuration,
|
||||||
|
dangerouslyUseHTMLString,
|
||||||
|
showClose: false,
|
||||||
|
grouping: true,
|
||||||
|
plain: true,
|
||||||
|
onClose: () => {
|
||||||
|
if (callback) callback()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const $baseAlert = (content: string | VNode, title = '温馨提示', callback?: any) => {
|
||||||
|
if (title && typeof title == 'function') {
|
||||||
|
callback = title
|
||||||
|
title = '温馨提示'
|
||||||
|
}
|
||||||
|
ElMessageBox.alert(content, title, {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
dangerouslyUseHTMLString: false,
|
||||||
|
draggable: true,
|
||||||
|
callback: () => {
|
||||||
|
if (callback) callback()
|
||||||
|
},
|
||||||
|
}).then(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
const $baseConfirm = (
|
||||||
|
content: string | VNode,
|
||||||
|
title: string,
|
||||||
|
callback1: any,
|
||||||
|
callback2: any,
|
||||||
|
confirmButtonText = '确定',
|
||||||
|
cancelButtonText = '取消'
|
||||||
|
) => {
|
||||||
|
ElMessageBox.confirm(content, title || '温馨提示', {
|
||||||
|
confirmButtonText,
|
||||||
|
cancelButtonText,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
draggable: true,
|
||||||
|
type: 'warning',
|
||||||
|
lockScroll: false,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
if (callback1) {
|
||||||
|
callback1()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (callback2) {
|
||||||
|
callback2()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const $baseNotify = (
|
||||||
|
message: string,
|
||||||
|
title: string,
|
||||||
|
type: 'success' | 'warning' | 'info' | 'error' = 'success',
|
||||||
|
position: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' = 'top-right',
|
||||||
|
duration: number = messageDuration
|
||||||
|
) => {
|
||||||
|
ElNotification({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
type,
|
||||||
|
duration,
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const _emitter = mitt()
|
||||||
|
|
||||||
|
const $pub = (...args: any[]) => {
|
||||||
|
_emitter.emit(head(args), args[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
const $sub = (...args: any[]) => {
|
||||||
|
Reflect.apply(_emitter.on, _emitter, toArray(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
const $unsub = (...args: any[]) => {
|
||||||
|
Reflect.apply(_emitter.off, _emitter, toArray(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCheck()) {
|
||||||
|
app.provide('$baseAlert', $baseAlert)
|
||||||
|
app.provide('$baseConfirm', $baseConfirm)
|
||||||
|
app.provide('$baseLoading', $baseLoading)
|
||||||
|
app.provide('$baseMessage', $baseMessage)
|
||||||
|
app.provide('$baseNotify', $baseNotify)
|
||||||
|
app.provide('$pub', $pub)
|
||||||
|
app.provide('$sub', $sub)
|
||||||
|
app.provide('$unsub', $unsub)
|
||||||
|
|
||||||
|
gp = {
|
||||||
|
$baseAlert,
|
||||||
|
$baseConfirm,
|
||||||
|
$baseLoading,
|
||||||
|
$baseMessage,
|
||||||
|
$baseNotify,
|
||||||
|
$pub,
|
||||||
|
$sub,
|
||||||
|
$unsub,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
160
library/styles/dark.scss
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
@use 'element-plus/theme-chalk/dark/css-vars.css' as *;
|
||||||
|
|
||||||
|
html.dark > body,
|
||||||
|
html.dark > body[class*='vab-theme-'] {
|
||||||
|
--el-color-black: #ffffff;
|
||||||
|
--el-color-white: #1d1e1f;
|
||||||
|
--el-color-grey: #ffffff;
|
||||||
|
--el-menu-background-color-second: #1d1e1f;
|
||||||
|
--el-border-color: #414243;
|
||||||
|
--el-menu-background-color: #1d1e1f;
|
||||||
|
--el-mask-color: rgba(0, 0, 0, 0.8);
|
||||||
|
--el-background-color: #1d1e1f;
|
||||||
|
|
||||||
|
--w-e-textarea-bg-color: #333;
|
||||||
|
--w-e-textarea-color: #fff;
|
||||||
|
--w-e-textarea-border-color: var(--el-border-color);
|
||||||
|
--w-e-textarea-slight-border-color: var(--el-border-color);
|
||||||
|
--w-e-textarea-slight-color: var(--el-border-color);
|
||||||
|
--w-e-textarea-slight-bg-color: var(--el-border-color);
|
||||||
|
--w-e-textarea-selected-border-color: var(--el-color-primary);
|
||||||
|
--w-e-textarea-handler-bg-color: var(--el-color-primary);
|
||||||
|
--w-e-toolbar-color: #fff;
|
||||||
|
--w-e-toolbar-bg-color: #333;
|
||||||
|
--w-e-toolbar-active-color: #fff;
|
||||||
|
--w-e-toolbar-active-bg-color: #333;
|
||||||
|
--w-e-toolbar-disabled-color: #999;
|
||||||
|
--w-e-toolbar-border-color: #333;
|
||||||
|
--w-e-modal-button-bg-color: #333;
|
||||||
|
--w-e-modal-button-border-color: #d9d9d9;
|
||||||
|
|
||||||
|
color: #ffffff !important;
|
||||||
|
background-color: var(--el-color-white) !important;
|
||||||
|
|
||||||
|
.vab-nav {
|
||||||
|
background: var(--el-color-white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*='-container'],
|
||||||
|
.no-background-container {
|
||||||
|
background: var(--el-color-white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
td {
|
||||||
|
border: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container .login-form {
|
||||||
|
background: rgba(#1d1e1f, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-column-bar {
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.el-tabs {
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.el-tabs__nav {
|
||||||
|
background: var(--el-color-white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
color: var(--el-color-grey) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-arrow {
|
||||||
|
.el-tabs {
|
||||||
|
.el-tabs__item {
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-menu-color-text) !important;
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
.vab-column-grid {
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
border-color: transparent var(--el-menu-color-text) transparent transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-tabs-content-card {
|
||||||
|
.el-tabs__header {
|
||||||
|
.el-tabs__item {
|
||||||
|
border: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-logo-column {
|
||||||
|
.logo {
|
||||||
|
background: var(--el-color-white) !important;
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-side-bar {
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-header {
|
||||||
|
border-bottom: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.vab-logo-horizontal {
|
||||||
|
height: calc(var(--el-header-height) - 2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-main {
|
||||||
|
.right-panel {
|
||||||
|
[class*='ri-'],
|
||||||
|
.username {
|
||||||
|
color: var(--el-color-grey) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* svg */
|
||||||
|
[fill='#fff'] {
|
||||||
|
fill: var(--el-color-white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button,
|
||||||
|
.el-switch,
|
||||||
|
.el-checkbox,
|
||||||
|
.el-checkbox-group,
|
||||||
|
.el-radio,
|
||||||
|
.el-radio-group,
|
||||||
|
.el-slider,
|
||||||
|
.el-tag,
|
||||||
|
.el-pagination,
|
||||||
|
.el-segmented,
|
||||||
|
.el-carousel,
|
||||||
|
.el-menu,
|
||||||
|
.el-card__body::after,
|
||||||
|
.el-alert.is-dark,
|
||||||
|
.el-badge,
|
||||||
|
.fold-unfold,
|
||||||
|
.schedule-box,
|
||||||
|
.vab-theme-setting div a:hover,
|
||||||
|
.transition-box,
|
||||||
|
.top-card-blue,
|
||||||
|
.icon-panel {
|
||||||
|
--el-color-white: var(--el-menu-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-divider__text {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-blockquote {
|
||||||
|
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
310
library/styles/plain.scss
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
html > body.vab-theme-plain {
|
||||||
|
--el-menu-background-color: #ffffff;
|
||||||
|
--el-menu-color-text: #515a6e;
|
||||||
|
|
||||||
|
@mixin container {
|
||||||
|
color: var(--el-menu-color-text) !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin active {
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
|
|
||||||
|
i,
|
||||||
|
svg,
|
||||||
|
span[title] {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
|
|
||||||
|
i,
|
||||||
|
svg,
|
||||||
|
span[title] {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu--vertical {
|
||||||
|
.el-menu-item {
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-item.is-active,
|
||||||
|
.el-sub-menu__title.is-active {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu--horizontal {
|
||||||
|
.el-menu-item:not(.is-disabled):focus,
|
||||||
|
.el-menu-item:not(.is-disabled):hover {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-sub-menu__title:not(.is-disabled):focus,
|
||||||
|
.el-sub-menu__title:not(.is-disabled):hover {
|
||||||
|
border-radius: var(--el-border-radius-base) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-logo-common,
|
||||||
|
.vab-logo-vertical,
|
||||||
|
.vab-logo-horizontal {
|
||||||
|
@include container;
|
||||||
|
|
||||||
|
.title,
|
||||||
|
.vab-icon {
|
||||||
|
@include container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-logo-column,
|
||||||
|
.vab-logo-comprehensive {
|
||||||
|
@include container;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include container;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo,
|
||||||
|
.vab-icon {
|
||||||
|
@include container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-column-bar {
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.el-tabs {
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
@include container;
|
||||||
|
|
||||||
|
.el-tabs__nav-wrap.is-left {
|
||||||
|
background: #f7faff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__item,
|
||||||
|
.el-tabs__nav {
|
||||||
|
@include container;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__item.is-active {
|
||||||
|
color: #ffffff !important;
|
||||||
|
background: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu {
|
||||||
|
.el-menu-item.is-active,
|
||||||
|
.el-sub-menu__title.is-active,
|
||||||
|
.el-menu-item:hover,
|
||||||
|
.el-sub-menu__title:hover {
|
||||||
|
i {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-card {
|
||||||
|
.el-tabs {
|
||||||
|
.el-tabs__item {
|
||||||
|
.vab-column-grid:hover {
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-arrow {
|
||||||
|
.el-tabs {
|
||||||
|
.el-tabs__item {
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-menu-color-text) !important;
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
.vab-column-grid {
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
border-color: transparent var(--el-border-color) transparent transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-tabs-content-card {
|
||||||
|
.el-tabs__header {
|
||||||
|
.el-tabs__item {
|
||||||
|
border: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-logo-column {
|
||||||
|
.logo {
|
||||||
|
background: #ffffff !important;
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-header {
|
||||||
|
@include container;
|
||||||
|
|
||||||
|
.vab-main {
|
||||||
|
@include container;
|
||||||
|
|
||||||
|
.right-panel {
|
||||||
|
.vab-right-tools {
|
||||||
|
[class*='ri-'] {
|
||||||
|
color: var(--el-menu-color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.username,
|
||||||
|
.username *,
|
||||||
|
> i,
|
||||||
|
> div > i,
|
||||||
|
> span > i,
|
||||||
|
> div > span > i {
|
||||||
|
@include container;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu {
|
||||||
|
&--horizontal {
|
||||||
|
.el-sub-menu .el-sub-menu__title,
|
||||||
|
.el-menu-item {
|
||||||
|
@include active;
|
||||||
|
|
||||||
|
[class*='ri-'] {
|
||||||
|
color: var(--el-menu-color-text) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .el-sub-menu.is-active > .el-sub-menu__title,
|
||||||
|
> .el-menu-item.is-active {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
@include active;
|
||||||
|
|
||||||
|
[class*='ri-'] {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .el-sub-menu > .el-sub-menu__title,
|
||||||
|
> .el-menu-item {
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-tabs {
|
||||||
|
&-more {
|
||||||
|
&-active,
|
||||||
|
&:hover {
|
||||||
|
.vab-tabs-more-icon {
|
||||||
|
.box:before,
|
||||||
|
.box:after {
|
||||||
|
background: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-tabs-content-card {
|
||||||
|
.el-tabs__header {
|
||||||
|
.el-tabs__item {
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background: var(--el-color-primary-light-9) !important;
|
||||||
|
border: 1px solid var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: 1px solid var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-tabs-content-smart {
|
||||||
|
.el-tabs__header {
|
||||||
|
.el-tabs__item {
|
||||||
|
&.is-active {
|
||||||
|
background: var(--el-color-primary-light-9) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
background-color: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--el-color-primary-light-9) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-tabs-content-smooth {
|
||||||
|
.el-tabs__header {
|
||||||
|
.el-tabs__item {
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background: var(--el-color-primary-light-9) !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
background: var(--el-color-primary-light-9) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-menu-color-text) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-side-bar {
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.el-sub-menu__title:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
293
library/styles/technology.scss
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
html > body.vab-theme-technology {
|
||||||
|
--el-dialog-bg-color: #040d32;
|
||||||
|
--el-color-black: #ffffff;
|
||||||
|
--el-color-white: #040d32;
|
||||||
|
--el-color-grey: #ffffff;
|
||||||
|
--el-menu-background-color: #040d32;
|
||||||
|
--el-menu-background-color-second: #040d32;
|
||||||
|
--el-background-technology: #040d32;
|
||||||
|
--el-menu-text-color: #fff;
|
||||||
|
--el-bg-color-page: #040d32;
|
||||||
|
--el-bg-color: #103171;
|
||||||
|
--el-bg-color-overlay: #103171;
|
||||||
|
--el-text-color-primary: #e5eaf3;
|
||||||
|
--el-text-color-regular: #e5eaf3;
|
||||||
|
--el-text-color-secondary: #e5eaf3;
|
||||||
|
--el-text-color-placeholder: #e5eaf3;
|
||||||
|
--el-text-color-disabled: #cfd3dc;
|
||||||
|
--el-border-color-darker: #{mix(#ffffff, #2c6191, 10%)};
|
||||||
|
--el-border-color-dark: #{mix(#ffffff, #2c6191, 20%)};
|
||||||
|
--el-border-color: #{mix(#ffffff, #2c6191, 10%)};
|
||||||
|
--el-border: 1px solid #{mix(#ffffff, #2c6191, 10%)};
|
||||||
|
--el-border-color-light: #{mix(#ffffff, #2c6191, 20%)};
|
||||||
|
--el-border-color-lighter: #{mix(#ffffff, #2c6191, 30%)};
|
||||||
|
--el-border-color-extra-light: #2c6191;
|
||||||
|
--el-fill-color-darker: #2c6191;
|
||||||
|
--el-fill-color-dark: #2c6191;
|
||||||
|
--el-fill-color: #103171;
|
||||||
|
--el-fill-color-light: #{mix(#ffffff, #103171, 10%)};
|
||||||
|
--el-fill-color-lighter: #{mix(#ffffff, #103171, 20%)};
|
||||||
|
--el-fill-color-extra-light: #{mix(#ffffff, #103171, 10%)};
|
||||||
|
--el-fill-color-blank: #{mix(#ffffff, #103171, 1%)};
|
||||||
|
--el-mask-color: rgba(16, 49, 113, 0.8);
|
||||||
|
|
||||||
|
--w-e-textarea-bg-color: #103171;
|
||||||
|
--w-e-textarea-color: #fff;
|
||||||
|
--w-e-textarea-border-color: #103171;
|
||||||
|
--w-e-textarea-slight-border-color: #2c6191;
|
||||||
|
--w-e-textarea-slight-color: #040d32;
|
||||||
|
--w-e-textarea-slight-bg-color: #040d32;
|
||||||
|
--w-e-textarea-selected-border-color: #b4d5ff;
|
||||||
|
--w-e-textarea-handler-bg-color: #4290f7;
|
||||||
|
--w-e-toolbar-color: #fff;
|
||||||
|
--w-e-toolbar-bg-color: #103171;
|
||||||
|
--w-e-toolbar-active-color: #fff;
|
||||||
|
--w-e-toolbar-active-bg-color: #2c6191;
|
||||||
|
--w-e-toolbar-disabled-color: #fff;
|
||||||
|
--w-e-toolbar-border-color: #2c6191;
|
||||||
|
--w-e-modal-button-bg-color: #2c6191;
|
||||||
|
--w-e-modal-button-border-color: #2c6191;
|
||||||
|
|
||||||
|
color: var(--el-color-grey) !important;
|
||||||
|
background-color: var(--el-color-white) !important;
|
||||||
|
|
||||||
|
.vab-side-bar {
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile {
|
||||||
|
.vab-side-bar {
|
||||||
|
border-left: 0 solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-nav {
|
||||||
|
background: var(--el-color-white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
section > div[class*='-container'],
|
||||||
|
.no-background-container {
|
||||||
|
background: var(--el-color-white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-column-bar {
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.el-tabs {
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
color: var(--el-color-grey) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__nav {
|
||||||
|
background: var(--el-color-white) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-arrow {
|
||||||
|
.el-tabs {
|
||||||
|
.el-tabs__item {
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-menu-color-text) !important;
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
.vab-column-grid {
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
border-color: transparent var(--el-menu-color-text) transparent transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-tabs-content-card {
|
||||||
|
.el-tabs__header {
|
||||||
|
.el-tabs__item {
|
||||||
|
border: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-logo-column {
|
||||||
|
.logo {
|
||||||
|
background: var(--el-color-white) !important;
|
||||||
|
border-right: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*el-dialog、el-message-box */
|
||||||
|
.el-dialog,
|
||||||
|
.el-message-box {
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
background: var(--el-background-technology);
|
||||||
|
border: 2px solid #00a1ff;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
position: relative;
|
||||||
|
background: var(--el-background-technology) !important;
|
||||||
|
|
||||||
|
.el-dialog__title,
|
||||||
|
.el-dialog__close {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
i[class*='__close'] {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 22px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
transition: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #00a1ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
bottom: -2px;
|
||||||
|
left: 30px;
|
||||||
|
z-index: 0;
|
||||||
|
width: calc(100% - 60px);
|
||||||
|
pointer-events: none;
|
||||||
|
content: '';
|
||||||
|
border-top: 2px solid #016886;
|
||||||
|
border-bottom: 2px solid #016886;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 30px;
|
||||||
|
right: -2px;
|
||||||
|
left: -2px;
|
||||||
|
z-index: 0;
|
||||||
|
height: calc(100% - 60px);
|
||||||
|
pointer-events: none;
|
||||||
|
content: '';
|
||||||
|
border-right: 2px solid #016886;
|
||||||
|
border-left: 2px solid #016886;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*el-drawer */
|
||||||
|
.el-drawer.rtl {
|
||||||
|
border-left: 1px solid var(--el-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-pagination {
|
||||||
|
&.is-background {
|
||||||
|
.btn-prev:disabled,
|
||||||
|
.btn-next:disabled {
|
||||||
|
background-color: var(--el-pagination-button-bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-drawer__header {
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-radio-button {
|
||||||
|
&__inner {
|
||||||
|
border: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input {
|
||||||
|
&.is-disabled {
|
||||||
|
.el-input__wrapper {
|
||||||
|
background: #214e85 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-header {
|
||||||
|
border-bottom: 1px solid var(--el-border-color) !important;
|
||||||
|
|
||||||
|
.vab-logo-horizontal {
|
||||||
|
height: calc(var(--el-header-height) - 2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-main {
|
||||||
|
.right-panel {
|
||||||
|
[class*='ri-'],
|
||||||
|
.username {
|
||||||
|
color: var(--el-color-grey) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-tabs-content-smooth {
|
||||||
|
.el-tabs__header {
|
||||||
|
.el-tabs__item {
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-grey);
|
||||||
|
background: #123372;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .vab-hey-message */
|
||||||
|
[class*='vab-hey-message'] {
|
||||||
|
border: 1px solid var(--el-border-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* svg */
|
||||||
|
[fill='#fff'] {
|
||||||
|
fill: var(--el-background-technology) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[fill='#f2f2f2'],
|
||||||
|
[fill='#d0d2d5'] {
|
||||||
|
fill: var(--el-bg-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container,
|
||||||
|
.register-container {
|
||||||
|
background: url('/@/assets/login_images/background.png') center center fixed no-repeat !important;
|
||||||
|
background-size: cover !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button,
|
||||||
|
.el-switch,
|
||||||
|
.el-checkbox,
|
||||||
|
.el-checkbox-group,
|
||||||
|
.el-radio,
|
||||||
|
.el-radio-group,
|
||||||
|
.el-slider,
|
||||||
|
.el-tag,
|
||||||
|
.el-pagination,
|
||||||
|
.el-segmented,
|
||||||
|
.el-carousel,
|
||||||
|
.el-menu,
|
||||||
|
.el-card__body::after,
|
||||||
|
.el-alert.is-dark,
|
||||||
|
.el-badge,
|
||||||
|
.fold-unfold,
|
||||||
|
.schedule-box,
|
||||||
|
.vab-theme-setting div a:hover,
|
||||||
|
.transition-box,
|
||||||
|
.top-card-blue,
|
||||||
|
.icon-panel {
|
||||||
|
--el-color-white: var(--el-menu-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-divider__text {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vab-blockquote {
|
||||||
|
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
1053
library/styles/vab.scss
Normal file
154
library/styles/var.scss
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
@use './variables' as *;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
//背景色
|
||||||
|
--el-background-color: #f6f8f9;
|
||||||
|
//菜单背景色
|
||||||
|
--el-menu-background-color: #282c34;
|
||||||
|
//分栏布局右侧菜单背景色
|
||||||
|
--el-menu-background-color-second: #ffffff;
|
||||||
|
//菜单文字颜色
|
||||||
|
--el-menu-color-text: #ffffff;
|
||||||
|
//菜单item高度
|
||||||
|
--el-menu-item-height: 50px;
|
||||||
|
//横向菜单sub-item高度
|
||||||
|
--el-menu-horizontal-sub-item-height: calc(var(--el-menu-item-height) - 8px);
|
||||||
|
// 浅黑色
|
||||||
|
--el-color-grey: rgba(0, 0, 0, 0.65);
|
||||||
|
//黑色
|
||||||
|
--el-color-black: rgba(0, 0, 0, 0.75);
|
||||||
|
// primary颜色
|
||||||
|
--el-color-primary: #4e88f3;
|
||||||
|
--el-color-primary-light-1: #{$vab-color-primary-light-1};
|
||||||
|
--el-color-primary-light-2: #{$vab-color-primary-light-2};
|
||||||
|
--el-color-primary-light-3: #{$vab-color-primary-light-3};
|
||||||
|
--el-color-primary-light-4: #{$vab-color-primary-light-4};
|
||||||
|
--el-color-primary-light-5: #{$vab-color-primary-light-5};
|
||||||
|
--el-color-primary-light-6: #{$vab-color-primary-light-6};
|
||||||
|
--el-color-primary-light-7: #{$vab-color-primary-light-7};
|
||||||
|
--el-color-primary-light-8: #{$vab-color-primary-light-8};
|
||||||
|
--el-color-primary-light-9: #{$vab-color-primary-light-9};
|
||||||
|
--el-color-primary-dark-2: #{$vab-color-primary};
|
||||||
|
// success颜色
|
||||||
|
--el-color-success: #{$vab-color-success};
|
||||||
|
--el-color-success-light: #{$vab-color-success-light};
|
||||||
|
--el-color-success-lighter: #{$vab-color-success-lighter};
|
||||||
|
--el-color-success-light-1: #{$vab-color-success-light-1};
|
||||||
|
--el-color-success-light-2: #{$vab-color-success-light-2};
|
||||||
|
--el-color-success-light-3: #{$vab-color-success-light-3};
|
||||||
|
--el-color-success-light-4: #{$vab-color-success-light-4};
|
||||||
|
--el-color-success-light-5: #{$vab-color-success-light-5};
|
||||||
|
--el-color-success-light-6: #{$vab-color-success-light-6};
|
||||||
|
--el-color-success-light-7: #{$vab-color-success-light-7};
|
||||||
|
--el-color-success-light-8: #{$vab-color-success-light-8};
|
||||||
|
--el-color-success-light-9: #{$vab-color-success-light-9};
|
||||||
|
--el-color-success-dark-2: #{$vab-color-success};
|
||||||
|
//warning颜色
|
||||||
|
--el-color-warning: #{$vab-color-warning};
|
||||||
|
--el-color-warning-light: #{$vab-color-warning-light};
|
||||||
|
--el-color-warning-lighter: #{$vab-color-warning-lighter};
|
||||||
|
--el-color-warning-light-1: #{$vab-color-warning-light-1};
|
||||||
|
--el-color-warning-light-2: #{$vab-color-warning-light-2};
|
||||||
|
--el-color-warning-light-3: #{$vab-color-warning-light-3};
|
||||||
|
--el-color-warning-light-4: #{$vab-color-warning-light-4};
|
||||||
|
--el-color-warning-light-5: #{$vab-color-warning-light-5};
|
||||||
|
--el-color-warning-light-6: #{$vab-color-warning-light-6};
|
||||||
|
--el-color-warning-light-7: #{$vab-color-warning-light-7};
|
||||||
|
--el-color-warning-light-8: #{$vab-color-warning-light-8};
|
||||||
|
--el-color-warning-light-9: #{$vab-color-warning-light-9};
|
||||||
|
--el-color-warning-dark-2: #{$vab-color-warning};
|
||||||
|
//danger颜色
|
||||||
|
--el-color-danger: #{$vab-color-danger};
|
||||||
|
--el-color-danger-light: #{$vab-color-danger-light};
|
||||||
|
--el-color-danger-lighter: #{$vab-color-danger-lighter};
|
||||||
|
--el-color-danger-light-1: #{$vab-color-danger-light-1};
|
||||||
|
--el-color-danger-light-2: #{$vab-color-danger-light-2};
|
||||||
|
--el-color-danger-light-3: #{$vab-color-danger-light-3};
|
||||||
|
--el-color-danger-light-4: #{$vab-color-danger-light-4};
|
||||||
|
--el-color-danger-light-5: #{$vab-color-danger-light-5};
|
||||||
|
--el-color-danger-light-6: #{$vab-color-danger-light-6};
|
||||||
|
--el-color-danger-light-7: #{$vab-color-danger-light-7};
|
||||||
|
--el-color-danger-light-8: #{$vab-color-danger-light-8};
|
||||||
|
--el-color-danger-light-9: #{$vab-color-danger-light-9};
|
||||||
|
--el-color-danger-dark-2: #{$vab-color-danger};
|
||||||
|
//error颜色
|
||||||
|
--el-color-error: #{$vab-color-error};
|
||||||
|
--el-color-error-light: #{$vab-color-error-light};
|
||||||
|
--el-color-error-lighter: #{$vab-color-error-lighter};
|
||||||
|
--el-color-error-light-1: #{$vab-color-error-light-1};
|
||||||
|
--el-color-error-light-2: #{$vab-color-error-light-2};
|
||||||
|
--el-color-error-light-3: #{$vab-color-error-light-3};
|
||||||
|
--el-color-error-light-4: #{$vab-color-error-light-4};
|
||||||
|
--el-color-error-light-5: #{$vab-color-error-light-5};
|
||||||
|
--el-color-error-light-6: #{$vab-color-error-light-6};
|
||||||
|
--el-color-error-light-7: #{$vab-color-error-light-7};
|
||||||
|
--el-color-error-light-8: #{$vab-color-error-light-8};
|
||||||
|
--el-color-error-light-9: #{$vab-color-error-light-9};
|
||||||
|
--el-color-error-dark-2: #{$vab-color-error};
|
||||||
|
//info颜色
|
||||||
|
--el-color-info: #{$vab-color-info};
|
||||||
|
--el-color-info-light: #{$vab-color-info-light};
|
||||||
|
--el-color-info-lighter: #{$vab-color-info-lighter};
|
||||||
|
--el-color-info-dark-2: #{$vab-color-info};
|
||||||
|
/**
|
||||||
|
* @description: 全局字体大小
|
||||||
|
* @author sundan
|
||||||
|
*/
|
||||||
|
--el-font-size-base: 14px;
|
||||||
|
--el-font-size-small: calc(var(--el-font-size-base) - 1px);
|
||||||
|
--el-font-size-extra-small: calc(var(--el-font-size-base) - 2px);
|
||||||
|
--el-font-size-medium: calc(var(--el-font-size-base) + 2px);
|
||||||
|
--el-font-size-large: calc(var(--el-font-size-base) + 4px);
|
||||||
|
--el-font-size-extra-large: calc(var(--el-font-size-base) + 6px);
|
||||||
|
|
||||||
|
--ti-common-font-size-base: calc(var(--el-font-size-base) - 2px) !important;
|
||||||
|
--ti-common-font-size-1: var(--el-font-size-base) !important;
|
||||||
|
--ti-common-font-size-2: calc(var(--el-font-size-base) + 2px) !important;
|
||||||
|
--ti-common-font-size-3: calc(var(--el-font-size-base) + 4px) !important;
|
||||||
|
--ti-common-font-size-4: calc(var(--el-font-size-base) + 6px) !important;
|
||||||
|
--ti-common-font-size-5: calc(var(--el-font-size-base) + 10px) !important;
|
||||||
|
--ti-common-font-size-6: calc(var(--el-font-size-base) + 18px) !important;
|
||||||
|
--ti-common-font-size-7: calc(var(--el-font-size-base) + 22px) !important;
|
||||||
|
//默认动画
|
||||||
|
// --el-transition-duration: 0.3s;
|
||||||
|
// --el-transition-function-ease-in-out-bezier: cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||||
|
--el-transition-duration: 0.25s;
|
||||||
|
--el-transition-function-ease-in-out-bezier: cubic-bezier(0.42, 0, 0.58, 1);
|
||||||
|
--el-transition: all var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier), border 0s, color 0.05s, font-size 0s;
|
||||||
|
//纵向、分栏左侧导航已折叠的宽度
|
||||||
|
--el-left-menu-width-min: 64px;
|
||||||
|
//纵向左侧导航已折叠右侧内容的宽度
|
||||||
|
--el-right-content-width-min: calc(100% - var(--el-left-menu-width-min));
|
||||||
|
//纵向左侧导航未折叠的宽度
|
||||||
|
--el-left-menu-width: 266px;
|
||||||
|
//默认padding
|
||||||
|
--el-padding: 20px;
|
||||||
|
//默认margin
|
||||||
|
--el-margin: 20px;
|
||||||
|
//顶部nav的高度
|
||||||
|
--el-nav-height: 60px;
|
||||||
|
//顶部标签页tabs的高度
|
||||||
|
--el-tabs-height: 50px;
|
||||||
|
//顶部标签页tabs中每一个item的高度
|
||||||
|
--el-tab-item-height: 34px;
|
||||||
|
//底部footer的高度
|
||||||
|
--el-footer-height: 50px;
|
||||||
|
//遮罩层颜色
|
||||||
|
--el-mask-color: rgba(255, 255, 255, 0.8);
|
||||||
|
//z-index
|
||||||
|
--el-z-index: 1999;
|
||||||
|
//横向top-bar、logo、一级菜单的高度
|
||||||
|
--el-header-height: 60px;
|
||||||
|
//纵向、综合、分栏logo的高度
|
||||||
|
--el-logo-height: 60px;
|
||||||
|
//标题高度
|
||||||
|
--el-title-color: #ffffff;
|
||||||
|
//输入框高度
|
||||||
|
--el-input-height: 32px;
|
||||||
|
//圆角
|
||||||
|
--el-border-radius-base: 5px;
|
||||||
|
//容器高度
|
||||||
|
--el-container-height: calc(
|
||||||
|
var(--vh, 1vh) * 100 - var(--el-nav-height) - var(--el-tabs-height) - var(--el-padding) * 3 - var(--el-footer-height)
|
||||||
|
);
|
||||||
|
}
|
||||||
78
library/styles/variables.scss
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* @description 全局主题变量配置
|
||||||
|
* @author sundan
|
||||||
|
*/
|
||||||
|
|
||||||
|
$base-color-primary: #4e88f3;
|
||||||
|
$base-color-success: #13ce66;
|
||||||
|
$base-color-warning: #e6a23c;
|
||||||
|
$base-color-danger: #fd4e4e;
|
||||||
|
$base-color-error: #fd4e4e;
|
||||||
|
$base-color-text: #909399;
|
||||||
|
|
||||||
|
$vab-color-primary: $base-color-primary;
|
||||||
|
$vab-color-primary-light-1: rgba($base-color-primary, 0.9);
|
||||||
|
$vab-color-primary-light-2: rgba($base-color-primary, 0.8);
|
||||||
|
$vab-color-primary-light-3: rgba($base-color-primary, 0.7);
|
||||||
|
$vab-color-primary-light-4: rgba($base-color-primary, 0.6);
|
||||||
|
$vab-color-primary-light-5: rgba($base-color-primary, 0.5);
|
||||||
|
$vab-color-primary-light-6: rgba($base-color-primary, 0.4);
|
||||||
|
$vab-color-primary-light-7: rgba($base-color-primary, 0.3);
|
||||||
|
$vab-color-primary-light-8: rgba($base-color-primary, 0.2);
|
||||||
|
$vab-color-primary-light-9: rgba($base-color-primary, 0.1);
|
||||||
|
|
||||||
|
$vab-color-success: $base-color-success;
|
||||||
|
$vab-color-success-light: rgba($base-color-success, 0.2);
|
||||||
|
$vab-color-success-lighter: rgba($base-color-success, 0.1);
|
||||||
|
$vab-color-success-light-1: rgba($base-color-success, 0.9);
|
||||||
|
$vab-color-success-light-2: rgba($base-color-success, 0.8);
|
||||||
|
$vab-color-success-light-3: rgba($base-color-success, 0.7);
|
||||||
|
$vab-color-success-light-4: rgba($base-color-success, 0.6);
|
||||||
|
$vab-color-success-light-5: rgba($base-color-success, 0.5);
|
||||||
|
$vab-color-success-light-6: rgba($base-color-success, 0.4);
|
||||||
|
$vab-color-success-light-7: rgba($base-color-success, 0.3);
|
||||||
|
$vab-color-success-light-8: rgba($base-color-success, 0.2);
|
||||||
|
$vab-color-success-light-9: rgba($base-color-success, 0.1);
|
||||||
|
|
||||||
|
$vab-color-warning: $base-color-warning;
|
||||||
|
$vab-color-warning-light: rgba($base-color-warning, 0.2);
|
||||||
|
$vab-color-warning-lighter: rgba($base-color-warning, 0.1);
|
||||||
|
$vab-color-warning-light-1: rgba($base-color-warning, 0.9);
|
||||||
|
$vab-color-warning-light-2: rgba($base-color-warning, 0.8);
|
||||||
|
$vab-color-warning-light-3: rgba($base-color-warning, 0.7);
|
||||||
|
$vab-color-warning-light-4: rgba($base-color-warning, 0.6);
|
||||||
|
$vab-color-warning-light-5: rgba($base-color-warning, 0.5);
|
||||||
|
$vab-color-warning-light-6: rgba($base-color-warning, 0.4);
|
||||||
|
$vab-color-warning-light-7: rgba($base-color-warning, 0.3);
|
||||||
|
$vab-color-warning-light-8: rgba($base-color-warning, 0.2);
|
||||||
|
$vab-color-warning-light-9: rgba($base-color-warning, 0.1);
|
||||||
|
|
||||||
|
$vab-color-danger: $base-color-danger;
|
||||||
|
$vab-color-danger-light: rgba($base-color-danger, 0.2);
|
||||||
|
$vab-color-danger-lighter: rgba($base-color-danger, 0.1);
|
||||||
|
$vab-color-danger-light-1: rgba($base-color-danger, 0.9);
|
||||||
|
$vab-color-danger-light-2: rgba($base-color-danger, 0.8);
|
||||||
|
$vab-color-danger-light-3: rgba($base-color-danger, 0.7);
|
||||||
|
$vab-color-danger-light-4: rgba($base-color-danger, 0.6);
|
||||||
|
$vab-color-danger-light-5: rgba($base-color-danger, 0.5);
|
||||||
|
$vab-color-danger-light-6: rgba($base-color-danger, 0.4);
|
||||||
|
$vab-color-danger-light-7: rgba($base-color-danger, 0.3);
|
||||||
|
$vab-color-danger-light-8: rgba($base-color-danger, 0.2);
|
||||||
|
$vab-color-danger-light-9: rgba($base-color-danger, 0.1);
|
||||||
|
|
||||||
|
$vab-color-error: $base-color-error;
|
||||||
|
$vab-color-error-light: rgba($base-color-error, 0.2);
|
||||||
|
$vab-color-error-lighter: rgba($base-color-error, 0.1);
|
||||||
|
$vab-color-error-light-1: rgba($base-color-error, 0.9);
|
||||||
|
$vab-color-error-light-2: rgba($base-color-error, 0.8);
|
||||||
|
$vab-color-error-light-3: rgba($base-color-error, 0.7);
|
||||||
|
$vab-color-error-light-4: rgba($base-color-error, 0.6);
|
||||||
|
$vab-color-error-light-5: rgba($base-color-error, 0.5);
|
||||||
|
$vab-color-error-light-6: rgba($base-color-error, 0.4);
|
||||||
|
$vab-color-error-light-7: rgba($base-color-error, 0.3);
|
||||||
|
$vab-color-error-light-8: rgba($base-color-error, 0.2);
|
||||||
|
$vab-color-error-light-9: rgba($base-color-error, 0.1);
|
||||||
|
|
||||||
|
$vab-color-info: $base-color-text;
|
||||||
|
$vab-color-info-light: rgba($base-color-text, 0.2);
|
||||||
|
$vab-color-info-lighter: rgba($base-color-text, 0.1);
|
||||||
141
package.json
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
{
|
||||||
|
"name": "student-apartment-system",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"author": "18758216547",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "set VITE_CJS_IGNORE_WARNING=true && vite",
|
||||||
|
"dev:mac": "export VITE_CJS_IGNORE_WARNING=true && vite",
|
||||||
|
"dev:vue-tsc": "set VITE_CJS_IGNORE_WARNING=true && vue-tsc --noEmit && vite",
|
||||||
|
"dev:https": "set VITE_CJS_IGNORE_WARNING=true && vite --config ./vite.config.dev.ts",
|
||||||
|
"vue-tsc": "vue-tsc --noEmit",
|
||||||
|
"build": "set VITE_CJS_IGNORE_WARNING=true && vue-tsc --noEmit && vite build",
|
||||||
|
"build:mac": "export VITE_CJS_IGNORE_WARNING=true && vue-tsc --noEmit && vite build",
|
||||||
|
"build:fast": "set VITE_CJS_IGNORE_WARNING=true && set NODE_OPTIONS=--max-old-space-size=8192 && vite build",
|
||||||
|
"build:fast:mac": "export VITE_CJS_IGNORE_WARNING=true && export NODE_OPTIONS=--max-old-space-size=8192 && vite build",
|
||||||
|
"build:website": "set VITE_CJS_IGNORE_WARNING=true && set NODE_OPTIONS=--max-old-space-size=8192 && vite build --config ./vite.config.website.ts && node write.version.js",
|
||||||
|
"build:website:mac": "export VITE_CJS_IGNORE_WARNING=true && export NODE_OPTIONS=--max-old-space-size=8192 && vite build --config ./vite.config.website.ts && node write.version.js",
|
||||||
|
"build:report": "set VITE_CJS_IGNORE_WARNING=true && set NODE_OPTIONS=--max-old-space-size=8192 && vite build --config ./vite.config.dev.ts",
|
||||||
|
"build:vue-school-apartment": "set VITE_CJS_IGNORE_WARNING=true && vite build",
|
||||||
|
"preview": " set VITE_CJS_IGNORE_WARNING=true && vite preview",
|
||||||
|
"lint:eslint": "eslint . --fix",
|
||||||
|
"lint:prettier": "prettier {src,mock,library,types}/**/*.{html,vue,css,sass,scss,js,ts} --write",
|
||||||
|
"lint:stylelint": "stylelint {src,mock,library}/**/*.{vue,css,sass,scss} --fix --cache --cache-location node_modules/.cache/stylelint/",
|
||||||
|
"global:install": "cnpm i -g nrm cnpm npm-check-updates pnpm",
|
||||||
|
"globle:update": "ncu -g",
|
||||||
|
"module:install": "pnpm install",
|
||||||
|
"module:update": "ncu -u --reject vite-plugin-mock,eslint,sass,vue-echarts --registry https://registry.npmmirror.com && npm run module:install",
|
||||||
|
"module:reinstall": "rimraf node_modules && npm run module:install",
|
||||||
|
"git": "start ./git.sh",
|
||||||
|
"generate-pwa-assets": "pwa-assets-generator",
|
||||||
|
"template": "plop"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||||
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
|
"@imengyu/vue3-context-menu": "^1.4.2",
|
||||||
|
"@kangc/v-md-editor": "^2.3.18",
|
||||||
|
"@logicflow/core": "^2.0.1",
|
||||||
|
"@logicflow/extension": "^2.0.1",
|
||||||
|
"@lucky-canvas/vue": "^0.1.11",
|
||||||
|
"@opentiny/vue": "^3.18.0",
|
||||||
|
"@vueuse/core": "^10.11.1",
|
||||||
|
"@vueuse/head": "^2.0.0",
|
||||||
|
"@wangeditor/editor": "^5.1.23",
|
||||||
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
|
"axios": "^1.7.3",
|
||||||
|
"dayjs": "^1.11.12",
|
||||||
|
"disable-devtool": "^0.3.7",
|
||||||
|
"echarts": "^5.5.1",
|
||||||
|
"element-plus": "2.7.8",
|
||||||
|
"jsencrypt": "^3.3.2",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"mitt": "^3.0.1",
|
||||||
|
"mockjs": "^1.1.0",
|
||||||
|
"normalize.css": "^8.0.1",
|
||||||
|
"nprogress": "^0.2.0",
|
||||||
|
"pinia": "^2.2.1",
|
||||||
|
"qs": "^6.13.0",
|
||||||
|
"sortablejs": "^1.15.2",
|
||||||
|
"typeit": "^8.8.4",
|
||||||
|
"vsv-icon": "^1.2.2",
|
||||||
|
"vue": "^3.4.37",
|
||||||
|
"vue-draggable-plus": "^0.5.3",
|
||||||
|
"vue-echarts": "6.7.3",
|
||||||
|
"vue-i18n": "^9.13.1",
|
||||||
|
"vue-json-viewer": "^3.0.4",
|
||||||
|
"vue-pdf-embed": "^2.1.0",
|
||||||
|
"vue-qr": "^4.0.9",
|
||||||
|
"vue-router": "^4.4.3",
|
||||||
|
"vue3-gantt": "1.1.8-7",
|
||||||
|
"vue3-puzzle-vcode": "^1.1.7",
|
||||||
|
"xgplayer": "^3.0.19",
|
||||||
|
"xgplayer-hls.js": "^3.0.19"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@element-plus/eslint-config": "2.7.8",
|
||||||
|
"@types/lodash-es": "^4.17.12",
|
||||||
|
"@types/mockjs": "^1.0.10",
|
||||||
|
"@types/nprogress": "^0.2.3",
|
||||||
|
"@types/qs": "^6.9.15",
|
||||||
|
"@vite-pwa/assets-generator": "^0.2.4",
|
||||||
|
"@vitejs/plugin-basic-ssl": "^1.1.0",
|
||||||
|
"@vitejs/plugin-vue": "^5.1.2",
|
||||||
|
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
||||||
|
"@vue/compiler-sfc": "^3.4.37",
|
||||||
|
"@vue/eslint-config-prettier": "^9.0.0",
|
||||||
|
"@vue/eslint-config-typescript": "^13.0.0",
|
||||||
|
"adm-zip": "^0.5.15",
|
||||||
|
"autoprefixer": "^10.4.20",
|
||||||
|
"chokidar": "^3.6.0",
|
||||||
|
"dompurify": "^3.2.6",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
|
"eslint-plugin-unicorn": "^55.0.0",
|
||||||
|
"eslint-plugin-vue": "^9.27.0",
|
||||||
|
"exceljs": "^4.4.0",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
|
"less": "^4.2.0",
|
||||||
|
"lint-staged": "^15.2.9",
|
||||||
|
"moment": "^2.30.1",
|
||||||
|
"picocolors": "^1.0.1",
|
||||||
|
"plop": "^4.0.1",
|
||||||
|
"postcss": "^8.4.41",
|
||||||
|
"postcss-html": "^1.7.0",
|
||||||
|
"prettier": "^3.3.3",
|
||||||
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
|
"sass": "1.77.6",
|
||||||
|
"stylelint": "^16.8.1",
|
||||||
|
"stylelint-config-recess-order": "^5.0.1",
|
||||||
|
"stylelint-config-recommended-scss": "^14.1.0",
|
||||||
|
"stylelint-config-recommended-vue": "^1.5.0",
|
||||||
|
"table-excel": "^2.0.5",
|
||||||
|
"terser": "^5.31.6",
|
||||||
|
"typescript": "^5.5.4",
|
||||||
|
"unplugin-auto-import": "^0.18.2",
|
||||||
|
"unplugin-vue-components": "^0.27.4",
|
||||||
|
"vite": "^5.4.0",
|
||||||
|
"vite-plugin-banner": "^0.7.1",
|
||||||
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
"vite-plugin-mock": "2.9.8",
|
||||||
|
"vite-plugin-pwa": "^0.20.1",
|
||||||
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
|
"vite-plugin-unplugin": "^1.8.0",
|
||||||
|
"vite-plugin-vitebar": "^0.0.8",
|
||||||
|
"vue-global-api": "^0.4.1",
|
||||||
|
"vue-tsc": "^2.0.29"
|
||||||
|
},
|
||||||
|
"gitHooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,ts,vue}": [
|
||||||
|
"npm run lint",
|
||||||
|
"npm run lint:prettier",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"participants": [
|
||||||
|
"FlowPeakFish"
|
||||||
|
]
|
||||||
|
}
|
||||||
6
plopfile.mjs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import curdGenerator from './plop-template/curd/prompt.mjs'
|
||||||
|
|
||||||
|
const plopfile = (plop) => {
|
||||||
|
plop.setGenerator('curd', curdGenerator)
|
||||||
|
}
|
||||||
|
export default plopfile
|
||||||
16
prettier.config.mjs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export default {
|
||||||
|
printWidth: 140,
|
||||||
|
tabWidth: 2,
|
||||||
|
useTabs: false,
|
||||||
|
semi: false,
|
||||||
|
singleQuote: true,
|
||||||
|
quoteProps: 'as-needed',
|
||||||
|
jsxSingleQuote: true,
|
||||||
|
trailingComma: 'es5',
|
||||||
|
bracketSpacing: true,
|
||||||
|
bracketSameLine: false,
|
||||||
|
arrowParens: 'always',
|
||||||
|
htmlWhitespaceSensitivity: 'ignore',
|
||||||
|
vueIndentScriptAndStyle: false,
|
||||||
|
endOfLine: 'lf',
|
||||||
|
}
|
||||||
BIN
public/favicon-vab.ico
Normal file
|
After Width: | Height: | Size: 927 B |
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 927 B |
BIN
public/logo.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
1
public/shuttle-bus-smart-platform.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":"1.0.0"}
|
||||||
114
public/static/css/loading.css
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
figure {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 6.25em;
|
||||||
|
height: 6.25em;
|
||||||
|
margin: auto;
|
||||||
|
animation: rotate 2.4s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.white {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background: white;
|
||||||
|
opacity: 0;
|
||||||
|
animation: flash 2.4s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
position: absolute;
|
||||||
|
width: 1.8em;
|
||||||
|
height: 1.8em;
|
||||||
|
margin: auto;
|
||||||
|
border-radius: 100%;
|
||||||
|
transition: all 1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot:nth-child(2) {
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background: #f56c6c;
|
||||||
|
animation: dotsY 2.4s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot:nth-child(3) {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
background: #e6a23c;
|
||||||
|
animation: dotsX 2.4s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot:nth-child(4) {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: #67c23a;
|
||||||
|
animation: dotsY 2.4s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot:nth-child(5) {
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background: #4e88f3;
|
||||||
|
animation: dotsX 2.4s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
10% {
|
||||||
|
width: 6.25em;
|
||||||
|
height: 6.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
66% {
|
||||||
|
width: 1.8em;
|
||||||
|
height: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
width: 6.25em;
|
||||||
|
height: 6.25em;
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dotsY {
|
||||||
|
66% {
|
||||||
|
width: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
77% {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dotsX {
|
||||||
|
66% {
|
||||||
|
height: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
77% {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes flash {
|
||||||
|
33% {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
55% {
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
public/static/json/china.json
Normal file
1
public/static/json/world.json
Normal file
6
pwa-assets.config.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { defineConfig, minimal2023Preset as preset } from '@vite-pwa/assets-generator/config'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
preset,
|
||||||
|
images: ['public/favicon.svg'],
|
||||||
|
})
|
||||||
28
src/App.vue
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<vab-app v-show="show" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useSettingsStore } from '/@/store/modules/settings'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'App',
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const { updateTheme } = settingsStore
|
||||||
|
const show = ref(false)
|
||||||
|
|
||||||
|
const resizeContainer = () => {
|
||||||
|
let vh = window.innerHeight * 0.01
|
||||||
|
const el = ref(null)
|
||||||
|
useCssVar('--vh', el).value = `${vh}px`
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
updateTheme()
|
||||||
|
window.addEventListener('orientationchange', resizeContainer)
|
||||||
|
window.addEventListener('resize', resizeContainer)
|
||||||
|
resizeContainer()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
44
src/api/index.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import request from '/@/utils/request'
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
export const usersLogin = (data: any) => { return request({ url: '/users/login', method: 'post', data }) }
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
export const usersUserinfo = (params?: any) => { return request({ url: '/users/userinfo', method: 'get', params }) }
|
||||||
|
|
||||||
|
// 获取枚举类型
|
||||||
|
export const getEnums = (params?: any) => { return request({ url: '/enums', method: 'get', params }) }
|
||||||
|
// 图片上传
|
||||||
|
export const busUpload = `${import.meta.env.VITE_APP_BASE_URL}/file/bus/upload`
|
||||||
|
|
||||||
|
// code登录
|
||||||
|
export const usersBindLogin = (data: any) => { return request({ url: '/users/bind/login', method: 'post', data }) }
|
||||||
|
|
||||||
|
// 获取菜单列表
|
||||||
|
export function menusList(data: any) { return request({ url: '/menus/list', method: 'get', params: data }) }
|
||||||
|
// 保存菜单
|
||||||
|
export function menusSave(data: any) { return request({ url: '/menus', method: 'post', data }) }
|
||||||
|
// 分配权限
|
||||||
|
export function menusAssign(data: any) { return request({ url: '/menus/assign', method: 'post', data }) }
|
||||||
|
// 获取当前类型权限分配
|
||||||
|
export function assignments(data: any) { return request({ url: '/menus/assignments', method: 'get', params: data }) }
|
||||||
|
// 获取我的所有权限
|
||||||
|
export function assignmentsAll(data: any) { return request({ url: '/menus/assignments/all', method: 'get', params: data }) }
|
||||||
|
// 获取我的菜单
|
||||||
|
export function myMenu(data: any) { return request({ url: '/menus/my', method: 'get', params: data }) }
|
||||||
|
// 重置所有路由
|
||||||
|
export function menusReSet(data: any) { return request({ url: '/menus/reset', method: 'post', data }) }
|
||||||
|
|
||||||
|
// 刷新token
|
||||||
|
export function refreshToken(data: any) { return request({ url: '/refreshToken', method: 'post', data }) }
|
||||||
|
|
||||||
|
// stationpassenger
|
||||||
|
export function stationpassengerList(data: any) { return request({ url: '/stationpassenger/list', method: 'get', params: data }) }
|
||||||
|
|
||||||
|
export const stationpassenger = { list: stationpassengerList }
|
||||||
|
|
||||||
|
// assessment
|
||||||
|
export function assessmentPagination(data: any) { return request({ url: '/assessment/pagination', method: 'get', params: data }) }
|
||||||
|
export function assessmentDelete(data: any) { return request({ url: '/assessment', method: 'delete', data }) }
|
||||||
|
export function assessmentUpdate(data: any) { return request({ url: '/assessment', method: 'put', data }) }
|
||||||
|
export function assessmentAdd(data: any) { return request({ url: '/assessment', method: 'post', data }) }
|
||||||
BIN
src/assets/data_screen_images/bgtop.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
src/assets/data_screen_images/bottom_01.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/data_screen_images/bottom_02.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/data_screen_images/bottom_03.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/data_screen_images/bottom_04.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/login_images/background.png
Normal file
|
After Width: | Height: | Size: 315 KiB |
BIN
src/assets/login_images/left_img_1.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
src/assets/login_images/left_img_2.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
src/assets/login_images/left_img_3.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
src/assets/login_images/left_img_4.png
Normal file
|
After Width: | Height: | Size: 240 KiB |
BIN
src/assets/login_images/left_img_5.png
Normal file
|
After Width: | Height: | Size: 220 KiB |
BIN
src/assets/login_images/left_img_6.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
src/assets/login_images/leftimg.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
src/assets/qr_images/scan.png
Normal file
|
After Width: | Height: | Size: 455 KiB |