5 Комити b8fec497eb ... 82e1ab1a73

Аутор SHA1 Порука Датум
  csk 82e1ab1a73 券码回收:新增回收记录弹窗,已过期券码改为全量回收并优化导入提示 пре 1 месец
  csk 9b6915e4f5 同步 FSD §7 券码审核:合并为待审核/已审核两个 Tab,新增审核状态列,类型仅保留导入 пре 1 месец
  csk f92da779d6 券码审核:合并审核通过/驳回为「已审核」Tab,列表新增审核状态列 пре 1 месец
  csk 591edc8d2c 券码审核类型暂时仅保留「导入」,回收相关类型先去掉 пре 1 месец
  csk 7df74d6e67 信用卡权益"设置显示页面"改为"编辑",新增是否启用/权益名称/权益图标,列表与批次增加分页 пре 1 месец

BIN
.resources/image-1780651125157.png


BIN
.resources/image-1780651234602.png


BIN
.resources/image-1780651374993.png


+ 10 - 0
diagrams/2026-06-07T022454/diagram.mmd

@@ -0,0 +1,10 @@
+sequenceDiagram
+    participant FE as 前端
+    participant BE as 后端
+    participant OSS as OSS存储
+    FE->>BE: 上传图片
+    BE->>BE: 校验格式
+    BE->>OSS: 保存原图
+    OSS-->>BE: 返回URL
+    BE->>BE: 生成水印图
+    BE-->>FE: 返回水印图URL

BIN
diagrams/2026-06-07T022454/diagram.png


+ 99 - 6
工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/components/ShowPagesDialog.vue

@@ -1,25 +1,31 @@
 <template>
   <el-dialog
-    title="设置显示页面"
+    title="编辑"
     v-model="dialogVisible"
     width="560px"
     @open="onOpen"
   >
     <el-form :model="form" label-width="140px" label-position="left">
-      <el-form-item label="权益首页/列表页">
+      <el-form-item label="是否启用">
+        <el-radio-group v-model="form.enabled">
+          <el-radio label="不启用">不启用</el-radio>
+          <el-radio label="启用">启用</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="权益首页">
         <el-radio-group v-model="form.showOnHome">
           <el-radio label="显示">显示</el-radio>
           <el-radio label="不显示">不显示</el-radio>
         </el-radio-group>
       </el-form-item>
-      <el-form-item label="组装页">
+      <el-form-item label="组装页" class="assembly-item">
         <el-radio-group v-model="form.showOnAssembly">
           <el-radio label="显示">显示</el-radio>
           <el-radio label="不显示">不显示</el-radio>
         </el-radio-group>
         <div
           v-if="form.showOnAssembly === '显示'"
-          style="background: #f1f2f5;padding: 16px;margin-top: 10px;"
+          class="assembly-select-box"
         >
           <el-select
             v-model="form.assemblyPages"
@@ -37,7 +43,36 @@
           </el-select>
         </div>
       </el-form-item>
+      <el-form-item label="权益名称" required>
+        <el-input
+          v-model="form.equityName"
+          maxlength="40"
+          placeholder="请输入权益名称"
+          style="width: 100%"
+        ></el-input>
+      </el-form-item>
+      <el-form-item label="权益图标" required>
+        <el-upload
+          action=""
+          :auto-upload="false"
+          :file-list="form.listIcon"
+          :on-change="file => handleUpload(file, 'listIcon')"
+          :on-preview="handlePreviewImage"
+          :on-remove="() => form.listIcon = []"
+          :class="{ 'upload-hide-trigger': form.listIcon.length >= 1 }"
+          :limit="1"
+          list-type="picture-card"
+        >
+          <el-icon><Plus /></el-icon>
+        </el-upload>
+        <div class="upload-size-tip">建议尺寸:80x80px</div>
+      </el-form-item>
     </el-form>
