feat:职代会h5端文件预览兼容、增加切换届次功能

This commit is contained in:
2026-07-31 11:06:45 +08:00
parent cc71822aae
commit 5477355f64
10 changed files with 676 additions and 361 deletions
@@ -1,30 +1,23 @@
package com.budwk.app.zhgh.democratic.congress.h5controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import org.nutz.dao.Chain;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import com.budwk.app.zhgh.democratic.congress.service.CongressH5Service;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.Date;
@IocBean
@At("/platform/h5/congress")
@Ok("json:full")
public class CongressH5Controller {
@Inject
private Dao dao;
private CongressH5Service congressH5Service;
@At("")
@Ok("beetl:/platform/zhghh5/democratic/congress/index.html")
@@ -65,309 +58,52 @@ public class CongressH5Controller {
@At("/repTimes")
@SaCheckLogin
public Result repTimes(@Param("emplid") String emplid) {
String loginName = StrUtil.blankToDefault(emplid, SecurityUtil.getUserLoginname());
Sql sql = Sqls.create("""
SELECT
t.id,
t.id AS timeId,
t.name,
t.year,
t.session_id AS sessionId,
t.session_name AS sessionName,
s.type,
r.id AS repId,
r.delegation_id AS delegationId,
r.delegation_name AS delegationName
FROM congress_times t
INNER JOIN (
SELECT id, time_id, delegation_id, delegation_name
FROM congress_rep
WHERE emplid = @emplid
AND status = 2
AND IFNULL(deleted, 0) = 0
GROUP BY id, time_id, delegation_id, delegation_name
) r ON t.id = r.time_id
INNER JOIN congress_sessions s ON t.session_id = s.id
WHERE IFNULL(t.deleted, 0) = 0
AND IFNULL(s.deleted, 0) = 0
ORDER BY t.create_time DESC, t.id DESC
""");
sql.setParam("emplid", loginName);
sql.setCallback(Sqls.callback.maps());
dao.execute(sql);
return Result.success(sql.getList(NutMap.class));
return Result.success(congressH5Service.repTimes(emplid));
}
@At("/materialsTypeList")
@SaCheckLogin
public Result materialsTypeList() {
Sql sql = Sqls.create("""
SELECT
SUBSTRING_INDEX(
GROUP_CONCAT(
id
ORDER BY
CASE
WHEN id IN (
'congress_type_file_notice',
'congress_type_evaluation_vote',
'congress_type_issue_vote',
'congress_type_vote_notice',
'congress_type_prepare_material'
) THEN 0
ELSE 1
END,
sort_no ASC,
create_time ASC
),
',',
1
) AS id,
MIN(sort_no) AS sortNo,
name,
MAX(is_vote) AS isVote
FROM congress_materials_type
WHERE IFNULL(deleted, 0) = 0
GROUP BY name
ORDER BY sortNo ASC, name ASC
""");
sql.setCallback(Sqls.callback.maps());
dao.execute(sql);
return Result.success(sql.getList(NutMap.class));
return Result.success(congressH5Service.materialsTypeList());
}
@At("/materialsList")
@SaCheckLogin
public Result materialsList(@Param("typeId") String typeId, @Param("timeId") String timeId, @Param("emplid") String emplid) {
String loginName = StrUtil.blankToDefault(emplid, SecurityUtil.getUserLoginname());
Sql sql = Sqls.create("""
SELECT
cm.id,
cm.session_id AS sessionId,
cm.time_id AS timeId,
cm.type_id AS typeId,
cm.sort_no AS sortNo,
cm.name,
cm.file_name AS fileName,
cm.file_url AS fileUrl,
cm.file_type AS fileType,
cm.file_size AS fileSize,
cm.create_time AS uploadTime,
CASE
WHEN cvi.id IS NULL THEN NULL
WHEN cvr.id IS NOT NULL THEN true
ELSE false
END AS hasVoted,
CASE WHEN cvr.id IS NOT NULL THEN true ELSE false END AS voted
FROM congress_materials cm
INNER JOIN congress_materials_type cmt ON cm.type_id = cmt.id AND IFNULL(cmt.deleted, 0) = 0
LEFT JOIN congress_voting_issue cvi ON cm.id = cvi.materials_id
AND IFNULL(cvi.deleted, 0) = 0
AND cmt.is_vote = true
LEFT JOIN congress_voting_record cvr ON cvi.id = cvr.issue_id
AND cvr.emplid = @emplid
AND IFNULL(cvr.deleted, 0) = 0
WHERE IFNULL(cm.deleted, 0) = 0
AND cm.type_id = @typeId
AND cm.time_id = @timeId
ORDER BY cm.sort_no ASC, cm.create_time DESC
""");
sql.setParam("typeId", typeId);
sql.setParam("timeId", timeId);
sql.setParam("emplid", loginName);
sql.setCallback(Sqls.callback.maps());
dao.execute(sql);
return Result.success(sql.getList(NutMap.class));
return Result.success(congressH5Service.materialsList(typeId, timeId, emplid));
}
@At("/user/meetingList")
@SaCheckLogin
public Result userMeetingList(@Param("timeId") String timeId, @Param("emplid") String emplid) {
String loginName = StrUtil.blankToDefault(emplid, SecurityUtil.getUserLoginname());
Sql sql = Sqls.create("""
SELECT
m.id,
m.meeting_name AS meetingName,
m.sign_in_start_time AS signInStartTime,
m.sign_in_end_time AS signInEndTime,
m.session_id AS sessionId,
m.time_id AS timeId,
m.remark,
(
SELECT COUNT(1)
FROM congress_sign_in s
WHERE s.meeting_id = m.id
AND IFNULL(s.deleted, 0) = 0
) AS signInCount,
si.id AS signInId,
si.create_time AS userSignInTime,
r.id AS repId
FROM congress_sign_in_meeting m
LEFT JOIN congress_sign_in si ON m.id = si.meeting_id
AND IFNULL(si.deleted, 0) = 0
AND si.emplid = @emplid
LEFT JOIN congress_rep r ON r.time_id = m.time_id
AND r.emplid = @emplid
AND IFNULL(r.deleted, 0) = 0
WHERE IFNULL(m.deleted, 0) = 0
AND m.time_id = @timeId
ORDER BY m.sign_in_start_time DESC, m.create_time DESC
""");
sql.setParam("timeId", timeId);
sql.setParam("emplid", loginName);
sql.setCallback(Sqls.callback.maps());
dao.execute(sql);
return Result.success(sql.getList(NutMap.class));
return Result.success(congressH5Service.userMeetingList(timeId, emplid));
}
@At("/signin")
@SaCheckLogin
@Aop(TransAop.READ_COMMITTED)
public Result signin(@Param("repId") String repId,
@Param("sessionId") String sessionId,
@Param("timeId") String timeId,
@Param("meetingId") String meetingId) {
if (StrUtil.hasBlank(repId, sessionId, timeId, meetingId)) {
return Result.error("签到参数不完整");
}
int count = countBySql("""
SELECT COUNT(1)
FROM congress_sign_in
WHERE rep_id = @repId
AND meeting_id = @meetingId
AND IFNULL(deleted, 0) = 0
""", NutMap.NEW().addv("repId", repId).addv("meetingId", meetingId));
if (count > 0) {
return Result.success(true);
}
Sql repSql = Sqls.create("""
SELECT id, emplid, name, delegation_id AS delegationId
FROM congress_rep
WHERE id = @repId
AND IFNULL(deleted, 0) = 0
LIMIT 1
""");
repSql.setParam("repId", repId);
repSql.setCallback(Sqls.callback.map());
dao.execute(repSql);
NutMap rep = repSql.getObject(NutMap.class);
if (rep == null) {
return Result.error("未找到代表信息");
}
dao.insert("congress_sign_in", Chain.make("id", R.UU32())
.add("session_id", sessionId)
.add("time_id", timeId)
.add("meeting_id", meetingId)
.add("delegation_id", rep.getString("delegationId"))
.add("rep_id", repId)
.add("emplid", rep.getString("emplid"))
.add("name", rep.getString("name"))
.add("create_time", new Date())
.add("update_time", new Date())
.add("deleted", false));
return Result.success(true);
return congressH5Service.signin(repId, sessionId, timeId, meetingId);
}
@At("/votingIssue")
@SaCheckLogin
public Result votingIssue(@Param("materialsId") String materialsId, @Param("repId") String repId) {
Sql sql = Sqls.create("""
SELECT
i.id,
i.session_id AS sessionId,
i.session_name AS sessionName,
i.time_id AS timeId,
i.time_name AS timeName,
i.name,
i.description,
i.content,
i.voting_type AS votingType,
i.voting_start_time AS votingStartTime,
i.voting_end_time AS votingEndTime,
i.voting_content AS votingContent,
i.voting_content_item_header AS votingContentItemHeader,
i.voting_content_option_header AS votingContentOptionHeader,
i.materials_id AS materialsId,
i.type_id AS typeId,
i.delegation_id AS delegationId,
mt.name AS typeName,
CASE WHEN vr.id IS NULL THEN false ELSE true END AS voted,
vr.vote_result AS voteResult
FROM congress_voting_issue i
LEFT JOIN congress_materials_type mt ON mt.id = i.type_id AND IFNULL(mt.deleted, 0) = 0
LEFT JOIN congress_voting_record vr ON vr.issue_id = i.id
AND vr.rep_id = @repId
AND IFNULL(vr.deleted, 0) = 0
WHERE i.materials_id = @materialsId
AND IFNULL(i.deleted, 0) = 0
LIMIT 1
""");
sql.setParam("materialsId", materialsId);
sql.setParam("repId", repId);
sql.setCallback(Sqls.callback.map());
dao.execute(sql);
return Result.success(sql.getObject(NutMap.class));
return Result.success(congressH5Service.votingIssue(materialsId, repId));
}
@At("/api/vote")
@SaCheckLogin
@Aop(TransAop.READ_COMMITTED)
public Result voteSubmit(@Param("issueId") String issueId,
@Param("repId") String repId,
@Param("sessionId") String sessionId,
@Param("timeId") String timeId,
@Param("delegationId") String delegationId,
@Param("voteResult") String voteResult) {
if (StrUtil.hasBlank(issueId, repId, sessionId, timeId, voteResult)) {
return Result.error("投票参数不完整");
}
int count = countBySql("""
SELECT COUNT(1)
FROM congress_voting_record
WHERE issue_id = @issueId
AND rep_id = @repId
AND IFNULL(deleted, 0) = 0
""", NutMap.NEW().addv("issueId", issueId).addv("repId", repId));
if (count > 0) {
return Result.error("您已投票,请勿重复提交");
}
Sql repSql = Sqls.create("""
SELECT user_id AS userId, emplid, name
FROM congress_rep
WHERE id = @repId
AND IFNULL(deleted, 0) = 0
LIMIT 1
""");
repSql.setParam("repId", repId);
repSql.setCallback(Sqls.callback.map());
dao.execute(repSql);
NutMap rep = repSql.getObject(NutMap.class);
if (rep == null) {
return Result.error("未找到代表信息");
}
dao.insert("congress_voting_record", Chain.make("id", R.UU32())
.add("issue_id", issueId)
.add("rep_id", repId)
.add("emplid", rep.getString("emplid"))
.add("name", rep.getString("name"))
.add("session_id", sessionId)
.add("time_id", timeId)
.add("delegation_id", delegationId)
.add("vote_result", voteResult)
.add("user_id", rep.getString("userId"))
.add("create_time", new Date())
.add("update_time", new Date())
.add("deleted", false));
return Result.success();
}
private int countBySql(String sqlText, NutMap params) {
Sql sql = Sqls.create(sqlText);
params.forEach(sql::setParam);
sql.setCallback(Sqls.callback.integer());
dao.execute(sql);
return sql.getInt();
return congressH5Service.voteSubmit(issueId, repId, sessionId, timeId, delegationId, voteResult);
}
}
@@ -0,0 +1,24 @@
package com.budwk.app.zhgh.democratic.congress.service;
import com.budwk.app.base.result.Result;
import org.nutz.lang.util.NutMap;
import java.util.List;
public interface CongressH5Service {
List<NutMap> repTimes(String emplid);
List<NutMap> materialsTypeList();
List<NutMap> materialsList(String typeId, String sessionId, String emplid);
List<NutMap> userMeetingList(String sessionId, String emplid);
Result signin(String repId, String sessionId, String timeId, String meetingId);
NutMap votingIssue(String materialsId, String repId);
Result voteSubmit(String issueId, String repId, String sessionId, String timeId,
String delegationId, String voteResult);
}
@@ -0,0 +1,340 @@
package com.budwk.app.zhgh.democratic.congress.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.congress.service.CongressH5Service;
import org.nutz.dao.Chain;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import java.util.Date;
import java.util.List;
@IocBean
public class CongressH5ServiceImpl implements CongressH5Service {
@Inject
private Dao dao;
@Override
public List<NutMap> repTimes(String emplid) {
String loginName = loginName(emplid);
Sql sql = Sqls.create("""
SELECT
s.id,
s.id AS timeId,
COALESCE(s.fullName, CONCAT(s.year, '年职代会')) AS name,
s.year,
s.id AS sessionId,
s.fullName AS sessionName,
NULL AS type,
d.id AS repId,
d.delegationId AS delegationId,
dg.name AS delegationName
FROM teacher_congress_delegate d
INNER JOIN teacher_congress_session s ON s.id = d.sessionId
AND COALESCE(s.delFlag, 0) = 0
LEFT JOIN teacher_congress_delegation dg ON dg.id = d.delegationId
AND COALESCE(dg.delFlag, 0) = 0
WHERE d.loginName = @emplid
AND COALESCE(d.delFlag, 0) = 0
ORDER BY s.startDate DESC, s.createdAt DESC, s.id DESC
""");
sql.setParam("emplid", loginName);
sql.setCallback(Sqls.callback.maps());
dao.execute(sql);
return sql.getList(NutMap.class);
}
@Override
public List<NutMap> materialsTypeList() {
Sql sql = Sqls.create("""
SELECT
SUBSTRING_INDEX(
GROUP_CONCAT(
id
ORDER BY
CASE
WHEN id IN (
'congress_type_file_notice',
'congress_type_evaluation_vote',
'congress_type_issue_vote',
'congress_type_vote_notice',
'congress_type_prepare_material'
) THEN 0
ELSE 1
END,
sort_no ASC,
create_time ASC
),
',',
1
) AS id,
MIN(sort_no) AS sortNo,
name,
MAX(is_vote) AS isVote
FROM congress_materials_type
WHERE COALESCE(deleted, 0) = 0
GROUP BY name
ORDER BY sortNo ASC, name ASC
""");
sql.setCallback(Sqls.callback.maps());
dao.execute(sql);
return sql.getList(NutMap.class);
}
@Override
public List<NutMap> materialsList(String typeId, String sessionId, String emplid) {
String loginName = loginName(emplid);
Sql sql = Sqls.create("""
SELECT
cm.id,
cm.session_id AS sessionId,
COALESCE(cm.time_id, cm.session_id) AS timeId,
cm.type_id AS typeId,
cm.sort_no AS sortNo,
cm.name,
cm.file_name AS fileName,
cm.file_url AS fileUrl,
cm.file_type AS fileType,
cm.file_size AS fileSize,
cm.create_time AS uploadTime,
CASE
WHEN cvi.id IS NULL THEN NULL
WHEN cvr.id IS NOT NULL THEN true
ELSE false
END AS hasVoted,
CASE WHEN cvr.id IS NOT NULL THEN true ELSE false END AS voted
FROM congress_materials cm
INNER JOIN teacher_congress_delegate d ON d.sessionId = cm.session_id
AND d.loginName = @emplid
AND COALESCE(d.delFlag, 0) = 0
INNER JOIN congress_materials_type cmt ON cm.type_id = cmt.id
AND COALESCE(cmt.deleted, 0) = 0
LEFT JOIN congress_voting_issue cvi ON cm.id = cvi.materials_id
AND COALESCE(cvi.deleted, 0) = 0
AND COALESCE(cmt.is_vote, 0) = 1
LEFT JOIN congress_voting_record cvr ON cvi.id = cvr.issue_id
AND (cvr.rep_id = d.id OR cvr.emplid = @emplid)
AND COALESCE(cvr.deleted, 0) = 0
WHERE COALESCE(cm.deleted, 0) = 0
AND cm.type_id = @typeId
AND (cm.session_id = @sessionId OR cm.time_id = @sessionId)
ORDER BY cm.sort_no ASC, cm.create_time DESC
""");
sql.setParam("typeId", typeId);
sql.setParam("sessionId", sessionId);
sql.setParam("emplid", loginName);
sql.setCallback(Sqls.callback.maps());
dao.execute(sql);
return sql.getList(NutMap.class);
}
@Override
public List<NutMap> userMeetingList(String sessionId, String emplid) {
String loginName = loginName(emplid);
Sql sql = Sqls.create("""
SELECT
m.id,
m.meeting_name AS meetingName,
m.sign_in_start_time AS signInStartTime,
m.sign_in_end_time AS signInEndTime,
m.session_id AS sessionId,
COALESCE(m.time_id, m.session_id) AS timeId,
m.remark,
(
SELECT COUNT(1)
FROM congress_sign_in s
WHERE s.meeting_id = m.id
AND COALESCE(s.deleted, 0) = 0
) AS signInCount,
si.id AS signInId,
si.create_time AS userSignInTime,
d.id AS repId
FROM congress_sign_in_meeting m
INNER JOIN teacher_congress_delegate d ON d.sessionId = m.session_id
AND d.loginName = @emplid
AND COALESCE(d.delFlag, 0) = 0
LEFT JOIN congress_sign_in si ON m.id = si.meeting_id
AND COALESCE(si.deleted, 0) = 0
AND si.rep_id = d.id
WHERE COALESCE(m.deleted, 0) = 0
AND (m.session_id = @sessionId OR m.time_id = @sessionId)
ORDER BY m.sign_in_start_time DESC, m.create_time DESC
""");
sql.setParam("sessionId", sessionId);
sql.setParam("emplid", loginName);
sql.setCallback(Sqls.callback.maps());
dao.execute(sql);
return sql.getList(NutMap.class);
}
@Override
public Result signin(String repId, String sessionId, String timeId, String meetingId) {
if (StrUtil.hasBlank(repId, sessionId, meetingId)) {
return Result.error("签到参数不完整");
}
int count = count("""
SELECT COUNT(1)
FROM congress_sign_in
WHERE rep_id = @repId
AND meeting_id = @meetingId
AND COALESCE(deleted, 0) = 0
""", NutMap.NEW().addv("repId", repId).addv("meetingId", meetingId));
if (count > 0) {
return Result.success(true);
}
NutMap rep = fetchMap("""
SELECT id, userId, loginName, userName, delegationId
FROM teacher_congress_delegate
WHERE id = @repId
AND sessionId = @sessionId
AND COALESCE(delFlag, 0) = 0
LIMIT 1
""", NutMap.NEW().addv("repId", repId).addv("sessionId", sessionId));
if (rep == null) {
return Result.error("当前用户不是该届次职工代表,无法签到");
}
dao.insert("congress_sign_in", Chain.make("id", R.UU32())
.add("session_id", sessionId)
.add("time_id", StrUtil.blankToDefault(timeId, sessionId))
.add("meeting_id", meetingId)
.add("delegation_id", rep.getString("delegationId"))
.add("rep_id", repId)
.add("emplid", rep.getString("loginName"))
.add("name", rep.getString("userName"))
.add("create_time", new Date())
.add("update_time", new Date())
.add("deleted", false));
return Result.success(true);
}
@Override
public NutMap votingIssue(String materialsId, String repId) {
Sql sql = Sqls.create("""
SELECT
i.id,
i.session_id AS sessionId,
i.session_name AS sessionName,
COALESCE(i.time_id, i.session_id) AS timeId,
i.time_name AS timeName,
i.name,
i.description,
i.content,
i.voting_type AS votingType,
i.voting_start_time AS votingStartTime,
i.voting_end_time AS votingEndTime,
i.voting_content AS votingContent,
i.voting_content_item_header AS votingContentItemHeader,
i.voting_content_option_header AS votingContentOptionHeader,
i.materials_id AS materialsId,
i.type_id AS typeId,
i.delegation_id AS delegationId,
mt.name AS typeName,
CASE WHEN vr.id IS NULL THEN false ELSE true END AS voted,
vr.vote_result AS voteResult
FROM congress_voting_issue i
INNER JOIN teacher_congress_delegate d ON d.id = @repId
AND d.sessionId = i.session_id
AND COALESCE(d.delFlag, 0) = 0
LEFT JOIN congress_materials_type mt ON mt.id = i.type_id
AND COALESCE(mt.deleted, 0) = 0
LEFT JOIN congress_voting_record vr ON vr.issue_id = i.id
AND (vr.rep_id = d.id OR vr.emplid = d.loginName)
AND COALESCE(vr.deleted, 0) = 0
WHERE i.materials_id = @materialsId
AND COALESCE(i.deleted, 0) = 0
LIMIT 1
""");
sql.setParam("materialsId", materialsId);
sql.setParam("repId", repId);
sql.setCallback(Sqls.callback.map());
dao.execute(sql);
return sql.getObject(NutMap.class);
}
@Override
public Result voteSubmit(String issueId, String repId, String sessionId, String timeId,
String delegationId, String voteResult) {
if (StrUtil.hasBlank(issueId, repId, sessionId, voteResult)) {
return Result.error("投票参数不完整");
}
NutMap issue = fetchMap("""
SELECT id, session_id AS sessionId, COALESCE(time_id, session_id) AS timeId
FROM congress_voting_issue
WHERE id = @issueId
AND session_id = @sessionId
AND COALESCE(deleted, 0) = 0
LIMIT 1
""", NutMap.NEW().addv("issueId", issueId).addv("sessionId", sessionId));
if (issue == null) {
return Result.error("投票议题不存在");
}
NutMap rep = fetchMap("""
SELECT id, userId, loginName, userName, delegationId
FROM teacher_congress_delegate
WHERE id = @repId
AND sessionId = @sessionId
AND COALESCE(delFlag, 0) = 0
LIMIT 1
""", NutMap.NEW().addv("repId", repId).addv("sessionId", sessionId));
if (rep == null) {
return Result.error("当前用户不是该届次职工代表,无法投票");
}
int count = count("""
SELECT COUNT(1)
FROM congress_voting_record
WHERE issue_id = @issueId
AND rep_id = @repId
AND COALESCE(deleted, 0) = 0
""", NutMap.NEW().addv("issueId", issueId).addv("repId", repId));
if (count > 0) {
return Result.error("您已投票,请勿重复提交");
}
dao.insert("congress_voting_record", Chain.make("id", R.UU32())
.add("issue_id", issueId)
.add("rep_id", repId)
.add("emplid", rep.getString("loginName"))
.add("name", rep.getString("userName"))
.add("session_id", sessionId)
.add("time_id", StrUtil.blankToDefault(timeId, issue.getString("timeId")))
.add("delegation_id", StrUtil.blankToDefault(delegationId, rep.getString("delegationId")))
.add("vote_result", voteResult)
.add("user_id", rep.getString("userId"))
.add("create_time", new Date())
.add("update_time", new Date())
.add("deleted", false));
return Result.success();
}
private String loginName(String emplid) {
return StrUtil.blankToDefault(emplid, SecurityUtil.getUserLoginname());
}
private NutMap fetchMap(String sqlText, NutMap params) {
Sql sql = Sqls.create(sqlText);
params.forEach(sql::setParam);
sql.setCallback(Sqls.callback.map());
dao.execute(sql);
return sql.getObject(NutMap.class);
}
private int count(String sqlText, NutMap params) {
Sql sql = Sqls.create(sqlText);
params.forEach(sql::setParam);
sql.setCallback(Sqls.callback.integer());
dao.execute(sql);
return sql.getInt();
}
}
@@ -112,8 +112,11 @@
$(document).on("pjax:complete", function () {
if (window.customStyleList) {
window.customStyleList.forEach((style) => {
style.parentNode.removeChild(style)
if (style && style.parentNode) {
style.parentNode.removeChild(style)
}
})
window.customStyleList = null
}
NProgress.done()
toggleShowBar()
@@ -15,8 +15,12 @@ layout("/layouts/platform_h5.html"){
<div class="page-header">
<div class="session-info-card">
<h2 class="session-title">{{ selectedSessionName }}</h2>
<p class="session-subtitle">{{ selectedTimeText }}</p>
<!-- <p class="session-subtitle">{{ selectedTimeText }}</p>-->
</div>
<button v-if="timesList.length > 1" type="button" class="header-switch-btn" @click="showTimePicker = true">
<span>切换届次</span>
<van-icon name="arrow-down" size="12"></van-icon>
</button>
</div>
<div class="module-list-container">
@@ -41,12 +45,6 @@ layout("/layouts/platform_h5.html"){
</div>
</div>
<div v-if="timesList.length > 1" class="bottom-switch-bar">
<span class="switch-text" @click="showTimePicker = true">
<van-icon name="arrow-down" size="12"></van-icon>
切换届次
</span>
</div>
</template>
<van-popup v-model="showTimePicker" position="bottom">
@@ -69,6 +67,7 @@ layout("/layouts/platform_h5.html"){
loading: true,
timesList: [],
selectedTimeId: "",
selectedSessionId: "",
selectedTimeText: "",
selectedSessionName: "",
selectedRepId: "",
@@ -80,7 +79,7 @@ layout("/layouts/platform_h5.html"){
formattedTimeColumns() {
return this.timesList.map((item) => ({
text: item.name,
value: item.id
value: item.timeId || item.id || item.sessionId
}))
}
},
@@ -90,7 +89,8 @@ layout("/layouts/platform_h5.html"){
try {
await this.fetchRepTimes()
if (this.timesList.length > 0) {
this.selectTime(this.timesList[0])
const initialTimeId = this.initialTimeId()
this.selectTime(this.resolveInitialTime(initialTimeId), !!initialTimeId)
await this.fetchModules()
}
} finally {
@@ -108,6 +108,13 @@ layout("/layouts/platform_h5.html"){
this.timesList = res.data || []
}
},
initialTimeId() {
const params = new URLSearchParams(window.location.search)
return params.get("timeId") || params.get("sessionId") || ""
},
resolveInitialTime(timeId) {
return this.timesList.find((item) => this.sameTime(item, timeId)) || this.timesList[0]
},
async fetchModules() {
const res = await this.$axios.get("/platform/h5/congress/materialsTypeList")
if (res.code !== 0) return
@@ -125,19 +132,38 @@ layout("/layouts/platform_h5.html"){
desc: "点击进行会议签到"
}].concat(modules)
},
selectTime(item) {
this.selectedTimeId = item.id || item.timeId || ""
selectTime(item, syncUrl) {
this.selectedTimeId = item.timeId || item.id || item.sessionId || ""
this.selectedSessionId = item.sessionId || this.selectedTimeId
this.selectedTimeText = item.name || ""
this.selectedSessionName = item.sessionName || ""
this.selectedRepId = item.repId || ""
window.localStorage.setItem("congressTimeId", this.selectedTimeId)
window.localStorage.setItem("congressSessionId", this.selectedSessionId)
window.localStorage.setItem("timeName", this.selectedTimeText)
window.localStorage.setItem("sessionName", this.selectedSessionName)
window.localStorage.setItem("repId", this.selectedRepId)
if (syncUrl) {
this.syncSelectedTimeToUrl()
}
},
sameTime(item, timeId) {
if (!item || !timeId) return false
return item.id === timeId || item.timeId === timeId || item.sessionId === timeId
},
syncSelectedTimeToUrl() {
if (!this.selectedTimeId) return
const params = new URLSearchParams(window.location.search)
params.set("timeId", this.selectedTimeId)
const nextUrl = window.location.pathname + "?" + params.toString()
const state = Object.assign({}, window.history.state || {})
state.url = nextUrl
window.history.replaceState(state, document.title, nextUrl)
},
onTimeConfirm(value) {
const selected = this.timesList.find((item) => item.id === value.value)
const selected = this.timesList.find((item) => this.sameTime(item, value.value))
if (selected) {
this.selectTime(selected)
this.selectTime(selected, true)
this.fetchModules()
}
this.showTimePicker = false
@@ -148,13 +174,15 @@ layout("/layouts/platform_h5.html"){
return
}
if (module.id === "signin" || module.name.indexOf("会议签到") > -1) {
this.$pjaxReplace("/platform/h5/congress/meeting/list?moduleId=signin&moduleName=会议签到&timeId=" + encodeURIComponent(this.selectedTimeId))
this.$pjaxReplace("/platform/h5/congress/meeting/list?moduleId=signin&moduleName=会议签到&timeId=" + encodeURIComponent(this.selectedTimeId) +
"&sessionId=" + encodeURIComponent(this.selectedSessionId))
return
}
this.$pjaxReplace("/platform/h5/congress/materials/list?moduleId=" + encodeURIComponent(module.id) +
"&typeId=" + encodeURIComponent(module.id) +
"&moduleName=" + encodeURIComponent(module.name) +
"&timeId=" + encodeURIComponent(this.selectedTimeId))
"&timeId=" + encodeURIComponent(this.selectedTimeId) +
"&sessionId=" + encodeURIComponent(this.selectedSessionId))
},
resolveIcon(name) {
if (!name) return "orders-o"
@@ -191,12 +219,13 @@ layout("/layouts/platform_h5.html"){
.page-header {
background: linear-gradient(135deg, #1989fa 0%, #0d6efd 100%);
padding: 24px 16px 28px;
padding: 34px 16px 28px;
color: #fff;
box-shadow: 0 4px 16px rgba(25, 137, 250, 0.15);
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.session-info-card {
@@ -221,6 +250,31 @@ layout("/layouts/platform_h5.html"){
margin: 0;
}
.header-switch-btn {
position: absolute;
top: 10px;
right: 12px;
height: 28px;
padding: 0 10px;
border: 0;
border-radius: 14px;
background: rgba(255, 255, 255, 0.18);
color: #fff;
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
line-height: 1;
cursor: pointer;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
backdrop-filter: blur(8px);
}
.header-switch-btn:active {
background: rgba(255, 255, 255, 0.28);
transform: scale(0.98);
}
.module-list-container {
padding: 16px;
}
@@ -327,22 +381,6 @@ layout("/layouts/platform_h5.html"){
transform: translateX(4px);
}
.bottom-switch-bar {
padding: 20px 16px calc(20px + env(safe-area-inset-bottom));
display: flex;
justify-content: center;
}
.switch-text {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 13px;
color: #969799;
cursor: pointer;
user-select: none;
}
.empty-box {
min-height: calc(100vh - 46px);
display: flex;
@@ -3,7 +3,7 @@ layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar :title="pageTitle" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<van-nav-bar :title="pageTitle" left-text="返回" left-arrow @click-left="backToCongress" placeholder fixed></van-nav-bar>
<div class="file-list-page">
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
@@ -16,7 +16,7 @@ layout("/layouts/platform_h5.html"){
<div class="card-left">
<div class="icon-wrapper">
<van-icon :name="getFileIcon(item.fileType)" size="36"></van-icon>
<img :src="getFileIconUrl(item)" :alt="resolveFileSuffix(item) || 'file'" class="file-type-icon">
</div>
</div>
<div class="card-content">
@@ -51,7 +51,8 @@ layout("/layouts/platform_h5.html"){
refreshing: false,
loadedOnce: false,
typeId: "",
timeId: ""
timeId: "",
sessionId: ""
}
},
methods: {
@@ -60,6 +61,11 @@ layout("/layouts/platform_h5.html"){
this.pageTitle = params.get("moduleName") || "文件公告"
this.typeId = params.get("typeId") || params.get("moduleId") || ""
this.timeId = params.get("timeId") || ""
this.sessionId = params.get("sessionId") || this.timeId
},
backToCongress() {
const timeId = this.timeId || this.sessionId || window.localStorage.getItem("congressTimeId") || ""
this.$pjaxReplace("/platform/h5/congress" + (timeId ? "?timeId=" + encodeURIComponent(timeId) : ""))
},
loginName() {
const user = (this.$store.state && this.$store.state.user) || JSON.parse(window.sessionStorage.getItem("user") || "{}")
@@ -95,16 +101,39 @@ layout("/layouts/platform_h5.html"){
this.refreshing = false
})
},
getFileIcon(mimeType) {
if (!mimeType) return "description"
const type = String(mimeType).toLowerCase()
if (type.indexOf("pdf") > -1) return "description"
if (type.indexOf("word") > -1) return "edit"
if (type.indexOf("excel") > -1 || type.indexOf("sheet") > -1) return "chart-trending-o"
if (type.indexOf("powerpoint") > -1 || type.indexOf("presentation") > -1) return "play-circle-o"
if (type.indexOf("image") > -1) return "photo"
if (type.indexOf("zip") > -1 || type.indexOf("rar") > -1) return "bag-o"
return "description"
getFileIconUrl(item) {
return this.$commonUtil.getFileItemShowIcon(this.resolveFileSuffix(item))
},
resolveFileSuffix(item) {
const fileType = String(item.fileType || "").replace(".", "").toLowerCase()
if (this.isKnownSuffix(fileType)) return fileType
const fileNameSuffix = this.extractSuffix(item.fileName || item.name || "")
if (fileNameSuffix) return fileNameSuffix
const fileUrlSuffix = this.extractSuffix((item.fileUrl || "").split("?")[0])
if (fileUrlSuffix) return fileUrlSuffix
if (fileType.indexOf("pdf") > -1) return "pdf"
if (fileType.indexOf("word") > -1) return "docx"
if (fileType.indexOf("excel") > -1 || fileType.indexOf("sheet") > -1) return "xlsx"
if (fileType.indexOf("powerpoint") > -1 || fileType.indexOf("presentation") > -1) return "pptx"
if (fileType.indexOf("image") > -1) return "jpg"
if (fileType.indexOf("zip") > -1 || fileType.indexOf("rar") > -1) return "zip"
if (fileType.indexOf("text") > -1) return "txt"
if (fileType.indexOf("video") > -1) return "mp4"
if (fileType.indexOf("audio") > -1) return "mp3"
return ""
},
extractSuffix(value) {
const text = String(value || "").trim()
const index = text.lastIndexOf(".")
if (index < 0 || index === text.length - 1) return ""
const suffix = text.substring(index + 1).toLowerCase()
return this.isKnownSuffix(suffix) ? suffix : ""
},
isKnownSuffix(suffix) {
return ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "zip", "rar", "jpg", "jpeg", "png", "gif", "bmp", "webp", "mp4", "avi", "wmv", "rmvb", "flv", "mkv", "mp3", "wav", "wma", "flac", "ape"].indexOf(suffix) > -1
},
formatTime(time) {
if (!time) return ""
@@ -126,12 +155,26 @@ layout("/layouts/platform_h5.html"){
return
}
this.$pjaxReplace("/platform/h5/congress/pdf?pdfPath=" + encodeURIComponent(item.fileUrl) +
"&fileName=" + encodeURIComponent(item.fileName || item.name || ""))
"&fileName=" + encodeURIComponent(item.fileName || item.name || "") +
"&fileType=" + encodeURIComponent(item.fileType || "") +
"&sysFileId=" + encodeURIComponent(this.resolveSysFileId(item.fileUrl)))
},
openVotePdf(item) {
this.$pjaxReplace("/platform/h5/congress/pdf?pdfPath=" + encodeURIComponent(item.fileUrl || "") +
"&fileName=" + encodeURIComponent(item.fileName || item.name || "") +
"&fileId=" + encodeURIComponent(item.id || ""))
"&materialsId=" + encodeURIComponent(item.id || "") +
"&fileType=" + encodeURIComponent(item.fileType || "") +
"&sysFileId=" + encodeURIComponent(this.resolveSysFileId(item.fileUrl)))
},
resolveSysFileId(fileUrl) {
if (!fileUrl) return ""
try {
const url = new URL(fileUrl, window.location.origin)
return url.searchParams.get("id") || ""
} catch (e) {
const match = String(fileUrl).match(/[?&]id=([^&]+)/)
return match ? decodeURIComponent(match[1]) : ""
}
},
isVoted(value) {
return value === true || value === 1 || value === "1" || value === "true"
@@ -224,12 +267,19 @@ layout("/layouts/platform_h5.html"){
.icon-wrapper {
width: 52px;
height: 52px;
border-radius: 14px;
background: linear-gradient(135deg, #f0f5ff 0%, #e6f0ff 100%);
border-radius: 12px;
background: #f7f8fa;
display: flex;
align-items: center;
justify-content: center;
color: #1989fa;
border: 1px solid #eef0f4;
}
.file-type-icon {
width: 38px;
height: 38px;
display: block;
object-fit: contain;
}
.card-content {
@@ -3,7 +3,7 @@ layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar title="会议签到" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<van-nav-bar title="会议签到" left-text="返回" left-arrow @click-left="backToCongress" placeholder fixed></van-nav-bar>
<div class="meeting-list-page">
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
@@ -63,13 +63,19 @@ layout("/layouts/platform_h5.html"){
finished: false,
refreshing: false,
loadedOnce: false,
timeId: ""
timeId: "",
sessionId: ""
}
},
methods: {
initQuery() {
const params = new URLSearchParams(window.location.search)
this.timeId = params.get("timeId") || ""
this.sessionId = params.get("sessionId") || this.timeId
},
backToCongress() {
const timeId = this.timeId || this.sessionId || window.localStorage.getItem("congressTimeId") || ""
this.$pjaxReplace("/platform/h5/congress" + (timeId ? "?timeId=" + encodeURIComponent(timeId) : ""))
},
loginName() {
const user = (this.$store.state && this.$store.state.user) || JSON.parse(window.sessionStorage.getItem("user") || "{}")
@@ -4,11 +4,15 @@ layout("/layouts/platform_h5.html"){
<div id="app" v-cloak>
<div class="pdf-viewer-page">
<van-nav-bar :title="fileName || 'PDF查看'" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<van-nav-bar :title="fileName || '文件预览'" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<div class="pdf-container" :class="{'has-footer': voteInfo}">
<iframe v-if="pdfPath" :src="previewUrl" class="pdf-frame"></iframe>
<div v-else class="pdf-empty"></div>
<div v-if="previewMode === 'pdf'" id="congress-pdf-container" class="pdf-render-box"></div>
<img v-else-if="previewMode === 'image'" :src="previewUrl" class="image-preview" @click="previewImage">
<iframe v-else-if="previewMode === 'iframe'" :src="previewUrl" class="pdf-frame"></iframe>
<div v-else class="pdf-empty">
<van-empty description="暂无可预览附件"></van-empty>
</div>
</div>
<div v-if="voteInfo" class="footer-bar">
@@ -25,13 +29,22 @@ layout("/layouts/platform_h5.html"){
pdfPath: "",
fileName: "",
fileId: "",
materialsId: "",
sysFileId: "",
fileType: "",
previewUrl: "",
previewMode: "empty",
pdfObj: null,
voteInfo: null
}
},
computed: {
previewUrl() {
if (!this.pdfPath) return ""
return this.pdfPath
fileSuffix() {
const name = this.fileName || ""
const url = (this.pdfPath || "").split("?")[0]
const source = name.indexOf(".") > -1 ? name : url
const index = source.lastIndexOf(".")
return index > -1 ? source.substring(index + 1).toLowerCase() : ""
}
},
methods: {
@@ -40,12 +53,101 @@ layout("/layouts/platform_h5.html"){
this.pdfPath = params.get("pdfPath") || ""
this.fileName = params.get("fileName") || ""
this.fileId = params.get("fileId") || ""
this.materialsId = params.get("materialsId") || params.get("materialId") || this.fileId
this.sysFileId = params.get("sysFileId") || ""
this.fileType = params.get("fileType") || ""
},
initPreview() {
if (!this.pdfPath) {
this.previewMode = "empty"
return
}
const suffix = this.resolveSuffix()
if (this.isImage(suffix)) {
this.previewUrl = this.pdfPath
this.previewMode = "image"
return
}
if (suffix === "pdf" || !suffix) {
this.previewUrl = this.pdfPath
this.previewMode = "pdf"
this.renderPdf(this.previewUrl)
return
}
if (this.isOfficeFile(suffix)) {
const fileId = this.resolveFileId()
if (fileId) {
this.previewUrl = "/assets/platform/plugins/pdfJs/web/viewer.html?file=" + encodeURIComponent("/platform/sys/file/convertPDF?id=" + fileId)
this.previewMode = "iframe"
return
}
}
this.previewUrl = this.pdfPath
this.previewMode = "iframe"
},
resolveSuffix() {
if (this.fileSuffix) return this.fileSuffix
const type = String(this.fileType || "").replace(".", "").toLowerCase()
if (["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "jpg", "jpeg", "png", "gif", "bmp", "webp"].indexOf(type) > -1) {
return type
}
if (type.indexOf("pdf") > -1) return "pdf"
if (type.indexOf("word") > -1) return "docx"
if (type.indexOf("excel") > -1 || type.indexOf("sheet") > -1) return "xlsx"
if (type.indexOf("powerpoint") > -1 || type.indexOf("presentation") > -1) return "pptx"
if (type.indexOf("image") > -1) return "jpg"
return ""
},
resolveFileId() {
if (this.sysFileId) return this.sysFileId
if (!this.pdfPath) return ""
try {
const url = new URL(this.pdfPath, window.location.origin)
return url.searchParams.get("id") || ""
} catch (e) {
const match = this.pdfPath.match(/[?&]id=([^&]+)/)
return match ? decodeURIComponent(match[1]) : ""
}
},
isImage(suffix) {
return ["jpg", "jpeg", "png", "gif", "bmp", "webp"].indexOf(suffix) > -1
},
isOfficeFile(suffix) {
return ["doc", "docx", "xls", "xlsx", "ppt", "pptx"].indexOf(suffix) > -1
},
renderPdf(url) {
this.$nextTick(() => {
const container = document.getElementById("congress-pdf-container")
if (!container || !url) return
container.innerHTML = ""
try {
if (this.pdfObj && this.pdfObj.destroy) {
this.pdfObj.destroy()
}
} catch (e) {}
try {
this.pdfObj = new Pdfh5("#congress-pdf-container", {
pdfurl: url,
lazy: true
})
} catch (e) {
this.previewUrl = "/assets/platform/plugins/pdfJs/web/viewer.html?file=" + encodeURIComponent(url)
this.previewMode = "iframe"
}
})
},
previewImage() {
if (!this.previewUrl) return
vant.ImagePreview({
images: [this.previewUrl],
closeable: true
})
},
fetchVoteInfo() {
if (!this.fileId) return
if (!this.materialsId) return
const repId = window.localStorage.getItem("repId") || ""
this.$axios.get("/platform/h5/congress/votingIssue", {
params: {materialsId: this.fileId, repId: repId}
params: {materialsId: this.materialsId, repId: repId}
}).then((res) => {
if (res.code === 0 && res.data) {
this.voteInfo = res.data
@@ -67,6 +169,7 @@ layout("/layouts/platform_h5.html"){
created() {
this.initQuery()
this.fetchVoteInfo()
this.initPreview()
}
})
</script>
@@ -77,22 +180,11 @@ layout("/layouts/platform_h5.html"){
background: #f5f6f8;
}
.pdf-viewer-page .van-nav-bar {
background: #1989fa;
box-shadow: 0 4px 16px rgba(25, 137, 250, 0.15);
}
.pdf-viewer-page .van-nav-bar__title,
.pdf-viewer-page .van-nav-bar .van-icon,
.pdf-viewer-page .van-nav-bar__text {
color: #fff;
font-weight: 600;
}
.pdf-container {
height: calc(100vh - 46px);
background: #f5f6f8;
overflow: hidden;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.pdf-container.has-footer {
@@ -107,10 +199,32 @@ layout("/layouts/platform_h5.html"){
background: #fff;
}
.pdf-render-box {
width: 100%;
min-height: 100%;
background: #f5f6f8;
}
.pdf-render-box .pdfjs {
background: #f5f6f8;
}
.image-preview {
display: block;
width: 100%;
height: auto;
min-height: 100%;
object-fit: contain;
background: #fff;
}
.pdf-empty {
width: 100%;
height: 100%;
background: #f5f6f8;
display: flex;
align-items: center;
justify-content: center;
}
.footer-bar {
@@ -3,6 +3,8 @@ layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar :title="voteInfo.name || '投票'" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<div class="vote-page">
<div class="vote-content">
<div class="vote-header-card">
@@ -14,10 +16,10 @@ layout("/layouts/platform_h5.html"){
<span>{{ sessionName }}</span>
</div>
<div class="meta-divider"></div>
<div class="meta-item">
<van-icon name="clock-o" size="14"></van-icon>
<span>{{ timeName }}</span>
</div>
<!-- <div class="meta-item">-->
<!-- <van-icon name="clock-o" size="14"></van-icon>-->
<!-- <span>{{ timeName }}</span>-->
<!-- </div>-->
</div>
</div>
@@ -199,7 +201,7 @@ layout("/layouts/platform_h5.html"){
<style id="style-congress-vote-h5">
.vote-page {
min-height: 100vh;
min-height: calc(100vh - 46px);
background: #f5f7fa;
display: flex;
flex-direction: column;
@@ -3,6 +3,8 @@ layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar :title="voteInfo.name || '查看投票'" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<div class="vote-page">
<div class="vote-content">
<div class="vote-header-card">
@@ -14,10 +16,10 @@ layout("/layouts/platform_h5.html"){
<span>{{ sessionName }}</span>
</div>
<div class="meta-divider"></div>
<div class="meta-item">
<van-icon name="clock-o" size="14"></van-icon>
<span>{{ timeName }}</span>
</div>
<!-- <div class="meta-item">-->
<!-- <van-icon name="clock-o" size="14"></van-icon>-->
<!-- <span>{{ timeName }}</span>-->
<!-- </div>-->
</div>
</div>
@@ -183,7 +185,7 @@ layout("/layouts/platform_h5.html"){
<style id="style-congress-voted-h5">
.vote-page {
min-height: 100vh;
min-height: calc(100vh - 46px);
background: #f5f7fa;
display: flex;
flex-direction: column;