diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3c3960b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# http://editorconfig.org + +root = true + +[*] # 表示所有文件适用 +charset = utf-8 # 设置文件字符集为 utf-8 +indent_style = space # 缩进风格(tab | space) +indent_size = 2 # 缩进大小 +end_of_line = lf # 控制换行类型(lf | cr | crlf) +trim_trailing_whitespace = true # 去除行首的任意空白字符 +insert_final_newline = true # 始终在文件末尾插入一个新行 + +[*.md] # 表示仅 md 文件适用以下规则 +max_line_length = off +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..e3ebad3 --- /dev/null +++ b/.env.development @@ -0,0 +1,5 @@ +## 开发环境 +VITE_APP_ENV = 'development' +VITE_APP_TITLE = 'vue-vite-project-admin' +VITE_APP_PORT = 8089 +VITE_APP_BASE_API = '/dev-api' ## '/dev-api'线上接口 '/mock'本地模拟数据 diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..f700f7b --- /dev/null +++ b/.env.production @@ -0,0 +1,5 @@ +## 生产环境 +VITE_APP_ENV = 'production' +VITE_APP_TITLE = 'vue-vite-project-admin' +VITE_APP_PORT = 8089 +VITE_APP_BASE_API = 'http://10.10.10.56:9000/' diff --git a/.env.staging b/.env.staging new file mode 100644 index 0000000..cb3b6ae --- /dev/null +++ b/.env.staging @@ -0,0 +1,5 @@ +## 模拟环境 +VITE_APP_ENV = 'staging' +VITE_APP_TITLE = 'vue-vite-project-admin' +VITE_APP_PORT = 8089 +VITE_APP_BASE_API = '/mock' diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..da6aa78 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,19 @@ +*.sh +node_modules +*.md +*.woff +*.ttf +.vscode +.idea +dist +/public +/docs +.husky +.local +/bin +.eslintrc.js +prettier.config.js +src/assets + +auto-imports.d.ts +components.d.ts diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json new file mode 100644 index 0000000..9f36942 --- /dev/null +++ b/.eslintrc-auto-import.json @@ -0,0 +1,269 @@ +{ + "globals": { + "EffectScope": true, + "ElForm": true, + "ElMessage": true, + "ElMessageBox": true, + "ElTree": true, + "asyncComputed": true, + "autoResetRef": true, + "computed": true, + "computedAsync": true, + "computedEager": true, + "computedInject": true, + "computedWithControl": true, + "controlledComputed": true, + "controlledRef": true, + "createApp": true, + "createEventHook": true, + "createGlobalState": true, + "createInjectionState": true, + "createReactiveFn": true, + "createSharedComposable": true, + "createUnrefFn": true, + "customRef": true, + "debouncedRef": true, + "debouncedWatch": true, + "defineAsyncComponent": true, + "defineComponent": true, + "eagerComputed": true, + "effectScope": true, + "extendRef": true, + "getCurrentInstance": true, + "getCurrentScope": true, + "h": true, + "ignorableWatch": true, + "inject": true, + "isDefined": true, + "isProxy": true, + "isReactive": true, + "isReadonly": true, + "isRef": true, + "makeDestructurable": true, + "markRaw": true, + "nextTick": true, + "onActivated": true, + "onBeforeMount": true, + "onBeforeUnmount": true, + "onBeforeUpdate": true, + "onClickOutside": true, + "onDeactivated": true, + "onErrorCaptured": true, + "onKeyStroke": true, + "onLongPress": true, + "onMounted": true, + "onRenderTracked": true, + "onRenderTriggered": true, + "onScopeDispose": true, + "onServerPrefetch": true, + "onStartTyping": true, + "onUnmounted": true, + "onUpdated": true, + "pausableWatch": true, + "provide": true, + "reactify": true, + "reactifyObject": true, + "reactive": true, + "reactiveComputed": true, + "reactiveOmit": true, + "reactivePick": true, + "readonly": true, + "ref": true, + "refAutoReset": true, + "refDebounced": true, + "refDefault": true, + "refThrottled": true, + "refWithControl": true, + "resolveComponent": true, + "resolveDirective": true, + "resolveRef": true, + "resolveUnref": true, + "shallowReactive": true, + "shallowReadonly": true, + "shallowRef": true, + "syncRef": true, + "syncRefs": true, + "templateRef": true, + "throttledRef": true, + "throttledWatch": true, + "toRaw": true, + "toReactive": true, + "toRef": true, + "toRefs": true, + "triggerRef": true, + "tryOnBeforeMount": true, + "tryOnBeforeUnmount": true, + "tryOnMounted": true, + "tryOnScopeDispose": true, + "tryOnUnmounted": true, + "unref": true, + "unrefElement": true, + "until": true, + "useActiveElement": true, + "useArrayEvery": true, + "useArrayFilter": true, + "useArrayFind": true, + "useArrayFindIndex": true, + "useArrayFindLast": true, + "useArrayJoin": true, + "useArrayMap": true, + "useArrayReduce": true, + "useArraySome": true, + "useArrayUnique": true, + "useAsyncQueue": true, + "useAsyncState": true, + "useAttrs": true, + "useBase64": true, + "useBattery": true, + "useBluetooth": true, + "useBreakpoints": true, + "useBroadcastChannel": true, + "useBrowserLocation": true, + "useCached": true, + "useClipboard": true, + "useCloned": true, + "useColorMode": true, + "useConfirmDialog": true, + "useCounter": true, + "useCssModule": true, + "useCssVar": true, + "useCssVars": true, + "useCurrentElement": true, + "useCycleList": true, + "useDark": true, + "useDateFormat": true, + "useDebounce": true, + "useDebounceFn": true, + "useDebouncedRefHistory": true, + "useDeviceMotion": true, + "useDeviceOrientation": true, + "useDevicePixelRatio": true, + "useDevicesList": true, + "useDisplayMedia": true, + "useDocumentVisibility": true, + "useDraggable": true, + "useDropZone": true, + "useElementBounding": true, + "useElementByPoint": true, + "useElementHover": true, + "useElementSize": true, + "useElementVisibility": true, + "useEventBus": true, + "useEventListener": true, + "useEventSource": true, + "useEyeDropper": true, + "useFavicon": true, + "useFetch": true, + "useFileDialog": true, + "useFileSystemAccess": true, + "useFocus": true, + "useFocusWithin": true, + "useFps": true, + "useFullscreen": true, + "useGamepad": true, + "useGeolocation": true, + "useIdle": true, + "useImage": true, + "useInfiniteScroll": true, + "useIntersectionObserver": true, + "useInterval": true, + "useIntervalFn": true, + "useKeyModifier": true, + "useLastChanged": true, + "useLocalStorage": true, + "useMagicKeys": true, + "useManualRefHistory": true, + "useMediaControls": true, + "useMediaQuery": true, + "useMemoize": true, + "useMemory": true, + "useMounted": true, + "useMouse": true, + "useMouseInElement": true, + "useMousePressed": true, + "useMutationObserver": true, + "useNavigatorLanguage": true, + "useNetwork": true, + "useNow": true, + "useObjectUrl": true, + "useOffsetPagination": true, + "useOnline": true, + "usePageLeave": true, + "useParallax": true, + "usePermission": true, + "usePointer": true, + "usePointerLock": true, + "usePointerSwipe": true, + "usePreferredColorScheme": true, + "usePreferredContrast": true, + "usePreferredDark": true, + "usePreferredLanguages": true, + "usePreferredReducedMotion": true, + "usePrevious": true, + "useRafFn": true, + "useRefHistory": true, + "useResizeObserver": true, + "useScreenOrientation": true, + "useScreenSafeArea": true, + "useScriptTag": true, + "useScroll": true, + "useScrollLock": true, + "useSessionStorage": true, + "useShare": true, + "useSlots": true, + "useSorted": true, + "useSpeechRecognition": true, + "useSpeechSynthesis": true, + "useStepper": true, + "useStorage": true, + "useStorageAsync": true, + "useStyleTag": true, + "useSupported": true, + "useSwipe": true, + "useTemplateRefsList": true, + "useTextDirection": true, + "useTextSelection": true, + "useTextareaAutosize": true, + "useThrottle": true, + "useThrottleFn": true, + "useThrottledRefHistory": true, + "useTimeAgo": true, + "useTimeout": true, + "useTimeoutFn": true, + "useTimeoutPoll": true, + "useTimestamp": true, + "useTitle": true, + "useToNumber": true, + "useToString": true, + "useToggle": true, + "useTransition": true, + "useUrlSearchParams": true, + "useUserMedia": true, + "useVModel": true, + "useVModels": true, + "useVibrate": true, + "useVirtualList": true, + "useWakeLock": true, + "useWebNotification": true, + "useWebSocket": true, + "useWebWorker": true, + "useWebWorkerFn": true, + "useWindowFocus": true, + "useWindowScroll": true, + "useWindowSize": true, + "watch": true, + "watchArray": true, + "watchAtMost": true, + "watchDebounced": true, + "watchEffect": true, + "watchIgnorable": true, + "watchOnce": true, + "watchPausable": true, + "watchPostEffect": true, + "watchSyncEffect": true, + "watchThrottled": true, + "watchTriggerable": true, + "watchWithFilter": true, + "whenever": true + } +} \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..722c3e0 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,33 @@ + +module.exports = { + env: { + browser: true, + es2021: true, + node: true, + }, + parser: 'vue-eslint-parser', + // https://eslint.vuejs.org/user-guide/#bundle-configurations + extends: [ + 'eslint:recommended', + 'plugin:vue/vue3-essential', + 'plugin:@typescript-eslint/recommended', + "./.eslintrc-auto-import.json" + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + parser: '@typescript-eslint/parser', + }, + plugins: ['vue', '@typescript-eslint'], + rules: { + 'vue/multi-word-component-names': 'off', + '@typescript-eslint/no-empty-function': 'off', // 关闭空方法检查 + '@typescript-eslint/no-explicit-any': 'off', // 关闭any类型的警告 + 'vue/no-v-model-argument': 'off' + }, + // https://eslint.org/docs/latest/use/configure/language-options#specifying-globals + globals: { + DialogOption: 'readonly', + OptionType:'readonly' + } +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5c3e83 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +pnpm-lock.yaml diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d251d2e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +/dist/* +.local +.output.js +/node_modules/** + +**/*.svg +**/*.sh + +/public/* \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..41db16e --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,38 @@ +/** + * 代码格式化配置 + */ +module.exports = { + // 一行最多 120 字符.. + printWidth: 120, + // 指定每个缩进级别的空格数 + tabWidth: 2, + // 使用制表符而不是空格缩进行 + useTabs: false, + // 在语句末尾打印分号 + semi: true, + // 使用单引号而不是双引号 + singleQuote: true, + // 更改引用对象属性的时间 可选值"" + quoteProps: 'as-needed', + // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"",默认none + trailingComma: 'none', + // 在对象文字中的括号之间打印空格 + bracketSpacing: true, + // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x + arrowParens: 'avoid', + // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 + rangeStart: 0, + rangeEnd: Infinity, + // 指定要使用的解析器,不需要写文件开头的 @prettier + requirePragma: false, + // 不需要自动在文件开头插入 @prettier + insertPragma: false, + // 换行设置 always\never\preserve + proseWrap: 'preserve', + // 指定HTML文件的全局空格敏感度 css\strict\ignore + htmlWhitespaceSensitivity: 'css', + // Vue文件脚本和样式标签缩进 + vueIndentScriptAndStyle: false, + // 换行符使用 lf 结尾是 可选值"" + endOfLine: 'lf' +}; diff --git a/README.md b/README.md new file mode 100644 index 0000000..33de8fd --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Luenmei Operational Cockpit MS + +> 联美运营驾驶舱管理系统 + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build +``` diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..ea55dc1 Binary files /dev/null and b/favicon.ico differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..c356dbe --- /dev/null +++ b/index.html @@ -0,0 +1,117 @@ + + + + + + + + + + + + 联美运营驾驶舱管理系统 + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..fcfc7e5 --- /dev/null +++ b/package.json @@ -0,0 +1,70 @@ +{ + "name": "luenmei-operational-cockpit-ms", + "version": "1.0.0", + "scripts": { + "dev": "vite serve --mode development", + "build:prod": "vite build --mode production &&vue-tsc --noEmit", + "build:stage": "vite build --mode staging", + "serve": "vite preview", + "lint": "eslint src/**/*.{ts,js,vue} --fix", + "prepare": "husky install", + "prettier": "prettier --write ." + }, + "dependencies": { + "@types/mockjs": "^1.0.7", + "@vueuse/core": "^9.1.1", + "@wangeditor/editor": "^5.0.0", + "@wangeditor/editor-for-vue": "^5.1.10", + "axios": "^1.3.4", + "echarts": "^5.2.2", + "element-plus": "^2.2.32", + "mitt": "^3.0.1", + "mockjs": "^1.1.0", + "nprogress": "^0.2.0", + "path-browserify": "^1.0.1", + "path-to-regexp": "^6.2.0", + "pinia": "^2.0.12", + "screenfull": "^6.0.2", + "terser": "^5.17.6", + "vue": "^3.2.45", + "vue-i18n": "^9.1.9", + "vue-router": "^4.1.6", + "vxe-table": "^4.5.6", + "xe-utils": "^3.5.12", + "xgplayer": "^3.0.2" + }, + "devDependencies": { + "@commitlint/cli": "^16.2.3", + "@commitlint/config-conventional": "^16.2.1", + "@iconify-json/ep": "^1.1.8", + "@types/nprogress": "^0.2.0", + "@types/path-browserify": "^1.0.0", + "@typescript-eslint/eslint-plugin": "^5.19.0", + "@typescript-eslint/parser": "^5.19.0", + "@vicons/antd": "^0.12.0", + "@vicons/ionicons5": "^0.12.0", + "@vicons/tabler": "^0.12.0", + "@vitejs/plugin-vue": "^4.2.3", + "autoprefixer": "^10.4.13", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-vue": "^9.9.0", + "fast-glob": "^3.2.11", + "husky": "^7.0.4", + "naive-ui": "^2.34.4", + "postcss": "^8.4.20", + "prettier": "^2.6.2", + "sass": "^1.53.0", + "typescript": "^4.7.4", + "unplugin-auto-import": "^0.13.0", + "unplugin-icons": "^0.15.1", + "unplugin-vue-components": "^0.23.0", + "vite": "^4.3.9", + "vite-plugin-svg-icons": "^2.0.1", + "vue-eslint-parser": "^9.1.0", + "vue-tsc": "^0.35.0" + }, + "repository": "https://gitee.com/fuguobin/vue-vite-project-admin.git", + "author": "Daniel" +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..ea55dc1 Binary files /dev/null and b/public/favicon.ico differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..9b70e17 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,11 @@ + + + diff --git a/src/api/auth/index.ts b/src/api/auth/index.ts new file mode 100644 index 0000000..7993b6a --- /dev/null +++ b/src/api/auth/index.ts @@ -0,0 +1,37 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { CaptchaResult, LoginData, LoginResult } from './types'; + +/** + * 登录API + * + * @param data {LoginData} + * @returns + */ +export function loginApi(data: LoginData): AxiosPromise { + return request({ + url: '/login', + method: 'post', + data: data + }); +} + +/** + * 注销API + */ +export function logoutApi() { + return request({ + url: '/logout', + method: 'delete' + }); +} + +/** + * 获取验证码 + */ +export function getCaptchaApi(): AxiosPromise { + return request({ + url: '/api/v1/auth/captcha', + method: 'get' + }); +} diff --git a/src/api/auth/types.ts b/src/api/auth/types.ts new file mode 100644 index 0000000..2e103a8 --- /dev/null +++ b/src/api/auth/types.ts @@ -0,0 +1,49 @@ +/** + * 登录请求参数 + */ +export interface LoginData { + /** + * 用户名 + */ + username: string; + /** + * 密码 + */ + password: string; +} + +/** + * 登录响应 + */ +export interface LoginResult { + /** + * 访问token + */ + token?: string; + /** + * 过期时间(单位:毫秒) + */ + expires?: number; + /** + * 刷新token + */ + refreshToken?: string; + /** + * token 类型 + */ + tokenType?: string; +} + +/** + * 验证码响应 + */ +export interface CaptchaResult { + /** + * 验证码缓存key + */ + verifyCodeKey: string; + /** + * 验证码图片Base64字符串 + */ + verifyCodeBase64: string; +} diff --git a/src/api/dept/index.ts b/src/api/dept/index.ts new file mode 100644 index 0000000..2ba1e20 --- /dev/null +++ b/src/api/dept/index.ts @@ -0,0 +1,77 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { DeptForm, DeptQuery, DeptVO } from './types'; + +/** + * 部门树形表格 + * + * @param queryParams + */ +export function listDepts(queryParams?: DeptQuery): AxiosPromise { + return request({ + url: '/api/v1/dept', + method: 'get', + params: queryParams + }); +} + +/** + * 部门下拉列表 + */ +export function listDeptOptions(): AxiosPromise<[]> { + return request({ + url: '/api/v1/dept/options', + method: 'get' + }); +} + +/** + * 获取部门详情 + * + * @param id + */ +export function getDeptForm(id: number): AxiosPromise { + return request({ + url: '/api/v1/dept/' + id + '/form', + method: 'get' + }); +} + +/** + * 新增部门 + * + * @param data + */ +export function addDept(data: DeptForm) { + return request({ + url: '/api/v1/dept', + method: 'post', + data: data + }); +} + +/** + * 修改部门 + * + * @param id + * @param data + */ +export function updateDept(id: number, data: DeptForm) { + return request({ + url: '/api/v1/dept/' + id, + method: 'put', + data: data + }); +} + +/** + * 删除部门 + * + * @param ids + */ +export function deleteDept(ids: string) { + return request({ + url: '/api/v1/dept/' + ids, + method: 'delete' + }); +} diff --git a/src/api/dept/types.ts b/src/api/dept/types.ts new file mode 100644 index 0000000..408c39c --- /dev/null +++ b/src/api/dept/types.ts @@ -0,0 +1,71 @@ +/** + * 部门查询参数 + */ +export interface DeptQuery { + keywords?: string; + status?: number; +} + +/** + * 部门类型 + */ +export interface DeptVO { + /** + * 子部门 + */ + children?: DeptVO[]; + /** + * 创建时间 + */ + createTime?: Date; + /** + * 部门ID + */ + id?: number; + /** + * 部门名称 + */ + name?: string; + /** + * 父部门ID + */ + parentId?: number; + /** + * 排序 + */ + sort?: number; + /** + * 状态(1:启用;0:禁用) + */ + status?: number; + /** + * 修改时间 + */ + updateTime?: Date; +} + +/** + * 部门表单类型 + */ +export interface DeptForm { + /** + * 部门ID(新增不填) + */ + id?: number; + /** + * 部门名称 + */ + name?: string; + /** + * 父部门ID + */ + parentId: number; + /** + * 排序 + */ + sort?: number; + /** + * 状态(1:启用;0:禁用) + */ + status?: number; +} diff --git a/src/api/device/index.ts b/src/api/device/index.ts new file mode 100644 index 0000000..b87a86f --- /dev/null +++ b/src/api/device/index.ts @@ -0,0 +1,50 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { deviceVo } from './types'; + +/** + * 获取用户部门 + */ +export function getDept(): AxiosPromise<[]> { + return request({ + url: '/bi/configuration/getMenu', + method: 'get' + }); +} + +/** + * 获取设备列表 + * + * @param params 部门Id + */ +export function getDeviceList(params: number): AxiosPromise { + return request({ + url: '/bi/configuration/getDeviceListByDept/'+params, + method: 'get' + }); +} + +/** + * 获取设备列表 + * + * @param params 设备Id + */ +export function getViewInfo(params: number): AxiosPromise { + return request({ + url: '/bi/configuration/getViewInfoById/'+params, + method: 'get' + }); +} + +/** + * 配置项修改 + * + * @param data + */ +export function editConfig(data: any) { + return request({ + url: '/dc/dcUserMonitorConfig/A', + method: 'post', + data: data + }); +} diff --git a/src/api/device/types.ts b/src/api/device/types.ts new file mode 100644 index 0000000..8acc06b --- /dev/null +++ b/src/api/device/types.ts @@ -0,0 +1,34 @@ +/** + * 部门列表 + */ +export interface menuVo { + createBy: null; + createTime: null; + updateBy: null; + updateTime: null; + remark: null; + deptId: number; + parentId: number; + ancestors: string; + deptName: string; + orderNum: number; + leader: string; + phone: string; + email: string; + status: string; + delFlag: null; + parentName: string; + children?: menuVo[]; +} + +/** + * 设备列表 + */ +export interface deviceVo { + deviceUUID: string; + deviceName: string; + partionCounts: number; + orgCode: number; + sourceType: string; + ifControl: number; +} diff --git a/src/api/file/index.ts b/src/api/file/index.ts new file mode 100644 index 0000000..350ce7d --- /dev/null +++ b/src/api/file/index.ts @@ -0,0 +1,34 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { FileInfo } from './types'; + +/** + * 上传文件 + * + * @param file + */ +export function uploadFileApi(file: File): AxiosPromise { + const formData = new FormData(); + formData.append('file', file); + return request({ + url: '/api/v1/files', + method: 'post', + data: formData, + headers: { + 'Content-Type': 'multipart/form-data' + } + }); +} + +/** + * 删除文件 + * + * @param filePath 文件完整路径 + */ +export function deleteFileApi(filePath?: string) { + return request({ + url: '/api/v1/files', + method: 'delete', + params: { filePath: filePath } + }); +} diff --git a/src/api/file/types.ts b/src/api/file/types.ts new file mode 100644 index 0000000..22b2be5 --- /dev/null +++ b/src/api/file/types.ts @@ -0,0 +1,7 @@ +/** + * 文件API类型声明 + */ +export interface FileInfo { + name: string; + url: string; +} diff --git a/src/api/menu/index.ts b/src/api/menu/index.ts new file mode 100644 index 0000000..9149db2 --- /dev/null +++ b/src/api/menu/index.ts @@ -0,0 +1,87 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { MenuQuery, MenuVO, MenuForm } from './types'; + +/** + * 获取路由列表 + */ +export function listRoutes() { + return request({ + url: '/getRouters', + method: 'get' + }); +} + +/** + * 获取菜单树形列表 + * + * @param queryParams + */ +export function listMenus(queryParams: MenuQuery): AxiosPromise { + return request({ + url: '/system/menu/roleMenuTreeselect', + method: 'get', + params: queryParams + }); +} + +/** + * 获取菜单下拉树形列表 + */ +export function listMenuOptions(): AxiosPromise { + return request({ + url: '/api/v1/menus/options', + method: 'get' + }); +} + +/** + * 获取菜单表单数据 + * + * @param id + */ +export function getMenuForm(id: number): AxiosPromise { + return request({ + url: '/api/v1/menus/' + id + '/form', + method: 'get' + }); +} + +/** + * 添加菜单 + * + * @param data + */ +export function addMenu(data: MenuForm) { + return request({ + url: '/api/v1/menus', + method: 'post', + data: data + }); +} + +/** + * 修改菜单 + * + * @param id + * @param data + */ +export function updateMenu(id: string, data: MenuForm) { + return request({ + url: '/api/v1/menus/' + id, + method: 'put', + data: data + }); +} + +/** + * 删除菜单 + * + * @param id 菜单ID + */ +export function deleteMenu(id: number) { + return request({ + url: '/api/v1/menus/' + id, + method: 'delete' + }); +} diff --git a/src/api/menu/types.ts b/src/api/menu/types.ts new file mode 100644 index 0000000..7cbd05e --- /dev/null +++ b/src/api/menu/types.ts @@ -0,0 +1,115 @@ +import { MenuTypeEnum } from '@/enums/MenuTypeEnum'; + +/** + * 菜单查询参数类型 + */ +export interface MenuQuery { + keywords?: string; +} + +/** + * 菜单视图对象类型 + */ +export interface MenuVO { + /** + * 子菜单 + */ + children?: MenuVO[]; + /** + * 组件路径 + */ + component?: string; + /** + * ICON + */ + icon?: string; + /** + * 菜单ID + */ + id?: number; + /** + * 菜单名称 + */ + name?: string; + /** + * 父菜单ID + */ + parentId?: number; + /** + * 按钮权限标识 + */ + perm?: string; + /** + * 跳转路径 + */ + redirect?: string; + /** + * 路由名称 + */ + routeName?: string; + /** + * 路由相对路径 + */ + routePath?: string; + /** + * 菜单排序(数字越小排名越靠前) + */ + sort?: number; + /** + * 菜单类型 + */ + type?: MenuTypeEnum; + /** + * 菜单是否可见(1:显示;0:隐藏) + */ + visible?: number; +} + +/** + * 菜单表单对象类型 + */ +export interface MenuForm { + /** + * 菜单ID + */ + id?: string; + /** + * 父菜单ID + */ + parentId?: number; + /** + * 菜单名称 + */ + name?: string; + /** + * 菜单是否可见(1:是;0:否;) + */ + visible: number; + icon?: string; + /** + * 排序 + */ + sort: number; + /** + * 组件路径 + */ + component?: string; + /** + * 路由路径 + */ + path?: string; + /** + * 跳转路由路径 + */ + redirect?: string; + + /** + * 菜单类型 + */ + type: MenuTypeEnum; + + /** + * 权限标识 + */ + perm?: string; +} diff --git a/src/api/pro/index.ts b/src/api/pro/index.ts new file mode 100644 index 0000000..787ed6a --- /dev/null +++ b/src/api/pro/index.ts @@ -0,0 +1,112 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { RoleQuery, RolePageResult, RoleForm } from './types'; + +/** + * 获取分页数据 + * + * @param queryParams + */ +export function getProPage( + queryParams?: RoleQuery +): AxiosPromise { + return request({ + url: '/api/v1/pro/page', + method: 'get', + data: queryParams + }); +} + +/** + * 获取角色下拉数据 + * + * @param queryParams + */ +export function listBrandsOptions( + queryParams?: RoleQuery +): AxiosPromise { + return request({ + url: '/api/v1/brands/options', + method: 'get', + params: queryParams + }); +} + +/** + * 获取角色的菜单ID集合 + * + * @param queryParams + */ +export function getRoleMenuIds(roleId: number): AxiosPromise { + return request({ + url: '/api/v1/roles/menuIds', + method: 'get' + }); +} + +/** + * 分配菜单权限给角色 + * + * @param queryParams + */ +export function updateRoleMenus( + roleId: number, + data: number[] +): AxiosPromise { + return request({ + url: '/api/v1/roles/' + roleId + '/menus', + method: 'put', + data: data + }); +} + +/** + * 获取角色详情 + * + * @param id + */ +export function getproForm(id: number): AxiosPromise { + return request({ + url: '/api/v1/pro/form', + method: 'get' + }); +} + +/** + * 添加角色 + * + * @param data + */ +export function addRole(data: RoleForm) { + return request({ + url: '/api/v1/roles', + method: 'post', + data: data + }); +} + +/** + * 更新角色 + * + * @param id + * @param data + */ +export function updateRole(id: number, data: RoleForm) { + return request({ + url: '/api/v1/roles/' + id, + method: 'put', + data: data + }); +} + +/** + * 批量删除角色,多个以英文逗号(,)分割 + * + * @param ids + */ +export function deleteRoles(ids: string) { + return request({ + url: '/api/v1/roles/' + ids, + method: 'delete' + }); +} diff --git a/src/api/pro/types.ts b/src/api/pro/types.ts new file mode 100644 index 0000000..16fabba --- /dev/null +++ b/src/api/pro/types.ts @@ -0,0 +1,91 @@ +/** + * 角色查询参数 + */ +export interface RoleQuery extends PageQuery { + keywords?: string; +} + +/** + * 角色分页对象 + */ +export interface RolePageVO { + /** + * 角色编码 + */ + code?: string; + + /** + * 角色ID + */ + id?: number; + /** + * 角色名称 + */ + name?: string; + /** + * 排序 + */ + sort?: number; + /** + * 角色状态 + */ + status?: number; + /** + * 创建时间 + */ + createTime?: Date; + /** + * 修改时间 + */ + updateTime?: Date; +} + +/** + * 角色分页 + */ +export type RolePageResult = PageResult; + +/** + * 商品表单对象 + */ +export interface RoleForm { + /** + * 角色ID + */ + id?: number; + + /** + * 商品名称 + */ + name: string; + + /** + * 商品品牌 + */ + brands: string; + + /** + * 商品价格 + */ + price?: number; + + /** + * 商品图片 + */ + picture: string; + + /** + * 商品类别 + */ + category?: RoleForm[]; + + /** + * 排序 + */ + sort?: number; + + /** + * 介绍 + */ + desc?: string; +} diff --git a/src/api/role/index.ts b/src/api/role/index.ts new file mode 100644 index 0000000..e5ad71d --- /dev/null +++ b/src/api/role/index.ts @@ -0,0 +1,112 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { RoleQuery, RolePageResult, RoleForm } from './types'; + +/** + * 获取角色分页数据 + * + * @param queryParams + */ +export function getRolePage( + queryParams?: RoleQuery +): AxiosPromise { + return request({ + url: '/api/v1/roles/page', + method: 'get', + data: queryParams + }); +} + +/** + * 获取角色下拉数据 + * + * @param queryParams + */ +export function listRoleOptions( + queryParams?: RoleQuery +): AxiosPromise { + return request({ + url: '/api/v1/roles/options', + method: 'get', + params: queryParams + }); +} + +/** + * 获取角色的菜单ID集合 + * + * @param queryParams + */ +export function getRoleMenuIds(roleId: number): AxiosPromise { + return request({ + url: '/api/v1/roles/menuIds', + method: 'get' + }); +} + +/** + * 分配菜单权限给角色 + * + * @param queryParams + */ +export function updateRoleMenus( + roleId: number, + data: number[] +): AxiosPromise { + return request({ + url: '/api/v1/roles/' + roleId + '/menus', + method: 'put', + data: data + }); +} + +/** + * 获取角色详情 + * + * @param id + */ +export function getRoleForm(id: number): AxiosPromise { + return request({ + url: '/api/v1/roles/form', + method: 'get' + }); +} + +/** + * 添加角色 + * + * @param data + */ +export function addRole(data: RoleForm) { + return request({ + url: '/api/v1/roles', + method: 'post', + data: data + }); +} + +/** + * 更新角色 + * + * @param id + * @param data + */ +export function updateRole(id: number, data: RoleForm) { + return request({ + url: '/api/v1/roles/' + id, + method: 'put', + data: data + }); +} + +/** + * 批量删除角色,多个以英文逗号(,)分割 + * + * @param ids + */ +export function deleteRoles(ids: string) { + return request({ + url: '/api/v1/roles/' + ids, + method: 'delete' + }); +} diff --git a/src/api/role/types.ts b/src/api/role/types.ts new file mode 100644 index 0000000..2d259c7 --- /dev/null +++ b/src/api/role/types.ts @@ -0,0 +1,78 @@ +/** + * 角色查询参数 + */ +export interface RoleQuery extends PageQuery { + keywords?: string; +} + +/** + * 角色分页对象 + */ +export interface RolePageVO { + /** + * 角色编码 + */ + code?: string; + + /** + * 角色ID + */ + id?: number; + /** + * 角色名称 + */ + name?: string; + /** + * 排序 + */ + sort?: number; + /** + * 角色状态 + */ + status?: number; + /** + * 创建时间 + */ + createTime?: Date; + /** + * 修改时间 + */ + updateTime?: Date; +} + +/** + * 角色分页 + */ +export type RolePageResult = PageResult; + +/** + * 角色表单对象 + */ +export interface RoleForm { + /** + * 角色ID + */ + id?: number; + + /** + * 角色编码 + */ + code: string; + /** + * 数据权限 + */ + dataScope?: number; + + /** + * 角色名称 + */ + name: string; + /** + * 排序 + */ + sort?: number; + /** + * 角色状态(1-正常;0-停用) + */ + status?: number; +} diff --git a/src/api/table/list.ts b/src/api/table/list.ts new file mode 100644 index 0000000..0089ac0 --- /dev/null +++ b/src/api/table/list.ts @@ -0,0 +1,58 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { TableVo } from './types'; + +/** + * 获取左侧菜单 + */ +export function getMenu(): AxiosPromise<[]> { + return request({ + url: '/bi/opt/getMenu', + method: 'get' + }); +} + +/** + * 获取热源信息 + */ +export function getStationInfo(): AxiosPromise<[]> { + return request({ + url: '/bi/opt/getStationInfo', + method: 'get' + }); +} + +/** + * 获取表格header + */ +export function getTableHeader(): AxiosPromise<[]> { + return request({ + url: '/bi/opt/getTableHeader', + method: 'get' + }); +} + +/** + * 获取表格数据 + * + * @param id + */ +export function getTableData(params: number): AxiosPromise { + return request({ + url: '/bi/opt/getTableData/'+params, + method: 'get' + }); +} + +/** + * 配置项修改 + * + * @param data + */ +export function editConfig(data: any) { + return request({ + url: '/dc/dcUserMonitorConfig/A', + method: 'post', + data: data + }); +} diff --git a/src/api/table/types.ts b/src/api/table/types.ts new file mode 100644 index 0000000..47d28d8 --- /dev/null +++ b/src/api/table/types.ts @@ -0,0 +1,58 @@ +/** + * 表格参数 + */ +export interface TableVo { + id: string; + deviceuuid: string; + areaid: string; + C018: object; + C019: object; + C020: object; + C021: object; + C022: object; + C023: object; + C024: object; + C025: object; + source: string; + data: string; + C007: object; + C026: object; + C027: object; + C028: object; + C029: object; + C005: object; + C006: object; + C001: object; + C002: object; + C003: object; + C004: object; + C008: object; + C009: object; + C010: object; + C011: object; + C012: object; + C013: object; + C014: object; + C015: object; + C016: boolean; + C017: object; + C030: object; + C031: object; + C032: object; + C033: object; + C034: object; + C035: object; + C036: object; + C037: object; + C038: object; + C039: object; + C040: object; + C041: object; + C042: object; + C043: object; + C044: object; + C045: object; + C046: object; + C047: object; + C048: object; +} diff --git a/src/api/user/index.ts b/src/api/user/index.ts new file mode 100644 index 0000000..e6100a0 --- /dev/null +++ b/src/api/user/index.ts @@ -0,0 +1,154 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { UserForm, UserInfo, UserPageVO, UserQuery } from './types'; + +/** + * 登录成功后获取用户信息(昵称、头像、权限集合和角色集合) + */ +export function getUserInfo(): AxiosPromise { + return request({ + url: '/getInfo', + method: 'get' + }); +} + +/** + * 获取用户分页列表 + * + * @param queryParams + */ +export function getUserPage( + queryParams: UserQuery +): AxiosPromise> { + return request({ + url: '/api/v1/users/page', + method: 'get', + data: queryParams + }); +} + +/** + * 获取用户表单详情 + * + * @param userId + */ +export function getUserForm(userId: number): AxiosPromise { + return request({ + url: '/api/v1/users/form', + method: 'get' + }); +} + +/** + * 添加用户 + * + * @param data + */ +export function addUser(data: any) { + return request({ + url: '/api/v1/users', + method: 'post', + data: data + }); +} + +/** + * 修改用户 + * + * @param id + * @param data + */ +export function updateUser(id: number, data: UserForm) { + return request({ + url: '/api/v1/users/' + id, + method: 'put', + data: data + }); +} + +/** + * 修改用户状态 + * + * @param id + * @param status + */ +export function updateUserStatus(id: number, status: number) { + return request({ + url: '/api/v1/users/' + id + '/status', + method: 'patch', + params: { status: status } + }); +} + +/** + * 修改用户密码 + * + * @param id + * @param password + */ +export function updateUserPassword(id: number, password: string) { + return request({ + url: '/api/v1/users/' + id + '/password', + method: 'patch', + params: { password: password } + }); +} + +/** + * 删除用户 + * + * @param ids + */ +export function deleteUsers(ids: string) { + return request({ + url: '/api/v1/users/' + ids, + method: 'delete' + }); +} + +/** + * 下载用户导入模板 + * + * @returns + */ +export function downloadTemplateApi() { + return request({ + url: '/api/v1/users/template', + method: 'get', + responseType: 'arraybuffer' + }); +} + +/** + * 导出用户 + * + * @param queryParams + * @returns + */ +export function exportUser(queryParams: UserQuery) { + return request({ + url: '/api/v1/users/_export', + method: 'get', + params: queryParams, + responseType: 'arraybuffer' + }); +} + +/** + * 导入用户 + * + * @param file + */ +export function importUser(deptId: number, file: File) { + const formData = new FormData(); + formData.append('file', file); + return request({ + url: '/api/v1/users/_import', + method: 'post', + params: { deptId: deptId }, + data: formData, + headers: { + 'Content-Type': 'multipart/form-data' + } + }); +} diff --git a/src/api/user/types.ts b/src/api/user/types.ts new file mode 100644 index 0000000..13f8edc --- /dev/null +++ b/src/api/user/types.ts @@ -0,0 +1,115 @@ +/** + * 登录用户信息 + */ +export interface UserInfo { + // nickname: string; + // avatar: string; + // roles: string[]; + // perms: string[]; + permissions: string[]; + roles: string[]; + loginIp: string; + user?: object; +} + +/** + * 用户查询对象类型 + */ +export interface UserQuery extends PageQuery { + keywords?: string; + status?: number; + deptId?: number; +} + +/** + * 用户分页对象 + */ +export interface UserPageVO { + /** + * 用户头像地址 + */ + avatar?: string; + /** + * 创建时间 + */ + createTime?: Date; + /** + * 部门名称 + */ + deptName?: string; + /** + * 用户邮箱 + */ + email?: string; + /** + * 性别 + */ + genderLabel?: string; + /** + * 用户ID + */ + id?: number; + /** + * 手机号 + */ + mobile?: string; + /** + * 用户昵称 + */ + nickname?: string; + /** + * 角色名称,多个使用英文逗号(,)分割 + */ + roleNames?: string; + /** + * 用户状态(1:启用;0:禁用) + */ + status?: number; + /** + * 用户名 + */ + username?: string; +} + +/** + * 用户表单类型 + */ +export interface UserForm { + /** + * 用户头像 + */ + avatar?: string; + /** + * 部门ID + */ + deptId?: number; + /** + * 邮箱 + */ + email?: string; + /** + * 性别 + */ + sex?: number; + /** + * 用户ID + */ + id?: number; + mobile?: string; + /** + * 昵称 + */ + nickname?: string; + /** + * 角色ID集合 + */ + roleIds?: number[]; + /** + * 用户状态(1:正常;0:禁用) + */ + status?: number; + /** + * 用户名 + */ + username?: string; +} diff --git a/src/assets/401_images/401.gif b/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/src/assets/401_images/401.gif differ diff --git a/src/assets/404_images/404.png b/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/src/assets/404_images/404.png differ diff --git a/src/assets/404_images/404_cloud.png b/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/src/assets/404_images/404_cloud.png differ diff --git a/src/assets/iconfont/demo.css b/src/assets/iconfont/demo.css new file mode 100644 index 0000000..a67054a --- /dev/null +++ b/src/assets/iconfont/demo.css @@ -0,0 +1,539 @@ +/* Logo 字体 */ +@font-face { + font-family: "iconfont logo"; + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); +} + +.logo { + font-family: "iconfont logo"; + font-size: 160px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* tabs */ +.nav-tabs { + position: relative; +} + +.nav-tabs .nav-more { + position: absolute; + right: 0; + bottom: 0; + height: 42px; + line-height: 42px; + color: #666; +} + +#tabs { + border-bottom: 1px solid #eee; +} + +#tabs li { + cursor: pointer; + width: 100px; + height: 40px; + line-height: 40px; + text-align: center; + font-size: 16px; + border-bottom: 2px solid transparent; + position: relative; + z-index: 1; + margin-bottom: -1px; + color: #666; +} + + +#tabs .active { + border-bottom-color: #f00; + color: #222; +} + +.tab-container .content { + display: none; +} + +/* 页面布局 */ +.main { + padding: 30px 100px; + width: 960px; + margin: 0 auto; +} + +.main .logo { + color: #333; + text-align: left; + margin-bottom: 30px; + line-height: 1; + height: 110px; + margin-top: -50px; + overflow: hidden; + *zoom: 1; +} + +.main .logo a { + font-size: 160px; + color: #333; +} + +.helps { + margin-top: 40px; +} + +.helps pre { + padding: 20px; + margin: 10px 0; + border: solid 1px #e7e1cd; + background-color: #fffdef; + overflow: auto; +} + +.icon_lists { + width: 100% !important; + overflow: hidden; + *zoom: 1; +} + +.icon_lists li { + width: 100px; + margin-bottom: 10px; + margin-right: 20px; + text-align: center; + list-style: none !important; + cursor: default; +} + +.icon_lists li .code-name { + line-height: 1.2; +} + +.icon_lists .icon { + display: block; + height: 100px; + line-height: 100px; + font-size: 42px; + margin: 10px auto; + color: #333; + -webkit-transition: font-size 0.25s linear, width 0.25s linear; + -moz-transition: font-size 0.25s linear, width 0.25s linear; + transition: font-size 0.25s linear, width 0.25s linear; +} + +.icon_lists .icon:hover { + font-size: 100px; +} + +.icon_lists .svg-icon { + /* 通过设置 font-size 来改变图标大小 */ + width: 1em; + /* 图标和文字相邻时,垂直对齐 */ + vertical-align: -0.15em; + /* 通过设置 color 来改变 SVG 的颜色/fill */ + fill: currentColor; + /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 + normalize.css 中也包含这行 */ + overflow: hidden; +} + +.icon_lists li .name, +.icon_lists li .code-name { + color: #666; +} + +/* markdown 样式 */ +.markdown { + color: #666; + font-size: 14px; + line-height: 1.8; +} + +.highlight { + line-height: 1.5; +} + +.markdown img { + vertical-align: middle; + max-width: 100%; +} + +.markdown h1 { + color: #404040; + font-weight: 500; + line-height: 40px; + margin-bottom: 24px; +} + +.markdown h2, +.markdown h3, +.markdown h4, +.markdown h5, +.markdown h6 { + color: #404040; + margin: 1.6em 0 0.6em 0; + font-weight: 500; + clear: both; +} + +.markdown h1 { + font-size: 28px; +} + +.markdown h2 { + font-size: 22px; +} + +.markdown h3 { + font-size: 16px; +} + +.markdown h4 { + font-size: 14px; +} + +.markdown h5 { + font-size: 12px; +} + +.markdown h6 { + font-size: 12px; +} + +.markdown hr { + height: 1px; + border: 0; + background: #e9e9e9; + margin: 16px 0; + clear: both; +} + +.markdown p { + margin: 1em 0; +} + +.markdown>p, +.markdown>blockquote, +.markdown>.highlight, +.markdown>ol, +.markdown>ul { + width: 80%; +} + +.markdown ul>li { + list-style: circle; +} + +.markdown>ul li, +.markdown blockquote ul>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown>ul li p, +.markdown>ol li p { + margin: 0.6em 0; +} + +.markdown ol>li { + list-style: decimal; +} + +.markdown>ol li, +.markdown blockquote ol>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown code { + margin: 0 3px; + padding: 0 5px; + background: #eee; + border-radius: 3px; +} + +.markdown strong, +.markdown b { + font-weight: 600; +} + +.markdown>table { + border-collapse: collapse; + border-spacing: 0px; + empty-cells: show; + border: 1px solid #e9e9e9; + width: 95%; + margin-bottom: 24px; +} + +.markdown>table th { + white-space: nowrap; + color: #333; + font-weight: 600; +} + +.markdown>table th, +.markdown>table td { + border: 1px solid #e9e9e9; + padding: 8px 16px; + text-align: left; +} + +.markdown>table th { + background: #F7F7F7; +} + +.markdown blockquote { + font-size: 90%; + color: #999; + border-left: 4px solid #e9e9e9; + padding-left: 0.8em; + margin: 1em 0; +} + +.markdown blockquote p { + margin: 0; +} + +.markdown .anchor { + opacity: 0; + transition: opacity 0.3s ease; + margin-left: 8px; +} + +.markdown .waiting { + color: #ccc; +} + +.markdown h1:hover .anchor, +.markdown h2:hover .anchor, +.markdown h3:hover .anchor, +.markdown h4:hover .anchor, +.markdown h5:hover .anchor, +.markdown h6:hover .anchor { + opacity: 1; + display: inline-block; +} + +.markdown>br, +.markdown>p>br { + clear: both; +} + + +.hljs { + display: block; + background: white; + padding: 0.5em; + color: #333333; + overflow-x: auto; +} + +.hljs-comment, +.hljs-meta { + color: #969896; +} + +.hljs-string, +.hljs-variable, +.hljs-template-variable, +.hljs-strong, +.hljs-emphasis, +.hljs-quote { + color: #df5000; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-type { + color: #a71d5d; +} + +.hljs-literal, +.hljs-symbol, +.hljs-bullet, +.hljs-attribute { + color: #0086b3; +} + +.hljs-section, +.hljs-name { + color: #63a35c; +} + +.hljs-tag { + color: #333333; +} + +.hljs-title, +.hljs-attr, +.hljs-selector-id, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #795da3; +} + +.hljs-addition { + color: #55a532; + background-color: #eaffea; +} + +.hljs-deletion { + color: #bd2c00; + background-color: #ffecec; +} + +.hljs-link { + text-decoration: underline; +} + +/* 代码高亮 */ +/* PrismJS 1.15.0 +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre)>code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre)>code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/src/assets/iconfont/demo_index.html b/src/assets/iconfont/demo_index.html new file mode 100644 index 0000000..334dc62 --- /dev/null +++ b/src/assets/iconfont/demo_index.html @@ -0,0 +1,1384 @@ + + + + + iconfont Demo + + + + + + + + + + + + + +
+

