You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

839 lines
21 KiB
Vue

1 year ago
<template>
1 year ago
<div>
<my-card title="搜索条件" search>
<n-form inline>
<n-form-item label="申请部门">
<n-tree-select
v-model:value="searchForm.deptId"
class="w-180px"
label-field="deptName"
key-field="deptId"
:options="treeOptions"
></n-tree-select>
1 year ago
1 year ago
<!-- <n-select v-model:value="searchForm.status" :options="statusOptions" class="w-180px"></n-select> -->
</n-form-item>
<n-form-item label="供应商">
<n-select v-model:value="searchForm.vendorId" :options="supperOptions" class="w-180px"></n-select>
</n-form-item>
<n-form-item label="计划日期">
<n-date-picker v-model:value="dateArr" type="daterange"></n-date-picker>
</n-form-item>
<n-form-item>
<component :is="useSearchBtn(search, reset)"></component>
</n-form-item>
</n-form>
</my-card>
<my-card title="采购订单">
<template #right>
<div>
<component
:is="useDelBtn(delSelect, undefined, '确定要删除所有已选中的数据吗?', Boolean(!selectKeys.length))"
></component>
<component
:is="
1 year ago
useAddBtn(
() => {
editFlag = true;
showDialog = true;
},
undefined,
'新增订单'
)
"
1 year ago
></component>
<cx-columns v-model:columns="columns"></cx-columns>
</div>
</template>
<n-data-table
children-key="lineList"
:row-key="row => row.planId"
:loading="loading"
:columns="columns"
:data="data"
@update-checked-row-keys="handleSelect"
></n-data-table>
<my-pagination v-model:search-form="searchForm" @init="init"></my-pagination>
</my-card>
<my-dialog
v-model:show="showDialog"
width="800px"
:title="!editFlag ? '修改采购订单' : '新增采购订单'"
@submit="submit"
@cancel="cancel"
>
<template #content>
<div>
<n-form ref="addFormRef" :rules="rules" label-placement="left" :label-width="100" :model="dialogForm">
<n-grid :cols="2" x-gap="10">
<n-form-item-grid-item :span="1" label="申请部门:" path="deptId">
<n-tree-select
v-model:value="dialogForm.deptId"
label-field="deptName"
key-field="deptId"
:options="treeOptions"
@update:value="treeSelect"
></n-tree-select>
</n-form-item-grid-item>
1 year ago
1 year ago
<!-- <n-form-item-grid-item :span="1" label="申请人:">
<n-input></n-input>
</n-form-item-grid-item> -->
<n-form-item-grid-item :span="1" label="供应商:" path="vendorId">
<n-select
v-model:value="dialogForm.vendorId"
:options="supperOptions"
@update:value="handleSelectSupper"
></n-select>
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="关联计划:" path="planId">
<n-select
v-model:value="dialogForm.planId"
:options="rawProucreOptions"
@update:value="handleSelectRawProucre"
></n-select>
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="总金额:">{{ getTotalPric }}</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="总数量:">
{{ getTotalNum }}
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="总重量:">
{{ getTotalWeight }}
</n-form-item-grid-item>
</n-grid>
</n-form>
<n-tabs
default-value="明细1"
type="card"
:addable="addable"
:closable="closable"
tab-style="min-width: 80px;"
@close="handleClose"
@add="handleAdd"
>
<n-tab-pane v-for="panel in panels" :key="panel.name" :name="panel.name">
<n-form label-placement="left" :label-width="100">
<n-grid :cols="2" :x-gap="10">
<n-form-item-grid-item :span="1" label="物品名称">
<n-select
v-model:value="panel.form.itemId"
placeholder="请选择物品名称"
:options="itemOptions"
filterable
@update:value="selectItem(panel.form)"
></n-select>
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="型号规格">
<n-input v-model:value="panel.form.specification" disabled placeholder="请选择物品"></n-input>
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="单位">
<n-input v-model:value="panel.form.numMeasureName" disabled placeholder="请选择物品单位"></n-input>
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="数量">
<n-input-number v-model:value="panel.form.quantity" class="w-full" :min="0">
<template #suffix>
<div>
{{ panel.form.numMeasureName }}
</div>
</template>
</n-input-number>
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="单价">
<n-input-number v-model:value="panel.form.price" class="w-full" :min="0">
<template #suffix>
<div></div>
</template>
</n-input-number>
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="重量">
<n-input-number v-model:value="panel.form.weight" class="w-full" :min="0">
<template #suffix>
<div>(kg)</div>
</template>
</n-input-number>
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="计划交货日期">
<n-date-picker
v-model:value="panel.form.deliveryDate"
type="date"
class="w-full"
:min="0"
></n-date-picker>
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="申请原因">
<n-input v-model:value="panel.form.remark" type="textarea"></n-input>
</n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="金额:">
{{ getListTotal(panel, panel.form.weight, panel.form.price) }}
</n-form-item-grid-item>
</n-grid>
</n-form>
</n-tab-pane>
</n-tabs>
</div>
</template>
</my-dialog>
<my-dialog
v-model:show="detail"
sub-text="关闭"
width="800px"
title="订单明细"
:show-cancel="false"
@cancel="closeDetail"
@submit="closeDetail"
>
<template #content>
<div>
<n-data-table
class="w-760px"
:loading="detailLoading"
:columns="detailColumns"
:data="detailData"
></n-data-table>
</div>
</template>
</my-dialog>
</div>
1 year ago
</template>
<script setup lang="tsx">
import type { Ref } from 'vue';
import { ref, onMounted, computed, getCurrentInstance } from 'vue';
1 year ago
import BigNumber from 'bignumber.js';
1 year ago
import type { FormInst, DataTableColumns, DataTableRowKey } from 'naive-ui';
import { useMessage } from 'naive-ui';
import { listDept } from '@/service';
import { addDateRange, deepClone, createRequiredFormRule } from '@/utils';
import { handleTree } from '@/utils/form';
const { proxy } = getCurrentInstance() as any;
const { erp_auxiliary_status } = proxy.useDict('erp_auxiliary_status');
import { useSearchBtn, useInfoBtn, useBtn, useAddBtn, useEditBtn, useDelBtn } from '@/hooks/common/useBtn';
import {
1 year ago
addPurchase,
getAllSupplier,
getAllRawProcureList,
getPurchaseList,
delPurchase,
getPurchaseDetail,
delPurchaseDetail,
getPurchaseTop,
editPurchase,
sendPurchase
1 year ago
} from '@/service/api/erp/rawProcureList';
import { getMaterialAllAccessory } from '@/service/api/md/itemlist/index';
import { useLoading } from '~/src/hooks';
const rules = {
1 year ago
deptId: createRequiredFormRule('请选择部门'),
vendorId: createRequiredFormRule('请选择供应商'),
planId: createRequiredFormRule('请选择计划')
1 year ago
};
const addFormRef = ref<FormInst | null>(null);
const message = useMessage();
const detailLoading = ref<boolean>(false);
const detail = ref<boolean>(false);
function closeDetail() {
1 year ago
detail.value = false;
1 year ago
}
const detailData = ref<Procure.rawProcureList.listType[]>([]);
const detailColumns: DataTableColumns<Procure.rawProcureList.listType> = [
1 year ago
{
title: '序号',
key: 'index',
render(_row, index) {
return index + 1;
},
width: 80
},
{
title: '物料名称',
key: 'itemName',
width: 180
},
{
title: '物料编码',
key: 'itemCode',
width: 180
},
1 year ago
1 year ago
{
title: '计划交货时间',
key: 'deliveryDate',
width: 180
},
{
title: '单价',
key: 'price',
width: 80
},
1 year ago
1 year ago
{
title: '重量',
key: 'weight',
width: 80
},
{
title: '数量',
key: 'quantity',
width: 80
},
{
title: '规格型号',
key: 'specification',
width: 180
},
{
title: '总价格',
key: 'totalPrice',
width: 80
},
{
title: '操作',
key: 'action',
width: 90,
fixed: 'right',
render: row => {
return useDelBtn(() => {
delPurchaseDetail(row.lineId as string).then(res => {
if (res.code === 200) {
message.success('删除成功');
detailLoading.value = false;
getPurchaseDetail(row.purchaseId as string).then(respon => {
detailLoading.value = false;
detailData.value = respon.rows;
});
} else {
message.error('删除失败');
}
});
}, 'tiny');
}
}
1 year ago
];
type Panel = {
1 year ago
name: string;
form: { [key: string]: any };
1 year ago
};
const panels = ref<Panel[]>([
1 year ago
{
name: '明细1',
form: {}
}
1 year ago
]);
const addable = computed(() => {
1 year ago
return panels.value.length < 10;
1 year ago
});
const closable = computed(() => {
1 year ago
return panels.value.length > 1;
1 year ago
});
function handleAdd() {
1 year ago
panels.value.push({ name: `明细${panels.value.length + 1}`, form: {} });
1 year ago
}
function sendTip(num: number) {
1 year ago
if (num <= panels.value.length) message.warning('请从尾部开始删除');
1 year ago
}
function handleClose(name: string) {
1 year ago
let flag = 0;
const number = panels.value.length - 1;
panels.value.forEach((panel, index) => {
if (panel.name === name && index === number) {
panels.value = panels.value.filter(item => item.name !== name);
}
flag += 1;
});
sendTip(flag);
1 year ago
}
function getListTotal(total, num, price) {
1 year ago
const tempNum = new BigNumber(num || 0);
const tempQuantity = new BigNumber(total.form.quantity || 0);
const tempPrice = new BigNumber(price || 0);
total.form.totalPrice = tempQuantity.times(tempPrice).toFixed(4);
// if (total.form.unitType === 'num') {
// total.form.totalPrice = tempQuantity.times(tempPrice).toFixed(4);
// } else if (total.form.unitType === 'weight') {
// total.form.totalPrice = tempNum.times(tempPrice).toFixed(4);
// }
// eslint-disable-next-line no-param-reassign, @typescript-eslint/no-unused-vars
1 year ago
1 year ago
return total.form.totalPrice.toFixed(4);
1 year ago
}
const editFlag = ref(false);
const getTotalPric = computed(() => {
1 year ago
return panels.value.reduce((total, panel) => {
const temp1 = new BigNumber(total || 0);
const temp2 = new BigNumber(panel.form.totalPrice || 0);
return parseFloat(temp1.plus(temp2).toFixed(4));
}, 0);
1 year ago
});
const getTotalNum = computed(() => {
1 year ago
return panels.value.reduce((total, panel) => {
const temp1 = new BigNumber(total || 0);
const temp2 = new BigNumber(panel.form.quantity || 0);
return parseFloat(temp1.plus(temp2).toFixed(4));
}, 0);
1 year ago
});
const getTotalWeight = computed(() => {
1 year ago
return panels.value.reduce((total, panel) => {
const temp1 = new BigNumber(total || 0);
const temp2 = new BigNumber(panel.form.weight || 0);
return parseFloat(temp1.plus(temp2).toFixed(4));
}, 0);
1 year ago
});
const { loading, startLoading, endLoading } = useLoading();
const showDialog = ref<boolean>(false);
const dateArr = ref<null | undefined | [number, number]>(null);
const selectKeys = ref<DataTableRowKey[]>([]);
function handleSelect(keys: DataTableRowKey[]) {
1 year ago
selectKeys.value = keys;
1 year ago
}
function delSelect() {
1 year ago
delPurchase(selectKeys.value.join(',')).then(res => {
if (res.code === 200) {
message.success('删除成功');
init();
} else {
message.error(res.msg);
}
});
1 year ago
}
// const statusOptions = [
// {
// label: '全部',
// value: undefined
// },
// {
// label: '待处理',
// value: '待处理'
// },
// {
// label: '处理中',
// value: '处理中'
// },
// {
// label: '已完成',
// value: '已完成'
// }
// ];
const searchForm = ref<{
1 year ago
vendorId: string | null;
attr1: string | null;
pageSize: number;
pageNum: number;
total: number;
deptId: string | null;
1 year ago
}>({
1 year ago
vendorId: null,
attr1: null,
deptId: null,
pageSize: 10,
pageNum: 1,
total: 0
1 year ago
});
function search() {
1 year ago
searchForm.value.pageNum = 1;
init();
1 year ago
}
function reset() {
1 year ago
searchForm.value = {
vendorId: null,
attr1: null,
deptId: null,
pageSize: 10,
pageNum: 1,
total: 0
};
dateArr.value = null;
init();
1 year ago
}
const dialogForm = ref<Procure.rawProcureList.columns>({
1 year ago
attr1: null,
beginTime: null,
checkedBy: null,
checkedByNick: null,
checkedTime: null,
contractTime: null,
createBy: null,
createByNick: null,
createTime: null,
deptId: null,
deptName: null,
enableFlag: null,
endTime: null,
filePath: null,
flowAction: null,
flowRecordJson: null,
invoiceSta: null,
lineList: [],
paidMoney: null,
params: {},
payableMoney: null,
planCode: null,
planId: null,
purchaseCode: null,
purchaseDate: null,
purchaseId: null,
quantity: null,
realTotalPrice: null,
realTotalWeight: null,
remark: null,
snidCode: null,
status: null,
totalPrice: null,
totalWeight: null,
type: null,
updateBy: null,
updateTime: null,
vendorAddress: null,
vendorCode: null,
vendorContact1: null,
vendorContact1Tel: null,
vendorId: null,
vendorName: null
1 year ago
});
function cancel() {
1 year ago
showDialog.value = false;
dialogForm.value = {
attr1: null,
beginTime: null,
checkedBy: null,
checkedByNick: null,
checkedTime: null,
contractTime: null,
createBy: null,
createByNick: null,
createTime: null,
deptId: null,
deptName: null,
enableFlag: null,
endTime: null,
filePath: null,
flowAction: null,
flowRecordJson: null,
invoiceSta: null,
lineList: [],
paidMoney: null,
params: {},
payableMoney: null,
planCode: null,
planId: null,
purchaseCode: null,
purchaseDate: null,
purchaseId: null,
quantity: null,
realTotalPrice: null,
realTotalWeight: null,
remark: null,
snidCode: null,
status: null,
totalPrice: null,
totalWeight: null,
type: null,
updateBy: null,
updateTime: null,
vendorAddress: null,
vendorCode: null,
vendorContact1: null,
vendorContact1Tel: null,
vendorId: null,
vendorName: null
};
panels.value = [
{
name: '明细1',
form: {}
}
];
init();
1 year ago
}
function submit() {
1 year ago
addFormRef.value?.validate(error => {
if (!error) {
dialogForm.value.lineList = [];
panels.value.forEach(item => {
delete item.form.id;
delete item.form.ifEnable;
delete item.form.ifSafeStock;
delete item.form.version;
delete item.form.createTime;
delete item.form.createBy;
item.form.unitOfMeasure = item.form.numMeasureName;
dialogForm.value.lineList.push(item.form as unknown as Procure.rawProcureList.listType);
});
delete dialogForm.value.children;
1 year ago
1 year ago
dialogForm.value.totalPrice = getTotalPric.value.toString();
dialogForm.value.quantity = getTotalNum.value.toString();
dialogForm.value.totalWeight = getTotalWeight.value.toString();
if (dialogForm.value.purchaseId) {
dialogForm.value.status = 'PREPARE';
editPurchase(dialogForm.value).then(res => {
if (res.code === 200) {
message.success(res.msg);
cancel();
}
});
} else {
dialogForm.value.attr1 = 'YL';
dialogForm.value.status = 'PREPARE';
addPurchase(dialogForm.value).then(res => {
if (res.code === 200) {
message.success(res.msg);
cancel();
}
});
}
}
});
1 year ago
}
const data = ref<Procure.rawProcureList.columns[]>([]);
const columns: Ref<DataTableColumns<Procure.rawProcureList.columns>> = ref([
1 year ago
{
type: 'selection'
},
{
title: '订单编号',
key: 'purchaseCode',
width: 100
},
{
title: '采购部门',
key: 'deptName',
width: 280
},
1 year ago
1 year ago
{ title: '供应商', key: 'vendorName', width: 180 },
{
title: '关联计划编码',
key: 'planCode',
width: 180
},
{
title: '计划数量',
key: 'quantity',
width: 80
},
{
title: '计划重量',
key: 'totalWeight',
width: 80
},
{
title: '总价',
key: 'totalPrice',
width: 80
},
1 year ago
1 year ago
// {
// title: '型号规格',
// key: 'specification',
// width: 180
// },
// {
// title: '单位',
// key: 'unitOfMeasure',
// width: 80
// },
// {
// title: '计划开始日期',
// key: 'planDateStart',
// width: 180
// },
// {
// title: '计划结束日期',
// key: 'planDateEnd',
// width: 180
// },
{
title: '计划员',
key: 'createBy',
width: 150
},
{
title: '状态',
key: 'status',
width: 80,
fixed: 'right',
align: 'center',
render: row => {
console.log(row.status);
return <dict-tag options={erp_auxiliary_status.value} value={row.status}></dict-tag>;
}
},
// {
// title: '计划时间',
// key: 'planMonth',
// width: 280
// },
{
title: '操作',
key: 'action',
fixed: 'right',
width: 220,
render: row => {
const Btn: JSX.Element[] = [];
if (row.status === 'PREPARE') {
Btn.push(
useBtn(
() => {
// row.status = 'DISTRIBUTE';
sendPurchase({ ...row, status: 'DISTRIBUTE' }).then(res => {
console.log(res);
if (res.code === 200) {
message.success('下发成功');
init();
}
});
},
'send',
'tiny'
)
);
Btn.push(
useEditBtn(() => {
editFlag.value = true;
getPurchaseTop(row.purchaseId as string).then(res => {
dialogForm.value = deepClone(res.data);
if (res.data.lineList.length > 0) {
panels.value = [];
res.data.lineList.forEach((item, index) => {
panels.value.push({
name: `明细${index + 1}`,
form: { ...item, deliveryDate: new Date(item.deliveryDate), numMeasureName: item.unitOfMeasure }
});
});
}
showDialog.value = true;
});
}, 'tiny')
);
}
Btn.push(
...[
useInfoBtn(
() => {
detail.value = true;
detailLoading.value = true;
getPurchaseDetail(row.purchaseId as string).then(res => {
detailLoading.value = false;
1 year ago
1 year ago
detailData.value = res.rows;
});
},
'tiny',
'明细'
),
1 year ago
1 year ago
useDelBtn(() => {
delPurchase(row.purchaseId as string).then(res => {
if (res.code === 200) {
message.success('删除成功!');
init();
}
});
}, 'tiny')
]
);
return Btn;
}
}
1 year ago
]);
function init() {
1 year ago
startLoading();
if (dateArr.value) {
addDateRange(searchForm.value, dateArr.value);
}
searchForm.value.attr1 = 'YL';
getPurchaseList(searchForm.value).then(res => {
data.value = res.rows;
searchForm.value.total = res.total;
});
endLoading();
1 year ago
}
const itemOptions = ref<
1 year ago
{
value: string;
label: string;
unitType: string;
numMeasureId: number;
numMeasureName: string;
weightMeasureId: number;
weightMeasureName: string;
}[]
1 year ago
>([]);
function selectItem(form) {
1 year ago
itemOptions.value.forEach(item => {
if (item.value === form.itemId) {
Object.assign(form, item);
if (item.numMeasureId && item.numMeasureId !== 0) {
form.numMeasureName = item.numMeasureName;
} else {
form.numMeasureName = item.weightMeasureName;
}
console.log('form.unitType', form.unitType);
}
});
1 year ago
}
const treeOptions = ref<any>();
function treeSelect(_val, item) {
1 year ago
dialogForm.value.deptId = item.deptId;
dialogForm.value.deptName = item.deptName;
1 year ago
}
const supperOptions = ref<{ value: string; label: string }[]>([]);
function handleSelectSupper(_val, item) {
1 year ago
dialogForm.value.vendorAddress = item.address;
dialogForm.value.vendorCode = item.vendorCode;
dialogForm.value.vendorContact1 = item.contact1;
dialogForm.value.vendorContact1Tel = item.contact1Tel;
dialogForm.value.vendorName = item.vendorName;
1 year ago
}
const rawProucreOptions = ref<{ value: string; label: string }[]>([]);
function handleSelectRawProucre(_val, item) {
1 year ago
dialogForm.value.planId = item.planId;
dialogForm.value.planCode = item.planCode;
// dialogForm.value = Object.assign(dialogForm.value, item);
1 year ago
}
onMounted(() => {
1 year ago
// 获取物料列表
getMaterialAllAccessory({ pageSize: 999 }).then(res => {
console.log(res);
res.rows.forEach(item => {
itemOptions.value.push({
value: item.id,
label: item.itemName,
...item
});
});
});
// 获取部门列表
listDept({}).then(res => {
treeOptions.value = handleTree(res.data, 'deptId', 'parentId', 'children');
// console.log(handleTree(res.data, 'deptId', 'parentId', 'children'), 222);
// console.log(res, 11);
});
// 获取供应商列表
getAllSupplier({ pageSize: 999 }).then(res => {
res.rows.forEach(item => {
supperOptions.value.push({
value: item.id,
label: item.supplierName,
...item
});
});
});
// 获取计划列表
getAllRawProcureList().then(res => {
res.data.forEach(item => {
rawProucreOptions.value.push({
value: item.planId,
label: item.planCode,
...item
});
});
});
init();
1 year ago
});
</script>
<style scoped></style>