浏览代码

费用管理,财务报销

ZaiZai 2 年之前
父节点
当前提交
bb089120c8

+ 16 - 0
src/api/modules/expense/finReimburse.js

@@ -57,4 +57,20 @@ export default {
             params: form
             params: form
         }, msg);
         }, msg);
     },
     },
+    //获取我的项目预算列表
+    async budget(form) {
+        return httpApi({
+            url: '/api/blade-control/ex/ma/project/budget/list',
+            method: 'get',
+            params: form
+        }, false);
+    },
+    //获取我的借款列表
+    async loanList(form = {}) {
+        return httpApi({
+            url: '/api/blade-control/ex/ma/loan/list',
+            method: 'get',
+            params: form
+        }, false);
+    },
 }
 }

+ 7 - 0
src/api/modules/other.js

@@ -13,3 +13,10 @@ export const userConfigInfo = (form, msg = true) => httpApi({
     method: 'get',
     method: 'get',
     params: form
     params: form
 }, msg);
 }, msg);
+
+//获取全部项目列表
+export const getProjectList = () => httpApi({
+    url: '/api/blade-control/ex/ma/project/list',
+    method: 'get',
+    params: {}
+}, false);

+ 7 - 20
src/views/expense/finReimburse/index.vue

@@ -9,7 +9,7 @@
             </div>
             </div>
             <div class="w-72 ml-2">
             <div class="w-72 ml-2">
                 <el-select v-model="searchForm.projectId" block clearable placeholder="项目名称" size="large">
                 <el-select v-model="searchForm.projectId" block clearable placeholder="项目名称" size="large">
-                    <el-option v-for="item in projectType" :label="item.name" :value="item.id"/>
+                    <el-option v-for="item in projectType" :label="item.projectName" :value="item.projectId"/>
                 </el-select>
                 </el-select>
             </div>
             </div>
             <div class="ml-4">
             <div class="ml-4">
@@ -77,9 +77,8 @@
 import {onActivated, ref} from "vue";
 import {onActivated, ref} from "vue";
 import {useRouter} from 'vue-router'
 import {useRouter} from 'vue-router'
 import mainApi from "~api/expense/finReimburse";
 import mainApi from "~api/expense/finReimburse";
-import projectApi from "~api/project/project-list";
+import {getProjectList} from "~api/other";
 import {getArrValue} from "js-fast-way";
 import {getArrValue} from "js-fast-way";
-import {getDictInfo} from "~api/system/parameter";
 import {delMessage} from "~uti/tools";
 import {delMessage} from "~uti/tools";
 
 
 const router = useRouter()
 const router = useRouter()
@@ -92,25 +91,16 @@ const getApi = () => {
     getProjectData()
     getProjectData()
     getTableData()
     getTableData()
     getDraftNum()
     getDraftNum()
-    //测试查字典
-    getChildListApi()
 }
 }
 
 
