Browse Source

修改智贸

Fan 1 week ago
parent
commit
c241f5a0db

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

@@ -1,6 +1,7 @@
 import { get, post, put, remove, upload } from '@/utils/request'
 
 export const getMarketingList = (data = {}) => post('/router/rest/marketing/list', data)
+export const getMarketingById = (id) => get(`/router/rest/marketing/detail/${id}`)
 export const getSopList = (data = {}) => post('/router/rest/api/sop/list', data)
 export const addSop = (data = {}) => post('/router/rest/api/sop/create', data)
 export const editSop = (id, data = {}) => put(`/router/rest/api/sop/${id}`, data)

+ 24 - 8
ui/smarttrade-platform/src/components/addMarketing.vue

@@ -211,7 +211,7 @@
                             </div>
                             <div class="form-group">
                                 <label class="form-label">客户状态</label>
-                                <el-select v-model="customerParams.customerStatusDictValue" clearable placeholder="客户状态">
+                                <el-select v-model="customerParams.customerStatusDictValue" clearable placeholder="客户状态" @change="getCustomerData()">
                                     <el-option label="暂无需求" value="1" />
                                     <el-option label="潜在客户" value="2" />
                                     <el-option label="意向客户" value="3" />
@@ -228,7 +228,7 @@
                         <div class="filter-row">
                             <div class="form-group">
                                 <label class="form-label">客户类型</label>
-                                <el-select v-model="customerParams.customerTypeDictValue" clearable placeholder="客户类型">
+                                <el-select v-model="customerParams.customerTypeDictValue" clearable placeholder="客户类型" @change="getCustomerData()">
                                     <el-option label="供应厂商" value="4" />
                                     <el-option label="代理合作" value="5" />
                                     <el-option label="普通客户" value="1" />
@@ -241,7 +241,7 @@
                             </div>
                             <div class="form-group">
                                 <label class="form-label">客户等级</label>
-                                <el-select v-model="customerParams.customerLevelDictValue" clearable placeholder="客户等级">
+                                <el-select v-model="customerParams.customerLevelDictValue" clearable placeholder="客户等级" @change="getCustomerData()">
                                     <el-option label="A级" value="1" />
                                     <el-option label="B级" value="2" />
                                     <el-option label="C级" value="3" />
@@ -727,12 +727,12 @@
         </div>
     </div>
 
-    <CustomerDetail ref="customerDetailRef" />
+    <CustomerDetail ref="customerDetailRef" @delete="deleteCustomer" @add="addCustomer" />
 </template>
 
 <script setup>
     import { ref, computed } from 'vue'
-    import { getSopList, addMarketing, editMarketing, addSop, editSop, getTradeCustomer } from '@/api/marketing'
+    import { getSopList, getMarketingById, addMarketing, editMarketing, addSop, editSop, getTradeCustomer } from '@/api/marketing'
     import { getListTree } from '@/api/layout'
     import { getUserEmails } from '@/api/emails'
     import { region } from '@/utils/region';
@@ -854,7 +854,7 @@
     
     const customerDetailRef = ref()
     const showCustomerDetail = (type) => {
-        customerDetailRef.value.open()
+        customerDetailRef.value.open(type, customerList.value)
     }
 
     const sopTemplates = ref([])
@@ -1097,12 +1097,15 @@
         isEditMode.value = !!data
         editingCampaignId.value = data?.id || data?.campaignId || data?.marketingId || null
         showCreateModal.value = true
+        customerList.value = []
         resetCampaignForm()
         if (data) {
-            populateCampaignForm(data)
+            getMarketingById(data.id).then(res => {
+                customerList.value = res.result.customerList || []
+                populateCampaignForm(res.result.campaign)
+            })
         }
         getSop(data?.sopId || '')
-        customerList.value = []
         getEmails()
         getTree()
     }
@@ -1116,6 +1119,7 @@
     const regionValue = ref([])
     const getRegion = (value) => {
         customerParams.value.country = value ? value[1] : ''
+        getCustomerData()
     }
     const customerParams = ref({
         blurry: '',
@@ -1142,6 +1146,18 @@
         })
     }
 
