فهرست منبع

feat: 修改report其他反馈的bug

Jack Zhou 1 ماه پیش
والد
کامیت
cb2c721e9e

+ 18 - 0
src/api/module/report.ts

@@ -18,6 +18,7 @@ export const getFilterPartyID = (params: any, config: any) => {
   )
   )
 }
 }
 
 
+
 /**
 /**
  * 获取report template management表格列数据
  * 获取report template management表格列数据
  */
  */
@@ -97,6 +98,23 @@ export const getSpecificRolesGroupName = (params: any, config: any) => {
 }
 }
 
 
 
 
+/**
+ * Create New Report Template页面获取Specific Roles的account 数据
+ */
+export const getSpecificRolesAccount = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'ajax',
+      operate: 'autody_extend',
+      type: 'system_account',
+      ...params
+    },
+    config
+  )
+}
+
+
 /**
 /**
  * 保存 Create New Report Template页面数据
  * 保存 Create New Report Template页面数据
  */
  */

+ 85 - 85
src/views/Layout/src/components/Menu/MenuView.vue

@@ -18,92 +18,92 @@ const menuList = ref()
 //   }
 //   }
 // )
 // )
 const getMenuList = () => {
 const getMenuList = () => {
-  // $api.getMenuList().then((res) => {
-  //   if (res.code === 200) {
-  //     menuList.value = res.data
-  //   }
-  // })
-  menuList.value = [
-    {
-      index: '1',
-      label: 'Dashboard',
-      icon: 'icon_data_fill_b',
-      path: '/dashboard'
-    },
-    {
-      index: '2',
-      label: 'Booking',
-      icon: 'icon_booking__fill_b',
-      type: 'list',
-      children: [
-        {
-          index: '2-1',
-          label: 'Booking Management',
-          path: '/booking'
-        },
-        {
-          index: '2-2',
-          label: 'Destination Delivery',
-          path: '/destination-delivery'
-        }
-      ]
-    },
-    {
-      index: '3',
-      label: 'Tracking',
-      icon: 'icon_tracking__fill_b',
-      path: '/tracking'
-    },
-    {
-      index: '4',
-      label: 'Report',
-      icon: 'icon_report__fill_b',
-      path: '/report'
-    },
-    {
-      index: '5',
-      label: 'System Management',
-      icon: 'icon_system__management_fill_b',
-      type: 'list',
-      children: [
-        {
-          index: '5-7',
-          label: 'Template Management',
-          path: '/template-management'
-        },
-        {
-          index: '5-1',
-          label: 'System Message',
-          path: '/system-message'
-        },
-        {
-          index: '5-2',
-          label: 'System Settings',
-          path: '/SystemSettings'
-        },
-        {
-          index: '5-3',
-          label: 'Chat Log',
-          path: '/chat-log'
-        },
-        {
-          index: '5-4',
-          label: 'AI API Log',
-          path: '/ai-api-log'
-        },
-        {
-          index: '5-5',
-          label: 'Operation Log',
-          path: '/Operationlog'
-        },
-        {
-          index: '5-6',
-          label: 'Prompt Configuration',
-          path: '/PromptConfiguration'
-        }
-      ]
+  $api.getMenuList().then((res) => {
+    if (res.code === 200) {
+      menuList.value = res.data
     }
     }
-  ]
+  })
+  // menuList.value = [
+  //   {
+  //     index: '1',
+  //     label: 'Dashboard',
+  //     icon: 'icon_data_fill_b',
+  //     path: '/dashboard'
+  //   },
+  //   {
+  //     index: '2',
+  //     label: 'Booking',
+  //     icon: 'icon_booking__fill_b',
+  //     type: 'list',
+  //     children: [
+  //       {
+  //         index: '2-1',
+  //         label: 'Booking Management',
+  //         path: '/booking'
+  //       },
+  //       {
+  //         index: '2-2',
+  //         label: 'Destination Delivery',
+  //         path: '/destination-delivery'
+  //       }
+  //     ]
+  //   },
+  //   {
+  //     index: '3',
+  //     label: 'Tracking',
+  //     icon: 'icon_tracking__fill_b',
+  //     path: '/tracking'
+  //   },
+  //   {
+  //     index: '4',
+  //     label: 'Report',
+  //     icon: 'icon_report__fill_b',
+  //     path: '/report'
+  //   },
+  //   {
+  //     index: '5',
+  //     label: 'System Management',
+  //     icon: 'icon_system__management_fill_b',
+  //     type: 'list',
+  //     children: [
+  //       {
+  //         index: '5-7',
+  //         label: 'Template Management',
+  //         path: '/template-management'
+  //       },
+  //       {
+  //         index: '5-1',
+  //         label: 'System Message',
+  //         path: '/system-message'
+  //       },
+  //       {
+  //         index: '5-2',
+  //         label: 'System Settings',
+  //         path: '/SystemSettings'
+  //       },
+  //       {
+  //         index: '5-3',
+  //         label: 'Chat Log',
+  //         path: '/chat-log'
+  //       },
+  //       {
+  //         index: '5-4',
+  //         label: 'AI API Log',
+  //         path: '/ai-api-log'
+  //       },
+  //       {
+  //         index: '5-5',
+  //         label: 'Operation Log',
+  //         path: '/Operationlog'
+  //       },
+  //       {
+  //         index: '5-6',
+  //         label: 'Prompt Configuration',
+  //         path: '/PromptConfiguration'
+  //       }
+  //     ]
+  //   }
+  // ]
 }
 }
 getMenuList()
 getMenuList()
 //监听窗口大小
 //监听窗口大小

