feat: 普惠疗休养优化4

This commit is contained in:
2026-11-10 14:29:19 +08:00
parent e4e856f6b2
commit faafb72eee
16 changed files with 348 additions and 356 deletions
+1
View File
@@ -53,3 +53,4 @@ outputs
semgrep.json
trivy.json
.workbuddy
@@ -1,5 +1,13 @@
package com.budwk.app.zhgh.staffbenefit.therapyRecuperation.controller.statistics;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import com.budwk.app.zhgh.staffbenefit.therapyRecuperation.support.TherapyDownload;
import org.apache.poi.ss.usermodel.Workbook;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.result.Result;
@@ -186,10 +194,27 @@ public class TheRapyRecuperationLineStatisticsController {
@Param(value = "selectId") String selectId,
@Param(value = "playMonth") String playMonth,
@Param(value = "regionalNature") String regionalNature,
@Param(value = "travelAgencyId") String travelAgencyId) {
Sql sql = Sqls.create("""
@Param(value = "travelAgencyId") String travelAgencyId,
@Param(value = "groupStatus") Integer groupStatus) {
List<Sql> queries = buildStatisticsQueries(startYear, endYear, unionId, takePartInLineId, lotId,
signUpMode, selectId, playMonth, regionalNature, travelAgencyId, groupStatus);
return enrollService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), queries.get(0), queries.get(1));
}
private List<Sql> buildStatisticsQueries(Integer startYear, Integer endYear, String unionId,
String takePartInLineId, String lotId, String signUpMode,
String selectId, String playMonth, String regionalNature,
String travelAgencyId, Integer groupStatus) {
if (startYear != null && endYear != null && startYear > endYear) {
throw new IllegalArgumentException("开始年度不能大于结束年度");
}
String groupedCondition = "IFNULL(lineu.estimatedFamilyNumbers, 0) > 0 AND "
+ "IFNULL(enrollStats.lineNum, 0) + IFNULL(enrollStats.adultFamilyNum, 0) >= lineu.estimatedFamilyNumbers";
String query = """
SELECT
line.id,
$groupStatus AS groupStatusName,
IFNULL(enrollStats.lineNum, 0) + IFNULL(enrollStats.adultFamilyNum, 0) AS groupCount,
line.lineName,
line.regionalNature,
lineu.lineId,
@@ -241,16 +266,12 @@ public class TheRapyRecuperationLineStatisticsController {
) enrollStats ON enrollStats.takePartInLineId = lineu.id
$condition
ORDER BY line.serialNumber ASC, lineu.playStartTime ASC
""");
Sql countSql = Sqls.create("""
SELECT
COUNT(1)
FROM
inclusive_therapy_v3_line_union_select lineu
LEFT JOIN inclusive_therapy_v3_line line ON line.id = lineu.lineId
LEFT JOIN inclusive_therapy_v3_lot lot ON lot.id = line.lotId
$condition
""");
""";
Sql sql = Sqls.create(query);
Sql countSql = Sqls.create("SELECT COUNT(1) FROM (" + query.substring(0, query.indexOf("ORDER BY")) + ") statistics");
String groupStatusExpression = "CASE WHEN " + groupedCondition + " THEN '成团' ELSE '未成团' END";
sql.setVar("groupStatus", groupStatusExpression);
countSql.setVar("groupStatus", groupStatusExpression);
Cnd cnd = Cnd.NEW();
if (startYear != null) {
cnd.and("lineu.selectTime", ">=", startYear + "-01-01 00:00:00");
@@ -260,9 +281,11 @@ public class TheRapyRecuperationLineStatisticsController {
}
cnd.andEX("lineu.signUpMode", "=", signUpMode);
sql.setVar("unionCnd", "");
countSql.setVar("unionCnd", "");
if (!TherapyContext.hasAnyRoles("sysadmin,H06")) {
String unionCndSql = "and (e.selfUnionId='%s' or e.takePartInUnionId = '%s')".formatted(TherapyContext.getUnionId(), TherapyContext.getUnionId());
sql.setVar("unionCnd", unionCndSql);
countSql.setVar("unionCnd", unionCndSql);
cnd.and("lineu.unionId", "=", TherapyContext.getUnionId());
} else {
if (Strings.isNotBlank(unionId)) {
@@ -282,9 +305,55 @@ public class TheRapyRecuperationLineStatisticsController {
}
cnd.andEX("line.regionalNature", "=", regionalNature);
addTravelAgencyCondition(cnd, travelAgencyId);
if (groupStatus != null && (groupStatus == 1 || groupStatus == 2)) {
cnd.and("(" + groupedCondition + ")", "=", groupStatus == 1 ? 1 : 0);
}
sql.setCondition(cnd);
countSql.setCondition(cnd);
return enrollService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql, countSql);
return Arrays.asList(sql, countSql);
}
@At("/exportList")
@Ok("void")
@SaCheckPermission("inclusiveTherapyV3.statistics")
public void exportList(@Param("startYear") Integer startYear,
@Param("endYear") Integer endYear,
@Param("unionId") String unionId,
@Param("takePartInLineId") String takePartInLineId,
@Param("lotId") String lotId,
@Param("signUpMode") String signUpMode,
@Param("selectId") String selectId,
@Param("playMonth") String playMonth,
@Param("regionalNature") String regionalNature,
@Param("travelAgencyId") String travelAgencyId,
@Param("groupStatus") Integer groupStatus,
HttpServletResponse response) throws IOException {
List<Sql> queries = buildStatisticsQueries(startYear, endYear, unionId, takePartInLineId, lotId,
signUpMode, selectId, playMonth, regionalNature, travelAgencyId, groupStatus);
List<NutMap> rows = enrollService.listMap(queries.get(0));
List<ExcelExportEntity> columns = new ArrayList<>();
columns.add(new ExcelExportEntity("年度", "year", 12));
columns.add(new ExcelExportEntity("线路名称", "lineName", 35));
columns.add(new ExcelExportEntity("选择线路工会", "unionname", 25));
columns.add(new ExcelExportEntity("出行时间", "linePlayTime", 20));
columns.add(new ExcelExportEntity("报名时间", "signUpTime", 20));
columns.add(new ExcelExportEntity("调剂报名截止时间", "adjustSignUpEndTime", 22));
columns.add(new ExcelExportEntity("承担旅行社", "travelAgencyName", 25));
columns.add(new ExcelExportEntity("线路类型", "regionalNature", 12));
columns.add(new ExcelExportEntity("联系人", "contact", 15));
columns.add(new ExcelExportEntity("联系方式", "contactMobileNumber", 20));
columns.add(new ExcelExportEntity("最少成团人数", "estimatedFamilyNumbers", 16));
columns.add(new ExcelExportEntity("成团计入人数", "groupCount", 16));
columns.add(new ExcelExportEntity("教职工报名人数", "lineNum", 18));
columns.add(new ExcelExportEntity("家属总人数", "signUpUserFamilyNum", 16));
columns.add(new ExcelExportEntity("成年家属人数", "adultFamilyNum", 16));
columns.add(new ExcelExportEntity("成团状态", "groupStatusName", 12));
TherapyDownload.excelResponse(response, "疗休养线路统计.xlsx");
ExportParams params = new ExportParams();
params.setType(ExcelType.XSSF);
try (Workbook workbook = ExcelExportUtil.exportExcel(params, columns, rows)) {
workbook.write(response.getOutputStream());
}
}
/**
@@ -231,15 +231,6 @@ layout("/layouts/platform.html"){
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" style="width: 100%"
row-key="id" @sort-change="pageOrder"
@@ -159,15 +159,6 @@ layout("/layouts/platform.html"){
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" style="width: 100%"
row-key="id" @sort-change="pageOrder"
@@ -224,15 +224,6 @@ layout("/layouts/platform.html"){
</el-radio-group>
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" style="width: 100%"
row-key="id" @sort-change="pageOrder"
@@ -82,15 +82,6 @@ layout("/layouts/platform.html"){
<el-button @click="openAdd" size="medium" type="primary">新建目的地</el-button>
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
align="center"
@@ -172,15 +172,6 @@ layout("/layouts/platform.html"){
<el-button @click="openAdd" size="medium" type="primary">新建线路</el-button>
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
align="center"
@@ -54,15 +54,6 @@ layout("/layouts/platform.html"){
<el-button @click="openAdd" size="medium" type="primary">新建旅行社</el-button>
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" :size="tableSize">
<el-table-column :index="indexMethod" align="center" header-align="center" label="序号" type="index"
@@ -343,7 +343,6 @@ layout("/layouts/platform.html"){
</el-select>
<template slot="append">
<el-button @click="doSearch">搜索</el-button>
<el-button @click="resetSearch" icon="el-icon-refresh-left">重置</el-button>
</template>
</el-input>
</el-col>
@@ -617,15 +616,6 @@ layout("/layouts/platform.html"){
</el-radio-group>
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :key="pageForm.state + '-' + pageForm.lb" :data="tableData" style="width: 100%"
@@ -17,6 +17,13 @@ layout("/layouts/platform.html"){
overflow: hidden;
}
.query-row .query-row-content > .el-row > .el-col > span:first-child {
display: inline-block;
width: 5em;
white-space: nowrap;
vertical-align: middle;
}
.query-row:not(:last-child) {
border-bottom: 1px dashed rgb(230, 230, 230);
}
@@ -42,23 +49,15 @@ layout("/layouts/platform.html"){
<el-row>
<el-col :span="12">
<span>年&emsp;&emsp;度:</span>
<el-date-picker
:clearable="false"
v-model="pageForm.startYear"
type="year"
value-format="yyyy"
placeholder="选择年"
style="width: 38%" @change="yearChange">
</el-date-picker>
<div style="display: inline-flex; align-items: center; gap: 8px; vertical-align: middle; min-width: 0; width: 80%;">
<el-date-picker v-model="pageForm.startYear" type="year" format="yyyy年" value-format="yyyy" :clearable="false"
placeholder="开始年度" :picker-options="{disabledDate: date => !!pageForm.endYear && date.getFullYear() > Number(pageForm.endYear)}"
@change="yearChange" style="flex: 1; width: 0; min-width: 0"></el-date-picker>
<span>至</span>
<el-date-picker
:clearable="false"
v-model="pageForm.endYear"
type="year"
value-format="yyyy"
placeholder="选择年"
style="width: 38%" @change="yearChange">
</el-date-picker>
<el-date-picker v-model="pageForm.endYear" type="year" format="yyyy年" value-format="yyyy" :clearable="false"
placeholder="结束年度" :picker-options="{disabledDate: date => !!pageForm.startYear && date.getFullYear() < Number(pageForm.startYear)}"
@change="yearChange" style="flex: 1; width: 0; min-width: 0"></el-date-picker>
</div>
</el-col>
<el-col :span="12">
<span>查询类型:</span>
@@ -73,7 +72,6 @@ layout("/layouts/platform.html"){
</el-select>
<template slot="append">
<el-button @click="doSearch">搜索</el-button>
<el-button @click="resetSearch" icon="el-icon-refresh-left">重置</el-button>
</template>
</el-input>
</el-col>
@@ -246,15 +244,6 @@ layout("/layouts/platform.html"){
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" style="width: 100%"
@@ -115,15 +115,6 @@ layout("/layouts/platform.html"){
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
align="center"
@@ -100,15 +100,6 @@ layout("/layouts/platform.html"){
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
align="center"
@@ -110,15 +110,6 @@ layout("/layouts/platform.html"){
<el-button @click="doExportExcel" icon="el-icon-download" size="small" type="primary">导出表格</el-button>
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData"
:size="tableSize"
@@ -203,15 +203,6 @@ layout("/layouts/platform.html"){
</el-radio-group>
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
ref="table"
@@ -3,24 +3,15 @@ layout("/layouts/platform.html"){
#-->
<!--#include("/platform/zhgh/staffbenefit/therapyRecuperation/common.html"){}#-->
<style>
.query-row {
height: 70px;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
}
.query-row .el-col {
overflow: hidden;
}
.query-row:not(:last-child) {
border-bottom: 1px dashed rgb(230, 230, 230);
}
.query-row-title {
width: 120px;
.statistics-filter-row + .statistics-filter-row { margin-top: 18px; }
.statistics-filter-item { display: flex; align-items: center; min-width: 0; }
.statistics-filter-label { flex: 0 0 80px; text-align: right; }
.statistics-filter-item > .el-select,
.statistics-filter-item > .el-date-editor { flex: 1; min-width: 0; }
.statistics-filter-item .el-range-separator { width: 24px; }
@media (max-width: 767px) {
.statistics-filter-row + .statistics-filter-row { margin-top: 0; }
.statistics-filter-item { margin-bottom: 14px; }
}
.el-table-container {
@@ -80,40 +71,29 @@ layout("/layouts/platform.html"){
.therapy-statistics-time-dialog .el-dialog__body { padding: 16px; }
}
</style>
<div id="app" v-cloak>
<guava ref="guava">
<template>
<el-card shadow="never">
<el-row align="middle" class="query-row" type="flex">
<el-col class="query-row-title"></el-col>
<el-col class="query-row-content">
<el-row>
<el-col :span="12">
<span>年&emsp;&emsp;度:</span>
<el-date-picker
:clearable="false"
v-model="pageForm.startYear"
type="year"
value-format="yyyy"
placeholder="选择年"
style="width: 38%" @change="yearChange">
</el-date-picker>
<el-card shadow="never" class="statistics-filters">
<el-row :gutter="24" class="statistics-filter-row">
<el-col :span="8" :xs="24" class="statistics-filter-item">
<span class="statistics-filter-label">年度:</span>
<div style="display: inline-flex; align-items: center; gap: 8px; vertical-align: middle; min-width: 0; width: 100%; flex: 1;">
<el-date-picker v-model="pageForm.startYear" type="year" format="yyyy年" value-format="yyyy"
placeholder="开始年度" :picker-options="{disabledDate: date => !!pageForm.endYear && date.getFullYear() > Number(pageForm.endYear)}"
@change="yearChange" style="flex: 1; width: 0; min-width: 0"></el-date-picker>
<span>至</span>
<el-date-picker
:clearable="false"
v-model="pageForm.endYear"
type="year"
value-format="yyyy"
placeholder="选择年"
style="width: 38%" @change="yearChange">
</el-date-picker>
<el-date-picker v-model="pageForm.endYear" type="year" format="yyyy年" value-format="yyyy"
placeholder="结束年度" :picker-options="{disabledDate: date => !!pageForm.startYear && date.getFullYear() < Number(pageForm.startYear)}"
@change="yearChange" style="flex: 1; width: 0; min-width: 0"></el-date-picker>
</div>
</el-col>
<el-col :span="12">
<span>所属工会:</span>
<el-col :span="8" :xs="24" class="statistics-filter-item">
<span class="statistics-filter-label">所属工会:</span>
<el-select v-model="pageForm.unionId" placeholder="请选择所属工会"
@change="doSearch()"
filterable clearable style="width: 80%">
filterable clearable style="width: 100%">
<el-option
v-for="item in unions"
:key="item.id"
@@ -122,20 +102,11 @@ layout("/layouts/platform.html"){
</el-option>
</el-select>
</el-col>
</el-row>
</el-col>
</el-row>
<el-row align="middle" class="query-row" type="flex">
<el-col class="query-row-title"></el-col>
<el-col class="query-row-content">
<el-row>
<el-col :span="12">
<span>旅&nbsp;行&nbsp;社:</span>
<el-col :span="8" :xs="24" class="statistics-filter-item">
<span class="statistics-filter-label">旅行社:</span>
<el-select v-model="pageForm.travelAgencyId" filterable clearable
placeholder="请选择旅行社"
style="width: 80%" @change="agencyChange">
style="width: 100%" @change="agencyChange">
<el-option v-for="item in agencyLists"
:key="item.id"
:label="item.travelAgencyName"
@@ -143,31 +114,24 @@ layout("/layouts/platform.html"){
</el-option>
</el-select>
</el-col>
<el-col :span="12">
<span>路线类型:</span>
</el-row>
<el-row :gutter="24" class="statistics-filter-row">
<el-col :span="8" :xs="24" class="statistics-filter-item">
<span class="statistics-filter-label">线路类型:</span>
<el-select v-model="pageForm.regionalNature" filterable
placeholder="请选择线路"
style="width: 80%"
placeholder="请选择线路类型"
style="width: 100%"
@change="lineTypeChange">
<el-option label="全部" value=""></el-option>
<el-option label="省内" value="省内"></el-option>
<el-option label="省外" value="省外"></el-option>
</el-select>
</el-col>
</el-row>
</el-col>
</el-row>
<el-row align="middle" class="query-row" type="flex">
<el-col class="query-row-title"></el-col>
<el-col class="query-row-content">
<el-row>
<el-col :span="12">
<span>线&emsp;&emsp;路:</span>
<el-col :span="8" :xs="24" class="statistics-filter-item">
<span class="statistics-filter-label">线路:</span>
<el-select v-model="pageForm.takePartInLineId" filterable clearable
placeholder="请选择线路"
style="width: 80%" @change="lineChange">
style="width: 100%">
<el-option v-for="item in lineList"
:key="item.id"
:label="item.lineName + '-' + item.regionalNature + '【' + item.lotName + '】' + '(' + item.signUpMode + ')'"
@@ -175,11 +139,11 @@ layout("/layouts/platform.html"){
</el-option>
</el-select>
</el-col>
<el-col :span="12">
<span>标&emsp;&emsp;段:</span>
<el-col :span="8" :xs="24" class="statistics-filter-item">
<span class="statistics-filter-label">标段:</span>
<el-select v-model="pageForm.lotId" filterable clearable
placeholder="请选择标段"
style="width: 80%" @change="doSearch()">
style="width: 100%">
<el-option v-for="item in modifyConfig.lots"
:key="item.id"
:label="item.lotName"
@@ -188,29 +152,23 @@ layout("/layouts/platform.html"){
</el-select>
</el-col>
</el-row>
<el-row :gutter="24" class="statistics-filter-row">
<el-col :span="8" :xs="24" class="statistics-filter-item">
<span class="statistics-filter-label">出行年月:</span>
<el-date-picker v-model="pageForm.playMonth" type="month" value-format="yyyy-MM"
placeholder="请选择出行年月" clearable style="width: 100%"></el-date-picker>
</el-col>
</el-row>
<el-row align="middle" class="query-row" type="flex">
<el-col class="query-row-title"></el-col>
<el-col class="query-row-content">
<el-row>
<el-col :span="12">
<span>出行年月:</span>
<el-date-picker
v-model="pageForm.playMonth"
type="month"
value-format="yyyy-MM"
placeholder="请选择出行年月"
style="width: 80%"
clearable
@change="doSearch">
</el-date-picker>
<el-col :span="8" :xs="24" class="statistics-filter-item">
<span class="statistics-filter-label">成团状态:</span>
<el-select v-model="pageForm.groupStatus" style="width: 100%">
<el-option label="全部" value=""></el-option>
<el-option label="成团" :value="1"></el-option>
<el-option label="未成团" :value="2"></el-option>
</el-select>
</el-col>
<el-col :span="12"></el-col>
</el-row>
<el-col :span="8" :xs="24" class="statistics-filter-item">
<el-button icon="el-icon-search" type="primary" @click="doSearch" :loading="tableLoading">搜索</el-button>
<el-button icon="el-icon-refresh-left" @click="resetSearch">重置</el-button>
</el-col>
</el-row>
</el-card>
@@ -219,6 +177,7 @@ layout("/layouts/platform.html"){
<el-card shadow="never" class="mt10">
<table-tool label="" :columns.sync="tableColumns" ref="table_tool">
<template>
<el-button icon="el-icon-download" size="small" type="primary" @click="exportCurrentList" :disabled="tableLoading || !exportQuery">导出当前查询列表</el-button>
<el-button @click="showGiveTimes"
size="small"
style="margin-right: 10px"
@@ -245,15 +204,6 @@ layout("/layouts/platform.html"){
</el-button>-->
</template>
<el-button icon="el-icon-refresh" @click="doSearch" circle></el-button>
<el-dropdown @command="tableSizeChange">
<el-button icon="el-icon-c-scale-to-original" circle></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="mini">Mini</el-dropdown-item>
<el-dropdown-item command="small">Small</el-dropdown-item>
<el-dropdown-item command="medium">Medium</el-dropdown-item>
<el-dropdown-item command="">Large</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" style="width: 100%"
ref="table"
@@ -489,7 +439,10 @@ layout("/layouts/platform.html"){
takePartInLineId: '',
playMonth: '',
travelAgencyId: '',
groupStatus: '',
},
appliedQuery: null,
exportQuery: null,
tableColumns: [
{prop: 'lineName', label: '线路名称', sortable: true, width: 300, fixed: 'left'},
{prop: 'unionname', label: '选择线路工会', sortable: true, width: 220, fixed: 'left'},
@@ -502,6 +455,7 @@ layout("/layouts/platform.html"){
{prop: 'contactMobileNumber', label: '联系方式', visible: false},
{prop: 'estimatedFamilyNumbers', label: '最少成团人数', width: 80},
{prop: 'groupCount', label: '成团计入人数', width: 80},
{prop: 'groupStatusName', label: '成团状态', width: 90},
{prop: 'lineNum', label: '教职工报名人数', width: 80},
{prop: 'signUpUserFamilyNum', label: '家属总人数', width: 80},
{prop: 'adultFamilyNum', label: '成年家属人数', width: 80},
@@ -643,6 +597,56 @@ layout("/layouts/platform.html"){
})
})
},
statisticsFilters() {
const keys = ['startYear', 'endYear', 'unionId', 'regionalNature', 'signUpMode',
'lotId', 'takePartInLineId', 'playMonth', 'travelAgencyId', 'groupStatus']
return keys.reduce((filters, key) => {
filters[key] = this.pageForm[key] == null ? '' : this.pageForm[key]
return filters
}, {})
},
doSearch() {
if (this.pageForm.startYear && this.pageForm.endYear && Number(this.pageForm.startYear) > Number(this.pageForm.endYear)) {
this.$message.warning('开始年度不能大于结束年度')
return
}
this.appliedQuery = this.statisticsFilters()
this.pageForm.pageNumber = 1
this.$refs.table && this.$refs.table.clearSelection()
return this.pageData()
},
async resetSearch() {
Object.assign(this.pageForm, {
startYear: moment().format('YYYY'), endYear: moment().format('YYYY'),
unionId: '', regionalNature: '', signUpMode: '', lotId: '',
takePartInLineId: '', playMonth: '', travelAgencyId: '', groupStatus: ''
})
this.getAgencyList()
this.getUnionSelectLine()
return this.doSearch()
},
pageData() {
if (!this.appliedQuery) this.appliedQuery = this.statisticsFilters()
const filters = {...this.appliedQuery}
const request = this._statisticsRequest = (this._statisticsRequest || 0) + 1
this.tableLoading = true
return this.$axios.post(loc() + '/pageData', {...this.pageForm, ...filters}).then(res => {
if (request !== this._statisticsRequest) return
if (res.code === 0) {
this.tableData = res.data.list
this.pageForm.totalCount = res.data.totalCount
this.exportQuery = filters
} else {
this.$message.error(res.msg || '查询失败')
}
}).finally(() => {
if (request === this._statisticsRequest) this.tableLoading = false
})
},
exportCurrentList() {
if (!this.exportQuery || this.tableLoading) return
this.downloadFile(loc() + '/exportList?' + $.param(this.exportQuery))
},
doExport() {
const {
startYear,
@@ -666,25 +670,19 @@ layout("/layouts/platform.html"){
"&types=" + JSON.stringify(['line']) +
"&linePlayTime=" + linePlayTime)
},
lineChange() {
this.doSearch()
},
lineTypeChange() {
this.pageForm.takePartInLineId = ''
this.getUnionSelectLine()
this.doSearch()
},
yearChange() {
this.$set(this.pageForm, 'travelAgencyId', '')
this.$set(this.pageForm, 'takePartInLineId', '')
this.getAgencyList()
this.getUnionSelectLine()
this.doSearch()
},
agencyChange() {
this.$set(this.pageForm, 'takePartInLineId', '')
this.getUnionSelectLine()
this.doSearch()
},
async sendSuccess(row) {
if (!this._therapyActions) this._therapyActions = new Set()
@@ -23,12 +23,13 @@ layout("/layouts/platform_h5.html"){
}
.van-skeleton {
padding: 20px;
padding: 0;
}
.van-skeleton__content {
height: 600px;
padding-top: 60px;
height: auto;
min-height: 60vh;
padding: 16px 12px;
}
.van-skeleton__row, .van-skeleton__title {
@@ -131,7 +132,7 @@ layout("/layouts/platform_h5.html"){
.top_icon {
position: fixed;
bottom: 80px;
right: 20px;
right: max(20px, calc(50vw - 300px));
}
.van-field__control {
@@ -183,8 +184,12 @@ layout("/layouts/platform_h5.html"){
color: white !important;
}
/* 固定导航条改为页面显式让出高度:H5 布局的滚动容器是 .page-wrapper,
van-sticky 无法在此生效,组件 placeholder 又在 v-cloak 期间测量为 0,会把首屏内容盖住。 */
#app.therapy-line-detail { height: auto; min-height: 100%; padding-top: calc(46px + env(safe-area-inset-top)); }
#app.therapy-line-detail > .van-nav-bar--fixed { z-index: 20; box-shadow: 0 1px 0 rgba(20, 37, 68, .06); }
#app.therapy-line-detail .van-nav-bar__title { color: #142544; font-size: 18px; font-weight: 700; }
#app.therapy-line-detail .container { padding: 16px 12px calc(90px + env(safe-area-inset-bottom)); }
#app.therapy-line-detail .container { box-sizing: border-box; max-width: 640px; margin: 0 auto; padding: 16px 12px calc(90px + env(safe-area-inset-bottom)); }
#app.therapy-line-detail .detail-card { margin-bottom: 14px; padding: 18px 16px; border: 1px solid #fff; border-radius: 18px; background: #fbfdff; color: #52627b; box-shadow: 0 7px 22px rgba(65, 115, 183, .06); }
#app.therapy-line-detail .detail-title-row { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px; }
#app.therapy-line-detail .detail-title-row h1 { flex: 1 1 180px; min-width: 0; margin: 0; color: #142544; font-size: 21px; font-weight: 700; line-height: 30px; overflow-wrap: anywhere; }
@@ -207,7 +212,7 @@ layout("/layouts/platform_h5.html"){
#app.therapy-line-detail .detail-time-label { grid-column: 1 / -1; color: #7d8da7; font-size: 13px; line-height: 20px; }
#app.therapy-line-detail .detail-date { display: flex; flex-direction: column; min-width: 0; }
#app.therapy-line-detail .detail-date strong { color: #243249; font-size: 24px; font-weight: 600; line-height: 32px; white-space: nowrap; }
#app.therapy-line-detail .detail-date span { margin-top: 8px; color: #929fb4; font-size: 12px; line-height: 20px; }
#app.therapy-line-detail .detail-date span { color: #929fb4; font-size: 12px; line-height: 20px; }
#app.therapy-line-detail .detail-date-end { text-align: right; }
#app.therapy-line-detail .detail-time-to { align-self: flex-start; margin: 0 8px; color: #b6c8e0; font-size: 28px; line-height: 32px; }
#app.therapy-line-detail .detail-contact-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px; border-radius: 12px; background: #f2f7ff; overflow-wrap: anywhere; }
@@ -242,25 +247,20 @@ layout("/layouts/platform_h5.html"){
#app.therapy-line-detail .detail-time-to { margin: 0 4px; }
}
#app.therapy-line-detail .joinPopup { display: flex; flex-direction: column; background: #f6f7f9; }
#app.therapy-line-detail .joinPopup .van-nav-bar__placeholder { flex-shrink: 0; }
#app.therapy-line-detail .joinPopup .van-nav-bar { background: #fff; }
#app.therapy-line-detail .joinPopup .van-nav-bar { flex-shrink: 0; background: #fff; }
#app.therapy-line-detail .join_content { flex: 1; min-height: 0; height: auto; padding: 16px 12px calc(90px + env(safe-area-inset-bottom)); box-sizing: border-box; overflow-y: auto; }
#app.therapy-line-detail .joinPopup .van-doc-card { margin: 0 0 16px; padding: 0 14px 8px; border: 1px solid #e1e6ee; border-radius: 12px; background: #fff; box-shadow: none; }
#app.therapy-line-detail .joinPopup .van-doc-card > div:first-child { display: none; }
#app.therapy-line-detail .joinPopup .van-card-header { display: flex; align-items: center; gap: 10px; padding: 14px 0; border-bottom: 1px solid #edf0f5; color: #006bff; font-size: 16px; line-height: 24px; }
#app.therapy-line-detail .joinPopup .van-card-header::before { content: ''; flex: 0 0 4px; height: 18px; border-radius: 2px; background: #006bff; }
#app.therapy-line-detail .joinPopup .van-card-body { padding: 0; }
#app.therapy-line-detail .joinPopup .van-field { align-items: center; min-height: 44px; padding: 7px 0; background: transparent; font-size: 14px; }
#app.therapy-line-detail .joinPopup .van-field { align-items: center; min-height: 44px; padding: 10px 0; background: transparent; font-size: 14px; }
#app.therapy-line-detail .joinPopup .van-cell::after { right: 0; left: 0; border-color: #edf0f5; }
#app.therapy-line-detail .joinPopup .van-field__label { width: 6.2em; margin-right: 12px; color: #58647a; line-height: 28px; }
#app.therapy-line-detail .joinPopup .van-field__body { min-height: 28px; }
#app.therapy-line-detail .joinPopup .van-field__control { color: #172033; line-height: 26px; }
#app.therapy-line-detail .joinPopup .van-field__control::placeholder { color: #9aa2b2; }
#app.therapy-line-detail .joinPopup .van-field__control:not([readonly]):not(.van-field__control--custom),
#app.therapy-line-detail .joinPopup .van-cell--clickable .van-field__body { padding: 0 10px; border: 1px solid #dce1e9; border-radius: 6px; background: #f8f9fb; box-sizing: border-box; }
#app.therapy-line-detail .joinPopup .van-cell--clickable { position: relative; }
#app.therapy-line-detail .joinPopup .van-cell--clickable .van-field__body { padding-right: 26px; }
#app.therapy-line-detail .joinPopup .van-cell__right-icon { position: absolute; right: 8px; top: 50%; margin: 0; transform: translateY(-50%); color: #7c899e; }
#app.therapy-line-detail .joinPopup .van-field__label { width: 84px; margin-right: 12px; color: #596779; line-height: 24px; }
#app.therapy-line-detail .joinPopup .van-field__body { min-height: 24px; }
#app.therapy-line-detail .joinPopup .van-field__control { color: #4f5d70; font-size: 14px; line-height: 24px; }
#app.therapy-line-detail .joinPopup .van-field__control::placeholder { color: #b1bac6; }
#app.therapy-line-detail .joinPopup .van-cell__right-icon { color: #7c899e; }
#app.therapy-line-detail .joinPopup .van-radio { margin-right: 20px; }
#app.therapy-line-detail .joinPopup .van-radio__label { color: #172033; }
#app.therapy-line-detail .joinPopup .van-radio__icon--checked .van-icon { border-color: #006bff; background: #006bff; }
@@ -268,11 +268,10 @@ layout("/layouts/platform_h5.html"){
#app.therapy-line-detail .joinPopup .companion-add .van-icon { font-size: 20px; }
#app.therapy-line-detail .joinPopup .companion-heading { display: flex; align-items: center; justify-content: space-between; margin: 10px 0 4px; padding: 8px 10px; border-radius: 6px; background: #eef3fd; color: #172033; font-size: 14px; line-height: 20px; }
#app.therapy-line-detail .joinPopup .companion-delete { padding: 0; border: 0; background: transparent; color: #ee3535; font: inherit; }
#app.therapy-line-detail .joinPopup .van-tabs__wrap { margin: 10px 0 0; }
#app.therapy-line-detail .joinPopup .single-companion .van-tabs__wrap { display: none; }
#app.therapy-line-detail .joinPopup .van-tabs__nav--card { margin: 0; background: #f3f5f9; }
#app.therapy-line-detail .joinPopup .van-tabs__nav--card .van-tab { color: #69768d; }
#app.therapy-line-detail .joinPopup .van-tabs__nav--card .van-tab--active { background: #006bff; color: #fff; }
#app.therapy-line-detail .joinPopup .companion-selector { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: 12px; }
#app.therapy-line-detail .joinPopup .companion-selector button { min-width: 0; padding: 8px 4px; border: 1px solid #e1e6ee; border-radius: 6px; background: #f3f5f9; color: #69768d; font: inherit; font-size: 13px; line-height: 20px; overflow-wrap: anywhere; }
#app.therapy-line-detail .joinPopup .companion-selector button.is-active { border-color: #006bff; background: #006bff; color: #fff; }
#app.therapy-line-detail .joinPopup .companion-tabs > .van-tabs__wrap { display: none; }
#app.therapy-line-detail .joinPopup .companionList_empty_text { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; min-height: 138px; padding: 12px 0; color: #98a2b5; font-size: 13px; line-height: 22px; }
#app.therapy-line-detail .joinPopup .companionList_empty_text .van-icon { color: #b0bacb; font-size: 36px; }
#app.therapy-line-detail .joinPopup .companion-room-tip { display: flex; align-items: flex-start; gap: 8px; margin: 10px 0; padding: 10px; border-radius: 6px; background: #eef3fd; color: #7c899e; font-size: 12px; line-height: 20px; }
@@ -283,10 +282,8 @@ layout("/layouts/platform_h5.html"){
<div id="app" class="therapy-mobile therapy-line-detail" v-cloak>
<van-sticky>
<van-nav-bar @click-left="historyBack()" fixed left-arrow placeholder
<van-nav-bar @click-left="historyBack()" fixed left-arrow safe-area-inset-top
title="线路详情"></van-nav-bar>
</van-sticky>
<template v-if="isLoad">
<!--线路详情-->
@@ -332,7 +329,7 @@ layout("/layouts/platform_h5.html"){
<a v-if="lineData.contactMobileNumber" :href="'tel:' + lineData.contactMobileNumber" class="detail-call" aria-label="拨打旅行社电话"><van-icon name="phone-o"></van-icon></a>
</div>
<div class="detail-contact-row">
<div><div class="detail-muted">联系人</div><div class="detail-contact-name">{{lineData.principal || '暂无信息'}} <span class="detail-contact-phone">{{lineData.principalContact}}</span></div></div>
<div><div class="detail-muted">联系人</div><div class="detail-contact-name">{{lineData.principal || '暂无信息'}}</div><div class="detail-contact-phone">{{lineData.principalContact}}</div></div>
<a v-if="lineData.principalContact" :href="'tel:' + lineData.principalContact" class="detail-call" aria-label="拨打联系人电话"><van-icon name="phone-o"></van-icon></a>
</div>
</section>
@@ -373,10 +370,10 @@ layout("/layouts/platform_h5.html"){
<!--报名弹出框-->
<van-popup v-model:show="joinPopup" position="right" class="joinPopup">
<van-nav-bar @click-left="signType == '2' ? historyBack() : joinPopup = false" fixed left-arrow placeholder title="疗休养报名"></van-nav-bar>
<van-nav-bar @click-left="signType == '2' ? historyBack() : joinPopup = false" left-arrow safe-area-inset-top title="疗休养报名"></van-nav-bar>
<div class="join_content">
<van-form @submit="joinSubmit">
<van-form @submit="joinSubmit" @failed="handleJoinValidationFailed" validate-first>
<!--基础信息-->
<div class="van-doc-card" style="margin-bottom: 16px">
@@ -387,9 +384,9 @@ layout("/layouts/platform_h5.html"){
<div class="van-card-body">
<van-field label="姓名" readonly v-model="formData.userName"></van-field>
<van-field label="一卡通号" readonly v-model="formData.loginName"></van-field>
<van-field label="身份证件" name="idCard" :rules="[{ required: true }]"
<van-field label="身份证件" name="idCard" :rules="[{ required: true, message: '请填写身份证件' }, { validator: validateIdentityDocument, message: '证件号码格式不正确,请检查后填写' }]"
placeholder="请填写身份证号、护照、台胞证等" v-model="formData.idCard"></van-field>
<van-field label="手机号" name="mobile" :rules="[{ required: true }]"
<van-field label="手机号" name="mobile" type="tel" :rules="[{ required: true, message: '请填写手机号' }, { validator: validateMobile, message: '请填写正确的11位手机号' }]"
placeholder="请填写手机号" v-model="formData.mobile"></van-field>
<van-field label="所属工会" readonly v-model="formData.unionName"></van-field>
<van-field v-if="signType != '2'" label="报名线路" readonly v-model="formData.lineName"></van-field>
@@ -411,8 +408,8 @@ layout("/layouts/platform_h5.html"){
name="bedNum" placeholder="请选择床位" v-model="formData.bedInfo.bedNum"
:rules="[{ required: true }]"></van-field>
<van-field v-if="formData.bedInfo.bedType == '标准间'"
:rules="[{ validator, message: '请选择是否拼房' }]"
label="是否拼房" name="validator">
:rules="[{ required: true, validator: validateBoolean, message: '请选择是否拼房' }]"
label="是否拼房" name="isSleepTogether" :value="formData.bedInfo.isSleepTogether">
<template #input>
<van-radio-group direction="horizontal" v-model="formData.bedInfo.isSleepTogether">
<van-radio :name="true" shape="square">是</van-radio>
@@ -440,20 +437,25 @@ layout("/layouts/platform_h5.html"){
</div>
<template v-if="configData.familyInfo === 2">
<div v-if="formData.companionList.length > 0" class="van-card-body">
<van-tabs v-model="active" type="card" color="#006bff" :class="{'single-companion': formData.companionList.length === 1}" animated>
<div v-if="formData.companionList.length > 1" class="companion-selector" role="group" aria-label="选择随行人">
<button v-for="(item, companionIndex) in formData.companionList" :key="item.id || companionIndex"
type="button" :class="{'is-active': active === companionIndex}"
:aria-pressed="active === companionIndex" @click="active = companionIndex">随行人 {{companionIndex + 1}}</button>
</div>
<van-tabs v-model="active" class="companion-tabs" type="card" color="#006bff" :lazy-render="false" animated>
<van-tab v-for="(item, index) in formData.companionList" :key="item.id || index" :title="'随行人' + (index + 1)">
<div class="companion-heading">
<span>随行人 {{index + 1}}</span>
<button type="button" class="companion-delete" @click="delCompanion">删除</button>
</div>
<van-field label="姓名" name="userName" placeholder="请填写姓名"
<van-field label="姓名" :name="'companion.' + index + '.userName'" placeholder="请填写姓名"
v-model="item.userName"
:rules="[{ required: true }]"></van-field>
:rules="[{ required: true, validator: validateRequiredText, message: '请填写姓名' }]"></van-field>
<!--<van-field label="一卡通号" name="loginName" placeholder="若没有工号,请忽略此项" v-model="item.loginName"></van-field>-->
<van-field label="年龄" name="age" placeholder="请填写年龄" v-model="item.age"
type="digit" :rules="[{ required: true }]"></van-field>
<van-field label="年龄" :name="'companion.' + index + '.age'" placeholder="请填写年龄" v-model="item.age"
type="digit" :rules="[{ required: true, message: '请填写年龄' }, { validator: validateAge, message: '年龄须为0至120之间的整数' }]"></van-field>
<van-field :rules="[{ required: true, message: '请选择性别' }]" label="性别"
name="validator">
:name="'companion.' + index + '.sex'" :value="item.sex">
<template #input>
<van-radio-group direction="horizontal" v-model="item.sex">
<van-radio name="男">男</van-radio>
@@ -461,19 +463,19 @@ layout("/layouts/platform_h5.html"){
</van-radio-group>
</template>
</van-field>
<van-field label="身份证件" name="idCard" placeholder="请填写身份证号、护照、台胞证等"
:rules="[{ required: true }]"
<van-field label="身份证件" :name="'companion.' + index + '.idCard'" placeholder="请填写身份证号、护照、台胞证等"
:rules="[{ required: true, message: '请填写身份证件' }, { validator: validateIdentityDocument, message: '证件号码格式不正确,请检查后填写' }]"
v-model="item.idCard"></van-field>
<van-field label="手机号" name="mobile" placeholder="请填写手机号"
:rules="[{ required: true }]" type="digit"
<van-field label="手机号" :name="'companion.' + index + '.mobile'" placeholder="请填写手机号"
:rules="[{ required: true, message: '请填写手机号' }, { validator: validateMobile, message: '请填写正确的11位手机号' }]" type="tel"
v-model="item.mobile"></van-field>
<van-field @click="companionVisible = true" readonly is-link label="与本人关系"
name="relation"
:name="'companion.' + index + '.relation'"
placeholder="随行人与本人关系" v-model="item.relation"
:rules="[{ required: true }]"></van-field>
:rules="[{ required: true, message: '请选择与本人关系' }]"></van-field>
<van-field @click="self = false; roomVisible = true" readonly is-link label="房间"
name="bedType" placeholder="请选择房间"
:name="'companion.' + index + '.bedType'" placeholder="请选择房间"
v-model="item.bedInfo.bedType"></van-field>
<div class="companion-room-tip">
<van-icon name="info-o"></van-icon>
@@ -482,10 +484,11 @@ layout("/layouts/platform_h5.html"){
<van-field v-if="item.bedInfo.bedType == '标准间'"
@click="self = false; bedVisible = true"
readonly is-link label="床位"
name="bedNum" placeholder="请选择床位"
v-model="item.bedInfo.bedNum"></van-field>
:name="'companion.' + index + '.bedNum'" placeholder="请选择床位"
v-model="item.bedInfo.bedNum" :rules="[{ required: true, message: '请选择床位' }]"></van-field>
<van-field v-if="item.bedInfo.bedType == '标准间'"
:rules="[{ validator, message: '请选择是否拼房' }]" label="是否拼房">
:rules="[{ required: true, validator: validateBoolean, message: '请选择是否拼房' }]" label="是否拼房"
:name="'companion.' + index + '.isSleepTogether'" :value="item.bedInfo.isSleepTogether">
<template #input>
<van-radio-group direction="horizontal"
v-model="item.bedInfo.isSleepTogether">
@@ -496,8 +499,8 @@ layout("/layouts/platform_h5.html"){
</van-field>
<van-field
v-if="item.bedInfo.bedType == '标准间' && item.bedInfo.isSleepTogether == true"
label="意向拼房人" name="otherSleepUser" placeholder="多个拼房人请用,隔开"
v-model="item.bedInfo.otherSleepUser"></van-field>
label="意向拼房人" :name="'companion.' + index + '.otherSleepUser'" placeholder="多个拼房人请用,隔开"
v-model="item.bedInfo.otherSleepUser" :rules="[{ required: true, validator: validateRequiredText, message: '请填写意向拼房人' }]"></van-field>
</van-tab>
</van-tabs>
</div>
@@ -510,7 +513,7 @@ layout("/layouts/platform_h5.html"){
</template>
<template v-else>
<van-field label="家属数量" name="familyNumber" placeholder="请填写家属数量" v-model="formData.familyNumber"
type="digit" :rules="[{ required: true }]"></van-field>
type="digit" :rules="[{ required: true, message: '请填写家属数量' }, { validator: validateFamilyNumber, message: '家属数量须为非负整数' }]"></van-field>
</template>
</div>
@@ -665,8 +668,45 @@ layout("/layouts/platform_h5.html"){
}
this.bedVisible = false
},
validator(value) {
return value != null
validateRequiredText(value) {
return value != null && String(value).trim().length > 0
},
validateMobile(value) {
return /^1[3-9]\d{9}$/.test(String(value == null ? '' : value).trim())
},
validateAge(value) {
const age = String(value == null ? '' : value).trim()
return /^(0|[1-9]\d{0,2})$/.test(age) && Number(age) <= 120
},
validateFamilyNumber(value) {
const count = String(value == null ? '' : value).trim()
return /^(0|[1-9]\d*)$/.test(count) && Number.isSafeInteger(Number(count)) && Number(count) <= 2147483647
},
validateBoolean(value) {
return value === true || value === false
},
validateIdentityDocument(value) {
const document = String(value == null ? '' : value).trim().toUpperCase()
if (/^\d{15}$/.test(document) || /^\d{17}[0-9A-Z]$/.test(document)) {
const legacy = document.length === 15
const birthday = legacy ? '19' + document.slice(6, 12) : document.slice(6, 14)
const date = moment(birthday, 'YYYYMMDD', true)
const sequence = legacy ? document.slice(12, 15) : document.slice(14, 17)
if (!/^[1-9]\d{5}/.test(document) || sequence === '000' || !date.isValid() || date.isAfter(moment(), 'day')) return false
if (legacy) return true
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
const sum = weights.reduce((total, weight, index) => total + Number(document[index]) * weight, 0)
return '10X98765432'[sum % 11] === document[17]
}
// 未区分证件类型,保留护照、台胞证及港澳通行证的号码格式。
return /^(?:\d{8,9}|(?=[A-Z0-9]{6,12}$)(?=.*[A-Z])(?=.*\d)[A-Z0-9]+)$/.test(document)
},
handleJoinValidationFailed({ errors }) {
const error = errors[0]
if (!error) return
const companion = /^companion\.(\d+)\./.exec(error.name || '')
if (companion) this.active = Number(companion[1])
this.$toast(error.message)
},
async joinSubmit() {
const toast = vant.Toast.loading({
@@ -676,11 +716,6 @@ layout("/layouts/platform_h5.html"){
message: '努力提交中',
})
const cloneData = clone(this.formData)
cloneData.companionList.forEach((item, index) => {
if (item.userName === '') {
cloneData.companionList.splice(index, 1)
}
})
let url = ''
if(this.signType == '2') {
cloneData.takePartInTravelAgencyId = this.takePartInTravelAgencyId