Explorar o código

fix: Danshop迁移到cfc-backend + coupons/upgrade模板修复

- config.js: 移除DANSHOP_BASE_URL,统一使用API_BASE_URL(9082)
- api.js: 移除danshopRequest/goodsCategory,所有danshop*函数改用request()
- coupons.vue: 添加statusClassMap computed替代方法调用
- upgrade.vue: 修复template单根元素+重新缩进
User hai 4 meses
pai
achega
d78b31c750

+ 0 - 10
cfc-frontend/config.js

@@ -12,7 +12,6 @@
 
 // 默认值(兜底)
 let API_BASE_URL = 'http://localhost:9082'
-let DANSHOP_BASE_URL = 'http://localhost:8081'
 
 try {
   const accountInfo = uni.getAccountInfoSync()
@@ -23,17 +22,14 @@ try {
       case 'develop':
         // 开发版 - 本地开发
         API_BASE_URL = 'http://localhost:9082'
-        DANSHOP_BASE_URL = 'http://localhost:9081'
         break
       case 'trial':
         // 体验版
         API_BASE_URL = 'https://cfc.iwintrue.com'
-        DANSHOP_BASE_URL = 'https://dev.iwintrue.com/danshop'
         break
       case 'release':
         // 正式版
         API_BASE_URL = 'https://cfc.etotem.com.cn'
-        DANSHOP_BASE_URL = 'https://danshop.etotem.com.cn'
         break
     }
   }
