Merge remote-tracking branch 'origin/feature_工会报销UI优化' into release_20260831

This commit is contained in:
2026-08-31 11:14:13 +08:00
7 changed files with 1075 additions and 530 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

@@ -4,12 +4,12 @@ layout("/layouts/platform_h5.html"){
<style> <style>
body { body {
background: #f5f6fa; background: #f3f7fd;
} }
#app { #app {
min-height: 100vh; min-height: 100vh;
background: #f5f6fa; background: #f3f7fd;
} }
.custom-nav { .custom-nav {
@@ -394,17 +394,22 @@ layout("/layouts/platform_h5.html"){
} }
</style> </style>
<div id="app" v-cloak> <style id="style-union-reimburse-h5">
<van-nav-bar title="报销申请" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed z-index="999" <!--#include('../common/unionReimburse.css'){}#-->
</style>
<div id="app" v-cloak class="union-reimburse-page union-reimburse-form">
<van-nav-bar title="报销申请" left-text="返回" left-arrow @click-left="onNavBack" placeholder fixed z-index="999"
class="custom-nav"></van-nav-bar> class="custom-nav"></van-nav-bar>
<div class="form-container"> <div class="form-container">
<van-form ref="formRef"> <van-form ref="formRef">
<van-cell-group title="信息填报" class="form-section"> <section class="form-section">
<h2 class="form-section__title">基本信息</h2>
<van-cell-group :border="false">
<van-field label="工号" v-model="formData.loginName" placeholder="从信息中心获取" readonly></van-field> <van-field label="工号" v-model="formData.loginName" placeholder="从信息中心获取" readonly></van-field>
<van-field label="经办人" v-model="formData.userName" placeholder="从信息中心获取" readonly></van-field> <van-field label="经办人" v-model="formData.userName" placeholder="从信息中心获取" readonly></van-field>
<!-- 联系方式 --> <!-- 经办人联系方式,使用当前登录用户的 mobile 初始化。 -->
<van-field label="联系方式" v-model="formData.mobile" required placeholder="请输入联系方式" <van-field label="联系方式" name="mobile" v-model="formData.mobile" required placeholder="请输入联系方式"
:rules="[ :rules="[
{ required: true, message: '请输入联系方式' }, { required: true, message: '请输入联系方式' },
{ pattern: /^(1[3-9]\d{9}|0\d{2,3}-?\d{7,8})$/, message: '请输入正确的联系方式' } { pattern: /^(1[3-9]\d{9}|0\d{2,3}-?\d{7,8})$/, message: '请输入正确的联系方式' }
@@ -422,7 +427,7 @@ layout("/layouts/platform_h5.html"){
required required
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showReimburseFundSourcePicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showReimburseFundSourcePicker">
<van-picker :columns="reimburseFundSourceColumns" @cancel="closePopup('showReimburseFundSourcePicker')" <van-picker :columns="reimburseFundSourceColumns" @cancel="closePopup('showReimburseFundSourcePicker')"
@confirm="onReimburseFundSourceConfirm" show-toolbar></van-picker> @confirm="onReimburseFundSourceConfirm" show-toolbar></van-picker>
</van-popup> </van-popup>
@@ -444,7 +449,7 @@ layout("/layouts/platform_h5.html"){
<span style="color: #ee0a24; text-align: left;">报销提示:{{getProjectTipByProject(formData.reimburseProject)}}</span> <span style="color: #ee0a24; text-align: left;">报销提示:{{getProjectTipByProject(formData.reimburseProject)}}</span>
</template> </template>
</van-cell> </van-cell>
<van-popup position="bottom" round v-model:show="showReimburseProjectPicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showReimburseProjectPicker">
<van-picker :columns="reimburseProjectColumns" @cancel="closePopup('showReimburseProjectPicker')" <van-picker :columns="reimburseProjectColumns" @cancel="closePopup('showReimburseProjectPicker')"
@confirm="onReimburseProjectConfirm" show-toolbar></van-picker> @confirm="onReimburseProjectConfirm" show-toolbar></van-picker>
</van-popup> </van-popup>
@@ -461,7 +466,7 @@ layout("/layouts/platform_h5.html"){
required required
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showClubPicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showClubPicker">
<van-picker :columns="clubColumns" @cancel="closePopup('showClubPicker')" <van-picker :columns="clubColumns" @cancel="closePopup('showClubPicker')"
@confirm="onClubConfirm" show-toolbar></van-picker> @confirm="onClubConfirm" show-toolbar></van-picker>
</van-popup> </van-popup>
@@ -532,8 +537,11 @@ layout("/layouts/platform_h5.html"){
</van-action-sheet> </van-action-sheet>
--> -->
</van-cell-group> </van-cell-group>
<van-cell-group :title="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1' ? '慰问信息' : '活动信息'" class="form-section"> </section>
<section class="form-section">
<h2 class="form-section__title">{{!formData.reimburseProject ? '报销信息' : (formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1' ? '慰问信息' : '活动信息')}}</h2>
<van-cell-group :border="false">
<template v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'"> <template v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
<van-field <van-field
@@ -548,7 +556,7 @@ layout("/layouts/platform_h5.html"){
required required
is-link is-link
></van-field> ></van-field>
<van-action-sheet v-model="showCondolenceSearch" title="慰问对象" class="height100"> <van-action-sheet v-model="showCondolenceSearch" :close-on-click-overlay="true" title="慰问对象" class="height100">
<van-search <van-search
v-model="condolenceSearchKeyword" v-model="condolenceSearchKeyword"
:show-action="false" :show-action="false"
@@ -587,7 +595,7 @@ layout("/layouts/platform_h5.html"){
required required
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showCondolenceTypePicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showCondolenceTypePicker">
<van-picker :columns="condolenceTypeColumns" @cancel="closePopup('showCondolenceTypePicker')" <van-picker :columns="condolenceTypeColumns" @cancel="closePopup('showCondolenceTypePicker')"
@confirm="onCondolenceTypeConfirm" show-toolbar></van-picker> @confirm="onCondolenceTypeConfirm" show-toolbar></van-picker>
</van-popup> </van-popup>
@@ -604,7 +612,7 @@ layout("/layouts/platform_h5.html"){
required required
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showCondolenceWayPicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showCondolenceWayPicker">
<van-picker :columns="condolenceWayColumns" @cancel="closePopup('showCondolenceWayPicker')" <van-picker :columns="condolenceWayColumns" @cancel="closePopup('showCondolenceWayPicker')"
@confirm="onCondolenceWayConfirm" show-toolbar></van-picker> @confirm="onCondolenceWayConfirm" show-toolbar></van-picker>
</van-popup> </van-popup>
@@ -627,7 +635,7 @@ layout("/layouts/platform_h5.html"){
required required
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showCondolenceTimePicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showCondolenceTimePicker">
<van-datetime-picker type="date" title="选择慰问时间" :min-date="minDate" :max-date="maxDate" <van-datetime-picker type="date" title="选择慰问时间" :min-date="minDate" :max-date="maxDate"
@cancel="closePopup('showCondolenceTimePicker')" @cancel="closePopup('showCondolenceTimePicker')"
@confirm="onCondolenceTimeConfirm"></van-datetime-picker> @confirm="onCondolenceTimeConfirm"></van-datetime-picker>
@@ -644,7 +652,7 @@ layout("/layouts/platform_h5.html"){
clickable clickable
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showMarryTimePicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showMarryTimePicker">
<van-datetime-picker type="date" title="选择结婚时间" :min-date="minDate" :max-date="maxDate" <van-datetime-picker type="date" title="选择结婚时间" :min-date="minDate" :max-date="maxDate"
@cancel="closePopup('showMarryTimePicker')" @cancel="closePopup('showMarryTimePicker')"
@confirm="onDateConfirm('marryTime', 'showMarryTimePicker', $event)"></van-datetime-picker> @confirm="onDateConfirm('marryTime', 'showMarryTimePicker', $event)"></van-datetime-picker>
@@ -661,7 +669,7 @@ layout("/layouts/platform_h5.html"){
clickable clickable
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showFertilityTimePicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showFertilityTimePicker">
<van-datetime-picker type="date" title="选择生育时间" :min-date="minDate" :max-date="maxDate" <van-datetime-picker type="date" title="选择生育时间" :min-date="minDate" :max-date="maxDate"
@cancel="closePopup('showFertilityTimePicker')" @cancel="closePopup('showFertilityTimePicker')"
@confirm="onDateConfirm('fertilityTime', 'showFertilityTimePicker', $event)"></van-datetime-picker> @confirm="onDateConfirm('fertilityTime', 'showFertilityTimePicker', $event)"></van-datetime-picker>
@@ -681,7 +689,7 @@ layout("/layouts/platform_h5.html"){
clickable clickable
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showHospitalizationStartTimePicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showHospitalizationStartTimePicker">
<van-datetime-picker type="date" title="选择住院开始时间" :min-date="minDate" :max-date="maxDate" <van-datetime-picker type="date" title="选择住院开始时间" :min-date="minDate" :max-date="maxDate"
@cancel="closePopup('showHospitalizationStartTimePicker')" @cancel="closePopup('showHospitalizationStartTimePicker')"
@confirm="onDateConfirm('hospitalizationStartTime', 'showHospitalizationStartTimePicker', $event)"></van-datetime-picker> @confirm="onDateConfirm('hospitalizationStartTime', 'showHospitalizationStartTimePicker', $event)"></van-datetime-picker>
@@ -696,7 +704,7 @@ layout("/layouts/platform_h5.html"){
clickable clickable
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showHospitalizationEndTimePicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showHospitalizationEndTimePicker">
<van-datetime-picker type="date" title="选择住院结束时间" :min-date="minDate" :max-date="maxDate" <van-datetime-picker type="date" title="选择住院结束时间" :min-date="minDate" :max-date="maxDate"
@cancel="closePopup('showHospitalizationEndTimePicker')" @cancel="closePopup('showHospitalizationEndTimePicker')"
@confirm="onDateConfirm('hospitalizationEndTime', 'showHospitalizationEndTimePicker', $event)"></van-datetime-picker> @confirm="onDateConfirm('hospitalizationEndTime', 'showHospitalizationEndTimePicker', $event)"></van-datetime-picker>
@@ -716,7 +724,7 @@ layout("/layouts/platform_h5.html"){
clickable clickable
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showDeathTimePicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showDeathTimePicker">
<van-datetime-picker type="date" title="选择去世时间" :min-date="minDate" :max-date="maxDate" <van-datetime-picker type="date" title="选择去世时间" :min-date="minDate" :max-date="maxDate"
@cancel="closePopup('showDeathTimePicker')" @cancel="closePopup('showDeathTimePicker')"
@confirm="onDateConfirm('deathTime', 'showDeathTimePicker', $event)"></van-datetime-picker> @confirm="onDateConfirm('deathTime', 'showDeathTimePicker', $event)"></van-datetime-picker>
@@ -731,13 +739,13 @@ layout("/layouts/platform_h5.html"){
clickable clickable
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showRelationshipPicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showRelationshipPicker">
<van-picker :columns="relationshipColumns" @cancel="closePopup('showRelationshipPicker')" <van-picker :columns="relationshipColumns" @cancel="closePopup('showRelationshipPicker')"
@confirm="onRelationshipConfirm" show-toolbar></van-picker> @confirm="onRelationshipConfirm" show-toolbar></van-picker>
</van-popup> </van-popup>
</template> </template>
<van-field label="参加随行人员" <van-field class="more-text" label="参加随行人员"
v-model="formData.participants" v-model="formData.participants"
placeholder="请填写参加随行人员" placeholder="请填写参加随行人员"
type="textarea" type="textarea"
@@ -745,13 +753,13 @@ layout("/layouts/platform_h5.html"){
autosize autosize
:rules="[{ required: true, message: '请填写参加随行人员' }]" :rules="[{ required: true, message: '请填写参加随行人员' }]"
required></van-field> required></van-field>
<van-field label="报销事由" <van-field class="more-text" label="报销事由"
v-model="formData.paymentNotes" v-model="formData.paymentNotes"
placeholder="请填写报销事由" placeholder="请填写报销事由"
type="textarea" type="textarea"
rows="2" rows="2"
autosize></van-field> autosize></van-field>
<van-field label="备注" <van-field class="more-text" label="备注"
v-model="formData.notes" v-model="formData.notes"
placeholder="需要填写:发票编号、开具方、金额" placeholder="需要填写:发票编号、开具方、金额"
type="textarea" type="textarea"
@@ -790,7 +798,7 @@ layout("/layouts/platform_h5.html"){
required required
is-link is-link
></van-field> ></van-field>
<van-popup position="bottom" round v-model:show="showActivityTimePicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showActivityTimePicker">
<van-datetime-picker type="date" title="选择活动时间" :min-date="minDate" :max-date="maxDate" <van-datetime-picker type="date" title="选择活动时间" :min-date="minDate" :max-date="maxDate"
@cancel="closePopup('showActivityTimePicker')" @cancel="closePopup('showActivityTimePicker')"
@confirm="onActivityTimeConfirm"></van-datetime-picker> @confirm="onActivityTimeConfirm"></van-datetime-picker>
@@ -811,7 +819,8 @@ layout("/layouts/platform_h5.html"){
></h5-file-upload> ></h5-file-upload>
</template> </template>
</van-field> </van-field>
</van-cell-group> </van-cell-group>
</section>
<van-cell-group class="form-section"> <van-cell-group class="form-section">
<div class="payment-section"> <div class="payment-section">
<div class="payment-toolbar"> <div class="payment-toolbar">
@@ -900,11 +909,11 @@ layout("/layouts/platform_h5.html"){
<span class="payment-dot" :class="{active: index === currentPaymentIndex}" v-for="(item, index) in paymentInfoRows"></span> <span class="payment-dot" :class="{active: index === currentPaymentIndex}" v-for="(item, index) in paymentInfoRows"></span>
</div> </div>
</div> </div>
<van-popup position="bottom" round v-model:show="showPaymentWayPicker"> <van-popup position="bottom" round :close-on-click-overlay="true" v-model:show="showPaymentWayPicker">
<van-picker :columns="paymentWayColumns" @cancel="closePopup('showPaymentWayPicker')" <van-picker :columns="paymentWayColumns" @cancel="closePopup('showPaymentWayPicker')"
@confirm="onPaymentWayConfirm" show-toolbar></van-picker> @confirm="onPaymentWayConfirm" show-toolbar></van-picker>
</van-popup> </van-popup>
<van-action-sheet v-model="payerSelectShow" title="付款人" class="height100"> <van-action-sheet v-model="payerSelectShow" :close-on-click-overlay="true" title="付款人" class="height100">
<van-search <van-search
v-model="payerSearchKeyword" v-model="payerSearchKeyword"
:show-action="false" :show-action="false"
@@ -926,7 +935,8 @@ layout("/layouts/platform_h5.html"){
<van-empty description="暂无数据" v-else></van-empty> <van-empty description="暂无数据" v-else></van-empty>
</van-action-sheet> </van-action-sheet>
</van-cell-group> </van-cell-group>
<van-cell-group title="电子发票" class="form-section" v-if="formData.reimburseProject"> <van-cell-group class="form-section" v-if="formData.reimburseProject">
<div class="form-section__title">电子发票</div>
<van-field label="报销金额" <van-field label="报销金额"
v-model="formData.money" v-model="formData.money"
placeholder="自动汇总发票金额" placeholder="自动汇总发票金额"
@@ -936,7 +946,7 @@ layout("/layouts/platform_h5.html"){
placeholder="自动统计发票数量" placeholder="自动统计发票数量"
readonly></van-field> readonly></van-field>
<!-- 支付内容 --> <!-- 支付内容 -->
<van-field label="支付内容" <van-field class="more-text" label="支付内容"
v-model="formData.paymentNotes" v-model="formData.paymentNotes"
placeholder="请填写支付内容" placeholder="请填写支付内容"
type="textarea" type="textarea"
@@ -952,6 +962,8 @@ layout("/layouts/platform_h5.html"){
<template #right-icon> <template #right-icon>
<van-switch v-model="formData.invoiceVerifyEnabled" <van-switch v-model="formData.invoiceVerifyEnabled"
size="22px" size="22px"
active-color="#1989fa"
inactive-color="#d8e0ea"
@change="handleVerifySwitchChange"></van-switch> @change="handleVerifySwitchChange"></van-switch>
</template> </template>
</van-cell> </van-cell>
@@ -966,9 +978,9 @@ layout("/layouts/platform_h5.html"){
<div class="invoice-detail-line">项目名称:{{item.itemName || '未填写'}}</div> <div class="invoice-detail-line">项目名称:{{item.itemName || '未填写'}}</div>
<div class="invoice-detail-line" v-if="getInvoiceFile(item)">文件:{{getInvoiceFile(item).name || getInvoiceFile(item).filename || '已上传'}}</div> <div class="invoice-detail-line" v-if="getInvoiceFile(item)">文件:{{getInvoiceFile(item).name || getInvoiceFile(item).filename || '已上传'}}</div>
<div class="invoice-detail-actions"> <div class="invoice-detail-actions">
<van-button native-type="button" size="mini" plain type="info" @click="previewInvoiceFile(item)">预览</van-button> <van-button native-type="button" size="mini" plain type="info" icon="eye-o" @click="previewInvoiceFile(item)">预览</van-button>
<van-button native-type="button" size="mini" plain type="primary" @click="openInvoiceDialog('edit', item, index)">编辑</van-button> <van-button native-type="button" size="mini" plain type="primary" icon="edit" @click="openInvoiceDialog('edit', item, index)">编辑</van-button>
<van-button native-type="button" size="mini" plain type="danger" @click="removeInvoiceDetail(index)">删除</van-button> <van-button native-type="button" size="mini" plain type="danger" icon="delete-o" @click="removeInvoiceDetail(index)">删除</van-button>
</div> </div>
</div> </div>
</template> </template>
@@ -985,22 +997,22 @@ layout("/layouts/platform_h5.html"){
<!-- 提交按钮 --> <!-- 提交按钮 -->
<div class="form-actions"> <div class="form-actions">
<van-button plain @click="onSave" type="info">保存</van-button> <van-button plain @click="onSave" type="info">保存申请</van-button>
<van-button @click="onSubmit" type="primary" v-if="!taskId">提交</van-button> <van-button @click="onSubmit" type="primary" v-if="!taskId">提交申请</van-button>
<van-button @click="onSubmitAgain" type="primary" v-else>提交</van-button> <van-button @click="onSubmitAgain" type="primary" v-else>提交申请</van-button>
</div> </div>
</van-form> </van-form>
</div> </div>
<van-popup v-model:show="invoiceDialogVisible" position="bottom" round class="invoice-popup"> <van-popup v-model:show="invoiceDialogVisible" position="bottom" round :close-on-click-overlay="true" class="invoice-popup">
<van-nav-bar :title="invoiceDialogMode === 'edit' ? '编辑发票明细' : '新增发票明细'" <van-nav-bar :title="invoiceDialogMode === 'edit' ? '编辑发票明细' : '新增发票明细'"
left-text="取消" left-text="取消"
left-arrow left-arrow
@click-left="invoiceDialogVisible = false"></van-nav-bar> @click-left="closePopup('invoiceDialogVisible')"></van-nav-bar>
<div class="invoice-popup-body"> <div class="invoice-popup-body">
<div class="invoice-recognizing-mask" v-if="recognizingInvoice"> <div class="invoice-recognizing-mask" v-if="recognizingInvoice">
<van-loading type="spinner" color="#1677ff" size="24px">发票识别中...</van-loading> <van-loading type="spinner" color="#1677ff" size="24px">发票识别中...</van-loading>
</div> </div>
<van-cell-group> <van-cell-group class="invoice-popup-card">
<van-field class="more-text" label="发票文件" required> <van-field class="more-text" label="发票文件" required>
<template #input> <template #input>
<h5-file-upload <h5-file-upload
@@ -1040,13 +1052,15 @@ layout("/layouts/platform_h5.html"){
rows="2" rows="2"
autosize></van-field> autosize></van-field>
</van-cell-group> </van-cell-group>
<div class="invoice-tip" style="padding: 8px 16px;">上传发票文件后会自动识别发票信息;开启自动校验时会同步校验发票是否重复。</div> <div class="invoice-tip invoice-popup-tip">上传发票文件后会自动识别发票信息;开启自动校验时会同步校验发票是否重复。</div>
</div> </div>
<div class="invoice-popup-actions"> <div class="invoice-popup-actions">
<van-button native-type="button" block plain @click="invoiceDialogVisible = false">取消</van-button> <van-button native-type="button" block plain type="info" @click="closePopup('invoiceDialogVisible')">取消</van-button>
<van-button native-type="button" block type="primary" @click="saveInvoiceDetail">保存</van-button> <van-button native-type="button" block type="primary" @click="saveInvoiceDetail">保存</van-button>
</div> </div>
</van-popup> </van-popup>
<van-dialog :value="submitConfirmVisible" title="温馨提示" message="您确定要提交吗?审核通过后可前往 PC 端我的报销打印。" show-cancel-button :confirm-button-loading="formLoading" @confirm="submitConfirmed" @cancel="closeSubmitConfirm"></van-dialog>
<van-dialog :value="removeInvoiceConfirmVisible" title="提示" message="确定删除这条发票明细吗?" show-cancel-button @confirm="confirmRemoveInvoiceDetail" @cancel="closeRemoveInvoiceConfirm"></van-dialog>
</div> </div>
<script nonce="${cspNonce!}"> <script nonce="${cspNonce!}">
new Vue({ new Vue({
@@ -1127,6 +1141,20 @@ layout("/layouts/platform_h5.html"){
files: [] files: []
}, },
invoiceDialogVisible: false, invoiceDialogVisible: false,
submitConfirmVisible: false,
removeInvoiceConfirmVisible: false,
formLoading: false,
pendingSubmitAction: "",
pendingPaymentDetails: [],
pendingInvoiceRemoveIndex: -1,
historyLayerUnsubscribe: null,
popupKeys: [
"showReimburseFundSourcePicker", "showReimburseProjectPicker", "showClubPicker", "showCondolenceSearch",
"showCondolenceTypePicker", "showCondolenceWayPicker", "showCondolenceTimePicker", "showMarryTimePicker",
"showFertilityTimePicker", "showHospitalizationStartTimePicker", "showHospitalizationEndTimePicker",
"showDeathTimePicker", "showRelationshipPicker", "showActivityTimePicker", "showPaymentWayPicker",
"payerSelectShow", "invoiceDialogVisible"
],
invoiceDialogMode: "add", invoiceDialogMode: "add",
detailFormIndex: -1, detailFormIndex: -1,
lastRecognizedFileId: "", lastRecognizedFileId: "",
@@ -1239,7 +1267,7 @@ layout("/layouts/platform_h5.html"){
const file = newVal && newVal.length ? newVal[0] : null const file = newVal && newVal.length ? newVal[0] : null
const currentFileId = this.getInvoiceFileId(file) const currentFileId = this.getInvoiceFileId(file)
if (!currentFileId) { if (!currentFileId) {
this.lastRecognizedFileId = "" this.$set(this, "lastRecognizedFileId", "")
return return
} }
if (currentFileId === this.lastRecognizedFileId) { if (currentFileId === this.lastRecognizedFileId) {
@@ -1298,6 +1326,49 @@ layout("/layouts/platform_h5.html"){
closePopup(key) { closePopup(key) {
this.$set(this, key, false) this.$set(this, key, false)
}, },
// 所有选择器与发票弹窗共用全局历史栈,遮罩关闭和浏览器返回键都只关闭当前弹层。
syncPopupHistory(key, visible) {
const manager = window.h5HistoryLayerManager
if (!manager) return
const layerName = "union-reimburse-apply-" + key
if (visible) { manager.open(layerName); return }
manager.close(layerName)
},
onNavBack() {
const manager = window.h5HistoryLayerManager
if (manager && manager.stack.length) { manager.close(); return }
historyBack()
},
openSubmitConfirm(action, paymentDetails) {
this.$set(this, "pendingSubmitAction", action)
this.$set(this, "pendingPaymentDetails", paymentDetails)
const manager = window.h5HistoryLayerManager
if (manager) { manager.open("union-reimburse-apply-submit-confirm"); return }
this.$set(this, "submitConfirmVisible", true)
},
closeSubmitConfirm(afterClose) {
const manager = window.h5HistoryLayerManager
if (manager) { manager.close("union-reimburse-apply-submit-confirm", afterClose); return }
this.$set(this, "submitConfirmVisible", false)
if (afterClose) afterClose()
},
openRemoveInvoiceConfirm(index) {
this.$set(this, "pendingInvoiceRemoveIndex", index)
const manager = window.h5HistoryLayerManager
if (manager) { manager.open("union-reimburse-apply-remove-invoice-confirm"); return }
this.$set(this, "removeInvoiceConfirmVisible", true)
},
closeRemoveInvoiceConfirm(afterClose) {
const manager = window.h5HistoryLayerManager
if (manager) { manager.close("union-reimburse-apply-remove-invoice-confirm", afterClose); return }
this.$set(this, "removeInvoiceConfirmVisible", false)
if (afterClose) afterClose()
},
navigateToMine() {
const manager = window.h5HistoryLayerManager
if (manager && manager.closeAllAndNavigate("/platform/unionReimburse/mine/h5")) return
this.$pjaxReplace("/platform/unionReimburse/mine/h5")
},
openDatePicker(key) { openDatePicker(key) {
this.openPopup(key) this.openPopup(key)
}, },
@@ -1740,10 +1811,11 @@ layout("/layouts/platform_h5.html"){
}) })
}, },
removeInvoiceDetail(index) { removeInvoiceDetail(index) {
this.$dialog.confirm({ this.openRemoveInvoiceConfirm(index)
title: "提示", },
message: "确定删除这条发票明细吗?" confirmRemoveInvoiceDetail() {
}).then(() => { const index = this.pendingInvoiceRemoveIndex
this.closeRemoveInvoiceConfirm(() => {
this.formData.invoiceDetails.splice(index, 1) this.formData.invoiceDetails.splice(index, 1)
this.rebuildInvoiceSummary() this.rebuildInvoiceSummary()
}) })
@@ -1815,11 +1887,12 @@ layout("/layouts/platform_h5.html"){
this.$set(this, "searchKeyword", "") this.$set(this, "searchKeyword", "")
this.$set(this, "userOptions", []) this.$set(this, "userOptions", [])
}, },
async userRemoteMethod(event) { userRemoteMethod(event) {
if (event) { if (event) {
const userOptions = await this.selectQueryUser(event) this.selectQueryUser(event).then((userOptions) => {
this.$set(this, "userOptions", userOptions) this.$set(this, "userOptions", userOptions)
this.$set(this, "certifierUserSelectShow", true) this.$set(this, "certifierUserSelectShow", true)
})
} }
}, },
selectQueryUser(keyword) { selectQueryUser(keyword) {
@@ -1991,29 +2064,29 @@ layout("/layouts/platform_h5.html"){
} }
}) })
}, },
async onSave() { onSave() {
this.normalizeContactFields() this.normalizeContactFields()
this.normalizeCondolenceDateFields() this.normalizeCondolenceDateFields()
this.rebuildInvoiceSummary() this.rebuildInvoiceSummary()
const paymentDetails = this.preparePaymentDetailsForSubmit() const paymentDetails = this.preparePaymentDetailsForSubmit()
this.$set(this, "formLoading", true)
this.$axios.post("/platform/unionReimburse/apply/save", { data: JSON.stringify(this.buildSubmitFormData(paymentDetails)) }) this.$axios.post("/platform/unionReimburse/apply/save", { data: JSON.stringify(this.buildSubmitFormData(paymentDetails)) })
.then((res) => { .then((res) => {
this.$toast.clear() this.$toast.clear()
if (res.code === 0) { if (res.code === 0) {
this.$toast.success(res.msg) this.$toast.success(res.msg)
this.$pjaxReplace("/platform/unionReimburse/mine/h5") this.navigateToMine()
} else { } else {
this.$toast.fail(res.msg || "保存失败") this.$toast.fail(res.msg || "保存失败")
} }
}) }).catch((err) => {
.catch(err => { this.$toast.clear(); this.$toast.fail(this.getRequestErrorMessage(err, "保存失败"))
this.$toast.clear() }).finally(() => {
this.$toast.fail(this.getRequestErrorMessage(err, "保存失败")) this.$set(this, "formLoading", false)
}) })
}, },
// 提交 // 提交
async onSubmit() { onSubmit() {
this.normalizeContactFields() this.normalizeContactFields()
this.normalizeCondolenceDateFields() this.normalizeCondolenceDateFields()
this.rebuildInvoiceSummary() this.rebuildInvoiceSummary()
@@ -2029,30 +2102,11 @@ layout("/layouts/platform_h5.html"){
if (!this.validateInvoiceDetailsBeforeSubmit()) { if (!this.validateInvoiceDetailsBeforeSubmit()) {
return return
} }
this.$dialog.confirm({ this.openSubmitConfirm("submit", paymentDetails)
title: "温馨提示",
message: "您确定要提交吗?审核通过后可前往pc端我的报销打印!"
}).then(() => {
this.$axios.post("/platform/unionReimburse/apply/submit", { data: JSON.stringify(this.buildSubmitFormData(paymentDetails)) })
.then(res => {
this.$toast.clear()
if (res.code === 0) {
this.$toast.success("提交成功")
this.$pjaxReplace("/platform/unionReimburse/mine/h5")
} else {
this.$toast.fail(res.msg || "提交失败")
}
})
.catch(err => {
this.$toast.clear()
this.$toast.fail(this.getRequestErrorMessage(err, "提交失败"))
})
})
}) })
}, },
// 再次提交 // 再次提交
async onSubmitAgain() { onSubmitAgain() {
this.normalizeContactFields() this.normalizeContactFields()
this.normalizeCondolenceDateFields() this.normalizeCondolenceDateFields()
this.rebuildInvoiceSummary() this.rebuildInvoiceSummary()
@@ -2068,27 +2122,24 @@ layout("/layouts/platform_h5.html"){
if (!this.validateInvoiceDetailsBeforeSubmit()) { if (!this.validateInvoiceDetailsBeforeSubmit()) {
return return
} }
this.$dialog.confirm({ this.openSubmitConfirm("submitAgain", paymentDetails)
title: "温馨提示", })
message: "您确定要提交吗?审核通过后可前往pc端我的报销打印!" },
}).then(() => { submitConfirmed() {
this.$axios.post("/platform/unionReimburse/apply/submitAgain", { const action = this.pendingSubmitAction
data: JSON.stringify(this.buildSubmitFormData(paymentDetails)), const paymentDetails = this.pendingPaymentDetails
taskId: GetQueryString("taskId") this.closeSubmitConfirm(() => {
}).then(res => { this.$set(this, "formLoading", true)
this.$toast.clear() const api = action === "submitAgain" ? "/platform/unionReimburse/apply/submitAgain" : "/platform/unionReimburse/apply/submit"
if (res.code === 0) { const params = {data: JSON.stringify(this.buildSubmitFormData(paymentDetails))}
this.$toast.success("提交成功") if (action === "submitAgain") params.taskId = GetQueryString("taskId")
setTimeout(() => { this.$axios.post(api, params).then((res) => {
this.$pjaxReplace("/platform/unionReimburse/mine/h5") this.$toast.clear()
}, 1500) if (res.code === 0) { this.$toast.success("提交成功"); this.navigateToMine() } else { this.$toast.fail(res.msg || "提交失败") }
} else { }).catch((err) => {
this.$toast.fail(res.msg || "提交失败") this.$toast.clear(); this.$toast.fail(this.getRequestErrorMessage(err, "提交失败"))
} }).finally(() => {
}).catch(err => { this.$set(this, "formLoading", false)
this.$toast.clear()
this.$toast.fail(this.getRequestErrorMessage(err, "提交失败"))
})
}) })
}) })
}, },
@@ -2218,46 +2269,34 @@ layout("/layouts/platform_h5.html"){
const project = this.descOptions.find(item => item.reimburseProject === projectCode) const project = this.descOptions.find(item => item.reimburseProject === projectCode)
return project ? project.projectTip : "" return project ? project.projectTip : ""
}, },
async initDictOptions() { initDictOptions() {
// 报销类别 return this.$businessTool.getDictOptions("UNION_REIMBURSE_TYPE").then((reimburseTypeData) => {
const reimburseTypeData = await this.$businessTool.getDictOptions("UNION_REIMBURSE_TYPE") this.$set(this, "reimburseTypeColumns", reimburseTypeData.map(item => ({value: item.code, text: item.name})))
this.reimburseTypeColumns = reimburseTypeData.map(item => { return this.$businessTool.getDictOptions("UNION_REIMBURSE_PAYMENT_WAY")
return { value: item.code, text: item.name } }).then((paymentWayData) => {
this.$set(this, "paymentWayColumns", paymentWayData.map(item => ({value: item.code, text: item.name})))
return this.$businessTool.getDictOptions("UNION_REIMBURSE_PROJECT")
}).then((reimburseProjectData) => {
this.$set(this, "reimburseProjectColumns", reimburseProjectData.map(item => ({value: item.code, text: item.name})))
this.$set(this, "reimburseProjectList", reimburseProjectData)
return this.$businessTool.getDictOptions("UNION_REIMBURSE_FUND_SOURCE")
}).then((reimburseFundSourceData) => {
this.$set(this, "reimburseFundSourceColumns", reimburseFundSourceData.map(item => ({value: item.code, text: item.name})))
this.filterReimburseFundSources()
}) })
// 支付方式
const paymentWayData = await this.$businessTool.getDictOptions("UNION_REIMBURSE_PAYMENT_WAY")
this.paymentWayColumns = paymentWayData.map(item => {
return { value: item.code, text: item.name }
})
// 报销项目
const reimburseProjectData = await this.$businessTool.getDictOptions("UNION_REIMBURSE_PROJECT")
this.reimburseProjectColumns = reimburseProjectData.map(item => {
return { value: item.code, text: item.name }
})
this.reimburseProjectList = reimburseProjectData // 保存原始数据
// 报销经费来源
const reimburseFundSourceData = await this.$businessTool.getDictOptions("UNION_REIMBURSE_FUND_SOURCE")
this.reimburseFundSourceColumns = reimburseFundSourceData.map(item => {
return { value: item.code, text: item.name }
})
// 添加经费来源区分
await this.filterReimburseFundSources()
}, },
// 查询慰问对象 // 查询慰问对象
queryCondolence(keyword) { queryCondolence(keyword) {
this.$axios.post("/platform/UnionReimburse/apply/listUser", { keyword: keyword }).then((res) => { this.$axios.post("/platform/UnionReimburse/apply/listUser", { keyword: keyword }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.condolenceOptions = res.data this.$set(this, "condolenceOptions", res.data)
this.condolenceColumns = res.data.map(item => { this.$set(this, "condolenceColumns", res.data.map(item => {
return { return {
text: item.userName + "" + item.loginName + "" + "" + item.unitName + "" + "" + item.sex + "", text: item.userName + "" + item.loginName + "" + "" + item.unitName + "" + "" + item.sex + "",
value: item.id value: item.id
} }
}) }))
} }
}) })
}, },
@@ -2305,10 +2344,10 @@ layout("/layouts/platform_h5.html"){
queryCondolenceType() { queryCondolenceType() {
return this.$axios.post("/platform/unionReimburse/apply/queryCondolenceType") return this.$axios.post("/platform/unionReimburse/apply/queryCondolenceType")
.then((resp) => { .then((resp) => {
this.typeOptions = resp.data this.$set(this, "typeOptions", resp.data)
this.condolenceTypeColumns = resp.data.map(item => { this.$set(this, "condolenceTypeColumns", resp.data.map(item => {
return { text: item.name, value: item.id } return { text: item.name, value: item.id }
}) }))
}) })
}, },
// 慰问时间确认事件 // 慰问时间确认事件
@@ -2328,10 +2367,10 @@ layout("/layouts/platform_h5.html"){
// 查询社团 // 查询社团
queryClubs() { queryClubs() {
return this.$businessTool.listClubByRole().then((res) => { return this.$businessTool.listClubByRole().then((res) => {
this.clubOptions = res this.$set(this, "clubOptions", res)
this.clubColumns = res.map(item => { this.$set(this, "clubColumns", res.map(item => {
return { text: item.clubName, value: item.id } return { text: item.clubName, value: item.id }
}) }))
if (res && res.length === 1 && this.formData.reimburseFundSource === "UNION_REIMBURSE_FUND_SOURCE_3" && !this.formData.clubId) { if (res && res.length === 1 && this.formData.reimburseFundSource === "UNION_REIMBURSE_FUND_SOURCE_3" && !this.formData.clubId) {
this.$set(this.formData, "clubId", res[0].id) this.$set(this.formData, "clubId", res[0].id)
this.$set(this.formData, "clubName", res[0].clubName) this.$set(this.formData, "clubName", res[0].clubName)
@@ -2404,7 +2443,7 @@ layout("/layouts/platform_h5.html"){
}) })
}, },
// 添加经费来源区分 // 添加经费来源区分
async filterReimburseFundSources() { filterReimburseFundSources() {
const filteredSources = [] const filteredSources = []
// 原角色过滤逻辑保留如下,手机端现统一改为按权限控制,与 PC 端保持一致 // 原角色过滤逻辑保留如下,手机端现统一改为按权限控制,与 PC 端保持一致
@@ -2459,16 +2498,16 @@ layout("/layouts/platform_h5.html"){
} }
// 更新经费来源选项 // 更新经费来源选项
this.reimburseFundSourceColumns = filteredSources this.$set(this, "reimburseFundSourceColumns", filteredSources)
}, },
init() { init() {
this.bizId = GetQueryString("bizId") this.$set(this, "bizId", GetQueryString("bizId"))
if (this.bizId) { if (this.bizId) {
this.$axios.post("/platform/unionReimburse/apply/info", { id: this.bizId }).then((res) => { this.$axios.post("/platform/unionReimburse/apply/info", { id: this.bizId }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
// 先保存原始数据 // 先保存原始数据
const originalData = { ...res.data } const originalData = { ...res.data }
this.formData = this.ensureFormData(res.data) this.$set(this, "formData", this.ensureFormData(res.data))
this.normalizeLoadedFormData(originalData) this.normalizeLoadedFormData(originalData)
// 处理报销类别显示 // 处理报销类别显示
@@ -2560,7 +2599,7 @@ layout("/layouts/platform_h5.html"){
const user = this.$store.state.user const user = this.$store.state.user
this.$set(this.formData, "userName", user.username) this.$set(this.formData, "userName", user.username)
this.$set(this.formData, "loginName", user.loginname) this.$set(this.formData, "loginName", user.loginname)
this.$set(this.formData, "mobile", user.mobile) this.$set(this.formData, "mobile", user.mobile || "")
this.$set(this.formData, "userId", user.id) this.$set(this.formData, "userId", user.id)
this.$set(this.formData, "unitId", user.unitId) this.$set(this.formData, "unitId", user.unitId)
this.$set(this.formData, "unitName", user.unit.name) this.$set(this.formData, "unitName", user.unit.name)
@@ -2579,12 +2618,27 @@ layout("/layouts/platform_h5.html"){
} }
}, },
async created() { created() {
await this.initDictOptions() const manager = window.h5HistoryLayerManager
await this.queryProjectDesc() if (manager) {
await this.queryCondolenceType() manager.ensureRegistered("union-reimburse-apply")
await this.queryClubs() this.$set(this, "historyLayerUnsubscribe", manager.subscribe((layers) => {
await this.init() this.popupKeys.forEach((key) => {
this.$set(this, key, layers.includes("union-reimburse-apply-" + key))
})
this.$set(this, "submitConfirmVisible", layers.includes("union-reimburse-apply-submit-confirm"))
this.$set(this, "removeInvoiceConfirmVisible", layers.includes("union-reimburse-apply-remove-invoice-confirm"))
}))
}
this.popupKeys.forEach((key) => {
this.$watch(key, (visible) => { this.syncPopupHistory(key, visible) })
})
this.initDictOptions().then(() => this.queryProjectDesc()).then(() => this.queryCondolenceType()).then(() => this.queryClubs()).then(() => this.init())
},
beforeDestroy() {
if (this.historyLayerUnsubscribe) this.historyLayerUnsubscribe()
const manager = window.h5HistoryLayerManager
if (manager) manager.unregister("union-reimburse-apply")
} }
}) })
</script> </script>
@@ -2,71 +2,57 @@
layout("/layouts/platform_h5.html"){ layout("/layouts/platform_h5.html"){
#--> #-->
<style> <style id="style-union-reimburse-h5">
.state-primary { <!--#include('../common/unionReimburse.css'){}#-->
color: #409eff;
}
.state-success {
color: #67c23a;
}
.state-danger {
color: #f56c6c;
}
</style> </style>
<div id="app" v-cloak> <div id="app" v-cloak class="union-reimburse-page">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="报销统计" placeholder fixed></van-nav-bar> <van-nav-bar @click-left="onNavBack" left-arrow left-text="返回" title="报销统计" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px"> <van-sticky offset-top="46px">
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false"> <div class="union-reimburse-sticky">
<van-dropdown-item :options="yearList" @change="doSearch" v-model="pageForm.year"></van-dropdown-item> <van-search class="union-reimburse-search" :reverse-color="false" :show-action="false" @search="doSearch" @clear="doSearch" input-align="left" placeholder="请输入姓名或工号搜索" v-model="pageForm.searchKeyword"></van-search>
</van-dropdown-menu> <van-dropdown-menu class="union-reimburse-filter" :close-on-click-outside="false" :close-on-click-overlay="false">
<van-search <van-dropdown-item :options="yearList" @change="doSearch" v-model="pageForm.year"></van-dropdown-item>
:reverse-color="false" <van-dropdown-item :options="reimburseProjectOptions" @change="doSearch" v-model="pageForm.reimburseProject"></van-dropdown-item>
:show-action="false" </van-dropdown-menu>
@search="doSearch" </div>
input-align="left"
placeholder="请输入经办人搜索"
v-model="pageForm.userName"
></van-search>
</van-sticky> </van-sticky>
<main class="union-reimburse-list-content">
<table-list api="/platform/unionReimburse/collect/pageData" :page_form.sync="pageForm" ref="tableListRef" <table-list api="/platform/unionReimburse/collect/pageData" :page_form.sync="pageForm" ref="tableListRef" :show_loading_overlay="false"
@ready="doSearch" > @ready="doSearch" @loading-change="onLoadingChange">
<template #header="{row}"><div v-if="row" class="union-reimburse-card-header"><div class="union-reimburse-card-header__heading"><div class="union-reimburse-card-header__title">{{row.userName || '--'}}</div><div class="union-reimburse-card-header__sub">工号:{{row.loginName || '--'}}</div></div><van-tag :class="['union-reimburse-card-header__status', getStatusClass(row.stateId)]">{{getStateName(row.stateId)}}</van-tag></div></template>
<template v-slot="{index,row}"> <template v-slot="{index,row}">
<table-column label="经办人工号">{{row.loginName}}</table-column> <table-column label="报销项目">{{getReimburseProjectName(row.reimburseProject) || '--'}}</table-column>
<table-column label="姓名">{{row.userName}}</table-column> <table-column label="" label_suffix="">{{getRemarkText(row) || '--'}}</table-column>
<table-column label="所属工会">{{row.unionName}}</table-column> <table-column label="所属工会">{{row.unionName || '--'}}</table-column>
<table-column label="所属单位">{{row.unitName}}</table-column> <table-column label="所属单位">{{row.unitName || '--'}}</table-column>
<table-column label="申请时间">{{formatCreateTime(row.createTime)}}</table-column> <table-column label="申请时间">{{formatCreateTime(row.createTime) || '--'}}</table-column>
<table-column label="报销项目">{{getReimburseProjectName(row.reimburseProject)}}</table-column> <table-column label="申请金额">{{formatMoney(row.money)}}</table-column>
<table-column label="备注">{{getRemarkText(row)}}</table-column>
<table-column label="申请状态">
<span :class="getStateClass(row.stateId)">{{getStateName(row.stateId)}}</span>
</table-column>
</template> </template>
<template #actions="{index,row}"> <template #actions="{index,row}">
<div class="action-btn" @click="onView(row)"> <div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i> <van-icon name="eye-o"></van-icon>
<span>查看</span> <span>查看</span>
</div> </div>
<!-- <div class="action-btn" @click="onEdit(row)" v-if="row.taskKey === 'startTask' || !row.instanceId">--> <!-- <div class="action-btn" @click="onEdit(row)" v-if="row.taskKey === 'startTask' || !row.instanceId">-->
<!-- <i class="fa fa-edit"></i>--> <!-- <i class="fa fa-edit"></i>-->
<!-- <span>编辑</span>--> <!-- <span>编辑</span>-->
<!-- </div>--> <!-- </div>-->
<div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(item)"> <div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)">
<i class="fa fa-undo"></i> <van-icon name="revoke"></van-icon>
<span>撤回</span> <span>撤回</span>
</div> </div>
<div class="action-btn delete" @click="onDelete(row)" v-if="row.taskKey === 'startTask' || !row.instanceId"> <div class="action-btn delete" @click="onDelete(row)" v-if="row.taskKey === 'startTask' || !row.instanceId">
<i class="fa fa-trash"></i> <van-icon name="delete-o"></van-icon>
<span>删除</span> <span>删除</span>
</div> </div>
</template> </template>
<template #empty><div v-if="listLoading" class="union-reimburse-list-skeleton"><div v-for="item in 2" :key="item" class="union-reimburse-skeleton-card"><div class="union-reimburse-skeleton-line union-reimburse-skeleton-line--title"></div><div class="union-reimburse-skeleton-line"></div><div class="union-reimburse-skeleton-line union-reimburse-skeleton-line--short"></div></div></div><div v-else class="union-reimburse-empty"><img src="/assets/mobile/img/unionReimburse/union-reimburse-empty.png" alt="暂无报销统计记录插画"><div class="union-reimburse-empty__title">暂无报销统计记录</div><div class="union-reimburse-empty__hint">当前筛选条件下暂时没有报销记录</div></div></template>
</table-list> </table-list>
</main>
<union-reimburse-info ref="unionReimburseInfoRef"></union-reimburse-info> <union-reimburse-info ref="unionReimburseInfoRef" history-layer-name="union-reimburse-collect-info"></union-reimburse-info>
<van-dialog :value="confirmVisible" title="提示" :message="confirmMessage" show-cancel-button :confirm-button-loading="formLoading" @confirm="confirmPendingAction" @cancel="closeConfirm"></van-dialog>
</div> </div>
<script nonce="${cspNonce!}"> <script nonce="${cspNonce!}">
@@ -80,18 +66,32 @@ layout("/layouts/platform_h5.html"){
}, },
data() { data() {
return { return {
viewShow: false, listLoading: true,
formLoading: false,
confirmVisible: false,
pendingAction: "",
pendingRow: null,
historyLayerUnsubscribe: null,
yearList: [], yearList: [],
reimburseProjectOptions: [
{text: "全部项目", value: ""},
{text: "慰问", value: "UNION_REIMBURSE_PROJECT_1"},
{text: "文体活动", value: "UNION_REIMBURSE_PROJECT_2"},
{text: "日常活动", value: "UNION_REIMBURSE_PROJECT_3"},
{text: "专项活动", value: "UNION_REIMBURSE_PROJECT_4"}
],
pageForm: { pageForm: {
pageNumber: 1, pageNumber: 1,
pageSize: 10, pageSize: 10,
totalCount: 0, totalCount: 0,
searchKeyword: "", searchKeyword: "",
year: "", year: "",
reimburseProject: ""
}, },
infoShow: false infoShow: false
} }
}, },
computed: { confirmMessage() { return this.pendingAction === "revoke" ? "您确定要撤回申请吗?" : "您确定要删除吗?" } },
methods: { methods: {
// 手机端统计列表字段展示与我的报销列表保持一致。 // 手机端统计列表字段展示与我的报销列表保持一致。
getReimburseProjectName(reimburseProject) { getReimburseProjectName(reimburseProject) {
@@ -133,58 +133,35 @@ layout("/layouts/platform_h5.html"){
} }
return stateId || "" return stateId || ""
}, },
getStateClass(stateId) { getStatusClass(stateId) { if (stateId == 3) return "union-reimburse-card-header__status--success"; if (stateId == 4 || stateId == 5) return "union-reimburse-card-header__status--danger"; return "union-reimburse-card-header__status--pending" },
if (stateId == 3) {
return "state-success"
}
if (stateId == 4) {
return "state-danger"
}
return "state-primary"
},
formatCreateTime(value) { formatCreateTime(value) {
return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : "" return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : ""
}, },
formatMoney(value) {
if (value === null || value === undefined || value === "") return "--"
const amount = Number(value)
return isNaN(amount) ? value : amount.toFixed(2) + "元"
},
onView(row) { onView(row) {
this.showApprovalForm = false this.$refs.unionReimburseInfoRef.onOpen(row, "报销统计详情")
this.$refs.unionReimburseInfoRef.onOpen(row)
}, },
onEdit(row) { onEdit(row) {
this.$pjaxReplace("/platform/unionReimburse/apply/h5?bizId=" + row.id + "&taskId=" + row.startTaskId) this.$pjaxReplace("/platform/unionReimburse/apply/h5?bizId=" + row.id + "&taskId=" + row.startTaskId)
}, },
onRevoke(row){ onLoadingChange(state) { this.$set(this, "listLoading", state.loading) },
this.$dialog onNavBack() { const manager = window.h5HistoryLayerManager; if (manager && manager.stack.length) { manager.close(); return } historyBack() },
.confirm({ openConfirm(action, row) { this.$set(this, "pendingAction", action); this.$set(this, "pendingRow", row); const manager = window.h5HistoryLayerManager; if (manager) { manager.open("union-reimburse-collect-confirm"); return } this.$set(this, "confirmVisible", true) },
title: "提示", closeConfirm(afterClose) { const manager = window.h5HistoryLayerManager; if (manager) { manager.close("union-reimburse-collect-confirm", afterClose); return } this.$set(this, "confirmVisible", false); if (afterClose) afterClose() },
message: "您确定要撤销申请吗?" confirmPendingAction() { const action = this.pendingAction; const row = this.pendingRow; this.closeConfirm(() => { if (action === "revoke") { this.revoke(row) } else { this.deleteApplication(row) } }) },
}) onRevoke(row) { this.openConfirm("revoke", row) },
.then(() => { revoke(row) { this.$set(this, "formLoading", true); this.$axios.post("/flow/common/revokeTask", {taskId: row.startTaskId}).then((resp) => { if (resp.code === 0) { this.$toast.success(resp.msg); this.doSearch() } else { this.$toast.fail(resp.msg) } }).finally(() => { this.$set(this, "formLoading", false) }) },
this.$axios.post("/flow/common/revokeTask", {taskId: row.startTaskId}).then((resp) => { onDelete(row) { this.openConfirm("delete", row) },
this.$toast.success(resp.msg) deleteApplication(row) { this.$set(this, "formLoading", true); this.$axios.post("/platform/unionReimburse/mine/delete", {id: row.id}).then((resp) => { if (resp.code === 0) { this.$toast.success(resp.msg); this.doSearch() } else { this.$toast.fail(resp.msg) } }).finally(() => { this.$set(this, "formLoading", false) }) },
this.doSearch()
})
})
},
onDelete(row) {
this.$dialog
.confirm({
title: "提示",
message: "您确定要删除吗?"
})
.then(() => {
this.$axios.post("/platform/unionReimburse/mine/delete", {id: row.id}).then((resp) => {
if (resp.code === 0) {
this.$toast.success(resp.msg)
this.doSearch()
}
})
})
},
doSearch() { doSearch() {
this.$nextTick(() => { this.$nextTick(() => {
this.pageForm.pageNumber = 1 this.$set(this.pageForm, "pageNumber", 1)
this.pageForm.totalCount = 0 this.$set(this.pageForm, "totalCount", 0)
this.$refs.tableListRef.doSearch() this.$refs.tableListRef.doSearch()
}) })
}, },
@@ -198,7 +175,13 @@ layout("/layouts/platform_h5.html"){
}, },
created() { created() {
this.initData() this.initData()
const manager = window.h5HistoryLayerManager
if (manager) { manager.ensureRegistered("union-reimburse-collect"); this.$set(this, "historyLayerUnsubscribe", manager.subscribe((layers) => { this.$set(this, "confirmVisible", layers.includes("union-reimburse-collect-confirm")) })) }
},
beforeDestroy() {
if (this.historyLayerUnsubscribe) this.historyLayerUnsubscribe()
const manager = window.h5HistoryLayerManager
if (manager) manager.unregister("union-reimburse-collect")
} }
}) })
</script> </script>
@@ -0,0 +1,472 @@
.union-reimburse-page {
min-height: 100vh;
padding-bottom: calc(24px + env(safe-area-inset-bottom));
box-sizing: border-box;
background: #f3f7fd;
color: #40526a;
}
.union-reimburse-page .van-nav-bar,
.union-reimburse-page .van-sticky--fixed { background: #fff; }
.union-reimburse-page .van-nav-bar__title {
color: #172033;
font-size: 16px;
font-weight: 600;
}
.union-reimburse-page .van-nav-bar .van-icon,
.union-reimburse-page .van-nav-bar .van-nav-bar__text { color: #1989fa; }
.union-reimburse-sticky {
padding: 22px 12px 1px;
box-sizing: border-box;
background: #f3f7fd;
}
.union-reimburse-search {
margin: 0 0 8px;
padding: 0;
overflow: hidden;
border-radius: 12px;
background: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .1);
}
.union-reimburse-search .van-search__content {
height: 42px;
padding-left: 12px;
align-items: center;
border-radius: 12px;
background: #fff;
}
.union-reimburse-search .van-field__control { color: #263548; font-size: 14px; }
.union-reimburse-search .van-field__control::placeholder { color: #a7b0bf; }
.union-reimburse-filter {
margin-bottom: 8px;
overflow: hidden;
border-radius: 10px;
background: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
}
.union-reimburse-filter .van-dropdown-menu__bar { height: 44px; box-shadow: none; }
.union-reimburse-filter .van-dropdown-menu__item,
.union-reimburse-filter .van-dropdown-menu__title { height: 44px; }
.union-reimburse-filter .van-dropdown-menu__item { align-items: center; justify-content: center; }
.union-reimburse-filter .van-dropdown-menu__title {
display: inline-flex;
align-items: center;
color: #66758a;
font-size: 14px;
line-height: 20px;
}
.union-reimburse-filter .van-dropdown-menu__title--active,
.union-reimburse-filter .van-dropdown-item__option--active,
.union-reimburse-filter .van-dropdown-item__option--active .van-dropdown-item__icon { color: #1989fa; }
.union-reimburse-filter .van-dropdown-item__content {
width: calc(100% - 24px);
margin: 0 12px;
overflow: hidden;
box-sizing: border-box;
border-radius: 0 0 12px 12px;
box-shadow: 0 10px 24px rgba(31, 65, 108, .14);
}
.union-reimburse-filter .van-dropdown-item__option {
display: flex;
min-height: 48px;
padding: 0 16px;
align-items: center;
color: #46566c;
font-size: 14px;
}
.union-reimburse-list-content { padding: 0 12px; }
.union-reimburse-page .table-list-container { margin-top: 0; padding-bottom: 2px; }
.union-reimburse-page .table-list-container .table-list-item {
margin-bottom: 12px;
padding: 14px;
border: 1px solid #e9eff7;
border-radius: 12px;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
}
.union-reimburse-card-header { display: flex; margin-bottom: 8px; align-items: flex-start; justify-content: space-between; }
.union-reimburse-card-header__heading { min-width: 0; padding-right: 10px; flex: 1; }
.union-reimburse-card-header__title {
overflow: hidden;
color: #253247;
font-size: 15px;
font-weight: 600;
line-height: 22px;
text-overflow: ellipsis;
white-space: nowrap;
}
.union-reimburse-card-header__sub { margin-top: 1px; color: #9ba6b6; font-size: 12px; line-height: 18px; }
.union-reimburse-card-header__status { display: inline-flex; flex: none; }
.union-reimburse-card-header__status.van-tag,
.union-reimburse-card-header__status .van-tag {
min-height: 24px;
padding: 3px 9px;
border: 0;
border-radius: 12px;
box-sizing: border-box;
font-size: 12px;
line-height: 18px;
}
.union-reimburse-card-header__status--pending { color: #1989fa; background: #eaf4ff; }
/* 报销成功与社团审核页 ProcessInstanceStateEnum 的“已完成”保持一致。 */
.union-reimburse-card-header__status--success { color: #67c23a; background: #f0f9eb; }
.union-reimburse-card-header__status--danger { color: #e35d5d; background: #fff0f0; }
.union-reimburse-page .table-list-container .table-list-item .item-actions {
margin-top: 10px;
padding-top: 10px;
border-top-color: #edf1f6;
}
.union-reimburse-page .table-list-container .table-list-item .action-btn {
min-height: 28px;
padding: 4px 11px;
border-radius: 14px;
color: #1989fa;
background: #eaf4ff;
font-size: 12px;
line-height: 18px;
}
.union-reimburse-page .table-list-container .table-list-item .action-btn.delete { color: #f04444; background: #fff0f0; }
.union-reimburse-page .table-list-container .table-list-item .action-btn.review { color: #1989fa; background: #eaf4ff; }
.union-reimburse-page .table-list-container .table-list-item .action-btn .van-icon { margin-right: 5px; font-size: 13px; }
.union-reimburse-page .empty-state { padding: 0; }
.union-reimburse-list-skeleton { padding: 0; }
.union-reimburse-skeleton-card {
margin-bottom: 12px;
padding: 14px;
border: 1px solid #e9eff7;
border-radius: 12px;
background: #fff;
}
.union-reimburse-skeleton-line {
height: 13px;
margin-bottom: 12px;
border-radius: 7px;
background: linear-gradient(90deg, #f2f3f5 25%, #e6e8eb 37%, #f2f3f5 63%);
background-size: 400% 100%;
animation: union-reimburse-skeleton-loading 1.4s ease infinite;
}
.union-reimburse-skeleton-line--title { width: 46%; height: 16px; }
.union-reimburse-skeleton-line--short { width: 60%; }
.union-reimburse-empty {
display: flex;
min-height: 280px;
padding: 46px 18px 36px;
align-items: center;
justify-content: center;
box-sizing: border-box;
flex-direction: column;
text-align: center;
}
.union-reimburse-empty img { display: block; width: 86%; max-width: 290px; height: auto; object-fit: contain; }
.union-reimburse-empty__title { margin-top: 14px; color: #50627a; font-size: 15px; font-weight: 600; line-height: 22px; }
.union-reimburse-empty__hint { margin-top: 5px; color: #9aa7b8; font-size: 12px; line-height: 18px; }
/* 申请页沿用社团申请的卡片层级,保留报销业务原有字段与交互。 */
.union-reimburse-form { padding-bottom: 0; }
.union-reimburse-form .custom-nav { box-shadow: none; }
.union-reimburse-form .form-container { padding: 12px 15px calc(86px + env(safe-area-inset-bottom)); }
.union-reimburse-form .form-section {
margin: 0 0 11px;
overflow: hidden;
border: 1px solid #e9eff7;
border-radius: 10px;
background: #fff;
box-shadow: 0 3px 11px rgba(51, 87, 126, .08);
}
.union-reimburse-form .form-section .van-cell-group__title,
.union-reimburse-form .payment-toolbar,
.union-reimburse-form .invoice-toolbar {
position: relative;
min-height: 42px;
margin: 0;
padding: 12px 14px 8px 21px;
box-sizing: border-box;
color: #354255;
font-size: 15px;
font-weight: 600;
line-height: 22px;
background: #fff;
}
.union-reimburse-form .form-section__title {
position: relative;
min-height: 41px;
margin: 0;
padding: 13px 15px 8px 24px;
box-sizing: border-box;
color: #1f2d3d;
font-size: 14px;
font-weight: 600;
line-height: 20px;
background: #fff;
}
.union-reimburse-form .form-section__title::before {
position: absolute;
top: 16px;
left: 15px;
width: 3px;
height: 14px;
border-radius: 2px;
background: #1989fa;
content: '';
}
.union-reimburse-form .form-section .van-cell-group__title::before,
.union-reimburse-form .payment-toolbar::before,
.union-reimburse-form .invoice-toolbar::before {
position: absolute;
top: 15px;
bottom: 11px;
left: 12px;
width: 2px;
border-radius: 2px;
background: #1989fa;
content: '';
}
.union-reimburse-form .form-section .van-cell { min-height: 46px; padding: 11px 15px; background: transparent; }
.union-reimburse-form .form-section .van-cell::after { right: 15px; left: 15px; border-color: #eef2f7; }
.union-reimburse-form .form-section .van-field__label { width: 105px; color: #596779; font-size: 13px; line-height: 24px; }
.union-reimburse-form .payment-detail-item .van-cell--required::before { left: 6px; color: #f06464; }
.union-reimburse-form .form-section .van-field__control { color: #40526a; font-size: 14px; line-height: 24px; }
.union-reimburse-form .form-section .van-field__control::placeholder { color: #a7b0bf; }
.union-reimburse-form .more-text { display: block; }
.union-reimburse-form .more-text .van-field__label { width: auto; margin-bottom: 7px; }
.union-reimburse-form .more-text .van-field__control {
min-height: 88px;
padding: 10px 12px;
box-sizing: border-box;
border: 1px solid #c9d5e5;
border-radius: 10px;
background: #f7f9fd;
}
.union-reimburse-form .payment-section,
.union-reimburse-form .invoice-section { padding: 0 0 14px; }
.union-reimburse-form .payment-toolbar,
.union-reimburse-form .invoice-toolbar { display: flex; align-items: center; justify-content: space-between; }
.union-reimburse-form .payment-toolbar .van-button,
.union-reimburse-form .invoice-toolbar .van-button {
height: 28px;
padding: 0 12px;
border: 0;
border-radius: 14px;
color: #1989fa;
background: #eaf4ff;
}
.union-reimburse-form .payment-detail-item,
.union-reimburse-form .invoice-detail-item {
margin: 8px 14px 0;
overflow: hidden;
border: 1px solid #e6ebf2;
border-radius: 10px;
background: #fff;
box-shadow: none;
}
.union-reimburse-form .invoice-detail-item { padding: 0; }
.union-reimburse-form .invoice-detail-title {
min-height: 38px;
padding: 0 12px;
box-sizing: border-box;
color: #40526a;
font-size: 14px;
font-weight: 600;
line-height: 22px;
background: #f7f9fd;
border-bottom: 1px solid #eef2f7;
}
.union-reimburse-form .invoice-detail-line {
margin: 0;
padding: 8px 12px 0;
color: #718096;
font-size: 13px;
line-height: 20px;
word-break: break-all;
}
.union-reimburse-form .invoice-detail-line:last-of-type { padding-bottom: 8px; }
.union-reimburse-form .invoice-detail-actions,
.union-reimburse-form .payment-detail-actions {
min-height: 28px;
margin-top: 8px;
padding: 9px 12px;
box-sizing: border-box;
border-top: 1px solid #eef2f7;
background: #fff;
}
.union-reimburse-form .invoice-detail-actions { justify-content: flex-end; gap: 6px; }
.union-reimburse-form .invoice-detail-actions .van-button,
.union-reimburse-form .payment-detail-actions .payment-delete-btn {
height: 28px;
min-width: 52px;
padding: 0 10px;
border: 0;
border-radius: 14px;
font-size: 12px;
line-height: 18px;
}
.union-reimburse-form .invoice-detail-actions .van-button--info,
.union-reimburse-form .invoice-detail-actions .van-button--primary {
color: #1989fa;
background: #eaf4ff;
}
.union-reimburse-form .invoice-detail-actions .van-button--danger,
.union-reimburse-form .payment-detail-actions .payment-delete-btn {
color: #f04444;
background: #fff0f0;
}
.union-reimburse-form .invoice-detail-actions .van-icon,
.union-reimburse-form .payment-detail-actions .payment-delete-btn .van-icon {
margin-right: 4px;
font-size: 13px;
}
.union-reimburse-form .payment-detail-item .van-cell { padding-right: 12px; padding-left: 12px; }
.union-reimburse-form .payment-detail-title {
min-height: 38px;
padding: 0 12px;
color: #40526a;
background: #f7f9fd;
border-bottom-color: #eef2f7;
}
.union-reimburse-form .invoice-tip { margin: 8px 14px 10px; border-color: #dce9f8; color: #718096; background: #f5f9ff; }
.union-reimburse-form .form-actions {
gap: 10px;
padding: 10px 15px calc(10px + env(safe-area-inset-bottom));
box-shadow: 0 -2px 12px rgba(43, 73, 112, .09);
}
.union-reimburse-form .form-actions .van-button { height: 40px; border-radius: 8px; font-size: 14px; font-weight: 500; }
.union-reimburse-form .form-actions .van-button--primary { border-color: #1989fa; background: #1989fa; box-shadow: none; }
.union-reimburse-form .form-actions .van-button--info.van-button--plain { color: #1989fa; border-color: #1989fa; }
/* 新增、编辑发票弹框与申请页共用卡片、字段和操作按钮视觉规范。 */
.union-reimburse-form .invoice-popup {
overflow: hidden;
background: #f3f7fd;
}
.union-reimburse-form .invoice-popup .van-nav-bar { flex: none; box-shadow: 0 1px 6px rgba(43, 73, 112, .08); }
.union-reimburse-form .invoice-popup .van-nav-bar__title { color: #1f2d3d; font-size: 16px; font-weight: 600; }
.union-reimburse-form .invoice-popup-body {
padding: 12px 15px;
box-sizing: border-box;
background: #f3f7fd;
}
.union-reimburse-form .invoice-popup-card {
overflow: hidden;
border: 1px solid #e9eff7;
border-radius: 10px;
background: #fff;
box-shadow: 0 3px 11px rgba(51, 87, 126, .08);
}
.union-reimburse-form .invoice-popup-card .van-cell { min-height: 46px; padding: 11px 15px; background: transparent; }
.union-reimburse-form .invoice-popup-card .van-cell::after { right: 15px; left: 15px; border-color: #eef2f7; }
.union-reimburse-form .invoice-popup-card .van-field__label { width: 105px; color: #596779; font-size: 13px; line-height: 24px; }
.union-reimburse-form .invoice-popup-card .van-field__control { color: #40526a; font-size: 14px; line-height: 24px; }
.union-reimburse-form .invoice-popup-card .van-field__control::placeholder { color: #a7b0bf; }
.union-reimburse-form .invoice-popup-card .more-text { display: block; }
.union-reimburse-form .invoice-popup-card .more-text .van-field__label { width: auto; margin-bottom: 7px; }
.union-reimburse-form .invoice-popup-card .more-text .van-field__control {
min-height: 88px;
padding: 10px 12px;
box-sizing: border-box;
border: 1px solid #c9d5e5;
border-radius: 10px;
background: #f7f9fd;
}
.union-reimburse-form .invoice-popup-tip {
margin: 10px 0 0;
padding: 9px 11px;
border-color: #dce9f8;
color: #718096;
background: #f5f9ff;
}
.union-reimburse-form .invoice-popup-actions {
flex: none;
gap: 10px;
padding: 10px 15px calc(10px + env(safe-area-inset-bottom));
box-shadow: 0 -2px 12px rgba(43, 73, 112, .09);
}
.union-reimburse-form .invoice-popup-actions .van-button { height: 40px; border-radius: 8px; font-size: 14px; font-weight: 500; }
.union-reimburse-form .invoice-popup-actions .van-button--primary { border-color: #1989fa; background: #1989fa; box-shadow: none; }
.union-reimburse-form .invoice-popup-actions .van-button--info.van-button--plain { color: #1989fa; border-color: #1989fa; background: #fff; }
/* 审核卡片与社团审核保持相同的标题、必填标记和按钮规格。 */
.union-reimburse-approval { padding: 0; background: #f3f7fd; }
.union-reimburse-approval-card {
margin: 0 0 11px;
overflow: hidden;
border: 1px solid #e9eff7;
border-radius: 10px;
background: #fff;
box-shadow: 0 3px 11px rgba(51, 87, 126, .08);
}
.union-reimburse-approval-card__title {
position: relative;
margin: 0;
padding: 12px 14px 8px 21px;
color: #354255;
font-size: 15px;
font-weight: 600;
line-height: 22px;
}
.union-reimburse-approval-card__title::before {
position: absolute;
top: 15px;
bottom: 11px;
left: 12px;
width: 2px;
border-radius: 2px;
background: #1989fa;
content: '';
}
.union-reimburse-approval-field-label {
position: relative;
margin: 0;
padding: 10px 14px 6px;
color: #596779;
font-size: 12px;
line-height: 24px;
}
.union-reimburse-approval-required { position: absolute; left: 6px; color: #ee0a24; font-size: 12px; }
.union-reimburse-approval-card .van-field.union-reimburse-approval-field {
width: calc(100% - 28px);
margin: 0 14px 14px;
padding: 12px;
box-sizing: border-box;
border: 1px solid #c9d5e5;
border-radius: 10px;
background: #f7f9fd;
}
.union-reimburse-approval-card .van-field.union-reimburse-approval-field .van-field__control {
min-height: 150px;
color: #253247;
font-size: 12px;
line-height: 22px;
}
.union-reimburse-approval-card .van-field.union-reimburse-approval-field::after { display: none; }
.union-reimburse-approval-actions {
display: flex;
column-gap: 10px;
padding: 10px 12px calc(12px + env(safe-area-inset-bottom));
}
.union-reimburse-approval-actions .van-button { height: 40px; border-radius: 14px; font-size: 14px; }
@keyframes union-reimburse-skeleton-loading {
0% { background-position: 100% 50%; }
100% { background-position: 0 50%; }
}
@@ -2,14 +2,10 @@ const UNION_REIMBURSE_INFO = {
template: template:
/*language=HTML*/ /*language=HTML*/
` `
<van-action-sheet v-model="visible" class="union-reimburse-info-sheet"> <van-popup v-model="visible" position="right" :style="{width: '100%', height: '100%'}" get-container="#app" :close-on-click-overlay="false" class="union-reimburse-info-sheet">
<div class="union-info-header"> <div class="union-reimburse-info-detail">
<div class="union-info-header-title">查看详情</div> <van-nav-bar :title="detailTitle" left-text="返回" left-arrow fixed @click-left="onClose"></van-nav-bar>
<div class="union-info-close" @click="onClose"> <div class="detail-container">
<van-icon name="cross"></van-icon>
</div>
</div>
<div class="detail-container">
<div class="union-info-scroll"> <div class="union-info-scroll">
<div class="union-info-section"> <div class="union-info-section">
<div class="union-info-section-title">报销申请</div> <div class="union-info-section-title">报销申请</div>
@@ -59,6 +55,8 @@ const UNION_REIMBURSE_INFO = {
</div> </div>
<van-swipe ref="paymentInfoSwipe" :loop="false" :show-indicators="false" @change="handlePaymentSwipeChange"> <van-swipe ref="paymentInfoSwipe" :loop="false" :show-indicators="false" @change="handlePaymentSwipeChange">
<van-swipe-item v-for="(item, index) in paymentInfoRows" :key="index"> <van-swipe-item v-for="(item, index) in paymentInfoRows" :key="index">
<div class="union-payment-card">
<div class="union-payment-card-title">支付信息{{ index + 1 }}</div>
<div class="union-info-row"> <div class="union-info-row">
<div class="union-info-label">支付方式</div> <div class="union-info-label">支付方式</div>
<div class="union-info-value">{{ getPaymentWayText(item.paymentWay) }}</div> <div class="union-info-value">{{ getPaymentWayText(item.paymentWay) }}</div>
@@ -85,6 +83,7 @@ const UNION_REIMBURSE_INFO = {
<div class="union-info-value">{{ item.bankCardNumber }}</div> <div class="union-info-value">{{ item.bankCardNumber }}</div>
</div> </div>
</template> </template>
</div>
</van-swipe-item> </van-swipe-item>
</van-swipe> </van-swipe>
</template> </template>
@@ -272,12 +271,12 @@ const UNION_REIMBURSE_INFO = {
<!-- </van-cell-group>--> <!-- </van-cell-group>-->
<template v-for="(task,index) in doneTasks"> <template v-for="(task,index) in doneTasks">
<div class="union-info-section"> <section class="union-info-section union-info-task-card">
<div class="process-title">{{ task.displayName }}</div> <h3 class="union-info-section-title union-info-task-card__title">{{ task.displayName || '审核节点' }}</h3>
<template v-if="task.ext.isFirstTaskNode"> <template v-if="task.ext && task.ext.isFirstTaskNode">
<div class="union-info-row"> <div class="union-info-row">
<div class="union-info-label">申请用户</div> <div class="union-info-label">申请用户</div>
<div class="union-info-value">{{ task.ext.initiatorName}}({{task.ext.initiatorAccount}})</div> <div class="union-info-value">{{ task.ext.initiatorName || '--' }}{{task.ext.initiatorAccount || '--'}}</div>
</div> </div>
<div class="union-info-row"> <div class="union-info-row">
<div class="union-info-label">申请时间</div> <div class="union-info-label">申请时间</div>
@@ -286,14 +285,14 @@ const UNION_REIMBURSE_INFO = {
<div class="union-info-row"> <div class="union-info-row">
<div class="union-info-label">办理结果</div> <div class="union-info-label">办理结果</div>
<div class="union-info-value"> <div class="union-info-value">
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag> <dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext && task.ext.submitType"></dict-tag>
</div> </div>
</div> </div>
</template> </template>
<template v-else> <template v-else>
<div class="union-info-row"> <div class="union-info-row">
<div class="union-info-label">办理用户</div> <div class="union-info-label">办理用户</div>
<div class="union-info-value">{{ task.taskFormData.userName}}({{task.taskFormData.loginName}})</div> <div class="union-info-value">{{ getTaskUserText(task) }}</div>
</div> </div>
<div class="union-info-row"> <div class="union-info-row">
<div class="union-info-label">办理时间</div> <div class="union-info-label">办理时间</div>
@@ -302,36 +301,44 @@ const UNION_REIMBURSE_INFO = {
<div class="union-info-row"> <div class="union-info-row">
<div class="union-info-label">办理结果</div> <div class="union-info-label">办理结果</div>
<div class="union-info-value"> <div class="union-info-value">
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag> <dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext && task.ext.submitType"></dict-tag>
</div> </div>
</div> </div>
<div class="union-info-row" v-if="!task.ext.isFirstTaskNode"> <div class="union-info-row">
<div class="union-info-label">办理意见</div> <div class="union-info-label">办理意见</div>
<div class="union-info-value">{{ task.taskFormData.opinion }}</div> <div class="union-info-value">{{ getTaskOpinion(task) }}</div>
</div> </div>
<div class="union-info-row" v-if="!task.ext.isFirstTaskNode && task.ext.tf_userSign"> <div class="union-info-row" v-if="task.ext && task.ext.tf_userSign">
<div class="union-info-label">签字</div> <div class="union-info-label">签字</div>
<div class="union-info-value"> <div class="union-info-value">
<van-image :src="task.ext.tf_userSign" class="signature-image"></van-image> <van-image :src="task.ext.tf_userSign" class="signature-image"></van-image>
</div> </div>
</div> </div>
</template> </template>
</div> </section>
</template> </template>
<slot></slot>
</div> </div>
</div> </div>
<slot></slot> </div>
</van-action-sheet> </van-popup>
`, `,
dicts: ["UNION_REIMBURSE_TYPE", "UNION_REIMBURSE_PAYMENT_WAY", "UNION_REIMBURSE_FUND_SOURCE", "UNION_REIMBURSE_PROJECT", "PROCESS_TASK_SUBMIT_TYPE"], dicts: ["UNION_REIMBURSE_TYPE", "UNION_REIMBURSE_PAYMENT_WAY", "UNION_REIMBURSE_FUND_SOURCE", "UNION_REIMBURSE_PROJECT", "PROCESS_TASK_SUBMIT_TYPE"],
props: {
historyLayerName: {
type: String,
default: "union-reimburse-info"
}
},
data() { data() {
return { return {
visible: false, visible: false,
viewData: {}, viewData: {},
doneTasks: [], doneTasks: [],
row: null, row: null,
currentPaymentIndex: 0 currentPaymentIndex: 0,
detailTitle: "报销详情",
historyLayerUnsubscribe: null
} }
}, },
computed: { computed: {
@@ -339,8 +346,29 @@ const UNION_REIMBURSE_INFO = {
return this.viewData.paymentDetails || [] return this.viewData.paymentDetails || []
} }
}, },
watch: {
visible(value) {
const manager = window.h5HistoryLayerManager
if (!manager) return
if (value) {
manager.open(this.historyLayerName)
return
}
manager.close(this.historyLayerName)
}
},
mounted() { mounted() {
this.injectInfoStyle() this.injectInfoStyle()
const manager = window.h5HistoryLayerManager
if (manager) {
manager.ensureRegistered("union-reimburse-info")
this.$set(this, "historyLayerUnsubscribe", manager.subscribe((layers) => {
this.$set(this, "visible", layers.includes(this.historyLayerName))
}))
}
},
beforeDestroy() {
if (this.historyLayerUnsubscribe) this.historyLayerUnsubscribe()
}, },
methods: { methods: {
injectInfoStyle() { injectInfoStyle() {
@@ -355,16 +383,19 @@ const UNION_REIMBURSE_INFO = {
styleNode.appendChild(document.createTextNode(this.$options.style)) styleNode.appendChild(document.createTextNode(this.$options.style))
document.head.appendChild(styleNode) document.head.appendChild(styleNode)
}, },
onOpen(row) { onOpen(row, title) {
this.row = row this.$set(this, "row", row)
this.visible = true this.$set(this, "detailTitle", title || "报销详情")
this.currentPaymentIndex = 0 this.$set(this, "visible", true)
this.$set(this, "currentPaymentIndex", 0)
this.getInfo() this.getInfo()
this.getDoneTasks() this.getDoneTasks()
}, },
// 关闭 // 由全局历史栈关闭详情,保证遮罩、关闭按钮和浏览器返回键遵循同一顺序。
onClose() { onClose() {
this.visible = false const manager = window.h5HistoryLayerManager
if (manager && manager.close(this.historyLayerName)) return
this.$set(this, "visible", false)
}, },
formatCreateTime(value) { formatCreateTime(value) {
return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : "" return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : ""
@@ -386,6 +417,16 @@ const UNION_REIMBURSE_INFO = {
const matchedPaymentWay = this.dict.type.UNION_REIMBURSE_PAYMENT_WAY.find(item => item.code === paymentWay) const matchedPaymentWay = this.dict.type.UNION_REIMBURSE_PAYMENT_WAY.find(item => item.code === paymentWay)
return matchedPaymentWay ? matchedPaymentWay.name : "" return matchedPaymentWay ? matchedPaymentWay.name : ""
}, },
getTaskUserText(task) {
const formData = task && task.taskFormData ? task.taskFormData : {}
const userName = formData.userName || formData.username || "--"
const loginName = formData.loginName || formData.loginname || "--"
return userName + "" + loginName + ""
},
getTaskOpinion(task) {
const formData = task && task.taskFormData ? task.taskFormData : {}
return formData.reviewOpinion || formData.opinion || formData.tf_opinion || "--"
},
buildViewData(data) { buildViewData(data) {
const viewData = Object.assign({ const viewData = Object.assign({
files: [], files: [],
@@ -425,15 +466,15 @@ const UNION_REIMBURSE_INFO = {
} }
}, },
handlePaymentSwipeChange(index) { handlePaymentSwipeChange(index) {
this.currentPaymentIndex = index this.$set(this, "currentPaymentIndex", index)
}, },
swipeToPayment(index) { swipeToPayment(index) {
if (!this.paymentInfoRows.length) { if (!this.paymentInfoRows.length) {
this.currentPaymentIndex = 0 this.$set(this, "currentPaymentIndex", 0)
return return
} }
const targetIndex = Math.max(0, Math.min(index, this.paymentInfoRows.length - 1)) const targetIndex = Math.max(0, Math.min(index, this.paymentInfoRows.length - 1))
this.currentPaymentIndex = targetIndex this.$set(this, "currentPaymentIndex", targetIndex)
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.paymentInfoSwipe && this.$refs.paymentInfoSwipe.swipeTo) { if (this.$refs.paymentInfoSwipe && this.$refs.paymentInfoSwipe.swipeTo) {
this.$refs.paymentInfoSwipe.swipeTo(targetIndex) this.$refs.paymentInfoSwipe.swipeTo(targetIndex)
@@ -456,8 +497,8 @@ const UNION_REIMBURSE_INFO = {
getInfo() { getInfo() {
this.$axios.post("/platform/unionReimburse/apply/info", { id: this.row.id }).then((res) => { this.$axios.post("/platform/unionReimburse/apply/info", { id: this.row.id }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.viewData = this.buildViewData(res.data) this.$set(this, "viewData", this.buildViewData(res.data))
this.currentPaymentIndex = 0 this.$set(this, "currentPaymentIndex", 0)
} }
}) })
}, },
@@ -471,120 +512,131 @@ const UNION_REIMBURSE_INFO = {
getDoneTasks() { getDoneTasks() {
this.$axios.post("/flow/common/doneTasks", { bizId: this.row.id }).then((res) => { this.$axios.post("/flow/common/doneTasks", { bizId: this.row.id }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.doneTasks = res.data this.$set(this, "doneTasks", res.data || [])
} }
}) })
} }
}, },
style: /*language=CSS*/ ` style: /*language=CSS*/ `
.union-reimburse-info-sheet { .union-reimburse-info-sheet {
height: 92vh;
display: flex;
flex-direction: column;
overflow: hidden; overflow: hidden;
background: #f3f7fd;
} }
.union-reimburse-info-sheet .van-action-sheet__content { .union-reimburse-info-detail {
height: 100%; height: 100%;
display: flex; display: flex;
padding-top: 46px;
box-sizing: border-box;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
background: #f3f7fd;
} }
.union-info-header { .union-reimburse-info-detail > .van-nav-bar {
flex: 0 0 auto; z-index: 20;
display: flex;
align-items: center;
justify-content: space-between;
height: 56px;
padding: 0 20px;
background: #fff; background: #fff;
border-top: 4px solid #1677ff;
border-bottom: 1px solid #e7ebf0;
} }
.union-info-header-title { .union-reimburse-info-detail > .van-nav-bar .van-nav-bar__title {
color: #111827; color: #172033;
font-size: 16px; font-size: 16px;
font-weight: 700; font-weight: 600;
} }
.union-info-close { .union-reimburse-info-detail > .van-nav-bar .van-icon,
display: flex; .union-reimburse-info-detail > .van-nav-bar .van-nav-bar__text {
align-items: center; color: #1989fa;
justify-content: center;
width: 28px;
height: 28px;
color: #8b95a3;
font-size: 20px;
} }
.detail-container { .detail-container {
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
background: #f2f4f7; background: #f3f7fd;
} }
.union-info-scroll { .union-info-scroll {
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
padding-bottom: 16px; padding: 12px 15px calc(18px + env(safe-area-inset-bottom));
box-sizing: border-box;
-webkit-overflow-scrolling: touch;
} }
.union-info-section { .union-info-section {
overflow: hidden;
margin-bottom: 11px;
border: 1px solid #e9eff7;
border-radius: 10px;
background: #fff; background: #fff;
margin-bottom: 10px; box-shadow: 0 3px 11px rgba(51, 87, 126, .08);
} }
.union-info-section-title { .union-info-section-title {
display: flex; position: relative;
align-items: center; margin: 0;
height: 44px; padding: 12px 14px 8px 21px;
padding: 0 16px; color: #354255;
color: #1f2937; font-size: 15px;
font-size: 14px; font-weight: 600;
font-weight: 700; line-height: 22px;
background: #f5f7fa;
border-bottom: 1px solid #e7ebf0;
} }
.union-info-section-title::before { .union-info-section-title::before {
content: ""; position: absolute;
width: 4px; top: 15px;
height: 16px; bottom: 11px;
margin-right: 8px; left: 12px;
width: 2px;
border-radius: 2px; border-radius: 2px;
background: #1677ff; background: #1989fa;
content: "";
} }
.union-info-row { .union-info-row {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
justify-content: space-between; justify-content: space-between;
min-height: 44px; min-height: 43px;
padding: 12px 20px; padding: 10px 14px;
border-bottom: 1px solid #edf0f4; border-bottom: 1px solid #eef2f7;
box-sizing: border-box; box-sizing: border-box;
} }
/* 查看详情中的流程节点字段与社团审核详情卡片保持一致的行高和内边距。 */
.union-info-task-card > .union-info-task-card__title {
margin: 0;
padding: 12px 14px 8px 21px;
}
.union-info-task-card .union-info-row {
min-height: 44px;
padding: 10px 14px;
}
.union-info-task-card .union-info-label,
.union-info-task-card .union-info-value {
line-height: 24px;
}
.union-info-row:last-child { .union-info-row:last-child {
border-bottom: 0; border-bottom: 0;
} }
.union-info-label { .union-info-label {
flex: 0 0 96px; flex: 0 0 102px;
color: #8a94a6; color: #596779;
font-size: 14px; font-size: 12px;
line-height: 20px; line-height: 22px;
} }
.union-info-value { .union-info-value {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
color: #1f2937; color: #4f5d70;
font-size: 14px; font-size: 12px;
line-height: 20px; line-height: 22px;
text-align: right; text-align: left;
word-break: break-all; word-break: break-all;
white-space: pre-line; white-space: pre-line;
} }
@@ -597,7 +649,7 @@ const UNION_REIMBURSE_INFO = {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 8px;
padding: 12px 20px 4px; padding: 10px 14px 3px;
background: #fff; background: #fff;
} }
@@ -617,21 +669,44 @@ const UNION_REIMBURSE_INFO = {
background: #1677ff; background: #1677ff;
} }
.union-payment-card {
margin: 8px 12px 12px;
overflow: hidden;
border: 1px solid #e6ebf2;
border-radius: 9px;
background: #fff;
}
.union-payment-card-title {
padding: 9px 12px;
color: #40526a;
font-size: 13px;
font-weight: 600;
line-height: 20px;
background: #f7f9fd;
border-bottom: 1px solid #eef2f7;
}
.union-payment-card .union-info-row {
padding-right: 12px;
padding-left: 12px;
}
.union-empty-text { .union-empty-text {
padding: 14px 20px; padding: 12px 14px;
color: #9aa3af; color: #9aa3af;
font-size: 13px; font-size: 13px;
} }
.union-file-preview { .union-file-preview {
padding: 12px 20px 16px; padding: 10px 14px 14px;
} }
.union-invoice-subtitle { .union-invoice-subtitle {
display: flex; display: flex;
align-items: center; align-items: center;
height: 40px; height: 40px;
padding: 0 20px; padding: 0 14px;
color: #6b7280; color: #6b7280;
font-size: 13px; font-size: 13px;
background: #f5f7fa; background: #f5f7fa;
@@ -640,13 +715,13 @@ const UNION_REIMBURSE_INFO = {
} }
.union-invoice-card-list { .union-invoice-card-list {
padding: 12px 16px 4px; padding: 10px 12px 0;
} }
.union-invoice-card { .union-invoice-card {
margin-bottom: 12px; margin-bottom: 12px;
padding: 14px 14px 12px; padding: 12px 12px 10px;
border-radius: 12px; border-radius: 9px;
border: 1px solid #e6ebf2; border: 1px solid #e6ebf2;
background: #fff; background: #fff;
box-shadow: 0 1px 3px rgba(31, 35, 41, 0.05); box-shadow: 0 1px 3px rgba(31, 35, 41, 0.05);
@@ -663,17 +738,6 @@ const UNION_REIMBURSE_INFO = {
padding: 12px 0 0; padding: 12px 0 0;
} }
.process-title {
display: flex;
align-items: center;
height: 40px;
padding: 0 20px;
color: #1f2937;
font-size: 14px;
font-weight: 700;
background: #f5f7fa;
}
.signature-image { .signature-image {
width: 100%; width: 100%;
height: 120px; height: 120px;
@@ -2,63 +2,65 @@
layout("/layouts/platform_h5.html"){ layout("/layouts/platform_h5.html"){
#--> #-->
<style> <style id="style-union-reimburse-h5">
.state-primary { <!--#include('../common/unionReimburse.css'){}#-->
color: #409eff;
}
.state-success {
color: #67c23a;
}
.state-danger {
color: #f56c6c;
}
</style> </style>
<div id="app" v-cloak> <div id="app" v-cloak class="union-reimburse-page">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="我的报销" placeholder fixed></van-nav-bar> <van-nav-bar @click-left="onNavBack" left-arrow left-text="返回" title="我的报销" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px"> <van-sticky offset-top="46px">
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false"> <div class="union-reimburse-sticky">
<van-dropdown-item :options="yearList" @change="doSearch" v-model="pageForm.year"></van-dropdown-item> <van-search v-model="pageForm.searchKeyword" class="union-reimburse-search" placeholder="请输入姓名或工号搜索" shape="round" clearable @search="doSearch" @clear="doSearch"></van-search>
</van-dropdown-menu> <van-dropdown-menu class="union-reimburse-filter" :close-on-click-outside="false" :close-on-click-overlay="false">
<van-dropdown-item :options="yearList" @change="doSearch" v-model="pageForm.year"></van-dropdown-item>
</van-dropdown-menu>
</div>
</van-sticky> </van-sticky>
<main class="union-reimburse-list-content">
<table-list api="/platform/unionReimburse/mine/pageData" :page_form.sync="pageForm" ref="tableListRef" <table-list api="/platform/unionReimburse/mine/pageData" :page_form.sync="pageForm" ref="tableListRef" :show_loading_overlay="false"
@ready="doSearch" > @ready="doSearch" @loading-change="onLoadingChange">
<template #header="{row}">
<div v-if="row" class="union-reimburse-card-header">
<div class="union-reimburse-card-header__heading"><div class="union-reimburse-card-header__title">{{row.userName || '--'}}</div><div class="union-reimburse-card-header__sub">工号:{{row.loginName || '--'}}</div></div>
<enum-tag class="union-reimburse-card-header__status" v-if="row.instanceState" :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
<van-tag v-else :class="['union-reimburse-card-header__status', getStatusClass(row.stateId)]">{{getStateName(row.stateId)}}</van-tag>
</div>
</template>
<template v-slot="{index,row}"> <template v-slot="{index,row}">
<table-column label="经办人工号">{{row.loginName}}</table-column> <table-column label="报销项目">{{getReimburseProjectName(row.reimburseProject) || '--'}}</table-column>
<table-column label="姓名">{{row.userName}}</table-column> <table-column label="" label_suffix="">{{getRemarkText(row) || '--'}}</table-column>
<table-column label="所属工会">{{row.unionName}}</table-column> <table-column label="所属工会">{{row.unionName || '--'}}</table-column>
<table-column label="所属单位">{{row.unitName}}</table-column> <table-column label="所属单位">{{row.unitName || '--'}}</table-column>
<table-column label="申请时间">{{formatCreateTime(row.createTime)}}</table-column> <table-column label="申请时间">{{formatCreateTime(row.createTime) || '--'}}</table-column>
<table-column label="报销项目">{{getReimburseProjectName(row.reimburseProject)}}</table-column> <table-column label="当前节点">{{row.taskName || '--'}}</table-column>
<table-column label="备注">{{getRemarkText(row)}}</table-column>
<table-column label="申请状态">
<span :class="getStateClass(row.stateId)">{{getStateName(row.stateId)}}</span>
</table-column>
</template> </template>
<template #actions="{index,row}"> <template #actions="{index,row}">
<div class="action-btn" @click="onView(row)"> <div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i> <van-icon name="eye-o"></van-icon>
<span>查看</span> <span>查看</span>
</div> </div>
<div class="action-btn" @click="onEdit(row)" v-if="row.stateId == 1 || row.stateId == 5"> <div class="action-btn" @click="onEdit(row)" v-if="row.stateId == 1 || row.stateId == 5">
<i class="fa fa-edit"></i> <van-icon name="edit"></van-icon>
<span>编辑</span> <span>编辑</span>
</div> </div>
<div class="action-btn delete" v-if="row.stateId == 2" @click="onRevoke(row)"> <div class="action-btn delete" v-if="row.stateId == 2" @click="onRevoke(row)">
<i class="fa fa-undo"></i> <van-icon name="revoke"></van-icon>
<span>撤回</span> <span>撤回</span>
</div> </div>
<div class="action-btn delete" @click="onDelete(row)" v-if="row.stateId == 1"> <div class="action-btn delete" @click="onDelete(row)" v-if="row.stateId == 1">
<i class="fa fa-trash"></i> <van-icon name="delete-o"></van-icon>
<span>删除</span> <span>删除</span>
</div> </div>
</template> </template>
<template #empty>
<div v-if="listLoading" class="union-reimburse-list-skeleton"><div v-for="item in 2" :key="item" class="union-reimburse-skeleton-card"><div class="union-reimburse-skeleton-line union-reimburse-skeleton-line--title"></div><div class="union-reimburse-skeleton-line"></div><div class="union-reimburse-skeleton-line union-reimburse-skeleton-line--short"></div></div></div>
<div v-else class="union-reimburse-empty"><img src="/assets/mobile/img/unionReimburse/union-reimburse-empty.png" alt="暂无报销记录插画"><div class="union-reimburse-empty__title">暂无报销记录</div><div class="union-reimburse-empty__hint">当前筛选条件下暂时没有报销申请</div></div>
</template>
</table-list> </table-list>
</main>
<union-reimburse-info ref="unionReimburseInfoRef"></union-reimburse-info> <union-reimburse-info ref="unionReimburseInfoRef" history-layer-name="union-reimburse-mine-info"></union-reimburse-info>
<van-dialog :value="confirmVisible" title="提示" :message="confirmMessage" show-cancel-button :confirm-button-loading="formLoading" @confirm="confirmPendingAction" @cancel="closeConfirm"></van-dialog>
</div> </div>
<script nonce="${cspNonce!}"> <script nonce="${cspNonce!}">
@@ -66,13 +68,18 @@ layout("/layouts/platform_h5.html"){
const vue = new Vue({ const vue = new Vue({
el: "#app", el: "#app",
store, store,
dicts: ["UNION_REIMBURSE_PROJECT"], dicts: ["UNION_REIMBURSE_PROJECT", "PROCESS_INSTANCE_STATE"],
components: { components: {
"union-reimburse-info":UNION_REIMBURSE_INFO "union-reimburse-info":UNION_REIMBURSE_INFO
}, },
data() { data() {
return { return {
viewShow: false, listLoading: true,
formLoading: false,
confirmVisible: false,
pendingAction: "",
pendingRow: null,
historyLayerUnsubscribe: null,
yearList: [], yearList: [],
pageForm: { pageForm: {
pageNumber: 1, pageNumber: 1,
@@ -84,6 +91,9 @@ layout("/layouts/platform_h5.html"){
infoShow: false infoShow: false
} }
}, },
computed: {
confirmMessage() { return this.pendingAction === "revoke" ? "您确定要撤销申请吗?" : "您确定要删除吗?" }
},
methods: { methods: {
// 手机端列表的报销项目、备注、状态文案与 PC 端 mine 列表保持一致。 // 手机端列表的报销项目、备注、状态文案与 PC 端 mine 列表保持一致。
getReimburseProjectName(reimburseProject) { getReimburseProjectName(reimburseProject) {
@@ -125,58 +135,34 @@ layout("/layouts/platform_h5.html"){
} }
return stateId || "" return stateId || ""
}, },
getStateClass(stateId) { getStatusClass(stateId) {
if (stateId == 3) { if (stateId == 3) return "union-reimburse-card-header__status--success"
return "state-success" if (stateId == 4 || stateId == 5) return "union-reimburse-card-header__status--danger"
} return "union-reimburse-card-header__status--pending"
if (stateId == 4) {
return "state-danger"
}
return "state-primary"
}, },
formatCreateTime(value) { formatCreateTime(value) {
return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : "" return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : ""
}, },
onView(row) { onView(row) {
this.showApprovalForm = false this.$refs.unionReimburseInfoRef.onOpen(row, "报销申请详情")
this.$refs.unionReimburseInfoRef.onOpen(row)
}, },
onEdit(row) { onEdit(row) {
this.$pjaxReplace('/platform/unionReimburse/apply/h5?taskId=' + (row.startTaskId || '') + "&bizId=" + row.id) this.$pjaxReplace('/platform/unionReimburse/apply/h5?taskId=' + (row.startTaskId || '') + "&bizId=" + row.id)
}, },
onRevoke(row){ onLoadingChange(state) { this.$set(this, "listLoading", state.loading) },
this.$dialog onNavBack() { const manager = window.h5HistoryLayerManager; if (manager && manager.stack.length) { manager.close(); return } historyBack() },
.confirm({ openConfirm(action, row) { this.$set(this, "pendingAction", action); this.$set(this, "pendingRow", row); const manager = window.h5HistoryLayerManager; if (manager) { manager.open("union-reimburse-mine-confirm"); return } this.$set(this, "confirmVisible", true) },
title: "提示", closeConfirm(afterClose) { const manager = window.h5HistoryLayerManager; if (manager) { manager.close("union-reimburse-mine-confirm", afterClose); return } this.$set(this, "confirmVisible", false); if (afterClose) afterClose() },
message: "您确定要撤销申请吗?" confirmPendingAction() { const action = this.pendingAction; const row = this.pendingRow; this.closeConfirm(() => { if (action === "revoke") { this.revoke(row) } else { this.deleteApplication(row) } }) },
}) onRevoke(row){ this.openConfirm("revoke", row) },
.then(() => { revoke(row) { this.$set(this, "formLoading", true); this.$axios.post("/platform/unionReimburse/mine/revokeTask", {id: row.id}).then((resp) => { if (resp.code === 0) { this.$toast.success(resp.msg); this.doSearch() } else { this.$toast.fail(resp.msg) } }).finally(() => { this.$set(this, "formLoading", false) }) },
this.$axios.post("/platform/unionReimburse/mine/revokeTask", {id: row.id}).then((resp) => { onDelete(row) { this.openConfirm("delete", row) },
this.$toast.success(resp.msg) deleteApplication(row) { this.$set(this, "formLoading", true); this.$axios.post("/platform/unionReimburse/mine/delete", {id: row.id}).then((resp) => { if (resp.code === 0) { this.$toast.success(resp.msg); this.doSearch() } else { this.$toast.fail(resp.msg) } }).finally(() => { this.$set(this, "formLoading", false) }) },
this.doSearch()
})
})
},
onDelete(row) {
this.$dialog
.confirm({
title: "提示",
message: "您确定要删除吗?"
})
.then(() => {
this.$axios.post("/platform/unionReimburse/mine/delete", {id: row.id}).then((resp) => {
if (resp.code === 0) {
this.$toast.success(resp.msg)
this.doSearch()
}
})
})
},
doSearch() { doSearch() {
this.$nextTick(() => { this.$nextTick(() => {
this.pageForm.pageNumber = 1 this.$set(this.pageForm, "pageNumber", 1)
this.pageForm.totalCount = 0 this.$set(this.pageForm, "totalCount", 0)
this.$refs.tableListRef.doSearch() this.$refs.tableListRef.doSearch()
}) })
}, },
@@ -190,7 +176,13 @@ layout("/layouts/platform_h5.html"){
}, },
created() { created() {
this.initData() this.initData()
const manager = window.h5HistoryLayerManager
if (manager) { manager.ensureRegistered("union-reimburse-mine"); this.$set(this, "historyLayerUnsubscribe", manager.subscribe((layers) => { this.$set(this, "confirmVisible", layers.includes("union-reimburse-mine-confirm")) })) }
},
beforeDestroy() {
if (this.historyLayerUnsubscribe) this.historyLayerUnsubscribe()
const manager = window.h5HistoryLayerManager
if (manager) manager.unregister("union-reimburse-mine")
} }
}) })
</script> </script>
@@ -2,100 +2,64 @@
layout("/layouts/platform_h5.html"){ layout("/layouts/platform_h5.html"){
#--> #-->
<style> <style id="style-union-reimburse-h5">
.state-primary { <!--#include('../common/unionReimburse.css'){}#-->
color: #409eff;
}
.state-success {
color: #67c23a;
}
.state-danger {
color: #f56c6c;
}
</style> </style>
<div id="app" v-cloak> <div id="app" v-cloak class="union-reimburse-page">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="报销审核" placeholder fixed></van-nav-bar> <van-nav-bar @click-left="onNavBack" left-arrow left-text="返回" title="报销审核" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px"> <van-sticky offset-top="46px">
<van-search <div class="union-reimburse-sticky">
:reverse-color="false" <van-search class="union-reimburse-search" :reverse-color="false" :show-action="false" @search="doSearch" @clear="doSearch" input-align="left" placeholder="请输入姓名或工号搜索" v-model="pageForm.searchKeyword"></van-search>
:show-action="false" <van-dropdown-menu class="union-reimburse-filter" :close-on-click-outside="false" :close-on-click-overlay="false">
@search="doSearch" <van-dropdown-item :options="yearList" @change="doSearch" v-model="pageForm.year"></van-dropdown-item>
input-align="left" <van-dropdown-item :options="reimburseProjectOptions" @change="doSearch" v-model="pageForm.reimburseProject"></van-dropdown-item>
placeholder="请输入经办人搜索" <van-dropdown-item :options="approvalOptions" @change="onApprovalFilterChange" v-model="pageForm.approvalText"></van-dropdown-item>
v-model="pageForm.searchKeyword" </van-dropdown-menu>
></van-search> </div>
<van-tabs v-model="pageForm.approvalText"
@change="(val)=>{this.pageForm.approval = val==='1';this.doSearch();}">
<van-tab title="已审核" name="1"></van-tab>
<van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</van-sticky> </van-sticky>
<main class="union-reimburse-list-content">
<table-list api="/platform/unionReimburse/review/pageData" :page_form.sync="pageForm" ref="tableListRef" <table-list api="/platform/unionReimburse/review/pageData" :page_form.sync="pageForm" ref="tableListRef" :show_loading_overlay="false"
@ready="doSearch"> @ready="doSearch" @loading-change="onLoadingChange">
<template #header="{row}"><div v-if="row" class="union-reimburse-card-header"><div class="union-reimburse-card-header__heading"><div class="union-reimburse-card-header__title">{{row.userName || '--'}}</div><div class="union-reimburse-card-header__sub">工号:{{row.loginName || '--'}}</div></div><van-tag :class="['union-reimburse-card-header__status', getStatusClass(row.stateId)]">{{getStateName(row.stateId)}}</van-tag></div></template>
<template v-slot="{index,row}"> <template v-slot="{index,row}">
<table-column label="经办人工号">{{row.loginName}}</table-column> <table-column label="报销项目">{{getReimburseProjectName(row.reimburseProject) || '--'}}</table-column>
<table-column label="姓名">{{row.userName}}</table-column> <table-column label="" label_suffix="">{{getRemarkText(row) || '--'}}</table-column>
<table-column label="所属工会">{{row.unionName}}</table-column> <table-column label="所属工会">{{row.unionName || '--'}}</table-column>
<table-column label="所属单位">{{row.unitName}}</table-column> <table-column label="所属单位">{{row.unitName || '--'}}</table-column>
<table-column label="申请时间">{{formatCreateTime(row.createTime)}}</table-column> <table-column label="申请时间">{{formatCreateTime(row.createTime) || '--'}}</table-column>
<table-column label="报销项目">{{getReimburseProjectName(row.reimburseProject)}}</table-column>
<table-column label="备注">{{getRemarkText(row)}}</table-column>
<table-column label="申请状态">
<span :class="getStateClass(row.stateId)">{{getStateName(row.stateId)}}</span>
</table-column>
</template> </template>
<template #actions="{index,row}"> <template #actions="{index,row}">
<div class="action-btn" @click="onView(row)"> <div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i> <van-icon name="eye-o"></van-icon>
<span>查看</span> <span>查看</span>
</div> </div>
<div class="action-btn" v-if="row.stateId == 2" @click="onAudit(row)"> <div class="action-btn review" v-if="row.stateId == 2" @click="onAudit(row)">
<i class="fa fa-edit"></i> <van-icon name="edit"></van-icon>
<span>审核</span> <span>审核</span>
</div> </div>
</template> </template>
<template #empty><div v-if="listLoading" class="union-reimburse-list-skeleton"><div v-for="item in 2" :key="item" class="union-reimburse-skeleton-card"><div class="union-reimburse-skeleton-line union-reimburse-skeleton-line--title"></div><div class="union-reimburse-skeleton-line"></div><div class="union-reimburse-skeleton-line union-reimburse-skeleton-line--short"></div></div></div><div v-else class="union-reimburse-empty"><img src="/assets/mobile/img/unionReimburse/union-reimburse-empty.png" alt="暂无报销审核记录插画"><div class="union-reimburse-empty__title">暂无报销审核记录</div><div class="union-reimburse-empty__hint">当前筛选条件下暂时没有待办申请</div></div></template>
</table-list> </table-list>
</main>
<union-reimburse-info ref="unionReimburseInfoRef"> <union-reimburse-info ref="unionReimburseInfoRef" history-layer-name="union-reimburse-review-info">
<div v-if="showApprovalForm"> <template v-if="showApprovalForm">
<div class="process-title"> <section class="union-reimburse-approval-card">
校工会审核 <h2 class="union-reimburse-approval-card__title">{{formData.taskName || '校工会审核'}}</h2>
</div>
<div class="form-container">
<van-form ref="formRef"> <van-form ref="formRef">
<van-cell-group title="审批意见"> <p class="union-reimburse-approval-field-label"><span class="union-reimburse-approval-required">*</span>审批意见</p>
<van-field label="" <van-field v-model="formData.reviewOpinion" class="union-reimburse-approval-field" name="reviewOpinion" type="textarea" rows="6" label="" placeholder="请输入审批意见" :rules="[{required: true, message: '请填写审批意见'}]" maxlength="100" show-word-limit></van-field>
:rules="[{ required: true,message:'请填审批意见' }]"
v-model="formData.reviewOpinion"
required
type="textarea"
name="reviewOpinion"
rows="4"
autosize
class="more-text"
placeholder="请填审批意见"></van-field>
</van-cell-group>
<!-- <van-cell-group title="电子签名" class="form-section">-->
<!-- <van-field class="more-text" name="tf_userSign" label="" required-->
<!-- v-model="formData.reviewOpinion" :rules="[{ required: true,message:'请签名' }]">-->
<!-- <template #input>-->
<!-- <h5-signature v-model="formData.tf_userSign" slot="input"></h5-signature>-->
<!-- </template>-->
<!-- </van-field>-->
<!-- </van-cell-group>-->
<div class="form-actions">
<van-button type="primary" @click="handleTaskAction(3)">同意申请</van-button>
<van-button type="danger" @click="handleTaskAction(4)">拒绝申请</van-button>
<van-button type="danger" @click="handleTaskAction(5)">退回到发起人</van-button>
</div>
</van-form> </van-form>
</section>
<div class="union-reimburse-approval-actions">
<van-button :loading="formLoading" type="danger" block @click="handleTaskAction(5)">退回</van-button>
<van-button :loading="formLoading" type="danger" block @click="handleTaskAction(4)">不同意</van-button>
<van-button :loading="formLoading" type="primary" block @click="handleTaskAction(3)">同意</van-button>
</div> </div>
</div> </template>
</union-reimburse-info> </union-reimburse-info>
<van-dialog :value="submitConfirmVisible" title="温馨提示" message="您确定要提交审核结果吗?" show-cancel-button :confirm-button-loading="formLoading" @confirm="submitTaskAction" @cancel="closeSubmitConfirm"></van-dialog>
</div> </div>
<script nonce="${cspNonce!}"> <script nonce="${cspNonce!}">
@@ -116,11 +80,26 @@ layout("/layouts/platform_h5.html"){
searchKeyword: "", searchKeyword: "",
approvalText: "0", approvalText: "0",
approval: false, approval: false,
year: new Date().getFullYear() year: new Date().getFullYear(),
reimburseProject: ""
}, },
yearList: [],
reimburseProjectOptions: [
{text: "全部项目", value: ""},
{text: "慰问", value: "UNION_REIMBURSE_PROJECT_1"},
{text: "文体活动", value: "UNION_REIMBURSE_PROJECT_2"},
{text: "日常活动", value: "UNION_REIMBURSE_PROJECT_3"},
{text: "专项活动", value: "UNION_REIMBURSE_PROJECT_4"}
],
approvalOptions: [{text: "未审核", value: "0"}, {text: "已审核", value: "1"}],
formData: {}, formData: {},
showApprovalForm: false, showApprovalForm: false,
infoShow: false infoShow: false,
listLoading: true,
formLoading: false,
submitConfirmVisible: false,
pendingSubmitType: null,
historyLayerUnsubscribe: null
} }
}, },
methods: { methods: {
@@ -164,62 +143,63 @@ layout("/layouts/platform_h5.html"){
} }
return stateId || "" return stateId || ""
}, },
getStateClass(stateId) { getStatusClass(stateId) { if (stateId == 3) return "union-reimburse-card-header__status--success"; if (stateId == 4 || stateId == 5) return "union-reimburse-card-header__status--danger"; return "union-reimburse-card-header__status--pending" },
if (stateId == 3) {
return "state-success"
}
if (stateId == 4) {
return "state-danger"
}
return "state-primary"
},
formatCreateTime(value) { formatCreateTime(value) {
return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : "" return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : ""
}, },
handleTaskAction(val) { handleTaskAction(val) {
const opinion = (this.formData.reviewOpinion || "").trim()
if (!opinion) {
this.$toast("请填写审批意见")
this.$refs.formRef.validate("reviewOpinion").catch(() => {})
return
}
this.$set(this.formData, "reviewOpinion", opinion)
this.$refs.formRef.validate().then(() => { this.$refs.formRef.validate().then(() => {
this.$dialog.confirm({ this.$set(this, "pendingSubmitType", val)
title: "温馨提示", const manager = window.h5HistoryLayerManager
message: "您确定要提交吗?" if (manager) { manager.open("union-reimburse-review-confirm"); return }
}).then(() => { this.$set(this, "submitConfirmVisible", true)
this.$axios.post("/platform/unionReimburse/review/reviewTask", { }).catch(() => {})
data: JSON.stringify(Object.assign({}, this.formData)),
submitType: val
}).then(res => {
if (res.code === 0) {
this.$toast.success("提交成功")
this.doSearch()
this.$refs.unionReimburseInfoRef.onClose()
}
})
}).catch(() => {
})
}).catch()
}, },
closeSubmitConfirm(afterClose) { const manager = window.h5HistoryLayerManager; if (manager) { manager.close("union-reimburse-review-confirm", afterClose); return } this.$set(this, "submitConfirmVisible", false); if (afterClose) afterClose() },
submitTaskAction() { const submitType = this.pendingSubmitType; this.closeSubmitConfirm(() => { this.$set(this, "formLoading", true); this.$axios.post("/platform/unionReimburse/review/reviewTask", {data: JSON.stringify(Object.assign({}, this.formData)), submitType: submitType}).then((res) => { if (res.code === 0) { this.$toast.success("提交成功"); this.doSearch(); this.$refs.unionReimburseInfoRef.onClose() } else { this.$toast.fail(res.msg) } }).finally(() => { this.$set(this, "formLoading", false) }) }) },
onView(row) { onView(row) {
this.showApprovalForm = false this.$set(this, "showApprovalForm", false)
this.$refs.unionReimburseInfoRef.onOpen(row) this.$refs.unionReimburseInfoRef.onOpen(row, "报销申请详情")
}, },
onAudit(row) { onAudit(row) {
this.showApprovalForm = true this.$set(this, "showApprovalForm", true)
this.formData = { this.$set(this, "formData", {
id: row.id, id: row.id,
reviewOpinion: "通过" taskName: "校工会审核",
} reviewOpinion: ""
this.$refs.unionReimburseInfoRef.onOpen(row) })
this.$refs.unionReimburseInfoRef.onOpen(row, "报销审核")
}, },
onLoadingChange(state) { this.$set(this, "listLoading", state.loading) },
onNavBack() { const manager = window.h5HistoryLayerManager; if (manager && manager.stack.length) { manager.close(); return } historyBack() },
onApprovalFilterChange(val) { this.$set(this.pageForm, "approval", val === "1"); this.doSearch() },
doSearch() { doSearch() {
this.$nextTick(() => { this.$nextTick(() => {
this.pageForm.pageNumber = 1 this.$set(this.pageForm, "pageNumber", 1)
this.pageForm.totalCount = 0 this.$set(this.pageForm, "totalCount", 0)
this.$refs.tableListRef.doSearch() this.$refs.tableListRef.doSearch()
}) })
} }
}, },
created() { created() {
for (let i = new Date().getFullYear() - 10; i <= new Date().getFullYear(); i++) {
this.yearList.unshift({value: i, text: i + "年"})
}
const manager = window.h5HistoryLayerManager
if (manager) { manager.ensureRegistered("union-reimburse-review"); this.$set(this, "historyLayerUnsubscribe", manager.subscribe((layers) => { this.$set(this, "submitConfirmVisible", layers.includes("union-reimburse-review-confirm")) })) }
},
beforeDestroy() {
if (this.historyLayerUnsubscribe) this.historyLayerUnsubscribe()
const manager = window.h5HistoryLayerManager
if (manager) manager.unregister("union-reimburse-review")
} }
}) })
</script> </script>