csk 1 tháng trước cách đây
mục cha
commit
33db17932a
21 tập tin đã thay đổi với 484 bổ sung363 xóa
  1. 20 10
      .claude/skills/pc-demo/references/code-style.md
  2. 6 6
      .claude/skills/pc-demo/references/faq.md
  3. 30 17
      .claude/skills/pc-demo/references/menu-config.md
  4. 18 9
      .claude/skills/pc-demo/references/mock-data.md
  5. 57 30
      .claude/skills/pc-demo/references/page-structure.md
  6. 42 21
      .claude/skills/requirements-analysis/SKILL.md
  7. 44 25
      .claude/skills/requirements-analysis/reference/analysis-template.md
  8. 18 4
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/components/ShowPagesDialog.vue
  9. 84 49
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/AddEquityProduct.vue
  10. 22 23
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/AuditDetail.vue
  11. 22 28
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/AuditEquityProduct.vue
  12. 0 1
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/CampaignAuditDetail.vue
  13. 84 49
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/EditEquityProduct.vue
  14. 0 5
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/EquityGrantCampaign.vue
  15. 1 37
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/EquityGrantCampaignEdit.vue
  16. 0 1
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/EquityGrantCampaignView.vue
  17. 1 12
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/EquityProducts.vue
  18. 0 1
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/GrantTaskDetail.vue
  19. 23 27
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/src/views/ViewEquityProduct.vue
  20. 1 1
      工作空间/产研/权益/汇丰全行/产品原型/pc-demo/vite.config.js
  21. 11 7
      工作空间/产研/权益/汇丰全行/需求文档/汇丰银行权益中台-FSD.md

+ 20 - 10
.claude/skills/pc-demo/references/code-style.md

@@ -2,8 +2,10 @@
 
 ## 组件命名
 
-- PascalCase,如 `OrderList.jsx`
-- 一个页面一个目录,目录下可包含子组件
+-   PascalCase,如 `OrderList.jsx`
+    
+-   一个页面一个目录,目录下可包含子组件
+    
 
 ## 文件组织
 
@@ -16,18 +18,22 @@ pages/
 
 ## 样式方案
 
-- 优先使用 antd 组件自带样式 + CSS Modules
-- 避免全局样式污染
+-   优先使用 antd 组件自带样式 + CSS Modules
+    
+-   避免全局样式污染
+    
 
 ## 注释规范
 
-- 只在复杂业务逻辑处添加注释
-- 不做冗余注释
+-   只在复杂业务逻辑处添加注释
+    
+-   不做冗余注释
+    
 
 ## Hooks 使用
 
-| Hook | 用途 |
-|------|------|
+| Hook | 用途  |
+| --- | --- |
 | `useState` | 管理组件内部状态 |
 | `useEffect` | 处理副作用 |
 | `useContext` | 获取全局状态(如标签页状态) |
@@ -35,5 +41,9 @@ pages/
 
 ## antd 组件使用
 
-- 直接使用,不做二次封装(除非有明确的复用需求)
-- 使用主题色 `#FF6B00` 作为主色调
+-   直接使用,不做二次封装(除非有明确的复用需求)
+    
+-   使用主题色 `#FF6B00` 作为主色调
+    
+
+<br>

+ 6 - 6
.claude/skills/pc-demo/references/faq.md

@@ -2,7 +2,7 @@
 
 记录历史 bug 修复经验,避免重复踩坑。
 
----
+* * *
 
 ## findLabel 函数递归问题
 
@@ -34,19 +34,19 @@ function findLabel(key, items) {
 }
 ```
 
----
+* * *
 
 ## 路由 path 与菜单 key 一致性
 
 App.jsx 中的 Route path 必须与菜单 key 完全一致,包括前导斜杠:
 
-| 类型 | 示例 |
-|------|------|
+| 类型  | 示例  |
+| --- | --- |
 | 菜单 key | `/purchase/order` |
 | 路由 path | `/purchase/order`(不是 `purchase/order`) |
 
----
+* * *
 
 ## 标签页状态管理
 
-标签页关闭时需判断是否为最后一个可关闭标签,避免关闭后 `activeKey` 指向不存在的标签。
+标签页关闭时需判断是否为最后一个可关闭标签,避免关闭后 `activeKey` 指向不存在的标签。

+ 30 - 17
.claude/skills/pc-demo/references/menu-config.md

@@ -52,23 +52,34 @@ const menuConfig = [
 
 ## 配置规则
 
-1. **key 命名规则**:
-   - 一级菜单:英文单词,如 `sales`、`purchase`、`warehouse`
-   - 二级菜单:`一级-描述`,如 `product-sales`、`purchase-order`
-   - 三级菜单:**必须是路由路径**,如 `/sales/order/list`,因为点击三级菜单会导航到该路由
-
-2. **label 字段**:
-   - 使用简洁中文名称
-   - 三级菜单的 label 会作为导航标签页的显示文字,保持一致
-
-3. **icon 字段**:
-   - 仅一级菜单需要配置图标
-   - 使用 `@ant-design/icons` 提供的图标
-
-4. **路由与菜单联动**:
-   - 菜单配置和路由配置保持同步
-   - 新增菜单项时同步新增路由
-   - 三级菜单的 key 必须与路由 path 完全一致
+1.  **key 命名规则**:
+    
+    -   一级菜单:英文单词,如 `sales`、`purchase`、`warehouse`
+        
+    -   二级菜单:`一级-描述`,如 `product-sales`、`purchase-order`
+        
+    -   三级菜单:**必须是路由路径**,如 `/sales/order/list`,因为点击三级菜单会导航到该路由
+        
+2.  **label 字段**:
+    
+    -   使用简洁中文名称
+        
+    -   三级菜单的 label 会作为导航标签页的显示文字,保持一致
+        
+3.  **icon 字段**:
+    
+    -   仅一级菜单需要配置图标
+        
+    -   使用 `@ant-design/icons` 提供的图标
+        
+4.  **路由与菜单联动**:
+    
+    -   菜单配置和路由配置保持同步
+        
+    -   新增菜单项时同步新增路由
+        
+    -   三级菜单的 key 必须与路由 path 完全一致
+        
 
 ## 示例:供应链管理菜单
 
@@ -158,3 +169,5 @@ const menuConfig = [
   },
 ];
 ```
+
+<br>

+ 18 - 9
.claude/skills/pc-demo/references/mock-data.md

@@ -2,13 +2,20 @@
 
 ## 设计原则
 
-1. 每个列表页对应一个 mock 数据文件,放在 `src/mock/` 目录下
-2. 使用工厂函数生成数据,默认生成 100 条
-3. 数据字段要贴合业务实际,不要使用 "test1"、"aaa" 等无意义数据
-4. ID 使用递增数字或带业务前缀的编号(如 `SO20260401001`)
-5. 日期使用合理的时间范围(如近 3 个月内)
-6. 金额使用合理的数值范围
-7. 状态字段使用枚举值,并配备对应的中文映射
+1.  每个列表页对应一个 mock 数据文件,放在 `src/mock/` 目录下
+    
+2.  使用工厂函数生成数据,默认生成 100 条
+    
+3.  数据字段要贴合业务实际,不要使用 "test1"、"aaa" 等无意义数据
+    
+4.  ID 使用递增数字或带业务前缀的编号(如 `SO20260401001`)
+    
+5.  日期使用合理的时间范围(如近 3 个月内)
+    
+6.  金额使用合理的数值范围
+    
+7.  状态字段使用枚举值,并配备对应的中文映射
+    
 
 ## 数据生成模板
 
