ソースを参照

feat:添加report 页面

AmandaG 3 週間 前
コミット
e859969597

+ 35 - 0
src/styles/Antdui.scss

@@ -209,4 +209,39 @@ tr
 }
 .ant-checkbox-checked:after{
   border-color: var(--color-theme) !important;
+}
+.ant-btn-primary {
+  background-color: var(--color-theme) !important;
+  span {
+    color: var(--color-white);
+  }
+  &:hover {
+    background-color: var(--color-btn-main-bg-hover);
+    color: var(--color-white);
+  }
+}
+.ant-picker-dropdown .ant-picker-time-panel-column >li.ant-picker-time-panel-cell-selected .ant-picker-time-panel-cell-inner {
+  background-color: var(--color-orange-6);
+  color: var(--color-theme);
+  &:hover {
+    background-color: var(--color-orange-6);
+  }
+}
+.ant-picker-dropdown .ant-picker-time-panel-column >li.ant-picker-time-panel-cell .ant-picker-time-panel-cell-inner {
+  &:hover {
+    background-color: var(--color-orange-6);
+  }
+}
+.ant-select:not(.ant-select-disabled):not(.ant-select-customize-input):not(.ant-pagination-size-changer):hover .ant-select-selector {
+  border-color: var(--color-theme);
+}
+.ant-select-dropdown .ant-select-item-option-active:not(.ant-select-item-option-disabled) {
+  background-color: var(--color-orange-6);
+}
+.ant-select-dropdown .ant-select-item-option-selected:not(.ant-select-item-option-disabled) {
+  background-color: var(--color-orange-6);
+  div {
+    color: var(--color-theme);
+    font-weight: 400;
+  }
 }

+ 1 - 0
src/styles/theme-g.scss

@@ -99,4 +99,5 @@
 
   // report
   --color-schedule-bg: #343A43;
+  --color-schedule-details-bg : #343A43;
 }

+ 1 - 0
src/styles/theme.scss

@@ -357,6 +357,7 @@
 
   // report
   --color-schedule-bg: #F6F8FA;
