This commit is contained in:
2026-07-01 18:17:27 +08:00
parent d72c86cc14
commit 3304b9113f
19 changed files with 1271 additions and 23 deletions
@@ -42,7 +42,7 @@ public class SysDataUserPullController {
@ApiOperation("分页数据")
@Ok("json:{locked:'password|idCard'}")
public Result pageData(@Valid SysDataUserPullPageForm pageForm) {
Sql sql = Sqls.create("select us.*,su.`name` AS unit_name from sys_user_source us left join sys_unit su ON su.id = us.unitId $condition");
Sql sql = Sqls.create("select us.*,su.`name` AS unitName from sys_user_source us left join sys_unit su ON su.id = us.unitId $condition");
Cnd cnd = Cnd.NEW();
cnd.andEX(Sys_user_source::getPullTime, "=", pageForm.getPullTime());
cnd.and(Cnd.likeEX(Sys_user_source::getUsername, pageForm.getUserName()));
@@ -30,12 +30,17 @@ import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
/**
@@ -179,6 +184,7 @@ public class AssetManageController {
@At
@ApiOperation("按条件导出资产台账")
@SaCheckPermission("asset.manage")
@Ok("void")
@SLog(type = "assetManage", tag = "资产台账", msg = "按条件导出资产台账")
public void doExport(AssetPageForm pageForm, HttpServletResponse response) {
Sql sql = assetService.getSql(pageForm);
@@ -213,4 +219,53 @@ public class AssetManageController {
e.printStackTrace();
}
}
/**
* 导入资产台账,controller 只负责接收文件,具体解析和业务转换放在 service 层。
*
* @param file 页面上传的资产导入 Excel
* @return 导入结果,包含成功数量和失败明细
*/
@At
@ApiOperation("导入资产台账")
@Aop(TransAop.READ_COMMITTED)
@SLog(type = "assetManage", tag = "资产台账", msg = "导入资产台账")
@SaCheckPermission("asset.manage")
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
public Result doImport(@Param("file") TempFile file) {
return Result.success(assetService.importAssets(file));
}
/**
* 下载资产导入模板,字段与导入解析 VO 保持一致,不包含预计报废时间。
*
* @param response 文件下载响应
*/
@At
@Ok("void")
@ApiOperation("下载资产导入模板")
@SaCheckPermission("asset.manage")
public void downloadTemplate(HttpServletResponse response) {
List<ExcelExportEntity> entities = new ArrayList<>();
entities.add(new ExcelExportEntity("资产编号", "assetNumber", 20));
entities.add(new ExcelExportEntity("资产名称", "assetName", 20));
entities.add(new ExcelExportEntity("资产类别", "assetCategoryName", 20));
entities.add(new ExcelExportEntity("资产规格型号", "assetSpecs", 20));
entities.add(new ExcelExportEntity("供应商名称", "assetSupplierName", 20));
entities.add(new ExcelExportEntity("采购方式", "assetFundingSubjectName", 20));
entities.add(new ExcelExportEntity("用途", "assetPurposeName", 20));
entities.add(new ExcelExportEntity("单价", "assetUnitPrice", 20));
entities.add(new ExcelExportEntity("数量", "assetQuantity", 20));
entities.add(new ExcelExportEntity("资产类型", "assetTypeName", 20));
entities.add(new ExcelExportEntity("开始使用日期", "assetUsedDate", 20));
entities.add(new ExcelExportEntity("负责人", "assetUseUserName", 20));
entities.add(new ExcelExportEntity("使用/管理部门", "assetUseUnionName", 20));
entities.add(new ExcelExportEntity("存放地点", "assetStorageLocation", 20));
entities.add(new ExcelExportEntity("使用状况", "assetUsageStateName", 20));
entities.add(new ExcelExportEntity("备注", "assetNotes", 30));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, Collections.emptyList());
CommonDownloadUtil.download("资产导入模板.xlsx", workbook, response);
}
}
@@ -0,0 +1,65 @@
package com.budwk.app.zhgh.dayofficework.asset.mode;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.budwk.app.base.model.ExcelImportError;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.Date;
/**
* 资产台账导入模板行数据,字段与资产新增页面红框内可录入字段保持一致。
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class AssetImportExcelMode extends ExcelImportError {
@Excel(name = "资产编号", width = 20)
private String assetNumber;
@Excel(name = "资产名称", width = 20)
private String assetName;
@Excel(name = "资产类别", width = 20)
private String assetCategoryName;
@Excel(name = "资产规格型号", width = 20)
private String assetSpecs;
@Excel(name = "供应商名称", width = 20)
private String assetSupplierName;
@Excel(name = "采购方式", width = 20)
private String assetFundingSubjectName;
@Excel(name = "用途", width = 20)
private String assetPurposeName;
@Excel(name = "单价", width = 20)
private BigDecimal assetUnitPrice;
@Excel(name = "数量", width = 20)
private Integer assetQuantity;
@Excel(name = "资产类型", width = 20)
private String assetTypeName;
@Excel(name = "开始使用日期", width = 20, format = "yyyy-MM-dd")
private Date assetUsedDate;
@Excel(name = "负责人", width = 20)
private String assetUseUserName;
@Excel(name = "使用/管理部门", width = 20)
private String assetUseUnionName;
@Excel(name = "存放地点", width = 20)
private String assetStorageLocation;
@Excel(name = "使用状况", width = 20)
private String assetUsageStateName;
@Excel(name = "备注", width = 30)
private String assetNotes;
}
@@ -1,10 +1,13 @@
package com.budwk.app.zhgh.dayofficework.asset.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.base.model.ExcelImportRes;
import com.budwk.app.zhgh.dayofficework.asset.mode.AssetImportExcelMode;
import com.budwk.app.zhgh.dayofficework.asset.model.Asset;
import com.budwk.app.zhgh.dayofficework.asset.param.AssetPageForm;
import org.nutz.dao.sql.Sql;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.upload.TempFile;
public interface AssetService extends BaseService<Asset> {
@@ -12,4 +15,12 @@ public interface AssetService extends BaseService<Asset> {
Sql getSql(AssetPageForm pageForm);
NutMap findOne(String id);
/**
* 导入资产台账数据,负责 Excel 解析、字段转换、预计报废时间计算和折旧记录初始化。
*
* @param file 页面上传的资产 Excel 文件
* @return 导入结果,包含成功数和失败明细
*/
ExcelImportRes<AssetImportExcelMode> importAssets(TempFile file);
}
@@ -1,21 +1,39 @@
package com.budwk.app.zhgh.dayofficework.asset.service.impl;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.model.ExcelImportRes;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_dict;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.sys.services.SysDictService;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.asset.mode.AssetImportExcelMode;
import com.budwk.app.zhgh.dayofficework.asset.model.Asset;
import com.budwk.app.zhgh.dayofficework.asset.model.AssetCategory;
import com.budwk.app.zhgh.dayofficework.asset.model.AssetDepreciationRecord;
import com.budwk.app.zhgh.dayofficework.asset.model.AssetStocktaking;
import com.budwk.app.zhgh.dayofficework.asset.param.AssetPageForm;
import com.budwk.app.zhgh.dayofficework.asset.service.AssetDepreciationRecordService;
import com.budwk.app.zhgh.dayofficework.asset.service.AssetService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.upload.TempFile;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author zhf
@@ -28,6 +46,12 @@ public class AssetServiceImpl extends BaseServiceImpl<Asset> implements AssetSer
super(dao);
}
@Inject
private AssetDepreciationRecordService assetDepreciationRecordService;
@Inject
private SysDictService sysDictService;
@Override
public Sql getSql(AssetPageForm pageForm) {
Sql sql = Sqls.create("""
@@ -83,4 +107,260 @@ public class AssetServiceImpl extends BaseServiceImpl<Asset> implements AssetSer
Cnd.where("assetId", "=", id)));
return nutMap;
}
/**
* 导入资产台账数据,逐行完成字段校验和新增,失败行不影响其他行入库。
*
* @param file 页面上传的资产 Excel 文件
* @return 导入统计结果和失败明细
*/
@Override
public ExcelImportRes<AssetImportExcelMode> importAssets(TempFile file) {
List<AssetImportExcelMode> importRows = ExcelImportUtil.importExcel(file.getFile(), AssetImportExcelMode.class, new ImportParams());
ExcelImportRes<AssetImportExcelMode> importRes = new ExcelImportRes<>();
importRes.setTotalRecords(importRows.size());
for (int i = 0; i < importRows.size(); i++) {
AssetImportExcelMode row = importRows.get(i);
List<String> errors = new ArrayList<>();
Asset asset = buildImportAsset(row, errors);
if (!errors.isEmpty()) {
row.setErrInfo(String.join(";", errors), i + 2);
continue;
}
insertAssetWithDepreciation(asset);
}
importRes.setErrorDetails(importRows.stream().filter(row -> StrUtil.isNotBlank(row.getErrMsg())).collect(Collectors.toList()));
importRes.setFailedCount(importRes.getErrorDetails().size());
importRes.setSuccessCount(Math.max(importRows.size() - importRes.getFailedCount(), 0));
return importRes;
}
/**
* 将 Excel 行转换为资产实体,包含字典值、单选值、负责人和部门等页面字段转换。
*
* @param row Excel 导入行
* @param errors 当前行错误收集器
* @return 可入库的资产实体
*/
private Asset buildImportAsset(AssetImportExcelMode row, List<String> errors) {
Asset asset = new Asset();
asset.setAssetNumber(StrUtil.trim(row.getAssetNumber()));
asset.setAssetName(requiredText(row.getAssetName(), "资产名称", errors));
AssetCategory category = resolveAssetCategory(row.getAssetCategoryName(), errors);
if (category != null) {
asset.setAssetCategoryId(category.getId());
}
asset.setAssetSpecs(requiredText(row.getAssetSpecs(), "资产规格型号", errors));
asset.setAssetSupplierName(requiredText(row.getAssetSupplierName(), "供应商名称", errors));
asset.setAssetFundingSubjectName(resolveFixedOption(row.getAssetFundingSubjectName(), "采购方式", Set.of("网购", "线下", "采购"), errors));
asset.setAssetPurposeName(resolveFixedOption(row.getAssetPurposeName(), "用途", Set.of("职工之家建设", "日常办公", "其他"), errors));
asset.setAssetUnitPrice(row.getAssetUnitPrice());
asset.setAssetQuantity(row.getAssetQuantity());
if (asset.getAssetUnitPrice() == null) {
errors.add("单价不能为空");
}
if (asset.getAssetQuantity() == null || asset.getAssetQuantity() <= 0) {
errors.add("数量不能为空且必须大于0");
}
asset.setAssetTypeCode(resolveAssetType(row.getAssetTypeName(), errors));
asset.setAssetUsedDate(row.getAssetUsedDate());
if (asset.getAssetUsedDate() == null) {
errors.add("开始使用日期不能为空");
}
NutMap user = resolveAssetUseUser(row.getAssetUseUserName(), errors);
if (user != null) {
asset.setAssetUseUserId(user.getString("id"));
}
Sys_union union = resolveAssetUseUnion(row.getAssetUseUnionName(), asset.getAssetTypeCode(), user, errors);
if (union != null) {
asset.setAssetUseUnionId(union.getId());
asset.setAssetUseUnionName(union.getName());
}
asset.setAssetStorageLocation(requiredText(row.getAssetStorageLocation(), "存放地点", errors));
asset.setAssetUsageStateName(resolveDictName(row.getAssetUsageStateName(), "ASSET_USAGE_STATE", "使用状况", errors));
asset.setAssetNotes(StrUtil.trim(row.getAssetNotes()));
if (category != null && asset.getAssetUsedDate() != null) {
asset.setAssetRetiredAssetsDate(calculateRetiredAssetsDate(asset.getAssetUsedDate(), category.getDepreciationYear()));
}
return asset;
}
/**
* 新增资产并按现有新增逻辑初始化折旧记录。
*
* @param asset 已完成转换和校验的资产
*/
private void insertAssetWithDepreciation(Asset asset) {
if (StrUtil.isBlank(asset.getAssetNumber())) {
asset.setAssetNumber(nextAssetNumber());
}
asset.setApplyUserId(SecurityUtil.getUserId());
insert(asset);
AssetDepreciationRecord record = assetDepreciationRecordService.initAssetDepreciationRecord(asset.getAssetUnitPrice(), asset.getAssetQuantity(), asset.getAssetUsedDate(), asset.getAssetRetiredAssetsDate());
if (record != null) {
record.setAssetId(asset.getId());
assetDepreciationRecordService.insert(record);
}
}
/**
* 按新增资产页面逻辑计算预计报废时间:开始使用日期加资产类别折旧年限。
*
* @param assetUsedDate 开始使用日期
* @param depreciationYear 资产类别折旧年限
* @return 预计报废时间
*/
private java.util.Date calculateRetiredAssetsDate(java.util.Date assetUsedDate, int depreciationYear) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(assetUsedDate);
calendar.add(Calendar.YEAR, depreciationYear);
return calendar.getTime();
}
/**
* 生成与新增资产一致的资产编号:当前年份 + 4 位流水。
*
* @return 新资产编号
*/
private String nextAssetNumber() {
int year = Calendar.getInstance().get(Calendar.YEAR);
Sql sql = Sqls.create("""
SELECT count(1) FROM `asset` where LEFT(assetNumber,4) = @year and LENGTH(assetNumber) = 8
""").setParam("year", year);
int count = count(sql);
return year + String.format("%04d", count + 1);
}
/**
* 按资产类别名称、编码或 ID 匹配资产类别。
*/
private AssetCategory resolveAssetCategory(String value, List<String> errors) {
String text = requiredText(value, "资产类别", errors);
if (StrUtil.isBlank(text)) {
return null;
}
AssetCategory category = dao().fetch(AssetCategory.class, Cnd.where("categoryName", "=", text).or("categoryCode", "=", text).or("id", "=", text));
if (category == null) {
errors.add("资产类别不存在:" + text);
}
return category;
}
/**
* 将资产类型单选文本转换为保存值。
*/
private Integer resolveAssetType(String value, List<String> errors) {
String text = requiredText(value, "资产类型", errors);
if (StrUtil.isBlank(text)) {
return null;
}
if ("校工会资产".equals(text) || "1".equals(text)) {
return 1;
}
if ("分工会资产".equals(text) || "2".equals(text)) {
return 2;
}
errors.add("资产类型只能填写校工会资产或分工会资产");
return null;
}
/**
* 使用/管理部门:校工会资产固定为校工会;分工会资产按导入值或负责人所属工会转换。
*/
private Sys_union resolveAssetUseUnion(String value, Integer assetTypeCode, NutMap user, List<String> errors) {
if (assetTypeCode != null && assetTypeCode == 1) {
Sys_union union = new Sys_union();
union.setId(user == null ? null : user.getString("unionId"));
union.setName("校工会");
return union;
}
String text = StrUtil.trim(value);
if (StrUtil.isBlank(text) && user != null) {
text = user.getString("unionName");
}
if (StrUtil.isBlank(text)) {
errors.add("使用/管理部门不能为空");
return null;
}
Sys_union union = dao().fetch(Sys_union.class, Cnd.where("name", "=", text).or("unionCode", "=", text).or("id", "=", text));
if (union == null) {
errors.add("使用/管理部门不存在:" + text);
}
return union;
}
/**
* 负责人支持工号、姓名、用户 ID,以及页面下拉常见的“姓名-工号”格式。
*/
private NutMap resolveAssetUseUser(String value, List<String> errors) {
String text = requiredText(value, "负责人", errors);
if (StrUtil.isBlank(text)) {
return null;
}
String keyword = text.contains("-") ? StrUtil.subAfter(text, "-", true) : text;
Sql sql = Sqls.create("""
SELECT
id,
loginname,
username,
unionName,
unionId
FROM
`vw_user`
WHERE
id = @keyword
OR loginname = @keyword
OR username = @keyword
""").setParam("keyword", keyword);
NutMap user = (NutMap) dao().execute(sql.setCallback(Sqls.callback.map())).getResult();
if (user == null) {
errors.add("负责人不存在:" + text);
}
return user;
}
/**
* 将数据字典文本或编码转换为当前系统保存的字典名称。
*/
private String resolveDictName(String value, String dictCode, String fieldName, List<String> errors) {
String text = requiredText(value, fieldName, errors);
if (StrUtil.isBlank(text)) {
return null;
}
Sys_dict dict = sysDictService.getSubListByCode(dictCode).stream()
.filter(item -> text.equals(item.getName()) || text.equals(item.getCode()))
.findFirst()
.orElse(null);
if (dict == null) {
errors.add(fieldName + "字典值不存在:" + text);
return null;
}
return dict.getName();
}
/**
* 校验固定选项字段,导入值按新增页面下拉选项保存为中文名称。
*/
private String resolveFixedOption(String value, String fieldName, Set<String> options, List<String> errors) {
String text = requiredText(value, fieldName, errors);
if (StrUtil.isBlank(text)) {
return null;
}
if (!options.contains(text)) {
errors.add(fieldName + "只能填写:" + String.join("、", options));
return null;
}
return text;
}
/**
* 必填文本统一去空格,便于导入错误提示保持一致。
*/
private String requiredText(String value, String fieldName, List<String> errors) {
String text = StrUtil.trim(value);
if (StrUtil.isBlank(text)) {
errors.add(fieldName + "不能为空");
}
return text;
}
}
@@ -65,10 +65,10 @@ public class MemberInfoGroupController {
public Result pageData(MemberInfoPageForm pageForm) {
Sql sql = memberInfoService.getSql(pageForm);
Pagination<NutMap> pagination = memberInfoService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
for (NutMap row : pagination.getList()) {
row.put("mobile",DesensitizedUtil.mobilePhone(row.getString("mobile")));
row.put("idCard",DesensitizedUtil.idCardNum(row.getString("idCard"), 3, 4));
}
// for (NutMap row : pagination.getList()) {
// row.put("mobile",DesensitizedUtil.mobilePhone(row.getString("mobile")));
// row.put("idCard",DesensitizedUtil.idCardNum(row.getString("idCard"), 3, 4));
// }
return Result.success(pagination);
}
@@ -0,0 +1,113 @@
package com.budwk.app.zhgh.staffmanage.member.controller.info;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffmanage.member.models.MemberChild;
import com.budwk.app.zhgh.staffmanage.member.param.pageform.MemberInfoPageForm;
import com.budwk.app.zhgh.staffmanage.member.service.MemberChildService;
import com.budwk.app.zhgh.staffmanage.member.service.MemberInfoService;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.ArrayList;
import java.util.List;
/**
* 个人信息维护控制器,提供会员基础信息只读展示和子女信息维护入口。
*/
@IocBean
@Ok("json:full")
@At("/platform/member/info/maintain")
public class MemberPersonalInfoMaintainController {
@Inject
private MemberInfoService memberInfoService;
@Inject
private MemberChildService memberChildService;
/**
* 个人信息维护列表页,查询条件和列表字段复用会员档案管理页面。
*/
@At("")
@Ok("beetl:/platform/zhgh/staffmanage/member/info/maintain/index.html")
@SaCheckPermission(value = {"member.info.maintain", "member.info.group"}, mode = SaMode.OR)
public void index() {
}
/**
* 个人信息维护编辑页,会员原有字段只读,子女信息允许维护。
*/
@At("/edit")
@Ok("beetl:/platform/zhgh/staffmanage/member/info/maintain/edit.html")
@SaCheckPermission(value = {"member.info.maintain", "member.info.group"}, mode = SaMode.OR)
public void edit() {
}
/**
* 查询会员列表,沿用会员档案管理的查询条件和权限过滤。
*
* @param pageForm 会员档案查询参数
* @return 分页会员列表
*/
@At
@Ok("json:{dateFormat:'yyyy-MM-dd'}")
@SaCheckPermission(value = {"member.info.maintain", "member.info.group"}, mode = SaMode.OR)
public Result pageData(MemberInfoPageForm pageForm) {
Sql sql = memberInfoService.getPersonalMaintainSql(pageForm);
Pagination<NutMap> pagination = memberInfoService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
}
/**
* 查询个人信息维护详情,返回只读会员信息和可编辑子女信息。
*
* @param userId 用户ID
* @return 会员信息和子女信息
*/
@At
@Ok("json:{dateFormat:'yyyy-MM-dd'}")
@SaCheckPermission(value = {"member.info.maintain", "member.info.group"}, mode = SaMode.OR)
public Result detail(String userId) {
try {
NutMap data = memberInfoService.getPersonalMaintainInfo(userId);
data.put("children", memberChildService.listByUserId(userId));
return Result.success(data);
} catch (RuntimeException e) {
return Result.error(e.getMessage());
}
}
/**
* 保存子女信息,原有会员字段不接收保存,仅覆盖当前会员的子女资料。
*
* @param userId 用户ID
* @param childrenJson 子女信息JSON字符串
* @return 保存结果
*/
@At
@SaCheckPermission(value = {"member.info.maintain", "member.info.group"}, mode = SaMode.OR)
public Result saveChildren(String userId, @Param("children") String childrenJson) {
try {
// 保存前复用详情权限校验,防止绕过列表入口修改无权限会员的子女信息。
memberInfoService.getPersonalMaintainInfo(userId);
List<MemberChild> children = new ArrayList<>();
if (Strings.isNotBlank(childrenJson)) {
children = Json.fromJsonAsList(MemberChild.class, childrenJson);
}
memberChildService.saveUserChildren(userId, children);
return Result.success();
} catch (RuntimeException e) {
return Result.error(e.getMessage());
}
}
}
@@ -2,6 +2,7 @@ package com.budwk.app.zhgh.staffmanage.member.controller.statistics;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.zhgh.staffmanage.member.service.MemberStatisticsService;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.ioc.loader.annotation.Inject;
@@ -56,28 +57,49 @@ public class MemberSummaryController {
public void doExport(@Param("queryId") String queryId, @Param("queryType") String queryType,
@Param("currentYear") Integer currentYear, HttpServletResponse response) {
List<NutMap> list = new ArrayList<>();
if ("union".equals(queryType)) {
boolean unionQuery = isUnionQuery(queryType);
if (unionQuery) {
list = memberStatisticsService.getUnionStatisticsData(queryId, currentYear);
} else {
list = memberStatisticsService.getUnitStatisticsData(queryId, currentYear);
}
// 导出数据与页面查询保持一致,并追加合计行方便 Excel 查看总量。
NutMap map = NutMap.NEW();
map.put("union".equals(queryType) ? "unionName" : "unitName", "合计");
map.put("totalNumber", list.stream().mapToInt(v -> v.getInt("TotalNumber")).sum());
map.put("maleMember", list.stream().mapToInt(v -> v.getInt("maleMember")).sum());
map.put("femaleMember", list.stream().mapToInt(v -> v.getInt("femaleMember")).sum());
map.put("smallForty", list.stream().mapToInt(v -> v.getInt("smallForty")).sum());
map.put("smallFifty", list.stream().mapToInt(v -> v.getInt("smallFifty")).sum());
map.put("smallFiftyFive", list.stream().mapToInt(v -> v.getInt("smallFiftyFive")).sum());
map.put("smallFiftyFive2", list.stream().mapToInt(v -> v.getInt("smallFiftyFive2")).sum());
map.put(unionQuery ? "unionName" : "unitName", "合计");
map.put("totalNumber", sumInt(list, "totalNumber"));
map.put("maleMember", sumInt(list, "maleMember"));
map.put("femaleMember", sumInt(list, "femaleMember"));
map.put("smallForty", sumInt(list, "smallForty"));
map.put("smallFifty", sumInt(list, "smallFifty"));
map.put("smallFiftyFive", sumInt(list, "smallFiftyFive"));
map.put("smallFiftyFive2", sumInt(list, "smallFiftyFive2"));
list.add(map);
try {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + new String(("会员性别统计.xlsx").getBytes("utf-8"), "ISO8859-1"));
Workbook workbook = memberStatisticsService.exportStatisticsExcel(list, queryType);
workbook.write(response.getOutputStream());
workbook.close();
Workbook workbook = memberStatisticsService.exportStatisticsExcel(list, unionQuery ? "union" : "unit");
CommonDownloadUtil.download("会员统计.xlsx", workbook, response);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 判断统计维度,兼容页面传入的 fgh 和服务层导出使用的 union。
*
* @param queryType 统计维度
* @return 是否按分工会统计
*/
private boolean isUnionQuery(String queryType) {
return "fgh".equals(queryType) || "union".equals(queryType);
}
/**
* 汇总 NutMap 数值字段,避免空值导致导出合计失败。
*
* @param list 统计列表
* @param key 汇总字段名
* @return 汇总值
*/
private int sumInt(List<NutMap> list, String key) {
return list.stream().mapToInt(item -> item.getInt(key, 0)).sum();
}
}
@@ -0,0 +1,59 @@
package com.budwk.app.zhgh.staffmanage.member.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.util.Date;
/**
* 会员子女信息表,维护个人信息维护页面可录入的子女资料。
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@Table("member_child")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("会员子女信息")
public class MemberChild extends BaseModel {
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userId;
@Column
@Comment("关系")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String relation;
@Column
@Comment("姓名")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String name;
@Column
@Comment("性别")
@ColDefine(type = ColType.VARCHAR, width = 10)
private String sex;
@Column
@Comment("出生日期")
@ColDefine(type = ColType.DATE)
private Date birthday;
}
@@ -0,0 +1,28 @@
package com.budwk.app.zhgh.staffmanage.member.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.staffmanage.member.models.MemberChild;
import java.util.List;
/**
* 会员子女信息业务服务,集中处理子女信息查询和保存逻辑。
*/
public interface MemberChildService extends BaseService<MemberChild> {
/**
* 根据会员用户ID查询子女信息列表。
*
* @param userId 会员用户ID
* @return 子女信息列表
*/
List<MemberChild> listByUserId(String userId);
/**
* 保存会员子女信息,页面按整表提交,服务层负责覆盖当前会员的子女资料。
*
* @param userId 会员用户ID
* @param children 页面提交的子女信息列表
*/
void saveUserChildren(String userId, List<MemberChild> children);
}
@@ -27,6 +27,22 @@ public interface MemberInfoService extends BaseService<Sys_user> {
*/
Sql getSql(MemberInfoPageForm pageForm);
/**
* 获取个人信息维护列表查询SQL,系统管理员查看全部,其他人员仅查看本人。
*
* @param pageForm 会员档案查询参数
* @return 个人信息维护列表查询SQL
*/
Sql getPersonalMaintainSql(MemberInfoPageForm pageForm);
/**
* 获取个人信息维护详情,复用会员档案查询权限展示会员基础信息。
*
* @param userId 用户ID
* @return 会员基础信息
*/
NutMap getPersonalMaintainInfo(String userId);
/**
* 获取会员历史
* @param pageForm
@@ -0,0 +1,60 @@
package com.budwk.app.zhgh.staffmanage.member.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.staffmanage.member.models.MemberChild;
import com.budwk.app.zhgh.staffmanage.member.service.MemberChildService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import java.util.List;
/**
* 会员子女信息业务实现,负责个人信息维护页面的子女资料持久化。
*/
@IocBean(args = {"refer:dao"})
public class MemberChildServiceImpl extends BaseServiceImpl<MemberChild> implements MemberChildService {
public MemberChildServiceImpl(Dao dao) {
super(dao);
}
/**
* 根据会员用户ID查询子女信息列表,并按创建时间保持页面展示顺序。
*
* @param userId 会员用户ID
* @return 子女信息列表
*/
@Override
public List<MemberChild> listByUserId(String userId) {
return query(Cnd.where("userId", "=", userId).asc("createdAt"));
}
/**
* 覆盖保存指定会员的子女信息,避免前端逐条新增删除导致数据残留。
*
* @param userId 会员用户ID
* @param children 页面提交的子女信息列表
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void saveUserChildren(String userId, List<MemberChild> children) {
clear(Cnd.where("userId", "=", userId));
if (Lang.isEmpty(children)) {
return;
}
children.stream()
.filter(child -> StrUtil.isNotBlank(child.getRelation()) || StrUtil.isNotBlank(child.getName())
|| StrUtil.isNotBlank(child.getSex()) || child.getBirthday() != null)
.forEach(child -> {
// 子女信息归属以后端传入的会员ID为准,防止前端篡改归属用户。
child.setId(null);
child.setUserId(userId);
insert(child);
});
}
}
@@ -15,6 +15,7 @@ import com.budwk.app.base.utils.easyexcel.EasyExcelUtil;
import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_unit;
import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
@@ -113,6 +114,87 @@ public class MemberInfoServiceImpl extends BaseServiceImpl<Sys_user> implements
return sql;
}
/**
* 获取个人信息维护详情,非学校管理员只能查看本分工会或未分配分工会的会员。
*
* @param userId 用户ID
* @return 会员基础信息
*/
/**
* 获取个人信息维护列表SQL,系统管理员按页面条件查询全部,其他人员强制查询本人数据。
*
* @param pageForm 会员档案查询参数
* @return 个人信息维护列表查询SQL
*/
@Override
public Sql getPersonalMaintainSql(MemberInfoPageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
u.id,
u.loginname,
u.username,
u.sex,
DATE_FORMAT(u.birthday, '%Y-%m-%d') AS birthday,
u.political,
DATE_FORMAT(u.joinPartyDate, '%Y-%m-%d') AS joinPartyDate,
u.nation,
u.nativePlace,
u.nationality,
u.idCardType,
u.mobile,
u.idCard,
u.marriage,
u.education,
u.academicDegree,
u.position,
u.jobCategory,
u.professionalTitle,
u.professionalLevel,
u.employeeSource,
u.administrativeLevel,
u.positionSeries,
u.positionLevel,
u.administrativePositionLevel,
u.arrivalAtSchoolDate,
u.personType,
u.preparedBy,
u.identityType,
u.userState,
u.unionName,
u.unitName,
DATE_FORMAT(u.teachingTime, '%Y-%m-%d') AS teachingTime,
DATE_FORMAT(u.expectedLeaveSchoolDate, '%Y-%m-%d') AS expectedLeaveSchoolDate,
u.member,
DATE_FORMAT(u.postDoctoralJoinDate, '%Y-%m-%d') AS postDoctoralJoinDate
FROM
vw_user u
$condition
""");
Cnd cnd = Cnd.NEW();
if (AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
pageForm.buildSearch(cnd, "u.");
} else {
// 个人信息维护只允许非系统管理员查看本人,避免通过查询条件越权查看其他人员。
cnd.and("u.id", "=", SecurityUtil.getUserId());
}
sql.setCondition(cnd);
return sql;
}
@Override
public NutMap getPersonalMaintainInfo(String userId) {
Cnd cnd = Cnd.where("id", "=", userId);
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
// 非系统管理员只能维护本人信息,保存子女信息前也复用此校验。
cnd.and("id", "=", SecurityUtil.getUserId());
}
View_user user = dao().fetch(View_user.class, cnd);
if (user == null) {
throw new RuntimeException("用户信息不存在或无权限查看");
}
return Lang.obj2nutmap(user);
}
@Override
public Sql getMemberHistorySql(MemberInfoPageForm pageForm) {
@@ -53,6 +53,9 @@ layout("/layouts/platform.html"){
<el-button @click="openAdd" size="small" type="primary">
新增资产
</el-button>
<el-button @click="showImportDialog = true" size="small" type="primary">
导入资产
</el-button>
<el-button @click="doExport" size="small" type="primary">
导出资产
</el-button>
@@ -110,6 +113,16 @@ layout("/layouts/platform.html"){
<template #edit>
<asset-form ref="assetForm" @save_success="saveSuccess"></asset-form>
</template>
<excel-import
ref="excelImportRef"
url="/platform/asset/manage/doImport"
template_url="/platform/asset/manage/downloadTemplate"
:visible.sync="showImportDialog"
title="导入资产"
width="700px"
@import-success="doSearch"
:extra_params="{}"
></excel-import>
</guava>
</div>
@@ -132,7 +145,8 @@ layout("/layouts/platform.html"){
{prop: 'assetUsageStateName', label: '使用状况'},
],
assetCategoryOption: [],
unionList: []
unionList: [],
showImportDialog: false
}
},
components: {
@@ -83,9 +83,35 @@
<el-descriptions-item label="岗级">{{ viewData.positionLevel }}</el-descriptions-item>
<el-descriptions-item label="行政岗级">{{ viewData.administrativePositionLevel }}</el-descriptions-item>
</el-descriptions>
<!-- 子女信息由个人信息维护详情接口返回,其他查看入口未返回 children 时不展示该区块 -->
<template v-if="viewData.children">
<div class="process-title">子女信息</div>
<el-table ref="childrenTable" v-if="viewData.children.length" :data="viewData.children" size="mini" border :header-cell-style="{background:'#eff3f6'}" style="width: 100%">
<el-table-column prop="relation" label="关系" align="center" min-width="160"></el-table-column>
<el-table-column prop="name" label="姓名" align="center" min-width="160"></el-table-column>
<el-table-column prop="sex" label="性别" align="center" min-width="120"></el-table-column>
<el-table-column label="出生日期" align="center" min-width="160">
<template slot-scope="scope">
{{ scope.row.birthday ? $moment(scope.row.birthday).format('YYYY-MM-DD') : '' }}
</template>
</el-table-column>
</el-table>
<el-empty v-else :image-size="50" description="暂无子女信息"></el-empty>
</template>
</div>
`,
store,
props: {
detailUrl: {
type: String,
default: "/platform/member/apply/common/findOne"
},
idParam: {
type: String,
default: "id"
}
},
data() {
return {
viewData: {}
@@ -93,9 +119,19 @@
},
methods: {
onOpen(id) {
this.$axios.post("/platform/member/apply/common/findOne", { id }).then((res) => {
// 详情接口和主键参数由调用页面决定,保证公共查看组件可复用不同权限口径的数据源。
const params = {}
params[this.idParam] = id
this.viewData = {}
this.$axios.post(this.detailUrl, params).then((res) => {
if (res.code === 0) {
this.viewData = res.data
this.$nextTick(() => {
// 查看页在隐藏容器中打开时,Element 表格需要重新计算列宽,避免子女信息列头纵向错位。
if (this.$refs.childrenTable) {
this.$refs.childrenTable.doLayout()
}
})
}
})
}
@@ -125,6 +125,7 @@ layout("/layouts/platform.html"){
{ prop: "nation", label: "民族", width: 120, sortable: true},
{ prop: "mobile", label: "联系电话", width: 120 },
{ prop: "political", label: "政治面貌", width: 120, sortable: true},
{ prop: "education", label: "学历", width: 120, sortable: true, checked: 0 },
{ prop: "userState", label: "在职状态", width: 120, sortable: true },
{ prop: "personType", label: "教职工类别", width: 120, sortable: true },
{ prop: "preparedBy", label: "编制类别", width: 120, sortable: true },
@@ -0,0 +1,226 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<div slot="header" class="clearfix">
<span>个人信息维护</span>
</div>
<el-form :model="formData" ref="formRef" label-width="0" label-suffix=":" class="flow-task-form">
<el-descriptions :column="3" border>
<el-descriptions-item label="工号">
<el-input v-model="formData.loginname" readonly size="small"></el-input>
</el-descriptions-item>
<el-descriptions-item label="姓名">
<el-input v-model="formData.username" readonly size="small"></el-input>
</el-descriptions-item>
<el-descriptions-item label="性别">
<el-radio-group disabled v-model="formData.sex" size="small">
<el-radio border label="男">男</el-radio>
<el-radio border label="女">女</el-radio>
</el-radio-group>
</el-descriptions-item>
<el-descriptions-item label="民族">
<dict-select style="width: 100%" v-model="formData.nation" disabled code="USER_NATION"></dict-select>
</el-descriptions-item>
<el-descriptions-item label="出生日期">
<el-date-picker v-model="formData.birthday" type="date" disabled format="yyyy-MM-dd" value-format="yyyy-MM-dd" style="width: 100%"></el-date-picker>
</el-descriptions-item>
<el-descriptions-item label="政治面貌">
<dict-select style="width: 100%" v-model="formData.political" disabled code="USER_POLITICAL"></dict-select>
</el-descriptions-item>
<el-descriptions-item label="学历">
<dict-select style="width: 100%" v-model="formData.education" disabled code="USER_EDUCATION"></dict-select>
</el-descriptions-item>
<el-descriptions-item label="学位">
<dict-select style="width: 100%" v-model="formData.academicDegree" disabled code="USER_ACADEMIC_DEGREE"></dict-select>
</el-descriptions-item>
<el-descriptions-item label="党政职务">
<el-input v-model="formData.position" disabled maxlength="50"></el-input>
</el-descriptions-item>
<el-descriptions-item label="工作单位">
<el-input v-model="formData.unitName" disabled></el-input>
</el-descriptions-item>
<el-descriptions-item label="所属工会">
<el-input v-model="formData.unionName" disabled></el-input>
</el-descriptions-item>
<el-descriptions-item label="所属校区">
<dict-select style="width: 100%" v-model="formData.campus" disabled code="USER_CAMPUS"></dict-select>
</el-descriptions-item>
<el-descriptions-item label="身份证号码">
<el-input v-model="formData.idCard" disabled maxlength="18"></el-input>
</el-descriptions-item>
<el-descriptions-item label="联系电话">
<el-input v-model="formData.mobile" disabled maxlength="15"></el-input>
</el-descriptions-item>
<el-descriptions-item label="电子邮箱">
<el-input v-model="formData.email" disabled maxlength="50"></el-input>
</el-descriptions-item>
<el-descriptions-item label="在职状态">
<dict-select v-model="formData.userState" code="USER_STATE" disabled style="width: 100%"></dict-select>
</el-descriptions-item>
<el-descriptions-item label="教职工类别">
<dict-select v-model="formData.personType" code="USER_PERSON_TYPE" disabled style="width: 100%"></dict-select>
</el-descriptions-item>
<el-descriptions-item label="编制类别">
<dict-select v-model="formData.preparedBy" code="USER_PREPARED_BY_TYPE" disabled style="width: 100%"></dict-select>
</el-descriptions-item>
<el-descriptions-item label="会员状态">
<el-radio-group v-model="formData.member" disabled size="small">
<el-radio border :label="true">会员</el-radio>
<el-radio border :label="false">非会员</el-radio>
</el-radio-group>
</el-descriptions-item>
<el-descriptions-item label="福利会员状态">
<el-radio-group v-model="formData.welfareMember" disabled size="small">
<el-radio border :label="true">福利会员</el-radio>
<el-radio border :label="false">非福利会员</el-radio>
</el-radio-group>
</el-descriptions-item>
<el-descriptions-item label="婚姻状况">
<el-input v-model="formData.marriage" disabled></el-input>
</el-descriptions-item>
<el-descriptions-item label="家庭主要成员" :span="3">
<el-table :data="formData.families" border size="small">
<el-table-column label="关系" prop="relation" align="center"></el-table-column>
<el-table-column label="姓名" prop="name" align="center"></el-table-column>
<el-table-column label="工作单位" prop="unit" align="center"></el-table-column>
<el-table-column label="备注" prop="remark" align="center"></el-table-column>
</el-table>
</el-descriptions-item>
<el-descriptions-item label="子女信息" :span="3">
<el-table :data="children" border size="small">
<el-table-column label="关系" prop="relation">
<template slot-scope="{row}">
<el-select v-model="row.relation" placeholder="请选择关系" style="width: 100%">
<el-option label="父子" value="父子"></el-option>
<el-option label="父女" value="父女"></el-option>
<el-option label="母子" value="母子"></el-option>
<el-option label="母女" value="母女"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="姓名" prop="name">
<template slot-scope="{row}">
<el-input v-model="row.name" maxlength="50" placeholder="请输入姓名"></el-input>
</template>
</el-table-column>
<el-table-column label="性别" prop="sex">
<template slot-scope="{row}">
<el-select v-model="row.sex" placeholder="请选择性别" style="width: 100%">
<el-option label="男" value="男"></el-option>
<el-option label="女" value="女"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="出生日期" prop="birthday">
<template slot-scope="{row}">
<el-date-picker v-model="row.birthday" type="date" format="yyyy-MM-dd" value-format="yyyy-MM-dd" placeholder="请选择出生日期" style="width: 100%"></el-date-picker>
</template>
</el-table-column>
<el-table-column width="100px">
<template slot="header" slot-scope="scope">
<el-button type="primary" size="mini" @click="children.push({})">添加</el-button>
</template>
<template slot-scope="scope">
<el-button type="danger" icon="el-icon-delete" size="mini" @click="children.splice(scope.$index, 1)"></el-button>
</template>
</el-table-column>
</el-table>
</el-descriptions-item>
</el-descriptions>
</el-form>
<el-row type="flex" justify="end" class="mt20">
<el-button type="primary" plain @click="goBack">返回</el-button>
<el-button type="primary" @click="saveChildren">保存</el-button>
</el-row>
</el-card>
</div>
<script>
new Vue({
el: "#app",
store,
data() {
return {
userId: GetQueryString("userId"),
formData: {
families: []
},
children: []
}
},
methods: {
initData() {
this.$axios.post("/platform/member/info/maintain/detail", { userId: this.userId }).then((res) => {
if (res.code === 0) {
this.formData = res.data
this.formData.member = this.formData.member === true || this.formData.member === 1
this.formData.welfareMember = this.formData.welfareMember === true || this.formData.welfareMember === 1
this.formData.families = this.formData.families || []
this.children = res.data.children || []
} else {
this.$message.error(res.msg)
}
})
},
saveChildren() {
const cleanedChildren = this.cleanChildren()
if (!this.validateChildren(cleanedChildren)) {
return
}
this.$confirm("确定保存子女信息吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/platform/member/info/maintain/saveChildren", {
userId: this.userId,
children: JSON.stringify(cleanedChildren)
}).then((res) => {
if (res.code === 0) {
this.$message.success("保存成功")
this.goBack()
} else {
this.$message.error(res.msg)
}
})
})
},
cleanChildren() {
// 保存前过滤完全空白的子女行,避免添加后未填写的空行进入查看页。
return this.children.filter((child) => {
return child.relation || child.name || child.sex || child.birthday
})
},
validateChildren(children) {
// 子女信息按整行保存,只要填写了一项,就要求关系、姓名、性别、出生日期完整。
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (!child.relation || !child.name || !child.sex || !child.birthday) {
this.$message.warning("请完整填写第" + (i + 1) + "行子女信息")
return false
}
}
return true
},
goBack() {
commonUtil.pjaxPush("/platform/member/info/maintain?refresh=" + new Date().getTime())
}
},
created() {
this.initData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,176 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<guava ref="guava">
<template>
<el-card shadow="never">
<common-query ref="commonQueryRef" @search="search"></common-query>
</el-card>
<el-card shadow="never" class="mt10">
<table-tool label="个人信息维护">
<el-popover placement="bottom" trigger="click" width="200">
<div style="padding:4px 0;border-bottom:1px solid #eee;">
<el-button size="mini" @click="checkAll">全选</el-button>
<el-button size="mini" @click="invertCheck">反选</el-button>
</div>
<div style="max-height:40vh;overflow-y:auto;padding:6px 0;">
<el-checkbox-group v-model="checkedFields">
<el-checkbox
v-for="f in tableColumns"
:key="f.prop"
:label="f.prop"
style="display:block;margin:6px 0;">
{{ f.label }}
</el-checkbox>
</el-checkbox-group>
</div>
<el-button
slot="reference"
type="text"
icon="el-icon-s-operation"
size="small"
style="margin-left:12px;">
列设置
</el-button>
</el-popover>
</table-tool>
<el-table :data="tableData" style="width: 100%" row-key="id" @sort-change="pageOrder" :size="tableSize" class="vi-table">
<el-table-column
align="center"
header-align="center"
type="index"
fixed="left"
:index="indexMethod"
label="序号"
width="80px"
></el-table-column>
<el-table-column
show-overflow-tooltip
align="center"
header-align="center"
v-for="column in tableColumns"
v-if="checkedFields.includes(column.prop)"
:label="column.label"
:fixed="column.fixed"
:prop="column.prop"
:sortable="column.sortable"
:width="column.width"
min-width="50"
></el-table-column>
<el-table-column prop="userOnline" align="center" header-align="center" label="操作" fixed="right" width="160px">
<template scope="scope">
<el-button size="mini" type="primary" @click="openView(scope.row.id)">查看</el-button>
<el-button size="mini" type="primary" @click="openEdit(scope.row.id)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</template>
<template #view>
<member-info ref="infoRef" detail-url="/platform/member/info/maintain/detail" id-param="userId"></member-info>
</template>
</guava>
</div>
<script>
<!--#include("../common/commonQuery.js"){}#-->
<!--#include("../../common/info/memberInfo.js"){}#-->
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
pageForm: {},
tableColumns: [
{ prop: "loginname", label: "工号", width: 120, fixed: "left"},
{ prop: "username", label: "姓名", width: 120, fixed: "left" },
{ prop: "sex", label: "性别", width: 120},
{ prop: "birthday", label: "出生年月", width: 120, sortable: true },
{ prop: "nationality", label: "国籍", width: 120, sortable: true, checked: 0 },
{ prop: "nation", label: "民族", width: 120, sortable: true},
{ prop: "mobile", label: "联系电话", width: 120 },
{ prop: "political", label: "政治面貌", width: 120, sortable: true},
{ prop: "education", label: "学历", width: 120, sortable: true, checked: 0 },
{ prop: "userState", label: "在职状态", width: 120, sortable: true },
{ prop: "personType", label: "教职工类别", width: 120, sortable: true },
{ prop: "preparedBy", label: "编制类别", width: 120, sortable: true },
{ prop: "postDoctoralJoinDate", label: "进站时间", sortable: true, checked: 0 },
{ prop: "unionName", label: "所属工会", width: 120, sortable: true },
{ prop: "unitName", label: "所属单位", width: 120, sortable: true },
{ prop: "idCardType", label: "证件类型", width: 120, sortable: true, checked: 0 },
{ prop: "idCard", label: "证件号码", width: 120, sortable: true, checked: 0 },
{ prop: "marriage", label: "婚姻状况", width: 120, sortable: true, checked: 0 },
{ prop: "jobCategory", label: "岗位类别", width: 120, sortable: true, checked: 0 },
{ prop: "professionalTitle", label: "职称", width: 120, sortable: true, checked: 0 },
{ prop: "professionalLevel", label: "职级", width: 120, sortable: true, checked: 0 },
{ prop: "employeeSource", label: "职工来源", width: 120, sortable: true, checked: 0 },
{ prop: "administrativeLevel", label: "行政级别(管理岗位)", width: 120, sortable: true, checked: 0 },
{ prop: "positionSeries", label: "岗位系列", width: 120, sortable: true, checked: 0 },
{ prop: "positionLevel", label: "岗级", width: 120, sortable: true, checked: 0 },
{ prop: "administrativePositionLevel", label: "行政岗级", width: 120, sortable: true, checked: 0 },
{ prop: "arrivalAtSchoolDate", label: "来校时间", width: 120, sortable: true, checked: 0 },
{ prop: "teachingTime", label: "从教年月", width: 120, sortable: true, checked: 0 },
{ prop: "expectedLeaveSchoolDate", label: "预计/最后离校时间", width: 120, sortable: true, checked: 0 }
],
checkedFields: []
}
},
components: {
"member-info": MEMBER_INFO,
"common-query": COMMON_QUERY
},
methods: {
openView(userId) {
this.$refs.guava.view(() => {
this.$refs.infoRef.onOpen(userId)
})
},
openEdit(userId) {
commonUtil.pjaxPush("/platform/member/info/maintain/edit?userId=" + userId)
},
search(pageForm) {
this.pageForm = Object.assign({}, this.pageForm, pageForm)
this.pageData()
},
pageData() {
this.tableLoading = true
const pageForm = clone(this.pageForm)
pageForm.userStates = Array.isArray(this.pageForm.userStates) ? JSON.stringify(this.pageForm.userStates) : this.pageForm.userStates
pageForm.personTypes = Array.isArray(this.pageForm.personTypes) ? JSON.stringify(this.pageForm.personTypes) : this.pageForm.personTypes
pageForm.preparedBys = Array.isArray(this.pageForm.preparedBys) ? JSON.stringify(this.pageForm.preparedBys) : this.pageForm.preparedBys
this.$axios.post(loc() + "/pageData", pageForm)
.then((data) => {
if (data.code === 0) {
this.tableData = data.data.list
this.pageForm.totalCount = data.data.totalCount
} else {
this.$message.error(data.msg)
}
})
.finally(() => {
this.tableLoading = false
})
},
checkAll() {
this.checkedFields = this.tableColumns.map(c => c.prop)
},
invertCheck() {
const all = this.tableColumns.map(c => c.prop)
this.checkedFields = all.filter(p => !this.checkedFields.includes(p))
}
},
created() {
this.checkedFields = this.tableColumns.filter(c => c.checked !== 0).map(c => c.prop)
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -42,7 +42,9 @@ layout("/layouts/platform.html"){
</el-card>
<el-card shadow="never" class="mt10">
<table-tool label="会员统计"></table-tool>
<table-tool label="会员统计">
<el-button type="primary" size="small" icon="el-icon-download" @click="doExport">导出</el-button>
</table-tool>
<el-table :data="tableData" show-summary>
<el-table-column
align="center"
@@ -90,7 +92,9 @@ layout("/layouts/platform.html"){
}
},
methods: {
doExport() {},
doExport() {
this.$downLoad(loc() + "/doExport", this.pageForm)
},
initData() {
if (this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])) {
this.$businessTool.listUnion().then((data) => {