merge 合并dev

This commit is contained in:
gssong 2024-03-05 21:09:56 +08:00
commit 63d07864f7
16 changed files with 127 additions and 196 deletions

View File

@ -18,6 +18,8 @@ VITE_APP_POWERJOB_ADMIN = 'http://localhost:7700/'
VITE_APP_PORT = 80 VITE_APP_PORT = 80
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
VITE_APP_ENCRYPT = true
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换 # 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换 # 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换

View File

@ -21,6 +21,8 @@ VITE_BUILD_COMPRESS = gzip
VITE_APP_PORT = 80 VITE_APP_PORT = 80
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
VITE_APP_ENCRYPT = true
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换 # 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换 # 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换

View File

@ -38,6 +38,7 @@
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fuse.js": "7.0.0", "fuse.js": "7.0.0",
"highlight.js": "11.9.0", "highlight.js": "11.9.0",
"image-conversion": "^2.1.1",
"js-cookie": "3.0.5", "js-cookie": "3.0.5",
"jsencrypt": "3.3.2", "jsencrypt": "3.3.2",
"moddle": "6.2.3", "moddle": "6.2.3",
@ -76,10 +77,10 @@
"eslint": "8.56.0", "eslint": "8.56.0",
"eslint-config-prettier": "9.1.0", "eslint-config-prettier": "9.1.0",
"eslint-define-config": "2.1.0", "eslint-define-config": "2.1.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "5.1.3", "eslint-plugin-prettier": "5.1.3",
"eslint-plugin-promise": "6.1.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-vue": "9.20.1", "eslint-plugin-vue": "9.20.1",
"fast-glob": "3.3.2", "fast-glob": "3.3.2",
"husky": "8.0.3", "husky": "8.0.3",

View File

@ -50,12 +50,13 @@ const values = computed(() => {
const unmatch = computed(() => { const unmatch = computed(() => {
if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === 'undefined') return false; if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === 'undefined') return false;
// //
let unmatch = false; //
values.value.forEach((item) => { values.value.forEach((item) => {
if (!props.options.some((v) => v.value === item)) { if (!props.options.some((v) => v.value === item)) {
return true; // true unmatch = true; // true
} }
}); });
return false; // return unmatch; //
}); });
const unmatchArray = computed(() => { const unmatchArray = computed(() => {

View File

@ -44,6 +44,7 @@ import { listByIds, delOss } from '@/api/system/oss';
import { OssVO } from '@/api/system/oss/types'; import { OssVO } from '@/api/system/oss/types';
import { propTypes } from '@/utils/propTypes'; import { propTypes } from '@/utils/propTypes';
import { globalHeaders } from '@/utils/request'; import { globalHeaders } from '@/utils/request';
import { compressAccurately } from 'image-conversion';
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -60,7 +61,14 @@ const props = defineProps({
isShowTip: { isShowTip: {
type: Boolean, type: Boolean,
default: true default: true
} },
//
compressSupport: {
type: Boolean,
default: false
},
// KB300KB300KB
compressTargetSize: propTypes.number.def(300)
}); });
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -138,8 +146,16 @@ const handleBeforeUpload = (file: any) => {
return false; return false;
} }
} }
proxy?.$modal.loading('正在上传图片,请稍候...');
number.value++; //
if (props.compressSupport && file.size / 1024 > props.compressTargetSize) {
proxy?.$modal.loading('正在上传图片,请稍候...');
number.value++;
return compressAccurately(file, props.compressTargetSize);
} else {
proxy?.$modal.loading('正在上传图片,请稍候...');
number.value++;
}
}; };
// //

View File

