提交 24bab73e authored 作者: 王鹏飞's avatar 王鹏飞

init

上级
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
VITE_LOGIN_URL=https://login2.ezijing.com/auth/login/index
{
"globals": {
"$": true,
"$$": true,
"$computed": true,
"$customRef": true,
"$ref": true,
"$shallowRef": true,
"$toRef": true,
"EffectScope": true,
"asyncComputed": true,
"autoResetRef": true,
"computed": true,
"computedAsync": true,
"computedEager": true,
"computedInject": true,
"computedWithControl": true,
"controlledComputed": true,
"controlledRef": true,
"createApp": true,
"createEventHook": true,
"createGenericProjection": true,
"createGlobalState": true,
"createInjectionState": true,
"createProjection": true,
"createReactiveFn": true,
"createSharedComposable": true,
"createUnrefFn": true,
"customRef": true,
"debouncedRef": true,
"debouncedWatch": true,
"defineAsyncComponent": true,
"defineComponent": true,
"eagerComputed": true,
"effectScope": true,
"extendRef": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"h": true,
"ignorableWatch": true,
"inject": true,
"isDefined": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"logicAnd": true,
"logicNot": true,
"logicOr": true,
"makeDestructurable": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onBeforeMount": true,
"onBeforeRouteLeave": true,
"onBeforeRouteUpdate": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onClickOutside": true,
"onDeactivated": true,
"onErrorCaptured": true,
"onKeyStroke": true,
"onLongPress": true,
"onMounted": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onStartTyping": true,
"onUnmounted": true,
"onUpdated": true,
"pausableWatch": true,
"provide": true,
"reactify": true,
"reactifyObject": true,
"reactive": true,
"reactiveComputed": true,
"reactiveOmit": true,
"reactivePick": true,
"readonly": true,
"ref": true,
"refAutoReset": true,
"refDebounced": true,
"refDefault": true,
"refThrottled": true,
"refWithControl": true,
"resolveComponent": true,
"resolveDirective": true,
"resolveRef": true,
"resolveUnref": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"syncRef": true,
"syncRefs": true,
"templateRef": true,
"throttledRef": true,
"throttledWatch": true,
"toRaw": true,
"toReactive": true,
"toRef": true,
"toRefs": true,
"triggerRef": true,
"tryOnBeforeMount": true,
"tryOnBeforeUnmount": true,
"tryOnMounted": true,
"tryOnScopeDispose": true,
"tryOnUnmounted": true,
"unref": true,
"unrefElement": true,
"until": true,
"useAbs": true,
"useActiveElement": true,
"useArrayEvery": true,
"useArrayFilter": true,
"useArrayFind": true,
"useArrayFindIndex": true,
"useArrayJoin": true,
"useArrayMap": true,
"useArrayReduce": true,
"useArraySome": true,
"useAsyncQueue": true,
"useAsyncState": true,
"useAttrs": true,
"useAverage": true,
"useBase64": true,
"useBattery": true,
"useBluetooth": true,
"useBreakpoints": true,
"useBroadcastChannel": true,
"useBrowserLocation": true,
"useCached": true,
"useCeil": true,
"useClamp": true,
"useClipboard": true,
"useCloned": true,
"useColorMode": true,
"useConfirmDialog": true,
"useCounter": true,
"useCssModule": true,
"useCssVar": true,
"useCssVars": true,
"useCurrentElement": true,
"useCycleList": true,
"useDark": true,
"useDateFormat": true,
"useDebounce": true,
"useDebounceFn": true,
"useDebouncedRefHistory": true,
"useDeviceMotion": true,
"useDeviceOrientation": true,
"useDevicePixelRatio": true,
"useDevicesList": true,
"useDisplayMedia": true,
"useDocumentVisibility": true,
"useDraggable": true,
"useDropZone": true,
"useElementBounding": true,
"useElementByPoint": true,
"useElementHover": true,
"useElementSize": true,
"useElementVisibility": true,
"useEventBus": true,
"useEventListener": true,
"useEventSource": true,
"useEyeDropper": true,
"useFavicon": true,
"useFetch": true,
"useFileDialog": true,
"useFileSystemAccess": true,
"useFloor": true,
"useFocus": true,
"useFocusWithin": true,
"useFps": true,
"useFullscreen": true,
"useGamepad": true,
"useGeolocation": true,
"useIdle": true,
"useImage": true,
"useInfiniteScroll": true,
"useIntersectionObserver": true,
"useInterval": true,
"useIntervalFn": true,
"useKeyModifier": true,
"useLastChanged": true,
"useLink": true,
"useLocalStorage": true,
"useMagicKeys": true,
"useManualRefHistory": true,
"useMath": true,
"useMax": true,
"useMediaControls": true,
"useMediaQuery": true,
"useMemoize": true,
"useMemory": true,
"useMin": true,
"useMounted": true,
"useMouse": true,
"useMouseInElement": true,
"useMousePressed": true,
"useMutationObserver": true,
"useNavigatorLanguage": true,
"useNetwork": true,
"useNow": true,
"useObjectUrl": true,
"useOffsetPagination": true,
"useOnline": true,
"usePageLeave": true,
"useParallax": true,
"usePermission": true,
"usePointer": true,
"usePointerSwipe": true,
"usePrecision": true,
"usePreferredColorScheme": true,
"usePreferredContrast": true,
"usePreferredDark": true,
"usePreferredLanguages": true,
"usePreferredReducedMotion": true,
"useProjection": true,
"useRafFn": true,
"useRefHistory": true,
"useResizeObserver": true,
"useRound": true,
"useRoute": true,
"useRouter": true,
"useScreenOrientation": true,
"useScreenSafeArea": true,
"useScriptTag": true,
"useScroll": true,
"useScrollLock": true,
"useSessionStorage": true,
"useShare": true,
"useSlots": true,
"useSorted": true,
"useSpeechRecognition": true,
"useSpeechSynthesis": true,
"useStepper": true,
"useStorage": true,
"useStorageAsync": true,
"useStyleTag": true,
"useSum": true,
"useSupported": true,
"useSwipe": true,
"useTemplateRefsList": true,
"useTextDirection": true,
"useTextSelection": true,
"useTextareaAutosize": true,
"useThrottle": true,
"useThrottleFn": true,
"useThrottledRefHistory": true,
"useTimeAgo": true,
"useTimeout": true,
"useTimeoutFn": true,
"useTimeoutPoll": true,
"useTimestamp": true,
"useTitle": true,
"useToFixed": true,
"useToNumber": true,
"useToString": true,
"useToggle": true,
"useTransition": true,
"useTrunc": true,
"useUrlSearchParams": true,
"useUserMedia": true,
"useVModel": true,
"useVModels": true,
"useVibrate": true,
"useVirtualList": true,
"useWakeLock": true,
"useWebNotification": true,
"useWebSocket": true,
"useWebWorker": true,
"useWebWorkerFn": true,
"useWindowFocus": true,
"useWindowScroll": true,
"useWindowSize": true,
"watch": true,
"watchArray": true,
"watchAtMost": true,
"watchDebounced": true,
"watchEffect": true,
"watchIgnorable": true,
"watchOnce": true,
"watchPausable": true,
"watchPostEffect": true,
"watchSyncEffect": true,
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"whenever": true
}
}
\ No newline at end of file
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript/recommended',
'./.eslintrc-auto-import.json'
],
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off'
},
parserOptions: {
ecmaVersion: 'latest'
}
}
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}
# center-zws
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```
// Generated by 'unplugin-auto-import'
export {}
declare global {
const $$: typeof import('vue/macros')['$$']
const $: typeof import('vue/macros')['$']
const $computed: typeof import('vue/macros')['$computed']
const $customRef: typeof import('vue/macros')['$customRef']
const $ref: typeof import('vue/macros')['$ref']
const $shallowRef: typeof import('vue/macros')['$shallowRef']
const $toRef: typeof import('vue/macros')['$toRef']
const EffectScope: typeof import('vue')['EffectScope']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
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 createGenericProjection: typeof import('@vueuse/math')['createGenericProjection']
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
const createProjection: typeof import('@vueuse/math')['createProjection']
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
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 eagerComputed: typeof import('@vueuse/core')['eagerComputed']
const effectScope: typeof import('vue')['effectScope']
const extendRef: typeof import('@vueuse/core')['extendRef']
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 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 logicAnd: typeof import('@vueuse/math')['logicAnd']
const logicNot: typeof import('@vueuse/math')['logicNot']
const logicOr: typeof import('@vueuse/math')['logicOr']
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
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 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 resolveDirective: typeof import('vue')['resolveDirective']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
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 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 useAbs: typeof import('@vueuse/math')['useAbs']
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
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 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 useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: typeof import('vue')['useAttrs']
const useAverage: typeof import('@vueuse/math')['useAverage']
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 useCeil: typeof import('@vueuse/math')['useCeil']
const useClamp: typeof import('@vueuse/math')['useClamp']
const useClipboard: typeof import('@vueuse/core')['useClipboard']
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 useFloor: typeof import('@vueuse/math')['useFloor']
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 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 useMath: typeof import('@vueuse/math')['useMath']
const useMax: typeof import('@vueuse/math')['useMax']
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 useMin: typeof import('@vueuse/math')['useMin']
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 usePermission: typeof import('@vueuse/core')['usePermission']
const usePointer: typeof import('@vueuse/core')['usePointer']
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePrecision: typeof import('@vueuse/math')['usePrecision']
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 useProjection: typeof import('@vueuse/math')['useProjection']
const useRafFn: typeof import('@vueuse/core')['useRafFn']
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
const useRound: typeof import('@vueuse/math')['useRound']
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 useSum: typeof import('@vueuse/math')['useSum']
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 useToFixed: typeof import('@vueuse/math')['useToFixed']
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 useTrunc: typeof import('@vueuse/math')['useTrunc']
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 watchEffect: typeof import('vue')['watchEffect']
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
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']
}
import fs from 'node:fs'
import path from 'node:path'
import axios from 'axios'
const __dirname = path.resolve()
const domain = 'dev.ezijing.com'
const outputPath = './https'
// 生成文件夹
function genOutputDir() {
if (fs.existsSync(outputPath)) {
console.log('The path exists.')
} else {
fs.mkdir(outputPath, { recursive: true }, err => {
if (err) throw err
})
}
}
function downloadCertFile() {
genOutputDir()
axios.get(`https://ssl.godzyx.com/${domain}/${domain}.key?get=allow`, { responseType: 'stream' }).then(response => {
response.data.pipe(fs.createWriteStream(path.join(__dirname, `${outputPath}/${domain}.key`)))
})
axios.get(`https://ssl.godzyx.com/${domain}/${domain}.pem?get=allow`, { responseType: 'stream' }).then(response => {
response.data.pipe(fs.createWriteStream(path.join(__dirname, `${outputPath}/${domain}.pem`)))
})
}
downloadCertFile()
import fs from 'node:fs'
import path from 'node:path'
import chalk from 'chalk'
import OSS from 'ali-oss'
const log = console.log
const __dirname = path.resolve()
const client = new OSS({
region: 'oss-cn-beijing',
accessKeyId: 'LTAIOTuuLTaWoGJj',
accessKeySecret: 'dE5tTGm2lh35eItct2krW2DeH2lf2I',
bucket: 'webapp-pub'
})
async function uploadTarget(src, dist) {
try {
const result = await client.put(dist, path.join(__dirname, src))
log(chalk.green('上传成功', result.url))
} catch (e) {
log(chalk.red('上传失败', src))
log(e)
}
}
function generateUploadTarget(src, dist) {
fs.readdir(path.join(__dirname, src), function (err, files) {
if (err) {
log(err)
return
}
files.forEach(function (file) {
const _src = src + '/' + file
const _dist = dist + '/' + file
const stats = fs.statSync(path.join(__dirname, _src))
// 判断是否为文件
stats.isFile() && uploadTarget(_src, _dist)
// 判断是否为文件夹
stats.isDirectory() && generateUploadTarget(_src, _dist)
})
})
}
generateUploadTarget('./dist', '/website/prod/saas-lab')
/// <reference types="vite/client" />
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAucCbdPPyAp6vmnr5XObuPsctUhVLyXwqbIpgI5jWzjG7wmk8
V6z8WJKPO9KZM6D9ejtN/bbbd3j1cRiw7NSl8AUykiVHJWz9TXAflET2EpILLera
I1B2XAcBsc8dZBGGJD/LT97ZvNLYzuQOr7R1wytWH1uisAK5ClzgnSptMenXFyhw
5Xw0Lm3zoeeqYF/KMQ1McAYMGxgu6s6dxXKiA0BcgWQ31yZey0c4HhCt7T7sA/UN
ahUsxtCcSNSvdgXay5Pu/l3N88TwW2QzaCzrueILHWRFwkREhpqyrwjN3gkaa+1T
jLxzCsk/pTnPccxlFwc3YQ3hYLMl36NJ/OIpHwIDAQABAoIBACuMmaXYz6OHmroI
HNCIH9E+F0UIUyVg4/1gj9uoqKvdAx04WPphRyRo8AXhgSOWmfb/UnCqX1fqVvj2
BfzwehsEzO9wp/aBT/3IzM6RQHPoI5DXX98prSY0SlRqr4RXi3CSOFN4duoLMOOI
mlzdXUKttVpSvJixerqQPeT7HnC18NBKOydFMYPdXsgWcMXvu2BuvRClIzsjlXKM
VP00BNRY3Oje6T9yl8N051jIZh48YD3yyEAVFKPOWaJVzUU/RRPOOdTb2Y3A1bek
IbCdurdzoEQoJxkeTuColnuL1jj2mpxIBskKYhPAMV5arYS0pZ0VAtjoGGCyn7gT
l/bkTVkCgYEA6EB15hzRD2iTTIFMtDBqw0l3vJWcuWPvwFZl6zculO8Cdsvx0cDZ
VbEXByA0+CG3q47/UrVqETRhtyuVnxuKrceKU8/zib1dvvTMNjeYLKosjyG49xO6
gDx7nVBwYHmQN/iEuWTobLg1vtSNyd99WgG4cFHvqF7kIJb2W0IaGrsCgYEAzL70
VHn9BUP3CGecoU8Fnck9/7GWhvGgFU58Q/dU3Jr8g6lroeDas9zQU2tCnJN0e7cr
13thq2kQQHTYCY4J6EUtjO89sNVx4bO83xqQhobZBwZXkE5QDWIKCbiYGRLAb1+f
AAEwIEdPBgM88YFHOU5YbPTYH8TLkJfxyvMonu0CgYEArGWE3n3PdVeT1zs3O52g
8jrrpVGNF1QmWCgJ2VKJwkW0F4iFhMRYzzH3vPNcPj+Q/cjUn4lIJWMzkWrJ0mP4
ScyPUm1PApRNLPy7RRd5XtYm40wN52F+k8fRnlFiSUqTEejoZFGR8Xm/c1qFsS6y
9ofGZ6F6ewmM3uAQGGd1xxcCgYBFhjoVTW8bkJ6b3gMTy2+Oyr0gzD7fB8FiOsp7
kcrhNke0tZz01ROuq7aZ/Pwbiv6s2+ApRZ4+xGheWs7ZP8AhfQwgpUR/fZs0FwJ1
h+G3rKaZeg/V0qHgSYA7GNGdAf8SUpf9OmoLK+urkQHqyAlVbkMcjG+vKfYt3Uqf
rb4HaQKBgQCxm1oz9QrmxWKJ4eYKHSsD9UPu4QZhltBECH1btgvTwAEmwuXaCcta
RaFNhMe609sQ+YVIxa9fK0MXBiq7DG6nSLGvnLfVEYo4nGe6EvL9nQ7IFZywJjTb
/Fw4rTMwT59VSWJdv8BPznV7Gk7p17fcXM55iJxxu65r3ZuOXjQSGA==
-----END RSA PRIVATE KEY-----
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIGtjCCBZ6gAwIBAgIQDjUArTRdZ4P5wtQVmCqJGjANBgkqhkiG9w0BAQsFADBj
MQswCQYDVQQGEwJDTjE2MDQGA1UECgwtQmVpamluZyBYaW5jaGFjaGEgQ3JlZGl0
IE1hbmFnZW1lbnQgQ28uLCBMdGQuMRwwGgYDVQQDDBNYY2MgVHJ1c3QgT1YgU1NM
IENBMB4XDTIyMDgxODE0MDAzMVoXDTIzMDkxNzE0MDAzMFowgY4xCzAJBgNVBAYT
AkNOMRIwEAYDVQQIDAnljJfkuqzluIIxEjAQBgNVBAcMCeWMl+S6rOW4gjE/MD0G
A1UECgw25riF5o6n57Sr6I2G77yI5YyX5Lqs77yJ5pWZ6IKy56eR5oqA6IKh5Lu9
5pyJ6ZmQ5YWs5Y+4MRYwFAYDVQQDDA0qLmV6aWppbmcuY29tMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAucCbdPPyAp6vmnr5XObuPsctUhVLyXwqbIpg
I5jWzjG7wmk8V6z8WJKPO9KZM6D9ejtN/bbbd3j1cRiw7NSl8AUykiVHJWz9TXAf
lET2EpILLeraI1B2XAcBsc8dZBGGJD/LT97ZvNLYzuQOr7R1wytWH1uisAK5Clzg
nSptMenXFyhw5Xw0Lm3zoeeqYF/KMQ1McAYMGxgu6s6dxXKiA0BcgWQ31yZey0c4
HhCt7T7sA/UNahUsxtCcSNSvdgXay5Pu/l3N88TwW2QzaCzrueILHWRFwkREhpqy
rwjN3gkaa+1TjLxzCsk/pTnPccxlFwc3YQ3hYLMl36NJ/OIpHwIDAQABo4IDODCC
AzQwDAYDVR0TAQH/BAIwADBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8veGluY2hh
Y2hhMm92LmNybC5jZXJ0dW0ucGwveGluY2hhY2hhMm92LmNybDB5BggrBgEFBQcB
AQRtMGswLwYIKwYBBQUHMAGGI2h0dHA6Ly94aW5jaGFjaGEyb3Yub2NzcC1jZXJ0
dW0uY29tMDgGCCsGAQUFBzAChixodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwv
eGluY2hhY2hhMm92LmNlcjAfBgNVHSMEGDAWgBT6oMvCx12BtSCSByALtjtwOwkO
VTAdBgNVHQ4EFgQUEJufsd5nLNR+wqR2GsFWDn7qTn0wTAYDVR0gBEUwQzAIBgZn
gQwBAgIwNwYMKoRoAYb2dwIFARYCMCcwJQYIKwYBBQUHAgEWGWh0dHBzOi8vd3d3
LmNlcnR1bS5wbC9DUFMwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA4G
A1UdDwEB/wQEAwIFoDAlBgNVHREEHjAcgg0qLmV6aWppbmcuY29tggtlemlqaW5n
LmNvbTCCAX4GCisGAQQB1nkCBAIEggFuBIIBagFoAHYAVYHUwhaQNgFK6gubVzxT
8MDkOHhwJQgXL6OqHQcT0wwAAAGCsUFwdgAABAMARzBFAiAc51lynft4sehXTgyw
tjQ83PAVmfZ3FC55eIpIETe2hgIhANvo5ZtGQpcZQ5HSGbRLy4Y9/MbUrS8dtkwP
l7I/lRj7AHYArfe++nz/EMiLnT2cHj4YarRnKV3PsQwkyoWGNOvcgooAAAGCsUFw
TwAABAMARzBFAiATnQDMw1wykBdnTIRfSSXPj7HZfDdRRq6VaiTbSpYKYQIhAMTp
V6K0W9gtdYGdeg4j6n17S1yMODYvLJxd3kAFFNDTAHYAejKMVNi3LbYg6jjgUh7p
hBZwMhOFTTvSK8E6V6NS61IAAAGCsUFwrQAABAMARzBFAiEAwWKXKxp6DcKG7R/d
rxmPUg8uNbUcdxysrpB3gOzkIlACICWpx7/+2ulDG2EC9m4RqGcXbts3VWu/yxpE
0pAYuAP8MA0GCSqGSIb3DQEBCwUAA4IBAQCf3AdKLO8EUntMjKaRa0lncwh/pBIQ
bcQfkJBfiTpo6tnRphR+DE50oYOX1TSQRm4cDgP2JURYiTK6Z2+ljqMbUx4mNLqe
+6yG+PGCUX6rX4BsJqlRP2W7WONE/I3/3S6MRfclKmakHSyrGFi8O/JJyNqm+5z9
8tnk8c1Cn7FboJZonhX0yszHkXLLeA93xm5+Etkw0+DvRcZGiEqKQivO3CnUh1gs
LZg27a8s3dtmAAHbb2icm5jloK9Jgpx/NkGL/cCoNZ8Ng2TZkRvo6GzIp43uS332
R07dQ6rNWZkPzdxKUdNcT0v3yJJyxD1H3Rk4/bxp78giw4JGHp52Df+U
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEzzCCA7egAwIBAgIRAPJECC2rqQ2ljHLp8pqTQK4wDQYJKoZIhvcNAQELBQAw
fjELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu
QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEiMCAG
A1UEAxMZQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQTAeFw0yMjA3MDEwNzQ4NDda
Fw0yNzA2MzAwNzQ4NDdaMGMxCzAJBgNVBAYTAkNOMTYwNAYDVQQKDC1CZWlqaW5n
IFhpbmNoYWNoYSBDcmVkaXQgTWFuYWdlbWVudCBDby4sIEx0ZC4xHDAaBgNVBAMM
E1hjYyBUcnVzdCBPViBTU0wgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCz2nxSPEsMeRBdunf+KZ8CrJIj9DUWGVu05WWyES+QkMHzVHCZt8cB2Blp
qzHHqFsXSDHVzcMxDeXBbudmQwYbg5NpSwY9vJHPTl6vlAlK4UWN4+oPTgP9//RN
N6KLmK3kjZ2Zc9F3kFKVaziSwVNjVvneacoUnz6blwLLBc5QelQ8oDT0eEv0QBAY
I0T8pHDgcvWQqTBaRrOb80plDhYju464XbVhtOHvFCiBIkKnfUBnBfptWjGM8Kis
sq4r8YX35B/pioj9g1YX34MLt+5L3vvWXEb2aMiwwB1Z3bSeeiU3N8aMuSb9E9jp
zd0uhzRameU7jAIAr8uuunb7GUJ7AgMBAAGjggFhMIIBXTASBgNVHRMBAf8ECDAG
AQH/AgEAMB0GA1UdDgQWBBT6oMvCx12BtSCSByALtjtwOwkOVTAfBgNVHSMEGDAW
gBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0lBBYw
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9j
cmwuY2VydHVtLnBsL2N0bmNhLmNybDBrBggrBgEFBQcBAQRfMF0wKAYIKwYBBQUH
MAGGHGh0dHA6Ly9zdWJjYS5vY3NwLWNlcnR1bS5jb20wMQYIKwYBBQUHMAKGJWh0
dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9jdG5jYS5jZXIwOgYDVR0gBDMwMTAv
BgRVHSAAMCcwJQYIKwYBBQUHAgEWGWh0dHBzOi8vd3d3LmNlcnR1bS5wbC9DUFMw
DQYJKoZIhvcNAQELBQADggEBACWzdb59JyYc/WivKUcTEZ+H8WOTuv3AiR6rPljB
QUEB/1kxIeBK+276WLJn0nFNFuT9QMUBo/iUvNvuRQqVjouuYB0Oea6zKQpeMWry
oYtbheW+5NYAbdl9tYE4MRRK9zBLMRrQz35q+XfgMInozidhNHCvuulWk38OMM51
O73z53+R1879nnoZZznoYnrnkISVDjlhe6OSMifvIJAwSYwH2z79Y6CTVgtqTDR9
793Gr7UUmk5ydoxClxnku7voTX8iJUPcuAjElxFrhSQoxm6uXXOfItEqGvLkWAcq
IC5mcyU9VpUg1YP+KkDMh+KQM7/k+2ka1em9hO+QHvJ08UQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEtDCCA5ygAwIBAgIRAJOShUABZXFflH8oj+/JmygwDQYJKoZIhvcNAQELBQAw
PjELMAkGA1UEBhMCUEwxGzAZBgNVBAoTElVuaXpldG8gU3AuIHogby5vLjESMBAG
A1UEAxMJQ2VydHVtIENBMB4XDTA4MTAyMjEyMDczN1oXDTI3MDYxMDEwNDYzOVow
fjELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu
QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEiMCAG
A1UEAxMZQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAOP7faNyusLwyRSH9WsBTuFuQAe6bSddf/dbLbNax1Ff
q6QypmGHtm4PhtIwApf412lXoRg5XWpkecYBWaw8MUo4fNIE0kso6CBfOweizE1z
2/OuT8dW1Vqnlon686to1COGWSfPCSe8rG5ygxwwct/gounS4XR1Gb0qnnsVVAQb
10M5rVUoxeIau/TA5K44STPMdoWfOUXSpJ7yEoxR+HzkLX/1rF/rFp+xLdG6zJFC
d0wlyZA4b9vwzPuOHpdZPtVgTuYFKO1JeRNLukjbL/ly0znK/h/YNHL1tEDPMQHD
7N4RLRddH7hQ0V4Zp2neBzMoylCV+adUy1SGUEWp+UkCAwEAAaOCAWswggFnMA8G
A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAh2zcsH/yT2xc3tu5C84oQ3RnX3MFIG
A1UdIwRLMEmhQqRAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNw
LiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQYIDAQAgMA4GA1UdDwEB/wQEAwIB
BjAsBgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vY3JsLmNlcnR1bS5wbC9jYS5jcmww
aAYIKwYBBQUHAQEEXDBaMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1j
ZXJ0dW0uY29tMC4GCCsGAQUFBzAChiJodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0u
cGwvY2EuY2VyMDkGA1UdIAQyMDAwLgYEVR0gADAmMCQGCCsGAQUFBwIBFhhodHRw
Oi8vd3d3LmNlcnR1bS5wbC9DUFMwDQYJKoZIhvcNAQELBQADggEBAI3m/UBmo0yc
p6uh2oTdHDAH5tvHLeyDoVbkHTwmoaUJK+h9Yr6ydZTdCPJ/KEHkgGcCToqPwzXQ
1aknKOrS9KsGhkOujOP5iH3g271CgYACEnWy6BdxqyGVMUZCDYgQOdNv7C9C6kBT
Yr/rynieq6LVLgXqM6vp1peUQl4E7Sztapx6lX0FKgV/CF1mrWHUdqx1lpdzY70a
QVkppV4ig8OLWfqaova9ML9yHRyZhpzyhTwd9yaWLy75ArG1qVDoOPqbCl60BMDO
TjksygtbYvBNWFA0meaaLNKQ1wmB1sCqXs7+0vehukvZ1oaOGR+mBkdCcuBWCgAc
eLmNzJkEN0k=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM
MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM
MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E
jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo
ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI
ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu
Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg
AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7
HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA
uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa
TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg
xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q
CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x
O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs
6GAqm4VKQPNriiTsBhYscw==
-----END CERTIFICATE-----
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="https://zws-imgs-pub.ezijing.com/pc/base/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>综合业务管理系统-紫荆教育-清华控股旗下品牌</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "center-zws",
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --mode dev",
"build": "vue-tsc --noEmit && vite build --mode prod && npm run deploy",
"build:test": "vue-tsc --noEmit && vite build --mode test",
"build:pre": "vue-tsc --noEmit && vite build --mode pre",
"preview": "vite preview --port 4173",
"typecheck": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"deploy": "node ./deploy.js",
"cert": "node ./cert.js"
},
"dependencies": {
"axios": "^1.1.3",
"blueimp-md5": "^2.19.0",
"element-plus": "^2.2.19",
"pinia": "^2.0.17",
"qs": "^6.11.0",
"vue": "^3.2.37",
"vue-router": "^4.1.3"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.4",
"@types/blueimp-md5": "^2.18.0",
"@types/node": "^16.11.47",
"@vitejs/plugin-vue": "^3.0.1",
"@vue/eslint-config-typescript": "^11.0.0",
"@vue/tsconfig": "^0.1.3",
"ali-oss": "^6.17.1",
"chalk": "^5.1.2",
"eslint": "^8.21.0",
"eslint-plugin-vue": "^9.3.0",
"npm-run-all": "^4.1.5",
"sass": "^1.55.0",
"typescript": "~4.7.4",
"unplugin-auto-import": "^0.11.4",
"vite": "^3.0.4",
"vue-tsc": "^0.39.5"
}
}
<template>
<RouterView />
</template>
<style>
@import '@/assets/styles/base.css';
</style>
import httpRequest from '@/utils/axios'
// 获取用户信息
export function getUser() {
return httpRequest.get('/api/resource/v1/util/info')
}
// 退出登录
export function logout() {
return httpRequest.get('/api/passport/rest/logout')
}
// 获取oss token
export function getToken() {
return httpRequest.get('/api/usercenter/aliyun/assume-role')
}
// 获取oss signature
export function getSignature() {
return httpRequest.get('/api/usercenter/aliyun/get-signature')
}
// 图片上传
export function uploadFile(data: Record<string, any>) {
return httpRequest
.post('https://webapp-pub.oss-cn-beijing.aliyuncs.com', data, {
withCredentials: false,
headers: { 'Content-Type': 'multipart/form-data' }
})
.then(() => data)
}
import type { IMenuItem } from '@/types'
import { VideoCamera, Suitcase } from '@element-plus/icons-vue'
export const menus: IMenuItem[] = [
{
name: '信息管理',
path: '/base',
children: [
{
icon: VideoCamera,
name: '渠道管理',
path: '/base/channel'
},
{
icon: Suitcase,
name: '项目管理',
path: '/base/project'
}
]
},
{
name: '权限管理',
path: 'https://app.ezijing.com'
}
]
body,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
p,
blockquote,
dl,
dt,
dd,
ul,
ol,
li,
pre,
form,
fieldset,
legend,
button,
input,
textarea,
th,
td {
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 100%;
}
ul,
ol,
li {
list-style: none;
}
em,
i {
font-style: normal;
}
strong,
b {
font-weight: normal;
}
img {
border: none;
}
input,
img {
vertical-align: middle;
}
a {
color: inherit;
text-decoration: none;
}
input,
button,
select,
textarea {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
appearance: none;
border: 0;
border-radius: 0;
font: inherit;
}
textarea:focus {
outline: 0;
}
:root {
--main-color: #aa1941;
}
// styles/element/index.scss
/* 只需要重写你需要的即可 */
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
'base': #aa1941
)
)
);
// 如果只是按需导入,则可以忽略以下内容。
// 如果你想导入所有样式:
@use 'element-plus/theme-chalk/src/index.scss' as *;
import AppCard from '@/components/base/AppCard.vue'
import AppList from '@/components/base/AppList.vue'
declare module 'vue' {
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
AppCard: typeof AppCard
AppList: typeof AppList
}
}
<script setup lang="ts">
defineProps<{ title?: string }>()
</script>
<template>
<div class="app-card">
<div class="app-card-hd">
<slot name="header">
<h2 class="app-card-hd__title" v-if="title">{{ title }}</h2>
<div class="app-card-hd-aside">
<slot name="header-aside"></slot>
</div>
</slot>
</div>
<div class="app-card-bd">
<slot></slot>
</div>
</div>
</template>
<style lang="scss">
.app-card {
background: #fff;
box-shadow: 0 1px 6px 0 rgb(228 232 235 / 20%);
border-radius: 6px;
padding: 20px;
}
.app-card + .app-card {
margin-top: 20px;
}
.app-card-hd {
display: flex;
}
.app-card-hd__title {
flex: 1;
padding-left: 5px;
font-size: 18px;
font-weight: 500;
line-height: 1;
margin-bottom: 24px;
border-left: 3px solid #aa1941;
}
</style>
<script setup lang="ts">
import { Search, RefreshLeft } from '@element-plus/icons-vue'
interface IRemoteProps {
params?: any
httpRequest?: any
beforeRequest?: any
callback?: any
}
const props = withDefaults(
defineProps<{
remote?: IRemoteProps
filters?: any[]
moreFilters?: any[]
columns?: any[]
data?: any[]
hasPagination?: boolean
limit?: number
isLimit?: boolean
}>(),
{
isLimit: false,
hasPagination: true,
limit: 10,
data() {
return []
}
}
)
const filterFormRef = ref()
const loading = ref(false)
const tableRef = ref()
const dataList = ref<any[]>([])
const page = reactive({ total: 0, size: props.limit, currentPage: 1 })
const params = reactive({ ...props.remote?.params })
watch(
() => props.data,
list => {
dataList.value = list || []
},
{ immediate: true }
)
// 获取数据
const fetchList = (isReset = false) => {
/**
* @param function httpRequest api接口
* @param function beforeRequest 接口请求之前
* @param function callback 接口请求成功回调
*/
const { httpRequest, beforeRequest, callback } = props.remote || {}
if (!httpRequest) {
return
}
// 参数设置
let requestParams = { ...params }
// 翻页参数设置
if (props.hasPagination) {
requestParams.page = page.currentPage
if (props.isLimit === true) {
requestParams.limit = page.size
} else {
requestParams['per-page'] = page.size
}
}
// 接口请求之前
if (beforeRequest) {
requestParams = beforeRequest(requestParams, isReset)
}
for (const key in params) {
if (params[key] === '' || params[key] === undefined || params[key] === undefined) {
delete params[key]
}
}
loading.value = true
return (
httpRequest(requestParams)
.then((res: any) => {
const { list = [], total = 0 } = callback ? callback(res.data, requestParams) : res.data || {}
page.total = total
dataList.value = list
})
// .catch(() => {
// page.total = 0
// dataList.value = []
// })
.finally(() => {
loading.value = false
})
)
}
// 搜索
const search = () => {
page.currentPage = 1
return fetchList()
}
// 重置
const reset = () => {
// 清空筛选条件
filterFormRef.value?.resetFields()
// 初始化页码
page.currentPage = 1
// 刷新列表
return fetchList(true)
}
// 刷新
const refetch = (isForce = false) => {
return isForce ? reset() : fetchList()
}
// 页数改变
const pageSizeChange = (value: number) => {
page.currentPage = 1
page.size = value
fetchList()
}
onMounted(() => {
fetchList()
})
defineExpose({ refetch, tableRef, params })
</script>
<template>
<div class="table-list">
<div class="table-list-hd">
<!-- 筛选 -->
<div class="table-list-filter" v-if="filters && filters.length">
<el-form :inline="true" :model="params" ref="filterFormRef" @submit.prevent>
<template v-for="item in filters" :key="item.prop">
<el-form-item :label="item.label" :prop="item.prop">
<template v-if="item.slots">
<slot :name="item.slots" v-bind="{ params }"></slot>
</template>
<template v-else>
<!-- input -->
<el-input
v-model="params[item.prop]"
v-bind="item"
clearable
@change="search"
style="width: 200px"
v-if="item.type === 'input'"
/>
<!-- select -->
<el-select
v-model="params[item.prop]"
v-bind="item"
clearable
@change="search"
v-if="item.type === 'select'"
style="width: 200px"
>
<el-option
:label="option[item.labelKey] || option.label"
:value="option[item.valueKey] || option.value"
v-for="(option, index) in item.options"
:key="index"
/>
</el-select>
</template>
</el-form-item>
</template>
<el-form-item class="filter-buttons">
<el-button type="primary" :icon="Search" @click="search">搜索</el-button>
<el-button :icon="RefreshLeft" @click="reset">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="table-list-hd-aside"><slot name="header-aside" /></div>
</div>
<slot></slot>
<!-- 主体 -->
<div class="table-list-bd">
<slot name="body" v-bind="{ data: dataList }">
<el-table
:data="dataList"
v-loading="loading"
v-bind="$attrs"
style="height: 100%"
ref="tableRef"
:header-cell-style="{ background: '#EFEFEF' }"
>
<el-table-column v-bind="item || {}" v-for="item in columns" :key="item.prop">
<template #default="scope" v-if="item.slots || item.computed">
<slot :name="item.slots" v-bind="scope" v-if="item.slots"></slot>
<div v-html="item.computed(scope)" v-if="item.computed"></div>
</template>
</el-table-column>
</el-table>
</slot>
</div>
<!-- 底部 -->
<div class="table-list-ft">
<div>
<slot name="footer"></slot>
</div>
<el-pagination
class="table-list-pagination"
background
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[10, 20, 30, 50, 100]"
:page-size="page.size"
:total="page.total"
v-model:currentPage="page.currentPage"
@size-change="pageSizeChange"
@current-change="fetchList()"
:hide-on-single-page="true"
v-if="hasPagination"
>
</el-pagination>
</div>
</div>
</template>
<style lang="scss">
// .table-list {
// height: 100%;
// display: flex;
// flex-direction: column;
// box-sizing: border-box;
// }
.table-list-hd {
display: flex;
margin-bottom: 10px;
}
.table-list-filter {
flex: 1;
}
// .table-list-bd {
// flex: 1;
// }
.table-list-ft {
padding: 10px 0;
display: flex;
align-items: center;
justify-content: space-between;
}
.table-list-pagination {
text-align: right;
}
.el-table-column--selection .cell {
padding: 0 14px !important;
}
</style>
<script lang="ts" setup>
import type { UploadProps, UploadUserFile } from 'element-plus'
import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import md5 from 'blueimp-md5'
import { getSignature } from '@/api/base'
interface Props {
modelValue: string | { name: string; url: string }[]
prefix?: string
size?: number
limit?: number
beforeUpload?: (file: any) => void
}
const props = withDefaults(defineProps<Props>(), {
prefix: 'upload/saas-lab/'
})
const emit = defineEmits(['update:modelValue', 'success'])
const uploadData = ref()
const fileList = ref<UploadUserFile[]>([])
watchEffect(() => {
fileList.value = Array.isArray(props.modelValue) ? props.modelValue.map(item => ({ ...item })) : []
})
const showFileList = computed(() => {
return Array.isArray(props.modelValue)
})
// 上传之前
const handleBeforeUpload = async (file: any) => {
if (props.limit && fileList.value.length >= props.limit && props.limit > 1) {
ElMessage.error(`只能上传${props.limit}个文件`)
return false
}
if (props.size && file.size > props.size) {
ElMessage.error(`文件大小不能超过${props.size / 1024 / 1024}M`)
return false
}
const fileName = file.name
const key = props.prefix + md5(fileName + new Date().getTime()) + fileName.substr(fileName.lastIndexOf('.'))
const response: Record<string, any> = await getSignature()
uploadData.value = {
key,
OSSAccessKeyId: response.accessid,
policy: response.policy,
signature: response.signature,
success_action_status: '200',
url: `${response.host}/${key}`
}
file.url = `${response.host}/${key}`
if (props.beforeUpload) {
return props.beforeUpload(file)
}
}
// 上传成功
const handleSuccess = (response: any, file: any, files: any) => {
if (!files.every((item: any) => item.status === 'success')) return
if (showFileList.value) {
if (props.limit && props.limit === 1) {
const last = files[files.length - 1]
emit('update:modelValue', [
{
name: last.name,
url: last.url || last.raw?.url,
size: last.size || last.raw?.size,
type: last.type || last.raw?.type
}
])
} else {
emit(
'update:modelValue',
files.map((item: any) => {
return {
name: item.name,
url: item.url || item.raw?.url,
size: item.size || item.raw?.size,
type: item.type || item.raw?.type
}
})
)
}
} else {
emit('update:modelValue', file.raw.url)
}
emit('success', file, files)
}
// 上传限制
const handleExceed: UploadProps['onExceed'] = () => {
ElMessage.warning('文件超出个数限制')
}
// 删除
const handleRemove: UploadProps['onRemove'] = (file, files) => {
if (showFileList.value) {
emit(
'update:modelValue',
files.map((item: any) => {
return { name: item.name, url: item.url || item.raw.url }
})
)
} else {
emit('update:modelValue', '')
}
}
// 预览
const handlePreview: UploadProps['onPreview'] = uploadFile => {
window.open(uploadFile.url)
}
</script>
<template>
<el-upload
action="https://webapp-pub.oss-cn-beijing.aliyuncs.com"
:data="uploadData"
:show-file-list="showFileList"
:before-upload="handleBeforeUpload"
:on-exceed="handleExceed"
:on-remove="handleRemove"
:on-preview="handlePreview"
:on-success="handleSuccess"
:file-list="fileList"
class="uploader">
<slot>
<template v-if="showFileList">
<template v-if="$attrs['list-type'] === 'picture-card'">
<el-icon><Plus /></el-icon>
</template>
<template v-else>
<el-button type="primary" plain round>本地文件</el-button>
</template>
</template>
<div class="avatar-uploader" v-else>
<el-image :src="(modelValue as string)" fit="contain" v-if="modelValue" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</div>
</slot>
<template #tip>
<div class="el-upload__tip"><slot name="tip"></slot></div>
</template>
</el-upload>
</template>
<style lang="scss">
.uploader {
flex: 1;
overflow: hidden;
}
.avatar-uploader {
width: 178px;
height: 178px;
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
.el-image {
width: 100%;
height: 100%;
}
}
.avatar-uploader:hover {
border-color: var(--el-color-primary);
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 100%;
height: 100%;
text-align: center;
}
</style>
<script lang="ts">
const DEFAULT_OPTIONS = {
controls: true,
autoplay: false,
controlBar: {
pictureInPictureToggle: false
},
// fluid: true,
responsive: true
// playbackRates: [0.5, 1, 1.5, 2]
}
const DEFAULT_EVENTS = [
'abort',
'canplay',
'canplaythrough',
'durationchange',
'emptied',
'ended',
'error',
'loadeddata',
'loadedmetadata',
'pause',
'play',
'playing',
'progress',
'ratechange',
'resize',
'seeked',
'seeking',
'stalled',
'suspend',
'timeupdate',
'volumechange',
'waiting'
]
</script>
<script setup lang="ts">
import videojs from 'video.js'
import type { VideoJsPlayerOptions, VideoJsPlayer } from 'video.js'
import 'video.js/dist/video-js.css'
interface Props {
src?: string | { src: string; type?: string }
options?: VideoJsPlayerOptions
}
const props = defineProps<Props>()
const emit = defineEmits(['ready', ...DEFAULT_EVENTS])
let player = $ref<VideoJsPlayer | null>()
const videoRef = $ref<HTMLVideoElement>()
const videoOptions = $computed<VideoJsPlayerOptions>(() => {
return Object.assign({}, DEFAULT_OPTIONS, props.options)
})
watch(
() => props.src,
src => {
src && changeSrc(src)
},
{ deep: true, immediate: true }
)
// 初始化播放器
function initPlayer() {
if (!videoRef) return
if (player) {
player.dispose()
player = null
}
player = videojs(videoRef, videoOptions, function onPlayerReady() {
props.src && changeSrc(props.src)
// 注册事件
DEFAULT_EVENTS.forEach(eventName => {
this.on(eventName, (...arg) => {
// console.log(eventName, ...arg)
emit(eventName, ...arg)
})
})
emit('ready', this)
})
return player
}
function changeSrc(src: string | { src: string; type?: string }) {
if (!player || !src) return
// if (!player.paused()) {
// console.log(2)
// player.pause()
// }
player.src(src)
// player.load()
// player.play()
}
onMounted(() => {
initPlayer()
})
onUnmounted(() => {
player && player.dispose()
})
</script>
<template>
<video class="video-js vjs-default-skin vjs-big-play-centered" ref="videoRef"></video>
</template>
<style>
.video-js {
font-size: 12px;
}
</style>
<script lang="ts">
export default {
name: 'AppAside'
}
</script>
<script setup lang="ts">
import { menus } from '@/assets/menus'
import type { IMenuItem } from '@/types'
const router = useRouter()
const route = useRoute()
const menuList = $computed<IMenuItem[]>(() => {
const found = menus.find(item => route.fullPath.includes(item.path))
return found?.children || []
})
const defaultActive = computed(() => {
// 扁平菜单
const flatMenuList: IMenuItem[] = menuList.reduce((result: IMenuItem[], item) => {
result.push(item)
if (item.children) {
result = result.concat(item.children)
}
return result
}, [])
const found = flatMenuList.reverse().find(item => {
return route.path.includes(item.path)
})
return found ? found.path : '/'
})
function isUrl(path: string) {
return /^https?:\/\//.test(path)
}
function handleClick(path: string) {
if (isUrl(path)) {
window.open(path)
} else {
router.push(path)
}
}
</script>
<template>
<aside class="app-aside">
<nav class="nav">
<el-menu :default-active="defaultActive" class="app-menu">
<template v-for="item in menuList" :key="item.path">
<el-sub-menu :index="item.path" v-permission="item.tag" v-if="item.children">
<template #title>
<el-icon><component :is="item.icon"></component></el-icon>{{ item.name }}
</template>
<el-menu-item
:index="subitem.path"
v-for="subitem in item.children"
:key="subitem.path"
v-permission="subitem.tag"
@click="handleClick(subitem.path)"
>
{{ subitem.name }}
</el-menu-item>
</el-sub-menu>
<el-menu-item :index="item.path" v-permission="item.tag" @click="handleClick(item.path)" v-else>
<el-icon><component :is="item.icon"></component></el-icon>{{ item.name }}
</el-menu-item>
</template>
</el-menu>
</nav>
</aside>
</template>
<style lang="scss">
.app-aside {
width: 200px;
background: #2e3036;
border-right: 1px solid rgba(0, 0, 0, 0.12);
overflow-x: hidden;
overflow-y: auto;
flex: 0 0 200px;
box-sizing: content-box;
}
.nav {
position: fixed;
width: 200px;
margin: 20px 0;
--el-menu-text-color: #cecece;
--el-menu-bg-color: #2e3036;
--el-menu-active-color: #fff;
--el-menu-active-bg-color: #4b4c50;
--el-menu-hover-color: #fff;
--el-menu-hover-bg-color: #4b4c50;
.el-menu {
border-right: 0;
i {
margin-right: 14px;
font-size: 24px;
}
.el-icon-arrow-down {
margin-right: 0;
font-size: 16px;
}
}
.el-menu-item {
&.is-active {
background: var(--el-menu-active-bg-color);
}
}
// .el-menu-item {
// display: flex;
// align-items: center;
// margin: 0 16px;
// font-size: 16px;
// border-radius: 8px;
// }
// .el-submenu .el-menu-item {
// min-width: auto;
// padding-left: 58px !important;
// }
// .el-submenu__title {
// display: flex;
// align-items: center;
// margin: 0 16px;
// font-size: 16px;
// border-radius: 8px;
// }
}
</style>
<template>
<div class="app-breadcrumb" v-if="routes.length">
<el-breadcrumb>
<el-breadcrumb-item v-for="route in routes" :key="route.path">
<router-link :to="route.path">{{ route.meta.title }}</router-link>
</el-breadcrumb-item>
</el-breadcrumb>
</div>
</template>
<script>
export default {
name: 'AppBreadcrumb',
computed: {
routes() {
return this.$route.matched.filter(route => route.meta.title)
}
}
}
</script>
<style lang="scss">
.app-breadcrumb {
padding: 18px 0 32px;
.el-breadcrumb {
font-size: 20px;
font-weight: 400;
line-height: 1;
}
.el-breadcrumb__inner a {
font-weight: normal;
color: #5b91fd;
}
.router-link-active {
color: #1a1b1c;
}
}
</style>
<script lang="ts">
export default { name: 'AppHeader' }
</script>
<script setup lang="ts">
import { menus } from '@/assets/menus'
import { useUserStore } from '@/stores/user'
import type { IMenuItem } from '@/types'
withDefaults(defineProps<{ hasTitle?: boolean }>(), {
hasTitle: true
})
const route = useRoute()
const userStore = useUserStore()
const userInfo = userStore.user
const logout = async () => {
await userStore.logout()
location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}`
}
function genNavClassName(data: IMenuItem) {
return route.fullPath.includes(data.path) ? 'is-active' : ''
}
</script>
<template>
<header class="app-header">
<div class="app-header-left">
<div class="logo">
<router-link to="/"><img src="https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo-white.svg" /></router-link>
</div>
<h1 class="app-name">ZWS系统</h1>
</div>
<div class="app-header-nav">
<div
class="app-header-nav-item"
v-for="(item, index) in menus"
:key="index"
:class="genNavClassName(item)"
v-permission="item.tag">
<router-link :to="item.path">{{ item.name }}</router-link>
</div>
</div>
<div class="app-header-right">
<el-dropdown v-if="userInfo">
<div class="avatar">
<img :src="userInfo.avatar || 'https://webapp-pub.ezijing.com/center_resource/avatar.png'" />
</div>
<template #dropdown>
<el-dropdown-menu style="width: 280px">
<div class="app-header-user">
<div class="app-header-user-avatar">
<img :src="userInfo.avatar || 'https://webapp-pub.ezijing.com/center_resource/avatar.png'" />
</div>
<div class="app-header-user-main">
<h3>{{ userInfo.name }}</h3>
<p>{{ userInfo.email || userInfo.mobile }}</p>
</div>
<div class="app-header-user-buttons">
<el-button round @click="logout">退出登录</el-button>
</div>
</div>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</header>
</template>
<style lang="scss">
.app-header {
position: sticky;
top: 0;
z-index: 2001;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
height: 70px;
background-color: var(--main-color);
color: #fff;
.logo {
width: 120px;
}
}
.app-header-left {
display: flex;
align-items: center;
.app-name {
margin-left: 20px;
padding: 0 15px;
line-height: 1;
border-left: 2px solid #fff;
font-size: 18px;
}
}
.app-header-nav {
flex: 1;
display: flex;
align-items: center;
// justify-content: center;
}
.app-header-nav-item {
height: 45px;
a {
display: inline-block;
font-size: 16px;
line-height: 45px;
padding: 0 18px;
}
&:hover,
&.is-active {
background: rgba(144, 6, 44, 0.39);
color: #fff;
}
}
.app-header-right {
display: flex;
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
overflow: hidden;
}
&:hover {
background-color: rgba(60, 64, 67, 0.08);
}
}
}
.app-header-user {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 16px;
}
.app-header-user-avatar {
margin-bottom: 6px;
width: 80px;
height: 80px;
border-radius: 50%;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.app-header-user-main {
h3 {
color: #202124;
font: 500 16px/22px Helvetica, Arial, sans-serif;
letter-spacing: 0.29px;
margin: 0;
text-align: center;
text-overflow: ellipsis;
overflow: hidden;
}
p {
color: #5f6368;
font: 400 14px/19px Helvetica, Arial, sans-serif;
letter-spacing: normal;
text-align: center;
text-overflow: ellipsis;
overflow: hidden;
}
}
.app-header-user-buttons {
padding-top: 16px;
}
</style>
<script lang="ts">
export default { name: 'AppLayout' }
</script>
<script setup lang="ts">
import AppHeader from './Header.vue'
import AppAside from './Aside.vue'
import AppMain from './Main.vue'
withDefaults(defineProps<{ sidebar?: boolean; hasTitle?: boolean }>(), {
sidebar: true,
hasTitle: true
})
</script>
<template>
<div class="app-layout">
<AppHeader :hasTitle="hasTitle"></AppHeader>
<div class="app-layout-container">
<AppAside v-if="sidebar"></AppAside>
<AppMain></AppMain>
</div>
</div>
</template>
<style lang="scss">
.app-layout {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #f8f8f8;
}
.app-layout-container {
flex: 1;
display: flex;
}
</style>
<script lang="ts">
export default { name: 'AppMain' }
</script>
<script setup lang="ts">
import AppBreadcrumb from './Breadcrumb.vue'
withDefaults(defineProps<{ hasBreadcrumb?: boolean }>(), {
hasBreadcrumb: true
})
</script>
<template>
<section class="app-main">
<div class="app-main-inner">
<div class="app-main-header">
<AppBreadcrumb v-if="hasBreadcrumb"></AppBreadcrumb>
</div>
<div class="app-main-container">
<RouterView></RouterView>
</div>
</div>
</section>
</template>
<style>
.manual-btn {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 50%;
right: 0;
width: 60px;
height: 60px;
background: rgba(255, 255, 255, 1);
box-shadow: 1px 3px 12px rgba(0, 0, 0, 0.11);
border-radius: 50%;
z-index: 999999;
}
.app-main {
position: relative;
flex: 1;
padding: 20px;
overflow: hidden;
}
.app-main-inner {
margin: 0 auto;
}
.app-main-container::after {
content: '';
display: table;
clear: both;
}
.el-form--label-top .el-form-item__label {
padding-bottom: 0;
}
</style>
<script setup lang="ts">
import Editor from '@tinymce/tinymce-vue'
import md5 from 'blueimp-md5'
import { getSignature, uploadFile } from '@/api/base'
const props = defineProps({
height: {
type: Number,
default: 600
}
})
const ImageUploadHandler = (blobInfo: any) =>
new Promise((resolve, reject) => {
const file = blobInfo.blob()
getSignature()
.then((response: any) => {
const prefix = 'upload/admin/'
const key = prefix + md5(file.name + new Date().getTime()) + file.name.substr(file.name.lastIndexOf('.'))
const { accessid, policy, signature, host } = response
const params = {
key,
OSSAccessKeyId: accessid,
policy,
signature,
success_action_status: '200',
file,
url: `${host}/${key}`
}
uploadFile(params)
.then((res: any) => {
resolve(res.url)
})
.catch(() => {
reject('上传失败')
})
})
.catch(() => {
reject('获取Signature失败')
})
})
const init = {
language: 'zh-Hans',
height: props.height,
menubar: false,
statusbar: false,
plugins: 'table charmap fullscreen lists link code preview quickbars',
toolbar:
'undo redo | fontsizeselect lineheight bold italic underline strikethrough forecolor backcolor | link quickimage image media table | align hangingindent indent outdent numlist bullist | charmap blockquote hr fullscreen | code preview',
// font_formats:
// '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Times New Roman',
fontsize_formats: '8px 10px 12px 14px 15px 16px 17px 18px 20px 24px',
lineheight_formats: '0.5 1 1.2 1.5 2',
images_upload_handler: ImageUploadHandler,
automatic_uploads: true,
quickbars_insert_toolbar: false,
// style_formats: [{ title: '悬挂缩进', block: 'p', styles: { textIndent: '-2em', paddingLeft: '2em' } }],
content_style: 'img {max-width:100%;}'
}
</script>
<template>
<editor :init="init" v-bind="$attrs" style="width: 100%" />
</template>
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import '@/assets/styles/element/index.scss'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import AppCard from '@/components/base/AppCard.vue'
import AppList from '@/components/base/AppList.vue'
import AppUpload from '@/components/base/AppUpload.vue'
import modules from './modules'
import { permissionDirective } from '@/utils/permission'
const app = createApp(App)
// 注册公共组件
app.component('AppCard', AppCard).component('AppList', AppList).component('AppUpload', AppUpload)
app.directive('permission', permissionDirective)
// 注册模块
modules({ router })
app.use(createPinia())
app.use(router)
app.use(ElementPlus, { locale: zhCn })
app.mount('#app')
import httpRequest from '@/utils/axios'
// 获取封面列表
export function getCoverList(params?: { type?: string; page?: number; ['per-page']?: number }) {
return httpRequest.get('/api/resource/v1/backend/cover/list', { params })
}
import type { RouteRecordRaw } from 'vue-router'
import AppLayout from '@/components/layout/Index.vue'
export const routes: Array<RouteRecordRaw> = [
{
path: '/base/channel',
component: AppLayout,
children: [{ path: '', component: () => import('./views/Index.vue') }]
}
]
<script setup lang="ts"></script>
<template>
<AppCard title="渠道管理"></AppCard>
</template>
import httpRequest from '@/utils/axios'
// 获取封面列表
export function getCoverList(params?: { type?: string; page?: number; ['per-page']?: number }) {
return httpRequest.get('/api/resource/v1/backend/cover/list', { params })
}
import type { RouteRecordRaw } from 'vue-router'
import AppLayout from '@/components/layout/Index.vue'
export const routes: Array<RouteRecordRaw> = [
{
path: '/base/project',
component: AppLayout,
children: [{ path: '', component: () => import('./views/Index.vue') }]
}
]
<script setup lang="ts"></script>
<template>
<AppCard title="渠道管理"></AppCard>
</template>
const routes = [
{
path: '/401',
component: () => import('./views/401.vue')
}
]
export { routes }
<script setup lang="ts">
const loginUrl = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.origin)}`
</script>
<template>
<div class="container">
<svg width="251" height="294">
<g fill="none" fill-rule="evenodd">
<path
d="M0 129.023v-2.084C0 58.364 55.591 2.774 124.165 2.774h2.085c68.574 0 124.165 55.59 124.165 124.165v2.084c0 68.575-55.59 124.166-124.165 124.166h-2.085C55.591 253.189 0 197.598 0 129.023"
fill="#E4EBF7"
></path>
<path d="M41.417 132.92a8.231 8.231 0 1 1-16.38-1.65 8.231 8.231 0 0 1 16.38 1.65" fill="#FFF"></path>
<path d="M38.652 136.36l10.425 5.91M49.989 148.505l-12.58 10.73" stroke="#FFF" stroke-width="2"></path>
<path
d="M41.536 161.28a5.636 5.636 0 1 1-11.216-1.13 5.636 5.636 0 0 1 11.216 1.13M59.154 145.261a5.677 5.677 0 1 1-11.297-1.138 5.677 5.677 0 0 1 11.297 1.138M100.36 29.516l29.66-.013a4.562 4.562 0 1 0-.004-9.126l-29.66.013a4.563 4.563 0 0 0 .005 9.126M111.705 47.754l29.659-.013a4.563 4.563 0 1 0-.004-9.126l-29.66.013a4.563 4.563 0 1 0 .005 9.126"
fill="#FFF"
></path>
<path
d="M114.066 29.503V29.5l15.698-.007a4.563 4.563 0 1 0 .004 9.126l-15.698.007v-.002a4.562 4.562 0 0 0-.004-9.122M185.405 137.723c-.55 5.455-5.418 9.432-10.873 8.882-5.456-.55-9.432-5.418-8.882-10.873.55-5.455 5.418-9.432 10.873-8.882 5.455.55 9.432 5.418 8.882 10.873"
fill="#FFF"
></path>
<path d="M180.17 143.772l12.572 7.129M193.841 158.42L178.67 171.36" stroke="#FFF" stroke-width="2"></path>
<path
d="M185.55 171.926a6.798 6.798 0 1 1-13.528-1.363 6.798 6.798 0 0 1 13.527 1.363M204.12 155.285a6.848 6.848 0 1 1-13.627-1.375 6.848 6.848 0 0 1 13.626 1.375"
fill="#FFF"
></path>
<path
d="M152.988 194.074a2.21 2.21 0 1 1-4.42 0 2.21 2.21 0 0 1 4.42 0zM225.931 118.217a2.21 2.21 0 1 1-4.421 0 2.21 2.21 0 0 1 4.421 0zM217.09 153.051a2.21 2.21 0 1 1-4.421 0 2.21 2.21 0 0 1 4.42 0zM177.84 109.842a2.21 2.21 0 1 1-4.422 0 2.21 2.21 0 0 1 4.421 0zM196.114 94.454a2.21 2.21 0 1 1-4.421 0 2.21 2.21 0 0 1 4.421 0zM202.844 182.523a2.21 2.21 0 1 1-4.42 0 2.21 2.21 0 0 1 4.42 0z"
stroke="#FFF"
stroke-width="2"
></path>
<path
stroke="#FFF"
stroke-width="2"
d="M215.125 155.262l-1.902 20.075-10.87 5.958M174.601 176.636l-6.322 9.761H156.98l-4.484 6.449M175.874 127.28V111.56M221.51 119.404l-12.77 7.859-15.228-7.86V96.668"
></path>
<path
d="M180.68 29.32C180.68 13.128 193.806 0 210 0c16.193 0 29.32 13.127 29.32 29.32 0 16.194-13.127 29.322-29.32 29.322-16.193 0-29.32-13.128-29.32-29.321"
fill="#A26EF4"
></path>
<path
d="M221.45 41.706l-21.563-.125a1.744 1.744 0 0 1-1.734-1.754l.071-12.23a1.744 1.744 0 0 1 1.754-1.734l21.562.125c.964.006 1.74.791 1.735 1.755l-.071 12.229a1.744 1.744 0 0 1-1.754 1.734"
fill="#FFF"
></path>
<path
d="M215.106 29.192c-.015 2.577-2.049 4.654-4.543 4.64-2.494-.014-4.504-2.115-4.489-4.693l.04-6.925c.016-2.577 2.05-4.654 4.543-4.64 2.494.015 4.504 2.116 4.49 4.693l-.04 6.925zm-4.53-14.074a6.877 6.877 0 0 0-6.916 6.837l-.043 7.368a6.877 6.877 0 0 0 13.754.08l.042-7.368a6.878 6.878 0 0 0-6.837-6.917zM167.566 68.367h-3.93a4.73 4.73 0 0 1-4.717-4.717 4.73 4.73 0 0 1 4.717-4.717h3.93a4.73 4.73 0 0 1 4.717 4.717 4.73 4.73 0 0 1-4.717 4.717"
fill="#FFF"
></path>
<path
d="M168.214 248.838a6.611 6.611 0 0 1-6.61-6.611v-66.108a6.611 6.611 0 0 1 13.221 0v66.108a6.611 6.611 0 0 1-6.61 6.61"
fill="#5BA02E"
></path>
<path
d="M176.147 248.176a6.611 6.611 0 0 1-6.61-6.61v-33.054a6.611 6.611 0 1 1 13.221 0v33.053a6.611 6.611 0 0 1-6.61 6.611"
fill="#92C110"
></path>
<path
d="M185.994 293.89h-27.376a3.17 3.17 0 0 1-3.17-3.17v-45.887a3.17 3.17 0 0 1 3.17-3.17h27.376a3.17 3.17 0 0 1 3.17 3.17v45.886a3.17 3.17 0 0 1-3.17 3.17"
fill="#F2D7AD"
></path>
<path
d="M81.972 147.673s6.377-.927 17.566-1.28c11.729-.371 17.57 1.086 17.57 1.086s3.697-3.855.968-8.424c1.278-12.077 5.982-32.827.335-48.273-1.116-1.339-3.743-1.512-7.536-.62-1.337.315-7.147-.149-7.983-.1l-15.311-.347s-3.487-.17-8.035-.508c-1.512-.113-4.227-1.683-5.458-.338-.406.443-2.425 5.669-1.97 16.077l8.635 35.642s-3.141 3.61 1.219 7.085"
fill="#FFF"
></path>
<path
d="M75.768 73.325l-.9-6.397 11.982-6.52s7.302-.118 8.038 1.205c.737 1.324-5.616.993-5.616.993s-1.836 1.388-2.615 2.5c-1.654 2.363-.986 6.471-8.318 5.986-1.708.284-2.57 2.233-2.57 2.233"
fill="#FFC6A0"
></path>
<path
d="M52.44 77.672s14.217 9.406 24.973 14.444c1.061.497-2.094 16.183-11.892 11.811-7.436-3.318-20.162-8.44-21.482-14.496-.71-3.258 2.543-7.643 8.401-11.76M141.862 80.113s-6.693 2.999-13.844 6.876c-3.894 2.11-10.137 4.704-12.33 7.988-6.224 9.314 3.536 11.22 12.947 7.503 6.71-2.651 28.999-12.127 13.227-22.367"
fill="#FFB594"
></path>
<path
d="M76.166 66.36l3.06 3.881s-2.783 2.67-6.31 5.747c-7.103 6.195-12.803 14.296-15.995 16.44-3.966 2.662-9.754 3.314-12.177-.118-3.553-5.032.464-14.628 31.422-25.95"
fill="#FFC6A0"
></path>
<path
d="M64.674 85.116s-2.34 8.413-8.912 14.447c.652.548 18.586 10.51 22.144 10.056 5.238-.669 6.417-18.968 1.145-20.531-.702-.208-5.901-1.286-8.853-2.167-.87-.26-1.611-1.71-3.545-.936l-1.98-.869zM128.362 85.826s5.318 1.956 7.325 13.734c-.546.274-17.55 12.35-21.829 7.805-6.534-6.94-.766-17.393 4.275-18.61 4.646-1.121 5.03-1.37 10.23-2.929"
fill="#FFF"
></path>
<path
d="M78.18 94.656s.911 7.41-4.914 13.078"
stroke="#E4EBF7"
stroke-width="1.051"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M87.397 94.68s3.124 2.572 10.263 2.572c7.14 0 9.074-3.437 9.074-3.437"
stroke="#E4EBF7"
stroke-width=".932"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M117.184 68.639l-6.781-6.177s-5.355-4.314-9.223-.893c-3.867 3.422 4.463 2.083 5.653 4.165 1.19 2.082.848 1.143-2.083.446-5.603-1.331-2.082.893 2.975 5.355 2.091 1.845 6.992.955 6.992.955l2.467-3.851z"
fill="#FFC6A0"
></path>
<path
d="M105.282 91.315l-.297-10.937-15.918-.027-.53 10.45c-.026.403.17.788.515.999 2.049 1.251 9.387 5.093 15.799.424.287-.21.443-.554.431-.91"
fill="#FFB594"
></path>
<path
d="M107.573 74.24c.817-1.147.982-9.118 1.015-11.928a1.046 1.046 0 0 0-.965-1.055l-4.62-.365c-7.71-1.044-17.071.624-18.253 6.346-5.482 5.813-.421 13.244-.421 13.244s1.963 3.566 4.305 6.791c.756 1.041.398-3.731 3.04-5.929 5.524-4.594 15.899-7.103 15.899-7.103"
fill="#5C2552"
></path>
<path
d="M88.426 83.206s2.685 6.202 11.602 6.522c7.82.28 8.973-7.008 7.434-17.505l-.909-5.483c-6.118-2.897-15.478.54-15.478.54s-.576 2.044-.19 5.504c-2.276 2.066-1.824 5.618-1.824 5.618s-.905-1.922-1.98-2.321c-.86-.32-1.897.089-2.322 1.98-1.04 4.632 3.667 5.145 3.667 5.145"
fill="#FFC6A0"
></path>
<path
stroke="#DB836E"
stroke-width="1.145"
stroke-linecap="round"
stroke-linejoin="round"
d="M100.843 77.099l1.701-.928-1.015-4.324.674-1.406"
></path>
<path
d="M105.546 74.092c-.022.713-.452 1.279-.96 1.263-.51-.016-.904-.607-.882-1.32.021-.713.452-1.278.96-1.263.51.016.904.607.882 1.32M97.592 74.349c-.022.713-.452 1.278-.961 1.263-.509-.016-.904-.607-.882-1.32.022-.713.452-1.279.961-1.263.51.016.904.606.882 1.32"
fill="#552950"
></path>
<path
d="M91.132 86.786s5.269 4.957 12.679 2.327"
stroke="#DB836E"
stroke-width="1.145"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M99.776 81.903s-3.592.232-1.44-2.79c1.59-1.496 4.897-.46 4.897-.46s1.156 3.906-3.457 3.25"
fill="#DB836E"
></path>
<path
d="M102.88 70.6s2.483.84 3.402.715M93.883 71.975s2.492-1.144 4.778-1.073"
stroke="#5C2552"
stroke-width="1.526"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M86.32 77.374s.961.879 1.458 2.106c-.377.48-1.033 1.152-.236 1.809M99.337 83.719s1.911.151 2.509-.254"
stroke="#DB836E"
stroke-width="1.145"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M87.782 115.821l15.73-3.012M100.165 115.821l10.04-2.008"
stroke="#E4EBF7"
stroke-width="1.051"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M66.508 86.763s-1.598 8.83-6.697 14.078"
stroke="#E4EBF7"
stroke-width="1.114"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M128.31 87.934s3.013 4.121 4.06 11.785"
stroke="#E4EBF7"
stroke-width="1.051"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M64.09 84.816s-6.03 9.912-13.607 9.903"
stroke="#DB836E"
stroke-width=".795"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M112.366 65.909l-.142 5.32s5.993 4.472 11.945 9.202c4.482 3.562 8.888 7.455 10.985 8.662 4.804 2.766 8.9 3.355 11.076 1.808 4.071-2.894 4.373-9.878-8.136-15.263-4.271-1.838-16.144-6.36-25.728-9.73"
fill="#FFC6A0"
></path>
<path
d="M130.532 85.488s4.588 5.757 11.619 6.214"
stroke="#DB836E"
stroke-width=".75"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M121.708 105.73s-.393 8.564-1.34 13.612"
stroke="#E4EBF7"
stroke-width="1.051"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M115.784 161.512s-3.57-1.488-2.678-7.14"
stroke="#648BD8"
stroke-width="1.051"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M101.52 290.246s4.326 2.057 7.408 1.03c2.842-.948 4.564.673 7.132 1.186 2.57.514 6.925 1.108 11.772-1.269-.104-5.551-6.939-4.01-12.048-6.763-2.582-1.39-3.812-4.757-3.625-8.863h-9.471s-1.402 10.596-1.169 14.68"
fill="#CBD1D1"
></path>
<path
d="M101.496 290.073s2.447 1.281 6.809.658c3.081-.44 3.74.485 7.479 1.039 3.739.554 10.802-.07 11.91-.9.415 1.108-.347 2.077-.347 2.077s-1.523.608-4.847.831c-2.045.137-5.843.293-7.663-.507-1.8-1.385-5.286-1.917-5.77-.243-3.947.958-7.41-.288-7.41-.288l-.16-2.667z"
fill="#2B0849"
></path>
<path d="M108.824 276.19h3.116s-.103 6.751 4.57 8.62c-4.673.624-8.62-2.32-7.686-8.62" fill="#A4AABA"></path>
<path
d="M57.65 272.52s-2.122 7.47-4.518 12.396c-1.811 3.724-4.255 7.548 5.505 7.548 6.698 0 9.02-.483 7.479-6.648-1.541-6.164.268-13.296.268-13.296H57.65z"
fill="#CBD1D1"
></path>
<path
d="M51.54 290.04s2.111 1.178 6.682 1.178c6.128 0 8.31-1.662 8.31-1.662s.605 1.122-.624 2.18c-1 .862-3.624 1.603-7.444 1.559-4.177-.049-5.876-.57-6.786-1.177-.831-.554-.692-1.593-.138-2.078"
fill="#2B0849"
></path>
<path
d="M58.533 274.438s.034 1.529-.315 2.95c-.352 1.431-1.087 3.127-1.139 4.17-.058 1.16 4.57 1.592 5.194.035.623-1.559 1.303-6.475 1.927-7.306.622-.831-4.94-2.135-5.667.15"
fill="#A4AABA"
></path>
<path
d="M100.885 277.015l13.306.092s1.291-54.228 1.843-64.056c.552-9.828 3.756-43.13.997-62.788l-12.48-.64-22.725.776s-.433 3.944-1.19 9.921c-.062.493-.677.838-.744 1.358-.075.582.42 1.347.318 1.956-2.35 14.003-6.343 32.926-8.697 46.425-.116.663-1.227 1.004-1.45 2.677-.04.3.21 1.516.112 1.785-6.836 18.643-10.89 47.584-14.2 61.551l14.528-.014s2.185-8.524 4.008-16.878c2.796-12.817 22.987-84.553 22.987-84.553l3-.517 1.037 46.1s-.223 1.228.334 2.008c.558.782-.556 1.117-.39 2.233l.39 1.784s-.446 7.14-.892 11.826c-.446 4.685-.092 38.954-.092 38.954"
fill="#7BB2F9"
></path>
<path
d="M77.438 220.434c1.146.094 4.016-2.008 6.916-4.91M107.55 223.931s2.758-1.103 6.069-3.862"
stroke="#648BD8"
stroke-width="1.051"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M108.459 220.905s2.759-1.104 6.07-3.863"
stroke="#648BD8"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M76.099 223.557s2.608-.587 6.47-3.346M87.33 150.82c-.27 3.088.297 8.478-4.315 9.073M104.829 149.075s.11 13.936-1.286 14.983c-2.207 1.655-2.975 1.934-2.975 1.934M101.014 149.63s.035 12.81-1.19 24.245M94.93 174.965s7.174-1.655 9.38-1.655M75.671 204.754c-.316 1.55-.64 3.067-.973 4.535 0 0-1.45 1.822-1.003 3.756.446 1.934-.943 2.034-4.96 15.273-1.686 5.559-4.464 18.49-6.313 27.447-.078.38-4.018 18.06-4.093 18.423M77.043 196.743a313.269 313.269 0 0 1-.877 4.729M83.908 151.414l-1.19 10.413s-1.091.148-.496 2.23c.111 1.34-2.66 15.692-5.153 30.267M57.58 272.94h13.238"
stroke="#648BD8"
stroke-width="1.051"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M117.377 147.423s-16.955-3.087-35.7.199c.157 2.501-.002 4.128-.002 4.128s14.607-2.802 35.476-.31c.251-2.342.226-4.017.226-4.017"
fill="#192064"
></path>
<path
d="M107.511 150.353l.004-4.885a.807.807 0 0 0-.774-.81c-2.428-.092-5.04-.108-7.795-.014a.814.814 0 0 0-.784.81l-.003 4.88c0 .456.371.82.827.808a140.76 140.76 0 0 1 7.688.017.81.81 0 0 0 .837-.806"
fill="#FFF"
></path>
<path
d="M106.402 149.426l.002-3.06a.64.64 0 0 0-.616-.643 94.135 94.135 0 0 0-5.834-.009.647.647 0 0 0-.626.643l-.001 3.056c0 .36.291.648.651.64 1.78-.04 3.708-.041 5.762.012.36.009.662-.279.662-.64"
fill="#192064"
></path>
<path
d="M101.485 273.933h12.272M102.652 269.075c.006 3.368.04 5.759.11 6.47M102.667 263.125c-.009 1.53-.015 2.98-.016 4.313M102.204 174.024l.893 44.402s.669 1.561-.224 2.677c-.892 1.116 2.455.67.893 2.231-1.562 1.562.893 1.116 0 3.347-.592 1.48-.988 20.987-1.09 34.956"
stroke="#648BD8"
stroke-width="1.051"
stroke-linecap="round"
stroke-linejoin="round"
></path>
</g>
</svg>
<p class="tips">很抱歉,你暂时无权限访问...</p>
<el-space>
<router-link to="/"><el-button round type="default">返回首页</el-button></router-link>
<a :href="loginUrl"><el-button round type="primary">重新登录</el-button></a>
</el-space>
</div>
</template>
<style lang="scss" scoped>
.container {
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.tips {
margin: 30px;
font-size: 24px;
color: #313131;
}
</style>
import type { Router, RouteRecordRaw } from 'vue-router'
export default function ({ router }: { router: Router }) {
const modules: Array<{ routes: Array<RouteRecordRaw> }> = Object.values(import.meta.globEager('./**/index.ts'))
modules.forEach(({ routes = [] }) => {
// 注册路由
routes.forEach(route => {
router.addRoute(route)
})
})
}
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/stores/user'
const router = createRouter({
history: createWebHistory(),
routes: [{ path: '/:pathMatch(.*)*', redirect: '/base/channel' }]
})
router.beforeEach(async (to, from, next) => {
const whiteList = ['/401']
const user = useUserStore()
if (!user.isLogin && !whiteList.includes(to.path)) {
try {
await user.getUser()
} catch (e) {
console.error(e)
}
user.isLogin ? next() : next('/401')
return
}
next()
})
export default router
import { defineStore } from 'pinia'
import { getMapList } from '@/api/base'
interface State {
mapList: IMapState[]
}
interface IMapState {
id: string
key: string
name: string
remark: string
values: IValuesList[]
}
interface IValuesList {
data_dictionary_id: string
id: string
label: string
remark: string
sort: string
value: string
}
export const useMapStore = defineStore({
id: 'map',
state: (): State => {
return {
mapList: []
}
},
getters: {
getMapValuesByKey: state => {
return (key: string) => state.mapList.find(map => map.key === key)?.values || []
}
},
actions: {
async getMapList() {
const res = await getMapList()
this.mapList = res.data || []
}
}
})
useMapStore().getMapList()
import { defineStore } from 'pinia'
import { getUser, logout } from '@/api/base'
import type { UserType, ProjectType, OrganizationType, RoleType, PermissionType } from '@/types'
interface State {
user: UserType | null
project: ProjectType | null
organization: OrganizationType | null
roles: RoleType[]
permissions: PermissionType[]
}
export const useUserStore = defineStore({
id: 'user',
state: (): State => ({
user: null,
organization: null,
project: null,
roles: [],
permissions: []
}),
getters: {
isLogin: state => !!state.user
},
actions: {
async getUser() {
const res = await getUser()
const { info } = res.data
const { organization, project, roles, permissions } = res.data.permissions
this.user = info
this.organization = organization
this.project = project
this.roles = roles
this.permissions = permissions
},
async logout() {
await logout()
this.user = null
}
}
})
import type { Component } from 'vue'
export interface IMenuItem {
tag?: string
name: string
path: string
icon?: Component
children?: IMenuItem[]
}
// 用户信息
export interface UserType {
id: string
mobile: string
name: string
email: string
username: string
avatar: string
}
// 项目信息
export interface ProjectType {
id: string
tab: string
name: string
}
// 机构信息
export interface OrganizationType {
id: string
name: string
contact_name: string
contact_information: string
validity_date: string
is_valid: 1 | 2
}
// 角色信息
export interface RoleType {
id: string
name: string
desc: string
}
// 权限信息
export interface PermissionType {
desc: string
effect_uris: string
id: string
name: string
parent_id: string
system_tag: number
type: number
tag: string
}
// 课程信息
export interface CourseType {
auth_view: boolean
belong_operator: string
belong_operator_name: string
big: string
classification: string
classification_name: string
cover: string
created_operator: string
created_operator_name: string
created_time: string
credit: string
department_public: string
elective_type: string
elective_type_name: string
id: string
name: string
online_type: string
online_type_name: string
organ_id: string
organ_id_name: string
platform_public: string
project_id: string
project_id_name: string
small: string
status: string
status_name: string
updated_operator: string
updated_operator_name: string
updated_time: string
}
import axios from 'axios'
import qs from 'qs'
import { ElMessage } from 'element-plus'
import router from '@/router'
const httpRequest = axios.create({
timeout: 60000,
withCredentials: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
// 请求拦截
httpRequest.interceptors.request.use(
function (config) {
if (config.headers?.['Content-Type'] === 'application/x-www-form-urlencoded') {
config.data = qs.stringify(config.data, { skipNulls: true })
}
if (config.headers?.['Content-Type'] === 'multipart/form-data') {
const formData = new window.FormData()
for (const key in config.data) {
formData.append(key, config.data[key])
}
config.data = formData
}
return config
},
function (error) {
return Promise.reject(error)
}
)
// 响应拦截
httpRequest.interceptors.response.use(
function (response) {
const { data } = response
// 未登录
if (data.code === 4001) {
location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}`
return Promise.reject(data)
}
if (data.code === 1) {
ElMessage.error(data.message || data.msg)
return Promise.reject(data)
}
return data
},
function (error) {
if (error.response) {
const { status, message } = error.response.data
// 未登录
if (status === 403) {
location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}`
} else if (status === 402) {
// 未授权
router.push('/401')
} else {
ElMessage.error(message)
console.error(`${status}: ${message}`)
}
} else {
console.log(error)
}
return Promise.reject(error.response || error)
}
)
export default httpRequest
// json to array
export const json2Array = function (data, isValueToNumber = true) {
return Object.keys(data).map(value => ({ label: data[value], value: isValueToNumber ? parseInt(value) : value }))
}
// 组卷模式
export const paperType = {
1: '选题组卷',
2: '自动组卷',
3: '自由组卷'
}
// 组卷模式列表
export const paperTypeList = json2Array(paperType)
// 试题类型
export const questionType = {
1: '单选题',
2: '多选题',
3: '问答题',
5: '案例题',
6: '判断题',
7: '实操题',
8: '情景题'
}
// 试题类型列表
export const questionTypeList = json2Array(questionType, false)
// 试题难度
export const questionDifficulty = {
1: '易',
2: '中',
3: '难',
0: '无'
}
// 试题难度列表
export const questionDifficultyList = json2Array(questionDifficulty, false)
import { useUserStore } from '@/stores/user'
import type { DirectiveBinding } from 'vue'
// 判断是否有权限
export function checkPermission(value: string | string[]): boolean {
const userStore = useUserStore()
const permissions = userStore.permissions
if (Array.isArray(value)) {
return permissions.some(item => value.includes(item.tag))
} else {
return !!permissions.find(item => item.tag === value)
}
}
// 权限指令
export function permissionDirective(el: HTMLElement, binding: DirectiveBinding) {
const { value } = binding
if (!value) return
if (!checkPermission(value)) {
el.parentNode && el.parentNode.removeChild(el)
}
}
{
"extends": "@vue/tsconfig/tsconfig.node.json",
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
"compilerOptions": {
"composite": true,
"types": ["node"]
}
}
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["auto-imports.d.ts", "env.d.ts", "src/**/*", "src/**/*.vue"],
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"types": ["element-plus/global"]
},
"references": [
{
"path": "./tsconfig.config.json"
}
]
}
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig(({ mode }) => ({
base: mode === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/saas-lab/' : '/',
plugins: [
vue({ reactivityTransform: true }),
AutoImport({
imports: ['vue', 'vue/macros', 'vue-router', '@vueuse/core', '@vueuse/math'],
dts: true,
eslintrc: { enabled: true }
})
],
server: {
open: true,
host: 'dev.ezijing.com',
https: {
key: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.key')),
cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem'))
},
proxy: {
'/api': 'https://saas-lab.ezijing.com'
}
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
}))
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论