1286 lines
46 KiB
Vue
1286 lines
46 KiB
Vue
<template>
|
|
<basic-container class="invoice-form-page">
|
|
<div class="archive-page-form__title">
|
|
{{ readonly ? '查看开票申请' : recordId ? '编辑开票申请' : '新增开票申请' }}
|
|
</div>
|
|
|
|
<el-form
|
|
ref="formRef"
|
|
:model="form"
|
|
:rules="rules"
|
|
label-position="right"
|
|
label-width="auto"
|
|
class="invoice-form-page__form"
|
|
>
|
|
<section-card title="开票基本信息">
|
|
<el-row :gutter="24">
|
|
<el-col :span="6">
|
|
<el-form-item label="结算单" prop="settlementIds">
|
|
<el-input :model-value="settlementLabel" readonly placeholder="请选择应收正式结算单">
|
|
<template v-if="!readonly" #append>
|
|
<el-button @click="openSettlementDialog">选择</el-button>
|
|
</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="所属项目">
|
|
<el-input v-model="form.projectName" disabled placeholder="根据结算单自动带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="开票单位">
|
|
<el-input v-model="form.issuerName" disabled placeholder="根据结算单自动带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="发票类型" prop="invoiceType">
|
|
<el-select
|
|
v-model="form.invoiceType"
|
|
:disabled="readonly"
|
|
@change="handleInvoiceTypeChange"
|
|
>
|
|
<el-option
|
|
v-for="item in invoiceTypeOptions"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="可开票金额">
|
|
<el-input
|
|
:model-value="form.settlements.length ? formatMoney(availableInvoiceAmount) : ''"
|
|
disabled
|
|
placeholder="根据结算单自动计算"
|
|
/>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="本次开票金额">
|
|
<el-input
|
|
:model-value="invoiceAmount > 0 ? formatMoney(invoiceAmount) : ''"
|
|
disabled
|
|
placeholder="根据开票明细自动计算"
|
|
/>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="申请日期">
|
|
<el-input v-model="form.applicationDate" disabled placeholder="提交后自动显示" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="申请人">
|
|
<el-input v-model="form.applicantName" disabled />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="承办部门">
|
|
<el-input
|
|
v-model="form.undertakingDeptName"
|
|
disabled
|
|
placeholder="根据结算单自动带出"
|
|
/>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="部门邮箱" prop="departmentEmails">
|
|
<el-select
|
|
v-model="form.departmentEmails"
|
|
:disabled="readonly"
|
|
multiple
|
|
filterable
|
|
:multiple-limit="3"
|
|
placeholder="请选择客商开票信息邮箱"
|
|
no-data-text="当前客商开票信息未配置邮箱"
|
|
>
|
|
<el-option
|
|
v-for="email in departmentEmailOptions"
|
|
:key="email"
|
|
:label="email"
|
|
:value="email"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
</section-card>
|
|
|
|
<section-card title="受票方信息">
|
|
<el-row :gutter="24">
|
|
<el-col :span="6">
|
|
<el-form-item label="受票方单位" prop="receiverInvoiceInfoId">
|
|
<el-select
|
|
v-model="form.receiverInvoiceInfoId"
|
|
:disabled="readonly"
|
|
filterable
|
|
@change="handleReceiverChange"
|
|
>
|
|
<el-option
|
|
v-for="item in receiverInvoiceOptions"
|
|
:key="item.id"
|
|
:label="item.invoiceTitle"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="纳税人识别号" prop="taxpayerNo">
|
|
<el-input v-model="form.taxpayerNo" disabled placeholder="选择受票方单位后自动带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="开户账号" prop="bankAccount">
|
|
<el-input
|
|
v-model="form.bankAccount"
|
|
disabled
|
|
placeholder="选择受票方单位后自动带出"
|
|
/>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="开户行" prop="bankName">
|
|
<el-input v-model="form.bankName" disabled placeholder="选择受票方单位后自动带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="注册地址" prop="registeredAddress">
|
|
<el-input
|
|
v-model="form.registeredAddress"
|
|
disabled
|
|
placeholder="选择受票方单位后自动带出"
|
|
/>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="联系人">
|
|
<el-select
|
|
v-model="form.contactName"
|
|
:disabled="readonly"
|
|
clearable
|
|
filterable
|
|
allow-create
|
|
@change="handleContactChange"
|
|
>
|
|
<el-option
|
|
v-for="item in contactOptions"
|
|
:key="item.id"
|
|
:label="item.contactName"
|
|
:value="item.contactName"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="联系电话" prop="contactPhone">
|
|
<el-input v-model="form.contactPhone" :disabled="readonly" maxlength="11" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="邮箱" prop="email">
|
|
<el-input v-model="form.email" :disabled="readonly" maxlength="100" />
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
</section-card>
|
|
|
|
<section-card title="开票内容"
|
|
><template #extra
|
|
><el-button v-if="!readonly" type="primary" @click="addSheet">添加</el-button></template
|
|
>
|
|
<div v-for="(sheet, sheetIndex) in form.sheets" :key="sheet.localId" class="invoice-sheet">
|
|
<div class="invoice-sheet__header">
|
|
<span>第{{ sheetIndex + 1 }}张</span>
|
|
</div>
|
|
<el-table :data="sheet.lines" border>
|
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
|
<el-table-column label="商品和服务分类" min-width="160">
|
|
<template #default="{ row }">
|
|
<el-select
|
|
v-model="row.goodsCategory"
|
|
:disabled="readonly"
|
|
filterable
|
|
@change="handleGoodsCategoryChange(row)"
|
|
>
|
|
<el-option
|
|
v-for="item in invoiceItemCategories"
|
|
:key="item"
|
|
:label="item"
|
|
:value="item"
|
|
/>
|
|
</el-select>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="货物或服务简称" min-width="170">
|
|
<template #default="{ row }">
|
|
<el-select
|
|
v-model="row.goodsName"
|
|
:disabled="readonly"
|
|
filterable
|
|
@change="handleGoodsNameChange(row)"
|
|
>
|
|
<el-option
|
|
v-for="item in invoiceItemNames(row.goodsCategory)"
|
|
:key="item.shortName"
|
|
:label="item.shortName"
|
|
:value="item.shortName"
|
|
/>
|
|
</el-select>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="单位" min-width="100">
|
|
<template #default="{ row }">
|
|
<el-select v-model="row.unit" :disabled="readonly" filterable allow-create>
|
|
<el-option v-for="item in unitOptions" :key="item" :label="item" :value="item" />
|
|
</el-select>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="数量" min-width="120">
|
|
<template #default="{ row }">
|
|
<el-input-number
|
|
v-model="row.quantity"
|
|
:disabled="readonly"
|
|
:min="0"
|
|
:precision="2"
|
|
:controls="false"
|
|
@change="recalculateLine(row)"
|
|
/>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="不含税单价" min-width="130">
|
|
<template #default="{ row }">
|
|
<el-input-number
|
|
v-model="row.unitPriceNoTax"
|
|
:disabled="readonly"
|
|
:min="0"
|
|
:precision="2"
|
|
:controls="false"
|
|
@change="recalculateLine(row)"
|
|
/>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="不含税金额" min-width="130" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.amountNoTax) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="税率" min-width="110">
|
|
<template #default="{ row }">
|
|
<el-select
|
|
v-model="row.taxRate"
|
|
:disabled="readonly"
|
|
@change="recalculateLine(row)"
|
|
>
|
|
<el-option
|
|
v-for="item in taxRateOptions"
|
|
:key="item"
|
|
:label="`${item}%`"
|
|
:value="item"
|
|
/>
|
|
</el-select>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="税额" min-width="120">
|
|
<template #default="{ row }">{{ formatMoney(row.taxAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="合计" min-width="130" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.totalAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="备注" min-width="170">
|
|
<template #default="{ row }">
|
|
<el-input v-model="row.remark" :disabled="readonly" maxlength="200" />
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column v-if="!readonly" label="操作" width="110" fixed="right" align="center">
|
|
<template #default="{ $index }">
|
|
<div class="invoice-form-page__links">
|
|
<el-link type="primary" @click="addLine(sheet)">添加</el-link>
|
|
<el-link
|
|
v-if="sheet.lines.length > 1"
|
|
type="danger"
|
|
@click="sheet.lines.splice($index, 1)"
|
|
>删除</el-link
|
|
>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<div v-if="!readonly && form.sheets.length > 1" class="invoice-sheet__actions">
|
|
<el-link type="danger" @click="removeSheet(sheetIndex)">删除</el-link>
|
|
</div>
|
|
</div>
|
|
</section-card>
|
|
|
|
<section-card title="结算信息">
|
|
<el-table :data="form.settlements" border>
|
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
|
<el-table-column prop="formalSettlementNo" label="结算单号" min-width="170" />
|
|
<el-table-column label="结算总金额" min-width="140" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.settlementAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="可开票金额" min-width="140" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.availableInvoiceAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="分摊发票金额" min-width="160">
|
|
<template #default="{ row }">
|
|
<el-input-number
|
|
v-model="row.allocatedInvoiceAmount"
|
|
:disabled="readonly"
|
|
:min="0"
|
|
:max="Number(row.availableInvoiceAmount || 0)"
|
|
:precision="2"
|
|
:controls="false"
|
|
/>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</section-card>
|
|
|
|
<section-card title="开票明细">
|
|
<el-table
|
|
ref="detailTable"
|
|
:data="detailRows"
|
|
border
|
|
@selection-change="handleDetailSelection"
|
|
>
|
|
<el-table-column
|
|
v-if="!readonly"
|
|
type="selection"
|
|
width="52"
|
|
fixed="left"
|
|
align="center"
|
|
/>
|
|
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
|
|
<el-table-column
|
|
v-for="column in detailColumns"
|
|
:key="column.prop"
|
|
v-bind="column"
|
|
align="center"
|
|
show-overflow-tooltip
|
|
>
|
|
<template #default="{ row }">
|
|
<el-link
|
|
v-if="column.link && row[column.prop]"
|
|
type="primary"
|
|
@click.stop="handleDetailLink(row, column)"
|
|
>
|
|
{{ row[column.prop] }}
|
|
</el-link>
|
|
<span
|
|
v-else-if="
|
|
['transportQuantity', 'mileage'].includes(column.prop) &&
|
|
Number(row[column.prop]) === -1
|
|
"
|
|
></span>
|
|
<span v-else-if="column.feeItemKeys">{{
|
|
formatMoney(getFeeItemValue(row, column.feeItemKeys))
|
|
}}</span>
|
|
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
|
<span v-else-if="column.prop === 'transportType'">{{
|
|
transportTypeLabel(row.transportType)
|
|
}}</span>
|
|
<span v-else-if="column.dateTime">{{ formatDateTime(row[column.prop]) }}</span>
|
|
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</section-card>
|
|
|
|
<section-card title="附件信息">
|
|
<vehicle-attachment-table v-model="form.attachments" :readonly="readonly" />
|
|
</section-card>
|
|
|
|
<section-card title="备注">
|
|
<el-form-item class="invoice-form-page__remark">
|
|
<el-input
|
|
v-model="form.remark"
|
|
:disabled="readonly"
|
|
type="textarea"
|
|
:rows="2"
|
|
maxlength="200"
|
|
show-word-limit
|
|
/>
|
|
</el-form-item>
|
|
</section-card>
|
|
|
|
<div class="invoice-form-page__actions">
|
|
<el-button @click="goBack">取消</el-button>
|
|
<el-button v-if="!readonly" type="primary" plain @click="syncReferenceData">同步</el-button>
|
|
<el-button v-if="!readonly && canSave" type="primary" plain @click="saveDraft"
|
|
>保存</el-button
|
|
>
|
|
<el-button
|
|
v-if="!readonly && canSave && hasPermission('invoice_application_submit')"
|
|
type="primary"
|
|
@click="submitForm"
|
|
>提交</el-button
|
|
>
|
|
</div>
|
|
</el-form>
|
|
|
|
<el-dialog
|
|
v-model="settlementDialog.visible"
|
|
title="选择应收正式结算单"
|
|
width="86%"
|
|
append-to-body
|
|
>
|
|
<div class="invoice-form-page__dialog-search">
|
|
<el-input
|
|
v-model="settlementDialog.keyword"
|
|
clearable
|
|
placeholder="结算单号、项目或合同"
|
|
@keyup.enter="handleSettlementSearch"
|
|
/>
|
|
<el-button type="primary" @click="handleSettlementSearch">查询</el-button>
|
|
</div>
|
|
<el-table
|
|
ref="settlementTable"
|
|
v-loading="settlementDialog.loading"
|
|
:data="settlementDialog.rows"
|
|
row-key="id"
|
|
border
|
|
@selection-change="settlementDialog.selection = $event"
|
|
>
|
|
<el-table-column type="selection" width="52" align="center" reserve-selection />
|
|
<el-table-column prop="formalSettlementNo" label="结算单号" min-width="170" />
|
|
<el-table-column prop="projectName" label="所属项目" min-width="150" />
|
|
<el-table-column prop="deptName" label="所属组织" min-width="150" />
|
|
<el-table-column prop="contractNo" label="合同编号" min-width="150" />
|
|
<el-table-column prop="contractName" label="合同名称" min-width="170" />
|
|
<el-table-column prop="issuerName" label="开票方" min-width="150" />
|
|
<el-table-column prop="receiverName" label="受票方" min-width="150" />
|
|
<el-table-column label="结算金额" min-width="130" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.settlementAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="可开票金额" min-width="140" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.availableInvoiceAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="100" fixed="right" align="center">
|
|
<template #default="{ row }">
|
|
<el-link type="primary" @click.stop="selectSettlement(row)">选择</el-link>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<div class="invoice-form-page__dialog-pagination">
|
|
<el-pagination
|
|
v-model:current-page="settlementDialog.page.current"
|
|
v-model:page-size="settlementDialog.page.size"
|
|
:total="settlementDialog.page.total"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
@current-change="loadSettlementCandidates"
|
|
@size-change="handleSettlementSizeChange"
|
|
/>
|
|
</div>
|
|
<template #footer>
|
|
<el-button @click="settlementDialog.visible = false">取消</el-button>
|
|
<el-button type="primary" @click="confirmSettlements">确定</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</basic-container>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapGetters } from 'vuex';
|
|
import * as api from '@/api/payment/invoiceApplication';
|
|
import { getAll as getInvoiceItems } from '@/api/base/invoice-item';
|
|
import { getList as getWaybillList } from '@/api/business/waybill-manage';
|
|
import { getList as getReceivablePayableDetailList } from '@/api/settlement/receivable-payable-detail';
|
|
import { getDictionary } from '@/api/system/dictbiz';
|
|
import { invoiceApplicationDetailColumns } from '@/option/payment/invoiceApplication';
|
|
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
|
|
|
|
let localId = 0;
|
|
const emptyLine = () => ({
|
|
localId: ++localId,
|
|
goodsCategory: '',
|
|
goodsName: '',
|
|
unit: '吨',
|
|
quantity: 0,
|
|
unitPriceNoTax: 0,
|
|
amountNoTax: 0,
|
|
amountWithTax: 0,
|
|
totalAmount: 0,
|
|
taxRate: 0,
|
|
taxAmount: 0,
|
|
remark: '',
|
|
});
|
|
const emptySheet = () => ({ localId: ++localId, lines: [emptyLine()] });
|
|
const emptyForm = () => ({
|
|
id: null,
|
|
applicationNo: '',
|
|
projectName: '',
|
|
deptName: '',
|
|
issuerName: '',
|
|
invoiceType: 'electronic_special',
|
|
applicationDate: '',
|
|
applicantName: '',
|
|
undertakingDeptName: '',
|
|
departmentEmails: [],
|
|
receiverInvoiceInfoId: null,
|
|
receiverName: '',
|
|
taxpayerNo: '',
|
|
bankName: '',
|
|
bankAccount: '',
|
|
registeredAddress: '',
|
|
contactName: '',
|
|
contactPhone: '',
|
|
email: '',
|
|
settlements: [],
|
|
sheets: [emptySheet()],
|
|
attachments: [],
|
|
remark: '',
|
|
});
|
|
|
|
export default {
|
|
name: 'InvoiceApplicationForm',
|
|
components: {
|
|
VehicleAttachmentTable,
|
|
},
|
|
data() {
|
|
return {
|
|
form: emptyForm(),
|
|
detailRows: [],
|
|
selectedDetailIds: [],
|
|
receiverInvoiceOptions: [],
|
|
contactOptions: [],
|
|
departmentEmailOptions: [],
|
|
invoiceItems: [],
|
|
transportTypeOptions: [],
|
|
invoiceTypeOptions: api.invoiceTypeOptions,
|
|
detailBaseColumns: invoiceApplicationDetailColumns.filter(
|
|
column => !['feeItemsJson', 'freightAmount'].includes(column.prop)
|
|
),
|
|
feeItemColumns: [],
|
|
unitOptions: ['吨', '车', '次', '项'],
|
|
settlementDialog: {
|
|
visible: false,
|
|
loading: false,
|
|
keyword: '',
|
|
rows: [],
|
|
selection: [],
|
|
page: { current: 1, size: 10, total: 0 },
|
|
},
|
|
rules: {
|
|
settlementIds: [{ validator: this.validateSettlements, trigger: 'change' }],
|
|
invoiceType: [{ required: true, message: '请选择发票类型', trigger: 'change' }],
|
|
departmentEmails: [{ validator: this.validateDepartmentEmails, trigger: 'change' }],
|
|
receiverInvoiceInfoId: [{ required: true, message: '请选择受票方单位', trigger: 'change' }],
|
|
contactPhone: [{ pattern: /^\d{11}$/, message: '联系电话必须为11位数字', trigger: 'blur' }],
|
|
taxpayerNo: [{ validator: this.validateInvoiceInfoField, trigger: 'change' }],
|
|
bankAccount: [{ validator: this.validateInvoiceInfoField, trigger: 'change' }],
|
|
bankName: [{ validator: this.validateInvoiceInfoField, trigger: 'change' }],
|
|
registeredAddress: [{ validator: this.validateInvoiceInfoField, trigger: 'change' }],
|
|
email: [{ validator: this.validateReceiverEmails, trigger: 'blur' }],
|
|
},
|
|
};
|
|
},
|
|
computed: {
|
|
...mapGetters(['permission']),
|
|
recordId() {
|
|
return this.$route.query.id || '';
|
|
},
|
|
readonly() {
|
|
return this.$route.query.mode === 'view';
|
|
},
|
|
canSave() {
|
|
const code = this.recordId ? 'invoice_application_edit' : 'invoice_application_add';
|
|
return this.hasPermission(code);
|
|
},
|
|
settlementLabel() {
|
|
return this.form.settlements
|
|
.map(item => item.formalSettlementNo)
|
|
.filter(Boolean)
|
|
.join(',');
|
|
},
|
|
settlementIds() {
|
|
return this.form.settlements.map(item => item.formalSettlementId || item.settlementId);
|
|
},
|
|
availableInvoiceAmount() {
|
|
return this.form.settlements.reduce(
|
|
(total, item) => total + Number(item.availableInvoiceAmount || 0),
|
|
0
|
|
);
|
|
},
|
|
invoiceAmount() {
|
|
return this.form.sheets.reduce(
|
|
(sheetTotal, sheet) =>
|
|
sheetTotal +
|
|
sheet.lines.reduce((lineTotal, line) => lineTotal + Number(line.totalAmount || 0), 0),
|
|
0
|
|
);
|
|
},
|
|
detailColumns() {
|
|
return [...this.detailBaseColumns, ...this.feeItemColumns];
|
|
},
|
|
invoiceItemCategories() {
|
|
return [...new Set(this.invoiceItems.map(item => item.categoryName).filter(Boolean))];
|
|
},
|
|
taxRateOptions() {
|
|
const rates = new Set([0, 1, 3, 6, 9, 13]);
|
|
this.invoiceItems.forEach(item => {
|
|
const rate = Number(item.defaultTaxRate);
|
|
if (Number.isFinite(rate) && rate >= 0 && rate <= 100) rates.add(rate);
|
|
});
|
|
return [...rates].sort((left, right) => left - right);
|
|
},
|
|
},
|
|
created() {
|
|
this.initialize();
|
|
},
|
|
methods: {
|
|
hasPermission(code) {
|
|
return this.permission?.[code] !== false;
|
|
},
|
|
unwrapData(response) {
|
|
const body = response?.data || response || {};
|
|
return body?.data || body;
|
|
},
|
|
parse(value) {
|
|
if (!value) return [];
|
|
if (Array.isArray(value)) return value;
|
|
try {
|
|
return JSON.parse(value) || [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
},
|
|
parseEmails(value) {
|
|
const values = Array.isArray(value) ? value : [value];
|
|
return values
|
|
.flatMap(item => String(item || '').split(/[;,,;]/))
|
|
.map(item => item.trim())
|
|
.filter(Boolean);
|
|
},
|
|
async initialize() {
|
|
await Promise.all([this.loadInvoiceItems(), this.loadTransportTypeOptions()]);
|
|
const userInfo = this.$store.getters.userInfo || {};
|
|
if (!this.recordId) {
|
|
this.form.applicantName = userInfo.realName || userInfo.userName || '';
|
|
return;
|
|
}
|
|
const data = this.unwrapData(await api.getDetail(this.recordId));
|
|
this.form = {
|
|
...emptyForm(),
|
|
...data,
|
|
departmentEmails: this.parseEmails(data.departmentEmails),
|
|
settlements: (data.settlements || []).map(item => ({
|
|
...item,
|
|
settlementId: item.formalSettlementId,
|
|
})),
|
|
sheets: (data.sheets || []).map(item => ({
|
|
...item,
|
|
localId: ++localId,
|
|
lines: (item.lines || []).map(line => ({ ...line, localId: ++localId })),
|
|
})),
|
|
attachments: this.parse(data.attachmentsJson),
|
|
};
|
|
if (!this.form.sheets.length) this.form.sheets = [emptySheet()];
|
|
this.form.sheets.forEach(sheet =>
|
|
sheet.lines.forEach(line => this.syncInvoiceItemLine(line, false))
|
|
);
|
|
this.selectedDetailIds = (data.details || []).map(item => item.formalSettlementDetailId);
|
|
if (this.settlementIds.length) {
|
|
await Promise.all([this.loadReceiverInformation(), this.loadSettlementDetails(false)]);
|
|
}
|
|
if (this.readonly)
|
|
this.$nextTick(() => this.$refs.formRef?.clearValidate('departmentEmails'));
|
|
this.$nextTick(this.restoreDetailSelection);
|
|
},
|
|
async loadInvoiceItems() {
|
|
const data = this.unwrapData(await getInvoiceItems());
|
|
this.invoiceItems = Array.isArray(data) ? data : data?.records || [];
|
|
this.form.sheets.forEach(sheet =>
|
|
sheet.lines.forEach(line => this.syncInvoiceItemLine(line, false))
|
|
);
|
|
},
|
|
async loadTransportTypeOptions() {
|
|
const data = this.unwrapData(await getDictionary({ code: 'transport_type' }));
|
|
const records = Array.isArray(data) ? data : data?.records || [];
|
|
this.transportTypeOptions = records.map(item => ({
|
|
label: item.dictValue || item.label || item.name,
|
|
value: item.dictKey || item.value || item.dictValue || item.name,
|
|
}));
|
|
},
|
|
transportTypeLabel(value) {
|
|
if (!value) return '-';
|
|
return (
|
|
this.transportTypeOptions.find(item => String(item.value) === String(value))?.label || value
|
|
);
|
|
},
|
|
invoiceItemNames(categoryName) {
|
|
return this.invoiceItems.filter(item => item.categoryName === categoryName);
|
|
},
|
|
findInvoiceItem(line) {
|
|
return this.invoiceItems.find(
|
|
item => item.categoryName === line.goodsCategory && item.shortName === line.goodsName
|
|
);
|
|
},
|
|
syncInvoiceItemLine(line, clearInvalid = true) {
|
|
const categoryExists = this.invoiceItemCategories.includes(line.goodsCategory);
|
|
if (!categoryExists && clearInvalid) line.goodsCategory = '';
|
|
const item = this.findInvoiceItem(line);
|
|
if (item) {
|
|
line.taxRate = Number(item.defaultTaxRate || 0);
|
|
} else if (clearInvalid) {
|
|
line.goodsName = '';
|
|
line.taxRate = 0;
|
|
}
|
|
this.recalculateLine(line);
|
|
},
|
|
handleGoodsCategoryChange(line) {
|
|
const names = this.invoiceItemNames(line.goodsCategory);
|
|
if (!names.some(item => item.shortName === line.goodsName)) line.goodsName = '';
|
|
line.taxRate = 0;
|
|
if (names.length === 1) {
|
|
line.goodsName = names[0].shortName;
|
|
this.handleGoodsNameChange(line);
|
|
}
|
|
},
|
|
handleGoodsNameChange(line) {
|
|
const item = this.findInvoiceItem(line);
|
|
if (!item) {
|
|
line.taxRate = 0;
|
|
return;
|
|
}
|
|
line.taxRate = Number(item.defaultTaxRate || 0);
|
|
this.recalculateLine(line);
|
|
},
|
|
validateSettlements(rule, value, callback) {
|
|
if (!this.form.settlements.length) {
|
|
callback(new Error('请选择正式结算单'));
|
|
return;
|
|
}
|
|
callback();
|
|
},
|
|
validateInvoiceInfoField(rule, value, callback) {
|
|
const labels = {
|
|
taxpayerNo: '纳税人识别号',
|
|
bankAccount: '开户账号',
|
|
bankName: '开户行',
|
|
registeredAddress: '注册地址',
|
|
};
|
|
if (this.form.invoiceType === 'electronic_special' && !value) {
|
|
callback(new Error(`电子专票的${labels[rule.field]}不能为空`));
|
|
return;
|
|
}
|
|
callback();
|
|
},
|
|
validateReceiverEmails(rule, value, callback) {
|
|
if (!value) {
|
|
callback();
|
|
return;
|
|
}
|
|
const emails = String(value)
|
|
.split(/[;,,;]/)
|
|
.map(item => item.trim())
|
|
.filter(Boolean);
|
|
const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (emails.length > 3) {
|
|
callback(new Error('邮箱最多填写3个'));
|
|
return;
|
|
}
|
|
if (emails.some(email => !valid.test(email))) {
|
|
callback(new Error('请输入正确的邮箱,多个邮箱使用分号分隔'));
|
|
return;
|
|
}
|
|
callback();
|
|
},
|
|
validateDepartmentEmails(rule, value, callback) {
|
|
if (this.readonly) {
|
|
callback();
|
|
return;
|
|
}
|
|
const emails = this.parseEmails(value);
|
|
if (!emails.length) {
|
|
callback(new Error('请选择部门邮箱'));
|
|
return;
|
|
}
|
|
if (emails.length > 3) {
|
|
callback(new Error('部门邮箱最多选择3个'));
|
|
return;
|
|
}
|
|
const options = new Set(this.departmentEmailOptions.map(item => item.toLowerCase()));
|
|
if (emails.some(email => !options.has(email.toLowerCase()))) {
|
|
callback(new Error('部门邮箱必须选择当前客商开票信息中的邮箱'));
|
|
return;
|
|
}
|
|
callback();
|
|
},
|
|
handleInvoiceTypeChange() {
|
|
this.$nextTick(() => {
|
|
this.$refs.formRef?.validateField([
|
|
'taxpayerNo',
|
|
'bankAccount',
|
|
'bankName',
|
|
'registeredAddress',
|
|
]);
|
|
});
|
|
},
|
|
async openSettlementDialog() {
|
|
this.settlementDialog.visible = true;
|
|
this.settlementDialog.page.current = 1;
|
|
this.settlementDialog.selection = [];
|
|
this.$nextTick(() => this.$refs.settlementTable?.clearSelection());
|
|
await this.loadSettlementCandidates();
|
|
},
|
|
async loadSettlementCandidates() {
|
|
this.settlementDialog.loading = true;
|
|
try {
|
|
const data = this.unwrapData(
|
|
await api.getSettlementCandidates(
|
|
this.settlementDialog.page.current,
|
|
this.settlementDialog.page.size,
|
|
{
|
|
keyword: this.settlementDialog.keyword,
|
|
contractCategory: '客户合同',
|
|
settlementType: 'receivable',
|
|
invoiceStatus: 'unreceived',
|
|
}
|
|
)
|
|
);
|
|
const rows = Array.isArray(data) ? data : data?.records || [];
|
|
this.settlementDialog.rows = rows;
|
|
this.settlementDialog.page.total = Number(
|
|
Array.isArray(data) ? data.length : data?.total || 0
|
|
);
|
|
} finally {
|
|
this.settlementDialog.loading = false;
|
|
}
|
|
},
|
|
handleSettlementSearch() {
|
|
this.settlementDialog.page.current = 1;
|
|
this.settlementDialog.selection = [];
|
|
this.$refs.settlementTable?.clearSelection();
|
|
this.loadSettlementCandidates();
|
|
},
|
|
handleSettlementSizeChange() {
|
|
this.settlementDialog.page.current = 1;
|
|
this.loadSettlementCandidates();
|
|
},
|
|
async confirmSettlements() {
|
|
const rows = this.settlementDialog.selection;
|
|
if (!rows.length) {
|
|
this.$message.warning('请至少选择一张应收正式结算单');
|
|
return;
|
|
}
|
|
await this.applySettlements(rows);
|
|
},
|
|
async selectSettlement(row) {
|
|
await this.applySettlements([row]);
|
|
},
|
|
async applySettlements(rows) {
|
|
const first = rows[0];
|
|
const incompatible = rows.some(
|
|
item =>
|
|
String(item.contractId) !== String(first.contractId) ||
|
|
String(item.projectId) !== String(first.projectId) ||
|
|
String(item.deptId) !== String(first.deptId) ||
|
|
item.issuerName !== first.issuerName ||
|
|
item.receiverName !== first.receiverName
|
|
);
|
|
if (incompatible) {
|
|
this.$message.warning('合并开票的结算单必须属于同一合同、项目、组织及收付款方');
|
|
return;
|
|
}
|
|
this.form.settlements = rows.map(item => ({
|
|
...item,
|
|
formalSettlementId: item.id,
|
|
settlementId: item.id,
|
|
allocatedInvoiceAmount: rows.length === 1 ? Number(item.availableInvoiceAmount || 0) : 0,
|
|
}));
|
|
this.form.projectName = first.projectName;
|
|
this.form.deptName = first.deptName;
|
|
this.form.issuerName = first.issuerName;
|
|
this.form.undertakingDeptName = first.deptName;
|
|
this.settlementDialog.visible = false;
|
|
await Promise.all([this.loadSettlementDetails(), this.loadReceiverInformation()]);
|
|
},
|
|
async loadSettlementDetails(resetSelection = true) {
|
|
this.detailRows =
|
|
this.unwrapData(await api.getSettlementDetails(this.settlementIds.join(','))) || [];
|
|
this.buildFeeItemColumns();
|
|
if (resetSelection) this.selectedDetailIds = this.detailRows.map(item => item.id);
|
|
this.$nextTick(this.restoreDetailSelection);
|
|
},
|
|
parseFeeItems(value) {
|
|
if (!value) return {};
|
|
if (typeof value === 'object' && !Array.isArray(value)) return value;
|
|
try {
|
|
const parsed = JSON.parse(value);
|
|
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
} catch {
|
|
return {};
|
|
}
|
|
},
|
|
buildFeeItemColumns() {
|
|
const groupedNames = new Map();
|
|
this.detailRows.forEach(row => {
|
|
Object.keys(this.parseFeeItems(row.feeItemsJson || row.feeItems)).forEach(name => {
|
|
const normalizedName = this.normalizeFeeItemName(name);
|
|
if (!normalizedName) return;
|
|
const key = this.feeItemNameKey(name);
|
|
const group = groupedNames.get(key) || { label: normalizedName, keys: [] };
|
|
if (!group.keys.includes(name)) group.keys.push(name);
|
|
groupedNames.set(key, group);
|
|
});
|
|
});
|
|
this.feeItemColumns = [...groupedNames.values()].map((group, index) => ({
|
|
prop: `feeItem_${index}`,
|
|
label: group.label,
|
|
minWidth: 130,
|
|
money: true,
|
|
feeItemKeys: group.keys,
|
|
}));
|
|
},
|
|
normalizeFeeItemName(name) {
|
|
return String(name || '')
|
|
.replace(/[\s\u200b-\u200d\ufeff]/g, '')
|
|
.trim();
|
|
},
|
|
feeItemNameKey(name) {
|
|
return this.normalizeFeeItemName(name).toLowerCase();
|
|
},
|
|
getFeeItemValue(row, names) {
|
|
const items = this.parseFeeItems(row.feeItemsJson || row.feeItems);
|
|
return names.reduce((total, name) => total + Number(items[name] || 0), 0);
|
|
},
|
|
async loadReceiverInformation() {
|
|
const data = this.unwrapData(await api.getReceiverInformation(this.settlementIds.join(',')));
|
|
const invoiceInfos = data.invoices || data.invoiceInfos || data.customer?.invoices || [];
|
|
this.receiverInvoiceOptions = Array.isArray(invoiceInfos) ? invoiceInfos : [];
|
|
this.contactOptions = data.contacts || [];
|
|
const invoiceInfoEmails = [
|
|
...this.receiverInvoiceOptions.flatMap(item => this.parseEmails(item.email)),
|
|
];
|
|
this.departmentEmailOptions = invoiceInfoEmails.filter(Boolean).reduce((emails, email) => {
|
|
if (!emails.some(item => item.toLowerCase() === email.toLowerCase())) emails.push(email);
|
|
return emails;
|
|
}, []);
|
|
this.form.departmentEmails = this.form.departmentEmails
|
|
.filter(email =>
|
|
this.departmentEmailOptions.some(option => option.toLowerCase() === email.toLowerCase())
|
|
)
|
|
.slice(0, 3);
|
|
this.form.issuerName = data.issuerName || this.form.issuerName;
|
|
let invoiceInfo = this.receiverInvoiceOptions.find(
|
|
item => String(item.id) === String(this.form.receiverInvoiceInfoId)
|
|
);
|
|
if (!invoiceInfo) {
|
|
invoiceInfo =
|
|
this.receiverInvoiceOptions.find(item => item.isDefault === 1) ||
|
|
this.receiverInvoiceOptions[0];
|
|
}
|
|
if (invoiceInfo) {
|
|
this.form.receiverInvoiceInfoId = invoiceInfo.id;
|
|
this.handleReceiverChange(invoiceInfo.id);
|
|
}
|
|
if (!this.form.contactName) {
|
|
const contact =
|
|
this.contactOptions.find(item => item.isDefault === 1) || this.contactOptions[0];
|
|
if (contact) {
|
|
this.form.contactName = contact.contactName;
|
|
this.handleContactChange(contact.contactName);
|
|
}
|
|
}
|
|
},
|
|
handleReceiverChange(id) {
|
|
const info = this.receiverInvoiceOptions.find(item => String(item.id) === String(id));
|
|
if (!info) return;
|
|
Object.assign(this.form, {
|
|
receiverName: info.invoiceTitle,
|
|
taxpayerNo: info.taxNo,
|
|
bankName: info.bankName,
|
|
bankAccount: info.bankAccount,
|
|
registeredAddress: info.registeredAddress,
|
|
email: info.email || '',
|
|
});
|
|
this.handleInvoiceTypeChange();
|
|
},
|
|
handleContactChange(name) {
|
|
const contact = this.contactOptions.find(item => item.contactName === name);
|
|
if (!contact) return;
|
|
this.form.contactPhone = contact.contactPhone || '';
|
|
this.form.email = contact.email || this.form.email || '';
|
|
},
|
|
handleDetailSelection(rows) {
|
|
if (this.readonly) return;
|
|
this.selectedDetailIds = rows.map(item => item.formalSettlementDetailId || item.id);
|
|
},
|
|
handleDetailLink(row, column) {
|
|
if (column.prop === 'documentNo') {
|
|
this.openReceivableDetail(row);
|
|
return;
|
|
}
|
|
if (column.prop === 'waybillNo') this.openWaybillDetail(row);
|
|
},
|
|
async openReceivableDetail(row) {
|
|
let detailId = row.sourceDetailId || '';
|
|
if (!detailId && row.documentNo) {
|
|
try {
|
|
const data = this.unwrapData(
|
|
await getReceivablePayableDetailList(1, 1, {
|
|
documentNo: row.documentNo,
|
|
settlementType: 'receivable',
|
|
})
|
|
);
|
|
detailId = (Array.isArray(data) ? data : data?.records || [])[0]?.id || '';
|
|
} catch {
|
|
detailId = '';
|
|
}
|
|
}
|
|
if (!detailId) {
|
|
this.$message.info('当前记录未找到对应的应收明细');
|
|
return;
|
|
}
|
|
this.$router.push({
|
|
path: '/settlement/receivable-detail',
|
|
query: { detailId },
|
|
});
|
|
},
|
|
async openWaybillDetail(row) {
|
|
let waybillId = row.waybillId || '';
|
|
if (!waybillId && row.waybillNo) {
|
|
try {
|
|
const data = this.unwrapData(await getWaybillList(1, 1, { waybillNo: row.waybillNo }));
|
|
waybillId = (Array.isArray(data) ? data : data?.records || [])[0]?.id || '';
|
|
} catch {
|
|
waybillId = '';
|
|
}
|
|
}
|
|
if (!waybillId) {
|
|
this.$message.info('当前记录未找到对应的运单详情');
|
|
return;
|
|
}
|
|
this.$router.push({
|
|
path: '/business/waybill-manage',
|
|
query: { detailId: waybillId },
|
|
});
|
|
},
|
|
restoreDetailSelection() {
|
|
if (this.readonly || !this.$refs.detailTable) return;
|
|
const selected = new Set(this.selectedDetailIds.map(String));
|
|
this.detailRows.forEach(row => {
|
|
const id = row.formalSettlementDetailId || row.id;
|
|
this.$refs.detailTable.toggleRowSelection(row, selected.has(String(id)));
|
|
});
|
|
},
|
|
addSheet() {
|
|
this.form.sheets.push(emptySheet());
|
|
},
|
|
removeSheet(index) {
|
|
this.form.sheets.splice(index, 1);
|
|
},
|
|
addLine(sheet) {
|
|
sheet.lines.push(emptyLine());
|
|
},
|
|
recalculateLine(row) {
|
|
const quantity = Number(row.quantity || 0);
|
|
const unitPrice = Number(row.unitPriceNoTax || 0);
|
|
const amountNoTax = Number((quantity * unitPrice).toFixed(2));
|
|
const rate = Number(row.taxRate || 0) / 100;
|
|
row.amountNoTax = amountNoTax;
|
|
row.taxAmount = rate ? Number((amountNoTax * rate).toFixed(2)) : 0;
|
|
row.totalAmount = Number((amountNoTax + row.taxAmount).toFixed(2));
|
|
row.amountWithTax = row.totalAmount;
|
|
},
|
|
validateBusiness() {
|
|
if (!this.selectedDetailIds.length) throw new Error('请至少选择一条开票明细');
|
|
if (this.invoiceAmount <= 0) throw new Error('本次开票金额必须大于0');
|
|
for (const sheet of this.form.sheets) {
|
|
if (!sheet.lines.length) throw new Error('每张发票至少需要一条商品行');
|
|
const hasFreight = sheet.lines.some(line => line.goodsName === '运费');
|
|
const hasOther = sheet.lines.some(line => line.goodsName !== '运费');
|
|
if (hasFreight && hasOther) throw new Error('运费不能与其他费用合并开在同一张发票中');
|
|
for (const line of sheet.lines) {
|
|
if (!line.goodsCategory || !line.goodsName) throw new Error('请完整填写商品和服务信息');
|
|
if (
|
|
[line.quantity, line.unitPriceNoTax, line.amountNoTax, line.taxRate].some(
|
|
Number.isNaN
|
|
)
|
|
) {
|
|
throw new Error('开票商品行金额格式不正确');
|
|
}
|
|
}
|
|
}
|
|
},
|
|
validateAvailableInvoiceAmount() {
|
|
const availableAmountCents = Math.round(Number(this.availableInvoiceAmount || 0) * 100);
|
|
const invoiceAmountCents = Math.round(Number(this.invoiceAmount || 0) * 100);
|
|
if (invoiceAmountCents > availableAmountCents) {
|
|
throw new Error('本次开票金额不能超过可开票金额');
|
|
}
|
|
},
|
|
payload() {
|
|
return {
|
|
id: this.form.id,
|
|
invoiceType: this.form.invoiceType,
|
|
departmentEmails: this.form.departmentEmails.join(';'),
|
|
receiverInvoiceInfoId: this.form.receiverInvoiceInfoId,
|
|
contactName: this.form.contactName,
|
|
contactPhone: this.form.contactPhone,
|
|
email: this.form.email,
|
|
attachmentsJson: JSON.stringify(this.form.attachments || []),
|
|
remark: this.form.remark,
|
|
settlements: this.form.settlements.map(item => ({
|
|
settlementId: item.formalSettlementId || item.settlementId,
|
|
allocatedInvoiceAmount: item.allocatedInvoiceAmount,
|
|
})),
|
|
detailIds: this.selectedDetailIds,
|
|
sheets: this.form.sheets.map(sheet => ({
|
|
lines: sheet.lines.map(line => ({
|
|
goodsCategory: line.goodsCategory,
|
|
goodsName: line.goodsName,
|
|
unit: line.unit,
|
|
quantity: line.quantity,
|
|
unitPriceNoTax: line.unitPriceNoTax,
|
|
amountNoTax: line.amountNoTax,
|
|
totalAmount: line.totalAmount,
|
|
amountWithTax: line.totalAmount,
|
|
taxRate: line.taxRate,
|
|
taxAmount: line.taxAmount,
|
|
remark: line.remark,
|
|
})),
|
|
})),
|
|
};
|
|
},
|
|
async saveDraft() {
|
|
await this.$refs.formRef.validate();
|
|
try {
|
|
this.validateBusiness();
|
|
} catch (error) {
|
|
this.$message.warning(error.message);
|
|
return false;
|
|
}
|
|
const id = this.unwrapData(await api.save(this.payload()));
|
|
this.form.id = id;
|
|
this.$message.success('保存成功');
|
|
return true;
|
|
},
|
|
async submitForm() {
|
|
try {
|
|
this.validateAvailableInvoiceAmount();
|
|
} catch (error) {
|
|
this.$message.warning(error.message);
|
|
return;
|
|
}
|
|
const saved = await this.saveDraft();
|
|
if (!saved) return;
|
|
await api.submit({ id: this.form.id });
|
|
this.$message.success('提交成功');
|
|
this.goBack();
|
|
},
|
|
async syncReferenceData() {
|
|
if (!this.settlementIds.length) {
|
|
this.$message.warning('请先选择正式结算单');
|
|
return;
|
|
}
|
|
await Promise.all([this.loadReceiverInformation(), this.loadSettlementDetails(false)]);
|
|
this.$message.success('关联信息已同步');
|
|
},
|
|
goBack() {
|
|
this.$router.push('/payment/invoice-application');
|
|
},
|
|
displayValue(value) {
|
|
return value === null || value === undefined || value === '' ? '-' : value;
|
|
},
|
|
formatDateTime(value) {
|
|
if (!value) return '-';
|
|
return this.$dayjs(value).format('YYYY-MM-DD HH:mm:ss');
|
|
},
|
|
formatMoney(value) {
|
|
return Number(value || 0).toFixed(2);
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.invoice-form-page__form {
|
|
padding-bottom: 72px;
|
|
}
|
|
.invoice-form-page__form :deep(.el-select),
|
|
.invoice-form-page__form :deep(.el-input-number) {
|
|
width: 100%;
|
|
}
|
|
.invoice-sheet {
|
|
margin-top: 16px;
|
|
}
|
|
.invoice-sheet__header {
|
|
min-height: 42px;
|
|
padding: 0 12px;
|
|
border: 1px solid #eff1f7;
|
|
border-bottom: 0;
|
|
background: #fafafa;
|
|
font-weight: 600;
|
|
}
|
|
.invoice-sheet__actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
padding: 8px 12px 0;
|
|
}
|
|
.invoice-form-page__links {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
}
|
|
.invoice-form-page__remark :deep(.el-form-item__content) {
|
|
margin-left: 0 !important;
|
|
}
|
|
.invoice-form-page__actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
padding: 12px 24px;
|
|
position: fixed;
|
|
right: 0;
|
|
left: 230px;
|
|
bottom: 0;
|
|
margin: 0;
|
|
z-index: 10;
|
|
background: #fff;
|
|
border-top: 1px solid #eff1f7;
|
|
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
|
|
}
|
|
:global(.avue--collapse .invoice-form-page__actions) {
|
|
left: 60px;
|
|
}
|
|
:global(.avue-layout--horizontal .invoice-form-page__actions) {
|
|
left: 0;
|
|
}
|
|
.invoice-form-page__dialog-search {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
margin-bottom: 12px;
|
|
}
|
|
.invoice-form-page__dialog-search .el-input {
|
|
width: 360px;
|
|
}
|
|
.invoice-form-page__dialog-pagination {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
margin-top: 12px;
|
|
}
|
|
.invoice-form-page :deep(.el-table) {
|
|
--el-table-border-color: #eff1f7;
|
|
}
|
|
.invoice-form-page :deep(.el-table__row:nth-child(even) > td.el-table__cell) {
|
|
background: #fafafa;
|
|
}
|
|
@media (max-width: 1200px) {
|
|
.invoice-form-page__form :deep(.el-col-6) {
|
|
max-width: 50%;
|
|
flex: 0 0 50%;
|
|
}
|
|
}
|
|
@media (max-width: 768px) {
|
|
.invoice-form-page__form :deep(.el-col-6) {
|
|
max-width: 100%;
|
|
flex: 0 0 100%;
|
|
}
|
|
}
|
|
</style>
|