@@ -113,11 +120,13 @@ const filteredData = useMemo(() => {
 ## 命名约定
 
 | 业务模块 | 文件名 | 导出函数 |
-|---------|--------|---------|
+| --- | --- | --- |
 | 销售订单 | `orderList.js` | `generateOrderList` |
 | 客户管理 | `customerList.js` | `generateCustomerList` |
 | 采购单 | `purchaseList.js` | `generatePurchaseList` |
 | 供应商 | `supplierList.js` | `generateSupplierList` |
-| 库存 | `inventoryList.js` | `generateInventoryList` |
+| 库存  | `inventoryList.js` | `generateInventoryList` |
 | 入库单 | `inboundList.js` | `generateInboundList` |
 | 出库单 | `outboundList.js` | `generateOutboundList` |
+
+<br>

+ 57 - 30
.claude/skills/pc-demo/references/page-structure.md

@@ -4,7 +4,7 @@
 
 整体页面分为三大块:**左侧菜单**、**顶部导航标签栏**、**右侧页面内容区**。使用 antd 的 `Layout` 组件实现。
 
----
+* * *
 
 ## 一、左侧菜单
 
@@ -12,17 +12,23 @@
 
 ### 结构要求
 
-- **顶部**:系统名称区,展示 **"PC后台"** 字样,居中显示,高度 56px
-- **中部**:三级菜单区,使用 antd Menu 的 `items` 配置,支持展开/折叠
-  - 一级菜单:如"销售中心"、"采购中心"、"仓储中心"
-  - 二级菜单:如"商品销售"、"销售退货"
-  - 三级菜单:如"订单列表"、"退货单列表"(点击三级菜单才打开页面)
-- **底部**:固定定位,高度 60px,展示"个人中心"和"消息中心"图标入口
+-   **顶部**:系统名称区,展示 **"PC后台"** 字样,居中显示,高度 56px
+    
+-   **中部**:三级菜单区,使用 antd Menu 的 `items` 配置,支持展开/折叠
+    
+    -   一级菜单:如"销售中心"、"采购中心"、"仓储中心"
+        
+    -   二级菜单:如"商品销售"、"销售退货"
+        
+    -   三级菜单:如"订单列表"、"退货单列表"(点击三级菜单才打开页面)
+        
+-   **底部**:固定定位,高度 60px,展示"个人中心"和"消息中心"图标入口
+    
 
 ### 菜单样式规范
 
-| 层级 | 字号 | 字重 | 选中效果 |
-|------|------|------|----------|
+| 层级  | 字号  | 字重  | 选中效果 |
+| --- | --- | --- | --- |
 | 一级菜单 | 15px | 500 | 主题色 #FF6B00 |
 | 二级菜单 | 14px | 400 | 主题色 #FF6B00 |
 | 三级菜单 | 13px | 400 | 主题色 #FF6B00 |
@@ -31,7 +37,7 @@
 
 集中管理在 `router/index.jsx`,包含菜单结构和路由映射。详见 [menu-config.md](./menu-config.md)。
 
----
+* * *
 
 ## 二、顶部导航标签栏
 
@@ -39,35 +45,56 @@
 
 ### 核心规则
 
-1. 导航栏只有一行,水平排列所有已打开的页面标签
-2. 标签文字与页面标题一致(取自菜单配置中的 `label` 字段)
-3. **首页标签**永远存在,不可关闭,不显示关闭按钮
-4. **其他标签**:鼠标悬浮时显示关闭按钮(X),不悬浮时隐藏关闭按钮
-5. 点击标签切换到对应页面
-6. **右键菜单**:右键点击任意标签,弹出上下文菜单,包含:
-   - 关闭所有(除首页外全部关闭)
-   - 关闭其他(关闭除当前标签和首页外的所有标签)
-   - 关闭左侧(关闭当前标签左侧所有可关闭标签)
-   - 关闭右侧(关闭当前标签右侧所有可关闭标签)
-7. 标签过多时支持左右滚动
+1.  导航栏只有一行,水平排列所有已打开的页面标签
+    
+2.  标签文字与页面标题一致(取自菜单配置中的 `label` 字段)
+    
+3.  **首页标签**永远存在,不可关闭,不显示关闭按钮
+    
+4.  **其他标签**:鼠标悬浮时显示关闭按钮(X),不悬浮时隐藏关闭按钮
+    
+5.  点击标签切换到对应页面
+    
+6.  **右键菜单**:右键点击任意标签,弹出上下文菜单,包含:
+    
+    -   关闭所有(除首页外全部关闭)
+        
+    -   关闭其他(关闭除当前标签和首页外的所有标签)
+        
+    -   关闭左侧(关闭当前标签左侧所有可关闭标签)
+        
+    -   关闭右侧(关闭当前标签右侧所有可关闭标签)
+        
+7.  标签过多时支持左右滚动
+    
 
 ### 实现要点
 
-- 使用 antd 的 `Tabs` 组件 或 自定义 div 实现
-- 右键菜单使用 antd 的 `Dropdown` 组件
-- 标签状态通过 React Context 全局管理
+-   使用 antd 的 `Tabs` 组件 或 自定义 div 实现
+    
+-   右键菜单使用 antd 的 `Dropdown` 组件
+    
+-   标签状态通过 React Context 全局管理
+    
 
----
+* * *
 
 ## 三、页面内容区
 
 ### 布局规范
 
-- 菜单、导航栏固定不滚动
-- 分页组件固定在页面底部,不随列表滚动
-- 列表区域内部滚动,表头固定
+-   菜单、导航栏固定不滚动
+    
+-   分页组件固定在页面底部,不随列表滚动
+    
+-   列表区域内部滚动,表头固定
+    
 
 ### 滚动实现
 
-- 整个内容区使用 `height: 100vh; overflow: hidden`
-- 列表 Table 使用 `scroll={{ x, y: 'calc(100vh - 300px)' }}` 实现内部滚动
+-   整个内容区使用 `height: 100vh; overflow: hidden`
+    
+-   列表 Table 使用 `scroll={{ x, y: 'calc(100vh - 300px)' }}` 实现内部滚动
+    
+
+<br>

+ 42 - 21
.claude/skills/requirements-analysis/SKILL.md

@@ -2,40 +2,53 @@
 name: requirements-analysis
 description: 需求分析 Skill - 在 PRD 撰写前进行需求调研、分析和确认。当用户说"需求分析"、"分析需求"、"撰写需求分析"、"需求调研"时调用此 skill。
 ---
-
 # 需求分析专家
 
 ## 工作原则
 
-1. **强制使用模板**:严格按照五段式结构输出(需求背景→目标确认→流程分析→功能拆解→待确认事项),不得省略章节
-2. **模板文件**:`reference/analysis-template.md`(需求分析模板),进行分析时必须按此模板要求编写
-3. **系统现状文件**:`reference/system-status.md`,拆解功能清单时必须参考此文件,了解现有系统功能和模块划分
-4. **干系人确认**:必须与相关干系人/用户确认需求,不得基于猜测撰写
-5. **Mermaid 强制**:业务流程必须使用 Mermaid 语法绘制,包含现状流程和目标流程
-6. **结构化输出**:功能拆解清单必须按表格格式输出,明确优先级
-7. **识别待确认项**:主动识别需求中尚未确认的事项,并记录在"待确认事项"章节
-
----
+1.  **强制使用模板**:严格按照五段式结构输出(需求背景→目标确认→流程分析→功能拆解→待确认事项),不得省略章节
+    
+2.  **模板文件**:`reference/analysis-template.md`(需求分析模板),进行分析时必须按此模板要求编写
+    
+3.  **系统现状文件**:`reference/system-status.md`,拆解功能清单时必须参考此文件,了解现有系统功能和模块划分
+    
+4.  **干系人确认**:必须与相关干系人/用户确认需求,不得基于猜测撰写
+    
+5.  **Mermaid 强制**:业务流程必须使用 Mermaid 语法绘制,包含现状流程和目标流程
+    
+6.  **结构化输出**:功能拆解清单必须按表格格式输出,明确优先级
+    
+7.  **识别待确认项**:主动识别需求中尚未确认的事项,并记录在"待确认事项"章节
+    
+
+* * *
 
 ## 输出规范
 
-1. **禁止模糊表述**:不得出现"可能"、"大概"、"待定"等未确认的描述
-2. **数据流向闭环**:每个流程节点有明确的输入和输出
-3. **Mermaid 强制性**:涉及条件分支必须用 Mermaid 流程图
-4. **优先级明确**:所有功能必须标注优先级(P0/P1/P2)
+1.  **禁止模糊表述**:不得出现"可能"、"大概"、"待定"等未确认的描述
+    
+2.  **数据流向闭环**:每个流程节点有明确的输入和输出
+    
+3.  **Mermaid 强制性**:涉及条件分支必须用 Mermaid 流程图
+    
+4.  **优先级明确**:所有功能必须标注优先级(P0/P1/P2)
+    
 
----
+* * *
 
 ## 禁止操作
 
-1. **不可在需求不清晰时跳过**:如需求背景、目标不清晰,必须主动询问用户确认
-2. **不可编造未确认的需求细节**:所有未与干系人确认的内容必须标注"待确认",不得自行假设
+1.  **不可在需求不清晰时跳过**:如需求背景、目标不清晰,必须主动询问用户确认
+    
+2.  **不可编造未确认的需求细节**:所有未与干系人确认的内容必须标注"待确认",不得自行假设
+    
 
----
+* * *
 
 ## 使用示例
 
 **触发场景 1:用户要求进行需求分析(最常见)**
+
 ```
 用户:帮我们分析一下这个燃气订单稽查功能的需求
 → 调用 requirements-analysis skill
@@ -45,6 +58,7 @@ description: 需求分析 Skill - 在 PRD 撰写前进行需求调研、分析
 ```
 
 **触发场景 2:用户提供初步想法进行分析**
+
 ```
 用户:我们要做一个会员积分系统,你帮我们分析一下
 → 调用 requirements-analysis skill
@@ -54,6 +68,7 @@ description: 需求分析 Skill - 在 PRD 撰写前进行需求调研、分析
 ```
 
 **触发场景 3:用户已有需求文档,需要补充分析**
+
 ```
 用户:帮我在这个需求文档里补充一下业务流程和功能拆解
 → 调用 requirements-analysis skill
@@ -63,6 +78,7 @@ description: 需求分析 Skill - 在 PRD 撰写前进行需求调研、分析
 ```
 
 **触发场景 4:PRD 撰写前必须完成需求分析**
+
 ```
 用户:帮我写 PRD
 → 调用 requirements-analysis skill
@@ -71,7 +87,7 @@ description: 需求分析 Skill - 在 PRD 撰写前进行需求调研、分析
 → 注意:禁止在需求不清晰时直接撰写 PRD
 ```
 
----
+* * *
 
 ## 与 PRD Writer 的工作流
 
@@ -83,5 +99,10 @@ description: 需求分析 Skill - 在 PRD 撰写前进行需求调研、分析
 ```
 
 **关键约束**:
-- requirements-analysis 完成后,prd-writer 才能基于分析结果撰写 PRD
-- 如果 requirements-analysis 发现需求不清晰,必须先与用户确认,不得直接跳到 PRD 撰写
+
+-   requirements-analysis 完成后,prd-writer 才能基于分析结果撰写 PRD
+    
+-   如果 requirements-analysis 发现需求不清晰,必须先与用户确认,不得直接跳到 PRD 撰写
+    
+
+<br>

+ 44 - 25
.claude/skills/requirements-analysis/reference/analysis-template.md

@@ -5,22 +5,30 @@
 描述需求的来源、现状问题或机会。明确为什么要做这个需求。
 
 **分析要点**:
-- 当前业务痛点是什么?
-- 受影响的用户/角色有哪些?
-- 不做这个需求会有什么影响?
 
----
+-   当前业务痛点是什么?
+    
+-   受影响的用户/角色有哪些?
+    
+-   不做这个需求会有什么影响?
+    
+
+* * *
 
 ## 二、需求目标确认
 
 基于需求背景,确认本次需求的核心目标和期望成果。
 
 **目标特征**:
-- 具体可衡量
-- 有明确的价值交付
-- 可落地执行
 
----
+-   具体可衡量
+    
+-   有明确的价值交付
+    
+-   可落地执行
+    
+
+* * *
 
 ## 三、业务流程分析
 
@@ -50,38 +58,49 @@ graph TD
     E --> F
 ```
 
----
+* * *
 
 ## 四、功能拆解清单
 
 按结构化表格表达:
 
-| 终端 | 模块 | 功能 | 功能描述 | 优先级 |
-|------|------|------|----------|----------|
-| 门店APP | 首页 | banner轮播 | 展示促销活动 Banner,支持点击跳转 | Must |
-| 门店APP | 我的 | 订单查询 | 支持按状态筛选订单列表 | Should |
+| 终端  | 模块  | 功能  | 功能描述 | 优先级 |
+| --- | --- | --- | --- | --- |
+| 门店APP | 首页  | banner轮播 | 展示促销活动 Banner,支持点击跳转 | Must |
+| 门店APP | 我的  | 订单查询 | 支持按状态筛选订单列表 | Should |
 | 门店后端 | 订单服务 | 数据同步 | 订单数据实时同步至供应链中台 | Must |
 
 **格式要求**:
-- 终端:门店APP / 门店PC / 供应链中台 / 客户小程序 / 门店后端 / 供应链中台后端 / 客户小程序后端
-- 模块:所属功能模块
-- 功能:前端按页面/弹窗拆分;后端按服务/能力拆分
-- 功能描述:简要描述该功能的需求要点
-- 优先级:Must(必须有)/ Should(应该有)/ Could(可以有)/ Won't(不会有)
+
+-   终端:门店APP / 门店PC / 供应链中台 / 客户小程序 / 门店后端 / 供应链中台后端 / 客户小程序后端
+    
+-   模块:所属功能模块
+    
+-   功能:前端按页面/弹窗拆分;后端按服务/能力拆分
+    
+-   功能描述:简要描述该功能的需求要点
+    
+-   优先级:Must(必须有)/ Should(应该有)/ Could(可以有)/ Won't(不会有)
+    
 
 **系统现状参考**:如需了解现有系统功能,参见 `reference/system-status.md`
 
----
+* * *
 
 ## 五、待确认事项
 
 列出需求中尚未确认、需要与干系人进一步沟通的内容。
 
-| 序号 | 待确认事项 | 状态 | 负责人 | 截止日期 |
-|------|------------|------|--------|----------|
-| 1 | XX 功能的具体规则 | 待确认 | - | - |
-| 2 | 第三方接口的响应格式 | 待确认 | - | - |
+| 序号  | 待确认事项 | 状态  | 负责人 | 截止日期 |
+| --- | --- | --- | --- | --- |
+| 1   | XX 功能的具体规则 | 待确认 | \-  | \-  |
+| 2   | 第三方接口的响应格式 | 待确认 | \-  | \-  |
 
 **确认原则**:
-- 所有 Must 级别功能必须在 PRD 撰写前完成确认
-- 未确认事项不得在 PRD 中编造,必须标注"待确认"
+
+-   所有 Must 级别功能必须在 PRD 撰写前完成确认
+    
+-   未确认事项不得在 PRD 中编造,必须标注"待确认"
+    
+
+<br>

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

@@ -28,10 +28,12 @@
             placeholder="请选择组装页"
             style="width: 100%"
           >
-            <el-option label="组装页A" value="组装页A"></el-option>
-            <el-option label="组装页B" value="组装页B"></el-option>
-            <el-option label="组装页C" value="组装页C"></el-option>
-            <el-option label="组装页D" value="组装页D"></el-option>
+            <el-option
+              v-for="page in assemblyPageOptions"
+              :key="page.id"
+              :label="page.name"
+              :value="page.name"
+            />
           </el-select>
         </div>
       </el-form-item>
@@ -46,6 +48,8 @@
 </template>
 
 <script>
+import request from '@/utils/request'
+
 export default {
   name: 'ShowPagesDialog',
   props: {
@@ -55,6 +59,7 @@ export default {
   emits: ['update:modelValue', 'confirm'],
   data() {
     return {
+      assemblyPageOptions: [],
       form: {
         showOnHome: '显示',
         showOnAssembly: '不显示',
@@ -69,7 +74,16 @@ export default {
     }
   },
   methods: {
+    async fetchAssemblyPages() {
+      try {
+        const res = await request.get('/api/assembly/list')
+        this.assemblyPageOptions = res.data || []
+      } catch {
+        this.assemblyPageOptions = []
+      }
+    },
     onOpen() {
+      this.fetchAssemblyPages()
       if (this.row) {
         this.form.showOnHome = this.row.showOnHome || '显示'
         this.form.showOnAssembly = this.row.showOnAssembly || '不显示'

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

@@ -262,27 +262,71 @@
             </el-row>
             <el-row :gutter="20">
               <el-col :span="12">
-                <el-form-item label="权益有效期" required>
-                  <template v-if="form.isPlatformExchange === '是'">
-                    <el-radio-group v-model="form.validityType">
-                      <el-radio label="fromGrantPlan">取自发放计划</el-radio>
-                      <el-radio label="custom">自定义输入</el-radio>
+                <el-form-item label="领取有效期" required>
+                  <div style="display: flex; align-items: center; gap: 12px; width: 100%;">
+                    <el-date-picker
+                      v-model="form.claimStartTime"
+                      type="datetime"
+                      placeholder="请选择开始时间"
+                      format="YYYY-MM-DD HH:mm:ss"
+                      value-format="YYYY-MM-DD HH:mm:ss"
+                      style="flex: 1;"
+                    ></el-date-picker>
+                    <span>至</span>
+                    <el-date-picker
+                      v-model="form.claimEndTime"
+                      type="datetime"
+                      placeholder="请选择结束时间"
+                      format="YYYY-MM-DD HH:mm:ss"
+                      value-format="YYYY-MM-DD HH:mm:ss"
+                      style="flex: 1;"
+                    ></el-date-picker>
+                  </div>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="使用有效期" required>
+                  <template v-if="form.isPlatformExchange === '是' && form.productType === '微信立减金'">
+                    <el-radio-group v-model="form.validityType" style="margin-bottom: 8px;">
+                      <el-radio label="fixed">固定日期</el-radio>
+                      <el-radio label="relative">相对日期</el-radio>
                     </el-radio-group>
-                    <el-input
-                      v-if="form.validityType === 'custom'"
-                      v-model="form.validityText"
-                      maxlength="50"
-                      show-word-limit
-                      placeholder="请输入权益有效期"
-                      style="margin-top: 8px;"
-                    ></el-input>
+                    <div v-if="form.validityType === 'fixed'">
+                      <el-date-picker
+                        v-model="form.validityRange"
+                        type="daterange"
+                        range-separator="至"
+                        start-placeholder="开始日期"
+                        end-placeholder="结束日期"
+                        value-format="YYYY-MM-DD"
+                        style="width: 100%;"
+                      />
+                    </div>
+                    <div v-else style="display: flex; align-items: center;">
+                      <span style="margin-right: 8px;">领取后</span>
+                      <el-input-number v-model="form.validityDays" :min="1" :max="3650" controls-position="right" style="width: 200px;" />
+                      <span style="margin-left: 8px;">天</span>
+                    </div>
+                  </template>
+                  <template v-else-if="form.isPlatformExchange === '是' && form.productType === '券码'">
+                    <el-date-picker
+                      v-model="form.validityRange"
+                      type="daterange"
+                      range-separator="至"
+                      start-placeholder="开始日期"
+                      end-placeholder="结束日期"
+                      value-format="YYYY-MM-DD"
+                      style="width: 100%;"
+                    />
                   </template>
                   <el-input
                     v-else
                     v-model="form.validityText"
                     maxlength="50"
                     show-word-limit
-                    placeholder="请输入权益有效期"
+                    placeholder="请输入使用有效期"
                   ></el-input>
                 </el-form-item>
               </el-col>
@@ -413,32 +457,8 @@
           </template>
           <div class="rule-section">
             <el-form :model="form" label-width="120px" label-position="left">
-              <el-form-item label="领取时间限制">
-                <el-switch v-model="form.claimTimeEnabled"></el-switch>
-                <template v-if="form.claimTimeEnabled">
-                  <div style="display: flex;max-width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;flex-basis: 100%;">
-                    <el-date-picker
-                      v-model="form.claimStartTime"
-                      type="datetime"
-                      placeholder="请选择开始时间"
-                      format="YYYY-MM-DD HH:mm:ss"
-                      value-format="YYYY-MM-DD HH:mm:ss"
-                      style="width: 100%"
-                    ></el-date-picker>
-                    <div style="width: 50px;text-align: center;">-</div>
-                    <el-date-picker
-                      v-model="form.claimEndTime"
-                      type="datetime"
-                      placeholder="请选择结束时间"
-                      format="YYYY-MM-DD HH:mm:ss"
-                      value-format="YYYY-MM-DD HH:mm:ss"
-                      style="width: 100%"
-                    ></el-date-picker>
-                  </div>
-                </template>
-              </el-form-item>
               <el-form-item label="领取客群限制">
-                <el-switch v-model="form.claimGroupEnabled"></el-switch>
+                <el-switch v-model="form.claimGroupEnabled" disabled></el-switch>
                 <template v-if="form.claimGroupEnabled">
                   <div style="max-width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;flex-basis: 100%;">
                     <el-alert
@@ -495,10 +515,12 @@
                   placeholder="请选择组装页"
                   style="width: 100%"
                 >
-                  <el-option label="组装页A" value="组装页A"></el-option>
-                  <el-option label="组装页B" value="组装页B"></el-option>
-                  <el-option label="组装页C" value="组装页C"></el-option>
-                  <el-option label="组装页D" value="组装页D"></el-option>
+                  <el-option
+                    v-for="page in assemblyPageOptions"
+                    :key="page.id"
+                    :label="page.name"
+                    :value="page.name"
+                  />
                 </el-select>
               </div>
             </el-form-item>
@@ -634,8 +656,10 @@ export default {
         listIcon: [],
         mainImage: [],
         introduction: '',
-        validityType: 'fromGrantPlan',
+        validityType: 'fixed',
         validityText: '',
+        validityRange: [],
+        validityDays: 30,
         overview: '',
         details: '',
         smsEnabled: false,
@@ -647,10 +671,9 @@ export default {
         showEndTime: '',
         showGroupEnabled: false,
         showGroupTagIds: [],
-        claimTimeEnabled: false,
         claimStartTime: '',
         claimEndTime: '',
-        claimGroupEnabled: false,
+        claimGroupEnabled: true,
         claimGroupIsPotential: false,
         claimGroupTagId: '',
         claimDenyMessage: '很抱歉,您暂不符合领取条件。',
@@ -659,7 +682,8 @@ export default {
         showOnHome: '显示',
         showOnAssembly: '不显示',
         assemblyPages: []
-      }
+      },
+      assemblyPageOptions: []
     }
   },
   computed: {
@@ -679,12 +703,21 @@ export default {
     }
   },
   created() {
+    this.fetchAssemblyPages()
     const copyFrom = this.$route.query.copyFrom
     if (copyFrom) {
       this.loadCopySource(copyFrom)
     }
   },
   methods: {
+    async fetchAssemblyPages() {
+      try {
+        const res = await request.get('/api/assembly/list')
+        this.assemblyPageOptions = res.data || []
+      } catch {
+        this.assemblyPageOptions = []
+      }
+    },
     handlePlatformExchangeChange(val) {
       if (val === '是') {
         this.form.productType = '券码'
@@ -910,8 +943,10 @@ export default {
         exchangeMethod: isNotPlatform
           ? (f.externalJumpMethod === '有' ? '第三方(详情页跳转)' : '第三方(列表跳转)')
           : '权益平台',
-        validityType: isCustomValidity ? 'custom' : 'fromGrantPlan',
-        validityText: isCustomValidity ? (f.validityText || '') : '',
+        validityType: f.validityType || 'fixed',
+        validityText: f.validityText || '',
+        validityRange: f.validityRange || [],
+        validityDays: f.validityDays || 30,
         showOnHome: f.showOnHome,
         showOnAssembly: f.showOnAssembly,
         assemblyPages: [...(f.assemblyPages || [])]

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

@@ -65,16 +65,6 @@
                   <el-input :value="form.equityName" readonly></el-input>
                 </el-form-item>
               </el-col>
-              <el-col :span="8">
-                <el-form-item label="权益副标题">
-                  <el-input :value="form.subTitle" readonly></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.isPlatformExchange === '是' && form.productType === '券码'">
-                <el-form-item label="SIP商品编号">
-                  <el-input :value="form.skuNo" readonly></el-input>
-                </el-form-item>
-              </el-col>
             </el-row>
             <el-row :gutter="20">
               <el-col :span="8">
@@ -113,9 +103,19 @@
             </el-row>
             <el-row :gutter="20">
               <el-col :span="12">
-                <el-form-item label="权益有效期">
+                <el-form-item label="领取有效期">
                   <el-input
-                    :value="form.validityType === 'fromGrantPlan' ? '取自发放计划' : (form.validityText || '-')"
+                    :value="form.claimStartTime && form.claimEndTime ? `${form.claimStartTime} 至 ${form.claimEndTime}` : '-'"
+                    readonly
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="使用有效期">
+                  <el-input
+                    :value="validityDisplay"
                     readonly
                   ></el-input>
                 </el-form-item>
@@ -177,16 +177,6 @@
             <div class="section-title">领取规则</div>
           </template>
           <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="领取时间限制">
-              <el-switch v-model="form.claimTimeEnabled" disabled></el-switch>
-              <template v-if="form.claimTimeEnabled">
-                <div class="rule-pane">
-                  <el-input :value="form.claimStartTime" readonly style="width: 240px;"></el-input>
-                  <span style="margin: 0 8px;">-</span>
-                  <el-input :value="form.claimEndTime" readonly style="width: 240px;"></el-input>
-                </div>
-              </template>
-            </el-form-item>
             <el-form-item label="领取客群限制">
               <el-switch v-model="form.claimGroupEnabled" disabled></el-switch>
             </el-form-item>
@@ -361,7 +351,7 @@ export default {
         listIcon: [],
         mainImage: [],
         introduction: '',
-        validityType: 'fromGrantPlan',
+        validityType: 'fixed',
         validityText: '',
         overview: '',
         details: '',
@@ -383,6 +373,15 @@ export default {
     }
   },
   computed: {
+    validityDisplay() {
+      if (this.form.validityType === 'relative') {
+        return `领取后${this.form.validityDays || '-'}天`
+      }
+      if (Array.isArray(this.form.validityRange) && this.form.validityRange.length === 2) {
+        return `${this.form.validityRange[0]} 至 ${this.form.validityRange[1]}`
+      }
+      return this.form.validityText || '-'
+    },
     mappedSource() {
       if (this.form.isPlatformExchange === '否') return '第三方'
       if (this.form.productType === '微信立减金') return '荣数商品'

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

@@ -106,21 +106,11 @@
                 </el-col>
               </el-row>
               <el-row :gutter="20">
-                <el-col :span="8" v-if="form.productType === '券码'">
-                  <el-form-item label="SIP商品编号">
-                    <el-input v-model="form.skuNo" readonly></el-input>
-                  </el-form-item>
-                </el-col>
                 <el-col :span="8">
                   <el-form-item label="权益名称">
                     <el-input v-model="form.equityName" readonly></el-input>
                   </el-form-item>
                 </el-col>
-                <el-col :span="8">
-                  <el-form-item label="权益副标题">
-                    <el-input v-model="form.subTitle" readonly></el-input>
-                  </el-form-item>
-                </el-col>
               </el-row>
               <el-row :gutter="20">
                 <el-col :span="8">
@@ -148,11 +138,6 @@
                     <el-input v-model="form.equityName" readonly></el-input>
                   </el-form-item>
                 </el-col>
-                <el-col :span="8">
-                  <el-form-item label="权益副标题">
-                    <el-input v-model="form.subTitle" readonly></el-input>
-                  </el-form-item>
-                </el-col>
               </el-row>
               <el-row :gutter="20" v-if="form.externalJumpMethod === '没有'">
                 <el-col :span="8">
@@ -222,9 +207,19 @@
             </el-row>
             <el-row :gutter="20">
               <el-col :span="12">
-                <el-form-item label="权益有效期">
+                <el-form-item label="领取有效期">
+                  <el-input
+                    :value="form.claimStartTime && form.claimEndTime ? `${form.claimStartTime} 至 ${form.claimEndTime}` : '-'"
+                    readonly
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="使用有效期">
                   <el-input
-                    :value="form.validityType === 'fromGrantPlan' ? '取自发放计划' : (form.validityText || '-')"
+                    :value="validityDisplay"
                     readonly
                   ></el-input>
                 </el-form-item>
@@ -317,16 +312,6 @@
           <div class="rule-section">
             <h4>领取规则</h4>
             <el-form :model="form" label-width="120px" label-position="left">
-              <el-form-item label="领取时间限制">
-                <el-switch v-model="form.claimTimeEnabled" disabled></el-switch>
-                <template v-if="form.claimTimeEnabled">
-                  <div style="display: flex;max-width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;flex-basis: 100%;">
-                    <el-input :value="form.claimStartTime" readonly style="width: 100%"></el-input>
-                    <div style="width: 50px;text-align: center;">-</div>
-                    <el-input :value="form.claimEndTime" readonly style="width: 100%"></el-input>
-                  </div>
-                </template>
-              </el-form-item>
               <el-form-item label="领取客群限制">
                 <el-switch v-model="form.claimGroupEnabled" disabled></el-switch>
                 <template v-if="form.claimGroupEnabled">
@@ -472,7 +457,7 @@ export default {
         listIcon: [],
         mainImage: [],
         introduction: '',
-        validityType: 'fromGrantPlan',
+        validityType: 'fixed',
         validityText: '',
         overview: '',
         details: '',
@@ -501,6 +486,15 @@ export default {
     }
   },
   computed: {
+    validityDisplay() {
+      if (this.form.validityType === 'relative') {
+        return `领取后${this.form.validityDays || '-'}天`
+      }
+      if (Array.isArray(this.form.validityRange) && this.form.validityRange.length === 2) {
+        return `${this.form.validityRange[0]} 至 ${this.form.validityRange[1]}`
+      }
+      return this.form.validityText || '-'
+    },
     mappedSource() {
       if (this.form.isPlatformExchange === '否') return '第三方'
       if (this.form.productType === '微信立减金') return '荣数商品'

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

@@ -55,7 +55,6 @@
             </template>
             <span v-else>-</span>
           </el-descriptions-item>
-          <el-descriptions-item label="权益有效期" :span="2">{{ validitySummary }}</el-descriptions-item>
           <el-descriptions-item label="短信通知" :span="2">
             <div class="sms-info-row">
               <span class="sms-tpl-name">{{ smsTemplateName }}</span>

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

@@ -262,27 +262,71 @@
           </el-row>
           <el-row :gutter="20">
             <el-col :span="12">
-              <el-form-item label="权益有效期" required>
-                <template v-if="form.isPlatformExchange === '是'">
-                  <el-radio-group v-model="form.validityType">
-                    <el-radio label="fromGrantPlan">取自发放计划</el-radio>
-                    <el-radio label="custom">自定义输入</el-radio>
+              <el-form-item label="领取有效期" required>
+                <div style="display: flex; align-items: center; gap: 12px; width: 100%;">
+                  <el-date-picker
+                    v-model="form.claimStartTime"
+                    type="datetime"
+                    placeholder="请选择开始时间"
+                    format="YYYY-MM-DD HH:mm:ss"
+                    value-format="YYYY-MM-DD HH:mm:ss"
+                    style="flex: 1;"
+                  ></el-date-picker>
+                  <span>至</span>
+                  <el-date-picker
+                    v-model="form.claimEndTime"
+                    type="datetime"
+                    placeholder="请选择结束时间"
+                    format="YYYY-MM-DD HH:mm:ss"
+                    value-format="YYYY-MM-DD HH:mm:ss"
+                    style="flex: 1;"
+                  ></el-date-picker>
+                </div>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <el-form-item label="使用有效期" required>
+                <template v-if="form.isPlatformExchange === '是' && form.productType === '微信立减金'">
+                  <el-radio-group v-model="form.validityType" style="margin-bottom: 8px;">
+                    <el-radio label="fixed">固定日期</el-radio>
+                    <el-radio label="relative">相对日期</el-radio>
                   </el-radio-group>
-                  <el-input
-                    v-if="form.validityType === 'custom'"
-                    v-model="form.validityText"
-                    maxlength="50"
-                    show-word-limit
-                    placeholder="请输入权益有效期"
-                    style="margin-top: 8px;"
-                  ></el-input>
+                  <div v-if="form.validityType === 'fixed'">
+                    <el-date-picker
+                      v-model="form.validityRange"
+                      type="daterange"
+                      range-separator="至"
+                      start-placeholder="开始日期"
+                      end-placeholder="结束日期"
+                      value-format="YYYY-MM-DD"
+                      style="width: 100%;"
+                    />
+                  </div>
+                  <div v-else style="display: flex; align-items: center;">
+                    <span style="margin-right: 8px;">领取后</span>
+                    <el-input-number v-model="form.validityDays" :min="1" :max="3650" controls-position="right" style="width: 200px;" />
+                    <span style="margin-left: 8px;">天</span>
+                  </div>
+                </template>
+                <template v-else-if="form.isPlatformExchange === '是' && form.productType === '券码'">
+                  <el-date-picker
+                    v-model="form.validityRange"
+                    type="daterange"
+                    range-separator="至"
+                    start-placeholder="开始日期"
+                    end-placeholder="结束日期"
+                    value-format="YYYY-MM-DD"
+                    style="width: 100%;"
+                  />
                 </template>
                 <el-input
                   v-else
                   v-model="form.validityText"
                   maxlength="50"
                   show-word-limit
-                  placeholder="请输入权益有效期"
+                  placeholder="请输入使用有效期"
                 ></el-input>
               </el-form-item>
             </el-col>
@@ -412,32 +456,8 @@
           <div class="section-title">领取规则</div>
         </template>
         <el-form :model="form" label-width="120px" label-position="left">
-          <el-form-item label="领取时间限制">
-            <el-switch v-model="form.claimTimeEnabled"></el-switch>
-            <template v-if="form.claimTimeEnabled">
-              <div style="display: flex;max-width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;flex-basis: 100%;">
-                <el-date-picker
-                  v-model="form.claimStartTime"
-                  type="datetime"
-                  placeholder="请选择开始时间"
-                  format="YYYY-MM-DD HH:mm:ss"
-                  value-format="YYYY-MM-DD HH:mm:ss"
-                  style="width: 100%"
-                ></el-date-picker>
-                <div style="width: 50px;text-align: center;">-</div>
-                <el-date-picker
-                  v-model="form.claimEndTime"
-                  type="datetime"
-                  placeholder="请选择结束时间"
-                  format="YYYY-MM-DD HH:mm:ss"
-                  value-format="YYYY-MM-DD HH:mm:ss"
-                  style="width: 100%"
-                ></el-date-picker>
-              </div>
-            </template>
-          </el-form-item>
           <el-form-item label="领取客群限制">
-            <el-switch v-model="form.claimGroupEnabled"></el-switch>
+            <el-switch v-model="form.claimGroupEnabled" disabled></el-switch>
             <template v-if="form.claimGroupEnabled">
               <div style="max-width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;flex-basis: 100%;">
                 <el-alert
@@ -493,10 +513,12 @@
                   placeholder="请选择组装页"
                   style="width: 100%"
                 >
-                  <el-option label="组装页A" value="组装页A"></el-option>
-                  <el-option label="组装页B" value="组装页B"></el-option>
-                  <el-option label="组装页C" value="组装页C"></el-option>
-                  <el-option label="组装页D" value="组装页D"></el-option>
+                  <el-option
+                    v-for="page in assemblyPageOptions"
+                    :key="page.id"
+                    :label="page.name"
+                    :value="page.name"
+                  />
                 </el-select>
               </div>
             </el-form-item>
@@ -642,8 +664,10 @@ export default {
         listIcon: [],
         mainImage: [],
         introduction: '',
-        validityType: 'fromGrantPlan',
+        validityType: 'fixed',
         validityText: '',
+        validityRange: [],
+        validityDays: 30,
         overview: '',
         details: '',
         smsEnabled: false,
@@ -655,10 +679,9 @@ export default {
         showEndTime: '',
         showGroupEnabled: false,
         showGroupTagIds: [],
-        claimTimeEnabled: false,
         claimStartTime: '',
         claimEndTime: '',
-        claimGroupEnabled: false,
+        claimGroupEnabled: true,
         claimGroupIsPotential: false,
         claimGroupTagId: '',
         claimDenyMessage: '很抱歉,您暂不符合领取条件。',
@@ -667,7 +690,8 @@ export default {
         showOnHome: '显示',
         showOnAssembly: '不显示',
         assemblyPages: []
-      }
+      },
+      assemblyPageOptions: []
     }
   },
   computed: {
@@ -687,10 +711,19 @@ export default {
     }
   },
   created() {
+    this.fetchAssemblyPages()
     this.loadData()
     this.loadStatus()
   },
   methods: {
+    async fetchAssemblyPages() {
+      try {
+        const res = await request.get('/api/assembly/list')
+        this.assemblyPageOptions = res.data || []
+      } catch {
+        this.assemblyPageOptions = []
+      }
+    },
     loadStatus() {
       const id = this.$route.params.id
       if (!id) return
@@ -740,8 +773,10 @@ export default {
         exchangeMethod: isNotPlatform
           ? (f.externalJumpMethod === '有' ? '第三方(详情页跳转)' : '第三方(列表跳转)')
           : '权益平台',
-        validityType: isCustomValidity ? 'custom' : 'fromGrantPlan',
-        validityText: isCustomValidity ? (f.validityText || '') : '',
+        validityType: f.validityType || 'fixed',
+        validityText: f.validityText || '',
+        validityRange: f.validityRange || [],
+        validityDays: f.validityDays || 30,
         showOnHome: f.showOnHome,
         showOnAssembly: f.showOnAssembly,
         assemblyPages: [...(f.assemblyPages || [])]

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

@@ -104,11 +104,6 @@
             {{ grantSummary(scope.row) }}
           </template>
         </el-table-column>
-        <el-table-column label="权益有效期" min-width="200" align="center">
-          <template #default="scope">
-            {{ validitySummary(scope.row.validity) }}
-          </template>
-        </el-table-column>
         <el-table-column label="是否自动发放" width="110" align="center">
           <template #default="scope">
             <el-tag :type="scope.row.isAutoGrant === '是' ? 'success' : 'info'" size="small">{{ scope.row.isAutoGrant }}</el-tag>

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

@@ -67,7 +67,7 @@
             <el-radio label="direct">直接发放</el-radio>
           </el-radio-group>
         </el-form-item>
-        <el-form-item label="选择权益" prop="equityList">
+        <el-form-item label="选择权益" prop="equityList" required>
           <el-select
             v-if="isClaimMode"
             v-model="form.equityList"
@@ -117,32 +117,6 @@
             <span class="grant-suffix">个</span>
           </div>
         </el-form-item>
-        <el-form-item label="权益有效期" required>
-          <el-radio-group v-model="form.validity.type">
-            <el-radio label="fixed">固定日期</el-radio>
-            <el-radio label="relative">相对日期</el-radio>
-          </el-radio-group>
-          <div v-if="form.validity.type === 'fixed'" class="validity-row" style="width:360px">
-            <el-date-picker
-              v-model="form.validity.fixedRange"
-              type="daterange"
-              range-separator="至"
-              start-placeholder="开始日期"
-              end-placeholder="结束日期"
-              value-format="YYYY-MM-DD"
-            />
-          </div>
-          <div v-else class="validity-row">
-            <el-input-number
-              v-model="form.validity.relativeDays"
-              :min="1"
-              :max="3650"
-              :controls-position="'right'"
-              style="width:200px"
-            />
-            <span class="grant-suffix">天(自发放起算)</span>
-          </div>
-        </el-form-item>
         <el-form-item label="短信通知" prop="smsTemplateId" required>
           <el-select
             v-model="form.smsTemplateId"
@@ -318,16 +292,6 @@ export default {
           ElMessage.warning('结束时间必须晚于开始时间')
           return
         }
-        const v = this.form.validity
-        if (v.type === 'fixed') {
-          if (!v.fixedRange || v.fixedRange.length !== 2) {
-            ElMessage.warning('请选择权益有效期的开始与结束日期')
-            return
-          }
-        } else if (!v.relativeDays || v.relativeDays < 1) {
-          ElMessage.warning('请输入权益有效期的天数')
-          return
-        }
         if (!this.form.grantQuantity || this.form.grantQuantity < 1) {
           ElMessage.warning('请输入单客户权益数量')
           return

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

@@ -19,7 +19,6 @@
         <el-descriptions-item label="单客户权益数量">{{ grantSummary }}</el-descriptions-item>
         <el-descriptions-item label="开始时间">{{ current.startTime || '-' }}</el-descriptions-item>
         <el-descriptions-item label="结束时间">{{ current.endTime || '-' }}</el-descriptions-item>
-        <el-descriptions-item label="权益有效期" :span="2">{{ validitySummary }}</el-descriptions-item>
         <el-descriptions-item label="选择客群" :span="2">
           <template v-if="(current.crowdList || []).length">
             <el-tag

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

@@ -149,12 +149,6 @@
             <span v-else class="empty-text">-</span>
           </template>
         </el-table-column>
-        <el-table-column label="SKU编号" width="160" show-overflow-tooltip>
-          <template #default="scope">
-            <span v-if="isPlatformProduct(scope.row) && scope.row.skuNo">{{ scope.row.skuNo }}</span>
-            <span v-else class="empty-text">-</span>
-          </template>
-        </el-table-column>
         <el-table-column label="库存数" width="100" align="right">
           <template #default="scope">
             <span v-if="isPlatformProduct(scope.row) && hasStock(scope.row)">{{ scope.row.stockCount }}</span>
@@ -167,12 +161,6 @@
             <span v-else class="empty-text">-</span>
           </template>
         </el-table-column>
-        <el-table-column label="是否有详情页" width="120">
-          <template #default="scope">
-            <span v-if="scope.row.isPlatformExchange === '否'">{{ scope.row.jumpMethod }}</span>
-            <span v-else class="empty-text">-</span>
-          </template>
-        </el-table-column>
         <el-table-column prop="productId" label="权益编号" width="100"></el-table-column>
         <el-table-column label="显示页面" min-width="220">
           <template #default="scope">
@@ -268,6 +256,7 @@ export default {
     },
     // 按钮可见性
     canEdit(row) {
+      if (row.shelfStatus === '已下架') return true
       return row.shelfStatus === '未上架' && row.auditStatus === '草稿'
     },
     canRequestOffshelf(row) {

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

@@ -13,7 +13,6 @@
           <el-tag :type="task.isAutoGrant === '是' ? 'success' : 'info'" size="small">{{ task.isAutoGrant }}</el-tag>
         </el-descriptions-item>
         <el-descriptions-item label="单客户权益数量">{{ task.grantQuantityLabel }}</el-descriptions-item>
-        <el-descriptions-item label="权益有效期">{{ task.validityLabel }}</el-descriptions-item>
         <el-descriptions-item label="开始时间">{{ task.planStartTime }}</el-descriptions-item>
         <el-descriptions-item label="结束时间">{{ task.planEndTime }}</el-descriptions-item>
         <el-descriptions-item label="选择客群">{{ task.crowdLabel }}</el-descriptions-item>

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

@@ -143,11 +143,6 @@
                     <el-input v-model="form.equityName" readonly></el-input>
                   </el-form-item>
                 </el-col>
-                <el-col :span="8">
-                  <el-form-item label="权益副标题">
-                    <el-input v-model="form.subTitle" readonly></el-input>
-                  </el-form-item>
-                </el-col>
               </el-row>
               <el-row :gutter="20" v-if="form.externalJumpMethod === '没有'">
                 <el-col :span="8">
@@ -185,13 +180,6 @@
               </el-row>
             </template>
 
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="SIP商品编号">
-                  <el-input v-model="form.skuNo" readonly></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
           </el-form>
         </el-card>
 
@@ -225,9 +213,19 @@
             </el-row>
             <el-row :gutter="20">
               <el-col :span="12">
-                <el-form-item label="权益有效期">
+                <el-form-item label="领取有效期">
+                  <el-input
+                    :value="form.claimStartTime && form.claimEndTime ? `${form.claimStartTime} 至 ${form.claimEndTime}` : '-'"
+                    readonly
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="使用有效期">
                   <el-input
-                    :value="form.validityType === 'fromGrantPlan' ? '取自发放计划' : (form.validityText || '-')"
+                    :value="validityDisplay"
                     readonly
                   ></el-input>
                 </el-form-item>
@@ -320,16 +318,6 @@
           <div class="rule-section">
             <h4>领取规则</h4>
             <el-form :model="form" label-width="120px" label-position="left">
-              <el-form-item label="领取时间限制">
-                <el-switch v-model="form.claimTimeEnabled" disabled></el-switch>
-                <template v-if="form.claimTimeEnabled">
-                  <div style="display: flex;max-width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;flex-basis: 100%;">
-                    <el-input :value="form.claimStartTime" readonly style="width: 100%"></el-input>
-                    <div style="width: 50px;text-align: center;">-</div>
-                    <el-input :value="form.claimEndTime" readonly style="width: 100%"></el-input>
-                  </div>
-                </template>
-              </el-form-item>
               <el-form-item label="领取客群限制">
                 <el-switch v-model="form.claimGroupEnabled" disabled></el-switch>
                 <template v-if="form.claimGroupEnabled">
@@ -505,7 +493,7 @@ export default {
         listIcon: [],
         mainImage: [],
         introduction: '',
-        validityType: 'fromGrantPlan',
+        validityType: 'fixed',
         validityText: '',
         overview: '',
         details: '',
@@ -535,7 +523,15 @@ export default {
     }
   },
   computed: {
-    // 展示客群:把客群任务 id 解析成「名称 (id)」展示
+    validityDisplay() {
+      if (this.form.validityType === 'relative') {
+        return `领取后${this.form.validityDays || '-'}天`
+      }
+      if (Array.isArray(this.form.validityRange) && this.form.validityRange.length === 2) {
+        return `${this.form.validityRange[0]} 至 ${this.form.validityRange[1]}`
+      }
+      return this.form.validityText || '-'
+    },
     showGroupLabels() {
       const ids = this.form.showGroupTagIds || []
       return ids.map(id => {
@@ -726,7 +722,7 @@ export default {
           smsTemplateId: 'SMS_TPL_002',
           smsTemplate: '【银行】尊敬的客户,您的${productName}购买成功,券码为${exchangeNum},请于${expireDate}前使用。',
           successButtonText: '',
-          validityType: 'fromGrantPlan',
+          validityType: 'fixed',
           validityText: '',
           showTimeEnabled: false,
           showStartTime: '',

+ 1 - 1
工作空间/产研/权益/汇丰全行/产品原型/pc-demo/vite.config.js

@@ -4,7 +4,7 @@ import { fileURLToPath, URL } from 'node:url'
 
 export default defineConfig({
   plugins: [vue()],
-  base: './',
+  base: '/',
   resolve: {
     alias: {
       '@': fileURLToPath(new URL('./src', import.meta.url))

+ 11 - 7
工作空间/产研/权益/汇丰全行/需求文档/汇丰银行权益中台-FSD.md

@@ -46,8 +46,6 @@
 
 进入【统一门户】-【用户管理】分配角色
 
-<br>
-
 ![BlockNote image](../../../../../.resources/image-1779243010146.png)![BlockNote image](../../../../../.resources/image-1779243019476.png)
 
 ## 2\. 用户登录
@@ -155,9 +153,7 @@
 | 是否权益平台兑换 | 是 / 否,Tag 展示:是(绿)/ 否(灰) |
 | 商品类型 | 仅是否权益平台兑换 = 是时显示,可选值:券码 / 微信立减金;是否权益平台兑换 = 否时显示”-” |
 | 外部权益链接 | 仅是否权益平台兑换 = 否时显示,链接来源:太古地产 / 龙腾 / h5链接 / 小程序链接;是否权益平台兑换 = 是时显示”-” |
-| 是否有详情页 | 仅是否权益平台兑换 = 否时使用,标识跳转触发位:”有” = 用户进入权益详情页后再跳出;”没有” = 用户在首页/列表页/组装页点击后直接跳出;是否权益平台兑换 = 是时显示”-” |
 | 权益编号 | 系统生成,统一 `Q` + 6 位序号格式(如 `Q000001`),全局唯一 |
-| SKU编号 | 仅是否权益平台兑换 = 是时显示,由"选择权益"或券码配置自动生成;  <br>  <br>是否权益平台兑换 = 否时显示"-" |
 | 库存数 | 仅是否权益平台兑换 = 是时显示,整数,微信立减金由荣数商品池回填,券码由券码管理统计剩余数量;是否权益平台兑换 = 否时显示"-" |
 | 显示页面 | 使用 Tag 展示当前权益启用的展示位:开启"权益首页/列表页"时显示一个「权益首页/列表页」Tag;开启"组装页"时按所选组装页(组装页 A/B/C/D…)追加 Tag。两项均未开启时显示"-" |
 | 创建人 | 创建该权益的用户昵称 |
@@ -172,7 +168,7 @@
 | 操作  | 显示条件 | 行为  |
 | --- | --- | --- |
 | 查看  | 所有状态 | 进入查看页,字段与编辑一致但所有输入态禁用,并展示操作日志 |
-| 编辑  | 商品状态 = `未上架` 且 审核状态 = `草稿` | 进入编辑向导继续完善草稿 |
+| 编辑  | 商品状态 = `未上架` 且 审核状态 = `草稿`;或 商品状态 = `已下架` | 进入编辑向导继续完善草稿 |
 | 下架  | 商品状态 = `已上架` 且 审核状态 = `审核通过` | 二次确认弹窗("确定提交'XXX'的下架审核吗?审核通过后才会下架。")→ 确认后生成下架审核单,权益审核状态切到 `下架待审核` |
 | 更多 → 调整显示顺序 | 商品状态 = `已上架` | 弹出气泡:"调整到第 N 位",输入框默认值=当前位次,范围 1 ~ 当前列表总条数;提示文案"移动端权益首页和列表页按照该顺序展示";【确定】后列表局部刷新到目标位置 |
 | 更多 → 复制 | 所有状态 | 进入【添加权益】向导,自动回填源权益的基本信息、规则、显示页面等字段;保存时由系统生成新权益编号 |
@@ -252,6 +248,14 @@
 
 <br>
 
+<br>
+
+<br>
+
+<br>
+
+<br>
+
 ![BlockNote image](./screenshots/2-2-equity-add-source-rongshu.png)![BlockNote image](./screenshots/dialog-rongshu-product.png)
 
 **C. 是否权益平台兑换 = 否**
@@ -335,7 +339,7 @@
 | --- | --- | --- |
 | 是否支持分享 | 开关  | 默认关闭。开启后展开下列字段: |
 | · 分享名称 | 必填  | 最长 40 字符。开关由关→开时,若分享名称为空则自动回填当前的"权益名称" |
-| · 分享图片 | 必填  | 上传 1 张 |
+| · 分享图片 | 必填  | 上传 1 张,建议尺寸 100×100px |
 
 * * *
 
@@ -525,7 +529,7 @@ PS2:信用卡权益展示&领取规则:通过客户号去CVP平台查询获
 | --- | --- |
 | 操作  | 行内"调整显示顺序"、"设置显示页面"两个文字按钮,最右侧为【更多】下拉菜单;详见 3.4 操作 |
 | 权益名称 | 权益名称,来源于营销系统,超过一行换行显示 |
-| 显示页面 | 使用 Tag 展示当前权益启用的展示位:开启"权益首页/列表页"时显示一枚「权益首页/列表页」Tag;开启"组装页"时按所选组装页(组装页 A/B/C/D…)追加 Tag。两项均未开启时显示"-" |
+| 显示页面 | 使用 Tag 展示当前权益启用的展示位:开启"权益首页/列表页"时显示一枚「权益首页/列表页」Tag;开启"组装页"时按所选组装页名称追加 Tag(组装页选项从页面管理-组装页列表动态获取)。两项均未开启时显示"-" |
 
 ### 3.4 操作