| <template> | 
|   <LoadingLayout :loading="isLoading"> | 
|     <AppScrollContainer> | 
|       <ChunkCell title="结算单详情"> | 
|         <ProForm :model="form" ref="formRef" label-width="120px" :is-read="true"> | 
|           <ProFormCol> | 
|             <ProFormColItem :span="8"> | 
|               <ProFormItemV2 label="所属任务:" prop="name"> | 
|                 <ProFormText v-model="form.name"> </ProFormText> | 
|               </ProFormItemV2> | 
|             </ProFormColItem> | 
|             <ProFormColItem :span="8"> | 
|               <ProFormItemV2 label="任务编号:" prop="code"> | 
|                 <ProFormText v-model="form.code"> </ProFormText> | 
|               </ProFormItemV2> | 
|             </ProFormColItem> | 
|           </ProFormCol> | 
|           <!-- <ProFormCol> | 
|             <ProFormColItem :span="8"> | 
|               <ProFormItemV2 label="结算单名称:" prop="settlementOrderName"> | 
|                 <ProFormText v-model="form.settlementOrderName"> </ProFormText> | 
|               </ProFormItemV2> | 
|             </ProFormColItem> | 
|             <ProFormColItem :span="8"> | 
|               <ProFormItemV2 label="上传时间:" prop="settlementOrderTime"> | 
|                 <ProFormDatePicker | 
|                   v-model="form.settlementOrderTime" | 
|                   type="date" | 
|                   format="YYYY-MM-DD HH:mm" | 
|                 ></ProFormDatePicker> | 
|               </ProFormItemV2> | 
|             </ProFormColItem> | 
|           </ProFormCol> --> | 
|           <ProFormCol> | 
|             <ProFormColItem :span="8"> | 
|               <ProFormItemV2 label="结算金额:" prop="settlementAmount"> | 
|                 <ProFormInputNumber v-model="form.settlementAmount" format-value="money"> | 
|                 </ProFormInputNumber> | 
|               </ProFormItemV2> | 
|             </ProFormColItem> | 
|             <ProFormColItem :span="8"> | 
|               <ProFormItemV2 label="实发金额:" prop="actualSettlementAmount"> | 
|                 <ProFormInputNumber v-model="form.actualSettlementAmount" format-value="money"> | 
|                 </ProFormInputNumber> | 
|               </ProFormItemV2> | 
|             </ProFormColItem> | 
|           </ProFormCol> | 
|         </ProForm> | 
|       </ChunkCell> | 
|       <ChunkCell title="结算名单" style="flex: 1" class="settlement-user-list-chunk"> | 
|         <ProTableQueryFilterBar @on-reset="reset"> | 
|           <template #query> | 
|             <QueryFilterItem> | 
|               <SearchInput | 
|                 v-model="extraParamState.keywords" | 
|                 style="width: 300px" | 
|                 placeholder="姓名/手机/身份证号/客户" | 
|                 @on-click-search="getList" | 
|               > | 
|               </SearchInput> | 
|             </QueryFilterItem> | 
|           </template> | 
|           <template #btn v-if="isSettlement"> | 
|             <BlFileUpload | 
|               v-model:file-url="form.settlementUrl" | 
|               ref="uploadRef" | 
|               :showTip="false" | 
|               :show-file-list="false" | 
|               class="pro-table-operation-btn upload-style-btn" | 
|               :on-success="(event) => handleUploadSuccess(event)" | 
|               :limitFileSize="null" | 
|               :limit="1" | 
|               accept="xlsx,xls" | 
|             > | 
|               <el-button text type="primary" class="pro-table-operation-btn">导入</el-button> | 
|             </BlFileUpload> | 
|             <el-button type="primary" link @click="handleExport()">导出</el-button> | 
|           </template> | 
|         </ProTableQueryFilterBar> | 
|         <ProTableV2 | 
|           v-bind="proTableProps" | 
|           :columns="SettlementListColumns" | 
|           :operationBtns="operationBtns" | 
|           :auto-height="false" | 
|           ref="proTable" | 
|           :tableProps="{ | 
|             maxHeight: '400px', | 
|           }" | 
|         > | 
|         </ProTableV2> | 
|         <div class="chuck-add-or-edit-actions"> | 
|           <el-button class="chuck-add-or-edit-actions" @click="handleBack">取消</el-button> | 
|           <el-button | 
|             v-if=" | 
|               isSettlement && | 
|               (!form.auditStatus || form.auditStatus !== EnumTaskSettlementAuditStatus.Pass) | 
|             " | 
|             class="chuck-add-or-edit-actions" | 
|             type="primary" | 
|             @click="handleSubmit()" | 
|             >结算申请</el-button | 
|           > | 
|           <el-button | 
|             v-if="isSettlement && form.auditStatus === EnumTaskSettlementAuditStatus.Pass" | 
|             class="chuck-add-or-edit-actions" | 
|             type="primary" | 
|             @click="handleSubmit()" | 
|             >提交结算</el-button | 
|           > | 
|         </div> | 
|       </ChunkCell> | 
|     </AppScrollContainer> | 
|     <EditAccountInfoDialog v-bind="dialogProps"></EditAccountInfoDialog> | 
|     <SettleDetailDialog v-bind="dialogSettleProps" /> | 
|   </LoadingLayout> | 
| </template> | 
| <script setup lang="ts"> | 
| import { | 
|   LoadingLayout, | 
|   AppContainer, | 
|   AppScrollContainer, | 
|   ChunkCell, | 
|   ProForm, | 
|   ProFormItemV2, | 
|   ProFormText, | 
|   ProFormCol, | 
|   ProFormColItem, | 
|   ProFormDatePicker, | 
|   ProFormInputNumber, | 
|   useTable, | 
|   ProTableV2, | 
|   defineOperationBtns, | 
|   SearchInput, | 
|   QueryFilterItem, | 
|   ProTableQueryFilterBar, | 
|   useFormDialog, | 
|   XLSXUtils, | 
|   BlFileUpload, | 
|   UploadUserFile, | 
| } from '@bole-core/components'; | 
| import { SettlementListColumns } from './constants'; | 
| import { useQuery } from '@tanstack/vue-query'; | 
| import * as taskServices from '@/services/api/task'; | 
| import * as taskUserServices from '@/services/api/taskUser'; | 
| import EditAccountInfoDialog from './components/EditAccountInfoDialog.vue'; | 
| import SettleDetailDialog from './components/SettleDetailDialog.vue'; | 
| import { Message } from '@bole-core/core'; | 
| import { SettlementReceiveStatusText, EnumTaskSettlementAuditStatus } from '@/constants'; | 
| import { | 
|   downloadFile, | 
|   downloadFileByUrl, | 
|   paginateList, | 
|   setOssFileName, | 
|   setOSSLink, | 
|   toThousand, | 
| } from '@/utils'; | 
| import _ from 'lodash'; | 
|   | 
| defineOptions({ | 
|   name: 'ServiceChargeDetail', | 
| }); | 
|   | 
| const { closeViewPush } = useRouteView(); | 
| const eventContext = useGlobalEventContext(); | 
| const operationBtns = defineOperationBtns([ | 
|   { | 
|     data: { | 
|       enCode: 'editBtn', | 
|       name: '编辑', | 
|     }, | 
|     emits: { | 
|       onClick: (role) => openDialog(role), | 
|     }, | 
|     extraProps: { | 
|       hide: () => !isSettlement.value, | 
|     }, | 
|   }, | 
| ]); | 
| const route = useRoute(); | 
| const id = (route.params.id as string) ?? ''; | 
| const settlement = (route.query.settlement as string) ?? ''; | 
| // const settlementAccess = route.query.settlementAccess ?? ''; | 
| const isSettlement = computed(() => !!settlement); | 
|   | 
| const form = reactive({ | 
|   name: '', | 
|   settlementAmount: 0, | 
|   actualSettlementAmount: 0, | 
|   code: '', | 
|   settlementOrderName: '', | 
|   settlementOrderTime: '', | 
|   settlementTaskUsers: [] as API.GetSettlementTaskUsersQueryResultItem[], | 
|   | 
|   settlementUrl: [] as UploadUserFile[], | 
|   | 
|   settlementAccess: '' as any as EnumEnterpriseWalletAccess, | 
|   isSettlement: isSettlement.value, | 
|   | 
|   auditStatus: '' as any as EnumTaskSettlementAuditStatus, | 
|   | 
|   timeoutServiceFee: 0, | 
| }); | 
|   | 
| const BaseState = { | 
|   loading: true, | 
| }; | 
|   | 
| const state = reactive({ ...BaseState }); | 
|   | 
| const { isLoading, refetch } = useQuery({ | 
|   queryKey: ['taskUserServices/getSettlementTaskUsers', id, toRef(form, 'isSettlement')], | 
|   queryFn: async () => { | 
|     let params: API.APIgetSettlementTaskUsersParams = { | 
|       id: id, | 
|     }; | 
|     // if (form.settlementAccess) { | 
|     //   params.settlementAccess = Number(form.settlementAccess); | 
|     // } | 
|     if (form.isSettlement) { | 
|       params.isImport = true; | 
|     } | 
|     return await taskUserServices.getSettlementTaskUsers(params, { | 
|       showLoading: false, | 
|     }); | 
|   }, | 
|   placeholderData: () => ({} as API.GetSettlementTaskUsersQueryResult), | 
|   onSuccess(res) { | 
|     form.settlementTaskUsers = res?.data ?? []; | 
|     if (res?.detail) { | 
|       form.name = res?.detail?.name; | 
|       form.settlementAmount = res?.detail?.settlementAmount ?? 0; | 
|       form.actualSettlementAmount = res?.detail?.actualSettlementAmount ?? 0; | 
|       form.code = res?.detail?.code; | 
|       form.settlementAccess = res?.detail?.settlementAccess; | 
|       form.auditStatus = res?.detail?.auditStatus; | 
|       form.timeoutServiceFee = res?.detail?.timeoutServiceFee ?? 0; | 
|       // form.settlementOrderName = setOssFileName(res?.detail?.settlementOrderName); | 
|       // form.settlementOrderTime = res?.detail?.settlementOrderTime ?? ''; | 
|     } | 
|     // if (res?.errors?.length > 0) { | 
|     //   Message.tipMessage('存在导入错误的数据,是否导出?').then(() => { | 
|     //     XLSXUtils.exportToXLSX({ | 
|     //       workbookDataList: res.errors, | 
|     //       fileName: '结算导入-错误数据', | 
|     //       workbookHeaderMap: { | 
|     //         name: '姓名', | 
|     //         identity: '身份证号', | 
|     //         taskName: '任务名称', | 
|     //         settlementAmount: '结算金额', | 
|     //         actualSettlementAmount: '实发金额', | 
|     //         receiveAccount: '收款账户', | 
|     //         bank: '所属银行', | 
|     //         bankBranch: '开户支行名称', | 
|     //         errorMessage: '错误信息', | 
|     //       }, | 
|     //     }); | 
|     //   }); | 
|     // } | 
|     getList(); | 
|   }, | 
|   enabled: !!id, | 
| }); | 
|   | 
| const { | 
|   getDataSource: getList, | 
|   proTableProps, | 
|   paginationState, | 
|   extraParamState, | 
|   reset, | 
| } = useTable( | 
|   async ({ pageIndex, pageSize }, extraParamState) => { | 
|     try { | 
|       let list = [...form.settlementTaskUsers]; | 
|       if (extraParamState.keywords) { | 
|         list = list?.filter((item) => { | 
|           return ( | 
|             item.name.includes(extraParamState.keywords) || | 
|             item.contactPhoneNumber.includes(extraParamState.keywords) || | 
|             item.identity.includes(extraParamState.keywords) | 
|           ); | 
|         }); | 
|       } | 
|   | 
|       return Promise.resolve({ | 
|         pageModel: { | 
|           rows: pageSize, | 
|           page: pageIndex, | 
|           totalCount: list.length, | 
|         }, | 
|         data: paginateList(list, pageIndex, pageSize), | 
|       }); | 
|     } catch (error) { | 
|       console.log('error: ', error); | 
|     } | 
|   }, | 
|   { | 
|     defaultExtraParams: { | 
|       keywords: '', | 
|       orderInput: [{ property: 'id', order: EnumPagedListOrder.Desc }], | 
|     }, | 
|     queryKey: ['taskUserServices/getSettlementTaskUsers'], | 
|     columnsRenderProps: { | 
|       settlementTime: { type: 'date' }, | 
|       settlementAmount: { type: 'money' }, | 
|       actualSettlementAmount: { type: 'money' }, | 
|       settlementReceiveStatus: { type: 'enum', valueEnum: SettlementReceiveStatusText }, | 
|       serviceFee: { type: 'money' }, | 
|       timeoutHours: { | 
|         formatter: (row: API.GetSettlementTaskUsersQueryResultItem) => | 
|           row.timeoutHours ? `${row.timeoutHours}小时` : '', | 
|       }, | 
|       timeoutFee: { type: 'money' }, | 
|       otherFee: { type: 'money' }, | 
|     }, | 
|   } | 
| ); | 
|   | 
| const { dialogProps, handleAdd, handleEdit, editForm } = useFormDialog({ | 
|   onConfirm: handleAddOrEdit, | 
|   defaultFormParams: { | 
|     id: '', | 
|     settlementAmount: null as number, | 
|     actualSettlementAmount: null as number, | 
|     receiveAccount: '', | 
|     bank: '', | 
|     // totalWorkHours: null as number, | 
|     settlementAccess: form.settlementAccess, | 
|     // bankBranch: '', | 
|     timeoutServiceFee: 0, | 
|     serviceFee: null as number, | 
|     timeoutHours: null as number, | 
|     timeoutFee: null as number, | 
|     otherFee: null as number, | 
|     remark: '', | 
|   }, | 
| }); | 
|   | 
| function openDialog(row?: API.GetSettlementTaskUsersQueryResultItem) { | 
|   if (row) { | 
|     handleEdit({ | 
|       id: row?.id, | 
|       settlementAmount: row?.settlementAmount ?? null, | 
|       actualSettlementAmount: row?.actualSettlementAmount ?? null, | 
|       receiveAccount: row?.receiveAccount ?? '', | 
|       bank: row?.bank ?? '', | 
|       // totalWorkHours: row?.totalWorkHours ?? null, | 
|       settlementAccess: form.settlementAccess, | 
|       // bankBranch: row?.bankBranch ?? '', | 
|       timeoutServiceFee: form.timeoutServiceFee, | 
|       serviceFee: row?.serviceFee ?? 0, | 
|       timeoutHours: row?.timeoutHours ?? null, | 
|       timeoutFee: row?.timeoutFee ?? null, | 
|       otherFee: row?.otherFee ?? null, | 
|       remark: row?.remark ?? '', | 
|     }); | 
|   } | 
| } | 
|   | 
| async function handleAddOrEdit() { | 
|   try { | 
|     const settlementAmount = | 
|       (editForm.timeoutFee ?? 0) + (editForm.serviceFee ?? 0) + (editForm.otherFee ?? 0); | 
|     let params: API.EditTaskSettlementOrderRosterCommand = { | 
|       id: editForm.id, | 
|       settlementAmount: settlementAmount, | 
|       actualSettlementAmount: editForm.actualSettlementAmount, | 
|       receiveAccount: editForm.receiveAccount, | 
|       bank: editForm.bank, | 
|       // totalWorkHours: editForm.totalWorkHours, | 
|       // bankBranch: editForm.bankBranch, | 
|       serviceFee: editForm.serviceFee, | 
|       timeoutHours: editForm.timeoutHours, | 
|       timeoutFee: editForm.timeoutFee, | 
|       otherFee: editForm.otherFee, | 
|       remark: editForm.remark, | 
|     }; | 
|     let res = await taskServices.editTaskSettlementOrderRoster(params); | 
|     if (res) { | 
|       Message.successMessage('操作成功'); | 
|       form.isSettlement = false; | 
|       refetch(); | 
|       getList(paginationState.pageIndex); | 
|     } | 
|   } catch (error) {} | 
| } | 
|   | 
| function handleUploadSuccess(response: UploadUserFile & { file: File & { uid: number } }) { | 
|   if (response.path) { | 
|     importTaskSettlementOrderRosters(response.path); | 
|   } | 
| } | 
|   | 
| async function importTaskSettlementOrderRosters(url: string) { | 
|   try { | 
|     let params: API.ImportTaskSettlementOrderRostersCommand = { | 
|       id: id, | 
|       url: url, | 
|     }; | 
|     let res = await taskServices.importTaskSettlementOrderRosters(params); | 
|     if (res) { | 
|       Message.successMessage('操作成功'); | 
|       form.isSettlement = false; | 
|       refetch(); | 
|       getList(paginationState.pageIndex); | 
|       if (res?.errors?.length > 0) { | 
|         Message.tipMessage('存在导入错误的数据,是否导出?').then(() => { | 
|           XLSXUtils.exportToXLSX({ | 
|             workbookDataList: res.errors, | 
|             fileName: '结算导入-错误数据', | 
|             workbookHeaderMap: { | 
|               name: '姓名', | 
|               identity: '身份证号', | 
|               contactPhoneNumber: '手机号', | 
|               bank: '所属银行', | 
|               bankBranch: '所属支行', | 
|               receiveAccount: '收款账户', | 
|               totalWorkHours: '累计工时(小时)', | 
|               settlementAmount: '结算金额', | 
|               actualSettlementAmount: '实发金额', | 
|               errorMessage: '错误信息', | 
|             }, | 
|           }); | 
|         }); | 
|       } | 
|     } | 
|   } catch (error) { | 
|   } finally { | 
|     form.settlementUrl = [] as UploadUserFile[]; | 
|   } | 
| } | 
|   | 
| function handleSubmit() { | 
|   handleSettleEdit({ | 
|     id: id, | 
|     name: form.name, | 
|     code: form.code, | 
|     settlementUserCount: form.settlementTaskUsers.length, | 
|     actualSettlementAmount: form.actualSettlementAmount, | 
|     settlementAmount: form.settlementAmount, | 
|     settlementAccess: form.settlementAccess, | 
|   }); | 
| } | 
|   | 
| const { | 
|   dialogProps: dialogSettleProps, | 
|   handleEdit: handleSettleEdit, | 
|   editForm: settleEditForm, | 
| } = useFormDialog({ | 
|   onConfirm: handleSettle, | 
|   defaultFormParams: { | 
|     id: '', | 
|     name: '', | 
|     code: '', | 
|     settlementUserCount: 0, | 
|     settlementAmount: 0, | 
|     actualSettlementAmount: 0, | 
|     settlementAccess: '' as any as EnumEnterpriseWalletAccess, | 
|   }, | 
| }); | 
|   | 
| async function handleSettle() { | 
|   sureTaskSettlementOrder(); | 
| } | 
|   | 
| async function sureTaskSettlementOrder() { | 
|   try { | 
|     let params: API.SureTaskSettlementCommand = { | 
|       taskInfoId: id, | 
|     }; | 
|     let res = await taskServices.sureTaskSettlement(params); | 
|     if (res) { | 
|       Message.successMessage('操作成功'); | 
|       eventContext.emit('sureTaskSettlementOrder'); | 
|       handleBack(); | 
|     } | 
|   } catch (error) {} | 
| } | 
|   | 
| const handleExport = _.debounce( | 
|   async () => { | 
|     if (paginationState.total === 0) { | 
|       Message.warnMessage('没有数据可以导出哦~'); | 
|       return; | 
|     } | 
|     try { | 
|       let params: API.ExportTaskSettlementOrderRostersCommand = { | 
|         id: id, | 
|       }; | 
|       let res = await taskServices.exportTaskSettlementOrderRosters(params); | 
|       if (res) { | 
|         downloadFileByUrl(setOSSLink(res), `${form.name}-${form.code}`); | 
|       } | 
|     } catch (error) {} | 
|   }, | 
|   1000, | 
|   { leading: true, trailing: false } | 
| ); | 
|   | 
| function handleBack() { | 
|   closeViewPush(route, { | 
|     name: 'ServiceChargeManageList', | 
|   }); | 
| } | 
| </script> | 
|   | 
| <style lang="scss" scoped> | 
| @use '@/style/common.scss' as *; | 
|   | 
| .step-wrapper { | 
|   margin: 0 auto; | 
|   padding: 24px 0; | 
| } | 
|   | 
| .settlement-user-list-chunk { | 
|   :deep() { | 
|     .no-data img { | 
|       width: 280px; | 
|     } | 
|   } | 
| } | 
|   | 
| .chuck-add-or-edit-actions { | 
|   margin-bottom: 20px; | 
| } | 
| </style> | 
| <style lang="scss"> | 
| .text-over-tooltip-content { | 
|   max-width: 600px; | 
|   word-break: break-all; | 
| } | 
| </style> |