+    const deleteCustomer = (data) => {
+        customerList.value = customerList.value.filter(item => item.id !== data.id)
+        ElMessage.success('删除成功')
+    }
+
+    const addCustomer = (data) => {
+        customerList.value.push(data)
+        //去重
+        customerList.value = [...new Set(customerList.value)]
+        ElMessage.success('添加成功')
+    }
+
     const emailList = ref([])
     const getEmails = () => {
         getUserEmails().then(res => {

+ 311 - 11
ui/smarttrade-platform/src/components/marketing/customerDetail.vue

@@ -2,19 +2,19 @@
     <div v-if="showCustomerPreview" class="modal-overlay" @click.self="showCustomerPreview = false">
         <div class="modal" style="max-width: 860px;">
             <div class="modal-header">
-                <div class="modal-title"><i class="fas fa-user-tag"></i>客户详情</div>
+                <div class="modal-title"><i class="fas fa-user-tag"></i>{{ modalType == 'add' ? '精确添加' : '客户详情' }}</div>
                 <button class="modal-close" @click="showCustomerPreview = false"><i class="fas fa-times"></i></button>
             </div>
             <div class="modal-body">
                 <div class="list-header">
-                    <el-form>
-                        <el-form-item label="">
+                    <el-form ref="customerFormRef" :model="customerParams">
+                        <el-form-item label="" prop="blurry">
                             <el-input v-model="customerParams.blurry" :prefix-icon="Search" placeholder="搜索客户信息"></el-input>
                         </el-form-item>
-                        <el-form-item label="">
+                        <el-form-item label="" prop="liaisonName">
                             <el-input v-model="customerParams.liaisonName" :prefix-icon="Search" placeholder="搜索联系人"></el-input>
                         </el-form-item>
-                        <el-form-item label="">
+                        <el-form-item label="" prop="customerStatusDictValue">
                             <el-select v-model="customerParams.customerStatusDictValue" clearable placeholder="客户状态">
                                 <el-option label="暂无需求" value="1" />
                                 <el-option label="潜在客户" value="2" />
@@ -28,11 +28,11 @@
                                 <el-option label="暂时搁置" value="66" />
                             </el-select>
                         </el-form-item>
-                        <el-form-item label="">
+                        <el-form-item label="" prop="regionValue">
                             <el-cascader style="width: 100%" placeholder="区域名称" :options="region"
                                     v-model="regionValue" clearable filterable @change="getRegion" />
                         </el-form-item>
-                        <el-form-item label="">
+                        <el-form-item label="" prop="customerTypeDictValue">
                             <el-select v-model="customerParams.customerTypeDictValue" clearable placeholder="客户类型">
                                 <el-option label="供应厂商" value="4" />
                                 <el-option label="代理合作" value="5" />
@@ -44,7 +44,7 @@
                                 <el-option label="协助项目" value="11" />
                             </el-select>
                         </el-form-item>
-                        <el-form-item label="">
+                        <el-form-item label="" prop="customerLevelDictValue">
                             <el-select v-model="customerParams.customerLevelDictValue" clearable placeholder="客户等级">
                                 <el-option label="A级" value="1" />
                                 <el-option label="B级" value="2" />
@@ -54,12 +54,53 @@
                         </el-form-item>
                     </el-form>
                     <div class="btn-div">
-                        <button class="btn btn-primary" @click="getCustomerList">
+                        <button class="btn btn-primary" @click="searchCustomer">
                             <el-icon><Search /></el-icon>搜索</button>
                         <button class="btn btn-secondary" @click="resetCustomerParams">
                             <el-icon><Refresh /></el-icon>重置</button>
                     </div>
                 </div>
+                <div class="list-body">
+                    <template  v-if="customers && customers.length">
+                        <div class="customer-list">
+                            <div v-for="customer in customers" :key="customer.id" class="customer-card">
+                                <div class="customer-card-left">
+                                    <div class="customer-logo">{{ customer.logoText || customer.customerName.charAt(0) }}</div>
+                                    <div class="customer-meta">
+                                        <div class="customer-name-row">
+                                            <div class="customer-name">{{ customer.customerName }}</div>
+                                            <span class="customer-badge">{{ customer.tag || '潜客' }}</span>
+                                        </div>
+                                        <div class="customer-subtitle">{{ customer.role || '采购总监' }} · {{ customer.company }}</div>
+                                        <div class="customer-info-row">
+                                            <span><i class="fas fa-map-marker-alt"></i> {{ customer.continent }} - {{ customer.country }}</span>
+                                            <span><i class="fas fa-dollar-sign"></i> {{ customer.annualPurchase }}</span>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="customer-card-right">
+                                    <div class="card-right">
+                                        <div class="customer-score">{{ customer.score }}</div>
+                                        <div class="customer-score-label">意向分</div>
+                                    </div>
+                                    <div class="right-btn">
+                                        <el-icon v-if="modalType == 'add'" @click="addCustomer(customer)"><Plus /></el-icon>
+                                        <el-icon v-else @click="deleteCustomer(customer)"><Delete /></el-icon>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <el-pagination
+                            :total="total"
+                            style="text-align: right;"
+                            :current-page="customerParams.pageIndex"
+                            :page-size="customerParams.pageSize"
+                            layout="total, prev, pager, next"
+                            @current-change="customerCurrentChange">
+                        </el-pagination>
+                    </template>
+                    <div v-else class="empty-state">暂无客户数据</div>
+                </div>
             </div>
         </div>
     </div>
@@ -67,15 +108,42 @@
 
 <script setup>
     import { ref } from 'vue'
-    import { Search, Refresh } from '@element-plus/icons-vue'
+    import { Search, Refresh, Plus, Delete } from '@element-plus/icons-vue'
     import { region } from '@/utils/region';
+    import { getTradeCustomer } from '@/api/marketing'
+
+    const emits = defineEmits(['add', 'delete'])
 
+    const customerFormRef = ref()
     const showCustomerPreview = ref(false)
-    const open = () => {
+    const modalType = ref('')
+    const allData = ref([])
+    const open = (type, data) => {
+        customers.value = []
+        total.value = 0
+        modalType.value = type
+        regionValue.value = []
+        customerParams.value = {
+            pageIndex: 1,
+            pageSize: 10,
+            blurry: '',
+            country: '',
+            customerStatusDictValue: '',
+            customerTypeDictValue: '',
+            customerLevelDictValue: '',
+            liaisonName: ''
+        }
+        if(type == 'detail'){
+            allData.value = data
+            customers.value = data.slice(0, 10)
+            total.value = data.length
+        }
         showCustomerPreview.value = true
     }
 
     const customerParams = ref({
+        pageIndex: 1,
+        pageSize: 10,
         blurry: '',
         country: '',
         customerStatusDictValue: '',
@@ -88,6 +156,65 @@
         customerParams.value.country = value ? value[1] : ''
     }
 
+    const customers = ref([])
+    const total = ref(0)
+    const getCustomerData = () => {
+        getTradeCustomer(customerParams.value).then(res => {
+            customers.value = res.result.records || []
+            total.value = res.result.total || 0
+        })
+    }
+    const customerCurrentChange = (page) => {
+        customerParams.value.pageIndex = page
+        if(modalType.value == 'add'){
+            getCustomerData()
+        }else{
+            customers.value = allData.value.slice((page - 1) * customerParams.value.pageSize, page * customerParams.value.pageSize)
+        }
+    }
+
+    const searchCustomer = () => {
+        if(modalType.value == 'add'){
+            customerParams.value.pageIndex = 1
+            getCustomerData()
+        }else{
+            const filterData = allData.value.filter(item => {
+                const blurryMatch = customerParams.value.blurry ? (item.customerName.includes(customerParams.value.blurry) || item.company.includes(customerParams.value.blurry) || item.email.includes(customerParams.value.blurry)) : true
+                const liaisonMatch = customerParams.value.liaisonName ? (item.liaisonName && item.liaisonName.includes(customerParams.value.liaisonName)) : true
+                const statusMatch = customerParams.value.customerStatusDictValue ? (item.customerStatusDictValue == customerParams.value.customerStatusDictValue) : true
+                const typeMatch = customerParams.value.customerTypeDictValue ? (item.customerTypeDictValue == customerParams.value.customerTypeDictValue) : true
+                const levelMatch = customerParams.value.customerLevelDictValue ? (item.customerLevelDictValue == customerParams.value.customerLevelDictValue) : true
+                const countryMatch = customerParams.value.country ? (item.country == customerParams.value.country) : true
+                return blurryMatch && liaisonMatch && statusMatch && typeMatch && levelMatch && countryMatch
+            })
+            customers.value = filterData.slice(0, 10)
+            total.value = filterData.length
+        }
+    }
+
+    const resetCustomerParams = () => {
+        customerFormRef.value.resetFields()
+        regionValue.value = []
+        if(modalType.value == 'add'){
+            customers.value = []
+            total.value = 0
+        }else{
+            customers.value = allData.value.slice(0, 10)
+            total.value = allData.value.length
+        }
+    }
+
+    const deleteCustomer = (data) => {
+        customers.value = customers.value.filter(item => item.id != data.id)
+        allData.value = allData.value.filter(item => item.id != data.id)
+        total.value = allData.value.length
+        emits('delete', data)
+    }
+
+    const addCustomer = (data) => {
+        emits('add', data)
+    }
+
     defineExpose({
         open
     })
@@ -115,6 +242,7 @@
     .list-header{
         display: flex;
         align-items: center;
+        padding-right: 20px;
     }
     .el-form{
         flex: 1;
@@ -137,4 +265,176 @@
         height: 32px;
         padding: 10px 13px;
     }
+    .customer-list {
+        display: flex;
+        flex-direction: column;
+        gap: 12px;
+        padding-right: 20px;
+        flex: 1;
+        overflow: auto;
+    }
+
+    .customer-card {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        gap: 18px;
+        padding: 18px 18px;
+        background: var(--bg-hover);
+        border: 1px solid var(--border);
+        border-radius: 16px;
+    }
+
+    .customer-card-left {
+        display: flex;
+        align-items: center;
+        gap: 14px;
+        min-width: 0;
+    }
+
+    .customer-logo {
+        width: 52px;
+        height: 52px;
+        border-radius: 14px;
+        background: rgba(56, 189, 248, 0.15);
+        color: var(--primary);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-weight: 700;
+        font-size: 18px;
+    }
+
+    .customer-meta {
+        min-width: 0;
+        flex: 1;
+    }
+
+    .customer-name-row {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        margin-bottom: 6px;
+        flex-wrap: wrap;
+    }
+
+    .customer-name {
+        font-size: 15px;
+        font-weight: 700;
+        color: var(--text-primary);
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+    }
+
+    .customer-badge {
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        padding: 2px 10px;
+        border-radius: 999px;
+        background: rgba(59, 130, 246, 0.12);
+        color: var(--primary);
+        font-size: 12px;
+        font-weight: 600;
+    }
+
+    .customer-subtitle {
+        font-size: 13px;
+        color: var(--text-secondary);
+        margin-bottom: 10px;
+        line-height: 1.5;
+    }
+
+    .customer-info-row {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 12px;
+        font-size: 12px;
+        color: var(--text-secondary);
+    }
+
+    .customer-info-row span {
+        display: inline-flex;
+        align-items: center;
+        gap: 6px;
+    }
+
+    .customer-info-row i {
+        width: 14px;
+        text-align: center;
+        color: var(--text-secondary);
+    }
+
+    .customer-card-right {
+        display: flex;
+        align-items: center;
+    }
+
+    .card-right{
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        min-width: 92px;
+    }
+
+    .customer-score {
+        width: 54px;
+        height: 54px;
+        display: flex;
+        border-radius: 50%;
+        align-items: center;
+        background: rgba(34, 197, 94, 0.12);
+        color: var(--success);
+        font-size: 16px;
+        font-weight: 700;
+        justify-content: center;
+    }
+
+    .customer-score-label {
+        margin-top: 8px;
+        font-size: 12px;
+        color: #64748B;
+    }
+
+    .right-btn{
+        width: 42px;
+        height: 28px;
+        background: #1E293B;
+        border-radius: 8px;
+        border: 1px solid #485569;
+        text-align: center;
+        line-height: 28px;
+        cursor: pointer;
+    }
+
+    .empty-state {
+        padding: 60px 0;
+        text-align: center;
+        color: var(--text-secondary);
+    }
+
+    .el-pagination{
+        margin-top: 12px;
+        justify-content: center;
+    }
+    .el-pagination :deep(button), .el-pagination :deep(li){
+        background: transparent!important;
+        color: #FFF;
+    }
+    .el-pagination :deep(li.is-active){
+        color: var(--primary);
+    }
+    .modal-body{
+        display: flex;
+        flex-direction: column;
+        padding-right: 0;
+    }
+    .list-body{
+        flex: 1;
+        overflow: hidden;
+        display: flex;
+        flex-direction: column;
+        margin-top: 16px;
+    }
 </style>