2
0

26 Коммитууд 5d1e63772a ... 39d72423ea

Эзэн SHA1 Мессеж Огноо
  Jack Zhou 39d72423ea Merge branch 'master' into reconstruction_booking 2 цаг өмнө
  Jack Zhou b3e8d6c8e9 fix: Tracking筛选项输入框值不会被清除bug 4 өдөр өмнө
  Jack Zhou 4b03bcce77 feat: 首页customer筛选时刷新recent部分 6 өдөр өмнө
  Jack Zhou 9b71c96a95 feat: 首页recent部分新增loading 6 өдөр өмнө
  Jack Zhou 048d5d784f feat: 解决首页重复刷新bug 6 өдөр өмнө
  Jack Zhou c320a20cf8 feat: 整理重构代码 1 долоо хоног өмнө
  Jack Zhou 73d1d89ab2 feat: 合并test分支代码 1 долоо хоног өмнө
  Jack Zhou 0851c116c3 feat: 重构首页 Booking筛选项 解决网络竞态问题 1 долоо хоног өмнө
  Jack Zhou efe69caabb Merge branch 'test' into reconstruction_booking 1 долоо хоног өмнө
  Jack Zhou 43d6d19ee2 feat: 实现首页customer filter功能 1 долоо хоног өмнө
  Jack Zhou 00adee2c68 Merge branch 'test' into reconstruction_booking 1 долоо хоног өмнө
  Jack Zhou 579caba335 feat: 首页新增对customer的筛选 1 долоо хоног өмнө
  Jack Zhou d1cc05cb14 feat: 重构首页筛选项 1 долоо хоног өмнө
  Jack Zhou 3dd8930455 feat: 重构首页筛选功能 1 долоо хоног өмнө
  Jack Zhou 3e5cd64a68 feat: 重构首页跳转功能部分代码 2 долоо хоног өмнө
  Jack Zhou 6277c9b9aa feat: 合并test分支代码 2 долоо хоног өмнө
  Jack Zhou 169f955d6d feat: 删除多余log代码 1 сар өмнө
  Jack Zhou c1662fc396 feat: 调整首页点击传值 1 сар өмнө
  Jack Zhou d2e2f832a3 feat: 调整本地环境 1 сар өмнө
  Jack Zhou c290883407 feat: 重构首页图表跳转功能 1 сар өмнө
  Jack Zhou 40dfcc5d13 feat: 实现首页拖拽暗黑模式样式 1 сар өмнө
  Jack Zhou dc942d53af feat: 解决morefilter部分赋值bug 1 сар өмнө
  Jack Zhou 0b9d818e53 feat: 重构more filter部分代码 1 сар өмнө
  Jack Zhou af53b228a0 feat: 重构Tracking筛选项 2 сар өмнө
  Jack Zhou ae51c41b16 feat: 完成Booking筛选项中 more filters部分的重构 2 сар өмнө
  Jack Zhou b83d51dabf feat: 重构Booking筛选项 2 сар өмнө
37 өөрчлөгдсөн 2774 нэмэгдсэн , 3938 устгасан
  1. 74 54
      src/components/AutoSelect/src/AutoSelect.vue
  2. 112 248
      src/components/DateRange/src/DateRange.vue
  3. 3 0
      src/components/DateRange/src/components/QuickCalendarDate.vue
  4. 188 0
      src/components/DateRange/src/components/VCalendarDate.vue
  5. 49 64
      src/components/FliterTags/src/FilterTags.vue
  6. 84 959
      src/components/MoreFilters/src/MoreFilters.vue
  7. 230 0
      src/components/MoreFilters/src/components/PartiesView.vue
  8. 222 0
      src/components/MoreFilters/src/components/PlacesView.vue
  9. 116 96
      src/components/MoreFilters/src/components/SelectValue.vue
  10. 0 114
      src/components/SelectTable/src/SelectTable copy.vue
  11. 170 156
      src/components/SelectTable/src/SelectTable.vue
  12. 57 152
      src/components/SelectTableSelect/src/SelectTableSelect.vue
  13. 49 175
      src/components/TransportMode/src/TransportMode.vue
  14. 175 194
      src/components/selectAutoSelect/src/selectAutoSelect.vue
  15. 3 0
      src/router/index.ts
  16. 115 0
      src/stores/modules/filtersList.ts
  17. 0 18
      src/stores/modules/loadingState.ts
  18. 8 4
      src/styles/icons/iconfont.css
  19. 0 0
      src/styles/icons/iconfont.js
  20. 2 0
      src/styles/icons/iconfont.svg
  21. BIN
      src/styles/icons/iconfont.ttf
  22. BIN
      src/styles/icons/iconfont.woff
  23. BIN
      src/styles/icons/iconfont.woff2
  24. 4 4
      src/utils/tools.ts
  25. 127 393
      src/views/Booking/src/BookingView.vue
  26. 13 9
      src/views/Booking/src/components/BookingTable/src/BookingTable.vue
  27. 18 4
      src/views/Booking/src/components/BookingTable/src/components/DownloadDialog.vue
  28. 418 418
      src/views/Dashboard/src/DashboardView.vue
  29. 196 0
      src/views/Dashboard/src/components/CustomerFilter.vue
  30. 92 90
      src/views/Dashboard/src/components/DashFiters.vue
  31. 1 1
      src/views/Dashboard/src/components/RecentStatus.vue
  32. 0 11
      src/views/DestinationDelivery/src/components/TableView/src/TableView.vue
  33. 14 30
      src/views/Layout/src/components/Header/HeaderView.vue
  34. 13 1
      src/views/Report/src/components/ReportDetail/src/components/FieldsTable.vue
  35. 179 673
      src/views/Tracking/src/TrackingView.vue
  36. 19 65
      src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue
  37. 23 5
      src/views/Tracking/src/components/TrackingTable/src/components/DownloadDialog.vue

+ 74 - 54
src/components/AutoSelect/src/AutoSelect.vue

@@ -1,27 +1,27 @@
 <script setup lang="ts">
