Przeglądaj źródła

feat: 修改report其他反馈的bug

Jack Zhou 1 miesiąc temu
rodzic
commit
cb2c721e9e

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

@@ -18,6 +18,7 @@ export const getFilterPartyID = (params: any, config: any) => {
   )
 }
 
+
 /**
  * 获取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页面数据
  */

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

@@ -18,92 +18,92 @@ const menuList = ref()
 //   }
 // )
 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()
 //监听窗口大小

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

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

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

@@ -2,9 +2,10 @@
 import { useRouter, useRoute } from 'vue-router'
 import partyIDSelect from './components/partyIDSelect.vue'
 import GroupNameSelect from './components/GroupNameSelect.vue'
+import AccountSelect from './components/AccountSelect.vue'
 import { VueDraggable } from 'vue-draggable-plus'
 import AdjustmentField from './components/AdjustmentField.vue'
-import { cloneDeep, uniq, uniqueId } from 'lodash'
+import { cloneDeep } from 'lodash'
 
 const router = useRouter()
 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)
+
+  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 curField = cloneDeep(fieldsList.value[index])
   curField.displayName = `${curField.displayName} (Copy)`
   curField.isFilter = false
   curField.isSort = false
   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()
@@ -130,11 +143,20 @@ const handleCustomizeColumns = () => {
     serial_no: '',
     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(
     params,
     -220,
     '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]
+
+  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')
@@ -206,7 +246,7 @@ const detailRef: Ref<HTMLElement | null> = ref(null)
 const specificRoles = ref({
   partyId: [],
   groupName: [],
-  systemAccount: ''
+  systemAccount: []
 })
 const changePartyId = (val: string[]) => {
   specificRoles.value.partyId = val
@@ -215,6 +255,10 @@ const changeGroupName = (val: string[]) => {
   specificRoles.value.groupName = val
 }
 
+const changeAccount = (val: string[]) => {
+  specificRoles.value.systemAccount = val
+}
+
 const fieldLoading = ref(false)
 const handleRightRemove = () => {}
 
@@ -238,9 +282,9 @@ const handlePageSave = () => {
   }
   if (
     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.')
     verified = false
@@ -248,6 +292,7 @@ const handlePageSave = () => {
   if (!verified) {
     return
   }
+  pageLoading.value = true
   const data = {
     report_name: infoData.value.reportName,
     report_level: infoData.value.reportLevel,
@@ -255,21 +300,26 @@ const handlePageSave = () => {
     access_type: accessControlType.value,
     party_ids: specificRoles.value.partyId || [],
     group_names: specificRoles.value.groupName || [],
-    system_account: specificRoles.value.systemAccount,
+    system_account: specificRoles.value.systemAccount || [],
     fieldsList: fieldsList.value
   }
   let serial_no = ''
   if (route.query.copy !== 't' && 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>
 <template>
@@ -416,7 +466,7 @@ const handlePageSave = () => {
                     class="font_family icon-icon_clone_b"
                   ></span>
                   <span
-                    @click="handleDeleteField(index)"
+                    @click="handleDeleteField(index, fieldItem.uniqueId)"
                     class="font_family icon-icon_delete_b"
                   ></span>
                 </div>
@@ -477,10 +527,10 @@ const handlePageSave = () => {
                         <span style="color: var(--color-danger)">*</span>
                         <span>KLN ONLINE Account</span>
                       </div>
-                      <el-input
-                        placeholder="Please enter KLN ONLINE Account"
-                        v-model="specificRoles.systemAccount"
-                      />
+                      <AccountSelect
+                        @change-data="changeAccount"
+                        :data="specificRoles.systemAccount"
+                      ></AccountSelect>
                     </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">
         <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>
       </div>
     </el-option>
@@ -133,6 +133,11 @@ onUnmounted(() => {
   flex: 1;
   display: flex;
 }
+.text-ellipsis {
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow: hidden;
+}
 </style>
 
 <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 { useRowClickStyle } from '@/hooks/rowClickStyle'
 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 { useUserStore } from '@/stores/modules/user'
 
+dayjs.extend(utc)
+dayjs.extend(timezone)
 const router = useRouter()
 const props = defineProps({
   height: {
@@ -14,6 +19,11 @@ const props = defineProps({
   }
 })
 
+const userStore = useUserStore()
+const formatString = computed(() => {
+  return userStore.dateFormat || 'MM/DD/YYYY'
+})
+
 const tableOriginColumnsField = ref()
 const tableColumns = [
   {
@@ -77,7 +87,8 @@ const handleColumns = (columns: any) => {
     if (item.formatter === 'date' || item.formatter === 'dateTime') {
       curColumn = {
         ...curColumn,
-        formatter: ({ cellValue }: any) => formatTimezone(cellValue)
+        formatter: ({ cellValue }: any) =>
+          dayjs.tz(cellValue, 'US/Pacific').format(formatString.value + ' HH:mm:ss ')
       }
     }
     return curColumn