4 Commits
6 changed files with 87 additions and 12 deletions
@@ -6,7 +6,6 @@ import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.DesensitizedUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.zhgh.staffmanage.member.param.pageform.MemberStatisticsPageForm;
@@ -54,13 +53,7 @@ public class MemberComprehensiveController {
@At
@SaCheckPermission("member.statistics.comprehensive")
public Result pageData(MemberStatisticsPageForm pageForm) {
Sql sql = memberStatisticsService.getComprehensiveSql(pageForm);
Pagination<NutMap> pagination = memberStatisticsService.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));
}
return Result.success(pagination);
return Result.success(memberStatisticsService.getComprehensivePage(pageForm));
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.staffmanage.member.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.staffmanage.member.param.pageform.MemberStatisticsPageForm;
import org.apache.poi.ss.usermodel.Workbook;
@@ -33,6 +34,13 @@ public interface MemberStatisticsService extends BaseService<Sys_user> {
*/
Sql getComprehensiveSql(MemberStatisticsPageForm pageForm);
/**
* 查询综合查询列表,证件号码是否脱敏由当前登录人的角色决定。
* @param pageForm 列表筛选、排序和分页条件,不接受调用方指定脱敏权限
* @return 分页结果,list中的mobile始终脱敏;idCard仅系统管理员或校工会管理员可见原值
*/
Pagination<NutMap> getComprehensivePage(MemberStatisticsPageForm pageForm);
/**
* 获取工会分析数据
@@ -7,6 +7,8 @@ import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.DesensitizedUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
@@ -44,6 +46,22 @@ public class MemberStatisticsServiceImpl extends BaseServiceImpl<Sys_user> imple
super(dao);
}
/** 列表沿用原查询范围,只按登录角色调整证件号码展示,不改变手机号及导出规则。 */
@Override
public Pagination<NutMap> getComprehensivePage(MemberStatisticsPageForm pageForm) {
Sql sql = getComprehensiveSql(pageForm);
Pagination<NutMap> pagination = listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
boolean showFullIdCard = AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name());
for (NutMap row : pagination.getList()) {
row.put("mobile", DesensitizedUtil.mobilePhone(row.getString("mobile")));
// 角色取自服务端会话;同时拥有普通角色的管理员仍按管理员展示原始证件号码。
if (!showFullIdCard) {
row.put("idCard", DesensitizedUtil.idCardNum(row.getString("idCard"), 3, 4));
}
}
return pagination;
}
@Override
public Cnd getCnd(MemberStatisticsPageForm pageForm) {
@@ -347,6 +347,19 @@ module.exports = {
// 预览图片
handlePreview(file) {
if (this.upload_mode === "image") {
// 回显记录的名称可能是无扩展名的下载地址;图片模式直接预览缩略图地址。
const url = file.url || (this.upload_result_type === "url" && file.response?.data)
if (!url) {
this.$message.warning("暂无可预览的图片")
return
}
const image = new Image()
image.src = url
const viewer = new Viewer(image, {zIndex: 99999, title: false, hidden: () => viewer.destroy()})
viewer.show()
return
}
const suffix = file.name.substring(file.name.lastIndexOf(".") + 1).toLowerCase()
const fileId = file.response.data.substring(file.response.data.lastIndexOf("=") + 1)
const buildFile = {
@@ -75,7 +75,11 @@ const optionSelect = {
class="option-tag" type="primary" effect="plain">已选择
</el-tag>
<div class="option-image">
<div class="option-image" role="button" :tabindex="option.imgUrl ? 0 : -1"
aria-label="预览福利图片" :aria-disabled="option.imgUrl ? 'false' : 'true'"
@click.stop="previewOptionImage(option.imgUrl)"
@keydown.enter.stop.prevent="previewOptionImage(option.imgUrl)"
@keydown.space.stop.prevent="previewOptionImage(option.imgUrl)">
<el-image :src="option.imgUrl" fit="cover"></el-image>
</div>
@@ -605,6 +609,15 @@ const optionSelect = {
}
},
previewOptionImage(url) {
if (!url) return
// 福利图片地址可能没有扩展名,直接预览原图,不触发福利选择。
const image = new Image()
image.src = url
const viewer = new Viewer(image, {zIndex: 99999, title: false, hidden: () => viewer.destroy()})
viewer.show()
},
// 显示选项详情
showOptionDetail(option) {
// 阻止事件冒泡,避免触发父元素的点击事件
@@ -886,6 +899,7 @@ const optionSelect = {
height: 3px;
background: linear-gradient(90deg, var(--primary-color), #66b1ff);
z-index: 1;
pointer-events: none;
}
/* 选项标签 */
@@ -894,6 +908,7 @@ const optionSelect = {
top: 8px;
right: 8px;
z-index: 2;
pointer-events: none;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
@@ -918,6 +933,10 @@ const optionSelect = {
background: linear-gradient(to top, rgba(0, 0, 0, 0.2), transparent);
}
/deep/ .option-image[aria-disabled="false"] {
cursor: zoom-in;
}
/deep/ .option-image .el-image {
width: 100%;
height: 100%;
@@ -707,9 +707,8 @@ layout("/layouts/platform_h5.html"){
:key="option.id"
class="welfare-option"
:class="{ selected: projectInfo.isCheckBox === 'radio' ? selectedRadioId === option.id : option.selectNum > 0 }"
@click="projectInfo.isCheckBox === 'radio' && !isDeadlinePassed ? selectRadioOption(option.id) : null"
>
<div class="welfare-option-image">
<div class="welfare-option-image" @click.stop="previewOptionImage(option.imgUrl)">
<van-image :src="option.imgUrl" fit="cover" width="100%" height="100%" radius="4px"></van-image>
<div class="welfare-tag"
v-if="projectInfo.isCheckBox === 'radio' ? selectedRadioId === option.id : option.selectNum > 0">
@@ -717,7 +716,8 @@ layout("/layouts/platform_h5.html"){
</div>
</div>
<div class="welfare-option-content">
<div class="welfare-option-content"
@click="projectInfo.isCheckBox === 'radio' && !isDeadlinePassed ? selectRadioOption(option.id) : null">
<div class="welfare-option-title">{{ option.optionName }}</div>
<!-- 查看详情按钮 -->
@@ -846,6 +846,10 @@ layout("/layouts/platform_h5.html"){
</div>
</van-action-sheet>
<van-image-preview :value="showOptionImagePreview" :images="optionPreviewImages"
closeable :show-index="false" :close-on-popstate="false"
@input="visible => { if (!visible) closeOptionImagePreview() }"></van-image-preview>
<!-- 选项详情弹窗 -->
<van-popup v-model="showOptionDetailDialog" round closeable close-icon="close" position="bottom"
:close-on-click-overlay="false"
@@ -932,6 +936,8 @@ layout("/layouts/platform_h5.html"){
isSubmitting: false,
hasSubmittedBefore: false, // 是否之前提交过
showOptionDetailDialog: false, // 选项详情弹窗
showOptionImagePreview: false,
optionPreviewImages: [],
selectedOption: null, // 当前选中的选项
popupHistoryOwner: "welfare-select-" + Date.now(),
detailScale: 1,
@@ -1347,6 +1353,21 @@ layout("/layouts/platform_h5.html"){
},
// 显示选项详情
previewOptionImage(url) {
if (!url || this.showOptionImagePreview) return
// 图片预览不修改选项状态,返回键只关闭当前预览层。
this.$set(this, "optionPreviewImages", [url])
this.$set(this, "showOptionImagePreview", true)
window.mobilePopupHistory.open(this.popupHistoryOwner, "optionImage")
},
closeOptionImagePreview() {
if (!this.showOptionImagePreview) return
// 先隐藏,防止关闭动画期间的重复点击触发多次历史回退。
this.$set(this, "showOptionImagePreview", false)
window.mobilePopupHistory.close(this.popupHistoryOwner, "optionImage")
},
showOptionDetail(option) {
this.selectedOption = option
this.showOptionDetailDialog = true
@@ -1370,6 +1391,9 @@ layout("/layouts/platform_h5.html"){
},
closePopupFromHistory(popupId) {
if (popupId === "optionImage") {
this.$set(this, "showOptionImagePreview", false)
}
if (popupId === "optionDetail") {
this.showOptionDetailDialog = false
}