2026-06-02 15:56:04 +08:00
|
|
|
|
<script setup lang="ts">
|
2026-06-04 17:51:11 +08:00
|
|
|
|
import { ref, computed, onMounted } from 'vue'
|
2026-06-02 15:56:04 +08:00
|
|
|
|
import { useInventory } from '../composables/useInventory'
|
|
|
|
|
|
import { apiRequest } from '@/shared/api'
|
|
|
|
|
|
import { addNotification, handleApiError } from '@/shared/notification'
|
|
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
|
state,
|
|
|
|
|
|
formatCurrency,
|
|
|
|
|
|
formatDateTime,
|
|
|
|
|
|
formatDate,
|
|
|
|
|
|
getSalesOrderStatusLabel,
|
2026-06-09 13:44:53 +08:00
|
|
|
|
getDeliveryStatusLabel,
|
|
|
|
|
|
getPaymentStatusLabel,
|
2026-06-02 15:56:04 +08:00
|
|
|
|
loadProductionOrders,
|
|
|
|
|
|
loadFinishedProducts,
|
|
|
|
|
|
loadCustomers,
|
|
|
|
|
|
loadMaterials,
|
|
|
|
|
|
loadInventory,
|
|
|
|
|
|
loadMovements
|
|
|
|
|
|
} = useInventory()
|
|
|
|
|
|
|
|
|
|
|
|
const showModal = ref(false)
|
|
|
|
|
|
const editingItem = ref<any>(null)
|
|
|
|
|
|
const form = ref<any>({})
|
|
|
|
|
|
const moldItems = ref<any[]>([])
|
|
|
|
|
|
const productionWarehouseId = ref<number | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
const showConsumptionModal = ref(false)
|
|
|
|
|
|
const consumptionItems = ref<any[]>([])
|
|
|
|
|
|
const consumedMaterials = ref<any[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
function openCreateOrder() {
|
|
|
|
|
|
editingItem.value = null
|
|
|
|
|
|
form.value = {
|
|
|
|
|
|
customer_id: null,
|
|
|
|
|
|
delivery_date: '',
|
|
|
|
|
|
remark: ''
|
|
|
|
|
|
}
|
|
|
|
|
|
moldItems.value = []
|
|
|
|
|
|
consumedMaterials.value = []
|
|
|
|
|
|
showModal.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function addMoldItem() {
|
|
|
|
|
|
moldItems.value.push({
|
|
|
|
|
|
mold_mode: 'new',
|
|
|
|
|
|
mold_sku: '',
|
|
|
|
|
|
mold_name: '',
|
|
|
|
|
|
mold_id: null,
|
|
|
|
|
|
quantity: 1,
|
|
|
|
|
|
unit_price: 0,
|
|
|
|
|
|
remark: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function removeMoldItem(index: number) {
|
|
|
|
|
|
moldItems.value.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const orderTotalAmount = computed(() => {
|
|
|
|
|
|
return moldItems.value.reduce((sum: number, item: any) => {
|
|
|
|
|
|
return sum + (item.unit_price || 0) * (item.quantity || 0)
|
|
|
|
|
|
}, 0)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
async function editOrder(order: any) {
|
|
|
|
|
|
await loadCustomers()
|
|
|
|
|
|
await loadFinishedProducts()
|
|
|
|
|
|
await loadMaterials()
|
|
|
|
|
|
try {
|
|
|
|
|
|
const detail = await apiRequest(`/api/sales-orders/${order.id}`)
|
|
|
|
|
|
editingItem.value = order
|
|
|
|
|
|
form.value = {
|
|
|
|
|
|
customer_id: detail.customer_id || order.customer_id || null,
|
|
|
|
|
|
delivery_date: detail.delivery_date || order.delivery_date || '',
|
|
|
|
|
|
remark: detail.remark || order.remark || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
moldItems.value = (detail.items || []).map((item: any) => ({
|
|
|
|
|
|
mold_mode: item.mold_id ? 'existing' : 'new',
|
|
|
|
|
|
mold_sku: item.sku || '',
|
|
|
|
|
|
mold_name: item.name || '',
|
|
|
|
|
|
mold_id: item.mold_id || null,
|
|
|
|
|
|
quantity: item.quantity || 1,
|
|
|
|
|
|
unit_price: item.unit_price || 0,
|
|
|
|
|
|
remark: item.remark || ''
|
|
|
|
|
|
}))
|
|
|
|
|
|
try {
|
|
|
|
|
|
const consumptionData = await apiRequest(`/api/sales-orders/${order.id}/consume-materials`)
|
|
|
|
|
|
consumedMaterials.value = (consumptionData.items || []).map((item: any) => ({
|
|
|
|
|
|
material_id: item.material_id,
|
|
|
|
|
|
material_name: item.material_name || item.material_sku || '',
|
|
|
|
|
|
quantity: item.quantity,
|
|
|
|
|
|
unit_price: item.unit_price || 0,
|
|
|
|
|
|
remark: item.remark || ''
|
|
|
|
|
|
}))
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
consumedMaterials.value = []
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
handleApiError(e, '加载销售订单详情')
|
|
|
|
|
|
}
|
|
|
|
|
|
showModal.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function saveSalesOrder() {
|
|
|
|
|
|
if (!form.value.customer_id) {
|
|
|
|
|
|
addNotification('请选择客户', 'warning')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!moldItems.value || moldItems.value.length === 0) {
|
|
|
|
|
|
addNotification('请至少添加一个模具', 'warning')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
const payload = {
|
|
|
|
|
|
customer_id: form.value.customer_id,
|
|
|
|
|
|
delivery_date: form.value.delivery_date || null,
|
|
|
|
|
|
remark: form.value.remark || '',
|
|
|
|
|
|
items: moldItems.value.map((item: any) => ({
|
|
|
|
|
|
mold_mode: item.mold_mode,
|
|
|
|
|
|
mold_sku: item.mold_sku || '',
|
|
|
|
|
|
mold_name: item.mold_name || '',
|
|
|
|
|
|
mold_id: item.mold_id || null,
|
|
|
|
|
|
quantity: item.quantity,
|
|
|
|
|
|
unit_price: item.unit_price || 0,
|
|
|
|
|
|
remark: item.remark || ''
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
if (editingItem.value) {
|
|
|
|
|
|
await apiRequest(`/api/sales-orders/${editingItem.value.id}`, {
|
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
|
body: JSON.stringify(payload)
|
|
|
|
|
|
})
|
|
|
|
|
|
addNotification('销售订单更新成功', 'success')
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await apiRequest('/api/sales-orders', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
body: JSON.stringify(payload)
|
|
|
|
|
|
})
|
|
|
|
|
|
addNotification('销售订单创建成功', 'success')
|
|
|
|
|
|
}
|
|
|
|
|
|
showModal.value = false
|
|
|
|
|
|
editingItem.value = null
|
|
|
|
|
|
form.value = {}
|
|
|
|
|
|
moldItems.value = []
|
|
|
|
|
|
consumedMaterials.value = []
|
|
|
|
|
|
loadProductionOrders()
|
|
|
|
|
|
loadFinishedProducts()
|
|
|
|
|
|
loadInventory()
|
|
|
|
|
|
loadMovements()
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
handleApiError(e, '保存销售订单')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function deleteOrder(id: number) {
|
|
|
|
|
|
if (!confirm('确定要删除这个销售订单吗?')) return
|
|
|
|
|
|
try {
|
|
|
|
|
|
await apiRequest(`/api/sales-orders/${id}`, { method: 'DELETE' })
|
|
|
|
|
|
addNotification('销售订单已删除', 'success')
|
|
|
|
|
|
loadProductionOrders()
|
|
|
|
|
|
loadInventory()
|
|
|
|
|
|
loadMovements()
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
handleApiError(e, '删除销售订单')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function updateStatus(orderId: number, status: string) {
|
|
|
|
|
|
const label = status === 'delivered' ? '已交付' : '已收款'
|
|
|
|
|
|
if (!confirm(`确认标记为${label}?`)) return
|
|
|
|
|
|
try {
|
|
|
|
|
|
await apiRequest(`/api/sales-orders/${orderId}/status`, {
|
|
|
|
|
|
method: 'PATCH',
|
|
|
|
|
|
body: JSON.stringify({ status })
|
|
|
|
|
|
})
|
|
|
|
|
|
addNotification(`已标记为${label}`, 'success')
|
|
|
|
|
|
loadProductionOrders()
|
|
|
|
|
|
loadMovements()
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
handleApiError(e, '更新状态')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function openConsumptionModal() {
|
|
|
|
|
|
consumptionItems.value = []
|
|
|
|
|
|
showConsumptionModal.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function addConsumptionItem() {
|
|
|
|
|
|
consumptionItems.value.push({
|
|
|
|
|
|
material_id: state.materials[0]?.id || null,
|
|
|
|
|
|
quantity: 1,
|
|
|
|
|
|
remark: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function removeConsumptionItem(index: number) {
|
|
|
|
|
|
consumptionItems.value.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const consumptionTotalAmount = computed(() => {
|
|
|
|
|
|
return consumptionItems.value.reduce((sum: number, item: any) => {
|
|
|
|
|
|
const material = state.materials.find((m: any) => m.id === item.material_id)
|
|
|
|
|
|
const unitPrice = material?.cost_price || 0
|
|
|
|
|
|
return sum + unitPrice * (item.quantity || 0)
|
|
|
|
|
|
}, 0)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
async function saveConsumption() {
|
|
|
|
|
|
if (!editingItem.value) return
|
|
|
|
|
|
if (!consumptionItems.value || consumptionItems.value.length === 0) {
|
|
|
|
|
|
addNotification('请至少添加一个消耗物料', 'warning')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
const payload = {
|
|
|
|
|
|
items: consumptionItems.value.map((item: any) => ({
|
|
|
|
|
|
material_id: item.material_id,
|
|
|
|
|
|
quantity: item.quantity,
|
|
|
|
|
|
unit_price: state.materials.find((m: any) => m.id === item.material_id)?.cost_price || 0,
|
|
|
|
|
|
remark: item.remark || ''
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
await apiRequest(`/api/sales-orders/${editingItem.value.id}/consume-materials`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
body: JSON.stringify(payload)
|
|
|
|
|
|
})
|
|
|
|
|
|
addNotification('物料消耗记录保存成功', 'success')
|
|
|
|
|
|
showConsumptionModal.value = false
|
|
|
|
|
|
try {
|
|
|
|
|
|
const consumptionData = await apiRequest(`/api/sales-orders/${editingItem.value.id}/consume-materials`)
|
|
|
|
|
|
consumedMaterials.value = (consumptionData.items || []).map((item: any) => ({
|
|
|
|
|
|
material_id: item.material_id,
|
|
|
|
|
|
material_name: item.material_name || item.material_sku || '',
|
|
|
|
|
|
quantity: item.quantity,
|
|
|
|
|
|
unit_price: item.unit_price || 0,
|
|
|
|
|
|
remark: item.remark || ''
|
|
|
|
|
|
}))
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
consumedMaterials.value = []
|
|
|
|
|
|
}
|
|
|
|
|
|
loadInventory()
|
|
|
|
|
|
loadMovements()
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
handleApiError(e, '保存物料消耗')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function closeModal() {
|
|
|
|
|
|
showModal.value = false
|
|
|
|
|
|
editingItem.value = null
|
|
|
|
|
|
form.value = {}
|
|
|
|
|
|
moldItems.value = []
|
|
|
|
|
|
consumedMaterials.value = []
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function closeConsumptionModal() {
|
|
|
|
|
|
showConsumptionModal.value = false
|
|
|
|
|
|
consumptionItems.value = []
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function initData() {
|
|
|
|
|
|
await loadCustomers()
|
|
|
|
|
|
await loadMaterials()
|
|
|
|
|
|
await loadFinishedProducts()
|
2026-06-04 17:51:11 +08:00
|
|
|
|
await loadProductionOrders()
|
2026-06-02 15:56:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-04 17:51:11 +08:00
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
initData()
|
|
|
|
|
|
})
|
2026-06-02 15:56:04 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div style="margin-bottom: 12px; display: flex; gap: 12px; align-items: center;">
|
|
|
|
|
|
<el-select
|
|
|
|
|
|
v-model="productionWarehouseId"
|
|
|
|
|
|
placeholder="选择仓库"
|
|
|
|
|
|
style="width: 200px;"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="warehouse in state.warehouses"
|
|
|
|
|
|
:key="warehouse.id"
|
|
|
|
|
|
:label="warehouse.name"
|
|
|
|
|
|
:value="warehouse.id"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
<el-button @click="loadProductionOrders">刷新</el-button>
|
|
|
|
|
|
<el-button type="primary" @click="openCreateOrder">新增销售订单</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-table :data="state.productionOrders" v-loading="state.loading" stripe>
|
|
|
|
|
|
<el-table-column prop="order_no" label="销售单" />
|
|
|
|
|
|
<el-table-column prop="customer_name" label="客户" />
|
|
|
|
|
|
<el-table-column label="订单金额">
|
|
|
|
|
|
<template #default="{ row }">{{ formatCurrency(row.total_amount) }}</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="交付日期">
|
|
|
|
|
|
<template #default="{ row }">{{ formatDate(row.delivery_date) }}</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="订单创建">
|
|
|
|
|
|
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="实际交付">
|
|
|
|
|
|
<template #default="{ row }">{{ row.delivered_at ? formatDateTime(row.delivered_at) : '-' }}</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="实际收款">
|
|
|
|
|
|
<template #default="{ row }">{{ row.paid_at ? formatDateTime(row.paid_at) : '-' }}</template>
|
|
|
|
|
|
</el-table-column>
|
2026-06-09 13:44:53 +08:00
|
|
|
|
<el-table-column label="订单状态" width="160">
|
2026-06-02 15:56:04 +08:00
|
|
|
|
<template #default="{ row }">
|
2026-06-09 13:44:53 +08:00
|
|
|
|
<div style="display: flex; gap: 4px;">
|
|
|
|
|
|
<el-tag
|
|
|
|
|
|
:type="row.delivery_status === 'delivered' ? 'warning' : row.delivery_status === 'cancelled' ? 'danger' : 'info'"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
>{{ getDeliveryStatusLabel(row.delivery_status) }}</el-tag>
|
|
|
|
|
|
<el-tag
|
|
|
|
|
|
:type="row.payment_status === 'paid' ? 'success' : 'info'"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
>{{ getPaymentStatusLabel(row.payment_status) }}</el-tag>
|
|
|
|
|
|
</div>
|
2026-06-02 15:56:04 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="操作" width="240">
|
|
|
|
|
|
<template #default="{ row }">
|
2026-06-09 13:44:53 +08:00
|
|
|
|
<el-dropdown style="margin-right: 8px;" v-if="row.delivery_status !== 'cancelled' && row.payment_status !== 'paid'">
|
|
|
|
|
|
<el-button size="small">状态 ▾</el-button>
|
2026-06-02 15:56:04 +08:00
|
|
|
|
<template #dropdown>
|
|
|
|
|
|
<el-dropdown-menu>
|
2026-06-09 13:44:53 +08:00
|
|
|
|
<el-dropdown-item v-if="row.delivery_status === 'manufacturing'" @click="updateStatus(row.id, 'delivered')">已交付</el-dropdown-item>
|
|
|
|
|
|
<el-dropdown-item v-if="row.delivery_status === 'delivered' && row.payment_status !== 'paid'" @click="updateStatus(row.id, 'paid')">已收款</el-dropdown-item>
|
|
|
|
|
|
<el-dropdown-item v-if="row.payment_status !== 'paid'" @click="updateStatus(row.id, 'cancelled')" style="color: #f56c6c;">作废</el-dropdown-item>
|
2026-06-02 15:56:04 +08:00
|
|
|
|
</el-dropdown-menu>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dropdown>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
size="small"
|
2026-06-09 13:44:53 +08:00
|
|
|
|
:disabled="row.payment_status === 'paid' || row.delivery_status === 'cancelled'"
|
2026-06-02 15:56:04 +08:00
|
|
|
|
@click="editOrder(row)"
|
|
|
|
|
|
>
|
|
|
|
|
|
编辑
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
type="danger"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
@click="deleteOrder(row.id)"
|
|
|
|
|
|
>
|
|
|
|
|
|
删除
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
2026-06-04 17:51:11 +08:00
|
|
|
|
<el-empty v-if="!state.loading && state.productionOrders.length === 0" description="暂无销售订单" />
|
2026-06-02 15:56:04 +08:00
|
|
|
|
|
|
|
|
|
|
<el-dialog
|
|
|
|
|
|
v-model="showModal"
|
|
|
|
|
|
:title="(editingItem ? '编辑' : '新增') + '销售订单'"
|
|
|
|
|
|
width="900px"
|
|
|
|
|
|
@closed="closeModal"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-form label-width="100px">
|
|
|
|
|
|
<el-form-item label="客户" required>
|
|
|
|
|
|
<el-select v-model="form.customer_id" placeholder="请选择客户" style="width:100%">
|
|
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="customer in state.customers"
|
|
|
|
|
|
:key="customer.id"
|
|
|
|
|
|
:label="customer.name"
|
|
|
|
|
|
:value="customer.id"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="交付日期">
|
|
|
|
|
|
<el-date-picker
|
|
|
|
|
|
v-model="form.delivery_date"
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
placeholder="选择日期"
|
|
|
|
|
|
style="width:100%"
|
|
|
|
|
|
value-format="YYYY-MM-DD"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="备注">
|
|
|
|
|
|
<el-input v-model="form.remark" placeholder="备注信息" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
|
|
|
|
|
|
<span style="font-weight: 600;">模具明细</span>
|
|
|
|
|
|
<div style="display: flex; gap: 8px;">
|
|
|
|
|
|
<el-button v-if="editingItem" type="warning" size="small" @click="openConsumptionModal">+ 消耗物料</el-button>
|
|
|
|
|
|
<el-button type="primary" size="small" @click="addMoldItem">+ 添加模具</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-table :data="moldItems" border size="small">
|
|
|
|
|
|
<el-table-column label="模式" width="80">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
<el-select v-model="line.mold_mode" size="small" style="width:100%">
|
|
|
|
|
|
<el-option label="新模" value="new" />
|
|
|
|
|
|
<el-option label="改模" value="existing" />
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="模具SKU" min-width="120">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
<el-input v-if="line.mold_mode === 'new'" v-model="line.mold_sku" size="small" placeholder="模具SKU" />
|
|
|
|
|
|
<el-select v-else v-model="line.mold_id" placeholder="选择模具" size="small" style="width:100%">
|
|
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="product in state.finishedProducts"
|
|
|
|
|
|
:key="product.id"
|
|
|
|
|
|
:label="`${product.sku} - ${product.name}`"
|
|
|
|
|
|
:value="product.id"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="模具名称" min-width="120">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-if="line.mold_mode === 'new'"
|
|
|
|
|
|
v-model="line.mold_name"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
placeholder="模具名称"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<span v-else style="padding-left: 8px;">
|
|
|
|
|
|
{{ state.finishedProducts.find((p: any) => p.id === line.mold_id)?.name || '-' }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="数量" width="100">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
<el-input-number v-model="line.quantity" :min="1" size="small" style="width:100%" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="单价" width="120">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
<el-input-number v-model="line.unit_price" :min="0" :precision="2" :step="0.01" size="small" style="width:100%" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="备注" width="120">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
<el-input v-model="line.remark" size="small" placeholder="备注" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="操作" width="80">
|
|
|
|
|
|
<template #default="{ $index }">
|
|
|
|
|
|
<el-button type="danger" size="small" @click="removeMoldItem($index)">删除</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
<div style="text-align: right; margin-top: 8px; font-weight: 600;">
|
|
|
|
|
|
订单总金额:{{ formatCurrency(orderTotalAmount) }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<template v-if="editingItem && consumedMaterials.length > 0">
|
|
|
|
|
|
<el-divider content-position="left">已消耗物料</el-divider>
|
|
|
|
|
|
<el-table :data="consumedMaterials" border size="small">
|
|
|
|
|
|
<el-table-column label="物料">
|
|
|
|
|
|
<template #default="{ row: item }">{{ item.material_name }}</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="数量">
|
|
|
|
|
|
<template #default="{ row: item }">{{ item.quantity }}</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="单价">
|
|
|
|
|
|
<template #default="{ row: item }">{{ formatCurrency(item.unit_price) }}</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="备注">
|
|
|
|
|
|
<template #default="{ row: item }">{{ item.remark || '-' }}</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<el-button @click="showModal = false">取消</el-button>
|
|
|
|
|
|
<el-button type="primary" @click="saveSalesOrder">保存</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<el-dialog
|
|
|
|
|
|
v-model="showConsumptionModal"
|
|
|
|
|
|
title="物料消耗"
|
|
|
|
|
|
width="800px"
|
|
|
|
|
|
@closed="closeConsumptionModal"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
|
|
|
|
|
|
<span style="font-weight: 600;">消耗物料明细</span>
|
|
|
|
|
|
<el-button type="primary" size="small" @click="addConsumptionItem">+ 添加物料</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-table :data="consumptionItems" border size="small">
|
|
|
|
|
|
<el-table-column label="物料" min-width="200">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
<el-select v-model="line.material_id" placeholder="请选择物料" style="width:100%">
|
|
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="material in state.materials"
|
|
|
|
|
|
:key="material.id"
|
|
|
|
|
|
:label="`${material.sku} - ${material.name}`"
|
|
|
|
|
|
:value="material.id"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="数量" width="120">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
<el-input-number v-model="line.quantity" :min="0.0001" :step="0.0001" size="small" style="width:100%" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="单价" width="120">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
<el-input-number
|
|
|
|
|
|
:model-value="state.materials.find((m: any) => m.id === line.material_id)?.cost_price || 0"
|
|
|
|
|
|
disabled
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
style="width:100%"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="总价" width="120">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
{{ formatCurrency((state.materials.find((m: any) => m.id === line.material_id)?.cost_price || 0) * (line.quantity || 0)) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="备注" width="150">
|
|
|
|
|
|
<template #default="{ row: line }">
|
|
|
|
|
|
<el-input v-model="line.remark" size="small" placeholder="备注" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="操作" width="80">
|
|
|
|
|
|
<template #default="{ $index }">
|
|
|
|
|
|
<el-button type="danger" size="small" @click="removeConsumptionItem($index)">删除</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
<div style="text-align: right; margin-top: 8px; font-weight: 600;">
|
|
|
|
|
|
消耗总金额:{{ formatCurrency(consumptionTotalAmount) }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<el-button @click="showConsumptionModal = false">取消</el-button>
|
|
|
|
|
|
<el-button type="primary" @click="saveConsumption">保存消耗</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
</style>
|