ouyang 2 дней назад
Родитель
Сommit
9933320957

+ 7 - 904
hdApp/src/admin/goods/category-ad.vue

@@ -1,916 +1,19 @@
 <!--
 <!--
-  分类广告设置页
-  用途:配置最多3张轮播图,数据存 xhCategorySetting.advertisement JSON
-  格式:[{"type":1,"img":"path","value":"id1,id2"}] type:1图文 2商品 3分类
+  分类广告旧独立页:入口已并入 categoryV2 第三 Tab
+  用途:兼容旧链接,进入后重定向到分类管理广告 Tab,避免两套设置页
 -->
 -->
 <template>
 <template>
-  <view class="page-container">
-    <view class="page-header">
-      <text class="page-title">分类广告图片</text>
-      <text class="page-sub">最多支持上传3张轮播图</text>
-    </view>
-
-    <view class="ad-card" v-for="(item, index) in adList" :key="index">
-      <view class="card-title">轮播图{{ index + 1 }}</view>
-
-      <view class="upload-area" @click="chooseImage(index)">
-        <block v-if="item.img">
-          <image class="banner-img" :src="imgFullUrl(item.img)" mode="aspectFill" />
-          <view class="remove-btn" @click.stop="removeImage(index)">×</view>
-        </block>
-        <block v-else>
-          <view class="upload-placeholder">
-            <text class="upload-icon">☁</text>
-            <text class="upload-text">点击上传图片</text>
-            <text class="upload-tip">建议尺寸:750*300,支持 jpg/png</text>
-          </view>
-        </block>
-      </view>
-
-      <view class="field-row">
-        <text class="field-label">关联类型</text>
-        <view class="type-tabs">
-          <view
-            class="type-tab"
-            v-for="opt in typeOptions"
-            :key="opt.value"
-            :class="{ active: item.type === opt.value }"
-            @click="changeType(index, opt.value)"
-          >{{ opt.label }}</view>
-        </view>
-      </view>
-
-      <view class="field-row field-row-link" @click="openLinkSelect(index)">
-        <text class="field-label">关联内容</text>
-        <view class="link-select">
-          <text class="link-text" :class="{ placeholder: !getActiveLink(item).valueLabel }">{{ getActiveLink(item).valueLabel || '选择关联内容' }}</text>
-          <text class="link-arrow">></text>
-        </view>
-      </view>
-    </view>
-
-    <view class="bottom-space"></view>
-
-    <!-- 分类多选弹层:支持搜索,分类较多时便于查找 -->
-    <view v-if="categoryPickerShow" class="picker-overlay" @touchmove.stop.prevent="preventTouchMove">
-      <view class="picker-mask" @tap.stop="closeCategoryPicker"></view>
-      <view class="picker-panel category-picker-panel">
-        <view class="picker-head">
-          <text class="picker-title">选择分类</text>
-          <text class="picker-count">已选 {{ categoryPickCount }} 个</text>
-          <text class="picker-close" @tap.stop="closeCategoryPicker">×</text>
-        </view>
-        <view class="picker-search-row">
-          <view class="picker-search-box">
-            <text class="picker-search-icon">⌕</text>
-            <input
-              v-model="categorySearchText"
-              class="picker-search-input"
-              placeholder="搜索分类名称"
-              confirm-type="search"
-            />
-          </view>
-          <text
-            v-if="categorySearchText"
-            class="picker-search-clear"
-            @tap.stop="clearCategorySearch"
-          >清空</text>
-        </view>
-        <scroll-view scroll-y class="picker-body">
-          <block v-if="filteredBouquetCategories.length">
-            <view
-              class="picker-item"
-              v-for="cat in filteredBouquetCategories"
-              :key="cat.id"
-              :data-id="cat.id"
-              @tap.stop="toggleCategoryPickByEvent"
-            >
-              <view class="check-box" :class="{ checked: !!categoryPickMap[String(cat.id)] }">
-                <text v-if="categoryPickMap[String(cat.id)]" class="check-mark">✓</text>
-              </view>
-              <text class="picker-item-name">{{ cat.categoryName }}</text>
-            </view>
-          </block>
-          <view v-else class="picker-empty">
-            <text>{{ categorySearchText ? '未找到匹配的分类' : '暂无分类' }}</text>
-          </view>
-        </scroll-view>
-        <view class="picker-footer">
-          <button class="picker-btn cancel" hover-class="none" @click="closeCategoryPicker">取消</button>
-          <button class="picker-btn confirm" hover-class="none" @click="confirmCategoryPick">确定</button>
-        </view>
-      </view>
-    </view>
-
-    <!-- 预览 -->
-    <view v-if="previewShow" class="picker-overlay" @touchmove.stop.prevent="preventTouchMove">
-      <view class="picker-mask" @click.stop="previewShow = false"></view>
-      <view class="preview-panel">
-        <swiper class="preview-swiper" indicator-dots circular>
-          <swiper-item v-for="(slide, idx) in previewSlides" :key="idx">
-            <image class="preview-img" :src="slide" mode="aspectFit" />
-          </swiper-item>
-        </swiper>
-        <button class="preview-close" hover-class="none" @click="previewShow = false">关闭预览</button>
-      </view>
-    </view>
-
-    <view class="bottom-bar">
-      <button class="bar-btn bar-btn-preview" hover-class="none" @click="previewAd">
-        <text class="preview-eye">👁</text>
-        <text>预览</text>
-      </button>
-      <button class="bar-btn bar-btn-save" hover-class="none" @click="saveAd">保存</button>
-    </view>
-  </view>
+  <view></view>
 </template>
 </template>
 
 
 <script>
 <script>
