소스 검색

feat: 完善KAM Mapping功能,完成delivery日历面板的样式和功能修改

Jack Zhou 1 개월 전
부모
커밋
a6c94d295e

+ 16 - 0
src/api/module/Delivery.ts

@@ -349,4 +349,20 @@ export const getDeliveryCalendarData = (params: any, config: any) => {
     },
     config
   )
+}
+
+
+/**
+ * 获取delivery calendar 日历的筛选项Consignee
+ */
+export const getDeliveryCalendarConsignee = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_load',
+      operate: 'destination_delivery_calendar_consignee',
+      ...params
+    },
+    config
+  )
 }

+ 2 - 0
src/styles/theme.scss

@@ -374,6 +374,7 @@
   --color-calendar-booking-tag-bg: rgba(130, 0, 218, 0.1);
    --color-calendar-booking-tag-text: #8200da;
   --color-calendar-booking-tag-label-text: rgba(130, 0, 218, 0.5);
+  --color-calendar-booking-tag-item-bg: rgba(130, 0, 218, 0.10);
   --color-calendar-selected-cell-bg: #f4f4f5;
   
   --color-delivery-calendar-delivery-date-sign-bg:rgba(20, 71, 230, 0.1);
@@ -641,6 +642,7 @@
   --color-calendar-booking-tag-bg: rgba(153, 1, 255, 0.2);
   --color-calendar-booking-tag-text: #fff;
   --color-calendar-booking-tag-label-text: rgba(255,255,255,0.5);
+  --color-calendar-booking-tag-item-bg: rgba(153, 1, 255, 0.20);
   --color-calendar-selected-cell-bg: #3d4047;
   
   --color-delivery-calendar-delivery-date-sign-bg:rgba(31, 85, 255, 0.3);

+ 1 - 1
src/utils/axios.ts

@@ -79,7 +79,7 @@ class HttpAxios {
           grouping: true
         })
         emitter.emit('login-out');
