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

chore: auto bump version and changelog [skip ci]

iwt 3 месяцев назад
Родитель
Сommit
22fee1516e

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-3258b6630ae2ed71d28e3d2bd0883d99ef0640f9
+4d9118db75607142e21c37d55cd4cc76fd3081d2

+ 1 - 1
cfc-web/package.json

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

+ 13 - 0
cfc-web/public/CHANGELOG-v1.0.md

@@ -4,6 +4,19 @@
 
 ---
 
+## v1.0.654 (2026-07-30)
+
+### Bug 修复
+- promotion try无catch语法错误 + 创建缺失的member-detail分包页面
+
+### 其他
+- 2. member-detail/report-detail.vue: 新建(pages.json已注册但文件缺失)
+- 3. member-detail/service-role-apply.vue: 新建(pages.json已注册但文件缺失)
+- Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+
+
 ## v1.0.653 (2026-07-30)
 
 ### 其他

+ 14 - 1
cfc-web/public/CHANGELOG.md

@@ -1,6 +1,6 @@
 # 更新日志
 
-> 当前版本: v1.0.653
+> 当前版本: v1.0.654
 
 ## 历史版本
 
@@ -8,6 +8,19 @@
 
 ---
 
+## v1.0.654 (2026-07-30)
+
+### Bug 修复
+- promotion try无catch语法错误 + 创建缺失的member-detail分包页面
+
+### 其他
+- 2. member-detail/report-detail.vue: 新建(pages.json已注册但文件缺失)
+- 3. member-detail/service-role-apply.vue: 新建(pages.json已注册但文件缺失)
+- Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+
+
 ## v1.0.653 (2026-07-30)
 
 ### 其他

+ 720 - 0
docs/superpowers/plans/2026-07-30-wealth-mall-ui-enhance.md

