diff --git a/frontend/.env b/frontend/.env new file mode 100644 index 0000000..d6d1181 --- /dev/null +++ b/frontend/.env @@ -0,0 +1,11 @@ +# title +VITE_GLOB_APP_TITLE=自动检测平台 + +# 本地运行端口号 +VITE_PORT=18091 + +# 启动时自动打开浏览器 +VITE_OPEN=true + +# 打包后是否生成包分析文件 +VITE_REPORT=false diff --git a/frontend/.env.development b/frontend/.env.development index 5d89297..cd292f2 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -1,6 +1,21 @@ -NODE_ENV='development' +# 本地环境 +VITE_USER_NODE_ENV=development + +# 公共基础路径 +VITE_PUBLIC_PATH=/ -VITE_TITLE="" # 路由模式 # Optional: hash | history -VITE_ROUTER_MODE = hash \ No newline at end of file +VITE_ROUTER_MODE=hash + +# 打包时是否删除 console +VITE_DROP_CONSOLE=true + +# 是否开启 VitePWA +VITE_PWA=false + +# 开发环境接口地址 +VITE_API_URL=/api + +# 开发环境跨域代理,支持配置多个 +VITE_PROXY=[["/api","http://192.168.1.125:18092/"]] diff --git a/frontend/.env.production b/frontend/.env.production index a5993a4..ab062ea 100644 --- a/frontend/.env.production +++ b/frontend/.env.production @@ -1,6 +1,25 @@ -NODE_ENV='production' +# 线上环境 +VITE_USER_NODE_ENV=production + +# 公共基础路径 +VITE_PUBLIC_PATH=/ -VITE_TITLE="" # 路由模式 # Optional: hash | history -VITE_ROUTER_MODE = hash \ No newline at end of file +VITE_ROUTER_MODE=hash + +# 是否启用 gzip 或 brotli 压缩打包,如果需要多个压缩规则,可以使用 “,” 分隔 +# Optional: gzip | brotli | none +VITE_BUILD_COMPRESS=none + +# 打包压缩后是否删除源文件 +VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE=false + +# 打包时是否删除 console +VITE_DROP_CONSOLE=true + +# 是否开启 VitePWA +VITE_PWA=true + +# 线上环境接口地址 +VITE_API_URL="https://mock.mengxuegu.com/mock/629d727e6163854a32e8307e" diff --git a/frontend/build/getEnv.ts b/frontend/build/getEnv.ts new file mode 100644 index 0000000..1487dc5 --- /dev/null +++ b/frontend/build/getEnv.ts @@ -0,0 +1,46 @@ +import path from "path"; + +export function isDevFn(mode: string): boolean { + return mode === "development"; +} + +export function isProdFn(mode: string): boolean { + return mode === "production"; +} + +export function isTestFn(mode: string): boolean { + return mode === "test"; +} + +/** + * Whether to generate package preview + */ +export function isReportMode(): boolean { + return process.env.VITE_REPORT === "true"; +} + +// Read all environment variable configuration files to process.env +export function wrapperEnv(envConf: Recordable): ViteEnv { + const ret: any = {}; + + for (const envName of Object.keys(envConf)) { + let realName = envConf[envName].replace(/\\n/g, "\n"); + realName = realName === "true" ? true : realName === "false" ? false : realName; + if (envName === "VITE_PORT") realName = Number(realName); + if (envName === "VITE_PROXY") { + try { + realName = JSON.parse(realName); + } catch (error) {} + } + ret[envName] = realName; + } + return ret; +} + +/** + * Get user root directory + * @param dir file path + */ +export function getRootPath(...dir: string[]) { + return path.resolve(process.cwd(), ...dir); +} diff --git a/frontend/build/plugins.ts b/frontend/build/plugins.ts new file mode 100644 index 0000000..bd2ec78 --- /dev/null +++ b/frontend/build/plugins.ts @@ -0,0 +1,108 @@ +import { resolve } from "path"; +import { PluginOption } from "vite"; +import { VitePWA } from "vite-plugin-pwa"; +import { visualizer } from "rollup-plugin-visualizer"; +import { createHtmlPlugin } from "vite-plugin-html"; +import { createSvgIconsPlugin } from "vite-plugin-svg-icons"; +import vue from "@vitejs/plugin-vue"; +import vueJsx from "@vitejs/plugin-vue-jsx"; +import eslintPlugin from "vite-plugin-eslint"; +import viteCompression from "vite-plugin-compression"; +import vueSetupExtend from "unplugin-vue-setup-extend-plus/vite"; + +/** + * 创建 vite 插件 + * @param viteEnv + */ +export const createVitePlugins = (viteEnv: ViteEnv): (PluginOption | PluginOption[])[] => { + const { VITE_GLOB_APP_TITLE, VITE_REPORT, VITE_PWA } = viteEnv; + return [ + vue(), + // vue 可以使用 jsx/tsx 语法 + vueJsx(), + // esLint 报错信息显示在浏览器界面上 + eslintPlugin(), + // name 可以写在 script 标签上 + vueSetupExtend({}), + // 创建打包压缩配置 + createCompression(viteEnv), + // 注入变量到 html 文件 + createHtmlPlugin({ + inject: { + data: { title: VITE_GLOB_APP_TITLE } + } + }), + // 使用 svg 图标 + createSvgIconsPlugin({ + iconDirs: [resolve(process.cwd(), "src/assets/icons")], + symbolId: "icon-[dir]-[name]" + }), + // vitePWA + VITE_PWA && createVitePwa(viteEnv), + // 是否生成包预览,分析依赖包大小做优化处理 + VITE_REPORT && (visualizer({ filename: "stats.html", gzipSize: true, brotliSize: true }) as PluginOption) + ]; +}; + +/** + * @description 根据 compress 配置,生成不同的压缩规则 + * @param viteEnv + */ +const createCompression = (viteEnv: ViteEnv): PluginOption | PluginOption[] => { + const { VITE_BUILD_COMPRESS = "none", VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv; + const compressList = VITE_BUILD_COMPRESS.split(","); + const plugins: PluginOption[] = []; + if (compressList.includes("gzip")) { + plugins.push( + viteCompression({ + ext: ".gz", + algorithm: "gzip", + deleteOriginFile: VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE + }) + ); + } + if (compressList.includes("brotli")) { + plugins.push( + viteCompression({ + ext: ".br", + algorithm: "brotliCompress", + deleteOriginFile: VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE + }) + ); + } + return plugins; +}; + +/** + * @description VitePwa + * @param viteEnv + */ +const createVitePwa = (viteEnv: ViteEnv): PluginOption | PluginOption[] => { + const { VITE_GLOB_APP_TITLE } = viteEnv; + return VitePWA({ + registerType: "autoUpdate", + manifest: { + name: VITE_GLOB_APP_TITLE, + short_name: VITE_GLOB_APP_TITLE, + theme_color: "#ffffff", + icons: [ + { + src: "/logo.png", + sizes: "192x192", + type: "image/png" + }, + { + src: "/logo.png", + sizes: "512x512", + type: "image/png" + }, + { + src: "/logo.png", + sizes: "512x512", + type: "image/png", + purpose: "any maskable" + } + ] + } + }); +}; diff --git a/frontend/build/proxy.ts b/frontend/build/proxy.ts new file mode 100644 index 0000000..1f39d3b --- /dev/null +++ b/frontend/build/proxy.ts @@ -0,0 +1,30 @@ +import type { ProxyOptions } from "vite"; + +type ProxyItem = [string, string]; + +type ProxyList = ProxyItem[]; + +type ProxyTargetList = Record; + +/** + * 创建代理,用于解析 .env.development 代理配置 + * @param list + */ +export function createProxy(list: ProxyList = []) { + const ret: ProxyTargetList = {}; + for (const [prefix, target] of list) { + const httpsRE = /^https:\/\//; + const isHttps = httpsRE.test(target); + + // https://github.com/http-party/node-http-proxy#options + ret[prefix] = { + target: target, + changeOrigin: true, + ws: true, + rewrite: path => path.replace(new RegExp(`^${prefix}`), ""), + // https is require secure=false + ...(isHttps ? { secure: false } : {}) + }; + } + return ret; +} diff --git a/frontend/index.html b/frontend/index.html index 9de13bc..86250ed 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -100,6 +100,15 @@
+ diff --git a/frontend/package.json b/frontend/package.json index dfe4c6c..8999bf8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,11 +1,11 @@ { "name": "frontend", - "version": "0.0.0", + "version": "0.0.1", "private": true, "type": "module", "scripts": { - "dev": "vite --host --port 18091", - "serve": "vite --host --port 18091", + "dev": "vite", + "serve": "vite", "build-staging": "vite build --mode staging", "build": "vite build", "preview": "vite preview", @@ -14,34 +14,69 @@ }, "dependencies": { "@element-plus/icons-vue": "^2.3.1", + "@vueuse/core": "^10.4.1", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.12", "axios": "^1.7.3", "crypto-js": "^4.2.0", + "dayjs": "^1.11.9", + "driver.js": "^1.3.0", + "echarts": "^5.4.3", + "echarts-liquidfill": "^3.1.0", "element-plus": "^2.7.8", + "md5": "^2.3.0", + "mitt": "^3.0.1", + "nprogress": "^0.2.0", "pinia": "^2.2.1", "pinia-plugin-persistedstate": "^3.2.1", + "print-js": "^1.6.0", + "qs": "^6.11.2", + "screenfull": "^6.0.2", + "sortablejs": "^1.15.0", "vue": "^3.4.29", - "vue-router": "^4.3.3" + "vue-i18n": "^9.4.0", + "vue-router": "^4.3.3", + "vuedraggable": "^4.1.0" }, "devDependencies": { "@rushstack/eslint-patch": "^1.8.0", "@tsconfig/node20": "^20.1.4", + "@types/md5": "^2.3.2", "@types/node": "^20.14.14", + "@types/nprogress": "^0.2.0", + "@types/qs": "^6.9.8", + "@types/sortablejs": "^1.15.2", + "@typescript-eslint/eslint-plugin": "^6.7.0", + "@typescript-eslint/parser": "^6.7.0", "@vitejs/plugin-vue": "^5.0.5", + "@vitejs/plugin-vue-jsx": "^3.0.2", "@vue/eslint-config-typescript": "^13.0.0", "@vue/tsconfig": "^0.5.1", "autoprefixer": "^10.4.20", "eslint": "^8.57.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-vue": "^9.23.0", "npm-run-all2": "^6.2.0", - "postcss": "^8.4.41", + "prettier": "^3.0.3", + "rollup-plugin-visualizer": "^5.9.2", "sass": "^1.77.8", + "standard-version": "^9.5.0", "tailwindcss": "^3.4.7", "typescript": "~5.4.0", "unplugin-auto-import": "^0.18.2", "unplugin-vue-components": "^0.27.4", + "unplugin-vue-setup-extend-plus": "^1.0.0", "vite": "^5.3.1", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-eslint": "^1.8.1", + "vite-plugin-html": "^3.2.0", "vite-plugin-node-polyfills": "^0.22.0", + "vite-plugin-pwa": "^0.16.5", "vite-plugin-svg-icons": "^2.0.1", "vue-tsc": "^2.0.21" + }, + "engines": { + "node": ">=16.0.0" } } diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 59fa68e..d407ed5 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,6 +1,6 @@ @@ -9,8 +9,42 @@ defineOptions({ name: 'App', }) -import { ElConfigProvider } from 'element-plus' -import zhCn from 'element-plus/es/locale/lang/zh-cn' +import { onMounted, reactive, computed } from "vue"; +import { useI18n } from "vue-i18n"; +import { getBrowserLang } from "@/utils"; +import { useTheme } from "@/hooks/useTheme"; +import { ElConfigProvider } from "element-plus"; +import { LanguageType } from "./stores/interface"; +import { useGlobalStore } from "@/stores/modules/global"; +import en from "element-plus/es/locale/lang/en"; +import zhCn from "element-plus/es/locale/lang/zh-cn"; + +const globalStore = useGlobalStore(); + +// init theme +const { initTheme } = useTheme(); +initTheme(); + +// init language +const i18n = useI18n(); +onMounted(() => { + const language = globalStore.language ?? getBrowserLang(); + i18n.locale.value = language; + globalStore.setGlobalState("language", language as LanguageType); +}); + +// element language +const locale = computed(() => { + if (globalStore.language == "zh") return zhCn; + if (globalStore.language == "en") return en; + return getBrowserLang() == "zh" ? zhCn : en; +}); + +// element assemblySize +const assemblySize = computed(() => globalStore.assemblySize); + +// element button config +const buttonConfig = reactive({ autoInsertSpace: false }); document.getElementById('loadingPage')?.remove() diff --git a/frontend/src/api/config/servicePort.ts b/frontend/src/api/config/servicePort.ts new file mode 100644 index 0000000..ee1702f --- /dev/null +++ b/frontend/src/api/config/servicePort.ts @@ -0,0 +1,2 @@ +// 后端微服务模块前缀 +export const PORT1 = "/admin"; diff --git a/frontend/src/api/helper/axiosCancel.ts b/frontend/src/api/helper/axiosCancel.ts new file mode 100644 index 0000000..b0f5712 --- /dev/null +++ b/frontend/src/api/helper/axiosCancel.ts @@ -0,0 +1,47 @@ +// ? 暂未使用,目前使用全局 Loading 来控制重复请求 +import { CustomAxiosRequestConfig } from "../index"; +import qs from "qs"; + +// 声明一个 Map 用于存储每个请求的标识 和 取消函数 +let pendingMap = new Map(); + +// 序列化参数 +export const getPendingUrl = (config: CustomAxiosRequestConfig) => + [config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join("&"); + +export class AxiosCanceler { + /** + * @description: 添加请求 + * @param {Object} config + * @return void + */ + addPending(config: CustomAxiosRequestConfig) { + // 在请求开始前,对之前的请求做检查取消操作 + this.removePending(config); + const url = getPendingUrl(config); + const controller = new AbortController(); + config.signal = controller.signal; + pendingMap.set(url, controller); + } + + /** + * @description: 移除请求 + * @param {Object} config + */ + removePending(config: CustomAxiosRequestConfig) { + const url = getPendingUrl(config); + // 如果在 pending 中存在当前请求标识,需要取消当前请求 + const controller = pendingMap.get(url); + controller && controller.abort(); + } + + /** + * @description: 清空所有pending + */ + removeAllPending() { + pendingMap.forEach(controller => { + controller && controller.abort(); + }); + pendingMap.clear(); + } +} diff --git a/frontend/src/api/helper/checkStatus.ts b/frontend/src/api/helper/checkStatus.ts new file mode 100644 index 0000000..ab2c685 --- /dev/null +++ b/frontend/src/api/helper/checkStatus.ts @@ -0,0 +1,43 @@ +import { ElMessage } from "element-plus"; + +/** + * @description: 校验网络请求状态码 + * @param {Number} status + * @return void + */ +export const checkStatus = (status: number) => { + switch (status) { + case 400: + ElMessage.error("请求失败!请您稍后重试"); + break; + case 401: + ElMessage.error("登录失效!请您重新登录"); + break; + case 403: + ElMessage.error("当前账号无权限访问!"); + break; + case 404: + ElMessage.error("你所访问的资源不存在!"); + break; + case 405: + ElMessage.error("请求方式错误!请您稍后重试"); + break; + case 408: + ElMessage.error("请求超时!请您稍后重试"); + break; + case 500: + ElMessage.error("服务异常!"); + break; + case 502: + ElMessage.error("网关错误!"); + break; + case 503: + ElMessage.error("服务不可用!"); + break; + case 504: + ElMessage.error("网关超时!"); + break; + default: + ElMessage.error("请求失败!"); + } +}; diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts new file mode 100644 index 0000000..85679fb --- /dev/null +++ b/frontend/src/api/index.ts @@ -0,0 +1,110 @@ +import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from "axios"; +import { showFullScreenLoading, tryHideFullScreenLoading } from "@/components/Loading/fullScreen"; +import { LOGIN_URL } from "@/config"; +import { ElMessage } from "element-plus"; +import { ResultData } from "@/api/interface"; +import { ResultEnum } from "@/enums/httpEnum"; +import { checkStatus } from "./helper/checkStatus"; +import { useUserStore } from "@/stores/modules/user"; +import router from "@/routers"; + +export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig { + loading?: boolean; +} + +const config = { + // 默认地址请求地址,可在 .env.** 文件中修改 + baseURL: import.meta.env.VITE_API_URL as string, + // 设置超时时间 + timeout: ResultEnum.TIMEOUT as number, + // 跨域时候允许携带凭证 + withCredentials: true +}; + +class RequestHttp { + service: AxiosInstance; + public constructor(config: AxiosRequestConfig) { + // instantiation + this.service = axios.create(config); + + /** + * @description 请求拦截器 + * 客户端发送请求 -> [请求拦截器] -> 服务器 + * token校验(JWT) : 接受服务器返回的 token,存储到 vuex/pinia/本地储存当中 + */ + this.service.interceptors.request.use( + (config: CustomAxiosRequestConfig) => { + const userStore = useUserStore(); + // 当前请求不需要显示 loading,在 api 服务中通过指定的第三个参数: { loading: false } 来控制 + config.loading ?? (config.loading = true); + config.loading && showFullScreenLoading(); + if (config.headers && typeof config.headers.set === "function") { + config.headers.set("x-access-token", userStore.token); + } + return config; + }, + (error: AxiosError) => { + return Promise.reject(error); + } + ); + + /** + * @description 响应拦截器 + * 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息 + */ + this.service.interceptors.response.use( + (response: AxiosResponse) => { + const { data } = response; + const userStore = useUserStore(); + tryHideFullScreenLoading(); + // 登陆失效 + if (data.code == ResultEnum.OVERDUE) { + userStore.setToken(""); + router.replace(LOGIN_URL); + ElMessage.error(data.message); + return Promise.reject(data); + } + // 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错) + if (data.code && data.code !== ResultEnum.SUCCESS) { + ElMessage.error(data.message); + return Promise.reject(data); + } + // 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑) + return data; + }, + async (error: AxiosError) => { + const { response } = error; + tryHideFullScreenLoading(); + // 请求超时 && 网络错误单独判断,没有 response + if (error.message.indexOf("timeout") !== -1) ElMessage.error("请求超时!请您稍后重试"); + if (error.message.indexOf("Network Error") !== -1) ElMessage.error("网络错误!请您稍后重试"); + // 根据服务器响应的错误状态码,做不同的处理 + if (response) checkStatus(response.status); + // 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面 + if (!window.navigator.onLine) router.replace("/500"); + return Promise.reject(error); + } + ); + } + + /** + * @description 常用请求方法封装 + */ + get(url: string, params?: object, _object = {}): Promise> { + return this.service.get(url, { params, ..._object }); + } + post(url: string, params?: object | string, _object = {}): Promise> { + return this.service.post(url, params, _object); + } + put(url: string, params?: object, _object = {}): Promise> { + return this.service.put(url, params, _object); + } + delete(url: string, params?: any, _object = {}): Promise> { + return this.service.delete(url, { params, ..._object }); + } + download(url: string, params?: object, _object = {}): Promise { + return this.service.post(url, params, { ..._object, responseType: "blob" }); + } +} + +export default new RequestHttp(config); diff --git a/frontend/src/api/interface/index.ts b/frontend/src/api/interface/index.ts new file mode 100644 index 0000000..16aa1eb --- /dev/null +++ b/frontend/src/api/interface/index.ts @@ -0,0 +1,90 @@ +// 请求响应参数(不包含data) +export interface Result { + code: string; + message: string; +} + +// 请求响应参数(包含data) +export interface ResultData extends Result { + data: T; +} + +// 分页响应参数 +export interface ResPage { + list: T[]; + pageNum: number; + pageSize: number; + total: number; +} + +// 分页请求参数 +export interface ReqPage { + pageNum: number; + pageSize: number; +} + +// 文件上传模块 +export namespace Upload { + export interface ResFileUrl { + fileUrl: string; + } +} + +// 登录模块 +export namespace Login { + export interface ReqLoginForm { + username: string; + password: string; + } + export interface ResLogin { + accessToken: string; + } + export interface ResAuthButtons { + [key: string]: string[]; + } +} + +// 用户管理模块 +export namespace User { + export interface ReqUserParams extends ReqPage { + username: string; + gender: number; + idCard: string; + email: string; + address: string; + createTime: string[]; + status: number; + } + export interface ResUserList { + id: string; + username: string; + gender: number; + user: { detail: { age: number } }; + idCard: string; + email: string; + address: string; + createTime: string; + status: number; + avatar: string; + photo: any[]; + children?: ResUserList[]; + } + export interface ResStatus { + userLabel: string; + userValue: number; + } + export interface ResGender { + genderLabel: string; + genderValue: number; + } + export interface ResDepartment { + id: string; + name: string; + children?: ResDepartment[]; + } + export interface ResRole { + id: string; + name: string; + children?: ResDepartment[]; + } +} diff --git a/frontend/src/api/main.js b/frontend/src/api/main.js deleted file mode 100644 index 58f2327..0000000 --- a/frontend/src/api/main.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * 主进程与渲染进程通信频道定义 - * Definition of communication channels between main process and rendering process - */ -const ipcApiRoute = { - test: 'controller.example.test', -} - -export { ipcApiRoute } diff --git a/frontend/src/api/modules/login.ts b/frontend/src/api/modules/login.ts new file mode 100644 index 0000000..4a1c473 --- /dev/null +++ b/frontend/src/api/modules/login.ts @@ -0,0 +1,26 @@ +import { Login } from "@/api/interface/index"; +import { PORT1 } from "@/api/config/servicePort"; +import http from "@/api"; + +/** + * @name 登录模块 + */ +// 用户登录 +export const loginApi = (params: Login.ReqLoginForm) => { + return http.post(PORT1 + `/login`, params, { loading: false }); // 正常 post json 请求 ==> application/json +}; + +// 获取菜单列表 +export const getAuthMenuListApi = () => { + return http.get(PORT1 + `/menu/list`, {}, { loading: false }); +}; + +// 获取按钮权限 +export const getAuthButtonListApi = () => { + return http.get(PORT1 + `/auth/buttons`, {}, { loading: false }); +}; + +// 用户退出登录 +export const logoutApi = () => { + return http.post(PORT1 + `/logout`); +}; diff --git a/frontend/src/api/modules/upload.ts b/frontend/src/api/modules/upload.ts new file mode 100644 index 0000000..267b3a4 --- /dev/null +++ b/frontend/src/api/modules/upload.ts @@ -0,0 +1,16 @@ +import { Upload } from "@/api/interface/index"; +import { PORT1 } from "@/api/config/servicePort"; +import http from "@/api"; + +/** + * @name 文件上传模块 + */ +// 图片上传 +export const uploadImg = (params: FormData) => { + return http.post(PORT1 + `/file/upload/img`, params); +}; + +// 视频上传 +export const uploadVideo = (params: FormData) => { + return http.post(PORT1 + `/file/upload/video`, params); +}; diff --git a/frontend/src/api/modules/user.ts b/frontend/src/api/modules/user.ts new file mode 100644 index 0000000..6555808 --- /dev/null +++ b/frontend/src/api/modules/user.ts @@ -0,0 +1,71 @@ +import { ResPage, User } from "@/api/interface/index"; +import { PORT1 } from "@/api/config/servicePort"; +import http from "@/api"; + +/** + * @name 用户管理模块 + */ +// 获取用户列表 +export const getUserList = (params: User.ReqUserParams) => { + return http.post>(PORT1 + `/user/list`, params); +}; + +// 获取树形用户列表 +export const getUserTreeList = (params: User.ReqUserParams) => { + return http.post>(PORT1 + `/user/tree/list`, params); +}; + +// 新增用户 +export const addUser = (params: { id: string }) => { + return http.post(PORT1 + `/user/add`, params); +}; + +// 批量添加用户 +export const BatchAddUser = (params: FormData) => { + return http.post(PORT1 + `/user/import`, params); +}; + +// 编辑用户 +export const editUser = (params: { id: string }) => { + return http.post(PORT1 + `/user/edit`, params); +}; + +// 删除用户 +export const deleteUser = (params: { id: string[] }) => { + return http.post(PORT1 + `/user/delete`, params); +}; + +// 切换用户状态 +export const changeUserStatus = (params: { id: string; status: number }) => { + return http.post(PORT1 + `/user/change`, params); +}; + +// 重置用户密码 +export const resetUserPassWord = (params: { id: string }) => { + return http.post(PORT1 + `/user/rest_password`, params); +}; + +// 导出用户数据 +export const exportUserInfo = (params: User.ReqUserParams) => { + return http.download(PORT1 + `/user/export`, params); +}; + +// 获取用户状态字典 +export const getUserStatus = () => { + return http.get(PORT1 + `/user/status`); +}; + +// 获取用户性别字典 +export const getUserGender = () => { + return http.get(PORT1 + `/user/gender`); +}; + +// 获取用户部门列表 +export const getUserDepartment = () => { + return http.get(PORT1 + `/user/department`); +}; + +// 获取用户角色字典 +export const getUserRole = () => { + return http.get(PORT1 + `/user/role`); +}; diff --git a/frontend/src/api/user/index.ts b/frontend/src/api/user/index.ts deleted file mode 100644 index 44fabc7..0000000 --- a/frontend/src/api/user/index.ts +++ /dev/null @@ -1,57 +0,0 @@ -import createAxios from '@/utils/http' -import { useUserInfoStore } from '@/stores/modules/user' -import { sm3Digest } from '@/assets/commjs/sm3.js' -import { sm2, encrypt } from '@/assets/commjs/sm2.js' - -// 获取公钥 -export const publicKey = (params?: any) => { - if (!params) { - const userInfo = useUserInfoStore() - params = { - loginName: encrypt(userInfo.loginName), - } - } - return createAxios({ - url: '/user/generateSm2Key', - method: 'get', - params, - }) -} - -export const pwdSm3 = async (pwd: any, loginName?: string) => { - let publicKeyStr = await publicKey( - loginName ? { loginName: encrypt(loginName) } : false, - ) - let sm3Pwd = sm3Digest(pwd) //SM3加密 - return sm2(sm3Pwd + '|' + pwd, publicKeyStr.data, 0) -} - -//登录获取token -export const login = async (params: any) => { - params.password = await pwdSm3(params.password, params.username) - params.username = encrypt(params.username) - return createAxios({ - url: '/pqs-auth/oauth/token', - method: 'post', - params, - }) -} - -//获取用户信息 -export const getUserById = () => { - const userInfo = useUserInfoStore() - return createAxios({ - url: '/user-boot/user/getUserById?id=' + userInfo.userIndex, - method: 'get', - }) -} - -// 刷新token -export function refreshToken(): Promise { - const userInfo = useUserInfoStore() - return login({ - grant_type: 'refresh_token', - refresh_token: userInfo.refresh_token, - username: userInfo.loginName, - }) -} \ No newline at end of file diff --git a/frontend/src/assets/commjs/sm2.js b/frontend/src/assets/commjs/sm2.js deleted file mode 100644 index bbb516a..0000000 --- a/frontend/src/assets/commjs/sm2.js +++ /dev/null @@ -1,3734 +0,0 @@ -import CryptoJS from 'crypto-js' -function SM2Cipher(a) { - this.ct = 1; - this.sm3c3 = this.sm3keybase = this.p2 = null; - this.key = Array(32); - this.keyOff = 0; - this.cipherMode = "undefined" != typeof a ? a : SM2CipherMode.C1C3C2 -} -(function (global, undefined) { - "use strict"; - var SM2CipherMode = { - C1C2C3: "0", - C1C3C2: "1" - }; - (function () { - function a(a, c) { - var b = (this._lBlock >>> a ^ this._rBlock) & c; - this._rBlock ^= b; - this._lBlock ^= b << a - } - - function b(a, c) { - var b = (this._rBlock >>> a ^ this._lBlock) & c; - this._lBlock ^= b; - this._rBlock ^= b << a - } - var c = CryptoJS, - d = c.lib, - e = d.WordArray, - d = d.BlockCipher, - f = c.algo, - g = [57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4], - h = [14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32], - k = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28], - l = [{ - 0: 8421888, - 268435456: 32768, - 536870912: 8421378, - 805306368: 2, - 1073741824: 512, - 1342177280: 8421890, - 1610612736: 8389122, - 1879048192: 8388608, - 2147483648: 514, - 2415919104: 8389120, - 2684354560: 33280, - 2952790016: 8421376, - 3221225472: 32770, - 3489660928: 8388610, - 3758096384: 0, - 4026531840: 33282, - 134217728: 0, - 402653184: 8421890, - 671088640: 33282, - 939524096: 32768, - 1207959552: 8421888, - 1476395008: 512, - 1744830464: 8421378, - 2013265920: 2, - 2281701376: 8389120, - 2550136832: 33280, - 2818572288: 8421376, - 3087007744: 8389122, - 3355443200: 8388610, - 3623878656: 32770, - 3892314112: 514, - 4160749568: 8388608, - 1: 32768, - 268435457: 2, - 536870913: 8421888, - 805306369: 8388608, - 1073741825: 8421378, - 1342177281: 33280, - 1610612737: 512, - 1879048193: 8389122, - 2147483649: 8421890, - 2415919105: 8421376, - 2684354561: 8388610, - 2952790017: 33282, - 3221225473: 514, - 3489660929: 8389120, - 3758096385: 32770, - 4026531841: 0, - 134217729: 8421890, - 402653185: 8421376, - 671088641: 8388608, - 939524097: 512, - 1207959553: 32768, - 1476395009: 8388610, - 1744830465: 2, - 2013265921: 33282, - 2281701377: 32770, - 2550136833: 8389122, - 2818572289: 514, - 3087007745: 8421888, - 3355443201: 8389120, - 3623878657: 0, - 3892314113: 33280, - 4160749569: 8421378 - }, { - 0: 1074282512, - 16777216: 16384, - 33554432: 524288, - 50331648: 1074266128, - 67108864: 1073741840, - 83886080: 1074282496, - 100663296: 1073758208, - 117440512: 16, - 134217728: 540672, - 150994944: 1073758224, - 167772160: 1073741824, - 184549376: 540688, - 201326592: 524304, - 218103808: 0, - 234881024: 16400, - 251658240: 1074266112, - 8388608: 1073758208, - 25165824: 540688, - 41943040: 16, - 58720256: 1073758224, - 75497472: 1074282512, - 92274688: 1073741824, - 109051904: 524288, - 125829120: 1074266128, - 142606336: 524304, - 159383552: 0, - 176160768: 16384, - 192937984: 1074266112, - 209715200: 1073741840, - 226492416: 540672, - 243269632: 1074282496, - 260046848: 16400, - 268435456: 0, - 285212672: 1074266128, - 301989888: 1073758224, - 318767104: 1074282496, - 335544320: 1074266112, - 352321536: 16, - 369098752: 540688, - 385875968: 16384, - 402653184: 16400, - 419430400: 524288, - 436207616: 524304, - 452984832: 1073741840, - 469762048: 540672, - 486539264: 1073758208, - 503316480: 1073741824, - 520093696: 1074282512, - 276824064: 540688, - 293601280: 524288, - 310378496: 1074266112, - 327155712: 16384, - 343932928: 1073758208, - 360710144: 1074282512, - 377487360: 16, - 394264576: 1073741824, - 411041792: 1074282496, - 427819008: 1073741840, - 444596224: 1073758224, - 461373440: 524304, - 478150656: 0, - 494927872: 16400, - 511705088: 1074266128, - 528482304: 540672 - }, { - 0: 260, - 1048576: 0, - 2097152: 67109120, - 3145728: 65796, - 4194304: 65540, - 5242880: 67108868, - 6291456: 67174660, - 7340032: 67174400, - 8388608: 67108864, - 9437184: 67174656, - 10485760: 65792, - 11534336: 67174404, - 12582912: 67109124, - 13631488: 65536, - 14680064: 4, - 15728640: 256, - 524288: 67174656, - 1572864: 67174404, - 2621440: 0, - 3670016: 67109120, - 4718592: 67108868, - 5767168: 65536, - 6815744: 65540, - 7864320: 260, - 8912896: 4, - 9961472: 256, - 11010048: 67174400, - 12058624: 65796, - 13107200: 65792, - 14155776: 67109124, - 15204352: 67174660, - 16252928: 67108864, - 16777216: 67174656, - 17825792: 65540, - 18874368: 65536, - 19922944: 67109120, - 20971520: 256, - 22020096: 67174660, - 23068672: 67108868, - 24117248: 0, - 25165824: 67109124, - 26214400: 67108864, - 27262976: 4, - 28311552: 65792, - 29360128: 67174400, - 30408704: 260, - 31457280: 65796, - 32505856: 67174404, - 17301504: 67108864, - 18350080: 260, - 19398656: 67174656, - 20447232: 0, - 21495808: 65540, - 22544384: 67109120, - 23592960: 256, - 24641536: 67174404, - 25690112: 65536, - 26738688: 67174660, - 27787264: 65796, - 28835840: 67108868, - 29884416: 67109124, - 30932992: 67174400, - 31981568: 4, - 33030144: 65792 - }, { - 0: 2151682048, - 65536: 2147487808, - 131072: 4198464, - 196608: 2151677952, - 262144: 0, - 327680: 4198400, - 393216: 2147483712, - 458752: 4194368, - 524288: 2147483648, - 589824: 4194304, - 655360: 64, - 720896: 2147487744, - 786432: 2151678016, - 851968: 4160, - 917504: 4096, - 983040: 2151682112, - 32768: 2147487808, - 98304: 64, - 163840: 2151678016, - 229376: 2147487744, - 294912: 4198400, - 360448: 2151682112, - 425984: 0, - 491520: 2151677952, - 557056: 4096, - 622592: 2151682048, - 688128: 4194304, - 753664: 4160, - 819200: 2147483648, - 884736: 4194368, - 950272: 4198464, - 1015808: 2147483712, - 1048576: 4194368, - 1114112: 4198400, - 1179648: 2147483712, - 1245184: 0, - 1310720: 4160, - 1376256: 2151678016, - 1441792: 2151682048, - 1507328: 2147487808, - 1572864: 2151682112, - 1638400: 2147483648, - 1703936: 2151677952, - 1769472: 4198464, - 1835008: 2147487744, - 1900544: 4194304, - 1966080: 64, - 2031616: 4096, - 1081344: 2151677952, - 1146880: 2151682112, - 1212416: 0, - 1277952: 4198400, - 1343488: 4194368, - 1409024: 2147483648, - 1474560: 2147487808, - 1540096: 64, - 1605632: 2147483712, - 1671168: 4096, - 1736704: 2147487744, - 1802240: 2151678016, - 1867776: 4160, - 1933312: 2151682048, - 1998848: 4194304, - 2064384: 4198464 - }, { - 0: 128, - 4096: 17039360, - 8192: 262144, - 12288: 536870912, - 16384: 537133184, - 20480: 16777344, - 24576: 553648256, - 28672: 262272, - 32768: 16777216, - 36864: 537133056, - 40960: 536871040, - 45056: 553910400, - 49152: 553910272, - 53248: 0, - 57344: 17039488, - 61440: 553648128, - 2048: 17039488, - 6144: 553648256, - 10240: 128, - 14336: 17039360, - 18432: 262144, - 22528: 537133184, - 26624: 553910272, - 30720: 536870912, - 34816: 537133056, - 38912: 0, - 43008: 553910400, - 47104: 16777344, - 51200: 536871040, - 55296: 553648128, - 59392: 16777216, - 63488: 262272, - 65536: 262144, - 69632: 128, - 73728: 536870912, - 77824: 553648256, - 81920: 16777344, - 86016: 553910272, - 90112: 537133184, - 94208: 16777216, - 98304: 553910400, - 102400: 553648128, - 106496: 17039360, - 110592: 537133056, - 114688: 262272, - 118784: 536871040, - 122880: 0, - 126976: 17039488, - 67584: 553648256, - 71680: 16777216, - 75776: 17039360, - 79872: 537133184, - 83968: 536870912, - 88064: 17039488, - 92160: 128, - 96256: 553910272, - 100352: 262272, - 104448: 553910400, - 108544: 0, - 112640: 553648128, - 116736: 16777344, - 120832: 262144, - 124928: 537133056, - 129024: 536871040 - }, { - 0: 268435464, - 256: 8192, - 512: 270532608, - 768: 270540808, - 1024: 268443648, - 1280: 2097152, - 1536: 2097160, - 1792: 268435456, - 2048: 0, - 2304: 268443656, - 2560: 2105344, - 2816: 8, - 3072: 270532616, - 3328: 2105352, - 3584: 8200, - 3840: 270540800, - 128: 270532608, - 384: 270540808, - 640: 8, - 896: 2097152, - 1152: 2105352, - 1408: 268435464, - 1664: 268443648, - 1920: 8200, - 2176: 2097160, - 2432: 8192, - 2688: 268443656, - 2944: 270532616, - 3200: 0, - 3456: 270540800, - 3712: 2105344, - 3968: 268435456, - 4096: 268443648, - 4352: 270532616, - 4608: 270540808, - 4864: 8200, - 5120: 2097152, - 5376: 268435456, - 5632: 268435464, - 5888: 2105344, - 6144: 2105352, - 6400: 0, - 6656: 8, - 6912: 270532608, - 7168: 8192, - 7424: 268443656, - 7680: 270540800, - 7936: 2097160, - 4224: 8, - 4480: 2105344, - 4736: 2097152, - 4992: 268435464, - 5248: 268443648, - 5504: 8200, - 5760: 270540808, - 6016: 270532608, - 6272: 270540800, - 6528: 270532616, - 6784: 8192, - 7040: 2105352, - 7296: 2097160, - 7552: 0, - 7808: 268435456, - 8064: 268443656 - }, { - 0: 1048576, - 16: 33555457, - 32: 1024, - 48: 1049601, - 64: 34604033, - 80: 0, - 96: 1, - 112: 34603009, - 128: 33555456, - 144: 1048577, - 160: 33554433, - 176: 34604032, - 192: 34603008, - 208: 1025, - 224: 1049600, - 240: 33554432, - 8: 34603009, - 24: 0, - 40: 33555457, - 56: 34604032, - 72: 1048576, - 88: 33554433, - 104: 33554432, - 120: 1025, - 136: 1049601, - 152: 33555456, - 168: 34603008, - 184: 1048577, - 200: 1024, - 216: 34604033, - 232: 1, - 248: 1049600, - 256: 33554432, - 272: 1048576, - 288: 33555457, - 304: 34603009, - 320: 1048577, - 336: 33555456, - 352: 34604032, - 368: 1049601, - 384: 1025, - 400: 34604033, - 416: 1049600, - 432: 1, - 448: 0, - 464: 34603008, - 480: 33554433, - 496: 1024, - 264: 1049600, - 280: 33555457, - 296: 34603009, - 312: 1, - 328: 33554432, - 344: 1048576, - 360: 1025, - 376: 34604032, - 392: 33554433, - 408: 34603008, - 424: 0, - 440: 34604033, - 456: 1049601, - 472: 1024, - 488: 33555456, - 504: 1048577 - }, { - 0: 134219808, - 1: 131072, - 2: 134217728, - 3: 32, - 4: 131104, - 5: 134350880, - 6: 134350848, - 7: 2048, - 8: 134348800, - 9: 134219776, - 10: 133120, - 11: 134348832, - 12: 2080, - 13: 0, - 14: 134217760, - 15: 133152, - 2147483648: 2048, - 2147483649: 134350880, - 2147483650: 134219808, - 2147483651: 134217728, - 2147483652: 134348800, - 2147483653: 133120, - 2147483654: 133152, - 2147483655: 32, - 2147483656: 134217760, - 2147483657: 2080, - 2147483658: 131104, - 2147483659: 134350848, - 2147483660: 0, - 2147483661: 134348832, - 2147483662: 134219776, - 2147483663: 131072, - 16: 133152, - 17: 134350848, - 18: 32, - 19: 2048, - 20: 134219776, - 21: 134217760, - 22: 134348832, - 23: 131072, - 24: 0, - 25: 131104, - 26: 134348800, - 27: 134219808, - 28: 134350880, - 29: 133120, - 30: 2080, - 31: 134217728, - 2147483664: 131072, - 2147483665: 2048, - 2147483666: 134348832, - 2147483667: 133152, - 2147483668: 32, - 2147483669: 134348800, - 2147483670: 134217728, - 2147483671: 134219808, - 2147483672: 134350880, - 2147483673: 134217760, - 2147483674: 134219776, - 2147483675: 0, - 2147483676: 133120, - 2147483677: 2080, - 2147483678: 131104, - 2147483679: 134350848 - }], - p = [4160749569, 528482304, 33030144, 2064384, 129024, 8064, 504, 2147483679], - n = f.DES = d.extend({ - _doReset: function () { - for (var a = this._key.words, c = [], b = 0; 56 > b; b++) { - var d = g[b] - 1; - c[b] = a[d >>> 5] >>> 31 - d % 32 & 1 - } - a = this._subKeys = []; - for (d = 0; 16 > d; d++) { - for (var e = a[d] = [], f = k[d], b = 0; 24 > b; b++) - e[b / 6 | 0] |= c[(h[b] - 1 + f) % 28] << 31 - b % 6, - e[4 + (b / 6 | 0)] |= c[28 + (h[b + 24] - 1 + f) % 28] << 31 - b % 6; - e[0] = e[0] << 1 | e[0] >>> 31; - for (b = 1; 7 > b; b++) - e[b] >>>= 4 * (b - 1) + 3; - e[7] = e[7] << 5 | e[7] >>> 27 - } - c = this._invSubKeys = []; - for (b = 0; 16 > b; b++) - c[b] = a[15 - b] - }, - encryptBlock: function (a, c) { - this._doCryptBlock(a, c, this._subKeys) - }, - decryptBlock: function (a, c) { - this._doCryptBlock(a, c, this._invSubKeys) - }, - _doCryptBlock: function (c, d, e) { - this._lBlock = c[d]; - this._rBlock = c[d + 1]; - a.call(this, 4, 252645135); - a.call(this, 16, 65535); - b.call(this, 2, 858993459); - b.call(this, 8, 16711935); - a.call(this, 1, 1431655765); - for (var f = 0; 16 > f; f++) { - for (var g = e[f], h = this._lBlock, k = this._rBlock, n = 0, u = 0; 8 > u; u++) - n |= l[u][((k ^ g[u]) & p[u]) >>> 0]; - this._lBlock = k; - this._rBlock = h ^ n - } - e = this._lBlock; - this._lBlock = this._rBlock; - this._rBlock = e; - a.call(this, 1, 1431655765); - b.call(this, 8, 16711935); - b.call(this, 2, 858993459); - a.call(this, 16, 65535); - a.call(this, 4, 252645135); - c[d] = this._lBlock; - c[d + 1] = this._rBlock - }, - keySize: 2, - ivSize: 2, - blockSize: 2 - }); - c.DES = d._createHelper(n); - f = f.TripleDES = d.extend({ - _doReset: function () { - var a = this._key.words; - this._des1 = n.createEncryptor(e.create(a.slice(0, 2))); - this._des2 = n.createEncryptor(e.create(a.slice(2, 4))); - this._des3 = n.createEncryptor(e.create(a.slice(4, 6))) - }, - encryptBlock: function (a, c) { - this._des1.encryptBlock(a, c); - this._des2.decryptBlock(a, c); - this._des3.encryptBlock(a, c) - }, - decryptBlock: function (a, c) { - this._des3.decryptBlock(a, c); - this._des2.encryptBlock(a, c); - this._des1.decryptBlock(a, c) - }, - keySize: 6, - ivSize: 2, - blockSize: 2 - }); - c.TripleDES = d._createHelper(f) - })(); - (function () { - var a = CryptoJS, - b = a.lib.WordArray; - a.enc.Base64 = { - stringify: function (a) { - var b = a.words, - e = a.sigBytes, - f = this._map; - a.clamp(); - a = []; - for (var g = 0; g < e; g += 3) - for (var h = (b[g >>> 2] >>> 24 - g % 4 * 8 & 255) << 16 | (b[g + 1 >>> 2] >>> 24 - (g + 1) % 4 * 8 & 255) << 8 | b[g + 2 >>> 2] >>> 24 - (g + 2) % 4 * 8 & 255, k = 0; 4 > k && g + .75 * k < e; k++) - a.push(f.charAt(h >>> 6 * (3 - k) & 63)); - if (b = f.charAt(64)) - for (; a.length % 4;) - a.push(b); - return a.join("") - }, - parse: function (a) { - var d = a.length, - e = this._map, - f = e.charAt(64); - f && (f = a.indexOf(f), - -1 != f && (d = f)); - for (var f = [], g = 0, h = 0; h < d; h++) - if (h % 4) { - var k = e.indexOf(a.charAt(h - 1)) << h % 4 * 2, - l = e.indexOf(a.charAt(h)) >>> 6 - h % 4 * 2; - f[g >>> 2] |= (k | l) << 24 - g % 4 * 8; - g++ - } - return b.create(f, g) - }, - _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" - } - })(); - var dbits, canary = 0xdeadbeefcafe, - j_lm = 15715070 == (canary & 16777215); - - function BigInteger(a, b, c) { - null != a && ("number" == typeof a ? this.fromNumber(a, b, c) : null == b && "string" != typeof a ? this.fromString(a, 256) : this.fromString(a, b)) - } - - function nbi() { - return new BigInteger(null) - } - - function am1(a, b, c, d, e, f) { - for (; 0 <= --f;) { - var g = b * this[a++] + c[d] + e; - e = Math.floor(g / 67108864); - c[d++] = g & 67108863 - } - return e - } - - function am2(a, b, c, d, e, f) { - var g = b & 32767; - for (b >>= 15; 0 <= --f;) { - var h = this[a] & 32767, - k = this[a++] >> 15, - l = b * h + k * g, - h = g * h + ((l & 32767) << 15) + c[d] + (e & 1073741823); - e = (h >>> 30) + (l >>> 15) + b * k + (e >>> 30); - c[d++] = h & 1073741823 - } - return e - } - - function am3(a, b, c, d, e, f) { - var g = b & 16383; - for (b >>= 14; 0 <= --f;) { - var h = this[a] & 16383, - k = this[a++] >> 14, - l = b * h + k * g, - h = g * h + ((l & 16383) << 14) + c[d] + e; - e = (h >> 28) + (l >> 14) + b * k; - c[d++] = h & 268435455 - } - return e - } - j_lm && "Microsoft Internet Explorer" == navigator.appName ? (BigInteger.prototype.am = am2, - dbits = 30) : j_lm && "Netscape" != navigator.appName ? (BigInteger.prototype.am = am1, - dbits = 26) : (BigInteger.prototype.am = am3, - dbits = 28); - BigInteger.prototype.DB = dbits; - BigInteger.prototype.DM = (1 << dbits) - 1; - BigInteger.prototype.DV = 1 << dbits; - var BI_FP = 52; - BigInteger.prototype.FV = Math.pow(2, BI_FP); - BigInteger.prototype.F1 = BI_FP - dbits; - BigInteger.prototype.F2 = 2 * dbits - BI_FP; - var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz", - BI_RC = [], - rr, vv; - rr = 48; - for (vv = 0; 9 >= vv; ++vv) - BI_RC[rr++] = vv; - rr = 97; - for (vv = 10; 36 > vv; ++vv) - BI_RC[rr++] = vv; - rr = 65; - for (vv = 10; 36 > vv; ++vv) - BI_RC[rr++] = vv; - - function int2char(a) { - return BI_RM.charAt(a) - } - - function intAt(a, b) { - var c = BI_RC[a.charCodeAt(b)]; - return null == c ? -1 : c - } - - function bnpCopyTo(a) { - for (var b = this.t - 1; 0 <= b; --b) - a[b] = this[b]; - a.t = this.t; - a.s = this.s - } - - function bnpFromInt(a) { - this.t = 1; - this.s = 0 > a ? -1 : 0; - 0 < a ? this[0] = a : -1 > a ? this[0] = a + this.DV : this.t = 0 - } - - function nbv(a) { - var b = nbi(); - b.fromInt(a); - return b - } - - function bnpFromString(a, b) { - var c; - if (16 == b) - c = 4; - else if (8 == b) - c = 3; - else if (256 == b) - c = 8; - else if (2 == b) - c = 1; - else if (32 == b) - c = 5; - else if (4 == b) - c = 2; - else { - this.fromRadix(a, b); - return - } - this.s = this.t = 0; - for (var d = a.length, e = !1, f = 0; 0 <= --d;) { - var g = 8 == c ? a[d] & 255 : intAt(a, d); - 0 > g ? "-" == a.charAt(d) && (e = !0) : (e = !1, - 0 == f ? this[this.t++] = g : f + c > this.DB ? (this[this.t - 1] |= (g & (1 << this.DB - f) - 1) << f, - this[this.t++] = g >> this.DB - f) : this[this.t - 1] |= g << f, - f += c, - f >= this.DB && (f -= this.DB)) - } - 8 == c && 0 != (a[0] & 128) && (this.s = -1, - 0 < f && (this[this.t - 1] |= (1 << this.DB - f) - 1 << f)); - this.clamp(); - e && BigInteger.ZERO.subTo(this, this) - } - - function bnpClamp() { - for (var a = this.s & this.DM; 0 < this.t && this[this.t - 1] == a;) - --this.t - } - - function bnToString(a) { - if (0 > this.s) - return "-" + this.negate().toString(a); - if (16 == a) - a = 4; - else if (8 == a) - a = 3; - else if (2 == a) - a = 1; - else if (32 == a) - a = 5; - else if (4 == a) - a = 2; - else - return this.toRadix(a); - var b = (1 << a) - 1, - c, d = !1, - e = "", - f = this.t, - g = this.DB - f * this.DB % a; - if (0 < f--) - for (g < this.DB && 0 < (c = this[f] >> g) && (d = !0, - e = int2char(c)); 0 <= f;) - g < a ? (c = (this[f] & (1 << g) - 1) << a - g, - c |= this[--f] >> (g += this.DB - a)) : (c = this[f] >> (g -= a) & b, - 0 >= g && (g += this.DB, - --f)), - 0 < c && (d = !0), - d && (e += int2char(c)); - return d ? e : "0" - } - - function bnNegate() { - var a = nbi(); - BigInteger.ZERO.subTo(this, a); - return a - } - - function bnAbs() { - return 0 > this.s ? this.negate() : this - } - - function bnCompareTo(a) { - var b = this.s - a.s; - if (0 != b) - return b; - var c = this.t, - b = c - a.t; - if (0 != b) - return 0 > this.s ? -b : b; - for (; 0 <= --c;) - if (0 != (b = this[c] - a[c])) - return b; - return 0 - } - - function nbits(a) { - var b = 1, - c; - 0 != (c = a >>> 16) && (a = c, - b += 16); - 0 != (c = a >> 8) && (a = c, - b += 8); - 0 != (c = a >> 4) && (a = c, - b += 4); - 0 != (c = a >> 2) && (a = c, - b += 2); - 0 != a >> 1 && (b += 1); - return b - } - - function bnBitLength() { - return 0 >= this.t ? 0 : this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ this.s & this.DM) - } - - function bnpDLShiftTo(a, b) { - var c; - for (c = this.t - 1; 0 <= c; --c) - b[c + a] = this[c]; - for (c = a - 1; 0 <= c; --c) - b[c] = 0; - b.t = this.t + a; - b.s = this.s - } - - function bnpDRShiftTo(a, b) { - for (var c = a; c < this.t; ++c) - b[c - a] = this[c]; - b.t = Math.max(this.t - a, 0); - b.s = this.s - } - - function bnpLShiftTo(a, b) { - var c = a % this.DB, - d = this.DB - c, - e = (1 << d) - 1, - f = Math.floor(a / this.DB), - g = this.s << c & this.DM, - h; - for (h = this.t - 1; 0 <= h; --h) - b[h + f + 1] = this[h] >> d | g, - g = (this[h] & e) << c; - for (h = f - 1; 0 <= h; --h) - b[h] = 0; - b[f] = g; - b.t = this.t + f + 1; - b.s = this.s; - b.clamp() - } - - function bnpRShiftTo(a, b) { - b.s = this.s; - var c = Math.floor(a / this.DB); - if (c >= this.t) - b.t = 0; - else { - var d = a % this.DB, - e = this.DB - d, - f = (1 << d) - 1; - b[0] = this[c] >> d; - for (var g = c + 1; g < this.t; ++g) - b[g - c - 1] |= (this[g] & f) << e, - b[g - c] = this[g] >> d; - 0 < d && (b[this.t - c - 1] |= (this.s & f) << e); - b.t = this.t - c; - b.clamp() - } - } - - function bnpSubTo(a, b) { - for (var c = 0, d = 0, e = Math.min(a.t, this.t); c < e;) - d += this[c] - a[c], - b[c++] = d & this.DM, - d >>= this.DB; - if (a.t < this.t) { - for (d -= a.s; c < this.t;) - d += this[c], - b[c++] = d & this.DM, - d >>= this.DB; - d += this.s - } else { - for (d += this.s; c < a.t;) - d -= a[c], - b[c++] = d & this.DM, - d >>= this.DB; - d -= a.s - } - b.s = 0 > d ? -1 : 0; - - 1 > d ? b[c++] = this.DV + d : 0 < d && (b[c++] = d); - b.t = c; - b.clamp() - } - - function bnpMultiplyTo(a, b) { - var c = this.abs(), - d = a.abs(), - e = c.t; - for (b.t = e + d.t; 0 <= --e;) - b[e] = 0; - for (e = 0; e < d.t; ++e) - b[e + c.t] = c.am(0, d[e], b, e, 0, c.t); - b.s = 0; - b.clamp(); - this.s != a.s && BigInteger.ZERO.subTo(b, b) - } - - function bnpSquareTo(a) { - for (var b = this.abs(), c = a.t = 2 * b.t; 0 <= --c;) - a[c] = 0; - for (c = 0; c < b.t - 1; ++c) { - var d = b.am(c, b[c], a, 2 * c, 0, 1); - (a[c + b.t] += b.am(c + 1, 2 * b[c], a, 2 * c + 1, d, b.t - c - 1)) >= b.DV && (a[c + b.t] -= b.DV, - a[c + b.t + 1] = 1) - } - 0 < a.t && (a[a.t - 1] += b.am(c, b[c], a, 2 * c, 0, 1)); - a.s = 0; - a.clamp() - } - - function bnpDivRemTo(a, b, c) { - var d = a.abs(); - if (!(0 >= d.t)) { - var e = this.abs(); - if (e.t < d.t) - null != b && b.fromInt(0), - null != c && this.copyTo(c); - else { - null == c && (c = nbi()); - var f = nbi(), - g = this.s; - a = a.s; - var h = this.DB - nbits(d[d.t - 1]); - 0 < h ? (d.lShiftTo(h, f), - e.lShiftTo(h, c)) : (d.copyTo(f), - e.copyTo(c)); - d = f.t; - e = f[d - 1]; - if (0 != e) { - var k = e * (1 << this.F1) + (1 < d ? f[d - 2] >> this.F2 : 0), - l = this.FV / k, - k = (1 << this.F1) / k, - p = 1 << this.F2, - n = c.t, - q = n - d, - m = null == b ? nbi() : b; - f.dlShiftTo(q, m); - 0 <= c.compareTo(m) && (c[c.t++] = 1, - c.subTo(m, c)); - BigInteger.ONE.dlShiftTo(d, m); - for (m.subTo(f, f); f.t < d;) - f[f.t++] = 0; - for (; 0 <= --q;) { - var r = c[--n] == e ? this.DM : Math.floor(c[n] * l + (c[n - 1] + p) * k); - if ((c[n] += f.am(0, r, c, q, 0, d)) < r) - for (f.dlShiftTo(q, m), - c.subTo(m, c); c[n] < --r;) - c.subTo(m, c) - } - null != b && (c.drShiftTo(d, b), - g != a && BigInteger.ZERO.subTo(b, b)); - c.t = d; - c.clamp(); - 0 < h && c.rShiftTo(h, c); - 0 > g && BigInteger.ZERO.subTo(c, c) - } - } - } - } - - function bnMod(a) { - var b = nbi(); - this.abs().divRemTo(a, null, b); - 0 > this.s && 0 < b.compareTo(BigInteger.ZERO) && a.subTo(b, b); - return b - } - - function Classic(a) { - this.m = a - } - - function cConvert(a) { - return 0 > a.s || 0 <= a.compareTo(this.m) ? a.mod(this.m) : a - } - - function cRevert(a) { - return a - } - - function cReduce(a) { - a.divRemTo(this.m, null, a) - } - - function cMulTo(a, b, c) { - a.multiplyTo(b, c); - this.reduce(c) - } - - function cSqrTo(a, b) { - a.squareTo(b); - this.reduce(b) - } - Classic.prototype.convert = cConvert; - Classic.prototype.revert = cRevert; - Classic.prototype.reduce = cReduce; - Classic.prototype.mulTo = cMulTo; - Classic.prototype.sqrTo = cSqrTo; - - function bnpInvDigit() { - if (1 > this.t) - return 0; - var a = this[0]; - if (0 == (a & 1)) - return 0; - var b = a & 3, - b = b * (2 - (a & 15) * b) & 15, - b = b * (2 - (a & 255) * b) & 255, - b = b * (2 - ((a & 65535) * b & 65535)) & 65535, - b = b * (2 - a * b % this.DV) % this.DV; - return 0 < b ? this.DV - b : -b - } - - function Montgomery(a) { - this.m = a; - this.mp = a.invDigit(); - this.mpl = this.mp & 32767; - this.mph = this.mp >> 15; - this.um = (1 << a.DB - 15) - 1; - this.mt2 = 2 * a.t - } - - function montConvert(a) { - var b = nbi(); - a.abs().dlShiftTo(this.m.t, b); - b.divRemTo(this.m, null, b); - 0 > a.s && 0 < b.compareTo(BigInteger.ZERO) && this.m.subTo(b, b); - return b - } - - function montRevert(a) { - var b = nbi(); - a.copyTo(b); - this.reduce(b); - return b - } - - function montReduce(a) { - for (; a.t <= this.mt2;) - a[a.t++] = 0; - for (var b = 0; b < this.m.t; ++b) { - var c = a[b] & 32767, - d = c * this.mpl + ((c * this.mph + (a[b] >> 15) * this.mpl & this.um) << 15) & a.DM, - c = b + this.m.t; - for (a[c] += this.m.am(0, d, a, b, 0, this.m.t); a[c] >= a.DV;) - a[c] -= a.DV, - a[++c]++ - } - a.clamp(); - a.drShiftTo(this.m.t, a); - 0 <= a.compareTo(this.m) && a.subTo(this.m, a) - } - - function montSqrTo(a, b) { - a.squareTo(b); - this.reduce(b) - } - - function montMulTo(a, b, c) { - a.multiplyTo(b, c); - this.reduce(c) - } - Montgomery.prototype.convert = montConvert; - Montgomery.prototype.revert = montRevert; - Montgomery.prototype.reduce = montReduce; - Montgomery.prototype.mulTo = montMulTo; - Montgomery.prototype.sqrTo = montSqrTo; - - function bnpIsEven() { - return 0 == (0 < this.t ? this[0] & 1 : this.s) - } - - function bnpExp(a, b) { - if (4294967295 < a || 1 > a) - return BigInteger.ONE; - var c = nbi(), - d = nbi(), - e = b.convert(this), - f = nbits(a) - 1; - for (e.copyTo(c); 0 <= --f;) - if (b.sqrTo(c, d), - 0 < (a & 1 << f)) - b.mulTo(d, e, c); - else - var g = c, - c = d, - d = g; - return b.revert(c) - } - - function bnModPowInt(a, b) { - var c; - c = 256 > a || b.isEven() ? new Classic(b) : new Montgomery(b); - return this.exp(a, c) - } - BigInteger.prototype.copyTo = bnpCopyTo; - BigInteger.prototype.fromInt = bnpFromInt; - BigInteger.prototype.fromString = bnpFromString; - BigInteger.prototype.clamp = bnpClamp; - BigInteger.prototype.dlShiftTo = bnpDLShiftTo; - BigInteger.prototype.drShiftTo = bnpDRShiftTo; - BigInteger.prototype.lShiftTo = bnpLShiftTo; - BigInteger.prototype.rShiftTo = bnpRShiftTo; - BigInteger.prototype.subTo = bnpSubTo; - BigInteger.prototype.multiplyTo = bnpMultiplyTo; - BigInteger.prototype.squareTo = bnpSquareTo; - BigInteger.prototype.divRemTo = bnpDivRemTo; - BigInteger.prototype.invDigit = bnpInvDigit; - BigInteger.prototype.isEven = bnpIsEven; - BigInteger.prototype.exp = bnpExp; - BigInteger.prototype.toString = bnToString; - BigInteger.prototype.negate = bnNegate; - BigInteger.prototype.abs = bnAbs; - BigInteger.prototype.compareTo = bnCompareTo; - BigInteger.prototype.bitLength = bnBitLength; - BigInteger.prototype.mod = bnMod; - BigInteger.prototype.modPowInt = bnModPowInt; - BigInteger.ZERO = nbv(0); - BigInteger.ONE = nbv(1); - - function bnClone() { - var a = nbi(); - this.copyTo(a); - return a - } - - function bnIntValue() { - if (0 > this.s) { - if (1 == this.t) - return this[0] - this.DV; - if (0 == this.t) - return -1 - } else { - if (1 == this.t) - return this[0]; - if (0 == this.t) - return 0 - } - return (this[1] & (1 << 32 - this.DB) - 1) << this.DB | this[0] - } - - function bnByteValue() { - return 0 == this.t ? this.s : this[0] << 24 >> 24 - } - - function bnShortValue() { - return 0 == this.t ? this.s : this[0] << 16 >> 16 - } - - function bnpChunkSize(a) { - return Math.floor(Math.LN2 * this.DB / Math.log(a)) - } - - function bnSigNum() { - return 0 > this.s ? -1 : 0 >= this.t || 1 == this.t && 0 >= this[0] ? 0 : 1 - } - - function bnpToRadix(a) { - null == a && (a = 10); - if (0 == this.signum() || 2 > a || 36 < a) - return "0"; - var b = this.chunkSize(a), - b = Math.pow(a, b), - c = nbv(b), - d = nbi(), - e = nbi(), - f = ""; - for (this.divRemTo(c, d, e); 0 < d.signum();) - f = (b + e.intValue()).toString(a).substr(1) + f, - d.divRemTo(c, d, e); - return e.intValue().toString(a) + f - } - - function bnpFromRadix(a, b) { - this.fromInt(0); - null == b && (b = 10); - for (var c = this.chunkSize(b), d = Math.pow(b, c), e = !1, f = 0, g = 0, h = 0; h < a.length; ++h) { - var k = intAt(a, h); - 0 > k ? "-" == a.charAt(h) && 0 == this.signum() && (e = !0) : (g = b * g + k, - ++f >= c && (this.dMultiply(d), - this.dAddOffset(g, 0), - g = f = 0)) - } - 0 < f && (this.dMultiply(Math.pow(b, f)), - this.dAddOffset(g, 0)); - e && BigInteger.ZERO.subTo(this, this) - } - - function bnpFromNumber(a, b, c) { - if ("number" == typeof b) - if (2 > a) - this.fromInt(1); - else - for (this.fromNumber(a, c), - this.testBit(a - 1) || this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this), - this.isEven() && this.dAddOffset(1, 0); !this.isProbablePrime(b);) - this.dAddOffset(2, 0), - this.bitLength() > a && this.subTo(BigInteger.ONE.shiftLeft(a - 1), this); - else { - c = []; - var d = a & 7; - c.length = (a >> 3) + 1; - b.nextBytes(c); - c[0] = 0 < d ? c[0] & (1 << d) - 1 : 0; - this.fromString(c, 256) - } - } - - function bnToByteArray() { - var a = this.t, - b = []; - b[0] = this.s; - var c = this.DB - a * this.DB % 8, - d, e = 0; - if (0 < a--) - for (c < this.DB && (d = this[a] >> c) != (this.s & this.DM) >> c && (b[e++] = d | this.s << this.DB - c); 0 <= a;) - if (8 > c ? (d = (this[a] & (1 << c) - 1) << 8 - c, - d |= this[--a] >> (c += this.DB - 8)) : (d = this[a] >> (c -= 8) & 255, - 0 >= c && (c += this.DB, - --a)), - 0 != (d & 128) && (d |= -256), - 0 == e && (this.s & 128) != (d & 128) && ++e, - 0 < e || d != this.s) - b[e++] = d; - return b - } - - function bnEquals(a) { - return 0 == this.compareTo(a) - } - - function bnMin(a) { - return 0 > this.compareTo(a) ? this : a - } - - function bnMax(a) { - return 0 < this.compareTo(a) ? this : a - } - - function bnpBitwiseTo(a, b, c) { - var d, e, f = Math.min(a.t, this.t); - for (d = 0; d < f; ++d) - c[d] = b(this[d], a[d]); - if (a.t < this.t) { - e = a.s & this.DM; - for (d = f; d < this.t; ++d) - c[d] = b(this[d], e); - c.t = this.t - } else { - e = this.s & this.DM; - for (d = f; d < a.t; ++d) - c[d] = b(e, a[d]); - c.t = a.t - } - c.s = b(this.s, a.s); - c.clamp() - } - - function op_and(a, b) { - return a & b - } - - function bnAnd(a) { - var b = nbi(); - this.bitwiseTo(a, op_and, b); - return b - } - - function op_or(a, b) { - return a | b - } - - function bnOr(a) { - var b = nbi(); - this.bitwiseTo(a, op_or, b); - return b - } - - function op_xor(a, b) { - return a ^ b - } - - function bnXor(a) { - var b = nbi(); - this.bitwiseTo(a, op_xor, b); - return b - } - - function op_andnot(a, b) { - return a & ~b - } - - function bnAndNot(a) { - var b = nbi(); - this.bitwiseTo(a, op_andnot, b); - return b - } - - function bnNot() { - for (var a = nbi(), b = 0; b < this.t; ++b) - a[b] = this.DM & ~this[b]; - a.t = this.t; - a.s = ~this.s; - return a - } - - function bnShiftLeft(a) { - var b = nbi(); - 0 > a ? this.rShiftTo(-a, b) : this.lShiftTo(a, b); - return b - } - - function bnShiftRight(a) { - var b = nbi(); - 0 > a ? this.lShiftTo(-a, b) : this.rShiftTo(a, b); - return b - } - - function lbit(a) { - if (0 == a) - return -1; - var b = 0; - 0 == (a & 65535) && (a >>= 16, - b += 16); - 0 == (a & 255) && (a >>= 8, - b += 8); - 0 == (a & 15) && (a >>= 4, - b += 4); - 0 == (a & 3) && (a >>= 2, - b += 2); - 0 == (a & 1) && ++b; - return b - } - - function bnGetLowestSetBit() { - for (var a = 0; a < this.t; ++a) - if (0 != this[a]) - return a * this.DB + lbit(this[a]); - return 0 > this.s ? this.t * this.DB : -1 - } - - function cbit(a) { - for (var b = 0; 0 != a;) - a &= a - 1, - ++b; - return b - } - - function bnBitCount() { - for (var a = 0, b = this.s & this.DM, c = 0; c < this.t; ++c) - a += cbit(this[c] ^ b); - return a - } - - function bnTestBit(a) { - var b = Math.floor(a / this.DB); - return b >= this.t ? 0 != this.s : 0 != (this[b] & 1 << a % this.DB) - } - - function bnpChangeBit(a, b) { - var c = BigInteger.ONE.shiftLeft(a); - this.bitwiseTo(c, b, c); - return c - } - - function bnSetBit(a) { - return this.changeBit(a, op_or) - } - - function bnClearBit(a) { - return this.changeBit(a, op_andnot) - } - - function bnFlipBit(a) { - return this.changeBit(a, op_xor) - } - - function bnpAddTo(a, b) { - for (var c = 0, d = 0, e = Math.min(a.t, this.t); c < e;) - d += this[c] + a[c], - b[c++] = d & this.DM, - d >>= this.DB; - if (a.t < this.t) { - for (d += a.s; c < this.t;) - d += this[c], - b[c++] = d & this.DM, - d >>= this.DB; - d += this.s - } else { - for (d += this.s; c < a.t;) - d += a[c], - b[c++] = d & this.DM, - d >>= this.DB; - d += a.s - } - b.s = 0 > d ? -1 : 0; - 0 < d ? b[c++] = d : -1 > d && (b[c++] = this.DV + d); - b.t = c; - b.clamp() - } - - function bnAdd(a) { - var b = nbi(); - this.addTo(a, b); - return b - } - - function bnSubtract(a) { - var b = nbi(); - this.subTo(a, b); - return b - } - - function bnMultiply(a) { - var b = nbi(); - this.multiplyTo(a, b); - return b - } - - function bnSquare() { - var a = nbi(); - this.squareTo(a); - return a - } - - function bnDivide(a) { - var b = nbi(); - this.divRemTo(a, b, null); - return b - } - - function bnRemainder(a) { - var b = nbi(); - this.divRemTo(a, null, b); - return b - } - - function bnDivideAndRemainder(a) { - var b = nbi(), - c = nbi(); - this.divRemTo(a, b, c); - return [b, c] - } - - function bnpDMultiply(a) { - this[this.t] = this.am(0, a - 1, this, 0, 0, this.t); - ++this.t; - this.clamp() - } - - function bnpDAddOffset(a, b) { - if (0 != a) { - for (; this.t <= b;) - this[this.t++] = 0; - for (this[b] += a; this[b] >= this.DV;) - this[b] -= this.DV, - ++b >= this.t && (this[this.t++] = 0), - ++this[b] - } - } - - function NullExp() {} - - function nNop(a) { - return a - } - - function nMulTo(a, b, c) { - a.multiplyTo(b, c) - } - - function nSqrTo(a, b) { - a.squareTo(b) - } - NullExp.prototype.convert = nNop; - NullExp.prototype.revert = nNop; - NullExp.prototype.mulTo = nMulTo; - NullExp.prototype.sqrTo = nSqrTo; - - function bnPow(a) { - return this.exp(a, new NullExp) - } - - function bnpMultiplyLowerTo(a, b, c) { - var d = Math.min(this.t + a.t, b); - c.s = 0; - for (c.t = d; 0 < d;) - c[--d] = 0; - var e; - for (e = c.t - this.t; d < e; ++d) - c[d + this.t] = this.am(0, a[d], c, d, 0, this.t); - for (e = Math.min(a.t, b); d < e; ++d) - this.am(0, a[d], c, d, 0, b - d); - c.clamp() - } - - function bnpMultiplyUpperTo(a, b, c) { - --b; - var d = c.t = this.t + a.t - b; - for (c.s = 0; 0 <= --d;) - c[d] = 0; - for (d = Math.max(b - this.t, 0); d < a.t; ++d) - c[this.t + d - b] = this.am(b - d, a[d], c, 0, 0, this.t + d - b); - c.clamp(); - c.drShiftTo(1, c) - } - - function Barrett(a) { - this.r2 = nbi(); - this.q3 = nbi(); - BigInteger.ONE.dlShiftTo(2 * a.t, this.r2); - this.mu = this.r2.divide(a); - this.m = a - } - - function barrettConvert(a) { - if (0 > a.s || a.t > 2 * this.m.t) - return a.mod(this.m); - if (0 > a.compareTo(this.m)) - return a; - var b = nbi(); - a.copyTo(b); - this.reduce(b); - return b - } - - function barrettRevert(a) { - return a - } - - function barrettReduce(a) { - a.drShiftTo(this.m.t - 1, this.r2); - a.t > this.m.t + 1 && (a.t = this.m.t + 1, - a.clamp()); - this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3); - for (this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2); 0 > a.compareTo(this.r2);) - a.dAddOffset(1, this.m.t + 1); - for (a.subTo(this.r2, a); 0 <= a.compareTo(this.m);) - a.subTo(this.m, a) - } - - function barrettSqrTo(a, b) { - a.squareTo(b); - this.reduce(b) - } - - function barrettMulTo(a, b, c) { - a.multiplyTo(b, c); - this.reduce(c) - } - Barrett.prototype.convert = barrettConvert; - Barrett.prototype.revert = barrettRevert; - Barrett.prototype.reduce = barrettReduce; - Barrett.prototype.mulTo = barrettMulTo; - Barrett.prototype.sqrTo = barrettSqrTo; - - function bnModPow(a, b) { - var c = a.bitLength(), - d, e = nbv(1), - f; - if (0 >= c) - return e; - d = 18 > c ? 1 : 48 > c ? 3 : 144 > c ? 4 : 768 > c ? 5 : 6; - f = 8 > c ? new Classic(b) : b.isEven() ? new Barrett(b) : new Montgomery(b); - var g = [], - h = 3, - k = d - 1, - l = (1 << d) - 1; - g[1] = f.convert(this); - if (1 < d) - for (c = nbi(), - f.sqrTo(g[1], c); h <= l;) - g[h] = nbi(), - f.mulTo(c, g[h - 2], g[h]), - h += 2; - for (var p = a.t - 1, n, q = !0, m = nbi(), c = nbits(a[p]) - 1; 0 <= p;) { - c >= k ? n = a[p] >> c - k & l : (n = (a[p] & (1 << c + 1) - 1) << k - c, - 0 < p && (n |= a[p - 1] >> this.DB + c - k)); - for (h = d; 0 == (n & 1);) - n >>= 1, - --h; - 0 > (c -= h) && (c += this.DB, - --p); - if (q) - g[n].copyTo(e), - q = !1; - else { - for (; 1 < h;) - f.sqrTo(e, m), - f.sqrTo(m, e), - h -= 2; - 0 < h ? f.sqrTo(e, m) : (h = e, - e = m, - m = h); - f.mulTo(m, g[n], e) - } - for (; 0 <= p && 0 == (a[p] & 1 << c);) - f.sqrTo(e, m), - h = e, - e = m, - m = h, - 0 > --c && (c = this.DB - 1, - --p) - } - return f.revert(e) - } - - function bnGCD(a) { - var b = 0 > this.s ? this.negate() : this.clone(); - a = 0 > a.s ? a.negate() : a.clone(); - if (0 > b.compareTo(a)) { - var c = b, - b = a; - a = c - } - var c = b.getLowestSetBit(), - d = a.getLowestSetBit(); - if (0 > d) - return b; - c < d && (d = c); - 0 < d && (b.rShiftTo(d, b), - a.rShiftTo(d, a)); - for (; 0 < b.signum();) - 0 < (c = b.getLowestSetBit()) && b.rShiftTo(c, b), - 0 < (c = a.getLowestSetBit()) && a.rShiftTo(c, a), - 0 <= b.compareTo(a) ? (b.subTo(a, b), - b.rShiftTo(1, b)) : (a.subTo(b, a), - a.rShiftTo(1, a)); - 0 < d && a.lShiftTo(d, a); - return a - } - - function bnpModInt(a) { - if (0 >= a) - return 0; - var b = this.DV % a, - c = 0 > this.s ? a - 1 : 0; - if (0 < this.t) - if (0 == b) - c = this[0] % a; - else - for (var d = this.t - 1; 0 <= d; --d) - c = (b * c + this[d]) % a; - return c - } - - function bnModInverse(a) { - var b = a.isEven(); - if (this.isEven() && b || 0 == a.signum()) - return BigInteger.ZERO; - for (var c = a.clone(), d = this.clone(), e = nbv(1), f = nbv(0), g = nbv(0), h = nbv(1); 0 != c.signum();) { - for (; c.isEven();) - c.rShiftTo(1, c), - b ? (e.isEven() && f.isEven() || (e.addTo(this, e), - f.subTo(a, f)), - e.rShiftTo(1, e)) : f.isEven() || f.subTo(a, f), - f.rShiftTo(1, f); - for (; d.isEven();) - d.rShiftTo(1, d), - b ? (g.isEven() && h.isEven() || (g.addTo(this, g), - h.subTo(a, h)), - g.rShiftTo(1, g)) : h.isEven() || h.subTo(a, h), - h.rShiftTo(1, h); - 0 <= c.compareTo(d) ? (c.subTo(d, c), - b && e.subTo(g, e), - f.subTo(h, f)) : (d.subTo(c, d), - b && g.subTo(e, g), - h.subTo(f, h)) - } - if (0 != d.compareTo(BigInteger.ONE)) - return BigInteger.ZERO; - if (0 <= h.compareTo(a)) - return h.subtract(a); - if (0 > h.signum()) - h.addTo(a, h); - else - return h; - return 0 > h.signum() ? h.add(a) : h - } - var lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997], - lplim = 67108864 / lowprimes[lowprimes.length - 1]; - - function bnIsProbablePrime(a) { - var b, c = this.abs(); - if (1 == c.t && c[0] <= lowprimes[lowprimes.length - 1]) { - for (b = 0; b < lowprimes.length; ++b) - if (c[0] == lowprimes[b]) - return !0; - return !1 - } - if (c.isEven()) - return !1; - for (b = 1; b < lowprimes.length;) { - for (var d = lowprimes[b], e = b + 1; e < lowprimes.length && d < lplim;) - d *= lowprimes[e++]; - for (d = c.modInt(d); b < e;) - if (0 == d % lowprimes[b++]) - return !1 - } - return c.millerRabin(a) - } - - function bnpMillerRabin(a) { - var b = this.subtract(BigInteger.ONE), - c = b.getLowestSetBit(); - if (0 >= c) - return !1; - var d = b.shiftRight(c); - a = a + 1 >> 1; - a > lowprimes.length && (a = lowprimes.length); - for (var e = nbi(), f = 0; f < a; ++f) { - e.fromInt(lowprimes[Math.floor(Math.random() * lowprimes.length)]); - var g = e.modPow(d, this); - if (0 != g.compareTo(BigInteger.ONE) && 0 != g.compareTo(b)) { - for (var h = 1; h++ < c && 0 != g.compareTo(b);) - if (g = g.modPowInt(2, this), - 0 == g.compareTo(BigInteger.ONE)) - return !1; - if (0 != g.compareTo(b)) - return !1 - } - } - return !0 - } - BigInteger.prototype.chunkSize = bnpChunkSize; - BigInteger.prototype.toRadix = bnpToRadix; - BigInteger.prototype.fromRadix = bnpFromRadix; - BigInteger.prototype.fromNumber = bnpFromNumber; - BigInteger.prototype.bitwiseTo = bnpBitwiseTo; - BigInteger.prototype.changeBit = bnpChangeBit; - BigInteger.prototype.addTo = bnpAddTo; - BigInteger.prototype.dMultiply = bnpDMultiply; - BigInteger.prototype.dAddOffset = bnpDAddOffset; - BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; - BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; - BigInteger.prototype.modInt = bnpModInt; - BigInteger.prototype.millerRabin = bnpMillerRabin; - BigInteger.prototype.clone = bnClone; - BigInteger.prototype.intValue = bnIntValue; - BigInteger.prototype.byteValue = bnByteValue; - BigInteger.prototype.shortValue = bnShortValue; - BigInteger.prototype.signum = bnSigNum; - BigInteger.prototype.toByteArray = bnToByteArray; - BigInteger.prototype.equals = bnEquals; - BigInteger.prototype.min = bnMin; - BigInteger.prototype.max = bnMax; - BigInteger.prototype.and = bnAnd; - BigInteger.prototype.or = bnOr; - BigInteger.prototype.xor = bnXor; - BigInteger.prototype.andNot = bnAndNot; - BigInteger.prototype.not = bnNot; - BigInteger.prototype.shiftLeft = bnShiftLeft; - BigInteger.prototype.shiftRight = bnShiftRight; - BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; - BigInteger.prototype.bitCount = bnBitCount; - BigInteger.prototype.testBit = bnTestBit; - BigInteger.prototype.setBit = bnSetBit; - BigInteger.prototype.clearBit = bnClearBit; - BigInteger.prototype.flipBit = bnFlipBit; - BigInteger.prototype.add = bnAdd; - BigInteger.prototype.subtract = bnSubtract; - BigInteger.prototype.multiply = bnMultiply; - BigInteger.prototype.divide = bnDivide; - BigInteger.prototype.remainder = bnRemainder; - BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; - BigInteger.prototype.modPow = bnModPow; - BigInteger.prototype.modInverse = bnModInverse; - BigInteger.prototype.pow = bnPow; - BigInteger.prototype.gcd = bnGCD; - BigInteger.prototype.isProbablePrime = bnIsProbablePrime; - BigInteger.prototype.square = bnSquare; - - function Arcfour() { - this.j = this.i = 0; - this.S = [] - } - - function ARC4init(a) { - var b, c, d; - for (b = 0; 256 > b; ++b) - this.S[b] = b; - for (b = c = 0; 256 > b; ++b) - c = c + this.S[b] + a[b % a.length] & 255, - d = this.S[b], - this.S[b] = this.S[c], - this.S[c] = d; - this.j = this.i = 0 - } - - function ARC4next() { - var a; - this.i = this.i + 1 & 255; - this.j = this.j + this.S[this.i] & 255; - a = this.S[this.i]; - this.S[this.i] = this.S[this.j]; - this.S[this.j] = a; - return this.S[a + this.S[this.i] & 255] - } - Arcfour.prototype.init = ARC4init; - Arcfour.prototype.next = ARC4next; - - function prng_newstate() { - return new Arcfour - } - var rng_psize = 256, - rng_state, rng_pool, rng_pptr; - - function rng_seed_int(a) { - rng_pool[rng_pptr++] ^= a & 255; - rng_pool[rng_pptr++] ^= a >> 8 & 255; - rng_pool[rng_pptr++] ^= a >> 16 & 255; - rng_pool[rng_pptr++] ^= a >> 24 & 255; - rng_pptr >= rng_psize && (rng_pptr -= rng_psize) - } - - function rng_seed_time() { - rng_seed_int((new Date).getTime()) - } - if (null == rng_pool) { - rng_pool = []; - rng_pptr = 0; - var t; - if ("Netscape" == navigator.appName && "5" > navigator.appVersion && window.crypto) { - var z = window.crypto.random(32); - for (t = 0; t < z.length; ++t) - rng_pool[rng_pptr++] = z.charCodeAt(t) & 255 - } - for (; rng_pptr < rng_psize;) - t = Math.floor(65536 * Math.random()), - rng_pool[rng_pptr++] = t >>> 8, - rng_pool[rng_pptr++] = t & 255; - rng_pptr = 0; - rng_seed_time() - } - - function rng_get_byte() { - if (null == rng_state) { - rng_seed_time(); - rng_state = prng_newstate(); - rng_state.init(rng_pool); - for (rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) - rng_pool[rng_pptr] = 0; - rng_pptr = 0 - } - return rng_state.next() - } - - function rng_get_bytes(a) { - var b; - for (b = 0; b < a.length; ++b) - a[b] = rng_get_byte() - } - - function SecureRandom() {} - SecureRandom.prototype.nextBytes = rng_get_bytes; - var KJUR = {}; - //"undefined" != typeof KJUR && KJUR || (KJUR = {}); - "undefined" != typeof KJUR.crypto && KJUR.crypto || (KJUR.crypto = {}); - KJUR.crypto.Util = new function () { - this.DIGESTINFOHEAD = { - sha1: "3021300906052b0e03021a05000414", - sha224: "302d300d06096086480165030402040500041c", - sha256: "3031300d060960864801650304020105000420", - sha384: "3041300d060960864801650304020205000430", - sha512: "3051300d060960864801650304020305000440", - md2: "3020300c06082a864886f70d020205000410", - md5: "3020300c06082a864886f70d020505000410", - ripemd160: "3021300906052b2403020105000414" - }; - this.DEFAULTPROVIDER = { - md5: "cryptojs", - sha1: "cryptojs", - sha224: "cryptojs", - sha256: "cryptojs", - sha384: "cryptojs", - sha512: "cryptojs", - ripemd160: "cryptojs", - hmacmd5: "cryptojs", - hmacsha1: "cryptojs", - hmacsha224: "cryptojs", - hmacsha256: "cryptojs", - hmacsha384: "cryptojs", - hmacsha512: "cryptojs", - hmacripemd160: "cryptojs", - sm3: "cryptojs", - MD5withRSA: "cryptojs/jsrsa", - SHA1withRSA: "cryptojs/jsrsa", - SHA224withRSA: "cryptojs/jsrsa", - SHA256withRSA: "cryptojs/jsrsa", - SHA384withRSA: "cryptojs/jsrsa", - SHA512withRSA: "cryptojs/jsrsa", - RIPEMD160withRSA: "cryptojs/jsrsa", - MD5withECDSA: "cryptojs/jsrsa", - SHA1withECDSA: "cryptojs/jsrsa", - SHA224withECDSA: "cryptojs/jsrsa", - SHA256withECDSA: "cryptojs/jsrsa", - SHA384withECDSA: "cryptojs/jsrsa", - SHA512withECDSA: "cryptojs/jsrsa", - RIPEMD160withECDSA: "cryptojs/jsrsa", - SHA1withDSA: "cryptojs/jsrsa", - SHA224withDSA: "cryptojs/jsrsa", - SHA256withDSA: "cryptojs/jsrsa", - MD5withRSAandMGF1: "cryptojs/jsrsa", - SHA1withRSAandMGF1: "cryptojs/jsrsa", - SHA224withRSAandMGF1: "cryptojs/jsrsa", - SHA256withRSAandMGF1: "cryptojs/jsrsa", - SHA384withRSAandMGF1: "cryptojs/jsrsa", - SHA512withRSAandMGF1: "cryptojs/jsrsa", - RIPEMD160withRSAandMGF1: "cryptojs/jsrsa" - }; - this.CRYPTOJSMESSAGEDIGESTNAME = { - md5: "CryptoJS.algo.MD5", - sha1: "CryptoJS.algo.SHA1", - sha224: "CryptoJS.algo.SHA224", - sha256: "CryptoJS.algo.SHA256", - sha384: "CryptoJS.algo.SHA384", - sha512: "CryptoJS.algo.SHA512", - ripemd160: "CryptoJS.algo.RIPEMD160", - sm3: "CryptoJS.algo.SM3" - }; - this.getDigestInfoHex = function (a, b) { - if ("undefined" == typeof this.DIGESTINFOHEAD[b]) - throw "alg not supported in Util.DIGESTINFOHEAD: " + b; - return this.DIGESTINFOHEAD[b] + a - }; - this.getPaddedDigestInfoHex = function (a, b, c) { - var d = this.getDigestInfoHex(a, b); - a = c / 4; - if (d.length + 22 > a) - throw "key is too short for SigAlg: keylen=" + c + "," + b; - b = "00" + d; - c = ""; - a = a - 4 - b.length; - for (d = 0; d < a; d += 2) - c += "ff"; - return "0001" + c + b - }; - this.hashString = function (a, b) { - return (new KJUR.crypto.MessageDigest({ - alg: b - })).digestString(a) - }; - this.hashHex = function (a, b) { - return (new KJUR.crypto.MessageDigest({ - alg: b - })).digestHex(a) - }; - this.sha1 = function (a) { - return (new KJUR.crypto.MessageDigest({ - alg: "sha1", - prov: "cryptojs" - })).digestString(a) - }; - this.sha256 = function (a) { - return (new KJUR.crypto.MessageDigest({ - alg: "sha256", - prov: "cryptojs" - })).digestString(a) - }; - this.sha256Hex = function (a) { - return (new KJUR.crypto.MessageDigest({ - alg: "sha256", - prov: "cryptojs" - })).digestHex(a) - }; - this.sha512 = function (a) { - return (new KJUR.crypto.MessageDigest({ - alg: "sha512", - prov: "cryptojs" - })).digestString(a) - }; - this.sha512Hex = function (a) { - return (new KJUR.crypto.MessageDigest({ - alg: "sha512", - prov: "cryptojs" - })).digestHex(a) - }; - this.md5 = function (a) { - return (new KJUR.crypto.MessageDigest({ - alg: "md5", - prov: "cryptojs" - })).digestString(a) - }; - this.ripemd160 = function (a) { - return (new KJUR.crypto.MessageDigest({ - alg: "ripemd160", - prov: "cryptojs" - })).digestString(a) - }; - this.getCryptoJSMDByName = function (a) {} - }; - KJUR.crypto.MessageDigest = function (a) { - this.setAlgAndProvider = function (a, c) { - null != a && void 0 === c && (c = KJUR.crypto.Util.DEFAULTPROVIDER[a]); - if (-1 != ":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:sm3:".indexOf(a) && "cryptojs" == c) { - try { - this.md = eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[a]).create() - } catch (d) { - throw "setAlgAndProvider hash alg set fail alg=" + a + "/" + d; - } - this.updateString = function (a) { - this.md.update(a) - }; - this.updateHex = function (a) { - a = CryptoJS.enc.Hex.parse(a); - this.md.update(a) - }; - this.digest = function () { - return this.md.finalize().toString(CryptoJS.enc.Hex) - }; - this.digestString = function (a) { - this.updateString(a); - return this.digest() - }; - this.digestHex = function (a) { - this.updateHex(a); - return this.digest() - } - } - if (-1 != ":sha256:".indexOf(a) && "sjcl" == c) { - try { - this.md = new sjcl.hash.sha256 - } catch (d) { - throw "setAlgAndProvider hash alg set fail alg=" + a + "/" + d; - } - this.updateString = function (a) { - this.md.update(a) - }; - this.updateHex = function (a) { - a = sjcl.codec.hex.toBits(a); - this.md.update(a) - }; - this.digest = function () { - var a = this.md.finalize(); - return sjcl.codec.hex.fromBits(a) - }; - this.digestString = function (a) { - this.updateString(a); - return this.digest() - }; - this.digestHex = function (a) { - this.updateHex(a); - return this.digest() - } - } - }; - this.updateString = function (a) { - throw "updateString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName; - }; - this.updateHex = function (a) { - throw "updateHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName; - }; - this.digest = function () { - throw "digest() not supported for this alg/prov: " + this.algName + "/" + this.provName; - }; - this.digestString = function (a) { - throw "digestString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName; - }; - this.digestHex = function (a) { - throw "digestHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName; - }; - void 0 !== a && void 0 !== a.alg && (this.algName = a.alg, - void 0 === a.prov && (this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]), - this.setAlgAndProvider(this.algName, this.provName)) - }; - KJUR.crypto.Mac = function (a) { - this.setAlgAndProvider = function (a, c) { - null == a && (a = "hmacsha1"); - a = a.toLowerCase(); - if ("hmac" != a.substr(0, 4)) - throw "setAlgAndProvider unsupported HMAC alg: " + a; - void 0 === c && (c = KJUR.crypto.Util.DEFAULTPROVIDER[a]); - this.algProv = a + "/" + c; - var d = a.substr(4); - if (-1 != ":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:".indexOf(d) && "cryptojs" == c) { - try { - var e = eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[d]); - this.mac = CryptoJS.algo.HMAC.create(e, this.pass) - } catch (f) { - throw "setAlgAndProvider hash alg set fail hashAlg=" + d + "/" + f; - } - this.updateString = function (a) { - this.mac.update(a) - }; - this.updateHex = function (a) { - a = CryptoJS.enc.Hex.parse(a); - this.mac.update(a) - }; - this.doFinal = function () { - return this.mac.finalize().toString(CryptoJS.enc.Hex) - }; - this.doFinalString = function (a) { - this.updateString(a); - return this.doFinal() - }; - this.doFinalHex = function (a) { - this.updateHex(a); - return this.doFinal() - } - } - }; - this.updateString = function (a) { - throw "updateString(str) not supported for this alg/prov: " + this.algProv; - }; - this.updateHex = function (a) { - throw "updateHex(hex) not supported for this alg/prov: " + this.algProv; - }; - this.doFinal = function () { - throw "digest() not supported for this alg/prov: " + this.algProv; - }; - this.doFinalString = function (a) { - throw "digestString(str) not supported for this alg/prov: " + this.algProv; - }; - this.doFinalHex = function (a) { - throw "digestHex(hex) not supported for this alg/prov: " + this.algProv; - }; - void 0 !== a && (void 0 !== a.pass && (this.pass = a.pass), - void 0 !== a.alg && (this.algName = a.alg, - void 0 === a.prov && (this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]), - this.setAlgAndProvider(this.algName, this.provName))) - }; - KJUR.crypto.Signature = function (a) { - var b = null; - this._setAlgNames = function () { - this.algName.match(/^(.+)with(.+)$/) && (this.mdAlgName = RegExp.$1.toLowerCase(), - this.pubkeyAlgName = RegExp.$2.toLowerCase()) - }; - this._zeroPaddingOfSignature = function (a, b) { - for (var e = "", f = b / 4 - a.length, g = 0; g < f; g++) - e += "0"; - return e + a - }; - this.setAlgAndProvider = function (a, b) { - this._setAlgNames(); - if ("cryptojs/jsrsa" != b) - throw "provider not supported: " + b; - if (-1 != ":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:sm3:".indexOf(this.mdAlgName)) { - try { - this.md = new KJUR.crypto.MessageDigest({ - alg: this.mdAlgName - }) - } catch (e) { - throw "setAlgAndProvider hash alg set fail alg=" + this.mdAlgName + "/" + e; - } - this.init = function (a, c) { - var b = null; - try { - b = void 0 === c ? KEYUTIL.getKey(a) : KEYUTIL.getKey(a, c) - } catch (d) { - throw "init failed:" + d; - } - if (!0 === b.isPrivate) - this.prvKey = b, - this.state = "SIGN"; - else if (!0 === b.isPublic) - this.pubKey = b, - this.state = "VERIFY"; - else - throw "init failed.:" + b; - }; - this.initSign = function (a) { - "string" == typeof a.ecprvhex && "string" == typeof a.eccurvename ? (this.ecprvhex = a.ecprvhex, - this.eccurvename = a.eccurvename) : this.prvKey = a; - this.state = "SIGN" - }; - this.initVerifyByPublicKey = function (a) { - "string" == typeof a.ecpubhex && "string" == typeof a.eccurvename ? (this.ecpubhex = a.ecpubhex, - this.eccurvename = a.eccurvename) : a instanceof KJUR.crypto.ECDSA ? this.pubKey = a : a instanceof RSAKey && (this.pubKey = a); - this.state = "VERIFY" - }; - this.initVerifyByCertificatePEM = function (a) { - var c = new X509; - c.readCertPEM(a); - this.pubKey = c.subjectPublicKeyRSA; - this.state = "VERIFY" - }; - this.updateString = function (a) { - this.md.updateString(a) - }; - this.updateHex = function (a) { - this.md.updateHex(a) - }; - this.sign = function () { - "sm2" != this.eccurvename && (this.sHashHex = this.md.digest()); - if ("undefined" != typeof this.ecprvhex && "undefined" != typeof this.eccurvename) { - if ("sm2" == this.eccurvename) { - var a = new KJUR.crypto.SM3withSM2({ - curve: this.eccurvename - }), - c = a.ecparams.G, - b = c.multiply(new BigInteger(this.ecprvhex, 16)), - d = b.getX().toBigInteger().toRadix(16) + b.getY().toBigInteger().toRadix(16), - b = new SM3Digest, - c = (new SM3Digest).GetZ(c, d), - c = b.GetWords(b.GetHex(c).toString()), - d = CryptoJS.enc.Utf8.stringify(this.md.md._data), - d = CryptoJS.enc.Utf8.parse(d).toString(), - d = b.GetWords(d), - k = Array(b.GetDigestSize()); - b.BlockUpdate(c, 0, c.length); - b.BlockUpdate(d, 0, d.length); - b.DoFinal(k, 0); - this.sHashHex = b.GetHex(k).toString() - } else - a = new KJUR.crypto.ECDSA({ - curve: this.eccurvename - }); - this.hSign = a.signHex(this.sHashHex, this.ecprvhex) - } else if ("rsaandmgf1" == this.pubkeyAlgName) - this.hSign = this.prvKey.signWithMessageHashPSS(this.sHashHex, this.mdAlgName, this.pssSaltLen); - else if ("rsa" == this.pubkeyAlgName) - this.hSign = this.prvKey.signWithMessageHash(this.sHashHex, this.mdAlgName); - else if (this.prvKey instanceof KJUR.crypto.ECDSA) - this.hSign = this.prvKey.signWithMessageHash(this.sHashHex); - else if (this.prvKey instanceof KJUR.crypto.DSA) - this.hSign = this.prvKey.signWithMessageHash(this.sHashHex); - else - throw "Signature: unsupported public key alg: " + this.pubkeyAlgName; - return this.hSign - }; - this.signString = function (a) { - this.updateString(a); - this.sign() - }; - this.signHex = function (a) { - this.updateHex(a); - this.sign() - }; - this.verify = function (a) { - "sm2" != this.eccurvename && (this.sHashHex = this.md.digest()); - if ("undefined" != typeof this.ecpubhex && "undefined" != typeof this.eccurvename) { - if ("sm2" == this.eccurvename) { - var c = new KJUR.crypto.SM3withSM2({ - curve: this.eccurvename - }), - b = c.ecparams.G, - d = this.ecpubhex.substr(2, 128), - k = new SM3Digest, - b = (new SM3Digest).GetZ(b, d), - b = k.GetWords(k.GetHex(b).toString()), - d = CryptoJS.enc.Utf8.stringify(this.md.md._data), - d = CryptoJS.enc.Utf8.parse(d).toString(), - d = k.GetWords(d), - l = Array(k.GetDigestSize()); - k.BlockUpdate(b, 0, b.length); - k.BlockUpdate(d, 0, d.length); - k.DoFinal(l, 0); - this.sHashHex = k.GetHex(l).toString() - } else - c = new KJUR.crypto.ECDSA({ - curve: this.eccurvename - }); - return c.verifyHex(this.sHashHex, a, this.ecpubhex) - } - if ("rsaandmgf1" == this.pubkeyAlgName) - return this.pubKey.verifyWithMessageHashPSS(this.sHashHex, a, this.mdAlgName, this.pssSaltLen); - if ("rsa" == this.pubkeyAlgName || this.pubKey instanceof KJUR.crypto.ECDSA || this.pubKey instanceof KJUR.crypto.DSA) - return this.pubKey.verifyWithMessageHash(this.sHashHex, a); - throw "Signature: unsupported public key alg: " + this.pubkeyAlgName; - } - } - }; - this.init = function (a, b) { - throw "init(key, pass) not supported for this alg:prov=" + this.algProvName; - }; - this.initVerifyByPublicKey = function (a) { - throw "initVerifyByPublicKey(rsaPubKeyy) not supported for this alg:prov=" + this.algProvName; - }; - this.initVerifyByCertificatePEM = function (a) { - throw "initVerifyByCertificatePEM(certPEM) not supported for this alg:prov=" + this.algProvName; - }; - this.initSign = function (a) { - throw "initSign(prvKey) not supported for this alg:prov=" + this.algProvName; - }; - this.updateString = function (a) { - throw "updateString(str) not supported for this alg:prov=" + this.algProvName; - }; - this.updateHex = function (a) { - throw "updateHex(hex) not supported for this alg:prov=" + this.algProvName; - }; - this.sign = function () { - throw "sign() not supported for this alg:prov=" + this.algProvName; - }; - this.signString = function (a) { - throw "digestString(str) not supported for this alg:prov=" + this.algProvName; - }; - this.signHex = function (a) { - throw "digestHex(hex) not supported for this alg:prov=" + this.algProvName; - }; - this.verify = function (a) { - throw "verify(hSigVal) not supported for this alg:prov=" + this.algProvName; - }; - this.initParams = a; - if (void 0 !== a && (void 0 !== a.alg && (this.algName = a.alg, - this.provName = void 0 === a.prov ? KJUR.crypto.Util.DEFAULTPROVIDER[this.algName] : a.prov, - this.algProvName = this.algName + ":" + this.provName, - this.setAlgAndProvider(this.algName, this.provName), - this._setAlgNames()), - void 0 !== a.psssaltlen && (this.pssSaltLen = a.psssaltlen), - void 0 !== a.prvkeypem)) { - if (void 0 !== a.prvkeypas) - throw "both prvkeypem and prvkeypas parameters not supported"; - try { - b = new RSAKey, - b.readPrivateKeyFromPEMString(a.prvkeypem), - this.initSign(b) - } catch (c) { - throw "fatal error to load pem private key: " + c; - } - } - }; - KJUR.crypto.OID = new function () { - this.oidhex2name = { - "2a864886f70d010101": "rsaEncryption", - "2a8648ce3d0201": "ecPublicKey", - "2a8648ce380401": "dsa", - "2a8648ce3d030107": "secp256r1", - "2b8104001f": "secp192k1", - "2b81040021": "secp224r1", - "2b8104000a": "secp256k1", - "2b81040023": "secp521r1", - "2b81040022": "secp384r1", - "2a8648ce380403": "SHA1withDSA", - "608648016503040301": "SHA224withDSA", - "608648016503040302": "SHA256withDSA" - } - }; - - function ECFieldElementFp(a, b) { - this.x = b; - this.q = a - } - - function feFpEquals(a) { - return a == this ? !0 : this.q.equals(a.q) && this.x.equals(a.x) - } - - function feFpToBigInteger() { - return this.x - } - - function feFpNegate() { - return new ECFieldElementFp(this.q, this.x.negate().mod(this.q)) - } - - function feFpAdd(a) { - return new ECFieldElementFp(this.q, this.x.add(a.toBigInteger()).mod(this.q)) - } - - function feFpSubtract(a) { - return new ECFieldElementFp(this.q, this.x.subtract(a.toBigInteger()).mod(this.q)) - } - - function feFpMultiply(a) { - return new ECFieldElementFp(this.q, this.x.multiply(a.toBigInteger()).mod(this.q)) - } - - function feFpSquare() { - return new ECFieldElementFp(this.q, this.x.square().mod(this.q)) - } - - function feFpDivide(a) { - return new ECFieldElementFp(this.q, this.x.multiply(a.toBigInteger().modInverse(this.q)).mod(this.q)) - } - ECFieldElementFp.prototype.equals = feFpEquals; - ECFieldElementFp.prototype.toBigInteger = feFpToBigInteger; - ECFieldElementFp.prototype.negate = feFpNegate; - ECFieldElementFp.prototype.add = feFpAdd; - ECFieldElementFp.prototype.subtract = feFpSubtract; - ECFieldElementFp.prototype.multiply = feFpMultiply; - ECFieldElementFp.prototype.square = feFpSquare; - ECFieldElementFp.prototype.divide = feFpDivide; - - function ECPointFp(a, b, c, d) { - this.curve = a; - this.x = b; - this.y = c; - this.z = null == d ? BigInteger.ONE : d; - this.zinv = null - } - - function pointFpGetX() { - null == this.zinv && (this.zinv = this.z.modInverse(this.curve.q)); - return this.curve.fromBigInteger(this.x.toBigInteger().multiply(this.zinv).mod(this.curve.q)) - } - - function pointFpGetY() { - null == this.zinv && (this.zinv = this.z.modInverse(this.curve.q)); - return this.curve.fromBigInteger(this.y.toBigInteger().multiply(this.zinv).mod(this.curve.q)) - } - - function pointFpEquals(a) { - return a == this ? !0 : this.isInfinity() ? a.isInfinity() : a.isInfinity() ? this.isInfinity() : a.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(a.z)).mod(this.curve.q).equals(BigInteger.ZERO) ? a.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(a.z)).mod(this.curve.q).equals(BigInteger.ZERO) : !1 - } - - function pointFpIsInfinity() { - return null == this.x && null == this.y ? !0 : this.z.equals(BigInteger.ZERO) && !this.y.toBigInteger().equals(BigInteger.ZERO) - } - - function pointFpNegate() { - return new ECPointFp(this.curve, this.x, this.y.negate(), this.z) - } - - function pointFpAdd(a) { - if (this.isInfinity()) - return a; - if (a.isInfinity()) - return this; - var b = a.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(a.z)).mod(this.curve.q), - c = a.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(a.z)).mod(this.curve.q); - if (BigInteger.ZERO.equals(c)) - return BigInteger.ZERO.equals(b) ? this.twice() : this.curve.getInfinity(); - var d = new BigInteger("3"), - e = this.x.toBigInteger(), - f = this.y.toBigInteger(); - a.x.toBigInteger(); - a.y.toBigInteger(); - var g = c.square(), - h = g.multiply(c), - e = e.multiply(g), - g = b.square().multiply(this.z), - c = g.subtract(e.shiftLeft(1)).multiply(a.z).subtract(h).multiply(c).mod(this.curve.q), - b = e.multiply(d).multiply(b).subtract(f.multiply(h)).subtract(g.multiply(b)).multiply(a.z).add(b.multiply(h)).mod(this.curve.q); - a = h.multiply(this.z).multiply(a.z).mod(this.curve.q); - return new ECPointFp(this.curve, this.curve.fromBigInteger(c), this.curve.fromBigInteger(b), a) - } - - function pointFpTwice() { - if (this.isInfinity()) - return this; - if (0 == this.y.toBigInteger().signum()) - return this.curve.getInfinity(); - var a = new BigInteger("3"), - b = this.x.toBigInteger(), - c = this.y.toBigInteger(), - d = c.multiply(this.z), - e = d.multiply(c).mod(this.curve.q), - c = this.curve.a.toBigInteger(), - f = b.square().multiply(a); - BigInteger.ZERO.equals(c) || (f = f.add(this.z.square().multiply(c))); - f = f.mod(this.curve.q); - c = f.square().subtract(b.shiftLeft(3).multiply(e)).shiftLeft(1).multiply(d).mod(this.curve.q); - a = f.multiply(a).multiply(b).subtract(e.shiftLeft(1)).shiftLeft(2).multiply(e).subtract(f.square().multiply(f)).mod(this.curve.q); - d = d.square().multiply(d).shiftLeft(3).mod(this.curve.q); - return new ECPointFp(this.curve, this.curve.fromBigInteger(c), this.curve.fromBigInteger(a), d) - } - - function pointFpMultiply(a) { - if (this.isInfinity()) - return this; - if (0 == a.signum()) - return this.curve.getInfinity(); - var b = a.multiply(new BigInteger("3")), - c = this.negate(), - d = this, - e; - for (e = b.bitLength() - 2; 0 < e; --e) { - var d = d.twice(), - f = b.testBit(e), - g = a.testBit(e); - f != g && (d = d.add(f ? this : c)) - } - return d - } - - function pointFpMultiplyTwo(a, b, c) { - var d; - d = a.bitLength() > c.bitLength() ? a.bitLength() - 1 : c.bitLength() - 1; - for (var e = this.curve.getInfinity(), f = this.add(b); 0 <= d;) - e = e.twice(), - a.testBit(d) ? e = c.testBit(d) ? e.add(f) : e.add(this) : c.testBit(d) && (e = e.add(b)), - --d; - return e - } - ECPointFp.prototype.getX = pointFpGetX; - ECPointFp.prototype.getY = pointFpGetY; - ECPointFp.prototype.equals = pointFpEquals; - ECPointFp.prototype.isInfinity = pointFpIsInfinity; - ECPointFp.prototype.negate = pointFpNegate; - ECPointFp.prototype.add = pointFpAdd; - ECPointFp.prototype.twice = pointFpTwice; - ECPointFp.prototype.multiply = pointFpMultiply; - ECPointFp.prototype.multiplyTwo = pointFpMultiplyTwo; - - function ECCurveFp(a, b, c) { - this.q = a; - this.a = this.fromBigInteger(b); - this.b = this.fromBigInteger(c); - this.infinity = new ECPointFp(this, null, null) - } - - function curveFpGetQ() { - return this.q - } - - function curveFpGetA() { - return this.a - } - - function curveFpGetB() { - return this.b - } - - function curveFpEquals(a) { - return a == this ? !0 : this.q.equals(a.q) && this.a.equals(a.a) && this.b.equals(a.b) - } - - function curveFpGetInfinity() { - return this.infinity - } - - function curveFpFromBigInteger(a) { - return new ECFieldElementFp(this.q, a) - } - - function curveFpDecodePointHex(a) { - switch (parseInt(a.substr(0, 2), 16)) { - case 0: - return this.infinity; - case 2: - case 3: - return null; - case 4: - case 6: - case 7: - var b = (a.length - 2) / 2, - c = a.substr(2, b); - a = a.substr(b + 2, b); - return new ECPointFp(this, this.fromBigInteger(new BigInteger(c, 16)), this.fromBigInteger(new BigInteger(a, 16))); - default: - return null - } - } - ECCurveFp.prototype.getQ = curveFpGetQ; - ECCurveFp.prototype.getA = curveFpGetA; - ECCurveFp.prototype.getB = curveFpGetB; - ECCurveFp.prototype.equals = curveFpEquals; - ECCurveFp.prototype.getInfinity = curveFpGetInfinity; - ECCurveFp.prototype.fromBigInteger = curveFpFromBigInteger; - ECCurveFp.prototype.decodePointHex = curveFpDecodePointHex; - ECFieldElementFp.prototype.getByteLength = function () { - return Math.floor((this.toBigInteger().bitLength() + 7) / 8) - }; - ECPointFp.prototype.getEncoded = function (a) { - var b = function (a, c) { - var b = a.toByteArrayUnsigned(); - if (c < b.length) - b = b.slice(b.length - c); - else - for (; c > b.length;) - b.unshift(0); - return b - }, - c = this.getX().toBigInteger(), - d = this.getY().toBigInteger(), - c = b(c, 32); - a ? d.isEven() ? c.unshift(2) : c.unshift(3) : (c.unshift(4), - c = c.concat(b(d, 32))); - return c - }; - ECPointFp.decodeFrom = function (a, b) { - var c = b.length - 1, - d = b.slice(1, 1 + c / 2), - c = b.slice(1 + c / 2, 1 + c); - d.unshift(0); - c.unshift(0); - d = new BigInteger(d); - c = new BigInteger(c); - return new ECPointFp(a, a.fromBigInteger(d), a.fromBigInteger(c)) - }; - ECPointFp.decodeFromHex = function (a, b) { - b.substr(0, 2); - var c = b.length - 2, - d = b.substr(2, c / 2), - c = b.substr(2 + c / 2, c / 2), - d = new BigInteger(d, 16), - c = new BigInteger(c, 16); - return new ECPointFp(a, a.fromBigInteger(d), a.fromBigInteger(c)) - }; - ECPointFp.prototype.add2D = function (a) { - if (this.isInfinity()) - return a; - if (a.isInfinity()) - return this; - if (this.x.equals(a.x)) - return this.y.equals(a.y) ? this.twice() : this.curve.getInfinity(); - var b = a.x.subtract(this.x), - b = a.y.subtract(this.y).divide(b); - a = b.square().subtract(this.x).subtract(a.x); - b = b.multiply(this.x.subtract(a)).subtract(this.y); - return new ECPointFp(this.curve, a, b) - }; - ECPointFp.prototype.twice2D = function () { - if (this.isInfinity()) - return this; - if (0 == this.y.toBigInteger().signum()) - return this.curve.getInfinity(); - var a = this.curve.fromBigInteger(BigInteger.valueOf(2)), - b = this.curve.fromBigInteger(BigInteger.valueOf(3)), - b = this.x.square().multiply(b).add(this.curve.a).divide(this.y.multiply(a)), - a = b.square().subtract(this.x.multiply(a)), - b = b.multiply(this.x.subtract(a)).subtract(this.y); - return new ECPointFp(this.curve, a, b) - }; - ECPointFp.prototype.multiply2D = function (a) { - if (this.isInfinity()) - return this; - if (0 == a.signum()) - return this.curve.getInfinity(); - var b = a.multiply(new BigInteger("3")), - c = this.negate(), - d = this, - e; - for (e = b.bitLength() - 2; 0 < e; --e) { - var d = d.twice(), - f = b.testBit(e), - g = a.testBit(e); - f != g && (d = d.add2D(f ? this : c)) - } - return d - }; - ECPointFp.prototype.isOnCurve = function () { - var a = this.getX().toBigInteger(), - b = this.getY().toBigInteger(), - c = this.curve.getA().toBigInteger(), - d = this.curve.getB().toBigInteger(), - e = this.curve.getQ(), - b = b.multiply(b).mod(e), - a = a.multiply(a).multiply(a).add(c.multiply(a)).add(d).mod(e); - return b.equals(a) - }; - ECPointFp.prototype.toString = function () { - return "(" + this.getX().toBigInteger().toString() + "," + this.getY().toBigInteger().toString() + ")" - }; - ECPointFp.prototype.validate = function () { - var a = this.curve.getQ(); - if (this.isInfinity()) - throw Error("Point is at infinity."); - var b = this.getX().toBigInteger(), - c = this.getY().toBigInteger(); - if (0 > b.compareTo(BigInteger.ONE) || 0 < b.compareTo(a.subtract(BigInteger.ONE))) - throw Error("x coordinate out of bounds"); - if (0 > c.compareTo(BigInteger.ONE) || 0 < c.compareTo(a.subtract(BigInteger.ONE))) - throw Error("y coordinate out of bounds"); - if (!this.isOnCurve()) - throw Error("Point is not on the curve."); - if (this.multiply(a).isInfinity()) - throw Error("Point is not a scalar multiple of G."); - return !0 - }; - "undefined" != typeof KJUR && KJUR || (KJUR = {}); - "undefined" != typeof KJUR.crypto && KJUR.crypto || (KJUR.crypto = {}); - KJUR.crypto.ECDSA = function (a) { - var b = new SecureRandom; - this.type = "EC"; - this.getBigRandom = function (a) { - return (new BigInteger(a.bitLength(), b)).mod(a.subtract(BigInteger.ONE)).add(BigInteger.ONE) - }; - this.setNamedCurve = function (a) { - this.ecparams = KJUR.crypto.ECParameterDB.getByName(a); - this.pubKeyHex = this.prvKeyHex = null; - this.curveName = a - }; - this.setPrivateKeyHex = function (a) { - this.isPrivate = !0; - this.prvKeyHex = a - }; - this.setPublicKeyHex = function (a) { - this.isPublic = !0; - this.pubKeyHex = a - }; - this.generateKeyPairHex = function () { - var a = this.getBigRandom(this.ecparams.n), - b = this.ecparams.G.multiply(a), - e = b.getX().toBigInteger(), - b = b.getY().toBigInteger(), - f = this.ecparams.keylen / 4, - a = ("0000000000" + a.toString(16)).slice(-f), - e = ("0000000000" + e.toString(16)).slice(-f), - b = ("0000000000" + b.toString(16)).slice(-f), - e = "04" + e + b; - this.setPrivateKeyHex(a); - this.setPublicKeyHex(e); - return { - ecprvhex: a, - ecpubhex: e - } - }; - this.signWithMessageHash = function (a) { - return this.signHex(a, this.prvKeyHex) - }; - this.signHex = function (a, b) { - var e = new BigInteger(b, 16), - f = this.ecparams.n, - g = new BigInteger(a, 16); - do - var h = this.getBigRandom(f), - k = this.ecparams.G.multiply(h).getX().toBigInteger().mod(f); - while (0 >= k.compareTo(BigInteger.ZERO)); - e = h.modInverse(f).multiply(g.add(e.multiply(k))).mod(f); - return KJUR.crypto.ECDSA.biRSSigToASN1Sig(k, e) - }; - this.sign = function (a, b) { - var e = this.ecparams.n, - f = BigInteger.fromByteArrayUnsigned(a); - do - var g = this.getBigRandom(e), - h = this.ecparams.G.multiply(g).getX().toBigInteger().mod(e); - while (0 >= h.compareTo(BigInteger.ZERO)); - e = g.modInverse(e).multiply(f.add(b.multiply(h))).mod(e); - return this.serializeSig(h, e) - }; - this.verifyWithMessageHash = function (a, b) { - return this.verifyHex(a, b, this.pubKeyHex) - }; - this.verifyHex = function (a, b, e) { - var f; - f = KJUR.crypto.ECDSA.parseSigHex(b); - b = f.r; - f = f.s; - e = ECPointFp.decodeFromHex(this.ecparams.curve, e); - a = new BigInteger(a, 16); - return this.verifyRaw(a, b, f, e) - }; - this.verify = function (a, b, e) { - var f; - if (Bitcoin.Util.isArray(b)) - b = this.parseSig(b), - f = b.r, - b = b.s; - else if ("object" === typeof b && b.r && b.s) - f = b.r, - b = b.s; - else - throw "Invalid value for signature"; - if (!(e instanceof ECPointFp)) - if (Bitcoin.Util.isArray(e)) - e = ECPointFp.decodeFrom(this.ecparams.curve, e); - else - throw "Invalid format for pubkey value, must be byte array or ECPointFp"; - a = BigInteger.fromByteArrayUnsigned(a); - return this.verifyRaw(a, f, b, e) - }; - this.verifyRaw = function (a, b, e, f) { - var g = this.ecparams.n, - h = this.ecparams.G; - if (0 > b.compareTo(BigInteger.ONE) || 0 <= b.compareTo(g) || 0 > e.compareTo(BigInteger.ONE) || 0 <= e.compareTo(g)) - return !1; - e = e.modInverse(g); - a = a.multiply(e).mod(g); - e = b.multiply(e).mod(g); - return h.multiply(a).add(f.multiply(e)).getX().toBigInteger().mod(g).equals(b) - }; - this.serializeSig = function (a, b) { - var e = a.toByteArraySigned(), - f = b.toByteArraySigned(), - g = []; - g.push(2); - g.push(e.length); - g = g.concat(e); - g.push(2); - g.push(f.length); - g = g.concat(f); - g.unshift(g.length); - g.unshift(48); - return g - }; - this.parseSig = function (a) { - var b; - if (48 != a[0]) - throw Error("Signature not a valid DERSequence"); - b = 2; - if (2 != a[b]) - throw Error("First element in signature must be a DERInteger"); - var e = a.slice(b + 2, b + 2 + a[b + 1]); - b += 2 + a[b + 1]; - if (2 != a[b]) - throw Error("Second element in signature must be a DERInteger"); - a = a.slice(b + 2, b + 2 + a[b + 1]); - e = BigInteger.fromByteArrayUnsigned(e); - a = BigInteger.fromByteArrayUnsigned(a); - return { - r: e, - s: a - } - }; - this.parseSigCompact = function (a) { - if (65 !== a.length) - throw "Signature has the wrong length"; - var b = a[0] - 27; - if (0 > b || 7 < b) - throw "Invalid signature type"; - var e = this.ecparams.n, - f = BigInteger.fromByteArrayUnsigned(a.slice(1, 33)).mod(e); - a = BigInteger.fromByteArrayUnsigned(a.slice(33, 65)).mod(e); - return { - r: f, - s: a, - i: b - } - }; - void 0 !== a && void 0 !== a.curve && (this.curveName = a.curve); - void 0 === this.curveName && (this.curveName = "secp256r1"); - this.setNamedCurve(this.curveName); - void 0 !== a && (void 0 !== a.prv && this.setPrivateKeyHex(a.prv), - void 0 !== a.pub && this.setPublicKeyHex(a.pub)) - }; - KJUR.crypto.ECDSA.parseSigHex = function (a) { - var b = KJUR.crypto.ECDSA.parseSigHexInHexRS(a); - a = new BigInteger(b.r, 16); - b = new BigInteger(b.s, 16); - return { - r: a, - s: b - } - }; - KJUR.crypto.ECDSA.parseSigHexInHexRS = function (a) { - if ("30" != a.substr(0, 2)) - throw "signature is not a ASN.1 sequence"; - var b = ASN1HEX.getPosArrayOfChildren_AtObj(a, 0); - if (2 != b.length) - throw "number of signature ASN.1 sequence elements seem wrong"; - var c = b[0], - b = b[1]; - if ("02" != a.substr(c, 2)) - throw "1st item of sequene of signature is not ASN.1 integer"; - if ("02" != a.substr(b, 2)) - throw "2nd item of sequene of signature is not ASN.1 integer"; - c = ASN1HEX.getHexOfV_AtObj(a, c); - a = ASN1HEX.getHexOfV_AtObj(a, b); - return { - r: c, - s: a - } - }; - KJUR.crypto.ECDSA.asn1SigToConcatSig = function (a) { - var b = KJUR.crypto.ECDSA.parseSigHexInHexRS(a); - a = b.r; - b = b.s; - "00" == a.substr(0, 2) && 8 == a.length / 2 * 8 % 128 && (a = a.substr(2)); - "00" == b.substr(0, 2) && 8 == b.length / 2 * 8 % 128 && (b = b.substr(2)); - if (0 != a.length / 2 * 8 % 128) - throw "unknown ECDSA sig r length error"; - if (0 != b.length / 2 * 8 % 128) - throw "unknown ECDSA sig s length error"; - return a + b - }; - KJUR.crypto.ECDSA.concatSigToASN1Sig = function (a) { - if (0 != a.length / 2 * 8 % 128) - throw "unknown ECDSA concatinated r-s sig length error"; - var b = a.substr(0, a.length / 2); - a = a.substr(a.length / 2); - return KJUR.crypto.ECDSA.hexRSSigToASN1Sig(b, a) - }; - KJUR.crypto.ECDSA.hexRSSigToASN1Sig = function (a, b) { - var c = new BigInteger(a, 16), - d = new BigInteger(b, 16); - return KJUR.crypto.ECDSA.biRSSigToASN1Sig(c, d) - }; - KJUR.crypto.ECDSA.biRSSigToASN1Sig = function (a, b) { - var c = new KJUR.asn1.DERInteger({ - bigint: a - }), - d = new KJUR.asn1.DERInteger({ - bigint: b - }); - return (new KJUR.asn1.DERSequence({ - array: [c, d] - })).getEncodedHex() - }; - (function () { - var a = CryptoJS, - b = a.lib, - c = b.WordArray, - d = b.Hasher, - e = [], - b = a.algo.SM3 = d.extend({ - _doReset: function () { - this._hash = new c.init([1937774191, 1226093241, 388252375, 3666478592, 2842636476, 372324522, 3817729613, 2969243214]) - }, - _doProcessBlock: function (a, b) { - for (var c = this._hash.words, d = c[0], l = c[1], p = c[2], n = c[3], q = c[4], m = 0; 80 > m; m++) { - if (16 > m) - e[m] = a[b + m] | 0; - else { - var r = e[m - 3] ^ e[m - 8] ^ e[m - 14] ^ e[m - 16]; - e[m] = r << 1 | r >>> 31 - } - r = (d << 5 | d >>> 27) + q + e[m]; - r = 20 > m ? r + ((l & p | ~l & n) + 1518500249) : 40 > m ? r + ((l ^ p ^ n) + 1859775393) : 60 > m ? r + ((l & p | l & n | p & n) - 1894007588) : r + ((l ^ p ^ n) - 899497514); - q = n; - n = p; - p = l << 30 | l >>> 2; - l = d; - d = r - } - c[0] = c[0] + d | 0; - c[1] = c[1] + l | 0; - c[2] = c[2] + p | 0; - c[3] = c[3] + n | 0; - c[4] = c[4] + q | 0 - }, - _doFinalize: function () { - var a = this._data, - b = a.words, - c = 8 * this._nDataBytes, - d = 8 * a.sigBytes; - b[d >>> 5] |= 128 << 24 - d % 32; - b[(d + 64 >>> 9 << 4) + 14] = Math.floor(c / 4294967296); - b[(d + 64 >>> 9 << 4) + 15] = c; - a.sigBytes = 4 * b.length; - this._process(); - return this._hash - }, - clone: function () { - var a = d.clone.call(this); - a._hash = this._hash.clone(); - return a - } - }); - a.SM3 = d._createHelper(b); - a.HmacSM3 = d._createHmacHelper(b) - })(); - - function SM3Digest() { - this.BYTE_LENGTH = 64; - this.xBuf = []; - this.byteCount = this.xBufOff = 0; - this.DIGEST_LENGTH = 32; - this.v0 = [1937774191, 1226093241, 388252375, 3666478592, 2842636476, 372324522, 3817729613, 2969243214]; - this.v0 = [1937774191, 1226093241, 388252375, -628488704, -1452330820, 372324522, -477237683, -1325724082]; - this.v = Array(8); - this.v_ = Array(8); - this.X0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - this.X = Array(68); - this.xOff = 0; - this.T_00_15 = 2043430169; - this.T_16_63 = 2055708042; - 0 < arguments.length ? this.InitDigest(arguments[0]) : this.Init() - } - SM3Digest.prototype = { - Init: function () { - this.xBuf = Array(4); - this.Reset() - }, - InitDigest: function (a) { - this.xBuf = Array(a.xBuf.length); - Array.Copy(a.xBuf, 0, this.xBuf, 0, a.xBuf.length); - this.xBufOff = a.xBufOff; - this.byteCount = a.byteCount; - Array.Copy(a.X, 0, this.X, 0, a.X.length); - this.xOff = a.xOff; - Array.Copy(a.v, 0, this.v, 0, a.v.length) - }, - GetDigestSize: function () { - return this.DIGEST_LENGTH - }, - Reset: function () { - this.xBufOff = this.byteCount = 0; - Array.Clear(this.xBuf, 0, this.xBuf.length); - Array.Copy(this.v0, 0, this.v, 0, this.v0.length); - this.xOff = 0; - Array.Copy(this.X0, 0, this.X, 0, this.X0.length) - }, - GetByteLength: function () { - return this.BYTE_LENGTH - }, - ProcessBlock: function () { - var a, b = this.X, - c = Array(64); - for (a = 16; 68 > a; a++) - b[a] = this.P1(b[a - 16] ^ b[a - 9] ^ this.ROTATE(b[a - 3], 15)) ^ this.ROTATE(b[a - 13], 7) ^ b[a - 6]; - for (a = 0; 64 > a; a++) - c[a] = b[a] ^ b[a + 4]; - var d = this.v, - e = this.v_; - Array.Copy(d, 0, e, 0, this.v0.length); - var f, g; - for (a = 0; 16 > a; a++) - g = this.ROTATE(e[0], 12), - f = Int32.parse(Int32.parse(g + e[4]) + this.ROTATE(this.T_00_15, a)), - f = this.ROTATE(f, 7), - g ^= f, - g = Int32.parse(Int32.parse(this.FF_00_15(e[0], e[1], e[2]) + e[3]) + g) + c[a], - f = Int32.parse(Int32.parse(this.GG_00_15(e[4], e[5], e[6]) + e[7]) + f) + b[a], - e[3] = e[2], - e[2] = this.ROTATE(e[1], 9), - e[1] = e[0], - e[0] = g, - e[7] = e[6], - e[6] = this.ROTATE(e[5], 19), - e[5] = e[4], - e[4] = this.P0(f); - for (a = 16; 64 > a; a++) - g = this.ROTATE(e[0], 12), - f = Int32.parse(Int32.parse(g + e[4]) + this.ROTATE(this.T_16_63, a)), - f = this.ROTATE(f, 7), - g ^= f, - g = Int32.parse(Int32.parse(this.FF_16_63(e[0], e[1], e[2]) + e[3]) + g) + c[a], - f = Int32.parse(Int32.parse(this.GG_16_63(e[4], e[5], e[6]) + e[7]) + f) + b[a], - e[3] = e[2], - e[2] = this.ROTATE(e[1], 9), - e[1] = e[0], - e[0] = g, - e[7] = e[6], - e[6] = this.ROTATE(e[5], 19), - e[5] = e[4], - e[4] = this.P0(f); - for (a = 0; 8 > a; a++) - d[a] ^= Int32.parse(e[a]); - this.xOff = 0; - Array.Copy(this.X0, 0, this.X, 0, this.X0.length) - }, - ProcessWord: function (a, b) { - var c = a[b] << 24, - c = c | (a[++b] & 255) << 16, - c = c | (a[++b] & 255) << 8, - c = c | a[++b] & 255; - this.X[this.xOff] = c; - 16 == ++this.xOff && this.ProcessBlock() - }, - ProcessLength: function (a) { - 14 < this.xOff && this.ProcessBlock(); - this.X[14] = this.URShiftLong(a, 32); - this.X[15] = a & 4294967295 - }, - IntToBigEndian: function (a, b, c) { - b[c] = Int32.parseByte(this.URShift(a, 24)); - b[++c] = Int32.parseByte(this.URShift(a, 16)); - b[++c] = Int32.parseByte(this.URShift(a, 8)); - b[++c] = Int32.parseByte(a) - }, - DoFinal: function (a, b) { - this.Finish(); - for (var c = 0; 8 > c; c++) - this.IntToBigEndian(this.v[c], a, b + 4 * c); - this.Reset(); - for (var d = a.length, c = 0; c < d; c++) - a[c] &= 255; - return this.DIGEST_LENGTH - }, - Update: function (a) { - this.xBuf[this.xBufOff++] = a; - this.xBufOff == this.xBuf.length && (this.ProcessWord(this.xBuf, 0), - this.xBufOff = 0); - this.byteCount++ - }, - BlockUpdate: function (a, b, c) { - for (; 0 != this.xBufOff && 0 < c;) - this.Update(a[b]), - b++, - c--; - for (; c > this.xBuf.length;) - this.ProcessWord(a, b), - b += this.xBuf.length, - c -= this.xBuf.length, - this.byteCount += this.xBuf.length; - for (; 0 < c;) - this.Update(a[b]), - b++, - c-- - }, - Finish: function () { - var a = this.byteCount << 3; - for (this.Update(128); 0 != this.xBufOff;) - this.Update(0); - this.ProcessLength(a); - this.ProcessBlock() - }, - ROTATE: function (a, b) { - return a << b | this.URShift(a, 32 - b) - }, - P0: function (a) { - return a ^ this.ROTATE(a, 9) ^ this.ROTATE(a, 17) - }, - P1: function (a) { - return a ^ this.ROTATE(a, 15) ^ this.ROTATE(a, 23) - }, - FF_00_15: function (a, b, c) { - return a ^ b ^ c - }, - FF_16_63: function (a, b, c) { - return a & b | a & c | b & c - }, - GG_00_15: function (a, b, c) { - return a ^ b ^ c - }, - GG_16_63: function (a, b, c) { - return a & b | ~a & c - }, - URShift: function (a, b) { - if (a > Int32.maxValue || a < Int32.minValue) - a = Int32.parse(a); - return 0 <= a ? a >> b : (a >> b) + (2 << ~b) - }, - URShiftLong: function (a, b) { - var c; - c = new BigInteger; - c.fromInt(a); - if (0 <= c.signum()) - c = c.shiftRight(b).intValue(); - else { - var d = new BigInteger; - d.fromInt(2); - var e = ~b; - c = ""; - if (0 > e) { - d = 64 + e; - for (e = 0; e < d; e++) - c += "0"; - d = new BigInteger; - d.fromInt(a >> b); - c = new BigInteger("10" + c, 2); - c.toRadix(10); - c = c.add(d).toRadix(10) - } else - c = d.shiftLeft(~b).intValue(), - c = (a >> b) + c - } - return c - }, - GetZ: function (a, b) { - var c = CryptoJS.enc.Utf8.parse("1234567812345678"), - d = 32 * c.words.length; - this.Update(d >> 8 & 255); - this.Update(d & 255); - c = this.GetWords(c.toString()); - this.BlockUpdate(c, 0, c.length); - var c = this.GetWords(a.curve.a.toBigInteger().toRadix(16)), - d = this.GetWords(a.curve.b.toBigInteger().toRadix(16)), - e = this.GetWords(a.getX().toBigInteger().toRadix(16)), - f = this.GetWords(a.getY().toBigInteger().toRadix(16)), - g = this.GetWords(b.substr(0, 64)), - h = this.GetWords(b.substr(64, 64)); - this.BlockUpdate(c, 0, c.length); - this.BlockUpdate(d, 0, d.length); - this.BlockUpdate(e, 0, e.length); - this.BlockUpdate(f, 0, f.length); - this.BlockUpdate(g, 0, g.length); - this.BlockUpdate(h, 0, h.length); - c = Array(this.GetDigestSize()); - this.DoFinal(c, 0); - return c - }, - GetWords: function (a) { - for (var b = [], c = a.length, d = 0; d < c; d += 2) - b[b.length] = parseInt(a.substr(d, 2), 16); - return b - }, - GetHex: function (a) { - for (var b = [], c = 0, d = 0; d < 2 * a.length; d += 2) - b[d >>> 3] |= parseInt(a[c]) << 24 - d % 8 * 4, - c++; - return new CryptoJS.lib.WordArray.init(b, a.length) - } - }; - Array.Clear = function (a, b, c) { - for (var elm in a) - a[elm] = null - }; - Array.Copy = function (a, b, c, d, e) { - a = a.slice(b, b + e); - for (b = 0; b < a.length; b++) - c[d] = a[b], - d++ - }; - var Int32 = { //zdk - minValue: -parseInt("10000000000000000000000000000000", 2), - maxValue: 2147483647, - parse: function (a) { - if (a < this.minValue) { - a = new Number(-a); - a = a.toString(2); - a = a.substr(a.length - 31, 31); - for (var b = "", c = 0; c < a.length; c++) - var d = a.substr(c, 1), - b = b + ("0" == d ? "1" : "0"); - a = parseInt(b, 2); - return a + 1 - } - if (a > this.maxValue) { - a = Number(a); - a = a.toString(2); - a = a.substr(a.length - 31, 31); - b = ""; - for (c = 0; c < a.length; c++) - d = a.substr(c, 1), - b += "0" == d ? "1" : "0"; - a = parseInt(b, 2); - return -(a + 1) - } - return a - }, - parseByte: function (a) { - if (0 > a) { - a = new Number(-a); - a = a.toString(2); - a = a.substr(a.length - 8, 8); - for (var b = "", c = 0; c < a.length; c++) - var d = a.substr(c, 1), - b = b + ("0" == d ? "1" : "0"); - return parseInt(b, 2) + 1 - } - return 255 < a ? (a = Number(a), - a = a.toString(2), - parseInt(a.substr(a.length - 8, 8), 2)) : a - } - }; - "undefined" != typeof KJUR && KJUR || (KJUR = {}); - "undefined" != typeof KJUR.crypto && KJUR.crypto || (KJUR.crypto = {}); - KJUR.crypto.SM3withSM2 = function (a) { - var b = new SecureRandom; - this.type = "SM2"; - this.getBigRandom = function (a) { - return (new BigInteger(a.bitLength(), b)).mod(a.subtract(BigInteger.ONE)).add(BigInteger.ONE) - }; - this.setNamedCurve = function (a) { - this.ecparams = KJUR.crypto.ECParameterDB.getByName(a); - this.pubKeyHex = this.prvKeyHex = null; - this.curveName = a - }; - this.setPrivateKeyHex = function (a) { - this.isPrivate = !0; - this.prvKeyHex = a - }; - this.setPublicKeyHex = function (a) { - this.isPublic = !0; - this.pubKeyHex = a - }; - this.generateKeyPairHex = function () { - var a = this.getBigRandom(this.ecparams.n), - b = this.ecparams.G.multiply(a), - e = b.getX().toBigInteger(), - b = b.getY().toBigInteger(), - f = this.ecparams.keylen / 4, - a = ("0000000000" + a.toString(16)).slice(-f), - e = ("0000000000" + e.toString(16)).slice(-f), - b = ("0000000000" + b.toString(16)).slice(-f), - e = "04" + e + b; - this.setPrivateKeyHex(a); - this.setPublicKeyHex(e); - return { - ecprvhex: a, - ecpubhex: e - } - }; - this.signWithMessageHash = function (a) { - return this.signHex(a, this.prvKeyHex) - }; - this.signHex = function (a, b) { - var e = new BigInteger(b, 16), - f = this.ecparams.n, - g = new BigInteger(a, 16), - h = null, - k = null, - l = k = null; - do { - do - k = this.generateKeyPairHex(), - h = new BigInteger(k.ecprvhex, 16), - k = ECPointFp.decodeFromHex(this.ecparams.curve, k.ecpubhex), - k = g.add(k.getX().toBigInteger()), - k = k.mod(f); - while (k.equals(BigInteger.ZERO) || k.add(h).equals(f)); - var p = e.add(BigInteger.ONE), - p = p.modInverse(f), - l = k.multiply(e), - l = h.subtract(l).mod(f), - l = p.multiply(l).mod(f) - } while (l.equals(BigInteger.ZERO)); - return KJUR.crypto.ECDSA.biRSSigToASN1Sig(k, l) - }; - this.sign = function (a, b) { - var e = this.ecparams.n, - f = BigInteger.fromByteArrayUnsigned(a); - do - var g = this.getBigRandom(e), - h = this.ecparams.G.multiply(g).getX().toBigInteger().mod(e); - while (0 >= h.compareTo(BigInteger.ZERO)); - e = g.modInverse(e).multiply(f.add(b.multiply(h))).mod(e); - return this.serializeSig(h, e) - }; - this.verifyWithMessageHash = function (a, b) { - return this.verifyHex(a, b, this.pubKeyHex) - }; - this.verifyHex = function (a, b, e) { - var f; - f = KJUR.crypto.ECDSA.parseSigHex(b); - b = f.r; - f = f.s; - e = ECPointFp.decodeFromHex(this.ecparams.curve, e); - a = new BigInteger(a, 16); - return this.verifyRaw(a, b, f, e) - }; - this.verify = function (a, b, e) { - var f; - if (Bitcoin.Util.isArray(b)) - b = this.parseSig(b), - f = b.r, - b = b.s; - else if ("object" === typeof b && b.r && b.s) - f = b.r, - b = b.s; - else - throw "Invalid value for signature"; - if (!(e instanceof ECPointFp)) - if (Bitcoin.Util.isArray(e)) - e = ECPointFp.decodeFrom(this.ecparams.curve, e); - else - throw "Invalid format for pubkey value, must be byte array or ECPointFp"; - a = BigInteger.fromByteArrayUnsigned(a); - return this.verifyRaw(a, f, b, e) - }; - this.verifyRaw = function (a, b, e, f) { - var g = this.ecparams.n, - h = this.ecparams.G, - k = b.add(e).mod(g); - if (k.equals(BigInteger.ZERO)) - return !1; - e = h.multiply(e); - e = e.add(f.multiply(k)); - a = a.add(e.getX().toBigInteger()).mod(g); - return b.equals(a) - }; - this.serializeSig = function (a, b) { - var e = a.toByteArraySigned(), - f = b.toByteArraySigned(), - g = []; - g.push(2); - g.push(e.length); - g = g.concat(e); - g.push(2); - g.push(f.length); - g = g.concat(f); - g.unshift(g.length); - g.unshift(48); - return g - }; - this.parseSig = function (a) { - var b; - if (48 != a[0]) - throw Error("Signature not a valid DERSequence"); - b = 2; - if (2 != a[b]) - throw Error("First element in signature must be a DERInteger"); - var e = a.slice(b + 2, b + 2 + a[b + 1]); - b += 2 + a[b + 1]; - if (2 != a[b]) - throw Error("Second element in signature must be a DERInteger"); - a = a.slice(b + 2, b + 2 + a[b + 1]); - e = BigInteger.fromByteArrayUnsigned(e); - a = BigInteger.fromByteArrayUnsigned(a); - return { - r: e, - s: a - } - }; - this.parseSigCompact = function (a) { - if (65 !== a.length) - throw "Signature has the wrong length"; - var b = a[0] - 27; - if (0 > b || 7 < b) - throw "Invalid signature type"; - var e = this.ecparams.n, - f = BigInteger.fromByteArrayUnsigned(a.slice(1, 33)).mod(e); - a = BigInteger.fromByteArrayUnsigned(a.slice(33, 65)).mod(e); - return { - r: f, - s: a, - i: b - } - }; - void 0 !== a && void 0 !== a.curve && (this.curveName = a.curve); - void 0 === this.curveName && (this.curveName = "sm2"); - this.setNamedCurve(this.curveName); - void 0 !== a && (void 0 !== a.prv && this.setPrivateKeyHex(a.prv), - void 0 !== a.pub && this.setPublicKeyHex(a.pub)) - }; - "undefined" != typeof KJUR && KJUR || (KJUR = {}); - "undefined" != typeof KJUR.crypto && KJUR.crypto || (KJUR.crypto = {}); - KJUR.crypto.ECParameterDB = new function () { - var a = {}, - b = {}; - this.getByName = function (c) { - var d = c; - "undefined" != typeof b[d] && (d = b[c]); - if ("undefined" != typeof a[d]) - return a[d]; - throw "unregistered EC curve name: " + d; - }; - this.regist = function (c, d, e, f, g, h, k, l, p, n, q, m) { - a[c] = {}; - e = new BigInteger(e, 16); - f = new BigInteger(f, 16); - g = new BigInteger(g, 16); - h = new BigInteger(h, 16); - k = new BigInteger(k, 16); - e = new ECCurveFp(e, f, g); - l = e.decodePointHex("04" + l + p); - a[c].name = c; - a[c].keylen = d; - a[c].curve = e; - a[c].G = l; - a[c].n = h; - a[c].h = k; - a[c].oid = q; - a[c].info = m; - for (d = 0; d < n.length; d++) - b[n[d]] = c - } - }; - KJUR.crypto.ECParameterDB.regist("secp128r1", 128, "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC", "E87579C11079F43DD824993C2CEE5ED3", "FFFFFFFE0000000075A30D1B9038A115", "1", "161FF7528B899B2D0C28607CA52C5B86", "CF5AC8395BAFEB13C02DA292DDED7A83", [], "", "secp128r1 : SECG curve over a 128 bit prime field"); - KJUR.crypto.ECParameterDB.regist("secp160k1", 160, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", "0", "7", "0100000000000000000001B8FA16DFAB9ACA16B6B3", "1", "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", "938CF935318FDCED6BC28286531733C3F03C4FEE", [], "", "secp160k1 : SECG curve over a 160 bit prime field"); - KJUR.crypto.ECParameterDB.regist("secp160r1", 160, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC", "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45", "0100000000000000000001F4C8F927AED3CA752257", "1", "4A96B5688EF573284664698968C38BB913CBFC82", "23A628553168947D59DCC912042351377AC5FB32", [], "", "secp160r1 : SECG curve over a 160 bit prime field"); - KJUR.crypto.ECParameterDB.regist("secp192k1", 192, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", "0", "3", "FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", "1", "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", []); - KJUR.crypto.ECParameterDB.regist("secp192r1", 192, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", "1", "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", []); - KJUR.crypto.ECParameterDB.regist("secp224r1", 224, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", "1", "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", []); - KJUR.crypto.ECParameterDB.regist("secp256k1", 256, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", "0", "7", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", "1", "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", []); - KJUR.crypto.ECParameterDB.regist("secp256r1", 256, "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", "1", "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", ["NIST P-256", "P-256", "prime256v1"]); - KJUR.crypto.ECParameterDB.regist("secp384r1", 384, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", "1", "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", ["NIST P-384", "P-384"]); - KJUR.crypto.ECParameterDB.regist("secp521r1", 521, "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", "051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", "1", "C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", "011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", ["NIST P-521", "P-521"]); - KJUR.crypto.ECParameterDB.regist("sm2", 256, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", "1", "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", ["sm2", "SM2"]); - - SM2Cipher.prototype = { - Reset: function () { - this.sm3keybase = new SM3Digest; - this.sm3c3 = new SM3Digest; - for (var a = this.p2.getX().toBigInteger().toRadix(16); 64 > a.length;) - a = "0" + a; - for (var a = this.GetWords(a), b = this.p2.getY().toBigInteger().toRadix(16); 64 > b.length;) - b = "0" + b; - b = this.GetWords(b); - this.sm3keybase.BlockUpdate(a, 0, a.length); - this.sm3c3.BlockUpdate(a, 0, a.length); - this.sm3keybase.BlockUpdate(b, 0, b.length); - this.ct = 1; - this.NextKey() - }, - NextKey: function () { - var a = new SM3Digest(this.sm3keybase); - a.Update(this.ct >> 24 & 255); - a.Update(this.ct >> 16 & 255); - a.Update(this.ct >> 8 & 255); - a.Update(this.ct & 255); - a.DoFinal(this.key, 0); - this.keyOff = 0; - this.ct++ - }, - KDF: function (a) { - var b = Array(a), - c = new SM3Digest, - d = Array(32), - e = 1, - f = a / 32; - a %= 32; - for (var g = this.p2.getX().toBigInteger().toRadix(16); 64 > g.length;) - g = "0" + g; - for (var g = this.GetWords(g), h = this.p2.getY().toBigInteger().toRadix(16); 64 > h.length;) - h = "0" + h; - for (var h = this.GetWords(h), k = 0, l = 0; l < f; l++) - c.BlockUpdate(g, 0, g.length), - c.BlockUpdate(h, 0, h.length), - c.Update(e >> 24 & 255), - c.Update(e >> 16 & 255), - c.Update(e >> 8 & 255), - c.Update(e & 255), - c.DoFinal(b, k), - k += 32, - e++; - 0 != a && (c.BlockUpdate(g, 0, g.length), - c.BlockUpdate(h, 0, h.length), - c.Update(e >> 24 & 255), - c.Update(e >> 16 & 255), - c.Update(e >> 8 & 255), - c.Update(e & 255), - c.DoFinal(d, 0)); - Array.Copy(d, 0, b, k, a); - for (l = 0; l < b.length; l++) - b[l] &= 255; - return b - }, - InitEncipher: function (a) { - var b = null, - c = null, - c = new KJUR.crypto.ECDSA({ - curve: "sm2" - }), - d = c.generateKeyPairHex(), - b = new BigInteger(d.ecprvhex, 16), - c = ECPointFp.decodeFromHex(c.ecparams.curve, d.ecpubhex); - this.p2 = a.multiply(b); - this.Reset(); - return c - }, - EncryptBlock: function (a) { - this.sm3c3.BlockUpdate(a, 0, a.length); - for (var b = this.KDF(a.length), c = 0; c < a.length; c++) - a[c] ^= b[c] - }, - InitDecipher: function (a, b) { - this.p2 = b.multiply(a); - this.p2.getX().toBigInteger().toRadix(16); - this.p2.getY().toBigInteger().toRadix(16); - this.Reset() - }, - DecryptBlock: function (a) { - for (var b = this.KDF(a.length), c = 0, d = "", c = 0; c < b.length; c++) - d += b[c].toString(16); - for (c = 0; c < a.length; c++) - a[c] ^= b[c]; - this.sm3c3.BlockUpdate(a, 0, a.length) - }, - Dofinal: function (a) { - for (var b = this.p2.getY().toBigInteger().toRadix(16); 64 > b.length;) - b = "0" + b; - b = this.GetWords(b); - this.sm3c3.BlockUpdate(b, 0, b.length); - this.sm3c3.DoFinal(a, 0); - this.Reset() - }, - Encrypt: function (a, b) { - var c = Array(b.length); - Array.Copy(b, 0, c, 0, b.length); - var d = this.InitEncipher(a); - this.EncryptBlock(c); - var e = Array(32); - this.Dofinal(e); - for (var f = d.getX().toBigInteger().toRadix(16), d = d.getY().toBigInteger().toRadix(16); 64 > f.length;) - f = "0" + f; - for (; 64 > d.length;) - d = "0" + d; - f += d; - c = this.GetHex(c).toString(); - 0 != c.length % 2 && (c = "0" + c); - e = this.GetHex(e).toString(); - d = f + c + e; - this.cipherMode == SM2CipherMode.C1C3C2 && (d = f + e + c); - return d - }, - GetWords: function (a) { - for (var b = [], c = a.length, d = 0; d < c; d += 2) - b[b.length] = parseInt(a.substr(d, 2), 16); - return b - }, - GetHex: function (a) { - for (var b = [], c = 0, d = 0; d < 2 * a.length; d += 2) - b[d >>> 3] |= parseInt(a[c]) << 24 - d % 8 * 4, - c++; - return new CryptoJS.lib.WordArray.init(b, a.length) - }, - Decrypt: function (a, b) { - var c = b.substr(0, 64), - d = b.substr(0 + c.length, 64), - e = b.substr(c.length + d.length, b.length - c.length - d.length - 64), - f = b.substr(b.length - 64); - this.cipherMode == SM2CipherMode.C1C3C2 && (f = b.substr(c.length + d.length, 64), - e = b.substr(c.length + d.length + 64)); - e = this.GetWords(e); - c = this.CreatePoint(c, d); - this.InitDecipher(a, c); - this.DecryptBlock(e); - c = Array(32); - this.Dofinal(c); - return this.GetHex(c).toString() == f ? (f = this.GetHex(e), - CryptoJS.enc.Utf8.stringify(f)) : "" - }, - CreatePoint: function (a, b) { - var c = new KJUR.crypto.ECDSA({ - curve: "sm2" - }); - return ECPointFp.decodeFromHex(c.ecparams.curve, "04" + a + b) - } - }; - - /*-------------下面修改----------*/ - - var SM2Key = function (key) { - this.setKey(key); - }; - - function SM2SetKey(key) { - if (key && typeof key === 'object') { - this.eccX = key.eccX; - this.eccY = key.eccY; - } else { - this.eccX = "F1342ADB38855E1F8C37D1181378DE446E52788389F7DB3DEA022A1FC4D4D856"; - this.eccY = "66FC6DE253C822F1E52914D9E0B80C5D825759CE696CF039A8449F98017510B7"; - } - } - - /* - *加密数据 - */ - function SM2Encrypt(text) { - var cipherMode = SM2CipherMode.C1C3C2, - cipher = new SM2Cipher(cipherMode), - textData = CryptoJS.enc.Utf8.parse(text); - var cipher = new SM2Cipher(cipherMode); - var userKey = cipher.CreatePoint(this.eccX, this.eccY); - var msgData = cipher.GetWords(textData.toString()); - - return cipher.Encrypt(userKey, msgData); - } - - SM2Key.prototype.setKey = SM2SetKey; - SM2Key.prototype.encrypt = SM2Encrypt; - - //export default SM2Key; - global.SM2 = { - SM2CipherMode: SM2CipherMode, - SM2Cipher: SM2Cipher, - CryptoJS: CryptoJS - } -}(window)); - -window.SM2Utils = {}; - -function sm2Encrypt(data, publickey, cipherMode) { - cipherMode = cipherMode == 0 ? cipherMode : 1; - // msg = SM2.utf8tob64(msg); - var msgData = CryptoJS.enc.Utf8.parse(data); - - msgData = CryptoJS.enc.Base64.stringify(msgData); - //在转utf-8 - msgData = CryptoJS.enc.Utf8.parse(msgData); - - var pubkeyHex = publickey; - if (pubkeyHex.length > 64 * 2) { - pubkeyHex = pubkeyHex.substr(pubkeyHex.length - 64 * 2); - } - var xHex = pubkeyHex.substr(0, 64); - var yHex = pubkeyHex.substr(64); - var cipher = new SM2Cipher(cipherMode); - var userKey = cipher.CreatePoint(xHex, yHex); - msgData = cipher.GetWords(msgData.toString()); - var encryptData = cipher.Encrypt(userKey, msgData); - - return '04' + encryptData; -} -function encrypt(word) { - var key = CryptoJS.enc.Utf8.parse("njcnpqsqpncjnggg"); - var srcs = CryptoJS.enc.Utf8.parse(word); - var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}); - return encrypted.toString(); -} -/** - * 根据公钥进行加密 - */ - function sm2(s, key, cipherMode) { - if (s == null || s.length == 0) { - return ""; - } - return sm2Encrypt(s, key, cipherMode); -} -//var sm2Encrypt = SM2Utils.encs -export { - sm2, - encrypt - } - \ No newline at end of file diff --git a/frontend/src/assets/commjs/sm3.js b/frontend/src/assets/commjs/sm3.js deleted file mode 100644 index ff88477..0000000 --- a/frontend/src/assets/commjs/sm3.js +++ /dev/null @@ -1,257 +0,0 @@ -/** - * 国密摘要算法(SM3) - * @param str:raw string - * @return the 256-bit hex string produced by SM3 from a raw string - */ -function sm3Digest(str) { - //1. 转换为二进制数组 - var binArr = str2bin(str2rstr_utf8(str)); - //2. 填充 - var groupNum = alignSM3(binArr, str.length); - //3. 迭代压缩 - var v = new Array(8);//初始值 - v[0] = 0x7380166f; - v[1] = 0x4914b2b9; - v[2] = 0x172442d7; - v[3] = 0xda8a0600; - v[4] = 0xa96f30bc; - v[5] = 0x163138aa; - v[6] = 0xe38dee4d; - v[7] = 0xb0fb0e4e; - //按 512bit 分组进行压缩 - for (var i = 0; i < groupNum; i++) { - v = compress(v, binArr, i); - } - return word2str(v, ''); -} - -/** - * 将数组转换为字符串。数组长度不定,每个元素为 32bit 的数字。 - * @param words:数组,每个元素为 32bit 的数字 - * @param seperator:在每个数组元素转换得到的字符串之间的分隔符 - */ -function word2str(words, seperator) { - var prefix = Array(8).join('0'); - for (var i = 0; i < words.length; i++) { - //若 hex 不足 8 位,则高位补 0 - words[i] = (prefix + (words[i] >>> 0).toString(16)).slice(-8); - } - - return words.join(seperator); -} - -/** - * 将字符串转换为二进制数组,默认字符串编码为 UTF-8,且范围在 0x00~0xFF 内。 - * 若某些字符的编码超过此范围,则会只保留最低字节。加密可正常进行,但加密结果有误。 - * 每个数组元素包含 4 个字符,即 32 bit。 - * @param 字符串 - * @return 数组,长度为(字符串长度 / 4),每个元素为 32bit 的数字 - */ -function str2bin(str) { - var binary = new Array(str.length >> 2); - for (var i = 0; i < str.length * 8; i += 8) { - binary[i >> 5] |= (str.charCodeAt(i / 8) & 0xFF) << (24 - i % 32); - } - return binary; -} - -/** - * 对明文的二进制串进行填充 - *
- * |  满足 mod 512 = 448 |           固定 64 位         |
- * | 明文二进制 |填充部分|明文二进制串的长度的二进制表示|
- *  xxxxxxxxxxxx 10.....0 0...........................xx
- * 
- * @param arr:数组,每个元素为 32bit 的数字 - * @param strLen:明文字符串长度 - * @return 数组,每个元素为 32bit 的数字,数组长度为 16 的倍数(包括 16) - */ -function alignSM3(arr, strLen) { - //在明文二进制串后面拼接 1000 0000 - arr[strLen >> 2] |= 0x80 << (24 - strLen % 4 * 8); - var groupNum = ((strLen + 8) >> 6) + 1;//以 512bit 为一组,总的组数 - var wordNum = groupNum * 16;//一个 word 32bit,总的 word 数 - - for (var i = (strLen >> 2) + 1; i < wordNum; i++) { - arr[i] = 0; - } - arr[wordNum - 1] = strLen * 8;//在末尾填上明文的二进制长度 - - return groupNum; -} - -/** - * 压缩函数中的置换函数 - */ -function p0(x) { - return x ^ bitRol(x, 9) ^ bitRol(x, 17); -} - -/** - * 压缩函数中的置换函数 - */ -function p1(x) { - return x ^ bitRol(x, 15) ^ bitRol(x, 23); -} - -/** - * 循环左移 - */ -function bitRol(input, n) { - return (input << n) | (input >>> (32 - n)); -} - -/** - * 压缩函数 - */ -function compress(v, binArr, i) { - //将消息分组扩展成 132 个字 - var w1 = new Array(68); - var w2 = new Array(64); - for (var j = 0; j < 68; j++) { - if (j < 16) { - w1[j] = binArr[i * 16 + j]; - } else { - w1[j] = p1(w1[j-16] ^ w1[j-9] ^ bitRol(w1[j-3], 15)) ^ bitRol(w1[j-13], 7) ^ w1[j-6]; - } - } - for (var j = 0; j < 64; j++) { - w2[j] = w1[j] ^ w1[j+4]; - } - - //压缩 - var a = v[0]; - var b = v[1]; - var c = v[2]; - var d = v[3]; - var e = v[4]; - var f = v[5]; - var g = v[6]; - var h = v[7]; - var ss1; - var ss2; - var tt1; - var tt2; - for (var j = 0; j < 64; j++) { - ss1 = bitRol(addAll(bitRol(a, 12) , e , bitRol(t(j), j)), 7); - ss2 = ss1 ^ bitRol(a, 12); - tt1 = addAll(ff(a, b, c, j) , d , ss2 , w2[j]); - tt2 = addAll(gg(e, f, g, j) , h , ss1 , w1[j]); - d = c; - c = bitRol(b, 9); - b = a; - a = tt1; - h = g; - g = bitRol(f, 19); - f = e; - e = p0(tt2); - } - v[0] ^= a; - v[1] ^= b; - v[2] ^= c; - v[3] ^= d; - v[4] ^= e; - v[5] ^= f; - v[6] ^= g; - v[7] ^= h; - return v; -} - -/** - * 常量 T 随 j 的不同而不同 - */ -function t(j) { - if (0 <= j && j < 16) { - return 0x79CC4519; - } else if (j < 64) { - return 0x7A879D8A; - } -} - -/** - * 布尔函数,随 j 的变化取不同的表达式 - */ -function ff(x, y, z, j) { - if (0 <= j && j < 16) { - return x ^ y ^ z; - } else if (j < 64) { - return (x & y) | (x & z) | (y & z); - } -} - -/** - * 布尔函数,随 j 的变化取不同的表达式 - */ -function gg(x, y, z, j) { - if (0 <= j && j < 16) { - return x ^ y ^ z; - } else if (j < 64) { - return (x & y) | (~x & z); - } -} - -/** - * 两数相加 - * 避免某些 js 引擎的 32 位加法的 bug - */ -function safe_add(x, y) { - var lsw = ( x & 0xFFFF ) + (y & 0xFFFF); - var msw = ( x >> 16 ) + (y >> 16) + (lsw >> 16); - return (msw << 16) | ( lsw & 0xFFFF ); -} - -/** - * 将所有参数相加 - */ -function addAll() { - var sum = 0; - for (var i = 0; i < arguments.length; i++) { - sum = safe_add(sum, arguments[i]); - } - return sum; -} - -/** - * UTF-16 --> UTF-8 - */ -function str2rstr_utf8(input) { - var output = "" ; - var i = -1 ; - var x, y ; - - while(++ i < input.length) { - //按 UTF-16 解码 - x = input.charCodeAt(i); - y = i + 1 < input.length ? input .charCodeAt (i + 1) : 0 ; - if( 0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF ) { - x = 0x10000 + ((x & 0x03FF) << 10 ) + (y & 0x03FF); - i++; - } - - //按 UTF-8 编码 - if( x <= 0x7F ) { - output += String.fromCharCode(x); - } - else if(x <= 0x7FF) { - output += String.fromCharCode( - 0xC0 | ((x >>> 6 ) & 0x1F), - 0x80 | ( x & 0x3F )); - } else if(x <= 0xFFFF) { - output += String.fromCharCode( - 0xE0 | ((x >>> 12) & 0x0F ), - 0x80 | ((x >>> 6 ) & 0x3F), - 0x80 | ( x & 0x3F )); - } else if(x <= 0x1FFFFF) { - output += String.fromCharCode( - 0xF0 | ((x >>> 18) & 0x07 ), - 0x80 | ((x >>> 12) & 0x3F), - 0x80 | ((x >>> 6 ) & 0x3F), - 0x80 | ( x & 0x3F )); - } - } - return output; -} -export { - sm3Digest - } - diff --git a/frontend/src/assets/font/DongFangDaKai-Regular.woff b/frontend/src/assets/font/DongFangDaKai-Regular.woff deleted file mode 100644 index ac40fe6..0000000 Binary files a/frontend/src/assets/font/DongFangDaKai-Regular.woff and /dev/null differ diff --git a/frontend/src/assets/font/DongFangDaKai-Regular.woff2 b/frontend/src/assets/font/DongFangDaKai-Regular.woff2 deleted file mode 100644 index 7901e46..0000000 Binary files a/frontend/src/assets/font/DongFangDaKai-Regular.woff2 and /dev/null differ diff --git a/frontend/src/assets/font/MFBanHei.ttf b/frontend/src/assets/font/MFBanHei.ttf deleted file mode 100644 index fa1d281..0000000 Binary files a/frontend/src/assets/font/MFBanHei.ttf and /dev/null differ diff --git a/frontend/src/assets/fonts/DIN.otf b/frontend/src/assets/fonts/DIN.otf new file mode 100644 index 0000000..3296d1e Binary files /dev/null and b/frontend/src/assets/fonts/DIN.otf differ diff --git a/frontend/src/assets/fonts/MetroDF.ttf b/frontend/src/assets/fonts/MetroDF.ttf new file mode 100644 index 0000000..9d31af4 Binary files /dev/null and b/frontend/src/assets/fonts/MetroDF.ttf differ diff --git a/frontend/src/assets/fonts/YouSheBiaoTiHei.ttf b/frontend/src/assets/fonts/YouSheBiaoTiHei.ttf new file mode 100644 index 0000000..3729151 Binary files /dev/null and b/frontend/src/assets/fonts/YouSheBiaoTiHei.ttf differ diff --git a/frontend/src/assets/fonts/font.scss b/frontend/src/assets/fonts/font.scss new file mode 100644 index 0000000..cc0ee58 --- /dev/null +++ b/frontend/src/assets/fonts/font.scss @@ -0,0 +1,14 @@ +@font-face { + font-family: YouSheBiaoTiHei; + src: url("./YouSheBiaoTiHei.ttf"); +} + +@font-face { + font-family: MetroDF; + src: url("./MetroDF.ttf"); +} + +@font-face { + font-family: DIN; + src: url("./DIN.Otf"); +} diff --git a/frontend/src/assets/iconfont/iconfont.scss b/frontend/src/assets/iconfont/iconfont.scss new file mode 100644 index 0000000..e9db2a6 --- /dev/null +++ b/frontend/src/assets/iconfont/iconfont.scss @@ -0,0 +1,48 @@ +@font-face { + font-family: iconfont; /* Project id 2667653 */ + src: url("iconfont.ttf?t=1694681005434") format("truetype"); +} +.iconfont { + font-family: iconfont !important; + font-size: 20px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + cursor: pointer; +} +.icon-yiwen::before { + font-size: 15px; + content: "\e693"; +} +.icon-xiala::before { + content: "\e62b"; +} +.icon-tuichu::before { + content: "\e645"; +} +.icon-xiaoxi::before { + font-size: 21.2px; + content: "\e61f"; +} +.icon-zhuti::before { + font-size: 22.4px; + content: "\e638"; +} +.icon-sousuo::before { + content: "\e611"; +} +.icon-contentright::before { + content: "\e8c9"; +} +.icon-contentleft::before { + content: "\e8ca"; +} +.icon-fangda::before { + content: "\e826"; +} +.icon-suoxiao::before { + content: "\e641"; +} +.icon-zhongyingwen::before { + content: "\e8cb"; +} diff --git a/frontend/src/assets/iconfont/iconfont.ttf b/frontend/src/assets/iconfont/iconfont.ttf new file mode 100644 index 0000000..99e4571 Binary files /dev/null and b/frontend/src/assets/iconfont/iconfont.ttf differ diff --git a/frontend/src/assets/icons/wind.svg b/frontend/src/assets/icons/wind.svg deleted file mode 100644 index 797905f..0000000 --- a/frontend/src/assets/icons/wind.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxingdaoyu.svg b/frontend/src/assets/icons/xianxingdaoyu.svg new file mode 100644 index 0000000..edfac8d --- /dev/null +++ b/frontend/src/assets/icons/xianxingdaoyu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxingdiqiu.svg b/frontend/src/assets/icons/xianxingdiqiu.svg new file mode 100644 index 0000000..e13cb00 --- /dev/null +++ b/frontend/src/assets/icons/xianxingdiqiu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxingditu.svg b/frontend/src/assets/icons/xianxingditu.svg new file mode 100644 index 0000000..ef1bdda --- /dev/null +++ b/frontend/src/assets/icons/xianxingditu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxingfanchuan.svg b/frontend/src/assets/icons/xianxingfanchuan.svg new file mode 100644 index 0000000..2c2da28 --- /dev/null +++ b/frontend/src/assets/icons/xianxingfanchuan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxingfeiji.svg b/frontend/src/assets/icons/xianxingfeiji.svg new file mode 100644 index 0000000..e4d9a87 --- /dev/null +++ b/frontend/src/assets/icons/xianxingfeiji.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxinglvhangriji.svg b/frontend/src/assets/icons/xianxinglvhangriji.svg new file mode 100644 index 0000000..2e1e47d --- /dev/null +++ b/frontend/src/assets/icons/xianxinglvhangriji.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxingtianqiyubao.svg b/frontend/src/assets/icons/xianxingtianqiyubao.svg new file mode 100644 index 0000000..3283580 --- /dev/null +++ b/frontend/src/assets/icons/xianxingtianqiyubao.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxingxiangjipaizhao.svg b/frontend/src/assets/icons/xianxingxiangjipaizhao.svg new file mode 100644 index 0000000..93ff9f5 --- /dev/null +++ b/frontend/src/assets/icons/xianxingxiangjipaizhao.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxingxiarilengyin.svg b/frontend/src/assets/icons/xianxingxiarilengyin.svg new file mode 100644 index 0000000..87132d1 --- /dev/null +++ b/frontend/src/assets/icons/xianxingxiarilengyin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxingyoulun.svg b/frontend/src/assets/icons/xianxingyoulun.svg new file mode 100644 index 0000000..487e1ee --- /dev/null +++ b/frontend/src/assets/icons/xianxingyoulun.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/xianxingzijiayou.svg b/frontend/src/assets/icons/xianxingzijiayou.svg new file mode 100644 index 0000000..449c610 --- /dev/null +++ b/frontend/src/assets/icons/xianxingzijiayou.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/images/403.png b/frontend/src/assets/images/403.png new file mode 100644 index 0000000..0c5ec7c Binary files /dev/null and b/frontend/src/assets/images/403.png differ diff --git a/frontend/src/assets/images/404.png b/frontend/src/assets/images/404.png new file mode 100644 index 0000000..2333586 Binary files /dev/null and b/frontend/src/assets/images/404.png differ diff --git a/frontend/src/assets/images/500.png b/frontend/src/assets/images/500.png new file mode 100644 index 0000000..6f3b6bf Binary files /dev/null and b/frontend/src/assets/images/500.png differ diff --git a/frontend/src/assets/images/avatar.gif b/frontend/src/assets/images/avatar.gif new file mode 100644 index 0000000..fdbd32c Binary files /dev/null and b/frontend/src/assets/images/avatar.gif differ diff --git a/frontend/src/assets/images/login/background.png b/frontend/src/assets/images/login/background.png deleted file mode 100644 index be0d12b..0000000 Binary files a/frontend/src/assets/images/login/background.png and /dev/null differ diff --git a/frontend/src/assets/images/login_bg.svg b/frontend/src/assets/images/login_bg.svg new file mode 100644 index 0000000..0a9514b --- /dev/null +++ b/frontend/src/assets/images/login_bg.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/images/login_left.png b/frontend/src/assets/images/login_left.png new file mode 100644 index 0000000..e9ebc11 Binary files /dev/null and b/frontend/src/assets/images/login_left.png differ diff --git a/frontend/src/assets/images/login_left1.png b/frontend/src/assets/images/login_left1.png new file mode 100644 index 0000000..eaaf3ab Binary files /dev/null and b/frontend/src/assets/images/login_left1.png differ diff --git a/frontend/src/assets/images/login_left2.png b/frontend/src/assets/images/login_left2.png new file mode 100644 index 0000000..0398053 Binary files /dev/null and b/frontend/src/assets/images/login_left2.png differ diff --git a/frontend/src/assets/images/login_left3.png b/frontend/src/assets/images/login_left3.png new file mode 100644 index 0000000..58e6e74 Binary files /dev/null and b/frontend/src/assets/images/login_left3.png differ diff --git a/frontend/src/assets/images/login_left4.png b/frontend/src/assets/images/login_left4.png new file mode 100644 index 0000000..051299d Binary files /dev/null and b/frontend/src/assets/images/login_left4.png differ diff --git a/frontend/src/assets/images/login_left5.png b/frontend/src/assets/images/login_left5.png new file mode 100644 index 0000000..7d9d430 Binary files /dev/null and b/frontend/src/assets/images/login_left5.png differ diff --git a/frontend/src/assets/images/logo.png b/frontend/src/assets/images/logo.png deleted file mode 100644 index 1889798..0000000 Binary files a/frontend/src/assets/images/logo.png and /dev/null differ diff --git a/frontend/src/assets/images/logo.svg b/frontend/src/assets/images/logo.svg new file mode 100644 index 0000000..7565660 --- /dev/null +++ b/frontend/src/assets/images/logo.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/images/msg01.png b/frontend/src/assets/images/msg01.png new file mode 100644 index 0000000..6ecca0d Binary files /dev/null and b/frontend/src/assets/images/msg01.png differ diff --git a/frontend/src/assets/images/msg02.png b/frontend/src/assets/images/msg02.png new file mode 100644 index 0000000..52c890a Binary files /dev/null and b/frontend/src/assets/images/msg02.png differ diff --git a/frontend/src/assets/images/msg03.png b/frontend/src/assets/images/msg03.png new file mode 100644 index 0000000..389512f Binary files /dev/null and b/frontend/src/assets/images/msg03.png differ diff --git a/frontend/src/assets/images/msg04.png b/frontend/src/assets/images/msg04.png new file mode 100644 index 0000000..6868f74 Binary files /dev/null and b/frontend/src/assets/images/msg04.png differ diff --git a/frontend/src/assets/images/msg05.png b/frontend/src/assets/images/msg05.png new file mode 100644 index 0000000..f375ab0 Binary files /dev/null and b/frontend/src/assets/images/msg05.png differ diff --git a/frontend/src/assets/images/notData.png b/frontend/src/assets/images/notData.png new file mode 100644 index 0000000..c579a78 Binary files /dev/null and b/frontend/src/assets/images/notData.png differ diff --git a/frontend/src/assets/images/welcome.png b/frontend/src/assets/images/welcome.png new file mode 100644 index 0000000..df138ab Binary files /dev/null and b/frontend/src/assets/images/welcome.png differ diff --git a/frontend/src/assets/logo.png b/frontend/src/assets/logo.png deleted file mode 100644 index 95dc60b..0000000 Binary files a/frontend/src/assets/logo.png and /dev/null differ diff --git a/frontend/src/assets/styles/tailMain.css b/frontend/src/assets/styles/tailMain.css deleted file mode 100644 index 56c5d07..0000000 --- a/frontend/src/assets/styles/tailMain.css +++ /dev/null @@ -1,19 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - - - -/* 申明字体为东方大楷 */ -@font-face { - font-family: 'DongFangDaKai'; - src: url('@/assets/font/DongFangDaKai-Regular.woff') format('woff'), - url('@/assets/font/DongFangDaKai-Regular.woff2') format('woff2'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: "MFBanHei"; /* Project id 1513211 */ - src: url('@/assets/font/MFBanHei.ttf?t=1643094287456') format('truetype'); -} diff --git a/frontend/src/components/ErrorMessage/403.vue b/frontend/src/components/ErrorMessage/403.vue new file mode 100644 index 0000000..73783ec --- /dev/null +++ b/frontend/src/components/ErrorMessage/403.vue @@ -0,0 +1,19 @@ + + + + + diff --git a/frontend/src/components/ErrorMessage/404.vue b/frontend/src/components/ErrorMessage/404.vue new file mode 100644 index 0000000..831a1e9 --- /dev/null +++ b/frontend/src/components/ErrorMessage/404.vue @@ -0,0 +1,19 @@ + + + + + diff --git a/frontend/src/components/ErrorMessage/500.vue b/frontend/src/components/ErrorMessage/500.vue new file mode 100644 index 0000000..14baedc --- /dev/null +++ b/frontend/src/components/ErrorMessage/500.vue @@ -0,0 +1,19 @@ + + + + + diff --git a/frontend/src/components/ErrorMessage/index.scss b/frontend/src/components/ErrorMessage/index.scss new file mode 100644 index 0000000..7a788b4 --- /dev/null +++ b/frontend/src/components/ErrorMessage/index.scss @@ -0,0 +1,32 @@ +.not-container { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + .not-img { + margin-right: 120px; + } + .not-detail { + display: flex; + flex-direction: column; + h2, + h4 { + padding: 0; + margin: 0; + } + h2 { + font-size: 60px; + color: var(--el-text-color-primary); + } + h4 { + margin: 30px 0 20px; + font-size: 19px; + font-weight: normal; + color: var(--el-text-color-regular); + } + .el-button { + width: 100px; + } + } +} diff --git a/frontend/src/components/Loading/fullScreen.ts b/frontend/src/components/Loading/fullScreen.ts new file mode 100644 index 0000000..ea4dc92 --- /dev/null +++ b/frontend/src/components/Loading/fullScreen.ts @@ -0,0 +1,45 @@ +import { ElLoading } from "element-plus"; + +/* 全局请求 loading */ +let loadingInstance: ReturnType; + +/** + * @description 开启 Loading + * */ +const startLoading = () => { + loadingInstance = ElLoading.service({ + fullscreen: true, + lock: true, + text: "Loading", + background: "rgba(0, 0, 0, 0.7)" + }); +}; + +/** + * @description 结束 Loading + * */ +const endLoading = () => { + loadingInstance.close(); +}; + +/** + * @description 显示全屏加载 + * */ +let needLoadingRequestCount = 0; +export const showFullScreenLoading = () => { + if (needLoadingRequestCount === 0) { + startLoading(); + } + needLoadingRequestCount++; +}; + +/** + * @description 隐藏全屏加载 + * */ +export const tryHideFullScreenLoading = () => { + if (needLoadingRequestCount <= 0) return; + needLoadingRequestCount--; + if (needLoadingRequestCount === 0) { + endLoading(); + } +}; diff --git a/frontend/src/components/Loading/index.scss b/frontend/src/components/Loading/index.scss new file mode 100644 index 0000000..f1a7125 --- /dev/null +++ b/frontend/src/components/Loading/index.scss @@ -0,0 +1,67 @@ +.loading-box { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + .loading-wrap { + display: flex; + align-items: center; + justify-content: center; + padding: 98px; + } +} +.dot { + position: relative; + box-sizing: border-box; + display: inline-block; + width: 32px; + height: 32px; + font-size: 32px; + transform: rotate(45deg); + animation: ant-rotate 1.2s infinite linear; +} +.dot i { + position: absolute; + display: block; + width: 14px; + height: 14px; + background-color: var(--el-color-primary); + border-radius: 100%; + opacity: 0.3; + transform: scale(0.75); + transform-origin: 50% 50%; + animation: ant-spin-move 1s infinite linear alternate; +} +.dot i:nth-child(1) { + top: 0; + left: 0; +} +.dot i:nth-child(2) { + top: 0; + right: 0; + animation-delay: 0.4s; +} +.dot i:nth-child(3) { + right: 0; + bottom: 0; + animation-delay: 0.8s; +} +.dot i:nth-child(4) { + bottom: 0; + left: 0; + animation-delay: 1.2s; +} + +@keyframes ant-rotate { + to { + transform: rotate(405deg); + } +} + +@keyframes ant-spin-move { + to { + opacity: 1; + } +} diff --git a/frontend/src/components/Loading/index.vue b/frontend/src/components/Loading/index.vue new file mode 100644 index 0000000..5b209a5 --- /dev/null +++ b/frontend/src/components/Loading/index.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/frontend/src/components/SwitchDark/index.vue b/frontend/src/components/SwitchDark/index.vue new file mode 100644 index 0000000..7e1d85c --- /dev/null +++ b/frontend/src/components/SwitchDark/index.vue @@ -0,0 +1,12 @@ + + + diff --git a/frontend/src/config/index.ts b/frontend/src/config/index.ts new file mode 100644 index 0000000..f76ab34 --- /dev/null +++ b/frontend/src/config/index.ts @@ -0,0 +1,19 @@ +// ? 全局默认配置项 + +// 首页地址(默认) +export const HOME_URL: string = "/home/index"; + +// 登录页地址(默认) +export const LOGIN_URL: string = "/login"; + +// 默认主题颜色 +export const DEFAULT_PRIMARY: string = "#003078"; + +// 路由白名单地址(本地存在的路由 staticRouter.ts 中) +export const ROUTER_WHITE_LIST: string[] = ["/500"]; + +// 高德地图 key +export const AMAP_MAP_KEY: string = ""; + +// 百度地图 key +export const BAIDU_MAP_KEY: string = ""; diff --git a/frontend/src/config/nprogress.ts b/frontend/src/config/nprogress.ts new file mode 100644 index 0000000..6b29db4 --- /dev/null +++ b/frontend/src/config/nprogress.ts @@ -0,0 +1,12 @@ +import NProgress from "nprogress"; +import "nprogress/nprogress.css"; + +NProgress.configure({ + easing: "ease", // 动画方式 + speed: 500, // 递增进度条的速度 + showSpinner: true, // 是否显示加载ico + trickleSpeed: 200, // 自动递增间隔 + minimum: 0.3 // 初始化时的最小百分比 +}); + +export default NProgress; diff --git a/frontend/src/directives/index.ts b/frontend/src/directives/index.ts new file mode 100644 index 0000000..436b954 --- /dev/null +++ b/frontend/src/directives/index.ts @@ -0,0 +1,28 @@ +import { App, Directive } from "vue"; +import auth from "./modules/auth"; +import copy from "./modules/copy"; +import waterMarker from "./modules/waterMarker"; +import draggable from "./modules/draggable"; +import debounce from "./modules/debounce"; +import throttle from "./modules/throttle"; +import longpress from "./modules/longpress"; + +const directivesList: { [key: string]: Directive } = { + auth, + copy, + waterMarker, + draggable, + debounce, + throttle, + longpress +}; + +const directives = { + install: function (app: App) { + Object.keys(directivesList).forEach(key => { + app.directive(key, directivesList[key]); + }); + } +}; + +export default directives; diff --git a/frontend/src/directives/modules/auth.ts b/frontend/src/directives/modules/auth.ts new file mode 100644 index 0000000..a9c9fe4 --- /dev/null +++ b/frontend/src/directives/modules/auth.ts @@ -0,0 +1,22 @@ +/** + * v-auth + * 按钮权限指令 + */ +import { useAuthStore } from "@/stores/modules/auth"; +import type { Directive, DirectiveBinding } from "vue"; + +const auth: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const { value } = binding; + const authStore = useAuthStore(); + const currentPageRoles = authStore.authButtonListGet[authStore.routeName] ?? []; + if (value instanceof Array && value.length) { + const hasPermission = value.every(item => currentPageRoles.includes(item)); + if (!hasPermission) el.remove(); + } else { + if (!currentPageRoles.includes(value)) el.remove(); + } + } +}; + +export default auth; diff --git a/frontend/src/directives/modules/copy.ts b/frontend/src/directives/modules/copy.ts new file mode 100644 index 0000000..47d86e2 --- /dev/null +++ b/frontend/src/directives/modules/copy.ts @@ -0,0 +1,37 @@ +/** + * v-copy + * 复制某个值至剪贴板 + * 接收参数:string类型/Ref类型/Reactive类型 + */ +import type { Directive, DirectiveBinding } from "vue"; +import { ElMessage } from "element-plus"; +interface ElType extends HTMLElement { + copyData: string | number; + __handleClick__: any; +} +const copy: Directive = { + mounted(el: ElType, binding: DirectiveBinding) { + el.copyData = binding.value; + el.addEventListener("click", handleClick); + }, + updated(el: ElType, binding: DirectiveBinding) { + el.copyData = binding.value; + }, + beforeUnmount(el: ElType) { + el.removeEventListener("click", el.__handleClick__); + } +}; + +async function handleClick(this: any) { + try { + await navigator.clipboard.writeText(this.copyData); + } catch (err) { + console.error("复制操作不被支持或失败: ", err); + } + ElMessage({ + type: "success", + message: "复制成功" + }); +} + +export default copy; diff --git a/frontend/src/directives/modules/debounce.ts b/frontend/src/directives/modules/debounce.ts new file mode 100644 index 0000000..7346bfe --- /dev/null +++ b/frontend/src/directives/modules/debounce.ts @@ -0,0 +1,31 @@ +/** + * v-debounce + * 按钮防抖指令,可自行扩展至input + * 接收参数:function类型 + */ +import type { Directive, DirectiveBinding } from "vue"; +interface ElType extends HTMLElement { + __handleClick__: () => any; +} +const debounce: Directive = { + mounted(el: ElType, binding: DirectiveBinding) { + if (typeof binding.value !== "function") { + throw "callback must be a function"; + } + let timer: NodeJS.Timeout | null = null; + el.__handleClick__ = function () { + if (timer) { + clearInterval(timer); + } + timer = setTimeout(() => { + binding.value(); + }, 500); + }; + el.addEventListener("click", el.__handleClick__); + }, + beforeUnmount(el: ElType) { + el.removeEventListener("click", el.__handleClick__); + } +}; + +export default debounce; diff --git a/frontend/src/directives/modules/draggable.ts b/frontend/src/directives/modules/draggable.ts new file mode 100644 index 0000000..3a737d6 --- /dev/null +++ b/frontend/src/directives/modules/draggable.ts @@ -0,0 +1,49 @@ +/* + 需求:实现一个拖拽指令,可在父元素区域任意拖拽元素。 + + 思路: + 1、设置需要拖拽的元素为absolute,其父元素为relative。 + 2、鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。 + 3、鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值 + 4、鼠标松开(onmouseup)时完成一次拖拽 + + 使用:在 Dom 上加上 v-draggable 即可 +
+*/ +import type { Directive } from "vue"; +interface ElType extends HTMLElement { + parentNode: any; +} +const draggable: Directive = { + mounted: function (el: ElType) { + el.style.cursor = "move"; + el.style.position = "absolute"; + el.onmousedown = function (e) { + let disX = e.pageX - el.offsetLeft; + let disY = e.pageY - el.offsetTop; + document.onmousemove = function (e) { + let x = e.pageX - disX; + let y = e.pageY - disY; + let maxX = el.parentNode.offsetWidth - el.offsetWidth; + let maxY = el.parentNode.offsetHeight - el.offsetHeight; + if (x < 0) { + x = 0; + } else if (x > maxX) { + x = maxX; + } + + if (y < 0) { + y = 0; + } else if (y > maxY) { + y = maxY; + } + el.style.left = x + "px"; + el.style.top = y + "px"; + }; + document.onmouseup = function () { + document.onmousemove = document.onmouseup = null; + }; + }; + } +}; +export default draggable; diff --git a/frontend/src/directives/modules/longpress.ts b/frontend/src/directives/modules/longpress.ts new file mode 100644 index 0000000..e5aa75f --- /dev/null +++ b/frontend/src/directives/modules/longpress.ts @@ -0,0 +1,49 @@ +/** + * v-longpress + * 长按指令,长按时触发事件 + */ +import type { Directive, DirectiveBinding } from "vue"; + +const directive: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + if (typeof binding.value !== "function") { + throw "callback must be a function"; + } + // 定义变量 + let pressTimer: any = null; + // 创建计时器( 2秒后执行函数 ) + const start = (e: any) => { + if (e.button) { + if (e.type === "click" && e.button !== 0) { + return; + } + } + if (pressTimer === null) { + pressTimer = setTimeout(() => { + handler(e); + }, 1000); + } + }; + // 取消计时器 + const cancel = () => { + if (pressTimer !== null) { + clearTimeout(pressTimer); + pressTimer = null; + } + }; + // 运行函数 + const handler = (e: MouseEvent | TouchEvent) => { + binding.value(e); + }; + // 添加事件监听器 + el.addEventListener("mousedown", start); + el.addEventListener("touchstart", start); + // 取消计时器 + el.addEventListener("click", cancel); + el.addEventListener("mouseout", cancel); + el.addEventListener("touchend", cancel); + el.addEventListener("touchcancel", cancel); + } +}; + +export default directive; diff --git a/frontend/src/directives/modules/throttle.ts b/frontend/src/directives/modules/throttle.ts new file mode 100644 index 0000000..a4a6e39 --- /dev/null +++ b/frontend/src/directives/modules/throttle.ts @@ -0,0 +1,41 @@ +/* + 需求:防止按钮在短时间内被多次点击,使用节流函数限制规定时间内只能点击一次。 + + 思路: + 1、第一次点击,立即调用方法并禁用按钮,等延迟结束再次激活按钮 + 2、将需要触发的方法绑定在指令上 + + 使用:给 Dom 加上 v-throttle 及回调函数即可 + +*/ +import type { Directive, DirectiveBinding } from "vue"; +interface ElType extends HTMLElement { + __handleClick__: () => any; + disabled: boolean; +} +const throttle: Directive = { + mounted(el: ElType, binding: DirectiveBinding) { + if (typeof binding.value !== "function") { + throw "callback must be a function"; + } + let timer: NodeJS.Timeout | null = null; + el.__handleClick__ = function () { + if (timer) { + clearTimeout(timer); + } + if (!el.disabled) { + el.disabled = true; + binding.value(); + timer = setTimeout(() => { + el.disabled = false; + }, 1000); + } + }; + el.addEventListener("click", el.__handleClick__); + }, + beforeUnmount(el: ElType) { + el.removeEventListener("click", el.__handleClick__); + } +}; + +export default throttle; diff --git a/frontend/src/directives/modules/waterMarker.ts b/frontend/src/directives/modules/waterMarker.ts new file mode 100644 index 0000000..5f4f9b4 --- /dev/null +++ b/frontend/src/directives/modules/waterMarker.ts @@ -0,0 +1,36 @@ +/* + 需求:给整个页面添加背景水印。 + + 思路: + 1、使用 canvas 特性生成 base64 格式的图片文件,设置其字体大小,颜色等。 + 2、将其设置为背景图片,从而实现页面或组件水印效果 + + 使用:设置水印文案,颜色,字体大小即可 +
+*/ + +import type { Directive, DirectiveBinding } from "vue"; +const addWaterMarker: Directive = (str: string, parentNode: any, font: any, textColor: string) => { + // 水印文字,父元素,字体,文字颜色 + let can: HTMLCanvasElement = document.createElement("canvas"); + parentNode.appendChild(can); + can.width = 205; + can.height = 140; + can.style.display = "none"; + let cans = can.getContext("2d") as CanvasRenderingContext2D; + cans.rotate((-20 * Math.PI) / 180); + cans.font = font || "16px Microsoft JhengHei"; + cans.fillStyle = textColor || "rgba(180, 180, 180, 0.3)"; + cans.textAlign = "left"; + cans.textBaseline = "Middle" as CanvasTextBaseline; + cans.fillText(str, can.width / 10, can.height / 2); + parentNode.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")"; +}; + +const waterMarker = { + mounted(el: DirectiveBinding, binding: DirectiveBinding) { + addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor); + } +}; + +export default waterMarker; diff --git a/frontend/src/enums/httpEnum.ts b/frontend/src/enums/httpEnum.ts new file mode 100644 index 0000000..896db42 --- /dev/null +++ b/frontend/src/enums/httpEnum.ts @@ -0,0 +1,35 @@ +/** + * @description:请求配置 + */ +export enum ResultEnum { + SUCCESS = "200", + ERROR = 500, + OVERDUE = "401", + TIMEOUT = 30000, + TYPE = "success" +} + +/** + * @description:请求方法 + */ +export enum RequestEnum { + GET = "GET", + POST = "POST", + PATCH = "PATCH", + PUT = "PUT", + DELETE = "DELETE" +} + +/** + * @description:常用的 contentTyp 类型 + */ +export enum ContentTypeEnum { + // json + JSON = "application/json;charset=UTF-8", + // text + TEXT = "text/plain;charset=UTF-8", + // form-data 一般配合qs + FORM_URLENCODED = "application/x-www-form-urlencoded;charset=UTF-8", + // form-data 上传 + FORM_DATA = "multipart/form-data;charset=UTF-8" +} diff --git a/frontend/src/hooks/interface/index.ts b/frontend/src/hooks/interface/index.ts new file mode 100644 index 0000000..ac5a300 --- /dev/null +++ b/frontend/src/hooks/interface/index.ts @@ -0,0 +1,32 @@ +export namespace Table { + export interface Pageable { + pageNum: number; + pageSize: number; + total: number; + } + export interface StateProps { + tableData: any[]; + pageable: Pageable; + searchParam: { + [key: string]: any; + }; + searchInitParam: { + [key: string]: any; + }; + totalParam: { + [key: string]: any; + }; + icon?: { + [key: string]: any; + }; + } +} + +export namespace HandleData { + export type MessageType = "" | "success" | "warning" | "info" | "error"; +} + +export namespace Theme { + export type ThemeType = "light" | "inverted" | "dark"; + export type GreyOrWeakType = "grey" | "weak"; +} diff --git a/frontend/src/hooks/useAuthButtons.ts b/frontend/src/hooks/useAuthButtons.ts new file mode 100644 index 0000000..1a89cf9 --- /dev/null +++ b/frontend/src/hooks/useAuthButtons.ts @@ -0,0 +1,22 @@ +import { computed } from "vue"; +import { useRoute } from "vue-router"; +import { useAuthStore } from "@/stores/modules/auth"; + +/** + * @description 页面按钮权限 + * */ +export const useAuthButtons = () => { + const route = useRoute(); + const authStore = useAuthStore(); + const authButtons = authStore.authButtonListGet[route.name as string] || []; + + const BUTTONS = computed(() => { + let currentPageAuthButton: { [key: string]: boolean } = {}; + authButtons.forEach(item => (currentPageAuthButton[item] = true)); + return currentPageAuthButton; + }); + + return { + BUTTONS + }; +}; diff --git a/frontend/src/hooks/useDownload.ts b/frontend/src/hooks/useDownload.ts new file mode 100644 index 0000000..54c3700 --- /dev/null +++ b/frontend/src/hooks/useDownload.ts @@ -0,0 +1,44 @@ +import { ElNotification } from "element-plus"; + +/** + * @description 接收数据流生成 blob,创建链接,下载文件 + * @param {Function} api 导出表格的api方法 (必传) + * @param {String} tempName 导出的文件名 (必传) + * @param {Object} params 导出的参数 (默认{}) + * @param {Boolean} isNotify 是否有导出消息提示 (默认为 true) + * @param {String} fileType 导出的文件格式 (默认为.xlsx) + * */ +export const useDownload = async ( + api: (param: any) => Promise, + tempName: string, + params: any = {}, + isNotify: boolean = true, + fileType: string = ".xlsx" +) => { + if (isNotify) { + ElNotification({ + title: "温馨提示", + message: "如果数据庞大会导致下载缓慢哦,请您耐心等待!", + type: "info", + duration: 3000 + }); + } + try { + const res = await api(params); + const blob = new Blob([res]); + // 兼容 edge 不支持 createObjectURL 方法 + if ("msSaveOrOpenBlob" in navigator) return window.navigator.msSaveOrOpenBlob(blob, tempName + fileType); + const blobUrl = window.URL.createObjectURL(blob); + const exportFile = document.createElement("a"); + exportFile.style.display = "none"; + exportFile.download = `${tempName}${fileType}`; + exportFile.href = blobUrl; + document.body.appendChild(exportFile); + exportFile.click(); + // 去除下载对 url 的影响 + document.body.removeChild(exportFile); + window.URL.revokeObjectURL(blobUrl); + } catch (error) { + console.log(error); + } +}; diff --git a/frontend/src/hooks/useHandleData.ts b/frontend/src/hooks/useHandleData.ts new file mode 100644 index 0000000..3390741 --- /dev/null +++ b/frontend/src/hooks/useHandleData.ts @@ -0,0 +1,34 @@ +import { ElMessageBox, ElMessage } from "element-plus"; +import { HandleData } from "./interface"; + +/** + * @description 操作单条数据信息 (二次确认【删除、禁用、启用、重置密码】) + * @param {Function} api 操作数据接口的api方法 (必传) + * @param {Object} params 携带的操作数据参数 {id,params} (必传) + * @param {String} message 提示信息 (必传) + * @param {String} confirmType icon类型 (不必传,默认为 warning) + * @returns {Promise} + */ +export const useHandleData = ( + api: (params: any) => Promise, + params: any = {}, + message: string, + confirmType: HandleData.MessageType = "warning" +) => { + return new Promise((resolve, reject) => { + ElMessageBox.confirm(`是否${message}?`, "温馨提示", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: confirmType, + draggable: true + }).then(async () => { + const res = await api(params); + if (!res) return reject(false); + ElMessage({ + type: "success", + message: `${message}成功!` + }); + resolve(true); + }); + }); +}; diff --git a/frontend/src/hooks/useOnline.ts b/frontend/src/hooks/useOnline.ts new file mode 100644 index 0000000..f1c1af1 --- /dev/null +++ b/frontend/src/hooks/useOnline.ts @@ -0,0 +1,27 @@ +import { ref, onMounted, onUnmounted } from "vue"; + +/** + * @description 网络是否可用 + * */ +export const useOnline = () => { + const online = ref(true); + const showStatus = (val: any) => { + online.value = typeof val == "boolean" ? val : val.target.online; + }; + // 在页面加载后,设置正确的网络状态 + navigator.onLine ? showStatus(true) : showStatus(false); + + onMounted(() => { + // 开始监听网络状态的变化 + window.addEventListener("online", showStatus); + window.addEventListener("offline", showStatus); + }); + + onUnmounted(() => { + // 移除监听网络状态的变化 + window.removeEventListener("online", showStatus); + window.removeEventListener("offline", showStatus); + }); + + return { online }; +}; diff --git a/frontend/src/hooks/useSelection.ts b/frontend/src/hooks/useSelection.ts new file mode 100644 index 0000000..5cdce57 --- /dev/null +++ b/frontend/src/hooks/useSelection.ts @@ -0,0 +1,34 @@ +import { ref, computed } from "vue"; + +/** + * @description 表格多选数据操作 + * @param {String} rowKey 当表格可以多选时,所指定的 id + * */ +export const useSelection = (rowKey: string = "id") => { + const isSelected = ref(false); + const selectedList = ref<{ [key: string]: any }[]>([]); + + // 当前选中的所有 ids 数组 + const selectedListIds = computed((): string[] => { + let ids: string[] = []; + selectedList.value.forEach(item => ids.push(item[rowKey])); + return ids; + }); + + /** + * @description 多选操作 + * @param {Array} rowArr 当前选择的所有数据 + * @return void + */ + const selectionChange = (rowArr: { [key: string]: any }[]) => { + rowArr.length ? (isSelected.value = true) : (isSelected.value = false); + selectedList.value = rowArr; + }; + + return { + isSelected, + selectedList, + selectedListIds, + selectionChange + }; +}; diff --git a/frontend/src/hooks/useTable.ts b/frontend/src/hooks/useTable.ts new file mode 100644 index 0000000..4decc65 --- /dev/null +++ b/frontend/src/hooks/useTable.ts @@ -0,0 +1,154 @@ +import { Table } from "./interface"; +import { reactive, computed, toRefs } from "vue"; + +/** + * @description table 页面操作方法封装 + * @param {Function} api 获取表格数据 api 方法 (必传) + * @param {Object} initParam 获取数据初始化参数 (非必传,默认为{}) + * @param {Boolean} isPageable 是否有分页 (非必传,默认为true) + * @param {Function} dataCallBack 对后台返回的数据进行处理的方法 (非必传) + * */ +export const useTable = ( + api?: (params: any) => Promise, + initParam: object = {}, + isPageable: boolean = true, + dataCallBack?: (data: any) => any, + requestError?: (error: any) => void +) => { + const state = reactive({ + // 表格数据 + tableData: [], + // 分页数据 + pageable: { + // 当前页数 + pageNum: 1, + // 每页显示条数 + pageSize: 10, + // 总条数 + total: 0 + }, + // 查询参数(只包括查询) + searchParam: {}, + // 初始化默认的查询参数 + searchInitParam: {}, + // 总参数(包含分页和查询参数) + totalParam: {} + }); + + /** + * @description 分页查询参数(只包括分页和表格字段排序,其他排序方式可自行配置) + * */ + const pageParam = computed({ + get: () => { + return { + pageNum: state.pageable.pageNum, + pageSize: state.pageable.pageSize + }; + }, + set: (newVal: any) => { + console.log("我是分页更新之后的值", newVal); + } + }); + + /** + * @description 获取表格数据 + * @return void + * */ + const getTableList = async () => { + if (!api) return; + try { + // 先把初始化参数和分页参数放到总参数里面 + Object.assign(state.totalParam, initParam, isPageable ? pageParam.value : {}); + let { data } = await api({ ...state.searchInitParam, ...state.totalParam }); + dataCallBack && (data = dataCallBack(data)); + state.tableData = isPageable ? data.list : data; + // 解构后台返回的分页数据 (如果有分页更新分页信息) + if (isPageable) { + const { pageNum, pageSize, total } = data; + updatePageable({ pageNum, pageSize, total }); + } + } catch (error) { + requestError && requestError(error); + } + }; + + /** + * @description 更新查询参数 + * @return void + * */ + const updatedTotalParam = () => { + state.totalParam = {}; + // 处理查询参数,可以给查询参数加自定义前缀操作 + let nowSearchParam: Table.StateProps["searchParam"] = {}; + // 防止手动清空输入框携带参数(这里可以自定义查询参数前缀) + for (let key in state.searchParam) { + // 某些情况下参数为 false/0 也应该携带参数 + if (state.searchParam[key] || state.searchParam[key] === false || state.searchParam[key] === 0) { + nowSearchParam[key] = state.searchParam[key]; + } + } + Object.assign(state.totalParam, nowSearchParam, isPageable ? pageParam.value : {}); + }; + + /** + * @description 更新分页信息 + * @param {Object} pageable 后台返回的分页数据 + * @return void + * */ + const updatePageable = (pageable: Table.Pageable) => { + Object.assign(state.pageable, pageable); + }; + + /** + * @description 表格数据查询 + * @return void + * */ + const search = () => { + state.pageable.pageNum = 1; + updatedTotalParam(); + getTableList(); + }; + + /** + * @description 表格数据重置 + * @return void + * */ + const reset = () => { + state.pageable.pageNum = 1; + // 重置搜索表单的时,如果有默认搜索参数,则重置默认的搜索参数 + state.searchParam = { ...state.searchInitParam }; + updatedTotalParam(); + getTableList(); + }; + + /** + * @description 每页条数改变 + * @param {Number} val 当前条数 + * @return void + * */ + const handleSizeChange = (val: number) => { + state.pageable.pageNum = 1; + state.pageable.pageSize = val; + getTableList(); + }; + + /** + * @description 当前页改变 + * @param {Number} val 当前页 + * @return void + * */ + const handleCurrentChange = (val: number) => { + state.pageable.pageNum = val; + getTableList(); + }; + + return { + ...toRefs(state), + getTableList, + search, + reset, + handleSizeChange, + handleCurrentChange, + updatedTotalParam + }; +}; diff --git a/frontend/src/hooks/useTheme.ts b/frontend/src/hooks/useTheme.ts new file mode 100644 index 0000000..5d6c2ea --- /dev/null +++ b/frontend/src/hooks/useTheme.ts @@ -0,0 +1,111 @@ +import { storeToRefs } from "pinia"; +import { Theme } from "./interface"; +import { ElMessage } from "element-plus"; +import { DEFAULT_PRIMARY } from "@/config"; +import { useGlobalStore } from "@/stores/modules/global"; +import { getLightColor, getDarkColor } from "@/utils/color"; +import { menuTheme } from "@/styles/theme/menu"; +import { asideTheme } from "@/styles/theme/aside"; +import { headerTheme } from "@/styles/theme/header"; + +/** + * @description 全局主题 hooks + * */ +export const useTheme = () => { + const globalStore = useGlobalStore(); + const { primary, isDark, isGrey, isWeak, layout, asideInverted, headerInverted } = storeToRefs(globalStore); + + // 切换暗黑模式 ==> 同时修改主题颜色、侧边栏、头部颜色 + const switchDark = () => { + const html = document.documentElement as HTMLElement; + if (isDark.value) html.setAttribute("class", "dark"); + else html.setAttribute("class", ""); + changePrimary(primary.value); + setAsideTheme(); + setHeaderTheme(); + }; + + // 修改主题颜色 + const changePrimary = (val: string | null) => { + if (!val) { + val = DEFAULT_PRIMARY; + ElMessage({ type: "success", message: `主题颜色已重置为 ${DEFAULT_PRIMARY}` }); + } + // 计算主题颜色变化 + document.documentElement.style.setProperty("--el-color-primary", val); + document.documentElement.style.setProperty( + "--el-color-primary-dark-2", + isDark.value ? `${getLightColor(val, 0.2)}` : `${getDarkColor(val, 0.3)}` + ); + for (let i = 1; i <= 9; i++) { + const primaryColor = isDark.value ? `${getDarkColor(val, i / 10)}` : `${getLightColor(val, i / 10)}`; + document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, primaryColor); + } + globalStore.setGlobalState("primary", val); + }; + + // 灰色和弱色切换 + const changeGreyOrWeak = (type: Theme.GreyOrWeakType, value: boolean) => { + const body = document.body as HTMLElement; + if (!value) return body.removeAttribute("style"); + const styles: Record = { + grey: "filter: grayscale(1)", + weak: "filter: invert(80%)" + }; + body.setAttribute("style", styles[type]); + const propName = type === "grey" ? "isWeak" : "isGrey"; + globalStore.setGlobalState(propName, false); + }; + + // 设置菜单样式 + const setMenuTheme = () => { + let type: Theme.ThemeType = "light"; + if (layout.value === "transverse" && headerInverted.value) type = "inverted"; + if (layout.value !== "transverse" && asideInverted.value) type = "inverted"; + if (isDark.value) type = "dark"; + const theme = menuTheme[type!]; + for (const [key, value] of Object.entries(theme)) { + document.documentElement.style.setProperty(key, value); + } + }; + + // 设置侧边栏样式 + const setAsideTheme = () => { + let type: Theme.ThemeType = "light"; + if (asideInverted.value) type = "inverted"; + if (isDark.value) type = "dark"; + const theme = asideTheme[type!]; + for (const [key, value] of Object.entries(theme)) { + document.documentElement.style.setProperty(key, value); + } + setMenuTheme(); + }; + + // 设置头部样式 + const setHeaderTheme = () => { + let type: Theme.ThemeType = "light"; + if (headerInverted.value) type = "inverted"; + if (isDark.value) type = "dark"; + const theme = headerTheme[type!]; + for (const [key, value] of Object.entries(theme)) { + document.documentElement.style.setProperty(key, value); + } + setMenuTheme(); + }; + + // init theme + const initTheme = () => { + switchDark(); + if (isGrey.value) changeGreyOrWeak("grey", true); + if (isWeak.value) changeGreyOrWeak("weak", true); + }; + + return { + initTheme, + switchDark, + changePrimary, + changeGreyOrWeak, + setAsideTheme, + setHeaderTheme + }; +}; diff --git a/frontend/src/hooks/useTime.ts b/frontend/src/hooks/useTime.ts new file mode 100644 index 0000000..e5a6674 --- /dev/null +++ b/frontend/src/hooks/useTime.ts @@ -0,0 +1,38 @@ +import { ref } from "vue"; + +/** + * @description 获取本地时间 + */ +export const useTime = () => { + const year = ref(0); // 年份 + const month = ref(0); // 月份 + const week = ref(""); // 星期几 + const day = ref(0); // 天数 + const hour = ref(0); // 小时 + const minute = ref(0); // 分钟 + const second = ref(0); // 秒 + const nowTime = ref(""); // 当前时间 + + // 更新时间 + const updateTime = () => { + const date = new Date(); + year.value = date.getFullYear(); + month.value = date.getMonth() + 1; + week.value = "日一二三四五六".charAt(date.getDay()); + day.value = date.getDate(); + hour.value = + (date.getHours() + "")?.padStart(2, "0") || + new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getHours()); + minute.value = + (date.getMinutes() + "")?.padStart(2, "0") || + new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getMinutes()); + second.value = + (date.getSeconds() + "")?.padStart(2, "0") || + new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getSeconds()); + nowTime.value = `${year.value}年${month.value}月${day.value} ${hour.value}:${minute.value}:${second.value}`; + }; + + updateTime(); + + return { year, month, day, hour, minute, second, week, nowTime }; +}; diff --git a/frontend/src/languages/index.ts b/frontend/src/languages/index.ts new file mode 100644 index 0000000..ef7d49c --- /dev/null +++ b/frontend/src/languages/index.ts @@ -0,0 +1,18 @@ +import { createI18n } from "vue-i18n"; +import { getBrowserLang } from "@/utils"; + +import zh from "./modules/zh"; +import en from "./modules/en"; + +const i18n = createI18n({ + // Use Composition API, Set to false + allowComposition: true, + legacy: false, + locale: getBrowserLang(), + messages: { + zh, + en + } +}); + +export default i18n; diff --git a/frontend/src/languages/modules/en.ts b/frontend/src/languages/modules/en.ts new file mode 100644 index 0000000..21f7e8c --- /dev/null +++ b/frontend/src/languages/modules/en.ts @@ -0,0 +1,29 @@ +export default { + home: { + welcome: "Welcome" + }, + tabs: { + refresh: "Refresh", + maximize: "Maximize", + closeCurrent: "Close current", + closeLeft: "Close Left", + closeRight: "Close Right", + closeOther: "Close other", + closeAll: "Close All" + }, + header: { + componentSize: "Component size", + language: "Language", + theme: "theme", + layoutConfig: "Layout config", + primary: "primary", + darkMode: "Dark Mode", + greyMode: "Grey mode", + weakMode: "Weak mode", + fullScreen: "Full Screen", + exitFullScreen: "Exit Full Screen", + personalData: "Personal Data", + changePassword: "Change Password", + logout: "Logout" + } +}; diff --git a/frontend/src/languages/modules/zh.ts b/frontend/src/languages/modules/zh.ts new file mode 100644 index 0000000..6afb416 --- /dev/null +++ b/frontend/src/languages/modules/zh.ts @@ -0,0 +1,29 @@ +export default { + home: { + welcome: "欢迎使用" + }, + tabs: { + refresh: "刷新", + maximize: "最大化", + closeCurrent: "关闭当前", + closeLeft: "关闭左侧", + closeRight: "关闭右侧", + closeOther: "关闭其它", + closeAll: "关闭所有" + }, + header: { + componentSize: "组件大小", + language: "国际化", + theme: "全局主题", + layoutConfig: "布局设置", + primary: "primary", + darkMode: "暗黑模式", + greyMode: "灰色模式", + weakMode: "色弱模式", + fullScreen: "全屏", + exitFullScreen: "退出全屏", + personalData: "个人信息", + changePassword: "修改密码", + logout: "退出登录" + } +}; diff --git a/frontend/src/layouts/CnHeader/index.vue b/frontend/src/layouts/CnHeader/index.vue deleted file mode 100644 index 21a79c8..0000000 --- a/frontend/src/layouts/CnHeader/index.vue +++ /dev/null @@ -1,92 +0,0 @@ - - - - diff --git a/frontend/src/layouts/LayoutClassic/index.scss b/frontend/src/layouts/LayoutClassic/index.scss new file mode 100644 index 0000000..2207999 --- /dev/null +++ b/frontend/src/layouts/LayoutClassic/index.scss @@ -0,0 +1,60 @@ +.el-container { + width: 100%; + height: 100%; + :deep(.el-header) { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: space-between; + height: 55px; + padding: 0 15px 0 0; + background-color: var(--el-header-bg-color); + border-bottom: 1px solid var(--el-header-border-color); + .header-lf { + display: flex; + align-items: center; + overflow: hidden; + white-space: nowrap; + .logo { + flex-shrink: 0; + width: 210px; + margin-right: 16px; + .logo-img { + width: 28px; + object-fit: contain; + margin-right: 6px; + } + .logo-text { + font-size: 21.5px; + font-weight: bold; + color: var(--el-header-logo-text-color); + white-space: nowrap; + } + } + } + } + .classic-content { + display: flex; + height: calc(100% - 55px); + :deep(.el-aside) { + width: auto; + background-color: var(--el-menu-bg-color); + border-right: 1px solid var(--el-aside-border-color); + .aside-box { + display: flex; + flex-direction: column; + height: 100%; + transition: width 0.3s ease; + .el-menu { + width: 100%; + overflow-x: hidden; + border-right: none; + } + } + } + .classic-main { + display: flex; + flex-direction: column; + } + } +} diff --git a/frontend/src/layouts/LayoutClassic/index.vue b/frontend/src/layouts/LayoutClassic/index.vue new file mode 100644 index 0000000..9bc6b56 --- /dev/null +++ b/frontend/src/layouts/LayoutClassic/index.vue @@ -0,0 +1,62 @@ + + + + + + diff --git a/frontend/src/layouts/LayoutColumns/index.scss b/frontend/src/layouts/LayoutColumns/index.scss new file mode 100644 index 0000000..9175538 --- /dev/null +++ b/frontend/src/layouts/LayoutColumns/index.scss @@ -0,0 +1,95 @@ +.el-container { + width: 100%; + height: 100%; + .aside-split { + display: flex; + flex-direction: column; + flex-shrink: 0; + width: 70px; + height: 100%; + background-color: var(--el-menu-bg-color); + border-right: 1px solid var(--el-aside-border-color); + .logo { + box-sizing: border-box; + height: 55px; + .logo-img { + width: 32px; + object-fit: contain; + } + } + .el-scrollbar { + height: calc(100% - 55px); + .split-list { + flex: 1; + .split-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 70px; + cursor: pointer; + transition: all 0.3s ease; + .el-icon { + font-size: 20px; + } + .title { + margin-top: 6px; + font-size: 12px; + } + .el-icon, + .title { + color: var(--el-menu-text-color); + } + } + .split-active { + background-color: var(--el-color-primary) !important; + .el-icon, + .title { + color: #ffffff !important; + } + } + } + } + } + .not-aside { + width: 0 !important; + border-right: none !important; + } + .el-aside { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; + background-color: var(--el-menu-bg-color); + border-right: 1px solid var(--el-aside-border-color); + transition: width 0.3s ease; + .el-scrollbar { + height: calc(100% - 55px); + .el-menu { + width: 100%; + overflow-x: hidden; + border-right: none; + } + } + .logo { + box-sizing: border-box; + height: 55px; + .logo-text { + font-size: 24px; + font-weight: bold; + color: var(--el-aside-logo-text-color); + white-space: nowrap; + } + } + } + .el-header { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: space-between; + height: 55px; + padding: 0 15px; + background-color: var(--el-header-bg-color); + border-bottom: 1px solid var(--el-border-color-light); + } +} diff --git a/frontend/src/layouts/LayoutColumns/index.vue b/frontend/src/layouts/LayoutColumns/index.vue new file mode 100644 index 0000000..f6d270d --- /dev/null +++ b/frontend/src/layouts/LayoutColumns/index.vue @@ -0,0 +1,103 @@ + + + + + + diff --git a/frontend/src/layouts/LayoutTransverse/index.scss b/frontend/src/layouts/LayoutTransverse/index.scss new file mode 100644 index 0000000..0b3a9ee --- /dev/null +++ b/frontend/src/layouts/LayoutTransverse/index.scss @@ -0,0 +1,60 @@ +.el-container { + width: 100%; + height: 100%; + :deep(.el-header) { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: space-between; + height: 55px; + padding: 0 15px 0 0; + background-color: var(--el-header-bg-color); + border-bottom: 1px solid var(--el-header-border-color); + .logo { + width: 210px; + margin-right: 30px; + .logo-img { + width: 28px; + object-fit: contain; + margin-right: 6px; + } + .logo-text { + font-size: 21.5px; + font-weight: bold; + color: var(--el-header-logo-text-color); + white-space: nowrap; + } + } + .el-menu { + flex: 1; + height: 100%; + overflow: hidden; + border-bottom: none; + .el-sub-menu__hide-arrow { + width: 65px; + height: 55px; + } + .el-menu-item.is-active { + color: #ffffff !important; + } + .is-active { + background-color: var(--el-color-primary) !important; + border-bottom-color: var(--el-color-primary) !important; + &::before { + width: 0; + } + .el-sub-menu__title { + color: #ffffff !important; + background-color: var(--el-color-primary) !important; + border-bottom-color: var(--el-color-primary) !important; + } + } + } + } + + @media screen and (width <= 730px) { + .logo { + display: none !important; + } + } +} diff --git a/frontend/src/layouts/LayoutTransverse/index.vue b/frontend/src/layouts/LayoutTransverse/index.vue new file mode 100644 index 0000000..c379d67 --- /dev/null +++ b/frontend/src/layouts/LayoutTransverse/index.vue @@ -0,0 +1,61 @@ + + + + + + diff --git a/frontend/src/layouts/LayoutVertical/index.scss b/frontend/src/layouts/LayoutVertical/index.scss new file mode 100644 index 0000000..33a9b42 --- /dev/null +++ b/frontend/src/layouts/LayoutVertical/index.scss @@ -0,0 +1,48 @@ +.el-container { + width: 100%; + height: 100%; + :deep(.el-aside) { + width: auto; + background-color: var(--el-menu-bg-color); + border-right: 1px solid var(--el-aside-border-color); + .aside-box { + display: flex; + flex-direction: column; + height: 100%; + transition: width 0.3s ease; + .el-scrollbar { + height: calc(100% - 55px); + .el-menu { + width: 100%; + overflow-x: hidden; + border-right: none; + } + } + .logo { + box-sizing: border-box; + height: 55px; + .logo-img { + width: 28px; + object-fit: contain; + margin-right: 6px; + } + .logo-text { + font-size: 21.5px; + font-weight: bold; + color: var(--el-aside-logo-text-color); + white-space: nowrap; + } + } + } + } + .el-header { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: space-between; + height: 55px; + padding: 0 15px; + background-color: var(--el-header-bg-color); + border-bottom: 1px solid var(--el-header-border-color); + } +} diff --git a/frontend/src/layouts/LayoutVertical/index.vue b/frontend/src/layouts/LayoutVertical/index.vue new file mode 100644 index 0000000..8303ac1 --- /dev/null +++ b/frontend/src/layouts/LayoutVertical/index.vue @@ -0,0 +1,56 @@ + + + + + + diff --git a/frontend/src/layouts/components/Footer/index.scss b/frontend/src/layouts/components/Footer/index.scss new file mode 100644 index 0000000..912823c --- /dev/null +++ b/frontend/src/layouts/components/Footer/index.scss @@ -0,0 +1,11 @@ +.footer { + height: 30px; + background-color: var(--el-bg-color); + border-top: 1px solid var(--el-border-color-light); + a { + font-size: 14px; + color: var(--el-text-color-secondary); + text-decoration: none; + letter-spacing: 0.5px; + } +} diff --git a/frontend/src/layouts/components/Footer/index.vue b/frontend/src/layouts/components/Footer/index.vue new file mode 100644 index 0000000..e13a7d9 --- /dev/null +++ b/frontend/src/layouts/components/Footer/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/frontend/src/layouts/components/Header/ToolBarLeft.vue b/frontend/src/layouts/components/Header/ToolBarLeft.vue new file mode 100644 index 0000000..17af7ca --- /dev/null +++ b/frontend/src/layouts/components/Header/ToolBarLeft.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/frontend/src/layouts/components/Header/ToolBarRight.vue b/frontend/src/layouts/components/Header/ToolBarRight.vue new file mode 100644 index 0000000..8c43bd8 --- /dev/null +++ b/frontend/src/layouts/components/Header/ToolBarRight.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/frontend/src/layouts/components/Header/components/AssemblySize.vue b/frontend/src/layouts/components/Header/components/AssemblySize.vue new file mode 100644 index 0000000..1708fa4 --- /dev/null +++ b/frontend/src/layouts/components/Header/components/AssemblySize.vue @@ -0,0 +1,37 @@ + + + diff --git a/frontend/src/layouts/components/Header/components/Avatar.vue b/frontend/src/layouts/components/Header/components/Avatar.vue new file mode 100644 index 0000000..181f31f --- /dev/null +++ b/frontend/src/layouts/components/Header/components/Avatar.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/frontend/src/layouts/components/Header/components/Breadcrumb.vue b/frontend/src/layouts/components/Header/components/Breadcrumb.vue new file mode 100644 index 0000000..698682c --- /dev/null +++ b/frontend/src/layouts/components/Header/components/Breadcrumb.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/frontend/src/layouts/components/Header/components/CollapseIcon.vue b/frontend/src/layouts/components/Header/components/CollapseIcon.vue new file mode 100644 index 0000000..81cde80 --- /dev/null +++ b/frontend/src/layouts/components/Header/components/CollapseIcon.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/frontend/src/layouts/components/Header/components/Fullscreen.vue b/frontend/src/layouts/components/Header/components/Fullscreen.vue new file mode 100644 index 0000000..493e522 --- /dev/null +++ b/frontend/src/layouts/components/Header/components/Fullscreen.vue @@ -0,0 +1,25 @@ + + + diff --git a/frontend/src/layouts/components/Header/components/InfoDialog.vue b/frontend/src/layouts/components/Header/components/InfoDialog.vue new file mode 100644 index 0000000..e2ad84b --- /dev/null +++ b/frontend/src/layouts/components/Header/components/InfoDialog.vue @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/layouts/components/Header/components/Language.vue b/frontend/src/layouts/components/Header/components/Language.vue new file mode 100644 index 0000000..b99fb98 --- /dev/null +++ b/frontend/src/layouts/components/Header/components/Language.vue @@ -0,0 +1,38 @@ + + + diff --git a/frontend/src/layouts/components/Header/components/Message.vue b/frontend/src/layouts/components/Header/components/Message.vue new file mode 100644 index 0000000..10b8efb --- /dev/null +++ b/frontend/src/layouts/components/Header/components/Message.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/frontend/src/layouts/components/Header/components/PasswordDialog.vue b/frontend/src/layouts/components/Header/components/PasswordDialog.vue new file mode 100644 index 0000000..bf30bb3 --- /dev/null +++ b/frontend/src/layouts/components/Header/components/PasswordDialog.vue @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/layouts/components/Header/components/SearchMenu.vue b/frontend/src/layouts/components/Header/components/SearchMenu.vue new file mode 100644 index 0000000..c8e58e4 --- /dev/null +++ b/frontend/src/layouts/components/Header/components/SearchMenu.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/frontend/src/layouts/components/Header/components/ThemeSetting.vue b/frontend/src/layouts/components/Header/components/ThemeSetting.vue new file mode 100644 index 0000000..b2de486 --- /dev/null +++ b/frontend/src/layouts/components/Header/components/ThemeSetting.vue @@ -0,0 +1,12 @@ + + + diff --git a/frontend/src/layouts/components/Main/components/Maximize.vue b/frontend/src/layouts/components/Main/components/Maximize.vue new file mode 100644 index 0000000..ce3d446 --- /dev/null +++ b/frontend/src/layouts/components/Main/components/Maximize.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/frontend/src/layouts/components/Main/index.scss b/frontend/src/layouts/components/Main/index.scss new file mode 100644 index 0000000..fd4f175 --- /dev/null +++ b/frontend/src/layouts/components/Main/index.scss @@ -0,0 +1,10 @@ +.el-main { + box-sizing: border-box; + padding: 10px 12px; + overflow-x: hidden; + background-color: var(--el-bg-color-page); +} +.el-footer { + height: auto; + padding: 0; +} diff --git a/frontend/src/layouts/components/Main/index.vue b/frontend/src/layouts/components/Main/index.vue new file mode 100644 index 0000000..8e0c366 --- /dev/null +++ b/frontend/src/layouts/components/Main/index.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/frontend/src/layouts/components/Menu/SubMenu.vue b/frontend/src/layouts/components/Menu/SubMenu.vue new file mode 100644 index 0000000..b277fb0 --- /dev/null +++ b/frontend/src/layouts/components/Menu/SubMenu.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/frontend/src/layouts/components/Tabs/components/MoreButton.vue b/frontend/src/layouts/components/Tabs/components/MoreButton.vue new file mode 100644 index 0000000..a9c8db1 --- /dev/null +++ b/frontend/src/layouts/components/Tabs/components/MoreButton.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/frontend/src/layouts/components/Tabs/index.scss b/frontend/src/layouts/components/Tabs/index.scss new file mode 100644 index 0000000..8f52d74 --- /dev/null +++ b/frontend/src/layouts/components/Tabs/index.scss @@ -0,0 +1,69 @@ +.tabs-box { + background-color: var(--el-bg-color); + .tabs-menu { + position: relative; + width: 100%; + .el-dropdown { + position: absolute; + top: 0; + right: 0; + bottom: 0; + .more-button { + display: flex; + align-items: center; + justify-content: center; + width: 43px; + cursor: pointer; + border-left: 1px solid var(--el-border-color-light); + transition: all 0.3s; + &:hover { + background-color: var(--el-color-info-light-9); + } + .iconfont { + font-size: 12.5px; + } + } + } + :deep(.el-tabs) { + .el-tabs__header { + box-sizing: border-box; + height: 40px; + padding: 0 10px; + margin: 0; + .el-tabs__nav-wrap { + position: absolute; + width: calc(100% - 70px); + .el-tabs__nav { + display: flex; + border: none; + .el-tabs__item { + display: flex; + align-items: center; + justify-content: center; + color: #afafaf; + border: none; + .tabs-icon { + margin: 1.5px 4px 0 0; + font-size: 15px; + } + .is-icon-close { + margin-top: 1px; + } + &.is-active { + color: var(--el-color-primary); + &::before { + position: absolute; + bottom: 0; + width: 100%; + height: 0; + content: ""; + border-bottom: 2px solid var(--el-color-primary) !important; + } + } + } + } + } + } + } + } +} diff --git a/frontend/src/layouts/components/Tabs/index.vue b/frontend/src/layouts/components/Tabs/index.vue new file mode 100644 index 0000000..b08d9e3 --- /dev/null +++ b/frontend/src/layouts/components/Tabs/index.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/frontend/src/layouts/components/ThemeDrawer/index.scss b/frontend/src/layouts/components/ThemeDrawer/index.scss new file mode 100644 index 0000000..c82209c --- /dev/null +++ b/frontend/src/layouts/components/ThemeDrawer/index.scss @@ -0,0 +1,137 @@ +.divider { + margin-top: 15px; + .el-icon { + position: relative; + top: 2px; + right: 5px; + font-size: 15px; + } +} +.theme-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 5px; + margin: 14px 0; + span { + display: flex; + align-items: center; + font-size: 14px; + .el-icon { + margin-left: 3px; + font-size: 15px; + color: var(--el-text-color-regular); + cursor: pointer; + } + } +} +.layout-box { + position: relative; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + padding: 15px 7px 0; + .layout-item { + position: relative; + box-sizing: border-box; + width: 100px; + height: 70px; + padding: 6px; + cursor: pointer; + border-radius: 5px; + box-shadow: 0 0 5px 1px var(--el-border-color-dark); + transition: all 0.2s; + .layout-dark { + background-color: var(--el-color-primary); + border-radius: 3px; + } + .layout-light { + background-color: var(--el-color-primary-light-5); + border-radius: 3px; + } + .layout-content { + background-color: var(--el-color-primary-light-8); + border: 1px dashed var(--el-color-primary); + border-radius: 3px; + } + .el-icon { + position: absolute; + right: 10px; + bottom: 10px; + color: var(--el-color-primary); + transition: all 0.2s; + } + &:hover { + box-shadow: 0 0 5px 1px var(--el-text-color-secondary); + } + } + .is-active { + box-shadow: 0 0 0 2px var(--el-color-primary) !important; + } + .layout-vertical { + display: flex; + justify-content: space-between; + margin-bottom: 20px; + .layout-dark { + width: 20%; + } + .layout-container { + display: flex; + flex-direction: column; + justify-content: space-between; + width: 72%; + .layout-light { + height: 20%; + } + .layout-content { + height: 67%; + } + } + } + .layout-classic { + display: flex; + flex-direction: column; + justify-content: space-between; + margin-bottom: 20px; + .layout-dark { + height: 22%; + } + .layout-container { + display: flex; + justify-content: space-between; + height: 70%; + .layout-light { + width: 20%; + } + .layout-content { + width: 70%; + } + } + } + .layout-transverse { + display: flex; + flex-direction: column; + justify-content: space-between; + margin-bottom: 15px; + .layout-dark { + height: 20%; + } + .layout-content { + height: 67%; + } + } + .layout-columns { + display: flex; + justify-content: space-between; + margin-bottom: 15px; + .layout-dark { + width: 14%; + } + .layout-light { + width: 17%; + } + .layout-content { + width: 55%; + } + } +} diff --git a/frontend/src/layouts/components/ThemeDrawer/index.vue b/frontend/src/layouts/components/ThemeDrawer/index.vue new file mode 100644 index 0000000..891ca60 --- /dev/null +++ b/frontend/src/layouts/components/ThemeDrawer/index.vue @@ -0,0 +1,186 @@ + + + + + diff --git a/frontend/src/layouts/index.vue b/frontend/src/layouts/index.vue index bff62b2..de85789 100644 --- a/frontend/src/layouts/index.vue +++ b/frontend/src/layouts/index.vue @@ -1,34 +1,32 @@ + - - diff --git a/frontend/src/layouts/indexAsync.vue b/frontend/src/layouts/indexAsync.vue new file mode 100644 index 0000000..2adb4cd --- /dev/null +++ b/frontend/src/layouts/indexAsync.vue @@ -0,0 +1,36 @@ + + + + + + diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 4faec90..c317fdb 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,42 +1,45 @@ -import { createApp } from 'vue' -import App from './App.vue' +import { createApp } from "vue"; +import App from "./App.vue"; +// reset style sheet +import "@/styles/reset.scss"; +// CSS common style sheet +import "@/styles/common.scss"; +// iconfont css +import "@/assets/iconfont/iconfont.scss"; +// font css +import "@/assets/fonts/font.scss"; +// element css +import "element-plus/dist/index.css"; +// element dark css +import "element-plus/theme-chalk/dark/css-vars.css"; +// custom element dark css +import "@/styles/element-dark.scss"; +// custom element css +import "@/styles/element.scss"; +// svg icons +import "virtual:svg-icons-register"; +// element plus +import ElementPlus from "element-plus"; +// element icons +import * as Icons from "@element-plus/icons-vue"; +// custom directives +import directives from "@/directives/index"; +// vue Router +import router from "@/routers"; +// vue i18n +import I18n from "@/languages/index"; +// pinia store +import pinia from "@/stores"; +// errorHandler +import errorHandler from "@/utils/errorHandler"; -// element-plus 全局引入图标 -import * as ElementPlusIconsVue from '@element-plus/icons-vue' +const app = createApp(App); -// 使用pinia -import pinia from '@/stores' -// 导入路由 -import Router from './router/index' -// 引入项目主CSS -import '@/assets/styles/tailMain.css' +app.config.errorHandler = errorHandler; -// 导入全局注册的组件 -import 'virtual:svg-icons-register' -import registerGlobComp from '@/components' - -//创建实例 -const app = createApp(App) -const setupAll = async () => { - //全局注册图标 - for(const [key, component] of Object.entries(ElementPlusIconsVue)){ - app.component(key, component) - } - - app - .use(Router) // 使用路由 - .use(pinia) // 使用pinia - .use(registerGlobComp) // 使用全局自定义组件 - .use(ElementPlusIconsVue) // 使用element-plus图标 - - - //待路由初始化完毕后,挂载app - await Router.isReady() -} - - -//挂载app -setupAll().then(() => { - app.mount('#app') -}) +// register the element Icons component +Object.keys(Icons).forEach(key => { + app.component(key, Icons[key as keyof typeof Icons]); +}); +app.use(ElementPlus).use(directives).use(router).use(I18n).use(pinia).mount("#app"); diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts deleted file mode 100644 index ba9b14a..0000000 --- a/frontend/src/router/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { createRouter, createWebHashHistory } from 'vue-router' -import routerMap from './routerMap' - -const Router = createRouter({ - history: createWebHashHistory(), - routes: routerMap, -}) - -Router.beforeEach((to, from, next) => { - next() -}) - -// 路由加载后 -Router.afterEach(() => {}) - -export default Router diff --git a/frontend/src/router/routerMap.ts b/frontend/src/router/routerMap.ts deleted file mode 100644 index 9a6786e..0000000 --- a/frontend/src/router/routerMap.ts +++ /dev/null @@ -1,27 +0,0 @@ -import Login from '@/views/Login.vue' - -/** - * 基础路由 - * @type { *[] } - */ -const constantRouterMap = [ - { - path: '/', - name: 'login', - component: Login, - }, - { - path: '/home', - name: 'home', - component: import("@/layouts/index.vue"), - children: [ - { - path: '/home', - name: 'home', - component: () => import("@/views/Home.vue"), - }, - ], - }, -] - -export default constantRouterMap diff --git a/frontend/src/routers/index.ts b/frontend/src/routers/index.ts new file mode 100644 index 0000000..99195e0 --- /dev/null +++ b/frontend/src/routers/index.ts @@ -0,0 +1,105 @@ +import { createRouter, createWebHashHistory, createWebHistory } from "vue-router"; +import { useUserStore } from "@/stores/modules/user"; +import { useAuthStore } from "@/stores/modules/auth"; +import { LOGIN_URL, ROUTER_WHITE_LIST } from "@/config"; +import { initDynamicRouter } from "@/routers/modules/dynamicRouter"; +import { staticRouter, errorRouter } from "@/routers/modules/staticRouter"; +import NProgress from "@/config/nprogress"; + +const mode = import.meta.env.VITE_ROUTER_MODE; + +const routerMode = { + hash: () => createWebHashHistory(), + history: () => createWebHistory() +}; + +/** + * @description 📚 路由参数配置简介 + * @param path ==> 路由菜单访问路径 + * @param name ==> 路由 name (对应页面组件 name, 可用作 KeepAlive 缓存标识 && 按钮权限筛选) + * @param redirect ==> 路由重定向地址 + * @param component ==> 视图文件路径 + * @param meta ==> 路由菜单元信息 + * @param meta.icon ==> 菜单和面包屑对应的图标 + * @param meta.title ==> 路由标题 (用作 document.title || 菜单的名称) + * @param meta.activeMenu ==> 当前路由为详情页时,需要高亮的菜单 + * @param meta.isLink ==> 路由外链时填写的访问地址 + * @param meta.isHide ==> 是否在菜单中隐藏 (通常列表详情页需要隐藏) + * @param meta.isFull ==> 菜单是否全屏 (示例:数据大屏页面) + * @param meta.isAffix ==> 菜单是否固定在标签页中 (首页通常是固定项) + * @param meta.isKeepAlive ==> 当前路由是否缓存 + * */ +const router = createRouter({ + history: routerMode[mode](), + routes: [...staticRouter, ...errorRouter], + strict: false, + scrollBehavior: () => ({ left: 0, top: 0 }) +}); + +/** + * @description 路由拦截 beforeEach + * */ +router.beforeEach(async (to, from, next) => { + const userStore = useUserStore(); + const authStore = useAuthStore(); + + // 1.NProgress 开始 + NProgress.start(); + + // 2.动态设置标题 + const title = import.meta.env.VITE_GLOB_APP_TITLE; + document.title = to.meta.title ? `${to.meta.title} - ${title}` : title; + + // 3.判断是访问登陆页,有 Token 就在当前页面,没有 Token 重置路由到登陆页 + if (to.path.toLocaleLowerCase() === LOGIN_URL) { + if (userStore.token) return next(from.fullPath); + resetRouter(); + return next(); + } + + // 4.判断访问页面是否在路由白名单地址(静态路由)中,如果存在直接放行 + if (ROUTER_WHITE_LIST.includes(to.path)) return next(); + + // 5.判断是否有 Token,没有重定向到 login 页面 + if (!userStore.token) return next({ path: LOGIN_URL, replace: true }); + + // 6.如果没有菜单列表,就重新请求菜单列表并添加动态路由 + if (!authStore.authMenuListGet.length) { + await initDynamicRouter(); + return next({ ...to, replace: true }); + } + + // 7.存储 routerName 做按钮权限筛选 + authStore.setRouteName(to.name as string); + + // 8.正常访问页面 + next(); +}); + +/** + * @description 重置路由 + * */ +export const resetRouter = () => { + const authStore = useAuthStore(); + authStore.flatMenuListGet.forEach(route => { + const { name } = route; + if (name && router.hasRoute(name)) router.removeRoute(name); + }); +}; + +/** + * @description 路由跳转错误 + * */ +router.onError(error => { + NProgress.done(); + console.warn("路由错误", error.message); +}); + +/** + * @description 路由跳转结束 + * */ +router.afterEach(() => { + NProgress.done(); +}); + +export default router; diff --git a/frontend/src/routers/modules/dynamicRouter.ts b/frontend/src/routers/modules/dynamicRouter.ts new file mode 100644 index 0000000..eaf517e --- /dev/null +++ b/frontend/src/routers/modules/dynamicRouter.ts @@ -0,0 +1,54 @@ +import router from "@/routers/index"; +import { LOGIN_URL } from "@/config"; +import { RouteRecordRaw } from "vue-router"; +import { ElNotification } from "element-plus"; +import { useUserStore } from "@/stores/modules/user"; +import { useAuthStore } from "@/stores/modules/auth"; + +// 引入 views 文件夹下所有 vue 文件 +const modules = import.meta.glob("@/views/**/*.vue"); + +/** + * @description 初始化动态路由 + */ +export const initDynamicRouter = async () => { + const userStore = useUserStore(); + const authStore = useAuthStore(); + + try { + // 1.获取菜单列表 && 按钮权限列表 + await authStore.getAuthMenuList(); + await authStore.getAuthButtonList(); + + // 2.判断当前用户有没有菜单权限 + if (!authStore.authMenuListGet.length) { + ElNotification({ + title: "无权限访问", + message: "当前账号无任何菜单权限,请联系系统管理员!", + type: "warning", + duration: 3000 + }); + userStore.setToken(""); + router.replace(LOGIN_URL); + return Promise.reject("No permission"); + } + + // 3.添加动态路由 + authStore.flatMenuListGet.forEach(item => { + item.children && delete item.children; + if (item.component && typeof item.component == "string") { + item.component = modules["/src/views" + item.component + ".vue"]; + } + if (item.meta.isFull) { + router.addRoute(item as unknown as RouteRecordRaw); + } else { + router.addRoute("layout", item as unknown as RouteRecordRaw); + } + }); + } catch (error) { + // 当按钮 || 菜单请求出错时,重定向到登陆页 + userStore.setToken(""); + router.replace(LOGIN_URL); + return Promise.reject(error); + } +}; diff --git a/frontend/src/routers/modules/staticRouter.ts b/frontend/src/routers/modules/staticRouter.ts new file mode 100644 index 0000000..b21eb82 --- /dev/null +++ b/frontend/src/routers/modules/staticRouter.ts @@ -0,0 +1,63 @@ +import { RouteRecordRaw } from "vue-router"; +import { HOME_URL, LOGIN_URL } from "@/config"; + +/** + * staticRouter (静态路由) + */ +export const staticRouter: RouteRecordRaw[] = [ + { + path: "/", + redirect: HOME_URL + }, + { + path: LOGIN_URL, + name: "login", + component: () => import("@/views/login/index.vue"), + meta: { + title: "登录" + } + }, + { + path: "/layout", + name: "layout", + component: () => import("@/layouts/index.vue"), + // component: () => import("@/layouts/indexAsync.vue"), + redirect: HOME_URL, + children: [] + } +]; + +/** + * errorRouter (错误页面路由) + */ +export const errorRouter = [ + { + path: "/403", + name: "403", + component: () => import("@/components/ErrorMessage/403.vue"), + meta: { + title: "403页面" + } + }, + { + path: "/404", + name: "404", + component: () => import("@/components/ErrorMessage/404.vue"), + meta: { + title: "404页面" + } + }, + { + path: "/500", + name: "500", + component: () => import("@/components/ErrorMessage/500.vue"), + meta: { + title: "500页面" + } + }, + // Resolve refresh page, route warnings + { + path: "/:pathMatch(.*)*", + component: () => import("@/components/ErrorMessage/404.vue") + } +]; diff --git a/frontend/src/stores/constant.ts b/frontend/src/stores/constant.ts new file mode 100644 index 0000000..247aade --- /dev/null +++ b/frontend/src/stores/constant.ts @@ -0,0 +1,16 @@ + +// pinia中auth store的key +export const AUTH_STORE_KEY = "cn-auth"; + +// pinia中global store的key +export const GLOBAL_STORE_KEY = "cn-global"; + +// pinia中keepAlive store的key +export const KEEP_ALIVE_STORE_KEY = "cn-keepAlive"; + +// pinia中tabs store的key +export const TABS_STORE_KEY = "cn-tabs"; + +// pinia中user store的key +export const USER_STORE_KEY = "cn-user"; + diff --git a/frontend/src/stores/helper/persist.ts b/frontend/src/stores/helper/persist.ts new file mode 100644 index 0000000..53a27a7 --- /dev/null +++ b/frontend/src/stores/helper/persist.ts @@ -0,0 +1,19 @@ +import { PersistedStateOptions } from "pinia-plugin-persistedstate"; + +/** + * @description pinia 持久化参数配置 + * @param {String} key 存储到持久化的 name + * @param {Array} paths 需要持久化的 state name + * @return persist + * */ +const piniaPersistConfig = (key: string, paths?: string[]) => { + const persist: PersistedStateOptions = { + key, + storage: localStorage, + // storage: sessionStorage, + paths + }; + return persist; +}; + +export default piniaPersistConfig; diff --git a/frontend/src/stores/index.ts b/frontend/src/stores/index.ts index 4abffe3..0c7dfda 100644 --- a/frontend/src/stores/index.ts +++ b/frontend/src/stores/index.ts @@ -1,3 +1,8 @@ -import { createPinia } from 'pinia' -const pinia = createPinia() -export default pinia +import { createPinia } from "pinia"; +import piniaPluginPersistedstate from "pinia-plugin-persistedstate"; + +// pinia persist +const pinia = createPinia(); +pinia.use(piniaPluginPersistedstate); + +export default pinia; diff --git a/frontend/src/stores/interface/index.ts b/frontend/src/stores/interface/index.ts index d6b8ef9..4b57088 100644 --- a/frontend/src/stores/interface/index.ts +++ b/frontend/src/stores/interface/index.ts @@ -18,7 +18,6 @@ export interface GlobalState { headerInverted: boolean; isCollapse: boolean; accordion: boolean; - watermark: boolean; breadcrumb: boolean; breadcrumbIcon: boolean; tabs: boolean; diff --git a/frontend/src/stores/modules/auth.ts b/frontend/src/stores/modules/auth.ts new file mode 100644 index 0000000..95f659d --- /dev/null +++ b/frontend/src/stores/modules/auth.ts @@ -0,0 +1,45 @@ +import {defineStore} from "pinia"; +import {AuthState} from "@/stores/interface"; +import {getAuthButtonListApi, getAuthMenuListApi} from "@/api/modules/login"; +import {getFlatMenuList, getShowMenuList, getAllBreadcrumbList} from "@/utils"; +import {AUTH_STORE_KEY} from "@/stores/constant"; + +export const useAuthStore = defineStore({ + id: AUTH_STORE_KEY, + state: (): AuthState => ({ + // 按钮权限列表 + authButtonList: {}, + // 菜单权限列表 + authMenuList: [], + // 当前页面的 router name,用来做按钮权限筛选 + routeName: "" + }), + getters: { + // 按钮权限列表 + authButtonListGet: state => state.authButtonList, + // 菜单权限列表 ==> 这里的菜单没有经过任何处理 + authMenuListGet: state => state.authMenuList, + // 菜单权限列表 ==> 左侧菜单栏渲染,需要剔除 isHide == true + showMenuListGet: state => getShowMenuList(state.authMenuList), + // 菜单权限列表 ==> 扁平化之后的一维数组菜单,主要用来添加动态路由 + flatMenuListGet: state => getFlatMenuList(state.authMenuList), + // 递归处理后的所有面包屑导航列表 + breadcrumbListGet: state => getAllBreadcrumbList(state.authMenuList) + }, + actions: { + // Get AuthButtonList + async getAuthButtonList() { + const {data} = await getAuthButtonListApi(); + this.authButtonList = data; + }, + // Get AuthMenuList + async getAuthMenuList() { + const {data} = await getAuthMenuListApi(); + this.authMenuList = data; + }, + // Set RouteName + async setRouteName(name: string) { + this.routeName = name; + } + } +}); diff --git a/frontend/src/stores/modules/global.ts b/frontend/src/stores/modules/global.ts new file mode 100644 index 0000000..7446281 --- /dev/null +++ b/frontend/src/stores/modules/global.ts @@ -0,0 +1,54 @@ +import { defineStore } from "pinia"; +import { GlobalState } from "@/stores/interface"; +import { DEFAULT_PRIMARY } from "@/config"; +import piniaPersistConfig from "@/stores/helper/persist"; +import {GLOBAL_STORE_KEY} from "@/stores/constant"; + +export const useGlobalStore = defineStore({ + id: GLOBAL_STORE_KEY, + // 修改默认值之后,需清除 localStorage 数据 + state: (): GlobalState => ({ + // 布局模式 (纵向:vertical | 经典:classic | 横向:transverse | 分栏:columns) + layout: "transverse", + // element 组件大小 + assemblySize: "default", + // 当前系统语言 + language: null, + // 当前页面是否全屏 + maximize: false, + // 主题颜色 + primary: DEFAULT_PRIMARY, + // 深色模式 + isDark: false, + // 灰色模式 + isGrey: false, + // 色弱模式 + isWeak: false, + // 侧边栏反转 + asideInverted: false, + // 头部反转 + headerInverted: false, + // 折叠菜单 + isCollapse: false, + // 菜单手风琴 + accordion: true, + // 面包屑导航 + breadcrumb: true, + // 面包屑导航图标 + breadcrumbIcon: true, + // 标签页 + tabs: true, + // 标签页图标 + tabsIcon: true, + // 页脚 + footer: false + }), + getters: {}, + actions: { + // Set GlobalState + setGlobalState(...args: ObjToKeyValArray) { + this.$patch({ [args[0]]: args[1] }); + } + }, + persist: piniaPersistConfig(GLOBAL_STORE_KEY) +}); diff --git a/frontend/src/stores/modules/keepAlive.ts b/frontend/src/stores/modules/keepAlive.ts new file mode 100644 index 0000000..52eb1e4 --- /dev/null +++ b/frontend/src/stores/modules/keepAlive.ts @@ -0,0 +1,24 @@ +import {defineStore} from "pinia"; +import {KeepAliveState} from "@/stores/interface"; +import {KEEP_ALIVE_STORE_KEY} from '@/stores/constant' + +export const useKeepAliveStore = defineStore({ + id: KEEP_ALIVE_STORE_KEY, + state: (): KeepAliveState => ({ + keepAliveName: [] + }), + actions: { + // Add KeepAliveName + async addKeepAliveName(name: string) { + !this.keepAliveName.includes(name) && this.keepAliveName.push(name); + }, + // Remove KeepAliveName + async removeKeepAliveName(name: string) { + this.keepAliveName = this.keepAliveName.filter(item => item !== name); + }, + // Set KeepAliveName + async setKeepAliveName(keepAliveName: string[] = []) { + this.keepAliveName = keepAliveName; + } + } +}); diff --git a/frontend/src/stores/modules/tabs.ts b/frontend/src/stores/modules/tabs.ts new file mode 100644 index 0000000..2b78d2e --- /dev/null +++ b/frontend/src/stores/modules/tabs.ts @@ -0,0 +1,75 @@ +import router from "@/routers"; +import { defineStore } from "pinia"; +import { getUrlWithParams } from "@/utils"; +import { useKeepAliveStore } from "./keepAlive"; +import { TabsState, TabsMenuProps } from "@/stores/interface"; +import piniaPersistConfig from "@/stores/helper/persist"; +import {TABS_STORE_KEY} from "@/stores/constant"; + +const keepAliveStore = useKeepAliveStore(); + +export const useTabsStore = defineStore({ + id: TABS_STORE_KEY, + state: (): TabsState => ({ + tabsMenuList: [] + }), + actions: { + // Add Tabs + async addTabs(tabItem: TabsMenuProps) { + if (this.tabsMenuList.every(item => item.path !== tabItem.path)) { + this.tabsMenuList.push(tabItem); + } + if (!keepAliveStore.keepAliveName.includes(tabItem.name) && tabItem.isKeepAlive) { + keepAliveStore.addKeepAliveName(tabItem.name); + } + }, + // Remove Tabs + async removeTabs(tabPath: string, isCurrent: boolean = true) { + if (isCurrent) { + this.tabsMenuList.forEach((item, index) => { + if (item.path !== tabPath) return; + const nextTab = this.tabsMenuList[index + 1] || this.tabsMenuList[index - 1]; + if (!nextTab) return; + router.push(nextTab.path); + }); + } + this.tabsMenuList = this.tabsMenuList.filter(item => item.path !== tabPath); + // remove keepalive + const tabItem = this.tabsMenuList.find(item => item.path === tabPath); + tabItem?.isKeepAlive && keepAliveStore.removeKeepAliveName(tabItem.name); + }, + // Close Tabs On Side + async closeTabsOnSide(path: string, type: "left" | "right") { + const currentIndex = this.tabsMenuList.findIndex(item => item.path === path); + if (currentIndex !== -1) { + const range = type === "left" ? [0, currentIndex] : [currentIndex + 1, this.tabsMenuList.length]; + this.tabsMenuList = this.tabsMenuList.filter((item, index) => { + return index < range[0] || index >= range[1] || !item.close; + }); + } + // set keepalive + const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive); + keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name)); + }, + // Close MultipleTab + async closeMultipleTab(tabsMenuValue?: string) { + this.tabsMenuList = this.tabsMenuList.filter(item => { + return item.path === tabsMenuValue || !item.close; + }); + // set keepalive + const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive); + keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name)); + }, + // Set Tabs + async setTabs(tabsMenuList: TabsMenuProps[]) { + this.tabsMenuList = tabsMenuList; + }, + // Set Tabs Title + async setTabsTitle(title: string) { + this.tabsMenuList.forEach(item => { + if (item.path == getUrlWithParams()) item.title = title; + }); + } + } + // persist: piniaPersistConfig(TABS_STORE_KEY) +}); diff --git a/frontend/src/stores/modules/user.ts b/frontend/src/stores/modules/user.ts new file mode 100644 index 0000000..fa94206 --- /dev/null +++ b/frontend/src/stores/modules/user.ts @@ -0,0 +1,24 @@ +import { defineStore } from "pinia"; +import { UserState } from "@/stores/interface"; +import piniaPersistConfig from "@/stores/helper/persist"; +import {USER_STORE_KEY} from "@/stores/constant"; + +export const useUserStore = defineStore({ + id: USER_STORE_KEY, + state: (): UserState => ({ + token: "", + userInfo: { name: "admin" } + }), + getters: {}, + actions: { + // Set Token + setToken(token: string) { + this.token = token; + }, + // Set setUserInfo + setUserInfo(userInfo: UserState["userInfo"]) { + this.userInfo = userInfo; + } + }, + persist: piniaPersistConfig(USER_STORE_KEY) +}); diff --git a/frontend/src/stores/modules/user/index.ts b/frontend/src/stores/modules/user/index.ts deleted file mode 100644 index d655c69..0000000 --- a/frontend/src/stores/modules/user/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { defineStore } from 'pinia' -import { USER_INFO } from '@/constants/storeKey' -import { TOKEN_PREFIX } from '@/constants/user' -import type { UserInfo } from '@/types/user' -import { getUserById } from '@/api/user' -import { createPersistedState } from 'pinia-plugin-persistedstate' - -export const useUserInfoStore = defineStore('userInfo', { - // 行为 - actions: { - dataFill(state: UserInfo) { - this.$state = { ...this.$state, ...state } - }, - async initUserInfo() { - this.dataFill(await getUserById()) - }, - removeToken() { - this.access_token = '' - this.refresh_token = '' - }, - setToken(token: string, type: 'auth' | 'refresh') { - const field = type == 'auth' ? 'token' : 'refresh_token' - this[field] = token - }, - getToken(type: 'auth' | 'refresh' = 'auth') { - if (type === 'auth') { - if (this.access_token) { - return `${TOKEN_PREFIX}${this.access_token}` - } else { - return '' - } - } else { - return this.refresh_token - } - }, - }, - // 状态 - state: (): UserInfo => { - return { - access_token: '', - refresh_token: '', - expires_in: 0, - scope: '', - nickname: '', - userType: 0, - deptIndex: '', - userIndex: '', - client_id: '', - headSculpture: '', - jti: '', - name: '', - deptId: '', - phone: '', - email: '', - limitIpStart: '', - limitIpEnd: '', - limitTime: '', - casualUser: 0, - type: 0, - smsNotice: 0, - emailNotice: 0, - role: [], - devCode: '', - id: '', - loginName: '', - state: 0, - registerTime: '', - loginTime: '', - deptName: '', - areaId: '', - areaName: '', - deptLevel: 0, - roleList: [], - roleCode: [], - } - }, - plugins: [ - createPersistedState({ - key: USER_INFO, - storage: window.localStorage - }), - ], -}) diff --git a/frontend/src/styles/common.scss b/frontend/src/styles/common.scss new file mode 100644 index 0000000..e94465c --- /dev/null +++ b/frontend/src/styles/common.scss @@ -0,0 +1,123 @@ +/* flex */ +.flx-center { + display: flex; + align-items: center; + justify-content: center; +} +.flx-justify-between { + display: flex; + align-items: center; + justify-content: space-between; +} +.flx-align-center { + display: flex; + align-items: center; +} + +/* clearfix */ +.clearfix::after { + display: block; + height: 0; + overflow: hidden; + clear: both; + content: ""; +} + +/* 文字单行省略号 */ +.sle { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* 文字多行省略号 */ +.mle { + display: -webkit-box; + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +/* 文字多了自动換行 */ +.break-word { + word-break: break-all; + word-wrap: break-word; +} + +/* fade-transform */ +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all 0.2s; +} +.fade-transform-enter-from { + opacity: 0; + transition: all 0.2s; + transform: translateX(-30px); +} +.fade-transform-leave-to { + opacity: 0; + transition: all 0.2s; + transform: translateX(30px); +} + +/* breadcrumb-transform */ +.breadcrumb-enter-active { + transition: all 0.2s; +} +.breadcrumb-enter-from, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(10px); +} + +/* scroll bar */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-thumb { + background-color: var(--el-border-color-darker); + border-radius: 20px; +} + +/* nprogress */ +#nprogress .bar { + background: var(--el-color-primary) !important; +} +#nprogress .spinner-icon { + border-top-color: var(--el-color-primary) !important; + border-left-color: var(--el-color-primary) !important; +} +#nprogress .peg { + box-shadow: + 0 0 10px var(--el-color-primary), + 0 0 5px var(--el-color-primary) !important; +} + +/* 外边距、内边距全局样式 */ +@for $i from 0 through 100 { + .mt#{$i} { + margin-top: #{$i}px !important; + } + .mr#{$i} { + margin-right: #{$i}px !important; + } + .mb#{$i} { + margin-bottom: #{$i}px !important; + } + .ml#{$i} { + margin-left: #{$i}px !important; + } + .pt#{$i} { + padding-top: #{$i}px !important; + } + .pr#{$i} { + padding-right: #{$i}px !important; + } + .pb#{$i} { + padding-bottom: #{$i}px !important; + } + .pl#{$i} { + padding-left: #{$i}px !important; + } +} diff --git a/frontend/src/styles/element-dark.scss b/frontend/src/styles/element-dark.scss new file mode 100644 index 0000000..633cea4 --- /dev/null +++ b/frontend/src/styles/element-dark.scss @@ -0,0 +1,28 @@ +/* 自定义 element 暗黑模式 */ +html.dark { + /* wangEditor */ + --w-e-toolbar-color: #eeeeee; + --w-e-toolbar-bg-color: #141414; + --w-e-textarea-bg-color: #141414; + --w-e-textarea-color: #eeeeee; + --w-e-toolbar-active-bg-color: #464646; + --w-e-toolbar-border-color: var(--el-border-color-darker); + .w-e-bar-item button:hover, + .w-e-menu-tooltip-v5::before { + color: #eeeeee; + } + + /* login */ + .login-container { + background-color: #191919 !important; + .login-box { + background-color: rgb(0 0 0 / 80%) !important; + .login-form { + box-shadow: rgb(255 255 255 / 12%) 0 2px 10px 2px !important; + .logo-text { + color: var(--el-text-color-primary) !important; + } + } + } + } +} diff --git a/frontend/src/styles/element.scss b/frontend/src/styles/element.scss new file mode 100644 index 0000000..a550bb2 --- /dev/null +++ b/frontend/src/styles/element.scss @@ -0,0 +1,259 @@ +/* 设置 notification、message 层级在 loading 之上 */ +.el-message, +.el-notification { + z-index: 2058 !important; +} + +/* el-alert */ +.el-alert { + border: 1px solid; +} + +/* 当前页面最大化 css */ +.main-maximize { + .aside-split, + .el-aside, + .el-header, + .el-footer, + .tabs-box { + display: none !important; + } +} + +/* mask image */ +.mask-image { + padding-right: 50px; + mask-image: linear-gradient(90deg, #000000 0%, #000000 calc(100% - 50px), transparent); +} + +/* custom card */ +.card { + box-sizing: border-box; + padding: 20px; + overflow-x: hidden; + background-color: var(--el-bg-color); + border: 1px solid var(--el-border-color-light); + border-radius: 6px; + box-shadow: 0 0 12px rgb(0 0 0 / 5%); +} + +/* ProTable 不需要 card 样式(在组件内使用 ProTable 会使用到) */ +.no-card { + .card { + padding: 0; + background-color: transparent; + border: none; + border-radius: 0; + box-shadow: none; + } + .table-search { + padding: 18px 0 0 !important; + margin-bottom: 0 !important; + } +} + +/* content-box (常用内容盒子) */ +.content-box { + display: flex; + flex-direction: column; + align-items: center; + height: 100%; + .text { + margin: 20px 0 30px; + font-size: 23px; + font-weight: bold; + color: var(--el-text-color-regular); + } + .el-descriptions { + width: 100%; + padding: 40px 0 0; + .el-descriptions__title { + font-size: 18px; + } + .el-descriptions__label { + width: 200px; + } + } +} + +/* main-box (树形表格 treeFilter 页面会使用,左右布局 flex) */ +.main-box { + display: flex; + width: 100%; + height: 100%; + .table-box { + // 这里减去的 230px 是 treeFilter 组件宽度 + width: calc(100% - 230px); + } +} + +/* proTable */ +.table-box, +.table-main { + display: flex; + flex: 1; + flex-direction: column; + width: 100%; + height: 100%; + + // table-search 表格搜索样式 + .table-search { + padding: 18px 18px 0; + margin-bottom: 10px; + .el-form { + .el-form-item__content > * { + width: 100%; + } + + // 去除时间选择器上下 padding + .el-range-editor.el-input__wrapper { + padding: 0 10px; + } + } + .operation { + display: flex; + align-items: center; + justify-content: flex-end; + margin-bottom: 18px; + } + } + + // 表格 header 样式 + .table-header { + .header-button-lf { + float: left; + } + .header-button-ri { + float: right; + } + .el-button { + margin-bottom: 15px; + } + } + + // el-table 表格样式 + .el-table { + flex: 1; + + // 修复 safari 浏览器表格错位 https://github.com/HalseySpicy/Geeker-Admin/issues/83 + table { + width: 100%; + } + .el-table__header th { + height: 45px; + font-size: 15px; + font-weight: bold; + color: var(--el-text-color-primary); + background: var(--el-fill-color-light); + } + .el-table__row { + height: 45px; + font-size: 14px; + .move { + cursor: move; + .el-icon { + cursor: move; + } + } + } + + // 设置 el-table 中 header 文字不换行,并省略 + .el-table__header .el-table__cell > .cell { + // white-space: nowrap; + white-space: wrap; + } + + // 解决表格数据为空时样式不居中问题(仅在element-plus中) + .el-table__empty-block { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + .table-empty { + line-height: 30px; + } + } + + // table 中 image 图片样式 + .table-image { + width: 50px; + height: 50px; + border-radius: 50%; + } + } + + // 表格 pagination 样式 + .el-pagination { + display: flex; + justify-content: flex-end; + margin-top: 20px; + } +} + +/* el-table 组件大小 */ +.el-table--small { + .el-table__header th { + height: 40px !important; + font-size: 14px !important; + } + .el-table__row { + height: 40px !important; + font-size: 13px !important; + } +} +.el-table--large { + .el-table__header th { + height: 50px !important; + font-size: 16px !important; + } + .el-table__row { + height: 50px !important; + font-size: 15px !important; + } +} + +/* el-drawer */ +.el-drawer { + .el-drawer__header { + padding: 16px 20px; + margin-bottom: 0; + border-bottom: 1px solid var(--el-border-color-lighter); + span { + font-size: 17px; + line-height: 17px; + color: var(--el-text-color-primary) !important; + } + } + .el-drawer__footer { + border-top: 1px solid var(--el-border-color-lighter); + } + + // select 样式 + .el-select { + width: 100%; + } + + // drawer-form 中存在两列 form-item 样式 + .drawer-multiColumn-form { + display: flex; + flex-wrap: wrap; + .el-form-item { + width: 47%; + &:nth-child(2n-1) { + margin-right: 5%; + } + } + } +} + +/* el-dialog */ +.el-dialog { + .el-dialog__header { + padding: 15px 20px; + margin: 0; + border-bottom: 1px solid var(--el-border-color-lighter); + .el-dialog__title { + font-size: 17px; + } + } +} diff --git a/frontend/src/styles/reset.scss b/frontend/src/styles/reset.scss new file mode 100644 index 0000000..3d09b81 --- /dev/null +++ b/frontend/src/styles/reset.scss @@ -0,0 +1,142 @@ +/* Reset style sheet */ + +/* 目前项目中使用富文本编辑器需要注释,如果你项目中没有使用富文本编辑器,可以取消注释 */ +// html, +// body, +// div, +// span, +// applet, +// object, +// iframe, +// h1, +// h2, +// h3, +// h4, +// h5, +// h6, +// p, +// blockquote, +// pre, +// a, +// abbr, +// acronym, +// address, +// big, +// cite, +// code, +// del, +// dfn, +// em, +// img, +// ins, +// kbd, +// q, +// s, +// samp, +// small, +// strike, +// strong, +// sub, +// sup, +// tt, +// var, +// b, +// u, +// i, +// center, +// dl, +// dt, +// dd, +// ol, +// ul, +// li, +// fieldset, +// form, +// label, +// legend, +// table, +// caption, +// tbody, +// tfoot, +// thead, +// tr, +// th, +// td, +// article, +// aside, +// canvas, +// details, +// embed, +// figure, +// figcaption, +// footer, +// header, +// hgroup, +// menu, +// nav, +// output, +// ruby, +// section, +// summary, +// time, +// mark, +// audio, +// video { +// padding: 0; +// margin: 0; +// font: inherit; +// font-size: 100%; +// vertical-align: baseline; +// border: 0; +// } + +// /* HTML5 display-role reset for older browsers */ +// article, +// aside, +// details, +// figcaption, +// figure, +// footer, +// header, +// hgroup, +// menu, +// nav, +// section { +// display: block; +// } +// body { +// padding: 0; +// margin: 0; +// } +// ol, +// ul { +// list-style: none; +// } +// blockquote, +// q { +// quotes: none; +// } +// blockquote::before, +// blockquote::after, +// q::before, +// q::after { +// content: ""; +// content: none; +// } +// table { +// border-spacing: 0; +// border-collapse: collapse; +// } +html, +body, +#app { + width: 100%; + height: 100%; + padding: 0; + margin: 0; +} + +/* 解决 h1 标签在 webkit 内核浏览器中文字大小失效问题 */ +:-webkit-any(article, aside, nav, section) h1 { + font-size: 2em; +} diff --git a/frontend/src/styles/theme/aside.ts b/frontend/src/styles/theme/aside.ts new file mode 100644 index 0000000..53cce73 --- /dev/null +++ b/frontend/src/styles/theme/aside.ts @@ -0,0 +1,16 @@ +import { Theme } from "@/hooks/interface"; + +export const asideTheme: Record = { + light: { + "--el-aside-logo-text-color": "#303133", + "--el-aside-border-color": "#e4e7ed" + }, + inverted: { + "--el-aside-logo-text-color": "#dadada", + "--el-aside-border-color": "#414243" + }, + dark: { + "--el-aside-logo-text-color": "#dadada", + "--el-aside-border-color": "#414243" + } +}; diff --git a/frontend/src/styles/theme/header.ts b/frontend/src/styles/theme/header.ts new file mode 100644 index 0000000..d2ba505 --- /dev/null +++ b/frontend/src/styles/theme/header.ts @@ -0,0 +1,25 @@ +import { Theme } from "@/hooks/interface"; + +export const headerTheme: Record = { + light: { + "--el-header-logo-text-color": "#303133", + "--el-header-bg-color": "#ffffff", + "--el-header-text-color": "#303133", + "--el-header-text-color-regular": "#606266", + "--el-header-border-color": "#e4e7ed" + }, + inverted: { + "--el-header-logo-text-color": "#dadada", + "--el-header-bg-color": "#191a20", + "--el-header-text-color": "#e5eaf3", + "--el-header-text-color-regular": "#cfd3dc", + "--el-header-border-color": "#414243" + }, + dark: { + "--el-header-logo-text-color": "#dadada", + "--el-header-bg-color": "#141414", + "--el-header-text-color": "#e5eaf3", + "--el-header-text-color-regular": "#cfd3dc", + "--el-header-border-color": "#414243" + } +}; diff --git a/frontend/src/styles/theme/menu.ts b/frontend/src/styles/theme/menu.ts new file mode 100644 index 0000000..32b02ba --- /dev/null +++ b/frontend/src/styles/theme/menu.ts @@ -0,0 +1,31 @@ +import { Theme } from "@/hooks/interface"; + +export const menuTheme: Record = { + light: { + "--el-menu-bg-color": "#ffffff", + "--el-menu-hover-bg-color": "#cccccc", + "--el-menu-active-bg-color": "var(--el-color-primary-light-9)", + "--el-menu-text-color": "#333333", + "--el-menu-active-color": "var(--el-color-primary)", + "--el-menu-hover-text-color": "#333333", + "--el-menu-horizontal-sub-item-height": "50px" + }, + inverted: { + "--el-menu-bg-color": "#191a20", + "--el-menu-hover-bg-color": "#000000", + "--el-menu-active-bg-color": "#000000", + "--el-menu-text-color": "#bdbdc0", + "--el-menu-active-color": "#ffffff", + "--el-menu-hover-text-color": "#ffffff", + "--el-menu-horizontal-sub-item-height": "50px" + }, + dark: { + "--el-menu-bg-color": "#141414", + "--el-menu-hover-bg-color": "#000000", + "--el-menu-active-bg-color": "#000000", + "--el-menu-text-color": "#bdbdc0", + "--el-menu-active-color": "#ffffff", + "--el-menu-hover-text-color": "#ffffff", + "--el-menu-horizontal-sub-item-height": "50px" + } +}; diff --git a/frontend/src/styles/var.scss b/frontend/src/styles/var.scss new file mode 100644 index 0000000..bdc1cb2 --- /dev/null +++ b/frontend/src/styles/var.scss @@ -0,0 +1,2 @@ +/* global css variable */ +$primary-color: var(--el-color-primary); diff --git a/frontend/src/theme/index.scss b/frontend/src/theme/index.scss deleted file mode 100644 index 22a98cf..0000000 --- a/frontend/src/theme/index.scss +++ /dev/null @@ -1,22 +0,0 @@ -@forward "element-plus/theme-chalk/src/common/var.scss" with ( - $colors: ( - "primary": (//主题色 - "base": #003078, - ), - "success": (//成功色 - "base": #67C23A, - ), - "warning": (//警告色 - "base": #E6A23C, - ), - "danger": (//危险色 - "base": #F56C6C, - ), - "error": (//错误色 - "base": #FF4949, - ), - "info": (//信息色 - "base": #409EFF, - ), - ), -); diff --git a/frontend/src/types/dict/index.ts b/frontend/src/types/dict/index.ts deleted file mode 100644 index 9eb761f..0000000 --- a/frontend/src/types/dict/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -// 字典数据 -export interface DictData { - basic: BasicDictData[] - area: BasicDictData[] - areaTree: BasicDictData[] - userList: string[] -} - -export interface BasicDictData { - name: string - id: string - code: string - value: null - sort: number | null - children?: BasicDictData[] -} diff --git a/frontend/src/types/global.d.ts b/frontend/src/types/global.d.ts deleted file mode 100644 index f7b0abe..0000000 --- a/frontend/src/types/global.d.ts +++ /dev/null @@ -1,108 +0,0 @@ -interface Window { - XEUtils: Record - existLoading: boolean - lazy: number - unique: number - tokenRefreshing: boolean - requests: Function[] - eventSource: EventSource - loadLangHandle: Record -} - -interface anyObj { - [key: string]: any -} - -interface treeData { - id?: string - value?: string - name?: string - label?: string - children: treeData[] - [key: string]: any -} - - -interface ApiResponse { - code: number - data: T - msg: string - time: number -} - -type ApiPromise = Promise> - -/* Menu */ -declare namespace Menu { - interface MenuOptions { - path: string; - name: string; - component?: string | (() => Promise); - redirect?: string; - meta: MetaProps; - children?: MenuOptions[]; - } - interface MetaProps { - icon: string; - title: string; - activeMenu?: string; - isLink?: string; - isHide: boolean; - isFull: boolean; - isAffix: boolean; - isKeepAlive: boolean; - } -} - -/* FileType */ -declare namespace File { - type ImageMimeType = - | "image/apng" - | "image/bmp" - | "image/gif" - | "image/jpeg" - | "image/pjpeg" - | "image/png" - | "image/svg+xml" - | "image/tiff" - | "image/webp" - | "image/x-icon"; - - type ExcelMimeType = "application/vnd.ms-excel" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; -} - -/* Vite */ -declare type Recordable = Record; - -declare interface ViteEnv { - VITE_USER_NODE_ENV: "development" | "production" | "test"; - VITE_GLOB_APP_TITLE: string; - VITE_PORT: number; - VITE_OPEN: boolean; - VITE_REPORT: boolean; - VITE_ROUTER_MODE: "hash" | "history"; - VITE_BUILD_COMPRESS: "gzip" | "brotli" | "gzip,brotli" | "none"; - VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean; - VITE_DROP_CONSOLE: boolean; - VITE_PWA: boolean; - VITE_DEVTOOLS: boolean; - VITE_PUBLIC_PATH: string; - VITE_API_URL: string; - VITE_PROXY: [string, string][]; -} - -interface ImportMetaEnv extends ViteEnv { - __: unknown; -} - -/* __APP_INFO__ */ -declare const __APP_INFO__: { - pkg: { - name: string; - version: string; - dependencies: Recordable; - devDependencies: Recordable; - }; - lastBuildTime: string; -}; - diff --git a/frontend/src/types/user/index.ts b/frontend/src/types/user/index.ts deleted file mode 100644 index 15329fa..0000000 --- a/frontend/src/types/user/index.ts +++ /dev/null @@ -1,39 +0,0 @@ - -// 用户信息 -export interface UserInfo { - access_token: string - refresh_token: string - expires_in: number - scope: string - nickname: string - userType: number - deptIndex: string - userIndex: string - client_id: string - headSculpture: any - jti: string - name: string - deptId: string - phone: string - email: string - limitIpStart: string - limitIpEnd: string - limitTime: string - casualUser: number - type: number - smsNotice: number - emailNotice: number - role: string[] - devCode: any - id: string - loginName: string - state: number - registerTime: string - loginTime: string - deptName: string - areaId: string - areaName: string - deptLevel: number - roleList: string[] - roleCode: string[] -} \ No newline at end of file diff --git a/frontend/src/typings/global.d.ts b/frontend/src/typings/global.d.ts new file mode 100644 index 0000000..ec56f63 --- /dev/null +++ b/frontend/src/typings/global.d.ts @@ -0,0 +1,72 @@ +/* Menu */ +declare namespace Menu { + interface MenuOptions { + path: string; + name: string; + component?: string | (() => Promise); + redirect?: string; + meta: MetaProps; + children?: MenuOptions[]; + } + interface MetaProps { + icon: string; + title: string; + activeMenu?: string; + isLink?: string; + isHide: boolean; + isFull: boolean; + isAffix: boolean; + isKeepAlive: boolean; + } +} + +/* FileType */ +declare namespace File { + type ImageMimeType = + | "image/apng" + | "image/bmp" + | "image/gif" + | "image/jpeg" + | "image/pjpeg" + | "image/png" + | "image/svg+xml" + | "image/tiff" + | "image/webp" + | "image/x-icon"; + + type ExcelMimeType = "application/vnd.ms-excel" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; +} + +/* Vite */ +declare type Recordable = Record; + +declare interface ViteEnv { + VITE_USER_NODE_ENV: "development" | "production" | "test"; + VITE_GLOB_APP_TITLE: string; + VITE_PORT: number; + VITE_OPEN: boolean; + VITE_REPORT: boolean; + VITE_ROUTER_MODE: "hash" | "history"; + VITE_BUILD_COMPRESS: "gzip" | "brotli" | "gzip,brotli" | "none"; + VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean; + VITE_DROP_CONSOLE: boolean; + VITE_PWA: boolean; + VITE_PUBLIC_PATH: string; + VITE_API_URL: string; + VITE_PROXY: [string, string][]; +} + +interface ImportMetaEnv extends ViteEnv { + __: unknown; +} + +/* __APP_INFO__ */ +declare const __APP_INFO__: { + pkg: { + name: string; + version: string; + dependencies: Recordable; + devDependencies: Recordable; + }; + lastBuildTime: string; +}; diff --git a/frontend/src/types/utils.d.ts b/frontend/src/typings/utils.d.ts similarity index 100% rename from frontend/src/types/utils.d.ts rename to frontend/src/typings/utils.d.ts diff --git a/frontend/src/types/window.d.ts b/frontend/src/typings/window.d.ts similarity index 100% rename from frontend/src/types/window.d.ts rename to frontend/src/typings/window.d.ts diff --git a/frontend/src/utils/color.ts b/frontend/src/utils/color.ts new file mode 100644 index 0000000..88ebaac --- /dev/null +++ b/frontend/src/utils/color.ts @@ -0,0 +1,59 @@ +import { ElMessage } from "element-plus"; + +/** + * @description hex颜色转rgb颜色 + * @param {String} str 颜色值字符串 + * @returns {String} 返回处理后的颜色值 + */ +export function hexToRgb(str: any) { + let hexs: any = ""; + let reg = /^\#?[0-9A-Fa-f]{6}$/; + if (!reg.test(str)) return ElMessage.warning("输入错误的hex"); + str = str.replace("#", ""); + hexs = str.match(/../g); + for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16); + return hexs; +} + +/** + * @description rgb颜色转Hex颜色 + * @param {*} r 代表红色 + * @param {*} g 代表绿色 + * @param {*} b 代表蓝色 + * @returns {String} 返回处理后的颜色值 + */ +export function rgbToHex(r: any, g: any, b: any) { + let reg = /^\d{1,3}$/; + if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage.warning("输入错误的rgb颜色值"); + let hexs = [r.toString(16), g.toString(16), b.toString(16)]; + for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`; + return `#${hexs.join("")}`; +} + +/** + * @description 加深颜色值 + * @param {String} color 颜色值字符串 + * @param {Number} level 加深的程度,限0-1之间 + * @returns {String} 返回处理后的颜色值 + */ +export function getDarkColor(color: string, level: number) { + let reg = /^\#?[0-9A-Fa-f]{6}$/; + if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值"); + let rgb = hexToRgb(color); + for (let i = 0; i < 3; i++) rgb[i] = Math.round(20.5 * level + rgb[i] * (1 - level)); + return rgbToHex(rgb[0], rgb[1], rgb[2]); +} + +/** + * @description 变浅颜色值 + * @param {String} color 颜色值字符串 + * @param {Number} level 加深的程度,限0-1之间 + * @returns {String} 返回处理后的颜色值 + */ +export function getLightColor(color: string, level: number) { + let reg = /^\#?[0-9A-Fa-f]{6}$/; + if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值"); + let rgb = hexToRgb(color); + for (let i = 0; i < 3; i++) rgb[i] = Math.round(255 * level + rgb[i] * (1 - level)); + return rgbToHex(rgb[0], rgb[1], rgb[2]); +} diff --git a/frontend/src/utils/dict.ts b/frontend/src/utils/dict.ts new file mode 100644 index 0000000..7eb30a5 --- /dev/null +++ b/frontend/src/utils/dict.ts @@ -0,0 +1,17 @@ +// ? 系统全局字典 + +/** + * @description:用户性别 + */ +export const genderType = [ + { label: "男", value: 1 }, + { label: "女", value: 2 } +]; + +/** + * @description:用户状态 + */ +export const userStatus = [ + { label: "启用", value: 1, tagType: "success" }, + { label: "禁用", value: 0, tagType: "danger" } +]; diff --git a/frontend/src/utils/eleValidate.ts b/frontend/src/utils/eleValidate.ts new file mode 100644 index 0000000..f9e7db1 --- /dev/null +++ b/frontend/src/utils/eleValidate.ts @@ -0,0 +1,14 @@ +// ? Element 常用表单校验规则 + +/** + * @rule 手机号 + */ +export function checkPhoneNumber(rule: any, value: any, callback: any) { + const regexp = /^(((13[0-9]{1})|(15[0-9]{1})|(16[0-9]{1})|(17[3-8]{1})|(18[0-9]{1})|(19[0-9]{1})|(14[5-7]{1}))+\d{8})$/; + if (value === "") callback("请输入手机号码"); + if (!regexp.test(value)) { + callback(new Error("请输入正确的手机号码")); + } else { + return callback(); + } +} diff --git a/frontend/src/utils/errorHandler.ts b/frontend/src/utils/errorHandler.ts new file mode 100644 index 0000000..d8f6aae --- /dev/null +++ b/frontend/src/utils/errorHandler.ts @@ -0,0 +1,27 @@ +import { ElNotification } from "element-plus"; + +/** + * @description 全局代码错误捕捉 + * */ +const errorHandler = (error: any) => { + // 过滤 HTTP 请求错误 + if (error.status || error.status == 0) return false; + let errorMap: { [key: string]: string } = { + InternalError: "Javascript引擎内部错误", + ReferenceError: "未找到对象", + TypeError: "使用了错误的类型或对象", + RangeError: "使用内置对象时,参数超范围", + SyntaxError: "语法错误", + EvalError: "错误的使用了Eval", + URIError: "URI错误" + }; + let errorName = errorMap[error.name] || "未知错误"; + ElNotification({ + title: errorName, + message: error, + type: "error", + duration: 3000 + }); +}; + +export default errorHandler; diff --git a/frontend/src/utils/http/index.ts b/frontend/src/utils/http/index.ts deleted file mode 100644 index 2e969c9..0000000 --- a/frontend/src/utils/http/index.ts +++ /dev/null @@ -1,229 +0,0 @@ -import type { AxiosRequestConfig, Method } from 'axios' -import axios from 'axios' -import { ElLoading, ElNotification, type LoadingOptions } from 'element-plus' -import { refreshToken } from '@/api/user' -import router from '@/router/index' -import { useUserInfoStore } from '@/stores/modules/user' - -window.requests = [] -window.tokenRefreshing = false -const pendingMap = new Map() -const loadingInstance: LoadingInstance = { - target: null, - count: 0, -} - - -/** - * 创建`Axios` - * 默认开启`reductDataFormat(简洁响应)`,返回类型为`ApiPromise` - * 关闭`reductDataFormat`,返回类型则为`AxiosPromise` - */ -function createAxios>( - axiosConfig: AxiosRequestConfig, - options: Options = {}, - loading: LoadingOptions = {}, -): T { - const userInfo = useUserInfoStore() - - const Axios = axios.create({ - baseURL: '/api', - timeout: 1000 * 60 * 5, - headers: {}, - responseType: 'json', - }) - - options = Object.assign( - { - CancelDuplicateRequest: true, // 是否开启取消重复请求, 默认为 true - loading: false, // 是否开启loading层效果, 默认为false - reductDataFormat: true, // 是否开启简洁的数据结构响应, 默认为true - showErrorMessage: true, // 是否开启接口错误信息展示,默认为true - showCodeMessage: true, // 是否开启code不为1时的信息提示, 默认为true - showSuccessMessage: false, // 是否开启code为1时的信息提示, 默认为false - anotherToken: '', // 当前请求使用另外的用户token - }, - options, - ) - - // 请求拦截 - Axios.interceptors.request.use( - config => { - removePending(config) - options.CancelDuplicateRequest && addPending(config) - // 创建loading实例 - if (options.loading) { - loadingInstance.count++ - if (loadingInstance.count === 1) { - loadingInstance.target = ElLoading.service(loading) - } - } - // 自动携带token - if (config.headers) { - const token = userInfo.getToken() - if (token) { - ;(config.headers as any).Authorization = token - } else { - config.headers.Authorization = 'Basic bmpjbnRlc3Q6bmpjbnBxcw==' - } - } - - return config - }, - error => { - return Promise.reject(error) - }, - ) - - // 响应拦截 - Axios.interceptors.response.use( - response => { - removePending(response.config) - options.loading && closeLoading(options) // 关闭loading - - if ( - response.data.code === 'A0000' || - response.data.type === 'application/json' - ) { - return options.reductDataFormat ? response.data : response - } else if (response.data.code == 'A0202') { - if (!window.tokenRefreshing) { - window.tokenRefreshing = true - return refreshToken() - .then(res => { - userInfo.setToken(res.data.token, 'auth') - response.headers.Authorization = `${res.data.token}` - window.requests.forEach(cb => cb(res.data.token)) - window.requests = [] - return Axios(response.config) - }) - .catch(err => { - userInfo.removeToken() - router.push({ name: 'login' }) - return Promise.reject(err) - }) - .finally(() => { - window.tokenRefreshing = false - }) - } else { - return new Promise(resolve => { - // 用函数形式将 resolve 存入,等待刷新后再执行 - window.requests.push((token: string) => { - response.headers.Authorization = `${token}` - resolve(Axios(response.config)) - }) - }) - } - } else if (response.data.code == 'A0024') { - userInfo.removeToken() - router.push({ name: 'login' }) - return Promise.reject(response.data) - } else { - if (options.showCodeMessage) { - ElNotification({ - type: 'error', - message: response.data.message || '未知错误', - }) - } - return Promise.reject(response.data) - } - }, - error => { - error.config && removePending(error.config) - options.loading && closeLoading(options) // 关闭loading - return Promise.reject(error) // 错误继续返回给到具体页面 - }, - ) - return Axios(axiosConfig) as T -} - -export default createAxios - -/** - * 关闭Loading层实例 - */ -function closeLoading(options: Options) { - if (options.loading && loadingInstance.count > 0) loadingInstance.count-- - if (loadingInstance.count === 0) { - loadingInstance.target.close() - loadingInstance.target = null - } -} - -/** - * 储存每个请求的唯一cancel回调, 以此为标识 - */ -function addPending(config: AxiosRequestConfig) { - const pendingKey = getPendingKey(config) - config.cancelToken = - config.cancelToken || - new axios.CancelToken(cancel => { - if (!pendingMap.has(pendingKey)) { - pendingMap.set(pendingKey, cancel) - } - }) -} - -/** - * 删除重复的请求 - */ -function removePending(config: AxiosRequestConfig) { - const pendingKey = getPendingKey(config) - if (pendingMap.has(pendingKey)) { - const cancelToken = pendingMap.get(pendingKey) - cancelToken(pendingKey) - pendingMap.delete(pendingKey) - } -} - -/** - * 生成每个请求的唯一key - */ -function getPendingKey(config: AxiosRequestConfig) { - let { data } = config - const { url, method, params, headers } = config - if (typeof data === 'string') data = JSON.parse(data) // response里面返回的config.data是个字符串对象 - return [ - url, - method, - headers && (headers as anyObj).Authorization ? (headers as anyObj).Authorization : '', - headers && (headers as anyObj)['ba-user-token'] ? (headers as anyObj)['ba-user-token'] : '', - JSON.stringify(params), - JSON.stringify(data), - ].join('&') -} - -/** - * 根据请求方法组装请求数据/参数 - */ -export function requestPayload(method: Method, data: anyObj) { - if (method == 'GET') { - return { - params: data, - } - } else if (method == 'POST') { - return { - data: data, - } - } -} - -interface LoadingInstance { - target: any - count: number -} - -interface Options { - // 是否开启取消重复请求, 默认为 true - CancelDuplicateRequest?: boolean - // 是否开启loading层效果, 默认为false - loading?: boolean - // 是否开启简洁的数据结构响应, 默认为true - reductDataFormat?: boolean - // 是否开启code不为A0000时的信息提示, 默认为true - showCodeMessage?: boolean - // 是否开启code为0时的信息提示, 默认为false - showSuccessMessage?: boolean - // 当前请求使用另外的用户token - anotherToken?: string -} diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts index e1eb997..f604164 100644 --- a/frontend/src/utils/index.ts +++ b/frontend/src/utils/index.ts @@ -1,4 +1,5 @@ import { isArray } from "@/utils/is"; +import { FieldNamesProps } from "@/components/ProTable/interface"; const mode = import.meta.env.VITE_ROUTER_MODE; @@ -275,6 +276,28 @@ export function handleProp(prop: string) { return propArr[propArr.length - 1]; } +/** + * @description 根据枚举列表查询当需要的数据(如果指定了 label 和 value 的 key值,会自动识别格式化) + * @param {String} callValue 当前单元格值 + * @param {Array} enumData 字典列表 + * @param {Array} fieldNames label && value && children 的 key 值 + * @param {String} type 过滤类型(目前只有 tag) + * @returns {String} + * */ +export function filterEnum(callValue: any, enumData?: any, fieldNames?: FieldNamesProps, type?: "tag") { + const value = fieldNames?.value ?? "value"; + const label = fieldNames?.label ?? "label"; + const children = fieldNames?.children ?? "children"; + let filterData: { [key: string]: any } = {}; + // 判断 enumData 是否为数组 + if (Array.isArray(enumData)) filterData = findItemNested(enumData, callValue, value, children); + // 判断是否输出的结果为 tag 类型 + if (type == "tag") { + return filterData?.tagType ? filterData.tagType : ""; + } else { + return filterData ? filterData[label] : "--"; + } +} /** * @description 递归查找 callValue 对应的 enum 值 diff --git a/frontend/src/utils/logout/index.ts b/frontend/src/utils/logout/index.ts deleted file mode 100644 index 256342a..0000000 --- a/frontend/src/utils/logout/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useUserInfoStore } from '@/stores/modules/user' -import { USER_INFO } from '@/constants/storeKey' - -export function clearUserInfo() { - const userInfo = useUserInfoStore() - userInfo.$reset() - // 清除用户信息缓存 - localStorage.removeItem(USER_INFO) -} diff --git a/frontend/src/utils/mittBus.ts b/frontend/src/utils/mittBus.ts new file mode 100644 index 0000000..1ab1228 --- /dev/null +++ b/frontend/src/utils/mittBus.ts @@ -0,0 +1,5 @@ +import mitt from "mitt"; + +const mittBus = mitt(); + +export default mittBus; diff --git a/frontend/src/utils/svg.ts b/frontend/src/utils/svg.ts new file mode 100644 index 0000000..0c191c9 --- /dev/null +++ b/frontend/src/utils/svg.ts @@ -0,0 +1,13 @@ +/** + * @description Loading Svg + */ +export const loadingSvg = ` + +`; diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue deleted file mode 100644 index f42c6c4..0000000 --- a/frontend/src/views/Login.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - diff --git a/frontend/src/views/dashboard/index.vue b/frontend/src/views/dashboard/index.vue index 9928094..00602c4 100644 --- a/frontend/src/views/dashboard/index.vue +++ b/frontend/src/views/dashboard/index.vue @@ -29,8 +29,6 @@ import { ref, onMounted } from "vue"; import { useRouter } from "vue-router"; import { useUserInfoStore } from "@/stores/modules/user"; import { ElMessage } from "element-plus"; -const user = useUserInfoStore(); -const activeIndex = ref("1-1"); const router = useRouter(); const modeList = [ { diff --git a/frontend/src/views/login/components/LoginForm.vue b/frontend/src/views/login/components/LoginForm.vue new file mode 100644 index 0000000..37f3667 --- /dev/null +++ b/frontend/src/views/login/components/LoginForm.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/frontend/src/views/login/index.scss b/frontend/src/views/login/index.scss new file mode 100644 index 0000000..f4791ae --- /dev/null +++ b/frontend/src/views/login/index.scss @@ -0,0 +1,83 @@ +.login-container { + height: 100%; + min-height: 550px; + background-color: #eeeeee; + background-image: url("@/assets/images/login_bg.svg"); + background-size: 100% 100%; + background-size: cover; + .login-box { + position: relative; + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: space-around; + width: 96.5%; + height: 94%; + padding: 0 50px; + background-color: rgb(255 255 255 / 80%); + border-radius: 10px; + .dark { + position: absolute; + top: 13px; + right: 18px; + } + .login-left { + width: 800px; + margin-right: 10px; + .login-left-img { + width: 100%; + height: 100%; + } + } + .login-form { + width: 420px; + padding: 50px 40px 45px; + background-color: var(--el-bg-color); + border-radius: 10px; + box-shadow: rgb(0 0 0 / 10%) 0 2px 10px 2px; + .login-logo { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 45px; + .login-icon { + width: 60px; + height: 52px; + } + .logo-text { + padding: 0 0 0 25px; + margin: 0; + font-size: 42px; + font-weight: bold; + color: #34495e; + white-space: nowrap; + } + } + .el-form-item { + margin-bottom: 40px; + } + .login-btn { + display: flex; + justify-content: space-between; + width: 100%; + margin-top: 40px; + white-space: nowrap; + .el-button { + width: 185px; + } + } + } + } +} + +@media screen and (width <= 1250px) { + .login-left { + display: none; + } +} + +@media screen and (width <= 600px) { + .login-form { + width: 97% !important; + } +} diff --git a/frontend/src/views/login/index.vue b/frontend/src/views/login/index.vue new file mode 100644 index 0000000..fd2aa8c --- /dev/null +++ b/frontend/src/views/login/index.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/frontend/vite-env.d.ts b/frontend/vite-env.d.ts deleted file mode 100644 index 360011f..0000000 --- a/frontend/vite-env.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -interface ImportMetaEnv { - readonly NODE_ENV: string; - readonly VITE_TITLE: string; - // ... 其他环境变量 -} - -interface ImportMeta { - readonly env: ImportMetaEnv; -} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 200c659..f188703 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,79 +1,75 @@ -import {defineConfig, loadEnv} from 'vite' +import { defineConfig, loadEnv, ConfigEnv, UserConfig } from 'vite' import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' import vue from '@vitejs/plugin-vue' import path from 'path' +import { wrapperEnv } from './build/getEnv' +import { createProxy } from './build/proxy' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' -export default defineConfig((config) => { - return { - plugins: [ - vue(), - // svg图标配置,可以使用svg图标 - createSvgIconsPlugin({ - iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')], - symbolId: 'icon-[dir]-[name]', - }), - AutoImport({ - resolvers: [ElementPlusResolver({ - importStyle: "sass", - })] - }), - Components({ - resolvers: [ElementPlusResolver({ - importStyle: "sass", - })] - }), - ], - // 基础配置 - base: './', - publicDir: 'public', - resolve: { - alias: { - '@': path.resolve(__dirname, 'src'), - }, +export default defineConfig(({ mode }: ConfigEnv): UserConfig => { + const root = process.cwd() + const env = loadEnv(mode, root) + const viteEnv = wrapperEnv(env) + return { + plugins: [ + vue(), + // svg图标配置,可以使用svg图标 + createSvgIconsPlugin({ + iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')], + symbolId: 'icon-[dir]-[name]', + }), + AutoImport({ + resolvers: [ElementPlusResolver({ + importStyle: 'sass', + })], + }), + Components({ + resolvers: [ElementPlusResolver({ + importStyle: 'sass', + })], + }), + ], + // 基础配置 + base: viteEnv.VITE_PUBLIC_PATH, + root, + publicDir: 'public', + resolve: { + alias: { + '@': path.resolve(__dirname, 'src'), + 'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js', + }, + }, + css: { + preprocessorOptions: { + scss: { + prependData: `@import "@/styles/var.scss";`, }, - css: { - preprocessorOptions: { - less: { - modifyVars: { - '@border-color-base': '#dce3e8', - }, - javascriptEnabled: true, - }, - scss: { - // 引入index.scss覆盖文件 - additionalData: `@use "@/theme/index.scss" as *;`, - } - }, + }, + }, + build: { + outDir: 'dist', + assetsDir: 'assets', + assetsInlineLimit: 4096, + cssCodeSplit: true, + sourcemap: false, + minify: 'terser', + terserOptions: { + compress: { + // 生产环境去除console及debug + drop_console: false, + drop_debugger: true, }, - build: { - outDir: 'dist', - assetsDir: 'assets', - assetsInlineLimit: 4096, - cssCodeSplit: true, - sourcemap: false, - minify: 'terser', - terserOptions: { - compress: { - // 生产环境去除console及debug - drop_console: false, - drop_debugger: true, - }, - }, - }, - server: { - host: '0.0.0.0', - open: false, - proxy: { - '/api': { - target: 'http://192.168.1.125:18092', //hsw - changeOrigin: true, - rewrite: path => path.replace(/^\/api/, '') //路径重写,把'/api'替换为'' - } - } - }, - } + }, + }, + server: { + host: '0.0.0.0', + port: viteEnv.VITE_PORT, + open: viteEnv.VITE_OPEN, + cors: true, + proxy: createProxy(viteEnv.VITE_PROXY), + }, + } })