-import { APIHOST } from '@/config';
-import { categoryListInfo } from '@/api/goods';
-import { categorySettingDetail, categorySettingSaveAdvertisement } from '@/api/category-setting';
-
-const EMPTY_LINK = () => ({ value: '', valueLabel: '' });
-
-const EMPTY_SLOT = () => ({
-  type: 1,
-  img: '',
-  // 各关联类型独立缓存,切换类型时不丢失已选内容
-  links: {
-    1: EMPTY_LINK(),
-    2: EMPTY_LINK(),
-    3: EMPTY_LINK()
-  }
-});
-
 export default {
 export default {
   name: 'category-ad',
   name: 'category-ad',
-  data() {
-    return {
-      constant: this.$constant,
-      settingId: 0,
-      adList: [EMPTY_SLOT(), EMPTY_SLOT(), EMPTY_SLOT()],
-      typeOptions: [
-        { label: '图文', value: 1 },
-        { label: '商品', value: 2 },
-        { label: '分类', value: 3 }
-      ],
-      bouquetCategories: [],
-      categoryPickerShow: false,
-      categoryPickIndex: 0,
-      categoryPickMap: {},
-      categorySearchText: '',
-      previewShow: false,
-      saving: false
-    };
-  },
-  computed: {
-    previewSlides() {
-      return this.adList
-        .filter((item) => item.img)
-        .map((item) => this.imgFullUrl(item.img));
-    },
-    /** 分类弹层内已选数量 */
-    categoryPickCount() {
-      return Object.keys(this.categoryPickMap).length;
-    },
-    /** 按关键词过滤花束分类,分类多时可快速定位 */
-    filteredBouquetCategories() {
-      const keyword = (this.categorySearchText || '').trim().toLowerCase();
-      if (!keyword) return this.bouquetCategories;
-      return this.bouquetCategories.filter((cat) => {
-        const name = (cat.categoryName || '').toLowerCase();
-        return name.indexOf(keyword) > -1;
-      });
-    }
-  },
   onLoad() {
   onLoad() {
-    this.loadSetting();
-    this.loadBouquetCategories();
-    uni.$on('categoryAdPicTextSelected', this.onPicTextSelected);
-    uni.$on('categoryAdGoodsSelected', this.onGoodsSelected);
-  },
-  onUnload() {
-    uni.$off('categoryAdPicTextSelected', this.onPicTextSelected);
-    uni.$off('categoryAdGoodsSelected', this.onGoodsSelected);
-  },
-  methods: {
-    preventTouchMove() {},
-    imgFullUrl(path) {
-      if (!path) return '';
-      if (path.indexOf('http') === 0) return path;
-      const base = (this.constant.imgUrl || '').replace(/\/$/, '');
-      return `${base}/${path.replace(/^\//, '')}`;
-    },
-    loadSetting() {
-      categorySettingDetail().then((res) => {
-        if (res.code != 1) return;
-        const data = res.data || {};
-        this.settingId = data.id || 0;
-        this.adList = this.parseAdvertisement(data.advertisement);
-      });
-    },
-    loadBouquetCategories() {
-      categoryListInfo().then((res) => {
-        const list = res.data || [];
-        this.bouquetCategories = list;
-        // 分类名称依赖列表,加载后刷新各 slot 分类类型展示文案
-        this.adList = this.adList.map((item) => this.refreshSlotLinkLabels(item));
-      });
-    },
-    /** 取当前关联类型下的内容与展示文案 */
-    getActiveLink(item) {
-      const type = item.type || 1;
-      return (item.links && item.links[type]) || EMPTY_LINK();
-    },
-    /** 写入指定类型的关联内容 */
-    setSlotLink(item, type, value, valueLabel) {
-      if (!item.links) {
-        item.links = { 1: EMPTY_LINK(), 2: EMPTY_LINK(), 3: EMPTY_LINK() };
-      }
-      this.$set(item.links, type, { value: value || '', valueLabel: valueLabel || '' });
-    },
-    /** 分类列表就绪后,重算 slot 内各类型展示文案 */
-    refreshSlotLinkLabels(item) {
-      const links = item.links || { 1: EMPTY_LINK(), 2: EMPTY_LINK(), 3: EMPTY_LINK() };
-      [1, 2, 3].forEach((type) => {
-        const link = links[type] || EMPTY_LINK();
-        if (link.value) {
-          links[type] = {
-            value: link.value,
-            valueLabel: this.buildValueLabel(type, link.value, link.valueLabel)
-          };
-        }
-      });
-      return { ...item, links };
-    },
-    parseAdvertisement(raw) {
-      const slots = [EMPTY_SLOT(), EMPTY_SLOT(), EMPTY_SLOT()];
-      if (!raw) return slots;
-      let arr = [];
-      try {
-        arr = typeof raw === 'string' ? JSON.parse(raw) : raw;
-      } catch (e) {
-        return slots;
-      }
-      if (!Array.isArray(arr)) return slots;
-      arr.slice(0, 3).forEach((item, index) => {
-        const type = parseInt(item.type, 10) || 1;
-        const value = item.value ? String(item.value) : '';
-        const links = {
-          1: EMPTY_LINK(),
-          2: EMPTY_LINK(),
-          3: EMPTY_LINK()
-        };
-        links[type] = {
-          value,
-          valueLabel: this.buildValueLabel(type, value)
-        };
-        slots[index] = {
-          type,
-          img: item.img || '',
-          links
-        };
-      });
-      return slots;
-    },
-    buildValueLabel(type, value, fallbackLabel) {
-      if (!value) return '';
-      const ids = value.split(',').filter(Boolean);
-      if (type === 1) {
-        if (fallbackLabel && fallbackLabel.indexOf('图文ID:') !== 0) return fallbackLabel;
-        return ids.length ? `图文ID:${ids.join(',')}` : '';
-      }
-      if (type === 2) return `已选${ids.length}个商品`;
-      if (type === 3) {
-        const names = ids.map((id) => {
-          const cat = this.bouquetCategories.find((c) => String(c.id) === String(id));
-          return cat ? cat.categoryName : id;
-        });
-        return names.join('、') || `已选${ids.length}个分类`;
-      }
-      return '';
-    },
-    /** 仅切换展示类型,不清空各类型已缓存的关联内容 */
-    changeType(index, type) {
-      const item = this.adList[index];
-      if (item.type === type) return;
-      item.type = type;
-    },
-    openLinkSelect(index) {
-      const item = this.adList[index];
-      const link = this.getActiveLink(item);
-      if (item.type === 1) {
-        uni.navigateTo({
-          url: `/admin/picText/picTextList?mode=selectAd&slotIndex=${index}&picTextId=${link.value || 0}`
-        });
-        return;
-      }
-      if (item.type === 2) {
-        uni.navigateTo({
-          url: `/admin/goods/ad-goods-select?slotIndex=${index}&selectedIds=${link.value || ''}`
-        });
-        return;
-      }
-      if (item.type === 3) {
-        this.categoryPickIndex = index;
-        this.categorySearchText = '';
-        this.categoryPickMap = {};
-        const ids = link.value ? link.value.split(',').filter(Boolean) : [];
-        ids.forEach((id) => {
-          this.$set(this.categoryPickMap, String(id), true);
-        });
-        this.categoryPickerShow = true;
-      }
-    },
-    onPicTextSelected(payload) {
-      const index = payload.slotIndex;
-      if (index === undefined || !this.adList[index]) return;
-      const valueLabel = payload.title || `图文ID:${payload.picTextId}`;
-      this.setSlotLink(this.adList[index], 1, String(payload.picTextId || ''), valueLabel);
-    },
-    onGoodsSelected(payload) {
-      const index = payload.slotIndex;
-      if (index === undefined || !this.adList[index]) return;
-      const ids = (payload.goodsIds || []).map(String);
-      const value = ids.join(',');
-      this.setSlotLink(
-        this.adList[index],
-        2,
-        value,
-        ids.length ? `已选${ids.length}个商品` : ''
-      );
-    },
-    /** 从 data-id 切换分类勾选,兼容小程序 tap 传参 */
-    toggleCategoryPickByEvent(e) {
-      const id = e.currentTarget.dataset.id;
-      if (id === undefined || id === null || id === '') return;
-      const key = String(id);
-      if (this.categoryPickMap[key]) {
-        this.$delete(this.categoryPickMap, key);
-      } else {
-        this.$set(this.categoryPickMap, key, true);
-      }
-    },
-    clearCategorySearch() {
-      this.categorySearchText = '';
-    },
-    closeCategoryPicker() {
-      this.categoryPickerShow = false;
-      this.categorySearchText = '';
-    },
-    confirmCategoryPick() {
-      const index = this.categoryPickIndex;
-      const ids = Object.keys(this.categoryPickMap);
-      const value = ids.join(',');
-      this.setSlotLink(this.adList[index], 3, value, this.buildValueLabel(3, value));
-      this.categoryPickerShow = false;
-      this.categorySearchText = '';
-    },
-    chooseImage(index) {
-      uni.chooseImage({
-        count: 1,
-        sizeType: ['compressed'],
-        sourceType: ['album', 'camera'],
-        success: (res) => {
-          const filePath = res.tempFilePaths[0];
-          this.uploadImageFile(filePath, index);
-        }
-      });
-    },
-    /** 通过 multipart 上传广告图,存相对路径 shortUrl 供保存与展示 */
-    uploadImageFile(filePath, index) {
-      uni.showLoading({ title: '上传中', mask: true });
-      const token = uni.getStorageSync('token') || '';
-      uni.uploadFile({
-        url: `${APIHOST}/upload/save-file`,
-        filePath,
-        name: 'file',
-        fileType: 'image',
-        header: { token },
-        success: (uploadRes) => {
-          uni.hideLoading();
-          try {
-            const res = JSON.parse(uploadRes.data || '{}');
-            if (res.code == 1 && res.data) {
-              // 保存相对路径,展示时由 imgFullUrl 拼接域名
-              this.adList[index].img = res.data.shortUrl || res.data.url || '';
-              if (!this.adList[index].img) {
-                this.$msg('上传失败');
-              }
-              return;
-            }
-            this.$msg(res.message || '上传失败');
-          } catch (e) {
-            this.$msg('上传失败');
-          }
-        },
-        fail: () => {
-          uni.hideLoading();
-          this.$msg('上传失败');
-        }
-      });
-    },
-    removeImage(index) {
-      this.adList[index].img = '';
-    },
-    previewAd() {
-      if (!this.previewSlides.length) {
-        this.$msg('请先上传广告图');
-        return;
-      }
-      this.previewShow = true;
-    },
-    saveAd() {
-      const payload = [];
-      for (let i = 0; i < this.adList.length; i++) {
-        const item = this.adList[i];
-        const link = this.getActiveLink(item);
-        if (!item.img && !link.value) continue;
-        if (item.img && !link.value) {
-          this.$msg(`请完善轮播图${i + 1}的关联内容`);
-          return;
-        }
-        if (link.value && !item.img) {
-          this.$msg(`请上传轮播图${i + 1}的图片`);
-          return;
-        }
-        payload.push({
-          type: item.type,
-          img: item.img,
-          value: link.value
-        });
-      }
-      if (this.saving) return;
-      this.saving = true;
-      categorySettingSaveAdvertisement({
-        advertisement: JSON.stringify(payload)
-      }).then((res) => {
-        if (res.code == 1) {
-          this.$msg('保存成功');
-          setTimeout(() => uni.navigateBack(), 500);
-        }
-      }).finally(() => {
-        this.saving = false;
-      });
-    }
+    // 旧工作台/应用页入口仍可能落到本页,统一进分类管理广告 Tab
+    uni.redirectTo({
+      url: '/admin/goods/categoryV2?tab=2'
+    });
   }
   }
 };
 };
 </script>
 </script>
