Files
zhgh_hmc/src/main/resources/views/mobile/activity/site/audit.html
T
c-hongqw1 b6a4a50e10 场地预约整改:审核默认同意
拉取数据筛选在职状态,不拉退休人员
选择福利界面增加已选择未选择
手机端主页活动回显
2026-09-11 16:02:26 +08:00

276 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--# layout("/mobile/platform.html"){ #-->
<style>
/* 日期和每个时间段保持完整,同日连续场次由后端统一合并展示。 */
.site-audit .reservation-times { text-align:left; font-variant-numeric:tabular-nums; }
.site-audit .reservation-time-day { display:flex; align-items:baseline; flex-wrap:wrap; gap:4px 12px; padding:3px 0; line-height:22px; }
.site-audit .reservation-time-date { flex:0 0 88px; white-space:nowrap; font-weight:500; }
.site-audit .reservation-time-ranges { display:flex; flex-wrap:wrap; gap:2px 12px; }
.site-audit .reservation-time-range { white-space:nowrap; }
.site-audit .reservation-times { padding:4px 0 6px; font-size:14px; overflow-wrap:normal; }
.site-audit .reservation-time-day { display:block; padding:4px 0; }
.site-audit .reservation-time-date { display:block; color:#606266; }
.site-audit .van-doc-card { margin:14px; padding:12px; background:#fff; border-radius:10px; box-shadow:0 8px 12px #ebedf0; line-height:20px; font-size:15px; position:relative; }
.site-audit .card-title { font-size:18px; font-weight:bold; color:#1867b0; padding-left:4px; }
.site-audit .title-mark { position:absolute; left:0; top:12px; color:#1867b0; font-weight:bold; }
.site-audit .card-fields { margin-top:4px; line-height:30px; overflow-wrap:anywhere; }
.site-audit .field-label { color:grey; }
.site-audit .van-divider { margin:6px 0; }
.site-audit .card-footer { display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:8px; }
.site-audit .card-footer .van-button { border-radius:8px; height:26px; font-size:16px; margin-left:4px; }
.site-audit .process-title { color:#1867b0; font-size:15px; font-weight:600; margin:10px 7px; padding:10px; }
.site-audit .audit-actions { display:flex; gap:10px; padding:10px; }
.site-audit .site-audit-section { color:#1867b0; font-weight:bold; font-size:16px; }
.site-audit .site-audit-loading { padding:24px; text-align:center; }
.site-audit .site-audit-text .van-cell__label { white-space:pre-wrap; overflow-wrap:anywhere; }
.site-audit .detail-popup { background:#f7f8fa; }
</style>
<div id="app" class="site-audit" v-cloak>
<van-sticky>
<van-nav-bar title="${reviewTitle}" left-text="返回" left-arrow @click-left="goBack"></van-nav-bar>
<van-search v-model="keyword" placeholder="请输入场地名称或申请人" @search="search" @clear="search"></van-search>
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
<van-dropdown-item ref="auditFilter" v-model="isAudit" :options="auditOptions" @change="search"
@open="filterOpened" @close="filterClosed"></van-dropdown-item>
</van-dropdown-menu>
</van-sticky>
<van-list v-model="loading" :finished="finished" :error.sync="listError" error-text="加载失败,点击重试"
:immediate-check="false" :finished-text="rows.length ? '没有更多了' : ''" @load="loadMore">
<div v-for="row in rows" :key="row.id" class="van-doc-card">
<div class="card-title van-ellipsis"><span class="title-mark">|</span>{{row.reserve_person}}{{row.loginname}}</div>
<div class="card-fields">
<div><span class="field-label">场地名称:</span>{{row.site_name}}</div>
<div><span class="field-label">所属单位:</span>{{row.reserve_person_unit || '—'}}</div>
<div><span class="field-label">预约类型:</span>{{typeName(row.reserve_type)}}</div>
<div v-if="row.club_name"><span class="field-label">所属协会:</span>{{row.club_name}}</div>
<div><span class="field-label">预约时段:</span><div class="reservation-times">
<div v-for="group in row.reservationTimeGroups" :key="group.date" class="reservation-time-day">
<span class="reservation-time-date">{{group.date}}</span>
<div class="reservation-time-ranges"><span v-for="(range,index) in group.ranges" :key="index" class="reservation-time-range">{{range.start}}{{range.end}}</span></div>
</div>
<span v-if="!row.reservationTimeGroups || !row.reservationTimeGroups.length"></span>
</div></div>
</div>
<van-divider></van-divider>
<div class="card-footer">
<!-- 仅状态内容绑定配置颜色,保留字段标签原有灰色。 -->
<div><span class="field-label">当前状态:</span><span :style="{color: row.state_color || null}">{{row.state_name}}</span></div>
<div>
<van-button size="small" type="info" @click="openDetail(row,false)">查看</van-button>
<van-button v-if="canReview(row)" size="small" type="primary" @click="openDetail(row,true)">审核</van-button>
<van-button v-if="isAudit===true" size="small" type="danger" :disabled="!row.canRevoke || formLoading"
:loading="formLoading && revokeId===row.id" @click="openRevoke(row)">撤回</van-button>
</div>
</div>
</div>
</van-list>
<van-empty v-if="!loading && !listError && !rows.length" image="/none.svg" description="暂无数据"></van-empty>
<van-popup v-model="detailShow" position="right" :style="{height:'100%',width:'100%'}" class="detail-popup" safe-area-inset-bottom>
<van-sticky>
<van-nav-bar title="申请详情" left-text="返回" left-arrow @click-left="goBack"></van-nav-bar>
</van-sticky>
<site-audit-info ref="infoRef" :review-api="reviewApi" @loaded="onDetailLoaded"></site-audit-info>
<div v-if="showApprovalForm">
<div class="process-title">${reviewTitle}</div>
<van-form @submit="openConfirm(true)">
<van-field label="审核人员" readonly v-model="formData.username"></van-field>
<van-field label="当前时间" readonly v-model="formData.auditTime"></van-field>
<van-field label="审核意见" type="textarea" maxlength="500" show-word-limit required
v-model="formData.auditOpinion" placeholder="请输入审核意见"></van-field>
</van-form>
<div class="audit-actions">
<van-button block type="default" :disabled="formLoading" @click="goBack">取消</van-button>
<van-button block type="danger" :disabled="!auditReady || formLoading" :loading="formLoading && !pendingPass" @click="openConfirm(false)">拒绝</van-button>
<van-button block type="primary" :disabled="!auditReady || formLoading" :loading="formLoading && pendingPass" @click="openConfirm(true)">通过</van-button>
</div>
</div>
</van-popup>
<van-dialog v-model="confirmShow" title="提示" show-cancel-button :before-close="beforeConfirmClose"
:message="pendingPass ? '确定通过该申请吗?' : '确定拒绝该申请吗?拒绝后流程结束。'">
</van-dialog>
<van-dialog v-model="revokeShow" title="撤回审核" show-cancel-button :before-close="beforeRevokeClose"
message="确定撤回本次审核吗?撤回后申请恢复到当前节点待审核。"></van-dialog>
</div>
<script>
(() => {
// PJAX 先执行内联脚本;捕获本次页面,依赖加载完成后才允许创建 Vue。
const pageRoot = document.getElementById('app')
const startPage = () => {
if (document.getElementById('app') !== pageRoot || !pageRoot.isConnected) return
<!--# include('./common/info.js'){} #-->
new Vue({
el: '#app',
mixins: [window.popupHistoryMixin],
components: {'site-audit-info': SITE_AUDIT_INFO},
data() {
return {
historyPopupKeys: ['filterShow','detailShow','confirmShow','revokeShow'],
revokeShow:false, revokeId:'',
reviewApi: '${reviewApi}',
auditOptions: [{text:'已审核',value:true},{text:'未审核',value:false}],
isAudit: false, keyword: '', rows: [], page: 1,
loading: false, finished: false, listError: false, requestVersion: 0, requesting: false,
filterShow: false, detailShow: false, confirmShow: false,
showApprovalForm: false, auditReady: false, pendingPass: false, formLoading: false,
formData: {id:'',username:'',auditTime:'',auditOpinion:''}
}
},
watch: {
// DropdownItem 没有弹层 v-model,通过全局历史状态同步关闭内部下拉层。
filterShow(value) { if (!value && this.$refs.auditFilter) this.$refs.auditFilter.toggle(false) }
},
methods: {
openRevoke(row) {
if (this.formLoading || !row.canRevoke) return
this.$set(this, 'revokeId', row.id)
this.$set(this, 'revokeShow', true)
},
// 确认框也进入历史栈;失败保留弹框,请求结束复位 Vant 内部 loading。
beforeRevokeClose(action, done) {
if (this.formLoading) { done(false); return }
if (action!=='confirm') { done(false); this.goBack(); return }
this.$set(this, 'formLoading', true)
$.post(this.reviewApi + '/doRevoke', {id:this.revokeId}).then((res) => {
if (res && res.code===0) {
this.clearPopupHistory(() => { this.$toast.success('已撤回审核'); this.search() })
} else { this.$toast.fail(res && res.msg || '响应异常,请刷新核对申请状态') }
}, () => { this.$toast.fail('请求失败,请刷新核对申请状态') }).always(() => {
this.$set(this, 'formLoading', false)
done(false)
})
},
// 菜单采用 PJAX replace 进入,列表返回应回手机首页,不能依赖上一条浏览器历史。
// 弹框存在时通过统一栈关闭最上层,重复点击也不会连续退过业务页面。
goBack() {
if (this.formLoading) return
const key = this.revokeShow ? 'revokeShow' : this.confirmShow ? 'confirmShow' : this.detailShow ? 'detailShow' : this.filterShow ? 'filterShow' : null
if (key) {
window.popupHistory.close(this._popupOwner, key)
} else {
this.clearPopupHistory(() => pjaxReplace('/mobile/index'))
}
},
typeName(type) { return {1:'个人预约',2:'分工会预约',3:'协会预约'}[type] || '—' },
filterOpened() { this.$set(this, 'filterShow', true) },
filterClosed() { this.$set(this, 'filterShow', false) },
canReview(row) {
const stage = this.reviewApi.substring(this.reviewApi.lastIndexOf('/') + 1)
return Number(row.reserve_state) === {union:4000,club:4010,school:4020}[stage]
},
search() {
// 筛选变更使旧响应失效,避免未审核和已审核数据混入同一列表。
this.$set(this, 'requestVersion', this.requestVersion + 1)
this.$set(this, 'requesting', false)
this.$set(this, 'rows', [])
this.$set(this, 'page', 1)
this.$set(this, 'finished', false)
this.$set(this, 'listError', false)
this.loadMore()
},
// isAudit 为审核状态,pageNumber/pageSize 为分页;响应 data 含 list 和 totalCount。
loadMore() {
if (this.requesting || this.finished) return
const version = this.requestVersion
this.$set(this, 'requesting', true)
this.$set(this, 'loading', true)
$.post(this.reviewApi + '/pageData', {
isAudit: this.isAudit, searchKeyword: this.keyword, pageNumber: this.page, pageSize: 10
}).then((res) => {
if (version !== this.requestVersion) return
if (res.code === 0) {
this.$set(this, 'rows', this.rows.concat(res.data.list))
this.$set(this, 'page', this.page + 1)
this.$set(this, 'finished', this.rows.length >= res.data.totalCount)
} else {
this.$set(this, 'listError', true)
this.$toast.fail(res.msg)
}
}).fail(() => {
if (version === this.requestVersion) this.$set(this, 'listError', true)
}).always(() => {
if (version === this.requestVersion) {
this.$set(this, 'loading', false)
this.$set(this, 'requesting', false)
}
})
},
// 进入审核时默认同意;仅查看时不填意见,切换申请不保留上一条输入。
openDetail(row, approval) {
this.$set(this, 'auditReady', false)
this.$set(this, 'showApprovalForm', approval)
this.$set(this, 'formData', {
id: row.id, username: "${@shiro.getPrincipalProperty('username')}",
auditTime: moment().format('YYYY-MM-DD HH:mm:ss'), auditOpinion: approval ? '同意' : ''
})
this.$set(this, 'detailShow', true)
this.$nextTick(() => this.$refs.infoRef.onOpen(row.id))
},
onDetailLoaded(detail) {
this.$set(this, 'auditReady', detail.id === this.formData.id && this.canReview(detail))
},
openConfirm(isPass) {
if (this.formLoading || !this.auditReady) return
if (!this.formData.auditOpinion.trim()) { this.$toast.fail('请填写审核意见'); return }
this.$set(this, 'pendingPass', isPass)
this.$set(this, 'confirmShow', true)
},
// 确认时保留 Dialog,请求收尾通过 done(false) 复位其内部 loading;取消只退一层历史。
beforeConfirmClose(action, done) {
if (this.formLoading) { done(false); return }
if (action === 'confirm') this.review(done)
else { done(false); this.goBack() }
},
// ids 为预约 ID 数组,isPass 为通过/拒绝;响应应为统一 code/msg,空响应不能视为成功。
review(dialogDone = () => {}) {
if (this.formLoading || !this.auditReady) { dialogDone(false); return }
const opinion = (this.formData.auditOpinion || '').trim()
if (!opinion) { dialogDone(false); this.$toast.fail('请填写审核意见'); return }
this.$set(this, 'formLoading', true)
$.post(this.reviewApi + '/doReview', {
ids: JSON.stringify([this.formData.id]), isPass: this.pendingPass,
auditOpinion: opinion
}).then((res) => {
// jQuery 1.x 的 then 回调异常可能中断后续链,先检查空或非标准响应再读取字段。
if (!res || typeof res !== 'object' || typeof res.code !== 'number') {
this.$toast.fail('审核响应异常,请刷新核对申请状态')
return
}
if (res.code === 0) {
this.$set(this, 'auditReady', false)
this.clearPopupHistory(() => { this.$toast.success('审核成功'); this.search() })
} else { this.$toast.fail(res.msg || '审核失败,请刷新核对申请状态') }
}).fail(() => { this.$toast.fail('提交失败,请刷新核对申请状态') }).always(() => {
// 同时复位表单按钮及 Vant Dialog 内部按钮,失败时保留意见供核对。
this.$set(this, 'formLoading', false)
dialogDone(false)
})
}
},
created() { this.search() },
mounted() {
// PJAX 替换 DOM 不会自动销毁 Vue,离开时注销弹框历史同步及本页事件。
this._siteAuditDispose = () => this.$destroy()
$(document).one('pjax:beforeReplace.siteAudit', this._siteAuditDispose)
},
beforeDestroy() {
$(document).off('pjax:beforeReplace.siteAudit', this._siteAuditDispose)
this.$set(this, 'requestVersion', this.requestVersion + 1)
}
})
}
if (window.popupHistoryMixin) {
startPage()
} else {
// 共用一次加载请求;失败后允许重新进入重试,不能在缺少 mixin 时继续挂载。
if (!window.siteAuditHistoryLoading) {
window.siteAuditHistoryLoading = $.getScript('/assets/mobile/js/popupHistory.js')
.fail(() => { window.siteAuditHistoryLoading = null })
}
window.siteAuditHistoryLoading.then(() => startPage()).fail(() => {
if (document.getElementById('app') === pageRoot) vant.Toast.fail('页面组件加载失败,请重新进入')
})
}
})()
</script>
<!--# } #-->