From bef3d94968fc70667f3123cb23ee69550e08a5ca Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 15 十月 2026 14:03:17 +0800
Subject: [PATCH] feat(crm): 优化合同管理及待办功能

---
 src/views/crm/backlog/index.vue                                 |   40 +++++--
 src/views/crm/backlog/modules/receivable-plan-remind-list.vue   |   30 ++++-
 src/api/crm/contract/index.ts                                   |    5 +
 src/views/mes/md/workstation/workshop/data.ts                   |   15 ---
 src/views/crm/backlog/modules/customer-follow-list.vue          |    5 +
 src/views/crm/backlog/modules/customer-put-pool-remind-list.vue |   10 +
 src/views/crm/saleQuotation/index.vue                           |   16 +++
 src/views/crm/backlog/modules/receivable-audit-list.vue         |   41 +++++--
 src/utils/contract-document.ts                                  |    8 +
 src/views/crm/backlog/modules/customer-today-contact-list.vue   |    5 +
 src/views/mes/md/workstation/components/workshop-select.vue     |    4 
 src/views/crm/backlog/modules/contract-audit-list.vue           |   36 ++++++-
 src/views/crm/contract/index.vue                                |    8 +
 src/views/crm/backlog/modules/contract-remind-list.vue          |   31 +++++
 src/views/crm/contract/detail/index.vue                         |    8 +
 src/views/mes/home/modules/alert-panel.vue                      |    8 -
 16 files changed, 196 insertions(+), 74 deletions(-)

diff --git a/src/api/crm/contract/index.ts b/src/api/crm/contract/index.ts
index 808b869..a37a8d7 100644
--- a/src/api/crm/contract/index.ts
+++ b/src/api/crm/contract/index.ts
@@ -93,6 +93,11 @@
   );
 }
 
