This commit is contained in:
Teo
2025-09-04 11:11:22 +08:00
36 changed files with 2619 additions and 578 deletions

View File

@ -69,7 +69,8 @@
"vue-types": "5.1.3",
"vue3-print-nb": "^0.1.4",
"vue3-scroll-seamless": "^1.0.6",
"vxe-table": "4.5.22"
"vxe-table": "4.5.22",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@eslint/js": "9.15.0",

Binary file not shown.

View File

@ -24,3 +24,11 @@ export const exportWord = (params) => {
method: 'post'
});
};
// 导出模版
export const exportExcel = (params) => {
return request({
url: '/design/collect/exportExcel',
method: 'post',
params: params
});
};

View File

@ -127,6 +127,17 @@ export const majorList = (params) => {
params: params
});
};
/**
* 获取人员列表
* @param query
*/
export const copyUserList = (params) => {
return request({
url: '/design/volumeCatalog/copyUserList',
method: 'get',
params: params
});
};
/**
* 获取二维码信息
* @param query

View File

@ -75,3 +75,11 @@ export const inventoryList = (id: any) => {
method: 'get'
});
};
//获取材料表信息
export const getMaterialInfo = (id: any) => {
return request({
url: '/materials/materials/listByFormCode/' + id,
method: 'get'
});
};

View File

@ -195,3 +195,35 @@ export const changeProject = (id: string | number) => {
method: 'get'
});
};
/**
* 打卡规则
* @param id
*/
export const attendanceRuleEdit = (data) => {
return request({
url: '/project/attendanceRule',
method: 'put',
data
});
};
/**
* 打卡规则
* @param id
*/
export const attendanceRuleAdd = (data) => {
return request({
url: '/project/attendanceRule',
method: 'post',
data
});
};
/**
* 获取规则
* @param id
*/
export const byProjectIdDetail = (id) => {
return request({
url: '/project/attendanceRule/byProjectId/' + id,
method: 'get'
});
};

View File

@ -71,7 +71,7 @@ export function getRoleList(deptId?: number | string): AxiosPromise<any[]> {
url: '/system/role/listNoPage',
method: 'get',
params: {
deptId
deptId,
}
});
}

View File

@ -5,8 +5,6 @@ import { ProjectTeamVO } from '@/api/project/projectTeam/types';
import useUserStore from '@/store/modules/user';
export const getProjectTeam = async () => {
const isPermission = useUserStore().permissions.some((item) => item == 'project:team:list');
console.log(useUserStore().permissions);
if (!isPermission && useUserStore().permissions[0] != '*:*:*') return;
const { id } = $cache.local.getJSON('selectedProject');

View File

@ -5,7 +5,7 @@
<el-form :model="queryForm" :inline="true">
<el-form-item label="版本号" prop="versions">
<el-select v-model="queryForm.versions" placeholder="选择版本号" @change="changeVersions">
<el-option v-for="item in options" :key="item.id" :label="item.versions" :value="item.id" />
<el-option v-for="item in options" :key="item.id" :label="item.versions" :value="item.versions" />
</el-select>
</el-form-item>
<el-form-item label="表名" prop="sheet">
@ -166,7 +166,8 @@ const getTableData = async () => {
const params = {
projectId: currentProject.value?.id,
sheet: queryForm.value.sheet,
versions: queryForm.value.versions
versions: queryForm.value.versions,
type: '1'
};
const res = await getTreeLimit(params);
loading.value = false;

View File

@ -210,7 +210,8 @@ const getListTable = async () => {
const res = await getTreeLimit({
projectId: currentProject.value?.id,
versions: form.value.versions,
sheet: form.value.sheet
sheet: form.value.sheet,
type: '0'
});
if (res.code == 200) {
tableData.value = res.data;

View File

@ -5,7 +5,7 @@
<el-form :model="queryForm" :inline="true">
<el-form-item label="版本号" prop="versions">
<el-select v-model="queryForm.versions" placeholder="选择版本号" @change="changeVersions">
<el-option v-for="item in options" :key="item.id" :label="item.versions" :value="item.id" />
<el-option v-for="item in options" :key="item.id" :label="item.versions" :value="item.versions" />
</el-select>
</el-form-item>
<el-form-item label="表名" prop="sheet">
@ -184,7 +184,8 @@ const getTableData = async () => {
const params = {
projectId: currentProject.value?.id,
sheet: queryForm.value.sheet,
versions: queryForm.value.versions
versions: queryForm.value.versions,
type: '0'
};
const res = await getTreeLimit(params);
loading.value = false;
@ -288,7 +289,7 @@ const handleExport = () => {
projectId: currentProject.value?.id,
sheet: queryForm.value.sheet
},
`限价一览表${queryForm.value.sheet}.xlsx`
`投标成本核算${queryForm.value.sheet}.xlsx`
);
};
// 审核

View File

@ -138,9 +138,14 @@
<el-table-column prop="useQuantity" label="剩余量" align="center">
<template #default="scope">
{{
(scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) == 0
(scope.row.quantity ? Number(scope.row.quantity) : 0) -
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
(scope.row.selectNum ? Number(scope.row.selectNum) : 0) ==
0
? ''
: (scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)
: (scope.row.quantity ? Number(scope.row.quantity) : 0) -
(scope.row.selectNum ? Number(scope.row.selectNum) : 0) -
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)
}}
</template>
</el-table-column>
@ -149,12 +154,16 @@
<el-table-column prop="price" label="总价" align="center">
<template #default="scope">
{{
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
((scope.row.quantity ? Number(scope.row.quantity) : 0) -
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
(scope.row.selectNum ? Number(scope.row.selectNum) : 0)) *
Number(scope.row.unitPrice) ==
0
? ''
: (
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
((scope.row.quantity ? Number(scope.row.quantity) : 0) -
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
(scope.row.selectNum ? Number(scope.row.selectNum) : 0)) *
Number(scope.row.unitPrice)
).toFixed(2)
}}
@ -328,18 +337,10 @@ const getVersionNums = async () => {
getSheetName();
} else {
treeForm.value.versions = '';
ElMessage({
message: '获取版本号失败',
type: 'warning'
});
}
}
} catch (error) {
console.log(error);
ElMessage({
message: '获取版本号失败',
type: 'warning'
});
}
};
//获取表名
@ -356,19 +357,11 @@ const getSheetName = async () => {
treeForm.value.sheet = res.data[0];
} else {
treeForm.value.sheet = '';
ElMessage({
message: '获取表名失败',
type: 'warning'
});
}
getTreeList();
}
} catch (error) {
console.log(error);
ElMessage({
message: '获取表名失败',
type: 'warning'
});
}
};
const handleSelection = (selection: any) => {

View File

@ -124,7 +124,7 @@ const loading = ref(false);
const options = ref<any[]>([]);
const sheets = ref<any[]>([]);
const tableData = ref<any[]>([]);
const isExpandAll = ref(false);
const isExpandAll = ref(true);
const reviewStatus = ref('');
const versionObj: any = ref({});
const versionMap = new Map();
@ -150,18 +150,10 @@ const getVersionNums = async () => {
getSheetName();
} else {
queryForm.value.versions = '';
ElMessage({
message: '获取版本号失败',
type: 'warning'
});
}
}
} catch (error) {
console.log(error);
ElMessage({
message: '获取版本号失败',
type: 'warning'
});
}
};
//选择版本号
@ -193,19 +185,11 @@ const getSheetName = async () => {
queryForm.value.sheet = res.data[0];
} else {
queryForm.value.sheet = '';
ElMessage({
message: '获取表名失败',
type: 'warning'
});
}
getTableData();
}
} catch (error) {
console.log(error);
ElMessage({
message: '获取表名失败',
type: 'warning'
});
}
};
//获取表格

View File

@ -65,7 +65,7 @@
>
<el-row :gutter="8" class="items-top">
<!-- 1. 专业选择核心统一所有角色的专业来源 -->
<el-col :span="3" class="mb-4 sm:mb-0 pl-4" style="margin-top:8px;">
<el-col :span="3" class="mb-4 sm:mb-0 pl-4" style="margin-top: 8px">
<el-form-item
:prop="`designers.${configIndex}.userMajor`"
:rules="[
@ -76,7 +76,8 @@
label-width="60px"
label="专业"
>
<el-select filterable
<el-select
filterable
v-model="form.designers[configIndex].userMajor"
placeholder="请选择专业"
class="w-full transition-all duration-300 border-gray-300"
@ -109,7 +110,8 @@
label="设计"
label-width="50px"
>
<el-select filterable
<el-select
filterable
v-model="person.userId"
placeholder="选择人员"
class="w-full transition-all duration-300 border-gray-300"
@ -165,7 +167,8 @@
label="校审"
label-width="50px"
>
<el-select filterable
<el-select
filterable
v-model="person.userId"
placeholder="选择人员"
class="w-full transition-all duration-300 border-gray-300"
@ -221,7 +224,8 @@
label="审定"
label-width="50px"
>
<el-select filterable
<el-select
filterable
v-model="person.userId"
placeholder="选择人员"
class="w-full transition-all duration-300 border-gray-300"
@ -277,7 +281,8 @@
label="审核"
label-width="50px"
>
<el-select filterable
<el-select
filterable
v-model="person.userId"
placeholder="选择人员"
class="w-full transition-all duration-300 border-gray-300"
@ -318,7 +323,7 @@
</el-col>
<!-- 操作列 -->
<el-col :span="2" class="text-right pr-4">
<el-col :span="2" class="pr-4 mt-2 text-right">
<el-button
type="text"
class="text-red-500 hover:text-red-700 transition-colors"

View File

