From 1ea7a89ba34756154470cab6c41abe4e058d174e Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 03 十一月 2026 20:09:49 +0800
Subject: [PATCH] feat: 完善仓储废料和合同导出界面

---
 src/views/wls/warehouse/data.ts         |   21 ++++++++++
 src/views/wls/materialstock/data.ts     |    5 ++
 src/views/crm/contract/index.vue        |    2 
 src/views/wls/scrap/index.vue           |   84 ++++++++++++++++++++++++++++++++++++++++++
 src/api/mes/wm/warehouse/index.ts       |    1 
 src/views/crm/contract/detail/index.vue |    2 
 6 files changed, 113 insertions(+), 2 deletions(-)

diff --git a/src/api/mes/wm/warehouse/index.ts b/src/api/mes/wm/warehouse/index.ts
index 661b62e..be5cbb5 100644
--- a/src/api/mes/wm/warehouse/index.ts
+++ b/src/api/mes/wm/warehouse/index.ts
@@ -8,6 +8,7 @@
     id?: number; // 浠撳簱缂栧彿
     code?: string; // 浠撳簱缂栫爜
     name?: string; // 浠撳簱鍚嶇О
+    warehouseType?: string; // 浠撳簱绫诲瀷
     address?: string; // 鍦板潃
     area?: number; // 闈㈢Н
     chargeUserId?: number; // 璐熻矗浜�
