commit
This commit is contained in:
@@ -25,6 +25,12 @@ public interface SysFileService extends BaseService<Sys_file> {
|
||||
*/
|
||||
String uploadReturnUrl(String engine, TempFile file);
|
||||
|
||||
/**
|
||||
* 文件字节上传,返回文件Url。
|
||||
* 参数 engine 为存储引擎,fileName 为带后缀的文件名,bytes 为文件内容;返回值为前端可直接预览的下载地址。
|
||||
*/
|
||||
String uploadBytesReturnUrl(String engine, String fileName, byte[] bytes);
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
|
||||
@@ -65,6 +65,11 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
||||
return this.storageFile(engine, file, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadBytesReturnUrl(String engine, String fileName, byte[] bytes) {
|
||||
return this.storageFile(engine, fileName, bytes, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination page(Sys_file file) {
|
||||
return null;
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package com.budwk.app.zhgh.activity.friendship.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.budwk.app.base.annotation.SLog;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.zhgh.activity.friendship.models.ActivityFriendshipSignup;
|
||||
import com.budwk.app.zhgh.activity.friendship.service.ActivityFriendshipSignupService;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
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 javax.validation.Valid;
|
||||
|
||||
@IocBean
|
||||
@Ok("json:full")
|
||||
@At("/platform/activity/friendship/signup")
|
||||
public class ActivityFriendshipSignupController {
|
||||
|
||||
@Inject
|
||||
private ActivityFriendshipSignupService activityFriendshipSignupService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/activity/friendship/index.html")
|
||||
@SaCheckPermission("activity.friendship.manage")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.friendship.manage")
|
||||
public Result pageData(PageForm pageForm, String sex, String maritalStatus, String education, String post, String personnelType, String unionId, String unitId) {
|
||||
return Result.success(activityFriendshipSignupService.pageData(pageForm, sex, maritalStatus, education, post, personnelType, unionId, unitId));
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.friendship.manage")
|
||||
public Result findOne(@Valid String id) {
|
||||
return Result.success(activityFriendshipSignupService.fetch(id));
|
||||
}
|
||||
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("activity.friendship.manage")
|
||||
@SLog(tag = "单身联谊", msg = "新增报名台账")
|
||||
public Result insert(@Param("data") @Valid ActivityFriendshipSignup signup) {
|
||||
activityFriendshipSignupService.insertSignup(signup);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("activity.friendship.manage")
|
||||
@SLog(tag = "单身联谊", msg = "修改报名台账")
|
||||
public Result update(@Param("data") @Valid ActivityFriendshipSignup signup) {
|
||||
activityFriendshipSignupService.updateSignup(signup);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("activity.friendship.manage")
|
||||
@SLog(tag = "单身联谊", msg = "删除报名台账")
|
||||
public Result delete(@Valid String id) {
|
||||
activityFriendshipSignupService.delete(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("activity.friendship.manage")
|
||||
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
|
||||
public Result doImport(@Param("file") TempFile file, @Param("isFlag") Boolean isFlag) {
|
||||
return activityFriendshipSignupService.doImport(file, isFlag);
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@SaCheckPermission("activity.friendship.manage")
|
||||
public void exportExcel(HttpServletResponse response) {
|
||||
activityFriendshipSignupService.exportExcel(response);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.budwk.app.zhgh.activity.friendship.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.zhgh.activity.friendship.service.ActivityFriendshipSignupService;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
@IocBean
|
||||
@Ok("json:full")
|
||||
@At("/platform/h5/activity/friendship")
|
||||
public class H5ActivityFriendshipController {
|
||||
|
||||
@Inject
|
||||
private ActivityFriendshipSignupService activityFriendshipSignupService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhghh5/activity/friendship/index.html")
|
||||
@SaCheckPermission("h5.activity.friendship.view")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("h5.activity.friendship.view")
|
||||
public Result listCards() {
|
||||
return Result.success(activityFriendshipSignupService.listMobileCards());
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package com.budwk.app.zhgh.activity.friendship.mode;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 单身联谊 Excel 导出结构。
|
||||
*/
|
||||
@Data
|
||||
public class ActivityFriendshipExcelMode {
|
||||
|
||||
@Excel(name = "编号", width = 15)
|
||||
private String signupNo;
|
||||
|
||||
@Excel(name = "工号", width = 20)
|
||||
private String loginName;
|
||||
|
||||
@Excel(name = "姓名", width = 20)
|
||||
private String username;
|
||||
|
||||
@Excel(name = "所属工会", width = 25)
|
||||
private String unionName;
|
||||
|
||||
@Excel(name = "所属单位", width = 30)
|
||||
private String unitName;
|
||||
|
||||
@Excel(name = "报名时间", width = 25)
|
||||
private String signupTime;
|
||||
|
||||
@Excel(name = "性别", width = 10)
|
||||
private String sex;
|
||||
|
||||
@Excel(name = "婚况", width = 15)
|
||||
private String maritalStatus;
|
||||
|
||||
@Excel(name = "学历", width = 15)
|
||||
private String education;
|
||||
|
||||
@Excel(name = "岗位", width = 20)
|
||||
private String post;
|
||||
|
||||
@Excel(name = "人事编制", width = 20)
|
||||
private String personnelType;
|
||||
|
||||
@Excel(name = "出生年份", width = 15)
|
||||
private String birthYear;
|
||||
|
||||
@Excel(name = "户籍(市)", width = 20)
|
||||
private String householdCity;
|
||||
|
||||
@Excel(name = "家乡(市)", width = 20)
|
||||
private String hometownCity;
|
||||
|
||||
@Excel(name = "身高(厘米)", width = 15)
|
||||
private String heightCm;
|
||||
|
||||
@Excel(name = "体重(千克)", width = 15)
|
||||
private String weightKg;
|
||||
|
||||
@Excel(name = "年收入(万)", width = 15)
|
||||
private String annualIncome;
|
||||
|
||||
@Excel(name = "车房", width = 25)
|
||||
private String carHouse;
|
||||
|
||||
@Excel(name = "自我描述", width = 40)
|
||||
private String selfDescription;
|
||||
|
||||
@Excel(name = "家庭情况", width = 40)
|
||||
private String familyInfo;
|
||||
|
||||
@Excel(name = "兴趣爱好", width = 40)
|
||||
private String hobbies;
|
||||
|
||||
@Excel(name = "择偶意向", width = 40)
|
||||
private String matePreference;
|
||||
|
||||
@Excel(name = "生活照(不展示)", width = 40)
|
||||
private String photoUrl;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.budwk.app.zhgh.activity.friendship.mode;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 单身联谊 Excel 导入错误行。
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ActivityFriendshipImportError {
|
||||
|
||||
private int rowNo;
|
||||
private String loginName;
|
||||
private String errorInfo;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.budwk.app.zhgh.activity.friendship.mode;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单身联谊 Excel 导入结果。
|
||||
*/
|
||||
@Data
|
||||
public class ActivityFriendshipImportResult {
|
||||
|
||||
private int totalCount;
|
||||
private int successCount;
|
||||
private int errorCount;
|
||||
private List<ActivityFriendshipImportError> errorList = new ArrayList<>();
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package com.budwk.app.zhgh.activity.friendship.models;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 单身联谊报名台账。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table("activity_friendship_signup")
|
||||
@TableMeta("{'mysql-charset':'utf8mb4'}")
|
||||
@Comment("单身联谊报名台账")
|
||||
public class ActivityFriendshipSignup extends BaseModel {
|
||||
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@PrevInsert(uu32 = true)
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@Comment("报名编号")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String signupNo;
|
||||
|
||||
@Column
|
||||
@Comment("工号")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String loginName;
|
||||
|
||||
@Column
|
||||
@Comment("报名时间")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 30)
|
||||
private String signupTime;
|
||||
|
||||
@Column
|
||||
@Comment("性别")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String sex;
|
||||
|
||||
@Column
|
||||
@Comment("婚况")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String maritalStatus;
|
||||
|
||||
@Column
|
||||
@Comment("学历")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String education;
|
||||
|
||||
@Column
|
||||
@Comment("岗位")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String post;
|
||||
|
||||
@Column
|
||||
@Comment("人事编制")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String personnelType;
|
||||
|
||||
@Column
|
||||
@Comment("出生年份")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String birthYear;
|
||||
|
||||
@Column
|
||||
@Comment("户籍")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String householdCity;
|
||||
|
||||
@Column
|
||||
@Comment("家乡")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String hometownCity;
|
||||
|
||||
@Column
|
||||
@Comment("身高厘米")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String heightCm;
|
||||
|
||||
@Column
|
||||
@Comment("体重千克")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String weightKg;
|
||||
|
||||
@Column
|
||||
@Comment("年收入万")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String annualIncome;
|
||||
|
||||
@Column
|
||||
@Comment("车房")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String carHouse;
|
||||
|
||||
@Column
|
||||
@Comment("自我描述")
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String selfDescription;
|
||||
|
||||
@Column
|
||||
@Comment("家庭情况")
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String familyInfo;
|
||||
|
||||
@Column
|
||||
@Comment("兴趣爱好")
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String hobbies;
|
||||
|
||||
@Column
|
||||
@Comment("择偶意向")
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String matePreference;
|
||||
|
||||
@Column
|
||||
@Comment("生活照URL")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 500)
|
||||
private String photoUrl;
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.budwk.app.zhgh.activity.friendship.service;
|
||||
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.activity.friendship.models.ActivityFriendshipSignup;
|
||||
import com.budwk.app.zhgh.activity.friendship.vo.ActivityFriendshipSignupVO;
|
||||
import org.nutz.mvc.upload.TempFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
public interface ActivityFriendshipSignupService extends BaseService<ActivityFriendshipSignup> {
|
||||
|
||||
/**
|
||||
* 查询报名台账分页。
|
||||
* 参数 pageForm 为分页和人员信息关键字参数,仅支持工号、姓名模糊查询;
|
||||
* sex、maritalStatus、education、post、personnelType、unionId、unitId 为页面下拉筛选值,分别对应性别、婚况、学历、岗位、人事编制、所属工会、所属单位;
|
||||
* 返回值为 ActivityFriendshipSignupVO 分页数据,包含台账字段和关联用户的姓名、工会、单位信息。
|
||||
*/
|
||||
Pagination<ActivityFriendshipSignupVO> pageData(PageForm pageForm, String sex, String maritalStatus, String education, String post, String personnelType, String unionId, String unitId);
|
||||
|
||||
/**
|
||||
* 查询移动端可查看的异性资料卡。
|
||||
* 根据当前登录用户性别自动筛选异性报名信息;返回值为资料卡展示 VO 列表。
|
||||
*/
|
||||
List<ActivityFriendshipSignupVO> listMobileCards();
|
||||
|
||||
/**
|
||||
* 新增报名记录。
|
||||
* 参数 signup 为前端表单数据;当编号为空时根据报名时间年份生成年份加三位流水号。
|
||||
*/
|
||||
void insertSignup(ActivityFriendshipSignup signup);
|
||||
|
||||
/**
|
||||
* 修改报名记录。
|
||||
* 参数 signup 为前端表单数据;已有编号不变,编号为空时按报名时间补生成。
|
||||
*/
|
||||
void updateSignup(ActivityFriendshipSignup signup);
|
||||
|
||||
/**
|
||||
* 导入报名 Excel。
|
||||
* 参数 file 为 xls/xlsx 文件,isFlag 为 true 时先清空台账再导入;返回导入统计和错误行。
|
||||
*/
|
||||
Result doImport(TempFile file, Boolean isFlag);
|
||||
|
||||
/**
|
||||
* 导出报名台账。
|
||||
* 参数 response 为下载响应,导出字段与报名模板保持一致。
|
||||
*/
|
||||
void exportExcel(HttpServletResponse response);
|
||||
}
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
package com.budwk.app.zhgh.activity.friendship.service.impl;
|
||||
|
||||
import cn.afterturn.easypoi.excel.ExcelExportUtil;
|
||||
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||
import cn.afterturn.easypoi.excel.entity.ExportParams;
|
||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.base.utils.CommonDownloadUtil;
|
||||
import com.budwk.app.sys.enums.SysFileEngineTypeEnum;
|
||||
import com.budwk.app.sys.views.View_user;
|
||||
import com.budwk.app.sys.services.SysFileService;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.activity.friendship.mode.ActivityFriendshipExcelMode;
|
||||
import com.budwk.app.zhgh.activity.friendship.mode.ActivityFriendshipImportError;
|
||||
import com.budwk.app.zhgh.activity.friendship.mode.ActivityFriendshipImportResult;
|
||||
import com.budwk.app.zhgh.activity.friendship.models.ActivityFriendshipSignup;
|
||||
import com.budwk.app.zhgh.activity.friendship.service.ActivityFriendshipSignupService;
|
||||
import com.budwk.app.zhgh.activity.friendship.vo.ActivityFriendshipSignupVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ooxml.POIXMLDocumentPart;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
|
||||
import org.apache.poi.xssf.usermodel.XSSFDrawing;
|
||||
import org.apache.poi.xssf.usermodel.XSSFPicture;
|
||||
import org.apache.poi.xssf.usermodel.XSSFShape;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.mvc.upload.TempFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.FileInputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Comparator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class ActivityFriendshipSignupServiceImpl extends BaseServiceImpl<ActivityFriendshipSignup> implements ActivityFriendshipSignupService {
|
||||
|
||||
private static final int PHOTO_COLUMN_INDEX = 18;
|
||||
|
||||
@Inject
|
||||
private SysFileService sysFileService;
|
||||
|
||||
public ActivityFriendshipSignupServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination<ActivityFriendshipSignupVO> pageData(PageForm pageForm, String sex, String maritalStatus, String education, String post, String personnelType, String unionId, String unitId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
afs.id,
|
||||
afs.signupNo,
|
||||
afs.loginName,
|
||||
vu.username,
|
||||
vu.unionId,
|
||||
vu.unionName,
|
||||
vu.unitId,
|
||||
vu.unitName,
|
||||
afs.signupTime,
|
||||
afs.sex,
|
||||
afs.maritalStatus,
|
||||
afs.education,
|
||||
afs.post,
|
||||
afs.personnelType,
|
||||
afs.birthYear,
|
||||
afs.householdCity,
|
||||
afs.hometownCity,
|
||||
afs.heightCm,
|
||||
afs.weightKg,
|
||||
afs.annualIncome,
|
||||
afs.carHouse,
|
||||
afs.selfDescription,
|
||||
afs.familyInfo,
|
||||
afs.hobbies,
|
||||
afs.matePreference,
|
||||
afs.photoUrl,
|
||||
afs.createdAt
|
||||
FROM
|
||||
activity_friendship_signup afs
|
||||
LEFT JOIN vw_user vu ON vu.loginname = afs.loginName
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("afs.delFlag", "=", false);
|
||||
cnd.andEX("afs.sex", "=", sex);
|
||||
cnd.andEX("afs.maritalStatus", "=", maritalStatus);
|
||||
cnd.andEX("afs.education", "=", education);
|
||||
cnd.andEX("afs.post", "=", post);
|
||||
cnd.andEX("afs.personnelType", "=", personnelType);
|
||||
cnd.andEX("vu.unionId", "=", unionId);
|
||||
cnd.andEX("vu.unitId", "=", unitId);
|
||||
if (StrUtil.isNotBlank(pageForm.getSearchKeyword()) && StrUtil.isNotBlank(pageForm.getSearchName())) {
|
||||
cnd.and(Cnd.likeEX(getSearchColumn(pageForm.getSearchName()), pageForm.getSearchKeyword()));
|
||||
}
|
||||
cnd.desc("afs.signupNo");
|
||||
cnd.desc("afs.createdAt");
|
||||
sql.setCondition(cnd);
|
||||
return listPageVO(pageForm, sql, ActivityFriendshipSignupVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertSignup(ActivityFriendshipSignup signup) {
|
||||
fillSignupNoIfBlank(signup);
|
||||
insert(signup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSignup(ActivityFriendshipSignup signup) {
|
||||
fillSignupNoIfBlank(signup);
|
||||
updateIgnoreNull(signup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ActivityFriendshipSignupVO> listMobileCards() {
|
||||
View_user currentUser = dao().fetch(View_user.class, Cnd.where("id", "=", SecurityUtil.getUserId()));
|
||||
if (currentUser == null || !List.of("男", "女").contains(currentUser.getSex())) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
String targetSex = "男".equals(currentUser.getSex()) ? "女" : "男";
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
afs.id,
|
||||
afs.signupNo,
|
||||
afs.loginName,
|
||||
vu.username,
|
||||
vu.unionId,
|
||||
vu.unionName,
|
||||
vu.unitId,
|
||||
vu.unitName,
|
||||
afs.signupTime,
|
||||
afs.sex,
|
||||
afs.maritalStatus,
|
||||
afs.education,
|
||||
afs.post,
|
||||
afs.personnelType,
|
||||
afs.birthYear,
|
||||
afs.householdCity,
|
||||
afs.hometownCity,
|
||||
afs.heightCm,
|
||||
afs.weightKg,
|
||||
afs.annualIncome,
|
||||
afs.carHouse,
|
||||
afs.selfDescription,
|
||||
afs.familyInfo,
|
||||
afs.hobbies,
|
||||
afs.matePreference,
|
||||
afs.createdAt
|
||||
FROM
|
||||
activity_friendship_signup afs
|
||||
LEFT JOIN vw_user vu ON vu.loginname = afs.loginName
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("afs.delFlag", "=", false);
|
||||
// cnd.and("afs.sex", "=", targetSex);
|
||||
cnd.desc("afs.signupNo");
|
||||
cnd.desc("afs.createdAt");
|
||||
sql.setCondition(cnd);
|
||||
return listVO(sql, ActivityFriendshipSignupVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result doImport(TempFile file, Boolean isFlag) {
|
||||
if (Lang.isEmpty(file)) {
|
||||
return Result.error("上传的文件不能为空");
|
||||
}
|
||||
String extName = FileUtil.extName(file.getFile());
|
||||
if (!List.of("xls", "xlsx").contains(extName.toLowerCase())) {
|
||||
return Result.error("请上传xls,xlsx文件");
|
||||
}
|
||||
|
||||
ActivityFriendshipImportResult result = new ActivityFriendshipImportResult();
|
||||
List<ActivityFriendshipSignup> signups = new ArrayList<>();
|
||||
try (FileInputStream inputStream = new FileInputStream(file.getFile());
|
||||
Workbook workbook = WorkbookFactory.create(inputStream)) {
|
||||
org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
|
||||
Map<Integer, String> rowPhotoUrlMap = buildRowPhotoUrlMap(sheet);
|
||||
|
||||
// 导入参数说明:Excel 第 1 行为字段表头,返回值为按 @Excel 表头映射好的报名导入实体列表。
|
||||
ImportParams importParams = new ImportParams();
|
||||
importParams.setTitleRows(0);
|
||||
importParams.setHeadRows(1);
|
||||
List<ActivityFriendshipExcelMode> excelList = ExcelImportUtil.importExcel(file.getFile(), ActivityFriendshipExcelMode.class, importParams);
|
||||
for (int dataIndex = 0; dataIndex < excelList.size(); dataIndex++) {
|
||||
ActivityFriendshipSignup signup = BeanUtil.copyProperties(excelList.get(dataIndex), ActivityFriendshipSignup.class);
|
||||
if (isBlankSignup(signup)) {
|
||||
continue;
|
||||
}
|
||||
result.setTotalCount(result.getTotalCount() + 1);
|
||||
signup.setPhotoUrl(rowPhotoUrlMap.get(dataIndex + importParams.getHeadRows() + importParams.getTitleRows()));
|
||||
if (StrUtil.isBlank(signup.getLoginName())) {
|
||||
result.getErrorList().add(new ActivityFriendshipImportError(dataIndex + importParams.getHeadRows() + importParams.getTitleRows() + 1, "", "工号不能为空"));
|
||||
continue;
|
||||
}
|
||||
signups.add(signup);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("单身联谊报名导入失败", e);
|
||||
return Result.error("读取不到数据,请检查excel文件格式");
|
||||
}
|
||||
|
||||
if (Boolean.TRUE.equals(isFlag)) {
|
||||
clear(Cnd.where("delFlag", "=", false));
|
||||
}
|
||||
fillSignupNo(signups, Boolean.TRUE.equals(isFlag));
|
||||
if (Lang.isNotEmpty(signups)) {
|
||||
insert(signups);
|
||||
}
|
||||
|
||||
result.setErrorCount(result.getErrorList().size());
|
||||
result.setSuccessCount(Math.max(result.getTotalCount() - result.getErrorCount(), 0));
|
||||
if (result.getErrorCount() > 0) {
|
||||
return Result.success(result);
|
||||
}
|
||||
return Result.success("导入成功");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportExcel(HttpServletResponse response) {
|
||||
try {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
afs.signupNo,
|
||||
afs.loginName,
|
||||
vu.username,
|
||||
vu.unionName,
|
||||
vu.unitName,
|
||||
afs.signupTime,
|
||||
afs.sex,
|
||||
afs.maritalStatus,
|
||||
afs.education,
|
||||
afs.post,
|
||||
afs.personnelType,
|
||||
afs.birthYear,
|
||||
afs.householdCity,
|
||||
afs.hometownCity,
|
||||
afs.heightCm,
|
||||
afs.weightKg,
|
||||
afs.annualIncome,
|
||||
afs.carHouse,
|
||||
afs.selfDescription,
|
||||
afs.familyInfo,
|
||||
afs.hobbies,
|
||||
afs.matePreference,
|
||||
afs.photoUrl
|
||||
FROM
|
||||
activity_friendship_signup afs
|
||||
LEFT JOIN vw_user vu ON vu.loginname = afs.loginName
|
||||
WHERE
|
||||
afs.delFlag = 0
|
||||
ORDER BY
|
||||
afs.signupNo DESC,
|
||||
afs.createdAt DESC
|
||||
""");
|
||||
List<ActivityFriendshipSignupVO> list = listVO(sql, ActivityFriendshipSignupVO.class);
|
||||
List<ActivityFriendshipExcelMode> exportList = list.stream().map(item -> BeanUtil.copyProperties(item, ActivityFriendshipExcelMode.class)).collect(Collectors.toList());
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
exportParams.setTitle("单身联谊报名人员名单");
|
||||
exportParams.setSheetName("报名人员名单");
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, ActivityFriendshipExcelMode.class, exportList);
|
||||
CommonDownloadUtil.download("单身联谊报名人员名单.xlsx", workbook, response);
|
||||
} catch (Exception e) {
|
||||
log.error("单身联谊报名导出失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将前端搜索字段转换为连表查询字段。
|
||||
* 参数 searchName 为前端选择的搜索项;返回值为带表别名的数据库字段,避免连表后字段歧义。
|
||||
*/
|
||||
private String getSearchColumn(String searchName) {
|
||||
return switch (searchName) {
|
||||
case "loginName" -> "afs.loginName";
|
||||
case "username" -> "vu.username";
|
||||
default -> "afs.loginName";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Excel 内嵌生活照。
|
||||
* 参数 sheet 为报名工作表;返回值 key 为 POI 行号,value 为系统文件下载 URL。
|
||||
*/
|
||||
private Map<Integer, String> buildRowPhotoUrlMap(org.apache.poi.ss.usermodel.Sheet sheet) {
|
||||
Map<Integer, String> rowPhotoUrlMap = new HashMap<>();
|
||||
if (!(sheet instanceof XSSFSheet xssfSheet)) {
|
||||
return rowPhotoUrlMap;
|
||||
}
|
||||
for (POIXMLDocumentPart relation : xssfSheet.getRelations()) {
|
||||
if (!(relation instanceof XSSFDrawing drawing)) {
|
||||
continue;
|
||||
}
|
||||
for (XSSFShape shape : drawing.getShapes()) {
|
||||
if (!(shape instanceof XSSFPicture picture)) {
|
||||
continue;
|
||||
}
|
||||
XSSFClientAnchor anchor = picture.getClientAnchor();
|
||||
if (anchor == null || anchor.getCol1() != PHOTO_COLUMN_INDEX) {
|
||||
continue;
|
||||
}
|
||||
byte[] imageBytes = picture.getPictureData().getData();
|
||||
String extension = picture.getPictureData().suggestFileExtension();
|
||||
String fileName = "friendship-photo-" + (anchor.getRow1() + 1) + "." + (StrUtil.isBlank(extension) ? "png" : extension);
|
||||
String photoUrl = sysFileService.uploadBytesReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), fileName, imageBytes);
|
||||
rowPhotoUrlMap.put(anchor.getRow1(), photoUrl);
|
||||
}
|
||||
}
|
||||
return rowPhotoUrlMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断导入实体是否为空行。
|
||||
* 参数 signup 为 EasyPOI 根据表头映射出的导入对象;返回 true 表示所有业务文本字段均为空,不参与统计和入库。
|
||||
*/
|
||||
private boolean isBlankSignup(ActivityFriendshipSignup signup) {
|
||||
return signup == null
|
||||
|| StrUtil.isAllBlank(signup.getLoginName(), signup.getSignupTime(), signup.getSex(), signup.getMaritalStatus(),
|
||||
signup.getEducation(), signup.getPost(), signup.getPersonnelType(), signup.getBirthYear(), signup.getHouseholdCity(),
|
||||
signup.getHometownCity(), signup.getHeightCm(), signup.getWeightKg(), signup.getAnnualIncome(), signup.getCarHouse(),
|
||||
signup.getSelfDescription(), signup.getFamilyInfo(), signup.getHobbies(), signup.getMatePreference(), signup.getPhotoUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成报名编号。
|
||||
* 参数 signups 为待入库报名记录,resetFromOne 表示是否忽略库中已有编号;编号格式为报名年份加三位流水号。
|
||||
*/
|
||||
private void fillSignupNo(List<ActivityFriendshipSignup> signups, boolean resetFromOne) {
|
||||
Map<String, Integer> yearMaxNoMap = new HashMap<>();
|
||||
signups.sort(Comparator.comparing(ActivityFriendshipSignup::getSignupTime, Comparator.nullsLast(String::compareTo)));
|
||||
for (ActivityFriendshipSignup signup : signups) {
|
||||
String year = parseSignupYear(signup.getSignupTime());
|
||||
int currentMax = yearMaxNoMap.computeIfAbsent(year, key -> resetFromOne ? 0 : queryMaxSignupNo(key));
|
||||
int nextNo = currentMax + 1;
|
||||
signup.setSignupNo(year + String.format("%03d", nextNo));
|
||||
yearMaxNoMap.put(year, nextNo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单条补充报名编号。
|
||||
* 参数 signup 为表单报名记录;当编号为空时按报名时间年份和数据库最大流水号生成下一号。
|
||||
*/
|
||||
private void fillSignupNoIfBlank(ActivityFriendshipSignup signup) {
|
||||
if (signup == null || StrUtil.isNotBlank(signup.getSignupNo())) {
|
||||
return;
|
||||
}
|
||||
String year = parseSignupYear(signup.getSignupTime());
|
||||
signup.setSignupNo(year + String.format("%03d", queryMaxSignupNo(year) + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定年份现有最大流水号。
|
||||
* 参数 year 为四位年份;返回值为该年份后三位流水号最大值,没有记录时返回 0。
|
||||
*/
|
||||
private int queryMaxSignupNo(String year) {
|
||||
List<ActivityFriendshipSignup> list = query(Cnd.where("delFlag", "=", false)
|
||||
.and("signupNo", "like", year + "%")
|
||||
.desc("signupNo"));
|
||||
if (Lang.isEmpty(list) || StrUtil.isBlank(list.get(0).getSignupNo())) {
|
||||
return 0;
|
||||
}
|
||||
String signupNo = list.get(0).getSignupNo();
|
||||
if (signupNo.length() <= 4) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(signupNo.substring(4));
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析报名时间年份。
|
||||
* 参数 signupTime 支持完整日期时间或直接填写年份;无法解析时使用当前年份,避免导入中断。
|
||||
*/
|
||||
private String parseSignupYear(String signupTime) {
|
||||
if (StrUtil.isBlank(signupTime)) {
|
||||
return String.valueOf(LocalDate.now().getYear());
|
||||
}
|
||||
String value = signupTime.trim();
|
||||
if (value.length() >= 4 && value.substring(0, 4).matches("\\d{4}")) {
|
||||
return value.substring(0, 4);
|
||||
}
|
||||
try {
|
||||
return String.valueOf(LocalDate.parse(value, DateTimeFormatter.ISO_LOCAL_DATE).getYear());
|
||||
} catch (Exception e) {
|
||||
return String.valueOf(LocalDate.now().getYear());
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.budwk.app.zhgh.activity.friendship.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 单身联谊报名台账前端展示数据。
|
||||
*/
|
||||
@Data
|
||||
public class ActivityFriendshipSignupVO {
|
||||
|
||||
private String id;
|
||||
private String signupNo;
|
||||
private String loginName;
|
||||
private String username;
|
||||
private String unionId;
|
||||
private String unionName;
|
||||
private String unitId;
|
||||
private String unitName;
|
||||
private String signupTime;
|
||||
private String sex;
|
||||
private String maritalStatus;
|
||||
private String education;
|
||||
private String post;
|
||||
private String personnelType;
|
||||
private String birthYear;
|
||||
private String householdCity;
|
||||
private String hometownCity;
|
||||
private String heightCm;
|
||||
private String weightKg;
|
||||
private String annualIncome;
|
||||
private String carHouse;
|
||||
private String selfDescription;
|
||||
private String familyInfo;
|
||||
private String hobbies;
|
||||
private String matePreference;
|
||||
private String photoUrl;
|
||||
private Long createdAt;
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="人员信息">
|
||||
<el-input @keyup.enter.native="doSearch" clearable placeholder="请输入内容" style="width: 100%" v-model="pageForm.searchKeyword">
|
||||
<el-select slot="prepend" style="width: 90px" v-model="pageForm.searchName">
|
||||
<el-option label="工号" value="loginName"></el-option>
|
||||
<el-option label="姓名" value="username"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="性别">
|
||||
<el-select clearable placeholder="请选择性别" style="width: 100%" v-model="pageForm.sex">
|
||||
<el-option label="男" value="男"></el-option>
|
||||
<el-option label="女" value="女"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="婚况">
|
||||
<el-select clearable placeholder="请选择婚况" style="width: 100%" v-model="pageForm.maritalStatus">
|
||||
<el-option label="未婚" value="未婚"></el-option>
|
||||
<el-option label="离异" value="离异"></el-option>
|
||||
<el-option label="其他" value="其他"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="学历">
|
||||
<el-select clearable placeholder="请选择学历" style="width: 100%" v-model="pageForm.education">
|
||||
<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>
|
||||
</search-item>
|
||||
<search-item label="岗位">
|
||||
<el-select clearable placeholder="请选择岗位" style="width: 100%" v-model="pageForm.post">
|
||||
<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-option label="其他" value="其他"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="人事编制">
|
||||
<el-select clearable placeholder="请选择人事编制" style="width: 100%" v-model="pageForm.personnelType">
|
||||
<el-option label="在编" value="在编"></el-option>
|
||||
<el-option label="人事代理" value="人事代理"></el-option>
|
||||
<el-option label="人才派遣" value="人才派遣"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="所属工会">
|
||||
<el-select clearable filterable placeholder="请选择工会" style="width: 100%" v-model="pageForm.unionId">
|
||||
<el-option :key="item.id" :label="item.name" :value="item.id" v-for="item in unionList"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="所属单位">
|
||||
<el-select clearable filterable placeholder="请选择单位" style="width: 100%" v-model="pageForm.unitId">
|
||||
<el-option :key="item.id" :label="item.name" :value="item.id" v-for="item in unitList"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="邮缘会台账">
|
||||
<el-button @click="openForm()" icon="el-icon-plus" size="small" type="primary">新增</el-button>
|
||||
<el-button @click="openImport" icon="el-icon-upload2" size="small" type="primary">导入</el-button>
|
||||
<el-button @click="doExport" icon="el-icon-download" size="small" type="primary">导出</el-button>
|
||||
</table-tool>
|
||||
|
||||
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder" class="vi-table" ref="table" row-key="id" style="width: 100%" v-loading="tableLoading">
|
||||
<el-table-column :index="indexMethod" align="center" header-align="center" label="序号" type="index" width="70"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="编号" prop="signupNo" show-overflow-tooltip width="110"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="生活照" width="100">
|
||||
<template scope="{row}">
|
||||
<el-image :preview-src-list="[row.photoUrl]" :src="row.photoUrl" fit="cover" style="width: 60px; height: 60px" v-if="row.photoUrl"></el-image>
|
||||
<el-tag type="warning" v-else>暂无</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="工号" prop="loginName" show-overflow-tooltip width="120"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="姓名" prop="username" show-overflow-tooltip width="120"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="所属工会" prop="unionName" show-overflow-tooltip width="150"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="所属单位" prop="unitName" show-overflow-tooltip width="180"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="报名时间" prop="signupTime" show-overflow-tooltip width="170"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="性别" prop="sex" width="80"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="婚况" prop="maritalStatus" width="90"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="学历" prop="education" show-overflow-tooltip width="100"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="岗位" prop="post" show-overflow-tooltip width="120"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="人事编制" prop="personnelType" show-overflow-tooltip width="120"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="出生年份" prop="birthYear" width="100"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="户籍" prop="householdCity" show-overflow-tooltip width="130"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="家乡" prop="hometownCity" show-overflow-tooltip width="130"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="身高" prop="heightCm" width="80"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="体重" prop="weightKg" width="80"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="年收入" prop="annualIncome" width="90"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="车房" prop="carHouse" show-overflow-tooltip width="140"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="自我描述" prop="selfDescription" show-overflow-tooltip width="180"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="家庭情况" prop="familyInfo" show-overflow-tooltip width="180"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="兴趣爱好" prop="hobbies" show-overflow-tooltip width="180"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="择偶意向" prop="matePreference" show-overflow-tooltip width="180"></el-table-column>
|
||||
<el-table-column align="center" fixed="right" header-align="center" label="操作" width="150">
|
||||
<template scope="{row}">
|
||||
<el-button @click="openForm(row)" size="mini" type="primary">编辑</el-button>
|
||||
<el-button @click="doDelete(row)" size="mini" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
</guava>
|
||||
|
||||
<el-dialog :close-on-click-modal="false" :title="formData.id ? '编辑报名信息' : '新增报名信息'" :visible.sync="formDialog" top="3vh" width="72%">
|
||||
<el-form :model="formData" :rules="formRules" label-width="120px" ref="form">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="编号" prop="signupNo">
|
||||
<el-input disabled placeholder="保存后自动生成" v-model="formData.signupNo"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="工号" prop="loginName">
|
||||
<el-input maxlength="100" placeholder="请输入工号" v-model="formData.loginName"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="报名时间" prop="signupTime">
|
||||
<el-date-picker placeholder="请选择报名时间" style="width: 100%" type="datetime" v-model="formData.signupTime" value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select clearable placeholder="请选择性别" style="width: 100%" v-model="formData.sex">
|
||||
<el-option label="男" value="男"></el-option>
|
||||
<el-option label="女" value="女"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="婚况" prop="maritalStatus">
|
||||
<el-select clearable placeholder="请选择婚况" style="width: 100%" v-model="formData.maritalStatus">
|
||||
<el-option label="未婚" value="未婚"></el-option>
|
||||
<el-option label="离异" value="离异"></el-option>
|
||||
<el-option label="丧偶" value="丧偶"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="学历" prop="education">
|
||||
<el-input maxlength="50" placeholder="请输入学历" v-model="formData.education"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="岗位" prop="post">
|
||||
<el-input maxlength="100" placeholder="请输入岗位" v-model="formData.post"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="人事编制" prop="personnelType">
|
||||
<el-input maxlength="100" placeholder="请输入人事编制" v-model="formData.personnelType"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出生年份" prop="birthYear">
|
||||
<el-input maxlength="20" placeholder="请输入出生年份" v-model="formData.birthYear"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="户籍(市)" prop="householdCity">
|
||||
<el-input maxlength="100" placeholder="请输入户籍" v-model="formData.householdCity"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="家乡(市)" prop="hometownCity">
|
||||
<el-input maxlength="100" placeholder="请输入家乡" v-model="formData.hometownCity"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="身高(厘米)" prop="heightCm">
|
||||
<el-input maxlength="20" placeholder="请输入身高" v-model="formData.heightCm"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="体重(千克)" prop="weightKg">
|
||||
<el-input maxlength="20" placeholder="请输入体重" v-model="formData.weightKg"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="年收入(万)" prop="annualIncome">
|
||||
<el-input maxlength="20" placeholder="请输入年收入" v-model="formData.annualIncome"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-form-item label="车房" prop="carHouse">
|
||||
<el-input maxlength="100" placeholder="请输入车房情况" v-model="formData.carHouse"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="生活照" prop="photoUrl">
|
||||
<file-upload :upload_number="1" :value.sync="formData.photoUrl" upload_mode="image" upload_result_category="interval" upload_result_type="url"></file-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自我描述" prop="selfDescription">
|
||||
<el-input :rows="4" maxlength="1000" placeholder="请输入自我描述" type="textarea" v-model="formData.selfDescription"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="家庭情况" prop="familyInfo">
|
||||
<el-input :rows="4" maxlength="1000" placeholder="请输入家庭情况" type="textarea" v-model="formData.familyInfo"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="兴趣爱好" prop="hobbies">
|
||||
<el-input :rows="4" maxlength="1000" placeholder="请输入兴趣爱好" type="textarea" v-model="formData.hobbies"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="择偶意向" prop="matePreference">
|
||||
<el-input :rows="4" maxlength="1000" placeholder="请输入择偶意向" type="textarea" v-model="formData.matePreference"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="formDialog = false" :disabled="formLoading">取消</el-button>
|
||||
<el-button @click="save" type="primary" v-loading="formLoading">提交</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :close-on-click-modal="false" :visible.sync="importDialog" title="导入报名人员名单" width="50%">
|
||||
<div style="padding: 20px 50px">
|
||||
<el-timeline>
|
||||
<el-timeline-item placement="top" timestamp="选择文件">
|
||||
<el-card>
|
||||
<el-form>
|
||||
<el-form-item label="导入模式">
|
||||
<el-radio-group v-model="importData.isFlag">
|
||||
<el-radio-button label="true">清空更新</el-radio-button>
|
||||
<el-radio-button label="false">追加</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-upload :auto-upload="false" :file-list="importData.fileList" :limit="1" :on-change="fileChange" :on-remove="fileRemove" ref="upload">
|
||||
<el-button icon="el-icon-upload" size="medium" style="width: 200px">选择文件</el-button>
|
||||
<div class="el-upload__tip" slot="tip" style="color: #f56c6c">只能上传 xls/xlsx 文件,生活照需为 Excel 内嵌图片</div>
|
||||
</el-upload>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item placement="top" timestamp="导入结果">
|
||||
<el-card shadow="never">
|
||||
<p>总记录数:{{ importResult.totalCount }}</p>
|
||||
<p>成功数:<span class="text-success">{{ importResult.successCount }}</span></p>
|
||||
<p>错误数:<span class="text-danger">{{ importResult.errorCount }}</span></p>
|
||||
<el-table :data="importResult.errorList" size="mini" v-if="importResult.errorCount > 0">
|
||||
<el-table-column label="行号" prop="rowNo" width="80"></el-table-column>
|
||||
<el-table-column label="工号" prop="loginName"></el-table-column>
|
||||
<el-table-column label="错误原因" prop="errorInfo"></el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div style="text-align: right">
|
||||
<el-button @click="importDialog = false" :disabled="importLoading">取消</el-button>
|
||||
<el-button @click="doImport" type="primary" v-loading="importLoading">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
searchName: "loginName"
|
||||
},
|
||||
formDialog: false,
|
||||
formLoading: false,
|
||||
importDialog: false,
|
||||
importLoading: false,
|
||||
formData: {},
|
||||
unionList: [],
|
||||
unitList: [],
|
||||
importData: {
|
||||
fileList: [],
|
||||
isFlag: "false"
|
||||
},
|
||||
importResult: {
|
||||
totalCount: 0,
|
||||
successCount: 0,
|
||||
errorCount: 0,
|
||||
errorList: []
|
||||
},
|
||||
formRules: {
|
||||
loginName: [{ required: true, message: "请输入工号", trigger: "blur" }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageData() {
|
||||
this.tableLoading = true
|
||||
this.$axios.post("/platform/activity/friendship/signup/pageData", this.pageForm)
|
||||
.then((res) => {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
})
|
||||
.finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
openForm(row) {
|
||||
if (row) {
|
||||
this.$set(this, "formData", clone(row))
|
||||
} else {
|
||||
this.$set(this, "formData", {
|
||||
signupNo: "",
|
||||
loginName: "",
|
||||
signupTime: moment().format("YYYY-MM-DD HH:mm:ss"),
|
||||
sex: "",
|
||||
maritalStatus: "",
|
||||
education: "",
|
||||
post: "",
|
||||
personnelType: "",
|
||||
birthYear: "",
|
||||
householdCity: "",
|
||||
hometownCity: "",
|
||||
heightCm: "",
|
||||
weightKg: "",
|
||||
annualIncome: "",
|
||||
carHouse: "",
|
||||
selfDescription: "",
|
||||
familyInfo: "",
|
||||
hobbies: "",
|
||||
matePreference: "",
|
||||
photoUrl: ""
|
||||
})
|
||||
}
|
||||
this.formDialog = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
save() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
this.formLoading = true
|
||||
const url = this.formData.id ? "/platform/activity/friendship/signup/update" : "/platform/activity/friendship/signup/insert"
|
||||
this.$axios.post(url, { data: JSON.stringify(this.formData) })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("保存成功")
|
||||
this.formDialog = false
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.warning(res.msg)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.formLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
doDelete(row) {
|
||||
this.$confirm("确定要删除该报名记录吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/activity/friendship/signup/delete", { id: row.id })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("删除成功")
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.warning(res.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
openImport() {
|
||||
this.$set(this, "importData", {
|
||||
fileList: [],
|
||||
isFlag: "false"
|
||||
})
|
||||
this.$set(this, "importResult", {
|
||||
totalCount: 0,
|
||||
successCount: 0,
|
||||
errorCount: 0,
|
||||
errorList: []
|
||||
})
|
||||
this.importDialog = true
|
||||
},
|
||||
fileRemove(file, fileList) {
|
||||
this.$set(this.importData, "fileList", fileList)
|
||||
},
|
||||
fileChange(file, fileList) {
|
||||
const fileType = file.name.split(".").pop().toLowerCase()
|
||||
if (!file.size) {
|
||||
this.$message.warning("您选择的是空文件")
|
||||
fileList.splice(fileList.findIndex((item) => item === file), 1)
|
||||
}
|
||||
if (!["xls", "xlsx"].includes(fileType)) {
|
||||
this.$message.warning("文件只能是 XLS/XLSX 格式")
|
||||
fileList.splice(fileList.findIndex((item) => item === file), 1)
|
||||
}
|
||||
this.$set(this.importData, "fileList", fileList)
|
||||
},
|
||||
doImport() {
|
||||
if (this.importData.fileList.length === 0) {
|
||||
this.$message.warning("请选择文件")
|
||||
return
|
||||
}
|
||||
const data = new FormData()
|
||||
data.append("isFlag", this.importData.isFlag)
|
||||
this.importData.fileList.forEach((val) => {
|
||||
data.append("file", val.raw, val.raw.name)
|
||||
})
|
||||
this.importLoading = true
|
||||
this.$axios.post("/platform/activity/friendship/signup/doImport", data)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
if (res.data) {
|
||||
this.$set(this, "importResult", res.data)
|
||||
if (res.data.errorCount > 0) {
|
||||
this.$message.warning("导入完成,存在错误记录")
|
||||
} else {
|
||||
this.$message.success("导入成功")
|
||||
}
|
||||
} else {
|
||||
this.$message.success("导入成功")
|
||||
}
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.warning(res.msg)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.importLoading = false
|
||||
})
|
||||
},
|
||||
doExport() {
|
||||
this.$downLoad("/platform/activity/friendship/signup/exportExcel")
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$businessTool.listUnion().then((res) => {
|
||||
this.unionList = res
|
||||
})
|
||||
this.$businessTool.listUnit().then((res) => {
|
||||
this.unitList = res
|
||||
})
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,803 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<div class="friendship-page">
|
||||
<header class="friendship-header">
|
||||
<van-nav-bar left-arrow left-text="返回" title="邮缘会" @click-left="historyBack"></van-nav-bar>
|
||||
|
||||
|
||||
<section class="friendship-filter-shell">
|
||||
<div class="filter-pill-row">
|
||||
<button :class="['filter-pill', filterForm.sex ? 'active' : '']" @click="openFilterPicker('sex')" type="button">
|
||||
<span>性别:{{ getFilterText('sex') }}</span>
|
||||
<i>▾</i>
|
||||
</button>
|
||||
<button :class="['filter-pill', filterForm.education ? 'active' : '']" @click="openFilterPicker('education')" type="button">
|
||||
<span>学历:{{ getFilterText('education') }}</span>
|
||||
<i>▾</i>
|
||||
</button>
|
||||
<button :class="['filter-pill', filterForm.post ? 'active' : '']" @click="openFilterPicker('post')" type="button">
|
||||
<span>岗位:{{ getFilterText('post') }}</span>
|
||||
<i>▾</i>
|
||||
</button>
|
||||
<button :class="['filter-pill', filterForm.personnelType ? 'active' : '']" @click="openFilterPicker('personnelType')" type="button">
|
||||
<span>编制:{{ getFilterText('personnelType') }}</span>
|
||||
<i>▾</i>
|
||||
</button>
|
||||
<button @click="clearFilters" class="filter-clear" type="button" v-if="hasFilter">清空</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="card-counter" v-if="cardList.length > 0">{{ currentIndex + 1 }}/{{ cardList.length }}</div>
|
||||
</header>
|
||||
|
||||
<main class="card-container">
|
||||
<van-empty description="暂无可查看的资料" v-if="!tableLoading && cardList.length === 0"></van-empty>
|
||||
|
||||
<van-swipe :duration="420" :loop="cardList.length > 1" :show-indicators="false" @change="handleSwipeChange" class="friendship-swipe" indicator-color="#333" v-else>
|
||||
<van-swipe-item :key="item.id" v-for="item in cardList">
|
||||
<article :class="['profile-card', 'responsive-text', item.sex === '男' ? 'male-card' : 'female-card']">
|
||||
<header class="card-header">
|
||||
<div class="signup-no">{{ item.signupNo || '暂无编号' }}</div>
|
||||
<div class="gender-name">
|
||||
<span class="gender-icon">{{ item.sex === '男' ? '👨' : '👩' }}</span>
|
||||
<span>{{ item.sex === '男' ? '男士' : '女士' }}</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="header-pointer"></div>
|
||||
|
||||
<section class="profile-main">
|
||||
<h1>{{ item.birthYear || '未知年份' }}年出生</h1>
|
||||
<p>{{ buildSubtitle(item) }}</p>
|
||||
</section>
|
||||
|
||||
<section class="info-grid">
|
||||
<div class="info-cell">
|
||||
<span class="cell-icon">↕</span>
|
||||
<span>身高</span>
|
||||
<strong>{{ item.heightCm || '未知' }}cm</strong>
|
||||
</div>
|
||||
<div class="info-cell">
|
||||
<span class="cell-icon">⚒</span>
|
||||
<span>体重</span>
|
||||
<strong>{{ item.weightKg || '未知' }}kg</strong>
|
||||
</div>
|
||||
<div class="info-cell">
|
||||
<span class="cell-icon">$</span>
|
||||
<span>年收入</span>
|
||||
<strong>{{ item.annualIncome || '未知' }}万</strong>
|
||||
</div>
|
||||
<div class="info-cell">
|
||||
<span class="cell-icon">⌂</span>
|
||||
<span>房车</span>
|
||||
<strong>{{ item.carHouse || '未知' }}</strong>
|
||||
</div>
|
||||
<div class="info-cell wide">
|
||||
<span class="cell-icon">●</span>
|
||||
<span>户籍</span>
|
||||
<strong>{{ item.householdCity || '未知' }}</strong>
|
||||
</div>
|
||||
<div class="info-cell wide">
|
||||
<span class="cell-icon">▣</span>
|
||||
<span>家乡</span>
|
||||
<strong>{{ item.hometownCity || '未知' }}</strong>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="text-section">
|
||||
<div class="text-block">
|
||||
<h2>✍ 自我描述</h2>
|
||||
<p>{{ item.selfDescription || '暂无' }}</p>
|
||||
</div>
|
||||
<div class="text-block">
|
||||
<h2>👨👩👧👦 家庭情况</h2>
|
||||
<p>{{ item.familyInfo || '暂无' }}</p>
|
||||
</div>
|
||||
<div class="text-block">
|
||||
<h2>🎯 兴趣爱好</h2>
|
||||
<p>{{ item.hobbies || '暂无' }}</p>
|
||||
</div>
|
||||
<div class="text-block intention">
|
||||
<h2>💬 择偶意向</h2>
|
||||
<p>{{ item.matePreference || '暂无' }}</p>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
</main>
|
||||
|
||||
<footer class="privacy-footer">
|
||||
<div>🛡 隐私保护:信息已脱敏处理</div>
|
||||
<p>若有意向,请联系校工会</p>
|
||||
</footer>
|
||||
|
||||
<van-popup position="bottom" safe-area-inset-bottom v-model="pickerVisible">
|
||||
<van-picker :columns="currentPickerOptions" :title="currentFilterTitle" show-toolbar value-key="label" @cancel="pickerVisible = false" @confirm="confirmFilter"></van-picker>
|
||||
</van-popup>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
tableLoading: false,
|
||||
sourceCardList: [],
|
||||
cardList: [],
|
||||
currentIndex: 0,
|
||||
pickerVisible: false,
|
||||
currentFilterType: "",
|
||||
currentFilterTitle: "",
|
||||
currentPickerOptions: [],
|
||||
filterForm: {
|
||||
sex: "",
|
||||
education: "",
|
||||
post: "",
|
||||
personnelType: ""
|
||||
},
|
||||
filterOptions: {
|
||||
sex: [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "男", value: "男" },
|
||||
{ label: "女", value: "女" }
|
||||
],
|
||||
education: [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "本科", value: "本科" },
|
||||
{ label: "硕士", value: "硕士" },
|
||||
{ label: "博士", value: "博士" },
|
||||
{ label: "其他", value: "其他" }
|
||||
],
|
||||
post: [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "教师", value: "教师" },
|
||||
{ label: "实验", value: "实验" },
|
||||
{ label: "辅导员", value: "辅导员" },
|
||||
{ label: "行政", value: "行政" },
|
||||
{ label: "其他", value: "其他" }
|
||||
],
|
||||
personnelType: [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "在编", value: "在编" },
|
||||
{ label: "人事代理", value: "人事代理" },
|
||||
{ label: "人才派遣", value: "人才派遣" }
|
||||
]
|
||||
},
|
||||
filterTitles: {
|
||||
sex: "选择性别",
|
||||
education: "选择学历",
|
||||
post: "选择岗位",
|
||||
personnelType: "选择人事编制"
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasFilter() {
|
||||
return this.filterForm.sex || this.filterForm.education || this.filterForm.post || this.filterForm.personnelType
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageData() {
|
||||
this.tableLoading = true
|
||||
this.$axios.post("/platform/h5/activity/friendship/listCards", {})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
const list = res.data || []
|
||||
this.$set(this, "sourceCardList", list)
|
||||
this.applyFilters()
|
||||
} else {
|
||||
this.$toast(res.msg)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
openFilterPicker(type) {
|
||||
this.currentFilterType = type
|
||||
this.currentFilterTitle = this.filterTitles[type] || "选择筛选条件"
|
||||
this.currentPickerOptions = this.filterOptions[type] || []
|
||||
this.pickerVisible = true
|
||||
},
|
||||
confirmFilter(option) {
|
||||
if (!this.currentFilterType) {
|
||||
this.pickerVisible = false
|
||||
return
|
||||
}
|
||||
this.$set(this.filterForm, this.currentFilterType, option.value)
|
||||
this.pickerVisible = false
|
||||
this.applyFilters()
|
||||
},
|
||||
getFilterText(type) {
|
||||
const options = this.filterOptions[type] || []
|
||||
const current = options.find((item) => item.value === this.filterForm[type])
|
||||
return current && current.value ? current.label : "全部"
|
||||
},
|
||||
clearFilters() {
|
||||
this.$set(this, "filterForm", {
|
||||
sex: "",
|
||||
education: "",
|
||||
post: "",
|
||||
personnelType: ""
|
||||
})
|
||||
this.applyFilters()
|
||||
},
|
||||
applyFilters() {
|
||||
const list = this.sourceCardList.filter((item) => {
|
||||
return this.matchFilter(item, "sex") && this.matchFilter(item, "education") && this.matchFilter(item, "post") && this.matchFilter(item, "personnelType")
|
||||
})
|
||||
this.$set(this, "cardList", list)
|
||||
// 筛选条件变化后回到第一张,避免轮播下标沿用旧列表位置。
|
||||
this.$set(this, "currentIndex", 0)
|
||||
},
|
||||
matchFilter(item, key) {
|
||||
const value = this.filterForm[key]
|
||||
if (!value) {
|
||||
return true
|
||||
}
|
||||
return item[key] === value
|
||||
},
|
||||
buildSubtitle(item) {
|
||||
const values = [item.maritalStatus, item.education, item.post, item.personnelType].filter((value) => value)
|
||||
return values.length > 0 ? values.join(" · ") : "暂无基础信息"
|
||||
},
|
||||
// 接收 Vant Swipe 返回的当前下标,用于同步右上角“当前张数/总张数”。
|
||||
handleSwipeChange(index) {
|
||||
this.$set(this, "currentIndex", index)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--friendship-page-bg: #f8f9fb;
|
||||
--friendship-surface: #ffffff;
|
||||
--friendship-surface-warm: #fffdf9;
|
||||
--friendship-border: #eceff3;
|
||||
--friendship-text-main: #15171a;
|
||||
--friendship-text-muted: #5f6673;
|
||||
--friendship-text-soft: #8a93a3;
|
||||
--friendship-shadow-soft: 0 10px 26px rgba(31, 41, 55, 0.08);
|
||||
--friendship-shadow-pill: 0 4px 14px rgba(31, 41, 55, 0.08);
|
||||
--friendship-pink: #f76f91;
|
||||
--friendship-pink-deep: #f45d82;
|
||||
--friendship-pink-soft: rgba(247, 111, 145, 0.08);
|
||||
--friendship-pink-line: rgba(247, 111, 145, 0.42);
|
||||
--friendship-blue: #4a90e2;
|
||||
--friendship-blue-deep: #2f7dd6;
|
||||
--friendship-blue-soft: rgba(74, 144, 226, 0.10);
|
||||
--friendship-blue-line: rgba(74, 144, 226, 0.38);
|
||||
--friendship-danger: #e53935;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--friendship-page-bg);
|
||||
}
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
font-size: clamp(13px, 3.7vw, 16px);
|
||||
}
|
||||
|
||||
.responsive-text {
|
||||
font-size: clamp(13px, 3.7vw, 16px);
|
||||
}
|
||||
|
||||
.friendship-page {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background: var(--friendship-page-bg);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.friendship-header {
|
||||
flex: 0 0 auto;
|
||||
z-index: 8;
|
||||
background: rgba(248, 249, 251, 0.96);
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 0.86);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.friendship-header .van-nav-bar {
|
||||
background: rgba(248, 249, 251, 0.96);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.friendship-header .van-nav-bar::after {
|
||||
border-color: var(--friendship-border);
|
||||
}
|
||||
|
||||
.friendship-header .van-nav-bar__title {
|
||||
color: #2f343b;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.friendship-header .van-nav-bar__left,
|
||||
.friendship-header .van-nav-bar__text,
|
||||
.friendship-header .van-nav-bar .van-icon {
|
||||
color: #2878d4;
|
||||
}
|
||||
|
||||
.friendship-filter-shell {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 430px;
|
||||
padding: clamp(0.625rem, 2.8vw, 0.8125rem) clamp(0.875rem, 4vw, 1rem) clamp(0.375rem, 1.8vw, 0.5rem);
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.card-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding: clamp(0.75rem, 3.6vw, 1rem) 0 clamp(0.75rem, 3.5vw, 1rem);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.filter-pill-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: clamp(0.5rem, 2.5vw, 0.75rem);
|
||||
overflow-x: auto;
|
||||
padding: 0.125rem 0 0.375rem;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.filter-pill-row::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.filter-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: clamp(0.125rem, 0.8vw, 0.1875rem);
|
||||
flex: 0 0 auto;
|
||||
height: clamp(2rem, 8.4vw, 2.375rem);
|
||||
max-width: clamp(7rem, 34vw, 8.75rem);
|
||||
padding: 0 clamp(0.75rem, 3.4vw, 0.9375rem);
|
||||
border: 1px solid #e4e7ec;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
color: #3f4650;
|
||||
text-align: center;
|
||||
box-shadow: var(--friendship-shadow-pill);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.filter-pill.active {
|
||||
border-color: rgba(247, 111, 145, 0.38);
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
color: var(--friendship-pink-deep);
|
||||
box-shadow: 0 5px 16px rgba(247, 111, 145, 0.13);
|
||||
}
|
||||
|
||||
.filter-pill span,
|
||||
.filter-pill i {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.filter-pill span {
|
||||
font-size: clamp(11px, 3.2vw, 13px);
|
||||
line-height: 1;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.filter-pill i {
|
||||
flex: 0 0 auto;
|
||||
margin-top: -0.0625rem;
|
||||
font-size: clamp(9px, 2.7vw, 11px);
|
||||
line-height: 1;
|
||||
font-style: normal;
|
||||
opacity: 0.78;
|
||||
}
|
||||
|
||||
.filter-clear {
|
||||
flex: 0 0 auto;
|
||||
height: clamp(2rem, 8.4vw, 2.375rem);
|
||||
padding: 0 clamp(0.75rem, 3.2vw, 0.9375rem);
|
||||
border: 1px solid rgba(247, 111, 145, 0.28);
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
color: var(--friendship-pink-deep);
|
||||
font-size: clamp(11px, 3.2vw, 13px);
|
||||
line-height: 1;
|
||||
box-shadow: var(--friendship-shadow-pill);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.card-counter {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
margin: 0 max(1rem, calc((100vw - 430px) / 2 + 1rem)) clamp(0.375rem, 2vw, 0.5rem) auto;
|
||||
min-width: clamp(3rem, 15vw, 3.375rem);
|
||||
width: fit-content;
|
||||
padding: clamp(0.3125rem, 1.6vw, 0.4375rem) clamp(0.75rem, 3.4vw, 0.9375rem);
|
||||
border: 1px solid rgba(236, 239, 243, 0.95);
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
color: #3d424b;
|
||||
font-size: clamp(12px, 3.3vw, 14px);
|
||||
line-height: 1.4;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
box-shadow: var(--friendship-shadow-pill);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.friendship-swipe {
|
||||
min-height: 100%;
|
||||
overflow: hidden;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.friendship-swipe .van-swipe__track {
|
||||
height: 100%;
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.friendship-swipe .van-swipe-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
min-height: 100%;
|
||||
overflow: visible;
|
||||
padding: 0 clamp(1rem, 4vw, 1.125rem) clamp(0.75rem, 3.5vw, 1.125rem);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 430px;
|
||||
overflow: hidden;
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
border: 1px solid rgba(231, 236, 242, 0.9);
|
||||
border-radius: clamp(1.25rem, 5vw, 1.75rem);
|
||||
background: var(--friendship-surface);
|
||||
box-shadow: var(--friendship-shadow-soft);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: clamp(3.625rem, 15vw, 4.75rem);
|
||||
padding: 0 clamp(1.375rem, 6vw, 1.75rem);
|
||||
color: #ffffff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.female-card .card-header {
|
||||
background: linear-gradient(135deg, var(--friendship-pink), var(--friendship-pink-deep));
|
||||
}
|
||||
|
||||
.male-card .card-header {
|
||||
background: linear-gradient(135deg, var(--friendship-blue), var(--friendship-blue-deep));
|
||||
}
|
||||
|
||||
.signup-no {
|
||||
min-width: clamp(6.25rem, 31vw, 7.5rem);
|
||||
padding: clamp(0.5rem, 2.2vw, 0.625rem) clamp(0.875rem, 4vw, 1.125rem);
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
font-size: clamp(15px, 4.6vw, 18px);
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
box-shadow: 0 5px 14px rgba(31, 41, 55, 0.08);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.female-card .signup-no {
|
||||
color: var(--friendship-pink-deep);
|
||||
}
|
||||
|
||||
.male-card .signup-no {
|
||||
color: var(--friendship-blue-deep);
|
||||
}
|
||||
|
||||
.gender-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: clamp(0.1875rem, 1.4vw, 0.3125rem);
|
||||
font-size: clamp(18px, 5.2vw, 22px);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.gender-icon {
|
||||
font-size: clamp(16px, 4.8vw, 20px);
|
||||
}
|
||||
|
||||
.header-pointer {
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: -0.125rem auto 0;
|
||||
border-left: clamp(0.875rem, 5vw, 1.25rem) solid transparent;
|
||||
border-right: clamp(0.875rem, 5vw, 1.25rem) solid transparent;
|
||||
}
|
||||
|
||||
.female-card .header-pointer {
|
||||
border-top: clamp(1rem, 5.5vw, 1.375rem) solid var(--friendship-pink-deep);
|
||||
}
|
||||
|
||||
.male-card .header-pointer {
|
||||
border-top: clamp(1rem, 5.5vw, 1.375rem) solid var(--friendship-blue-deep);
|
||||
}
|
||||
|
||||
.profile-main {
|
||||
padding: clamp(1.25rem, 5.6vw, 1.75rem) clamp(1rem, 5vw, 1.375rem) clamp(0.625rem, 2.8vw, 0.875rem);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.profile-main h1 {
|
||||
margin: 0;
|
||||
color: #050505;
|
||||
font-size: clamp(26px, 7.4vw, 34px);
|
||||
line-height: 1.25;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.profile-main p {
|
||||
margin: clamp(0.375rem, 1.8vw, 0.5rem) 0 0;
|
||||
color: #9a684d;
|
||||
font-size: clamp(13px, 4vw, 16px);
|
||||
line-height: 1.35;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: clamp(0.625rem, 3.2vw, 0.875rem);
|
||||
padding: 0 clamp(1.25rem, 5.5vw, 1.5rem) clamp(0.875rem, 4vw, 1.125rem);
|
||||
}
|
||||
|
||||
.info-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: clamp(0.5rem, 2.4vw, 0.6875rem);
|
||||
min-height: clamp(2.625rem, 11vw, 3.125rem);
|
||||
padding: clamp(0.625rem, 2.8vw, 0.75rem) clamp(0.75rem, 3.6vw, 0.9375rem);
|
||||
border-radius: 0.625rem;
|
||||
font-size: clamp(13px, 3.8vw, 15px);
|
||||
line-height: 1.35;
|
||||
box-sizing: border-box;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.62);
|
||||
}
|
||||
|
||||
.female-card .info-cell,
|
||||
.female-card .text-block {
|
||||
background: var(--friendship-pink-soft);
|
||||
}
|
||||
|
||||
.male-card .info-cell,
|
||||
.male-card .text-block {
|
||||
background: var(--friendship-blue-soft);
|
||||
}
|
||||
|
||||
.info-cell.wide {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.info-cell strong {
|
||||
min-width: 0;
|
||||
color: var(--friendship-text-main);
|
||||
font-weight: 600;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.cell-icon {
|
||||
flex: 0 0 auto;
|
||||
width: clamp(1rem, 4.4vw, 1.25rem);
|
||||
font-size: clamp(14px, 4vw, 16px);
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.female-card .cell-icon {
|
||||
color: var(--friendship-pink-deep);
|
||||
}
|
||||
|
||||
.male-card .cell-icon {
|
||||
color: var(--friendship-blue-deep);
|
||||
}
|
||||
|
||||
.text-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: clamp(0.875rem, 4vw, 1rem);
|
||||
padding: 0 clamp(1.25rem, 5.5vw, 1.5rem) clamp(1.125rem, 5vw, 1.5rem);
|
||||
}
|
||||
|
||||
.text-block {
|
||||
padding: clamp(0.875rem, 4vw, 1.125rem) clamp(0.9375rem, 4.5vw, 1.25rem);
|
||||
border-left: 4px solid;
|
||||
border-radius: 0.625rem;
|
||||
box-sizing: border-box;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.female-card .text-block {
|
||||
border-left-color: var(--friendship-pink);
|
||||
}
|
||||
|
||||
.male-card .text-block {
|
||||
border-left-color: var(--friendship-blue);
|
||||
}
|
||||
|
||||
.text-block h2 {
|
||||
margin: 0 0 clamp(0.5rem, 2.4vw, 0.625rem);
|
||||
color: var(--friendship-text-main);
|
||||
font-size: clamp(15px, 4.2vw, 17px);
|
||||
line-height: 1.3;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.text-block p {
|
||||
margin: 0;
|
||||
color: #30343a;
|
||||
font-size: clamp(13px, 3.7vw, 15px);
|
||||
line-height: 1.8;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.female-card .intention {
|
||||
background: var(--friendship-pink-soft);
|
||||
border-left-color: var(--friendship-pink);
|
||||
}
|
||||
|
||||
.male-card .intention {
|
||||
background: var(--friendship-blue-soft);
|
||||
border-left-color: var(--friendship-blue);
|
||||
}
|
||||
|
||||
.privacy-footer {
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
padding: clamp(0.5rem, 2.8vw, 0.625rem) clamp(0.875rem, 5vw, 1.25rem) clamp(0.5rem, 3.3vw, 0.75rem);
|
||||
border-top: 1px dashed #d7dce3;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
color: var(--friendship-text-muted);
|
||||
text-align: center;
|
||||
font-size: clamp(11px, 3.3vw, 13px);
|
||||
line-height: 1.7;
|
||||
box-shadow: 0 -6px 18px rgba(31, 41, 55, 0.04);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.privacy-footer div {
|
||||
color: var(--friendship-text-muted);
|
||||
}
|
||||
|
||||
.privacy-footer p {
|
||||
margin: 0.125rem 0 0;
|
||||
color: var(--friendship-danger);
|
||||
}
|
||||
|
||||
@media (max-height: 700px) {
|
||||
.card-container {
|
||||
padding-top: clamp(0.25rem, 1.6vh, 0.375rem);
|
||||
padding-bottom: clamp(0.375rem, 2vh, 0.5rem);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
min-height: clamp(2.875rem, 12vh, 3.5rem);
|
||||
}
|
||||
|
||||
.profile-main {
|
||||
padding-top: clamp(0.875rem, 3.2vh, 1.125rem);
|
||||
padding-bottom: clamp(0.125rem, 1vh, 0.25rem);
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
gap: clamp(0.25rem, 1.6vh, 0.5rem);
|
||||
padding-bottom: clamp(0.375rem, 2vh, 0.625rem);
|
||||
}
|
||||
|
||||
.text-section {
|
||||
gap: clamp(0.375rem, 2vh, 0.625rem);
|
||||
padding-bottom: clamp(0.375rem, 2vh, 0.75rem);
|
||||
}
|
||||
|
||||
.text-block {
|
||||
padding-top: clamp(0.375rem, 2vh, 0.625rem);
|
||||
padding-bottom: clamp(0.375rem, 2vh, 0.625rem);
|
||||
}
|
||||
|
||||
.text-block p {
|
||||
max-height: clamp(3.75rem, 14vh, 5.625rem);
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.privacy-footer {
|
||||
padding-top: clamp(0.375rem, 1.8vh, 0.5rem);
|
||||
padding-bottom: clamp(0.375rem, 1.8vh, 0.5rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.card-container {
|
||||
padding-top: clamp(0.5rem, 2.8vw, 0.75rem);
|
||||
padding-bottom: clamp(0.5rem, 2.8vw, 0.75rem);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 0 clamp(0.75rem, 4vw, 1.125rem);
|
||||
}
|
||||
|
||||
.signup-no {
|
||||
min-width: clamp(4.75rem, 27vw, 6.125rem);
|
||||
font-size: clamp(13px, 4vw, 15px);
|
||||
}
|
||||
|
||||
.profile-main h1 {
|
||||
font-size: clamp(20px, 6.4vw, 23px);
|
||||
}
|
||||
|
||||
.info-grid,
|
||||
.text-section {
|
||||
padding-left: clamp(0.5rem, 3.5vw, 0.875rem);
|
||||
padding-right: clamp(0.5rem, 3.5vw, 0.875rem);
|
||||
}
|
||||
|
||||
.info-cell {
|
||||
padding-left: clamp(0.375rem, 2.4vw, 0.5rem);
|
||||
padding-right: clamp(0.375rem, 2.4vw, 0.5rem);
|
||||
}
|
||||
|
||||
.text-block p {
|
||||
max-height: clamp(4.5rem, 24vw, 6.25rem);
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 330px) {
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.info-cell.wide {
|
||||
grid-column: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
Reference in New Issue
Block a user