This commit is contained in:
=
2026-05-27 14:34:19 +08:00
parent 1a8bd11302
commit d6f0c5d507
3 changed files with 384 additions and 0 deletions
@@ -0,0 +1,129 @@
package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.service.ActivityReimbursementService;
import com.budwk.app.zhgh.activity.declarereimbursement.vo.CommonPageParam;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.util.List;
/**
* 费用报销出纳支付节点列表。
* 这里直接复用通用流程审批能力,只负责按出纳节点筛选待办和已办数据。
*/
@IocBean
@Ok("json")
@ApiOperation("活动报销,出纳支付")
@At("/platform/activityReimbursement/cashier")
public class ActivityReimbursementCashierController {
@Inject
private ActivityReimbursementService activityReimbursementService;
@At("")
@SaCheckPermission("activityReimbursement.cashier")
@Ok("beetl:/platform/zhgh/activity/declarereimbursement/reimbursement/cashier/index.html")
public void index() {
}
@At
@ApiOperation("活动报销,出纳支付列表")
@SaCheckPermission("activityReimbursement.cashier")
public Result pageData(CommonPageParam pageParam) {
Sql sql = Sqls.create("""
SELECT
info.id,
info.userName,
info.mobile,
info.loginName,
info.applyTime,
info.activityName,
info.activityAddress,
info.planStartTime,
info.planEndTime,
info.declareUnitName,
info.activityType,
info.activityContent,
info.activityNumber,
info.activityReimbursementMode,
info.outlayManageSource,
info.deductionBudgetId,
info.actualMoney,
outlay.budgetReimburseType,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName,
IF(rt.id IS NOT NULL, 1, 0) AS canRevoke
FROM
wf_process_task t
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
LEFT JOIN activity_reimbursement_info info ON info.id = ins.businessNo
LEFT JOIN activity_budget outlay on info.deductionBudgetId=outlay.id
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
$condition
""");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageParam.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("info.userName", pageParam.getSearchKeyword());
seg.orLike("info.loginName", pageParam.getSearchKeyword());
cnd.and(seg);
}
cnd.andEX("info.unionId", "=", pageParam.getUnionId());
cnd.andEX("info.clubId", "=", pageParam.getClubId());
cnd.and("t.taskName", "=", "e283f33f-4ac7-4c66-9b16-e0653e8dc452");
if (!StpUtil.hasRole(RoleConstant.SYSADMIN.name())) {
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
}
cnd.andEX("outlay.budgetReimburseType", "=", pageParam.getBudgetReimburseType());
cnd.andEX("info.outlayManageSource", "=", pageParam.getOutlayManageSource());
cnd.andEX("info.activityReimbursementMode", "=", pageParam.getActivityReimbursementMode());
cnd.andEX("YEAR(info.applyTime)", "=", pageParam.getYear());
if (pageParam.getAudit()) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.WITHDRAW.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
if (StrUtil.isAllBlank(pageParam.getPageOrderName(), pageParam.getPageOrderBy())) {
cnd.desc("info.applyTime");
} else {
cnd.orderBy("info." + pageParam.getPageOrderName(), PageUtil.getOrder(pageParam.getPageOrderBy()));
}
cnd.groupBy("t.id");
sql.setCondition(cnd);
Pagination<NutMap> pagination = activityReimbursementService.listPageMap(pageParam.getPageNumber(), pageParam.getPageSize(), sql);
return Result.success().addData(pagination);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,255 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app">
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度">
<el-date-picker
placeholder="请选择年度"
type="year"
style="width: 100%"
v-model="pageForm.year"
value-format="yyyy">
</el-date-picker>
</search-item>
<search-item label="申请人">
<el-input placeholder="请输入申请人工号或姓名" clearable
v-model="pageForm.searchKeyword"></el-input>
</search-item>
<search-item label="报销模式">
<el-select clearable filterable placeholder="请选择报销模式"
style="width: 100%;" v-model="pageForm.activityReimbursementMode">
<el-option label="活动报销" value="activity"></el-option>
<el-option label="日常报销" value="daily"></el-option>
</el-select>
</search-item>
<search-item label="经费类型">
<dict-select code="ACTIVITY_BUDGET_TYPE" v-model="pageForm.outlayManageSource"
placeholder="请选择经费类型"></dict-select>
</search-item>
<search-item label="预算条目">
<dict-select code="ACTIVITY_BUDGET_REIMBURSE_TYPE" v-model="pageForm.budgetReimburseType"
placeholder="请选择预算条目"></dict-select>
</search-item>
<search-item label="所属工会">
<el-select clearable
filterable
placeholder="请选择所属工会"
style="width: 100%;"
v-model="pageForm.unionId">
<el-option :label="item.name" :value="item.id"
v-for="item in unions"></el-option>
</el-select>
</search-item>
<search-item label="所属协会">
<el-select clearable filterable placeholder="所属协会"
style="width: 100%;" v-model="pageForm.clubId">
<el-option :label="item.clubName" :value="item.id"
v-for="item in clubs"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card shadow="never" class="mt10">
<table-tool :app="this" label="审批列表">
<el-radio-group @change="doSearch" class="mr5" size="small" v-model="pageForm.audit">
<el-radio-button label="true">已审核</el-radio-button>
<el-radio-button label="false">未审核</el-radio-button>
</el-radio-group>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
ref="table" row-key="id" style="width: 100%">
<el-table-column :index="indexMethod" header-align="center" label="序号"
type="index" width="60px"></el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
:sortable="column.sortable"
:width="column.width"
header-align="center"
min-width="100px"
show-overflow-tooltip
v-for="column in tableColumns"
>
<template scope="{row}" v-if="column.prop=='loginName'">
<span>{{row.userName + '(' + row.loginName + ')' }}</span>
</template>
<template scope="{row}" v-else-if="column.prop=='outlayManageSource'">
<dict-tag :options="dict.type.ACTIVITY_BUDGET_TYPE" :value="row.outlayManageSource"></dict-tag>
</template>
<template scope="{row}" v-else-if="column.prop=='budgetReimburseType'">
<dict-tag :options="dict.type.ACTIVITY_BUDGET_REIMBURSE_TYPE"
:value="row.budgetReimburseType"></dict-tag>
</template>
<template scope="{row}" v-else-if="column.prop=='activityReimbursementMode'">
<span>{{ row.activityReimbursementMode==='activity'?'活动报销' : '日常报销' }}</span>
</template>
<template scope="{row}" v-else-if="column.prop=='instanceState'">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
size="small"></enum-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="200px">
<template slot-scope="{row}">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskState === 10" @click="openAudit(row)" size="mini" type="primary">审核
</el-button>
<el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回
</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #edit>
<info ref="infoRef">
<div v-if="showApprovalForm">
<div class="process-title">
{{formData.taskName}}
</div>
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="0" label-suffix=""
class="flow-task-form">
<el-form-item label="审批意见" prop="tf_opinion"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item>
<el-form-item label="电子签名" prop="tf_userSign"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<pc-signature v-model="formData.tf_userSign"></pc-signature>
</el-form-item>
</el-form>
<el-row type="flex" justify="end">
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-button @click="handleTaskAction(6)" size="small" type="info">退回到发起人</el-button>
<el-button @click="handleTaskAction(2)" size="small" type="danger">拒绝申请</el-button>
<el-button @click="handleTaskAction(1)" size="small" type="primary">同意申请</el-button>
</el-row>
</div>
</info>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
<!--#include("../common/info.js"){}#-->
new Vue({
el: "#app",
mixins: [initTableMixins],
store,
dicts: ["ACTIVITY_BUDGET_TYPE", "ACTIVITY_BUDGET_REIMBURSE_TYPE"],
data() {
return {
pageForm: {
year: this.$moment().format("YYYY")
},
tableColumns: [
{prop: "loginName", label: "申报人", sortable: true},
{prop: "activityName", label: "项目名称"},
{prop: "activityReimbursementMode", label: "报销模式"},
{prop: "outlayManageSource", label: "经费类型"},
{prop: "budgetReimburseType", label: "预算条目"},
{prop: "actualMoney", label: "报销金额(元)"},
{prop: "declareUnitName", label: "申报单位/协会", sortable: true},
{prop: "applyTime", label: "申报时间", sortable: true},
{prop: "curTaskName", label: "当前节点"},
{prop: "instanceState", label: "流程状态"}
],
activityDeclareReimbursementList: [],
unions: [],
units: [],
clubs: [],
// 审核相关
formData: {},
showApprovalForm: false
}
},
components: {
'info': INFO,
},
methods: {
openView(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = false
this.$refs.infoRef.onOpen(row)
})
},
openAudit(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = true
this.formData = {
processTaskId: row.taskId,
taskName: row.curTaskName
}
this.$refs.infoRef.onOpen(row)
})
},
handleTaskAction(val) {
this.$refs.formRef.validate((valid) => {
if (valid) {
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: val
})
}).then((res) => {
if (res.code === 0) {
this.$refs.guava.index()
this.$message.success(res.msg)
this.doSearch()
}
})
})
}
})
},
onRevoke(row) {
this.$confirm("您确定要撤回吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "info"
}).then(() => {
this.$axios.post("/flow/common/revokeTask", {taskId: row.taskId}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.doSearch()
}
})
})
},
getActivityTypeName(type) {
const data = this.activityDeclareReimbursementList.find(item => item.name === type)
return data ? data.typeName : ""
},
},
async created() {
this.pageData()
this.unions = await this.$businessTool.listUnion()
this.clubs = await this.$businessTool.listCLubByRole()
this.activityDeclareReimbursementList = await this.$businessTool.getEnumOptions("ActivityDeclareReimbursement")
}
})
</script>
<!--#
}
#-->