chem_mes/mes-ui/mes-ui-admin-vue3/src/views/biz/saledelivery/SaleDeliveryForm.vue

611 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1500px">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<!-- 基础信息 -->
<el-card title="基础信息" class="mb-4">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="销售订单" prop="saleOrdId">
<el-input
v-model="formData.saleOrdNo"
placeholder="请选择"
readonly
@click="openOrderSelect"
class="cursor-pointer"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="单据日期" prop="ordDate">
<el-date-picker
v-model="formData.ordDate"
type="date"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
placeholder="选择单据日期"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="单据状态" prop="deliveryStatus">
<el-select v-model="formData.deliveryStatus" disabled>
<el-option label="已创建" value="1" />
<el-option label="已确认" value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="交货方式" prop="deliveryType">
<el-select v-model="formData.deliveryType" placeholder="请选择">
<el-option label="发货" value="1" />
<el-option label="自提" value="2" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" class="mt-4">
<el-col :span="8">
<el-form-item label="客户名称" prop="custName">
<el-input v-model="formData.custName" placeholder="请输入客户名称" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="产品名称" prop="materialName">
<el-input v-model="formData.materialName" placeholder="请输入产品名称" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="规格型号" prop="spec">
<el-input v-model="formData.spec" placeholder="请输入规格型号" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" class="mt-4">
<el-col :span="6">
<el-form-item label="出库单号" prop="saleDeliveryNo">
<el-input v-model="formData.saleDeliveryNo" placeholder="自动生成" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="订单数量" prop="ordQty">
<el-input v-model="formData.ordQty" placeholder="请输入订单数量" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="剩余数量" prop="remaimQty">
<el-input v-model="formData.remaimQty" placeholder="请输入剩余数量" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="发货数量" prop="deliveriedQty">
<el-input
v-model="formData.deliveriedQty"
placeholder="请输入发货数量"
type="number"
:min="0"
:max="formData.remaimQty || 0"
/>
</el-form-item>
</el-col>
</el-row>
<el-row class="mt-4">
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
placeholder="请输入备注"
:rows="3"
/>
</el-form-item>
</el-col>
</el-row>
</el-card>
<!-- 收货信息 -->
<el-card title="收货信息" class="mb-4">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="联系人" prop="contact">
<el-input v-model="formData.contact" placeholder="请输入联系人" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系电话" prop="conPhone">
<el-input v-model="formData.conPhone" placeholder="请输入联系电话" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系地址" prop="conAddress">
<el-input v-model="formData.conAddress" placeholder="请输入联系地址" />
</el-form-item>
</el-col>
</el-row>
</el-card>
<!-- 产品信息 -->
<el-card title="产品信息">
<div class="mb-3 flex items-center justify-between">
<span></span>
<el-button type="primary" plain @click="addProductItem">新增</el-button>
</div>
<el-table :data="productList" show-summary border :summary-method="getSummary">
<el-table-column label="序号" type="index" width="60px" />
<el-table-column label="仓库(*)" prop="warehouse" width="100px">
<template #default="scope">
<el-input v-model="scope.row.warehouse" placeholder="请输入仓库" />
</template>
</el-table-column>
<el-table-column label="库区(*)" prop="warehouseArea" width="100px">
<template #default="scope">
<el-input v-model="scope.row.warehouseArea" placeholder="请输入库区" />
</template>
</el-table-column>
<el-table-column label="批次号(*)" prop="batchNo" width="120px">
<template #default="scope">
<el-input v-model="scope.row.batchNo" placeholder="请输入批次号" />
</template>
</el-table-column>
<el-table-column label="库存数量" prop="stockQty" width="100px">
<template #default="scope">
<el-input v-model="scope.row.stockQty" placeholder="库存数量" readonly />
</template>
</el-table-column>
<el-table-column label="库存袋数" prop="stockBag" width="100px">
<template #default="scope">
<el-input v-model="scope.row.stockBag" placeholder="库存袋数" readonly />
</template>
</el-table-column>
<el-table-column label="单袋规格" prop="bagSpec" width="100px">
<template #default="scope">
<el-input v-model="scope.row.bagSpec" placeholder="手动录入" />
</template>
</el-table-column>
<el-table-column label="发货袋数(*)" prop="deliveriedBag" width="120px">
<template #default="scope">
<el-input v-model="scope.row.deliveriedBag" placeholder="请输入" />
</template>
</el-table-column>
<el-table-column label="发货数量(*)" prop="deliveriedQty" width="120px">
<template #default="scope">
<el-input v-model="scope.row.deliveriedQty" placeholder="请输入" />
</template>
</el-table-column>
<el-table-column label="备注" prop="remark" width="150px">
<template #default="scope">
<el-input v-model="scope.row.remark" placeholder="文本-手动录入" />
</template>
</el-table-column>
<el-table-column label="操作" width="80px">
<template #default="scope">
<el-button
link
type="danger"
@click="removeProductItem(scope.$index)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">保存</el-button>
<el-button @click="submitAudit" type="primary" :disabled="formLoading">确认</el-button>
<el-button @click="dialogVisible = false">取消</el-button>
</template>
</Dialog>
<!-- 订单选择弹窗 -->
<OrderSelectDialog ref="orderSelectRef" @select="handleOrderSelect" />
</template>
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import * as SaleDeliveryApi from '@/api/biz/saledelivery'
import * as OrderApi from '@/api/biz/tsoorder'
import * as CustomerApi from '@/api/biz/customer'
import { watch } from 'vue'
import OrderSelectDialog from './OrderSelectDialog.vue'
const { t } = useI18n()
const message = useMessage()
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formLoading = ref(false)
const formType = ref('')
// 销售订单选项
const saleOrderOptions = ref([])
// 订单选择弹窗
const orderSelectRef = ref()
// 获取今日日期
const getToday = () => {
const today = new Date()
const year = today.getFullYear()
const month = String(today.getMonth() + 1).padStart(2, '0')
const day = String(today.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
const formData = reactive({
id: undefined,
saleDeliveryNo: '自动生成',
deliveryDate: getToday(),
deliveryStatus: '1',
deliveryType: undefined,
ordDate: undefined,
custId: undefined,
custName: undefined,
contact: undefined,
conPhone: undefined,
conAddress: undefined,
remark: undefined,
saleOrdId: undefined,
saleOrdNo: undefined,
deliveryEmpId: undefined,
deliveryEmpName: undefined,
materialId: undefined,
materialCode: undefined,
materialName: undefined,
spec: undefined,
unit: undefined,
ordQty: undefined,
remaimQty: undefined,
deliveriedQty: undefined,
})
// 产品列表
const productList = ref([
{
warehouse: '',
warehouseArea: '',
batchNo: '',
stockQty: '',
stockBag: '',
bagSpec: '',
deliveriedBag: '',
deliveriedQty: '',
remark: '',
},
])
// 合计
const totalBag = computed(() => {
return productList.value.reduce((sum, item) => sum + (parseInt(item.deliveriedBag) || 0), 0)
})
const totalQty = computed(() => {
return productList.value.reduce((sum, item) => sum + (parseInt(item.deliveriedQty) || 0), 0)
})
/** 表格合计方法 */
const getSummary = (param: any) => {
const { columns, data } = param
const sums: any[] = []
columns.forEach((column: any, index: number) => {
if (index === 0) {
sums[index] = '合计'
} else if (column.prop === 'deliveriedBag') {
const total = data.reduce((sum: number, item: any) => sum + (parseInt(item.deliveriedBag) || 0), 0)
sums[index] = total
} else if (column.prop === 'deliveriedQty') {
const total = data.reduce((sum: number, item: any) => sum + (parseInt(item.deliveriedQty) || 0), 0)
sums[index] = total
} else {
sums[index] = ''
}
})
return sums
}
// 监听弹窗关闭
watch(dialogVisible, (val) => {
if (!val) {
emit('close')
}
})
const formRules = reactive({
saleOrdId: [{ required: true, message: '销售订单不能为空', trigger: 'change' }],
deliveryDate: [{ required: true, message: '单据日期不能为空', trigger: 'change' }],
deliveryStatus: [{ required: true, message: '单据状态不能为空', trigger: 'change' }],
deliveriedQty: [
{ required: true, message: '发货数量不能为空', trigger: 'change' },
{
validator: (rule, value, callback) => {
if (value && formData.remaimQty !== undefined && Number(value) > Number(formData.remaimQty)) {
callback(new Error(`发货数量不能大于剩余数量(${formData.remaimQty})`))
} else {
callback()
}
},
trigger: ['change', 'blur']
}
],
})
const formRef = ref()
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 加载销售订单选项
await loadSaleOrderOptions()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
const data = await SaleDeliveryApi.getSaleDelivery(id)
Object.assign(formData, data)
// 加载产品列表
if (data.items) {
productList.value = data.items
}
} finally {
formLoading.value = false
}
}
}
defineExpose({ open })
/** 加载销售订单选项 */
const loadSaleOrderOptions = async () => {
try {
const data = await OrderApi.getOrderList({ pageNo: 1, pageSize: 100 })
saleOrderOptions.value = data.list.map(item => ({
id: item.id,
saleOrdNo: item.saleOrdNo,
custName: item.custName,
materialName: item.materialName,
spec: item.spec,
ordQty: item.ordQty,
remaimQty: item.remaimQty,
}))
} catch (e) {
console.error('加载销售订单失败', e)
}
}
/** 销售订单变更处理 */
const handleSaleOrderChange = async (value: number) => {
const order = saleOrderOptions.value.find(item => item.id === value)
if (order) {
formData.custName = order.custName
formData.materialName = order.materialName
formData.spec = order.spec
formData.ordQty = order.ordQty
formData.remaimQty = order.remaimQty
formData.deliveriedQty = order.remaimQty
}
}
// 防止重复点击
let isOpeningOrderSelect = false
/** 打开订单选择弹窗 */
const openOrderSelect = () => {
if (isOpeningOrderSelect) return
isOpeningOrderSelect = true
try {
orderSelectRef.value.open()
} finally {
setTimeout(() => {
isOpeningOrderSelect = false
}, 300)
}
}
/** 处理订单选择 */
const handleOrderSelect = async (data: any) => {
console.log('handleOrderSelect data:', data)
formData.saleOrdId = data.saleOrdId
formData.saleOrdNo = data.saleOrdNo
formData.custId = data.custId
formData.custName = data.custName
formData.materialName = data.materialName
formData.materialId = data.materialId
formData.materialCode = data.materialCode
formData.spec = data.spec
formData.ordQty = data.ordQty
formData.remaimQty = data.remaimQty
formData.deliveriedQty = data.remaimQty
formData.unit = data.unit
// 将订单日期带入到单据日期
if (data.ordDate) {
// 确保日期格式为 YYYY-MM-DD 字符串类型
const ordDateStr = String(data.ordDate).trim()
if (/^\d{4}-\d{2}-\d{2}$/.test(ordDateStr)) {
formData.ordDate = ordDateStr
} else {
try {
const date = new Date(data.ordDate)
if (!isNaN(date.getTime())) {
formData.ordDate = date.toISOString().split('T')[0]
}
} catch (e) {
console.warn('日期转换失败:', e)
}
}
} else {
// 如果没有订单日期,清空单据日期
formData.ordDate = null
}
console.log('formData after set:', formData)
// 添加到选项列表以便显示
if (!saleOrderOptions.value.find(item => item.id === data.saleOrdId)) {
saleOrderOptions.value.push({
id: data.saleOrdId,
saleOrdNo: data.saleOrdNo,
custName: data.custName,
materialName: data.materialName,
spec: data.spec,
ordQty: data.ordQty,
remaimQty: data.remaimQty,
deliveriedQty: data.deliveriedQty,
})
}
// 如果有客户ID查询客户联系人信息
if (data.custId) {
await loadCustomerContact(data.custId)
}
}
/** 根据客户ID查询客户联系人信息 */
const loadCustomerContact = async (custId: number) => {
try {
const customer = await CustomerApi.getCustomer(custId)
if (customer) {
// 优先使用contact1如果为空则使用contact2
formData.contact = customer.contact1 || customer.contact2
formData.conPhone = customer.conPhone1 || customer.conPhone2
formData.conAddress = customer.conAddress1 || customer.conAddress2
}
} catch (error) {
console.error('查询客户联系人信息失败', error)
}
}
/** 添加产品项 */
const addProductItem = () => {
productList.value.push({
warehouse: '',
warehouseArea: '',
batchNo: '',
stockQty: '',
stockBag: '',
bagSpec: '',
deliveriedBag: '',
deliveriedQty: '',
remark: '',
})
}
/** 删除产品项 */
const removeProductItem = (index: number) => {
if (productList.value.length > 1) {
productList.value.splice(index, 1)
}
}
/** 提交表单 */
const emit = defineEmits(['success', 'close'])
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = {
...formData,
items: productList.value,
}
if (formType.value === 'create') {
await SaleDeliveryApi.createSaleDelivery(data)
message.success(t('common.createSuccess'))
} else {
await SaleDeliveryApi.updateSaleDelivery(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
emit('success')
} finally {
formLoading.value = false
}
}
/** 提交确认 */
const submitAudit = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = {
...formData,
deliveryStatus: '2', // 已确认
items: productList.value,
}
if (formType.value === 'create') {
await SaleDeliveryApi.createSaleDelivery(data)
message.success(t('common.createSuccess'))
} else {
await SaleDeliveryApi.updateSaleDelivery(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
Object.assign(formData, {
id: undefined,
saleDeliveryNo: '自动生成',
deliveryDate: getToday(),
deliveryStatus: '1',
deliveryType: undefined,
custId: undefined,
custName: undefined,
contact: undefined,
conPhone: undefined,
conAddress: undefined,
remark: undefined,
saleOrdId: undefined,
saleOrdNo: undefined,
deliveryEmpId: undefined,
deliveryEmpName: undefined,
materialId: undefined,
materialCode: undefined,
materialName: undefined,
spec: undefined,
unit: undefined,
ordQty: undefined,
remaimQty: undefined,
deliveriedQty: undefined,
ordDate: null,
})
productList.value = [{
warehouse: '',
warehouseArea: '',
batchNo: '',
stockQty: '',
stockBag: '',
bagSpec: '',
deliveriedBag: '',
deliveriedQty: '',
remark: '',
}]
formRef.value?.resetFields()
}
</script>