!72 发布 vue 版本 5.1.2 与 cloud 版本 2.1.2

Merge pull request !72 from 疯狂的狮子Li/temp
This commit is contained in:
疯狂的狮子Li 2023-12-22 05:26:26 +00:00 committed by Gitee
commit cc9b67d467
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
25 changed files with 99 additions and 38 deletions

View File

@ -20,6 +20,8 @@ VITE_APP_PORT = 80
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
# 客户端id
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'

View File

@ -23,6 +23,8 @@ VITE_APP_PORT = 80
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
# 客户端id
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'

View File

@ -17,7 +17,9 @@ npm install --registry=https://registry.npmmirror.com
# 启动服务
npm run dev
# 构建生产环境 yarn build:prod
# 构建生产环境
npm run build:prod
# 前端访问地址 http://localhost:80
```

View File

@ -1,6 +1,6 @@
{
"name": "ruoyi-vue-plus",
"version": "5.1.1",
"version": "5.1.2",
"description": "RuoYi-Vue-Plus多租户管理系统",
"author": "LionLi",
"license": "MIT",

View File

@ -2,7 +2,7 @@
<div>
<template v-for="(item, index) in options">
<template v-if="values.includes(item.value)">
<span v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)"
<span v-if="(item.elTagType === 'default' || item.elTagType === '') && (item.elTagClass === '' || item.elTagClass == null)"
:key="item.value" :index="index" :class="item.elTagClass">
{{ item.label + " " }}
</span>
@ -11,7 +11,7 @@
:disable-transitions="true"
:key="item.value + ''"
:index="index"
:type="item.elTagType === 'primary' ? '' : item.elTagType"
:type="(item.elTagType === 'primary' || item.elTagType === 'default')? '' : item.elTagType"
:class="item.elTagClass"
>
{{ item.label + " " }}

View File

@ -2,7 +2,7 @@
<div class="relative" :style="{ width: width }">
<el-input v-model="modelValue" readonly @click="visible = !visible" placeholder="点击选择图标">
<template #prepend>
<svg-icon :icon-class="modelValue as string" />
<svg-icon :icon-class="modelValue" />
</template>
</el-input>

View File

@ -18,8 +18,8 @@
</template>
<sidebar-item
v-for="(child) in item.children as RouteOption[]"
:key="child.path"
v-for="(child, index) in item.children"
:key="child.path + index"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"

View File

@ -20,6 +20,8 @@ router.beforeEach(async (to, from, next) => {
if (to.path === '/login') {
next({ path: '/' });
NProgress.done();
} else if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
if (useUserStore().roles.length === 0) {
isRelogin.show = true;

View File

@ -139,7 +139,7 @@ export const dynamicRoutes: RouteOption[] = [
path: '/system/oss-config',
component: Layout,
hidden: true,
permissions: ['system:oss:list'],
permissions: ['system:ossConfig:list'],
children: [
{
path: 'index',

View File

@ -1,4 +1,4 @@
import { TagView } from 'vue-router';
import { TagView, RouteRecordNormalized } from 'vue-router';
export const useTagsViewStore = defineStore('tagsView', () => {
const visitedViews = ref<TagView[]>([]);
@ -35,7 +35,9 @@ export const useTagsViewStore = defineStore('tagsView', () => {
const delView = (view: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> => {
return new Promise((resolve) => {
delVisitedView(view);
if (!isDynamicRoute(view)) {
delCachedView(view);
}
resolve({
visitedViews: [...visitedViews.value],
cachedViews: [...cachedViews.value]
@ -177,6 +179,11 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}
};
const isDynamicRoute = (view: any): boolean => {
// 检查匹配的路由记录中是否有动态段
return view.matched.some((m: RouteRecordNormalized) => m.path.includes(':'));
};
return {
visitedViews,
cachedViews,

View File

@ -63,6 +63,10 @@ export const useUserStore = defineStore('user', () => {
removeToken();
};
const setAvatar = (value: string) => {
avatar.value = value;
};
return {
userId,
token,
@ -72,7 +76,8 @@ export const useUserStore = defineStore('user', () => {
permissions,
login,
getInfo,
logout
logout,
setAvatar
};
});

1
src/types/env.d.ts vendored
View File

@ -68,6 +68,7 @@ interface ImportMetaEnv {
VITE_APP_POWERJOB_ADMIN: string;
VITE_APP_ENV: string;
VITE_APP_RSA_PUBLIC_KEY: string;
VITE_APP_RSA_PRIVATE_KEY: string;
VITE_APP_CLIENT_ID: string;
VITE_APP_WEBSOCKET: string;
}

View File

@ -30,6 +30,13 @@ export const encryptBase64 = (str: CryptoJS.lib.WordArray) => {
return CryptoJS.enc.Base64.stringify(str);
};
/**
* base64
*/
export const decryptBase64 = (str: string) => {
return CryptoJS.enc.Base64.parse(str);
};
/**
* 使
* @param message
@ -43,3 +50,17 @@ export const encryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray)
});
return encrypted.toString();
};
/**
* 使
* @param message
* @param aesKey
* @returns {string}
*/
export const decryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray) => {
const decrypted = CryptoJS.AES.decrypt(message, aesKey, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return decrypted.toString(CryptoJS.enc.Utf8);
};

View File

@ -4,7 +4,7 @@ import JSEncrypt from 'jsencrypt';
const publicKey = import.meta.env.VITE_APP_RSA_PUBLIC_KEY;
// 前端不建议存放私钥 不建议解密数据 因为都是透明的意义不大
const privateKey = '**********';
const privateKey = import.meta.env.VITE_APP_RSA_PRIVATE_KEY;
// 加密
export const encrypt = (txt: string) => {

View File

@ -8,9 +8,10 @@ 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';
import { encryptBase64, encryptWithAes, generateAesKey, decryptWithAes, decryptBase64 } from '@/utils/crypto';
import { encrypt, decrypt } from '@/utils/jsencrypt';
const encryptHeader = 'encrypt-key';
let downloadLoadingInstance: LoadingInstance;
// 是否显示重新登录
export const isRelogin = { show: false };
@ -78,7 +79,7 @@ service.interceptors.request.use(
if (isEncrypt && (config.method === 'post' || config.method === 'put')) {
// 生成一个 AES 密钥
const aesKey = generateAesKey();
config.headers['encrypt-key'] = encrypt(encryptBase64(aesKey));
config.headers[encryptHeader] = encrypt(encryptBase64(aesKey));
config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey);
}
// FormData数据去请求头Content-Type
@ -96,6 +97,20 @@ service.interceptors.request.use(
// 响应拦截器
service.interceptors.response.use(
(res: AxiosResponse) => {
// 加密后的 AES 秘钥
const keyStr = res.headers[encryptHeader];
// 加密
if (keyStr != null && keyStr != '') {
const data = res.data;
// 请求体 AES 解密
const base64Str = decrypt(keyStr);
// base64 解码 得到请求头的 AES 秘钥
const aesKey = decryptBase64(base64Str.toString());
// aesKey 解码 data
const decryptData = decryptWithAes(data, aesKey);
// 将结果 (得到的是 JSON 字符串) 转为 JSON
res.data = JSON.parse(decryptData);
}
// 未设置状态码则默认成功状态
const code = res.data.code || HttpStatus.SUCCESS;
// 获取错误信息

View File

@ -33,7 +33,7 @@
* 部署方式 Docker 容器编排 一键部署业务集群<br />
* 国际化 SpringMessage Spring标准国际化方案<br />
</p>
<p><b>当前版本:</b> <span>v5.1.1</span></p>
<p><b>当前版本:</b> <span>v5.1.2</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>
@ -78,7 +78,7 @@
* 分布式监控 PrometheusGrafana 全方位性能监控<br />
* 其余与 Vue 版本一致<br />
</p>
<p><b>当前版本:</b> <span>v2.1.1</span></p>
<p><b>当前版本:</b> <span>v2.1.2</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>

View File

@ -63,6 +63,8 @@
<script setup name="Online" lang="ts">
import { forceLogout, list as initData } from "@/api/monitor/online";
import { OnlineQuery, OnlineVO } from "@/api/monitor/online/types";
import api from "@/api/system/user";
import {to} from "await-to-js";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type"));
@ -100,11 +102,13 @@ const resetQuery = () => {
}
/** 强退按钮操作 */
const handleForceLogout = async (row: OnlineVO) => {
await proxy?.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?');
const [err] = await to(proxy?.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?') as any);
if (!err) {
await forceLogout(row.tokenId);
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
}
onMounted(() => {
getList();

View File

@ -105,12 +105,12 @@ const registerRules: ElFormRules = {
const codeUrl = ref("");
const loading = ref(false);
const captchaEnabled = ref(true);
const registerRef = ref(ElForm);
const registerRef = ref<ElFormInstance>();
//
const tenantList = ref<TenantVO[]>([]);
const handleRegister = () => {
registerRef.value.validate(async (valid: boolean) => {
registerRef.value?.validate(async (valid: boolean) => {
if (valid) {
loading.value = true;
const [err] = await to(register(registerForm.value));

View File

@ -70,7 +70,7 @@
</el-table>
</el-card>
<el-dialog :title="dialog.title" v-model="dialog.visible" destroy-on-close append-to-bod width="600px">
<el-dialog :title="dialog.title" v-model="dialog.visible" destroy-on-close append-to-body width="600px">
<el-form ref="deptFormRef" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="24" v-if="form.parentId !== 0">

View File

@ -49,8 +49,8 @@
<el-table-column label="字典编码" align="center" prop="dictCode" v-if="false" />
<el-table-column label="字典标签" align="center" prop="dictLabel">
<template #default="scope">
<span v-if="(scope.row.listClass == '' || scope.row.listClass == 'default') && (scope.row.cssClass == '' || scope.row.cssClass == null)">{{ scope.row.dictLabel }}</span>
<el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass" :class="scope.row.cssClass">{{ scope.row.dictLabel }}</el-tag>
<span v-if="(scope.row.listClass === '' || scope.row.listClass === 'default') && (scope.row.cssClass === '' || scope.row.cssClass == null)">{{ scope.row.dictLabel }}</span>
<el-tag v-else :type="(scope.row.listClass === 'primary' || scope.row.listClass === 'default') ? '' : scope.row.listClass" :class="scope.row.cssClass">{{ scope.row.dictLabel }}</el-tag>
</template>
</el-table-column>
<el-table-column label="字典键值" align="center" prop="dictValue" />

View File

@ -169,7 +169,7 @@
<el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
<template #label>
<span>
<el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top">
<el-tooltip content="控制器中定义的权限字符,如:@SaCheckPermission('system:user:list')" placement="top">
<el-icon>
<question-filled />
</el-icon>

View File

@ -29,13 +29,13 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:oss:add']">新增</el-button>
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:ossConfig:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:oss:edit']">修改</el-button>
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:ossConfig:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:oss:remove']">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:ossConfig:remove']">
删除
</el-button>
</el-col>
@ -67,10 +67,10 @@
<el-table-column label="操作" fixed="right" align="center" width="150" class-name="small-padding">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:oss:edit']"></el-button>
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:ossConfig:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:oss:remove']"></el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:ossConfig:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>

View File

@ -107,7 +107,7 @@
<el-form-item prop="roleKey">
<template #label>
<span>
<el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasRole('admin')`)" placement="top">
<el-tooltip content="控制器中定义的权限字符,如:@SaCheckRole('admin')" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
权限字符

