This commit is contained in:
2026-07-14 16:28:31 +08:00
15 changed files with 473 additions and 279 deletions
@@ -266,6 +266,11 @@ public class Sys_user extends BaseModel implements Serializable {
@DataCenterColumn(name = "单位", key = "SZDWH")
private String unitId;
@Column
@Comment("三级单位ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String threeUnitId;
@Column
@ColDefine(type = ColType.VARCHAR, width = 100)
private String unitPath;
@@ -262,7 +262,7 @@ public class HonorManageController {
excelExportEntity.add(new ExcelExportEntity("分工会", "unionName", 20));
excelExportEntity.add(new ExcelExportEntity("单位", "unitName", 20));
excelExportEntity.add(new ExcelExportEntity("奖项", "prize", 20));
excelExportEntity.add(new ExcelExportEntity("荣誉", "honorLevel", 20));
excelExportEntity.add(new ExcelExportEntity("荣誉", "honorLevel", 20));
excelExportEntity.add(new ExcelExportEntity("授予单位", "grantUnit", 20));
excelExportEntity.add(new ExcelExportEntity("授予时间", "grantDate", 20));
excelExportEntity.add(new ExcelExportEntity("奖项介质", "awardType", 20));
@@ -276,7 +276,7 @@ public class HonorManageController {
excelExportEntity.add(new ExcelExportEntity("会员人数", "memberNumber", 10));
excelExportEntity.add(new ExcelExportEntity("工会小组数", "unionGroupNumber", 10));
excelExportEntity.add(new ExcelExportEntity("奖项", "prize", 20));
excelExportEntity.add(new ExcelExportEntity("荣誉", "honorLevel", 20));
excelExportEntity.add(new ExcelExportEntity("荣誉", "honorLevel", 20));
excelExportEntity.add(new ExcelExportEntity("授予单位", "grantUnit", 20));
excelExportEntity.add(new ExcelExportEntity("参加比赛名称", "gameName", 20));
excelExportEntity.add(new ExcelExportEntity("授予时间", "grantDate", 20));
@@ -124,6 +124,13 @@ public class UnionReimburseCollectController {
return unionReimburseService.updateActuallyAmount(id, realMoney);
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"unionReimburse.collect", "h5.unionReimburse.collect"}, mode = SaMode.OR)
public Result updateReimburseProject(String id, String reimburseProject) {
return unionReimburseService.updateReimburseProject(id, reimburseProject);
}
@At
@Ok("void")
@SaCheckPermission(value = {"unionReimburse.collect", "h5.unionReimburse.collect"}, mode = SaMode.OR)
@@ -45,6 +45,11 @@ public class UnionReimburseDesc extends BaseModel {
@ColDefine(type = ColType.VARCHAR, width = 200)
private String fileDesc;
@Column
@Comment("报销项目提示")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String projectTip;
@Column
@Comment("排序字段")
@Prev({
@@ -69,6 +69,11 @@ public interface UnionReimburseService extends BaseService<UnionReimburse> {
*/
Result updateActuallyAmount(String id, Double realMoney);
/**
* 修改非慰问报销记录的活动项目。
*/
Result updateReimburseProject(String id, String reimburseProject);
/**
* 批量审核选中的待审核报销申请。
*
@@ -66,6 +66,13 @@ import java.util.stream.Collectors;
@Slf4j
@IocBean(args = {"refer:dao"})
public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> implements UnionReimburseService {
/** 非慰问报销项目允许在三个活动项目之间切换。 */
private static final Set<String> ACTIVITY_REIMBURSE_PROJECTS = Set.of(
"UNION_REIMBURSE_PROJECT_2",
"UNION_REIMBURSE_PROJECT_3",
"UNION_REIMBURSE_PROJECT_4"
);
@Inject
private SysFileService sysFileService;
@Inject
@@ -424,6 +431,11 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
if (dbRecord == null) {
return Result.error("未找到对应的报销记录");
}
// 实际报销金额不得超过申请金额,避免绕过前端校验直接修改数据。
BigDecimal declaredMoney = getDeclaredApplyMoney(dbRecord);
if (newMoney.compareTo(declaredMoney) > 0) {
return Result.error("实际报销金额不能超过申请金额:" + declaredMoney.toPlainString());
}
if (Integer.valueOf(3).equals(dbRecord.getStateId()) && !skipBudgetDeduct(dbRecord)) {
OutlayUseDetail oldDetail = dao().fetch(OutlayUseDetail.class, Cnd.where("outlayReimburseId", "=", dbRecord.getId()));
BigDecimal oldMoney = oldDetail != null && oldDetail.getAdjustMoney() != null
@@ -439,6 +451,28 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
return Result.success();
}
@Override
public Result updateReimburseProject(String id, String reimburseProject) {
if (!isAdmin()) {
return Result.error("无权修改报销项目");
}
if (StrUtil.hasBlank(id, reimburseProject)) {
return Result.error("报销记录ID和报销项目不能为空");
}
UnionReimburse dbRecord = this.fetch(id);
if (dbRecord == null) {
return Result.error("未找到对应的报销记录");
}
// 慰问记录与活动项目字段不同,只允许活动项目之间切换。
if (!ACTIVITY_REIMBURSE_PROJECTS.contains(dbRecord.getReimburseProject())
|| !ACTIVITY_REIMBURSE_PROJECTS.contains(reimburseProject)) {
return Result.error("慰问记录不可修改,报销项目只能在三个活动项目之间切换");
}
dbRecord.setReimburseProject(reimburseProject);
this.updateIgnoreNull(dbRecord);
return Result.success();
}
private void normalizeInvoiceDetails(UnionReimburse unionReimburse) {
if (unionReimburse.getInvoiceDetails() == null) {
unionReimburse.setInvoiceDetails(new ArrayList<>());
@@ -46,6 +46,9 @@ public class WelfareFilterUserPageForm extends PageForm {
@ApiModelProperty("人员类型")
private String[] personTypes;
@ApiModelProperty("人员属性")
private String[] userAttributes;
@ApiModelProperty("聘用方式")
private String[] preparedBys;
@@ -444,11 +444,13 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
u.userState,
u.unitId,
u.unitName,
threeUnit.name AS threeUnitName,
u.unionId,
u.unionName,
u.member
FROM
vw_user u
LEFT JOIN sys_unit threeUnit ON threeUnit.id = u.threeUnitId
LEFT JOIN welfare_list w ON u.id = w.userId AND w.projectId = @projectId
$condition
""");
@@ -471,6 +473,7 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
cnd.andEX("u.unionId", "=", pageForm.getUnionId());
cnd.andEX("u.unitId", "in", pageForm.getUnitIds());
cnd.andEX("u.personType", "in", pageForm.getPersonTypes());
cnd.andEX("u.userAttribute", "in", pageForm.getUserAttributes());
cnd.andEX("u.preparedBy", "in", pageForm.getPreparedBys());
cnd.andEX("u.userState", "in", pageForm.getUserStates());
cnd.andEX("u.member","=",pageForm.getIsMember());
@@ -523,6 +526,7 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
cnd.andEX("unionId", "=", pageForm.getUnionId());
cnd.andEX("unitId", "in", pageForm.getUnitIds());
cnd.andEX("personType", "in", pageForm.getPersonTypes());
cnd.andEX("userAttribute", "in", pageForm.getUserAttributes());
cnd.andEX("preparedBy", "in", pageForm.getPreparedBys());
cnd.andEX("userState", "in", pageForm.getUserStates());
cnd.andEX("member","=",pageForm.getIsMember());
@@ -27,8 +27,8 @@ layout("/layouts/platform.html"){
:value="o.id"></el-option>
</el-select>
</search-item>
<search-item label="荣誉别">
<el-select v-model="pageForm.honorLevel" placeholder="请选择荣誉别" clearable>
<search-item label="荣誉别">
<el-select v-model="pageForm.honorLevel" placeholder="请选择荣誉别" clearable>
<el-option v-for="o in honorLevelList" :key="o.id" :label="o.name" :value="o.id"></el-option>
</el-select>
</search-item>
@@ -77,7 +77,7 @@ layout("/layouts/platform.html"){
sortable></el-table-column>
<!--<el-table-column prop="prizeDesc" label="奖项说明" ></el-table-column>-->
<el-table-column prop="typeName" label="荣誉类型"></el-table-column>
<el-table-column prop="levelName" label="别"
<el-table-column prop="levelName" label="荣誉类别"
sortable></el-table-column>
<el-table-column prop="grantUnit" label="授予单位"
show-overflow-tooltip></el-table-column>
@@ -185,7 +185,7 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-col>-->
<el-col :span="6">
<el-form-item label="别">{{ viewData.levelName }}</el-form-item>
<el-form-item label="荣誉类别">{{ viewData.levelName }}</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="授予单位">{{ viewData.grantUnit }}</el-form-item>
@@ -303,9 +303,9 @@ layout("/layouts/platform.html"){
prize +
"&honorLevel=" +
honorLevel +
"&userName" +
"&userName=" +
userName +
"&unitName" +
"&unitName=" +
unitName
},
openEdit(row) {
@@ -96,6 +96,9 @@ layout("/layouts/platform.html"){
{{item.name}}
</el-radio>
</el-radio-group>
<span class="ml10 text-danger" v-if="getProjectTipByProject(formData.reimburseProject)">
{{getProjectTipByProject(formData.reimburseProject)}}
</span>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="经办人">{{formData.userName}}({{formData.loginName}})</el-descriptions-item>
@@ -400,145 +403,6 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="支付信息" :span="3">
<div class="mb10">
<el-button type="primary" size="mini" @click="addPaymentInfo">新增支付信息</el-button>
</div>
<el-table class="payment-info-table" :data="paymentInfoRows" border empty-text="暂无支付信息" style="width: 100%">
<el-table-column label="支付方式" min-width="160">
<template slot-scope="scope">
<el-form-item :prop="'paymentDetails.' + scope.$index + '.paymentWay'"
:rules="paymentDetailRules.paymentWay">
<el-select v-model="scope.row.paymentWay"
clearable
placeholder="请选择支付方式"
style="width: 100%"
@change="handlePaymentWayChange(scope.row)">
<el-option
v-for="item in dict.type.UNION_REIMBURSE_PAYMENT_WAY"
:key="item.code"
:label="item.name"
:value="item.code">
</el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="金额" min-width="140">
<template slot-scope="scope">
<el-form-item :prop="'paymentDetails.' + scope.$index + '.money'"
:rules="paymentDetailRules.money">
<el-input v-model="scope.row.money" placeholder="请输入金额"></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="付款人" min-width="240">
<template slot-scope="scope">
<el-form-item v-if="isPrivatePayment(scope.row)"
:prop="'paymentDetails.' + scope.$index + '.payer'"
:rules="paymentDetailRules.payer">
<el-select style="width: 100%"
v-model="scope.row.payer"
@change="payerUserChange(scope.row)"
filterable
clearable
remote
reserve-keyword
placeholder="请输入姓名或工号查询"
:remote-method="createRemoteMethodForPayer(scope.row)">
<el-option
v-for="item in scope.row.payerOptions"
:key="item.id"
:label="item.userName+''+item.loginName+''+''+item.unitName+''"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<span v-else>无需填写</span>
</template>
</el-table-column>
<el-table-column label="户名" min-width="180">
<template slot-scope="scope">
<el-form-item v-if="isPrivatePayment(scope.row)"
:prop="'paymentDetails.' + scope.$index + '.bankUserName'"
:rules="paymentDetailRules.bankUserName">
<el-select
v-model="scope.row.bankUserName"
@change="handleBankUserNameChange(scope.row)"
clearable
filterable
allow-create
default-first-option
placeholder="请输入户名"
style="width: 100%">
<el-option
v-for="(item, index) in getBankHistoryUserNames(scope.row)"
:key="index"
:label="item"
:value="item">
</el-option>
</el-select>
</el-form-item>
<span v-else>无需填写</span>
</template>
</el-table-column>
<el-table-column label="开户行" min-width="220">
<template slot-scope="scope">
<el-form-item v-if="isPrivatePayment(scope.row)"
:prop="'paymentDetails.' + scope.$index + '.bankOfDeposit'"
:rules="paymentDetailRules.bankOfDeposit">
<el-select
v-model="scope.row.bankOfDeposit"
@change="fillBankInfoFromHistory(scope.row)"
clearable
filterable
allow-create
default-first-option
placeholder="请输入开户行名称"
style="width: 100%">
<el-option
v-for="(item, index) in scope.row.historyData"
:key="index"
:label="item.bankOfDeposit"
:value="item.bankOfDeposit">
</el-option>
</el-select>
</el-form-item>
<span v-else>无需填写</span>
</template>
</el-table-column>
<el-table-column label="银行卡号" min-width="220">
<template slot-scope="scope">
<el-form-item v-if="isPrivatePayment(scope.row)"
:prop="'paymentDetails.' + scope.$index + '.bankCardNumber'"
:rules="paymentDetailRules.bankCardNumber">
<el-select
v-model="scope.row.bankCardNumber"
@change="fillBankInfoFromHistory(scope.row)"
clearable
filterable
allow-create
default-first-option
placeholder="请输入银行卡号"
style="width: 100%">
<el-option
v-for="(item, index) in scope.row.historyData"
:key="index"
:label="item.bankCardNumber"
:value="item.bankCardNumber">
</el-option>
</el-select>
</el-form-item>
<span v-else>无需填写</span>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="120">
<template slot-scope="scope">
<el-button type="text" style="color: #f56c6c;" @click="deletePaymentInfo(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-descriptions-item>
<el-descriptions-item label="参加随行人员" :span="3"
v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
<el-form-item prop="participants" label="参加随行人员">
@@ -563,7 +427,8 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-descriptions-item>
<el-descriptions-item :span="3">
<el-descriptions-item :span="3"
v-if="formData.reimburseProject !== 'UNION_REIMBURSE_PROJECT_1'">
<template slot="label">
附件
</template>
@@ -619,6 +484,148 @@ layout("/layouts/platform.html"){
type="textarea" :autosize="{ minRows: 4, maxRows: 8}"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<div class="invoice-field-title">支付信息</div>
<div>
<div class="mb10">
<el-button type="primary" size="mini" @click="addPaymentInfo">新增支付信息</el-button>
</div>
<el-table class="payment-info-table" :data="paymentInfoRows" border empty-text="暂无支付信息" style="width: 100%">
<el-table-column label="支付方式" min-width="160">
<template slot-scope="scope">
<el-form-item :prop="'paymentDetails.' + scope.$index + '.paymentWay'"
:rules="paymentDetailRules.paymentWay">
<el-select v-model="scope.row.paymentWay"
clearable
placeholder="请选择支付方式"
style="width: 100%"
@change="handlePaymentWayChange(scope.row)">
<el-option
v-for="item in dict.type.UNION_REIMBURSE_PAYMENT_WAY"
:key="item.code"
:label="item.name"
:value="item.code">
</el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="金额" min-width="140">
<template slot-scope="scope">
<el-form-item :prop="'paymentDetails.' + scope.$index + '.money'"
:rules="paymentDetailRules.money">
<el-input v-model="scope.row.money" placeholder="请输入金额"></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="付款人" min-width="240">
<template slot-scope="scope">
<el-form-item v-if="isPrivatePayment(scope.row)"
:prop="'paymentDetails.' + scope.$index + '.payer'"
:rules="paymentDetailRules.payer">
<el-select style="width: 100%"
v-model="scope.row.payer"
@change="payerUserChange(scope.row)"
filterable
clearable
remote
reserve-keyword
placeholder="请输入姓名或工号查询"
:remote-method="createRemoteMethodForPayer(scope.row)">
<el-option
v-for="item in scope.row.payerOptions"
:key="item.id"
:label="item.userName+''+item.loginName+''+''+item.unitName+''"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<span v-else>无需填写</span>
</template>
</el-table-column>
<el-table-column label="户名" min-width="180">
<template slot-scope="scope">
<el-form-item v-if="isPrivatePayment(scope.row)"
:prop="'paymentDetails.' + scope.$index + '.bankUserName'"
:rules="paymentDetailRules.bankUserName">
<el-select
v-model="scope.row.bankUserName"
@change="handleBankUserNameChange(scope.row)"
clearable
filterable
allow-create
default-first-option
placeholder="请输入户名"
style="width: 100%">
<el-option
v-for="(item, index) in getBankHistoryUserNames(scope.row)"
:key="index"
:label="item"
:value="item">
</el-option>
</el-select>
</el-form-item>
<span v-else>无需填写</span>
</template>
</el-table-column>
<el-table-column label="开户行" min-width="220">
<template slot-scope="scope">
<el-form-item v-if="isPrivatePayment(scope.row)"
:prop="'paymentDetails.' + scope.$index + '.bankOfDeposit'"
:rules="paymentDetailRules.bankOfDeposit">
<el-select
v-model="scope.row.bankOfDeposit"
@change="fillBankInfoFromHistory(scope.row)"
clearable
filterable
allow-create
default-first-option
placeholder="请输入开户行名称"
style="width: 100%">
<el-option
v-for="(item, index) in scope.row.historyData"
:key="index"
:label="item.bankOfDeposit"
:value="item.bankOfDeposit">
</el-option>
</el-select>
</el-form-item>
<span v-else>无需填写</span>
</template>
</el-table-column>
<el-table-column label="银行卡号" min-width="220">
<template slot-scope="scope">
<el-form-item v-if="isPrivatePayment(scope.row)"
:prop="'paymentDetails.' + scope.$index + '.bankCardNumber'"
:rules="paymentDetailRules.bankCardNumber">
<el-select
v-model="scope.row.bankCardNumber"
@change="fillBankInfoFromHistory(scope.row)"
clearable
filterable
allow-create
default-first-option
placeholder="请输入银行卡号"
style="width: 100%">
<el-option
v-for="(item, index) in scope.row.historyData"
:key="index"
:label="item.bankCardNumber"
:value="item.bankCardNumber">
</el-option>
</el-select>
</el-form-item>
<span v-else>无需填写</span>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="120">
<template slot-scope="scope">
<el-button type="text" style="color: #f56c6c;" @click="deletePaymentInfo(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-col>
<el-col :span="24">
<div class="invoice-toolbar">
<div>
@@ -819,7 +826,21 @@ layout("/layouts/platform.html"){
{required: true, message: "请填写联系方式", trigger: ["change", "blur"]},
{pattern: /^(1[3-9]\d{9}|0\d{2,3}-?\d{7,8})$/, message: "请输入正确的联系方式", trigger: "blur"}
],
files: [{required: true, message: "请上传附件", trigger: ["change", "blur"]}],
files: [{
validator: (rule, value, callback) => {
// 慰问项目不展示附件上传,提交时无需校验附件。
if (this.formData.reimburseProject === "UNION_REIMBURSE_PROJECT_1") {
callback()
return
}
if (!value || value.length === 0) {
callback(new Error("请上传附件"))
return
}
callback()
},
trigger: ["change", "blur"]
}],
userSign: [{required: true, message: "请签名", trigger: ["change", "blur"]}],
// certifierUserId: [{required: true, message: "请填写认证人", trigger: ["change", "blur"]}],
@@ -1205,6 +1226,11 @@ layout("/layouts/platform.html"){
this.rebuildInvoiceSummary()
this.normalizeFundBalance()
}
this.$nextTick(() => {
if (this.$refs.formRef) {
this.$refs.formRef.clearValidate("files")
}
})
},
openInvoiceDialog(mode, row, index) {
this.invoiceDialogMode = mode
@@ -1561,10 +1587,10 @@ layout("/layouts/platform.html"){
return new Promise((resolve) => {
this.rebuildInvoiceSummary()
const paymentDetails = this.preparePaymentDetailsForSubmit()
this.$refs.formRef.validate((valid) => {
this.$refs.formRef.validate((valid, invalidFields) => {
if (valid) {
if (!this.validatePaymentDetailsBeforeSubmit(paymentDetails)) {
this.$set(this, "activeTabName", "basic")
this.$set(this, "activeTabName", "invoice")
resolve(false)
return
}
@@ -1581,6 +1607,12 @@ layout("/layouts/platform.html"){
this.normalizeFundBalance()
resolve(true)
} else {
// 支付信息已移至发票信息页,支付明细校验失败时跳转到对应页签。
const paymentDetailInvalid = Object.keys(invalidFields || {})
.some((field) => field.indexOf("paymentDetails.") === 0)
if (paymentDetailInvalid) {
this.$set(this, "activeTabName", "invoice")
}
this.$message.warning("请完善必填信息")
resolve(false)
}
@@ -1592,7 +1624,7 @@ layout("/layouts/platform.html"){
const isValid = await this.validateBeforeSubmit()
if (!isValid) return
this.$confirm("您确定要提交吗?", "提示", {
this.$confirm("您确定要提交吗?审核通过后可前往我的报销打印!", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
@@ -1612,7 +1644,7 @@ layout("/layouts/platform.html"){
const isValid = await this.validateBeforeSubmit()
if (!isValid) return
this.$confirm("您确定要提交吗?", "提示", {
this.$confirm("您确定要提交吗?审核通过后可前往我的报销打印!", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
@@ -1650,6 +1682,14 @@ layout("/layouts/platform.html"){
const project = this.descOptions.find(item => item.reimburseProject === projectCode);
return project ? project.fileDesc : '';
},
// 根据当前选择的报销项目获取管理端配置的提示内容。
getProjectTipByProject(projectCode) {
if (!this.descOptions || !Array.isArray(this.descOptions)) {
return '';
}
const project = this.descOptions.find(item => item.reimburseProject === projectCode);
return project ? project.projectTip : '';
},
// 新建申请默认优先选择分工会经费;若当前用户没有对应权限,则按页面可见权限回退到其它经费来源。
getDefaultFundSource() {
if (this.$auth.hasPermission("unionReimburse.fgh")) {
@@ -62,11 +62,17 @@ layout("/layouts/platform.html"){
</el-table-column>
<el-table-column prop="reimburseProject" label="报销项目" sortable="custom">
<template slot-scope="{row}">
<span v-if="row.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">慰问</span>
<span v-else-if="row.reimburseProject === 'UNION_REIMBURSE_PROJECT_2'">文体活动</span>
<span v-else-if="row.reimburseProject === 'UNION_REIMBURSE_PROJECT_3'">日常活动</span>
<span v-else-if="row.reimburseProject === 'UNION_REIMBURSE_PROJECT_4'">专项活动</span>
<span v-else>{{ row.reimburseProject }}</span>
<el-select v-if="isAdmin && row.reimburseProject !== 'UNION_REIMBURSE_PROJECT_1'"
:value="row.reimburseProject"
size="mini"
style="width: 120px"
@change="updateReimburseProject(row, $event)">
<el-option v-for="item in activityReimburseProjectOptions"
:key="item.code"
:label="item.name"
:value="item.code"></el-option>
</el-select>
<span v-else>{{ getReimburseProjectName(row.reimburseProject) }}</span>
</template>
</el-table-column>
<el-table-column label="备注">
@@ -106,6 +112,7 @@ layout("/layouts/platform.html"){
<el-form :model="editFormData" :rules="editFormRules" ref="editForm" label-width="120px">
<el-form-item label="实际报销金额" prop="realMoney">
<el-input v-model="editFormData.realMoney" placeholder="请输入实际报销金额"></el-input>
<div class="text-muted mt5">申请金额:{{ editFormData.maxMoney }}</div>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -137,7 +144,8 @@ layout("/layouts/platform.html"){
editDialogVisible: false,
editFormData: {
realMoney: '',
id: '' // 报销记录ID
id: '', // 报销记录ID
maxMoney: '0.00' // 申请金额上限
},
editFormRules: {
realMoney: [
@@ -151,12 +159,50 @@ layout("/layouts/platform.html"){
computed: {
isAdmin() {
return this.$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN'])
},
activityReimburseProjectOptions() {
const activityProjectCodes = [
"UNION_REIMBURSE_PROJECT_2",
"UNION_REIMBURSE_PROJECT_3",
"UNION_REIMBURSE_PROJECT_4"
]
return (this.dict.type.UNION_REIMBURSE_PROJECT || [])
.filter((item) => activityProjectCodes.includes(item.code))
}
},
methods: {
formatCreateTime(value) {
return value && this.$moment(value).isValid() ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : ""
},
getReimburseProjectName(reimburseProject) {
const project = (this.dict.type.UNION_REIMBURSE_PROJECT || [])
.find((item) => item.code === reimburseProject)
return project ? project.name : reimburseProject
},
async updateReimburseProject(row, reimburseProject) {
if (reimburseProject === row.reimburseProject) {
return
}
try {
await this.$confirm("确定修改该记录的报销项目吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
const res = await this.$axios.post("/platform/unionReimburse/collect/updateReimburseProject", {
id: row.id,
reimburseProject: reimburseProject
})
if (res.code === 0) {
this.$set(row, "reimburseProject", reimburseProject)
this.$message.success("报销项目修改成功")
} else {
this.$message.error(res.msg)
}
} catch (error) {
// 取消确认时保持列表原项目,不修改页面数据。
}
},
onView(row) {
this.$refs.guava.edit(()=>{
this.showApprovalForm = false
@@ -179,8 +225,10 @@ layout("/layouts/platform.html"){
},
openEdit(row) {
// 初始化编辑表单数据
this.editFormData.id = row.id;
this.editFormData.realMoney = row.realMoney || '';
const applyMoney = row.money != null ? row.money : row.condolenceMoney
this.$set(this.editFormData, "id", row.id)
this.$set(this.editFormData, "realMoney", row.realMoney || '')
this.$set(this.editFormData, "maxMoney", Number(applyMoney || 0).toFixed(2))
this.editDialogVisible = true;
},
@@ -195,6 +243,10 @@ layout("/layouts/platform.html"){
saveFinallyMoney() {
this.$refs.editForm.validate((valid) => {
if (valid) {
if (Number(this.editFormData.realMoney) > Number(this.editFormData.maxMoney)) {
this.$message.error("实际报销金额不能超过申请金额:" + this.editFormData.maxMoney)
return
}
this.$axios.post("/platform/unionReimburse/collect/updateActuallyAmount", {
id: this.editFormData.id,
realMoney: this.editFormData.realMoney
@@ -14,6 +14,10 @@ const basicForm = {
<el-input type="text" v-model="formData.fileDesc" maxlength="50"
placeholder="请填写附件说明"></el-input>
</el-form-item>
<el-form-item label="报销项目提示" prop="projectTip">
<el-input type="textarea" v-model="formData.projectTip" maxlength="500" show-word-limit
placeholder="请填写报销项目提示"></el-input>
</el-form-item>
</el-form-item>
</el-form>
@@ -84,6 +84,7 @@ layout("/layouts/platform.html"){
{prop: 'code', label: '项目编码'},
{prop: 'name', label: '项目名称'},
{prop: 'fileDesc', label: '附件说明'},
{prop: 'projectTip', label: '报销项目提示'},
// {prop: 'isUploadFile', label: '是否上传附件'},
],
}
@@ -84,6 +84,13 @@ const filterUser = {
v-model="pageForm.personTypes"></dict-select>
</search-item>
<search-item label="人员属性:">
<dict-select clearable code="USER_ATTRIBUTE"
multiple
collapse-tags
placeholder="请选择人员属性"
v-model="pageForm.userAttributes"></dict-select>
</search-item>
<search-item label="在职状态:">
<dict-select clearable code="USER_STATE"
@@ -152,6 +159,7 @@ const filterUser = {
userStates: [],
personTypes: [],
preparedBys: [],
userAttributes: [],
unitIds: []
},
sexOptions: ["男", "女"],
@@ -165,7 +173,8 @@ const filterUser = {
{ prop: "personType", label: "人员类型", sortable: true },
{ prop: "userState", label: "在职状态", sortable: true },
{ prop: "unionName", label: "所属工会", sortable: true },
{ prop: "unitName", label: "所属单位", sortable: true }
{ prop: "unitName", label: "所属单位", sortable: true },
{ prop: "threeUnitName", label: "三级单位", sortable: true }
]
}
},
@@ -186,9 +195,10 @@ const filterUser = {
this.$set(this.pageForm, "birthday", null)
this.$set(this.pageForm, "birthMonths", [])
this.$set(this.pageForm, "unionId", null)
this.$set(this.pageForm, "unitId", null)
this.$set(this.pageForm, "unitIds", [])
this.$set(this.pageForm, "personTypes", [])
this.$set(this.pageForm, "preparedBys", [])
this.$set(this.pageForm, "userAttributes", [])
this.$set(this.pageForm, "userStates", [])
this.$set(this.pageForm, "isMember", null)
},
@@ -439,6 +439,11 @@ layout("/layouts/platform_h5.html"){
required
is-link
></van-field>
<van-cell v-if="getProjectTipByProject(formData.reimburseProject)">
<template #title>
<span style="color: #ee0a24; text-align: left;">报销提示:{{getProjectTipByProject(formData.reimburseProject)}}</span>
</template>
</van-cell>
<van-popup position="bottom" round v-model:show="showReimburseProjectPicker">
<van-picker :columns="reimburseProjectColumns" @cancel="closePopup('showReimburseProjectPicker')"
@confirm="onReimburseProjectConfirm" show-toolbar></van-picker>
@@ -528,120 +533,6 @@ layout("/layouts/platform_h5.html"){
-->
</van-cell-group>
<van-cell-group class="form-section">
<div class="payment-section">
<div class="payment-toolbar">
<span>支付信息</span>
<van-button native-type="button" size="small" type="primary" icon="plus" @click="addPaymentInfo">新增</van-button>
</div>
<div class="invoice-tip" v-if="!paymentInfoRows.length">请至少维护一条支付信息。</div>
<van-swipe
ref="paymentSwipe"
class="payment-swipe"
:loop="false"
:show-indicators="false"
@change="handlePaymentSwipeChange"
v-if="paymentInfoRows.length">
<van-swipe-item v-for="(item, index) in paymentInfoRows" :key="index">
<div class="payment-detail-item">
<div class="payment-detail-title">
<div class="payment-title-left">
<span>支付信息{{index + 1}}</span>
<span class="payment-index-text">{{index + 1}} / {{paymentInfoRows.length}}</span>
</div>
<van-tag v-if="item.paymentWayName" type="primary">{{item.paymentWayName}}</van-tag>
</div>
<van-field
v-model="item.paymentWayName"
label="支付方式"
readonly
placeholder="请选择支付方式"
@click="openPaymentWayPicker(item, index)"
clickable
required
is-link
></van-field>
<van-field
label="金额"
v-model="item.money"
placeholder="请输入金额"
type="number"
required
></van-field>
<template v-if="isPrivatePayment(item)">
<van-field
v-model="item.payerName"
label="付款人"
readonly
placeholder="请选择付款人"
@click="openPayerSelect(item, index)"
clickable
required
is-link
></van-field>
<van-field
label="户名"
v-model="item.bankUserName"
placeholder="请输入户名"
required
></van-field>
<van-field
label="开户行"
v-model="item.bankOfDeposit"
placeholder="请输入开户行"
required
></van-field>
<van-field
label="银行卡号"
v-model="item.bankCardNumber"
placeholder="请输入银行卡号"
required
></van-field>
</template>
<div class="payment-detail-actions">
<div class="payment-nav-buttons">
<span class="payment-nav-btn" :class="{active: currentPaymentIndex > 0}" @click="showPrevPayment">
<van-icon name="arrow-left"></van-icon>
</span>
<span class="payment-nav-btn" :class="{active: currentPaymentIndex < paymentInfoRows.length - 1}" @click="showNextPayment">
<van-icon name="arrow"></van-icon>
</span>
</div>
<van-button class="payment-delete-btn" native-type="button" size="small" plain type="danger" icon="delete-o" @click="deletePaymentInfo(index)">删除</van-button>
</div>
</div>
</van-swipe-item>
</van-swipe>
<div class="payment-dots" v-if="paymentInfoRows.length > 1">
<span class="payment-dot" :class="{active: index === currentPaymentIndex}" v-for="(item, index) in paymentInfoRows"></span>
</div>
</div>
<van-popup position="bottom" round v-model:show="showPaymentWayPicker">
<van-picker :columns="paymentWayColumns" @cancel="closePopup('showPaymentWayPicker')"
@confirm="onPaymentWayConfirm" show-toolbar></van-picker>
</van-popup>
<van-action-sheet v-model="payerSelectShow" title="付款人" class="height100">
<van-search
v-model="payerSearchKeyword"
:show-action="false"
:reverse-color="false"
input-align="left"
placeholder="请输入工号或姓名选择付款人"
@input="payerRemoteMethod"
@clear="payerRemoteMethod"
shape="round"
></van-search>
<div class="van-action-sheet__content mt5" v-if="payerOptions && payerOptions.length>0">
<template v-for="item in payerOptions">
<van-button native-type="button" @click="payerUserChange(item)"
class="van-action-sheet__item van-hairline--bottom">
<span class="van-action-sheet__name">{{item.userName}}{{item.loginName}}</span>
</van-button>
</template>
</div>
<van-empty description="暂无数据" v-else></van-empty>
</van-action-sheet>
</van-cell-group>
<van-cell-group :title="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1' ? '慰问信息' : '活动信息'" class="form-section">
<template v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
@@ -921,6 +812,120 @@ layout("/layouts/platform_h5.html"){
</template>
</van-field>
</van-cell-group>
<van-cell-group class="form-section">
<div class="payment-section">
<div class="payment-toolbar">
<span>支付信息</span>
<van-button native-type="button" size="small" type="primary" icon="plus" @click="addPaymentInfo">新增</van-button>
</div>
<div class="invoice-tip" v-if="!paymentInfoRows.length">请至少维护一条支付信息。</div>
<van-swipe
ref="paymentSwipe"
class="payment-swipe"
:loop="false"
:show-indicators="false"
@change="handlePaymentSwipeChange"
v-if="paymentInfoRows.length">
<van-swipe-item v-for="(item, index) in paymentInfoRows" :key="index">
<div class="payment-detail-item">
<div class="payment-detail-title">
<div class="payment-title-left">
<span>支付信息{{index + 1}}</span>
<span class="payment-index-text">{{index + 1}} / {{paymentInfoRows.length}}</span>
</div>
<van-tag v-if="item.paymentWayName" type="primary">{{item.paymentWayName}}</van-tag>
</div>
<van-field
v-model="item.paymentWayName"
label="支付方式"
readonly
placeholder="请选择支付方式"
@click="openPaymentWayPicker(item, index)"
clickable
required
is-link
></van-field>
<van-field
label="金额"
v-model="item.money"
placeholder="请输入金额"
type="number"
required
></van-field>
<template v-if="isPrivatePayment(item)">
<van-field
v-model="item.payerName"
label="付款人"
readonly
placeholder="请选择付款人"
@click="openPayerSelect(item, index)"
clickable
required
is-link
></van-field>
<van-field
label="户名"
v-model="item.bankUserName"
placeholder="请输入户名"
required
></van-field>
<van-field
label="开户行"
v-model="item.bankOfDeposit"
placeholder="请输入开户行"
required
></van-field>
<van-field
label="银行卡号"
v-model="item.bankCardNumber"
placeholder="请输入银行卡号"
required
></van-field>
</template>
<div class="payment-detail-actions">
<div class="payment-nav-buttons">
<span class="payment-nav-btn" :class="{active: currentPaymentIndex > 0}" @click="showPrevPayment">
<van-icon name="arrow-left"></van-icon>
</span>
<span class="payment-nav-btn" :class="{active: currentPaymentIndex < paymentInfoRows.length - 1}" @click="showNextPayment">
<van-icon name="arrow"></van-icon>
</span>
</div>
<van-button class="payment-delete-btn" native-type="button" size="small" plain type="danger" icon="delete-o" @click="deletePaymentInfo(index)">删除</van-button>
</div>
</div>
</van-swipe-item>
</van-swipe>
<div class="payment-dots" v-if="paymentInfoRows.length > 1">
<span class="payment-dot" :class="{active: index === currentPaymentIndex}" v-for="(item, index) in paymentInfoRows"></span>
</div>
</div>
<van-popup position="bottom" round v-model:show="showPaymentWayPicker">
<van-picker :columns="paymentWayColumns" @cancel="closePopup('showPaymentWayPicker')"
@confirm="onPaymentWayConfirm" show-toolbar></van-picker>
</van-popup>
<van-action-sheet v-model="payerSelectShow" title="付款人" class="height100">
<van-search
v-model="payerSearchKeyword"
:show-action="false"
:reverse-color="false"
input-align="left"
placeholder="请输入工号或姓名选择付款人"
@input="payerRemoteMethod"
@clear="payerRemoteMethod"
shape="round"
></van-search>
<div class="van-action-sheet__content mt5" v-if="payerOptions && payerOptions.length>0">
<template v-for="item in payerOptions">
<van-button native-type="button" @click="payerUserChange(item)"
class="van-action-sheet__item van-hairline--bottom">
<span class="van-action-sheet__name">{{item.userName}}{{item.loginName}}</span>
</van-button>
</template>
</div>
<van-empty description="暂无数据" v-else></van-empty>
</van-action-sheet>
</van-cell-group>
<van-cell-group title="电子发票" class="form-section" v-if="formData.reimburseProject">
<van-field label="报销金额"
v-model="formData.money"
@@ -1186,6 +1191,7 @@ layout("/layouts/platform_h5.html"){
clubOptions: [],
condolenceOptions: [],
typeOptions: [],
descOptions: [],
// 添加报销项目原始数据列表
reimburseProjectList: [],
// 添加日期选择器相关变量
@@ -2025,7 +2031,7 @@ layout("/layouts/platform_h5.html"){
}
this.$dialog.confirm({
title: "温馨提示",
message: "您确定要提交吗?"
message: "您确定要提交吗?审核通过后可前往pc端我的报销打印!"
}).then(() => {
this.$axios.post("/platform/unionReimburse/apply/submit", { data: JSON.stringify(this.buildSubmitFormData(paymentDetails)) })
.then(res => {
@@ -2064,7 +2070,7 @@ layout("/layouts/platform_h5.html"){
}
this.$dialog.confirm({
title: "温馨提示",
message: "您确定要提交吗?"
message: "您确定要提交吗?审核通过后可前往pc端我的报销打印!"
}).then(() => {
this.$axios.post("/platform/unionReimburse/apply/submitAgain", {
data: JSON.stringify(this.buildSubmitFormData(paymentDetails)),
@@ -2195,6 +2201,23 @@ layout("/layouts/platform_h5.html"){
const matchedFundSource = this.reimburseFundSourceColumns.find(item => item.value === code)
return matchedFundSource ? matchedFundSource.text : ""
},
// 查询管理端配置的报销项目提示。
queryProjectDesc() {
return this.$axios.post("/platform/unionReimburse/desc/queryFileDesc")
.then((resp) => {
if (resp.code === 0) {
this.$set(this, "descOptions", resp.data || [])
}
})
},
// 根据当前选择的报销项目获取管理端配置的提示内容。
getProjectTipByProject(projectCode) {
if (!this.descOptions || !Array.isArray(this.descOptions)) {
return ""
}
const project = this.descOptions.find(item => item.reimburseProject === projectCode)
return project ? project.projectTip : ""
},
async initDictOptions() {
// 报销类别
const reimburseTypeData = await this.$businessTool.getDictOptions("UNION_REIMBURSE_TYPE")
@@ -2558,6 +2581,7 @@ layout("/layouts/platform_h5.html"){
async created() {
await this.initDictOptions()
await this.queryProjectDesc()
await this.queryCondolenceType()
await this.queryClubs()
await this.init()