@ -6,7 +6,7 @@
<el-card v-if="index < 3" shadow="always">
<el-form :model="state.queryForm" :inline="true">
<el-form-item label="版本号" prop="versions">
<el-select v-model="state.queryForm.versions" placeholder="选择版本号">
<el-select v-model="state.queryForm.versions" placeholder="选择版本号" @change="handleChangeVersion">
<el-option v-for="item in state.options" :key="item.versions" :label="item.versions" :value="item.versions" />
</el-select>
</el-form-item>
@ -16,7 +16,7 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="openTable(index)">{{ isExpandAll ? '一键收起' : '一键展开' }}</el-button>
<el-button type="primary" @click="openTable(index)">{{ isExpandAll ? '一键收起' : '一键展开' }}</el-button>
</el-form-item>
<el-form-item>
<el-button type="success" @click="downloadTemplate(1)">下载模板</el-button>
@ -62,15 +62,10 @@
</el-upload>
</el-form-item>
<el-form-item v-if="state.versionsData.status == 'draft'">
<el-button type="primary" con="edit" @click="clickApprovalSheet()">审核</el-button>
<el-button type="primary" con="edit" @click="clickApprovalSheet()">审核</el-button>
</el-form-item>
<el-form-item v-if="state.versionsData.status == 'waiting' || state.versionsData.status == 'finish'">
<el-button
icon="view"
@click="lookApprovalFlow()"
type="warning"
>查看流程</el-button
>
<el-button icon="view" @click="lookApprovalFlow()" type="warning">查看流程</el-button>
</el-form-item>
</el-form>
</el-card>
@ -171,6 +166,7 @@ const handleTabChange = (tab) => {
onMounted(async () => {
await getVersionNums();
});
// 获取版本号
async function getVersionNums(isSheet = true) {
try {
@ -247,10 +243,10 @@ async function handleSheetName() {
// 获取列表
async function handleQueryList(isSheet = true) {
if (isSheet && !state.queryForm.sheet) {
console.warn('表名不存在,无法获取列表数据');
return;
}
// if (isSheet && !state.queryForm) {
// console.warn('表名不存在,无法获取列表数据');
// return;
// }
try {
state.loading.list = true;
@ -308,12 +304,12 @@ function handleChange(sheet) {
function handleChangeVersion(versions) {
state.queryForm.versions = versions;
state.versionsData = state.options.find((e) => e.versions == versions);
console.log('state.versionsData', state.versionsData);
// console.log('state.versionsData', state.versionsData);
state.sheets = [];
handleQueryList();
}
// 在 openTable 方法中通过索引获取对应的表格实例
function openTable( index) {
function openTable(index) {
isExpandAll.value = !isExpandAll.value;
nextTick(() => {
// 通过索引获取当前标签页的表格实例
@ -358,12 +354,12 @@ function lookApprovalFlow(row) {
const downloadTemplate = (type) => {
// 导出模版文件
try {
let linkurl = '';
let linkurl = '';
let name = '';
if (type==1) {
if (type == 1) {
linkurl = '/billOfQuantities.xlsx';
name = '工程量清单模板.xlsx';
}else{
} else {
linkurl = '/materialsEquipment.xlsx';
name = '物资设备清单模板.xlsx';
}

View File

@ -16,7 +16,7 @@
<!-- 表单区域 -->
<el-card class="rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md overflow-hidden">
<div class="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-100">
<h3 class="text-lg font-semibold text-gray-800">标工程清单</h3>
<h3 class="text-lg font-semibold text-gray-800">标工程清单</h3>
</div>
<div class="p-6">
<el-form

View File

@ -49,7 +49,32 @@
<!-- 资料文件区域 -->
<div class="mb-8">
<div class="flex items-center justify-between mb-5">
<h3 class="text-lg font-semibold text-blue-700">资料文件清单</h3>
<div style="display: flex; align-items: center">
<h3 class="text-lg font-semibold text-blue-700" style="margin-right: 20px">资料文件清单</h3>
<el-upload
class="upload-excel"
action="#"
v-if="!form.id || form.status == 'draft'"
ref="uploadRef"
:auto-upload="false"
:on-change="importTemplate"
:show-file-list="false"
:accept="'.xlsx,.xls'"
:limit="1"
>
<el-button type="primary" icon="Upload">导入文件</el-button>
</el-upload>
<el-button
v-if="!form.id || form.status == 'draft'"
type="primary"
style="margin-left: 20px"
icon="Download"
@click="exportTemplate"
class="transition-all hover:bg-blue-600"
>
导出模版
</el-button>
</div>
<el-button type="primary" size="small" @click="addDocumentItem" v-if="!disabledAll" icon="Plus" class="transition-all hover:bg-blue-600">
添加资料
</el-button>
@ -180,11 +205,11 @@ import { ref, reactive, computed, onMounted, onUnmounted, watch, getCurrentInsta
import { useUserStoreHook } from '@/store/modules/user';
import { ElMessage, ElLoading, FormRules } from 'element-plus';
import { systemUserList } from '@/api/design/appointment';
import { collectBatch, byProjectId, exportWord } from '@/api/design/received';
import { collectBatch, byProjectId, exportWord, exportExcel } from '@/api/design/received';
import { getUser } from '@/api/system/user';
import type { ComponentInternalInstance, ElFormInstance } from 'element-plus';
import { getInfo } from '@/api/login';
import * as XLSX from 'xlsx';
// 全局实例与状态管理
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userStore = useUserStoreHook();
@ -200,7 +225,7 @@ const documentsFormRef = ref<ElFormInstance>();
const userList = ref<any[]>([]);
const userMap = new Map<string, string>(); // 存储用户ID与昵称映射
const disabledAll = ref(false); // 表单是否全部禁用
const uploadRef = ref<any>();
// 表单核心数据
const form = reactive({
projectId: currentProject.value?.id,
@ -445,7 +470,66 @@ const onLoad = async () => {
console.error('文件导出错误:', error);
}
};
const exportTemplate = async () => {
// 导出模版
proxy?.download(
'design/collect/exportExcel',
{
deptId: userStore.deptId
},
`收资清单表格.xlsx`
);
};
const importTemplate = async (files, fileList) => {
// 导入表格数据
const file = fileList[0].raw; // 获取原始文件对象
const reader = new FileReader();
let obj = {
id: '编码',
name: '人员',
fliename: '目录名',
remark: '备注'
};
reader.onload = (e) => {
try {
// 读取文件内容
const data = new Uint8Array(e.target.result);
// 解析Excel
const workbook = XLSX.read(data, { type: 'array' });
// 获取第一个工作表名称
const firstSheetName = workbook.SheetNames[0];
// 获取第一个工作表内容
const worksheet = workbook.Sheets[firstSheetName];
// 转换为JSON格式
const jsonData = XLSX.utils.sheet_to_json(worksheet);
if (jsonData.length === 0) {
ElMessage.info('Excel文件中没有数据');
return;
}
let arr = [];
// 判断form.documents 是否对象
jsonData.forEach((item, index) => {
if (item[obj.id]) {
arr.push({
id: Date.now() + index,
catalogueName: item[obj.fliename],
remark: item[obj.remark],
userId: item[obj.id]
});
}
});
form.documents = arr;
uploadRef.value.clearFiles();
console.log(arr);
} catch (err) {}
};
// 以ArrayBuffer方式读取文件
reader.readAsArrayBuffer(file);
};
/** 页面挂载初始化 */
onMounted(() => {
// 先获取当前用户信息,再获取部门用户列表,最后回显表单数据

View File

@ -153,6 +153,11 @@
<el-form-item v-if="uploadForm.type == '3'" label="蓝图" prop="fileIds">
<file-upload :fileType="['pdf']" :isShowTip="false" :fileSize="100" v-model="uploadForm.fileIds"></file-upload>
</el-form-item>
<el-form-item v-if="uploadForm.type == '3'" label="抄送人">
<el-select multiple filterable clearable v-model="form.userIds" placeholder="请选择抄送人">
<el-option :value="item.userId" v-for="item in userCoryList" :key="item.userId" :label="item.nickName + '-' + item.phonenumber" />
</el-select>
</el-form-item>
<el-form-item v-if="uploadForm.type == '1'" label="过程图纸" prop="cancellationIds">
<file-upload :fileType="['pdf']" :isShowTip="false" :fileSize="100" v-model="uploadForm.cancellationIds"></file-upload>
</el-form-item>
@ -255,7 +260,8 @@ import {
uploadVolumeFile,
majorList,
getVolumeCatafileList,
volumeFileList
volumeFileList,
copyUserList
} from '@/api/design/volumeCatalog';
import { VolumeCatalogVO } from '@/api/design/volumeCatalog/types';
import { useUserStoreHook } from '@/store/modules/user';
@ -281,6 +287,7 @@ const uploadOpinionVisible = ref(false);
const design = ref('');
const total = ref(0);
const dialogHistory = ref(false);
const userCoryList = ref([]);
const opinion = ref('');
const updateRow = ref({
opinion: []
@ -416,7 +423,13 @@ const cancel = () => {
reset();
dialog.visible = false;
};
// 获取人员列表
const getDesignUserList = async () => {
const res = await copyUserList({ projectId: currentProject.value?.id, userType: 2 });
if (res.code === 200) {
userCoryList.value = res.data;
}
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
@ -546,7 +559,7 @@ const onSubmit = async () => {
type: uploadForm.type
};
try {
await uploadVolumeFile(obj);
await uploadVolumeFile({ ...obj, userIds: form.value.userIds });
proxy?.$modal.msgSuccess('文件上传成功');
uploadVisible.value = false;
await getList();
@ -656,6 +669,7 @@ const handleAuditInfo = (row) => {
// 审核图纸
};
onMounted(() => {
getDesignUserList();
getSpecialtyList();
getList();
});
@ -666,6 +680,7 @@ const listeningProject = watch(
(nid, oid) => {
queryParams.value.projectId = nid;
form.value.projectId = nid;
getDesignUserList();
getSpecialtyList();
getList();
}

View File

@ -81,7 +81,7 @@
<el-table v-loading="loading" :data="formalitiesAreConsolidatedList" @selection-change="handleSelectionChange" row-key="id" default-expand-all>
<el-table-column type="selection" width="55" align="center" />
<!-- <el-table-column label="手续办理清单模板父级" align="center" prop="formalitiesPname" /> -->
<el-table-column label="手续办理清单" align="center" prop="formalitiesName" />
<el-table-column label="手续办理清单" align="left" prop="formalitiesName" />
<el-table-column label="计划开始时间" align="center" prop="planTheStartTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.planTheStartTime, '{y}-{m}-{d}') }}</span>

View File

@ -1,13 +1,11 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
:leave-active-class="proxy?.animate.searchAnimate.leave">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="auto">
<el-form-item label="材料名称" prop="materialsName">
<el-input v-model="queryParams.materialsName" placeholder="请输入材料名称" clearable
@keyup.enter="handleQuery" />
<el-input v-model="queryParams.materialsName" placeholder="请输入材料名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="材料提供商" prop="companyId">
<el-select v-model="queryParams.companyId" clearable placeholder="全部">
@ -27,22 +25,20 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['materials:materials:add']"> 新增
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['materials:materials:add']"> 新增 </el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['materials:materials:edit']"
>修改
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
v-hasPermi="['materials:materials:edit']">修改
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['materials:materials:remove']"
>删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
v-hasPermi="['materials:materials:remove']">删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport"
v-hasPermi="['materials:materials:export']">导出 </el-button>
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['materials:materials:export']">导出 </el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@ -72,25 +68,28 @@
<el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="320">
<template #default="scope">
<el-space>
<el-button link type="primary" icon="View" @click="handleShowDrawer(scope.row)"
v-hasPermi="['materials:materials:query']">
<el-button link type="primary" icon="View" @click="handleShowDrawer(scope.row)" v-hasPermi="['materials:materials:query']">
详情
</el-button>
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)"
v-hasPermi="['materials:materials:edit']"> 修改 </el-button>
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)"
v-hasPermi="['materials:materials:remove']">
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['materials:materials:edit']"> 修改 </el-button>
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['materials:materials:remove']">
删除
</el-button>
<el-button v-hasPermi="['materials:materialsInventory:edit']" link type="primary" icon="Plus"
@click="handleAddMaterialsInventory(scope.row)"> 出入库 </el-button>
<el-button
v-hasPermi="['materials:materialsInventory:edit']"
link
type="primary"
icon="Plus"
@click="handleAddMaterialsInventory(scope.row)"
>
出入库
</el-button>
</el-space>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改材料名称对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
@ -121,12 +120,17 @@
<el-form-item label="材料文件" prop="fileOssIdMap">
<div :key="item.value" v-for="item in materials_file_type">
<h3>{{ item.label }}</h3>
<file-upload v-model="ossIdMap[item.value]" :limit="1" :file-size="50" :file-type="['pdf']"
<file-upload
v-model="ossIdMap[item.value]"
:limit="1"
:file-size="50"
:file-type="['pdf']"
@update:model-value="
(args) => {
handleOssUpdate(args, item.value);
}
" />
"
/>
</div>
</el-form-item>
</el-form>
@ -137,8 +141,7 @@
</div>
</template>
</el-dialog>
<materials-inventory-add-dialog :materials-id="currentMaterialsId" :project-id="currentProject.id" ref="dialogRef"
@submit="getList" />
<materials-inventory-add-dialog :materials-id="currentMaterialsId" :project-id="currentProject.id" ref="dialogRef" @submit="getList" />
<el-dialog title="材料详情" v-model="showDetailDrawer" width="700px">
<materials-detail-drawer :materials-id="currentMaterialsId" />
</el-dialog>

View File

@ -73,7 +73,10 @@
<el-row>
<el-col :span="12">
<el-form-item label="表单编号" prop="formCode">
<el-input v-model="form.formCode" placeholder="请输入表单编号" />
<!-- <el-input v-model="form.formCode" placeholder="请输入表单编号" /> -->
<el-select v-model="form.formCode" placeholder="请选择表单编号" @change="(value) => formCodeChange(value)">
<el-option v-for="item in options" :key="item.formCode" :label="item.formCode" :value="item.formCode" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
@ -123,11 +126,11 @@
<el-col :span="12">
<el-form-item
label="名称"
:prop="`itemList.${index}.name`"
:prop="`itemList.${index}.materialsId`"
:rules="[{ required: true, message: '名称不能为空', trigger: 'blur' }]"
>
<el-select v-model="item.inventoryId" placeholder="请选择名称" @change="(value) => getNameChange(value, index, item)">
<el-option v-for="opt in optionsName" :key="opt.id" :label="opt.materialsName" :value="opt.id" />
<el-select v-model="item.materialsId" placeholder="请选择名称" @change="(value) => getNameChange(value, index, item)">
<el-option v-for="opt in optionsName" :key="opt.id" :label="`${opt.materialsName}_${opt.createTime}`" :value="opt.id" />
</el-select>
</el-form-item>
</el-col>
@ -168,13 +171,9 @@
</el-col> -->
<el-col :span="12">
<el-form-item label="领取" :prop="`itemList.${index}.issuedQuantity`">
<el-input
v-model.number="item.issuedQuantity"
disabled
placeholder="请输入领取数量"
@input="handleIssuedChange(index)"
@blur="handleIssuedChange(index)"
/>
<el-select v-model="item.issuedQuantity" placeholder="请选择数量">
<el-option v-for="opt in item.outList" :key="opt.id" :label="opt.number" :value="opt.number" />
</el-select>
</el-form-item>
</el-col>
<!-- <el-col :span="12">
@ -247,7 +246,8 @@ import {
addMaterialIssue,
updateMaterialIssue,
inventoryList,
getMaterialName
getMaterialName,
getMaterialInfo
} from '@/api/materials/materialIssue';
import { MaterialIssueVO, MaterialIssueQuery, MaterialIssueForm } from '@/api/materials/materialIssue/types';
@ -307,6 +307,7 @@ const getInitFormData = () => {
itemList: [
{
id: undefined,
specification: undefined,
unit: undefined,
stockQuantity: undefined,
@ -314,7 +315,8 @@ const getInitFormData = () => {
remainingQuantity: undefined,
name: undefined, // 数量验收的名称
remark: undefined,
materialsId: undefined
materialsId: undefined,
outList: []
}
]
};
@ -365,7 +367,7 @@ const computeMaterialName = () => {
.map((item) => item.name.trim())
.filter((name, index, self) => self.indexOf(name) === index); // 去重(如需保留重复则删除这行)
form.value.materialName = validNames.join(',');
// form.value.materialName = validNames.join(',');
};
/** 查询物料领料单列表 */
@ -404,6 +406,7 @@ const getNameChange = (value, index, item) => {
item.unit = selected.weightId;
item.issuedQuantity = selected.number;
item.stockQuantity = Number(selected.inventoryNumber) || 0;
item.outList = selected.outList || [];
// calculateRemaining(index); // 计算剩余数量
}
};
@ -529,8 +532,27 @@ const handleAdd = () => {
dialog.title = '添加物料领料单';
// 新增时初始计算材料名称
computeMaterialName();
getFormData();
};
const options = ref([]);
//新增获取表单数据
const getFormData = async () => {
const res = await getMaterialInfo(currentProject.value.id);
if (res.code == 200) {
options.value = res.data;
}
};
const formCodeChange = (value) => {
console.log(value);
const selected = options.value.find((opt) => opt.formCode === value);
if (selected) {
form.value.materialName = selected.materialName;
form.value.orderingUnit = selected.orderingUnit;
form.value.supplierUnit = selected.supplierUnit;
optionsName.value = selected.materials;
}
};
/** 修改按钮操作 */
const handleUpdate = async (row?: MaterialIssueVO) => {
reset();
@ -597,6 +619,7 @@ const submitForm = () => {
remainingQuantity: Number(item.remainingQuantity)
}))
};
console.log('提交数据:', submitData);
if (form.value.id) {
await updateMaterialIssue(submitData);
@ -638,10 +661,13 @@ const addItem = () => {
// 删除数量验收条目
const removeItem = (index: number) => {
if (form.value.itemList.length > 1) {
console.log(111111);
console.log(itemWatchStopFns.value[index]);
// 停止该条目的监听
if (itemWatchStopFns.value[index]) {
itemWatchStopFns.value[index]();
}
// if (itemWatchStopFns.value[index]) {
// itemWatchStopFns.value[index]();
// }
form.value.itemList.splice(index, 1);
itemWatchStopFns.value.splice(index, 1);
// 删除后重新计算材料名称
@ -682,7 +708,7 @@ watch(
onMounted(() => {
getList();
getName();
// getName();
});
// 监听项目id刷新数据
@ -692,7 +718,7 @@ const listeningProject = watch(
queryParams.value.projectId = nid;
form.value.projectId = nid;
getList();
getName();
// getName();
}
);

View File

@ -42,7 +42,7 @@
<thead>
<tr>
<th colspan="2">领料单位</th>
<td class="th-bg" colspan="2">{{ formData.placeholder }}</td>
<td class="th-bg" colspan="2">{{ formData.issueUnit }}</td>
<th colspan="2">保管单位</th>
<td class="th-bg" colspan="2">{{ formData.storageUnit }}</td>
</tr>

View File

@ -30,6 +30,9 @@
>一键全部保存</el-button
>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="toggleExpandAll">{{ isExpandAll ? '一键收起' : '一键展开' }}</el-button>
</el-form-item>
</el-form>
<right-toolbar @queryTable="getMasterDataList"></right-toolbar>
</el-row>
@ -38,6 +41,7 @@
<el-table
:data="state.tableData"
v-loading="state.loading.list"
ref="tableRef"
stripe
style="width: 100%; margin-bottom: 20px; height: calc(100vh - 230px)"
row-key="id"
@ -264,6 +268,17 @@ const formRules = reactive({
],
compileDate: [{ required: true, message: '请选择编制日期', trigger: 'change' }]
});
// 展开状态
const isExpandAll = ref(false);
const tableRef = ref(null);
// 切换展开状态
const toggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value;
console.log(isExpandAll.value);
state.tableData.forEach((row) => {
tableRef.value.toggleRowExpansion(row, isExpandAll.value);
});
};
// 获取主表数据
async function getMasterDataList() {
try {

View File

@ -36,7 +36,6 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="monthPlanList">
<el-table-column type="index" label="序号" width="55" align="center" />
<el-table-column label="计划月份" align="center" prop="planMonth" />
@ -82,7 +81,7 @@
<el-form-item label="计划产值(元)" prop="planValue">
<el-input v-model="form.planValue" placeholder="请输入计划产值" type="number" />
</el-form-item>
<el-form-item label="计划月份(元)" prop="planMonth">
<el-form-item label="计划月份" prop="planMonth">
<el-date-picker v-model="form.planMonth" type="month" value-format="YYYY-MM" placeholder="请选择计划月份" />
</el-form-item>
<el-form-item label="产值类型" prop="valueType">

View File

@ -175,7 +175,7 @@ const getInfo = () => {
form.value = res.data as any;
console.log('🚀 ~ getInfo ~ form.value:', form.value[0].projectId);
form.value[0].mid = form.value[0].id;
form.value[0].id = form.value[0].projectId + '_' + form.value[0].planMonth;
loading.value = false;
buttonLoading.value = false;
@ -231,13 +231,15 @@ const approvalVerifyOpen = async () => {
// 图纸上传成功之后 开始提交
const submit = async (status, data) => {
form.value = data;
console.log(form.value);
if (status === 'draft') {
buttonLoading.value = false;
proxy?.$modal.msgSuccess('暂存成功');
proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
} else {
const res = await isSubmit(data[1]?.id);
const res = await isSubmit(data[0]?.mid);
if (!res.data) {
proxy?.$modal.msgError('三种计划产值必须填写');

View File

@ -78,6 +78,7 @@
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 地块表单弹窗 -->
<el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="landBlockFormRef" :model="form" :rules="rules" label-width="100px">
@ -107,20 +108,22 @@
</div>
</template>
</el-dialog>
<!-- 关联方阵弹窗核心修改区域 -->
<!-- 关联方阵弹窗核心修复区域 -->
<el-dialog draggable :title="dialogMatrix.title" v-model="dialogMatrix.visible" width="900px" append-to-body>
<el-button type="primary" plain icon="Plus" @click="addUnitBoItem" style="margin-bottom: 15px">添加</el-button>
<!-- 方阵表单绑定unitBoList的索引实现动态校验 -->
<!-- 修复1表单模型绑定formM根模型确保嵌套字段校验生效 -->
<el-form ref="landBlockFormMatrixRef" :model="formM" label-width="100px">
<el-row v-for="(item, i) of unitBoList" :key="i" class="mb-4">
<!-- 方阵选择必填校验 -->
<!-- 修复2循环formM.unitBoList而非独立ref保证数据双向绑定 -->
<el-row v-for="(item, i) of formM.unitBoList" :key="i" class="mb-4">
<!-- 方阵选择修复校验规则移除min:2改为min:1适配单层级选择 -->
<el-col :span="8">
<el-form-item
label="方阵"
:prop="`unitBoList[${i}].unitProjectId`"
:rules="[
{ required: true, message: '请选择方阵', trigger: 'change' },
{ type: 'array', min: 2, message: '请选择完整的方阵层级', trigger: 'change' }
{ type: 'array', min: 1, message: '请选择完整的方阵', trigger: 'change' }
]"
>
<el-cascader
@ -133,7 +136,7 @@
/>
</el-form-item>
</el-col>
<!-- 所属工区必填校验 -->
<!-- 所属工区保留原有规则 -->
<el-col :span="8">
<el-form-item
label="所属工区"
@ -143,7 +146,7 @@
<el-input v-model="item.unitProjectArea" placeholder="请输入所属工区" />
</el-form-item>
</el-col>
<!-- 方阵状态必填校验 -->
<!-- 方阵状态保留原有规则 -->
<el-col :span="6">
<el-form-item
label="方阵状态"
@ -153,8 +156,15 @@
<el-input v-model="item.unitProjectStatus" placeholder="请输入方阵状态" />
</el-form-item>
</el-col>
<!-- 删除按钮禁用逻辑优化至少保留1项 -->
<el-col :span="1" style="margin-left: 15px; display: flex; align-items: flex-end">
<el-button style="margin-bottom: 18px" type="danger" icon="Delete" @click="removeUnitBoItem(i)"></el-button>
<el-button
style="margin-bottom: 18px"
type="danger"
icon="Delete"
@click="removeUnitBoItem(i)"
:disabled="formM.unitBoList.length <= 1"
></el-button>
</el-col>
</el-row>
</el-form>
@ -181,21 +191,35 @@ import {
} from '@/api/system/landTransfer/landBlock';
import { LandBlockVO, LandBlockQuery, LandBlockForm } from '@/api/system/landTransfer/landBlock/types';
import { useUserStoreHook } from '@/store/modules/user';
import { getCurrentInstance, ComponentInternalInstance, onMounted, onUnmounted, watch } from 'vue';
import { ElFormInstance, ElMessage } from 'element-plus';
import { getCurrentInstance, ComponentInternalInstance, onMounted, onUnmounted, watch, reactive, ref, toRefs, computed } from 'vue';
import { ElFormInstance } from 'element-plus';
// 类型定义补充避免any
// 类型定义补充
interface DialogOption {
visible: boolean;
title: string;
}
// 方阵级联选择器选项类型
interface FangzhenOption {
matrixId: string | number;
name: string;
children?: FangzhenOption[];
}
// 方阵表单项类型
interface UnitBoItem {
unitProjectArea: string;
unitProjectStatus: string;
unitProjectId: (string | number)[]; // 级联选择值(数组)
}
// 方阵表单根模型类型关键显式声明unitBoList
interface MatrixForm {
landId?: string | number; // 关联的地块ID
unitBoList: UnitBoItem[]; // 方阵列表
}
// 基础实例与Store
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userStore = useUserStoreHook();
@ -203,14 +227,17 @@ const currentProject = computed(() => userStore.selectedProject);
// 响应式数据
const landBlockList = ref<LandBlockVO[]>([]);
const fangzhenList = ref<any[]>([]); // 方阵列表(实际项目建议定义具体类型)
const fangzhenList = ref<FangzhenOption[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const uploadRef = ref<any>(null); // upload组件ref
const uploadRef = ref<any>(null);
// 方阵表单数据(核心修改:初始值为空,避免默认填充无效数据
const unitBoList = ref<UnitBoItem[]>([{ unitProjectArea: '', unitProjectStatus: '', unitProjectId: [] }]);
// 方阵表单模型(核心修使用reactive并显式声明结构
const formM = reactive<MatrixForm>({
landId: undefined,
unitBoList: [{ unitProjectArea: '', unitProjectStatus: '', unitProjectId: [] }]
});
// 表格选择相关
const ids = ref<Array<string | number>>([]);
@ -243,7 +270,6 @@ const initFormData: LandBlockForm = {
// 核心数据(含表单规则)
const data = reactive({
form: { ...initFormData },
formM: { landId: undefined }, // 方阵关联表单仅存地块ID
queryParams: {
pageNum: 1,
pageSize: 10,
@ -256,7 +282,7 @@ const data = reactive({
farmerCount: undefined,
params: {}
},
// 地块表单规则(原有规则保留)
// 地块表单规则
rules: {
id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }],
@ -265,7 +291,7 @@ const data = reactive({
}
});
const { queryParams, form, rules, formM } = toRefs(data);
const { queryParams, form, rules } = toRefs(data);
/** 查询地块列表 */
const getList = async () => {
@ -381,10 +407,10 @@ const getfangzhenList = async () => {
loading.value = true;
try {
const res = await subMatrix(currentProject.value.id);
// 处理方阵数据(级联选择需父子结构,此处保留原有逻辑
// 处理方阵数据(级联选择需父子结构)
res.data.forEach((item: any) => {
item.children?.forEach((item2: any) => {
item2.matrixId = `${item2.name}_${item2.matrixId}`; // 拼接名称+ID便于后续拆分
item2.matrixId = `${item2.name}_${item2.matrixId}`;
});
});
fangzhenList.value = res.data;
@ -395,68 +421,75 @@ const getfangzhenList = async () => {
}
};
/** 关联方阵(核心修改:打开弹窗前强制重置表单) */
/** 关联方阵 */
const handleView = async (row: LandBlockVO) => {
if (!row?.id) return proxy?.$modal.msgWarning('请选择有效的地块');
// 1. 重置方阵表单(清空历史数据)
// 重置方阵表单
resetMatrix();
// 2. 绑定当前地块ID
formM.value.landId = row.id;
// 3. 打开弹窗
// 绑定当前地块ID
formM.landId = row.id;
// 打开弹窗
dialogMatrix.visible = true;
dialogMatrix.title = `关联方阵(地块:${row.landName || row.landCode}`;
};
/** 新增方阵表单项 */
const addUnitBoItem = () => {
unitBoList.value.push({
formM.unitBoList.push({
unitProjectArea: '',
unitProjectStatus: '',
unitProjectId: [] // 级联选择初始为空数组
unitProjectId: []
});
// 重置校验状态
landBlockFormMatrixRef.value?.clearValidate();
};
/** 删除方阵表单项 */
const removeUnitBoItem = (index: number) => {
if (unitBoList.value.length <= 1) {
if (formM.unitBoList.length <= 1) {
return proxy?.$modal.msgWarning('至少保留一项方阵配置');
}
unitBoList.value.splice(index, 1);
// 重置表单校验状态(避免删除后残留校验提示)
formM.unitBoList.splice(index, 1);
landBlockFormMatrixRef.value?.clearValidate();
};
/** 提交方阵关联表单 */
/** 提交方阵关联表单(核心修复:数据处理逻辑) */
const submitFormMatrix = () => {
landBlockFormMatrixRef.value?.validate(async (valid: boolean) => {
if (!valid) return;
if (!formM.value.landId) return proxy?.$modal.msgWarning('地块ID异常请重新选择地块');
if (!formM.landId) return proxy?.$modal.msgWarning('地块ID异常请重新选择地块');
try {
// 处理方阵数据(拆分名称+ID
const unitBoListParams = unitBoList.value.map((item) => {
const [unitProjectName, unitProjectId] = item.unitProjectId[1]?.split('_') || [];
if (!unitProjectId) throw new Error('方阵ID解析失败请重新选择方阵');
// 处理方阵数据(修复ID拆分逻辑
const unitBoListParams = formM.unitBoList.map((item) => {
// 取级联选择的最后一层值
const lastLevelValue = item.unitProjectId[item.unitProjectId.length - 1];
if (!lastLevelValue) throw new Error('请选择完整的方阵');
// 拆分名称和ID
const [unitProjectName, unitProjectId] = String(lastLevelValue).split('_');
if (!unitProjectId) throw new Error('方阵ID解析失败请重新选择');
return {
unitProjectArea: item.unitProjectArea.trim(),
unitProjectStatus: item.unitProjectStatus.trim(),
unitProjectId: unitProjectId, // 纯ID后端需要
unitProjectName: unitProjectName // 名称(可选,用于显示)
unitProjectId: unitProjectId,
unitProjectName: unitProjectName
};
});
// 调用关联接口
const res = await LandUnit({
landId: formM.value.landId,
landId: formM.landId,
unitBoList: unitBoListParams
});
if (res.code === 200) {
proxy?.$modal.msgSuccess('关联方阵成功');
dialogMatrix.visible = false;
await getList(); // 刷新地块列表
await getList();
} else {
proxy?.$modal.msgError(res.msg || '关联失败');
}
@ -472,13 +505,10 @@ const cancelMatrix = () => {
dialogMatrix.visible = false;
};
/** 方阵表单重置(核心修改:清空所有数据+重置校验) */
/** 方阵表单重置 */
const resetMatrix = () => {
// 1. 清空地块ID
formM.value.landId = undefined;
// 2. 重置方阵列表(仅保留一个空项)
unitBoList.value = [{ unitProjectArea: '', unitProjectStatus: '', unitProjectId: [] }];
// 3. 重置表单校验状态
formM.landId = undefined;
formM.unitBoList = [{ unitProjectArea: '', unitProjectStatus: '', unitProjectId: [] }];
if (landBlockFormMatrixRef.value) {
landBlockFormMatrixRef.value.resetFields();
landBlockFormMatrixRef.value.clearValidate();
@ -491,11 +521,11 @@ const listeningProject = watch(
(newId) => {
if (newId) {
queryParams.value.projectId = newId;
getfangzhenList(); // 刷新方阵列表
getList(); // 刷新地块列表
getfangzhenList();
getList();
}
},
{ immediate: true } // 初始加载时执行一次
{ immediate: true }
);
/** 导入Excel */
@ -524,36 +554,34 @@ const handleImport = (options: { file: File }) => {
loading.value = false;
});
};
/** 导出模板 */
const exportFile = () => {
// 导出模版文件
try {
// 创建a标签
const link = document.createElement('a');
// 设置PDF文件路径 - 相对于public目录
link.href = '/dikuai.xlsx';
// 设置下载后的文件名
link.download = '地块信息导入模版.xlsx';
// 触发点击
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
} catch (error) {
alert('下载失败,请重试');
}
};
/** 下载导入模板 */
const downloadTemplate = () => {
try {
const link = document.createElement('a');
link.href = '/landBlock.xlsx'; // 模板路径需确保public目录下存在该文件
link.download = '地块信息导入模板.xlsx'; // 下载后文件名
link.href = '/landBlock.xlsx';
link.download = '地块信息导入模板.xlsx';
document.body.appendChild(link);
link.click(); // 触发下载
link.click();
} catch (err) {
proxy?.$modal.msgError('模板下载失败,请重试');
} finally {
document.body.removeChild(link); // 清理DOM
const link = document.querySelector('a[download="地块信息导入模板.xlsx"]');
if (link) document.body.removeChild(link);
}
};

View File

@ -144,13 +144,16 @@
</el-table-column> -->
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="400">
<el-table-column fixed="right" label="操作" align="center" width="500">
<template #default="scope">
<el-space>
<el-button link type="primary" icon="Edit" @click="handleCheckRules(scope.row)" v-hasPermi="['project:attendanceRule:byProjectId']"
>打卡规则
</el-button>
<!-- <el-button link type="primary" icon="Edit" @click="handleScope(scope.row)" v-hasPermi="['project:project:edit']">打卡范围 </el-button> -->
<el-button link type="primary" icon="FolderOpened" @click="handleShowUpload(scope.row)" v-hasPermi="['project:project:edit']"
>导入安全协议书
</el-button>
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:project:edit']">修改 </el-button>
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:project:remove']">删除 </el-button>
<el-button link type="primary" icon="upload" @click="handleUpload(scope.row)" v-hasPermi="['project:project:saveTenderFile']"
@ -160,7 +163,6 @@
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改项目对话框 -->
@ -253,78 +255,6 @@
</el-col>
</el-row>
</div>
<div class="block-box">
<div class="">打卡设置</div>
<el-row :gutter="20">
<el-col :span="12" :offset="0">
<el-form-item label="打卡开始时间" prop="playCardStart" label-width="110px">
<!-- <el-time-picker value-format="HH:mm" v-model="form.playCardStart" placeholder="请输入打卡开始时间" /> -->
<el-time-select
v-model="form.playCardStart"
style="width: 100%"
class="mr-4"
placeholder="请输入打卡开始时间"
value-format="HH:mm"
start="00:00"
step="00:15"
end="23:59"
/>
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="打卡结束时间" prop="playCardEnd" label-width="110px">
<!-- <el-time-picker value-format="HH:mm" v-model="form.playCardEnd" placeholder="请输入打卡结束时间" /> -->
<el-time-select
v-model="form.playCardEnd"
style="width: 100%"
:min-time="form.playCardStart"
class="mr-4"
placeholder="请输入打卡结束时间"
value-format="HH:mm"
start="00:00"
step="00:15"
end="23:59"
/>
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="打卡类型" prop="playCardStart" label-width="110px">
<!-- <el-time-picker value-format="HH:mm" v-model="form.playCardStart" placeholder="请输入打卡开始时间" /> -->
<el-time-select
v-model="form.playCardStart"
style="width: 100%"
class="mr-4"
placeholder="请输入打卡开始时间"
value-format="HH:mm"
start="00:00"
step="00:15"
end="23:59"
/>
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="工作日" prop="playCardEnd" label-width="110px">
<!-- <el-time-picker value-format="HH:mm" v-model="form.playCardEnd" placeholder="请输入打卡结束时间" /> -->
<el-time-select
v-model="form.playCardEnd"
style="width: 100%"
:min-time="form.playCardStart"
class="mr-4"
placeholder="请输入打卡结束时间"
value-format="HH:mm"
start="00:00"
step="00:15"
end="23:59"
/>
</el-form-item>
</el-col>
<!-- <el-col :span="24" :offset="0">
<el-form-item label="安全协议书" prop="securityAgreement">
<file-upload v-model="form.securityAgreement" :limit="1" :file-type="['pdf']" :file-size="50" />
</el-form-item>
</el-col> -->
</el-row>
</div>
</el-form>
<template #footer>
<div class="dialog-footer">
@ -342,13 +272,12 @@
</div>
</template>
</el-dialog>
<!-- //选取项目地址弹窗 -->
<el-dialog v-model="amapStatus" :title="form.projectName + '-获取经纬度'" width="80%">
<el-dialog draggable v-model="amapStatus" :title="form.projectName + '-获取经纬度'" width="80%">
<amap height="620px" @setLocation="setPoi"></amap>
</el-dialog>
<!-- 选取方阵地址 -->
<el-dialog title="设置方阵" v-model="polygonStatus" width="1400px" :close-on-click-modal="false">
<el-dialog draggable title="设置方阵" v-model="polygonStatus" width="1400px" :close-on-click-modal="false">
<open-layers-map
:project-id="projectId"
:design-id="designId"
@ -356,7 +285,7 @@
@close="polygonStatus = false"
></open-layers-map>
</el-dialog>
<el-dialog title="添加子项目" v-model="childProjectStatus" width="400">
<el-dialog draggable title="添加子项目" v-model="childProjectStatus" width="400">
<span>填写子项目名称</span>
<el-input v-model="childProjectForm.projectName"></el-input>
<template #footer>
@ -366,7 +295,7 @@
</span>
</template>
</el-dialog>
<el-dialog title="上传文件" v-model="fileVisble" width="400">
<el-dialog draggable title="上传文件" v-model="fileVisble" width="400">
<file-upload v-model="fileForm.tenderFiles" :limit="10" :file-type="['pdf']" :file-size="50" />
<template #footer>
<div class="dialog-footer">
@ -375,6 +304,86 @@
</div>
</template>
</el-dialog>
<el-dialog draggable title="打卡规则" v-model="ruleFlag" width="800">
<template #footer>
<el-form ref="projectFormRef" :model="form" :rules="rules" label-width="100px">
<el-row :gutter="20">
<el-col :span="12" :offset="0">
<el-form-item label="上班时间" prop="clockInTime">
<el-time-select
v-model="form.clockInTime"
style="width: 100%"
class="mr-4"
placeholder="请输入打卡开始时间"
value-format="HH:mm"
start="00:00"
step="00:15"
end="23:59"
/>
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="下班时间" prop="clockOutTime">
<el-time-select
v-model="form.clockOutTime"
style="width: 100%"
:min-time="form.clockInTime"
class="mr-4"
placeholder="请输入打卡结束时间"
value-format="HH:mm"
start="00:00"
step="00:15"
end="23:59"
/>
</el-form-item>
</el-col>
<el-col :span="24" :offset="0">
<el-form-item label="打卡类型" prop="type">
<el-radio-group v-model="form.type">
<el-radio value="1" size="large">无限制</el-radio>
<el-radio value="2" size="large">范围内打卡</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="打卡类型" prop="weekday">
<el-checkbox-group v-model="form.weekday" size="small">
<el-checkbox label="星期一" value="1" />
<el-checkbox label="星期二" value="2" />
<el-checkbox label="星期三" value="3" />
<el-checkbox label="星期四" value="4" />
<el-checkbox label="星期五" value="5" />
<el-checkbox label="星期六" value="6" />
<el-checkbox label="星期日" value="7" />
</el-checkbox-group>
</el-form-item>
</el-col> </el-row
></el-form>
<div class="dialog-footer">
<el-button type="primary" @click="ruleSubmit"> 提交</el-button>
<el-button @click="ruleFlag = false">取消</el-button>
</div>
</template>
</el-dialog>
<el-dialog draggable title="打卡范围" v-model="ScopeFlag" width="600">
<div v-for="(item, i) of punchRangeList" :key="i" class="options_item">
<el-row>
<el-col :span="1"> <el-color-picker v-model="item.punchColor" show-alpha /></el-col>
<el-col :span="12"> <el-input v-model="item.punchName" placeholder="请输入打卡范围名称" class="ml-8" /></el-col>
<el-col :span="10" style="text-align: right; margin-top: 5px">
<el-button link type="primary" icon="view">预览</el-button>
<el-button link type="primary" icon="plus">添加</el-button>
<el-button link type="primary" icon="delete">移除</el-button>
</el-col>
</el-row>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="scopeSubmit"> 提交</el-button>
<el-button @click="ScopeFlag = false">取消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
@ -382,14 +391,14 @@
import {
addChildProject,
addProject,
addProjectFacilities,
addProjectPilePoint,
addProjectSquare,
delProject,
uploadProjectFile,
getProject,
listProject,
updateProject
updateProject,
attendanceRuleAdd,
attendanceRuleEdit,
byProjectIdDetail
} from '@/api/project/project';
import { ProjectForm, ProjectQuery, ProjectVO, childProjectQuery, locationType } from '@/api/project/project/types';
import amap from '@/components/amap/index.vue';
@ -413,6 +422,14 @@ const polygonStatus = ref(false);
const dxfFile = ref(null);
const projectId = ref<string>('');
const designId = ref<string>('');
const ruleFlag = ref(false);
const ScopeFlag = ref(false);
const punchRangeList = ref<any>([
{
punchName: '',
punchColor: '#1983ff'
}
]);
const childProjectForm = reactive<childProjectQuery>({
projectName: '',
pid: '',
@ -432,7 +449,7 @@ const fileForm = ref({
const jsonData = ref(null);
const fullscreenLoading = ref(false);
const initFormData: ProjectForm = {
const initFormData = {
id: undefined,
projectName: undefined,
shortName: undefined,
@ -451,13 +468,15 @@ const initFormData: ProjectForm = {
lat: undefined,
plan: undefined,
onStreamTime: undefined,
playCardStart: undefined,
playCardEnd: undefined,
clockInTime: undefined,
clockOutTime: undefined,
designTotal: undefined,
securityAgreement: undefined,
sort: 0,
showHidden: undefined,
isDelete: undefined
isDelete: undefined,
type: '1',
weekday: []
};
const data = reactive<PageData<ProjectForm, ProjectQuery>>({
form: { ...initFormData },
@ -480,8 +499,6 @@ const data = reactive<PageData<ProjectForm, ProjectQuery>>({
lat: undefined,
plan: undefined,
onStreamTime: undefined,
playCardStart: undefined,
playCardEnd: undefined,
designTotal: undefined,
securityAgreement: undefined,
sort: undefined,
@ -490,8 +507,8 @@ const data = reactive<PageData<ProjectForm, ProjectQuery>>({
params: {}
},
rules: {
playCardStart: [{ required: true, message: '打卡开始时间不能为空', trigger: 'blur' }],
playCardEnd: [{ required: true, message: '打卡结束时间不能为空', trigger: 'blur' }],
clockInTime: [{ required: true, message: '打卡开始时间不能为空', trigger: 'blur' }],
clockOutTime: [{ required: true, message: '打卡结束时间不能为空', trigger: 'blur' }],
projectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
shortName: [{ required: true, message: '项目简称不能为空', trigger: 'blur' }],
principalPhone: [{ required: true, message: '负责人电话不能为空', trigger: 'blur' }],
@ -692,7 +709,48 @@ const handleSetChild = async () => {
}
}
};
const handleScope = (row) => {
// 打卡范围
ScopeFlag.value = true;
projectId.value = row.id;
};
const scopeSubmit = () => {
// 提交打卡范围
};
// 添加规则
const handleCheckRules = async (row?: ProjectVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await byProjectIdDetail(_id);
if (res.data) {
res.data.weekday = res.data.weekday.split(',');
Object.assign(form.value, res.data);
}
projectId.value = row.id;
ruleFlag.value = true;
};
const ruleSubmit = async () => {
console.log(form.value);
projectFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
let obj = {
weekday: form.value.weekday.join(','),
projectId: projectId.value,
id: projectId.value,
clockInTime: form.value.clockInTime,
clockOutTime: form.value.clockOutTime,
type: form.value.type
};
if (form.value.id) {
await attendanceRuleEdit(obj);
} else {
await attendanceRuleAdd(obj);
}
ruleFlag.value = false;
await getList();
}
});
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(

View File

@ -0,0 +1,258 @@
<template>
<div class="p-2 editInfo">
<el-form label-position="top" ref="userFormRef" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="10">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="2"></el-col>
<el-col :span="10">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select
v-model="form.deptId"
:data="enabledDeptOptions"
:props="{ value: 'id', label: 'label', children: 'children' }"
value-key="id"
placeholder="请选择归属部门"
check-strictly
@change="handleDeptChange"
/>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item> </el-col
><el-col :span="2"></el-col>
<el-col :span="10">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
</el-form-item> </el-col
><el-col :span="2"></el-col>
<el-col :span="10">
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="用户性别">
<el-select v-model="form.sex" placeholder="请选择">
<el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select>
</el-form-item> </el-col
><el-col :span="2"></el-col>
<el-col :span="10">
<el-form-item label="岗位">
<el-select v-model="form.postIds" multiple placeholder="请选择">
<el-option
v-for="item in postOptions"
:key="item.postId"
:label="item.postName"
:value="item.postId"
:disabled="item.status == '1'"
></el-option>
</el-select>
</el-form-item> </el-col
><el-col :span="2"></el-col>
<el-col :span="10">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }} </el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- <div class="box_submit">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel()"> </el-button>
</div> -->
</div>
</template>
<script setup name="Profile" lang="ts">
import api from '@/api/system/user';
import { listProject } from '@/api/project/project';
import { getProjectByDeptId, getRoleList, optionselect } from '@/api/system/post';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
const enabledDeptOptions = ref([]);
const deptOptions = ref([]);
const projectOptions = ref<any[]>([]);
const postOptions = ref([]);
const roleOptions = ref([]);
const userFormRef = ref<ElFormInstance>();
const initFormData = {
userId: undefined,
deptId: undefined,
userName: '',
nickName: undefined,
password: '',
phonenumber: undefined,
email: undefined,
sex: undefined,
projectRoles: [
{
projectId: '',
roleIds: []
}
],
status: '0',
remark: '',
postIds: [],
filePath: undefined
};
const initData = {
form: { ...initFormData },
rules: {
userName: [
{ required: true, message: '用户名称不能为空', trigger: 'blur' },
{
min: 2,
max: 20,
message: '用户名称长度必须介于 2 和 20 之间',
trigger: 'blur'
}
],
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
password: [
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
{
min: 5,
max: 20,
message: '用户密码长度必须介于 5 和 20 之间',
trigger: 'blur'
},
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\ |', trigger: 'blur' }
],
email: [
{
type: 'email',
message: '请输入正确的邮箱地址',
trigger: ['blur', 'change']
}
],
phonenumber: [
{ required: true, message: '请输入手机号码', trigger: 'blur' },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
]
}
};
const data = reactive(initData);
const { form, rules } = toRefs(data);
/** 查询部门下拉树结构 */
const getDeptTree = async () => {
const res = await api.deptTreeSelect({ isShow: '1' });
deptOptions.value = res.data;
enabledDeptOptions.value = filterDisabledDept(res.data);
const projectList = await listProject();
projectOptions.value = projectList.rows;
};
/** 过滤禁用的部门 */
const filterDisabledDept = (deptList) => {
return deptList.filter((dept) => {
if (dept.disabled) {
return false;
}
if (dept.children && dept.children.length) {
dept.children = filterDisabledDept(dept.children);
}
return true;
});
};
async function handleDeptChange(value: number | string) {
proxy?.$emit('setDeptId', value);
const response = await optionselect(value);
const roleList = await getRoleList(value);
roleOptions.value = roleList.data;
postOptions.value = response.data;
form.value.postIds = [];
form.value.projectRoles = [
{
projectId: '',
roleIds: []
}
];
}
const submitForm = () => {
userFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value);
proxy?.$modal.msgSuccess('操作成功');
proxy?.$emit('submit', false);
}
});
};
const cancel = () => {
proxy?.$emit('close', false);
};
/** 重置操作表单 */
const reset = () => {
form.value = { ...initFormData };
form.value.projectRoles = [
{
projectId: '',
roleIds: []
}
];
userFormRef.value?.resetFields();
};
// 打开弹框调用参数
const open = async (row?: any) => {
reset();
if (row) {
// 编辑
const { data } = await api.getUser(row.userId);
Object.assign(form.value, data.user);
postOptions.value = data.posts;
roleOptions.value = data.roles;
form.value.postIds = data.postIds;
form.value.projectRoles = data.projectRoles;
form.value.password = '';
const roleList = await getRoleList(form.value.deptId);
roleOptions.value = roleList.data;
} else {
// 新增
const { data } = await api.getUser();
postOptions.value = data.posts;
}
};
const getInfoForm = () => {
return form.value;
};
onMounted(() => {
getDeptTree();
});
defineExpose({ open, getInfoForm });
</script>
<style lang="scss" scoped>
.editInfo {
position: relative;
height: 100%;
.box_submit {
position: absolute;
right: 10px;
bottom: 10px;
}
}
</style>

View File

@ -0,0 +1,477 @@
<template>
<div class="roleInfo">
<div class="title_detail">
<span>当前选定角色信息预览</span>
<div style="margin-top: 10px">
<el-table :data="roleList" border height="150">
<el-table-column label="所属部门" align="center" prop="deptName" />
<el-table-column label="关联项目" align="center" prop="projectName" />
<el-table-column label="web端担任角色" align="center" prop="webRoles" />
<el-table-column label="APP端担任角色" align="center" prop="appRoles" />
</el-table>
</div>
</div>
<div class="title_detail" style="margin-top: 20px">
<span>选择或修改当前角色信息</span>
<div style="margin-top: 10px" class="box_detail">
<!-- 项目列表选择区 -->
<div class="project_list">
<span>关联项目模块</span>
<div class="project-items">
<div
v-for="item in projectOptions"
:key="item.id"
class="project-item"
:class="{ 'project-item-selected': isProjectSelected(item.id) }"
@click="toggleProjectSelection(item)"
>
<div class="project-item-content">
<el-checkbox v-model="item.checked" :value="item.id" class="project-checkbox" @change="handleProjectCheck(item, $event)" />
<span class="project-name">{{ item.projectName }}</span>
</div>
</div>
</div>
</div>
<!-- 角色分配区 -->
<div class="post_list">
<span>关联项目角色分配</span>
<div v-if="selectedProjects.length === 0" class="no-selection">请从左侧选择项目进行角色分配</div>
<div v-for="(project, index) in selectedProjects" :key="project.id" class="project-role-container">
<div class="project-header">
<span class="project-title">{{ project.projectName }}</span>
<el-button type="text" class="remove-project" @click="removeProject(project.id)"> 移除 </el-button>
</div>
<div class="role-assignment">
<div class="role-group">
<label class="role-label">web端角色</label>
<el-checkbox-group v-model="project.webRoles" @change="updateRoleList">
<el-checkbox v-for="role in allRoles" :key="role.roleId" :value="role.roleId">
{{ role.roleName }}
</el-checkbox>
</el-checkbox-group>
</div>
<div class="role-group">
<label class="role-label">APP端角色</label>
<el-checkbox-group v-model="project.appRoles" @change="updateRoleList">
<el-checkbox v-for="role in AppRoles" :key="role.roleId" :value="role.roleId">
{{ role.roleName }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box_submit">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel()"> </el-button>
</div>
</div>
</template>
<script setup name="RoleProjectManagement" lang="ts">
import { ref, reactive, toRefs, getCurrentInstance, ComponentInternalInstance, defineExpose, watch } from 'vue';
import { ElFormInstance } from 'element-plus';
import api from '@/api/system/user';
import { listProject } from '@/api/project/project';
import { getRoleList } from '@/api/system/post';
// 类型定义
interface Project {
id: number | string;
projectName: string;
checked: boolean;
webRoles: string[];
appRoles: string[];
}
interface Role {
roleId: number | string;
roleName: string;
roleSort?: number;
}
interface RoleInfo {
userNick: string;
deptName: string;
postName: string;
projectName: string;
webRoles: string;
appRoles: string;
}
// 组件实例
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 响应式数据
const projectOptions = ref<Project[]>([]);
const selectedProjects = ref<Project[]>([]);
const allRoles = ref<Role[]>([]); // web端角色
const AppRoles = ref<Role[]>([]); // APP端角色
const roleList = ref<RoleInfo[]>([]);
// 表单初始数据
const initFormData = {
userId: undefined,
deptId: undefined,
userName: '',
nickName: undefined,
password: '',
phonenumber: undefined,
email: undefined,
sex: undefined,
projectRoles: [] as Array<{ projectId: number | string; webRoles: string[]; appRoles: string[] }>,
status: '0',
remark: '',
postIds: [],
filePath: undefined,
deptName: '' // 新增部门名称字段
};
const data = reactive({
form: { ...initFormData }
});
const deptName = ref('');
const { form } = toRefs(data);
// 核心方法:更新预览列表
const updateRoleList = () => {
if (selectedProjects.value.length === 0) {
roleList.value = [];
return;
}
roleList.value = selectedProjects.value.map((project) => {
// 处理web端角色名称
var webRoleNames = project.webRoles
.map((roleId) => {
const role = allRoles.value.find((r) => r.roleId === roleId);
return role ? role.roleName : '';
})
.filter(Boolean);
// 处理APP端角色名称
var appRoleNames = project.appRoles
.map((roleId) => {
const role = AppRoles.value.find((r) => r.roleId === roleId);
return role ? role.roleName : '';
})
.filter(Boolean);
webRoleNames = [...new Set(webRoleNames)];
appRoleNames = [...new Set(appRoleNames)];
return {
deptName: deptName.value,
projectName: project.projectName,
webRoles: webRoleNames.length > 0 ? webRoleNames.join('') : '无',
appRoles: appRoleNames.length > 0 ? appRoleNames.join('') : '无'
};
});
};
// 监听已选项目变化,自动更新预览列表
watch(
selectedProjects,
() => {
updateRoleList();
},
{ deep: true }
);
// 检查项目是否被选中
const isProjectSelected = (projectId: number | string) => {
return selectedProjects.value.some((p) => p.id === projectId);
};
// 切换项目选择状态
const toggleProjectSelection = (project: Project) => {
// handleProjectCheck(project, !project.checked);
};
// 处理项目勾选状态变化
const handleProjectCheck = (project: Project, checked: boolean) => {
// project.checked = checked;
const index = selectedProjects.value.findIndex((p) => p.id === project.id);
if (checked && index === -1) {
// 添加选中的项目
selectedProjects.value.push({
...project,
webRoles: [],
appRoles: []
});
} else if (!checked && index !== -1) {
// 移除取消选中的项目
selectedProjects.value.splice(index, 1);
}
};
// 移除项目
const removeProject = (projectId: number | string) => {
// 更新选中项目列表
selectedProjects.value = selectedProjects.value.filter((p) => p.id !== projectId);
// 更新项目选项的勾选状态
const project = projectOptions.value.find((p) => p.id === projectId);
if (project) {
project.checked = false;
}
};
// 提交表单
const submitForm = async () => {
// 整理项目角色数据
form.value.projectRoles = selectedProjects.value.map((project) => ({
projectId: project.id,
roleIds: [...new Set(project.webRoles), ...new Set(project.appRoles)]
}));
// 提交数据
try {
if (form.value.userId) {
await api.updateUser(form.value);
} else {
await api.addUser(form.value);
}
proxy?.$modal.msgSuccess('操作成功');
proxy?.$emit('submit', form.value);
cancel();
} catch (error) {
proxy?.$modal.msgError('操作失败,请重试');
console.error('提交失败:', error);
}
};
// 取消操作
const cancel = () => {
resetForm();
proxy?.$emit('close');
};
// 重置表单
const resetForm = () => {
data.form = { ...initFormData };
projectOptions.value.forEach((p) => (p.checked = false));
selectedProjects.value = [];
roleList.value = [];
};
// 初始化数据
const initData = async () => {
try {
// 获取项目列表
const projectRes = await listProject();
projectOptions.value = projectRes.rows.map((item: any) => ({
id: item.id,
projectName: item.projectName,
checked: false
}));
} catch (error) {
proxy?.$modal.msgError('数据加载失败');
console.error('初始化数据失败:', error);
}
};
// 打开弹窗时调用
const open = async (row?: any, deptId?: any) => {
resetForm();
await initData();
deptName.value = row.deptName;
console.log(row);
if (row) {
try {
if (!row.createTime) {
form.value = { ...row };
// 获取角色列表
if (form.value.deptId) {
deptId = form.value.deptId;
}
const roleRes = await getRoleList(deptId);
allRoles.value = roleRes.data.filter((item: Role) => item.roleSort === 1);
AppRoles.value = roleRes.data.filter((item: Role) => item.roleSort !== 1);
} else {
const { data } = await api.getUser(row.userId);
Object.assign(form.value, data.user);
// 获取角色列表
if (form.value.deptId) {
deptId = form.value.deptId;
}
const roleRes = await getRoleList(deptId);
// 区分web端和app端角色
allRoles.value = roleRes.data.filter((item: Role) => item.roleSort === 1);
AppRoles.value = roleRes.data.filter((item: Role) => item.roleSort !== 1);
// 加载项目角色数据
if (data.projectRoles && data.projectRoles.length) {
data.projectRoles.forEach((pr: any) => {
const project = projectOptions.value.find((p) => p.id === pr.projectId);
if (project) {
project.checked = true;
selectedProjects.value.push({
...project,
webRoles: pr.roleIds || [],
appRoles: pr.roleIds || []
});
}
});
}
}
} catch (error) {
proxy?.$modal.msgError('加载用户数据失败');
console.error('加载用户数据失败:', error);
}
}
};
// 暴露方法
defineExpose({ open });
</script>
<style lang="scss">
.roleInfo {
position: relative;
height: 100%;
padding-bottom: 60px;
box-sizing: border-box;
.title_detail {
> span {
font-size: 16px;
font-weight: 600;
color: #333;
display: inline-block;
padding-bottom: 5px;
border-bottom: 2px solid #409eff;
}
.box_detail {
display: flex;
gap: 15px;
margin-top: 15px;
> div {
height: 350px;
padding: 15px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
overflow-y: auto;
box-sizing: border-box;
}
.project_list {
width: 320px;
background-color: #fff;
.project-items {
margin-top: 10px;
}
.project-item {
padding: 10px 12px;
margin-bottom: 8px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
border: 1px solid #eee;
&:hover {
background-color: #f5f7fa;
border-color: #e4e7ed;
}
.project-item-content {
display: flex;
align-items: center;
}
.project-checkbox {
margin-right: 8px;
}
.project-name {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.project-item-selected {
background-color: #ecf5ff;
border-color: #c6e2ff;
}
}
.post_list {
flex: 1;
background-color: #fff;
.no-selection {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #909399;
font-size: 14px;
}
.project-role-container {
padding: 15px;
margin-bottom: 15px;
background-color: #f9f9f9;
border-radius: 4px;
border: 1px solid #f0f0f0;
.project-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
.project-title {
font-weight: 500;
color: #333;
font-size: 14px;
}
.remove-project {
color: #f56c6c;
padding: 0 5px;
&:hover {
color: #e4393c;
}
}
}
.role-assignment {
.role-group {
margin-bottom: 15px;
.role-label {
display: inline-block;
width: 110px;
color: #606266;
font-size: 14px;
}
.el-checkbox-group {
display: inline-block;
margin-left: 10px;
.el-checkbox {
margin-right: 15px;
margin-bottom: 8px;
}
}
}
}
}
}
}
}
.box_submit {
position: absolute;
right: 20px;
bottom: 0px;
display: flex;
gap: 10px;
}
}
</style>

View File

@ -0,0 +1,788 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 部门树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
<el-tree
ref="deptTreeRef"
class="mt-2"
node-key="id"
:data="deptOptions"
:props="{ label: 'label', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
/>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="用户状态" clearable>
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="创建时间" style="width: 308px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery" @v-has-permi="['system:user:query']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10">
<el-col :span="1.5">
<el-button v-has-permi="['system:user:add']" type="primary" plain icon="Plus" @click="handleAdd()"> 新增 </el-button>
</el-col>
<el-col :span="1.5">
<el-button v-has-permi="['system:user:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">
修改
</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-has-permi="['system:user:remove']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">
删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-dropdown class="mt-[1px]">
<el-button plain type="info">
更多
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="Download" @click="importTemplate">下载模板</el-dropdown-item>
<el-dropdown-item v-has-permi="['system:user:import']" icon="Top" @click="handleImport">导入数据 </el-dropdown-item>
<el-dropdown-item v-has-permi="['system:user:export']" icon="Download" @click="handleExport"> 导出数据 </el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col>
<right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column v-if="columns[0].visible" key="userId" label="用户编号" align="center" prop="userId" />
<el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column v-if="columns[2].visible" key="nickName" label="用户昵称" align="center" prop="nickName" :show-overflow-tooltip="true" />
<el-table-column v-if="columns[3].visible" key="deptName" label="部门" align="center" prop="deptName" :show-overflow-tooltip="true" />
<el-table-column v-if="columns[4].visible" key="phonenumber" label="手机号码" align="center" prop="phonenumber" width="120" />
<el-table-column v-if="columns[5].visible" key="status" label="状态" align="center">
<template #default="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column v-if="columns[6].visible" label="创建时间" align="center" prop="createTime" width="160">
<template #default="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="230" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip v-if="scope.row.userId !== 1" content="修改" placement="top">
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="删除" placement="top">
<el-button v-hasPermi="['system:user:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="重置密码" placement="top">
<el-button v-hasPermi="['system:user:resetPwd']" link type="primary" icon="Key" @click="handleResetPwd(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="分配角色" placement="top">
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="编辑关联项目" placement="top">
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdateProject(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="上传证书目录" placement="top">
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Upload" @click="handleUploadCert(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<el-dialog v-model="shuttleVisible" title="编辑关联项目" width="auto" destroy-on-close>
<shuttle-frame :userId="selectedUserId" @close="shuttleVisible = false" />
</el-dialog>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</el-card>
</el-col>
</el-row>
<!-- 添加或修改用户配置对话框 -->
<el-dialog draggable ref="formDialogRef" v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body @close="closeDialog">
<el-form ref="userFormRef" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select
v-model="form.deptId"
:data="enabledDeptOptions"
:props="{ value: 'id', label: 'label', children: 'children' }"
value-key="id"
placeholder="请选择归属部门"
check-strictly
@change="handleDeptChange"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="用户性别">
<el-select v-model="form.sex" placeholder="请选择">
<el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="岗位">
<el-select v-model="form.postIds" multiple placeholder="请选择">
<el-option
v-for="item in postOptions"
:key="item.postId"
:label="item.postName"
:value="item.postId"
:disabled="item.status == '1'"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-row :gutter="20" v-for="(item, index) in form.projectRoles">
<el-col :span="11" :offset="0">
<el-form-item label="项目列表">
<el-select v-model="item.projectId" placeholder="请选择">
<el-option v-for="dict in projectOptions" :key="dict.id" :label="dict.shortName" :value="dict.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="11" :offset="0">
<el-form-item label="角色">
<el-select v-model="item.roleIds" filterable multiple placeholder="请选择">
<el-option
v-for="item in roleOptions"
:key="item.roleId"
:label="item.roleName"
:value="item.roleId"
:disabled="item.status == '1'"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="1" :offset="0">
<el-button type="primary" circle icon="Plus" @click="handleAddProject" v-if="index == 0"></el-button>
<el-button type="danger" circle icon="Delete" @click="delProject(index)" v-else></el-button>
</el-col>
</el-row>
</el-col>
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }} </el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel()"> </el-button>
</div>
</template>
</el-dialog>
<!-- 用户导入对话框 -->
<el-dialog draggable v-model="upload.open" :title="upload.title" width="400px" append-to-body>
<el-upload
ref="uploadRef"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<el-icon class="el-icon--upload">
<i-ep-upload-filled />
</el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="text-center el-upload__tip">
<div class="el-upload__tip">
<el-checkbox v-model="upload.updateSupport" />
是否更新已经存在的用户数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板 </el-link>
</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</template>
</el-dialog>
<el-dialog title="上传证书目录" v-model="certDialog" width="30%" destroy-on-close>
<!-- <File-upload v-model="fileUpload" :limit="5"></File-upload> -->
<ImageUpload v-model="fileUpload" :limit="5"></ImageUpload>
<template #footer>
<span>
<el-button @click="certDialog = false">取消</el-button>
<el-button type="primary" @click="uploadCert">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup name="User" lang="ts">
import api, { uploadCertList } from '@/api/system/user';
import { UserForm, UserQuery, UserVO } from '@/api/system/user/types';
import { DeptTreeVO, DeptVO } from '@/api/system/dept/types';
import { RoleVO } from '@/api/system/role/types';
import { PostVO } from '@/api/system/post/types';
import { globalHeaders } from '@/utils/request';
import { to } from 'await-to-js';
import { getProjectByDeptId, getRoleList, optionselect } from '@/api/system/post';
import ShuttleFrame from '../../project/projectRelevancy/component/ShuttleFrame.vue';
import { listProject } from '@/api/project/project';
import editInfo from './comm/editInfo.vue';
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
const userList = ref<UserVO[]>();
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const deptName = ref('');
const deptOptions = ref<DeptTreeVO[]>([]);
const enabledDeptOptions = ref<DeptTreeVO[]>([]);
const initPassword = ref<string>('');
const postOptions = ref<PostVO[]>([]);
const roleOptions = ref<RoleVO[]>([]);
const projectOptions = ref<any[]>([]);
/*** 用户导入参数 */
const upload = reactive<ImportOption>({
// 是否显示弹出层(用户导入)
open: false,
// 弹出层标题(用户导入)
title: '',
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的用户数据
updateSupport: 0,
// 设置上传的请求头部
headers: globalHeaders(),
// 上传的地址
url: import.meta.env.VITE_APP_BASE_API + '/system/user/importData'
});
// 列显隐信息
const columns = ref<FieldOption[]>([
{ key: 0, label: `用户编号`, visible: false, children: [] },
{ key: 1, label: `用户名称`, visible: true, children: [] },
{ key: 2, label: `用户昵称`, visible: true, children: [] },
{ key: 3, label: `部门`, visible: true, children: [] },
{ key: 4, label: `手机号码`, visible: true, children: [] },
{ key: 5, label: `状态`, visible: true, children: [] },
{ key: 6, label: `创建时间`, visible: true, children: [] }
]);
const deptTreeRef = ref<ElTreeInstance>();
const queryFormRef = ref<ElFormInstance>();
const userFormRef = ref<ElFormInstance>();
const uploadRef = ref<ElUploadInstance>();
const formDialogRef = ref<ElDialogInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: UserForm = {
userId: undefined,
deptId: undefined,
userName: '',
nickName: undefined,
password: '',
phonenumber: undefined,
email: undefined,
sex: undefined,
projectRoles: [
{
projectId: '',
roleIds: []
}
],
status: '0',
remark: '',
postIds: [],
filePath: undefined
};
const initData: PageData<UserForm, UserQuery> = {
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
userName: '',
phonenumber: '',
status: '',
deptId: '',
roleId: ''
},
rules: {
userName: [
{ required: true, message: '用户名称不能为空', trigger: 'blur' },
{
min: 2,
max: 20,
message: '用户名称长度必须介于 2 和 20 之间',
trigger: 'blur'
}
],
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
password: [
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
{
min: 5,
max: 20,
message: '用户密码长度必须介于 5 和 20 之间',
trigger: 'blur'
},
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\ |', trigger: 'blur' }
],
email: [
{
type: 'email',
message: '请输入正确的邮箱地址',
trigger: ['blur', 'change']
}
],
phonenumber: [
{ required: true, message: '请输入手机号码', trigger: 'blur' },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
]
}
};
const data = reactive<PageData<UserForm, UserQuery>>(initData);
const { queryParams, form, rules } = toRefs<PageData<UserForm, UserQuery>>(data);
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
deptTreeRef.value?.filter(deptName.value);
},
{
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
}
);
/** 查询用户列表 */
const getList = async () => {
loading.value = true;
const res = await api.listUser(proxy?.addDateRange(queryParams.value, dateRange.value));
loading.value = false;
userList.value = res.rows;
total.value = res.total;
};
/** 查询部门下拉树结构 */
const getDeptTree = async () => {
const res = await api.deptTreeSelect({ isShow: '1' });
deptOptions.value = res.data;
enabledDeptOptions.value = filterDisabledDept(res.data);
const projectList = await listProject();
projectOptions.value = projectList.rows;
};
/** 过滤禁用的部门 */
const filterDisabledDept = (deptList: DeptTreeVO[]) => {
return deptList.filter((dept) => {
if (dept.disabled) {
return false;
}
if (dept.children && dept.children.length) {
dept.children = filterDisabledDept(dept.children);
}
return true;
});
};
/** 节点单击事件 */
const handleNodeClick = (data: DeptVO) => {
queryParams.value.deptId = data.id;
handleQuery();
};
/** 部门选择变化 */
const handleAddProject = () => {
form.value.projectRoles.push({
projectId: '',
roleIds: []
});
};
/** 删除项目 */
const delProject = (index: number) => {
form.value.projectRoles.splice(index, 1);
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.deptId = undefined;
deptTreeRef.value?.setCurrentKey(undefined);
handleQuery();
};
/** 删除按钮操作 */
const handleDelete = async (row?: UserVO) => {
const userIds = row?.userId || ids.value;
const [err] = await to(proxy?.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?') as any);
if (!err) {
await api.delUser(userIds);
await getList();
proxy?.$modal.msgSuccess('删除成功');
}
};
/** 用户状态修改 */
const handleStatusChange = async (row: UserVO) => {
let text = row.status === '0' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?');
await api.changeUserStatus(row.userId, row.status);
proxy?.$modal.msgSuccess(text + '成功');
} catch (err) {
row.status = row.status === '0' ? '1' : '0';
}
};
/** 跳转角色分配 */
const handleAuthRole = (row: UserVO) => {
const userId = row.userId;
router.push('/system/user-auth/role/' + userId);
};
/** 重置密码按钮操作 */
const handleResetPwd = async (row: UserVO) => {
const [err, res] = await to(
ElMessageBox.prompt('请输入"' + row.userName + '"的新密码', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间',
inputValidator: (value) => {
if (/<|>|"|'|\||\\/.test(value)) {
return '不能包含非法字符:< > " \' \\ |';
}
}
})
);
if (!err && res) {
await api.resetUserPwd(row.userId, res.value);
proxy?.$modal.msgSuccess('修改成功,新密码是:' + res.value);
}
};
/** 选择条数 */
const handleSelectionChange = (selection: UserVO[]) => {
ids.value = selection.map((item) => item.userId);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 导入按钮操作 */
const handleImport = () => {
upload.title = '用户导入';
upload.open = true;
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'system/user/export',
{
...queryParams.value
},
`user_${new Date().getTime()}.xlsx`
);
};
/** 下载模板操作 */
const importTemplate = () => {
proxy?.download('system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`);
};
/**文件上传中处理 */
const handleFileUploadProgress = () => {
upload.isUploading = true;
};
/** 文件上传成功处理 */
const handleFileSuccess = (response: any, file: UploadFile) => {
upload.open = false;
upload.isUploading = false;
uploadRef.value?.handleRemove(file);
ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + '</div>', '导入结果', {
dangerouslyUseHTMLString: true
});
getList();
};
/** 提交上传文件 */
function submitFileForm() {
uploadRef.value?.submit();
}
/** 重置操作表单 */
const reset = () => {
form.value = { ...initFormData };
form.value.projectRoles = [
{
projectId: '',
roleIds: []
}
];
userFormRef.value?.resetFields();
};
/** 取消按钮 */
const cancel = () => {
dialog.visible = false;
reset();
};
/** 新增按钮操作 */
const handleAdd = async () => {
reset();
const { data } = await api.getUser();
dialog.visible = true;
dialog.title = '新增用户';
postOptions.value = data.posts;
form.value.password = initPassword.value.toString();
};
/** 修改按钮操作 */
const handleUpdate = async (row?: UserForm) => {
reset();
const userId = row?.userId || ids.value[0];
const { data } = await api.getUser(userId);
dialog.visible = true;
dialog.title = '修改用户';
Object.assign(form.value, data.user);
postOptions.value = data.posts;
roleOptions.value = data.roles;
form.value.postIds = data.postIds;
form.value.projectRoles = data.projectRoles;
form.value.password = '';
const roleList = await getRoleList(form.value.deptId);
roleOptions.value = roleList.data;
};
const validate = () => {
for (let i = 0; i < form.value.projectRoles.length; i++) {
const item = form.value.projectRoles[i];
if (!item.projectId || item.projectId.length === 0) {
proxy?.$modal.msgError(`${i + 1} 行“项目列表”未填写`);
return false; // 阻止提交
}
if (!item.roleIds || item.roleIds.length === 0) {
proxy?.$modal.msgError(`${i + 1} 行“角色”未填写`);
return false; // 阻止提交
}
}
return true;
};
/** 提交按钮 */
const submitForm = () => {
const isValid = validate();
if (!isValid) return;
userFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value);
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/**
* 关闭用户弹窗
*/
const closeDialog = () => {
dialog.visible = false;
resetForm();
};
/**
* 重置表单
*/
const resetForm = () => {
userFormRef.value?.resetFields();
userFormRef.value?.clearValidate();
form.value.id = undefined;
form.value.status = '1';
};
onMounted(() => {
getDeptTree(); // 初始化部门数据
getList(); // 初始化列表数据
proxy?.getConfigKey('sys.user.initPassword').then((response) => {
initPassword.value = response.data;
});
});
async function handleDeptChange(value: number | string) {
const response = await optionselect(value);
const roleList = await getRoleList(value);
roleOptions.value = roleList.data;
postOptions.value = response.data;
form.value.postIds = [];
form.value.projectRoles = [
{
projectId: [],
roleIds: []
}
];
}
const shuttleVisible = ref(false);
const selectedUserId = ref<number>();
const handleUpdateProject = (row) => {
if (row) {
selectedUserId.value = row.userId;
shuttleVisible.value = true;
}
};
const certDialog = ref(false);
const certId = ref<string | number>(undefined);
const fileUpload = ref<string>();
//上传证书
const handleUploadCert = (row: UserVO) => {
certId.value = row.userId;
fileUpload.value = row.filePath;
certDialog.value = true;
};
const uploadCert = async () => {
if (!fileUpload.value) {
proxy?.$modal.msgError('请上传证书目录');
return;
}
let res = await uploadCertList({
userId: certId.value,
fileId: fileUpload.value
});
console.log(res);
certDialog.value = false;
proxy?.$modal.msgSuccess('上传证书目录成功');
};
</script>
<style lang="scss" scoped></style>

View File

@ -5,14 +5,22 @@
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
<el-tree ref="deptTreeRef" class="mt-2" node-key="id" :data="deptOptions"
:props="{ label: 'label', children: 'children' }" :expand-on-click-node="false"
:filter-node-method="filterNode" highlight-current default-expand-all @node-click="handleNodeClick" />
<el-tree
ref="deptTreeRef"
class="mt-2"
node-key="id"
:data="deptOptions"
:props="{ label: 'label', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
/>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
:leave-active-class="proxy?.animate.searchAnimate.leave">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
@ -20,24 +28,27 @@
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable
@keyup.enter="handleQuery" />
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="用户状态" clearable>
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label"
:value="dict.value" />
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="创建时间" style="width: 308px">
<el-date-picker v-model="dateRange" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"></el-date-picker>
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"
@v-has-permi="['system:user:query']">搜索</el-button>
<el-button type="primary" icon="Search" @click="handleQuery" @v-has-permi="['system:user:query']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
@ -50,18 +61,15 @@
<template #header>
<el-row :gutter="10">
<el-col :span="1.5">
<el-button v-has-permi="['system:user:add']" type="primary" plain icon="Plus" @click="handleAdd()"> 新增
</el-button>
<el-button v-has-permi="['system:user:add']" type="primary" plain icon="Plus" @click="handleAdd()"> 新增 </el-button>
</el-col>
<el-col :span="1.5">
<el-button v-has-permi="['system:user:edit']" type="success" plain :disabled="single" icon="Edit"
@click="handleUpdate()">
<el-button v-has-permi="['system:user:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">
修改
</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-has-permi="['system:user:remove']" type="danger" plain :disabled="multiple" icon="Delete"
@click="handleDelete()">
<el-button v-has-permi="['system:user:remove']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">
删除
</el-button>
</el-col>
@ -76,34 +84,26 @@
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="Download" @click="importTemplate">下载模板</el-dropdown-item>
<el-dropdown-item v-has-permi="['system:user:import']" icon="Top" @click="handleImport">导入数据
</el-dropdown-item>
<el-dropdown-item v-has-permi="['system:user:export']" icon="Download" @click="handleExport"> 导出数据
</el-dropdown-item>
<el-dropdown-item v-has-permi="['system:user:import']" icon="Top" @click="handleImport">导入数据 </el-dropdown-item>
<el-dropdown-item v-has-permi="['system:user:export']" icon="Download" @click="handleExport"> 导出数据 </el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col>
<right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true"
@query-table="getList"></right-toolbar>
<right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column v-if="columns[0].visible" key="userId" label="用户编号" align="center" prop="userId" />
<el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName"
:show-overflow-tooltip="true" />
<el-table-column v-if="columns[2].visible" key="nickName" label="用户昵称" align="center" prop="nickName"
:show-overflow-tooltip="true" />
<el-table-column v-if="columns[3].visible" key="deptName" label="部门" align="center" prop="deptName"
:show-overflow-tooltip="true" />
<el-table-column v-if="columns[4].visible" key="phonenumber" label="手机号码" align="center" prop="phonenumber"
width="120" />
<el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column v-if="columns[2].visible" key="nickName" label="用户昵称" align="center" prop="nickName" :show-overflow-tooltip="true" />
<el-table-column v-if="columns[3].visible" key="deptName" label="部门" align="center" prop="deptName" :show-overflow-tooltip="true" />
<el-table-column v-if="columns[4].visible" key="phonenumber" label="手机号码" align="center" prop="phonenumber" width="120" />
<el-table-column v-if="columns[5].visible" key="status" label="状态" align="center">
<template #default="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1"
@change="handleStatusChange(scope.row)"></el-switch>
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template>
</el-table-column>
@ -116,30 +116,24 @@
<el-table-column label="操作" fixed="right" width="230" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip v-if="scope.row.userId !== 1" content="修改" placement="top">
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit"
@click="handleUpdate(scope.row)"></el-button>
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="删除" placement="top">
<el-button v-hasPermi="['system:user:remove']" link type="primary" icon="Delete"
@click="handleDelete(scope.row)"></el-button>
<el-button v-hasPermi="['system:user:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="重置密码" placement="top">
<el-button v-hasPermi="['system:user:resetPwd']" link type="primary" icon="Key"
@click="handleResetPwd(scope.row)"></el-button>
<el-button v-hasPermi="['system:user:resetPwd']" link type="primary" icon="Key" @click="handleResetPwd(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="分配角色" placement="top">
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="CircleCheck"
@click="handleAuthRole(scope.row)"></el-button>
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="编辑关联项目" placement="top">
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit"
@click="handleUpdateProject(scope.row)"></el-button>
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdateProject(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="上传证书目录" placement="top">
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Upload"
@click="handleUploadCert(scope.row)"></el-button>
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Upload" @click="handleUploadCert(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
@ -148,117 +142,47 @@
<shuttle-frame :userId="selectedUserId" @close="shuttleVisible = false" />
</el-dialog>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
:total="total" @pagination="getList" />
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</el-card>
</el-col>
</el-row>
<!-- 添加或修改用户配置对话框 -->
<el-dialog ref="formDialogRef" v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body
@close="closeDialog">
<el-form ref="userFormRef" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select v-model="form.deptId" :data="enabledDeptOptions"
:props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择归属部门"
check-strictly @change="handleDeptChange" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="用户性别">
<el-select v-model="form.sex" placeholder="请选择">
<el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label"
:value="dict.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="岗位">
<el-select v-model="form.postIds" multiple placeholder="请选择">
<el-option v-for="item in postOptions" :key="item.postId" :label="item.postName" :value="item.postId"
:disabled="item.status == '1'"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-row :gutter="20" v-for="(item, index) in form.projectRoles">
<el-col :span="11" :offset="0">
<el-form-item label="项目列表">
<el-select v-model="item.projectId" placeholder="请选择">
<el-option v-for="dict in projectOptions" :key="dict.id" :label="dict.shortName"
:value="dict.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="11" :offset="0">
<el-form-item label="角色">
<el-select v-model="item.roleIds" filterable multiple placeholder="请选择">
<el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName"
:value="item.roleId" :disabled="item.status == '1'"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="1" :offset="0">
<el-button type="primary" circle icon="Plus" @click="handleAddProject" v-if="index == 0"></el-button>
<el-button type="danger" circle icon="Delete" @click="delProject(index)" v-else></el-button>
</el-col>
</el-row>
</el-col>
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel()"> </el-button>
<el-dialog draggable ref="formDialogRef" v-model="dialog.visible" :title="dialog.title" width="1300px" append-to-body @close="closeDialog">
<div class="boxDetial">
<div class="tab_info">
<span @click="onTab(1)" :class="{ active: type == 1 }">基本资料</span>
<span @click="onTab(2)" :class="{ active: type == 2 }">角色信息</span>
</div>
</template>
<div class="tab_content" v-show="type == 1">
<editInfo ref="editInfoRef" @close="dialog.visible = false" @submit="getList" @setDeptId="setDeptId"></editInfo>
</div>
<div class="tab_content" v-show="type == 2">
<roleInfo ref="roleInfoRef" @close="dialog.visible = false" @submit="getList"></roleInfo>
</div>
</div>
</el-dialog>
<!-- 用户导入对话框 -->
<el-dialog draggable v-model="upload.open" :title="upload.title" width="400px" append-to-body>
<el-upload ref="uploadRef" :limit="1" accept=".xlsx, .xls" :headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading"
:on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
<el-upload
ref="uploadRef"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<el-icon class="el-icon--upload">
<i-ep-upload-filled />
</el-icon>
@ -270,8 +194,7 @@
是否更新已经存在的用户数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline"
@click="importTemplate">下载模板 </el-link>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板 </el-link>
</div>
</template>
</el-upload>
@ -307,7 +230,8 @@ import { to } from 'await-to-js';
import { getProjectByDeptId, getRoleList, optionselect } from '@/api/system/post';
import ShuttleFrame from '../../project/projectRelevancy/component/ShuttleFrame.vue';
import { listProject } from '@/api/project/project';
import editInfo from './comm/editInfo.vue';
import roleInfo from './comm/roleInfo.vue';
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
@ -326,7 +250,8 @@ const initPassword = ref<string>('');
const postOptions = ref<PostVO[]>([]);
const roleOptions = ref<RoleVO[]>([]);
const projectOptions = ref<any[]>([]);
const editInfoRef = ref<InstanceType<typeof editInfo> | null>(null);
const roleInfoRef = ref<InstanceType<typeof roleInfo> | null>(null);
/*** 用户导入参数 */
const upload = reactive<ImportOption>({
// 是否显示弹出层(用户导入)
@ -358,7 +283,8 @@ const queryFormRef = ref<ElFormInstance>();
const userFormRef = ref<ElFormInstance>();
const uploadRef = ref<ElUploadInstance>();
const formDialogRef = ref<ElDialogInstance>();
const deptIdRole = ref<number>();
const type = ref(1);
const dialog = reactive<DialogOption>({
visible: false,
title: ''
@ -452,7 +378,9 @@ watchEffect(
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
}
);
const setDeptId = (deptId: number) => {
deptIdRole.value = deptId;
};
/** 查询用户列表 */
const getList = async () => {
loading.value = true;
@ -637,8 +565,12 @@ const cancel = () => {
const handleAdd = async () => {
reset();
const { data } = await api.getUser();
type.value = 1;
dialog.visible = true;
dialog.title = '新增用户';
nextTick(() => {
editInfoRef.value?.open();
});
postOptions.value = data.posts;
form.value.password = initPassword.value.toString();
};
@ -646,19 +578,13 @@ const handleAdd = async () => {
/** 修改按钮操作 */
const handleUpdate = async (row?: UserForm) => {
reset();
const userId = row?.userId || ids.value[0];
const { data } = await api.getUser(userId);
dialog.visible = true;
dialog.title = '修改用户';
Object.assign(form.value, data.user);
postOptions.value = data.posts;
roleOptions.value = data.roles;
form.value.postIds = data.postIds;
form.value.projectRoles = data.projectRoles;
form.value.password = '';
const roleList = await getRoleList(form.value.deptId);
roleOptions.value = roleList.data;
type.value = 1;
form.value = row;
nextTick(() => {
editInfoRef.value?.open(row);
});
};
const validate = () => {
@ -764,6 +690,46 @@ const uploadCert = async () => {
certDialog.value = false;
proxy?.$modal.msgSuccess('上传证书目录成功');
};
const onTab = (val) => {
type.value = val;
if (val == 2) {
let obj = editInfoRef.value?.getInfoForm();
form.value = obj;
nextTick(() => {
roleInfoRef.value?.open(form.value, deptIdRole.value);
});
}
};
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.boxDetial {
display: flex;
justify-content: space-between;
align-items: center;
height: 680px;
.tab_info {
height: 100%;
width: 200px;
background: #f0f2f5;
padding: 10px;
text-align: center;
display: flex;
flex-direction: column;
font-size: 18px;
> span {
margin-bottom: 10px;
cursor: pointer;
}
.active {
color: #1890ff;
}
}
.tab_content {
height: 100%;
width: calc(100% - 200px);
padding: 20px 10px;
background: #ccc;
}
}
</style>

View File

@ -10,7 +10,7 @@
<el-option v-for="item in options" :key="item.versions" :label="item.versions" :value="item.versions" />
</el-select>
</el-form-item>
<el-form-item label="表名" prop="sheet">
<el-form-item label="表名" prop="sheet" v-if="activeTab != '3'">
<el-select v-model="queryForm.sheet" placeholder="选择表名" @change="changeSheet">
<el-option v-for="item in sheets" :key="item" :label="item" :value="item" />
</el-select>
@ -139,7 +139,7 @@ const sheets = ref([]);
const options = ref([]);
const tableData = ref([]);
const tableRef = ref();
const isExpandAll = ref(false);
const isExpandAll = ref(true);
const loading = ref(false);
const versionMap = new Map();

View File

@ -191,9 +191,14 @@
<el-table-column prop="useQuantity" label="剩余量" align="center">
<template #default="scope">
{{
(scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) == 0
(scope.row.quantity ? Number(scope.row.quantity) : 0) -
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
(scope.row.selectNum ? Number(scope.row.selectNum) : 0) ==
0
? ''
: (scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)
: (scope.row.quantity ? Number(scope.row.quantity) : 0) -
(scope.row.selectNum ? Number(scope.row.selectNum) : 0) -
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)
}}
</template>
</el-table-column>
@ -219,12 +224,16 @@
<el-table-column prop="price" label="总价" align="center">
<template #default="scope">
{{
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
((scope.row.quantity ? Number(scope.row.quantity) : 0) -
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
(scope.row.selectNum ? Number(scope.row.selectNum) : 0)) *
Number(scope.row.unitPrice) ==
0
? ''
: (
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
((scope.row.quantity ? Number(scope.row.quantity) : 0) -
(scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) -
(scope.row.selectNum ? Number(scope.row.selectNum) : 0)) *
Number(scope.row.unitPrice)
).toFixed(2)
}}
@ -459,19 +468,11 @@ const getSheetName = async () => {
treeForm.value.sheet = res.data[0];
} else {
treeForm.value.sheet = '';
ElMessage({
message: '获取表名失败',
type: 'warning'
});
}
getTreeList();
}
} catch (error) {
console.log(error);
ElMessage({
message: '获取表名失败',
type: 'warning'
});
}
};
const handleSelection = (selection: any) => {

View File

@ -234,17 +234,17 @@
<!-- 第十一行注册人员数量仅劳务类型显示 -->
<el-row :gutter="20" class="mb-4" v-if="form.supplierType === '劳务'">
<el-col :span="12">
<el-form-item label="一建建造师" prop="build1">
<el-input v-model="form.build1" placeholder="请输入一建建造师数量" clearable />
<el-form-item label="一建建造师" prop="firstBuildingNumber">
<el-input v-model="form.firstBuildingNumber" placeholder="请输入一建建造师数量" clearable />
</el-form-item>
<el-form-item label="二建建造师" prop="build2">
<el-input v-model="form.build2" placeholder="请输入二建建造师数量" clearable />
<el-form-item label="二建建造师" prop="secondBuildingNumber">
<el-input v-model="form.secondBuildingNumber" placeholder="请输入二建建造师数量" clearable />
</el-form-item>
<el-form-item label="注册造价工程师" prop="build3">
<el-input v-model="form.build3" placeholder="请输入注册造价工程师数量" clearable />
<el-form-item label="注册造价工程师" prop="registeredEngineerNumber">
<el-input v-model="form.registeredEngineerNumber" placeholder="请输入注册造价工程师数量" clearable />
</el-form-item>
<el-form-item label="其他(分别写)" prop="build4">
<el-input v-model="form.build4" placeholder="请输入其他人员数量" clearable />
<el-form-item label="其他(分别写)" prop="otherBuildingNumber">
<el-input v-model="form.otherBuildingNumber" placeholder="请输入其他人员数量" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
@ -254,17 +254,17 @@
<!-- 第十二行职称人员数量仅劳务类型显示 -->
<el-row :gutter="20" class="mb-4" v-if="form.supplierType === '劳务'">
<el-col :span="12">
<el-form-item label="高级工程师人数" prop="personnelNumber1">
<el-input v-model="form.personnelNumber1" placeholder="请输高级工程师数量" clearable />
<el-form-item label="高级工程师人数" prop="seniorEngineerNumber">
<el-input v-model="form.seniorEngineerNumber" placeholder="请输高级工程师数量" clearable />
</el-form-item>
<el-form-item label="工程师数量" prop="personnelNumber2">
<el-input v-model="form.personnelNumber2" placeholder="请输入工程师数量" clearable />
<el-form-item label="工程师数量" prop="engineerNumber">
<el-input v-model="form.engineerNumber" placeholder="请输入工程师数量" clearable />
</el-form-item>
<el-form-item label="助理工程师数量" prop="personnelNumber3">
<el-input v-model="form.personnelNumber3" placeholder="请输入助理工程师数量" clearable />
<el-form-item label="助理工程师数量" prop="assistantEngineerNumber">
<el-input v-model="form.assistantEngineerNumber" placeholder="请输入助理工程师数量" clearable />
</el-form-item>
<el-form-item label="其他人员数量" prop="personnelNumber4">
<el-input v-model="form.personnelNumber4" placeholder="请输入其他人员数量" clearable />
<el-form-item label="其他人员数量" prop="otherPersonnelNumber">
<el-input v-model="form.otherPersonnelNumber" placeholder="请输入其他人员数量" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
@ -285,8 +285,8 @@
:data="form"
uploadUrl="/supplierInput/supplierInput"
:limit="1"
:onUploadSuccess="handleUploadSuccess"
@handleChange="change"
@handleChange="handleFileChange"
@handleRemove="handleFileRemove"
showFileList
>
<div><el-button type="primary">上传文件</el-button><br /></div>
@ -309,7 +309,7 @@
<script setup name="SupplierInput" lang="ts">
import { ComponentInternalInstance, getCurrentInstance, onMounted, ref, reactive, toRefs, computed } from 'vue';
import { ElFormInstance } from 'element-plus';
import { listSupplierInput, getSupplierInput, delSupplierInput } from '@/api/supplierInput/supplierInput/index';
import { listSupplierInput, getSupplierInput, delSupplierInput, updateSupplierInput } from '@/api/supplierInput/supplierInput/index';
import { SupplierInputVO, SupplierInputQuery, SupplierInputForm, PageData, DialogOption } from '@/api/supplierInput/supplierInput/types';
import Pagination from '@/components/Pagination/index.vue';
import RightToolbar from '@/components/RightToolbar/index.vue';
@ -369,14 +369,14 @@ const initFormData: any = {
inputFile: undefined,
// state: '0', // 新增默认待审核
// 新增:用于表单输入的单独字段
build1: undefined, // 一建建造师
build2: undefined, // 二建建造师
build3: undefined, // 注册造价工程师
build4: undefined, // 其他注册人员
personnelNumber1: undefined, // 高级工程师
personnelNumber2: undefined, // 工程师
personnelNumber3: undefined, // 助理工程师
personnelNumber4: undefined // 其他职称人员
firstBuildingNumber: undefined, // 一建建造师
secondBuildingNumber: undefined, // 二建建造师
registeredEngineerNumber: undefined, // 注册造价工程师
otherBuildingNumber: undefined, // 其他注册人员
seniorEngineerNumber: undefined, // 高级工程师
engineerNumber: undefined, // 工程师
assistantEngineerNumber: undefined, // 助理工程师
otherPersonnelNumber: undefined // 其他职称人员
};
// 核心数据(表单+查询参数)
const data = reactive<PageData<SupplierInputForm, SupplierInputQuery>>({
@ -411,14 +411,14 @@ const rules = computed(() => {
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
],
id: [{ required: true, message: 'ID不能为空', trigger: 'blur' }],
build1: [{ required: true, message: '请输入一建建造师数量', trigger: 'change' }],
build2: [{ required: true, message: '请输入二建建造师数量', trigger: 'change' }],
build3: [{ required: true, message: '请输入注册造价工程师数量', trigger: 'change' }],
build4: [{ required: true, message: '请输入其他数量', trigger: 'change' }],
personnelNumber1: [{ required: true, message: '请输入高级工程师数量', trigger: 'change' }],
personnelNumber2: [{ required: true, message: '请输入工程师数量', trigger: 'change' }],
personnelNumber3: [{ required: true, message: '请输入助理工程师数量', trigger: 'change' }],
personnelNumber4: [{ required: true, message: '请输入其他数量', trigger: 'change' }]
firstBuildingNumber: [{ required: true, message: '请输入一建建造师数量', trigger: 'change' }],
secondBuildingNumber: [{ required: true, message: '请输入二建建造师数量', trigger: 'change' }],
registeredEngineerNumber: [{ required: true, message: '请输入注册造价工程师数量', trigger: 'change' }],
otherBuildingNumber: [{ required: true, message: '请输入其他数量', trigger: 'change' }],
seniorEngineerNumber: [{ required: true, message: '请输入高级工程师数量', trigger: 'change' }],
engineerNumber: [{ required: true, message: '请输入工程师数量', trigger: 'change' }],
assistantEngineerNumber: [{ required: true, message: '请输入助理工程师数量', trigger: 'change' }],
otherPersonnelNumber: [{ required: true, message: '请输入其他数量', trigger: 'change' }]
};
// 仅当类型为"劳务"时,添加安全生产许可证+人员数量校验
@ -430,15 +430,15 @@ const rules = computed(() => {
safeCodeData: [{ required: true, message: '请选择安全生产许可证发证日期', trigger: 'change' }],
safeCertificateValidity: [{ required: true, message: '请选择安全生产许可证有效期', trigger: 'change' }],
// 注册人员数量校验
build1: [{ required: true, message: '请输入一建建造师数量', trigger: 'blur' }],
build2: [{ required: true, message: '请输入二建建造师数量', trigger: 'blur' }],
build3: [{ required: true, message: '请输入注册造价工程师数量', trigger: 'blur' }],
build4: [{ required: true, message: '请输入其他注册人员数量', trigger: 'blur' }],
firstBuildingNumber: [{ required: true, message: '请输入一建建造师数量', trigger: 'blur' }],
secondBuildingNumber: [{ required: true, message: '请输入二建建造师数量', trigger: 'blur' }],
registeredEngineerNumber: [{ required: true, message: '请输入注册造价工程师数量', trigger: 'blur' }],
otherBuildingNumber: [{ required: true, message: '请输入其他注册人员数量', trigger: 'blur' }],
// 职称人员数量校验
personnelNumber1: [{ required: true, message: '请输入高级工程师数量', trigger: 'blur' }],
personnelNumber2: [{ required: true, message: '请输入工程师数量', trigger: 'blur' }],
personnelNumber3: [{ required: true, message: '请输入助理工程师数量', trigger: 'blur' }],
personnelNumber4: [{ required: true, message: '请输入其他职称人员数量', trigger: 'blur' }]
seniorEngineerNumber: [{ required: true, message: '请输入高级工程师数量', trigger: 'blur' }],
engineerNumber: [{ required: true, message: '请输入工程师数量', trigger: 'blur' }],
assistantEngineerNumber: [{ required: true, message: '请输入助理工程师数量', trigger: 'blur' }],
otherPersonnelNumber: [{ required: true, message: '请输入其他职称人员数量', trigger: 'blur' }]
};
}
@ -455,22 +455,26 @@ const handleTypeChange = () => {
form.value.registeredNumber = undefined;
form.value.personnelNumber = undefined;
// 清空表单单独字段
form.value.build1 = form.value.build2 = form.value.build3 = form.value.build4 = undefined;
form.value.personnelNumber1 = form.value.personnelNumber2 = form.value.personnelNumber3 = form.value.personnelNumber4 = undefined;
form.value.firstBuildingNumber =
form.value.secondBuildingNumber =
form.value.registeredEngineerNumber =
form.value.otherBuildingNumber =
undefined;
form.value.seniorEngineerNumber = form.value.engineerNumber = form.value.assistantEngineerNumber = form.value.otherPersonnelNumber = undefined;
}
// 重置隐藏字段的校验状态,避免错误提示残留
supplierInputFormRef.value?.clearValidate([
'safeCode',
'safeCodeData',
'safeCertificateValidity',
'build1',
'build2',
'build3',
'build4',
'personnelNumber1',
'personnelNumber2',
'personnelNumber3',
'personnelNumber4'
'firstBuildingNumber',
'secondBuildingNumber',
'registeredEngineerNumber',
'otherBuildingNumber',
'seniorEngineerNumber',
'engineerNumber',
'assistantEngineerNumber',
'otherPersonnelNumber'
]);
};
@ -517,18 +521,18 @@ const resetQuery = () => {
const splitBackEndStrToForm = (resData: any) => {
if (resData.registeredNumber) {
const registeredArr = resData.registeredNumber.split(',');
form.value.build1 = registeredArr[0] || undefined; // 一建建造师
form.value.build2 = registeredArr[1] || undefined; // 二建建造师
form.value.build3 = registeredArr[2] || undefined; // 注册造价工程师
form.value.build4 = registeredArr[3] || undefined; // 其他注册人员
form.value.firstBuildingNumber = registeredArr[0] || undefined; // 一建建造师
form.value.secondBuildingNumber = registeredArr[1] || undefined; // 二建建造师
form.value.registeredEngineerNumber = registeredArr[2] || undefined; // 注册造价工程师
form.value.otherBuildingNumber = registeredArr[3] || undefined; // 其他注册人员
}
if (resData.personnelNumber) {
const personnelArr = resData.personnelNumber.split(',');
form.value.personnelNumber1 = personnelArr[0] || undefined; // 高级工程师
form.value.personnelNumber2 = personnelArr[1] || undefined; // 工程师
form.value.personnelNumber3 = personnelArr[2] || undefined; // 助理工程师
form.value.personnelNumber4 = personnelArr[3] || undefined; // 其他职称人员
form.value.seniorEngineerNumber = personnelArr[0] || undefined; // 高级工程师
form.value.engineerNumber = personnelArr[1] || undefined; // 工程师
form.value.assistantEngineerNumber = personnelArr[2] || undefined; // 助理工程师
form.value.otherPersonnelNumber = personnelArr[3] || undefined; // 其他职称人员
}
};
/** 审核过程按钮操作 */
@ -572,6 +576,7 @@ const handleAdd = () => {
dialog.title = '添加供应商入库';
};
const editFileId = ref('');
const handleUpdate = async (row?: SupplierInputVO) => {
reset();
const _id = row?.id || ids.value[0];
@ -579,7 +584,8 @@ const handleUpdate = async (row?: SupplierInputVO) => {
try {
const res = await getSupplierInput(_id);
const resData = res.data || {};
const resData: any = res.data || {};
editFileId.value = resData.fileId;
// 1. 基础字段回显
form.value = { ...form.value, ...resData, inputFile: '' };
// 2. 核心修复:拆分后端拼接字符串到表单单独字段
@ -594,32 +600,61 @@ const handleUpdate = async (row?: SupplierInputVO) => {
}
};
const fileStatus = ref(false);
const updateFileStatus = ref(true);
const isUpdateFile = ref(false); //记录是否在修改页面时是否有新上传的文件
const handleFileChange = (file, fileList) => {
if (form.value.id) {
updateFileStatus.value = true;
isUpdateFile.value = true; //记录是否在修改页面时是否有新上传的文件
}
fileStatus.value = true;
};
const handleFileRemove = (file, fileList) => {
if (form.value.id) {
updateFileStatus.value = false;
isUpdateFile.value = false; //记录是否在修改页面时是否有新上传的文件
}
fileStatus.value = false;
};
/** 提交表单 */
const submitForm = () => {
supplierInputFormRef.value?.validate(async (valid: boolean) => {
if (!valid) return;
if (form.value.supplierType === '劳务') {
form.value.registeredNumber = [form.value.build1, form.value.build2, form.value.build3, form.value.build4].join(',');
form.value.registeredNumber = [
form.value.firstBuildingNumber,
form.value.secondBuildingNumber,
form.value.registeredEngineerNumber,
form.value.otherBuildingNumber
].join(',');
form.value.personnelNumber = [
form.value.personnelNumber1,
form.value.personnelNumber2,
form.value.personnelNumber3,
form.value.personnelNumber4
form.value.seniorEngineerNumber,
form.value.engineerNumber,
form.value.assistantEngineerNumber,
form.value.otherPersonnelNumber
].join(',');
}
buttonLoading.value = true;
try {
if (fileUploadRef.value) {
await fileUploadRef.value.submitUpload().then((res) => {
console.log(res);
if (res == 'noFile') {
proxy?.$modal.msgError('请上传文件');
return;
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
getList();
});
if (form.value.fileId === editFileId.value && !isUpdateFile.value) {
console.log(1111111111);
editFormData();
} else {
fileUploadRef.value.submitUpload().then((res) => {
if (res == 'noFile') {
proxy?.$modal.msgError('请上传文件');
return;
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
getList();
});
}
}
} catch (error) {
proxy?.$modal.msgError('提交失败,请重试');
@ -628,7 +663,14 @@ const submitForm = () => {
}
});
};
const editFormData = async () => {
const res = await updateSupplierInput(form.value);
if ((res.code = 200)) {
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
getList();
}
};
/** 删除操作 */
const handleDelete = async (row?: SupplierInputVO) => {
const _ids = row?.id || ids.value;

View File

@ -122,41 +122,41 @@
</el-row>
<el-row class="mb-4" v-if="form.supplierType === '劳务'">
<el-col :span="12">
<el-form-item label="一建建造师" prop="build1">
<el-input v-model="form.build1" placeholder="请输入一建建造师数量" clearable />
<el-form-item label="一建建造师" prop="firstBuildingNumber">
<el-input v-model="form.firstBuildingNumber" placeholder="请输入一建建造师数量" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="二建建造师" prop="build2">
<el-input v-model="form.build2" placeholder="请输入二建建造师数量" clearable />
<el-form-item label="二建建造师" prop="secondBuildingNumber">
<el-input v-model="form.secondBuildingNumber" placeholder="请输入二建建造师数量" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="其他(分别写)" prop="build4">
<el-input v-model="form.build3" placeholder="请输入其他人员数量" clearable />
<el-form-item label="其他(分别写)" prop="otherBuildingNumber">
<el-input v-model="form.otherBuildingNumber" placeholder="请输入其他人员数量" clearable />
</el-form-item> </el-col
><el-col :span="12">
<el-form-item label="注册造价工程师" prop="build3">
<el-input v-model="form.build4" placeholder="请输入注册造价工程师数量" clearable />
<el-form-item label="注册造价工程师" prop="registeredEngineerNumber">
<el-input v-model="form.registeredEngineerNumber" placeholder="请输入注册造价工程师数量" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24" class="mb-4" v-if="form.supplierType === '劳务'">
<el-col :span="12">
<el-form-item label="高级工程师人数" prop="personnelNumber1">
<el-input v-model="form.personnelNumber1" placeholder="请输高级工程师数量" clearable />
<el-form-item label="高级工程师人数" prop="seniorEngineerNumber">
<el-input v-model="form.seniorEngineerNumber" placeholder="请输高级工程师数量" clearable />
</el-form-item> </el-col
><el-col :span="12">
<el-form-item label="工程师数量" prop="personnelNumber2">
<el-input v-model="form.personnelNumber2" placeholder="请输入工程师数量" clearable />
<el-form-item label="工程师数量" prop="engineerNumber">
<el-input v-model="form.engineerNumber" placeholder="请输入工程师数量" clearable />
</el-form-item> </el-col
><el-col :span="12">
<el-form-item label="助理工程师数量" prop="personnelNumber3">
<el-input v-model="form.personnelNumber3" placeholder="请输入助理工程师数量" clearable />
<el-form-item label="助理工程师数量" prop="assistantEngineerNumber">
<el-input v-model="form.assistantEngineerNumber" placeholder="请输入助理工程师数量" clearable />
</el-form-item> </el-col
><el-col :span="12">
<el-form-item label="其他人员数量" prop="personnelNumber4">
<el-input v-model="form.personnelNumber4" placeholder="请输入其他人员数量" clearable />
<el-form-item label="其他人员数量" prop="otherPersonnelNumber">
<el-input v-model="form.otherPersonnelNumber" placeholder="请输入其他人员数量" clearable />
</el-form-item>
</el-col>
</el-row>
@ -270,14 +270,14 @@ const initFormData = {
inputFile: undefined,
state: '0', // 新增默认待审核
// 新增:用于表单输入的单独字段
build1: undefined, // 一建建造师
build2: undefined, // 二建建造师
build3: undefined, // 注册造价工程师
build4: undefined, // 其他注册人员
personnelNumber1: undefined, // 高级工程师
personnelNumber2: undefined, // 工程师
personnelNumber3: undefined, // 助理工程师
personnelNumber4: undefined // 其他职称人员
firstBuildingNumber: undefined, // 一建建造师
secondBuildingNumber: undefined, // 二建建造师
registeredEngineerNumber: undefined, // 注册造价工程师
otherBuildingNumber: undefined, // 其他注册人员
seniorEngineerNumber: undefined, // 高级工程师
engineerNumber: undefined, // 工程师
assistantEngineerNumber: undefined, // 助理工程师
otherPersonnelNumber: undefined
};
const data = reactive<PageData<LeaveForm, LeaveQuery>>({
form: { ...initFormData },
@ -332,17 +332,27 @@ const getInfo = () => {
buttonLoading.value = false;
nextTick(async () => {
const res = await getSupplierInput(routeParams.value.id);
console.log(res, '------------------res');
Object.assign(form.value, res.data);
form.value.registeredNumber = form.value.registeredNumber?.split(',');
form.value.build1 = form.value.registeredNumber[0] || '';
form.value.build2 = form.value.registeredNumber[1] || '';
form.value.build3 = form.value.registeredNumber[2] || '';
form.value.build4 = form.value.registeredNumber[3] || '';
form.value.personnelNumber = form.value.personnelNumber?.split(',');
form.value.personnelNumber1 = form.value.personnelNumber[0] || '';
form.value.personnelNumber2 = form.value.personnelNumber[1] || '';
form.value.personnelNumber3 = form.value.personnelNumber[2] || '';
form.value.personnelNumber4 = form.value.personnelNumber[3] || '';
// form.value.firstBuildingNumber=res.data.firstBuildingNumber, // 一建建造师
// secondBuildingNumber: undefined, // 二建建造师
// registeredEngineerNumber: undefined, // 注册造价工程师
// otherBuildingNumber: undefined, // 其他注册人员
// seniorEngineerNumber: undefined, // 高级工程师
// engineerNumber: undefined, // 工程师
// assistantEngineerNumber: undefined, // 助理工程师
// otherPersonnelNumber: undefined
// form.value.registeredNumber = form.value.registeredNumber?.split(',');
// form.value.build1 = form.value.registeredNumber[0] || '';
// form.value.build2 = form.value.registeredNumber[1] || '';
// form.value.build3 = form.value.registeredNumber[2] || '';
// form.value.build4 = form.value.registeredNumber[3] || '';
// form.value.personnelNumber = form.value.personnelNumber?.split(',');
// form.value.personnelNumber1 = form.value.personnelNumber[0] || '';
// form.value.personnelNumber2 = form.value.personnelNumber[1] || '';
// form.value.personnelNumber3 = form.value.personnelNumber[2] || '';
// form.value.personnelNumber4 = form.value.personnelNumber[3] || '';
loading.value = false;
buttonLoading.value = false;

File diff suppressed because one or more lines are too long