-
-<style lang="scss" scoped>
-.page-container {
-  min-height: 100vh;
-  background: #f5f5f5;
-  padding-bottom: 160rpx;
-}
-
-.page-header {
-  padding: 24rpx 30rpx 8rpx;
-}
-
-.page-title {
-  display: block;
-  font-size: 30rpx;
-  color: #333;
-  font-weight: 600;
-  margin-bottom: 8rpx;
-}
-
-.page-sub {
-  font-size: 24rpx;
-  color: #999;
-}
-
-.ad-card {
-  margin: 20rpx 24rpx 0;
-  padding: 24rpx;
-  background: #fff;
-  border-radius: 12rpx;
-  border: 1rpx solid #eee;
-}
-
-.card-title {
-  font-size: 28rpx;
-  color: #333;
-  font-weight: 600;
-  margin-bottom: 20rpx;
-}
-
-.upload-area {
-  position: relative;
-  width: 100%;
-  height: 240rpx;
-  border-radius: 8rpx;
-  overflow: hidden;
-  margin-bottom: 24rpx;
-}
-
-.banner-img {
-  width: 100%;
-  height: 100%;
-}
-
-.remove-btn {
-  position: absolute;
-  top: 12rpx;
-  right: 12rpx;
-  width: 44rpx;
-  height: 44rpx;
-  line-height: 40rpx;
-  text-align: center;
-  background: rgba(0, 0, 0, 0.45);
-  color: #fff;
-  border-radius: 50%;
-  font-size: 32rpx;
-}
-
-.upload-placeholder {
-  width: 100%;
-  height: 100%;
-  border: 2rpx dashed #52c41a;
-  border-radius: 8rpx;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  background: #fafafa;
-}
-
-.upload-icon {
-  font-size: 48rpx;
-  color: #52c41a;
-  margin-bottom: 12rpx;
-}
-
-.upload-text {
-  font-size: 28rpx;
-  color: #52c41a;
-  margin-bottom: 8rpx;
-}
-
-.upload-tip {
-  font-size: 22rpx;
-  color: #999;
-}
-
-.field-row {
-  margin-bottom: 20rpx;
-}
-
-.field-row-link {
-  margin-bottom: 0;
-}
-
-.field-label {
-  display: block;
-  font-size: 26rpx;
-  color: #333;
-  margin-bottom: 12rpx;
-}
-
-.type-tabs {
-  display: flex;
-  flex-direction: row;
-}
-
-.type-tab {
-  flex: 1;
-  height: 64rpx;
-  line-height: 64rpx;
-  text-align: center;
-  font-size: 26rpx;
-  color: #666;
-  border: 2rpx solid #e5e5e5;
-  background: #fff;
-  margin-right: 16rpx;
-  border-radius: 8rpx;
-}
-
-.type-tab:last-child {
-  margin-right: 0;
-}
-
-.type-tab.active {
-  color: #52c41a;
-  border-color: #52c41a;
-  background: #f6ffed;
-}
-
-.link-select {
-  display: flex;
-  align-items: center;
-  height: 72rpx;
-  padding: 0 20rpx;
-  border: 1rpx solid #e5e5e5;
-  border-radius: 8rpx;
-  background: #fff;
-}
-
-.link-text {
-  flex: 1;
-  font-size: 26rpx;
-  color: #333;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-
-.link-text.placeholder {
-  color: #bbb;
-}
-
-.link-arrow {
-  color: #ccc;
-  font-size: 28rpx;
-  margin-left: 12rpx;
-}
-
-.bottom-space {
-  height: 40rpx;
-}
-
-.bottom-bar {
-  position: fixed;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  padding: 20rpx 24rpx;
-  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
-  background: #fff;
-  box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.06);
-  z-index: 100;
-}
-
-.bar-btn {
-  height: 80rpx;
-  line-height: 80rpx;
-  margin: 0;
-  padding: 0;
-  font-size: 28rpx;
-  border-radius: 8rpx;
-  text-align: center;
-}
-
-.bar-btn::after {
-  border: none;
-}
-
-.bar-btn-preview {
-  width: 200rpx;
-  background: #fff;
-  color: #52c41a;
-  border: 2rpx solid #ddd;
-  margin-right: 16rpx;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  line-height: 1;
-}
-
-.preview-eye {
-  margin-right: 6rpx;
-  font-size: 28rpx;
-}
-
-.bar-btn-save {
-  flex: 1;
-  background: #52c41a;
-  color: #fff;
-}
-
-.picker-overlay {
-  position: fixed;
-  left: 0;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  z-index: 200;
-  display: flex;
-  align-items: flex-end;
-  justify-content: center;
-}
-
-.picker-mask {
-  position: absolute;
-  left: 0;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  background: rgba(0, 0, 0, 0.5);
-}
-
-.picker-panel {
-  position: relative;
-  z-index: 1;
-  width: 100%;
-  max-height: 70vh;
-  background: #fff;
-  border-radius: 24rpx 24rpx 0 0;
-  display: flex;
-  flex-direction: column;
-}
-
-.category-picker-panel {
-  max-height: 82vh;
-}
-
-.picker-head {
-  position: relative;
-  height: 88rpx;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  border-bottom: 1rpx solid #f0f0f0;
-  flex-shrink: 0;
-}
-
-.picker-title {
-  font-size: 32rpx;
-  font-weight: 600;
-  color: #333;
-}
-
-.picker-count {
-  position: absolute;
-  left: 24rpx;
-  font-size: 24rpx;
-  color: #52c41a;
-}
-
-.picker-close {
-  position: absolute;
-  right: 24rpx;
-  font-size: 36rpx;
-  color: #999;
-  padding: 8rpx;
-}
-
-.picker-search-row {
-  display: flex;
-  align-items: center;
-  padding: 16rpx 24rpx;
-  border-bottom: 1rpx solid #f5f5f5;
-  flex-shrink: 0;
-}
-
-.picker-search-box {
-  flex: 1;
-  min-width: 0;
-  height: 68rpx;
-  display: flex;
-  align-items: center;
-  padding: 0 20rpx;
-  border: 2rpx solid #e8e8e8;
-  border-radius: 34rpx;
-  background: #fafafa;
-  box-sizing: border-box;
-}
-
-.picker-search-icon {
-  font-size: 28rpx;
-  color: #999;
-  margin-right: 12rpx;
-  flex-shrink: 0;
-}
-
-.picker-search-input {
-  flex: 1;
-  min-width: 0;
-  height: 64rpx;
-  font-size: 26rpx;
-  color: #333;
-}
-
-.picker-search-clear {
-  margin-left: 16rpx;
-  font-size: 24rpx;
-  color: #52c41a;
-  flex-shrink: 0;
-}
-
-.picker-body {
-  flex: 1;
-  min-height: 0;
-  max-height: 46vh;
-  padding: 8rpx 24rpx 16rpx;
-  box-sizing: border-box;
-}
-
-.picker-empty {
-  padding: 80rpx 0;
-  text-align: center;
-  font-size: 26rpx;
-  color: #999;
-}
-
-.picker-item {
-  display: flex;
-  align-items: center;
-  padding: 22rpx 0;
-  border-bottom: 1rpx solid #f5f5f5;
-}
-
-.picker-item:last-child {
-  border-bottom: none;
-}
-
-.check-box {
-  width: 36rpx;
-  height: 36rpx;
-  border: 2rpx solid #ccc;
-  border-radius: 6rpx;
-  margin-right: 16rpx;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  flex-shrink: 0;
-}
-
-.check-box.checked {
-  background: #52c41a;
-  border-color: #52c41a;
-}
-
-.check-mark {
-  color: #fff;
-  font-size: 24rpx;
-}
-
-.picker-item-name {
-  flex: 1;
-  min-width: 0;
-  font-size: 28rpx;
-  color: #333;
-  line-height: 1.4;
-}
-
-.picker-footer {
-  display: flex;
-  padding: 20rpx 24rpx;
-  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
-  flex-shrink: 0;
-  border-top: 1rpx solid #f0f0f0;
-}
-
-.picker-btn {
-  flex: 1;
-  height: 80rpx;
-  line-height: 80rpx;
-  margin: 0;
-  font-size: 28rpx;
-  border-radius: 8rpx;
-}
-
-.picker-btn::after {
-  border: none;
-}
-
-.picker-btn.cancel {
-  margin-right: 20rpx;
-  background: #fff;
-  color: #333;
-  border: 2rpx solid #ddd;
-}
-
-.picker-btn.confirm {
-  background: #52c41a;
-  color: #fff;
-}
-
-.preview-panel {
-  position: relative;
-  z-index: 1;
-  width: 90%;
-  background: #fff;
-  border-radius: 16rpx;
-  padding: 24rpx;
-  box-sizing: border-box;
-}
-
-.preview-swiper {
-  height: 320rpx;
-}
-
-.preview-img {
-  width: 100%;
-  height: 320rpx;
-}
-
-.preview-close {
-  margin-top: 20rpx;
-  height: 72rpx;
-  line-height: 72rpx;
-  background: #52c41a;
-  color: #fff;
-  font-size: 28rpx;
-  border-radius: 8rpx;
-}
-
-.preview-close::after {
-  border: none;
-}
-</style>

