| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { FinanceReimbursementAiApi } from '#/api/finance/reimbursement/ai'; |
| | | import type { FinanceReimbursementApi } from '#/api/finance/reimbursement'; |
| | | import type { SystemStorageApi } from '#/api/system/storage'; |
| | | |
| | | import type { DefaultOptionType } from 'ant-design-vue/es/select'; |
| | | |
| | | import { nextTick, watch } from 'vue'; |
| | | import { nextTick, ref, watch } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | import { erpPriceInputFormatter } from '@vben/utils'; |
| | | |
| | | import { Button, DatePicker, Input, InputNumber, Select } from 'ant-design-vue'; |
| | | import { |
| | | Button, |
| | | DatePicker, |
| | | Input, |
| | | InputNumber, |
| | | Select, |
| | | message, |
| | | } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { toSubmitItems, useItemColumns } from '../data'; |
| | | import { |
| | | INVOICE_TYPE_OPTIONS, |
| | | toSubmitItems, |
| | | useItemColumns, |
| | | } from '../data'; |
| | | import OcrUpload from './ocr-upload.vue'; |
| | | |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | |
| | | }, |
| | | ); |
| | | |
| | | const emit = defineEmits(['update:items', 'update:totalAmount']); |
| | | const emit = defineEmits<{ |
| | | 'ocr-blobs': [blobs: SystemStorageApi.StorageBlob[]]; |
| | | 'update:items': [items: FinanceReimbursementApi.ReimbursementItem[]]; |
| | | 'update:totalAmount': [totalAmount: number]; |
| | | }>(); |
| | | |
| | | const ocrUploadRef = ref<InstanceType<typeof OcrUpload>>(); |
| | | |
| | | const expenseTypeOptions = getDictOptions( |
| | | DICT_TYPE.FINANCE_EXPENSE_TYPE, |
| | |
| | | recalc(); |
| | | } |
| | | |
| | | /** OCR 识别结果:追加为明细行,并把发票文件同步给主表附件 */ |
| | | async function handleOcrSuccess( |
| | | payload: FinanceReimbursementAiApi.OcrImportPayload, |
| | | ) { |
| | | for (const item of payload.items) { |
| | | await gridApi.grid.insertAt(item, -1); |
| | | } |
| | | recalc(); |
| | | message.success( |
| | | `已填入 ${payload.items.length} 条发票明细,请补充费用类型后提交`, |
| | | ); |
| | | if (payload.blobs.length > 0) { |
| | | emit('ocr-blobs', payload.blobs); |
| | | } |
| | | } |
| | | |
| | | /** 删除明细行 */ |
| | | async function handleDelete(row: FinanceReimbursementApi.ReimbursementItem) { |
| | | await gridApi.grid.remove(row); |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <OcrUpload ref="ocrUploadRef" @success="handleOcrSuccess" /> |
| | | <Grid class="w-full"> |
| | | <template #expenseType="{ row }"> |
| | | <Select |
| | |
| | | :disabled="disabled" |
| | | :options="expenseTypeOptions" |
| | | placeholder="请选择费用类型" |
| | | allow-clear |
| | | @change="() => recalc()" |
| | | /> |
| | | </template> |
| | | <template #invoiceType="{ row }"> |
| | | <Select |
| | | v-model:value="row.invoiceType" |
| | | :disabled="disabled" |
| | | :options="INVOICE_TYPE_OPTIONS" |
| | | placeholder="请选择发票类型" |
| | | allow-clear |
| | | @change="() => recalc()" |
| | | /> |
| | |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | <div v-if="!disabled" class="-mt-4 flex justify-center"> |
| | | <div v-if="!disabled" class="-mt-4 flex justify-center gap-2"> |
| | | <Button @click="ocrUploadRef?.open()">OCR 识别发票</Button> |
| | | <Button type="primary" ghost @click="handleAdd"> 添加报销明细 </Button> |
| | | </div> |
| | | </template> |