This commit is contained in:
那些花儿
2025-07-02 19:22:58 +08:00
parent 725dc7b289
commit 56cbae8ec2
14 changed files with 1355 additions and 143 deletions
@@ -0,0 +1,25 @@
package com.budwk.app.base.model;
import lombok.Getter;
/**
* Excel导入错误信息
*/
public class ExcelImportError {
/**
* 行号
*/
private int row;
/**
* 错误信息
*/
@Getter
private String errMsg;
public void setErrInfo(String errMsg, int row) {
this.errMsg = errMsg;
this.row = row;
}
}
@@ -0,0 +1,29 @@
package com.budwk.app.base.model;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* Excel导入结果
* @param <T>
*/
@Data
public class ExcelImportRes<T extends ExcelImportError> {
private int totalRecords = 0;
private int successCount = 0;
private int failedCount = 0;
private List<T> errorDetails = new ArrayList<>();
// 失败数+1
public void addFailedCount() {
failedCount++;
System.out.println(failedCount);
}
}
@@ -176,9 +176,9 @@ public class SysHomeController {
Date startDate = activity.getStartDate();
Date endDate = activity.getEndDate();
// 判断当前时间是否在活动范围内,不在就过滤掉
if (!DateUtil.isIn(today, startDate, endDate)) {
continue;
}
// if (!DateUtil.isIn(today, startDate, endDate)) {
// continue;
// }
Integer allowUserGroupId = activity.getAllowUserGroupId();
String allowUserSql = activity.getAllowUserSql();
activity.setAllowUserSql(null);
@@ -11,10 +11,12 @@ import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.Static;
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.At;
@@ -32,11 +34,9 @@ import org.nutz.mvc.annotation.Param;
@Ok("json:full")
public class ActivityBasicScopeUserDataController {
@Inject
private BaseService baseService;
@At("")
@Ok("beetl:platform/zhgh/activity/basic/userScopeData/index.html")
@SaCheckPermission("activity.basic.user")
@@ -126,6 +126,7 @@ public class ActivityBasicScopeUserDataController {
@At
@SLog(tag = "活动人员查询", msg = "删除活动人员")
@SaCheckPermission("activity.basic.user")
@Aop(TransAop.READ_COMMITTED)
public Result doDelete(@Param(value = "id") String id,
@Param(value = "groupId") Integer groupId,
@Param(value = "searchKeyword") String searchKeyword,
@@ -158,6 +159,7 @@ public class ActivityBasicScopeUserDataController {
cnd.and(new Static("userId in (" + sql + ")"));
}
baseService.dao().clear(ActivityUserScope.class, cnd);
baseService.dao().clear(ActivityUserScope.class, Cnd.where(ActivityUserScope::getUserId, "not in", Sqls.create("select id from sys_user")));
return Result.success();
}
@@ -1,19 +1,21 @@
package com.budwk.app.zhgh.welfare.controller;
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.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.model.ExcelImportRes;
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.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.EasyExcelUtil;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.welfare.mode.WelfareUserImportExcel;
import com.budwk.app.zhgh.welfare.mode.WelfareUserTemp;
import com.budwk.app.zhgh.welfare.model.WelfareList;
import com.budwk.app.zhgh.welfare.model.WelfareUserSelection;
@@ -23,16 +25,15 @@ import com.budwk.app.zhgh.welfare.service.WelfareListService;
import com.budwk.app.zhgh.welfare.service.WelfareProjectService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.AdaptBy;
@@ -44,22 +45,16 @@ import org.nutz.mvc.upload.UploadAdaptor;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* @ClassName WelfareListController
* @Description TODO
* @Author zhf
* @Date 2024/8/13 18:28
*/
@IocBean
@Ok("json:full")
@At("/platform/welfare/list/mange")
@Api(tags = "福利名单")
@Slf4j
public class WelfareListController {
@Inject
@@ -120,7 +115,6 @@ public class WelfareListController {
welfareListService.exportXlsx(pageForm, response);
}
@At
@SaCheckPermission("welfare.list.mange")
@ApiOperation("编辑备注")
@@ -132,18 +126,16 @@ public class WelfareListController {
@At
@Ok("void")
@SaCheckPermission("welfare.list.mange")
public void downloadImport(HttpServletResponse response) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();) {
EasyExcel.write(byteArrayOutputStream, WelfareUserTemp.class)
.sheet("福利名单导入模版")
.doWrite(ArrayList::new);
CommonDownloadUtil.download("福利名单导入模版.xlsx", byteArrayOutputStream.toByteArray(), response);
} catch (Exception e) {
e.printStackTrace();
}
@ApiOperation("下载模版")
public void downloadTemplate(HttpServletResponse response) {
List<ExcelExportEntity> entities = new ArrayList<>();
entities.add(new ExcelExportEntity("工号", "loginname", 20));
entities.add(new ExcelExportEntity("姓名", "username", 20));
ExportParams exportParams = new ExportParams();
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, Collections.emptyList());
CommonDownloadUtil.download("福利名单模版.xlsx", workbook, response);
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("welfare.list.mange")
@@ -193,4 +185,75 @@ public class WelfareListController {
}
return Result.success("导入成功");
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("welfare.list.mange")
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
@SLog(tag = "福利名单管理", msg = "导入福利名单")
public Result importByExcel(@Param("file") TempFile tempFile, @Param("projectId") String projectId) {
// 读取数据
List<WelfareUserImportExcel> excelList = ExcelImportUtil.importExcel(tempFile.getFile(), WelfareUserImportExcel.class, new ImportParams());
// 查询用户
List<String> loginNames = excelList.stream().map(WelfareUserImportExcel::getLoginName).filter(StrUtil::isNotBlank).collect(Collectors.toList());
List<View_user> sysUsers = welfareListService.dao().query(View_user.class, Cnd.where(View_user::getLoginname, "in", loginNames));
// 查询当前的福利会员
List<WelfareList> welfareLists = welfareListService.query(Cnd.where("projectId", "=", projectId));
// 创建结果集
ExcelImportRes<WelfareUserImportExcel> excelImportRes = new ExcelImportRes<>();
excelImportRes.setTotalRecords(excelList.size());
for (int i = 0; i < excelList.size(); i++) {
WelfareUserImportExcel excel = excelList.get(i);
if (StrUtil.isBlank(excel.getLoginName())) {
excel.setErrInfo("工号为空", i + 1);
continue;
}
// 不能在excel里重复
if (excelList.stream().filter(s -> s.getLoginName().equals(excel.getLoginName())).count() > 1) {
excel.setErrInfo("重复数据", i + 1);
}
View_user user = sysUsers.stream().filter(s -> s.getLoginname().equals(excel.getLoginName())).findFirst().orElse(null);
if (user == null) {
excel.setErrInfo("无此用户", i + 1);
continue;
}
if (welfareLists.stream().anyMatch(s -> s.getUserId().equals(user.getId()))) {
excel.setErrInfo("该用户已加入福利名单", i + 1);
continue;
}
WelfareList wl = new WelfareList();
wl.setProjectId(projectId);
wl.setUserId(user.getId());
wl.setWelfareUnitId(user.getUnitId());
wl.setWelfareUnitName(user.getUnitName());
wl.setWelfareUnionId(user.getUnionId());
wl.setWelfareUnionName(user.getUnionName());
wl.setPersonType(user.getPersonType());
wl.setPreparedBy(user.getPreparedBy());
wl.setUserState(user.getUserState());
try {
welfareListService.insert(wl);
} catch (Exception e) {
log.error("添加福利名单失败:{}", e.getMessage());
excel.setErrInfo("添加失败", i + 1);
}
}
// 添加错误记录
excelImportRes.setErrorDetails(excelList.stream().filter(s -> StrUtil.isNotBlank(s.getErrMsg())).collect(Collectors.toList()));
excelImportRes.setFailedCount(excelImportRes.getErrorDetails().size());
return Result.success(excelImportRes);
}
}
@@ -0,0 +1,18 @@
package com.budwk.app.zhgh.welfare.mode;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.budwk.app.base.model.ExcelImportError;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class WelfareUserImportExcel extends ExcelImportError {
@Excel(name = "工号")
private String loginName;
@Excel(name = "姓名")
private String userName;
}
@@ -127,10 +127,10 @@
<el-card shadow="never" class="mt10">
<table-tool :label="currentGroupName">
<el-button @click="doExportUser" icon="el-icon-printer" size="medium" type="primary" :disabled="!pageForm.groupId">
<el-button @click="doExportUser" icon="el-icon-printer" size="small" type="primary" :disabled="!pageForm.groupId">
导出活动分组人员xlsx
</el-button>
<el-button @click="doDelete(null)" type="danger" size="medium" :disabled="tableData.length === 0">
<el-button @click="doDelete(null)" type="danger" size="small" :disabled="tableData.length === 0">
删除{{ currentGroupName }}
</el-button>
</table-tool>
@@ -238,7 +238,7 @@ module.exports = {
components: {},
methods: {
viewGroupName() {
if (this.pageForm.groupId) {
if (this.pageForm?.groupId) {
const group = this.activityGroupList.find((v) => v.groupId === this.pageForm.groupId)
this.currentGroupName = group.groupName + "人员"
return
@@ -282,14 +282,16 @@ module.exports = {
async getActivityGroup(id) {
const resp = await $.get("/platform/activity/basic/scope/getActivityUserScopeGroup")
this.activityGroupList = resp.data
if (id) {
if (this.activityGroupList.some((v) => v.groupId === id)) {
this.$set(this.pageForm, "groupId", id)
if (resp.data && resp.data.length > 0) {
if (id) {
if (this.activityGroupList.some((v) => v.groupId === id)) {
this.$set(this.pageForm, "groupId", id)
} else {
this.$set(this.pageForm, "groupId", resp.data[0].groupId)
}
} else {
this.$set(this.pageForm, "groupId", resp.data[0].groupId)
}
} else {
this.$set(this.pageForm, "groupId", resp.data[0].groupId)
}
await this.doSearch()
},
@@ -0,0 +1,994 @@
<script>
module.exports = {
name: "excelImport",
props: {
url: {
type: String,
required: true,
description: "后端接收文件的URL"
},
extra_params: {
type: Object,
default: () => ({}),
description: "上传时附加的参数"
},
template_url: {
type: String,
default: "",
description: "下载导入模板的URL地址"
},
mode: {
type: String,
default: "dialog",
validator: (value) => ["dialog", "direct"].includes(value),
description: "显示模式:dialog-弹窗模式(默认)direct-直接显示"
},
title: {
type: String,
default: "Excel 数据导入",
description: "弹窗标题(dialog模式下有效)"
},
width: {
type: String,
default: "800px",
description: "弹窗宽度(dialog模式下有效)"
},
visible: {
type: Boolean,
default: false,
description: "是否显示弹窗(dialog模式下有效)"
}
},
data() {
return {
selectedFile: null,
importing: false,
importComplete: false,
progress: 0,
progressText: "",
stats: {
totalRecords: 0,
successCount: 0,
failedCount: 0
},
errorDetails: [],
excelData: [],
dialogVisible: this.visible,
downloading: false
}
},
computed: {
progressColor() {
if (this.progress < 30) return "#F56C6C"
if (this.progress < 70) return "#E6A23C"
return "#67C23A"
},
successRate() {
if (this.stats.totalRecords === 0) return 0
return Math.round((this.stats.successCount / this.stats.totalRecords) * 100)
},
showTemplateBtn() {
return !!this.template_url
}
},
watch: {
visible(val) {
this.dialogVisible = val
},
dialogVisible(val) {
if (val !== this.visible) {
this.$emit("update:visible", val)
}
if (!val) {
this.resetUpload()
}
}
},
methods: {
handleFileChange(file) {
this.selectedFile = file.raw
this.resetStats()
},
beforeUpload(file) {
const isExcel =
file.type === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
file.type === "application/vnd.ms-excel" ||
file.name.toLowerCase().endsWith(".xlsx") ||
file.name.toLowerCase().endsWith(".xls")
if (!isExcel) {
this.$message.error("只能上传 Excel 文件!")
return false
}
const isLt10M = file.size / 1024 / 1024 < 10
if (!isLt10M) {
this.$message.error("文件大小不能超过 10MB")
return false
}
return false // 阻止自动上传
},
async startImport() {
if (!this.selectedFile) {
this.$message.warning("请先选择文件!")
return
}
if (!this.url) {
this.$message.error("未配置上传URL")
return
}
this.importing = true
this.progress = 0
this.progressText = "准备上传文件..."
try {
// 创建FormData对象
const formData = new FormData()
formData.append("file", this.selectedFile)
// 添加额外参数
if (this.extra_params) {
Object.keys(this.extra_params).forEach((key) => {
formData.append(key, this.extra_params[key])
})
}
// 设置上传进度
await this.updateProgress(20, "正在上传文件...")
// 发送请求到后端
const { code, data } = await this.$axios.post(this.url, formData, {
headers: {
"Content-Type": "multipart/form-data"
},
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round((progressEvent.loaded * 40) / progressEvent.total)
this.updateProgress(20 + percentCompleted, "上传中...")
}
})
// 处理后端返回的结果
await this.updateProgress(70, "处理后端导入结果...")
if (code === 0 && data) {
// 处理成功
const result = data || {}
this.stats.totalRecords = result.totalRecords || 0
this.stats.successCount = result.successCount || 0
this.stats.failedCount = result.failedCount || 0
this.errorDetails = result.errorDetails || []
await this.updateProgress(100, "导入完成!")
setTimeout(() => {
this.importing = false
this.importComplete = true
this.$message.success("Excel 导入完成!")
this.$emit("import-success", result)
}, 500)
} else {
// 处理失败
throw new Error(msg || "导入失败")
}
} catch (error) {
this.importing = false
this.$message.error("导入失败:" + (error.message || "未知错误"))
this.$emit("import-error", error)
}
},
async downloadTemplate() {
if (!this.template_url) {
this.$message.warning("未配置模板下载地址!")
return
}
this.downloading = true
try {
// 下载模板文件
const response = await this.$axios({
url: this.template_url,
method: "GET",
responseType: "blob"
})
// 创建下载链接
const blob = new Blob([response.data])
const url = window.URL.createObjectURL(blob)
// 获取文件名,从Content-Disposition中提取或使用默认名称
let filename = "导入模板.xlsx"
const disposition = response.headers["content-disposition"]
if (disposition && disposition.indexOf("attachment") !== -1) {
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
const matches = filenameRegex.exec(disposition)
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, "")
// 解决中文乱码问题
try {
filename = decodeURIComponent(filename)
} catch (e) {
// 解码失败时使用原始文件名
}
}
}
// 触发下载
const link = document.createElement("a")
link.href = url
link.download = filename
link.style.display = "none"
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
this.$message.success("模板下载成功!")
} catch (error) {
this.$message.error("模板下载失败:" + (error.message || "未知错误"))
this.$emit("template-error", error)
} finally {
this.downloading = false
}
},
readExcelFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = (e) => {
try {
const data = new Uint8Array(e.target.result)
const workbook = XLSX.read(data, { type: "array" })
const firstSheet = workbook.Sheets[workbook.SheetNames[0]]
const jsonData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 })
resolve(jsonData)
} catch (error) {
reject(new Error("文件解析失败"))
}
}
reader.onerror = () => reject(new Error("文件读取失败"))
reader.readAsArrayBuffer(file)
})
},
async processData(data) {
this.excelData = data
this.stats.totalRecords = data.length - 1 // 减去表头
this.errorDetails = []
let successCount = 0
let failedCount = 0
// 模拟数据处理过程
for (let i = 1; i < data.length; i++) {
await this.updateProgress(40 + Math.round((i / (data.length - 1)) * 50), `处理第 ${i} 条记录...`)
// 模拟数据验证和处理
const row = data[i]
const isValid = this.validateRow(row, i + 1)
if (isValid) {
successCount++
} else {
failedCount++
}
// 添加延迟以显示进度效果
if (i % 10 === 0) {
await new Promise((resolve) => setTimeout(resolve, 50))
}
}
this.stats.successCount = successCount
this.stats.failedCount = failedCount
},
validateRow(row, rowIndex) {
// 模拟数据验证逻辑
if (!row || row.length === 0) {
this.errorDetails.push({
row: rowIndex,
message: "空行数据"
})
return false
}
// 随机生成一些错误(用于演示)
if (Math.random() < 0.1) {
// 10% 的错误率
const errors = ["必填字段为空", "数据格式不正确", "数据长度超出限制", "重复数据", "外键约束违反"]
this.errorDetails.push({
row: rowIndex,
message: errors[Math.floor(Math.random() * errors.length)]
})
return false
}
return true
},
updateProgress(percentage, text) {
return new Promise((resolve) => {
setTimeout(() => {
this.progress = percentage
this.progressText = text
resolve()
}, 100)
})
},
getSuccessRateColor(rate) {
if (rate >= 90) return "#67C23A"
if (rate >= 70) return "#E6A23C"
return "#F56C6C"
},
formatFileSize(bytes) {
if (bytes === 0) return "0 Bytes"
const k = 1024
const sizes = ["Bytes", "KB", "MB", "GB"]
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]
},
downloadReport() {
if (this.errorDetails.length === 0) return
let content = "错误报告\n\n"
content += `总记录数: ${this.stats.totalRecords}\n`
content += `成功数: ${this.stats.successCount}\n`
content += `失败数: ${this.stats.failedCount}\n`
content += `成功率: ${this.successRate}%\n\n`
content += "错误详情:\n"
this.errorDetails.forEach((error) => {
content += `${error.row}行: ${error.message}\n`
})
const blob = new Blob([content], { type: "text/plain;charset=utf-8" })
const url = window.URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = "导入错误报告.txt"
a.click()
window.URL.revokeObjectURL(url)
this.$message.success("错误报告下载完成!")
},
resetUpload() {
this.selectedFile = null
this.importing = false
this.importComplete = false
this.progress = 0
this.progressText = ""
this.resetStats()
if (this.$refs.upload) {
this.$refs.upload.clearFiles()
}
this.$emit("reset")
},
resetStats() {
this.stats = {
totalRecords: 0,
successCount: 0,
failedCount: 0
}
this.errorDetails = []
this.excelData = []
},
closeDialog() {
if (this.mode === "dialog") {
this.dialogVisible = false
}
},
showDialog() {
if (this.mode === "dialog") {
this.dialogVisible = true
}
}
}
}
</script>
<template>
<!-- Dialog模式 -->
<el-dialog
v-if="mode === 'dialog'"
:visible.sync="dialogVisible"
:title="title"
:width="width"
@close="resetUpload"
:append-to-body="true"
:destroy-on-close="true"
>
<div class="excel-import-container dialog-mode">
<div class="content">
<!-- 文件上传区域 -->
<div class="upload-area" v-if="!importing && !importComplete">
<el-upload
ref="upload"
action="#"
:auto-upload="false"
:on-change="handleFileChange"
:before-upload="beforeUpload"
accept=".xlsx,.xls"
drag
:show-file-list="false"
>
<div class="upload-icon">📁</div>
<div class="upload-text"> Excel 文件拖拽到此处或点击选择文件</div>
<div class="upload-hint">支持 .xlsx.xls 格式文件大小不超过 10MB</div>
</el-upload>
</div>
<!-- 下载模板按钮 -->
<div v-if="showTemplateBtn && !importing && !importComplete" class="template-download">
<el-button type="text" @click="downloadTemplate" :loading="downloading" icon="el-icon-download">下载导入模板</el-button>
</div>
<!-- 已选择文件信息 -->
<div v-if="selectedFile && !importing && !importComplete" class="file-info">
<div class="file-info-item">
<span><strong>文件名:</strong></span>
<span>{{ selectedFile.name }}</span>
</div>
<div class="file-info-item">
<span><strong>文件大小:</strong></span>
<span>{{ formatFileSize(selectedFile.size) }}</span>
</div>
<div class="file-info-item">
<span><strong>文件类型:</strong></span>
<span>{{ selectedFile.type || "未知" }}</span>
</div>
</div>
<!-- 导入按钮 -->
<div v-if="selectedFile && !importing && !importComplete" class="actions">
<el-button type="primary" size="large" @click="startImport" :loading="importing">
<i class="el-icon-upload2"></i>
开始导入
</el-button>
<el-button size="large" @click="resetUpload">
<i class="el-icon-refresh"></i>
重新选择
</el-button>
</div>
<!-- 导入进度 -->
<div v-if="importing" class="progress-section">
<h3 style="text-align: center; color: #409eff; margin-bottom: 20px">
<i class="el-icon-loading"></i>
正在处理文件...
</h3>
<el-progress :percentage="progress" :color="progressColor" :stroke-width="8" text-inside></el-progress>
<p style="text-align: center; margin-top: 15px; color: #606266">
{{ progressText }}
</p>
</div>
<!-- 导入结果 -->
<div v-if="importComplete" class="result-section">
<div class="result-header">
<div class="result-icon">
<i class="el-icon-circle-check"></i>
</div>
<h3>导入完成</h3>
</div>
<div class="stats-container">
<div class="stat-card">
<div class="stat-icon total-icon">
<i class="el-icon-document"></i>
</div>
<div class="stat-content">
<div class="stat-number total">{{ stats.totalRecords }}</div>
<div class="stat-label">总记录数</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon success-icon">
<i class="el-icon-check"></i>
</div>
<div class="stat-content">
<div class="stat-number success">{{ stats.successCount }}</div>
<div class="stat-label">导入成功</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon failed-icon">
<i class="el-icon-close"></i>
</div>
<div class="stat-content">
<div class="stat-number failed">{{ stats.failedCount }}</div>
<div class="stat-label">导入失败</div>
</div>
</div>
</div>
<!-- 成功率展示 -->
<div class="success-rate-section">
<div class="success-rate-header">
<span class="success-rate-label">导入成功率</span>
<span class="success-rate-value" :style="{ color: getSuccessRateColor(successRate) }">{{ successRate }}%</span>
</div>
<el-progress :percentage="successRate" :color="getSuccessRateColor(successRate)" :stroke-width="6"></el-progress>
</div>
<!-- 错误详情 -->
<div v-if="errorDetails.length > 0" class="error-details-section">
<el-collapse>
<el-collapse-item name="errors">
<template slot="title">
<div class="error-title">
<i class="el-icon-warning-outline"></i>
<span>查看错误详情 ({{ errorDetails.length }})</span>
</div>
</template>
<div class="error-list">
<div v-for="(error, index) in errorDetails" :key="index" class="error-item">
<div class="error-row"> {{ error.row }} </div>
<div class="error-message">{{ error.errMsg }}</div>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
<!-- 操作按钮 -->
<div class="actions">
<el-button type="primary" @click="downloadReport" v-if="stats.failedCount > 0">
<i class="el-icon-download"></i>
下载错误报告
</el-button>
<el-button @click="resetUpload">
<i class="el-icon-refresh"></i>
重新导入
</el-button>
<el-button @click="closeDialog">
<i class="el-icon-close"></i>
关闭
</el-button>
</div>
</div>
</div>
</div>
</el-dialog>
<!-- 直接显示模式 -->
<div v-else class="excel-import-container">
<div class="header">
<h1>📊 Excel 文件导入</h1>
<p>支持 .xlsx.xls 格式文件自动解析并统计导入结果</p>
</div>
<div class="content">
<!-- 文件上传区域 -->
<div class="upload-area" v-if="!importing && !importComplete">
<el-upload
ref="upload"
action="#"
:auto-upload="false"
:on-change="handleFileChange"
:before-upload="beforeUpload"
accept=".xlsx,.xls"
drag
:show-file-list="false"
>
<div class="upload-icon">📁</div>
<div class="upload-text"> Excel 文件拖拽到此处或点击选择文件</div>
<div class="upload-hint">支持 .xlsx.xls 格式文件大小不超过 10MB</div>
</el-upload>
</div>
<!-- 下载模板按钮 -->
<div v-if="showTemplateBtn && !importing && !importComplete" class="template-download">
<el-button type="text" @click="downloadTemplate" :loading="downloading" icon="el-icon-download">下载导入模板</el-button>
</div>
<!-- 已选择文件信息 -->
<div v-if="selectedFile && !importing && !importComplete" class="file-info">
<div class="file-info-item">
<span><strong>文件名:</strong></span>
<span>{{ selectedFile.name }}</span>
</div>
<div class="file-info-item">
<span><strong>文件大小:</strong></span>
<span>{{ formatFileSize(selectedFile.size) }}</span>
</div>
<div class="file-info-item">
<span><strong>文件类型:</strong></span>
<span>{{ selectedFile.type || "未知" }}</span>
</div>
</div>
<!-- 导入按钮 -->
<div v-if="selectedFile && !importing && !importComplete" class="actions">
<el-button type="primary" size="large" @click="startImport" :loading="importing">
<i class="el-icon-upload2"></i>
开始导入
</el-button>
<el-button size="large" @click="resetUpload">
<i class="el-icon-refresh"></i>
重新选择
</el-button>
</div>
<!-- 导入进度 -->
<div v-if="importing" class="progress-section">
<h3 style="text-align: center; color: #409eff; margin-bottom: 20px">
<i class="el-icon-loading"></i>
正在处理文件...
</h3>
<el-progress :percentage="progress" :color="progressColor" :stroke-width="8" text-inside></el-progress>
<p style="text-align: center; margin-top: 15px; color: #606266">
{{ progressText }}
</p>
</div>
<!-- 导入结果 -->
<div v-if="importComplete" class="result-section">
<div class="result-header">
<div class="result-icon">
<i class="el-icon-circle-check"></i>
</div>
<h3>导入完成</h3>
</div>
<div class="stats-container">
<div class="stat-card">
<div class="stat-icon total-icon">
<i class="el-icon-document"></i>
</div>
<div class="stat-content">
<div class="stat-number total">{{ stats.totalRecords }}</div>
<div class="stat-label">总记录数</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon success-icon">
<i class="el-icon-check"></i>
</div>
<div class="stat-content">
<div class="stat-number success">{{ stats.successCount }}</div>
<div class="stat-label">导入成功</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon failed-icon">
<i class="el-icon-close"></i>
</div>
<div class="stat-content">
<div class="stat-number failed">{{ stats.failedCount }}</div>
<div class="stat-label">导入失败</div>
</div>
</div>
</div>
<!-- 错误详情 -->
<div v-if="errorDetails.length > 0" class="error-details-section">
<el-collapse>
<el-collapse-item name="errors">
<template slot="title">
<div class="error-title">
<i class="el-icon-warning-outline"></i>
<span>查看错误详情 ({{ errorDetails.length }})</span>
</div>
</template>
<div class="error-list">
<div v-for="(error, index) in errorDetails" :key="index" class="error-item">
<div class="error-row"> {{ error.row }} </div>
<div class="error-message">{{ error.message }}</div>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
<!-- 操作按钮 -->
<div class="actions">
<el-button type="primary" @click="downloadReport" v-if="stats.failedCount > 0">
<i class="el-icon-download"></i>
下载错误报告
</el-button>
<el-button @click="resetUpload">
<i class="el-icon-refresh"></i>
重新导入
</el-button>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.excel-import-container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 12px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.excel-import-container.dialog-mode {
max-width: 100%;
margin: 0;
box-shadow: none;
border-radius: 0;
}
.header {
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
margin: 0;
font-size: 28px;
font-weight: 300;
}
.header p {
margin: 10px 0 0 0;
opacity: 0.9;
font-size: 14px;
}
.content {
padding: 40px;
}
.template-download {
text-align: center;
margin-bottom: 20px;
}
.upload-area {
border: 2px dashed #d9d9d9;
border-radius: 8px;
padding: 40px;
text-align: center;
background: #fafafa;
transition: all 0.3s ease;
margin-bottom: 15px;
}
.upload-area:hover {
border-color: #409eff;
background: #f0f9ff;
}
.upload-icon {
font-size: 48px;
color: #c0c4cc;
margin-bottom: 16px;
}
.upload-text {
color: #606266;
font-size: 16px;
margin-bottom: 8px;
}
.upload-hint {
color: #909399;
font-size: 14px;
}
.progress-section {
margin: 30px 0;
}
.stats-container {
display: flex;
justify-content: space-between;
gap: 15px;
margin-top: 20px;
}
.stat-card {
flex: 1;
background: linear-gradient(135deg, #f6f8fa 0%, #ffffff 100%);
border: 1px solid #e1e8ed;
border-radius: 8px;
padding: 15px;
transition: transform 0.2s ease;
display: flex;
align-items: center;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
.stat-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
flex-shrink: 0;
}
.total-icon {
background-color: rgba(64, 158, 255, 0.1);
color: #409eff;
}
.success-icon {
background-color: rgba(103, 194, 58, 0.1);
color: #67c23a;
}
.failed-icon {
background-color: rgba(245, 108, 108, 0.1);
color: #f56c6c;
}
.stat-content {
text-align: left;
}
.stat-number {
font-size: 24px;
font-weight: bold;
line-height: 1;
margin-bottom: 4px;
}
.total {
color: #409eff;
}
.success {
color: #67c23a;
}
.failed {
color: #f56c6c;
}
.stat-label {
color: #606266;
font-size: 12px;
}
.result-section {
margin-top: 30px;
padding: 30px;
background: #fff;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
}
.result-header {
text-align: center;
margin-bottom: 30px;
}
.result-icon {
font-size: 48px;
color: #67c23a;
margin-bottom: 10px;
}
.result-header h3 {
font-size: 20px;
font-weight: 500;
margin: 0;
color: #67c23a;
}
.success-rate-section {
margin-top: 30px;
background: #f9f9f9;
border-radius: 8px;
padding: 20px;
}
.success-rate-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.success-rate-label {
font-weight: 500;
font-size: 14px;
color: #606266;
}
.success-rate-value {
font-weight: bold;
font-size: 18px;
}
.error-details-section {
margin-top: 20px;
}
.error-title {
display: flex;
align-items: center;
color: #f56c6c;
font-weight: 500;
}
.error-title i {
margin-right: 8px;
font-size: 16px;
}
.error-list {
max-height: 250px;
overflow-y: auto;
background: #fff5f5;
border-radius: 4px;
}
.error-item {
padding: 12px;
border-bottom: 1px solid #fde2e2;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
}
.error-item:last-child {
border-bottom: none;
}
.error-row {
color: #f56c6c;
font-weight: bold;
background: rgba(245, 108, 108, 0.1);
padding: 2px 8px;
border-radius: 4px;
margin-right: 8px;
}
.error-message {
color: #606266;
flex: 1;
}
.file-info {
background: #e8f4fd;
border: 1px solid #b3d8ff;
border-radius: 6px;
padding: 16px;
margin-bottom: 20px;
}
.file-info-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.file-info-item:last-child {
margin-bottom: 0;
}
.actions {
text-align: center;
margin-top: 30px;
}
</style>
@@ -430,6 +430,7 @@
Vue.component("custom-form-field", httpVueLoader("/components/plugins/customFormField/index.vue?v=" + new Date().getTime()))
Vue.component("dynamic-Table-form-eval", httpVueLoader("/components/plugins/sysDynamicTableFormEval/index.vue?v=" + new Date().getTime()))
Vue.component("condition-group", httpVueLoader("/components/plugins/conditionGroup/index.vue?v=" + new Date().getTime()))
Vue.component("excel-import", httpVueLoader("/components/plugins/sysImport/excelImport.vue?v=" + new Date().getTime()))
</script>
</head>
<body>
@@ -3,28 +3,47 @@ const activity = {
<div class="home-activity">
<el-card shadow="never">
<div class="card-title" slot="header">最新活动</div>
<div v-if="activityOptions && activityOptions.length > 0" v-for="item in activityOptions" :key="item.id">
<div style="width: 100%">
<div style="background-color: #fff; position: relative; margin-bottom: 10px">
<div style="display: flex; justify-content: center; align-items: center">
<el-image :src="item.cover" alt="" fit="contain" style="margin: 3px 14px 0 0; width: 100px; height: 70px; border-radius: 8px">
<div v-if="activityOptions && activityOptions.length > 0" v-for="item in activityOptions"
:key="item.id">
<div style="width: 100%">
<div style="background-color: #fff; position: relative; margin-bottom: 10px">
<div style="display: flex; justify-content: center; align-items: center;border-bottom: 1px solid var(--border-color-lighter);padding-bottom: 8px">
<div
style="position: relative;flex-shrink: 1;width: 150px; height: 90px;margin-right: 10px; ">
<el-image :src="item.cover" alt="" fit="cover"
style="width: 100%; height: 100%; border-radius: 8px;">
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
<div style="flex: 1">
<div style="line-height: 22px; color: #111">
<i class="el-icon-star-on" style="color: red; font-size: 17px"></i>{{item.name}}
</div>
<div style="font-size: 12px; line-height: 20px; color: #999">开始时间:{{$moment(item.startDate).format('MM-DD HH:mm')}}</div>
<div style="font-size: 12px; line-height: 20px; color: #999">结束时间:{{$moment(item.endDate).format('MM-DD HH:mm')}}</div>
<el-tag class="act-item-tag" type="danger" size="mini"
v-if="$moment().unix() > $moment(item.endDate).unix()">
已结束
</el-tag>
<el-tag class="act-item-tag" type="success" size="mini"
v-else-if="$moment().unix() < $moment(item.endDate).unix()">
进行中
</el-tag>
</div>
<div style="flex: 1;height: 90px;display: flex;flex-direction: column;justify-content: space-between;">
<div style="color: #111">
<i class="el-icon-star-on" style="color: red; font-size: 17px"></i>{{item.name}}
</div>
<div @click="enterActivity(item)">
<el-tag style="cursor: pointer">查看<i class="el-icon-s-promotion"></i></el-tag>
<div style="font-size: 12px; color: #999">
开始时间:{{$moment(item.startDate).format('MM-DD HH:mm')}}
</div>
<div style="font-size: 12px; color: #999">
结束时间:{{$moment(item.endDate).format('MM-DD HH:mm')}}
</div>
</div>
<div @click="enterActivity(item)">
<el-button size="mini" type="primary">查看<i class="el-icon-s-promotion"></i>
</el-button>
</div>
</div>
</div>
</div>
</div>
<el-empty v-if="activityOptions.length === 0" description="暂无活动"></el-empty>
</el-card>
@@ -63,5 +82,12 @@ const activity = {
.home-activity .el-empty {
padding: 40px 0 !important;
}
.home-activity .act-item-tag{
position: absolute;
right: 0;
bottom: 0;
z-index: 1;
}
`
}
@@ -22,7 +22,8 @@ const SYS_MENU_QUICK_ENTRY_COMPONENT = {
<el-table-column label="名称" prop="name"></el-table-column>
<el-table-column label="图标" prop="icon">
<template scope="{row}">
<svg-icon :name="row.icon"></svg-icon>
<svg-icon :name="row.icon" v-if="platform==='PC'"></svg-icon>
<img v-else :src="row.icon" alt="" style="width: 30px;height: 30px">
</template>
</el-table-column>
</el-table>
@@ -31,10 +31,10 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
style="width: 100%;"
v-model="formData.projectTypeCode">
<el-option
:key="item.id"
:label="item.name+' ('+item.code+')'"
:value="item.code"
v-for="item in projectTypeList">
:key="item.id"
:label="item.name+' ('+item.code+')'"
:value="item.code"
v-for="item in projectTypeList">
</el-option>
</el-select>
</el-form-item>
@@ -68,10 +68,10 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
reserve-keyword
style="width: 100%" v-model="formData.userId">
<el-option
:key="item.userId"
:label="item.userName+'-'+item.loginName+'-'+item.unitName"
:value="item.userId"
v-for="item in userList">
:key="item.userId"
:label="item.userName+'-'+item.loginName+'-'+item.unitName"
:value="item.userId"
v-for="item in userList">
</el-option>
</el-select>
</el-form-item>
@@ -84,48 +84,48 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
</el-form-item>
</el-col>
<!-- <el-col :span="12"-->
<!-- v-if="formData.isEnrollSystem === true">-->
<!-- <el-form-item label="活动计划时间" prop="plannedDate">-->
<!-- <el-date-picker-->
<!-- @change="plannedDateChange"-->
<!-- end-placeholder="结束日期"-->
<!-- range-separator="-"-->
<!-- start-placeholder="开始日期"-->
<!-- style="width: 100%"-->
<!-- type="daterange"-->
<!-- v-model="formData.plannedDate" value-format="yyyy-MM-dd">-->
<!-- </el-date-picker>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- <el-col :span="12"-->
<!-- v-if="formData.isEnrollSystem === true">-->
<!-- <el-form-item label="活动计划时间" prop="plannedDate">-->
<!-- <el-date-picker-->
<!-- @change="plannedDateChange"-->
<!-- end-placeholder="结束日期"-->
<!-- range-separator="-"-->
<!-- start-placeholder="开始日期"-->
<!-- style="width: 100%"-->
<!-- type="daterange"-->
<!-- v-model="formData.plannedDate" value-format="yyyy-MM-dd">-->
<!-- </el-date-picker>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<el-col :span="12">
<el-form-item label="实际活动时间" prop="time">
<el-date-picker
end-placeholder="结束日期"
range-separator="-"
start-placeholder="开始日期"
style="width: 100%"
type="daterange"
v-model="formData.time"
value-format="yyyy-MM-dd">
end-placeholder="结束日期"
range-separator="-"
start-placeholder="开始日期"
style="width: 100%"
type="daterange"
v-model="formData.time"
value-format="yyyy-MM-dd">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:rules="[{required:[1,2].includes(formData.signUpMethod),message:'请选择报名日期',trigger:['change','blur']}]"
label="报名日期"
prop="applyTime2">
:rules="[{required:[1,2].includes(formData.signUpMethod),message:'请选择报名日期',trigger:['change','blur']}]"
label="报名日期"
prop="applyTime2">
<el-date-picker
end-placeholder="结束时间"
range-separator="-"
start-placeholder="开始时间"
style="width: 100%"
type="datetimerange"
v-model="formData.applyTime2"
value-format="yyyy-MM-dd HH:mm">
end-placeholder="结束时间"
range-separator="-"
start-placeholder="开始时间"
style="width: 100%"
type="datetimerange"
v-model="formData.applyTime2"
value-format="yyyy-MM-dd HH:mm">
</el-date-picker>
</el-form-item>
</el-col>
@@ -133,9 +133,9 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
<el-col :span="12" v-if="!formData.isEnrollSystem">
<el-form-item label="活动人数" prop="peopleNum">
<el-input
maxlength="10"
placeholder="请填写活动人数"
v-model="formData.peopleNum"></el-input>
maxlength="10"
placeholder="请填写活动人数"
v-model="formData.peopleNum"></el-input>
</el-form-item>
</el-col>
@@ -156,8 +156,8 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
</div>
<div>
<el-button
@click="$refs.drawerUserScope.userScopeDialog = true"
type="primary">设置
@click="$refs.drawerUserScope.userScopeDialog = true"
type="primary">设置
</el-button>
</div>
</div>
@@ -181,7 +181,9 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
<el-form-item label="报名人数限制" prop="userNumberLimit">
<el-radio-group v-model="formData.userNumberLimit" size="small">
<el-radio border :label="1">总人数限制</el-radio>
<el-radio border :label="2" v-if="activity_type===40001">分工会人数限制</el-radio>
<el-radio border :label="2" v-if="activity_type===40001">
分工会人数限制
</el-radio>
<el-radio border :label="null">不限制</el-radio>
</el-radio-group>
</el-form-item>
@@ -315,10 +317,10 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
<el-col :span="24" v-if="formData.isEnrollSystem === false">
<el-form-item label="活动考核内容" prop="activityExamineContent">
<el-input
:rows="7"
placeholder="请输入活动考核内容"
type="textarea"
v-model="formData.activityExamineContent">
:rows="7"
placeholder="请输入活动考核内容"
type="textarea"
v-model="formData.activityExamineContent">
</el-input>
</el-form-item>
</el-col>
@@ -428,16 +430,16 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
<el-table :data="userData" border height="500" size="small"
stripe v-loading="userTabLoading" row-key="userId">
<el-table-column
label="序号" type="index"
width="100">
label="序号" type="index"
width="100">
</el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
:key="column.prop"
:sortable="column.sortable"
v-for="column in userColumns">
:label="column.label"
:prop="column.prop"
:key="column.prop"
:sortable="column.sortable"
v-for="column in userColumns">
</el-table-column>
<el-table-column label="操作"
prop="userOnline"
@@ -455,26 +457,25 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
</table-tool>
<el-form-item label="" prop="activitySummary">
<el-input
:disabled="!formData.time || Date.now() < Date.parse(formData.time[1])"
:rows="5"
placeholder="请输入活动内容"
type="textarea"
v-model="formData.activitySummary">
:disabled="!formData.time || Date.now() < Date.parse(formData.time[1])"
:rows="5"
placeholder="请输入活动内容"
type="textarea"
v-model="formData.activitySummary">
</el-input>
</el-form-item>
<el-alert
class="mb20"
show-icon
style="margin-left: 130px"
title="活动结束后可输入活动总结"
type="info"
v-if="!formData.time || Date.now() < Date.parse(formData.time[1])">
class="mb20"
show-icon
style="margin-left: 130px"
title="活动结束后可输入活动总结"
type="info"
v-if="!formData.time || Date.now() < Date.parse(formData.time[1])">
</el-alert>
</el-tab-pane>
</el-tabs>
<div style="padding: 20px;text-align: center;">
<el-button @click="operation" style="width: 300px" type="primary" :disabled="formLoading">
确 定
@@ -484,12 +485,12 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
</el-card>
<drawer-user-scope
@group_change="getActivityGroup"
ref="drawerUserScope"
:group_id.sync="formData.groupId"
@group_change="getActivityGroup"
ref="drawerUserScope"
:group_id.sync="formData.groupId"
></drawer-user-scope>
<el-dialog :close-on-click-modal="false" :visible.sync="userDialogVisible" title="添加人员" width="40%">
<el-dialog :close-on-click-modal="false" :visible.sync="userDialogVisible" append-to-body title="添加人员" width="40%">
<el-form :model="formData" label-width="100px">
<el-form-item label="参加人员" prop="userId">
@@ -501,11 +502,11 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
style="width: 100%"
v-model="formData.tissuePersonList">
<el-option
:disabled="userData.map(v=>v.userId).includes(item.userId)"
:key="item.userId"
:label="item.userName+'-'+item.loginName+'-'+item.unitName"
:value="item.userId"
v-for="item in userList">
:disabled="userData.map(v=>v.userId).includes(item.userId)"
:key="item.userId"
:label="item.userName+'-'+item.loginName+'-'+item.unitName"
:value="item.userId"
v-for="item in userList">
</el-option>
</el-select>
</el-form-item>
@@ -99,11 +99,14 @@ layout("/layouts/platform.html"){
</search-item>
<search-item label="聘用方式:">
<dict-select clearable code="USER_PREPARED_BY_TYPE"
multiple
collapse-tags
placeholder="请选择聘用方式"
v-model="pageForm.preparedBys"></dict-select>
<dict-select
clearable
code="USER_PREPARED_BY_TYPE"
multiple
collapse-tags
placeholder="请选择聘用方式"
v-model="pageForm.preparedBys"
></dict-select>
</search-item>
<search-item label="在职状态:">
@@ -201,9 +204,22 @@ layout("/layouts/platform.html"){
<add_welfare_list_by_select ref="addWelfareListUser" @search="doSearch"></add_welfare_list_by_select>
<!--添加福利人员-->
<add-user ref="addUserRef" @refresh="doSearch"></add-user>
<!--人员备注-->
<update-remark ref="updateRemarkRef" @refresh="doSearch"></update-remark>
<!--导入福利名单-->
<excel-import
ref="excelImportRef"
url="/platform/welfare/list/mange/importByExcel"
template_url="/platform/welfare/list/mange/downloadTemplate"
:visible.sync="showImportDialog"
title="导入用户数据"
width="700px"
:extra_params="{projectId:pageForm.projectId}"
></excel-import>
</div>
<script>
@@ -260,7 +276,9 @@ layout("/layouts/platform.html"){
importLoading: false,
importDialog: false,
welfareListUserDrawer: false
welfareListUserDrawer: false,
showImportDialog: false
}
},
methods: {
@@ -275,12 +293,14 @@ layout("/layouts/platform.html"){
this.doSearch()
this.importDialog = false
},
// 打开导入名单
openImport() {
this.importData = {
isfg: 1,
fileList: []
}
this.importDialog = true
this.showImportDialog = true
},
doDelete(row) {
this.$confirm("您确定要删除吗?", "提示", {
@@ -16,12 +16,13 @@ const home = {
<!--快捷入口-->
<div class="quick-grid">
<div class="section-title">快速入口</div>
<van-grid :column-num="4" icon-size="46" square clickable :border="false">
<van-grid-item v-for="item in quickEntries"
@click="menuClick(item)"
:key="item.id"
:text="item.name">
<img src="https://staticfile.org/images/banners/Home/7@2x.png"
<img :src="item.icon"
slot="icon"
style="width: 40px; height: 40px"/>
</van-grid-item>
@@ -30,8 +31,17 @@ const home = {
<!--活动列表-->
<div class="act-list">
<div class="section-title">最新活动</div>
<template v-if="activityOptions && activityOptions.length > 0">
<div v-for="item in activityOptions" :key="item.id" class="act-item" @click="enterAct(item)">
<van-tag class="act-item-wrapper-tag" v-if="$moment().unix() > $moment(item.endDate).unix()">
已结束
</van-tag>
<van-tag class="act-item-wrapper-tag" type="success" v-else-if="$moment().unix() < $moment(item.endDate).unix()">
进行中
</van-tag>
<div class="act-item-cover">
<van-image v-if="item.cover" width="120" height="84" :src="item.cover"></van-image>
</div>
@@ -93,6 +103,8 @@ const home = {
.quick-grid {
background-color: #fff;
margin: 10px;
border-radius: 10px;
padding: 10px;
}
.quick-grid .van-grid-item .van-grid-item__text {
@@ -101,6 +113,7 @@ const home = {
text-overflow: ellipsis;
width: 85%;
text-align: center;
flex-shrink: 0;
}
.act-list {
@@ -115,17 +128,22 @@ const home = {
width: 100%;
padding: 6px;
background-color: #fff;
position: relative;
display: flex;
position: relative;
}
.act-item-cover {
margin: 0 14px 0 0;
width: 120px;
height: 84px;
flex-shrink: 0;
}
.act-item-cover .van-image{
width: 120px;
height: 84px;
/*width: 120px;*/
/*height: 84px;*/
width: 100%;
height: 100%;
}
.act-item-wrapper{
@@ -135,6 +153,13 @@ const home = {
width: 100%;
}
.act-item-wrapper-tag{
position: absolute;
left: 10px;
top: 10px;
z-index: 1;
}
.act-item-wrapper .content-title{
font-size: 14px;
width: 100%;
@@ -151,5 +176,10 @@ const home = {
color: grey;
padding-bottom: 6px;
}
.section-title{
padding: 10px 0 5px 10px;
}
`
}