Răsfoiți Sursa

feat: 实现多语言配置页

Jack Zhou 3 săptămâni în urmă
părinte
comite
9316744ac8
2 a modificat fișierele cu 345 adăugiri și 34 ștergeri
  1. 6 0
      src/styles/theme.scss
  2. 339 34
      src/views/MultilingualConfig/src/MultilingualConfig.vue

+ 6 - 0
src/styles/theme.scss

@@ -396,6 +396,9 @@
   --color-delivery-calendar-th-bg: #f6f8fa;
 
   --color-delivery-date-picker-cell-bg: #b3e5d4;
+
+  --color-multilingual-config-unverified-tag-bg: #fff4d1;
+  --color-multilingual-config-verified-tag-bg: #e8fbe4;
 }
 
 :root.dark {
@@ -664,5 +667,8 @@
   --color-delivery-calendar-th-bg: #343a43;
 
   --color-delivery-date-picker-cell-bg: #22584c;
+  
+  --color-multilingual-config-unverified-tag-bg: #4f462b;
+  --color-multilingual-config-verified-tag-bg: #354a3f;
 }
   

+ 339 - 34
src/views/MultilingualConfig/src/MultilingualConfig.vue

@@ -1,10 +1,45 @@
 <script setup lang="ts">
-import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
+import { type VxeGridProps } from 'vxe-table'
 import { useCalculatingHeight } from '@/hooks/calculatingHeight'
 
+const selectedPage = ref('destination-delivery')
+const pageList = [
+  { label: 'Destination Delivery', value: 'destination-delivery', unverifiedNumber: 1 },
+  { label: 'Page 2', value: 'page-2', unverifiedNumber: 1 },
+  { label: 'Page 3', value: 'page-3', unverifiedNumber: 1 }
+]
+const getCurrentPageUnverifiedNumber = () => {
+  return pageList.find((item) => item.value === selectedPage.value)?.unverifiedNumber || 0
+}
+const searchKey = ref('')
+
 const containerHeight = useCalculatingHeight(document.documentElement, 456, [])
 
