Files
geg-gas-web/src/views/erp/purchase/components/OrderInfoModal.vue

666 lines
22 KiB
Vue
Raw Normal View History

<template>
2025-10-21 18:04:02 +08:00
<BasicModal v-bind="$attrs" @register="registerModal" title="查看订单详情" @cancel="handleCancel">
<div class="info-box">
<a-card :bordered="false">
<div class="title-name">{{ baseInfo?.theme }}</div>
<div class="title-info">
<span>创建人{{ baseInfo?.createUserName }}</span>
<span>创建时间{{ baseInfo?.createDate }}</span>
<span>最后修改人{{ baseInfo?.modifyUserName }}</span>
<span>修改时间{{ baseInfo?.modifyDate }}</span>
</div>
<div>
<a-button size="small" class="title-btn" @click="handlePrint">打印</a-button>
</div>
</a-card>
<a-card :bordered="false">
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="订单信息">
<a-row>基础信息</a-row>
<a-row>
<a-col :span="12">采购单号{{ baseInfo?.purchaseNumber }}</a-col>
<a-col :span="12">订单主题{{ baseInfo?.theme }}</a-col>
</a-row>
<a-row>
<a-col :span="12">采购日期{{ baseInfo?.purchaseDate }}</a-col>
<a-col :span="12">供应商名称{{ baseInfo?.supplierName }}</a-col>
</a-row>
<a-row>
<a-col :span="12">联系人{{ baseInfo?.supplierPerson }}</a-col>
<a-col :span="12">联系方式{{ baseInfo?.supplierWay }}</a-col>
</a-row>
<a-row>
<a-col :span="12">采购部门{{ baseInfo?.purchaseDeptName }}</a-col>
<a-col :span="12">采购人员{{ baseInfo?.purchasePersonName }}</a-col>
</a-row>
<a-row>
<a-col :span="12">联系电话{{ baseInfo?.purchasePhone }}</a-col>
<a-col :span="12">关联项目{{ baseInfo?.relatedProjectName }}</a-col>
</a-row>
<a-row> 结算方式{{ baseInfo?.payTypeName }} </a-row>
<a-row> 交货地址{{ baseInfo?.payAddress }} </a-row>
<a-row> 备注{{ baseInfo?.remark }} </a-row>
<BasicTable @register="registerPurchaseTable">
<template #toolbar>
<a-button type="primary" @click="handleExport('purchase')">导出</a-button>
</template>
</BasicTable>
<div class="table-bottom">
<span>合计</span>
<div>
<span>总量{{ baseInfo?.countSum }}</span>
<span> 总金额{{ baseInfo?.amountSum }}</span>
</div>
</div>
<div>
附件
<Upload v-if="baseInfo?.filePath" v-model:value="baseInfo.filePath" listType="dragger" :style="{ width: '200px', display: 'none' }" :showRemoveIcon="false" />
</div>
</a-tab-pane>
<a-tab-pane key="2">
<template #tab>
<span>
入库记录
<Icon icon="ant-design:check-circle-filled" :size="20" v-if="baseInfo?.inStoreState === 1" />
<Icon icon="ant-design:exclamation-circle-filled" color="#FF1A2E" :size="20" v-else />
</span>
</template>
2025-10-21 18:04:02 +08:00
<BasicTable @register="registerInstoreTable">
<template #toolbar>
<a-button type="primary" @click="handleExport('instore')">导出</a-button>
</template>
</BasicTable>
</a-tab-pane>
<a-tab-pane key="3">
<template #tab>
<span>
到票记录
<Icon icon="ant-design:check-circle-filled" :size="20" v-if="baseInfo?.ticketState === 1" />
<Icon icon="ant-design:exclamation-circle-filled" color="#FF1A2E" :size="20" v-else />
</span>
</template>
<BasicTable @register="registerTicketTable">
<template #toolbar>
<a-button type="primary" @click="handleExport('ticket')">导出</a-button>
</template>
</BasicTable>
</a-tab-pane>
<a-tab-pane key="4">
<template #tab>
<span>
付款记录
<Icon icon="ant-design:check-circle-filled" :size="20" v-if="baseInfo?.payState === 1" />
<Icon icon="ant-design:exclamation-circle-filled" color="#FF1A2E" :size="20" v-else />
</span>
</template>
<BasicTable @register="registerPayTable">
<template #toolbar>
<a-button type="primary" @click="handleExport('pay')">导出</a-button>
</template>
</BasicTable>
</a-tab-pane>
<a-tab-pane key="5" tab="审批记录">
<FlowRecord v-if="isReady" :list="workflowList" :processInstanceId="baseInfo.processInstanceId" />
</a-tab-pane>
<a-tab-pane key="6" tab="操作记录">
<BasicTable @register="registerLogTable">
<template #toolbar>
<a-button type="primary" @click="handleExport('log')">导出</a-button>
</template>
</BasicTable>
</a-tab-pane>
</a-tabs>
</a-card>
</div>
</BasicModal>
</template>
<script lang="ts" setup>
2025-10-21 18:04:02 +08:00
import { ref } from 'vue';
import { BasicTable, useTable, BasicColumn, FormSchema } from '/@/components/Table';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { exportPurchaseInfo, exportInstoreInfo, exportTicketInfo, exportPayInfo, exportLogInfo } from '/@/api/erp/purchase/order';
import { getPurchaseCheckInfo } from '/@/api/erp/purchase/order';
import { getRecordList } from '/@/api/erp/purchase/order';
import { getFileList } from '/@/api/system/file';
import { useMessage } from '/@/hooks/web/useMessage';
import { downloadByData } from '/@/utils/file/download';
import FlowRecord from '/@/views/workflow/task/components/flow/FlowRecord.vue';
import Icon from '/@/components/Icon/index';
import Upload from '/@/components/Form/src/components/Upload.vue';
import printJS from 'print-js';
import domtoimage from 'dom-to-image';
import { isNil } from 'lodash-es';
2025-10-21 18:04:02 +08:00
const purchaseColumns: BasicColumn[] = [
{
title: '物料编码',
dataIndex: 'code'
},
{
title: '物料名称',
dataIndex: 'name'
},
{
title: '规格型号',
dataIndex: 'model'
},
{
title: '单位',
dataIndex: 'unitName'
},
{
title: '单价',
dataIndex: 'price'
},
{
title: '采购数量',
dataIndex: 'count'
},
{
title: '折扣',
dataIndex: 'discount'
},
{
title: '税率',
dataIndex: 'taxRate'
},
{
title: '税费',
dataIndex: 'taxBreak'
},
{
title: '税后金额',
dataIndex: 'afterTaxAmount'
},
{
title: '已入库数量',
dataIndex: 'inStoreCount'
},
{
title: '未入库数量',
dataIndex: 'noInStoreCount'
},
{
title: '退货数量',
dataIndex: 'returnCount'
},
{
title: '交付日期',
dataIndex: 'deliveryDate'
}
];
const instoreColumns: BasicColumn[] = [
{
dataIndex: 'code',
title: '入库单号',
align: 'center',
width: 100
},
2025-10-21 18:04:02 +08:00
{
dataIndex: 'theme',
title: '入库订单主题',
align: 'center',
width: 100
},
{
dataIndex: 'date',
title: '入库日期',
align: 'center',
width: 100
},
{
dataIndex: 'name',
title: '供应商名称',
align: 'center',
width: 100
},
{
dataIndex: 'person',
title: '入库人员',
align: 'center',
width: 100
},
{
dataIndex: 'store',
title: '入库仓库',
align: 'center',
width: 100
}
];
const ticketColumns: BasicColumn[] = [
{
dataIndex: 'code',
title: '到票编号',
align: 'center',
width: 100
},
{
dataIndex: 'theme',
title: '到票主题',
align: 'center',
width: 100
},
{
dataIndex: 'date',
title: '到票日期',
align: 'center',
width: 100
},
{
dataIndex: 'name',
title: '开票方',
align: 'center',
width: 100
},
{
dataIndex: 'ticketNum',
title: '发票号码',
align: 'center',
width: 100
},
{
dataIndex: 'ticketAmout',
title: '到票金额',
align: 'center',
width: 100
}
];
const payColumns: BasicColumn[] = [
{
dataIndex: 'code',
title: '付款编号',
align: 'center',
width: 100
},
{
dataIndex: 'theme',
title: '付款主题',
align: 'center',
width: 100
},
{
dataIndex: 'date',
title: '付款日期',
align: 'center',
width: 100
},
{
dataIndex: 'amount',
title: '付款总额',
align: 'center',
width: 100
},
{
dataIndex: 'payer',
title: '收款方',
align: 'center',
width: 100
},
{
dataIndex: 'account',
title: '银行账号',
align: 'center',
width: 100
}
];
const logColumns: BasicColumn[] = [
{
dataIndex: 'operateUserAccount',
title: '操作人',
align: 'center'
},
{
dataIndex: 'createDate',
title: '操作时间',
align: 'center'
},
{
dataIndex: 'executeResultJson',
title: '操作类型',
align: 'center'
}
];
2025-10-21 18:04:02 +08:00
const searchFormSchema: FormSchema[] = [
{
field: 'keyword',
label: '',
component: 'Input',
colProps: { span: 8 },
componentProps: {
placeholder: '请输入要查询的关键字'
}
}
];
2025-10-21 18:04:02 +08:00
const purchaseInfoDataSource = ref<any>([]);
const instoreDataSource = ref<any>([]);
const ticketDataSource = ref<any>([]);
const payDataSource = ref<any>([]);
const logInfoDataSource = ref<any>([]);
2025-10-21 18:04:02 +08:00
const isReady = ref(false);
const rowId = ref('');
const activeKey = ref('1');
const baseInfo = ref();
const workflowList = ref();
const imgList = ref<any>([]);
const { notification } = useMessage();
2025-10-21 18:04:02 +08:00
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
setModalProps({
confirmLoading: false,
destroyOnClose: true,
showCancelBtn: false,
showOkBtn: false,
width: 1000,
fixedHeight: true,
footer: null
});
2025-10-21 18:04:02 +08:00
rowId.value = data.id;
getInfo();
});
2025-10-21 18:04:02 +08:00
const [registerPurchaseTable, { getSelectRowKeys: getPurchaseRowKeys }] = useTable({
title: '采购物料',
dataSource: purchaseInfoDataSource,
rowKey: 'id',
columns: purchaseColumns,
bordered: true,
pagination: false,
rowSelection: {
type: 'checkbox'
}
});
2025-10-21 18:04:02 +08:00
const [registerInstoreTable, { getSelectRowKeys: getInstoreRowKeys, setProps: setInstoreProps }] = useTable({
dataSource: instoreDataSource,
rowKey: 'id',
columns: instoreColumns,
handleSearchInfoFn(info) {
const filterData = instoreDataSource.value.filter((item) => {
for (const key in item) {
if (!isNil(item[key]) && item[key].toString().indexOf(info.keyword) > -1) {
return item;
}
}
});
setInstoreProps({ dataSource: filterData });
return info;
},
formConfig: {
schemas: searchFormSchema
},
bordered: true,
pagination: false,
useSearchForm: true,
rowSelection: {
type: 'checkbox'
}
});
2025-10-21 18:04:02 +08:00
const [registerTicketTable, { getSelectRowKeys: getTicketRowKeys, setProps: setTicketProps }] = useTable({
dataSource: ticketDataSource,
rowKey: 'id',
columns: ticketColumns,
handleSearchInfoFn(info) {
const filterData = ticketDataSource.value.filter((item) => {
for (const key in item) {
if (!isNil(item[key]) && item[key].toString().indexOf(info.keyword) > -1) {
return item;
}
}
});
setTicketProps({ dataSource: filterData });
return info;
},
formConfig: {
schemas: searchFormSchema
},
bordered: true,
pagination: false,
useSearchForm: true,
rowSelection: {
type: 'checkbox'
}
});
2025-10-21 18:04:02 +08:00
const [registerPayTable, { getSelectRowKeys: getPayRowKeys, setProps: setPayProps }] = useTable({
dataSource: payDataSource,
rowKey: 'id',
columns: payColumns,
handleSearchInfoFn(info) {
const filterData = payDataSource.value.filter((item) => {
for (const key in item) {
if (!isNil(item[key]) && item[key].toString().indexOf(info.keyword) > -1) {
return item;
}
}
});
setPayProps({ dataSource: filterData });
return info;
},
formConfig: {
schemas: searchFormSchema
},
bordered: true,
pagination: false,
useSearchForm: true,
rowSelection: {
type: 'checkbox'
}
});
2025-10-21 18:04:02 +08:00
const [registerLogTable, { getSelectRowKeys: getLogRowKeys, setProps: setLogProps }] = useTable({
dataSource: logInfoDataSource,
rowKey: 'id',
columns: logColumns,
handleSearchInfoFn(info) {
const filterData = logInfoDataSource.value.filter((item) => {
for (const key in item) {
if (!isNil(item[key]) && item[key].toString().indexOf(info.keyword) > -1) {
return item;
}
}
});
setLogProps({ dataSource: filterData });
return info;
},
2025-10-21 18:04:02 +08:00
formConfig: {
schemas: searchFormSchema
},
useSearchForm: true,
bordered: true,
pagination: false,
rowSelection: {
type: 'checkbox'
}
});
2025-10-21 18:04:02 +08:00
const getInfo = async () => {
const res = await getPurchaseCheckInfo(rowId.value);
baseInfo.value = res;
purchaseInfoDataSource.value = res?.caseErpPurchaseDetailList;
instoreDataSource.value = res?.inStoreLogVoList.map((x) => {
return {
code: x.code,
theme: x.theme,
date: x.date,
name: x.name,
person: x.person,
store: x.store
};
});
ticketDataSource.value = res?.ticketLogVoList.map((x) => {
return {
code: x.code,
theme: x.theme,
date: x.date,
name: x.name,
ticketNum: x.ticketNum,
ticketAmout: x.ticketAmout
};
});
payDataSource.value = res?.payLogVoList.map((x) => {
return {
code: x.code,
theme: x.theme,
date: x.date,
amount: x.amount,
payer: x.payer,
account: x.account
};
});
logInfoDataSource.value = res?.logList.map((x) => {
return {
operateUserAccount: x.operateUserAccount,
createDate: x.createDate,
executeResultJson: x.executeResultJson
};
});
if (baseInfo.value.processInstanceId) {
const res = await getRecordList(baseInfo.value.processInstanceId);
workflowList.value = [
{
records: res.taskRecords,
schemaName: '当前流程'
}
];
}
isReady.value = true;
2025-10-21 18:04:02 +08:00
if (baseInfo.value.filePath) {
imgList.value = await getFileList({ folderId: baseInfo.value.filePath });
}
};
2025-10-21 18:04:02 +08:00
const handleCancel = () => {
isReady.value = false;
workflowList.value = [];
activeKey.value = '1';
};
2025-10-21 18:04:02 +08:00
const handleExport = async (type) => {
const params: any = {
saleId: rowId.value
};
let title = '';
let res;
switch (type) {
case 'purchase':
if (!notice(getPurchaseRowKeys().length)) return;
title = '采购信息';
params.ids = getPurchaseRowKeys();
res = await exportPurchaseInfo(params);
break;
case 'instore':
if (!notice(getInstoreRowKeys().length)) return;
title = '入库记录';
params.ids = getInstoreRowKeys();
res = await exportInstoreInfo(params);
break;
case 'ticket':
if (!notice(getTicketRowKeys().length)) return;
title = '到票记录';
params.ids = getTicketRowKeys();
res = await exportTicketInfo(params);
break;
case 'pay':
if (!notice(getPayRowKeys().length)) return;
title = '付款记录';
params.ids = getPayRowKeys();
res = await exportPayInfo(params);
break;
case 'log':
title = '操作记录';
params.ids = getLogRowKeys();
res = await exportLogInfo(params);
break;
}
downloadByData(res.data, `${title}.xlsx`, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
};
2025-10-21 18:04:02 +08:00
const notice = (length) => {
if (!length) {
notification.warning({
message: 'Tip',
description: '请选择需要导出的数据'
});
return false;
}
return true;
};
const handlePrint = async () => {
const element: HTMLElement = window.document.querySelector('.info-box')!;
const url = await domtoimage.toPng(element);
printJS({
printable: url,
type: 'image',
documentTitle: '打印'
});
};
</script>
<style lang="less" scoped>
2025-10-21 18:04:02 +08:00
.info-box {
background-color: #fff;
width: 100%;
height: 100%;
padding: 16px;
margin-right: 8px;
2025-10-21 18:04:02 +08:00
.title-name {
font-size: 18px;
color: #444;
font-weight: 700;
}
2025-10-21 18:04:02 +08:00
.title-info {
margin: 10px 0;
2025-10-21 18:04:02 +08:00
span {
margin-right: 40px;
}
}
2025-10-21 18:04:02 +08:00
.table-bottom {
display: flex;
justify-content: space-between;
margin: 10px 0;
2025-10-21 18:04:02 +08:00
& > div > span {
margin-left: 40px;
}
}
2025-10-21 18:04:02 +08:00
.title-btn {
margin-right: 5px;
}
}
2025-10-21 18:04:02 +08:00
.ant-row {
margin-bottom: 15px;
}
2025-10-21 18:04:02 +08:00
:deep(.vben-basic-table) {
height: 300px;
}
2025-10-21 18:04:02 +08:00
:deep(.ant-table-wrapper),
:deep(.ant-table-title) {
background-color: #f8f8f8;
}
2025-10-21 18:04:02 +08:00
:deep(.ant-card-body) {
background-color: #f8f8f8;
margin-bottom: 12px;
padding: 15px;
}
2025-10-21 18:04:02 +08:00
:deep(.ant-tabs) {
height: 70%;
}
2025-10-21 18:04:02 +08:00
:deep(.ant-tabs-content) {
height: 100%;
}
</style>