登录接口对接、人事管理接口对接
This commit is contained in:
@@ -6,7 +6,7 @@ const AuthAPI = {
|
||||
/** 登录接口*/
|
||||
login(data: LoginFormData) {
|
||||
const formData = new FormData();
|
||||
formData.append("username", data.username);
|
||||
formData.append("username", "admin");
|
||||
formData.append("password", data.password);
|
||||
formData.append("captchaKey", data.captchaKey);
|
||||
formData.append("captchaCode", data.captchaCode);
|
||||
|
||||
58
src/api/calibration/department/index.ts
Normal file
58
src/api/calibration/department/index.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import request from "@/utils/request";
|
||||
const AUTH_BASE_URL = "/api2";
|
||||
|
||||
// 公司部门列表
|
||||
export const UserDepartment = (name: string) => {
|
||||
const formData = new FormData();
|
||||
formData.append("name", name);
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/user/department`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 添加部门
|
||||
export const UserAddDepartment = (name: string) => {
|
||||
const formData = new FormData();
|
||||
formData.append("name", name);
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/user/add_department`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 删除部门
|
||||
export const UserDeleteDepartment = (name: string) => {
|
||||
const formData = new FormData();
|
||||
formData.append("name", name);
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/user/delete_department`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 公司部门人员名单
|
||||
export const UserPersonlist = (name: string) => {
|
||||
const formData = new FormData();
|
||||
formData.append("name", name);
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/user/personlist`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
};
|
||||
31
src/api/calibration/login/index.ts
Normal file
31
src/api/calibration/login/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
// const AUTH_BASE_URL = "http://8.137.99.82:8006";
|
||||
const AUTH_BASE_URL = "/api2";
|
||||
export const userLogin = (data: any) => {
|
||||
const formData = new FormData();
|
||||
formData.append("username", data.username);
|
||||
formData.append("password", data.password);
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/user/login`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 人员展示接口
|
||||
export const UserGetInfo = (account: string) => {
|
||||
const formData = new FormData();
|
||||
formData.append("account", account);
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/user/get_info`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
};
|
||||
93
src/api/calibration/personnelManagement/index.ts
Normal file
93
src/api/calibration/personnelManagement/index.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import request from "@/utils/request";
|
||||
const AUTH_BASE_URL = "/api2";
|
||||
|
||||
// 人员列表
|
||||
export const UserPersonnelList = (data: any) => {
|
||||
const formData = new FormData();
|
||||
formData.append("page", data.pageNum);
|
||||
formData.append("per_page", data.pageSize);
|
||||
formData.append("username", data.username);
|
||||
formData.append("department", data.department);
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/user/personnel-list`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 人事管理-人员添加
|
||||
export const UserCreateUser = (data: any) => {
|
||||
const formData = new FormData();
|
||||
formData.append("username", data.username);
|
||||
formData.append("account", data.account);
|
||||
formData.append("password", data.password);
|
||||
formData.append("nation", data.nation);
|
||||
formData.append("IdCard", data.IdCard);
|
||||
formData.append("department", data.department);
|
||||
formData.append("mobilePhone", data.mobilePhone);
|
||||
formData.append("position", data.position);
|
||||
formData.append("team", data.team);
|
||||
formData.append("Dateofjoining", data.Dateofjoining);
|
||||
formData.append("Confirmationtime", data.Confirmationtime);
|
||||
formData.append("Practicingcertificatetime", data.Practicingcertificatetime);
|
||||
formData.append("AcademicResume", data.AcademicResume);
|
||||
|
||||
formData.append("academic", JSON.stringify(data.academic));
|
||||
formData.append("contract", data.contract);
|
||||
formData.append("ApplicationForm", data.ApplicationForm);
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/user/create-user`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 人事管理-人员编辑
|
||||
export const UserEditorialStaff = (data: any) => {
|
||||
const formData = new FormData();
|
||||
formData.append("id", data.id);
|
||||
formData.append("username", data.username);
|
||||
formData.append("account", data.account);
|
||||
formData.append("password", data.password);
|
||||
formData.append("nation", data.nation);
|
||||
formData.append("IdCard", data.IdCard);
|
||||
formData.append("department", data.department);
|
||||
formData.append("mobilePhone", data.mobilePhone);
|
||||
formData.append("position", data.position);
|
||||
formData.append("team", data.team);
|
||||
formData.append("Dateofjoining", data.Dateofjoining);
|
||||
formData.append("Confirmationtime", data.Confirmationtime);
|
||||
formData.append("Practicingcertificatetime", data.Practicingcertificatetime);
|
||||
formData.append("AcademicResume", data.AcademicResume);
|
||||
formData.append("academic", JSON.stringify(data.academic));
|
||||
formData.append("contract", data.contract);
|
||||
formData.append("ApplicationForm", data.ApplicationForm);
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/user/editorial-staff`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 人员展示接口
|
||||
export const UserPersonnelDetails = (data: any) => {
|
||||
const formData = new FormData();
|
||||
formData.append("account", data.account);
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/user/personnel-details`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -87,7 +87,7 @@ const UserAPI = {
|
||||
*
|
||||
* @param ids 用户ID字符串,多个以英文逗号(,)分割
|
||||
*/
|
||||
deleteByIds(ids: string) {
|
||||
deleteByIds(ids: string | number) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/${ids}`,
|
||||
method: "delete",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-dropdown trigger="click">
|
||||
<el-badge v-if="noticeList.length > 0" :value="noticeList.length" :max="99">
|
||||
<el-badge v-if="noticeList?.length > 0" :value="noticeList?.length" :max="99">
|
||||
<div class="i-svg:bell" />
|
||||
</el-badge>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<template #dropdown>
|
||||
<div class="p-5">
|
||||
<template v-if="noticeList.length > 0">
|
||||
<template v-if="noticeList?.length > 0">
|
||||
<div v-for="(item, index) in noticeList" :key="index" class="w-500px py-3">
|
||||
<div class="flex-y-center">
|
||||
<DictLabel v-model="item.type" code="notice_type" size="small" />
|
||||
@@ -35,7 +35,7 @@
|
||||
</el-icon>
|
||||
</el-link>
|
||||
<el-link
|
||||
v-if="noticeList.length > 0"
|
||||
v-if="noticeList?.length > 0"
|
||||
type="primary"
|
||||
underline="never"
|
||||
@click="handleMarkAllAsRead"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { usePermissionStoreHook } from "@/store/modules/permission-store";
|
||||
import { useDictStoreHook } from "@/store/modules/dict-store";
|
||||
import { useTagsViewStore } from "@/store";
|
||||
import { cleanupWebSocket } from "@/plugins/websocket";
|
||||
import { userLogin } from "@/api/calibration/login";
|
||||
|
||||
export const useUserStore = defineStore("user", () => {
|
||||
// 用户信息
|
||||
@@ -24,12 +25,19 @@ export const useUserStore = defineStore("user", () => {
|
||||
function login(LoginFormData: LoginFormData) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
AuthAPI.login(LoginFormData)
|
||||
.then((data) => {
|
||||
.then(({ data }: any) => {
|
||||
const { accessToken, refreshToken } = data;
|
||||
// 保存记住我状态和token
|
||||
rememberMe.value = LoginFormData.rememberMe;
|
||||
AuthStorage.setTokens(accessToken, refreshToken, rememberMe.value);
|
||||
resolve();
|
||||
userLogin(LoginFormData)
|
||||
.then(() => {
|
||||
console.log("login success");
|
||||
resolve();
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
|
||||
141
src/utils/auxiliaryFunction/index.ts
Normal file
141
src/utils/auxiliaryFunction/index.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* 节流函数 - 限制函数执行频率
|
||||
* @param func 需要节流的函数
|
||||
* @param delay 节流延迟时间(毫秒)
|
||||
* @returns 节流后的函数
|
||||
*/
|
||||
export function throttle<T extends (...args: any[]) => any>(
|
||||
func: T,
|
||||
delay: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
let lastExecTime = 0;
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
|
||||
const now = Date.now();
|
||||
|
||||
// 如果距离上次执行时间超过延迟时间,则立即执行
|
||||
if (now - lastExecTime >= delay) {
|
||||
func.apply(this, args);
|
||||
lastExecTime = now;
|
||||
} else {
|
||||
// 否则延迟执行,避免频繁触发
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(
|
||||
() => {
|
||||
func.apply(this, args);
|
||||
lastExecTime = Date.now();
|
||||
timer = null;
|
||||
},
|
||||
delay - (now - lastExecTime)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 处理后端返回的JSON字符串 正确格式化
|
||||
export function formatJsonString(jsonString: string) {
|
||||
// 移除首尾空白字符
|
||||
jsonString = jsonString.trim();
|
||||
|
||||
// 首先检查是否为空字符串
|
||||
if (!jsonString) return '""';
|
||||
|
||||
try {
|
||||
// 尝试直接解析(处理标准JSON)
|
||||
return JSON.stringify(JSON.parse(jsonString), null, 2);
|
||||
} catch (error) {
|
||||
// 增强版处理策略,特别优化对单引号数组的处理
|
||||
try {
|
||||
// 针对数组类型的特殊处理
|
||||
if (jsonString.startsWith("[") && jsonString.endsWith("]")) {
|
||||
// 处理数组中的单引号元素
|
||||
const arrayFixed = jsonString
|
||||
// 替换数组中所有单引号包裹的元素为双引号
|
||||
.replace(/'([^']*)'/g, '"$1"');
|
||||
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(arrayFixed), null, 2);
|
||||
} catch (arrayError) {
|
||||
// 如果仍失败,尝试更细致的处理
|
||||
const detailedFixed = jsonString
|
||||
// 1. 先处理特殊情况:确保数组元素之间的逗号正确处理
|
||||
.replace(/',\s*'/g, '", "')
|
||||
// 2. 处理数组首尾的单引号
|
||||
.replace(/^\['/, '["')
|
||||
.replace(/'\]$/, '"]');
|
||||
|
||||
return JSON.stringify(JSON.parse(detailedFixed), null, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 通用处理策略
|
||||
const fixedJson = jsonString
|
||||
// 1. 处理对象键名的单引号
|
||||
.replace(/([{\[,\s])\s*'([^']+?)'\s*:/g, '$1"$2":')
|
||||
// 2. 处理数组元素的单引号
|
||||
.replace(/(\[|,)\s*'([^']*?)'(?=\s*[\],])/g, '$1"$2"')
|
||||
// 3. 处理对象值的单引号
|
||||
.replace(/:\s*'([^']*?)'\s*([}\],]|$)/g, ': "$1"$2');
|
||||
|
||||
return JSON.stringify(JSON.parse(fixedJson), null, 2);
|
||||
} catch (secondError) {
|
||||
// 最后的尝试:使用更简单直接的方法
|
||||
try {
|
||||
// 最基础的处理:将所有单引号替换为双引号
|
||||
// 这对于简单的单引号格式JSON很有效
|
||||
const basicFix = jsonString.replace(/'/g, '"');
|
||||
return JSON.stringify(JSON.parse(basicFix), null, 2);
|
||||
} catch (finalError) {
|
||||
// 如果所有尝试都失败,返回原始字符串
|
||||
console.warn("无法解析的JSON字符串:", jsonString);
|
||||
return jsonString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件路径数组转换为对象数组
|
||||
* @param filePaths 文件路径数组或单个文件路径字符串
|
||||
* @returns 包含文件信息的对象数组
|
||||
*/
|
||||
export function convertFilePathsToObject(filePaths: string | string[]): Array<{
|
||||
url: string;
|
||||
name: string;
|
||||
domain: string;
|
||||
uuid: string;
|
||||
}> {
|
||||
// 如果是字符串,先转换为数组
|
||||
const paths = Array.isArray(filePaths) ? filePaths : [filePaths];
|
||||
|
||||
return paths.map((path) => {
|
||||
// 确保path是字符串类型
|
||||
if (typeof path !== "string") {
|
||||
return {
|
||||
url: "",
|
||||
domain: "",
|
||||
name: "",
|
||||
uuid: "",
|
||||
};
|
||||
}
|
||||
|
||||
// 分割域名和文件部分
|
||||
const parts = path.split("/");
|
||||
const domain = parts[0] || "";
|
||||
const filePart = parts.slice(1).join("/") || "";
|
||||
|
||||
// 分割文件名和UUID
|
||||
const lastUnderscoreIndex = filePart.lastIndexOf("_");
|
||||
const name =
|
||||
lastUnderscoreIndex > 0 ? filePart.substring(0, lastUnderscoreIndex) : filePart;
|
||||
const uuid = lastUnderscoreIndex > 0 ? filePart.substring(lastUnderscoreIndex + 1) : "";
|
||||
|
||||
return {
|
||||
url: path,
|
||||
domain,
|
||||
name,
|
||||
uuid,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -8,6 +8,16 @@ import { authConfig } from "@/settings";
|
||||
// 初始化token刷新组合式函数
|
||||
const { refreshTokenAndRetry } = useTokenRefresh();
|
||||
|
||||
// 生成随机Token
|
||||
const generateRandomToken = () => {
|
||||
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let token = "";
|
||||
for (let i = 0; i < 32; i++) {
|
||||
token += characters.charAt(Math.floor(Math.random() * characters.length));
|
||||
}
|
||||
return token;
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建 HTTP 请求实例
|
||||
*/
|
||||
@@ -24,7 +34,6 @@ const httpRequest = axios.create({
|
||||
httpRequest.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
const accessToken = AuthStorage.getAccessToken();
|
||||
|
||||
// 如果 Authorization 设置为 no-auth,则不携带 Token
|
||||
if (config.headers.Authorization !== "no-auth" && accessToken) {
|
||||
config.headers.Authorization = `Bearer ${accessToken}`;
|
||||
@@ -50,11 +59,10 @@ httpRequest.interceptors.response.use(
|
||||
return response;
|
||||
}
|
||||
|
||||
const { code, data, msg } = response.data;
|
||||
|
||||
const { code, msg } = response.data;
|
||||
// 请求成功
|
||||
if (code === ApiCodeEnum.SUCCESS) {
|
||||
return data;
|
||||
if (code === ApiCodeEnum.SUCCESS || code == "0") {
|
||||
return response.data as any;
|
||||
}
|
||||
|
||||
// 业务错误
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<div cursor-pointer h-40px w-120px flex-center @click="getCaptcha">
|
||||
<el-icon v-if="codeLoading" class="is-loading" size="20"><Loading /></el-icon>
|
||||
<img
|
||||
v-else-if="captchaBase64"
|
||||
v-else-if="captchaBase64 && captchaBase64.length > 0"
|
||||
border-rd-4px
|
||||
object-cover
|
||||
shadow="[0_0_0_1px_var(--el-border-color)_inset]"
|
||||
@@ -89,7 +89,6 @@ import type { FormInstance } from "element-plus";
|
||||
import AuthAPI, { type LoginFormData } from "@/api/auth-api";
|
||||
import router from "@/router";
|
||||
import { useUserStore } from "@/store";
|
||||
import CommonWrapper from "@/components/CommonWrapper/index.vue";
|
||||
import { AuthStorage } from "@/utils/auth";
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -151,7 +150,7 @@ const codeLoading = ref(false);
|
||||
function getCaptcha() {
|
||||
codeLoading.value = true;
|
||||
AuthAPI.getCaptcha()
|
||||
.then((data) => {
|
||||
.then(({ data }: any) => {
|
||||
loginFormData.value.captchaKey = data.captchaKey;
|
||||
captchaBase64.value = data.captchaBase64;
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- 部门树 -->
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<el-input v-model="deptName" placeholder="部门名称" clearable>
|
||||
<el-input v-model="deptName" placeholder="部门名称" clearable @input="deptNameInput">
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
@@ -11,7 +11,10 @@
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
:data="deptList"
|
||||
:props="{ children: 'children', label: 'label', disabled: '' }"
|
||||
:props="{ children: 'children', label: 'name', disabled: '' }"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
:current-node-key="deptList?.length ? deptList[0].id : ''"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="handleFilter"
|
||||
default-expand-all
|
||||
@@ -21,7 +24,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DeptAPI from "@/api/system/dept-api";
|
||||
import { UserDepartment } from "@/api/calibration/department";
|
||||
import { throttle } from "@/utils/auxiliaryFunction";
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
@@ -29,23 +33,26 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const deptList = ref<OptionType[]>(); // 部门列表
|
||||
const deptList = ref<any[]>(); // 部门列表
|
||||
const deptTreeRef = ref(); // 部门树
|
||||
const deptName = ref(); // 部门名称
|
||||
const deptName = ref(""); // 部门名称
|
||||
|
||||
const emits = defineEmits(["node-click"]);
|
||||
const emits = defineEmits(["node-click", "update:modelValue"]);
|
||||
|
||||
const deptId = useVModel(props, "modelValue", emits);
|
||||
|
||||
watchEffect(
|
||||
() => {
|
||||
deptTreeRef.value.filter(deptName.value);
|
||||
},
|
||||
{
|
||||
flush: "post", // watchEffect会在DOM挂载或者更新之前就会触发,此属性控制在DOM元素更新后运行
|
||||
}
|
||||
);
|
||||
// watchEffect(
|
||||
// () => {
|
||||
// deptTreeRef.value.filter(deptName.value);
|
||||
// },
|
||||
// {
|
||||
// flush: "post", // watchEffect会在DOM挂载或者更新之前就会触发,此属性控制在DOM元素更新后运行
|
||||
// }
|
||||
// );
|
||||
|
||||
const deptNameInput = throttle((value: string) => {
|
||||
DepartmentList(value);
|
||||
}, 300);
|
||||
/**
|
||||
* 部门筛选
|
||||
*/
|
||||
@@ -58,13 +65,19 @@ function handleFilter(value: string, data: any) {
|
||||
|
||||
/** 部门树节点 Click */
|
||||
function handleNodeClick(data: { [key: string]: any }) {
|
||||
deptId.value = data.value;
|
||||
deptId.value = data.name;
|
||||
emits("node-click");
|
||||
}
|
||||
const DepartmentList = (name: string = "") => {
|
||||
UserDepartment(name).then((res: any) => {
|
||||
deptList.value = res.data;
|
||||
if (deptList.value && deptList.value.length > 0) {
|
||||
deptId.value = deptList.value[0].name;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
DeptAPI.getOptions().then((data) => {
|
||||
deptList.value = data;
|
||||
});
|
||||
DepartmentList();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<el-row :gutter="20">
|
||||
<!-- 部门树 -->
|
||||
<el-col :lg="4" :xs="24" class="mb-[12px]">
|
||||
<DeptTree v-model="queryParams.deptId" @node-click="handleQuery" />
|
||||
<DeptTree v-model="queryParams.department" @node-click="handleQuery" />
|
||||
</el-col>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
@@ -14,36 +14,36 @@
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="auto">
|
||||
<el-form-item label="关键字" prop="keywords">
|
||||
<el-input
|
||||
v-model="queryParams.keywords"
|
||||
v-model="queryParams.username"
|
||||
placeholder="用户名/昵称/手机号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="全部"
|
||||
clearable
|
||||
style="width: 100px"
|
||||
>
|
||||
<el-option label="正常" :value="1" />
|
||||
<el-option label="禁用" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="状态" prop="status">-->
|
||||
<!-- <el-select-->
|
||||
<!-- v-model="queryParams.status"-->
|
||||
<!-- placeholder="全部"-->
|
||||
<!-- clearable-->
|
||||
<!-- style="width: 100px"-->
|
||||
<!-- >-->
|
||||
<!-- <el-option label="正常" :value="1" />-->
|
||||
<!-- <el-option label="禁用" :value="0" />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
:editable="false"
|
||||
type="daterange"
|
||||
range-separator="~"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="截止时间"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="创建时间">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="queryParams.createTime"-->
|
||||
<!-- :editable="false"-->
|
||||
<!-- type="daterange"-->
|
||||
<!-- range-separator="~"-->
|
||||
<!-- start-placeholder="开始时间"-->
|
||||
<!-- end-placeholder="截止时间"-->
|
||||
<!-- value-format="YYYY-MM-DD"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
|
||||
<el-form-item class="search-buttons">
|
||||
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
|
||||
@@ -74,13 +74,13 @@
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<el-button
|
||||
v-hasPerm="'sys:user:import'"
|
||||
icon="upload"
|
||||
@click="handleOpenImportDialog"
|
||||
>
|
||||
导入
|
||||
</el-button>
|
||||
<!-- <el-button-->
|
||||
<!-- v-hasPerm="'sys:user:import'"-->
|
||||
<!-- icon="upload"-->
|
||||
<!-- @click="handleOpenImportDialog"-->
|
||||
<!-- >-->
|
||||
<!-- 导入-->
|
||||
<!-- </el-button>-->
|
||||
|
||||
<el-button v-hasPerm="'sys:user:export'" icon="download" @click="handleExport">
|
||||
导出
|
||||
@@ -99,43 +99,35 @@
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="用户名" prop="username" />
|
||||
<el-table-column label="昵称" width="150" align="center" prop="nickname" />
|
||||
<el-table-column label="性别" width="100" align="center">
|
||||
<el-table-column label="用户名" prop="account" />
|
||||
<el-table-column label="昵称" align="center" prop="username" />
|
||||
<!-- <el-table-column label="性别" width="100" align="center">-->
|
||||
<!-- <template #default="scope">-->
|
||||
<!-- <DictLabel v-model="scope.row.gender" code="gender" />-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="部门" align="center" prop="department" />
|
||||
<el-table-column label="手机号码" align="center" prop="mobilePhone" />
|
||||
<el-table-column label="入职时间" align="center" prop="Dateofjoining" />
|
||||
<el-table-column label="操作" fixed="right" align="center" width="220">
|
||||
<template #default="scope">
|
||||
<DictLabel v-model="scope.row.gender" code="gender" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="部门" width="120" align="center" prop="deptName" />
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="120" />
|
||||
<el-table-column label="邮箱" align="center" prop="email" width="160" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status == 1 ? 'success' : 'info'">
|
||||
{{ scope.row.status == 1 ? "正常" : "禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="150" />
|
||||
<el-table-column label="操作" fixed="right" width="220">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPerm="'sys:user:reset-password'"
|
||||
type="primary"
|
||||
icon="RefreshLeft"
|
||||
size="small"
|
||||
link
|
||||
@click="handleResetPassword(scope.row)"
|
||||
>
|
||||
重置密码
|
||||
</el-button>
|
||||
<!-- <el-button-->
|
||||
<!-- v-hasPerm="'sys:user:reset-password'"-->
|
||||
<!-- type="primary"-->
|
||||
<!-- icon="RefreshLeft"-->
|
||||
<!-- size="small"-->
|
||||
<!-- link-->
|
||||
<!-- @click="handleResetPassword(scope.row)"-->
|
||||
<!-- >-->
|
||||
<!-- 重置密码-->
|
||||
<!-- </el-button>-->
|
||||
<el-button
|
||||
v-hasPerm="'sys:user:edit'"
|
||||
type="primary"
|
||||
icon="edit"
|
||||
link
|
||||
size="small"
|
||||
@click="handleOpenDialog(scope.row.id)"
|
||||
@click="handleOpenDialog(scope.row)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
@@ -174,53 +166,33 @@
|
||||
>
|
||||
<el-form ref="userFormRef" :model="formData" :rules="rules" label-width="120px">
|
||||
<el-form-item label="姓名" prop="username">
|
||||
<el-input
|
||||
v-model="formData.username"
|
||||
:readonly="!!formData.id"
|
||||
placeholder="请输入姓名"
|
||||
/>
|
||||
<el-input v-model="formData.username" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="民族" prop="nationality">
|
||||
<el-input
|
||||
v-model="formData.nationality"
|
||||
:readonly="!!formData.nationality"
|
||||
placeholder="请输入民族"
|
||||
/>
|
||||
<el-form-item label="账号" prop="account">
|
||||
<el-input v-model="formData.account" placeholder="请输入账号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号码" prop="idCard">
|
||||
<el-input
|
||||
v-model="formData.idCard"
|
||||
:readonly="!!formData.idCard"
|
||||
placeholder="请输入身份证号码"
|
||||
maxlength="18"
|
||||
/>
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input v-model="formData.password" placeholder="请输入密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="mobile">
|
||||
<el-input
|
||||
v-model="formData.mobile"
|
||||
:readonly="!!formData.mobile"
|
||||
placeholder="请输入手机号"
|
||||
maxlength="11"
|
||||
/>
|
||||
<el-form-item label="民族" prop="nation">
|
||||
<el-input v-model="formData.nation" placeholder="请输入民族" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属部门" prop="deptId">
|
||||
<el-select
|
||||
v-model="formData.deptId"
|
||||
placeholder="请选择所属部门"
|
||||
:readonly="!!formData.deptId"
|
||||
>
|
||||
<el-form-item label="身份证号码" prop="IdCard">
|
||||
<el-input v-model="formData.IdCard" placeholder="请输入身份证号码" maxlength="18" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="mobilePhone">
|
||||
<el-input v-model="formData.mobilePhone" placeholder="请输入手机号" maxlength="11" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属部门" prop="department">
|
||||
<el-select v-model="formData.department" placeholder="请选择所属部门">
|
||||
<el-option key="行政部" label="行政部" value="行政部" />
|
||||
<el-option key="财务部" label="财务部" value="财务部" />
|
||||
<el-option key="执业律师" label="执业律师" value="执业律师" />
|
||||
<el-option key="实习律师" label="实习律师" value="实习律师" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位" prop="roleIds">
|
||||
<el-select
|
||||
v-model="formData.roleIds"
|
||||
placeholder="请选择所属部门"
|
||||
:readonly="!!formData.roleIds"
|
||||
>
|
||||
<el-form-item label="岗位" prop="position">
|
||||
<el-select v-model="formData.position" placeholder="请选择所属部门">
|
||||
<el-option key="助理" label="助理" value="助理" />
|
||||
<el-option key="独立律师" label="独立律师" value="独立律师" />
|
||||
<el-option key="一级主办律师" label="一级主办律师" value="一级主办律师" />
|
||||
@@ -230,158 +202,151 @@
|
||||
<el-option key="已离职" label="已离职" value="已离职" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属团队" prop="teamIds">
|
||||
<el-select
|
||||
v-model="formData.teamIds"
|
||||
placeholder="请选择所属团队"
|
||||
:readonly="!!formData.teamIds"
|
||||
>
|
||||
<el-form-item label="所属团队" prop="team">
|
||||
<el-select v-model="formData.team" placeholder="请选择所属团队">
|
||||
<el-option key="团队一" label="团队一" value="团队一" />
|
||||
<el-option key="团队二" label="团队二" value="团队二" />
|
||||
<el-option key="团队三" label="团队三" value="团队三" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="入职时间" prop="hiredDate">
|
||||
<el-form-item label="入职时间" prop="Dateofjoining">
|
||||
<el-date-picker
|
||||
v-model="formData.hiredDate"
|
||||
v-model="formData.Dateofjoining"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择入职时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="转正时间" prop="promotionDate">
|
||||
<el-form-item label="转正时间" prop="Confirmationtime">
|
||||
<el-date-picker
|
||||
v-model="formData.promotionDate"
|
||||
v-model="formData.Confirmationtime"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择转正时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="执业证时间" prop="licenseDate">
|
||||
<el-form-item label="执业证时间" prop="Practicingcertificatetime">
|
||||
<el-date-picker
|
||||
v-model="formData.licenseDate"
|
||||
v-model="formData.Practicingcertificatetime"
|
||||
type="date"
|
||||
value-format="YYYY-MM"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择执业证时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="离职时间" prop="resignationDate">
|
||||
<el-date-picker
|
||||
v-model="formData.resignationDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择离职时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="学业简历" prop="educationResume">
|
||||
<!-- 学业简历表格样式布局 -->
|
||||
<div class="education-resume-table">
|
||||
<!-- 表头 -->
|
||||
<div class="education-resume-header">
|
||||
<div class="table-col date-col">日期区间</div>
|
||||
<div class="table-col school-col">毕业院校</div>
|
||||
<div class="table-col major-col">专业</div>
|
||||
<div class="table-col degree-col">学历</div>
|
||||
<div class="table-col action-col">操作</div>
|
||||
<!-- <el-form-item label="离职时间" prop="resignationDate">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="formData.resignationDate"-->
|
||||
<!-- type="date"-->
|
||||
<!-- value-format="YYYY-MM-DD"-->
|
||||
<!-- placeholder="请选择离职时间"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="学业简历" prop="academic">
|
||||
<div style="width: 440px; display: flex; flex-direction: column">
|
||||
<!-- 学业简历表格样式布局 -->
|
||||
<div class="education-resume-table">
|
||||
<!-- 表头 -->
|
||||
<div class="education-resume-header">
|
||||
<div class="table-col date-col">日期区间</div>
|
||||
<div class="table-col school-col">毕业院校</div>
|
||||
<div class="table-col major-col">专业</div>
|
||||
<div class="table-col degree-col">学历</div>
|
||||
<div class="table-col action-col">操作</div>
|
||||
</div>
|
||||
<!-- 表体 -->
|
||||
<div
|
||||
v-for="(item, index) in formData.academic"
|
||||
:key="index"
|
||||
class="education-resume-row"
|
||||
>
|
||||
<div class="table-col date-col">
|
||||
<el-date-picker
|
||||
v-model="item.education"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结业时间"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-col school-col">
|
||||
<el-input v-model="item.institute" placeholder="请输入毕业院校" />
|
||||
</div>
|
||||
<div class="table-col major-col">
|
||||
<el-input v-model="item.major" placeholder="请输入专业" />
|
||||
</div>
|
||||
<div class="table-col degree-col">
|
||||
<el-select v-model="item.educationLevel" placeholder="请选择学历">
|
||||
<el-option key="高中" label="高中" value="高中" />
|
||||
<el-option key="大专" label="大专" value="大专" />
|
||||
<el-option key="本科" label="本科" value="本科" />
|
||||
<el-option key="硕士" label="硕士" value="硕士" />
|
||||
<el-option key="博士" label="博士" value="博士" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="table-col action-col">
|
||||
<el-button type="danger" @click="handleDelete(index)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 表体 -->
|
||||
<div
|
||||
v-for="(item, index) in formData.educationResume"
|
||||
:key="index"
|
||||
class="education-resume-row"
|
||||
>
|
||||
<div class="table-col date-col">
|
||||
<el-date-picker
|
||||
v-model="item.education"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结业时间"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-col school-col">
|
||||
<el-input v-model="item.institute" placeholder="请输入毕业院校" />
|
||||
</div>
|
||||
<div class="table-col major-col">
|
||||
<el-input v-model="item.major" placeholder="请输入专业" />
|
||||
</div>
|
||||
<div class="table-col degree-col">
|
||||
<el-select v-model="item.educationLevel" placeholder="请选择学历">
|
||||
<el-option key="高中" label="高中" value="高中" />
|
||||
<el-option key="大专" label="大专" value="大专" />
|
||||
<el-option key="本科" label="本科" value="本科" />
|
||||
<el-option key="硕士" label="硕士" value="硕士" />
|
||||
<el-option key="博士" label="博士" value="博士" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="table-col action-col">
|
||||
<el-button type="danger" @click="handleDelete(index)">删除</el-button>
|
||||
</div>
|
||||
<!-- 添加按钮 -->
|
||||
<div class="education-resume-actions">
|
||||
<el-button type="primary" @click="AddEducationalBackground">添加教育经历</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 添加按钮 -->
|
||||
<div class="education-resume-actions">
|
||||
<el-button type="primary" @click="handleAddEducationResume">添加教育经历</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="学历证书" prop="educationLevel">
|
||||
<el-form-item label="学历证书" prop="AcademicResume">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:on-success="(res, file: File) => handleFileUploadSuccess(res, file, 'educationLevel')"
|
||||
:on-change="(file) => handleFileSelect(file, 'AcademicResume')"
|
||||
:before-upload="handleBeforeUpload"
|
||||
>
|
||||
<div v-if="formData.educationLevel" class="upload-preview">
|
||||
<el-link :href="formData.educationLevel" target="_blank" type="primary">
|
||||
查看学历证书
|
||||
</el-link>
|
||||
<el-button type="danger" size="small" @click.stop="removeFile('educationLevel')">
|
||||
<div v-if="formData.AcademicResume" class="upload-preview">
|
||||
<span>{{ formData.AcademicResume.name }}</span>
|
||||
<el-button type="danger" size="small" @click.stop="removeFile('AcademicResume')">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button v-else size="small" type="primary">点击上传</el-button>
|
||||
<el-button v-else size="small" type="primary">点击选择文件</el-button>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="劳动合同" prop="contractResume">
|
||||
<el-form-item label="劳动合同" prop="contract">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:on-success="(res, file: File) => handleFileUploadSuccess(res, file, 'contractResume')"
|
||||
:on-change="(file) => handleFileSelect(file, 'contract')"
|
||||
:before-upload="handleBeforeUpload"
|
||||
>
|
||||
<div v-if="formData.contractResume" class="upload-preview">
|
||||
<el-link :href="formData.contractResume" target="_blank" type="primary">
|
||||
查看劳动合同
|
||||
</el-link>
|
||||
<el-button type="danger" size="small" @click.stop="removeFile('contractResume')">
|
||||
<div v-if="formData.contract" class="upload-preview">
|
||||
<span>{{ formData.contract.name }}</span>
|
||||
<el-button type="danger" size="small" @click.stop="removeFile('contract')">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button v-else size="small" type="primary">点击上传</el-button>
|
||||
<el-button v-else size="small" type="primary">点击选择文件</el-button>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="入职申请表" prop="hiredResume">
|
||||
<el-form-item label="入职申请表" prop="ApplicationForm">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:on-success="
|
||||
(res: any, file: File) => handleFileUploadSuccess(res, file, 'hiredResume')
|
||||
"
|
||||
:on-change="(file) => handleFileSelect(file, 'ApplicationForm')"
|
||||
:before-upload="handleBeforeUpload"
|
||||
>
|
||||
<div v-if="formData.hiredResume" class="upload-preview">
|
||||
<el-link :href="formData.hiredResume" target="_blank" type="primary">
|
||||
查看入职申请表
|
||||
</el-link>
|
||||
<el-button type="danger" size="small" @click.stop="removeFile('hiredResume')">
|
||||
<div v-if="formData.ApplicationForm" class="upload-preview">
|
||||
<span>{{ formData.ApplicationForm.name }}</span>
|
||||
<el-button type="danger" size="small" @click.stop="removeFile('ApplicationForm')">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button v-else size="small" type="primary">点击上传</el-button>
|
||||
<el-button v-else size="small" type="primary">点击选择文件</el-button>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -407,12 +372,11 @@ import { useDebounceFn } from "@vueuse/core";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
// ==================== 3. 类型定义 ====================
|
||||
import type { UserForm, UserPageQuery, UserPageVO } from "@/api/system/user-api";
|
||||
import type { UserPageVO } from "@/api/system/user-api";
|
||||
// ==================== 4. API 服务 ====================
|
||||
import UserAPI from "@/api/system/user-api";
|
||||
import DeptAPI from "@/api/system/dept-api";
|
||||
import RoleAPI from "@/api/system/role-api";
|
||||
import fileApi from "@/api/file-api";
|
||||
|
||||
// ==================== 5. Store ====================
|
||||
import { useAppStore } from "@/store/modules/app-store";
|
||||
@@ -422,11 +386,17 @@ import { useUserStore } from "@/store";
|
||||
import { DeviceEnum } from "@/enums/settings/device-enum";
|
||||
|
||||
// ==================== 7. Composables ====================
|
||||
import { useAiAction, useTableSelection } from "@/composables";
|
||||
import { useTableSelection } from "@/composables";
|
||||
|
||||
// ==================== 8. 组件 ====================
|
||||
import DeptTree from "./components/DeptTree.vue";
|
||||
import UserImport from "./components/UserImport.vue";
|
||||
import {
|
||||
UserCreateUser,
|
||||
UserEditorialStaff,
|
||||
UserPersonnelList,
|
||||
} from "@/api/calibration/personnelManagement";
|
||||
import { formatJsonString, convertFilePathsToObject } from "@/utils/auxiliaryFunction";
|
||||
|
||||
// ==================== 组件配置 ====================
|
||||
defineOptions({
|
||||
@@ -445,13 +415,15 @@ const queryFormRef = ref();
|
||||
const userFormRef = ref();
|
||||
|
||||
// 列表查询参数
|
||||
const queryParams = reactive<UserPageQuery>({
|
||||
const queryParams = reactive<any>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
department: undefined,
|
||||
username: "",
|
||||
});
|
||||
|
||||
// 列表数据
|
||||
const pageData = ref<UserPageVO[]>();
|
||||
const pageData = ref<any[]>([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
|
||||
@@ -462,18 +434,30 @@ const dialog = reactive({
|
||||
});
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive<UserForm>({
|
||||
educationResume: [
|
||||
const formData = reactive<any>({
|
||||
account: "",
|
||||
username: "",
|
||||
password: "",
|
||||
nation: "",
|
||||
IdCard: "",
|
||||
department: "",
|
||||
mobilePhone: "",
|
||||
position: "",
|
||||
team: "",
|
||||
Dateofjoining: "",
|
||||
Confirmationtime: "",
|
||||
Practicingcertificatetime: "",
|
||||
AcademicResume: undefined,
|
||||
academic: [
|
||||
{
|
||||
education: [],
|
||||
// 毕业院校
|
||||
institute: "",
|
||||
// 专业
|
||||
major: "",
|
||||
// 学历
|
||||
educationLevel: "",
|
||||
},
|
||||
],
|
||||
contract: undefined,
|
||||
ApplicationForm: undefined,
|
||||
});
|
||||
|
||||
// 下拉选项数据
|
||||
@@ -500,68 +484,120 @@ const rules = reactive({
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
nickname: [
|
||||
account: [
|
||||
{
|
||||
required: true,
|
||||
message: "用户昵称不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
deptId: [
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
message: "所属部门不能为空",
|
||||
message: "密码不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
roleIds: [
|
||||
nation: [
|
||||
{
|
||||
required: true,
|
||||
message: "用户角色不能为空",
|
||||
message: "民族不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
email: [
|
||||
IdCard: [
|
||||
{
|
||||
pattern: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/,
|
||||
message: "请输入正确的邮箱地址",
|
||||
required: true,
|
||||
message: "身份证不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
mobile: [
|
||||
department: [
|
||||
{
|
||||
required: true,
|
||||
message: "归属部门不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
mobilePhone: [
|
||||
{
|
||||
required: true,
|
||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||
message: "请输入正确的手机号码",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
educationResume: [
|
||||
position: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入教育经历",
|
||||
message: "岗位不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
team: [
|
||||
{
|
||||
required: true,
|
||||
message: "所属团队不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
Dateofjoining: [
|
||||
{
|
||||
required: true,
|
||||
message: "入职时间不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
academic: [
|
||||
{
|
||||
required: true,
|
||||
message: "学业简历不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
AcademicResume: [
|
||||
{
|
||||
required: true,
|
||||
message: "学历证明不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
|
||||
contract: [
|
||||
{
|
||||
required: true,
|
||||
message: "合同不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
ApplicationForm: [
|
||||
{
|
||||
required: true,
|
||||
message: "入职申请表不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// ==================== 数据加载 ====================
|
||||
|
||||
/**
|
||||
* 获取用户列表数据
|
||||
* 获取列表数据
|
||||
*/
|
||||
async function fetchUserList(): Promise<void> {
|
||||
const fetchUserList = useDebounceFn(async () => {
|
||||
if (!queryParams.department) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await UserAPI.getPage(queryParams);
|
||||
pageData.value = data.list;
|
||||
total.value = data.total;
|
||||
const res: any = await UserPersonnelList(queryParams);
|
||||
pageData.value = res.data;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
ElMessage.error("获取用户列表失败");
|
||||
console.error("获取用户列表失败:", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ==================== 表格选择 ====================
|
||||
const { selectedIds, hasSelection, handleSelectionChange } = useTableSelection<UserPageVO>();
|
||||
@@ -581,8 +617,7 @@ function handleQuery(): Promise<void> {
|
||||
*/
|
||||
function handleResetQuery(): void {
|
||||
queryFormRef.value.resetFields();
|
||||
queryParams.deptId = undefined;
|
||||
queryParams.createTime = undefined;
|
||||
queryParams.username = "";
|
||||
handleQuery();
|
||||
}
|
||||
// ==================== 用户操作 ====================
|
||||
@@ -591,25 +626,25 @@ function handleResetQuery(): void {
|
||||
* 重置用户密码
|
||||
* @param row 用户数据
|
||||
*/
|
||||
function handleResetPassword(row: UserPageVO): void {
|
||||
ElMessageBox.prompt(`请输入用户【${row.username}】的新密码`, "重置密码", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
inputPattern: /.{6,}/,
|
||||
inputErrorMessage: "密码至少需要6位字符",
|
||||
})
|
||||
.then(({ value }) => {
|
||||
return UserAPI.resetPassword(row.id, value);
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success("密码重置成功");
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error !== "cancel") {
|
||||
ElMessage.error("密码重置失败");
|
||||
}
|
||||
});
|
||||
}
|
||||
// function handleResetPassword(row: UserPageVO): void {
|
||||
// ElMessageBox.prompt(`请输入用户【${row.username}】的新密码`, "重置密码", {
|
||||
// confirmButtonText: "确定",
|
||||
// cancelButtonText: "取消",
|
||||
// inputPattern: /.{6,}/,
|
||||
// inputErrorMessage: "密码至少需要6位字符",
|
||||
// })
|
||||
// .then(({ value }) => {
|
||||
// return UserAPI.resetPassword(row.id, value);
|
||||
// })
|
||||
// .then(() => {
|
||||
// ElMessage.success("密码重置成功");
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// if (error !== "cancel") {
|
||||
// ElMessage.error("密码重置失败");
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// ==================== 弹窗操作 ====================
|
||||
|
||||
@@ -617,7 +652,7 @@ function handleResetPassword(row: UserPageVO): void {
|
||||
* 打开用户表单弹窗
|
||||
* @param id 用户ID(编辑时传入)
|
||||
*/
|
||||
async function handleOpenDialog(id?: string): Promise<void> {
|
||||
async function handleOpenDialog(data?: any): Promise<void> {
|
||||
dialog.visible = true;
|
||||
|
||||
// 并行加载下拉选项数据
|
||||
@@ -632,10 +667,15 @@ async function handleOpenDialog(id?: string): Promise<void> {
|
||||
}
|
||||
|
||||
// 编辑:加载用户数据
|
||||
if (id) {
|
||||
if (data?.id) {
|
||||
dialog.title = "修改用户";
|
||||
try {
|
||||
const data = await UserAPI.getFormData(id);
|
||||
data.academic = parseJsonToArray(data.academic);
|
||||
|
||||
data.AcademicResume = convertFilePathsToObject(JSON.parse(data.AcademicResume))[0];
|
||||
data.contract = convertFilePathsToObject(JSON.parse(data.contract))[0];
|
||||
data.ApplicationForm = convertFilePathsToObject(JSON.parse(data.ApplicationForm))[0];
|
||||
console.log(data);
|
||||
Object.assign(formData, data);
|
||||
} catch (error) {
|
||||
ElMessage.error("加载用户数据失败");
|
||||
@@ -647,6 +687,17 @@ async function handleOpenDialog(id?: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// 将 JSON 字符串转换为数组对象
|
||||
function parseJsonToArray(jsonString: string): any[] {
|
||||
try {
|
||||
const result = JSON.parse(jsonString);
|
||||
return Array.isArray(result) ? result : [];
|
||||
} catch (error) {
|
||||
console.error("JSON解析失败:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭用户表单弹窗
|
||||
*/
|
||||
@@ -671,10 +722,10 @@ const handleSubmit = useDebounceFn(async () => {
|
||||
|
||||
try {
|
||||
if (userId) {
|
||||
await UserAPI.update(userId, formData);
|
||||
await UserEditorialStaff(formData);
|
||||
ElMessage.success("修改用户成功");
|
||||
} else {
|
||||
await UserAPI.create(formData);
|
||||
await UserCreateUser(formData);
|
||||
ElMessage.success("新增用户成功");
|
||||
}
|
||||
handleCloseDialog();
|
||||
@@ -687,11 +738,20 @@ const handleSubmit = useDebounceFn(async () => {
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
const AddEducationalBackground = () => {
|
||||
formData.academic.push({
|
||||
education: [],
|
||||
institute: "",
|
||||
major: "",
|
||||
educationLevel: "",
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
* @param id 用户ID(单个删除时传入)
|
||||
*/
|
||||
function handleDelete(id?: string): void {
|
||||
function handleDelete(id?: string | number): void {
|
||||
const userIds = id ? id : selectedIds.value.join(",");
|
||||
|
||||
if (!userIds) {
|
||||
@@ -737,6 +797,19 @@ function handleDelete(id?: string): void {
|
||||
|
||||
// ==================== 上传文件 ====================
|
||||
|
||||
/**
|
||||
* 处理文件选择
|
||||
* @param file 选择的文件对象
|
||||
* @param field 表单字段名
|
||||
*/
|
||||
function handleFileSelect(file: any, field: string): void {
|
||||
// 将文件对象保存到表单数据中
|
||||
formData[field] = file.raw; // file.raw 是实际的 File 对象
|
||||
console.log(formData[field]);
|
||||
userFormRef.value.clearValidate(field);
|
||||
ElMessage.success("文件选择成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传前的校验
|
||||
* @param file 上传的文件对象
|
||||
@@ -757,11 +830,11 @@ function handleBeforeUpload(file: File): boolean {
|
||||
* @param file 上传的文件对象
|
||||
* @param field 表单字段名
|
||||
*/
|
||||
function handleFileUploadSuccess(res: any, file: File, field: string): void {
|
||||
formData[field] = res.data.url;
|
||||
userFormRef.value.clearValidate(field);
|
||||
ElMessage.success("文件上传成功");
|
||||
}
|
||||
// function handleFileUploadSuccess(res: any, file: File, field: string): void {
|
||||
// formData[field] = res.data.url;
|
||||
// userFormRef.value.clearValidate(field);
|
||||
// ElMessage.success("文件上传成功");
|
||||
// }
|
||||
|
||||
/**
|
||||
* 删除已上传的文件
|
||||
@@ -826,11 +899,18 @@ async function handleExport(): Promise<void> {
|
||||
onMounted(() => {
|
||||
handleQuery();
|
||||
});
|
||||
watch(
|
||||
() => queryParams.department,
|
||||
() => {
|
||||
fetchUserList();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<!-- 学业简历表格样式 -->
|
||||
<style scoped>
|
||||
.education-resume-table {
|
||||
flex: 1;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -44,13 +44,21 @@ export default defineConfig(({ mode }: ConfigEnv) => {
|
||||
port: +(env.VITE_APP_PORT as string),
|
||||
open: true,
|
||||
proxy: {
|
||||
"/dev-api/api2": {
|
||||
changeOrigin: true,
|
||||
target: "http://8.137.99.82:8006",
|
||||
rewrite: (path: string) => {
|
||||
return path.replace(/^\/dev-api\/api2/, "");
|
||||
},
|
||||
},
|
||||
// 代理 /dev-api 的请求
|
||||
[env.VITE_APP_BASE_API as string]: {
|
||||
changeOrigin: true,
|
||||
// 代理目标地址:https://api.youlai.tech
|
||||
target: env.VITE_APP_API_URL as string,
|
||||
rewrite: (path: string) =>
|
||||
path.replace(new RegExp("^" + (env.VITE_APP_BASE_API as string)), ""),
|
||||
rewrite: (path: string) => {
|
||||
return path.replace(new RegExp("^" + (env.VITE_APP_BASE_API as string)), "");
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user