@@ -43,15 +39,9 @@ try {
 
 export default {
   API_BASE_URL,
-  DANSHOP_BASE_URL,
 
   /** 构建 API 完整 URL */
   api(path) {
     return API_BASE_URL + path
-  },
-
-  /** 构建 DAN 测评 API URL */
-  danshop(path) {
-    return DANSHOP_BASE_URL + path
   }
 }

+ 118 - 118
cfc-frontend/pages/membership/upgrade.vue

@@ -1,144 +1,144 @@
 <template>
   <view class="upgrade-page-wrapper">
-  <scroll-view class="page" scroll-y>
-    <!-- 当前会员状态卡片 -->
-    <view class="status-card">
-      <view class="status-badge" :class="isFree ? 'badge-free' : 'badge-family'">
-        <text class="badge-icon">{{ isFree ? '○' : '●' }}</text>
-        <text class="badge-text">{{ currentLevelName }}</text>
-      </view>
-      <view class="status-body" v-if="membership && membership.levelCode !== 'FREE'">
-        <view class="expiry-row">
-          <text class="expiry-label">到期时间</text>
-          <text class="expiry-value">{{ formatDate(membership.endDate) }}</text>
+    <scroll-view class="page" scroll-y>
+      <!-- 当前会员状态卡片 -->
+      <view class="status-card">
+        <view class="status-badge" :class="isFree ? 'badge-free' : 'badge-family'">
+          <text class="badge-icon">{{ isFree ? '○' : '●' }}</text>
+          <text class="badge-text">{{ currentLevelName }}</text>
         </view>
-        <view class="days-remaining" v-if="membership.isActive">
-          <text class="days-num">{{ membership.daysRemaining || 0 }}</text>
-          <text class="days-unit">天剩余</text>
+        <view class="status-body" v-if="membership && membership.levelCode !== 'FREE'">
+          <view class="expiry-row">
+            <text class="expiry-label">到期时间</text>
+            <text class="expiry-value">{{ formatDate(membership.endDate) }}</text>
+          </view>
+          <view class="days-remaining" v-if="membership.isActive">
+            <text class="days-num">{{ membership.daysRemaining || 0 }}</text>
+            <text class="days-unit">天剩余</text>
+          </view>
+          <view class="expired-msg" v-else>
+            <text class="expired-text">会员已过期,续费恢复权益</text>
+          </view>
         </view>
-        <view class="expired-msg" v-else>
-          <text class="expired-text">会员已过期,续费恢复权益</text>
+        <view class="status-body" v-else>
+          <text class="free-hint">当前为免费版,开通家庭会员解锁全部特权</text>
         </view>
       </view>
-      <view class="status-body" v-else>
-        <text class="free-hint">当前为免费版,开通家庭会员解锁全部特权</text>
-      </view>
-    </view>
 
-    <!-- 家庭会员开通卡片 -->
-    <view class="upgrade-card" v-if="isFree">
-      <view class="upgrade-header">
-        <text class="upgrade-title">家庭会员</text>
-        <text class="upgrade-subtitle">FAMILY</text>
-      </view>
-      <view class="upgrade-price">
-        <text class="price-symbol">¥</text>
-        <text class="price-amount">365</text>
-        <text class="price-unit">/年</text>
-      </view>
-      <view class="feature-list">
-        <view class="feature-item" v-for="(feature, idx) in features" :key="idx">
-          <text class="feature-check">✓</text>
-          <text class="feature-text">{{ feature }}</text>
+      <!-- 家庭会员开通卡片 -->
+      <view class="upgrade-card" v-if="isFree">
+        <view class="upgrade-header">
+          <text class="upgrade-title">家庭会员</text>
+          <text class="upgrade-subtitle">FAMILY</text>
         </view>
+        <view class="upgrade-price">
+          <text class="price-symbol">¥</text>
+          <text class="price-amount">365</text>
+          <text class="price-unit">/年</text>
+        </view>
+        <view class="feature-list">
+          <view class="feature-item" v-for="(feature, idx) in features" :key="idx">
+            <text class="feature-check">✓</text>
+            <text class="feature-text">{{ feature }}</text>
+          </view>
+        </view>
+        <!-- Coupon section -->
+        <view class="coupon-row" @click="openCouponPicker">
+          <text class="coupon-row-label">优惠券</text>
+          <text v-if="selectedCoupon" class="coupon-row-discount">-{{ formatPriceWithSymbol(selectedCoupon.value) }} {{ selectedCoupon.name }}</text>
+          <text v-else class="coupon-row-placeholder">选择优惠券</text>
+          <text class="coupon-row-arrow">›</text>
+        </view>
+        <button class="btn-upgrade" @click="handleUpgrade">立即开通</button>
       </view>
-      <!-- Coupon section -->
-      <view class="coupon-row" @click="openCouponPicker">
-        <text class="coupon-row-label">优惠券</text>
-        <text v-if="selectedCoupon" class="coupon-row-discount">-{{ formatPriceWithSymbol(selectedCoupon.value) }} {{ selectedCoupon.name }}</text>
-        <text v-else class="coupon-row-placeholder">选择优惠券</text>
-        <text class="coupon-row-arrow">›</text>
-      </view>
-      <button class="btn-upgrade" @click="handleUpgrade">立即开通</button>
-    </view>
-
-    <!-- 续费卡片 -->
-    <view class="upgrade-card" v-else>
-      <view class="upgrade-header">
-        <text class="upgrade-title">续费会员</text>
-        <text class="upgrade-subtitle">保持权益不中断</text>
-      </view>
-      <view class="upgrade-price">
-        <text class="price-symbol">¥</text>
-        <text class="price-amount">365</text>
-        <text class="price-unit">/年</text>
-      </view>
-      <!-- Coupon section -->
-      <view class="coupon-row" @click="openCouponPicker">
-        <text class="coupon-row-label">优惠券</text>
-        <text v-if="selectedCoupon" class="coupon-row-discount">-{{ formatPriceWithSymbol(selectedCoupon.value) }} {{ selectedCoupon.name }}</text>
-        <text v-else class="coupon-row-placeholder">选择优惠券</text>
-        <text class="coupon-row-arrow">›</text>
-      </view>
-      <button class="btn-upgrade btn-renew" @click="handleRenew">立即续费</button>
-    </view>
 
-    <!-- 权益对比表 -->
-    <view class="compare-section">
-      <text class="section-title">权益对比</text>
-      <view class="compare-table">
-        <view class="compare-row header-row">
-          <text class="compare-cell cell-feature">权益项</text>
-          <text class="compare-cell cell-free">免费</text>
-          <text class="compare-cell cell-family active">家庭会员</text>
+      <!-- 续费卡片 -->
+      <view class="upgrade-card" v-else>
+        <view class="upgrade-header">
+          <text class="upgrade-title">续费会员</text>
+          <text class="upgrade-subtitle">保持权益不中断</text>
+        </view>
+        <view class="upgrade-price">
+          <text class="price-symbol">¥</text>
+          <text class="price-amount">365</text>
+          <text class="price-unit">/年</text>
         </view>
-        <view class="compare-row" v-for="(item, idx) in compareData" :key="idx">
-          <text class="compare-cell cell-feature">{{ item.name }}</text>
-          <text class="compare-cell cell-free">{{ item.free }}</text>
-          <text class="compare-cell cell-family active">{{ item.family }}</text>
+        <!-- Coupon section -->
+        <view class="coupon-row" @click="openCouponPicker">
+          <text class="coupon-row-label">优惠券</text>
+          <text v-if="selectedCoupon" class="coupon-row-discount">-{{ formatPriceWithSymbol(selectedCoupon.value) }} {{ selectedCoupon.name }}</text>
+          <text v-else class="coupon-row-placeholder">选择优惠券</text>
+          <text class="coupon-row-arrow">›</text>
         </view>
+        <button class="btn-upgrade btn-renew" @click="handleRenew">立即续费</button>
       </view>
-    </view>
 
-    <!-- 升级记录 -->
-    <view class="records-section" v-if="records.length > 0">
-      <text class="section-title">升级记录</text>
-      <view class="record-item" v-for="record in records" :key="record.id">
-        <view class="record-left">
-          <text class="record-level">{{ record.levelName }}</text>
-          <text class="record-time">{{ formatDate(record.createdAt) }}</text>
+      <!-- 权益对比表 -->
+      <view class="compare-section">
+        <text class="section-title">权益对比</text>
+        <view class="compare-table">
+          <view class="compare-row header-row">
+            <text class="compare-cell cell-feature">权益项</text>
+            <text class="compare-cell cell-free">免费</text>
+            <text class="compare-cell cell-family active">家庭会员</text>
+          </view>
+          <view class="compare-row" v-for="(item, idx) in compareData" :key="idx">
+            <text class="compare-cell cell-feature">{{ item.name }}</text>
+            <text class="compare-cell cell-free">{{ item.free }}</text>
+            <text class="compare-cell cell-family active">{{ item.family }}</text>
+          </view>
         </view>
-        <text class="record-amount">{{ formatPriceWithSymbol(record.amount) }}</text>
       </view>
-    </view>
-  </scroll-view>
-
-  <!-- Coupon Picker Bottom Sheet -->
-  <view class="modal-mask" v-if="showCouponPicker" @click="showCouponPicker = false">
-    <view class="coupon-picker" @click.stop>
-      <view class="picker-header">
-        <text class="picker-title">选择优惠券</text>
-        <text class="picker-close" @click="showCouponPicker = false">关闭</text>
-      </view>
-      <scroll-view scroll-y class="picker-list">
-        <view
-          v-for="coupon in availableCoupons"
-          :key="coupon.id"
-          class="picker-item"
-          :class="selectedCoupon && selectedCoupon.id === coupon.id ? 'picker-item-active' : ''"
-          @click="selectCoupon(coupon)"
-        >
-          <view class="picker-item-left">
-            <text class="picker-item-value">{{ formatPriceWithSymbol(coupon.value) }}</text>
+
+      <!-- 升级记录 -->
+      <view class="records-section" v-if="records.length > 0">
+        <text class="section-title">升级记录</text>
+        <view class="record-item" v-for="record in records" :key="record.id">
+          <view class="record-left">
+            <text class="record-level">{{ record.levelName }}</text>
+            <text class="record-time">{{ formatDate(record.createdAt) }}</text>
           </view>
-          <view class="picker-item-right">
-            <text class="picker-item-name">{{ coupon.name }}</text>
-            <text class="picker-item-condition">{{ getCouponCondition(coupon.minSpend) }}</text>
+          <text class="record-amount">{{ formatPriceWithSymbol(record.amount) }}</text>
+        </view>
+      </view>
+    </scroll-view>
+
+    <!-- Coupon Picker Bottom Sheet -->
+    <view class="modal-mask" v-if="showCouponPicker" @click="showCouponPicker = false">
+      <view class="coupon-picker" @click.stop>
+        <view class="picker-header">
+          <text class="picker-title">选择优惠券</text>
+          <text class="picker-close" @click="showCouponPicker = false">关闭</text>
+        </view>
+        <scroll-view scroll-y class="picker-list">
+          <view
+            v-for="coupon in availableCoupons"
+            :key="coupon.id"
+            class="picker-item"
+            :class="selectedCoupon && selectedCoupon.id === coupon.id ? 'picker-item-active' : ''"
+            @click="selectCoupon(coupon)"
+          >
+            <view class="picker-item-left">
+              <text class="picker-item-value">{{ formatPriceWithSymbol(coupon.value) }}</text>
+            </view>
+            <view class="picker-item-right">
+              <text class="picker-item-name">{{ coupon.name }}</text>
+              <text class="picker-item-condition">{{ getCouponCondition(coupon.minSpend) }}</text>
+            </view>
+            <view class="picker-item-check" v-if="selectedCoupon && selectedCoupon.id === coupon.id">
+              <text>✓</text>
+            </view>
           </view>
-          <view class="picker-item-check" v-if="selectedCoupon && selectedCoupon.id === coupon.id">
-            <text>✓</text>
+          <view class="picker-empty" v-if="availableCoupons.length === 0">
+            <text>暂无可用优惠券</text>
           </view>
+        </scroll-view>
+        <view class="picker-footer" v-if="selectedCoupon">
+          <button class="picker-btn-remove" @click="removeCoupon">不使用优惠券</button>
         </view>
-        <view class="picker-empty" v-if="availableCoupons.length === 0">
-          <text>暂无可用优惠券</text>
-        </view>
-      </scroll-view>
-      <view class="picker-footer" v-if="selectedCoupon">
-        <button class="picker-btn-remove" @click="removeCoupon">不使用优惠券</button>
       </view>
     </view>
   </view>
-  </view>
 </template>
 
 <script>

+ 18 - 3
cfc-frontend/pages/profile/coupons.vue

@@ -33,7 +33,7 @@
           v-for="coupon in filteredList"
           :key="coupon.id"
           class="coupon-card"
-          :class="getStatusClass(coupon)"
+          :class="statusClassMap[coupon.id]"
         >
           <view class="card-left">
             <text class="card-value">{{ formatPriceWithSymbol(coupon.value) }}</text>
@@ -42,7 +42,7 @@
           <view class="card-right">
             <view class="card-header">
               <text class="card-name">{{ coupon.name }}</text>
-              <text v-if="getStatusLabel(coupon)" class="card-status-badge" :class="getStatusClass(coupon)">{{ getStatusLabel(coupon) }}</text>
+              <text v-if="getStatusLabel(coupon)" class="card-status-badge" :class="statusClassMap[coupon.id]">{{ getStatusLabel(coupon) }}</text>
             </view>
             <text class="card-condition">{{ getConditionText(coupon.minSpend) }}</text>
             <text class="card-scope">{{ getScopeText(coupon.applicableTo) }}</text>
@@ -77,7 +77,7 @@ export default {
     }
   },
   computed: {
-    filteredList() {
+    filteredList: function() {
       var self = this
       var currentStatus = this.tabs[this.tabIndex].status
       if (currentStatus === 'AVAILABLE') {
@@ -88,6 +88,21 @@ export default {
       return this.coupons.filter(function(c) {
         return c.status === currentStatus
       })
+    },
+    statusClassMap: function() {
+      var map = {}
+      var list = this.filteredList
+      for (var i = 0; i < list.length; i++) {
+        var c = list[i]
+        if (c.status === 'USED') {
+          map[c.id] = 'status-used'
+        } else if (c.status === 'EXPIRED') {
+          map[c.id] = 'status-expired'
+        } else {
+          map[c.id] = ''
+        }
+      }
+      return map
     }
   },
   onShow() {

+ 73 - 68
cfc-frontend/utils/api.js

@@ -1,7 +1,6 @@
 import config from '@/config.js'
 
 const BASE_URL = config.API_BASE_URL
-const DANSHOP_BASE = config.DANSHOP_BASE_URL
 
 var _redirectingToLogin = false
 
@@ -956,15 +955,6 @@ export const vendorInfo = () => {
 
 // ===================== 浠艾福俱乐部 - 商品系统 =====================
 
-export const goodsCategory = (parentId = '0') => {
-  return new Promise((resolve, reject) => {
-    uni.request({
-      url: DANSHOP_BASE + '/buyer/goods/category/get/' + parentId,
-      method: 'GET'
-    })
-  })
-}
-
 // 商品列表(公开)
 export const productList = (params) => {
   return request('/api/product/list', 'POST', params)
@@ -1295,62 +1285,30 @@ export const deleteInsurance = (id) => {
   return request('/api/wealth/insurance/delete', 'POST', { id })
 }
 
-// ===================== DanShop 电商模块 (独立模块 port 9081) =====================
-
-const danshopRequest = (path, method = 'POST', data = {}) => {
-  const token = uni.getStorageSync('token')
-  return new Promise((resolve, reject) => {
-    uni.request({
-      url: DANSHOP_BASE + path,
-      method: method,
-      data: data,
-      header: {
-        'Content-Type': 'application/json',
-        'Authorization': token ? `Bearer ${token}` : ''
-      },
-      success: (res) => {
-        if (res.statusCode === 401 || res.data.code === 401) {
-          _clearAuthAndRedirect()
-          reject(res.data)
-          return
-        }
-        if (res.data.code === 200 || res.data.code === 0) {
-          resolve(res.data)
-        } else {
-          uni.showToast({ title: res.data.message || '请求失败', icon: 'none' })
-          reject(res.data)
-        }
-      },
-      fail: (err) => {
-        uni.showToast({ title: '网络请求失败', icon: 'none' })
-        reject(err)
-      }
-    })
-  })
-}
-
-export const danshopCategoryList = (data) => danshopRequest('/api/shop/category/list', 'POST', data)
-export const danshopCategoryTree = (data) => danshopRequest('/api/shop/category/tree', 'POST', data)
-export const danshopProductList = (data) => danshopRequest('/api/shop/product/list', 'POST', data)
-export const danshopProductDetail = (data) => danshopRequest('/api/shop/product/detail', 'POST', data)
-export const danshopCartList = (data) => danshopRequest('/api/shop/cart/list', 'POST', data)
-export const danshopCartAdd = (data) => danshopRequest('/api/shop/cart/add', 'POST', data)
-export const danshopCartRemove = (data) => danshopRequest('/api/shop/cart/remove', 'POST', data)
-export const danshopCartClear = (data) => danshopRequest('/api/shop/cart/clear', 'POST', data)
-export const danshopOrderCreate = (data) => danshopRequest('/api/shop/order/create', 'POST', data)
-export const danshopOrderList = (data) => danshopRequest('/api/shop/order/list', 'POST', data)
-export const danshopOrderDetail = (data) => danshopRequest('/api/shop/order/detail', 'POST', data)
-export const danshopOrderConfirm = (data) => danshopRequest('/api/shop/order/confirm', 'POST', data)
-export const danshopOrderCancel = (data) => danshopRequest('/api/shop/order/cancel', 'POST', data)
-export const danshopAddressList = (data) => danshopRequest('/api/shop/address/list', 'POST', data)
-export const danshopAddressSave = (data) => danshopRequest('/api/shop/address/save', 'POST', data)
-export const danshopAddressDelete = (data) => danshopRequest('/api/shop/address/delete', 'POST', data)
-export const danshopAddressSetDefault = (data) => danshopRequest('/api/shop/address/setDefault', 'POST', data)
-export const danshopPaymentPrecreate = (data) => danshopRequest('/api/shop/payment/precreate', 'POST', data)
-export const danshopLogisticsQuery = (data) => danshopRequest('/api/shop/logistics/query', 'POST', data)
-export const danshopFavoriteList = (data) => danshopRequest('/api/shop/favorite/list', 'POST', data)
-export const danshopFavoriteAdd = (data) => danshopRequest('/api/shop/favorite/add', 'POST', data)
-export const danshopFavoriteRemove = (data) => danshopRequest('/api/shop/favorite/remove', 'POST', data)
+// ===================== 商城模块(已迁移至 cfc-backend) =====================
+
+export const danshopCategoryList = (data) => request('/api/shop/category/list', 'POST', data)
+export const danshopCategoryTree = (data) => request('/api/shop/category/tree', 'POST', data)
+export const danshopProductList = (data) => request('/api/shop/product/list', 'POST', data)
+export const danshopProductDetail = (data) => request('/api/shop/product/detail', 'POST', data)
+export const danshopCartList = (data) => request('/api/shop/cart/list', 'POST', data)
+export const danshopCartAdd = (data) => request('/api/shop/cart/add', 'POST', data)
+export const danshopCartRemove = (data) => request('/api/shop/cart/remove', 'POST', data)
+export const danshopCartClear = (data) => request('/api/shop/cart/clear', 'POST', data)
+export const danshopOrderCreate = (data) => request('/api/shop/order/create', 'POST', data)
+export const danshopOrderList = (data) => request('/api/shop/order/list', 'POST', data)
+export const danshopOrderDetail = (data) => request('/api/shop/order/detail', 'POST', data)
+export const danshopOrderConfirm = (data) => request('/api/shop/order/confirm', 'POST', data)
+export const danshopOrderCancel = (data) => request('/api/shop/order/cancel', 'POST', data)
+export const danshopAddressList = (data) => request('/api/shop/address/list', 'POST', data)
+export const danshopAddressSave = (data) => request('/api/shop/address/save', 'POST', data)
+export const danshopAddressDelete = (data) => request('/api/shop/address/delete', 'POST', data)
+export const danshopAddressSetDefault = (data) => request('/api/shop/address/setDefault', 'POST', data)
+export const danshopPaymentPrecreate = (data) => request('/api/shop/payment/precreate', 'POST', data)
+export const danshopLogisticsQuery = (data) => request('/api/shop/logistics/query', 'POST', data)
+export const danshopFavoriteList = (data) => request('/api/shop/favorite/list', 'POST', data)
+export const danshopFavoriteAdd = (data) => request('/api/shop/favorite/add', 'POST', data)
+export const danshopFavoriteRemove = (data) => request('/api/shop/favorite/remove', 'POST', data)
 
 // ===== 健康打卡 =====
 export const healthCheckinList = (data) => request('/api/health/checkin/list', 'POST', data)
@@ -1370,9 +1328,9 @@ export const getTaskHistoryByCategory = (childId, category, page, size) => {
   return request('/api/tasks/history', 'POST', { childId: childId, category: category, page: page || 1, size: size || 10 })
 }
 
-// ===== 维度商品(domain 筛选,使用已有 danshopProductList) =====
+// ===== 维度商品(domain 筛选,使用 productList) =====
 export const getProductsByDomain = (domain, page, size) => {
-  return danshopProductList({ domain: domain, page: page || 1, size: size || 6 })
+  return productList({ domain: domain, page: page || 1, size: size || 6 })
 }
 
 // ===== 健康报告(两阶段入库) =====
@@ -1509,3 +1467,50 @@ export const verifyMathAnswer = (childId, expression, userAnswer) => {
 export const completeMathQuiz = (data) => {
   return request('/api/wisdom/math/complete', 'POST', data)
 }
+
+// ===== 关系质量评分 =====
+export const getRelationshipScores = (familyId) => {
+  return request('/api/family/relationship/scores', 'POST', { familyId })
+}
+
+export const calculateRelationshipScore = (memberId) => {
+  return request('/api/family/relationship/calculate', 'POST', { memberId })
+}
+
+export const getHealthAlerts = (familyId) => {
+  return request('/api/family/relationship/health-alerts', 'POST', { familyId })
+}
+
+export const getMilestones = (familyId, daysAhead) => {
+  return request('/api/family/relationship/milestones', 'POST', { familyId, daysAhead })
+}
+
+// ===== 互动记录 =====
+export const addInteraction = (data) => {
+  return request('/api/family/interaction/add', 'POST', data)
+}
+
+export const listInteractions = (data) => {
+  return request('/api/family/interaction/list', 'POST', data)
+}
+
+export const getInteractionTypes = () => {
+  return request('/api/family/interaction/types', 'GET', {})
+}
+
+// ===== 关系问卷 =====
+export const generateQuestionnaire = (memberId) => {
+  return request('/api/family/questionnaire/generate', 'POST', { memberId })
+}
+
+export const submitQuestionnaire = (data) => {
+  return request('/api/family/questionnaire/submit', 'POST', data)
+}
+
+export const getQuestionnaireSnapshot = (memberId) => {
+  return request('/api/family/questionnaire/snapshot/' + memberId, 'GET', {})
+}
+
+export const getQuestionnaireHistory = (memberId) => {
+  return request('/api/family/questionnaire/history/' + memberId, 'GET', {})
+}