+  --color-schedule-details-bg: #F5F7FA;
 }
 
 :root.dark {

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

@@ -158,7 +158,7 @@ const changeRouter = (path: any) => {
     localStorage.removeItem('loginAI')
     emitter.emit('login-success')
   }
-  if (path == '/PromptConfiguration') {
+  if (path == '/PromptConfiguration' || path == '/report') {
     emitter.emit('checkPrompt')
   } else {
     if (localStorage.getItem('loginAI')) {

+ 39 - 2
src/views/Report/src/components/ReportSchedule/src/ReportSchedule.vue

@@ -1,9 +1,31 @@
 <script setup lang="ts">
 import ValidityPeriod from './components/ValidityPeriod.vue'; 
 import TimeRange from './components/TimeRange.vue'; 
+import EmailConfiguration from './components/EmailConfiguration.vue'; 
+import FieldsTable from './components/FieldsTable.vue'; 
 const isSaveDisabled = computed(() => {
   return true
 })
+const FieldsTableRef = ref()
+
+const customStartDate = ref('')
+const customEndDate = ref('')
+const rollingStartDate = ref('')
+const rollingEndDate = ref('')
+const dataTimeField = ref('')
+// 提交ValidityPeriod组件数据
+const handleSubmitValidity = (data: any) => {
+  customStartDate.value = data.startDate
+  customEndDate.value = data.endDate
+  console.log(data)
+}
+
+// 提交TimeRange组件数据
+const handleSubmitRolling = (data: any) => {
+  dataTimeField.value = data.DataTimeSelection
+  rollingStartDate.value = data.rollingStartDate
+  rollingEndDate.value = data.rollingEndDate
+}
 </script>
 <template>
   <div class="Title">
@@ -17,7 +39,10 @@ const isSaveDisabled = computed(() => {
         Schedule Rule Validity Period
       </div>
       <div class="schedule_container">
-        <ValidityPeriod></ValidityPeriod>
+        <ValidityPeriod
+          @handleSubmitValidity="handleSubmitValidity"
+        >
+        </ValidityPeriod>
       </div>
     </div>
     <div class="schedule_rule" style="margin: 8px 0;">
@@ -25,9 +50,21 @@ const isSaveDisabled = computed(() => {
         Report Data Time Range
       </div>
       <div class="schedule_container">
-        <TimeRange></TimeRange>
+        <TimeRange
+          @handleSubmitRolling="handleSubmitRolling"
+        >
+        </TimeRange>
+      </div>
+    </div>
+    <div class="schedule_rule" style="margin: 8px 0;">
+      <div class="schedule_title">
+        Report Delivery Frequency & Email Configuration
+      </div>
+      <div class="schedule_container">
+        <EmailConfiguration></EmailConfiguration>
       </div>
     </div>
+    <FieldsTable ref="FieldsTableRef"></FieldsTable>
   </div>
 </template>
 

+ 646 - 0
src/views/Report/src/components/ReportSchedule/src/components/EmailConfiguration.vue

@@ -0,0 +1,646 @@
+<script setup lang="ts">
+import { ElMessage } from 'element-plus'
+import type { SelectProps } from 'ant-design-vue';
+
+const emailValue = ref('')
+const TimeZoneValue = ref('')
+const FrequencyValue = ref('')
+const timeValue = ref('')
+const TimeItem = ref<SelectProps['options']>([
+  {
+    value: '00:00',
+    label: '00:00'
+  },
+  {
+    value: '00:30',
+    label: '00:30'
+  },
+  {
+    value: '01:00',
+    label: '01:00'
+  },
+  {
+    value: '01:30',
+    label: '01:30'
+  },
+  {
+    value: '02:00',
+    label: '02:00'
+  },
+  {
+    value: '02:30',
+    label: '02:30'
+  },
+  {
+    value: '03:00',
+    label: '03:00'
+  },
+  {
+    value: '03:30',
+    label: '03:30'
+  },
+  {
+    value: '04:00',
+    label: '04:00'
+  },
+  {
+    value: '04:30',
+    label: '04:30'
+  },
+  {
+    value: '05:00',
+    label: '05:00'
+  },
+  {
+    value: '05:30',
+    label: '05:30'
+  },
+  {
+    value: '06:00',
+    label: '06:00'
+  },
+  {
+    value: '06:30',
+    label: '06:30'
+  },
+  {
+    value: '07:00',
+    label: '07:00'
+  },
+  {
+    value: '07:30',
+    label: '07:30'
+  },
+  {
+    value: '08:00',
+    label: '08:00'
+  },
+  {
+    value: '08:30',
+    label: '08:30'
+  },
+  {
+    value: '09:00',
+    label: '09:00'
+  },
+  {
+    value: '09:30',
+    label: '09:30'
+  },
+  {
+    value: '10:00',
+    label: '10:00'
+  },
+  {
+    value: '10:30',
+    label: '10:30'
+  },
+  {
+    value: '11:00',
+    label: '11:00'
+  },
+  {
+    value: '11:30',
+    label: '11:30'
+  },
+  {
+    value: '12:00',
+    label: '12:00'
+  },
+  {
+    value: '12:30',
+    label: '12:30'
+  },
+  {
+    value: '13:00',
+    label: '13:00'
+  },
+  {
+    value: '13:30',
+    label: '13:30'
+  },
+  {
+    value: '14:00',
+    label: '14:00'
+  },
+  {
+    value: '14:30',
+    label: '14:30'
+  },
+  {
+    value: '15:00',
+    label: '15:00'
+  },
+  {
+    value: '15:30',
+    label: '15:30'
+  },
+  {
+    value: '16:00',
+    label: '16:00'
+  },
+  {
+    value: '16:30',
+    label: '16:30'
+  },
+  {
+    value: '17:00',
+    label: '17:00'
+  },
+  {
+    value: '17:30',
+    label: '17:30'
+  },
+  {
+    value: '18:00',
+    label: '18:00'
+  },
+  {
+    value: '18:30',
+    label: '18:30'
+  },
+  {
+    value: '19:00',
+    label: '19:00'
+  },
+  {
+    value: '19:30',
+    label: '19:30'
+  },
+  {
+    value: '20:00',
+    label: '20:00'
+  },
+  {
+    value: '20:30',
+    label: '20:30'
+  },
+  {
+    value: '21:00',
+    label: '21:00'
+  },
+  {
+    value: '21:30',
+    label: '21:30'
+  },
+  {
+    value: '22:00',
+    label: '22:00'
+  },
+  {
+    value: '22:30',
+    label: '22:30'
+  },
+  {
+    value: '23:00',
+    label: '23:00'
+  },
+  {
+    value: '23:30',
+    label: '23:30'
+  }
+])
+const Timezoneoptions = ref(
+  [
+    {
+      label: 'UTC-08',
+      value: 'UTC-08:00'
+    },
+    {
+      label: 'UTC-07',
+      value: 'UTC-07:00'
+    },
+    {
+      label: 'UTC-06',
+      value: 'UTC-06:00'
+    },
+    {
+      label: 'UTC-05',
+      value: 'UTC-05:00'
+    },
+    {
+      label: 'UTC-04',
+      value: 'UTC-04:00'
+    },
+    {
+      label: 'UTC-03',
+      value: 'UTC-03:00'
+    },
+    {
+      label: 'UTC-02',
+      value: 'UTC-02:00'
+    },
+    {
+      label: 'UTC-01',
+      value: 'UTC-01:00'
+    },
+    {
+      label: 'UTC-00',
+      value: 'UTC-00:00'
+    },
+    {
+      label: 'UTC+01',
+      value: 'UTC+01:00'
+    },
+    {
+      label: 'UTC+02',
+      value: 'UTC+02:00'
+    },
+    {
+      label: 'UTC+03',
+      value: 'UTC+03:00'
+    },
+    {
+      label: 'UTC+04',
+      value: 'UTC+04:00'
+    },
+    {
+      label: 'UTC+05',
+      value: 'UTC+05:00'
+    },
+    {
+      label: 'UTC+05:30',
+      value: 'UTC+05:30'
+    },
+    {
+      label: 'UTC+06',
+      value: 'UTC+06:00'
+    },
+    {
+      label: 'UTC+07',
+      value: 'UTC+07:00'
+    },
+    {
+      label: 'UTC+08',
+      value: 'UTC+08:00'
+    },
+    {
+      label: 'UTC+09',
+      value: 'UTC+09:00'
+    },
+    {
+      label: 'UTC+10',
+      value: 'UTC+10:00'
+    },
+    {
+      label: 'UTC+11',
+      value: 'UTC+11:00'
+    },
+    {
+      label: 'UTC+12',
+      value: 'UTC+12:00'
+    }
+  ]
+)
+const Frequencyoptions = ref(
+  [
+    {
+      value: 'Daily',
+      label: 'Daily'
+    },
+    {
+      value: 'Weekly',
+      label: 'Weekly'
+    },
+    {
+      value: 'Monthly',
+      label: 'Monthly'
+    },
+    {
+      value: 'Quarterly',
+      label: 'Quarterly'
+    },
+    {
+      value: 'Yearly',
+      label: 'Yearly'
+    }
+  ]
+)
+
+const weeklyCheckList = ref(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'])
+const maxWeeklySelected = computed(() => weeklyCheckList.value.length - 1)
+const weeklyChecked = ref([])
+
+const MonthlyCheckList = computed(() => {
+  const now = new Date()
+  const year = now.getFullYear()
+  const month = now.getMonth()
+  const lastDay = new Date(year, month + 1, 0).getDate()
+  const daysArray: string[] = []
+  for (let i = 1; i <= lastDay; i++) {
+    daysArray.push(i.toString())
+  }
+  return daysArray
+})
+const maxMonthlySelected = computed(() => MonthlyCheckList.value.length - 1)
+const monthlyChecked = ref([])
+
+const isFebruary = computed(() => MonthlyCheckList.value.length <= 28)
+const is29 = computed(() => MonthlyCheckList.value.length == 29)
+
+const YearlyCheckList = ref(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'])
+const dayList = ref(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', 'Last Day'])
+const maxYearlySelected = computed(() => YearlyCheckList.value.length - 1)
+const yearlyChecked = ref([])
+const YearlyDayValue = ref('')
+
+const QuarterMonth = ref(['1st Month', '2nd Month', '3rd Month'])
+const QuarterMonthValue = ref('')
+
+const showWarning = () => {
+  ElMessage({
+    message: 'If you want to select all, please choose the Daily option!',
+    type: 'warning'
+  })
+}
+const handleChangeCheckbox = (value: any) => {
+  if(value == 'weekly') {
+    if(weeklyChecked.value.length == maxWeeklySelected.value) {
+      showWarning()
+    }
+  } else if(value == 'monthly') { 
+    if(monthlyChecked.value.length == maxMonthlySelected.value) {
+      showWarning()
+    }
+  } else if(value == 'yearly') { 
+    if(yearlyChecked.value.length == maxYearlySelected.value) {
+      showWarning()
+    }
+  }
+}
+
+// 切换frequency时,清空内容
+const handleClickFrequency = (value: string) => {
+  if(value == 'Daily') {
+    weeklyChecked.value = []
+    monthlyChecked.value = []
+    yearlyChecked.value = []
+    QuarterMonthValue.value = ''
+    YearlyDayValue.value = ''
+  } else if(value == 'Weekly') { 
+    monthlyChecked.value = []
+    yearlyChecked.value = []
+    QuarterMonthValue.value = ''
+    YearlyDayValue.value = ''
+  } else if(value == 'Monthly') { 
+    weeklyChecked.value = []
+    yearlyChecked.value = []
+    QuarterMonthValue.value = ''
+    YearlyDayValue.value = ''
+  } else if(value == 'Quarterly') { 
+    weeklyChecked.value = []
+    monthlyChecked.value = []
+    yearlyChecked.value = []
+    YearlyDayValue.value = ''
+  } else if(value == 'Yearly') { 
+    weeklyChecked.value = []
+    monthlyChecked.value = []
+    QuarterMonthValue.value = ''
+  }
+}
+</script>
+<template>
+  <div style="padding: 8px 16px 0 16px;">
+    <div class="title">
+      <span class="stars_red">*</span>
+      Email Recipients
+    </div>
+    <el-input v-model="emailValue" :rows="4" type="textarea" placeholder="Enter email address separated by commas" style="margin-top: 4px;"></el-input>
+  </div>
+  <div class="line"></div>
+  <div style="padding: 0 16px;">
+    <div class="flex">
+      <div class="timezone" style="margin-right: 8px;">
+        <div class="title">
+          <span class="stars_red">*</span>
+          Timezone
+        </div>
+        <el-select class="select_time" v-model="TimeZoneValue" placeholder="Please select...">
+          <el-option
+            v-for="item in Timezoneoptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </div>
+      <div class="timezone">
+        <div class="title">
+          <span class="stars_red">*</span>
+          Delivery Frequency
+        </div>
+        <el-select class="select_time" v-model="FrequencyValue" placeholder="Please select..." @change="handleClickFrequency">
+          <el-option
+            v-for="item in Frequencyoptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </div>
+    </div>
+    <div class="schedule_details">
+      <div class="detail_title">
+        Schedule Details
+      </div>
+      <div class="weelkly_detailes" v-if="FrequencyValue == 'Weekly'">
+        <div class="title">
+          <span class="stars_red">*</span>
+          Days of Week (Select multiple)
+        </div>
+        <el-checkbox-group
+          :max="maxWeeklySelected"
+          v-model="weeklyChecked"
+          style="margin: 4px 0 16px 0;"
+          @change="handleChangeCheckbox('weekly')"
+        >
+          <el-checkbox border v-for="item in weeklyCheckList" :key="item" :value="item">
+            {{ item }}
+          </el-checkbox>
+        </el-checkbox-group>
+      </div>
+      <div class="monthly_detailes" :class="{'february_class' : isFebruary, 'nine_class' : is29}" v-else-if="FrequencyValue == 'Monthly'">
+        <div class="title">
+          <span class="stars_red">*</span>
+          Days of Month (Select multiple)
+        </div>
+        <el-checkbox-group
+          :max="maxMonthlySelected"
+          v-model="monthlyChecked"
+          style="margin: 4px 0 16px 0;"
+          @change="handleChangeCheckbox('monthly')"
+        >
+          <el-checkbox border v-for="item in MonthlyCheckList" :key="item" :value="item">
+            {{ item }}
+          </el-checkbox>
+        </el-checkbox-group>
+      </div>
+      <div class="yearly_detailes" v-else-if="FrequencyValue == 'Yearly'">
+        <div class="title">
+          <span class="stars_red">*</span>
+          Months (Select multiple)
+        </div>
+        <el-checkbox-group
+          :max="maxYearlySelected"
+          v-model="yearlyChecked"
+          style="margin: 4px 0 16px 0;"
+          @change="handleChangeCheckbox('yearly')"
+        >
+          <el-checkbox border v-for="item in YearlyCheckList" :key="item" :value="item">
+            {{ item }}
+          </el-checkbox>
+        </el-checkbox-group>
+      </div>
+      <div style="display: flex;">
+        <div class="details_time" v-if="FrequencyValue == 'Quarterly'">
+          <div class="title">
+            <span class="stars_red">*</span>
+            Quarter Month
+          </div>
+          <el-select 
+            style="width: 480px;margin: 4px 8px 0 0;"
+            v-model="QuarterMonthValue"
+            placeholder="Please select...">
+            <el-option
+              v-for="item in QuarterMonth"
+              :key="item"
+              :label="item"
+              :value="item"
+            />
+          </el-select>
+        </div>
+        <div class="details_time" v-if="FrequencyValue == 'Yearly'">
+          <div class="title">
+            <span class="stars_red">*</span>
+            Day
+          </div>
+          <el-select 
+            style="width: 480px;margin: 4px 8px 0 0;"
+            v-model="YearlyDayValue"
+            placeholder="Please select...">
+            <el-option
+              v-for="item in dayList"
+              :key="item"
+              :label="item"
+              :value="item"
+            />
+          </el-select>
+        </div>
+        <div class="details_time">
+          <div class="title">
+            <span class="stars_red">*</span>
+            Time
+          </div>
+          <a-select
+            v-model:value="timeValue"
+            :options="TimeItem"
+            style="width: 480px;margin: 4px 0 0 0;"
+            placeholder="Please select..."
+          >
+            <template #suffixIcon>
+              <span class="font_family icon-icon_time_b"></span>
+            </template>
+          </a-select>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.title {
+  color: var(--color-neutral-2);
+  font-size: 12px;
+  font-weight: 400;
+}
+.stars_red {
+  color: var(--color-danger);
+}
+:deep(.el-textarea) {
+  .el-textarea__inner {
+    resize: none; // 去除右下角图标
+    padding: 5px 7px 5px 10px;
+    box-shadow: 0 0 0 1px var(--color-select-border) inset;
+  }
+}
+.line {
+  margin: 16px 0 ;
+  border-bottom: 1px solid var(--color-border);
+}
+.flex {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+.timezone {
+  width: 50%;
+}
+.select_time {
+  margin-top: 4px;
+}
+.schedule_details {
+  background-color: var(--color-schedule-details-bg);
+  border-radius: 6px;
+  margin: 8px 0 20px 0;
+  padding: 11px 0 8px 8px;
+  .detail_title {
+    color: var(--color-neutral-1);
+    font-weight: 700;
+    font-size: 14px;
+    margin-bottom: 20px;
+  }
+}
+:deep(.el-checkbox.is-bordered) {
+  width: 14.2%;
+  margin-right: 0;
+  border-radius: 0;
+  border: 1px solid var(--color-select-border);
+  background-color: var(--management-bg-color);
+  height: 40px;
+}
+:deep(.el-checkbox.is-bordered:first-child) {
+  border-radius: 6px 0 0 6px;
+}
+:deep(.el-checkbox.is-bordered:last-child) {
+  border-radius: 0 6px 6px 0;
+}
+:deep(.monthly_detailes .el-checkbox.is-bordered:first-child) {
+  border-radius: 6px 0 0 0;
+}
+:deep(.monthly_detailes .el-checkbox.is-bordered:nth-child(7)) {
+  border-radius: 0 6px 0 0;
+}
+:deep(.monthly_detailes .el-checkbox.is-bordered:nth-child(28)) {
+  border-radius: 0 0 6px 0;
+}
+:deep(.monthly_detailes .el-checkbox.is-bordered:nth-child(29)) {
+  border-radius: 0 0 0 6px;
+}
+:deep(.february_class .el-checkbox.is-bordered:nth-child(29)) {
+  border-radius: 0 0 0 0;
+}
+:deep(.february_class .el-checkbox.is-bordered:nth-child(22)) {
+  border-radius: 0 0 0 6px;
+}
+:deep(.monthly_detailes .el-checkbox.is-bordered:last-child) {
+  border-radius: 0 0 6px 0;
+}
+:deep(.nine_class .el-checkbox.is-bordered:last-child) {
+  border-radius: 0 0 6px 6px;
+}
+:deep(.yearly_detailes .el-checkbox.is-bordered) {
+  width: 16.6%;
+}
+:deep(.yearly_detailes .el-checkbox.is-bordered:first-child) {
+  border-radius: 6px 0 0 0;
+}
+:deep(.yearly_detailes .el-checkbox.is-bordered:nth-child(6)) {
+  border-radius: 0 6px 0 0;
+}
+:deep(.yearly_detailes .el-checkbox.is-bordered:nth-child(7)) {
+  border-radius: 0 0 0 6px;
+}
+:deep(.yearly_detailes .el-checkbox.is-bordered:last-child) {
+  border-radius: 0 0 6px 0;
+}
+</style>

+ 361 - 0
src/views/Report/src/components/ReportSchedule/src/components/FieldsTable.vue

@@ -0,0 +1,361 @@
+<script setup lang="ts">
+import { ref, onMounted } from 'vue'
+import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
+import { useRowClickStyle } from '@/hooks/rowClickStyle'
+import { formatNumber } from '@/utils/tools'
+import dayjs from 'dayjs'
+import { autoWidth } from '@/utils/table'
+import { useLoadingState } from '@/stores/modules/loadingState'
+import { useCalculatingHeight } from '@/hooks/calculatingHeight'
+import ManageReportFields from './ManageReportFields.vue'
+
+const filterRef: Ref<HTMLElement | null> = ref(null)
+
+const containerHeight = useCalculatingHeight(document.documentElement, 446, [filterRef])
+
+const columnstest = ref([
+  {
+    title: 'Reference No. ',
+    field: 'reference_no',
+    displayName: 'Reference No. ',
+    type: 'normal',
+    formatter:'',
+    checked: true
+  },
+  {
+    title: 'Purchase',
+    field: 'purchase',
+    displayName: 'Purchase',
+    type: 'normal',
+    formatter:'',
+    checked: true
+  },
+  {
+    title: 'House Bill of Lading',
+    field: 'house_bill_of_lading',
+    displayName: '',
+    type: 'normal',
+    formatter:'',
+    checked: true
+  },
+  {
+    title: 'Master Bill of Lading',
+    field: 'master_bill_of_lading',
+    displayName: '',
+    type: 'normal',
+    formatter:'',
+    checked: true
+  },
+  {
+    title: 'Vessel Name',
+    field: 'vessel_name',
+    displayName: '',
+    type: 'normal',
+    formatter:'',
+    checked: true
+  },
+  {
+    title: 'Status',
+    field: 'status',
+    displayName: '',
+    type: 'status',
+    formatter:'',
+    checked: true
+  },
+  {
+    title: 'Container Number',
+    field: 'container_number',
+    displayName: '',
+    type: 'normal',
+    formatter:'',
+    checked: true
+  },
+  {
+    title: 'Service Type',
+    field: 'service_type',
+    displayName: '',
+    type: 'normal',
+    formatter:'',
+    checked: true
+  },
+])
+const ColumnSortValue = ref('')
+const ManageReportFieldsRef = ref()
+const ColumnSortoptions = [
+  {
+    value: 'Option1',
+    label: 'Option1',
+  },
+  {
+    value: 'Option2',
+    label: 'Option2',
+  },
+  {
+    value: 'Option3',
+    label: 'Option3',
+  },
+  {
+    value: 'Option4',
+    label: 'Option4',
+  },
+  {
+    value: 'Option5',
+    label: 'Option5',
+  },
+]
+
+const loadingState = useLoadingState()
+const tableLoadingColumn = ref(false)
+const tableLoadingTable = ref(false)
+const exportLoading = ref(false)
+
+const tableData = ref<VxeGridProps<any>>({
+  border: true,
+  round: true,
+  columns: [],
+  data: [],
+  scrollY: { enabled: true, oSize: 20, gt: 30 },
+  stripe: true,
+  emptyText: ' ',
+  showHeaderOverflow: true,
+  showOverflow: true,
+  headerRowStyle: {
+    backgroundColor: 'var(--color-table-header-bg)'
+  },
+  columnConfig: { resizable: true, useKey: true },
+  rowConfig: { isHover: true },
+  exportConfig: {
+    types: ['csv', 'html', 'txt', 'xlsx'],
+    modes: ['current', 'selected', 'all']
+  }
+})
+
+const tableRef = ref<VxeGridInstance | null>(null)
+const pageInfo = ref({ pageNo: 1, pageSize: 50, total: 0 })
+
+const handleColumns = (columns: any) => {
+  const newColumns = columns.map((item: any) => {
+    let curColumn: any = {
+      title: item.title,
+      field: item.field,
+      width: item.width
+    }
+    // 设置插槽
+    if (item.type === 'status') {
+      curColumn = {
+        ...curColumn,
+        slots: { default: 'status' }
+      }
+    }
+    return curColumn
+  })
+  return newColumns
+}
+// 获取表格列
+const getTableColumns = async () => {
+  // tableLoadingColumn.value = true
+  tableData.value.columns = handleColumns(columnstest.value)
+  nextTick(() => {
+    tableRef.value && autoWidth(tableData.value, tableRef.value)
+    tableLoadingColumn.value = false
+  })
+}
+// 获取表格数据
+const getTableData = (isPageChange?: boolean) => {
+  tableLoadingTable.value = true
+  $api
+    .getConfigurationList({
+      cp: pageInfo.value.pageNo,
+      ps: pageInfo.value.pageSize,
+      rc: isPageChange ? pageInfo.value.total : -1
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        pageInfo.value.total = Number(res.data.rc)
+        pageInfo.value.pageNo = res.data.cp
+        pageInfo.value.pageSize = res.data.ps
+        tableData.value.data = [
+          {
+            reference_no: '1234567890',
+            purchase: 'Purchase',
+            house_bill_of_lading: 'House Bill of Lading',
+            master_bill_of_lading: 'Master Bill of Lading',
+            vessel_name: 'Vessel Name',
+            status: 'Created',
+            container_number: 'Container Number',
+            service_type: 'Service Type'
+          }
+        ]
+      }
+    })
+    .finally(() => {
+      nextTick(() => {
+        tableRef.value && autoWidth(tableData.value, tableRef.value)
+        tableLoadingTable.value = false
+      })
+    })
+}
+
+// 查询时调用接口
+const handleSearch = (val: any) => {
+  tableLoadingTable.value = true
+  setTimeout(() => {
+    tableLoadingTable.value = false
+  }, 1000);
+}
+// 下载
+const getExportTableData = (type: string, column: any) => {
+  if(column) {
+    const newColumns = column.map((item: any) => {
+      let curColumn: any = {
+        title: item.displayName != '' ? item.displayName : item.title,
+        field: item.field,
+        width: item.width
+      }
+      return curColumn
+    })
+    column = newColumns
+  }
+  exportLoading.value = true
+  const exportConfig: any = {
+    type: type,
+    message: false,
+    filename: `Report List_${dayjs().format('YYYYMMDDHH[h]mm[m]ss[s]')}`,
+    columns: column || tableData.value.columns
+  }
+
+  tableRef.value.exportData(exportConfig)
+  setTimeout(() => {
+    exportLoading.value = false
+  }, 1000)
+}
+
+const handleClickManageFields = () => {
+  ManageReportFieldsRef.value.openDialog(tableData.value.columns)
+}
+
+const ApplyNewColumn = (column: any) => {
+  tableData.value.columns = column
+}
+
+// 实现行点击样式
+useRowClickStyle(tableRef)
+
+onMounted(() => {
+  getTableColumns()
+  getTableData()
+})
+
+defineExpose({
+  handleSearch,
+  getExportTableData
+})
+</script>
+<template>
+  <div
+    v-loading.fullscreen.lock="exportLoading"
+    element-loading-text="Loading..."
+    element-loading-custom-class="element-loading"
+    element-loading-background="rgb(43, 47, 54, 0.7)"
+  >
+    <div class="SettingTable">
+      <div class="flex">
+        <div class="Title">
+          Report Data Review
+        </div>
+        <div class="flex">
+          <span class="sort-text">Sort by:</span>
+          <el-select style="width: 160px;margin: 0 8px;" v-model="ColumnSortValue" placeholder="Please select...">
+            <el-option
+              v-for="item in ColumnSortoptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+          <el-button type="default" @click="handleClickManageFields">
+            <span class="font_family icon-icon_set_b"></span>Manage Fields
+          </el-button>
+        <ManageReportFields ref="ManageReportFieldsRef" @ApplyNewColumn="ApplyNewColumn"></ManageReportFields>
+        </div>
+      </div>
+      <vxe-grid
+        ref="tableRef"
+        :style="{ border: 'none'}"
+        v-bind="tableData"
+        :height="containerHeight"
+        v-vloading="tableLoadingColumn || tableLoadingTable || loadingState.trackingTableLoading"
+      >
+        <!-- 空数据时的插槽 -->
+        <template #empty>
+          <img src="../images/default_no_data@2x.png" />>
+          <div class="empty-text">No data</div>
+        </template>
+        <!-- Status字段的插槽 -->
+        <template #status="{ row, column }">
+          <VTag :type="row[column.field]">{{ row[column.field] }}</VTag>
+        </template>
+      </vxe-grid>
+      <div class="pagination">
+        <span>Total {{ formatNumber(pageInfo.total) }}</span>
+        <el-pagination
+          v-model:current-page="pageInfo.pageNo"
+          v-model:page-size="pageInfo.pageSize"
+          :page-sizes="[50, 100, 200, 300, 400]"
+          :pagerCount="5"
+          background
+          layout="sizes, prev, pager, next"
+          :total="pageInfo.total"
+          @size-change="getTableData(true)"
+          @current-change="getTableData(true)"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.flex {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 13px;
+  .sort-text {
+    font-size: 12px;
+    font-weight: 400;
+    color: var(--color-neutral-2);
+  }
+}
+.Title {
+  font-size: var(--font-size-6);
+  font-weight: 700;
+}
+.pagination {
+  display: flex;
+  justify-content: space-between;
+  font-weight: 400;
+  font-size: 15px;
+  align-items: center;
+  border: 1px solid var(--color-border);
+  border-top: none;
+  padding: 4px 8px;
+  border-radius: 0 0 6px 6px;
+}
+:deep(.el-icon svg) {
+  width: 1em !important;
+}
+.SettingTable {
+  padding: 13px 24px 24px 24px;
+  font-weight: 400;
+  border: 1px solid var(--color-border);
+  border-radius: 12px;
+  border-top: 1px solid var(--color-border);
+}
+.empty-text {
+  margin: 8px 0;
+  color: var(--color-neutral-2);
+  text-align: center;
+  font-size: 14px;
+  font-weight: 700;
+}
+</style>

+ 231 - 0
src/views/Report/src/components/ReportSchedule/src/components/ManageReportFields.vue

@@ -0,0 +1,231 @@
+<script setup lang="ts">
+import { Item } from 'ant-design-vue/es/menu'
+import { VueDraggable } from 'vue-draggable-plus'
+interface ColumnItem {
+  name: string
+  title: string
+  field: string
+  checked: boolean
+  displayName: string
+  formatter: string
+  type: string
+}
+
+const ManageFieldsVisible = ref(false)
+const ManageFieldsColumns = ref<ColumnItem[]>([])
+const openDialog = (column: any) => {
+  ManageFieldsColumns.value = column
+  ManageFieldsVisible.value = true
+}
+const curSelectedColumns = computed(() => {
+  if (!ManageFieldsColumns.value) return []
+  return ManageFieldsColumns.value.filter((i: any) => i.checked)
+})
+const handleMoveUpSelect = (item: any) => {
+  const index = ManageFieldsColumns.value.findIndex((i: any) => i.title === item.title)
+  if (index === 0) return
+  const temp = ManageFieldsColumns.value[index]
+  ManageFieldsColumns.value[index] = ManageFieldsColumns.value[index - 1]
+  ManageFieldsColumns.value[index - 1] = temp
+}
+const handleMoveDownSelect = (item: any) => {
+  const index = ManageFieldsColumns.value.findIndex((i: any) => i.title === item.title)
+  if (index === ManageFieldsColumns.value.length - 1) return
+  const temp = ManageFieldsColumns.value[index]
+  ManageFieldsColumns.value[index] = ManageFieldsColumns.value[index + 1]
+  ManageFieldsColumns.value[index + 1] = temp
+}
+
+const ShowAll = (type: string) => {
+  ManageFieldsColumns.value.forEach((item: any) => {
+    item.checked = type === 'show'
+  })
+}
+const emit = defineEmits(['ApplyNewColumn'])
+const handleApply = () => {
+  ManageFieldsVisible.value = false
+  emit('ApplyNewColumn', curSelectedColumns.value)
+}
+
+defineExpose({
+  openDialog
+})
+
+</script>
+<template>
+  <el-dialog
+    title="Manage Report Fields"
+    v-model="ManageFieldsVisible"
+    :show-close="false"
+    footer-class="manage-footer-class"
+    width="800px">
+      <div class="flex">
+        <div class="flex">
+          <el-button style="height: 32px; padding: 0 13px;" type="default" @click="ShowAll('show')">Show All</el-button>
+          <el-button style="height: 32px; padding: 0 13px;" type="default" @click="ShowAll('hide')">Hide All</el-button>
+        </div>
+        <div class="fields-title">{{ curSelectedColumns.length }} of {{ ManageFieldsColumns.length }} Fields Visible</div>
+      </div>
+      <div class="system-list">
+        <div class="system-list-one"></div>
+        <div class="system-list-two"></div>
+        <div class="system-list-name">System Name</div>
+        <div class="system-list-display-name">Display Name in Report</div>
+        <div class="system-list-icon"></div>
+      </div>
+      <VueDraggable
+        v-model="ManageFieldsColumns"  
+        class="column-list"
+        ghost-class="ghost-column"
+        :forceFallback="true"
+        fallback-class="fallback-class"
+      >
+        <template v-for="item in ManageFieldsColumns" :key="item.name">
+          <div class="column-item">
+            <div class="system-list-one">
+              <span class="font_family icon-icon_dragsort__b draggable-icon"></span>
+            </div>
+            <div class="system-list-two">
+              <el-checkbox v-model="item.checked"></el-checkbox>
+            </div>
+            <div class="system-list-name" :class="{'hide-class' : !item.checked}">{{ item.title }}</div>
+            <div class="system-list-display-name">
+              <el-input v-model="item.displayName"></el-input>
+            </div>
+            <div class="system-list-icon">
+              <span
+                class="font_family icon-icon_moveup_b move-icon"
+                style="margin-right: 16px;"
+                @click="handleMoveUpSelect(item)"
+              ></span>
+              <span
+                class="font_family icon-icon_movedown_b move-icon"
+                @click="handleMoveDownSelect(item)"
+              ></span>
+            </div>
+          </div>
+        </template>
+      </VueDraggable>
+    <template #footer>
+      <el-button
+        type="default"
+        style="height: 40px; padding: 8px 40px"
+        @click="ManageFieldsVisible = false"
+        >Cancel</el-button
+      >
+      <el-button
+        class="el-button--dark"
+        style="height: 40px; padding: 8px 40px"
+        @click="handleApply"
+      >
+        Apply
+      </el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.flex {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+.fields-title {
+  font-size: 12px;
+  color: var(--color-neutral-2);
+  font-weight: 400;
+}
+.system-list {
+  background-color: var(--color-header-bg);
+  height: 24px;
+  display: flex;
+  align-items: center;
+  border-radius: 6px;
+  margin: 10px 0 0 0;
+  color: var(--color-neutral-2);
+  .system-list-name,.system-list-display-name {
+    font-size: 12px;
+    color: var(--color-neutral-2);
+    font-weight: 400;
+  }
+}
+.system-list-one {
+  width: 40px;
+}
+.system-list-two {
+  width: 32px;
+}
+.system-list-name {
+  width: 28%;
+}
+.system-list-display-name {
+  width: 50%;
+}
+.system-list-icon {
+  width: 10%;
+  display: flex;
+  align-items: center;
+  justify-content: end;
+}
+.column-list {
+  height: 400px;
+  overflow: auto;
+  padding-right: 8px;
+  .column-item {
+    display: flex;
+    align-items: center;
+    height: 40px;
+    border-radius: 6px;
+    border: 1px solid var(--color-border);
+    background-color: var(--color-table-header-bg);
+    margin: 4px 0;
+    user-select: none;
+    .system-list-one {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+    .system-list-name {
+      font-size: 14px;
+      font-weight: 400;
+      color: var(--color-neutral-1);
+    }
+    .hide-class {
+      text-decoration: line-through;
+      color: var(--color-neutral-2);
+    }
+    .system-list-display-name {
+      :deep(.el-input__wrapper) {
+        background-color: var(--color-table-header-bg);
+        border-radius: 6px;
+      }
+    }
+    span.draggable-icon {
+      color: var(--color-customize-column-item-drag-icon);
+    }
+    .font_family {
+      font-size: 16px;
+      cursor: pointer;
+    }
+    .move-icon {
+      &:hover {
+        color: var(--color-theme);
+      }
+    }
+  }
+}
+.ghost-column {
+  cursor: move !important;
+  span {
+    opacity: 0;
+  }
+  border: 1px dashed var(--color-customize-column-item-drag-border) !important;
+  background-color: var(--color-customize-column-item-drag-bg) !important;
+  box-shadow: none !important;
+}
+.fallback-class {
+  opacity: 1 !important;
+  background-color: var(--color-customize-column-item-hover-bg) !important;
+  cursor: move !important;
+}
+</style>

+ 440 - 11
src/views/Report/src/components/ReportSchedule/src/components/TimeRange.vue

@@ -1,15 +1,177 @@
 <script setup lang="ts">
+import { useUserStore } from '@/stores/modules/user'
+import dayjs from 'dayjs'
+
+const userStore = useUserStore()
 const DataTimeSelection = ref('')
-const DataTimeoptions = [
-  {
-    value: 'ETD',
-    label: 'ETD'
-  },
-  {
-    value: 'ETA',
-    label: 'ETA'
-  }
-]
+const startDate = ref('')
+const endDate = ref('')
+const RollingValueStart = ref()
+const RollingValueeEnd = ref()
+const radio = ref(0)
+const fixedRangeRadio = ref('')
+const rollingRangeRadio = ref('')
+const DataTimeoptions = ref(
+  [
+    {
+      value: 'ETD',
+      label: 'ETD'
+    },
+    {
+      value: 'ETA',
+      label: 'ETA'
+    }
+  ]
+)
+
+const isshowRolling = computed(() => radio.value === 1)
+const isShowFixed = computed(() => radio.value === 2)
+
+
+const clampedValueStart = computed({
+  get: () => RollingValueStart.value,
+  set: (newVal) => {
+    // 转换为整数
+    const num = parseInt(newVal, 10)
+    // 处理非数字和NaN情况
+    if (isNaN(num)) {
+      RollingValueStart.value = 0
+      return
+    }
+    // 范围限制
+    RollingValueStart.value = Math.max(0, Math.min(365, num))
+  }
+})
+const clampedValueEnd = computed({
+  get: () => RollingValueeEnd.value,
+  set: (newVal) => {
+    // 转换为整数
+    const num = parseInt(newVal, 10)
+    // 处理非数字和NaN情况
+    if (isNaN(num)) {
+      RollingValueeEnd.value = 0
+      return
+    }
+    // 范围限制
+    RollingValueeEnd.value = Math.max(0, Math.min(365, num))
+  }
+})
+
+let RollingRangeTimeStr: any = ''
+
+const emits = defineEmits(['handleSubmitRolling'])
+
+// 输入时间
+const changeTime = (val: any) => {
+  if (val == 1) {
+    startDate.value = ''
+    endDate.value = ''
+    if (
+      typeof clampedValueStart.value == 'number' &&
+      typeof clampedValueEnd.value == 'number'
+    ) {
+      if (clampedValueStart.value == 0 && clampedValueEnd.value == 30) {
+        rollingRangeRadio.value = 'Next 30 days'
+      } else if (clampedValueStart.value == 0 && clampedValueEnd.value == 60) {
+        rollingRangeRadio.value = 'Next 60 days'
+      } else if (clampedValueStart.value == 10 && clampedValueEnd.value == 60) {
+        rollingRangeRadio.value = 'Past 10 days to next 60 day'
+      } else if (clampedValueStart.value == 30 && clampedValueEnd.value == 0) {
+        rollingRangeRadio.value = 'Past 30 days'
+      } else {
+        rollingRangeRadio.value = 'Customize'
+      }
+      if (clampedValueStart.value == 0 && clampedValueEnd.value == 0) {
+        RollingRangeTimeStr = ''
+      } else {
+        RollingRangeTimeStr =
+          'ETD: minus ' +
+          clampedValueStart.value +
+          ' Day(s) to Plus ' +
+          clampedValueEnd.value +
+          ' Day(s)'
+      }
+    } else {
+      RollingRangeTimeStr = ''
+    }
+    emits('handleSubmitRolling', {rollingStartDate: clampedValueStart.value, rollingEndDate: clampedValueEnd.value, DataTimeSelection: DataTimeSelection.value})
+  } else if(val == 2) {
+    clampedValueStart.value = 0
+    clampedValueEnd.value = 0
+    if (startDate.value == dayjs().startOf('month').format('YYYY.MM.DD') && endDate.value == dayjs().endOf('month').format('YYYY.MM.DD')) {
+      fixedRangeRadio.value = 'This Month'
+    } else if (startDate.value == dayjs().subtract(1, 'month').startOf('month').format('YYYY.MM.DD') && endDate.value == dayjs().subtract(1, 'month').endOf('month').format('YYYY.MM.DD')) {
+      fixedRangeRadio.value = 'Last Month'
+    } else if (startDate.value == dayjs().month(Math.floor(dayjs().month() / 3) * 3).startOf('month').format('YYYY.MM.DD') && endDate.value == dayjs().month(Math.floor(dayjs().month() / 3) * 3 + 2).endOf('month').format('YYYY.MM.DD')) {
+      fixedRangeRadio.value = 'This Quarter'
+    } else if (startDate.value == dayjs().month(Math.floor(dayjs().month() / 3) * 3 - 3).startOf('month').format('YYYY.MM.DD') && endDate.value == dayjs().month(Math.floor(dayjs().month() / 3) * 3 - 1).endOf('month').format('YYYY.MM.DD')) {
+      fixedRangeRadio.value = 'Last Quarter'
+    } else if (startDate.value == dayjs().startOf('year').format('YYYY.MM.DD') && endDate.value == dayjs().endOf('year').format('YYYY.MM.DD')) {
+      fixedRangeRadio.value = 'This Year'
+    } else if (startDate.value == dayjs().subtract(1, 'year').startOf('year').format('YYYY.MM.DD') && endDate.value == dayjs().subtract(1, 'year').endOf('year').format('YYYY.MM.DD')) {
+      fixedRangeRadio.value = 'Last Year'
+    } else {
+      fixedRangeRadio.value = 'Customize'
+    }
+    emits('handleSubmitRolling', {rollingStartDate: startDate.value, rollingEndDate: endDate.value, DataTimeSelection: DataTimeSelection.value})
+  } else {
+    RollingRangeTimeStr = ''
+  }
+}
+// 选择Dynamic Rolling Range默认值
+const ChangeRollingRangeRadio = (val: any) => { 
+  if (val == 'Next 30 days') {
+    clampedValueStart.value = 0
+    clampedValueEnd.value = 30
+  } else if (val == 'Next 60 days') {
+    clampedValueStart.value = 0
+    clampedValueEnd.value = 60
+  } else if (val == 'Past 30 days') {
+    clampedValueStart.value = 30
+    clampedValueEnd.value = 0
+  } else if (val == 'Past 10 days to next 60 days') {
+    clampedValueStart.value = 10
+    clampedValueEnd.value = 60
+  } else {
+    clampedValueStart.value = 0
+    clampedValueEnd.value = 0
+  }
+  if (clampedValueStart.value == 0 && clampedValueEnd.value == 0) {
+    RollingRangeTimeStr = ''
+  } else {
+    RollingRangeTimeStr =
+      'ETD: minus ' +
+      clampedValueStart.value +
+      ' Day(s) to Plus ' +
+      clampedValueEnd.value +
+      ' Day(s)'
+  }
+  emits('handleSubmitRolling', {rollingStartDate: clampedValueStart.value, rollingEndDate: clampedValueEnd.value, DataTimeSelection: DataTimeSelection.value})
+}
+
+// 选择fixed range默认值
+const changeFixedRange = (val: any) => {
+  if(val == 'This Month') {
+    startDate.value = dayjs().startOf('month').format('YYYY.MM.DD')
+    endDate.value = dayjs().endOf('month').format('YYYY.MM.DD')
+  } else if(val == 'Last Month') {
+    startDate.value = dayjs().subtract(1, 'month').startOf('month').format('YYYY.MM.DD')
+    endDate.value = dayjs().subtract(1, 'month').endOf('month').format('YYYY.MM.DD')
+  } else if(val == 'This Quarter') {
+    startDate.value = dayjs().month(Math.floor(dayjs().month() / 3) * 3).startOf('month').format('YYYY.MM.DD')
+    endDate.value = dayjs().month(Math.floor(dayjs().month() / 3) * 3 + 2).endOf('month').format('YYYY.MM.DD')
+  } else if(val == 'Last Quarter') {
+    startDate.value = dayjs().month(Math.floor(dayjs().month() / 3) * 3 - 3).startOf('month').format('YYYY.MM.DD')
+    endDate.value = dayjs().month(Math.floor(dayjs().month() / 3) * 3 - 1).endOf('month').format('YYYY.MM.DD')
+  } else if(val == 'This Year') {
+    startDate.value = dayjs().startOf('year').format('YYYY.MM.DD')
+    endDate.value = dayjs().endOf('year').format('YYYY.MM.DD')
+  } else if(val == 'Last Year') {
+    startDate.value = dayjs().subtract(1, 'year').startOf('year').format('YYYY.MM.DD')
+    endDate.value = dayjs().subtract(1, 'year').endOf('year').format('YYYY.MM.DD')
+  }
+  emits('handleSubmitRolling', {rollingStartDate: startDate.value, rollingEndDate: endDate.value, DataTimeSelection: DataTimeSelection.value})
+}
 </script>
 <template>
   <div style="padding: 8px 16px 16px 16px;">
@@ -17,7 +179,7 @@ const DataTimeoptions = [
       <span class="stars_red">*</span>
       Data Time Reference Field Selection
     </div>
-    <el-select v-model="DataTimeSelection" placeholder="Please select...">
+    <el-select class="select_time" v-model="DataTimeSelection" placeholder="Please select...">
       <el-option
         v-for="item in DataTimeoptions"
         :key="item.value"
@@ -25,6 +187,136 @@ const DataTimeoptions = [
         :value="item.value"
       />
     </el-select>
+    <div class="schedule_rule">
+      <div class="schedule_title">
+        <span class="stars_red">*</span>
+        Data Range Configuration Method
+      </div>
+      <div class="schedule_container">
+        <el-radio-group v-model="radio" @change="changeTime">
+          <el-radio :value="1">
+            <div class="radio_custom">
+              <div style="height: 38px;">
+                Dynamic Rolling Range
+                <el-tooltip
+                  popper-class="schedule-popper"
+                  effect="dark"
+                  placement="right"
+                >
+                  <template #content>
+                    Configuration: Past X days to Future Y days, always dynamically calculated based on current date when generating reports.<br/>
+                    Usage: For example, setting 5 days to 3 days means the data range differs each time it's automatically generated.
+                    Used for short-cycle operational monitoring.
+                  </template>
+                  <span class="font_family icon-icon_info_b"></span>
+                </el-tooltip>
+              </div>
+              <div v-if="isshowRolling" class="oceanCheckbox2">
+                <el-radio-group v-model="rollingRangeRadio" @change="ChangeRollingRangeRadio">
+                  <el-radio-button label="Next 30 days" value="Next 30 days" />
+                  <el-radio-button label="Next 60 days" value="Next 60 days" />
+                  <el-radio-button
+                    label="Past 10 days to next 60 days"
+                    value="Past 10 days to next 60 days"
+                  />
+                  <el-radio-button label="Past 30 days" value="Past 30 days" />
+                  <el-radio-button label="Customize" value="Customize" />
+                </el-radio-group>
+                <div class="flex" style="align-items: end; margin: 0 8px 8px 0; flex-wrap: wrap">
+                  <div class="date_flex" style="margin-right: 10px;">
+                    <div class="time_title">Start Date</div>
+                    <div class="flex">
+                      <div class="currentTime">Past</div>
+                      <el-input
+                        v-model="clampedValueStart"
+                        @input="changeTime('1')"
+                        class="input-with-select"
+                      >
+                      </el-input>
+                      <div class="Days">Day(s)</div>
+                    </div>
+                  </div>
+                  <div class="date_flex">
+                    <div class="time_title">End Date</div>
+                    <div class="flex">
+                      <div class="currentTime">Future</div>
+                      <el-input
+                        v-model="clampedValueEnd"
+                        @input="changeTime('1')"
+                        class="input-with-select"
+                      >
+                      </el-input>
+                      <div class="Days">Day(s)</div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </el-radio>
+          <el-radio :value="2">
+            <div class="radio_custom">
+              <div style="height: 38px;">
+                Fixed Range
+                <el-tooltip
+                  popper-class="schedule-popper"
+                  effect="dark"
+                  placement="right"
+                >
+                  <template #content>
+                    Configuration: Specific start and end dates, always query this range when automatically generating reports.<br />
+                    Example: Start date [2025-01-01], End date [2025-12-31]
+                  </template>
+                  <span class="font_family icon-icon_info_b"></span>
+                </el-tooltip>
+              </div>
+              <div v-if="isShowFixed">
+                <div class="oceanCheckbox2">
+                  <el-radio-group v-model="fixedRangeRadio" @change="changeFixedRange" class="oceanCheckbox2">
+                    <el-radio-button label="This Month" value="This Month" />
+                    <el-radio-button label="Last Month" value="Last Month" />
+                    <el-radio-button label="This Quarter" value="This Quarter"/>
+                    <el-radio-button label="Last Quarter" value="Last Quarter" />
+                    <el-radio-button label="This Year" value="This Year"/>
+                    <el-radio-button label="Last Year" value="Last Year" />
+                    <el-radio-button label="Customize" value="Customize" />
+                  </el-radio-group>
+                </div>
+                <div style="display: flex;">
+                  <div style="margin-right: 9px;width: 50%;">
+                  <div class="date_text">Start Date</div>
+                  <a-date-picker
+                    :format="userStore.dateFormat"
+                    valueFormat="YYYY.MM.DD"
+                    :showToday="false"
+                    @change="changeTime('2')"
+                    style="width: 100%;"
+                    v-model:value="startDate">
+                    <template #suffixIcon>
+                      <span class="font_family icon-icon_date_b"></span>
+                    </template>
+                  </a-date-picker>
+                  </div>
+                  <div style="width: 50%;">
+                    <div class="date_text">End Date</div>
+                    <a-date-picker
+                      :format="userStore.dateFormat"
+                      valueFormat="YYYY.MM.DD"
+                      :showToday="false"
+                      @change="changeTime('2')"
+                      style="width: 100%;"
+                      v-model:value="endDate">
+                      <template #suffixIcon>
+                        <span class="font_family icon-icon_date_b"></span>
+                      </template>
+                    </a-date-picker>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </el-radio>
+        </el-radio-group>
+      </div>
+    </div>
   </div>
 </template>
 
@@ -37,4 +329,141 @@ const DataTimeoptions = [
 .stars_red {
   color: var(--color-danger);
 }
+.select_time {
+  margin: 4px 0 8px 0;
+  width: 320px;
+}
+.schedule_rule {
+  border: 1px solid var(--color-border);
+  background-color: var(--color-schedule-bg);
+  border-radius: 12px;
+  .schedule_title {
+    padding: 11px 9px;
+    font-weight: 700;
+    font-size: 18px;
+    color: var(--color-neutral-1);
+  }
+  .schedule_container {
+    margin: 0 9px 8px 9px;
+  }
+}
+:deep(.el-radio-group) {
+  display: block;
+}
+:deep(.el-radio) {
+  display: flex;
+  min-height: 40px;
+  border: 1px solid var(--color-system-border);
+  background-color: var(--color-system-body-bg);
+  margin-bottom: 8px;
+  border-radius: 6px;
+  padding: 0 8px;
+  margin-right: 0;
+  height: fit-content;
+  line-height: 40px;
+  align-items: start;
+}
+.oceanCheckbox2 {
+  :deep(.el-radio-group) {
+    display: flex;
+    flex-direction: row;
+  }
+}
+.oceanCheckbox2 {
+  :deep(.el-radio-button) {
+    flex: 1;
+    border: 1px solid var(--color-system-input-border);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  :deep(.el-radio-button:first-child) {
+    border-radius: 6px 0 0 6px;
+  }
+  :deep(.el-radio-button:last-child) {
+    border-radius: 0 6px 6px 0;
+  }
+}
+:deep(.el-radio-button__inner) {
+  border: none;
+  height: 32px;
+  flex: 1;
+}
+:deep(.el-radio-button:first-child .el-radio-button__inner) {
+  border-left: none;
+  border-radius: 6px 0 0 6px;
+}
+:deep(.el-radio-button:last-child .el-radio-button__inner) {
+  border-radius: 0 6px 6px 0;
+}
+:deep(.radio_custom) {
+  flex-direction: column;
+  width: 100%;
+}
+:deep(.el-radio__input.is-checked + .el-radio__label) {
+  color: var(--color-neutral-1);
+}
+.input-with-select {
+  border-radius: 0;
+}
+:deep(.el-input__wrapper) {
+  border-radius: 0;
+  opacity: 0.8;
+  height: 32px;
+}
+:deep(.el-radio__label) {
+  display: flex;
+  align-items: center;
+  .font_family {
+    color: var(--color-neutral-2);
+    margin-left: 4px;
+  }
+}
+:deep(.el-radio__inner) {
+  margin-top: 11px;
+}
+:deep(.el-radio__input) {
+  margin-top: 2px;
+}
+.date_text {
+  height: 26px;
+  color: var(--color-neutral-2);
+  font-size: 12px;
+  font-weight: 400;
+}
+.flex {
+  display: flex;
+  align-items: center;
+}
+.currentTime {
+  background-color: var(--el-disabled-bg-color);
+  border-radius: 6px 0 0 6px;
+  padding: 0 16px;
+  height: 32px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border: 1px solid var(--color-system-input-border);
+}
+.Days {
+  width: 150px;
+  border: 1px solid var(--color-system-input-border);
+  border-radius: 0 6px 6px 0;
+  background-color: var(--color-system-body-bg);
+  border-left: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  opacity: 0.8;
+  height: 32px;
+}
+.time_title {
+  color: var(--color-neutral-2);
+  font-size: 12px;
+  height: 25px;
+  margin-bottom: 4px;
+}
+.date_flex {
+  flex: 1;
+}
 </style>

+ 17 - 4
src/views/Report/src/components/ReportSchedule/src/components/ValidityPeriod.vue

@@ -4,15 +4,26 @@ import { useUserStore } from '@/stores/modules/user'
 const userStore = useUserStore()
 
 const radio = ref(0)
-const startDate = ref()
-const endDate = ref()
+const startDate = ref('')
+const endDate = ref('')
 const isShowEffective = computed(() => {
   return radio.value == 2
 })
+const emit = defineEmits(['handleSubmitValidity'])
+// 选择Validity Period部分
+const ChangeValidity = (val:any) => {
+  if(val == 2) {
+    emit('handleSubmitValidity', {type: 'Custom Period', startDate: startDate.value? startDate.value : '', endDate: endDate.value? endDate.value : ''})
+  } else if(val == 1) {
+    emit('handleSubmitValidity', {type: 'Permanent Valid'})
+    startDate.value = ''
+    endDate.value = ''
+  }
+}
 </script>
 <template>
   <div style="padding: 8px 16px 16px 16px;">
-    <el-radio-group v-model="radio">
+    <el-radio-group v-model="radio" @change="ChangeValidity">
       <el-radio :value="1">
         Permanent Valid
         <el-tooltip
@@ -45,6 +56,7 @@ const isShowEffective = computed(() => {
                 valueFormat="YYYY.MM.DD"
                 :showToday="false"
                 style="width: 320px;"
+                @change="ChangeValidity(2)"
                 v-model:value="startDate">
                 <template #suffixIcon>
                   <span class="font_family icon-icon_date_b"></span>
@@ -57,6 +69,7 @@ const isShowEffective = computed(() => {
                 :format="userStore.dateFormat"
                 valueFormat="YYYY.MM.DD"
                 :showToday="false"
+                @change="ChangeValidity(2)"
                 style="width: 320px;"
                 v-model:value="endDate">
                 <template #suffixIcon>
@@ -102,7 +115,7 @@ const isShowEffective = computed(() => {
   }
 }
 :deep(.el-radio__inner) {
-  margin-top: 11px;
+  margin-top: 12px;
 }
 .date_text {
   height: 26px;