员工亲属实体关联
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -89,4 +89,8 @@ ruoyi-ui/vue.config.js
|
||||
|
||||
tests/
|
||||
|
||||
tongweb_62318.properties
|
||||
tongweb_62318.properties
|
||||
|
||||
.superpowers/
|
||||
|
||||
tmp/
|
||||
@@ -4,8 +4,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO;
|
||||
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
|
||||
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
|
||||
import com.ruoyi.info.collection.domain.vo.*;
|
||||
import com.ruoyi.info.collection.service.ICcdiBaseStaffAssetImportService;
|
||||
import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService;
|
||||
import com.ruoyi.info.collection.service.ICcdiBaseStaffService;
|
||||
import com.ruoyi.info.collection.utils.EasyExcelUtil;
|
||||
@@ -45,6 +47,9 @@ public class CcdiBaseStaffController extends BaseController {
|
||||
@Resource
|
||||
private ICcdiBaseStaffImportService importAsyncService;
|
||||
|
||||
@Resource
|
||||
private ICcdiBaseStaffAssetImportService baseStaffAssetImportService;
|
||||
|
||||
/**
|
||||
* 查询员工列表
|
||||
*/
|
||||
@@ -120,7 +125,14 @@ public class CcdiBaseStaffController extends BaseController {
|
||||
@Operation(summary = "下载导入模板")
|
||||
@PostMapping("/importTemplate")
|
||||
public void importTemplate(HttpServletResponse response) {
|
||||
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiBaseStaffExcel.class, "员工信息");
|
||||
EasyExcelUtil.importTemplateWithDictDropdown(
|
||||
response,
|
||||
CcdiBaseStaffExcel.class,
|
||||
"员工信息",
|
||||
CcdiBaseStaffAssetInfoExcel.class,
|
||||
"员工资产信息",
|
||||
"员工信息维护导入模板"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,21 +142,33 @@ public class CcdiBaseStaffController extends BaseController {
|
||||
@PreAuthorize("@ss.hasPermi('ccdi:baseStaff:import')")
|
||||
@Log(title = "员工信息", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importData")
|
||||
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
|
||||
List<CcdiBaseStaffExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiBaseStaffExcel.class);
|
||||
public AjaxResult importData(MultipartFile file) throws Exception {
|
||||
List<CcdiBaseStaffExcel> staffList = EasyExcelUtil.importExcel(
|
||||
file.getInputStream(),
|
||||
CcdiBaseStaffExcel.class,
|
||||
"员工信息"
|
||||
);
|
||||
List<CcdiBaseStaffAssetInfoExcel> assetList = EasyExcelUtil.importExcel(
|
||||
file.getInputStream(),
|
||||
CcdiBaseStaffAssetInfoExcel.class,
|
||||
"员工资产信息"
|
||||
);
|
||||
|
||||
if (list == null || list.isEmpty()) {
|
||||
boolean hasStaffRows = staffList != null && !staffList.isEmpty();
|
||||
boolean hasAssetRows = assetList != null && !assetList.isEmpty();
|
||||
|
||||
if (!hasStaffRows && !hasAssetRows) {
|
||||
return error("至少需要一条数据");
|
||||
}
|
||||
|
||||
// 提交异步任务
|
||||
String taskId = baseStaffService.importBaseStaff(list, updateSupport);
|
||||
|
||||
// 立即返回,不等待后台任务完成
|
||||
ImportResultVO result = new ImportResultVO();
|
||||
result.setTaskId(taskId);
|
||||
result.setStatus("PROCESSING");
|
||||
result.setMessage("导入任务已提交,正在后台处理");
|
||||
BaseStaffImportSubmitResultVO result = new BaseStaffImportSubmitResultVO();
|
||||
if (hasStaffRows) {
|
||||
result.setStaffTaskId(baseStaffService.importBaseStaff(staffList));
|
||||
}
|
||||
if (hasAssetRows) {
|
||||
result.setAssetTaskId(baseStaffAssetImportService.importAssetInfo(assetList));
|
||||
}
|
||||
result.setMessage(buildImportSubmitMessage(hasStaffRows, hasAssetRows));
|
||||
|
||||
return AjaxResult.success("导入任务已提交,正在后台处理", result);
|
||||
}
|
||||
@@ -190,4 +214,14 @@ public class CcdiBaseStaffController extends BaseController {
|
||||
|
||||
return getDataTable(pageData, failures.size());
|
||||
}
|
||||
|
||||
private String buildImportSubmitMessage(boolean hasStaffRows, boolean hasAssetRows) {
|
||||
if (hasStaffRows && hasAssetRows) {
|
||||
return "已提交员工信息和员工资产信息导入任务";
|
||||
}
|
||||
if (hasStaffRows) {
|
||||
return "已提交员工信息导入任务";
|
||||
}
|
||||
return "已提交员工资产信息导入任务";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationQueryDTO;
|
||||
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
|
||||
import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel;
|
||||
import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO;
|
||||
import com.ruoyi.info.collection.domain.vo.ImportResultVO;
|
||||
import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
|
||||
import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportFailureVO;
|
||||
import com.ruoyi.info.collection.domain.vo.StaffFmyRelationImportSubmitResultVO;
|
||||
import com.ruoyi.info.collection.service.ICcdiAssetInfoImportService;
|
||||
import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationImportService;
|
||||
import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationService;
|
||||
import com.ruoyi.info.collection.utils.EasyExcelUtil;
|
||||
@@ -49,6 +51,9 @@ public class CcdiStaffFmyRelationController extends BaseController {
|
||||
@Resource
|
||||
private ICcdiStaffFmyRelationImportService relationImportService;
|
||||
|
||||
@Resource
|
||||
private ICcdiAssetInfoImportService assetInfoImportService;
|
||||
|
||||
/**
|
||||
* 查询员工亲属关系列表
|
||||
*/
|
||||
@@ -115,7 +120,14 @@ public class CcdiStaffFmyRelationController extends BaseController {
|
||||
@Operation(summary = "下载导入模板")
|
||||
@PostMapping("/importTemplate")
|
||||
public void importTemplate(HttpServletResponse response) {
|
||||
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiStaffFmyRelationExcel.class, "员工亲属关系信息");
|
||||
EasyExcelUtil.importTemplateWithDictDropdown(
|
||||
response,
|
||||
CcdiStaffFmyRelationExcel.class,
|
||||
"员工亲属关系信息",
|
||||
CcdiAssetInfoExcel.class,
|
||||
"亲属资产信息",
|
||||
"员工亲属关系维护导入模板"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,20 +139,32 @@ public class CcdiStaffFmyRelationController extends BaseController {
|
||||
@Log(title = "员工亲属关系", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importData")
|
||||
public AjaxResult importData(@Parameter(description = "导入文件") MultipartFile file) throws Exception {
|
||||
List<CcdiStaffFmyRelationExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiStaffFmyRelationExcel.class);
|
||||
List<CcdiStaffFmyRelationExcel> relationList = EasyExcelUtil.importExcel(
|
||||
file.getInputStream(),
|
||||
CcdiStaffFmyRelationExcel.class,
|
||||
"员工亲属关系信息"
|
||||
);
|
||||
List<CcdiAssetInfoExcel> assetList = EasyExcelUtil.importExcel(
|
||||
file.getInputStream(),
|
||||
CcdiAssetInfoExcel.class,
|
||||
"亲属资产信息"
|
||||
);
|
||||
|
||||
if (list == null || list.isEmpty()) {
|
||||
boolean hasRelationRows = relationList != null && !relationList.isEmpty();
|
||||
boolean hasAssetRows = assetList != null && !assetList.isEmpty();
|
||||
|
||||
if (!hasRelationRows && !hasAssetRows) {
|
||||
return error("至少需要一条数据");
|
||||
}
|
||||
|
||||
// 提交异步任务
|
||||
String taskId = relationService.importRelation(list);
|
||||
|
||||
// 立即返回,不等待后台任务完成
|
||||
ImportResultVO result = new ImportResultVO();
|
||||
result.setTaskId(taskId);
|
||||
result.setStatus("PROCESSING");
|
||||
result.setMessage("导入任务已提交,正在后台处理");
|
||||
StaffFmyRelationImportSubmitResultVO result = new StaffFmyRelationImportSubmitResultVO();
|
||||
if (hasRelationRows) {
|
||||
result.setRelationTaskId(relationService.importRelation(relationList));
|
||||
}
|
||||
if (hasAssetRows) {
|
||||
result.setAssetTaskId(assetInfoImportService.importAssetInfo(assetList));
|
||||
}
|
||||
result.setMessage(buildImportSubmitMessage(hasRelationRows, hasAssetRows));
|
||||
|
||||
return AjaxResult.success("导入任务已提交,正在后台处理", result);
|
||||
}
|
||||
@@ -186,4 +210,14 @@ public class CcdiStaffFmyRelationController extends BaseController {
|
||||
|
||||
return getDataTable(pageData, failures.size());
|
||||
}
|
||||
|
||||
private String buildImportSubmitMessage(boolean hasRelationRows, boolean hasAssetRows) {
|
||||
if (hasRelationRows && hasAssetRows) {
|
||||
return "已提交员工亲属关系和亲属资产信息导入任务";
|
||||
}
|
||||
if (hasRelationRows) {
|
||||
return "已提交员工亲属关系导入任务";
|
||||
}
|
||||
return "已提交亲属资产信息导入任务";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package com.ruoyi.info.collection.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
@@ -20,29 +18,19 @@ public class CcdiPurchaseTransactionSupplierDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotBlank(message = "供应商名称不能为空")
|
||||
@Size(max = 200, message = "供应商名称长度不能超过200个字符")
|
||||
@Schema(description = "供应商名称")
|
||||
private String supplierName;
|
||||
|
||||
@Pattern(
|
||||
regexp = "^$|^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$",
|
||||
message = "供应商统一信用代码格式不正确"
|
||||
)
|
||||
@NotBlank(message = "供应商统一信用代码不能为空")
|
||||
@Schema(description = "供应商统一信用代码")
|
||||
private String supplierUscc;
|
||||
|
||||
@Size(max = 50, message = "供应商联系人长度不能超过50个字符")
|
||||
@Schema(description = "供应商联系人")
|
||||
private String contactPerson;
|
||||
|
||||
@Pattern(
|
||||
regexp = "^$|^1[3-9]\\d{9}$|^0\\d{2,3}-?\\d{7,8}$",
|
||||
message = "供应商联系电话格式不正确"
|
||||
)
|
||||
@Schema(description = "供应商联系电话")
|
||||
private String contactPhone;
|
||||
|
||||
@Size(max = 50, message = "供应商银行账户长度不能超过50个字符")
|
||||
@Schema(description = "供应商银行账户")
|
||||
private String supplierBankAccount;
|
||||
|
||||
|
||||
@@ -15,6 +15,14 @@ import java.math.BigDecimal;
|
||||
@Schema(description = "亲属资产信息导入失败记录")
|
||||
public class AssetImportFailureVO {
|
||||
|
||||
/** Sheet名称 */
|
||||
@Schema(description = "Sheet名称")
|
||||
private String sheetName;
|
||||
|
||||
/** Excel行号 */
|
||||
@Schema(description = "Excel行号")
|
||||
private Integer rowNum;
|
||||
|
||||
/** 亲属证件号 */
|
||||
@Schema(description = "亲属证件号")
|
||||
private String personId;
|
||||
|
||||
@@ -15,6 +15,14 @@ import java.math.BigDecimal;
|
||||
@Schema(description = "员工资产信息导入失败记录")
|
||||
public class BaseStaffAssetImportFailureVO {
|
||||
|
||||
/** Sheet名称 */
|
||||
@Schema(description = "Sheet名称")
|
||||
private String sheetName;
|
||||
|
||||
/** Excel行号 */
|
||||
@Schema(description = "Excel行号")
|
||||
private Integer rowNum;
|
||||
|
||||
/** 员工身份证号 */
|
||||
@Schema(description = "员工身份证号")
|
||||
private String personId;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.ruoyi.info.collection.domain.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 员工双Sheet导入提交结果
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "员工双Sheet导入提交结果")
|
||||
public class BaseStaffImportSubmitResultVO {
|
||||
|
||||
@Schema(description = "员工信息导入任务ID")
|
||||
private String staffTaskId;
|
||||
|
||||
@Schema(description = "员工资产信息导入任务ID")
|
||||
private String assetTaskId;
|
||||
|
||||
@Schema(description = "提交说明")
|
||||
private String message;
|
||||
}
|
||||
@@ -14,8 +14,14 @@ import java.math.BigDecimal;
|
||||
@Schema(description = "导入失败记录")
|
||||
public class ImportFailureVO {
|
||||
|
||||
@Schema(description = "Sheet名称")
|
||||
private String sheetName;
|
||||
|
||||
@Schema(description = "Excel行号")
|
||||
private Integer rowNum;
|
||||
|
||||
@Schema(description = "柜员号")
|
||||
private Long employeeId;
|
||||
private Long staffId;
|
||||
|
||||
@Schema(description = "姓名")
|
||||
private String name;
|
||||
|
||||
@@ -15,6 +15,14 @@ import java.math.BigDecimal;
|
||||
@Schema(description = "招投标信息导入失败记录")
|
||||
public class PurchaseTransactionImportFailureVO {
|
||||
|
||||
/** 失败来源Sheet */
|
||||
@Schema(description = "失败来源Sheet")
|
||||
private String sheetName;
|
||||
|
||||
/** 失败行号 */
|
||||
@Schema(description = "失败行号")
|
||||
private String sheetRowNum;
|
||||
|
||||
/** 采购事项ID */
|
||||
@Schema(description = "采购事项ID")
|
||||
private String purchaseId;
|
||||
|
||||
@@ -15,6 +15,14 @@ import java.math.BigDecimal;
|
||||
@Schema(description = "员工亲属关系信息导入失败记录")
|
||||
public class StaffFmyRelationImportFailureVO {
|
||||
|
||||
/** Sheet名称 */
|
||||
@Schema(description = "Sheet名称")
|
||||
private String sheetName;
|
||||
|
||||
/** Excel行号 */
|
||||
@Schema(description = "Excel行号")
|
||||
private Integer rowNum;
|
||||
|
||||
/** 员工身份证号 */
|
||||
@Schema(description = "员工身份证号")
|
||||
private String personId;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.ruoyi.info.collection.domain.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 员工亲属关系双Sheet导入提交结果
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-22
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "员工亲属关系双Sheet导入提交结果")
|
||||
public class StaffFmyRelationImportSubmitResultVO {
|
||||
|
||||
@Schema(description = "员工亲属关系导入任务ID")
|
||||
private String relationTaskId;
|
||||
|
||||
@Schema(description = "亲属资产信息导入任务ID")
|
||||
private String assetTaskId;
|
||||
|
||||
@Schema(description = "提交结果提示")
|
||||
private String message;
|
||||
}
|
||||
@@ -15,10 +15,9 @@ public interface ICcdiBaseStaffImportService {
|
||||
/**
|
||||
* 异步导入员工数据
|
||||
*
|
||||
* @param excelList Excel数据列表
|
||||
* @param isUpdateSupport 是否更新已存在的数据
|
||||
* @param excelList Excel数据列表
|
||||
*/
|
||||
void importBaseStaffAsync(List<CcdiBaseStaffExcel> excelList, Boolean isUpdateSupport, String taskId);
|
||||
void importBaseStaffAsync(List<CcdiBaseStaffExcel> excelList, String taskId);
|
||||
|
||||
/**
|
||||
* 查询导入状态
|
||||
|
||||
@@ -78,11 +78,10 @@ public interface ICcdiBaseStaffService {
|
||||
/**
|
||||
* 导入员工数据
|
||||
*
|
||||
* @param excelList Excel实体列表
|
||||
* @param isUpdateSupport 是否更新支持
|
||||
* @param excelList Excel实体列表
|
||||
* @return 结果
|
||||
*/
|
||||
String importBaseStaff(List<CcdiBaseStaffExcel> excelList, Boolean isUpdateSupport);
|
||||
String importBaseStaff(List<CcdiBaseStaffExcel> excelList);
|
||||
|
||||
/**
|
||||
* 查询员工下拉列表
|
||||
|
||||
@@ -39,6 +39,8 @@ import java.util.concurrent.TimeUnit;
|
||||
public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportService {
|
||||
|
||||
private static final String STATUS_KEY_PREFIX = "import:assetInfo:";
|
||||
private static final String SHEET_NAME = "亲属资产信息";
|
||||
private static final int EXCEL_DATA_START_ROW = 2;
|
||||
|
||||
@Resource
|
||||
private CcdiAssetInfoMapper assetInfoMapper;
|
||||
@@ -91,7 +93,8 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
|
||||
|
||||
Map<String, Set<String>> ownerMap = buildOwnerMap(personIds);
|
||||
|
||||
for (CcdiAssetInfoExcel excel : excelList) {
|
||||
for (int i = 0; i < excelList.size(); i++) {
|
||||
CcdiAssetInfoExcel excel = excelList.get(i);
|
||||
try {
|
||||
validateExcel(excel);
|
||||
Set<String> familyIds = ownerMap.get(excel.getPersonId());
|
||||
@@ -111,6 +114,8 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
|
||||
} catch (Exception e) {
|
||||
AssetImportFailureVO failureVO = new AssetImportFailureVO();
|
||||
BeanUtils.copyProperties(excel, failureVO);
|
||||
failureVO.setSheetName(SHEET_NAME);
|
||||
failureVO.setRowNum(i + EXCEL_DATA_START_ROW);
|
||||
failureVO.setErrorMessage(e.getMessage());
|
||||
failures.add(failureVO);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.info.collection.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.info.collection.domain.CcdiAssetInfo;
|
||||
@@ -90,14 +91,24 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
|
||||
.toList();
|
||||
|
||||
Map<String, Set<String>> ownerMap = buildOwnerMap(personIds);
|
||||
Set<String> existingAssetKeys = buildExistingAssetKeys(personIds);
|
||||
Set<String> importedAssetKeys = new java.util.LinkedHashSet<>();
|
||||
|
||||
for (CcdiBaseStaffAssetInfoExcel excel : excelList) {
|
||||
for (int i = 0; i < excelList.size(); i++) {
|
||||
CcdiBaseStaffAssetInfoExcel excel = excelList.get(i);
|
||||
try {
|
||||
validateExcel(excel);
|
||||
Set<String> familyIds = ownerMap.get(excel.getPersonId());
|
||||
if (familyIds == null || familyIds.isEmpty()) {
|
||||
throw new RuntimeException("员工资产导入仅支持员工本人证件号");
|
||||
}
|
||||
String assetKey = buildAssetKey(excel.getPersonId(), excel.getAssetMainType(), excel.getAssetSubType(), excel.getAssetName());
|
||||
if (existingAssetKeys.contains(assetKey)) {
|
||||
throw new RuntimeException("资产记录已存在");
|
||||
}
|
||||
if (!importedAssetKeys.add(assetKey)) {
|
||||
throw new RuntimeException("资产记录在导入文件中重复");
|
||||
}
|
||||
|
||||
CcdiAssetInfo assetInfo = new CcdiAssetInfo();
|
||||
BeanUtils.copyProperties(excel, assetInfo);
|
||||
@@ -109,6 +120,8 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
|
||||
} catch (Exception e) {
|
||||
BaseStaffAssetImportFailureVO failureVO = new BaseStaffAssetImportFailureVO();
|
||||
BeanUtils.copyProperties(excel, failureVO);
|
||||
failureVO.setSheetName("员工资产信息");
|
||||
failureVO.setRowNum(i + 2);
|
||||
failureVO.setErrorMessage(e.getMessage());
|
||||
failures.add(failureVO);
|
||||
}
|
||||
@@ -168,6 +181,18 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<String> buildExistingAssetKeys(List<String> personIds) {
|
||||
if (personIds == null || personIds.isEmpty()) {
|
||||
return Set.of();
|
||||
}
|
||||
LambdaQueryWrapper<CcdiAssetInfo> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.in(CcdiAssetInfo::getPersonId, personIds);
|
||||
return assetInfoMapper.selectList(wrapper).stream()
|
||||
.filter(asset -> StringUtils.equals(asset.getFamilyId(), asset.getPersonId()))
|
||||
.map(asset -> buildAssetKey(asset.getPersonId(), asset.getAssetMainType(), asset.getAssetSubType(), asset.getAssetName()))
|
||||
.collect(java.util.stream.Collectors.toCollection(java.util.LinkedHashSet::new));
|
||||
}
|
||||
|
||||
private void mergeOwnerMappings(Map<String, Set<String>> result, List<Map<String, String>> mappings) {
|
||||
if (mappings == null) {
|
||||
return;
|
||||
@@ -203,6 +228,14 @@ public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetI
|
||||
}
|
||||
}
|
||||
|
||||
private String buildAssetKey(String personId, String assetMainType, String assetSubType, String assetName) {
|
||||
return String.join("|",
|
||||
StringUtils.nvl(personId, ""),
|
||||
StringUtils.nvl(assetMainType, ""),
|
||||
StringUtils.nvl(assetSubType, ""),
|
||||
StringUtils.nvl(assetName, ""));
|
||||
}
|
||||
|
||||
private void updateImportStatus(String taskId, String status, ImportResult result) {
|
||||
Map<String, Object> statusData = new HashMap<>();
|
||||
statusData.put("status", status);
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.ruoyi.info.collection.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.common.core.domain.entity.SysDept;
|
||||
import com.ruoyi.info.collection.domain.CcdiBaseStaff;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO;
|
||||
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
|
||||
@@ -13,6 +14,7 @@ import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService;
|
||||
import com.ruoyi.info.collection.utils.ImportLogUtils;
|
||||
import com.ruoyi.common.utils.IdCardUtil;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.system.mapper.SysDeptMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -43,16 +45,18 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Resource
|
||||
private SysDeptMapper deptMapper;
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public void importBaseStaffAsync(List<CcdiBaseStaffExcel> excelList, Boolean isUpdateSupport, String taskId) {
|
||||
public void importBaseStaffAsync(List<CcdiBaseStaffExcel> excelList, String taskId) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 记录导入开始
|
||||
ImportLogUtils.logImportStart(log, taskId, "员工基础信息", excelList.size(), "系统");
|
||||
|
||||
List<CcdiBaseStaff> newRecords = new ArrayList<>();
|
||||
List<CcdiBaseStaff> updateRecords = new ArrayList<>();
|
||||
List<ImportFailureVO> failures = new ArrayList<>();
|
||||
|
||||
// 批量查询已存在的员工ID和身份证号
|
||||
@@ -75,13 +79,12 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
||||
CcdiBaseStaffAddDTO addDTO = new CcdiBaseStaffAddDTO();
|
||||
BeanUtils.copyProperties(excel, addDTO);
|
||||
|
||||
// 验证数据(支持更新模式)
|
||||
validateStaffData(addDTO, isUpdateSupport, existingIds, existingIdCards);
|
||||
validateStaffData(addDTO, existingIds, existingIdCards);
|
||||
|
||||
CcdiBaseStaff staff = new CcdiBaseStaff();
|
||||
BeanUtils.copyProperties(excel, staff);
|
||||
|
||||
// 统一检查Excel内重复(更新和新增两个分支都需要检查)
|
||||
// 统一检查Excel内重复
|
||||
if (processedStaffIds.contains(excel.getStaffId())) {
|
||||
throw new RuntimeException(String.format("员工ID[%d]在导入文件中重复,已跳过此条记录", excel.getStaffId()));
|
||||
}
|
||||
@@ -90,20 +93,7 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
||||
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
|
||||
}
|
||||
|
||||
// 检查员工ID是否在数据库中已存在
|
||||
if (existingIds.contains(excel.getStaffId())) {
|
||||
// 员工ID已存在于数据库
|
||||
if (!isUpdateSupport) {
|
||||
throw new RuntimeException("员工ID已存在且未启用更新支持");
|
||||
}
|
||||
|
||||
// 通过检查,添加到更新列表
|
||||
updateRecords.add(staff);
|
||||
|
||||
} else {
|
||||
// 员工ID不存在,添加到新增列表
|
||||
newRecords.add(staff);
|
||||
}
|
||||
newRecords.add(staff);
|
||||
|
||||
// 统一标记为已处理(只有成功添加到列表后才会执行到这里)
|
||||
if (excel.getStaffId() != null) {
|
||||
@@ -115,11 +105,13 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
||||
|
||||
// 记录进度
|
||||
ImportLogUtils.logProgress(log, taskId, i + 1, excelList.size(),
|
||||
newRecords.size() + updateRecords.size(), failures.size());
|
||||
newRecords.size(), failures.size());
|
||||
|
||||
} catch (Exception e) {
|
||||
ImportFailureVO failure = new ImportFailureVO();
|
||||
BeanUtils.copyProperties(excel, failure);
|
||||
failure.setSheetName("员工信息");
|
||||
failure.setRowNum(i + 2);
|
||||
failure.setErrorMessage(e.getMessage());
|
||||
failures.add(failure);
|
||||
|
||||
@@ -137,13 +129,6 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
||||
saveBatch(newRecords, 500);
|
||||
}
|
||||
|
||||
// 批量更新已有数据(先删除再插入)
|
||||
if (!updateRecords.isEmpty() && isUpdateSupport) {
|
||||
ImportLogUtils.logBatchOperationStart(log, taskId, "更新",
|
||||
(updateRecords.size() + 499) / 500, 500);
|
||||
baseStaffMapper.insertOrUpdateBatch(updateRecords);
|
||||
}
|
||||
|
||||
// 保存失败记录到Redis
|
||||
if (!failures.isEmpty()) {
|
||||
try {
|
||||
@@ -157,7 +142,7 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
||||
|
||||
ImportResult result = new ImportResult();
|
||||
result.setTotalCount(excelList.size());
|
||||
result.setSuccessCount(newRecords.size() + updateRecords.size());
|
||||
result.setSuccessCount(newRecords.size());
|
||||
result.setFailureCount(failures.size());
|
||||
|
||||
// 更新最终状态
|
||||
@@ -299,11 +284,10 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
||||
* 验证员工数据
|
||||
*
|
||||
* @param addDTO 新增DTO
|
||||
* @param isUpdateSupport 是否支持更新
|
||||
* @param existingIds 已存在的员工ID集合(导入场景使用,传null表示单条新增)
|
||||
* @param existingIdCards 已存在的身份证号集合(导入场景使用,传null表示单条新增)
|
||||
*/
|
||||
public void validateStaffData(CcdiBaseStaffAddDTO addDTO, Boolean isUpdateSupport, Set<Long> existingIds, Set<String> existingIdCards) {
|
||||
public void validateStaffData(CcdiBaseStaffAddDTO addDTO, Set<Long> existingIds, Set<String> existingIdCards) {
|
||||
// 验证必填字段
|
||||
if (StringUtils.isEmpty(addDTO.getName())) {
|
||||
throw new RuntimeException("姓名不能为空");
|
||||
@@ -326,6 +310,7 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
||||
if (StringUtils.isEmpty(addDTO.getStatus())) {
|
||||
throw new RuntimeException("状态不能为空");
|
||||
}
|
||||
validateDeptId(addDTO.getDeptId());
|
||||
|
||||
// 验证身份证号格式
|
||||
String idCardError = IdCardUtil.getErrorMessage(addDTO.getIdCard());
|
||||
@@ -347,12 +332,11 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
||||
throw new RuntimeException("该身份证号已存在");
|
||||
}
|
||||
} else {
|
||||
// 导入场景:如果员工ID不存在,才检查身份证号唯一性
|
||||
if (!existingIds.contains(addDTO.getStaffId())) {
|
||||
// 使用批量查询的结果检查身份证号唯一性
|
||||
if (existingIdCards != null && existingIdCards.contains(addDTO.getIdCard())) {
|
||||
throw new RuntimeException("该身份证号已存在");
|
||||
}
|
||||
if (existingIds.contains(addDTO.getStaffId())) {
|
||||
throw new RuntimeException("该员工ID已存在");
|
||||
}
|
||||
if (existingIdCards != null && existingIdCards.contains(addDTO.getIdCard())) {
|
||||
throw new RuntimeException("该身份证号已存在");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,4 +362,11 @@ public class CcdiBaseStaffImportServiceImpl implements ICcdiBaseStaffImportServi
|
||||
throw new RuntimeException(fieldLabel + "最多保留2位小数");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDeptId(Long deptId) {
|
||||
SysDept dept = deptMapper.selectDeptById(deptId);
|
||||
if (dept == null || !"0".equals(dept.getStatus()) || !"0".equals(dept.getDelFlag())) {
|
||||
throw new RuntimeException(String.format("所属部门ID[%d]不存在或已停用/删除,请检查机构号", deptId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,13 +211,12 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
|
||||
/**
|
||||
* 导入员工数据
|
||||
*
|
||||
* @param excelList Excel实体列表
|
||||
* @param isUpdateSupport 是否更新支持
|
||||
* @param excelList Excel实体列表
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public String importBaseStaff(List<CcdiBaseStaffExcel> excelList, Boolean isUpdateSupport) {
|
||||
public String importBaseStaff(List<CcdiBaseStaffExcel> excelList) {
|
||||
String taskId = UUID.randomUUID().toString();
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
@@ -236,7 +235,7 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
|
||||
redisTemplate.opsForHash().putAll(statusKey, statusData);
|
||||
redisTemplate.expire(statusKey, 7, java.util.concurrent.TimeUnit.DAYS);
|
||||
|
||||
importAsyncService.importBaseStaffAsync(excelList, isUpdateSupport, taskId);
|
||||
importAsyncService.importBaseStaffAsync(excelList, taskId);
|
||||
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ import java.util.stream.Collectors;
|
||||
public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTransactionImportService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CcdiPurchaseTransactionImportServiceImpl.class);
|
||||
private static final String MAIN_SHEET_NAME = "招投标主信息";
|
||||
private static final String SUPPLIER_SHEET_NAME = "供应商明细";
|
||||
private static final int EXCEL_DATA_START_ROW = 2;
|
||||
|
||||
@Resource
|
||||
private CcdiPurchaseTransactionMapper transactionMapper;
|
||||
@@ -62,6 +65,8 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
|
||||
long startTime = System.currentTimeMillis();
|
||||
List<CcdiPurchaseTransactionExcel> safeMainList = mainExcelList == null ? List.of() : mainExcelList;
|
||||
List<CcdiPurchaseTransactionSupplierExcel> safeSupplierList = supplierExcelList == null ? List.of() : supplierExcelList;
|
||||
List<MainImportRow> indexedMainRows = buildMainImportRows(safeMainList);
|
||||
List<SupplierImportRow> indexedSupplierRows = buildSupplierImportRows(safeSupplierList);
|
||||
int totalCount = countImportUnits(safeMainList, safeSupplierList);
|
||||
|
||||
// 记录导入开始
|
||||
@@ -76,17 +81,17 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
|
||||
Set<String> existingIds = getExistingPurchaseIds(safeMainList);
|
||||
ImportLogUtils.logBatchQueryComplete(log, taskId, "采购事项ID", existingIds.size());
|
||||
|
||||
Map<String, List<CcdiPurchaseTransactionExcel>> mainGroupMap = safeMainList.stream()
|
||||
.filter(item -> StringUtils.isNotEmpty(item.getPurchaseId()))
|
||||
Map<String, List<MainImportRow>> mainGroupMap = indexedMainRows.stream()
|
||||
.filter(item -> StringUtils.isNotEmpty(item.data().getPurchaseId()))
|
||||
.collect(Collectors.groupingBy(
|
||||
CcdiPurchaseTransactionExcel::getPurchaseId,
|
||||
item -> item.data().getPurchaseId(),
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList()
|
||||
));
|
||||
Map<String, List<CcdiPurchaseTransactionSupplierExcel>> supplierGroupMap = safeSupplierList.stream()
|
||||
.filter(item -> StringUtils.isNotEmpty(item.getPurchaseId()))
|
||||
Map<String, List<SupplierImportRow>> supplierGroupMap = indexedSupplierRows.stream()
|
||||
.filter(item -> StringUtils.isNotEmpty(item.data().getPurchaseId()))
|
||||
.collect(Collectors.groupingBy(
|
||||
CcdiPurchaseTransactionSupplierExcel::getPurchaseId,
|
||||
item -> item.data().getPurchaseId(),
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList()
|
||||
));
|
||||
@@ -94,34 +99,53 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
|
||||
purchaseIds.addAll(mainGroupMap.keySet());
|
||||
purchaseIds.addAll(supplierGroupMap.keySet());
|
||||
|
||||
for (CcdiPurchaseTransactionSupplierExcel supplierExcel : safeSupplierList) {
|
||||
if (StringUtils.isEmpty(supplierExcel.getPurchaseId())) {
|
||||
failures.add(buildFailure(null, null, "供应商明细Sheet中的采购事项ID不能为空"));
|
||||
for (SupplierImportRow supplierExcel : indexedSupplierRows) {
|
||||
if (StringUtils.isEmpty(supplierExcel.data().getPurchaseId())) {
|
||||
failures.add(buildFailure(
|
||||
null,
|
||||
null,
|
||||
SUPPLIER_SHEET_NAME,
|
||||
String.valueOf(supplierExcel.sheetRowNum()),
|
||||
"供应商明细Sheet中的采购事项ID不能为空"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (String purchaseId : purchaseIds) {
|
||||
index++;
|
||||
List<CcdiPurchaseTransactionExcel> mainRows = mainGroupMap.getOrDefault(purchaseId, List.of());
|
||||
List<CcdiPurchaseTransactionSupplierExcel> supplierRows = supplierGroupMap.getOrDefault(purchaseId, List.of());
|
||||
List<MainImportRow> mainRows = mainGroupMap.getOrDefault(purchaseId, List.of());
|
||||
List<SupplierImportRow> supplierRows = supplierGroupMap.getOrDefault(purchaseId, List.of());
|
||||
|
||||
try {
|
||||
if (existingIds.contains(purchaseId)) {
|
||||
throw new RuntimeException(String.format("采购事项ID[%s]已存在,请勿重复导入", purchaseId));
|
||||
throw buildValidationException(
|
||||
MAIN_SHEET_NAME,
|
||||
extractMainRowNums(mainRows),
|
||||
String.format("采购事项ID[%s]已存在,请勿重复导入", purchaseId)
|
||||
);
|
||||
}
|
||||
if (mainRows.isEmpty()) {
|
||||
throw new RuntimeException(String.format("采购事项ID[%s]缺少招投标主信息", purchaseId));
|
||||
throw buildValidationException(
|
||||
SUPPLIER_SHEET_NAME,
|
||||
extractSupplierRowNums(supplierRows),
|
||||
String.format("采购事项ID[%s]缺少招投标主信息", purchaseId)
|
||||
);
|
||||
}
|
||||
if (mainRows.size() > 1) {
|
||||
throw new RuntimeException(String.format("采购事项ID[%s]在招投标主信息Sheet中重复", purchaseId));
|
||||
throw buildValidationException(
|
||||
MAIN_SHEET_NAME,
|
||||
extractMainRowNums(mainRows),
|
||||
String.format("采购事项ID[%s]在招投标主信息Sheet中重复", purchaseId)
|
||||
);
|
||||
}
|
||||
|
||||
CcdiPurchaseTransactionExcel mainExcel = mainRows.getFirst();
|
||||
MainImportRow mainRow = mainRows.getFirst();
|
||||
CcdiPurchaseTransactionExcel mainExcel = mainRow.data();
|
||||
CcdiPurchaseTransactionAddDTO addDTO = new CcdiPurchaseTransactionAddDTO();
|
||||
BeanUtils.copyProperties(mainExcel, addDTO);
|
||||
|
||||
validateTransactionData(addDTO);
|
||||
validateTransactionData(addDTO, mainRow.sheetRowNum());
|
||||
List<CcdiPurchaseTransactionSupplier> suppliers = buildSupplierEntities(purchaseId, supplierRows, userName);
|
||||
|
||||
CcdiPurchaseTransaction transaction = new CcdiPurchaseTransaction();
|
||||
@@ -137,8 +161,16 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
|
||||
newTransactions.size(), failures.size());
|
||||
|
||||
} catch (Exception e) {
|
||||
CcdiPurchaseTransactionExcel mainExcel = mainRows.isEmpty() ? null : mainRows.getFirst();
|
||||
failures.add(buildFailure(mainExcel, purchaseId, e.getMessage()));
|
||||
MainImportRow mainRow = mainRows.isEmpty() ? null : mainRows.getFirst();
|
||||
CcdiPurchaseTransactionExcel mainExcel = mainRow == null ? null : mainRow.data();
|
||||
FailureMeta failureMeta = resolveFailureMeta(e, mainRows, supplierRows);
|
||||
failures.add(buildFailure(
|
||||
mainExcel,
|
||||
purchaseId,
|
||||
failureMeta.sheetName(),
|
||||
failureMeta.sheetRowNum(),
|
||||
e.getMessage()
|
||||
));
|
||||
|
||||
// 记录验证失败日志
|
||||
String keyData = String.format("采购事项ID=%s, 采购类别=%s, 标的物=%s",
|
||||
@@ -301,97 +333,99 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
|
||||
*
|
||||
* @param addDTO 新增DTO
|
||||
*/
|
||||
private void validateTransactionData(CcdiPurchaseTransactionAddDTO addDTO) {
|
||||
private void validateTransactionData(CcdiPurchaseTransactionAddDTO addDTO, int sheetRowNum) {
|
||||
// 验证必填字段
|
||||
if (StringUtils.isEmpty(addDTO.getPurchaseId())) {
|
||||
throw new RuntimeException("采购事项ID不能为空");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购事项ID不能为空");
|
||||
}
|
||||
if (StringUtils.isEmpty(addDTO.getPurchaseCategory())) {
|
||||
throw new RuntimeException("采购类别不能为空");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购类别不能为空");
|
||||
}
|
||||
if (StringUtils.isEmpty(addDTO.getSubjectName())) {
|
||||
throw new RuntimeException("标的物名称不能为空");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "标的物名称不能为空");
|
||||
}
|
||||
if (addDTO.getPurchaseQty() == null) {
|
||||
throw new RuntimeException("采购数量不能为空");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购数量不能为空");
|
||||
}
|
||||
if (addDTO.getBudgetAmount() == null) {
|
||||
throw new RuntimeException("预算金额不能为空");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "预算金额不能为空");
|
||||
}
|
||||
if (StringUtils.isEmpty(addDTO.getPurchaseMethod())) {
|
||||
throw new RuntimeException("采购方式不能为空");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购方式不能为空");
|
||||
}
|
||||
if (addDTO.getApplyDate() == null) {
|
||||
throw new RuntimeException("采购申请日期不能为空");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购申请日期不能为空");
|
||||
}
|
||||
if (StringUtils.isEmpty(addDTO.getApplicantId())) {
|
||||
throw new RuntimeException("申请人工号不能为空");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "申请人工号不能为空");
|
||||
}
|
||||
if (StringUtils.isEmpty(addDTO.getApplicantName())) {
|
||||
throw new RuntimeException("申请人姓名不能为空");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "申请人姓名不能为空");
|
||||
}
|
||||
if (StringUtils.isEmpty(addDTO.getApplyDepartment())) {
|
||||
throw new RuntimeException("申请部门不能为空");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "申请部门不能为空");
|
||||
}
|
||||
|
||||
// 验证工号格式(7位数字)
|
||||
if (!addDTO.getApplicantId().matches("^\\d{7}$")) {
|
||||
throw new RuntimeException("申请人工号必须为7位数字");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "申请人工号必须为7位数字");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(addDTO.getPurchaseLeaderId()) && !addDTO.getPurchaseLeaderId().matches("^\\d{7}$")) {
|
||||
throw new RuntimeException("采购负责人工号必须为7位数字");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购负责人工号必须为7位数字");
|
||||
}
|
||||
|
||||
// 验证金额非负
|
||||
if (addDTO.getPurchaseQty().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new RuntimeException("采购数量必须大于0");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "采购数量必须大于0");
|
||||
}
|
||||
if (addDTO.getBudgetAmount().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new RuntimeException("预算金额必须大于0");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "预算金额必须大于0");
|
||||
}
|
||||
if (addDTO.getBidAmount() != null && addDTO.getBidAmount().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new RuntimeException("中标金额必须大于0");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "中标金额必须大于0");
|
||||
}
|
||||
if (addDTO.getActualAmount() != null && addDTO.getActualAmount().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new RuntimeException("实际采购金额必须大于0");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "实际采购金额必须大于0");
|
||||
}
|
||||
if (addDTO.getContractAmount() != null && addDTO.getContractAmount().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new RuntimeException("合同金额必须大于0");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "合同金额必须大于0");
|
||||
}
|
||||
if (addDTO.getSettlementAmount() != null && addDTO.getSettlementAmount().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new RuntimeException("结算金额必须大于0");
|
||||
throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "结算金额必须大于0");
|
||||
}
|
||||
}
|
||||
|
||||
private List<CcdiPurchaseTransactionSupplier> buildSupplierEntities(
|
||||
String purchaseId,
|
||||
List<CcdiPurchaseTransactionSupplierExcel> supplierRows,
|
||||
List<SupplierImportRow> supplierRows,
|
||||
String userName
|
||||
) {
|
||||
List<CcdiPurchaseTransactionSupplierExcel> normalizedRows = supplierRows == null
|
||||
List<SupplierImportRow> normalizedRows = supplierRows == null
|
||||
? List.of()
|
||||
: supplierRows.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(this::hasAnySupplierValue)
|
||||
.filter(item -> hasAnySupplierValue(item.data()))
|
||||
.toList();
|
||||
|
||||
long winnerCount = normalizedRows.stream()
|
||||
.filter(item -> parseIsBidWinner(item.getIsBidWinner()) == 1)
|
||||
.count();
|
||||
if (winnerCount > 1) {
|
||||
throw new RuntimeException(String.format("采购事项ID[%s]存在多条中标供应商", purchaseId));
|
||||
}
|
||||
|
||||
Set<String> duplicateSupplierKeys = new LinkedHashSet<>();
|
||||
List<Integer> winnerRowNums = new ArrayList<>();
|
||||
Map<String, Integer> supplierKeyRowMap = new LinkedHashMap<>();
|
||||
List<CcdiPurchaseTransactionSupplier> result = new ArrayList<>();
|
||||
for (int i = 0; i < normalizedRows.size(); i++) {
|
||||
CcdiPurchaseTransactionSupplierExcel supplierRow = normalizedRows.get(i);
|
||||
validateSupplierRow(supplierRow);
|
||||
for (SupplierImportRow supplierImportRow : normalizedRows) {
|
||||
CcdiPurchaseTransactionSupplierExcel supplierRow = supplierImportRow.data();
|
||||
int isBidWinner = validateSupplierRow(supplierImportRow);
|
||||
if (isBidWinner == 1) {
|
||||
winnerRowNums.add(supplierImportRow.sheetRowNum());
|
||||
}
|
||||
|
||||
String duplicateKey = StringUtils.trimToEmpty(supplierRow.getSupplierName()) + "|"
|
||||
+ StringUtils.trimToEmpty(supplierRow.getSupplierUscc());
|
||||
if (!duplicateSupplierKeys.add(duplicateKey)) {
|
||||
throw new RuntimeException(String.format("采购事项ID[%s]存在重复供应商", purchaseId));
|
||||
Integer firstRowNum = supplierKeyRowMap.putIfAbsent(duplicateKey, supplierImportRow.sheetRowNum());
|
||||
if (firstRowNum != null) {
|
||||
throw buildValidationException(
|
||||
SUPPLIER_SHEET_NAME,
|
||||
List.of(firstRowNum, supplierImportRow.sheetRowNum()),
|
||||
String.format("采购事项ID[%s]存在重复供应商", purchaseId)
|
||||
);
|
||||
}
|
||||
|
||||
CcdiPurchaseTransactionSupplier supplier = new CcdiPurchaseTransactionSupplier();
|
||||
@@ -401,37 +435,45 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
|
||||
supplier.setContactPerson(StringUtils.trimToNull(supplierRow.getContactPerson()));
|
||||
supplier.setContactPhone(StringUtils.trimToNull(supplierRow.getContactPhone()));
|
||||
supplier.setSupplierBankAccount(StringUtils.trimToNull(supplierRow.getSupplierBankAccount()));
|
||||
supplier.setIsBidWinner(parseIsBidWinner(supplierRow.getIsBidWinner()));
|
||||
supplier.setSortOrder(supplierRow.getSortOrder() == null ? i + 1 : supplierRow.getSortOrder());
|
||||
supplier.setIsBidWinner(isBidWinner);
|
||||
supplier.setSortOrder(supplierRow.getSortOrder() == null ? result.size() + 1 : supplierRow.getSortOrder());
|
||||
supplier.setCreatedBy(userName);
|
||||
supplier.setUpdatedBy(userName);
|
||||
result.add(supplier);
|
||||
}
|
||||
if (winnerRowNums.size() > 1) {
|
||||
throw buildValidationException(
|
||||
SUPPLIER_SHEET_NAME,
|
||||
winnerRowNums,
|
||||
String.format("采购事项ID[%s]存在多条中标供应商", purchaseId)
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void validateSupplierRow(CcdiPurchaseTransactionSupplierExcel supplierRow) {
|
||||
private int validateSupplierRow(SupplierImportRow supplierImportRow) {
|
||||
CcdiPurchaseTransactionSupplierExcel supplierRow = supplierImportRow.data();
|
||||
if (StringUtils.isEmpty(supplierRow.getSupplierName())) {
|
||||
throw new RuntimeException("供应商名称不能为空");
|
||||
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商名称不能为空");
|
||||
}
|
||||
if (StringUtils.length(supplierRow.getSupplierName()) > 200) {
|
||||
throw new RuntimeException("供应商名称长度不能超过200个字符");
|
||||
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商名称长度不能超过200个字符");
|
||||
}
|
||||
if (StringUtils.length(supplierRow.getContactPerson()) > 50) {
|
||||
throw new RuntimeException("供应商联系人长度不能超过50个字符");
|
||||
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商联系人长度不能超过50个字符");
|
||||
}
|
||||
if (StringUtils.length(supplierRow.getSupplierBankAccount()) > 50) {
|
||||
throw new RuntimeException("供应商银行账户长度不能超过50个字符");
|
||||
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商银行账户长度不能超过50个字符");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(supplierRow.getContactPhone())
|
||||
&& !supplierRow.getContactPhone().matches("^1[3-9]\\d{9}$|^0\\d{2,3}-?\\d{7,8}$")) {
|
||||
throw new RuntimeException("供应商联系电话格式不正确");
|
||||
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商联系电话格式不正确");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(supplierRow.getSupplierUscc())
|
||||
&& !supplierRow.getSupplierUscc().matches("^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$")) {
|
||||
throw new RuntimeException("供应商统一信用代码格式不正确");
|
||||
throw buildValidationException(SUPPLIER_SHEET_NAME, List.of(supplierImportRow.sheetRowNum()), "供应商统一信用代码格式不正确");
|
||||
}
|
||||
parseIsBidWinner(supplierRow.getIsBidWinner());
|
||||
return parseIsBidWinner(supplierRow.getIsBidWinner(), supplierImportRow.sheetRowNum());
|
||||
}
|
||||
|
||||
private boolean hasAnySupplierValue(CcdiPurchaseTransactionSupplierExcel supplierRow) {
|
||||
@@ -445,7 +487,7 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
|
||||
|| supplierRow.getSortOrder() != null;
|
||||
}
|
||||
|
||||
private int parseIsBidWinner(String rawValue) {
|
||||
private int parseIsBidWinner(String rawValue, Integer sheetRowNum) {
|
||||
if (StringUtils.isEmpty(rawValue)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -458,7 +500,11 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
|
||||
|| "FALSE".equalsIgnoreCase(normalized)) {
|
||||
return 0;
|
||||
}
|
||||
throw new RuntimeException("是否中标仅支持填写“是/否”或“1/0”");
|
||||
throw buildValidationException(
|
||||
SUPPLIER_SHEET_NAME,
|
||||
sheetRowNum == null ? List.of() : List.of(sheetRowNum),
|
||||
"是否中标仅支持填写“是/否”或“1/0”"
|
||||
);
|
||||
}
|
||||
|
||||
private void fillWinnerSummary(
|
||||
@@ -487,12 +533,16 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
|
||||
private PurchaseTransactionImportFailureVO buildFailure(
|
||||
CcdiPurchaseTransactionExcel mainExcel,
|
||||
String purchaseId,
|
||||
String sheetName,
|
||||
String sheetRowNum,
|
||||
String errorMessage
|
||||
) {
|
||||
PurchaseTransactionImportFailureVO failure = new PurchaseTransactionImportFailureVO();
|
||||
if (mainExcel != null) {
|
||||
BeanUtils.copyProperties(mainExcel, failure);
|
||||
}
|
||||
failure.setSheetName(sheetName);
|
||||
failure.setSheetRowNum(sheetRowNum);
|
||||
if (StringUtils.isNotEmpty(purchaseId)) {
|
||||
failure.setPurchaseId(purchaseId);
|
||||
}
|
||||
@@ -519,4 +569,87 @@ public class CcdiPurchaseTransactionImportServiceImpl implements ICcdiPurchaseTr
|
||||
);
|
||||
return purchaseIds.size();
|
||||
}
|
||||
|
||||
private List<MainImportRow> buildMainImportRows(List<CcdiPurchaseTransactionExcel> mainExcelList) {
|
||||
List<MainImportRow> rows = new ArrayList<>();
|
||||
for (int i = 0; i < mainExcelList.size(); i++) {
|
||||
rows.add(new MainImportRow(mainExcelList.get(i), i + EXCEL_DATA_START_ROW));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
private List<SupplierImportRow> buildSupplierImportRows(List<CcdiPurchaseTransactionSupplierExcel> supplierExcelList) {
|
||||
List<SupplierImportRow> rows = new ArrayList<>();
|
||||
for (int i = 0; i < supplierExcelList.size(); i++) {
|
||||
rows.add(new SupplierImportRow(supplierExcelList.get(i), i + EXCEL_DATA_START_ROW));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
private List<Integer> extractMainRowNums(List<MainImportRow> rows) {
|
||||
return rows.stream().map(MainImportRow::sheetRowNum).toList();
|
||||
}
|
||||
|
||||
private List<Integer> extractSupplierRowNums(List<SupplierImportRow> rows) {
|
||||
return rows.stream().map(SupplierImportRow::sheetRowNum).toList();
|
||||
}
|
||||
|
||||
private ImportValidationException buildValidationException(String sheetName, List<Integer> rowNums, String message) {
|
||||
return new ImportValidationException(sheetName, formatSheetRowNum(rowNums), message);
|
||||
}
|
||||
|
||||
private FailureMeta resolveFailureMeta(
|
||||
Exception exception,
|
||||
List<MainImportRow> mainRows,
|
||||
List<SupplierImportRow> supplierRows
|
||||
) {
|
||||
if (exception instanceof ImportValidationException validationException) {
|
||||
return new FailureMeta(validationException.getSheetName(), validationException.getSheetRowNum());
|
||||
}
|
||||
if (!mainRows.isEmpty()) {
|
||||
return new FailureMeta(MAIN_SHEET_NAME, formatSheetRowNum(extractMainRowNums(mainRows)));
|
||||
}
|
||||
if (!supplierRows.isEmpty()) {
|
||||
return new FailureMeta(SUPPLIER_SHEET_NAME, formatSheetRowNum(extractSupplierRowNums(supplierRows)));
|
||||
}
|
||||
return new FailureMeta("", "");
|
||||
}
|
||||
|
||||
private String formatSheetRowNum(List<Integer> rowNums) {
|
||||
if (rowNums == null || rowNums.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return rowNums.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.sorted()
|
||||
.map(String::valueOf)
|
||||
.collect(Collectors.joining("、"));
|
||||
}
|
||||
|
||||
private record MainImportRow(CcdiPurchaseTransactionExcel data, int sheetRowNum) {}
|
||||
|
||||
private record SupplierImportRow(CcdiPurchaseTransactionSupplierExcel data, int sheetRowNum) {}
|
||||
|
||||
private record FailureMeta(String sheetName, String sheetRowNum) {}
|
||||
|
||||
private static class ImportValidationException extends RuntimeException {
|
||||
|
||||
private final String sheetName;
|
||||
private final String sheetRowNum;
|
||||
|
||||
private ImportValidationException(String sheetName, String sheetRowNum, String message) {
|
||||
super(message);
|
||||
this.sheetName = sheetName;
|
||||
this.sheetRowNum = sheetRowNum;
|
||||
}
|
||||
|
||||
public String getSheetName() {
|
||||
return sheetName;
|
||||
}
|
||||
|
||||
public String getSheetRowNum() {
|
||||
return sheetRowNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ import java.util.stream.Collectors;
|
||||
public class CcdiStaffFmyRelationImportServiceImpl implements ICcdiStaffFmyRelationImportService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CcdiStaffFmyRelationImportServiceImpl.class);
|
||||
private static final String SHEET_NAME = "员工亲属关系信息";
|
||||
private static final int EXCEL_DATA_START_ROW = 2;
|
||||
|
||||
@Resource
|
||||
private CcdiStaffFmyRelationMapper relationMapper;
|
||||
@@ -168,6 +170,8 @@ public class CcdiStaffFmyRelationImportServiceImpl implements ICcdiStaffFmyRelat
|
||||
} catch (Exception e) {
|
||||
StaffFmyRelationImportFailureVO failure = new StaffFmyRelationImportFailureVO();
|
||||
BeanUtils.copyProperties(excel, failure);
|
||||
failure.setSheetName(SHEET_NAME);
|
||||
failure.setRowNum(i + EXCEL_DATA_START_ROW);
|
||||
failure.setErrorMessage(e.getMessage());
|
||||
failures.add(failure);
|
||||
|
||||
|
||||
@@ -96,8 +96,12 @@ class CcdiAssetInfoControllerTest {
|
||||
@Test
|
||||
void getImportFailures_shouldReturnPagedRows() {
|
||||
AssetImportFailureVO failure1 = new AssetImportFailureVO();
|
||||
failure1.setSheetName("亲属资产信息");
|
||||
failure1.setRowNum(2);
|
||||
failure1.setPersonId("A1");
|
||||
AssetImportFailureVO failure2 = new AssetImportFailureVO();
|
||||
failure2.setSheetName("亲属资产信息");
|
||||
failure2.setRowNum(3);
|
||||
failure2.setPersonId("A2");
|
||||
when(assetInfoImportService.getImportFailures("task-3")).thenReturn(List.of(failure1, failure2));
|
||||
|
||||
@@ -105,7 +109,10 @@ class CcdiAssetInfoControllerTest {
|
||||
|
||||
assertEquals(2, result.getTotal());
|
||||
assertEquals(1, result.getRows().size());
|
||||
assertEquals("A2", ((AssetImportFailureVO) result.getRows().get(0)).getPersonId());
|
||||
AssetImportFailureVO row = (AssetImportFailureVO) result.getRows().get(0);
|
||||
assertEquals("亲属资产信息", row.getSheetName());
|
||||
assertEquals(3, row.getRowNum());
|
||||
assertEquals("A2", row.getPersonId());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -93,10 +93,39 @@ class CcdiBaseStaffAssetImportServiceImplTest {
|
||||
ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
|
||||
verify(valueOperations).set(eq("import:baseStaffAsset:task-2:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS));
|
||||
BaseStaffAssetImportFailureVO failure = (BaseStaffAssetImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
|
||||
assertEquals("员工资产信息", failure.getSheetName());
|
||||
assertEquals(2, failure.getRowNum());
|
||||
assertEquals("320101199201010022", failure.getPersonId());
|
||||
assertTrue(failure.getErrorMessage().contains("员工资产导入仅支持员工本人证件号"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void importAssetInfoAsync_shouldFailWhenAssetAlreadyExists() {
|
||||
CcdiBaseStaffAssetInfoExcel excel = buildExcel("320101199001010011", "房产");
|
||||
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
when(assetInfoMapper.selectOwnerCandidatesByBaseStaffIdCards(List.of("320101199001010011")))
|
||||
.thenReturn(List.of(owner("320101199001010011", "320101199001010011")));
|
||||
|
||||
CcdiAssetInfo existing = new CcdiAssetInfo();
|
||||
existing.setFamilyId("320101199001010011");
|
||||
existing.setPersonId("320101199001010011");
|
||||
existing.setAssetMainType("房产");
|
||||
existing.setAssetSubType("房产小类");
|
||||
existing.setAssetName("房产名称");
|
||||
when(assetInfoMapper.selectList(any())).thenReturn(List.of(existing));
|
||||
|
||||
service.importAssetInfoAsync(List.of(excel), "task-duplicate", "tester");
|
||||
|
||||
verify(assetInfoMapper, never()).insertBatch(any());
|
||||
ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
|
||||
verify(valueOperations).set(eq("import:baseStaffAsset:task-duplicate:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS));
|
||||
BaseStaffAssetImportFailureVO failure = (BaseStaffAssetImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
|
||||
assertEquals("员工资产信息", failure.getSheetName());
|
||||
assertEquals(2, failure.getRowNum());
|
||||
assertTrue(failure.getErrorMessage().contains("资产记录已存在"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImportStatusAndFailures_shouldUseBaseStaffAssetPrefixes() {
|
||||
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
package com.ruoyi.info.collection.service;
|
||||
|
||||
import com.ruoyi.common.core.domain.entity.SysDept;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO;
|
||||
import com.ruoyi.info.collection.service.impl.CcdiBaseStaffImportServiceImpl;
|
||||
import com.ruoyi.system.mapper.SysDeptMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
@@ -11,60 +17,109 @@ import java.util.Set;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CcdiBaseStaffImportServiceImplTest {
|
||||
|
||||
private final CcdiBaseStaffImportServiceImpl service = new CcdiBaseStaffImportServiceImpl();
|
||||
@InjectMocks
|
||||
private CcdiBaseStaffImportServiceImpl service;
|
||||
|
||||
@Mock
|
||||
private SysDeptMapper deptMapper;
|
||||
|
||||
@Test
|
||||
void validateStaffData_shouldAllowEmptyAnnualIncome() {
|
||||
assertDoesNotThrow(() -> service.validateStaffData(buildDto(null), false, Collections.emptySet(), Collections.emptySet()));
|
||||
mockNormalDept();
|
||||
assertDoesNotThrow(() -> service.validateStaffData(buildDto(null), Collections.emptySet(), Collections.emptySet()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateStaffData_shouldAllowZeroAndTwoDecimalAnnualIncome() {
|
||||
assertDoesNotThrow(() -> service.validateStaffData(buildDto(new BigDecimal("0.00")), false, Collections.emptySet(), Collections.emptySet()));
|
||||
assertDoesNotThrow(() -> service.validateStaffData(buildDto(new BigDecimal("12345.67")), false, Collections.emptySet(), Collections.emptySet()));
|
||||
mockNormalDept();
|
||||
assertDoesNotThrow(() -> service.validateStaffData(buildDto(new BigDecimal("0.00")), Collections.emptySet(), Collections.emptySet()));
|
||||
assertDoesNotThrow(() -> service.validateStaffData(buildDto(new BigDecimal("12345.67")), Collections.emptySet(), Collections.emptySet()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateStaffData_shouldAllowPartyMemberValuesZeroAndOne() {
|
||||
mockNormalDept();
|
||||
CcdiBaseStaffAddDTO nonPartyMember = buildDto(null);
|
||||
nonPartyMember.setPartyMember(0);
|
||||
CcdiBaseStaffAddDTO partyMember = buildDto(null);
|
||||
partyMember.setPartyMember(1);
|
||||
|
||||
assertDoesNotThrow(() -> service.validateStaffData(nonPartyMember, false, Collections.emptySet(), Collections.emptySet()));
|
||||
assertDoesNotThrow(() -> service.validateStaffData(partyMember, false, Collections.emptySet(), Collections.emptySet()));
|
||||
assertDoesNotThrow(() -> service.validateStaffData(nonPartyMember, Collections.emptySet(), Collections.emptySet()));
|
||||
assertDoesNotThrow(() -> service.validateStaffData(partyMember, Collections.emptySet(), Collections.emptySet()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateStaffData_shouldRejectInvalidPartyMemberValue() {
|
||||
mockNormalDept();
|
||||
CcdiBaseStaffAddDTO dto = buildDto(null);
|
||||
dto.setPartyMember(2);
|
||||
|
||||
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||
() -> service.validateStaffData(dto, false, Set.of(), Set.of()));
|
||||
() -> service.validateStaffData(dto, Set.of(), Set.of()));
|
||||
|
||||
assertEquals("是否党员只能填写'0'或'1'", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateStaffData_shouldRejectNegativeAnnualIncome() {
|
||||
mockNormalDept();
|
||||
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||
() -> service.validateStaffData(buildDto(new BigDecimal("-1.00")), false, Set.of(), Set.of()));
|
||||
() -> service.validateStaffData(buildDto(new BigDecimal("-1.00")), Set.of(), Set.of()));
|
||||
|
||||
assertEquals("年收入不能为负数", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateStaffData_shouldRejectAnnualIncomeWithMoreThanTwoDecimals() {
|
||||
mockNormalDept();
|
||||
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||
() -> service.validateStaffData(buildDto(new BigDecimal("12.345")), false, Set.of(), Set.of()));
|
||||
() -> service.validateStaffData(buildDto(new BigDecimal("12.345")), Set.of(), Set.of()));
|
||||
|
||||
assertEquals("年收入最多保留2位小数", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateStaffData_shouldAllowWhenDeptIsNormalAndNotDeleted() {
|
||||
mockNormalDept();
|
||||
assertDoesNotThrow(() -> service.validateStaffData(buildDto(null), Set.of(), Set.of()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateStaffData_shouldRejectWhenDeptDoesNotExist() {
|
||||
when(deptMapper.selectDeptById(10L)).thenReturn(null);
|
||||
|
||||
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||
() -> service.validateStaffData(buildDto(null), Set.of(), Set.of()));
|
||||
|
||||
assertEquals("所属部门ID[10]不存在或已停用/删除,请检查机构号", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateStaffData_shouldRejectWhenDeptIsDisabled() {
|
||||
when(deptMapper.selectDeptById(10L)).thenReturn(buildDept("1", "0"));
|
||||
|
||||
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||
() -> service.validateStaffData(buildDto(null), Set.of(), Set.of()));
|
||||
|
||||
assertEquals("所属部门ID[10]不存在或已停用/删除,请检查机构号", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateStaffData_shouldRejectWhenDeptIsDeleted() {
|
||||
when(deptMapper.selectDeptById(10L)).thenReturn(buildDept("0", "2"));
|
||||
|
||||
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||
() -> service.validateStaffData(buildDto(null), Set.of(), Set.of()));
|
||||
|
||||
assertEquals("所属部门ID[10]不存在或已停用/删除,请检查机构号", exception.getMessage());
|
||||
}
|
||||
|
||||
private CcdiBaseStaffAddDTO buildDto(BigDecimal annualIncome) {
|
||||
CcdiBaseStaffAddDTO dto = new CcdiBaseStaffAddDTO();
|
||||
dto.setName("张三");
|
||||
@@ -77,4 +132,17 @@ class CcdiBaseStaffImportServiceImplTest {
|
||||
dto.setAnnualIncome(annualIncome);
|
||||
return dto;
|
||||
}
|
||||
|
||||
private SysDept buildDept(String status, String delFlag) {
|
||||
SysDept dept = new SysDept();
|
||||
dept.setDeptId(10L);
|
||||
dept.setDeptName("测试部门");
|
||||
dept.setStatus(status);
|
||||
dept.setDelFlag(delFlag);
|
||||
return dept;
|
||||
}
|
||||
|
||||
private void mockNormalDept() {
|
||||
lenient().when(deptMapper.selectDeptById(10L)).thenReturn(buildDept("0", "0"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# 员工信息维护双 Sheet 导入后端实施计划
|
||||
|
||||
## 目标
|
||||
- 将员工信息维护导入模板改为 `员工信息` + `员工资产信息` 双 Sheet。
|
||||
- 统一由 `/ccdi/baseStaff/importData` 接收单文件上传,并按有数据的 Sheet 分别调用现有员工导入与员工资产导入方法。
|
||||
- 员工信息导入取消“更新已存在员工”能力,命中现有员工 ID 或身份证号时直接记失败。
|
||||
- 两类失败记录统一补充 `sheetName`、`rowNum`、`errorMessage`,便于直接定位 Excel 中的失败位置。
|
||||
|
||||
## 实施内容
|
||||
- 控制器改造
|
||||
- 修改 `CcdiBaseStaffController#importTemplate`,下载双 Sheet 模板,文件名统一为“员工信息维护导入模板”。
|
||||
- 修改 `CcdiBaseStaffController#importData`,按 Sheet 名分别读取 `CcdiBaseStaffExcel` 与 `CcdiBaseStaffAssetInfoExcel`。
|
||||
- 两个 Sheet 均为空时返回错误;任一 Sheet 有数据时,仅提交对应导入任务。
|
||||
- 返回新的双任务提交结果对象,包含 `staffTaskId`、`assetTaskId`、`message`。
|
||||
- 服务改造
|
||||
- 修改 `ICcdiBaseStaffService`、`CcdiBaseStaffServiceImpl`,移除 `updateSupport` 参数。
|
||||
- 修改 `ICcdiBaseStaffImportService`、`CcdiBaseStaffImportServiceImpl`,移除更新分支与 `insertOrUpdateBatch` 调用。
|
||||
- 员工导入校验统一为:
|
||||
- 员工 ID 已存在:失败
|
||||
- 身份证号已存在:失败
|
||||
- Excel 内重复:失败
|
||||
- 员工资产导入补充重复校验:
|
||||
- 数据库中存在同一 `personId + assetMainType + assetSubType + assetName`:失败
|
||||
- 导入文件中存在同一组合重复:失败
|
||||
- VO 修正
|
||||
- 新增员工双 Sheet 提交结果 VO。
|
||||
- 修正员工导入失败记录 VO 字段名为 `staffId`,与前端表格字段保持一致。
|
||||
- 员工与员工资产失败记录 VO 均增加 `sheetName`、`rowNum`。
|
||||
|
||||
## 验证
|
||||
- `mvn -pl ccdi-info-collection,ruoyi-admin -am -DskipTests compile`
|
||||
- 补充控制器与服务层回归测试,覆盖双 Sheet 分发与“已存在即失败”规则。
|
||||
|
||||
## 影响范围
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/`
|
||||
@@ -0,0 +1,26 @@
|
||||
# 2026-04-22 招投标导入失败展示增强后端实施计划
|
||||
|
||||
## 1. 目标
|
||||
|
||||
- 为招投标导入失败记录补充失败来源 `Sheet`
|
||||
- 为失败记录补充 Excel 失败行号
|
||||
- 保持现有导入校验逻辑不变,仅增强失败记录元数据
|
||||
|
||||
## 2. 涉及范围
|
||||
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/PurchaseTransactionImportFailureVO.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionImportServiceImpl.java`
|
||||
|
||||
## 3. 实施步骤
|
||||
|
||||
1. 在失败记录 VO 中新增 `sheetName`、`sheetRowNum` 字段,供前端弹窗直接读取
|
||||
2. 在导入服务中为主信息 Sheet 和供应商明细 Sheet 建立“Excel 数据行号”上下文
|
||||
3. 在主信息校验、供应商校验、主从关系校验、空采购事项 ID 供应商校验等失败分支中,统一写入对应的 `Sheet` 与行号
|
||||
4. 对跨多行触发的失败场景,行号以合并字符串形式返回,便于页面直接展示
|
||||
5. 保留原有失败原因与业务字段,避免影响已有失败记录查询接口
|
||||
|
||||
## 4. 验证方式
|
||||
|
||||
- 执行后端编译,确认新增字段和异常封装无编译错误
|
||||
- 通过真实页面上传失败样本,核对失败记录接口返回 `sheetName / sheetRowNum / errorMessage`
|
||||
- 覆盖至少一个主信息失败样本和一个供应商明细失败样本
|
||||
@@ -0,0 +1,39 @@
|
||||
# 员工亲属关系维护双 Sheet 导入后端实施计划
|
||||
|
||||
## 目标
|
||||
- 将员工亲属关系维护导入模板改为双 Sheet:
|
||||
- `员工亲属关系信息`
|
||||
- `亲属资产信息`
|
||||
- 将导入提交入口统一到 `/ccdi/staffFmyRelation/importData`。
|
||||
- 统一补充失败记录定位字段,支持前端展示 `Sheet / Excel行号 / 失败原因`。
|
||||
|
||||
## 实施内容
|
||||
- Controller 调整
|
||||
- `CcdiStaffFmyRelationController#importTemplate` 改为输出双 Sheet 模板,模板文件名统一为“员工亲属关系维护导入模板”。
|
||||
- `CcdiStaffFmyRelationController#importData` 一次读取两个 Sheet。
|
||||
- 按有数据的 Sheet 分别提交亲属关系导入任务和亲属资产导入任务。
|
||||
- 返回新的提交结果 VO,包含 `relationTaskId`、`assetTaskId` 和提示文案。
|
||||
- VO 调整
|
||||
- `StaffFmyRelationImportFailureVO` 增加 `sheetName`、`rowNum`。
|
||||
- `AssetImportFailureVO` 增加 `sheetName`、`rowNum`。
|
||||
- 新增 `StaffFmyRelationImportSubmitResultVO`。
|
||||
- 导入服务调整
|
||||
- `CcdiStaffFmyRelationImportServiceImpl` 失败记录写入固定 `sheetName=员工亲属关系信息`,并记录 Excel 数据行号。
|
||||
- `CcdiAssetInfoImportServiceImpl` 失败记录写入固定 `sheetName=亲属资产信息`,并记录 Excel 数据行号。
|
||||
- 兼容策略
|
||||
- 保留原 `CcdiAssetInfoController` 的状态查询与失败记录查询接口,前端继续复用原有资产任务轮询与失败记录查看能力。
|
||||
|
||||
## 验证
|
||||
- 后端优先验证:
|
||||
- `CcdiStaffFmyRelationControllerTest`
|
||||
- `CcdiAssetInfoControllerTest`
|
||||
- 编译验证:
|
||||
- `mvn -pl ccdi-info-collection -am -Dmaven.test.skip=true compile`
|
||||
|
||||
## 影响范围
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffFmyRelationController.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationImportServiceImpl.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/StaffFmyRelationImportFailureVO.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/AssetImportFailureVO.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/StaffFmyRelationImportSubmitResultVO.java`
|
||||
@@ -0,0 +1,32 @@
|
||||
# 员工信息导入机构号校验后端实施计划
|
||||
|
||||
## 目标
|
||||
- 在员工信息 Excel 导入链路中校验 `所属部门ID(deptId)` 是否对应有效机构号。
|
||||
- 有效口径统一为 `sys_dept` 中“正常且未删除”的部门,即 `status = '0'` 且 `del_flag = '0'`。
|
||||
- 命中不存在、已停用或已删除的部门时,不入库,直接进入员工导入失败记录。
|
||||
|
||||
## 实施内容
|
||||
- 导入服务改造
|
||||
- 修改 `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffImportServiceImpl.java`。
|
||||
- 在 `validateStaffData` 中于必填校验后增加 `deptId` 有效性校验。
|
||||
- 新增私有方法按 `deptId` 查询部门并校验 `status` 与 `delFlag`。
|
||||
- 校验失败时抛出统一错误文案:`所属部门ID[xxx]不存在或已停用/删除,请检查机构号`。
|
||||
- 部门 Mapper 对齐
|
||||
- 修改 `ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml`。
|
||||
- 为 `selectDeptById` 查询补齐 `d.del_flag` 字段,保证导入服务可同时判断停用与逻辑删除状态。
|
||||
- 单元测试补充
|
||||
- 修改 `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffImportServiceImplTest.java`。
|
||||
- 增加部门存在、停用、删除三类校验测试。
|
||||
- 修改 `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffDualImportServiceTest.java`。
|
||||
- 增加混合导入场景测试,验证合法员工成功入库、非法 `deptId` 写入失败记录且任务状态为 `PARTIAL_SUCCESS`。
|
||||
|
||||
## 验证
|
||||
- 定向单测:
|
||||
- `mvn -pl ccdi-info-collection -am -Dsurefire.failIfNoSpecifiedTests=false -Dtest=CcdiBaseStaffImportServiceImplTest,CcdiBaseStaffDualImportServiceTest test`
|
||||
- 编译校验:
|
||||
- `mvn -pl ccdi-info-collection,ruoyi-admin -am -DskipTests compile`
|
||||
|
||||
## 影响范围
|
||||
- 员工信息导入后端异步校验逻辑
|
||||
- 系统部门主键查询字段映射
|
||||
- 员工导入相关单元测试
|
||||
@@ -0,0 +1,23 @@
|
||||
# 招投标供应商校验后端实施计划
|
||||
|
||||
## 目标
|
||||
- 让招投标信息维护页面的新增、编辑接口仅保留供应商名称和统一信用代码必填校验。
|
||||
- 移除供应商联系人、联系电话、银行账户,以及供应商名称/统一信用代码的内容格式校验,避免页面保存被接口层拦截。
|
||||
|
||||
## 实施内容
|
||||
- 调整 `CcdiPurchaseTransactionSupplierDTO`
|
||||
- 保留 `supplierName` 的 `@NotBlank`。
|
||||
- 为 `supplierUscc` 增加 `@NotBlank` 必填校验。
|
||||
- 移除 `supplierName` 的长度校验。
|
||||
- 移除 `supplierUscc` 的格式校验。
|
||||
- 移除 `contactPerson`、`contactPhone`、`supplierBankAccount` 的内容校验注解。
|
||||
|
||||
## 验证
|
||||
- `mvn -pl ccdi-info-collection -am -DskipTests compile`
|
||||
- `sh bin/restart_java_backend.sh`
|
||||
- 结合真实页面验证:
|
||||
- 新增弹窗提交 `supplierUscc=ABC`、`contactPhone=123` 成功
|
||||
- 编辑弹窗提交 `supplierUscc=XYZ`、`contactPhone=abc123` 成功
|
||||
|
||||
## 产出文件
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiPurchaseTransactionSupplierDTO.java`
|
||||
@@ -0,0 +1,38 @@
|
||||
# 员工信息维护双 Sheet 导入前端实施计划
|
||||
|
||||
## 目标
|
||||
- 将员工信息维护页面的两个导入按钮合并为一个。
|
||||
- 统一使用双 Sheet 模板上传,并根据后端返回的 `staffTaskId`、`assetTaskId` 分别沿用原有轮询与失败记录能力。
|
||||
- 保留员工失败记录与员工资产失败记录两个独立查看入口。
|
||||
- 两个失败记录弹窗都需要展示失败来源 Sheet、Excel 行号和失败原因。
|
||||
|
||||
## 实施内容
|
||||
- 页面入口调整
|
||||
- 删除“导入资产信息”按钮,仅保留一个“导入”按钮。
|
||||
- 删除独立员工资产上传弹窗,保留一个统一上传弹窗。
|
||||
- 上传交互调整
|
||||
- 去掉“是否更新已经存在的员工数据”复选框。
|
||||
- 模板提示调整为双 Sheet 说明,明确支持只填一个或同时填写两个 Sheet。
|
||||
- 下载模板文件名统一为“员工信息维护导入模板”。
|
||||
- 任务处理调整
|
||||
- 上传成功后解析 `staffTaskId`、`assetTaskId`。
|
||||
- 有员工任务 ID 时,启动原员工导入轮询。
|
||||
- 有资产任务 ID 时,启动原资产导入轮询。
|
||||
- 未返回的任务类型不清空对应历史失败记录状态。
|
||||
- 失败记录展示调整
|
||||
- 员工失败记录弹窗增加 `Sheet`、`Excel行号` 列。
|
||||
- 员工资产失败记录弹窗增加 `Sheet`、`Excel行号` 列。
|
||||
- API 调整
|
||||
- `ruoyi-ui/src/api/ccdiBaseStaff.js` 去掉 `updateSupport` 参数,保持单文件上传定义。
|
||||
|
||||
## 验证
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && npm run build:prod`
|
||||
- 页面联调覆盖:
|
||||
- 只导员工 Sheet
|
||||
- 只导资产 Sheet
|
||||
- 双 Sheet 同时导入
|
||||
- 两类失败记录入口分别展示
|
||||
|
||||
## 影响范围
|
||||
- `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||
- `ruoyi-ui/src/api/ccdiBaseStaff.js`
|
||||
@@ -0,0 +1,23 @@
|
||||
# 2026-04-22 招投标导入失败展示增强前端实施计划
|
||||
|
||||
## 1. 目标
|
||||
|
||||
- 调整招投标信息维护页面的“导入失败记录”弹窗
|
||||
- 让失败列表直接展示失败来源 `Sheet`、失败行号、失败原因
|
||||
|
||||
## 2. 涉及范围
|
||||
|
||||
- `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue`
|
||||
|
||||
## 3. 实施步骤
|
||||
|
||||
1. 在失败记录弹窗表格中新增 `失败Sheet` 列
|
||||
2. 在失败记录弹窗表格中新增 `失败行号` 列
|
||||
3. 保留现有 `采购事项ID / 项目名称 / 标的物名称 / 失败原因` 上下文信息
|
||||
4. 对多行场景直接展示后端返回的合并行号,不在前端重复解析
|
||||
|
||||
## 4. 验证方式
|
||||
|
||||
- 使用真实页面上传失败样本
|
||||
- 打开“查看导入失败记录”弹窗,核对主信息失败能显示 `招投标主信息 + 行号 + 原因`
|
||||
- 核对供应商失败能显示 `供应商明细 + 行号 + 原因`
|
||||
@@ -0,0 +1,35 @@
|
||||
# 员工亲属关系维护双 Sheet 导入前端实施计划
|
||||
|
||||
## 目标
|
||||
- 将员工亲属关系维护页面顶部两个导入按钮合并为一个。
|
||||
- 上传弹窗改为双 Sheet 提示和统一模板下载。
|
||||
- 保留原有两套任务轮询与失败记录入口,但失败记录列表统一展示 `Sheet / Excel行号 / 失败原因`。
|
||||
|
||||
## 实施内容
|
||||
- 页面入口调整
|
||||
- 删除“导入亲属资产信息”独立按钮。
|
||||
- 删除独立资产上传弹窗,仅保留统一导入弹窗。
|
||||
- 上传交互调整
|
||||
- 导入弹窗提示模板包含 `员工亲属关系信息` 和 `亲属资产信息` 两个 Sheet。
|
||||
- 下载模板文件名统一为“员工亲属关系维护导入模板”。
|
||||
- 上传成功后解析后端返回的 `relationTaskId`、`assetTaskId`。
|
||||
- 状态管理调整
|
||||
- 有 `relationTaskId` 时沿用原亲属关系任务轮询与失败记录缓存。
|
||||
- 有 `assetTaskId` 时沿用原亲属资产任务轮询与失败记录缓存。
|
||||
- 未返回的任务类型不主动清空既有历史失败记录状态。
|
||||
- 失败记录展示调整
|
||||
- 亲属关系失败记录弹窗新增 `Sheet`、`Excel行号` 列。
|
||||
- 亲属资产失败记录弹窗新增 `Sheet`、`Excel行号` 列。
|
||||
|
||||
## 验证
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && node tests/unit/staff-family-asset-detail-import-ui.test.js`
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && node tests/unit/staff-family-asset-submit-flow.test.js`
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && node tests/unit/staff-family-asset-maintenance-layout.test.js`
|
||||
- 页面联调覆盖:
|
||||
- 只导亲属关系 Sheet
|
||||
- 只导亲属资产 Sheet
|
||||
- 双 Sheet 同时导入
|
||||
- 两类失败记录列表都显示 `Sheet / Excel行号 / 失败原因`
|
||||
|
||||
## 影响范围
|
||||
- `ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue`
|
||||
@@ -0,0 +1,22 @@
|
||||
# 招投标供应商校验前端实施计划
|
||||
|
||||
## 目标
|
||||
- 将招投标信息维护页面新增、编辑弹窗中的供应商明细校验收敛为:
|
||||
- 供应商名称必填
|
||||
- 统一信用代码必填
|
||||
- 移除联系人、联系电话、银行账户,以及供应商名称/统一信用代码的内容校验提示。
|
||||
|
||||
## 实施内容
|
||||
- 调整 `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` 中的 `getSupplierFieldRules`
|
||||
- `supplierName` 仅保留必填规则。
|
||||
- `supplierUscc` 改为仅保留必填规则。
|
||||
- `contactPerson`、`contactPhone`、`supplierBankAccount` 不再返回校验规则。
|
||||
|
||||
## 验证
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && npm run build:prod`
|
||||
- 使用 Playwright 打开真实页面 `http://127.0.0.1:62319/maintain/purchaseTransaction`
|
||||
- 新增弹窗录入 `supplierUscc=ABC`、`contactPhone=123` 后可保存
|
||||
- 编辑弹窗录入 `supplierUscc=XYZ`、`contactPhone=abc123` 后可保存
|
||||
|
||||
## 产出文件
|
||||
- `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue`
|
||||
@@ -0,0 +1,40 @@
|
||||
# 员工信息维护双 Sheet 导入实施记录
|
||||
|
||||
## 本次修改
|
||||
- 后端将员工信息维护导入模板改为双 Sheet:
|
||||
- `员工信息`
|
||||
- `员工资产信息`
|
||||
- 后端导入入口统一到 `/ccdi/baseStaff/importData`,按有数据的 Sheet 分别提交员工导入任务与员工资产导入任务。
|
||||
- 员工信息导入移除了更新现有员工能力,现有员工 ID 或身份证号冲突统一进入失败记录。
|
||||
- 员工资产导入补充了重复校验,当前按 `personId + assetMainType + assetSubType + assetName` 识别重复,命中数据库或导入文件内重复时直接进入失败记录。
|
||||
- 前端删除独立“导入资产信息”按钮与独立资产上传弹窗,改为单入口上传双 Sheet 模板。
|
||||
- 前端上传成功后,按返回的两个任务 ID 分别沿用原有轮询、失败记录缓存和失败记录弹窗能力。
|
||||
- 修正员工导入失败记录字段为 `staffId`,保证失败记录列表能正确显示柜员号。
|
||||
- 员工失败记录与员工资产失败记录都增加了 `Sheet`、`Excel行号`、`失败原因` 定位信息。
|
||||
|
||||
## 影响范围
|
||||
- 后端
|
||||
- `ccdi-info-collection` 员工导入控制器、服务接口、异步导入服务、导入结果 VO
|
||||
- 前端
|
||||
- `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||
- `ruoyi-ui/src/api/ccdiBaseStaff.js`
|
||||
|
||||
## 验证情况
|
||||
- 后端编译
|
||||
- `mvn -pl ccdi-info-collection,ruoyi-admin -am -DskipTests compile`
|
||||
- 结果:通过
|
||||
- 前端构建
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && npm run build:prod`
|
||||
- 结果:通过
|
||||
- 备注:构建输出仍有既有体积告警,但未阻断构建
|
||||
|
||||
## 测试说明
|
||||
- 已补充员工双 Sheet 导入相关单测文件,并执行:
|
||||
- `mvn -pl ccdi-info-collection -am -Dtest=CcdiBaseStaffControllerTest,CcdiBaseStaffDualImportServiceTest,CcdiBaseStaffAssetImportServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test`
|
||||
- 结果:通过
|
||||
- 已通过 Playwright 在真实员工信息维护页面验证:
|
||||
- 页面仅保留一个“导入”按钮
|
||||
- 导入弹窗展示双 Sheet 提示文案
|
||||
- 两个失败入口可恢复显示
|
||||
- 员工失败弹窗显示 `Sheet / Excel行号 / 失败原因`
|
||||
- 资产失败弹窗显示 `Sheet / Excel行号 / 失败原因`
|
||||
@@ -0,0 +1,51 @@
|
||||
# 2026-04-22 员工信息维护真实页面测试执行记录
|
||||
|
||||
## 1. 执行内容
|
||||
|
||||
- 启动前端开发服务,端口使用 `1025`
|
||||
- 使用真实浏览器进入“信息维护-员工信息维护”页面
|
||||
- 完整验证以下链路:
|
||||
- 列表加载
|
||||
- 详情查看
|
||||
- 新增员工及资产
|
||||
- 编辑员工及资产
|
||||
- 删除
|
||||
- 导入模板下载
|
||||
- 双 Sheet 导入
|
||||
- 员工导入失败记录查看
|
||||
- 员工资产导入失败记录查看
|
||||
|
||||
## 2. 环境信息
|
||||
|
||||
- 前端地址:`http://127.0.0.1:1025`
|
||||
- 后端地址:`http://127.0.0.1:62318`
|
||||
- Mock 服务:`http://127.0.0.1:8000`
|
||||
- Node 版本:`v14.21.3`
|
||||
- 浏览器方式:Playwright headed 模式真实浏览器
|
||||
|
||||
## 3. 结果摘要
|
||||
|
||||
- 页面新增员工 `9260422` 成功,编辑成功,详情可正常展示资产明细。
|
||||
- 双 Sheet 导入成功写入员工 `9260423`,并成功向员工 `9260422` 导入资产 `导入资产车位A-0422`。
|
||||
- 员工导入失败记录正确命中 `该员工ID已存在`。
|
||||
- 员工资产导入失败记录正确命中 `员工资产导入仅支持员工本人证件号`。
|
||||
- 本轮新增与导入成功数据均已通过真实页面删除清理。
|
||||
- 页面本地导入缓存 `employee_import_last_task`、`employee_asset_import_last_task` 已清理。
|
||||
|
||||
## 4. 发现问题
|
||||
|
||||
- 员工详情弹窗中“所属部门”未正确回显。
|
||||
- 同一员工在列表中显示 `若依科技`,进入详情后该字段显示为 `-`。
|
||||
|
||||
## 5. 产出物
|
||||
|
||||
- 测试记录:
|
||||
- `/Users/wkc/Desktop/ccdi/ccdi/docs/tests/records/2026-04-22-base-staff-maintenance-browser-test-record.md`
|
||||
- 导入样本:
|
||||
- `/Users/wkc/Desktop/ccdi/ccdi/output/spreadsheet/base_staff_import_browser_mixed.xlsx`
|
||||
|
||||
## 6. 收尾情况
|
||||
|
||||
- 已关闭 Playwright 浏览器会话
|
||||
- 已关闭前端 `1025` 进程
|
||||
- 已停止本轮通过 `bin/restart_java_backend.sh` 拉起的后端进程
|
||||
@@ -0,0 +1,46 @@
|
||||
# 2026-04-22 招投标导入失败展示增强实施记录
|
||||
|
||||
## 1. 本次变更
|
||||
|
||||
- 招投标导入失败记录新增失败来源 `Sheet`
|
||||
- 招投标导入失败记录新增 Excel 失败行号
|
||||
- 招投标信息维护页面失败弹窗新增 `失败Sheet`、`失败行号` 展示列
|
||||
|
||||
## 2. 文档落点确认
|
||||
|
||||
- 后端实施计划:`/Users/wkc/Desktop/ccdi/ccdi/docs/plans/backend/2026-04-22-bidding-import-failure-display-backend-implementation.md`
|
||||
- 前端实施计划:`/Users/wkc/Desktop/ccdi/ccdi/docs/plans/frontend/2026-04-22-bidding-import-failure-display-frontend-implementation.md`
|
||||
- 实施记录:`/Users/wkc/Desktop/ccdi/ccdi/docs/reports/implementation/2026-04-22-bidding-import-failure-display-implementation.md`
|
||||
|
||||
## 3. 实施内容
|
||||
|
||||
- 在后端失败记录 VO 中新增 `sheetName`、`sheetRowNum`
|
||||
- 在导入服务中为主信息、供应商明细两类导入行建立行号上下文
|
||||
- 在主信息校验失败、供应商校验失败、主从关系失败等场景下统一返回失败来源 Sheet 与行号
|
||||
- 在前端失败弹窗中新增 `失败Sheet`、`失败行号` 列,直接展示后端返回值
|
||||
|
||||
## 4. 预期验证点
|
||||
|
||||
- 主信息失败记录显示 `招投标主信息`
|
||||
- 供应商失败记录显示 `供应商明细`
|
||||
- 失败行号与 Excel 实际数据行一致
|
||||
- 同一失败由多行触发时,页面可直接展示合并行号
|
||||
|
||||
## 5. 实际验证结果
|
||||
|
||||
- 后端执行 `mvn -pl ccdi-info-collection -am compile -DskipTests` 编译通过
|
||||
- 后端通过 `bin/restart_java_backend.sh` 完成重启,前端通过 `nvm use` 后启动真实页面进行验证
|
||||
- 在真实页面上传 `bidding_import_structure.xlsx` 后,导入任务 `f1026563-4bf3-4f1d-ae27-d3f9623547f4` 成功生成失败记录
|
||||
- 页面“查看导入失败记录”弹窗已展示以下表头:
|
||||
- `失败Sheet`
|
||||
- `失败行号`
|
||||
- `采购事项ID`
|
||||
- `项目名称`
|
||||
- `标的物名称`
|
||||
- `失败原因`
|
||||
- 真实页面已验证的失败展示样例:
|
||||
- `供应商明细 | 第8行 | 供应商明细Sheet中的采购事项ID不能为空`
|
||||
- `招投标主信息 | 第2行 | 采购事项ID[LSFXMOCKP2PUR001]已存在,请勿重复导入`
|
||||
- `招投标主信息 | 第3、4行 | 采购事项ID[IMPCOV20260422154511STR03]在招投标主信息Sheet中重复`
|
||||
- `供应商明细 | 第4、5行 | 采购事项ID[IMPCOV20260422154511STR02]缺少招投标主信息`
|
||||
- 本次验证样本未产生成功导入数据,无需额外回滚业务数据
|
||||
@@ -0,0 +1,48 @@
|
||||
# 员工亲属关系维护双 Sheet 导入实施记录
|
||||
|
||||
## 本次修改
|
||||
- 将员工亲属关系维护顶部两个导入按钮合并为一个统一导入入口。
|
||||
- 导入模板改为双 Sheet:
|
||||
- `员工亲属关系信息`
|
||||
- `亲属资产信息`
|
||||
- 导入提交入口统一到 `/ccdi/staffFmyRelation/importData`,按有数据的 Sheet 分别提交亲属关系任务和亲属资产任务。
|
||||
- 新增 `StaffFmyRelationImportSubmitResultVO`,返回 `relationTaskId`、`assetTaskId` 和提交提示文案。
|
||||
- 员工亲属关系失败记录与亲属资产失败记录都增加了 `Sheet`、`Excel行号`、`失败原因` 定位信息。
|
||||
- 前端删除独立亲属资产上传弹窗,但保留原有两套轮询和失败记录查看入口。
|
||||
|
||||
## 影响范围
|
||||
- 后端
|
||||
- `ccdi-info-collection` 员工亲属关系导入控制器、两类异步导入服务、失败记录 VO、导入提交结果 VO、相关控制器测试
|
||||
- 前端
|
||||
- `ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue`
|
||||
- `ruoyi-ui/tests/unit/staff-family-asset-detail-import-ui.test.js`
|
||||
|
||||
## 验证情况
|
||||
- 后端构建
|
||||
- `sh bin/restart_java_backend.sh restart`
|
||||
- 结果:通过,`ruoyi-admin` 已基于本次代码重新打包并启动成功。
|
||||
- 前端单测
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && node tests/unit/staff-family-asset-detail-import-ui.test.js`
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && node tests/unit/staff-family-asset-submit-flow.test.js`
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && node tests/unit/staff-family-asset-maintenance-layout.test.js`
|
||||
- 结果:通过
|
||||
- 后端编译/测试
|
||||
- `mvn -pl ccdi-info-collection -am -Dtest=CcdiStaffFmyRelationControllerTest,CcdiAssetInfoControllerTest -Dsurefire.failIfNoSpecifiedTests=false test`
|
||||
- 结果:通过,`Tests run: 10, Failures: 0, Errors: 0`
|
||||
- 真实页面实测
|
||||
- 前端端口:`1026`
|
||||
- 使用 Playwright 打开真实页面 `/maintain/staffFmyRelation`
|
||||
- 实测结果:
|
||||
- 页面顶部仅保留一个“导入”按钮
|
||||
- 从页面内下载到新的双 Sheet 模板,实际包含 `员工亲属关系信息`、`亲属资产信息` 两张表
|
||||
- 通过双 Sheet 失败样本上传后,页面同时出现“查看导入失败记录”和“查看亲属资产导入失败记录”两个失败入口
|
||||
- 后端接口核验
|
||||
- 使用双 Sheet 失败样本调用 `/ccdi/staffFmyRelation/importData`
|
||||
- 再分别查询 `/ccdi/staffFmyRelation/importFailures/{relationTaskId}` 与 `/ccdi/assetInfo/importFailures/{assetTaskId}`
|
||||
- 返回结果已确认包含:
|
||||
- 亲属关系失败记录:`sheetName=员工亲属关系信息`、`rowNum=2`、`errorMessage=员工身份证号格式不正确`
|
||||
- 亲属资产失败记录:`sheetName=亲属资产信息`、`rowNum=2`、`errorMessage=未找到亲属资产归属员工`
|
||||
|
||||
## 后续实测
|
||||
- 本轮已完成真实页面双 Sheet 导入实测与失败接口核验。
|
||||
- 若后续需要补充界面截图或失败弹窗截图,可直接复用 `output/playwright/base-staff-maintenance-test/` 下本轮产物继续追踪。
|
||||
@@ -0,0 +1,63 @@
|
||||
# 2026-04-23 `116.62.17.81:9444` Docker 部署记录
|
||||
|
||||
## 保存路径确认
|
||||
|
||||
- 目标目录:`docs/reports/implementation/`
|
||||
- 文档用途:记录本次部署执行过程、影响范围与验证结果
|
||||
- 路径检查结果:符合仓库实施记录归档规范
|
||||
|
||||
## 本次操作
|
||||
|
||||
- 在本地仓库 `/Users/wkc/Desktop/ccdi/ccdi` 执行部署
|
||||
- 前端先通过 `nvm use` 切换到 `ruoyi-ui/.nvmrc` 指定版本:`Node v14.21.3`
|
||||
- 执行后端打包:`mvn clean package -DskipTests`
|
||||
- 执行前端打包:`cd ruoyi-ui && npm run build:prod`
|
||||
- 执行部署脚本:`./deploy/deploy-to-nas.sh`
|
||||
- 部署目标:
|
||||
- SSH:`116.62.17.81:9444`
|
||||
- 远端目录:`/volume1/webapp/ccdi`
|
||||
- 宿主机内网地址:`192.168.0.111`
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 远端部署目录 `/volume1/webapp/ccdi` 已刷新为本次构建产物
|
||||
- Docker 服务已重建:
|
||||
- `ccdi-backend`
|
||||
- `ccdi-frontend`
|
||||
- `ccdi-lsfx-mock`
|
||||
- 本次仓库内仅新增本实施记录文档,无业务代码改动
|
||||
|
||||
## 验证结果
|
||||
|
||||
### 构建验证
|
||||
|
||||
- Maven 聚合打包成功,`ruoyi-admin/target/ruoyi-admin.jar` 已生成
|
||||
- Vue 生产构建成功,`ruoyi-ui/dist` 已生成
|
||||
- 前端构建期间仅出现体积告警,无构建失败
|
||||
|
||||
### 远端容器验证
|
||||
|
||||
- `docker compose ps` 结果:
|
||||
- `ccdi-backend`:`Up`
|
||||
- `ccdi-frontend`:`Up`
|
||||
- `ccdi-lsfx-mock`:`Up`
|
||||
- 端口监听结果:
|
||||
- `62318 -> backend:8080`
|
||||
- `62319 -> frontend:80`
|
||||
- `62320 -> backend network / mock:8000`
|
||||
|
||||
### 应用可用性验证
|
||||
|
||||
- 宿主机本机访问 `127.0.0.1` 返回正常:
|
||||
- `http://127.0.0.1:62319` 返回 `200 OK`
|
||||
- `http://127.0.0.1:62318/swagger-ui/index.html` 返回 `200`
|
||||
- `http://127.0.0.1:62320/docs` 返回 `200 OK`
|
||||
- 后端日志确认:
|
||||
- `nas` profile 已启用
|
||||
- TongWeb `8080` 已启动
|
||||
- `RuoYiApplication` 启动完成
|
||||
|
||||
## 额外说明
|
||||
|
||||
- 通过公网地址 `116.62.17.81:62318/62319/62320` 访问时,当前观测到的是 `Empty reply from server`
|
||||
- 由于宿主机实际网卡地址为 `192.168.0.111`,`116.62.17.81` 属于外层公网映射地址,因此当前应用侧与 Docker 侧均已正常,剩余问题若需继续处理,应排查公网入口到宿主机 `62318/62319/62320` 的端口转发或 NAT/网关链路
|
||||
@@ -0,0 +1,28 @@
|
||||
# 员工信息导入机构号校验实施记录
|
||||
|
||||
## 文档信息
|
||||
- 保存路径:`docs/reports/implementation/2026-04-23-base-staff-import-dept-validation-implementation.md`
|
||||
- 实施日期:2026-04-23
|
||||
- 关联范围:员工信息维护后端导入链路
|
||||
|
||||
## 本次修改内容
|
||||
1. 在 `CcdiBaseStaffImportServiceImpl` 中新增 `deptId` 校验逻辑,要求导入员工的所属部门必须在 `sys_dept` 中存在且处于正常、未删除状态。
|
||||
2. 机构号校验失败时,导入记录不入库,失败原因统一写入现有失败记录字段 `errorMessage`,错误文案为 `所属部门ID[xxx]不存在或已停用/删除,请检查机构号`。
|
||||
3. 补齐 `SysDeptMapper.xml` 中 `selectDeptById` 的 `del_flag` 字段映射,避免导入服务误把逻辑删除部门识别为有效部门。
|
||||
4. 补充员工导入服务层与异步导入流程单元测试,覆盖部门不存在、部门停用、部门删除和合法/非法混合导入场景。
|
||||
|
||||
## 影响范围
|
||||
- 后端:
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffImportServiceImpl.java`
|
||||
- `ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml`
|
||||
- 测试:
|
||||
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffImportServiceImplTest.java`
|
||||
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffDualImportServiceTest.java`
|
||||
|
||||
## 验证情况
|
||||
1. 定向单测:
|
||||
- 命令:`mvn -pl ccdi-info-collection -am -Dsurefire.failIfNoSpecifiedTests=false -Dtest=CcdiBaseStaffImportServiceImplTest,CcdiBaseStaffDualImportServiceTest test`
|
||||
- 结果:通过,`Tests run: 13, Failures: 0, Errors: 0, Skipped: 0`
|
||||
2. 编译校验:
|
||||
- 命令:`mvn -pl ccdi-info-collection,ruoyi-admin -am -DskipTests compile`
|
||||
- 结果:通过,Reactor Summary 中 `ccdi-info-collection`、`ruoyi-admin` 及其依赖模块均为 `SUCCESS`
|
||||
@@ -0,0 +1,44 @@
|
||||
# 招投标供应商校验调整实施记录
|
||||
|
||||
## 本次改动
|
||||
- 招投标信息维护页面新增、编辑弹窗中的供应商明细校验调整为仅保留:
|
||||
- 供应商名称必填
|
||||
- 统一信用代码必填
|
||||
- 移除了前端弹窗中供应商名称长度、统一信用代码格式、联系人长度、联系电话格式、银行账户长度校验。
|
||||
- 移除了新增/编辑接口 DTO 中对应的供应商内容校验,确保真实页面保存链路与弹窗规则一致。
|
||||
|
||||
## 关键文件
|
||||
- 前端
|
||||
- `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue`
|
||||
- 后端
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiPurchaseTransactionSupplierDTO.java`
|
||||
|
||||
## 验证结果
|
||||
- 后端编译
|
||||
- 命令:`mvn -pl ccdi-info-collection -am -DskipTests compile`
|
||||
- 结果:通过。
|
||||
- 后端重启
|
||||
- 命令:`sh bin/restart_java_backend.sh`
|
||||
- 结果:构建并重启成功。
|
||||
- 前端构建
|
||||
- 命令:`source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && npm run build:prod`
|
||||
- 结果:通过,仅有既有产物体积 warning。
|
||||
- Playwright 实页验证
|
||||
- 页面:`http://127.0.0.1:62319/maintain/purchaseTransaction`
|
||||
- 新增验证:
|
||||
- 采购事项ID:`AUTOBID20260423145630`
|
||||
- 供应商统一信用代码:`ABC`
|
||||
- 供应商联系电话:`123`
|
||||
- 结果:新增成功。
|
||||
- 编辑验证:
|
||||
- 项目名称改为:`校验放开回归项目-145630-编辑`
|
||||
- 供应商统一信用代码改为:`XYZ`
|
||||
- 供应商联系电话改为:`abc123`
|
||||
- 结果:修改成功。
|
||||
- 清理验证:
|
||||
- 删除 `AUTOBID20260423145630`
|
||||
- 结果:删除成功,列表总数回到 `2004`。
|
||||
|
||||
## 过程说明
|
||||
- 首轮真实页验证发现,前端规则放开后,新增接口仍因 `CcdiPurchaseTransactionSupplierDTO` 的 Bean Validation 拦截 `supplierUscc` 和 `contactPhone`。
|
||||
- 因此本次最终方案同时调整了前端弹窗规则与后端 DTO 校验,保证页面行为与需求一致。
|
||||
@@ -0,0 +1,188 @@
|
||||
# 信息维护页面搜索区四列栅格统一实施记录
|
||||
|
||||
## 1. 实施目标
|
||||
|
||||
将信息维护同批页面的搜索区统一为每行 4 个字段位的栅格布局,超过 4 个字段自动换行,最后一行不足 4 项时保留空白字段位,不拉伸现有字段;同时清理查询区内遗留的固定像素宽度写法。
|
||||
|
||||
## 2. 实施范围
|
||||
|
||||
本次实际调整的前端文件如下:
|
||||
|
||||
- `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiStaffTransfer/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiCreditInfo/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiEnterpriseBaseInfo/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiIntermediary/components/SearchForm.vue`
|
||||
- `ruoyi-ui/src/views/ccdiAccountInfo/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue`
|
||||
|
||||
本轮未提交的临时验证文件:
|
||||
|
||||
- `output/playwright/info-maintenance-search-grid-check.js`
|
||||
|
||||
## 3. 实施内容
|
||||
|
||||
### 3.1 查询区统一为四列栅格
|
||||
|
||||
- 将目标页面的头部查询表单从 `:inline="true"` 流式排布改为 `el-row + el-col :span="6"` 栅格排布
|
||||
- 每个查询项固定占一个字段位
|
||||
- 超过 4 个字段自动换到下一行
|
||||
- 最后一行不足 4 项时保留空白字段位,不做横向拉伸
|
||||
|
||||
### 3.2 字段内部控件统一铺满字段位
|
||||
|
||||
- 将查询区里的输入框、下拉框、日期范围、树选择统一改为 `style="width: 100%"`
|
||||
- 清理查询区中遗留的固定像素宽度写法,不再依赖 `style="width: Npx"` 控制布局
|
||||
- 补充查询区专用样式,使 `el-row` 使用换行布局,`el-col` 不再依赖旧浮动行为导致错位
|
||||
|
||||
### 3.3 保持原有交互和业务逻辑不变
|
||||
|
||||
- 未修改 `queryParams`、`handleQuery`、`resetQuery`
|
||||
- 未修改搜索、重置、业务按钮、`right-toolbar` 的行为
|
||||
- 日期范围仍按一个字段位处理,仅在所属字段位内铺满
|
||||
|
||||
## 4. TDD / 结构校验
|
||||
|
||||
### 4.1 先写临时结构校验脚本
|
||||
|
||||
新增临时脚本:
|
||||
|
||||
- `output/playwright/info-maintenance-search-grid-check.js`
|
||||
|
||||
脚本校验口径:
|
||||
|
||||
- 只识别头部查询表单,不误扫弹窗表单
|
||||
- 查询区是否采用 `el-col :span="6"`
|
||||
- 查询项数量是否符合预期
|
||||
- 查询区内是否仍保留 `style="width: Npx"` 固定像素宽度
|
||||
- 账户库管理页是否仍残留旧的查询区局部覆盖样式
|
||||
|
||||
### 4.2 先失败,再通过
|
||||
|
||||
首次执行命令:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && cd ruoyi-ui && nvm use >/dev/null && cd .. && node output/playwright/info-maintenance-search-grid-check.js
|
||||
```
|
||||
|
||||
初次结果:
|
||||
|
||||
- 12 个目标页面全部报出“未使用 `span=6`”和“仍存在固定像素宽度”的失败
|
||||
|
||||
页面改造完成后再次执行同一命令,结果为:
|
||||
|
||||
- `信息维护搜索区四列栅格结构校验通过`
|
||||
|
||||
## 5. 构建验证
|
||||
|
||||
### 5.1 Node 版本
|
||||
|
||||
执行命令:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && cd ruoyi-ui && nvm use
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
- 命中项目 `nvm` 配置
|
||||
- 使用项目要求的 Node 版本完成后续脚本和构建
|
||||
|
||||
### 5.2 前端构建
|
||||
|
||||
执行命令:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && cd ruoyi-ui && nvm use >/dev/null && npm run build:prod
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
- 构建成功
|
||||
- 无新增模板语法错误
|
||||
- 仅保留项目原有的体积告警
|
||||
|
||||
## 6. 真实页面浏览器验证
|
||||
|
||||
### 6.1 启动服务
|
||||
|
||||
前端开发服务:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && cd ruoyi-ui && nvm use >/dev/null && npm_config_port=1025 npm run dev
|
||||
```
|
||||
|
||||
后端服务:
|
||||
|
||||
```bash
|
||||
sh bin/restart_java_backend.sh start
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 本轮验证时后端原本未运行,因此使用仓库规定脚本启动
|
||||
- 使用真实登录页进入系统,账号为 `admin / admin123`
|
||||
|
||||
### 6.2 布局验证结果
|
||||
|
||||
使用 Playwright 进入以下代表页并读取查询区实际列位位置:
|
||||
|
||||
- `/maintain/baseStaff`
|
||||
- `/maintain/enterpriseBaseInfo`
|
||||
- `/maintain/accountInfo`
|
||||
- `/maintain/creditInfo`
|
||||
- `/maintain/intermediary`
|
||||
- `/maintain/staffTransfer`
|
||||
- `/maintain/purchaseTransaction`
|
||||
|
||||
验证结果:
|
||||
|
||||
- 员工信息维护:5 个字段,实际排布为 `4 + 1`
|
||||
- 实体库管理:8 个字段,实际排布为 `4 + 4`
|
||||
- 账户库管理:9 个字段,实际排布为 `4 + 4 + 1`
|
||||
- 征信维护:2 个字段,实际排布为单行 2 项,未拉伸成半屏布局
|
||||
- 中介库管理:4 个字段,实际排布为单行 4 列
|
||||
- 员工调动记录:6 个字段,实际排布为 `4 + 2`
|
||||
- 招投标信息维护:4 个字段,日期范围与其他查询项等宽,占 1 个字段位
|
||||
|
||||
浏览器读取到的列位位置表现一致:
|
||||
|
||||
- 同一行 4 列的左边界稳定为四等分位置
|
||||
- 超过 4 个字段的新一行从第一列起始位置重新排列
|
||||
- 最后一行不足 4 项时未发生拉伸
|
||||
|
||||
### 6.3 重置验证
|
||||
|
||||
在真实业务页面中使用 Playwright 做了两组输入与重置验证:
|
||||
|
||||
1. 员工信息维护
|
||||
- 向“姓名”输入框填入 `测试姓名`
|
||||
- 点击页面 `重置`
|
||||
- 再次读取输入框值,结果为空字符串
|
||||
|
||||
2. 账户库管理
|
||||
- 向“员工姓名”输入框填入 `验证员工`
|
||||
- 点击页面 `重置`
|
||||
- 再次读取输入框值,结果为空字符串
|
||||
|
||||
结论:
|
||||
|
||||
- 搜索区改为四列栅格后,`重置` 交互未受影响
|
||||
|
||||
## 7. 进程清理
|
||||
|
||||
本轮验证结束后已关闭:
|
||||
|
||||
- 前端开发服务 `npm run dev`
|
||||
- 后端服务 `sh bin/restart_java_backend.sh start` 启动的后端进程,使用 `sh bin/restart_java_backend.sh stop` 停止
|
||||
- Playwright 浏览器会话
|
||||
|
||||
## 8. 风险与说明
|
||||
|
||||
- 当前工作区存在其他与本任务无关的未提交改动,本次未回退这些既有改动,仅在搜索区四列栅格范围内继续修改目标页面
|
||||
- 临时结构校验脚本位于 `output/playwright/`,用于本轮 TDD 校验,不纳入 git 提交
|
||||
@@ -59,7 +59,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</select>
|
||||
|
||||
<select id="selectDeptById" parameterType="Long" resultMap="SysDeptResult">
|
||||
select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status,
|
||||
select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag,
|
||||
(select dept_name from sys_dept where dept_id = d.parent_id) parent_name
|
||||
from sys_dept d
|
||||
where d.dept_id = #{deptId}
|
||||
@@ -156,4 +156,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
update sys_dept set del_flag = '2' where dept_id = #{deptId}
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
</mapper>
|
||||
|
||||
@@ -51,10 +51,10 @@ export function importTemplate() {
|
||||
})
|
||||
}
|
||||
|
||||
// 导入员工
|
||||
export function importData(data, updateSupport) {
|
||||
// 导入员工双Sheet数据
|
||||
export function importData(data) {
|
||||
return request({
|
||||
url: '/ccdi/baseStaff/importData?updateSupport=' + updateSupport,
|
||||
url: '/ccdi/baseStaff/importData',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
|
||||
@@ -6,62 +6,81 @@
|
||||
ref="queryForm"
|
||||
:model="queryParams"
|
||||
size="small"
|
||||
:inline="true"
|
||||
class="query-form"
|
||||
label-width="96px"
|
||||
>
|
||||
<el-form-item label="所属人类型">
|
||||
<el-select v-model="queryParams.ownerType" placeholder="请选择所属人类型" clearable style="width: 180px">
|
||||
<el-option label="员工" value="EMPLOYEE" />
|
||||
<el-option label="员工关系人" value="RELATION" />
|
||||
<el-option label="中介" value="INTERMEDIARY" />
|
||||
<el-option label="外部人员" value="EXTERNAL" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="员工姓名">
|
||||
<el-input v-model="queryParams.staffName" placeholder="请输入员工姓名" clearable style="width: 220px" @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="账户范围">
|
||||
<el-select v-model="queryParams.bankScope" placeholder="请选择账户范围" clearable style="width: 160px">
|
||||
<el-option label="行内" value="INTERNAL" />
|
||||
<el-option label="行外" value="EXTERNAL" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关系类型">
|
||||
<el-select v-model="queryParams.relationType" placeholder="请选择关系类型" clearable style="width: 180px">
|
||||
<el-option v-for="item in relationTypeOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="账户姓名">
|
||||
<el-input v-model="queryParams.accountName" placeholder="请输入账户姓名" clearable style="width: 220px" @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="账户类型">
|
||||
<el-select v-model="queryParams.accountType" placeholder="请选择账户类型" clearable style="width: 180px">
|
||||
<el-option label="银行账户" value="BANK" />
|
||||
<el-option label="证券账户" value="SECURITIES" />
|
||||
<el-option label="支付账户" value="PAYMENT" />
|
||||
<el-option label="其他" value="OTHER" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否实控">
|
||||
<el-select v-model="queryParams.isActualControl" placeholder="请选择是否实控" clearable style="width: 180px">
|
||||
<el-option label="是" :value="1" />
|
||||
<el-option label="否" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="风险等级">
|
||||
<el-select v-model="queryParams.riskLevel" placeholder="请选择风险等级" clearable style="width: 180px">
|
||||
<el-option label="LOW" value="LOW" />
|
||||
<el-option label="MEDIUM" value="MEDIUM" />
|
||||
<el-option label="HIGH" value="HIGH" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 160px">
|
||||
<el-option label="正常" :value="1" />
|
||||
<el-option label="已销户" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="所属人类型">
|
||||
<el-select v-model="queryParams.ownerType" placeholder="请选择所属人类型" clearable style="width: 100%">
|
||||
<el-option label="员工" value="EMPLOYEE" />
|
||||
<el-option label="员工关系人" value="RELATION" />
|
||||
<el-option label="中介" value="INTERMEDIARY" />
|
||||
<el-option label="外部人员" value="EXTERNAL" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="员工姓名">
|
||||
<el-input v-model="queryParams.staffName" placeholder="请输入员工姓名" clearable style="width: 100%" @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="账户范围">
|
||||
<el-select v-model="queryParams.bankScope" placeholder="请选择账户范围" clearable style="width: 100%">
|
||||
<el-option label="行内" value="INTERNAL" />
|
||||
<el-option label="行外" value="EXTERNAL" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="关系类型">
|
||||
<el-select v-model="queryParams.relationType" placeholder="请选择关系类型" clearable style="width: 100%">
|
||||
<el-option v-for="item in relationTypeOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="账户姓名">
|
||||
<el-input v-model="queryParams.accountName" placeholder="请输入账户姓名" clearable style="width: 100%" @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="账户类型">
|
||||
<el-select v-model="queryParams.accountType" placeholder="请选择账户类型" clearable style="width: 100%">
|
||||
<el-option label="银行账户" value="BANK" />
|
||||
<el-option label="证券账户" value="SECURITIES" />
|
||||
<el-option label="支付账户" value="PAYMENT" />
|
||||
<el-option label="其他" value="OTHER" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="是否实控">
|
||||
<el-select v-model="queryParams.isActualControl" placeholder="请选择是否实控" clearable style="width: 100%">
|
||||
<el-option label="是" :value="1" />
|
||||
<el-option label="否" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="风险等级">
|
||||
<el-select v-model="queryParams.riskLevel" placeholder="请选择风险等级" clearable style="width: 100%">
|
||||
<el-option label="LOW" value="LOW" />
|
||||
<el-option label="MEDIUM" value="MEDIUM" />
|
||||
<el-option label="HIGH" value="HIGH" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
|
||||
<el-option label="正常" :value="1" />
|
||||
<el-option label="已销户" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
@@ -838,6 +857,19 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.query-form ::v-deep .el-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-col {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-form-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.account-page {
|
||||
min-height: calc(100vh - 84px);
|
||||
background: #f5f7fa;
|
||||
@@ -881,10 +913,4 @@ export default {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.query-form ::v-deep .el-form-item {
|
||||
display: block;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,45 +1,57 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="柜员号" prop="staffId">
|
||||
<el-input
|
||||
v-model="queryParams.staffId"
|
||||
placeholder="请输入7位柜员号"
|
||||
clearable
|
||||
maxlength="7"
|
||||
@input="queryParams.staffId = queryParams.staffId.replace(/[^\d]/g, '')"
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属部门" prop="deptId">
|
||||
<treeselect v-model="queryParams.deptId" :options="deptOptions" :show-count="true" placeholder="请选择所属部门" clearable style="width: 240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号" prop="idCard">
|
||||
<el-input
|
||||
v-model="queryParams.idCard"
|
||||
placeholder="请输入身份证号"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="状态" clearable style="width: 240px">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="在职" value="0" />
|
||||
<el-option label="离职" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="100px" class="query-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="柜员号" prop="staffId">
|
||||
<el-input
|
||||
v-model="queryParams.staffId"
|
||||
placeholder="请输入7位柜员号"
|
||||
clearable
|
||||
maxlength="7"
|
||||
@input="queryParams.staffId = queryParams.staffId.replace(/[^\d]/g, '')"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="所属部门" prop="deptId">
|
||||
<treeselect v-model="queryParams.deptId" :options="deptOptions" :show-count="true" placeholder="请选择所属部门" clearable style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="身份证号" prop="idCard">
|
||||
<el-input
|
||||
v-model="queryParams.idCard"
|
||||
placeholder="请输入身份证号"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="状态" clearable style="width: 100%">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="在职" value="0" />
|
||||
<el-option label="离职" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
@@ -69,16 +81,6 @@
|
||||
v-hasPermi="['ccdi:employee:import']"
|
||||
>导入</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="el-icon-upload"
|
||||
size="mini"
|
||||
@click="handleAssetImport"
|
||||
v-hasPermi="['ccdi:employee:import']"
|
||||
>导入资产信息</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-if="showFailureButton">
|
||||
<el-tooltip
|
||||
:content="getLastImportTooltip()"
|
||||
@@ -386,7 +388,7 @@
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:headers="upload.headers"
|
||||
:action="upload.url + '?updateSupport=' + upload.updateSupport"
|
||||
:action="upload.url"
|
||||
:disabled="upload.isUploading"
|
||||
:on-progress="handleFileUploadProgress"
|
||||
:on-success="handleFileSuccess"
|
||||
@@ -396,11 +398,13 @@
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的员工数据
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
|
||||
</div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<span>仅允许导入"xls"或"xlsx"格式文件。</span>
|
||||
<span>模板包含“员工信息”和“员工资产信息”两个Sheet,可单独填写其中一个,也可同时填写两个。</span>
|
||||
</div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<span>仅允许导入"xls"或"xlsx"格式文件;员工信息Sheet命中现有员工会直接报错,资产信息Sheet仅支持员工本人资产。</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
@@ -417,41 +421,6 @@
|
||||
@close="handleImportResultClose"
|
||||
/>
|
||||
|
||||
<el-dialog :title="assetUpload.title" :visible.sync="assetUpload.open" width="400px" append-to-body @close="handleAssetImportDialogClose" v-loading="assetUpload.isUploading" element-loading-text="正在导入员工资产数据,请稍候..." element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.7)">
|
||||
<el-upload
|
||||
ref="assetUpload"
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:headers="assetUpload.headers"
|
||||
:action="assetUpload.url"
|
||||
:disabled="assetUpload.isUploading"
|
||||
:on-progress="handleAssetFileUploadProgress"
|
||||
:on-success="handleAssetFileSuccess"
|
||||
:auto-upload="false"
|
||||
drag
|
||||
>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline;" @click="importAssetTemplate">下载员工资产模板</el-link>
|
||||
</div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<span>仅支持导入员工本人资产数据,文件需为"xls"或"xlsx"格式,导入身份证号需与员工本人一致。</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitAssetFileForm" :loading="assetUpload.isUploading">确 定</el-button>
|
||||
<el-button @click="assetUpload.open = false" :disabled="assetUpload.isUploading">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<import-result-dialog
|
||||
:visible.sync="assetImportResultVisible"
|
||||
:content="assetImportResultContent"
|
||||
title="员工资产导入结果"
|
||||
@close="handleAssetImportResultClose"
|
||||
/>
|
||||
|
||||
<!-- 导入失败记录对话框 -->
|
||||
<el-dialog
|
||||
title="导入失败记录"
|
||||
@@ -468,6 +437,8 @@
|
||||
/>
|
||||
|
||||
<el-table :data="failureList" v-loading="failureLoading">
|
||||
<el-table-column label="Sheet" prop="sheetName" align="center" min-width="120" />
|
||||
<el-table-column label="Excel行号" prop="rowNum" align="center" width="100" />
|
||||
<el-table-column label="姓名" prop="name" align="center" />
|
||||
<el-table-column label="柜员号" prop="staffId" align="center" />
|
||||
<el-table-column label="身份证号" prop="idCard" align="center" />
|
||||
@@ -510,7 +481,8 @@
|
||||
/>
|
||||
|
||||
<el-table :data="assetFailureList" v-loading="assetFailureLoading">
|
||||
<el-table-column label="归属员工身份证号" prop="familyId" align="center" min-width="180" />
|
||||
<el-table-column label="Sheet" prop="sheetName" align="center" min-width="120" />
|
||||
<el-table-column label="Excel行号" prop="rowNum" align="center" width="100" />
|
||||
<el-table-column label="资产实际持有人身份证号" prop="personId" align="center" min-width="180" />
|
||||
<el-table-column label="资产大类" prop="assetMainType" align="center" />
|
||||
<el-table-column label="资产小类" prop="assetSubType" align="center" />
|
||||
@@ -649,8 +621,6 @@ export default {
|
||||
title: "",
|
||||
// 是否禁用上传
|
||||
isUploading: false,
|
||||
// 是否更新已经存在的数据
|
||||
updateSupport: 0,
|
||||
// 设置上传的请求头部
|
||||
headers: { Authorization: "Bearer " + getToken() },
|
||||
// 上传的地址
|
||||
@@ -659,8 +629,6 @@ export default {
|
||||
// 导入结果弹窗
|
||||
importResultVisible: false,
|
||||
importResultContent: "",
|
||||
assetImportResultVisible: false,
|
||||
assetImportResultContent: "",
|
||||
// 轮询定时器
|
||||
pollingTimer: null,
|
||||
assetPollingTimer: null,
|
||||
@@ -679,13 +647,6 @@ export default {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
assetUpload: {
|
||||
open: false,
|
||||
title: "",
|
||||
isUploading: false,
|
||||
headers: { Authorization: "Bearer " + getToken() },
|
||||
url: process.env.VUE_APP_BASE_API + "/ccdi/baseStaff/asset/importData"
|
||||
},
|
||||
assetFailureDialogVisible: false,
|
||||
assetFailureList: [],
|
||||
assetFailureLoading: false,
|
||||
@@ -1214,13 +1175,9 @@ export default {
|
||||
},
|
||||
/** 导入按钮操作 */
|
||||
handleImport() {
|
||||
this.upload.title = "员工数据导入";
|
||||
this.upload.title = "员工信息维护导入";
|
||||
this.upload.open = true;
|
||||
},
|
||||
handleAssetImport() {
|
||||
this.assetUpload.title = "员工资产数据导入";
|
||||
this.assetUpload.open = true;
|
||||
},
|
||||
/** 导入对话框关闭事件 */
|
||||
handleImportDialogClose() {
|
||||
this.$nextTick(() => {
|
||||
@@ -1229,130 +1186,79 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
handleAssetImportDialogClose() {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.assetUpload) {
|
||||
this.$refs.assetUpload.clearFiles();
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 下载模板操作 */
|
||||
importTemplate() {
|
||||
this.download('ccdi/baseStaff/importTemplate', {}, `员工信息模板_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
importAssetTemplate() {
|
||||
this.download('ccdi/baseStaff/asset/importTemplate', {}, `员工资产信息模板_${new Date().getTime()}.xlsx`)
|
||||
this.download('ccdi/baseStaff/importTemplate', {}, `员工信息维护导入模板_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
// 文件上传中处理
|
||||
handleFileUploadProgress(event, file, fileList) {
|
||||
handleFileUploadProgress() {
|
||||
this.upload.isUploading = true;
|
||||
},
|
||||
handleAssetFileUploadProgress() {
|
||||
this.assetUpload.isUploading = true;
|
||||
},
|
||||
// 文件上传成功处理
|
||||
handleFileSuccess(response, file, fileList) {
|
||||
handleFileSuccess(response) {
|
||||
this.upload.isUploading = false;
|
||||
this.upload.open = false;
|
||||
|
||||
if (response.code === 200) {
|
||||
// 验证响应数据完整性
|
||||
if (!response.data || !response.data.taskId) {
|
||||
this.$modal.msgError('导入任务创建失败:缺少任务ID');
|
||||
this.upload.isUploading = false;
|
||||
this.upload.open = true;
|
||||
return;
|
||||
}
|
||||
if (response.code !== 200) {
|
||||
this.$modal.msgError(response.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const taskId = response.data.taskId;
|
||||
const importResult = response.data || {};
|
||||
const staffTaskId = importResult.staffTaskId;
|
||||
const assetTaskId = importResult.assetTaskId;
|
||||
if (!staffTaskId && !assetTaskId) {
|
||||
this.$modal.msgError('导入任务创建失败:缺少任务ID');
|
||||
this.upload.open = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除旧的导入记录(防止并发)
|
||||
if (staffTaskId) {
|
||||
if (this.pollingTimer) {
|
||||
clearInterval(this.pollingTimer);
|
||||
this.pollingTimer = null;
|
||||
}
|
||||
|
||||
this.clearImportTaskFromStorage();
|
||||
|
||||
// 保存新任务的初始状态
|
||||
this.saveImportTaskToStorage({
|
||||
taskId: taskId,
|
||||
taskId: staffTaskId,
|
||||
status: 'PROCESSING',
|
||||
timestamp: Date.now(),
|
||||
hasFailures: false
|
||||
});
|
||||
|
||||
// 重置状态
|
||||
this.showFailureButton = false;
|
||||
this.currentTaskId = taskId;
|
||||
|
||||
// 显示后台处理提示
|
||||
this.$notify({
|
||||
title: '导入任务已提交',
|
||||
message: '正在后台处理中,处理完成后将通知您',
|
||||
type: 'info',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
// 开始轮询检查状态
|
||||
this.startImportStatusPolling(taskId);
|
||||
} else {
|
||||
this.$modal.msgError(response.msg);
|
||||
this.currentTaskId = staffTaskId;
|
||||
this.startImportStatusPolling(staffTaskId);
|
||||
}
|
||||
},
|
||||
handleAssetFileSuccess(response) {
|
||||
this.assetUpload.isUploading = false;
|
||||
this.assetUpload.open = false;
|
||||
|
||||
if (response.code === 200) {
|
||||
if (!response.data || !response.data.taskId) {
|
||||
this.$modal.msgError('员工资产导入任务创建失败:缺少任务ID');
|
||||
this.assetUpload.isUploading = false;
|
||||
this.assetUpload.open = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const taskId = response.data.taskId;
|
||||
|
||||
if (assetTaskId) {
|
||||
if (this.assetPollingTimer) {
|
||||
clearInterval(this.assetPollingTimer);
|
||||
this.assetPollingTimer = null;
|
||||
}
|
||||
|
||||
this.clearAssetImportTaskFromStorage();
|
||||
this.saveAssetImportTaskToStorage({
|
||||
taskId: taskId,
|
||||
taskId: assetTaskId,
|
||||
status: 'PROCESSING',
|
||||
timestamp: Date.now(),
|
||||
hasFailures: false
|
||||
});
|
||||
|
||||
this.assetShowFailureButton = false;
|
||||
this.assetCurrentTaskId = taskId;
|
||||
|
||||
this.$notify({
|
||||
title: '员工资产导入任务已提交',
|
||||
message: '正在后台处理中,处理完成后将通知您',
|
||||
type: 'info',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
this.startAssetImportStatusPolling(taskId);
|
||||
} else if (response.code === 601) {
|
||||
this.$modal.msgWarning(response.msg);
|
||||
} else {
|
||||
this.$modal.msgError(response.msg);
|
||||
this.assetCurrentTaskId = assetTaskId;
|
||||
this.startAssetImportStatusPolling(assetTaskId);
|
||||
}
|
||||
|
||||
this.$notify({
|
||||
title: '导入任务已提交',
|
||||
message: importResult.message || '正在后台处理中,处理完成后将通知您',
|
||||
type: 'info',
|
||||
duration: 3000
|
||||
});
|
||||
},
|
||||
// 导入结果弹窗关闭
|
||||
handleImportResultClose() {
|
||||
this.importResultVisible = false;
|
||||
this.importResultContent = "";
|
||||
},
|
||||
handleAssetImportResultClose() {
|
||||
this.assetImportResultVisible = false;
|
||||
this.assetImportResultContent = "";
|
||||
},
|
||||
/** 开始轮询导入状态 */
|
||||
startImportStatusPolling(taskId) {
|
||||
let pollCount = 0;
|
||||
@@ -1560,15 +1466,25 @@ export default {
|
||||
// 提交上传文件
|
||||
submitFileForm() {
|
||||
this.$refs.upload.submit();
|
||||
},
|
||||
submitAssetFileForm() {
|
||||
this.$refs.assetUpload.submit();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.query-form ::v-deep .el-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-col {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-form-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.detail-form .el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@@ -1,39 +1,49 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="身份证号" prop="personId">
|
||||
<el-input
|
||||
v-model="queryParams.personId"
|
||||
placeholder="请输入身份证号"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="统一社会信用代码" prop="socialCreditCode">
|
||||
<el-input
|
||||
v-model="queryParams.socialCreditCode"
|
||||
placeholder="请输入统一社会信用代码"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="企业名称" prop="enterpriseName">
|
||||
<el-input
|
||||
v-model="queryParams.enterpriseName"
|
||||
placeholder="请输入企业名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 240px">
|
||||
<el-option label="有效" :value="1" />
|
||||
<el-option label="无效" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="120px" class="query-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="身份证号" prop="personId">
|
||||
<el-input
|
||||
v-model="queryParams.personId"
|
||||
placeholder="请输入身份证号"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="统一社会信用代码" prop="socialCreditCode">
|
||||
<el-input
|
||||
v-model="queryParams.socialCreditCode"
|
||||
placeholder="请输入统一社会信用代码"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="企业名称" prop="enterpriseName">
|
||||
<el-input
|
||||
v-model="queryParams.enterpriseName"
|
||||
placeholder="请输入企业名称"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
|
||||
<el-option label="有效" :value="1" />
|
||||
<el-option label="无效" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
@@ -831,6 +841,19 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.query-form ::v-deep .el-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-col {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-form-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
@@ -1,40 +1,50 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="信贷客户身份证号" prop="personId">
|
||||
<el-input
|
||||
v-model="queryParams.personId"
|
||||
placeholder="请输入身份证号(支持模糊搜索)"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="关系类型" prop="relationType">
|
||||
<el-select v-model="queryParams.relationType" placeholder="请选择关系类型" clearable style="width: 240px">
|
||||
<el-option
|
||||
v-for="item in relationTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关系人姓名" prop="relationName">
|
||||
<el-input
|
||||
v-model="queryParams.relationName"
|
||||
placeholder="请输入关系人姓名"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 240px">
|
||||
<el-option label="有效" :value="1" />
|
||||
<el-option label="无效" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="100px" class="query-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="信贷客户身份证号" prop="personId">
|
||||
<el-input
|
||||
v-model="queryParams.personId"
|
||||
placeholder="请输入身份证号(支持模糊搜索)"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="关系类型" prop="relationType">
|
||||
<el-select v-model="queryParams.relationType" placeholder="请选择关系类型" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in relationTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="关系人姓名" prop="relationName">
|
||||
<el-input
|
||||
v-model="queryParams.relationName"
|
||||
placeholder="请输入关系人姓名"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
|
||||
<el-option label="有效" :value="1" />
|
||||
<el-option label="无效" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
@@ -1087,6 +1097,19 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.query-form ::v-deep .el-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-col {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-form-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
@@ -1,39 +1,49 @@
|
||||
<template>
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名称"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="证件号" prop="certificateNo">
|
||||
<el-input
|
||||
v-model="queryParams.certificateNo"
|
||||
placeholder="请输入证件号"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="记录类型" prop="recordType">
|
||||
<el-select v-model="queryParams.recordType" placeholder="请选择记录类型" clearable style="width: 180px">
|
||||
<el-option label="中介本人" value="INTERMEDIARY" />
|
||||
<el-option label="中介亲属" value="RELATIVE" />
|
||||
<el-option label="关联机构" value="ENTERPRISE_RELATION" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关联中介信息" prop="relatedIntermediaryKeyword">
|
||||
<el-input
|
||||
v-model="queryParams.relatedIntermediaryKeyword"
|
||||
placeholder="请输入关联中介姓名或证件号"
|
||||
clearable
|
||||
style="width: 260px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="100px" class="query-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名称"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="证件号" prop="certificateNo">
|
||||
<el-input
|
||||
v-model="queryParams.certificateNo"
|
||||
placeholder="请输入证件号"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="记录类型" prop="recordType">
|
||||
<el-select v-model="queryParams.recordType" placeholder="请选择记录类型" clearable style="width: 100%">
|
||||
<el-option label="中介本人" value="INTERMEDIARY" />
|
||||
<el-option label="中介亲属" value="RELATIVE" />
|
||||
<el-option label="关联机构" value="ENTERPRISE_RELATION" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="关联中介信息" prop="relatedIntermediaryKeyword">
|
||||
<el-input
|
||||
v-model="queryParams.relatedIntermediaryKeyword"
|
||||
placeholder="请输入关联中介姓名或证件号"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
@@ -57,3 +67,18 @@ export default {
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.query-form ::v-deep .el-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-col {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-form-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,44 +1,54 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="标的物名称" prop="subjectName">
|
||||
<el-input
|
||||
v-model="queryParams.subjectName"
|
||||
placeholder="请输入标的物名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请人" prop="applicantName">
|
||||
<el-input
|
||||
v-model="queryParams.applicantName"
|
||||
placeholder="请输入申请人"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="100px" class="query-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="标的物名称" prop="subjectName">
|
||||
<el-input
|
||||
v-model="queryParams.subjectName"
|
||||
placeholder="请输入标的物名称"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="申请人" prop="applicantName">
|
||||
<el-input
|
||||
v-model="queryParams.applicantName"
|
||||
placeholder="请输入申请人"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="申请日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 100%"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
@@ -1360,6 +1370,19 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.query-form ::v-deep .el-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-col {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-form-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
@@ -1,40 +1,50 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="员工姓名" prop="personName">
|
||||
<el-input
|
||||
v-model="queryParams.personName"
|
||||
placeholder="请输入员工姓名"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="关系类型" prop="relationType">
|
||||
<el-select v-model="queryParams.relationType" placeholder="请选择关系类型" clearable style="width: 240px">
|
||||
<el-option
|
||||
v-for="item in relationTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关系人姓名" prop="relationName">
|
||||
<el-input
|
||||
v-model="queryParams.relationName"
|
||||
placeholder="请输入关系人姓名"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 240px">
|
||||
<el-option label="有效" :value="1" />
|
||||
<el-option label="无效" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="100px" class="query-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="员工姓名" prop="personName">
|
||||
<el-input
|
||||
v-model="queryParams.personName"
|
||||
placeholder="请输入员工姓名"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="关系类型" prop="relationType">
|
||||
<el-select v-model="queryParams.relationType" placeholder="请选择关系类型" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in relationTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="关系人姓名" prop="relationName">
|
||||
<el-input
|
||||
v-model="queryParams.relationName"
|
||||
placeholder="请输入关系人姓名"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
|
||||
<el-option label="有效" :value="1" />
|
||||
<el-option label="无效" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
@@ -64,16 +74,6 @@
|
||||
v-hasPermi="['ccdi:staffFmyRelation:import']"
|
||||
>导入</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="el-icon-upload"
|
||||
size="mini"
|
||||
@click="handleAssetImport"
|
||||
v-hasPermi="['ccdi:staffFmyRelation:import']"
|
||||
>导入亲属资产信息</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-if="showFailureButton">
|
||||
<el-tooltip
|
||||
:content="getLastImportTooltip()"
|
||||
@@ -534,6 +534,10 @@
|
||||
width="400px"
|
||||
append-to-body
|
||||
@close="handleImportDialogClose"
|
||||
v-loading="upload.isUploading"
|
||||
element-loading-text="正在导入数据,请稍候..."
|
||||
element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(0, 0, 0, 0.7)"
|
||||
>
|
||||
<el-upload
|
||||
ref="upload"
|
||||
@@ -553,7 +557,10 @@
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
|
||||
</div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<span>仅允许导入"xls"或"xlsx"格式文件。</span>
|
||||
<span>模板包含“员工亲属关系信息”和“亲属资产信息”两个Sheet,可单独填写其中一个,也可同时填写两个。</span>
|
||||
</div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<span>仅允许导入"xls"或"xlsx"格式文件;亲属资产Sheet将根据关系人证件号自动识别归属员工。</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
@@ -562,44 +569,6 @@
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
:title="assetUpload.title"
|
||||
:visible.sync="assetUpload.open"
|
||||
width="400px"
|
||||
append-to-body
|
||||
@close="handleAssetImportDialogClose"
|
||||
v-loading="assetUpload.isUploading"
|
||||
element-loading-text="正在导入亲属资产数据,请稍候..."
|
||||
element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(0, 0, 0, 0.7)"
|
||||
>
|
||||
<el-upload
|
||||
ref="assetUpload"
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:headers="assetUpload.headers"
|
||||
:action="assetUpload.url"
|
||||
:disabled="assetUpload.isUploading"
|
||||
:on-progress="handleAssetFileUploadProgress"
|
||||
:on-success="handleAssetFileSuccess"
|
||||
:auto-upload="false"
|
||||
drag
|
||||
>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline;" @click="importAssetTemplate">下载亲属资产模板</el-link>
|
||||
</div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<span>仅允许导入"xls"或"xlsx"格式文件,系统将根据关系人证件号自动识别归属员工。</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitAssetFileForm" :loading="assetUpload.isUploading">确 定</el-button>
|
||||
<el-button @click="assetUpload.open = false" :disabled="assetUpload.isUploading">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导入失败记录对话框 -->
|
||||
<el-dialog
|
||||
title="导入失败记录"
|
||||
@@ -616,6 +585,8 @@
|
||||
/>
|
||||
|
||||
<el-table :data="failureList" v-loading="failureLoading">
|
||||
<el-table-column label="Sheet" prop="sheetName" align="center" min-width="120"/>
|
||||
<el-table-column label="Excel行号" prop="rowNum" align="center" width="100"/>
|
||||
<el-table-column label="员工身份证号" prop="personId" align="center" width="180"/>
|
||||
<el-table-column label="关系类型" prop="relationType" align="center" width="100"/>
|
||||
<el-table-column label="关系人姓名" prop="relationName" align="center" width="120"/>
|
||||
@@ -652,6 +623,8 @@
|
||||
/>
|
||||
|
||||
<el-table :data="assetFailureList" v-loading="assetFailureLoading">
|
||||
<el-table-column label="Sheet" prop="sheetName" align="center" min-width="120" />
|
||||
<el-table-column label="Excel行号" prop="rowNum" align="center" width="100" />
|
||||
<el-table-column label="关系人证件号" prop="personId" align="center" min-width="180" />
|
||||
<el-table-column label="资产大类" prop="assetMainType" align="center" min-width="120" />
|
||||
<el-table-column label="资产小类" prop="assetSubType" align="center" min-width="120" />
|
||||
@@ -803,13 +776,6 @@ export default {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
assetUpload: {
|
||||
open: false,
|
||||
title: "",
|
||||
isUploading: false,
|
||||
headers: { Authorization: "Bearer " + getToken() },
|
||||
url: process.env.VUE_APP_BASE_API + "/ccdi/assetInfo/importData"
|
||||
},
|
||||
assetImportPollingTimer: null,
|
||||
assetShowFailureButton: false,
|
||||
assetCurrentTaskId: null,
|
||||
@@ -1209,113 +1175,79 @@ export default {
|
||||
},
|
||||
/** 导入按钮操作 */
|
||||
handleImport() {
|
||||
this.upload.title = "员工亲属关系数据导入";
|
||||
this.upload.title = "员工亲属关系维护导入";
|
||||
this.upload.open = true;
|
||||
},
|
||||
handleAssetImport() {
|
||||
this.assetUpload.title = "亲属资产数据导入";
|
||||
this.assetUpload.open = true;
|
||||
},
|
||||
/** 下载模板操作 */
|
||||
importTemplate() {
|
||||
this.download('ccdi/staffFmyRelation/importTemplate', {}, `员工亲属关系导入模板_${new Date().getTime()}.xlsx`);
|
||||
},
|
||||
importAssetTemplate() {
|
||||
this.download('ccdi/assetInfo/importTemplate', {}, `亲属资产信息模板_${new Date().getTime()}.xlsx`);
|
||||
this.download('ccdi/staffFmyRelation/importTemplate', {}, `员工亲属关系维护导入模板_${new Date().getTime()}.xlsx`);
|
||||
},
|
||||
// 文件上传中处理
|
||||
handleFileUploadProgress(event, file, fileList) {
|
||||
this.upload.isUploading = true;
|
||||
},
|
||||
handleAssetFileUploadProgress() {
|
||||
this.assetUpload.isUploading = true;
|
||||
},
|
||||
// 文件上传成功处理
|
||||
handleFileSuccess(response, file, fileList) {
|
||||
this.upload.isUploading = false;
|
||||
this.upload.open = false;
|
||||
|
||||
if (response.code === 200) {
|
||||
if (!response.data || !response.data.taskId) {
|
||||
this.$modal.msgError('导入任务创建失败:缺少任务ID');
|
||||
this.upload.isUploading = false;
|
||||
this.upload.open = true;
|
||||
return;
|
||||
}
|
||||
if (response.code !== 200) {
|
||||
this.$modal.msgError(response.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const taskId = response.data.taskId;
|
||||
const importResult = response.data || {}
|
||||
const relationTaskId = importResult.relationTaskId
|
||||
const assetTaskId = importResult.assetTaskId
|
||||
|
||||
if (!relationTaskId && !assetTaskId) {
|
||||
this.$modal.msgError('导入任务创建失败:缺少任务ID');
|
||||
this.upload.open = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (relationTaskId) {
|
||||
if (this.importPollingTimer) {
|
||||
clearInterval(this.importPollingTimer);
|
||||
this.importPollingTimer = null;
|
||||
}
|
||||
|
||||
this.clearImportTaskFromStorage();
|
||||
|
||||
this.saveImportTaskToStorage({
|
||||
taskId: taskId,
|
||||
taskId: relationTaskId,
|
||||
status: 'PROCESSING',
|
||||
timestamp: Date.now(),
|
||||
hasFailures: false
|
||||
});
|
||||
|
||||
this.showFailureButton = false;
|
||||
this.currentTaskId = taskId;
|
||||
|
||||
this.$notify({
|
||||
title: '导入任务已提交',
|
||||
message: '正在后台处理中,处理完成后将通知您',
|
||||
type: 'info',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
this.startImportStatusPolling(taskId);
|
||||
} else {
|
||||
this.$modal.msgError(response.msg);
|
||||
this.currentTaskId = relationTaskId;
|
||||
this.startImportStatusPolling(relationTaskId);
|
||||
}
|
||||
},
|
||||
handleAssetFileSuccess(response) {
|
||||
this.assetUpload.isUploading = false;
|
||||
this.assetUpload.open = false;
|
||||
|
||||
if (response.code === 200) {
|
||||
if (!response.data || !response.data.taskId) {
|
||||
this.$modal.msgError('亲属资产导入任务创建失败:缺少任务ID');
|
||||
this.assetUpload.isUploading = false;
|
||||
this.assetUpload.open = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const taskId = response.data.taskId;
|
||||
|
||||
if (assetTaskId) {
|
||||
if (this.assetImportPollingTimer) {
|
||||
clearInterval(this.assetImportPollingTimer);
|
||||
this.assetImportPollingTimer = null;
|
||||
}
|
||||
|
||||
this.clearAssetImportTaskFromStorage();
|
||||
|
||||
this.saveAssetImportTaskToStorage({
|
||||
taskId: taskId,
|
||||
taskId: assetTaskId,
|
||||
status: 'PROCESSING',
|
||||
timestamp: Date.now(),
|
||||
hasFailures: false
|
||||
});
|
||||
|
||||
this.assetShowFailureButton = false;
|
||||
this.assetCurrentTaskId = taskId;
|
||||
|
||||
this.$notify({
|
||||
title: '亲属资产导入任务已提交',
|
||||
message: '正在后台处理中,处理完成后将通知您',
|
||||
type: 'info',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
this.startAssetImportStatusPolling(taskId);
|
||||
} else {
|
||||
this.$modal.msgError(response.msg);
|
||||
this.assetCurrentTaskId = assetTaskId;
|
||||
this.startAssetImportStatusPolling(assetTaskId);
|
||||
}
|
||||
|
||||
this.$notify({
|
||||
title: '导入任务已提交',
|
||||
message: importResult.message || '正在后台处理中,处理完成后将通知您',
|
||||
type: 'info',
|
||||
duration: 3000
|
||||
});
|
||||
},
|
||||
/** 开始轮询导入状态 */
|
||||
startImportStatusPolling(taskId) {
|
||||
@@ -1497,9 +1429,6 @@ export default {
|
||||
submitFileForm() {
|
||||
this.$refs.upload.submit();
|
||||
},
|
||||
submitAssetFileForm() {
|
||||
this.$refs.assetUpload.submit();
|
||||
},
|
||||
// 关闭导入对话框
|
||||
handleImportDialogClose() {
|
||||
this.upload.isUploading = false;
|
||||
@@ -1509,14 +1438,6 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
handleAssetImportDialogClose() {
|
||||
this.assetUpload.isUploading = false;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.assetUpload) {
|
||||
this.$refs.assetUpload.clearFiles();
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 保存导入任务到localStorage
|
||||
*/
|
||||
@@ -1644,6 +1565,19 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.query-form ::v-deep .el-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-col {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-form-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
@@ -1,59 +1,73 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="招聘项目名称" prop="recruitName">
|
||||
<el-input
|
||||
v-model="queryParams.recruitName"
|
||||
placeholder="请输入招聘项目名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="职位名称" prop="posName">
|
||||
<el-input
|
||||
v-model="queryParams.posName"
|
||||
placeholder="请输入职位名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="候选人姓名" prop="candName">
|
||||
<el-input
|
||||
v-model="queryParams.candName"
|
||||
placeholder="请输入候选人姓名"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="证件号码" prop="candId">
|
||||
<el-input
|
||||
v-model="queryParams.candId"
|
||||
placeholder="请输入证件号码"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="录用情况" prop="admitStatus">
|
||||
<el-select v-model="queryParams.admitStatus" placeholder="请选择录用情况" clearable style="width: 240px">
|
||||
<el-option label="录用" value="录用" />
|
||||
<el-option label="未录用" value="未录用" />
|
||||
<el-option label="放弃" value="放弃" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="招聘类型" prop="recruitType">
|
||||
<el-select v-model="queryParams.recruitType" placeholder="请选择招聘类型" clearable style="width: 240px">
|
||||
<el-option
|
||||
v-for="item in recruitTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="100px" class="query-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="招聘项目名称" prop="recruitName">
|
||||
<el-input
|
||||
v-model="queryParams.recruitName"
|
||||
placeholder="请输入招聘项目名称"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="职位名称" prop="posName">
|
||||
<el-input
|
||||
v-model="queryParams.posName"
|
||||
placeholder="请输入职位名称"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="候选人姓名" prop="candName">
|
||||
<el-input
|
||||
v-model="queryParams.candName"
|
||||
placeholder="请输入候选人姓名"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="证件号码" prop="candId">
|
||||
<el-input
|
||||
v-model="queryParams.candId"
|
||||
placeholder="请输入证件号码"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="录用情况" prop="admitStatus">
|
||||
<el-select v-model="queryParams.admitStatus" placeholder="请选择录用情况" clearable style="width: 100%">
|
||||
<el-option label="录用" value="录用" />
|
||||
<el-option label="未录用" value="未录用" />
|
||||
<el-option label="放弃" value="放弃" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="招聘类型" prop="recruitType">
|
||||
<el-select v-model="queryParams.recruitType" placeholder="请选择招聘类型" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in recruitTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
@@ -1501,6 +1515,19 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.query-form ::v-deep .el-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-col {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-form-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
@@ -1,64 +1,78 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="员工工号" prop="staffId">
|
||||
<el-select v-model="queryParams.staffId" placeholder="请选择员工工号" clearable filterable style="width: 240px">
|
||||
<el-option
|
||||
v-for="item in staffOptions"
|
||||
:key="item.staffId"
|
||||
:label="item.staffId"
|
||||
:value="item.staffId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="员工姓名" prop="staffName">
|
||||
<el-input
|
||||
v-model="queryParams.staffName"
|
||||
placeholder="请输入员工姓名"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="调动类型" prop="transferType">
|
||||
<el-select v-model="queryParams.transferType" placeholder="请选择调动类型" clearable style="width: 240px">
|
||||
<el-option
|
||||
v-for="dict in dict.type.ccdi_transfer_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="调动日期" prop="transferDateRange">
|
||||
<el-date-picker
|
||||
v-model="queryParams.transferDateRange"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="调动前部门" prop="deptNameBefore">
|
||||
<el-input
|
||||
v-model="queryParams.deptNameBefore"
|
||||
placeholder="请输入调动前部门"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="调动后部门" prop="deptNameAfter">
|
||||
<el-input
|
||||
v-model="queryParams.deptNameAfter"
|
||||
placeholder="请输入调动后部门"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="100px" class="query-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="员工工号" prop="staffId">
|
||||
<el-select v-model="queryParams.staffId" placeholder="请选择员工工号" clearable filterable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in staffOptions"
|
||||
:key="item.staffId"
|
||||
:label="item.staffId"
|
||||
:value="item.staffId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="员工姓名" prop="staffName">
|
||||
<el-input
|
||||
v-model="queryParams.staffName"
|
||||
placeholder="请输入员工姓名"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="调动类型" prop="transferType">
|
||||
<el-select v-model="queryParams.transferType" placeholder="请选择调动类型" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in dict.type.ccdi_transfer_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="调动日期" prop="transferDateRange">
|
||||
<el-date-picker
|
||||
v-model="queryParams.transferDateRange"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="调动前部门" prop="deptNameBefore">
|
||||
<el-input
|
||||
v-model="queryParams.deptNameBefore"
|
||||
placeholder="请输入调动前部门"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="调动后部门" prop="deptNameAfter">
|
||||
<el-input
|
||||
v-model="queryParams.deptNameAfter"
|
||||
placeholder="请输入调动后部门"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
@@ -955,4 +969,16 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.query-form ::v-deep .el-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-col {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.query-form ::v-deep .el-form-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -9,25 +9,23 @@ const componentPath = path.resolve(
|
||||
const source = fs.readFileSync(componentPath, "utf8");
|
||||
|
||||
[
|
||||
"导入亲属资产信息",
|
||||
"查看亲属资产导入失败记录",
|
||||
"assetUpload",
|
||||
"assetImportPollingTimer",
|
||||
"assetCurrentTaskId",
|
||||
"assetFailureDialogVisible",
|
||||
"staff_fmy_asset_import_last_task",
|
||||
"亲属资产数据导入",
|
||||
"亲属资产导入失败记录",
|
||||
"viewAssetImportFailures()",
|
||||
"handleAssetImport()",
|
||||
"handleAssetFileSuccess(response)",
|
||||
"startAssetImportStatusPolling(taskId)",
|
||||
"getAssetFailureList()",
|
||||
"clearAssetImportHistory()",
|
||||
'from "@/api/ccdiAssetInfo"',
|
||||
"/ccdi/assetInfo/importData",
|
||||
"ccdi/assetInfo/importTemplate",
|
||||
"亲属资产信息模板_",
|
||||
"relationTaskId",
|
||||
"assetTaskId",
|
||||
"模板包含“员工亲属关系信息”和“亲属资产信息”两个Sheet,可单独填写其中一个,也可同时填写两个。",
|
||||
"/ccdi/staffFmyRelation/importData",
|
||||
"ccdi/staffFmyRelation/importTemplate",
|
||||
"员工亲属关系维护导入模板_",
|
||||
].forEach((token) => {
|
||||
assert(source.includes(token), `亲属资产导入 UI 缺少关键结构或状态: ${token}`);
|
||||
});
|
||||
@@ -42,6 +40,15 @@ const source = fs.readFileSync(componentPath, "utf8");
|
||||
});
|
||||
|
||||
[
|
||||
"导入亲属资产信息",
|
||||
"assetUpload",
|
||||
"handleAssetImport()",
|
||||
"handleAssetFileSuccess(response)",
|
||||
"submitAssetFileForm()",
|
||||
"handleAssetImportDialogClose()",
|
||||
"/ccdi/assetInfo/importData",
|
||||
"ccdi/assetInfo/importTemplate",
|
||||
"亲属资产信息模板_",
|
||||
"@/api/ccdiBaseStaffAsset",
|
||||
"/ccdi/baseStaff/asset/importData",
|
||||
"ccdi/baseStaff/asset/importTemplate",
|
||||
|
||||
Reference in New Issue
Block a user