-const tableData = ref({
+type RowItem = {
+  key: string
+  english: string
+  englishStatus: number
+  simplifiedChinese: string
+  simplifiedChineseStatus: number
+  traditionalChinese: string
+  traditionalChineseStatus: number
+  french: string
+  frenchStatus: number
+  spanish: string
+  spanishStatus: number
+  portuguese: string
+  portugueseStatus: number
+}
+
+type EditableField =
+  | 'english'
+  | 'simplifiedChinese'
+  | 'traditionalChinese'
+  | 'french'
+  | 'spanish'
+  | 'portuguese'
+
+const tableData = ref<VxeGridProps<RowItem>>({
   border: true,
   round: true,
   columns: [
@@ -19,74 +54,266 @@ const tableData = ref({
     {
       title: 'English',
       field: 'english',
-      width: 300,
+      minWidth: 300,
       slots: {
-        default: 'english'
+        header: 'table_header',
+        default: 'languageVerification'
       }
     },
     {
       title: 'Chinese',
-      field: 'chinese',
-      width: 300,
+      field: 'simplifiedChinese',
+      minWidth: 300,
       slots: {
-        default: 'chinese'
+        header: 'table_header',
+        default: 'languageVerification'
       }
     }
   ],
   cellConfig: {
     height: 68
   },
-  data: [
-    {
-      key: 'Destination Delivery',
-      english: 'Destination Delivery',
-      chinese: '目的地交付'
-    },
-    {
-      key: 'Configurations',
-      english: 'Configurations',
-      chinese: '配置'
-    }
-  ],
   scrollY: { enabled: true, oSize: 20, gt: 30 },
   emptyText: ' ',
   showHeaderOverflow: true,
   showOverflow: true
 })
-const tableRef = ref<VxeGridInstance | null>(null)
 const tableLoadingTableData = ref(false)
 const tableLoadingColumn = ref(false)
+
+const originalTableData = ref([
+  {
+    key: 'username',
+    traditionalChinese: '用戶名', // 繁体中文
+    traditionalChineseStatus: 0, // 0: 未审核, 1: 已审核, 2: 已修改
+    simplifiedChinese: '用户名', // 简体中文
+    simplifiedChineseStatus: 0, // 0: 未审核, 1: 已审核, 2: 已修改
+    english: 'Username', // 英文
+    englishStatus: 0, // 0: 未审核, 1: 已审核, 2: 已修改
+    french: "Nom d'utilisateur", // 法语
+    frenchStatus: 0, // 0: 未审核, 1: 已审核, 2: 已修改
+    spanish: 'Nombre de usuario', // 西班牙语
+    spanishStatus: 0, // 0: 未审核, 1: 已审核, 2: 已修改
+    portuguese: 'Nome de usuário', // 葡萄牙语
+    portugueseStatus: 0 // 0: 未审核, 1: 已审核, 2: 已修改
+  },
+  {
+    key: 'password',
+    traditionalChinese: '密碼', // 繁体中文
+    traditionalChineseStatus: 1, // 0: 未审核, 1: 已审核, 2: 已修改
+    simplifiedChinese: '密码', // 简体中文
+    simplifiedChineseStatus: 0, // 0: 未审核, 1: 已审核, 2: 已修改
+    english: 'Password', // 英文
+    englishStatus: 1, // 0: 未审核, 1: 已审核, 2: 已修改
+    french: 'Mot de passe', // 法语
+    frenchStatus: 0, // 0: 未审核, 1: 已审核, 2: 已修改
+    spanish: 'Contraseña', // 西班牙语
+    spanishStatus: 0, // 0: 未审核, 1: 已审核, 2: 已修改
+    portuguese: 'Senha', // 葡萄牙语
+    portugueseStatus: 0 // 0: 未审核, 1: 已审核, 2: 已修改
+  }
+])
+
+const showTableData = computed(() => {
+  let arr = originalTableData.value.filter((item) => {
+    return item.key.includes(searchKey.value)
+  })
+  const unverifiedFilter = Object.entries(headerSwitchMap.value).find((item) => item[1] === true)
+  if (unverifiedFilter) {
+    arr = arr.filter((item) => {
+      return item[unverifiedFilter[0]] === 0
+    })
+  }
+  console.log(unverifiedFilter, 'unverifiedFilter')
+  return arr
+})
+
+const englishUnverifiedNumber = computed(() => {
+  return showTableData.value.filter((item) => item.englishStatus === 0).length
+})
+const simplifiedChineseUnverifiedNumber = computed(() => {
+  return showTableData.value.filter((item) => item.simplifiedChineseStatus === 0).length
+})
+const traditionalChineseUnverifiedNumber = computed(() => {
+  return showTableData.value.filter((item) => item.traditionalChineseStatus === 0).length
+})
+const frenchUnverifiedNumber = computed(() => {
+  return showTableData.value.filter((item) => item.frenchStatus === 0).length
+})
+const spanishUnverifiedNumber = computed(() => {
+  return showTableData.value.filter((item) => item.spanishStatus === 0).length
+})
+const portugueseUnverifiedNumber = computed(() => {
+  return showTableData.value.filter((item) => item.portugueseStatus === 0).length
+})
+
+const unverifiedNumberMap = computed(() => {
+  return {
+    english: englishUnverifiedNumber.value,
+    simplifiedChinese: simplifiedChineseUnverifiedNumber.value,
+    traditionalChinese: traditionalChineseUnverifiedNumber.value,
+    french: frenchUnverifiedNumber.value,
+    spanish: spanishUnverifiedNumber.value,
+    portuguese: portugueseUnverifiedNumber.value
+  }
+})
+
+const headerSwitchMap = ref<Record<string, boolean>>({
+  traditionalChineseStatus: false,
+  simplifiedChineseStatus: false,
+  englishStatus: false,
+  frenchStatus: false,
+  spanishStatus: false,
+  portugueseStatus: false
+})
+
+const handleSwitchChange = (field: EditableField) => {
+  Object.entries(headerSwitchMap.value).forEach((item) => {
+    if (item[0] !== field) {
+      headerSwitchMap.value[item[0]] = false
+    }
+  })
+}
+
+const currentEditor = ref<{ rowIndex: number | null; field: EditableField | null }>({
+  rowIndex: null,
+  field: null
+})
+const currentEditorValue = ref('')
+const handleEdit = (rowIndex: number, field: EditableField, value: string) => {
+  currentEditor.value = { rowIndex, field }
+  currentEditorValue.value = value
+}
+const handleComfirmEdit = (rowIndex: number, field: EditableField) => {
+  showTableData.value[rowIndex][field] = currentEditorValue.value
+  currentEditor.value = { rowIndex: null, field: null }
+  currentEditorValue.value = ''
+}
+const handleCancelEdit = () => {
+  currentEditor.value = { rowIndex: null, field: null }
+  currentEditorValue.value = ''
+}
 </script>
 
 <template>
   <div class="multilingual-config">
     <div class="header">
-      <span>Destination Delivery</span>
+      <span>Multilingual Config</span>
+    </div>
+    <div class="page-filter">
+      <div class="top-content">
+        <span class="font_family icon-icon_page_b" style="font-size: 24px"></span>
+        <span style="margin: 0 8px; font-size: 18px; font-weight: 700">Select Page</span>
+        <el-select
+          v-model="selectedPage"
+          placeholder="Select Page"
+          style="width: 400px; height: 48px"
+        >
+          <template #label="{ label }">
+            <div style="display: flex; align-items: center">
+              <span style="font-size: 18px">{{ label }}</span>
+              <div class="page-select-unverified-tag">
+                <span> {{ getCurrentPageUnverifiedNumber() }} Unverified</span>
+              </div>
+            </div>
+          </template>
+          <el-option
+            v-for="item in pageList"
+            :value="item.value"
+            :label="item.label"
+            :key="item.value"
+          >
+            <div style="display: flex; align-items: center">
+              <span>{{ item.label }}</span>
+              <div class="page-item-unverified-tag" style="margin-left: 20px">
+                <span style="font-size: 10px">
+                  {{ getCurrentPageUnverifiedNumber() }} Unverified</span
+                >
+              </div>
+            </div>
+          </el-option>
+        </el-select>
+      </div>
     </div>
+    <div class="label">Filter</div>
+    <el-input
+      placeholder="Search key..."
+      v-model="searchKey"
+      style="margin-left: 24px; width: 400px"
+    >
+      <template #prefix>
+        <span class="font_family icon-icon_search_b"></span>
+      </template>
+    </el-input>
     <el-divider style="width: calc(100% - 48px); margin: 8px auto" />
     <div class="config-table">
       <vxe-grid
         ref="tableRef"
         v-vloading="tableLoadingTableData || tableLoadingColumn"
         :height="containerHeight"
+        :data="showTableData"
         :style="{ border: 'none' }"
         v-bind="tableData"
       >
+        <template #table_header="{ column }">
+          <div style="display: flex; align-items: center; width: 100%">
+            <span>{{ column.title }}</span>
+            <span class="unverified-number">{{
+              unverifiedNumberMap[column.field as keyof typeof unverifiedNumberMap]
+            }}</span>
+            <el-switch
+              v-model="headerSwitchMap[(column.field + 'Status') as keyof typeof headerSwitchMap]"
+              active-text="Unverified only"
+              inactive-text=""
+              @change="handleSwitchChange((column.field + 'Status') as EditableField)"
+            />
+          </div>
+        </template>
+
         <template #key="{ row }">
           <span>{{ row.key }}</span>
         </template>
-        <template #english="{ row }">
-          <span>{{ row.english }}</span>
-          <div class="unverified-tag">
-            <span class="font_family icon-icon_active" style="font-size: 9px"></span>
-            <span>Unverified</span>
+        <template #languageVerification="{ row, column, rowIndex }">
+          <div
+            class="show-content"
+            v-if="currentEditor.rowIndex !== rowIndex || currentEditor.field !== column.field"
+          >
+            <span>{{ row[column.field as EditableField] }}</span>
+            <span
+              class="font_family icon-icon_edit_b edit-icon"
+              style="display: none"
+              @click="
+                handleEdit(
+                  rowIndex,
+                  column.field as EditableField,
+                  row[column.field as EditableField]
+                )
+              "
+            ></span>
           </div>
-        </template>
-        <template #chinese="{ row }">
-          <span>{{ row.chinese }}</span>
-          <div class="verified-tag">
+          <div class="editor-input" v-else>
+            <el-input v-model="currentEditorValue" style="height: 28px; margin-bottom: 3px">
+              <template #suffix>
+                <span
+                  class="font_family icon-icon_reject_b"
+                  style="margin-top: 1px; margin-right: 10px; font-size: 16px"
+                  @click="handleCancelEdit"
+                ></span>
+                <span
+                  class="font_family icon-icon_confirm_b"
+                  style="margin-top: 1px; font-size: 18px; color: #ed6d00; cursor: pointer"
+                  @click="handleComfirmEdit(rowIndex, column.field as EditableField)"
+                ></span>
+              </template>
+            </el-input>
+          </div>
+          <div class="unverified-tag" v-if="row[(column.field as EditableField) + 'Status'] === 0">
             <span class="font_family icon-icon_delay_b" style="font-size: 14px"></span>
-            <span>Verified</span>
+            <span style="display: inline-block; margin-top: 1px">Unverified</span>
+          </div>
+          <div class="verified-tag" v-if="row[(column.field as EditableField) + 'Status'] === 1">
+            <span class="font_family icon-icon_active" style="font-size: 9px"></span>
+            <span style="display: inline-block; margin-top: 1px">Verified</span>
           </div>
         </template>
       </vxe-grid>
@@ -99,6 +326,39 @@ const tableLoadingColumn = ref(false)
   position: relative;
   padding-bottom: 40px;
   background-color: var(--color-mode);
+  .page-filter {
+    margin: 16px 24px 24px;
+    border-radius: 12px;
+    .top-content {
+      display: flex;
+      align-items: center;
+      height: 80px;
+      padding: 28px 16px;
+      background-color: var(--color-shipment-status-header-bg);
+      :deep(.el-select__wrapper) {
+        height: 48px;
+      }
+    }
+  }
+  .label {
+    margin-left: 24px;
+    margin-bottom: 12px;
+    font-size: 18px;
+    font-weight: 700;
+  }
+  .page-select-unverified-tag {
+    display: inline-flex;
+    height: 20px;
+    margin-left: 8px;
+    padding: 5px 7px;
+    line-height: 11px;
+    border-radius: 3px;
+    background-color: var(--color-multilingual-config-unverified-tag-bg);
+    span {
+      color: var(--color-steps-current-icon-color);
+      font-size: 10px;
+    }
+  }
 }
 .header {
   position: sticky;
@@ -117,18 +377,63 @@ const tableLoadingColumn = ref(false)
 
 .config-table {
   padding: 0 24px;
+  .unverified-number {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    height: 20px;
+    width: 20px;
+    margin-top: 1px;
+    margin-left: 8px;
+    margin-right: 40px;
+    border-radius: 50%;
+    font-size: 10px;
+    color: var(--color-theme);
+    background-color: var(--color-tag-unfinished-approval-bg);
+  }
   .unverified-tag,
   .verified-tag {
-    display: flex;
+    display: inline-flex;
     height: 16px;
     padding: 2px 4px;
     font-size: 10px;
+    line-height: 11px;
+    border-radius: 3px;
   }
   .unverified-tag {
-    background-color: var(--color-warning-tips-bg);
+    padding-left: 2px;
+    background-color: var(--color-multilingual-config-unverified-tag-bg);
+
+    span {
+      color: var(--color-steps-current-icon-color);
+    }
   }
   .verified-tag {
-    background-color: var(--color-tag-confirmed-bg);
+    background-color: var(--color-multilingual-config-verified-tag-bg);
+    .font_family {
+      margin-right: 2px;
+    }
+    span {
+      color: #5bb462;
+    }
+  }
+  :deep(.vxe-cell--title) {
+    width: 100%;
+  }
+  :deep(.el-switch__label--right) {
+    margin-left: 4px;
+  }
+}
+
+.show-content {
+  &:hover {
+    .edit-icon {
+      display: block !important;
+      float: right;
+      color: #ed6d00;
+      font-size: 18px;
+      cursor: pointer;
+    }
   }
 }
 </style>