南大三十年教职工疗休养

This commit is contained in:
2026-06-25 14:24:29 +08:00
parent 3d0bcabc65
commit 521fc4446b
26 changed files with 2219 additions and 99 deletions
@@ -0,0 +1,7 @@
-- 30年教龄疗休养报名图片材料字段。
-- PC端选择线路、移动端提交报名上传的多张图片统一保存为逗号分隔路径。
ALTER TABLE `thirty_teach_tour_user_assignment`
ADD COLUMN `photoFiles` text DEFAULT NULL COMMENT '报名图片材料' AFTER `boardingPlace`;
ALTER TABLE `thirty_teach_tour_ledger`
ADD COLUMN `photoFiles` text DEFAULT NULL COMMENT '报名图片材料' AFTER `boardingPlace`;
@@ -22,6 +22,7 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_ledger` (
`travelAgencyId` varchar(32) DEFAULT NULL COMMENT '旅行社ID',
`travelAgencyName` varchar(100) DEFAULT NULL COMMENT '旅行社',
`boardingPlace` varchar(100) DEFAULT NULL COMMENT '乘车地点',
`photoFiles` text DEFAULT NULL COMMENT '报名图片材料',
`hasFamily` tinyint(1) DEFAULT 0 COMMENT '是否携带家属',
`intendedRoommate` varchar(100) DEFAULT NULL COMMENT '意向拼床人',
`bedType` varchar(50) DEFAULT NULL COMMENT '床型',
@@ -74,6 +74,42 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_union_quota` (
KEY `idx_thirty_teach_tour_setting_union_quota_union` (`unionId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='疗休养配置分工会名额分配';
CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_travel_period` (
`id` varchar(32) NOT NULL COMMENT 'ID',
`settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID',
`periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称',
`startDate` varchar(10) DEFAULT NULL COMMENT '开始日期',
`endDate` varchar(10) DEFAULT NULL COMMENT '结束日期',
`enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用',
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_thirty_teach_tour_setting_period_setting` (`settingId`),
KEY `idx_thirty_teach_tour_setting_period_date` (`startDate`, `endDate`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段';
CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_period_quota` (
`id` varchar(32) NOT NULL COMMENT 'ID',
`settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID',
`unionId` varchar(32) DEFAULT NULL COMMENT '分工会ID',
`unionName` varchar(100) DEFAULT NULL COMMENT '分工会名称',
`periodId` varchar(32) DEFAULT NULL COMMENT '时间段ID',
`periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称',
`formalQuota` int DEFAULT 0 COMMENT '正式人员名额',
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_thirty_teach_tour_period_quota_setting` (`settingId`),
KEY `idx_thirty_teach_tour_period_quota_union` (`unionId`),
KEY `idx_thirty_teach_tour_period_quota_period` (`periodId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段名额';
-- 疗休养人员分配表。
CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` (
`id` varchar(32) NOT NULL COMMENT 'ID',
@@ -85,6 +121,8 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` (
`lineId` varchar(32) DEFAULT NULL COMMENT '线路ID',
`lineName` varchar(100) DEFAULT NULL COMMENT '线路名称',
`boardingPlace` varchar(100) DEFAULT NULL COMMENT '乘车地点',
`periodId` varchar(32) DEFAULT NULL COMMENT '出行时间段ID',
`periodName` varchar(50) DEFAULT NULL COMMENT '出行时间段名称',
`userId` varchar(32) DEFAULT NULL COMMENT '人员ID',
`loginName` varchar(50) DEFAULT NULL COMMENT '工号',
`userName` varchar(100) DEFAULT NULL COMMENT '姓名',
@@ -112,6 +150,7 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` (
KEY `idx_thirty_teach_tour_user_assignment_setting` (`settingId`),
KEY `idx_thirty_teach_tour_user_assignment_user` (`userId`),
KEY `idx_thirty_teach_tour_user_assignment_union` (`unionId`),
KEY `idx_thirty_teach_tour_user_assignment_period` (`periodId`),
KEY `idx_thirty_teach_tour_user_assignment_source` (`assignSource`),
KEY `idx_thirty_teach_tour_user_assignment_person_type` (`personType`),
KEY `idx_thirty_teach_tour_user_assignment_cancelled` (`cancelled`)
@@ -0,0 +1,35 @@
CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_travel_period` (
`id` varchar(32) NOT NULL COMMENT 'ID',
`settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID',
`periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称',
`startDate` varchar(10) DEFAULT NULL COMMENT '开始日期',
`endDate` varchar(10) DEFAULT NULL COMMENT '结束日期',
`enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用',
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_thirty_teach_tour_setting_period_setting` (`settingId`),
KEY `idx_thirty_teach_tour_setting_period_date` (`startDate`, `endDate`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段';
CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_period_quota` (
`id` varchar(32) NOT NULL COMMENT 'ID',
`settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID',
`unionId` varchar(32) DEFAULT NULL COMMENT '分工会ID',
`unionName` varchar(100) DEFAULT NULL COMMENT '分工会名称',
`periodId` varchar(32) DEFAULT NULL COMMENT '时间段ID',
`periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称',
`formalQuota` int DEFAULT 0 COMMENT '正式人员名额',
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_thirty_teach_tour_period_quota_setting` (`settingId`),
KEY `idx_thirty_teach_tour_period_quota_union` (`unionId`),
KEY `idx_thirty_teach_tour_period_quota_period` (`periodId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段名额';
@@ -0,0 +1,5 @@
-- 分工会正式人员分配按配置时间段占用名额,人员分配表保留所选时间段快照。
ALTER TABLE `thirty_teach_tour_user_assignment`
ADD COLUMN `periodId` varchar(32) DEFAULT NULL COMMENT '出行时间段ID' AFTER `boardingPlace`,
ADD COLUMN `periodName` varchar(50) DEFAULT NULL COMMENT '出行时间段名称' AFTER `periodId`,
ADD KEY `idx_thirty_teach_tour_user_assignment_period` (`periodId`);
@@ -9,6 +9,9 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` (
`lineId` varchar(32) DEFAULT NULL COMMENT '线路ID',
`lineName` varchar(100) DEFAULT NULL COMMENT '线路名称',
`boardingPlace` varchar(100) DEFAULT NULL COMMENT '乘车地点',
`photoFiles` text DEFAULT NULL COMMENT '报名图片材料',
`periodId` varchar(32) DEFAULT NULL COMMENT '出行时间段ID',
`periodName` varchar(50) DEFAULT NULL COMMENT '出行时间段名称',
`userId` varchar(32) DEFAULT NULL COMMENT '人员ID',
`loginName` varchar(50) DEFAULT NULL COMMENT '工号',
`userName` varchar(100) DEFAULT NULL COMMENT '姓名',
@@ -36,6 +39,7 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` (
KEY `idx_thirty_teach_tour_user_assignment_setting` (`settingId`),
KEY `idx_thirty_teach_tour_user_assignment_user` (`userId`),
KEY `idx_thirty_teach_tour_user_assignment_union` (`unionId`),
KEY `idx_thirty_teach_tour_user_assignment_period` (`periodId`),
KEY `idx_thirty_teach_tour_user_assignment_source` (`assignSource`),
KEY `idx_thirty_teach_tour_user_assignment_person_type` (`personType`),
KEY `idx_thirty_teach_tour_user_assignment_cancelled` (`cancelled`)
@@ -69,7 +69,13 @@ layout("/layouts/platform.html"){
<el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="线路" prop="lineName" min-width="200" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="乘车地点" prop="boardingPlace" min-width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="已上传图片数量" width="140" align="center" header-align="center">
<template slot-scope="{row}">
{{ photoFileCount(row) }}
</template>
</el-table-column>
<el-table-column label="人员类型" prop="personType" width="110" sortable="custom" align="center" header-align="center">
<template slot-scope="{row}">
<el-tag size="mini" :type="row.personType === 'BACKUP' ? 'warning' : 'success'">{{ personTypeText(row.personType) }}</el-tag>
@@ -81,9 +87,10 @@ layout("/layouts/platform.html"){
</template>
</el-table-column>
<el-table-column label="分配时间" prop="assignedAt" width="170" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="380" align="center" header-align="center" fixed="right">
<el-table-column label="操作" width="470" align="center" header-align="center" fixed="right">
<template slot-scope="{row}">
<el-button v-if="!row.matterId && row.personType !== 'BACKUP' && $auth.hasPermission('thirtyTeachTour.branchUserAssignment.selectMatter')" size="mini" type="primary" @click="openSelectMatter(row)">选择路线</el-button>
<el-button v-if="$auth.hasPermission('thirtyTeachTour.branchUserAssignment.assign')" size="mini" type="primary" @click="openListPhotoUpload(row)">{{ row.photoFiles ? "已上传" : "上传图片" }}</el-button>
<el-button v-if="$auth.hasPermission('thirtyTeachTour.branchUserAssignment.switchPersonType')" size="mini" type="primary" :loading="personTypeSwitching === row.id" @click="switchPersonType(row, row.personType === 'BACKUP' ? 'FORMAL' : 'BACKUP')">{{ row.personType === 'BACKUP' ? '转为正式' : '转为替补' }}</el-button>
<el-button v-if="isCancelled(row) && $auth.hasPermission('thirtyTeachTour.branchUserAssignment.cancelRestore')" size="mini" type="warning" @click="restoreCancel(row)">取消退出</el-button>
<el-button v-if="$auth.hasPermission('thirtyTeachTour.branchUserAssignment.delete')" size="mini" type="danger" @click="doDelete(row)">删除</el-button>
@@ -129,7 +136,7 @@ layout("/layouts/platform.html"){
</el-select>
</el-form-item>
</el-col>
<el-col>
<el-col :span="6">
<el-form-item label="人员类型" prop="personType">
<el-radio-group v-model="assignForm.personType" @change="assignPersonTypeChange">
<el-radio-button label="FORMAL">正式人员</el-radio-button>
@@ -137,7 +144,22 @@ layout("/layouts/platform.html"){
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-col :span="18" v-if="assignForm.personType === 'FORMAL'">
<el-form-item label="出行时间">
<div class="period-button-list">
<el-button
v-for="item in assignPeriodOptions"
:key="item.id"
size="mini"
:type="assignForm.periodId === item.id ? 'primary' : 'default'"
@click="toggleAssignPeriod(item)">
{{ item.label || item.periodName }}
</el-button>
<span v-if="assignPeriodOptions.length === 0" class="period-empty">暂无启用时间段</span>
</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="quota-bar">
@@ -190,6 +212,13 @@ layout("/layouts/platform.html"){
<el-table-column label="手机号" prop="mobile" width="130" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="所属分工会" prop="unionName" min-width="160" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="所在单位" prop="unitName" min-width="220" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="材料" width="110" align="center" header-align="center">
<template slot-scope="{row}">
<el-button type="text" size="mini" :disabled="!isCandidateSelected(row)" @click="openPhotoUpload(row)">
{{ row.photoFiles ? "已上传" : "上传" }}
</el-button>
</template>
</el-table-column>
</el-table>
<el-row class="el-pagination-container candidate-pagination">
<el-pagination
@@ -209,6 +238,64 @@ layout("/layouts/platform.html"){
</span>
</el-dialog>
<el-dialog
title="上传图片材料"
:visible.sync="photoUploadDialogVisible"
:close-on-click-modal="false"
width="520px"
@closed="resetPhotoUploadDialog">
<el-form :model="photoUploadForm" label-width="100px">
<el-form-item label="人员">
<el-input v-model="photoUploadForm.userName" disabled></el-input>
</el-form-item>
<el-form-item label="图片材料">
<file-upload
style="--upload-width: 96px;--upload-height:96px"
:upload_number="9"
:upload_size="20971520"
:value.sync="photoUploadForm.photoFiles"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="interval">
</file-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="photoUploadDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmPhotoUpload">确定</el-button>
</span>
</el-dialog>
<el-dialog
title="上传图片材料"
:visible.sync="listPhotoDialogVisible"
:close-on-click-modal="false"
width="520px"
@closed="resetListPhotoUploadDialog">
<el-form :model="listPhotoForm" label-width="100px">
<el-form-item label="人员">
<el-input v-model="listPhotoForm.userName" disabled></el-input>
</el-form-item>
<el-form-item label="图片材料">
<file-upload
style="--upload-width: 96px;--upload-height:96px"
:upload_number="9"
:upload_size="20971520"
:value.sync="listPhotoForm.photoFiles"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="interval">
</file-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="listPhotoDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="listPhotoSubmitting" @click="saveListPhotoFiles">保存</el-button>
</span>
</el-dialog>
<el-dialog
title="选择分配路线"
:visible.sync="selectMatterDialogVisible"
@@ -224,6 +311,18 @@ layout("/layouts/platform.html"){
<el-option v-for="item in selectMatterOptions" :key="item.matterId" :label="matterOptionLabel(item)" :value="item.matterId"></el-option>
</el-select>
</el-form-item>
<el-form-item label="图片材料">
<file-upload
style="--upload-width: 96px;--upload-height:96px"
:upload_number="9"
:upload_size="20971520"
:value.sync="selectMatterForm.photoFiles"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="interval">
</file-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="selectMatterDialogVisible = false">取消</el-button>
@@ -272,6 +371,24 @@ layout("/layouts/platform.html"){
color: #606266;
white-space: nowrap;
}
.period-button-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
min-height: 32px;
align-items: center;
}
.period-button-list .el-button + .el-button {
margin-left: 0;
}
.period-empty {
color: #909399;
font-size: 13px;
}
.candidate-pagination {
margin-top: 12px;
margin-bottom: 0;
@@ -311,6 +428,7 @@ layout("/layouts/platform.html"){
pageMatterOptions: [],
assignSettingOptions: [],
assignMatterOptions: [],
assignPeriodOptions: [],
quotaInfo: {
formalQuota: 0,
backupQuota: 0,
@@ -327,9 +445,24 @@ layout("/layouts/platform.html"){
selectedCandidates: [],
selectedCandidateIds: [],
assignSubmitting: false,
photoUploadDialogVisible: false,
photoUploadRow: null,
photoUploadForm: {
userId: "",
userName: "",
photoFiles: ""
},
selectMatterDialogVisible: false,
selectMatterSubmitting: false,
selectMatterOptions: [],
listPhotoDialogVisible: false,
listPhotoSubmitting: false,
listPhotoRow: null,
listPhotoForm: {
id: "",
userName: "",
photoFiles: ""
},
personTypeSwitching: "",
pageForm: {
pageNumber: 1,
@@ -347,6 +480,7 @@ layout("/layouts/platform.html"){
year: currentYear,
settingId: "",
matterId: "",
periodId: "",
personType: "FORMAL"
},
candidateForm: {
@@ -359,7 +493,8 @@ layout("/layouts/platform.html"){
id: "",
settingId: "",
matterId: "",
userName: ""
userName: "",
photoFiles: ""
},
assignRules: {
year: [{required: true, message: "请选择年度", trigger: ["change", "blur"]}],
@@ -377,6 +512,7 @@ layout("/layouts/platform.html"){
year: currentYear || moment().format("YYYY"),
settingId: "",
matterId: "",
periodId: "",
personType: "FORMAL"
}
},
@@ -403,7 +539,8 @@ layout("/layouts/platform.html"){
id: "",
settingId: "",
matterId: "",
userName: ""
userName: "",
photoFiles: ""
}
},
resetSearch() {
@@ -466,6 +603,7 @@ layout("/layouts/platform.html"){
const year = this.pageForm.year || moment().format("YYYY")
this.assignForm = this.defaultAssignForm(year)
this.candidateForm = this.defaultCandidateForm()
this.assignPeriodOptions = []
this.quotaInfo = this.defaultQuotaInfo()
this.candidateData = []
this.selectedCandidates = []
@@ -478,6 +616,7 @@ layout("/layouts/platform.html"){
this.assignForm = this.defaultAssignForm(moment().format("YYYY"))
this.candidateForm = this.defaultCandidateForm()
this.assignMatterOptions = []
this.assignPeriodOptions = []
this.quotaInfo = this.defaultQuotaInfo()
this.candidateData = []
this.selectedCandidates = []
@@ -488,7 +627,9 @@ layout("/layouts/platform.html"){
assignYearChange() {
this.assignForm.settingId = ""
this.assignForm.matterId = ""
this.assignForm.periodId = ""
this.assignMatterOptions = []
this.assignPeriodOptions = []
this.quotaInfo = this.defaultQuotaInfo()
this.selectedCandidateIds = []
this.candidateUserOptions = []
@@ -498,19 +639,22 @@ layout("/layouts/platform.html"){
},
assignSettingChange() {
this.assignForm.matterId = ""
this.assignForm.periodId = ""
this.assignMatterOptions = []
this.assignPeriodOptions = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.candidateForm.keyword = ""
this.clearCandidateSelection()
this.loadAssignMatterOptions()
this.loadQuotaInfo()
this.loadAssignTravelPeriods(true)
this.loadCandidatePageData()
},
assignPersonTypeChange() {
if (this.assignForm.personType === "BACKUP") {
this.assignForm.matterId = ""
}
this.loadQuotaInfo()
},
loadAssignSettingOptions() {
this.$axios.post(loc() + "/settingOptions", {year: this.assignForm.year}).then((res) => {
@@ -520,14 +664,15 @@ layout("/layouts/platform.html"){
if (!this.assignForm.settingId && this.assignSettingOptions.length > 0) {
this.assignForm.settingId = this.assignSettingOptions[0].id
this.loadAssignMatterOptions()
this.loadQuotaInfo()
this.loadAssignTravelPeriods(true)
this.loadCandidatePageData()
} else if (this.assignForm.settingId) {
this.loadAssignMatterOptions()
this.loadQuotaInfo()
this.loadAssignTravelPeriods(true)
this.loadCandidatePageData()
} else {
this.assignMatterOptions = []
this.assignPeriodOptions = []
this.quotaInfo = this.defaultQuotaInfo()
this.candidateData = []
this.candidateForm.totalCount = 0
@@ -535,6 +680,29 @@ layout("/layouts/platform.html"){
}
})
},
loadAssignTravelPeriods(defaultFirst) {
if (!this.assignForm.settingId) {
this.assignPeriodOptions = []
this.assignForm.periodId = ""
this.loadQuotaInfo()
return
}
this.$axios.post(loc() + "/travelPeriodOptions", {settingId: this.assignForm.settingId}).then((res) => {
if (res.code === 0) {
this.assignPeriodOptions = res.data || []
const hasCurrent = this.assignPeriodOptions.some(item => item.id === this.assignForm.periodId)
if ((defaultFirst || !hasCurrent) && this.assignPeriodOptions.length > 0) {
this.assignForm.periodId = this.assignPeriodOptions[0].id
} else if (!hasCurrent) {
this.assignForm.periodId = ""
}
} else {
this.assignPeriodOptions = []
this.assignForm.periodId = ""
}
this.loadQuotaInfo()
})
},
loadAssignMatterOptions() {
if (!this.assignForm.settingId) {
this.assignMatterOptions = []
@@ -553,10 +721,41 @@ layout("/layouts/platform.html"){
}
this.$axios.post(loc() + "/quotaInfo", {settingId: this.assignForm.settingId}).then((res) => {
if (res.code === 0) {
this.quotaInfo = Object.assign(this.defaultQuotaInfo(), res.data || {})
const baseQuota = Object.assign(this.defaultQuotaInfo(), res.data || {})
if (this.assignForm.personType === "FORMAL") {
baseQuota.formalQuota = 0
baseQuota.formalUsed = 0
baseQuota.formalRemaining = 0
}
this.quotaInfo = baseQuota
if (this.assignForm.personType === "FORMAL" && this.assignForm.periodId) {
this.loadPeriodQuotaInfo()
}
}
})
},
loadPeriodQuotaInfo() {
this.$axios.post(loc() + "/periodQuotaInfo", {
settingId: this.assignForm.settingId,
periodId: this.assignForm.periodId
}).then((res) => {
if (res.code === 0) {
const periodQuota = res.data || {}
this.quotaInfo = Object.assign({}, this.quotaInfo, {
formalQuota: periodQuota.formalQuota || 0,
formalUsed: periodQuota.formalUsed || 0,
formalRemaining: periodQuota.formalRemaining || 0
})
}
})
},
toggleAssignPeriod(item) {
this.assignForm.periodId = this.assignForm.periodId === item.id ? "" : item.id
this.candidateForm.pageNumber = 1
this.clearCandidateSelection()
this.loadQuotaInfo()
this.loadCandidatePageData()
},
loadCandidatePageData() {
if (!this.assignForm.settingId) {
this.candidateData = []
@@ -573,7 +772,9 @@ layout("/layouts/platform.html"){
}).then((res) => {
if (res.code === 0) {
const data = res.data || {}
this.candidateData = data.list || []
this.candidateData = (data.list || []).map(item => Object.assign({}, item, {
photoFiles: ""
}))
this.candidateForm.totalCount = data.totalCount || 0
this.mergeCandidateOptions(this.candidateData)
} else {
@@ -654,6 +855,80 @@ layout("/layouts/platform.html"){
})
return Object.keys(map).map(key => map[key])
},
isCandidateSelected(row) {
return !!row && this.selectedCandidates.some(item => item.userId === row.userId)
},
openPhotoUpload(row) {
if (!this.isCandidateSelected(row)) {
this.$message.warning("请先勾选人员")
return
}
this.photoUploadRow = row
// 行级图片只是暂存到候选人员对象中,再次打开时从当前行回显。
this.photoUploadForm = {
userId: row.userId || "",
userName: row.userName || "",
photoFiles: row.photoFiles || ""
}
this.photoUploadDialogVisible = true
},
confirmPhotoUpload() {
if (this.photoUploadRow) {
this.$set(this.photoUploadRow, "photoFiles", this.photoUploadForm.photoFiles || "")
}
this.photoUploadDialogVisible = false
},
resetPhotoUploadDialog() {
this.photoUploadRow = null
this.photoUploadForm = {
userId: "",
userName: "",
photoFiles: ""
}
},
openListPhotoUpload(row) {
this.listPhotoRow = row
// 主列表上传读取当前行 photoFiles;人员分配弹窗已保存过图片时可直接回显。
this.listPhotoForm = {
id: row.id || "",
userName: row.userName || "",
photoFiles: row.photoFiles || ""
}
this.listPhotoDialogVisible = true
},
resetListPhotoUploadDialog() {
this.listPhotoSubmitting = false
this.listPhotoRow = null
this.listPhotoForm = {
id: "",
userName: "",
photoFiles: ""
}
},
saveListPhotoFiles() {
if (!this.listPhotoForm.id) {
this.$message.warning("人员分配记录不存在")
return
}
this.listPhotoSubmitting = true
this.$axios.post(loc() + "/updatePhotoFiles", {
id: this.listPhotoForm.id,
photoFiles: this.listPhotoForm.photoFiles || ""
}).then((res) => {
if (res.code === 0) {
if (this.listPhotoRow) {
this.$set(this.listPhotoRow, "photoFiles", this.listPhotoForm.photoFiles || "")
}
this.$message.success("图片材料保存成功")
this.listPhotoDialogVisible = false
this.doSearch()
} else {
this.$message.warning(res.msg || "图片材料保存失败")
}
}).finally(() => {
this.listPhotoSubmitting = false
})
},
clearCandidateSelection() {
this.selectedCandidates = []
if (this.$refs.candidateTable) {
@@ -667,18 +942,28 @@ layout("/layouts/platform.html"){
this.$message.warning("请选择需要分配的人员")
return
}
if (this.assignForm.personType === "FORMAL" && !this.assignForm.periodId) {
this.$message.warning("请选择出行时间")
return
}
this.$confirm("确定保存当前人员分配吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
const userIds = this.selectedCandidates.map(item => item.userId)
const assignItems = this.selectedCandidates.map(item => {
return {
userId: item.userId,
photoFiles: item.photoFiles || ""
}
})
this.assignSubmitting = true
this.$axios.post(loc() + "/doAssign", {
settingId: this.assignForm.settingId,
matterId: this.assignForm.personType === "BACKUP" ? "" : this.assignForm.matterId,
periodId: this.assignForm.personType === "BACKUP" ? "" : this.assignForm.periodId,
personType: this.assignForm.personType,
userIds: JSON.stringify(userIds)
assignItems: JSON.stringify(assignItems)
}).then((res) => {
if (res.code === 0) {
const data = res.data || {}
@@ -715,7 +1000,8 @@ layout("/layouts/platform.html"){
id: row.id,
settingId: row.settingId,
matterId: "",
userName: row.userName || ""
userName: row.userName || "",
photoFiles: ""
}
this.selectMatterOptions = []
this.selectMatterDialogVisible = true
@@ -743,7 +1029,8 @@ layout("/layouts/platform.html"){
this.selectMatterSubmitting = true
this.$axios.post(loc() + "/selectMatter", {
id: this.selectMatterForm.id,
matterId: this.selectMatterForm.matterId
matterId: this.selectMatterForm.matterId,
photoFiles: this.selectMatterForm.photoFiles
}).then((res) => {
if (res.code === 0) {
const data = res.data || {}
@@ -792,6 +1079,12 @@ layout("/layouts/platform.html"){
isCancelled(row) {
return row && (row.cancelled === true || row.cancelled === 1)
},
photoFileCount(row) {
if (!row || !row.photoFiles) {
return 0
}
return String(row.photoFiles).split(",").map(item => item.trim()).filter(item => item).length
},
restoreCancel(row) {
this.$confirm("确定将【" + row.userName + "】恢复为未退出状态吗?", "提示", {
confirmButtonText: "确定",
@@ -88,6 +88,11 @@ layout("/layouts/platform.html"){
<el-table-column label="线路" prop="lineName" min-width="200" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="乘车地点" prop="boardingPlace" min-width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="已上传图片数量" width="140" align="center" header-align="center">
<template slot-scope="{row}">
{{ photoFileCount(row) }}
</template>
</el-table-column>
<el-table-column label="分配类别" prop="assignSource" width="120" sortable="custom" align="center" header-align="center">
<template slot-scope="{row}">
<el-tag size="mini" :type="row.assignSource === 'BRANCH_UNION' ? 'warning' : 'primary'">{{ assignSourceText(row.assignSource) }}</el-tag>
@@ -104,9 +109,10 @@ layout("/layouts/platform.html"){
</template>
</el-table-column>
<el-table-column label="分配时间" prop="assignedAt" width="170" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="320" align="center" header-align="center" fixed="right">
<el-table-column label="操作" width="410" align="center" header-align="center" fixed="right">
<template slot-scope="{row}">
<el-button v-if="isSchoolUnionAssignment(row) && !row.matterId && row.personType !== 'BACKUP'" size="mini" type="primary" @click="openSelectMatter(row)">选择线路</el-button>
<el-button v-if="isSchoolUnionAssignment(row)" size="mini" type="primary" @click="openListPhotoUpload(row)">{{ row.photoFiles ? "已上传" : "上传图片" }}</el-button>
<el-button v-if="isSchoolUnionAssignment(row) && isCancelled(row) && $auth.hasPermission('thirtyTeachTour.schoolUserAssignment.cancelRestore')" size="mini" type="warning" @click="restoreCancel(row)">取消退出</el-button>
<el-button v-if="isSchoolUnionAssignment(row)" size="mini" type="danger" @click="doDelete(row)">删除</el-button>
</template>
@@ -189,6 +195,14 @@ layout("/layouts/platform.html"){
<el-select v-model="candidateForm.unionId" clearable filterable placeholder="所属分工会" style="width: 220px" @change="candidateSearch">
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-select v-model="candidateForm.summerJoined" clearable placeholder="今年是否已参加暑期疗休养" style="width: 230px" @change="candidateSearch">
<el-option label="否" value="false"></el-option>
<el-option label="是" value="true"></el-option>
</el-select>
<el-select v-model="candidateForm.lastThirtyJoined" clearable placeholder="去年是否参加30年教龄疗休养" style="width: 250px" @change="candidateSearch">
<el-option label="否" value="false"></el-option>
<el-option label="是" value="true"></el-option>
</el-select>
<el-select
v-model="selectedCandidateIds"
multiple
@@ -238,6 +252,13 @@ layout("/layouts/platform.html"){
</el-select>
</template>
</el-table-column>
<el-table-column label="材料" width="110" align="center" header-align="center">
<template slot-scope="{row}">
<el-button type="text" size="mini" :disabled="!isCandidateSelected(row)" @click="openPhotoUpload(row)">
{{ row.photoFiles ? "已上传" : "上传" }}
</el-button>
</template>
</el-table-column>
</el-table>
<el-row class="el-pagination-container candidate-pagination">
<el-pagination
@@ -257,6 +278,64 @@ layout("/layouts/platform.html"){
</span>
</el-dialog>
<el-dialog
title="上传图片材料"
:visible.sync="photoUploadDialogVisible"
:close-on-click-modal="false"
width="520px"
@closed="resetPhotoUploadDialog">
<el-form :model="photoUploadForm" label-width="100px">
<el-form-item label="人员">
<el-input v-model="photoUploadForm.userName" disabled></el-input>
</el-form-item>
<el-form-item label="图片材料">
<file-upload
style="--upload-width: 96px;--upload-height:96px"
:upload_number="9"
:upload_size="20971520"
:value.sync="photoUploadForm.photoFiles"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="interval">
</file-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="photoUploadDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmPhotoUpload">确定</el-button>
</span>
</el-dialog>
<el-dialog
title="上传图片材料"
:visible.sync="listPhotoDialogVisible"
:close-on-click-modal="false"
width="520px"
@closed="resetListPhotoUploadDialog">
<el-form :model="listPhotoForm" label-width="100px">
<el-form-item label="人员">
<el-input v-model="listPhotoForm.userName" disabled></el-input>
</el-form-item>
<el-form-item label="图片材料">
<file-upload
style="--upload-width: 96px;--upload-height:96px"
:upload_number="9"
:upload_size="20971520"
:value.sync="listPhotoForm.photoFiles"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="interval">
</file-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="listPhotoDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="listPhotoSubmitting" @click="saveListPhotoFiles">保存</el-button>
</span>
</el-dialog>
<el-dialog
title="选择分配线路"
:visible.sync="selectMatterDialogVisible"
@@ -272,6 +351,18 @@ layout("/layouts/platform.html"){
<el-option v-for="item in selectMatterOptions" :key="item.matterId" :label="matterOptionLabel(item)" :value="item.matterId"></el-option>
</el-select>
</el-form-item>
<el-form-item label="图片材料">
<file-upload
style="--upload-width: 96px;--upload-height:96px"
:upload_number="9"
:upload_size="20971520"
:value.sync="selectMatterForm.photoFiles"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="interval">
</file-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="selectMatterDialogVisible = false">取消</el-button>
@@ -350,9 +441,24 @@ layout("/layouts/platform.html"){
selectedCandidates: [],
selectedCandidateIds: [],
assignSubmitting: false,
photoUploadDialogVisible: false,
photoUploadRow: null,
photoUploadForm: {
userId: "",
userName: "",
photoFiles: ""
},
selectMatterDialogVisible: false,
selectMatterSubmitting: false,
selectMatterOptions: [],
listPhotoDialogVisible: false,
listPhotoSubmitting: false,
listPhotoRow: null,
listPhotoForm: {
id: "",
userName: "",
photoFiles: ""
},
pageForm: {
pageNumber: 1,
pageSize: 10,
@@ -378,13 +484,16 @@ layout("/layouts/platform.html"){
pageSize: 10,
totalCount: 0,
unionId: "",
summerJoined: "false",
lastThirtyJoined: "false",
keyword: ""
},
selectMatterForm: {
id: "",
settingId: "",
matterId: "",
userName: ""
userName: "",
photoFiles: ""
},
remindForm: {
content: ""
@@ -415,6 +524,8 @@ layout("/layouts/platform.html"){
pageSize: 10,
totalCount: 0,
unionId: "",
summerJoined: "false",
lastThirtyJoined: "false",
keyword: ""
}
},
@@ -423,7 +534,8 @@ layout("/layouts/platform.html"){
id: "",
settingId: "",
matterId: "",
userName: ""
userName: "",
photoFiles: ""
}
},
defaultRemindForm() {
@@ -649,13 +761,16 @@ layout("/layouts/platform.html"){
pageSize: this.candidateForm.pageSize,
settingId: this.assignForm.settingId,
unionId: this.candidateForm.unionId,
summerJoined: this.candidateForm.summerJoined,
lastThirtyJoined: this.candidateForm.lastThirtyJoined,
keyword: (this.selectedCandidateIds || []).length > 0 ? "" : this.candidateForm.keyword,
userIds: JSON.stringify(this.selectedCandidateIds || [])
}).then((res) => {
if (res.code === 0) {
const data = res.data || {}
this.candidateData = (data.list || []).map(item => Object.assign({}, item, {
assignmentMatterId: ""
assignmentMatterId: "",
photoFiles: ""
}))
this.candidateForm.totalCount = data.totalCount || 0
this.mergeCandidateOptions(this.candidateData)
@@ -678,6 +793,8 @@ layout("/layouts/platform.html"){
},
resetCandidateSearch() {
this.candidateForm.unionId = ""
this.candidateForm.summerJoined = "false"
this.candidateForm.lastThirtyJoined = "false"
this.candidateForm.keyword = ""
this.candidateForm.pageNumber = 1
this.selectedCandidateIds = []
@@ -723,6 +840,8 @@ layout("/layouts/platform.html"){
pageSize: 20,
settingId: this.assignForm.settingId,
unionId: this.candidateForm.unionId,
summerJoined: this.candidateForm.summerJoined,
lastThirtyJoined: this.candidateForm.lastThirtyJoined,
keyword: keyword || ""
}).then((res) => {
if (res.code === 0) {
@@ -758,6 +877,77 @@ layout("/layouts/platform.html"){
isCandidateSelected(row) {
return !!row && this.selectedCandidates.some(item => item.userId === row.userId)
},
openPhotoUpload(row) {
if (!this.isCandidateSelected(row)) {
this.$message.warning("请先勾选人员")
return
}
this.photoUploadRow = row
// 行级图片只是暂存到候选人员对象中,再次打开时从当前行回显。
this.photoUploadForm = {
userId: row.userId || "",
userName: row.userName || "",
photoFiles: row.photoFiles || ""
}
this.photoUploadDialogVisible = true
},
confirmPhotoUpload() {
if (this.photoUploadRow) {
this.$set(this.photoUploadRow, "photoFiles", this.photoUploadForm.photoFiles || "")
}
this.photoUploadDialogVisible = false
},
resetPhotoUploadDialog() {
this.photoUploadRow = null
this.photoUploadForm = {
userId: "",
userName: "",
photoFiles: ""
}
},
openListPhotoUpload(row) {
this.listPhotoRow = row
// 主列表上传读取当前行 photoFiles;人员分配弹窗已保存过图片时可直接回显。
this.listPhotoForm = {
id: row.id || "",
userName: row.userName || "",
photoFiles: row.photoFiles || ""
}
this.listPhotoDialogVisible = true
},
resetListPhotoUploadDialog() {
this.listPhotoSubmitting = false
this.listPhotoRow = null
this.listPhotoForm = {
id: "",
userName: "",
photoFiles: ""
}
},
saveListPhotoFiles() {
if (!this.listPhotoForm.id) {
this.$message.warning("人员分配记录不存在")
return
}
this.listPhotoSubmitting = true
this.$axios.post(loc() + "/updatePhotoFiles", {
id: this.listPhotoForm.id,
photoFiles: this.listPhotoForm.photoFiles || ""
}).then((res) => {
if (res.code === 0) {
if (this.listPhotoRow) {
this.$set(this.listPhotoRow, "photoFiles", this.listPhotoForm.photoFiles || "")
}
this.$message.success("图片材料保存成功")
this.listPhotoDialogVisible = false
this.doSearch()
} else {
this.$message.warning(res.msg || "图片材料保存失败")
}
}).finally(() => {
this.listPhotoSubmitting = false
})
},
clearCandidateSelection() {
this.selectedCandidates = []
if (this.$refs.candidateTable) {
@@ -779,7 +969,8 @@ layout("/layouts/platform.html"){
const assignItems = this.selectedCandidates.map(item => {
return {
userId: item.userId,
matterId: item.assignmentMatterId || this.assignForm.matterId || ""
matterId: item.assignmentMatterId || this.assignForm.matterId || "",
photoFiles: item.photoFiles || ""
}
})
this.assignSubmitting = true
@@ -806,7 +997,8 @@ layout("/layouts/platform.html"){
id: row.id,
settingId: row.settingId,
matterId: "",
userName: row.userName || ""
userName: row.userName || "",
photoFiles: ""
}
this.selectMatterOptions = []
this.selectMatterDialogVisible = true
@@ -834,7 +1026,8 @@ layout("/layouts/platform.html"){
this.selectMatterSubmitting = true
this.$axios.post(loc() + "/selectMatter", {
id: this.selectMatterForm.id,
matterId: this.selectMatterForm.matterId
matterId: this.selectMatterForm.matterId,
photoFiles: this.selectMatterForm.photoFiles
}).then((res) => {
if (res.code === 0) {
const data = res.data || {}
@@ -895,6 +1088,12 @@ layout("/layouts/platform.html"){
isCancelled(row) {
return row && (row.cancelled === true || row.cancelled === 1)
},
photoFileCount(row) {
if (!row || !row.photoFiles) {
return 0
}
return String(row.photoFiles).split(",").map(item => item.trim()).filter(item => item).length
},
restoreCancel(row) {
this.$confirm("确定将【" + row.userName + "】恢复为未退出状态吗?", "提示", {
confirmButtonText: "确定",
@@ -347,7 +347,7 @@ layout("/layouts/platform.html"){
<strong>{{ branchTotalQuota }}</strong>
</div>
<div class="tour-quota-stat">
<span>分工会总会员</span>
<span>分工会可参加总人</span>
<strong>{{ branchTotalMemberCount }}</strong>
</div>
<el-input-number
@@ -371,7 +371,7 @@ layout("/layouts/platform.html"){
style="width: 100%">
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></el-table-column>
<el-table-column label="分工会" prop="unionName" min-width="220" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="当前会员数" prop="memberCount" width="120" align="center" header-align="center"></el-table-column>
<el-table-column label="可参加人数" prop="memberCount" width="120" align="center" header-align="center"></el-table-column>
<el-table-column label="正式人员数量" width="170" align="center" header-align="center">
<template slot-scope="{row}">
<el-input-number v-model="row.formalQuota" :controls="false" :min="0" :precision="0" style="width: 100%"></el-input-number>
@@ -385,6 +385,120 @@ layout("/layouts/platform.html"){
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="时间段管理" name="travelPeriod">
<div class="tour-tab-fill">
<el-alert
title="时间段用于划分报名的时间范围,保存后将应用于时间段名额分配。"
type="info"
show-icon
:closable="false"
class="tour-tab-alert">
</el-alert>
<div class="tour-tab-toolbar tour-tab-toolbar-left">
<el-button type="primary" size="medium" icon="el-icon-plus" @click="openPeriodDialog()">新增时间段</el-button>
</div>
<el-table
:data="formData.travelPeriods"
border
class="vi-table tour-tab-table"
empty-text="暂无时间段"
height="100%"
style="width: 100%">
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></el-table-column>
<el-table-column label="时间段名称" prop="periodName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="开始日期" prop="startDate" width="150" align="center" header-align="center"></el-table-column>
<el-table-column label="结束日期" prop="endDate" width="150" align="center" header-align="center"></el-table-column>
<el-table-column label="状态" prop="enabled" width="100" align="center" header-align="center">
<template slot-scope="{row}">
<el-tag size="mini" type="success" v-if="row.enabled">启用</el-tag>
<el-tag size="mini" type="info" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createdAt" width="170" align="center" header-align="center">
<template slot-scope="{row}">
{{ formatPeriodCreatedAt(row.createdAt) }}
</template>
</el-table-column>
<el-table-column label="操作" width="130" align="center" header-align="center">
<template slot-scope="scope">
<el-button type="text" size="mini" @click="openPeriodDialog(scope.row, scope.$index)">编辑</el-button>
<el-button type="text" size="mini" class="text-danger" @click="deleteTravelPeriod(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="时间段名额分配" name="periodQuota">
<div class="tour-tab-fill">
<el-alert
title="按时间段分配各分工会的可用名额,所有时间段名额之和不能超过分工会的正式名额。"
type="info"
show-icon
:closable="false"
class="tour-tab-alert">
</el-alert>
<div class="tour-period-quota-toolbar">
<el-select v-model="periodQuotaUnionId" clearable filterable placeholder="全部分工会" size="medium" class="tour-period-quota-union">
<el-option
v-for="item in formData.unionQuotas"
:key="item.unionId"
:label="item.unionName"
:value="item.unionId">
</el-option>
</el-select>
<el-date-picker
v-model="periodQuotaDateRange"
type="daterange"
value-format="yyyy-MM-dd"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
size="medium">
</el-date-picker>
<el-button type="primary" size="medium" @click="checkPeriodQuotaMessage">批量检查</el-button>
</div>
<el-table
:data="periodQuotaMatrixRows"
border
class="vi-table tour-tab-table tour-period-quota-table"
empty-text="暂无分工会名额"
height="100%"
show-summary
:summary-method="periodQuotaSummary"
style="width: 100%">
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></el-table-column>
<el-table-column label="分工会" prop="unionName" min-width="200" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="总名额" prop="formalQuota" width="110" align="center" header-align="center"></el-table-column>
<el-table-column label="时间段名额分配(可用名额)" align="center" header-align="center">
<el-table-column
v-for="period in periodQuotaDisplayPeriods"
:key="period.id"
:label="period.startDate + ' ~ ' + period.endDate"
min-width="230"
align="center"
header-align="center">
<template slot-scope="{row}">
<div class="tour-period-quota-cell">
<el-input
:value="periodQuotaValue(row.unionId, period.id)"
maxlength="10"
@input="setPeriodQuotaValue(row, period, $event)">
</el-input>
<span>可用:{{ periodQuotaRemaining(row.unionId) }}</span>
</div>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="已分配合计" width="150" align="center" header-align="center">
<template slot-scope="{row}">
<span :class="periodQuotaRowTotal(row.unionId) > toNonNegativeInteger(row.formalQuota) ? 'text-danger' : 'text-success'">
{{ periodQuotaRowTotal(row.unionId) }} / {{ toNonNegativeInteger(row.formalQuota) }}
</span>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="服务须知" name="notice">
<el-form-item prop="serviceNotice" label-width="0">
<text-editor v-model="formData.serviceNotice"></text-editor>
@@ -397,6 +511,46 @@ layout("/layouts/platform.html"){
<el-button type="primary" :loading="submitLoading" @click="doSubmit">确认</el-button>
</span>
</el-dialog>
<el-dialog
:title="periodDialogTitle"
:visible.sync="periodDialogVisible"
:close-on-click-modal="false"
width="520px"
append-to-body>
<el-form :model="periodForm" :rules="periodRules" label-width="110px" ref="periodForm">
<el-form-item label="时间段名称" prop="periodName">
<el-input v-model="periodForm.periodName" maxlength="20" show-word-limit placeholder="请输入时间段名称"></el-input>
</el-form-item>
<el-form-item label="开始日期" prop="startDate">
<el-date-picker
v-model="periodForm.startDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择开始日期"
style="width: 100%">
</el-date-picker>
</el-form-item>
<el-form-item label="结束日期" prop="endDate">
<el-date-picker
v-model="periodForm.endDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择结束日期"
style="width: 100%">
</el-date-picker>
</el-form-item>
<el-form-item label="状态" prop="enabled">
<el-radio-group v-model="periodForm.enabled">
<el-radio :label="true">启用</el-radio>
<el-radio :label="false">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="periodDialogVisible = false">取消</el-button>
<el-button type="primary" @click="savePeriodDialog">确定</el-button>
</span>
</el-dialog>
<drawer-user-scope
ref="drawerUserScope"
:group_id.sync="formData.activityGroupId"
@@ -456,6 +610,18 @@ layout("/layouts/platform.html"){
quotaAllocate: {
ratio: null
},
periodQuotaUnionId: "",
periodQuotaDateRange: [],
periodDialogVisible: false,
periodDialogTitle: "新增时间段",
periodEditIndex: -1,
periodForm: {
id: "",
periodName: "",
startDate: "",
endDate: "",
enabled: true
},
inheritLoading: false,
formRules: {
year: [{required: true, message: "必填", trigger: ["blur", "change"]}],
@@ -466,6 +632,11 @@ layout("/layouts/platform.html"){
outProvinceFixedPeople: [{validator: checkOutProvinceFixedPeople, trigger: ["blur", "change"]}],
cycleStartYear: [{validator: checkCycleYear, trigger: ["blur", "change"]}],
cycleEndYear: [{validator: checkCycleYear, trigger: ["blur", "change"]}]
},
periodRules: {
periodName: [{required: true, message: "必填", trigger: ["blur", "change"]}],
startDate: [{required: true, message: "必填", trigger: ["blur", "change"]}],
endDate: [{required: true, message: "必填", trigger: ["blur", "change"]}]
}
}
},
@@ -483,11 +654,48 @@ layout("/layouts/platform.html"){
branchTotalQuota() {
const quota = this.travelPeopleQuota - this.toNonNegativeInteger(this.quotaOverview.schoolFormalAssignedCount)
return quota > 0 ? quota : 0
},
enabledTravelPeriods() {
const rows = this.formData.travelPeriods || []
return rows.filter((row) => {
return row && row.id && row.enabled
}).sort((a, b) => {
return String(a.startDate || "").localeCompare(String(b.startDate || ""))
})
},
periodQuotaDisplayPeriods() {
const range = this.periodQuotaDateRange || []
return this.enabledTravelPeriods.filter((period) => {
if (!range.length || !range[0] || !range[1]) {
return true
}
return String(period.endDate || "") >= range[0] && String(period.startDate || "") <= range[1]
})
},
periodQuotaMatrixRows() {
const rows = this.formData.unionQuotas || []
return rows.filter((row) => {
return !this.periodQuotaUnionId || row.unionId === this.periodQuotaUnionId
}).map((row) => {
return Object.assign({}, row, {
formalQuota: this.toNonNegativeInteger(row.formalQuota)
})
})
}
},
watch: {
"formData.travelPeopleQuota": function() {
this.refreshQuotaRatio()
},
"formData.year": function() {
if (this.dialogVisible) {
this.reloadUnionQuotaRows()
}
},
"formData.activityGroupId": function() {
if (this.dialogVisible) {
this.reloadUnionQuotaRows()
}
}
},
methods: {
@@ -502,15 +710,22 @@ layout("/layouts/platform.html"){
})
},
loadUnionQuotaRows(settingId) {
this.$axios.post(loc() + "/unionQuotaRows", {settingId: settingId || ""}).then((res) => {
this.$axios.post(loc() + "/unionQuotaRows", {
settingId: settingId || "",
year: this.formData.year || "",
activityGroupId: this.formData.activityGroupId || ""
}).then((res) => {
if (res.code === 0) {
this.$set(this.formData, "unionQuotas", this.normalizeUnionQuotaRows(res.data || []))
this.$set(this.formData, "unionQuotas", this.mergeUnionQuotaRows(res.data || []))
this.refreshQuotaRatio()
} else {
this.$message.warning(res.msg || "查询分工会名额失败")
}
})
},
reloadUnionQuotaRows() {
this.loadUnionQuotaRows(this.formData.id || "")
},
loadUnionQuotaOverview(settingId) {
this.$axios.post(loc() + "/unionQuotaOverview", {settingId: settingId || ""}).then((res) => {
if (res.code === 0) {
@@ -531,6 +746,50 @@ layout("/layouts/platform.html"){
})
})
},
normalizeTravelPeriods(rows) {
return (rows || []).map((item) => {
return Object.assign({}, item, {
id: item.id || this.newClientId(),
periodName: item.periodName || "",
startDate: item.startDate || "",
endDate: item.endDate || "",
enabled: item.enabled === undefined || item.enabled === null ? true : !!item.enabled
})
})
},
normalizePeriodQuotas(rows) {
return (rows || []).map((item) => {
return Object.assign({}, item, {
formalQuota: this.toNonNegativeInteger(item.formalQuota)
})
}).filter((item) => {
return item.unionId && item.periodId
})
},
mergeUnionQuotaRows(rows) {
const currentRows = this.formData.unionQuotas || []
const currentQuotaMap = {}
currentRows.forEach((row) => {
if (row && row.unionId) {
currentQuotaMap[row.unionId] = row
}
})
// 刷新可参加人数时只更新统计口径,保留页面上已继承或已录入的正式/替补名额。
return this.normalizeUnionQuotaRows(rows).map((row) => {
const current = currentQuotaMap[row.unionId]
if (!current) {
return row
}
return Object.assign({}, row, {
formalQuota: this.toNonNegativeInteger(current.formalQuota),
backupQuota: this.toNonNegativeInteger(current.backupQuota)
})
})
},
newClientId() {
const source = String(Date.now()) + String(Math.floor(Math.random() * 100000000000000000))
return (source + "00000000000000000000000000000000").substring(0, 32)
},
toNonNegativeInteger(value) {
const numberValue = parseInt(value, 10)
if (isNaN(numberValue) || numberValue < 0) {
@@ -538,6 +797,148 @@ layout("/layouts/platform.html"){
}
return numberValue
},
formatPeriodCreatedAt(value) {
if (!value) {
return ""
}
if (String(value).length === 13) {
return moment(Number(value)).format("YYYY-MM-DD HH:mm:ss")
}
if (String(value).length === 10) {
return moment(Number(value) * 1000).format("YYYY-MM-DD HH:mm:ss")
}
return value
},
openPeriodDialog(row, index) {
this.periodDialogTitle = row ? "编辑时间段" : "新增时间段"
this.periodEditIndex = row ? index : -1
this.periodForm = Object.assign({
id: this.newClientId(),
periodName: "",
startDate: "",
endDate: "",
enabled: true
}, row || {})
this.periodDialogVisible = true
this.$nextTick(() => this.$refs.periodForm && this.$refs.periodForm.clearValidate())
},
savePeriodDialog() {
this.$refs.periodForm.validate((valid) => {
if (!valid) return
if (this.periodForm.startDate > this.periodForm.endDate) {
this.$message.warning("开始日期不能大于结束日期")
return
}
if (!this.formData.travelPeriods) {
this.$set(this.formData, "travelPeriods", [])
}
const row = Object.assign({}, this.periodForm, {
id: this.periodForm.id || this.newClientId(),
enabled: !!this.periodForm.enabled
})
if (this.periodEditIndex >= 0) {
this.$set(this.formData.travelPeriods, this.periodEditIndex, row)
} else {
this.formData.travelPeriods.push(row)
}
this.periodDialogVisible = false
})
},
deleteTravelPeriod(index, row) {
this.$confirm("确定删除该时间段吗?删除后对应的时间段名额也会移除。", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.formData.travelPeriods.splice(index, 1)
this.formData.periodQuotas = (this.formData.periodQuotas || []).filter((item) => {
return item.periodId !== row.id
})
})
},
periodQuotaKey(unionId, periodId) {
return String(unionId || "") + "_" + String(periodId || "")
},
findPeriodQuota(unionId, periodId) {
const key = this.periodQuotaKey(unionId, periodId)
const rows = this.formData.periodQuotas || []
return rows.find((item) => {
return this.periodQuotaKey(item.unionId, item.periodId) === key
})
},
periodQuotaValue(unionId, periodId) {
const row = this.findPeriodQuota(unionId, periodId)
return row ? this.toNonNegativeInteger(row.formalQuota) : 0
},
periodEnabledMap() {
const result = {}
;(this.formData.travelPeriods || []).forEach((period) => {
if (period && period.id && period.enabled) {
result[period.id] = true
}
})
return result
},
setPeriodQuotaValue(row, period, value) {
const nextValue = this.toNonNegativeInteger(String(value || "").replace(/[^\d]/g, ""))
if (!this.formData.periodQuotas) {
this.$set(this.formData, "periodQuotas", [])
}
const existing = this.findPeriodQuota(row.unionId, period.id)
if (existing) {
this.$set(existing, "formalQuota", nextValue)
this.$set(existing, "periodName", period.periodName || "")
this.$set(existing, "unionName", row.unionName || "")
} else {
this.formData.periodQuotas.push({
unionId: row.unionId,
unionName: row.unionName,
periodId: period.id,
periodName: period.periodName,
formalQuota: nextValue
})
}
},
periodQuotaRowTotal(unionId) {
const rows = this.formData.periodQuotas || []
const periodMap = this.periodEnabledMap()
return rows.filter((item) => {
return item.unionId === unionId && periodMap[item.periodId]
}).reduce((sum, item) => {
return sum + this.toNonNegativeInteger(item.formalQuota)
}, 0)
},
periodQuotaRemaining(unionId) {
const unionRow = (this.formData.unionQuotas || []).find((item) => item.unionId === unionId)
const formalQuota = unionRow ? this.toNonNegativeInteger(unionRow.formalQuota) : 0
const remaining = formalQuota - this.periodQuotaRowTotal(unionId)
return remaining > 0 ? remaining : 0
},
buildPeriodQuotasForSubmit() {
const periodMap = {}
;(this.formData.travelPeriods || []).forEach((period) => {
if (period && period.id && period.enabled) {
periodMap[period.id] = period
}
})
const unionMap = {}
;(this.formData.unionQuotas || []).forEach((row) => {
if (row && row.unionId) {
unionMap[row.unionId] = row
}
})
return (this.formData.periodQuotas || []).filter((item) => {
return item && item.unionId && item.periodId && periodMap[item.periodId] && this.toNonNegativeInteger(item.formalQuota) > 0
}).map((item) => {
const period = periodMap[item.periodId] || {}
const unionRow = unionMap[item.unionId] || {}
return Object.assign({}, item, {
unionName: unionRow.unionName || item.unionName || "",
periodName: period.periodName || item.periodName || "",
formalQuota: this.toNonNegativeInteger(item.formalQuota)
})
})
},
toNumber(value) {
const numberValue = parseFloat(value)
return isNaN(numberValue) ? null : numberValue
@@ -572,6 +973,33 @@ layout("/layouts/platform.html"){
return ""
})
},
periodQuotaSummary(param) {
const columns = param.columns || []
const rows = this.periodQuotaMatrixRows || []
return columns.map((column, index) => {
if (index === 0) {
return "合计"
}
if (column.property === "formalQuota") {
return rows.reduce((sum, row) => sum + this.toNonNegativeInteger(row.formalQuota), 0)
}
if (index >= 3 && index < 3 + this.periodQuotaDisplayPeriods.length) {
const period = this.periodQuotaDisplayPeriods[index - 3]
return rows.reduce((sum, row) => sum + this.periodQuotaValue(row.unionId, period.id), 0)
}
if (column.label === "已分配合计") {
const total = rows.reduce((sum, row) => sum + this.periodQuotaRowTotal(row.unionId), 0)
const quota = rows.reduce((sum, row) => sum + this.toNonNegativeInteger(row.formalQuota), 0)
return total + " / " + quota
}
return ""
})
},
checkPeriodQuotaMessage() {
if (this.validatePeriodQuotas()) {
this.$message.success("时间段名额校验通过")
}
},
allocateQuotaByRatio() {
const rows = this.formData.unionQuotas || []
const ratio = this.toNumber(this.quotaAllocate.ratio)
@@ -587,10 +1015,11 @@ layout("/layouts/platform.html"){
this.$message.warning("比例不能为负数")
return
}
// 按比例分配:比例 * 分工会会员数,小数直接舍去,不补余数。
// 按比例分配:比例 * 分工会可参加人数,小数直接舍去;单个分工会结果不能超过本分工会可参加人数。
const nextFormalQuotas = rows.map(row => {
const memberCount = this.toNonNegativeInteger(row.memberCount)
return Math.floor(ratio * memberCount)
const formalQuota = Math.floor(ratio * memberCount)
return Math.min(formalQuota, memberCount)
})
const formalTotal = nextFormalQuotas.reduce((sum, value) => sum + value, 0)
if (formalTotal > this.branchTotalQuota) {
@@ -607,11 +1036,11 @@ layout("/layouts/platform.html"){
this.$refs.drawerUserScope.userScopeDialog = true
}
},
async handleActivityGroupChange() {
handleActivityGroupChange() {
if (this.formData.activityGroupId !== null && this.formData.activityGroupId !== undefined) {
this.formData.activityGroupId = String(this.formData.activityGroupId)
}
await this.getActivityGroup()
this.getActivityGroup()
},
resetSearch() {
this.pageForm.year = ""
@@ -645,6 +1074,8 @@ layout("/layouts/platform.html"){
homeSignupEntryImage: "",
lots: [],
unionQuotas: [],
travelPeriods: [],
periodQuotas: [],
serviceNotice: ""
}
},
@@ -655,6 +1086,8 @@ layout("/layouts/platform.html"){
// 新建时给出默认年度和开关值,减少校工会管理员录入成本。
this.formData = this.defaultFormData()
this.boardingPlaceRows = []
this.periodQuotaUnionId = ""
this.periodQuotaDateRange = []
this.quotaOverview.schoolFormalAssignedCount = 0
this.dialogVisible = true
this.loadUnionQuotaRows("")
@@ -689,12 +1122,36 @@ layout("/layouts/platform.html"){
settingId: ""
})
})
const periodIdMap = {}
const inheritedTravelPeriods = this.normalizeTravelPeriods(previous.travelPeriods || []).map((item) => {
const newId = this.newClientId()
periodIdMap[item.id] = newId
return Object.assign({}, item, {
id: newId,
settingId: "",
createdAt: "",
createdBy: "",
updatedAt: "",
updatedBy: ""
})
})
const inheritedPeriodQuotas = this.normalizePeriodQuotas(previous.periodQuotas || []).filter((item) => {
return !!periodIdMap[item.periodId]
}).map((item) => {
return Object.assign({}, item, {
id: "",
settingId: "",
periodId: periodIdMap[item.periodId]
})
})
this.formData = Object.assign(this.defaultFormData(), previous, {
year: String(currentYear),
cycleStartYear: previous.cycleStartYear ? String(previous.cycleStartYear) : "",
cycleEndYear: previous.cycleEndYear ? String(previous.cycleEndYear) : "",
lots: inheritedLots,
unionQuotas: inheritedUnionQuotas
unionQuotas: inheritedUnionQuotas,
travelPeriods: inheritedTravelPeriods,
periodQuotas: inheritedPeriodQuotas
})
this.boardingPlaceRows = this.parseBoardingPlaceRows(this.formData.boardingPlace)
delete this.formData.id
@@ -719,6 +1176,8 @@ layout("/layouts/platform.html"){
this.$set(this.formData, "homeSignupEntryEnabled", false)
this.$set(this.formData, "homeSignupEntryImage", "")
this.lotDeleteList = []
this.periodQuotaUnionId = ""
this.periodQuotaDateRange = []
this.$message.success("已延用上一年配置信息")
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
}).catch(() => {
@@ -749,6 +1208,8 @@ layout("/layouts/platform.html"){
homeSignupEntryImage: "",
lots: [],
unionQuotas: [],
travelPeriods: [],
periodQuotas: [],
serviceNotice: ""
}, res.data || {})
this.boardingPlaceRows = this.parseBoardingPlaceRows(this.formData.boardingPlace)
@@ -779,6 +1240,10 @@ layout("/layouts/platform.html"){
allowOverReimbursement: !!item.allowOverReimbursement
}))
this.formData.unionQuotas = this.normalizeUnionQuotaRows(this.formData.unionQuotas || [])
this.formData.travelPeriods = this.normalizeTravelPeriods(this.formData.travelPeriods || [])
this.formData.periodQuotas = this.normalizePeriodQuotas(this.formData.periodQuotas || [])
this.periodQuotaUnionId = ""
this.periodQuotaDateRange = []
this.loadUnionQuotaOverview(row.id)
this.dialogVisible = true
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
@@ -792,6 +1257,8 @@ layout("/layouts/platform.html"){
if (!valid) return
if (!this.validateLots()) return
if (!this.validateUnionQuotas()) return
if (!this.validateTravelPeriods()) return
if (!this.validatePeriodQuotas()) return
this.submitLoading = true
// Nutz 对子表集合按字符串化 JSON 绑定更稳定,和体检项目维护的提交方式保持一致。
const submitData = JSON.parse(JSON.stringify(this.formData))
@@ -808,6 +1275,8 @@ layout("/layouts/platform.html"){
}
submitData.lots = JSON.stringify(this.formData.lots || [])
submitData.unionQuotas = JSON.stringify(this.formData.unionQuotas || [])
submitData.travelPeriods = JSON.stringify(this.formData.travelPeriods || [])
submitData.periodQuotas = JSON.stringify(this.buildPeriodQuotasForSubmit())
submitData.lotDeleteList = JSON.stringify(this.lotDeleteList)
this.$axios.post(loc() + "/doSubmit", submitData).then((res) => {
this.submitLoading = false
@@ -955,6 +1424,39 @@ layout("/layouts/platform.html"){
}
return true
},
validateTravelPeriods() {
const rows = this.formData.travelPeriods || []
for (let i = 0; i < rows.length; i++) {
const row = rows[i]
const rowNo = "第" + (i + 1) + "行"
if (!row.periodName) {
this.$message.warning(rowNo + "时间段名称不能为空")
return false
}
if (!row.startDate || !row.endDate) {
this.$message.warning(rowNo + "开始日期和结束日期不能为空")
return false
}
if (row.startDate > row.endDate) {
this.$message.warning(rowNo + "开始日期不能大于结束日期")
return false
}
}
return true
},
validatePeriodQuotas() {
const rows = this.formData.unionQuotas || []
for (let i = 0; i < rows.length; i++) {
const row = rows[i]
const formalQuota = this.toNonNegativeInteger(row.formalQuota)
const usedQuota = this.periodQuotaRowTotal(row.unionId)
if (usedQuota > formalQuota) {
this.$message.warning("分工会【" + row.unionName + "】时间段名额合计 " + usedQuota + ",不能大于正式名额 " + formalQuota)
return false
}
}
return true
},
deleteLot(index, row) {
this.$confirm("确定删除该标段吗?", "提示", {
confirmButtonText: "确定",
@@ -993,6 +1495,17 @@ layout("/layouts/platform.html"){
this.quotaOverview = {
schoolFormalAssignedCount: 0
}
this.periodQuotaUnionId = ""
this.periodQuotaDateRange = []
this.periodDialogVisible = false
this.periodEditIndex = -1
this.periodForm = {
id: "",
periodName: "",
startDate: "",
endDate: "",
enabled: true
}
this.inheritLoading = false
this.activeTab = "basic"
}
@@ -1094,11 +1607,56 @@ layout("/layouts/platform.html"){
text-align: right;
}
.tour-tab-toolbar-left {
text-align: left;
}
.tour-tab-alert {
flex-shrink: 0;
margin-bottom: 10px;
}
.tour-tab-table {
flex: 1;
min-height: 0;
}
.tour-period-quota-toolbar {
align-items: center;
display: flex;
flex-shrink: 0;
gap: 10px;
margin-bottom: 10px;
}
.tour-period-quota-union {
width: 180px;
}
.tour-period-quota-cell {
align-items: center;
display: flex;
gap: 8px;
justify-content: center;
}
.tour-period-quota-cell .el-input {
width: 120px;
}
.tour-period-quota-cell span {
color: #606266;
white-space: nowrap;
}
.text-danger {
color: #f56c6c;
}
.text-success {
color: #67c23a;
}
.tour-setting-dialog {
height: 85vh;
max-height: 85vh;
@@ -183,6 +183,20 @@ layout("/layouts/platform.html"){
<el-input v-model="signupForm.travelAgencyName" :disabled="isDirectFamilyLine(signupForm)" readonly></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="图片材料">
<file-upload
style="--upload-width: 96px;--upload-height:96px"
:upload_number="9"
:upload_size="20971520"
:value.sync="signupForm.photoFiles"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="interval">
</file-upload>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
@@ -867,6 +881,7 @@ layout("/layouts/platform.html"){
travelAgencyId: matter.travelAgencyId || "",
travelAgencyName: matter.travelAgencyName || "",
boardingPlace: matter.defaultBoardingPlace || "",
photoFiles: "",
travelPeriod: matter.travelPeriod || "",
travelStartTime: matter.travelStartTime || "",
travelEndTime: matter.travelEndTime || "",
@@ -886,6 +901,7 @@ layout("/layouts/platform.html"){
travelAgencyId: matter.travelAgencyId || ledger.travelAgencyId || "",
travelAgencyName: matter.travelAgencyName || ledger.travelAgencyName || "",
boardingPlace: ledger.boardingPlace || matter.defaultBoardingPlace || "",
photoFiles: ledger.photoFiles || "",
travelPeriod: matter.travelPeriod || "",
travelStartTime: matter.travelStartTime || "",
travelEndTime: matter.travelEndTime || "",
@@ -129,7 +129,7 @@ layout("/layouts/platform.html"){
</el-select>
</el-form-item>
</el-col>
<el-col>
<el-col :span="6">
<el-form-item label="人员类型" prop="personType">
<el-radio-group v-model="assignForm.personType" @change="assignPersonTypeChange">
<el-radio-button label="FORMAL">正式人员</el-radio-button>
@@ -115,6 +115,17 @@ layout("/layouts/platform_h5.html"){
font-size: 13px;
text-align: center;
}
.tour-upload-field {
padding: 12px 16px 14px;
}
.tour-upload-label {
margin-bottom: 8px;
color: #323233;
font-size: 14px;
line-height: 20px;
}
</style>
<div id="app">
@@ -162,6 +173,18 @@ layout("/layouts/platform_h5.html"){
</van-field>
<van-field v-if="fillBedInfo" label="床位信息" v-model="signupForm.bedInfo" maxlength="100" placeholder="请输入床位数量"></van-field>
<van-field v-if="fillBedInfo" label="意向拼床人" v-model="signupForm.intendedRoommate" maxlength="100" placeholder="请输入意向拼床人"></van-field>
<div class="tour-upload-field">
<div class="tour-upload-label">图片材料</div>
<h5-file-upload
:upload_number="9"
:upload_size="20971520"
:value.sync="signupForm.photoFiles"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="array">
</h5-file-upload>
</div>
<van-field v-if="canApplyOverReimbursement(signupForm)" class="tour-over-cost" label="报销超出费用">
<template #input>
<van-radio-group v-model="signupForm.overCostReimbursed" direction="horizontal">
@@ -411,6 +434,7 @@ layout("/layouts/platform_h5.html"){
travelAgencyId: matter.travelAgencyId || "",
travelAgencyName: matter.travelAgencyName || "",
boardingPlace: "",
photoFiles: "",
travelPeriod: matter.travelPeriod || "",
travelStartTime: matter.travelStartTime || "",
travelEndTime: matter.travelEndTime || "",
@@ -430,6 +454,7 @@ layout("/layouts/platform_h5.html"){
travelAgencyId: matter.travelAgencyId || ledger.travelAgencyId || "",
travelAgencyName: matter.travelAgencyName || ledger.travelAgencyName || "",
boardingPlace: ledger.boardingPlace || assignment.boardingPlace || "",
photoFiles: this.toUploadFileList(ledger.photoFiles || assignment.photoFiles || ""),
travelPeriod: matter.travelPeriod || "",
travelStartTime: matter.travelStartTime || "",
travelEndTime: matter.travelEndTime || "",
@@ -466,6 +491,38 @@ layout("/layouts/platform_h5.html"){
return String(value).split(",").map((item) => item.trim()).filter((item) => item)
}
},
toUploadFileList(value) {
if (!value) return []
return String(value)
.split(",")
.map((url, index) => {
url = url.trim()
return {
url: url,
name: "photo_" + index + ".jpg",
status: null,
isImage: true
}
})
},
fileNameFromUrl(url) {
if (!url) return "图片材料"
const pureUrl = String(url).split("?")[0]
const name = pureUrl.substring(pureUrl.lastIndexOf("/") + 1)
return name || "图片材料"
},
serializePhotoFiles(value) {
if (!value) return ""
if (Array.isArray(value)) {
return value.map((item) => {
if (typeof item === "string") return item.trim()
return item && item.url ? String(item.url).trim() : ""
}).filter((item) => item).join(",")
}
return String(value).split(",").map((item) => item.trim()).filter((item) => item).join(",")
},
emptyFamily() {
return {
familyName: "",
@@ -667,6 +724,7 @@ layout("/layouts/platform_h5.html"){
if (!this.validateDirectRelative()) return
const families = this.allowFamily && this.signupForm.hasFamily ? this.familyData : []
const form = Object.assign({}, this.signupForm, {
photoFiles: this.serializePhotoFiles(this.signupForm.photoFiles),
families: JSON.stringify(families),
directRelative: this.isDirectFamilyLine(this.signupForm) ? JSON.stringify(this.directRelativeForm) : ""
})