-      } else if (response.data.code !== 200 && response.data.code !== 400) {
+      } else if (response.data.code !== 200 && response.data.code !== 400 && response.data.code !== 4001 && response.data.code !== 4002 && response.data.code !== 4003) {
         ElMessageBox.alert(
           response.data?.data?.msg || 'The request failed. Please try again later',
           'Prompt',

+ 1 - 1
src/utils/table.ts

@@ -12,7 +12,7 @@ export const autoWidth = (tableData: VxeGridProps, grid: VxeGridInstance, custom
   const columnsWidth: { width: number; field: any }[] = []
   if (!columns || !data) return
   columns.forEach((column: any) => {
-    if (!column.title) return
+    if (!column.title || column.width) return
     let curStr = ''
     let width = 0
     const field = column.field

+ 148 - 17
src/views/DestinationDelivery/src/components/CalendarView.vue

@@ -4,6 +4,8 @@ import type { Dayjs } from 'dayjs'
 import dayjs from 'dayjs'
 import CalendarTagDetailDialog from './CalendarTagDetailDialog.vue'
 import { useUserStore } from '@/stores/modules/user'
+import { cloneDeep, debounce } from 'lodash'
+import axios from 'axios'
 
 const userStore = useUserStore()
 
@@ -22,11 +24,55 @@ const yearOptions = computed(() => {
 })
 
 const calendarData = ref({})
+const consigneeValue = ref('')
+const consigneeLoading = ref(false)
+const consignessList = ref([])
 
 const getDataByDate = (date: Dayjs, key: string) => {
-  return calendarData.value[date.format('YYYY-MM-DD')]?.[key] ?? 0
+  return calendarData.value?.[date.format('YYYY-MM-DD')]?.[key] ?? 0
 }
 
+const remoteMethod = (query: string) => {
+  currentController.value?.abort()
+
+  const newController = new AbortController()
+  currentController.value = newController
+  consigneeLoading.value = true
+
+  $api
+    .getDeliveryCalendarConsignee(
+      {
+        month: dayjs(displayMonth.value).format('MM/YYYY'),
+        consignee: query
+      },
+      { signal: newController.signal }
+    )
+    .then((res) => {
+      if (!newController.signal.aborted && res.code === 200) {
+        consignessList.value = res.data || []
+      }
+    })
+    .catch((err) => {
+      consignessList.value = []
+      if (!axios.isCancel(err) && !newController.signal.aborted) {
+        ElMessage.error('Failed to load options')
+      }
+    })
+    .finally(() => {
+      // 仅当这是最新请求时,才关闭 loading
+      if (currentController.value === newController) {
+        consigneeLoading.value = false
+      }
+    })
+}
+
+const debouncedRemoteMethod = debounce(remoteMethod, 200)
+const handleVisibleChange = (visible) => {
+  !visible && (consignessList.value = [])
+}
+
+const currentController = ref<AbortController | null>(null)
+
 const emit = defineEmits(['add', 'jumpListPage'])
 const calendarLoading = ref(false)
 const handleAddClick = (date) => {
@@ -80,6 +126,16 @@ const handleTagClick = (type, date) => {
 const jumpListPage = (date) => {
   emit('jumpListPage', date.format('YYYY-MM-DD'))
 }
+
+const test = () => {
+  ElMessageBox.alert(
+    "This account's password has expired and is currently unavailable. Please select a different customer account to continue.",
+    {
+      confirmButtonText: 'OK',
+      confirmButtonClass: 'el-button--dark'
+    }
+  )
+}
 </script>
 
 <template>
@@ -94,7 +150,7 @@ const jumpListPage = (date) => {
       <!-- 自定义头部:value 来自 displayMonth(已同步) -->
       <template #headerRender="{ value, onChange }">
         <div class="custom-header">
-          <div class="label-type destination-booking">
+          <!-- <div class="label-type destination-booking">
             <div class="sign"></div>
             <div class="label">Destination Booking</div>
           </div>
@@ -106,20 +162,26 @@ const jumpListPage = (date) => {
             <div class="sign"></div>
             <div class="label">Free Storage Period Ends</div>
           </div>
-          <div class="grid-lines"></div>
+          <div class="grid-lines"></div> -->
           <a-select
+            :bordered="false"
             :value="value.year()"
-            style="width: 180px; margin-right: 8px"
+            style="width: 140px"
             @change="(year) => handleYearChange(year, onChange)"
           >
             <a-select-option v-for="y in yearOptions" :key="y" :value="y">
               {{ y }}
             </a-select-option>
+            <template #suffixIcon>
+              <!-- 这里可以使用任何图标组件,包括 Element Plus 的 -->
+              <span class="font_family icon-icon_dropdown_b"></span>
+            </template>
           </a-select>
 
           <a-select
+            :bordered="false"
             :value="value.month() + 1"
-            style="width: 80px"
+            style="width: 92px"
             @change="(month) => handleMonthChange(month, onChange)"
           >
             <a-select-option v-for="m in 12" :key="m" :value="m">
@@ -140,21 +202,45 @@ const jumpListPage = (date) => {
                 ][m - 1]
               }}
             </a-select-option>
+            <template #suffixIcon>
+              <!-- 这里可以使用任何图标组件,包括 Element Plus 的 -->
+              <span class="font_family icon-icon_dropdown_b"></span>
+            </template>
           </a-select>
+          <div class="grid-lines"></div>
+
+          <el-select
+            :model-value="consigneeValue"
+            multiple
+            filterable
+            reserve-keyword
+            placeholder="Consignee"
+            :loading="consigneeLoading"
+            style="width: 240px"
+            popper-class="part-id-select-popper"
+            :filter-method="debouncedRemoteMethod"
+            @visible-change="handleVisibleChange"
+          >
+            <el-option
+              v-for="item in consignessList"
+              :key="item.value"
+              :label="item.value"
+              :value="item.value"
+            >
+            </el-option>
+          </el-select>
         </div>
       </template>
 
       <template #dateCellRender="{ current }">
         <ul class="events">
           <!-- 如果不为当前月份的日期,则不显示标签 -->
-          <div
-            class="tags-details"
-            v-if="
+          <!-- v-if="
               calendarData?.[dayjs(current).format('YYYY-MM-DD')] &&
               dayjs(current).isSame(dayjs(displayMonth), 'month')
-            "
-          >
-            <div
+            " -->
+          <div class="tags-details">
+            <!-- <div
               v-if="getDataByDate(current, 'endingNumber') || getDataByDate(current, 'endingCtns')"
               class="ending-tag tag-style"
               @click="handleTagClick('ending', current)"
@@ -179,20 +265,27 @@ const jumpListPage = (date) => {
                 <span class="type">{{ getDataByDate(current, 'shipmentNumber') }} Shipments</span>
                 <span class="ctns-tag">{{ getDataByDate(current, 'shipmentCtns') }} ctns</span>
               </div>
-            </div>
+            </div> -->
+
             <div
-              class="booking-tag"
               v-if="
                 getDataByDate(current, 'bookingNumber') || getDataByDate(current, 'bookingCtns')
               "
+              class="booking-tag"
               @click="jumpListPage(current)"
             >
-              <div class="label">Destination Booking</div>
+              <!-- <div class="label">Destination Booking</div> -->
               <div class="tag-style">
                 <span class="font_family icon-icon_booking_order_b" style="font-size: 12px"></span>
                 <span class="type">{{ getDataByDate(current, 'bookingNumber') }} Bookings</span>
+                <div class="grid-lines"></div>
                 <span class="ctns-tag">{{ getDataByDate(current, 'bookingCtns') }} ctns</span>
               </div>
+              <div class="list">
+                <div class="item" v-for="size in getDataByDate(current, 'ctnrSize')">
+                  {{ size }}
+                </div>
+              </div>
             </div>
           </div>
           <!-- 新增图标 -->
@@ -212,8 +305,6 @@ const jumpListPage = (date) => {
 
 <style scoped lang="scss">
 .calendar-container {
-  // position: relative;
-  // z-index: 99;
   margin-top: -46px;
   padding: 0 24px;
 }
@@ -223,6 +314,20 @@ const jumpListPage = (date) => {
   justify-content: flex-end;
   align-items: center;
   padding: 2px 0;
+  :deep(.ant-select-selection-item) {
+    margin-right: 4px;
+    font-size: 24px;
+    font-weight: 700;
+    text-align: right;
+  }
+  :deep(
+    .ant-select-focused.ant-select:not(.ant-select-disabled):not(.ant-select-customize-input):not(
+        .ant-pagination-size-changer
+      )
+      div.ant-select-selector
+  ) {
+    border: 1px solid transparent !important;
+  }
   .label-type {
     display: flex;
     align-items: center;
@@ -456,7 +561,7 @@ const jumpListPage = (date) => {
 }
 .delivery-tag,
 .booking-tag {
-  height: 36px;
+  // height: 36px;
   width: 100%;
   padding-top: 4px;
   border-radius: 3px;
@@ -498,6 +603,32 @@ const jumpListPage = (date) => {
   .ctns-tag {
     background-color: var(--color-calendar-booking-tag-bg);
   }
+  .grid-lines {
+    height: 12px;
+    width: 1px;
+    margin-left: 6px;
+    background-color: var(--color-calendar-booking-tag-label-text);
+  }
+  .list {
+    padding: 6px 8px 8px;
+    .item {
+      height: 12px;
+      width: 100%;
+      padding: 2px 4px;
+      margin-top: 1px;
+      font-size: 8px;
+      line-height: 10px;
+      background-color: var(--color-calendar-booking-tag-item-bg);
+      // background-color: red;
+      &:first-child {
+        margin-top: 0;
+        border-radius: 3px 3px 0 0;
+      }
+      &:last-child {
+        border-radius: 0 0 3px 3px;
+      }
+    }
+  }
 }
 .ending-tag {
   height: 24px;

+ 0 - 6
src/views/DestinationDelivery/src/components/ConfiguRations/src/ConfiguRations.vue

@@ -26,12 +26,6 @@ const AddRulesTableColumns = ref([
     title: 'Booking Window',
     type: 'normal',
     formatter: ''
-  },
-  {
-    field: 'recommended_delivery_date_desc',
-    title: 'Reecommended Delivery Date',
-    type: 'normal',
-    formatter: ''
   }
 ])
 const containerHeight = useCalculatingHeight(document.documentElement, 290, [filterRef])

+ 66 - 67
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/CreateNewRule.vue

@@ -66,19 +66,18 @@ const InitRuleData = () => {
           }
           countryCheckedList.value = returnData.station
           CountryCheckboxList.value = returnData.CountryCheckedList
-          recommendata.value = returnData.RcommendDeliveryDate
+          // recommendata.value = returnData.RcommendDeliveryDate
         }
       })
   }
 }
 
 const CreateRuleDisabled = computed(() => {
-  // 1. 检查基本条件是否满足
+  // 1. 检查基本条件是否满足 || recommendRadio.value === undefined
   if (
     countryCheckedList.value.length === 0 ||
     selectedCountry.value === '' ||
     windowRadio.value === undefined ||
-    recommendRadio.value === undefined ||
     KLNPLCvalue.value.length === 0
   ) {
     return true
@@ -93,40 +92,40 @@ const CreateRuleDisabled = computed(() => {
   }
 
   // 3. 处理推荐日期条件
-  if (recommendRadio.value !== 1) {
-    // 3.1 确保至少选择了一个运输方式
-    if (recommendCheckedList.value.length === 0) {
-      return true
-    }
+  // if (recommendRadio.value !== 1) {
+  //   // 3.1 确保至少选择了一个运输方式
+  //   if (recommendCheckedList.value.length === 0) {
+  //     return true
+  //   }
 
-    // 3.2 验证航空规则(如果选择了 Air)
-    if (recommendCheckedList.value.includes('Air')) {
-      const isAirValid = recommendCheckedAirList.value.every(
-        (item) =>
-          item.ports.length > 0 &&
-          item.recommended_delivery_from !== '' &&
-          item.recommended_delivery_to !== ''
-      )
+  //   // 3.2 验证航空规则(如果选择了 Air)
+  //   if (recommendCheckedList.value.includes('Air')) {
+  //     const isAirValid = recommendCheckedAirList.value.every(
+  //       (item) =>
+  //         item.ports.length > 0 &&
+  //         item.recommended_delivery_from !== '' &&
+  //         item.recommended_delivery_to !== ''
+  //     )
 
-      if (!isAirValid) return true
-    }
+  //     if (!isAirValid) return true
+  //   }
 
-    // 3.3 验证海运规则(如果选择了 Sea)
-    if (recommendCheckedList.value.includes('Sea')) {
-      const hasInvalidSeaItem = recommendCheckedSeaList.value.some((item) => {
-        const hasValidDeliveryTime = item.recommended_delivery_from && item.recommended_delivery_to
+  //   // 3.3 验证海运规则(如果选择了 Sea)
+  //   if (recommendCheckedList.value.includes('Sea')) {
+  //     const hasInvalidSeaItem = recommendCheckedSeaList.value.some((item) => {
+  //       const hasValidDeliveryTime = item.recommended_delivery_from && item.recommended_delivery_to
 
-        const hasRequiredFields =
-          item.rule_type !== 'Single Dimension'
-            ? item.ports.length > 0 && item.carrier.length > 0
-            : item.ports.length > 0 || item.carrier.length > 0
+  //       const hasRequiredFields =
+  //         item.rule_type !== 'Single Dimension'
+  //           ? item.ports.length > 0 && item.carrier.length > 0
+  //           : item.ports.length > 0 || item.carrier.length > 0
 
-        return !hasValidDeliveryTime || !hasRequiredFields
-      })
+  //       return !hasValidDeliveryTime || !hasRequiredFields
+  //     })
 
-      if (hasInvalidSeaItem) return true
-    }
-  }
+  //     if (hasInvalidSeaItem) return true
+  //   }
+  // }
   // 4. 所有条件都满足,返回 false(不禁用)
   return false
 })
@@ -227,38 +226,38 @@ const handleSubmitRule = () => {
       windowAfterDays.value +
       ' days after'
   }
-  if (recommendRadio.value == 1) {
-    recommendDelivery.value = 'No_Recommended'
-    recommenddetail.value = 'No Specific recommended time for choosing delivery date'
-  } else {
-    recommendDelivery.value = 'Delivery_ETA_ATA'
-    if (recommendCheckedList.value.includes('Air')) {
-      recommenddetail.value +=
-        'Air:\nDefault Rule- Air Port: ALL,\nRecommend Delivery Date: ETA/ATA+' +
-        airlist[0].recommended_delivery_from +
-        ' Days to ETA/ATA+' +
-        airlist[0].recommended_delivery_to +
-        ' Days;\n'
-      airlist.forEach((item) => {
-        item.ports = item.ports.join(',')
-        item.carrier = ''
-      })
-      mergeData = [...airlist]
-    }
-    if (recommendCheckedList.value.includes('Sea')) {
-      recommenddetail.value +=
-        'Sea:\nDefault Rule- ort: ALL, Carrier: ALL,\nRecommend Delivery Date: ETA/ATA+' +
-        seaList[0].recommended_delivery_from +
-        ' Days to ETA/ATA+' +
-        seaList[0].recommended_delivery_to +
-        ' Days;'
-      seaList.forEach((item) => {
-        item.ports = item.ports.join(',')
-        item.carrier = item.carrier.join(',')
-      })
-      mergeData = [...mergeData, ...seaList]
-    }
-  }
+  // if (recommendRadio.value == 1) {
+  //   recommendDelivery.value = 'No_Recommended'
+  //   recommenddetail.value = 'No Specific recommended time for choosing delivery date'
+  // } else {
+  //   recommendDelivery.value = 'Delivery_ETA_ATA'
+  //   if (recommendCheckedList.value.includes('Air')) {
+  //     recommenddetail.value +=
+  //       'Air:\nDefault Rule- Air Port: ALL,\nRecommend Delivery Date: ETA/ATA+' +
+  //       airlist[0].recommended_delivery_from +
+  //       ' Days to ETA/ATA+' +
+  //       airlist[0].recommended_delivery_to +
+  //       ' Days;\n'
+  //     airlist.forEach((item) => {
+  //       item.ports = item.ports.join(',')
+  //       item.carrier = ''
+  //     })
+  //     mergeData = [...airlist]
+  //   }
+  //   if (recommendCheckedList.value.includes('Sea')) {
+  //     recommenddetail.value +=
+  //       'Sea:\nDefault Rule- ort: ALL, Carrier: ALL,\nRecommend Delivery Date: ETA/ATA+' +
+  //       seaList[0].recommended_delivery_from +
+  //       ' Days to ETA/ATA+' +
+  //       seaList[0].recommended_delivery_to +
+  //       ' Days;'
+  //     seaList.forEach((item) => {
+  //       item.ports = item.ports.join(',')
+  //       item.carrier = item.carrier.join(',')
+  //     })
+  //     mergeData = [...mergeData, ...seaList]
+  //   }
+  // }
   if (seaList.length != 0) {
     airData = Object.keys(seaList?.[0])
     airData.forEach((item) => {
@@ -275,10 +274,10 @@ const handleSubmitRule = () => {
       booking_window: bookingWindow.value,
       booking_window_date_start: windowBeforeDays.value,
       booking_window_date_end: windowAfterDays.value,
-      recommended_delivery: recommendDelivery.value,
+      // recommended_delivery: recommendDelivery.value,
       booking_window_desc: bookingdetail.value,
       kln_pic: KLNPLCvalue.value,
-      recommended_delivery_date_desc: recommenddetail.value,
+      // recommended_delivery_date_desc: recommenddetail.value,
       ...airlistInfo
     })
     .then((res: any) => {
@@ -432,7 +431,7 @@ onMounted(() => {
           </div>
         </el-collapse-item>
       </el-collapse>
-      <el-collapse v-model="activeRules" @change="IsThreeActive = !IsThreeActive">
+      <!-- <el-collapse v-model="activeRules" @change="IsThreeActive = !IsThreeActive">
         <el-collapse-item name="RecommendDeliveryDate">
           <template #title>
             <div class="Rules_Title">
@@ -453,7 +452,7 @@ onMounted(() => {
             ></RecommendDate>
           </div>
         </el-collapse-item>
-      </el-collapse>
+      </el-collapse> -->
       <el-collapse v-model="activeRules" @change="IsFourActive = !IsFourActive">
         <el-collapse-item name="KLNPLC">
           <template #title>

+ 16 - 4
src/views/DestinationDelivery/src/components/TableView/src/TableView.vue

@@ -88,7 +88,10 @@ const getTableColumns = async () => {
     }
   })
   nextTick(() => {
-    tableRef.value && autoWidth(tableData.value, tableRef.value)
+    tableRef.value &&
+      autoWidth(tableData.value, tableRef.value, {
+        status: 158
+      })
     tableLoadingColumn.value = false
     selectedTableData.value = []
   })
@@ -174,7 +177,10 @@ const getTableData = async (isPageChange?: boolean) => {
     .finally(() => {
       selectedTableData.value = []
       nextTick(() => {
-        tableRef.value && autoWidth(tableData.value, tableRef.value)
+        tableRef.value &&
+          autoWidth(tableData.value, tableRef.value, {
+            status: 158
+          })
         tableLoadingTableData.value = false
       })
     })
@@ -198,7 +204,10 @@ const searchTableData = () => {
     .finally(() => {
       selectedTableData.value = []
       nextTick(() => {
-        tableRef.value && autoWidth(tableData.value, tableRef.value)
+        tableRef.value &&
+          autoWidth(tableData.value, tableRef.value, {
+            status: 158
+          })
         tableLoadingTableData.value = false
       })
     })
@@ -206,7 +215,10 @@ const searchTableData = () => {
 onMounted(() => {
   Promise.all([getTableColumns()]).finally(() => {
     nextTick(() => {
-      tableRef.value && autoWidth(tableData.value, tableRef.value)
+      tableRef.value &&
+        autoWidth(tableData.value, tableRef.value, {
+          status: 158
+        })
     })
   })
 })

+ 3 - 1
src/views/Layout/src/components/Header/HeaderView.vue

@@ -234,7 +234,9 @@ const handleDemoVideo = () => {
           <span style="margin-top: -1px" class="font_family icon-icon_search_b"></span>
         </template>
       </el-input>
-      <KAMMapping v-if="userStore.isLogin"></KAMMapping>
+      <KAMMapping
+        v-if="userStore.isLogin && userStore.userInfo?.kam_customers_name?.length !== 0"
+      ></KAMMapping>
       <div class="notice-icon" v-if="userStore.isLogin">
         <span v-if="notificationMsgStore.hasNewMsg" class="unread-tip-icon"></span>
         <el-button

+ 96 - 34
src/views/Layout/src/components/Header/components/KAMMapping.vue

@@ -1,6 +1,5 @@
 <script setup lang="ts">
 import { useUserStore } from '@/stores/modules/user'
-import { use } from 'vxe-pc-ui'
 
 const userStore = useUserStore()
 const customer = ref(userStore.customerInfo.name || '')
@@ -29,54 +28,101 @@ const filterCustomer = (query: string) => {
 }
 
 const loadingVisible = ref(false)
-const changeShowMapping = async () => {
-  userStore.setCustomerInfo({ name: '', isShowMapping: isShowMapping.value })
-  customer.value = ''
-  if (!isShowMapping.value && userStore.originalUName !== userStore.userInfo.uname) {
-    try {
-      loadingVisible.value = true
-      const res = await $api.handleCustomerSelection({ uname: userStore.originalUName })
-      if (res.code === 200) {
-        userStore.setUserInfo(
-          {
-            ...res.data.user_info,
-            kam_customers_name: userStore.userInfo.kam_customers_name
-          },
-          true
-        )
-        window.location.reload()
-      }
-    } catch (err) {
-      console.error('Error occurred while selecting customer:', err)
-    } finally {
-      loadingVisible.value = false
-    }
+// 提取通用的核心逻辑
+const processCustomerSwitch = async (targetUname: string, isMappingMode: boolean) => {
+  // 1. 统一更新映射状态 (根据需求,切换时通常先设为目标状态)
+  userStore.setCustomerInfo({
+    name: targetUname || '',
+    isShowMapping: isMappingMode
+  })
+
+  // 如果仅仅是切换映射模式且用户名没变(即 changeShowMapping 中 !isShowMapping 但 uname 相同的情况),则不需要请求后端
+  if (!targetUname) {
+    return
   }
-}
-const changeCustomer = async (cust: string) => {
-  if (cust === customer.value) return
-  customer.value = cust
-  myPopover.value?.hide()
+
   try {
     loadingVisible.value = true
-    const res = await $api.handleCustomerSelection({ uname: cust })
+
+    // 2. 统一调用 API
+    const res = await $api.handleCustomerSelection({ uname: targetUname })
+
     if (res.code === 200) {
+      // 3. 统一处理成功逻辑
       userStore.setUserInfo(
         {
           ...res.data.user_info,
+          // 保留原有的 kam_customers_name,防止被后端数据覆盖(如果这是业务需求)
           kam_customers_name: userStore.userInfo.kam_customers_name
         },
         true
       )
-      userStore.setCustomerInfo({ name: cust, isShowMapping: true })
+      // 先更新本地状态 UI (无网络请求部分)
+      userStore.setCustomerInfo({
+        name: isMappingMode ? targetUname : '', // 原逻辑中这里设为空
+        isShowMapping: isShowMapping.value
+      })
+
+      // 确保切换客户后映射状态为真 (对应原 changeCustomer 的逻辑)
+      // if (isMappingMode) {
+      // userStore.setCustomerInfo({ name: targetUname, isShowMapping: true })
+      // }
+
       window.location.reload()
+    } else if (res.code === 4001) {
+      ElMessageBox.alert(
+        "This account's password has expired and is currently unavailable. Please select a different customer account to continue.",
+        {
+          confirmButtonText: 'OK',
+          confirmButtonClass: 'el-button--dark'
+        }
+      )
+    } else if (res.code === 4002) {
+      ElMessageBox.alert(
+        'This account has not been activated yet. Please select a different customer account to continue.',
+        {
+          confirmButtonText: 'OK',
+          confirmButtonClass: 'el-button--dark'
+        }
+      )
+    } else if (res.code === 4003) {
+      ElMessageBox.alert(
+        'This account has been disabled and is no longer accessible. Please select a different customer account to continue.',
+        {
+          confirmButtonText: 'OK',
+          confirmButtonClass: 'el-button--dark'
+        }
+      )
     }
   } catch (err) {
-    console.error('Error occurred while selecting customer:', err)
+    ElMessage.error('操作失败,请稍后重试') // 给用户一个友好的提示
   } finally {
     loadingVisible.value = false
   }
 }
+
+// 优化后的 changeShowMapping
+const changeShowMapping = async () => {
+  // 清空当前选中的客户展示
+  customer.value = ''
+
+  // 核心判断:只有当【关闭映射】且【当前用户名与原始用户名不一致】时,才需要请求后端切换回原始用户
+  if (!isShowMapping.value && userStore.originalUName !== userStore.userInfo.uname) {
+    await processCustomerSwitch(userStore.originalUName, false)
+  }
+}
+
+// 优化后的 changeCustomer
+const changeCustomer = async (cust: string) => {
+  if (cust === customer.value) return
+
+  // 更新本地选中状态并关闭弹窗
+  customer.value = cust
+  myPopover.value?.hide()
+
+  // 直接调用通用处理函数,传入新用户名,并标记为映射模式
+  await processCustomerSwitch(cust, true)
+}
 watch(
   () => userStore.customerInfo,
   (newVal) => {
@@ -97,10 +143,12 @@ watch(
     element-loading-custom-class="element-loading"
     element-loading-background="rgb(43, 47, 54, 0.7)"
     class="kam-mapping-container"
-    :class="{ focus: visible }"
+    :class="{ focus: visible, 'hide-mapping': !isShowMapping }"
     :style="{ 'justify-content': !isShowMapping ? 'center' : 'space-between' }"
   >
-    <span style="margin-right: 5px" v-if="!isShowMapping">View as Customer</span>
+    <span style="margin-right: 5px; color: var(--color-neutral-3)" v-if="!isShowMapping"
+      >View as Customer</span
+    >
 
     <el-popover
       @show="visible = true"
@@ -141,6 +189,7 @@ watch(
           >
             {{ cust.kam_customers_name }}
           </div>
+          <div class="empty-content" v-if="showCustomerList.length === 0">no data</div>
         </div>
       </div>
     </el-popover>
@@ -154,7 +203,8 @@ watch(
   align-items: center;
   width: 280px;
   height: 32px;
-  margin-left: 20px;
+  margin-left: 16px;
+  margin-right: 4px;
   padding: 0 8px;
   border-radius: 32px;
   line-height: 32px;
@@ -164,6 +214,13 @@ watch(
     background-color: var(--color-mode) !important;
     border-color: var(--color-theme) !important;
   }
+  &.hide-mapping {
+    width: 175px;
+    margin-left: 8px;
+    margin-right: 0px;
+    border: none;
+    background-color: transparent;
+  }
 }
 .select-customer {
   cursor: pointer;
@@ -188,5 +245,10 @@ watch(
       cursor: pointer;
     }
   }
+  .empty-content {
+    height: 100%;
+    line-height: 160px;
+    text-align: center;
+  }
 }
 </style>