-//测试查字典
-const getChildListApi = () => {
-    getDictInfo('expense_fr_type')
-}
 
 
 //项目类型
 //项目类型
 const projectType = ref([])
 const projectType = ref([])
 const getProjectData = async () => {
 const getProjectData = async () => {
-    const {error, code, data} = await projectApi.getProjectList({
-        current: 1,
-        size: 1000,
-    })
+    const {error, code, data} = await getProjectList()
     //判断状态
     //判断状态
     if (!error && code === 200) {
     if (!error && code === 200) {
-        projectType.value = getArrValue(data?.records)
+        projectType.value = getArrValue(data)
     } else {
     } else {
         projectType.value = []
         projectType.value = []
     }
     }
@@ -169,16 +159,13 @@ const getTableData = async () => {
 
 
 //预览
 //预览
 const rowNameTap = ({id}) => {
 const rowNameTap = ({id}) => {
-    router.push({
-        name: 'expense-finReimburse-record',
-        query: {id: id, type: 'view'}
-    })
+    window.$message?.error('未知功能')
 }
 }
 
 
 //撤销
 //撤销
 const rowCancel = async (row) => {
 const rowCancel = async (row) => {
     row.isCancelLoading = true
     row.isCancelLoading = true
-    const {error, code, msg} = await mainApi.page({
+    const {error, code, msg} = await mainApi.cancel({
         id: row.id
         id: row.id
     })
     })
     //判断状态
     //判断状态
@@ -245,7 +232,7 @@ const editDraftClick = ({emdraftIds}) => {
 const delDraftClick = ({groupId}) => {
 const delDraftClick = ({groupId}) => {
     delMessage(async () => {
     delMessage(async () => {
         const {error, code, msg} = await mainApi.remove({
         const {error, code, msg} = await mainApi.remove({
-            id: groupId
+            groupId: groupId
         })
         })
         //判断状态
         //判断状态
         if (!error && code === 200) {
         if (!error && code === 200) {

+ 232 - 50
src/views/expense/finReimburse/record.vue

@@ -7,15 +7,21 @@
             <div class="record-form-box">
             <div class="record-form-box">
                 <el-scrollbar>
                 <el-scrollbar>
                     <HcCardItem :title="'报销明细' + (index + 1)" v-for="(item, index) in detailsData.details" ui="hac-bg-grey mb-5">
                     <HcCardItem :title="'报销明细' + (index + 1)" v-for="(item, index) in detailsData.details" ui="hac-bg-grey mb-5">
+                        <template #extra>
+                            <el-button type="danger" size="small" @click="delDetailsData(index)" v-if="index > 0">
+                                <HcIcon name="delete-bin"/>
+                                <span>删除明细</span>
+                            </el-button>
+                        </template>
                         <el-form ref="formRef" :model="item" :rules="formRules" label-position="left" label-width="auto" size="large">
                         <el-form ref="formRef" :model="item" :rules="formRules" label-position="left" label-width="auto" size="large">
                             <div class="hc-form-item">
                             <div class="hc-form-item">
                                 <el-form-item label="所属项目:" prop="projectId">
                                 <el-form-item label="所属项目:" prop="projectId">
                                     <el-select block v-model="item.projectId">
                                     <el-select block v-model="item.projectId">
-                                        <el-option v-for="item in projectData" :label="item.name" :value="item.id"/>
+                                        <el-option v-for="items in projectData" :label="items.projectName" :value="items.projectId"/>
                                     </el-select>
                                     </el-select>
                                 </el-form-item>
                                 </el-form-item>
                                 <div class="ml-2">
                                 <div class="ml-2">
-                                    <el-button type="default" @click="budgetModalShow">
+                                    <el-button type="default" @click="budgetModalShow(item,index)">
                                         <HcIcon name="add"/>
                                         <HcIcon name="add"/>
                                         <span>关联预算计划</span>
                                         <span>关联预算计划</span>
                                     </el-button>
                                     </el-button>
@@ -30,8 +36,7 @@
                                 </el-form-item>
                                 </el-form-item>
                                 <el-form-item label="费用类型:" prop="frType">
                                 <el-form-item label="费用类型:" prop="frType">
                                     <el-select block v-model="item.frType">
                                     <el-select block v-model="item.frType">
-                                        <el-option label="暂无接口1" value="1"/>
-                                        <el-option label="暂无接口2" value="2"/>
+                                        <el-option v-for="items in frTypeData" :label="items.dictName" :value="items.dictValue"/>
                                     </el-select>
                                     </el-select>
                                 </el-form-item>
                                 </el-form-item>
                             </div>
                             </div>
@@ -58,7 +63,7 @@
                     </HcCardItem>
                     </HcCardItem>
                     <div class="record-form-action-box mt-11">
                     <div class="record-form-action-box mt-11">
                         <el-divider content-position="right" border-style="dashed">
                         <el-divider content-position="right" border-style="dashed">
-                            <el-button type="primary" hc-btn>
+                            <el-button type="primary" hc-btn @click="addDetailsData">
                                 <HcIcon name="add"/>
                                 <HcIcon name="add"/>
                                 <span>添加明细</span>
                                 <span>添加明细</span>
                             </el-button>
                             </el-button>
@@ -78,8 +83,7 @@
                             </el-form-item>
                             </el-form-item>
                             <el-form-item label="选择借款项">
                             <el-form-item label="选择借款项">
                                 <el-select v-model="detailsData.deductLoanId">
                                 <el-select v-model="detailsData.deductLoanId">
-                                    <el-option label="暂无接口1" value="1" />
-                                    <el-option label="暂无接口2" value="2" />
+                                    <el-option v-for="item in loanListData" :label="item.loanName" :value="item.id" />
                                 </el-select>
                                 </el-select>
                             </el-form-item>
                             </el-form-item>
                             <el-form-item label="冲抵后的实际报销金额">
                             <el-form-item label="冲抵后的实际报销金额">
@@ -104,11 +108,11 @@
                     </el-scrollbar>
                     </el-scrollbar>
                 </div>
                 </div>
                 <div class="action">
                 <div class="action">
-                    <el-button type="warning" hc-btn>
+                    <el-button type="warning" :loading="tempLoading" hc-btn @click="tempDraftData">
                         <HcIcon name="draft"/>
                         <HcIcon name="draft"/>
                         <span>暂存草稿</span>
                         <span>暂存草稿</span>
                     </el-button>
                     </el-button>
-                    <el-button type="primary" hc-btn>
+                    <el-button type="primary" :loading="submitLoading" hc-btn @click="submitFormData">
                         <HcIcon name="check-double"/>
                         <HcIcon name="check-double"/>
                         <span>提交报销申请</span>
                         <span>提交报销申请</span>
                     </el-button>
                     </el-button>
@@ -131,13 +135,14 @@
                   @close="budgetCloseClick"
                   @close="budgetCloseClick"
                   @save="budgetSaveClick"
                   @save="budgetSaveClick"
         >
         >
-            <HcTable :column="tableBudgetColumn" :datas="tableBudgetData">
+            <HcTable :column="tableBudgetColumn" :datas="tableBudgetData" :loading="tableBudgetLoading">
                 <template #action="{row,index}">
                 <template #action="{row,index}">
-                    <el-button size="small" type="danger" @click="row.relevance = false" v-if="row.relevance">取消关联</el-button>
-                    <el-button size="small" type="primary" @click="row.relevance = true" v-else>关联</el-button>
+                    <el-button size="small" type="danger" @click="rowDisassociate(row)" v-if="row.isRelevance">取消关联</el-button>
+                    <el-button size="small" type="primary" @click="rowRelevance(row)" v-else>关联</el-button>
                 </template>
                 </template>
             </HcTable>
             </HcTable>
         </HcDialog>
         </HcDialog>
+
     </HcCard>
     </HcCard>
 </template>
 </template>
 
 
@@ -145,24 +150,34 @@
 import {onActivated, ref} from "vue";
 import {onActivated, ref} from "vue";
 import {useRoute, useRouter} from 'vue-router'
 import {useRoute, useRouter} from 'vue-router'
 import mainApi from "~api/expense/finReimburse";
 import mainApi from "~api/expense/finReimburse";
-import projectApi from "~api/project/project-list";
+import {getDictInfo} from "~api/system/parameter";
 import {getTokenHeader} from "~src/api/request/header";
 import {getTokenHeader} from "~src/api/request/header";
-import {getArrValue, getObjValue} from "js-fast-way";
+import {deepClone, getArrValue, getObjValue} from "js-fast-way";
+import {getProjectList} from "~api/other";
+import {delMessage} from "~uti/tools";
 
 
+//初始变量
 const router = useRouter()
 const router = useRouter()
 const useRoutes = useRoute()
 const useRoutes = useRoute()
 
 
+//路由参数
 const dataId = ref(useRoutes?.query?.id ?? '')
 const dataId = ref(useRoutes?.query?.id ?? '')
 const dataType = ref(useRoutes?.query?.type ?? 'add') // add:新增 view:预览 draft:草稿
 const dataType = ref(useRoutes?.query?.type ?? 'add') // add:新增 view:预览 draft:草稿
 
 
+//页面被激活
 onActivated(() => {
 onActivated(() => {
     dataId.value = useRoutes?.query?.id ?? ''
     dataId.value = useRoutes?.query?.id ?? ''
     dataType.value = useRoutes?.query?.type ?? 'add'
     dataType.value = useRoutes?.query?.type ?? 'add'
     getApi()
     getApi()
 })
 })
 
 
+//请求接口
 const getApi = () => {
 const getApi = () => {
+    //下拉框相关数据
     getProjectData()
     getProjectData()
+    expenseFrType()
+    getLoanListData()
+    //获取数据详情
     if (dataId.value > 0 && dataType.value !== 'add') {
     if (dataId.value > 0 && dataType.value !== 'add') {
         getDetailsData()
         getDetailsData()
     } else {
     } else {
@@ -174,21 +189,43 @@ const getApi = () => {
     }
     }
 }
 }
 
 
-//项目类型
+//获取项目数据
 const projectData = ref([])
 const projectData = ref([])
 const getProjectData = async () => {
 const getProjectData = async () => {
-    const {error, code, data} = await projectApi.getProjectList({
-        current: 1,
-        size: 1000,
-    })
+    const {error, code, data} = await getProjectList()
     //判断状态
     //判断状态
     if (!error && code === 200) {
     if (!error && code === 200) {
-        projectData.value = getArrValue(data?.records)
+        projectData.value = getArrValue(data)
     } else {
     } else {
         projectData.value = []
         projectData.value = []
     }
     }
 }
 }
 
 
+//费用类型字典
+const frTypeData = ref([])
+const expenseFrType = async () => {
+    const {error, code, data} = await getDictInfo('expense_fr_type')
+    //判断状态
+    if (!error && code === 200) {
+        frTypeData.value = getArrValue(data)
+    } else {
+        frTypeData.value = []
+    }
+}
+
+//获取我的借款列表
+const loanListData = ref([])
+const getLoanListData = async () => {
+    const {error, code, data} = await mainApi.loanList()
+    //判断状态
+    if (!error && code === 200) {
+        loanListData.value = getArrValue(data)
+    } else {
+        loanListData.value = []
+    }
+}
+
+
 //基础详情
 //基础详情
 const detailsObj = {
 const detailsObj = {
     frMoney: 0,             // 报销金额
     frMoney: 0,             // 报销金额
@@ -196,8 +233,6 @@ const detailsObj = {
     userIdVesting: null,    // 归属人id
     userIdVesting: null,    // 归属人id
     isDeductLoan: 0,        // 是否抵扣借款 0=否 1=是
     isDeductLoan: 0,        // 是否抵扣借款 0=否 1=是
     deductLoanId: null,     // 借款信息id
     deductLoanId: null,     // 借款信息id
-    approvalResultName: '', // 审批结果
-    approvalStatusName: '', // 审批状态
     status: null,           // 审批状态 0=未上报 1=待审批 2=已审批 3=已驳回
     status: null,           // 审批状态 0=未上报 1=待审批 2=已审批 3=已驳回
     //details: [detailsObj1],
     //details: [detailsObj1],
 }
 }
@@ -213,10 +248,21 @@ const detailsObj1 = {
     frAttachmentUrl: '',    // 附件url地址
     frAttachmentUrl: '',    // 附件url地址
 }
 }
 
 
+//添加明细
+const addDetailsData = () => {
+    detailsData.value?.details.push(detailsObj1)
+}
+
+//删除明细
+const delDetailsData = (index) => {
+    delMessage(() => {
+        detailsData.value?.details.splice(index, 1)
+    })
+}
+
 //获取详情数据
 //获取详情数据
 const totalFrMoney = ref('')
 const totalFrMoney = ref('')
 const detailsData = ref({})
 const detailsData = ref({})
-
 const getDetailsData = async () => {
 const getDetailsData = async () => {
     if (dataType.value === 'view') {
     if (dataType.value === 'view') {
         //预览详情
         //预览详情
@@ -235,6 +281,7 @@ const getDetailsData = async () => {
             Object.keys(detailsObj1).forEach(key => {
             Object.keys(detailsObj1).forEach(key => {
                 newDetails1[key] = res[key]
                 newDetails1[key] = res[key]
             })
             })
+            newDetails1.id = res?.id
             totalFrMoney.value = res?.frMoney
             totalFrMoney.value = res?.frMoney
             newDetails.details = [newDetails1]
             newDetails.details = [newDetails1]
         } else {
         } else {
@@ -247,14 +294,37 @@ const getDetailsData = async () => {
     } else if (dataType.value === 'draft') {
     } else if (dataType.value === 'draft') {
         //草稿详情
         //草稿详情
         const {error, code, data} = await mainApi.draftDetail({
         const {error, code, data} = await mainApi.draftDetail({
-            id: dataId.value
+            eMDraftIds: dataId.value
         })
         })
         //判断状态
         //判断状态
-        console.log(data)
-        if (!error && code === 200) {
-
+        const res = getArrValue(data)
+        if (!error && code === 200 && res.length > 0) {
+            let newDetails = {}, newDetailsArr = [], frMoney = 0
+            res.forEach((item, index) => {
+                //基础数据
+                if (index === 0) {
+                    Object.keys(detailsObj).forEach(key => {
+                        newDetails[key] = item[key]
+                    })
+                }
+                //明细数据
+                let newDetails1 = {}
+                Object.keys(detailsObj1).forEach(key => {
+                    newDetails1[key] = item[key]
+                })
+                newDetails1.id = item?.id
+                newDetailsArr.push(newDetails1)
+                frMoney = Number(frMoney) + Number(item?.frMoney)
+            })
+            newDetails.details = newDetailsArr
+            totalFrMoney.value = frMoney
+            detailsData.value = newDetails
         } else {
         } else {
-
+            totalFrMoney.value = '0'
+            detailsData.value = {
+                ...detailsObj,
+                details: [detailsObj1]
+            }
         }
         }
     } else {
     } else {
         totalFrMoney.value = '0'
         totalFrMoney.value = '0'
@@ -267,37 +337,88 @@ const getDetailsData = async () => {
 
 
 //明细表单
 //明细表单
 const formRef = ref(null)
 const formRef = ref(null)
-const formModel = ref({
-    key1: '', key2: '', key3: '', key4: '', key5: '',
-})
-const formRules = {}
+const formRules = {
+    projectId: [{required: true, message: '请选择所属项目', trigger: 'change'}],
+    frMoney: [{required: true, message: '请输入报销金额', trigger: 'blur'}],
+    frDate: [{required: true, message: '请选择费用发生日期', trigger: 'change'}],
+    frType: [{required: true, message: '请选择费用类型', trigger: 'change'}],
+}
 
 
 //关联预算计划
 //关联预算计划
 const budgetModal = ref(false)
 const budgetModal = ref(false)
-const budgetModalShow = () => {
+const budgetIds = ref([])
+const budgetIndex = ref([])
+const budgetModalShow = (item, index) => {
     budgetModal.value = true
     budgetModal.value = true
-}
-const budgetCloseClick = () => {
-    budgetModal.value = false
-}
-const budgetSaveClick = () => {
-
+    budgetIndex.value = index
+    budgetIds.value = item.budgetPlanIds?.split(',') || []
+    getBudgetTableData(item.projectId)
 }
 }
 
 
 //关联预算计划表格
 //关联预算计划表格
+const tableBudgetLoading = ref(false)
 const tableBudgetColumn = [
 const tableBudgetColumn = [
-    {key: 'key1', name: '任务所属项目'},
-    {key: 'key2', name: '状态',  width: '120', align: 'center'},
-    {key: 'key3', name: '任务描述'},
-    {key: 'key4', name: '完成指标', width: '120', align: 'center'},
+    {key: 'projectName', name: '任务所属项目'},
+    {key: 'status', name: '状态',  width: '120', align: 'center'},
+    {key: 'planTaskDesc', name: '任务描述'},
+    {key: 'planTarget', name: '完成指标', width: '120', align: 'center'},
     {key: 'action', name: '操作', width: '100', align: 'center'},
     {key: 'action', name: '操作', width: '100', align: 'center'},
 ]
 ]
-const tableBudgetData = ref([
-    {id: 1, key1: 'xxxx'},
-    {id: 2, key1: 'xxxx'},
-    {id: 3, key1: 'xxxx'},
-    {id: 4, key1: 'xxxx'},
-])
+const tableBudgetData = ref([])
+const getBudgetTableData = async (projectId) => {
+    tableBudgetLoading.value = true
+    const {error, code, data} = await mainApi.budget({projectId})
+    //判断状态
+    tableBudgetLoading.value = false
+    if (!error && code === 200) {
+        tableBudgetData.value = await isRowRelevance(data)
+    } else {
+        tableBudgetData.value = []
+    }
+}
+
+//判断是否关联
+const isRowRelevance = async (data) => {
+    const res = getArrValue(data)
+    const ids = budgetIds.value
+    res.forEach(item => {
+        item.isRelevance = ids.includes(item.id)
+    })
+    return res
+}
+
+//取消关联
+const rowDisassociate = (row) => {
+    const ids = budgetIds.value
+    const index = ids.indexOf(row.id)
+    if (index > -1) {
+        ids.splice(index, 1)
+    }
+    budgetIds.value = ids
+    row.isRelevance = false
+}
+
+//关联
+const rowRelevance = (row) => {
+    const ids = budgetIds.value
+    ids.push(row.id)
+    budgetIds.value = ids
+    row.isRelevance = true
+}
+
+
+//确认关联预算计划
+const budgetSaveClick = () => {
+    const index = budgetIndex.value
+    const ids = budgetIds.value?.join(',')
+    console.log('ids', ids)
+    console.log('index', index)
+}
+
+//关闭预算计划
+const budgetCloseClick = () => {
+    budgetModal.value = false
+}
 
 
 
 
 //上传配置
 //上传配置
@@ -363,7 +484,7 @@ const setUploadFileData = (echoParams, resData) => {
     detailsData.value.details[index][name] = url
     detailsData.value.details[index][name] = url
 }
 }
 
 
-
+//流程数据
 const timeLineData = ref([
 const timeLineData = ref([
     {title: '审批人', section: '部门负责人'},
     {title: '审批人', section: '部门负责人'},
     {title: '财务审核', section: '财务'},
     {title: '财务审核', section: '财务'},
@@ -371,6 +492,67 @@ const timeLineData = ref([
     {title: '出纳付款', section: '出纳'},
     {title: '出纳付款', section: '出纳'},
     {title: '抄送人', section: '总经理、财务、申请人'},
     {title: '抄送人', section: '总经理、财务、申请人'},
 ])
 ])
+
+//处理表单数据
+const getFormData = (submitStatus = 1) => {
+    const res = deepClone(detailsData.value)
+    const cashierUser = {userId: ''}            //出纳人
+    const ccUserList = [{userId: ''}]           //抄送人列表
+    const finalConfirmationUser = {userId: ''}  //最终确认付款人
+    const financeUser = {userId: ''}            //财务人员
+    const responsibleUser = {userId: ''}        //部门负责人
+    //----处理数据----
+    let newFormData = [], newDetails = {}
+    //基础数据
+    Object.keys(detailsObj).forEach(key => {
+        newDetails[key] = res[key]
+    })
+    //组装为数组集合的表单数据
+    const list = res?.details
+    list.forEach(item => {
+        newFormData.push({
+            ...newDetails,
+            ...item,
+            cashierUser,
+            ccUserList,
+            finalConfirmationUser,
+            financeUser,
+            responsibleUser,
+            submitStatus //提交状态 1=暂存 2=提交审批
+        })
+    })
+    return newFormData
+}
+
+//暂存数据
+const tempLoading = ref(false)
+const tempDraftData = async () => {
+    tempLoading.value = true
+    const form = getFormData(1)
+    const {error, code, msg} = await mainApi.submit(form)
+    //判断状态
+    tempLoading.value = false
+    if (!error && code === 200) {
+        window.$message?.success('暂存成功')
+    } else {
+        window.$message?.error(msg)
+    }
+}
+
+//提交报销申请
+const submitLoading = ref(false)
+const submitFormData = async () => {
+    submitLoading.value = true
+    const form = getFormData(2)
+    const {error, code, msg} = await mainApi.submit(form)
+    //判断状态
+    submitLoading.value = false
+    if (!error && code === 200) {
+        window.$message?.success('提交成功')
+    } else {
+        window.$message?.error(msg)
+    }
+}
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">