+ 4 - 8
src/views/Report/src/components/ReportSchedule/src/components/TimeRange.vue

@@ -9,8 +9,8 @@ const endDate = ref('')
 const rollingValueStart = ref(0)
 const rollingValueStart = ref(0)
 const rollingValueEnd = ref(0)
 const rollingValueEnd = ref(0)
 const type = ref('')
 const type = ref('')
-const fixedRangeRadio = ref('')
-const rollingRangeRadio = ref('')
+const fixedRangeRadio = ref('Customize')
+const rollingRangeRadio = ref('Customize')
 const dataTimeoptions = ref([
 const dataTimeoptions = ref([
   {
   {
     label: 'ETD',
     label: 'ETD',
@@ -92,6 +92,7 @@ const changeTime = (val: any) => {
     startDate.value = ''
     startDate.value = ''
     endDate.value = ''
     endDate.value = ''
 
 
+    rollingRangeRadio.value = 'Customize'
     emits('changeTimeRange', {
     emits('changeTimeRange', {
       startDate: clampedValueStart.value,
       startDate: clampedValueStart.value,
       endDate: clampedValueEnd.value,
       endDate: clampedValueEnd.value,
@@ -102,6 +103,7 @@ const changeTime = (val: any) => {
     clampedValueStart.value = 0
     clampedValueStart.value = 0
     clampedValueEnd.value = 0
     clampedValueEnd.value = 0
 
 
+    fixedRangeRadio.value = 'Customize'
     emits('changeTimeRange', {
     emits('changeTimeRange', {
       startDate: startDate.value,
       startDate: startDate.value,
       endDate: endDate.value,
       endDate: endDate.value,
@@ -135,9 +137,6 @@ const ChangeRollingRangeRadio = (val: any) => {
     fieldType: fieldType.value,
     fieldType: fieldType.value,
     type: type.value
     type: type.value
   })
   })
-  setTimeout(() => {
-    rollingRangeRadio.value = ''
-  }, 300)
 }
 }
 
 
 // 选择fixed range默认值
 // 选择fixed range默认值
@@ -179,9 +178,6 @@ const changeFixedRange = (val: any) => {
     fieldType: fieldType.value,
     fieldType: fieldType.value,
     type: type.value
     type: type.value
   })
   })
-  setTimeout(() => {
-    fixedRangeRadio.value = ''
-  }, 300)
 }
 }
 
 
 const getData = () => {
 const getData = () => {

+ 72 - 22
src/views/TemplateManagement/src/components/CreateReportTemplate/src/CreateReportTemplate.vue

@@ -2,9 +2,10 @@
 import { useRouter, useRoute } from 'vue-router'
 import { useRouter, useRoute } from 'vue-router'
 import partyIDSelect from './components/partyIDSelect.vue'
 import partyIDSelect from './components/partyIDSelect.vue'
 import GroupNameSelect from './components/GroupNameSelect.vue'
 import GroupNameSelect from './components/GroupNameSelect.vue'
+import AccountSelect from './components/AccountSelect.vue'
 import { VueDraggable } from 'vue-draggable-plus'
 import { VueDraggable } from 'vue-draggable-plus'
 import AdjustmentField from './components/AdjustmentField.vue'
 import AdjustmentField from './components/AdjustmentField.vue'
-import { cloneDeep, uniq, uniqueId } from 'lodash'
+import { cloneDeep } from 'lodash'
 
 
 const router = useRouter()
 const router = useRouter()
 const route = useRoute()
 const route = useRoute()
@@ -107,16 +108,28 @@ const changeFieldConfig = (state: any, field: string, index: number, key: string
   }
   }
 }
 }
 
 
-const handleDeleteField = (index: number) => {
+const handleDeleteField = (index: number, uniqueId: string) => {
   fieldsList.value.splice(index, 1)
   fieldsList.value.splice(index, 1)
+
+  Object.keys(copyFieldsList.value).forEach((key) => {
+    copyFieldsList.value[key] = copyFieldsList.value[key].filter(
+      (item) => item.uniqueId !== uniqueId
+    )
+  })
 }
 }
+const copyFieldsList = ref({})
 const handleCopyField = (index: number) => {
 const handleCopyField = (index: number) => {
   const curField = cloneDeep(fieldsList.value[index])
   const curField = cloneDeep(fieldsList.value[index])
   curField.displayName = `${curField.displayName} (Copy)`
   curField.displayName = `${curField.displayName} (Copy)`
   curField.isFilter = false
   curField.isFilter = false
   curField.isSort = false
   curField.isSort = false
   curField.uniqueId = generate8DigitUnique()
   curField.uniqueId = generate8DigitUnique()
-  fieldsList.value.splice(index + 1, 0, curField)
+  if (copyFieldsList.value[curField.field]) {
+    copyFieldsList.value[curField.field].push(cloneDeep(curField))
+  } else {
+    copyFieldsList.value[curField.field] = [cloneDeep(curField)]
+  }
+  fieldsList.value.splice(index + 1, 0, cloneDeep(curField))
 }
 }
 
 
 const AdjustmentFieldRef = ref()
 const AdjustmentFieldRef = ref()
@@ -130,11 +143,20 @@ const handleCustomizeColumns = () => {
     serial_no: '',
     serial_no: '',
     level: infoData.value.reportLevel
     level: infoData.value.reportLevel
   }
   }
+  const seen = new Set()
+  const uniqueArray = fieldsList.value.filter((item) => {
+    if (seen.has(item.field)) {
+      return false // 已存在,跳过
+    }
+    seen.add(item.field)
+    return true // 第一次出现,保留
+  })
+
   AdjustmentFieldRef.value.openDialog(
   AdjustmentFieldRef.value.openDialog(
     params,
     params,
     -220,
     -220,
     'Drag item over to this selection or click "add" icon to show the field on report template list',
     'Drag item over to this selection or click "add" icon to show the field on report template list',
-    fieldsList.value
+    uniqueArray
   )
   )
 }
 }
 
 
@@ -198,6 +220,24 @@ const handleApplay = (data: any) => {
     }
     }
   })
   })
   fieldsList.value = [...customizeData, ...fieldsList.value]
   fieldsList.value = [...customizeData, ...fieldsList.value]
