xxx
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { useInventory } from '@/modules/inventory/composables/useInventory'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useInventoryStore } from '@/stores/inventory'
|
||||
|
||||
const { state, menuGroups, openGroups, toggleGroup, handleMenuClick } = useInventory()
|
||||
const router = useRouter()
|
||||
const store = useInventoryStore()
|
||||
const { menuGroups, openGroups, toggleGroup } = store
|
||||
|
||||
const navigateTo = (itemKey: string) => {
|
||||
store.activeTab = itemKey
|
||||
router.push(`/inventory/${itemKey}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -15,8 +23,8 @@ const { state, menuGroups, openGroups, toggleGroup, handleMenuClick } = useInven
|
||||
<div
|
||||
v-for="item in group.items"
|
||||
:key="item.key"
|
||||
:class="['sidebar-item', { active: state.activeTab === item.key }]"
|
||||
@click="handleMenuClick(item.key)"
|
||||
:class="['sidebar-item', { active: store.activeTab === item.key }]"
|
||||
@click="navigateTo(item.key)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,9 @@ import { ref, computed, onMounted } from 'vue'
|
||||
import { DialogPlugin } from 'tdesign-vue-next'
|
||||
import { useInventory } from '../composables/useInventory'
|
||||
import { apiRequest } from '@/shared/api'
|
||||
import { inventoryApi } from '@/shared/api-client'
|
||||
import { addNotification, handleApiError } from '@/shared/notification'
|
||||
import type { Schema } from '@/types/schemas'
|
||||
|
||||
function confirmDialog(header: string, body: string, theme: string, confirmText: string, cancelText: string): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
@@ -47,6 +49,46 @@ const receiveWarehouseId = ref<number | null>(null)
|
||||
const receiveRemark = ref('')
|
||||
const receiving = ref(false)
|
||||
|
||||
// ── 采购需求推导 ──
|
||||
const showDemandModal = ref(false)
|
||||
const demandLoading = ref(false)
|
||||
const salesOrdersForDemand = ref<Schema<'SalesOrderResponse'>[]>([])
|
||||
const selectedSalesOrderIds = ref<number[]>([])
|
||||
const demandResult = ref<Schema<'PurchaseDemandResponse'> | null>(null)
|
||||
|
||||
async function openDemandDialog() {
|
||||
showDemandModal.value = true
|
||||
demandResult.value = null
|
||||
selectedSalesOrderIds.value = []
|
||||
try {
|
||||
const data = await inventoryApi.listSalesOrders({ limit: 100 })
|
||||
salesOrdersForDemand.value = (data as any)?.items || data || []
|
||||
} catch (e) {
|
||||
handleApiError(e, '加载销售订单')
|
||||
}
|
||||
}
|
||||
|
||||
async function calculateDemands() {
|
||||
if (!selectedSalesOrderIds.value.length) {
|
||||
addNotification('请选择至少一个销售订单', 'warning')
|
||||
return
|
||||
}
|
||||
demandLoading.value = true
|
||||
try {
|
||||
demandResult.value = await inventoryApi.calculatePurchaseDemands(selectedSalesOrderIds.value)
|
||||
} catch (e) {
|
||||
handleApiError(e, '计算采购需求')
|
||||
} finally {
|
||||
demandLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function closeDemandModal() {
|
||||
showDemandModal.value = false
|
||||
demandResult.value = null
|
||||
selectedSalesOrderIds.value = []
|
||||
}
|
||||
|
||||
function openCreateOrder() {
|
||||
editingItem.value = null
|
||||
form.value = {
|
||||
@@ -299,6 +341,7 @@ onMounted(() => {
|
||||
</t-select>
|
||||
<t-button @click="loadPurchaseOrders">刷新</t-button>
|
||||
<t-button type="primary" @click="openCreateOrder">新增采购订单</t-button>
|
||||
<t-button theme="success" variant="outline" @click="openDemandDialog">采购建议</t-button>
|
||||
</div>
|
||||
|
||||
<t-table :data="state.purchaseOrders" :loading="state.loading" stripe>
|
||||
@@ -515,6 +558,87 @@ onMounted(() => {
|
||||
<t-button type="primary" :loading="receiving" :disabled="receiving" @click="receivePurchaseOrder">确认入库</t-button>
|
||||
</template>
|
||||
</t-dialog>
|
||||
<!-- 采购需求推导对话框 -->
|
||||
<t-dialog
|
||||
v-model:visible="showDemandModal"
|
||||
title="采购需求推导"
|
||||
width="1000px"
|
||||
@closed="closeDemandModal"
|
||||
>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<p style="margin-bottom: 8px; color: var(--td-text-color-secondary);">
|
||||
选择销售订单,系统将自动按 BOM 展开物料需求、对比库存、推荐供应商。
|
||||
</p>
|
||||
<div style="display: flex; gap: 12px; align-items: center;">
|
||||
<t-select
|
||||
v-model="selectedSalesOrderIds"
|
||||
multiple
|
||||
placeholder="请选择销售订单"
|
||||
style="flex: 1;"
|
||||
:loading="salesOrdersForDemand.length === 0"
|
||||
>
|
||||
<t-option
|
||||
v-for="order in salesOrdersForDemand"
|
||||
:key="order.id"
|
||||
:label="`${order.order_no} - ${order.customer_name || ''}`"
|
||||
:value="order.id"
|
||||
/>
|
||||
</t-select>
|
||||
<t-button
|
||||
type="primary"
|
||||
:loading="demandLoading"
|
||||
:disabled="!selectedSalesOrderIds.length"
|
||||
@click="calculateDemands"
|
||||
>
|
||||
计算
|
||||
</t-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="demandResult">
|
||||
<div style="display: flex; gap: 24px; margin-bottom: 12px; font-weight: 600;">
|
||||
<span>来源订单:{{ demandResult.source_order_nos?.join(', ') || '-' }}</span>
|
||||
<span>缺货物料:<t-tag :type="demandResult.shortage_count > 0 ? 'danger' : 'success'" size="small">{{ demandResult.shortage_count }}</t-tag></span>
|
||||
<span>预计采购总额:{{ formatCurrency(Number(demandResult.total_estimated_cost || 0)) }}</span>
|
||||
</div>
|
||||
|
||||
<t-table :data="demandResult.items || []" stripe border size="small" max-height="400">
|
||||
<t-table-column prop="material_sku" label="物料SKU" width="120" />
|
||||
<t-table-column prop="material_name" label="物料名称" min-width="140" />
|
||||
<t-table-column label="需求量" width="90">
|
||||
<template #default="{ row }">{{ row.required_quantity }}</template>
|
||||
</t-table-column>
|
||||
<t-table-column label="库存量" width="90">
|
||||
<template #default="{ row }">{{ row.available_quantity }}</template>
|
||||
</t-table-column>
|
||||
<t-table-column label="缺口" width="90">
|
||||
<template #default="{ row }">
|
||||
<t-tag :type="row.shortage_quantity > 0 ? 'danger' : 'success'" size="small">
|
||||
{{ row.shortage_quantity }}
|
||||
</t-tag>
|
||||
</template>
|
||||
</t-table-column>
|
||||
<t-table-column label="单价" width="90">
|
||||
<template #default="{ row }">{{ formatCurrency(Number(row.unit_cost || 0)) }}</template>
|
||||
</t-table-column>
|
||||
<t-table-column label="预计金额" width="110">
|
||||
<template #default="{ row }">{{ formatCurrency(Number(row.estimated_cost || 0)) }}</template>
|
||||
</t-table-column>
|
||||
<t-table-column prop="suggested_supplier_name" label="建议供应商" min-width="120">
|
||||
<template #default="{ row }">{{ row.suggested_supplier_name || '-' }}</template>
|
||||
</t-table-column>
|
||||
<t-table-column label="交期(天)" width="80">
|
||||
<template #default="{ row }">{{ row.supplier_lead_time ?? '-' }}</template>
|
||||
</t-table-column>
|
||||
</t-table>
|
||||
|
||||
<t-empty v-if="(demandResult.items || []).length === 0" description="无物料需求(BOM 为空或订单无明细)" />
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<t-button @click="showDemandModal = false">关闭</t-button>
|
||||
</template>
|
||||
</t-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user