瀏覽代碼

feat: 实现report新增功能

Jack Zhou 1 周之前
父節點
當前提交
28966e9f03

+ 6 - 14
src/router/index.ts

@@ -62,13 +62,6 @@ const router = createRouter({
           meta: {
             activeMenu: '/tracking'
           }
-        }, {
-          path: '/tracking/download-attachment',
-          name: 'Tracking Download Attachment',
-          component: () => import('../views/Tracking/src/components/DownloadAttachment'),
-          meta: {
-            activeMenu: '/tracking'
-          }
         },
         {
           path: '/shipment/detail',
@@ -145,9 +138,12 @@ const router = createRouter({
           component: () => import('../views/TemplateManagement')
         },
         {
-          path: '/create-report-template',
+          path: '/template-management/create-report-template',
           name: 'Create Report Template',
-          component: () => import('../views/TemplateManagement/src/components/CreateReportTemplate')
+          component: () => import('../views/TemplateManagement/src/components/CreateReportTemplate'),
+          meta: {
+            activeMenu: '/template-management'
+          }
         },
         {
           path: '/system-message',
@@ -212,11 +208,7 @@ const router = createRouter({
             breadName: 'Modify Booking',
             activeMenu: '/destination-delivery'
           }
-        }, {
-          path: '/demo-video',
-          name: 'Demo Video',
-          component: () => import('../views/Video'),
-        },
+        }
       ]
     }
   ]

+ 19 - 1
src/styles/Antdui.scss

@@ -244,4 +244,22 @@ tr
     color: var(--color-theme);
     font-weight: 400;
   }
-}
+}
+
+:where(.css-dev-only-do-not-override-1p3hq3p).ant-select-dropdown {
+  background-color: var(--management-bg-color);
+}
+:where(.css-dev-only-do-not-override-1p3hq3p).ant-select:not(.ant-select-customize-input) div.ant-select-selector {
+  background-color: var(--management-bg-color);
+}
+
+ :where(.css-dev-only-do-not-override-1p3hq3p).ant-select-single.ant-select-open .ant-select-selection-item {
+    color: var(--color-neutral-1);
+ }
+ :where(.css-dev-only-do-not-override-1p3hq3p).ant-select:not(.ant-select-customize-input) .ant-select-selector {
+  border-color: var(--color-select-border);
+ }
+
+ .rc-virtual-list-scrollbar-thumb {
+  background-color: var(--color-scrollbar-thumb) !important;
+ }

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

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

文件差異過大導致無法顯示
+ 0 - 0
src/styles/icons/iconfont.js


+ 2 - 0
src/styles/icons/iconfont.svg

@@ -14,6 +14,8 @@
     />
       <missing-glyph />
       
+      <glyph glyph-name="icon_active" unicode="&#59204;" d="M512 896a512 512 0 1 0 0-1024A512 512 0 0 0 512 896z m0-87.771429a424.228571 424.228571 0 1 1 0-848.457142A424.228571 424.228571 0 0 1 512 808.228571z m287.012571-290.962285l-359.570285-359.643429-31.085715 31.012572-183.369142 183.442285 31.012571 31.012572 31.012571 31.085714 152.356572-152.356571 297.691428 297.472 61.952-62.025143z"  horiz-adv-x="1024" />
+      
       <glyph glyph-name="icon_video_b" unicode="&#59203;" d="M871.104 818.24c47.744 0 86.4-38.656 86.4-86.4v-734.08a86.4 86.4 0 0 0-86.4-86.4H137.024a86.4 86.4 0 0 0-86.4 86.4V731.84c0 47.744 38.656 86.4 86.4 86.4h734.08z m-743.68-820.48c0-5.312 4.288-9.6 9.6-9.6h734.08a9.6 9.6 0 0 1 9.6 9.6V503.616H127.36v-505.856z m276.032 397.312a16 16 0 0 0 24 13.888l256.704-148.288a16 16 0 0 0 0-27.648L427.52 84.736a16 16 0 0 0-24 13.824V395.072zM136.96 741.504a9.6 9.6 0 0 1-9.6-9.6v-151.552h144l-12.608 36.992a22548.48 22548.48 0 0 0-41.792 124.16H136.96z m194.56-99.456l20.992-61.696h139.52l-12.608 36.992c-13.696 40.192-29.376 86.912-41.792 124.16H297.984c10.752-31.808 22.72-67.648 33.6-99.456z m220.672 0l20.992-61.696h139.52l-12.608 36.992c-13.632 40.192-29.376 86.912-41.792 124.16H518.656c10.752-31.872 22.784-67.648 33.6-99.456z m220.672 0l20.992-61.696h86.784V731.904a9.6 9.6 0 0 1-9.6 9.6h-131.712c10.688-31.872 22.72-67.648 33.536-99.456z"  horiz-adv-x="1024" />
       
       <glyph glyph-name="icon_filters_up_b" unicode="&#59202;" d="M267.648 79.68L512 324.032l244.416-244.352 54.272 54.336-271.552 271.488a38.4 38.4 0 0 1-54.272 0l-271.552-271.488 54.336-54.336z m0 271.488L512 595.584l244.416-244.416 54.272 54.336-271.552 271.488a38.4 38.4 0 0 1-54.272 0L213.312 405.504l54.336-54.336z"  horiz-adv-x="1024" />