@ -36,7 +36,7 @@
:data="roleList" :data="roleList"
:loading="loading" :loading="loading"
:row-config="{ keyField: 'roleId' }" :row-config="{ keyField: 'roleId' }"
:checkbox-config="{ reserve: true, checkRowKeys: defaultSelectRoleIds }" :checkbox-config="{ reserve: true, checkRowKeys: roleIds }"
highlight-current-row highlight-current-row
@checkbox-all="handleCheckboxAll" @checkbox-all="handleCheckboxAll"
@checkbox-change="handleCheckboxChange" @checkbox-change="handleCheckboxChange"
@ -63,41 +63,42 @@
v-model:total="total" v-model:total="total"
v-model:page="queryParams.pageNum" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
@pagination="pageList" @pagination="getList"
/> />
</el-card> </el-card>
<template #footer> <template #footer>
<el-button @click="close">取消</el-button> <el-button @click="roleDialog.closeDialog">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button> <el-button type="primary" @click="confirm">确定</el-button>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup name="Role" lang="ts">
import { listRole } from '@/api/system/role';
import { RoleVO, RoleQuery } from '@/api/system/role/types'; import { RoleVO, RoleQuery } from '@/api/system/role/types';
import { VxeTableInstance } from 'vxe-table'; import { VxeTableInstance } from 'vxe-table';
import useDialog from '@/hooks/useDialog'; import useDialog from '@/hooks/useDialog';
import api from '@/api/system/role';
interface PropType { interface PropType {
modelValue?: RoleVO[] | RoleVO | undefined; modelValue?: RoleVO[];
multiple?: boolean;
data?: string | number | (string | number)[];
} }
const prop = withDefaults(defineProps<PropType>(), { const prop = withDefaults(defineProps<PropType>(), {
multiple: true, modelValue: () => []
modelValue: undefined,
data: undefined
}); });
const emit = defineEmits(['update:modelValue', 'confirmCallBack']); const emit = defineEmits(['update:modelValue']);
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable')); const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
const roleIds = computed(() => prop.modelValue.map((item) => item.roleId as string));
const roleList = ref<RoleVO[]>(); const roleList = ref<RoleVO[]>();
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0); const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']); const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const selectRoleList = ref<RoleVO[]>([]); const selectRoleList = ref<RoleVO[]>([]);
@ -116,46 +117,22 @@ const queryParams = ref<RoleQuery>({
roleKey: '', roleKey: '',
status: '' status: ''
}); });
const defaultSelectRoleIds = computed(() => computedIds(prop.data));
const confirm = () => { const confirm = () => {
emit('update:modelValue', selectRoleList.value); emit('update:modelValue', [...selectRoleList.value]);
emit('confirmCallBack', selectRoleList.value);
roleDialog.closeDialog(); roleDialog.closeDialog();
}; };
const computedIds = (data) => {
if (data instanceof Array) {
return [...data];
} else if (typeof data === 'string') {
return data.split(',');
} else if (typeof data === 'number') {
return [data];
} else {
console.warn('<RoleSelect> The data type of data should be array or string or number, but I received other');
return [];
}
};
/** /**
* 查询角色列表 * 查询角色列表
*/ */
const getList = () => { const getList = () => {
loading.value = true; loading.value = true;
api.listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then((res) => { listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then((res) => {
roleList.value = res.rows; roleList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
}); });
}; };
const pageList = async () => {
await getList();
const roles = roleList.value.filter((item) => {
return selectRoleList.value.some((role) => role.roleId === item.roleId);
});
await tableRef.value.setCheckboxRow(roles, true);
};
/** /**
* 搜索按钮操作 * 搜索按钮操作
*/ */
@ -172,10 +149,6 @@ const resetQuery = () => {
}; };
const handleCheckboxChange = (checked) => { const handleCheckboxChange = (checked) => {
if (!prop.multiple && checked.checked) {
tableRef.value.setCheckboxRow(selectRoleList.value, false);
selectRoleList.value = [];
}
const row = checked.row; const row = checked.row;
if (checked.checked) { if (checked.checked) {
selectRoleList.value.push(row); selectRoleList.value.push(row);
@ -208,43 +181,19 @@ const handleCloseTag = (user: RoleVO) => {
tableRef.value?.setCheckboxRow(rows, false); tableRef.value?.setCheckboxRow(rows, false);
selectRoleList.value.splice(index, 1); selectRoleList.value.splice(index, 1);
}; };
/**
* 初始化选中数据
*/
const initSelectRole = async () => {
if (defaultSelectRoleIds.value.length > 0) {
const { data } = await api.optionSelect(defaultSelectRoleIds.value);
selectRoleList.value = data;
const users = roleList.value.filter((item) => {
return defaultSelectRoleIds.value.includes(String(item.roleId));
});
await nextTick(() => {
tableRef.value.setCheckboxRow(users, true);
});
}
};
const close = () => {
roleDialog.closeDialog();
};
watch( watch(
() => roleDialog.visible.value, () => prop.modelValue,
(newValue: boolean) => { (newVal, oldValue) => {
if (newValue) { Object.assign(selectRoleList.value, newVal);
initSelectRole(); },
} else { { deep: true }
tableRef.value.clearCheckboxReserve();
tableRef.value.clearCheckboxRow();
resetQuery();
selectRoleList.value = [];
}
}
); );
onMounted(() => {
getList(); //
});
defineExpose({ defineExpose({
open: roleDialog.openDialog, open: roleDialog.openDialog,
close: roleDialog.closeDialog close: roleDialog.closeDialog
}); });
onMounted(() => {
getList();
});
</script> </script>

View File

@ -47,7 +47,7 @@
</transition> </transition>
<el-card shadow="hover"> <el-card shadow="hover">
<template v-if="prop.multiple" #header> <template #header>
<el-tag v-for="user in selectUserList" :key="user.userId" closable style="margin: 2px" @close="handleCloseTag(user)"> <el-tag v-for="user in selectUserList" :key="user.userId" closable style="margin: 2px" @close="handleCloseTag(user)">
{{ user.userName }} {{ user.userName }}
</el-tag> </el-tag>
@ -60,8 +60,9 @@
show-overflow show-overflow
:data="userList" :data="userList"
:loading="loading" :loading="loading"
:row-config="{ keyField: 'userId', isHover: true }" :row-config="{ keyField: 'userId' }"
:checkbox-config="{ reserve: true, trigger: 'row', highlight: true, showHeader: prop.multiple }" :checkbox-config="{ reserve: true, checkRowKeys: userIds }"
highlight-current-row
@checkbox-all="handleCheckboxAll" @checkbox-all="handleCheckboxAll"
@checkbox-change="handleCheckboxChange" @checkbox-change="handleCheckboxChange"
> >
@ -89,14 +90,14 @@
v-model:page="queryParams.pageNum" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
:total="total" :total="total"
@pagination="pageList" @pagination="getList"
/> />
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
<template #footer> <template #footer>
<el-button @click="close">取消</el-button> <el-button @click="userDialog.closeDialog">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button> <el-button type="primary" @click="confirm">确定</el-button>
</template> </template>
</el-dialog> </el-dialog>
@ -111,20 +112,18 @@ import { VxeTableInstance } from 'vxe-table';
import useDialog from '@/hooks/useDialog'; import useDialog from '@/hooks/useDialog';
interface PropType { interface PropType {
modelValue?: UserVO[] | UserVO | undefined; modelValue?: UserVO[];
multiple?: boolean;
data?: string | number | (string | number)[];
} }
const prop = withDefaults(defineProps<PropType>(), { const prop = withDefaults(defineProps<PropType>(), {
multiple: true, modelValue: () => []
modelValue: undefined,
data: undefined
}); });
const emit = defineEmits(['update:modelValue', 'confirmCallBack']); const emit = defineEmits(['update:modelValue']);
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable')); const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
const userIds = computed(() => prop.modelValue.map((item) => item.userId as string));
const userList = ref<UserVO[]>(); const userList = ref<UserVO[]>();
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
@ -152,8 +151,11 @@ const queryParams = ref<UserQuery>({
roleId: '' roleId: ''
}); });
const defaultSelectUserIds = computed(() => computedIds(prop.data)); /** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */ /** 根据名称筛选部门树 */
watchEffect( watchEffect(
() => { () => {
@ -165,30 +167,10 @@ watchEffect(
); );
const confirm = () => { const confirm = () => {
emit('update:modelValue', selectUserList.value); emit('update:modelValue', [...selectUserList.value]);
emit('confirmCallBack', selectUserList.value);
userDialog.closeDialog(); userDialog.closeDialog();
}; };
const computedIds = (data) => {
if (data instanceof Array) {
return [...data];
} else if (typeof data === 'string') {
return data.split(',');
} else if (typeof data === 'number') {
return [data];
} else {
console.warn('<UserSelect> The data type of data should be array or string or number, but I received other');
return [];
}
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
/** 查询部门下拉树结构 */ /** 查询部门下拉树结构 */
const getTreeSelect = async () => { const getTreeSelect = async () => {
const res = await api.deptTreeSelect(); const res = await api.deptTreeSelect();
@ -204,14 +186,6 @@ const getList = async () => {
total.value = res.total; total.value = res.total;
}; };
const pageList = async () => {
await getList();
const users = userList.value.filter((item) => {
return selectUserList.value.some((user) => user.userId === item.userId);
});
await tableRef.value.setCheckboxRow(users, true);
};
/** 节点单击事件 */ /** 节点单击事件 */
const handleNodeClick = (data: DeptVO) => { const handleNodeClick = (data: DeptVO) => {
queryParams.value.deptId = data.id; queryParams.value.deptId = data.id;
@ -234,10 +208,6 @@ const resetQuery = () => {
}; };
const handleCheckboxChange = (checked) => { const handleCheckboxChange = (checked) => {
if (!prop.multiple && checked.checked) {
tableRef.value.setCheckboxRow(selectUserList.value, false);
selectUserList.value = [];
}
const row = checked.row; const row = checked.row;
if (checked.checked) { if (checked.checked) {
selectUserList.value.push(row); selectUserList.value.push(row);
@ -264,46 +234,22 @@ const handleCheckboxAll = (checked) => {
const handleCloseTag = (user: UserVO) => { const handleCloseTag = (user: UserVO) => {
const userId = user.userId; const userId = user.userId;
// 使split
const index = selectUserList.value.findIndex((item) => item.userId === userId); const index = selectUserList.value.findIndex((item) => item.userId === userId);
const rows = selectUserList.value[index]; const rows = selectUserList.value[index];
tableRef.value?.setCheckboxRow(rows, false); tableRef.value?.setCheckboxRow(rows, false);
selectUserList.value.splice(index, 1); selectUserList.value.splice(index, 1);
}; };
const initSelectUser = async () => {
if (defaultSelectUserIds.value.length > 0) {
const { data } = await api.optionSelect(defaultSelectUserIds.value);
selectUserList.value = data;
const users = userList.value.filter((item) => {
return defaultSelectUserIds.value.includes(String(item.userId));
});
await nextTick(() => {
tableRef.value.setCheckboxRow(users, true);
});
}
};
const close = () => {
userDialog.closeDialog();
};
watch( watch(
() => userDialog.visible.value, () => prop.modelValue,
(newValue: boolean) => { (newVal, oldValue) => {
if (newValue) { Object.assign(selectUserList.value, newVal);
initSelectUser(); },
} else { { deep: true }
tableRef.value.clearCheckboxReserve();
tableRef.value.clearCheckboxRow();
resetQuery();
selectUserList.value = [];
}
}
); );
onMounted(() => { onMounted(() => {
getTreeSelect(); // getTreeSelect();
getList(); // getList();
}); });
defineExpose({ defineExpose({

View File

@ -2,18 +2,15 @@ import { Ref } from 'vue';
interface Options { interface Options {
title?: string; title?: string;
visible?: boolean;
} }
interface Return { interface Return {
title: Ref<string>; title: Ref<string>;
visible: Ref<boolean>; visible: Ref<boolean>;
openDialog: () => void; openDialog: () => void;
closeDialog: () => void; closeDialog: () => void;
} }
export default (ops?: Options): Return => { export default (ops?: Options): Return => {
const visible = ref(ops.visible !== undefined ? ops.visible : false); const visible = ref(false);
const title = ref(ops.title || ''); const title = ref(ops.title || '');
const openDialog = () => { const openDialog = () => {

View File

@ -38,6 +38,10 @@ VXETable.config({
zIndex: 999999 zIndex: 999999
}); });
// 修改 el-dialog 默认点击遮照为不关闭
import { ElDialog } from 'element-plus';
ElDialog.props.closeOnClickModal.default = false;
const app = createApp(App); const app = createApp(App);
app.use(HighLight); app.use(HighLight);

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

@ -14,6 +14,7 @@ interface ImportMetaEnv {
VITE_APP_MONITRO_ADMIN: string; VITE_APP_MONITRO_ADMIN: string;
VITE_APP_POWERJOB_ADMIN: string; VITE_APP_POWERJOB_ADMIN: string;
VITE_APP_ENV: string; VITE_APP_ENV: string;
VITE_APP_ENCRYPT: string
VITE_APP_RSA_PUBLIC_KEY: string; VITE_APP_RSA_PUBLIC_KEY: string;
VITE_APP_RSA_PRIVATE_KEY: string; VITE_APP_RSA_PRIVATE_KEY: string;
VITE_APP_CLIENT_ID: string; VITE_APP_CLIENT_ID: string;

View File

@ -76,12 +76,14 @@ service.interceptors.request.use(
} }
} }
} }
// 当开启参数加密 if (import.meta.env.VITE_APP_ENCRYPT === 'true') {
if (isEncrypt && (config.method === 'post' || config.method === 'put')) { // 当开启参数加密
// 生成一个 AES 密钥 if (isEncrypt && (config.method === 'post' || config.method === 'put')) {
const aesKey = generateAesKey(); // 生成一个 AES 密钥
config.headers[encryptHeader] = encrypt(encryptBase64(aesKey)); const aesKey = generateAesKey();
config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, 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 // FormData数据去请求头Content-Type
if (config.data instanceof FormData) { if (config.data instanceof FormData) {
@ -97,19 +99,21 @@ service.interceptors.request.use(
// 响应拦截器 // 响应拦截器
service.interceptors.response.use( service.interceptors.response.use(
(res: AxiosResponse) => { (res: AxiosResponse) => {
// 加密后的 AES 秘钥 if (import.meta.env.VITE_APP_ENCRYPT === 'true') {
const keyStr = res.headers[encryptHeader]; // 加密后的 AES 秘钥
// 加密 const keyStr = res.headers[encryptHeader];
if (keyStr != null && keyStr != '') { // 加密
const data = res.data; if (keyStr != null && keyStr != '') {
// 请求体 AES 解密 const data = res.data;
const base64Str = decrypt(keyStr); // 请求体 AES 解密
// base64 解码 得到请求头的 AES 秘钥 const base64Str = decrypt(keyStr);
const aesKey = decryptBase64(base64Str.toString()); // base64 解码 得到请求头的 AES 秘钥
// aesKey 解码 data const aesKey = decryptBase64(base64Str.toString());
const decryptData = decryptWithAes(data, aesKey); // aesKey 解码 data
// 将结果 (得到的是 JSON 字符串) 转为 JSON const decryptData = decryptWithAes(data, aesKey);
res.data = JSON.parse(decryptData); // 将结果 (得到的是 JSON 字符串) 转为 JSON
res.data = JSON.parse(decryptData);
}
} }
// 未设置状态码则默认成功状态 // 未设置状态码则默认成功状态
const code = res.data.code || HttpStatus.SUCCESS; const code = res.data.code || HttpStatus.SUCCESS;

View File

@ -91,7 +91,8 @@ const registerRules: ElFormRules = {
], ],
password: [ password: [
{ required: true, trigger: 'blur', message: '请输入您的密码' }, { required: true, trigger: 'blur', message: '请输入您的密码' },
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' } { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
], ],
confirmPassword: [ confirmPassword: [
{ required: true, trigger: 'blur', message: '请再次输入您的密码' }, { required: true, trigger: 'blur', message: '请再次输入您的密码' },

View File

@ -197,11 +197,10 @@ const getList = async () => {
loading.value = false; loading.value = false;
showTable.value = true; showTable.value = true;
}; };
function checkFileSuffix(fileSuffix: string[]) { function checkFileSuffix(fileSuffix: string | string[]) {
let arr = ['png', 'jpg', 'jpeg']; const arr = [".png", ".jpg", ".jpeg"];
return arr.some((type) => { const suffixArray = Array.isArray(fileSuffix) ? fileSuffix : [fileSuffix];
return fileSuffix.indexOf(type) > -1; return suffixArray.some(suffix => arr.includes(suffix.toLowerCase()));
});
} }
/** 取消按钮 */ /** 取消按钮 */
function cancel() { function cancel() {

View File

@ -59,6 +59,7 @@ import { RoleVO } from '@/api/system/role/types';
import { getAuthRole, updateAuthRole } from '@/api/system/user'; import { getAuthRole, updateAuthRole } from '@/api/system/user';
import { UserForm } from '@/api/system/user/types'; import { UserForm } from '@/api/system/user/types';
import { RouteLocationNormalized } from 'vue-router'; import { RouteLocationNormalized } from 'vue-router';
import { parseTime } from "@/utils/ruoyi";
const route = useRoute(); const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;

View File

@ -395,7 +395,8 @@ const initData: PageData<UserForm, UserQuery> = {
max: 20, max: 20,
message: '用户密码长度必须介于 5 和 20 之间', message: '用户密码长度必须介于 5 和 20 之间',
trigger: 'blur' trigger: 'blur'
} },
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
], ],
email: [ email: [
{ {
@ -504,7 +505,12 @@ const handleResetPwd = async (row: UserVO) => {
cancelButtonText: '取消', cancelButtonText: '取消',
closeOnClickModal: false, closeOnClickModal: false,
inputPattern: /^.{5,20}$/, inputPattern: /^.{5,20}$/,
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间' inputErrorMessage: '用户密码长度必须介于 5 和 20 之间',
inputValidator: (value) => {
if (/<|>|"|'|\||\\/.test(value)) {
return "不能包含非法字符:< > \" ' \\\ |"
}
}
}) })
); );
if (!err && res) { if (!err && res) {

View File

@ -44,7 +44,8 @@ const rules = ref({
max: 20, max: 20,
message: '长度在 6 到 20 个字符', message: '长度在 6 到 20 个字符',
trigger: 'blur' trigger: 'blur'
} },
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
], ],
confirmPassword: [ confirmPassword: [
{ required: true, message: '确认密码不能为空', trigger: 'blur' }, { required: true, message: '确认密码不能为空', trigger: 'blur' },