├── .editorconfig ├── .env ├── .env.development ├── .env.production ├── .eslintrc.cjs ├── .gitignore ├── .husky └── pre-commit ├── .prettierrc.json ├── README.md ├── auto-imports.d.ts ├── components.d.ts ├── env.d.ts ├── index.html ├── netlify.toml ├── package.json ├── pnpm-lock.yaml ├── public └── favicon.ico ├── src ├── App.vue ├── assets │ ├── css │ │ ├── common.less │ │ ├── index.less │ │ └── reset.less │ └── img │ │ ├── error.jpg │ │ ├── login-bg.svg │ │ └── qrcode.png ├── components │ ├── echart │ │ ├── data │ │ │ └── china.json │ │ ├── index.ts │ │ ├── src │ │ │ ├── bar-echart.vue │ │ │ ├── base-echart.vue │ │ │ ├── line-echart.vue │ │ │ ├── map-echart.vue │ │ │ ├── pie-echart.vue │ │ │ └── rose-echart.vue │ │ ├── type │ │ │ └── index.d.ts │ │ └── utils │ │ │ ├── convert-data.ts │ │ │ └── coordinate-data.ts │ ├── icon-select │ │ ├── data.ts │ │ ├── index.ts │ │ └── src │ │ │ ├── Select.vue │ │ │ ├── hooks.ts │ │ │ ├── iconfont.ts │ │ │ └── types.ts │ ├── image-upload │ │ └── image-upload.vue │ ├── main-header │ │ ├── c-cpns │ │ │ ├── header-breadcrumb.vue │ │ │ └── header-info.vue │ │ └── main-header.vue │ ├── main-menu │ │ └── main-menu.vue │ ├── page-comment │ │ └── page-comment.vue │ ├── page-content │ │ └── page-content.vue │ ├── page-editor │ │ └── page-editor.vue │ ├── page-modal │ │ └── page-modal.vue │ ├── page-panel │ │ └── page-panel.vue │ └── page-search │ │ └── page-search.vue ├── enums │ └── scan-status.enum.ts ├── global │ ├── index.ts │ └── register-icons.ts ├── hooks │ ├── useAddDept2Config.ts │ ├── useAddGoodsCategory2Config.ts │ ├── useAddRole2Config.ts │ ├── usePageContent.ts │ ├── usePageModal.ts │ └── useToValueDeep.ts ├── main.ts ├── router │ ├── index.ts │ └── main │ │ ├── analysis │ │ ├── dashboard │ │ │ └── dashboard.ts │ │ └── overview │ │ │ └── overview.ts │ │ ├── product │ │ ├── category │ │ │ └── category.ts │ │ └── goods │ │ │ └── goods.ts │ │ ├── story │ │ ├── chat │ │ │ └── chat.ts │ │ └── list │ │ │ └── list.ts │ │ └── system │ │ ├── department │ │ └── department.ts │ │ ├── menu │ │ └── menu.ts │ │ ├── role │ │ └── role.ts │ │ └── user │ │ └── user.ts ├── services │ ├── config │ │ ├── index.ts │ │ └── type.ts │ ├── index.ts │ ├── login │ │ └── login.ts │ ├── main │ │ ├── analysis │ │ │ └── analysis.ts │ │ ├── main.ts │ │ ├── story │ │ │ └── story.ts │ │ └── system │ │ │ └── system.ts │ └── request │ │ └── index.ts ├── store │ ├── index.ts │ ├── login │ │ └── login.ts │ └── main │ │ ├── analysis │ │ └── analysis.ts │ │ ├── main.ts │ │ ├── story │ │ └── story.ts │ │ └── system │ │ └── system.ts ├── types │ ├── config │ │ ├── appFormItem.config.d.ts │ │ ├── modal.config.d.ts │ │ ├── seach.config.d.ts │ │ └── table.config.d.ts │ ├── editor │ │ └── custom-types.d.ts │ ├── index.d.ts │ ├── login.d.ts │ └── main │ │ ├── analysis │ │ └── analysis.d.ts │ │ ├── main.d.ts │ │ ├── story │ │ └── story.ts │ │ └── system │ │ └── system.d.ts ├── utils │ ├── format-number.ts │ ├── format-time.ts │ └── map-util.ts └── views │ ├── login │ ├── c-cpns │ │ ├── login-pane.vue │ │ ├── pane-account.vue │ │ └── pane-code.vue │ └── login.vue │ ├── main │ ├── analysis │ │ ├── dashboard │ │ │ └── dashboard.vue │ │ └── overview │ │ │ └── overview.vue │ ├── main.vue │ ├── product │ │ ├── category │ │ │ ├── category.vue │ │ │ └── config │ │ │ │ ├── modal.config.ts │ │ │ │ ├── search.config.ts │ │ │ │ └── table.config.ts │ │ └── goods │ │ │ ├── config │ │ │ ├── modal.config.ts │ │ │ ├── search.config.ts │ │ │ └── table.config.ts │ │ │ └── goods.vue │ ├── story │ │ ├── chat │ │ │ └── chat.vue │ │ └── list │ │ │ └── list.vue │ └── system │ │ ├── department │ │ ├── config │ │ │ ├── modal.config.ts │ │ │ ├── search.config.ts │ │ │ └── table.config.ts │ │ └── department.vue │ │ ├── menu │ │ ├── config │ │ │ ├── modal.config.ts │ │ │ └── table.config.ts │ │ └── menu.vue │ │ ├── role │ │ ├── config │ │ │ ├── modal.config.ts │ │ │ ├── search.config.ts │ │ │ └── table.config.ts │ │ └── role.vue │ │ └── user │ │ ├── config │ │ ├── modal.config.ts │ │ ├── search.config.ts │ │ └── table.config.ts │ │ └── user.vue │ └── not-found │ └── not-found.vue ├── tsconfig.config.json ├── tsconfig.json ├── uno.config.ts └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-21 14:08:22 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2022-12-21 14:10:39 6 | * @Description:有助于为不同 IDE 编辑器上处理同一项目的多个开发人员维护一致的编码风格。 7 | */ 8 | # http://editorconfig.org 9 | 10 | root = true 11 | [*] # 表示所有文件适用 12 | charset = utf-8 # 设置文件字符集为 utf-8 13 | indent_style = space # 缩进风格(tab | space) 14 | indent_size = 2 # 缩进大小 15 | end_of_line = lf # 控制换行类型(lf | cr | crlf) 16 | trim_trailing_whitespace = true # 去除行尾的任意空白字符 17 | insert_final_newline = false # 始终在文件末尾插入一个新行 18 | 19 | [*.md] # 表示仅 md 文件适用以下规则 20 | max_line_length = off 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | VITE_BASE_URL=https://cms.server.hqk10.top/api/v1 2 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | VITE_BASE_URL=/proxy 2 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VITE_BASE_URL=/proxy 2 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-20 21:47:04 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2022-12-22 10:59:57 6 | * @Description:代码不符合规范报错 7 | */ 8 | /* eslint-env node */ 9 | require('@rushstack/eslint-patch/modern-module-resolution') 10 | 11 | module.exports = { 12 | root: true, 13 | extends: [ 14 | 'plugin:vue/vue3-essential', 15 | 'eslint:recommended', 16 | '@vue/eslint-config-typescript', 17 | '@vue/eslint-config-prettier', 18 | 'plugin:prettier/recommended' 19 | ], 20 | parserOptions: { 21 | ecmaVersion: 'latest' 22 | }, 23 | rules: { 24 | 'vue/multi-word-component-names': 'off', 25 | 'no-undef': 'off' 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | echo "husky lint-staged start" 5 | npx lint-staged 6 | echo "husky lint-staged end" 7 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "tabWidth": 2, 4 | "printWidth": 140, 5 | "singleQuote": true, 6 | "trailingComma": "none", 7 | "semi": false 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VUE3-CMS-TS-PINIA 2 | 3 | ## 后端地址 4 | 后端仓库地址,采用nest框架 5 | 6 | ## 介绍 7 | 8 | **VUE3**+**Pinia**+**Pnpm**+**Ts**+**tailwindcss**+**ElementPlus**+**VueRouter**+**VueUse**+**axios** 9 | 10 | ## 使用步骤 11 | 12 | ``` 13 | pnpm install 14 | pnpm lint 15 | pnpm dev 16 | ``` 17 | 18 | ## 上传代码注意 19 | 20 | 该模板使用了 husky+commitizen 管里提交代码,所以,你应该按如下步骤进行提交 21 | 22 | ``` 23 | git add . 24 | pnpm commit 25 | git push origin main 26 | ``` 27 | 28 | 29 | -------------------------------------------------------------------------------- /auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by 'unplugin-auto-import' 2 | export {} 3 | declare global { 4 | const EffectScope: typeof import('vue')['EffectScope'] 5 | const ElLoading: typeof import('element-plus/es')['ElLoading'] 6 | const ElMessage: typeof import('element-plus/es')['ElMessage'] 7 | const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] 8 | const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] 9 | const computed: typeof import('vue')['computed'] 10 | const computedAsync: typeof import('@vueuse/core')['computedAsync'] 11 | const computedEager: typeof import('@vueuse/core')['computedEager'] 12 | const computedInject: typeof import('@vueuse/core')['computedInject'] 13 | const computedWithControl: typeof import('@vueuse/core')['computedWithControl'] 14 | const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] 15 | const controlledRef: typeof import('@vueuse/core')['controlledRef'] 16 | const createApp: typeof import('vue')['createApp'] 17 | const createEventHook: typeof import('@vueuse/core')['createEventHook'] 18 | const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] 19 | const createInjectionState: typeof import('@vueuse/core')['createInjectionState'] 20 | const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] 21 | const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] 22 | const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] 23 | const customRef: typeof import('vue')['customRef'] 24 | const debouncedRef: typeof import('@vueuse/core')['debouncedRef'] 25 | const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] 26 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 27 | const defineComponent: typeof import('vue')['defineComponent'] 28 | const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] 29 | const effectScope: typeof import('vue')['effectScope'] 30 | const extendRef: typeof import('@vueuse/core')['extendRef'] 31 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 32 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 33 | const h: typeof import('vue')['h'] 34 | const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] 35 | const inject: typeof import('vue')['inject'] 36 | const isDefined: typeof import('@vueuse/core')['isDefined'] 37 | const isProxy: typeof import('vue')['isProxy'] 38 | const isReactive: typeof import('vue')['isReactive'] 39 | const isReadonly: typeof import('vue')['isReadonly'] 40 | const isRef: typeof import('vue')['isRef'] 41 | const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] 42 | const markRaw: typeof import('vue')['markRaw'] 43 | const nextTick: typeof import('vue')['nextTick'] 44 | const onActivated: typeof import('vue')['onActivated'] 45 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 46 | const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] 47 | const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] 48 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 49 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 50 | const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] 51 | const onDeactivated: typeof import('vue')['onDeactivated'] 52 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 53 | const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke'] 54 | const onLongPress: typeof import('@vueuse/core')['onLongPress'] 55 | const onMounted: typeof import('vue')['onMounted'] 56 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 57 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 58 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 59 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 60 | const onStartTyping: typeof import('@vueuse/core')['onStartTyping'] 61 | const onUnmounted: typeof import('vue')['onUnmounted'] 62 | const onUpdated: typeof import('vue')['onUpdated'] 63 | const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] 64 | const provide: typeof import('vue')['provide'] 65 | const reactify: typeof import('@vueuse/core')['reactify'] 66 | const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] 67 | const reactive: typeof import('vue')['reactive'] 68 | const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed'] 69 | const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit'] 70 | const reactivePick: typeof import('@vueuse/core')['reactivePick'] 71 | const readonly: typeof import('vue')['readonly'] 72 | const ref: typeof import('vue')['ref'] 73 | const refAutoReset: typeof import('@vueuse/core')['refAutoReset'] 74 | const refDebounced: typeof import('@vueuse/core')['refDebounced'] 75 | const refDefault: typeof import('@vueuse/core')['refDefault'] 76 | const refThrottled: typeof import('@vueuse/core')['refThrottled'] 77 | const refWithControl: typeof import('@vueuse/core')['refWithControl'] 78 | const resolveComponent: typeof import('vue')['resolveComponent'] 79 | const resolveDirective: typeof import('vue')['resolveDirective'] 80 | const resolveRef: typeof import('@vueuse/core')['resolveRef'] 81 | const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] 82 | const shallowReactive: typeof import('vue')['shallowReactive'] 83 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 84 | const shallowRef: typeof import('vue')['shallowRef'] 85 | const storeToRefs: typeof import('pinia')['storeToRefs'] 86 | const syncRef: typeof import('@vueuse/core')['syncRef'] 87 | const syncRefs: typeof import('@vueuse/core')['syncRefs'] 88 | const templateRef: typeof import('@vueuse/core')['templateRef'] 89 | const throttledRef: typeof import('@vueuse/core')['throttledRef'] 90 | const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] 91 | const toRaw: typeof import('vue')['toRaw'] 92 | const toReactive: typeof import('@vueuse/core')['toReactive'] 93 | const toRef: typeof import('vue')['toRef'] 94 | const toRefs: typeof import('vue')['toRefs'] 95 | const triggerRef: typeof import('vue')['triggerRef'] 96 | const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount'] 97 | const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] 98 | const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted'] 99 | const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose'] 100 | const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted'] 101 | const unref: typeof import('vue')['unref'] 102 | const unrefElement: typeof import('@vueuse/core')['unrefElement'] 103 | const until: typeof import('@vueuse/core')['until'] 104 | const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] 105 | const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery'] 106 | const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter'] 107 | const useArrayFind: typeof import('@vueuse/core')['useArrayFind'] 108 | const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex'] 109 | const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast'] 110 | const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin'] 111 | const useArrayMap: typeof import('@vueuse/core')['useArrayMap'] 112 | const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce'] 113 | const useArraySome: typeof import('@vueuse/core')['useArraySome'] 114 | const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique'] 115 | const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] 116 | const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] 117 | const useAttrs: typeof import('vue')['useAttrs'] 118 | const useBase64: typeof import('@vueuse/core')['useBase64'] 119 | const useBattery: typeof import('@vueuse/core')['useBattery'] 120 | const useBluetooth: typeof import('@vueuse/core')['useBluetooth'] 121 | const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints'] 122 | const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel'] 123 | const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] 124 | const useCached: typeof import('@vueuse/core')['useCached'] 125 | const useClipboard: typeof import('@vueuse/core')['useClipboard'] 126 | const useCloned: typeof import('@vueuse/core')['useCloned'] 127 | const useColorMode: typeof import('@vueuse/core')['useColorMode'] 128 | const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] 129 | const useCounter: typeof import('@vueuse/core')['useCounter'] 130 | const useCssModule: typeof import('vue')['useCssModule'] 131 | const useCssVar: typeof import('@vueuse/core')['useCssVar'] 132 | const useCssVars: typeof import('vue')['useCssVars'] 133 | const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement'] 134 | const useCycleList: typeof import('@vueuse/core')['useCycleList'] 135 | const useDark: typeof import('@vueuse/core')['useDark'] 136 | const useDateFormat: typeof import('@vueuse/core')['useDateFormat'] 137 | const useDebounce: typeof import('@vueuse/core')['useDebounce'] 138 | const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] 139 | const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] 140 | const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion'] 141 | const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation'] 142 | const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio'] 143 | const useDevicesList: typeof import('@vueuse/core')['useDevicesList'] 144 | const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia'] 145 | const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility'] 146 | const useDraggable: typeof import('@vueuse/core')['useDraggable'] 147 | const useDropZone: typeof import('@vueuse/core')['useDropZone'] 148 | const useElementBounding: typeof import('@vueuse/core')['useElementBounding'] 149 | const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint'] 150 | const useElementHover: typeof import('@vueuse/core')['useElementHover'] 151 | const useElementSize: typeof import('@vueuse/core')['useElementSize'] 152 | const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility'] 153 | const useEventBus: typeof import('@vueuse/core')['useEventBus'] 154 | const useEventListener: typeof import('@vueuse/core')['useEventListener'] 155 | const useEventSource: typeof import('@vueuse/core')['useEventSource'] 156 | const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] 157 | const useFavicon: typeof import('@vueuse/core')['useFavicon'] 158 | const useFetch: typeof import('@vueuse/core')['useFetch'] 159 | const useFileDialog: typeof import('@vueuse/core')['useFileDialog'] 160 | const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess'] 161 | const useFocus: typeof import('@vueuse/core')['useFocus'] 162 | const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] 163 | const useFps: typeof import('@vueuse/core')['useFps'] 164 | const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] 165 | const useGamepad: typeof import('@vueuse/core')['useGamepad'] 166 | const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] 167 | const useIdle: typeof import('@vueuse/core')['useIdle'] 168 | const useImage: typeof import('@vueuse/core')['useImage'] 169 | const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] 170 | const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver'] 171 | const useInterval: typeof import('@vueuse/core')['useInterval'] 172 | const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] 173 | const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] 174 | const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] 175 | const useLink: typeof import('vue-router')['useLink'] 176 | const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] 177 | const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] 178 | const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory'] 179 | const useMediaControls: typeof import('@vueuse/core')['useMediaControls'] 180 | const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery'] 181 | const useMemoize: typeof import('@vueuse/core')['useMemoize'] 182 | const useMemory: typeof import('@vueuse/core')['useMemory'] 183 | const useMounted: typeof import('@vueuse/core')['useMounted'] 184 | const useMouse: typeof import('@vueuse/core')['useMouse'] 185 | const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement'] 186 | const useMousePressed: typeof import('@vueuse/core')['useMousePressed'] 187 | const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver'] 188 | const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage'] 189 | const useNetwork: typeof import('@vueuse/core')['useNetwork'] 190 | const useNow: typeof import('@vueuse/core')['useNow'] 191 | const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl'] 192 | const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination'] 193 | const useOnline: typeof import('@vueuse/core')['useOnline'] 194 | const usePageLeave: typeof import('@vueuse/core')['usePageLeave'] 195 | const useParallax: typeof import('@vueuse/core')['useParallax'] 196 | const usePermission: typeof import('@vueuse/core')['usePermission'] 197 | const usePointer: typeof import('@vueuse/core')['usePointer'] 198 | const usePointerLock: typeof import('@vueuse/core')['usePointerLock'] 199 | const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] 200 | const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] 201 | const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast'] 202 | const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] 203 | const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] 204 | const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] 205 | const usePrevious: typeof import('@vueuse/core')['usePrevious'] 206 | const useRafFn: typeof import('@vueuse/core')['useRafFn'] 207 | const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] 208 | const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] 209 | const useRoute: typeof import('vue-router')['useRoute'] 210 | const useRouter: typeof import('vue-router')['useRouter'] 211 | const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] 212 | const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] 213 | const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] 214 | const useScroll: typeof import('@vueuse/core')['useScroll'] 215 | const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] 216 | const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] 217 | const useShare: typeof import('@vueuse/core')['useShare'] 218 | const useSlots: typeof import('vue')['useSlots'] 219 | const useSorted: typeof import('@vueuse/core')['useSorted'] 220 | const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] 221 | const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] 222 | const useStepper: typeof import('@vueuse/core')['useStepper'] 223 | const useStorage: typeof import('@vueuse/core')['useStorage'] 224 | const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] 225 | const useStyleTag: typeof import('@vueuse/core')['useStyleTag'] 226 | const useSupported: typeof import('@vueuse/core')['useSupported'] 227 | const useSwipe: typeof import('@vueuse/core')['useSwipe'] 228 | const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] 229 | const useTextDirection: typeof import('@vueuse/core')['useTextDirection'] 230 | const useTextSelection: typeof import('@vueuse/core')['useTextSelection'] 231 | const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize'] 232 | const useThrottle: typeof import('@vueuse/core')['useThrottle'] 233 | const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn'] 234 | const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory'] 235 | const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] 236 | const useTimeout: typeof import('@vueuse/core')['useTimeout'] 237 | const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] 238 | const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll'] 239 | const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] 240 | const useTitle: typeof import('@vueuse/core')['useTitle'] 241 | const useToNumber: typeof import('@vueuse/core')['useToNumber'] 242 | const useToString: typeof import('@vueuse/core')['useToString'] 243 | const useToggle: typeof import('@vueuse/core')['useToggle'] 244 | const useTransition: typeof import('@vueuse/core')['useTransition'] 245 | const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] 246 | const useUserMedia: typeof import('@vueuse/core')['useUserMedia'] 247 | const useVModel: typeof import('@vueuse/core')['useVModel'] 248 | const useVModels: typeof import('@vueuse/core')['useVModels'] 249 | const useVibrate: typeof import('@vueuse/core')['useVibrate'] 250 | const useVirtualList: typeof import('@vueuse/core')['useVirtualList'] 251 | const useWakeLock: typeof import('@vueuse/core')['useWakeLock'] 252 | const useWebNotification: typeof import('@vueuse/core')['useWebNotification'] 253 | const useWebSocket: typeof import('@vueuse/core')['useWebSocket'] 254 | const useWebWorker: typeof import('@vueuse/core')['useWebWorker'] 255 | const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn'] 256 | const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus'] 257 | const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll'] 258 | const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] 259 | const watch: typeof import('vue')['watch'] 260 | const watchArray: typeof import('@vueuse/core')['watchArray'] 261 | const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] 262 | const watchDebounced: typeof import('@vueuse/core')['watchDebounced'] 263 | const watchEffect: typeof import('vue')['watchEffect'] 264 | const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable'] 265 | const watchOnce: typeof import('@vueuse/core')['watchOnce'] 266 | const watchPausable: typeof import('@vueuse/core')['watchPausable'] 267 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 268 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 269 | const watchThrottled: typeof import('@vueuse/core')['watchThrottled'] 270 | const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable'] 271 | const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] 272 | const whenever: typeof import('@vueuse/core')['whenever'] 273 | } 274 | -------------------------------------------------------------------------------- /components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/core/pull/3399 4 | import '@vue/runtime-core' 5 | 6 | export {} 7 | 8 | declare module '@vue/runtime-core' { 9 | export interface GlobalComponents { 10 | BarEchart: typeof import('./src/components/echart/src/bar-echart.vue')['default'] 11 | BaseEchart: typeof import('./src/components/echart/src/base-echart.vue')['default'] 12 | ElAside: typeof import('element-plus/es')['ElAside'] 13 | ElAvatar: typeof import('element-plus/es')['ElAvatar'] 14 | ElBadge: typeof import('element-plus/es')['ElBadge'] 15 | ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] 16 | ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] 17 | ElButton: typeof import('element-plus/es')['ElButton'] 18 | ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] 19 | ElCol: typeof import('element-plus/es')['ElCol'] 20 | ElContainer: typeof import('element-plus/es')['ElContainer'] 21 | ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] 22 | ElDialog: typeof import('element-plus/es')['ElDialog'] 23 | ElDivider: typeof import('element-plus/es')['ElDivider'] 24 | ElDropdown: typeof import('element-plus/es')['ElDropdown'] 25 | ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] 26 | ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] 27 | ElForm: typeof import('element-plus/es')['ElForm'] 28 | ElFormItem: typeof import('element-plus/es')['ElFormItem'] 29 | ElHeader: typeof import('element-plus/es')['ElHeader'] 30 | ElIcon: typeof import('element-plus/es')['ElIcon'] 31 | ElImage: typeof import('element-plus/es')['ElImage'] 32 | ElInput: typeof import('element-plus/es')['ElInput'] 33 | ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] 34 | ElLink: typeof import('element-plus/es')['ElLink'] 35 | ElMain: typeof import('element-plus/es')['ElMain'] 36 | ElMenu: typeof import('element-plus/es')['ElMenu'] 37 | ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] 38 | ElOption: typeof import('element-plus/es')['ElOption'] 39 | ElPagination: typeof import('element-plus/es')['ElPagination'] 40 | ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm'] 41 | ElPopover: typeof import('element-plus/es')['ElPopover'] 42 | ElRow: typeof import('element-plus/es')['ElRow'] 43 | ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] 44 | ElSelect: typeof import('element-plus/es')['ElSelect'] 45 | ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] 46 | ElSwitch: typeof import('element-plus/es')['ElSwitch'] 47 | ElTable: typeof import('element-plus/es')['ElTable'] 48 | ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] 49 | ElTabPane: typeof import('element-plus/es')['ElTabPane'] 50 | ElTabs: typeof import('element-plus/es')['ElTabs'] 51 | ElTree: typeof import('element-plus/es')['ElTree'] 52 | ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect'] 53 | ElUpload: typeof import('element-plus/es')['ElUpload'] 54 | HeaderBreadcrumb: typeof import('./src/components/main-header/c-cpns/header-breadcrumb.vue')['default'] 55 | HeaderInfo: typeof import('./src/components/main-header/c-cpns/header-info.vue')['default'] 56 | IEpChatDotSquare: typeof import('~icons/ep/chat-dot-square')['default'] 57 | IEpCircleClose: typeof import('~icons/ep/circle-close')['default'] 58 | IEpDelete: typeof import('~icons/ep/delete')['default'] 59 | IEpEdit: typeof import('~icons/ep/edit')['default'] 60 | IEpElementPlus: typeof import('~icons/ep/element-plus')['default'] 61 | IEpExpand: typeof import('~icons/ep/expand')['default'] 62 | IEpFold: typeof import('~icons/ep/fold')['default'] 63 | IEpInfoFilled: typeof import('~icons/ep/info-filled')['default'] 64 | IEpMessage: typeof import('~icons/ep/message')['default'] 65 | IEpRefresh: typeof import('~icons/ep/refresh')['default'] 66 | IEpSearch: typeof import('~icons/ep/search')['default'] 67 | IEpUnlock: typeof import('~icons/ep/unlock')['default'] 68 | IEpUserFilled: typeof import('~icons/ep/user-filled')['default'] 69 | IEpWarning: typeof import('~icons/ep/warning')['default'] 70 | ImageUpload: typeof import('./src/components/image-upload/image-upload.vue')['default'] 71 | LineEchart: typeof import('./src/components/echart/src/line-echart.vue')['default'] 72 | MainHeader: typeof import('./src/components/main-header/main-header.vue')['default'] 73 | MainMenu: typeof import('./src/components/main-menu/main-menu.vue')['default'] 74 | MapEchart: typeof import('./src/components/echart/src/map-echart.vue')['default'] 75 | PageComment: typeof import('./src/components/page-comment/page-comment.vue')['default'] 76 | PageContent: typeof import('./src/components/page-content/page-content.vue')['default'] 77 | PageEditor: typeof import('./src/components/page-editor/page-editor.vue')['default'] 78 | PageModal: typeof import('./src/components/page-modal/page-modal.vue')['default'] 79 | PagePanel: typeof import('./src/components/page-panel/page-panel.vue')['default'] 80 | PageSearch: typeof import('./src/components/page-search/page-search.vue')['default'] 81 | PieEchart: typeof import('./src/components/echart/src/pie-echart.vue')['default'] 82 | RoseEchart: typeof import('./src/components/echart/src/rose-echart.vue')['default'] 83 | RouterLink: typeof import('vue-router')['RouterLink'] 84 | RouterView: typeof import('vue-router')['RouterView'] 85 | Select: typeof import('./src/components/icon-select/src/Select.vue')['default'] 86 | } 87 | export interface ComponentCustomProperties { 88 | vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll'] 89 | vLoading: typeof import('element-plus/es')['ElLoadingDirective'] 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /env.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-20 21:47:04 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-16 23:47:49 6 | * @Description: 7 | */ 8 | /// 9 | 10 | // declare module '*.vue' { 11 | // import { DefineComponent } from 'vue' 12 | // const component: DefineComponent 13 | // export default component 14 | // } 15 | 16 | declare module '*.mjs' 17 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | cms 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/proxy/*" 3 | to = "https://hqk10.xyz:3000/api/v1/:splat" 4 | status = 200 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-cms-pinia", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite --host --open", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "type-check": "vue-tsc --noEmit", 10 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", 11 | "prettier": "prettier --write .", 12 | "preinstall": "npx only-allow pnpm", 13 | "prepare": "husky install", 14 | "commit": "git add . & pnpm cz", 15 | "up": "taze major -I" 16 | }, 17 | "dependencies": { 18 | "@element-plus/icons-vue": "^2.1.0", 19 | "@unocss/reset": "^0.48.5", 20 | "@vueuse/core": "^9.13.0", 21 | "@vueuse/integrations": "^10.6.1", 22 | "@wangeditor/editor": "^5.1.23", 23 | "@wangeditor/editor-for-vue": "^5.1.12", 24 | "axios": "^1.6.2", 25 | "dayjs": "^1.11.10", 26 | "echarts": "^5.4.3", 27 | "element-plus": "^2.4.2", 28 | "less": "^4.2.0", 29 | "lint-staged": "^13.3.0", 30 | "pinia": "^2.1.7", 31 | "pinia-plugin-persistedstate": "^3.2.0", 32 | "qrcode": "^1.5.3", 33 | "vite-plugin-compression": "^0.5.1", 34 | "vue": "^3.3.8", 35 | "vue-router": "^4.2.5" 36 | }, 37 | "devDependencies": { 38 | "@iconify-json/ep": "^1.1.12", 39 | "@iconify/vue": "^4.1.1", 40 | "@rushstack/eslint-patch": "^1.5.1", 41 | "@types/node": "^18.18.9", 42 | "@vitejs/plugin-vue": "^4.4.1", 43 | "@vue/eslint-config-prettier": "^7.1.0", 44 | "@vue/eslint-config-typescript": "^11.0.3", 45 | "@vue/tsconfig": "^0.1.3", 46 | "commitizen": "^4.3.0", 47 | "cz-conventional-changelog": "3.3.0", 48 | "eslint": "^8.53.0", 49 | "eslint-config-prettier": "^8.10.0", 50 | "eslint-plugin-vue": "^9.18.1", 51 | "http-proxy-middleware": "^2.0.6", 52 | "husky": "^8.0.3", 53 | "postcss": "^8.4.31", 54 | "prettier": "^2.8.8", 55 | "taze": "^0.12.0", 56 | "typescript": "~4.7.4", 57 | "unocss": "^0.48.5", 58 | "unplugin-auto-import": "^0.12.2", 59 | "unplugin-icons": "^0.14.15", 60 | "unplugin-vue-components": "^0.22.12", 61 | "vite": "^4.5.0", 62 | "vue-tsc": "^1.8.22" 63 | }, 64 | "config": { 65 | "commitizen": { 66 | "path": "cz-conventional-changelog" 67 | } 68 | }, 69 | "lint-staged": { 70 | "*.{js,vue,ts,jsx,tsx}": [ 71 | "prettier --write", 72 | "eslint --fix" 73 | ], 74 | "*.{html,css,less,scss,md}": [ 75 | "prettier --write" 76 | ] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeoKun1231/VUE3-CMS-TS-PINIA/17e0375a93aff3e900fccd0a557cd878b321fb25/public/favicon.ico -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 15 | 16 | 23 | 29 | -------------------------------------------------------------------------------- /src/assets/css/common.less: -------------------------------------------------------------------------------- 1 | /* reset.css样式重置文件 */ 2 | /* margin/padding重置 */ 3 | a, 4 | address, 5 | b, 6 | big, 7 | blockquote, 8 | body, 9 | center, 10 | cite, 11 | code, 12 | dd, 13 | del, 14 | div, 15 | dl, 16 | dt, 17 | em, 18 | fieldset, 19 | font, 20 | form, 21 | h1, 22 | h2, 23 | h3, 24 | h4, 25 | h5, 26 | h6, 27 | html, 28 | i, 29 | iframe, 30 | img, 31 | ins, 32 | label, 33 | legend, 34 | li, 35 | ol, 36 | p, 37 | pre, 38 | small, 39 | span, 40 | strong, 41 | u, 42 | ul, 43 | var { 44 | padding: 0; 45 | margin: 0; 46 | } 47 | 48 | /* a元素重置 */ 49 | a { 50 | text-decoration: none; 51 | color: #333; 52 | } 53 | 54 | /* img的vertical-align重置 */ 55 | img { 56 | vertical-align: top; 57 | } 58 | 59 | /* ul, ol, li重置 */ 60 | ul, 61 | ol, 62 | li { 63 | list-style: none; 64 | } 65 | 66 | /* 对斜体元素重置 */ 67 | i, 68 | em { 69 | font-style: normal; 70 | } 71 | -------------------------------------------------------------------------------- /src/assets/css/index.less: -------------------------------------------------------------------------------- 1 | @import './common.less'; 2 | @import './reset.less'; 3 | 4 | /* 修改垂直滚动条 */ 5 | ::-webkit-scrollbar { 6 | width: 8px; /* 修改宽度 */ 7 | } 8 | 9 | /* 修改滚动条轨道背景色 */ 10 | ::-webkit-scrollbar-track { 11 | background-color: #f1f1f1; 12 | } 13 | 14 | /* 修改滚动条滑块颜色 */ 15 | ::-webkit-scrollbar-thumb { 16 | background-color: #ddd; 17 | } 18 | 19 | /* 修改滚动条滑块悬停时的颜色 */ 20 | ::-webkit-scrollbar-thumb:hover { 21 | background-color: #ddd; 22 | } 23 | 24 | /* 修改滚动条滑块移动时的颜色 */ 25 | ::-webkit-scrollbar-thumb:active { 26 | background-color: #ddd; 27 | } 28 | 29 | /* 修改滚动条滑块的圆角 */ 30 | ::-webkit-scrollbar-thumb { 31 | border-radius: 8px; 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/css/reset.less: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-size: 2em; 3 | } 4 | h2 { 5 | font-size: 1.5em; 6 | } 7 | h3 { 8 | font-size: 1.17em; 9 | } 10 | h4 { 11 | font-size: 1em; 12 | } 13 | h5 { 14 | font-size: 0.83em; 15 | } 16 | h1, 17 | h2, 18 | h3, 19 | h4, 20 | h5 { 21 | font-weight: bold; 22 | } 23 | -------------------------------------------------------------------------------- /src/assets/img/error.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeoKun1231/VUE3-CMS-TS-PINIA/17e0375a93aff3e900fccd0a557cd878b321fb25/src/assets/img/error.jpg -------------------------------------------------------------------------------- /src/assets/img/login-bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 21 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/assets/img/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeoKun1231/VUE3-CMS-TS-PINIA/17e0375a93aff3e900fccd0a557cd878b321fb25/src/assets/img/qrcode.png -------------------------------------------------------------------------------- /src/components/echart/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-13 17:42:16 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-13 21:04:59 6 | * @Description: 7 | */ 8 | import BaseEchart from './src/base-echart.vue' 9 | import MapEchart from './src/map-echart.vue' 10 | import PieEchart from './src/pie-echart.vue' 11 | import RoseEchart from './src/rose-echart.vue' 12 | import LineEchart from './src/line-echart.vue' 13 | import BarEchart from './src/bar-echart.vue' 14 | 15 | export { PieEchart, MapEchart, RoseEchart, LineEchart, BarEchart } 16 | 17 | export default BaseEchart 18 | -------------------------------------------------------------------------------- /src/components/echart/src/bar-echart.vue: -------------------------------------------------------------------------------- 1 | 8 | 53 | 54 | 59 | 60 | -------------------------------------------------------------------------------- /src/components/echart/src/base-echart.vue: -------------------------------------------------------------------------------- 1 | 8 | 46 | 47 | 53 | 54 | -------------------------------------------------------------------------------- /src/components/echart/src/line-echart.vue: -------------------------------------------------------------------------------- 1 | 8 | 37 | 38 | 43 | 44 | -------------------------------------------------------------------------------- /src/components/echart/src/map-echart.vue: -------------------------------------------------------------------------------- 1 | 8 | 90 | 91 | 96 | 97 | -------------------------------------------------------------------------------- /src/components/echart/src/pie-echart.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 42 | 43 | -------------------------------------------------------------------------------- /src/components/echart/src/rose-echart.vue: -------------------------------------------------------------------------------- 1 | 8 | 46 | 47 | 52 | 53 | -------------------------------------------------------------------------------- /src/components/echart/type/index.d.ts: -------------------------------------------------------------------------------- 1 | export default interface EchartValueType { 2 | value: number 3 | name: string 4 | } 5 | -------------------------------------------------------------------------------- /src/components/echart/utils/convert-data.ts: -------------------------------------------------------------------------------- 1 | import { coordinateData } from './coordinate-data' 2 | 3 | export function convertData(data: any) { 4 | const res = [] 5 | for (let i = 0; i < data.length; i++) { 6 | const geoCoord = coordinateData[data[i].name] 7 | if (geoCoord) { 8 | res.push({ 9 | name: data[i].name, 10 | value: geoCoord.concat(data[i].value) 11 | }) 12 | } 13 | } 14 | return res 15 | } 16 | -------------------------------------------------------------------------------- /src/components/echart/utils/coordinate-data.ts: -------------------------------------------------------------------------------- 1 | export const coordinateData: any = { 2 | 海门: [121.15, 31.89], 3 | 鄂尔多斯: [109.781327, 39.608266], 4 | 招远: [120.38, 37.35], 5 | 舟山: [122.207216, 29.985295], 6 | 齐齐哈尔: [123.97, 47.33], 7 | 盐城: [120.13, 33.38], 8 | 赤峰: [118.87, 42.28], 9 | 青岛: [120.33, 36.07], 10 | 乳山: [121.52, 36.89], 11 | 金昌: [102.188043, 38.520089], 12 | 泉州: [118.58, 24.93], 13 | 莱西: [120.53, 36.86], 14 | 日照: [119.46, 35.42], 15 | 胶南: [119.97, 35.88], 16 | 南通: [121.05, 32.08], 17 | 拉萨: [91.11, 29.97], 18 | 云浮: [112.02, 22.93], 19 | 梅州: [116.1, 24.55], 20 | 文登: [122.05, 37.2], 21 | 上海: [121.48, 31.22], 22 | 攀枝花: [101.718637, 26.582347], 23 | 威海: [122.1, 37.5], 24 | 承德: [117.93, 40.97], 25 | 厦门: [118.1, 24.46], 26 | 汕尾: [115.375279, 22.786211], 27 | 潮州: [116.63, 23.68], 28 | 丹东: [124.37, 40.13], 29 | 太仓: [121.1, 31.45], 30 | 曲靖: [103.79, 25.51], 31 | 烟台: [121.39, 37.52], 32 | 福州: [119.3, 26.08], 33 | 瓦房店: [121.979603, 39.627114], 34 | 即墨: [120.45, 36.38], 35 | 抚顺: [123.97, 41.97], 36 | 玉溪: [102.52, 24.35], 37 | 张家口: [114.87, 40.82], 38 | 阳泉: [113.57, 37.85], 39 | 莱州: [119.942327, 37.177017], 40 | 湖州: [120.1, 30.86], 41 | 汕头: [116.69, 23.39], 42 | 昆山: [120.95, 31.39], 43 | 宁波: [121.56, 29.86], 44 | 湛江: [110.359377, 21.270708], 45 | 揭阳: [116.35, 23.55], 46 | 荣成: [122.41, 37.16], 47 | 连云港: [119.16, 34.59], 48 | 葫芦岛: [120.836932, 40.711052], 49 | 常熟: [120.74, 31.64], 50 | 东莞: [113.75, 23.04], 51 | 河源: [114.68, 23.73], 52 | 淮安: [119.15, 33.5], 53 | 泰州: [119.9, 32.49], 54 | 南宁: [108.33, 22.84], 55 | 营口: [122.18, 40.65], 56 | 惠州: [114.4, 23.09], 57 | 江阴: [120.26, 31.91], 58 | 蓬莱: [120.75, 37.8], 59 | 韶关: [113.62, 24.84], 60 | 嘉峪关: [98.289152, 39.77313], 61 | 广州: [113.23, 23.16], 62 | 延安: [109.47, 36.6], 63 | 太原: [112.53, 37.87], 64 | 清远: [113.01, 23.7], 65 | 中山: [113.38, 22.52], 66 | 昆明: [102.73, 25.04], 67 | 寿光: [118.73, 36.86], 68 | 盘锦: [122.070714, 41.119997], 69 | 长治: [113.08, 36.18], 70 | 深圳: [114.07, 22.62], 71 | 珠海: [113.52, 22.3], 72 | 宿迁: [118.3, 33.96], 73 | 咸阳: [108.72, 34.36], 74 | 铜川: [109.11, 35.09], 75 | 平度: [119.97, 36.77], 76 | 佛山: [113.11, 23.05], 77 | 海口: [110.35, 20.02], 78 | 江门: [113.06, 22.61], 79 | 章丘: [117.53, 36.72], 80 | 肇庆: [112.44, 23.05], 81 | 大连: [121.62, 38.92], 82 | 临汾: [111.5, 36.08], 83 | 吴江: [120.63, 31.16], 84 | 石嘴山: [106.39, 39.04], 85 | 沈阳: [123.38, 41.8], 86 | 苏州: [120.62, 31.32], 87 | 茂名: [110.88, 21.68], 88 | 嘉兴: [120.76, 30.77], 89 | 长春: [125.35, 43.88], 90 | 胶州: [120.03336, 36.264622], 91 | 银川: [106.27, 38.47], 92 | 张家港: [120.555821, 31.875428], 93 | 三门峡: [111.19, 34.76], 94 | 锦州: [121.15, 41.13], 95 | 南昌: [115.89, 28.68], 96 | 柳州: [109.4, 24.33], 97 | 三亚: [109.511909, 18.252847], 98 | 自贡: [104.778442, 29.33903], 99 | 吉林: [126.57, 43.87], 100 | 阳江: [111.95, 21.85], 101 | 泸州: [105.39, 28.91], 102 | 西宁: [101.74, 36.56], 103 | 宜宾: [104.56, 29.77], 104 | 呼和浩特: [111.65, 40.82], 105 | 成都: [104.06, 30.67], 106 | 大同: [113.3, 40.12], 107 | 镇江: [119.44, 32.2], 108 | 桂林: [110.28, 25.29], 109 | 张家界: [110.479191, 29.117096], 110 | 宜兴: [119.82, 31.36], 111 | 北海: [109.12, 21.49], 112 | 西安: [108.95, 34.27], 113 | 金坛: [119.56, 31.74], 114 | 东营: [118.49, 37.46], 115 | 牡丹江: [129.58, 44.6], 116 | 遵义: [106.9, 27.7], 117 | 绍兴: [120.58, 30.01], 118 | 扬州: [119.42, 32.39], 119 | 常州: [119.95, 31.79], 120 | 潍坊: [119.1, 36.62], 121 | 重庆: [106.54, 29.59], 122 | 台州: [121.420757, 28.656386], 123 | 南京: [118.78, 32.04], 124 | 滨州: [118.03, 37.36], 125 | 贵阳: [106.71, 26.57], 126 | 无锡: [120.29, 31.59], 127 | 本溪: [123.73, 41.3], 128 | 克拉玛依: [84.77, 45.59], 129 | 渭南: [109.5, 34.52], 130 | 马鞍山: [118.48, 31.56], 131 | 宝鸡: [107.15, 34.38], 132 | 焦作: [113.21, 35.24], 133 | 句容: [119.16, 31.95], 134 | 北京: [116.46, 39.92], 135 | 徐州: [117.2, 34.26], 136 | 衡水: [115.72, 37.72], 137 | 包头: [110, 40.58], 138 | 绵阳: [104.73, 31.48], 139 | 乌鲁木齐: [87.68, 43.77], 140 | 枣庄: [117.57, 34.86], 141 | 杭州: [120.19, 30.26], 142 | 淄博: [118.05, 36.78], 143 | 鞍山: [122.85, 41.12], 144 | 溧阳: [119.48, 31.43], 145 | 库尔勒: [86.06, 41.68], 146 | 安阳: [114.35, 36.1], 147 | 开封: [114.35, 34.79], 148 | 济南: [117, 36.65], 149 | 德阳: [104.37, 31.13], 150 | 温州: [120.65, 28.01], 151 | 九江: [115.97, 29.71], 152 | 邯郸: [114.47, 36.6], 153 | 临安: [119.72, 30.23], 154 | 兰州: [103.73, 36.03], 155 | 沧州: [116.83, 38.33], 156 | 临沂: [118.35, 35.05], 157 | 南充: [106.110698, 30.837793], 158 | 天津: [117.2, 39.13], 159 | 富阳: [119.95, 30.07], 160 | 泰安: [117.13, 36.18], 161 | 诸暨: [120.23, 29.71], 162 | 郑州: [113.65, 34.76], 163 | 哈尔滨: [126.63, 45.75], 164 | 聊城: [115.97, 36.45], 165 | 芜湖: [118.38, 31.33], 166 | 唐山: [118.02, 39.63], 167 | 平顶山: [113.29, 33.75], 168 | 邢台: [114.48, 37.05], 169 | 德州: [116.29, 37.45], 170 | 济宁: [116.59, 35.38], 171 | 荆州: [112.239741, 30.335165], 172 | 宜昌: [111.3, 30.7], 173 | 义乌: [120.06, 29.32], 174 | 丽水: [119.92, 28.45], 175 | 洛阳: [112.44, 34.7], 176 | 秦皇岛: [119.57, 39.95], 177 | 株洲: [113.16, 27.83], 178 | 石家庄: [114.48, 38.03], 179 | 莱芜: [117.67, 36.19], 180 | 常德: [111.69, 29.05], 181 | 保定: [115.48, 38.85], 182 | 湘潭: [112.91, 27.87], 183 | 金华: [119.64, 29.12], 184 | 岳阳: [113.09, 29.37], 185 | 长沙: [113, 28.21], 186 | 衢州: [118.88, 28.97], 187 | 廊坊: [116.7, 39.53], 188 | 菏泽: [115.480656, 35.23375], 189 | 合肥: [117.27, 31.86], 190 | 武汉: [114.31, 30.52], 191 | 大庆: [125.03, 46.58] 192 | } 193 | -------------------------------------------------------------------------------- /src/components/icon-select/data.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 想要哪个图标集 自行添加即可 请注意此处添加的图标集均为在线图标(https://iconify.design/docs/api/#public-api) 3 | * 如果项目在内网环境下 参考 https://www.bilibili.com/video/BV17S4y1J79d?p=4&vd_source=5a992808de6229d78e7810536c5f9ab3 教程自行离线部署图标 4 | * https://icones.js.org/collections/图标集前缀名-meta.json(如:https://icones.js.org/collections/ri-meta.json 取icons字段,可获得当前图标集的所有图标) 5 | */ 6 | export const IconJson = [ 7 | 'add-location', 8 | 'aim', 9 | 'alarm-clock', 10 | 'apple', 11 | 'arrow-down', 12 | 'arrow-down-bold', 13 | 'arrow-left', 14 | 'arrow-left-bold', 15 | 'arrow-right', 16 | 'arrow-right-bold', 17 | 'arrow-up', 18 | 'arrow-up-bold', 19 | 'avatar', 20 | 'back', 21 | 'baseball', 22 | 'basketball', 23 | 'bell', 24 | 'bell-filled', 25 | 'bicycle', 26 | 'bottom', 27 | 'bottom-left', 28 | 'bottom-right', 29 | 'bowl', 30 | 'box', 31 | 'briefcase', 32 | 'brush', 33 | 'brush-filled', 34 | 'burger', 35 | 'calendar', 36 | 'camera', 37 | 'camera-filled', 38 | 'caret-bottom', 39 | 'caret-left', 40 | 'caret-right', 41 | 'caret-top', 42 | 'cellphone', 43 | 'chat-dot-round', 44 | 'chat-dot-square', 45 | 'chat-line-round', 46 | 'chat-line-square', 47 | 'chat-round', 48 | 'chat-square', 49 | 'check', 50 | 'checked', 51 | 'cherry', 52 | 'chicken', 53 | 'chrome-filled', 54 | 'circle-check', 55 | 'circle-check-filled', 56 | 'circle-close', 57 | 'circle-close-filled', 58 | 'circle-plus', 59 | 'circle-plus-filled', 60 | 'clock', 61 | 'close', 62 | 'close-bold', 63 | 'cloudy', 64 | 'coffee', 65 | 'coffee-cup', 66 | 'coin', 67 | 'cold-drink', 68 | 'collection', 69 | 'collection-tag', 70 | 'comment', 71 | 'compass', 72 | 'connection', 73 | 'coordinate', 74 | 'copy-document', 75 | 'cpu', 76 | 'credit-card', 77 | 'crop', 78 | 'd-arrow-left', 79 | 'd-arrow-right', 80 | 'd-caret', 81 | 'data-analysis', 82 | 'data-board', 83 | 'data-line', 84 | 'delete', 85 | 'delete-filled', 86 | 'delete-location', 87 | 'dessert', 88 | 'discount', 89 | 'dish', 90 | 'dish-dot', 91 | 'document', 92 | 'document-add', 93 | 'document-checked', 94 | 'document-copy', 95 | 'document-delete', 96 | 'document-remove', 97 | 'download', 98 | 'drizzling', 99 | 'edit', 100 | 'edit-pen', 101 | 'eleme', 102 | 'eleme-filled', 103 | 'element-plus', 104 | 'expand', 105 | 'failed', 106 | 'female', 107 | 'files', 108 | 'film', 109 | 'filter', 110 | 'finished', 111 | 'first-aid-kit', 112 | 'flag', 113 | 'fold', 114 | 'folder', 115 | 'folder-add', 116 | 'folder-checked', 117 | 'folder-delete', 118 | 'folder-opened', 119 | 'folder-remove', 120 | 'food', 121 | 'football', 122 | 'fork-spoon', 123 | 'fries', 124 | 'full-screen', 125 | 'goblet', 126 | 'goblet-full', 127 | 'goblet-square', 128 | 'goblet-square-full', 129 | 'gold-medal', 130 | 'goods', 131 | 'goods-filled', 132 | 'grape', 133 | 'grid', 134 | 'guide', 135 | 'handbag', 136 | 'headset', 137 | 'help', 138 | 'help-filled', 139 | 'hide', 140 | 'histogram', 141 | 'home-filled', 142 | 'hot-water', 143 | 'house', 144 | 'ice-cream', 145 | 'ice-cream-round', 146 | 'ice-cream-square', 147 | 'ice-drink', 148 | 'ice-tea', 149 | 'info-filled', 150 | 'iphone', 151 | 'key', 152 | 'knife-fork', 153 | 'lightning', 154 | 'link', 155 | 'list', 156 | 'loading', 157 | 'location', 158 | 'location-filled', 159 | 'location-information', 160 | 'lock', 161 | 'lollipop', 162 | 'magic-stick', 163 | 'magnet', 164 | 'male', 165 | 'management', 166 | 'map-location', 167 | 'medal', 168 | 'memo', 169 | 'menu', 170 | 'message', 171 | 'message-box', 172 | 'mic', 173 | 'microphone', 174 | 'milk-tea', 175 | 'minus', 176 | 'money', 177 | 'monitor', 178 | 'moon', 179 | 'moon-night', 180 | 'more', 181 | 'more-filled', 182 | 'mostly-cloudy', 183 | 'mouse', 184 | 'mug', 185 | 'mute', 186 | 'mute-notification', 187 | 'no-smoking', 188 | 'notebook', 189 | 'notification', 190 | 'odometer', 191 | 'office-building', 192 | 'open', 193 | 'operation', 194 | 'opportunity', 195 | 'orange', 196 | 'paperclip', 197 | 'partly-cloudy', 198 | 'pear', 199 | 'phone', 200 | 'phone-filled', 201 | 'picture', 202 | 'picture-filled', 203 | 'picture-rounded', 204 | 'pie-chart', 205 | 'place', 206 | 'platform', 207 | 'plus', 208 | 'pointer', 209 | 'position', 210 | 'postcard', 211 | 'pouring', 212 | 'present', 213 | 'price-tag', 214 | 'printer', 215 | 'promotion', 216 | 'quartz-watch', 217 | 'question-filled', 218 | 'rank', 219 | 'reading', 220 | 'reading-lamp', 221 | 'refresh', 222 | 'refresh-left', 223 | 'refresh-right', 224 | 'refrigerator', 225 | 'remove', 226 | 'remove-filled', 227 | 'right', 228 | 'scale-to-original', 229 | 'school', 230 | 'scissor', 231 | 'search', 232 | 'select', 233 | 'sell', 234 | 'semi-select', 235 | 'service', 236 | 'set-up', 237 | 'setting', 238 | 'share', 239 | 'ship', 240 | 'shop', 241 | 'shopping-bag', 242 | 'shopping-cart', 243 | 'shopping-cart-full', 244 | 'shopping-trolley', 245 | 'smoking', 246 | 'soccer', 247 | 'sold-out', 248 | 'sort', 249 | 'sort-down', 250 | 'sort-up', 251 | 'stamp', 252 | 'star', 253 | 'star-filled', 254 | 'stopwatch', 255 | 'success-filled', 256 | 'sugar', 257 | 'suitcase', 258 | 'suitcase-line', 259 | 'sunny', 260 | 'sunrise', 261 | 'sunset', 262 | 'switch', 263 | 'switch-button', 264 | 'switch-filled', 265 | 'takeaway-box', 266 | 'ticket', 267 | 'tickets', 268 | 'timer', 269 | 'toilet-paper', 270 | 'tools', 271 | 'top', 272 | 'top-left', 273 | 'top-right', 274 | 'trend-charts', 275 | 'trophy', 276 | 'trophy-base', 277 | 'turn-off', 278 | 'umbrella', 279 | 'unlock', 280 | 'upload', 281 | 'upload-filled', 282 | 'user', 283 | 'user-filled', 284 | 'van', 285 | 'video-camera', 286 | 'video-camera-filled', 287 | 'video-pause', 288 | 'video-play', 289 | 'view', 290 | 'wallet', 291 | 'wallet-filled', 292 | 'warn-triangle-filled', 293 | 'warning', 294 | 'warning-filled', 295 | 'watch', 296 | 'watermelon', 297 | 'wind-power', 298 | 'zoom-in', 299 | 'zoom-out' 300 | ] 301 | -------------------------------------------------------------------------------- /src/components/icon-select/index.ts: -------------------------------------------------------------------------------- 1 | import iconSelect from './src/Select.vue' 2 | import fontIcon from './src/iconfont' 3 | 4 | /** `IconSelect`图标选择器组件 */ 5 | const IconSelect = iconSelect 6 | /** `iconfont`组件 */ 7 | const FontIcon = fontIcon 8 | 9 | export { IconSelect, FontIcon } 10 | -------------------------------------------------------------------------------- /src/components/icon-select/src/Select.vue: -------------------------------------------------------------------------------- 1 | 86 | 87 | 145 | 146 | 198 | -------------------------------------------------------------------------------- /src/components/icon-select/src/hooks.ts: -------------------------------------------------------------------------------- 1 | import type { iconType } from './types' 2 | import { h, defineComponent, type Component } from 'vue' 3 | import { IconifyIconOnline, IconifyIconOffline, FontIcon } from '../index' 4 | 5 | /** 6 | * 支持 `iconfont`、自定义 `svg` 以及 `iconify` 中所有的图标 7 | * @see 点击查看文档图标篇 {@link https://yiming_chang.gitee.io/pure-admin-doc/pages/icon/} 8 | * @param icon 必传 图标 9 | * @param attrs 可选 iconType 属性 10 | * @returns Component 11 | */ 12 | export function useRenderIcon(icon: any, attrs?: iconType): Component { 13 | // iconfont 14 | const ifReg = /^IF-/ 15 | // typeof icon === "function" 属于SVG 16 | if (ifReg.test(icon)) { 17 | // iconfont 18 | const name = icon.split(ifReg)[1] 19 | const iconName = name.slice(0, name.indexOf(' ') == -1 ? name.length : name.indexOf(' ')) 20 | const iconType = name.slice(name.indexOf(' ') + 1, name.length) 21 | return defineComponent({ 22 | name: 'FontIcon', 23 | render() { 24 | return h(FontIcon, { 25 | icon: iconName, 26 | iconType, 27 | ...attrs 28 | }) 29 | } 30 | }) 31 | } else if (typeof icon === 'function' || typeof icon?.render === 'function') { 32 | // svg 33 | return icon 34 | } else if (typeof icon === 'object') { 35 | return defineComponent({ 36 | name: 'OfflineIcon', 37 | render() { 38 | return h(IconifyIconOffline, { 39 | icon: icon, 40 | ...attrs 41 | }) 42 | } 43 | }) 44 | } else { 45 | // 通过是否存在 : 符号来判断是在线还是本地图标,存在即是在线图标,反之 46 | return defineComponent({ 47 | name: 'Icon', 48 | render() { 49 | const IconifyIcon = icon && icon.includes(':') ? IconifyIconOnline : IconifyIconOffline 50 | return h(IconifyIcon, { 51 | icon: icon, 52 | ...attrs 53 | }) 54 | } 55 | }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/components/icon-select/src/iconfont.ts: -------------------------------------------------------------------------------- 1 | import { h, defineComponent } from 'vue' 2 | 3 | // 封装iconfont组件,默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 (https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code) 4 | export default defineComponent({ 5 | name: 'FontIcon', 6 | props: { 7 | icon: { 8 | type: String, 9 | default: '' 10 | } 11 | }, 12 | render() { 13 | const attrs = this.$attrs 14 | if (Object.keys(attrs).includes('uni') || attrs?.iconType === 'uni') { 15 | return h( 16 | 'i', 17 | { 18 | class: 'iconfont', 19 | ...attrs 20 | }, 21 | this.icon 22 | ) 23 | } else if (Object.keys(attrs).includes('svg') || attrs?.iconType === 'svg') { 24 | return h( 25 | 'svg', 26 | { 27 | class: 'icon-svg', 28 | 'aria-hidden': true 29 | }, 30 | { 31 | default: () => [ 32 | h('use', { 33 | 'xlink:href': `#${this.icon}` 34 | }) 35 | ] 36 | } 37 | ) 38 | } else { 39 | return h('i', { 40 | class: `iconfont ${this.icon}`, 41 | ...attrs 42 | }) 43 | } 44 | } 45 | }) 46 | -------------------------------------------------------------------------------- /src/components/icon-select/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface iconType { 2 | // iconify (https://docs.iconify.design/icon-components/vue/#properties) 3 | inline?: boolean 4 | width?: string | number 5 | height?: string | number 6 | horizontalFlip?: boolean 7 | verticalFlip?: boolean 8 | flip?: string 9 | rotate?: number | string 10 | color?: string 11 | horizontalAlign?: boolean 12 | verticalAlign?: boolean 13 | align?: string 14 | onLoad?: Function 15 | includes?: Function 16 | 17 | // all icon 18 | style?: object 19 | } 20 | -------------------------------------------------------------------------------- /src/components/image-upload/image-upload.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 91 | 111 | -------------------------------------------------------------------------------- /src/components/main-header/c-cpns/header-breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 8 | 29 | 30 | 45 | 46 | -------------------------------------------------------------------------------- /src/components/main-header/c-cpns/header-info.vue: -------------------------------------------------------------------------------- 1 | 8 | 19 | 20 | 66 | 67 | -------------------------------------------------------------------------------- /src/components/main-header/main-header.vue: -------------------------------------------------------------------------------- 1 | 8 | 12 | 13 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/main-menu/main-menu.vue: -------------------------------------------------------------------------------- 1 | 8 | 33 | 34 | 62 | 86 | -------------------------------------------------------------------------------- /src/components/page-comment/page-comment.vue: -------------------------------------------------------------------------------- 1 | 8 | 18 | 19 | 35 | 36 | -------------------------------------------------------------------------------- /src/components/page-content/page-content.vue: -------------------------------------------------------------------------------- 1 | 8 | 77 | 78 | 158 | 165 | -------------------------------------------------------------------------------- /src/components/page-editor/page-editor.vue: -------------------------------------------------------------------------------- 1 | 8 | 77 | 78 | 93 | 94 | 102 | -------------------------------------------------------------------------------- /src/components/page-modal/page-modal.vue: -------------------------------------------------------------------------------- 1 | 8 | 81 | 82 | 123 | 129 | -------------------------------------------------------------------------------- /src/components/page-panel/page-panel.vue: -------------------------------------------------------------------------------- 1 | 8 | 49 | 50 | 72 | 79 | -------------------------------------------------------------------------------- /src/components/page-search/page-search.vue: -------------------------------------------------------------------------------- 1 | 8 | 58 | 59 | 128 | 140 | -------------------------------------------------------------------------------- /src/enums/scan-status.enum.ts: -------------------------------------------------------------------------------- 1 | export enum ScanStatusEnum { 2 | // 未扫码 3 | NotScan = 0, 4 | // 已扫码 5 | Scanned = 1, 6 | // 已确认 7 | Confirmed = 2, 8 | // 已取消 9 | Canceled = 3, 10 | // 已过期 11 | Expired = 4 12 | } 13 | -------------------------------------------------------------------------------- /src/global/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-23 12:36:09 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2022-12-23 12:43:00 6 | * @Description: 7 | */ 8 | 9 | import type { App } from 'vue' 10 | import resgisterIcons from './register-icons' 11 | 12 | export default function registerGlobal(app: App) { 13 | app.use(resgisterIcons) 14 | } 15 | -------------------------------------------------------------------------------- /src/global/register-icons.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-23 12:35:52 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2022-12-25 14:38:50 6 | * @Description: 7 | */ 8 | // import * as ElementPlusIconsVue from '@element-plus/icons-vue' 9 | import type { App } from 'vue' 10 | 11 | //废弃 12 | export default function resgisterIcons(app: App) { 13 | // for (const [key, component] of Object.entries(ElementPlusIconsVue)) { 14 | // app.component(key, component) 15 | // } 16 | } 17 | -------------------------------------------------------------------------------- /src/hooks/useAddDept2Config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-09 17:20:04 4 | * @LastEditors: Leo l024983409@qq.com 5 | * @LastEditTime: 2023-11-15 10:22:47 6 | * @Description: 7 | */ 8 | import useMainStore from '@/store/main/main' 9 | 10 | const useAddDept2Config = (config: any) => { 11 | const mainStore = useMainStore() 12 | 13 | const configRef = computed(() => { 14 | const departmentList = 15 | mainStore.departmentList?.map((item: any) => { 16 | return { 17 | label: item.name, 18 | value: item.id 19 | } 20 | }) || [] 21 | for (const item of config.formItems) { 22 | if (item.prop == 'parentId' || item.prop == 'departmentId') { 23 | item.options = [...departmentList] 24 | } 25 | } 26 | return config 27 | }) 28 | return { 29 | configRef 30 | } 31 | } 32 | 33 | export default useAddDept2Config 34 | -------------------------------------------------------------------------------- /src/hooks/useAddGoodsCategory2Config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-09 17:20:04 4 | * @LastEditors: Leo l024983409@qq.com 5 | * @LastEditTime: 2023-11-15 10:22:47 6 | * @Description: 7 | */ 8 | import useMainStore from '@/store/main/main' 9 | 10 | const useAddGoodsCategoryConfig = (config: any) => { 11 | const mainStore = useMainStore() 12 | 13 | const configRef = computed(() => { 14 | const categoryList = 15 | mainStore.categoryList?.map((item: any) => { 16 | return { 17 | label: item.name, 18 | value: item.id 19 | } 20 | }) || [] 21 | for (const item of config.formItems) { 22 | if (item.prop == 'categoryId') { 23 | item.options = [...categoryList] 24 | } 25 | } 26 | return config 27 | }) 28 | return { 29 | configRef 30 | } 31 | } 32 | 33 | export default useAddGoodsCategoryConfig 34 | -------------------------------------------------------------------------------- /src/hooks/useAddRole2Config.ts: -------------------------------------------------------------------------------- 1 | import useMainStore from '@/store/main/main' 2 | 3 | const useAddRole2Config = (config: any) => { 4 | const mainStore = useMainStore() 5 | 6 | const configRef = computed(() => { 7 | const roleList = 8 | mainStore.roleList?.map((item: any) => { 9 | return { 10 | label: item.name, 11 | value: item.id 12 | } 13 | }) || [] 14 | for (const item of config.formItems) { 15 | if (item.prop == 'roleId') { 16 | item.options = [...roleList] 17 | } 18 | } 19 | return config 20 | }) 21 | return { 22 | configRef 23 | } 24 | } 25 | 26 | export default useAddRole2Config 27 | -------------------------------------------------------------------------------- /src/hooks/usePageContent.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-09 11:52:19 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 11:57:52 6 | * @Description: 7 | */ 8 | import type PageContent from '@/components/page-content/page-content.vue' 9 | import type { MaybeRef } from 'vue' 10 | import { useToValueDeep } from './useToValueDeep' 11 | 12 | type ResetCallback = () => void 13 | const usePageContent = (otherSearch?: Record, resetCallback?: ResetCallback) => { 14 | const pageContentRef = ref>() 15 | function handleQuery(searchData: any) { 16 | pageContentRef.value?.queryDataList({ ...searchData, ...useToValueDeep(otherSearch) }) 17 | } 18 | 19 | function handleReset() { 20 | resetCallback && resetCallback() 21 | pageContentRef.value?.resetDataList() 22 | } 23 | 24 | return { 25 | pageContentRef, 26 | handleQuery, 27 | handleReset 28 | } 29 | } 30 | 31 | export default usePageContent 32 | -------------------------------------------------------------------------------- /src/hooks/usePageModal.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-09 11:52:29 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-11 17:57:19 6 | * @Description: 7 | */ 8 | import type PageModal from '@/components/page-modal/page-modal.vue' 9 | 10 | type CreateCallBack = (data?: any) => void 11 | type EditCallBack = (data?: any) => void 12 | 13 | const usePageModal = (createCallBack?: CreateCallBack, editCallBack?: EditCallBack) => { 14 | const pageModalRef = ref>() 15 | 16 | function handleCreate() { 17 | createCallBack && createCallBack() 18 | pageModalRef.value?.setDialogVisible() 19 | } 20 | 21 | function handleEdit(info: any) { 22 | editCallBack && editCallBack(info) 23 | pageModalRef.value?.setDialogVisible(false, info) 24 | } 25 | 26 | return { 27 | pageModalRef, 28 | handleCreate, 29 | handleEdit 30 | } 31 | } 32 | 33 | export default usePageModal 34 | -------------------------------------------------------------------------------- /src/hooks/useToValueDeep.ts: -------------------------------------------------------------------------------- 1 | import { toValue, type MaybeRefOrGetter, type Ref } from 'vue' 2 | 3 | export type ToValueDeep = T extends Ref 4 | ? ToValueDeep 5 | : T extends Array 6 | ? Array> 7 | : T extends object 8 | ? { 9 | [K in keyof T]: ToValueDeep 10 | } 11 | : T 12 | 13 | /** 14 | * @description: 递归将ref转换为value 15 | * @param {MaybeRefOrGetter} value 16 | * @return {ToValueDeep} 17 | */ 18 | export function useToValueDeep(value: MaybeRefOrGetter): ToValueDeep { 19 | const _val = toValue(value) 20 | if (isRef(_val)) { 21 | return useToValueDeep(_val) 22 | } else if (Array.isArray(_val)) { 23 | return _val.map((v) => useToValueDeep(v)) as ToValueDeep 24 | } else if (typeof _val === 'object' && _val !== null) { 25 | return Object.fromEntries(Object.entries(_val).map(([k, v]) => [k, useToValueDeep(v)])) as ToValueDeep 26 | } 27 | 28 | return _val as ToValueDeep 29 | } 30 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-20 21:47:04 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-16 23:41:06 6 | * @Description: 7 | */ 8 | import '@unocss/reset/tailwind.css' 9 | import { createApp } from 'vue' 10 | import App from './App.vue' 11 | 12 | import '@/assets/css/index.less' 13 | import registerGlobal from './global' 14 | import router from './router' 15 | import store from './store' 16 | 17 | import 'uno.css' 18 | 19 | const app = createApp(App) 20 | app.use(registerGlobal) 21 | app.use(store) 22 | 23 | app.use(router) 24 | app.mount('#app') 25 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-21 19:09:44 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-17 16:37:59 6 | * @Description: 7 | */ 8 | import { createRouter, createWebHashHistory } from 'vue-router' 9 | import type { RouteRecordRaw } from 'vue-router' 10 | import { firstShowMenu } from '@/utils/map-util' 11 | 12 | const routes: RouteRecordRaw[] = [ 13 | { 14 | path: '/', 15 | redirect: '/main' 16 | }, 17 | { 18 | path: '/main', 19 | name: 'main', 20 | component: () => import('@/views/main/main.vue'), 21 | children: [] 22 | }, 23 | { 24 | path: '/login', 25 | name: 'login', 26 | component: () => import('@/views/login/login.vue') 27 | }, 28 | { 29 | path: '/:pathMatch(.*)', 30 | component: () => import('@/views/not-found/not-found.vue') 31 | } 32 | ] 33 | 34 | const router = createRouter({ 35 | history: createWebHashHistory(), 36 | routes 37 | }) 38 | 39 | router.beforeEach((to, from) => { 40 | const { token } = useLocalStorage('login', { token: '' }).value 41 | if (to.path == '/main' && (!token || token == '')) { 42 | return '/login' 43 | } 44 | //如果是第一次进来,则跳转到第一个菜单 45 | if (to.path == '/main') { 46 | return firstShowMenu?.url 47 | } 48 | }) 49 | 50 | export default router 51 | -------------------------------------------------------------------------------- /src/router/main/analysis/dashboard/dashboard.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | path: '/main/analysis/dashboard', 3 | component: () => import('@/views/main/analysis/dashboard/dashboard.vue') 4 | } 5 | -------------------------------------------------------------------------------- /src/router/main/analysis/overview/overview.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | path: '/main/analysis/overview', 3 | component: () => import('@/views/main/analysis/overview/overview.vue') 4 | } 5 | -------------------------------------------------------------------------------- /src/router/main/product/category/category.ts: -------------------------------------------------------------------------------- 1 | const category = () => import('@/views/main/product/category/category.vue') 2 | export default { 3 | path: '/main/product/category', 4 | name: 'category', 5 | component: category, 6 | children: [] 7 | } 8 | -------------------------------------------------------------------------------- /src/router/main/product/goods/goods.ts: -------------------------------------------------------------------------------- 1 | const goods = () => import('@/views/main/product/goods/goods.vue') 2 | export default { 3 | path: '/main/product/goods', 4 | name: 'goods', 5 | component: goods, 6 | children: [] 7 | } 8 | -------------------------------------------------------------------------------- /src/router/main/story/chat/chat.ts: -------------------------------------------------------------------------------- 1 | const chat = () => import('@/views/main/story/chat/chat.vue') 2 | export default { 3 | path: '/main/story/chat', 4 | name: 'chat', 5 | component: chat, 6 | children: [] 7 | } 8 | -------------------------------------------------------------------------------- /src/router/main/story/list/list.ts: -------------------------------------------------------------------------------- 1 | const list = () => import('@/views/main/story/list/list.vue') 2 | export default { 3 | path: '/main/story/list', 4 | name: 'list', 5 | component: list, 6 | children: [] 7 | } 8 | -------------------------------------------------------------------------------- /src/router/main/system/department/department.ts: -------------------------------------------------------------------------------- 1 | const department = () => import('@/views/main/system/department/department.vue') 2 | export default { 3 | path: '/main/system/department', 4 | name: 'department', 5 | component: department, 6 | children: [] 7 | } 8 | -------------------------------------------------------------------------------- /src/router/main/system/menu/menu.ts: -------------------------------------------------------------------------------- 1 | const menu = () => import('@/views/main/system/menu/menu.vue') 2 | export default { 3 | path: '/main/system/menu', 4 | name: 'menu', 5 | component: menu, 6 | children: [] 7 | } 8 | -------------------------------------------------------------------------------- /src/router/main/system/role/role.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | path: '/main/system/role', 3 | component: () => import('@/views/main/system/role/role.vue') 4 | } 5 | -------------------------------------------------------------------------------- /src/router/main/system/user/user.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-02 13:09:12 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-02 14:45:40 6 | * @Description: 7 | */ 8 | export default { 9 | path: '/main/system/user', 10 | name: 'user', 11 | component: () => import('@/views/main/system/user/user.vue') 12 | } 13 | -------------------------------------------------------------------------------- /src/services/config/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-21 19:29:40 4 | * @LastEditors: Leo l024983409@qq.com 5 | * @LastEditTime: 2023-10-12 12:32:49 6 | * @Description: 7 | */ 8 | export const BASE_URL = import.meta.env.VITE_BASE_URL 9 | 10 | export const TIME_OUT = 30000 11 | -------------------------------------------------------------------------------- /src/services/config/type.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-21 19:35:13 4 | * @LastEditors: Leo l024983409@qq.com 5 | * @LastEditTime: 2023-11-15 09:59:08 6 | * @Description: 7 | */ 8 | import type { AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios' 9 | 10 | export interface AppInterceptor { 11 | requestSuccessFn?: (config: InternalAxiosRequestConfig & { showLoading?: boolean; skipError?: boolean }) => InternalAxiosRequestConfig 12 | requestFailureFn?: (err: any) => any 13 | responseSuccessFn?: (res: T) => T 14 | responseFailureFn?: (err: any) => any 15 | } 16 | 17 | export default interface AppRequestConfig extends AxiosRequestConfig { 18 | interceptors?: AppInterceptor 19 | showLoading?: boolean 20 | skipError?: boolean 21 | } 22 | -------------------------------------------------------------------------------- /src/services/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-21 19:28:46 4 | * @LastEditors: Leo l024983409@qq.com 5 | * @LastEditTime: 2023-11-15 11:19:13 6 | * @Description: 7 | */ 8 | import router from '@/router' 9 | import useLoginStore from '@/store/login/login' 10 | import { AxiosError } from 'axios' 11 | import { BASE_URL, TIME_OUT } from './config' 12 | import AppRequest from './request' 13 | 14 | let ElLoadingInstance: any = null 15 | let skipError: boolean = false 16 | 17 | const appRequest = new AppRequest({ 18 | baseURL: BASE_URL, 19 | timeout: TIME_OUT, 20 | interceptors: { 21 | requestSuccessFn(config) { 22 | // console.log('实例请求成功') 23 | if (config.skipError != undefined) { 24 | skipError = config.skipError 25 | } 26 | if (config!.showLoading) { 27 | ElLoadingInstance = ElLoading.service({ 28 | fullscreen: true, 29 | lock: true, 30 | text: '加载中...', 31 | background: 'rgba(0, 0, 0, 0.5)' 32 | }) 33 | } 34 | //添加token 35 | const loginStore = useLoginStore() 36 | if (loginStore.token.length > 0 && config.headers) { 37 | config!.headers.Authorization = 'Bearer ' + loginStore.token 38 | } 39 | return config 40 | }, 41 | responseSuccessFn(res) { 42 | if (res instanceof AxiosError) { 43 | if (skipError) { 44 | skipError = false 45 | return res 46 | } 47 | if (ElLoadingInstance) { 48 | ElLoadingInstance.close() 49 | } 50 | //如果是401,则跳转到登录页面 51 | if (res.response?.status == 401) { 52 | ElMessage.error({ 53 | key: '401', 54 | grouping: true, 55 | message: res.response?.data?.message 56 | }) 57 | 58 | router.replace('/login') 59 | return res 60 | } 61 | ElMessage.error(res.response?.data?.message) 62 | 63 | return res 64 | } 65 | if (ElLoadingInstance) { 66 | ElLoadingInstance.close() 67 | } 68 | return res 69 | } 70 | } 71 | }) 72 | 73 | export default appRequest 74 | -------------------------------------------------------------------------------- /src/services/login/login.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-23 17:35:00 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2022-12-24 14:48:03 6 | * @Description: 7 | */ 8 | import type { CheckStatus, GenerateCodeInfo, LoginAccount, LoginInfo, MenuInfo, UserInfo } from '@/types' 9 | import appRequest from '..' 10 | 11 | export function accountLoginRequest(account: LoginAccount) { 12 | return appRequest.post({ 13 | url: '/login', 14 | data: account 15 | }) 16 | } 17 | 18 | export function getUserInfoRequest(id: number) { 19 | return appRequest.get({ 20 | url: '/users/' + id 21 | }) 22 | } 23 | 24 | export function getUserMenusRequest(id: number) { 25 | return appRequest.get({ 26 | url: `/role/` + id + `/menu` 27 | }) 28 | } 29 | 30 | export function generateQrcodeRequest() { 31 | return appRequest.get({ 32 | url: '/qrcode/generate' 33 | }) 34 | } 35 | 36 | export function checkStatusRequest(id: string) { 37 | return appRequest.get({ 38 | url: '/qrcode/check/' + id, 39 | showLoading: false, 40 | skipError: true 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /src/services/main/analysis/analysis.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-13 13:26:23 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-13 21:30:25 6 | * @Description: 7 | */ 8 | import appRequest from '@/services' 9 | import type { GoodsAddressSale, GoodsAmountInfo, GoodsCategory, GoodsCategoryFavor, GoodsSaleTop10 } from '@/types/main/analysis/analysis' 10 | 11 | export function getGoodsAmountList() { 12 | return appRequest.get({ 13 | url: '/goods/amount/list' 14 | }) 15 | } 16 | 17 | export function getGoodAddressSale() { 18 | return appRequest.get({ 19 | url: '/goods/address/sale' 20 | }) 21 | } 22 | 23 | export function getGoodsCategoryCount() { 24 | return appRequest.get({ 25 | url: '/goods/category/count' 26 | }) 27 | } 28 | 29 | export function getGoodsCategorySale() { 30 | return appRequest.get({ 31 | url: '/goods/category/sale' 32 | }) 33 | } 34 | 35 | export function getGoodsCategoryFavor() { 36 | return appRequest.get({ 37 | url: '/goods/category/favor' 38 | }) 39 | } 40 | 41 | export function getGoodsSaleTop10() { 42 | return appRequest.get({ 43 | url: '/goods/sale/top10' 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /src/services/main/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-05 14:21:06 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-11 21:23:50 6 | * @Description: 7 | */ 8 | import type { DepartmentList, ICategoryListItem, MenuList, RoleList } from '@/types' 9 | import appRequest from '..' 10 | 11 | export function postRoleList() { 12 | return appRequest.post({ 13 | url: '/role/list', 14 | data: { 15 | offset: 0, 16 | size: 1000 17 | } 18 | }) 19 | } 20 | 21 | export function postDepartmentList() { 22 | return appRequest.post({ 23 | url: '/department/list', 24 | data: { 25 | offset: 0, 26 | size: 1000 27 | } 28 | }) 29 | } 30 | 31 | export function postMenuList() { 32 | return appRequest.post({ 33 | url: '/menu/list' 34 | }) 35 | } 36 | 37 | export function createNewData(pageName: string, data: any) { 38 | return appRequest.post({ 39 | url: `/${pageName}`, 40 | data 41 | }) 42 | } 43 | 44 | export function patchData(pageName: string, id: number, data: any) { 45 | return appRequest.patch({ 46 | url: `/${pageName}/${id}`, 47 | data 48 | }) 49 | } 50 | 51 | export function queryDataList(pageName: string, data: any) { 52 | return appRequest.post({ 53 | url: `/${pageName}/list`, 54 | data 55 | }) 56 | } 57 | 58 | export function deleteDataById(pageName: string, id: number) { 59 | return appRequest.delete({ 60 | url: `/${pageName}/${id}` 61 | }) 62 | } 63 | 64 | export function queryDataById(pageName: string, id: number) { 65 | return appRequest.get({ 66 | url: `/${pageName}/${id}` 67 | }) 68 | } 69 | 70 | export function queryCategoryList() { 71 | return appRequest.get({ 72 | url: '/category/all' 73 | }) 74 | } 75 | -------------------------------------------------------------------------------- /src/services/main/story/story.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-17 17:39:56 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-17 17:43:16 6 | * @Description: 7 | */ 8 | 9 | import appRequest from '@/services' 10 | import type { CreateStoryData, StoryList, StoryListParams } from '@/types/main/story/story' 11 | 12 | export function createNewStory(data: CreateStoryData) { 13 | return appRequest.post({ 14 | url: '/story', 15 | data 16 | }) 17 | } 18 | 19 | export function postStoryList(data: StoryListParams) { 20 | return appRequest.post({ 21 | url: '/story/list', 22 | data 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /src/services/main/system/system.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-04 19:31:24 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-05 18:32:02 6 | * @Description: 7 | */ 8 | import type { CreateUser, PatchUser, SystemUserList } from '@/types' 9 | import appRequest from '../..' 10 | 11 | export function postUserList(data: any) { 12 | return appRequest.post({ 13 | url: '/users/list', 14 | data 15 | }) 16 | } 17 | 18 | export function createUser(data: CreateUser) { 19 | return appRequest.post({ 20 | url: '/users', 21 | data 22 | }) 23 | } 24 | 25 | export function deleteUserById(id: number) { 26 | return appRequest.delete({ 27 | url: `/users/${id}` 28 | }) 29 | } 30 | 31 | export function patchUserById(id: number, data: PatchUser) { 32 | return appRequest.patch({ 33 | url: `/users/${id}`, 34 | data 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /src/services/request/index.ts: -------------------------------------------------------------------------------- 1 | import type { IBaseResult } from '@/types' 2 | import type { AxiosInstance } from 'axios' 3 | import axios from 'axios' 4 | import type AppRequestConfig from '../config/type' 5 | 6 | class AppRequest { 7 | instance: AxiosInstance 8 | constructor(config: AppRequestConfig) { 9 | this.instance = axios.create(config) 10 | //全局拦截器 11 | this.instance.interceptors.request.use( 12 | (config) => { 13 | // console.log('全局请求成功') 14 | return config 15 | }, 16 | (err) => { 17 | console.log('全局请求失败') 18 | 19 | return err 20 | } 21 | ) 22 | this.instance.interceptors.response.use( 23 | (res) => { 24 | // console.log('全局响应成功') 25 | return res.data 26 | }, 27 | (err) => { 28 | return err 29 | } 30 | ) 31 | 32 | // 实例拦截器 33 | this.instance.interceptors.request.use(config.interceptors?.requestSuccessFn, config.interceptors?.requestFailureFn) 34 | this.instance.interceptors.response.use(config.interceptors?.responseSuccessFn, config.interceptors?.responseFailureFn) 35 | } 36 | 37 | request(config: AppRequestConfig>) { 38 | //方法拦截器 39 | if (config.interceptors?.requestSuccessFn) { 40 | config = config.interceptors.requestSuccessFn(config as any) 41 | } 42 | 43 | return new Promise>((resolve, reject) => { 44 | return this.instance 45 | .request>(config) 46 | .then((res) => { 47 | if (config.interceptors?.responseSuccessFn) { 48 | res = config.interceptors.responseSuccessFn(res) 49 | } 50 | resolve(res) 51 | }) 52 | .catch((err) => { 53 | reject(err) 54 | }) 55 | }) 56 | } 57 | get(config: AppRequestConfig>) { 58 | return this.request({ ...config, method: 'GET' }) 59 | } 60 | post(config: AppRequestConfig>) { 61 | return this.request({ ...config, method: 'POST' }) 62 | } 63 | delete(config: AppRequestConfig>) { 64 | return this.request({ ...config, method: 'DELETE' }) 65 | } 66 | patch(config: AppRequestConfig>) { 67 | return this.request({ ...config, method: 'PATCH' }) 68 | } 69 | } 70 | 71 | export default AppRequest 72 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-21 18:05:02 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-15 18:21:04 6 | * @Description: 7 | */ 8 | import { createPinia } from 'pinia' 9 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 10 | import type { App } from 'vue' 11 | import useLoginStore from './login/login' 12 | 13 | const store = createPinia() 14 | 15 | //持久化 也可以选择vueuse 16 | store.use(piniaPluginPersistedstate) 17 | 18 | export default function registerStore(app: App) { 19 | app.use(store) 20 | 21 | //在这里读取本地用户路由 22 | const loginState = useLoginStore() 23 | if (loginState.token && loginState.token.length > 0) { 24 | loginState.refreshData() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/store/login/login.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-23 17:36:44 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-18 13:58:02 6 | * @Description: 7 | */ 8 | import router from '@/router' 9 | import { accountLoginRequest, getUserInfoRequest, getUserMenusRequest } from '@/services/login/login' 10 | import type { LoginAccount, MenuInfo, UserInfo } from '@/types' 11 | import mapMenu2Routes from '@/utils/map-util' 12 | import { defineStore } from 'pinia' 13 | import useMainStore from '../main/main' 14 | 15 | const useLoginStore = defineStore('login', { 16 | state: () => { 17 | return { 18 | token: '', 19 | isLogining: false, 20 | userInfo: {} as UserInfo, 21 | menuInfo: [] as MenuInfo[] 22 | } 23 | }, 24 | actions: { 25 | async accountLoginAction(account: LoginAccount) { 26 | this.isLogining = true 27 | // 1.登录拿到token和用户id 28 | try { 29 | const { 30 | data: { token, id } 31 | } = await accountLoginRequest(account) 32 | this.token = token 33 | // 2.根据用户id请求用户信息 34 | const { data: userInfo } = await getUserInfoRequest(id) 35 | this.userInfo = userInfo 36 | //3.根据用户角色id请求菜单 37 | const { data: menuInfo } = await getUserMenusRequest(userInfo.role.id) 38 | 39 | // 4.根据用户菜单匹配本地route动态注册路由 40 | this.menuInfo = menuInfo 41 | await this.refreshData() 42 | ElMessage.success({ message: '登陆成功' }) 43 | this.isLogining = false 44 | router.push({ path: '/main' }) 45 | } catch (error) { 46 | this.isLogining = false 47 | } 48 | }, 49 | async refreshData() { 50 | // 防止刷新数据丢失 51 | // 1. 从菜单信息中遍历得出路由,动态注册路由 52 | const mainRoutes = mapMenu2Routes(this.menuInfo) 53 | mainRoutes.forEach((mainRoutesItem) => router.addRoute('main', mainRoutesItem)) 54 | useLocalStorage('mainRoutes', mainRoutes) 55 | //2.获取部门和角色列表 56 | const mainStore = useMainStore() 57 | await mainStore.postAllListAction() 58 | } 59 | }, 60 | persist: { 61 | paths: ['token', 'userInfo', 'menuInfo'], 62 | key: 'login' 63 | } 64 | }) 65 | 66 | export default useLoginStore 67 | -------------------------------------------------------------------------------- /src/store/main/analysis/analysis.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-13 13:26:23 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-13 21:39:24 6 | * @Description: 7 | */ 8 | 9 | import { 10 | getGoodAddressSale, 11 | getGoodsAmountList, 12 | getGoodsCategoryCount, 13 | getGoodsCategorySale, 14 | getGoodsCategoryFavor, 15 | getGoodsSaleTop10 16 | } from '@/services/main/analysis/analysis' 17 | import type { GoodsAddressSale, GoodsAmountInfo, GoodsCategory, GoodsCategoryFavor, GoodsSaleTop10 } from '@/types/main/analysis/analysis' 18 | import { defineStore } from 'pinia' 19 | 20 | const useAnalysisStore = defineStore('analysis', () => { 21 | const goodsAmountList = ref([]) 22 | const goodAddressSale = ref([]) 23 | const goodsCategoryCount = ref([]) 24 | const goodsCategorySale = ref([]) 25 | const goodsCategoryFavor = ref([]) 26 | const goodsSaleTop10 = ref([]) 27 | 28 | async function getGoodsAmountListAction() { 29 | const res = await getGoodsAmountList() 30 | goodsAmountList.value = res.data 31 | } 32 | async function getGoodsAddressSaleAction() { 33 | const res = await getGoodAddressSale() 34 | goodAddressSale.value = res.data 35 | } 36 | async function getGoodsCategoryCountAction() { 37 | const res = await getGoodsCategoryCount() 38 | goodsCategoryCount.value = res.data 39 | } 40 | async function getGoodsCategorySaleAction() { 41 | const res = await getGoodsCategorySale() 42 | goodsCategorySale.value = res.data 43 | } 44 | async function getGoodsCategoryFavorAction() { 45 | const res = await getGoodsCategoryFavor() 46 | goodsCategoryFavor.value = res.data 47 | } 48 | async function getGoodsSaleTop10Action() { 49 | const res = await getGoodsSaleTop10() 50 | goodsSaleTop10.value = res.data 51 | } 52 | 53 | async function getDashBoardDataAction() { 54 | getGoodsAmountListAction() 55 | getGoodsAddressSaleAction() 56 | getGoodsCategoryCountAction() 57 | getGoodsCategorySaleAction() 58 | getGoodsCategoryFavorAction() 59 | getGoodsSaleTop10Action() 60 | } 61 | 62 | return { 63 | goodsAmountList, 64 | goodAddressSale, 65 | goodsCategoryCount, 66 | goodsCategorySale, 67 | goodsCategoryFavor, 68 | goodsSaleTop10, 69 | getDashBoardDataAction 70 | } 71 | }) 72 | 73 | export default useAnalysisStore 74 | -------------------------------------------------------------------------------- /src/store/main/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-26 13:28:47 4 | * @LastEditors: Leo l024983409@qq.com 5 | * @LastEditTime: 2023-11-15 10:39:16 6 | * @Description: 7 | */ 8 | import { 9 | createNewData, 10 | deleteDataById, 11 | patchData, 12 | postDepartmentList, 13 | postMenuList, 14 | postRoleList, 15 | queryCategoryList, 16 | queryDataList 17 | } from '@/services/main/main' 18 | import type { DepartmentInfo, RoleInfo } from '@/types' 19 | import type { ICategoryListItem, MenuInfo } from '@/types/main/main' 20 | import { defineStore } from 'pinia' 21 | 22 | const useMainStore = defineStore('main', () => { 23 | //判断菜单是否展开 24 | const isMenuExpand = ref(false) 25 | 26 | const isLoading = ref(false) 27 | 28 | //请求角色列表 29 | const roleList = ref() 30 | //角色列表 31 | const departmentList = ref() 32 | 33 | const categoryList = ref([]) 34 | 35 | //菜单列表 36 | const menuList = ref() 37 | 38 | async function postAllListAction() { 39 | const [roleListRes, departmentListRes, menuListRes, categoryListRes] = await Promise.all([ 40 | postRoleList(), 41 | postDepartmentList(), 42 | postMenuList(), 43 | queryCategoryList() 44 | ]) 45 | roleList.value = roleListRes.data?.list 46 | departmentList.value = departmentListRes.data?.list 47 | menuList.value = menuListRes.data?.list 48 | categoryList.value = categoryListRes.data 49 | } 50 | 51 | //通用信息 52 | const dataList = ref([]) 53 | const count = ref(0) 54 | const pageSize = ref(10) 55 | const currentPage = ref(1) 56 | 57 | //创建新的数据 58 | async function createNewDataAction(pageName: string, data: any) { 59 | const res = await createNewData(pageName, data) 60 | showMessage(res) 61 | resetPageAction() 62 | queryDataListAction(pageName) 63 | queryAgain(pageName) 64 | } 65 | 66 | //修改数据 67 | async function patchDataAction(pageName: string, id: number, data: any) { 68 | const res = await patchData(pageName, id, data) 69 | showMessage(res) 70 | queryDataListAction(pageName) 71 | queryAgain(pageName) 72 | } 73 | 74 | //查询数据 75 | async function queryDataListAction(pageName: string, data?: any) { 76 | isLoading.value = true 77 | const res = await queryDataList(pageName, { 78 | size: pageSize.value, 79 | offset: (currentPage.value - 1) * pageSize.value, 80 | ...data 81 | }) 82 | dataList.value = res.data?.list 83 | count.value = res.data?.totalCount 84 | isLoading.value = false 85 | } 86 | 87 | //删除数据 88 | async function deleteDataByIdAction(pageName: string, id: number) { 89 | const res = await deleteDataById(pageName, id) 90 | showMessage(res) 91 | queryDataListAction(pageName) 92 | queryAgain(pageName) 93 | } 94 | 95 | //提示信息 96 | function showMessage(res: any) { 97 | if (res.code == 200) { 98 | ElMessage.success(res.message) 99 | } 100 | } 101 | 102 | //重置页码和大小 103 | function resetPageAction() { 104 | pageSize.value = 10 105 | currentPage.value = 1 106 | } 107 | 108 | async function postMenuListDataAction() { 109 | const menuListRes = await postMenuList() 110 | menuList.value = menuListRes.data?.list 111 | } 112 | 113 | async function queryCategoryListAction() { 114 | const categoryListRes = await queryCategoryList() 115 | categoryList.value = categoryListRes.data 116 | } 117 | 118 | async function postRoleListAction() { 119 | const roleListRes = await postRoleList() 120 | roleList.value = roleListRes.data?.list 121 | } 122 | 123 | async function postDepartmentListAction() { 124 | const departmentListRes = await postDepartmentList() 125 | departmentList.value = departmentListRes.data?.list 126 | } 127 | 128 | async function queryAllUserNameAction() {} 129 | 130 | //根据页面名称判断是否重新请求列表 131 | function queryAgain(pageName: string) { 132 | switch (pageName) { 133 | case 'department': 134 | postDepartmentListAction() 135 | break 136 | case 'role': 137 | postRoleListAction() 138 | break 139 | case 'menu': 140 | postMenuListDataAction() 141 | break 142 | case 'category': 143 | queryCategoryListAction() 144 | break 145 | default: 146 | break 147 | } 148 | } 149 | 150 | return { 151 | isLoading, 152 | isMenuExpand, 153 | roleList, 154 | departmentList, 155 | menuList, 156 | categoryList, 157 | postAllListAction, 158 | postMenuListDataAction, 159 | queryCategoryListAction, 160 | dataList, 161 | count, 162 | pageSize, 163 | currentPage, 164 | createNewDataAction, 165 | patchDataAction, 166 | queryDataListAction, 167 | deleteDataByIdAction, 168 | resetPageAction 169 | } 170 | }) 171 | 172 | export default useMainStore 173 | -------------------------------------------------------------------------------- /src/store/main/story/story.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-17 17:43:57 4 | * @LastEditors: Leo l024983409@qq.com 5 | * @LastEditTime: 2023-11-15 10:39:20 6 | * @Description: 7 | */ 8 | 9 | import { createNewStory, postStoryList } from '@/services/main/story/story' 10 | import type { CreateStoryData, StoryInfo } from '@/types/main/story/story' 11 | import { defineStore } from 'pinia' 12 | 13 | const useStoryStore = defineStore('story', () => { 14 | const pageSize = ref(5) 15 | 16 | const storyList = ref([]) 17 | const count = ref(0) 18 | 19 | async function createNewStoryAction(data: CreateStoryData) { 20 | const res = await createNewStory(data) 21 | 22 | if (res.code == 200) { 23 | ElMessage.success(res.message) 24 | } 25 | } 26 | 27 | async function postStoryListAction() { 28 | const res = await postStoryList({ 29 | offset: 0, 30 | size: pageSize.value 31 | }) 32 | count.value = res.data?.totalCount 33 | storyList.value = res.data?.list 34 | } 35 | 36 | return { 37 | createNewStoryAction, 38 | pageSize, 39 | count, 40 | storyList, 41 | postStoryListAction 42 | } 43 | }) 44 | 45 | export default useStoryStore 46 | -------------------------------------------------------------------------------- /src/store/main/system/system.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-05 10:59:46 4 | * @LastEditors: Leo l024983409@qq.com 5 | * @LastEditTime: 2023-11-15 10:39:26 6 | * @Description: 7 | */ 8 | import { createUser, deleteUserById, patchUserById, postUserList } from '@/services/main/system/system' 9 | import type { CreateUser, PatchUser, SystemUserInfo } from '@/types' 10 | import { defineStore } from 'pinia' 11 | 12 | const useSystemStore = defineStore('system', () => { 13 | const userList = ref() 14 | const userCounts = ref(0) 15 | const pageSize = ref(10) 16 | const currentPage = ref(1) 17 | 18 | //请求用户列表 19 | async function postUserListAction(data: any = { size: 10, offset: 0 }) { 20 | const { 21 | data: { list, totalCount } 22 | } = await postUserList(data) 23 | userList.value = list 24 | userCounts.value = totalCount 25 | } 26 | 27 | //创建用户 28 | async function createUserAction(data: CreateUser) { 29 | const res = await createUser(data) 30 | refreshUserList(res) 31 | } 32 | 33 | //删除用户 34 | async function deleteUserAction(id: number) { 35 | const res = await deleteUserById(id) 36 | refreshUserList(res) 37 | } 38 | 39 | //修改用户 40 | async function patchUserAction(id: number, userInfo: PatchUser) { 41 | const res = await patchUserById(id, userInfo) 42 | refreshUserList(res) 43 | } 44 | 45 | //刷新用户列表 46 | function refreshUserList(res: any) { 47 | if (res.code == 200) { 48 | ElMessage.success(res.message) 49 | //删除成功,则重新请求列表 50 | postUserListAction() 51 | } 52 | } 53 | 54 | return { 55 | pageSize, 56 | currentPage, 57 | userList, 58 | userCounts, 59 | postUserListAction, 60 | deleteUserAction, 61 | createUserAction, 62 | patchUserAction 63 | } 64 | }) 65 | 66 | export default useSystemStore 67 | -------------------------------------------------------------------------------- /src/types/config/appFormItem.config.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-08 15:13:48 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-10 17:54:28 6 | * @Description: 7 | */ 8 | 9 | import type { FormItemProps } from 'element-plus' 10 | 11 | interface SelectOptions { 12 | label: string 13 | value: string | number 14 | } 15 | 16 | export interface AppFormItems extends Partial { 17 | initialValue?: string 18 | type?: 'input' | 'select' | 'date-picker' | 'number' | 'upload' | 'custom' 19 | placeholder?: string 20 | options?: SelectOptions[] 21 | } 22 | -------------------------------------------------------------------------------- /src/types/config/modal.config.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-08 14:57:26 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-08 15:15:35 6 | * @Description: 7 | */ 8 | 9 | import type { AppFormItems } from './appFormItem.config' 10 | 11 | export interface ModalConfig { 12 | pageName: string 13 | title: string 14 | formItems: AppFormItems[] 15 | } 16 | -------------------------------------------------------------------------------- /src/types/config/seach.config.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-06 20:50:06 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-08 15:12:55 6 | * @Description: 7 | */ 8 | 9 | import type { AppFormItems } from './appFormItem.config' 10 | 11 | export interface SearchConfig { 12 | formItems: AppFormItems[] 13 | } 14 | -------------------------------------------------------------------------------- /src/types/config/table.config.d.ts: -------------------------------------------------------------------------------- 1 | import type { TableColumnCtx } from 'element-plus' 2 | 3 | /* 4 | * @Author: hqk 5 | * @Date: 2023-01-06 19:54:43 6 | * @LastEditors: hqk 7 | * @LastEditTime: 2023-01-11 10:47:34 8 | * @Description: 9 | */ 10 | 11 | interface AppTableColumn extends Partial> { 12 | slotName?: string 13 | } 14 | 15 | export interface TableConfig { 16 | header: { 17 | title: string 18 | btnText: string 19 | } 20 | pageName: string 21 | propsList: AppTableColumn[] 22 | table?: { 23 | rowKey?: string 24 | treeProps?: { 25 | hasChildren?: string 26 | children?: string 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/types/editor/custom-types.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-15 20:14:06 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-15 20:14:24 6 | * @Description: 7 | */ 8 | import { SlateDescendant, SlateElement, SlateText } from '@wangeditor/editor' 9 | 10 | declare module '@wangeditor/editor' { 11 | // 扩展 Text 12 | interface SlateText { 13 | text: string 14 | } 15 | 16 | // 扩展 Element 17 | interface SlateElement { 18 | type: string 19 | children: SlateDescendant[] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-23 15:12:58 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-05 14:27:45 6 | * @Description: 7 | */ 8 | 9 | export interface IBaseResult { 10 | code: number 11 | data: T 12 | message: string 13 | } 14 | 15 | export * from './login' 16 | 17 | export * from './main/main' 18 | -------------------------------------------------------------------------------- /src/types/login.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-23 15:11:15 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2022-12-24 21:39:34 6 | * @Description: 7 | */ 8 | export interface LoginAccount { 9 | name: string 10 | password: string 11 | } 12 | 13 | export interface LoginInfo { 14 | id: number 15 | name: string 16 | token: string 17 | } 18 | 19 | export interface Role { 20 | id: number 21 | name: string 22 | intro: string 23 | createAt: Date 24 | updateAt: Date 25 | } 26 | 27 | export interface Department { 28 | id: number 29 | name: string 30 | parentId?: any 31 | createAt: Date 32 | updateAt: Date 33 | leader: string 34 | } 35 | 36 | export interface UserInfo { 37 | id: number 38 | name: string 39 | realname: string 40 | cellphone: number 41 | enable: number 42 | createAt: Date 43 | updateAt: Date 44 | role: Role 45 | department: Department 46 | } 47 | 48 | export interface MenuInfo { 49 | id: number 50 | name: string 51 | type: number 52 | url: string 53 | icon: string 54 | sort: number 55 | children: MenuInfoChildren[] 56 | } 57 | 58 | export interface MenuInfoChildren { 59 | id: number 60 | url: null | string 61 | name: string 62 | sort: number | null 63 | type: number 64 | children?: MenuInfoChildren[] | null 65 | parentId: number 66 | permission?: string 67 | } 68 | 69 | export interface GenerateCodeInfo { 70 | id: string 71 | url: string 72 | } 73 | 74 | export interface CheckStatus { 75 | status: number 76 | } 77 | -------------------------------------------------------------------------------- /src/types/main/analysis/analysis.d.ts: -------------------------------------------------------------------------------- 1 | export interface GoodsAmountInfo { 2 | amount: string 3 | title: string 4 | tips: string 5 | subtitle: string 6 | number1: number 7 | number2: number 8 | } 9 | 10 | export interface GoodsAddressSale { 11 | address: string 12 | count: number 13 | } 14 | 15 | export interface GoodsCategory { 16 | id: number 17 | name: string 18 | goodsCount: number 19 | } 20 | 21 | export interface GoodsCategoryFavor { 22 | id: number 23 | name: string 24 | goodsFavor: number 25 | } 26 | 27 | export interface GoodsSaleTop10 { 28 | id: number 29 | name: string 30 | saleCount: number 31 | } 32 | -------------------------------------------------------------------------------- /src/types/main/main.d.ts: -------------------------------------------------------------------------------- 1 | export * from './system/system' 2 | 3 | export interface RoleList { 4 | list: RoleInfo[] 5 | totalCount: number 6 | } 7 | 8 | export interface RoleInfo { 9 | id: number 10 | name: string 11 | intro: string 12 | createAt: string 13 | updateAt: string 14 | menuList: any[] 15 | } 16 | 17 | export interface DepartmentList { 18 | list: DepartmentInfo[] 19 | totalCount: number 20 | } 21 | 22 | export interface DepartmentInfo { 23 | id: number 24 | name: string 25 | parentId?: number 26 | createAt: string 27 | updateAt: string 28 | leader: string 29 | } 30 | 31 | export interface MenuList { 32 | list: MenuInfo[] 33 | } 34 | 35 | export interface MenuInfo { 36 | id: number 37 | name: string 38 | type: number 39 | url: string 40 | icon: string 41 | sort: number 42 | createAt: string 43 | updateAt: string 44 | children: MenuChildren[] 45 | } 46 | 47 | export interface MenuChildren { 48 | id: number 49 | url: string 50 | name: string 51 | sort: number 52 | type: number 53 | children?: MenuChildren[] 54 | createAt: string 55 | parentId: number 56 | updateAt: string 57 | permission?: string 58 | } 59 | 60 | export interface ICategoryListItem { 61 | id: number 62 | name: string 63 | } 64 | -------------------------------------------------------------------------------- /src/types/main/story/story.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-17 17:41:51 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-17 18:46:05 6 | * @Description: 7 | */ 8 | export interface CreateStoryData { 9 | title: string 10 | content: string 11 | } 12 | 13 | export interface StoryListParams { 14 | offset: number 15 | size: number 16 | } 17 | 18 | export interface StoryList { 19 | list: StoryInfo[] 20 | totalCount: number 21 | } 22 | 23 | export interface StoryInfo { 24 | id: number 25 | title: string 26 | content: string 27 | createAt: Date 28 | } 29 | -------------------------------------------------------------------------------- /src/types/main/system/system.d.ts: -------------------------------------------------------------------------------- 1 | export interface SystemUserInfo { 2 | id: number 3 | name: string 4 | realname: string 5 | cellphone: any 6 | enable: number 7 | departmentId: number 8 | roleId: number 9 | createAt: Date 10 | updateAt: Date 11 | } 12 | 13 | export interface SystemUserList { 14 | list: SystemUserInfo[] 15 | totalCount: number 16 | } 17 | 18 | export interface CreateUser { 19 | name: string 20 | realname: string 21 | password: string 22 | cellphone: string 23 | departmentId: string 24 | roleId: string 25 | } 26 | 27 | export interface PatchUser extends Partial> {} 28 | -------------------------------------------------------------------------------- /src/utils/format-number.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-13 16:28:06 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-13 16:48:41 6 | * @Description: 7 | */ 8 | /** 9 | * @description: 如23122332 格式化成 23,122,332.00 10 | * @param {number} number 11 | * @return {*} 12 | */ 13 | export function formatDoubleNumber(number: string) { 14 | if (!number) return '0.00' 15 | const val = parseFloat(number).toFixed(2) 16 | const str = String(val).replace(/\B(?=(\d{3})+(?!\d))/g, ',') 17 | const arrTemp = str.split('.') 18 | if (arrTemp.length < 2) arrTemp.push('00') 19 | arrTemp[1] = arrTemp[1].padEnd(2, '00') 20 | return arrTemp.join('.') 21 | } 22 | 23 | /** 24 | * @description: 格式化整型数钱 如23122332 格式化成 23,122,332 25 | * @param {number} number 26 | * @return {*} 27 | */ 28 | export function formatIntNumber(number: string) { 29 | if (!number) return number 30 | const str = String(number).replace(/\B(?=(\d{3})+(?!\d))/g, ',') 31 | const arrTemp = str.split('.') 32 | return arrTemp[0] 33 | } 34 | -------------------------------------------------------------------------------- /src/utils/format-time.ts: -------------------------------------------------------------------------------- 1 | export function formatTime(date: Date, formatStr: string = 'YYYY/MM/DD HH:mm:ss') { 2 | return useDateFormat(date, formatStr).value 3 | } 4 | -------------------------------------------------------------------------------- /src/utils/map-util.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-02 15:32:58 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-18 13:59:43 6 | * @Description: 7 | */ 8 | 9 | import type { RouteRecordRaw } from 'vue-router' 10 | 11 | /** 12 | * @description:获取某路径下的所有route对象 13 | */ 14 | export function getLocalRoutes() { 15 | const localRoutes: RouteRecordRaw[] = [] 16 | const files: Record = import.meta.glob('../router/main/**/*.ts', { eager: true }) 17 | for (const file in files) { 18 | const module = files[file] 19 | localRoutes.push(module.default) 20 | } 21 | return localRoutes.filter((item) => item.path != '/main/analysis/overview') 22 | } 23 | 24 | export let firstShowMenu: any = null 25 | 26 | /** 27 | * @description:将菜单数组转换成路由对象 28 | * @param {any} menuInfo 29 | * @return {*} 30 | */ 31 | export default function mapMenu2Routes(menuInfo: any[]) { 32 | // 1.获取router文件夹里的route对象 33 | const localRoutes = getLocalRoutes() 34 | 35 | // 2.根据用户菜单url匹配本地route对象 注册路由 36 | const mainRoutes: RouteRecordRaw[] = [] 37 | 38 | let firstLevelRoute: any = null 39 | function recursionMenuInfo(menuInfo: any[], localRoutes: RouteRecordRaw[]) { 40 | for (const menuInfoItem of menuInfo) { 41 | // 1.获取当前匹配的路由 42 | const route = localRoutes.find((localRoutesItem) => localRoutesItem.path == menuInfoItem.url) 43 | // 2.记录当前的一级路由 44 | if (menuInfoItem.type == 1) { 45 | firstLevelRoute = menuInfoItem.url 46 | } 47 | // 3.找到匹配的路由 48 | if (route) { 49 | mainRoutes.push(route) 50 | if (!mainRoutes.find((item) => item.path === firstLevelRoute)) { 51 | //将一级路由添加到mainRoutes中,并重定向到默认的第一个子路由 52 | mainRoutes.push({ path: firstLevelRoute, redirect: route.path }) 53 | } 54 | } 55 | 56 | //记录第一次进来所要展示的默认路由 57 | if (!firstShowMenu && route) firstShowMenu = menuInfoItem 58 | 59 | if (menuInfoItem.children?.length > 0) { 60 | recursionMenuInfo(menuInfoItem.children, localRoutes) 61 | } 62 | } 63 | } 64 | recursionMenuInfo(menuInfo, localRoutes) 65 | 66 | return mainRoutes 67 | } 68 | 69 | /** 70 | * @description:根据指定的路径匹配对应的菜单 71 | * @param {string} path 72 | * @param {any} routes 73 | * @return {*} 74 | */ 75 | export function mapPath2Menu(path: string, menus: any[]): any { 76 | let firstMenu: any = null 77 | function recursionMenu(menus: any[]) { 78 | for (const menu of menus) { 79 | if (menu.url == path && !firstMenu) { 80 | firstMenu = menu 81 | } 82 | if (menu.children?.length > 0) { 83 | recursionMenu(menu.children) 84 | } 85 | } 86 | } 87 | recursionMenu(menus) 88 | return firstMenu 89 | } 90 | 91 | /** 92 | * @description: 获取当前路径的菜单以及父菜单 93 | * @param {string} path 94 | * @param {any} menus 95 | * @return {*} 96 | */ 97 | export function mapPath2Breadcrumb(path: string, menus: any[]) { 98 | const breadcrumb: any[] = [] 99 | for (const menu of menus) { 100 | for (const subMenu of menu.children || []) { 101 | if (subMenu.url == path) { 102 | breadcrumb.push({ 103 | name: menu.name, 104 | url: menu.url 105 | }) 106 | breadcrumb.push({ 107 | name: subMenu.name 108 | }) 109 | } 110 | } 111 | } 112 | return breadcrumb 113 | } 114 | 115 | export function mapMenuInfo2Tree(menuInfo: any[]) { 116 | function recursionMenuInfo(items: any[]) { 117 | for (const item of items) { 118 | if (item.children) { 119 | item.value = item.id 120 | item.label = item.name 121 | recursionMenuInfo(item.children ?? []) 122 | } else { 123 | item.value = item.id 124 | item.label = item.name 125 | } 126 | } 127 | } 128 | recursionMenuInfo(menuInfo) 129 | return menuInfo 130 | } 131 | 132 | export function mapMenuChecked(menuList: any[]) { 133 | const checkedId: number[] = [] 134 | 135 | function recursionMenuList(items: any[]) { 136 | for (const item of items) { 137 | if (item.children) { 138 | recursionMenuList(item.children ?? []) 139 | } else { 140 | checkedId.push(item.id) 141 | } 142 | } 143 | } 144 | recursionMenuList(menuList) 145 | return checkedId 146 | } 147 | -------------------------------------------------------------------------------- /src/views/login/c-cpns/login-pane.vue: -------------------------------------------------------------------------------- 1 | 8 | 14 | 15 | 42 | 43 | -------------------------------------------------------------------------------- /src/views/login/c-cpns/pane-account.vue: -------------------------------------------------------------------------------- 1 | 9 | 89 | 90 | 111 | 112 | -------------------------------------------------------------------------------- /src/views/login/c-cpns/pane-code.vue: -------------------------------------------------------------------------------- 1 | 8 | 79 | 80 | 130 | 131 | -------------------------------------------------------------------------------- /src/views/login/login.vue: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | 17 | 24 | -------------------------------------------------------------------------------- /src/views/main/analysis/dashboard/dashboard.vue: -------------------------------------------------------------------------------- 1 | 8 | 43 | 44 | 67 | 68 | -------------------------------------------------------------------------------- /src/views/main/analysis/overview/overview.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /src/views/main/main.vue: -------------------------------------------------------------------------------- 1 | 8 | 24 | 25 | 40 | 53 | -------------------------------------------------------------------------------- /src/views/main/product/category/category.vue: -------------------------------------------------------------------------------- 1 | 8 | 22 | 23 | 37 | 38 | -------------------------------------------------------------------------------- /src/views/main/product/category/config/modal.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-08 14:58:35 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 19:52:23 6 | * @Description: 7 | */ 8 | import type { ModalConfig } from '@/types/config/modal.config' 9 | 10 | export const modalConfig: ModalConfig = { 11 | pageName: 'category', 12 | title: '商品', 13 | formItems: [ 14 | { 15 | type: 'input', 16 | label: '商品名称', 17 | prop: 'name', 18 | placeholder: '请输入商品名称' 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/views/main/product/category/config/search.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-06 20:47:14 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 19:52:06 6 | * @Description: 7 | */ 8 | 9 | import type { SearchConfig } from '@/types/config/seach.config' 10 | 11 | export const searchConfig: SearchConfig = { 12 | formItems: [ 13 | { 14 | type: 'input', 15 | prop: 'name', 16 | label: '商品名称', 17 | placeholder: '请输入查询的商品名称' 18 | }, 19 | { 20 | type: 'select', 21 | prop: 'enable', 22 | label: '状态', 23 | placeholder: '请选择状态', 24 | options: [ 25 | { label: '启用', value: '1' }, 26 | { label: '禁用', value: '0' } 27 | ] 28 | }, 29 | { 30 | type: 'date-picker', 31 | prop: 'createAt', 32 | label: '创建时间' 33 | }, 34 | { 35 | type: 'date-picker', 36 | prop: 'updateAt', 37 | label: '更新时间' 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /src/views/main/product/category/config/table.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-05 20:44:03 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 19:51:35 6 | * @Description: 7 | */ 8 | 9 | import type { TableConfig } from '@/types/config/table.config' 10 | 11 | export const tableConfig: TableConfig = { 12 | header: { 13 | title: '商品列表', 14 | btnText: '新建商品' 15 | }, 16 | pageName: 'category', 17 | propsList: [ 18 | { type: 'index', label: '序号', align: 'center', width: '60px' }, 19 | { prop: 'name', label: '商品名称', align: 'center', slotName: 'name' }, 20 | { type: 'switch', prop: 'enable', label: '状态', align: 'center', width: '100px', slotName: 'enable' }, 21 | { prop: 'createAt', label: '创建时间', align: 'center', slotName: 'createAt' }, 22 | { prop: 'updateAt', label: '更新时间', align: 'center', slotName: 'updateAt' }, 23 | { 24 | type: 'operate', 25 | prop: 'operate', 26 | label: '操作', 27 | align: 'center', 28 | slotName: 'operate' 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /src/views/main/product/goods/config/modal.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-08 14:58:35 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-14 20:22:04 6 | * @Description: 7 | */ 8 | import type { ModalConfig } from '@/types/config/modal.config' 9 | 10 | export const modalConfig: ModalConfig = { 11 | pageName: 'goods', 12 | title: '商品', 13 | formItems: [ 14 | { 15 | type: 'input', 16 | label: '商品名称', 17 | prop: 'name', 18 | placeholder: '请输入商品名称' 19 | }, 20 | { 21 | type: 'select', 22 | label: '商品种类', 23 | prop: 'categoryId', 24 | placeholder: '请选择商品种类', 25 | options: [] 26 | }, 27 | { 28 | type: 'input', 29 | label: '商品原价格', 30 | prop: 'oldPrice', 31 | placeholder: '请输入商品原价格' 32 | }, 33 | { 34 | type: 'input', 35 | label: '商品新价格', 36 | prop: 'newPrice', 37 | placeholder: '请输入商品新价格' 38 | }, 39 | { 40 | type: 'input', 41 | label: '商品描述', 42 | prop: 'desc', 43 | placeholder: '请输入商品描述' 44 | }, 45 | { 46 | type: 'select', 47 | label: '商品状态', 48 | prop: 'status', 49 | placeholder: '请选择商品状态', 50 | options: [ 51 | { value: 1, label: '可用' }, 52 | { value: 0, label: '下架' } 53 | ] 54 | }, 55 | { 56 | type: 'upload', 57 | label: '商品图片地址', 58 | prop: 'imgUrl', 59 | placeholder: '请输入商品图片地址' 60 | }, 61 | { 62 | type: 'input', 63 | label: '商品库存', 64 | prop: 'inventoryCount', 65 | placeholder: '请输入商品库存' 66 | }, 67 | { 68 | type: 'input', 69 | label: '商品销量', 70 | prop: 'saleCount', 71 | placeholder: '请输入商品销量' 72 | }, 73 | { 74 | type: 'input', 75 | label: '商品收藏量', 76 | prop: 'favorCount', 77 | placeholder: '请输入商品收藏量' 78 | }, 79 | { 80 | type: 'input', 81 | label: '商品地址', 82 | prop: 'address', 83 | placeholder: '请输入商品地址' 84 | } 85 | ] 86 | } 87 | -------------------------------------------------------------------------------- /src/views/main/product/goods/config/search.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-06 20:47:14 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-15 14:21:53 6 | * @Description: 7 | */ 8 | 9 | import type { SearchConfig } from '@/types/config/seach.config' 10 | 11 | export const searchConfig: SearchConfig = { 12 | formItems: [ 13 | { 14 | type: 'number', 15 | prop: 'oldPrice', 16 | label: '原价格' 17 | }, 18 | { 19 | type: 'input', 20 | prop: 'name', 21 | label: '商品名称', 22 | placeholder: '请输入查询的商品名称' 23 | }, 24 | { 25 | type: 'number', 26 | prop: 'newPrice', 27 | label: '新价格' 28 | }, 29 | { 30 | type: 'input', 31 | prop: 'address', 32 | label: '商品地址', 33 | placeholder: '请输入查询的商品地址' 34 | }, 35 | { 36 | type: 'number', 37 | prop: 'inventoryCount', 38 | label: '库存' 39 | }, 40 | { 41 | type: 'select', 42 | prop: 'status', 43 | label: '状态', 44 | options: [ 45 | { value: '1', label: '可用' }, 46 | { value: '0', label: '下架' } 47 | ] 48 | }, 49 | { 50 | type: 'number', 51 | prop: 'saleCount', 52 | label: '销量' 53 | }, 54 | { 55 | type: 'date-picker', 56 | prop: 'createAt', 57 | label: '创建时间' 58 | }, 59 | 60 | { 61 | type: 'number', 62 | prop: 'favorCount', 63 | label: '收藏' 64 | }, 65 | { 66 | type: 'date-picker', 67 | prop: 'updateAt', 68 | label: '更新时间' 69 | }, 70 | { 71 | type: 'select', 72 | prop: 'categoryId', 73 | label: '商品种类', 74 | options: [] 75 | } 76 | ] 77 | } 78 | -------------------------------------------------------------------------------- /src/views/main/product/goods/config/table.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-05 20:44:03 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-15 19:37:02 6 | * @Description: 7 | */ 8 | 9 | import type { TableConfig } from '@/types/config/table.config' 10 | 11 | export const tableConfig: TableConfig = { 12 | header: { 13 | title: '商品列表', 14 | btnText: '新建商品' 15 | }, 16 | pageName: 'goods', 17 | propsList: [ 18 | { prop: 'name', label: '商品名称', align: 'center', width: '120px', slotName: 'name', showOverflowTooltip: true }, 19 | { prop: 'oldPrice', label: '原价格', align: 'center', slotName: 'oldPrice' }, 20 | { prop: 'newPrice', label: '新价格', align: 'center', slotName: 'newPrice' }, 21 | { prop: 'desc', label: '商品描述', align: 'center', width: '120px', slotName: 'desc', showOverflowTooltip: true }, 22 | { type: 'switch', prop: 'status', label: '状态', align: 'center', width: '80px', slotName: 'status' }, 23 | { prop: 'imgUrl', label: '图片', minWidth: '200px', align: 'center', slotName: 'imgUrl' }, 24 | { prop: 'inventoryCount', label: '库存', align: 'center', slotName: 'inventoryCount' }, 25 | { prop: 'saleCount', label: '销量', align: 'center', slotName: 'saleCount' }, 26 | { prop: 'favorCount', label: '收藏', align: 'center', slotName: 'favorCount' }, 27 | { prop: 'address', label: '地址', align: 'center', slotName: 'address' }, 28 | { prop: 'createAt', label: '创建时间', align: 'center', width: '200px', slotName: 'createAt' }, 29 | { prop: 'updateAt', label: '更新时间', align: 'center', width: '200px', slotName: 'updateAt' }, 30 | { 31 | type: 'operate', 32 | prop: 'operate', 33 | label: '操作', 34 | minWidth: '200px', 35 | align: 'center', 36 | slotName: 'operate' 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /src/views/main/product/goods/goods.vue: -------------------------------------------------------------------------------- 1 | 8 | 36 | 37 | 68 | 76 | -------------------------------------------------------------------------------- /src/views/main/story/chat/chat.vue: -------------------------------------------------------------------------------- 1 | 8 | 34 | 35 | 61 | 62 | -------------------------------------------------------------------------------- /src/views/main/story/list/list.vue: -------------------------------------------------------------------------------- 1 | 8 | 22 | 23 | 30 | 43 | -------------------------------------------------------------------------------- /src/views/main/system/department/config/modal.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-08 14:58:35 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 11:00:36 6 | * @Description: 7 | */ 8 | import type { ModalConfig } from '@/types/config/modal.config' 9 | 10 | export const modalConfig: ModalConfig = { 11 | pageName: 'department', 12 | title: '部门', 13 | formItems: [ 14 | { type: 'input', label: '部门名称', prop: 'name', placeholder: '请输入部门名称', initialValue: '1' }, 15 | { type: 'select', label: '上级部门', prop: 'parentId', placeholder: '请选择上级部门', options: [] }, 16 | { type: 'input', label: '领导名称', prop: 'leader', placeholder: '请输入领导名称' } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/views/main/system/department/config/search.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-06 20:47:14 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-06 21:07:03 6 | * @Description: 7 | */ 8 | 9 | import type { SearchConfig } from '@/types/config/seach.config' 10 | 11 | export const searchConfig: SearchConfig = { 12 | formItems: [ 13 | { 14 | type: 'input', 15 | prop: 'name', 16 | label: '部门名称', 17 | placeholder: '请输入查询的部门名称' 18 | }, 19 | { 20 | type: 'input', 21 | prop: 'leader', 22 | label: '部门领导', 23 | placeholder: '请输入查询的领导名称' 24 | }, 25 | { 26 | type: 'select', 27 | prop: 'enable', 28 | label: '状态', 29 | placeholder: '请选择状态', 30 | options: [ 31 | { label: '启用', value: '1' }, 32 | { label: '禁用', value: '0' } 33 | ] 34 | }, 35 | { 36 | type: 'date-picker', 37 | prop: 'createAt', 38 | label: '创建时间' 39 | }, 40 | { 41 | type: 'date-picker', 42 | prop: 'updateAt', 43 | label: '更新时间' 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /src/views/main/system/department/config/table.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-05 20:44:03 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 11:41:26 6 | * @Description: 7 | */ 8 | 9 | import type { TableConfig } from '@/types/config/table.config' 10 | 11 | export const tableConfig: TableConfig = { 12 | header: { 13 | title: '部门列表', 14 | btnText: '新建部门' 15 | }, 16 | pageName: 'department', 17 | propsList: [ 18 | { type: 'selection', align: 'center' }, 19 | { type: 'index', label: '序号', align: 'center', width: '60px' }, 20 | { prop: 'name', label: '部门名称', align: 'center', slotName: 'name' }, 21 | { prop: 'leader', label: '部门领导', align: 'center', slotName: 'leader' }, 22 | { type: 'switch', prop: 'enable', label: '状态', align: 'center', width: '100px', slotName: 'enable' }, 23 | 24 | { prop: 'parentId', label: '上级部门', align: 'center', width: '120px', slotName: 'parentId' }, 25 | { prop: 'createAt', label: '创建时间', align: 'center', slotName: 'createAt' }, 26 | { prop: 'updateAt', label: '更新时间', align: 'center', slotName: 'updateAt' }, 27 | { 28 | type: 'operate', 29 | prop: 'operate', 30 | label: '操作', 31 | align: 'center', 32 | slotName: 'operate' 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /src/views/main/system/department/department.vue: -------------------------------------------------------------------------------- 1 | 8 | 46 | 47 | 61 | 62 | -------------------------------------------------------------------------------- /src/views/main/system/menu/config/modal.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-08 14:58:35 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-10 19:59:21 6 | * @Description: 7 | */ 8 | import type { ModalConfig } from '@/types/config/modal.config' 9 | 10 | export const modalConfig: ModalConfig = { 11 | pageName: 'menu', 12 | title: '菜单', 13 | formItems: [ 14 | { 15 | type: 'input', 16 | label: '菜单名称', 17 | prop: 'name', 18 | placeholder: '请输入菜单名称' 19 | }, 20 | { 21 | type: 'select', 22 | label: '菜单级别', 23 | prop: 'type', 24 | placeholder: '请选择菜单级别', 25 | options: [ 26 | { label: '1', value: '1' }, 27 | { label: '2', value: '2' }, 28 | { label: '3', value: '3' } 29 | ] 30 | }, 31 | { 32 | type: 'input', 33 | label: '菜单路径', 34 | prop: 'url', 35 | placeholder: '请输入菜单路径' 36 | }, 37 | { 38 | type: 'input', 39 | label: '排序', 40 | prop: 'sort', 41 | placeholder: '请输入排序' 42 | }, 43 | { 44 | type: 'custom', 45 | label: '图标', 46 | prop: 'icon', 47 | placeholder: '请输入图标' 48 | }, 49 | { 50 | type: 'custom', 51 | label: '上级菜单', 52 | prop: 'parentId', 53 | placeholder: '请输入上级菜单' 54 | }, 55 | { 56 | type: 'input', 57 | label: '权限', 58 | prop: 'permission', 59 | placeholder: '请输入权限' 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /src/views/main/system/menu/config/table.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-05 20:44:03 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-11 11:09:58 6 | * @Description: 7 | */ 8 | 9 | import type { TableConfig } from '@/types/config/table.config' 10 | 11 | export const tableConfig: TableConfig = { 12 | header: { 13 | title: '菜单列表', 14 | btnText: '新建菜单' 15 | }, 16 | pageName: 'menu', 17 | propsList: [ 18 | { prop: 'name', label: '菜单名称', align: 'center', slotName: 'name' }, 19 | { prop: 'type', label: '菜单级别', align: 'center', width: '100px', slotName: 'type' }, 20 | { prop: 'url', label: '路径', align: 'center', slotName: 'url' }, 21 | { prop: 'icon', label: '图标', align: 'center', slotName: 'icon' }, 22 | { prop: 'sort', label: '排序', align: 'center', slotName: 'sort' }, 23 | { type: 'switch', prop: 'enable', label: '状态', align: 'center', width: '100px', slotName: 'enable' }, 24 | { prop: 'permission', label: '权限', align: 'center', slotName: 'permission' }, 25 | { prop: 'createAt', label: '创建时间', align: 'center', width: '200px', slotName: 'createAt' }, 26 | { prop: 'updateAt', label: '更新时间', align: 'center', width: '200px', slotName: 'updateAt' }, 27 | { 28 | type: 'operate', 29 | prop: 'operate', 30 | label: '操作', 31 | align: 'center', 32 | slotName: 'operate' 33 | } 34 | ], 35 | table: { 36 | treeProps: { 37 | children: 'children', 38 | hasChildren: 'hasChildren' 39 | }, 40 | rowKey: 'id' 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/views/main/system/menu/menu.vue: -------------------------------------------------------------------------------- 1 | 8 | 44 | 45 | 78 | 79 | -------------------------------------------------------------------------------- /src/views/main/system/role/config/modal.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-08 14:58:35 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 19:29:42 6 | * @Description: 7 | */ 8 | import type { ModalConfig } from '@/types/config/modal.config' 9 | 10 | export const modalConfig: ModalConfig = { 11 | pageName: 'role', 12 | title: '角色', 13 | formItems: [ 14 | { 15 | type: 'input', 16 | label: '角色名称', 17 | prop: 'name', 18 | placeholder: '请输入角色名称' 19 | }, 20 | { 21 | type: 'input', 22 | label: '权限介绍', 23 | prop: 'intro', 24 | placeholder: '请输入权限介绍' 25 | }, 26 | { 27 | type: 'custom', 28 | label: '菜单列表', 29 | prop: 'menuList' 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/views/main/system/role/config/search.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-06 20:47:14 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 19:27:25 6 | * @Description: 7 | */ 8 | 9 | import type { SearchConfig } from '@/types/config/seach.config' 10 | 11 | export const searchConfig: SearchConfig = { 12 | formItems: [ 13 | { 14 | type: 'input', 15 | prop: 'name', 16 | label: '角色名称', 17 | placeholder: '请输入查询的角色名称' 18 | }, 19 | { 20 | type: 'input', 21 | prop: 'intro', 22 | label: '权限介绍', 23 | placeholder: '请输入查询的权限介绍' 24 | }, 25 | { 26 | type: 'select', 27 | prop: 'enable', 28 | label: '状态', 29 | placeholder: '请选择状态', 30 | options: [ 31 | { label: '启用', value: '1' }, 32 | { label: '禁用', value: '0' } 33 | ] 34 | }, 35 | { 36 | type: 'date-picker', 37 | prop: 'createAt', 38 | label: '创建时间' 39 | }, 40 | { 41 | type: 'date-picker', 42 | prop: 'updateAt', 43 | label: '更新时间' 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /src/views/main/system/role/config/table.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-05 20:44:03 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 19:31:47 6 | * @Description: 7 | */ 8 | 9 | import type { TableConfig } from '@/types/config/table.config' 10 | 11 | export const tableConfig: TableConfig = { 12 | header: { 13 | title: '角色列表', 14 | btnText: '新建角色' 15 | }, 16 | pageName: 'role', 17 | propsList: [ 18 | { type: 'selection', align: 'center' }, 19 | { type: 'index', label: '序号', align: 'center', width: '60px' }, 20 | { prop: 'name', label: '角色名称', align: 'center', slotName: 'name' }, 21 | { prop: 'intro', label: '权限介绍', align: 'center', slotName: 'intro' }, 22 | { type: 'switch', prop: 'enable', label: '状态', align: 'center', width: '100px', slotName: 'enable' }, 23 | { prop: 'createAt', label: '创建时间', align: 'center', slotName: 'createAt' }, 24 | { prop: 'updateAt', label: '更新时间', align: 'center', slotName: 'updateAt' }, 25 | { 26 | type: 'operate', 27 | prop: 'operate', 28 | label: '操作', 29 | align: 'center', 30 | slotName: 'operate' 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /src/views/main/system/role/role.vue: -------------------------------------------------------------------------------- 1 | 8 | 54 | 55 | 73 | 74 | -------------------------------------------------------------------------------- /src/views/main/system/user/config/modal.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-08 14:58:35 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 17:56:06 6 | * @Description: 7 | */ 8 | import type { ModalConfig } from '@/types/config/modal.config' 9 | 10 | export const modalConfig: ModalConfig = { 11 | pageName: 'users', 12 | title: '用户', 13 | formItems: [ 14 | { type: 'input', label: '用户名', prop: 'name', placeholder: '请输入用户名' }, 15 | { type: 'input', label: '真实姓名', prop: 'realname', placeholder: '请输入真实姓名' }, 16 | { type: 'input', label: '密码', prop: 'password', placeholder: '请输入密码' }, 17 | { type: 'input', label: '电话号码', prop: 'cellphone', placeholder: '请输入电话号码' }, 18 | { type: 'upload', label: '头像', prop: 'avatar', placeholder: '请选择头像' }, 19 | { type: 'select', label: '角色', prop: 'roleId', placeholder: '请选择角色', options: [] }, 20 | { type: 'select', label: '部门', prop: 'departmentId', placeholder: '请选择部门', options: [] } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src/views/main/system/user/config/search.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-06 20:47:14 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-15 15:26:49 6 | * @Description: 7 | */ 8 | 9 | import type { SearchConfig } from '@/types/config/seach.config' 10 | 11 | export const searchConfig: SearchConfig = { 12 | formItems: [ 13 | { 14 | type: 'input', 15 | prop: 'name', 16 | label: '用户名', 17 | placeholder: '请输入查询的用户名称' 18 | }, 19 | { 20 | type: 'input', 21 | prop: 'realname', 22 | label: '真实姓名', 23 | placeholder: '请输入查询的真实姓名' 24 | }, 25 | { 26 | type: 'input', 27 | prop: 'cellphone', 28 | label: '手机号码', 29 | placeholder: '请输入查询的手机号码' 30 | }, 31 | { 32 | type: 'select', 33 | prop: 'enable', 34 | label: '状态', 35 | placeholder: '请选择状态', 36 | options: [ 37 | { label: '启用', value: '1' }, 38 | { label: '禁用', value: '0' } 39 | ] 40 | }, 41 | { 42 | type: 'select', 43 | prop: 'roleId', 44 | label: '角色', 45 | placeholder: '请选择状态', 46 | options: [] 47 | }, 48 | { 49 | type: 'select', 50 | prop: 'departmentId', 51 | label: '部门', 52 | placeholder: '请选择部门', 53 | options: [] 54 | }, 55 | { 56 | type: 'date-picker', 57 | prop: 'createAt', 58 | label: '创建时间' 59 | }, 60 | { 61 | type: 'date-picker', 62 | prop: 'updateAt', 63 | label: '更新时间' 64 | } 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /src/views/main/system/user/config/table.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2023-01-05 20:44:03 4 | * @LastEditors: hqk 5 | * @LastEditTime: 2023-01-09 20:05:44 6 | * @Description: 7 | */ 8 | 9 | import type { TableConfig } from '@/types/config/table.config' 10 | 11 | export const tableConfig: TableConfig = { 12 | header: { 13 | title: '用户列表', 14 | btnText: '新建用户' 15 | }, 16 | pageName: 'users', 17 | propsList: [ 18 | { type: 'selection', align: 'center' }, 19 | { type: 'index', label: '序号', align: 'center', width: '60px' }, 20 | { prop: 'name', label: '用户名', align: 'center', slotName: 'name' }, 21 | { prop: 'avatar', label: '头像', align: 'center', slotName: 'avatar' }, 22 | { prop: 'realname', label: '真实名', align: 'center', slotName: 'realname' }, 23 | { prop: 'cellphone', label: '手机号码', align: 'center', width: '120px', slotName: 'cellphone', showOverflowTooltip: true }, 24 | { type: 'switch', prop: 'enable', label: '状态', align: 'center', width: '100px', slotName: 'enable' }, 25 | { prop: 'createAt', label: '创建时间', align: 'center', slotName: 'createAt' }, 26 | { prop: 'updateAt', label: '更新时间', align: 'center', slotName: 'updateAt' }, 27 | { 28 | type: 'operate', 29 | prop: 'operate', 30 | label: '操作', 31 | align: 'center', 32 | slotName: 'operate' 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /src/views/main/system/user/user.vue: -------------------------------------------------------------------------------- 1 | 8 | 48 | 49 | 74 | 75 | -------------------------------------------------------------------------------- /src/views/not-found/not-found.vue: -------------------------------------------------------------------------------- 1 | 8 | 21 | 22 | 37 | 48 | -------------------------------------------------------------------------------- /tsconfig.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "extends": "@vue/tsconfig/tsconfig.node.json", 4 | "include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "ignoreDeprecations": "5.0", 8 | "types": ["node"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "extends": "@vue/tsconfig/tsconfig.web.json", 4 | "include": ["src/**/*", "src/**/*.vue", "env.d.ts", "components.d.ts", "auto-imports.d.ts", "./src/types/editor/custom-types.d.ts"], 5 | "compilerOptions": { 6 | "ignoreDeprecations": "5.0", 7 | "baseUrl": ".", 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | }, 11 | "types": ["element-plus/global"] 12 | }, 13 | 14 | "references": [ 15 | { 16 | "path": "./tsconfig.config.json" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /uno.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Leo l024983409@qq.com 3 | * @Date: 2023-09-19 20:22:55 4 | * @LastEditors: Leo l024983409@qq.com 5 | * @LastEditTime: 2023-09-23 10:43:41 6 | * @FilePath: \power-system-visualization\uno.config.ts 7 | * @Description: 8 | */ 9 | 10 | import { 11 | defineConfig, 12 | presetAttributify, 13 | presetIcons, 14 | presetTypography, 15 | presetUno, 16 | presetWebFonts, 17 | transformerDirectives, 18 | transformerVariantGroup 19 | } from 'unocss' 20 | 21 | export default defineConfig({ 22 | shortcuts: [ 23 | ['around', 'flex justify-around items-center'], 24 | ['between', 'flex justify-between items-center'], 25 | ['center', 'flex justify-center items-center'], 26 | ['center-y', 'flex items-center'], 27 | [ 28 | 'btn', 29 | 'px-4 py-1 rounded inline-block bg-teal-600 text-white cursor-pointer hover:bg-teal-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50' 30 | ], 31 | [ 32 | 'icon-btn', 33 | 'h-30px w-30px transition-all duration-400 dark:h-30px dark:w-30px hover:scale-110 inline-block cursor-pointer select-none transition duration-200 ease-in-out !outline-none' 34 | ], 35 | ['b-bottom', 'border-b-1 border-b-solid border-b-[#eee]'], 36 | ['switch-animation', 'transition duration-300'], 37 | ['text', 'text-[#00706c] dark:text-[#fff] switch-animation'] 38 | ], 39 | presets: [ 40 | presetUno(), 41 | presetAttributify(), 42 | presetIcons({ 43 | scale: 1.2, 44 | warn: true 45 | }), 46 | presetTypography(), 47 | presetWebFonts({ 48 | fonts: { 49 | sans: 'DM Sans', 50 | serif: 'DM Serif Display', 51 | mono: 'DM Mono' 52 | } 53 | }) 54 | ], 55 | rules: [ 56 | // 多行省略 57 | [/^line-clamp-(\d+)$/, ([, value]) => handlerLineClamp(value)] 58 | ], 59 | transformers: [transformerDirectives(), transformerVariantGroup()] 60 | }) 61 | // 处理多行省略 62 | function handlerLineClamp(value: any) { 63 | return { 64 | overflow: 'hidden', 65 | display: '-webkit-box', 66 | 'word-break': 'break-all', 67 | '-webkit-box-orient': 'vertical', 68 | '-webkit-line-clamp': `${value}` 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hqk 3 | * @Date: 2022-12-20 21:47:04 4 | * @LastEditors: Leo l024983409@qq.com 5 | * @LastEditTime: 2023-11-14 21:58:10 6 | * @Description: 7 | */ 8 | import { fileURLToPath, URL } from 'node:url' 9 | import { defineConfig } from 'vite' 10 | 11 | import AutoImport from 'unplugin-auto-import/vite' 12 | import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' 13 | import Components from 'unplugin-vue-components/vite' 14 | 15 | //自动导入图标 16 | import IconsResolver from 'unplugin-icons/resolver' 17 | import Icons from 'unplugin-icons/vite' 18 | 19 | import vue from '@vitejs/plugin-vue' 20 | import presetUno from 'unocss/preset-uno' 21 | import Unocss from 'unocss/vite' 22 | 23 | //gzip 24 | import VitePluginCompression from 'vite-plugin-compression' 25 | 26 | // https://vitejs.dev/config/ 27 | export default defineConfig({ 28 | plugins: [ 29 | vue(), 30 | AutoImport({ 31 | // Auto import functions from Vue, e.g. ref, reactive, toRef... 32 | // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等 33 | // 自动导入vueuse 以及pinia的storeToRefs 34 | imports: ['vue', '@vueuse/core', { pinia: ['storeToRefs'] }, 'vue-router'], 35 | resolvers: [ 36 | // Auto import functions from Element Plus, e.g. ElMessage, ElMessageBox... (with style) 37 | // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式) 38 | ElementPlusResolver(), 39 | // Auto import icon components 40 | // 自动导入图标组件 41 | IconsResolver({ 42 | prefix: 'Icon' 43 | }) 44 | ] 45 | }), 46 | Components({ 47 | resolvers: [ 48 | ElementPlusResolver(), 49 | // Auto register icon components 50 | // 自动注册图标组件 51 | //默认前缀为i,可自定义前缀 52 | IconsResolver({ 53 | enabledCollections: ['ep'] 54 | // prefix: 'icon' 55 | }) 56 | ] 57 | }), 58 | Icons({ 59 | autoInstall: true 60 | }), 61 | Unocss({ 62 | presets: [presetUno()] 63 | }), 64 | // Gzip 65 | VitePluginCompression() 66 | ], 67 | resolve: { 68 | alias: { 69 | '@': fileURLToPath(new URL('./src', import.meta.url)) 70 | } 71 | }, 72 | build: { 73 | rollupOptions: { 74 | output: { 75 | manualChunks: { 76 | echarts: ['echarts'] 77 | } 78 | } 79 | }, 80 | // 关闭生成map文件 可以达到缩小打包体积 81 | sourcemap: false, 82 | // 关闭文件计算 83 | reportCompressedSize: false 84 | }, 85 | server: { 86 | proxy: { 87 | '/proxy': { 88 | target: 'https://cms.server.hqk10.top/api/v1', 89 | changeOrigin: true, 90 | rewrite: (path) => path.replace(/^\/proxy/, '') 91 | } 92 | } 93 | } 94 | }) 95 | --------------------------------------------------------------------------------