+/** 鎸夎喘閿�鍚堝悓妯℃澘涓嬭浇 Word 鏂囦欢 */
+export function downloadContractDocument(id: number) {
+  return requestClient.download('/crm/contract/download-document', { params: { id } });
+}
+
 /** 鏌ヨ鍚堝悓璇︽儏 */
 export function getContract(id: number) {
   return requestClient.get<CrmContractApi.Contract>(
diff --git a/src/utils/contract-document.ts b/src/utils/contract-document.ts
new file mode 100644
index 0000000..f1e10c0
--- /dev/null
+++ b/src/utils/contract-document.ts
@@ -0,0 +1,8 @@
+import { downloadFileFromBlobPart } from '@vben/utils';
+import { downloadContractDocument } from '#/api/crm/contract';
+
+export async function downloadContractWord(id: number, no?: string) {
+  const source = await downloadContractDocument(id);
+  const suffix = (no || String(id)).replaceAll(/[\\/:*?"<>|]/g, '_');
+  downloadFileFromBlobPart({ fileName: `璐攢鍚堝悓-${suffix}.docx`, source });
+}
diff --git a/src/views/crm/backlog/index.vue b/src/views/crm/backlog/index.vue
index a183a22..432531f 100644
--- a/src/views/crm/backlog/index.vue
+++ b/src/views/crm/backlog/index.vue
@@ -1,4 +1,6 @@
 <script lang="ts" setup>
+import type { Ref } from 'vue';
+
 import { computed, onActivated, onMounted, ref, watch } from 'vue';
 
 import { Page } from '@vben/common-ui';
@@ -78,16 +80,32 @@
   leftMenu.value = item.menu;
 }
 
-/** 鑾峰彇鏁伴噺 */
-async function getCount() {
-  customerTodayContactCount.value = await getTodayContactCustomerCount();
-  customerPutPoolRemindCount.value = await getPutPoolRemindCustomerCount();
-  customerFollowCount.value = await getFollowCustomerCount();
-  clueFollowCount.value = await getFollowClueCount();
-  if (!automaticApproval.value) contractAuditCount.value = await getAuditContractCount();
-  contractRemindCount.value = await getRemindContractCount();
-  if (!automaticApproval.value) receivableAuditCount.value = await getAuditReceivableCount();
-  receivablePlanRemindCount.value = await getReceivablePlanRemindCount();
+/** 鐙珛鍔犺浇鍚勭被鏁伴噺锛屽崟涓帴鍙eけ璐ヤ笉闃绘柇鍏朵粬寰呭姙銆� */
+let countRequest: Promise<void> | undefined;
+function getCount() {
+  if (countRequest) return countRequest;
+  const requests: [Ref<number>, () => Promise<number>][] = [
+    [customerTodayContactCount, getTodayContactCustomerCount],
+    [customerPutPoolRemindCount, getPutPoolRemindCustomerCount],
+    [customerFollowCount, getFollowCustomerCount],
+    [clueFollowCount, getFollowClueCount],
+    [contractRemindCount, getRemindContractCount],
+    [receivablePlanRemindCount, getReceivablePlanRemindCount],
+  ];
+  if (!automaticApproval.value) {
+    requests.push(
+      [contractAuditCount, getAuditContractCount],
+      [receivableAuditCount, getAuditReceivableCount],
+    );
+  }
+  countRequest = Promise.allSettled(
+    requests.map(async ([count, request]) => {
+      count.value = await request();
+    }),
+  ).then(() => undefined).finally(() => {
+    countRequest = undefined;
+  });
+  return countRequest;
 }
 
 /** 婵�娲绘椂 */
@@ -126,7 +144,7 @@
           </template>
         </List>
       </Card>
-      <component class="ml-4 w-4/5" :is="currentComponent" />
+      <component class="ml-4 w-4/5" :is="currentComponent" @refresh="getCount" />
     </div>
   </Page>
 </template>
diff --git a/src/views/crm/backlog/modules/contract-audit-list.vue b/src/views/crm/backlog/modules/contract-audit-list.vue
index 98f944c..c43df5d 100644
--- a/src/views/crm/backlog/modules/contract-audit-list.vue
+++ b/src/views/crm/backlog/modules/contract-audit-list.vue
@@ -5,7 +5,7 @@
 
 import { useRouter } from 'vue-router';
 
-import { Button } from 'ant-design-vue';
+import { Button, Tag } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import { getContractPage } from '#/api/crm/contract';
@@ -29,17 +29,21 @@
 }
 /** 鎵撳紑瀹㈡埛璇︽儏 */
 function handleCustomerDetail(row: CrmContractApi.Contract) {
-  push({ name: 'CrmCustomerDetail', params: { id: row.id } });
+  if (row.customerId) push({ name: 'CrmCustomerDetail', params: { id: row.customerId } });
 }
 
 /** 鎵撳紑鑱旂郴浜鸿鎯� */
 function handleContactDetail(row: CrmContractApi.Contract) {
-  push({ name: 'CrmContactDetail', params: { id: row.id } });
+  if (row.signContactId) push({ name: 'CrmContactDetail', params: { id: row.signContactId } });
 }
 
 /** 鎵撳紑鍟嗘満璇︽儏 */
 function handleBusinessDetail(row: CrmContractApi.Contract) {
-  push({ name: 'CrmBusinessDetail', params: { id: row.id } });
+  if (row.businessId) push({ name: 'CrmBusinessDetail', params: { id: row.businessId } });
+}
+
+function handleViewOrder(row: CrmContractApi.Contract) {
+  if (row.orderId) push({ name: 'ErpSaleOrder', query: { id: row.orderId } });
 }
 
 const [Grid] = useVbenVxeGrid({
@@ -87,6 +91,11 @@
 
 <template>
   <Grid>
+    <template #no="{ row }">
+      <Button type="link" @click="handleContractDetail(row)">
+        {{ row.no }}
+      </Button>
+    </template>
     <template #name="{ row }">
       <Button type="link" @click="handleContractDetail(row)">
         {{ row.name }}
@@ -102,10 +111,23 @@
         {{ row.businessName }}
       </Button>
     </template>
-    <template #contactName="{ row }">
+    <template #signContactName="{ row }">
       <Button type="link" @click="handleContactDetail(row)">
-        {{ row.contactName }}
+        {{ row.signContactName }}
       </Button>
+    </template>
+    <template #auditStatus="{ row }">
+      <Tag v-if="row.auditStatus === 0">鏈彁浜�</Tag>
+      <Tag v-else-if="row.auditStatus === 10" color="warning">瀹℃壒涓�</Tag>
+      <Tag v-else-if="row.auditStatus === 20" color="success">瀹℃牳閫氳繃</Tag>
+      <Tag v-else-if="row.auditStatus === 30" color="error">瀹℃牳涓嶉�氳繃</Tag>
+      <Tag v-else-if="row.auditStatus === 40">宸插彇娑�</Tag>
+      <Tag v-else-if="row.auditStatus === 50" color="error">宸蹭綔搴�</Tag>
+      <span v-else>鈥�</span>
+    </template>
+    <template #orderNo="{ row }">
+      <Button v-if="row.orderId" type="link" @click="handleViewOrder(row)">{{ row.orderNo }}</Button>
+      <span v-else>鈥�</span>
     </template>
     <template #actions="{ row }">
       <TableAction
@@ -114,6 +136,8 @@
             label: '鏌ョ湅瀹℃壒',
             type: 'link',
             icon: ACTION_ICON.VIEW,
+            auth: ['crm:contract:update'],
+            ifShow: !!row.processInstanceId,
             onClick: handleProcessDetail.bind(null, row),
           },
         ]"
diff --git a/src/views/crm/backlog/modules/contract-remind-list.vue b/src/views/crm/backlog/modules/contract-remind-list.vue
index a394b3b..cdd4078 100644
--- a/src/views/crm/backlog/modules/contract-remind-list.vue
+++ b/src/views/crm/backlog/modules/contract-remind-list.vue
@@ -5,7 +5,7 @@
 
 import { useRouter } from 'vue-router';
 
-import { Button } from 'ant-design-vue';
+import { Button, Tag } from 'ant-design-vue';
 
 import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import { getContractPage } from '#/api/crm/contract';
@@ -30,17 +30,21 @@
 
 /** 鎵撳紑瀹㈡埛璇︽儏 */
 function handleCustomerDetail(row: CrmContractApi.Contract) {
-  push({ name: 'CrmCustomerDetail', params: { id: row.id } });
+  if (row.customerId) push({ name: 'CrmCustomerDetail', params: { id: row.customerId } });
 }
 
 /** 鎵撳紑鑱旂郴浜鸿鎯� */
 function handleContactDetail(row: CrmContractApi.Contract) {
-  push({ name: 'CrmContactDetail', params: { id: row.id } });
+  if (row.signContactId) push({ name: 'CrmContactDetail', params: { id: row.signContactId } });
 }
 
 /** 鎵撳紑鍟嗘満璇︽儏 */
 function handleBusinessDetail(row: CrmContractApi.Contract) {
-  push({ name: 'CrmBusinessDetail', params: { id: row.id } });
+  if (row.businessId) push({ name: 'CrmBusinessDetail', params: { id: row.businessId } });
+}
+
+function handleViewOrder(row: CrmContractApi.Contract) {
+  if (row.orderId) push({ name: 'ErpSaleOrder', query: { id: row.orderId } });
 }
 
 const [Grid] = useVbenVxeGrid({
@@ -88,6 +92,11 @@
 
 <template>
   <Grid>
+    <template #no="{ row }">
+      <Button type="link" @click="handleContractDetail(row)">
+        {{ row.no }}
+      </Button>
+    </template>
     <template #name="{ row }">
       <Button type="link" @click="handleContractDetail(row)">
         {{ row.name }}
@@ -108,6 +117,19 @@
         {{ row.signContactName }}
       </Button>
     </template>
+    <template #auditStatus="{ row }">
+      <Tag v-if="row.auditStatus === 0">鏈彁浜�</Tag>
+      <Tag v-else-if="row.auditStatus === 10" color="warning">瀹℃壒涓�</Tag>
+      <Tag v-else-if="row.auditStatus === 20" color="success">瀹℃牳閫氳繃</Tag>
+      <Tag v-else-if="row.auditStatus === 30" color="error">瀹℃牳涓嶉�氳繃</Tag>
+      <Tag v-else-if="row.auditStatus === 40">宸插彇娑�</Tag>
+      <Tag v-else-if="row.auditStatus === 50" color="error">宸蹭綔搴�</Tag>
+      <span v-else>鈥�</span>
+    </template>
+    <template #orderNo="{ row }">
+      <Button v-if="row.orderId" type="link" @click="handleViewOrder(row)">{{ row.orderNo }}</Button>
+      <span v-else>鈥�</span>
+    </template>
     <template #actions="{ row }">
       <TableAction
         :actions="[
@@ -115,6 +137,7 @@
             label: '鏌ョ湅瀹℃壒',
             type: 'link',
             auth: ['crm:contract:update'],
+            ifShow: !!row.processInstanceId,
             onClick: handleProcessDetail.bind(null, row),
           },
         ]"
diff --git a/src/views/crm/backlog/modules/customer-follow-list.vue b/src/views/crm/backlog/modules/customer-follow-list.vue
index 6e344d0..4eae0ad 100644
--- a/src/views/crm/backlog/modules/customer-follow-list.vue
+++ b/src/views/crm/backlog/modules/customer-follow-list.vue
@@ -4,6 +4,7 @@
 import type { CrmCustomerApi } from '#/api/crm/customer';
 
 import { useRouter } from 'vue-router';
+import { erpNumberFormatter } from '@vben/utils';
 
 import { Button } from 'ant-design-vue';
 
@@ -68,6 +69,10 @@
     <template #name="{ row }">
       <Button type="link" @click="handleDetail(row)">{{ row.name }}</Button>
     </template>
+    <template #contracts="{ row }">
+      <span v-if="row.contractCount">{{ row.contractCount }} 浠� 路 楼{{ erpNumberFormatter(row.contractTotalPrice, 2) }}</span>
+      <span v-else>鈥�</span>
+    </template>
     <template #actions="{ row }">
       <Button type="link" @click="handleDetail(row)">鏌ョ湅璇︽儏</Button>
     </template>
diff --git a/src/views/crm/backlog/modules/customer-put-pool-remind-list.vue b/src/views/crm/backlog/modules/customer-put-pool-remind-list.vue
index fe78245..c64788e 100644
--- a/src/views/crm/backlog/modules/customer-put-pool-remind-list.vue
+++ b/src/views/crm/backlog/modules/customer-put-pool-remind-list.vue
@@ -4,11 +4,12 @@
 import type { CrmCustomerApi } from '#/api/crm/customer';
 
 import { useRouter } from 'vue-router';
+import { erpNumberFormatter } from '@vben/utils';
 
 import { Button } from 'ant-design-vue';
 
 import { useVbenVxeGrid } from '#/adapter/vxe-table';
-import { getCustomerPage } from '#/api/crm/customer';
+import { getPutPoolRemindCustomerPage } from '#/api/crm/customer';
 import { useGridColumns } from '#/views/crm/customer/data';
 
 import { SCENE_TYPES } from '../data';
@@ -42,10 +43,9 @@
     proxyConfig: {
       ajax: {
         query: async ({ page }, formValues) => {
-          return await getCustomerPage({
+          return await getPutPoolRemindCustomerPage({
             pageNo: page.currentPage,
             pageSize: page.pageSize,
-            pool: true, // 鍥哄畾 鍏捣鍙傛暟涓� true
             ...formValues,
           });
         },
@@ -68,6 +68,10 @@
     <template #name="{ row }">
       <Button type="link" @click="handleDetail(row)">{{ row.name }}</Button>
     </template>
+    <template #contracts="{ row }">
+      <span v-if="row.contractCount">{{ row.contractCount }} 浠� 路 楼{{ erpNumberFormatter(row.contractTotalPrice, 2) }}</span>
+      <span v-else>鈥�</span>
+    </template>
     <template #actions="{ row }">
       <Button type="link" @click="handleDetail(row)">鏌ョ湅璇︽儏</Button>
     </template>
diff --git a/src/views/crm/backlog/modules/customer-today-contact-list.vue b/src/views/crm/backlog/modules/customer-today-contact-list.vue
index 03d0b25..a37e099 100644
--- a/src/views/crm/backlog/modules/customer-today-contact-list.vue
+++ b/src/views/crm/backlog/modules/customer-today-contact-list.vue
@@ -4,6 +4,7 @@
 import type { CrmCustomerApi } from '#/api/crm/customer';
 
 import { useRouter } from 'vue-router';
+import { erpNumberFormatter } from '@vben/utils';
 
 import { Button } from 'ant-design-vue';
 
@@ -78,6 +79,10 @@
     <template #name="{ row }">
       <Button type="link" @click="handleDetail(row)">{{ row.name }}</Button>
     </template>
+    <template #contracts="{ row }">
+      <span v-if="row.contractCount">{{ row.contractCount }} 浠� 路 楼{{ erpNumberFormatter(row.contractTotalPrice, 2) }}</span>
+      <span v-else>鈥�</span>
+    </template>
     <template #actions="{ row }">
       <Button type="link" @click="handleDetail(row)">鏌ョ湅璇︽儏</Button>
     </template>
diff --git a/src/views/crm/backlog/modules/receivable-audit-list.vue b/src/views/crm/backlog/modules/receivable-audit-list.vue
index a2a80be..de37fe4 100644
--- a/src/views/crm/backlog/modules/receivable-audit-list.vue
+++ b/src/views/crm/backlog/modules/receivable-audit-list.vue
@@ -7,13 +7,14 @@
 
 import { Button, message } from 'ant-design-vue';
 
-import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import { getReceivablePage, updateReceivableStatus } from '#/api/crm/receivable';
 import { useGridColumns } from '#/views/crm/receivable/data';
 
 import { AUDIT_STATUS } from '../data';
 
 const { push } = useRouter();
+const emit = defineEmits<{ refresh: [] }>();
 
 /** 瀹℃牳 */
 async function handleAudit(row: CrmReceivableApi.Receivable) {
@@ -24,7 +25,8 @@
   try {
     await updateReceivableStatus(row.id!, 20);
     message.success('瀹℃牳鎴愬姛');
-    gridApi.query();
+    await gridApi.query();
+    emit('refresh');
   } finally {
     hideLoading();
   }
@@ -42,7 +44,11 @@
 
 /** 鎵撳紑鍚堝悓璇︽儏 */
 function handleContractDetail(row: CrmReceivableApi.Receivable) {
-  push({ name: 'CrmContractDetail', params: { id: row.contractId } });
+  if (row.contractId) push({ name: 'CrmContractDetail', params: { id: row.contractId } });
+}
+
+function handleInvoiceDetail(row: CrmReceivableApi.Receivable) {
+  if (row.invoiceId) push({ name: 'CrmInvoiceDetail', params: { id: row.invoiceId } });
 }
 
 const [Grid, gridApi] = useVbenVxeGrid({
@@ -50,7 +56,7 @@
     schema: [
       {
         fieldName: 'auditStatus',
-        label: '鍚堝悓鐘舵��',
+        label: '瀹℃牳鐘舵��',
         component: 'Select',
         componentProps: {
           allowClear: true,
@@ -70,6 +76,7 @@
           return await getReceivablePage({
             pageNo: page.currentPage,
             pageSize: page.pageSize,
+            sceneType: 1,
             ...formValues,
           });
         },
@@ -100,18 +107,26 @@
       </Button>
     </template>
     <template #contractNo="{ row }">
-      <Button type="link" @click="handleContractDetail(row)">
-        {{ row.contractNo }}
+      <Button v-if="row.contractId" type="link" @click="handleContractDetail(row)">
+        {{ row.contract?.no ?? row.contractNo ?? '鈥�' }}
       </Button>
+      <span v-else>鈥�</span>
+    </template>
+    <template #invoiceNo="{ row }">
+      <template v-if="row.invoice">
+        <Button type="link" @click="handleInvoiceDetail(row)">{{ row.invoice.no }}</Button>
+        <span> / {{ row.invoice.invoiceNo ?? '鈥�' }} / 楼{{ row.invoice.price ?? 0 }}</span>
+      </template>
+      <span v-else>鈥�</span>
     </template>
     <template #actions="{ row }">
-      <Button
-        v-if="row.auditStatus === 10"
-        type="link"
-        @click="handleAudit(row)"
-      >
-        瀹℃牳
-      </Button>
+      <TableAction :actions="[{
+        label: '瀹℃牳',
+        type: 'link',
+        auth: ['crm:receivable:update'],
+        ifShow: row.auditStatus === 10,
+        onClick: handleAudit.bind(null, row),
+      }]" />
     </template>
   </Grid>
 </template>
diff --git a/src/views/crm/backlog/modules/receivable-plan-remind-list.vue b/src/views/crm/backlog/modules/receivable-plan-remind-list.vue
index 4bafa46..697e673 100644
--- a/src/views/crm/backlog/modules/receivable-plan-remind-list.vue
+++ b/src/views/crm/backlog/modules/receivable-plan-remind-list.vue
@@ -9,7 +9,7 @@
 
 import { Button } from 'ant-design-vue';
 
-import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import { getReceivablePlanPage } from '#/api/crm/receivable/plan';
 import Form from '#/views/crm/receivable/modules/form.vue';
 import { useGridColumns } from '#/views/crm/receivable/plan/data';
@@ -17,15 +17,21 @@
 import { RECEIVABLE_REMIND_TYPE } from '../data';
 
 const { push } = useRouter();
+const emit = defineEmits<{ refresh: [] }>();
+
+function handleRefresh() {
+  gridApi.query();
+  emit('refresh');
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-/** 鎵撳紑鍥炴璇︽儏 */
+/** 鎵撳紑鍥炴璁″垝璇︽儏 */
 function handleDetail(row: CrmReceivablePlanApi.Plan) {
-  push({ name: 'CrmReceivableDetail', params: { id: row.id } });
+  push({ name: 'CrmReceivablePlanDetail', params: { id: row.id } });
 }
 
 /** 鎵撳紑瀹㈡埛璇︽儏 */
@@ -35,15 +41,16 @@
 
 /** 鍒涘缓鍥炴 */
 function handleCreateReceivable(row: CrmReceivablePlanApi.Plan) {
+  if (row.receivableId) return;
   formModalApi.setData({ plan: row }).open();
 }
 
-const [Grid] = useVbenVxeGrid({
+const [Grid, gridApi] = useVbenVxeGrid({
   formOptions: {
     schema: [
       {
         fieldName: 'remindType',
-        label: '鍚堝悓鐘舵��',
+        label: '鍥炴鐘舵��',
         component: 'Select',
         componentProps: {
           allowClear: true,
@@ -63,6 +70,7 @@
           return await getReceivablePlanPage({
             pageNo: page.currentPage,
             pageSize: page.pageSize,
+            sceneType: 1,
             ...formValues,
           });
         },
@@ -82,7 +90,7 @@
 
 <template>
   <div>
-    <FormModal />
+    <FormModal @success="handleRefresh" />
     <Grid>
       <template #customerName="{ row }">
         <Button type="link" @click="handleCustomerDetail(row)">
@@ -93,9 +101,13 @@
         <Button type="link" @click="handleDetail(row)">{{ row.period }}</Button>
       </template>
       <template #actions="{ row }">
-        <Button type="link" @click="handleCreateReceivable(row)">
-          鍒涘缓鍥炴
-        </Button>
+        <TableAction :actions="[{
+          label: '鍒涘缓鍥炴',
+          type: 'link',
+          auth: ['crm:receivable:create'],
+          ifShow: !row.receivableId,
+          onClick: handleCreateReceivable.bind(null, row),
+        }]" />
       </template>
     </Grid>
   </div>
diff --git a/src/views/crm/contract/detail/index.vue b/src/views/crm/contract/detail/index.vue
index 310c838..cd14401 100644
--- a/src/views/crm/contract/detail/index.vue
+++ b/src/views/crm/contract/detail/index.vue
@@ -17,6 +17,7 @@
 import { OperateLog } from '#/components/operate-log';
 import { ACTION_ICON, TableAction } from '#/components/table-action';
 import { $t } from '#/locales';
+import { downloadContractWord } from '#/utils/contract-document';
 import { FollowUp } from '#/views/crm/followup';
 import { PermissionList, TransferForm } from '#/views/crm/permission';
 import { ProductDetailsList } from '#/views/crm/product/components';
@@ -119,6 +120,13 @@
             onClick: handleEdit,
           },
           {
+            label: '涓嬭浇鍚堝悓',
+            type: 'primary',
+            icon: 'lucide:download',
+            auth: ['crm:contract:query'],
+            onClick: () => downloadContractWord(contractId, contract.no),
+          },
+          {
             label: '杞Щ',
             type: 'primary',
             ifShow: permissionListRef?.validateOwnerUser,
diff --git a/src/views/crm/contract/index.vue b/src/views/crm/contract/index.vue
index c57bffe..cd8650e 100644
--- a/src/views/crm/contract/index.vue
+++ b/src/views/crm/contract/index.vue
@@ -21,6 +21,7 @@
 } from '#/api/crm/contract';
 import { $t } from '#/locales';
 import { automaticApproval } from '#/store/audit-policy';
+import { downloadContractWord } from '#/utils/contract-document';
 
 import { useGridColumns, useGridFormSchema } from './data';
 import Form from './modules/form.vue';
@@ -332,6 +333,13 @@
         <TableAction
           :actions="[
             {
+              label: '涓嬭浇鍚堝悓',
+              type: 'link',
+              icon: 'lucide:download',
+              auth: ['crm:contract:query'],
+              onClick: () => downloadContractWord(row.id, row.no),
+            },
+            {
               label: $t('common.edit'),
               type: 'link',
               icon: ACTION_ICON.EDIT,
diff --git a/src/views/crm/saleQuotation/index.vue b/src/views/crm/saleQuotation/index.vue
index 9a221d3..cf01246 100644
--- a/src/views/crm/saleQuotation/index.vue
+++ b/src/views/crm/saleQuotation/index.vue
@@ -15,6 +15,7 @@
   getSaleQuotationPage,
 } from '#/api/crm/saleQuotation';
 import { $t } from '#/locales';
+import { downloadContractWord } from '#/utils/contract-document';
 
 import type { CrmSaleQuotationAiApi } from '#/api/crm/saleQuotation/ai';
 
@@ -107,6 +108,12 @@
     async onOk() {
       const contractId = await convertSaleQuotationToContract(row.id!);
       message.success('杞悎鍚屾垚鍔�');
+      gridApi.query();
+      try {
+        await downloadContractWord(contractId);
+      } catch {
+        message.warning('鍚堝悓宸插垱寤猴紝鏂囦欢涓嬭浇澶辫触锛屽彲鍦ㄥ悎鍚岃鎯呬腑閲嶆柊涓嬭浇');
+      }
       push({ name: 'CrmContractDetail', params: { id: contractId } });
     },
   });
@@ -218,6 +225,13 @@
               ifShow: !!row.contractId,
             },
             {
+              label: '涓嬭浇鍚堝悓',
+              type: 'link',
+              auth: ['crm:contract:query'],
+              onClick: () => downloadContractWord(row.contractId!, row.contractNo),
+              ifShow: !!row.contractId,
+            },
+            {
               label: $t('common.delete'),
               type: 'link',
               danger: true,
@@ -233,4 +247,4 @@
       </template>
     </Grid>
   </Page>
-</template>
\ No newline at end of file
+</template>
diff --git a/src/views/mes/home/modules/alert-panel.vue b/src/views/mes/home/modules/alert-panel.vue
index 8584cce..1acc6d0 100644
--- a/src/views/mes/home/modules/alert-panel.vue
+++ b/src/views/mes/home/modules/alert-panel.vue
@@ -20,14 +20,6 @@
 /** 寰呭姙鎻愰啋鍒楄〃锛氭爣绛俱�佹弿杩般�佸浘鏍囥�佺洰鏍囪矾鐢卞悕绉般�佹暟閲� */
 const alertItems = computed(() => [
   {
-    count: props.summary.andonActiveCount,
-    desc: '鏈缃殑瀹夌伅鍛煎彨',
-    icon: 'lucide:bell-ring',
-    iconClass: 'bg-red-50 text-red-500',
-    label: '瀹夌伅鎶ヨ',
-    routeName: 'MesProAndonRecord',
-  },
-  {
     count: props.summary.repairActiveCount,
     desc: '寰呭鐞嗙殑缁翠慨璁㈠崟',
     icon: 'lucide:wrench',
diff --git a/src/views/mes/md/workstation/components/workshop-select.vue b/src/views/mes/md/workstation/components/workshop-select.vue
index bf71b92..3b525d0 100644
--- a/src/views/mes/md/workstation/components/workshop-select.vue
+++ b/src/views/mes/md/workstation/components/workshop-select.vue
@@ -82,10 +82,6 @@
       <div v-if="selectedItem" class="leading-6">
         <div>缂栫爜锛歿{ selectedItem.code || '-' }}</div>
         <div>鍚嶇О锛歿{ selectedItem.name || '-' }}</div>
-        <div>
-          闈㈢Н锛�
-          {{ selectedItem.area !== null ? `${selectedItem.area} 銕 : '-' }}
-        </div>
         <div>璐熻矗浜猴細{{ selectedItem.chargeUserName || '-' }}</div>
       </div>
     </template>
diff --git a/src/views/mes/md/workstation/workshop/data.ts b/src/views/mes/md/workstation/workshop/data.ts
index 21e977d..3b8fbe2 100644
--- a/src/views/mes/md/workstation/workshop/data.ts
+++ b/src/views/mes/md/workstation/workshop/data.ts
@@ -76,16 +76,6 @@
       rules: 'required',
     },
     {
-      fieldName: 'area',
-      label: '闈㈢Н',
-      component: 'InputNumber',
-      componentProps: {
-        class: '!w-full',
-        min: 0,
-        precision: 2,
-      },
-    },
-    {
       fieldName: 'chargeUserId',
       label: '璐熻矗浜�',
       component: 'ApiSelect',
@@ -168,11 +158,6 @@
       field: 'name',
       title: '杞﹂棿鍚嶇О',
       minWidth: 150,
-    },
-    {
-      field: 'area',
-      title: '闈㈢Н',
-      width: 120,
     },
     {
       field: 'chargeUserName',

--
Gitblit v1.9.3