Просмотр исходного кода

feat: split payment-info into standalone menu with dual tabs

Remove payment tab from protocol-audit and keep deep-link redirect.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 1 месяц назад
Родитель
Сommit
32c01a4c69

+ 11 - 0
backend/app/api/menu.py

@@ -60,6 +60,17 @@ STATIC_MENU_TREE = [
                         "component": "/cell-storage/presign-order/index",
                         "meta": {"title": "预签约订单", "keepAlive": True, "menuCode": "cell-storage:presign-order:list"},
                     },
+                    {
+                        "id": "904b5173519411f1944300163e3bdd96",
+                        "name": "CellStoragePaymentInfo",
+                        "path": "payment-info",
+                        "component": "/cell-storage/payment-info/index",
+                        "meta": {
+                            "title": "缴费信息",
+                            "keepAlive": True,
+                            "menuCode": "cellStorage:paymentInfo:list",
+                        },
+                    },
                     {
                         "id": "50000000000000000000000000000013",
                         "name": "CellStorageSampleInfo",

+ 11 - 12
web-admin/src/router/modules/sqxp.ts

@@ -71,6 +71,17 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [
               menuCode: 'cell-storage:presign-order:list'
             }
           },
+          {
+            id: '904b5173519411f1944300163e3bdd96',
+            name: 'CellStoragePaymentInfo',
+            path: 'payment-info',
+            component: '/cell-storage/payment-info/index',
+            meta: {
+              title: '缴费信息',
+              keepAlive: true,
+              menuCode: 'cellStorage:paymentInfo:list'
+            }
+          },
           {
             id: '50000000000000000000000000000013',
             name: 'CellStorageSampleInfo',
@@ -82,18 +93,6 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [
               menuCode: 'cell-storage:sample-info:list'
             }
           },
-          {
-            id: '904b5173519411f1944300163e3bdd96',
-            name: 'CellStoragePaymentInfo',
-            path: 'payment-info',
-            component: '/cell-storage/payment-info/index',
-            meta: {
-              title: '缴费信息',
-              keepAlive: false,
-              isHide: true,
-              menuCode: 'cellStorage:paymentInfo:list'
-            }
-          },
           {
             id: '50000000000000000000000000000004',
             name: 'CellStoragePaymentConfirm',

+ 9 - 0
web-admin/src/views/cell-storage/payment-info/PaymentBySampleTab.vue

@@ -0,0 +1,9 @@
+<template>
+  <div class="payment-by-sample-tab">
+    <el-empty description="按样本视图开发中" :image-size="80" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  defineOptions({ name: 'CellStoragePaymentBySampleTab' })
+</script>

+ 77 - 7
web-admin/src/views/cell-storage/payment-info/index.vue

@@ -1,18 +1,88 @@
-<!-- 旧「缴费信息」菜单入口:重定向至采集协议单管理 · 缴费信息 Tab -->
+<!-- 缴费信息(流程看板 / 按样本) -->
 <template>
-  <div />
+  <div class="page-container">
+    <section class="panel">
+      <nav class="pill-tab-nav" role="tablist" aria-label="视图类型">
+        <div class="pill-tab-nav__rail">
+          <div class="pill-tab-nav__progress" :style="pillProgressStyle" />
+          <button
+            v-for="(item, index) in entityTabs"
+            :key="item.key"
+            type="button"
+            class="pill-tab-nav__step"
+            :class="{
+              'is-active': activeTab === item.key,
+              'is-done': index < activeTabIndex
+            }"
+            role="tab"
+            :aria-selected="activeTab === item.key"
+            @click="activeTab = item.key"
+          >
+            <span class="pill-tab-nav__index">{{ index + 1 }}</span>
+            <span class="pill-tab-nav__text">
+              <span class="pill-tab-nav__title">{{ item.title }}</span>
+              <span class="pill-tab-nav__desc">{{ item.desc }}</span>
+            </span>
+          </button>
+        </div>
+      </nav>
+
+      <PaymentInfoTab v-if="activeTab === 'flow'" />
+      <PaymentBySampleTab v-else-if="activeTab === 'by-sample'" />
+    </section>
+  </div>
 </template>
 
 <script setup lang="ts">
