Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3ea84bfad | ||
|
|
61b8819057 | ||
|
|
01921f7fcf | ||
|
|
465a3257b5 | ||
|
|
45410cb0a3 | ||
|
|
6ef45e5403 | ||
|
|
4235deede7 | ||
|
|
43ae24dceb | ||
|
|
cccad1a852 | ||
|
|
0b3fef4d71 | ||
|
|
0b44f36c83 | ||
|
|
2de6820c1a | ||
|
|
478b70c2d1 | ||
|
|
d305317458 | ||
|
|
982065c424 | ||
|
|
a92f75730d | ||
|
|
8a0714a8e3 | ||
|
|
bf40155fb9 |
@@ -133,6 +133,38 @@
|
|||||||
</dependencies>
|
</dependencies>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<version>4.0.1</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.websocket</groupId>
|
||||||
|
<artifactId>javax.websocket-api</artifactId>
|
||||||
|
<version>1.1</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
|
<artifactId>tomcat-embed-core</artifactId>
|
||||||
|
<version>${tomcat.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
|
<artifactId>tomcat-embed-websocket</artifactId>
|
||||||
|
<version>${tomcat.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
|
<artifactId>tomcat-embed-el</artifactId>
|
||||||
|
<version>${tomcat.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
|
<artifactId>tomcat-embed-jasper</artifactId>
|
||||||
|
<version>${tomcat.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.nutz</groupId>
|
<groupId>org.nutz</groupId>
|
||||||
<artifactId>nutzboot-parent</artifactId>
|
<artifactId>nutzboot-parent</artifactId>
|
||||||
@@ -151,6 +183,7 @@
|
|||||||
<easypoi.version>4.5.0</easypoi.version>
|
<easypoi.version>4.5.0</easypoi.version>
|
||||||
<jsoup.veision>1.15.3</jsoup.veision>
|
<jsoup.veision>1.15.3</jsoup.veision>
|
||||||
<mysql-connector-java.version>8.0.28</mysql-connector-java.version>
|
<mysql-connector-java.version>8.0.28</mysql-connector-java.version>
|
||||||
|
<tomcat.version>9.0.121</tomcat.version>
|
||||||
<sa-token.version>1.25.0</sa-token.version>
|
<sa-token.version>1.25.0</sa-token.version>
|
||||||
<easy-captcha.version>1.6.2</easy-captcha.version>
|
<easy-captcha.version>1.6.2</easy-captcha.version>
|
||||||
<emoji-java.veision>4.0.0</emoji-java.veision>
|
<emoji-java.veision>4.0.0</emoji-java.veision>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<nutzboot.version>2.6.0-SNAPSHOT</nutzboot.version>
|
<nutzboot.version>2.6.0-SNAPSHOT</nutzboot.version>
|
||||||
<nutz.version>1.r.70-SNAPSHOT</nutz.version>
|
<nutz.version>1.r.70-SNAPSHOT</nutz.version>
|
||||||
|
<tomcat.version>9.0.121</tomcat.version>
|
||||||
<mysql-connector-java.version>8.0.28</mysql-connector-java.version>
|
<mysql-connector-java.version>8.0.28</mysql-connector-java.version>
|
||||||
<jaxb-api.version>2.3.1</jaxb-api.version>
|
<jaxb-api.version>2.3.1</jaxb-api.version>
|
||||||
<slf4j.version>2.0.8</slf4j.version>
|
<slf4j.version>2.0.8</slf4j.version>
|
||||||
@@ -144,8 +145,22 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.nutz</groupId>
|
<groupId>org.nutz</groupId>
|
||||||
<artifactId>nutzboot-starter-jetty</artifactId>
|
<artifactId>nutzboot-starter-tomcat</artifactId>
|
||||||
<version>${nutzboot.version}</version>
|
<version>${nutzboot.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.gandon.tomcat</groupId>
|
||||||
|
<artifactId>juli-to-slf4j</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
|
<artifactId>tomcat-embed-websocket</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nutz</groupId>
|
||||||
|
<artifactId>nutz-plugins-websocket</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.nutz</groupId>
|
<groupId>org.nutz</groupId>
|
||||||
@@ -424,6 +439,40 @@
|
|||||||
</dependencies>
|
</dependencies>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- Servlet API 由 Tomcat 提供,防止旧 API 混入可执行 JAR。 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<version>4.0.1</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.websocket</groupId>
|
||||||
|
<artifactId>javax.websocket-api</artifactId>
|
||||||
|
<version>1.1</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- 显式覆盖导入的 NutzBoot BOM,避免回退到其默认 Tomcat 8.5。 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
|
<artifactId>tomcat-embed-core</artifactId>
|
||||||
|
<version>${tomcat.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
|
<artifactId>tomcat-embed-websocket</artifactId>
|
||||||
|
<version>${tomcat.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
|
<artifactId>tomcat-embed-el</artifactId>
|
||||||
|
<version>${tomcat.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
|
<artifactId>tomcat-embed-jasper</artifactId>
|
||||||
|
<version>${tomcat.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.nutz</groupId>
|
<groupId>org.nutz</groupId>
|
||||||
<artifactId>nutzboot-parent</artifactId>
|
<artifactId>nutzboot-parent</artifactId>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.budwk.app.web.commons.auth.satoken.SaTokenDaoRedisImpl;
|
|||||||
import com.budwk.app.web.commons.auth.satoken.StpInterfaceImpl;
|
import com.budwk.app.web.commons.auth.satoken.StpInterfaceImpl;
|
||||||
import com.budwk.app.web.commons.base.Globals;
|
import com.budwk.app.web.commons.base.Globals;
|
||||||
import com.budwk.app.web.commons.ext.pubsub.WebPubSub;
|
import com.budwk.app.web.commons.ext.pubsub.WebPubSub;
|
||||||
|
import com.budwk.app.web.commons.ext.handler.WkTomcatConfiguration;
|
||||||
import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService;
|
import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.beetl.core.GroupTemplate;
|
import org.beetl.core.GroupTemplate;
|
||||||
@@ -65,6 +66,7 @@ public class MainLauncher {
|
|||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
NbApp nb = new NbApp().setArgs(args).setPrintProcDoc(true);
|
NbApp nb = new NbApp().setArgs(args).setPrintProcDoc(true);
|
||||||
nb.getAppContext().setMainPackage("com.budwk");
|
nb.getAppContext().setMainPackage("com.budwk");
|
||||||
|
nb.addListener(new WkTomcatConfiguration());
|
||||||
nb.run();
|
nb.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,15 @@ public class FlowTodoCenterController {
|
|||||||
return Result.success(pagination);
|
return Result.success(pagination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询当前用户的已办任务,仅返回列表展示及详情跳转所需的信息。
|
||||||
|
*
|
||||||
|
* @param pageNumber 查询页码
|
||||||
|
* @param pageSize 每页记录数
|
||||||
|
* @param searchKeyword 流程名称查询关键字
|
||||||
|
* @param category 流程分类
|
||||||
|
* @return 已办任务分页结果,不包含完整流程变量和任务表单数据
|
||||||
|
*/
|
||||||
@At
|
@At
|
||||||
@SaCheckLogin
|
@SaCheckLogin
|
||||||
public Result done(Integer pageNumber, Integer pageSize, String searchKeyword, String category) {
|
public Result done(Integer pageNumber, Integer pageSize, String searchKeyword, String category) {
|
||||||
@@ -106,8 +115,13 @@ public class FlowTodoCenterController {
|
|||||||
t.h5FormKey,
|
t.h5FormKey,
|
||||||
t.createdAt,
|
t.createdAt,
|
||||||
t.finishTime,
|
t.finishTime,
|
||||||
t.variable AS taskVariable,
|
-- 保留任务变量字段类型,但已办列表不返回其中的业务表单数据。
|
||||||
ins.variable,
|
'{}' AS taskVariable,
|
||||||
|
-- 列表仅需流程名称和申请人,避免完整变量中的身份证等信息泄露。
|
||||||
|
JSON_OBJECT(
|
||||||
|
'instanceName', ins.variable -> '$.instanceName',
|
||||||
|
'initiatorName', ins.variable -> '$.initiatorName'
|
||||||
|
) AS variable,
|
||||||
ins.state AS instanceState,
|
ins.state AS instanceState,
|
||||||
ins.id AS instanceId,
|
ins.id AS instanceId,
|
||||||
ins.businessNo,
|
ins.businessNo,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.budwk.app.flow.entity.ProcessCcInstance;
|
|||||||
import com.budwk.app.flow.entity.ProcessDefine;
|
import com.budwk.app.flow.entity.ProcessDefine;
|
||||||
import com.budwk.app.flow.entity.ProcessInstance;
|
import com.budwk.app.flow.entity.ProcessInstance;
|
||||||
import com.budwk.app.flow.entity.ProcessTask;
|
import com.budwk.app.flow.entity.ProcessTask;
|
||||||
|
import com.budwk.app.flow.entity.ProcessTaskActor;
|
||||||
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
|
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
|
||||||
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
|
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
|
||||||
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
||||||
@@ -556,11 +557,18 @@ public class ProcessInstanceServiceImpl extends BaseServiceImpl<ProcessInstance>
|
|||||||
public void deleteProcessInstanceById(Long processInstanceId) {
|
public void deleteProcessInstanceById(Long processInstanceId) {
|
||||||
List<ProcessTask> taskList = dao().query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId));
|
List<ProcessTask> taskList = dao().query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId));
|
||||||
List<Long> taskIds = taskList.stream().map(ProcessTask::getId).toList();
|
List<Long> taskIds = taskList.stream().map(ProcessTask::getId).toList();
|
||||||
dao().clear(ProcessTask.class, Cnd.where(ProcessTask::getId, "in", taskIds));
|
// 任务参与人依赖流程任务,删除流程时先清理参与人,避免遗留无法回查所属流程的孤立记录。
|
||||||
|
if (CollectionUtil.isNotEmpty(taskIds)) {
|
||||||
|
dao().clear(ProcessTaskActor.class, Cnd.where(ProcessTaskActor::getProcessTaskId, "in", taskIds));
|
||||||
|
dao().clear(ProcessTask.class, Cnd.where(ProcessTask::getId, "in", taskIds));
|
||||||
|
}
|
||||||
|
// 抄送记录直接依赖流程实例,需要与实例一并清理。
|
||||||
|
dao().clear(ProcessCcInstance.class, Cnd.where(ProcessCcInstance::getProcessInstanceId, "=", processInstanceId));
|
||||||
dao().clear(ProcessInstance.class, Cnd.where(ProcessInstance::getId, "=", processInstanceId));
|
dao().clear(ProcessInstance.class, Cnd.where(ProcessInstance::getId, "=", processInstanceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public void deleteProcessInstanceByBusinessKey(String businessKey) {
|
public void deleteProcessInstanceByBusinessKey(String businessKey) {
|
||||||
ProcessInstance processInstance = fetch(Cnd.where(ProcessInstance::getBusinessNo, "=", businessKey));
|
ProcessInstance processInstance = fetch(Cnd.where(ProcessInstance::getBusinessNo, "=", businessKey));
|
||||||
if (processInstance != null) {
|
if (processInstance != null) {
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ import org.nutz.mvc.upload.TempFile;
|
|||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -43,6 +47,9 @@ import java.nio.file.StandardOpenOption;
|
|||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -54,6 +61,9 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
|||||||
static String IMG_BASE64_PATTERN = "<img\\s+[^>]*src\\s*=\\s*['\"](data:image/[^'\"]+;base64,[^'\"]+)['\"][^>]*>";
|
static String IMG_BASE64_PATTERN = "<img\\s+[^>]*src\\s*=\\s*['\"](data:image/[^'\"]+;base64,[^'\"]+)['\"][^>]*>";
|
||||||
private static final int DOWNLOAD_FILE_CACHE_LIMIT = 2000;
|
private static final int DOWNLOAD_FILE_CACHE_LIMIT = 2000;
|
||||||
private final Map<String, Sys_file> downloadFileCache = new ConcurrentHashMap<>();
|
private final Map<String, Sys_file> downloadFileCache = new ConcurrentHashMap<>();
|
||||||
|
private static final Set<String> UPLOAD_SUFFIXES = Set.of("gif", "jpg", "jpeg", "png", "doc", "docx",
|
||||||
|
"xls", "xlsx", "rar", "zip", "7z", "txt", "pdf", "pptx", "ppt", "mp4");
|
||||||
|
private static final Set<String> ACTIVE_SUFFIXES = Set.of("svg", "svgz", "html", "htm", "xhtml", "xml", "js", "mjs", "swf");
|
||||||
|
|
||||||
public SysFileServiceImpl(Dao dao) {
|
public SysFileServiceImpl(Dao dao) {
|
||||||
super(dao);
|
super(dao);
|
||||||
@@ -81,11 +91,13 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void download(String id, HttpServletRequest request, HttpServletResponse response) throws IOException {
|
public void download(String id, HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||||
if (handleCachedFileRequest(id, request, response)) {
|
response.setHeader("X-Content-Type-Options", "nosniff");
|
||||||
return;
|
response.setHeader("Cache-Control", "private, no-store");
|
||||||
}
|
|
||||||
Sys_file sys_file = fetchDownloadFile(id);
|
Sys_file sys_file = fetchDownloadFile(id);
|
||||||
if (!ObjectUtil.isEmpty(sys_file) && handleCachedImageRequest(sys_file, request, response)) {
|
// 历史主动内容也必须在缓存协商前拦截,避免旧 SVG 继续在业务域下执行。
|
||||||
|
if (sys_file != null && (ACTIVE_SUFFIXES.contains(StrUtil.blankToDefault(sys_file.getSuffix(), "").toLowerCase(Locale.ROOT))
|
||||||
|
|| ACTIVE_SUFFIXES.contains(FileUtil.extName(sys_file.getName()).toLowerCase(Locale.ROOT)))) {
|
||||||
|
response.sendError(HttpServletResponse.SC_FORBIDDEN, "不允许访问此类型的附件");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ObjectUtil.isEmpty(sys_file)) {
|
if (ObjectUtil.isEmpty(sys_file)) {
|
||||||
@@ -112,16 +124,6 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleCachedFileRequest(String id, HttpServletRequest request, HttpServletResponse response) {
|
|
||||||
String etag = buildFileEtag(id);
|
|
||||||
if (etag.equals(request.getHeader("If-None-Match"))) {
|
|
||||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
|
||||||
setImageCacheHeaders(response, etag);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Sys_file fetchDownloadFile(String id) {
|
private Sys_file fetchDownloadFile(String id) {
|
||||||
if (StrUtil.isBlank(id)) {
|
if (StrUtil.isBlank(id)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -140,23 +142,15 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
|||||||
return sysFile;
|
return sysFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleCachedImageRequest(Sys_file sysFile, HttpServletRequest request, HttpServletResponse response) {
|
|
||||||
if (!isImage(sysFile)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
String etag = buildFileEtag(sysFile);
|
|
||||||
if (etag.equals(request.getHeader("If-None-Match"))) {
|
|
||||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
|
||||||
setImageCacheHeaders(response, etag);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean writeImageResponse(Sys_file sysFile, byte[] bytes, HttpServletResponse response) throws IOException {
|
private boolean writeImageResponse(Sys_file sysFile, byte[] bytes, HttpServletResponse response) throws IOException {
|
||||||
if (!isImage(sysFile)) {
|
if (!isImage(sysFile)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
String suffix = StrUtil.blankToDefault(sysFile.getSuffix(), FileUtil.extName(sysFile.getName())).toLowerCase(Locale.ROOT);
|
||||||
|
if (!isValidImage(bytes, suffix)) {
|
||||||
|
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "图片内容与文件类型不符");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
setImageCacheHeaders(response, buildFileEtag(sysFile));
|
setImageCacheHeaders(response, buildFileEtag(sysFile));
|
||||||
response.setHeader("Content-Disposition", "inline;filename=" + java.net.URLEncoder.encode(sysFile.getName(), java.nio.charset.StandardCharsets.UTF_8));
|
response.setHeader("Content-Disposition", "inline;filename=" + java.net.URLEncoder.encode(sysFile.getName(), java.nio.charset.StandardCharsets.UTF_8));
|
||||||
response.setHeader("Content-Length", String.valueOf(bytes.length));
|
response.setHeader("Content-Length", String.valueOf(bytes.length));
|
||||||
@@ -166,9 +160,9 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setImageCacheHeaders(HttpServletResponse response, String etag) {
|
private void setImageCacheHeaders(HttpServletResponse response, String etag) {
|
||||||
response.setHeader("Cache-Control", "public, max-age=604800, immutable");
|
response.setHeader("Cache-Control", "private, no-cache");
|
||||||
response.setHeader("ETag", etag);
|
response.setHeader("ETag", etag);
|
||||||
response.setDateHeader("Expires", System.currentTimeMillis() + 604800000L);
|
response.setDateHeader("Expires", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildFileEtag(Sys_file sysFile) {
|
private String buildFileEtag(Sys_file sysFile) {
|
||||||
@@ -176,12 +170,12 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String buildFileEtag(String id) {
|
private String buildFileEtag(String id) {
|
||||||
return "\"" + id + "\"";
|
return "\"safe-" + id + "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isImage(Sys_file sysFile) {
|
private boolean isImage(Sys_file sysFile) {
|
||||||
String suffix = StrUtil.blankToDefault(sysFile.getSuffix(), FileUtil.extName(sysFile.getName())).toLowerCase();
|
String suffix = StrUtil.blankToDefault(sysFile.getSuffix(), FileUtil.extName(sysFile.getName())).toLowerCase();
|
||||||
return "jpg".equals(suffix) || "jpeg".equals(suffix) || "png".equals(suffix) || "gif".equals(suffix) || "webp".equals(suffix) || "bmp".equals(suffix) || "svg".equals(suffix);
|
return "jpg".equals(suffix) || "jpeg".equals(suffix) || "png".equals(suffix) || "gif".equals(suffix) || "webp".equals(suffix) || "bmp".equals(suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getImageContentType(Sys_file sysFile) {
|
private String getImageContentType(Sys_file sysFile) {
|
||||||
@@ -192,7 +186,6 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
|||||||
case "gif" -> "image/gif";
|
case "gif" -> "image/gif";
|
||||||
case "webp" -> "image/webp";
|
case "webp" -> "image/webp";
|
||||||
case "bmp" -> "image/bmp";
|
case "bmp" -> "image/bmp";
|
||||||
case "svg" -> "image/svg+xml";
|
|
||||||
default -> "application/octet-stream";
|
default -> "application/octet-stream";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -305,6 +298,7 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
|||||||
* 存储文件
|
* 存储文件
|
||||||
**/
|
**/
|
||||||
private String storageFile(String engine, TempFile file, boolean returnFileId) {
|
private String storageFile(String engine, TempFile file, boolean returnFileId) {
|
||||||
|
validateUpload(file);
|
||||||
// 如果引擎为空,默认使用本地
|
// 如果引擎为空,默认使用本地
|
||||||
if (ObjectUtil.isEmpty(engine)) {
|
if (ObjectUtil.isEmpty(engine)) {
|
||||||
engine = SysFileEngineTypeEnum.LOCAL.getValue();
|
engine = SysFileEngineTypeEnum.LOCAL.getValue();
|
||||||
@@ -378,6 +372,53 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 所有业务上传共用校验,客户端声明的 MIME 类型不能作为图片真实性依据。 */
|
||||||
|
private void validateUpload(TempFile file) {
|
||||||
|
if (file == null || StrUtil.isBlank(file.getSubmittedFileName())) {
|
||||||
|
throw new BaseException("请选择要上传的文件");
|
||||||
|
}
|
||||||
|
String suffix = FileUtil.extName(file.getSubmittedFileName()).toLowerCase(Locale.ROOT);
|
||||||
|
if (!UPLOAD_SUFFIXES.contains(suffix)) {
|
||||||
|
throw new BaseException("不允许上传此类型的文件");
|
||||||
|
}
|
||||||
|
if (Set.of("gif", "jpg", "jpeg", "png").contains(suffix)) {
|
||||||
|
try {
|
||||||
|
if (!isValidImage(Files.readAllBytes(file.getFile().toPath()), suffix)) {
|
||||||
|
throw new BaseException("图片内容与文件类型不符");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new BaseException("图片读取失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 解码真实图片并限制像素数量,阻止伪装为图片的 SVG 和异常大尺寸图片。 */
|
||||||
|
private boolean isValidImage(byte[] bytes, String suffix) {
|
||||||
|
try (ImageInputStream input = ImageIO.createImageInputStream(new ByteArrayInputStream(bytes))) {
|
||||||
|
if (input == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
||||||
|
if (!readers.hasNext()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ImageReader reader = readers.next();
|
||||||
|
try {
|
||||||
|
reader.setInput(input, true, true);
|
||||||
|
String expected = "jpg".equals(suffix) ? "jpeg" : suffix;
|
||||||
|
if (!expected.equalsIgnoreCase(reader.getFormatName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long pixels = (long) reader.getWidth(0) * reader.getHeight(0);
|
||||||
|
return pixels > 0 && pixels <= 40000000 && reader.read(0) != null;
|
||||||
|
} finally {
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
|
} catch (IOException | RuntimeException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 存储文件
|
* 存储文件
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import cn.dev33.satoken.servlet.model.SaStorageForServlet;
|
|||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
import org.nutz.mvc.Mvcs;
|
import org.nutz.mvc.Mvcs;
|
||||||
|
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletResponseWrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author wizzer@qq.com
|
* @author wizzer@qq.com
|
||||||
*/
|
*/
|
||||||
@@ -22,7 +25,20 @@ public class SaTokenContextImpl implements SaTokenContext {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SaResponse getResponse() {
|
public SaResponse getResponse() {
|
||||||
return new SaResponseForServlet(Mvcs.getResp());
|
// 当前 Sa-Token 版本没有 Cookie 安全属性配置,在写入登录 Cookie 时统一补齐。
|
||||||
|
return new SaResponseForServlet(new HttpServletResponseWrapper(Mvcs.getResp()) {
|
||||||
|
/**
|
||||||
|
* 限制登录 Cookie 的脚本访问,并在 HTTPS 请求中仅允许安全传输。
|
||||||
|
*
|
||||||
|
* @param cookie Sa-Token 写入或清除的 Cookie
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addCookie(Cookie cookie) {
|
||||||
|
cookie.setHttpOnly(true);
|
||||||
|
cookie.setSecure(cookie.getSecure() || Mvcs.getReq().isSecure());
|
||||||
|
super.addCookie(cookie);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ import java.lang.reflect.Method;
|
|||||||
public class SaCheckLoginInterceptor implements MethodInterceptor {
|
public class SaCheckLoginInterceptor implements MethodInterceptor {
|
||||||
@Override
|
@Override
|
||||||
public void filter(InterceptorChain chain) throws Throwable {
|
public void filter(InterceptorChain chain) throws Throwable {
|
||||||
|
if (SaTokenAuthIgnoreUtil.isIgnore()) {
|
||||||
|
chain.doChain();
|
||||||
|
return;
|
||||||
|
}
|
||||||
Method method = chain.getCallingMethod();
|
Method method = chain.getCallingMethod();
|
||||||
SaCheckLogin at = method.getAnnotation(SaCheckLogin.class);
|
SaCheckLogin at = method.getAnnotation(SaCheckLogin.class);
|
||||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||||
|
|||||||
+4
@@ -15,6 +15,10 @@ import java.lang.reflect.Method;
|
|||||||
public class SaCheckPermissionInterceptor implements MethodInterceptor {
|
public class SaCheckPermissionInterceptor implements MethodInterceptor {
|
||||||
@Override
|
@Override
|
||||||
public void filter(InterceptorChain chain) throws Throwable {
|
public void filter(InterceptorChain chain) throws Throwable {
|
||||||
|
if (SaTokenAuthIgnoreUtil.isIgnore()) {
|
||||||
|
chain.doChain();
|
||||||
|
return;
|
||||||
|
}
|
||||||
Method method = chain.getCallingMethod();
|
Method method = chain.getCallingMethod();
|
||||||
SaCheckPermission at = method.getAnnotation(SaCheckPermission.class);
|
SaCheckPermission at = method.getAnnotation(SaCheckPermission.class);
|
||||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ import java.lang.reflect.Method;
|
|||||||
public class SaCheckRoleInterceptor implements MethodInterceptor {
|
public class SaCheckRoleInterceptor implements MethodInterceptor {
|
||||||
@Override
|
@Override
|
||||||
public void filter(InterceptorChain chain) throws Throwable {
|
public void filter(InterceptorChain chain) throws Throwable {
|
||||||
|
if (SaTokenAuthIgnoreUtil.isIgnore()) {
|
||||||
|
chain.doChain();
|
||||||
|
return;
|
||||||
|
}
|
||||||
Method method = chain.getCallingMethod();
|
Method method = chain.getCallingMethod();
|
||||||
SaCheckRole at = method.getAnnotation(SaCheckRole.class);
|
SaCheckRole at = method.getAnnotation(SaCheckRole.class);
|
||||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.budwk.app.web.commons.auth.satoken.aop;
|
||||||
|
|
||||||
|
import com.budwk.app.web.commons.base.Globals;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.lang.Strings;
|
||||||
|
import org.nutz.mvc.Mvcs;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sa-Token 注解认证白名单工具。
|
||||||
|
* <p>
|
||||||
|
* 用于集中维护不需要登录、权限、角色校验即可访问的 URL,匹配规则与旧 Shiro 体系中的
|
||||||
|
* authIgnoreUrlArr 保持一致:数组中的每一项都按正则表达式与当前请求 URI 完整匹配。
|
||||||
|
*/
|
||||||
|
public class SaTokenAuthIgnoreUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不需要认证授权的 URL。
|
||||||
|
* <p>
|
||||||
|
* 参数说明:数组中的字符串为请求 URI 正则表达式,匹配的是去除项目 contextPath 后的路径。
|
||||||
|
* 返回值说明:通过 {@link #isIgnore()} 返回 boolean,true 表示当前请求跳过 Sa-Token 注解校验。
|
||||||
|
*/
|
||||||
|
private static final String[] AUTH_IGNORE_URL_ARR = Lang.array("/",
|
||||||
|
"/platform/activity/basic/scope/getScopeUser",
|
||||||
|
"/platform/fitnessWalk/stepManage/getActivityDateStep",
|
||||||
|
"/platform/fitnessWalk/stepManage/winningRecord",
|
||||||
|
"/platform/fitnessWalk/stepManage/updateStepMonth",
|
||||||
|
"/platform/fitnessWalk/stepRanking/getUserStepRanking",
|
||||||
|
"/platform/fitnessWalk/stepManage/getActivityQualifyProgressBar",
|
||||||
|
"/platform/fitnessWalk/stepWining/getUserStepWining",
|
||||||
|
"/platform/fitnessWalk/common/getIssueById",
|
||||||
|
"/platform/fitnessWalk/common/saveAnswer",
|
||||||
|
"/platform/fitnessWalk/stepWining/prizeOption",
|
||||||
|
"/platform/fitnessWalk/stepWining/prizeUsers",
|
||||||
|
"/platform/fitnessWalk/punchLottery/judgeWinningToPunch",
|
||||||
|
"/platform/fitnessWalk/punchLottery/getLotteryRecord",
|
||||||
|
"/platform/fitnessWalk/punchLottery/doExchange",
|
||||||
|
"/platform/fitnessWalk/punchLottery/getPunchWining",
|
||||||
|
"/platform/fitnessWalk/punchLottery/doReadLottery",
|
||||||
|
"/platform/fitnessWalk/activityRelation/isCurrentUserRelationActivityQualified",
|
||||||
|
"/platform/fitnessWalk/welfare/addWelfareList",
|
||||||
|
"/platform/fitnessWalk/weapp/.*");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前请求是否命中匿名访问白名单。
|
||||||
|
*
|
||||||
|
* @return boolean true 表示当前 URL 不需要执行登录、权限、角色注解校验;false 表示继续走原有 Sa-Token 校验逻辑
|
||||||
|
*/
|
||||||
|
public static boolean isIgnore() {
|
||||||
|
HttpServletRequest request = Mvcs.getReq();
|
||||||
|
if (request == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String requestURI = Strings.sNull(request.getRequestURI()).replaceFirst("^" + Pattern.quote(Strings.sNull(Globals.AppBase)), "");
|
||||||
|
return Arrays.stream(AUTH_IGNORE_URL_ARR).anyMatch(v -> Pattern.compile(v).matcher(requestURI).matches());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.budwk.app.web.commons.ext.handler;
|
package com.budwk.app.web.commons.ext.handler;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.nutz.boot.starter.WebServletFace;
|
||||||
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
|
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
import org.nutz.json.Json;
|
import org.nutz.json.Json;
|
||||||
import org.nutz.lang.util.NutMap;
|
import org.nutz.lang.util.NutMap;
|
||||||
@@ -9,28 +8,63 @@ import org.nutz.log.Log;
|
|||||||
import org.nutz.log.Logs;
|
import org.nutz.log.Logs;
|
||||||
|
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
|
import javax.servlet.DispatcherType;
|
||||||
|
import javax.servlet.Servlet;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 错误页拦截器,登陆后台显示友好提示
|
* 错误页拦截器,登陆后台显示友好提示
|
||||||
*/
|
*/
|
||||||
@IocBean
|
@IocBean
|
||||||
public class WkErrorPageHandler extends ErrorPageErrorHandler {
|
public class WkErrorPageHandler extends HttpServlet implements WebServletFace {
|
||||||
private static final Log log = Logs.get();
|
private static final Log log = Logs.get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回容器错误处理 Servlet 的注册名称。
|
||||||
|
* @return 独立于业务路由的 Servlet 名称
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
public String getName() {
|
||||||
|
return "wkErrorPage";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回容器错误页的内部派发地址。
|
||||||
|
* @return 错误页映射路径
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getPathSpec() {
|
||||||
|
return "/__container_error";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将当前错误处理实例交给 NutzBoot 注册。
|
||||||
|
* @return 当前 Servlet 实例
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Servlet getServlet() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||||
|
if (request.getDispatcherType() != DispatcherType.ERROR) {
|
||||||
|
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (response.getStatus() == 403 || response.getStatus() == 404 || response.getStatus() == 500) {
|
if (response.getStatus() == 403 || response.getStatus() == 404 || response.getStatus() == 500) {
|
||||||
try {
|
try {
|
||||||
if (isAjax(request)) {
|
if (isAjax(request)) {
|
||||||
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
response.getWriter().write(Json.toJson(new NutMap("code", "-1").setv("msg", response.getStatus() + " error")));
|
response.getWriter().write(Json.toJson(new NutMap("code", "-1").setv("msg", response.getStatus() + " error")));
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
request.setAttribute("original_request_uri", request.getRequestURI());
|
request.setAttribute("original_request_uri", request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI));
|
||||||
RequestDispatcher rd = request.getRequestDispatcher("/platform/home/" + response.getStatus());
|
RequestDispatcher rd = request.getRequestDispatcher("/platform/home/" + response.getStatus());
|
||||||
rd.forward(request, response);
|
rd.forward(request, response);
|
||||||
return;
|
return;
|
||||||
@@ -39,11 +73,11 @@ public class WkErrorPageHandler extends ErrorPageErrorHandler {
|
|||||||
log.error(e);
|
log.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.handle(target, baseRequest, request, response);
|
// 不回落到容器默认错误页,避免泄露异常堆栈和容器信息。
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAjax(ServletRequest req) {
|
private boolean isAjax(ServletRequest req) {
|
||||||
String value = ((HttpServletRequest) req).getHeader("X-Requested-With");
|
String value = ((HttpServletRequest) req).getHeader("X-Requested-With");
|
||||||
return value != null && "XMLHttpRequest".equalsIgnoreCase(value.trim());
|
return value != null && "XMLHttpRequest".equalsIgnoreCase(value.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.budwk.app.web.commons.ext.handler;
|
||||||
|
|
||||||
|
import com.budwk.app.web.commons.ext.websocket.WkWebSocket;
|
||||||
|
import org.apache.catalina.core.StandardContext;
|
||||||
|
import org.apache.catalina.Executor;
|
||||||
|
import org.apache.catalina.core.StandardThreadExecutor;
|
||||||
|
import org.apache.catalina.startup.Tomcat;
|
||||||
|
import org.apache.catalina.valves.ErrorReportValve;
|
||||||
|
import org.apache.tomcat.websocket.server.WsSci;
|
||||||
|
import org.apache.tomcat.util.descriptor.web.FilterMap;
|
||||||
|
import org.nutz.boot.NbApp;
|
||||||
|
import org.nutz.boot.starter.tomcat.TomcatStarter;
|
||||||
|
import org.nutz.boot.tools.NbAppEventListener;
|
||||||
|
import org.nutz.ioc.impl.PropertiesProxy;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.servlet.ServletContextEvent;
|
||||||
|
import javax.servlet.ServletContextListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在容器启动前补齐 NutzBoot Tomcat 的 WebSocket 注册和线程池绑定。
|
||||||
|
*/
|
||||||
|
public class WkTomcatConfiguration implements NbAppEventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置官方嵌入式 Tomcat,保留项目的监听地址与消息推送能力。
|
||||||
|
*
|
||||||
|
* @param app 当前 NutzBoot 应用
|
||||||
|
* @param eventType 只在服务器启动前配置,避免重复注册端点
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void whenStartServers(NbApp app, EventType eventType) {
|
||||||
|
if (eventType != EventType.before) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TomcatStarter starter = app.getAppContext().getIoc().get(TomcatStarter.class);
|
||||||
|
Tomcat tomcat = starter.getServer();
|
||||||
|
PropertiesProxy conf = app.getAppContext().getConf();
|
||||||
|
tomcat.getConnector().setProperty("address", starter.getHost());
|
||||||
|
|
||||||
|
// Starter 创建了共享线程池,但尚未把它交给 Connector 使用。
|
||||||
|
for (Executor executor : tomcat.getService().findExecutors()) {
|
||||||
|
if (executor instanceof StandardThreadExecutor) {
|
||||||
|
StandardThreadExecutor threads = (StandardThreadExecutor) executor;
|
||||||
|
threads.setMinSpareThreads(conf.getInt("tomcat.executor.minSpareThreads", 10));
|
||||||
|
threads.setMaxIdleTime(conf.getInt("tomcat.executor.maxIdleTime", 60000));
|
||||||
|
tomcat.getConnector().getProtocolHandler().setExecutor(threads);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NutzBoot 使用编程式 Context,必须显式运行官方 WebSocket 初始化器。
|
||||||
|
StandardContext context = (StandardContext) tomcat.getHost().findChild(starter.getContextPath());
|
||||||
|
context.setUseHttpOnly(conf.getBoolean("tomcat.session.cookie.httponly", true));
|
||||||
|
context.addServletContainerInitializer(new WsSci(), Set.of(WkWebSocket.class));
|
||||||
|
// Tomcat 的错误页再次 forward 时仍保持 ERROR 派发;让 MVC 渲染原业务错误页。
|
||||||
|
// Nutz 的过滤器在前一个监听器中注册,须等其完成后才能添加映射。
|
||||||
|
context.addApplicationLifecycleListener(new ServletContextListener() {
|
||||||
|
/**
|
||||||
|
* 在 Nutz 注册完成后补充业务错误页的 ERROR 派发映射。
|
||||||
|
* @param event 当前 Servlet 上下文初始化事件
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void contextInitialized(ServletContextEvent event) {
|
||||||
|
FilterMap errorPages = new FilterMap();
|
||||||
|
errorPages.setFilterName("nutz");
|
||||||
|
errorPages.setDispatcher("ERROR");
|
||||||
|
// ERROR 再转发时 URL 匹配路径不变,按目标 Servlet 匹配才能进入 MVC。
|
||||||
|
errorPages.addServletName("default");
|
||||||
|
context.addFilterMap(errorPages);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Host 的兜底错误页也不能重新暴露此前已关闭的版本和堆栈。
|
||||||
|
ErrorReportValve errorReport = null;
|
||||||
|
for (org.apache.catalina.Valve valve : tomcat.getHost().getPipeline().getValves()) {
|
||||||
|
if (valve instanceof ErrorReportValve) {
|
||||||
|
errorReport = (ErrorReportValve) valve;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errorReport == null) {
|
||||||
|
errorReport = new ErrorReportValve();
|
||||||
|
tomcat.getHost().getPipeline().addValve(errorReport);
|
||||||
|
}
|
||||||
|
errorReport.setShowReport(false);
|
||||||
|
errorReport.setShowServerInfo(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-18
@@ -162,27 +162,10 @@ public class ActivityWorksCollectionManageController {
|
|||||||
@SaCheckPermission(value = {"activity.workscollection.manage", "activity.workscollection.new"}, mode = SaMode.OR)
|
@SaCheckPermission(value = {"activity.workscollection.manage", "activity.workscollection.new"}, mode = SaMode.OR)
|
||||||
@Aop(TransAop.READ_COMMITTED)
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public Result update(@Param("data") @Valid Activity_works_collection worksCollection) {
|
public Result update(@Param("data") @Valid Activity_works_collection worksCollection) {
|
||||||
String validateMessage = activityWorksCollectionService.validateReferencedTypesBeforeUpdate(worksCollection);
|
String validateMessage = activityWorksCollectionService.updatePreservingTypeIds(worksCollection);
|
||||||
if (StrUtil.isNotBlank(validateMessage)) {
|
if (StrUtil.isNotBlank(validateMessage)) {
|
||||||
return Result.error(validateMessage);
|
return Result.error(validateMessage);
|
||||||
}
|
}
|
||||||
dao.update(worksCollection);
|
|
||||||
dao.updateLinks(worksCollection, "subjectTypes");
|
|
||||||
dao.insertLinks(worksCollection, "subjectTypes");
|
|
||||||
//清除页面已经删除的
|
|
||||||
dao.clear(Activity_works_subjectType.class,
|
|
||||||
Cnd.where(Activity_works_subjectType::getActivityId, "=", worksCollection.getId())
|
|
||||||
.andEX("id", "not in", worksCollection.getSubjectTypes().stream().map(Activity_works_subjectType::getId).toList()));
|
|
||||||
|
|
||||||
worksCollection.getSubjectTypes().forEach(item -> {
|
|
||||||
dao.updateLinks(item, "worksTypes");
|
|
||||||
dao.insertLinks(item, "worksTypes");
|
|
||||||
//清除页面已经删除的
|
|
||||||
List<String> workIds = item.getWorksTypes().stream().map(Activity_works_worksType::getId).toList();
|
|
||||||
dao.clear(Activity_works_worksType.class,
|
|
||||||
Cnd.where(Activity_works_worksType::getId, "not in ", workIds)
|
|
||||||
.and(Activity_works_worksType::getSubjectId,"=",item.getId()));
|
|
||||||
});
|
|
||||||
syncHomeActivity(worksCollection);
|
syncHomeActivity(worksCollection);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -35,6 +35,14 @@ public interface ActivityWorksCollectionService extends BaseService<Activity_wor
|
|||||||
*/
|
*/
|
||||||
void cancelTemplate(String id);
|
void cancelTemplate(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新作品征集活动及其主题、作品类型,并保留所有原有类型ID。
|
||||||
|
*
|
||||||
|
* @param worksCollection 前端提交的活动及其主题、作品类型配置
|
||||||
|
* @return 更新成功返回 {@code null};数据层级异常或删除了投稿引用类型时返回提示信息
|
||||||
|
*/
|
||||||
|
String updatePreservingTypeIds(Activity_works_collection worksCollection);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验编辑活动时删除的主题类型、作品类型是否已被投稿引用。
|
* 校验编辑活动时删除的主题类型、作品类型是否已被投稿引用。
|
||||||
*
|
*
|
||||||
|
|||||||
+132
@@ -21,7 +21,9 @@ import org.nutz.ioc.aop.Aop;
|
|||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -116,6 +118,136 @@ public class ActivityWorksCollectionServiceImpl extends BaseServiceImpl<Activity
|
|||||||
Cnd.where("id", "=", id));
|
Cnd.where("id", "=", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 活动编辑采用显式新增、更新、删除,避免通用关联保存重新生成已有类型ID。
|
||||||
|
* 原有主题和作品类型必须携带数据库ID;只有页面新增的类型允许生成新ID。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
public String updatePreservingTypeIds(Activity_works_collection worksCollection) {
|
||||||
|
if (worksCollection == null || StrUtil.isBlank(worksCollection.getId())) {
|
||||||
|
return "活动数据不存在,请刷新页面后重试";
|
||||||
|
}
|
||||||
|
Activity_works_collection persistedActivity = dao.fetch(Activity_works_collection.class, worksCollection.getId());
|
||||||
|
if (persistedActivity == null) {
|
||||||
|
return "活动不存在或已被删除";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Activity_works_subjectType> submittedSubjectTypes = CollUtil.defaultIfEmpty(
|
||||||
|
worksCollection.getSubjectTypes(), Collections.emptyList());
|
||||||
|
List<Activity_works_subjectType> persistedSubjectTypes = dao.query(Activity_works_subjectType.class,
|
||||||
|
Cnd.where(Activity_works_subjectType::getActivityId, "=", worksCollection.getId()));
|
||||||
|
Map<String, Activity_works_subjectType> persistedSubjectMap = persistedSubjectTypes.stream()
|
||||||
|
.collect(Collectors.toMap(Activity_works_subjectType::getId, item -> item));
|
||||||
|
|
||||||
|
Set<String> submittedSubjectIds = new HashSet<>();
|
||||||
|
for (Activity_works_subjectType subjectType : submittedSubjectTypes) {
|
||||||
|
if (subjectType == null) {
|
||||||
|
return "主题类型数据不能为空";
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(subjectType.getId())) {
|
||||||
|
if (!persistedSubjectMap.containsKey(subjectType.getId())) {
|
||||||
|
return "主题类型数据已发生变化,请刷新页面后重新编辑";
|
||||||
|
}
|
||||||
|
if (!submittedSubjectIds.add(subjectType.getId())) {
|
||||||
|
return "主题类型数据重复,请刷新页面后重新编辑";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Activity_works_worksType> submittedWorksTypes = CollUtil.defaultIfEmpty(
|
||||||
|
subjectType.getWorksTypes(), Collections.emptyList());
|
||||||
|
if (StrUtil.isBlank(subjectType.getId())) {
|
||||||
|
boolean containsPersistedWorksType = submittedWorksTypes.stream()
|
||||||
|
.anyMatch(worksType -> worksType != null && StrUtil.isNotBlank(worksType.getId()));
|
||||||
|
if (containsPersistedWorksType) {
|
||||||
|
return "新增主题不能关联原有作品类型,请刷新页面后重新编辑";
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Activity_works_worksType> persistedWorksTypes = dao.query(Activity_works_worksType.class,
|
||||||
|
Cnd.where(Activity_works_worksType::getSubjectId, "=", subjectType.getId()));
|
||||||
|
Map<String, Activity_works_worksType> persistedWorksTypeMap = persistedWorksTypes.stream()
|
||||||
|
.collect(Collectors.toMap(Activity_works_worksType::getId, item -> item));
|
||||||
|
Set<String> submittedWorksIds = new HashSet<>();
|
||||||
|
for (Activity_works_worksType worksType : submittedWorksTypes) {
|
||||||
|
if (worksType == null) {
|
||||||
|
return "作品类型数据不能为空";
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(worksType.getId()) && !persistedWorksTypeMap.containsKey(worksType.getId())) {
|
||||||
|
return "作品类型数据已发生变化,请刷新页面后重新编辑";
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(worksType.getId()) && !submittedWorksIds.add(worksType.getId())) {
|
||||||
|
return "作品类型数据重复,请刷新页面后重新编辑";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String validateMessage = validateReferencedTypesBeforeUpdate(worksCollection);
|
||||||
|
if (StrUtil.isNotBlank(validateMessage)) {
|
||||||
|
return validateMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
dao.update(worksCollection);
|
||||||
|
for (Activity_works_subjectType subjectType : submittedSubjectTypes) {
|
||||||
|
subjectType.setActivityId(worksCollection.getId());
|
||||||
|
if (StrUtil.isBlank(subjectType.getId())) {
|
||||||
|
dao.insert(subjectType);
|
||||||
|
} else {
|
||||||
|
dao.update(subjectType);
|
||||||
|
}
|
||||||
|
submittedSubjectIds.add(subjectType.getId());
|
||||||
|
|
||||||
|
List<Activity_works_worksType> submittedWorksTypes = CollUtil.defaultIfEmpty(
|
||||||
|
subjectType.getWorksTypes(), Collections.emptyList());
|
||||||
|
Set<String> retainedWorksIds = new HashSet<>();
|
||||||
|
for (Activity_works_worksType worksType : submittedWorksTypes) {
|
||||||
|
worksType.setSubjectId(subjectType.getId());
|
||||||
|
if (StrUtil.isBlank(worksType.getId())) {
|
||||||
|
dao.insert(worksType);
|
||||||
|
} else {
|
||||||
|
dao.update(worksType);
|
||||||
|
}
|
||||||
|
retainedWorksIds.add(worksType.getId());
|
||||||
|
}
|
||||||
|
clearRemovedWorksTypes(subjectType.getId(), retainedWorksIds);
|
||||||
|
}
|
||||||
|
clearRemovedSubjectTypes(worksCollection.getId(), submittedSubjectIds);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除页面已移除且未被投稿引用的作品类型;空集合表示删除当前主题下全部原有类型。
|
||||||
|
*/
|
||||||
|
private void clearRemovedWorksTypes(String subjectId, Set<String> retainedWorksIds) {
|
||||||
|
Cnd cnd = Cnd.where(Activity_works_worksType::getSubjectId, "=", subjectId);
|
||||||
|
if (CollUtil.isNotEmpty(retainedWorksIds)) {
|
||||||
|
cnd.and(Activity_works_worksType::getId, "not in", retainedWorksIds);
|
||||||
|
}
|
||||||
|
dao.clear(Activity_works_worksType.class, cnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除页面已移除且未被投稿引用的主题类型;空集合表示删除当前活动全部原有主题。
|
||||||
|
*/
|
||||||
|
private void clearRemovedSubjectTypes(String activityId, Set<String> retainedSubjectIds) {
|
||||||
|
Cnd cnd = Cnd.where(Activity_works_subjectType::getActivityId, "=", activityId);
|
||||||
|
if (CollUtil.isNotEmpty(retainedSubjectIds)) {
|
||||||
|
cnd.and(Activity_works_subjectType::getId, "not in", retainedSubjectIds);
|
||||||
|
}
|
||||||
|
List<String> removedSubjectIds = dao.query(Activity_works_subjectType.class, cnd).stream()
|
||||||
|
.map(Activity_works_subjectType::getId)
|
||||||
|
.toList();
|
||||||
|
if (CollUtil.isEmpty(removedSubjectIds)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 先删除未被投稿引用主题下的作品类型,避免产生作品类型孤儿数据。
|
||||||
|
dao.clear(Activity_works_worksType.class,
|
||||||
|
Cnd.where(Activity_works_worksType::getSubjectId, "in", removedSubjectIds));
|
||||||
|
dao.clear(Activity_works_subjectType.class,
|
||||||
|
Cnd.where(Activity_works_subjectType::getId, "in", removedSubjectIds));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String validateReferencedTypesBeforeUpdate(Activity_works_collection worksCollection) {
|
public String validateReferencedTypesBeforeUpdate(Activity_works_collection worksCollection) {
|
||||||
if (worksCollection == null || StrUtil.isBlank(worksCollection.getId())) {
|
if (worksCollection == null || StrUtil.isBlank(worksCollection.getId())) {
|
||||||
|
|||||||
+4
@@ -218,6 +218,10 @@ public class ClubUserJoinApplyController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProcessInstance processInstance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", userApply.getId()));
|
ProcessInstance processInstance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", userApply.getId()));
|
||||||
|
// 申请记录存在但没有流程实例时,说明用户仅保存了申请草稿,需提示其先到“我的申请”继续提交。
|
||||||
|
if (processInstance == null) {
|
||||||
|
return Result.error(99, "您有该社团的申请记录尚未提交,请到我的申请里查看!");
|
||||||
|
}
|
||||||
if (!List.of(ProcessInstanceStateEnum.REJECT.getCode(), ProcessInstanceStateEnum.FINISHED.getCode()).contains(processInstance.getState())) {
|
if (!List.of(ProcessInstanceStateEnum.REJECT.getCode(), ProcessInstanceStateEnum.FINISHED.getCode()).contains(processInstance.getState())) {
|
||||||
return Result.error(99, "您有该社团的申请记录尚未完成,请到我的申请里查看!");
|
return Result.error(99, "您有该社团的申请记录尚未完成,请到我的申请里查看!");
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-2
@@ -11,6 +11,7 @@ import com.budwk.app.base.result.Result;
|
|||||||
import com.budwk.app.base.utils.PageUtil;
|
import com.budwk.app.base.utils.PageUtil;
|
||||||
import com.budwk.app.flow.constant.FlowConst;
|
import com.budwk.app.flow.constant.FlowConst;
|
||||||
import com.budwk.app.flow.engine.FlowEngine;
|
import com.budwk.app.flow.engine.FlowEngine;
|
||||||
|
import com.budwk.app.flow.entity.ProcessDefine;
|
||||||
import com.budwk.app.flow.entity.ProcessInstance;
|
import com.budwk.app.flow.entity.ProcessInstance;
|
||||||
import com.budwk.app.flow.entity.ProcessTask;
|
import com.budwk.app.flow.entity.ProcessTask;
|
||||||
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
|
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
|
||||||
@@ -95,6 +96,7 @@ public class ClubRefreshReportController {
|
|||||||
sys_club_refresh info
|
sys_club_refresh info
|
||||||
LEFT JOIN sys_club club ON club.id = info.clubId
|
LEFT JOIN sys_club club ON club.id = info.clubId
|
||||||
LEFT JOIN `wf_process_instance` ins ON ins.businessNo = info.id
|
LEFT JOIN `wf_process_instance` ins ON ins.businessNo = info.id
|
||||||
|
AND ins.processDefineId IN (SELECT id FROM wf_process_define WHERE name = 'XHHJBG')
|
||||||
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10
|
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10
|
||||||
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
|
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
|
||||||
$condition
|
$condition
|
||||||
@@ -176,10 +178,23 @@ public class ClubRefreshReportController {
|
|||||||
@ApiOperation("删除")
|
@ApiOperation("删除")
|
||||||
@Aop(TransAop.READ_COMMITTED)
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
@SaCheckPermission("club.infoManage.refreshReport")
|
@SaCheckPermission("club.infoManage.refreshReport")
|
||||||
@SLog(tag = "社团管理系统-信息管理", msg = "删除换届报告")
|
@SLog(tag = "社团管理系统-信息管理", msg = "删除换届报告")
|
||||||
public Result delete(@Param("id") String id) {
|
public Result delete(@Param("id") String id) {
|
||||||
|
// 历史换届记录可能与社团注册共用业务编号,必须按流程定义精确定位换届流程,避免误删注册流程。
|
||||||
|
List<Long> refreshDefineIds = dao.query(ProcessDefine.class,
|
||||||
|
Cnd.where(ProcessDefine::getName, "=", "XHHJBG"))
|
||||||
|
.stream()
|
||||||
|
.map(ProcessDefine::getId)
|
||||||
|
.toList();
|
||||||
|
List<ProcessInstance> refreshInstances = refreshDefineIds.isEmpty()
|
||||||
|
? List.of()
|
||||||
|
: dao.query(ProcessInstance.class,
|
||||||
|
Cnd.where(ProcessInstance::getBusinessNo, "=", id)
|
||||||
|
.and(ProcessInstance::getProcessDefineId, "in", refreshDefineIds));
|
||||||
clubInfoManageService.dao().delete(SysClubRefresh.class, id);
|
clubInfoManageService.dao().delete(SysClubRefresh.class, id);
|
||||||
flowEngine.processInstanceService().deleteProcessInstanceByBusinessKey(id);
|
for (ProcessInstance refreshInstance : refreshInstances) {
|
||||||
|
flowEngine.processInstanceService().deleteProcessInstanceById(refreshInstance.getId());
|
||||||
|
}
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
@@ -17,6 +17,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.nutz.dao.Cnd;
|
import org.nutz.dao.Cnd;
|
||||||
import org.nutz.dao.Dao;
|
import org.nutz.dao.Dao;
|
||||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||||
|
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.Inject;
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
import org.nutz.lang.Lang;
|
import org.nutz.lang.Lang;
|
||||||
@@ -161,6 +163,26 @@ public class SiteCugManageController {
|
|||||||
return Result.success(savedInfo == null ? info : savedInfo);
|
return Result.success(savedInfo == null ? info : savedInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换场地开启状态。
|
||||||
|
*
|
||||||
|
* @param id 场地ID,必须传入已存在的 site_cug_info 主键
|
||||||
|
* @param state 目标状态,true 表示开启,false 表示关闭
|
||||||
|
* @return Result 响应;code 为 0 表示状态更新成功,否则 msg 为参数错误信息
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@ApiOperation("切换场地开启状态")
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
@SaCheckPermission("siteCug.manage")
|
||||||
|
@SLog(tag = "场馆功能管理-场地管理", msg = "切换场地开启状态")
|
||||||
|
public Result switchState(String id, Boolean state) {
|
||||||
|
if (StrUtil.isBlank(id) || state == null) {
|
||||||
|
return Result.error("场地ID或开启状态不能为空");
|
||||||
|
}
|
||||||
|
infoService.updateState(id, state);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
@At
|
@At
|
||||||
@ApiOperation("delete")
|
@ApiOperation("delete")
|
||||||
@SaCheckPermission("siteCug.manage")
|
@SaCheckPermission("siteCug.manage")
|
||||||
|
|||||||
@@ -4,4 +4,12 @@ import com.budwk.app.base.service.BaseService;
|
|||||||
import com.budwk.app.zhgh.dayofficework.siteCug.model.SiteCugInfo;
|
import com.budwk.app.zhgh.dayofficework.siteCug.model.SiteCugInfo;
|
||||||
|
|
||||||
public interface SiteCugInfoService extends BaseService<SiteCugInfo> {
|
public interface SiteCugInfoService extends BaseService<SiteCugInfo> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅更新场地开启状态,不触发场地完整表单校验。
|
||||||
|
*
|
||||||
|
* @param id 场地ID
|
||||||
|
* @param state true 表示开启,false 表示关闭
|
||||||
|
*/
|
||||||
|
void updateState(String id, Boolean state);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -4,7 +4,11 @@ import com.budwk.app.base.service.impl.BaseServiceImpl;
|
|||||||
import com.budwk.app.zhgh.dayofficework.siteCug.model.SiteCugInfo;
|
import com.budwk.app.zhgh.dayofficework.siteCug.model.SiteCugInfo;
|
||||||
import com.budwk.app.zhgh.dayofficework.siteCug.service.SiteCugInfoService;
|
import com.budwk.app.zhgh.dayofficework.siteCug.service.SiteCugInfoService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
|
import org.nutz.dao.Chain;
|
||||||
|
import org.nutz.dao.Cnd;
|
||||||
import org.nutz.dao.Dao;
|
import org.nutz.dao.Dao;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -14,4 +18,16 @@ public class SiteCugInfoServiceImpl extends BaseServiceImpl<SiteCugInfo> impleme
|
|||||||
public SiteCugInfoServiceImpl(Dao dao) {
|
public SiteCugInfoServiceImpl(Dao dao) {
|
||||||
super(dao);
|
super(dao);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅更新指定场地的开启状态,防止开关操作覆盖其他场地配置。
|
||||||
|
*
|
||||||
|
* @param id 场地ID
|
||||||
|
* @param state true 表示开启,false 表示关闭
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
public void updateState(String id, Boolean state) {
|
||||||
|
update(Chain.make("state", state), Cnd.where("id", "=", id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -54,7 +54,7 @@ import java.util.List;
|
|||||||
@IocBean
|
@IocBean
|
||||||
@At("/platform/unionReimburse/apply")
|
@At("/platform/unionReimburse/apply")
|
||||||
@Api("工会报销申请")
|
@Api("工会报销申请")
|
||||||
@Ok("json:full")
|
@Ok("json:{locked:'condolenceIdCard|condolenceBirthday|condolenceMobile',ignoreNull:false}")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class UnionReimburseApplyController {
|
public class UnionReimburseApplyController {
|
||||||
|
|
||||||
@@ -151,14 +151,11 @@ public class UnionReimburseApplyController {
|
|||||||
username as userName,
|
username as userName,
|
||||||
loginname as loginName,
|
loginname as loginName,
|
||||||
sex,
|
sex,
|
||||||
mobile,
|
|
||||||
technicalTitle,
|
technicalTitle,
|
||||||
IFNULL(unitname, '暂无') as unitName,
|
IFNULL(unitname, '暂无') as unitName,
|
||||||
unitid as unitId,
|
unitid as unitId,
|
||||||
unionid as unionId,
|
unionid as unionId,
|
||||||
unionname as unionName,
|
unionname as unionName,
|
||||||
DATE(birthday) AS birthday,
|
|
||||||
idCard,
|
|
||||||
unionCode
|
unionCode
|
||||||
from
|
from
|
||||||
vw_user
|
vw_user
|
||||||
@@ -173,6 +170,9 @@ public class UnionReimburseApplyController {
|
|||||||
}
|
}
|
||||||
cnd.and(View_user::getId, "!=", SecurityUtil.getUserId());
|
cnd.and(View_user::getId, "!=", SecurityUtil.getUserId());
|
||||||
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
||||||
|
if (StrUtil.isBlank(SecurityUtil.getUnionId())) {
|
||||||
|
return Result.success(java.util.Collections.emptyList());
|
||||||
|
}
|
||||||
cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId());
|
cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId());
|
||||||
}
|
}
|
||||||
sql.setCondition(cnd);
|
sql.setCondition(cnd);
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@IocBean
|
@IocBean
|
||||||
@At("/platform/unionReimburse/certifierUserSign")
|
@At("/platform/unionReimburse/certifierUserSign")
|
||||||
@Ok("json:full")
|
@Ok("json:{locked:'condolenceIdCard|condolenceBirthday|condolenceMobile',ignoreNull:false}")
|
||||||
@Api("证明人签字")
|
@Api("证明人签字")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class UnionReimburseCertifierUserSignController {
|
public class UnionReimburseCertifierUserSignController {
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ import java.util.HashMap;
|
|||||||
@IocBean
|
@IocBean
|
||||||
@At("/platform/unionReimburse/mine")
|
@At("/platform/unionReimburse/mine")
|
||||||
@Api("工会报销我的")
|
@Api("工会报销我的")
|
||||||
@Ok("json:full")
|
@Ok("json:{locked:'condolenceIdCard|condolenceBirthday|condolenceMobile',ignoreNull:false}")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class UnionReimburseMineController {
|
public class UnionReimburseMineController {
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@IocBean
|
@IocBean
|
||||||
@At("/platform/unionReimburse/review")
|
@At("/platform/unionReimburse/review")
|
||||||
@Ok("json:full")
|
@Ok("json:{locked:'condolenceIdCard|condolenceBirthday|condolenceMobile',ignoreNull:false}")
|
||||||
@Api("工会报销审核")
|
@Api("工会报销审核")
|
||||||
public class UnionReimburseReviewController {
|
public class UnionReimburseReviewController {
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@IocBean
|
@IocBean
|
||||||
@At("/platform/unionReimburse/statistics")
|
@At("/platform/unionReimburse/statistics")
|
||||||
@Ok("json:full")
|
@Ok("json:{locked:'condolenceIdCard|condolenceBirthday|condolenceMobile',ignoreNull:false}")
|
||||||
@Api("慰问统计")
|
@Api("慰问统计")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class UnionReimburseStatisticsController {
|
public class UnionReimburseStatisticsController {
|
||||||
|
|||||||
+123
-3
@@ -4,10 +4,12 @@ import cn.hutool.core.date.DateUtil;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.budwk.app.base.constant.RoleConstant;
|
import com.budwk.app.base.constant.RoleConstant;
|
||||||
|
import com.budwk.app.base.exception.BaseException;
|
||||||
import com.budwk.app.base.page.Pagination;
|
import com.budwk.app.base.page.Pagination;
|
||||||
import com.budwk.app.base.result.Result;
|
import com.budwk.app.base.result.Result;
|
||||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||||
import com.budwk.app.sys.models.Sys_file;
|
import com.budwk.app.sys.models.Sys_file;
|
||||||
|
import com.budwk.app.sys.views.View_user;
|
||||||
import com.budwk.app.sys.services.SysFileService;
|
import com.budwk.app.sys.services.SysFileService;
|
||||||
import com.budwk.app.zhgh.club.model.ClubUser;
|
import com.budwk.app.zhgh.club.model.ClubUser;
|
||||||
import com.budwk.app.zhgh.dayofficework.outlay.outlayManage.club.model.OutlayManageClub;
|
import com.budwk.app.zhgh.dayofficework.outlay.outlayManage.club.model.OutlayManageClub;
|
||||||
@@ -125,6 +127,37 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
|
|||||||
if (oldRecord == null) {
|
if (oldRecord == null) {
|
||||||
return Result.error("未找到对应的报销记录");
|
return Result.error("未找到对应的报销记录");
|
||||||
}
|
}
|
||||||
|
if (!isAdmin() && !Objects.equals(oldRecord.getUserId(), SecurityUtil.getUserId())) {
|
||||||
|
return Result.error("无权修改此报销申请");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 人员身份和敏感资料由服务端补齐,避免前端删减字段导致丢失或被篡改。
|
||||||
|
if (oldRecord == null) {
|
||||||
|
View_user handler = dao().fetch(View_user.class, Cnd.where("id", "=", SecurityUtil.getUserId()));
|
||||||
|
if (handler == null) {
|
||||||
|
return Result.error("经办人信息不存在");
|
||||||
|
}
|
||||||
|
unionReimburse.setUserId(handler.getId());
|
||||||
|
unionReimburse.setUserName(handler.getUsername());
|
||||||
|
unionReimburse.setLoginName(handler.getLoginname());
|
||||||
|
unionReimburse.setUnitId(handler.getUnitId());
|
||||||
|
unionReimburse.setUnitName(handler.getUnitName());
|
||||||
|
unionReimburse.setUnionId(handler.getUnionId());
|
||||||
|
unionReimburse.setUnionName(handler.getUnionName());
|
||||||
|
} else {
|
||||||
|
// 修改历史单据时保留原经办组织,避免人员调动改变经费归属。
|
||||||
|
unionReimburse.setUserId(oldRecord.getUserId());
|
||||||
|
unionReimburse.setUserName(oldRecord.getUserName());
|
||||||
|
unionReimburse.setLoginName(oldRecord.getLoginName());
|
||||||
|
unionReimburse.setUnitId(oldRecord.getUnitId());
|
||||||
|
unionReimburse.setUnitName(oldRecord.getUnitName());
|
||||||
|
unionReimburse.setUnionId(oldRecord.getUnionId());
|
||||||
|
unionReimburse.setUnionName(oldRecord.getUnionName());
|
||||||
|
}
|
||||||
|
Result recipientResult = fillCondolencePersonalData(unionReimburse, oldRecord);
|
||||||
|
if (recipientResult != null) {
|
||||||
|
return recipientResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeInvoiceDetails(unionReimburse);
|
normalizeInvoiceDetails(unionReimburse);
|
||||||
@@ -179,14 +212,104 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
|
|||||||
if (unionReimburse == null) {
|
if (unionReimburse == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (!canReadApply(unionReimburse)) {
|
||||||
|
throw new BaseException("无权查看此报销申请");
|
||||||
|
}
|
||||||
List<UnionReimburseInvoiceDetail> invoiceDetails = this.dao().query(
|
List<UnionReimburseInvoiceDetail> invoiceDetails = this.dao().query(
|
||||||
UnionReimburseInvoiceDetail.class,
|
UnionReimburseInvoiceDetail.class,
|
||||||
Cnd.where("reimburseId", "=", id).asc("createdAt")
|
Cnd.where("reimburseId", "=", id).asc("createdAt")
|
||||||
);
|
);
|
||||||
unionReimburse.setInvoiceDetails(invoiceDetails == null ? new ArrayList<>() : invoiceDetails);
|
unionReimburse.setInvoiceDetails(invoiceDetails == null ? new ArrayList<>() : invoiceDetails);
|
||||||
|
// 申请页和详情页共用此接口,原始身份资料仅保留在服务端业务记录中。
|
||||||
|
unionReimburse.setCondolenceIdCard(null);
|
||||||
|
unionReimburse.setCondolenceBirthday(null);
|
||||||
|
unionReimburse.setCondolenceMobile(null);
|
||||||
return unionReimburse;
|
return unionReimburse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 保留历史单据的身份资料快照,新选人员须符合人员查询的数据范围。 */
|
||||||
|
private Result fillCondolencePersonalData(UnionReimburse form, UnionReimburse oldRecord) {
|
||||||
|
form.setCondolenceIdCard(null);
|
||||||
|
form.setCondolenceBirthday(null);
|
||||||
|
form.setCondolenceMobile(null);
|
||||||
|
if (!"UNION_REIMBURSE_PROJECT_1".equals(form.getReimburseProject())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(form.getCondolenceUserId())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (oldRecord != null && Objects.equals(oldRecord.getCondolenceUserId(), form.getCondolenceUserId())) {
|
||||||
|
form.setCondolenceIdCard(oldRecord.getCondolenceIdCard());
|
||||||
|
form.setCondolenceBirthday(oldRecord.getCondolenceBirthday());
|
||||||
|
form.setCondolenceMobile(oldRecord.getCondolenceMobile());
|
||||||
|
form.setCondolenceUnionId(oldRecord.getCondolenceUnionId());
|
||||||
|
form.setCondolenceUnitId(oldRecord.getCondolenceUnitId());
|
||||||
|
form.setCondolenceUserName(oldRecord.getCondolenceUserName());
|
||||||
|
form.setCondolenceLoginName(oldRecord.getCondolenceLoginName());
|
||||||
|
form.setCondolenceUnitName(oldRecord.getCondolenceUnitName());
|
||||||
|
form.setCondolenceUnionName(oldRecord.getCondolenceUnionName());
|
||||||
|
form.setCondolenceUnionCode(oldRecord.getCondolenceUnionCode());
|
||||||
|
form.setCondolenceSex(oldRecord.getCondolenceSex());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
View_user user = dao().fetch(View_user.class, Cnd.where("id", "=", form.getCondolenceUserId()));
|
||||||
|
if (user == null || Objects.equals(user.getId(), SecurityUtil.getUserId())
|
||||||
|
|| (!isAdmin() && (StrUtil.isBlank(SecurityUtil.getUnionId())
|
||||||
|
|| !Objects.equals(SecurityUtil.getUnionId(), user.getUnionId())))) {
|
||||||
|
return Result.error("无权选择此慰问对象");
|
||||||
|
}
|
||||||
|
form.setCondolenceUserName(user.getUsername());
|
||||||
|
form.setCondolenceLoginName(user.getLoginname());
|
||||||
|
form.setCondolenceUnitId(user.getUnitId());
|
||||||
|
form.setCondolenceUnitName(user.getUnitName());
|
||||||
|
form.setCondolenceUnionId(user.getUnionId());
|
||||||
|
form.setCondolenceUnionName(user.getUnionName());
|
||||||
|
form.setCondolenceUnionCode(user.getUnionCode());
|
||||||
|
form.setCondolenceSex(user.getSex());
|
||||||
|
form.setCondolenceIdCard(user.getIdCard());
|
||||||
|
form.setCondolenceBirthday(user.getBirthday() == null ? null : DateUtil.formatDate(user.getBirthday()));
|
||||||
|
form.setCondolenceMobile(user.getMobile());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 详情访问沿用经办、审核、统计和流程参与范围,不能仅凭单据 ID 查询。 */
|
||||||
|
private boolean canReadApply(UnionReimburse form) {
|
||||||
|
String userId = SecurityUtil.getUserId();
|
||||||
|
if (StrUtil.isBlank(userId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isAdmin() || userId.equals(form.getUserId())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (form.getStateId() != 1 && (AuthUtil.hasPermission("unionReimburse.review")
|
||||||
|
|| AuthUtil.hasPermission("h5.unionReimburse.review"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((form.getStateId() == 2 || form.getStateId() == 3)
|
||||||
|
&& StrUtil.isNotBlank(SecurityUtil.getUnionId())
|
||||||
|
&& Objects.equals(SecurityUtil.getUnionId(), form.getCondolenceUnionId())
|
||||||
|
&& (AuthUtil.hasPermission("unionReimburse.statistics")
|
||||||
|
|| AuthUtil.hasPermission("h5.unionReimburse.statistics"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 旧流程单据允许任务参与人和抄送人回看,关系必须由数据库确认。
|
||||||
|
Sql sql = Sqls.create("""
|
||||||
|
SELECT COUNT(*) FROM wf_process_instance i
|
||||||
|
WHERE i.businessNo = @id AND (
|
||||||
|
EXISTS (SELECT 1 FROM wf_process_task t
|
||||||
|
INNER JOIN wf_process_task_actor a ON a.processTaskId = t.id
|
||||||
|
WHERE t.processInstanceId = i.id AND a.actorId = @userId)
|
||||||
|
OR EXISTS (SELECT 1 FROM wf_process_cc_instance c
|
||||||
|
WHERE c.processInstanceId = i.id AND c.actorId = @userId)
|
||||||
|
)
|
||||||
|
""");
|
||||||
|
sql.setParam("id", form.getId());
|
||||||
|
sql.setParam("userId", userId);
|
||||||
|
sql.setCallback(Sqls.callback.integer());
|
||||||
|
dao().execute(sql);
|
||||||
|
return sql.getInt() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteApply(String id) {
|
public void deleteApply(String id) {
|
||||||
if (StrUtil.isBlank(id)) {
|
if (StrUtil.isBlank(id)) {
|
||||||
@@ -242,14 +365,11 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
|
|||||||
u.username AS userName,
|
u.username AS userName,
|
||||||
u.loginname AS loginName,
|
u.loginname AS loginName,
|
||||||
u.sex,
|
u.sex,
|
||||||
u.mobile,
|
|
||||||
u.technicalTitle,
|
u.technicalTitle,
|
||||||
IFNULL(u.unitname, '暂无') AS unitName,
|
IFNULL(u.unitname, '暂无') AS unitName,
|
||||||
u.unitid AS unitId,
|
u.unitid AS unitId,
|
||||||
u.unionid AS unionId,
|
u.unionid AS unionId,
|
||||||
u.unionname AS unionName,
|
u.unionname AS unionName,
|
||||||
DATE(u.birthday) AS birthday,
|
|
||||||
u.idCard,
|
|
||||||
u.unionCode
|
u.unionCode
|
||||||
FROM
|
FROM
|
||||||
vw_user u
|
vw_user u
|
||||||
|
|||||||
+11
-3
@@ -91,6 +91,7 @@ public class CongressVotingStatisticalController {
|
|||||||
@At("/records")
|
@At("/records")
|
||||||
@SaCheckPermission("congress.vote.statistical")
|
@SaCheckPermission("congress.vote.statistical")
|
||||||
public Result voteRecords(@Param("issueId") String issueId) {
|
public Result voteRecords(@Param("issueId") String issueId) {
|
||||||
|
// H5 投票使用现有职代会代表表写入 rep_id,旧表关联仅作为历史数据兼容回退。
|
||||||
Sql sql = Sqls.create("""
|
Sql sql = Sqls.create("""
|
||||||
SELECT
|
SELECT
|
||||||
r.id,
|
r.id,
|
||||||
@@ -98,10 +99,17 @@ public class CongressVotingStatisticalController {
|
|||||||
r.name,
|
r.name,
|
||||||
r.vote_result AS voteResult,
|
r.vote_result AS voteResult,
|
||||||
r.create_time AS createTime,
|
r.create_time AS createTime,
|
||||||
COALESCE(rep.union_id, du.union_id) AS unionId,
|
COALESCE(tc_delegate.unionId, rep.union_id, du.union_id) AS unionId,
|
||||||
du.union_name AS unionName,
|
COALESCE(tc_delegate.unionName, du.union_name) AS unionName,
|
||||||
COALESCE(rep.delegation_name, d.name) AS delegationName
|
COALESCE(delegate_delegation.name, rep.delegation_name, d.name) AS delegationName
|
||||||
FROM congress_voting_record r
|
FROM congress_voting_record r
|
||||||
|
LEFT JOIN teacher_congress_delegate tc_delegate ON tc_delegate.id = r.rep_id
|
||||||
|
AND tc_delegate.sessionId = r.session_id
|
||||||
|
AND COALESCE(tc_delegate.delFlag, 0) = 0
|
||||||
|
LEFT JOIN teacher_congress_delegation delegate_delegation
|
||||||
|
ON delegate_delegation.id = COALESCE(r.delegation_id, tc_delegate.delegationId)
|
||||||
|
AND delegate_delegation.sessionId = r.session_id
|
||||||
|
AND COALESCE(delegate_delegation.delFlag, 0) = 0
|
||||||
LEFT JOIN congress_rep rep ON rep.id = r.rep_id
|
LEFT JOIN congress_rep rep ON rep.id = r.rep_id
|
||||||
LEFT JOIN congress_delegation d ON d.id = r.delegation_id
|
LEFT JOIN congress_delegation d ON d.id = r.delegation_id
|
||||||
LEFT JOIN (
|
LEFT JOIN (
|
||||||
|
|||||||
+1
-4
@@ -173,10 +173,7 @@ public class ProposalSecondedController {
|
|||||||
}
|
}
|
||||||
ProposalSearchParam.buildSearch(cnd, pageForm);
|
ProposalSearchParam.buildSearch(cnd, pageForm);
|
||||||
cnd.groupBy("t.id");
|
cnd.groupBy("t.id");
|
||||||
// 用户未指定有效排序字段时,继续沿用原有任务创建时间倒序。
|
cnd.desc("t.createdAt");
|
||||||
if (!proposalCommonService.applySafePageOrder(cnd, pageForm, ORDER_COLUMNS)) {
|
|
||||||
cnd.desc("t.createdAt");
|
|
||||||
}
|
|
||||||
sql.setCondition(cnd);
|
sql.setCondition(cnd);
|
||||||
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||||
return Result.success(pagination);
|
return Result.success(pagination);
|
||||||
|
|||||||
+1
-1
@@ -244,7 +244,7 @@ public class ProposalUnderTakeReplyController {
|
|||||||
@SaCheckPermission("proposal.unitReply")
|
@SaCheckPermission("proposal.unitReply")
|
||||||
@ApiOperation("查询本单位可转办用户")
|
@ApiOperation("查询本单位可转办用户")
|
||||||
public Result selectViceUser(@Param("keyword") @Valid String keyword) {
|
public Result selectViceUser(@Param("keyword") @Valid String keyword) {
|
||||||
Sql sql = Sqls.create("select id,loginname,username from vw_user $condition");
|
Sql sql = Sqls.create("select id,loginname,username,unitName from vw_user $condition");
|
||||||
Cnd cnd = Cnd.NEW();
|
Cnd cnd = Cnd.NEW();
|
||||||
cnd.andEX("unitId", "=", SecurityUtil.getUnitId());
|
cnd.andEX("unitId", "=", SecurityUtil.getUnitId());
|
||||||
cnd.andEX("id", "!=", SecurityUtil.getUserId());
|
cnd.andEX("id", "!=", SecurityUtil.getUserId());
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class ProposalSearchParam extends PageForm {
|
|||||||
if (StrUtil.isAllNotBlank(searchParam.getPageOrderName(), searchParam.getPageOrderBy())) {
|
if (StrUtil.isAllNotBlank(searchParam.getPageOrderName(), searchParam.getPageOrderBy())) {
|
||||||
cnd.orderBy(searchParam.getPageOrderName(), PageUtil.getOrder(searchParam.getPageOrderBy()));
|
cnd.orderBy(searchParam.getPageOrderName(), PageUtil.getOrder(searchParam.getPageOrderBy()));
|
||||||
} else {
|
} else {
|
||||||
cnd.asc("info.code");
|
cnd.desc("info.code");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
|
||||||
import org.nutz.dao.entity.annotation.*;
|
import org.nutz.dao.entity.annotation.*;
|
||||||
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||||
|
|
||||||
@@ -161,13 +160,13 @@ public class MallGoods extends BaseModel {
|
|||||||
//循环sku,判定sku是否有效
|
//循环sku,判定sku是否有效
|
||||||
for (Map<String, Object> sku : goodsOperationDTO.getSkuList()) {
|
for (Map<String, Object> sku : goodsOperationDTO.getSkuList()) {
|
||||||
//判定参数不能为空
|
//判定参数不能为空
|
||||||
if ((!sku.containsKey("price") || StringUtil.isEmpty(sku.get("price").toString()) || Convert.toDouble(sku.get("price")) <= 0)) {
|
if ((!sku.containsKey("price") || CharSequenceUtil.isEmpty(sku.get("price").toString()) || Convert.toDouble(sku.get("price")) <= 0)) {
|
||||||
throw new BaseException("商品SKU价格不能小于等于0");
|
throw new BaseException("商品SKU价格不能小于等于0");
|
||||||
}
|
}
|
||||||
// if ((!sku.containsKey("cost") || StringUtil.isEmpty(sku.get("cost").toString()) || Convert.toDouble(sku.get("cost")) <= 0)) {
|
// if ((!sku.containsKey("cost") || StringUtil.isEmpty(sku.get("cost").toString()) || Convert.toDouble(sku.get("cost")) <= 0)) {
|
||||||
// throw new BaseException("商品SKU成本价不能小于等于0");
|
// throw new BaseException("商品SKU成本价不能小于等于0");
|
||||||
// }
|
// }
|
||||||
if (!sku.containsKey("quantity") || StringUtil.isEmpty(sku.get("quantity").toString()) || Convert.toInt(sku.get("quantity").toString()) < 0) {
|
if (!sku.containsKey("quantity") || CharSequenceUtil.isEmpty(sku.get("quantity").toString()) || Convert.toInt(sku.get("quantity").toString()) < 0) {
|
||||||
throw new BaseException("商品库存数量不能为负数");
|
throw new BaseException("商品库存数量不能为负数");
|
||||||
}
|
}
|
||||||
sku.values().forEach(i -> {
|
sku.values().forEach(i -> {
|
||||||
|
|||||||
+3
-1
@@ -127,7 +127,6 @@ public class CondolenceApplyController {
|
|||||||
username as userName,
|
username as userName,
|
||||||
loginname as loginName,
|
loginname as loginName,
|
||||||
sex,
|
sex,
|
||||||
mobile,
|
|
||||||
technicalTitle,
|
technicalTitle,
|
||||||
IFNULL(unitname, '暂无') as unitName,
|
IFNULL(unitname, '暂无') as unitName,
|
||||||
unitid as unitId,
|
unitid as unitId,
|
||||||
@@ -146,6 +145,9 @@ public class CondolenceApplyController {
|
|||||||
}
|
}
|
||||||
if(!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
if(!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
||||||
if(AuthUtil.hasRoleOr(RoleConstant.BRANCH_UNION_ADMIN.name(), RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
|
if(AuthUtil.hasRoleOr(RoleConstant.BRANCH_UNION_ADMIN.name(), RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
|
||||||
|
if (StrUtil.isBlank(SecurityUtil.getUnionId())) {
|
||||||
|
return Result.success(java.util.Collections.emptyList());
|
||||||
|
}
|
||||||
cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId());
|
cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId());
|
||||||
} else {
|
} else {
|
||||||
cnd.and(View_user::getId, "=", SecurityUtil.getUserId());
|
cnd.and(View_user::getId, "=", SecurityUtil.getUserId());
|
||||||
|
|||||||
@@ -16,16 +16,20 @@ sm4cbc:
|
|||||||
keyHex: 00112233445566778899aabbccddeeff
|
keyHex: 00112233445566778899aabbccddeeff
|
||||||
ivHex: 0102030405060708090a0b0c0d0e0f10
|
ivHex: 0102030405060708090a0b0c0d0e0f10
|
||||||
|
|
||||||
jetty:
|
tomcat:
|
||||||
contextPath: /
|
contextPath: ""
|
||||||
threadpool:
|
# 容器错误响应不披露版本;Session Cookie 保持 HttpOnly。
|
||||||
idleTimeout: 60000
|
session:
|
||||||
minThreads: 10
|
cookie:
|
||||||
|
httponly: true
|
||||||
|
executor:
|
||||||
|
maxIdleTime: 60000
|
||||||
|
minSpareThreads: 10
|
||||||
maxThreads: 500
|
maxThreads: 500
|
||||||
page:
|
page:
|
||||||
403: /error/403.html
|
403: /__container_error
|
||||||
404: /error/404.html
|
404: /__container_error
|
||||||
500: /error/500.html
|
500: /__container_error
|
||||||
#结合ftp使用,或用nginx代理ftp路径
|
#结合ftp使用,或用nginx代理ftp路径
|
||||||
#staticPath: /Users/wizzer/temp/files
|
#staticPath: /Users/wizzer/temp/files
|
||||||
#开发模式静态资源
|
#开发模式静态资源
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ var ioc={
|
|||||||
// 单个文件最大尺寸(大约的值,单位为字节,即 2097152 为 2M)
|
// 单个文件最大尺寸(大约的值,单位为字节,即 2097152 为 2M)
|
||||||
maxFileSize : 2097152,
|
maxFileSize : 2097152,
|
||||||
// 正则表达式匹配可以支持的文件名
|
// 正则表达式匹配可以支持的文件名
|
||||||
nameFilter : '^(.+[.])(gif|jpg|png|svg)$'
|
nameFilter : '(?i)^(.+[.])(gif|jpg|jpeg|png)$'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
imageUpload : {
|
imageUpload : {
|
||||||
@@ -32,7 +32,7 @@ var ioc={
|
|||||||
// 单个文件最大尺寸(大约的值,单位为字节,即 209715200 为 200M)
|
// 单个文件最大尺寸(大约的值,单位为字节,即 209715200 为 200M)
|
||||||
maxFileSize : 209715200,
|
maxFileSize : 209715200,
|
||||||
// 正则表达式匹配可以支持的文件名
|
// 正则表达式匹配可以支持的文件名
|
||||||
nameFilter : '^(.+[.])(gif||svg|jpg|png|jpeg|doc|docx|xls|xlsx|rar|zip|7z|txt|pdf|pptx|ppt|mp4)$'
|
nameFilter : '(?i)^(.+[.])(gif|jpg|png|jpeg|doc|docx|xls|xlsx|rar|zip|7z|txt|pdf|pptx|ppt|mp4)$'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fileUpload : {
|
fileUpload : {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ layout("/layouts/platform.html"){
|
|||||||
新增场地
|
新增场地
|
||||||
</el-button>
|
</el-button>
|
||||||
</table-tool>
|
</table-tool>
|
||||||
<el-table :data="tableData" @sort-change="pageOrder" :size="tableSize">
|
<el-table v-loading="tableLoading" :data="tableData" @sort-change="pageOrder" :size="tableSize">
|
||||||
<el-table-column label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
|
<el-table-column label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="column in tableColumns"
|
v-for="column in tableColumns"
|
||||||
@@ -152,11 +152,23 @@ layout("/layouts/platform.html"){
|
|||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
},
|
},
|
||||||
switchChange(row) {
|
switchChange(row) {
|
||||||
this.$axios.post("/platform/siteCug/manage/submit", row).then((res) => {
|
const previousState = !row.state
|
||||||
|
this.tableLoading = true
|
||||||
|
this.$axios.post("/platform/siteCug/manage/switchState", {
|
||||||
|
id: row.id,
|
||||||
|
state: row.state
|
||||||
|
}).then((res) => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
this.$message.success(res.msg)
|
this.$message.success(res.msg)
|
||||||
this.pageData()
|
} else {
|
||||||
|
// 状态更新失败时恢复开关,避免页面显示与数据库实际状态不一致。
|
||||||
|
this.$set(row, "state", previousState)
|
||||||
|
this.$message.warning(res.msg)
|
||||||
}
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
this.$set(row, "state", previousState)
|
||||||
|
}).finally(() => {
|
||||||
|
this.tableLoading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
querySiteType() {
|
querySiteType() {
|
||||||
|
|||||||
+1
-33
@@ -187,31 +187,6 @@ layout("/layouts/platform.html"){
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item label="生日"
|
|
||||||
v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
|
|
||||||
<el-form-item prop="condolenceBirthday" label="生日">
|
|
||||||
<el-input
|
|
||||||
:value="formData.condolenceBirthday && $moment(formData.condolenceBirthday).isValid() ?$moment(formData.condolenceBirthday).format('YYYY-MM-DD') : ''"
|
|
||||||
show-word-limit placeholder="请输入生日" disabled></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item label="身份证号"
|
|
||||||
v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
|
|
||||||
<el-form-item prop="condolenceIdCard" label="身份证号">
|
|
||||||
<el-input v-model="formData.condolenceIdCard" show-word-limit
|
|
||||||
placeholder="请输入身份证号" disabled></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item label="联系方式"
|
|
||||||
v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
|
|
||||||
<el-form-item prop="condolenceMobile" label="联系方式">
|
|
||||||
<el-input v-model="formData.condolenceMobile" show-word-limit
|
|
||||||
placeholder="请输入联系方式" disabled></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item label="慰问类型"
|
<el-descriptions-item label="慰问类型"
|
||||||
v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
|
v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
|
||||||
<el-form-item label="慰问类型" prop="condolenceTypeId">
|
<el-form-item label="慰问类型" prop="condolenceTypeId">
|
||||||
@@ -816,7 +791,6 @@ layout("/layouts/platform.html"){
|
|||||||
trigger: ["change", "blur"]
|
trigger: ["change", "blur"]
|
||||||
}, {pattern: /^([1-9]{1})(\d{15}|\d{18})$/, message: "请输入正确的银行卡号", trigger: "blur"}],
|
}, {pattern: /^([1-9]{1})(\d{15}|\d{18})$/, message: "请输入正确的银行卡号", trigger: "blur"}],
|
||||||
bankOfDeposit: [{required: true, message: "请填写开户行", trigger: ["change", "blur"]}],
|
bankOfDeposit: [{required: true, message: "请填写开户行", trigger: ["change", "blur"]}],
|
||||||
condolenceMobile: [{pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号码", trigger: "blur"}],
|
|
||||||
condolenceTime: [{required: true, message: "请选择慰问时间", trigger: ["change", "blur"]}],
|
condolenceTime: [{required: true, message: "请选择慰问时间", trigger: ["change", "blur"]}],
|
||||||
// invoiceNumber: [{required: true, message: "请填写发票张数", trigger: ["change", "blur"]}],
|
// invoiceNumber: [{required: true, message: "请填写发票张数", trigger: ["change", "blur"]}],
|
||||||
// invoice: [{required: true, message: "请填写发票号码", trigger: ["change", "blur"]}],
|
// invoice: [{required: true, message: "请填写发票号码", trigger: ["change", "blur"]}],
|
||||||
@@ -1187,10 +1161,7 @@ layout("/layouts/platform.html"){
|
|||||||
unionId,
|
unionId,
|
||||||
unionName,
|
unionName,
|
||||||
unionCode,
|
unionCode,
|
||||||
sex,
|
sex
|
||||||
birthday,
|
|
||||||
idCard,
|
|
||||||
mobile
|
|
||||||
} = user
|
} = user
|
||||||
this.$set(this.formData, "condolenceUserName", userName)
|
this.$set(this.formData, "condolenceUserName", userName)
|
||||||
this.$set(this.formData, "condolenceLoginName", loginName)
|
this.$set(this.formData, "condolenceLoginName", loginName)
|
||||||
@@ -1200,9 +1171,6 @@ layout("/layouts/platform.html"){
|
|||||||
this.$set(this.formData, "condolenceUnionName", unionName)
|
this.$set(this.formData, "condolenceUnionName", unionName)
|
||||||
this.$set(this.formData, "condolenceUnionCode", unionCode)
|
this.$set(this.formData, "condolenceUnionCode", unionCode)
|
||||||
this.$set(this.formData, "condolenceSex", sex)
|
this.$set(this.formData, "condolenceSex", sex)
|
||||||
this.$set(this.formData, "condolenceBirthday", birthday)
|
|
||||||
this.$set(this.formData, "condolenceIdCard", idCard)
|
|
||||||
this.$set(this.formData, "condolenceMobile", mobile)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
typeChange(val) {
|
typeChange(val) {
|
||||||
|
|||||||
@@ -86,18 +86,6 @@ const unionReimburseInfo = {
|
|||||||
<span>{{ viewData.condolenceSex }}</span>
|
<span>{{ viewData.condolenceSex }}</span>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item label="生日" v-if="viewData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
|
|
||||||
<span>{{ viewData.condolenceBirthday && $moment(viewData.condolenceBirthday).isValid() ? $moment(viewData.condolenceBirthday).format('YYYY-MM-DD') : '' }}</span>
|
|
||||||
</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item label="身份证号" v-if="viewData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
|
|
||||||
<span>{{ viewData.condolenceIdCard }}</span>
|
|
||||||
</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item label="联系方式" v-if="viewData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
|
|
||||||
<span>{{ viewData.condolenceMobile }}</span>
|
|
||||||
</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item label="慰问类型" v-if="viewData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
|
<el-descriptions-item label="慰问类型" v-if="viewData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
|
||||||
<span>{{ viewData.typeName }}</span>
|
<span>{{ viewData.typeName }}</span>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|||||||
+15
-3
@@ -109,15 +109,27 @@ layout("/layouts/platform.html"){
|
|||||||
if (!value) return []
|
if (!value) return []
|
||||||
try {
|
try {
|
||||||
const data = typeof value === "string" ? JSON.parse(value) : value
|
const data = typeof value === "string" ? JSON.parse(value) : value
|
||||||
return Array.isArray(data) ? data : []
|
if (Array.isArray(data)) return data
|
||||||
|
if (data && typeof data === "object") {
|
||||||
|
return Object.keys(data).map(item => ({item, result: data[item]}))
|
||||||
|
}
|
||||||
|
return []
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
voteOptionText(item) {
|
||||||
|
const value = item.result || item.vote || item.selected || item.option || item.value
|
||||||
|
if (Array.isArray(value)) return value.join("、")
|
||||||
|
if (value && typeof value === "object") {
|
||||||
|
return value.label || value.name || value.value || "-"
|
||||||
|
}
|
||||||
|
return value || "-"
|
||||||
|
},
|
||||||
voteResultText(value) {
|
voteResultText(value) {
|
||||||
return this.parseVoteResult(value).map(item => {
|
return this.parseVoteResult(value).map(item => {
|
||||||
return (item.item || item.itemName || "投票项") + ":" +
|
return (item.item || item.itemName || "投票项") + ":" +
|
||||||
(item.selected || item.option || item.value || "-")
|
this.voteOptionText(item)
|
||||||
}).join(";")
|
}).join(";")
|
||||||
},
|
},
|
||||||
buildSummary(records) {
|
buildSummary(records) {
|
||||||
@@ -125,7 +137,7 @@ layout("/layouts/platform.html"){
|
|||||||
records.forEach(record => {
|
records.forEach(record => {
|
||||||
this.parseVoteResult(record.voteResult).forEach(item => {
|
this.parseVoteResult(record.voteResult).forEach(item => {
|
||||||
const itemName = item.item || item.itemName || "投票项"
|
const itemName = item.item || item.itemName || "投票项"
|
||||||
const option = item.selected || item.option || item.value || "-"
|
const option = this.voteOptionText(item)
|
||||||
if (!counter[itemName]) counter[itemName] = {}
|
if (!counter[itemName]) counter[itemName] = {}
|
||||||
counter[itemName][option] = (counter[itemName][option] || 0) + 1
|
counter[itemName][option] = (counter[itemName][option] || 0) + 1
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -110,10 +110,10 @@ const PROPOSAL_INFO = {
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!--审核信息支持按需同时展开多条记录,初次加载和切换提案时仍保持全部折叠。-->
|
<!--审核信息支持按需同时展开多条记录,初次加载和切换提案时仍保持全部折叠。-->
|
||||||
<template v-if="doneTasks.length">
|
<template v-if="displayDoneTasks.length">
|
||||||
<div class="process-title">审核信息</div>
|
<div class="process-title">审核信息</div>
|
||||||
<el-collapse v-model="activeTaskIds">
|
<el-collapse v-model="activeTaskIds">
|
||||||
<el-collapse-item v-for="task in doneTasks" :key="task.id" :name="task.id">
|
<el-collapse-item v-for="task in displayDoneTasks" :key="task.id" :name="task.id">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<!--折叠标题使用独立的单行布局,避免复用 process-title 后产生外边距和高度冲突。-->
|
<!--折叠标题使用独立的单行布局,避免复用 process-title 后产生外边距和高度冲突。-->
|
||||||
<div style="display: flex;align-items: center;justify-content: space-between;width: 100%;min-width: 0;padding-left: 10px">
|
<div style="display: flex;align-items: center;justify-content: space-between;width: 100%;min-width: 0;padding-left: 10px">
|
||||||
@@ -125,8 +125,28 @@ const PROPOSAL_INFO = {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<!--同一提案可能存在多个承办单位答复任务,查看时合并为一个流程节点,节点内保留每条答复。-->
|
||||||
|
<template v-if="task.replyTasks">
|
||||||
|
<el-descriptions v-for="replyTask in task.replyTasks" border class="flow-task-form"
|
||||||
|
:column="3" :key="replyTask.id">
|
||||||
|
<el-descriptions-item label="承办单位" :span="3">
|
||||||
|
{{replyTask.ext.underTakeName}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="办理用户">
|
||||||
|
{{replyTask.taskFormData.userName}}({{replyTask.taskFormData.loginName}})
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="办理时间">{{replyTask.finishTime}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="办理结果">
|
||||||
|
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE"
|
||||||
|
:value="replyTask.ext.submitType"></dict-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="办理意见" :span="3">
|
||||||
|
<div v-html="replyTask.taskFormData.tf_opinion"></div>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</template>
|
||||||
<el-descriptions border class="flow-task-form" :column="3" :key="task.id"
|
<el-descriptions border class="flow-task-form" :column="3" :key="task.id"
|
||||||
v-if="task.ext.isFirstTaskNode">
|
v-else-if="task.ext.isFirstTaskNode">
|
||||||
<el-descriptions-item label="申请用户">{{ task.ext.initiatorName
|
<el-descriptions-item label="申请用户">{{ task.ext.initiatorName
|
||||||
}}({{task.ext.initiatorAccount}})
|
}}({{task.ext.initiatorAccount}})
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
@@ -233,6 +253,7 @@ const PROPOSAL_INFO = {
|
|||||||
tableLoading: false,
|
tableLoading: false,
|
||||||
viewData: {},
|
viewData: {},
|
||||||
doneTasks: [],
|
doneTasks: [],
|
||||||
|
displayDoneTasks: [],
|
||||||
activeTaskIds: [],
|
activeTaskIds: [],
|
||||||
row: null,
|
row: null,
|
||||||
viewDialogVisible: false
|
viewDialogVisible: false
|
||||||
@@ -258,6 +279,7 @@ const PROPOSAL_INFO = {
|
|||||||
this.$set(this, "tableLoading", true)
|
this.$set(this, "tableLoading", true)
|
||||||
// 切换提案时先清空上一条提案的审核记录和展开状态,避免异步加载期间展示旧数据。
|
// 切换提案时先清空上一条提案的审核记录和展开状态,避免异步加载期间展示旧数据。
|
||||||
this.doneTasks = []
|
this.doneTasks = []
|
||||||
|
this.displayDoneTasks = []
|
||||||
this.activeTaskIds = []
|
this.activeTaskIds = []
|
||||||
let pendingRequestCount = 2
|
let pendingRequestCount = 2
|
||||||
const handleRequestComplete = () => {
|
const handleRequestComplete = () => {
|
||||||
@@ -293,15 +315,94 @@ const PROPOSAL_INFO = {
|
|||||||
return this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => {
|
return this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
const tasks = res.data || []
|
const tasks = res.data || []
|
||||||
this.doneTasks = tasks
|
// 提案历史按业务阶段展示,避免较晚完成的附议记录被排到团长审核之后。
|
||||||
|
const sortedTasks = this.sortDoneTasks(tasks)
|
||||||
|
this.$set(this, "doneTasks", sortedTasks)
|
||||||
|
// 参考历史系统的展示方式,将多个单位的答复收纳在一个流程节点内。
|
||||||
|
this.$set(this, "displayDoneTasks", this.groupReplyTasks(sortedTasks))
|
||||||
// 审核记录加载完成后默认全部折叠,由用户按需展开查看。
|
// 审核记录加载完成后默认全部折叠,由用户按需展开查看。
|
||||||
this.activeTaskIds = []
|
this.activeTaskIds = []
|
||||||
// 通知外层页面已办节点数据已加载完成,便于当前环节做表单回显。
|
// 通知外层页面已办节点数据已加载完成,便于当前环节做表单回显。
|
||||||
this.$emit("done-tasks", tasks)
|
this.$emit("done-tasks", this.doneTasks)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按提案业务阶段对审核历史排序。
|
||||||
|
*
|
||||||
|
* @param tasks doneTasks 接口返回的已办任务数组
|
||||||
|
* @return 排序后的新数组;同一阶段按办理时间、任务 ID 保持稳定顺序
|
||||||
|
*/
|
||||||
|
sortDoneTasks(tasks) {
|
||||||
|
return (tasks || []).map((task, index) => ({task, index})).sort((left, right) => {
|
||||||
|
const stageDiff = this.getTaskStageOrder(left.task) - this.getTaskStageOrder(right.task)
|
||||||
|
if (stageDiff !== 0) {
|
||||||
|
return stageDiff
|
||||||
|
}
|
||||||
|
const leftTime = String(left.task.finishTime || "")
|
||||||
|
const rightTime = String(right.task.finishTime || "")
|
||||||
|
if (leftTime !== rightTime) {
|
||||||
|
if (!leftTime) return 1
|
||||||
|
if (!rightTime) return -1
|
||||||
|
return leftTime.localeCompare(rightTime)
|
||||||
|
}
|
||||||
|
const idDiff = Number(left.task.id || 0) - Number(right.task.id || 0)
|
||||||
|
return idDiff !== 0 ? idDiff : left.index - right.index
|
||||||
|
}).map((item) => item.task)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将同一提案的承办单位答复任务合并成一个展示节点。
|
||||||
|
*
|
||||||
|
* @param tasks 已按业务阶段排好顺序的任务数组
|
||||||
|
* @return 仅用于查看页面的展示数组;原始任务及每条答复内容均保存在 replyTasks 中
|
||||||
|
*/
|
||||||
|
groupReplyTasks(tasks) {
|
||||||
|
const replyTaskNames = ["opinion_unit_reply", "unit_reply", "two_unit_reply", "master_reply", "slave_reply"]
|
||||||
|
const replyTasks = (tasks || []).filter((task) => replyTaskNames.includes(task.taskName))
|
||||||
|
if (!replyTasks.length) {
|
||||||
|
return tasks || []
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstReplyTask = replyTasks[0]
|
||||||
|
const replyTaskIds = new Set(replyTasks.map((task) => task.id))
|
||||||
|
return (tasks || []).reduce((displayTasks, task) => {
|
||||||
|
if (!replyTaskIds.has(task.id)) {
|
||||||
|
displayTasks.push(task)
|
||||||
|
} else if (task.id === firstReplyTask.id) {
|
||||||
|
displayTasks.push({
|
||||||
|
id: "reply-group-" + firstReplyTask.id,
|
||||||
|
taskName: "replyGroup",
|
||||||
|
displayName: "承办单位答复",
|
||||||
|
replyTasks: replyTasks
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return displayTasks
|
||||||
|
}, [])
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取提案任务所属的展示阶段。
|
||||||
|
* 未单列的预审核、委员审议、确认承办单位和复核记录统一放在委员会立案阶段,
|
||||||
|
* 答复后的校领导审批通过 legacyStage 标识排在承办单位答复之后。
|
||||||
|
*
|
||||||
|
* @param task 单条已办任务
|
||||||
|
* @return 数字类型的阶段顺序
|
||||||
|
*/
|
||||||
|
getTaskStageOrder(task) {
|
||||||
|
const taskName = task.taskName
|
||||||
|
const legacyStage = task.ext ? task.ext.legacyStage : ""
|
||||||
|
if (taskName === "startTask") return 10
|
||||||
|
if (taskName === "invite") return 15
|
||||||
|
if (taskName === "second") return 20
|
||||||
|
if (taskName === "delegation") return 30
|
||||||
|
if (["opinion_unit_reply", "unit_reply", "two_unit_reply", "master_reply", "slave_reply"].includes(taskName)) return 50
|
||||||
|
if (taskName === "schoolLeader" && legacyStage === "replyLeaderAudit") return 55
|
||||||
|
if (taskName === "feedback") return 60
|
||||||
|
return 40
|
||||||
|
},
|
||||||
|
|
||||||
openChart(){
|
openChart(){
|
||||||
this.$refs.snakerChartRef.onOpenFull(this.row.instanceProcessDefineId, this.row.instanceId)
|
this.$refs.snakerChartRef.onOpenFull(this.row.instanceProcessDefineId, this.row.instanceId)
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-10
@@ -179,16 +179,17 @@ layout("/layouts/platform.html"){
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="tf_attachment" label="附件">
|
<!-- 承办单位答复附件上传入口暂时停用,保留原配置便于后续恢复。 -->
|
||||||
<file-upload
|
<!-- <el-form-item prop="tf_attachment" label="附件">-->
|
||||||
:value.sync="formData.tf_attachment"
|
<!-- <file-upload-->
|
||||||
upload_mode="drag"
|
<!-- :value.sync="formData.tf_attachment"-->
|
||||||
:upload_number="5"
|
<!-- upload_mode="drag"-->
|
||||||
upload_result_category="array"
|
<!-- :upload_number="5"-->
|
||||||
complete_result
|
<!-- upload_result_category="array"-->
|
||||||
upload_mode="file"
|
<!-- complete_result-->
|
||||||
></file-upload>
|
<!-- upload_mode="file"-->
|
||||||
</el-form-item>
|
<!-- ></file-upload>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
<el-form-item label="答复内容" prop="tf_opinion"
|
<el-form-item label="答复内容" prop="tf_opinion"
|
||||||
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
|
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
|
||||||
<text-editor v-model="formData.tf_opinion"></text-editor>
|
<text-editor v-model="formData.tf_opinion"></text-editor>
|
||||||
|
|||||||
@@ -14,12 +14,11 @@ const welfareOption = {
|
|||||||
|
|
||||||
<el-table-column align="center" header-align="center" label="系统默认选择" width="140">
|
<el-table-column align="center" header-align="center" label="系统默认选择" width="140">
|
||||||
<template slot-scope="{row}">
|
<template slot-scope="{row}">
|
||||||
<el-radio
|
<el-checkbox
|
||||||
v-model="systemDefaultOption"
|
:value="!!row.isSystemDefault"
|
||||||
:label="row"
|
@change="setSystemDefault(row, $event)">
|
||||||
@change="setSystemDefault(row)">
|
|
||||||
|
|
||||||
</el-radio>
|
</el-checkbox>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
@@ -124,9 +123,10 @@ const welfareOption = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 每个福利项目只允许配置一个系统默认选项,保存时会随福利选项一并提交。
|
// 每个福利项目只允许配置一个系统默认选项,保存时会随福利选项一并提交。
|
||||||
setSystemDefault(defaultOption) {
|
setSystemDefault(defaultOption, checked = true) {
|
||||||
|
this.systemDefaultOption = checked ? defaultOption : null;
|
||||||
this.welfareList.forEach((item) => {
|
this.welfareList.forEach((item) => {
|
||||||
this.$set(item, 'isSystemDefault', item === defaultOption);
|
this.$set(item, 'isSystemDefault', checked && item === defaultOption);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ const welfareOption = {
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .welfare-option .el-radio__label {
|
/deep/ .welfare-option .el-checkbox__label {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -397,6 +397,19 @@ layout("/layouts/platform_h5.html"){
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sports-event-union-action-hint {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #ed6a0c;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sports-event-union-action-hint.is-authorized {
|
||||||
|
color: #1989fa;
|
||||||
|
}
|
||||||
|
|
||||||
.sports-event-page .table-card .table-card-footer .van-cell__value {
|
.sports-event-page .table-card .table-card-footer .van-cell__value {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -700,6 +713,12 @@ layout("/layouts/platform_h5.html"){
|
|||||||
取消报名
|
取消报名
|
||||||
</van-button>
|
</van-button>
|
||||||
</div>
|
</div>
|
||||||
|
<span
|
||||||
|
v-if="row.applyWay.includes(2) && !row.applyWay.includes(1)"
|
||||||
|
class="sports-event-union-action-hint"
|
||||||
|
:class="{'is-authorized': canUnionApplyOnPc}">
|
||||||
|
{{ canUnionApplyOnPc ? '分工会报名请在PC端进行' : '该活动为分工会报名,暂无权限' }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -731,6 +750,12 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<i class="fa fa-trash"></i>取消报名
|
<i class="fa fa-trash"></i>取消报名
|
||||||
</van-button>
|
</van-button>
|
||||||
</div>
|
</div>
|
||||||
|
<span
|
||||||
|
v-if="row.applyWay.includes(2) && !row.applyWay.includes(1)"
|
||||||
|
class="sports-event-union-action-hint"
|
||||||
|
:class="{'is-authorized': canUnionApplyOnPc}">
|
||||||
|
{{ canUnionApplyOnPc ? '分工会报名请在PC端进行' : '该活动为分工会报名,暂无权限' }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -768,6 +793,16 @@ layout("/layouts/platform_h5.html"){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {},
|
components: {},
|
||||||
|
computed: {
|
||||||
|
canUnionApplyOnPc() {
|
||||||
|
return this.$auth.hasRoleOr([
|
||||||
|
"SYSADMIN",
|
||||||
|
"SCHOOL_UNION_ADMIN",
|
||||||
|
"BRANCH_UNION_CHAIRMAN",
|
||||||
|
"BRANCH_UNION_WENTI_SPORTS"
|
||||||
|
]) && this.$auth.hasPermission("activity.apply")
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleViewMode() {
|
toggleViewMode() {
|
||||||
this.viewMode = this.viewMode === "card" ? "list" : "card"
|
this.viewMode = this.viewMode === "card" ? "list" : "card"
|
||||||
|
|||||||
@@ -324,11 +324,37 @@ layout("/layouts/platform_h5.html"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
.brand-course-card__detail:nth-child(-n + 2) .brand-course-card__detail-label,
|
.brand-course-card__detail:nth-child(-n + 2) .brand-course-card__detail-label,
|
||||||
.brand-course-card__detail:nth-child(-n + 2) .brand-course-card__detail-value {
|
.brand-course-card__detail:nth-child(2) .brand-course-card__detail-value {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
overflow-wrap: normal;
|
overflow-wrap: normal;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.brand-course-card__detail:first-child .brand-course-card__detail-value {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-wrap: normal;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-course-contact-popover {
|
||||||
|
display: block;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-course-contact-popover__content {
|
||||||
|
max-width: 240px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
color: #33445a;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.brand-course-card__links {
|
.brand-course-card__links {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@@ -913,7 +939,17 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<van-icon name="friends-o"></van-icon>
|
<van-icon name="friends-o"></van-icon>
|
||||||
<div class="brand-course-card__detail-copy">
|
<div class="brand-course-card__detail-copy">
|
||||||
<div class="brand-course-card__detail-label">联系人</div>
|
<div class="brand-course-card__detail-label">联系人</div>
|
||||||
<div class="brand-course-card__detail-value">{{row.courseInstructor}}</div>
|
<van-popover
|
||||||
|
class="brand-course-contact-popover"
|
||||||
|
:value="contactPopoverIndex === index"
|
||||||
|
placement="top-start"
|
||||||
|
trigger="click"
|
||||||
|
@input="setContactPopover(index, $event)">
|
||||||
|
<div class="brand-course-contact-popover__content">{{row.courseInstructor || '暂无'}}</div>
|
||||||
|
<template #reference>
|
||||||
|
<div class="brand-course-card__detail-value">{{row.courseInstructor || '暂无'}}</div>
|
||||||
|
</template>
|
||||||
|
</van-popover>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="brand-course-card__detail">
|
<div class="brand-course-card__detail">
|
||||||
@@ -1009,6 +1045,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
activeAssort: null,
|
activeAssort: null,
|
||||||
filterOpened: false,
|
filterOpened: false,
|
||||||
viewMode: 'card',
|
viewMode: 'card',
|
||||||
|
contactPopoverIndex: -1,
|
||||||
|
|
||||||
weekdayCNMap: {0: '周日', 1: '周一', 2: '周二', 3: '周三', 4: '周四', 5: '周五', 6: '周六'},
|
weekdayCNMap: {0: '周日', 1: '周一', 2: '周二', 3: '周三', 4: '周四', 5: '周五', 6: '周六'},
|
||||||
}
|
}
|
||||||
@@ -1017,6 +1054,9 @@ layout("/layouts/platform_h5.html"){
|
|||||||
toggleViewMode() {
|
toggleViewMode() {
|
||||||
this.viewMode = this.viewMode === 'card' ? 'list' : 'card'
|
this.viewMode = this.viewMode === 'card' ? 'list' : 'card'
|
||||||
},
|
},
|
||||||
|
setContactPopover(index, visible) {
|
||||||
|
this.contactPopoverIndex = visible ? index : -1
|
||||||
|
},
|
||||||
onAssortChange(name) {
|
onAssortChange(name) {
|
||||||
this.pageForm.assortTypes = name ? JSON.stringify([name]) : []
|
this.pageForm.assortTypes = name ? JSON.stringify([name]) : []
|
||||||
this.doSearch()
|
this.doSearch()
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<button type="button" class="works-mine-card__action" @click="onView(row)">
|
<button type="button" class="works-mine-card__action" @click="onView(row)">
|
||||||
<van-icon name="eye-o"></van-icon>查看
|
<van-icon name="eye-o"></van-icon>查看
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="works-mine-card__action is-primary" @click="onEdit(row)">
|
<button type="button" class="works-mine-card__action" @click="onEdit(row)">
|
||||||
<van-icon name="edit"></van-icon>编辑
|
<van-icon name="edit"></van-icon>编辑
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="works-mine-card__action is-danger" @click="onDelete(row)">
|
<button type="button" class="works-mine-card__action is-danger" @click="onDelete(row)">
|
||||||
@@ -467,7 +467,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
:close-on-click-overlay="false" show-cancel-button cancel-button-text="取消" confirm-button-text="确认删除"
|
:close-on-click-overlay="false" show-cancel-button cancel-button-text="取消" confirm-button-text="确认删除"
|
||||||
@cancel="closeDeleteConfirm" @confirm="confirmDelete"></van-dialog>
|
@cancel="closeDeleteConfirm" @confirm="confirmDelete"></van-dialog>
|
||||||
|
|
||||||
<apply-form ref="applyFormRef"></apply-form>
|
<apply-form ref="applyFormRef" @submit-success="handleEditSubmitSuccess"></apply-form>
|
||||||
<info ref="infoRef"></info>
|
<info ref="infoRef"></info>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -541,6 +541,10 @@ layout("/layouts/platform_h5.html"){
|
|||||||
}
|
}
|
||||||
this.$refs.applyFormRef.onOpenEdit(row)
|
this.$refs.applyFormRef.onOpenEdit(row)
|
||||||
},
|
},
|
||||||
|
// 编辑提交成功后留在我的作品页面,仅刷新当前列表数据。
|
||||||
|
handleEditSubmitSuccess() {
|
||||||
|
this.loadWorks(true)
|
||||||
|
},
|
||||||
onDelete(row) {
|
onDelete(row) {
|
||||||
this.$set(this, 'pendingDeleteRow', row)
|
this.$set(this, 'pendingDeleteRow', row)
|
||||||
window.h5HistoryLayerManager.open('works-collection-mine-delete-confirm')
|
window.h5HistoryLayerManager.open('works-collection-mine-delete-confirm')
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const INFO = {
|
|||||||
<van-sticky>
|
<van-sticky>
|
||||||
<van-nav-bar title="作品详情" left-text="返回" left-arrow @click-left="closeInfo"></van-nav-bar>
|
<van-nav-bar title="作品详情" left-text="返回" left-arrow @click-left="closeInfo"></van-nav-bar>
|
||||||
</van-sticky>
|
</van-sticky>
|
||||||
<div class="works-detail-scroll">
|
<div ref="detailScrollRef" class="works-detail-scroll">
|
||||||
<section class="works-detail-card">
|
<section class="works-detail-card">
|
||||||
<h2 class="works-detail-card__title">基础信息</h2>
|
<h2 class="works-detail-card__title">基础信息</h2>
|
||||||
<van-cell-group :border="false">
|
<van-cell-group :border="false">
|
||||||
@@ -154,6 +154,7 @@ const INFO = {
|
|||||||
onOpen(row) {
|
onOpen(row) {
|
||||||
this.$set(this, "viewData", Object.assign({}, row, {files: this.normalizeFiles(row.files)}))
|
this.$set(this, "viewData", Object.assign({}, row, {files: this.normalizeFiles(row.files)}))
|
||||||
window.h5HistoryLayerManager.open("works-collection-mine-info")
|
window.h5HistoryLayerManager.open("works-collection-mine-info")
|
||||||
|
this.resetScrollPosition()
|
||||||
this.$axios.post("/platform/activity/worksCollection/common/findOne", {id: row.id}).then((res) => {
|
this.$axios.post("/platform/activity/worksCollection/common/findOne", {id: row.id}).then((res) => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
const viewData = res.data || {}
|
const viewData = res.data || {}
|
||||||
@@ -162,6 +163,12 @@ const INFO = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
// 弹窗组件会被复用,每次打开后需清除上一次查看时保留的滚动位置。
|
||||||
|
resetScrollPosition() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.detailScrollRef) this.$refs.detailScrollRef.scrollTop = 0
|
||||||
|
})
|
||||||
|
},
|
||||||
normalizeFiles(files) {
|
normalizeFiles(files) {
|
||||||
if (Array.isArray(files)) return files
|
if (Array.isArray(files)) return files
|
||||||
if (!files) return []
|
if (!files) return []
|
||||||
|
|||||||
@@ -222,24 +222,30 @@ layout("/layouts/platform_h5.html"){
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.works-read-like-tag {
|
.works-read-like-summary {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
min-height: 24px;
|
min-height: 24px;
|
||||||
padding: 3px 9px;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex: none;
|
flex: none;
|
||||||
border: 0;
|
color: #7f8da1;
|
||||||
border-radius: 12px;
|
|
||||||
color: #397de0;
|
|
||||||
background-color: #edf5ff;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
white-space: nowrap;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.works-read-like-tag .van-icon {
|
/* 顶部仅展示累计点赞信息,人员图标与无底色文字用于区别底部可点击的点赞按钮。 */
|
||||||
margin-right: 4px;
|
.works-read-like-summary .van-icon {
|
||||||
font-size: 14px;
|
display: inline-flex;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 5px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #8795aa;
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.works-read-files-row {
|
.works-read-files-row {
|
||||||
@@ -441,8 +447,9 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<div class="works-read-card-title">{{row.name || '未命名作品'}}</div>
|
<div class="works-read-card-title">{{row.name || '未命名作品'}}</div>
|
||||||
<div class="works-read-card-activity">{{row.activityName || '暂无活动信息'}}</div>
|
<div class="works-read-card-activity">{{row.activityName || '暂无活动信息'}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="works-read-like-tag">
|
<div class="works-read-like-summary">
|
||||||
<van-icon name="good-job-o"></van-icon>{{row.num || 0}}
|
<van-icon name="friends"></van-icon>
|
||||||
|
<span>已有 {{row.num || 0}} 人点赞</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+19
-1
@@ -10,7 +10,7 @@ const applyForm = {
|
|||||||
<van-nav-bar title="作品提交" left-text="返回" left-arrow @click-left="closeApply"></van-nav-bar>
|
<van-nav-bar title="作品提交" left-text="返回" left-arrow @click-left="closeApply"></van-nav-bar>
|
||||||
</van-sticky>
|
</van-sticky>
|
||||||
<van-form ref="formRef" class="works-apply-form form-container">
|
<van-form ref="formRef" class="works-apply-form form-container">
|
||||||
<div class="works-apply-scroll">
|
<div ref="applyScrollRef" class="works-apply-scroll">
|
||||||
<section class="works-apply-section">
|
<section class="works-apply-section">
|
||||||
<h2 class="works-apply-section__title">基础信息</h2>
|
<h2 class="works-apply-section__title">基础信息</h2>
|
||||||
<van-cell-group :border="false">
|
<van-cell-group :border="false">
|
||||||
@@ -356,6 +356,7 @@ const applyForm = {
|
|||||||
showOptionPicker: false, optionPopupType: "", optionPopupTitle: "",
|
showOptionPicker: false, optionPopupType: "", optionPopupTitle: "",
|
||||||
optionPopupOptions: [], pendingOptionValue: "",
|
optionPopupOptions: [], pendingOptionValue: "",
|
||||||
submitConfirmVisible: false, fileAccept: null, chooseWorksType: {},
|
submitConfirmVisible: false, fileAccept: null, chooseWorksType: {},
|
||||||
|
submitSuccessMode: "navigate",
|
||||||
historyLayerPageKey: "works-collection-upload-h5",
|
historyLayerPageKey: "works-collection-upload-h5",
|
||||||
historyLayerUnsubscribe: null, ownsHistoryRegistration: false
|
historyLayerUnsubscribe: null, ownsHistoryRegistration: false
|
||||||
}
|
}
|
||||||
@@ -384,16 +385,19 @@ const applyForm = {
|
|||||||
},
|
},
|
||||||
onOpen(row) {
|
onOpen(row) {
|
||||||
this.resetFormData()
|
this.resetFormData()
|
||||||
|
this.$set(this, "submitSuccessMode", "navigate")
|
||||||
this.$set(this, "row", row)
|
this.$set(this, "row", row)
|
||||||
this.$set(this.formData, "activityId", row.id)
|
this.$set(this.formData, "activityId", row.id)
|
||||||
this.activityChange(row.id).then(() => {
|
this.activityChange(row.id).then(() => {
|
||||||
window.h5HistoryLayerManager.open("works-collection-apply")
|
window.h5HistoryLayerManager.open("works-collection-apply")
|
||||||
|
this.resetScrollPosition()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onOpenEdit(row) {
|
onOpenEdit(row) {
|
||||||
const formData = clone(row)
|
const formData = clone(row)
|
||||||
formData.files = this.normalizeFiles(formData.files)
|
formData.files = this.normalizeFiles(formData.files)
|
||||||
this.resetFormData()
|
this.resetFormData()
|
||||||
|
this.$set(this, "submitSuccessMode", "close")
|
||||||
this.$set(this, "row", formData)
|
this.$set(this, "row", formData)
|
||||||
this.$set(this, "formData", formData)
|
this.$set(this, "formData", formData)
|
||||||
const user = this.$store.state.user || {}
|
const user = this.$store.state.user || {}
|
||||||
@@ -408,6 +412,13 @@ const applyForm = {
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.setSelectedWorksType(formData.worksId)
|
this.setSelectedWorksType(formData.worksId)
|
||||||
window.h5HistoryLayerManager.open("works-collection-apply")
|
window.h5HistoryLayerManager.open("works-collection-apply")
|
||||||
|
this.resetScrollPosition()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 表单弹窗会被复用,每次打开后需清除上一次编辑或提交时保留的滚动位置。
|
||||||
|
resetScrollPosition() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.applyScrollRef) this.$refs.applyScrollRef.scrollTop = 0
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 附件可能由列表 SQL 返回 JSON 字符串,也可能已被序列化为数组;统一转换为上传组件需要的数组。
|
// 附件可能由列表 SQL 返回 JSON 字符串,也可能已被序列化为数组;统一转换为上传组件需要的数组。
|
||||||
@@ -505,6 +516,13 @@ const applyForm = {
|
|||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
this.$toast.success("提交成功")
|
this.$toast.success("提交成功")
|
||||||
|
if (this.submitSuccessMode === "close") {
|
||||||
|
// 我的作品页使用弹窗编辑,成功后只关闭当前表单并通知父页面刷新列表。
|
||||||
|
window.h5HistoryLayerManager.close("works-collection-apply", () => {
|
||||||
|
this.$emit("submit-success")
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
window.h5HistoryLayerManager.closeAllAndNavigate("/platform/activity/worksCollection/mine/h5")
|
window.h5HistoryLayerManager.closeAllAndNavigate("/platform/activity/worksCollection/mine/h5")
|
||||||
}
|
}
|
||||||
}).finally(() => { this.$set(this, "formLoading", false) })
|
}).finally(() => { this.$set(this, "formLoading", false) })
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<van-field v-model="formData.tf_opinion" class="club-audit-field" name="tf_opinion" type="textarea" rows="6" label="" placeholder="请输入审批意见" :rules="[{ required: true, message: '请填写审批意见' }]" maxlength="100" show-word-limit></van-field>
|
<van-field v-model="formData.tf_opinion" class="club-audit-field" name="tf_opinion" type="textarea" rows="6" label="" placeholder="请输入审批意见" :rules="[{ required: true, message: '请填写审批意见' }]" maxlength="100" show-word-limit></van-field>
|
||||||
</van-form>
|
</van-form>
|
||||||
</section>
|
</section>
|
||||||
<div class="club-audit-actions"><van-button :loading="formLoading" type="danger" block @click="handleTaskAction(6)">退回</van-button><van-button :loading="formLoading" type="danger" block @click="handleTaskAction(2)">不同意</van-button><van-button :loading="formLoading" type="primary" block @click="handleTaskAction(1)">同意</van-button></div>
|
<div class="club-audit-actions"><van-button :loading="formLoading" type="primary" block @click="handleTaskAction(1)">同意</van-button><van-button :loading="formLoading" type="danger" block @click="handleTaskAction(2)">拒绝</van-button><van-button :loading="formLoading" type="danger" block @click="handleTaskAction(6)">退回</van-button></div>
|
||||||
</template>
|
</template>
|
||||||
</info>
|
</info>
|
||||||
<van-dialog :value="confirmVisible" title="提示" :message="confirmMessage" show-cancel-button :confirm-button-loading="formLoading" @confirm="confirmPendingAction" @cancel="closeConfirm"></van-dialog>
|
<van-dialog :value="confirmVisible" title="提示" :message="confirmMessage" show-cancel-button :confirm-button-loading="formLoading" @confirm="confirmPendingAction" @cancel="closeConfirm"></van-dialog>
|
||||||
|
|||||||
@@ -62,22 +62,48 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</template>
|
</template>
|
||||||
</table-list>
|
</table-list>
|
||||||
</main>
|
</main>
|
||||||
<info ref="infoRef" page-key="club-mineclub" :merge-apply-info="true"></info>
|
<van-popup v-model="clubDetailVisible" position="right" :style="{ width: '100%', height: '100%' }" get-container="#app" class="club-user-join-popup" :close-on-click-overlay="false">
|
||||||
|
<div class="club-user-join-detail">
|
||||||
|
<van-nav-bar title="社团详情" left-text="返回" left-arrow fixed @click-left="closeClubDetail"></van-nav-bar>
|
||||||
|
<div class="club-user-join-detail__scroll">
|
||||||
|
<main class="club-user-join-detail__content">
|
||||||
|
<section class="club-user-join-card club-user-join-info-card">
|
||||||
|
<h3 class="club-user-join-card__title">社团基础信息</h3>
|
||||||
|
<van-cell-group :border="false">
|
||||||
|
<van-cell title="社团名称">{{ clubDetail.clubName || '--' }}</van-cell>
|
||||||
|
<van-cell title="成立时间">{{ clubDetail.foundTime || '--' }}</van-cell>
|
||||||
|
<van-cell title="社团编码">{{ clubDetail.clubCode || '--' }}</van-cell>
|
||||||
|
<van-cell title="社团类型"><dict-tag :options="dict.type.CLUB_REGISTER_TYPE" :value="clubDetail.clubType"></dict-tag></van-cell>
|
||||||
|
<van-cell title="会长">{{ clubDetail.clubLeader || '--' }}</van-cell>
|
||||||
|
<van-cell title="秘书长">{{ clubDetail.clubSecretary || '--' }}</van-cell>
|
||||||
|
<van-cell title="当前人数">{{ clubDetail.currentPeopleNum || 0 }} 人</van-cell>
|
||||||
|
<van-cell title="会费标准">{{ clubDetail.due || '--' }}</van-cell>
|
||||||
|
<van-cell title="社团介绍" class="club-user-join-info-long-cell"><div v-if="hasClubContent(clubDetail.introduce)" v-html="clubDetail.introduce"></div><span v-else>暂无</span></van-cell>
|
||||||
|
<van-cell title="社团宗旨" class="club-user-join-info-long-cell"><div v-if="hasClubContent(clubDetail.purpose)" v-html="clubDetail.purpose"></div><span v-else>暂无</span></van-cell>
|
||||||
|
<van-cell title="社团章程" class="club-user-join-info-long-cell"><file-preview v-if="clubDetail.rulesFile && clubDetail.rulesFile.length" :files="clubDetail.rulesFile" complete_result></file-preview><span v-else>暂无</span></van-cell>
|
||||||
|
<van-cell title="年度活动计划" class="club-user-join-info-long-cell"><file-preview v-if="clubDetail.yearPlanFile && clubDetail.yearPlanFile.length" :files="clubDetail.yearPlanFile" complete_result></file-preview><span v-else>暂无</span></van-cell>
|
||||||
|
</van-cell-group>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-popup>
|
||||||
<van-dialog :value="confirmVisible" title="提示" :message="confirmMessage" show-cancel-button :confirm-button-loading="formLoading" @confirm="confirmExit" @cancel="closeConfirm"></van-dialog>
|
<van-dialog :value="confirmVisible" title="提示" :message="confirmMessage" show-cancel-button :confirm-button-loading="formLoading" @confirm="confirmExit" @cancel="closeConfirm"></van-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script nonce="${cspNonce!}">
|
<script nonce="${cspNonce!}">
|
||||||
<!--#include('../common/clubUserJoin.js'){}#-->
|
|
||||||
new Vue({
|
new Vue({
|
||||||
el: "#app", store, components: {info: clubUserJoin},
|
el: "#app", store, dicts: ['CLUB_REGISTER_TYPE'],
|
||||||
data() { return {pageForm: {pageNumber: 1, pageSize: 10, totalCount: 0, clubName: ''}, listLoading: true, formLoading: false, confirmVisible: false, currentRow: null, historyLayerUnsubscribe: null} },
|
data() { return {pageForm: {pageNumber: 1, pageSize: 10, totalCount: 0, clubName: ''}, listLoading: true, formLoading: false, clubDetailVisible: false, clubDetail: {}, confirmVisible: false, currentRow: null, historyLayerUnsubscribe: null} },
|
||||||
computed: { confirmMessage() { return this.currentRow ? '您确定要申请退出“' + this.currentRow.clubName + '”吗?' : '您确定要申请退出该社团吗?'; } },
|
computed: { confirmMessage() { return this.currentRow ? '您确定要申请退出“' + this.currentRow.clubName + '”吗?' : '您确定要申请退出该社团吗?'; } },
|
||||||
created() { const manager = window.h5HistoryLayerManager; if (manager) { manager.ensureRegistered('club-mineclub'); this.historyLayerUnsubscribe = manager.subscribe((layers) => { this.$set(this, 'confirmVisible', layers.includes('club-mineclub-confirm')); }); } },
|
created() { const manager = window.h5HistoryLayerManager; if (manager) { manager.ensureRegistered('club-mineclub'); this.historyLayerUnsubscribe = manager.subscribe((layers) => { this.$set(this, 'clubDetailVisible', layers.includes('club-mineclub-detail')); this.$set(this, 'confirmVisible', layers.includes('club-mineclub-confirm')); }); } },
|
||||||
beforeDestroy() { if (this.historyLayerUnsubscribe) { this.historyLayerUnsubscribe(); } const manager = window.h5HistoryLayerManager; if (manager) { manager.unregister('club-mineclub'); } },
|
beforeDestroy() { if (this.historyLayerUnsubscribe) { this.historyLayerUnsubscribe(); } const manager = window.h5HistoryLayerManager; if (manager) { manager.unregister('club-mineclub'); } },
|
||||||
methods: {
|
methods: {
|
||||||
onNavBack() { const manager = window.h5HistoryLayerManager; if (manager && manager.stack.length) { manager.close(); return; } historyBack(); },
|
onNavBack() { const manager = window.h5HistoryLayerManager; if (manager && manager.stack.length) { manager.close(); return; } historyBack(); },
|
||||||
onLoadingChange(state) { this.$set(this, 'listLoading', state.loading); },
|
onLoadingChange(state) { this.$set(this, 'listLoading', state.loading); },
|
||||||
onView(row) { this.$refs.infoRef.onOpen(Object.assign({}, row, {id: row.applyId}), '社团申请详情'); },
|
onView(row) { this.$set(this, 'clubDetail', Object.assign({}, row || {})); const manager = window.h5HistoryLayerManager; if (manager) { manager.open('club-mineclub-detail'); return; } this.$set(this, 'clubDetailVisible', true); },
|
||||||
|
closeClubDetail(afterClose) { const manager = window.h5HistoryLayerManager; if (manager) { manager.close('club-mineclub-detail', afterClose); return; } this.$set(this, 'clubDetailVisible', false); if (afterClose) { afterClose(); } },
|
||||||
|
hasClubContent(value) { return value !== null && value !== undefined && String(value).replace(/<[^>]*>/g, '').replace(/ /gi, '').trim().length > 0; },
|
||||||
onExit(row) { this.$set(this, 'currentRow', row); const manager = window.h5HistoryLayerManager; if (manager) { manager.open('club-mineclub-confirm'); return; } this.$set(this, 'confirmVisible', true); },
|
onExit(row) { this.$set(this, 'currentRow', row); const manager = window.h5HistoryLayerManager; if (manager) { manager.open('club-mineclub-confirm'); return; } this.$set(this, 'confirmVisible', true); },
|
||||||
closeConfirm(afterClose) { const manager = window.h5HistoryLayerManager; if (manager) { manager.close('club-mineclub-confirm', afterClose); return; } this.$set(this, 'confirmVisible', false); if (afterClose) { afterClose(); } },
|
closeConfirm(afterClose) { const manager = window.h5HistoryLayerManager; if (manager) { manager.close('club-mineclub-confirm', afterClose); return; } this.$set(this, 'confirmVisible', false); if (afterClose) { afterClose(); } },
|
||||||
confirmExit() { const row = this.currentRow; this.closeConfirm(() => { this.$set(this, 'formLoading', true); this.$axios.post('/platform/club/join/apply/submit', {data: JSON.stringify(row), mode: false}).then((res) => { if (res.code === 0) { this.$toast(res.msg); const manager = window.h5HistoryLayerManager; if (manager) { manager.closeAll(() => { this.$pjaxReplace('/platform/club/join/mine/h5'); }); } else { this.$pjaxReplace('/platform/club/join/mine/h5'); } } }).finally(() => { this.$set(this, 'formLoading', false); }); }); },
|
confirmExit() { const row = this.currentRow; this.closeConfirm(() => { this.$set(this, 'formLoading', true); this.$axios.post('/platform/club/join/apply/submit', {data: JSON.stringify(row), mode: false}).then((res) => { if (res.code === 0) { this.$toast(res.msg); const manager = window.h5HistoryLayerManager; if (manager) { manager.closeAll(() => { this.$pjaxReplace('/platform/club/join/mine/h5'); }); } else { this.$pjaxReplace('/platform/club/join/mine/h5'); } } }).finally(() => { this.$set(this, 'formLoading', false); }); }); },
|
||||||
|
|||||||
+10
-12
@@ -579,9 +579,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</van-action-sheet>
|
</van-action-sheet>
|
||||||
|
|
||||||
<van-field label="性别" v-model="formData.condolenceSex" readonly placeholder="自动带出"></van-field>
|
<van-field label="性别" v-model="formData.condolenceSex" readonly placeholder="自动带出"></van-field>
|
||||||
<van-field label="生日" :value="formatDateText(formData.condolenceBirthday)" readonly placeholder="自动带出"></van-field>
|
|
||||||
<van-field label="身份证号" v-model="formData.condolenceIdCard" readonly placeholder="自动带出"></van-field>
|
|
||||||
<van-field label="联系方式" v-model="formData.condolenceMobile" readonly placeholder="自动带出"></van-field>
|
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
v-model="formData.typeName"
|
v-model="formData.typeName"
|
||||||
@@ -805,8 +802,13 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</van-popup>
|
</van-popup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<van-field class="more-text" name="files" required
|
</van-cell-group>
|
||||||
label="附件" :rules="[{ required: true, message: '请上传附件' }]">
|
</section>
|
||||||
|
<section class="form-section">
|
||||||
|
<h2 class="form-section__title">附件信息</h2>
|
||||||
|
<van-cell-group :border="false">
|
||||||
|
<van-field class="more-text reimburse-upload-field" name="files" required
|
||||||
|
label="" :rules="[{ required: true, message: '请上传附件' }]">
|
||||||
<template #input>
|
<template #input>
|
||||||
<h5-file-upload
|
<h5-file-upload
|
||||||
slot="input"
|
slot="input"
|
||||||
@@ -1003,7 +1005,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</div>
|
</div>
|
||||||
</van-form>
|
</van-form>
|
||||||
</div>
|
</div>
|
||||||
<van-popup v-model:show="invoiceDialogVisible" position="bottom" round :close-on-click-overlay="true" class="invoice-popup">
|
<van-popup v-model:show="invoiceDialogVisible" position="bottom" round :close-on-click-overlay="true" get-container="#app" class="invoice-popup">
|
||||||
<van-nav-bar :title="invoiceDialogMode === 'edit' ? '编辑发票明细' : '新增发票明细'"
|
<van-nav-bar :title="invoiceDialogMode === 'edit' ? '编辑发票明细' : '新增发票明细'"
|
||||||
left-text="取消"
|
left-text="取消"
|
||||||
left-arrow
|
left-arrow
|
||||||
@@ -1013,7 +1015,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<van-loading type="spinner" color="#1677ff" size="24px">发票识别中...</van-loading>
|
<van-loading type="spinner" color="#1677ff" size="24px">发票识别中...</van-loading>
|
||||||
</div>
|
</div>
|
||||||
<van-cell-group class="invoice-popup-card">
|
<van-cell-group class="invoice-popup-card">
|
||||||
<van-field class="more-text" label="发票文件" required>
|
<van-field class="more-text invoice-popup-file-field" label="发票文件" required>
|
||||||
<template #input>
|
<template #input>
|
||||||
<h5-file-upload
|
<h5-file-upload
|
||||||
slot="input"
|
slot="input"
|
||||||
@@ -1407,7 +1409,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
},
|
},
|
||||||
normalizeContactFields() {
|
normalizeContactFields() {
|
||||||
// 提交前裁剪联系方式空格,避免系统手机号带空格时后端格式校验失败。
|
// 提交前裁剪联系方式空格,避免系统手机号带空格时后端格式校验失败。
|
||||||
const fields = ["mobile", "condolenceMobile"]
|
const fields = ["mobile"]
|
||||||
fields.forEach((fieldName) => {
|
fields.forEach((fieldName) => {
|
||||||
if (this.formData[fieldName]) {
|
if (this.formData[fieldName]) {
|
||||||
this.$set(this.formData, fieldName, String(this.formData[fieldName]).trim())
|
this.$set(this.formData, fieldName, String(this.formData[fieldName]).trim())
|
||||||
@@ -2204,7 +2206,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
const selectedUser = this.condolenceOptions.find(user => user.id === o.value)
|
const selectedUser = this.condolenceOptions.find(user => user.id === o.value)
|
||||||
if (selectedUser) {
|
if (selectedUser) {
|
||||||
this.fillCondolenceUser(selectedUser)
|
this.fillCondolenceUser(selectedUser)
|
||||||
this.$set(this.formData, "condolenceMobile", selectedUser.mobile)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCondolenceTypeConfirm(o) {
|
onCondolenceTypeConfirm(o) {
|
||||||
@@ -2336,9 +2337,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
this.$set(this.formData, "condolenceUnionName", user.unionName)
|
this.$set(this.formData, "condolenceUnionName", user.unionName)
|
||||||
this.$set(this.formData, "condolenceUnionCode", user.unionCode)
|
this.$set(this.formData, "condolenceUnionCode", user.unionCode)
|
||||||
this.$set(this.formData, "condolenceSex", user.sex)
|
this.$set(this.formData, "condolenceSex", user.sex)
|
||||||
this.$set(this.formData, "condolenceBirthday", user.birthday)
|
|
||||||
this.$set(this.formData, "condolenceIdCard", user.idCard)
|
|
||||||
this.$set(this.formData, "condolenceMobile", user.mobile)
|
|
||||||
},
|
},
|
||||||
// 查询慰问类型
|
// 查询慰问类型
|
||||||
queryCondolenceType() {
|
queryCondolenceType() {
|
||||||
|
|||||||
+1
-1
@@ -143,7 +143,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
return isNaN(amount) ? value : amount.toFixed(2) + "元"
|
return isNaN(amount) ? value : amount.toFixed(2) + "元"
|
||||||
},
|
},
|
||||||
onView(row) {
|
onView(row) {
|
||||||
this.$refs.unionReimburseInfoRef.onOpen(row, "报销统计详情")
|
this.$refs.unionReimburseInfoRef.onOpen(row, "报销详情")
|
||||||
},
|
},
|
||||||
onEdit(row) {
|
onEdit(row) {
|
||||||
this.$pjaxReplace("/platform/unionReimburse/apply/h5?bizId=" + row.id + "&taskId=" + row.startTaskId)
|
this.$pjaxReplace("/platform/unionReimburse/apply/h5?bizId=" + row.id + "&taskId=" + row.startTaskId)
|
||||||
|
|||||||
+18
@@ -258,6 +258,15 @@
|
|||||||
background: #f7f9fd;
|
background: #f7f9fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 附件上传区域与困难帮扶申请页保持一致,避免上传控件被文本域样式包裹。 */
|
||||||
|
.union-reimburse-form .form-section .reimburse-upload-field .van-field__control {
|
||||||
|
min-height: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.union-reimburse-form .payment-section,
|
.union-reimburse-form .payment-section,
|
||||||
.union-reimburse-form .invoice-section { padding: 0 0 14px; }
|
.union-reimburse-form .invoice-section { padding: 0 0 14px; }
|
||||||
.union-reimburse-form .payment-toolbar,
|
.union-reimburse-form .payment-toolbar,
|
||||||
@@ -388,6 +397,15 @@
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background: #f7f9fd;
|
background: #f7f9fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 发票文件只保留上传控件本身,移除报销文本域的边框、背景和内边距。 */
|
||||||
|
.union-reimburse-form .invoice-popup-card .more-text.invoice-popup-file-field .van-field__control {
|
||||||
|
min-height: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
.union-reimburse-form .invoice-popup-tip {
|
.union-reimburse-form .invoice-popup-tip {
|
||||||
margin: 10px 0 0;
|
margin: 10px 0 0;
|
||||||
padding: 9px 11px;
|
padding: 9px 11px;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const UNION_REIMBURSE_INFO = {
|
|||||||
</div>
|
</div>
|
||||||
<div class="union-info-row">
|
<div class="union-info-row">
|
||||||
<div class="union-info-label">经办人</div>
|
<div class="union-info-label">经办人</div>
|
||||||
<div class="union-info-value union-info-link">{{ viewData.userName }}</div>
|
<div class="union-info-value">{{ viewData.userName }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="union-info-row">
|
<div class="union-info-row">
|
||||||
<div class="union-info-label">联系方式</div>
|
<div class="union-info-label">联系方式</div>
|
||||||
@@ -100,18 +100,6 @@ const UNION_REIMBURSE_INFO = {
|
|||||||
<div class="union-info-label">性别</div>
|
<div class="union-info-label">性别</div>
|
||||||
<div class="union-info-value">{{ viewData.condolenceSex }}</div>
|
<div class="union-info-value">{{ viewData.condolenceSex }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="union-info-row">
|
|
||||||
<div class="union-info-label">生日</div>
|
|
||||||
<div class="union-info-value">{{ formatDateText(viewData.condolenceBirthday) }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="union-info-row">
|
|
||||||
<div class="union-info-label">身份证号</div>
|
|
||||||
<div class="union-info-value">{{ viewData.condolenceIdCard }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="union-info-row">
|
|
||||||
<div class="union-info-label">联系方式</div>
|
|
||||||
<div class="union-info-value">{{ viewData.condolenceMobile }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="union-info-row">
|
<div class="union-info-row">
|
||||||
<div class="union-info-label">慰问类型</div>
|
<div class="union-info-label">慰问类型</div>
|
||||||
<div class="union-info-value">{{ viewData.typeName || viewData.condolenceTypeName }}</div>
|
<div class="union-info-value">{{ viewData.typeName || viewData.condolenceTypeName }}</div>
|
||||||
@@ -186,7 +174,7 @@ const UNION_REIMBURSE_INFO = {
|
|||||||
<div class="union-info-section-title">活动信息</div>
|
<div class="union-info-section-title">活动信息</div>
|
||||||
<div class="union-info-row">
|
<div class="union-info-row">
|
||||||
<div class="union-info-label">活动名称</div>
|
<div class="union-info-label">活动名称</div>
|
||||||
<div class="union-info-value union-info-link">{{ viewData.activityName }}</div>
|
<div class="union-info-value">{{ viewData.activityName }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="union-info-row">
|
<div class="union-info-row">
|
||||||
<div class="union-info-label">活动人数</div>
|
<div class="union-info-label">活动人数</div>
|
||||||
@@ -346,17 +334,6 @@ const UNION_REIMBURSE_INFO = {
|
|||||||
return this.viewData.paymentDetails || []
|
return this.viewData.paymentDetails || []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
visible(value) {
|
|
||||||
const manager = window.h5HistoryLayerManager
|
|
||||||
if (!manager) return
|
|
||||||
if (value) {
|
|
||||||
manager.open(this.historyLayerName)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
manager.close(this.historyLayerName)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.injectInfoStyle()
|
this.injectInfoStyle()
|
||||||
const manager = window.h5HistoryLayerManager
|
const manager = window.h5HistoryLayerManager
|
||||||
@@ -386,12 +363,19 @@ const UNION_REIMBURSE_INFO = {
|
|||||||
onOpen(row, title) {
|
onOpen(row, title) {
|
||||||
this.$set(this, "row", row)
|
this.$set(this, "row", row)
|
||||||
this.$set(this, "detailTitle", title || "报销详情")
|
this.$set(this, "detailTitle", title || "报销详情")
|
||||||
this.$set(this, "visible", true)
|
|
||||||
this.$set(this, "currentPaymentIndex", 0)
|
this.$set(this, "currentPaymentIndex", 0)
|
||||||
|
// 详情弹层的显示状态由全局历史栈统一同步,确保系统返回键与页面返回按钮关闭同一层。
|
||||||
|
const manager = window.h5HistoryLayerManager
|
||||||
|
if (manager && manager.open(this.historyLayerName)) {
|
||||||
|
this.getInfo()
|
||||||
|
this.getDoneTasks()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$set(this, "visible", true)
|
||||||
this.getInfo()
|
this.getInfo()
|
||||||
this.getDoneTasks()
|
this.getDoneTasks()
|
||||||
},
|
},
|
||||||
// 由全局历史栈关闭详情,保证遮罩、关闭按钮和浏览器返回键遵循同一顺序。
|
// 只关闭当前详情对应的历史层,避免误关闭审核确认弹框等上层业务弹框。
|
||||||
onClose() {
|
onClose() {
|
||||||
const manager = window.h5HistoryLayerManager
|
const manager = window.h5HistoryLayerManager
|
||||||
if (manager && manager.close(this.historyLayerName)) return
|
if (manager && manager.close(this.historyLayerName)) return
|
||||||
|
|||||||
+1
-1
@@ -144,7 +144,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : ""
|
return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : ""
|
||||||
},
|
},
|
||||||
onView(row) {
|
onView(row) {
|
||||||
this.$refs.unionReimburseInfoRef.onOpen(row, "报销申请详情")
|
this.$refs.unionReimburseInfoRef.onOpen(row, "报销详情")
|
||||||
},
|
},
|
||||||
onEdit(row) {
|
onEdit(row) {
|
||||||
this.$pjaxReplace('/platform/unionReimburse/apply/h5?taskId=' + (row.startTaskId || '') + "&bizId=" + row.id)
|
this.$pjaxReplace('/platform/unionReimburse/apply/h5?taskId=' + (row.startTaskId || '') + "&bizId=" + row.id)
|
||||||
|
|||||||
+1
-1
@@ -166,7 +166,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
submitTaskAction() { const submitType = this.pendingSubmitType; this.closeSubmitConfirm(() => { this.$set(this, "formLoading", true); this.$axios.post("/platform/unionReimburse/review/reviewTask", {data: JSON.stringify(Object.assign({}, this.formData)), submitType: submitType}).then((res) => { if (res.code === 0) { this.$toast.success("提交成功"); this.doSearch(); this.$refs.unionReimburseInfoRef.onClose() } else { this.$toast.fail(res.msg) } }).finally(() => { this.$set(this, "formLoading", false) }) }) },
|
submitTaskAction() { const submitType = this.pendingSubmitType; this.closeSubmitConfirm(() => { this.$set(this, "formLoading", true); this.$axios.post("/platform/unionReimburse/review/reviewTask", {data: JSON.stringify(Object.assign({}, this.formData)), submitType: submitType}).then((res) => { if (res.code === 0) { this.$toast.success("提交成功"); this.doSearch(); this.$refs.unionReimburseInfoRef.onClose() } else { this.$toast.fail(res.msg) } }).finally(() => { this.$set(this, "formLoading", false) }) }) },
|
||||||
onView(row) {
|
onView(row) {
|
||||||
this.$set(this, "showApprovalForm", false)
|
this.$set(this, "showApprovalForm", false)
|
||||||
this.$refs.unionReimburseInfoRef.onOpen(row, "报销申请详情")
|
this.$refs.unionReimburseInfoRef.onOpen(row, "报销详情")
|
||||||
},
|
},
|
||||||
onAudit(row) {
|
onAudit(row) {
|
||||||
this.$set(this, "showApprovalForm", true)
|
this.$set(this, "showApprovalForm", true)
|
||||||
|
|||||||
@@ -82,6 +82,22 @@
|
|||||||
background-color: #ff976a;
|
background-color: #ff976a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 校工会预审核四个操作按钮与 PC 端 Element UI 的 primary、danger、info 配色保持一致。 */
|
||||||
|
.proposal-pre-audit-actions .proposal-pre-audit-button--primary {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-pre-audit-actions .proposal-pre-audit-button--danger {
|
||||||
|
border-color: #f56c6c;
|
||||||
|
background-color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-pre-audit-actions .proposal-pre-audit-button--info {
|
||||||
|
border-color: #909399;
|
||||||
|
background-color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
.proposal-soft-pagination .van-pagination__item {
|
.proposal-soft-pagination .van-pagination__item {
|
||||||
color: var(--proposal-soft-primary);
|
color: var(--proposal-soft-primary);
|
||||||
background-color: #f8fbff;
|
background-color: #f8fbff;
|
||||||
@@ -280,11 +296,18 @@
|
|||||||
.proposal-view-card .van-cell__title {
|
.proposal-view-card .van-cell__title {
|
||||||
flex: 0 0 92px;
|
flex: 0 0 92px;
|
||||||
width: 92px;
|
width: 92px;
|
||||||
color: #596779;
|
color: #354255;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 纵向字段中 flex-basis 会转为标题高度,需要取消横向字段使用的 92px 固定占位。 */
|
||||||
|
.proposal-view-card .direction-column-field .van-cell__title {
|
||||||
|
flex: none;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.proposal-view-card .van-cell__value {
|
.proposal-view-card .van-cell__value {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
color: #4f5d70;
|
color: #4f5d70;
|
||||||
@@ -495,7 +518,7 @@
|
|||||||
|
|
||||||
.proposal-view-table-wrap .el-table {
|
.proposal-view-table-wrap .el-table {
|
||||||
min-width: 560px;
|
min-width: 560px;
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.proposal-view-slot:empty {
|
.proposal-view-slot:empty {
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ const PROPOSAL_INFO = {
|
|||||||
:value="viewData.caseFilingType"></dict-tag>
|
:value="viewData.caseFilingType"></dict-tag>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell title="提案类型">{{ viewData.typeName || '--' }}</van-cell>
|
<van-cell title="提案类型">{{ viewData.typeName || '--' }}</van-cell>
|
||||||
<van-cell title="提案方式">{{ getDictText(dict.type.PROPOSAL_SOURCE, viewData.source) }}</van-cell>
|
<van-cell title="提案方式">{{ getDictText(dict.type.PROPOSAL_SOURCE, viewData.source) }}
|
||||||
|
</van-cell>
|
||||||
<van-cell title="代表姓名">{{ viewData.createUserName || '--' }}</van-cell>
|
<van-cell title="代表姓名">{{ viewData.createUserName || '--' }}</van-cell>
|
||||||
<van-cell title="所属教代会">{{ viewData.sessionName || viewData.fullName || '--' }}</van-cell>
|
<van-cell title="所属教代会">{{ viewData.sessionName || viewData.fullName || '--' }}</van-cell>
|
||||||
<van-cell :title="viewData.committeeName ? '所属委员会' : '所属代表团'">
|
<van-cell :title="viewData.committeeName ? '所属委员会' : '所属代表团'">
|
||||||
@@ -28,7 +29,7 @@ const PROPOSAL_INFO = {
|
|||||||
<van-cell title="单位">{{ viewData.unitName || '--' }}</van-cell>
|
<van-cell title="单位">{{ viewData.unitName || '--' }}</van-cell>
|
||||||
<van-cell title="联系电话">{{ viewData.mobile || '--' }}</van-cell>
|
<van-cell title="联系电话">{{ viewData.mobile || '--' }}</van-cell>
|
||||||
<van-cell title="提案时间">{{ viewData.createTime || '--' }}</van-cell>
|
<van-cell title="提案时间">{{ viewData.createTime || '--' }}</van-cell>
|
||||||
<van-cell title="提案案由" class="proposal-view-long-cell">
|
<van-cell title="案由" class="proposal-view-long-cell">
|
||||||
<div class="proposal-view-rich-value" v-html="viewData.brief || '--'"></div>
|
<div class="proposal-view-rich-value" v-html="viewData.brief || '--'"></div>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell title="建议措施" class="proposal-view-long-cell">
|
<van-cell title="建议措施" class="proposal-view-long-cell">
|
||||||
@@ -62,7 +63,9 @@ const PROPOSAL_INFO = {
|
|||||||
<h2 class="proposal-view-card__title">委员会成员意见 {{ index + 1 }}</h2>
|
<h2 class="proposal-view-card__title">委员会成员意见 {{ index + 1 }}</h2>
|
||||||
<van-cell-group :border="false">
|
<van-cell-group :border="false">
|
||||||
<van-cell title="委员">
|
<van-cell title="委员">
|
||||||
{{ opinion.commissionerName || '--' }}<template v-if="opinion.commissionerLoginName">({{ opinion.commissionerLoginName }})</template>
|
{{ opinion.commissionerName || '--' }}
|
||||||
|
<template v-if="opinion.commissionerLoginName">({{ opinion.commissionerLoginName }})
|
||||||
|
</template>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell title="立案结果">
|
<van-cell title="立案结果">
|
||||||
<dict-tag :options="dict.type.PROPOSAL_CASE_FILING_RESULT"
|
<dict-tag :options="dict.type.PROPOSAL_CASE_FILING_RESULT"
|
||||||
@@ -109,12 +112,12 @@ const PROPOSAL_INFO = {
|
|||||||
|
|
||||||
<!-- 点击时间轴节点后,只在此处展示该任务原有的完整详情。 -->
|
<!-- 点击时间轴节点后,只在此处展示该任务原有的完整详情。 -->
|
||||||
<section class="proposal-view-card proposal-flow-detail" v-if="selectedTask">
|
<section class="proposal-view-card proposal-flow-detail" v-if="selectedTask">
|
||||||
<h2 class="proposal-view-card__title">节点详情</h2>
|
<h2 class="proposal-view-card__title">{{ selectedTask.displayName || '--' }}</h2>
|
||||||
<h3 class="proposal-flow-detail__name">{{ selectedTask.displayName || '--' }}</h3>
|
|
||||||
<van-cell-group :border="false">
|
<van-cell-group :border="false">
|
||||||
<template v-if="selectedTask.ext && selectedTask.ext.isFirstTaskNode">
|
<template v-if="selectedTask.ext && selectedTask.ext.isFirstTaskNode">
|
||||||
<van-cell title="申请用户">
|
<van-cell title="申请用户">
|
||||||
{{ selectedTask.ext.initiatorName || '--' }}({{ selectedTask.ext.initiatorAccount || '--' }})
|
{{ selectedTask.ext.initiatorName || '--' }}({{ selectedTask.ext.initiatorAccount ||
|
||||||
|
'--' }})
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell title="申请时间">{{ selectedTask.finishTime || '--' }}</van-cell>
|
<van-cell title="申请时间">{{ selectedTask.finishTime || '--' }}</van-cell>
|
||||||
<van-cell title="办理结果">
|
<van-cell title="办理结果">
|
||||||
@@ -125,26 +128,35 @@ const PROPOSAL_INFO = {
|
|||||||
|
|
||||||
<template v-else-if="selectedTask.taskName === 'invite'">
|
<template v-else-if="selectedTask.taskName === 'invite'">
|
||||||
<van-cell title="办理用户">
|
<van-cell title="办理用户">
|
||||||
{{ selectedTask.taskFormData.userName || '--' }}({{ selectedTask.taskFormData.loginName || '--' }})
|
{{ selectedTask.taskFormData.userName || '--' }}({{ selectedTask.taskFormData.loginName
|
||||||
|
|| '--' }})
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell title="办理时间">{{ selectedTask.finishTime || '--' }}</van-cell>
|
<van-cell title="办理时间">{{ selectedTask.finishTime || '--' }}</van-cell>
|
||||||
<van-cell title="附议人" class="proposal-view-long-cell">
|
<van-cell title="附议人" class="proposal-view-long-cell">
|
||||||
<div class="proposal-view-table-wrap">
|
<div class="proposal-view-table-wrap">
|
||||||
<el-table :data="selectedTask?.taskFormData?.seconder">
|
<el-table :data="selectedTask?.taskFormData?.seconder">
|
||||||
<el-table-column label="姓名" prop="userName"></el-table-column>
|
<el-table-column label="姓名" prop="userName" width="80px"
|
||||||
<el-table-column label="工号" prop="loginName"></el-table-column>
|
header-align="center" align="center"></el-table-column>
|
||||||
<el-table-column label="性别" prop="sex"></el-table-column>
|
<el-table-column label="工号" prop="loginName" width="80px"
|
||||||
<el-table-column label="单位" prop="unitName" show-overflow-tooltip></el-table-column>
|
header-align="center" align="center"></el-table-column>
|
||||||
<el-table-column label="分工会" prop="unionName" show-overflow-tooltip></el-table-column>
|
<el-table-column label="性别" prop="sex" width="50px" header-align="center"
|
||||||
<el-table-column label="代表团" prop="delegationName" show-overflow-tooltip></el-table-column>
|
align="center"></el-table-column>
|
||||||
|
<el-table-column label="单位" prop="unitName"
|
||||||
|
show-overflow-tooltip header-align="center"
|
||||||
|
align="center"></el-table-column>
|
||||||
|
<el-table-column label="代表团" prop="delegationName"
|
||||||
|
show-overflow-tooltip header-align="center"
|
||||||
|
align="center"></el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="selectedTask.taskName === 'committee' || selectedTask.taskName === 'committeeFilingUnit'">
|
<template
|
||||||
|
v-else-if="selectedTask.taskName === 'committee' || selectedTask.taskName === 'committeeFilingUnit'">
|
||||||
<van-cell title="办理用户">
|
<van-cell title="办理用户">
|
||||||
{{ selectedTask.taskFormData.userName || '--' }}({{ selectedTask.taskFormData.loginName || '--' }})
|
{{ selectedTask.taskFormData.userName || '--' }}({{ selectedTask.taskFormData.loginName
|
||||||
|
|| '--' }})
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell title="办理时间">{{ selectedTask.finishTime || '--' }}</van-cell>
|
<van-cell title="办理时间">{{ selectedTask.finishTime || '--' }}</van-cell>
|
||||||
<van-cell title="立案结果">
|
<van-cell title="立案结果">
|
||||||
@@ -155,19 +167,23 @@ const PROPOSAL_INFO = {
|
|||||||
<dict-tag :options="dict.type.PROPOSAL_CASE_FILING_TYPE"
|
<dict-tag :options="dict.type.PROPOSAL_CASE_FILING_TYPE"
|
||||||
:value="selectedTask.ext.tf_caseFilingType"></dict-tag>
|
:value="selectedTask.ext.tf_caseFilingType"></dict-tag>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<template v-if="['CONFIRM_FILING', 'SUGGESTION'].includes(selectedTask.ext.tf_caseFilingResult)">
|
<template
|
||||||
|
v-if="['CONFIRM_FILING', 'SUGGESTION'].includes(selectedTask.ext.tf_caseFilingResult)">
|
||||||
<van-cell v-if="selectedTask.ext.tf_caseFilingResult === 'SUGGESTION'" title="承办单位">
|
<van-cell v-if="selectedTask.ext.tf_caseFilingResult === 'SUGGESTION'" title="承办单位">
|
||||||
{{ selectedTask?.ext?.tf_masterUnitNameStr || selectedTask?.ext?.tf_slaveUnitNameStr || '--' }}
|
{{ selectedTask?.ext?.tf_masterUnitNameStr || selectedTask?.ext?.tf_slaveUnitNameStr
|
||||||
|
|| '--' }}
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<van-cell title="主办单位">{{ selectedTask?.ext?.tf_masterUnitNameStr || '--' }}</van-cell>
|
<van-cell title="主办单位">{{ selectedTask?.ext?.tf_masterUnitNameStr || '--' }}
|
||||||
<van-cell title="协办单位">{{ selectedTask?.ext?.tf_slaveUnitNameStr || '--' }}</van-cell>
|
</van-cell>
|
||||||
|
<van-cell title="协办单位">{{ selectedTask?.ext?.tf_slaveUnitNameStr || '--' }}
|
||||||
|
</van-cell>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<van-cell title="办理意见"
|
<van-cell title="办理意见"
|
||||||
v-if="selectedTask.taskFormData.tf_opinion"
|
v-if="selectedTask.taskFormData.tf_opinion">
|
||||||
class="proposal-view-long-cell">
|
<div class="proposal-view-rich-value"
|
||||||
<div class="proposal-view-rich-value" v-html="selectedTask.taskFormData.tf_opinion"></div>
|
v-html="selectedTask.taskFormData.tf_opinion"></div>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -177,7 +193,8 @@ const PROPOSAL_INFO = {
|
|||||||
{{ selectedTask.ext.underTakeName || '--' }}
|
{{ selectedTask.ext.underTakeName || '--' }}
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell title="办理用户">
|
<van-cell title="办理用户">
|
||||||
{{ selectedTask.taskFormData.userName || '--' }}({{ selectedTask.taskFormData.loginName || '--' }})
|
{{ selectedTask.taskFormData.userName || '--' }}({{ selectedTask.taskFormData.loginName
|
||||||
|
|| '--' }})
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell title="办理时间">{{ selectedTask.finishTime || '--' }}</van-cell>
|
<van-cell title="办理时间">{{ selectedTask.finishTime || '--' }}</van-cell>
|
||||||
<van-cell title="办理评价" v-if="selectedTask.taskName === 'feedback'">
|
<van-cell title="办理评价" v-if="selectedTask.taskName === 'feedback'">
|
||||||
@@ -189,9 +206,9 @@ const PROPOSAL_INFO = {
|
|||||||
:value="selectedTask.ext.submitType"></dict-tag>
|
:value="selectedTask.ext.submitType"></dict-tag>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell title="办理意见"
|
<van-cell title="办理意见"
|
||||||
v-if="selectedTask.taskFormData && selectedTask.taskFormData.tf_opinion"
|
v-if="selectedTask.taskFormData && selectedTask.taskFormData.tf_opinion">
|
||||||
class="proposal-view-long-cell">
|
<div class="proposal-view-rich-value"
|
||||||
<div class="proposal-view-rich-value" v-html="selectedTask.taskFormData.tf_opinion"></div>
|
v-html="selectedTask.taskFormData.tf_opinion"></div>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
</template>
|
</template>
|
||||||
</van-cell-group>
|
</van-cell-group>
|
||||||
|
|||||||
+386
-72
@@ -1,114 +1,389 @@
|
|||||||
const PROPOSAL_TASK_POPUP = {
|
const PROPOSAL_TASK_POPUP = {
|
||||||
template: /*language=HTML*/ `
|
template: /*language=HTML*/ `
|
||||||
<van-popup v-model="visible" position="right" class="proposal-full-popup" :close-on-click-overlay="false" get-container="#app">
|
<van-popup v-model="visible" position="right" class="proposal-full-popup" :close-on-click-overlay="false"
|
||||||
|
get-container="#app">
|
||||||
<div class="proposal-full-popup__page">
|
<div class="proposal-full-popup__page">
|
||||||
<van-nav-bar @click-left="close" left-arrow left-text="返回" :title="title" placeholder fixed></van-nav-bar>
|
<van-nav-bar @click-left="close" left-arrow left-text="返回" :title="title" placeholder
|
||||||
|
fixed></van-nav-bar>
|
||||||
<div class="proposal-full-popup__scroll">
|
<div class="proposal-full-popup__scroll">
|
||||||
<proposal-info v-if="bizId" :biz-id="bizId" :visible="visible">
|
<proposal-info v-if="bizId" :biz-id="bizId" :visible="visible">
|
||||||
<div v-if="isApproval">
|
<div v-if="isApproval">
|
||||||
<van-form ref="formRef">
|
<van-form ref="formRef">
|
||||||
<template v-if="kind === 'delegation' || kind === 'preAudit' || kind === 'schoolLeaderApproval'">
|
<template
|
||||||
|
v-if="kind === 'delegation' || kind === 'preAudit' || kind === 'schoolLeaderApproval'">
|
||||||
<section class="proposal-view-card">
|
<section class="proposal-view-card">
|
||||||
<h2 class="proposal-view-card__title">审批意见<span class="proposal-audit-card__required">*</span></h2>
|
<h2 class="proposal-view-card__title">审批意见<span
|
||||||
<van-field v-model="formData.tf_opinion" class="proposal-audit-field" name="tf_opinion" type="textarea" rows="6" label="" placeholder="请输入审批意见" :rules="[{required:true,message:'请填写审批意见'}]" required maxlength="100" show-word-limit></van-field>
|
class="proposal-audit-card__required">*</span></h2>
|
||||||
|
<van-field v-model="formData.tf_opinion" class="proposal-audit-field"
|
||||||
|
name="tf_opinion" type="textarea" rows="6" label=""
|
||||||
|
placeholder="请输入审批意见"
|
||||||
|
:rules="[{required:true,message:'请填写审批意见'}]" required
|
||||||
|
maxlength="100" show-word-limit></van-field>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="kind === 'feedbackEvaluation'">
|
<template v-if="kind === 'feedbackEvaluation'">
|
||||||
<van-field v-model="formData.tf_feedback_name" label="满意度" placeholder="请选择满意度" :rules="[{required:true,message:'请选择满意度'}]" required readonly is-link @click="openLayer('feedback-picker')"></van-field>
|
|
||||||
<section class="proposal-view-card">
|
<section class="proposal-view-card">
|
||||||
<h2 class="proposal-view-card__title">审批意见<span class="proposal-audit-card__required">*</span></h2>
|
|
||||||
<van-field v-model="formData.tf_opinion" class="proposal-audit-field" name="tf_opinion" type="textarea" rows="6" label="" placeholder="请输入审批意见" :rules="[{required:true,message:'请填写审批意见'}]" required maxlength="100" show-word-limit></van-field>
|
<h2 class="proposal-view-card__title">反馈评价<span
|
||||||
|
class="proposal-audit-card__required">*</span></h2>
|
||||||
|
<van-field v-model="formData.tf_feedback_name" label="满意度"
|
||||||
|
placeholder="请选择满意度"
|
||||||
|
:rules="[{required:true,message:'请选择满意度'}]" required readonly
|
||||||
|
is-link @click="openLayer('feedback-picker')"></van-field>
|
||||||
|
<van-field v-model="formData.tf_opinion" class="proposal-audit-field"
|
||||||
|
name="tf_opinion" type="textarea" rows="6" label=""
|
||||||
|
placeholder="请输入反馈意见"
|
||||||
|
:rules="[{required:true,message:'请填写反馈意见'}]"
|
||||||
|
maxlength="100" show-word-limit></van-field>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="kind === 'suggestion'">
|
<template v-if="kind === 'suggestion'">
|
||||||
<van-field v-model="formData.result" label="承办意向" placeholder="请选择承办意向" :rules="[{required:true,message:'请选择承办意向'}]" required readonly is-link @click="openLayer('result-picker')"></van-field>
|
|
||||||
<section class="proposal-view-card">
|
<section class="proposal-view-card">
|
||||||
<h2 class="proposal-view-card__title">反馈意见<span class="proposal-audit-card__required">*</span></h2>
|
<van-field v-model="formData.result" label="承办意向"
|
||||||
<van-field v-model="formData.opinion" class="proposal-audit-field" name="opinion" type="textarea" rows="6" label="" placeholder="请输入反馈意见" :rules="[{required:true,message:'请输入反馈意见'}]" required show-word-limit></van-field>
|
placeholder="请选择承办意向"
|
||||||
|
:rules="[{required:true,message:'请选择承办意向'}]" required readonly
|
||||||
|
is-link @click="openLayer('result-picker')"></van-field>
|
||||||
|
<h2 class="proposal-view-card__title">反馈意见<span
|
||||||
|
class="proposal-audit-card__required">*</span></h2>
|
||||||
|
<van-field v-model="formData.opinion" class="proposal-audit-field"
|
||||||
|
name="opinion" type="textarea" rows="6" label=""
|
||||||
|
placeholder="请输入反馈意见"
|
||||||
|
:rules="[{required:true,message:'请输入反馈意见'}]" required
|
||||||
|
show-word-limit></van-field>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="kind === 'unitReply'">
|
<template v-if="kind === 'unitReply'">
|
||||||
<section class="proposal-view-card">
|
<section class="proposal-view-card">
|
||||||
<van-field :value="formData.underTakeName" label="承办单位" readonly></van-field>
|
<h2 class="proposal-view-card__title">承办单位答复<span
|
||||||
<van-field v-model="formData.tf_implementStateName" label="落实情况" placeholder="请选择落实情况" :rules="[{required:true,message:'请选择落实情况'}]" required readonly is-link @click="openLayer('implement-picker')"></van-field>
|
class="proposal-audit-card__required">*</span></h2>
|
||||||
<h2 class="proposal-view-card__title">答复内容<span class="proposal-audit-card__required">*</span></h2>
|
<van-field :value="formData.underTakeName" label="承办单位"
|
||||||
<van-field v-model="formData.tf_opinion" class="proposal-audit-field" name="tf_opinion" type="textarea" rows="6" label="" placeholder="请输入答复内容" :rules="[{required:true,message:'请填写答复内容'}]" required maxlength="100" show-word-limit></van-field>
|
readonly></van-field>
|
||||||
|
<van-field v-model="formData.tf_implementStateName" label="落实情况"
|
||||||
|
placeholder="请选择落实情况"
|
||||||
|
:rules="[{required:true,message:'请选择落实情况'}]" required readonly
|
||||||
|
is-link @click="openLayer('implement-picker')"></van-field>
|
||||||
|
<!-- 承办单位答复附件上传入口暂时停用,保留公用上传组件配置便于后续恢复。 -->
|
||||||
|
<!-- <van-field label="附件" name="tf_attachment" class="direction-column-field">-->
|
||||||
|
<!-- <template #input>-->
|
||||||
|
<!-- <h5-file-upload-->
|
||||||
|
<!-- :value.sync="formData.tf_attachment"-->
|
||||||
|
<!-- :upload_number="5"-->
|
||||||
|
<!-- upload_mode="file"-->
|
||||||
|
<!-- upload_result_category="array"-->
|
||||||
|
<!-- upload_result_type="url"-->
|
||||||
|
<!-- complete_result>-->
|
||||||
|
<!-- </h5-file-upload>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </van-field>-->
|
||||||
|
<van-field v-model="formData.tf_opinion" class="proposal-audit-field"
|
||||||
|
name="tf_opinion" type="textarea" rows="6" label=""
|
||||||
|
placeholder="请输入答复内容"
|
||||||
|
:rules="[{required:true,message:'请填写答复内容'}]"
|
||||||
|
maxlength="100" show-word-limit></van-field>
|
||||||
</section>
|
</section>
|
||||||
<van-field v-if="supportCandidate && formData.underTakeIsMaster" v-model="formData.tf_nextNodeOperatorName" label="校领导" placeholder="请选择校领导" :rules="[{required:true,message:'请选择校领导'}]" required readonly is-link @click="openLayer('candidate-picker')"></van-field>
|
<van-field v-if="supportCandidate && formData.underTakeIsMaster"
|
||||||
|
v-model="formData.tf_nextNodeOperatorName" label="校领导"
|
||||||
|
placeholder="请选择校领导"
|
||||||
|
:rules="[{required:true,message:'请选择校领导'}]" required readonly
|
||||||
|
is-link @click="openLayer('candidate-picker')"></van-field>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="kind === 'caseCheck'">
|
<template v-if="kind === 'caseCheck'">
|
||||||
<van-field :value="formData.tf_username" label="审核人" readonly></van-field>
|
<van-field :value="formData.tf_username" label="审核人" readonly></van-field>
|
||||||
<van-field :value="formData.tf_auditTime" label="审核时间" readonly></van-field>
|
<van-field :value="formData.tf_auditTime" label="审核时间" readonly></van-field>
|
||||||
<van-field :value="formData.tf_caseCheckNeedSecondReply" label="二次答复" required :rules="[{validator:validateSecondReply,message:'请选择是否需要二次答复'}]">
|
<van-field :value="formData.tf_caseCheckNeedSecondReply" label="二次答复" required
|
||||||
<template #input><van-radio-group v-model="formData.tf_caseCheckNeedSecondReply" direction="horizontal" @change="onSecondReplyChange"><van-radio :name="0">无需二次答复</van-radio><van-radio :name="1">需要二次答复</van-radio></van-radio-group></template>
|
:rules="[{validator:validateSecondReply,message:'请选择是否需要二次答复'}]">
|
||||||
|
<template #input>
|
||||||
|
<van-radio-group v-model="formData.tf_caseCheckNeedSecondReply"
|
||||||
|
direction="horizontal" @change="onSecondReplyChange">
|
||||||
|
<van-radio :name="0">无需二次答复</van-radio>
|
||||||
|
<van-radio :name="1">需要二次答复</van-radio>
|
||||||
|
</van-radio-group>
|
||||||
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
<van-field v-if="formData.tf_caseCheckNeedSecondReply === 1" :value="selectedReplyUnitNames" label="答复单位" placeholder="请选择办理单位" readonly required is-link :rules="[{validator:validateSecondReplyUnits,message:'请选择需要二次答复的办理单位'}]" @click="openReplyUnitPicker"></van-field>
|
<van-field v-if="formData.tf_caseCheckNeedSecondReply === 1"
|
||||||
|
:value="selectedReplyUnitNames" label="答复单位"
|
||||||
|
placeholder="请选择办理单位" readonly required is-link
|
||||||
|
:rules="[{validator:validateSecondReplyUnits,message:'请选择需要二次答复的办理单位'}]"
|
||||||
|
@click="openReplyUnitPicker"></van-field>
|
||||||
<section class="proposal-view-card">
|
<section class="proposal-view-card">
|
||||||
<h2 class="proposal-view-card__title">审批意见<span class="proposal-audit-card__required">*</span></h2>
|
<h2 class="proposal-view-card__title">审批意见<span
|
||||||
<van-field v-model="formData.tf_opinion" class="proposal-audit-field" name="tf_opinion" type="textarea" rows="6" label="" placeholder="请输入审批意见" required maxlength="100" show-word-limit :rules="[{required:true,message:'请填写审批意见'}]"></van-field>
|
class="proposal-audit-card__required">*</span></h2>
|
||||||
|
<van-field v-model="formData.tf_opinion" class="proposal-audit-field"
|
||||||
|
name="tf_opinion" type="textarea" rows="6" label=""
|
||||||
|
placeholder="请输入审批意见" required maxlength="100" show-word-limit
|
||||||
|
:rules="[{required:true,message:'请填写审批意见'}]"></van-field>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
</van-form>
|
</van-form>
|
||||||
<div class="proposal-popup-form-actions" v-if="kind === 'delegation'">
|
<div class="proposal-popup-form-actions" v-if="kind === 'delegation'">
|
||||||
<van-button type="danger" block :loading="formLoading" class="proposal-soft-button proposal-soft-button--danger" @click="prepareSubmit(6)">退回</van-button><van-button type="danger" block :loading="formLoading" class="proposal-soft-button proposal-soft-button--danger" @click="prepareSubmit(2)">不同意</van-button><van-button type="primary" block :loading="formLoading" class="proposal-soft-button" @click="prepareSubmit(1)">同意</van-button>
|
<van-button type="primary" block :loading="formLoading" class="proposal-soft-button"
|
||||||
|
@click="prepareSubmit(1)">同意
|
||||||
|
</van-button>
|
||||||
|
<van-button type="danger" block :loading="formLoading"
|
||||||
|
class="proposal-soft-button proposal-soft-button--danger"
|
||||||
|
@click="prepareSubmit(2)">不同意
|
||||||
|
</van-button>
|
||||||
|
<van-button type="danger" block :loading="formLoading"
|
||||||
|
class="proposal-soft-button proposal-soft-button--danger"
|
||||||
|
@click="prepareSubmit(6)">退回
|
||||||
|
</van-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="proposal-popup-form-actions" v-else-if="kind === 'preAudit'" style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr))">
|
<div class="proposal-popup-form-actions proposal-pre-audit-actions" v-else-if="kind === 'preAudit'"
|
||||||
<van-button type="warning" :loading="formLoading" class="proposal-soft-button proposal-soft-button--warning" @click="prepareSubmit(4)">退回(不审核)</van-button><van-button type="warning" :loading="formLoading" class="proposal-soft-button proposal-soft-button--warning" @click="prepareSubmit(6)">退回(审核)</van-button><van-button type="danger" :loading="formLoading" class="proposal-soft-button proposal-soft-button--danger" @click="prepareSubmit(2)">不同意</van-button><van-button type="primary" :loading="formLoading" class="proposal-soft-button" @click="prepareSubmit(1)">同意</van-button>
|
style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr))">
|
||||||
|
<van-button type="primary" :loading="formLoading"
|
||||||
|
class="proposal-soft-button proposal-pre-audit-button--primary"
|
||||||
|
@click="prepareSubmit(1)">同意
|
||||||
|
</van-button>
|
||||||
|
<van-button type="danger" :loading="formLoading"
|
||||||
|
class="proposal-soft-button proposal-pre-audit-button--danger"
|
||||||
|
@click="prepareSubmit(2)">不同意
|
||||||
|
</van-button>
|
||||||
|
<van-button type="info" :loading="formLoading"
|
||||||
|
class="proposal-soft-button proposal-pre-audit-button--info"
|
||||||
|
@click="prepareSubmit(4)">退回(不审核)
|
||||||
|
</van-button>
|
||||||
|
<van-button type="info" :loading="formLoading"
|
||||||
|
class="proposal-soft-button proposal-pre-audit-button--info"
|
||||||
|
@click="prepareSubmit(6)">退回(审核)
|
||||||
|
</van-button>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="proposal-popup-form-actions" v-else-if="kind === 'schoolLeaderApproval'">
|
||||||
|
<van-button type="primary" block :loading="formLoading" class="proposal-soft-button"
|
||||||
|
@click="prepareSubmit('agree')">同意答复
|
||||||
|
</van-button>
|
||||||
|
<van-button type="danger" block :loading="formLoading"
|
||||||
|
class="proposal-soft-button proposal-soft-button--danger"
|
||||||
|
@click="prepareSubmit('back')">退回答复
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
<div class="proposal-popup-form-actions" v-else>
|
||||||
|
<van-button type="primary" block :loading="formLoading" class="proposal-soft-button"
|
||||||
|
@click="prepareSubmit(1)">提交
|
||||||
|
</van-button>
|
||||||
|
<van-button block :disabled="formLoading" class="proposal-rounded-button"
|
||||||
|
@click="close">返回
|
||||||
|
</van-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="proposal-popup-form-actions" v-else-if="kind === 'schoolLeaderApproval'"><van-button type="danger" block :loading="formLoading" class="proposal-soft-button proposal-soft-button--danger" @click="prepareSubmit('back')">退回答复</van-button><van-button type="primary" block :loading="formLoading" class="proposal-soft-button" @click="prepareSubmit('agree')">同意答复</van-button></div>
|
|
||||||
<div class="proposal-popup-form-actions" v-else><van-button block :disabled="formLoading" class="proposal-rounded-button" @click="close">取消</van-button><van-button type="primary" block :loading="formLoading" class="proposal-soft-button" @click="prepareSubmit(1)">提交</van-button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</proposal-info>
|
</proposal-info>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 字典通过异步请求加载,初始化阶段可能为 null;使用空数组保证弹层首次渲染不执行 null.map。 -->
|
<!-- 字典通过异步请求加载,初始化阶段可能为 null;使用空数组保证弹层首次渲染不执行 null.map。 -->
|
||||||
<van-popup v-model="feedbackPickerVisible" position="bottom" :close-on-click-overlay="false"><van-picker show-toolbar :columns="(dict.type.PROPOSAL_FEEDBACK || []).map((item) => ({text:item.label,value:item.code}))" @confirm="confirmFeedback" @cancel="closeLayer('feedback-picker')"></van-picker></van-popup>
|
<van-popup v-model="feedbackPickerVisible" position="bottom" :close-on-click-overlay="false">
|
||||||
<van-popup v-model="resultPickerVisible" position="bottom" :close-on-click-overlay="false"><van-picker show-toolbar :columns="(dict.type.UNDERTAKE_SUGGESTION || []).map((item) => item.code)" @confirm="confirmResult" @cancel="closeLayer('result-picker')"></van-picker></van-popup>
|
<van-picker show-toolbar
|
||||||
<van-popup v-model="implementPickerVisible" position="bottom" :close-on-click-overlay="false"><van-picker show-toolbar :columns="(dict.type.PROPOSAL_REPLY_IMPLEMENT || []).map((item) => ({text:item.label,value:item.code}))" @confirm="confirmImplement" @cancel="closeLayer('implement-picker')"></van-picker></van-popup>
|
:columns="(dict.type.PROPOSAL_FEEDBACK || []).map((item) => ({text:item.label,value:item.code}))"
|
||||||
<van-popup v-model="candidatePickerVisible" position="bottom" :close-on-click-overlay="false"><van-picker show-toolbar :columns="candidates.map((item) => ({text:item.userName,value:item.userId}))" @confirm="confirmCandidate" @cancel="closeLayer('candidate-picker')"></van-picker></van-popup>
|
@confirm="confirmFeedback" @cancel="closeLayer('feedback-picker')"></van-picker>
|
||||||
<van-popup v-model="replyUnitPickerVisible" position="bottom" round :close-on-click-overlay="false" :style="{height:'70%'}">
|
</van-popup>
|
||||||
<div class="reply-unit-popup"><div class="reply-unit-toolbar"><button type="button" @click="closeLayer('reply-unit-picker')">取消</button><div class="reply-unit-toolbar-title">选择答复单位</div><button type="button" @click="confirmReplyUnits">确定</button></div>
|
<van-popup v-model="resultPickerVisible" position="bottom" :close-on-click-overlay="false">
|
||||||
<div class="reply-unit-list" v-if="replyUnitOptions.length"><van-checkbox-group v-model="pendingReplyUnitIds"><van-cell v-for="item in replyUnitOptions" :key="item.unitId" clickable :title="item.unitName" :label="item.isMaster ? '主办单位' : '协办单位'" @click="toggleReplyUnit(item)"><template #right-icon><van-checkbox :name="item.unitId" @click.stop></van-checkbox></template></van-cell></van-checkbox-group></div>
|
<van-picker show-toolbar :columns="(dict.type.UNDERTAKE_SUGGESTION || []).map((item) => item.code)"
|
||||||
|
@confirm="confirmResult" @cancel="closeLayer('result-picker')"></van-picker>
|
||||||
|
</van-popup>
|
||||||
|
<van-popup v-model="implementPickerVisible" position="bottom" :close-on-click-overlay="false">
|
||||||
|
<van-picker show-toolbar
|
||||||
|
:columns="(dict.type.PROPOSAL_REPLY_IMPLEMENT || []).map((item) => ({text:item.label,value:item.code}))"
|
||||||
|
@confirm="confirmImplement" @cancel="closeLayer('implement-picker')"></van-picker>
|
||||||
|
</van-popup>
|
||||||
|
<van-popup v-model="candidatePickerVisible" position="bottom" :close-on-click-overlay="false">
|
||||||
|
<van-picker show-toolbar :columns="candidates.map((item) => ({text:item.userName,value:item.userId}))"
|
||||||
|
@confirm="confirmCandidate" @cancel="closeLayer('candidate-picker')"></van-picker>
|
||||||
|
</van-popup>
|
||||||
|
<van-popup v-model="replyUnitPickerVisible" position="bottom" round :close-on-click-overlay="false"
|
||||||
|
:style="{height:'70%'}">
|
||||||
|
<div class="reply-unit-popup">
|
||||||
|
<div class="reply-unit-toolbar">
|
||||||
|
<button type="button" @click="closeLayer('reply-unit-picker')">取消</button>
|
||||||
|
<div class="reply-unit-toolbar-title">选择答复单位</div>
|
||||||
|
<button type="button" @click="confirmReplyUnits">确定</button>
|
||||||
|
</div>
|
||||||
|
<div class="reply-unit-list" v-if="replyUnitOptions.length">
|
||||||
|
<van-checkbox-group v-model="pendingReplyUnitIds">
|
||||||
|
<van-cell v-for="item in replyUnitOptions" :key="item.unitId" clickable
|
||||||
|
:title="item.unitName" :label="item.isMaster ? '主办单位' : '协办单位'"
|
||||||
|
@click="toggleReplyUnit(item)">
|
||||||
|
<template #right-icon>
|
||||||
|
<van-checkbox :name="item.unitId" @click.stop></van-checkbox>
|
||||||
|
</template>
|
||||||
|
</van-cell>
|
||||||
|
</van-checkbox-group>
|
||||||
|
</div>
|
||||||
<div class="reply-unit-empty" v-else>暂无可选办理单位</div>
|
<div class="reply-unit-empty" v-else>暂无可选办理单位</div>
|
||||||
</div>
|
</div>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
<van-dialog :value="confirmVisible" title="提示" message="您确定要提交吗?" show-cancel-button @confirm="submit" @cancel="closeLayer('submit-confirm')"></van-dialog>
|
<van-dialog :value="confirmVisible" title="提示" message="您确定要提交吗?" show-cancel-button
|
||||||
|
@confirm="submit" @cancel="closeLayer('submit-confirm')"></van-dialog>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
`,
|
`,
|
||||||
dicts: ["PROPOSAL_FEEDBACK", "UNDERTAKE_SUGGESTION", "PROPOSAL_REPLY_IMPLEMENT"],
|
dicts: ["PROPOSAL_FEEDBACK", "UNDERTAKE_SUGGESTION", "PROPOSAL_REPLY_IMPLEMENT"],
|
||||||
components: {"proposal-info": PROPOSAL_INFO},
|
components: {"proposal-info": PROPOSAL_INFO},
|
||||||
props: {kind: {type: String, required: true}, pageKey: {type: String, required: true}},
|
props: {kind: {type: String, required: true}, pageKey: {type: String, required: true}},
|
||||||
data() { return {visible:false,bizId:"",action:"view",formData:{},formLoading:false,pendingSubmitType:1,feedbackPickerVisible:false,resultPickerVisible:false,implementPickerVisible:false,candidatePickerVisible:false,replyUnitPickerVisible:false,confirmVisible:false,supportCandidate:false,candidates:[],replyUnitOptions:[],pendingReplyUnitIds:[],unsubscribeHistoryLayer:null} },
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
bizId: "",
|
||||||
|
action: "view",
|
||||||
|
formData: {},
|
||||||
|
formLoading: false,
|
||||||
|
pendingSubmitType: 1,
|
||||||
|
feedbackPickerVisible: false,
|
||||||
|
resultPickerVisible: false,
|
||||||
|
implementPickerVisible: false,
|
||||||
|
candidatePickerVisible: false,
|
||||||
|
replyUnitPickerVisible: false,
|
||||||
|
confirmVisible: false,
|
||||||
|
supportCandidate: false,
|
||||||
|
candidates: [],
|
||||||
|
replyUnitOptions: [],
|
||||||
|
pendingReplyUnitIds: [],
|
||||||
|
unsubscribeHistoryLayer: null
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isApproval() { return this.action === "approval" },
|
isApproval() {
|
||||||
title() { const titles={delegation:"团长审核",preAudit:"校工会预审核",schoolLeaderApproval:"分管校领导审批",feedbackEvaluation:"反馈评价",suggestion:"承办单位意见",unitReply:"承办单位答复",caseCheck:"提案委员会审查"}; return this.isApproval ? titles[this.kind] : "提案详情" },
|
return this.action === "approval"
|
||||||
selectedReplyUnitNames() { const ids=this.formData.tf_secondReplyUnitIds || []; return this.replyUnitOptions.filter((item) => ids.indexOf(item.unitId)>-1).map((item) => item.unitName+"("+(item.isMaster?"主办":"协办")+")").join("、") }
|
},
|
||||||
|
title() {
|
||||||
|
const titles = {
|
||||||
|
delegation: "团长审核",
|
||||||
|
preAudit: "校工会预审核",
|
||||||
|
schoolLeaderApproval: "分管校领导审批",
|
||||||
|
feedbackEvaluation: "反馈评价",
|
||||||
|
suggestion: "承办单位意见",
|
||||||
|
unitReply: "承办单位答复",
|
||||||
|
caseCheck: "提案委员会审查"
|
||||||
|
};
|
||||||
|
return this.isApproval ? titles[this.kind] : "提案详情"
|
||||||
|
},
|
||||||
|
selectedReplyUnitNames() {
|
||||||
|
const ids = this.formData.tf_secondReplyUnitIds || [];
|
||||||
|
return this.replyUnitOptions.filter((item) => ids.indexOf(item.unitId) > -1).map((item) => item.unitName + "(" + (item.isMaster ? "主办" : "协办") + ")").join("、")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
syncLayers(stack) { this.$set(this,"visible",stack.includes("detail")); this.$set(this,"feedbackPickerVisible",stack.includes("feedback-picker")); this.$set(this,"resultPickerVisible",stack.includes("result-picker")); this.$set(this,"implementPickerVisible",stack.includes("implement-picker")); this.$set(this,"candidatePickerVisible",stack.includes("candidate-picker")); this.$set(this,"replyUnitPickerVisible",stack.includes("reply-unit-picker")); this.$set(this,"confirmVisible",stack.includes("submit-confirm")) },
|
syncLayers(stack) {
|
||||||
openLayer(name) { window.h5HistoryLayerManager.open(name) }, closeLayer(name,callback) { window.h5HistoryLayerManager.close(name,callback) }, close() { this.closeLayer("detail") },
|
this.$set(this, "visible", stack.includes("detail"));
|
||||||
|
this.$set(this, "feedbackPickerVisible", stack.includes("feedback-picker"));
|
||||||
|
this.$set(this, "resultPickerVisible", stack.includes("result-picker"));
|
||||||
|
this.$set(this, "implementPickerVisible", stack.includes("implement-picker"));
|
||||||
|
this.$set(this, "candidatePickerVisible", stack.includes("candidate-picker"));
|
||||||
|
this.$set(this, "replyUnitPickerVisible", stack.includes("reply-unit-picker"));
|
||||||
|
this.$set(this, "confirmVisible", stack.includes("submit-confirm"))
|
||||||
|
},
|
||||||
|
openLayer(name) {
|
||||||
|
window.h5HistoryLayerManager.open(name)
|
||||||
|
}, closeLayer(name, callback) {
|
||||||
|
window.h5HistoryLayerManager.close(name, callback)
|
||||||
|
}, close() {
|
||||||
|
this.closeLayer("detail")
|
||||||
|
},
|
||||||
/** row 提供详情主键和当前 WF 任务参数,action 为 view 或 approval。 */
|
/** row 提供详情主键和当前 WF 任务参数,action 为 view 或 approval。 */
|
||||||
open(row,action) {
|
open(row, action) {
|
||||||
const defaultTaskNames={delegation:"团长审核",preAudit:"校工会预审核",feedbackEvaluation:"反馈评价",unitReply:"承办单位答复",caseCheck:"提案委员会审查"}
|
const defaultTaskNames = {
|
||||||
const taskName=this.kind === "unitReply" ? (row.taskName || defaultTaskNames[this.kind]) : (row.curTaskName || row.taskName || defaultTaskNames[this.kind] || "")
|
delegation: "团长审核",
|
||||||
this.$set(this,"bizId",row.id); this.$set(this,"action",action); this.$set(this,"formData",{proposalId:row.id,processTaskId:row.taskId || "",taskKey:row.taskKey || "",taskName:taskName,instanceId:row.instanceId || "",underTakeName:row.underTakeName || "",underTakeIsMaster:!!row.underTakeIsMaster,tf_opinion:"",opinion:""})
|
preAudit: "校工会预审核",
|
||||||
if (action === "approval" && this.kind === "unitReply") this.loadCandidates(row.taskId || "")
|
feedbackEvaluation: "反馈评价",
|
||||||
if (action === "approval" && this.kind === "caseCheck") { this.$set(this.formData,"tf_auditTime",moment().format("YYYY-MM-DD")); this.$set(this.formData,"tf_username",(this.$store.state.user && this.$store.state.user.username) || ""); this.$set(this.formData,"tf_caseCheckNeedSecondReply",0); this.$set(this.formData,"tf_secondReplyUnitIds",[]); this.loadReplyUnits(row.id) }
|
unitReply: "承办单位答复",
|
||||||
|
caseCheck: "提案委员会审查"
|
||||||
|
}
|
||||||
|
const taskName = this.kind === "unitReply" ? (row.taskName || defaultTaskNames[this.kind]) : (row.curTaskName || row.taskName || defaultTaskNames[this.kind] || "")
|
||||||
|
this.$set(this, "bizId", row.id);
|
||||||
|
this.$set(this, "action", action);
|
||||||
|
this.$set(this, "formData", {
|
||||||
|
proposalId: row.id,
|
||||||
|
processTaskId: row.taskId || "",
|
||||||
|
taskKey: row.taskKey || "",
|
||||||
|
taskName: taskName,
|
||||||
|
instanceId: row.instanceId || "",
|
||||||
|
underTakeName: row.underTakeName || "",
|
||||||
|
underTakeIsMaster: !!row.underTakeIsMaster,
|
||||||
|
tf_opinion: "",
|
||||||
|
opinion: ""
|
||||||
|
})
|
||||||
|
if (action === "approval" && this.kind === "unitReply") {
|
||||||
|
// 承办单位答复附件上传入口暂时停用,保留初始化代码便于后续恢复。
|
||||||
|
// this.$set(this.formData, "tf_attachment", [])
|
||||||
|
this.loadCandidates(row.taskId || "")
|
||||||
|
}
|
||||||
|
if (action === "approval" && this.kind === "caseCheck") {
|
||||||
|
this.$set(this.formData, "tf_auditTime", moment().format("YYYY-MM-DD"));
|
||||||
|
this.$set(this.formData, "tf_username", (this.$store.state.user && this.$store.state.user.username) || "");
|
||||||
|
this.$set(this.formData, "tf_caseCheckNeedSecondReply", 0);
|
||||||
|
this.$set(this.formData, "tf_secondReplyUnitIds", []);
|
||||||
|
this.loadReplyUnits(row.id)
|
||||||
|
}
|
||||||
this.openLayer("detail")
|
this.openLayer("detail")
|
||||||
},
|
},
|
||||||
loadCandidates(taskId) { this.$axios.post("/flow/common/candidate",{taskId:taskId}).then((res) => { if (res.code === 0) { this.$set(this,"candidates",res.data.candidates || []); this.$set(this,"supportCandidate",res.data.support) } }) },
|
loadCandidates(taskId) {
|
||||||
confirmFeedback(value) { this.$set(this.formData,"tf_feedback_name",value.text); this.$set(this.formData,"tf_feedback",value.value); this.closeLayer("feedback-picker") },
|
this.$axios.post("/flow/common/candidate", {taskId: taskId}).then((res) => {
|
||||||
confirmResult(value) { this.$set(this.formData,"result",value); this.closeLayer("result-picker") },
|
if (res.code === 0) {
|
||||||
confirmImplement(value) { this.$set(this.formData,"tf_implementStateName",value.text); this.$set(this.formData,"tf_implementState",value.value); this.closeLayer("implement-picker") },
|
this.$set(this, "candidates", res.data.candidates || []);
|
||||||
confirmCandidate(value) { this.$set(this.formData,"tf_nextNodeOperatorName",value.text); this.$set(this.formData,"tf_nextNodeOperator",value.value); this.closeLayer("candidate-picker") },
|
this.$set(this, "supportCandidate", res.data.support)
|
||||||
validateSecondReply(value) { return value === 0 || value === 1 },
|
}
|
||||||
validateSecondReplyUnits() { return this.formData.tf_caseCheckNeedSecondReply !== 1 || (this.formData.tf_secondReplyUnitIds || []).length > 0 },
|
})
|
||||||
onSecondReplyChange(value) { if (value !== 1) this.$set(this.formData,"tf_secondReplyUnitIds",[]) },
|
},
|
||||||
loadReplyUnits(proposalId) { this.$axios.post("/platform/proposal/case/check/listReplyUnits",{proposalId:proposalId}).then((res) => { if (res && res.code === 0) this.$set(this,"replyUnitOptions",res.data || []) }) },
|
confirmFeedback(value) {
|
||||||
openReplyUnitPicker() { this.$set(this,"pendingReplyUnitIds",[...(this.formData.tf_secondReplyUnitIds || [])]); this.openLayer("reply-unit-picker") },
|
this.$set(this.formData, "tf_feedback_name", value.text);
|
||||||
toggleReplyUnit(item) { const ids=[...this.pendingReplyUnitIds]; const index=ids.indexOf(item.unitId); if (index>-1) ids.splice(index,1); else ids.push(item.unitId); this.$set(this,"pendingReplyUnitIds",ids) },
|
this.$set(this.formData, "tf_feedback", value.value);
|
||||||
confirmReplyUnits() { this.$set(this.formData,"tf_secondReplyUnitIds",[...this.pendingReplyUnitIds]); this.closeLayer("reply-unit-picker") },
|
this.closeLayer("feedback-picker")
|
||||||
buildCaseCheckData() { const data={...this.formData}; const units=data.tf_caseCheckNeedSecondReply === 1 ? this.replyUnitOptions.filter((item) => data.tf_secondReplyUnitIds.indexOf(item.unitId)>-1) : []; const masters=units.filter((item) => item.isMaster); const slaves=units.filter((item) => !item.isMaster); data.tf_masterUnitIds=masters.map((item) => item.unitId); data.tf_masterUnitId=data.tf_masterUnitIds[0] || ""; data.tf_masterUnitNames=masters.map((item) => item.unitName); data.tf_masterUnitName=data.tf_masterUnitNames[0] || ""; data.tf_masterUnitNameStr=data.tf_masterUnitNames.join(","); data.tf_slaveUnitIds=slaves.map((item) => item.unitId); data.tf_slaveUnitNames=slaves.map((item) => item.unitName); data.tf_slaveUnitNameStr=data.tf_slaveUnitNames.join(","); return data },
|
},
|
||||||
|
confirmResult(value) {
|
||||||
|
this.$set(this.formData, "result", value);
|
||||||
|
this.closeLayer("result-picker")
|
||||||
|
},
|
||||||
|
confirmImplement(value) {
|
||||||
|
this.$set(this.formData, "tf_implementStateName", value.text);
|
||||||
|
this.$set(this.formData, "tf_implementState", value.value);
|
||||||
|
this.closeLayer("implement-picker")
|
||||||
|
},
|
||||||
|
confirmCandidate(value) {
|
||||||
|
this.$set(this.formData, "tf_nextNodeOperatorName", value.text);
|
||||||
|
this.$set(this.formData, "tf_nextNodeOperator", value.value);
|
||||||
|
this.closeLayer("candidate-picker")
|
||||||
|
},
|
||||||
|
validateSecondReply(value) {
|
||||||
|
return value === 0 || value === 1
|
||||||
|
},
|
||||||
|
validateSecondReplyUnits() {
|
||||||
|
return this.formData.tf_caseCheckNeedSecondReply !== 1 || (this.formData.tf_secondReplyUnitIds || []).length > 0
|
||||||
|
},
|
||||||
|
onSecondReplyChange(value) {
|
||||||
|
if (value !== 1) this.$set(this.formData, "tf_secondReplyUnitIds", [])
|
||||||
|
},
|
||||||
|
loadReplyUnits(proposalId) {
|
||||||
|
this.$axios.post("/platform/proposal/case/check/listReplyUnits", {proposalId: proposalId}).then((res) => {
|
||||||
|
if (res && res.code === 0) this.$set(this, "replyUnitOptions", res.data || [])
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openReplyUnitPicker() {
|
||||||
|
this.$set(this, "pendingReplyUnitIds", [...(this.formData.tf_secondReplyUnitIds || [])]);
|
||||||
|
this.openLayer("reply-unit-picker")
|
||||||
|
},
|
||||||
|
toggleReplyUnit(item) {
|
||||||
|
const ids = [...this.pendingReplyUnitIds];
|
||||||
|
const index = ids.indexOf(item.unitId);
|
||||||
|
if (index > -1) ids.splice(index, 1); else ids.push(item.unitId);
|
||||||
|
this.$set(this, "pendingReplyUnitIds", ids)
|
||||||
|
},
|
||||||
|
confirmReplyUnits() {
|
||||||
|
this.$set(this.formData, "tf_secondReplyUnitIds", [...this.pendingReplyUnitIds]);
|
||||||
|
this.closeLayer("reply-unit-picker")
|
||||||
|
},
|
||||||
|
buildCaseCheckData() {
|
||||||
|
const data = {...this.formData};
|
||||||
|
const units = data.tf_caseCheckNeedSecondReply === 1 ? this.replyUnitOptions.filter((item) => data.tf_secondReplyUnitIds.indexOf(item.unitId) > -1) : [];
|
||||||
|
const masters = units.filter((item) => item.isMaster);
|
||||||
|
const slaves = units.filter((item) => !item.isMaster);
|
||||||
|
data.tf_masterUnitIds = masters.map((item) => item.unitId);
|
||||||
|
data.tf_masterUnitId = data.tf_masterUnitIds[0] || "";
|
||||||
|
data.tf_masterUnitNames = masters.map((item) => item.unitName);
|
||||||
|
data.tf_masterUnitName = data.tf_masterUnitNames[0] || "";
|
||||||
|
data.tf_masterUnitNameStr = data.tf_masterUnitNames.join(",");
|
||||||
|
data.tf_slaveUnitIds = slaves.map((item) => item.unitId);
|
||||||
|
data.tf_slaveUnitNames = slaves.map((item) => item.unitName);
|
||||||
|
data.tf_slaveUnitNameStr = data.tf_slaveUnitNames.join(",");
|
||||||
|
return data
|
||||||
|
},
|
||||||
prepareSubmit(value) {
|
prepareSubmit(value) {
|
||||||
// 各办理页面提交前统一校验意见字段;空白字符按未填写处理,提示后不再打开确认弹框。
|
// 各办理页面提交前统一校验意见字段;空白字符按未填写处理,提示后不再打开确认弹框。
|
||||||
const opinionConfig = this.kind === "suggestion"
|
const opinionConfig = this.kind === "suggestion"
|
||||||
@@ -120,24 +395,63 @@ const PROPOSAL_TASK_POPUP = {
|
|||||||
this.$toast(opinionConfig.message)
|
this.$toast(opinionConfig.message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.$refs.formRef.validate().then(() => { this.$set(this,"pendingSubmitType",value); this.openLayer("submit-confirm") }).catch(() => {})
|
this.$refs.formRef.validate().then(() => {
|
||||||
|
this.$set(this, "pendingSubmitType", value);
|
||||||
|
this.openLayer("submit-confirm")
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
},
|
},
|
||||||
/** 根据页面类型保留原接口和参数分支,成功后清理全部弹层历史并通知列表刷新。 */
|
/** 根据页面类型保留原接口和参数分支,成功后清理全部弹层历史并通知列表刷新。 */
|
||||||
submit() {
|
submit() {
|
||||||
this.$set(this,"formLoading",true)
|
this.$set(this, "formLoading", true)
|
||||||
let request
|
let request
|
||||||
if (this.kind === "suggestion") request=this.$axios.post("/platform/proposal/suggestion/suggest",this.formData)
|
if (this.kind === "suggestion") request = this.$axios.post("/platform/proposal/suggestion/suggest", this.formData)
|
||||||
else if (this.kind === "schoolLeaderApproval") { const data={...this.formData,tf_approval:this.pendingSubmitType}; delete data.taskKey; delete data.taskName; request=this.$axios.post("/platform/proposal/schoolLeaderApproval/executeTask",{data:JSON.stringify(data)}) }
|
else if (this.kind === "schoolLeaderApproval") {
|
||||||
else {
|
const data = {...this.formData, tf_approval: this.pendingSubmitType};
|
||||||
const data=this.kind === "caseCheck" ? {...this.buildCaseCheckData(),submitType:this.pendingSubmitType} : {...this.formData,submitType:this.pendingSubmitType}
|
delete data.taskKey;
|
||||||
if (this.kind === "preAudit") { data.taskName="startTask"; if (this.pendingSubmitType === 4) { data.tf_sourceTaskKey=this.formData.taskKey; data.tf_audit=false } }
|
delete data.taskName;
|
||||||
if (this.kind === "unitReply") { delete data.underTakeIsMaster; delete data.underTakeName }
|
request = this.$axios.post("/platform/proposal/schoolLeaderApproval/executeTask", {data: JSON.stringify(data)})
|
||||||
if (this.kind === "feedbackEvaluation" && data.tf_feedback === "DISSATISFIED") request=this.$axios.post("/platform/proposal/feedbackEvaluation/caseInfo",{instId:data.instanceId}).then((res) => { if (res.code !== 0) return Promise.reject(new Error(res.msg || "查询立案信息失败")); data.tf_masterUnitId=res.data.tf_masterUnitId; return this.$axios.post("/flow/common/executeTask",{data:JSON.stringify(data)}) })
|
} else {
|
||||||
else request=this.$axios.post("/flow/common/executeTask",{data:JSON.stringify(data)})
|
const data = this.kind === "caseCheck" ? {
|
||||||
|
...this.buildCaseCheckData(),
|
||||||
|
submitType: this.pendingSubmitType
|
||||||
|
} : {...this.formData, submitType: this.pendingSubmitType}
|
||||||
|
if (this.kind === "preAudit") {
|
||||||
|
data.taskName = "startTask";
|
||||||
|
if (this.pendingSubmitType === 4) {
|
||||||
|
data.tf_sourceTaskKey = this.formData.taskKey;
|
||||||
|
data.tf_audit = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.kind === "unitReply") {
|
||||||
|
delete data.underTakeIsMaster;
|
||||||
|
delete data.underTakeName
|
||||||
|
}
|
||||||
|
if (this.kind === "feedbackEvaluation" && data.tf_feedback === "DISSATISFIED") request = this.$axios.post("/platform/proposal/feedbackEvaluation/caseInfo", {instId: data.instanceId}).then((res) => {
|
||||||
|
if (res.code !== 0) return Promise.reject(new Error(res.msg || "查询立案信息失败"));
|
||||||
|
data.tf_masterUnitId = res.data.tf_masterUnitId;
|
||||||
|
return this.$axios.post("/flow/common/executeTask", {data: JSON.stringify(data)})
|
||||||
|
})
|
||||||
|
else request = this.$axios.post("/flow/common/executeTask", {data: JSON.stringify(data)})
|
||||||
}
|
}
|
||||||
request.then((res) => { if (res && res.code === 0) { this.$toast.success(res.msg); window.h5HistoryLayerManager.closeAll(() => this.$emit("success")) } }).catch((error) => { if (error && error.message && error.message !== "cancel") this.$toast.fail(error.message) }).finally(() => { this.$set(this,"formLoading",false) })
|
request.then((res) => {
|
||||||
|
if (res && res.code === 0) {
|
||||||
|
this.$toast.success(res.msg);
|
||||||
|
window.h5HistoryLayerManager.closeAll(() => this.$emit("success"))
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
if (error && error.message && error.message !== "cancel") this.$toast.fail(error.message)
|
||||||
|
}).finally(() => {
|
||||||
|
this.$set(this, "formLoading", false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() { window.h5HistoryLayerManager.ensureRegistered(this.pageKey); this.$set(this,"unsubscribeHistoryLayer",window.h5HistoryLayerManager.subscribe((stack) => this.syncLayers(stack))) },
|
created() {
|
||||||
beforeDestroy() { if (this.unsubscribeHistoryLayer) this.unsubscribeHistoryLayer(); if (window.h5HistoryLayerManager.pageKey === this.pageKey) window.h5HistoryLayerManager.unregister(this.pageKey) }
|
window.h5HistoryLayerManager.ensureRegistered(this.pageKey);
|
||||||
|
this.$set(this, "unsubscribeHistoryLayer", window.h5HistoryLayerManager.subscribe((stack) => this.syncLayers(stack)))
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.unsubscribeHistoryLayer) this.unsubscribeHistoryLayer();
|
||||||
|
if (window.h5HistoryLayerManager.pageKey === this.pageKey) window.h5HistoryLayerManager.unregister(this.pageKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+184
@@ -47,6 +47,190 @@ layout("/layouts/platform_h5.html"){
|
|||||||
color: #1989fa;
|
color: #1989fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Popup 内固定导航栏不依赖 placeholder 占位,避免遮挡邀请附议筛选框。 */
|
||||||
|
.proposal-invite-popup {
|
||||||
|
display: flex;
|
||||||
|
padding-top: 46px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 邀请附议人弹框固定展示工号、姓名查询入口,清空关键词后恢复完整人员列表。 */
|
||||||
|
.proposal-invite-search {
|
||||||
|
padding: 12px 12px 8px;
|
||||||
|
background-color: #f3f7fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-search .van-search__content {
|
||||||
|
height: 42px;
|
||||||
|
padding-left: 12px;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 5px 16px rgba(43, 73, 112, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-search .van-field__control {
|
||||||
|
color: #263548;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-search .van-field__control::placeholder {
|
||||||
|
color: #a7b0bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 邀请状态使用双分段切换,当前状态通过白色底和蓝色短线突出展示。 */
|
||||||
|
.proposal-invite-tabs {
|
||||||
|
padding: 4px 12px 8px;
|
||||||
|
background-color: #f3f7fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-tabs .van-tabs__wrap {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-tabs .van-tabs__nav {
|
||||||
|
padding: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 14px;
|
||||||
|
background-color: #edf4ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-tabs .van-tab {
|
||||||
|
height: 40px;
|
||||||
|
color: #6e7d92;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-tabs .van-tab--active {
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #1989fa;
|
||||||
|
font-weight: 600;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 4px 10px rgba(31, 89, 164, .08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-tabs .van-tabs__line {
|
||||||
|
bottom: 4px;
|
||||||
|
width: 30px;
|
||||||
|
height: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #1989fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 人员卡片区域独立滚动,顶部筛选区和底部操作区始终保留在视口内。 */
|
||||||
|
.proposal-invite-scroll {
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 人员列表以独立卡片呈现,姓名、工号、单位和勾选区域层级清晰。 */
|
||||||
|
.proposal-invite-list {
|
||||||
|
padding: 0 12px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-list .van-cell-group {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-person {
|
||||||
|
min-height: 70px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 6px 16px rgba(43, 73, 112, .08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-person::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-person__title {
|
||||||
|
display: block;
|
||||||
|
color: #253247;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-person__login-name {
|
||||||
|
color: #79879a;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-person__unit {
|
||||||
|
display: block;
|
||||||
|
margin-top: 3px;
|
||||||
|
color: #8c99aa;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-person__checkbox {
|
||||||
|
display: flex;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-person__checkbox .van-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #4b8cff;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-person__checkbox .van-checkbox__icon--checked .van-icon {
|
||||||
|
border-color: #1989fa;
|
||||||
|
background-color: #1989fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分页和邀请按钮沿用同一圆角语言,与人员卡片形成统一的操作区域。 */
|
||||||
|
.proposal-invite-actions {
|
||||||
|
padding: 0 12px calc(12px + env(safe-area-inset-bottom));
|
||||||
|
background-color: #f3f7fd;
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-pagination {
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-pagination .van-pagination__item {
|
||||||
|
min-height: 36px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 4px 12px rgba(43, 73, 112, .06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-pagination .van-pagination__item--active,
|
||||||
|
.proposal-invite-pagination .van-pagination__item:not(.van-pagination__item--disabled):active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1989fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-submit {
|
||||||
|
height: 42px;
|
||||||
|
margin-top: 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-invite-empty {
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* 详情弹框复用项目全屏 Popup 的固定导航结构,内容区域单独滚动。 */
|
/* 详情弹框复用项目全屏 Popup 的固定导航结构,内容区域单独滚动。 */
|
||||||
.proposal-detail-popup {
|
.proposal-detail-popup {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
+60
-43
@@ -11,60 +11,51 @@ const PROPOSAL_INVITE_SECONDER_COMPONENT = {
|
|||||||
left-arrow
|
left-arrow
|
||||||
left-text="返回"
|
left-text="返回"
|
||||||
fixed
|
fixed
|
||||||
placeholder
|
|
||||||
@click-left="$emit('close')">
|
@click-left="$emit('close')">
|
||||||
</van-nav-bar>
|
</van-nav-bar>
|
||||||
|
|
||||||
<van-search
|
<van-search
|
||||||
v-model="pageForm.searchKeyword"
|
v-model="pageForm.searchKeyword"
|
||||||
|
class="proposal-invite-search"
|
||||||
:show-action="false"
|
:show-action="false"
|
||||||
|
shape="round"
|
||||||
|
clearable
|
||||||
placeholder="请输入工号或者姓名查询"
|
placeholder="请输入工号或者姓名查询"
|
||||||
@search="doSearch">
|
@search="doSearch"
|
||||||
|
@clear="doSearch">
|
||||||
</van-search>
|
</van-search>
|
||||||
<van-tabs v-model="isInvite" @click="isInviteClick">
|
<van-tabs v-model="isInvite" class="proposal-invite-tabs" @click="isInviteClick">
|
||||||
<van-tab title="已邀请" name="1"></van-tab>
|
<van-tab title="已邀请" name="1"></van-tab>
|
||||||
<van-tab title="可邀请" name="0"></van-tab>
|
<van-tab title="可邀请" name="0"></van-tab>
|
||||||
</van-tabs>
|
</van-tabs>
|
||||||
|
|
||||||
<van-checkbox-group v-model="tableSelection" v-if="tableData && tableData.length > 0">
|
<div class="proposal-invite-scroll" v-if="tableData && tableData.length > 0">
|
||||||
<van-cell-group>
|
<van-checkbox-group class="proposal-invite-list" v-model="tableSelection">
|
||||||
<van-cell
|
<van-cell-group>
|
||||||
v-for="(item, index) in tableData"
|
<van-cell
|
||||||
clickable
|
class="proposal-invite-person"
|
||||||
:key="item.loginName"
|
v-for="(item, index) in tableData"
|
||||||
@click="tableRowToggle(index)">
|
clickable
|
||||||
<span slot="title">
|
:key="item.loginName"
|
||||||
{{item.userName}}({{item.loginName}})
|
@click="tableRowToggle(index)">
|
||||||
</span>
|
<span slot="title" class="proposal-invite-person__title">
|
||||||
<span slot="label" class="font-size0 van-color-text-assist">
|
{{item.userName}} <span class="proposal-invite-person__login-name">({{item.loginName}})</span>
|
||||||
{{item.unitName}}
|
</span>
|
||||||
</span>
|
<span slot="label" class="proposal-invite-person__unit">
|
||||||
<van-checkbox
|
{{item.unitName}}
|
||||||
:name="item"
|
</span>
|
||||||
:ref="'checkboxes' + item.loginName"
|
<van-checkbox
|
||||||
slot="right-icon"
|
:name="item"
|
||||||
v-if="!pageForm.isInvite">
|
:ref="'checkboxes' + item.loginName"
|
||||||
</van-checkbox>
|
class="proposal-invite-person__checkbox"
|
||||||
</van-cell>
|
slot="right-icon"
|
||||||
</van-cell-group>
|
@click.stop
|
||||||
<div class="p10">
|
v-if="!pageForm.isInvite">
|
||||||
<van-pagination
|
</van-checkbox>
|
||||||
class="proposal-soft-pagination"
|
</van-cell>
|
||||||
v-model="pageForm.pageNumber"
|
</van-cell-group>
|
||||||
:total-items="pageForm.totalCount"
|
</van-checkbox-group>
|
||||||
:items-per-page="pageForm.pageSize"
|
</div>
|
||||||
@change="pageData">
|
|
||||||
</van-pagination>
|
|
||||||
<van-button
|
|
||||||
@click="invite"
|
|
||||||
type="primary"
|
|
||||||
block
|
|
||||||
:disabled="pageForm.isInvite"
|
|
||||||
class="mt10 proposal-soft-button">
|
|
||||||
邀请
|
|
||||||
</van-button>
|
|
||||||
</div>
|
|
||||||
</van-checkbox-group>
|
|
||||||
|
|
||||||
<!-- 空状态提示随“已邀请 / 可邀请”标签切换,便于用户判断当前无数据的具体范围。 -->
|
<!-- 空状态提示随“已邀请 / 可邀请”标签切换,便于用户判断当前无数据的具体范围。 -->
|
||||||
<div v-else class="proposal-invite-empty">
|
<div v-else class="proposal-invite-empty">
|
||||||
@@ -75,6 +66,24 @@ const PROPOSAL_INVITE_SECONDER_COMPONENT = {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="proposal-invite-actions" v-if="tableData && tableData.length > 0">
|
||||||
|
<van-pagination
|
||||||
|
class="proposal-soft-pagination proposal-invite-pagination"
|
||||||
|
v-model="pageForm.pageNumber"
|
||||||
|
:total-items="pageForm.totalCount"
|
||||||
|
:items-per-page="pageForm.pageSize"
|
||||||
|
@change="pageData">
|
||||||
|
</van-pagination>
|
||||||
|
<van-button
|
||||||
|
@click="invite"
|
||||||
|
type="primary"
|
||||||
|
block
|
||||||
|
:disabled="pageForm.isInvite"
|
||||||
|
class="proposal-soft-button proposal-invite-submit">
|
||||||
|
邀请
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<van-dialog
|
<van-dialog
|
||||||
:value="confirmVisible"
|
:value="confirmVisible"
|
||||||
title="提示"
|
title="提示"
|
||||||
@@ -102,7 +111,7 @@ const PROPOSAL_INVITE_SECONDER_COMPONENT = {
|
|||||||
tableData: [],
|
tableData: [],
|
||||||
pageForm: {
|
pageForm: {
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 5,
|
pageSize: 6,
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
pageOrderName: "",
|
pageOrderName: "",
|
||||||
pageOrderBy: "",
|
pageOrderBy: "",
|
||||||
@@ -207,8 +216,16 @@ const PROPOSAL_INVITE_SECONDER_COMPONENT = {
|
|||||||
this.doSearch()
|
this.doSearch()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击人员卡片时切换对应的附议人勾选状态。
|
||||||
|
* index 为当前页人员索引;已邀请状态或找不到对应复选框时不处理,无返回值。
|
||||||
|
*/
|
||||||
tableRowToggle(index) {
|
tableRowToggle(index) {
|
||||||
if (this.pageForm.isInvite || index < 0) return
|
if (this.pageForm.isInvite || index < 0) return
|
||||||
|
const item = this.tableData[index]
|
||||||
|
const checkboxRef = item ? this.$refs["checkboxes" + item.loginName] : null
|
||||||
|
if (!checkboxRef || !checkboxRef[0]) return
|
||||||
|
checkboxRef[0].toggle()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -411,8 +411,8 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</van-form>
|
</van-form>
|
||||||
</section>
|
</section>
|
||||||
<div class="proposal-seconded-form-actions">
|
<div class="proposal-seconded-form-actions">
|
||||||
<van-button type="danger" block :loading="formLoading" class="proposal-soft-button proposal-soft-button--danger" @click="submitSeconded(false)">不同意</van-button>
|
|
||||||
<van-button type="primary" block :loading="formLoading" class="proposal-soft-button" @click="submitSeconded(true)">同意</van-button>
|
<van-button type="primary" block :loading="formLoading" class="proposal-soft-button" @click="submitSeconded(true)">同意</van-button>
|
||||||
|
<van-button type="danger" block :loading="formLoading" class="proposal-soft-button proposal-soft-button--danger" @click="submitSeconded(false)">不同意</van-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</proposal-info>
|
</proposal-info>
|
||||||
|
|||||||
+151
-2
@@ -237,6 +237,109 @@ layout("/layouts/platform_h5.html"){
|
|||||||
box-shadow: 0 4px 12px rgba(43, 73, 112, .08), inset 0 1px 1px rgba(255, 255, 255, .82);
|
box-shadow: 0 4px 12px rgba(43, 73, 112, .08), inset 0 1px 1px rgba(255, 255, 255, .82);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 选择转交人弹框沿用提案列表的圆角搜索框、人员卡片和插图空状态。 */
|
||||||
|
.proposal-transfer-user-popup .proposal-full-popup__page {
|
||||||
|
background-color: #f3f7fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-search {
|
||||||
|
padding: 12px 12px 8px;
|
||||||
|
background-color: #f3f7fd;
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-search .van-search__content {
|
||||||
|
height: 42px;
|
||||||
|
padding-left: 12px;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 5px 16px rgba(43, 73, 112, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-search .van-field__control {
|
||||||
|
color: #263548;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-search .van-field__control::placeholder {
|
||||||
|
color: #a7b0bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-scroll {
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-list {
|
||||||
|
padding: 0 12px 12px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-item {
|
||||||
|
min-height: 64px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 6px 16px rgba(43, 73, 112, .08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-item::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-item__name {
|
||||||
|
display: block;
|
||||||
|
color: #253247;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-item__unit {
|
||||||
|
color: #79879a;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-empty {
|
||||||
|
display: flex;
|
||||||
|
min-height: 420px;
|
||||||
|
padding: 40px 18px 36px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-empty img {
|
||||||
|
display: block;
|
||||||
|
width: 82%;
|
||||||
|
max-width: 270px;
|
||||||
|
height: auto;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-empty__title {
|
||||||
|
margin-top: 14px;
|
||||||
|
color: #50627a;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-transfer-user-empty__hint {
|
||||||
|
margin-top: 5px;
|
||||||
|
color: #9aa7b8;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 350px) {
|
@media (max-width: 350px) {
|
||||||
.proposal-unit-reply-content,
|
.proposal-unit-reply-content,
|
||||||
.proposal-unit-reply-sticky {
|
.proposal-unit-reply-sticky {
|
||||||
@@ -340,7 +443,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<div class="action-btn" v-if="!pageForm.approval && $auth.hasRoleOr('SYSADMIN,PROPOSAL_UNIT_LEADER')"
|
<div class="action-btn" v-if="!pageForm.approval && $auth.hasRoleOr('SYSADMIN,PROPOSAL_UNIT_LEADER')"
|
||||||
@click="openTransfer(row)">
|
@click="openTransfer(row)">
|
||||||
<van-icon class="proposal-action-icon" name="exchange"></van-icon>
|
<van-icon class="proposal-action-icon" name="exchange"></van-icon>
|
||||||
<span>转交</span>
|
<span>转办</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</table-list>
|
</table-list>
|
||||||
@@ -350,6 +453,16 @@ layout("/layouts/platform_h5.html"){
|
|||||||
|
|
||||||
<transfer ref="transferRef" @success="doSearch"></transfer>
|
<transfer ref="transferRef" @success="doSearch"></transfer>
|
||||||
<proposal-task-popup ref="taskPopup" kind="unitReply" page-key="proposal-unit-reply-task" @success="doSearch"></proposal-task-popup>
|
<proposal-task-popup ref="taskPopup" kind="unitReply" page-key="proposal-unit-reply-task" @success="doSearch"></proposal-task-popup>
|
||||||
|
<van-dialog
|
||||||
|
:value="communicationConfirmVisible"
|
||||||
|
title="提示"
|
||||||
|
message="是否已与提案代表进行充分沟通?"
|
||||||
|
confirm-button-text="是"
|
||||||
|
cancel-button-text="否"
|
||||||
|
show-cancel-button
|
||||||
|
@confirm="confirmCommunication"
|
||||||
|
@cancel="closeCommunicationConfirm">
|
||||||
|
</van-dialog>
|
||||||
</div>
|
</div>
|
||||||
<script nonce="${cspNonce!}">
|
<script nonce="${cspNonce!}">
|
||||||
<!--#include("transfer.js"){}#-->
|
<!--#include("transfer.js"){}#-->
|
||||||
@@ -378,6 +491,10 @@ layout("/layouts/platform_h5.html"){
|
|||||||
{text: "未审核", value: "0"},
|
{text: "未审核", value: "0"},
|
||||||
{text: "已审核", value: "1"}
|
{text: "已审核", value: "1"}
|
||||||
],
|
],
|
||||||
|
communicationConfirmVisible: false,
|
||||||
|
pendingApprovalRow: null,
|
||||||
|
historyLayerPageKey: "proposal-unit-reply",
|
||||||
|
unsubscribeHistoryLayer: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
@@ -414,7 +531,29 @@ layout("/layouts/platform_h5.html"){
|
|||||||
},
|
},
|
||||||
|
|
||||||
onApproval(row, index) {
|
onApproval(row, index) {
|
||||||
this.$refs.taskPopup.open(row, "approval")
|
// row 为当前待答复提案;确认已充分沟通后,才进入原有答复表单。
|
||||||
|
this.$set(this, "pendingApprovalRow", row)
|
||||||
|
window.h5HistoryLayerManager.open("communication-confirm")
|
||||||
|
},
|
||||||
|
|
||||||
|
/** stack 为当前页面弹层名称数组;同步沟通确认框状态,无返回值。 */
|
||||||
|
syncHistoryLayers(stack) {
|
||||||
|
const visible = stack.includes("communication-confirm")
|
||||||
|
this.$set(this, "communicationConfirmVisible", visible)
|
||||||
|
if (!visible) this.$set(this, "pendingApprovalRow", null)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 取消确认时同步回退浏览器历史,手机返回键与弹窗按钮行为保持一致。
|
||||||
|
closeCommunicationConfirm() {
|
||||||
|
window.h5HistoryLayerManager.close("communication-confirm")
|
||||||
|
},
|
||||||
|
|
||||||
|
// 确认后先关闭沟通弹层,再使用暂存的提案数据打开原有答复表单。
|
||||||
|
confirmCommunication() {
|
||||||
|
const row = this.pendingApprovalRow
|
||||||
|
window.h5HistoryLayerManager.close("communication-confirm", () => {
|
||||||
|
if (row) this.$refs.taskPopup.open(row, "approval")
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
onRevoke(row) {
|
onRevoke(row) {
|
||||||
@@ -445,6 +584,10 @@ layout("/layouts/platform_h5.html"){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
window.h5HistoryLayerManager.ensureRegistered(this.historyLayerPageKey)
|
||||||
|
this.$set(this, "unsubscribeHistoryLayer", window.h5HistoryLayerManager.subscribe((stack) => {
|
||||||
|
this.syncHistoryLayers(stack)
|
||||||
|
}))
|
||||||
const tab = new URLSearchParams(window.location.search).get("tab")
|
const tab = new URLSearchParams(window.location.search).get("tab")
|
||||||
if (tab === "done") {
|
if (tab === "done") {
|
||||||
this.$set(this.pageForm, "approval", true)
|
this.$set(this.pageForm, "approval", true)
|
||||||
@@ -453,6 +596,12 @@ layout("/layouts/platform_h5.html"){
|
|||||||
this.$set(this.pageForm, "approval", false)
|
this.$set(this.pageForm, "approval", false)
|
||||||
this.$set(this.pageForm, "approvalText", "0")
|
this.$set(this.pageForm, "approvalText", "0")
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.unsubscribeHistoryLayer) this.unsubscribeHistoryLayer()
|
||||||
|
if (window.h5HistoryLayerManager.pageKey === this.historyLayerPageKey) {
|
||||||
|
window.h5HistoryLayerManager.unregister(this.historyLayerPageKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
+53
-20
@@ -1,28 +1,58 @@
|
|||||||
const transfer = {
|
const transfer = {
|
||||||
template: /*language=HTML*/ `
|
template: /*language=HTML*/ `
|
||||||
<van-popup v-model="showTransferDialog" position="right" class="proposal-full-popup" :close-on-click-overlay="false" get-container="#app">
|
<van-popup v-model="showTransferDialog" position="right" class="proposal-full-popup proposal-transfer-dialog" :close-on-click-overlay="false" get-container="#app">
|
||||||
<div class="proposal-full-popup__page">
|
<div class="proposal-full-popup__page">
|
||||||
<van-nav-bar @click-left="closeTransfer" left-arrow left-text="返回" title="提案转办" placeholder fixed></van-nav-bar>
|
<van-nav-bar @click-left="closeTransfer" left-arrow left-text="返回" title="提案转办" placeholder fixed></van-nav-bar>
|
||||||
<div class="proposal-full-popup__scroll transfer-dialog">
|
<div class="proposal-full-popup__scroll transfer-dialog">
|
||||||
<div class="proposal-info-card">
|
<section class="proposal-view-card proposal-transfer-info-card">
|
||||||
<div class="info-item"><label>提案编号:</label><span>{{transferForm.code}}</span></div>
|
<van-cell-group :border="false">
|
||||||
<div class="info-item"><label>提案名称:</label><span>{{transferForm.name}}</span></div>
|
<van-cell title="提案编号">{{transferForm.code || '--'}}</van-cell>
|
||||||
<div class="info-item"><label>代表团:</label><span>{{transferForm.delegationName}}</span></div>
|
<van-cell title="提案名称">{{transferForm.name || '--'}}</van-cell>
|
||||||
</div>
|
<van-cell title="代表团">{{transferForm.delegationName || '--'}}</van-cell>
|
||||||
<div class="proposal-info-card">
|
</van-cell-group>
|
||||||
<van-field v-model="searchKeyword" label="转交给" placeholder="搜索用户" required readonly is-link @click="openUserList"></van-field>
|
</section>
|
||||||
</div>
|
<section class="proposal-view-card proposal-transfer-user-card">
|
||||||
|
<van-cell-group :border="false">
|
||||||
|
<van-field v-model="searchKeyword" label="转交给" placeholder="搜索用户" required readonly is-link @click="openUserList"></van-field>
|
||||||
|
</van-cell-group>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="button-group proposal-transfer-button-group">
|
||||||
|
<van-button block type="info" class="proposal-soft-button" @click="handleTransfer">提交</van-button>
|
||||||
|
<van-button block plain class="proposal-transfer-cancel-button" @click="closeTransfer">返回</van-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-group proposal-transfer-button-group"><van-button block plain class="proposal-transfer-cancel-button" @click="closeTransfer">取消</van-button><van-button block type="info" class="proposal-soft-button" @click="handleTransfer">提交</van-button></div>
|
|
||||||
</div>
|
</div>
|
||||||
<van-popup v-model="showUserList" position="right" class="proposal-full-popup" :close-on-click-overlay="false" get-container="#app">
|
<van-popup v-model="showUserList" position="right" class="proposal-full-popup proposal-transfer-user-popup" :close-on-click-overlay="false" get-container="#app">
|
||||||
<div class="proposal-full-popup__page">
|
<div class="proposal-full-popup__page">
|
||||||
<van-nav-bar @click-left="closeUserList" left-arrow left-text="返回" title="选择转交人" placeholder fixed></van-nav-bar>
|
<van-nav-bar @click-left="closeUserList" left-arrow left-text="返回" title="选择转交人" placeholder fixed></van-nav-bar>
|
||||||
<div class="proposal-full-popup__scroll">
|
<van-search
|
||||||
<van-search v-model="keyword" placeholder="搜索用户" @search="selectTransferUser(keyword)"></van-search>
|
v-model="keyword"
|
||||||
<div class="proposal-view-main">
|
class="proposal-transfer-user-search"
|
||||||
<div v-if="transferUserOptions.length" class="proposal-info-card"><van-cell v-for="item in transferUserOptions" :key="item.id" :title="item.username" @click="selectUser(item)"></van-cell></div>
|
shape="round"
|
||||||
<van-empty v-else class="empty-tip">请搜索需要转办的用户</van-empty>
|
clearable
|
||||||
|
placeholder="请输入姓名或工号搜索"
|
||||||
|
@search="selectTransferUser(keyword)"
|
||||||
|
@clear="resetTransferUserSearch">
|
||||||
|
</van-search>
|
||||||
|
<div class="proposal-transfer-user-scroll">
|
||||||
|
<van-cell-group v-if="transferUserOptions.length" class="proposal-transfer-user-list" :border="false">
|
||||||
|
<van-cell
|
||||||
|
v-for="item in transferUserOptions"
|
||||||
|
:key="item.id"
|
||||||
|
class="proposal-transfer-user-item"
|
||||||
|
clickable
|
||||||
|
@click="selectUser(item)">
|
||||||
|
<span slot="title" class="proposal-transfer-user-item__name">
|
||||||
|
{{item.username}}<span class="proposal-transfer-user-item__unit">({{item.unitName || '--'}})</span>
|
||||||
|
</span>
|
||||||
|
</van-cell>
|
||||||
|
</van-cell-group>
|
||||||
|
<div v-else class="proposal-transfer-user-empty">
|
||||||
|
<img src="/assets/mobile/img/proposal/proposal-empty.png" alt="暂无转交用户插画">
|
||||||
|
<div class="proposal-transfer-user-empty__title">
|
||||||
|
{{hasUserSearched ? '暂无搜索记录' : '请搜索需要转办的用户'}}
|
||||||
|
</div>
|
||||||
|
<div v-if="hasUserSearched" class="proposal-transfer-user-empty__hint">请调整搜索条件后重试</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,7 +61,7 @@ const transfer = {
|
|||||||
</van-popup>
|
</van-popup>
|
||||||
`,
|
`,
|
||||||
data() {
|
data() {
|
||||||
return {showTransferDialog:false,transferForm:{},transferUserOptions:[],searchKeyword:"",showUserList:false,keyword:"",confirmVisible:false,historyLayerPageKey:"proposal-unit-reply-transfer",unsubscribeHistoryLayer:null}
|
return {showTransferDialog:false,transferForm:{},transferUserOptions:[],searchKeyword:"",showUserList:false,keyword:"",hasUserSearched:false,confirmVisible:false,historyLayerPageKey:"proposal-unit-reply-transfer",unsubscribeHistoryLayer:null}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
confirmMessage() {
|
confirmMessage() {
|
||||||
@@ -43,12 +73,15 @@ const transfer = {
|
|||||||
syncLayers(stack) { this.$set(this,"showTransferDialog",stack.includes("transfer")); this.$set(this,"showUserList",stack.includes("user-list")); this.$set(this,"confirmVisible",stack.includes("confirm")) },
|
syncLayers(stack) { this.$set(this,"showTransferDialog",stack.includes("transfer")); this.$set(this,"showUserList",stack.includes("user-list")); this.$set(this,"confirmVisible",stack.includes("confirm")) },
|
||||||
openTransfer(row) { this.$set(this,"transferForm",{taskId:row.taskId,name:row.name,code:row.code,delegationName:row.delegationName}); this.$set(this,"searchKeyword",""); window.h5HistoryLayerManager.open("transfer") },
|
openTransfer(row) { this.$set(this,"transferForm",{taskId:row.taskId,name:row.name,code:row.code,delegationName:row.delegationName}); this.$set(this,"searchKeyword",""); window.h5HistoryLayerManager.open("transfer") },
|
||||||
closeTransfer() { window.h5HistoryLayerManager.close("transfer") },
|
closeTransfer() { window.h5HistoryLayerManager.close("transfer") },
|
||||||
openUserList() { window.h5HistoryLayerManager.open("user-list") },
|
/** 打开用户列表前重置搜索条件和结果,确保首次进入展示搜索引导空状态。 */
|
||||||
|
openUserList() { this.resetTransferUserSearch(); window.h5HistoryLayerManager.open("user-list") },
|
||||||
closeUserList() { window.h5HistoryLayerManager.close("user-list") },
|
closeUserList() { window.h5HistoryLayerManager.close("user-list") },
|
||||||
closeConfirm() { window.h5HistoryLayerManager.close("confirm") },
|
closeConfirm() { window.h5HistoryLayerManager.close("confirm") },
|
||||||
selectUser(item) { this.$set(this.transferForm,"userId",item.id); this.$set(this,"searchKeyword",item.username); this.closeUserList() },
|
selectUser(item) { this.$set(this.transferForm,"userId",item.id); this.$set(this,"searchKeyword",item.username); this.closeUserList() },
|
||||||
/** keyword 为姓名关键字,接口返回可转交用户数组。 */
|
/** 清空姓名或工号关键词、搜索状态和人员结果,无返回值。 */
|
||||||
selectTransferUser(keyword) { this.$axios.post("/platform/proposal/unitReply/selectViceUser",{keyword:keyword}).then((res) => { if (res.code === 0) this.$set(this,"transferUserOptions",res.data || []) }) },
|
resetTransferUserSearch() { this.$set(this,"keyword",""); this.$set(this,"hasUserSearched",false); this.$set(this,"transferUserOptions",[]) },
|
||||||
|
/** keyword 为姓名或工号关键词;空值仅恢复搜索引导,非空值查询并返回可转交用户数组。 */
|
||||||
|
selectTransferUser(keyword) { const searchText=(keyword || "").trim(); if (!searchText) { this.resetTransferUserSearch(); return } this.$set(this,"hasUserSearched",true); this.$axios.post("/platform/proposal/unitReply/selectViceUser",{keyword:searchText}).then((res) => { if (res.code === 0) this.$set(this,"transferUserOptions",res.data || []) }) },
|
||||||
handleTransfer() { if (!this.transferForm.userId) { this.$toast("请选择转交用户"); return } window.h5HistoryLayerManager.open("confirm") },
|
handleTransfer() { if (!this.transferForm.userId) { this.$toast("请选择转交用户"); return } window.h5HistoryLayerManager.open("confirm") },
|
||||||
/** 提交 taskId 和 userId 完成转办,成功后清理弹层并通知父列表刷新。 */
|
/** 提交 taskId 和 userId 完成转办,成功后清理弹层并通知父列表刷新。 */
|
||||||
confirmTransfer() { this.$axios.post("/platform/proposal/unitReply/transfer",this.transferForm).then((res) => { if (res.code === 0) { this.$toast.success(res.msg); window.h5HistoryLayerManager.closeAll(() => this.$emit("success")) } }) }
|
confirmTransfer() { this.$axios.post("/platform/proposal/unitReply/transfer",this.transferForm).then((res) => { if (res.code === 0) { this.$toast.success(res.msg); window.h5HistoryLayerManager.closeAll(() => this.$emit("success")) } }) }
|
||||||
|
|||||||
@@ -278,11 +278,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
.welfare-list-card__choices-arrow {
|
.welfare-list-card__choices-arrow {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
flex: none;
|
flex: none;
|
||||||
transition: transform .2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-list-card__choices-arrow.is-expanded {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.welfare-list-card__choices > .welfare-inline-loading,
|
.welfare-list-card__choices > .welfare-inline-loading,
|
||||||
@@ -446,32 +441,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.welfare-list-card__actions {
|
|
||||||
display: flex;
|
|
||||||
min-height: 42px;
|
|
||||||
margin-top: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
border-top: 1px solid #edf1f6;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-list-card__detail-button {
|
|
||||||
display: inline-flex;
|
|
||||||
height: 30px;
|
|
||||||
padding: 0 14px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border: 1px solid #1989fa;
|
|
||||||
border-radius: 16px;
|
|
||||||
color: #1989fa;
|
|
||||||
background-color: #fff;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 28px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-list-card__time .van-icon {
|
.welfare-list-card__time .van-icon {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
flex: none;
|
flex: none;
|
||||||
@@ -486,76 +455,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.welfare-list-card__expanded {
|
|
||||||
padding: 12px;
|
|
||||||
border-top: 1px solid #edf1f6;
|
|
||||||
background-color: #f8fbff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-inline-card {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 0 12px;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: #fff;
|
|
||||||
box-shadow: 0 4px 14px rgba(55, 85, 126, .06);
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-inline-card:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-inline-card__title {
|
|
||||||
position: relative;
|
|
||||||
height: 42px;
|
|
||||||
padding-left: 8px;
|
|
||||||
border-bottom: 1px solid #edf1f6;
|
|
||||||
color: #263548;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-inline-card__title::before {
|
|
||||||
position: absolute;
|
|
||||||
top: 14px;
|
|
||||||
bottom: 14px;
|
|
||||||
left: 0;
|
|
||||||
width: 3px;
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: #1989fa;
|
|
||||||
content: '';
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-inline-row {
|
|
||||||
display: flex;
|
|
||||||
min-height: 40px;
|
|
||||||
padding: 6px 0;
|
|
||||||
align-items: center;
|
|
||||||
border-bottom: 1px solid #edf1f6;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-inline-row:last-child {
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-inline-row__label {
|
|
||||||
width: 72px;
|
|
||||||
flex: none;
|
|
||||||
color: #7f8da1;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-inline-row__value {
|
|
||||||
min-width: 0;
|
|
||||||
flex: 1;
|
|
||||||
color: #263548;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 19px;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-inline-selection {
|
.welfare-inline-selection {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 72px;
|
min-height: 72px;
|
||||||
@@ -780,8 +679,10 @@ layout("/layouts/platform_h5.html"){
|
|||||||
v-for="row in tableData"
|
v-for="row in tableData"
|
||||||
:key="row.id"
|
:key="row.id"
|
||||||
class="welfare-list-card"
|
class="welfare-list-card"
|
||||||
|
role="button"
|
||||||
|
@click="modifySelection(row)"
|
||||||
>
|
>
|
||||||
<div class="welfare-list-card__summary" @click="toggleRow(row)">
|
<div class="welfare-list-card__summary">
|
||||||
<div class="welfare-list-card__order-head">
|
<div class="welfare-list-card__order-head">
|
||||||
<div class="welfare-list-card__order-info">
|
<div class="welfare-list-card__order-info">
|
||||||
<div class="welfare-list-card__title">{{ row.name }}</div>
|
<div class="welfare-list-card__title">{{ row.name }}</div>
|
||||||
@@ -812,66 +713,9 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</template>
|
</template>
|
||||||
<div v-else class="welfare-inline-empty">暂无福利选择记录</div>
|
<div v-else class="welfare-inline-empty">暂无福利选择记录</div>
|
||||||
</div>
|
</div>
|
||||||
<van-icon class="welfare-list-card__arrow welfare-list-card__choices-arrow" :class="{'is-expanded': row.expanded}" name="arrow"></van-icon>
|
<van-icon class="welfare-list-card__arrow welfare-list-card__choices-arrow" name="arrow"></van-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="welfare-list-card__expanded" v-show="row.expanded">
|
|
||||||
<div class="welfare-inline-loading" v-if="row.detailLoading">
|
|
||||||
<van-loading size="20" color="#1989fa">加载中...</van-loading>
|
|
||||||
</div>
|
|
||||||
<template v-else-if="row.projectInfo">
|
|
||||||
<section class="welfare-inline-card">
|
|
||||||
<div class="welfare-inline-card__title">项目基本信息</div>
|
|
||||||
<div class="welfare-inline-row">
|
|
||||||
<span class="welfare-inline-row__label">项目名称</span>
|
|
||||||
<span class="welfare-inline-row__value">{{ row.projectInfo.name || row.name }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="welfare-inline-row">
|
|
||||||
<span class="welfare-inline-row__label">选择时间</span>
|
|
||||||
<span class="welfare-inline-row__value">{{ formatRange(row.projectInfo.choiceTimeStart, row.projectInfo.choiceTimeEnd) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="welfare-inline-row">
|
|
||||||
<span class="welfare-inline-row__label">发放时间</span>
|
|
||||||
<span class="welfare-inline-row__value">{{ formatRange(row.projectInfo.provideTimeStart, row.projectInfo.provideTimeEnd) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="welfare-inline-row">
|
|
||||||
<span class="welfare-inline-row__label">发放地点</span>
|
|
||||||
<span class="welfare-inline-row__value">{{ row.projectInfo.provideAddress || '暂无' }}</span>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="welfare-inline-card">
|
|
||||||
<div class="welfare-inline-card__title">联系信息</div>
|
|
||||||
<div class="welfare-inline-row">
|
|
||||||
<span class="welfare-inline-row__label">联系电话</span>
|
|
||||||
<span class="welfare-inline-row__value">{{ firstSelection(row).mobile || '暂无' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="welfare-inline-row">
|
|
||||||
<span class="welfare-inline-row__label">收货地址</span>
|
|
||||||
<span class="welfare-inline-row__value">{{ firstSelection(row).receiveAddress || '暂无' }}</span>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="welfare-inline-card">
|
|
||||||
<div class="welfare-inline-card__title">我的选择</div>
|
|
||||||
<div v-if="row.selections.length > 0">
|
|
||||||
<div class="welfare-inline-selection" v-for="item in row.selections" :key="item.selectId || item.id">
|
|
||||||
<van-image class="welfare-inline-selection__image" :src="item.imgUrl" fit="cover" @click.stop="previewSelectionImage(item.imgUrl)">
|
|
||||||
<template #error><van-icon name="photo-o" size="24"></van-icon></template>
|
|
||||||
</van-image>
|
|
||||||
<div class="welfare-inline-selection__content">
|
|
||||||
<div class="welfare-inline-selection__name">{{ item.optionName || '未知选项' }}</div>
|
|
||||||
<div class="welfare-inline-selection__meta">
|
|
||||||
<span class="welfare-inline-selection__tag">× {{ item.selectNum || 0 }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else class="welfare-inline-empty">暂无福利选择记录</div>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</van-list>
|
</van-list>
|
||||||
@@ -928,10 +772,8 @@ layout("/layouts/platform_h5.html"){
|
|||||||
if (res.code !== 0 || !res.data) return
|
if (res.code !== 0 || !res.data) return
|
||||||
const list = (res.data.list || []).map((row) => ({
|
const list = (res.data.list || []).map((row) => ({
|
||||||
...row,
|
...row,
|
||||||
expanded: false,
|
|
||||||
detailLoading: false,
|
detailLoading: false,
|
||||||
detailLoaded: false,
|
detailLoaded: false,
|
||||||
projectInfo: null,
|
|
||||||
selections: []
|
selections: []
|
||||||
}))
|
}))
|
||||||
this.tableData = this.tableData.concat(list)
|
this.tableData = this.tableData.concat(list)
|
||||||
@@ -993,7 +835,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
description: option.description
|
description: option.description
|
||||||
}
|
}
|
||||||
}) : []
|
}) : []
|
||||||
this.$set(row, "projectInfo", projectInfo)
|
|
||||||
this.$set(row, "selections", selections)
|
this.$set(row, "selections", selections)
|
||||||
this.$set(row, "detailLoaded", true)
|
this.$set(row, "detailLoaded", true)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1001,36 +842,8 @@ layout("/layouts/platform_h5.html"){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
modifySelection(row) {
|
||||||
* 展开或收起当前福利卡片的内容详情。
|
this.$pjaxReplace("/platform/h5/welfare/userSelect/index?id=" + (row.projectId || row.id) + "&from=mine")
|
||||||
* @param row 当前福利项目
|
|
||||||
*/
|
|
||||||
toggleRow(row) {
|
|
||||||
row.expanded = !row.expanded
|
|
||||||
},
|
|
||||||
|
|
||||||
previewSelectionImage(imageUrl) {
|
|
||||||
if (imageUrl) vant.ImagePreview([imageUrl])
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前福利的首条选择记录,用于展示联系电话和收货地址。
|
|
||||||
* @param row 当前福利项目
|
|
||||||
* @return {Object} 首条选择记录;无记录时返回空对象
|
|
||||||
*/
|
|
||||||
firstSelection(row) {
|
|
||||||
return row.selections && row.selections.length > 0 ? row.selections[0] : {}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 格式化项目起止时间。
|
|
||||||
* @param startTime 开始时间
|
|
||||||
* @param endTime 结束时间
|
|
||||||
* @return {string} 起止时间文本
|
|
||||||
*/
|
|
||||||
formatRange(startTime, endTime) {
|
|
||||||
if (!startTime || !endTime) return "暂无"
|
|
||||||
return this.$moment(startTime).format("YYYY-MM-DD HH:mm") + " 至 " + this.$moment(endTime).format("YYYY-MM-DD HH:mm")
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ layout("/layouts/platform_h5.html"){
|
|||||||
background: var(--bg-color);
|
background: var(--bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-container.has-footer-contact {
|
||||||
|
padding-bottom: calc(126px + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
.page-container .van-nav-bar {
|
.page-container .van-nav-bar {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
@@ -383,27 +387,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.welfare-option-selected-mark {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
z-index: 2;
|
|
||||||
display: flex;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
padding: 0 0 4px 4px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-radius: 0 11px 0 18px;
|
|
||||||
color: #fff;
|
|
||||||
background: #35b878;
|
|
||||||
box-shadow: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 32px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-deadline {
|
.welfare-deadline {
|
||||||
margin: 0 12px 8px;
|
margin: 0 12px 8px;
|
||||||
padding: 9px 11px;
|
padding: 9px 11px;
|
||||||
@@ -742,10 +725,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.van-sticky--fixed .welfare-section-title {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-section-title .title-text {
|
.welfare-section-title .title-text {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -773,10 +752,482 @@ layout("/layouts/platform_h5.html"){
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 项目封面保持单图全宽展示,下拉时通过动态高度逐步露出完整画面。 */
|
||||||
|
.welfare-header {
|
||||||
|
height: 232px;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
transition: height .28s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-header.is-pulling {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-header.is-expanded {
|
||||||
|
background-color: #f3f7fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-header-img .van-image__img {
|
||||||
|
object-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-cover-pull-hint {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 28px;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: rgba(255, 255, 255, .92);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
text-shadow: 0 1px 5px rgba(20, 50, 88, .5);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-cover-pull-hint::before {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: -28px;
|
||||||
|
left: 0;
|
||||||
|
z-index: -1;
|
||||||
|
height: 76px;
|
||||||
|
background: linear-gradient(180deg, rgba(18, 45, 82, 0), rgba(18, 45, 82, .42));
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-cover-pull-hint .van-icon {
|
||||||
|
margin-right: 6px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card {
|
||||||
|
margin: -20px 12px 12px;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 14px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 8px 24px rgba(50, 83, 126, .12);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__head {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__title-wrap {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__title {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 25px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__festival {
|
||||||
|
height: 22px;
|
||||||
|
margin-left: 8px;
|
||||||
|
padding: 0 7px;
|
||||||
|
flex: none;
|
||||||
|
border: 1px solid #acd3ff;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #1989fa;
|
||||||
|
background-color: #f4f9ff;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__status {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 8px;
|
||||||
|
align-items: center;
|
||||||
|
color: #1989fa;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__status::before {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
margin-right: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: currentColor;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__status.is-ended {
|
||||||
|
color: #a8b0bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__facts {
|
||||||
|
display: grid;
|
||||||
|
margin-top: 12px;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-fact {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 10px;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f7faff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-fact__icon {
|
||||||
|
display: flex;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
margin-right: 9px;
|
||||||
|
flex: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #fff;
|
||||||
|
background: #4c9cff;
|
||||||
|
box-shadow: 0 4px 10px rgba(25, 137, 250, .18);
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-fact__content {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-fact__label {
|
||||||
|
color: #8491a5;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-fact__value {
|
||||||
|
margin-top: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #1989fa;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 19px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__meta {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
margin-top: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__address {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #4c596b;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 20px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__address .van-icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
flex: none;
|
||||||
|
color: #66758a;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__tags {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
column-gap: 5px;
|
||||||
|
row-gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-project-card__tag {
|
||||||
|
height: 22px;
|
||||||
|
padding: 0 7px;
|
||||||
|
border: 1px solid #b8d9ff;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #1989fa;
|
||||||
|
background-color: #f7fbff;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-deadline {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-section-title {
|
||||||
|
min-height: 42px;
|
||||||
|
margin: 0 12px 8px;
|
||||||
|
padding: 7px 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: rgba(243, 247, 253, .96);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-section-title .title-text {
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-section-title .title-label {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-section-title .title-count {
|
||||||
|
margin-left: 5px;
|
||||||
|
color: #7d899b;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option {
|
||||||
|
border-color: #e2eaf4;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option.selected {
|
||||||
|
border-color: #c5dfff;
|
||||||
|
background: linear-gradient(90deg, #f5faff 0%, #fff 100%);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-main {
|
||||||
|
min-height: 128px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-image {
|
||||||
|
width: 94px;
|
||||||
|
height: 94px;
|
||||||
|
margin: 16px 0 16px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-content {
|
||||||
|
padding: 14px 12px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-title {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-supplier {
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: #7a8799;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 17px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-rank {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-desc {
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-meta {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
margin-top: 7px;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-meta__tags {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
column-gap: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-meta__tag {
|
||||||
|
height: 19px;
|
||||||
|
padding: 0 5px;
|
||||||
|
flex: none;
|
||||||
|
border: 1px solid #b8d9ff;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #1989fa;
|
||||||
|
background-color: #f7fbff;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 17px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-meta__total {
|
||||||
|
margin-left: 6px;
|
||||||
|
flex: none;
|
||||||
|
color: #8a96a8;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-option-select-row {
|
||||||
|
min-height: 44px;
|
||||||
|
padding: 7px 12px;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer.has-contact {
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: calc(8px + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__selection {
|
||||||
|
min-width: 92px;
|
||||||
|
color: #66758a;
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__selection strong {
|
||||||
|
margin-left: 3px;
|
||||||
|
color: #1989fa;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 50px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding: 5px 8px;
|
||||||
|
align-items: center;
|
||||||
|
color: #66758a;
|
||||||
|
background-color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact-icon {
|
||||||
|
display: flex;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
margin-right: 10px;
|
||||||
|
flex: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #1989fa;
|
||||||
|
background-color: #edf5ff;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact-info {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact-primary,
|
||||||
|
.welfare-footer__contact-address {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
align-items: baseline;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact-primary {
|
||||||
|
color: #1f2937;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact-address {
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact-label {
|
||||||
|
margin-right: 3px;
|
||||||
|
flex: none;
|
||||||
|
color: #929cab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact-name,
|
||||||
|
.welfare-footer__contact-mobile,
|
||||||
|
.welfare-footer__contact-address-value {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #1f2937;
|
||||||
|
font-weight: 500;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact-name {
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact-mobile {
|
||||||
|
margin-left: 12px;
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-footer__contact-address-value {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-submit-btn {
|
||||||
|
width: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welfare-submit-btn.is-deadline-passed.van-button--disabled {
|
||||||
|
color: #a8b0bc;
|
||||||
|
background: #edf0f4;
|
||||||
|
border-color: #edf0f4;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div id="app" v-cloak>
|
<div id="app" v-cloak>
|
||||||
<div class="page-container">
|
<div class="page-container" :class="{'has-footer-contact': fromMine && hasSubmittedBefore}">
|
||||||
<van-nav-bar title="福利选择" left-text="返回" left-arrow @click-left="handlePageBack" fixed
|
<van-nav-bar title="福利选择" left-text="返回" left-arrow @click-left="handlePageBack" fixed
|
||||||
placeholder></van-nav-bar>
|
placeholder></van-nav-bar>
|
||||||
|
|
||||||
@@ -794,8 +1245,17 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<van-empty v-else-if="!projectInfo.id" description="暂无福利信息"></van-empty>
|
<van-empty v-else-if="!projectInfo.id" description="暂无福利信息"></van-empty>
|
||||||
|
|
||||||
<!-- 项目头部信息 -->
|
<!-- 项目头部信息 -->
|
||||||
<div class="welfare-header" v-if="!pageLoading && projectInfo.id">
|
<div
|
||||||
<van-image v-if="projectInfo.cover" :src="projectInfo.cover" class="welfare-header-img" fit="cover">
|
class="welfare-header"
|
||||||
|
:class="{'is-pulling': coverPullEnabled, 'is-expanded': coverExpanded}"
|
||||||
|
:style="coverHeaderStyle"
|
||||||
|
v-if="!pageLoading && projectInfo.id"
|
||||||
|
@touchstart="handleCoverTouchStart"
|
||||||
|
@touchmove="handleCoverTouchMove"
|
||||||
|
@touchend="handleCoverTouchEnd"
|
||||||
|
@touchcancel="handleCoverTouchEnd"
|
||||||
|
>
|
||||||
|
<van-image v-if="projectInfo.cover" :src="projectInfo.cover" class="welfare-header-img" :fit="coverImageFit">
|
||||||
<template #error>
|
<template #error>
|
||||||
<div class="welfare-image-state">
|
<div class="welfare-image-state">
|
||||||
<van-icon name="photo-o"></van-icon>
|
<van-icon name="photo-o"></van-icon>
|
||||||
@@ -807,24 +1267,67 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<van-icon name="photo-o"></van-icon>
|
<van-icon name="photo-o"></van-icon>
|
||||||
<span>暂无图片</span>
|
<span>暂无图片</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="welfare-cover-pull-hint" v-if="projectInfo.cover">
|
||||||
|
<van-icon :name="coverExpanded ? 'arrow-up' : 'arrow-down'"></van-icon>
|
||||||
|
<span>{{ coverExpanded ? '上滑收起封面' : '下拉查看完整封面' }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="main-content" v-if="!pageLoading && projectInfo.id">
|
<div class="main-content" v-if="!pageLoading && projectInfo.id">
|
||||||
|
<section class="welfare-project-card"
|
||||||
|
@touchstart="handleCoverTouchStart"
|
||||||
|
@touchmove="handleCoverTouchMove"
|
||||||
|
@touchend="handleCoverTouchEnd"
|
||||||
|
@touchcancel="handleCoverTouchEnd">
|
||||||
|
<div class="welfare-project-card__head">
|
||||||
|
<div class="welfare-project-card__title-wrap">
|
||||||
|
<div class="welfare-project-card__title">{{ projectInfo.name }}</div>
|
||||||
|
<span class="welfare-project-card__festival" v-if="projectInfo.festival">{{ projectInfo.festival }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="welfare-project-card__status" :class="{'is-ended': isDeadlinePassed}">{{ projectChoiceStatus }}</div>
|
||||||
|
<div class="welfare-project-card__facts">
|
||||||
|
<div class="welfare-project-fact">
|
||||||
|
<van-icon class="welfare-project-fact__icon" name="calendar-o"></van-icon>
|
||||||
|
<div class="welfare-project-fact__content">
|
||||||
|
<div class="welfare-project-fact__label">选择截止</div>
|
||||||
|
<div class="welfare-project-fact__value">{{ formatProjectDateTime(projectInfo.choiceTimeEnd) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="welfare-project-fact">
|
||||||
|
<van-icon class="welfare-project-fact__icon" name="clock-o"></van-icon>
|
||||||
|
<div class="welfare-project-fact__content">
|
||||||
|
<div class="welfare-project-fact__label">发放时间</div>
|
||||||
|
<div class="welfare-project-fact__value">{{ formatProjectDateRange(projectInfo.provideTimeStart, projectInfo.provideTimeEnd) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="welfare-project-card__meta">
|
||||||
|
<div class="welfare-project-card__address">
|
||||||
|
<van-icon name="location-o"></van-icon>
|
||||||
|
<span>发放地点:{{ projectInfo.provideAddress || '待定' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="welfare-project-card__tags">
|
||||||
|
<span class="welfare-project-card__tag">{{ projectInfo.isCheckBox === 'radio' ? '单选' : '多选' }}</span>
|
||||||
|
<span class="welfare-project-card__tag">最多{{ selectionLimit }}份</span>
|
||||||
|
<span class="welfare-project-card__tag" v-if="hasExpressOption">支持快递</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- 截止时间提醒 -->
|
<!-- 截止时间提醒 -->
|
||||||
<div class="welfare-deadline" v-if="isDeadlineSoon">
|
<div class="welfare-deadline" v-if="isDeadlineSoon">
|
||||||
<van-icon name="warning-o"/>
|
<van-icon name="warning-o"/>
|
||||||
<span>选择截止时间即将到期,请尽快选择!</span>
|
<span>选择截止时间即将到期,请尽快选择!</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 标题到达顶部导航栏下方后固定,福利卡片继续在标题下方滚动。 -->
|
<div class="welfare-section-title">
|
||||||
<van-sticky :offset-top="46" :z-index="30">
|
<div class="title-text">
|
||||||
<div class="welfare-section-title">
|
<span class="title-label">福利选项</span>
|
||||||
<div class="title-text">
|
<span class="title-count">({{ projectInfo.options.length }})</span>
|
||||||
<span class="title-count">共 {{ projectInfo.options.length }} 项</span>
|
|
||||||
</div>
|
|
||||||
<div class="title-selection">已选 <strong>{{ selectedCount }}</strong>/{{ selectionLimit }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</van-sticky>
|
<div class="title-selection">已选 <strong>{{ selectedCount }}</strong>/{{ selectionLimit }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 福利选项列表 -->
|
<!-- 福利选项列表 -->
|
||||||
<div class="welfare-options-container">
|
<div class="welfare-options-container">
|
||||||
@@ -834,12 +1337,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
class="welfare-option"
|
class="welfare-option"
|
||||||
:class="{ selected: projectInfo.isCheckBox === 'radio' ? selectedRadioId === option.id : option.selectNum > 0 }"
|
:class="{ selected: projectInfo.isCheckBox === 'radio' ? selectedRadioId === option.id : option.selectNum > 0 }"
|
||||||
>
|
>
|
||||||
<van-icon
|
|
||||||
v-if="projectInfo.isCheckBox === 'radio' ? selectedRadioId === option.id : option.selectNum > 0"
|
|
||||||
class="welfare-option-selected-mark"
|
|
||||||
name="success"
|
|
||||||
></van-icon>
|
|
||||||
|
|
||||||
<div class="welfare-option-main">
|
<div class="welfare-option-main">
|
||||||
<div class="welfare-option-image" role="button" aria-label="查看福利图片"
|
<div class="welfare-option-image" role="button" aria-label="查看福利图片"
|
||||||
@click.stop="previewOptionImage(option)">
|
@click.stop="previewOptionImage(option)">
|
||||||
@@ -862,6 +1359,10 @@ layout("/layouts/platform_h5.html"){
|
|||||||
{{ option.optionName }}
|
{{ option.optionName }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="welfare-option-supplier" v-if="option.supplier || option.shoppingName">
|
||||||
|
供应商:{{ option.supplier || option.shoppingName }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- <div class="welfare-option-tag-row">-->
|
<!-- <div class="welfare-option-tag-row">-->
|
||||||
<!-- <span class="welfare-option-type-tag">套餐</span>-->
|
<!-- <span class="welfare-option-type-tag">套餐</span>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
@@ -880,10 +1381,21 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<van-icon name="arrow"/>
|
<van-icon name="arrow"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="welfare-option-meta">
|
||||||
|
<div class="welfare-option-meta__tags">
|
||||||
|
<span class="welfare-option-meta__tag" v-if="option.isExpressDelivery">支持快递</span>
|
||||||
|
<span class="welfare-option-meta__tag" v-if="option.isSystemDefault">系统默认</span>
|
||||||
|
</div>
|
||||||
|
<span class="welfare-option-meta__total" v-if="option.selectedTotal !== undefined && option.selectedTotal !== null">
|
||||||
|
累计选择 {{ option.selectedTotal }} 份
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="welfare-option-select-row" @click.stop>
|
<div class="welfare-option-select-row" @click.stop>
|
||||||
|
<div class="welfare-option-select-label">选择数量</div>
|
||||||
<div class="welfare-option-checkbox">
|
<div class="welfare-option-checkbox">
|
||||||
<van-stepper
|
<van-stepper
|
||||||
:key="'input-number-'+index+'-'+(option.selectNumKey || 0)"
|
:key="'input-number-'+index+'-'+(option.selectNumKey || 0)"
|
||||||
@@ -906,11 +1418,28 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 底部提交按钮 -->
|
<!-- 底部提交按钮 -->
|
||||||
<div class="welfare-footer" v-if="!pageLoading && projectInfo.id">
|
<div class="welfare-footer" :class="{'has-contact': fromMine && hasSubmittedBefore}" v-if="!pageLoading && projectInfo.id">
|
||||||
<van-button type="primary" class="welfare-submit-btn" :disabled="submitButtonDisabled"
|
<div class="welfare-footer__contact" v-if="fromMine && hasSubmittedBefore">
|
||||||
@click="submitSelection" round>
|
<div class="welfare-footer__contact-icon"><van-icon name="location"></van-icon></div>
|
||||||
{{ isDeadlinePassed ? '已截止' : '确认选择' }}
|
<div class="welfare-footer__contact-info">
|
||||||
</van-button>
|
<div class="welfare-footer__contact-primary">
|
||||||
|
<span class="welfare-footer__contact-label">收货人:</span>
|
||||||
|
<span class="welfare-footer__contact-name">{{ formData.userName || '暂无' }}</span>
|
||||||
|
<span class="welfare-footer__contact-mobile">{{ formData.mobile || '暂无' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="welfare-footer__contact-address">
|
||||||
|
<span class="welfare-footer__contact-label">收货地址:</span>
|
||||||
|
<span class="welfare-footer__contact-address-value">{{ formData.receiveAddress || '暂无' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="welfare-footer__actions">
|
||||||
|
<div class="welfare-footer__selection">已选 <strong>{{ selectedCount }}</strong>/{{ selectionLimit }}</div>
|
||||||
|
<van-button type="primary" class="welfare-submit-btn" :class="{'is-deadline-passed': isDeadlinePassed}" :disabled="submitButtonDisabled"
|
||||||
|
@click="submitSelection" round>
|
||||||
|
{{ isDeadlinePassed ? '已截止' : '确认选择' }}
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 全屏确认页从右侧滑入;顶部导航栏固定,表单内容在中间区域独立滚动。 -->
|
<!-- 全屏确认页从右侧滑入;顶部导航栏固定,表单内容在中间区域独立滚动。 -->
|
||||||
@@ -1057,6 +1586,12 @@ layout("/layouts/platform_h5.html"){
|
|||||||
projectInfo: {},
|
projectInfo: {},
|
||||||
userSelection: [],
|
userSelection: [],
|
||||||
projectId: "",
|
projectId: "",
|
||||||
|
fromMine: false,
|
||||||
|
coverPullStartY: 0,
|
||||||
|
coverPullStartDistance: 0,
|
||||||
|
coverPullDistance: 0,
|
||||||
|
coverPullEnabled: false,
|
||||||
|
coverExpanded: false,
|
||||||
selectedRadioId: "", // 单选模式下选中的选项ID
|
selectedRadioId: "", // 单选模式下选中的选项ID
|
||||||
showConfirmDialog: false,
|
showConfirmDialog: false,
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
@@ -1069,6 +1604,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
userSign: "", // 用户签名
|
userSign: "", // 用户签名
|
||||||
mobile: "", // 手机号码
|
mobile: "", // 手机号码
|
||||||
address: "",// 收货地址
|
address: "",// 收货地址
|
||||||
|
receiveAddress: "", // 已提交的收货地址
|
||||||
userName: "",//收货人
|
userName: "",//收货人
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1080,6 +1616,34 @@ layout("/layouts/platform_h5.html"){
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
||||||
|
coverHeaderStyle() {
|
||||||
|
const viewportWidth = Math.min(document.documentElement.clientWidth || window.innerWidth || 390, 430)
|
||||||
|
const baseHeight = Math.max(230, Math.min(Math.round(viewportWidth * 0.66), 280))
|
||||||
|
return {height: baseHeight + this.coverPullDistance + "px"}
|
||||||
|
},
|
||||||
|
|
||||||
|
coverMaxPullDistance() {
|
||||||
|
const viewportHeight = document.documentElement.clientHeight || window.innerHeight || 800
|
||||||
|
return Math.max(170, Math.min(Math.round(viewportHeight * 0.25), 220))
|
||||||
|
},
|
||||||
|
|
||||||
|
coverImageFit() {
|
||||||
|
return this.coverPullDistance > 48 ? "contain" : "cover"
|
||||||
|
},
|
||||||
|
|
||||||
|
projectChoiceStatus() {
|
||||||
|
const now = Date.now()
|
||||||
|
const startTime = this.projectInfo.choiceTimeStart ? new Date(this.projectInfo.choiceTimeStart).getTime() : null
|
||||||
|
const endTime = this.projectInfo.choiceTimeEnd ? new Date(this.projectInfo.choiceTimeEnd).getTime() : null
|
||||||
|
if (startTime && now < startTime) return "选择未开始"
|
||||||
|
if (endTime && now > endTime) return "选择已截止"
|
||||||
|
return "选择进行中"
|
||||||
|
},
|
||||||
|
|
||||||
|
hasExpressOption() {
|
||||||
|
return !!(this.projectInfo.options || []).find((option) => option.isExpressDelivery)
|
||||||
|
},
|
||||||
|
|
||||||
// 系统默认福利必须被保留,管理端限制每个项目最多配置一个。
|
// 系统默认福利必须被保留,管理端限制每个项目最多配置一个。
|
||||||
systemDefaultOption() {
|
systemDefaultOption() {
|
||||||
if (!this.projectInfo.options) return null
|
if (!this.projectInfo.options) return null
|
||||||
@@ -1152,6 +1716,64 @@ layout("/layouts/platform_h5.html"){
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* H5 布局由 page-wrapper 承担实际滚动,页面逻辑统一读取该容器而不是 window。
|
||||||
|
*/
|
||||||
|
getPageScroller() {
|
||||||
|
return document.querySelector("body > .main-page .page-wrapper") || document.querySelector(".page-wrapper")
|
||||||
|
},
|
||||||
|
|
||||||
|
resetPageScroll() {
|
||||||
|
const pageScroller = this.getPageScroller()
|
||||||
|
if (pageScroller) pageScroller.scrollTop = 0
|
||||||
|
window.scrollTo(0, 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅在页面位于顶部时启用封面下拉,避免与正常列表滚动冲突。
|
||||||
|
*/
|
||||||
|
handleCoverTouchStart(event) {
|
||||||
|
const pageScroller = this.getPageScroller()
|
||||||
|
const scrollTop = pageScroller ? pageScroller.scrollTop : window.pageYOffset
|
||||||
|
if (scrollTop > 0 || !event.touches || !event.touches.length) return
|
||||||
|
this.coverPullStartY = event.touches[0].clientY
|
||||||
|
this.coverPullStartDistance = this.coverPullDistance
|
||||||
|
this.coverPullEnabled = true
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封面展开后保留展示高度,主题信息卡片随封面底边移动;反向上滑可逐步收起。
|
||||||
|
*/
|
||||||
|
handleCoverTouchMove(event) {
|
||||||
|
if (!this.coverPullEnabled || !event.touches || !event.touches.length) return
|
||||||
|
const distance = event.touches[0].clientY - this.coverPullStartY
|
||||||
|
if (this.coverPullStartDistance === 0 && distance <= 0) return
|
||||||
|
this.coverPullDistance = Math.max(0, Math.min(
|
||||||
|
this.coverPullStartDistance + Math.round(distance * 0.65),
|
||||||
|
this.coverMaxPullDistance
|
||||||
|
))
|
||||||
|
if (event.cancelable) event.preventDefault()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCoverTouchEnd() {
|
||||||
|
if (!this.coverPullEnabled) return
|
||||||
|
const shouldExpand = this.coverPullDistance >= this.coverMaxPullDistance * 0.5
|
||||||
|
this.coverPullEnabled = false
|
||||||
|
this.coverExpanded = shouldExpand
|
||||||
|
this.coverPullDistance = shouldExpand ? this.coverMaxPullDistance : 0
|
||||||
|
},
|
||||||
|
|
||||||
|
formatProjectDateTime(value) {
|
||||||
|
return value ? this.$moment(value).format("MM/DD HH:mm") : "待定"
|
||||||
|
},
|
||||||
|
|
||||||
|
formatProjectDateRange(startTime, endTime) {
|
||||||
|
if (!startTime && !endTime) return "待定"
|
||||||
|
if (!startTime) return this.$moment(endTime).format("MM/DD")
|
||||||
|
if (!endTime) return this.$moment(startTime).format("MM/DD")
|
||||||
|
return this.$moment(startTime).format("MM/DD") + "–" + this.$moment(endTime).format("MM/DD")
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册当前福利页面的弹层历史。
|
* 注册当前福利页面的弹层历史。
|
||||||
* 页面标识由固定前缀和福利项目 ID 组成;回调参数是当前弹层名称数组,无返回值。
|
* 页面标识由固定前缀和福利项目 ID 组成;回调参数是当前弹层名称数组,无返回值。
|
||||||
@@ -1724,6 +2346,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.projectId = GetQueryString("id")
|
this.projectId = GetQueryString("id")
|
||||||
|
this.fromMine = GetQueryString("from") === "mine"
|
||||||
if (this.projectId) {
|
if (this.projectId) {
|
||||||
this.registerHistoryLayers()
|
this.registerHistoryLayers()
|
||||||
this.loadPageData()
|
this.loadPageData()
|
||||||
@@ -1735,6 +2358,11 @@ layout("/layouts/platform_h5.html"){
|
|||||||
}, 1500)
|
}, 1500)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.resetPageScroll()
|
||||||
|
})
|
||||||
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (window.h5HistoryLayerManager) {
|
if (window.h5HistoryLayerManager) {
|
||||||
window.h5HistoryLayerManager.unregister(this.historyLayerPageKey)
|
window.h5HistoryLayerManager.unregister(this.historyLayerPageKey)
|
||||||
|
|||||||
@@ -265,35 +265,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.welfare-list-card__info {
|
|
||||||
display: flex;
|
|
||||||
min-width: 0;
|
|
||||||
margin-top: 8px;
|
|
||||||
align-items: center;
|
|
||||||
color: #718198;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-list-card__info .van-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
flex: none;
|
|
||||||
color: #8da2bd;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-list-card__info-label {
|
|
||||||
flex: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-list-card__info-value {
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
color: #42546c;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welfare-list-card__status {
|
.welfare-list-card__status {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
min-width: 54px;
|
min-width: 54px;
|
||||||
@@ -517,11 +488,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
{{ getChoiceModeText(row) }}
|
{{ getChoiceModeText(row) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="welfare-list-card__info" v-if="row.gift">
|
|
||||||
<van-icon name="gift-o"></van-icon>
|
|
||||||
<span class="welfare-list-card__info-label">福利:</span>
|
|
||||||
<span class="welfare-list-card__info-value">{{ row.gift }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="welfare-list-card__time" v-if="row.choiceTimeStart && row.choiceTimeEnd">
|
<div class="welfare-list-card__time" v-if="row.choiceTimeStart && row.choiceTimeEnd">
|
||||||
<van-icon name="clock-o"></van-icon>
|
<van-icon name="clock-o"></van-icon>
|
||||||
<span>选择时间:{{ $moment(row.choiceTimeStart).format('MM/DD HH:mm') }} - {{ $moment(row.choiceTimeEnd).format('MM/DD HH:mm') }}</span>
|
<span>选择时间:{{ $moment(row.choiceTimeStart).format('MM/DD HH:mm') }} - {{ $moment(row.choiceTimeEnd).format('MM/DD HH:mm') }}</span>
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell :border="false" :value="projectInfo.name" title="福利名称"></van-cell>
|
<van-cell :border="false" :value="projectInfo.name" title="福利名称"></van-cell>
|
||||||
<van-cell :border="false" :value="projectInfo.festival" title="发放节日"></van-cell>
|
<van-cell :border="false" :value="projectInfo.festival" title="发放节日"></van-cell>
|
||||||
<van-cell :border="false" :value="projectInfo.gift" title="福利礼品" v-if="!projectInfo.flexible"></van-cell>
|
|
||||||
<van-cell :border="false" title="选择形式">{{projectInfo.isCheckBox==='checkBox'?'多选':'单选'}}</van-cell>
|
<van-cell :border="false" title="选择形式">{{projectInfo.isCheckBox==='checkBox'?'多选':'单选'}}</van-cell>
|
||||||
<van-cell
|
<van-cell
|
||||||
:border="false"
|
:border="false"
|
||||||
|
|||||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
|||||||
|
887dae22390914b7be8401e8ce87571d8f776eb6
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
71eb5eaecf2b7fd91d9dd7409a56314b9f2f132b3dffbb9e13ee56d0999b8c59
|
||||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
|||||||
|
5bc042d969b2a8fae96441b439096181eb11bbce
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
0d9b4629144a1fd15ed01362ce0dc2c9c5246e4f57250dd95fb52dbbbb75c532
|
||||||
+150
@@ -0,0 +1,150 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-project</artifactId>
|
||||||
|
<version>9.4.57.v20241219</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>jetty-client</artifactId>
|
||||||
|
<version>9.4.57.v20241219-cug-patch1</version>
|
||||||
|
<name>Jetty :: Asynchronous HTTP Client</name>
|
||||||
|
<properties>
|
||||||
|
<bundle-symbolic-name>${project.groupId}.client</bundle-symbolic-name>
|
||||||
|
<jetty.test.policy.loc>target/test-policy</jetty.test.policy.loc>
|
||||||
|
<spotbugs.onlyAnalyze>org.eclipse.client.*</spotbugs.onlyAnalyze>
|
||||||
|
</properties>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>unpack</id>
|
||||||
|
<phase>generate-test-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>unpack</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<artifactItems>
|
||||||
|
<artifactItem>
|
||||||
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
|
<artifactId>jetty-test-policy</artifactId>
|
||||||
|
<version>${jetty-test-policy.version}</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<overWrite>true</overWrite>
|
||||||
|
<includes>**/*.keystore,**/*.pem</includes>
|
||||||
|
<outputDirectory>${jetty.test.policy.loc}</outputDirectory>
|
||||||
|
</artifactItem>
|
||||||
|
</artifactItems>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<minimizeJar>true</minimizeJar>
|
||||||
|
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||||
|
<shadedClassifierName>hybrid</shadedClassifierName>
|
||||||
|
<artifactSet>
|
||||||
|
<includes>
|
||||||
|
<include>org.eclipse.jetty:jetty-http</include>
|
||||||
|
<include>org.eclipse.jetty:jetty-io</include>
|
||||||
|
<include>org.eclipse.jetty:jetty-util</include>
|
||||||
|
</includes>
|
||||||
|
</artifactSet>
|
||||||
|
<relocations>
|
||||||
|
<relocation>
|
||||||
|
<pattern>org.eclipse.jetty.http</pattern>
|
||||||
|
<shadedPattern>org.eclipse.jetty.client.shaded.http</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
<relocation>
|
||||||
|
<pattern>org.eclipse.jetty.io</pattern>
|
||||||
|
<shadedPattern>org.eclipse.jetty.client.shaded.io</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
<relocation>
|
||||||
|
<pattern>org.eclipse.jetty.util</pattern>
|
||||||
|
<shadedPattern>org.eclipse.jetty.client.shaded.util</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
</relocations>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-http</artifactId>
|
||||||
|
<version>9.4.57.v20241219-cug-patch1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-io</artifactId>
|
||||||
|
<version>9.4.57.v20241219</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-jmx</artifactId>
|
||||||
|
<version>9.4.57.v20241219</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
<version>9.4.57.v20241219-cug-patch1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-security</artifactId>
|
||||||
|
<version>9.4.57.v20241219-cug-patch1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.kerby</groupId>
|
||||||
|
<artifactId>kerb-simplekdc</artifactId>
|
||||||
|
<version>2.1.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.jline</groupId>
|
||||||
|
<artifactId>jline</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<!-- transitive dependency defined as a range and we don't want that -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.minidev</groupId>
|
||||||
|
<artifactId>json-smart</artifactId>
|
||||||
|
<version>2.5.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
|
<artifactId>jetty-test-helper</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
024e0627736731cbd441931d50fcb22bf17f4cc0
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
989a013455688dc528a988c41514fbfece0e3354a54398af5a404cda582c94bd
|
||||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
|||||||
|
d551472980052c26105c45dc476bfe07cc23dfb5
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
dcfe694ef6f27d9d6268e2fb135a1c9a7aabdfa55e7d738fc8df79f573086027
|
||||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
|||||||
|
086fa31420e9a5a623cac7967b0b6de0674d93af
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
90058e595b73d85b96e192b7e6806098f83467680a95466974f445d6706ed682
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>jetty-project</artifactId>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<version>9.4.57.v20241219</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>jetty-http</artifactId>
|
||||||
|
<version>9.4.57.v20241219-cug-patch1</version>
|
||||||
|
<name>Jetty :: Http Utility</name>
|
||||||
|
<properties>
|
||||||
|
<bundle-symbolic-name>${project.groupId}.http</bundle-symbolic-name>
|
||||||
|
<spotbugs.onlyAnalyze>org.eclipse.jetty.http.*</spotbugs.onlyAnalyze>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-util</artifactId>
|
||||||
|
<version>9.4.57.v20241219</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-io</artifactId>
|
||||||
|
<version>9.4.57.v20241219</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
|
<artifactId>jetty-test-helper</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<configuration>
|
||||||
|
<instructions>
|
||||||
|
<Require-Capability>osgi.serviceloader; filter:="(osgi.serviceloader=org.eclipse.jetty.http.HttpFieldPreEncoder)";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional</Require-Capability>
|
||||||
|
<Provide-Capability>osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.http.HttpFieldPreEncoder</Provide-Capability>
|
||||||
|
</instructions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>test-jar</id>
|
||||||
|
<goals>
|
||||||
|
<goal>test-jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<finalName>${jmhjar.name}</finalName>
|
||||||
|
<shadeTestJar>true</shadeTestJar>
|
||||||
|
<artifactSet>
|
||||||
|
<includes>
|
||||||
|
<include>org.openjdk.jmh:jmh-core</include>
|
||||||
|
</includes>
|
||||||
|
</artifactSet>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>org.openjdk.jmh.Main</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
<filters>
|
||||||
|
<filter>
|
||||||
|
<artifact>org.openjdk.jmh:jmh-core</artifact>
|
||||||
|
<includes>
|
||||||
|
<include>**</include>
|
||||||
|
</includes>
|
||||||
|
</filter>
|
||||||
|
<filter>
|
||||||
|
<artifact>*:*</artifact>
|
||||||
|
<excludes>
|
||||||
|
<exclude>META-INF/*.SF</exclude>
|
||||||
|
<exclude>META-INF/*.DSA</exclude>
|
||||||
|
<exclude>META-INF/*.RSA</exclude>
|
||||||
|
</excludes>
|
||||||
|
</filter>
|
||||||
|
</filters>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
f27754642c5b5e3b2e570669b7a1ceef613fc589
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
967fff3457fb62eb7485fe303cc4162edc1c02b15498c2f8401fbca263649ab8
|
||||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
|||||||
|
0f398cff4f7ab827fecb9d4f657685b973512e35
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
770e71875fa37effaab5c11e932b9a52c2e019236062575b0403760242b0bf08
|
||||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
|||||||
|
b01b199c670073760b66dbd5f79bbf0c3639ffc5
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
d757e04e165d940c019e9346c36e9bf8d1a71c8b6b196519bda4200e2a16f5d4
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-project</artifactId>
|
||||||
|
<version>9.4.57.v20241219</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>jetty-jaspi</artifactId>
|
||||||
|
<version>9.4.57.v20241219-cug-patch1</version>
|
||||||
|
<name>Jetty :: JASPI Security</name>
|
||||||
|
<description>Jetty security infrastructure</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<bundle-symbolic-name>${project.groupId}.security.jaspi</bundle-symbolic-name>
|
||||||
|
<spotbugs.onlyAnalyze>org.eclipse.jetty.jaspi.*</spotbugs.onlyAnalyze>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>manifest</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<instructions>
|
||||||
|
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
|
||||||
|
<Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.security.Authenticator$Factory</Provide-Capability>
|
||||||
|
</instructions>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-security</artifactId>
|
||||||
|
<version>9.4.57.v20241219-cug-patch1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
|
<artifactId>jetty-test-helper</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||||
|
<artifactId>javax.security.auth.message</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.xml.bind</groupId>
|
||||||
|
<artifactId>jaxb-api</artifactId>
|
||||||
|
<version>2.3.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jaxb</groupId>
|
||||||
|
<artifactId>jaxb-runtime</artifactId>
|
||||||
|
<!-- Must stay on 3.x as 4.x is for Java 11 -->
|
||||||
|
<version>3.0.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.activation</groupId>
|
||||||
|
<artifactId>javax.activation-api</artifactId>
|
||||||
|
<version>1.2.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.geronimo.components</groupId>
|
||||||
|
<artifactId>geronimo-jaspi</artifactId>
|
||||||
|
<version>2.0.0</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.geronimo.specs</groupId>
|
||||||
|
<artifactId>geronimo-jaspic_1.0_spec</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
7be35862967482c8ebe9615f5d5cd47230f9f1ee
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
8e49cd7df44f050c96d896726129fa5c9d0ac5895261cda774c8c899e617e5a6
|
||||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
|||||||
|
41dcd1e0dc8c03bf94f2419067f5330217a43468
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
b9ff8bf676cf5911cce7080c438d5cb96436b27d25462a671cc47b2eb6a5f322
|
||||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
|||||||
|
56e0e92f7cefb02d7c5748b480f45c063c6163ae
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user