@@ -0,0 +1,720 @@
+# Wealth Page & Mall Homepage UI Enhancement Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Clean up the wealth (富维度) page by removing duplicate entry and redundant user card, densify icon grid, and enhance the mall homepage with search bar and featured product sections.
+
+**Architecture:** Two independent frontend-only changes targeting single-page Vue components in the uni-app mini-program. No backend changes needed. Task 1-3 modify `pages/wealth/index.vue` (main package). Task 4-6 modify `pages/shop/index/index.vue` (shop subpackage).
+
+**Tech Stack:** uni-app (Vue 2 Options API), WeChat Mini-Program
+
+---
+
+## File Structure
+
+| File | Status | Responsibility |
+|------|--------|---------------|
+| `cfc-frontend/pages/wealth/index.vue` | Modify | Remove user-card section, densify to 4-column grid, remove duplicate 推广中心 menu entry |
+| `cfc-frontend/pages/shop/index/index.vue` | Modify | Add search bar, enhance category grid, add recommended products section |
+
+---
+
+### Task 1: Remove user-card section from wealth page
+
+**Files:**
+- Modify: `cfc-frontend/pages/wealth/index.vue:37-45`
+
+The user-card (avatar, nickname, role) at the top of the wealth page is redundant because the profile page already shows this info and the TabBar already provides access. We remove the entire `<view class="user-card">` block.
+
+- [ ] **Step 1: Remove user-card template block**
+
+Edit `pages/wealth/index.vue`, remove lines 37-45 (the user-card view block):
+
+```
+Current lines 37-45:
+ <!-- 用户卡片 -->
+      <view class="user-card" @click="goToProfile">
+        <view class="avatar">👤</view>
+        <view class="user-info">
+          <text class="nickname">{{ nickname || '未设置昵称' }}</text>
+          <text class="role">{{ role === 'parent' ? '家长模式' : (role === 'child' ? '孩子模式' : '成长规划师模式') }}</text>
+        </view>
+        <text class="card-arrow">›</text>
+      </view>
+```
+
+Remove from `<!-- 用户卡片 -->` through the closing `</view>`.
+
+- [ ] **Step 2: Remove related data properties**
+
+In the `data()` function, remove the `nickname` property (line 217: `nickname: ''`) since it's no longer used in the template. Also remove the `nickname` assignment in `onShow()` (line 279: `this.nickname = (userInfo && userInfo.nickname) || ''`).
+
+- [ ] **Step 3: Remove unused CSS**
+
+Remove the `.user-card`, `.avatar`, `.user-info`, `.nickname`, `.role`, `.card-arrow` CSS rule block (lines 559-598), since the user-card DOM is gone.
+
+- [ ] **Step 4: Verify**
+
+Run: `lsp_diagnostics` on `pages/wealth/index.vue` — expect 0 errors.
+
+---
+
+### Task 2: Make service grids 4 columns instead of 3
+
+**Files:**
+- Modify: `cfc-frontend/pages/wealth/index.vue`
+
+The `service-grid` items currently use `width: 33.33%` (3 columns). Change to `width: 25%` (4 columns) to make icons denser.
+
+- [ ] **Step 1: Change CSS width**
+
+Edit `.service-item` CSS rule (line 722-728):
+
+Change:
+```css
+.service-item {
+  width: 33.33%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 16rpx 0;
+}
+```
+
+To:
+```css
+.service-item {
+  width: 25%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 16rpx 0;
+}
+```
+
+- [ ] **Step 2: Verify**
+
+Run: `lsp_diagnostics` on `pages/wealth/index.vue` — expect 0 errors.
+
+---
+
+### Task 3: Remove 推广中心 from bottom menu
+
+**Files:**
+- Modify: `cfc-frontend/pages/wealth/index.vue`
+
+The "推广中心" menu item duplicates "创客中心" (both navigate to `/pages/promotion/index`). Remove the "推广中心" menu entry from the bottom menu list.
+
+- [ ] **Step 1: Remove the duplicate menu item**
+
+Edit `pages/wealth/index.vue`, remove lines 179-182:
+
+```html
+        <view class="menu-item" @click="goToPromotion">
+          <text>📣 推广中心</text>
+          <text class="arrow">›</text>
+        </view>
+```
+
+- [ ] **Step 2: Remove the unused goToPromotion method**
+
+Remove the `goToPromotion()` method (lines 491-493):
+
+```javascript
+    goToPromotion() {
+      uni.navigateTo({ url: '/pages/promotion/index' })
+    },
+```
+
+- [ ] **Step 3: Verify**
+
+Run: `lsp_diagnostics` on `pages/wealth/index.vue` — expect 0 errors.
+
+---
+
+### Task 4: Add search bar to mall homepage
+
+**Files:**
+- Modify: `cfc-frontend/pages/shop/index/index.vue`
+
+Add a search bar at the top of the mall homepage so users can search products by keyword.
+
+- [ ] **Step 1: Add search bar template**
+
+Insert immediately after the `member-banner` `</view>` closing tag (after line 13) and before the `cat-level1` section:
+
+```html
+    <!-- 搜索栏 -->
+    <view class="search-bar" @click="goSearch">
+      <text class="search-icon">🔍</text>
+      <text class="search-placeholder">搜索商品</text>
+    </view>
+```
+
+- [ ] **Step 2: Add goSearch method**
+
+Add to the `methods` block:
+
+```javascript
+    goSearch() {
+      uni.navigateTo({ url: '/pages/shop/search/search' })
+    },
+```
+
+- [ ] **Step 3: Register the search page in pages.json**
+
+Add to the shop subpackage in `pages.json` (after the `index/index` entry, around line 430-435):
+
+```json
+        {
+          "path": "search/search",
+          "style": {
+            "navigationBarTitleText": "搜索商品"
+          }
+        },
+```
+
+- [ ] **Step 4: Create search page**
+
+Create `cfc-frontend/pages/shop/search/search.vue`:
+
+```vue
+<template>
+  <view class="search-container">
+    <!-- 搜索输入框 -->
+    <view class="search-header">
+      <view class="search-input-wrap">
+        <text class="search-icon">🔍</text>
+        <input
+          class="search-input"
+          v-model="keyword"
+          :focus="true"
+          placeholder="输入商品名称"
+          @confirm="onSearch"
+          @input="onInput"
+        />
+        <text v-if="keyword" class="clear-btn" @click="clearKeyword">✕</text>
+      </view>
+      <text class="search-cancel" @click="goBack">取消</text>
+    </view>
+
+    <!-- 搜索建议 / 历史记录 -->
+    <view v-if="!searched" class="suggest-wrap">
+      <view class="suggest-section" v-if="history.length">
+        <view class="suggest-header">
+          <text class="suggest-title">搜索历史</text>
+          <text class="clear-history" @click="clearHistory">清除</text>
+        </view>
+        <view class="history-tags">
+          <text
+            v-for="(item, idx) in history"
+            :key="idx"
+            class="history-tag"
+            @click="searchKeyword(item)"
+          >{{ item }}</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 搜索结果 -->
+    <view v-else class="result-wrap">
+      <view v-if="results.length === 0" class="empty-wrap">
+        <text class="empty-icon">📦</text>
+        <text class="empty-text">没有找到相关商品</text>
+      </view>
+      <view v-else class="product-grid">
+        <view
+          v-for="item in results"
+          :key="item.id"
+          class="product-card"
+          @click="goDetail(item.id)"
+        >
+          <image
+            class="cover"
+            :src="item.image || item.coverImage || '/static/default-product.png'"
+            mode="aspectFill"
+          />
+          <view class="card-body">
+            <text class="product-name">{{ item.name }}</text>
+            <view class="price-row">
+              <text class="price">{{ formatPriceWithSymbol(item.price) }}</text>
+              <text v-if="item.memberPrice" class="member-price">会员{{ formatPriceWithSymbol(item.memberPrice) }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+      <view v-if="loadingMore" class="loading-more">
+        <text class="loading-text">加载中...</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import { productList } from '@/utils/api.js'
+export default {
+  data() {
+    return {
+      keyword: '',
+      searched: false,
+      results: [],
+      page: 1,
+      size: 20,
+      loadingMore: false,
+      noMore: false,
+      history: []
+    }
+  },
+  onLoad() {
+    var historyStr = uni.getStorageSync('searchHistory')
+    this.history = historyStr ? JSON.parse(historyStr) : []
+  },
+  methods: {
+    onInput(e) {
+      this.keyword = e.detail.value
+    },
+    onSearch() {
+      if (!this.keyword.trim()) return
+      this.searched = true
+      this.page = 1
+      this.results = []
+      this.noMore = false
+      this.saveHistory()
+      this.loadResults()
+    },
+    searchKeyword(keyword) {
+      this.keyword = keyword
+      this.onSearch()
+    },
+    loadResults() {
+      if (this.loadingMore) return
+      this.loadingMore = true
+      var params = { page: this.page, size: this.size, keyword: this.keyword.trim() }
+      productList(params).then(res => {
+        this.loadingMore = false
+        if (res.code === 200 && res.data) {
+          var records = res.data.records || []
+          if (this.page === 1) {
+            this.results = records
+          } else {
+            this.results = this.results.concat(records)
+          }
+          var total = res.data.total || 0
+          this.noMore = this.results.length >= total
+        }
+      }).catch(() => {
+        this.loadingMore = false
+      })
+    },
+    saveHistory() {
+      var kw = this.keyword.trim()
+      if (!kw) return
+      var list = this.history.filter(function(h) { return h !== kw })
+      list.unshift(kw)
+      if (list.length > 10) list = list.slice(0, 10)
+      this.history = list
+      uni.setStorageSync('searchHistory', JSON.stringify(list))
+    },
+    clearHistory() {
+      this.history = []
+      uni.removeStorageSync('searchHistory')
+    },
+    clearKeyword() {
+      this.keyword = ''
+      this.searched = false
+      this.results = []
+    },
+    goBack() {
+      uni.navigateBack()
+    },
+    goDetail(id) {
+      uni.navigateTo({ url: '/pages/shop/detail/detail?id=' + id })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.search-container {
+  min-height: 100vh;
+  background: #f5f5f5;
+}
+.search-header {
+  display: flex;
+  align-items: center;
+  padding: 16rpx 20rpx;
+  background: #fff;
+}
+.search-input-wrap {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  background: #f5f5f5;
+  border-radius: 50rpx;
+  padding: 0 24rpx;
+  height: 72rpx;
+}
+.search-icon {
+  font-size: 32rpx;
+  margin-right: 12rpx;
+  flex-shrink: 0;
+}
+.search-input {
+  flex: 1;
+  font-size: 28rpx;
+  color: #333;
+  height: 72rpx;
+}
+.clear-btn {
+  font-size: 28rpx;
+  color: #999;
+  padding: 8rpx;
+  flex-shrink: 0;
+}
+.search-cancel {
+  font-size: 28rpx;
+  color: #666;
+  margin-left: 20rpx;
+  flex-shrink: 0;
+}
+.suggest-wrap {
+  padding: 20rpx;
+}
+.suggest-section {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  margin-bottom: 20rpx;
+}
+.suggest-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20rpx;
+}
+.suggest-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+}
+.clear-history {
+  font-size: 24rpx;
+  color: #999;
+}
+.history-tags {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16rpx;
+}
+.history-tag {
+  padding: 12rpx 24rpx;
+  background: #f5f5f5;
+  border-radius: 30rpx;
+  font-size: 24rpx;
+  color: #666;
+}
+.result-wrap {
+  padding: 0 20rpx;
+}
+.product-grid {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  padding: 20rpx 0;
+}
+.product-card {
+  width: 49%;
+  background: #fff;
+  border-radius: 16rpx;
+  overflow: hidden;
+  margin-bottom: 20rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.06);
+}
+.cover {
+  width: 100%;
+  height: 320rpx;
+  background: #f0f0f0;
+}
+.card-body {
+  padding: 16rpx;
+}
+.product-name {
+  display: block;
+  font-size: 28rpx;
+  color: #333;
+  lines: 2;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  margin-bottom: 12rpx;
+}
+.price-row {
+  display: flex;
+  align-items: baseline;
+  margin-bottom: 8rpx;
+}
+.price {
+  font-size: 32rpx;
+  color: #F97316;
+  font-weight: bold;
+  margin-right: 12rpx;
+}
+.member-price {
+  font-size: 22rpx;
+  color: #999;
+}
+.empty-wrap {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding-top: 200rpx;
+}
+.empty-icon {
+  font-size: 100rpx;
+  margin-bottom: 30rpx;
+}
+.empty-text {
+  font-size: 28rpx;
+  color: #999;
+}
+.loading-more {
+  text-align: center;
+  padding: 30rpx;
+}
+.loading-text {
+  font-size: 24rpx;
+  color: #999;
+}
+.bottom-spacer {
+  height: 120rpx;
+}
+</style>
+```
+
+- [ ] **Step 5: Add search bar CSS to shop/index**
+
+Add search bar styles to `pages/shop/index/index.vue`:
+
+```css
+/* 搜索栏 */
+.search-bar {
+  display: flex;
+  align-items: center;
+  background: #fff;
+  margin: 16rpx 16rpx 0;
+  padding: 16rpx 24rpx;
+  border-radius: 50rpx;
+  border: 1rpx solid #eee;
+}
+.search-icon {
+  font-size: 32rpx;
+  margin-right: 12rpx;
+}
+.search-placeholder {
+  font-size: 26rpx;
+  color: #bbb;
+}
+```
+
+- [ ] **Step 6: Verify**
+
+Run: `lsp_diagnostics` on both `pages/shop/index/index.vue` and `pages/shop/search/search.vue` — expect 0 errors.
+
+---
+
+### Task 5: Enhance category grid layout on mall homepage
+
+**Files:**
+- Modify: `cfc-frontend/pages/shop/index/index.vue`
+
+The current category grid is a 2-column full-width layout. Enhance it with rounded icons and auto-arrangement for a more visual mall feel.
+
+- [ ] **Step 1: Improve category item CSS**
+
+Update the category styles in `pages/shop/index/index.vue`:
+
+Replace existing `.cat1-item` rule:
+
+```css
+.cat1-item {
+  width: 50%;
+  box-sizing: border-box;
+  padding: 8rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+  border-radius: 12rpx;
+  margin-bottom: 8rpx;
+}
+```
+
+With:
+
+```css
+.cat1-item {
+  width: 25%;
+  box-sizing: border-box;
+  padding: 16rpx 8rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+  border-radius: 16rpx;
+  margin-bottom: 8rpx;
+}
+```
+
+This changes from 2 columns to 4 columns for a denser, more mall-like category grid.
+
+- [ ] **Step 2: Verify**
+
+Run: `lsp_diagnostics` on `pages/shop/index/index.vue` — expect 0 errors.
+
+---
+
+### Task 6: Add recommended products carousel to mall homepage
+
+**Files:**
+- Modify: `cfc-frontend/pages/shop/index/index.vue`
+
+Add a "为你推荐" (Recommended for You) product carousel section between the categories and the main product list.
+
+- [ ] **Step 1: Add recommended section template**
+
+Insert after the `cat-level2-scroll` closing `</scroll-view>` and before the main `product-list` `<scroll-view>`:
+
+```html
+    <!-- 为你推荐 -->
+    <view class="recommend-section" v-if="recommended.length > 0">
+      <view class="recommend-header">
+        <text class="recommend-title">🔥 为你推荐</text>
+      </view>
+      <scroll-view scroll-x enable-flex show-scrollbar="false" class="recommend-scroll">
+        <view
+          v-for="item in recommended"
+          :key="item.id"
+          class="recommend-card"
+          @click="goDetail(item.id)"
+        >
+          <image
+            class="recommend-img"
+            :src="item.image || item.coverImage || '/static/default-product.png'"
+            mode="aspectFill"
+          />
+          <text class="recommend-name">{{ item.name }}</text>
+          <text class="recommend-price">{{ formatPriceWithSymbol(item.price) }}</text>
+        </view>
+      </scroll-view>
+    </view>
+```
+
+- [ ] **Step 2: Add data and method**
+
+Add to `data()`:
+```javascript
+      recommended: [],
+```
+
+Add to `onLoad()` after `this.loadProducts()`:
+```javascript
+    this.loadRecommended()
+```
+
+Add method:
+```javascript
+    loadRecommended() {
+      productList({ page: 1, size: 6, sort: 'sales' }).then(res => {
+        if (res.code === 200 && res.data) {
+          this.recommended = res.data.records || []
+        }
+      }).catch(() => {})
+    },
+```
+
+- [ ] **Step 3: Add recommend section CSS**
+
+Add to styles:
+
+```css
+/* 为你推荐 */
+.recommend-section {
+  margin: 16rpx;
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 20rpx;
+}
+.recommend-header {
+  margin-bottom: 16rpx;
+}
+.recommend-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+}
+.recommend-scroll {
+  white-space: nowrap;
+  padding-bottom: 8rpx;
+}
+.recommend-card {
+  display: inline-flex;
+  flex-direction: column;
+  width: 200rpx;
+  margin-right: 16rpx;
+  flex-shrink: 0;
+}
+.recommend-img {
+  width: 200rpx;
+  height: 200rpx;
+  border-radius: 12rpx;
+  background: #f0f0f0;
+  margin-bottom: 8rpx;
+}
+.recommend-name {
+  font-size: 24rpx;
+  color: #333;
+  lines: 1;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  margin-bottom: 4rpx;
+}
+.recommend-price {
+  font-size: 26rpx;
+  color: #F97316;
+  font-weight: bold;
+}
+```
+
+Also add `formatPriceWithSymbol` helper if not already present. Check the script for it - if missing, add:
+
+```javascript
+    formatPriceWithSymbol(price) {
+      if (price === null || price === undefined) return ''
+      return '¥' + parseFloat(price).toFixed(2)
+    },
+```
+
+- [ ] **Step 4: Verify**
+
+Run: `lsp_diagnostics` on `pages/shop/index/index.vue` — expect 0 errors.
+
+---
+
+### Task 7: Commit
+
+- [ ] **Step 1: Stage and commit**
+
+```bash
+git add pages/wealth/index.vue pages/shop/index/index.vue pages/shop/search/search.vue pages.json
+git commit -m "feat: wealth page cleanup and mall homepage enhancement
+
+- Remove user-card section from wealth page (redundant)
+- Densify service grid to 4 columns
+- Remove duplicate 推广中心 menu entry
+- Add search bar and search page to mall homepage
+- Enhance category grid to 4 columns
+- Add recommended products carousel"
+```