+ 328 - 226
hdApp/src/admin/goods/categoryV2.vue

@@ -4,88 +4,102 @@
 -->
 -->
 <template>
 <template>
   <view class="page-container">
   <view class="page-container">
-    <!-- Tabs:花束 / 花材 / 分类广告(广告在本页组件内编辑) -->
-    <view class="tabs">
-      <view class="tab-item" :class="{active: currentTab === 0}" @click="switchTab(0)">
-        <text>花束分类</text>
-        <view class="tab-line" v-if="currentTab === 0"></view>
-      </view>
-      <view class="tab-item" :class="{active: currentTab === 1}" @click="switchTab(1)">
-        <text>花材分类</text>
-        <view class="tab-line" v-if="currentTab === 1"></view>
+    <!-- Tab + 表头吸顶:列表滚动时列名仍对齐,方便对照操作 -->
+    <view class="sticky-top">
+      <view class="modern-tabs-container">
+        <view class="modern-tabs">
+          <view class="modern-tab" :class="{ active: currentTab === 0 }" @click="switchTab(0)">
+            <view class="tab-content">
+              <text class="tab-text">花束分类</text>
+            </view>
+          </view>
+          <view class="modern-tab" :class="{ active: currentTab === 1 }" @click="switchTab(1)">
+            <view class="tab-content">
+              <text class="tab-text">花材分类</text>
+            </view>
+          </view>
+          <view class="modern-tab" :class="{ active: currentTab === 2 }" @click="switchTab(2)">
+            <view class="tab-content">
+              <text class="tab-text">分类广告</text>
+            </view>
+          </view>
+        </view>
       </view>
       </view>
