Explorar o código

Merge remote changes and resolve conflicts

User hai 2 meses
pai
achega
a91de2cc42

+ 80 - 0
cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java

@@ -4124,6 +4124,86 @@ try {
         } catch (Exception e) {
             log.warn("迁移family_members.generation数据失败: {}", e.getMessage());
         }
+
+        // 迁移38: 创建供应商体系相关表
+        try {
+            jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_system (" +
+                "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
+                "name VARCHAR(100) NOT NULL COMMENT '体系名称', " +
+                "admin_id BIGINT COMMENT '管理员用户ID', " +
+                "settlement_period_days INT DEFAULT 30 COMMENT '账期天数', " +
+                "platform_profit_rate DECIMAL(10,2) DEFAULT 0.00 COMMENT '平台留利比例(%)', " +
+                "description VARCHAR(500) COMMENT '描述', " +
+                "status VARCHAR(20) DEFAULT 'active' COMMENT '状态 active/disabled', " +
+                "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
+                "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
+                "INDEX idx_admin (admin_id), " +
+                "INDEX idx_status (status)" +
+                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商体系'");
+            log.info("已创建supply_system表");
+        } catch (Exception e) {
+            log.warn("创建supply_system表失败: {}", e.getMessage());
+        }
+
+        try {
+            jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_system_member (" +
+                "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
+                "system_id BIGINT NOT NULL COMMENT '体系ID', " +
+                "user_id BIGINT NOT NULL COMMENT '用户ID', " +
+                "role VARCHAR(20) DEFAULT 'member' COMMENT '角色 admin/member', " +
+                "status VARCHAR(20) DEFAULT 'active' COMMENT '状态 active/disabled', " +
+                "joined_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
+                "INDEX idx_system (system_id), " +
+                "INDEX idx_user (user_id), " +
+                "UNIQUE INDEX idx_system_user (system_id, user_id)" +
+                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商体系成员'");
+            log.info("已创建supply_system_member表");
+        } catch (Exception e) {
+            log.warn("创建supply_system_member表失败: {}", e.getMessage());
+        }
+
+        try {
+            jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_settlement (" +
+                "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
+                "system_id BIGINT NOT NULL COMMENT '体系ID', " +
+                "period_start DATE COMMENT '结算周期开始', " +
+                "period_end DATE COMMENT '结算周期结束', " +
+                "total_sales INT DEFAULT 0 COMMENT '销售总额(分)', " +
+                "platform_profit INT DEFAULT 0 COMMENT '平台留利(分)', " +
+                "supplier_payout INT DEFAULT 0 COMMENT '供应商实付(分)', " +
+                "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态 pending/settled', " +
+                "settled_at DATETIME COMMENT '结算时间', " +
+                "remark VARCHAR(500) COMMENT '备注', " +
+                "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
+                "INDEX idx_system (system_id), " +
+                "INDEX idx_status (status), " +
+                "INDEX idx_period (period_start, period_end)" +
+                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商结算单'");
+            log.info("已创建supply_settlement表");
+        } catch (Exception e) {
+            log.warn("创建supply_settlement表失败: {}", e.getMessage());
+        }
+
+        try {
+            jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_settlement_detail (" +
+                "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
+                "settlement_id BIGINT NOT NULL COMMENT '结算单ID', " +
+                "order_id BIGINT COMMENT '订单ID', " +
+                "order_no VARCHAR(64) COMMENT '订单号', " +
+                "product_name VARCHAR(200) COMMENT '商品名称', " +
+                "amount INT DEFAULT 0 COMMENT '订单金额(分)', " +
+                "platform_fee INT DEFAULT 0 COMMENT '平台留利(分)', " +
+                "supplier_id BIGINT COMMENT '收款供应商ID', " +
+                "supplier_name VARCHAR(100) COMMENT '收款供应商名称', " +
+                "payout INT DEFAULT 0 COMMENT '实付(分)', " +
+                "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
+                "INDEX idx_settlement (settlement_id), " +
+                "INDEX idx_order (order_id)" +
+                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商结算明细'");
+            log.info("已创建supply_settlement_detail表");
+        } catch (Exception e) {
+            log.warn("创建supply_settlement_detail表失败: {}", e.getMessage());
+        }
     }
 
     private void insertSysConfigSeed(String key, String value, String desc) {

+ 43 - 21
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/SupplySettlementController.java

@@ -1,16 +1,17 @@
 package com.etotem.cfc.controller.admin;
 
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.entity.SupplySettlement;
 import com.etotem.cfc.entity.SupplySettlementDetail;
 import com.etotem.cfc.service.SupplySettlementService;
-import org.springframework.format.annotation.DateTimeFormat;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
-import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 @RestController
 @RequestMapping("/api/admin/supply-settlement")
@@ -19,35 +20,56 @@ public class SupplySettlementController {
     @Resource
     private SupplySettlementService supplySettlementService;
 
+    /**
+     * 结算记录列表(分页+过滤)
+     */
     @PostMapping("/list")
-    public Result<Page<SupplySettlement>> list(@RequestParam(required = false) Long systemId,
-                                               @RequestParam(required = false) String status,
-                                               @RequestParam(defaultValue = "1") int page,
-                                               @RequestParam(defaultValue = "10") int size) {
-        return Result.success(supplySettlementService.list(systemId, status, page, size));
+    public Result<Map<String, Object>> list(@RequestBody Map<String, Object> params) {
+        return supplySettlementService.list(params);
     }
 
+    /**
+     * 结算详情
+     */
     @PostMapping("/detail")
-    public Result<SupplySettlement> detail(@RequestParam Long id) {
-        return Result.success(supplySettlementService.detail(id));
+    public Result<SupplySettlement> detail(@RequestBody Map<String, Object> params) {
+        Long id = params.get("id") != null ? ((Number) params.get("id")).longValue() : null;
+        if (id == null) {
+            return Result.error("id不能为空");
+        }
+        return supplySettlementService.detail(id);
     }
 
+    /**
+     * 结算明细列表
+     */
     @PostMapping("/detail-items")
-    public Result<List<SupplySettlementDetail>> detailItems(@RequestParam Long settlementId) {
-        return Result.success(supplySettlementService.detailItems(settlementId));
+    public Result<List<SupplySettlementDetail>> detailItems(@RequestBody Map<String, Object> params) {
+        Long settlementId = params.get("settlementId") != null
+                ? ((Number) params.get("settlementId")).longValue() : null;
+        if (settlementId == null) {
+            return Result.error("settlementId不能为空");
+        }
+        return supplySettlementService.detailItems(settlementId);
     }
 
+    /**
+     * 手动生成结算单
+     */
     @PostMapping("/create")
-    public Result<SupplySettlement> create(@RequestParam Long systemId,
-                                           @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date periodStart,
-                                           @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date periodEnd,
-                                           @RequestParam(required = false) String remark) {
-        return Result.success(supplySettlementService.createSettlement(systemId, periodStart, periodEnd, remark));
+    public Result<SupplySettlement> create(@RequestBody Map<String, Object> params) {
+        return supplySettlementService.createSettlement(params);
     }
 
+    /**
+     * 确认结算
+     */
     @PostMapping("/confirm")
-    public Result<Void> confirm(@RequestParam Long id) {
-        boolean success = supplySettlementService.confirm(id);
-        return success ? Result.success(null) : Result.error("确认结算失败");
+    public Result<Void> confirm(@RequestBody Map<String, Object> params) {
+        Long id = params.get("id") != null ? ((Number) params.get("id")).longValue() : null;
+        if (id == null) {
+            return Result.error("id不能为空");
+        }
+        return supplySettlementService.confirm(id);
     }
 }

+ 3 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/AddFamilyMemberDTO.java

@@ -32,6 +32,9 @@ public class AddFamilyMemberDTO {
     /** 出生日期 yyyy-MM-dd */
     private String birthday;
 
+    /** 关系类型标识(必填):spouse/parent/child/sibling 等 */
+    private String relationshipType;
+
     /** 角色覆盖: auto/parent/child/elderly(选填,默认auto) */
     private String roleOverride;
 }

+ 1 - 1
cfc-backend/src/main/java/com/etotem/cfc/entity/SupplySettlement.java

@@ -22,4 +22,4 @@ public class SupplySettlement implements Serializable {
     private Date settledAt;
     private String remark;
     private Date createdAt;
-}
+}

+ 2 - 0
cfc-backend/src/main/java/com/etotem/cfc/entity/SupplySettlementDetail.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import java.io.Serializable;
+import java.util.Date;
 
 @Data
 @TableName("supply_settlement_detail")
@@ -20,4 +21,5 @@ public class SupplySettlementDetail implements Serializable {
     private Long supplierId;
     private String supplierName;
     private Integer payout;
+    private Date createdAt;
 }

+ 154 - 35
cfc-backend/src/main/java/com/etotem/cfc/service/SupplySettlementService.java

@@ -3,6 +3,7 @@ package com.etotem.cfc.service;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.etotem.cfc.common.Result;
 import com.etotem.cfc.entity.ProductOrder;
 import com.etotem.cfc.entity.SupplySettlement;
 import com.etotem.cfc.entity.SupplySettlementDetail;
@@ -11,16 +12,26 @@ import com.etotem.cfc.mapper.ProductOrderMapper;
 import com.etotem.cfc.mapper.SupplySettlementDetailMapper;
 import com.etotem.cfc.mapper.SupplySettlementMapper;
 import com.etotem.cfc.mapper.SupplySystemMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 @Service
 public class SupplySettlementService extends ServiceImpl<SupplySettlementMapper, SupplySettlement> {
 
+    private static final Logger log = LoggerFactory.getLogger(SupplySettlementService.class);
+
     @Resource
     private SupplySettlementDetailMapper supplySettlementDetailMapper;
 
@@ -30,62 +41,160 @@ public class SupplySettlementService extends ServiceImpl<SupplySettlementMapper,
     @Resource
     private ProductOrderMapper productOrderMapper;
 
-    public Page<SupplySettlement> list(Long systemId, String status, int page, int size) {
-        Page<SupplySettlement> pageParam = new Page<>(page, size);
-        LambdaQueryWrapper<SupplySettlement> wrapper = new LambdaQueryWrapper<>();
+    /**
+     * 分页查询结算记录列表
+     */
+    public Result<Map<String, Object>> list(Map<String, Object> params) {
+        Integer pageNum = params.get("page") != null ? ((Number) params.get("page")).intValue() : 1;
+        Integer pageSize = params.get("size") != null ? ((Number) params.get("size")).intValue() : 20;
+        Long systemId = params.get("systemId") != null ? ((Number) params.get("systemId")).longValue() : null;
+        String status = (String) params.get("status");
+
+        Page<SupplySettlement> pageParam = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<SupplySettlement> wrapper = new LambdaQueryWrapper<SupplySettlement>()
+                .orderByDesc(SupplySettlement::getCreatedAt);
+
         if (systemId != null) {
             wrapper.eq(SupplySettlement::getSystemId, systemId);
         }
         if (status != null && !status.isEmpty()) {
             wrapper.eq(SupplySettlement::getStatus, status);
         }
-        wrapper.orderByDesc(SupplySettlement::getId);
-        return this.page(pageParam, wrapper);
+
+        Page<SupplySettlement> result = this.page(pageParam, wrapper);
+        Map<String, Object> data = new HashMap<>();
+        data.put("records", result.getRecords());
+        data.put("total", result.getTotal());
+        data.put("page", result.getCurrent());
+        data.put("size", result.getSize());
+        return Result.success(data);
     }
 
-    public SupplySettlement detail(Long id) {
-        return this.getById(id);
+    /**
+     * 按 ID 查询结算详情
+     */
+    public Result<SupplySettlement> detail(Long id) {
+        SupplySettlement settlement = this.getById(id);
+        if (settlement == null) {
+            return Result.error("结算记录不存在");
+        }
+        return Result.success(settlement);
     }
 
-    public List<SupplySettlementDetail> detailItems(Long settlementId) {
+    /**
+     * 查询结算明细列表
+     */
+    public Result<List<SupplySettlementDetail>> detailItems(Long settlementId) {
         LambdaQueryWrapper<SupplySettlementDetail> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(SupplySettlementDetail::getSettlementId, settlementId);
-        return supplySettlementDetailMapper.selectList(wrapper);
+        List<SupplySettlementDetail> items = supplySettlementDetailMapper.selectList(wrapper);
+        return Result.success(items);
     }
 
+    /**
+     * 手动生成结算单
+     *
+     * 汇总指定周期内该体系下所有已完成的订单,按公式计算平台留利和供应商实付:
+     *   platform_profit = total_sales × platform_profit_rate / 100
+     *   supplier_payout = total_sales - platform_profit
+     */
     @Transactional
-    public SupplySettlement createSettlement(Long systemId, Date periodStart, Date periodEnd, String remark) {
+    public Result<SupplySettlement> createSettlement(Map<String, Object> params) {
+        Long systemId = params.get("systemId") != null ? ((Number) params.get("systemId")).longValue() : null;
+        String periodStartStr = (String) params.get("periodStart");
+        String periodEndStr = (String) params.get("periodEnd");
+
+        if (systemId == null) {
+            return Result.error("systemId不能为空");
+        }
+        if (periodStartStr == null || periodEndStr == null) {
+            return Result.error("结算周期不能为空");
+        }
+
         SupplySystem system = supplySystemMapper.selectById(systemId);
-        if (system == null) return null;
+        if (system == null) {
+            return Result.error("供应商体系不存在");
+        }
 
-        // Query all completed orders in the period for this system
+        Date periodStart;
+        Date periodEnd;
+        try {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+            periodStart = sdf.parse(periodStartStr);
+            periodEnd = sdf.parse(periodEndStr);
+        } catch (Exception e) {
+            return Result.error("日期格式错误,应为 yyyy-MM-dd");
+        }
+
+        // 查询该体系下已完成且未被结算的订单
         LambdaQueryWrapper<ProductOrder> orderWrapper = new LambdaQueryWrapper<>();
         orderWrapper.eq(ProductOrder::getSupplySystemId, systemId)
-                    .ge(ProductOrder::getPaidAt, periodStart)
-                    .le(ProductOrder::getPaidAt, periodEnd)
-                    .ne(ProductOrder::getStatus, "CANCELLED");
+                .eq(ProductOrder::getStatus, "completed")
+                .ge(ProductOrder::getCreatedAt, periodStart)
+                .le(ProductOrder::getCreatedAt, periodEnd);
+
         List<ProductOrder> orders = productOrderMapper.selectList(orderWrapper);
+        if (orders.isEmpty()) {
+            return Result.error("该周期内没有已完成的订单");
+        }
+
+        // 排除已存在于结算明细中的订单
+        List<Long> orderIds = orders.stream().map(ProductOrder::getId).collect(Collectors.toList());
+        LambdaQueryWrapper<SupplySettlementDetail> existingWrapper = new LambdaQueryWrapper<>();
+        existingWrapper.in(SupplySettlementDetail::getOrderId, orderIds);
+        List<SupplySettlementDetail> existingDetails = supplySettlementDetailMapper.selectList(existingWrapper);
+        Set<Long> existingOrderIds = existingDetails.stream()
+                .map(SupplySettlementDetail::getOrderId)
+                .collect(Collectors.toSet());
+
+        List<ProductOrder> settleableOrders = orders.stream()
+                .filter(o -> !existingOrderIds.contains(o.getId()))
+                .collect(Collectors.toList());
+
+        if (settleableOrders.isEmpty()) {
+            return Result.error("该周期内的订单均已结算");
+        }
+
+        // 计算销售总额(单位:分)
+        int totalSales = settleableOrders.stream()
+                .mapToInt(o -> o.getTotalAmount() != null ? o.getTotalAmount() : 0)
+                .sum();
 
-        if (orders.isEmpty()) return null;
+        // 获取平台留利比例
+        BigDecimal rate = system.getPlatformProfitRate() != null
+                ? system.getPlatformProfitRate()
+                : BigDecimal.ZERO;
 
-        int totalSales = 0;
-        int platformProfit = 0;
+        // 计算公式(金额单位:分)
+        // platform_profit = total_sales × platform_profit_rate / 100
+        BigDecimal totalSalesBD = BigDecimal.valueOf(totalSales);
+        BigDecimal platformProfitBD = totalSalesBD.multiply(rate)
+                .divide(BigDecimal.valueOf(100), 0, BigDecimal.ROUND_HALF_UP);
+        BigDecimal supplierPayoutBD = totalSalesBD.subtract(platformProfitBD);
 
+        int platformProfit = platformProfitBD.intValue();
+        int supplierPayout = supplierPayoutBD.intValue();
+
+        // 创建结算记录
         SupplySettlement settlement = new SupplySettlement();
         settlement.setSystemId(systemId);
         settlement.setPeriodStart(periodStart);
         settlement.setPeriodEnd(periodEnd);
+        settlement.setTotalSales(totalSales);
+        settlement.setPlatformProfit(platformProfit);
+        settlement.setSupplierPayout(supplierPayout);
         settlement.setStatus("pending");
-        settlement.setRemark(remark);
         settlement.setCreatedAt(new Date());
+        this.save(settlement);
 
-        for (ProductOrder order : orders) {
+        // 创建结算明细
+        for (ProductOrder order : settleableOrders) {
             int orderAmount = order.getTotalAmount() != null ? order.getTotalAmount() : 0;
-            int profitRate = system.getPlatformProfitRate() != null ? system.getPlatformProfitRate().multiply(java.math.BigDecimal.valueOf(10)).intValue() : 0;
-            int orderProfit = (orderAmount * profitRate) / 1000;
-
-            totalSales += orderAmount;
-            platformProfit += orderProfit;
+            BigDecimal orderAmountBD = BigDecimal.valueOf(orderAmount);
+            int orderPlatformFee = orderAmountBD.multiply(rate)
+                    .divide(BigDecimal.valueOf(100), 0, BigDecimal.ROUND_HALF_UP)
+                    .intValue();
+            int orderPayout = orderAmount - orderPlatformFee;
 
             SupplySettlementDetail detail = new SupplySettlementDetail();
             detail.setSettlementId(settlement.getId());
@@ -93,26 +202,36 @@ public class SupplySettlementService extends ServiceImpl<SupplySettlementMapper,
             detail.setOrderNo(order.getOrderNo());
             detail.setProductName(order.getProductName());
             detail.setAmount(orderAmount);
-            detail.setPlatformFee(orderProfit);
+            detail.setPlatformFee(orderPlatformFee);
             detail.setSupplierId(order.getSupplierId());
-            detail.setPayout(orderAmount - orderProfit);
+            detail.setPayout(orderPayout);
+            detail.setCreatedAt(new Date());
             supplySettlementDetailMapper.insert(detail);
         }
 
-        settlement.setTotalSales(totalSales);
-        settlement.setPlatformProfit(platformProfit);
-        settlement.setSupplierPayout(totalSales - platformProfit);
-        this.save(settlement);
+        log.info("已创建结算单 ID={}, systemId={}, 周期={}~{}, 总额={}, 留利={}, 实付={}",
+                settlement.getId(), systemId, periodStartStr, periodEndStr,
+                totalSales, platformProfit, supplierPayout);
 
-        return settlement;
+        return Result.success(settlement);
     }
 
+    /**
+     * 确认结算(pending → settled)
+     */
     @Transactional
-    public boolean confirm(Long id) {
+    public Result<Void> confirm(Long id) {
         SupplySettlement settlement = this.getById(id);
-        if (settlement == null) return false;
+        if (settlement == null) {
+            return Result.error("结算记录不存在");
+        }
+        if (!"pending".equals(settlement.getStatus())) {
+            return Result.error("只有待结算状态的记录才能确认");
+        }
         settlement.setStatus("settled");
         settlement.setSettledAt(new Date());
-        return this.updateById(settlement);
+        this.updateById(settlement);
+        log.info("已确认结算单 ID={}", id);
+        return Result.success(null);
     }
 }

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-7aea8dbf2dc6637dbfb4ee75d75defd0e89b068c
+b36b888ce1a3e1d12eb0320e68a5f95c871a3c32

+ 44 - 0
cfc-web/CHANGELOG.md

@@ -3,6 +3,50 @@
 此文件记录所有构建版本的变更。
 ## v$(node (2026-07-07)
 
+### Bug 修复
+- migration 36-37 — family_members generation/is_spouse columns
+- admin edit pages and CHANGELOG updates
+- add GenerationLevel enum and family member entity updates
+- PaymentService testMode mock + WeChat JSAPI payer.openid
+- resolve 5 UI bugs across CFC mini-program
+- CORS - add PrivateNetwork header + explicit allowedOrigins
+- points_log表添加family_member_id列(迁移35,修复家庭能量计算报错)
+- align supply distribution system with spec — member management, period-based settlement, 3-page frontend
+
+### 其他
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+-   - 只保留 subPackages 中正确注册的 pages/shop/index/index.vue
+- FRONTEND-02: 缩短过渡页显示时间 5s→2s
+-   - action/wisdom/mind/body/index + profile + index 页面
+- FRONTEND-03: 智页推荐阅读'更多'导航修复
+-   - wisdom/index.vue goMoreArticles() 修正为目标路由
+- FRONTEND-04: 行页面推荐阅读缺失 (relatedDimensions JSON 解析)
+-   - 新增 parseRelatedDimensions() 方法处理 JSON 数组字符串/逗号分隔/数组
+-   - 修复 action/wisdom/mind 三页面 loadFeaturedArticles
+- FRONTEND-05: 行页面移除孩子管理快捷操作组件
+-   - 删除 UserQuickEntry 组件及其 import 和注册
+- 
+- - Replace allowedOriginPatterns("*") with explicit origins (CORS spec: "*" + allowCredentials invalid)
+- - Allowed: https://cfc.etotem.com.cn, http://cfc.etotem.com.cn, localhost/127.0.0.1 dev origins
+- 
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+
+### 文档
+- add family member relationship conversion spec and plan
+
+
+## v$(node (2026-07-07)
+
 ### 重构
 - rebuild admin dashboard
 

+ 1 - 1
cfc-web/package.json

@@ -1,6 +1,6 @@
 {
   "name": "cfc-web",
-  "version": "1.0.8",
+  "version": "1.0.9",
   "private": true,
   "scripts": {
     "dev": "vue-cli-service serve",

+ 4 - 2
cfc-web/src/router/index.js

@@ -306,13 +306,15 @@ const routes = [
         path: 'supply-system/:id',
         name: 'SupplySystemDetail',
         component: () => import('@/views/admin/supply-system/Detail.vue'),
-        meta: { title: '体系详情', perm: 'system:supply' }
+        meta: { title: '体系详情', perm: 'system:supply' },
+        props: true
       },
       {
         path: 'supply-system/:id/edit',
         name: 'SupplySystemEdit',
         component: () => import('@/views/admin/supply-system/Form.vue'),
-        meta: { title: '编辑体系', perm: 'system:supply' }
+        meta: { title: '编辑体系', perm: 'system:supply' },
+        props: true
       },
       {
         path: 'zodiac-configs',