浏览代码

修改智贸

Fan 1 月之前
父节点
当前提交
f8113015be

+ 4 - 1
ui/smarttrade-platform/src/api/marketing/index.js

@@ -15,5 +15,8 @@ export const setSuspendCustomer = (data = {}) => post('/router/rest/marketEmails
 export const getReplyCustomer = (data = {}) => get('/router/rest/marketEmails/reply-list', data)
 export const getInterventionCustomer = (data = {}) => get('/router/rest/marketEmails/intervention-list', data)
 export const getEmailList = (data = {}) => get('/router/rest/marketEmails/management-detail', data)
+export const cancelEmail = (data = {}) => post('/router/rest/marketEmails/cancel', data)
+export const setMarketingStatus = (data = {}) => post('/router/rest/marketing/status', data)
 export const getTradeCustomer = (data = {}) => post('/router/rest/customerTrade/list', data)
-export const saveGenerateEmail = (data = {}) => post('/router/rest/sopDetail/edit', data)
+export const saveGenerateEmail = (data = {}) => post('/router/rest/sopDetail/edit', data)
+export const getEmailDetail = (data = {}) => post('/router/rest/mkt/email/get_mail_detial', data)

+ 3 - 0
ui/smarttrade-platform/src/assets/styles/marketing.css

@@ -920,6 +920,9 @@
 .step-number { display: none; }
 .step-text { display: none; }
 
+.no-data{ min-height: 100px; display: flex; align-items: center; justify-content: center; color: var(--text-muted); font-size: 14px; }
+:deep(.el-loading-mask){ background: transparent; }
+
 
 @media (max-width: 1400px) {
   .campaigns-grid { grid-template-columns: 1fr; }

+ 57 - 42
ui/smarttrade-platform/src/components/marketing/emailList.vue

@@ -36,37 +36,39 @@
           </div>
         </div>
 
-        <div class="email-card-list">
+        <div class="email-card-list" v-loading="loading">
           <div
-            v-for="item in filteredEmailList"
+            v-for="item in filterEmailList"
             :key="item.id"
             class="email-card"
+            v-if="filterEmailList.length > 0"
           >
             <div class="email-card-left">
               <div class="email-card-top">
                 <div>
                   <div class="email-card-title">
                     {{ item.customerName }}
-                    <span class="status-pill">{{ item.type }}</span>
+                    <span class="status-pill">{{ item.stageName }}</span>
                   </div>
                   <div class="email-card-subtitle">{{ item.email }}</div>
-                  <div class="email-card-subtitle">预计发送时间:{{ item.sendTime }}</div>
+                  <div class="email-card-subtitle">预计发送时间:{{ item.expectSendTime }}</div>
                 </div>
                 
               </div>
             </div>
             <div class="email-card-actions">
-              <button class="btn btn-secondary" @click="cancelSend(item)">
-                取消发送
+              <button v-if="activeFilter !== 'sent'" class="btn btn-secondary" @click="cancelSend(item)">
+                {{  activeFilter === 'pending' ? '取消' : '重新' }}发送
               </button>
               <button class="btn btn-secondary" @click="viewDetail(item)">
                 邮件详情
               </button>
-              <button class="btn btn-primary" @click="editEmail(item)">
+              <button v-if="activeFilter !== 'sent'" class="btn btn-primary" @click="editEmail(item)">
                 编辑邮件
               </button>
             </div>
           </div>
+          <div v-else class="no-data">暂无数据</div>
         </div>
       </div>
       <div class="modal-footer email-manage-footer">
@@ -79,38 +81,45 @@
 <script setup>
 import { computed, ref } from 'vue'
 import { useRouter } from 'vue-router'
-import { getEmailList } from '@/api/marketing'
+import { getEmailList, cancelEmail } from '@/api/marketing'
+import { ElMessageBox } from 'element-plus'
 
-const router = useRouter()
-const showEmailListModal = ref(false)
 const selectedCampaign = ref(null)
-const emailList = ref([
-  { id: 1, customerName: '中东贸易公司(admin)', email: 'buyer@middle-east-trade.com', status: 'pending', sendTime: '2026-03-23 11:45', replyCount: 0, type: '首次介绍' },
-  { id: 2, customerName: '中东贸易公司(admin)', email: 'buyer@middle-east-trade.com', status: 'sent', sendTime: '2026-03-23 11:45', replyCount: 3, type: '邮件类型' },
-  { id: 3, customerName: '中东贸易公司(admin)', email: 'buyer@middle-east-trade.com', status: 'failed', sendTime: '2026-03-23 11:45', replyCount: 0, type: '产品推荐' },
-  { id: 4, customerName: '中东贸易公司(admin)', email: 'buyer@middle-east-trade.com', status: 'pending', sendTime: '2026-03-23 11:45', replyCount: 1, type: '报价邮件' },
-  { id: 5, customerName: '中东贸易公司(admin)', email: 'buyer@middle-east-trade.com', status: 'sent', sendTime: '2026-03-23 11:45', replyCount: 2, type: '产品推荐' }
-])
-const activeFilter = ref('pending')
+const emailList = ref([])
+
+const open = (campaign) => {
+  selectedCampaign.value = campaign || null
+  emailList.value = []
+  getList()
+  showEmailListModal.value = true
+}
 
-const statusLabel = (status) => {
-  if (status === 'sent') return '已发送'
-  if (status === 'pending') return '待发送'
-  if (status === 'failed') return '已取消'
-  return '未知'
+const loading = ref(false)
+const getList = () => {
+  loading.value = true
+  getEmailList({ id: selectedCampaign.value.id }).then((res) => {
+    emailList.value = res.result || []
+  }).finally(() => {
+    loading.value = false
+  })
 }
 
-const filteredEmailList = computed(() => {
+const router = useRouter()
+const showEmailListModal = ref(false)
+
+const activeFilter = ref('pending')
+
+const filterEmailList = computed(() => {
   if (activeFilter.value === 'all') {
     return emailList.value
   }
-  return emailList.value.filter((item) => item.status === activeFilter.value)
+  return emailList.value.filter((item) => item.status === (activeFilter.value == 'pending' ? -2 : activeFilter.value == 'sent' ? 1 : -3))
 })
 
 const statusCounts = computed(() => ({
-  pending: emailList.value.filter((item) => item.status === 'pending').length,
-  sent: emailList.value.filter((item) => item.status === 'sent').length,
-  failed: emailList.value.filter((item) => item.status === 'failed').length
+  pending: emailList.value.filter((item) => item.status === -2).length,
+  sent: emailList.value.filter((item) => item.status === 1).length,
+  failed: emailList.value.filter((item) => item.status === -3).length
 }))
 
 const refreshList = () => {
@@ -123,12 +132,29 @@ const exportList = () => {
 
 const cancelSend = (item) => {
   // 这里可以接入取消发送逻辑
+  ElMessageBox.confirm(
+    `请确认是否${ activeFilter.value === 'pending' ? '取消' : '重新' }发送?`,
+    '提示',
+    {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }
+  ).then(() => {
+    cancelEmail({ id: item.id, status: (item.status == -2 ? -3 : -2) }).then(() => {
+      ElMessage.success('操作成功')
+      getList()
+    }).catch((err) => {
+      ElMessage.error(err.message || '操作失败')
+    })
+  })
 }
 
 const viewDetail = (item) => {
   // 这里可以接入查看详情逻辑
   const { href } = router.resolve({
-    path: '/emails/detail'
+    path: '/emails/detail',
+    query: { id: item.emailId }
   })
   window.open(href, '_blank')
 }
@@ -136,7 +162,8 @@ const viewDetail = (item) => {
 const editEmail = (item) => {
   // 这里可以接入编辑邮件逻辑
   const { href } = router.resolve({
-    path: '/emails/edit'
+    path: '/emails/edit',
+    query: { id: item.emailId }
   })
   window.open(href, '_blank')
 }
@@ -145,17 +172,6 @@ const batchOperate = () => {
   // 这里可以接入批量操作逻辑
 }
 
-const open = (campaign, list = null) => {
-  selectedCampaign.value = campaign || null
-  if (Array.isArray(list)) {
-    emailList.value = list
-  }
-  getEmailList({ id: campaign.id }).then((res) => {
-    console.log('res :>> ', res);
-  })
-  showEmailListModal.value = true
-}
-
 const close = () => {
   showEmailListModal.value = false
 }
@@ -269,7 +285,6 @@ defineExpose({ open, close })
 .email-card-actions {
   display: flex;
   gap: 10px;
-  width: 280px;
 }
 
 .email-manage-footer {

+ 39 - 16
ui/smarttrade-platform/src/components/marketing/interventionCustomers.vue

@@ -18,32 +18,33 @@
           </div>
 
           <!-- Protected Customers List -->
-          <div class="detail-section">
+          <div class="detail-section" v-if="protectedCustomersList.length > 0">
             <div class="protected-customers">
               <div v-for="customer in protectedCustomersList" :key="customer.id" class="protected-customer-item">
                 <div class="customer-header">
                   <div class="customer-info">
                     <div class="customer-name">
-                      {{ customer.name }}
-                      <span class="protection-level" :class="customer.protectionLevel">
+                      {{ customer.customerName }}
+                      <!-- <span class="protection-level" :class="customer.protectionLevel">
                         {{ customer.protectionLevel }}保护
-                      </span>
+                      </span> -->
                     </div>
-                    <div class="customer-email">{{ customer.email }}</div>
+                    <div class="customer-email">{{ customer.recipient }}</div>
                   </div>
                 </div>
                 <div class="customer-details">
-                  <span class="detail">保护原因: {{ customer.reason }}</span>
-                  <span class="detail">保护时间: {{ customer.protectedSince }}</span>
+                  <span class="detail">{{ tabType == 'protect' ? '保护' : "暂停" }}原因: {{ customer.protectReason }}</span>
+                  <span class="detail">{{ tabType == 'protect' ? '保护' : "暂停" }}时间: {{ customer.protectTime }}</span>
                 </div>
                 <div class="customer-actions">
                   <button class="btn btn-secondary btn-sm" @click="unprotectCustomer(customer)">
-                    <i class="fas fa-times"></i> 移除保护
+                    <i class="fas fa-times"></i> {{ tabType == 'protect' ? '移除保护' : "取消暂停" }}
                   </button>
                 </div>
               </div>
             </div>
           </div>
+          <div v-else class="no-data">暂无数据</div>
         </div>
         <div class="modal-footer">
           <button class="btn btn-secondary" @click="showInterventionModal = false">关闭</button>
@@ -61,13 +62,7 @@
 
     const showInterventionModal = ref(false);
     // 客户保护白名单 - 防止AI营销影响
-    const protectedCustomersList = ref([
-        { id: 1, name: '阿里巴巴国际', email: 'contact@alibaba-intl.com', protectionLevel: '高', reason: '高价值客户', annualValue: '¥500K+', status: 'active', protectedSince: '2026-03-20', assignedSalesRep: '张三' },
-        { id: 2, name: '迪拜电子商务', email: 'info@dubai-ecom.ae', protectionLevel: '高', reason: '高价值客户', annualValue: '¥800K+', status: 'active', protectedSince: '2026-03-18', assignedSalesRep: '李四' },
-        { id: 3, name: '德国工业集团', email: 'procurement@de-industry.de', protectionLevel: '高', reason: '高价值客户', annualValue: '¥1.2M+', status: 'active', protectedSince: '2026-03-15', assignedSalesRep: '王五' },
-        { id: 4, name: '沙特阿美贸易', email: 'trade@aramco-sa.com', protectionLevel: '中', reason: '活跃客户', annualValue: '¥200K', status: 'active', protectedSince: '2026-03-22', assignedSalesRep: '赵六' },
-        { id: 5, name: '英国分销商', email: 'sales@uk-dist.co.uk', protectionLevel: '中', reason: '活跃客户', annualValue: '¥300K', status: 'active', protectedSince: '2026-03-21', assignedSalesRep: '孙七' }
-    ])
+    const protectedCustomersList = ref([])
     const tabType = ref('protect')
 
     const marketingData = ref()
@@ -79,10 +74,38 @@
 
     const getData = () => {
         getInterventionCustomer({ id: marketingData.value.id, tabType: tabType.value }).then(res => {
-            console.log('res :>> ', res);
+            protectedCustomersList.value = res.result || []
         })
     }
 
+    const unprotectCustomer = (customer) => {
+      ElMessageBox.confirm(
+        `请确认是否${ tabType == 'protect' ? '移除保护' : '取消暂停' }?`,
+        '提示',
+        {
+          confirmButtonText: '确认',
+          cancelButtonText: '取消',
+          type: 'warning',
+        }
+      ).then(() => {
+        let params = { id: customer.id }
+        let request = null
+        if(tabType.value == 'protect') {
+          params.hasProtect = 0
+          request = setProtectCustomer
+        } else {
+          params.hasSuspend = 0
+          request = setSuspendCustomer
+        }
+        request(params).then(res => {
+          getData()
+          ElMessage.success('操作成功')
+        }).catch(err => {
+          ElMessage.error('操作失败')
+        })
+      })
+    }
+
     const changeTab = (type) => {
         tabType.value = type
         getData()

+ 54 - 28
ui/smarttrade-platform/src/components/marketing/openedCustomers.vue

@@ -7,14 +7,14 @@
           </div>
           <button class="modal-close" @click="showOpenedCustomersModal = false"><i class="fas fa-times"></i></button>
         </div>
-        <div class="modal-body">
+        <div class="modal-body" v-loading="loading">
           <div class="filter-bar">
             <button class="filter-btn active">全部</button>
             <button class="filter-btn">高价值</button>
             <button class="filter-btn">已保护</button>
             <button class="filter-btn">营销暂停</button>
           </div>
-          <div class="customers-list">
+          <div v-if="openedCustomers.length > 0" class="customers-list">
             <div v-for="customer in openedCustomers" class="customer-item" :class="{ protected: customer.hasProtect }">
               <div class="customer-header">
                 <div class="customer-info">
@@ -53,6 +53,7 @@
               </div>
             </div>
           </div>
+          <div v-else class="no-data">暂无数据</div>
         </div>
         <div class="modal-footer">
           <button class="btn btn-secondary" @click="showOpenedCustomersModal = false">关闭</button>
@@ -67,52 +68,77 @@
 <script setup>
     import { ref } from 'vue'
     import { getOpenCustomer, setProtectCustomer, setSuspendCustomer } from '@/api/marketing'
+    import { ElMessageBox } from 'element-plus'
 
     const showOpenedCustomersModal = ref(false)
     const selectedOpenedCampaign = ref()
     // 打开邮件的客户列表 - 需要人工跟进
     const openedCustomers = ref([
-        { id: 1, name: '阿里巴巴国际', email: 'contact@alibaba-intl.com', openTime: '2026-03-23 10:30', clickCount: 2, status: 'opened', engagement: '高', value: '高价值', annualPurchase: '¥500K+', lastOrder: '2026-02-15', protected: true, protectionReason: '高价值客户', interventionStatus: 'assigned-to-sales' },
-        { id: 2, name: '沙特阿美贸易', email: 'trade@aramco-sa.com', openTime: '2026-03-23 09:15', clickCount: 1, status: 'opened', engagement: '中', value: '中价值', annualPurchase: '¥200K', lastOrder: '2026-03-01', protected: true, protectionReason: '活跃客户', interventionStatus: 'pending-review' },
-        { id: 3, name: '迪拜电子商务', email: 'info@dubai-ecom.ae', openTime: '2026-03-23 08:45', clickCount: 3, status: 'opened', engagement: '高', value: '高价值', annualPurchase: '¥800K+', lastOrder: '2026-02-28', protected: true, protectionReason: '高价值客户', interventionStatus: 'assigned-to-sales' },
-        { id: 4, name: '新加坡科技', email: 'sales@sg-tech.com.sg', openTime: '2026-03-22 16:20', clickCount: 0, status: 'opened', engagement: '低', value: '低价值', annualPurchase: '¥50K', lastOrder: '2025-12-10', protected: false, protectionReason: '', interventionStatus: 'auto-follow' },
-        { id: 5, name: '印度IT服务', email: 'contact@india-it.in', openTime: '2026-03-22 14:50', clickCount: 2, status: 'opened', engagement: '中', value: '中价值', annualPurchase: '¥150K', lastOrder: '2026-01-20', protected: true, protectionReason: '活跃客户', interventionStatus: 'pending-review' },
-        { id: 6, name: '巴西进出口', email: 'export@brasil-trade.br', openTime: '2026-03-22 11:30', clickCount: 1, status: 'opened', engagement: '低', value: '低价值', annualPurchase: '¥30K', lastOrder: '2025-11-05', protected: false, protectionReason: '', interventionStatus: 'auto-follow' },
-        { id: 7, name: '德国工业集团', email: 'procurement@de-industry.de', openTime: '2026-03-21 15:45', clickCount: 4, status: 'opened', engagement: '高', value: '高价值', annualPurchase: '¥1.2M+', lastOrder: '2026-03-10', protected: true, protectionReason: '高价值客户', interventionStatus: 'assigned-to-sales' },
-        { id: 8, name: '英国分销商', email: 'sales@uk-dist.co.uk', openTime: '2026-03-21 13:20', clickCount: 2, status: 'opened', engagement: '中', value: '中价值', annualPurchase: '¥300K', lastOrder: '2026-02-20', protected: true, protectionReason: '活跃客户', interventionStatus: 'pending-review' }
+        // { id: 1, name: '阿里巴巴国际', email: 'contact@alibaba-intl.com', openTime: '2026-03-23 10:30', clickCount: 2, status: 'opened', engagement: '高', value: '高价值', annualPurchase: '¥500K+', lastOrder: '2026-02-15', protected: true, protectionReason: '高价值客户', interventionStatus: 'assigned-to-sales' },
+        // { id: 2, name: '沙特阿美贸易', email: 'trade@aramco-sa.com', openTime: '2026-03-23 09:15', clickCount: 1, status: 'opened', engagement: '中', value: '中价值', annualPurchase: '¥200K', lastOrder: '2026-03-01', protected: true, protectionReason: '活跃客户', interventionStatus: 'pending-review' },
+        // { id: 3, name: '迪拜电子商务', email: 'info@dubai-ecom.ae', openTime: '2026-03-23 08:45', clickCount: 3, status: 'opened', engagement: '高', value: '高价值', annualPurchase: '¥800K+', lastOrder: '2026-02-28', protected: true, protectionReason: '高价值客户', interventionStatus: 'assigned-to-sales' },
+        // { id: 4, name: '新加坡科技', email: 'sales@sg-tech.com.sg', openTime: '2026-03-22 16:20', clickCount: 0, status: 'opened', engagement: '低', value: '低价值', annualPurchase: '¥50K', lastOrder: '2025-12-10', protected: false, protectionReason: '', interventionStatus: 'auto-follow' },
+        // { id: 5, name: '印度IT服务', email: 'contact@india-it.in', openTime: '2026-03-22 14:50', clickCount: 2, status: 'opened', engagement: '中', value: '中价值', annualPurchase: '¥150K', lastOrder: '2026-01-20', protected: true, protectionReason: '活跃客户', interventionStatus: 'pending-review' },
+        // { id: 6, name: '巴西进出口', email: 'export@brasil-trade.br', openTime: '2026-03-22 11:30', clickCount: 1, status: 'opened', engagement: '低', value: '低价值', annualPurchase: '¥30K', lastOrder: '2025-11-05', protected: false, protectionReason: '', interventionStatus: 'auto-follow' },
+        // { id: 7, name: '德国工业集团', email: 'procurement@de-industry.de', openTime: '2026-03-21 15:45', clickCount: 4, status: 'opened', engagement: '高', value: '高价值', annualPurchase: '¥1.2M+', lastOrder: '2026-03-10', protected: true, protectionReason: '高价值客户', interventionStatus: 'assigned-to-sales' },
+        // { id: 8, name: '英国分销商', email: 'sales@uk-dist.co.uk', openTime: '2026-03-21 13:20', clickCount: 2, status: 'opened', engagement: '中', value: '中价值', annualPurchase: '¥300K', lastOrder: '2026-02-20', protected: true, protectionReason: '活跃客户', interventionStatus: 'pending-review' }
     ])
 
+    const loading = ref(false)
     const open = (data) => {
         selectedOpenedCampaign.value = data
+        loading.value = true
         getOpenCustomer({ marketingCampaignId: data.id, pageIndex: 1, pageSize: 10000 }).then(res => {
             openedCustomers.value = res.result.records || []
+        }).finally(() => {
+            loading.value = false
         })
         showOpenedCustomersModal.value = true
     }
 
     const protectCustomer = (customer) => {
-      let params = {
-        id: customer.customerId,
-        hasProtect: customer.hasProtect ? 0 : 1
-      }
-      setProtectCustomer(params).then(res => {
-        customer.hasProtect = !customer.hasProtect
-        ElMessage.success('操作成功')
-      }).catch(err => {
-        ElMessage.error('操作失败')
+      ElMessageBox.confirm(
+        `请确认是否${ customer.hasProtect ? '取消保护' : '添加到保护白名单' }?`,
+        '提示',
+        {
+          confirmButtonText: '确认',
+          cancelButtonText: '取消',
+          type: 'warning',
+        }
+      ).then(() => {
+        let params = {
+          id: customer.id,
+          hasProtect: customer.hasProtect ? 0 : 1
+        }
+        setProtectCustomer(params).then(res => {
+          customer.hasProtect = !customer.hasProtect
+          ElMessage.success('操作成功')
+        }).catch(err => {
+          ElMessage.error('操作失败')
+        })
       })
     }
 
     const pauseCampaignForCustomer = (customer) => {
-      let params = {
-        id: customer.customerId,
-        hasSuspend: customer.hasSuspend ? 0 : 1
-      }
-      setSuspendCustomer(params).then(res => {
-        customer.hasSuspend = !customer.hasSuspend
-        ElMessage.success('操作成功')
-      }).catch(err => {
-        ElMessage.error('操作失败')
+      ElMessageBox.confirm(
+        `请确认是否${ customer.hasSuspend ? '恢复' : '暂停' }?`,
+        '提示',
+        {
+          confirmButtonText: '确认',
+          cancelButtonText: '取消',
+          type: 'warning',
+        }
+      ).then(() => {
+        let params = {
+          id: customer.id,
+          hasSuspend: customer.hasSuspend ? 0 : 1
+        }
+        setSuspendCustomer(params).then(res => {
+          customer.hasSuspend = !customer.hasSuspend
+          ElMessage.success('操作成功')
+        }).catch(err => {
+          ElMessage.error('操作失败')
+        })
       })
     }
 

+ 76 - 24
ui/smarttrade-platform/src/components/marketing/replyCustomers.vue

@@ -7,61 +7,62 @@
           </div>
           <button class="modal-close" @click="showReplyReviewModal = false"><i class="fas fa-times"></i></button>
         </div>
-        <div class="modal-body">
+        <div class="modal-body" v-loading="loading">
           <div class="review-stats">
             <div class="stat-box">
               <span class="stat-label">全部</span>
-              <span class="stat-value">{{ repliedCustomers.filter(r => r.status === 'pending-review').length }}</span>
+              <span class="stat-value">{{ repliedCustomers.length }}</span>
             </div>
             <div class="stat-box">
               <span class="stat-label">已保护</span>
-              <span class="stat-value">{{ repliedCustomers.filter(r => r.requiresHumanReview).length }}</span>
+              <span class="stat-value">{{ repliedCustomers.filter(r => r.hasProtect).length }}</span>
             </div>
             <div class="stat-box">
               <span class="stat-label">营销暂停</span>
-              <span class="stat-value">{{ repliedCustomers.filter(r => r.priority === '高').length }}</span>
+              <span class="stat-value">{{ repliedCustomers.filter(r => r.hasSuspend).length }}</span>
             </div>
           </div>
-          <div class="replies-list">
-            <div v-for="reply in repliedCustomers" :key="reply.id" class="reply-item" :class="[reply.status, { 'requires-review': reply.requiresHumanReview }]">
+          <div class="replies-list" v-if="repliedCustomers.length > 0">
+            <div v-for="reply in repliedCustomers" :key="reply.id" class="reply-item" :class="['pending-review', { 'requires-review': !reply.requiresHumanReview }]">
               <div class="reply-header">
                 <div class="reply-info">
                   <div class="reply-name">
-                    {{ reply.name }}
+                    {{ reply.customerName }}
                   </div>
-                  <div class="reply-email">{{ reply.email }}</div>
+                  <div class="reply-email">{{ reply.recipient }}</div>
                 </div>
-                <div class="reply-badges">
+                <!-- <div class="reply-badges">
                   <span class="priority-badge" :class="reply.priority">
                     {{ reply.priority }}优先级
                   </span>
                   <span class="value-badge" :class="reply.value">{{ reply.value }}</span>
-                </div>
+                </div> -->
               </div>
-              <div class="reply-time">回复时间: {{ reply.replyTime }}</div>
+              <div class="reply-time">回复时间: {{ reply.firstReplyTime }}</div>
               <div class="reply-content">
                 <p>{{ reply.replyContent }}</p>
               </div>
-              <div class="reply-review-notes" v-if="reply.reviewNotes">
+              <!-- <div class="reply-review-notes" v-if="reply.reviewNotes">
                 <i class="fas fa-lightbulb"></i>
                 <span>{{ reply.reviewNotes }}</span>
-              </div>
+              </div> -->
               <div class="reply-actions">
-                <button v-if="!reply.protected" class="btn btn-secondary btn-sm" @click="protectCustomer(reply)" title="添加到保护白名单">
+                <button v-if="!reply.hasProtect" class="btn btn-secondary btn-sm" @click="protectCustomer(reply)" title="添加到保护白名单">
                   <i class="fas fa-shield-alt"></i> 保护
                 </button>
-                <button v-else class="btn btn-secondary btn-sm" @click="unprotectCustomer(reply)" title="从保护白名单中移除">
+                <button v-else class="btn btn-secondary btn-sm" @click="protectCustomer(reply)" title="从保护白名单中移除">
                   <i class="fas fa-times"></i> 取消保护
                 </button>
-                <button class="btn btn-secondary btn-sm" @click="pauseCampaignForCustomer(reply)" v-if="reply.interventionStatus !== 'paused'" title="暂停向该客户发送邮件">
+                <button class="btn btn-secondary btn-sm" @click="pauseCampaignForCustomer(reply)" v-if="!reply.hasSuspend" title="暂停向该客户发送邮件">
                   <i class="fas fa-pause"></i> 暂停
                 </button>
-                <button class="btn btn-secondary btn-sm" @click="resumeCampaignForCustomer(reply)" v-else title="恢复向该客户发送邮件">
+                <button class="btn btn-secondary btn-sm" @click="pauseCampaignForCustomer(reply)" v-else title="恢复向该客户发送邮件">
                   <i class="fas fa-play"></i> 恢复
                 </button>
               </div>
             </div>
           </div>
+          <div v-else class="no-data">暂无数据</div>
         </div>
         <div class="modal-footer">
           <button class="btn btn-secondary" @click="showReplyReviewModal = false">关闭</button>
@@ -76,23 +77,74 @@
 <script setup>
     import { ref } from 'vue';
     import { getReplyCustomer, setProtectCustomer, setSuspendCustomer } from '@/api/marketing'
+    import { ElMessageBox } from 'element-plus'
 
     const showReplyReviewModal = ref(false);
     // 回复邮件的客户列表 - 需要人工审核和介入
     const repliedCustomers = ref([
-        { id: 1, name: '中东贸易公司', email: 'buyer@middle-east-trade.com', replyTime: '2026-03-23 11:45', replyContent: '感谢您的邮件,我们对您的产品很感兴趣,能否提供详细的产品规格和价格?', status: 'pending-review', sentiment: '正面', priority: '高', value: '高价值', annualPurchase: '¥600K+', lastOrder: '2026-02-10', requiresHumanReview: true, reviewNotes: '需要销售人员立即跟进,客户明确表示感兴趣', assignedTo: '', reviewedBy: '', reviewTime: '' },
-        { id: 2, name: '欧洲数据中心', email: 'procurement@eu-datacenter.eu', replyTime: '2026-03-23 10:20', replyContent: '我们正在评估新的存储解决方案,您能否安排一次技术演示?', status: 'pending-review', sentiment: '正面', priority: '高', value: '高价值', annualPurchase: '¥1M+', lastOrder: '2026-03-05', requiresHumanReview: true, reviewNotes: '需要技术团队配合,安排产品演示', assignedTo: '', reviewedBy: '', reviewTime: '' },
-        { id: 3, name: '北美分销', email: 'sales@na-dist.com', replyTime: '2026-03-22 16:30', replyContent: '价格太高了,能否提供折扣?', status: 'pending-review', sentiment: '中立', priority: '中', value: '中价值', annualPurchase: '¥250K', lastOrder: '2026-01-15', requiresHumanReview: true, reviewNotes: '需要销售经理评估折扣空间', assignedTo: '', reviewedBy: '', reviewTime: '' },
-        { id: 4, name: '东南亚进口商', email: 'import@sea-import.com', replyTime: '2026-03-22 14:15', replyContent: '我们有兴趣,但需要了解更多关于技术支持的信息。', status: 'pending-review', sentiment: '正面', priority: '中', value: '中价值', annualPurchase: '¥180K', lastOrder: '2026-02-01', requiresHumanReview: true, reviewNotes: '需要技术支持团队提供详细信息', assignedTo: '', reviewedBy: '', reviewTime: '' },
-        { id: 5, name: '日本电子商社', email: 'trade@japan-elec.jp', replyTime: '2026-03-21 17:00', replyContent: '暂时不需要,但会保留您的联系方式。', status: 'pending-review', sentiment: '中立', priority: '低', value: '低价值', annualPurchase: '¥80K', lastOrder: '2025-10-20', requiresHumanReview: false, reviewNotes: '可自动标记为后续跟进', assignedTo: '', reviewedBy: '', reviewTime: '' },
-        { id: 6, name: '韩国科技公司', email: 'contact@korea-tech.kr', replyTime: '2026-03-21 15:30', replyContent: '这不是我们需要的产品类型。', status: 'pending-review', sentiment: '负面', priority: '低', value: '低价值', annualPurchase: '¥40K', lastOrder: '2025-09-10', requiresHumanReview: false, reviewNotes: '可自动标记为不适配,从列表中移除', assignedTo: '', reviewedBy: '', reviewTime: '' }
+        // { id: 1, name: '中东贸易公司', email: 'buyer@middle-east-trade.com', replyTime: '2026-03-23 11:45', replyContent: '感谢您的邮件,我们对您的产品很感兴趣,能否提供详细的产品规格和价格?', status: 'pending-review', sentiment: '正面', priority: '高', value: '高价值', annualPurchase: '¥600K+', lastOrder: '2026-02-10', requiresHumanReview: true, reviewNotes: '需要销售人员立即跟进,客户明确表示感兴趣', assignedTo: '', reviewedBy: '', reviewTime: '' },
+        // { id: 2, name: '欧洲数据中心', email: 'procurement@eu-datacenter.eu', replyTime: '2026-03-23 10:20', replyContent: '我们正在评估新的存储解决方案,您能否安排一次技术演示?', status: 'pending-review', sentiment: '正面', priority: '高', value: '高价值', annualPurchase: '¥1M+', lastOrder: '2026-03-05', requiresHumanReview: true, reviewNotes: '需要技术团队配合,安排产品演示', assignedTo: '', reviewedBy: '', reviewTime: '' },
+        // { id: 3, name: '北美分销', email: 'sales@na-dist.com', replyTime: '2026-03-22 16:30', replyContent: '价格太高了,能否提供折扣?', status: 'pending-review', sentiment: '中立', priority: '中', value: '中价值', annualPurchase: '¥250K', lastOrder: '2026-01-15', requiresHumanReview: true, reviewNotes: '需要销售经理评估折扣空间', assignedTo: '', reviewedBy: '', reviewTime: '' },
+        // { id: 4, name: '东南亚进口商', email: 'import@sea-import.com', replyTime: '2026-03-22 14:15', replyContent: '我们有兴趣,但需要了解更多关于技术支持的信息。', status: 'pending-review', sentiment: '正面', priority: '中', value: '中价值', annualPurchase: '¥180K', lastOrder: '2026-02-01', requiresHumanReview: true, reviewNotes: '需要技术支持团队提供详细信息', assignedTo: '', reviewedBy: '', reviewTime: '' },
+        // { id: 5, name: '日本电子商社', email: 'trade@japan-elec.jp', replyTime: '2026-03-21 17:00', replyContent: '暂时不需要,但会保留您的联系方式。', status: 'pending-review', sentiment: '中立', priority: '低', value: '低价值', annualPurchase: '¥80K', lastOrder: '2025-10-20', requiresHumanReview: false, reviewNotes: '可自动标记为后续跟进', assignedTo: '', reviewedBy: '', reviewTime: '' },
+        // { id: 6, name: '韩国科技公司', email: 'contact@korea-tech.kr', replyTime: '2026-03-21 15:30', replyContent: '这不是我们需要的产品类型。', status: 'pending-review', sentiment: '负面', priority: '低', value: '低价值', annualPurchase: '¥40K', lastOrder: '2025-09-10', requiresHumanReview: false, reviewNotes: '可自动标记为不适配,从列表中移除', assignedTo: '', reviewedBy: '', reviewTime: '' }
     ])
 
+    const loading = ref(false);
     const open = (data) => {
         showReplyReviewModal.value = true;
+        loading.value = true;
         getReplyCustomer({ id: data.id }).then(res => {
-            console.log('res :>> ', res);
+            repliedCustomers.value = res.result || [];
+        }).finally(() => {
+            loading.value = false;
+        })
+    }
+
+    const protectCustomer = (customer) => {
+      ElMessageBox.confirm(
+        `请确认是否${ customer.hasProtect ? '取消保护' : '添加到保护白名单' }?`,
+        '提示',
+        {
+          confirmButtonText: '确认',
+          cancelButtonText: '取消',
+          type: 'warning',
+        }
+      ).then(() => {
+        let params = {
+          id: customer.id,
+          hasProtect: customer.hasProtect ? 0 : 1
+        }
+        setProtectCustomer(params).then(res => {
+          customer.hasProtect = !customer.hasProtect
+          ElMessage.success('操作成功')
+        }).catch(err => {
+          ElMessage.error('操作失败')
+        })
+      })
+    }
+
+    const pauseCampaignForCustomer = (customer) => {
+      ElMessageBox.confirm(
+        `请确认是否${ customer.hasSuspend ? '恢复' : '暂停' }?`,
+        '提示',
+        {
+          confirmButtonText: '确认',
+          cancelButtonText: '取消',
+          type: 'warning',
+        }
+      ).then(() => {
+        let params = {
+          id: customer.id,
+          hasSuspend: customer.hasSuspend ? 0 : 1
+        }
+        setSuspendCustomer(params).then(res => {
+          customer.hasSuspend = !customer.hasSuspend
+          ElMessage.success('操作成功')
+        }).catch(err => {
+          ElMessage.error('操作失败')
         })
+      })
     }
 
     defineExpose({

+ 9 - 41
ui/smarttrade-platform/src/views/MarketingView.vue

@@ -134,8 +134,9 @@
               <img src="../assets/images/email-list.svg" />
             </button>
           </div>
-          <button class="btn btn-secondary btn-sm" @click="changeStatus(campaign)" title="开始">
-            <img src="../assets/images/marketing-start.svg" />
+          <button class="btn btn-secondary btn-sm" @click="changeStatus(campaign)" :title="campaign.status == 1 ? '暂停' : '开始'">
+            <img v-if="campaign.status == 1" src="../assets/images/marketing-pause.svg" />
+            <img v-else src="../assets/images/marketing-start.svg" />
           </button>
         </div>
       </div>
@@ -732,7 +733,7 @@
 import { ref, computed, onMounted } from 'vue'
 import { ElMessageBox } from 'element-plus'
 import AddMarketing from '../components/addMarketing.vue'
-import { getMarketingList, getSopById, marketingAnalysis } from '../api/marketing'
+import { getMarketingList, setMarketingStatus, marketingAnalysis } from '../api/marketing'
 import OpenedCustomers from '../components/marketing/openedCustomers.vue'
 import ReplyCustomers from '../components/marketing/replyCustomers.vue'
 import InterventionCustomers from '../components/marketing/interventionCustomers.vue'
@@ -905,7 +906,7 @@ const openEmailList = (campaign) => {
 
 const changeStatus = (campaign) => {
   ElMessageBox.confirm(
-    '请确认是否停止邮件生产和发送,操作后立即生效!',
+    `${ campaign.status == 1 ? '请确认是否停止邮件生产和发送' : '请确认是否开始邮件生成和发送' },操作后立即生效!`,
     '提示',
     {
       confirmButtonText: '确认',
@@ -913,7 +914,10 @@ const changeStatus = (campaign) => {
       type: 'warning',
     }
   ).then(() => {
-    
+    setMarketingStatus({ id: campaign.id, status: campaign.status == 1 ? 2 : 1 }).then(() => {
+      ElMessage.success('操作成功')
+      getData()
+    })
   })
 }
 
@@ -1117,47 +1121,11 @@ const getSentimentColor = (sentiment) => {
   return colors[sentiment] || '#6366F1'
 }
 
-const protectCustomer = (customer) => {
-  customer.protected = true
-  customer.protectionReason = '高价值客户'
-  customer.interventionStatus = 'assigned-to-sales'
-  protectedCustomersList.value.push({
-    id: protectedCustomersList.value.length + 1,
-    name: customer.name,
-    email: customer.email,
-    protectionLevel: customer.value === '高价值' ? '高' : '中',
-    reason: customer.value === '高价值' ? '高价值客户' : '活跃客户',
-    annualValue: customer.annualPurchase,
-    status: 'active',
-    protectedSince: new Date().toLocaleDateString('zh-CN'),
-    assignedSalesRep: '待分配'
-  })
-  toast(`已将 ${customer.name} 添加到保护白名单`, 'success')
-}
-
-const unprotectCustomer = (customer) => {
-  customer.protected = false
-  customer.protectionReason = ''
-  customer.interventionStatus = 'auto-follow'
-  protectedCustomersList.value = protectedCustomersList.value.filter(c => c.email !== customer.email)
-  toast(`已将 ${customer.name} 从保护白名单中移除`, 'success')
-}
-
 const assignToSalesRep = (customer, salesRep) => {
   customer.interventionStatus = 'assigned-to-sales'
   toast(`已分配 ${customer.name} 给销售人员 ${salesRep}`, 'success')
 }
 
-const pauseCampaignForCustomer = (customer) => {
-  customer.interventionStatus = 'paused'
-  toast(`已暂停向 ${customer.name} 发送营销邮件`, 'success')
-}
-
-const resumeCampaignForCustomer = (customer) => {
-  customer.interventionStatus = 'auto-follow'
-  toast(`已恢复向 ${customer.name} 发送营销邮件`, 'success')
-}
-
 const getChannelStyleByName = (ch) => {
   const styles = {
     '邮件': { background: 'rgba(99, 102, 241, 0.2)', color: 'var(--primary)' },

+ 14 - 0
ui/smarttrade-platform/src/views/emails/detail.vue

@@ -49,6 +49,8 @@
 
 <script setup>
     import { ref, onMounted } from 'vue';
+    import { getEmailDetail } from '@/api/marketing'
+    import { useRouter } from 'vue-router'
 
     const emailLoading = ref(false);
     const emailDetail = ref({
@@ -59,6 +61,18 @@
         sentDate: '2025-10-16 17:20:02',
         content: '<p>Dear all,</p><p>One-stop storage solution focusing on SSD Controller</p>',
     })
+
+    const router = useRouter();
+    let queryData = router.currentRoute.value.query
+    const getData = () => {
+        getEmailDetail({ mailId: queryData.id }).then(res => {
+            console.log('res :>> ', res);
+        })
+    }
+
+    onMounted(() => {
+        getData()
+    })
 </script>
 
 <style scoped>

+ 11 - 20
ui/smarttrade-platform/src/views/emails/edit.vue

@@ -97,37 +97,28 @@
 <script setup>
     import { ref, reactive, onMounted } from 'vue'
     import { useRouter } from 'vue-router'
-    // import {
-    //     sendEmails,
-    //     getEmailDetailContent,
-    //     getUserEmails,
-    //     getCustomerEmails,
-    //     getColleagueEmails,
-    //     saveDraft,
-    //     setDefault,
-    //     getEmailTemplateList,
-    //     subjectRecordList,
-    //     subjectRecordClick
-    // } from '@/api/emails'
-    // import { getPrompt } from '@/api/system/messageTemplate'
-    // import { getToken } from '@/utils/auth';
-    // import request from '@/utils/request';
+    import { getEmailDetail } from '@/api/marketing'
     import ckeditor from '@/components/ckeditor/index.vue'
-    import moment from 'moment';
-    // import SvgIcon from '@/components/SvgIcon/index.vue'
 
     const getContent = (val) => {
         form.value.content = val
     }
 
+    const router = useRouter();
+    let queryData = router.currentRoute.value.query
+    const getData = () => {
+        getEmailDetail({ mailId: queryData.id }).then(res => {
+            console.log('res :>> ', res);
+        })
+    }
+
     const listType = ref('1')
     const copyFlag = ref(true)
     const themeFlag = ref(true)
     const loading = ref(false)
     const dataLoadng = ref(false)
 
-    const router = useRouter();
-    let queryData = router.currentRoute.value.query
+    
 
     const emailsData = ref([])
     const selectedEmail = ref('')
@@ -542,7 +533,7 @@
     }
 
     onMounted(() => {
-        // getUser()
+        getData()
     })
 </script>