-      <view class="tab-item" :class="{active: currentTab === 2}" @click="switchTab(2)">
-        <text>分类广告</text>
-        <view class="tab-line" v-if="currentTab === 2"></view>
+
+      <view
+        v-if="currentTab !== 2"
+        class="table-head-bar"
+        :class="{ 'layout-item-class': currentTab === 1 }"
+      >
+        <view class="list-header">
+          <view class="col-name">分类名称</view>
+          <view class="col-num" v-if="currentTab === 0">数量</view>
+          <view class="col-pic" v-if="currentTab === 0">图文</view>
+          <view class="col-action">操作</view>
+        </view>
       </view>
       </view>
     </view>
     </view>
+    <!-- APP 用 fixed 吸顶,占位避免内容顶到导航栏下 -->
+    <!-- #ifdef APP-PLUS -->
+    <view class="sticky-spacer" :class="{ 'has-table-head': currentTab !== 2 }"></view>
+    <!-- #endif -->
 
 
     <!-- 分类广告:3 个广告位表格,组件内完成设置 -->
     <!-- 分类广告:3 个广告位表格,组件内完成设置 -->
     <category-ad-setting v-if="currentTab === 2" ref="categoryAdSetting" />
     <category-ad-setting v-if="currentTab === 2" ref="categoryAdSetting" />
 
 
-    <!-- 列表头部 -->
+    <!-- 表体单独滚动,表头已吸顶 -->
     <view
     <view
       v-if="currentTab !== 2"
       v-if="currentTab !== 2"
-      class="list-header"
+      class="table-body"
       :class="{ 'layout-item-class': currentTab === 1 }"
       :class="{ 'layout-item-class': currentTab === 1 }"
     >
     >
-      <view class="col-name">分类名称</view>
-      <view class="col-num" v-if="currentTab === 0">商品数量</view>
-      <view class="col-pic" v-if="currentTab === 0">图文</view>
-      <view class="col-action">操作</view>
-    </view>
-
-    <!-- 分类列表:不用 scroll-view,避免小程序内点击事件被拦截 -->
-    <view
-      v-if="currentTab !== 2"
-      class="list-container"
-      :class="{ 'layout-item-class': currentTab === 1 }"
-    >
-      <block v-if="!$util.isEmpty(filteredList)">
-        <view
-          class="list-item"
-          :class="{'is-disabled': item.status != 1}"
-          v-for="item in filteredList"
-          :key="item.listKey"
-        >
-          <view class="col-name item-name">
-            <!-- 花束分类名称只展示文字,去掉封面图标,避免挤占操作列 -->
-            <image
-              v-if="currentTab === 1 && item.img"
-              :src="item.img"
-              mode="aspectFit"
-              class="cat-img"
-            ></image>
-            <text class="cat-text">{{ item.categoryName }}</text>
-          </view>
-
-          <view class="col-num item-num" v-if="currentTab === 0">
-            <text>{{ item.goodsNum || 0 }}</text>
-          </view>
-
+      <!-- 不用 scroll-view,避免小程序内点击事件被拦截 -->
+      <view class="list-container">
+        <block v-if="!$util.isEmpty(filteredList)">
           <view
           <view
-            class="col-pic item-pic"
-            v-if="currentTab === 0"
-            :data-id="item.id"
-            :data-pic-text-id="item.picTextId"
-            @click.stop="selectPicText"
+            class="list-item"
+            :class="{'is-disabled': item.status != 1}"
+            v-for="item in filteredList"
+            :key="item.listKey"
           >
           >