-import { ref, watch } from 'vue'
+import { useFiltersStore } from '@/stores/modules/filtersList'
+import { cloneDeep, debounce } from 'lodash'
+import axios from 'axios'
+
+const filtersStore = useFiltersStore()
 
 const props = defineProps({
-  ASPlaceholder: {
+  placeholder: {
     type: String
   },
-  ASValue: {
+  data: {
     type: Array
   },
-  ASisDisabled: {
-    type: Boolean
-  },
-  ASType: {
-    type: String
-  },
-  ASSearchFiled: {
+
+  type: {
     type: String
   },
-  ASSearchMode: {
+  title: {
     type: String
   },
-  ASSearchObj: {
-    type: Object
+  isDisabled: {
+    type: Boolean,
+    default: false
   }
 })
 
@@ -31,50 +31,56 @@ interface ListItem {
   checked: boolean
 }
 
-const list = ref<ListItem[]>([])
 const options = ref<ListItem[]>([])
-const value = ref(props.ASValue)
-const type = ref(props.ASType)
-const search_field = ref(props.ASSearchFiled)
-const search_mode = ref(props.ASSearchMode)
-const MoreFiltersObj = ref(props.ASSearchObj)
+const pageData = ref(cloneDeep(props.data))
 const loading = ref(false)
+const currentController = ref<AbortController | null>(null)
 watch(
-  () => props.ASValue,
-  (current) => {
-    value.value = current
-  }
-)
-watch(
-  () => props.ASSearchObj,
+  () => props.data,
   (current) => {
-    MoreFiltersObj.value = current
+    pageData.value = cloneDeep(current)
   }
 )
+
 const remoteMethod = (query: string) => {
   if (query) {
+    currentController.value?.abort()
+
+    const newController = new AbortController()
+    currentController.value = newController
     loading.value = true
-    setTimeout(() => {
-      $api
-        .getMoreFiltersData({
+
+    const queryData = filtersStore.getQueryData
+    $api
+      .getMoreFiltersData(
+        {
           term: query,
-          type: type.value,
-          search_field: search_field.value,
-          search_mode: search_mode.value,
-          ...MoreFiltersObj.value
-        })
-        .then((res: any) => {
+          type: props.type,
+          search_field: props.title,
+          search_mode: 'booking',
+          ...queryData
+        },
+        { signal: newController.signal }
+      )
+      .then((res: any) => {
+        if (!newController.signal.aborted && res.code == 200) {
+          options.value = res.data.map((item: any) => {
+            return { value: item, label: item, checked: pageData.value?.includes(item) }
+          })
+        }
+      })
+      .catch((err) => {
+        options.value = []
+        if (!axios.isCancel(err) && !newController.signal.aborted) {
+          ElMessage.error('Failed to load options')
+        }
+      })
+      .finally(() => {
+        // 仅当这是最新请求时,才关闭 loading
+        if (currentController.value === newController) {
           loading.value = false
-          if (res.code == 200) {
-            list.value = res.data.map((item: any) => {
-              return { value: item, label: item, checked: value.value?.includes(item) }
-            })
-            options.value = list.value.filter((item) => {
-              return item.label.toLowerCase().includes(query.toLowerCase())
-            })
-          }
-        })
-    }, 200)
+        }
+      })
   } else {
     options.value = []
   }
@@ -82,7 +88,7 @@ const remoteMethod = (query: string) => {
 const emit = defineEmits(['changeAutoSelect', 'changeFocus'])
 // 选中改变
 const changeAutoSelect = () => {
-  emit('changeAutoSelect', value)
+  emit('changeAutoSelect', pageData.value, props.title)
 }
 // 清除警告样式
 const removeClass = () => {
@@ -94,28 +100,42 @@ const removeClass = () => {
     }
   }
 }
+
+const handleBlur = () => {
+  emit('changeFocus', false)
+  nextTick(() => {
+    options.value = []
+  })
+}
+
+// 防抖版本(可选)
+const debouncedRemoteMethod = debounce(remoteMethod, 200)
+
+onUnmounted(() => {
+  currentController.value?.abort()
+})
 </script>
 <template>
   <div>
     <el-select
-      v-model="value"
+      v-model="pageData"
       multiple
       filterable
       remote
       @change="changeAutoSelect"
       reserve-keyword
-      :placeholder="props.ASPlaceholder"
+      :placeholder="props.placeholder"
       collapse-tags
       @focus="removeClass"
-      @blur="emit('changeFocus', false)"
-      :disabled="props.ASisDisabled"
+      @blur="handleBlur"
+      :disabled="props.isDisabled"
       collapse-tags-tooltip
       :max-collapse-tags="3"
-      :remote-method="remoteMethod"
+      :remote-method="debouncedRemoteMethod"
       :loading="loading"
     >
       <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
-        <el-checkbox :checked="item.checked">
+        <el-checkbox :checked="item.checked" style="flex: 1">
           <span class="label" @click="item.checked = !item.checked">{{ item.value }}</span>
         </el-checkbox>
       </el-option>
@@ -142,4 +162,4 @@ const removeClass = () => {
     color: var(--badge__content--warning);
   }
 }
-</style>
+</style>

+ 112 - 248
src/components/DateRange/src/DateRange.vue

@@ -1,254 +1,123 @@
 <script setup lang="ts">
-import emitter from '@/utils/bus'
 import { ref, watch, onMounted, onBeforeMount } from 'vue'
 import IconDropDown from '@/components/IconDropDown'
-import CalendarDate from './components/CalendarDate.vue'
-import dayjs from 'dayjs'
-import { useUserStore } from '@/stores/modules/user'
+import VCalendarDate from './components/VCalendarDate.vue'
+import { useFiltersStore } from '@/stores/modules/filtersList'
+import { useRoute } from 'vue-router'
 
-const userStore = useUserStore()
-const formatDate = userStore.dateFormat
-const valueFormatDate = 'MM/DD/YYYY'
+const route = useRoute()
+const searchMode = route.path.includes('booking') ? 'booking' : 'tracking'
+const etaKey = searchMode === 'booking' ? ['f_eta_start', 'f_eta_start'] : ['eta_start', 'eta_end']
+const etdKey = searchMode === 'booking' ? ['f_etd_start', 'f_etd_start'] : ['etd_start', 'etd_end']
 
-onMounted(() => {
-  const emitPayload = defaultDate()
-  // 统一 emit(只调用一次)
-  emit('defaultDate', daterangeObj2, emitPayload)
-  emitter.on('clearTag', (tag: any) => {
-    if (tag.includes('ETD')) {
-      clearETdDateRange()
-    }
-    if (tag.includes('ETA')) {
-      clearEtaDateRange()
-    }
-    if (tag.includes('Creation Time')) {
-      clearEtaDateRange()
-    }
-  })
-  emitter.on('clearDaterangeObj', () => {
-    clearDaterangeObj()
-  })
-})
-
-onBeforeMount(() => {
-  emitter.off('clearTag')
-  emitter.off('clearDaterangeObj')
-})
+const filtersStore = useFiltersStore()
+const filtersList = computed(() => filtersStore.filtersList)
 
-const Date_visible = ref(false)
-const DateType = ref()
-const DateTypeoptions = ref([
+const popoverVisible = ref(false)
+const allOtherType = ref([
   {
-    value: 'Creation Time',
-    label: 'Creation Time'
+    title: 'Creation Time',
+    key: ['created_time_start', 'created_time_end']
   }
 ])
-const AddDateType = ref()
-AddDateType.value = []
-const AddType = () => {
-  AddDateType.value.push({
+const otherDateType = ref([])
+
+const getRangeDate = (title: string) => {
+  const data = filtersStore.getFilterByTitle(title)
+  if (data) {
+    const [startStr, endStr] = data.value
+    return [startStr, endStr]
+  } else {
+    return ['', '']
+  }
+}
+const initDate = () => {
+  etdDateRange.value = getRangeDate('ETD')
+  etaDateRange.value = getRangeDate('ETA')
+
+  otherDateType.value = []
+  filtersList.value.forEach((item) => {
+    const curIndex = allOtherType.value.findIndex((type) => type.title === item.title)
+    if (curIndex !== -1) {
+      otherDateType.value.push({
+        title: item.title,
+        value: getRangeDate(item.title)
+      })
+    }
+  })
+}
+
+const addType = () => {
+  otherDateType.value.push({
     label: '',
-    value: ''
+    value: ['', '']
   })
 }
 const deleteType = (i: any) => {
-  AddDateType.value.splice(i, 1)
-  DateType.value = ''
-  clearDateCreation()
-}
-const props = defineProps({
-  isShipment: Boolean
-})
-const etdDateRange = ref()
-etdDateRange.value = []
-const etaDateRange = ref()
-etaDateRange.value = []
-const DateCreation = ref()
-// 查询默认日期
-const defaultDate = () => {
-  // 工具函数:安全格式化日期范围
-  const formatDateRange = (startStr: string | null, endStr: string | null) => {
-    if (!startStr || !endStr) return [null, null]
-    const start = dayjs(startStr)
-    const end = dayjs(endStr)
-    return start.isValid() && end.isValid()
-      ? [start.format(formatDate), end.format(formatDate)]
-      : [null, null]
-  }
+  otherDateType.value.splice(i, 1)
+}
 
-  // 工具函数:设置 daterangeObj2 条目
-  const setRange = (key: string, title: string, startStr: string | null, endStr: string | null) => {
-    const [start, end] = formatDateRange(startStr, endStr)
-    if (start && end) {
-      daterangeObj2[key] = { title, data: [start, end] }
-    }
-  }
+const etdDateRange = ref([])
+const etaDateRange = ref([])
 
-  // 工具函数:设置 AddDateType(仅用于 Creation Time)
-  const setAddDateType = (startStr: string | null, endStr: string | null) => {
-    const [start, end] = formatDateRange(startStr, endStr)
-    AddDateType.value = [{ label: 'Creation Time', value: [start, end] }]
+const emit = defineEmits(['DateRangeSearch', 'clearDaterangeTags'])
+const hasValidDate = (date: any) => {
+  return date && date.length === 2 && date[0] && date[1]
+}
+const DateRangeSearch = () => {
+  if (hasValidDate(etaDateRange.value)) {
+    filtersStore.updateFilter({
+      title: 'ETA',
+      keyType: 'dateRange',
+      value: etaDateRange.value,
+      key: etaKey
+    })
+  } else {
+    filtersStore.deleteFilterByTitle('ETA')
   }
 
-  // 默认时间范围(2个月前月初 到 下个月)
-  const getDefaultRange = () => [
-    dayjs().subtract(2, 'month').startOf('month').format(formatDate),
-    dayjs().add(1, 'month').format(formatDate)
-  ]
-
-  // ----------------------------
-  // 主逻辑开始
-  // ----------------------------
-
-  let trackingData: Record<string, any> = {}
-  let emitPayload: Record<string, any> = {}
-
-  if (props.isShipment) {
-    const clickParams = sessionStorage.getItem('clickParams')
-    if (clickParams && clickParams !== '{}') {
-      // 场景 A: 有 clickParams → 读取 reportList
-      const reportList = JSON.parse(sessionStorage.getItem('reportList') || '{}')
-      trackingData = JSON.parse(sessionStorage.getItem('searchTableQeuryTracking') || '{}')
-      etdDateRange.value = [
-        reportList.etd_start
-          ? dayjs(reportList.etd_start, valueFormatDate).format(formatDate)
-          : null,
-        reportList.etd_end ? dayjs(reportList.etd_end, valueFormatDate).format(formatDate) : null
-      ]
-      etaDateRange.value = [
-        reportList.eta_start
-          ? dayjs(reportList.eta_start, valueFormatDate).format(formatDate)
-          : null,
-        reportList.eta_end ? dayjs(reportList.eta_end, valueFormatDate).format(formatDate) : null
-      ]
-      setRange('ETD', 'ETD', reportList.etd_start, reportList.etd_end)
-      setRange('ETA', 'ETA', reportList.eta_start, reportList.eta_end)
-    } else {
-      // 场景 B: 无 clickParams → 读取 searchTableQeuryTracking
-      const stored = sessionStorage.getItem('searchTableQeuryTracking')
-      if (!stored) {
-        // 子场景 B1: 无存储 → 用默认值
-        const [start, end] = getDefaultRange()
-        etdDateRange.value = [start, end]
-        setRange('ETD', 'ETD', start, end)
-      } else {
-        // 子场景 B2: 有存储
-        trackingData = JSON.parse(stored)
-        etdDateRange.value = [
-          trackingData.etd_start ? trackingData.etd_start : null,
-          trackingData.etd_end ? trackingData.etd_end : null
-        ]
-        etaDateRange.value = [
-          trackingData.eta_start ? trackingData.eta_start : null,
-          trackingData.eta_end ? trackingData.eta_end : null
-        ]
-        setRange('ETD', 'ETD', trackingData.etd_start, trackingData.etd_end)
-        setRange('ETA', 'ETA', trackingData.eta_start, trackingData.eta_end)
-        if (trackingData.created_time_start) {
-          setAddDateType(trackingData.created_time_start, trackingData.created_time_end)
-          setRange(
-            'Creation Time',
-            'Creation Time',
-            trackingData.created_time_start,
-            trackingData.created_time_end
-          )
-        }
-      }
-    }
-    emitPayload = trackingData
+  if (hasValidDate(etdDateRange.value)) {
+    filtersStore.updateFilter({
+      title: 'ETD',
+      keyType: 'dateRange',
+      value: etdDateRange.value,
+      key: etdKey
+    })
   } else {
-    // 非 shipment 场景
-    const stored = sessionStorage.getItem('searchTableQeury')
-    if (!stored) {
-      // 无存储 → 默认值
-      const [start, end] = getDefaultRange()
-      etdDateRange.value = [start, end]
-      setRange('ETD', 'ETD', start, end)
-    } else {
-      // 有存储
-      const queryData = JSON.parse(stored)
-      emitPayload = queryData
-
-      etdDateRange.value = [
-        queryData.f_etd_start ? queryData.f_etd_start : null,
-        queryData.f_etd_end ? queryData.f_etd_end : null
-      ]
-      etaDateRange.value = [
-        queryData.m_eta_start ? queryData.m_eta_start : null,
-        queryData.m_eta_end ? queryData.m_eta_end : null
-      ]
-      setRange('ETD', 'ETD', queryData.f_etd_start, queryData.f_etd_end)
-      setRange('ETA', 'ETA', queryData.m_eta_start, queryData.m_eta_end)
-      if (queryData.created_time_start) {
-        setAddDateType(queryData.created_time_start, queryData.created_time_end)
-        setRange(
-          'Creation Time',
-          'Creation Time',
-          queryData.created_time_start,
-          queryData.created_time_end
-        )
-      }
-    }
+    filtersStore.deleteFilterByTitle('ETD')
   }
-  return emitPayload
-}
-const daterangedata = ref()
-daterangedata.value = []
-let daterangeObj2: any = {}
-const DateRangeChange = (val: any) => {
-  if (val.data != null) {
-    val.data = [
-      dayjs(val.data[0], valueFormatDate).format(formatDate),
-      dayjs(val.data[1], valueFormatDate).format(formatDate)
-    ]
-    daterangeObj2[val.title] = val
-  } else {
-    delete daterangeObj2[val.title]
-    if (val.title == 'ETD') {
-      etdDateRange.value = []
-    } else if (val.title == 'ETA') {
-      etaDateRange.value = []
+
+  allOtherType.value.forEach((item) => {
+    const curIndex = otherDateType.value.findIndex((type) => type.title === item.title)
+    if (curIndex !== -1 && hasValidDate(otherDateType.value[curIndex]?.value)) {
+      filtersStore.updateFilter({
+        title: item.title,
+        keyType: 'dateRange',
+        value: otherDateType.value[curIndex].value,
+        key: item.key
+      })
     } else {
-      DateCreation.value = []
-      AddDateType.value = []
-      DateType.value = ''
+      filtersStore.deleteFilterByTitle(item.title)
     }
-  }
-}
-const emit = defineEmits(['DateRangeSearch', 'clearDaterangeTags', 'defaultDate'])
-const DateRangeSearch = () => {
-  emit('DateRangeSearch', daterangeObj2)
-  Date_visible.value = false
+  })
+  emit('DateRangeSearch')
+  popoverVisible.value = false
 }
 
 // 清除
 const clearRest = () => {
+  filtersStore.deleteFilterByTitle('ETA')
+  filtersStore.deleteFilterByTitle('ETD')
+  allOtherType.value.forEach((item) => {
+    filtersStore.deleteFilterByTitle(item.title)
+  })
   emit('clearDaterangeTags')
-  clearETdDateRange()
-  clearEtaDateRange()
-  clearDateCreation()
-  clearDaterangeObj()
 }
-// 清除EDT
-const clearETdDateRange = () => {
+
+const closeRset = () => {
   etdDateRange.value = []
-  delete daterangeObj2['ETD']
-}
-// 清除EDA
-const clearEtaDateRange = () => {
   etaDateRange.value = []
-  delete daterangeObj2['ETA']
-}
-// 清除Creation Time
-const clearDateCreation = () => {
-  DateCreation.value = []
-  AddDateType.value = []
-  DateType.value = ''
-  delete daterangeObj2['Creation Time']
-}
-// 清除 daterangeObj
-const clearDaterangeObj = () => {
-  daterangeObj2 = {}
+  otherDateType.value = []
 }
 </script>
 <template>
@@ -256,13 +125,17 @@ const clearDaterangeObj = () => {
     <el-popover
       trigger="click"
       :width="400"
-      :visible="Date_visible"
+      :visible="popoverVisible"
       popper-class="DaterangeClass"
-      @before-enter="defaultDate()"
-      @hide="clearDateCreation()"
+      @before-enter="initDate()"
+      @close="closeRset"
     >
       <template #reference>
-        <div class="Date_Range" @blur="Date_visible = false" @click="Date_visible = !Date_visible">
+        <div
+          class="Date_Range"
+          @blur="popoverVisible = false"
+          @click="popoverVisible = !popoverVisible"
+        >
           <div class="select_title">Date Range</div>
           <span class="iconfont_icon">
             <svg class="iconfont icon_dark" aria-hidden="true">
@@ -274,20 +147,12 @@ const clearDaterangeObj = () => {
       <div class="date_header">
         <div class="title">Date Range</div>
         <div class="ETD">
-          <CalendarDate
-            CalendarTitle="ETD"
-            :Date="etdDateRange"
-            @DateRangeChange="DateRangeChange"
-          ></CalendarDate>
+          <VCalendarDate CalendarTitle="ETD" v-model:date="etdDateRange"></VCalendarDate>
         </div>
         <div class="ETA">
-          <CalendarDate
-            CalendarTitle="ETA"
-            :Date="etaDateRange"
-            @DateRangeChange="DateRangeChange"
-          ></CalendarDate>
+          <VCalendarDate CalendarTitle="ETA" v-model:date="etaDateRange"></VCalendarDate>
         </div>
-        <div class="AddType" v-for="(item, index) in AddDateType" :key="item.value">
+        <div class="addType" v-for="(item, index) in otherDateType" :key="item.title">
           <div>
             <div class="ETD_title Date_Title">
               <div class="Date_type">Date Type</div>
@@ -300,28 +165,27 @@ const clearDaterangeObj = () => {
             <el-select
               :suffix-icon="IconDropDown"
               placeholder="Please Select Date Type"
-              v-model="item.label"
+              v-model="item.title"
             >
               <el-option
-                v-for="item in DateTypeoptions"
-                :key="item.value"
-                :label="item.label"
-                :value="item.value"
+                v-for="ite in allOtherType"
+                :key="ite.title"
+                :label="ite.title"
+                :value="ite.title"
               >
               </el-option>
             </el-select>
           </div>
           <div style="margin-top: 16px">
-            <CalendarDate
+            <VCalendarDate
               :CalendarTitle="item.label || 'Date Range'"
               CalendarWidth="352px"
-              :Date="item.value"
-              @DateRangeChange="DateRangeChange"
+              v-model:date="item.value"
               :isType="true"
-            ></CalendarDate>
+            ></VCalendarDate>
           </div>
         </div>
-        <div class="MoreType" @click="AddType" v-if="AddDateType.length != DateTypeoptions.length">
+        <div class="MoreType" @click="addType" v-if="otherDateType.length != allOtherType.length">
           <el-button class="el-button--noborder moretype">+ More Date Type</el-button>
         </div>
         <div class="daterange_bottom">
@@ -416,7 +280,7 @@ const clearDaterangeObj = () => {
   border-radius: var(--border-radius-6);
   height: 40px;
 }
-.AddType {
+.addType {
   background-color: var(--color-header-bg);
   margin: 0 16px 8px 16px;
   padding: 8px;

+ 3 - 0
src/components/DateRange/src/components/QuickCalendarDate.vue

@@ -26,6 +26,9 @@ watch(
   () => props.Date,
   (current) => {
     ETDDate.value = current
+  },
+  {
+    immediate: true
   }
 )
 const emit = defineEmits(['DateRangeChange', 'DateChange'])

+ 188 - 0
src/components/DateRange/src/components/VCalendarDate.vue

@@ -0,0 +1,188 @@
+<script lang="ts" setup>
+import dayjs, { Dayjs } from 'dayjs'
+import { ref, watch } from 'vue'
+import { useUserStore } from '@/stores/modules/user'
+
+const userStore = useUserStore()
+const formatDate = userStore.dateFormat
+const valueFormatDate = 'MM/DD/YYYY'
+// type RangeValue = [Dayjs, Dayjs]
+// const ETDDate = ref<RangeValue>()
+const props = defineProps({
+  CalendarWidth: {
+    type: String,
+    default: '368px'
+  },
+  CalendarTitle: {
+    type: String
+  },
+  date: {
+    type: Array
+  },
+  isType: {
+    type: Boolean,
+    default: false
+  },
+  isNeedFooter: {
+    type: Boolean,
+    default: true
+  },
+  isETA: {
+    type: Boolean,
+    default: false
+  },
+  isShowPopupClass: {
+    type: Boolean,
+    default: false
+  }
+})
+const ETDDate = ref([])
+const CreatePlaceholder = computed(() => {
+  if (props.isETA) {
+    return ['Start ETA Time', 'End ETA Time']
+  } else {
+    return ['Start ATA Time', 'End ATA Time']
+  }
+})
+// watch(
+//   () => props.date,
+//   (current: any) => {
+//     if (current?.length == 2) {
+//       ETDDate.value = [
+//         current[0] ? dayjs(current[0], formatDate).format(valueFormatDate) : '',
+//         current[1] ? dayjs(current[1], formatDate).format(valueFormatDate) : ''
+//       ]
+//     } else {
+//       ETDDate.value = []
+//     }
+//   },
+//   { immediate: true, deep: true }
+// )
+
+const emit = defineEmits(['DateRangeChange', 'DateChange', 'update:date'])
+const open = ref(false)
+const isShowExtra = ref(true)
+
+const DateList = ref()
+DateList.value = []
+const daterange = (val: any) => {
+  if (DateList.value.length == 2 && val != undefined) {
+    const rangedata = {
+      title: props.CalendarTitle,
+      data: DateList.value
+    }
+    emit('DateRangeChange', rangedata)
+  }
+  emit('update:date', props.date)
+}
+const ChangeToday = (val: any) => {
+  if (val == 'Earliest') {
+    DateList.value[0] = dayjs().format(valueFormatDate)
+    daterange(DateList.value[1])
+  } else {
+    DateList.value[1] = dayjs().format(valueFormatDate)
+    daterange(DateList.value[0])
+  }
+}
+
+const Earliest = () => {
+  DateList.value[0] = dayjs('Oct-05-2009').format(valueFormatDate)
+  daterange(DateList.value[1])
+}
+const Latest = () => {
+  DateList.value[1] = dayjs().format(valueFormatDate)
+  daterange(DateList.value[0])
+}
+const changeRangeData = (value: any) => {
+  emit('update:date', value)
+}
+const handleCalendarOpen = (date: any) => {
+  open.value = !open.value
+  if (open.value == false) {
+    if (date.length != 2) {
+      DateList.value = []
+      ETDDate.value = []
+    }
+  }
+}
+</script>
+<template>
+  <div>
+    <div class="ETD_title" v-if="props.CalendarTitle">{{ props.CalendarTitle }}</div>
+    <a-range-picker
+      separator="To"
+      :showToday="false"
+      :style="{
+        width: props.CalendarWidth,
+        backgroundColor: props.isType ? 'var(--more-type-bg-color)' : 'var(--management-bg-color)'
+      }"
+      :popupClassName="props.isShowPopupClass ? 'th-color' : ''"
+      :open="open"
+      @change="changeRangeData"
+      :placeholder="isNeedFooter ? ['Start Time', 'End Time'] : CreatePlaceholder"
+      :format="userStore.dateFormat"
+      @openChange="handleCalendarOpen(ETDDate)"
+      :valueFormat="userStore.dateFormat"
+      :value="props.date"
+    >
+      <template #suffixIcon>
+        <span class="iconfont_icon">
+          <svg class="iconfont icon_suffix" aria-hidden="true">
+            <use xlink:href="#icon-icon_date_b"></use>
+          </svg>
+        </span>
+      </template>
+      <template #renderExtraFooter v-if="isShowExtra && isNeedFooter">
+        <div class="calender_flex">
+          <div class="footer_left">
+            <el-button class="el-button--noborder" @click="Earliest">Earliest Time</el-button>
+            <el-button class="el-button--noborder" @click="ChangeToday('Earliest')"
+              >Today</el-button
+            >
+          </div>
+          <div class="footer_left footer_right">
+            <el-button @click="Latest" class="el-button--noborder">Latest Time</el-button>
+            <el-button class="el-button--noborder" @click="ChangeToday('Latest')">Today</el-button>
+          </div>
+        </div>
+      </template>
+    </a-range-picker>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.calender_flex {
+  display: flex;
+  justify-content: space-between;
+}
+.footer_left {
+  display: flex;
+  flex: 50%;
+  padding: 0 0 0 5px;
+  height: 48px;
+  display: flex;
+  align-items: center;
+}
+.footer_right {
+  border-left: 1px solid var(--border-color-2);
+  padding-left: 8px;
+}
+.el-button--noborder {
+  color: var(--color-theme);
+}
+.ETD_title {
+  font-size: var(--font-size-2);
+  margin-bottom: 4px;
+  color: var(--color-neutral-2);
+}
+.iconfont_icon {
+  margin-right: 0;
+}
+.iconfont {
+  width: 14px;
+  height: 14px;
+}
+.icon_suffix {
+  fill: var(--color-neutral-1);
+}
+</style>

+ 49 - 64
src/components/FliterTags/src/FilterTags.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
-import { ref, onMounted, onBeforeMount, watch, computed } from 'vue'
-import emitter from '@/utils/bus'
+import { cloneDeep } from 'lodash'
+
 interface ListItem {
   name: string
   number: number
@@ -8,73 +8,58 @@ interface ListItem {
   checked: boolean
 }
 interface Props {
-  TagsListItem: ListItem[]
+  tagsList: ListItem[]
 }
 const props = withDefaults(defineProps<Props>(), {})
 
-const TagsList = ref(props.TagsListItem)
-const NewTagsList = ref()
-
-watch(
-  () => props.TagsListItem,
-  (current) => {
-    TagsList.value = current
-  }
-)
-onMounted(() => {
-  emitter.on('clearTag', (tag: any) => {
-    if (tag.includes('Shipment status')) {
-      checkedCount = []
-      // TagsList.value.forEach((item: any) => {
-      //   item.checked = false
-      // })
-      // TagsList.value[0].checked = true
-      NewTagsList.value = ['All']
-      emits('changeTag', NewTagsList.value, false)
-    }
-  })
-})
+const emits = defineEmits(['tabChange'])
 
-onBeforeMount(() => {
-  emitter.off('clearTag')
-})
-
-NewTagsList.value = []
-
-let checkedCount: any[] = []
-const emits = defineEmits(['changeTag'])
-const checkedBox = (i: any, name: any, checked: any) => {
-  if (name == 'All') {
-    checkedCount = []
-    TagsList.value.forEach((item: any) => {
-      item.checked = false
-    })
-    TagsList.value[0].checked = true
-  } else if (!checked) {
-    TagsList.value[0].checked = false
-    TagsList.value[i].checked = !TagsList.value[i].checked
-    checkedCount.push(name)
-    const map = new Map()
-    checkedCount.forEach((item) => map.set(item, true))
-    checkedCount = [...map.keys()]
+const getCheckedTabs = (tagsList: ListItem[]) => {
+  const checkedList = tagsList.filter((item) => item.checked)
+  return checkedList.map((item) => item.name)
+}
+// 判断是否除了all,其他的全选了
+const isAllExceptAllSelected = (tagsList: ListItem[]) => {
+  const curCheckedTags = getCheckedTabs(tagsList)
+  if (curCheckedTags.length === tagsList.length - 1 && !curCheckedTags.includes('All')) {
+    return true
   } else {
-    checkedCount.splice(checkedCount.indexOf(name), 1)
-    TagsList.value[0].checked = false
-    TagsList.value[i].checked = !TagsList.value[i].checked
+    return false
   }
-  if (checkedCount.length === TagsList.value.length - 1 || checkedCount.length == 0) {
-    checkedCount = []
-    TagsList.value.forEach((item: any) => {
-      item.checked = false
+}
+//  点击标签 如果除了all
+const handleTagToggle = (index: any, checked: any) => {
+  // 如果点击的是all,并且当前选中也是all,那么直接返回
+  if (index === 0 && checked) return
+
+  const curTagList = cloneDeep(props.tagsList)
+  if (index !== 0) {
+    const curTag = curTagList[index]
+    const curCheckedTags = getCheckedTabs(curTagList)
+    // 如果只选中了一个标签,并且这个标签不是all,那么改为选中all,取消选中当前选项
+    if (curCheckedTags.includes(curTag.name) && curCheckedTags.length === 1) {
+      curTagList[index].checked = false
+      curTagList[0].checked = true
+      emits('tabChange', curTagList)
+      return
+    }
+    curTagList[index].checked = !checked
+    const isCheckedAll = isAllExceptAllSelected(curTagList)
+    if (isCheckedAll) {
+      curTagList[0].checked = true
+      curTagList.forEach((item, index) => {
+        index !== 0 && (item.checked = false)
+      })
+    } else {
+      curTagList[0].checked = false
+    }
+  } else if (index === 0 && !checked) {
+    curTagList[0].checked = true
+    curTagList.forEach((item, index) => {
+      index !== 0 && (item.checked = false)
     })
-    TagsList.value[0].checked = true
-  }
-  if (checkedCount.length == 0) {
-    NewTagsList.value = ['All']
-  } else {
-    NewTagsList.value = checkedCount
   }
-  emits('changeTag', NewTagsList.value, true)
+  emits('tabChange', curTagList)
 }
 </script>
 
@@ -83,10 +68,10 @@ const checkedBox = (i: any, name: any, checked: any) => {
     <div class="TagsBox">
       <div
         class="list"
-        v-for="(item, index) of TagsList"
-        :key="index"
+        v-for="(item, index) of props.tagsList"
+        :key="item.name"
         :class="[item.checked ? 'checked' : '']"
-        @click="checkedBox(index, item.name, item.checked)"
+        @click="handleTagToggle(index, item.checked)"
       >
         {{ item.name }}
         <div

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 84 - 959
src/components/MoreFilters/src/MoreFilters.vue


+ 230 - 0
src/components/MoreFilters/src/components/PartiesView.vue

@@ -0,0 +1,230 @@
+<script setup lang="ts">
+import { useFiltersStore } from '@/stores/modules/filtersList'
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+const searchMode = route.path.includes('booking') ? 'booking' : 'tracking'
+
+const filtersStore = useFiltersStore()
+const filtersList = computed(() => filtersStore.filtersList)
+
+const pageData = ref([
+  {
+    title: 'Shipper Name',
+    key: 'shipper',
+    type: 'contanct',
+    value: [],
+    placeholder: 'Please input shipper name'
+  },
+  {
+    title: 'Consignee Name',
+    key: 'consignee',
+    type: 'contanct',
+    value: [],
+    placeholder: 'Please input consignee name'
+  }
+])
+const changeAutoSelect = (data: any, title: string) => {
+  const index = pageData.value.findIndex((item: any) => item.title === title)
+  pageData.value[index].value = data
+}
+
+const partyTypeOptions = ref([
+  {
+    title: 'Origin Agent',
+    key: 'origin',
+    type: 'apex'
+  },
+  {
+    title: 'Destination Agent',
+    key: 'agent',
+    type: 'apex'
+  },
+  {
+    title: 'Sales',
+    key: 'sales_rep',
+    type: 'sales'
+  }
+])
+if (searchMode === 'tracking') {
+  partyTypeOptions.value.push(
+    {
+      title: 'Notify Party',
+      key: 'notify',
+      type: 'apex'
+    },
+    {
+      title: 'Bill to',
+      key: 'bill_to',
+      type: 'apex'
+    },
+    {
+      title: 'Destination Operator',
+      key: 'dest_op',
+      type: 'sales'
+    }
+  )
+}
+
+const moreList = ref([])
+
+const initData = () => {
+  moreList.value = []
+  pageData.value.forEach((item) => {
+    item.value = []
+  })
+  filtersList.value.forEach((item) => {
+    pageData.value.forEach((pageItem) => {
+      if (item.title === pageItem.title) {
+        pageItem.value = item.value as string[]
+      }
+    })
+    partyTypeOptions.value.forEach((typeItem) => {
+      if (item.title === typeItem.title) {
+        moreList.value.push({
+          title: item.title,
+          value: item.value
+        })
+      }
+    })
+  })
+}
+
+const generate3UniqueIds = () => {
+  return Math.floor(Math.random() * 900000) + 100000
+}
+const addType = () => {
+  moreList.value.push({
+    title: '',
+    id: generate3UniqueIds(),
+    value: []
+  })
+}
+
+const changeTitle = (data: any, title: string) => {
+  const index = moreList.value.findIndex((item) => item.id === data.id)
+  moreList.value[index].title = title
+  moreList.value[index].value = []
+}
+const changeValue = (data: any, value: any) => {
+  const index = moreList.value.findIndex((item) => item.id === data.id)
+  moreList.value[index].value = value
+}
+const deleteItem = (title: string) => {
+  const index = moreList.value.findIndex((item) => item.title === title)
+  moreList.value.splice(index, 1)
+}
+
+const getQueryData = () => {
+  pageData.value.forEach((item: any) => {
+    if (item.value.length) {
+      filtersStore.updateFilter({
+        title: item.title,
+        key: item.key,
+        value: item.value,
+        keyType: 'array'
+      })
+    } else {
+      filtersStore.deleteFilterByTitle(item.title)
+    }
+  })
+  moreList.value.forEach((item) => {
+    partyTypeOptions.value.forEach((ite) => {
+      if (item.title === ite.title) {
+        item.key = ite.key
+      }
+    })
+  })
+
+  partyTypeOptions.value.forEach((item) => {
+    const index = moreList.value.findIndex((ite) => ite.title === item.title && ite.value.length)
+    if (index !== -1) {
+      const ite = moreList.value[index]
+      filtersStore.updateFilter({
+        title: ite.title,
+        key: ite.key,
+        value: ite.value,
+        keyType: 'array'
+      })
+    } else {
+      filtersStore.deleteFilterByTitle(item.title)
+    }
+  })
+}
+const getBadgeData = () => {
+  let count = 0
+  pageData.value.forEach((item) => {
+    if (item.value.length) {
+      count++
+    }
+  })
+  moreList.value.forEach((item) => {
+    if (item.value.length) {
+      count++
+    }
+  })
+  return count
+}
+
+defineExpose({
+  getQueryData,
+  initData,
+  getBadgeData
+})
+</script>
+
+<template>
+  <div>
+    <div class="ETD" v-for="item in pageData" :key="item.title">
+      <div class="ETD_title">{{ item.title }}</div>
+      <AutoSelect
+        :type="item.type"
+        :title="item.title"
+        :data="item.value"
+        :placeholder="item.placeholder"
+        @changeAutoSelect="changeAutoSelect"
+      >
+      </AutoSelect>
+    </div>
+
+    <SelectAutoSelect
+      :typeOptions="partyTypeOptions"
+      :data="moreList"
+      @deleteItem="deleteItem"
+      @changeTitle="changeTitle"
+      @changeValue="changeValue"
+      placeholder="Please input party name"
+    >
+    </SelectAutoSelect>
+    <div class="MoreType" @click="addType" v-if="partyTypeOptions.length != moreList.length">
+      <el-button class="el-button--noborder moretype">+ More Party Type</el-button>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.ETD {
+  margin: 8px 16px;
+}
+
+.ETA {
+  margin: 16px 16px;
+}
+
+.ETD_title {
+  font-size: var(--font-size-2);
+  color: var(--color-neutral-2);
+}
+
+.MoreType {
+  color: var(--color-accent-2);
+  padding: 0 0 16px 16px;
+  cursor: pointer;
+}
+
+.moretype {
+  background-color: transparent;
+  padding: 0 4px;
+  height: 24px;
+}
+</style>

+ 222 - 0
src/components/MoreFilters/src/components/PlacesView.vue

@@ -0,0 +1,222 @@
+<script setup lang="ts">
+import { useFiltersStore } from '@/stores/modules/filtersList'
+import { cloneDeep } from 'lodash'
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+const searchMode = route.path.includes('booking') ? 'booking' : 'tracking'
+
+const filtersStore = useFiltersStore()
+const filtersList = computed(() => filtersStore.filtersList)
+
+const pageData = ref([
+  {
+    title: 'Origin',
+    key: 'shipper_city',
+    value: [],
+    placeholder: 'Please input shipper name'
+  },
+  {
+    title: 'Destination',
+    key: 'consignee_city',
+    value: [],
+    placeholder: 'Please input consignee name'
+  }
+])
+const moreList = ref([])
+const placeTypeOptions = ref([
+  {
+    title: 'Place of delivery',
+    key: 'place_of_delivery/place_of_delivery_exp',
+    needKey: 'city'
+  },
+  {
+    title: ' Place of Receipt',
+    key: 'place_of_receipt/place_of_receipt_exp',
+    needKey: 'city'
+  },
+  {
+    title: 'Port of Loading',
+    key: 'fport_of_loading_uncode/fport_of_loading_exp',
+    type: 'uncode'
+  }
+])
+if (searchMode === 'tracking') {
+  placeTypeOptions.value.push({
+    title: 'Place of Discharge',
+    key: 'Place of Discharge',
+    needKey: 'city'
+  })
+}
+
+const initData = () => {
+  moreList.value = []
+  pageData.value.forEach((item) => {
+    item.value = []
+  })
+  filtersList.value.forEach((item) => {
+    pageData.value.forEach((ite) => {
+      if (item.title === ite.title) {
+        ite.value = item.value as string[]
+      }
+    })
+    placeTypeOptions.value.forEach((typeItem) => {
+      if (item.title === typeItem.title) {
+        moreList.value.push({
+          title: item.title,
+          value: item.value
+        })
+      }
+    })
+  })
+}
+
+const changePageData = (data: any, title: string) => {
+  const index = pageData.value.findIndex((item: any) => item.title === title)
+  if (index !== -1) {
+    pageData.value[index].value = cloneDeep(data)
+  }
+}
+
+const generate3UniqueIds = () => {
+  return Math.floor(Math.random() * 900000) + 100000
+}
+const addType = () => {
+  moreList.value.push({
+    title: '',
+    needKey: '',
+    id: generate3UniqueIds(),
+    value: []
+  })
+}
+
+const changeTitle = (id: any, title: string) => {
+  const needKey = placeTypeOptions.value.find((item) => item.title === title)?.needKey
+  moreList.value.forEach((item) => {
+    if (item.id === id) {
+      item.value = []
+      item.title = title
+      item.needKey = needKey
+    }
+  })
+}
+const deleteType = (id) => {
+  moreList.value = moreList.value.filter((item) => item.id !== id)
+}
+const changeData = (data: any, title: string) => {
+  const index = moreList.value.findIndex((item: any) => item.title === title)
+  if (index !== -1) {
+    moreList.value[index].value = cloneDeep(data)
+  }
+}
+
+const getQueryData = () => {
+  pageData.value.forEach((item: any) => {
+    if (item.value.length) {
+      filtersStore.updateFilter({
+        title: item.title,
+        key: item.key,
+        value: item.value,
+        keyType: 'array'
+      })
+    } else {
+      filtersStore.deleteFilterByTitle(item.title)
+    }
+  })
+  moreList.value.forEach((item) => {
+    placeTypeOptions.value.forEach((ite) => {
+      if (item.title === ite.title) {
+        item.key = ite.key
+      }
+    })
+  })
+
+  placeTypeOptions.value.forEach((item) => {
+    const index = moreList.value.findIndex((ite) => ite.title === item.title && ite.value.length)
+    if (index !== -1) {
+      const ite = moreList.value[index]
+      filtersStore.updateFilter({
+        title: ite.title,
+        key: ite.key,
+        value: ite.value,
+        keyType: 'array'
+      })
+    } else {
+      filtersStore.deleteFilterByTitle(item.title)
+    }
+  })
+}
+
+const getBadgeData = () => {
+  let count = 0
+  pageData.value.forEach((item) => {
+    if (item.value.length) {
+      count++
+    }
+  })
+  moreList.value.forEach((item) => {
+    if (item.value.length) {
+      count++
+    }
+  })
+  return count
+}
+
+defineExpose({
+  initData,
+  getQueryData,
+  getBadgeData
+})
+</script>
+
+<template>
+  <div class="ETD" v-for="item in pageData">
+    <div class="ETD_title">{{ item.title }}</div>
+    <SelectTable :title="item.title" :data="item.value" @input="changePageData" />
+  </div>
+  <SelectTableSelect
+    :data="moreList"
+    :dateTypeoptions="placeTypeOptions"
+    @changeData="changeData"
+    @changeTitle="changeTitle"
+    @deleteType="deleteType"
+    placeholder="Please input places name"
+  >
+  </SelectTableSelect>
+  <!-- More Place Type -->
+  <div class="MoreType" @click="addType()" v-if="moreList.length !== placeTypeOptions.length">
+    <el-button class="el-button--noborder moretype">+ More Place Type</el-button>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.icon_delete {
+  fill: var(--color-danger);
+  cursor: pointer;
+}
+
+.ETD {
+  margin: 8px 16px;
+}
+
+.ETD_title {
+  font-size: var(--font-size-2);
+  color: var(--color-neutral-2);
+}
+
+.MoreType {
+  color: var(--color-accent-2);
+  padding: 0 0 16px 16px;
+  cursor: pointer;
+}
+
+:deep(.el-overlay) {
+  background-color: transparent;
+}
+
+.moretype {
+  background-color: transparent;
+  padding: 0 4px;
+  height: 24px;
+}
+</style>

+ 116 - 96
src/components/MoreFilters/src/components/SelectValue.vue

@@ -1,101 +1,69 @@
 <script setup lang="ts">
-import { ref, watch, computed } from 'vue'
 import type { DropdownInstance } from 'element-plus'
+import { cloneDeep } from 'lodash'
+import { useFiltersStore } from '@/stores/modules/filtersList'
+
+const filtersStore = useFiltersStore()
 
 interface ListItem {
   name: string
   checked: boolean
 }
 interface Props {
-  TransportListItem: ListItem[]
-  title: String
-  Serval: String
-  checkAll: boolean
-}
-const props = withDefaults(defineProps<Props>(), {
-  checkAll: false
-})
-const TransportList = ref(props.TransportListItem)
-const Title = ref(props.title)
-const Serval = ref(props.Serval)
-const checkAll = ref(props.checkAll)
-watch(
-  () => props.TransportListItem,
-  (current) => {
-    TransportList.value = current
-  }
-)
-watch(
-  () => props.Serval,
-  (current) => {
-    Serval.value = current
-  }
-)
+  transportListData: ListItem[]
+  title: string
+  key: string
+}
+const props = withDefaults(defineProps<Props>(), {})
+const transportList = ref(props.transportListData)
+const title = ref(props.title)
+const checkAll = ref()
+const selectData = ref()
 watch(
-  () => props.checkAll,
+  () => props.transportListData,
   (current) => {
-    checkAll.value = current
+    transportList.value = cloneDeep(current)
+    checkAll.value = transportList.value.every((item) => {
+      return item.checked
+    })
+  },
+  {
+    immediate: true,
+    deep: true
   }
 )
 
 const dropdown1 = ref<DropdownInstance>()
-let checkedCount: any[] = []
 
-const handleCheckAllChange = (val: any) => {
-  checkedCount = []
-  TransportList.value.forEach((item: any) => {
-    if (val) {
-      item.checked = true
-      checkedCount.push(item.name)
-    } else {
-      item.checked = false
-      checkedCount = []
-    }
+const handleCheckAll = (val: any) => {
+  transportList.value.forEach((item: any) => {
+    item.checked = val
   })
 }
-const handleCheckedTransportChange = (value: any, checked: any, index: any) => {
-  if (checked) {
-    checkedCount.push(value)
-    const map = new Map()
-    checkedCount.forEach((item) => map.set(item, true))
-    checkedCount = [...map.keys()]
-  } else {
-    if (checkedCount.length == 1) {
-      checkedCount.splice(0, 1)
-    } else {
-      checkedCount.splice(index, 1)
-    }
-  }
-  checkAll.value = checkedCount.length === TransportList.value.length
+const handleCheckedTransport = () => {
+  checkAll.value = transportList.value.every((item) => item.checked)
 }
 
 // 清除选中
 const clearList = () => {
   checkAll.value = false
-  TransportList.value.forEach((item: any) => {
+  transportList.value.forEach((item: any) => {
     item.checked = false
   })
-  changedata.value = ''
-  checkedCount = []
+  selectData.value = ''
 }
-const emit = defineEmits(['generalSearch'])
-const changedata = ref()
-const changedata2 = ref()
+
 //点击搜索
 const TransportSearch = (visible: any) => {
-  if (checkedCount.length == TransportList.value.length) {
-    changedata.value = 'All'
-    changedata2.value = 'All'
+  if (checkAll.value) {
+    selectData.value = 'All'
   } else {
-    changedata.value = checkedCount.join(', ')
-    changedata2.value = checkedCount
-  }
-  const TransportData = {
-    title: Title.value,
-    data: changedata.value
+    selectData.value = transportList.value
+      .filter((item) => item.checked)
+      .map((item) => item.name)
+      .join(', ')
   }
-  Serval.value = changedata.value
-  emit('generalSearch', TransportData, changedata2.value)
+  seeall.value = false
   if (!dropdown1.value) return
   if (visible) {
     dropdown1.value.handleClose()
@@ -107,33 +75,78 @@ const seeall = ref(false)
 const clickSeeAll = () => {
   seeall.value = !seeall.value
 }
+
+const getQueryData = () => {
+  if (!selectData.value) {
+    filtersStore.deleteFilterByTitle(props.title)
+    return
+  }
+  if (checkAll.value) {
+    filtersStore.updateFilter({
+      title: props.title,
+      key: props.key,
+      value: ['All'],
+      keyType: 'array'
+    })
+  } else {
+    filtersStore.updateFilter({
+      title: props.title,
+      key: props.key,
+      value: selectData.value.split(', '),
+      keyType: 'array'
+    })
+  }
+}
+
+const initData = () => {
+  const filterData = filtersStore.getFilterByTitle(props.title)
+  if (filterData) {
+    if (filterData.value == 'All') {
+      transportList.value.forEach((item) => {
+        item.checked = true
+      })
+      selectData.value = 'All'
+    } else {
+      filterData.value.forEach((item) => {
+        transportList.value.forEach((item2) => {
+          if (item2.name == item) {
+            item2.checked = true
+          } else {
+            item2.checked = false
+          }
+        })
+      })
+    }
+    selectData.value = filterData.value.join(', ')
+  } else {
+    transportList.value.forEach((item) => {
+      item.checked = false
+    })
+    selectData.value = ''
+  }
+}
+
+defineExpose({
+  getQueryData,
+  initData
+})
 </script>
 <template>
-  <div class="select" :class="{ isDisabled: TransportList.length == 0 }">
+  <div class="select" :class="{ isDisabled: transportList?.length == 0 }">
     <el-dropdown
       ref="dropdown1"
       trigger="click"
       :hide-on-click="false"
-      :disabled="TransportList.length == 0"
+      :disabled="transportList?.length == 0"
     >
       <div class="el-dropdown-link">
-        <div
-          v-if="
-            props.title == 'Incoterms' && (Serval == 'Please Select Date Range' || Serval == '')
-          "
-          class="select_title"
-        >
+        <div v-if="props.title == 'Incoterms' && !selectData" class="select_title">
           Please Select Date Range
         </div>
-        <div
-          v-else-if="
-            props.title == 'Service' && (Serval == 'Please Select Service' || Serval == '')
-          "
-          class="select_title"
-        >
+        <div v-else-if="props.title == 'Service' && !selectData" class="select_title">
           Please Select Service
         </div>
-        <div v-else class="select_title_2">{{ Serval }}</div>
+        <div v-else class="select_title_2">{{ selectData }}</div>
         <span class="iconfont_icon">
           <svg class="iconfont icon_dark" aria-hidden="true">
             <use xlink:href="#icon-icon_dropdown_b"></use>
@@ -142,21 +155,21 @@ const clickSeeAll = () => {
       </div>
       <template #dropdown>
         <div class="dropdownwidth">
-          <div class="title">{{ Title }}</div>
+          <div class="title" style="margin-bottom: 3px">{{ title }}</div>
           <el-dropdown-menu>
             <el-dropdown-item>
-              <el-checkbox v-model="checkAll" class="checkbox" @change="handleCheckAllChange">
+              <el-checkbox v-model="checkAll" class="checkbox" @change="handleCheckAll">
                 <div class="checkbox_title">Select All</div>
               </el-checkbox>
             </el-dropdown-item>
-            <el-divider></el-divider>
+            <el-divider style="border-color: var(--input-border)"></el-divider>
             <div class="inval" :class="{ seeall: seeall }">
-              <el-dropdown-item v-for="(item, index) in TransportList" :key="index">
+              <el-dropdown-item v-for="(item, index) in transportList" :key="index">
                 <el-checkbox
                   :value="item.name"
                   v-model="item.checked"
                   class="checkbox"
-                  @change="handleCheckedTransportChange(item.name, item.checked, index)"
+                  @change="handleCheckedTransport()"
                 >
                   <div class="checkbox_title">
                     {{ item.name }}
@@ -197,23 +210,26 @@ const clickSeeAll = () => {
 .select {
   cursor: pointer;
   width: 368px;
-  height: 32px;
   display: flex;
   align-items: center;
   justify-content: space-between;
   background-color: white;
-  border: 1px solid var(--color-select-border);
-  border-radius: var(--border-radius-6);
   background-color: transparent;
 }
-.select:hover {
-  border: 1px solid var(--color-theme);
-}
+
 .el-dropdown-link {
   width: 360px;
+  min-height: 32px;
+  padding-top: 3px;
   display: flex;
   align-items: center;
   justify-content: space-between;
+  border-radius: var(--border-radius-6);
+  border: 1px solid var(--color-select-border);
+
+  &:hover {
+    border: 1px solid var(--color-theme);
+  }
 }
 .select_title {
   font-size: var(--font-size-3);
@@ -225,6 +241,7 @@ const clickSeeAll = () => {
   font-size: var(--font-size-3);
   font-weight: 400;
   margin-left: 8.53px;
+  line-height: 21px;
   color: var(--color-neutral-1);
 }
 .title {
@@ -291,6 +308,9 @@ const clickSeeAll = () => {
 :deep(.el-dropdown-menu__item) {
   padding: 0;
   margin: 10px 16px;
+  &:first-child {
+    margin-top: 0;
+  }
 }
 :deep(.el-dropdown-menu__item:not(.is-disabled):focus) {
   background-color: var(--border-hover-color);
@@ -330,7 +350,7 @@ const clickSeeAll = () => {
 }
 .seeall {
   max-height: 426px;
-  overflow: scroll;
+  overflow-y: auto;
 }
 .isDisabled {
   background-color: var(--input-disabled-bg-color);

+ 0 - 114
src/components/SelectTable/src/SelectTable copy.vue

@@ -1,114 +0,0 @@
-<script setup lang="ts" name="SelTable">
-
-const emit = defineEmits(['check', 'input'])
-
-const props = defineProps({
-  value: {
-    type: Array,
-    default: () => []
-  },
-  addTagOnKeys: {
-    type: Array,
-    default: () => []
-  },
-  readOnly: {
-    type: Boolean,
-    default: false
-  },
-})
-
-const tagInputRef: any = ref(null)
-const newTag = ref('')
-const innerTags = ref([...props.value])
-
-watch(
-  () => props.value,
-  () => {
-    innerTags.value = [...props.value]
-  }
-)
-
-function focusTagInput() {
-  if (props.readOnly || !tagInputRef.value) {
-    return
-  } else {
-    tagInputRef.value.focus()
-  }
-}
-
-function inputTag(ev: any) {
-  newTag.value = ev.target.value
-}
-function remove(index: number) {
-  innerTags.value.splice(index, 1)
-  tagChange()
-}
-function addNew(e: any) {
-  if (e && (!props.addTagOnKeys.includes(e.keyCode)) && (e.type !== 'blur')) {
-    return
-  }
-  if (e) {
-    e.stopPropagation()
-    e.preventDefault()
-  }
-  let addSuccess = false
-  if (newTag.value.includes(',')) {
-    newTag.value.split(',').forEach(item => {
-      if (addTag(item.trim())) {
-        addSuccess = true
-      }
-    })
-  } else {
-    if (addTag(newTag.value.trim())) {
-      addSuccess = true
-    }
-  }
-  if (addSuccess) {
-    tagChange()
-    newTag.value = ''
-  }
-}
-function addTag(tag: any) {
-  tag = tag.trim()
-  if (tag && !innerTags.value.includes(tag)) {
-    innerTags.value.push(tag)
-    return true
-  }
-  return false
-}
-
-function removeLastTag() {
-  if (newTag.value) {
-    return
-  }
-  innerTags.value.pop()
-  tagChange()
-}
-function tagChange() {
-  emit('input', innerTags.value)
-}
-
-
-</script>
-<template>
-  <div class="el-input el-input--suffix el-tooltip__trigger" @click="focusTagInput">
-    <div class="el-input__wrapper">
-      <el-space>
-        <template v-for="(tag, idx) in innerTags" :key="tag">
-          <el-tag v-bind="$attrs" type="info" size="small" round :closable="!readOnly" :disable-transitions="false"
-            @close="remove(idx)">
-            {{ tag }}
-          </el-tag>
-        </template>
-        <input v-if="!readOnly" ref="tagInputRef" class="el-input__inner" @input="inputTag" :value="newTag"
-          @keydown.delete.stop="removeLastTag" @keydown="addNew" @blur="addNew" />
-      </el-space>
-    </div>
-  </div>
-</template>
-
-<style scoped lang="scss">
-.el-input__wrapper {
-  justify-content: flex-start;
-}
-</style>

+ 170 - 156
src/components/SelectTable/src/SelectTable.vue

@@ -1,178 +1,192 @@
 <script setup lang="ts" name="SelTable">
-import _ from 'lodash'
-import { reactive, ref, onMounted, watch } from 'vue'
+import { ref, reactive, watch, onUnmounted, nextTick } from 'vue'
 import { ElMessage } from 'element-plus'
 import { formatNumber } from '@/utils/tools'
+import { useFiltersStore } from '@/stores/modules/filtersList'
+import { cloneDeep, debounce } from 'lodash'
+import { useRoute } from 'vue-router'
+import axios from 'axios'
 
-const emit = defineEmits(['check', 'input'])
+const filtersStore = useFiltersStore()
+const route = useRoute()
+const searchMode = route.path.includes('booking') ? 'booking' : 'tracking'
+
+const emit = defineEmits(['input'])
 const props = defineProps({
-  poverWidth: {
-    type: Number,
-    default: 380
-  },
-  searchInput: {
-    type: Array,
-    default: () => []
-  },
-  disabled: {
-    type: Boolean,
-    default: false
-  },
-  keyVal: {
-    type: String,
-    default: 'city'
-  },
-  isError: {
-    type: Boolean,
-    default: false
-  },
-  ASSearchMode: {
-    type: String,
-    default: ''
-  },
-  ASSearchFiled: {
-    type: String,
-    default: ''
-  },
-  ASSearchObj: {
-    type: Object
-  }
+  poverWidth: { type: Number, default: 380 },
+  data: { type: Array, default: () => [] },
+  needKey: { type: String, default: 'city' },
+  isError: { type: Boolean, default: false },
+  title: { type: String, default: '' },
+  disabled: { type: Boolean, default: false }
 })
 
-const searchVal = ref(null)
-const innerTags: any = ref([])
-const tagInputRef: any = ref(null)
+const searchVal = ref('')
+const innerTags = ref<string[]>([])
+const tagInputRef = ref<HTMLInputElement | null>(null)
+
+// 用于取消上一次搜索请求
+let searchAbortController: AbortController | null = null
 
 watch(
-  () => props.searchInput,
+  () => props.data,
   (current) => {
-    innerTags.value = current
+    innerTags.value = cloneDeep(current as string[])
   },
-  {
-    deep: true,
-    immediate: true
-  }
+  { deep: true, immediate: true }
 )
-// 响应数据
-const state: any = reactive({
+
+const state = reactive({
   poverShow: false,
   currentPage: 1,
   pageSize: 10,
   total: 0,
-  activeRowIndex: '',
   tableData: [],
   loading: false
 })
 
-// 点击行
-const handleRowClick = (row: any) => {
-  state.poverShow = false
-  if (!innerTags.value.includes(row[props.keyVal])) {
-    innerTags.value.push(row[props.keyVal])
-    state.activeRowIndex = row.id
-    emit('input', innerTags.value)
-    emit('check', innerTags.value)
-  } else {
-    ElMessage({
-      message: 'Cannot add duplicate cities.',
-      type: 'success'
-    })
+// —————— 获取数据 ——————
+const fetchData = async (query: string, page: number, isPageChange = false) => {
+  if (!isPageChange) {
+    if (searchAbortController) {
+      searchAbortController.abort()
+    }
+    searchAbortController = new AbortController()
   }
-  searchVal.value = null
-}
-const handleSearch = _.debounce((val?) => {
-  state.loading = true
-  searchVal.value = val?.target?.value
-  let filterList: any = []
-  setTimeout(() => {
-    $api
-      .getMoreFiltersTableData({
-        term: searchVal.value ? searchVal.value : '',
-        cp: state.currentPage,
-        ps: state.pageSize,
-        rc: '-1',
-        search_field: props.ASSearchFiled,
-        search_mode: props.ASSearchMode,
-        ...props.ASSearchObj
-      })
-      .then((res: any) => {
-        if (res.code == 200) {
-          filterList = res.data.searchData
-          state.loading = false
-          filterList = res.data.searchData.filter((p: any) => {
-            return (
-              p.country.toLowerCase().indexOf(searchVal.value.toLowerCase()) > -1 ||
-              p.city.toLowerCase().indexOf(searchVal.value.toLowerCase()) > -1 ||
-              p.uncode.toLowerCase().indexOf(searchVal.value.toLowerCase()) > -1
-            )
-          })
-          state.tableData = filterList
-          state.total = Number(res.data.rc)
-          state.loading = false
-        }
-      })
-  }, 800)
-}, 500)
-// 分页 请求接口
-const handleCurrentChange = () => {
+
   state.loading = true
-  let filterList: any = []
-  setTimeout(() => {
-    $api
-      .getMoreFiltersTableData({
-        term: searchVal.value ? searchVal.value : '',
-        cp: state.currentPage,
+
+  try {
+    const rc = isPageChange ? String(state.total) : '-1'
+    const queryData = filtersStore.getQueryData
+
+    const res = await $api.getMoreFiltersTableData(
+      {
+        term: query?.trim(),
+        cp: page,
         ps: state.pageSize,
-        rc: state.total,
-        search_field: props.ASSearchFiled,
-        search_mode: props.ASSearchMode,
-        ...props.ASSearchObj
-      })
-      .then((res: any) => {
-        if (res.code == 200) {
-          filterList = res.data.searchData
-          state.loading = false
-          filterList = res.data.searchData.filter((p: any) => {
-            return (
-              p.country.toLowerCase().indexOf(searchVal.value.toLowerCase()) > -1 ||
-              p.city.toLowerCase().indexOf(searchVal.value.toLowerCase()) > -1 ||
-              p.uncode.toLowerCase().indexOf(searchVal.value.toLowerCase()) > -1
-            )
-          })
-          state.tableData = filterList
-          state.total = Number(res.data.rc)
-          state.loading = false
-        }
-      })
-  }, 800)
+        rc,
+        search_field: props.title,
+        search_mode: searchMode,
+        ...queryData
+      },
+      { signal: searchAbortController?.signal }
+    )
+
+    if (searchAbortController?.signal.aborted) return
+
+    if (res.code === 200) {
+      state.tableData = res.data.searchData || []
+      state.total = Number(res.data.rc || 0)
+    } else {
+      state.tableData = []
+      state.total = 0
+    }
+  } catch (err) {
+    if (!axios.isCancel(err) && !searchAbortController?.signal.aborted) {
+      ElMessage.error('Failed to load data')
+      state.tableData = []
+      state.total = 0
+    }
+  } finally {
+    if (!searchAbortController?.signal.aborted) {
+      state.loading = false
+    }
+  }
 }
-// 删除
+
+// —————— 防抖搜索 ——————
+const debouncedSearch = debounce((query: string) => {
+  state.currentPage = 1
+  fetchData(query, 1, false)
+}, 300)
+
+// —————— 事件处理 ——————
+const handleSearchInput = () => {
+  const val = searchVal.value
+  if (val.trim() === '') {
+    state.tableData = []
+    state.total = 0
+    state.currentPage = 1
+  } else {
+    debouncedSearch(val)
+  }
+}
+
+const handlePageChange = () => {
+  fetchData(searchVal.value, state.currentPage, true)
+}
+
+const handleRowClick = (row: any) => {
+  const keyVal = row[props.needKey]
+  if (innerTags.value.includes(keyVal)) {
+    ElMessage.success('Cannot add duplicate cities.')
+    return
+  }
+
+  innerTags.value.push(keyVal)
+  emit('input', innerTags.value, props.title)
+
+  // 关闭弹窗并重置搜索
+  state.poverShow = false
+  searchVal.value = ''
+  state.tableData = []
+  state.currentPage = 1
+  state.total = 0
+}
+
 const remove = (idx: number) => {
   innerTags.value.splice(idx, 1)
-  emit('input', innerTags.value)
-  emit('check', innerTags.value)
+  emit('input', innerTags.value, props.title)
 }
 
-const onInput = () => {
-  tagInputRef.value.focus()
+// 👇 仅在点击 reference 区域时打开弹窗
+const openPopover = () => {
+  if (props.disabled) return
+  state.poverShow = true
+  nextTick(() => {
+    tagInputRef.value?.focus()
+  })
 }
+
+const clearSearchInput = () => {
+  searchVal.value = ''
+  state.tableData = []
+  state.currentPage = 1
+  state.total = 0
+  if (searchAbortController) {
+    searchAbortController.abort()
+    searchAbortController = null
+  }
+}
+
+defineExpose({ clearSearchInput })
+
+onUnmounted(() => {
+  if (searchAbortController) {
+    searchAbortController.abort()
+  }
+})
 </script>
+
 <template>
   <div>
+    <!-- 手动控制弹窗 -->
     <el-popover
       v-model:visible="state.poverShow"
       placement="bottom-start"
       :teleported="false"
       :width="props.poverWidth"
-      trigger="click"
+      trigger="manual"
     >
       <template #reference>
+        <!-- 点击此区域才打开弹窗 -->
         <div
           class="el-input el-input--suffix el-tooltip__trigger"
-          @click="disabled ? null : onInput"
+          @click="openPopover"
           :class="{ is_error: props.isError }"
         >
-          <div class="el-input__wrapper" :class="{ 'is-disabled': disabled }">
+          <div class="el-input__wrapper" :class="{ 'is-disabled': props.disabled }">
             <el-space :class="[innerTags.length ? 'custom-el-spaceno' : 'custom-el-space']">
               <template v-for="(item, idx) in innerTags" :key="item">
                 <template v-if="idx <= 2">
@@ -181,7 +195,7 @@ const onInput = () => {
                   </el-tag>
                 </template>
               </template>
-              <template v-if="innerTags.length && innerTags.length > 3">
+              <template v-if="innerTags.length > 3">
                 <el-popover placement="bottom" trigger="hover">
                   <template #reference>
                     <el-tag type="info" size="small" round :disable-transitions="false">
@@ -212,19 +226,20 @@ const onInput = () => {
                 </el-popover>
               </template>
               <input
-                :disabled="props.disabled"
-                :value="searchVal"
+                v-model="searchVal"
                 ref="tagInputRef"
                 :placeholder="innerTags.length ? '' : 'Please input country/city/uncode'"
                 class="el-input__inner"
+                :disabled="props.disabled"
                 type="text"
-                @input="handleSearch"
-                @click="handleSearch"
+                @input="handleSearchInput"
+                @keydown.enter.stop.prevent
+                @focus="debouncedSearch('')"
               />
             </el-space>
-            <div class="el-input__suffix">
-              <div class="el-input__suffix-inner" v-if="!disabled">
-                <el-icon :class="state.poverShow ? 'reverse' : ''">
+            <div class="el-input__suffix" v-if="!props.disabled">
+              <div class="el-input__suffix-inner">
+                <el-icon :class="{ reverse: state.poverShow }">
                   <CaretBottom />
                 </el-icon>
               </div>
@@ -232,6 +247,8 @@ const onInput = () => {
           </div>
         </div>
       </template>
+
+      <!-- 数据表格 -->
       <el-table
         :data="state.tableData"
         border
@@ -239,11 +256,14 @@ const onInput = () => {
         v-loading="state.loading"
         @row-click="handleRowClick"
         header-row-class-name="cus-header"
+        style="width: 100%"
       >
-        <el-table-column property="country" label="Country" />
+        <el-table-column property="country" label="Country" width="75" />
         <el-table-column property="city" label="City" />
-        <el-table-column property="uncode" label="Uncode" />
+        <el-table-column property="uncode" label="Uncode" width="80" />
       </el-table>
+
+      <!-- 分页 -->
       <div class="pagination">
         <span>Total {{ formatNumber(state.total) }}</span>
         <el-pagination
@@ -253,8 +273,8 @@ const onInput = () => {
           layout="prev, pager, next"
           :pager-count="5"
           :total="state.total"
-          @current-change="handleCurrentChange"
-          @size-change="handleCurrentChange"
+          @current-change="handlePageChange"
+          @size-change="handlePageChange"
         />
       </div>
     </el-popover>
@@ -290,22 +310,16 @@ const onInput = () => {
   background-color: var(--color-table-header-bg) !important;
 }
 
-:deep(.el-table__row) {
-  td {
-    cursor: pointer;
-  }
+:deep(.el-table__row) td {
+  cursor: pointer;
 }
 
-:deep(.el-table__row:not(.current-row):hover) {
-  td {
-    background-color: var(--color-btn-default-bg-hover) !important;
-  }
+:deep(.el-table__row:not(.current-row):hover) td {
+  background-color: var(--color-btn-default-bg-hover) !important;
 }
 
-:deep(.current-row) {
-  td {
-    background-color: #ffe3cd !important;
-  }
+:deep(.current-row) td {
+  background-color: #ffe3cd !important;
 }
 
 .pagination {

+ 57 - 152
src/components/SelectTableSelect/src/SelectTableSelect.vue

@@ -2,171 +2,89 @@
 import { ref, watch } from 'vue'
 import IconDropDown from '@/components/IconDropDown'
 import SelectTable from '@/components/SelectTable'
+import { cloneDeep } from 'lodash'
 
-interface TypeItem {
-  placesType: ''
-  placesname: []
-}
-interface dateoptions {
-  value: string
-  label: string
-}
 interface Props {
-  AddDateType: TypeItem[]
-  ASPlaceholder: string
-  DateTypeoptions: dateoptions[]
-  selectedPartyTypeoptions: Array<string>
-  TablesearchTableQeury: Object
-  TablesearchMode: String
-}
-interface optionsItem {
-  value: string
-  label: string
+  data: any[]
+  placeholder: string
+  dateTypeoptions: any[]
 }
+
 const props = withDefaults(defineProps<Props>(), {})
-const AddType: any = ref([])
-const dataTypeoptions = ref<optionsItem[]>([])
-const typeSelectIndex = ref(-1)
-watch(
-  () => props.selectedPartyTypeoptions,
-  (newV) => {
-    let arr: any = []
-    if (newV.length == 0) {
-      arr = props.DateTypeoptions
-    } else {
-      props.DateTypeoptions.forEach((item: any) => {
-        let index = newV.findIndex((con: any) => {
-          return con == item.value
-        })
-        if (index < 0) {
-          arr.push(item)
-        }
-      })
-    }
-    dataTypeoptions.value = arr
-  },
-  {
-    immediate: true,
-    deep: true
-  }
-)
+
+const pageData = ref(props.data)
 
 watch(
-  () => props.AddDateType,
-  (val) => {
-    AddType.value = val
+  () => props.data,
+  (newVal) => {
+    pageData.value = cloneDeep(newVal)
   },
   {
-    immediate: true,
     deep: true
   }
 )
 
-// 删除当前more type
-const deleteType = (i: any) => {
-  emit('delSelect', i, AddType.value[i].placesType)
-  AddType.value.splice(i, 1)
-}
-// 选中type改变
-const changeSelect = (val: any) => {
-  emit('changeAutoSelectAddType', typeSelectIndex.value, val)
-}
-// 选中name改变
-const emit = defineEmits(['changeAutoSelectAddType', 'delSelect', 'changeAutoSelect'])
-const errorBoolean = ref(false)
-let AutoSelectObj: any = {}
-let AutoSelectObj2: any = {}
-const changeAutoSelect = (val: any, value: any) => {
-  if (value.length) {
-    errorBoolean.value = true
-  } else {
-    errorBoolean.value = false
-  }
-  AutoSelectObj[val] = value.join()
-  AutoSelectObj2[val] = value
-  emit('changeAutoSelect', AutoSelectObj, AutoSelectObj2, errorBoolean.value)
-}
-const typeSelectFocus = (index: any, e: any) => {
-  typeSelectIndex.value = index
-}
-const typeSelectBlur = () => {
-  typeSelectIndex.value = -1
-  let arr: any = []
-  if (props.selectedPartyTypeoptions.length == 0) {
-    arr = props.DateTypeoptions
-  } else {
-    props.DateTypeoptions.forEach((item: any) => {
-      let index = props.selectedPartyTypeoptions.findIndex((con: any) => {
-        return con == item.value
-      })
-      if (index < 0) {
-        arr.push(item)
-      }
+const originnalTypeOptions = ref(props.dateTypeoptions)
+
+// 计算排除其他已选项后的可用选项
+const getAvailableOptions = (curTitle: string) => {
+  // 获取其他下拉已选的值(排除自己)
+  const otherTypeOptions = pageData.value
+    .filter((type) => {
+      return type.title !== curTitle
     })
-  }
-  dataTypeoptions.value = arr
-}
-const typeSelectClick = (index: any, val: any) => {
-  if (AddType.value[index].placesType) {
-    let j = props.DateTypeoptions.findIndex((item: any) => {
-      return item.value == AddType.value[index].placesType
+    .map((type) => {
+      return type.title
     })
-    let i = dataTypeoptions.value.findIndex((item: any) => {
-      return item.value == AddType.value[index].placesType
+  return originnalTypeOptions.value.filter((type) => {
+    return !otherTypeOptions.includes(type.title)
+  })
+}
+
+const deleteType = (id: number) => {
+  emit('deleteType', id)
+}
+const selectTableRef = ref()
+
+// 在title变化时,清空搜索框
+const changeTitle = (title: string, item: any) => {
+  // item.value = []
+  emit('changeTitle', item.id, title)
+  nextTick(() => {
+    const index = pageData.value.findIndex((type) => {
+      return type.id === item.id
     })
-    if (i < 0) {
-      dataTypeoptions.value.push(props.DateTypeoptions[j])
-    }
-  } else {
-    let arr: any = []
-    if (props.selectedPartyTypeoptions.length == 0) {
-      arr = props.DateTypeoptions
-    } else {
-      props.DateTypeoptions.forEach((item: any) => {
-        let index = props.selectedPartyTypeoptions.findIndex((con: any) => {
-          return con == item.value
-        })
-        if (index < 0) {
-          arr.push(item)
-        }
-      })
-    }
-    dataTypeoptions.value = arr
-  }
+    selectTableRef.value[index].clearSearchInput()
+  })
 }
+const emit = defineEmits(['changeTitle', 'deleteType', 'changeData'])
 
-const checkdestination = (row: any, index: any) => {
-  if (row) {
-    AddType.value[index].placesname = row
-    changeAutoSelect(AddType.value[index].placesType, AddType.value[index].placesname)
-  }
+const changePageData = (data: any, title: string) => {
+  emit('changeData', data, title)
 }
 </script>
 <template>
-  <div class="AddType" v-for="(item, index) in AddType" :key="index">
+  <div class="AddType" v-for="item in pageData" :key="item.id">
     <div>
       <div class="Date_Title">
         <div class="ETD_title">Places Type</div>
-        <span class="iconfont_icon" @click="deleteType(index)">
+        <span class="iconfont_icon" @click="deleteType(item.id)">
           <svg class="iconfont icon_delete" aria-hidden="true">
             <use xlink:href="#icon-icon_delete_b"></use>
           </svg>
         </span>
       </div>
       <el-select
-        v-model="AddType[index].placesType"
+        :model-value="item.title"
         :suffix-icon="IconDropDown"
-        @blur="typeSelectBlur"
-        @focus="typeSelectFocus(index, $event)"
-        @click="typeSelectClick(index, $event)"
-        @change="changeSelect(AddType[index].placesType)"
+        @change="changeTitle($event, item)"
         placeholder="Please Select Party Type"
       >
         <el-option
-          v-for="item in dataTypeoptions"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value"
+          v-for="type in getAvailableOptions(item.title)"
+          :key="type.title"
+          :label="type.title"
+          :value="type.title"
         >
         </el-option>
       </el-select>
@@ -174,28 +92,15 @@ const checkdestination = (row: any, index: any) => {
     <div style="margin-top: 16px">
       <div class="ETD_title">Places Details</div>
       <SelectTable
-        :ASSearchFiled="AddType[index].placesType"
-        :ASSearchObj="props.TablesearchTableQeury"
-        :ASSearchMode="props.TablesearchMode"
-        :key-val="
-          AddType[index].placesType &&
-          (AddType[index].placesType === 'Port of Loading' ||
-            AddType[index].placesType === 'Port of Discharge')
-            ? 'uncode'
-            : 'city'
-        "
-        :is-error="
-          AddType[index].placesType != '' && AddType[index].placesname.length == 0 ? true : false
-        "
-        :disabled="AddType[index].placesType ? false : true"
-        :searchInput="AddType[index].placesname"
-        @check="(row) => checkdestination(row, index)"
+        ref="selectTableRef"
+        :title="item.title"
+        :data="item.value"
+        :needKey="item.needKey"
+        :disabled="!item.title ? true : false"
+        @input="changePageData"
       />
 
-      <div
-        class="error"
-        v-if="AddType[index].placesType != '' && AddType[index].placesname.length == 0"
-      >
+      <div class="error" v-if="item.title != '' && item.value.length === 0">
         Please Input Places Details
       </div>
     </div>

+ 49 - 175
src/components/TransportMode/src/TransportMode.vue

@@ -1,8 +1,9 @@
 <script setup lang="ts">
-import { ref, onMounted, onBeforeMount, watch, computed } from 'vue'
 import type { DropdownInstance } from 'element-plus'
-import emitter from '@/utils/bus'
 import { cloneDeep } from 'lodash'
+import { useFiltersStore } from '@/stores/modules/filtersList'
+
+const filtersStore = useFiltersStore()
 
 interface ListItem {
   name: string
@@ -12,203 +13,76 @@ interface ListItem {
   checked: boolean
 }
 interface Props {
-  TransportListItem: ListItem[]
-  isShipment: Boolean
+  transportListItem: ListItem[]
 }
 const props = withDefaults(defineProps<Props>(), {})
-const TransportList = ref(props.TransportListItem)
+const transportList = ref()
+const checkAll = ref(false)
+const dropdownVisible = ref<DropdownInstance>()
 const updateTransportList = (data: any) => {
-  TransportList.value = cloneDeep(data)
-  TransportList.value.forEach((item: any) => {
-    if (item.checked) {
-      checkedCount.push(item.sname)
-      const map = new Map()
-      checkedCount.forEach((item) => map.set(item, true))
-      checkedCount = [...map.keys()]
-    }
-    if (checkedCount.length == TransportList.value.length) {
-      checkAll.value = true
-    }
-  })
+  transportList.value = cloneDeep(data)
+  const isCheckedAll = transportList.value?.every((item: any) => item.checked)
+  isCheckedAll ? (checkAll.value = true) : (checkAll.value = false)
 }
 watch(
-  () => props.TransportListItem,
+  () => props.transportListItem,
   (current) => {
     updateTransportList(current)
+  },
+  {
+    immediate: true,
+    deep: true
   }
 )
 
-onMounted(() => {
-  emitter.on('clearTag', (tag: any) => {
-    if (tag.includes('Transport Mode')) {
-      clearList()
-    }
-  })
-  defaultTransport()
-})
-
-onBeforeMount(() => {
-  emitter.off('clearTag')
-})
-
-const checkAll = ref(false)
-const dropdownVisible = ref<DropdownInstance>()
-let checkedCount: any[] = []
-const TotalAll = computed(() => {
-  var total_sum = 0
-  if (TransportList.value != undefined) {
-    TransportList.value.map((item) => {
-      total_sum += item.number
-    })
-  }
-  return total_sum
+const totalNumber = computed(() => {
+  return (
+    transportList.value?.filter((item) => {
+      return item.checked
+    }).length || 0
+  )
 })
 
-const handleCheckAllChange = (val: any) => {
-  checkedCount = []
-  TransportList.value.forEach((item: any) => {
-    if (val) {
-      item.checked = true
-      checkedCount.push(item.sname)
-    } else {
-      item.checked = false
-      checkedCount = []
-    }
+const handleCheckAllChange = (checked: any) => {
+  transportList.value.forEach((item: any) => {
+    item.checked = checked
   })
 }
-const handleCheckedTransportChange = (value: any, checked: any) => {
-  if (checked) {
-    checkedCount.push(value)
-    const map = new Map()
-    checkedCount.forEach((item) => map.set(item, true))
-    checkedCount = [...map.keys()]
-  } else {
-    checkedCount.splice(checkedCount.indexOf(value), 1)
-  }
-  checkAll.value = checkedCount.length === TransportList.value.length
+const handleCheckedTransportChange = (item: any) => {
+  const isCheckedAll = transportList.value.every((item: any) => item.checked)
+  isCheckedAll ? (checkAll.value = true) : (checkAll.value = false)
 }
 
 // 清除选中
 const clearList = () => {
   checkAll.value = false
-  if (TransportList.value != undefined) {
-    TransportList.value.forEach((item: any) => {
+  if (transportList.value != undefined) {
+    transportList.value.forEach((item: any) => {
       item.checked = false
     })
   }
-  changedata.value = ''
-  checkedCount = []
-  emit('clearTransportTags')
-}
-const emit = defineEmits(['TransportSearch', 'clearTransportTags', 'defaultTransport'])
-const changedata = ref()
-//点击搜索
-const TransportData = {
-  title: 'Transport Mode',
-  data: ['']
-}
-const TransportSearch = (visible: any) => {
-  TransportList.value.forEach((item: any) => {
-    if (item.checked) {
-      checkedCount.push(item.sname)
-      const map = new Map()
-      checkedCount.forEach((item) => map.set(item, true))
-      checkedCount = [...map.keys()]
-    }
-  })
-  if (checkedCount.length == TransportList.value.length) {
-    changedata.value = ['All']
-  } else {
-    changedata.value = checkedCount
-  }
-  TransportData.data = changedata.value
-  emit('TransportSearch', TransportData)
-  if (!dropdownVisible.value) return
-  if (visible) {
-    dropdownVisible.value.handleClose()
-  } else {
-    dropdownVisible.value.handleOpen()
-  }
 }
-const searchTableQeury = ref()
-const searchTableQeuryTracking = ref()
-const defaultTransport = () => {
-  if (props.isShipment) {
-    if (
-      sessionStorage.getItem('clickParams') == null ||
-      sessionStorage.getItem('clickParams') == '{}'
-    ) {
-      if (sessionStorage.getItem('searchTableQeuryTracking') == null) {
-        checkAll.value = true
-        TransportData.data = ['All']
-      } else {
-        searchTableQeuryTracking.value =
-          JSON.parse(sessionStorage.getItem('searchTableQeuryTracking') as string) || {}
-        if (searchTableQeuryTracking.value.transport_mode != undefined) {
-          if (searchTableQeuryTracking.value.transport_mode.length !== 0) {
-            TransportData.data = searchTableQeuryTracking.value.transport_mode
-            searchTableQeuryTracking.value.transport_mode.forEach((item: any) => {
-              if (item == 'All') {
-                checkAll.value = true
-              } else {
-                checkAll.value = false
-              }
-            })
-          }
-        } else {
-          TransportData.data = []
-          checkAll.value = false
-        }
-      }
-      emit('defaultTransport', TransportData, searchTableQeuryTracking.value)
-    } else {
-      const data = JSON.parse(sessionStorage.getItem('reportList') as string) || {}
-      searchTableQeuryTracking.value =
-        JSON.parse(sessionStorage.getItem('searchTableQeuryTracking') as string) || {}
-      if (data.transport_mode) {
-        const obj = {
-          title: 'Transport Mode',
-          data: data.transport_mode
-        }
-        data.transport_mode.forEach((item: any) => {
-          if (item == 'All') {
-            checkAll.value = true
-          }
-        })
-        emit('defaultTransport', obj, searchTableQeuryTracking.value)
-      }
-    }
-  } else {
-    if (sessionStorage.getItem('searchTableQeury') == null) {
-      checkAll.value = true
-      TransportData.data = ['All']
-    } else {
-      searchTableQeury.value =
-        JSON.parse(sessionStorage.getItem('searchTableQeury') as string) || {}
-      if (searchTableQeury.value.transport_mode !== undefined) {
-        if (searchTableQeury.value.transport_mode.length !== 0) {
-          TransportData.data = searchTableQeury.value.transport_mode
-          searchTableQeury.value.transport_mode.forEach((item: any) => {
-            if (item == 'All') {
-              checkAll.value = true
-            } else {
-              checkAll.value = false
-            }
-          })
-        }
-      } else {
-        TransportData.data = []
-        checkAll.value = false
-      }
-    }
-    emit('defaultTransport', TransportData, searchTableQeury.value)
-  }
+const emit = defineEmits(['transportSearch'])
+
+const transportSearch = () => {
+  const allChecked = transportList.value.every((item) => item.checked)
+  const selectedNames = allChecked
+    ? ['All']
+    : transportList.value.filter((item) => item.checked).map((item) => item.sname)
+  filtersStore.updateFilter({
+    title: 'Transport Mode',
+    value: selectedNames,
+    keyType: 'array',
+    key: 'transport_mode'
+  })
+  dropdownVisible.value.handleClose()
+  emit('transportSearch', transportList.value)
 }
 
 // 每次打开时都应该重新赋值
 const handleDropdownVisibleChange = (visible: boolean) => {
   if (visible) {
-    updateTransportList(props.TransportListItem)
+    updateTransportList(props.transportListItem)
   }
 }
 </script>
@@ -242,16 +116,16 @@ const handleDropdownVisibleChange = (visible: boolean) => {
                   </span>
                   Select All
                 </div>
-                <div class="checkbox_number">({{ TotalAll }})</div>
+                <div class="checkbox_number">({{ totalNumber }})</div>
               </el-checkbox>
             </el-dropdown-item>
             <el-divider></el-divider>
-            <el-dropdown-item v-for="(item, index) in TransportList" :key="index">
+            <el-dropdown-item v-for="(item, index) in transportList" :key="index">
               <el-checkbox
                 :value="item.name"
                 v-model="item.checked"
                 class="checkbox"
-                @change="handleCheckedTransportChange(item.sname, item.checked)"
+                @change="handleCheckedTransportChange(item)"
               >
                 <div class="checkbox_title">
                   <span class="iconfont_icon">
@@ -269,7 +143,7 @@ const handleDropdownVisibleChange = (visible: boolean) => {
                 <el-button class="clear" type="default" @click="clearList">Reset</el-button>
               </div>
               <div>
-                <el-button class="search el-button--dark" @click="TransportSearch"
+                <el-button class="search el-button--dark" @click="transportSearch"
                   >Search</el-button
                 >
               </div>

+ 175 - 194
src/components/selectAutoSelect/src/selectAutoSelect.vue

@@ -1,251 +1,232 @@
 <script setup lang="ts">
-import { ref, watch } from 'vue'
+import { ref, watch, onUnmounted } from 'vue'
 import IconDropDown from '@/components/IconDropDown'
-interface TypeItem {
-  partyType: ''
-  partyname: []
-}
-interface ListItem {
-  value: string
-  label: string
-}
-interface dateoptions {
-  value: string
-  label: string
-}
+import { cloneDeep, debounce } from 'lodash'
+import { useFiltersStore } from '@/stores/modules/filtersList'
+import { useRoute } from 'vue-router'
+import axios from 'axios'
+
+const route = useRoute()
+const searchMode = route.path.includes('booking') ? 'booking' : 'tracking'
+const filtersStore = useFiltersStore()
+
 interface Props {
-  AddDateType: TypeItem[]
-  ASPlaceholder: string
-  DateTypeoptions: dateoptions[]
-  selectedPartyTypeoptions: Array<string>
-  ASSearchMode: String
-  ASSearchObj: Object
-}
-interface optionsItem {
-  value: string
-  label: string
-  checked: boolean
+  placeholder: string
+  typeOptions: Array<any>
+  data: Array<any>
 }
 
-const list = ref<ListItem[]>([])
-const options = ref<ListItem[]>([])
-const loading = ref(false)
 const props = withDefaults(defineProps<Props>(), {})
-const AddType = ref(props.AddDateType)
-watch(
-  () => props.AddDateType,
-  (val) => {
-    AddType.value = val
-  },
-  {
-    immediate: true,
-    deep: true
-  }
+
+// 每个 item 的独立状态:loading、options、abortController
+const itemStates = ref(
+  new Map<
+    string,
+    {
+      loading: boolean
+      options: Array<{ value: string; label: string; checked: boolean }>
+      controller: AbortController | null
+    }
+  >()
 )
-// const ErrorNumber = ref(0)
-const dataTypeoptions = ref<optionsItem[]>([])
-const typeSelectIndex = ref(-1)
+
+const pageData = ref(cloneDeep(props.data))
+
 watch(
-  () => props.selectedPartyTypeoptions,
-  (newV) => {
-    let arr: any = []
-    if (newV.length == 0) {
-      arr = props.DateTypeoptions
-    } else {
-      props.DateTypeoptions.forEach((item: any) => {
-        let index = newV.findIndex((con: any) => {
-          return con == item.value
+  () => props.data,
+  (newVal) => {
+    pageData.value = cloneDeep(newVal)
+    // 初始化每个 item 的状态(如果不存在)
+    newVal.forEach((item) => {
+      if (!itemStates.value.has(item.title)) {
+        itemStates.value.set(item.title, {
+          loading: false,
+          options: [],
+          controller: null
         })
-        if (index < 0) {
-          arr.push(item)
-        }
-      })
-    }
-    dataTypeoptions.value = arr
+      }
+    })
   },
-  {
-    immediate: true,
-    deep: true
-  }
+  { deep: true, immediate: true }
 )
-const ErrorNumber = ref(false)
-const str = ref()
-const search_field = ref()
-const InputAutoSelect = (val: any) => {
-  search_field.value = val
-  if (val.includes('Agent')) {
-    str.value = 'apex'
-  } else {
-    str.value = 'sales'
-  }
-}
-const remoteMethod = (query: string) => {
-  if (query) {
-    loading.value = true
-    setTimeout(() => {
-      $api
-        .getMoreFiltersData({
-          term: query,
-          type: str.value,
-          search_field: search_field.value,
-          search_mode: props.ASSearchMode,
-          ...props.ASSearchObj
-        })
-        .then((res: any) => {
-          if (res.code == 200) {
-            loading.value = false
-            list.value = res.data.map((item: any) => {
-              return { value: item, label: item, checked: testAuto.value?.includes(item) }
-            })
-            options.value = list.value.filter((item) => {
-              return item.label.toLowerCase().includes(query.toLowerCase())
-            })
-          }
-        })
-    }, 200)
-  } else {
-    options.value = []
-  }
+
+const originnalTypeOptions = ref(props.typeOptions)
+
+// 计算排除其他已选项后的可用选项
+const getAvailableOptions = (curTitle: string) => {
+  const otherTypeOptions = pageData.value
+    .filter((type) => type.title !== curTitle)
+    .map((type) => type.title)
+  return originnalTypeOptions.value.filter((type) => !otherTypeOptions.includes(type.title))
 }
-// 删除当前more type
-const deleteType = (i: any) => {
-  emit('delSelect', i, AddType.value[i].partyType)
-  AddType.value.splice(i, 1)
+
+const emit = defineEmits(['deleteItem', 'changeTitle', 'changeValue'])
+
+const changeTitle = (title: string, item: any) => {
+  emit('changeTitle', item, title)
 }
-// 选中type改变
-const changeSelect = (val: any) => {
-  emit('changeAutoSelectAddType', typeSelectIndex.value, val)
+
+const changeValue = (value: string[], item: any) => {
+  emit('changeValue', item, value)
 }
-// 选中name改变
-const emit = defineEmits(['changeAutoSelectAddType', 'delSelect', 'changeAutoSelect'])
-let AutoSelectObj: any = {}
-let AutoSelectObj2: any = {}
-const testAuto = ref()
-const changeAutoSelect = (val: any, value: any) => {
-  testAuto.value = value
-  AutoSelectObj[val] = value.join()
-  AutoSelectObj2[val] = value
-  if (value.length) {
-    ErrorNumber.value = true
-  } else {
-    ErrorNumber.value = false
+
+const deleteItem = (title: string) => {
+  // 清理该 item 的状态和 abort pending request
+  const state = itemStates.value.get(title)
+  if (state?.controller) {
+    state.controller.abort()
   }
-  emit('changeAutoSelect', AutoSelectObj, AutoSelectObj2, ErrorNumber.value)
-}
-const typeSelectFocus = (index: any, e: any) => {
-  typeSelectIndex.value = index
+  itemStates.value.delete(title)
+  emit('deleteItem', title)
 }
-const typeSelectBlur = () => {
-  typeSelectIndex.value = -1
-  let arr: any = []
-  if (props.selectedPartyTypeoptions.length == 0) {
-    arr = props.DateTypeoptions
-  } else {
-    props.DateTypeoptions.forEach((item: any) => {
-      let index = props.selectedPartyTypeoptions.findIndex((con: any) => {
-        return con == item.value
-      })
-      if (index < 0) {
-        arr.push(item)
-      }
-    })
+
+const visibleChange = (val: boolean, title: string) => {
+  if (!val) {
+    const state = itemStates.value.get(title)
+    if (state) {
+      state.options = []
+    }
   }
-  dataTypeoptions.value = arr
 }
-const typeSelectClick = (index: any, val: any) => {
-  if (AddType.value[index].partyType) {
-    let j = props.DateTypeoptions.findIndex((item: any) => {
-      return item.value == AddType.value[index].partyType
-    })
-    let i = dataTypeoptions.value.findIndex((item: any) => {
-      return item.value == AddType.value[index].partyType
-    })
-    if (i < 0) {
-      dataTypeoptions.value.push(props.DateTypeoptions[j])
+
+// 创建带防抖 + 竞态处理的远程搜索函数
+const createQueryHandler = (title: string, curValue: string[]) => {
+  const debouncedSearch = debounce((query: string) => {
+    const state = itemStates.value.get(title)
+    if (!state) return
+
+    // 取消上一次请求
+    if (state.controller) {
+      state.controller.abort()
     }
-  } else {
-    let arr: any = []
-    if (props.selectedPartyTypeoptions.length == 0) {
-      arr = props.DateTypeoptions
-    } else {
-      props.DateTypeoptions.forEach((item: any) => {
-        let index = props.selectedPartyTypeoptions.findIndex((con: any) => {
-          return con == item.value
-        })
-        if (index < 0) {
-          arr.push(item)
+
+    if (!query.trim()) {
+      state.options = []
+      state.loading = false
+      return
+    }
+
+    const newController = new AbortController()
+    state.controller = newController
+    state.loading = true
+
+    const curType = originnalTypeOptions.value.find((type) => type.title === title)
+    if (!curType) {
+      state.loading = false
+      return
+    }
+
+    const queryData = filtersStore.getQueryData
+    $api
+      .getMoreFiltersData(
+        {
+          term: query,
+          type: curType.type,
+          search_field: title,
+          search_mode: searchMode,
+          ...queryData
+        },
+        { signal: newController.signal }
+      )
+      .then((res: any) => {
+        if (!newController.signal.aborted && res?.code === 200) {
+          state.options = (res.data || []).map((item: string) => ({
+            value: item,
+            label: item,
+            checked: curValue?.includes(item)
+          }))
         }
       })
-    }
-    dataTypeoptions.value = arr
-  }
+      .catch((err) => {
+        if (!axios.isCancel(err) && !newController.signal.aborted) {
+          // ElMessage.error('Failed to load options')
+        }
+        if (itemStates.value.has(title)) {
+          itemStates.value.get(title)!.options = []
+        }
+      })
+      .finally(() => {
+        // 仅当这是最新 controller 时才关闭 loading
+        if (
+          itemStates.value.has(title) &&
+          itemStates.value.get(title)?.controller === newController
+        ) {
+          state.loading = false
+        }
+      })
+  }, 200)
+
+  return debouncedSearch
 }
+
+// 组件卸载时取消所有 pending 请求
+onUnmounted(() => {
+  itemStates.value.forEach((state) => {
+    if (state.controller) {
+      state.controller.abort()
+    }
+  })
+})
 </script>
+
 <template>
-  <div class="AddType" v-for="(item, index) in AddType" :key="index">
+  <div class="addType" v-for="item in pageData" :key="item.id">
     <div>
       <div class="Date_Title">
         <div class="ETD_title">Party Type</div>
-        <span class="iconfont_icon" @click="deleteType(index)">
+        <span class="iconfont_icon" @click="deleteItem(item.title)">
           <svg class="iconfont icon_delete" aria-hidden="true">
             <use xlink:href="#icon-icon_delete_b"></use>
           </svg>
         </span>
       </div>
       <el-select
-        v-model="AddType[index].partyType"
+        :model-value="item.title"
         :suffix-icon="IconDropDown"
-        @blur="typeSelectBlur"
-        @focus="typeSelectFocus(index, $event)"
-        @click="typeSelectClick(index, $event)"
-        @change="changeSelect(AddType[index].partyType)"
+        @change="changeTitle($event, item)"
         placeholder="Please Select Party Type"
       >
         <el-option
-          v-for="item in dataTypeoptions"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value"
-        >
-        </el-option>
+          v-for="type in getAvailableOptions(item.title)"
+          :key="type.title"
+          :label="type.title"
+          :value="type.title"
+        />
       </el-select>
     </div>
     <div style="margin-top: 16px">
       <div class="ETD_title">Party Details</div>
       <el-select
-        v-model="AddType[index].partyname"
+        :model-value="item.value"
         multiple
         filterable
         remote
-        :class="{
-          is_error: AddType[index].partyType != '' && AddType[index].partyname.length == 0
-        }"
+        :class="{ is_error: item.value.length > 0 && item.title }"
         reserve-keyword
-        :placeholder="props.ASPlaceholder"
+        :placeholder="props.placeholder"
         collapse-tags
-        @input="InputAutoSelect(AddType[index].partyType)"
-        @change="changeAutoSelect(AddType[index].partyType, AddType[index].partyname)"
-        :disabled="AddType[index].partyType ? false : true"
+        :disabled="!item.title"
         collapse-tags-tooltip
         :max-collapse-tags="3"
-        :remote-method="remoteMethod"
-        :loading="loading"
+        :remote-method="createQueryHandler(item.title, item.value)"
+        :loading="itemStates.get(item.title)?.loading ?? false"
+        @change="changeValue($event, item)"
+        @visible-change="(val) => visibleChange(val, item.title)"
       >
         <el-option
-          v-for="item in options"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value"
+          v-for="infoItem in itemStates.get(item.title)?.options || []"
+          :key="infoItem.label"
+          :label="infoItem.label"
+          :value="infoItem.label"
         >
-          <el-checkbox :checked="item.checked">
-            <span class="label" @click="item.checked = !item.checked">{{ item.value }}</span>
+          <el-checkbox :checked="infoItem.checked" style="flex: 1">
+            <span class="label" @click="infoItem.checked = !infoItem.checked">{{
+              infoItem.label
+            }}</span>
           </el-checkbox>
         </el-option>
       </el-select>
-      <div
-        class="error"
-        v-if="AddType[index].partyType != '' && AddType[index].partyname.length == 0"
-      >
+      <div class="error" v-if="item.value.length === 0 && item.title">
         Please Input Party Details
       </div>
     </div>
@@ -253,7 +234,7 @@ const typeSelectClick = (index: any, val: any) => {
 </template>
 
 <style lang="scss" scoped>
-.AddType {
+.addType {
   background-color: var(--addparties-background-color);
   margin: 0 16px 8px 16px;
   padding: 8px;

+ 3 - 0
src/router/index.ts

@@ -1,6 +1,7 @@
 import { createRouter, createWebHistory } from 'vue-router'
 import { useUserStore } from '@/stores/modules/user'
 import { useBreadCrumb } from '@/stores/modules/breadCrumb'
+import { useHeaderSearch } from '@/stores/modules/headerSearch'
 
 const router = createRouter({
   history: createWebHistory(`${import.meta.env.VITE_BASE_URL}`),
@@ -233,10 +234,12 @@ const router = createRouter({
 router.beforeEach(async (to, from, next) => {
   useBreadCrumb().setRouteList(to)
   const userStore = useUserStore()
+  const headerSearchStore = useHeaderSearch()
   // 如果手动跳转登录页,清除登录信息
   if (to.path === '/login') {
     if (userStore.isLogin) {
       await userStore.logout()
+      headerSearchStore.clearSearchData()
     }
     sessionStorage.removeItem('trackingTablePageInfo')
     sessionStorage.removeItem('bookingTablePageInfo')

+ 115 - 0
src/stores/modules/filtersList.ts

@@ -0,0 +1,115 @@
+import { defineStore } from 'pinia'
+import dayjs from 'dayjs'
+import { useUserStore } from '@/stores/modules/user'
+
+const valueFormat = 'MM/DD/YYYY'
+
+interface StateType {
+  filtersList: FiltersType[]
+}
+
+export type FiltersType =
+  | {
+    title: string
+    key: string
+    keyType: 'normal'
+    value: string
+    isHide?: boolean
+  }
+  | {
+    title: string
+    key: string
+    keyType: 'array'
+    value: string[]
+    isHide?: boolean
+  } | {
+    title: string
+    key: string[]
+    keyType: 'dateRange'
+    value: string[]
+    isHide?: boolean
+  }
+
+export const useFiltersStore = defineStore('filtersStore', {
+  state: (): StateType => ({ filtersList: [] }),
+
+  getters: {
+    getQueryData(state) {
+      const userStore = useUserStore()
+      const formatDate = userStore.dateFormat
+      return state.filtersList.reduce((acc, cur) => {
+        if (cur.keyType === 'normal') {
+          acc[cur.key] = cur.value
+        } else if (cur.keyType === 'array') {
+          acc[cur.key] = cur.value
+        } else if (cur.keyType === 'dateRange') {
+          acc[cur.key[0]] = cur.isHide ? cur.value[0] : dayjs(cur.value[0], formatDate).format(valueFormat)
+          acc[cur.key[1]] = cur.isHide ? cur.value[1] : dayjs(cur.value[1], formatDate).format(valueFormat)
+        }
+        return acc
+      }, {})
+    },
+    getTagsList(state) {
+      return state.filtersList
+        .filter(item => !item.isHide)
+        .map(filter => {
+          let displayValue: string
+          if (filter.keyType === 'dateRange') {
+            displayValue = `${filter.value[0]} To ${filter.value[1]}`
+          } else if (filter.keyType === 'array') {
+            displayValue = filter.value.join(', ')
+          } else {
+            displayValue = String(filter.value) // 防止 value 是 number/null
+          }
+          return {
+            title: filter.title,
+            value: `${filter.title}: ${displayValue}`
+          }
+        })
+    }
+  },
+  actions: {
+    getFilterIndexByKey(key: string) {
+      return this.filtersList.findIndex(filter => {
+        return filter.key === key
+      })
+    },
+    getFilterIndexByTitle(title: string) {
+      return this.filtersList.findIndex(filter => {
+        return filter.title === title
+      })
+    },
+    getFilterByKey(key: string) {
+      return this.filtersList.find(filter => filter.key === key)
+    },
+    getFilterByTitle(title: string) {
+      return this.filtersList.find(filter => filter.title === title)
+    },
+    deleteFilterByKey(key: string) {
+      const index = this.getFilterIndexByKey(key)
+      if (index !== -1) {
+        this.filtersList.splice(index, 1)
+      }
+    },
+    deleteFilterByTitle(title: string) {
+      const index = this.getFilterIndexByTitle(title)
+      if (index !== -1) {
+        this.filtersList.splice(index, 1)
+      }
+    },
+    updateFilter(filter: FiltersType) {
+      const index = this.getFilterIndexByTitle(filter.title)
+      if (index !== -1) {
+        this.filtersList[index] = filter
+      } else {
+        this.filtersList.push(filter)
+      }
+    },
+    clearFilters() {
+      this.filtersList = []
+    }
+  },
+  persist: {
+    storage: sessionStorage // 👈 使用 sessionStorage
+  }
+})

+ 0 - 18
src/stores/modules/loadingState.ts

@@ -1,18 +0,0 @@
-import { defineStore } from 'pinia'
-
-interface LoadingState {
-  trackingTableLoading: boolean
-}
-
-export const useLoadingState = defineStore('loadingState', {
-  state: (): LoadingState => ({
-    trackingTableLoading: JSON.parse(localStorage.getItem('trackingTableLoading')) || false
-  }),
-  getters: {},
-  actions: {
-    setTrackingTableLoading(state: boolean) {
-      localStorage.setItem('trackingTableLoading', JSON.stringify(state))
-      this.trackingTableLoading = state
-    }
-  }
-})

+ 8 - 4
src/styles/icons/iconfont.css

@@ -1,9 +1,9 @@
 @font-face {
   font-family: "font_family"; /* Project id 4672385 */
-  src: url('iconfont.woff2?t=1767838502999') format('woff2'),
-       url('iconfont.woff?t=1767838502999') format('woff'),
-       url('iconfont.ttf?t=1767838502999') format('truetype'),
-       url('iconfont.svg?t=1767838502999#font_family') format('svg');
+  src: url('iconfont.woff2?t=1769408857052') format('woff2'),
+       url('iconfont.woff?t=1769408857052') format('woff'),
+       url('iconfont.ttf?t=1769408857052') format('truetype'),
+       url('iconfont.svg?t=1769408857052#font_family') format('svg');
 }
 
 .font_family {
@@ -14,6 +14,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-icon_search__user_b:before {
+  content: "\e752";
+}
+
 .icon-icon_convert_b:before {
   content: "\e751";
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
src/styles/icons/iconfont.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 2 - 0
src/styles/icons/iconfont.svg


BIN
src/styles/icons/iconfont.ttf


BIN
src/styles/icons/iconfont.woff


BIN
src/styles/icons/iconfont.woff2


+ 4 - 4
src/utils/tools.ts

@@ -8,16 +8,16 @@ const formatString = computed(() => {
   return userStore.dateFormat || 'MM/DD/YYYY'
 })
 
-export const formatTimezone = (time: string, timezone?: string, is12HourClock?: boolean) => {
+export const formatTimezone = (time: string, timezone?: string, is12HourClock?: boolean, originFormat?: string) => {
   if (!time) return '--'
   let formattedTime = ''
   if (time.length > 12) {
     if (is12HourClock) {
       // 如果是12小时制,使用12小时制格式化
-      formattedTime = moment(time).format(`${formatString.value} hh:mm A`)
+      formattedTime = moment(time, originFormat).format(`${formatString.value} hh:mm A`)
     } else {
       // 如果是24小时制,使用24小时制格式化
-      formattedTime = moment(time).format(`${formatString.value} HH:mm`)
+      formattedTime = moment(time, originFormat).format(`${formatString.value} HH:mm`)
     }
     if (!timezone) {
       return formattedTime
@@ -29,7 +29,7 @@ export const formatTimezone = (time: string, timezone?: string, is12HourClock?:
     utcOffset = `(UTC${timeZoneOffset.slice(0, 3)})`
     return `${formattedTime} ${utcOffset}`
   } else {
-    formattedTime = moment(time).format(formatString.value)
+    formattedTime = moment(time, originFormat).format(formatString.value)
     return formattedTime
   }
 }

+ 127 - 393
src/views/Booking/src/BookingView.vue

@@ -1,5 +1,4 @@
 <script setup lang="ts">
-import emitter from '@/utils/bus'
 import FilterTags from '@/components/FliterTags'
 import TransportMode from '@/components/TransportMode'
 import BookingTable from './components/BookingTable'
@@ -9,362 +8,93 @@ import { ref, reactive } from 'vue'
 import { useCalculatingHeight } from '@/hooks/calculatingHeight'
 import { useUserStore } from '@/stores/modules/user'
 import dayjs from 'dayjs'
+import { useFiltersStore } from '@/stores/modules/filtersList'
 
 const userStore = useUserStore()
 const formatDate = userStore.dateFormat
-const valueFormatDate = 'MM/DD/YYYY'
+const filtersStore = useFiltersStore()
+const filtersList = computed(() => filtersStore.filtersList)
 
 const filterRef: Ref<HTMLElement | null> = ref(null)
-
 const containerHeight = useCalculatingHeight(document.documentElement, 246, [filterRef])
 
-const BookingSearch = ref()
-const tableLoadingTableData = ref(false)
-let searchTableQeury: any = {}
-const filterData = reactive({
-  filtersTagData: [] as Array<string>,
-  transportData: [] as Array<string>,
-  daterangeData: [] as Array<string>,
-  morefiltersData: [] as Array<string>
-})
+const tabList = ref([
+  {
+    name: 'All',
+    number: 0,
+    checked: true,
+    type: 'all'
+  },
+  {
+    name: 'Created',
+    number: 0,
+    checked: false,
+    type: 'created'
+  },
+  {
+    name: 'Confirmed',
+    number: 0,
+    checked: false,
+    type: 'confirmed'
+  },
+  {
+    name: 'Cancelled',
+    number: 0,
+    checked: false,
+    type: 'cancelled'
+  }
+])
 
-const tagsData: any = ref([])
-const handleClose = (tag: any) => {
-  emitter.emit('clearTag', tag)
-  tagsData.value.splice(tagsData.value.indexOf(tag), 1)
-  if (tag.includes('Transport')) {
-    delete searchTableQeury.transport_mode
-  } else if (tag.includes('ETD')) {
-    filterData.daterangeData.forEach((item: any, index: any) => {
-      if (item.includes('ETD')) {
-        filterData.daterangeData.splice(index, 1)
-      }
+const initPage = () => {
+  if (!filtersList.value || (filtersList.value && filtersList.value.length == 0)) {
+    filtersStore.updateFilter({
+      title: 'Transport Mode',
+      value: ['All'],
+      keyType: 'array',
+      key: 'transport_mode'
     })
-    delete searchTableQeury.f_etd_start
-    delete searchTableQeury.f_etd_end
-  } else if (tag.includes('ETA')) {
-    filterData.daterangeData.forEach((item: any, index: any) => {
-      if (item.includes('ETA')) {
-        filterData.daterangeData.splice(index, 1)
-      }
+    filtersStore.updateFilter({
+      title: 'ETD',
+      value: [
+        dayjs().subtract(2, 'month').startOf('month').format(formatDate),
+        dayjs().add(1, 'month').format(formatDate)
+      ],
+      keyType: 'dateRange',
+      key: ['f_etd_start', 'f_etd_end']
     })
-    delete searchTableQeury.m_eta_start
-    delete searchTableQeury.m_eta_end
-  } else if (tag.includes('Creation')) {
-    filterData.daterangeData.forEach((item: any, index: any) => {
-      if (item.includes('Creation')) {
-        filterData.daterangeData.splice(index, 1)
-      }
+    filtersStore.updateFilter({
+      title: 'Shipment Status',
+      value: ['All'],
+      keyType: 'array',
+      key: 'filterTag'
     })
-    delete searchTableQeury.created_time_start
-    delete searchTableQeury.created_time_end
-  } else if (tag.includes('Shippername')) {
-    delete searchTableQeury.shipper
-  } else if (tag.includes('Consigneename')) {
-    delete searchTableQeury.consignee
-  } else if (tag.includes('Origin Agent')) {
-    delete searchTableQeury.origin
-  } else if (tag.includes('Destination Agent')) {
-    delete searchTableQeury.agent
-  } else if (tag.includes('Sales')) {
-    delete searchTableQeury.sales_rep
-  } else if (tag.includes('Origin')) {
-    delete searchTableQeury.shipper_city
-  } else if (tag.includes('Destination')) {
-    delete searchTableQeury.consignee_city
-  } else if (tag.includes('Place of Receipt')) {
-    delete searchTableQeury['place_of_receipt/place_of_receipt_exp']
-  } else if (tag.includes('Port of Loading')) {
-    delete searchTableQeury['fport_of_loading_uncode/fport_of_loading_exp']
-  } else if (tag.includes('Place of delivery')) {
-    delete searchTableQeury['place_of_delivery/place_of_delivery_exp']
-  } else if (tag.includes('Vessel')) {
-    delete searchTableQeury['f_vessel/m_vessel']
-  } else if (tag.includes('Voyage')) {
-    delete searchTableQeury['f_voyage/m_voyage']
   }
-  sessionStorage.setItem('searchTableQeury', JSON.stringify(searchTableQeury))
-  getbookingdata()
 }
-// 筛选框查询
-const FiltersSeach = (val: any, value: any) => {
-  searchTableQeury[val] = value
-  sessionStorage.setItem('searchTableQeury', JSON.stringify(searchTableQeury))
-  getbookingdata()
-}
-//TransportSearch
-const TransportSearch = (val: any) => {
-  filterData.transportData = []
-  if (val.data.length != 0) {
-    let str = `${val.title}:${val.data}`
-    filterData.transportData.push(str)
-  }
-  FiltersSeach('transport_mode', val.data)
-  renderTagsData()
-}
-// defaultTransport
-const defaultTransport = (val: any, value: any) => {
-  filterData.transportData = []
-  if (val.data.length != 0) {
-    let str = `${val.title}:${val.data}`
-    filterData.transportData.push(str)
-  }
-  if (sessionStorage.getItem('searchTableQeury') == null) {
-    searchTableQeury.transport_mode = val.data
-  } else {
-    searchTableQeury = value
-  }
-  renderTagsData()
-}
-// defaultDate
-const defaultDate = (dateRangeData: any, data: any) => {
-  setFilterData(dateRangeData)
 
-  if (sessionStorage.getItem('searchTableQeury') == null) {
-    for (const key in dateRangeData) {
-      searchTableQeury.f_etd_start = dateRangeData[key].data[0]
-      searchTableQeury.f_etd_end = dateRangeData[key].data[1]
-    }
-  } else {
-    searchTableQeury = data
-    if (searchTableQeury._textSearch) {
-      BookingSearch.value = searchTableQeury._textSearch
-    }
-  }
-  getbookingdata()
-  renderTagsData()
-}
+initPage()
 
-const setFilterData = (dateRangeData: any) => {
-  filterData.daterangeData = []
-  for (const key in dateRangeData) {
-    const startEnd = dateRangeData[key].data[0] + ' To ' + dateRangeData[key].data[1]
-    let str = `${key}:${startEnd}`
-    filterData.daterangeData.push(str)
-  }
-}
-//DateRangeSearch
-const DateRangeSearch = (dateRangeData: any) => {
-  setFilterData(dateRangeData)
+const textSearch = ref()
+const tableLoadingTableData = ref(false)
 
-  if (Object.keys(dateRangeData).length == 0) {
-    delete searchTableQeury.f_etd_start
-    delete searchTableQeury.f_etd_end
-    delete searchTableQeury.m_eta_start
-    delete searchTableQeury.m_eta_end
-    delete searchTableQeury.created_time_start
-    delete searchTableQeury.created_time_end
-  }
-  const fieldList = [
-    {
-      title: 'ETD',
-      keys: ['f_etd_start', 'f_etd_end']
-    },
-    { title: 'ETA', keys: ['m_eta_start', 'm_eta_end'] },
-    { title: 'Creation Time', keys: ['created_time_start', 'created_time_end'] }
-  ]
-  fieldList.forEach((item) => {
-    if (!dateRangeData.hasOwnProperty(item.title)) {
-      // 删除不存在的字段
-      searchTableQeury[item.keys[0]] = undefined
-      searchTableQeury[item.keys[1]] = undefined
-    }
-  })
-  for (const key in dateRangeData) {
-    if (key == 'ETD') {
-      searchTableQeury.f_etd_start = dateRangeData[key].data[0]
-      searchTableQeury.f_etd_end = dateRangeData[key].data[1]
-    } else if (key == 'ETA') {
-      searchTableQeury.m_eta_start = dateRangeData[key].data[0]
-      searchTableQeury.m_eta_end = dateRangeData[key].data[1]
-    } else {
-      searchTableQeury.created_time_start = dateRangeData[key].data[0]
-      searchTableQeury.created_time_end = dateRangeData[key].data[1]
-    }
-  }
-  sessionStorage.setItem('searchTableQeury', JSON.stringify(searchTableQeury))
-  getbookingdata()
-  renderTagsData()
-}
-//MoreFiltersSearch
-const MoreFiltersSearch = (val: any, value: any) => {
-  filterData.morefiltersData = []
-  if (Object.keys(value).length == 0) {
-    delete searchTableQeury.shipper
-    delete searchTableQeury.consignee
-    delete searchTableQeury.origin
-    delete searchTableQeury.agent
-    delete searchTableQeury.sales_rep
-    delete searchTableQeury.shipper_city
-    delete searchTableQeury.consignee_city
-    delete searchTableQeury['place_of_receipt/place_of_receipt_exp']
-    delete searchTableQeury['fport_of_loading_uncode/fport_of_loading_exp']
-    delete searchTableQeury['place_of_delivery/place_of_delivery_exp']
-    delete searchTableQeury['f_vessel/m_vessel']
-    delete searchTableQeury['f_voyage/m_voyage']
-  }
-  for (const key in val) {
-    let str = `${key}:${val[key]}`
-    filterData.morefiltersData.push(str)
-    if (key == 'Shippername') {
-      searchTableQeury.shipper = value[key]
-    } else if (key == 'Consigneename') {
-      searchTableQeury.consignee = value[key]
-    } else if (key == 'Origin Agent') {
-      searchTableQeury.origin = value[key]
-    } else if (key == 'Destination Agent') {
-      searchTableQeury.agent = value[key]
-    } else if (key == 'Sales') {
-      searchTableQeury.sales_rep = value[key]
-    } else if (key == 'Origin') {
-      searchTableQeury.shipper_city = value[key]
-    } else if (key == 'Destination') {
-      searchTableQeury.consignee_city = value[key]
-    } else if (key == 'Place of Receipt') {
-      searchTableQeury['place_of_receipt/place_of_receipt_exp'] = value[key]
-    } else if (key == 'Port of Loading') {
-      searchTableQeury['fport_of_loading_uncode/fport_of_loading_exp'] = value[key]
-    } else if (key == 'Place of delivery') {
-      searchTableQeury['place_of_delivery/place_of_delivery_exp'] = value[key]
-    } else if (key == 'Vessel') {
-      searchTableQeury['f_vessel/m_vessel'] = value[key]
-    } else if (key == 'Voyage') {
-      searchTableQeury['f_voyage/m_voyage'] = value[key]
-    }
-  }
-  sessionStorage.setItem('searchTableQeury', JSON.stringify(searchTableQeury))
-  getbookingdata()
-  renderTagsData()
-}
-const defaultMorefilters = (val: any, value: any, data: any) => {
-  filterData.morefiltersData = []
-  for (const key in val) {
-    let str = `${key}:${val[key]}`
-    filterData.morefiltersData.push(str)
-    if (key == 'Shippername') {
-      searchTableQeury.shipper = value[key]
-    } else if (key == 'Consigneename') {
-      searchTableQeury.consignee = value[key]
-    } else if (key == 'Origin Agent') {
-      searchTableQeury.origin = value[key]
-    } else if (key == 'Destination Agent') {
-      searchTableQeury.agent = value[key]
-    } else if (key == 'Sales') {
-      searchTableQeury.sales_rep = value[key]
-    } else if (key == 'Origin') {
-      searchTableQeury.shipper_city = value[key]
-    } else if (key == 'Destination') {
-      searchTableQeury.consignee_city = value[key]
-    } else if (key == 'Place of Receipt') {
-      searchTableQeury['place_of_receipt/place_of_receipt_exp'] = value[key]
-    } else if (key == 'Port of Loading') {
-      searchTableQeury['fport_of_loading_uncode/fport_of_loading_exp'] = value[key]
-    } else if (key == 'Place of delivery') {
-      searchTableQeury['place_of_delivery/place_of_delivery_exp'] = value[key]
-    } else if (key == 'Vessel') {
-      searchTableQeury['f_vessel/m_vessel'] = value[key]
-    } else if (key == 'Voyage') {
-      searchTableQeury['f_voyage/m_voyage'] = value[key]
-    }
-  }
-  if (sessionStorage.getItem('searchTableQeury') != null) {
-    searchTableQeury = data
-  }
-  renderTagsData()
+const handleClose = (tagTitle: any) => {
+  filtersStore.deleteFilterByTitle(tagTitle)
+  getBookingData()
 }
+
 const clearfilters = () => {
-  BookingSearch.value = ''
-  filterData.filtersTagData = []
-  tagsData.value = []
-  let str = 'Shipment status: All'
-  filterData.filtersTagData.push(str)
-  filterData.transportData = []
-  filterData.daterangeData = []
-  filterData.morefiltersData = []
-  emitter.emit('clearTag', 'Shipment status')
-  emitter.emit('clearTag', 'Transport Mode')
-  emitter.emit('clearTag', 'ETD')
-  emitter.emit('clearTag', 'ETA')
-  emitter.emit('clearTag', 'Creation Time')
-  emitter.emit('clearTag', 'Shippername')
-  emitter.emit('clearTag', 'Consigneename')
-  emitter.emit('clearTag', 'Origin Agent')
-  emitter.emit('clearTag', 'Destination Agent')
-  emitter.emit('clearTag', 'Sales')
-  emitter.emit('clearTag', 'Origin')
-  emitter.emit('clearTag', 'Destination')
-  emitter.emit('clearTag', 'Place of Receipt')
-  emitter.emit('clearTag', 'Port of Loading')
-  emitter.emit('clearTag', 'Place of delivery')
-  emitter.emit('clearTag', 'Vessel')
-  emitter.emit('clearTag', 'Voyage')
-  searchTableQeury = {}
-  searchTableQeury.filterTag = ['All']
-  sessionStorage.setItem('searchTableQeury', JSON.stringify(searchTableQeury))
-  getbookingdata()
-  renderTagsData()
-}
-const renderTagsData = () => {
-  tagsData.value = []
-  if (filterData.transportData.length) {
-    tagsData.value.push(filterData.transportData[0])
-  }
-  if (filterData.daterangeData.length) {
-    filterData.daterangeData.forEach((item) => {
-      tagsData.value.push(item)
-    })
-  }
-  if (filterData.morefiltersData.length) {
-    filterData.morefiltersData.forEach((item) => {
-      tagsData.value.push(item)
-    })
-  }
-  if (filterData.filtersTagData.length) {
-    filterData.filtersTagData.forEach((item) => {
-      tagsData.value.push(item)
-    })
-  }
-}
-// 清除 Transport Tags
-const clearTransportTags = () => {
-  filterData.transportData = []
-}
-// 清除 Daterange Tags
-const clearDaterangeTags = () => {
-  filterData.daterangeData = []
-}
-// 清除 MoreFilters Tags
-const clearMoreFiltersTags = () => {
-  filterData.morefiltersData = []
+  filtersStore.clearFilters()
+  getBookingData()
 }
 
+const clearDaterangeTags = () => {}
+const clearMoreFiltersTags = () => {}
+
 const BookingTable_ref = ref()
-const TransportListItem = ref()
-interface ListItem {
-  name: string
-  number: number
-  type: string
-  checked: boolean
-}
-const TagsList = ref<ListItem[]>([])
-const filterTag = ref(['All'])
+const transportListItem = ref()
+
 const isShowAlertIcon = ref(false)
-const setSearchQeury = () => {}
-const getbookingdata = () => {
-  const dateRangeKeys = [
-    'f_etd_start',
-    'f_etd_end',
-    'm_eta_start',
-    'm_eta_end',
-    'created_time_start',
-    'created_time_end'
-  ]
-  const curRangeData: any = {}
-  for (const key of dateRangeKeys) {
-    if (searchTableQeury[key] !== undefined) {
-      curRangeData[key] = dayjs(searchTableQeury[key], formatDate).format(valueFormatDate)
-    }
-  }
+const getBookingData = () => {
+  const queryData = filtersStore.getQueryData
 
   tableLoadingTableData.value = true
   BookingTable_ref.value.getLoadingData(tableLoadingTableData.value)
@@ -374,30 +104,28 @@ const getbookingdata = () => {
       ps: BookingTable_ref.value.pageInfo.pageSize,
       rc: -1,
       other_filed: '',
-      ...searchTableQeury,
-      ...curRangeData
+      _textSearch: textSearch.value,
+      ...queryData
     })
     .then((res: any) => {
       if (res.code === 200) {
-        TransportListItem.value = res.data.TransportList
-        TagsList.value = res.data.tagsList
-        let taglist: any = []
-        if (Object.keys(filterData.filtersTagData).length == 0) {
-          TagsList.value.forEach((item: any) => {
-            if (item.checked == true) {
-              taglist.push(item.name)
-            }
-          })
-          let str = 'Shipment status: ' + taglist.toString()
-          filterData.filtersTagData.push(str)
-          filterData.filtersTagData.forEach((item) => {
-            tagsData.value.push(item)
-          })
-        }
+        transportListItem.value = res.data.TransportList
+
+        tabList.value = res.data.tagsList
+        const checkedTabNames = tabList.value
+          .filter((item) => item.checked)
+          .map((item) => item.name)
+        filtersStore.updateFilter({
+          title: 'Shipment Status',
+          value: checkedTabNames,
+          keyType: 'array',
+          key: 'filterTag'
+        })
+
         sessionStorage.setItem('BookingData', JSON.stringify(res.data))
-        BookingTable_ref.value.searchTableData(searchTableQeury)
+        BookingTable_ref.value.searchTableData()
         // 查询没结果的话显示icon
-        if (BookingSearch.value != '' && BookingSearch.value != undefined) {
+        if (textSearch.value != '' && textSearch.value != undefined) {
           if (res.data.searchData.length == 0) {
             isShowAlertIcon.value = true
           }
@@ -407,33 +135,47 @@ const getbookingdata = () => {
       }
     })
 }
-const changeTag = (val: any, boolean: any) => {
-  filterData.filtersTagData = []
-  searchTableQeury.filterTag = val
-  let str = 'Shipment status: ' + val
-  filterData.filtersTagData.push(str)
-  sessionStorage.setItem('searchTableQeury', JSON.stringify(searchTableQeury))
-  if (boolean) {
-    getbookingdata()
-  }
-  renderTagsData()
-  filterTag.value = val
+const tabChange = (changeTabList: any) => {
+  tabList.value = changeTabList
+  const checkedTabNames = tabList.value.filter((item) => item.checked).map((item) => item.name)
+  filtersStore.updateFilter({
+    title: 'Shipment Status',
+    value: checkedTabNames,
+    keyType: 'array',
+    key: 'filterTag'
+  })
+
+  getBookingData()
 }
 // 点击search按钮
 const SearchInput = () => {
-  searchTableQeury._textSearch = BookingSearch.value
-  sessionStorage.setItem('searchTableQeury', JSON.stringify(searchTableQeury))
-  getbookingdata()
+  getBookingData()
 }
 
+onMounted(() => {
+  getBookingData()
+})
 import BookingGuide from './components/BookingGuide.vue'
 import { useGuideStore } from '@/stores/modules/guide'
+import { onBeforeRouteLeave } from 'vue-router'
 
 const guideStore = useGuideStore()
 const bookingGuideRef = ref()
 const handleGuide = () => {
   bookingGuideRef.value.startGuide() // 开始引导
 }
+
+onBeforeRouteLeave((route: any) => {
+  // guideStore.clearGuide()
+  const whiteList = ['Booking Detail', 'Tracking Detail']
+  if (!whiteList.includes(route?.name)) {
+    filtersStore.clearFilters()
+  }
+})
+
+const handleSearch = () => {
+  getBookingData()
+}
 </script>
 
 <template>
@@ -445,12 +187,12 @@ const handleGuide = () => {
   <div class="display" ref="filterRef">
     <div class="filter-box">
       <div class="filters-container" id="booking-filters-container-guide">
-        <FilterTags :TagsListItem="TagsList" @changeTag="changeTag"></FilterTags>
+        <FilterTags :tagsList="tabList" @tabChange="tabChange"></FilterTags>
         <div class="heaer_top">
           <div class="search">
             <el-input
               placeholder="Enter Booking/HBL/PO/Carrier Booking No. "
-              v-model="BookingSearch"
+              v-model="textSearch"
               class="log_input"
               @keyup.enter="SearchInput"
             >
@@ -480,27 +222,18 @@ const handleGuide = () => {
             </el-input>
           </div>
           <TransportMode
-            :isShipment="false"
-            :TransportListItem="TransportListItem"
-            @TransportSearch="TransportSearch"
-            @defaultTransport="defaultTransport"
-            @clearTransportTags="clearTransportTags"
+            :transportListItem="transportListItem"
+            @transportSearch="getBookingData()"
           ></TransportMode>
           <DateRange
-            :isShipment="false"
-            @DateRangeSearch="DateRangeSearch"
+            @DateRangeSearch="getBookingData()"
             @clearDaterangeTags="clearDaterangeTags"
-            @defaultDate="defaultDate"
           ></DateRange>
         </div>
       </div>
       <MoreFilters
-        :isShipment="false"
-        :pageMode="'booking'"
-        :searchTableQeury="searchTableQeury"
-        @MoreFiltersSearch="MoreFiltersSearch"
+        @handleSearch="handleSearch"
         @clearMoreFiltersTags="clearMoreFiltersTags"
-        @defaultMorefilters="defaultMorefilters"
         :isShowMoreFiltersGuidePhoto="guideStore.booking.isShowMoreFiltersGuidePhoto"
       ></MoreFilters>
       <el-button class="el-button--dark" style="margin-left: 8px" @click="SearchInput"
@@ -508,33 +241,34 @@ const handleGuide = () => {
       >
     </div>
     <!-- 筛选项 -->
-    <div class="filtersTag" v-if="tagsData.length" id="booking-filter-tag-guide">
+    <div class="filtersTag" v-if="filtersStore.getTagsList.length" id="booking-filter-tag-guide">
       <el-tag
-        :key="tag"
         class="tag"
-        v-for="tag in tagsData"
-        :closable="!tag.includes('Shipment')"
+        v-for="tag in filtersStore.getTagsList"
+        :key="tag.title"
+        :closable="!tag.title.includes('Shipment')"
         :disable-transitions="false"
-        @close="handleClose(tag)"
+        @close="handleClose(tag.title)"
         color="#EFEFF0"
       >
         <el-tooltip
           class="box-item"
           effect="dark"
-          :content="tag"
+          :content="tag.value"
           placement="top"
-          v-if="tag.length > 39"
+          :popper-style="{ maxWidth: '500px', whiteSpace: 'normal', wordWrap: 'break-word' }"
+          v-if="tag.value.length > 39"
         >
-          {{ tag.length > 39 ? tag.substr(0, 39) + '...' : tag }}
+          {{ tag.value.length > 39 ? tag.value.slice(0, 39) + '...' : tag.value }}
         </el-tooltip>
-        <div v-else>{{ tag }}</div>
+        <div v-else>{{ tag.value }}</div>
       </el-tag>
       <div class="text_button" @click="clearfilters">Clear Filters</div>
     </div>
   </div>
   <BookingTable
+    :textSearch="textSearch"
     :height="containerHeight"
-    :tagsData="tagsData"
     ref="BookingTable_ref"
   ></BookingTable>
 </template>

+ 13 - 9
src/views/Booking/src/components/BookingTable/src/BookingTable.vue

@@ -10,6 +10,10 @@ import { transportationMode } from '@/components/transportationMode'
 import { useThemeStore } from '@/stores/modules/theme'
 import { useVisitedRowState } from '@/stores/modules/visitedRow'
 import { formatTimezone, formatNumber } from '@/utils/tools'
+import { useFiltersStore } from '@/stores/modules/filtersList'
+
+const filtersStore = useFiltersStore()
+const filtersList = computed(() => filtersStore.filtersList)
 
 const visitedRowState = useVisitedRowState()
 const themeStore = useThemeStore()
@@ -19,9 +23,9 @@ const props = defineProps({
     type: Number,
     default: 440
   },
-  tagsData: {
-    type: Array,
-    default: () => []
+  textSearch: {
+    type: String,
+    default: ''
   }
 })
 
@@ -115,7 +119,6 @@ assignPageInfo()
 
 const curTableData = ref([])
 const tempSearch = ref()
-const filterdataobj = ref()
 // 获得表格数据后赋值
 const assignTableData = (data: any) => {
   bookingTable.value.data = data.searchData || []
@@ -135,6 +138,7 @@ const assignTableData = (data: any) => {
 // 获取表格数据
 const getTableData = async (isPageChange?: boolean) => {
   const rc = isPageChange ? pageInfo.value.total : -1
+  const queryData = filtersStore.getQueryData
   // 保存页长以及当前页码
   sessionStorage.setItem('bookingTablePageInfo', JSON.stringify(pageInfo.value))
   tableLoadingTable.value = true
@@ -144,7 +148,8 @@ const getTableData = async (isPageChange?: boolean) => {
       ps: pageInfo.value.pageSize,
       rc,
       other_filed: '',
-      ...filterdataobj.value
+      _textSearch: props.textSearch,
+      ...queryData
     })
     .then((res: any) => {
       if (res.code === 200) {
@@ -161,8 +166,7 @@ const getTableData = async (isPageChange?: boolean) => {
     })
 }
 // 查询列表数据
-const searchTableData = (data: any) => {
-  filterdataobj.value = data
+const searchTableData = () => {
   if (sessionStorage.getItem('BookingData') != null) {
     const data = JSON.parse(sessionStorage.getItem('BookingData') as string) || {}
     assignTableData(data)
@@ -175,6 +179,8 @@ const searchTableData = (data: any) => {
         sessionStorage.removeItem('BookingData')
       })
     }, 100)
+  } else {
+    getTableData()
   }
 }
 
@@ -299,7 +305,6 @@ const handleDownload = () => {
     }
   })
   downloadDialogRef.value.openDialog(
-    props.tagsData,
     curSelectedColumns,
     selectedNumber.value || pageInfo.value.total
   )
@@ -331,7 +336,6 @@ const getExportTableData = (status: number) => {
   $api
     .getAllBookingTableData({
       selected_fields: column,
-      excel_filter_condition: props.tagsData.join(','),
       tmp_search: tempSearch.value
     })
     .then((res: any) => {

+ 18 - 4
src/views/Booking/src/components/BookingTable/src/components/DownloadDialog.vue

@@ -1,8 +1,11 @@
 <script setup lang="ts">
+import { useFiltersStore } from '@/stores/modules/filtersList'
+
+const filtersStore = useFiltersStore()
+
 const dialogVisible = ref(false)
 
-const openDialog = (tagsData: string[], selectedColumns: string[], slectedDataNumber: number) => {
-  listData.value = tagsData
+const openDialog = (selectedColumns: string[], slectedDataNumber: number) => {
   selectedDataNumber.value = slectedDataNumber
   columns.value = selectedColumns
   dialogVisible.value = true
@@ -12,7 +15,6 @@ const isShowSelectColumn = ref(false)
 
 const downloadFilter = ref(1)
 const selectedDataNumber = ref(0)
-const listData = ref()
 
 const columns = ref()
 
@@ -44,7 +46,19 @@ defineExpose({
           </div>
         </div>
         <div class="data-filter">
-          <div class="filter-item" v-for="item in listData" :key="item">{{ item }}</div>
+          <div class="filter-item" v-for="item in filtersStore.getTagsList" :key="item.title">
+            <el-tooltip
+              class="box-item"
+              effect="dark"
+              :content="item.value"
+              placement="top"
+              :popper-style="{ maxWidth: '500px', whiteSpace: 'normal', wordWrap: 'break-word' }"
+              v-if="item.value.length > 39"
+            >
+              {{ item.value.length > 39 ? item.value.slice(0, 39) + '...' : item.value }}
+            </el-tooltip>
+            <div v-else>{{ item.value }}</div>
+          </div>
         </div>
         <div class="download-filter">
           <el-radio-group v-model="downloadFilter">

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 418 - 418
src/views/Dashboard/src/DashboardView.vue


+ 196 - 0
src/views/Dashboard/src/components/CustomerFilter.vue

@@ -0,0 +1,196 @@
+<script setup lang="ts">
+import { cloneDeep, debounce } from 'lodash'
+import axios from 'axios'
+
+const props = defineProps({
+  data: {
+    type: Object as () => {
+      customerCode: string[]
+      customerType: string[]
+    }
+  },
+  default: () => {
+    return {
+      customerCode: [],
+      customerType: []
+    }
+  }
+})
+
+const customerInfo = ref({
+  customerCode: [] as string[],
+  customerType: [] as string[]
+})
+
+watch(
+  () => props.data,
+  (newValue) => {
+    customerInfo.value = cloneDeep(newValue) || {
+      customerCode: [],
+      customerType: []
+    }
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+const emit = defineEmits(['changeCustomerData', 'handleCumstomerSearch'])
+const changeData = (val: string[], type: string) => {
+  // 同步选中状态
+  emit('changeCustomerData', val, type)
+}
+
+interface ListItem {
+  label: string
+  id: string
+}
+
+const options = ref<ListItem[]>([])
+const loading = ref(false)
+const currentController = ref<AbortController | null>(null)
+
+const handleVisibleChange = (visible) => {
+  !visible && (options.value = [])
+}
+const remoteMethod = (query: string) => {
+  currentController.value?.abort()
+
+  const newController = new AbortController()
+  currentController.value = newController
+  loading.value = true
+
+  $api
+    .getSpecificRolesPartyID({ term: query }, { signal: newController.signal })
+    .then((res) => {
+      if (!newController.signal.aborted && res.code === 200) {
+        options.value = (res.data || []).map((item) => ({
+          id: item.id,
+          label: item.label
+        }))
+      }
+    })
+    .catch((err) => {
+      options.value = []
+      if (!axios.isCancel(err) && !newController.signal.aborted) {
+        ElMessage.error('Failed to load options')
+      }
+    })
+    .finally(() => {
+      // 仅当这是最新请求时,才关闭 loading
+      if (currentController.value === newController) {
+        loading.value = false
+      }
+    })
+}
+
+// 防抖版本(可选)
+const debouncedRemoteMethod = debounce(remoteMethod, 200)
+
+onUnmounted(() => {
+  currentController.value?.abort()
+})
+
+const typeOptions = ref([
+  {
+    label: 'Shipper',
+    value: 'shipper_id'
+  },
+  {
+    label: 'Consignee',
+    value: 'consignee_id'
+  },
+  {
+    label: 'Controlling Customer',
+    value: 'customer_code'
+  },
+  {
+    label: 'Bill to',
+    value: 'billto_id'
+  },
+  {
+    label: 'Notify Party',
+    value: 'notify_party_id'
+  }
+])
+
+const handleSearch = () => {
+  emit('handleCumstomerSearch')
+}
+</script>
+
+<template>
+  <div class="customer-filter">
+    <el-select
+      :model-value="customerInfo.customerCode"
+      multiple
+      filterable
+      reserve-keyword
+      placeholder="Search by Customer code, Customer name"
+      :loading="loading"
+      style="width: 400px"
+      collapse-tags
+      :max-collapse-tags="2"
+      collapse-tags-tooltip
+      popper-class="part-id-select-popper"
+      :filter-method="debouncedRemoteMethod"
+      @change="changeData($event, 'customerCode')"
+      @visible-change="handleVisibleChange"
+    >
+      <el-option
+        v-for="item in options"
+        :key="item.id + item.label"
+        :label="item.label"
+        :value="item.label"
+      >
+        <div class="select-option">
+          <el-checkbox
+            :model-value="customerInfo.customerCode.includes(item.label)"
+            style="flex: 1"
+          >
+            <span style="display: inline-block; width: 220px">{{ item.label }}</span>
+            <span class="text-ellipsis" style="flex: 1; width: 200px">{{ item.id }}</span>
+          </el-checkbox>
+        </div>
+      </el-option>
+    </el-select>
+    <el-select
+      placeholder="Customer Type"
+      multiple
+      :max-collapse-tags="1"
+      collapse-tags
+      collapse-tags-tooltip
+      :model-value="customerInfo.customerType"
+      @change="changeData($event, 'customerType')"
+      style="width: 240px; margin-left: 8px"
+    >
+      <el-option
+        v-for="item in typeOptions"
+        :key="item.value"
+        :label="item.label"
+        :value="item.value"
+      >
+        <div class="select-option">
+          <el-checkbox
+            :model-value="customerInfo.customerType.includes(item.value)"
+            style="flex: 1"
+          >
+            <span style="display: inline-block; width: 220px">{{ item.label }}</span>
+          </el-checkbox>
+        </div>
+      </el-option>
+    </el-select>
+    <el-button class="el-button--default" @click="handleSearch">Search</el-button>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.customer-filter {
+  display: flex;
+  align-items: center;
+  height: 48px;
+  padding-left: 24px;
+  border: 1px solid var(--color-border);
+  margin-bottom: 8px;
+}
+</style>

+ 92 - 90
src/views/Dashboard/src/components/DashFiters.vue

@@ -1,6 +1,5 @@
 <script setup lang="ts">
 import { ref, watch, onMounted, computed } from 'vue'
-import moment from 'moment'
 import dayjs from 'dayjs'
 import { formatTimezone } from '@/utils/tools'
 
@@ -8,7 +7,7 @@ const props = defineProps({
   defaultData: {
     type: Object
   },
-  radioisDisabled: {
+  isPending: {
     type: Boolean,
     default: false
   },
@@ -31,29 +30,23 @@ const props = defineProps({
   isShowTransportModeGuide: {
     type: Boolean,
     default: false
+  },
+  threeMonthsInterval: {
+    type: Boolean,
+    default: false
   }
 })
 
-const defaultfiltersData = ref(props.defaultData)
-watch(
-  () => props.defaultData,
-  (current) => {
-    defaultfiltersData.value = current
-  }
-)
-const isDisabled = ref(false)
 const checkboxGroup1 = ref(['All'])
 const CheckboxGroup2 = ref('ETD')
-const CheckboxGroup3 = ref('invoice Issue Date')
+const CheckboxGroup3 = ref('Invoice Issue Date')
 const filters_visible = ref(false)
 const shipper = ref(['All', 'Air', 'Sea', 'Road', 'Rail'])
 const shipper_two = ref(['ETD', 'ETA'])
-const shipper_three = ref(['invoice Issue Date'])
-const DashDate = ref()
-DashDate.value = []
+const shipper_three = ref(['Invoice Issue Date'])
+const DashDate = ref([])
 const startDate = ref()
 const EndDate = ref()
-let dashboardObj: any = {}
 
 const checkboxDisabled = computed(() => {
   if (props.isContainer && !props.isETDToETA) {
@@ -62,42 +55,86 @@ const checkboxDisabled = computed(() => {
     return false
   }
 })
-onMounted(() => {
-  getdefaultdata()
-})
-const getdefaultdata = () => {
-  checkboxGroup1.value = defaultfiltersData.value?.transportation
-  CheckboxGroup2.value = defaultfiltersData.value?.date_type
-  if (defaultfiltersData.value?.date_start == '') {
-    DashDate.value = []
-    isDisabled.value = true
+// onMounted(() => {
+//   getdefaultdata()
+// })
+
+const emit = defineEmits(['FilterSearch'])
+const timeRange = ref([])
+const DateChange = (value: any) => {
+  timeRange.value = value
+}
+const DateRangeSearch = () => {
+  let date = { date_start: '', date_end: '' }
+  if (!props.isPending) {
+    date = props.isContainer
+      ? {
+          date_start: dayjs(startDate.value).format('MM/YYYY'),
+          date_end: dayjs(EndDate.value).format('MM/YYYY')
+        }
+      : {
+          date_start: dayjs(timeRange.value[0]).format('MM/DD/YYYY'),
+          date_end: dayjs(timeRange.value[1]).format('MM/DD/YYYY')
+        }
+  }
+  const data = {
+    transportation: checkboxGroup1.value,
+    date_type: CheckboxGroup2.value,
+    date_start: date.date_start,
+    date_end: date.date_end
+  }
+  emit('FilterSearch', data)
+  filters_visible.value = false
+}
+const getdefaultdata = (data: any, isReset: boolean = false) => {
+  checkboxGroup1.value = data?.transportation
+  CheckboxGroup2.value = data?.date_type
+
+  if (props.isContainer) {
+    // 容器模式:固定12个月范围(含当前月)
+    startDate.value = dayjs().subtract(12, 'month').format('YYYY-MM')
+    EndDate.value = dayjs().format('YYYY-MM')
   } else {
-    if (props.isContainer) {
-      startDate.value = defaultfiltersData.value?.date_start_two
-      EndDate.value = defaultfiltersData.value?.date_end_two
-    } else {
-      DashDate.value = [
-        dayjs(defaultfiltersData.value?.date_start_two),
-        dayjs(defaultfiltersData.value?.date_end_two)
-      ]
-    }
-    if (props.isContainer) {
-      dashboardObj.date_start = defaultfiltersData.value?.date_start
-      dashboardObj.date_start_two = defaultfiltersData.value?.date_start_two
-      dashboardObj.date_end = defaultfiltersData.value?.date_end
-      dashboardObj.date_end_two = defaultfiltersData.value?.date_end_two
+    if (props.isPending) {
+      DashDate.value = ['', '']
     } else {
-      dashboardObj.date_start = dayjs(DashDate.value[0]).format('MM/DD/YYYY')
-      dashboardObj.date_start_two = dayjs(DashDate.value[0]).format('YYYY-MM-DD')
-      dashboardObj.date_end = dayjs(DashDate.value[1]).format('MM/DD/YYYY')
-      dashboardObj.date_end_two = dayjs(DashDate.value[1]).format('YYYY-MM-DD')
+      const monthsInterval = props.threeMonthsInterval ? 3 : 12
+
+      let start = dayjs().subtract(monthsInterval, 'month').startOf('month')
+      let end = dayjs()
+
+      // isRecent 模式下,结束时间延后一个月
+      if (props.isRecent) {
+        end = dayjs().add(1, 'month')
+      }
+
+      DashDate.value = [start, end]
     }
+    timeRange.value = DashDate.value
+  }
+  if (!isReset) {
+    DateRangeSearch()
   }
-  dashboardObj.transportation = checkboxGroup1.value
-  dashboardObj.date_type = CheckboxGroup2.value
 }
+let unwatch // 先声明
+
+unwatch = watch(
+  () => props.defaultData,
+  (value) => {
+    if (value && Object.keys(value).length > 0) {
+      nextTick(() => {
+        getdefaultdata(value)
+        unwatch() // ✅ 现在可以安全调用
+      })
+    }
+  },
+  {
+    immediate: true,
+    deep: true
+  }
+)
 const changeCheckboxGroup1 = (val: any) => {
-  if (val.length == 3) {
+  if (val.length == 4) {
     checkboxGroup1.value = ['All']
   }
   if (val.length == 0) {
@@ -109,18 +146,8 @@ const changeCheckboxGroup1 = (val: any) => {
       checkboxGroup1.value.splice(val.indexOf('All'), 1)
     }
   }
-  dashboardObj.transportation = checkboxGroup1.value
-}
-const changeCheckboxGroup2 = (val: any) => {
-  dashboardObj.date_type = val
-}
-const emit = defineEmits(['FilterSearch'])
-const DateChange = (value: any) => {
-  dashboardObj.date_start = value[0]
-  dashboardObj.date_start_two = dayjs(value[0]).format('YYYY-MM-DD')
-  dashboardObj.date_end = value[1]
-  dashboardObj.date_end_two = dayjs(value[1]).format('YYYY-MM-DD')
 }
+
 const StartChange = (val: any) => {
   if (!props.isETDToETA) {
     const nextMonth = new Date(val)
@@ -129,14 +156,6 @@ const StartChange = (val: any) => {
     let current = currentYear + '-' + currentMonth
     EndDate.value = current
   }
-  const date1 = moment(String(val)).format('MM/YYYY')
-  const date1_two = moment(String(val)).format('MM-YYYY')
-  const date2 = moment(EndDate.value).format('MM/YYYY')
-  const date2_two = moment(EndDate.value).format('MM-YYYY')
-  dashboardObj.date_start = date1
-  dashboardObj.date_start_two = date1_two
-  dashboardObj.date_end = date2
-  dashboardObj.date_end_two = date2_two
 }
 const EndChange = (val: any) => {
   if (!props.isETDToETA) {
@@ -146,31 +165,13 @@ const EndChange = (val: any) => {
     let current = currentYear + '-' + currentMonth
     startDate.value = current
   }
-  const date1 = moment(startDate.value).format('MM/YYYY')
-  const date1_two = moment(startDate.value).format('MM-YYYY')
-  const date2 = moment(String(val)).format('MM/YYYY')
-  const date2_two = moment(String(val)).format('MM-YYYY')
-  dashboardObj.date_start = date1
-  dashboardObj.date_start_two = date1_two
-  dashboardObj.date_end = date2
-  dashboardObj.date_end_two = date2_two
 }
 // 清除
 const clearrest = () => {
-  getdefaultdata()
-}
-// 查询
-const DateRangeSearch = () => {
-  if (props.isRecent) {
-    emit('FilterSearch', false, dashboardObj)
-  } else {
-    emit('FilterSearch', dashboardObj)
-  }
-  filters_visible.value = false
+  getdefaultdata({}, true)
 }
 
 import { useThemeStore } from '@/stores/modules/theme'
-
 import { useGuideStore } from '@/stores/modules/guide'
 import transportModeLight from '@/views/Dashboard/src/guideImage/transport-mode.png'
 import transportModeDark from '@/views/Dashboard/src/guideImage/dark-transport-mode.png'
@@ -213,7 +214,12 @@ const guideStore = useGuideStore()
           size="large"
           :disabled="checkboxDisabled"
         >
-          <el-checkbox-button class="filter_button" v-for="item in shipper" :key="item" :value="item">
+          <el-checkbox-button
+            class="filter_button"
+            v-for="item in shipper"
+            :key="item"
+            :value="item"
+          >
             {{ item }}
           </el-checkbox-button>
         </el-checkbox-group>
@@ -232,11 +238,7 @@ const guideStore = useGuideStore()
           </el-radio-group>
         </div>
         <div class="filter_filter" v-else>
-          <el-radio-group
-            v-model="CheckboxGroup2"
-            @change="changeCheckboxGroup2"
-            :disabled="props.radioisDisabled"
-          >
+          <el-radio-group v-model="CheckboxGroup2" :disabled="props.isPending">
             <el-radio-button v-for="item in shipper_two" :key="item" :value="item" :label="item">
             </el-radio-button>
           </el-radio-group>
@@ -275,7 +277,7 @@ const guideStore = useGuideStore()
             v-else
             @DateChange="DateChange"
             :Date="DashDate"
-            :isDisabled="isDisabled"
+            :isDisabled="isPending"
           ></QuickCalendarDate>
         </div>
       </div>

+ 1 - 1
src/views/Dashboard/src/components/RecentStatus.vue

@@ -84,7 +84,7 @@ const SubscribeShipments = (val: any) => {
       </div>
       <div class="recent-header-right">
         <el-button
-          class="recent_button"
+          class="el-button--default"
           @click="SubscribeShipments(item)"
           :class="item.is_subscribe ? 'IsSubscribe' : ''"
         >

+ 0 - 11
src/views/DestinationDelivery/src/components/TableView/src/TableView.vue

@@ -310,17 +310,6 @@ const handleCreate = () => {
   router.push({ name: 'Create New Booking' })
 }
 
-const testData = [
-  {
-    key: 'A1703530062',
-    value: 'CD1Xeh4rG%2FPmYIiCHAw2%2B0Gw8BFZ8k%2F2pQDkYX3vuf6iZ3kcQXj%2BzQ'
-  },
-  {
-    key: 'A1703530062',
-    value: 'CD1Xeh4rG%2FPmYIiCHAw2%2B0Gw8BFZ8k%2F2pQDkYX3vuf6iZ3kcQXj%2BzQ'
-  }
-]
-
 const handleMultLinkClick = (item: any) => {
   router.push({
     path: '/tracking/detail',

+ 14 - 30
src/views/Layout/src/components/Header/HeaderView.vue

@@ -6,7 +6,6 @@ import { useRouter, useRoute } from 'vue-router'
 import { useUserStore } from '@/stores/modules/user'
 import { useHeaderSearch } from '@/stores/modules/headerSearch'
 import { onBeforeRouteUpdate } from 'vue-router'
-import { useLoadingState } from '@/stores/modules/loadingState'
 import { useThemeStore } from '@/stores/modules/theme'
 import { useNotificationMessage } from '@/stores/modules/notificationMessage'
 import NotificationDrawer from './components/NotificationDrawer.vue'
@@ -29,7 +28,7 @@ const toggleThemeMode = (theme: string) => {
 }
 
 const searchValue = ref('')
-// 用于判断是否在搜索后跳转页面,跳转后清空搜索框的值
+// 用于判断是否在搜索后跳转页面,跳转后清空搜索框的值  false不用清空,true清空
 const isJumpPageBySearch = ref(false)
 const handleSearch = () => {
   if (!searchValue.value) {
@@ -69,34 +68,19 @@ const handleSearch = () => {
       }
     })
   } else {
-    const trackingTableLoadingState = useLoadingState()
-    trackingTableLoadingState.setTrackingTableLoading(true)
-    // 已登录
-    $api
-      .getTrackingTableData({
-        _textSearch: searchValue.value
-      })
-      .then((res) => {
-        if (res.code === 200) {
-          // 如果是在tracking页面搜索,那么isJumpPageBySearch不用置为true,跳转路由后直接清空搜索框
-          if (route.path === '/tracking') {
-            isJumpPageBySearch.value = false
-          } else {
-            isJumpPageBySearch.value = true
-          }
-          headerSearch.setSearchData({
-            searchValue: searchValue.value,
-            searchResult: '',
-            trackingData: res.data
-          })
-          router.push({
-            name: 'Tracking'
-          })
-        }
-      })
-      .finally(() => {
-        trackingTableLoadingState.setTrackingTableLoading(false)
-      })
+    headerSearch.setSearchData({
+      searchValue: searchValue.value,
+      searchResult: '',
+      trackingData: []
+    })
+    if (route.path === '/tracking') {
+      isJumpPageBySearch.value = false
+    } else {
+      isJumpPageBySearch.value = true
+    }
+    router.push({
+      name: 'Tracking'
+    })
   }
 }
 onBeforeRouteUpdate((to, from, next) => {

+ 13 - 1
src/views/Report/src/components/ReportDetail/src/components/FieldsTable.vue

@@ -2,7 +2,7 @@
 import { ref, onMounted } from 'vue'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import { useRowClickStyle } from '@/hooks/rowClickStyle'
-import { formatNumber } from '@/utils/tools'
+import { formatNumber, formatTimezone } from '@/utils/tools'
 import dayjs from 'dayjs'
 import { autoWidth } from '@/utils/table'
 import { useRoute } from 'vue-router'
@@ -85,6 +85,18 @@ const handleColumns = (columns: any) => {
         slots: { default: 'status' }
       }
     }
+    // 格式化
+    if (item.formatter === 'date' || item.formatter === 'dateTime') {
+      curColumn = {
+        ...curColumn,
+        formatter: ({ cellValue }: any) => formatTimezone(cellValue, '', '', 'MM/DD/YYYY HH:mm')
+      }
+    } else if (item.formatter === 'number') {
+      curColumn = {
+        ...curColumn,
+        formatter: ({ cellValue }: any) => formatNumber(Number(cellValue), item?.digits)
+      }
+    }
     return curColumn
   })
   return newColumns

+ 179 - 673
src/views/Tracking/src/TrackingView.vue

@@ -1,6 +1,5 @@
 <script setup lang="ts">
 import FilterTags from '@/components/FliterTags'
-import emitter from '@/utils/bus'
 import TransportMode from '@/components/TransportMode'
 import TrackingTable from './components/TrackingTable'
 import DateRange from '@/components/DateRange'
@@ -10,612 +9,116 @@ import { useCalculatingHeight } from '@/hooks/calculatingHeight'
 import { useHeaderSearch } from '@/stores/modules/headerSearch'
 import { useUserStore } from '@/stores/modules/user'
 import dayjs from 'dayjs'
+import { useFiltersStore } from '@/stores/modules/filtersList'
 
 const userStore = useUserStore()
 const formatDate = userStore.dateFormat
-const valueFormatDate = 'MM/DD/YYYY'
-
-const headerSearch = useHeaderSearch()
 
 const filterRef: Ref<HTMLElement | null> = ref(null)
-
 const containerHeight = useCalculatingHeight(document.documentElement, 246, [filterRef])
 
-const TrackingSearch = ref()
-const tableLoadingTableData = ref(false)
-let searchTableQeuryTracking: any = {}
-const filterData = reactive({
-  filtersTagData: [] as Array<string>,
-  transportData: [] as Array<string>,
-  daterangeData: [] as Array<string>,
-  morefiltersData: [] as Array<string>,
-  dashboardData: [] as Array<string>
-})
-const tagsData: any = ref([])
-const handleClose = (tag: any) => {
-  emitter.emit('clearTag', tag)
-  tagsData.value.splice(tagsData.value.indexOf(tag), 1)
-  if (
-    sessionStorage.getItem('reportList') != null ||
-    sessionStorage.getItem('reportList') != '{}'
-  ) {
-    const reportList = JSON.parse(sessionStorage.getItem('reportList') as string) || {}
-    let data = JSON.parse(sessionStorage.getItem('tagsList') as string) || {}
-    if (tag.includes('Transport')) {
-      delete reportList.transport_mode
-    } else if (tag.includes('Day') || tag.includes('CO2e')) {
-      delete reportList._reportRef
-      delete reportList._reportType
-      delete reportList._reportRefe_date
-      delete reportList._reportRefb_date
-      delete reportList._reportStationType
-      delete reportList._reportDataType
-      delete reportList._reportStationType
-      filterData.dashboardData = []
-      data = {}
-    } else if (tag.includes('ETD')) {
-      filterData.daterangeData.forEach((item: any, index: any) => {
-        if (item.includes('ETD')) {
-          filterData.daterangeData.splice(index, 1)
-        }
-      })
-      delete reportList.etd_start
-      delete reportList.etd_end
-    } else if (tag.includes('ETA')) {
-      filterData.daterangeData.forEach((item: any, index: any) => {
-        if (item.includes('ETA')) {
-          filterData.daterangeData.splice(index, 1)
-        }
-      })
-      delete reportList.eta_start
-      delete reportList.eta_end
-    } else if (tag.includes('Origin')) {
-      delete reportList.shipper_city
-      delete reportList._city_name
-      filterData.dashboardData = []
-    } else if (tag.includes('Destination')) {
-      delete reportList.consignee_city
-    }
-    sessionStorage.setItem('reportList', JSON.stringify(reportList))
-    sessionStorage.setItem('tagsList', JSON.stringify(data))
-  }
-  if (tag.includes('Transport')) {
-    delete searchTableQeuryTracking.transport_mode
-  } else if (tag.includes('Day') || tag.includes('CO2e')) {
-    delete searchTableQeuryTracking._reportRef
-    delete searchTableQeuryTracking._reportType
-    delete searchTableQeuryTracking._reportRefe_date
-    delete searchTableQeuryTracking._reportRefb_date
-    delete searchTableQeuryTracking._reportStationType
-    delete searchTableQeuryTracking._reportDataType
-    delete searchTableQeuryTracking._reportStationType
-    filterData.dashboardData = []
-  } else if (tag.includes('ETD')) {
-    filterData.daterangeData.forEach((item: any, index: any) => {
-      if (item.includes('ETD')) {
-        filterData.daterangeData.splice(index, 1)
-      }
-    })
-    delete searchTableQeuryTracking.etd_start
-    delete searchTableQeuryTracking.etd_end
-  } else if (tag.includes('ETA')) {
-    filterData.daterangeData.forEach((item: any, index: any) => {
-      if (item.includes('ETA')) {
-        filterData.daterangeData.splice(index, 1)
-      }
-    })
-    delete searchTableQeuryTracking.eta_start
-    delete searchTableQeuryTracking.eta_end
-  } else if (tag.includes('Creation')) {
-    delete searchTableQeuryTracking.created_time_start
-    delete searchTableQeuryTracking.created_time_end
-  } else if (tag.includes('Shippername')) {
-    delete searchTableQeuryTracking.shipper
-  } else if (tag.includes('Consigneename')) {
-    delete searchTableQeuryTracking.consignee
-  } else if (tag.includes('Service')) {
-    delete searchTableQeuryTracking.service
-  } else if (tag.includes('Incoterms')) {
-    delete searchTableQeuryTracking.incoterms
-  } else if (tag.includes('Notify Party')) {
-    delete searchTableQeuryTracking.notify_party
-  } else if (tag.includes('Bill to')) {
-    delete searchTableQeuryTracking.billto
-  } else if (tag.includes('Origin Agent')) {
-    delete searchTableQeuryTracking.origin
-  } else if (tag.includes('Destination Agent')) {
-    delete searchTableQeuryTracking.agent
-  } else if (tag.includes('Destination Operator')) {
-    delete searchTableQeuryTracking.dest_op
-  } else if (tag.includes('Sales')) {
-    delete searchTableQeuryTracking.sales_rep
-  } else if (tag.includes('Origin')) {
-    delete searchTableQeuryTracking.shipper_city
-    delete searchTableQeuryTracking._city_name
-    filterData.dashboardData = []
-  } else if (tag.includes('Destination')) {
-    delete searchTableQeuryTracking.consignee_city
-  } else if (tag.includes('Place of Receipt')) {
-    delete searchTableQeuryTracking.place_of_receipt_exp
-  } else if (tag.includes('Port of Loading')) {
-    delete searchTableQeuryTracking.port_of_loading
-  } else if (tag.includes('Place of delivery')) {
-    delete searchTableQeuryTracking.place_of_delivery_exp
-  } else if (tag.includes('Place of Discharge')) {
-    delete searchTableQeuryTracking.port_of_discharge
-  } else if (tag.includes('Vessel')) {
-    delete searchTableQeuryTracking['f_vessel/vessel']
-  } else if (tag.includes('Voyage')) {
-    delete searchTableQeuryTracking['f_voyage/voyage']
-  }
-
-  sessionStorage.setItem('searchTableQeuryTracking', JSON.stringify(searchTableQeuryTracking))
-  getTrackingData()
-}
-// 筛选框查询
-const FiltersSeach = (val: any, value: any) => {
-  searchTableQeuryTracking[val] = value
-
-  sessionStorage.setItem('searchTableQeuryTracking', JSON.stringify(searchTableQeuryTracking))
-  getTrackingData()
-}
-//TransportSearch
-const TransportSearch = (val: any) => {
-  filterData.transportData = []
-  if (val.data.length != 0) {
-    let str = `${val.title}:${val.data}`
-    filterData.transportData.push(str)
-  }
-  FiltersSeach('transport_mode', val.data)
-  renderTagsData()
-}
-// defaultTransport
-const defaultTransport = (val: any, value: any) => {
-  filterData.transportData = []
-  if (val.data.length != 0) {
-    let str = `${val.title}:${val.data}`
-    filterData.transportData.push(str)
-  }
-  if (sessionStorage.getItem('searchTableQeuryTracking') == null) {
-    if (
-      sessionStorage.getItem('clickParams') === null ||
-      sessionStorage.getItem('clickParams') === '{}'
-    ) {
-      searchTableQeuryTracking.transport_mode = val.data
-    }
-  } else {
-    searchTableQeuryTracking = value
-  }
-}
-const setFilterData = (dateRangeData: any) => {
-  filterData.daterangeData = []
-  for (const key in dateRangeData) {
-    const startEnd = dateRangeData[key].data[0] + ' To ' + dateRangeData[key].data[1]
-    let str = `${key}:${startEnd}`
-    filterData.daterangeData.push(str)
-  }
-}
-//defaultDate
-const defaultDate = (dateRangeData: any, data: any) => {
-  setFilterData(dateRangeData)
-
-  if (sessionStorage.getItem('searchTableQeuryTracking') == null) {
-    if (
-      sessionStorage.getItem('clickParams') === null ||
-      sessionStorage.getItem('clickParams') === '{}'
-    ) {
-      if (Object.keys(dateRangeData).length == 0) {
-        delete searchTableQeuryTracking.etd_start
-        delete searchTableQeuryTracking.etd_end
-      }
-      for (const key in dateRangeData) {
-        searchTableQeuryTracking.etd_start = dateRangeData[key].data[0]
-
-        searchTableQeuryTracking.etd_end = dateRangeData[key].data[1]
-      }
-    }
-  } else {
-    searchTableQeuryTracking = data
-    if (searchTableQeuryTracking._textSearch) {
-      TrackingSearch.value = searchTableQeuryTracking._textSearch
-    }
-  }
-
-  const rawData = localStorage.getItem('searchData')
-  const trackingData = rawData ? JSON.parse(rawData) : null
-  if (trackingData) {
-    // 根据顶部搜索框的搜索结果赋值
-    initDataByHeaderSearch()
-  } else if (!trackingData && !sessionStorage.getItem('clickParams')) {
-    getTrackingData()
-  }
-
-  renderTagsData()
-}
-//DateRangeSearch
-const DateRangeSearch = (dateRangeData: any) => {
-  setFilterData(dateRangeData)
-  if (Object.keys(dateRangeData).length == 0) {
-    delete searchTableQeuryTracking.etd_start
-    delete searchTableQeuryTracking.etd_end
-    delete searchTableQeuryTracking.eta_start
-    delete searchTableQeuryTracking.eta_end
-    delete searchTableQeuryTracking.created_time_start
-    delete searchTableQeuryTracking.created_time_end
-  }
-  const fieldList = [
-    {
-      title: 'ETD',
-      keys: ['etd_start', 'etd_end']
-    },
-    { title: 'ETA', keys: ['eta_start', 'eta_end'] },
-    { title: 'Creation Time', keys: ['created_time_start', 'created_time_end'] }
-  ]
-  fieldList.forEach((item) => {
-    if (!dateRangeData.hasOwnProperty(item.title)) {
-      // 删除不存在的字段
-      searchTableQeuryTracking[item.keys[0]] = undefined
-      searchTableQeuryTracking[item.keys[1]] = undefined
-    }
-  })
-  for (const key in dateRangeData) {
-    if (key == 'ETD') {
-      searchTableQeuryTracking.etd_start = dateRangeData[key].data[0]
-      searchTableQeuryTracking.etd_end = dateRangeData[key].data[1]
-    } else if (key == 'ETA') {
-      searchTableQeuryTracking.eta_start = dateRangeData[key].data[0]
-      searchTableQeuryTracking.eta_end = dateRangeData[key].data[1]
-    } else {
-      searchTableQeuryTracking.created_time_start = dateRangeData[key].data[0]
-      searchTableQeuryTracking.created_time_end = dateRangeData[key].data[1]
-    }
-  }
-  sessionStorage.setItem('searchTableQeuryTracking', JSON.stringify(searchTableQeuryTracking))
-  getTrackingData()
-  renderTagsData()
-}
-//MoreFiltersSearch
-const MoreFiltersSearch = (val: any, value: any) => {
-  filterData.morefiltersData = []
-  if (Object.keys(value).length == 0) {
-    delete searchTableQeuryTracking.shipper
-    delete searchTableQeuryTracking.consignee
-    delete searchTableQeuryTracking.service
-    delete searchTableQeuryTracking.incoterms
-    delete searchTableQeuryTracking.notify_party
-    delete searchTableQeuryTracking.billto
-    delete searchTableQeuryTracking.origin
-    delete searchTableQeuryTracking.agent
-    delete searchTableQeuryTracking.sales_rep
-    delete searchTableQeuryTracking.dest_op
-    delete searchTableQeuryTracking.consignee_city
-    delete searchTableQeuryTracking.place_of_receipt_exp
-    delete searchTableQeuryTracking.place_of_delivery_exp
-    delete searchTableQeuryTracking.port_of_discharge
-    delete searchTableQeuryTracking.shipper_city
-    delete searchTableQeuryTracking['port_of_loading/fport_of_loading_un']
-    delete searchTableQeuryTracking['f_vessel/vessel']
-    delete searchTableQeuryTracking['f_voyage/voyage']
-  }
-  for (const key in val) {
-    let str = `${key}:${val[key]}`
-    filterData.morefiltersData.push(str)
-    if (key == 'Shippername') {
-      searchTableQeuryTracking.shipper = value[key]
-    } else if (key == 'Consigneename') {
-      searchTableQeuryTracking.consignee = value[key]
-    } else if (key == 'Service') {
-      searchTableQeuryTracking.service = value[key]
-    } else if (key == 'Incoterms') {
-      searchTableQeuryTracking.incoterms = value[key]
-    } else if (key == 'Notify Party') {
-      searchTableQeuryTracking.notify_party = value[key]
-    } else if (key == 'Bill to') {
-      searchTableQeuryTracking.billto = value[key]
-    } else if (key == 'Origin Agent') {
-      searchTableQeuryTracking.origin = value[key]
-    } else if (key == 'Destination Agent') {
-      searchTableQeuryTracking.agent = value[key]
-    } else if (key == 'Destination Operator') {
-      searchTableQeuryTracking.dest_op = value[key]
-    } else if (key == 'Sales') {
-      searchTableQeuryTracking.sales_rep = value[key]
-    } else if (key == 'Destination') {
-      searchTableQeuryTracking.consignee_city = value[key]
-    } else if (key == 'Place of Receipt') {
-      searchTableQeuryTracking.place_of_receipt_exp = value[key]
-    } else if (key == 'Origin') {
-      searchTableQeuryTracking.shipper_city = value[key]
-    } else if (key == 'Port of Loading') {
-      searchTableQeuryTracking['port_of_loading/fport_of_loading_un'] = value[key]
-    } else if (key == 'Place of delivery') {
-      searchTableQeuryTracking.place_of_delivery_exp = value[key]
-    } else if (key == 'Port of Discharge') {
-      searchTableQeuryTracking.port_of_discharge = value[key]
-    } else if (key == 'Vessel') {
-      searchTableQeuryTracking['f_vessel/vessel'] = value[key]
-    } else if (key == 'Voyage') {
-      searchTableQeuryTracking['f_voyage/voyage'] = value[key]
-    }
-  }
-  sessionStorage.setItem('searchTableQeuryTracking', JSON.stringify(searchTableQeuryTracking))
-  getTrackingData()
-  renderTagsData()
-}
-const defaultMorefilters = (val: any, value: any, data: any) => {
-  filterData.morefiltersData = []
-  for (const key in val) {
-    let str = `${key}:${val[key]}`
-    filterData.morefiltersData.push(str)
-    for (const key in val) {
-      if (key == 'Shippername') {
-        searchTableQeuryTracking.shipper = value[key]
-      } else if (key == 'Consigneename') {
-        searchTableQeuryTracking.consignee = value[key]
-      } else if (key == 'Service') {
-        searchTableQeuryTracking.service = value[key]
-      } else if (key == 'Incoterms') {
-        searchTableQeuryTracking.incoterms = value[key]
-      } else if (key == 'Notify Party') {
-        searchTableQeuryTracking.notify_party = value[key]
-      } else if (key == 'Bill to') {
-        searchTableQeuryTracking.billto = value[key]
-      } else if (key == 'Destination Operator') {
-        searchTableQeuryTracking.dest_op = value[key]
-      } else if (key == 'Origin Agent') {
-        searchTableQeuryTracking.origin = value[key]
-      } else if (key == 'Destination Agent') {
-        searchTableQeuryTracking.agent = value[key]
-      } else if (key == 'Sales') {
-        searchTableQeuryTracking.sales_rep = value[key]
-      } else if (key == 'Origin') {
-        searchTableQeuryTracking.shipper_city = value[key]
-      } else if (key == 'Destination') {
-        searchTableQeuryTracking.consignee_city = value[key]
-      } else if (key == 'Place of Receipt') {
-        searchTableQeuryTracking.place_of_receipt_exp = value[key]
-      } else if (key == 'Port of Loading') {
-        searchTableQeuryTracking.port_of_loading = value[key]
-      } else if (key == 'Place of delivery') {
-        searchTableQeuryTracking.place_of_delivery_exp = value[key]
-      } else if (key == 'Place of Discharge') {
-        searchTableQeuryTracking.port_of_discharge = value[key]
-      } else if (key == 'Vessel') {
-        searchTableQeuryTracking['f_vessel/vessel'] = value[key]
-      } else if (key == 'Voyage') {
-        searchTableQeuryTracking['f_voyage/voyage'] = value[key]
-      }
-    }
-    if (sessionStorage.getItem('searchTableQeuryTracking') != null) {
-      searchTableQeuryTracking = data
-    }
-  }
-}
-const clearfilters = () => {
-  TrackingSearch.value = ''
-  filterData.filtersTagData = []
-  tagsData.value = []
-  let str = 'Shipment status: All'
-  filterData.filtersTagData.push(str)
-  filterData.transportData = []
-  filterData.daterangeData = []
-  filterData.morefiltersData = []
-  filterData.dashboardData = []
-  emitter.emit('clearTag', 'Shipment status')
-  emitter.emit('clearTag', 'Transport Mode')
-  emitter.emit('clearTag', 'ETD')
-  emitter.emit('clearTag', 'ETA')
-  emitter.emit('clearTag', 'Creation Time')
-  emitter.emit('clearTag', 'Shippername')
-  emitter.emit('clearTag', 'Consigneename')
-  emitter.emit('clearTag', 'Service')
-  emitter.emit('clearTag', 'Incoterms')
-  emitter.emit('clearTag', 'Notify Party')
-  emitter.emit('clearTag', 'Bill to')
-  emitter.emit('clearTag', 'Origin Agent')
-  emitter.emit('clearTag', 'Destination Agent')
-  emitter.emit('clearTag', 'Destination Operator')
-  emitter.emit('clearTag', 'Sales')
-  emitter.emit('clearTag', 'Origin')
-  emitter.emit('clearTag', 'Destination')
-  emitter.emit('clearTag', 'Place of Receipt')
-  emitter.emit('clearTag', 'Port of Loading')
-  emitter.emit('clearTag', 'Place of delivery')
-  emitter.emit('clearTag', 'Port of Discharge')
-  emitter.emit('clearTag', 'Vessel')
-  emitter.emit('clearTag', 'Voyage')
-  searchTableQeuryTracking = {}
-  searchTableQeuryTracking.filterTag = ['All']
-  sessionStorage.setItem('searchTableQeuryTracking', JSON.stringify(searchTableQeuryTracking))
-  if (
-    sessionStorage.getItem('reportList') != null ||
-    sessionStorage.getItem('reportList') != '{}'
-  ) {
-    // sessionStorage.removeItem('reportList')
-    sessionStorage.removeItem('tagsList')
-  }
-  getTrackingData()
-  renderTagsData()
-}
-const renderTagsData = () => {
-  const data = JSON.parse(sessionStorage.getItem('tagsList') as string) || {}
-  if (Object.keys(data).length != 0) {
-    let str = `${data.title}:${data.name}`
-    !filterData.dashboardData.includes(str) && filterData.dashboardData.push(str)
-  }
-  tagsData.value = []
-  if (filterData.transportData.length) {
-    tagsData.value.push(filterData.transportData[0])
-  }
-  if (filterData.daterangeData.length) {
-    filterData.daterangeData.forEach((item) => {
-      tagsData.value.push(item)
-    })
-  }
-  if (filterData.morefiltersData.length) {
-    filterData.morefiltersData.forEach((item) => {
-      tagsData.value.push(item)
-    })
-  }
-  if (filterData.dashboardData.length) {
-    filterData.dashboardData.forEach((item) => {
-      tagsData.value.push(item)
-    })
-  }
-  if (filterData.filtersTagData.length) {
-    filterData.filtersTagData.forEach((item) => {
-      tagsData.value.push(item)
-    })
-  }
-}
-// 清除 Transport Tags
-const clearTransportTags = () => {
-  filterData.transportData = []
-}
-// 清除 Daterange Tags
-const clearDaterangeTags = () => {
-  filterData.daterangeData = []
-}
-// 清除 MoreFilters Tags
-const clearMoreFiltersTags = () => {
-  filterData.morefiltersData = []
-}
-
-const initDataByHeaderSearch = () => {
-  const data = JSON.parse(localStorage.getItem('searchData'))
-  if (data) {
-    // 更新搜索框的值
-    TrackingSearch.value = data.searchValue
-    // 更新表格数据
-    TrackingTable_ref.value.getSharedTableData()
-    // 更新tagsList和TransportList
-    TransportListItem.value = data.trackingData.TransportList
-    TagsList.value = data.trackingData.tagsList
-    let obj = {
-      IncotermsList: data.trackingData.IncotermsList,
-      ServiceList: data.trackingData.ServiceList
-    }
-    sessionStorage.setItem('incotermsList', JSON.stringify(obj))
-    headerSearch.clearSearchData()
-    tagsData.value = []
-    filterData.transportData = []
-    filterData.daterangeData = []
-    filterData.morefiltersData = []
-    filterData.dashboardData = []
-    filterData.filtersTagData = []
-    let str = 'Shipment status: All'
-    filterData.filtersTagData.push(str)
-    searchTableQeuryTracking = {}
-    sessionStorage.removeItem('searchTableQeuryTracking')
-    searchTableQeuryTracking._textSearch = TrackingSearch.value
-    searchTableQeuryTracking.filterTag = ['All']
-
-    sessionStorage.setItem('searchTableQeuryTracking', JSON.stringify(searchTableQeuryTracking))
-    setTimeout(() => {
-      emitter.emit('clearTag', 'Shipment status')
-      emitter.emit('clearTag', 'Transport Mode')
-      emitter.emit('clearTag', 'ETD')
-      emitter.emit('clearTag', 'ETA')
-      emitter.emit('clearTag', 'Creation Time')
-      emitter.emit('clearTag', 'Shippername')
-      emitter.emit('clearTag', 'Consigneename')
-      emitter.emit('clearTag', 'Service')
-      emitter.emit('clearTag', 'Incoterms')
-      emitter.emit('clearTag', 'Notify Party')
-      emitter.emit('clearTag', 'Bill to')
-      emitter.emit('clearTag', 'Origin Agent')
-      emitter.emit('clearTag', 'Destination Agent')
-      emitter.emit('clearTag', 'Destination Operator')
-      emitter.emit('clearTag', 'Sales')
-      emitter.emit('clearTag', 'Origin')
-      emitter.emit('clearTag', 'Destination')
-      emitter.emit('clearTag', 'Place of Receipt')
-      emitter.emit('clearTag', 'Port of Loading')
-      emitter.emit('clearTag', 'Place of delivery')
-      emitter.emit('clearTag', 'Port of Discharge')
-      emitter.emit('clearTag', 'Vessel')
-      emitter.emit('clearTag', 'Voyage')
-    }, 2000)
-    renderTagsData()
-  }
-}
+const filtersStore = useFiltersStore()
+const filtersList = computed(() => filtersStore.filtersList)
 
+const headerSearch = useHeaderSearch()
 // 从 store 中获取数据并绑定到输入框
-const headerSearchdData = computed(() => headerSearch.searchValue)
+const headerSearchValue = computed(() => headerSearch.searchValue)
 // 监听顶部搜索结果的变化
 watch(
-  () => headerSearchdData.value,
+  () => headerSearchValue.value,
   (newData) => {
     if (newData) {
-      initDataByHeaderSearch()
+      // 从顶部获取的值
+      filtersStore.clearFilters()
+      filtersStore.updateFilter({
+        title: 'Shipment Status',
+        value: ['All'],
+        keyType: 'array',
+        key: 'filterTag'
+      })
+      textSearch.value = headerSearchValue.value
+      getTrackingData()
     }
   }
 )
 
-const TrackingTable_ref = ref()
-const TransportListItem = ref()
-interface ListItem {
-  name: string
-  number: number
-  type: string
-  checked: boolean
-}
-const TagsList = ref<ListItem[]>([])
-const filterTag = ref(['All'])
-const isShowAlertIcon = ref(false)
-const getTrackingData = () => {
-  const dateRangeKeys = [
-    'etd_start',
-    'etd_end',
-    'eta_start',
-    'eta_end',
-    'created_time_start',
-    'created_time_end'
-  ]
-  const curRangeData = {}
-  for (const key of dateRangeKeys) {
-    if (searchTableQeuryTracking[key]) {
-      curRangeData[key] = dayjs(searchTableQeuryTracking[key], formatDate).format(valueFormatDate)
-    }
+const textSearch = ref()
+const tableLoadingTableData = ref(false)
+
+const tabList = ref([
+  {
+    name: 'All',
+    number: 0,
+    type: 'all',
+    checked: true
+  },
+  {
+    name: 'Created',
+    number: 0,
+    type: 'created',
+    checked: false
+  },
+  {
+    name: 'Cargo Received',
+    number: 0,
+    type: 'cargo_received',
+    checked: false
+  },
+  {
+    name: 'Departed',
+    number: 0,
+    type: 'departed',
+    checked: false
+  },
+  {
+    name: 'Arrived',
+    number: 0,
+    type: 'arrived',
+    checked: false
+  },
+  {
+    name: 'Completed',
+    number: 0,
+    type: 'completed',
+    checked: false
   }
+])
+
+const getTrackingData = () => {
+  const queryData = filtersStore.getQueryData
 
   tableLoadingTableData.value = true
-  TrackingTable_ref.value.getLoadingData(tableLoadingTableData.value)
+  trackingTable_ref.value.getLoadingData(tableLoadingTableData.value)
   $api
     .getTrackingTableData({
-      cp: TrackingTable_ref.value.pageInfo.pageNo,
-      ps: TrackingTable_ref.value.pageInfo.pageSize,
+      cp: trackingTable_ref.value.pageInfo.pageNo,
+      ps: trackingTable_ref.value.pageInfo.pageSize,
       rc: -1,
       other_filed: '',
-      ...searchTableQeuryTracking,
-      ...curRangeData
+      _textSearch: textSearch.value,
+      ...queryData
     })
     .then((res: any) => {
       if (res.code === 200) {
-        TransportListItem.value = res.data.TransportList
-        TagsList.value = res.data.tagsList
-        let obj = {
-          IncotermsList: res.data.IncotermsList,
-          ServiceList: res.data.ServiceList
-        }
-        let taglist: any = []
-        if (Object.keys(filterData.filtersTagData).length == 0) {
-          TagsList.value.forEach((item: any) => {
-            if (item.checked == true) {
-              taglist.push(item.name)
-            }
-          })
-          let str = 'Shipment status: ' + taglist.toString()
-          filterData.filtersTagData.push(str)
-          filterData.filtersTagData.forEach((item) => {
-            tagsData.value.push(item)
-          })
-        }
-        sessionStorage.setItem('incotermsList', JSON.stringify(obj))
+        transportListItem.value = res.data.TransportList
+
+        tabList.value = res.data.tagsList
+        const checkedTabNames = tabList.value
+          .filter((item) => item.checked)
+          .map((item) => item.name)
+        filtersStore.updateFilter({
+          title: 'Shipment Status',
+          value: checkedTabNames,
+          keyType: 'array',
+          key: 'filterTag'
+        })
+        incotermsList.value = res.data.IncotermsList
+        serviceList.value = res.data.ServiceList
+
         sessionStorage.setItem('TrackingData', JSON.stringify(res.data))
-        TrackingTable_ref.value.searchTableData(searchTableQeuryTracking)
+        trackingTable_ref.value.searchTableData()
         // 查询没结果的话显示icon
-        if (TrackingSearch.value != '' && TrackingSearch.value != undefined) {
+        if (textSearch.value != '' && textSearch.value != undefined) {
           if (res.data.searchData.length == 0) {
             isShowAlertIcon.value = true
           }
@@ -624,96 +127,105 @@ const getTrackingData = () => {
         }
       }
     })
-    .finally(() => {
-      sessionStorage.removeItem('clickParams')
+}
+onMounted(() => {})
+const initPage = () => {
+  if (headerSearchValue.value) {
+    filtersStore.clearFilters()
+    filtersStore.updateFilter({
+      title: 'Shipment Status',
+      value: ['All'],
+      keyType: 'array',
+      key: 'filterTag'
+    })
+    textSearch.value = headerSearchValue.value
+  } else if (!filtersList.value || (filtersList.value && filtersList.value.length == 0)) {
+    filtersStore.updateFilter({
+      title: 'Transport Mode',
+      value: ['All'],
+      keyType: 'array',
+      key: 'transport_mode'
+    })
+    filtersStore.updateFilter({
+      title: 'ETD',
+      value: [
+        dayjs().subtract(2, 'month').startOf('month').format(formatDate),
+        dayjs().add(1, 'month').format(formatDate)
+      ],
+      keyType: 'dateRange',
+      key: ['etd_start', 'etd_end']
     })
+    filtersStore.updateFilter({
+      title: 'Shipment Status',
+      value: ['All'],
+      keyType: 'array',
+      key: 'filterTag'
+    })
+  }
+  getTrackingData()
 }
+
 onMounted(() => {
-  if (
-    sessionStorage.getItem('clickParams') != null &&
-    sessionStorage.getItem('clickParams') != '{}'
-  ) {
-    const reportList = JSON.parse(sessionStorage.getItem('reportList') as string) || {}
-    for (const key in reportList) {
-      let str = ''
-      switch (key) {
-        case 'etd_start':
-          const startDate = dayjs(reportList['etd_start'], valueFormatDate).format(formatDate)
-          searchTableQeuryTracking['etd_start'] = startDate
-          const endDate = dayjs(reportList['etd_end'], valueFormatDate).format(formatDate)
-          searchTableQeuryTracking['etd_end'] = endDate
-          const startEnd = startDate + ' To ' + endDate
-          str = `ETD:${startEnd}`
-          // filterData.daterangeData.push(str)
-          break
+  initPage()
+})
 
-        case 'etd_end':
-          break
-        case 'eta_start':
-          const etaStart = dayjs(reportList['eta_start'], valueFormatDate).format(formatDate)
-          searchTableQeuryTracking['eta_start'] = etaStart
-          const etaEnd = dayjs(reportList['eta_end'], valueFormatDate).format(formatDate)
-          searchTableQeuryTracking['eta_end'] = etaEnd
-          str = `ETA:${etaStart} To ${etaEnd}`
-          // filterData.daterangeData.push(str)
-          break
+//defaultDate
 
-        case 'eta_end':
-          break
-        case 'created_time_start':
-          const createdTimeStart = dayjs(reportList['created_time_start'], valueFormatDate).format(
-            formatDate
-          )
-          searchTableQeuryTracking['created_time_start'] = createdTimeStart
-          const createdTimeEnd = dayjs(reportList['created_time_end'], valueFormatDate).format(
-            formatDate
-          )
-          searchTableQeuryTracking['created_time_end'] = createdTimeEnd
-          str = `Creation Time:${createdTimeStart} To ${createdTimeEnd}`
-          // filterData.daterangeData.push(str)
-          break
-        case 'created_time_end':
-          break
-        default:
-          searchTableQeuryTracking[key] = reportList[key]
-      }
-    }
+const trackingTable_ref = ref()
+const transportListItem = ref()
 
-    // sessionStorage.removeItem('reportList')
-    sessionStorage.setItem('searchTableQeuryTracking', JSON.stringify(searchTableQeuryTracking))
-    getTrackingData()
-  }
-  renderTagsData()
-})
-const changeTag = (val: any, boolean: any) => {
-  filterData.filtersTagData = []
-  searchTableQeuryTracking.filterTag = val
-  let str = 'Shipment status: ' + val
-  filterData.filtersTagData.push(str)
-  filterTag.value = val
+const isShowAlertIcon = ref(false)
+const incotermsList = ref([])
+const serviceList = ref([])
 
-  sessionStorage.setItem('searchTableQeuryTracking', JSON.stringify(searchTableQeuryTracking))
-  if (boolean) {
-    getTrackingData()
-  }
-  renderTagsData()
+const tabChange = (changeTabList: any) => {
+  tabList.value = changeTabList
+  const checkedTabNames = tabList.value.filter((item) => item.checked).map((item) => item.name)
+  filtersStore.updateFilter({
+    title: 'Shipment Status',
+    value: checkedTabNames,
+    keyType: 'array',
+    key: 'filterTag'
+  })
+
+  getTrackingData()
 }
 // 点击search按钮
 const SearchInput = () => {
-  searchTableQeuryTracking._textSearch = TrackingSearch.value
-
-  sessionStorage.setItem('searchTableQeuryTracking', JSON.stringify(searchTableQeuryTracking))
   getTrackingData()
 }
 
 import TrackingGuide from './components/TrackingGuide.vue'
 import { useGuideStore } from '@/stores/modules/guide'
+import { onBeforeRouteLeave } from 'vue-router'
 
 const guideStore = useGuideStore()
 const trackingGuideRef = ref()
 const handleGuide = () => {
   trackingGuideRef.value.startGuide() // 开始引导
 }
+
+const clearfilters = () => {
+  filtersStore.clearFilters()
+  getTrackingData()
+}
+
+const handleClose = (tagTitle: any) => {
+  filtersStore.deleteFilterByTitle(tagTitle)
+  getTrackingData()
+}
+
+const clearDaterangeTags = () => {}
+
+const clearMoreFiltersTags = () => {}
+
+onBeforeRouteLeave((route: any) => {
+  // guideStore.clearGuide()
+  const whiteList = ['Booking Detail', 'Tracking Detail']
+  if (!whiteList.includes(route?.name)) {
+    filtersStore.clearFilters()
+  }
+})
 </script>
 
 <template>
@@ -725,12 +237,12 @@ const handleGuide = () => {
   <div class="display" ref="filterRef">
     <div class="filter-box">
       <div class="filters-container" id="filters-container-guide">
-        <FilterTags :TagsListItem="TagsList" @changeTag="changeTag"></FilterTags>
+        <FilterTags :tagsList="tabList" @tabChange="tabChange"></FilterTags>
         <div class="heaer_top">
           <div class="search">
             <el-input
               placeholder="Enter Booking/HBL/PO/Container/Carrier Booking No. "
-              v-model="TrackingSearch"
+              v-model="textSearch"
               class="log_input"
               @keyup.enter="SearchInput"
             >
@@ -761,62 +273,56 @@ const handleGuide = () => {
           </div>
 
           <TransportMode
-            :isShipment="true"
-            :TransportListItem="TransportListItem"
-            @TransportSearch="TransportSearch"
-            @defaultTransport="defaultTransport"
-            @clearTransportTags="clearTransportTags"
+            :transportListItem="transportListItem"
+            @TransportSearch="getTrackingData()"
           ></TransportMode>
           <DateRange
-            :isShipment="true"
-            @DateRangeSearch="DateRangeSearch"
+            @DateRangeSearch="getTrackingData()"
             @clearDaterangeTags="clearDaterangeTags"
-            @defaultDate="defaultDate"
           ></DateRange>
         </div>
       </div>
       <MoreFilters
-        :isShipment="true"
-        :searchTableQeury="searchTableQeuryTracking"
-        @MoreFiltersSearch="MoreFiltersSearch"
+        @handleSearch="getTrackingData()"
+        :incotermsList="incotermsList"
+        :serviceList="serviceList"
         @clearMoreFiltersTags="clearMoreFiltersTags"
-        @defaultMorefilters="defaultMorefilters"
         :isShowMoreFiltersGuidePhoto="guideStore.tracking.isShowMoreFiltersGuidePhoto"
-        :pageMode="'tracking'"
       ></MoreFilters>
       <el-button class="el-button--dark" style="margin-left: 8px" @click="SearchInput"
         >Search</el-button
       >
     </div>
     <!-- 筛选项 -->
-    <div class="filtersTag" v-if="tagsData.length" id="filter-tag-guide">
+    <div class="filtersTag" v-if="filtersStore.getTagsList.length" id="filter-tag-guide">
       <el-tag
-        :key="tag"
+        :key="tag.title"
         class="tag"
-        v-for="tag in tagsData"
-        :closable="!tag.includes('Shipment')"
+        v-for="tag in filtersStore.getTagsList"
+        :closable="!tag.title.includes('Shipment')"
         :disable-transitions="false"
-        @close="handleClose(tag)"
+        @close="handleClose(tag.title)"
         color="#EFEFF0"
       >
         <el-tooltip
           class="box-item"
           effect="dark"
-          :content="tag"
+          :content="tag.value"
           placement="top"
-          v-if="tag.length > 39"
+          :popper-style="{ maxWidth: '500px', whiteSpace: 'normal', wordWrap: 'break-word' }"
+          v-if="tag.value.length > 39"
         >
-          {{ tag.length > 39 ? tag.substr(0, 39) + '...' : tag }}
+          {{ tag.value.length > 39 ? tag.value.slice(0, 39) + '...' : tag.value }}
         </el-tooltip>
-        <div v-else>{{ tag }}</div>
+        <div v-else>{{ tag.value }}</div>
       </el-tag>
       <div class="text_button" @click="clearfilters">Clear Filters</div>
     </div>
   </div>
   <TrackingTable
+    :textSearch="textSearch"
     :height="containerHeight"
-    :tagsData="tagsData"
-    ref="TrackingTable_ref"
+    ref="trackingTable_ref"
   ></TrackingTable>
 </template>
 

+ 19 - 65
src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue

@@ -6,11 +6,15 @@ import dayjs from 'dayjs'
 import { useRouter } from 'vue-router'
 import { ref, onMounted } from 'vue'
 import { transportationMode } from '@/components/transportationMode'
-import { useLoadingState } from '@/stores/modules/loadingState'
 import { useThemeStore } from '@/stores/modules/theme'
 import { useVisitedRowState } from '@/stores/modules/visitedRow'
 import { formatTimezone, formatNumber } from '@/utils/tools'
 import { useTrackingDownloadData } from '@/stores/modules/trackingDownloadData'
+import { useFiltersStore } from '@/stores/modules/filtersList'
+import { useHeaderSearch } from '@/stores/modules/headerSearch'
+
+const filtersStore = useFiltersStore()
+const headerSearchStore = useHeaderSearch()
 
 const visitedRowState = useVisitedRowState()
 const themeStore = useThemeStore()
@@ -22,9 +26,9 @@ const props = defineProps({
     type: Number,
     default: 440
   },
-  tagsData: {
-    type: Array,
-    default: () => []
+  textSearch: {
+    type: String,
+    default: ''
   }
 })
 const tableLoadingTable = ref()
@@ -154,36 +158,21 @@ const assignTableData = (data: any) => {
   }, 1000)
 }
 
-const filterdataobj = ref()
-const getSharedTableData = () => {
-  const trackingData = JSON.parse(localStorage.getItem('searchData'))?.trackingData
-  if (trackingData) {
-    pageInfo.value.pageSize = Number(trackingData.ps)
-    sessionStorage.setItem('trackingTablePageInfo', JSON.stringify(pageInfo.value))
-    assignTableData(trackingData)
-    selectedNumber.value = 0
-    selectedTableData.value = []
-    nextTick(() => {
-      tableRef.value && autoWidth(trackingTable.value, tableRef.value)
-    })
-    return true
-  }
-  return false
-}
-
 // 切换分页时重新获取表格数据
 const getTableData = async (isPageChange?: boolean) => {
   // 保存页长以及当前页码
   sessionStorage.setItem('trackingTablePageInfo', JSON.stringify(pageInfo.value))
   tableLoadingTable.value = true
   const rc = isPageChange ? pageInfo.value.total : -1
+  const queryData = filtersStore.getQueryData
   await $api
     .getTrackingTableData({
       cp: pageInfo.value.pageNo,
       ps: pageInfo.value.pageSize,
       rc,
       other_filed: '',
-      ...filterdataobj.value
+      _textSearch: props.textSearch,
+      ...queryData
     })
     .then((res: any) => {
       if (res.code === 200) {
@@ -198,46 +187,10 @@ const getTableData = async (isPageChange?: boolean) => {
       selectedNumber.value = 0
       selectedTableData.value = []
     })
-  // if (
-  //   sessionStorage.getItem('clickParams') != null &&
-  //   sessionStorage.getItem('clickParams') != '{}'
-  // ) {
-  //   const data = JSON.parse(sessionStorage.getItem('clickParams') as string) || {}
-  //   assignTableData(data)
-  //   nextTick(() => {
-  //     tableRef.value && autoWidth(trackingTable.value, tableRef.value)
-  //     tableLoadingTable.value = false
-  //     selectedNumber.value = 0
-  //     selectedTableData.value = []
-  //   })
-  // } else {
-  //   await $api
-  //     .getTrackingTableData({
-  //       cp: pageInfo.value.pageNo,
-  //       ps: pageInfo.value.pageSize,
-  //       rc,
-  //       other_filed: '',
-  //       ...filterdataobj.value
-  //     })
-  //     .then((res: any) => {
-  //       if (res.code === 200) {
-  //         assignTableData(res.data)
-  //       }
-  //     })
-  //     .finally(() => {
-  //       nextTick(() => {
-  //         tableRef.value && autoWidth(trackingTable.value, tableRef.value)
-  //         tableLoadingTable.value = false
-  //       })
-  //       selectedNumber.value = 0
-  //       selectedTableData.value = []
-  //     })
-  // }
 }
 
 // 查询列表数据
-const searchTableData = (data: any) => {
-  filterdataobj.value = data
+const searchTableData = () => {
   if (sessionStorage.getItem('TrackingData') != null) {
     const data = JSON.parse(sessionStorage.getItem('TrackingData') as string) || {}
     assignTableData(data)
@@ -250,6 +203,8 @@ const searchTableData = (data: any) => {
         sessionStorage.removeItem('TrackingData')
       })
     }, 100)
+  } else {
+    getTableData()
   }
 }
 
@@ -387,7 +342,6 @@ const handleDownload = () => {
     }
   })
   downloadDialogRef.value.openDialog(
-    props.tagsData,
     curSelectedColumns,
     selectedNumber.value || pageInfo.value.total
   )
@@ -443,7 +397,6 @@ const getExportTableData = (status: number) => {
   $api
     .getAllTrackingTableData({
       selected_fields: column,
-      excel_filter_condition: props.tagsData.join(','),
       tmp_search: tempSearch.value
     })
     .then((res: any) => {
@@ -569,8 +522,6 @@ const handleVGM = (row) => {
   })
 }
 
-const loadingState = useLoadingState()
-
 // 表格右键点击事件
 const handleCurrentRow = (params) => {
   tableRef.value?.setCurrentRow(params.row)
@@ -609,9 +560,12 @@ const SubscribeShipments = (row: any) => {
     })
 }
 
+onUnmounted(() => {
+  headerSearchStore.clearSearchData()
+})
+
 defineExpose({
   searchTableData,
-  getSharedTableData,
   getLoadingData,
   pageInfo
 })
@@ -676,7 +630,7 @@ defineExpose({
 
     <vxe-grid
       ref="tableRef"
-      v-vloading="tableLoadingColumn || tableLoadingTable || loadingState.trackingTableLoading"
+      v-vloading="tableLoadingColumn || tableLoadingTable"
       :height="props.height"
       :style="{ border: 'none' }"
       :row-class-name="handleRowClassName"

+ 23 - 5
src/views/Tracking/src/components/TrackingTable/src/components/DownloadDialog.vue

@@ -1,8 +1,11 @@
 <script setup lang="ts">
+import { useFiltersStore } from '@/stores/modules/filtersList'
+
+const filtersStore = useFiltersStore()
+
 const dialogVisible = ref(false)
 
-const openDialog = (tagsData: string[], selectedColumns: string[], slectedDataNumber: number) => {
-  listData.value = tagsData
+const openDialog = (selectedColumns: string[], slectedDataNumber: number) => {
   selectedDataNumber.value = slectedDataNumber
   columns.value = selectedColumns
   dialogVisible.value = true
@@ -13,8 +16,6 @@ const isShowSelectColumn = ref(false)
 const downloadFilter = ref(1)
 const selectedDataNumber = ref(0)
 
-const listData = ref()
-
 const columns = ref()
 
 const emits = defineEmits<{ export: [number] }>()
@@ -44,7 +45,19 @@ defineExpose({
           </div>
         </div>
         <div class="data-filter">
-          <div class="filter-item" v-for="item in listData" :key="item">{{ item }}</div>
+          <div class="filter-item" v-for="item in filtersStore.getTagsList" :key="item.title">
+            <el-tooltip
+              class="box-item"
+              effect="dark"
+              :content="item.value"
+              placement="top"
+              :popper-style="{ maxWidth: '500px', whiteSpace: 'normal', wordWrap: 'break-word' }"
+              v-if="item.value.length > 39"
+            >
+              {{ item.value.length > 39 ? item.value.slice(0, 39) + '...' : item.value }}
+            </el-tooltip>
+            <div v-else>{{ item.value }}</div>
+          </div>
         </div>
         <div class="download-filter">
           <el-radio-group v-model="downloadFilter">
@@ -129,6 +142,11 @@ defineExpose({
     }
 
     .column-number {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      min-width: 18px;
+      height: 18px;
       padding: 3px 5px;
       background-color: var(--color-theme);
       border-radius: 12px;

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно