diff --git a/.env.development b/.env.development index 4ec5afb..6d2f8dd 100644 --- a/.env.development +++ b/.env.development @@ -13,7 +13,13 @@ VITE_APP_CONTEXT_PATH = '/' # 监控地址 VITE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications' -# xxl-job 控制台地址 -VITE_APP_XXL_JOB_ADMIN = 'http://localhost:9100/xxl-job-admin' +# powerjob 控制台地址 +VITE_APP_POWERJOB_ADMIN = 'http://localhost:7700/' VITE_APP_PORT = 80 + +# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换 +VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' + +# 客户端id +VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' diff --git a/.env.production b/.env.production index 6e6510a..d723d2a 100644 --- a/.env.production +++ b/.env.production @@ -10,8 +10,8 @@ VITE_APP_CONTEXT_PATH = '/' # 监控地址 VITE_APP_MONITRO_ADMIN = '/admin/applications' -# 监控地址 -VITE_APP_XXL_JOB_ADMIN = '/xxl-job-admin' +# powerjob 控制台地址 +VITE_APP_POWERJOB_ADMIN = '/powerjob' # 生产环境 VITE_APP_BASE_API = '/prod-api' @@ -20,3 +20,9 @@ VITE_APP_BASE_API = '/prod-api' VITE_BUILD_COMPRESS = gzip VITE_APP_PORT = 80 + +# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换 +VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' + +# 客户端id +VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json index d076c08..a6661b6 100644 --- a/.eslintrc-auto-import.json +++ b/.eslintrc-auto-import.json @@ -1,5 +1,36 @@ { "globals": { + "ComponentInternalInstance": true, + "TransferKey": true, + "ElFormRules": true, + "CheckboxValueType": true, + "PropType": true, + "DateModelType": true, + "UploadFile": true, + "ElFormInstance": true, + "ElTableInstance": true, + "ElTreeInstance": true, + "ElTreeSelectInstance": true, + "ElSelectInstance": true, + "ElUploadInstance": true, + "ElCardInstance": true, + "ElDialogInstance": true, + "ElInputInstance": true, + "ElInputNumberInstance": true, + "ElRadioInstance": true, + "ElRadioGroupInstance": true, + "ElRadioButtonInstance": true, + "ElCheckboxInstance": true, + "ElCheckboxGroupInstance": true, + "ElSwitchInstance": true, + "ElDatePickerInstance": true, + "ElTimePickerInstance": true, + "ElTimeSelectInstance": true, + "ElScrollbarInstance": true, + "ElCascaderInstance": true, + "ElColorPickerInstance": true, + "ElRateInstance": true, + "ElSliderInstance": true, "useRouter": true, "useRoute": true, "EffectScope": true, diff --git a/.eslintrc.js b/.eslintrc.js index 2b2e0d0..b6b10a6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -29,7 +29,8 @@ module.exports = { // 关闭空类型检查 {} extendDefaults: true, types: { - '{}': false + '{}': false, + 'Function': false } } ] diff --git a/.gitignore b/.gitignore index 79e7fd9..40df474 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +.history node_modules/ dist/ npm-debug.log* diff --git a/package.json b/package.json index d9a587d..039083c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ruoyi-vue-plus", - "version": "5.0.0", + "version": "5.1.0", "description": "RuoYi-Vue-Plus多租户管理系统", "author": "LionLi", "license": "MIT", @@ -29,19 +29,23 @@ "fuse.js": "6.6.2", "js-cookie": "3.0.1", "jsencrypt": "3.3.1", + "crypto-js": "^4.1.1", "nprogress": "0.2.0", "path-browserify": "1.0.1", "path-to-regexp": "6.2.0", "pinia": "2.0.22", "screenfull": "6.0.0", + "vform3-builds": "3.0.8", "vue": "3.2.45", "vue-cropper": "1.0.3", "vue-i18n": "9.2.2", - "vue-router": "4.1.4" + "vue-router": "4.1.4", + "vue-types": "^5.0.3" }, "devDependencies": { "@iconify/json": "^2.2.40", "@intlify/unplugin-vue-i18n": "0.8.2", + "@types/crypto-js": "^4.1.1", "@types/file-saver": "2.0.5", "@types/js-cookie": "3.0.3", "@types/node": "18.14.2", @@ -69,11 +73,11 @@ "unplugin-auto-import": "0.13.0", "unplugin-icons": "0.15.1", "unplugin-vue-components": "0.23.0", + "vite": "4.3.1", "vite-plugin-compression": "0.5.1", "vite-plugin-svg-icons": "2.0.1", "vite-plugin-vue-setup-extend": "^0.4.0", "vitest": "^0.29.7", - "vite": "4.3.1", "vue-eslint-parser": "9.1.0", "vue-tsc": "0.35.0" } diff --git a/src/api/login.ts b/src/api/login.ts index b95a0c0..9efc86d 100644 --- a/src/api/login.ts +++ b/src/api/login.ts @@ -3,22 +3,24 @@ import { AxiosPromise } from 'axios'; import { LoginData, LoginResult, VerifyCodeResult, TenantInfo } from './types'; import { UserInfo } from '@/api/system/user/types'; +// pc端固定客户端授权id +const clientId = import.meta.env.VITE_APP_CLIENT_ID; + /** * @param data {LoginData} * @returns */ export function login(data: LoginData): AxiosPromise { const params = { - tenantId: data.tenantId, - username: data.username.trim(), - password: data.password, - code: data.code, - uuid: data.uuid + ...data, + clientId: data.clientId || clientId, + grantType: data.grantType || 'password' }; return request({ url: '/auth/login', headers: { - isToken: false + isToken: false, + isEncrypt: true }, method: 'post', data: params @@ -52,7 +54,7 @@ export function logout() { */ export function getCodeImg(): AxiosPromise { return request({ - url: '/code', + url: '/auth/code', headers: { isToken: false }, @@ -61,6 +63,22 @@ export function getCodeImg(): AxiosPromise { }); } +/** + * 第三方登录 + */ +export function callback(data: LoginData): AxiosPromise { + const LoginData = { + ...data, + clientId: clientId, + grantType: 'social' + }; + return request({ + url: '/auth/social/callback', + method: 'post', + data: LoginData + }); +} + // 获取用户详细信息 export function getInfo(): AxiosPromise { return request({ diff --git a/src/api/system/client/index.ts b/src/api/system/client/index.ts new file mode 100644 index 0000000..06544da --- /dev/null +++ b/src/api/system/client/index.ts @@ -0,0 +1,80 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { ClientVO, ClientForm, ClientQuery } from '@/api/system/client/types'; + +/** + * 查询客户端管理列表 + * @param query + * @returns {*} + */ + +export const listClient = (query?: ClientQuery): AxiosPromise => { + return request({ + url: '/system/client/list', + method: 'get', + params: query + }); +}; + +/** + * 查询客户端管理详细 + * @param id + */ +export const getClient = (id: string | number): AxiosPromise => { + return request({ + url: '/system/client/' + id, + method: 'get' + }); +}; + +/** + * 新增客户端管理 + * @param data + */ +export const addClient = (data: ClientForm) => { + return request({ + url: '/system/client', + method: 'post', + data: data + }); +}; + +/** + * 修改客户端管理 + * @param data + */ +export const updateClient = (data: ClientForm) => { + return request({ + url: '/system/client', + method: 'put', + data: data + }); +}; + +/** + * 删除客户端管理 + * @param id + */ +export const delClient = (id: string | number | Array) => { + return request({ + url: '/system/client/' + id, + method: 'delete' + }); +}; + +/** + * 状态修改 + * @param id ID + * @param status 状态 + */ +export function changeStatus(id: number | string, status: string) { + const data = { + id, + status + }; + return request({ + url: '/system/client/changeStatus', + method: 'put', + data: data + }); +} diff --git a/src/api/system/client/types.ts b/src/api/system/client/types.ts new file mode 100644 index 0000000..e67f95f --- /dev/null +++ b/src/api/system/client/types.ts @@ -0,0 +1,138 @@ +export interface ClientVO { + /** + * id + */ + id: string | number; + + /** + * 客户端id + */ + clientId: string | number; + + /** + * 客户端key + */ + clientKey: string; + + /** + * 客户端秘钥 + */ + clientSecret: string; + + /** + * 授权类型 + */ + grantTypeList: string[]; + + /** + * 设备类型 + */ + deviceType: string; + + /** + * token活跃超时时间 + */ + activeTimeout: number; + + /** + * token固定超时 + */ + timeout: number; + + /** + * 状态(0正常 1停用) + */ + status: string; + +} + +export interface ClientForm extends BaseEntity { + /** + * id + */ + id?: string | number; + + /** + * 客户端id + */ + clientId?: string | number; + + /** + * 客户端key + */ + clientKey?: string; + + /** + * 客户端秘钥 + */ + clientSecret?: string; + + /** + * 授权类型 + */ + grantTypeList?: string[]; + + /** + * 设备类型 + */ + deviceType?: string; + + /** + * token活跃超时时间 + */ + activeTimeout?: number; + + /** + * token固定超时 + */ + timeout?: number; + + /** + * 状态(0正常 1停用) + */ + status?: string; + +} + +export interface ClientQuery extends PageQuery { + /** + * 客户端id + */ + clientId?: string | number; + + /** + * 客户端key + */ + clientKey?: string; + + /** + * 客户端秘钥 + */ + clientSecret?: string; + + /** + * 授权类型 + */ + grantType?: string; + + /** + * 设备类型 + */ + deviceType?: string; + + /** + * token活跃超时时间 + */ + activeTimeout?: number; + + /** + * token固定超时 + */ + timeout?: number; + + /** + * 状态(0正常 1停用) + */ + status?: string; + +} diff --git a/src/api/system/social/auth.ts b/src/api/system/social/auth.ts new file mode 100644 index 0000000..17a46d3 --- /dev/null +++ b/src/api/system/social/auth.ts @@ -0,0 +1,24 @@ +import request from '@/utils/request'; + +// 绑定账号 +export function authBinding(source: string) { + return request({ + url: '/auth/binding/' + source, + method: 'get' + }); +} + +// 解绑账号 +export function authUnlock(authId: string) { + return request({ + url: '/auth/unlock/' + authId, + method: 'delete' + }); +} +//获取授权列表 +export function getAuthList() { + return request({ + url: '/system/social/list', + method: 'get' + }); +} diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts index d9fc1bd..21325c3 100644 --- a/src/api/system/user/index.ts +++ b/src/api/system/user/index.ts @@ -9,64 +9,64 @@ import { parseStrEmpty } from '@/utils/ruoyi'; * 查询用户列表 * @param query */ -export function listUser(query: UserQuery): AxiosPromise { +export const listUser = (query: UserQuery): AxiosPromise => { return request({ url: '/system/user/list', method: 'get', params: query }); -} +}; /** * 获取用户详情 * @param userId */ -export function getUser(userId?: string | number): AxiosPromise { +export const getUser = (userId?: string | number): AxiosPromise => { return request({ url: '/system/user/' + parseStrEmpty(userId), method: 'get' }); -} +}; /** * 新增用户 */ -export function addUser(data: UserForm) { +export const addUser = (data: UserForm) => { return request({ url: '/system/user', method: 'post', data: data }); -} +}; /** * 修改用户 */ -export function updateUser(data: UserForm) { +export const updateUser = (data: UserForm) => { return request({ url: '/system/user', method: 'put', data: data }); -} +}; /** * 删除用户 * @param userId 用户ID */ -export function delUser(userId: Array | string | number) { +export const delUser = (userId: Array | string | number) => { return request({ url: '/system/user/' + userId, method: 'delete' }); -} +}; /** * 用户密码重置 * @param userId 用户ID * @param password 密码 */ -export function resetUserPwd(userId: string | number, password: string) { +export const resetUserPwd = (userId: string | number, password: string) => { const data = { userId, password @@ -76,14 +76,14 @@ export function resetUserPwd(userId: string | number, password: string) { method: 'put', data: data }); -} +}; /** * 用户状态修改 * @param userId 用户ID * @param status 用户状态 */ -export function changeUserStatus(userId: number | string, status: string) { +export const changeUserStatus = (userId: number | string, status: string) => { const data = { userId, status @@ -93,36 +93,36 @@ export function changeUserStatus(userId: number | string, status: string) { method: 'put', data: data }); -} +}; /** * 查询用户个人信息 */ -export function getUserProfile(): AxiosPromise { +export const getUserProfile = (): AxiosPromise => { return request({ url: '/system/user/profile', method: 'get' }); -} +}; /** * 修改用户个人信息 * @param data 用户信息 */ -export function updateUserProfile(data: UserForm) { +export const updateUserProfile = (data: UserForm) => { return request({ url: '/system/user/profile', method: 'put', data: data }); -} +}; /** * 用户密码重置 * @param oldPassword 旧密码 * @param newPassword 新密码 */ -export function updateUserPwd(oldPassword: string, newPassword: string) { +export const updateUserPwd = (oldPassword: string, newPassword: string) => { const data = { oldPassword, newPassword @@ -132,49 +132,66 @@ export function updateUserPwd(oldPassword: string, newPassword: string) { method: 'put', params: data }); -} +}; /** * 用户头像上传 * @param data 头像文件 */ -export function uploadAvatar(data: FormData) { +export const uploadAvatar = (data: FormData) => { return request({ url: '/system/user/profile/avatar', method: 'post', data: data }); -} +}; /** * 查询授权角色 * @param userId 用户ID */ -export function getAuthRole(userId: string | number): AxiosPromise<{ user: UserVO; roles: RoleVO[] }> { +export const getAuthRole = (userId: string | number): AxiosPromise<{ user: UserVO; roles: RoleVO[] }> => { return request({ url: '/system/user/authRole/' + userId, method: 'get' }); -} +}; /** * 保存授权角色 * @param data 用户ID */ -export function updateAuthRole(data: { userId: string; roleIds: string }) { +export const updateAuthRole = (data: { userId: string; roleIds: string }) => { return request({ url: '/system/user/authRole', method: 'put', params: data }); -} +}; /** * 查询部门下拉树结构 */ -export function deptTreeSelect(): AxiosPromise { +export const deptTreeSelect = (): AxiosPromise => { return request({ url: '/system/user/deptTree', method: 'get' }); -} +}; + +export default { + listUser, + getUser, + addUser, + updateUser, + delUser, + resetUserPwd, + changeUserStatus, + getUserProfile, + updateUserProfile, + updateUserPwd, + uploadAvatar, + getAuthRole, + updateAuthRole, + deptTreeSelect +}; diff --git a/src/api/types.ts b/src/api/types.ts index 68fb427..617286c 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -15,19 +15,24 @@ export type RegisterForm = { * 登录请求 */ export interface LoginData { - tenantId: string; - username: string; - password: string; + tenantId?: string; + username?: string; + password?: string; rememberMe?: boolean; + socialCode?: string; + socialState?: string; + source?: string; code?: string; uuid?: string; + clientId: string; + grantType: string; } /** * 登录响应 */ export interface LoginResult { - token: string; + access_token: string; } /** diff --git a/src/assets/icons/svg/gitee.svg b/src/assets/icons/svg/gitee.svg new file mode 100644 index 0000000..6324608 --- /dev/null +++ b/src/assets/icons/svg/gitee.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/svg/maxkey.svg b/src/assets/icons/svg/maxkey.svg new file mode 100644 index 0000000..f8f8a7d --- /dev/null +++ b/src/assets/icons/svg/maxkey.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss index e797f64..e31ea97 100644 --- a/src/assets/styles/index.scss +++ b/src/assets/styles/index.scss @@ -26,6 +26,10 @@ html { box-sizing: border-box; } +html.dark .svg-icon, html.dark svg { + fill: var(--el-text-color-regular); +} + #app { height: 100%; } @@ -137,6 +141,7 @@ aside { border: 1px solid var(--el-border-color-light); background-color: var(--el-bg-color-overlay); padding: 0.75rem; + transition: all ease 0.3s; &:hover { box-shadow: 0 2px 12px #0000001a; @@ -199,4 +204,4 @@ aside { vertical-align: middle; margin-bottom: 10px; } -} \ No newline at end of file +} diff --git a/src/assets/styles/ruoyi.scss b/src/assets/styles/ruoyi.scss index 2e00d00..d2ecb24 100644 --- a/src/assets/styles/ruoyi.scss +++ b/src/assets/styles/ruoyi.scss @@ -90,8 +90,8 @@ h6 { .el-table__fixed-header-wrapper { th { word-break: break-word; - background-color: #f8f8f9 !important; - color: #515a6e; + background-color: $table-header-bg !important; + color: $table-header-text-color; height: 40px !important; font-size: 13px; } diff --git a/src/assets/styles/sidebar.scss b/src/assets/styles/sidebar.scss index 1db15fa..06bf057 100644 --- a/src/assets/styles/sidebar.scss +++ b/src/assets/styles/sidebar.scss @@ -84,7 +84,7 @@ .sub-menu-title-noDropdown, .el-sub-menu__title { &:hover { - background-color: rgba(0, 0, 0, 0.06) !important; + background-color: $base-sub-menu-title-hover !important; } } @@ -211,7 +211,7 @@ .el-menu-item { &:hover { // you can use $sub-menuHover - background-color: rgba(0, 0, 0, 0.06) !important; + background-color: $base-menu-hover !important; } } diff --git a/src/assets/styles/variables.module.scss b/src/assets/styles/variables.module.scss index 639b23b..d07d3d4 100644 --- a/src/assets/styles/variables.module.scss +++ b/src/assets/styles/variables.module.scss @@ -1,3 +1,40 @@ +// 全局SCSS变量 +:root { + --menuBg: #304156; + --menuColor: #bfcbd9; + --menuActiveText: #f4f4f5; + --menuHover: #263445; + + --subMenuBg: #1f2d3d; + --subMenuActiveText: #f4f4f5; + --subMenuHover: #001528; + --subMenuTitleHover: #293444; + + --fixedHeaderBg: #ffffff; + --tableHeaderBg: #f8f8f9; + --tableHeaderTextColor: #515a6e; +} +html.dark { + --menuBg: #1d1e1f; + --menuColor: #bfcbd9; + --menuActiveText: #f4f4f5; + --menuHover: #171819; + + --subMenuBg: #1d1e1f; + --subMenuActiveText: #1d1e1f; + --subMenuHover: #171819; + --subMenuTitleHover: #171819; + + --fixedHeaderBg: #171819; + --tableHeaderBg: var(--el-bg-color); + --tableHeaderTextColor: var(--el-text-color); + + // 覆盖ele 高亮当前行的标准暗色 + .el-tree-node__content { + --el-color-primary-light-9: #262727; + } +} + // base color $blue: #324157; $light-blue: #3a71a8; @@ -9,32 +46,23 @@ $yellow: #fec171; $panGreen: #30b08f; // 默认菜单主题风格 -$base-menu-color: #bfcbd9; -$base-menu-color-active: #f4f4f5; -$base-menu-background: #304156; +$base-menu-color: var(--menuColor); +$base-menu-hover: var(--menuHover); +$base-menu-color-active: var(--menuActiveText); +$base-menu-background: var(--menuBg); $base-logo-title-color: #ffffff; $base-menu-light-color: rgba(0, 0, 0, 0.7); $base-menu-light-background: #ffffff; $base-logo-light-title-color: #001529; -$base-sub-menu-background: #1f2d3d; -$base-sub-menu-hover: #001528; - -// 自定义暗色菜单风格 -/** -$base-menu-color:hsla(0,0%,100%,.65); -$base-menu-color-active:#fff; -$base-menu-background:#001529; -$base-logo-title-color: #ffffff; - -$base-menu-light-color:rgba(0,0,0,.70); -$base-menu-light-background:#ffffff; -$base-logo-light-title-color: #001529; - -$base-sub-menu-background:#000c17; -$base-sub-menu-hover:#001528; -*/ +$base-sub-menu-background: var(--subMenuBg); +$base-sub-menu-hover: var(--subMenuHover); +$base-sub-menu-title-hover: var(--subMenuTitleHover); +// 表单头背景色和标题颜色 +$fixed-header-bg: var(--fixedHeaderBg); +$table-header-bg: var(--tableHeaderBg); +$table-header-text-color: var(--tableHeaderTextColor); $--color-primary: #409eff; $--color-success: #67c23a; diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue index 435d0cc..b96829a 100644 --- a/src/components/Breadcrumb/index.vue +++ b/src/components/Breadcrumb/index.vue @@ -18,34 +18,34 @@ const router = useRouter(); const levelList = ref([]) const getBreadcrumb = () => { - // only show routes with meta.title - let matched = route.matched.filter(item => item.meta && item.meta.title); - const first = matched[0] - // 判断是否为首页 - if (!isDashboard(first)) { - matched = ([{ path: '/index', meta: { title: '首页' } }] as any).concat(matched) - } - levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) + // only show routes with meta.title + let matched = route.matched.filter(item => item.meta && item.meta.title); + const first = matched[0] + // 判断是否为首页 + if (!isDashboard(first)) { + matched = ([{ path: '/index', meta: { title: '首页' } }] as any).concat(matched) + } + levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) } const isDashboard = (route: RouteLocationMatched) => { - const name = route && route.name as string - if (!name) { - return false - } - return name.trim() === 'Index' + const name = route && route.name as string + if (!name) { + return false + } + return name.trim() === 'Index' } const handleLink = (item: RouteLocationMatched) => { - const { redirect, path } = item - redirect ? router.push(redirect as string) : router.push(path) + const { redirect, path } = item + redirect ? router.push(redirect as string) : router.push(path) } watchEffect(() => { - // if you go to the redirect page, do not update the breadcrumbs - if (route.path.startsWith('/redirect/')) return - getBreadcrumb() + // if you go to the redirect page, do not update the breadcrumbs + if (route.path.startsWith('/redirect/')) return + getBreadcrumb() }) onMounted(() => { - getBreadcrumb(); + getBreadcrumb(); }) diff --git a/src/components/BuildCode/index.vue b/src/components/BuildCode/index.vue new file mode 100644 index 0000000..b23665c --- /dev/null +++ b/src/components/BuildCode/index.vue @@ -0,0 +1,64 @@ + + + + + + diff --git a/src/components/BuildCode/render.vue b/src/components/BuildCode/render.vue new file mode 100644 index 0000000..5693ed8 --- /dev/null +++ b/src/components/BuildCode/render.vue @@ -0,0 +1,62 @@ + + + + diff --git a/src/components/DictTag/index.vue b/src/components/DictTag/index.vue index f27b16a..08aef21 100644 --- a/src/components/DictTag/index.vue +++ b/src/components/DictTag/index.vue @@ -24,7 +24,7 @@ diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue index fffcedf..489ac43 100644 --- a/src/components/Hamburger/index.vue +++ b/src/components/Hamburger/index.vue @@ -1,6 +1,6 @@ diff --git a/src/components/HeaderSearch/index.vue b/src/components/HeaderSearch/index.vue index 60ebb9d..9b0f2c6 100644 --- a/src/components/HeaderSearch/index.vue +++ b/src/components/HeaderSearch/index.vue @@ -17,12 +17,12 @@ - @@ -58,13 +57,16 @@ const realHeight = computed(() => border-radius: 5px; background-color: #ebeef5; box-shadow: 0 0 5px 1px #ccc; + :deep(.el-image__inner) { transition: all 0.3s; cursor: pointer; + &:hover { transform: scale(1.2); } } + :deep(.image-slot) { display: flex; justify-content: center; diff --git a/src/components/ImageUpload/index.vue b/src/components/ImageUpload/index.vue index 0791f3e..0c1e640 100644 --- a/src/components/ImageUpload/index.vue +++ b/src/components/ImageUpload/index.vue @@ -17,7 +17,9 @@ :on-preview="handlePictureCardPreview" :class="{ hide: fileList.length >= limit }" > - + + +
@@ -38,29 +40,20 @@ \ No newline at end of file + diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue index 156011f..d001ea8 100644 --- a/src/layout/components/Navbar.vue +++ b/src/layout/components/Navbar.vue @@ -20,8 +20,13 @@ - - + + + +
+ +
+
@@ -68,17 +73,18 @@ diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue index 64d9fdb..b50c628 100644 --- a/src/layout/components/TagsView/ScrollPane.vue +++ b/src/layout/components/TagsView/ScrollPane.vue @@ -6,22 +6,21 @@ + + diff --git a/src/layout/index.vue b/src/layout/index.vue index 5c01f90..8239ed7 100644 --- a/src/layout/index.vue +++ b/src/layout/index.vue @@ -107,6 +107,7 @@ const setLayout = () => { z-index: 9; width: calc(100% - #{$base-sidebar-width}); transition: width 0.28s; + background: $fixed-header-bg; } .hideSidebar .fixed-header { diff --git a/src/permission.ts b/src/permission.ts index 4713383..4543d08 100644 --- a/src/permission.ts +++ b/src/permission.ts @@ -10,7 +10,7 @@ import useSettingsStore from '@/store/modules/settings'; import usePermissionStore from '@/store/modules/permission'; NProgress.configure({ showSpinner: false }); -const whiteList = ['/login', '/register']; +const whiteList = ['/login', '/register', '/social-callback']; router.beforeEach(async (to, from, next) => { NProgress.start(); diff --git a/src/plugins/download.ts b/src/plugins/download.ts index e1c4414..b661457 100644 --- a/src/plugins/download.ts +++ b/src/plugins/download.ts @@ -1,9 +1,9 @@ import axios from 'axios'; import FileSaver from 'file-saver'; -import { getToken } from '@/utils/auth'; import errorCode from '@/utils/errorCode'; import { blobValidate } from '@/utils/ruoyi'; import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'; +import { globalHeaders } from "@/utils/request"; const baseURL = import.meta.env.VITE_APP_BASE_API; let downloadLoadingInstance: LoadingInstance; @@ -16,7 +16,7 @@ export default { method: 'get', url: url, responseType: 'blob', - headers: { Authorization: 'Bearer ' + getToken() } + headers: globalHeaders, }); const isBlob = blobValidate(res.data); if (isBlob) { @@ -34,21 +34,26 @@ export default { }, async zip(url: string, name: string) { url = baseURL + url; - const res = await axios({ - method: 'get', - url: url, - responseType: 'blob', - headers: { - Authorization: 'Bearer ' + getToken(), - datasource: localStorage.getItem('dataName') + downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' }); + try { + const res = await axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: globalHeaders, + }); + const isBlob = blobValidate(res.data); + if (isBlob) { + const blob = new Blob([res.data], { type: 'application/zip' }); + FileSaver.saveAs(blob, name); + } else { + this.printErrMsg(res.data); } - }); - const isBlob = blobValidate(res.data); - if (isBlob) { - const blob = new Blob([res.data], { type: 'application/zip' }); - FileSaver.saveAs(blob, name); - } else { - this.printErrMsg(res.data); + downloadLoadingInstance.close(); + } catch (r) { + console.error(r); + ElMessage.error('下载文件出现错误,请联系管理员!'); + downloadLoadingInstance.close(); } }, async printErrMsg(data: any) { diff --git a/src/plugins/modal.ts b/src/plugins/modal.ts index 9526623..a8b0548 100644 --- a/src/plugins/modal.ts +++ b/src/plugins/modal.ts @@ -1,57 +1,57 @@ -import { ElMessage, ElMessageBox, ElNotification, ElLoading, MessageBoxData } from 'element-plus'; +import { MessageBoxData } from 'element-plus'; import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'; let loadingInstance: LoadingInstance; export default { // 消息提示 - msg(content: string) { + msg(content: any) { ElMessage.info(content); }, // 错误消息 - msgError(content: string) { + msgError(content: any) { ElMessage.error(content); }, // 成功消息 - msgSuccess(content: string) { + msgSuccess(content: any) { ElMessage.success(content); }, // 警告消息 - msgWarning(content: string) { + msgWarning(content: any) { ElMessage.warning(content); }, // 弹出提示 - alert(content: string) { + alert(content: any) { ElMessageBox.alert(content, '系统提示'); }, // 错误提示 - alertError(content: string) { + alertError(content: any) { ElMessageBox.alert(content, '系统提示', { type: 'error' }); }, // 成功提示 - alertSuccess(content: string, s: string, p: { dangerouslyUseHTMLString: boolean }) { + alertSuccess(content: any) { ElMessageBox.alert(content, '系统提示', { type: 'success' }); }, // 警告提示 - alertWarning(content: string) { + alertWarning(content: any) { ElMessageBox.alert(content, '系统提示', { type: 'warning' }); }, // 通知提示 - notify(content: string) { + notify(content: any) { ElNotification.info(content); }, // 错误通知 - notifyError(content: string) { + notifyError(content: any) { ElNotification.error(content); }, // 成功通知 - notifySuccess(content: string) { + notifySuccess(content: any) { ElNotification.success(content); }, // 警告通知 - notifyWarning(content: string) { + notifyWarning(content: any) { ElNotification.warning(content); }, // 确认窗体 - confirm(content: string): Promise { + confirm(content: any): Promise { return ElMessageBox.confirm(content, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', @@ -59,7 +59,7 @@ export default { }); }, // 提交内容 - prompt(content: string) { + prompt(content: any) { return ElMessageBox.prompt(content, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', diff --git a/src/plugins/tab.ts b/src/plugins/tab.ts index a310154..97442ff 100644 --- a/src/plugins/tab.ts +++ b/src/plugins/tab.ts @@ -3,8 +3,11 @@ import router from '@/router'; import { TagView, RouteLocationRaw } from 'vue-router'; export default { - // 刷新当前tab页签 - async refreshPage(obj: TagView): Promise { + /** + * 刷新当前tab页签 + * @param obj 标签对象 + */ + async refreshPage(obj?: TagView): Promise { const { path, query, matched } = router.currentRoute.value; if (obj === undefined) { matched.forEach((m) => { @@ -15,11 +18,16 @@ export default { } }); } - // prettier-ignore - await useTagsViewStore().delCachedView(obj) - router.replace({ - path: '/redirect' + obj.path, - query: obj.query + let query1: undefined | {} = {}; + let path1: undefined | string = ''; + if (obj) { + query1 = obj.query; + path1 = obj.path; + } + await useTagsViewStore().delCachedView(obj); + await router.replace({ + path: '/redirect' + path1, + query: query1 }); }, // 关闭当前tab页签,打开新页签 @@ -34,9 +42,9 @@ export default { if (obj === undefined) { // prettier-ignore const { visitedViews } = await useTagsViewStore().delView(router.currentRoute.value) as any - const latestView = visitedViews.slice(-1)[0] + const latestView = visitedViews.slice(-1)[0]; if (latestView) { - return router.push(latestView.fullPath) + return router.push(latestView.fullPath); } return router.push('/'); } @@ -47,22 +55,31 @@ export default { return useTagsViewStore().delAllViews(); }, // 关闭左侧tab页签 - closeLeftPage(obj: TagView) { + closeLeftPage(obj?: TagView) { return useTagsViewStore().delLeftTags(obj || router.currentRoute.value); }, // 关闭右侧tab页签 - closeRightPage(obj: TagView) { + closeRightPage(obj?: TagView) { return useTagsViewStore().delRightTags(obj || router.currentRoute.value); }, // 关闭其他tab页签 - closeOtherPage(obj: TagView) { + closeOtherPage(obj?: TagView) { return useTagsViewStore().delOthersViews(obj || router.currentRoute.value); }, - // 打开tab页签 - openPage(url: RouteLocationRaw) { - return router.push(url); + /** + * 打开tab页签 + * @param url 路由地址 + * @param title 标题 + * @param query 参数 + */ + openPage(url: string, title?: string, query?: any) { + const obj = { path: url, query: { ...query, title } }; + return router.push(obj); }, - // 修改tab页签 + /** + * 修改tab页签 + * @param obj 标签对象 + */ updatePage(obj: TagView) { return useTagsViewStore().updateVisitedView(obj); } diff --git a/src/router/index.ts b/src/router/index.ts index 9540ab2..d6aefe9 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -37,6 +37,11 @@ export const constantRoutes: RouteOption[] = [ } ] }, + { + path: '/social-callback', + hidden: true, + component: () => import('@/layout/components/SocialCallback/index.vue') + }, { path: '/login', component: () => import('@/views/login.vue'), @@ -176,4 +181,5 @@ const router = createRouter({ } }); + export default router; diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts index af980ef..cd065d1 100644 --- a/src/store/modules/tagsView.ts +++ b/src/store/modules/tagsView.ts @@ -54,8 +54,11 @@ export const useTagsViewStore = defineStore('tagsView', () => { resolve([...visitedViews.value]); }); }; - const delCachedView = (view: TagView): Promise => { - const viewName = view.name as string; + const delCachedView = (view?: TagView): Promise => { + let viewName = ''; + if (view) { + viewName = view.name as string; + } return new Promise((resolve) => { const index = cachedViews.value.indexOf(viewName); index > -1 && cachedViews.value.splice(index, 1); @@ -167,6 +170,7 @@ export const useTagsViewStore = defineStore('tagsView', () => { const addCachedView = (view: TagView): void => { const viewName = view.name as string; + if (!viewName) return; if (cachedViews.value.includes(viewName)) return; if (!view.meta?.noCache) { cachedViews.value.push(viewName); diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts index 6f30437..2593d1a 100644 --- a/src/store/modules/user.ts +++ b/src/store/modules/user.ts @@ -23,8 +23,8 @@ export const useUserStore = defineStore('user', () => { const [err, res] = await to(loginApi(userInfo)); if (res) { const data = res.data; - setToken(data.token); - token.value = data.token; + setToken(data.access_token); + token.value = data.access_token; return Promise.resolve(); } return Promise.reject(err); diff --git a/src/types/element.d.ts b/src/types/element.d.ts index 8632484..e8d1f62 100644 --- a/src/types/element.d.ts +++ b/src/types/element.d.ts @@ -1 +1,35 @@ -declare type ElTagType = '' | 'success' | 'warning' | 'info' | 'danger' | 'default' | 'primary'; +import type * as ep from 'element-plus'; +declare global { + declare type ElTagType = '' | 'success' | 'warning' | 'info' | 'danger' | 'default' | 'primary'; + declare type ElFormInstance = InstanceType; + declare type ElTableInstance = InstanceType; + declare type ElTreeInstance = InstanceType; + declare type ElTreeSelectInstance = InstanceType; + declare type ElSelectInstance = InstanceType; + declare type ElUploadInstance = InstanceType; + declare type ElCardInstance = InstanceType; + declare type ElDialogInstance = InstanceType; + declare type ElInputInstance = InstanceType; + declare type ElInputNumberInstance = InstanceType; + declare type ElRadioInstance = InstanceType; + declare type ElRadioGroupInstance = InstanceType; + declare type ElRadioButtonInstance = InstanceType; + declare type ElCheckboxInstance = InstanceType; + declare type ElCheckboxGroupInstance = InstanceType; + declare type ElSwitchInstance = InstanceType; + declare type ElDatePickerInstance = InstanceType; + declare type ElTimePickerInstance = InstanceType; + declare type ElTimeSelectInstance = InstanceType; + declare type ElCascaderInstance = InstanceType; + declare type ElColorPickerInstance = InstanceType; + declare type ElRateInstance = InstanceType; + declare type ElSliderInstance = InstanceType; + declare type ElUploadInstance = InstanceType; + declare type ElScrollbarInstance = InstanceType; + + declare type TransferKey = ep.TransferKey; + declare type CheckboxValueType = ep.CheckboxValueType; + declare type ElFormRules = ep.FormRules; + declare type DateModelType = ep.DateModelType; + declare type UploadFile = typeof ep.UploadFile; +} diff --git a/src/types/env.d.ts b/src/types/env.d.ts index 7e5134a..fabf641 100644 --- a/src/types/env.d.ts +++ b/src/types/env.d.ts @@ -65,8 +65,10 @@ interface ImportMetaEnv { VITE_APP_BASE_URL: string; VITE_APP_CONTEXT_PATH: string; VITE_APP_MONITRO_ADMIN: string; - VITE_APP_XXL_JOB_ADMIN: string; + VITE_APP_POWERJOB_ADMIN: string; VITE_APP_ENV: string; + VITE_APP_RSA_PUBLIC_KEY: string; + VITE_APP_CLIENT_ID: string; } interface ImportMeta { readonly env: ImportMetaEnv; diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 5a972a3..b9bc5f4 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,9 +1,15 @@ -import { FormRules } from 'element-plus'; +import type { ComponentInternalInstance as ComponentInstance, PropType as VuePropType } from 'vue'; + declare global { + /** vue Instance */ + declare type ComponentInternalInstance = ComponentInstance; + /**vue */ + declare type PropType = VuePropType; + /** * 界面字段隐藏属性 */ - interface FieldOption { + declare interface FieldOption { key: number; label: string; visible: boolean; @@ -12,7 +18,7 @@ declare global { /** * 弹窗属性 */ - interface DialogOption { + declare interface DialogOption { /** * 弹窗标题 */ @@ -23,7 +29,7 @@ declare global { visible: boolean; } - interface UploadOption { + declare interface UploadOption { /** 设置上传的请求头部 */ headers: { [key: string]: any }; @@ -34,7 +40,7 @@ declare global { /** * 导入属性 */ - interface ImportOption extends UploadOption { + declare interface ImportOption extends UploadOption { /** 是否显示弹出层 */ open: boolean; /** 弹出层标题 */ @@ -48,14 +54,14 @@ declare global { /** * 字典数据 数据配置 */ - interface DictDataOption { + declare interface DictDataOption { label: string; value: string; elTagType?: ElTagType; elTagClass?: string; } - interface BaseEntity { + declare interface BaseEntity { createBy?: any; createDept?: any; createTime?: string; @@ -68,15 +74,15 @@ declare global { * T : 表单数据 * D : 查询参数 */ - interface PageData { + declare interface PageData { form: T; queryParams: D; - rules: FormRules; + rules: ElFormRules; } /** * 分页查询参数 */ - interface PageQuery { + declare interface PageQuery { pageNum: number; pageSize: number; } diff --git a/src/types/module.d.ts b/src/types/module.d.ts index b99bc44..987c349 100644 --- a/src/types/module.d.ts +++ b/src/types/module.d.ts @@ -1,23 +1,27 @@ -import modal from '@/plugins/modal'; -import tab from '@/plugins/tab'; -import { useDict } from '@/utils/dict'; -import { addDateRange, handleTree, selectDictLabel, selectDictLabels, parseTime } from '@/utils/ruoyi'; -import { getConfigKey, updateConfigByKey } from '@/api/system/config'; -import { download as download1 } from '@/utils/request'; -import download from '@/plugins/download'; -import animate from '@/animate'; +import type modal from '@/plugins/modal'; +import type tab from '@/plugins/tab'; +import type download from '@/plugins/download'; +import type auth from '@/plugins/auth'; +import type cache from '@/plugins/cache'; +import type animate from '@/animate'; +import type { useDict } from '@/utils/dict'; +import type { addDateRange, handleTree, selectDictLabel, selectDictLabels, parseTime } from '@/utils/ruoyi'; +import type { getConfigKey, updateConfigByKey } from '@/api/system/config'; +import type { download as rd } from '@/utils/request'; -declare module 'vue' { - export interface ComponentCustomProperties { +declare module '@vue/runtime-core' { + interface ComponentCustomProperties { // 全局方法声明 $modal: typeof modal; $tab: typeof tab; $download: typeof download; + $auth: typeof auth; + $cache: typeof cache; animate: typeof animate; useDict: typeof useDict; addDateRange: typeof addDateRange; - download: typeof download1; + download: typeof rd; handleTree: typeof handleTree; getConfigKey: typeof getConfigKey; updateConfigByKey: typeof updateConfigByKey; diff --git a/src/types/router.d.ts b/src/types/router.d.ts index b60b831..9731fa2 100644 --- a/src/types/router.d.ts +++ b/src/types/router.d.ts @@ -1,7 +1,7 @@ import { RouteRecordRaw } from 'vue-router'; declare module 'vue-router' { - type RouteOption = { + declare type RouteOption = { hidden?: boolean; permissions?: string[]; roles?: string[]; @@ -16,15 +16,15 @@ declare module 'vue-router' { query?: string; } & RouteRecordRaw; - interface _RouteLocationBase { + declare interface _RouteLocationBase { children?: RouteOption[]; } - interface RouteLocationOptions { + declare interface RouteLocationOptions { fullPath?: string; } - interface TagView extends Partial<_RouteLocationBase> { + declare interface TagView extends Partial<_RouteLocationBase> { title?: string; meta?: { link?: string; diff --git a/src/types/vform3-builds.d.ts b/src/types/vform3-builds.d.ts new file mode 100644 index 0000000..82573de --- /dev/null +++ b/src/types/vform3-builds.d.ts @@ -0,0 +1,4 @@ +declare module 'vform3-builds' { + const content: any; + export = content; +} diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 4020003..db50ac9 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -4,6 +4,6 @@ const tokenStorage = useStorage(TokenKey, null); export const getToken = () => tokenStorage.value; -export const setToken = (token: string) => (tokenStorage.value = token); +export const setToken = (access_token: string) => (tokenStorage.value = access_token); export const removeToken = () => (tokenStorage.value = null); diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts new file mode 100644 index 0000000..133893e --- /dev/null +++ b/src/utils/crypto.ts @@ -0,0 +1,45 @@ +import CryptoJS from 'crypto-js'; + +/** + * 随机生成32位的字符串 + * @returns {string} + */ +const generateRandomString = () => { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + const charactersLength = characters.length; + for (let i = 0; i < 32; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +}; + +/** + * 随机生成aes 密钥 + * @returns {string} + */ +export const generateAesKey = () => { + return CryptoJS.enc.Utf8.parse(generateRandomString()); +}; + +/** + * 加密base64 + * @returns {string} + */ +export const encryptBase64 = (str: CryptoJS.lib.WordArray) => { + return CryptoJS.enc.Base64.stringify(str); +}; + +/** + * 使用密钥对数据进行加密 + * @param message + * @param aesKey + * @returns {string} + */ +export const encryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray) => { + const encrypted = CryptoJS.AES.encrypt(message, aesKey, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }); + return encrypted.toString(); +}; diff --git a/src/utils/jsencrypt.ts b/src/utils/jsencrypt.ts index 18493ad..98114b4 100644 --- a/src/utils/jsencrypt.ts +++ b/src/utils/jsencrypt.ts @@ -1,18 +1,10 @@ import JSEncrypt from 'jsencrypt'; // 密钥对生成 http://web.chacuo.net/netrsakeypair -const publicKey = - 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='; +const publicKey = import.meta.env.VITE_APP_RSA_PUBLIC_KEY; -const privateKey = - 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + - '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' + - 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' + - 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' + - 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' + - 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' + - 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' + - 'UP8iWi1Qw0Y='; +// 前端不建议存放私钥 不建议解密数据 因为都是透明的意义不大 +const privateKey = '**********'; // 加密 export const encrypt = (txt: string) => { diff --git a/src/utils/propTypes.ts b/src/utils/propTypes.ts new file mode 100644 index 0000000..1847040 --- /dev/null +++ b/src/utils/propTypes.ts @@ -0,0 +1,25 @@ +import { CSSProperties } from 'vue'; +import VueTypes, { createTypes, toValidableType, VueTypeValidableDef, VueTypesInterface } from 'vue-types'; + +type PropTypes = VueTypesInterface & { + readonly style: VueTypeValidableDef; +}; + +const propTypes = createTypes({ + func: undefined, + bool: undefined, + string: undefined, + number: undefined, + object: undefined, + integer: undefined +}) as PropTypes; + +export default class ProjectTypes extends VueTypes { + static get style() { + return toValidableType('style', { + type: [String, Object], + default: undefined + }); + } +} +export { propTypes }; diff --git a/src/utils/request.ts b/src/utils/request.ts index d5fac4e..5b8480b 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -8,12 +8,19 @@ import { errorCode } from '@/utils/errorCode'; import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'; import FileSaver from 'file-saver'; import { getLanguage } from '@/lang'; +import { encryptBase64, encryptWithAes, generateAesKey } from '@/utils/crypto'; +import { encrypt } from '@/utils/jsencrypt'; let downloadLoadingInstance: LoadingInstance; // 是否显示重新登录 export const isRelogin = { show: false }; +export const globalHeaders = { + Authorization: "Bearer " + getToken(), + clientid: import.meta.env.VITE_APP_CLIENT_ID +} axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'; +axios.defaults.headers['clientid'] = import.meta.env.VITE_APP_CLIENT_ID; // 创建 axios 实例 const service = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API, @@ -29,6 +36,8 @@ service.interceptors.request.use( const isToken = (config.headers || {}).isToken === false; // 是否需要防止数据重复提交 const isRepeatSubmit = (config.headers || {}).repeatSubmit === false; + // 是否需要加密 + const isEncrypt = (config.headers || {}).isEncrypt === 'true'; if (getToken() && !isToken) { config.headers['Authorization'] = 'Bearer ' + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改 } @@ -63,6 +72,13 @@ service.interceptors.request.use( } } } + // 当开启参数加密 + if (isEncrypt && (config.method === 'post' || config.method === 'put')) { + // 生成一个 AES 密钥 + const aesKey = generateAesKey(); + config.headers['encrypt-key'] = encrypt(encryptBase64(aesKey)); + config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey); + } // FormData数据去请求头Content-Type if (config.data instanceof FormData) { delete config.headers['Content-Type']; diff --git a/src/views/demo/demo/index.vue b/src/views/demo/demo/index.vue index 2786a51..fa2c625 100644 --- a/src/views/demo/demo/index.vue +++ b/src/views/demo/demo/index.vue @@ -1,32 +1,34 @@