-            <!-- 图文列只保留文字状态,去掉图标,整块可点 -->
-            <view class="pic-tag" :class="item.picTextId > 0 ? 'is-set' : 'no-set'">
-              <text>{{ item.picTextId > 0 ? '已设置' : '未设置' }}</text>
+            <view class="col-name item-name">
+              <!-- 花束分类名称只展示文字,去掉封面图标,避免挤占操作列 -->
+              <image
+                v-if="currentTab === 1 && item.img"
+                :src="item.img"
+                mode="aspectFit"
+                class="cat-img"
+              ></image>
+              <text class="cat-text">{{ item.categoryName }}</text>
+            </view>
+
+            <view class="col-num item-num" v-if="currentTab === 0">
+              <text class="num-pill">{{ item.goodsNum || 0 }}</text>
+            </view>
+
+            <view
+              class="col-pic item-pic"
+              v-if="currentTab === 0"
+              :data-id="item.id"
+              :data-pic-text-id="item.picTextId"
+              @click.stop="selectPicText"
+            >
+              <!-- 图文列只保留文字状态,去掉图标,整块可点 -->
+              <view class="pic-tag" :class="item.picTextId > 0 ? 'is-set' : 'no-set'">
+                <text>{{ item.picTextId > 0 ? '已设置' : '未设置' }}</text>
+              </view>
             </view>
             </view>
-          </view>
 
 
-          <view class="col-action item-actions">
-            <button class="action-btn edit-btn" plain hover-class="none" :data-id="item.id" @click.stop="editCategory">编辑</button>
-            <text class="action-divider">|</text>
-            <button class="action-btn delete-btn" plain hover-class="none" v-if="item.default != 1" :data-id="item.id" @click.stop="delCategory">删除</button>
+            <view class="col-action item-actions">
+              <button class="action-btn edit-btn" plain hover-class="none" :data-id="item.id" @click.stop="editCategory">编辑</button>
+              <button class="action-btn delete-btn" plain hover-class="none" v-if="item.default != 1" :data-id="item.id" @click.stop="delCategory">删除</button>
+            </view>
           </view>
           </view>
-        </view>
-      </block>
-      <block v-else>
-        <app-wrapper-empty title="暂无分类数据" :is-empty="$util.isEmpty(filteredList)" />
-      </block>
+        </block>
+        <block v-else>
+          <app-wrapper-empty title="暂无分类数据" :is-empty="$util.isEmpty(filteredList)" />
+        </block>
+      </view>
     </view>
     </view>
 
 
     <!-- 底部操作栏:分类广告 Tab 用组件自己的预览/保存栏 -->
     <!-- 底部操作栏:分类广告 Tab 用组件自己的预览/保存栏 -->
@@ -626,64 +640,127 @@ export default {
 .page-container {
 .page-container {
   min-height: 100vh;
   min-height: 100vh;
   background: #f5f5f5;
   background: #f5f5f5;
+}
+
+/* 不设 flex 容器,保证走页面滚动,sticky 才能吸在导航栏下 */
+.sticky-top {
+  position: sticky;
+  top: 0;
+  z-index: 20;
+  background: #f5f5f5;
+}
+
+/* APP 部分 WebView sticky 不稳,改 fixed 并靠 spacer 撑开高度 */
+/* #ifdef APP-PLUS */
+.sticky-top {
+  position: fixed;
+  left: 0;
+  right: 0;
+  top: 0;
+}
+
+.sticky-spacer {
+  width: 100%;
+  height: 132upx;
+}
+
+.sticky-spacer.has-table-head {
+  height: 232upx;
+}
+/* #endif */
+
+/* 顶部花束/花材/广告切换:对齐 manage.vue 的 modern-tabs */
+.modern-tabs-container {
+  padding: 0;
+  background-color: #f8f9fa;
+  width: 100%;
+}
+
+.modern-tabs {
   display: flex;
   display: flex;
-  flex-direction: column;
+  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: 30upx 0;
+  position: relative;
+  transition: all 0.35s cubic-bezier(0.25, 1, 0.5, 1);
+  border-radius: 12upx;
+  margin: 0 2upx;
+  background-color: #e2dfdf;
+  border: 2upx solid #bfbfbf;
+  box-sizing: border-box;
+  box-shadow: none;
 }
 }
 
 
-/* Tabs */
-.tabs {
+.modern-tab .tab-content {
   display: flex;
   display: flex;
-  background: #fff;
-  padding: 0 20upx;
-  border-bottom: 1upx solid #eee;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
 
 
-  .tab-item {
-    flex: 1;
-    text-align: center;
-    font-size: 28upx;
-    color: #666;
-    padding: 24upx 0;
-    position: relative;
+.modern-tab .tab-text {
+  font-size: 30upx;
+  color: #666;
+  font-weight: 700;
+}
 
 
-    &.active {
-      color: #52c41a;
-      font-weight: bold;
-    }
+.modern-tab.active {
+  background: #09C567;
+  border-color: #07a056;
+}
 
 
-    .tab-line {
-      position: absolute;
-      bottom: 0;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 80upx;
-      height: 4upx;
-      background: #52c41a;
-      border-radius: 4upx;
-    }
-  }
+.modern-tab.active .tab-text {
+  color: #ffffff;
+  font-weight: 700;
+  font-size: 30upx;
+}
+
+/* 吸顶表头条:与下方表体左右对齐,滚动时盖住上滑的行 */
+.table-head-bar {
+  padding: 16upx 20upx 0;
+  background: #f5f5f5;
 }
 }
 
 
-/* List Header */
 .list-header {
 .list-header {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
-  padding: 24upx 30upx;
-  background: #fdfdfd;
-  margin-top: 20upx;
-  border-bottom: 1upx solid #f0f0f0;
-  border-radius: 12upx 12upx 0 0;
-  margin: 20upx 20upx 0 20upx;
+  padding: 20upx 24upx;
+  background: #f4faf6;
+  border: 2upx solid #eef1ee;
+  border-bottom: 2upx solid #e6f4ea;
+  border-radius: 16upx 16upx 0 0;
+  box-shadow: 0 8upx 16upx rgba(22, 24, 25, 0.04);
 
 
   view {
   view {
     font-size: 24upx;
     font-size: 24upx;
-    color: #333;
-    font-weight: 500;
+    color: #7a847c;
+    font-weight: 600;
     text-align: center;
     text-align: center;
+    line-height: 1.4;
   }
   }
 }
 }
 
 
+.table-body {
+  margin: 0 20upx;
+  background: #fff;
+  border: 2upx solid #eef1ee;
+  border-top: none;
+  border-radius: 0 0 16upx 16upx;
+  box-shadow: 0 8upx 24upx rgba(22, 24, 25, 0.04);
+}
+
 .col-name { flex: 2.6; min-width: 0; text-align: left !important; }
 .col-name { flex: 2.6; min-width: 0; text-align: left !important; }
-.col-num { flex: 1.4; flex-shrink: 0; }
+.col-num { flex: 1.3; flex-shrink: 0; }
 .col-pic { flex: 1.6; flex-shrink: 0; }
 .col-pic { flex: 1.6; flex-shrink: 0; }
 .col-action { flex: 2.8; flex-shrink: 0; }
 .col-action { flex: 2.8; flex-shrink: 0; }
 
 
@@ -698,153 +775,178 @@ export default {
   }
   }
 }
 }
 
 
-/* List */
 .list-container {
 .list-container {
-  flex: 1;
   background: #fff;
   background: #fff;
-  margin: 0 20upx 0 20upx;
-  border-radius: 0 0 12upx 12upx;
-  overflow-y: auto;
   /* 预留底部固定工具栏高度,避免最后一行被遮挡 */
   /* 预留底部固定工具栏高度,避免最后一行被遮挡 */
   padding-bottom: calc(200upx + env(safe-area-inset-bottom));
   padding-bottom: calc(200upx + env(safe-area-inset-bottom));
   box-sizing: border-box;
   box-sizing: border-box;
+}
 
 
-  .list-item {
-    display: flex;
-    align-items: flex-start;
-    padding: 24upx 30upx;
-    border-bottom: 1upx solid #f0f0f0;
+.list-item {
+  display: flex;
+  align-items: center;
+  padding: 22upx 24upx;
+  border-bottom: 1upx solid #f2f4f2;
+  background: #fff;
 
 
-    &:last-child {
-      border-bottom: none;
-    }
+  &:last-child {
+    border-bottom: none;
+  }
 
 
-    /* 禁用分类:参考设计图开业乔迁,整行置灰 */
-    &.is-disabled {
-      background: #f7f7f7;
+  /* 禁用分类:参考设计图开业乔迁,整行置灰 */
+  &.is-disabled {
+    background: #f7f8f7;
 
 
-      .cat-img {
-        opacity: 0.4;
-      }
+    .cat-img {
+      opacity: 0.4;
+    }
 
 
-      .cat-text {
-        color: #c8c8c8;
-        font-weight: 400;
-      }
+    .cat-text {
+      color: #c8c8c8;
+      font-weight: 400;
+    }
 
 
-      .item-num {
-        color: #d0d0d0;
-      }
+    .num-pill {
+      color: #c0c4c8;
+      background: #f0f0f0;
+    }
 
 
-      .pic-tag {
-        &.no-set,
-        &.is-set {
-          color: #d0d0d0;
-        }
+    .pic-tag {
+      &.no-set,
+      &.is-set {
+        color: #c0c4c8;
+        background: #f0f0f0;
+        border-color: #e5e5e5;
       }
       }
     }
     }
 
 
-    .item-name {
-      display: flex;
-      align-items: flex-start;
-      min-width: 0;
-
-      .cat-img {
-        width: 60upx;
-        height: 60upx;
-        margin-right: 16upx;
-        margin-top: 4upx;
-        border-radius: 8upx;
-        flex-shrink: 0;
-      }
-      .cat-text {
-        flex: 1;
-        min-width: 0;
-        font-size: 28upx;
-        color: #333;
-        font-weight: 500;
-        line-height: 1.5;
-        white-space: normal;
-        word-wrap: break-word;
-        word-break: break-all;
-      }
+    .edit-btn,
+    .delete-btn {
+      color: #c0c4c8;
+      border-color: #e0e0e0;
+      background: #fafafa;
     }
     }
+  }
+}
+
+.item-name {
+  display: flex;
+  align-items: center;
+  min-width: 0;
+
+  .cat-img {
+    width: 56upx;
+    height: 56upx;
+    margin-right: 14upx;
+    border-radius: 10upx;
+    flex-shrink: 0;
+    background: #f5f7f5;
+    border: 2upx solid #eef1ee;
+  }
+
+  .cat-text {
+    flex: 1;
+    min-width: 0;
+    font-size: 28upx;
+    color: #161819;
+    font-weight: 600;
+    line-height: 1.45;
+    white-space: normal;
+    word-wrap: break-word;
+    word-break: break-all;
+  }
+}
+
+.item-num {
+  display: flex;
+  justify-content: center;
+}
+
+.num-pill {
+  min-width: 56upx;
+  padding: 4upx 14upx;
+  border-radius: 20upx;
+  background: #eef3ff;
+  color: #3b5bdf;
+  font-size: 26upx;
+  font-weight: 600;
+  text-align: center;
+  line-height: 36upx;
+}
+
+.item-pic {
+  display: flex;
+  justify-content: center;
 
 
-    .item-num,
-    .item-pic,
-    .item-actions {
-      align-self: center;
+  .pic-tag {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    min-height: 52upx;
+    padding: 6upx 16upx;
+    border-radius: 26upx;
+    font-size: 24upx;
+    font-weight: 600;
+    border: 2upx solid transparent;
+    box-sizing: border-box;
+
+    &.no-set {
+      color: #8a9199;
+      background: #f5f6f7;
+      border-color: #e6e8eb;
     }
     }
 
 
-    .item-num {
-      font-size: 28upx;
-      color: #3b5bdf;
-      text-align: center;
+    &.is-set {
+      color: #09c567;
+      background: #f0fbf4;
+      border-color: #b7eb8f;
     }
     }
+  }
+}
 
 
-    .item-pic {
-      display: flex;
-      justify-content: center;
-
-      .pic-tag {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        min-height: 64upx;
-        padding: 8upx 12upx;
-        border-radius: 8upx;
-        font-size: 28upx;
-        font-weight: 600;
-
-        &.no-set {
-          color: #999;
-        }
+.item-actions {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  white-space: nowrap;
 
 
-        &.is-set {
-          color: #52c41a;
-        }
-      }
-    }
+  .action-btn {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    width: auto;
+    min-width: 88upx;
+    height: auto;
+    min-height: 56upx;
+    padding: 8upx 18upx;
+    margin: 0;
+    background: #fff;
+    border: 2upx solid #d9d9d9;
+    border-radius: 28upx;
+    box-sizing: border-box;
+    line-height: 1.3;
+    font-size: 26upx;
+    font-weight: 600;
+  }
 
 
-    .item-actions {
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      font-size: 32upx;
-      white-space: nowrap;
-
-      .action-btn {
-        display: inline-flex;
-        align-items: center;
-        justify-content: center;
-        width: auto;
-        min-width: 72upx;
-        height: auto;
-        min-height: 72upx;
-        padding: 12upx 10upx;
-        margin: 0;
-        background: transparent;
-        border: none;
-        line-height: 1.4;
-        font-size: 32upx;
-        font-weight: 600;
-      }
+  .action-btn::after {
+    border: none;
+  }
 
 
-      .action-btn::after {
-        border: none;
-      }
+  .action-btn + .action-btn {
+    margin-left: 12upx;
+  }
 
 
-      .edit-btn {
-        color: #52c41a;
-      }
-      .action-divider {
-        color: #eee;
-        margin: 0 8upx;
-      }
-      .delete-btn {
-        color: #FF3B30;
-      }
-    }
+  .edit-btn {
+    color: #09c567;
+    border-color: #09c567;
+    background: #f0fbf4;
+  }
+
+  .delete-btn {
+    color: #ff3b30;
+    border-color: #ffb4af;
+    background: #fff5f5;
   }
   }
 }
 }
 
 