+  import PaymentInfoTab from '../protocol-audit/PaymentInfoTab.vue'
+  import PaymentBySampleTab from './PaymentBySampleTab.vue'
+
   defineOptions({ name: 'CellStoragePaymentInfo' })
 
-  const router = useRouter()
+  const VALID_TABS = ['flow', 'by-sample'] as const
+
+  type EntityTabKey = (typeof VALID_TABS)[number]
+
+  const entityTabs: Array<{
+    key: EntityTabKey
+    title: string
+    desc: string
+  }> = [
+    {
+      key: 'flow',
+      title: '流程看板',
+      desc: '按缴费流程状态处理'
+    },
+    {
+      key: 'by-sample',
+      title: '按样本',
+      desc: '一协议一样本的缴费总览'
+    }
+  ]
+
   const route = useRoute()
+  const activeTab = ref<EntityTabKey>('flow')
+
+  const activeTabIndex = computed(() =>
+    Math.max(
+      0,
+      entityTabs.findIndex((item) => item.key === activeTab.value)
+    )
+  )
+
+  const pillProgressStyle = computed(() => ({
+    width: `calc(${100 / entityTabs.length}% - 3px)`,
+    transform: `translateX(${activeTabIndex.value * 100}%)`
+  }))
 
   onMounted(() => {
-    router.replace({
-      name: 'CellStorageProtocolAudit',
-      query: { ...route.query, tab: 'payment-info' }
-    })
+    const tab = String(route.query.tab || '')
+    if (tab === 'payment-info') {
+      activeTab.value = 'flow'
+      return
+    }
+    if (VALID_TABS.includes(tab as EntityTabKey)) {
+      activeTab.value = tab as EntityTabKey
+    }
   })
 </script>

+ 9 - 9
web-admin/src/views/cell-storage/protocol-audit/index.vue

@@ -1,4 +1,4 @@
-<!-- 采集协议单管理(协议单 / 缴费 / 储存证 / 满月待办) -->
+<!-- 采集协议单管理(协议单 / 储存证 / 满月待办) -->
 <template>
   <div class="page-container">
     <section class="panel">
@@ -28,7 +28,6 @@
       </nav>
 
       <ProtocolAuditTab v-if="activeTab === 'protocol'" />
-      <PaymentInfoTab v-else-if="activeTab === 'payment-info'" />
       <StorageCertTab v-else-if="activeTab === 'storage-cert'" />
       <FullmoonTodoTab v-else-if="activeTab === 'fullmoon-todo'" />
     </section>
@@ -37,7 +36,6 @@
 
 <script setup lang="ts">
   import ProtocolAuditTab from './ProtocolAuditTab.vue'
-  import PaymentInfoTab from './PaymentInfoTab.vue'
   import StorageCertTab from './StorageCertTab.vue'
   import FullmoonTodoTab from './FullmoonTodoTab.vue'
 
@@ -45,7 +43,7 @@
 
   const LEGACY_SAMPLE_TABS = ['sample-reception', 'storage-entry'] as const
 
-  const VALID_TABS = ['protocol', 'payment-info', 'storage-cert', 'fullmoon-todo'] as const
+  const VALID_TABS = ['protocol', 'storage-cert', 'fullmoon-todo'] as const
 
   type EntityTabKey = (typeof VALID_TABS)[number]
 
@@ -59,11 +57,6 @@
       title: '协议单管理',
       desc: '协议审核与状态维护'
     },
-    {
-      key: 'payment-info',
-      title: '缴费信息',
-      desc: '缴费计划与到账查询'
-    },
     {
       key: 'storage-cert',
       title: '储存证管理',
@@ -101,6 +94,13 @@
       })
       return
     }
+    if (tab === 'payment-info') {
+      router.replace({
+        name: 'CellStoragePaymentInfo',
+        query: { ...route.query, tab: 'flow' }
+      })
+      return
+    }
     if (VALID_TABS.includes(tab as EntityTabKey)) {
       activeTab.value = tab as EntityTabKey
     }