+
+  const validFields = new Set(fieldsList.value.map((item) => item.field))
+  for (const field in copyFieldsList.value) {
+    if (!validFields.has(field)) {
+      delete copyFieldsList.value[field]
+    }
+  }
+
+  // === 第三步:从后往前插入副本(不修改原始项)===
+  for (let i = fieldsList.value.length - 1; i >= 0; i--) {
+    const field = fieldsList.value[i].field
+    const copies = copyFieldsList.value[field]
+
+    if (copies?.length) {
+      // 插入副本到原项后面
+      fieldsList.value.splice(i + 1, 0, ...copies)
+    }
+  }
 }
 }
 
 
 const accessControlType = ref('All Users')
 const accessControlType = ref('All Users')
@@ -206,7 +246,7 @@ const detailRef: Ref<HTMLElement | null> = ref(null)
 const specificRoles = ref({
 const specificRoles = ref({
   partyId: [],
   partyId: [],
   groupName: [],
   groupName: [],
-  systemAccount: ''
+  systemAccount: []
 })
 })
 const changePartyId = (val: string[]) => {
 const changePartyId = (val: string[]) => {
   specificRoles.value.partyId = val
   specificRoles.value.partyId = val
@@ -215,6 +255,10 @@ const changeGroupName = (val: string[]) => {
   specificRoles.value.groupName = val
   specificRoles.value.groupName = val
 }
 }
 
 
+const changeAccount = (val: string[]) => {
+  specificRoles.value.systemAccount = val
+}
+
 const fieldLoading = ref(false)
 const fieldLoading = ref(false)
 const handleRightRemove = () => {}
 const handleRightRemove = () => {}
 
 
@@ -238,9 +282,9 @@ const handlePageSave = () => {
   }
   }
   if (
   if (
     accessControlType.value === 'Specific Roles' &&
     accessControlType.value === 'Specific Roles' &&
-    specificRoles.value.partyId.length === 0 &&
-    specificRoles.value.groupName.length === 0 &&
-    specificRoles.value.systemAccount
+    specificRoles.value.partyId?.length === 0 &&
+    specificRoles.value.groupName?.length === 0 &&
+    specificRoles.value.systemAccount?.length === 0
   ) {
   ) {
     ElMessage.warning('Please select Party ID or Group Name for Specific Roles access control.')
     ElMessage.warning('Please select Party ID or Group Name for Specific Roles access control.')
     verified = false
     verified = false
@@ -248,6 +292,7 @@ const handlePageSave = () => {
   if (!verified) {
   if (!verified) {
     return
     return
   }
   }
+  pageLoading.value = true
   const data = {
   const data = {
     report_name: infoData.value.reportName,
     report_name: infoData.value.reportName,
     report_level: infoData.value.reportLevel,
     report_level: infoData.value.reportLevel,
@@ -255,21 +300,26 @@ const handlePageSave = () => {
     access_type: accessControlType.value,
     access_type: accessControlType.value,
     party_ids: specificRoles.value.partyId || [],
     party_ids: specificRoles.value.partyId || [],
     group_names: specificRoles.value.groupName || [],
     group_names: specificRoles.value.groupName || [],
-    system_account: specificRoles.value.systemAccount,
+    system_account: specificRoles.value.systemAccount || [],
     fieldsList: fieldsList.value
     fieldsList: fieldsList.value
   }
   }
   let serial_no = ''
   let serial_no = ''
   if (route.query.copy !== 't' && route.query.serial_no) {
   if (route.query.copy !== 't' && route.query.serial_no) {
     serial_no = String(route.query.serial_no)
     serial_no = String(route.query.serial_no)
   }
   }
-  $api.saveNewReportTemplate({ ...data, serial_no }).then((res: any) => {
-    if (res.code === 200) {
-      ElMessage.success('Report Template saved successfully!')
-      router.push('/template-management')
-    } else {
-      ElMessage.error(res.data.msg || 'Failed to save Report Template.')
-    }
-  })
+  $api
+    .saveNewReportTemplate({ ...data, serial_no })
+    .then((res: any) => {
+      if (res.code === 200) {
+        ElMessage.success('Report Template saved successfully!')
+        router.push('/template-management')
+      } else {
+        ElMessage.error(res.data.msg || 'Failed to save Report Template.')
+      }
+    })
+    .finally(() => {
+      pageLoading.value = false
+    })
 }
 }
 </script>
 </script>
 <template>
 <template>
@@ -416,7 +466,7 @@ const handlePageSave = () => {
                     class="font_family icon-icon_clone_b"
                     class="font_family icon-icon_clone_b"
                   ></span>
                   ></span>
                   <span
                   <span
-                    @click="handleDeleteField(index)"
+                    @click="handleDeleteField(index, fieldItem.uniqueId)"
                     class="font_family icon-icon_delete_b"
                     class="font_family icon-icon_delete_b"
                   ></span>
                   ></span>
                 </div>
                 </div>
@@ -477,10 +527,10 @@ const handlePageSave = () => {
                         <span style="color: var(--color-danger)">*</span>
                         <span style="color: var(--color-danger)">*</span>
                         <span>KLN ONLINE Account</span>
                         <span>KLN ONLINE Account</span>
                       </div>
                       </div>
-                      <el-input
-                        placeholder="Please enter KLN ONLINE Account"
-                        v-model="specificRoles.systemAccount"
-                      />
+                      <AccountSelect
+                        @change-data="changeAccount"
+                        :data="specificRoles.systemAccount"
+                      ></AccountSelect>
                     </div>
                     </div>
                   </div>
                   </div>
                 </div>
                 </div>

+ 143 - 0
src/views/TemplateManagement/src/components/CreateReportTemplate/src/components/AccountSelect.vue

@@ -0,0 +1,143 @@
+<script setup lang="ts">
+import { cloneDeep, debounce } from 'lodash'
+import axios from 'axios'
+
+const props = defineProps({
+  data: {
+    type: Array as () => string[],
+    default: () => []
+  }
+})
+
+const selectData = ref<string[]>([])
+watch(
+  () => props.data,
+  (newValue) => {
+    selectData.value = cloneDeep(newValue) || []
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+const emit = defineEmits(['changeData'])
+const changeData = (val: string[]) => {
+  // 同步选中状态
+  emit('changeData', val)
+}
+
+interface ListItem {
+  label: string
+  id: string
+}
+
+const options = ref<ListItem[]>([])
+const loading = ref(false)
+const currentController = ref<AbortController | null>(null)
+
+const remoteMethod = (query: string) => {
+  currentController.value?.abort()
+
+  const newController = new AbortController()
+  currentController.value = newController
+  loading.value = true
+
+  $api
+    .getSpecificRolesAccount({ 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()
+})
+
+// 首次聚焦或输入时加载(可选:如果希望空搜也加载)
+// 但通常 remote 场景是“输入才搜”,所以这里只在 filter 时调用
+</script>
+
+<template>
+  <el-select
+    :model-value="selectData"
+    multiple
+    filterable
+    reserve-keyword
+    placeholder="Select Party IDs (Multi-select allowed)"
+    :loading="loading"
+    style="width: 100%"
+    popper-class="part-id-select-popper"
+    :filter-method="debouncedRemoteMethod"
+    @change="changeData"
+  >
+    <el-option v-for="item in options" :key="item.id" :label="item.label" :value="item.label">
+      <div class="select-option">
+        <el-checkbox :model-value="selectData.includes(item.label)" style="flex: 1">
+          <span style="display: inline-block; width: 220px">{{ item.label }}</span>
+        </el-checkbox>
+      </div>
+    </el-option>
+  </el-select>
+</template>
+
+<style lang="scss" scoped>
+.select-option {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  width: 100%;
+  & > span {
+    margin-left: 56px;
+    line-height: 16px;
+  }
+}
+
+:deep(.el-checkbox__inner) {
+  height: 16px;
+  width: 16px;
+  &::after {
+    border-width: 2px;
+    height: 9px;
+    width: 5px;
+    left: 4px;
+    top: 0;
+  }
+}
+:deep(.el-checkbox__label) {
+  flex: 1;
+  display: flex;
+}
+.text-ellipsis {
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow: hidden;
+}
+</style>
+
+<style lang="scss">
+.part-id-select-popper {
+  // width: 100% !important;
+  // width: auto;
+  // min-width: unset !important;
+}
+</style>

+ 7 - 2
src/views/TemplateManagement/src/components/CreateReportTemplate/src/components/PartyIDSelect.vue

@@ -98,8 +98,8 @@ onUnmounted(() => {
     >
     >
       <div class="select-option">
       <div class="select-option">
         <el-checkbox :model-value="selectData.includes(item.label)" style="flex: 1">
         <el-checkbox :model-value="selectData.includes(item.label)" style="flex: 1">
-          <span style="flex: 1; display: inline-block; width: 320px">{{ item.label }}</span>
-          <span style="width: 200px">{{ item.id }}</span>
+          <span style="display: inline-block; width: 220px">{{ item.label }}</span>
+          <span class="text-ellipsis" style="flex: 1; width: 200px">{{ item.id }}</span>
         </el-checkbox>
         </el-checkbox>
       </div>
       </div>
     </el-option>
     </el-option>
@@ -133,6 +133,11 @@ onUnmounted(() => {
   flex: 1;
   flex: 1;
   display: flex;
   display: flex;
 }
 }
+.text-ellipsis {
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow: hidden;
+}
 </style>
 </style>
 
 
 <style lang="scss">
 <style lang="scss">

+ 13 - 2
src/views/TemplateManagement/src/components/TableView/src/TableView.vue

@@ -3,9 +3,14 @@ import { ref, nextTick, onMounted } from 'vue'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import { useRowClickStyle } from '@/hooks/rowClickStyle'
 import { useRowClickStyle } from '@/hooks/rowClickStyle'
 import dayjs from 'dayjs'
 import dayjs from 'dayjs'
-import { formatTimezone, formatNumber } from '@/utils/tools'
+import utc from 'dayjs/plugin/utc'
+import timezone from 'dayjs/plugin/timezone'
+import { formatNumber } from '@/utils/tools'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
+import { useUserStore } from '@/stores/modules/user'
 
 
+dayjs.extend(utc)
+dayjs.extend(timezone)
 const router = useRouter()
 const router = useRouter()
 const props = defineProps({
 const props = defineProps({
   height: {
   height: {
@@ -14,6 +19,11 @@ const props = defineProps({
   }
   }
 })
 })
 
 
+const userStore = useUserStore()
+const formatString = computed(() => {
+  return userStore.dateFormat || 'MM/DD/YYYY'
+})
+
 const tableOriginColumnsField = ref()
 const tableOriginColumnsField = ref()
 const tableColumns = [
 const tableColumns = [
   {
   {
@@ -77,7 +87,8 @@ const handleColumns = (columns: any) => {
     if (item.formatter === 'date' || item.formatter === 'dateTime') {
     if (item.formatter === 'date' || item.formatter === 'dateTime') {
       curColumn = {
       curColumn = {
         ...curColumn,
         ...curColumn,
-        formatter: ({ cellValue }: any) => formatTimezone(cellValue)
+        formatter: ({ cellValue }: any) =>
+          dayjs.tz(cellValue, 'US/Pacific').format(formatString.value + ' HH:mm:ss ')
       }
       }
     }
     }
     return curColumn
     return curColumn