+ 2 - 1
hdApp/src/admin/home/apply.vue

@@ -56,7 +56,8 @@ export default {
             { name: '商品', icon: 'bouquet', url: '/admin/goods/manage', pf: 1 },
             { name: '商品', icon: 'bouquet', url: '/admin/goods/manage', pf: 1 },
             { name: '新增商品', icon: 'add-bouquet', url: '/admin/goods/add', pf: 1 },
             { name: '新增商品', icon: 'add-bouquet', url: '/admin/goods/add', pf: 1 },
             { name: '分类', icon: 'category-filled', url: '/admin/goods/categoryV2?tab=0', pf: 1 },
             { name: '分类', icon: 'category-filled', url: '/admin/goods/categoryV2?tab=0', pf: 1 },
-            { name: '分类广告', icon: 'pic-text', url: '/admin/goods/category-ad', pf: 1 },
+            // 分类广告并入分类页第三 Tab,不再进独立页
+            { name: '分类广告', icon: 'pic-text', url: '/admin/goods/categoryV2?tab=2', pf: 1 },
             { name: '排序', icon: 'sort-filled', url: '/admin/goods/categorySort', pf: 1 },
             { name: '排序', icon: 'sort-filled', url: '/admin/goods/categorySort', pf: 1 },
             { name: '场景管理', icon: 'category-filled', url: '/admin/useCase/list', pf: 1 },
             { name: '场景管理', icon: 'category-filled', url: '/admin/useCase/list', pf: 1 },
             { name: '涨价管理', icon: 'price-increase', url: '/admin/goods/price-increase', pf: 1 }
             { name: '涨价管理', icon: 'price-increase', url: '/admin/goods/price-increase', pf: 1 }

+ 2 - 3
hdApp/src/admin/home/components/mall-home-panel.vue

@@ -223,13 +223,13 @@ export default {
         { name: '买花记录', icon: 'purchase-record', url: '/pagesPurchase/shopping', pf: 1 },
         { name: '买花记录', icon: 'purchase-record', url: '/pagesPurchase/shopping', pf: 1 },
         { name: '买花', icon: 'buy-item', url: '/pagesPurchase/order', pf: 1 }
         { name: '买花', icon: 'buy-item', url: '/pagesPurchase/order', pf: 1 }
       ],
       ],
-      // 业务菜单栏(写死)
+      // 业务菜单:分类广告已并入分类页第三 Tab,原广告位给红包管理
       bizMenuList: [
       bizMenuList: [
         { name: '商城码', icon: 'mall-filled', url: '/admin/cg/item', pf: 1 },
         { name: '商城码', icon: 'mall-filled', url: '/admin/cg/item', pf: 1 },
         { name: '商品', icon: 'bouquet', url: '/admin/goods/manage', pf: 1 },
         { name: '商品', icon: 'bouquet', url: '/admin/goods/manage', pf: 1 },
         { name: '新增商品', icon: 'add-bouquet', url: '/admin/goods/add', pf: 1 },
         { name: '新增商品', icon: 'add-bouquet', url: '/admin/goods/add', pf: 1 },
         { name: '分类', icon: 'category-filled', url: '/admin/goods/categoryV2?tab=0', pf: 1 },
         { name: '分类', icon: 'category-filled', url: '/admin/goods/categoryV2?tab=0', pf: 1 },
-        { name: '分类广告', icon: 'pic-text', url: '/admin/goods/category-ad', pf: 1 },
+        { name: '红包管理', icon: 'red-packet', url: '/admin/hb/list', pf: 1 },
         { name: '排序', icon: 'sort-filled', url: '/admin/goods/categorySort', pf: 1 },
         { name: '排序', icon: 'sort-filled', url: '/admin/goods/categorySort', pf: 1 },
         { name: '涨价管理', icon: 'price-increase', url: '/admin/goods/price-increase', pf: 1 },
         { name: '涨价管理', icon: 'price-increase', url: '/admin/goods/price-increase', pf: 1 },
         { name: '首页配置', icon: 'settings-filled', url: '/admin/homePageConfig/index', pf: 1 },
         { name: '首页配置', icon: 'settings-filled', url: '/admin/homePageConfig/index', pf: 1 },
@@ -248,7 +248,6 @@ export default {
         { name: '利润表', icon: 'profit-filled', url: '/admin/stat/profit', pf: 1 },
         { name: '利润表', icon: 'profit-filled', url: '/admin/stat/profit', pf: 1 },
         { name: '图文', icon: 'pic-text', url: '/admin/picText/picTextList', pf: 1 },
         { name: '图文', icon: 'pic-text', url: '/admin/picText/picTextList', pf: 1 },
         { name: '收款码流水', icon: 'pay-code-flow', url: '/admin/order/scanPay', pf: 1 },
         { name: '收款码流水', icon: 'pay-code-flow', url: '/admin/order/scanPay', pf: 1 },
-        { name: '红包管理', icon: 'red-packet', url: '/admin/hb/list', pf: 1 },
         { name: '充值设置', icon: 'recharge', url: '/admin/recharge/rechargeSetting', pf: 1 },
         { name: '充值设置', icon: 'recharge', url: '/admin/recharge/rechargeSetting', pf: 1 },
         { name: '公告', icon: 'notice', url: '/admin/shopNotice/list', pf: 1 }
         { name: '公告', icon: 'notice', url: '/admin/shopNotice/list', pf: 1 }
       ],
       ],