+ + +

+ +
+
+
    + +
  • + +
    箭头_切换向下
    +
    &#xeb04;
    +
  • + +
  • + +
    箭头_切换向上
    +
    &#xeb05;
    +
  • + +
  • + +
    访问量
    +
    &#xe6a5;
    +
  • + +
  • + +
    总访问量
    +
    &#xe651;
    +
  • + +
  • + +
    订单成交额
    +
    &#xec18;
    +
  • + +
  • + +
    成交数据-01
    +
    &#xe682;
    +
  • + +
  • + +
    下载数量
    +
    &#xe6f8;
    +
  • + +
  • + +
    用户管理
    +
    &#xe617;
    +
  • + +
  • + +
    首页
    +
    &#xe62e;
    +
  • + +
  • + +
    编辑
    +
    &#xeb61;
    +
  • + +
  • + +
    icon_应用管理
    +
    &#xeb8f;
    +
  • + +
  • + +
    setting
    +
    &#xe78e;
    +
  • + +
  • + +
    folder-open
    +
    &#xe7d1;
    +
  • + +
  • + +
    image
    +
    &#xe7de;
    +
  • + +
  • + +
    云端下载
    +
    &#xec1d;
    +
  • + +
  • + +
    云端上传
    +
    &#xec1e;
    +
  • + +
  • + +
    角色管理
    +
    &#xe645;
    +
  • + +
  • + +
    key
    +
    &#xe775;
    +
  • + +
  • + +
    网商银行无线端_安全
    +
    &#xe627;
    +
  • + +
  • + +
    安全
    +
    &#xec4d;
    +
  • + +
  • + +
    其他
    +
    &#xe60b;
    +
  • + +
  • + +
    page_last
    +
    &#xe65b;
    +
  • + +
  • + +
    search
    +
    &#xe63c;
    +
  • + +
  • + +
    icon_设置
    +
    &#xeb8d;
    +
  • + +
  • + +
    重置
    +
    &#xe614;
    +
  • + +
  • + +
    camera
    +
    &#xe666;
    +
  • + +
  • + +
    qr_code
    +
    &#xe6b0;
    +
  • + +
  • + +
    delete
    +
    &#xe6b4;
    +
  • + +
  • + +
    地址
    +
    &#xe652;
    +
  • + +
  • + +
    闹钟
    +
    &#xe890;
    +
  • + +
  • + +
    地址
    +
    &#xe63d;
    +
  • + +
  • + +
    scan
    +
    &#xe68b;
    +
  • + +
  • + +
    edit
    +
    &#xe6aa;
    +
  • + +
  • + +
    page_first
    +
    &#xe659;
    +
  • + +
  • + +
    字体大小
    +
    &#xe689;
    +
  • + +
  • + +
    filter
    +
    &#xe648;
    +
  • + +
  • + +
    ashbin
    +
    &#xe665;
    +
  • + +
  • + +
    code
    +
    &#xe667;
    +
  • + +
  • + +
    copy
    +
    &#xe66a;
    +
  • + +
  • + +
    link
    +
    &#xe676;
    +
  • + +
  • + +
    layers
    +
    &#xe675;
    +
  • + +
  • + +
    fullscreen-expand
    +
    &#xe677;
    +
  • + +
  • + +
    menu
    +
    &#xe678;
    +
  • + +
  • + +
    minus
    +
    &#xe67a;
    +
  • + +
  • + +
    refresh
    +
    &#xe67b;
    +
  • + +
  • + +
    scanning
    +
    &#xe67d;
    +
  • + +
  • + +
    关闭其他-01
    +
    &#xe624;
    +
  • + +
  • + +
    close
    +
    &#xe668;
    +
  • + +
  • + +
    arrow-down-filling
    +
    &#xe688;
    +
  • + +
  • + +
    中英切换
    +
    &#xe739;
    +
  • + +
  • + +
    密码
    +
    &#xe62a;
    +
  • + +
  • + +
    customer-fill
    +
    &#xe828;
    +
  • + +
+
+

Unicode 引用

+
+ +

Unicode 是字体在网页端最原始的应用方式,特点是:

+
    +
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • +
  • 默认情况下不支持多色,直接添加多色图标会自动去色。
  • +
+
+

注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)

+
+

Unicode 使用步骤如下:

+

第一步:拷贝项目下面生成的 @font-face

+
@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.woff2?t=1693533922330') format('woff2'),
+       url('iconfont.woff?t=1693533922330') format('woff'),
+       url('iconfont.ttf?t=1693533922330') format('truetype');
+}
+
+

第二步:定义使用 iconfont 的样式

+
.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+

第三步:挑选相应图标并获取字体编码,应用于页面

+
+<span class="iconfont">&#x33;</span>
+
+
+

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    + 箭头_切换向下 +
    +
    .icon-decline +
    +
  • + +
  • + +
    + 箭头_切换向上 +
    +
    .icon-rise +
    +
  • + +
  • + +
    + 访问量 +
    +
    .icon-visits-chart +
    +
  • + +
  • + +
    + 总访问量 +
    +
    .icon-user-visits +
    +
  • + +
  • + +
    + 订单成交额 +
    +
    .icon-turnover +
    +
  • + +
  • + +
    + 成交数据-01 +
    +
    .icon-num-transactions +
    +
  • + +
  • + +
    + 下载数量 +
    +
    .icon-num-download +
    +
  • + +
  • + +
    + 用户管理 +
    +
    .icon-user-mgt +
    +
  • + +
  • + +
    + 首页 +
    +
    .icon-home +
    +
  • + +
  • + +
    + 编辑 +
    +
    .icon-set-edit +
    +
  • + +
  • + +
    + icon_应用管理 +
    +
    .icon-app-mgt +
    +
  • + +
  • + +
    + setting +
    +
    .icon-setting +
    +
  • + +
  • + +
    + folder-open +
    +
    .icon-folder-open +
    +
  • + +
  • + +
    + image +
    +
    .icon-image +
    +
  • + +
  • + +
    + 云端下载 +
    +
    .icon-cloud-download +
    +
  • + +
  • + +
    + 云端上传 +
    +
    .icon-cloud-upload +
    +
  • + +
  • + +
    + 角色管理 +
    +
    .icon-role-mgt +
    +
  • + +
  • + +
    + key +
    +
    .icon-key +
    +
  • + +
  • + +
    + 网商银行无线端_安全 +
    +
    .icon-safe-code +
    +
  • + +
  • + +
    + 安全 +
    +
    .icon-safe +
    +
  • + +
  • + +
    + 其他 +
    +
    .icon-other +
    +
  • + +
  • + +
    + page_last +
    +
    .icon-page-right +
    +
  • + +
  • + +
    + search +
    +
    .icon-search +
    +
  • + +
  • + +
    + icon_设置 +
    +
    .icon-setup +
    +
  • + +
  • + +
    + 重置 +
    +
    .icon-reset +
    +
  • + +
  • + +
    + camera +
    +
    .icon-camera +
    +
  • + +
  • + +
    + qr_code +
    +
    .icon-qrcode +
    +
  • + +
  • + +
    + delete +
    +
    .icon-delete +
    +
  • + +
  • + +
    + 地址 +
    +
    .icon-address +
    +
  • + +
  • + +
    + 闹钟 +
    +
    .icon-clock +
    +
  • + +
  • + +
    + 地址 +
    +
    .icon-location +
    +
  • + +
  • + +
    + scan +
    +
    .icon-scan +
    +
  • + +
  • + +
    + edit +
    +
    .icon-edit +
    +
  • + +
  • + +
    + page_first +
    +
    .icon-page-left +
    +
  • + +
  • + +
    + 字体大小 +
    +
    .icon-font-size +
    +
  • + +
  • + +
    + filter +
    +
    .icon-filter +
    +
  • + +
  • + +
    + ashbin +
    +
    .icon-ashbin +
    +
  • + +
  • + +
    + code +
    +
    .icon-code +
    +
  • + +
  • + +
    + copy +
    +
    .icon-copy +
    +
  • + +
  • + +
    + link +
    +
    .icon-link +
    +
  • + +
  • + +
    + layers +
    +
    .icon-layers +
    +
  • + +
  • + +
    + fullscreen-expand +
    +
    .icon-fullscreen +
    +
  • + +
  • + +
    + menu +
    +
    .icon-menu +
    +
  • + +
  • + +
    + minus +
    +
    .icon-minus +
    +
  • + +
  • + +
    + refresh +
    +
    .icon-refresh +
    +
  • + +
  • + +
    + scanning +
    +
    .icon-scanning +
    +
  • + +
  • + +
    + 关闭其他-01 +
    +
    .icon-close-other +
    +
  • + +
  • + +
    + close +
    +
    .icon-close +
    +
  • + +
  • + +
    + arrow-down-filling +
    +
    .icon-arrow-down +
    +
  • + +
  • + +
    + 中英切换 +
    +
    .icon-language +
    +
  • + +
  • + +
    + 密码 +
    +
    .icon-lock +
    +
  • + +
  • + +
    + customer-fill +
    +
    .icon-user +
    +
  • + +
+
+

font-class 引用

+
+ +

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

+

与 Unicode 使用方式相比,具有如下特点:

+
    +
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • +
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 fontclass 代码:

+
<link rel="stylesheet" href="./iconfont.css">
+
+

第二步:挑选相应图标并获取类名,应用于页面:

+
<span class="iconfont icon-xxx"></span>
+
+
+

" + iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    箭头_切换向下
    +
    #icon-decline
    +
  • + +
  • + +
    箭头_切换向上
    +
    #icon-rise
    +
  • + +
  • + +
    访问量
    +
    #icon-visits-chart
    +
  • + +
  • + +
    总访问量
    +
    #icon-user-visits
    +
  • + +
  • + +
    订单成交额
    +
    #icon-turnover
    +
  • + +
  • + +
    成交数据-01
    +
    #icon-num-transactions
    +
  • + +
  • + +
    下载数量
    +
    #icon-num-download
    +
  • + +
  • + +
    用户管理
    +
    #icon-user-mgt
    +
  • + +
  • + +
    首页
    +
    #icon-home
    +
  • + +
  • + +
    编辑
    +
    #icon-set-edit
    +
  • + +
  • + +
    icon_应用管理
    +
    #icon-app-mgt
    +
  • + +
  • + +
    setting
    +
    #icon-setting
    +
  • + +
  • + +
    folder-open
    +
    #icon-folder-open
    +
  • + +
  • + +
    image
    +
    #icon-image
    +
  • + +
  • + +
    云端下载
    +
    #icon-cloud-download
    +
  • + +
  • + +
    云端上传
    +
    #icon-cloud-upload
    +
  • + +
  • + +
    角色管理
    +
    #icon-role-mgt
    +
  • + +
  • + +
    key
    +
    #icon-key
    +
  • + +
  • + +
    网商银行无线端_安全
    +
    #icon-safe-code
    +
  • + +
  • + +
    安全
    +
    #icon-safe
    +
  • + +
  • + +
    其他
    +
    #icon-other
    +
  • + +
  • + +
    page_last
    +
    #icon-page-right
    +
  • + +
  • + +
    search
    +
    #icon-search
    +
  • + +
  • + +
    icon_设置
    +
    #icon-setup
    +
  • + +
  • + +
    重置
    +
    #icon-reset
    +
  • + +
  • + +
    camera
    +
    #icon-camera
    +
  • + +
  • + +
    qr_code
    +
    #icon-qrcode
    +
  • + +
  • + +
    delete
    +
    #icon-delete
    +
  • + +
  • + +
    地址
    +
    #icon-address
    +
  • + +
  • + +
    闹钟
    +
    #icon-clock
    +
  • + +
  • + +
    地址
    +
    #icon-location
    +
  • + +
  • + +
    scan
    +
    #icon-scan
    +
  • + +
  • + +
    edit
    +
    #icon-edit
    +
  • + +
  • + +
    page_first
    +
    #icon-page-left
    +
  • + +
  • + +
    字体大小
    +
    #icon-font-size
    +
  • + +
  • + +
    filter
    +
    #icon-filter
    +
  • + +
  • + +
    ashbin
    +
    #icon-ashbin
    +
  • + +
  • + +
    code
    +
    #icon-code
    +
  • + +
  • + +
    copy
    +
    #icon-copy
    +
  • + +
  • + +
    link
    +
    #icon-link
    +
  • + +
  • + +
    layers
    +
    #icon-layers
    +
  • + +
  • + +
    fullscreen-expand
    +
    #icon-fullscreen
    +
  • + +
  • + +
    menu
    +
    #icon-menu
    +
  • + +
  • + +
    minus
    +
    #icon-minus
    +
  • + +
  • + +
    refresh
    +
    #icon-refresh
    +
  • + +
  • + +
    scanning
    +
    #icon-scanning
    +
  • + +
  • + +
    关闭其他-01
    +
    #icon-close-other
    +
  • + +
  • + +
    close
    +
    #icon-close
    +
  • + +
  • + +
    arrow-down-filling
    +
    #icon-arrow-down
    +
  • + +
  • + +
    中英切换
    +
    #icon-language
    +
  • + +
  • + +
    密码
    +
    #icon-lock
    +
  • + +
  • + +
    customer-fill
    +
    #icon-user
    +
  • + +
+
+

Symbol 引用

+
+ +

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 + 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

+
    +
  • 支持多色图标了,不再受单色限制。
  • +
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • +
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • +
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 symbol 代码:

+
<script src="./iconfont.js"></script>
+
+

第二步:加入通用 CSS 代码(引入一次就行):

+
<style>
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+</style>
+
+

第三步:挑选相应图标并获取类名,应用于页面:

+
<svg class="icon" aria-hidden="true">
+  <use xlink:href="#icon-xxx"></use>
+</svg>
+
+
+
+ +
+
+ + + diff --git a/src/assets/iconfont/iconfont.css b/src/assets/iconfont/iconfont.css new file mode 100644 index 0000000..6ac9814 --- /dev/null +++ b/src/assets/iconfont/iconfont.css @@ -0,0 +1,223 @@ +@font-face { + font-family: "iconfont"; /* Project id 3987011 */ + src: url('iconfont.woff2?t=1693533922330') format('woff2'), + url('iconfont.woff?t=1693533922330') format('woff'), + url('iconfont.ttf?t=1693533922330') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-decline:before { + content: "\eb04"; +} + +.icon-rise:before { + content: "\eb05"; +} + +.icon-visits-chart:before { + content: "\e6a5"; +} + +.icon-user-visits:before { + content: "\e651"; +} + +.icon-turnover:before { + content: "\ec18"; +} + +.icon-num-transactions:before { + content: "\e682"; +} + +.icon-num-download:before { + content: "\e6f8"; +} + +.icon-user-mgt:before { + content: "\e617"; +} + +.icon-home:before { + content: "\e62e"; +} + +.icon-set-edit:before { + content: "\eb61"; +} + +.icon-app-mgt:before { + content: "\eb8f"; +} + +.icon-setting:before { + content: "\e78e"; +} + +.icon-folder-open:before { + content: "\e7d1"; +} + +.icon-image:before { + content: "\e7de"; +} + +.icon-cloud-download:before { + content: "\ec1d"; +} + +.icon-cloud-upload:before { + content: "\ec1e"; +} + +.icon-role-mgt:before { + content: "\e645"; +} + +.icon-key:before { + content: "\e775"; +} + +.icon-safe-code:before { + content: "\e627"; +} + +.icon-safe:before { + content: "\ec4d"; +} + +.icon-other:before { + content: "\e60b"; +} + +.icon-page-right:before { + content: "\e65b"; +} + +.icon-search:before { + content: "\e63c"; +} + +.icon-setup:before { + content: "\eb8d"; +} + +.icon-reset:before { + content: "\e614"; +} + +.icon-camera:before { + content: "\e666"; +} + +.icon-qrcode:before { + content: "\e6b0"; +} + +.icon-delete:before { + content: "\e6b4"; +} + +.icon-address:before { + content: "\e652"; +} + +.icon-clock:before { + content: "\e890"; +} + +.icon-location:before { + content: "\e63d"; +} + +.icon-scan:before { + content: "\e68b"; +} + +.icon-edit:before { + content: "\e6aa"; +} + +.icon-page-left:before { + content: "\e659"; +} + +.icon-font-size:before { + content: "\e689"; +} + +.icon-filter:before { + content: "\e648"; +} + +.icon-ashbin:before { + content: "\e665"; +} + +.icon-code:before { + content: "\e667"; +} + +.icon-copy:before { + content: "\e66a"; +} + +.icon-link:before { + content: "\e676"; +} + +.icon-layers:before { + content: "\e675"; +} + +.icon-fullscreen:before { + content: "\e677"; +} + +.icon-menu:before { + content: "\e678"; +} + +.icon-minus:before { + content: "\e67a"; +} + +.icon-refresh:before { + content: "\e67b"; +} + +.icon-scanning:before { + content: "\e67d"; +} + +.icon-close-other:before { + content: "\e624"; +} + +.icon-close:before { + content: "\e668"; +} + +.icon-arrow-down:before { + content: "\e688"; +} + +.icon-language:before { + content: "\e739"; +} + +.icon-lock:before { + content: "\e62a"; +} + +.icon-user:before { + content: "\e828"; +} + diff --git a/src/assets/iconfont/iconfont.js b/src/assets/iconfont/iconfont.js new file mode 100644 index 0000000..bf2a9cf --- /dev/null +++ b/src/assets/iconfont/iconfont.js @@ -0,0 +1 @@ +window._iconfont_svg_string_3987011='',function(a){var l=(l=document.getElementsByTagName("script"))[l.length-1],c=l.getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var h,i,t,o,s,e=function(l,c){c.parentNode.insertBefore(l,c)};if(c&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_3987011,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?e(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(i=function(){document.removeEventListener("DOMContentLoaded",i,!1),h()},document.addEventListener("DOMContentLoaded",i,!1)):document.attachEvent&&(t=h,o=a.document,s=!1,m(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,d())})}function d(){s||(s=!0,t())}function m(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(m,50)}d()}}(window); \ No newline at end of file diff --git a/src/assets/iconfont/iconfont.json b/src/assets/iconfont/iconfont.json new file mode 100644 index 0000000..0b680db --- /dev/null +++ b/src/assets/iconfont/iconfont.json @@ -0,0 +1,373 @@ +{ + "id": "3987011", + "name": "vue3后台管理系统", + "font_family": "iconfont", + "css_prefix_text": "icon-", + "description": "", + "glyphs": [ + { + "icon_id": "5387575", + "name": "箭头_切换向下", + "font_class": "decline", + "unicode": "eb04", + "unicode_decimal": 60164 + }, + { + "icon_id": "5387578", + "name": "箭头_切换向上", + "font_class": "rise", + "unicode": "eb05", + "unicode_decimal": 60165 + }, + { + "icon_id": "6033101", + "name": "访问量", + "font_class": "visits-chart", + "unicode": "e6a5", + "unicode_decimal": 59045 + }, + { + "icon_id": "7956168", + "name": "总访问量", + "font_class": "user-visits", + "unicode": "e651", + "unicode_decimal": 58961 + }, + { + "icon_id": "26442901", + "name": "订单成交额", + "font_class": "turnover", + "unicode": "ec18", + "unicode_decimal": 60440 + }, + { + "icon_id": "26908793", + "name": "成交数据-01", + "font_class": "num-transactions", + "unicode": "e682", + "unicode_decimal": 59010 + }, + { + "icon_id": "31500093", + "name": "下载数量", + "font_class": "num-download", + "unicode": "e6f8", + "unicode_decimal": 59128 + }, + { + "icon_id": "1348257", + "name": "用户管理", + "font_class": "user-mgt", + "unicode": "e617", + "unicode_decimal": 58903 + }, + { + "icon_id": "2675009", + "name": "首页", + "font_class": "home", + "unicode": "e62e", + "unicode_decimal": 58926 + }, + { + "icon_id": "3868263", + "name": "编辑", + "font_class": "set-edit", + "unicode": "eb61", + "unicode_decimal": 60257 + }, + { + "icon_id": "4347582", + "name": "icon_应用管理", + "font_class": "app-mgt", + "unicode": "eb8f", + "unicode_decimal": 60303 + }, + { + "icon_id": "4765891", + "name": "setting", + "font_class": "setting", + "unicode": "e78e", + "unicode_decimal": 59278 + }, + { + "icon_id": "4766847", + "name": "folder-open", + "font_class": "folder-open", + "unicode": "e7d1", + "unicode_decimal": 59345 + }, + { + "icon_id": "4766917", + "name": "image", + "font_class": "image", + "unicode": "e7de", + "unicode_decimal": 59358 + }, + { + "icon_id": "4932635", + "name": "云端下载", + "font_class": "cloud-download", + "unicode": "ec1d", + "unicode_decimal": 60445 + }, + { + "icon_id": "4932636", + "name": "云端上传", + "font_class": "cloud-upload", + "unicode": "ec1e", + "unicode_decimal": 60446 + }, + { + "icon_id": "6447176", + "name": "角色管理", + "font_class": "role-mgt", + "unicode": "e645", + "unicode_decimal": 58949 + }, + { + "icon_id": "6150969", + "name": "key", + "font_class": "key", + "unicode": "e775", + "unicode_decimal": 59253 + }, + { + "icon_id": "1599882", + "name": "网商银行无线端_安全", + "font_class": "safe-code", + "unicode": "e627", + "unicode_decimal": 58919 + }, + { + "icon_id": "5961299", + "name": "安全", + "font_class": "safe", + "unicode": "ec4d", + "unicode_decimal": 60493 + }, + { + "icon_id": "13106274", + "name": "其他", + "font_class": "other", + "unicode": "e60b", + "unicode_decimal": 58891 + }, + { + "icon_id": "8777535", + "name": "page_last", + "font_class": "page-right", + "unicode": "e65b", + "unicode_decimal": 58971 + }, + { + "icon_id": "8776842", + "name": "search", + "font_class": "search", + "unicode": "e63c", + "unicode_decimal": 58940 + }, + { + "icon_id": "4347549", + "name": "icon_设置", + "font_class": "setup", + "unicode": "eb8d", + "unicode_decimal": 60301 + }, + { + "icon_id": "27253675", + "name": "重置", + "font_class": "reset", + "unicode": "e614", + "unicode_decimal": 58900 + }, + { + "icon_id": "30414", + "name": "camera", + "font_class": "camera", + "unicode": "e666", + "unicode_decimal": 58982 + }, + { + "icon_id": "32472", + "name": "qr_code", + "font_class": "qrcode", + "unicode": "e6b0", + "unicode_decimal": 59056 + }, + { + "icon_id": "32477", + "name": "delete", + "font_class": "delete", + "unicode": "e6b4", + "unicode_decimal": 59060 + }, + { + "icon_id": "658000", + "name": "地址", + "font_class": "address", + "unicode": "e652", + "unicode_decimal": 58962 + }, + { + "icon_id": "2076264", + "name": "闹钟", + "font_class": "clock", + "unicode": "e890", + "unicode_decimal": 59536 + }, + { + "icon_id": "9690751", + "name": "地址", + "font_class": "location", + "unicode": "e63d", + "unicode_decimal": 58941 + }, + { + "icon_id": "16617390", + "name": "scan", + "font_class": "scan", + "unicode": "e68b", + "unicode_decimal": 59019 + }, + { + "icon_id": "16618154", + "name": "edit", + "font_class": "edit", + "unicode": "e6aa", + "unicode_decimal": 59050 + }, + { + "icon_id": "8777532", + "name": "page_first", + "font_class": "page-left", + "unicode": "e659", + "unicode_decimal": 58969 + }, + { + "icon_id": "24502124", + "name": "字体大小", + "font_class": "font-size", + "unicode": "e689", + "unicode_decimal": 59017 + }, + { + "icon_id": "8777080", + "name": "filter", + "font_class": "filter", + "unicode": "e648", + "unicode_decimal": 58952 + }, + { + "icon_id": "15838430", + "name": "ashbin", + "font_class": "ashbin", + "unicode": "e665", + "unicode_decimal": 58981 + }, + { + "icon_id": "15838443", + "name": "code", + "font_class": "code", + "unicode": "e667", + "unicode_decimal": 58983 + }, + { + "icon_id": "15838456", + "name": "copy", + "font_class": "copy", + "unicode": "e66a", + "unicode_decimal": 58986 + }, + { + "icon_id": "15838493", + "name": "link", + "font_class": "link", + "unicode": "e676", + "unicode_decimal": 58998 + }, + { + "icon_id": "15838496", + "name": "layers", + "font_class": "layers", + "unicode": "e675", + "unicode_decimal": 58997 + }, + { + "icon_id": "15838498", + "name": "fullscreen-expand", + "font_class": "fullscreen", + "unicode": "e677", + "unicode_decimal": 58999 + }, + { + "icon_id": "15838501", + "name": "menu", + "font_class": "menu", + "unicode": "e678", + "unicode_decimal": 59000 + }, + { + "icon_id": "15838514", + "name": "minus", + "font_class": "minus", + "unicode": "e67a", + "unicode_decimal": 59002 + }, + { + "icon_id": "15838519", + "name": "refresh", + "font_class": "refresh", + "unicode": "e67b", + "unicode_decimal": 59003 + }, + { + "icon_id": "15838527", + "name": "scanning", + "font_class": "scanning", + "unicode": "e67d", + "unicode_decimal": 59005 + }, + { + "icon_id": "33077825", + "name": "关闭其他-01", + "font_class": "close-other", + "unicode": "e624", + "unicode_decimal": 58916 + }, + { + "icon_id": "15838444", + "name": "close", + "font_class": "close", + "unicode": "e668", + "unicode_decimal": 58984 + }, + { + "icon_id": "15838582", + "name": "arrow-down-filling", + "font_class": "arrow-down", + "unicode": "e688", + "unicode_decimal": 59016 + }, + { + "icon_id": "31061118", + "name": "中英切换", + "font_class": "language", + "unicode": "e739", + "unicode_decimal": 59193 + }, + { + "icon_id": "611345", + "name": "密码", + "font_class": "lock", + "unicode": "e62a", + "unicode_decimal": 58922 + }, + { + "icon_id": "34452814", + "name": "customer-fill", + "font_class": "user", + "unicode": "e828", + "unicode_decimal": 59432 + } + ] +} diff --git a/src/assets/iconfont/iconfont.ttf b/src/assets/iconfont/iconfont.ttf new file mode 100644 index 0000000..32ddd6b Binary files /dev/null and b/src/assets/iconfont/iconfont.ttf differ diff --git a/src/assets/iconfont/iconfont.woff b/src/assets/iconfont/iconfont.woff new file mode 100644 index 0000000..a51e51c Binary files /dev/null and b/src/assets/iconfont/iconfont.woff differ diff --git a/src/assets/iconfont/iconfont.woff2 b/src/assets/iconfont/iconfont.woff2 new file mode 100644 index 0000000..f052773 Binary files /dev/null and b/src/assets/iconfont/iconfont.woff2 differ diff --git a/src/assets/icons/advert.svg b/src/assets/icons/advert.svg new file mode 100644 index 0000000..5adcf43 --- /dev/null +++ b/src/assets/icons/advert.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/api.svg b/src/assets/icons/api.svg new file mode 100644 index 0000000..2753743 --- /dev/null +++ b/src/assets/icons/api.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/brand.svg b/src/assets/icons/brand.svg new file mode 100644 index 0000000..e4b7cee --- /dev/null +++ b/src/assets/icons/brand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/bug.svg b/src/assets/icons/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/src/assets/icons/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/cascader.svg b/src/assets/icons/cascader.svg new file mode 100644 index 0000000..e256024 --- /dev/null +++ b/src/assets/icons/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/chart.svg b/src/assets/icons/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/src/assets/icons/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/client.svg b/src/assets/icons/client.svg new file mode 100644 index 0000000..ad4bc15 --- /dev/null +++ b/src/assets/icons/client.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close.svg b/src/assets/icons/close.svg new file mode 100644 index 0000000..5b5057f --- /dev/null +++ b/src/assets/icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_all.svg b/src/assets/icons/close_all.svg new file mode 100644 index 0000000..aa13cd7 --- /dev/null +++ b/src/assets/icons/close_all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_left.svg b/src/assets/icons/close_left.svg new file mode 100644 index 0000000..e5708ea --- /dev/null +++ b/src/assets/icons/close_left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_other.svg b/src/assets/icons/close_other.svg new file mode 100644 index 0000000..212e6c2 --- /dev/null +++ b/src/assets/icons/close_other.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/close_right.svg b/src/assets/icons/close_right.svg new file mode 100644 index 0000000..14d3cf3 --- /dev/null +++ b/src/assets/icons/close_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/coupon.svg b/src/assets/icons/coupon.svg new file mode 100644 index 0000000..2f952b2 --- /dev/null +++ b/src/assets/icons/coupon.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/dashboard.svg b/src/assets/icons/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/src/assets/icons/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/dict.svg b/src/assets/icons/dict.svg new file mode 100644 index 0000000..22a8278 --- /dev/null +++ b/src/assets/icons/dict.svg @@ -0,0 +1,18 @@ + + + + + + + diff --git a/src/assets/icons/dict_item.svg b/src/assets/icons/dict_item.svg new file mode 100644 index 0000000..903109a --- /dev/null +++ b/src/assets/icons/dict_item.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/src/assets/icons/document.svg b/src/assets/icons/document.svg new file mode 100644 index 0000000..918ae33 --- /dev/null +++ b/src/assets/icons/document.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/download.svg b/src/assets/icons/download.svg new file mode 100644 index 0000000..61ec1f9 --- /dev/null +++ b/src/assets/icons/download.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/drag.svg b/src/assets/icons/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/src/assets/icons/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/edit.svg b/src/assets/icons/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/src/assets/icons/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/exit-fullscreen.svg b/src/assets/icons/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/src/assets/icons/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/eye-open.svg b/src/assets/icons/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/src/assets/icons/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/eye.svg b/src/assets/icons/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/src/assets/icons/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/fullscreen.svg b/src/assets/icons/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/src/assets/icons/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/github.svg b/src/assets/icons/github.svg new file mode 100644 index 0000000..db0a0d4 --- /dev/null +++ b/src/assets/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/goods-list.svg b/src/assets/icons/goods-list.svg new file mode 100644 index 0000000..fcb971e --- /dev/null +++ b/src/assets/icons/goods-list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/goods.svg b/src/assets/icons/goods.svg new file mode 100644 index 0000000..60c1c73 --- /dev/null +++ b/src/assets/icons/goods.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/guide.svg b/src/assets/icons/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/src/assets/icons/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/homepage.svg b/src/assets/icons/homepage.svg new file mode 100644 index 0000000..48f4e24 --- /dev/null +++ b/src/assets/icons/homepage.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/lab.svg b/src/assets/icons/lab.svg new file mode 100644 index 0000000..d4d60aa --- /dev/null +++ b/src/assets/icons/lab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/language.svg b/src/assets/icons/language.svg new file mode 100644 index 0000000..d2dd693 --- /dev/null +++ b/src/assets/icons/language.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/link.svg b/src/assets/icons/link.svg new file mode 100644 index 0000000..9748d53 --- /dev/null +++ b/src/assets/icons/link.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/menu.svg b/src/assets/icons/menu.svg new file mode 100644 index 0000000..92c364c --- /dev/null +++ b/src/assets/icons/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/message.svg b/src/assets/icons/message.svg new file mode 100644 index 0000000..ea1ddef --- /dev/null +++ b/src/assets/icons/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/money.svg b/src/assets/icons/money.svg new file mode 100644 index 0000000..60f7acf --- /dev/null +++ b/src/assets/icons/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/monitor.svg b/src/assets/icons/monitor.svg new file mode 100644 index 0000000..bc308cb --- /dev/null +++ b/src/assets/icons/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/src/assets/icons/multi_level.svg b/src/assets/icons/multi_level.svg new file mode 100644 index 0000000..a1a2792 --- /dev/null +++ b/src/assets/icons/multi_level.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/nested.svg b/src/assets/icons/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/src/assets/icons/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/number.svg b/src/assets/icons/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/src/assets/icons/number.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/order.svg b/src/assets/icons/order.svg new file mode 100644 index 0000000..8f2107e --- /dev/null +++ b/src/assets/icons/order.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/password.svg b/src/assets/icons/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/src/assets/icons/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/peoples.svg b/src/assets/icons/peoples.svg new file mode 100644 index 0000000..383b82d --- /dev/null +++ b/src/assets/icons/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/perm.svg b/src/assets/icons/perm.svg new file mode 100644 index 0000000..b38d065 --- /dev/null +++ b/src/assets/icons/perm.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/publish.svg b/src/assets/icons/publish.svg new file mode 100644 index 0000000..e9b489c --- /dev/null +++ b/src/assets/icons/publish.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/rabbitmq.svg b/src/assets/icons/rabbitmq.svg new file mode 100644 index 0000000..65aa198 --- /dev/null +++ b/src/assets/icons/rabbitmq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/rate.svg b/src/assets/icons/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/src/assets/icons/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/redis.svg b/src/assets/icons/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/src/assets/icons/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/refresh.svg b/src/assets/icons/refresh.svg new file mode 100644 index 0000000..1f549f1 --- /dev/null +++ b/src/assets/icons/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/role.svg b/src/assets/icons/role.svg new file mode 100644 index 0000000..c484b13 --- /dev/null +++ b/src/assets/icons/role.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/security.svg b/src/assets/icons/security.svg new file mode 100644 index 0000000..bcd9d2e --- /dev/null +++ b/src/assets/icons/security.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/shopping.svg b/src/assets/icons/shopping.svg new file mode 100644 index 0000000..8d2b4bf --- /dev/null +++ b/src/assets/icons/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/size.svg b/src/assets/icons/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/src/assets/icons/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/skill.svg b/src/assets/icons/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/src/assets/icons/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/system.svg b/src/assets/icons/system.svg new file mode 100644 index 0000000..e3b7e2d --- /dev/null +++ b/src/assets/icons/system.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/theme.svg b/src/assets/icons/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/src/assets/icons/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/tree.svg b/src/assets/icons/tree.svg new file mode 100644 index 0000000..d40a414 --- /dev/null +++ b/src/assets/icons/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/user.svg b/src/assets/icons/user.svg new file mode 100644 index 0000000..e4c7b38 --- /dev/null +++ b/src/assets/icons/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/uv.svg b/src/assets/icons/uv.svg new file mode 100644 index 0000000..ca4c301 --- /dev/null +++ b/src/assets/icons/uv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/valid_code.svg b/src/assets/icons/valid_code.svg new file mode 100644 index 0000000..39bf478 --- /dev/null +++ b/src/assets/icons/valid_code.svg @@ -0,0 +1,9 @@ + + + + diff --git a/src/assets/icons/verify_code.svg b/src/assets/icons/verify_code.svg new file mode 100644 index 0000000..39bf478 --- /dev/null +++ b/src/assets/icons/verify_code.svg @@ -0,0 +1,9 @@ + + + + diff --git a/src/assets/images/avatar.png b/src/assets/images/avatar.png new file mode 100644 index 0000000..b5bce2a Binary files /dev/null and b/src/assets/images/avatar.png differ diff --git a/src/assets/images/device01.gif b/src/assets/images/device01.gif new file mode 100644 index 0000000..beb27d6 Binary files /dev/null and b/src/assets/images/device01.gif differ diff --git a/src/assets/images/device02.jpg b/src/assets/images/device02.jpg new file mode 100644 index 0000000..b8db0be Binary files /dev/null and b/src/assets/images/device02.jpg differ diff --git a/src/assets/images/device03.jpg b/src/assets/images/device03.jpg new file mode 100644 index 0000000..48893ff Binary files /dev/null and b/src/assets/images/device03.jpg differ diff --git a/src/assets/images/login-bg.png b/src/assets/images/login-bg.png new file mode 100644 index 0000000..187fdf2 Binary files /dev/null and b/src/assets/images/login-bg.png differ diff --git a/src/assets/images/screen.jpg b/src/assets/images/screen.jpg new file mode 100644 index 0000000..2e78c89 Binary files /dev/null and b/src/assets/images/screen.jpg differ diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..33039f2 Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..6e6c1c2 --- /dev/null +++ b/src/components/Breadcrumb/index.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue new file mode 100644 index 0000000..8b017e3 --- /dev/null +++ b/src/components/Hamburger/index.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/src/components/LangSelect/index.vue b/src/components/LangSelect/index.vue new file mode 100644 index 0000000..5333998 --- /dev/null +++ b/src/components/LangSelect/index.vue @@ -0,0 +1,42 @@ + + + \ No newline at end of file diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue new file mode 100644 index 0000000..b8b3e0d --- /dev/null +++ b/src/components/Pagination/index.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/src/components/RightPanel/index.vue b/src/components/RightPanel/index.vue new file mode 100644 index 0000000..7bc253e --- /dev/null +++ b/src/components/RightPanel/index.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/src/components/Screenfull/index.vue b/src/components/Screenfull/index.vue new file mode 100644 index 0000000..62dbbef --- /dev/null +++ b/src/components/Screenfull/index.vue @@ -0,0 +1,15 @@ + + + + + diff --git a/src/components/Signature/index.vue b/src/components/Signature/index.vue new file mode 100644 index 0000000..9077fec --- /dev/null +++ b/src/components/Signature/index.vue @@ -0,0 +1,157 @@ + + + diff --git a/src/components/SizeSelect/index.vue b/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..631f702 --- /dev/null +++ b/src/components/SizeSelect/index.vue @@ -0,0 +1,37 @@ + + + + diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..07b65e8 --- /dev/null +++ b/src/components/SvgIcon/index.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/src/components/Table/index.vue b/src/components/Table/index.vue new file mode 100644 index 0000000..7515ed4 --- /dev/null +++ b/src/components/Table/index.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/src/components/Upload/MultiUpload.vue b/src/components/Upload/MultiUpload.vue new file mode 100644 index 0000000..1d7b619 --- /dev/null +++ b/src/components/Upload/MultiUpload.vue @@ -0,0 +1,125 @@ + + + diff --git a/src/components/Upload/SingleUpload.vue b/src/components/Upload/SingleUpload.vue new file mode 100644 index 0000000..189d141 --- /dev/null +++ b/src/components/Upload/SingleUpload.vue @@ -0,0 +1,89 @@ + + + + + + + diff --git a/src/components/Video/index.vue b/src/components/Video/index.vue new file mode 100644 index 0000000..6d3fde0 --- /dev/null +++ b/src/components/Video/index.vue @@ -0,0 +1,155 @@ + + diff --git a/src/components/WangEditor/index.vue b/src/components/WangEditor/index.vue new file mode 100644 index 0000000..71e3b31 --- /dev/null +++ b/src/components/WangEditor/index.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/src/directive/index.ts b/src/directive/index.ts new file mode 100644 index 0000000..960fa44 --- /dev/null +++ b/src/directive/index.ts @@ -0,0 +1,9 @@ +import type { App } from 'vue'; + +import { hasPerm } from './permission'; + +// 全局注册 directive +export function setupDirective(app: App) { + // 使 v-hasPerm 在所有组件中都可用 + app.directive('hasPerm', hasPerm); +} diff --git a/src/directive/permission/index.ts b/src/directive/permission/index.ts new file mode 100644 index 0000000..b8d0dee --- /dev/null +++ b/src/directive/permission/index.ts @@ -0,0 +1,55 @@ +import { useUserStoreHook } from '@/store/modules/user'; +import { Directive, DirectiveBinding } from 'vue'; + +/** + * 按钮权限 + */ +export const hasPerm: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + // 「超级管理员」拥有所有的按钮权限 + const { roles, perms } = useUserStoreHook(); + if (roles.includes('ROOT')) { + return true; + } + // 「其他角色」按钮权限校验 + const { value } = binding; + if (value) { + const requiredPerms = value; // DOM绑定需要的按钮权限标识 + + const hasPerm = perms?.some(perm => { + return requiredPerms.includes(perm); + }); + + if (!hasPerm) { + el.parentNode && el.parentNode.removeChild(el); + } + } else { + throw new Error( + "need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\"" + ); + } + } +}; + +/** + * 角色权限 + */ +export const hasRole: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const { value } = binding; + + if (value) { + const requiredRoles = value; // DOM绑定需要的角色编码 + const { roles } = useUserStoreHook(); + const hasRole = roles.some(perm => { + return requiredRoles.includes(perm); + }); + + if (!hasRole) { + el.parentNode && el.parentNode.removeChild(el); + } + } else { + throw new Error("need roles! Like v-has-role=\"['admin','test']\""); + } + } +}; diff --git a/src/enums/MenuTypeEnum.ts b/src/enums/MenuTypeEnum.ts new file mode 100644 index 0000000..96c612e --- /dev/null +++ b/src/enums/MenuTypeEnum.ts @@ -0,0 +1,19 @@ +export enum MenuTypeEnum { + /** + * 目录 + */ + CATALOG = 'CATALOG', + /** + * 菜单 + */ + MENU = 'MENU', + + /** + * 按钮 + */ + BUTTON = 'BUTTON', + /** + * 外链 + */ + EXTLINK = 'EXTLINK' +} diff --git a/src/lang/en.ts b/src/lang/en.ts new file mode 100644 index 0000000..00f7714 --- /dev/null +++ b/src/lang/en.ts @@ -0,0 +1,24 @@ +export default { + // 路由国际化 + route: { + dashboard: 'Dashboard', + system:'System Management', + user:'User', + role:'Role', + }, + // 登录页面国际化 + login: { + title: 'Luenmei Operational Cockpit MS', + username: 'Username', + password: 'Password', + login: 'Login', + verifyCode: 'Verify Code', + copyright: 'All Rights Reserved.', + }, + // 导航栏国际化 + navbar: { + dashboard: 'Dashboard', + logout: 'Logout', + document: 'Document', + } +}; diff --git a/src/lang/index.ts b/src/lang/index.ts new file mode 100644 index 0000000..8ed51e9 --- /dev/null +++ b/src/lang/index.ts @@ -0,0 +1,45 @@ +// 自定义国际化配置 +import { createI18n } from 'vue-i18n'; + +// 本地语言包 +import enLocale from './en'; +import zhCnLocale from './zh-cn'; + +const messages = { + 'zh-cn': { + ...zhCnLocale + }, + en: { + ...enLocale + } +}; + +/** + * 获取当前系统使用语言字符串 + * + * @returns zh-cn|en ... + */ +export const getLanguage = () => { + // 本地缓存获取 + let language = localStorage.getItem('language'); + if (language) { + return language; + } + // 浏览器使用语言 + language = navigator.language.toLowerCase(); + const locales = Object.keys(messages); + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale; + } + } + return 'zh-cn'; +}; + +const i18n = createI18n({ + legacy: false, + locale: getLanguage(), + messages: messages +}); + +export default i18n; diff --git a/src/lang/zh-cn.ts b/src/lang/zh-cn.ts new file mode 100644 index 0000000..7b93420 --- /dev/null +++ b/src/lang/zh-cn.ts @@ -0,0 +1,24 @@ +export default { + // 路由国际化 + route: { + dashboard: '首页', + system:'系统管理', + user:'用户管理', + roel:'角色管理', + }, + // 登录页面国际化 + login: { + title: '联美运营驾驶舱管理系统', + username: '用户名', + password: '密码', + login: '登 录', + verifyCode: '验证码', + copyright: '版权所有', + }, + // 导航栏国际化 + navbar: { + dashboard: '首页', + logout: '注销', + document: '项目文档', + } +}; diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue new file mode 100644 index 0000000..d94a68a --- /dev/null +++ b/src/layout/components/AppMain.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue new file mode 100644 index 0000000..566c219 --- /dev/null +++ b/src/layout/components/Navbar.vue @@ -0,0 +1,138 @@ + + + + + diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..8840a34 --- /dev/null +++ b/src/layout/components/Settings/index.vue @@ -0,0 +1,208 @@ + + + + + diff --git a/src/layout/components/Sidebar/Link.vue b/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..8aa1fa6 --- /dev/null +++ b/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..d9c3255 --- /dev/null +++ b/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/src/layout/components/Sidebar/SidebarItem.vue b/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..65f3d0d --- /dev/null +++ b/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,119 @@ + + + + diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..45af1c2 --- /dev/null +++ b/src/layout/components/Sidebar/index.vue @@ -0,0 +1,29 @@ + + + diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..0fa63f4 --- /dev/null +++ b/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..a6e5b21 --- /dev/null +++ b/src/layout/components/TagsView/index.vue @@ -0,0 +1,361 @@ + + + + + diff --git a/src/layout/components/index.ts b/src/layout/components/index.ts new file mode 100644 index 0000000..4dca96e --- /dev/null +++ b/src/layout/components/index.ts @@ -0,0 +1,4 @@ +export { default as Navbar } from './Navbar.vue'; +export { default as AppMain } from './AppMain.vue'; +export { default as Settings } from './Settings/index.vue'; +export { default as TagsView } from './TagsView/index.vue'; diff --git a/src/layout/index.vue b/src/layout/index.vue new file mode 100644 index 0000000..1ac144c --- /dev/null +++ b/src/layout/index.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..01816bf --- /dev/null +++ b/src/main.ts @@ -0,0 +1,32 @@ +import { createApp } from 'vue'; +import App from './App.vue'; +import router from '@/router'; +import { setupStore } from '@/store'; +import { setupDirective } from '@/directive'; +import VXETable from 'vxe-table'; + +import './utils/rem'; //引入px和rem适配方案 +import '@/permission'; + +// 国际化 +import i18n from '@/lang/index'; + +//本地模拟数据 +import '@/mock/mockServer' + +// 本地SVG图标 +import 'virtual:svg-icons-register'; + +// 样式 +import '@/styles/index.scss'; +import 'element-plus/theme-chalk/dark/css-vars.css'; +import '@/assets/iconfont/iconfont.css' +import 'vxe-table/lib/style.css'; + +const app = createApp(App); +// 全局注册 自定义指令(directive) +setupDirective(app); +// 全局注册 状态管理(store) +setupStore(app); + +app.use(router).use(i18n).use(VXETable).mount('#app'); diff --git a/src/mock/brands.json b/src/mock/brands.json new file mode 100644 index 0000000..280728c --- /dev/null +++ b/src/mock/brands.json @@ -0,0 +1,18 @@ +{ + "code": "00000", + "data": [ + { + "value": 1, + "label": "品牌A" + }, + { + "value": 2, + "label": "品牌B" + }, + { + "value": 3, + "label": "品牌C" + } + ], + "msg": "seccess" +} diff --git a/src/mock/brandsData.json b/src/mock/brandsData.json new file mode 100644 index 0000000..9326d8d --- /dev/null +++ b/src/mock/brandsData.json @@ -0,0 +1,21 @@ +{ + "code": "00000", + "data": { + "list": [ + { + "id": 1, + "name": "品牌A" + }, + { + "id": 2, + "name": "品牌B" + }, + { + "id": 3, + "name": "品牌C" + } + ], + "total": 3 + }, + "msg": "seccess" +} diff --git a/src/mock/captcha.json b/src/mock/captcha.json new file mode 100644 index 0000000..ca1f6e0 --- /dev/null +++ b/src/mock/captcha.json @@ -0,0 +1,8 @@ +{ + "code": "00000", + "data": { + "verifyCodeKey": "f156bf5930b444b18377ef79daff7bdf", + "verifyCodeBase64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAAkCAIAAADNSmkJAAAFhUlEQVR4Xu2Z/U9TVxjH/Z8I2UzGgiHLBoIxSxYmMftpYyAGFETkZWY6mE4yzZwLg6pYSgGRxigzCh1jBR2OURGwNLwUJNBRXkuBFtqVnn1vL5T26e1tu8ilLveTk+b2+zynKd977nOeUw4wGUk4QAWZvUE2WiJkoyVCNloiIjJ6rb9/orR0MCWlLy4Or7iGQpNkRAljtMftnrp8eSgtbUGjcZrNUPCKayjQEaUTZEIQxmi4OXrypNtuJzoU6IgSXSYUYkajPmDlBrvMAx1RuYZEiJjREyUl8xoNVf1AFDlUlRFCzOiB5GTn7CxV/UAUOVSVEULMaP3Bgx6Xi6p+IIocqsoIIWb0yJEj7vp6VlHBTp9mJ06wsjKmVrOlJV+C9Cva7nL91Nv78d277yoU79fWfvHwYbvJRJOkwm40Tl26ZEhP709IeJGQMJKVtarX06QdxIxmmZkCIy+PTU3xcYlrtG1z85Pm5rjqajJgPU2VBJwqyNDHx9t6emieFzGjt4qL544d2xocZMvLDDUEffS1a5zXlZVsP7qOymfPYOthtbpzchKmY/w2OZlcXx9fUzOyuEiz957hjIz55mYcLFBCHSYTVjS8Hj5+nOZ5ETOaBffRcBxG5+TsSx+dolbD6D7vuclHr9kM8Yfnz/3FfcE1P88t6hCbVhij6cnQZoPRW7m5UZwMUeVxb86dY6uruyKui4o4HdGIeUehgKf2wP0ZbyF+/uCBv7gvYFFzP1GkptKAlzBG8/C/dWBvXElPhztL+flRVAzcjCtXtgsOf2Pwimso0CO5VTt8qFKFWtEfqVT+ovRsmExDR4/C6Nk7d2jMS0RG7+6EZ8+yjg4aDQtZv/wah4LnIxq+0en4Gv37To1GsT7srSfv3bpFs0U5kNkXyaDTQrBuMPQnJsLlsbw8z9YWDXuJ0uisLHb1KvsPHRUalZwc7hNu3OCrPHv9muYwZjFZtDVaVaFKkaVo/rq5raptrHfMs+Xhowt2O1+m/UdFVxdesR8GflIYgj0VHHSaEJtm88ukJLhsKiz0OJ00vENkRgPcKHTQnZ1cTw27h4dpQljQhPlumFAPNPTrkLpIbew2rlvX8dZhc5j6TK3ft2rKNWuLa3zOksPx3dOnqWo16nVaQ0PdwIBpeRlGJ9bW+n+UlMBfuDyBQ0aItcwTsdE+Xr3inMIpJlo2NlhBATcXrXfQd7KMW+Cy3SrwA9ZQx1BDSYPTLrxYHo2OwuhP792jAanoP3QIRrsWFmggkOiNxtPBP/vRgqfbt6JbWkhQ+7PW2GUkoo9udXdPi8BDAArb22H0t93dNCBKcJUQHHSaEPxRhXm261soojfaYOCcKi6mujg6HTcrN5crGtnZ3HVg36I6o+IrhiDWv62NJY1UxWKfm0N1htEDFguNiRLsqeCg04TYNjocokajPnR1sZkZ5nCwzU2GP+bJE3bqFGeT6M+nlOnp7Z2QPytrtdw1jvJzc74URbbC/U/IVs/tct/MuYmLL1tb/5ieRr+x5nSiaCQplXAZi5pOiD1EjfY96WSg8Qi9vVJwh86f52b5t7pVVZxy8SJ3svdSd6bOOmvdTQhkZW6lobgBF6TlwPjs/v21yL/MHvAmVjRWIoppeTm3ivG8Fxay69cZDrvh6lEAt29znl644POUAxsjtmnoSiUvPP7x8fhf47sJgegf6bvruSr8YnY2v63tg7o6NM4ZGo1qYMAVtK9KzJswWkLG/hxDJ0dVL6sLq8oCpW0+utNNrBErRuNUgn4ZnRzRbQu2pq+aDDoD0d86YsVogFMJCjFKxNIM978FlGz9L3plvnJYF/3hKPaIIaMBTiXol5vKmqozqxtLG2H6214xfMSW0f9jZKMlQjZaImSjJUI2WiL+BUA0Zy/Z1socAAAAAElFTkSuQmCC" + }, + "msg": "seccess" +} \ No newline at end of file diff --git a/src/mock/categoryData.json b/src/mock/categoryData.json new file mode 100644 index 0000000..faec521 --- /dev/null +++ b/src/mock/categoryData.json @@ -0,0 +1,34 @@ +{ + "code": "00000", + "data": [ + { + "value": 1, + "label": "类别A", + "children": [ + { + "value": 11, + "label": "类别A-A" + }, + { + "value": 12, + "label": "类别A-B" + } + ] + }, + { + "value": 2, + "label": "类别B", + "children": [ + { + "value": 21, + "label": "类别B-A" + }, + { + "value": 22, + "label": "类别B-B" + } + ] + } + ], + "msg": "seccess" +} diff --git a/src/mock/dept.json b/src/mock/dept.json new file mode 100644 index 0000000..a7199c5 --- /dev/null +++ b/src/mock/dept.json @@ -0,0 +1,20 @@ +{ + "code": "00000", + "data": [ + { + "value": 1, + "label": "奇思喵想", + "children": [ + { + "value": 2, + "label": "研发部门" + }, + { + "value": 3, + "label": "产品部门" + } + ] + } + ], + "msg": "seccess" +} diff --git a/src/mock/files.json b/src/mock/files.json new file mode 100644 index 0000000..7b2bc0b --- /dev/null +++ b/src/mock/files.json @@ -0,0 +1,8 @@ +{ + "code": "00000", + "data": { + "name": "2023/06/15/5c935887feb141a48630610cf5a4981d.png", + "url": "../../src/assets/images/daniel.jpg" + }, + "msg": "seccess" +} \ No newline at end of file diff --git a/src/mock/login.json b/src/mock/login.json new file mode 100644 index 0000000..d0c630a --- /dev/null +++ b/src/mock/login.json @@ -0,0 +1,10 @@ +{ + "code": "00000", + "data": { + "accessToken": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImp0aSI6IjU1MDY5ZjJkZWVjZjQyY2FhNjNmOWE3ODg4NjU4N2U1IiwidXNlcklkIjoyLCJ1c2VybmFtZSI6ImFkbWluIiwiZGVwdElkIjoyLCJkYXRhU2NvcGUiOjEsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwiZXhwIjoxNjgzNzI1NTg4fQ.T5g6G2wykMOyk5rUXMtMAueCeWtb0x7W_4gf9vZrkS4", + "tokenType": "Bearer", + "refreshToken": null, + "expires": null + }, + "msg": "seccess" +} \ No newline at end of file diff --git a/src/mock/logout.json b/src/mock/logout.json new file mode 100644 index 0000000..2bc2901 --- /dev/null +++ b/src/mock/logout.json @@ -0,0 +1,5 @@ +{ + "code": "00000", + "data": "注销成功", + "msg": "seccess" +} \ No newline at end of file diff --git a/src/mock/menuIds.json b/src/mock/menuIds.json new file mode 100644 index 0000000..5176405 --- /dev/null +++ b/src/mock/menuIds.json @@ -0,0 +1,8 @@ +{ + "code": "00000", + "data": [ + 1, 2, 3, 4, 5, 6, 37, 20, 21, 22, 23, 24, 32, 33, 39, 26, 30, 31, 36, 38, 39, 40, 41, 1, 2, 3, 4, 5, 6, 20, 21, 22, + 23, 24, 26, 30, 31, 32, 33, 36, 37, 38, 39, 40, 41, 70, 71, 72, 73, 74, 75, 76, 77, 78, 10,11 + ], + "msg": "seccess" +} diff --git a/src/mock/menusData.json b/src/mock/menusData.json new file mode 100644 index 0000000..7f82b2d --- /dev/null +++ b/src/mock/menusData.json @@ -0,0 +1,98 @@ +{ + "code": "00000", + "data": [ + { + "value": 1, + "label": "系统管理", + "children": [ + { + "value": 2, + "label": "用户管理", + "children": [ + { + "value": 31, + "label": "用户新增" + }, + { + "value": 32, + "label": "用户编辑" + }, + { + "value": 33, + "label": "用户删除" + }, + { + "value": 88, + "label": "重置密码" + } + ] + }, + { + "value": 3, + "label": "角色管理", + "children": [ + { + "value": 70, + "label": "角色新增" + }, + { + "value": 71, + "label": "角色编辑" + }, + { + "value": 72, + "label": "角色删除" + } + ] + }, + { + "value": 4, + "label": "菜单管理", + "children": [ + { + "value": 73, + "label": "菜单新增" + }, + { + "value": 75, + "label": "菜单删除" + }, + { + "value": 74, + "label": "菜单编辑" + } + ] + }, + { + "value": 5, + "label": "部门管理", + "children": [ + { + "value": 76, + "label": "部门新增" + }, + { + "value": 77, + "label": "部门编辑" + }, + { + "value": 78, + "label": "部门删除" + } + ] + } + ] + }, + { + "value": 10, + "label": "DEMO", + "children": [ + { + "value": 11, + "label": "DEMO" + } + ] + } + ], + "msg": "seccess" +} diff --git a/src/mock/mockServer.ts b/src/mock/mockServer.ts new file mode 100644 index 0000000..d55d576 --- /dev/null +++ b/src/mock/mockServer.ts @@ -0,0 +1,57 @@ +/** + * Author: Fu Guobin + * Date: 2023/05/10 + * Last Modified by: Fu Guobin + * Last Modified time: 2023/05/12 + * Copyright:Daniel(Fu Guobin) + * Description:mock配置 + */ + +// 先引入mockjs模块 +import Mock from 'mockjs'; + +// 引入Json数据格式 +import login from './login.json'; +import logout from './logout.json'; +import captcha from './captcha.json'; +import user from './user.json'; +import routes from './routes.json'; + +import dept from './dept.json'; +import userData from './userData.json'; +import roles from './roles.json'; +import userEdit from './userEdit.json'; + +import rolesData from './rolesData.json'; +import roleEdit from './roleEdit.json'; +import menusData from './menusData.json'; +import menuIds from './menuIds.json'; + +import files from './files.json'; + +import proData from './proData.json'; +import proEdit from './proEdit.json'; +import brands from './brands.json'; + +// mock数据:第一个参数请求地址,第二个参数请求数据 +Mock.mock('/mock/api/v1/auth/login', login); //登录 +Mock.mock('/mock/api/v1/auth/logout', logout); //注销 +Mock.mock('/mock/api/v1/auth/captcha', captcha); //验证码 +Mock.mock('/mock/api/v1/users/me', user); //登录权限 +Mock.mock('/mock/api/v1/menus/routes', routes); //菜单路由 + +Mock.mock('/mock/api/v1/dept/options', dept); //部门 +Mock.mock('/mock/api/v1/users/page', userData); //部门人员列表 +Mock.mock('/mock/api/v1/roles/options', roles); //用户人员列表 +Mock.mock('/mock/api/v1/users/form', userEdit); //编辑用户人员列表 + +Mock.mock('/mock/api/v1/roles/page', rolesData); //角色人员列表 +Mock.mock('/mock/api/v1/roles/form', roleEdit); //编辑角色人员列表 +Mock.mock('/mock/api/v1/menus/options', menusData); //获取角色权限菜单 +Mock.mock('/mock/api/v1/roles/menuIds', menuIds); //回显角色权限菜单 + +Mock.mock('/mock/api/v1/files', files); //单图上传 + +Mock.mock('/mock/api/v1/pro/page', proData); //商品列表 +Mock.mock('/mock/api/v1/pro/form', proEdit); //编辑商品列表 +Mock.mock('/mock/api/v1/brands/options', brands); //品牌列表 diff --git a/src/mock/proData.json b/src/mock/proData.json new file mode 100644 index 0000000..53f3f50 --- /dev/null +++ b/src/mock/proData.json @@ -0,0 +1,45 @@ +{ + "code": "00000", + "data": { + "list": [ + { + "id": 1, + "name": "A商品", + "brands":"品牌A", + "price":"200", + "picture":"../../src/assets/images/daniel.jpg", + "category":"类别A", + "state": 1, + "sort": 1, + "desc": "这是一段简短的商品介绍,用来介绍商品", + "createTime": "2023-03-25 12:39:55" + }, + { + "id": 2, + "name": "B商品", + "brands":"品牌B", + "price":"500", + "picture":"../../src/assets/images/daniel.jpg", + "category":"类别B", + "state": 0, + "sort": 2, + "desc": "这是一段简短的商品介绍,用来介绍商品", + "createTime": "2023-03-25 12:39:55" + }, + { + "id": 3, + "name": "C商品", + "brands":"品牌C", + "price":"800", + "picture":"../../src/assets/images/daniel.jpg", + "category":"类别C", + "state": 2, + "sort": 3, + "desc": "这是一段简短的商品介绍,用来介绍商品", + "createTime": "2023-03-25 12:39:55" + } + ], + "total": 3 + }, + "msg": "seccess" +} diff --git a/src/mock/proEdit.json b/src/mock/proEdit.json new file mode 100644 index 0000000..347e064 --- /dev/null +++ b/src/mock/proEdit.json @@ -0,0 +1,15 @@ +{ + "code": "00000", + "data": { + "id": 1, + "name": "A商品", + "brands": 1, + "price": "200", + "picture": "../../src/assets/images/daniel.jpg", + "category": 2, + "sort": 1, + "desc": "这是一段简短的商品介绍,用来介绍商品", + "createTime": "2023-03-25 12:39:55" + }, + "msg": "seccess" +} diff --git a/src/mock/roleEdit.json b/src/mock/roleEdit.json new file mode 100644 index 0000000..3437d28 --- /dev/null +++ b/src/mock/roleEdit.json @@ -0,0 +1,12 @@ +{ + "code": "00000", + "data": { + "id": 2, + "name": "系统管理员", + "code": "ADMIN", + "sort": 2, + "status": 1, + "dataScope": 1 + }, + "msg": "seccess" +} diff --git a/src/mock/roles.json b/src/mock/roles.json new file mode 100644 index 0000000..72d276e --- /dev/null +++ b/src/mock/roles.json @@ -0,0 +1,14 @@ +{ + "code": "00000", + "data": [ + { + "value": 1, + "label": "系统管理员" + }, + { + "value": 2, + "label": "普通用户" + } + ], + "msg": "seccess" +} diff --git a/src/mock/rolesData.json b/src/mock/rolesData.json new file mode 100644 index 0000000..7bf8d93 --- /dev/null +++ b/src/mock/rolesData.json @@ -0,0 +1,99 @@ +{ + "code": "00000", + "data": { + "list": [ + { + "id": 2, + "name": "系统管理员", + "code": "ADMIN", + "status": 1, + "sort": 2, + "createTime": "2023-03-25 12:39:54", + "updateTime": null + }, + { + "id": 3, + "name": "访问游客", + "code": "GUEST", + "status": 1, + "sort": 3, + "createTime": "2023-05-26 15:49:05", + "updateTime": "2023-05-05 16:00:00" + }, + { + "id": 4, + "name": "系统管理员1", + "code": "ADMIN1", + "status": 1, + "sort": 2, + "createTime": "2023-03-25 12:39:54", + "updateTime": null + }, + { + "id": 5, + "name": "系统管理员2", + "code": "ADMIN1", + "status": 1, + "sort": 2, + "createTime": "2023-03-25 12:39:54", + "updateTime": null + }, + { + "id": 6, + "name": "系统管理员3", + "code": "ADMIN1", + "status": 1, + "sort": 2, + "createTime": "2023-03-25 12:39:54", + "updateTime": null + }, + { + "id": 7, + "name": "系统管理员4", + "code": "ADMIN1", + "status": 1, + "sort": 2, + "createTime": "2023-03-25 12:39:54", + "updateTime": null + }, + { + "id": 8, + "name": "系统管理员5", + "code": "ADMIN1", + "status": 1, + "sort": 2, + "createTime": "2023-03-25 12:39:54", + "updateTime": null + }, + { + "id": 9, + "name": "系统管理员6", + "code": "ADMIN1", + "status": 1, + "sort": 2, + "createTime": "2023-03-25 12:39:54", + "updateTime": null + }, + { + "id": 10, + "name": "系统管理员7", + "code": "ADMIN1", + "status": 1, + "sort": 2, + "createTime": "2023-03-25 12:39:54", + "updateTime": null + }, + { + "id": 11, + "name": "系统管理员8", + "code": "ADMIN1", + "status": 1, + "sort": 2, + "createTime": "2023-03-25 12:39:54", + "updateTime": null + } + ], + "total": 11 + }, + "msg": "seccess" +} diff --git a/src/mock/routes.json b/src/mock/routes.json new file mode 100644 index 0000000..1074598 --- /dev/null +++ b/src/mock/routes.json @@ -0,0 +1,43 @@ +{ + "code": "00000", + "data": [ + { + "path": "/monitoring", + "component": "Layout", + "meta": { + "title": "监控系统", + "icon": "multi_level", + "hidden": false, + "roles": ["admin"], + "keepAlive": true + }, + "children": [ + { + "path": "screen", + "component": "monitoring/screen/index", + "name": "screen", + "meta": { + "title": "数据大屏", + "icon": "document", + "hidden": false, + "roles": ["admin"], + "keepAlive": true + } + }, + { + "path": "devicemanage", + "component": "monitoring/devicemanage/index", + "name": "devicemanage", + "meta": { + "title": "设备管理", + "icon": "system", + "hidden": false, + "roles": ["admin"], + "keepAlive": true + } + } + ] + } + ], + "msg": "seccess" +} diff --git a/src/mock/user.json b/src/mock/user.json new file mode 100644 index 0000000..c452154 --- /dev/null +++ b/src/mock/user.json @@ -0,0 +1,11 @@ +{ + "code": "00000", + "data": { + "userId": 2, + "nickname": "系统管理员", + "avatar": "../../src/assets/images/daniel.jpg", + "roles": ["ADMIN"], + "perms": ["sys:user:edit", "sys:user:delete", "sys:user:add"] + }, + "msg": "seccess" +} diff --git a/src/mock/userData.json b/src/mock/userData.json new file mode 100644 index 0000000..89520d0 --- /dev/null +++ b/src/mock/userData.json @@ -0,0 +1,22 @@ +{ + "code": "00000", + "data": { + "list": [ + { + "id": 1, + "username": "admin", + "nickname": "系统管理员", + "mobile": "15800008571", + "sex": "男", + "avatar": "../../src/assets/images/daniel.jpg", + "email": null, + "status": 1, + "deptName": "研发部门", + "roleNames": "系统管理员", + "createTime": "2023-05-16" + } + ], + "total": 1 + }, + "msg": "seccess" +} diff --git a/src/mock/userEdit.json b/src/mock/userEdit.json new file mode 100644 index 0000000..3a1ff3b --- /dev/null +++ b/src/mock/userEdit.json @@ -0,0 +1,16 @@ +{ + "code": "00000", + "data": { + "id": 1, + "username": "admin", + "nickname": "系统管理员", + "mobile": "15800008571", + "sex": 1, + "avatar": "../../src/assets/images/daniel.jpg", + "email": "", + "status": 1, + "deptId": 2, + "roleIds": [1] + }, + "msg": "seccess" +} \ No newline at end of file diff --git a/src/permission.ts b/src/permission.ts new file mode 100644 index 0000000..cf029dd --- /dev/null +++ b/src/permission.ts @@ -0,0 +1,68 @@ +/** + * Author: Fu Guobin + * Date: 2022/12/28 + * Last Modified by: Fu Guobin + * Last Modified time: 2023/05/05 + * Copyright:Daniel(Fu Guobin) + * Description:router配置 + */ +import router from '@/router'; +import { useUserStoreHook } from '@/store/modules/user'; +import { usePermissionStoreHook } from '@/store/modules/permission'; +import NProgress from 'nprogress'; +import 'nprogress/nprogress.css'; +NProgress.configure({ showSpinner: false }); // 进度条 + +const permissionStore = usePermissionStoreHook(); + +// 白名单路由 +const whiteList = ['/login']; + +router.beforeEach(async (to, from, next) => { + NProgress.start(); + const hasToken = localStorage.getItem('userToken'); + if (hasToken) { + if (to.path === '/login') { + // 如果已登录,跳转首页 + next({ path: '/' }); + NProgress.done(); + } else { + const userStore = useUserStoreHook(); + const hasRoles = userStore.roles && userStore.roles.length > 0; + if (hasRoles) { + // 未匹配到任何路由,跳转404 + if (to.matched.length === 0) { + from.name ? next({ name: from.name }) : next('/404'); + } else { + next(); + } + } else { + try { + const { roles } = await userStore.getInfo(); + const accessRoutes = await permissionStore.generateRoutes(roles); + accessRoutes.forEach(route => { + router.addRoute(route); + }); + next({ ...to, replace: true }); + } catch (error) { + // 移除 token 并跳转登录页 + await userStore.resetToken(); + next(`/login?redirect=${to.path}`); + NProgress.done(); + } + } + } + } else { + // 未登录可以访问白名单页面 + if (whiteList.indexOf(to.path) !== -1) { + next(); + } else { + next(`/login?redirect=${to.path}`); + NProgress.done(); + } + } +}); + +router.afterEach(() => { + NProgress.done(); +}); diff --git a/src/plugins/bus.ts b/src/plugins/bus.ts new file mode 100644 index 0000000..1a13398 --- /dev/null +++ b/src/plugins/bus.ts @@ -0,0 +1,2 @@ +import mitt from 'mitt' +export default mitt() \ No newline at end of file diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..f08e391 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,138 @@ +/** + * Author: Fu Guobin + * Date: 2022/12/28 + * Last Modified by: Fu Guobin + * Last Modified time: 2023/05/12 + * Copyright:Daniel(Fu Guobin) + * Description:router配置 + */ + +import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'; +export const Layout = () => import('@/layout/index.vue'); + +// 静态路由 +export const constantRoutes: RouteRecordRaw[] = [ + { + path: '/redirect', + component: Layout, + meta: { hidden: true }, + children: [ + { + path: '/redirect/:path(.*)', + component: () => import('@/views/redirect/index.vue') + } + ] + }, + + { + path: '/login', + component: () => import('@/views/login/index.vue'), + meta: { hidden: true } + }, + { + path: '/screen', + component: () => import('@/views/monitoring/screen/index.vue'), + meta: { hidden: true } + }, + { + path: '/devicemanage', + component: () => import('@/views/monitoring/devicemanage/index.vue'), + meta: { hidden: true } + }, + + { + path: '/', + component: Layout, + redirect: '/monitoring/screen', + children: [ + // { + // path: 'dashboard', + // component: () => import('@/views/dashboard/index.vue'), + // name: 'Dashboard', + // meta: { title: 'dashboard', icon: 'icon-home', affix: true } + // }, + { + path: '401', + component: () => import('@/views/error-page/401.vue'), + meta: { hidden: true } + }, + { + path: '404', + component: () => import('@/views/error-page/404.vue'), + meta: { hidden: true } + } + ] + } + + // 外部链接 + /*{ + path: '/external-link', + component: Layout, + children: [ + { + path: 'https://www.baidu.com/', + meta: { title: '外部链接', icon: 'link' } + } + ] + }*/ + // 多级嵌套路由 + /* { + path: '/nested', + component: Layout, + redirect: '/nested/level1/level2', + name: 'Nested', + meta: {title: '多级菜单', icon: 'nested'}, + children: [ + { + path: 'level1', + component: () => import('@/views/nested/level1/index.vue'), + name: 'Level1', + meta: {title: '菜单一级'}, + redirect: '/nested/level1/level2', + children: [ + { + path: 'level2', + component: () => import('@/views/nested/level1/level2/index.vue'), + name: 'Level2', + meta: {title: '菜单二级'}, + redirect: '/nested/level1/level2/level3', + children: [ + { + path: 'level3-1', + component: () => import('@/views/nested/level1/level2/level3/index1.vue'), + name: 'Level3-1', + meta: {title: '菜单三级-1'} + }, + { + path: 'level3-2', + component: () => import('@/views/nested/level1/level2/level3/index2.vue'), + name: 'Level3-2', + meta: {title: '菜单三级-2'} + } + ] + } + ] + }, + ] + }*/ +]; + +/** + * 创建路由 + */ +const router = createRouter({ + history: createWebHashHistory(), + routes: constantRoutes as RouteRecordRaw[], + // 刷新时,滚动条位置还原 + scrollBehavior: () => ({ left: 0, top: 0 }) +}); + +/** + * 重置路由 + */ +export function resetRouter() { + router.replace({ path: '/login' }); + location.reload(); +} + +export default router; diff --git a/src/settings.ts b/src/settings.ts new file mode 100644 index 0000000..cda29e9 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,67 @@ +// 系统设置 +interface DefaultSettings { + /** + * 系统title + */ + title: string; + + /** + * 是否显示设置 + */ + showSettings: boolean; + /** + * 是否显示多标签导航 + */ + tagsView: boolean; + /** + *是否固定头部 + */ + fixedHeader: boolean; + /** + * 是否显示侧边栏Logo + */ + sidebarLogo: boolean; + /** + * 是否深色侧边栏 + */ + sidebarDark: boolean; + /** + * 导航栏布局 + */ + layout: string; + /** + * 主题模式 + */ + theme: string; + + /** + * 布局大小 + */ + size: string; + + /** + * 语言 + */ + language: string; +} + +const defaultSettings: DefaultSettings = { + title: '联美运营驾驶舱管理系统', + showSettings: true, + tagsView: true, + fixedHeader: false, + sidebarLogo: true, + sidebarDark:true, + layout: 'left', + /** + * 主题模式 + * + * dark:暗黑模式 + * light: 明亮模式 + */ + theme: 'dark', + size: 'default', // default |large |small + language: 'zh-cn' // zh-cn| en +}; + +export default defaultSettings; diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..ceb4219 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,11 @@ +import type { App } from 'vue'; +import { createPinia } from 'pinia'; + +const store = createPinia(); + +// 全局注册 store +export function setupStore(app: App) { + app.use(store); +} + +export { store }; diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts new file mode 100644 index 0000000..d057372 --- /dev/null +++ b/src/store/modules/app.ts @@ -0,0 +1,79 @@ +import { defineStore } from 'pinia'; +import { useStorage } from '@vueuse/core'; +import defaultSettings from '@/settings'; + +// Element Plus 语言包 +import zhCn from 'element-plus/es/locale/lang/zh-cn'; +import en from 'element-plus/es/locale/lang/en'; + +// setup +export const useAppStore = defineStore('app', () => { + // state + + const device = useStorage('device', 'desktop'); + const size = useStorage('size', defaultSettings.size); + const language = useStorage('language', defaultSettings.language); + + const sidebarStatus = useStorage('sidebarStatus', 'closed'); + const sidebar = reactive({ + opened: sidebarStatus.value !== 'closed', + withoutAnimation: false + }); + + const locale = computed(() => { + if (language?.value == 'en') { + return en; + } else { + return zhCn; + } + }); + + // actions + function toggleSidebar(withoutAnimation: boolean) { + sidebar.opened = !sidebar.opened; + sidebar.withoutAnimation = withoutAnimation; + if (sidebar.opened) { + sidebarStatus.value = 'opened'; + } else { + sidebarStatus.value = 'closed'; + } + } + + function closeSideBar(withoutAnimation: boolean) { + sidebar.opened = false; + sidebar.withoutAnimation = withoutAnimation; + sidebarStatus.value = 'closed'; + } + + function openSideBar(withoutAnimation: boolean) { + sidebar.opened = true; + sidebar.withoutAnimation = withoutAnimation; + sidebarStatus.value = 'opened'; + } + + function toggleDevice(val: string) { + device.value = val; + } + + function changeSize(val: string) { + size.value = val; + } + + function changeLanguage(val: string) { + language.value = val; + } + + return { + device, + sidebar, + language, + locale, + size, + toggleDevice, + changeSize, + changeLanguage, + toggleSidebar, + closeSideBar, + openSideBar + }; +}); diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts new file mode 100644 index 0000000..48b3c23 --- /dev/null +++ b/src/store/modules/permission.ts @@ -0,0 +1,113 @@ +import { RouteRecordRaw } from 'vue-router'; +import { defineStore } from 'pinia'; +import { constantRoutes } from '@/router'; +import { store } from '@/store'; +import { listRoutes, listMenus } from '@/api/menu'; +import routesData from '@/mock/routes.json'; + +const modules = import.meta.glob('../../views/**/**.vue'); +const Layout = () => import('@/layout/index.vue'); + +/** + * Use meta.role to determine if the current user has permission + * + * @param roles 用户角色集合 + * @param route 路由 + * @returns + */ +const hasPermission = (roles: string[], route: RouteRecordRaw) => { + if (route.meta && route.meta.roles) { + // 角色【超级管理员】拥有所有权限,忽略校验 + if (roles.includes('ROOT')) { + return true; + } + return roles.some(role => { + if (route.meta?.roles !== undefined) { + return (route.meta.roles as string[]).includes(role); + } + }); + } + return false; +}; + +/** + * 递归过滤有权限的异步(动态)路由 + * + * @param routes 接口返回的异步(动态)路由 + * @param roles 用户角色集合 + * @returns 返回用户有权限的异步(动态)路由 + */ +const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => { + const asyncRoutes: RouteRecordRaw[] = []; + + routes.forEach(route => { + const tmpRoute = { ...route }; // ES6扩展运算符复制新对象 + // 判断用户(角色)是否有该路由的访问权限 + if (hasPermission(roles, tmpRoute)) { + if (tmpRoute.component?.toString() == 'Layout') { + tmpRoute.component = Layout; + console.log(); + } else { + const component = modules[`../../views/${tmpRoute.component}.vue`]; + if (component) { + tmpRoute.component = component; + } else { + tmpRoute.component = modules[`../../views/error-page/404.vue`]; + } + } + + if (tmpRoute.children) { + tmpRoute.children = filterAsyncRoutes(tmpRoute.children, roles); + } + + asyncRoutes.push(tmpRoute); + } + }); + + return asyncRoutes; +}; + +// setup +export const usePermissionStore = defineStore('permission', () => { + // state + const routes = ref([]); + + // actions + function setRoutes(newRoutes: RouteRecordRaw[]) { + routes.value = constantRoutes.concat(newRoutes); + } + /** + * 生成动态路由 + * + * @param roles 用户角色集合 + * @returns + */ + function generateRoutes(roles: string[]) { + return new Promise((resolve, reject) => { + // 接口获取所有路由 + listRoutes() + .then(({ data: asyncRoutes }) => { + // 根据角色获取有访问权限的路由 + // if (asyncRoutes.length === 0) { + const routesArr = routesData.data; + const accessedRoutes = filterAsyncRoutes(routesArr, roles); + setRoutes(accessedRoutes); + resolve(accessedRoutes); + // } else { + // const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles); + // setRoutes(accessedRoutes); + // resolve(accessedRoutes); + // } + }) + .catch(error => { + reject(error); + }); + }); + } + return { routes, setRoutes, generateRoutes }; +}); + +// 非setup +export function usePermissionStoreHook() { + return usePermissionStore(store); +} diff --git a/src/store/modules/settings.ts b/src/store/modules/settings.ts new file mode 100644 index 0000000..38e2376 --- /dev/null +++ b/src/store/modules/settings.ts @@ -0,0 +1,53 @@ +import { defineStore } from 'pinia'; +import defaultSettings from '@/settings'; +import { useStorage } from '@vueuse/core'; + +export const useSettingsStore = defineStore('setting', () => { + // state + const tagsView = useStorage('tagsView', defaultSettings.tagsView); + + const title = ref(defaultSettings.title); + const showSettings = ref(defaultSettings.showSettings); + const fixedHeader = ref(defaultSettings.fixedHeader); + const sidebarLogo = ref(defaultSettings.sidebarLogo); + const sidebarDark = ref(defaultSettings.sidebarDark); + const layout = useStorage('layout', defaultSettings.layout); + + // actions + function changeSetting(param: { key: string; value: any }) { + const { key, value } = param; + switch (key) { + case 'showSettings': + showSettings.value = value; + break; + case 'fixedHeader': + fixedHeader.value = value; + break; + case 'tagsView': + tagsView.value = value; + break; + case 'sidevarLogo': + sidebarLogo.value = value; + break; + case 'sidebarDark': + sidebarDark.value = value; + break; + case 'layout': + layout.value = value; + break; + default: + break; + } + } + + return { + title, + showSettings, + tagsView, + fixedHeader, + sidebarLogo, + sidebarDark, + layout, + changeSetting + }; +}); diff --git a/src/store/modules/table.ts b/src/store/modules/table.ts new file mode 100644 index 0000000..bacfc33 --- /dev/null +++ b/src/store/modules/table.ts @@ -0,0 +1,26 @@ +import { defineStore } from 'pinia'; +import { store } from '@/store'; + +export const tableStore = defineStore({ + id: 'app-table', + state: () => ({ + tableMenuStory:[], + tableDataStore: [], + tableNewDataStore:[] + }), + getters: {}, + actions: { + tableMenuAction(val:any) { + this.tableMenuStory = val; + }, + tableDataAction(val:any) { + this.tableDataStore = val; + }, + tableNewDataAction(val:any) { + this.tableNewDataStore = val; + }, + }, +}); +export function useTableStore() { + return tableStore(store); +} diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts new file mode 100644 index 0000000..f564b76 --- /dev/null +++ b/src/store/modules/tagsView.ts @@ -0,0 +1,214 @@ +import { ref } from 'vue'; +import { defineStore } from 'pinia'; +import { RouteLocationNormalized } from 'vue-router'; + +export interface TagView extends Partial { + title?: string; +} + +// setup +export const useTagsViewStore = defineStore('tagsView', () => { + // state + const visitedViews = ref([]); + const cachedViews = ref([]); + + // actions + function addVisitedView(view: TagView) { + if (visitedViews.value.some(v => v.path === view.path)) return; + if (view.meta && view.meta.affix) { + visitedViews.value.unshift( + Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }) + ); + } else { + visitedViews.value.push( + Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }) + ); + } + } + + function addCachedView(view: TagView) { + const viewName = view.name as string; + if (cachedViews.value.includes(viewName)) return; + if (view.meta?.keepAlive) { + cachedViews.value.push(viewName); + } + } + + function delVisitedView(view: TagView) { + return new Promise(resolve => { + for (const [i, v] of visitedViews.value.entries()) { + if (v.path === view.path) { + visitedViews.value.splice(i, 1); + break; + } + } + resolve([...visitedViews.value]); + }); + } + + function delCachedView(view: TagView) { + const viewName = view.name as string; + return new Promise(resolve => { + const index = cachedViews.value.indexOf(viewName); + index > -1 && cachedViews.value.splice(index, 1); + resolve([...cachedViews.value]); + }); + } + + function delOtherVisitedViews(view: TagView) { + return new Promise(resolve => { + visitedViews.value = visitedViews.value.filter(v => { + return v.meta?.affix || v.path === view.path; + }); + resolve([...visitedViews.value]); + }); + } + + function delOtherCachedViews(view: TagView) { + const viewName = view.name as string; + return new Promise(resolve => { + const index = cachedViews.value.indexOf(viewName); + if (index > -1) { + cachedViews.value = cachedViews.value.slice(index, index + 1); + } else { + // if index = -1, there is no cached tags + cachedViews.value = []; + } + resolve([...cachedViews.value]); + }); + } + + function updateVisitedView(view: TagView) { + for (let v of visitedViews.value) { + if (v.path === view.path) { + v = Object.assign(v, view); + break; + } + } + } + + function addView(view: TagView) { + addVisitedView(view); + addCachedView(view); + } + + function delView(view: TagView) { + return new Promise(resolve => { + delVisitedView(view); + delCachedView(view); + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value] + }); + }); + } + + function delOtherViews(view: TagView) { + return new Promise(resolve => { + delOtherVisitedViews(view); + delOtherCachedViews(view); + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value] + }); + }); + } + + function delLeftViews(view: TagView) { + return new Promise(resolve => { + const currIndex = visitedViews.value.findIndex(v => v.path === view.path); + if (currIndex === -1) { + return; + } + visitedViews.value = visitedViews.value.filter((item, index) => { + // affix:true 固定tag,例如“首页” + if (index >= currIndex || (item.meta && item.meta.affix)) { + return true; + } + + const cacheIndex = cachedViews.value.indexOf(item.name as string); + if (cacheIndex > -1) { + cachedViews.value.splice(cacheIndex, 1); + } + return false; + }); + resolve({ + visitedViews: [...visitedViews.value] + }); + }); + } + function delRightViews(view: TagView) { + return new Promise(resolve => { + const currIndex = visitedViews.value.findIndex(v => v.path === view.path); + if (currIndex === -1) { + return; + } + visitedViews.value = visitedViews.value.filter((item, index) => { + // affix:true 固定tag,例如“首页” + if (index <= currIndex || (item.meta && item.meta.affix)) { + return true; + } + + const cacheIndex = cachedViews.value.indexOf(item.name as string); + if (cacheIndex > -1) { + cachedViews.value.splice(cacheIndex, 1); + } + return false; + }); + resolve({ + visitedViews: [...visitedViews.value] + }); + }); + } + + function delAllViews() { + return new Promise(resolve => { + const affixTags = visitedViews.value.filter(tag => tag.meta?.affix); + visitedViews.value = affixTags; + cachedViews.value = []; + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value] + }); + }); + } + + function delAllVisitedViews() { + return new Promise(resolve => { + const affixTags = visitedViews.value.filter(tag => tag.meta?.affix); + visitedViews.value = affixTags; + resolve([...visitedViews.value]); + }); + } + + function delAllCachedViews() { + return new Promise(resolve => { + cachedViews.value = []; + resolve([...cachedViews.value]); + }); + } + + return { + visitedViews, + cachedViews, + addVisitedView, + addCachedView, + delVisitedView, + delCachedView, + delOtherVisitedViews, + delOtherCachedViews, + updateVisitedView, + addView, + delView, + delOtherViews, + delLeftViews, + delRightViews, + delAllViews, + delAllVisitedViews, + delAllCachedViews + }; +}); diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts new file mode 100644 index 0000000..77a5a16 --- /dev/null +++ b/src/store/modules/user.ts @@ -0,0 +1,105 @@ +import { defineStore } from 'pinia'; + +import { loginApi, logoutApi } from '@/api/auth'; +import { getUserInfo } from '@/api/user'; +import { resetRouter } from '@/router'; +import { store } from '@/store'; + +import { LoginData } from '@/api/auth/types'; +import { UserInfo } from '@/api/user/types'; + +import { useStorage } from '@vueuse/core'; + +export const useUserStore = defineStore('user', () => { + // state + const token = useStorage('userToken', ''); + const nickname = ref(''); + const avatar = ref(''); + const roles = ref>([]); // 用户角色编码集合 → 判断路由权限 + const perms = ref>([]); // 用户权限编码集合 → 判断按钮权限 + + /** + * 登录调用 + * + * @param {LoginData} + * @returns + */ + function login(loginData: LoginData) { + return new Promise((resolve, reject) => { + loginApi(loginData) + .then((response:any) => { + const userToken = response.token; + token.value = 'Bearer ' + userToken; // Bearer eyJhbGciOiJIUzI1NiJ9.xxx.xxx + resolve(); + }) + .catch(error => { + reject(error); + }); + }); + } + + // 获取信息(用户昵称、头像、角色集合、权限集合) + function getInfo() { + return new Promise((resolve, reject) => { + getUserInfo() + .then((data:any) => { + if (!data) { + return reject('Verification failed, please Login again.'); + } + if (!data.roles || data.roles.length <= 0) { + reject('getUserInfo: roles must be a non-null array!'); + } + const user=data.user + nickname.value = user.nickName; + avatar.value = user.avatar; + roles.value = data.roles; + perms.value = data.permissions; + useStorage('userInfo', user); + resolve(data); + }) + .catch(error => { + reject(error); + }); + }); + } + + // 注销 + function logout() { + return new Promise((resolve, reject) => { + logoutApi() + .then(() => { + resetRouter(); + resetToken(); + resolve(); + }) + .catch(error => { + reject(error); + }); + }); + } + + // 重置 + function resetToken() { + token.value = ''; + nickname.value = ''; + avatar.value = ''; + roles.value = []; + perms.value = []; + } + return { + token, + nickname, + avatar, + roles, + perms, + login, + getInfo, + logout, + resetToken + }; +}); + +// 非setup +export function useUserStoreHook() { + return useUserStore(store); +} diff --git a/src/styles/dark.scss b/src/styles/dark.scss new file mode 100644 index 0000000..70ffbdb --- /dev/null +++ b/src/styles/dark.scss @@ -0,0 +1,24 @@ +html.dark{ + --menuBg:var(--el-bg-color-overlay); + --menuText:#fff; + --menuActiveText:var(--el-menu-active-color); + --menuActiveBg:var(--el-bg-color-overlay); + --menuHover:rgba(0,0,0,.2); + + --subMenuBg: var(--el-menu-bg-color); + --subMenuActiveText:var(--el-menu-active-color); + --subMenuHover: rgba(0,0,0,.2); + + + .navbar { + background-color: var(--el-bg-color); + color: var(--el-text-color-regular); + } + + .right-panel-btn{ + background-color: var(--el-color-primary-dark); + } + .svg-icon,svg{ + fill: var(--el-text-color-regular); + } +} diff --git a/src/styles/index.scss b/src/styles/index.scss new file mode 100644 index 0000000..dbe3009 --- /dev/null +++ b/src/styles/index.scss @@ -0,0 +1,20 @@ +@import './sidebar.scss'; +@import './reset.scss'; +@import './variablesDark.scss'; +@import './dark.scss'; +@import './style.scss'; + + +.app-container { + padding: 20px; + .search { + padding: 18px 0 0 10px; + margin-bottom: 10px; + border-radius: 4px; + border: 1px solid var(--el-border-color-light); + box-shadow: var(--el-box-shadow-light); + background-color: var(--el-bg-color-overlay); + } + +} + diff --git a/src/styles/reset.scss b/src/styles/reset.scss new file mode 100644 index 0000000..cd586c3 --- /dev/null +++ b/src/styles/reset.scss @@ -0,0 +1,81 @@ +*, +::before, +::after { + box-sizing: border-box; + border-width: 0; + border-style: solid; + border-color: currentColor; +} + +#app { + width: 100%; + height: 100%; +} + +html { + line-height: 1.5; + -webkit-text-size-adjust: 100%; + -moz-tab-size: 4; + tab-size: 4; + width: 100%; + height: 100%; + box-sizing: border-box; +} + +body { + margin: 0; + line-height: inherit; + width: 100%; + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizelegibility; + font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", + "Microsoft YaHei", "微软雅黑", Arial, sans-serif; +} + + +a { + color: inherit; + text-decoration: inherit; +} + + +img, +svg + { + display: inline-block; +} +svg { + vertical-align: -0.15em; //因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 +} + + +ul,li{ + margin: 0; + padding: 0; + list-style: none; +} + + +*, +*::before, +*::after { + box-sizing: inherit; +} + +a:focus, +a:active , +div:focus +{ + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + diff --git a/src/styles/sidebar.scss b/src/styles/sidebar.scss new file mode 100644 index 0000000..205d35f --- /dev/null +++ b/src/styles/sidebar.scss @@ -0,0 +1,212 @@ +#app { + .mainContainer { + min-height: 100%; + transition: margin-left 0.28s; + margin-left: $sideBarWidth; + position: relative; + } + + .sidebarContainer { + transition: width 0.28s; + width: $sideBarWidth !important; + background-color: $menuBg; + box-shadow: 2px 0 8px #1d23290d; + height: 100%; + position: fixed; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, + 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.sidebarLogo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + + .svg-icon { + margin-right: 16px; + } + + .sub-el-icon { + margin-right: 12px; + margin-left: -2px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + // menu hover + .el-sub-menu__title { + &:hover { + background-color: $menuHover !important; + } + } + + .is-active>.el-sub-menu__title { + color: $subMenuActiveText !important; + } + + & .nest-menu .el-sub-menu>.el-sub-menu__title, + & .el-sub-menu .el-menu-item { + min-width: $sideBarWidth !important; + // background-color: $subMenuBg !important; + + &:hover { + background-color: $subMenuHover !important; + } + } + + .el-menu-item.is-active,.el-sub-menu .el-menu-item.is-active { + background-color: $menuActiveBg !important; + } + } + + .hideSidebar { + .sidebarContainer { + width: 54px !important; + + .svg-icon { + margin-right: 0px; + } + } + + .mainContainer { + margin-left: 54px; + } + + .el-sub-menu { + overflow: hidden; + + &>.el-sub-menu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + .sub-el-icon { + margin-left: 19px; + } + + .el-sub-menu__icon-arrow { + display: none; + } + } + } + + .el-menu--collapse { + .el-sub-menu { + &>.el-sub-menu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-sub-menu { + min-width: $sideBarWidth !important; + } + + // mobile responsive + .mobile { + .mainContainer { + margin-left: 0px; + } + + .sidebarContainer { + transition: transform 0.28s; + width: $sideBarWidth !important; + } + + &.hideSidebar { + .sidebarContainer { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$sideBarWidth, 0, 0); + } + } + } + + .withoutAnimation { + + .mainContainer, + .sidebarContainer { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + + .sub-el-icon { + margin-right: 12px; + margin-left: -2px; + } + } + + .nest-menu .el-sub-menu>.el-sub-menu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: $menuHover !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/src/styles/style.scss b/src/styles/style.scss new file mode 100644 index 0000000..8a62fd4 --- /dev/null +++ b/src/styles/style.scss @@ -0,0 +1,28 @@ +.flex { + display: flex; +} +.justify-center { + justify-content: center; +} +.justify-between { + justify-content: space-between; +} +.items-center { + align-items: center; +} +.column { + flex-direction: column; +} +.flex-1 { + flex: 1 1 0%; +} + +.text-center { + text-align: center; +} +.width-100 { + width: 100%; +} +.height-100 { + height: 100%; +} diff --git a/src/styles/variables.module.scss b/src/styles/variables.module.scss new file mode 100644 index 0000000..2aae6fd --- /dev/null +++ b/src/styles/variables.module.scss @@ -0,0 +1,7 @@ +// 导出 variables.module.scss 变量提供给TypeScript使用 +:export { + menuBg: $menuBg; + menuText: $menuText; + menuActiveText: $menuActiveText; + menuActiveBg: $menuActiveBg +} \ No newline at end of file diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 0000000..1818429 --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,36 @@ +// 全局SCSS变量 + +:root{ + --menuBg:#ffffff; + --menuText:#333639; + --menuActiveText:#409eff; + --menuActiveBg:#eff0ff; + --menuHover:#d9d9d9; + + --subMenuBg: #ffffff; + --subMenuActiveText: #409eff; + --subMenuHover: #d9d9d9; + + --menuDarkBg:#304156; + --menuDarkText:#bfcbd9; + --menuDarkActiveText:#409eff; + --menuDarkActiveBg:rgba(0, 0, 0, 0.12); + --menuDarkHover:#263445; + + --subMenuDarkBg: #1f2d3d; + --subMenuDarkActiveText: #f4f4f5; + --subMenuDarkHover: #001528; +} + + +$menuBg: var(--menuBg); +$menuText: var(--menuText); +$menuActiveText: var(--menuActiveText); +$menuActiveBg: var(--menuActiveBg); +$menuHover: var(--menuHover); + +$subMenuBg:var(--subMenuBg); +$subMenuActiveText:var(--subMenuActiveText); +$subMenuHover:var(--subMenuHover); + +$sideBarWidth: 210px; diff --git a/src/styles/variablesDark.scss b/src/styles/variablesDark.scss new file mode 100644 index 0000000..b1f2d9d --- /dev/null +++ b/src/styles/variablesDark.scss @@ -0,0 +1,28 @@ +// 侧边栏深色主题变量 + +.layoutContainer { + .sidebarDark { + --menuBg: var(--menuDarkBg); + --menuText: var(--menuDarkText); + --menuActiveText: var(--menuDarkActiveText); + --menuActiveBg: var(--menuDarkActiveBg); + --menuHover: var(--menuDarkHover); + + --subMenuBg: var(--subMenuDarkBg); + --subMenuActiveText: var(--subMenuDarkActiveText); + --subMenuHover: var(--subMenuDarkHover); + } +} +.dark { + .sidebarDark { + --menuBg: var(--el-bg-color-overlay); + --menuText: #fff; + --menuActiveText: var(--el-menu-active-color); + --menuActiveBg: var(--el-bg-color-overlay); + --menuHover: rgba(0, 0, 0, 0.2); + + --subMenuBg: var(--el-menu-bg-color); + --subMenuActiveText: var(--el-menu-active-color); + --subMenuHover: rgba(0, 0, 0, 0.2); + } +} diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts new file mode 100644 index 0000000..89da715 --- /dev/null +++ b/src/types/auto-imports.d.ts @@ -0,0 +1,538 @@ +// Generated by 'unplugin-auto-import' +export {} +declare global { + const EffectScope: typeof import('vue')['EffectScope'] + const ElMessage: typeof import('element-plus/es')['ElMessage'] + const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] + const NEllipsis: typeof import('naive-ui')['NEllipsis'] + const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] + const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] + const computed: typeof import('vue')['computed'] + const computedAsync: typeof import('@vueuse/core')['computedAsync'] + const computedEager: typeof import('@vueuse/core')['computedEager'] + const computedInject: typeof import('@vueuse/core')['computedInject'] + const computedWithControl: typeof import('@vueuse/core')['computedWithControl'] + const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] + const controlledRef: typeof import('@vueuse/core')['controlledRef'] + const createApp: typeof import('vue')['createApp'] + const createEventHook: typeof import('@vueuse/core')['createEventHook'] + const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] + const createInjectionState: typeof import('@vueuse/core')['createInjectionState'] + const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] + const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] + const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] + const customRef: typeof import('vue')['customRef'] + const debouncedRef: typeof import('@vueuse/core')['debouncedRef'] + const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] + const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] + const defineComponent: typeof import('vue')['defineComponent'] + const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] + const effectScope: typeof import('vue')['effectScope'] + const extendRef: typeof import('@vueuse/core')['extendRef'] + const getCurrentInstance: typeof import('vue')['getCurrentInstance'] + const getCurrentScope: typeof import('vue')['getCurrentScope'] + const h: typeof import('vue')['h'] + const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] + const inject: typeof import('vue')['inject'] + const isDefined: typeof import('@vueuse/core')['isDefined'] + const isProxy: typeof import('vue')['isProxy'] + const isReactive: typeof import('vue')['isReactive'] + const isReadonly: typeof import('vue')['isReadonly'] + const isRef: typeof import('vue')['isRef'] + const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] + const markRaw: typeof import('vue')['markRaw'] + const nextTick: typeof import('vue')['nextTick'] + const onActivated: typeof import('vue')['onActivated'] + const onBeforeMount: typeof import('vue')['onBeforeMount'] + const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] + const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] + const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] + const onDeactivated: typeof import('vue')['onDeactivated'] + const onErrorCaptured: typeof import('vue')['onErrorCaptured'] + const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke'] + const onLongPress: typeof import('@vueuse/core')['onLongPress'] + const onMounted: typeof import('vue')['onMounted'] + const onRenderTracked: typeof import('vue')['onRenderTracked'] + const onRenderTriggered: typeof import('vue')['onRenderTriggered'] + const onScopeDispose: typeof import('vue')['onScopeDispose'] + const onServerPrefetch: typeof import('vue')['onServerPrefetch'] + const onStartTyping: typeof import('@vueuse/core')['onStartTyping'] + const onUnmounted: typeof import('vue')['onUnmounted'] + const onUpdated: typeof import('vue')['onUpdated'] + const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] + const provide: typeof import('vue')['provide'] + const reactify: typeof import('@vueuse/core')['reactify'] + const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] + const reactive: typeof import('vue')['reactive'] + const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed'] + const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit'] + const reactivePick: typeof import('@vueuse/core')['reactivePick'] + const readonly: typeof import('vue')['readonly'] + const ref: typeof import('vue')['ref'] + const refAutoReset: typeof import('@vueuse/core')['refAutoReset'] + const refDebounced: typeof import('@vueuse/core')['refDebounced'] + const refDefault: typeof import('@vueuse/core')['refDefault'] + const refThrottled: typeof import('@vueuse/core')['refThrottled'] + const refWithControl: typeof import('@vueuse/core')['refWithControl'] + const resolveComponent: typeof import('vue')['resolveComponent'] + const resolveDirective: typeof import('vue')['resolveDirective'] + const resolveRef: typeof import('@vueuse/core')['resolveRef'] + const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] + const shallowReactive: typeof import('vue')['shallowReactive'] + const shallowReadonly: typeof import('vue')['shallowReadonly'] + const shallowRef: typeof import('vue')['shallowRef'] + const syncRef: typeof import('@vueuse/core')['syncRef'] + const syncRefs: typeof import('@vueuse/core')['syncRefs'] + const templateRef: typeof import('@vueuse/core')['templateRef'] + const throttledRef: typeof import('@vueuse/core')['throttledRef'] + const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] + const toRaw: typeof import('vue')['toRaw'] + const toReactive: typeof import('@vueuse/core')['toReactive'] + const toRef: typeof import('vue')['toRef'] + const toRefs: typeof import('vue')['toRefs'] + const triggerRef: typeof import('vue')['triggerRef'] + const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount'] + const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] + const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted'] + const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose'] + const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted'] + const unref: typeof import('vue')['unref'] + const unrefElement: typeof import('@vueuse/core')['unrefElement'] + const until: typeof import('@vueuse/core')['until'] + const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] + const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery'] + const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter'] + const useArrayFind: typeof import('@vueuse/core')['useArrayFind'] + const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex'] + const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast'] + const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin'] + const useArrayMap: typeof import('@vueuse/core')['useArrayMap'] + const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce'] + const useArraySome: typeof import('@vueuse/core')['useArraySome'] + const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique'] + const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] + const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] + const useAttrs: typeof import('vue')['useAttrs'] + const useBase64: typeof import('@vueuse/core')['useBase64'] + const useBattery: typeof import('@vueuse/core')['useBattery'] + const useBluetooth: typeof import('@vueuse/core')['useBluetooth'] + const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints'] + const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel'] + const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] + const useCached: typeof import('@vueuse/core')['useCached'] + const useClipboard: typeof import('@vueuse/core')['useClipboard'] + const useCloned: typeof import('@vueuse/core')['useCloned'] + const useColorMode: typeof import('@vueuse/core')['useColorMode'] + const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] + const useCounter: typeof import('@vueuse/core')['useCounter'] + const useCssModule: typeof import('vue')['useCssModule'] + const useCssVar: typeof import('@vueuse/core')['useCssVar'] + const useCssVars: typeof import('vue')['useCssVars'] + const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement'] + const useCycleList: typeof import('@vueuse/core')['useCycleList'] + const useDark: typeof import('@vueuse/core')['useDark'] + const useDateFormat: typeof import('@vueuse/core')['useDateFormat'] + const useDebounce: typeof import('@vueuse/core')['useDebounce'] + const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] + const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] + const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion'] + const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation'] + const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio'] + const useDevicesList: typeof import('@vueuse/core')['useDevicesList'] + const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia'] + const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility'] + const useDraggable: typeof import('@vueuse/core')['useDraggable'] + const useDropZone: typeof import('@vueuse/core')['useDropZone'] + const useElementBounding: typeof import('@vueuse/core')['useElementBounding'] + const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint'] + const useElementHover: typeof import('@vueuse/core')['useElementHover'] + const useElementSize: typeof import('@vueuse/core')['useElementSize'] + const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility'] + const useEventBus: typeof import('@vueuse/core')['useEventBus'] + const useEventListener: typeof import('@vueuse/core')['useEventListener'] + const useEventSource: typeof import('@vueuse/core')['useEventSource'] + const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] + const useFavicon: typeof import('@vueuse/core')['useFavicon'] + const useFetch: typeof import('@vueuse/core')['useFetch'] + const useFileDialog: typeof import('@vueuse/core')['useFileDialog'] + const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess'] + const useFocus: typeof import('@vueuse/core')['useFocus'] + const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] + const useFps: typeof import('@vueuse/core')['useFps'] + const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] + const useGamepad: typeof import('@vueuse/core')['useGamepad'] + const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] + const useIdle: typeof import('@vueuse/core')['useIdle'] + const useImage: typeof import('@vueuse/core')['useImage'] + const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] + const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver'] + const useInterval: typeof import('@vueuse/core')['useInterval'] + const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] + const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] + const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] + const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] + const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] + const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory'] + const useMediaControls: typeof import('@vueuse/core')['useMediaControls'] + const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery'] + const useMemoize: typeof import('@vueuse/core')['useMemoize'] + const useMemory: typeof import('@vueuse/core')['useMemory'] + const useMounted: typeof import('@vueuse/core')['useMounted'] + const useMouse: typeof import('@vueuse/core')['useMouse'] + const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement'] + const useMousePressed: typeof import('@vueuse/core')['useMousePressed'] + const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver'] + const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage'] + const useNetwork: typeof import('@vueuse/core')['useNetwork'] + const useNow: typeof import('@vueuse/core')['useNow'] + const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl'] + const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination'] + const useOnline: typeof import('@vueuse/core')['useOnline'] + const usePageLeave: typeof import('@vueuse/core')['usePageLeave'] + const useParallax: typeof import('@vueuse/core')['useParallax'] + const usePermission: typeof import('@vueuse/core')['usePermission'] + const usePointer: typeof import('@vueuse/core')['usePointer'] + const usePointerLock: typeof import('@vueuse/core')['usePointerLock'] + const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] + const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] + const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast'] + const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] + const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] + const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] + const usePrevious: typeof import('@vueuse/core')['usePrevious'] + const useRafFn: typeof import('@vueuse/core')['useRafFn'] + const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] + const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] + const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] + const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] + const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] + const useScroll: typeof import('@vueuse/core')['useScroll'] + const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] + const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] + const useShare: typeof import('@vueuse/core')['useShare'] + const useSlots: typeof import('vue')['useSlots'] + const useSorted: typeof import('@vueuse/core')['useSorted'] + const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] + const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] + const useStepper: typeof import('@vueuse/core')['useStepper'] + const useStorage: typeof import('@vueuse/core')['useStorage'] + const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] + const useStyleTag: typeof import('@vueuse/core')['useStyleTag'] + const useSupported: typeof import('@vueuse/core')['useSupported'] + const useSwipe: typeof import('@vueuse/core')['useSwipe'] + const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] + const useTextDirection: typeof import('@vueuse/core')['useTextDirection'] + const useTextSelection: typeof import('@vueuse/core')['useTextSelection'] + const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize'] + const useThrottle: typeof import('@vueuse/core')['useThrottle'] + const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn'] + const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory'] + const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] + const useTimeout: typeof import('@vueuse/core')['useTimeout'] + const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] + const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll'] + const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] + const useTitle: typeof import('@vueuse/core')['useTitle'] + const useToNumber: typeof import('@vueuse/core')['useToNumber'] + const useToString: typeof import('@vueuse/core')['useToString'] + const useToggle: typeof import('@vueuse/core')['useToggle'] + const useTransition: typeof import('@vueuse/core')['useTransition'] + const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] + const useUserMedia: typeof import('@vueuse/core')['useUserMedia'] + const useVModel: typeof import('@vueuse/core')['useVModel'] + const useVModels: typeof import('@vueuse/core')['useVModels'] + const useVibrate: typeof import('@vueuse/core')['useVibrate'] + const useVirtualList: typeof import('@vueuse/core')['useVirtualList'] + const useWakeLock: typeof import('@vueuse/core')['useWakeLock'] + const useWebNotification: typeof import('@vueuse/core')['useWebNotification'] + const useWebSocket: typeof import('@vueuse/core')['useWebSocket'] + const useWebWorker: typeof import('@vueuse/core')['useWebWorker'] + const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn'] + const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus'] + const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll'] + const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] + const watch: typeof import('vue')['watch'] + const watchArray: typeof import('@vueuse/core')['watchArray'] + const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] + const watchDebounced: typeof import('@vueuse/core')['watchDebounced'] + const watchEffect: typeof import('vue')['watchEffect'] + const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable'] + const watchOnce: typeof import('@vueuse/core')['watchOnce'] + const watchPausable: typeof import('@vueuse/core')['watchPausable'] + const watchPostEffect: typeof import('vue')['watchPostEffect'] + const watchSyncEffect: typeof import('vue')['watchSyncEffect'] + const watchThrottled: typeof import('@vueuse/core')['watchThrottled'] + const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable'] + const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] + const whenever: typeof import('@vueuse/core')['whenever'] +} +// for vue template auto import +import { UnwrapRef } from 'vue' +declare module 'vue' { + interface ComponentCustomProperties { + readonly EffectScope: UnwrapRef + readonly ElMessage: UnwrapRef + readonly ElMessageBox: UnwrapRef + readonly NEllipsis: UnwrapRef + readonly asyncComputed: UnwrapRef + readonly autoResetRef: UnwrapRef + readonly computed: UnwrapRef + readonly computedAsync: UnwrapRef + readonly computedEager: UnwrapRef + readonly computedInject: UnwrapRef + readonly computedWithControl: UnwrapRef + readonly controlledComputed: UnwrapRef + readonly controlledRef: UnwrapRef + readonly createApp: UnwrapRef + readonly createEventHook: UnwrapRef + readonly createGlobalState: UnwrapRef + readonly createInjectionState: UnwrapRef + readonly createReactiveFn: UnwrapRef + readonly createSharedComposable: UnwrapRef + readonly createUnrefFn: UnwrapRef + readonly customRef: UnwrapRef + readonly debouncedRef: UnwrapRef + readonly debouncedWatch: UnwrapRef + readonly defineAsyncComponent: UnwrapRef + readonly defineComponent: UnwrapRef + readonly eagerComputed: UnwrapRef + readonly effectScope: UnwrapRef + readonly extendRef: UnwrapRef + readonly getCurrentInstance: UnwrapRef + readonly getCurrentScope: UnwrapRef + readonly h: UnwrapRef + readonly ignorableWatch: UnwrapRef + readonly inject: UnwrapRef + readonly isDefined: UnwrapRef + readonly isProxy: UnwrapRef + readonly isReactive: UnwrapRef + readonly isReadonly: UnwrapRef + readonly isRef: UnwrapRef + readonly makeDestructurable: UnwrapRef + readonly markRaw: UnwrapRef + readonly nextTick: UnwrapRef + readonly onActivated: UnwrapRef + readonly onBeforeMount: UnwrapRef + readonly onBeforeUnmount: UnwrapRef + readonly onBeforeUpdate: UnwrapRef + readonly onClickOutside: UnwrapRef + readonly onDeactivated: UnwrapRef + readonly onErrorCaptured: UnwrapRef + readonly onKeyStroke: UnwrapRef + readonly onLongPress: UnwrapRef + readonly onMounted: UnwrapRef + readonly onRenderTracked: UnwrapRef + readonly onRenderTriggered: UnwrapRef + readonly onScopeDispose: UnwrapRef + readonly onServerPrefetch: UnwrapRef + readonly onStartTyping: UnwrapRef + readonly onUnmounted: UnwrapRef + readonly onUpdated: UnwrapRef + readonly pausableWatch: UnwrapRef + readonly provide: UnwrapRef + readonly reactify: UnwrapRef + readonly reactifyObject: UnwrapRef + readonly reactive: UnwrapRef + readonly reactiveComputed: UnwrapRef + readonly reactiveOmit: UnwrapRef + readonly reactivePick: UnwrapRef + readonly readonly: UnwrapRef + readonly ref: UnwrapRef + readonly refAutoReset: UnwrapRef + readonly refDebounced: UnwrapRef + readonly refDefault: UnwrapRef + readonly refThrottled: UnwrapRef + readonly refWithControl: UnwrapRef + readonly resolveComponent: UnwrapRef + readonly resolveDirective: UnwrapRef + readonly resolveRef: UnwrapRef + readonly resolveUnref: UnwrapRef + readonly shallowReactive: UnwrapRef + readonly shallowReadonly: UnwrapRef + readonly shallowRef: UnwrapRef + readonly syncRef: UnwrapRef + readonly syncRefs: UnwrapRef + readonly templateRef: UnwrapRef + readonly throttledRef: UnwrapRef + readonly throttledWatch: UnwrapRef + readonly toRaw: UnwrapRef + readonly toReactive: UnwrapRef + readonly toRef: UnwrapRef + readonly toRefs: UnwrapRef + readonly triggerRef: UnwrapRef + readonly tryOnBeforeMount: UnwrapRef + readonly tryOnBeforeUnmount: UnwrapRef + readonly tryOnMounted: UnwrapRef + readonly tryOnScopeDispose: UnwrapRef + readonly tryOnUnmounted: UnwrapRef + readonly unref: UnwrapRef + readonly unrefElement: UnwrapRef + readonly until: UnwrapRef + readonly useActiveElement: UnwrapRef + readonly useArrayEvery: UnwrapRef + readonly useArrayFilter: UnwrapRef + readonly useArrayFind: UnwrapRef + readonly useArrayFindIndex: UnwrapRef + readonly useArrayFindLast: UnwrapRef + readonly useArrayJoin: UnwrapRef + readonly useArrayMap: UnwrapRef + readonly useArrayReduce: UnwrapRef + readonly useArraySome: UnwrapRef + readonly useArrayUnique: UnwrapRef + readonly useAsyncQueue: UnwrapRef + readonly useAsyncState: UnwrapRef + readonly useAttrs: UnwrapRef + readonly useBase64: UnwrapRef + readonly useBattery: UnwrapRef + readonly useBluetooth: UnwrapRef + readonly useBreakpoints: UnwrapRef + readonly useBroadcastChannel: UnwrapRef + readonly useBrowserLocation: UnwrapRef + readonly useCached: UnwrapRef + readonly useClipboard: UnwrapRef + readonly useCloned: UnwrapRef + readonly useColorMode: UnwrapRef + readonly useConfirmDialog: UnwrapRef + readonly useCounter: UnwrapRef + readonly useCssModule: UnwrapRef + readonly useCssVar: UnwrapRef + readonly useCssVars: UnwrapRef + readonly useCurrentElement: UnwrapRef + readonly useCycleList: UnwrapRef + readonly useDark: UnwrapRef + readonly useDateFormat: UnwrapRef + readonly useDebounce: UnwrapRef + readonly useDebounceFn: UnwrapRef + readonly useDebouncedRefHistory: UnwrapRef + readonly useDeviceMotion: UnwrapRef + readonly useDeviceOrientation: UnwrapRef + readonly useDevicePixelRatio: UnwrapRef + readonly useDevicesList: UnwrapRef + readonly useDisplayMedia: UnwrapRef + readonly useDocumentVisibility: UnwrapRef + readonly useDraggable: UnwrapRef + readonly useDropZone: UnwrapRef + readonly useElementBounding: UnwrapRef + readonly useElementByPoint: UnwrapRef + readonly useElementHover: UnwrapRef + readonly useElementSize: UnwrapRef + readonly useElementVisibility: UnwrapRef + readonly useEventBus: UnwrapRef + readonly useEventListener: UnwrapRef + readonly useEventSource: UnwrapRef + readonly useEyeDropper: UnwrapRef + readonly useFavicon: UnwrapRef + readonly useFetch: UnwrapRef + readonly useFileDialog: UnwrapRef + readonly useFileSystemAccess: UnwrapRef + readonly useFocus: UnwrapRef + readonly useFocusWithin: UnwrapRef + readonly useFps: UnwrapRef + readonly useFullscreen: UnwrapRef + readonly useGamepad: UnwrapRef + readonly useGeolocation: UnwrapRef + readonly useIdle: UnwrapRef + readonly useImage: UnwrapRef + readonly useInfiniteScroll: UnwrapRef + readonly useIntersectionObserver: UnwrapRef + readonly useInterval: UnwrapRef + readonly useIntervalFn: UnwrapRef + readonly useKeyModifier: UnwrapRef + readonly useLastChanged: UnwrapRef + readonly useLocalStorage: UnwrapRef + readonly useMagicKeys: UnwrapRef + readonly useManualRefHistory: UnwrapRef + readonly useMediaControls: UnwrapRef + readonly useMediaQuery: UnwrapRef + readonly useMemoize: UnwrapRef + readonly useMemory: UnwrapRef + readonly useMounted: UnwrapRef + readonly useMouse: UnwrapRef + readonly useMouseInElement: UnwrapRef + readonly useMousePressed: UnwrapRef + readonly useMutationObserver: UnwrapRef + readonly useNavigatorLanguage: UnwrapRef + readonly useNetwork: UnwrapRef + readonly useNow: UnwrapRef + readonly useObjectUrl: UnwrapRef + readonly useOffsetPagination: UnwrapRef + readonly useOnline: UnwrapRef + readonly usePageLeave: UnwrapRef + readonly useParallax: UnwrapRef + readonly usePermission: UnwrapRef + readonly usePointer: UnwrapRef + readonly usePointerLock: UnwrapRef + readonly usePointerSwipe: UnwrapRef + readonly usePreferredColorScheme: UnwrapRef + readonly usePreferredContrast: UnwrapRef + readonly usePreferredDark: UnwrapRef + readonly usePreferredLanguages: UnwrapRef + readonly usePreferredReducedMotion: UnwrapRef + readonly usePrevious: UnwrapRef + readonly useRafFn: UnwrapRef + readonly useRefHistory: UnwrapRef + readonly useResizeObserver: UnwrapRef + readonly useScreenOrientation: UnwrapRef + readonly useScreenSafeArea: UnwrapRef + readonly useScriptTag: UnwrapRef + readonly useScroll: UnwrapRef + readonly useScrollLock: UnwrapRef + readonly useSessionStorage: UnwrapRef + readonly useShare: UnwrapRef + readonly useSlots: UnwrapRef + readonly useSorted: UnwrapRef + readonly useSpeechRecognition: UnwrapRef + readonly useSpeechSynthesis: UnwrapRef + readonly useStepper: UnwrapRef + readonly useStorage: UnwrapRef + readonly useStorageAsync: UnwrapRef + readonly useStyleTag: UnwrapRef + readonly useSupported: UnwrapRef + readonly useSwipe: UnwrapRef + readonly useTemplateRefsList: UnwrapRef + readonly useTextDirection: UnwrapRef + readonly useTextSelection: UnwrapRef + readonly useTextareaAutosize: UnwrapRef + readonly useThrottle: UnwrapRef + readonly useThrottleFn: UnwrapRef + readonly useThrottledRefHistory: UnwrapRef + readonly useTimeAgo: UnwrapRef + readonly useTimeout: UnwrapRef + readonly useTimeoutFn: UnwrapRef + readonly useTimeoutPoll: UnwrapRef + readonly useTimestamp: UnwrapRef + readonly useTitle: UnwrapRef + readonly useToNumber: UnwrapRef + readonly useToString: UnwrapRef + readonly useToggle: UnwrapRef + readonly useTransition: UnwrapRef + readonly useUrlSearchParams: UnwrapRef + readonly useUserMedia: UnwrapRef + readonly useVModel: UnwrapRef + readonly useVModels: UnwrapRef + readonly useVibrate: UnwrapRef + readonly useVirtualList: UnwrapRef + readonly useWakeLock: UnwrapRef + readonly useWebNotification: UnwrapRef + readonly useWebSocket: UnwrapRef + readonly useWebWorker: UnwrapRef + readonly useWebWorkerFn: UnwrapRef + readonly useWindowFocus: UnwrapRef + readonly useWindowScroll: UnwrapRef + readonly useWindowSize: UnwrapRef + readonly watch: UnwrapRef + readonly watchArray: UnwrapRef + readonly watchAtMost: UnwrapRef + readonly watchDebounced: UnwrapRef + readonly watchEffect: UnwrapRef + readonly watchIgnorable: UnwrapRef + readonly watchOnce: UnwrapRef + readonly watchPausable: UnwrapRef + readonly watchPostEffect: UnwrapRef + readonly watchSyncEffect: UnwrapRef + readonly watchThrottled: UnwrapRef + readonly watchTriggerable: UnwrapRef + readonly watchWithFilter: UnwrapRef + readonly whenever: UnwrapRef + } +} diff --git a/src/types/components.d.ts b/src/types/components.d.ts new file mode 100644 index 0000000..41572a7 --- /dev/null +++ b/src/types/components.d.ts @@ -0,0 +1,55 @@ +// generated by unplugin-vue-components +// We suggest you to commit this file into source control +// Read more: https://github.com/vuejs/core/pull/3399 +import '@vue/runtime-core' + +export {} + +declare module '@vue/runtime-core' { + export interface GlobalComponents { + Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default'] + ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] + ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] + ElButton: typeof import('element-plus/es')['ElButton'] + ElCard: typeof import('element-plus/es')['ElCard'] + ElCol: typeof import('element-plus/es')['ElCol'] + ElDialog: typeof import('element-plus/es')['ElDialog'] + ElDivider: typeof import('element-plus/es')['ElDivider'] + ElDropdown: typeof import('element-plus/es')['ElDropdown'] + ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] + ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] + ElForm: typeof import('element-plus/es')['ElForm'] + ElFormItem: typeof import('element-plus/es')['ElFormItem'] + ElInput: typeof import('element-plus/es')['ElInput'] + ElMenu: typeof import('element-plus/es')['ElMenu'] + ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] + ElRow: typeof import('element-plus/es')['ElRow'] + ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] + ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] + ElSwitch: typeof import('element-plus/es')['ElSwitch'] + ElTooltip: typeof import('element-plus/es')['ElTooltip'] + ElTree: typeof import('element-plus/es')['ElTree'] + Hamburger: typeof import('./../components/Hamburger/index.vue')['default'] + LangSelect: typeof import('./../components/LangSelect/index.vue')['default'] + MultiUpload: typeof import('./../components/Upload/MultiUpload.vue')['default'] + NButton: typeof import('naive-ui')['NButton'] + NCard: typeof import('naive-ui')['NCard'] + NIcon: typeof import('naive-ui')['NIcon'] + NMenu: typeof import('naive-ui')['NMenu'] + NModal: typeof import('naive-ui')['NModal'] + NSpin: typeof import('naive-ui')['NSpin'] + NTooltip: typeof import('naive-ui')['NTooltip'] + Pagination: typeof import('./../components/Pagination/index.vue')['default'] + RightPanel: typeof import('./../components/RightPanel/index.vue')['default'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] + Screenfull: typeof import('./../components/Screenfull/index.vue')['default'] + Signature: typeof import('./../components/Signature/index.vue')['default'] + SingleUpload: typeof import('./../components/Upload/SingleUpload.vue')['default'] + SizeSelect: typeof import('./../components/SizeSelect/index.vue')['default'] + SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default'] + Table: typeof import('./../components/Table/index.vue')['default'] + Video: typeof import('./../components/Video/index.vue')['default'] + WangEditor: typeof import('./../components/WangEditor/index.vue')['default'] + } +} diff --git a/src/types/env.d.ts b/src/types/env.d.ts new file mode 100644 index 0000000..bcddf3e --- /dev/null +++ b/src/types/env.d.ts @@ -0,0 +1,19 @@ +/// + +declare module '*.vue' { + import { DefineComponent } from 'vue'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any>; + export default component; +} + +// 环境变量 TypeScript的智能提示 +interface ImportMetaEnv { + VITE_APP_TITLE: string; + VITE_APP_PORT: string; + VITE_APP_BASE_API: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 0000000..ad7b72b --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,55 @@ +declare global { + /** + * 分页查询参数 + */ + interface PageQuery { + pageNum: number; + pageSize: number; + } + + /** + * 分页响应对象 + */ + interface PageResult { + /** + * 数据列表 + */ + list: T; + /** + * 数据总数 + */ + total: number; + } + + /** + * 弹窗属性 + */ + interface DialogOption { + /** + * 弹窗标题 + */ + title?: string; + /** + * 是否显示 + */ + visible: boolean; + } + /** + * 组件数据源 + */ + interface OptionType { + /** + * 值 + */ + value: number; + /** + * 文本 + */ + label: string; + /** + * 子列表 + */ + children?: OptionType[]; + } +} +export {}; diff --git a/src/utils/countTo.ts b/src/utils/countTo.ts new file mode 100644 index 0000000..7893e05 --- /dev/null +++ b/src/utils/countTo.ts @@ -0,0 +1,187 @@ +import { defineComponent, reactive, computed, onMounted, watch, onUnmounted } from 'vue'; +const props = { + start: { + type: Number, + required: false, + default: 0 + }, + end: { + type: Number, + required: false, + default: 0 + }, + duration: { + type: Number, + required: false, + default: 5000 + }, + autoPlay: { + type: Boolean, + required: false, + default: true + }, + decimals: { + type: Number, + required: false, + default: 0, + validator(value: any) { + return value >= 0; + } + }, + decimal: { + type: String, + required: false, + default: '.' + }, + separator: { + type: String, + required: false, + default: ',' + }, + prefix: { + type: String, + required: false, + default: '' + }, + suffix: { + type: String, + required: false, + default: '' + }, + useEasing: { + type: Boolean, + required: false, + default: true + }, + easingFn: { + type: Function, + default(t: any, b: any, c: any, d: any) { + return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b; + } + } +}; +export default defineComponent({ + name: 'CountTo', + props: props, + emits: ['onMountedcallback', 'callback'], + setup(props, { emit }) { + const isNumber = (val: any) => { + return !isNaN(parseFloat(val)); + }; + // 格式化数据,返回想要展示的数据格式 + const formatNumber = (val: any) => { + val = val.toFixed(props.decimals); + val += ''; + const x = val.split('.'); + let x1 = x[0]; + const x2 = x.length > 1 ? props.decimal + x[1] : ''; + const rgx = /(\d+)(\d{3})/; + if (props.separator && !isNumber(props.separator)) { + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + props.separator + '$2'); + } + } + return props.prefix + x1 + x2 + props.suffix; + }; + const state = reactive<{ + localStart: number; + displayValue: number | string; + printVal: any; + paused: boolean; + localDuration: any; + startTime: any; + timestamp: any; + remaining: any; + rAF: any; + }>({ + localStart: props.start, + displayValue: formatNumber(props.start), + printVal: null, + paused: false, + localDuration: props.duration, + startTime: null, + timestamp: null, + remaining: null, + rAF: null + }); + // 定义一个计算属性,当开始数字大于结束数字时返回true + const stopCount = computed(() => { + return props.start > props.end; + }); + const startCount = () => { + state.localStart = props.start; + state.startTime = null; + state.localDuration = props.duration; + state.paused = false; + state.rAF = requestAnimationFrame(count); + }; + + watch( + () => props.start, + () => { + if (props.autoPlay) { + startCount(); + } + } + ); + + watch( + () => props.end, + () => { + if (props.autoPlay) { + startCount(); + } + } + ); + // dom挂在完成后执行一些操作 + onMounted(() => { + if (props.autoPlay) { + startCount(); + } + emit('onMountedcallback'); + }); + const count = (timestamp: any) => { + if (!state.startTime) state.startTime = timestamp; + state.timestamp = timestamp; + const progress = timestamp - state.startTime; + state.remaining = state.localDuration - progress; + // 是否使用速度变化曲线 + if (props.useEasing) { + if (stopCount.value) { + state.printVal = + state.localStart - props.easingFn(progress, 0, state.localStart - props.end, state.localDuration); + } else { + state.printVal = props.easingFn( + progress, + state.localStart, + props.end - state.localStart, + state.localDuration + ); + } + } else { + if (stopCount.value) { + state.printVal = state.localStart - (state.localStart - props.end) * (progress / state.localDuration); + } else { + state.printVal = state.localStart + (props.end - state.localStart) * (progress / state.localDuration); + } + } + if (stopCount.value) { + state.printVal = state.printVal < props.end ? props.end : state.printVal; + } else { + state.printVal = state.printVal > props.end ? props.end : state.printVal; + } + + state.displayValue = formatNumber(state.printVal); + if (progress < state.localDuration) { + state.rAF = requestAnimationFrame(count); + } else { + emit('callback'); + } + }; + // 组件销毁时取消动画 + onUnmounted(() => { + cancelAnimationFrame(state.rAF); + }); + return () => state.displayValue; + } +}); diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts new file mode 100644 index 0000000..c081ede --- /dev/null +++ b/src/utils/i18n.ts @@ -0,0 +1,12 @@ +// translate router.meta.title, be used in breadcrumb sidebar tagsview +import i18n from '@/lang/index'; + +export function translateRouteTitleI18n(title: any) { + // 判断是否存在国际化配置,如果没有原生返回 + const hasKey = i18n.global.te('route.' + title); + if (hasKey) { + const translatedTitle = i18n.global.t('route.' + title); + return translatedTitle; + } + return title; +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..9482cb6 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,273 @@ +/** + * Author: Fu Guobin + * Date: 2020/06/28 + * Last Modified by: Fu Guobin + * Last Modified time: 2023/05/29 + * Copyright:Daniel(Fu Guobin) + * Description:封装函数工具 + */ + +import { useTransition, TransitionPresets } from '@vueuse/core'; + +/** + * 校验数据类型 + * typeOf('type') + */ +export function typeOf(obj: any) { + return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase(); +} + +/** + * uuid + */ +export function uuid() { + const temp_url = URL.createObjectURL(new Blob()); + const uuid = temp_url.toString(); + URL.revokeObjectURL(temp_url); //释放这个url + return uuid.substring(uuid.lastIndexOf('/') + 1); +} + +/** + * 随机数 + */ +export function numberRandom(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +/** + * 值之间的过度 + * @param {number} num 需要过度的数值 + * @param {number} duration 过渡时间 + */ +export function transitionNum(num: number, duration: number) { + const initial = ref(0); + const initialNum = useTransition(initial, { + duration: duration, + transition: TransitionPresets.easeInOutCubic + }); + initial.value = num; + return initialNum; +} + +/** + * 判断当前设备系统类型 + * 1: ios + * 2: android + * 3: 其它 + */ +export function getOSType() { + let u = navigator.userAgent; + let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; + let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); + if (isIOS) { + return 1; + } + if (isAndroid) { + return 2; + } + return 3; +} + +/** + * 获取两个数字之间的随机数 + * @param {number} min 数字区间 + * @param {number} max 数字区间 + */ +export function random(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +/** + * 计算平均值 + * @param {number} args 数值(1, 2, 3, 4, 5) + */ +export function average(...args: any[]) { + return args.reduce((a, b) => a + b) / args.length; +} + +/** + * 计算两个日期之间天数 + * @param {Date} date 日期new Date("2023-05-18") + * @param {Date} otherDate 日期new Date("2023-07-18") + */ +export function diffDays(date: any, otherDate: any) { + return Math.ceil(Math.abs(date - otherDate) / (1000 * 60 * 60 * 24)); +} + +/** + * 确认一个数字是奇数还是偶数 + * @param {number} num 数字 + */ +export function isEven(num: number) { + return num % 2 === 0; +} + +/** + * 获取一个随机的颜色值 + */ +export function randomColor() { + return `#${Math.random().toString(16).slice(2, 8).padEnd(6, '0')}`; +} + +/** + * 将RGB颜色转换为十六进制颜色值 + * @param {number} rgb RGB(255, 255, 255) + */ +export function rgbToHex(r: number, g: number, b: number) { + return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); +} + +/** + * 将字符串转换为小驼峰 + * @param {string} str ('background-color') + */ +export function toCamelCase(str: string) { + return str.trim().replace(/[-_\s]+(.)?/g, (_: any, c: string) => (c ? c.toUpperCase() : '')); +} + +/** + * 单词首字母大写 + * @param {string} str ('hello world') + */ +export function uppercaseWords(str: string) { + return str.replace(/^(.)|\s+(.)/g, c => c.toUpperCase()); +} + +/** + * 数组去重 + * @param {array} arr 要去重的数组 + */ +export function uniqueArray(arr = []) { + return [...new Set(arr)]; +} + +/** + * 数组对象根据字段去重 + * @param {array} arr 要去重的数组 + * @param {string} key 根据去重的字段名 + */ +export function uniqueArrayObject(arr = [], key = 'id') { + if (arr.length === 0) return; + let list = []; + const map = {}; + arr.forEach(item => { + if (!map[item[key]]) { + map[item[key]] = item; + } + }); + list = Object.values(map); + return list; +} + +/** + * 数字补位 + */ +export function numberPad(source: number, length = 2) { + let pre = ''; + const negative = source < 0; + const string = String(Math.abs(source)); + if (string.length < length) { + pre = new Array(length - string.length + 1).join('0'); + } + return (negative ? '-' : '') + pre + string; +} + +/** + * 保留小数点后几位 + */ +export function cutNumber(number: number, no = 2) { + if (typeof number != 'number') { + number = Number(number); + } + return Number(number.toFixed(no)); +} + +/** + * 手机号脱敏 + * @param phone + * @returns + */ +export function hidePhone(phone: string) { + return phone.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2'); +} + +/** + * 身份证号脱敏 + * @param idCard + * @returns + */ +export function hideIdCard(idCard: string) { + return idCard.replace(/^(.{6})(?:\d+)(.{4})$/, '$1****$2'); +} + +/** + * 验证canvas画布是否为空函数 + * @param {object} canvas 画布对象 + */ +export function isCanvasBlank(canvas: { width: number; height: number; toDataURL: () => string }) { + let blank = document.createElement('canvas'); //系统获取一个空canvas对象 + blank.width = canvas.width; + blank.height = canvas.height; + return canvas.toDataURL() == blank.toDataURL(); //比较值相等则为空 +} + +/** + * 时间段 + * @param hours + * @returns + */ +export function timePeriod(hours: number) { + if (hours >= 3 && hours < 8) { + return '早安!'; + } else if (hours >= 8 && hours < 11) { + return '上午好!'; + } else if (hours >= 11 && hours < 13) { + return '中午好!'; + } else if (hours >= 13 && hours < 17) { + return '下午好!'; + } else if (hours >= 17 && hours < 23) { + return '晚上好!'; + } else if (hours >= 23 && hours < 3) { + return '晚安!'; + } +} + +/** + * Check if an element has a class + * @param {HTMLElement} elm + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele: HTMLElement, cls: string) { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); +} + +/** + * Add class to element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function addClass(ele: HTMLElement, cls: string) { + if (!hasClass(ele, cls)) ele.className += ' ' + cls; +} + +/** + * Remove class from element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function removeClass(ele: HTMLElement, cls: string) { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + ele.className = ele.className.replace(reg, ' '); + } +} + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path: string) { + const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path); + return isExternal; +} diff --git a/src/utils/lunar.js b/src/utils/lunar.js new file mode 100644 index 0000000..d143553 --- /dev/null +++ b/src/utils/lunar.js @@ -0,0 +1,1126 @@ +/** + * Author: Fu Guobin + * Date: 2020/06/28 + * Last Modified by: Fu Guobin + * Last Modified time: 2023/05/26 + * Copyright:Daniel(Fu Guobin) + * Description:农历函数封装 + */ +/** + * @1900-2100区间内的公历、农历互转 + * @charset UTF-8 + * @Author Daniel + * @Time 2022-07-26 + * @Version 1.0.3 + * @公历转农历:calendar.solarToLunar(1987,11,01); //[you can ignore params of prefix 0] + * @农历转公历:calendar.lunarToSolar(1987,09,10); //[you can ignore params of prefix 0] + */ +const calendar = { + /** + * 农历1900-2100的润大小信息表 + * @Array Of Property + * @return Hex + */ + lunarInfo: [ + 0x04bd8, + 0x04ae0, + 0x0a570, + 0x054d5, + 0x0d260, + 0x0d950, + 0x16554, + 0x056a0, + 0x09ad0, + 0x055d2, //1900-1909 + 0x04ae0, + 0x0a5b6, + 0x0a4d0, + 0x0d250, + 0x1d255, + 0x0b540, + 0x0d6a0, + 0x0ada2, + 0x095b0, + 0x14977, //1910-1919 + 0x04970, + 0x0a4b0, + 0x0b4b5, + 0x06a50, + 0x06d40, + 0x1ab54, + 0x02b60, + 0x09570, + 0x052f2, + 0x04970, //1920-1929 + 0x06566, + 0x0d4a0, + 0x0ea50, + 0x16a95, + 0x05ad0, + 0x02b60, + 0x186e3, + 0x092e0, + 0x1c8d7, + 0x0c950, //1930-1939 + 0x0d4a0, + 0x1d8a6, + 0x0b550, + 0x056a0, + 0x1a5b4, + 0x025d0, + 0x092d0, + 0x0d2b2, + 0x0a950, + 0x0b557, //1940-1949 + 0x06ca0, + 0x0b550, + 0x15355, + 0x04da0, + 0x0a5b0, + 0x14573, + 0x052b0, + 0x0a9a8, + 0x0e950, + 0x06aa0, //1950-1959 + 0x0aea6, + 0x0ab50, + 0x04b60, + 0x0aae4, + 0x0a570, + 0x05260, + 0x0f263, + 0x0d950, + 0x05b57, + 0x056a0, //1960-1969 + 0x096d0, + 0x04dd5, + 0x04ad0, + 0x0a4d0, + 0x0d4d4, + 0x0d250, + 0x0d558, + 0x0b540, + 0x0b6a0, + 0x195a6, //1970-1979 + 0x095b0, + 0x049b0, + 0x0a974, + 0x0a4b0, + 0x0b27a, + 0x06a50, + 0x06d40, + 0x0af46, + 0x0ab60, + 0x09570, //1980-1989 + 0x04af5, + 0x04970, + 0x064b0, + 0x074a3, + 0x0ea50, + 0x06b58, + 0x05ac0, + 0x0ab60, + 0x096d5, + 0x092e0, //1990-1999 + 0x0c960, + 0x0d954, + 0x0d4a0, + 0x0da50, + 0x07552, + 0x056a0, + 0x0abb7, + 0x025d0, + 0x092d0, + 0x0cab5, //2000-2009 + 0x0a950, + 0x0b4a0, + 0x0baa4, + 0x0ad50, + 0x055d9, + 0x04ba0, + 0x0a5b0, + 0x15176, + 0x052b0, + 0x0a930, //2010-2019 + 0x07954, + 0x06aa0, + 0x0ad50, + 0x05b52, + 0x04b60, + 0x0a6e6, + 0x0a4e0, + 0x0d260, + 0x0ea65, + 0x0d530, //2020-2029 + 0x05aa0, + 0x076a3, + 0x096d0, + 0x04afb, + 0x04ad0, + 0x0a4d0, + 0x1d0b6, + 0x0d250, + 0x0d520, + 0x0dd45, //2030-2039 + 0x0b5a0, + 0x056d0, + 0x055b2, + 0x049b0, + 0x0a577, + 0x0a4b0, + 0x0aa50, + 0x1b255, + 0x06d20, + 0x0ada0, //2040-2049 + /**Add By JJonline@JJonline.Cn**/ + 0x14b63, + 0x09370, + 0x049f8, + 0x04970, + 0x064b0, + 0x168a6, + 0x0ea50, + 0x06b20, + 0x1a6c4, + 0x0aae0, //2050-2059 + 0x092e0, + 0x0d2e3, + 0x0c960, + 0x0d557, + 0x0d4a0, + 0x0da50, + 0x05d55, + 0x056a0, + 0x0a6d0, + 0x055d4, //2060-2069 + 0x052d0, + 0x0a9b8, + 0x0a950, + 0x0b4a0, + 0x0b6a6, + 0x0ad50, + 0x055a0, + 0x0aba4, + 0x0a5b0, + 0x052b0, //2070-2079 + 0x0b273, + 0x06930, + 0x07337, + 0x06aa0, + 0x0ad50, + 0x14b55, + 0x04b60, + 0x0a570, + 0x054e4, + 0x0d160, //2080-2089 + 0x0e968, + 0x0d520, + 0x0daa0, + 0x16aa6, + 0x056d0, + 0x04ae0, + 0x0a9d4, + 0x0a2d0, + 0x0d150, + 0x0f252, //2090-2099 + 0x0d520 + ], //2100 + + /** + * 公历每个月份的天数普通表 + * @Array Of Property + * @return Number + */ + solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + + /** + * 天干地支之天干速查表 + * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"] + * @return Cn string + */ + Gan: [ + "\u7532", + "\u4e59", + "\u4e19", + "\u4e01", + "\u620a", + "\u5df1", + "\u5e9a", + "\u8f9b", + "\u58ec", + "\u7678" + ], + + /** + * 天干地支之地支速查表 + * @Array Of Property + * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"] + * @return Cn string + */ + Zhi: [ + "\u5b50", + "\u4e11", + "\u5bc5", + "\u536f", + "\u8fb0", + "\u5df3", + "\u5348", + "\u672a", + "\u7533", + "\u9149", + "\u620c", + "\u4ea5" + ], + + /** + * 天干地支之地支速查表<=>生肖 + * @Array Of Property + * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"] + * @return Cn string + */ + Animals: [ + "\u9f20", + "\u725b", + "\u864e", + "\u5154", + "\u9f99", + "\u86c7", + "\u9a6c", + "\u7f8a", + "\u7334", + "\u9e21", + "\u72d7", + "\u732a" + ], + + /** + * 阳历节日 + */ + festival: { + "1-1": { title: "元旦节" }, + "2-14": { title: "情人节" }, + "5-1": { title: "劳动节" }, + "5-4": { title: "青年节" }, + "6-1": { title: "儿童节" }, + "9-10": { title: "教师节" }, + "10-1": { title: "国庆节" }, + "12-25": { title: "圣诞节" }, + + "3-8": { title: "妇女节" }, + "3-12": { title: "植树节" }, + "4-1": { title: "愚人节" }, + "5-12": { title: "护士节" }, + "7-1": { title: "建党节" }, + "8-1": { title: "建军节" }, + "12-24": { title: "平安夜" } + }, + + /** + * 农历节日 + */ + lFestival: { + "12-30": { title: "除夕" }, + "1-1": { title: "春节" }, + "1-15": { title: "元宵节" }, + "2-2": { title: "龙抬头" }, + "5-5": { title: "端午节" }, + "7-7": { title: "七夕节" }, + "7-15": { title: "中元节" }, + "8-15": { title: "中秋节" }, + "9-9": { title: "重阳节" }, + "10-1": { title: "寒衣节" }, + "10-15": { title: "下元节" }, + "12-8": { title: "腊八节" }, + "12-23": { title: "北方小年" }, + "12-24": { title: "南方小年" } + }, + + /** + * 返回默认定义的阳历节日 + */ + getFestival() { + return this.festival; + }, + + /** + * 返回默认定义的内容里节日 + */ + getLunarFestival() { + return this.lFestival; + }, + + /** + * + * @param param {Object} 按照festival的格式输入数据,设置阳历节日 + */ + setFestival(param = {}) { + this.festival = param; + }, + + /** + * + * @param param {Object} 按照lFestival的格式输入数据,设置农历节日 + */ + setLunarFestival(param = {}) { + this.lFestival = param; + }, + + /** + * 24节气速查表 + * @Array Of Property + * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"] + * @return Cn string + */ + solarTerm: [ + "\u5c0f\u5bd2", + "\u5927\u5bd2", + "\u7acb\u6625", + "\u96e8\u6c34", + "\u60ca\u86f0", + "\u6625\u5206", + "\u6e05\u660e", + "\u8c37\u96e8", + "\u7acb\u590f", + "\u5c0f\u6ee1", + "\u8292\u79cd", + "\u590f\u81f3", + "\u5c0f\u6691", + "\u5927\u6691", + "\u7acb\u79cb", + "\u5904\u6691", + "\u767d\u9732", + "\u79cb\u5206", + "\u5bd2\u9732", + "\u971c\u964d", + "\u7acb\u51ac", + "\u5c0f\u96ea", + "\u5927\u96ea", + "\u51ac\u81f3" + ], + + /** + * 1900-2100各年的24节气日期速查表 + * @Array Of Property + * @return 0x string For splice + */ + sTermInfo: [ + "9778397bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf97c3598082c95f8c965cc920f", + "97bd0b06bdb0722c965ce1cfcc920f", + "b027097bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf97c359801ec95f8c965cc920f", + "97bd0b06bdb0722c965ce1cfcc920f", + "b027097bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf97c359801ec95f8c965cc920f", + "97bd0b06bdb0722c965ce1cfcc920f", + "b027097bd097c36b0b6fc9274c91aa", + "9778397bd19801ec9210c965cc920e", + "97b6b97bd19801ec95f8c965cc920f", + "97bd09801d98082c95f8e1cfcc920f", + "97bd097bd097c36b0b6fc9210c8dc2", + "9778397bd197c36c9210c9274c91aa", + "97b6b97bd19801ec95f8c965cc920e", + "97bd09801d98082c95f8e1cfcc920f", + "97bd097bd097c36b0b6fc9210c8dc2", + "9778397bd097c36c9210c9274c91aa", + "97b6b97bd19801ec95f8c965cc920e", + "97bcf97c3598082c95f8e1cfcc920f", + "97bd097bd097c36b0b6fc9210c8dc2", + "9778397bd097c36c9210c9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf97c3598082c95f8c965cc920f", + "97bd097bd097c35b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf97c3598082c95f8c965cc920f", + "97bd097bd097c35b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf97c359801ec95f8c965cc920f", + "97bd097bd097c35b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf97c359801ec95f8c965cc920f", + "97bd097bd097c35b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf97c359801ec95f8c965cc920f", + "97bd097bd07f595b0b6fc920fb0722", + "9778397bd097c36b0b6fc9210c8dc2", + "9778397bd19801ec9210c9274c920e", + "97b6b97bd19801ec95f8c965cc920f", + "97bd07f5307f595b0b0bc920fb0722", + "7f0e397bd097c36b0b6fc9210c8dc2", + "9778397bd097c36c9210c9274c920e", + "97b6b97bd19801ec95f8c965cc920f", + "97bd07f5307f595b0b0bc920fb0722", + "7f0e397bd097c36b0b6fc9210c8dc2", + "9778397bd097c36c9210c9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bd07f1487f595b0b0bc920fb0722", + "7f0e397bd097c36b0b6fc9210c8dc2", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf7f1487f595b0b0bb0b6fb0722", + "7f0e397bd097c35b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf7f1487f595b0b0bb0b6fb0722", + "7f0e397bd097c35b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf7f1487f531b0b0bb0b6fb0722", + "7f0e397bd097c35b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c965cc920e", + "97bcf7f1487f531b0b0bb0b6fb0722", + "7f0e397bd07f595b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b97bd19801ec9210c9274c920e", + "97bcf7f0e47f531b0b0bb0b6fb0722", + "7f0e397bd07f595b0b0bc920fb0722", + "9778397bd097c36b0b6fc9210c91aa", + "97b6b97bd197c36c9210c9274c920e", + "97bcf7f0e47f531b0b0bb0b6fb0722", + "7f0e397bd07f595b0b0bc920fb0722", + "9778397bd097c36b0b6fc9210c8dc2", + "9778397bd097c36c9210c9274c920e", + "97b6b7f0e47f531b0723b0b6fb0722", + "7f0e37f5307f595b0b0bc920fb0722", + "7f0e397bd097c36b0b6fc9210c8dc2", + "9778397bd097c36b0b70c9274c91aa", + "97b6b7f0e47f531b0723b0b6fb0721", + "7f0e37f1487f595b0b0bb0b6fb0722", + "7f0e397bd097c35b0b6fc9210c8dc2", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f595b0b0bb0b6fb0722", + "7f0e397bd097c35b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f531b0b0bb0b6fb0722", + "7f0e397bd097c35b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f531b0b0bb0b6fb0722", + "7f0e397bd097c35b0b6fc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f531b0b0bb0b6fb0722", + "7f0e397bd07f595b0b0bc920fb0722", + "9778397bd097c36b0b6fc9274c91aa", + "97b6b7f0e47f531b0723b0787b0721", + "7f0e27f0e47f531b0b0bb0b6fb0722", + "7f0e397bd07f595b0b0bc920fb0722", + "9778397bd097c36b0b6fc9210c91aa", + "97b6b7f0e47f149b0723b0787b0721", + "7f0e27f0e47f531b0723b0b6fb0722", + "7f0e397bd07f595b0b0bc920fb0722", + "9778397bd097c36b0b6fc9210c8dc2", + "977837f0e37f149b0723b0787b0721", + "7f07e7f0e47f531b0723b0b6fb0722", + "7f0e37f5307f595b0b0bc920fb0722", + "7f0e397bd097c35b0b6fc9210c8dc2", + "977837f0e37f14998082b0787b0721", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e37f1487f595b0b0bb0b6fb0722", + "7f0e397bd097c35b0b6fc9210c8dc2", + "977837f0e37f14998082b0787b06bd", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f531b0b0bb0b6fb0722", + "7f0e397bd097c35b0b6fc920fb0722", + "977837f0e37f14998082b0787b06bd", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f531b0b0bb0b6fb0722", + "7f0e397bd097c35b0b6fc920fb0722", + "977837f0e37f14998082b0787b06bd", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f531b0b0bb0b6fb0722", + "7f0e397bd07f595b0b0bc920fb0722", + "977837f0e37f14998082b0787b06bd", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f531b0b0bb0b6fb0722", + "7f0e397bd07f595b0b0bc920fb0722", + "977837f0e37f14998082b0787b06bd", + "7f07e7f0e47f149b0723b0787b0721", + "7f0e27f0e47f531b0b0bb0b6fb0722", + "7f0e397bd07f595b0b0bc920fb0722", + "977837f0e37f14998082b0723b06bd", + "7f07e7f0e37f149b0723b0787b0721", + "7f0e27f0e47f531b0723b0b6fb0722", + "7f0e397bd07f595b0b0bc920fb0722", + "977837f0e37f14898082b0723b02d5", + "7ec967f0e37f14998082b0787b0721", + "7f07e7f0e47f531b0723b0b6fb0722", + "7f0e37f1487f595b0b0bb0b6fb0722", + "7f0e37f0e37f14898082b0723b02d5", + "7ec967f0e37f14998082b0787b0721", + "7f07e7f0e47f531b0723b0b6fb0722", + "7f0e37f1487f531b0b0bb0b6fb0722", + "7f0e37f0e37f14898082b0723b02d5", + "7ec967f0e37f14998082b0787b06bd", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e37f1487f531b0b0bb0b6fb0722", + "7f0e37f0e37f14898082b072297c35", + "7ec967f0e37f14998082b0787b06bd", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f531b0b0bb0b6fb0722", + "7f0e37f0e37f14898082b072297c35", + "7ec967f0e37f14998082b0787b06bd", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f531b0b0bb0b6fb0722", + "7f0e37f0e366aa89801eb072297c35", + "7ec967f0e37f14998082b0787b06bd", + "7f07e7f0e47f149b0723b0787b0721", + "7f0e27f1487f531b0b0bb0b6fb0722", + "7f0e37f0e366aa89801eb072297c35", + "7ec967f0e37f14998082b0723b06bd", + "7f07e7f0e47f149b0723b0787b0721", + "7f0e27f0e47f531b0723b0b6fb0722", + "7f0e37f0e366aa89801eb072297c35", + "7ec967f0e37f14998082b0723b06bd", + "7f07e7f0e37f14998083b0787b0721", + "7f0e27f0e47f531b0723b0b6fb0722", + "7f0e37f0e366aa89801eb072297c35", + "7ec967f0e37f14898082b0723b02d5", + "7f07e7f0e37f14998082b0787b0721", + "7f07e7f0e47f531b0723b0b6fb0722", + "7f0e36665b66aa89801e9808297c35", + "665f67f0e37f14898082b0723b02d5", + "7ec967f0e37f14998082b0787b0721", + "7f07e7f0e47f531b0723b0b6fb0722", + "7f0e36665b66a449801e9808297c35", + "665f67f0e37f14898082b0723b02d5", + "7ec967f0e37f14998082b0787b06bd", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e36665b66a449801e9808297c35", + "665f67f0e37f14898082b072297c35", + "7ec967f0e37f14998082b0787b06bd", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e26665b66a449801e9808297c35", + "665f67f0e37f1489801eb072297c35", + "7ec967f0e37f14998082b0787b06bd", + "7f07e7f0e47f531b0723b0b6fb0721", + "7f0e27f1487f531b0b0bb0b6fb0722" + ], + + /** + * 数字转中文速查表 + * @Array Of Property + * @trans ['日','一','二','三','四','五','六','七','八','九','十'] + * @return Cn string + */ + nStr1: [ + "\u65e5", + "\u4e00", + "\u4e8c", + "\u4e09", + "\u56db", + "\u4e94", + "\u516d", + "\u4e03", + "\u516b", + "\u4e5d", + "\u5341" + ], + + /** + * 日期转农历称呼速查表 + * @Array Of Property + * @trans ['初','十','廿','卅'] + * @return Cn string + */ + nStr2: ["\u521d", "\u5341", "\u5eff", "\u5345"], + + /** + * 月份转农历称呼速查表 + * @Array Of Property + * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊'] + * @return Cn string + */ + nStr3: [ + "\u6b63", + "\u4e8c", + "\u4e09", + "\u56db", + "\u4e94", + "\u516d", + "\u4e03", + "\u516b", + "\u4e5d", + "\u5341", + "\u51ac", + "\u814a" + ], + + /** + * 返回农历y年一整年的总天数 + * @param y lunar Year + * @return Number + * @eg:var count = calendar.lYearDays(1987) ;//count=387 + */ + lYearDays: function (y) { + let i, + sum = 348; + for (i = 0x8000; i > 0x8; i >>= 1) { + sum += this.lunarInfo[y - 1900] & i ? 1 : 0; + } + return sum + this.leapDays(y); + }, + + /** + * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0 + * @param y lunar Year + * @return Number (0-12) + * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6 + */ + leapMonth: function (y) { + //闰字编码 \u95f0 + return this.lunarInfo[y - 1900] & 0xf; + }, + + /** + * 返回农历y年闰月的天数 若该年没有闰月则返回0 + * @param y lunar Year + * @return Number (0、29、30) + * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29 + */ + leapDays: function (y) { + if (this.leapMonth(y)) { + return this.lunarInfo[y - 1900] & 0x10000 ? 30 : 29; + } + return 0; + }, + + /** + * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法 + * @param y lunar Year + * @param m lunar Month + * @return Number (-1、29、30) + * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29 + */ + monthDays: function (y, m) { + if (m > 12 || m < 1) { + return -1; + } //月份参数从1至12,参数错误返回-1 + return this.lunarInfo[y - 1900] & (0x10000 >> m) ? 30 : 29; + }, + + /** + * 返回公历(!)y年m月的天数 + * @param y solar Year + * @param m solar Month + * @return Number (-1、28、29、30、31) + * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30 + */ + solarDays: function (y, m) { + if (m > 12 || m < 1) { + return -1; + } //若参数错误 返回-1 + const ms = m - 1; + if (ms === 1) { + //2月份的闰平规律测算后确认返回28或29 + return (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0 ? 29 : 28; + } else { + return this.solarMonth[ms]; + } + }, + + /** + * 农历年份转换为干支纪年 + * @param lYear 农历年的年份数 + * @return Cn string + */ + toGanZhiYear: function (lYear) { + let ganKey = (lYear - 3) % 10; + let zhiKey = (lYear - 3) % 12; + if (ganKey === 0) ganKey = 10; //如果余数为0则为最后一个天干 + if (zhiKey === 0) zhiKey = 12; //如果余数为0则为最后一个地支 + return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]; + }, + + /** + * 公历月、日判断所属星座 + * @param cMonth [description] + * @param cDay [description] + * @return Cn string + */ + toAstro: function (cMonth, cDay) { + const s = + "\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf"; + const arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]; + return ( + s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + "\u5ea7" + ); //座 + }, + + /** + * 传入offset偏移量返回干支 + * @param offset 相对甲子的偏移量 + * @return Cn string + */ + toGanZhi: function (offset) { + return this.Gan[offset % 10] + this.Zhi[offset % 12]; + }, + + /** + * 传入公历(!)y年获得该年第n个节气的公历日期 + * @param y y公历年(1900-2100) + * @param n n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 + * @return day Number + * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春 + */ + getTerm: function (y, n) { + if (y < 1900 || y > 2100) { + return -1; + } + if (n < 1 || n > 24) { + return -1; + } + const _table = this.sTermInfo[y - 1900]; + const _info = [ + parseInt("0x" + _table.substr(0, 5)).toString(), + parseInt("0x" + _table.substr(5, 5)).toString(), + parseInt("0x" + _table.substr(10, 5)).toString(), + parseInt("0x" + _table.substr(15, 5)).toString(), + parseInt("0x" + _table.substr(20, 5)).toString(), + parseInt("0x" + _table.substr(25, 5)).toString() + ]; + const _calcDay = [ + _info[0].substr(0, 1), + _info[0].substr(1, 2), + _info[0].substr(3, 1), + _info[0].substr(4, 2), + + _info[1].substr(0, 1), + _info[1].substr(1, 2), + _info[1].substr(3, 1), + _info[1].substr(4, 2), + + _info[2].substr(0, 1), + _info[2].substr(1, 2), + _info[2].substr(3, 1), + _info[2].substr(4, 2), + + _info[3].substr(0, 1), + _info[3].substr(1, 2), + _info[3].substr(3, 1), + _info[3].substr(4, 2), + + _info[4].substr(0, 1), + _info[4].substr(1, 2), + _info[4].substr(3, 1), + _info[4].substr(4, 2), + + _info[5].substr(0, 1), + _info[5].substr(1, 2), + _info[5].substr(3, 1), + _info[5].substr(4, 2) + ]; + return parseInt(_calcDay[n - 1]); + }, + + /** + * 传入农历数字月份返回汉语通俗表示法 + * @param m lunar month + * @return Cn string + * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月' + */ + toChinaMonth: function (m) { + // 月 => \u6708 + if (m > 12 || m < 1) { + return -1; + } //若参数错误 返回-1 + let s = this.nStr3[m - 1]; + s += "\u6708"; //加上月字 + return s; + }, + + /** + * 传入农历日期数字返回汉字表示法 + * @param d lunar day + * @return Cn string + * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一' + */ + toChinaDay: function (d) { + //日 => \u65e5 + let s; + switch (d) { + case 10: + s = "\u521d\u5341"; + break; + case 20: + s = "\u4e8c\u5341"; + break; + case 30: + s = "\u4e09\u5341"; + break; + default: + s = this.nStr2[Math.floor(d / 10)]; + s += this.nStr1[d % 10]; + } + return s; + }, + + /** + * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春” + * @param y year + * @return Cn string + * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔' + */ + getAnimal: function (y) { + return this.Animals[(y - 4) % 12]; + }, + + /** + * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON + * !important! 公历参数区间1900.1.31~2100.12.31 + * @param yPara solar year + * @param mPara solar month + * @param dPara solar day + * @return JSON object + * @eg:console.log(calendar.solarToLunar(1987,11,01)); + */ + solarToLunar: function (yPara, mPara, dPara) { + let y = parseInt(yPara); + let m = parseInt(mPara); + let d = parseInt(dPara); + //年份限定、上限 + if (y < 1900 || y > 2100) { + return -1; // undefined转换为数字变为NaN + } + //公历传参最下限 + if (y === 1900 && m === 1 && d < 31) { + return -1; + } + + //未传参 获得当天 + let objDate; + if (!y) { + objDate = new Date(); + } else { + objDate = new Date(y, parseInt(m.toString()) - 1, d); + } + let i, + leap = 0, + temp = 0; + //修正ymd参数 + y = objDate.getFullYear(); + m = objDate.getMonth() + 1; + d = objDate.getDate(); + let offset = + (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - + Date.UTC(1900, 0, 31)) / + 86400000; + for (i = 1900; i < 2101 && offset > 0; i++) { + temp = this.lYearDays(i); + offset -= temp; + } + if (offset < 0) { + offset += temp; + i--; + } + + //是否今天 + // eslint-disable-next-line prefer-const + let isTodayObj = new Date(), + isToday = false; + if ( + isTodayObj.getFullYear() === y && + isTodayObj.getMonth() + 1 === m && + isTodayObj.getDate() === d + ) { + isToday = true; + } + //星期几 + let nWeek = objDate.getDay(), + // eslint-disable-next-line prefer-const + cWeek = this.nStr1[nWeek]; + //数字表示周几顺应天朝周一开始的惯例 + if (nWeek === 0) { + nWeek = 7; + } + //农历年 + const year = i; + leap = this.leapMonth(i); //闰哪个月 + let isLeap = false; + + //效验闰月 + for (i = 1; i < 13 && offset > 0; i++) { + //闰月 + if (leap > 0 && i === leap + 1 && isLeap === false) { + --i; + isLeap = true; + temp = this.leapDays(year); //计算农历闰月天数 + } else { + temp = this.monthDays(year, i); //计算农历普通月天数 + } + //解除闰月 + if (isLeap === true && i === leap + 1) { + isLeap = false; + } + offset -= temp; + } + // 闰月导致数组下标重叠取反 + if (offset === 0 && leap > 0 && i === leap + 1) { + if (isLeap) { + isLeap = false; + } else { + isLeap = true; + --i; + } + } + if (offset < 0) { + offset += temp; + --i; + } + //农历月 + const month = i; + //农历日 + const day = offset + 1; + //天干地支处理 + const sm = m - 1; + const cnY = this.toGanZhiYear(year); + + // 当月的两个节气 + // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year` + const firstNode = this.getTerm(y, m * 2 - 1); //返回当月「节」为几日开始 + const secondNode = this.getTerm(y, m * 2); //返回当月「节」为几日开始 + + // 依据12节气修正干支月 + let cnM = this.toGanZhi((y - 1900) * 12 + m + 11); + if (d >= firstNode) { + cnM = this.toGanZhi((y - 1900) * 12 + m + 12); + } + + //传入的日期的节气与否 + let isTerm = false; + let Term = null; + if (firstNode === d) { + isTerm = true; + Term = this.solarTerm[m * 2 - 2]; + } + if (secondNode === d) { + isTerm = true; + Term = this.solarTerm[m * 2 - 1]; + } + //日柱 当月一日与 1900/1/1 相差天数 + const dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10; + const cnD = this.toGanZhi(dayCyclical + d - 1); + //该日期所属的星座 + const astro = this.toAstro(m, d); + + const solarDate = y + "-" + m + "-" + d; + const lunarDate = year + "-" + month + "-" + day; + + const festival = this.festival; + const lFestival = this.lFestival; + + const festivalDate = m + "-" + d; + const lunarFestivalDate = month + "-" + day; + + return { + date: solarDate, + lunarDate: lunarDate, + festival: festival[festivalDate] ? festival[festivalDate].title : null, + lunarFestival: lFestival[lunarFestivalDate] + ? lFestival[lunarFestivalDate].title + : null, + lYear: year, + lMonth: month, + lDay: day, + Animal: this.getAnimal(year), + IMonthCn: (isLeap ? "\u95f0" : "") + this.toChinaMonth(month), + IDayCn: this.toChinaDay(day), + cYear: y, + cMonth: m, + cDay: d, + cnYear: cnY, + cnMonth: cnM, + cnDay: cnD, + isToday: isToday, + isLeap: isLeap, + nWeek: nWeek, + ncWeek: "\u661f\u671f" + cWeek, + isTerm: isTerm, + Term: Term, + astro: astro + }; + }, + + /** + * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON + * !important! 参数区间1900.1.31~2100.12.1 + * @param y lunar year + * @param m lunar month + * @param d lunar day + * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] + * @return JSON object + * @eg:console.log(calendar.lunarToSolar(1987,9,10)); + */ + lunarToSolar: function (y, m, d, isLeapMonth) { + y = parseInt(y); + m = parseInt(m); + d = parseInt(d); + isLeapMonth = !!isLeapMonth; + // const leapOffset = 0; + const leapMonth = this.leapMonth(y); + // const leapDay = this.leapDays(y); + if (isLeapMonth && leapMonth !== m) { + return -1; + } //传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同 + if ( + (y === 2100 && m === 12 && d > 1) || + (y === 1900 && m === 1 && d < 31) + ) { + return -1; + } //超出了最大极限值 + const day = this.monthDays(y, m); + let _day = day; + //bugFix 2016-9-25 + //if month is leap, _day use leapDays method + if (isLeapMonth) { + _day = this.leapDays(y, m); + } + if (y < 1900 || y > 2100 || d > _day) { + return -1; + } //参数合法性效验 + + //计算农历的时间差 + let offset = 0; + let i; + for (i = 1900; i < y; i++) { + offset += this.lYearDays(i); + } + let leap = 0, + isAdd = false; + for (i = 1; i < m; i++) { + leap = this.leapMonth(y); + if (!isAdd) { + //处理闰月 + if (leap <= i && leap > 0) { + offset += this.leapDays(y); + isAdd = true; + } + } + offset += this.monthDays(y, i); + } + //转换闰月农历 需补充该年闰月的前一个月的时差 + if (isLeapMonth) { + offset += day; + } + //1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点) + const strap = Date.UTC(1900, 1, 30, 0, 0, 0); + const calObj = new Date((offset + d - 31) * 86400000 + strap); + const cY = calObj.getUTCFullYear(); + const cM = calObj.getUTCMonth() + 1; + const cD = calObj.getUTCDate(); + + return this.solarToLunar(cY, cM, cD); + } +}; + +export default calendar; \ No newline at end of file diff --git a/src/utils/rem.js b/src/utils/rem.js new file mode 100644 index 0000000..e4871e2 --- /dev/null +++ b/src/utils/rem.js @@ -0,0 +1,21 @@ + +/** + * Author: Fu Guobin + * Date: 2020/06/28 + * Last Modified by: Fu Guobin + * Last Modified time: 2023/08/28 + * Copyright:Daniel(Fu Guobin) + * Description:封装一个根据屏幕尺寸自动改变 html 的 font-size 大小的函数 + */ +const init = function () { + let clientWidth = + document.documentElement.clientWidth || document.body.clientWidth + // 设计图尺寸是 1920px,这样 *10 之后,1rem 就等于 10px; + const fontSize = (clientWidth / 1920 * 10) + document.documentElement.style.fontSize = fontSize + "px" +} + +init() + +window.addEventListener("resize", init) +export default init \ No newline at end of file diff --git a/src/utils/request.ts b/src/utils/request.ts new file mode 100644 index 0000000..c57486c --- /dev/null +++ b/src/utils/request.ts @@ -0,0 +1,69 @@ +/** + * Author: Fu Guobin + * Date: 2022/12/28 + * Last Modified by: Fu Guobin + * Last Modified time: 2023/05/12 + * Copyright:Daniel(Fu Guobin) + * Description:axios配置 + */ +import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios'; +import { useUserStoreHook } from '@/store/modules/user'; + +// 创建 axios 实例 +const service = axios.create({ + baseURL: import.meta.env.VITE_APP_BASE_API, + timeout: 50000, + headers: { 'Content-Type': 'application/json;charset=utf-8' } +}); + +// 请求拦截器 +service.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + const userStore = useUserStoreHook(); + if (userStore.token) { + config.headers.Authorization = userStore.token; + } + return config; + }, + (error: any) => { + return Promise.reject(error); + } +); + +// 响应拦截器 +service.interceptors.response.use( + (response: AxiosResponse) => { + const { code, msg } = response.data; + if (code === 200) { + return response.data; + } + // 响应数据为二进制流处理(Excel导出) + if (response.data instanceof ArrayBuffer) { + return response; + } + + ElMessage.error(msg || '系统出错'); + return Promise.reject(new Error(msg || 'Error')); + }, + (error: any) => { + if (error.response.data) { + const { code, msg } = error.response.data; + // token 过期,重新登录 + if (code === '401') { + ElMessageBox.confirm('当前页面已失效,请重新登录', '提示', { + confirmButtonText: '确定', + type: 'warning' + }).then(() => { + localStorage.clear(); + window.location.href = '/'; + }); + } else { + ElMessage.error(msg || '系统出错'); + } + } + return Promise.reject(error.message); + } +); + +// 导出 axios 实例 +export default service; diff --git a/src/utils/scroll-to.ts b/src/utils/scroll-to.ts new file mode 100644 index 0000000..591e3ec --- /dev/null +++ b/src/utils/scroll-to.ts @@ -0,0 +1,69 @@ +const easeInOutQuad = (t: number, b: number, c: number, d: number) => { + t /= d / 2; + if (t < 1) { + return (c / 2) * t * t + b; + } + t--; + return (-c / 2) * (t * (t - 2) - 1) + b; +}; + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +const requestAnimFrame = (function () { + return ( + window.requestAnimationFrame || + (window as any).webkitRequestAnimationFrame || + (window as any).mozRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + } + ); +})(); + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +const move = (amount: number) => { + document.documentElement.scrollTop = amount; + (document.body.parentNode as HTMLElement).scrollTop = amount; + document.body.scrollTop = amount; +}; + +const position = () => { + return ( + document.documentElement.scrollTop || + (document.body.parentNode as HTMLElement).scrollTop || + document.body.scrollTop + ); +}; + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export const scrollTo = (to: number, duration: number, callback?: any) => { + const start = position(); + const change = to - start; + const increment = 20; + let currentTime = 0; + duration = typeof duration === 'undefined' ? 500 : duration; + const animateScroll = function () { + // increment the time + currentTime += increment; + // find the value with the quadratic in-out easing function + const val = easeInOutQuad(currentTime, start, change, duration); + // move the document.body + move(val); + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll); + } else { + if (callback && typeof callback === 'function') { + // the animation is done so lets callback + callback(); + } + } + }; + animateScroll(); +}; diff --git a/src/utils/socket.ts b/src/utils/socket.ts new file mode 100644 index 0000000..5346e18 --- /dev/null +++ b/src/utils/socket.ts @@ -0,0 +1,81 @@ +/** + * Author: Fu Guobin + * Date: 2020/06/28 + * Last Modified by: Fu Guobin + * Last Modified time: 2023/08/28 + * Copyright:Daniel(Fu Guobin) + * Description:websocket函数封装 + */ +import { reactive, toRefs } from 'vue'; +import { tableStore } from '@/store/modules/table'; +import mitt from '@/plugins/bus'; +// const tableStoreCounter = tableStore(); + +class WebSocketService { + websocket: WebSocket | null; + isInitialized: boolean; + isConnected: boolean; + data: any; + + constructor() { + this.websocket = null; + this.isInitialized = false; + this.isConnected = false; + this.data = null; + } + + initialize(url: string): void { + this.websocket = new WebSocket(url); + this.websocket.onopen = this.onOpen.bind(this); + this.websocket.onclose = this.onClose.bind(this); + this.websocket.onerror = this.onError.bind(this); + this.websocket.onmessage = this.onMessage.bind(this); + this.isInitialized = true; + } + + onOpen(): void { + this.isConnected = true; + // 进行握手操作,如果需要的话 + } + + onClose(): void { + this.isConnected = false; + // 关闭WebSocket连接的处理逻辑 + } + + onError(error: Event): void { + console.error('WebSocket error:', error); + // 错误处理的逻辑 + } + + onMessage(event: MessageEvent): void { + if (event.data != '连接成功') { + const response = JSON.parse(event.data); + console.log(response); + this.data = response; + // 处理返回的数据 + mitt.emit('tableMessage', response); + // const oldData = tableStoreCounter.tableDataStore; + // const index = oldData.findIndex((obj) => obj.id === response.id); + // if (index !== -1) { + // oldData.splice(index, 1, response); + // } + // tableStoreCounter.tableDataAction(oldData); + } + } + + send(data: any): void { + console.log('websocketSend:', JSON.stringify(data)); + if (this.isConnected) { + this.websocket?.send(JSON.stringify(data)); + } + } + + close(): void { + this.websocket?.close(); + } +} + +const webSocketService = new WebSocketService(); + +export default webSocketService; diff --git a/src/utils/validate.ts b/src/utils/validate.ts new file mode 100644 index 0000000..84552f6 --- /dev/null +++ b/src/utils/validate.ts @@ -0,0 +1,61 @@ +/** + * Author: Fu Guobin + * Date: 2020/07/06 + * Last Modified by: Fu Guobin + * Last Modified time: 2023/05/09 + * Copyright:Daniel(Fu Guobin) + * Description:正则验证封装 + */ + +/** + * @param {string} nuval + * @returns {Boolean} + */ +export function validZhCn(nuval: string) { + //匹配中文及英文 + const reg = /^[\u4e00-\u9fa5A-Za-z]+$/; + return reg.test(nuval); +} + +/** + * @param {string} noval + * @returns {Boolean} + */ +export function validNoCn(noval: string) { + //匹配数字及英文 + const reg = /^[A-Za-z0-9]+$/; + return reg.test(noval); +} + +/** + * @param {string} tel + * @returns {Boolean} + */ +export function validTel(tel: string) { + //手机号正则 + // const reg = /^(((13[0-9]{1})|(14[57]{1})|(15[012356789]{1})|(17[03678]{1})|(18[0-9]{1})|(19[89]{1})|(16[6]{1}))+\d{8})$/ + const reg = /^(0[0-9]{9,11})$|([1][3,4,5,6,7,8,9][0-9]{9})$/; + return reg.test(tel); +} + +/** + * @param {string} email + * @returns {Boolean} + */ +export function validEmail(email: string) { + //邮箱正则 + const reg = + /^([a-zA-Z0-9]{1,63}|[a-zA-Z0-9][a-zA-Z0-9-_.]{0,62}[a-zA-Z0-9])@(([a-zA-Z0-9]+|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.){0,3}((com)|(net)|(org)|(gov\.cn)|(info)|(cc)|(com\.cn)|(net\.cn)|(org\.cn)|(name)|(biz)|(tv)|(cn)|(mobi)|(name)|(sh)|(ac)|(io)|(tw)|(com\.tw)|(hk)|(com\.hk)|(ws)|(travel)|(us)|(tm)|(la)|(me\.uk)|(org\.uk)|(ltd\.uk)|(plc\.uk)|(in)|(eu)|(it)|(jp))$/; + return reg.test(email); +} + +/** + * @param {string} code + * @returns {Boolean} + */ +export function validIdCode(code: string) { + //身份证正则 + const reg = + /^\d{2}(0[1-9]|[1-9][0-9])\d{2}((((1[6-9]|[2-9]\d)\d{2})(0[13578]|1[02])(0[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})(0[13456789]|1[012])(0[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})02(0[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))0229))\d{3}(\d|x|X)$/; + return reg.test(code); +} diff --git a/src/views/dashboard/components/BarChart.vue b/src/views/dashboard/components/BarChart.vue new file mode 100644 index 0000000..79db9ea --- /dev/null +++ b/src/views/dashboard/components/BarChart.vue @@ -0,0 +1,81 @@ + + + + diff --git a/src/views/dashboard/components/LineChart.vue b/src/views/dashboard/components/LineChart.vue new file mode 100644 index 0000000..2f6759a --- /dev/null +++ b/src/views/dashboard/components/LineChart.vue @@ -0,0 +1,72 @@ + + + + diff --git a/src/views/dashboard/components/PieChart.vue b/src/views/dashboard/components/PieChart.vue new file mode 100644 index 0000000..eb45a6d --- /dev/null +++ b/src/views/dashboard/components/PieChart.vue @@ -0,0 +1,85 @@ + + + + diff --git a/src/views/dashboard/index.scss b/src/views/dashboard/index.scss new file mode 100644 index 0000000..a30259e --- /dev/null +++ b/src/views/dashboard/index.scss @@ -0,0 +1,78 @@ +.dashboardContainer { + position: relative; + + .userInfo { + margin-bottom: 15px; + .userName { + img { + width: 40px; + height: 40px; + border-radius: 50%; + margin-right: 10px; + } + .info { + .time { + color: #9ca3af; + font-size: 12px; + } + } + } + .userNum { + .title { + color: #9ca3af; + } + span { + font-size: 22px; + } + } + // .currentTime{ + // span:last-child{ + // font-size: 13px; + // } + // } + } + .dataCard { + margin-bottom: 15px; + .item { + height: 100px; + padding: 16px; + border-radius: 8px; + color: #fff; + h3{ + font-size: 18px; + margin: 0; + } + .data{ + display: flex; + justify-content: space-between; + margin-top: 10px; + .iconfont{ + font-size: 22px; + } + span{ + font-size: 28px; + } + } + } + // .el-col:nth-child(1) { + // .item { + // background-image: linear-gradient(to right bottom, #ec4786, #b955a4); + // } + // } + // .el-col:nth-child(2) { + // .item { + // background-image: linear-gradient(to right bottom, #865ec0, #5144b4); + // } + // } + // .el-col:nth-child(3) { + // .item { + // background-image: linear-gradient(to right bottom, #56cdf3, #719de3); + // } + // } + // .el-col:nth-child(4) { + // .item { + // background-image: linear-gradient(to right bottom, #fcbc25, #f68057); + // } + // } + } +} diff --git a/src/views/dashboard/index.vue b/src/views/dashboard/index.vue new file mode 100644 index 0000000..11e718d --- /dev/null +++ b/src/views/dashboard/index.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/src/views/error-page/401.vue b/src/views/error-page/401.vue new file mode 100644 index 0000000..59b6f1a --- /dev/null +++ b/src/views/error-page/401.vue @@ -0,0 +1,114 @@ + + + + + + + + diff --git a/src/views/error-page/404.vue b/src/views/error-page/404.vue new file mode 100644 index 0000000..8559404 --- /dev/null +++ b/src/views/error-page/404.vue @@ -0,0 +1,280 @@ + + + + + + + + diff --git a/src/views/login/index.scss b/src/views/login/index.scss new file mode 100644 index 0000000..0bc3928 --- /dev/null +++ b/src/views/login/index.scss @@ -0,0 +1,103 @@ +.loginContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + background: url('../../assets/images/login-bg.png'); + background-size: cover; + overflow: hidden; + + .loginCard { + display: flex; + flex-direction: column; + width: 420px; + height: auto; + padding: 20px 30px; + border-radius: 20px; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(30px); + border: 2px solid rgba(255, 255, 255, 0.1); + box-shadow: 0 0 80px rgba(0, 0, 0, 0.25); + overflow: hidden; + + .loginForm { + width: 100%; + + .title { + padding: 1rem 0; + + span { + font-size: 1.5rem; + // color: #fff; + } + } + + .el-form-item { + border: 1px solid #dcdfe6; + border-radius: 5px; + background-color: #fff; + + .icon { + padding: 0 0.5rem; + + .iconfont { + color: #8a8a8a; + } + } + + :deep(.el-form-item__error) { + padding-top: 5px; + } + + .el-input { + :deep(.el-input__wrapper) { + padding: 0; + box-shadow: none; + border-radius: 0; + + .el-input__suffix { + padding-right: 0.5rem; + } + + .el-input__inner { + border: 0px; + border-radius: 0px; + + &:-webkit-autofill { + box-shadow: 0 0 0 1000px transparent inset !important; + -webkit-text-fill-color: #fff !important; + } + + // 设置输入框自动填充的延迟属性 + &:-webkit-autofill, + &:-webkit-autofill:hover, + &:-webkit-autofill:focus, + &:-webkit-autofill:active { + -webkit-transition-delay: 99999s; + -webkit-transition: color 99999s ease-out, + background-color 99999s ease-out; + } + } + } + } + .captcha{ + line-height: normal; + } + } + } + } + .copyright{ + position: fixed; + bottom: 0; + width: 100%; + height: 50px; + line-height: 50px; + text-align: center; + color: #9ca3af; + font-family: Arial; + font-size: 12px; + letter-spacing: 1px; + } +} diff --git a/src/views/login/index.vue b/src/views/login/index.vue new file mode 100644 index 0000000..1a2d4d9 --- /dev/null +++ b/src/views/login/index.vue @@ -0,0 +1,155 @@ + + + + + diff --git a/src/views/monitoring/devicemanage/components/main.vue b/src/views/monitoring/devicemanage/components/main.vue new file mode 100644 index 0000000..863fb2d --- /dev/null +++ b/src/views/monitoring/devicemanage/components/main.vue @@ -0,0 +1,307 @@ + + \ No newline at end of file diff --git a/src/views/monitoring/devicemanage/components/menu.vue b/src/views/monitoring/devicemanage/components/menu.vue new file mode 100644 index 0000000..c2c3f7e --- /dev/null +++ b/src/views/monitoring/devicemanage/components/menu.vue @@ -0,0 +1,134 @@ + + + + diff --git a/src/views/monitoring/devicemanage/index.scss b/src/views/monitoring/devicemanage/index.scss new file mode 100644 index 0000000..ab505dc --- /dev/null +++ b/src/views/monitoring/devicemanage/index.scss @@ -0,0 +1,520 @@ +.devicemanage { + width: 100%; + height: 100vh; + background: -webkit-linear-gradient(top, #020e38 0%, #04195b 100%); + background-size: 100% 100%; + padding: 20px; + overflow: hidden; + + .title { + display: flex; + justify-content: space-between; + align-items: center; + height: 5.7rem; + + h3 { + font-size: 3.8rem; + font-weight: 800; + color: #84e0f7; + } + } + + .seeting { + .tooltips { + color: #606266; + border: 2px solid #1e60a6; + box-shadow: inset 0px 0px 10px 0px rgb(36, 90, 124); + background-color: transparent; + + :deep(span) { + color: #5beff9; + } + + margin-left: 10px; + } + } + + .layout { + display: flex; + margin-top: 20px; + + .sidebar { + width: 12%; + height: calc(100vh - 5.7rem - 60px); + flex-shrink: 0; + border: 2px solid #1e60a6; + padding: 10px; + box-shadow: inset 0px 0px 10px 0px rgb(36, 90, 12); + margin-right: 1%; + overflow: scroll; + + .menu { + text-align: center; + border-right: none; + + .el-sub-menu { + span { + font-size: 2.2rem; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + .el-menu-item { + span { + font-size: 1.4rem; + } + } + } + + .el-sub-menu:hover, + .el-menu-item:hover { + background-color: transparent !important; + } + } + } + + .main { + width: 87%; + border: 2px solid #1e60a6; + box-shadow: inset 0px 0px 10px 0px rgb(36, 90, 124); + + :deep(.devicePrt) { + .deviceImg { + position: relative; + + .deviceImg { + width: 100%; + } + + .card { + position: absolute; + display: inline-block; + color: #87e8de; + font-size: 1.4rem; + padding: 10px 20px; + border-radius: 10px; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.1); + box-shadow: 0 0 80px rgba(0, 0, 0, 0.25); + z-index: 99; + overflow: hidden; + + .dataInfo { + padding: 5px 0; + + span { + display: block; + } + } + .dataFlex{ + display: flex; + .flexItem{ + margin-right: 15px; + } + .flexItem:last-child{ + margin-right: 0; + } + } + } + + // .dataInfo { + // position: absolute; + // display: inline-block; + // color: #87e8de; + // padding: 10px; + // font-size: 16px; + // // border: 2px solid #39c33c; + // z-index: 99; + + // span { + // display: block; + // } + // } + } + + .deviceImgA { + .card_a { + top: 40%; + left: 4%; + } + .card_b { + top: 5%; + left: 3%; + } + .card_c { + top: 9%; + right: 3%; + } + .card_f { + top: 52%; + right: 6%; + } + } + .deviceImgB { + .card_a { + top: 42%; + left: 8%; + } + .card_b { + top: 10%; + left: 9%; + } + .card_c { + top: 1%; + right: 21%; + } + .card_d { + top: 32%; + right: 18%; + } + .card_f { + top: 62%; + right: 11%; + } + } + .deviceImgC { + .card_a { + top: 1%; + left: 9%; + } + .card_b { + top: 55%; + left: 6%; + } + .card_c { + top: 1%; + right: 22%; + } + .card_d { + top: 32%; + right: 20%; + } + .card_e { + top: 36%; + right: 4%; + } + .card_f { + top: 68%; + right: 13%; + } + } + + // .deviceImgA { + // .dataInfo0 { + // top: 7%; + // left: 6%; + // } + + // .dataInfo1 { + // top: 35%; + // left: 9%; + // } + + // .dataInfo2 { + // top: 0%; + // left: 18%; + // } + + // .dataInfo3 { + // top: 0%; + // left: 25%; + // } + + // .dataInfo4 { + // top: 15%; + // left: 17.5%; + // } + + // .dataInfo5 { + // top: 26%; + // left: 17.5%; + // } + + // .dataInfo6 { + // top: 0%; + // right: 20%; + // } + + // .dataInfo7 { + // top: 10%; + // right: 20%; + // } + // } + // .deviceImgB { + // .dataInfo0 { + // top: 23%; + // left: 10.5%; + // } + + // .dataInfo1 { + // top: 55%; + // left: 12%; + // } + + // .dataInfo2 { + // top: 0%; + // left: 23%; + // } + + // .dataInfo3 { + // top: 0%; + // left: 30%; + // } + + // .dataInfo4 { + // top: 10%; + // left: 23%; + // } + + // .dataInfo5 { + // top: 20%; + // left: 23%; + // } + + // .dataInfo6 { + // top: 0%; + // right: 36%; + // } + + // .dataInfo7 { + // top: 10%; + // right: 36%; + // } + // .dataInfo8 { + // top: 26%; + // right: 31%; + // } + + // .dataInfo9 { + // top: 36%; + // right: 31%; + // } + // } + // .deviceImgC { + // .dataInfo0 { + // bottom: 38%; + // left: 8.5%; + // } + + // .dataInfo1 { + // bottom: 1%; + // left: 11%; + // } + + // .dataInfo2 { + // top: 0%; + // left: 23%; + // } + + // .dataInfo3 { + // top: 0%; + // left: 30%; + // } + + // .dataInfo4 { + // top: 10%; + // left: 23%; + // } + + // .dataInfo5 { + // top: 20%; + // left: 23%; + // } + + // .dataInfo6 { + // top: 0%; + // right: 36%; + // } + + // .dataInfo7 { + // top: 0%; + // right: 29%; + // } + // .dataInfo8 { + // top: 22%; + // right: 36%; + // } + + // .dataInfo9 { + // top: 22%; + // right: 29%; + // } + // .dataInfo10 { + // top: 47%; + // right: 36%; + // } + + // .dataInfo11 { + // top: 47%; + // right: 29%; + // } + // } + + .devicInfo { + display: flex; + justify-content: space-between; + padding: 10px 20px; + margin: 3% 20px 0 20px; + // border: 2px solid #292a48; + + .l_green { + color: #39c33c; + } + + .l_blue { + color: #41a5d7; + } + + .orange { + color: #c15757; + } + + .title { + font-size: 16px; + } + + .valves { + display: flex; + align-items: center; + flex-shrink: 0; + width: 40%; + + .title { + writing-mode: vertical-rl; + // transform: rotate(180deg); + } + + .info { + .select { + font-weight: 800; + + .n-button { + margin-right: 5px; + background: linear-gradient(#3370cb, #162f68, #162f68); + } + } + + .table { + border: 1px solid #39c33c; + border-radius: 3px; + margin: 10px 0 10px 20px; + + .item { + display: flex; + align-items: center; + padding: 10px 20px; + line-height: 26px; + border-bottom: 1px solid #39c33c; + + .lable { + font-size: 16px; + flex-shrink: 0; + } + + .parameters { + display: flex; + flex-wrap: wrap; + + .name { + color: #c15757; + margin-left: 15px; + } + + .name:nth-child(even) { + color: #41a5d7; + } + + .n-radio__label { + color: #41a5d7; + } + } + } + + .item:last-child { + border-bottom: none; + } + } + + .switch { + display: flex; + + span { + display: flex; + align-items: center; + margin-right: 5px; + } + } + } + } + + .pump { + width: 55%; + padding: 5px 10px; + border: 1px solid #39c33c; + + .item { + line-height: 28px; + + .info { + display: flex; + flex-wrap: wrap; + + .name { + color: #c15757; + margin-left: 15px; + } + + .name:nth-child(even) { + color: #41a5d7; + } + } + } + } + } + } + } + + /*滚动条整体部分*/ + .sidebar::-webkit-scrollbar, + .tableGrid ::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + /*滚动条的轨道*/ + .sidebar::-webkit-scrollbar-track, + .tableGrid ::-webkit-scrollbar-track { + background-color: transparent; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + border-radius: 8px; + } + + /*滚动条里面的小方块,能向上向下移动*/ + .sidebar::-webkit-scrollbar-thumb, + .tableGrid ::-webkit-scrollbar-thumb { + background-color: rgb(147, 147, 153, 0.5); + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + border-radius: 8px; + } + + .sidebar::-webkit-scrollbar-thumb:hover, + .tableGrid ::-webkit-scrollbar-thumb:hover { + background-color: #a8a8a8; + } + + .sidebar::-webkit-scrollbar-thumb:active, + .tableGrid :-webkit-scrollbar-thumb:active { + background-color: #787878; + } + + /*边角,即两个滚动条的交汇处*/ + .sidebar::-webkit-scrollbar-corner, + .tableGrid ::-webkit-scrollbar-corner { + background-color: transparent; + } + } +} \ No newline at end of file diff --git a/src/views/monitoring/devicemanage/index.vue b/src/views/monitoring/devicemanage/index.vue new file mode 100644 index 0000000..8f5a8ee --- /dev/null +++ b/src/views/monitoring/devicemanage/index.vue @@ -0,0 +1,65 @@ + + + diff --git a/src/views/monitoring/screen/components/main.vue b/src/views/monitoring/screen/components/main.vue new file mode 100644 index 0000000..fb532a6 --- /dev/null +++ b/src/views/monitoring/screen/components/main.vue @@ -0,0 +1,325 @@ + + + diff --git a/src/views/monitoring/screen/components/menu.vue b/src/views/monitoring/screen/components/menu.vue new file mode 100644 index 0000000..117bebc --- /dev/null +++ b/src/views/monitoring/screen/components/menu.vue @@ -0,0 +1,76 @@ + + + + diff --git a/src/views/monitoring/screen/components/showTree.vue b/src/views/monitoring/screen/components/showTree.vue new file mode 100644 index 0000000..d1824c3 --- /dev/null +++ b/src/views/monitoring/screen/components/showTree.vue @@ -0,0 +1,292 @@ + + + + diff --git a/src/views/monitoring/screen/index.scss b/src/views/monitoring/screen/index.scss new file mode 100644 index 0000000..d8178c5 --- /dev/null +++ b/src/views/monitoring/screen/index.scss @@ -0,0 +1,325 @@ +.screen { + width: 100%; + height: 100vh; + background: url(@/assets/images/screen.jpg); + background-size: 100% 100%; + padding: 20px; + overflow: hidden; + + .title { + display: flex; + justify-content: space-between; + align-items: center; + height: 5.7rem; + + h3 { + font-size: 3.8rem; + font-weight: 800; + color: #84e0f7; + } + } + + .seeting { + .tooltips { + color: #606266; + border: 2px solid #1e60a6; + box-shadow: inset 0px 0px 10px 0px rgb(36, 90, 124); + background-color: transparent; + + :deep(span) { + color: #5beff9; + } + + margin-left: 10px; + } + } + + .layout { + display: flex; + margin-top: 20px; + + .sidebar { + width: 12%; + height: calc(100vh - 5.7rem - 60px); + flex-shrink: 0; + border: 2px solid #1e60a6; + padding: 10px; + box-shadow: inset 0px 0px 10px 0px rgb(36, 90, 12); + margin-right: 1vw; + overflow: scroll; + + .menu { + text-align: center; + + :deep(.n-submenu) { + --n-item-color-hover: auto; + + .n-menu-item { + .n-menu-item-content { + padding-left: 18px !important; + } + + .n-menu-item-content-header { + font-size: 2.2rem; + color: #84e0f7; + } + } + + .n-submenu-children { + .n-menu-item-content-header { + font-size: 1.4rem; + } + + .n-menu-item-content--selected { + .n-menu-item-content-header { + color: #ffd04b; + } + } + } + + .n-base-icon { + color: #84e0f7; + } + } + } + } + + .main { + width: 87%; + + .headerInfo { + border: 2px solid #1e60a6; + box-shadow: inset 0px 0px 10px 0px rgb(36, 90, 124); + margin-bottom: 10px; + + .header { + display: flex; + align-items: center; + flex-wrap: wrap; + + .headerItem { + width: 11.1%; + color: #fff; + text-align: center; + + .name { + font-size: 12px; + line-height: 36px; + padding: 0 3rem; + color: #ffffffb3; + background-color: #123f7580; + border-top: 1px solid #265a89; + border-bottom: 1px solid #265a89; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + .value { + font-size: 18px; + color: #5beff9; + line-height: 36px; + } + } + } + } + + .mainTable { + // height: calc(100vh - 8rem - 202px); + // height: calc(100% - 162px); + overflow: hidden; + + .tableGrid { + --vxe-table-header-background-color: none; + --vxe-table-body-background-color: none; + --vxe-table-footer-background-color: none; + --vxe-table-border-color: none; + --vxe-font-color: #5beff9; + --vxe-table-header-font-color: #ffffffb3; + border: 2px solid #1e60a6; + box-shadow: inset 0px 0px 10px 0px rgb(36, 90, 124); + + :deep(.vxe-table) { + + // height: calc(100vh - 5.7rem - 60px) !important; + .vxe-table--body-wrapper { + // height: calc(100vh - 5.7rem - 182px) !important; + } + + .headerRowClass { + .headerCellClass { + border: 1px solid rgba(255, 255, 255, 0.1) !important; + border-right: none !important; + } + + .headerCellClass.col--fixed { + // background-color: #020e38; + // box-shadow: inset 0px 0px 10px 0px #245a7c; + } + } + + .tableRowClass { + .tableCellClass { + border-bottom: 1px solid #265a89 !important; + + .vxe-cell--html { + display: flex; + justify-content: center; + align-items: center; + + .warning{ + font-size: 15px; + font-weight: bold; + color: red; + } + + .iconfont { + font-size: 18px; + } + + .iconfont.icon-rise { + color: green; + } + + .iconfont.icon-decline { + color: red; + } + } + } + } + + .tips { + display: inline-block; + width: 10px; + height: 10px; + margin: 0 auto; + border-radius: 10px; + margin-right: 5px; + } + + .tips.green { + background-color: green; + } + + .tips.red { + background-color: red; + } + + .cellName { + display: flex; + justify-content: center; + align-items: center; + color: #fef961; + } + + .cellInput { + input { + color: #222; + text-align: center; + } + } + + .vxe-table--fixed-left-wrapper.scrolling--middle { + background: -webkit-linear-gradient(top, #020e38 0%, #04195b 100%); + box-shadow: inset 0px 0px 10px rgb(36, 90, 124) !important; + } + } + } + + // /*滚动条整体部分*/ + // .tableGrid ::-webkit-scrollbar { + // width: 8px; + // height: 8px; + // } + // /*滚动条的轨道*/ + // .tableGrid ::-webkit-scrollbar-track { + // background-color: transparent; + // -webkit-border-radius: 8px; + // -moz-border-radius: 8px; + // border-radius: 8px; + // } + // /*滚动条里面的小方块,能向上向下移动*/ + // .tableGrid ::-webkit-scrollbar-thumb { + // background-color: rgb(147, 147, 153, 0.5); + // -webkit-border-radius: 8px; + // -moz-border-radius: 8px; + // border-radius: 8px; + // } + // .tableGrid ::-webkit-scrollbar-thumb:hover { + // background-color: #a8a8a8; + // } + // .tableGrid ::-webkit-scrollbar-thumb:active { + // background-color: #787878; + // } + // /*边角,即两个滚动条的交汇处*/ + // .tableGrid ::-webkit-scrollbar-corner { + // background-color: transparent; + // } + } + } + + /*滚动条整体部分*/ + .sidebar::-webkit-scrollbar, + .tableGrid ::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + /*滚动条的轨道*/ + .sidebar::-webkit-scrollbar-track, + .tableGrid ::-webkit-scrollbar-track { + background-color: transparent; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + border-radius: 8px; + } + + /*滚动条里面的小方块,能向上向下移动*/ + .sidebar::-webkit-scrollbar-thumb, + .tableGrid ::-webkit-scrollbar-thumb { + background-color: rgb(147, 147, 153, 0.5); + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + border-radius: 8px; + } + + .sidebar::-webkit-scrollbar-thumb:hover, + .tableGrid ::-webkit-scrollbar-thumb:hover { + background-color: #a8a8a8; + } + + .sidebar::-webkit-scrollbar-thumb:active, + .tableGrid :-webkit-scrollbar-thumb:active { + background-color: #787878; + } + + /*边角,即两个滚动条的交汇处*/ + .sidebar::-webkit-scrollbar-corner, + .tableGrid ::-webkit-scrollbar-corner { + background-color: transparent; + } + } +} + +.cardClass { + .showTree { + .weight { + display: flex; + align-items: center; + margin-left: auto; + + span { + margin-right: 2px; + } + } + } + + .treeBtn { + margin-top: 20px; + text-align: right; + + .n-button { + margin-left: 10px; + } + } +} \ No newline at end of file diff --git a/src/views/monitoring/screen/index.vue b/src/views/monitoring/screen/index.vue new file mode 100644 index 0000000..df1f3dd --- /dev/null +++ b/src/views/monitoring/screen/index.vue @@ -0,0 +1,131 @@ + + + diff --git a/src/views/redirect/index.vue b/src/views/redirect/index.vue new file mode 100644 index 0000000..47cad96 --- /dev/null +++ b/src/views/redirect/index.vue @@ -0,0 +1,15 @@ + + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..58824ee --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "esnext", + "useDefineForClassFields": true, + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "jsx": "preserve", + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "lib": ["esnext", "dom"], + "baseUrl": ".", + "allowJs": true, + "paths": { + "@/*": ["src/*"] + }, + "outDir": "dist", + "types": ["vite/client", "element-plus/global","unplugin-icons/types/vue"], + "skipLibCheck": true /* Skip type checking all .d.ts files. */, + "allowSyntheticDefaultImports": true /* 允许默认导入 */, + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + }, + "include": ["src/**/*.ts", "src/**/*.vue", "src/types/**/*.d.ts"], + "exclude": ["node_modules", "dist", "**/*.js"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..b1b3632 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,201 @@ +/** + * Author: Fu Guobin + * Date: 2022/12/28 + * Last Modified by: Fu Guobin + * Last Modified time: 2023/08/28 + * Copyright:Daniel(Fu Guobin) + * Description:vite配置 + */ +import vue from '@vitejs/plugin-vue'; +import { UserConfig, ConfigEnv, loadEnv, defineConfig } from 'vite'; +import AutoImport from 'unplugin-auto-import/vite'; +import Components from 'unplugin-vue-components/vite'; +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; +import { NaiveUiResolver } from 'unplugin-vue-components/resolvers' +import { createSvgIconsPlugin } from "vite-plugin-svg-icons"; +import Icons from 'unplugin-icons/vite'; +import IconsResolver from 'unplugin-icons/resolver'; +import path from 'path'; +const pathSrc = path.resolve(__dirname, 'src'); + +export default defineConfig(({ mode }: ConfigEnv): UserConfig => { + const env = loadEnv(mode, process.cwd()); + return { + resolve: { + alias: { + '@': pathSrc, + 'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js' + } + }, + //静态资源处理 + assetsInclude: "", + //控制台输出的级别 info 、warn、error、silent + logLevel: "info", + // 设为false 可以避免 vite 清屏而错过在终端中打印某些关键信息 + clearScreen: true, + //本地运行配置,以及反向代理配置 + server: { + host: "localhost", + https: false,//是否启用 http 2 + cors: true,//为开发服务器配置 CORS , 默认启用并允许任何源 + open: true,//服务启动时自动在浏览器中打开应用 + port: Number(env.VITE_APP_PORT), + strictPort: false, //设为true时端口被占用则直接退出,不会尝试下一个可用端口 + force: false,//是否强制依赖预构建 + hmr: true,//配置HMR + proxy: { + [env.VITE_APP_BASE_API]: { + target: 'http://172.1.2.29:9000/',//本地接口地址 + // target: 'http://10.10.10.56:9000/',//线上接口地址 + changeOrigin: true, + rewrite: path => + path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '') + } + } + }, + plugins: [ + vue(), + AutoImport({ + // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等 + imports: ['vue', '@vueuse/core'], + eslintrc: { + enabled: false, // Default `false` + filepath: './.eslintrc-auto-import.json', // Default `./.eslintrc-auto-import.json` + globalsPropValue: true // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable') + }, + resolvers: [ + // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式) + ElementPlusResolver(), + NaiveUiResolver(), + // 自动导入图标组件 + IconsResolver({}) + ], + vueTemplate: true, // 是否在 vue 模板中自动导入 + dts: path.resolve(pathSrc, 'types', 'auto-imports.d.ts') // (false) 配置文件生成位置,默认是根目录 /auto-imports.d.ts + }), + + Components({ + resolvers: [ + // 自动注册图标组件 + IconsResolver({ + enabledCollections: ['ep'] //@iconify-json/ep 是 Element Plus 的图标库 + }), + // 自动导入 Element Plus 组件 + ElementPlusResolver(), + NaiveUiResolver() + ], + dts: path.resolve(pathSrc, 'types', 'components.d.ts') // (false) 配置文件生成位置,默认是根目录 /components.d.ts + }), + + Icons({ + // 自动安装图标库 + autoInstall: true + }), + + createSvgIconsPlugin({ + // 指定需要缓存的图标文件夹 + iconDirs: [path.resolve(pathSrc, "assets/icons")], + // 指定symbolId格式 + symbolId: "icon-[dir]-[name]", + }), + ], + css: { + // CSS 预处理器 + preprocessorOptions: { + //define global scss variable + scss: { + javascriptEnabled: true, + additionalData: ` + @use "@/styles/variables.scss" as *; + ` + } + } + }, + //打包配置 + build: { + //浏览器兼容性 "esnext"|"modules" + target: "modules", + //指定输出路径 + outDir: "dist", + //生成静态资源的存放路径 + assetsDir: "assets", + //小于此阈值的导入或引用资源将内联为 base64 编码,以避免额外的 http 请求。设置为 0 可以完全禁用此项 + assetsInlineLimit: 4096, + //启用/禁用 CSS 代码拆分 + cssCodeSplit: true, + //构建后是否生成 source map 文件 + sourcemap: false, + //自定义底层的 Rollup 打包配置 + rollupOptions: { + }, + //@rollup/plugin-commonjs 插件的选项 + commonjsOptions: { + }, + //当设置为 true,构建后将会生成 manifest.json 文件 + manifest: false, + // 设置为 false 可以禁用最小化混淆, + // 或是用来指定使用哪种混淆器 + // boolean | 'terser' | 'esbuild' + minify: "terser", //terser 构建后文件体积更小 + //传递给 Terser 的更多 minify 选项。 + terserOptions: { + }, + //设置为 false 来禁用将构建后的文件写入磁盘 + write: true, + //默认情况下,若 outDir 在 root 目录下,则 Vite 会在构建时清空该目录。 + emptyOutDir: true, + //chunk 大小警告的限制 + chunkSizeWarningLimit: 500 + }, + optimizeDeps: { + include: [ + 'vue', + 'vue-router', + 'pinia', + 'axios', + 'element-plus/es/components/form/style/css', + 'element-plus/es/components/form-item/style/css', + 'element-plus/es/components/button/style/css', + 'element-plus/es/components/input/style/css', + 'element-plus/es/components/input-number/style/css', + 'element-plus/es/components/switch/style/css', + 'element-plus/es/components/upload/style/css', + 'element-plus/es/components/menu/style/css', + 'element-plus/es/components/col/style/css', + 'element-plus/es/components/icon/style/css', + 'element-plus/es/components/row/style/css', + 'element-plus/es/components/tag/style/css', + 'element-plus/es/components/dialog/style/css', + 'element-plus/es/components/loading/style/css', + 'element-plus/es/components/radio/style/css', + 'element-plus/es/components/radio-group/style/css', + 'element-plus/es/components/popover/style/css', + 'element-plus/es/components/scrollbar/style/css', + 'element-plus/es/components/tooltip/style/css', + 'element-plus/es/components/dropdown/style/css', + 'element-plus/es/components/dropdown-menu/style/css', + 'element-plus/es/components/dropdown-item/style/css', + 'element-plus/es/components/sub-menu/style/css', + 'element-plus/es/components/menu-item/style/css', + 'element-plus/es/components/divider/style/css', + 'element-plus/es/components/card/style/css', + 'element-plus/es/components/link/style/css', + 'element-plus/es/components/breadcrumb/style/css', + 'element-plus/es/components/breadcrumb-item/style/css', + 'element-plus/es/components/table/style/css', + 'element-plus/es/components/tree-select/style/css', + 'element-plus/es/components/table-column/style/css', + 'element-plus/es/components/select/style/css', + 'element-plus/es/components/option/style/css', + 'element-plus/es/components/pagination/style/css', + 'element-plus/es/components/tree/style/css', + 'element-plus/es/components/alert/style/css', + + '@vueuse/core', + 'path-to-regexp', + 'echarts', + 'vue-i18n' + ] + } + }; +});