+
+    <el-dialog v-model="previewVisible" title="预览" width="400px" append-to-body>
+      <img v-if="previewImageUrl" :src="previewImageUrl" style="width: 100%" />
+    </el-dialog>
+
     <template #footer>
       <span>
         <el-button @click="dialogVisible = false">取消</el-button>
@@ -49,6 +84,7 @@
 
 <script>
 import request from '@/utils/request'
+import { ElMessage } from 'element-plus'
 
 export default {
   name: 'ShowPagesDialog',
@@ -60,10 +96,15 @@ export default {
   data() {
     return {
       assemblyPageOptions: [],
+      previewVisible: false,
+      previewImageUrl: '',
       form: {
+        enabled: '不启用',
         showOnHome: '显示',
         showOnAssembly: '不显示',
-        assemblyPages: []
+        assemblyPages: [],
+        equityName: '',
+        listIcon: []
       }
     }
   },
@@ -85,21 +126,73 @@ export default {
     onOpen() {
       this.fetchAssemblyPages()
       if (this.row) {
+        this.form.enabled = this.row.enabled || '不启用'
         this.form.showOnHome = this.row.showOnHome || '显示'
         this.form.showOnAssembly = this.row.showOnAssembly || '不显示'
         this.form.assemblyPages = Array.isArray(this.row.assemblyPages)
           ? [...this.row.assemblyPages]
           : []
+        this.form.equityName = this.row.equityName || ''
+        this.form.listIcon = this.row.equityIcon
+          ? [{ name: '权益图标', url: this.row.equityIcon }]
+          : []
       } else {
+        this.form.enabled = '不启用'
         this.form.showOnHome = '显示'
         this.form.showOnAssembly = '不显示'
         this.form.assemblyPages = []
+        this.form.equityName = ''
+        this.form.listIcon = []
       }
     },
+    handleUpload(file, field) {
+      if (!file || !file.raw) return
+      const reader = new FileReader()
+      reader.onload = (e) => {
+        this.form[field] = [{ name: file.name, url: e.target.result, uid: file.uid }]
+      }
+      reader.readAsDataURL(file.raw)
+    },
+    handlePreviewImage(file) {
+      this.previewImageUrl = file.url || ''
+      this.previewVisible = !!this.previewImageUrl
+    },
     handleConfirm() {
-      this.$emit('confirm', { ...this.form, assemblyPages: [...this.form.assemblyPages] })
+      if (!this.form.equityName) {
+        ElMessage.warning('请填写权益名称')
+        return
+      }
+      if (!this.form.listIcon.length) {
+        ElMessage.warning('请上传权益图标')
+        return
+      }
+      const equityIcon = this.form.listIcon.length ? this.form.listIcon[0].url : ''
+      this.$emit('confirm', {
+        ...this.form,
+        assemblyPages: [...this.form.assemblyPages],
+        equityIcon
+      })
       this.dialogVisible = false
     }
   }
 }
 </script>
+
+<style scoped>
+.assembly-item >>> .el-form-item__content { flex-wrap: wrap; }
+.assembly-select-box {
+  width: 100%;
+  box-sizing: border-box;
+  background: #f1f2f5;
+  padding: 16px;
+  margin-top: 10px;
+}
+.upload-hide-trigger >>> .el-upload--picture-card { display: none; }
+.upload-size-tip {
+  width: 100%;
+  margin-top: 4px;
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.5;
+}
+</style>

+ 120 - 2
工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/mock/index.js

@@ -1573,6 +1573,118 @@ const couponAuditList = [
     auditTime: '',
     auditStatus: 'pending'
   },
+  {
+    id: 'CPA202606010001',
+    type: 'import',
+    productName: '权益C',
+    productId: 'Q100003',
+    importCount: 1500,
+    effectiveTime: '2026-06-08 00:00:00',
+    expireTime: '2026-12-31 23:59:59',
+    creator: '王五',
+    createTime: '2026-06-01 09:15:00',
+    auditorName: '',
+    auditTime: '',
+    auditStatus: 'pending'
+  },
+  {
+    id: 'CPA202605310001',
+    type: 'recycle_wrong',
+    productName: '权益A',
+    productId: 'Q100001',
+    importCount: 20,
+    effectiveTime: '2026-04-16 00:00:00',
+    expireTime: '2026-12-31 23:59:59',
+    creator: '李四',
+    createTime: '2026-05-31 14:22:00',
+    auditorName: '',
+    auditTime: '',
+    auditStatus: 'pending'
+  },
+  {
+    id: 'CPA202605300001',
+    type: 'import',
+    productName: '权益D',
+    productId: 'Q100004',
+    importCount: 800,
+    effectiveTime: '2026-06-15 00:00:00',
+    expireTime: '2026-11-30 23:59:59',
+    creator: '赵六',
+    createTime: '2026-05-30 10:05:00',
+    auditorName: '',
+    auditTime: '',
+    auditStatus: 'pending'
+  },
+  {
+    id: 'CPA202605290001',
+    type: 'recycle_unissued',
+    productName: '权益A',
+    productId: 'Q100001',
+    importCount: 60,
+    effectiveTime: '2026-04-16 00:00:00',
+    expireTime: '2026-12-31 23:59:59',
+    creator: '张三',
+    createTime: '2026-05-29 16:48:00',
+    auditorName: '',
+    auditTime: '',
+    auditStatus: 'pending'
+  },
+  {
+    id: 'CPA202605270001',
+    type: 'import',
+    productName: '微信立减金 5元(满10减5)',
+    productId: 'Q100007',
+    importCount: 3000,
+    effectiveTime: '2026-06-01 00:00:00',
+    expireTime: '2026-09-30 23:59:59',
+    creator: '李四',
+    createTime: '2026-05-27 11:30:00',
+    auditorName: '',
+    auditTime: '',
+    auditStatus: 'pending'
+  },
+  {
+    id: 'CPA202605260001',
+    type: 'recycle_expired',
+    productName: '权益B',
+    productId: 'Q100002',
+    importCount: 35,
+    effectiveTime: '2026-04-21 00:00:00',
+    expireTime: '2026-05-20 23:59:59',
+    creator: '王五',
+    createTime: '2026-05-26 09:55:00',
+    auditorName: '',
+    auditTime: '',
+    auditStatus: 'pending'
+  },
+  {
+    id: 'CPA202605250001',
+    type: 'import',
+    productName: '权益E',
+    productId: 'Q100005',
+    importCount: 1200,
+    effectiveTime: '2026-06-01 00:00:00',
+    expireTime: '2026-10-31 23:59:59',
+    creator: '张三',
+    createTime: '2026-05-25 15:12:00',
+    auditorName: '',
+    auditTime: '',
+    auditStatus: 'pending'
+  },
+  {
+    id: 'CPA202605240001',
+    type: 'recycle_unissued',
+    productName: '权益C',
+    productId: 'Q100003',
+    importCount: 90,
+    effectiveTime: '2026-05-25 00:00:00',
+    expireTime: '2026-10-31 23:59:59',
+    creator: '赵六',
+    createTime: '2026-05-24 10:40:00',
+    auditorName: '',
+    auditTime: '',
+    auditStatus: 'pending'
+  },
   {
     id: 'CPA202605220001',
     type: 'recycle_wrong',
@@ -1593,8 +1705,14 @@ Mock.mock(/\/api\/coupon-audit\/list/, 'get', (options) => {
   const url = new URL(options.url, 'http://localhost')
   const auditStatus = url.searchParams.get('auditStatus') || ''
   const productId = url.searchParams.get('productId') || ''
-  let filtered = couponAuditList
-  if (auditStatus) filtered = filtered.filter(a => a.auditStatus === auditStatus)
+  // 券码审核类型暂时仅保留「导入」,回收相关类型先去掉
+  let filtered = couponAuditList.filter(a => a.type === 'import')
+  if (auditStatus === 'reviewed') {
+    // 已审核 = 审核通过 + 审核驳回
+    filtered = filtered.filter(a => a.auditStatus === 'approved' || a.auditStatus === 'rejected')
+  } else if (auditStatus) {
+    filtered = filtered.filter(a => a.auditStatus === auditStatus)
+  }
   if (productId) filtered = filtered.filter(a => a.productId === productId)
   return { code: 200, data: filtered, msg: 'ok' }
 })

+ 13 - 3
工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/AuditList.vue

@@ -23,7 +23,7 @@
         </div>
       </div>
 
-      <el-table :data="tableData" stripe style="width: 100%">
+      <el-table :data="pagedTableData" stripe style="width: 100%">
         <el-table-column label="操作" min-width="140" fixed="left">
           <template #default="scope">
             <el-button
@@ -67,8 +67,9 @@
       <div class="pagination-section">
         <span>Total {{ tableData.length }}</span>
         <el-pagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
           layout="prev, pager, next, sizes, jumper"
-          :page-size="10"
           :page-sizes="[10, 20, 50]"
           :total="tableData.length"
         ></el-pagination>
@@ -89,7 +90,15 @@ export default {
         bizName: '',
         bizId: ''
       },
-      tableData: []
+      tableData: [],
+      currentPage: 1,
+      pageSize: 10
+    }
+  },
+  computed: {
+    pagedTableData() {
+      const start = (this.currentPage - 1) * this.pageSize
+      return this.tableData.slice(start, start + this.pageSize)
     }
   },
   created() {
@@ -113,6 +122,7 @@ export default {
       if (this.searchForm.bizId) params.append('bizId', this.searchForm.bizId)
       request.get('/api/audit/list?' + params.toString()).then(res => {
         this.tableData = res.data || []
+        this.currentPage = 1
       }).catch(() => {})
     },
     handleReset() {

+ 32 - 4
工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/CouponAuditList.vue

@@ -3,8 +3,7 @@
     <el-card>
       <el-tabs v-model="activeTab" @tab-change="loadData">
         <el-tab-pane label="待审核" name="pending"></el-tab-pane>
-        <el-tab-pane label="审核通过" name="approved"></el-tab-pane>
-        <el-tab-pane label="审核驳回" name="rejected"></el-tab-pane>
+        <el-tab-pane label="已审核" name="reviewed"></el-tab-pane>
       </el-tabs>
       <div class="search-section">
         <div class="search-row">
@@ -20,7 +19,7 @@
         </div>
       </div>
 
-      <el-table :data="tableData" stripe style="width: 100%">
+      <el-table :data="pagedTableData" stripe style="width: 100%">
         <el-table-column label="操作" min-width="100" fixed="left">
           <template #default="scope">
             <el-button
@@ -57,13 +56,19 @@
             <span>{{ scope.row.auditTime || '-' }}</span>
           </template>
         </el-table-column>
+        <el-table-column label="审核状态" width="120" align="center">
+          <template #default="scope">
+            <el-tag :type="auditStatusTag(scope.row.auditStatus)" size="small">{{ auditStatusLabel(scope.row.auditStatus) }}</el-tag>
+          </template>
+        </el-table-column>
       </el-table>
 
       <div class="pagination-section">
         <span>Total {{ tableData.length }}</span>
         <el-pagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
           layout="prev, pager, next, sizes, jumper"
-          :page-size="10"
           :page-sizes="[10, 20, 50]"
           :total="tableData.length"
         />
@@ -122,6 +127,8 @@ export default {
       activeTab: 'pending',
       searchForm: { productId: '' },
       tableData: [],
+      currentPage: 1,
+      pageSize: 10,
       auditDialogVisible: false,
       currentRow: null
     }
@@ -132,6 +139,12 @@ export default {
   activated() {
     this.loadData()
   },
+  computed: {
+    pagedTableData() {
+      const start = (this.currentPage - 1) * this.pageSize
+      return this.tableData.slice(start, start + this.pageSize)
+    }
+  },
   methods: {
     loadData() {
       const params = new URLSearchParams()
@@ -139,6 +152,7 @@ export default {
       if (this.searchForm.productId) params.append('productId', this.searchForm.productId)
       request.get('/api/coupon-audit/list?' + params.toString()).then(res => {
         this.tableData = res.data || []
+        this.currentPage = 1
       }).catch(() => {})
     },
     handleReset() {
@@ -161,6 +175,20 @@ export default {
         recycle_wrong: 'danger'
       }[t] || ''
     },
+    auditStatusLabel(s) {
+      return {
+        pending: '待审核',
+        approved: '审核通过',
+        rejected: '审核驳回'
+      }[s] || s
+    },
+    auditStatusTag(s) {
+      return {
+        pending: 'warning',
+        approved: 'success',
+        rejected: 'danger'
+      }[s] || ''
+    },
     openAudit(row) {
       this.currentRow = row
       this.auditDialogVisible = true

+ 13 - 2
工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/CreditCardEquity.vue

@@ -52,7 +52,7 @@
                   <el-button v-if="scope.row.shelfStatus === '已上架'" type="text" size="small">调整显示顺序</el-button>
                 </template>
               </el-popover>
-              <el-button type="text" size="small" @click="openShowPagesDialog(scope.row)">设置显示页面</el-button>
+              <el-button type="text" size="small" @click="openShowPagesDialog(scope.row)">编辑</el-button>
               <el-popover
                 placement="bottom"
                 trigger="click"
@@ -73,6 +73,11 @@
           </template>
         </el-table-column>
         <el-table-column prop="equityName" label="权益名称" min-width="220" show-overflow-tooltip></el-table-column>
+        <el-table-column label="是否启用" width="120">
+          <template #default="scope">
+            <el-tag :type="scope.row.enabled === '启用' ? 'success' : 'info'" size="small">{{ scope.row.enabled || '不启用' }}</el-tag>
+          </template>
+        </el-table-column>
         <el-table-column label="显示页面" min-width="240">
           <template #default="scope">
             <template v-if="visiblePages(scope.row).length">
@@ -140,6 +145,7 @@ export default {
           merchantName: '环亚机场贵宾',
           createTime: '2026-01-10 09:32:15',
           shelfStatus: '已上架',
+          enabled: '不启用',
           showOnHome: '显示',
           showOnAssembly: '不显示',
           assemblyPages: []
@@ -151,6 +157,7 @@ export default {
           merchantName: '尊享高尔夫俱乐部',
           createTime: '2026-01-15 14:08:42',
           shelfStatus: '已上架',
+          enabled: '不启用',
           showOnHome: '显示',
           showOnAssembly: '不显示',
           assemblyPages: []
@@ -162,6 +169,7 @@ export default {
           merchantName: '车之家精洗服务',
           createTime: '2026-02-01 11:20:05',
           shelfStatus: '已上架',
+          enabled: '不启用',
           showOnHome: '显示',
           showOnAssembly: '不显示',
           assemblyPages: []
@@ -213,10 +221,13 @@ export default {
     },
     onShowPagesConfirm(values) {
       if (this.showPagesRow) {
+        this.showPagesRow.enabled = values.enabled
         this.showPagesRow.showOnHome = values.showOnHome
         this.showPagesRow.showOnAssembly = values.showOnAssembly
         this.showPagesRow.assemblyPages = [...values.assemblyPages]
-        ElMessage.success('显示页面已更新')
+        this.showPagesRow.equityName = values.equityName
+        this.showPagesRow.equityIcon = values.equityIcon
+        ElMessage.success('已保存')
       }
     },
     getPopoverInstance(name) {

+ 175 - 17
工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/EquityProductCoupons.vue

@@ -4,7 +4,7 @@
       <template #header>
         <div class="header-title">
           <span>券码管理</span>
-          <span class="product-id">权益名称:{{ equityName }}</span>
+          <span class="product-id">{{ equityName }}</span>
         </div>
       </template>
 
@@ -12,9 +12,12 @@
         <el-button type="primary" @click="openImportDialog">
           <el-icon><Upload /></el-icon>导入券码
         </el-button>
+        <el-button @click="openRecycleRecordDialog">
+          <el-icon><Document /></el-icon>回收记录
+        </el-button>
       </div>
 
-      <el-table :data="batchList" stripe style="width: 100%">
+      <el-table :data="pagedBatchList" stripe style="width: 100%">
         <el-table-column prop="importCount" label="导入数量" width="100"></el-table-column>
         <el-table-column prop="actualImportCount" label="实际导入数量" width="120"></el-table-column>
         <el-table-column prop="failCount" label="导入失败数量" width="120"></el-table-column>
@@ -45,6 +48,17 @@
           </template>
         </el-table-column>
       </el-table>
+
+      <div class="pagination-section">
+        <span>Total {{ batchList.length }}</span>
+        <el-pagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
+          layout="prev, pager, next, sizes, jumper"
+          :page-sizes="[10, 20, 50]"
+          :total="batchList.length"
+        />
+      </div>
     </el-card>
 
     <el-dialog
@@ -66,15 +80,18 @@
 
       <el-form label-width="100px" label-position="left" style="margin-top: 24px;">
         <el-form-item label="回收数量" required>
-          <el-input-number
-            v-model="recycleForm.amount"
-            :min="1"
-            :max="recycleMaxAmount"
-            :placeholder="`输入范围1-${recycleMaxAmount}`"
-            :controls-position="''"
-            style="width: 100%"
-          ></el-input-number>
-          <div class="recycle-tip">本批次{{ currentRecycleTypeLabel }}最多可回收数量:{{ recycleMaxAmount }}</div>
+          <span v-if="recycleForm.type === 'expired'">{{ recycleMaxAmount }}</span>
+          <template v-else>
+            <el-input-number
+              v-model="recycleForm.amount"
+              :min="1"
+              :max="recycleMaxAmount"
+              :placeholder="`输入范围1-${recycleMaxAmount}`"
+              :controls-position="''"
+              style="width: 100%"
+            ></el-input-number>
+            <div class="recycle-tip">本批次{{ currentRecycleTypeLabel }}最多可回收数量:{{ recycleMaxAmount }}</div>
+          </template>
         </el-form-item>
       </el-form>
 
@@ -101,7 +118,11 @@
               <el-icon><Upload /></el-icon>选择文件
             </el-button>
             <template #tip>
-              <div class="el-upload__tip">支持 .xlsx / .csv,仅可上传一个文件</div>
+              <div class="el-upload__tip">
+                <div>*只支持模板格式,请先下载模板再导入</div>
+                <div>*不能导入相同的券码</div>
+                <div>*最多支持50000条,若超过请分次导入</div>
+              </div>
               <el-button type="text" @click="downloadTemplate">
                 <el-icon><Download /></el-icon>模板下载
               </el-button>
@@ -116,6 +137,23 @@
         </span>
       </template>
     </el-dialog>
+
+    <el-dialog title="回收记录" v-model="recycleRecordDialogVisible" width="900px">
+      <el-table :data="recycleRecords" stripe style="width: 100%">
+        <el-table-column prop="recycleTime" label="回收时间" width="180"></el-table-column>
+        <el-table-column prop="type" label="类型" width="150"></el-table-column>
+        <el-table-column prop="operator" label="操作人" width="120"></el-table-column>
+        <el-table-column prop="amount" label="回收数量" width="120"></el-table-column>
+        <el-table-column label="操作" min-width="140">
+          <template #default="scope">
+            <el-button type="text" size="small" @click="downloadRecycleCoupons(scope.row)">下载回收券码</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <template #footer>
+        <el-button @click="recycleRecordDialogVisible = false">关闭</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -128,15 +166,22 @@ export default {
     return {
       importDialogVisible: false,
       recycleDialogVisible: false,
+      recycleRecordDialogVisible: false,
+      currentPage: 1,
+      pageSize: 10,
       recycleRow: null,
+      recycleRecords: [
+        { recycleTime: '2026-05-10 14:22:08', type: '未发放券码回收', operator: '张三', amount: 120 },
+        { recycleTime: '2026-04-28 09:15:33', type: '已过期券码回收', operator: '李四', amount: 80 },
+        { recycleTime: '2026-04-12 16:40:51', type: '未发放券码回收', operator: '王五', amount: 200 }
+      ],
       recycleForm: {
         type: 'unissued',
         amount: undefined
       },
       recycleTypeOptions: [
         { label: '未发放券码回收', value: 'unissued' },
-        { label: '已过期券码回收', value: 'expired' },
-        { label: '导错码券码回收', value: 'wrong' }
+        { label: '已过期券码回收', value: 'expired' }
       ],
       importForm: {
         files: []
@@ -185,6 +230,94 @@ export default {
           fileStatus: '处理失败',
           importTime: '2026-05-01 16:45:30',
           creator: '赵六'
+        },
+        {
+          importCount: 1500,
+          actualImportCount: 1500,
+          failCount: 0,
+          remainingCount: 1280,
+          effectiveTime: '2026-04-10 00:00:00',
+          expireTime: '2026-12-31 23:59:59',
+          fileStatus: '处理成功',
+          importTime: '2026-04-09 09:21:05',
+          creator: '张三'
+        },
+        {
+          importCount: 600,
+          actualImportCount: 598,
+          failCount: 2,
+          remainingCount: 420,
+          effectiveTime: '2026-04-05 00:00:00',
+          expireTime: '2026-11-30 23:59:59',
+          fileStatus: '处理成功',
+          importTime: '2026-04-04 14:02:33',
+          creator: '李四'
+        },
+        {
+          importCount: 300,
+          actualImportCount: 300,
+          failCount: 0,
+          remainingCount: 0,
+          effectiveTime: '2026-03-20 00:00:00',
+          expireTime: '2026-09-30 23:59:59',
+          fileStatus: '处理成功',
+          importTime: '2026-03-19 10:45:12',
+          creator: '王五'
+        },
+        {
+          importCount: 2000,
+          actualImportCount: 1995,
+          failCount: 5,
+          remainingCount: 1620,
+          effectiveTime: '2026-03-15 00:00:00',
+          expireTime: '2026-12-31 23:59:59',
+          fileStatus: '处理成功',
+          importTime: '2026-03-14 16:30:48',
+          creator: '张三'
+        },
+        {
+          importCount: 450,
+          actualImportCount: 450,
+          failCount: 0,
+          remainingCount: 130,
+          effectiveTime: '2026-03-01 00:00:00',
+          expireTime: '2026-08-31 23:59:59',
+          fileStatus: '处理成功',
+          importTime: '2026-02-28 11:18:27',
+          creator: '李四'
+        },
+        {
+          importCount: 800,
+          actualImportCount: 796,
+          failCount: 4,
+          remainingCount: 510,
+          effectiveTime: '2026-02-20 00:00:00',
+          expireTime: '2026-10-31 23:59:59',
+          fileStatus: '处理成功',
+          importTime: '2026-02-19 15:55:09',
+          creator: '王五'
+        },
+        {
+          importCount: 1200,
+          actualImportCount: 1200,
+          failCount: 0,
+          remainingCount: 60,
+          effectiveTime: '2026-02-10 00:00:00',
+          expireTime: '2026-07-31 23:59:59',
+          fileStatus: '处理成功',
+          importTime: '2026-02-09 09:40:51',
+          creator: '赵六'
+        },
+        {
+          importCount: 350,
+          actualImportCount: 348,
+          failCount: 2,
+          remainingCount: 0,
+          effectiveTime: '2026-01-15 00:00:00',
+          expireTime: '2026-06-30 23:59:59',
+          fileStatus: '处理成功',
+          importTime: '2026-01-14 13:27:36',
+          creator: '张三'
         }
       ]
     }
@@ -193,6 +326,10 @@ export default {
     productId() {
       return this.$route.params.id || ''
     },
+    pagedBatchList() {
+      const start = (this.currentPage - 1) * this.pageSize
+      return this.batchList.slice(start, start + this.pageSize)
+    },
     equityName() {
       const map = {
         'Q100001': '权益A',
@@ -217,7 +354,6 @@ export default {
           const ts = row.expireTime ? new Date(row.expireTime).getTime() : 0
           return ts && ts < Date.now() ? Number(row.remainingCount) || 0 : 0
         }
-        case 'wrong': return Number(row.failCount) || 0
         default: return 0
       }
     }
@@ -280,15 +416,22 @@ export default {
     handleExportFail(row) {
       ElMessage.success(`已开始导出失败券码(${row.failCount} 条)`)
     },
+    openRecycleRecordDialog() {
+      this.recycleRecordDialogVisible = true
+    },
+    downloadRecycleCoupons() {
+      ElMessage.success('已开始下载回收券码')
+    },
     resetRecycleForm() {
       this.recycleRow = null
       this.recycleForm = { type: 'unissued', amount: undefined }
     },
     confirmRecycle() {
       const max = this.recycleMaxAmount
-      const amount = Number(this.recycleForm.amount)
+      // 已过期券码回收:数量为文本展示,直接按可回收数量全部回收
+      const amount = this.recycleForm.type === 'expired' ? max : Number(this.recycleForm.amount)
       if (!amount || amount < 1) {
-        ElMessage.warning('请输入回收数量')
+        ElMessage.warning(this.recycleForm.type === 'expired' ? '当前批次没有可回收的已过期券码' : '请输入回收数量')
         return
       }
       if (amount > max) {
@@ -298,6 +441,13 @@ export default {
       if (this.recycleRow && this.recycleForm.type === 'unissued') {
         this.recycleRow.remainingCount = Math.max(0, Number(this.recycleRow.remainingCount) - amount)
       }
+      const typeLabel = (this.recycleTypeOptions.find(o => o.value === this.recycleForm.type) || {}).label || ''
+      this.recycleRecords.unshift({
+        recycleTime: this.formatNow(),
+        type: typeLabel,
+        operator: '当前用户',
+        amount
+      })
       ElMessage.success('回收成功')
       this.recycleDialogVisible = false
     }
@@ -375,4 +525,12 @@ export default {
   justify-content: center;
   gap: 24px;
 }
+
+.pagination-section {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  gap: 20px;
+  margin-top: 20px;
+}
 </style>

+ 8 - 35
工作空间/产研/权益/汇丰全行/需求文档/汇丰借记卡移动端——权益.md

@@ -222,41 +222,14 @@
         
     -   **开启了领取时间限制且超过领取结束时间:** 显示【已失效】,按钮无法点击
         
--   **其他:** 显示【领取权益】
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    <br>
-    
-    ![|50%](../../../../../.resources/image-1776678923546.png)![|50%](../../../../../.resources/image-1776678942001.png)
+
+**其他:** 显示【领取权益】
+
+<br>
+
+<br>
+
+![|50%](../../../../../.resources/image-1776678923546.png)![|50%](../../../../../.resources/image-1776678942001.png)
 
 **(2)领取权益**
 

Разлика између датотеке није приказан због своје велике величине
+ 50 - 33
工作空间/产研/权益/汇丰全行/需求文档/汇丰银行权益中台-FSD.md


Неке датотеке нису приказане због велике количине промена