This commit is contained in:
JyuHsin
2024-12-06 08:47:16 +08:00
parent 035b0a46c7
commit c957e2ddd4
20 changed files with 580 additions and 181 deletions
+1 -2
View File
@@ -84,8 +84,7 @@ public class MainLauncher {
} }
public void init() { public void init() {
Mvcs.X_POWERED_BY = "BudWK-V5 <budwk.com>"; Mvcs.DISABLE_X_POWERED_BY = true;
Globals.AppBase = Mvcs.getServletContext().getContextPath(); Globals.AppBase = Mvcs.getServletContext().getContextPath();
Globals.AppRoot = Mvcs.getServletContext().getRealPath("/"); Globals.AppRoot = Mvcs.getServletContext().getRealPath("/");
//注册自定义标签 //注册自定义标签
@@ -2,6 +2,7 @@ package io.v.nutz.member.controller;
import cn.wizzer.framework.base.Result; import cn.wizzer.framework.base.Result;
import io.v.nutz.annontation.ViReturn; import io.v.nutz.annontation.ViReturn;
import io.v.nutz.base.utils.Vi;
import io.v.nutz.member.UserMode; import io.v.nutz.member.UserMode;
import io.v.nutz.member.model.MemberApplyRecord; import io.v.nutz.member.model.MemberApplyRecord;
import io.v.nutz.member.model.MemberChangeRecord; import io.v.nutz.member.model.MemberChangeRecord;
@@ -9,6 +10,7 @@ import io.v.nutz.member.service.MemberService;
import io.v.nutz.service.ViService; import io.v.nutz.service.ViService;
import io.v.nutz.sys.models.Sys_user; import io.v.nutz.sys.models.Sys_user;
import io.v.nutz.sys.services.SysUserService; import io.v.nutz.sys.services.SysUserService;
import io.v.nutz.web.commons.utils.ShiroUtil;
import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.nutz.dao.Cnd; import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls; import org.nutz.dao.Sqls;
@@ -59,6 +61,9 @@ public class MemberCommonController {
@ViReturn @ViReturn
@Ok("json:{locked:'password|salt',ignoreNull:false}") @Ok("json:{locked:'password|salt',ignoreNull:false}")
public Object userInfo(String userId, String source) { public Object userInfo(String userId, String source) {
if (!ShiroUtil.hasAnyRoles("sysadmin,A06")){
userId= ShiroUtil.getUserId();
}
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
u.*,unit.`name` unitname,un.unionname,unit.unionid u.*,unit.`name` unitname,un.unionname,unit.unionid
@@ -47,7 +47,6 @@ import java.util.List;
@At("/platform/member/group/info") @At("/platform/member/group/info")
@Ok("json:full") @Ok("json:full")
@IocBean @IocBean
@RequiresAuthentication
public class MemberGroupInfoController { public class MemberGroupInfoController {
@At("") @At("")
@Ok("beetl:/platform/member/info/GroupInfo.html") @Ok("beetl:/platform/member/info/GroupInfo.html")
@@ -111,11 +110,13 @@ public class MemberGroupInfoController {
} }
@At @At
@RequiresPermissions("member.group.person")
public Integer getMemberHistoryNum(Integer year) { public Integer getMemberHistoryNum(Integer year) {
return memberHistoryService.count(Cnd.where("year", "=", year)); return memberHistoryService.count(Cnd.where("year", "=", year));
} }
@At @At
@RequiresPermissions("member.group.person")
public Object archive(Integer year) { public Object archive(Integer year) {
try { try {
@@ -139,12 +140,14 @@ public class MemberGroupInfoController {
@At @At
@ViReturn @ViReturn
@RequiresPermissions("member.group.person")
public Object pageData(PageForm pageForm, String unionId, String unitId, String[] personTypes, String[] userStates, String campus, String sex, Integer memberStatus, String position, String jobTitle) { public Object pageData(PageForm pageForm, String unionId, String unitId, String[] personTypes, String[] userStates, String campus, String sex, Integer memberStatus, String position, String jobTitle) {
return memberService.list(pageForm, dataSql(pageForm, unionId, unitId, personTypes, userStates, campus, sex, memberStatus, position, jobTitle)); return memberService.list(pageForm, dataSql(pageForm, unionId, unitId, personTypes, userStates, campus, sex, memberStatus, position, jobTitle));
} }
@At @At
@ViReturn @ViReturn
@RequiresPermissions("member.group.person")
public Object delete(String userId) { public Object delete(String userId) {
UserMode.removeMemberRole(userId); UserMode.removeMemberRole(userId);
return null; return null;
@@ -152,7 +155,7 @@ public class MemberGroupInfoController {
@At @At
@Ok("void") @Ok("void")
@RequiresAuthentication @RequiresPermissions("member.group.person")
public void export(HttpServletResponse response, String props, PageForm pageForm, String unionId, String unitId, String personTypes, String userStates, String campus, String sex, Integer memberStatus, String position, String jobTitle) throws IOException { public void export(HttpServletResponse response, String props, PageForm pageForm, String unionId, String unitId, String personTypes, String userStates, String campus, String sex, Integer memberStatus, String position, String jobTitle) throws IOException {
ViTool.excelResponse(response, "会员名单.xls"); ViTool.excelResponse(response, "会员名单.xls");
@@ -45,7 +45,6 @@ import java.util.Map;
@At("/platform/member/inquire/integrate") @At("/platform/member/inquire/integrate")
@Ok("json:full") @Ok("json:full")
@IocBean @IocBean
@RequiresAuthentication
public class MemberInquireIntegrateController { public class MemberInquireIntegrateController {
@At("") @At("")
@@ -65,6 +64,7 @@ public class MemberInquireIntegrateController {
@At @At
@ViReturn @ViReturn
@RequiresPermissions("member.inquire.Integrate")
public Object pageData(PageForm pageForm, Integer year, String unionId, String unitId, String[] personTypes, String[] userStates, String[] memberStatus, String campus) { public Object pageData(PageForm pageForm, Integer year, String unionId, String unitId, String[] personTypes, String[] userStates, String[] memberStatus, String campus) {
int yyyy = Calendar.getInstance().get(Calendar.YEAR); int yyyy = Calendar.getInstance().get(Calendar.YEAR);
@@ -328,6 +328,7 @@ public class MemberInquireIntegrateController {
@At @At
@Ok("void") @Ok("void")
@RequiresPermissions("member.inquire.Integrate")
public void doExport(HttpServletResponse response, Integer year, String unionId, String unitId, String memberStatus, String personTypes, String userStates) { public void doExport(HttpServletResponse response, Integer year, String unionId, String unitId, String memberStatus, String personTypes, String userStates) {
try { try {
int yyyy = Calendar.getInstance().get(Calendar.YEAR); int yyyy = Calendar.getInstance().get(Calendar.YEAR);
@@ -385,6 +386,7 @@ public class MemberInquireIntegrateController {
@At @At
@Ok("void") @Ok("void")
@RequiresPermissions("member.inquire.Integrate")
public void doExportByUnion(HttpServletResponse response, Integer year, String unionId, String unitId, String memberStatus, String personTypes, String userStates, String props) { public void doExportByUnion(HttpServletResponse response, Integer year, String unionId, String unitId, String memberStatus, String personTypes, String userStates, String props) {
try { try {
int yyyy = Calendar.getInstance().get(Calendar.YEAR); int yyyy = Calendar.getInstance().get(Calendar.YEAR);
@@ -465,5 +467,4 @@ public class MemberInquireIntegrateController {
} }
} }
} }
@@ -1,5 +1,7 @@
package io.v.nutz.question.controller; package io.v.nutz.question.controller;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
@@ -31,6 +33,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* 问卷H5 * 问卷H5
@@ -286,6 +289,12 @@ public class QuestionH5Controller {
//判断是否将题目加进去了 //判断是否将题目加进去了
Sql issueSql = Sqls.create(" SELECT iid FROM `question_issue` $condition"); Sql issueSql = Sqls.create(" SELECT iid FROM `question_issue` $condition");
issueSql = Sqls.create("""
SELECT iid FROM (
SELECT * FROM `question_issue` $condition
) AS subquery ORDER BY ipxbh ASC;
""");
Cnd commonIssueCnd = Cnd.NEW(); Cnd commonIssueCnd = Cnd.NEW();
commonIssueCnd.and("iqid", "=", id); commonIssueCnd.and("iqid", "=", id);
@@ -305,12 +314,17 @@ public class QuestionH5Controller {
QuestionRandomIssue maxQuestionRandomIssue = randomIssues.stream().filter(randomIssue -> randomIssue.getNum() == finalMaxAnswer).findFirst().orElse(null); QuestionRandomIssue maxQuestionRandomIssue = randomIssues.stream().filter(randomIssue -> randomIssue.getNum() == finalMaxAnswer).findFirst().orElse(null);
if (randomIssues.size() < ranDomSum && Lang.isNotEmpty(maxQuestionRandomIssue) && maxQuestionRandomIssue.getIsFinish()) { if (randomIssues.size() < ranDomSum && Lang.isNotEmpty(maxQuestionRandomIssue) && maxQuestionRandomIssue.getIsFinish()) {
List<String> allIssueIds = randomIssues.stream().flatMap(randomIssue -> randomIssue.getIssueIds().stream()).collect(Collectors.toList()); List<String> allIssueIds = randomIssues.stream().flatMap(randomIssue -> randomIssue.getIssueIds().stream()).collect(Collectors.toList());
commonIssueCnd.andEX("iid", "not in", allIssueIds); //每次都从20题里面去找
// commonIssueCnd.andEX("iid", "not in", allIssueIds);
}else{ }else{
needFor = false; needFor = false;
} }
} }
//固定3题
List<String> fixedIssueIds = new java.util.ArrayList<>(List.of("52e2a5c9d54e4a3ab3e60443b1c961aa", "140388aa1a10445bbcd9e6356c28ef2b", "5f04a8e4f43d46378f012a573001bea9","c81599b8bf5e424bba619de39c5860ca","f701af5766244d788513439b9379f3f0"));
commonIssueCnd.and("iid","not in",fixedIssueIds);
if (ObjectUtil.isAllNotEmpty(ranDomSum, ranDomSingleIssueNum)) { if (ObjectUtil.isAllNotEmpty(ranDomSum, ranDomSingleIssueNum)) {
commonIssueCnd.and(new Static("1=1 order by rand() limit " + ranDomSingleIssueNum)); commonIssueCnd.and(new Static("1=1 order by rand() limit " + ranDomSingleIssueNum));
} }
@@ -320,48 +334,31 @@ public class QuestionH5Controller {
issueSql.setCallback(Sqls.callback.strList()); issueSql.setCallback(Sqls.callback.strList());
dao.execute(issueSql); dao.execute(issueSql);
List<String> randomIssueIds = (List<String>) issueSql.getResult(); List<String> randomIssueIds = (List<String>) issueSql.getResult();
Collections.shuffle(randomIssueIds); // Collections.shuffle(randomIssueIds);
List<String> allIssueIds = Stream.concat(fixedIssueIds.stream(), randomIssueIds.stream()).collect(Collectors.toList());
QuestionRandomIssue questionRandomIssue = new QuestionRandomIssue(); QuestionRandomIssue questionRandomIssue = new QuestionRandomIssue();
questionRandomIssue.setQuestionId(id); questionRandomIssue.setQuestionId(id);
questionRandomIssue.setUserId(ShiroUtil.getUserId()); questionRandomIssue.setUserId(ShiroUtil.getUserId());
questionRandomIssue.setIssueIds(randomIssueIds); questionRandomIssue.setIssueIds(allIssueIds);
questionRandomIssue.setNum(maxAnswer + 1); questionRandomIssue.setNum(maxAnswer + 1);
questionRandomIssue.setIsFinish(false); questionRandomIssue.setIsFinish(false);
dao.insert(questionRandomIssue); if(maxAnswer==0){
issueCnd.and("iid", "in", randomIssueIds); dao.insert(questionRandomIssue);
}
issueCnd.and("iid", "in", allIssueIds);
}else{ }else{
// List<String> allIssueIds = randomIssues.stream().flatMap(randomIssue -> randomIssue.getIssueIds().stream()).collect(Collectors.toList()); // List<String> allIssueIds = randomIssues.stream().flatMap(randomIssue -> randomIssue.getIssueIds().stream()).collect(Collectors.toList());
int finalMaxAnswer1 = maxAnswer; int finalMaxAnswer1 = maxAnswer;
List<String> allIssueIds = randomIssues.stream().filter(randomIssue -> randomIssue.getNum() == finalMaxAnswer1).flatMap(randomIssue -> randomIssue.getIssueIds().stream()).collect(Collectors.toList()); List<String> allIssueIds = randomIssues.stream().filter(randomIssue -> randomIssue.getNum() == finalMaxAnswer1).flatMap(randomIssue -> randomIssue.getIssueIds().stream()).collect(Collectors.toList());
issueCnd.and("iid", "in", allIssueIds); issueCnd.and("iid", "in", allIssueIds);
} }
// if (Lang.isEmpty(randomIssue)) {
//
// Sql issueSql = Sqls.create(" SELECT iid FROM `question_issue` where iqid = @qid $randLimit");
// issueSql.setParam("qid", id);
// if (ranDomSingleIssueNum != null && ranDomSingleIssueNum > 0) {
// issueSql.setVar("randLimit", " order by rand() limit " + ranDomSingleIssueNum);
// }
//
// issueSql.setCallback(Sqls.callback.strList());
// dao.execute(issueSql);
// List<String> randomIssueIds = (List<String>) issueSql.getResult();
// Collections.shuffle(randomIssueIds);
// QuestionRandomIssue questionRandomIssue = new QuestionRandomIssue();
// questionRandomIssue.setQuestionId(id);
// questionRandomIssue.setUserId(ShiroUtil.getUserId());
// questionRandomIssue.setIssueIds(randomIssueIds);
// questionRandomIssue.setNum(null);
// questionRandomIssue.setIsFinish(false);
// dao.insert(questionRandomIssue);
// issueCnd.and("iid", "in", randomIssueIds);
// } else {
// issueCnd.and("iid", "in", randomIssue.getIssueIds());
// }
} }
// issueCnd.asc("ipxbh"); // issueCnd.and(new Static("1=1 ORDER BY FIELD(iid,'52e2a5c9d54e4a3ab3e60443b1c961aa', '140388aa1a10445bbcd9e6356c28ef2b', '5f04a8e4f43d46378f012a573001bea9', 'c81599b8bf5e424bba619de39c5860ca', 'f701af5766244d788513439b9379f3f0') DESC"));
issueCnd.desc("ipxbh");
questionService.fetchLinks(question, "issues", issueCnd); questionService.fetchLinks(question, "issues", issueCnd);
for (Question_issue issue : question.getIssues()) { for (Question_issue issue : question.getIssues()) {
questionService.fetchLinks(issue, "wors", Cnd.NEW().asc("wpxbh")); questionService.fetchLinks(issue, "wors", Cnd.NEW().asc("wpxbh"));
@@ -435,6 +432,80 @@ public class QuestionH5Controller {
return Result.success(); return Result.success();
} }
@At
public Result answerAgainCommon(String id){
Question question = questionService.fetch(id);
Cnd issueCnd = Cnd.NEW();
Sql issueSql = Sqls.create(" SELECT iid FROM `question_issue` $condition");
issueSql = Sqls.create("""
SELECT * FROM (
SELECT * FROM `question_issue` $condition
) AS subquery ORDER BY ipxbh ASC;
""");
Cnd commonIssueCnd = Cnd.NEW();
commonIssueCnd.and("iqid", "=", id);
//判断是否要随机
Integer ranDomSum = question.getRanDomSum();
Integer ranDomSingleIssueNum = question.getRanDomSingleIssueNum();
//已经回答最大次数
int maxAnswer = 0;
boolean needFor = true;
List<QuestionRandomIssue> randomIssues = dao.query(QuestionRandomIssue.class, Cnd.where("questionId", "=", question.getQid()).and("userId", "=", ShiroUtil.getUserId()));
if (Lang.isNotEmpty(randomIssues)) {
maxAnswer = randomIssues.stream().mapToInt(QuestionRandomIssue::getNum).max().orElse(0);
int finalMaxAnswer = maxAnswer;
QuestionRandomIssue maxQuestionRandomIssue = randomIssues.stream().filter(randomIssue -> randomIssue.getNum() == finalMaxAnswer).findFirst().orElse(null);
if (randomIssues.size() < ranDomSum && Lang.isNotEmpty(maxQuestionRandomIssue) && maxQuestionRandomIssue.getIsFinish()) {
List<String> allIssueIds = randomIssues.stream().flatMap(randomIssue -> randomIssue.getIssueIds().stream()).collect(Collectors.toList());
//每次都从20题里面去找
// commonIssueCnd.andEX("iid", "not in", allIssueIds);
}else{
needFor = false;
}
}
if (ObjectUtil.isAllNotEmpty(ranDomSum, ranDomSingleIssueNum)) {
commonIssueCnd.and(new Static("1=1 order by rand() limit " + ranDomSingleIssueNum));
}
if(needFor){
issueSql.setCondition(commonIssueCnd);
issueSql.setCallback(Sqls.callback.strList());
dao.execute(issueSql);
List<String> randomIssueIds = (List<String>) issueSql.getResult();
Collections.shuffle(randomIssueIds);
QuestionRandomIssue questionRandomIssue = new QuestionRandomIssue();
questionRandomIssue.setQuestionId(id);
questionRandomIssue.setUserId(ShiroUtil.getUserId());
questionRandomIssue.setIssueIds(randomIssueIds);
questionRandomIssue.setNum(maxAnswer + 1);
questionRandomIssue.setIsFinish(false);
dao.insert(questionRandomIssue);
issueCnd.and("iid", "in", randomIssueIds);
}else{
// List<String> allIssueIds = randomIssues.stream().flatMap(randomIssue -> randomIssue.getIssueIds().stream()).collect(Collectors.toList());
int finalMaxAnswer1 = maxAnswer;
List<String> allIssueIds = randomIssues.stream().filter(randomIssue -> randomIssue.getNum() == finalMaxAnswer1).flatMap(randomIssue -> randomIssue.getIssueIds().stream()).collect(Collectors.toList());
issueCnd.and("iid", "in", allIssueIds);
}
questionService.fetchLinks(question, "issues", issueCnd);
for (Question_issue issue : question.getIssues()) {
questionService.fetchLinks(issue, "wors", Cnd.NEW().asc("wpxbh"));
}
if (question.getDisruptingOrder()) {
Collections.shuffle(question.getIssues());
}
return Result.success(question);
}
@At("/winInfo") @At("/winInfo")
public Object winInfo(String questionId) { public Object winInfo(String questionId) {
// List<QuestionWinner> winners = dao.query(QuestionWinner.class, Cnd.where("questionId", "=", questionId)); // List<QuestionWinner> winners = dao.query(QuestionWinner.class, Cnd.where("questionId", "=", questionId));
@@ -581,12 +652,26 @@ public class QuestionH5Controller {
} else if (question.getCtMode() == 3) { } else if (question.getCtMode() == 3) {
QuestionUserScore userScore = dao.fetch(QuestionUserScore.class, Cnd.where("questionId", "=", questionId) QuestionUserScore userScore = dao.fetch(QuestionUserScore.class, Cnd.where("questionId", "=", questionId)
.and("userId", "=", ShiroUtil.getUserId()) .and("userId", "=", ShiroUtil.getUserId())
.desc(question.getCalcScoreMode() == 1 ? "score" : "answerNum")); // .desc(question.getCalcScoreMode() == 1 ? "score" : "answerNum"));
.desc("answerNum"));
return Result.success().addData(Optional.ofNullable(userScore).map(v -> v.getScore()).orElse(0)); return Result.success().addData(Optional.ofNullable(userScore).map(v -> v.getScore()).orElse(0));
} }
return Result.success(); return Result.success();
} }
/**
* 仅限常规模式
* @param questionId
* @return
*/
@At
@RequiresAuthentication
public Result historyScore(String questionId){
List<QuestionUserScore> scores = dao.query(QuestionUserScore.class, Cnd.where("questionId", "=", questionId)
.and("userId","=",ShiroUtil.getUserId()).asc("answerNum"));
return Result.success(scores);
}
/** /**
* 获取用户已回答的轮数 针对单天随机 * 获取用户已回答的轮数 针对单天随机
* *
@@ -97,12 +97,44 @@ public class TrainEduStatisticsVideoController {
LEFT JOIN `user` u ON u.id = tevwr.userId LEFT JOIN `user` u ON u.id = tevwr.userId
$condition $condition
"""); """);
sql = Sqls.create("""
SELECT
u.username,
u.loginname,
u.unitname,
u.unionname,
tev.qualifiedNum,
SUM(CASE
WHEN sub.cnt > tev.everyDayMaxWatchNum THEN tev.everyDayMaxWatchNum
ELSE sub.cnt
END) AS watchCount
FROM
`train_edu_video_watch_record` tevwr
LEFT JOIN train_edu_video tev ON tev.id = tevwr.videoId
LEFT JOIN train_edu_album tea ON tea.id = tevwr.albumId
LEFT JOIN `user` u ON u.id = tevwr.userId
LEFT JOIN (
SELECT
userId,
albumId,
watchTime,
COUNT(*) AS cnt
FROM
`train_edu_video_watch_record`
WHERE
albumId = @albumId
GROUP BY
userId,
watchTime
) AS sub ON tevwr.userId = sub.userId AND tevwr.albumId = sub.albumId AND tevwr.watchTime = sub.watchTime
$condition
""").setParam("albumId", albumId);
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.and("tevwr.albumId", "=", albumId); cnd.and("tevwr.albumId", "=", albumId);
cnd.and(new Static("IF(tea.createType = 1, u.unionId = tea.unionId , 1=1)")); cnd.and(new Static("IF(tea.createType = 1, u.unionId = tea.unionId , 1=1)"));
cnd.and(new Static("tevwr.watchTime >= tev.startDate")); cnd.and(new Static("tevwr.watchTime >= tev.startDate"));
cnd.and(new Static("tevwr.watchTime <= tev.endDate")); cnd.and(new Static("tevwr.watchTime <= tev.endDate"));
cnd.groupBy("tevwr.userId"); cnd.groupBy("u.username,u.loginname,u.unitname,u.unionname,tev.qualifiedNum");
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 pagination; return pagination;
@@ -152,57 +184,70 @@ public class TrainEduStatisticsVideoController {
@At @At
@Ok("void") @Ok("void")
@RequiresPermissions("trainEdu.statistics.video") @RequiresPermissions("trainEdu.statistics.video")
public void exportXlsx(String albumId,HttpServletResponse response){ public void exportXlsx(String albumId, HttpServletResponse response) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
u.username, u.username,
u.loginname, u.loginname,
u.unitname, u.unitname,
u.unionname, u.unionname,
tev.qualifiedNum, tev.qualifiedNum,
( SUM(CASE
SELECT WHEN sub.cnt > tev.everyDayMaxWatchNum THEN tev.everyDayMaxWatchNum
SUM(IF( cnt > tev.everyDayMaxWatchNum, tev.everyDayMaxWatchNum, cnt )) AS count ELSE sub.cnt
FROM END) AS watchCount
( SELECT watchTime, COUNT( 1 ) AS cnt FROM train_edu_video_watch_record where userId = tevwr.userId and albumId = tevwr.albumId GROUP BY watchTime ) AS subquery
) as watchCount
FROM FROM
`train_edu_video_watch_record` tevwr `train_edu_video_watch_record` tevwr
LEFT JOIN train_edu_video tev ON tev.id = tevwr.videoId LEFT JOIN train_edu_video tev ON tev.id = tevwr.videoId
LEFT JOIN train_edu_album tea on tea.id = tevwr.albumId LEFT JOIN train_edu_album tea ON tea.id = tevwr.albumId
LEFT JOIN `user` u ON u.id = tevwr.userId LEFT JOIN `user` u ON u.id = tevwr.userId
LEFT JOIN (
SELECT
userId,
albumId,
watchTime,
COUNT(*) AS cnt
FROM
`train_edu_video_watch_record`
WHERE
albumId = @albumId
GROUP BY
userId,
watchTime
) AS sub ON tevwr.userId = sub.userId AND tevwr.albumId = sub.albumId AND tevwr.watchTime = sub.watchTime
$condition $condition
"""); """).setParam("albumId", albumId);
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.and("tevwr.albumId", "=", albumId); cnd.and("tevwr.albumId", "=", albumId);
cnd.and(new Static("IF(tea.createType = 1, u.unionId = tea.unionId , 1=1)")); cnd.and(new Static("IF(tea.createType = 1, u.unionId = tea.unionId , 1=1)"));
cnd.and(new Static("tevwr.watchTime >= tev.startDate")); cnd.and(new Static("tevwr.watchTime >= tev.startDate"));
cnd.and(new Static("tevwr.watchTime <= tev.endDate")); cnd.and(new Static("tevwr.watchTime <= tev.endDate"));
cnd.groupBy("tevwr.userId"); cnd.groupBy("u.username,u.loginname,u.unitname,u.unionname,tev.qualifiedNum");
sql.setCondition(cnd); sql.setCondition(cnd);
List<NutMap> list = baseService.listMap(sql); List<NutMap> list = baseService.listMap(sql);
List<ExcelExportEntity> excelExportEntities = new ArrayList<>(); List<ExcelExportEntity> excelExportEntities = new ArrayList<>();
excelExportEntities.add(new ExcelExportEntity("序号","no",10)); excelExportEntities.add(new ExcelExportEntity("序号", "no", 10));
excelExportEntities.add(new ExcelExportEntity("姓名","username",20)); excelExportEntities.add(new ExcelExportEntity("姓名", "username", 20));
excelExportEntities.add(new ExcelExportEntity("工号","loginname",20)); excelExportEntities.add(new ExcelExportEntity("工号", "loginname", 20));
excelExportEntities.add(new ExcelExportEntity("单位","unitname",20)); excelExportEntities.add(new ExcelExportEntity("单位", "unitname", 20));
excelExportEntities.add(new ExcelExportEntity("分工会","unionname",20)); excelExportEntities.add(new ExcelExportEntity("分工会", "unionname", 20));
excelExportEntities.add(new ExcelExportEntity("学习总次数","watchCount",20)); excelExportEntities.add(new ExcelExportEntity("学习总次数", "watchCount", 20));
excelExportEntities.add(new ExcelExportEntity("是否达标","qualifiedNum",20)); excelExportEntities.add(new ExcelExportEntity("是否达标", "qualified", 20));
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
list.get(i).put("no",i+1); list.get(i).put("no", i + 1);
list.get(i).put("qualifiedNum",list.get(i).getInt("qualifiedNum",0)==1?"是":"否"); int watchCount = list.get(i).getInt("watchCount", 0);
int qualifiedNum = list.get(i).getInt("qualifiedNum", 0);
list.get(i).put("qualified", watchCount >= qualifiedNum ? "是" : "否");
} }
try{ try {
ViTool.excelResponse(response, "获奖人员名单.xls"); ViTool.excelResponse(response, "获奖人员名单.xls");
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), excelExportEntities, list); Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), excelExportEntities, list);
workbook.write(response.getOutputStream()); workbook.write(response.getOutputStream());
}catch (Exception e){ } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@@ -33,27 +33,29 @@ public class GlobalsSettingProcessor extends AbstractProcessor {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public void process(ActionContext ac) throws Throwable { public void process(ActionContext ac) throws Throwable {
ac.getRequest().setAttribute("AppRoot", Globals.AppRoot); ac.getRequest().setAttribute("AppRoot", Globals.AppRoot);
ac.getRequest().setAttribute("AppBase", Globals.AppBase); ac.getRequest().setAttribute("AppBase", Globals.AppBase);
ac.getRequest().setAttribute("AppName", Globals.AppName); ac.getRequest().setAttribute("AppName", Globals.AppName);
ac.getRequest().setAttribute("AppDomain", Globals.AppDomain); ac.getRequest().setAttribute("AppDomain", Globals.AppDomain);
ac.getRequest().setAttribute("AppShrotName", Globals.AppShrotName); ac.getRequest().setAttribute("AppShrotName", Globals.AppShrotName);
ac.getRequest().setAttribute("AppFileDomain", Globals.AppFileDomain); ac.getRequest().setAttribute("AppFileDomain", Globals.AppFileDomain);
ac.getRequest().setAttribute("config", Globals.MyConfig); ac.getRequest().setAttribute("config", Globals.MyConfig);
ac.getRequest().setAttribute("shiro", ShiroUtil); ac.getRequest().setAttribute("shiro", ShiroUtil);
ac.getRequest().setAttribute("date", dateUtil); ac.getRequest().setAttribute("date", dateUtil);
ac.getRequest().setAttribute("string", stringUtil); ac.getRequest().setAttribute("string", stringUtil);
ac.getRequest().setAttribute("BrandActivityName", Globals.BrandActivityName); ac.getRequest().setAttribute("BrandActivityName", Globals.BrandActivityName);
// 如果url中有语言属性则设置 // 如果url中有语言属性则设置
String lang = ac.getRequest().getParameter("lang"); String lang = ac.getRequest().getParameter("lang");
if (!Strings.isEmpty(lang)) { if (!Strings.isEmpty(lang)) {
Mvcs.setLocalizationKey(lang); Mvcs.setLocalizationKey(lang);
} else { } else {
// Mvcs.getLocalizationKey() 1.r.56 版本是null,所以要做两次判断, 1.r.57已修复为默认值 Nutz:Fix issue 1072 // Mvcs.getLocalizationKey() 1.r.56 版本是null,所以要做两次判断, 1.r.57已修复为默认值 Nutz:Fix issue 1072
lang = Strings.isBlank(Mvcs.getLocalizationKey()) ? Mvcs.getDefaultLocalizationKey() : Mvcs.getLocalizationKey(); lang = Strings.isBlank(Mvcs.getLocalizationKey()) ? Mvcs.getDefaultLocalizationKey() : Mvcs.getLocalizationKey();
} }
ac.getRequest().setAttribute("lang", lang); ac.getRequest().setAttribute("lang", lang);
doNext(ac); doNext(ac);
} }
} }
@@ -1,6 +1,7 @@
package io.v.nutz.web.commons.proc; package io.v.nutz.web.commons.proc;
import io.v.nutz.base.result.Result; import io.v.nutz.base.result.Result;
import org.eclipse.jetty.http.BadMessageException;
import org.nutz.integration.shiro.NutShiro; import org.nutz.integration.shiro.NutShiro;
import org.nutz.ioc.IocException; import org.nutz.ioc.IocException;
import org.nutz.log.Log; import org.nutz.log.Log;
@@ -35,6 +36,12 @@ public class WkFailProcessor extends ViewProcessor {
new ForwardView(errorUri).render(ac.getRequest(), ac.getResponse(), Mvcs.getMessage(ac.getRequest(), "system.exception")); new ForwardView(errorUri).render(ac.getRequest(), ac.getResponse(), Mvcs.getMessage(ac.getRequest(), "system.exception"));
} }
} }
if(ac.getError() instanceof BadMessageException){
NutShiro.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error("表单参数错误"));
}
if(ac.getError() instanceof RuntimeException){
NutShiro.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error("程序异常"));
}
super.process(ac); super.process(ac);
} }
} }
@@ -1,6 +1,8 @@
package io.v.nutz.web.commons.proc; package io.v.nutz.web.commons.proc;
import cn.hutool.http.HtmlUtil; import cn.hutool.http.HtmlUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.v.nutz.base.result.Result; import io.v.nutz.base.result.Result;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.nutz.integration.shiro.NutShiro; import org.nutz.integration.shiro.NutShiro;
@@ -16,9 +18,11 @@ import org.nutz.mvc.impl.processor.AbstractProcessor;
import org.nutz.mvc.view.ForwardView; import org.nutz.mvc.view.ForwardView;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.Arrays; import java.io.UnsupportedEncodingException;
import java.util.Iterator; import java.net.URLDecoder;
import java.util.List; import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Pattern;
/** /**
* SQL XSS拦截 * SQL XSS拦截
@@ -37,6 +41,13 @@ public class XssSqlFilterProcessor extends AbstractProcessor {
private final static String regxpForImaTagSrcAttrib = "src=\"([^\"]+)\""; // 找出IMG标签的SRC属性 private final static String regxpForImaTagSrcAttrib = "src=\"([^\"]+)\""; // 找出IMG标签的SRC属性
//合法字段
private static final Set<String> ALLOWED_ORDER_FIELDS = new HashSet<>(Arrays.asList(
"createdat", "updatedat"
));
private static final ObjectMapper objectMapper = new ObjectMapper();
@Override @Override
public void init(NutConfig config, ActionInfo ai) throws Throwable { public void init(NutConfig config, ActionInfo ai) throws Throwable {
try { try {
@@ -66,64 +77,57 @@ public class XssSqlFilterProcessor extends AbstractProcessor {
protected boolean checkParams(ActionContext ac) { protected boolean checkParams(ActionContext ac) {
HttpServletRequest req = ac.getRequest(); HttpServletRequest req = ac.getRequest();
/* String pageOrderName = req.getParameter("pageOrderName");
if (pageOrderName != null && ALLOWED_ORDER_FIELDS.contains(pageOrderName.toLowerCase())) {
// 如果是预定义的排序字段,直接放行
return false;
}*/
Iterator<String[]> values = req.getParameterMap().values().iterator();// 获取所有的表单参数 Iterator<String[]> values = req.getParameterMap().values().iterator();// 获取所有的表单参数
Iterator<String[]> values2 = req.getParameterMap().values().iterator();// 因为是游标所以要重新获取 Iterator<String[]> values2 = req.getParameterMap().values().iterator();// 因为是游标所以要重新获取
boolean isError = false; boolean isError = false;
//use remove -|, // XSS过滤
String regEx_sql = """ String regEx_xss = "script|iframe";
select|update|and|or|delete|insert|trancate|char|chr|into|substr|ascii|declare|exec|count|master|drop|execute
""";
String regEx_xss = "script|iframe|img";
//SQL过滤 //SQL过滤
while (values.hasNext()) { while (values.hasNext()) {
String[] valueArray = (String[]) values.next(); String[] valueArray = (String[]) values.next();
for (int i = 0; i < valueArray.length; i++) { for (int i = 0; i < valueArray.length; i++) {
String value = valueArray[i].toLowerCase(); // 不转换为小写,保持原始值进行检查
String value = valueArray[i];
//排除掉base64 // URL解码
if (value.startsWith("data:image/png;base64,")) { try {
value = URLDecoder.decode(value, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
log.error("URL解码失败", e);
isError = true;
break; break;
} }
//分拆关键字
String[] inj_stra = StringUtils.split(regEx_sql, "\\|");
for (int j = 0; j < inj_stra.length; j++) { // 检查是否是JSON格式
// 判断如果路径参数值中含有关键字则返回true,并且结束循环 if (isValidJson(value)) {
// if (value.contains(inj_stra[j] + " ") || value.contains(" " + inj_stra[j] + " ") || value.contains(" " + inj_stra[j])) { // 对JSON内容进行安全检查
// isError = true; if (checkJsonContent(value)) {
// log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "SQL关键字过滤:" + value); isError = true;
// break; log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "JSON内容包含危险字符:" + value);
// } break;
if ("and".equals(inj_stra[j]) || "or".equals(inj_stra[j]) || "into".equals(inj_stra[j])) {
if (value.contains(" " + inj_stra[j] + " ")) {
isError = true;
log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "SQL关键字过滤:" + value);
break;
}
} else {
if (value.contains(" " + inj_stra[j] + " ")
|| value.contains(inj_stra[j] + " ")
|| value.contains(inj_stra[j] + "/**/")
|| value.contains(inj_stra[j] + "/*")
|| value.contains("/**/" + inj_stra[j] + "/**/")
|| value.contains(inj_stra[j] + "*/")
) {
isError = true;
log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "SQL关键字过滤:" + value);
break;
}
} }
// JSON格式验证通过,继续处理下一个参数
continue;
} }
if (isError) {
// 非JSON格式,进行普通的SQL注入检查
if (containsSqlInjection(value, false)) {
isError = true;
log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "SQL注入风险:" + value);
break; break;
} }
} }
if (isError) { if (isError) break;
break;
}
} }
if (!isError) { if (!isError) {
// XSS漏洞过滤 // XSS漏洞过滤
@@ -131,14 +135,6 @@ public class XssSqlFilterProcessor extends AbstractProcessor {
String[] valueArray = (String[]) values2.next(); String[] valueArray = (String[]) values2.next();
for (int i = 0; i < valueArray.length; i++) { for (int i = 0; i < valueArray.length; i++) {
String value = valueArray[i].toLowerCase(); String value = valueArray[i].toLowerCase();
value = HtmlUtil.escape(value);
if (value.trim().startsWith("<") || value.trim().endsWith(">") ||
value.trim().endsWith("/")) {
log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "XSS关键字过滤(已被我转义):" + value);
}
// 分拆关键字 // 分拆关键字
String[] inj_stra = StringUtils.split(regEx_xss, "|"); String[] inj_stra = StringUtils.split(regEx_xss, "|");
for (int j = 0; j < inj_stra.length; j++) { for (int j = 0; j < inj_stra.length; j++) {
@@ -162,4 +158,197 @@ public class XssSqlFilterProcessor extends AbstractProcessor {
} }
return isError; return isError;
} }
/**
* 验证是否为有效的JSON
*/
private boolean isValidJson(String value) {
try {
objectMapper.readTree(value);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 检查JSON内容是否包含危险字符
*/
private boolean checkJsonContent(String jsonStr) {
try {
JsonNode rootNode = objectMapper.readTree(jsonStr);
return checkJsonNode(rootNode, true);
} catch (Exception e) {
log.error("JSON内容检查失败", e);
return true; // 解析失败当作危险处理
}
}
/**
* 递归检查JSON节点
*/
private boolean checkJsonNode(JsonNode node, boolean isJsonContext) {
if (node.isObject()) {
Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
// JSON对象的键名不需要进行特殊字符检查,因为它们是受控的标识符
// 递归检查值
if (checkJsonNode(entry.getValue(), true)) {
return true;
}
}
} else if (node.isArray()) {
for (JsonNode element : node) {
if (checkJsonNode(element, true)) {
return true;
}
}
} else if (node.isTextual()) {
String value = node.asText();
// 在JSON上下文中使用更宽松的规则
return containsSqlInjection(value, true);
}
return false;
}
/**
* 检查是否包含SQL注入风险
*
* @param value 要检查的值
* @param isJsonContext 是否在JSON上下文中
*/
private boolean containsSqlInjection(String value, boolean isJsonContext) {
if (StringUtils.isBlank(value)) {
return false;
}
// 规范化输入:移除多余的空白字符
value = value.replaceAll("\\s+", " ").trim();
// 检查编码后的特殊字符
String decodedValue = value.replaceAll("%20", " ")
.replaceAll("%2B", "+")
.replaceAll("%2F", "/")
.replaceAll("%3D", "=");
// 检查注释符号和其他危险字符
if (decodedValue.contains("--") ||
decodedValue.contains("/*") ||
decodedValue.contains("*/") ||
decodedValue.contains("#") ||
Pattern.compile("[;'\"\\\\]").matcher(decodedValue).find()) {
return true;
}
// 如果是UUID格式,直接放行
if (value.matches("[a-fA-F0-9]{32}") || value.matches("[a-fA-F0-9-]{36}")) {
return false;
}
// 如果包含HTML标签,使用更宽松的检查规则
if (value.contains("<") && value.contains(">")) {
return checkHtmlContent(value);
}
// 添加对常见标识符的检查
if (isCommonIdentifier(value) || ALLOWED_ORDER_FIELDS.contains(value.toLowerCase())) {
return false;
}
// 检查注释符号
if (value.contains("--") || value.contains("/*") || value.contains("*/")) {
return true;
}
// 检查危险的特殊字符(针对非JSON数据)
if (!isValidJson(value) && containsDangerousChars(value)) {
return true;
}
// 检查SQL关键字,但要避免误判
String valueLower = value.toLowerCase();
for (String keyword : SQL_KEYWORDS) {
// 在JSON上下文中,只有当关键字作为独立单词出现时才判定为危险
if (isJsonContext) {
if (isStandaloneWord(valueLower, keyword)) {
return true;
}
} else if (valueLower.contains(keyword)) {
return true;
}
}
return false;
}
/**
* 判断是否为常见的UUID或标识符格式
*/
private boolean isCommonIdentifier(String value) {
// UUID格式
if (value.matches("[a-fA-F0-9]{32}") || value.matches("[a-fA-F0-9-]{36}")) {
return true;
}
// 常见的标识符格式(字母、数字、下划线、中划线)
if (value.matches("^[a-zA-Z0-9_-]+$")) {
return true;
}
return false;
}
/**
* 检查HTML内容是否包含危险脚本
*/
private boolean checkHtmlContent(String html) {
String lowerHtml = html.toLowerCase();
// 只检查真正危险的脚本标签
return lowerHtml.contains("<script") ||
lowerHtml.contains("javascript:") ||
lowerHtml.contains("onerror=") ||
lowerHtml.contains("onclick=");
}
/**
* 检查是否包含危险的特殊字符
*/
private boolean containsDangerousChars(String value) {
// 定义真正危险的字符(减少误判)
String[] dangerousChars = {
"'", // 单引号
";", // 分号
"\\", // 反斜杠
"@@", // 系统变量前缀
"0x", // 十六进制前缀
"/*", // 注释开始
"*/" // 注释结束
};
for (String dangerous : dangerousChars) {
if (value.contains(dangerous)) {
return true;
}
}
return false;
}
private static final Set<String> SQL_KEYWORDS = new HashSet<>(Arrays.asList(
"select", "update", "delete", "insert", "truncate",
"drop", "execute", "exec", "declare", "union",
"create", "alter", "database", "table", "varchar",
"extractvalue", "concat", "benchmark", "sleep", "delay",
"waitfor", "pg_sleep", "information_schema", "sysobjects",
"load_file", "outfile", "dumpfile"
));
/**
* 检查关键字是否作为独立单词出现
*/
private boolean isStandaloneWord(String text, String keyword) {
String wordBoundary = "\\b" + keyword + "\\b";
return Pattern.compile(wordBoundary).matcher(text).find();
}
} }
@@ -32,7 +32,7 @@
} }
.van-tabs__line { .van-tabs__line {
background-color: #1867b0; background-color: #1867b0!important;
} }
.font-theme-color { .font-theme-color {
@@ -273,6 +273,18 @@ var sublime = function () {
}, },
//2022/02/14 zxy pjax跳转自定义页面 //2022/02/14 zxy pjax跳转自定义页面
jumpPagePjax(pageUrl) { jumpPagePjax(pageUrl) {
// const url = new URL(location.protocol + "//" + location.host + jump_url)
// const pathname = url.pathname
// headerNavBarVueApp.matchMenu(pathname)
// setTimeout(() => {
// const a = document.querySelector('a[href="' + pathname + '"][data-pjax]')
// if (a) {
// a.href = jump_url
// a.click()
// a.href = pathname
// // leftMenuApp.assignmentMenu()
// }
// }, 0)
const index = pageUrl.indexOf("?") const index = pageUrl.indexOf("?")
const doMainUrl = index > -1 ? pageUrl.substring(0, index) : pageUrl const doMainUrl = index > -1 ? pageUrl.substring(0, index) : pageUrl
const {protocol, host} = window.location const {protocol, host} = window.location
@@ -67,8 +67,8 @@
<script src="${base!}/assets/platform/plugins/echarts/macarons.js"></script><!--主题--> <script src="${base!}/assets/platform/plugins/echarts/macarons.js"></script><!--主题-->
<script src="${base!}/assets/platform/plugins/echarts/echarts.js"></script> <script src="${base!}/assets/platform/plugins/echarts/echarts.js"></script>
<script src="${base!}/assets/platform/plugins/echarts/langZH.js"></script> <script src="${base!}/assets/platform/plugins/echarts/langZH.js"></script>
<!-- <script src="https://cdn.staticfile.org/echarts/5.2.2/echarts.js"></script>--> <!-- <script src="https://cdn.staticfile.org/echarts/5.2.2/echarts.js"></script>-->
<!-- <script src="https://cdn.staticfile.org/echarts/5.2.2/i18n/langZH.js"></script>--> <!-- <script src="https://cdn.staticfile.org/echarts/5.2.2/i18n/langZH.js"></script>-->
<script src="${base!}/assets/platform/module/mixins/initTableMixins.js?v=1.0.3"></script> <script src="${base!}/assets/platform/module/mixins/initTableMixins.js?v=1.0.3"></script>
@@ -441,7 +441,7 @@
secondMenus: {}, secondMenus: {},
userDefaultModuleId: null, userDefaultModuleId: null,
menuPopover: false, menuPopover: false,
appModuleId:"" appModuleId: ""
} }
}, },
methods: { methods: {
@@ -609,13 +609,12 @@
const selectEntranceModuleStr = window.sessionStorage.getItem('selectEntranceModule') const selectEntranceModuleStr = window.sessionStorage.getItem('selectEntranceModule')
if (selectEntranceModuleStr) { if (selectEntranceModuleStr) {
this.selectEntranceModule = JSON.parse(selectEntranceModuleStr) this.selectEntranceModule = JSON.parse(selectEntranceModuleStr)
this.appModuleId = this.selectEntranceModule.id
} }
this.appModuleId=this.selectEntranceModule.id
await this.getEntranceModule() await this.getEntranceModule()
await this.getFirstMenus(this.selectEntranceModule.id) await this.getFirstMenus(this.selectEntranceModule.id)
await this.getSecondMenus() await this.getSecondMenus()
this.setAttr() this.setAttr()
// let pjax_url = window.sessionStorage.getItem('pjax_url') // let pjax_url = window.sessionStorage.getItem('pjax_url')
// pjax_url = pjax_url.replace(window.location.origin, '') // pjax_url = pjax_url.replace(window.location.origin, '')
// $.pjax({ // $.pjax({
@@ -53,8 +53,8 @@
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script src="${base!}/assets/platform/js/scannerQrCodeJs.js"></script> <script src="${base!}/assets/platform/js/scannerQrCodeJs.js"></script>
<script type="text/javascript" <!-- <script type="text/javascript"-->
src="https://webapi.amap.com/maps?v=2.0&key=57f0d098ba1b881ecc436c4cfd23bbbf&plugin=AMap.PolyEditor,AMap.Geolocation"></script> <!-- src="https://webapi.amap.com/maps?v=2.0&key=57f0d098ba1b881ecc436c4cfd23bbbf&plugin=AMap.PolyEditor,AMap.Geolocation"></script>-->
<script src="https://cdn-go.cn/cdn/vod-js-sdk-v6/latest/vod-js-sdk-v6.js"></script> <script src="https://cdn-go.cn/cdn/vod-js-sdk-v6/latest/vod-js-sdk-v6.js"></script>
<link href="https://web.sdk.qcloud.com/player/tcplayer/release/v4.5.2/tcplayer.min.css" rel="stylesheet"/> <link href="https://web.sdk.qcloud.com/player/tcplayer/release/v4.5.2/tcplayer.min.css" rel="stylesheet"/>
@@ -157,26 +157,30 @@ layout("/mobile/platform.html"){
<div class="page-survey" style="touch-action: manipulation;min-height: 100vh;"> <div class="page-survey" style="touch-action: manipulation;min-height: 100vh;">
<div class="survey-container"> <div class="survey-container">
<!-- v-if="questionData.qlx==2 && (questionData.ctMode==1 || questionData.ctMode==2)"--> <!-- v-if="questionData.qlx==2 && (questionData.ctMode==1 || questionData.ctMode==2)"-->
<div class="survey-head-top"> <div class="survey-head-top" v-if="finishAnswer && ( currentScore || currentScore===0 )">
<template> <!-- <template>-->
<div v-if="questionData.qlx==2 && questionData.ctMode==2 && isSameDay && randomCount > 0" <!-- <div v-if="questionData.qlx==2 && questionData.ctMode==2 && isSameDay && randomCount > 0"-->
class="randomCount" <!-- class="randomCount"-->
@click="openRandomCount"> <!-- @click="openRandomCount">-->
第<span>{{randomCount}}</span>轮 <!-- 第<span>{{randomCount}}</span>轮-->
</div> <!-- </div>-->
<div v-if="questionData.qlx==2 && (questionData.ctMode==1 || (questionData.ctMode==2 && !isSameDay))" <!-- <div v-if="questionData.qlx==2 && (questionData.ctMode==1 || (questionData.ctMode==2 && !isSameDay))"-->
class="randomCount" <!-- class="randomCount"-->
@click="openRandomCount"> <!-- @click="openRandomCount">-->
<!--定时定题 可切换时间查看以前的题目--> <!-- &lt;!&ndash;定时定题 可切换时间查看以前的题目&ndash;&gt;-->
<span>{{currentDate}}</span> <!-- <span>{{currentDate}}</span>-->
</div> <!-- </div>-->
</template> <!-- </template>-->
<div class="score" v-if="finishAnswer && ( currentScore || currentScore===0 )"> <div class="score" v-if="finishAnswer && ( currentScore || currentScore===0 )">
{{questionData.calcScoreMode==1?'今日最高成绩':'得分'}}{{currentScore}} 最新得分{{currentScore}}
<!-- {{questionData.calcScoreMode==1?'最高成绩':'得分'}}{{currentScore}}-->
</div> </div>
<div class="timer" v-if="questionData.enableTimer && !finishAnswer"> <div class="" style="margin-left: 5px;color: #1989fa" @click="viewHistoryScore">
<span>剩余时间:{{remainder}}</span> 查看历史得分
</div> </div>
<!-- <div class="timer" v-if="questionData.enableTimer && !finishAnswer">-->
<!-- <span>剩余时间:{{remainder}}</span>-->
<!-- </div>-->
</div> </div>
<div class="survey-head"> <div class="survey-head">
<div class="title"> <div class="title">
@@ -196,8 +200,8 @@ layout("/mobile/platform.html"){
{{issue.iisdx?'单选':'多选'}} {{issue.iisdx?'单选':'多选'}}
</van-tag> </van-tag>
</template> </template>
<!-- <van-tag v-if="issue.fraction" type="primary" size="large">{{issue.fraction}}分--> <van-tag v-if="issue.fraction" type="primary" size="large">{{issue.fraction}}分
<!-- </van-tag>--> </van-tag>
</div> </div>
<h2 class="question-title"> <h2 class="question-title">
<span class="question-seq"> <span class="question-seq">
@@ -267,6 +271,15 @@ layout("/mobile/platform.html"){
close-on-click-action close-on-click-action
></van-action-sheet> ></van-action-sheet>
<van-action-sheet v-model="historyScorePopup" title="历史得分记录">
<van-cell-group style="min-height: 500px">
<van-cell v-for="item in historyScores">
<div slot="title">第{{item.answerNum}}次</div>
<div style="font-weight: bold;color: #000000">{{item.score}}分</div>
</van-cell>
</van-cell-group>
</van-action-sheet>
</div> </div>
</div> </div>
@@ -310,7 +323,11 @@ layout("/mobile/platform.html"){
//答题时间(使用秒) //答题时间(使用秒)
countDownSeconds: 0, countDownSeconds: 0,
//剩余时间 //剩余时间
remainder: null remainder: null,
//历史分数
historyScores:[],
historyScorePopup:false
} }
}, },
@@ -362,6 +379,14 @@ layout("/mobile/platform.html"){
}, },
methods: { methods: {
viewHistoryScore(){
$.get(BASE_URL+"/historyScore",{questionId:this.id}).then(res=>{
if(res.code===0){
this.historyScores = res.data
this.historyScorePopup = true
}
})
},
openRandomCount() { openRandomCount() {
this.randomCountSheetShow = true this.randomCountSheetShow = true
this.randomActions = [] this.randomActions = []
@@ -573,7 +598,20 @@ layout("/mobile/platform.html"){
answerAgain() { answerAgain() {
if(this.questionData.ctMode===3){ if(this.questionData.ctMode===3){
location.reload() $.post(BASE_URL+'/answerAgainCommon',{id:this.id}).then(resp=>{
if(resp.code===0){
this.questionData = resp.data
this.finishAnswer = false
setTimeout(() => {
this.questionData.issues.forEach(issue => {
issue.wors.forEach(wor => {
this.$refs['checkboxes' + wor.wid][0].toggle(false)
})
})
}, 0)
this.checkTimer()
}
})
}else{ }else{
this.finishAnswer = false this.finishAnswer = false
setTimeout(() => { setTimeout(() => {
@@ -653,7 +691,16 @@ layout("/mobile/platform.html"){
} else { } else {
this.countDownSeconds = this.countDownSeconds - 1 this.countDownSeconds = this.countDownSeconds - 1
} }
},
getHistoryScore(){
$.get(BASE_URL+"/historyScore",{questionId:this.id}).then(res=>{
if(res.code===0){
this.historyScores = res.data
}
})
} }
}, },
async created() { async created() {
if (this.timerInterval) { if (this.timerInterval) {
@@ -666,6 +713,7 @@ layout("/mobile/platform.html"){
await this.checkUserFinishAnswer() await this.checkUserFinishAnswer()
await this.getScore() await this.getScore()
await this.checkTimer() await this.checkTimer()
this.getHistoryScore()
this.mLoading = false this.mLoading = false
} }
}) })
@@ -63,7 +63,7 @@ const onlineAnswer = {
<el-col :span="1" v-if="issue.row.fraction"> <el-col :span="1" v-if="issue.row.fraction">
<span>({{issue.row.fraction}}分)</span> <span>({{issue.row.fraction}}分)</span>
</el-col> </el-col>
<el-col :span="23"> <el-col :span="23" style="display: flex">
<span v-html="issue.row.iname"></span> <span v-html="issue.row.iname"></span>
<span v-if="issue.row.itx===1" size="mini" style="font-weight: bold"> <span v-if="issue.row.itx===1" size="mini" style="font-weight: bold">
({{issue.row.iisdx ? '单选' : '多选'}}) ({{issue.row.iisdx ? '单选' : '多选'}})
@@ -74,7 +74,7 @@ const onlineAnswer = {
<div style="color: rgb(160,160,160)" v-html="issue.row.its" <div style="color: rgb(160,160,160)" v-html="issue.row.its"
v-if="!isNull(issue.row.its)"></div> v-if="!isNull(issue.row.its)"></div>
</el-row> </el-row>
<el-row v-if="issue.row.itx==1"> <el-row>
<el-row v-for="wor,idx in issue.row.wors"> <el-row v-for="wor,idx in issue.row.wors">
<el-row style="margin-top: 5px" v-if="wor.wimg"> <el-row style="margin-top: 5px" v-if="wor.wimg">
<el-image <el-image
@@ -22,8 +22,7 @@ const batchInsert = {
<div style="height: 100%;width: 50%;overflow: auto;padding: 20px 10px 10px"> <div style="height: 100%;width: 50%;overflow: auto;padding: 20px 10px 10px">
<div v-for="(issue,index) in issues" :key="index" style="margin-bottom: 10px"> <div v-for="(issue,index) in issues" :key="index" style="margin-bottom: 10px">
<div style="display: flex"> <div style="display: flex">
<div>{{index+1}}、</div> <div>{{index+1}}、{{issue.iname}}</div>
<div>{{issue.iname}}</div>
</div> </div>
<div v-if="issue.wors"> <div v-if="issue.wors">
<el-radio-group v-if="issue.iisdx"> <el-radio-group v-if="issue.iisdx">
@@ -539,7 +539,7 @@ layout("/layouts/platform.html"){
<div style="color: rgb(160,160,160)" v-html="issue.row.its" <div style="color: rgb(160,160,160)" v-html="issue.row.its"
v-if="!isNull(issue.row.its)"></div> v-if="!isNull(issue.row.its)"></div>
</el-row> </el-row>
<el-row v-if="issue.row.itx==1"> <el-row v-if="issue.row.itx==1 || issue.row.itx==3">
<el-row v-for="wor,idx in issue.row.wors"> <el-row v-for="wor,idx in issue.row.wors">
<el-row style="margin-top: 5px" v-if="wor.wimg"> <el-row style="margin-top: 5px" v-if="wor.wimg">
<el-image <el-image
@@ -863,7 +863,7 @@ layout("/layouts/platform.html"){
:key="item.x" :key="item.x"
:label="item.y" :label="item.y"
:value="item.x" :value="item.x"
v-for="item in [{x:1,y:'选择题'},{x:2,y:'填空题'}]"> v-for="item in [{x:1,y:'选择题'},{x:2,y:'填空题'},{x:3,y:'判断题'}]">
</el-option> </el-option>
</el-select> </el-select>
</div> </div>
@@ -928,9 +928,9 @@ layout("/layouts/platform.html"){
</el-date-picker> </el-date-picker>
</el-row> </el-row>
<template v-if="issueData[currentModifyIssueIndex].itx===1"> <template v-if="issueData[currentModifyIssueIndex].itx===1 || issueData[currentModifyIssueIndex].itx===3">
<vi-title2 title="选项设置"></vi-title2> <vi-title2 title="选项设置"></vi-title2>
<el-row v-if="currentModifyIssue.itx==1"> <el-row v-if="currentModifyIssue.itx==1 || currentModifyIssue.itx==3">
<el-table :data="issueData[currentModifyIssueIndex].wors" border <el-table :data="issueData[currentModifyIssueIndex].wors" border
style="width: 100%;margin-top: 10px;"> style="width: 100%;margin-top: 10px;">
<el-table-column header-align="center" label="选项文字" property="wtxt"> <el-table-column header-align="center" label="选项文字" property="wtxt">
@@ -942,6 +942,7 @@ layout("/layouts/platform.html"){
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="图片" align="center" header-align="center" <el-table-column label="图片" align="center" header-align="center"
v-if="currentModifyIssue.itx==1"
width="150px"> width="150px">
<template slot-scope="scope"> <template slot-scope="scope">
<el-upload <el-upload
@@ -967,6 +968,7 @@ layout("/layouts/platform.html"){
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" header-align="center" label="说明" <el-table-column align="center" header-align="center" label="说明"
v-if="currentModifyIssue.itx==1"
property="wsm" width="250px"> property="wsm" width="250px">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input <el-input
@@ -984,6 +986,7 @@ layout("/layouts/platform.html"){
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" header-align="center" label="链接" <el-table-column align="center" header-align="center" label="链接"
v-if="currentModifyIssue.itx==1"
property="wlj" width="150px"> property="wlj" width="150px">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input <el-input
@@ -1007,6 +1010,7 @@ layout("/layouts/platform.html"){
</template> </template>
</el-table-column>--> </el-table-column>-->
<el-table-column align="center" header-align="center" label="默认选中" <el-table-column align="center" header-align="center" label="默认选中"
v-if="currentModifyIssue.itx==1"
property="wismr" property="wismr"
width="80px"> width="80px">
<template slot-scope="scope"> <template slot-scope="scope">
@@ -7,7 +7,7 @@ const activity = {
<div style="width: 100%"> <div style="width: 100%">
<div style="background-color: #fff; position: relative; margin-bottom: 10px"> <div style="background-color: #fff; position: relative; margin-bottom: 10px">
<div style="display: flex; justify-content: center; align-items: center"> <div style="display: flex; justify-content: center; align-items: center">
<el-image :src="CREATE_PREVIEW_URL(item.cover.id)" alt="" fit="fill" <el-image :src="'/file_server/fileStreamPreview?id='+item.cover.filepath" alt="" fit="fill"
style="margin: 3px 14px 0 0; width: 100px; height: 70px; border-radius: 8px"> style="margin: 3px 14px 0 0; width: 100px; height: 70px; border-radius: 8px">
<div slot="error" class="image-slot"> <div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i> <i class="el-icon-picture-outline"></i>
@@ -39,7 +39,7 @@ const appModule = {
created() { created() {
this.getEntranceModule() this.getEntranceModule()
setTimeout(() => { setTimeout(() => {
this.id = headerNavBarVueApp.appModuleId // this.id = headerNavBarVueApp.appModuleId
}, 100) }, 400)
} }
} }
@@ -37,6 +37,7 @@ const quickEntry = {
})*/ })*/
}, },
menuClick(item) { menuClick(item) {
// location.href=item.url
sublime.jumpPagePjax(item.url) sublime.jumpPagePjax(item.url)
} }
}, },