View File

@ -1,6 +1,6 @@
<template>
<div class="user-info-head" @click="editCropper()">
<img :src="options.img as string" title="点击上传头像" class="img-circle img-lg" />
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
<el-row>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
@ -62,7 +62,7 @@ import { uploadAvatar } from "@/api/system/user";
import useUserStore from "@/store/modules/user";
interface Options {
img: string | ArrayBuffer | null; //
img: string | any; //
autoCrop: boolean; //
autoCropWidth: number; //
autoCropHeight: number; //
@ -140,7 +140,7 @@ const uploadImg = async () => {
const res = await uploadAvatar(formData);
open.value = false;
options.img = res.data.imgUrl;
userStore.avatar = options.img as string
userStore.setAvatar(options.img as string)
proxy?.$modal.msgSuccess("修改成功");
visible.value = false;
});

View File

@ -97,8 +97,8 @@
<el-tabs v-model="preview.activeName">
<el-tab-pane
v-for="(value, key) in preview.data"
:label="(key as any).substring((key as any).lastIndexOf('/') + 1, (key as any).indexOf('.vm'))"
:name="(key as any).substring((key as any).lastIndexOf('/') + 1, (key as any).indexOf('.vm'))"
:label="key.substring(key.lastIndexOf('/') + 1, key.indexOf('.vm'))"
:name="key.substring(key.lastIndexOf('/') + 1, key.indexOf('.vm'))"
:key="value"
>
<el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right">