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

Merge branch 'redesign‌-260706' of http://git.huaml.com/zhh/front-end into redesign‌-260706

ouyang 3 дней назад
Родитель
Сommit
4ebedbe6e4

+ 0 - 1127
hdApp/src/admin/goods/list.vue

@@ -1,1127 +0,0 @@
-<template>
-  <view class="app-content">
-
-    <view class="modern-tabs-container">
-      <view class="modern-tabs">
-        <view class="modern-tab" :class="{ 'active': priceType === -1 }" @click="switchPriceTab(-1)" >
-          <view class="tab-content">
-            <text class="tab-text">全部</text>
-          </view>
-        </view>
-        <view class="modern-tab" :class="{ 'active': priceType === 1 }" @click="switchPriceTab(1)" >
-          <view class="tab-content">
-            <text class="tab-text">有价</text>
-          </view>
-        </view>
-        <view class="modern-tab" :class="{ 'active': priceType === 0 }" @click="switchPriceTab(0)" >
-          <view class="tab-content">
-            <text class="tab-text">无价</text>
-          </view>
-        </view>
-      </view>
-    </view>
-
-		<view class="input-wrap">
-      <view>
-        <button class="admin-button-com middle " :class="[searchType == 0 ? 'blue' : 'default']" style="width:100upx;margin-right:7upx;" @click="changeSearchType(0)">名称</button>
-        <button class="admin-button-com middle " :class="[searchType == 1 ? 'blue' : 'default']" style="width:100upx;margin-right:7upx;" @click="changeSearchType(1)">编号</button>
-      </view>
-			<view class="search-bar">
-				<app-search-module v-model="searchText" placeholder="请输入" @input="searchFn" />
-			</view>
-			<view>
-        <button class="admin-button-com middle blue" style="margin-right:7upx;margin-left:7upx;width:100upx;" @click="openFilterPanel">筛选</button>
-        <button class="admin-button-com middle blue" style="margin-right:7upx;width:100upx;" @click="rank">排序</button>
-        <button class="admin-button-com middle blue" style="width:100upx;" @click="addGoods()">新增</button>
-			</view>
-		</view>
-    <view class="app-middle">
-      <scroll-view scroll-y scroll-with-animation class="tab-view" :scroll-top="scrollTop">
-        <view v-for="(item,index) in tabbar" :key="index" class="tab-bar-item" :class="[currentTab==index ? 'active' : '']" :data-current="index" @tap.stop="swichTab($event ,item)" >
-          <text>{{item.categoryName || ''}}</text>
-        </view>
-      </scroll-view>
-      <block v-for="(item,index) in tabbar" :key="index">
-        <scroll-view scroll-y class="right-box" v-if="currentTab==index" @scrolltolower="goBottom">
-          <block v-if="!$util.isEmpty(list.data)">
-            <view class="page-view">
-              <view class="goods-list" v-for="(goodsItem, idx) in list.data" :key="idx">
-                <view class="goods-list-top" @click="edit(goodsItem.id)">
-                  <view class="img-blo">
-                    <block v-if="goodsItem.stock <= 0">
-                      <view class="sold-out">
-                        <view class="sold-out-xj">已下架</view>
-                      </view>
-                    </block>
-                    <block v-else>
-                      <view v-if="goodsItem.status == 0" class="sold-out">
-                        <view class="sold-out-xj">隐藏</view>
-                      </view>
-                    </block>
-                    <img :src="goodsItem.smallCover" mode="aspectFit" />
-                  </view>
-                  <view class="goods-det">
-                    <view class="goods-det-top" style="margin-bottom:4upx;">
-                      <view class="goods-name" style="font-size:29upx;font-weight:bold;	overflow: hidden;text-overflow: ellipsis;white-space: nowrap;width:350upx;">{{ goodsItem.name||'' }}</view>
-                    </view>
-                    <view class="operate-wrap" style="margin-bottom:15upx;">
-                      <view v-if="goodsItem.priceType == 1">
-                        <!-- 当涨价开关开启时,显示涨价后价格和原价 -->
-                        <view class="price-container">
-                          <text class="goods-price" style="font-size:26upx;">¥{{ goodsItem.price?parseFloat(goodsItem.price):0 }}</text>
-                          <text class="original-price" style="font-size:22upx;" v-if="parseFloat(goodsItem.price)!=parseFloat(goodsItem.priceBeforeRise)">¥{{ goodsItem.priceBeforeRise?parseFloat(goodsItem.priceBeforeRise):0 }}</text>
-                        </view>
-                      </view>
-                      <view class="goods-price" style="font-size:26upx;" v-else>---</view>
-                    </view>
-                    <view class="operate-wrap">
-                      <view style="font-size:23upx;">
-                        <text v-if="Number(goodsItem.sn)>0" style="color:#078c4a;font-weight:bold;margin-right:15upx;font-size:25upx;">#{{ goodsItem.sn }}</text>
-                        <text>库存</text> <text style="font-weight:bold;margin-left:8upx;">{{goodsItem.stock}}</text>
-                        <text style="margin-left:18upx;color:#CCCCCC;">已售 {{ goodsItem.actualSold }}</text>
-                      </view>
-                    </view>
-                  </view>
-                </view>
-                <view class="goods-list-bottom flex-center-between">
-                  <text @click.stop="printLabel(goodsItem)">打标签</text>
-                  <text @click.stop="getStockChange(goodsItem)">库存明细</text>
-                  <text @click.stop="editDesc(goodsItem)">编辑简介</text>
-                  <text @click.stop="moreAction(goodsItem)">更多</text>
-                </view>
-              </view>
-              <!-- 底部占位,避免最后一个商品操作按钮被遮挡 -->
-              <view class="bottom-spacer"></view>
-            </view>
-          </block>
-          <block v-else>
-            <app-wrapper-empty title="暂无商品" :is-empty="$util.isEmpty(list.data)" />
-          </block>
-        </scroll-view>
-      </block>
-    </view>
-
-    <!-- 筛选下拉面板 -->
-    <view v-if="showFilterPanel" class="filter-overlay" @click="closeFilterPanel">
-      <view class="filter-panel" @click.stop>
-        <view class="filter-header">筛选商品</view>
-        
-        <!-- 价格筛选已移至顶部标签页 -->
-
-        <!-- 库存筛选 -->
-        <view class="filter-section">
-          <view class="filter-title">库存</view>
-          <view class="filter-options">
-            <view 
-              v-for="(item, index) in stockOptions" 
-              :key="index"
-              class="filter-option"
-              :class="{ 'active': hasStock === item.value }"
-              @click="selectStock(item.value)"
-            >
-              {{ item.label }}
-            </view>
-          </view>
-        </view>
-
-        <!-- 支数筛选 -->
-        <view class="filter-section">
-          <view class="filter-title">支数</view>
-          <view class="filter-options">
-            <view 
-              v-for="(item, index) in flowerNumOptions" 
-              :key="index"
-              class="filter-option"
-              :class="{ 'active': flowerNum === item.value }"
-              @click="selectFlowerNum(item.value)"
-            >
-              {{ item.label }}
-            </view>
-          </view>
-        </view>
-
-        <!-- 操作按钮 -->
-        <view class="filter-actions">
-          <button class="filter-action-btn clear-btn middle" @click="clearFilters">清空</button>
-          <button class="filter-action-btn confirm-btn  middle blue" @click="confirmFilters">确定</button>
-        </view>
-      </view>
-    </view>
-
-    <!-- 打标签弹框 -->
-    <view v-if="showPrintModal" class="print-modal-overlay" @click="closePrintModal">
-      <view class="print-modal-content" @click.stop>
-        <view class="print-modal-header">
-          <text class="print-modal-title">打印标签</text>
-          <text class="print-modal-close" @click="closePrintModal">×</text>
-        </view>
-        
-        <view class="print-modal-body">
-          <view class="print-modal-item">
-            <text class="print-modal-label">商品名称:</text>
-            <text class="print-modal-value">{{ currentPrintItem.name }}</text>
-          </view>
-          
-          <view class="print-modal-item">
-            <text class="print-modal-label">打印数量:</text>
-            <input 
-              v-model.number="printQuantity" 
-              type="number" 
-              class="print-modal-input" 
-              placeholder="请输入打印数量"
-              min="1"
-              @focus="clearInput"
-            />
-          </view>
-        </view>
-        
-        <view class="print-modal-footer">
-          <button class="print-modal-btn cancel-btn" @click="closePrintModal">取消</button>
-          <button class="print-modal-btn confirm-btn" @click="confirmPrint">打印</button>
-        </view>
-      </view>
-    </view>
-
-    <uni-popup ref="rightShowMore" background-color="#fff" type="center" :animation="true" class="class-popup-style">
-      <view class="action-popup-container">
-        <view class="action-button-item">
-          <button class="action-button" @click="shareGoods()">分享到微信</button>
-        </view>
-        <view class="action-button-item">
-          <button class="action-button" @click="getMyPoster()">分享海报</button>
-        </view>
-        <view class="action-button-item cancel">
-          <button class="action-button cancel" @click="closeRightShow()">取消</button>
-        </view>
-      </view>
-    </uni-popup>
-
-    <item-poster-popup ref="itemPosterPopup" :poster-url="posterUrl" @close="closePoster" @save="downloadImg" />
-
-  </view>
-</template>
-<script>
-import AppSearchModule from "@/components/module/app-search"
-import AppWrapperEmpty from "@/components/app-wrapper-empty"
-import {categoryListInfo,getCategoryToGoods,print,getRise,getPoster} from "@/api/goods"
-import { getWeixinId } from "@/api/invite"
-import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue'
-import ItemPosterPopup from '@/components/item-poster-popup.vue'
-import { list } from "@/mixins"
-import { mapGetters } from "vuex"
-export default {
-  name: "list",
-  components: {
-    AppWrapperEmpty,
-    AppSearchModule,
-    uniPopup,
-    ItemPosterPopup
-  },
-  mixins: [list],
-  data() {
-    return {
-      tabbar: [],
-      currentTab: 0,
-      scrollTop: 0,
-      catId: "",
-      catName:"",
-      flowerNum:0,
-      searchText:'',
-      searchType:0,
-      lastSearchText:'', // 上次的搜索内容
-      searchTimer: null, // 搜索防抖定时器
-      riseData: {}, // 价格上涨配置
-      hasStock:-1, // 0没库存 1有库存
-      priceType:-1,// 0 没价,1 有价 
-      // 新增筛选相关数据
-      showFilterPanel: false,
-      // 打标签弹框相关数据
-      showPrintModal: false,
-      currentPrintItem: {},
-      printQuantity: '',
-      // 价格筛选已移至顶部标签页
-      stockOptions: [
-        { label: '有库存', value: 1 },
-        { label: '没库存', value: 0 }
-      ],
-      currentInfo: {},
-      posterUrl: '',
-      getAppIdList: {},
-      flowerNumOptions: [
-        { label: '6支', value: 6 },
-        { label: '9支', value: 9 },
-        { label: '11支', value: 11 },
-        { label: '19支', value: 19 },
-        { label: '33支', value: 33 },
-        { label: '52支', value: 52 },
-        { label: '66支', value: 66 },
-        { label: '99支', value: 99 }
-      ]
-    };
-  },
-
-  computed: {
-    ...mapGetters({ loginInfo: "getLoginInfo", dictInfo: "getDictionariesInfo" }),
-    // 检查是否有活跃的筛选条件
-    hasActiveFilters() {
-      return this.flowerNum != 0 || this.hasStock!=-1
-    }
-  },
-
-  onLoad() {
-    getWeixinId().then(res => {
-      this.getAppIdList = res.data
-    })
-  },
-
-  onShow() {
-    // 检查是否需要刷新数据
-    const shouldRefresh = uni.getStorageSync('goods_list_refresh');
-    if (shouldRefresh) {
-      uni.removeStorageSync('goods_list_refresh');
-      this.resetList();
-      this.getGoodsList();
-    }
-  },
-
-  onUnload() {
-    // 清理搜索定时器
-    if (this.searchTimer) {
-      clearTimeout(this.searchTimer);
-      this.searchTimer = null;
-    }
-  },
-
-  methods: {
-    init(){
-      this.getCategoryData()
-    },
-    rank(){
-      if(Number(this.catId)>0){
-        this.$util.pageTo({url: "/admin/goods/goodsSort?id="+this.catId+"&name="+this.catName})
-      }
-    },
-    // 新的筛选相关方法
-    openFilterPanel() {
-      this.showFilterPanel = true;
-    },
-    
-    closeFilterPanel() {
-      this.showFilterPanel = false;
-    },
-    selectPrice(value) {
-      this.priceType = value
-    },
-    
-    // 切换价格筛选标签
-    switchPriceTab(value) {
-      if (this.priceType === value) return;
-      
-      this.priceType = value;
-      this.resetList();
-      this.getGoodsList();
-    },
-    
-    selectStock(value) {
-      this.hasStock = value
-    },
-    selectFlowerNum(value) {
-      this.flowerNum = value
-    },
-    clearFilters() {
-
-      // 清空后立即更新筛选变量
-      this.hasStock = -1;
-      this.flowerNum = 0;
-      
-      // 关闭筛选面板
-      this.closeFilterPanel();
-      
-      // 触发搜索
-      this.resetList();
-      this.getGoodsList();
-    },
-    confirmFilters() {
-      // 关闭筛选面板
-      this.closeFilterPanel();
-      // 重新加载数据
-      this.resetList();
-      this.getGoodsList();
-    },
-
-    // 打标签弹框相关方法
-    printLabel(item){
-      this.currentPrintItem = item;
-      this.printQuantity = '';
-      this.showPrintModal = true;
-    },
-
-    closePrintModal(){
-      this.showPrintModal = false;
-      this.currentPrintItem = {};
-      this.printQuantity = '';
-    },
-
-    confirmPrint(){
-      if (!this.printQuantity || this.printQuantity <= 0) {
-        this.$msg('请输入有效的打印数量');
-        return;
-      }
-      
-      let that = this;
-      print({id: this.currentPrintItem.id, num: this.printQuantity}).then(res => {
-        if(res.code == 1){
-          that.$msg(res.msg);
-          that.closePrintModal();
-        } else {
-          that.$msg(res.msg || '打印失败');
-        }
-      }).catch(err => {
-        that.$msg('打印请求失败');
-      });
-    },
-
-    clearInput(){
-      this.printQuantity = '';
-    },
-    getStockChange(item){
-      this.$util.pageTo({url: "/admin/goods/stockChange?id="+item.id+'&name='+item.name})
-    },
-    editDesc(item){
-      this.$util.pageTo({url: "/admin/goods/picTextAdd-Edit?gId="+item.id+'&name='+item.name})
-    },
-    moreAction(item) {
-      this.currentInfo = item
-      this.$refs.rightShowMore.open('top')
-    },
-    closeRightShow() {
-      this.$refs.rightShowMore.close()
-    },
-    shareGoods() {
-      const that = this
-      const id = this.currentInfo.id
-      const account = that.loginInfo.shopId
-      const categoryId = this.currentInfo.categoryId || this.catId || 0
-      this.closeRightShow()
-      // 商城花束详情需 account(门店)才能通过 /hd/info 建立会员 custom,与 category/mall 页跳转一致
-      let sharePath = 'pages/goods/detail?id=' + id + '&account=' + account + '&shopId=' + account
-      if (Number(categoryId) > 0) {
-        sharePath += '&categoryId=' + categoryId
-      }
-      // #ifdef MP-WEIXIN
-      uni.navigateToMiniProgram({
-        appId: that.getAppIdList.mall.miniAppId,
-        path: sharePath,
-        envVersion: process.env.NODE_ENV === 'development' ? 'develop' : 'release',
-        extraData: {
-          shopId: account,
-          id: 0,
-          name: ''
-        },
-        success() {}
-      })
-      // #endif
-      // #ifdef APP-PLUS
-      if (plus.runtime.isApplicationExist({ pname: 'com.tencent.mm', action: 'weixin://' })) {
-        plus.share.getServices(function(s) {
-          let sweixin
-          for (let i = 0; i < s.length; i++) {
-            if (s[i].id == 'weixin') {
-              sweixin = s[i]
-            }
-          }
-          const miniOriginalId = that.dictInfo.miniOriginalId && that.dictInfo.miniOriginalId.current && that.dictInfo.miniOriginalId.current.mall ? that.dictInfo.miniOriginalId.current.mall : ''
-          if (sweixin) {
-            sweixin.launchMiniProgram({
-              id: miniOriginalId,
-              path: sharePath,
-              type: 0
-            })
-          } else {
-            that.$msg('没有找到微信服务')
-          }
-        }, function() {
-          that.$msg('没有找到微信服务哦')
-        })
-      } else {
-        this.$msg('请安装微信,再打开小程序')
-      }
-      // #endif
-    },
-    closePoster() {
-      this.$refs.itemPosterPopup.close()
-    },
-    getMyPoster() {
-      this.closeRightShow()
-      const that = this
-      uni.showLoading({ mask: true })
-      getPoster({ id: this.currentInfo.id, env_version: this.$util.getMiniEnvVersion() }).then(res => {
-        uni.hideLoading()
-        if (res.code == 1) {
-          that.posterUrl = res.data.imgUrl
-          that.$refs.itemPosterPopup.open()
-        }
-      })
-    },
-    downloadImg() {
-      const that = this
-      uni.downloadFile({
-        url: this.posterUrl,
-        success: (res) => {
-          if (res.statusCode === 200) {
-            uni.saveImageToPhotosAlbum({
-              filePath: res.tempFilePath,
-              success() {
-                that.$refs.itemPosterPopup.close()
-                uni.showToast({ title: '保存成功' })
-              }
-            })
-          }
-        }
-      })
-    },
-    goBottom(){
-      if (!this.list.finished) {
-        this.getGoodsList().then((res) => {
-          uni.stopPullDownRefresh();
-        });
-      } else {
-        uni.stopPullDownRefresh();
-      }
-    },
-    addGoods(){
-        this.$util.pageTo({url:"/admin/goods/add"})
-    },
-    edit(id){
-        this.$util.pageTo({ url: "/admin/goods/detail", query: { id: id } })
-    },
-    getCategoryData() {
-      categoryListInfo().then(res => {
-        this.tabbar = res.data;
-        if(this.tabbar && this.tabbar[0] && this.tabbar[0].id){
-          this.catId = this.tabbar[0].id
-          this.catName = this.tabbar[0].categoryName
-          this.getGoodsList()
-        }
-      })
-    },
-    changeSearchType(i){
-      this.searchType=i
-      this.searchFn()
-    },
-    searchFn(){
-      // 清除之前的定时器
-      if (this.searchTimer) {
-        clearTimeout(this.searchTimer);
-        this.searchTimer = null;
-      }
-
-      // 1. 首次点击搜索框:searchText 为空,lastSearchText 也为空 → 跳过搜索
-      if (this.$util.isEmpty(this.searchText) && this.$util.isEmpty(this.lastSearchText)) {
-        return;
-      }
-
-      // 2. 用户输入后清空:searchText 为空,但 lastSearchText 有值 → 恢复分类筛选
-      if (this.$util.isEmpty(this.searchText) && !this.$util.isEmpty(this.lastSearchText)) {
-        this.lastSearchText = '';
-        this.resetList();
-        this.getGoodsList();
-        return;
-      }
-
-      // 4. 重复相同搜索:searchText 与 lastSearchText 相同 → 跳过搜索
-      if (this.searchText === this.lastSearchText) {
-        return;
-      }
-
-      // 3. 正常输入搜索:searchText 有值且与 lastSearchText 不同 → 执行搜索(1秒后触发)
-      if (!this.$util.isEmpty(this.searchText)) {
-        this.searchTimer = setTimeout(() => {
-          this.lastSearchText = this.searchText;
-          this.catId = 0
-          this.catName = ""
-          this.currentTab = 0
-          this.resetList()
-          this.getGoodsList()
-        }, 1000);
-      }
-    },
-    getGoodsList() {
-      return getCategoryToGoods({page: this.list.page,searchText:this.lastSearchText,searchType:this.searchType,catId:this.catId, flowerNum:this.flowerNum, priceType:this.priceType, hasStock:this.hasStock,requestType:'goodsList'}).then(res => {
-        this.completes(res)
-      })
-    },
-    swichTab(e, item) {
-      let cur = e.currentTarget.dataset.current;
-      if (this.currentTab == cur) {
-        return false;
-      } else {
-        this.currentTab = cur;
-        this.catId = item.id;
-        this.catName = item.categoryName
-        this.resetList();
-        this.getGoodsList();
-      }
-    }
-  }
-};
-</script>
-<style lang="scss" scoped>
-.app-content {
-  min-height: calc(100vh - 80upx);
-  background-color: #fff;
-}
-
-/* 现代化标签样式 */
-.modern-tabs-container {
-  padding: 0;
-  background-color: #f8f9fa;
-  width: 100%;
-}
-
-.modern-tabs {
-  display: flex;
-  background-color: #ffffff;
-  padding: 6upx;
-  box-shadow: 0 2upx 12upx rgba(0, 0, 0, 0.06);
-  border: 2upx solid #f0f2f5;
-  position: relative;
-  z-index: 5;
-  width: 100%;
-}
-
-.modern-tab {
-  flex: 1;
-  text-align: center;
-  padding: 22upx 0;
-  position: relative;
-  transition: all 0.35s cubic-bezier(0.25, 1, 0.5, 1);
-  border-radius: 12upx;
-  margin: 0 2upx;
-  background-color: #e4e6e9; /* 未选中态背景 */
-  border: 2upx solid #e4e6e9; /* 未选中态边框 */
-  box-shadow: none;
-  
-  .tab-content {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-  }
-  
-  .tab-text {
-    font-size: 30upx;
-    color: #666;
-    font-weight: 700;
-  }
-  
-  &.active {
-    background: linear-gradient(135deg, #34d399 0%, #09C567 100%); /* 渐变背景 */
-    box-shadow: 0 4upx 12upx rgba(16, 185, 129, 0.25); /* 柔和外发光 */
-    transform: none;
-    border-color: rgba(16, 185, 129, 0.25);
-    
-    .tab-text {
-      color: #ffffff;
-      font-weight: 700;
-      font-size: 30upx;
-    }
-  }
-  
-  /* H5 未选中态悬停效果(其它端不影响) */
-  &:not(.active):hover {
-    background-color: #f6f7f9;
-    border-color: #e9ecef;
-  }
-  
-  &:active {
-    transform: scale(0.98);
-  }
-}
-page {
-  background: #fff;
-}
-::-webkit-scrollbar {
-  width: 0;
-  height: 0;
-  color: transparent;
-}
-.tab-view {
-	height: 100vh;
-  width: 200upx;
-  position: fixed;
-  left: 0;
-  z-index: 10;
-  background-color: #f0f2f6;
-  padding-bottom:120upx;
-  .tab-bar-item {
-    width: 200upx;
-    height: 100upx;
-    box-sizing: border-box;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    font-size: 26upx;
-    color: #444;
-    font-weight: 400;
-    padding-left:4upx;
-    text-align: center;
-  }
-  .active {
-    position: relative;
-    color: $mainColor;
-    font-size: 26upx;
-    background: #fff;
-  }
-  .active::before {
-    content: "";
-    position: absolute;
-    border-left: 8upx solid $mainColor;
-    height: 100%;
-    left: 0;
-  }
-}
-.right-box {
-  width: 100%;
-  height:100vh;
-  position: fixed;
-  padding-left:220upx;
-  padding-bottom:120upx;
-  box-sizing: border-box;
-  left: 0;
-  .page-view {
-    width: 100%;
-    overflow: hidden;
-    box-sizing: border-box;
-    padding-bottom: env(safe-area-inset-bottom);
-    .bottom-spacer {
-      height: 160upx; /* 预留底部空间,避免遮挡操作按钮 */
-    }
-    .goods-list {
-      margin: 40upx 0;
-      .goods-list-top {
-        position: relative;
-        @include disFlex(flex-start, flex-start);
-      }
-      .goods-list-bottom {
-        width: 96%;
-        margin-top: 16upx;
-        color: rgb(180, 178, 178);
-        flex-wrap: wrap;
-        gap: 12upx 0;
-        text{
-          border:1px solid rgb(196, 193, 193);
-          border-radius: 15upx;
-          font-size:22upx;
-          padding:12upx 18upx;
-        }
-      }
-      .img-blo {
-        width: 130upx;
-        height: 130upx;
-        margin-right: 20upx;
-        & > img {
-          height: 100%;
-          width: 100%;
-        }
-
-        .sold-out{
-          z-index:10;
-          width:130upx;
-          height:130upx;
-          background-color:black;
-          position:absolute;
-          top:0;
-          left:0;
-          opacity: 0.5;
-          color:white;
-          text-align:center;
-          .sold-out-xj{
-            display: flex;
-            flex-direction: column;
-            justify-content: center;
-            height: 130upx;
-            font-size:24upx;
-          }
-        }
-
-      }
-      .goods-det {
-        width: calc(100% - 150upx);
-        font-size: 28upx;
-        .operate-wrap {
-          @include disFlex(flex-end, space-between);
-        }
-        .operate-icon-wrap {
-          left: -20upx;
-        }
-        .goods-name {
-          margin-bottom: 10upx;
-        }
-        .goods-sales {
-          color: $fontColor3;
-          font-size: 24upx;
-        }
-        .goods-price {
-          color: #ff2842;
-          font-weight: bold;
-        }
-        
-        // 价格容器样式
-        .price-container {
-          display: flex;
-          align-items: center;
-          gap: 15upx;
-        }
-        
-        // 原价格划线样式
-        .original-price {
-          color: #999999;
-          margin-left: 10upx;
-        }
-      }
-    }
-  }
-}
-.app-footer {
-  justify-content: space-between;
-  .btn-list {
-    width: 100%;
-    &:first-child {
-      border-left: 0;
-    }
-    .admin-button-com {
-      width: 160upx;
-      padding-top: 16upx;
-      padding-bottom: 16upx;
-      float:right;
-      margin-right:30upx;
-    }
-  }
-  .other-btn {
-    color: $fontColor2;
-    .iconfont {
-      font-size: 24upx;
-      color: $fontColor3;
-      transform: scale(0.4);
-    }
-  }
-}
-.input-wrap {
-		display: flex;
-		padding: 10upx 10upx 12upx 13upx;
-		.search-bar {
-			flex: 1;
-		}
-}
-
-@keyframes filterDotBlink {
-  0%, 100% {
-    background-color: #ffffff;
-  }
-  50% {
-    background-color: #ff3b30;
-  }
-}
-
-/* 筛选面板样式 */
-.filter-overlay {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background-color: rgba(0, 0, 0, 0.5);
-  z-index: 9999;
-  display: flex;
-  align-items: flex-start;
-  justify-content: center;
-}
-
-.filter-panel {
-  background-color: #fff;
-  border-radius: 0 0 20upx 20upx; /* 只保留下边圆角 */
-  width: 100%; /* 铺满宽度 */
-  max-height: 80vh;
-  overflow-y: auto;
-  animation: slideDown 0.3s ease-out;
-}
-
-@keyframes slideDown {
-  from {
-    opacity: 0;
-    transform: translateY(-50upx);
-  }
-  to {
-    opacity: 1;
-    transform: translateY(0);
-  }
-}
-
-.filter-header {
-  text-align: center;
-  padding: 16upx 0;
-  font-size: 32upx;
-  font-weight: bold;
-  border-bottom: 1upx solid #f0f0f0;
-  color: #333;
-}
-
-.filter-section {
-  padding: 30upx 40upx;
-  border-bottom: 1upx solid #f0f0f0;
-  
-  &:last-child {
-    border-bottom: none;
-  }
-}
-
-.filter-title {
-  font-size: 28upx;
-  font-weight: 600;
-  color: #333;
-  margin-bottom: 20upx;
-}
-
-.filter-options {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 20upx;
-}
-
-.filter-option {
-  padding: 16upx 32upx;
-  border: 2upx solid #e0e0e0;
-  border-radius: 25upx;
-  font-size: 26upx;
-  color: #666;
-  background-color: #fff;
-  text-align: center;
-  min-width: 120upx;
-  transition: all 0.3s ease;
-  
-  &.active {
-    border-color: #52c41a;
-    background-color: #f6ffed;
-    color: #52c41a;
-    font-weight: 600;
-  }
-}
-
-.filter-actions {
-  display: flex;
-  padding: 30upx 40upx;
-  gap: 20upx;
-}
-
-.filter-action-btn {
-  flex: 1;
-  padding: 14upx 0;
-  border-radius: 12upx;
-  font-size: 28upx;
-  border: none;
-  font-weight: 600;
-  
-  &.clear-btn {
-    background-color: #f5f5f5;
-    color: #666;
-  }
-  
-  &.confirm-btn {
-    background-color: #09C567;
-    color: #fff;
-  }
-}
-
-/* 打标签弹框样式 */
-.print-modal-overlay {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background-color: rgba(0, 0, 0, 0.5);
-  z-index: 10000;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.print-modal-content {
-  background-color: #fff;
-  border-radius: 20upx;
-  width: 80%;
-  max-width: 600upx;
-  animation: scaleIn 0.3s ease-out;
-  box-shadow: 0 8upx 24upx rgba(0, 0, 0, 0.15);
-}
-
-@keyframes scaleIn {
-  from {
-    opacity: 0;
-    transform: scale(0.8);
-  }
-  to {
-    opacity: 1;
-    transform: scale(1);
-  }
-}
-
-.print-modal-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 24upx 24upx;
-  border-bottom: 1upx solid #f0f0f0;
-}
-
-.print-modal-title {
-  font-size: 32upx;
-  font-weight: 600;
-  color: #333;
-}
-
-.print-modal-close {
-  font-size: 40upx;
-  color: #999;
-  font-weight: 300;
-  line-height: 1;
-  cursor: pointer;
-  transition: color 0.3s ease;
-  
-  &:active {
-    color: #333;
-  }
-}
-
-.print-modal-body {
-  padding: 30upx 24upx;
-}
-
-.print-modal-item {
-  display: flex;
-  align-items: center;
-  margin-bottom: 24upx;
-  
-  &:last-child {
-    margin-bottom: 0;
-  }
-}
-
-.print-modal-label {
-  font-size: 28upx;
-  color: #666;
-  font-weight: 500;
-  min-width: 140upx;
-}
-
-.print-modal-value {
-  font-size: 28upx;
-  color: #333;
-  flex: 1;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-
-.print-modal-input {
-  flex: 1;
-  padding: 0;
-  height: 80upx;
-  border: 2upx solid #e0e0e0;
-  border-radius: 8upx;
-  font-size: 36upx;
-  color: #333;
-  background-color: #fafafa;
-  transition: border-color 0.3s ease;
-  text-align: center;
-  line-height: 80upx;
-  
-  &::placeholder {
-    text-align: center;
-    color: #ccc;
-  }
-  
-  &:focus {
-    border-color: #09C567;
-    background-color: #fff;
-  }
-}
-
-.print-modal-footer {
-  display: flex;
-  gap: 16upx;
-  padding: 24upx 24upx;
-  border-top: 1upx solid #f0f0f0;
-}
-
-.print-modal-btn {
-  flex: 1;
-  padding: 16upx 0;
-  border-radius: 12upx;
-  font-size: 28upx;
-  border: none;
-  font-weight: 600;
-  transition: all 0.3s ease;
-  
-  &.cancel-btn {
-    background-color: #f5f5f5;
-    color: #666;
-    
-    &:active {
-      background-color: #e8e8e8;
-    }
-  }
-  
-  &.confirm-btn {
-    background-color: #09C567;
-    color: #fff;
-    
-    &:active {
-      background-color: #07A850;
-    }
-  }
-}
-
-.class-popup-style {
-  z-index: 999999;
-}
-
-.action-popup-container {
-  display: grid;
-  grid-template-columns: 1fr 1fr;
-  gap: 24upx;
-  padding: 50upx 40upx 60upx 40upx;
-  max-height: 99vh;
-  overflow-y: auto;
-  width: 92vw;
-  max-width: 650upx;
-  margin: 0 auto;
-
-  .action-button-item.cancel {
-    grid-column: 1 / -1;
-    margin-top: 30upx;
-  }
-}
-
-.action-button {
-  width: 100%;
-  height: 120upx;
-  border: 2upx solid #d0d7de;
-  border-radius: 16upx;
-  background: #ffffff;
-  color: #24292f;
-  font-size: 34upx;
-  font-weight: 600;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-
-  &.cancel {
-    background: #f6f8fa;
-    color: #656d76;
-    font-size: 32upx;
-  }
-}
-
-</style>

+ 3 - 3
hdApp/src/admin/goods/manage.vue

@@ -247,7 +247,7 @@
       </view>
     </view>
 
-    <button class="admin-button-com big blue footer-btn" @click="addCurrent">{{ activeType === 'goods' ? '新增商品' : '新增花材' }}</button>
+    <button class="admin-button-com big blue footer-btn" @click="addCurrent">{{ activeType === 'goods' ? '新增花束' : '新增花材' }}</button>
 
     <view v-if="showFilterPanel" class="filter-overlay" @click="closeFilterPanel">
       <view class="filter-panel" @click.stop>
@@ -1247,8 +1247,8 @@ page {
 .top-tabs {
   display: flex;
   align-items: flex-end;
-  height: 118upx;
-  padding: 10upx 120upx 0;
+  height: 100upx;
+  padding: 0 120upx 0;
   box-sizing: border-box;
   flex-shrink: 0;
 }

+ 5 - 7
hdApp/src/admin/home/homeMenus.js

@@ -9,12 +9,11 @@ export const APPLY_MENU_GROUPS = [
     titleIcon: homeIcon('bouquet'),
     list: [
       { name: '商城码', icon: homeIcon('mall-filled'), url: '/admin/cg/mall', pf: 1 },
-      { name: '花束', icon: homeIcon('bouquet'), url: '/admin/goods/list', pf: 1 },
       { name: '商品管理', icon: homeIcon('bouquet'), url: '/admin/goods/manage', pf: 1 },
       { name: '新增花束', icon: homeIcon('add-bouquet'), url: '/admin/goods/add', pf: 1 },
-      { name: '分类', icon: homeIcon('category-filled'), url: '/admin/goods/categoryV2?tab=0', pf: 1 },
+      { name: '花束分类', icon: homeIcon('category-filled'), url: '/admin/goods/categoryV2?tab=0', pf: 1 },
       { name: '使用场景', icon: homeIcon('category-filled'), url: '/admin/useCase/list', pf: 1 },
-      { name: '排序', icon: homeIcon('sort-filled'), url: '/admin/goods/categorySort', pf: 1 },
+      { name: '花束排序', icon: homeIcon('sort-filled'), url: '/admin/goods/categorySort', pf: 1 },
       { name: '任务', icon: homeIcon('task-filled'), url: '/admin/work/list', pf: 1 },
       { name: '涨价设置', icon: homeIcon('price-increase'), url: '/admin/goods/price-increase', pf: 1 }
     ]
@@ -112,7 +111,6 @@ export const HOME_MENU_ITEMS = {
   mall: { name: '商城码', icon: homeIcon('mall-filled'), url: '/admin/cg/mall', pf: 1 },
   itemMall: { name: '商城码', icon: homeIcon('mall-filled'), url: '/admin/cg/item', pf: 1 },
   task: { name: '任务', icon: homeIcon('task-filled'), url: '/admin/work/list', pf: 1 },
-  flower: { name: '花束', icon: homeIcon('bouquet'), url: '/admin/goods/list', pf: 1 },
   goodsManage: { name: '商品管理', icon: homeIcon('bouquet'), url: '/admin/goods/manage', pf: 1 },
   addFlower: { name: '新增花束', icon: homeIcon('add-bouquet'), url: '/admin/goods/add', pf: 1 },
   order: { name: '订单', icon: homeIcon('order-filled'), url: '/admin/home/order', type: 4, pf: 1 },
@@ -143,8 +141,8 @@ export const HOME_MENU_ITEMS = {
  * quickMenus: 快捷菜单, flowerMenus: 花束菜单, plantMenus: 绿植花材菜单, moreMenus: 更多菜单
  */
 export const HOME_MENU_DEFAULT_KEYS = {
-  quickMenus: ['itemBill', 'itemQuickBill', 'flower', 'buyItem'],
-  flowerMenus: ['mall', 'task', 'flowerCategory', 'flowerSort', 'flower', 'goodsManage', 'addFlower', 'priceIncrease', 'shopNotice'],
+  quickMenus: ['itemBill', 'itemQuickBill', 'buyItem'],
+  flowerMenus: ['mall', 'task', 'flowerCategory', 'flowerSort', 'goodsManage', 'addFlower', 'priceIncrease', 'shopNotice'],
   plantMenus: ['itemMall', 'plant', 'part', 'changePrice', 'breakage', 'plantCategory', 'buyItem', 'purchaseRecord'],
   moreMenus: ['calculator', 'staff', 'profit', 'picText', 'scanPay', 'payCode', 'redPacket', 'rechargeSetting']
 }
@@ -158,7 +156,7 @@ export const HOME_MENU_STORAGE_KEYS = {
 export const HOME_OVERVIEW_FALLBACK = [
   { name: '花材数', value: 0, url: '/admin/item/list2' },
   { name: '花材值(元)', value: 0, url: '/admin/stat/kind' },
-  { name: '花束', value: 0, url: '/admin/goods/list' },
+  { name: '花束', value: 0, url: '/admin/goods/manage' },
   { name: '客户总数', value: 0, url: '/admin/home/member', type: 4 },
   { name: '总订单', value: 0, url: '/admin/home/order', type: 4 },
   { name: '总挂账(元)', value: 0, url: '/admin/custom/showTotalBalance' }

+ 37 - 1
hdApp/src/admin/homePageConfig/index.vue

@@ -30,8 +30,19 @@
           <view class="list-title">{{ item.name }}</view>
           <view class="list-desc">{{ item.desc }}</view>
         </view>
-        <view class="list-switch" @click.stop @touchstart.stop>
+        <view class="list-edit" @click.stop="goEdit(item)" data-drag-ignore="true">
+          编辑
+        </view>
+        <!--
+          注意:这里不能用 @touchstart.stop 之类的 catch 写法拦截触摸事件。
+          微信小程序里 catch 会在原生手势层面“吃掉”触摸,导致内置 <switch>
+          自身的滑动/点击手势也收不到事件,表现为开关完全点不动。
+          所以改为:不拦截事件冒泡,而是给开关区域打上 data-drag-ignore 标记,
+          在 dragStart 里识别到该标记后直接放弃进入拖拽,把触摸交还给 switch 自己处理。
+        -->
+        <view class="list-switch" data-drag-ignore="true">
           <switch
+            data-drag-ignore="true"
             :checked="item.enabled == 1"
             color="#09C567"
             @change="toggleEnabled(item, $event)"
@@ -220,8 +231,25 @@ export default {
       }
       return 'translateY(0upx)'
     },
+    /**
+     * 判断触摸目标是否位于标记了 data-drag-ignore 的区域(如开关按钮)。
+     * 小程序端 e.target 是精简对象,只能直接读 dataset;
+     * H5/App 端是真实 DOM 节点,兼容用 closest 向上查找,防止触摸到 switch 内部子节点时漏判。
+     */
+    isDragIgnoreTarget(target) {
+      if (!target) return false
+      if (target.dataset && (target.dataset.dragIgnore === 'true' || target.dataset.dragIgnore === true)) {
+        return true
+      }
+      if (typeof target.closest === 'function') {
+        return !!target.closest('[data-drag-ignore="true"]')
+      }
+      return false
+    },
     dragStart(e) {
       if (this.isDragging) return
+      // 触摸起点落在开关区域时不进入拖拽,避免抢占 switch 自身的点击手势
+      if (this.isDragIgnoreTarget(e.target)) return
       const index = parseInt(e.currentTarget.dataset.index, 10)
       if (isNaN(index) || index < 0 || index >= this.list.length) {
         return
@@ -413,6 +441,14 @@ export default {
   white-space: nowrap;
 }
 
+.list-edit {
+  flex-shrink: 0;
+  color: #09C567;
+  font-size: 28upx;
+  padding: 10upx 16upx;
+  margin-right: 4upx;
+}
+
 .list-switch {
   flex-shrink: 0;
   @include disFlex(center, center);

+ 1 - 2
hdApp/src/pages.json

@@ -338,7 +338,6 @@
 		{
 			"root": "admin/goods",
 			"pages": [
-				{ "path": "list", "style": { "navigationBarTitleText": "花束" } },
 				{ "path": "manage", "style": { "navigationBarTitleText": "商品管理" } },
 				{ "path": "lz", "style": { "navigationBarTitleText": "绿植" } },
 				{ "path": "categoryV2", "style": { "navigationBarTitleText": "分类管理", "enablePullDownRefresh": true } },
@@ -349,7 +348,7 @@
 				{ "path": "kind", "style": { "navigationBarTitleText": "花束品类" } },
 				{ "path": "lzKind", "style": { "navigationBarTitleText": "绿植品类" } },
 				{ "path": "detail", "style": { "navigationBarTitleText": "商品详情" } },
-				{ "path": "add", "style": { "navigationBarTitleText": "新增商品" } },
+				{ "path": "add", "style": { "navigationBarTitleText": "新增花束" } },
 				{ "path": "selectItem", "style": { "navigationBarTitleText": "选择花材" } },
 				{ "path": "img-store", "style": { "navigationBarTitleText": "图库管理" } },
 				{ "path": "goods-name", "style": { "navigationBarTitleText": "填写商品信息" } },

+ 33 - 6
mallApp/src/components/CountDown.vue

@@ -2,11 +2,17 @@
   通用倒计时组件
   用途:团购详情页活动倒计时、拼团剩余时间等
   入参 endTime 为秒级 unix;结束后触发 ended 事件
+  剩余 ≥24h 时拆出「天」单位,小时限制在 0–23(与首页活动专区一致)
 -->
 <template>
   <view v-if="visible" class="count-down" :class="theme">
     <text v-if="label" class="cd-label">{{ label }}</text>
     <view class="cd-blocks">
+      <!-- 剩余超过 24 小时时展示「天」单位,避免小时数无限增大 -->
+      <template v-if="display.showDay">
+        <text class="cd-unit">{{ display.d }}</text>
+        <text class="cd-day">天</text>
+      </template>
       <text class="cd-unit">{{ display.h }}</text>
       <text class="cd-colon">:</text>
       <text class="cd-unit">{{ display.m }}</text>
@@ -30,7 +36,8 @@ export default {
   data() {
     return {
       visible: false,
-      display: { h: '00', m: '00', s: '00' },
+      // showDay:剩余 ≥24h 时展示天;d/h/m/s 为补零后的展示字符串
+      display: { showDay: false, d: '00', h: '00', m: '00', s: '00' },
       _timer: null
     }
   },
@@ -57,24 +64,34 @@ export default {
       this.tick()
       this._timer = setInterval(this.tick, 1000)
     },
+    /**
+     * 按 endTime 刷新展示;剩余 ≥24h 拆出天单位,小时为当天剩余 0–23
+     */
     tick() {
       const end = Number(this.endTime) || 0
       const now = Math.floor(Date.now() / 1000)
       if (end <= 0 || now >= end) {
         this.visible = false
-        this.display = { h: '00', m: '00', s: '00' }
+        this.display = { showDay: false, d: '00', h: '00', m: '00', s: '00' }
         this.clearTimer()
         this.$emit('ended')
         return
       }
       this.visible = true
       let diff = end - now
-      const h = Math.floor(diff / 3600)
-      diff -= h * 3600
-      const m = Math.floor(diff / 60)
+      if (diff < 0) diff = 0
+      const d = Math.floor(diff / 86400)
+      const h = Math.floor((diff % 86400) / 3600)
+      const m = Math.floor((diff % 3600) / 60)
       const s = diff % 60
       const pad = (n) => String(n).padStart(2, '0')
-      this.display = { h: pad(h), m: pad(m), s: pad(s) }
+      this.display = {
+        showDay: d > 0,
+        d: pad(d),
+        h: pad(h),
+        m: pad(m),
+        s: pad(s)
+      }
     }
   }
 }
@@ -113,9 +130,19 @@ export default {
   font-size: 22upx;
   font-weight: 600;
 }
+.cd-day {
+  margin: 0 6upx 0 4upx;
+  font-size: 22upx;
+  color: #ff4d6d;
+  font-weight: 600;
+}
 .pink {
   .cd-unit {
     background: #ff6b8a;
   }
+  .cd-day,
+  .cd-colon {
+    color: #ff6b8a;
+  }
 }
 </style>

+ 1 - 1
mallApp/src/pages/billing/affirmMix.vue

@@ -77,7 +77,7 @@
 				<view class="affirm-card">
 					<view class="section-head">
 						<view class="section-head-bar"></view>
-						<text class="section-head-title">选择购买方式</text>
+						<text class="section-head-title">选择配送方式</text>
 						
 					</view>
 					<view class="send-type-list" v-if="sendTypeOptions.length">

+ 4 - 2
mallApp/src/pages/chat/chatPage.vue

@@ -225,7 +225,7 @@ export default {
   onLoad(options) {
     const eventChannel = this.getOpenerEventChannel();
     eventChannel.on('acceptDataFromOpenerPage', (data) => {
-      // console.log("---------- pageParams: ", data);
+      // console.log("---------- acceptDataFromOpenerPage pageParams: ", data);
       this.initializePage(data);
     })
     
@@ -1067,9 +1067,11 @@ export default {
       const goodsMessage = {
         ...this.currentGoodsInfo,
         key: keyValue,
+        name: this.currentGoodsInfo.name || this.currentGoodsInfo.goodsName || '', // 商品名称兼容来源
       };
 
       const messageContent = JSON.stringify(goodsMessage); // 所看到的消息体
+      // console.log("---------- sendMessageOfGoodsInfo messageContent: ", messageContent);
 
       try {
         // 立即在本地显示发送的消息(右侧)
@@ -1236,7 +1238,7 @@ export default {
         hdId: goodsInfo.hdId,
         categoryId: categoryId,
         shopId: goodsInfo.shopId,
-        name: goodsInfo.name,
+        name: goodsInfo.name || goodsInfo.goodsName || '', // 商品名称兼容来源
         avatar: goodsInfo.avatar,
       };
 

+ 59 - 5
mallApp/src/pages/goods/components/buy-foot.vue

@@ -1,9 +1,20 @@
+<!--
+  商品详情底部操作栏
+  用途:左侧购物车/咨询入口 + 右侧加购/购买(或询价)
+  使用方:pages/goods/detail
+-->
 <template>
 	<view class="app-footer">
         <view class="btn-left">
-            <view class="btn-wrap" @click="goShop">
-                <i class="iconfont icondianpu"></i>
-                <view class="btn-text">店铺</view>
+            <!-- 购物车入口:展示本地购物车商品数量角标,点击跳转店铺购物车页 -->
+            <view class="btn-wrap" @click="goCart">
+                <view class="cart-icon-wrap">
+                    <zui-svg-icon icon="general-cartFull" :width="24" :height="24" color="#333" />
+                    <view class="cart-badge" v-if="cartCount > 0">
+                        <text class="cart-badge-text">{{ cartCountText }}</text>
+                    </view>
+                </view>
+                <view class="btn-text">购物车</view>
             </view>
             <view class="btn-wrap" @click="goChat">
                 <i class="iconfont iconxiaoxi"></i>
@@ -31,6 +42,17 @@ export default {
         priceType: {
             type: Number,
             default: 1
+        },
+        /** 购物车商品数量角标,由详情页根据 Vuex/本地缓存汇总传入 */
+        cartCount: {
+            type: Number,
+            default: 0
+        }
+    },
+    computed: {
+        /** 角标文案:超过 99 显示 99+,与店铺底部 tab 一致 */
+        cartCountText() {
+            return this.cartCount > 99 ? '99+' : String(this.cartCount)
         }
     },
 	methods: {
@@ -40,8 +62,9 @@ export default {
         cart() {
             this.$emit('cart')
         },
-        goShop(){
-            this.$emit('goShop')
+        /** 跳转购物车页,由父组件拼接 account/hdId 后导航 */
+        goCart(){
+            this.$emit('goCart')
         },
         goChat(){
             this.$emit('goChat')
@@ -78,6 +101,37 @@ export default {
             padding: 8upx 0;
             justify-content: space-evenly;
         }
+
+        .cart-icon-wrap {
+            position: relative;
+            width: 48upx;
+            height: 48upx;
+            margin-bottom: 8upx;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+        }
+
+        .cart-badge {
+            position: absolute;
+            top: -8upx;
+            right: -16upx;
+            min-width: 28upx;
+            height: 28upx;
+            padding: 0 6upx;
+            border-radius: 14upx;
+            background: #ff4757;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            box-sizing: border-box;
+        }
+
+        .cart-badge-text {
+            font-size: 18upx;
+            color: #fff;
+            line-height: 1;
+        }
         
         .iconfont {
             font-size: 48upx;

+ 54 - 14
mallApp/src/pages/goods/detail.vue

@@ -99,9 +99,10 @@
     <buy-foot
       @buy="showPopup('buy')"
       @cart="showPopup('cart')"
-      @goShop="goToShop"
+      @goCart="goToCart"
       :stock="data.stock"
       :priceType="data.priceType"
+      :cart-count="cartBadgeCount"
       @goChat="goChat"
     />
   </view>
@@ -170,6 +171,10 @@ export default {
   beforeDestroy() {
     this.clearSeckillTimer();
   },
+  /** 返回本页时从本地缓存恢复购物车,保证底部角标与加购合并正确 */
+  onShow() {
+    this.restoreCartFromStorage();
+  },
   onShareAppMessage() {
     const path = this.buildGoodsSharePath()
     const sharePrice = this.data && this.data.priceType == 1
@@ -204,6 +209,14 @@ export default {
       const endTime = Number(this.activityEndTime) || 0;
       return endTime > 0 && endTime > Math.floor(Date.now() / 1000);
     },
+    /** 底部购物车角标:扎数 + 支数(与分类页/首页 tab 统计口径一致) */
+    cartBadgeCount() {
+      if (!this.selectList || !this.selectList.length) {
+        return 0;
+      }
+      const count = this.allCount || {};
+      return (Number(count.bigLength) || 0) + (Number(count.smallLength) || 0);
+    },
   },
   methods: {
     /** 解析首页秒杀专区/更多列表带过来的活动参数 */
@@ -227,6 +240,9 @@ export default {
       this.tickSeckillCountdown();
       this._seckillTimer = setInterval(this.tickSeckillCountdown, 1000);
     },
+    /**
+     * 刷新秒杀倒计时文案;剩余 ≥24h 时拆出「天」,小时限制在 0–23(与首页活动专区一致)
+     */
     tickSeckillCountdown() {
       const end = Number(this.activityEndTime) || 0;
       const now = Math.floor(Date.now() / 1000);
@@ -236,12 +252,15 @@ export default {
         return;
       }
       let diff = end - now;
-      const h = Math.floor(diff / 3600);
-      diff -= h * 3600;
-      const m = Math.floor(diff / 60);
+      if (diff < 0) diff = 0;
+      const d = Math.floor(diff / 86400);
+      const h = Math.floor((diff % 86400) / 3600);
+      const m = Math.floor((diff % 3600) / 60);
       const s = diff % 60;
       const pad = (n) => String(n).padStart(2, '0');
-      this.seckillCountdownText = `${pad(h)}:${pad(m)}:${pad(s)}`;
+      const hms = `${pad(h)}:${pad(m)}:${pad(s)}`;
+      // ≥1 天时前置「X天」,避免出现 48:00:00 这类超大小时数
+      this.seckillCountdownText = d > 0 ? `${d}天${hms}` : hms;
     },
     /** 用秒杀价覆盖详情展示价格,原价保留用于划线展示;仅覆盖展示层,实际下单价格由后端独立核价 */
     applySeckillPriceIfNeeded() {
@@ -260,13 +279,16 @@ export default {
       if (!this.option || !this.option.id) {
         return;
       }
+
       this.initializeOption(this.option);
       this.initActivityOption(this.option);
       this.hdId = this.option.hdId || 0;
       // 外链/跨小程序进入时可能只带 shopId(与花材详情分享一致),需映射为 account
       this.account = this.option.account || this.option.shopId || 0;
+      // account 就绪后恢复购物车,保证底部角标与后续加购合并正确
+      this.restoreCartFromStorage();
       const id = Number(this.option.id) || 0;
-      const account = Number(this.option.account) || 0;
+      const account = Number(this.account) || 0;
       if (id > 0 && account > 0) {
         uni.setStorageSync('account', account);
         getHdInfo().then((res) => {
@@ -284,10 +306,11 @@ export default {
     /** 拉取当前用户 custom.id,仅用于分享追加 inviterCustomId */
     syncMyCustomId() {
       const hdId = Number(this.hdId) || Number(this.option.hdId) || Number(uni.getStorageSync('hdId')) || 0
-      if (!hdId) {
+      const account = Number(this.account) || Number(this.option.account) || Number(uni.getStorageSync('account')) || 0
+      if (!hdId || !account) {
         return Promise.resolve()
       }
-      return getInfo({ hdId }).then((res) => {
+      return getInfo({ hdId, account }).then((res) => {
         if (res.code === 1 && res.data.custom && res.data.custom.id) {
           this.myCustomId = Number(res.data.custom.id)
         }
@@ -402,6 +425,7 @@ export default {
       }
     },
     loadGoodsDetail() {
+      // console.log("---------- loadGoodsDetail: ", this.option);
       const id = parseInt(this.option.id);
       if (isNaN(id) || id <= 0) {
         this.$msg("商品id数据异常");
@@ -473,12 +497,28 @@ export default {
     changeSpec(spec) {
       this.data = {...this.data, price: spec.price, priceType: spec.priceType, stock: spec.stock, selectedSpecGoodsId: spec.id, selectedSpecName: spec.specName};
     },
-    goToShop() {
-      let account = uni.getStorageSync("account");
-      let hdId = this.option.hdId ? this.option.hdId : 0;
+    /**
+     * 从本地缓存恢复当前店铺购物车到 Vuex
+     * 缓存 key 与分类/购物车页一致:selectListcg_hd_{account}
+     * 解决详情页 autoLoad=false 时角标为 0、加购覆盖已有购物车的问题
+     */
+    restoreCartFromStorage() {
+      const account = this.account || (this.option && this.option.account) || uni.getStorageSync('account') || '';
+      if (!account) {
+        return;
+      }
+      const cached = uni.getStorageSync('selectListcg_hd_' + account);
+      if (Array.isArray(cached) && cached.length) {
+        this.setSelectInfoByType({ type: this.pageType || 'cg', info: cached });
+      }
+    },
+    /** 跳转店铺购物车页 */
+    goToCart() {
+      const account = this.account || (this.option && this.option.account) || uni.getStorageSync('account') || '';
+      const hdId = this.hdId || (this.option && this.option.hdId) || uni.getStorageSync('hdId') || 0;
       this.$util.pageTo({
-        url: "/pages/home/shop?account=" + account + "&hdId=" + hdId,
-        type: 4,
+        url: '/pages/home/cart?account=' + account + '&hdId=' + hdId,
+        type: 2,
       });
     },
     showPopup(mode = 'buy') {
@@ -573,7 +613,7 @@ export default {
         goodsImg: this.data.smallCover, // smallCover, cover, bigCover。 取smallCover
         account: this.shopInfo.account,
         hdId: this.shopInfo.hdId,
-        categoryId: this.shopInfo.categoryId,
+        categoryId: this.data.categoryIdList[0] || 0,
       };
       // console.log("-----goChat----- params: ", params);
       uni.navigateTo({

+ 170 - 9
mallApp/src/pages/goods/list.vue

@@ -2,6 +2,8 @@
   商品列表页
   从店铺首页金刚区进入;支持搜索、分页触底加载、筛选弹窗(配送占位/价格/分类/场景)
   按钮配色与店铺首页统一:#FF4D6D / linear-gradient(135deg, #FF8FA3, #FF4D6D)
+  右侧购物车:单规格直接加购,多规格跳转详情选规格(与首页 goodsSection 一致)
+  左下角悬浮购物车:展示当前店铺购物车数量,点击跳转 pages/home/cart
 -->
 <template>
   <view class="goods-list-page">
@@ -47,12 +49,18 @@
             >{{ tag }}</text>
           </view>
           <view class="bottom-row">
-            <text class="price">
-              <text class="price-symbol">¥</text>
-              {{ formatPrice(item.price) }}
-              <text class="price-suffix">起</text>
-            </text>
-            <text class="sold">已售{{ formatSold(item.totalSold) }}件</text>
+            <view class="price-sold-col">
+              <text class="price">
+                <text class="price-symbol">¥</text>
+                {{ formatPrice(item.price) }}
+                <text class="price-suffix">起</text>
+              </text>
+              <text class="sold">已售{{ formatSold(item.totalSold) }}件</text>
+            </view>
+            <!-- 加购:圆形底 + 白色购物车 SVG,与首页商品区一致 -->
+            <view class="cart-icon" @click.stop="handleCartClick(item)">
+              <zui-svg-icon icon="general-cartFull" :width="16" :height="16" color="#fff" style="margin-top: 6upx" />
+            </view>
           </view>
         </view>
       </view>
@@ -62,6 +70,14 @@
       <app-wrapper-empty title="暂无商品" :is-empty="true" />
     </block>
 
+    <!-- 左下角悬浮购物车:白底圆形 + 角标数量,点击进店铺购物车页 -->
+    <view class="float-cart" @click="goToCart">
+      <zui-svg-icon icon="general-cartFull" :width="26" :height="26" color="#333" />
+      <view v-if="cartBadgeCount > 0" class="float-cart-badge">
+        <text class="float-cart-badge-text">{{ cartBadgeText }}</text>
+      </view>
+    </view>
+
     <!-- 筛选弹窗 -->
     <bottom-popup :show="filterShow" bgcolor="#f5f5f5" :height="0" @close="closeFilter">
       <view class="filter-panel">
@@ -157,6 +173,7 @@ import AppWrapperEmpty from '@/components/app-wrapper-empty'
 import BottomPopup from '@/components/plugin/bottom-popup'
 import { getClass, getList, getUseCaseList } from '@/api/category'
 import { list } from '@/mixins'
+import productMins from '@/mixins/cgProduct'
 
 const PRICE_PRESETS = [
   { key: 'lt50', label: '50元以下', min: '', max: '50' },
@@ -189,9 +206,12 @@ export default {
     AppWrapperEmpty,
     BottomPopup
   },
-  mixins: [list],
+  mixins: [list, productMins],
   data() {
     return {
+      // 购物车 pageType;关闭 mixin 自动拉花材列表,本页只复用加购能力
+      pageType: 'cg',
+      autoLoad: false,
       searchText: '',
       searchTimer: null,
       filterShow: false,
@@ -212,12 +232,55 @@ export default {
       draft: emptyDraft()
     }
   },
+  computed: {
+    /** 悬浮购物车角标:扎数 + 支数,与详情页/店铺 tab 统计口径一致 */
+    cartBadgeCount() {
+      if (!this.selectList || !this.selectList.length) {
+        return 0
+      }
+      const count = this.allCount || {}
+      return (Number(count.bigLength) || 0) + (Number(count.smallLength) || 0)
+    },
+    /** 角标文案:超过 99 显示 99+ */
+    cartBadgeText() {
+      return this.cartBadgeCount > 99 ? '99+' : String(this.cartBadgeCount)
+    }
+  },
+  /** 从详情/购物车返回时刷新角标,避免仍显示旧数量 */
+  onShow() {
+    this.restoreCartFromStorage()
+  },
   methods: {
     init() {
       this.applyRouteFilter()
+      // autoLoad=false 时需主动按店铺 account 恢复购物车,否则悬浮角标为 0
+      this.restoreCartFromStorage()
       this.loadFilterOptions()
       this.loadGoods()
     },
+    /**
+     * 从本地缓存恢复当前店铺购物车到 Vuex
+     * 缓存 key 与分类/详情/购物车页一致:selectListcg_hd_{account}
+     * 空数组也要写入,避免从购物车清空后返回仍显示旧角标
+     */
+    restoreCartFromStorage() {
+      const account = (this.option && this.option.account) || uni.getStorageSync('account') || ''
+      if (!account) {
+        return
+      }
+      const cached = uni.getStorageSync('selectListcg_hd_' + account)
+      if (Array.isArray(cached)) {
+        this.setSelectInfoByType({ type: this.pageType || 'cg', info: cached })
+      }
+    },
+    /** 跳转店铺购物车页 */
+    goToCart() {
+      const account = (this.option && this.option.account) || uni.getStorageSync('account') || ''
+      const hdId = (this.option && this.option.hdId) || uni.getStorageSync('hdId') || 0
+      this.pageTo({
+        url: '/pages/home/cart?account=' + account + '&hdId=' + hdId
+      })
+    },
     /**
      * 解析路由带入的 filterType / filterValue
      * type=2 商品ID列表、type=3 分类、type=4 场景(首页轮播/金刚区跳转)
@@ -405,6 +468,44 @@ export default {
       this.pageTo({
         url: `/pages/goods/detail?id=${item.id}&account=${account}&hdId=${hdId}`
       })
+    },
+    /**
+     * 列表加购:单规格(普通)直接加入购物车;多规格跳转详情选规格后再加购
+     * @param {Object} item 列表商品行(含 specEnabled / price)
+     */
+    handleCartClick(item) {
+      if (!item || !item.id) return
+      // 多规格:进入详情页选择规格
+      if (Number(item.specEnabled) === 1) {
+        this.goDetail(item)
+        return
+      }
+      if (!(Number(item.price) > 0)) {
+        this.$msg('商品还没有价格哦')
+        return
+      }
+      const account = (this.option && this.option.account) || uni.getStorageSync('account') || ''
+      const hdId = (this.option && this.option.hdId) || uni.getStorageSync('hdId') || ''
+      // rememberProduct 依赖 option.account,保证按店铺维度记忆购物车
+      if (!this.option) {
+        this.option = { account, hdId }
+      } else if (!this.option.account) {
+        this.option.account = account
+      }
+      const goods = {
+        ...item,
+        priceType: item.priceType != null ? item.priceType : 1,
+        cover: item.smallCover || item.cover,
+        smallCover: item.smallCover || item.cover
+      }
+      const ok = this.addBouquetToCart(goods, null, 1)
+      if (ok) {
+        uni.showToast({
+          title: '已加入购物车',
+          icon: 'none',
+          duration: 1500
+        })
+      }
     }
   },
   async onPullDownRefresh() {
@@ -430,7 +531,46 @@ export default {
 .goods-list-page {
   min-height: 100vh;
   background: #f5f5f5;
-  padding-bottom: 40upx;
+  /* 底部预留悬浮购物车高度,避免列表最后一项被遮挡 */
+  padding-bottom: calc(140upx + env(safe-area-inset-bottom));
+}
+
+/* 左下角悬浮购物车:白底圆形阴影 + 右上角数量红点 */
+.float-cart {
+  position: fixed;
+  left: 32upx;
+  bottom: calc(48upx + env(safe-area-inset-bottom));
+  z-index: 100;
+  width: 92upx;
+  height: 92upx;
+  border-radius: 50%;
+  background: #fff;
+  box-shadow: 0 8upx 24upx rgba(0, 0, 0, 0.12);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.float-cart-badge {
+  position: absolute;
+  top: -4upx;
+  right: -4upx;
+  min-width: 36upx;
+  height: 36upx;
+  padding: 0 8upx;
+  border-radius: 18upx;
+  background: #FF3B30;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-sizing: border-box;
+}
+
+.float-cart-badge-text {
+  font-size: 20upx;
+  color: #fff;
+  line-height: 1;
+  font-weight: 600;
 }
 
 .search-bar {
@@ -553,12 +693,19 @@ export default {
 
 .bottom-row {
   display: flex;
-  align-items: baseline;
+  align-items: center;
   justify-content: space-between;
   margin-top: auto;
   padding-top: 12upx;
 }
 
+.price-sold-col {
+  flex: 1;
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+}
+
 .price {
   font-size: 34upx;
   font-weight: 700;
@@ -577,10 +724,24 @@ export default {
 }
 
 .sold {
+  margin-top: 4upx;
   font-size: 22upx;
   color: #999;
 }
 
+/* 加购按钮:圆形底 + 白色购物车,与首页商品区一致 */
+.cart-icon {
+  flex-shrink: 0;
+  margin-left: 16upx;
+  width: 52upx;
+  height: 52upx;
+  border-radius: 50%;
+  background: #FF4D6D;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
 .list-end {
   padding: 30upx 0 20upx;
   text-align: center;

+ 149 - 3
mallApp/src/pages/goods/section-list.vue

@@ -3,6 +3,8 @@
   从秒杀/团购/热门推荐/今日上新/下拉商品的「更多」进入;
   样式参照 goods/list.vue,本次不包含搜索与筛选功能。
   秒杀/团购保留活动价、原价划线与团购人数展示。
+  右侧购物车:单规格直接加购,多规格跳转详情选规格(与 list.vue 一致)
+  左下角悬浮购物车:展示当前店铺购物车数量,点击跳转 pages/home/cart
 -->
 <template>
   <view class="goods-list-page">
@@ -31,6 +33,10 @@
               </text>
             </view>
             <text v-if="!isActivity" class="sold">已售{{ formatSold(item.sold || item.totalSold) }}件</text>
+            <!-- 加购:圆形底 + 白色购物车 SVG,与 list.vue / 首页商品区一致 -->
+            <view class="cart-icon" @click.stop="handleCartClick(item)">
+              <zui-svg-icon icon="general-cartFull" :width="16" :height="16" color="#fff" style="margin-top: 6upx" />
+            </view>
           </view>
         </view>
       </view>
@@ -39,6 +45,14 @@
     <block v-else-if="!list.loading">
       <app-wrapper-empty title="暂无商品" :is-empty="true" />
     </block>
+
+    <!-- 左下角悬浮购物车:白底圆形 + 角标数量,点击进店铺购物车页 -->
+    <view class="float-cart" @click="goToCart">
+      <zui-svg-icon icon="general-cartFull" :width="26" :height="26" color="#333" />
+      <view v-if="cartBadgeCount > 0" class="float-cart-badge">
+        <text class="float-cart-badge-text">{{ cartBadgeText }}</text>
+      </view>
+    </view>
   </view>
 </template>
 
@@ -46,15 +60,19 @@
 import AppWrapperEmpty from '@/components/app-wrapper-empty'
 import { getSectionGoods } from '@/api/home-page-config'
 import { list } from '@/mixins'
+import productMins from '@/mixins/cgProduct'
 
 export default {
   name: 'sectionGoodsList',
   components: {
     AppWrapperEmpty
   },
-  mixins: [list],
+  mixins: [list, productMins],
   data() {
     return {
+      // 购物车 pageType;关闭 mixin 自动拉花材列表,本页只复用加购能力
+      pageType: 'cg',
+      autoLoad: false,
       moduleKey: '',
       pageTitle: '商品列表',
       /** 秒杀活动结束时间,取自接口返回,用于点击商品时带给详情页 */
@@ -76,8 +94,24 @@ export default {
     },
     hdId() {
       return (this.option && this.option.hdId) || uni.getStorageSync('hdId') || ''
+    },
+    /** 悬浮购物车角标:扎数 + 支数,与详情页/店铺 tab 统计口径一致 */
+    cartBadgeCount() {
+      if (!this.selectList || !this.selectList.length) {
+        return 0
+      }
+      const count = this.allCount || {}
+      return (Number(count.bigLength) || 0) + (Number(count.smallLength) || 0)
+    },
+    /** 角标文案:超过 99 显示 99+ */
+    cartBadgeText() {
+      return this.cartBadgeCount > 99 ? '99+' : String(this.cartBadgeCount)
     }
   },
+  /** 从详情/购物车返回时刷新角标,避免仍显示旧数量 */
+  onShow() {
+    this.restoreCartFromStorage()
+  },
   methods: {
     init() {
       this.moduleKey = (this.option && this.option.moduleKey) || ''
@@ -89,8 +123,31 @@ export default {
       }
       this.pageTitle = title || '商品列表'
       uni.setNavigationBarTitle({ title: this.pageTitle })
+      // autoLoad=false 时需主动按店铺 account 恢复购物车,否则悬浮角标为 0
+      this.restoreCartFromStorage()
       this.loadGoods()
     },
+    /**
+     * 从本地缓存恢复当前店铺购物车到 Vuex
+     * 缓存 key 与分类/详情/购物车页一致:selectListcg_hd_{account}
+     * 空数组也要写入,避免从购物车清空后返回仍显示旧角标
+     */
+    restoreCartFromStorage() {
+      const account = this.account
+      if (!account) {
+        return
+      }
+      const cached = uni.getStorageSync('selectListcg_hd_' + account)
+      if (Array.isArray(cached)) {
+        this.setSelectInfoByType({ type: this.pageType || 'cg', info: cached })
+      }
+    },
+    /** 跳转店铺购物车页 */
+    goToCart() {
+      this.pageTo({
+        url: '/pages/home/cart?account=' + this.account + '&hdId=' + (this.hdId || 0)
+      })
+    },
     buildQuery() {
       return {
         moduleKey: this.moduleKey,
@@ -148,6 +205,43 @@ export default {
         url += `&activityType=seckill&activityPrice=${item.price}&activityLimit=${item.limit || 0}&activityStock=${item.stock || 0}&activityEndTime=${this.activityEndTime}`
       }
       this.pageTo({ url })
+    },
+    /**
+     * 列表加购:单规格(普通)直接加入购物车;多规格跳转详情选规格后再加购
+     * @param {Object} item 列表商品行(含 specEnabled / price)
+     */
+    handleCartClick(item) {
+      if (!item || !(item.id || item.goodsId)) return
+      // 多规格:进入详情页选择规格
+      if (Number(item.specEnabled) === 1) {
+        this.goDetail(item)
+        return
+      }
+      if (!(Number(item.price) > 0)) {
+        this.$msg('商品还没有价格哦')
+        return
+      }
+      // rememberProduct 依赖 option.account,保证按店铺维度记忆购物车
+      if (!this.option) {
+        this.option = { account: this.account, hdId: this.hdId }
+      } else if (!this.option.account) {
+        this.option.account = this.account
+      }
+      const goods = {
+        ...item,
+        id: item.id || item.goodsId,
+        priceType: item.priceType != null ? item.priceType : 1,
+        cover: item.coverUrl || item.smallCover || item.cover,
+        smallCover: item.smallCover || item.coverUrl || item.cover
+      }
+      const ok = this.addBouquetToCart(goods, null, 1)
+      if (ok) {
+        uni.showToast({
+          title: '已加入购物车',
+          icon: 'none',
+          duration: 1500
+        })
+      }
     }
   },
   async onPullDownRefresh() {
@@ -167,7 +261,46 @@ export default {
 .goods-list-page {
   min-height: 100vh;
   background: #f5f5f5;
-  padding-bottom: 40upx;
+  /* 底部预留悬浮购物车高度,避免列表最后一项被遮挡 */
+  padding-bottom: calc(140upx + env(safe-area-inset-bottom));
+}
+
+/* 左下角悬浮购物车:白底圆形阴影 + 右上角数量红点 */
+.float-cart {
+  position: fixed;
+  left: 32upx;
+  bottom: calc(48upx + env(safe-area-inset-bottom));
+  z-index: 100;
+  width: 92upx;
+  height: 92upx;
+  border-radius: 50%;
+  background: #fff;
+  box-shadow: 0 8upx 24upx rgba(0, 0, 0, 0.12);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.float-cart-badge {
+  position: absolute;
+  top: -4upx;
+  right: -4upx;
+  min-width: 36upx;
+  height: 36upx;
+  padding: 0 8upx;
+  border-radius: 18upx;
+  background: #FF3B30;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-sizing: border-box;
+}
+
+.float-cart-badge-text {
+  font-size: 20upx;
+  color: #fff;
+  line-height: 1;
+  font-weight: 600;
 }
 
 .card-list {
@@ -232,7 +365,7 @@ export default {
 
 .bottom-row {
   display: flex;
-  align-items: baseline;
+  align-items: center;
   justify-content: space-between;
   margin-top: auto;
   padding-top: 12upx;
@@ -269,6 +402,19 @@ export default {
   color: #999;
 }
 
+/* 加购按钮:圆形底 + 白色购物车,与首页商品区一致 */
+.cart-icon {
+  flex-shrink: 0;
+  margin-left: 16upx;
+  width: 52upx;
+  height: 52upx;
+  border-radius: 50%;
+  background: #FF4D6D;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
 .list-end {
   padding: 30upx 0 20upx;
   text-align: center;