diff --git a/src/views/crm/contract/detail/index.vue b/src/views/crm/contract/detail/index.vue
index cd14401..3aac028 100644
--- a/src/views/crm/contract/detail/index.vue
+++ b/src/views/crm/contract/detail/index.vue
@@ -120,7 +120,7 @@
             onClick: handleEdit,
           },
           {
-            label: '涓嬭浇鍚堝悓',
+            label: '瀵煎嚭 Word',
             type: 'primary',
             icon: 'lucide:download',
             auth: ['crm:contract:query'],
diff --git a/src/views/crm/contract/index.vue b/src/views/crm/contract/index.vue
index cd8650e..10e2b45 100644
--- a/src/views/crm/contract/index.vue
+++ b/src/views/crm/contract/index.vue
@@ -333,7 +333,7 @@
         <TableAction
           :actions="[
             {
-              label: '涓嬭浇鍚堝悓',
+              label: '瀵煎嚭 Word',
               type: 'link',
               icon: 'lucide:download',
               auth: ['crm:contract:query'],
diff --git a/src/views/wls/materialstock/data.ts b/src/views/wls/materialstock/data.ts
index 4887418..1ea0bc9 100644
--- a/src/views/wls/materialstock/data.ts
+++ b/src/views/wls/materialstock/data.ts
@@ -104,6 +104,11 @@
       minWidth: 120,
     },
     {
+      field: 'technicalParameters',
+      title: '鎶�鏈弬鏁�',
+      minWidth: 160,
+    },
+    {
       field: 'quantity',
       title: '鍦ㄥ簱鏁伴噺',
       width: 100,
diff --git a/src/views/wls/scrap/index.vue b/src/views/wls/scrap/index.vue
new file mode 100644
index 0000000..c9e1ada
--- /dev/null
+++ b/src/views/wls/scrap/index.vue
@@ -0,0 +1,84 @@
+<script lang="ts" setup>
+import { onMounted, reactive, ref } from 'vue';
+import { Page } from '@vben/common-ui';
+import { Button, Card, DatePicker, Form, FormItem, Input, InputNumber, Modal, Space, Table, Tabs, TabPane, message } from 'ant-design-vue';
+import dayjs from 'dayjs';
+import { requestClient } from '#/api/request';
+import { MdItemSelect } from '#/views/mes/md/item/components';
+import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components';
+import { WmMaterialStockSelect } from '#/views/wls/materialstock/components';
+import { WmWarehouseAreaSelect, WmWarehouseLocationSelect, WmWarehouseSelect } from '#/views/wls/warehouse/components';
+
+defineOptions({ name: 'WlsScrap' });
+const receiptOpen = ref(false); const saleOpen = ref(false); const detailsOpen = ref(false); const saleLines = ref<any[]>([]); const busy = ref(false);
+const receiptRows = ref<any[]>([]); const saleRows = ref<any[]>([]); const summaryRows = ref<any[]>([]);
+const month = ref(dayjs());
+const receiptPage = reactive({ current: 1, pageSize: 20, total: 0 });
+const salePage = reactive({ current: 1, pageSize: 20, total: 0 });
+const receipt = reactive<any>({}); const sale = reactive<any>({});
+const receiptColumns = [{title:'鍏ュ簱鍗曞彿',dataIndex:'code'}, {title:'鐢熶骇璁㈠崟鍙�',dataIndex:'workOrderCode'}, {title:'鐗╂枡缂栧彿',dataIndex:'itemId'}, {title:'鏁伴噺',dataIndex:'quantity'}, {title:'搴撲綅缂栧彿',dataIndex:'areaId'}, {title:'鍏ュ簱鏃堕棿',dataIndex:'receiptTime'}];
+const saleColumns = [{title:'鍑哄敭鍗曞彿',dataIndex:'code'}, {title:'璐拱鏂�',dataIndex:'buyer'}, {title:'鐘舵��',dataIndex:'status'}, {title:'鍒涘缓鏃堕棿',dataIndex:'createTime'}, {title:'鎿嶄綔',key:'actions'}];
+const summaryColumns = [{title:'鐢熶骇璁㈠崟鍙�',dataIndex:'workOrderCode'}, {title:'鐗╂枡缂栧彿',dataIndex:'itemId'}, {title:'搴熸枡鏁伴噺',dataIndex:'quantity'}];
+const statusText: Record<number,string> = {0:'鑽夌',1:'瀹℃壒涓�',2:'宸插嚭搴�',3:'宸查┏鍥�',4:'宸插彇娑�'};
+async function loadReceipts() { const r: any = await requestClient.get('/mes/wm/scrap-receipt/page', {params:{pageNo:receiptPage.current,pageSize:receiptPage.pageSize}}); receiptRows.value = r.list; receiptPage.total = r.total; }
+async function loadSales() { const r: any = await requestClient.get('/mes/wm/scrap-sale/page', {params:{pageNo:salePage.current,pageSize:salePage.pageSize}}); saleRows.value = r.list; salePage.total = r.total; }
+async function loadSummary() { if (month.value) summaryRows.value = await requestClient.get('/mes/wm/scrap-receipt/monthly-summary', {params:{month:month.value.format('YYYY-MM')}}); }
+function newRequestId() { return globalThis.crypto?.randomUUID?.() ?? 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replaceAll(/[xy]/g, c => { const n = Math.trunc(Math.random() * 16); return (c === 'x' ? n : (n & 3) | 8).toString(16); }); }
+async function showDetails(row:any) { saleLines.value = await requestClient.get('/mes/wm/scrap-sale/lines', {params:{id:row.id}}); detailsOpen.value = true; }
+function openReceipt() { Object.keys(receipt).forEach(k=>delete receipt[k]); Object.assign(receipt,{requestId:newRequestId(),quantity:1}); receiptOpen.value=true; }
+function openSale() { Object.keys(sale).forEach(k=>delete sale[k]); Object.assign(sale,{requestId:newRequestId(),quantity:1,unitPrice:0}); saleOpen.value=true; }
+async function saveReceipt() {
+ if (!receipt.workOrderId || !receipt.itemId || !receipt.warehouseId || !receipt.locationId || !receipt.areaId || !(receipt.quantity > 0)) { message.warning('璇峰~鍐欑敓浜ц鍗曘�佺墿鏂欍�佸簱浣嶅拰鏁伴噺'); return; }
+ busy.value=true; try { await requestClient.post('/mes/wm/scrap-receipt/create',receipt); receiptOpen.value=false; message.success('鍏ュ簱鎴愬姛'); await loadReceipts(); } finally {busy.value=false;}
+}
+async function saveSale() {
+ if (!sale.buyer?.trim() || !sale.materialStockId || !(sale.quantity > 0) || sale.unitPrice == null || sale.unitPrice < 0) { message.warning('璇峰~鍐欒喘涔版柟銆佸簱瀛樸�佹暟閲忓拰鍗曚环'); return; }
+ busy.value=true; try { await requestClient.post('/mes/wm/scrap-sale/create',{requestId:sale.requestId,buyer:sale.buyer,remark:sale.remark,lines:[{materialStockId:sale.materialStockId,quantity:sale.quantity,unitPrice:sale.unitPrice}]}); saleOpen.value=false; message.success('鍑哄敭鍗曞凡鍒涘缓'); await loadSales(); } finally {busy.value=false;}
+}
+async function submitSale(row:any) { await requestClient.put('/mes/wm/scrap-sale/submit',null,{params:{id:row.id}}); message.success('宸叉彁浜よ储鍔″鎵�'); await loadSales(); }
+onMounted(async()=>{ await Promise.all([loadReceipts(),loadSales(),loadSummary()]); });
+</script>
+<template>
+ <Page auto-content-height>
+  <Card title="搴熸枡绠$悊">
+   <Tabs>
+    <TabPane key="receipts" tab="搴熸枡鍏ュ簱">
+     <Space style="margin-bottom: 16px"><Button type="primary" @click="openReceipt">鐧昏鍏ュ簱</Button><Button @click="loadReceipts">鍒锋柊</Button></Space>
+     <Table :data-source="receiptRows" :columns="receiptColumns" row-key="id" :pagination="receiptPage" :scroll="{x:900}" @change="(p:any)=>{receiptPage.current=p.current;receiptPage.pageSize=p.pageSize;loadReceipts()}" />
+    </TabPane>
+    <TabPane key="sales" tab="搴熸枡鍑哄敭">
+     <Space style="margin-bottom: 16px"><Button type="primary" @click="openSale">鏂板缓鍑哄敭鍗�</Button><Button @click="loadSales">鍒锋柊</Button></Space>
+     <Table :data-source="saleRows" :columns="saleColumns" row-key="id" :pagination="salePage" :scroll="{x:800}" @change="(p:any)=>{salePage.current=p.current;salePage.pageSize=p.pageSize;loadSales()}">
+      <template #bodyCell="{column,record}"><template v-if="column.dataIndex === 'status'">{{ statusText[record.status] || record.status }}</template><template v-else-if="column.key === 'actions'"><Button type="link" @click="showDetails(record)">鏄庣粏</Button><Button v-if="record.status === 0" type="link" @click="submitSale(record)">鎻愪氦璐㈠姟瀹℃壒</Button></template></template>
+     </Table>
+    </TabPane>
+    <TabPane key="summary" tab="鎸夎鍗曠粺璁�">
+     <Space style="margin-bottom: 16px"><DatePicker v-model:value="month" picker="month" :allow-clear="false" /><Button @click="loadSummary">鏌ヨ</Button></Space>
+     <Table :data-source="summaryRows" :columns="summaryColumns" :pagination="false" :row-key="(r:any)=>`${r.workOrderId}-${r.itemId}`" />
+    </TabPane>
+   </Tabs>
+  </Card>
+  <Modal v-model:open="receiptOpen" title="鐧昏搴熸枡鍏ュ簱" :confirm-loading="busy" @ok="saveReceipt">
+   <Form layout="vertical">
+    <FormItem label="鐢熶骇璁㈠崟" required><ProWorkOrderSelect v-model="receipt.workOrderId" /></FormItem>
+    <FormItem label="搴熸枡鐗╂枡" required><MdItemSelect v-model="receipt.itemId" /></FormItem>
+    <FormItem label="浠撳簱" required><WmWarehouseSelect v-model="receipt.warehouseId" @change="receipt.locationId=undefined;receipt.areaId=undefined" /></FormItem>
+    <FormItem label="搴撳尯" required><WmWarehouseLocationSelect v-model="receipt.locationId" :warehouse-id="receipt.warehouseId" @change="receipt.areaId=undefined" /></FormItem>
+    <FormItem label="搴熸枡搴撲綅" required><WmWarehouseAreaSelect v-model="receipt.areaId" :location-id="receipt.locationId" /></FormItem>
+    <FormItem label="鏁伴噺" required><InputNumber v-model:value="receipt.quantity" :min="0.0001" :precision="4" style="width:100%" /></FormItem>
+    <FormItem label="鎵规鍙�"><Input v-model:value="receipt.batchCode" :maxlength="100" /></FormItem>
+    <FormItem label="澶囨敞"><Input.TextArea v-model:value="receipt.remark" :maxlength="500" /></FormItem>
+   </Form>
+  </Modal>
+  <Modal v-model:open="detailsOpen" title="鍑哄敭鏄庣粏" :footer="null"><Table :data-source="saleLines" row-key="id" :pagination="false" :columns="[{title:'搴撳瓨缂栧彿',dataIndex:'materialStockId'},{title:'鏁伴噺',dataIndex:'quantity'},{title:'鍗曚环',dataIndex:'unitPrice'},{title:'鍑哄簱娴佹按缂栧彿',dataIndex:'transactionId'}]" /></Modal>
+  <Modal v-model:open="saleOpen" title="鏂板缓搴熸枡鍑哄敭鍗�" :confirm-loading="busy" @ok="saveSale">
+   <Form layout="vertical">
+    <FormItem label="璐拱鏂�" required><Input v-model:value="sale.buyer" :maxlength="200" /></FormItem>
+    <FormItem label="搴熸枡搴撳瓨" required><WmMaterialStockSelect v-model="sale.materialStockId" /></FormItem>
+    <FormItem label="鏁伴噺" required><InputNumber v-model:value="sale.quantity" :min="0.0001" :precision="4" style="width:100%" /></FormItem>
+    <FormItem label="鍗曚环" required><InputNumber v-model:value="sale.unitPrice" :min="0" :precision="4" style="width:100%" /></FormItem>
+    <FormItem label="澶囨敞"><Input.TextArea v-model:value="sale.remark" :maxlength="500" /></FormItem>
+   </Form>
+  </Modal>
+ </Page>
+</template>
diff --git a/src/views/wls/warehouse/data.ts b/src/views/wls/warehouse/data.ts
index c702cd9..7b48e4c 100644
--- a/src/views/wls/warehouse/data.ts
+++ b/src/views/wls/warehouse/data.ts
@@ -70,6 +70,16 @@
       rules: 'required',
     },
     {
+      fieldName: 'warehouseType',
+      label: '浠撳簱绫诲瀷',
+      component: 'Input',
+      componentProps: {
+        allowClear: true,
+        maxlength: 50,
+        placeholder: '渚嬪鍘熸枡浠撱�佸崐鎴愬搧浠撱�佹垚鍝佷粨鎴栧簾鏂欎粨',
+      },
+    },
+    {
       fieldName: 'chargeUserId',
       label: '璐熻矗浜�',
       component: 'ApiSelect',
@@ -146,6 +156,12 @@
       },
     },
     {
+      fieldName: 'warehouseType',
+      label: '浠撳簱绫诲瀷',
+      component: 'Input',
+      componentProps: { allowClear: true, placeholder: '璇疯緭鍏ヤ粨搴撶被鍨�' },
+    },
+    {
       fieldName: 'frozen',
       label: '鏄惁鍐荤粨',
       component: 'Select',
@@ -176,6 +192,11 @@
       minWidth: 160,
     },
     {
+      field: 'warehouseType',
+      title: '浠撳簱绫诲瀷',
+      minWidth: 120,
+    },
+    {
       field: 'address',
       title: '浠撳簱鍦板潃',
       minWidth: 180,

--
Gitblit v1.9.3