571 lines
15 KiB
Vue
571 lines
15 KiB
Vue
<template>
|
||
<BasicModal
|
||
v-bind="$attrs"
|
||
@register="registerModal"
|
||
:title="getTitle"
|
||
@ok="handleSubmit"
|
||
:width="1000"
|
||
>
|
||
<div class="sub-title">基础信息</div>
|
||
<BasicForm @register="registerForm">
|
||
<template #code="{ model }">
|
||
<a-input
|
||
v-model:value="model.purchaseNumber"
|
||
placeholder="请输入采购单号"
|
||
:readonly="model.isSysNumBoolean"
|
||
/>
|
||
</template>
|
||
<template #isSysNum="{ model }">
|
||
<a-checkbox
|
||
v-model:checked="model.isSysNumBoolean"
|
||
@change="handleSysChange"
|
||
style="margin-left: 10px"
|
||
>
|
||
用系统编号
|
||
</a-checkbox>
|
||
</template>
|
||
<template #isRelation="{ model }">
|
||
<a-checkbox
|
||
v-model:checked="model.isRelationApplyBoolean"
|
||
@change="handleRelationChange"
|
||
style="margin-left: 10px"
|
||
>
|
||
不关联
|
||
</a-checkbox>
|
||
</template>
|
||
<template #user="{ model }">
|
||
<SelectUser
|
||
v-model:value="model.purchasePersonId"
|
||
:multiple="false"
|
||
suffix="ant-design:setting-outlined"
|
||
placeholder="请选择采购人员"
|
||
@change="handleUserChange"
|
||
/>
|
||
</template>
|
||
</BasicForm>
|
||
<BasicTable @register="registerTable">
|
||
<template #toolbar>
|
||
<a-button type="primary" @click="openModal(true, { type: 'checkbox' })"> 添加 </a-button>
|
||
<a-button type="primary" danger @click="handleDelete"> 移除</a-button>
|
||
</template>
|
||
<template #bodyCell="{ column, record }">
|
||
<template v-if="column.key === 'price'">
|
||
<a-input-number
|
||
v-model:value="record.price"
|
||
:min="0"
|
||
@change="handleNumberChange(record)"
|
||
/>
|
||
</template>
|
||
<template v-if="column.key === 'count'">
|
||
<a-input-number
|
||
v-model:value="record.count"
|
||
:min="0"
|
||
@change="handleNumberChange(record)"
|
||
/>
|
||
</template>
|
||
<template v-if="column.key === 'discount'">
|
||
<a-input-number
|
||
v-model:value="record.discount"
|
||
addon-after="%"
|
||
:min="0"
|
||
@change="handleNumberChange(record)"
|
||
/>
|
||
</template>
|
||
<template v-if="column.key === 'taxRate'">
|
||
<a-input-number
|
||
v-model:value="record.taxRate"
|
||
addon-after="%"
|
||
:min="0"
|
||
@change="handleNumberChange(record)"
|
||
/>
|
||
</template>
|
||
<template v-if="column.key === 'taxBreak'">
|
||
<a-input v-model:value="record.taxBreak" disabled />
|
||
</template>
|
||
<template v-if="column.key === 'afterTaxAmount'">
|
||
<a-input v-model:value="record.afterTaxAmount" disabled />
|
||
</template>
|
||
<template v-if="column.key === 'deliveryDate'">
|
||
<XjrDatePicker v-model:value="record.deliveryDate" format="YYYY-MM-DD" />
|
||
</template>
|
||
<template v-if="column.key === 'remark'">
|
||
<a-input v-model:value="record.remark" />
|
||
</template>
|
||
</template>
|
||
</BasicTable>
|
||
<div class="table-bottom">
|
||
<span>合计</span>
|
||
<div>
|
||
<span>
|
||
总量:
|
||
<span class="price">{{ total.countSum }}</span>
|
||
</span>
|
||
<span>
|
||
总金额:
|
||
<span class="price">{{ total.amountSum }}</span>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="sub-title">附件</div>
|
||
<Upload
|
||
v-model:value="folderId"
|
||
listType="dragger"
|
||
:tip="fileTip"
|
||
:style="{ width: '200px' }"
|
||
/>
|
||
<SelectModal @register="registerSelectModal" @success="handleSuccess" />
|
||
</BasicModal>
|
||
</template>
|
||
<script lang="ts" setup>
|
||
import { ref, computed, unref } from 'vue';
|
||
import { BasicModal, useModalInner, useModal } from '/@/components/Modal';
|
||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||
import { BasicTable, useTable, BasicColumn, FormSchema } from '/@/components/Table';
|
||
import Upload from '/@/components/Form/src/components/Upload.vue';
|
||
import SelectModal from '../../bom/components/SelectModal.vue';
|
||
import SelectUser from '/@/components/Form/src/components/SelectUser.vue';
|
||
import { XjrDatePicker } from '/@/components/DatePicker';
|
||
import { getSaleCode } from '/@/api/erp/sale/order';
|
||
import { getPurchaseInfo, updatePurchase, addPurchase } from '/@/api/erp/purchase/order';
|
||
import { getCaseErpApply } from '/@/api/erp/purchase/apply';
|
||
import { getCaseErpApplyList } from '/@/api/erp/purchase/apply';
|
||
import { getSupplierList } from '/@/api/erp/supplier/list';
|
||
import { getUser } from '/@/api/system/user';
|
||
import { useMessage } from '/@/hooks/web/useMessage';
|
||
import { useI18n } from '/@/hooks/web/useI18n';
|
||
|
||
const { t } = useI18n();
|
||
|
||
const handleApplyChange = async (id) => {
|
||
const res = await getCaseErpApply(id);
|
||
let userInfo;
|
||
if (res.applyUserIds) {
|
||
userInfo = await getUser(res.applyUserIds);
|
||
}
|
||
setFieldsValue({
|
||
relatedProject: res.relatedProject,
|
||
purchasePersonId: res.applyUserIds,
|
||
purchaseDeptId: res.applyDepId,
|
||
purchasePhone: userInfo?.mobile || '',
|
||
filePath: res.filePath,
|
||
});
|
||
|
||
unref(total).countSum = res.countSum || 0;
|
||
unref(total).amountSum = res.amountSum || 0;
|
||
|
||
setTableData(res.caseErpApplyDetailList || []);
|
||
};
|
||
|
||
const FormSchema: FormSchema[] = [
|
||
{
|
||
field: 'purchaseNumber',
|
||
label: '采购单号',
|
||
component: 'Input',
|
||
slot: 'code',
|
||
required: true,
|
||
colProps: { span: 9 },
|
||
},
|
||
{
|
||
field: 'isSysNumBoolean',
|
||
label: '',
|
||
component: 'Input',
|
||
slot: 'isSysNum',
|
||
colProps: { span: 3 },
|
||
},
|
||
{
|
||
field: 'applyId',
|
||
label: '关联申请单',
|
||
component: 'ApiSelect',
|
||
colProps: { span: 9 },
|
||
componentProps: {
|
||
placeholder: '请选择关联申请单',
|
||
api: getCaseErpApplyList,
|
||
labelField: 'theme',
|
||
valueField: 'id',
|
||
getPopupContainer: () => document.body,
|
||
onChange: handleApplyChange,
|
||
},
|
||
},
|
||
{
|
||
field: 'isRelationApplyBoolean',
|
||
label: '',
|
||
component: 'Input',
|
||
slot: 'isRelation',
|
||
colProps: { span: 3 },
|
||
},
|
||
{
|
||
field: 'theme',
|
||
label: '订单主题',
|
||
component: 'Input',
|
||
required: true,
|
||
colProps: { span: 24 },
|
||
componentProps: {
|
||
placeholder: '请输入订单主题',
|
||
},
|
||
},
|
||
{
|
||
field: 'purchaseDate',
|
||
label: '采购日期',
|
||
component: 'DatePicker',
|
||
required: true,
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
format: 'YYYY-MM-DD',
|
||
placeholder: '请选择采购日期',
|
||
getPopupContainer: () => document.body,
|
||
},
|
||
},
|
||
{
|
||
field: 'supplierId',
|
||
label: '供应商名称',
|
||
component: 'ApiSelect',
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
placeholder: '请选择供应商名称',
|
||
api: getSupplierList,
|
||
labelField: 'name',
|
||
valueField: 'id',
|
||
getPopupContainer: () => document.body,
|
||
onChange: (_, option) => {
|
||
if (option) {
|
||
setFieldsValue({
|
||
supplierPerson: option.person || '',
|
||
supplierWay: option.phone || '',
|
||
});
|
||
}
|
||
},
|
||
},
|
||
},
|
||
{
|
||
field: 'supplierPerson',
|
||
label: '联系人',
|
||
component: 'Input',
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
disabled: true,
|
||
},
|
||
},
|
||
{
|
||
field: 'supplierWay',
|
||
label: '联系方式',
|
||
component: 'Input',
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
disabled: true,
|
||
},
|
||
},
|
||
{
|
||
field: 'purchaseDeptId',
|
||
label: '采购部门',
|
||
component: 'Dept',
|
||
colProps: { span: 12 },
|
||
},
|
||
{
|
||
field: 'purchasePersonId',
|
||
label: '采购人员',
|
||
component: 'Input',
|
||
slot: 'user',
|
||
colProps: { span: 12 },
|
||
},
|
||
{
|
||
field: 'purchasePhone',
|
||
label: '联系电话',
|
||
component: 'Input',
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
disabled: true,
|
||
},
|
||
},
|
||
{
|
||
field: 'relatedProject',
|
||
label: '关联项目',
|
||
component: 'DicSelect',
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
placeholder: '请选择关联项目',
|
||
itemId: '1680768933996957698',
|
||
isShowAdd: false,
|
||
getPopupContainer: () => document.body,
|
||
},
|
||
},
|
||
{
|
||
field: 'payType',
|
||
label: '结算方式',
|
||
component: 'DicSelect',
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
placeholder: '请选择结算方式',
|
||
itemId: '1680821338692333570',
|
||
isShowAdd: false,
|
||
getPopupContainer: () => document.body,
|
||
},
|
||
},
|
||
{
|
||
field: 'payAddress',
|
||
label: '交付地址',
|
||
component: 'Input',
|
||
colProps: { span: 24 },
|
||
componentProps: {
|
||
placeholder: '请输入交货地址',
|
||
},
|
||
},
|
||
{
|
||
field: 'remark',
|
||
label: '备注',
|
||
component: 'InputTextArea',
|
||
colProps: { span: 24 },
|
||
componentProps: {
|
||
placeholder: '请输入备注',
|
||
},
|
||
},
|
||
];
|
||
|
||
const columns: 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: 'deliveryDate',
|
||
},
|
||
];
|
||
|
||
const { notification } = useMessage();
|
||
const isUpdate = ref(true);
|
||
const rowId = ref('');
|
||
const folderId = ref('');
|
||
const fileTip = '支持扩展名:.doc .docx .pdf .jpg ...';
|
||
|
||
const total = ref({
|
||
countSum: 0 as any,
|
||
amountSum: 0 as any,
|
||
});
|
||
|
||
const emit = defineEmits(['success']);
|
||
|
||
const [registerForm, { setFieldsValue, resetFields, validate, updateSchema }] = useForm({
|
||
labelWidth: 100,
|
||
schemas: FormSchema,
|
||
showActionButtonGroup: false,
|
||
actionColOptions: {
|
||
span: 23,
|
||
},
|
||
});
|
||
const customRow = (record) => {
|
||
return {
|
||
onClick: () => {
|
||
let selectedRowKeys = [...getSelectRowKeys()];
|
||
if (selectedRowKeys.indexOf(record.key) >= 0) {
|
||
let index = selectedRowKeys.indexOf(record.key);
|
||
selectedRowKeys.splice(index, 1);
|
||
} else {
|
||
selectedRowKeys.push(record.key);
|
||
}
|
||
setSelectedRowKeys(selectedRowKeys);
|
||
},
|
||
};
|
||
};
|
||
const [registerTable, { setSelectedRowKeys, getSelectRowKeys, setTableData, getDataSource }] =
|
||
useTable({
|
||
title: '采购物料',
|
||
columns,
|
||
striped: false,
|
||
pagination: false,
|
||
rowSelection: {
|
||
type: 'checkbox',
|
||
},
|
||
customRow,
|
||
});
|
||
|
||
const [registerSelectModal, { openModal }] = useModal();
|
||
|
||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||
resetFields();
|
||
setModalProps({ confirmLoading: false, destroyOnClose: true });
|
||
|
||
isUpdate.value = !!data?.isUpdate;
|
||
if (unref(isUpdate)) {
|
||
rowId.value = data.id;
|
||
const record = await getPurchaseInfo(data.id);
|
||
folderId.value = record.filePath || '';
|
||
setFieldsValue({
|
||
...record,
|
||
});
|
||
setTableData(record.caseErpPurchaseDetails || []);
|
||
unref(total).countSum = record.countSum || 0;
|
||
unref(total).amountSum = record.amountSum || 0;
|
||
} else {
|
||
folderId.value = '';
|
||
const code = await getSaleCode();
|
||
setFieldsValue({ purchaseNumber: code, isSysNumBoolean: true });
|
||
}
|
||
});
|
||
|
||
const getTitle = computed(() => (!unref(isUpdate) ? '新增订单' : '编辑订单'));
|
||
|
||
const handleSysChange = async (e) => {
|
||
let code = '';
|
||
if (e.target.checked) {
|
||
code = await getSaleCode();
|
||
}
|
||
setFieldsValue({ purchaseNumber: code });
|
||
};
|
||
const handleRelationChange = async (e) => {
|
||
updateSchema({
|
||
field: 'applyId',
|
||
componentProps: { disabled: e.target.checked },
|
||
});
|
||
if (e.target.checked) {
|
||
setFieldsValue({
|
||
applyId: '',
|
||
relatedProject: '',
|
||
purchasePersonId: '',
|
||
purchaseDeptId: '',
|
||
purchasePhone: '',
|
||
filePath: '',
|
||
});
|
||
|
||
unref(total).countSum = 0;
|
||
unref(total).amountSum = 0;
|
||
|
||
setTableData([]);
|
||
}
|
||
};
|
||
|
||
const handleUserChange = (_, options) => {
|
||
setFieldsValue({ purchasePhone: options[0]?.mobile });
|
||
};
|
||
|
||
const handleNumberChange = (record) => {
|
||
const price = record.price || 0;
|
||
const count = record.count || 0;
|
||
const discount = record.discount || 0;
|
||
const taxRate = record.taxRate || 0;
|
||
if (discount) {
|
||
record.taxBreak = price * count * (1 - discount / 100) * (taxRate / 100);
|
||
} else {
|
||
record.taxBreak = price * count * (taxRate / 100);
|
||
}
|
||
record.afterTaxAmount = price * count * (1 - discount / 100) + record.taxBreak;
|
||
record.taxBreak = record.taxBreak.toFixed(2);
|
||
record.afterTaxAmount = record.afterTaxAmount.toFixed(2);
|
||
|
||
handleTotalChange();
|
||
};
|
||
|
||
const handleSuccess = async (data) => {
|
||
data.map((x) => {
|
||
x.taxRate = 3;
|
||
x.count = 0;
|
||
});
|
||
setTableData([...getDataSource(), ...data]);
|
||
};
|
||
|
||
const handleDelete = () => {
|
||
const datasource = getDataSource().filter((x) => !getSelectRowKeys().includes(x.key));
|
||
setTableData(datasource);
|
||
handleTotalChange();
|
||
};
|
||
|
||
const handleSubmit = async () => {
|
||
try {
|
||
const values = await validate();
|
||
values.filePath = folderId.value;
|
||
values.addCaseErpPurchaseDetailDtoList = getDataSource() || [];
|
||
Object.assign(values, unref(total));
|
||
setModalProps({ confirmLoading: true });
|
||
|
||
if (!unref(isUpdate)) {
|
||
await addPurchase(values);
|
||
notification.success({
|
||
message: '新增订单',
|
||
description: t('成功'),
|
||
});
|
||
} else {
|
||
values.id = rowId.value;
|
||
await updatePurchase(values);
|
||
notification.success({
|
||
message: '编辑订单',
|
||
description: t('成功'),
|
||
});
|
||
}
|
||
|
||
closeModal();
|
||
emit('success');
|
||
} catch (error) {
|
||
setModalProps({ confirmLoading: false });
|
||
}
|
||
};
|
||
|
||
const handleTotalChange = () => {
|
||
unref(total).countSum = 0;
|
||
unref(total).amountSum = 0;
|
||
getDataSource().map((item) => {
|
||
const price = item.price || 0;
|
||
const count = item.count || 0;
|
||
unref(total).countSum += count;
|
||
unref(total).amountSum += count * price;
|
||
});
|
||
unref(total).amountSum = unref(total).amountSum.toFixed(2);
|
||
};
|
||
</script>
|
||
<style lang="less" scoped>
|
||
.sub-title {
|
||
font-weight: bold;
|
||
margin: 15px 0 20px 30px;
|
||
color: #606266;
|
||
}
|
||
|
||
.table-bottom {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
border-bottom: 1px solid #ddd;
|
||
padding: 5px;
|
||
|
||
& > div > span {
|
||
margin-left: 40px;
|
||
}
|
||
}
|
||
|
||
.price {
|
||
color: #f00;
|
||
}
|
||
</style>
|