Merge remote-tracking branch 'origin/devlopment' into devlopment

This commit is contained in:
Maple 2025-08-14 17:16:01 +08:00
commit 8193abfd2b
4 changed files with 494 additions and 581 deletions

View File

@ -906,12 +906,61 @@ export const systemRoutes: RouteRecordRaw[] = [
}, },
], ],
}, },
// start
{
path: '/construction-operation-platform',
name: 'ConstructionOperationPlatform',
component: Layout,
redirect: '/construction-operation-platform/implementation-workflow/field-construction',
meta: { title: '我的工作台', icon: 'tool', hidden: false, sort: 5 },
children: [
// {
// path: '/construction-operation-platform/implementation-workflow',
// name: 'ImplementationWorkflow',
// component: () => import('@/components/ParentView/index.vue'),
// redirect: '/construction-operation-platform/implementation-workflow/field-construction',
// meta: { title: '项目实施工作流程', icon: 'fork', hidden: false },
// children: [
{
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/attachment',
name: 'AttachmentManagement',
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/data-storage/index.vue'),
meta: { title: '附件管理', icon: 'attachment', hidden: false },
},
{
path: '/construction-operation-platform/implementation-workflow/data-processing/model-config',
name: 'ModelConfig',
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/model-config/index.vue'),
meta: { title: '模型配置', icon: 'robot', hidden: false },
},
{
path: '/construction-operation-platform/implementation-workflow/field-construction',
name: 'FieldConstruction',
component: () => import('@/components/ParentView/index.vue'),
redirect: '/construction-operation-platform/implementation-workflow/field-construction/project-list',
meta: { title: '我的项目', icon: 'construction', hidden: false },
children: [
{
path: '/project-management/projects/list',
name: 'ProjectList',
component: () => import('@/views/project-management/projects/list/index.vue'),
meta: {
title: '项目列表',
icon: 'unordered-list',
hidden: false,
},
},
{
path: '/construction-operation-platform/implementation-workflow/field-construction/technology',
name: 'FieldConstructionTechnology',
component: () => import('@/views/project-operation-platform/implementation-workflow/field-construction/project-list/index.vue'),
meta: { title: '我的施工', icon: 'tool', hidden: false },
},
// start
// 数据管理 // 数据管理
{ {
path: '/data-management', path: '/data-management',
name: 'DataManagement', name: 'DataManagement',
component: Layout,
redirect: '/data-management/project-management/project-template', redirect: '/data-management/project-management/project-template',
meta: { title: '数据管理', icon: 'database', hidden: false, sort: 4 }, meta: { title: '数据管理', icon: 'database', hidden: false, sort: 4 },
children: [ children: [
@ -1027,55 +1076,6 @@ export const systemRoutes: RouteRecordRaw[] = [
], ],
}, },
// end // end
{
path: '/construction-operation-platform',
name: 'ConstructionOperationPlatform',
component: Layout,
redirect: '/construction-operation-platform/implementation-workflow/field-construction',
meta: { title: '我的工作台', icon: 'tool', hidden: false, sort: 5 },
children: [
// {
// path: '/construction-operation-platform/implementation-workflow',
// name: 'ImplementationWorkflow',
// component: () => import('@/components/ParentView/index.vue'),
// redirect: '/construction-operation-platform/implementation-workflow/field-construction',
// meta: { title: '项目实施工作流程', icon: 'fork', hidden: false },
// children: [
{
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/attachment',
name: 'AttachmentManagement',
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/data-storage/index.vue'),
meta: { title: '附件管理', icon: 'attachment', hidden: false },
},
{
path: '/construction-operation-platform/implementation-workflow/data-processing/model-config',
name: 'ModelConfig',
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/model-config/index.vue'),
meta: { title: '模型配置', icon: 'robot', hidden: false },
},
{
path: '/construction-operation-platform/implementation-workflow/field-construction',
name: 'FieldConstruction',
component: () => import('@/components/ParentView/index.vue'),
redirect: '/construction-operation-platform/implementation-workflow/field-construction/project-list',
meta: { title: '我的项目', icon: 'construction', hidden: false },
children: [
{
path: '/project-management/projects/list',
name: 'ProjectList',
component: () => import('@/views/project-management/projects/list/index.vue'),
meta: {
title: '项目列表',
icon: 'unordered-list',
hidden: false,
},
},
{
path: '/construction-operation-platform/implementation-workflow/field-construction/technology',
name: 'FieldConstructionTechnology',
component: () => import('@/views/project-operation-platform/implementation-workflow/field-construction/project-list/index.vue'),
meta: { title: '我的施工', icon: 'tool', hidden: false },
},
{ {
path: '/construction-operation-platform/implementation-workflow/project-delivery', path: '/construction-operation-platform/implementation-workflow/project-delivery',
name: 'ProjectDelivery', name: 'ProjectDelivery',

View File

@ -7,70 +7,7 @@ export {}
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
ApprovalAssistant: typeof import('./../components/ApprovalAssistant/index.vue')['default']
ApprovalMessageItem: typeof import('./../components/NotificationCenter/ApprovalMessageItem.vue')['default']
Avatar: typeof import('./../components/Avatar/index.vue')['default']
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default']
CellCopy: typeof import('./../components/CellCopy/index.vue')['default']
Chart: typeof import('./../components/Chart/index.vue')['default']
CircularProgress: typeof import('./../components/CircularProgress/index.vue')['default']
ColumnSetting: typeof import('./../components/GiTable/src/components/ColumnSetting.vue')['default']
CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default']
CronModal: typeof import('./../components/GenCron/CronModal/index.vue')['default']
DateRangePicker: typeof import('./../components/DateRangePicker/index.vue')['default']
DayForm: typeof import('./../components/GenCron/CronForm/component/day-form.vue')['default']
FilePreview: typeof import('./../components/FilePreview/index.vue')['default']
GiCellAvatar: typeof import('./../components/GiCell/GiCellAvatar.vue')['default']
GiCellGender: typeof import('./../components/GiCell/GiCellGender.vue')['default']
GiCellStatus: typeof import('./../components/GiCell/GiCellStatus.vue')['default']
GiCellTag: typeof import('./../components/GiCell/GiCellTag.vue')['default']
GiCellTags: typeof import('./../components/GiCell/GiCellTags.vue')['default']
GiCodeView: typeof import('./../components/GiCodeView/index.vue')['default']
GiDot: typeof import('./../components/GiDot/index.tsx')['default']
GiEditTable: typeof import('./../components/GiEditTable/GiEditTable.vue')['default']
GiFooter: typeof import('./../components/GiFooter/index.vue')['default']
GiForm: typeof import('./../components/GiForm/src/GiForm.vue')['default']
GiIconBox: typeof import('./../components/GiIconBox/index.vue')['default']
GiIconSelector: typeof import('./../components/GiIconSelector/index.vue')['default']
GiIframe: typeof import('./../components/GiIframe/index.vue')['default']
GiOption: typeof import('./../components/GiOption/index.vue')['default']
GiOptionItem: typeof import('./../components/GiOptionItem/index.vue')['default']
GiPageLayout: typeof import('./../components/GiPageLayout/index.vue')['default']
GiSpace: typeof import('./../components/GiSpace/index.vue')['default']
GiSplitButton: typeof import('./../components/GiSplitButton/index.vue')['default']
GiSplitPane: typeof import('./../components/GiSplitPane/index.vue')['default']
GiSplitPaneFlexibleBox: typeof import('./../components/GiSplitPane/components/GiSplitPaneFlexibleBox.vue')['default']
GiSvgIcon: typeof import('./../components/GiSvgIcon/index.vue')['default']
GiTable: typeof import('./../components/GiTable/src/GiTable.vue')['default']
GiTag: typeof import('./../components/GiTag/index.tsx')['default']
GiThemeBtn: typeof import('./../components/GiThemeBtn/index.vue')['default']
HourForm: typeof import('./../components/GenCron/CronForm/component/hour-form.vue')['default']
Icon403: typeof import('./../components/icons/Icon403.vue')['default']
Icon404: typeof import('./../components/icons/Icon404.vue')['default']
Icon500: typeof import('./../components/icons/Icon500.vue')['default']
IconBorders: typeof import('./../components/icons/IconBorders.vue')['default']
IconTableSize: typeof import('./../components/icons/IconTableSize.vue')['default']
IconTreeAdd: typeof import('./../components/icons/IconTreeAdd.vue')['default']
IconTreeReduce: typeof import('./../components/icons/IconTreeReduce.vue')['default']
ImageImport: typeof import('./../components/ImageImport/index.vue')['default']
ImageImportWizard: typeof import('./../components/ImageImportWizard/index.vue')['default']
IndustrialImageList: typeof import('./../components/IndustrialImageList/index.vue')['default']
JsonPretty: typeof import('./../components/JsonPretty/index.vue')['default']
MinuteForm: typeof import('./../components/GenCron/CronForm/component/minute-form.vue')['default']
MonthForm: typeof import('./../components/GenCron/CronForm/component/month-form.vue')['default']
NotificationCenter: typeof import('./../components/NotificationCenter/index.vue')['default']
ParentView: typeof import('./../components/ParentView/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
SecondForm: typeof import('./../components/GenCron/CronForm/component/second-form.vue')['default']
SplitPanel: typeof import('./../components/SplitPanel/index.vue')['default']
TextCopy: typeof import('./../components/TextCopy/index.vue')['default']
TurbineGrid: typeof import('./../components/TurbineGrid/index.vue')['default']
UserSelect: typeof import('./../components/UserSelect/index.vue')['default']
Verify: typeof import('./../components/Verify/index.vue')['default']
VerifyPoints: typeof import('./../components/Verify/Verify/VerifyPoints.vue')['default']
VerifySlide: typeof import('./../components/Verify/Verify/VerifySlide.vue')['default']
WeekForm: typeof import('./../components/GenCron/CronForm/component/week-form.vue')['default']
YearForm: typeof import('./../components/GenCron/CronForm/component/year-form.vue')['default']
} }
} }

View File

@ -1,474 +1,380 @@
<template> <template>
<a-modal <a-modal
:visible="visible" :visible="visible"
title="设备付款" title="付款申请"
width="900px" width="800px"
:confirm-loading="loading" :footer="false"
@cancel="handleCancel" @cancel="handleCancel"
@ok="handleSubmit" @update:visible="handleVisibleChange"
> >
<a-form <div class="payment-application-container">
ref="formRef" <!-- 设备基本信息 -->
:model="formData" <a-card title="设备信息" class="info-card" :bordered="false">
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
layout="vertical"
>
<!-- 基本信息 -->
<a-card title="设备信息" class="detail-card" :bordered="false">
<a-descriptions :column="2" bordered> <a-descriptions :column="2" bordered>
<a-descriptions-item label="设备名称"> <a-descriptions-item label="设备名称">
{{ equipmentData?.equipmentName || '-' }} {{ equipmentData?.equipmentName }}
</a-descriptions-item>
<a-descriptions-item label="设备类型">
{{ equipmentData?.equipmentType || '-' }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="设备型号"> <a-descriptions-item label="设备型号">
{{ equipmentData?.equipmentModel || '-' }} {{ equipmentData?.equipmentModel }}
</a-descriptions-item> </a-descriptions-item>
<a-col :span="12">
<a-form-item label="品牌">
{{ equipmentData?.brand || '-' }}
</a-form-item>
</a-col>
<a-descriptions-item label="供应商"> <a-descriptions-item label="供应商">
{{ equipmentData?.supplierName || '-' }} {{ equipmentData?.supplierName }}
</a-descriptions-item>
<a-descriptions-item label="采购订单">
{{ equipmentData?.purchaseOrder || '-' }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="采购价格"> <a-descriptions-item label="采购价格">
¥{{ equipmentData?.purchasePrice || '-' }} ¥{{ formatPrice(equipmentData?.purchasePrice || 0) }}
</a-descriptions-item>
<a-descriptions-item label="采购数量">
{{ equipmentData?.quantity || '-' }}
</a-descriptions-item> </a-descriptions-item>
</a-descriptions> </a-descriptions>
</a-card> </a-card>
<!-- 支付信息 --> <!-- 付款申请信息 -->
<a-card title="支付信息" class="detail-card" :bordered="false"> <a-card title="付款申请信息" class="info-card" :bordered="false">
<a-row :gutter="16"> <a-form
<a-col :span="12"> ref="paymentFormRef"
<a-form-item label="支付方式" field="paymentMethod" required> :model="paymentForm"
<a-select :rules="paymentRules"
v-model="formData.paymentMethod" layout="vertical"
placeholder="请选择支付方式" >
style="width: 100%" <a-row :gutter="16">
> <a-col :span="12">
<a-option value="银行转账">银行转账</a-option> <a-form-item label="申请日期" name="applicationDate">
<a-option value="现金">现金</a-option> <a-date-picker
<a-option value="支票">支票</a-option> v-model="paymentForm.applicationDate"
<a-option value="信用卡">信用卡</a-option> placeholder="请选择申请日期"
<a-option value="其他">其他</a-option> style="width: 100%"
</a-select> />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-form-item label="支付金额" field="paymentAmount" required> <a-form-item label="部门" name="department">
<a-input-number <a-input
v-model="formData.paymentAmount" v-model="paymentForm.department"
placeholder="请输入支付金额" placeholder="请输入部门名称"
:min="0.01" allow-clear
:max="999999.99" />
:precision="2" </a-form-item>
style="width: 100%" </a-col>
/> </a-row>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="12"> <a-col :span="12">
<a-form-item label="支付时间" field="paymentTime" required> <a-form-item label="归属项目" name="projectName">
<a-date-picker <a-input
v-model="formData.paymentTime" v-model="paymentForm.projectName"
show-time placeholder="请输入项目名称"
format="YYYY-MM-DD HH:mm:ss" allow-clear
placeholder="请选择支付时间" />
style="width: 100%" </a-form-item>
/> </a-col>
</a-form-item> <a-col :span="12">
</a-col> <a-form-item label="支出类别" name="expenseCategory">
<a-col :span="12"> <a-select
<a-form-item label="支付人" field="paymentPerson" required> v-model="paymentForm.expenseCategory"
<a-input placeholder="请选择支出类别"
v-model="formData.paymentPerson" allow-clear
placeholder="请输入支付人姓名" >
show-word-limit <a-option value="EXPENSE_TRANSPORT">a.费用支出-交通费</a-option>
:max-length="50" <a-option value="EXPENSE_ACCOMMODATION">a.费用支出-住宿费</a-option>
/> <a-option value="EXPENSE_TOOLS">a.费用支出-小工具</a-option>
</a-form-item> <a-option value="EXPENSE_VEHICLE_MAINTENANCE">a.费用支出-车辆保养</a-option>
</a-col> <a-option value="PROCUREMENT_DRONE">b.采购支出-设备采购-无人机</a-option>
</a-row> <a-option value="PROCUREMENT_VEHICLE">b.采购支出-设备采购-</a-option>
<a-option value="PROCUREMENT_COMPUTER">b.采购支出-设备采购-计算机</a-option>
<a-option value="SALARY">c.工资支出</a-option>
<a-option value="TAX">d.税款支出</a-option>
<a-option value="OTHER">f.其他支出</a-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="24"> <a-col :span="12">
<a-form-item label="支付备注" field="paymentRemark"> <a-form-item label="事由" name="reason">
<a-textarea <a-textarea
v-model="formData.paymentRemark" v-model="paymentForm.reason"
placeholder="请输入支付备注" placeholder="请输入付款事由"
:rows="3" :rows="3"
show-word-limit allow-clear
:max-length="200" />
/> </a-form-item>
</a-form-item> </a-col>
</a-col> <a-col :span="12">
</a-row> <a-form-item label="经手人" name="handler">
<a-input
v-model="paymentForm.handler"
placeholder="请输入经手人姓名"
allow-clear
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="金额" name="amount">
<a-input-number
v-model="paymentForm.amount"
placeholder="请输入付款金额"
:precision="2"
:min="0"
style="width: 100%"
addon-before="¥"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="收款单位" name="payeeUnit">
<a-input
v-model="paymentForm.payeeUnit"
placeholder="请输入收款单位名称"
allow-clear
/>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="收款账号" name="payeeAccount">
<a-input
v-model="paymentForm.payeeAccount"
placeholder="请输入收款账号"
allow-clear
/>
</a-form-item>
<!-- 发票附件及其他附件 -->
<a-form-item label="发票附件" name="invoiceAttachments">
<a-upload
v-model:file-list="paymentForm.invoiceAttachments"
:action="uploadAction"
:headers="uploadHeaders"
:before-upload="beforeUpload"
:on-success="onInvoiceUploadSuccess"
:on-error="onUploadError"
accept=".pdf,.jpg,.jpeg,.png"
:max-count="5"
>
<a-button>
<template #icon>
<IconUpload />
</template>
上传发票附件
</a-button>
<template #itemRender="{ file }">
<a-space>
<IconFile />
<span>{{ file.name }}</span>
<a-button
type="text"
size="small"
status="danger"
@click="removeInvoiceAttachment(file)"
>
删除
</a-button>
</a-space>
</template>
</a-upload>
</a-form-item>
<!-- 银行付款凭证 -->
<a-form-item label="银行付款凭证" name="bankPaymentVouchers">
<a-upload
v-model:file-list="paymentForm.bankPaymentVouchers"
:action="uploadAction"
:headers="uploadHeaders"
:before-upload="beforeUpload"
:on-success="onBankVoucherUploadSuccess"
:on-error="onUploadError"
accept=".pdf,.jpg,.jpeg,.png"
:max-count="3"
>
<a-button>
<template #icon>
<IconUpload />
</template>
上传银行付款凭证
</a-button>
<template #itemRender="{ file }">
<a-space>
<IconFile />
<span>{{ file.name }}</span>
<a-button
type="text"
size="small"
status="danger"
@click="removeBankVoucher(file)"
>
删除
</a-button>
</a-space>
</template>
</a-upload>
</a-form-item>
<a-form-item label="备注" name="remarks">
<a-textarea
v-model="paymentForm.remarks"
placeholder="请输入备注信息"
:rows="3"
allow-clear
/>
</a-form-item>
</a-form>
</a-card> </a-card>
<!-- 发票信息 --> <!-- 操作按钮 -->
<a-card title="发票信息" class="detail-card" :bordered="false"> <div class="action-buttons">
<a-row :gutter="16"> <a-space>
<a-col :span="12"> <a-button @click="handleCancel">取消</a-button>
<a-form-item label="发票类型" field="invoiceType" required> <a-button type="primary" @click="handleSubmit" :loading="submitting">
<a-select 提交付款申请
v-model="formData.invoiceType" </a-button>
placeholder="请选择发票类型" </a-space>
style="width: 100%" </div>
> </div>
<a-option value="增值税专用发票">增值税专用发票</a-option>
<a-option value="增值税普通发票">增值税普通发票</a-option>
<a-option value="电子发票">电子发票</a-option>
<a-option value="其他">其他</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="发票号码" field="invoiceNumber" required>
<a-input
v-model="formData.invoiceNumber"
placeholder="请输入发票号码"
show-word-limit
:max-length="50"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="开票日期" field="invoiceDate" required>
<a-date-picker
v-model="formData.invoiceDate"
format="YYYY-MM-DD"
placeholder="请选择开票日期"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="发票金额" field="invoiceAmount" required>
<a-input-number
v-model="formData.invoiceAmount"
placeholder="请输入发票金额"
:min="0.01"
:max="999999.99"
:precision="2"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="税率(%)" field="taxRate" required>
<a-input-number
v-model="formData.taxRate"
placeholder="请输入税率"
:min="0"
:max="100"
:precision="2"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="税额" field="taxAmount" required>
<a-input-number
v-model="formData.taxAmount"
placeholder="请输入税额"
:min="0"
:max="999999.99"
:precision="2"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="不含税金额" field="amountWithoutTax" required>
<a-input-number
v-model="formData.amountWithoutTax"
placeholder="请输入不含税金额"
:min="0.01"
:max="999999.99"
:precision="2"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
</a-card>
<!-- 合同信息 -->
<a-card title="合同信息" class="detail-card" :bordered="false">
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="合同编号" field="contractNumber" required>
<a-input
v-model="formData.contractNumber"
placeholder="请输入合同编号"
show-word-limit
:max-length="50"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="合同金额" field="contractAmount" required>
<a-input-number
v-model="formData.contractAmount"
placeholder="请输入合同金额"
:min="0.01"
:max="999999.99"
:precision="2"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="签订日期" field="contractDate" required>
<a-date-picker
v-model="formData.contractDate"
format="YYYY-MM-DD"
placeholder="请选择签订日期"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="付款条件" field="paymentTerms" required>
<a-input
v-model="formData.paymentTerms"
placeholder="请输入付款条件"
show-word-limit
:max-length="100"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="付款期限" field="paymentDeadline" required>
<a-date-picker
v-model="formData.paymentDeadline"
format="YYYY-MM-DD"
placeholder="请选择付款期限"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
</a-card>
</a-form>
</a-modal> </a-modal>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { ref, reactive, watch, computed } from 'vue' import { ref, reactive } from 'vue'
import { Message } from '@arco-design/web-vue' import { IconUpload, IconFile } from '@arco-design/web-vue/es/icon'
import type { FormInstance } from '@arco-design/web-vue' import message from '@arco-design/web-vue/es/message'
import type { EquipmentResp, PaymentRequest } from '@/apis/equipment/type' import type { EquipmentResp } from '@/apis/equipment/type'
import { equipmentProcurementApi } from '@/apis/equipment/procurement'
interface Props { interface Props {
visible: boolean visible: boolean
equipmentData?: EquipmentResp | null equipmentData: EquipmentResp | null
} }
const props = withDefaults(defineProps<Props>(), { interface PaymentForm {
visible: false, applicationDate: string | null
equipmentData: null, department: string
}) projectName: string
expenseCategory: string
reason: string
handler: string
amount: number | null
payeeUnit: string
payeeAccount: string
invoiceAttachments: any[]
bankPaymentVouchers: any[]
remarks: string
}
const props = defineProps<Props>()
const emit = defineEmits<{ const emit = defineEmits<{
'update:visible': [value: boolean] 'update:visible': [value: boolean]
'success': [] 'success': []
}>() }>()
const formRef = ref<FormInstance>() //
const loading = ref(false) const paymentFormRef = ref()
// //
const formData = reactive<PaymentRequest>({ const submitting = ref(false)
paymentMethod: '',
paymentAmount: 0,
paymentTime: '',
paymentPerson: '',
paymentRemark: '',
invoiceType: '',
invoiceNumber: '',
invoiceDate: '',
invoiceAmount: 0,
taxRate: 13,
taxAmount: 0,
amountWithoutTax: 0,
contractNumber: '',
contractAmount: 0,
contractDate: '',
paymentTerms: '',
paymentDeadline: '',
})
// //
const calculatedTaxAmount = computed(() => { const paymentForm = reactive<PaymentForm>({
if (formData.invoiceAmount && formData.taxRate) { applicationDate: null,
return (formData.invoiceAmount * formData.taxRate) / 100 department: '',
} projectName: '',
return 0 expenseCategory: '',
}) reason: '',
handler: '',
const calculatedAmountWithoutTax = computed(() => { amount: null,
if (formData.invoiceAmount && formData.taxAmount) { payeeUnit: '',
return formData.invoiceAmount - formData.taxAmount payeeAccount: '',
} invoiceAttachments: [],
return 0 bankPaymentVouchers: [],
}) remarks: ''
//
watch([() => formData.invoiceAmount, () => formData.taxRate], () => {
formData.taxAmount = calculatedTaxAmount.value
formData.amountWithoutTax = calculatedAmountWithoutTax.value
}) })
// //
const rules = { const paymentRules = {
paymentMethod: [ applicationDate: [
{ required: true, message: '请选择支付方式' } { required: true, message: '请选择申请日期', trigger: 'change' }
], ],
paymentAmount: [ department: [
{ required: true, message: '请输入支付金额' }, { required: true, message: '请输入部门名称', trigger: 'blur' }
{ type: 'number', min: 0.01, message: '支付金额必须大于0' }
], ],
paymentTime: [ projectName: [
{ required: true, message: '请选择支付时间' } { required: true, message: '请输入项目名称', trigger: 'blur' }
], ],
paymentPerson: [ expenseCategory: [
{ required: true, message: '请输入支付人姓名' }, { required: true, message: '请选择支出类别', trigger: 'change' }
{ min: 2, max: 50, message: '支付人姓名长度应在2-50个字符之间' }
], ],
invoiceType: [ reason: [
{ required: true, message: '请选择发票类型' } { required: true, message: '请输入付款事由', trigger: 'blur' }
], ],
invoiceNumber: [ handler: [
{ required: true, message: '请输入发票号码' }, { required: true, message: '请输入经手人姓名', trigger: 'blur' }
{ min: 2, max: 50, message: '发票号码长度应在2-50个字符之间' }
], ],
invoiceDate: [ amount: [
{ required: true, message: '请选择开票日期' } { required: true, message: '请输入付款金额', trigger: 'blur' }
], ],
invoiceAmount: [ payeeUnit: [
{ required: true, message: '请输入发票金额' }, { required: true, message: '请输入收款单位名称', trigger: 'blur' }
{ type: 'number', min: 0.01, message: '发票金额必须大于0' }
],
taxRate: [
{ required: true, message: '请输入税率' },
{ type: 'number', min: 0, max: 100, message: '税率应在0-100之间' }
],
contractNumber: [
{ required: true, message: '请输入合同编号' },
{ min: 2, max: 50, message: '合同编号长度应在2-50个字符之间' }
],
contractAmount: [
{ required: true, message: '请输入合同金额' },
{ type: 'number', min: 0.01, message: '合同金额必须大于0' }
],
contractDate: [
{ required: true, message: '请选择签订日期' }
],
paymentTerms: [
{ required: true, message: '请输入付款条件' },
{ min: 2, max: 100, message: '付款条件长度应在2-100个字符之间' }
],
paymentDeadline: [
{ required: true, message: '请选择付款期限' }
], ],
payeeAccount: [
{ required: true, message: '请输入收款账号', trigger: 'blur' }
]
} }
// //
watch(() => props.visible, (visible) => { const uploadAction = '/api/file/upload'
if (visible && props.equipmentData) { const uploadHeaders = {
// //
Object.assign(formData, { }
paymentMethod: '',
paymentAmount: props.equipmentData.purchasePrice || 0, //
paymentTime: '', const formatPrice = (price: number) => {
paymentPerson: '', return price.toLocaleString('zh-CN', {
paymentRemark: '', minimumFractionDigits: 2,
invoiceType: '增值税专用发票', maximumFractionDigits: 2,
invoiceNumber: '', })
invoiceDate: '', }
invoiceAmount: props.equipmentData.purchasePrice || 0,
taxRate: 13, //
taxAmount: 0, const beforeUpload = (file: File) => {
amountWithoutTax: 0, const isValidSize = file.size / 1024 / 1024 < 10 // 10MB
contractNumber: props.equipmentData.purchaseOrder || '', if (!isValidSize) {
contractAmount: props.equipmentData.purchasePrice || 0, message.error('文件大小不能超过10MB')
contractDate: '', return false
paymentTerms: '货到付款',
paymentDeadline: '',
})
//
formData.taxAmount = calculatedTaxAmount.value
formData.amountWithoutTax = calculatedAmountWithoutTax.value
formRef.value?.clearValidate()
} }
}) return true
}
// //
const handleSubmit = async () => { const onInvoiceUploadSuccess = (response: any, file: any) => {
try { console.log('发票附件上传成功:', response, file)
await formRef.value?.validate() message.success('发票附件上传成功')
loading.value = true }
if (!props.equipmentData?.equipmentId) {
throw new Error('设备ID不能为空')
}
// //
const paymentTime = formData.paymentTime ? new Date(formData.paymentTime).toISOString() : new Date().toISOString() const onBankVoucherUploadSuccess = (response: any, file: any) => {
const invoiceDate = formData.invoiceDate ? new Date(formData.invoiceDate).toISOString() : new Date().toISOString() console.log('银行付款凭证上传成功:', response, file)
const contractDate = formData.contractDate ? new Date(formData.contractDate).toISOString() : new Date().toISOString() message.success('银行付款凭证上传成功')
const paymentDeadline = formData.paymentDeadline ? new Date(formData.paymentDeadline).toISOString() : new Date().toISOString() }
const requestData: PaymentRequest = {
...formData,
paymentTime,
invoiceDate,
contractDate,
paymentDeadline,
}
await equipmentProcurementApi.makePayment(props.equipmentData.equipmentId, requestData) //
const onUploadError = (error: any) => {
Message.success('付款成功') console.error('文件上传失败:', error)
emit('success') message.error('文件上传失败')
emit('update:visible', false) }
} catch (error: any) {
console.error('付款失败:', error) //
Message.error(error?.message || '付款失败,请检查表单信息') const removeInvoiceAttachment = (file: any) => {
} finally { const index = paymentForm.invoiceAttachments.findIndex(f => f.uid === file.uid)
loading.value = false if (index > -1) {
paymentForm.invoiceAttachments.splice(index, 1)
}
}
//
const removeBankVoucher = (file: any) => {
const index = paymentForm.bankPaymentVouchers.findIndex(f => f.uid === file.uid)
if (index > -1) {
paymentForm.bankPaymentVouchers.splice(index, 1)
} }
} }
@ -476,30 +382,54 @@ const handleSubmit = async () => {
const handleCancel = () => { const handleCancel = () => {
emit('update:visible', false) emit('update:visible', false)
} }
// visible
const handleVisibleChange = (newVisible: boolean) => {
emit('update:visible', newVisible)
}
//
const handleSubmit = async () => {
try {
submitting.value = true
//
await paymentFormRef.value?.validate()
// API
console.log('提交付款申请:', {
...paymentForm,
equipmentId: props.equipmentData?.equipmentId
})
message.success('付款申请提交成功')
emit('success')
emit('update:visible', false)
} catch (error) {
console.error('提交失败:', error)
message.error('提交失败,请检查表单信息')
} finally {
submitting.value = false
}
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.detail-card { .payment-application-container {
margin-bottom: 16px; .info-card {
margin-bottom: 16px;
&:last-child {
margin-bottom: 0;
}
}
&:last-child { .action-buttons {
margin-bottom: 0; text-align: center;
margin-top: 24px;
padding-top: 16px;
border-top: 1px solid var(--color-border);
} }
} }
.arco-form-item {
margin-bottom: 16px;
}
.arco-input,
.arco-select,
.arco-input-number,
.arco-date-picker,
.arco-textarea {
border-radius: 6px;
}
.arco-textarea {
resize: vertical;
}
</style> </style>

View File

@ -201,6 +201,8 @@
<a-button type="text" size="small" @click="handleEdit(record)"> <a-button type="text" size="small" @click="handleEdit(record)">
编辑 编辑
</a-button> </a-button>
<!-- 1. 采购相关操作 -->
<!-- 申请采购按钮 - 只在特定状态下显示 --> <!-- 申请采购按钮 - 只在特定状态下显示 -->
<a-button <a-button
v-if="canApplyProcurement(record)" v-if="canApplyProcurement(record)"
@ -210,15 +212,8 @@
> >
申请采购 申请采购
</a-button> </a-button>
<!-- 收货操作按钮 -->
<a-button <!-- 2. 合同发票管理相关操作 -->
v-if="canReceiveGoods(record)"
type="primary"
size="small"
@click="handleReceiveGoods(record)"
>
确认收货
</a-button>
<!-- 管理合同发票按钮 - 在采购审批通过后显示 --> <!-- 管理合同发票按钮 - 在采购审批通过后显示 -->
<a-button <a-button
v-if="canManageContractInvoice(record)" v-if="canManageContractInvoice(record)"
@ -228,22 +223,16 @@
> >
管理合同发票 管理合同发票
</a-button> </a-button>
<a-button
v-if="record.receiptStatus === 'RECEIVED'" <!-- 3. 支付相关操作 -->
type="text" <!-- 申请付款按钮 -->
size="small"
@click="handleViewReceipt(record)"
>
查看收货
</a-button>
<!-- 支付操作按钮 -->
<a-button <a-button
v-if="canMakePayment(record)" v-if="canMakePayment(record)"
type="outline" type="outline"
size="small" size="small"
@click="handleMakePayment(record)" @click="handleMakePayment(record)"
> >
付款 申请付款
</a-button> </a-button>
<a-button <a-button
v-if="record.paymentStatus === 'PAID'" v-if="record.paymentStatus === 'PAID'"
@ -253,29 +242,75 @@
> >
查看支付详情 查看支付详情
</a-button> </a-button>
<!-- 手动刷新按钮 - 用于调试状态更新 -->
<!-- 4. 收货相关操作 -->
<!-- 收货操作按钮 -->
<a-button <a-button
v-if="canReceiveGoods(record)"
type="primary"
size="small"
@click="handleReceiveGoods(record)"
>
确认收货
</a-button>
<a-button
v-if="record.receiptStatus === 'RECEIVED'"
type="text" type="text"
size="small" size="small"
@click="handleRefreshRecord(record)" @click="handleViewReceipt(record)"
title="刷新此条记录状态"
> >
<template #icon> 查看收货
<IconRefresh />
</template>
刷新
</a-button> </a-button>
<!-- 显示采购状态 - 优先显示采购状态 -->
<!-- 状态标签 - 按照要求的顺序排列 -->
<!-- 1. 采购状态 -->
<a-tag <a-tag
v-if="record.procurementStatus && record.procurementStatus !== 'NOT_STARTED'" v-if="record.procurementStatus && record.procurementStatus !== 'NOT_STARTED'"
:color="getProcurementStatusColor(record.procurementStatus)" :color="getProcurementStatusColor(record.procurementStatus)"
> >
{{ getProcurementStatusText(record.procurementStatus) }} {{ getProcurementStatusText(record.procurementStatus) }}
</a-tag> </a-tag>
<!-- 显示审批状态 -->
<a-tag v-if="record.approvalStatus" :color="getApprovalStatusColor(record.approvalStatus)"> <!-- 2. 合同发票管理状态 -->
{{ getApprovalStatusText(record.approvalStatus) }} <a-tag
v-if="hasContractInvoiceInfo(record)"
color="green"
>
已完善
</a-tag> </a-tag>
<a-tag
v-else-if="record.procurementStatus === 'APPROVED'"
color="orange"
>
待完善
</a-tag>
<a-tag
v-else
color="gray"
>
未开始
</a-tag>
<!-- 3. 支付状态 -->
<a-tag
v-if="record.paymentStatus && record.paymentStatus !== 'NOT_PAID'"
:color="getPaymentStatusColor(record.paymentStatus)"
>
{{ getPaymentStatusText(record.paymentStatus) }}
</a-tag>
<a-tag v-else color="gray">未支付</a-tag>
<!-- 4. 收货状态 -->
<a-tag
v-if="record.receiptStatus && record.receiptStatus !== 'NOT_RECEIVED'"
:color="getReceiptStatusColor(record.receiptStatus)"
>
{{ getReceiptStatusText(record.receiptStatus) }}
</a-tag>
<a-tag v-else color="gray">未收货</a-tag>
<!-- 删除按钮 --> <!-- 删除按钮 -->
<a-popconfirm <a-popconfirm
content="确定要删除这条记录吗?" content="确定要删除这条记录吗?"
@ -1207,6 +1242,15 @@ const handleContractInvoiceSuccess = () => {
loadData(currentSearchParams.value) loadData(currentSearchParams.value)
} }
//
const hasContractInvoiceInfo = (record: EquipmentResp) => {
//
// 使
//
return record.procurementStatus === 'APPROVED' &&
(record.receiptStatus === 'RECEIVED' || record.paymentStatus === 'PAID')
}
onMounted(() => { onMounted(() => {
loadData() loadData()
}) })
@ -1393,6 +1437,8 @@ onMounted(() => {
} }
} }
} }
} }
// //