二進制
src/styles/icons/iconfont.ttf


二進制
src/styles/icons/iconfont.woff


二進制
src/styles/icons/iconfont.woff2


+ 21 - 24
src/views/Report/src/components/ReportSchedule/src/ReportSchedule.vue

@@ -1,8 +1,8 @@
 <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'; 
+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
 })
@@ -30,7 +30,10 @@ const handleSubmitRolling = (data: any) => {
 <template>
   <div class="Title">
     <span>Schedule Configuration - Shipment Status Report</span>
-    <el-button :disabled="isSaveDisabled" class="el-button--main save_button"><span class="font_family icon-icon_save_b icon_dark" style="margin-right: 5px;"></span>Save</el-button>
+    <el-button :disabled="isSaveDisabled" class="el-button--main save_button"
+      ><span class="font_family icon-icon_save_b icon_dark" style="margin-right: 5px"></span
+      >Save</el-button
+    >
   </div>
   <div class="container">
     <div class="schedule_rule">
@@ -39,27 +42,17 @@ const handleSubmitRolling = (data: any) => {
         Schedule Rule Validity Period
       </div>
       <div class="schedule_container">
-        <ValidityPeriod
-          @handleSubmitValidity="handleSubmitValidity"
-        >
-        </ValidityPeriod>
+        <ValidityPeriod @handleSubmitValidity="handleSubmitValidity"> </ValidityPeriod>
       </div>
     </div>
-    <div class="schedule_rule" style="margin: 8px 0;">
-      <div class="schedule_title">
-        Report Data Time Range
-      </div>
+    <div class="schedule_rule" style="margin: 8px 0">
+      <div class="schedule_title">Report Data Time Range</div>
       <div class="schedule_container">
-        <TimeRange
-          @handleSubmitRolling="handleSubmitRolling"
-        >
-        </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_rule" style="margin: 8px 0">
+      <div class="schedule_title">Report Delivery Frequency & Email Configuration</div>
       <div class="schedule_container">
         <EmailConfiguration></EmailConfiguration>
       </div>
@@ -72,12 +65,16 @@ const handleSubmitRolling = (data: any) => {
 .Title {
   display: flex;
   justify-content: space-between;
+  position: sticky;
+  z-index: 100;
+  top: 0;
+  padding: 0 24px;
+  align-items: center;
+  background-color: var(--color-mode);
   height: 68px;
   border-bottom: 1px solid var(--color-border);
   font-size: var(--font-size-6);
   font-weight: 700;
-  padding: 0 24px;
-  align-items: center;
 }
 .save_button {
   height: 40px;
@@ -101,4 +98,4 @@ const handleSubmitRolling = (data: any) => {
     }
   }
 }
-</style>
+</style>

+ 3 - 1
src/views/TemplateManagement/src/TemplateManagement.vue

@@ -49,7 +49,9 @@ const Search = () => {
 
 const handleCreate = () => {
   // Navigate to the Create Report Template page
-  router.push('/create-report-template')
+  router.push({
+    name: 'Create Report Template'
+  })
 }
 </script>
 <template>

+ 234 - 28
src/views/TemplateManagement/src/components/CreateReportTemplate/src/CreateReportTemplate.vue

@@ -2,6 +2,7 @@
 import { useRouter } from 'vue-router'
 import partyIDSelect from './components/partyIDSelect.vue'
 import GroupNameSelect from './components/GroupNameSelect.vue'
+import { VueDraggable } from 'vue-draggable-plus'
 
 const router = useRouter()
 const filterRef: Ref<HTMLElement | null> = ref(null)
@@ -31,7 +32,19 @@ const handleCreate = () => {
   router.push('/create-report-template')
 }
 
-const fieldsList = ref([])
+interface Field {
+  field: string
+  title: string
+  displayName: string
+  value?: string
+  isFilter: boolean
+  isSort: boolean
+}
+const fieldsList = ref<Field[]>([])
+
+const handleDeleteField = (field: string) => {
+  fieldsList.value = fieldsList.value.filter((item) => item.field !== field)
+}
 
 const CustomizeColumnsRef = ref()
 // 打开定制表格弹窗
@@ -62,10 +75,49 @@ const customizeColumns = async () => {
   })
 }
 
-const radioa = ref('1')
+const newFieldInfo = ref<{
+  name: string
+  fieldType: 'Blank' | 'Fixed Value'
+  value: string
+}>({
+  name: '',
+  fieldType: 'Blank',
+  value: ''
+})
+const addNewFieldVisible = ref(false)
+// 添加新字段
+const handleAddNewField = () => {
+  addNewFieldVisible.value = true
+}
+const handleFieldTypeChange = () => {
+  newFieldInfo.value.value = ''
+}
+const addNewField = () => {
+  if (newFieldInfo.value.name) {
+    fieldsList.value.push({
+      field: newFieldInfo.value.name,
+      title: newFieldInfo.value.name,
+      value: newFieldInfo.value.value,
+      displayName: '',
+      isFilter: false,
+      isSort: false
+    })
+    newFieldInfo.value = {
+      name: '',
+      fieldType: 'Blank',
+      value: ''
+    }
+    addNewFieldVisible.value = false
+  } else {
+    ElMessage.warning('Please enter the new field name.')
+    return
+  }
+}
+
+const reportAccessControlRadio = ref('All Users')
 const detailRef: Ref<HTMLElement | null> = ref(null)
-watch(radioa, (newVal) => {
-  if (newVal === '2') {
+watch(reportAccessControlRadio, (newVal) => {
+  if (newVal === 'Specific Roles') {
     // 等待下一个渲染周期结束后,获取detailRef的高度
     nextTick(() => {
       if (detailRef.value) {
@@ -77,6 +129,9 @@ watch(radioa, (newVal) => {
     })
   }
 })
+
+const loading = ref(false)
+const handleRightRemove = () => {}
 </script>
 <template>
   <div class="dashboard">
@@ -133,14 +188,26 @@ watch(radioa, (newVal) => {
       <div class="fields-configuration template-box">
         <div class="header">
           <span>Report Fields Configuration</span>
-          <el-button
-            v-if="fieldsList.length > 0"
-            class="el-button--dark"
-            @click="handleCustomizeColumns"
-            style="margin-left: auto; width: 110px; padding-top: 11px"
-          >
-            <span style="margin-right: 3px" class="font_family icon-icon_add_b"></span>Add Field
-          </el-button>
+
+          <div class="right-option">
+            <el-button
+              class="el-button--dark"
+              @click="handleAddNewField"
+              style="width: 148px; padding-top: 11px"
+            >
+              <span style="margin-right: 3px" class="font_family icon-icon_add_b"></span>
+              <span>Add New Field</span>
+            </el-button>
+            <el-button
+              v-if="fieldsList.length > 0"
+              class="el-button--dark"
+              @click="handleCustomizeColumns"
+              style="width: 110px; padding-top: 11px"
+            >
+              <span style="margin-right: 3px" class="font_family icon-icon_add_b"></span>
+              <span>Select Field</span>
+            </el-button>
+          </div>
         </div>
         <div class="content-box">
           <div class="empty-box" v-if="fieldsList.length === 0">
@@ -150,25 +217,53 @@ watch(radioa, (newVal) => {
             <p>No field selected. click “Add Field” to get started.</p>
           </div>
           <div class="fields-list" v-else>
-            <div class="field-item" v-for="(field, index) in fieldsList" :key="index">
-              <div class="label">
-                <span style="font-weight: 700">[{{ field.title }}]</span>
-                <span style="margin-left: 8px">{{ field.field }}</span>
-              </div>
-              <el-input class="display-name" placeholder="Display Name in Report"></el-input>
-              <div class="actions">
-                <div><el-checkbox>Filter</el-checkbox> <el-checkbox>Sort</el-checkbox></div>
-                <span class="font_family icon-icon_delete_b"></span>
+            <VueDraggable
+              v-vloading="loading"
+              v-model="fieldsList"
+              class="column-list"
+              ghost-class="ghost-column"
+              :forceFallback="true"
+              fallback-class="fallback-class"
+              group="customizeColumns"
+              item-key="field"
+              @end="handleRightRemove"
+            >
+              <div class="field-item" v-for="fieldItem in fieldsList" :key="fieldItem.field">
+                <span
+                  class="font_family icon-icon_dragsort__b draggable-icon"
+                  style="margin-right: 12px; font-size: 16px"
+                ></span>
+                <div class="label">
+                  <span style="font-weight: 700">[{{ fieldItem.title }}]</span>
+                  <span style="margin-left: 8px">{{
+                    fieldItem.displayName || fieldItem.title
+                  }}</span>
+                </div>
+                <el-input
+                  class="display-name"
+                  v-model="fieldItem.displayName"
+                  placeholder="Display Name in Report"
+                ></el-input>
+                <div class="actions">
+                  <div>
+                    <el-checkbox v-model="fieldItem.isFilter">Filter</el-checkbox>
+                    <el-checkbox v-model="fieldItem.isSort">Sort</el-checkbox>
+                  </div>
+                  <span
+                    @click="handleDeleteField(fieldItem.field)"
+                    class="font_family icon-icon_delete_b"
+                  ></span>
+                </div>
               </div>
-            </div>
+            </VueDraggable>
           </div>
         </div>
       </div>
       <div class="report-access-control template-box">
         <div class="header">Report Access Control</div>
         <div class="content-box">
-          <el-radio-group class="radio-group" v-model="radioa">
-            <el-radio class="radio-item" value="1">
+          <el-radio-group class="radio-group" v-model="reportAccessControlRadio">
+            <el-radio class="radio-item" value="All Users">
               <template #default>
                 <div class="radio-content">
                   <p class="label">All Users</p>
@@ -178,14 +273,18 @@ watch(radioa, (newVal) => {
                 </div>
               </template>
             </el-radio>
-            <el-radio class="radio-item specific-roles" value="2">
+            <el-radio class="radio-item specific-roles" value="Specific Roles">
               <template #default>
                 <div class="radio-content">
                   <div class="top-options">
                     <p class="label">Specific Roles</p>
                     <p class="description">Restrict access to specific user roles</p>
                   </div>
-                  <div class="extended-filter" v-if="radioa === '2'" ref="detailRef">
+                  <div
+                    class="extended-filter"
+                    v-if="reportAccessControlRadio === 'Specific Roles'"
+                    ref="detailRef"
+                  >
                     <div class="dividing-line"></div>
                     <div class="filter-item" style="margin-bottom: 16px">
                       <div class="label">
@@ -209,7 +308,53 @@ watch(radioa, (newVal) => {
         </div>
       </div>
     </div>
+
     <CustomizeColumns @customize="customizeColumns" ref="CustomizeColumnsRef" />
+    <el-dialog
+      class="add-new-field-dialog"
+      title="Add New Field"
+      v-model="addNewFieldVisible"
+      width="480"
+    >
+      <div>
+        <div class="field-item">
+          <div class="label">
+            <span class="required-symbol">*</span>
+            <span>New Field Name</span>
+          </div>
+          <el-input placeholder="Please enter..." v-model="newFieldInfo.name"></el-input>
+        </div>
+        <div class="field-item field-value">
+          <div class="label">
+            <span class="required-symbol">*</span>
+            <span>Field Value</span>
+          </div>
+          <el-radio-group v-model="newFieldInfo.fieldType" @change="handleFieldTypeChange">
+            <el-radio label="Blank">Blank</el-radio>
+            <el-radio label="Fixed Value">Fixed Value</el-radio>
+          </el-radio-group>
+        </div>
+        <div class="field-item" v-if="newFieldInfo.fieldType === 'Fixed Value'">
+          <div class="label">
+            <span class="required-symbol">*</span>
+            <span>Fixed Value</span>
+          </div>
+          <el-input placeholder="Please enter..." v-model="newFieldInfo.value"></el-input>
+        </div>
+      </div>
+      <template #footer>
+        <el-button
+          style="height: 40px; width: 115px"
+          class="cancel-btn"
+          type="default"
+          @click="addNewFieldVisible = false"
+          >Cancel</el-button
+        >
+        <el-button style="height: 40px; width: 120px" class="el-button--dark" @click="addNewField"
+          >Apply</el-button
+        >
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -258,6 +403,9 @@ watch(radioa, (newVal) => {
     font-size: 18px;
     font-weight: bold;
   }
+  .right-option {
+    margin-left: auto;
+  }
   .content-box {
     height: 100%;
     padding: 8px 16px 16px;
@@ -266,7 +414,7 @@ watch(radioa, (newVal) => {
 .fields-configuration {
   div.content-box {
     display: flex;
-    align-items: center;
+    align-items: flex-start;
     justify-content: center;
     min-height: 272px;
     max-height: 400px;
@@ -275,6 +423,7 @@ watch(radioa, (newVal) => {
     padding-right: 0px;
     // overflow: auto;
     .empty-box {
+      align-self: center;
       width: 100%;
       text-align: center;
       p {
@@ -285,15 +434,23 @@ watch(radioa, (newVal) => {
     .fields-list {
       width: 100%;
       max-height: 400px;
-      padding-top: 8px;
+      padding: 8px 0;
       padding-right: 16px;
       overflow: auto;
+      user-select: none;
       .field-item {
         display: flex;
         align-items: center;
+        height: 48px;
         margin-bottom: 8px;
+        padding: 16px;
+        border-radius: 6px;
+        border: 1px solid var(--color-border);
         .label {
           flex: 1;
+          .required-symbol {
+            color: var(--color-danger);
+          }
         }
         .display-name {
           flex: 1.3;
@@ -332,6 +489,21 @@ watch(radioa, (newVal) => {
       }
     }
   }
+  .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;
+  }
 }
 .basic-info {
   .info-item {
@@ -424,3 +596,37 @@ watch(radioa, (newVal) => {
   }
 }
 </style>
+<style lang="scss">
+.add-new-field-dialog {
+  .field-item {
+    margin-bottom: 16px;
+    .label {
+      margin-bottom: 4px;
+    }
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+  .field-value {
+    .el-radio-group {
+      width: 100%;
+      .el-radio {
+        flex: 1;
+        margin-right: 0;
+        padding-left: 12px;
+        border: 1px solid var(--color-border);
+        &:first-child {
+          border-radius: 6px 0 0 6px;
+          border-right: none;
+        }
+        &:last-child {
+          border-radius: 0 6px 6px 0;
+        }
+        .el-radio__label {
+          color: var(--color-neutral-1);
+        }
+      }
+    }
+  }
+}
+</style>

+ 20 - 3
src/views/TemplateManagement/src/components/TableView/src/TableView.vue

@@ -6,7 +6,9 @@ import { autoWidth } from '@/utils/table'
 import { useRowClickStyle } from '@/hooks/rowClickStyle'
 import dayjs from 'dayjs'
 import { formatTimezone, formatNumber } from '@/utils/tools'
+import { useRouter } from 'vue-router'
 
+const router = useRouter()
 const props = defineProps({
   height: {
     type: Number,
@@ -358,6 +360,12 @@ const handleLinkClick = (row) => {
     })
 }
 
+const handleCreate = () => {
+  router.push({
+    name: 'Create Report Template'
+  })
+}
+
 defineExpose({
   SearchOperationLog
 })
@@ -405,15 +413,24 @@ defineExpose({
           style="height: 24px; padding: 8px 4px; padding-left: 5px; font-size: 12px"
         >
           <span
-            style="margin-right: 2px; font-size: 15px"
-            class="font_family icon-icon_delete_b"
+            style="margin-right: 2px; font-size: 16px"
+            class="font_family icon-icon_disablee__b"
+          ></span>
+        </el-button>
+        <el-button
+          class="el-button--blue"
+          style="height: 24px; padding: 8px 4px; padding-left: 5px; font-size: 12px"
+        >
+          <span
+            style="margin-right: 2px; font-size: 13px"
+            class="font_family icon-icon_active"
           ></span>
         </el-button>
       </template>
       <!-- 空数据时的插槽 -->
       <template #empty v-if="!tableLoadingTableData && tableData.data.length === 0">
         <div class="empty-box">
-          <el-button class="el-button--dark">
+          <el-button class="el-button--dark" @click="handleCreate">
             <span class="font_family icon-icon_add_b"></span> Create New Report Template</el-button
           >
           <p>Click the "Create New Report Template" button to add a report template.</p>

部分文件因文件數量過多而無法顯示