xw 1 deň pred
rodič
commit
1b7e87d0d3

+ 26 - 8
src/views/his/user/index.vue

@@ -241,17 +241,25 @@
             />
           </el-select>
         </el-form-item>
-        <el-form-item label="用户备注" prop="remark" required>
-          <el-input v-model="form.remark" placeholder="请输入用户备注" type="textarea"/>
-        </el-form-item>
-        <el-form-item label="是否拉黑" prop="manageStatus">
-          <el-select v-model="form.manageStatus" placeholder="请选择管理状态" clearable size="small">
+        <el-form-item label="是否设置拉黑提醒" prop="manageStatus">
+          <el-select
+            v-model="form.manageStatus"
+            placeholder="请选择管理状态"
+            clearable
+            size="small"
+            popper-append-to-body
+            popper-class="user-manage-status-select-popper"
+            :popper-options="{ placement: 'top-start', modifiers: { flip: { enabled: false } } }"
+          >
             <el-option label="否" :value="1" />
             <el-option label="是" :value="2" />
           </el-select>
         </el-form-item>
-        <el-form-item label="拉黑原因" prop="manageRemark">
-          <el-input v-model="form.manageRemark" placeholder="请输入拉黑原因" type="textarea"/>
+        <el-form-item v-if="form.manageStatus === 2" label="拉黑提醒原因" prop="manageRemark">
+          <el-input v-model="form.manageRemark" placeholder="请输入拉黑提醒原因" type="textarea"/>
+        </el-form-item>
+        <el-form-item label="用户备注" prop="remark" required>
+          <el-input v-model="form.remark" placeholder="请输入用户备注" type="textarea"/>
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -416,6 +424,10 @@ export default {
 
   },
   methods: {
+    normalizeManageStatus(value) {
+      const status = Number(value)
+      return status === 2 ? 2 : 1
+    },
     /** 销售选择变化 */
     handleCompanyUserChange(userId) {
       if (!this.changeCompanyUserForm.companyId) {
@@ -517,7 +529,7 @@ export default {
         this.form.status = String(this.form.status)
         this.form.level = String(this.form.level)
         this.form.isPromoter = String(this.form.isPromoter)
-        this.form.manageStatus = this.form.manageStatus != null ? Number(this.form.manageStatus) : 1
+        this.form.manageStatus = this.normalizeManageStatus(this.form.manageStatus)
 
       });
     },
@@ -604,3 +616,9 @@ export default {
   }
 };
 </script>
+
+<style>
+.user-manage-status-select-popper {
+  z-index: 9999 !important;
+}
+</style>

+ 255 - 62
src/views/hisStore/storeProduct/index.vue

@@ -969,6 +969,72 @@
         <el-form-item label="处方名" v-if="form.productType==2" prop="prescribeName">
           <el-input v-model="form.prescribeName" placeholder="请输入处方名" />
         </el-form-item>
+        <el-form-item label="用户端分类(多选)" prop="userEndCategoryIds">
+          <div class="quick-select-row">
+            <el-button type="text" size="small" @click="selectAllUserEndCategoryByPosition(1)">全选金刚区</el-button>
+            <el-button type="text" size="small" @click="selectAllUserEndCategoryByPosition(2)">全选瀑布流</el-button>
+          </div>
+          <div class="scroll-select-wrap">
+            <div
+              class="scroll-box scroll-box-category"
+              ref="userEndCategoryScroll"
+              @scroll="onUserEndCategoryScroll"
+              style="overflow-y: auto; max-height: 200px;"
+            >
+              <el-checkbox-group v-model="form.userEndCategoryIds" :key="'uec-' + (userEndCategoryList.length ? 'ready' : 'empty')">
+                <div class="scroll-options">
+                  <el-checkbox
+                    v-for="c in userEndCategoryList"
+                    :key="'c'+c.id"
+                    :label="Number(c.id)"
+                    border
+                    size="small"
+                    class="scroll-option-item"
+                  >
+                    {{ c.categoryName }}({{ c.position === 1 ? '金刚区' : '瀑布流' }})
+                  </el-checkbox>
+                </div>
+              </el-checkbox-group>
+              <div v-if="userEndCategoryLoading" class="scroll-loading">加载中...</div>
+              <div v-else-if="userEndCategoryList.length >= userEndCategoryTotal && userEndCategoryTotal > 0" class="scroll-end">已加载全部</div>
+              <div v-else-if="userEndCategoryList.length > 0 && userEndCategoryList.length < userEndCategoryTotal" class="scroll-more" @click="loadUserEndCategoryPage(true)">
+                <span>点击加载更多</span>
+              </div>
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item label="商品标签(最多3个)" prop="tagIds">
+          <div class="scroll-select-wrap">
+            <div
+              class="scroll-box scroll-box-tag"
+              ref="tagScroll"
+              @scroll="onTagScroll"
+              style="overflow-y: auto; max-height: 200px;"
+            >
+              <el-checkbox-group v-model="form.tagIds" @change="onTagIdsChange" :key="'tag-' + (tagList.length ? 'ready' : 'empty')">
+                <div class="scroll-options">
+                  <el-checkbox
+                    v-for="t in tagList"
+                    :key="'t'+t.id"
+                    :label="Number(t.id)"
+                    :disabled="isTagDisabled(Number(t.id))"
+                    border
+                    size="small"
+                    class="scroll-option-item"
+                  >
+                    {{ t.tagName }}
+                  </el-checkbox>
+                </div>
+              </el-checkbox-group>
+              <div v-if="tagLoading" class="scroll-loading">加载中...</div>
+              <div v-else-if="tagList.length >= tagTotal && tagTotal > 0" class="scroll-end">已加载全部</div>
+              <div v-else-if="tagList.length > 0 && tagList.length < tagTotal" class="scroll-more" @click="loadTagPage(true)">
+                <span>点击加载更多</span>
+              </div>
+            </div>
+          </div>
+          <span v-if="form.tagIds && form.tagIds.length > 3" class="text-danger">最多选择3个</span>
+        </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitForm">确 定</el-button>
@@ -1008,6 +1074,8 @@
 </template>
 
 <script>
+import { listUserEndCategory } from "@/api/hisStore/userEndCategory";
+import { listStoreProductTag } from "@/api/hisStore/storeProductTag";
 import {
   genFormatAttr,
   listStoreProduct,
@@ -1207,6 +1275,14 @@ export default {
       },
       // 表单参数
       form: {},
+      userEndCategoryList: [],
+      userEndCategoryPageNum: 1,
+      userEndCategoryTotal: 0,
+      userEndCategoryLoading: false,
+      tagList: [],
+      tagPageNum: 1,
+      tagTotal: 0,
+      tagLoading: false,
       storeForm: {isAudit:1,status:1},
       // 表单校验
       rules: {
@@ -1630,6 +1706,100 @@ export default {
         this.loading = false;
       });
     },
+    loadUserEndCategoryForProduct(storeId) {
+      this.userEndCategoryPageNum = 1;
+      this.userEndCategoryList = [];
+      this.userEndCategoryTotal = 0;
+      return this.loadUserEndCategoryPage(false);
+    },
+    loadUserEndCategoryPage(append) {
+      if (this.userEndCategoryLoading) return Promise.resolve();
+      if (append && this.userEndCategoryList.length >= this.userEndCategoryTotal && this.userEndCategoryTotal > 0) return Promise.resolve();
+      this.userEndCategoryLoading = true;
+      const pageNum = append ? this.userEndCategoryPageNum : 1;
+      return listUserEndCategory({ pageNum, pageSize: 10, status: 1 }).then(response => {
+        const rows = response.rows || [];
+        this.userEndCategoryTotal = response.total || 0;
+        if (append) {
+          this.userEndCategoryList = this.userEndCategoryList.concat(rows);
+          this.userEndCategoryPageNum = pageNum + 1;
+        } else {
+          this.userEndCategoryList = rows;
+          this.userEndCategoryPageNum = 2;
+        }
+      }).finally(() => {
+        this.userEndCategoryLoading = false;
+      });
+    },
+    onUserEndCategoryScroll(e) {
+      const el = e && e.target;
+      if (!el) return;
+      if (this.$refs.userEndCategoryScroll && el !== this.$refs.userEndCategoryScroll) return;
+      const scrollTop = el.scrollTop;
+      const scrollHeight = el.scrollHeight;
+      const clientHeight = el.clientHeight;
+      if (clientHeight + scrollTop >= scrollHeight - 10) {
+        this.loadUserEndCategoryPage(true);
+      }
+    },
+    selectAllUserEndCategoryByPosition(position) {
+      const list = this.userEndCategoryList || [];
+      const idsOfPosition = list.filter(c => c.position === position).map(c => Number(c.id)).filter(id => id !== 0 && !Number.isNaN(id));
+      const current = this.form.userEndCategoryIds || [];
+      const otherPositionIds = current.filter(id => {
+        const c = list.find(x => Number(x.id) === Number(id));
+        return c ? c.position !== position : true;
+      });
+      this.$set(this.form, 'userEndCategoryIds', [...new Set([...otherPositionIds, ...idsOfPosition])]);
+    },
+    loadTagPage(append) {
+      if (this.tagLoading) return Promise.resolve();
+      if (append && this.tagList.length >= this.tagTotal && this.tagTotal > 0) return Promise.resolve();
+      this.tagLoading = true;
+      const pageNum = append ? this.tagPageNum : 1;
+      return listStoreProductTag({ pageNum, pageSize: 10, status: 1 }).then(response => {
+        const rows = response.rows || [];
+        this.tagTotal = response.total || 0;
+        if (append) {
+          this.tagList = this.tagList.concat(rows);
+          this.tagPageNum = pageNum + 1;
+        } else {
+          this.tagList = rows;
+          this.tagPageNum = 2;
+        }
+      }).finally(() => {
+        this.tagLoading = false;
+      });
+    },
+    onTagScroll(e) {
+      const el = e && e.target;
+      if (!el) return;
+      if (this.$refs.tagScroll && el !== this.$refs.tagScroll) return;
+      const scrollTop = el.scrollTop;
+      const scrollHeight = el.scrollHeight;
+      const clientHeight = el.clientHeight;
+      if (clientHeight + scrollTop >= scrollHeight - 10) {
+        this.loadTagPage(true);
+      }
+    },
+    onTagIdsChange(val) {
+      if (val && val.length > 3) {
+        this.$message.warning('商品标签最多选择3个');
+        this.$set(this.form, 'tagIds', val.slice(0, 3));
+      }
+    },
+    isTagDisabled(tagId) {
+      const ids = this.form.tagIds || [];
+      const n = Number(tagId);
+      if (ids.some(id => Number(id) === n)) return false;
+      return ids.length >= 3;
+    },
+    loadTagOptions() {
+      this.tagPageNum = 1;
+      this.tagList = [];
+      this.tagTotal = 0;
+      return this.loadTagPage(false);
+    },
     // 取消按钮
     cancel() {
       this.open = false;
@@ -1701,7 +1871,9 @@ export default {
         adverseReactions: null, // 不良反应
         contraindications: null, // 禁忌
         precautions: null, // 注意事项
-        purchaseLimit: null // 限购数量
+        purchaseLimit: null, // 限购数量
+        userEndCategoryIds: [],
+        tagIds: []
       };
       // 重置药品展示图
       this.drugImageArr = [];
@@ -1755,13 +1927,16 @@ export default {
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
-
-      this.open = true;
-      this.title = "添加商品";
-      setTimeout(() => {
-        this.$refs.myeditor.setText("");
-      }, 500);
-
+      Promise.all([
+        this.loadTagOptions(),
+        this.loadUserEndCategoryForProduct(this.form.storeId)
+      ]).then(() => {
+        this.open = true;
+        this.title = "添加商品";
+        this.$nextTick(() => {
+          if (this.$refs.myeditor) this.$refs.myeditor.setText("");
+        });
+      });
     },
     /** 修改按钮操作 */
     handleUpdate(row) {
@@ -1810,63 +1985,67 @@ export default {
         }
 
         this.appIds = this.form.appIds ? this.form.appIds.split(',') : [];
-        setTimeout(() => {
-          that.generate();
-        }, 200);
-        if(this.form.specType === 0){
-          that.manyFormValidate = [];
-        }else {
-          that.createBnt = true;
-          that.oneFormValidate = [
-            {
-              image: '',
-              hasDepositFeature:false,
-              hasDepositFeatureType: 0,//开启类型
-              depositAmount:0,//定金金额
-              onBehalfPaymentAmount:0,//代付金额
-              price: 0,
-              agentPrice: 0,
-              cost: 0,
-              otPrice: 0,
-              stock: 0,
-              purchaseLimit: 0,
-              barCode: '',
-              weight: 0,
-              volume: 0,
-              integral: 0,
-              brokerage:0,
-              brokerageTwo:0,
-              brokerageThree:0
-            }
-          ]
-        }
-        setTimeout(() => {
-          if(this.form.description==null){
-            this.$refs.myeditor.setText("");
-          }
-          else{
-            this.$refs.myeditor.setText(this.form.description);
+        this.$set(this.form, 'userEndCategoryIds', (response.userEndCategoryIds || []).map(id => Number(id)).filter(id => !Number.isNaN(id)));
+        this.$set(this.form, 'tagIds', (response.tagIds || []).map(id => Number(id)).filter(id => !Number.isNaN(id)));
+        Promise.all([
+          this.loadTagOptions(),
+          this.loadUserEndCategoryForProduct(this.form.storeId)
+        ]).then(() => {
+          setTimeout(() => {
+            that.generate();
+          }, 200);
+          if(this.form.specType === 0){
+            that.manyFormValidate = [];
+          }else {
+            that.createBnt = true;
+            that.oneFormValidate = [
+              {
+                image: '',
+                hasDepositFeature:false,
+                hasDepositFeatureType: 0,
+                depositAmount:0,
+                onBehalfPaymentAmount:0,
+                price: 0,
+                agentPrice: 0,
+                cost: 0,
+                otPrice: 0,
+                stock: 0,
+                purchaseLimit: 0,
+                barCode: '',
+                weight: 0,
+                volume: 0,
+                integral: 0,
+                brokerage:0,
+                brokerageTwo:0,
+                brokerageThree:0
+              }
+            ]
           }
-        }, 200);
-        if(this.form.image!=null && this.form.image.trim()){
-          this.imageArr=this.form.image.split(",");
-          // 验证现有的商品图片
-          const productImages = this.imageArr.filter(img => img && img.trim());
-          if (productImages.length > 0) {
-            this.validateImage(productImages[productImages.length - 1], 'product');
+          setTimeout(() => {
+            if(this.form.description==null){
+              this.$refs.myeditor.setText("");
+            }
+            else{
+              this.$refs.myeditor.setText(this.form.description);
+            }
+          }, 200);
+          if(this.form.image!=null && this.form.image.trim()){
+            this.imageArr=this.form.image.split(",");
+            const productImages = this.imageArr.filter(img => img && img.trim());
+            if (productImages.length > 0) {
+              this.validateImage(productImages[productImages.length - 1], 'product');
+            }
           }
-        }
-        if(this.form.sliderImage!=null && this.form.sliderImage.trim()){
-          this.photoArr=this.form.sliderImage.split(",");
-          // 验证现有的轮播图片
-          const carouselImages = this.photoArr.filter(img => img && img.trim());
-          if (carouselImages.length > 0) {
-            this.validateImage(carouselImages[carouselImages.length - 1], 'carousel');
+          if(this.form.sliderImage!=null && this.form.sliderImage.trim()){
+            this.photoArr=this.form.sliderImage.split(",");
+            const carouselImages = this.photoArr.filter(img => img && img.trim());
+            if (carouselImages.length > 0) {
+              this.validateImage(carouselImages[carouselImages.length - 1], 'carousel');
+            }
           }
-        }
-        console.log(this.oneFormValidate)
-        this.open = true;
-        this.title = "修改商品";
+          this.open = true;
+          this.title = "修改商品";
+        });
       });
     },
     /** 提交按钮 */
@@ -1879,6 +2058,9 @@ export default {
     },
     // 执行提交操作
     doSubmit() {
+      if (this.form.tagIds && this.form.tagIds.length > 3) {
+        return this.$message.warning('商品标签最多选择3个');
+      }
       if(this.form.specType ===0 ){
         this.form.items = [];
         this.form.values = this.oneFormValidate;
@@ -2055,4 +2237,15 @@ export default {
     transform: translateY(0);
   }
 }
+
+.text-danger { color: #F56C6C; font-size: 12px; }
+.scroll-select-wrap { width: 100%; }
+.quick-select-row { margin-bottom: 6px; }
+.quick-select-row .el-button { padding: 0 8px; }
+.scroll-box { max-height: 200px; overflow-y: auto; border: 1px solid #DCDFE6; border-radius: 4px; padding: 8px; background: #FAFAFA; }
+.scroll-options { display: flex; flex-wrap: wrap; gap: 8px; }
+.scroll-option-item { margin-right: 0; }
+.scroll-loading, .scroll-end { text-align: center; color: #909399; font-size: 12px; padding: 8px 0; }
+.scroll-more { text-align: center; padding: 8px 0; cursor: pointer; font-size: 12px; color: #409EFF; }
+.scroll-more:hover { text-decoration: underline; }
 </style>

+ 218 - 18
src/views/hisStore/storeProduct/indexZm.vue

@@ -934,6 +934,72 @@
         <el-form-item label="处方名" v-if="form.productType==2" prop="prescribeName">
           <el-input v-model="form.prescribeName" placeholder="请输入处方名" />
         </el-form-item>
+        <el-form-item label="用户端分类(多选)" prop="userEndCategoryIds">
+          <div class="quick-select-row">
+            <el-button type="text" size="small" @click="selectAllUserEndCategoryByPosition(1)">全选金刚区</el-button>
+            <el-button type="text" size="small" @click="selectAllUserEndCategoryByPosition(2)">全选瀑布流</el-button>
+          </div>
+          <div class="scroll-select-wrap">
+            <div
+              class="scroll-box scroll-box-category"
+              ref="userEndCategoryScroll"
+              @scroll="onUserEndCategoryScroll"
+              style="overflow-y: auto; max-height: 200px;"
+            >
+              <el-checkbox-group v-model="form.userEndCategoryIds" :key="'uec-' + (userEndCategoryList.length ? 'ready' : 'empty')">
+                <div class="scroll-options">
+                  <el-checkbox
+                    v-for="c in userEndCategoryList"
+                    :key="'c'+c.id"
+                    :label="Number(c.id)"
+                    border
+                    size="small"
+                    class="scroll-option-item"
+                  >
+                    {{ c.categoryName }}({{ c.position === 1 ? '金刚区' : '瀑布流' }})
+                  </el-checkbox>
+                </div>
+              </el-checkbox-group>
+              <div v-if="userEndCategoryLoading" class="scroll-loading">加载中...</div>
+              <div v-else-if="userEndCategoryList.length >= userEndCategoryTotal && userEndCategoryTotal > 0" class="scroll-end">已加载全部</div>
+              <div v-else-if="userEndCategoryList.length > 0 && userEndCategoryList.length < userEndCategoryTotal" class="scroll-more" @click="loadUserEndCategoryPage(true)">
+                <span>点击加载更多</span>
+              </div>
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item label="商品标签(最多3个)" prop="tagIds">
+          <div class="scroll-select-wrap">
+            <div
+              class="scroll-box scroll-box-tag"
+              ref="tagScroll"
+              @scroll="onTagScroll"
+              style="overflow-y: auto; max-height: 200px;"
+            >
+              <el-checkbox-group v-model="form.tagIds" @change="onTagIdsChange" :key="'tag-' + (tagList.length ? 'ready' : 'empty')">
+                <div class="scroll-options">
+                  <el-checkbox
+                    v-for="t in tagList"
+                    :key="'t'+t.id"
+                    :label="Number(t.id)"
+                    :disabled="isTagDisabled(Number(t.id))"
+                    border
+                    size="small"
+                    class="scroll-option-item"
+                  >
+                    {{ t.tagName }}
+                  </el-checkbox>
+                </div>
+              </el-checkbox-group>
+              <div v-if="tagLoading" class="scroll-loading">加载中...</div>
+              <div v-else-if="tagList.length >= tagTotal && tagTotal > 0" class="scroll-end">已加载全部</div>
+              <div v-else-if="tagList.length > 0 && tagList.length < tagTotal" class="scroll-more" @click="loadTagPage(true)">
+                <span>点击加载更多</span>
+              </div>
+            </div>
+          </div>
+          <span v-if="form.tagIds && form.tagIds.length > 3" class="text-danger">最多选择3个</span>
+        </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitForm">确 定</el-button>
@@ -973,6 +1039,8 @@
 </template>
 
 <script>
+import { listUserEndCategory } from "@/api/hisStore/userEndCategory";
+import { listStoreProductTag } from "@/api/hisStore/storeProductTag";
 import {
   genFormatAttr,
   listStoreProduct,
@@ -1145,6 +1213,14 @@ export default {
       },
       // 表单参数
       form: {},
+      userEndCategoryList: [],
+      userEndCategoryPageNum: 1,
+      userEndCategoryTotal: 0,
+      userEndCategoryLoading: false,
+      tagList: [],
+      tagPageNum: 1,
+      tagTotal: 0,
+      tagLoading: false,
       storeForm: {isAudit:1,status:1},
       // 表单校验
       rules: {
@@ -1532,6 +1608,100 @@ export default {
         this.loading = false;
       });
     },
+    loadUserEndCategoryForProduct(storeId) {
+      this.userEndCategoryPageNum = 1;
+      this.userEndCategoryList = [];
+      this.userEndCategoryTotal = 0;
+      return this.loadUserEndCategoryPage(false);
+    },
+    loadUserEndCategoryPage(append) {
+      if (this.userEndCategoryLoading) return Promise.resolve();
+      if (append && this.userEndCategoryList.length >= this.userEndCategoryTotal && this.userEndCategoryTotal > 0) return Promise.resolve();
+      this.userEndCategoryLoading = true;
+      const pageNum = append ? this.userEndCategoryPageNum : 1;
+      return listUserEndCategory({ pageNum, pageSize: 10, status: 1 }).then(response => {
+        const rows = response.rows || [];
+        this.userEndCategoryTotal = response.total || 0;
+        if (append) {
+          this.userEndCategoryList = this.userEndCategoryList.concat(rows);
+          this.userEndCategoryPageNum = pageNum + 1;
+        } else {
+          this.userEndCategoryList = rows;
+          this.userEndCategoryPageNum = 2;
+        }
+      }).finally(() => {
+        this.userEndCategoryLoading = false;
+      });
+    },
+    onUserEndCategoryScroll(e) {
+      const el = e && e.target;
+      if (!el) return;
+      if (this.$refs.userEndCategoryScroll && el !== this.$refs.userEndCategoryScroll) return;
+      const scrollTop = el.scrollTop;
+      const scrollHeight = el.scrollHeight;
+      const clientHeight = el.clientHeight;
+      if (clientHeight + scrollTop >= scrollHeight - 10) {
+        this.loadUserEndCategoryPage(true);
+      }
+    },
+    selectAllUserEndCategoryByPosition(position) {
+      const list = this.userEndCategoryList || [];
+      const idsOfPosition = list.filter(c => c.position === position).map(c => Number(c.id)).filter(id => id !== 0 && !Number.isNaN(id));
+      const current = this.form.userEndCategoryIds || [];
+      const otherPositionIds = current.filter(id => {
+        const c = list.find(x => Number(x.id) === Number(id));
+        return c ? c.position !== position : true;
+      });
+      this.$set(this.form, 'userEndCategoryIds', [...new Set([...otherPositionIds, ...idsOfPosition])]);
+    },
+    loadTagPage(append) {
+      if (this.tagLoading) return Promise.resolve();
+      if (append && this.tagList.length >= this.tagTotal && this.tagTotal > 0) return Promise.resolve();
+      this.tagLoading = true;
+      const pageNum = append ? this.tagPageNum : 1;
+      return listStoreProductTag({ pageNum, pageSize: 10, status: 1 }).then(response => {
+        const rows = response.rows || [];
+        this.tagTotal = response.total || 0;
+        if (append) {
+          this.tagList = this.tagList.concat(rows);
+          this.tagPageNum = pageNum + 1;
+        } else {
+          this.tagList = rows;
+          this.tagPageNum = 2;
+        }
+      }).finally(() => {
+        this.tagLoading = false;
+      });
+    },
+    onTagScroll(e) {
+      const el = e && e.target;
+      if (!el) return;
+      if (this.$refs.tagScroll && el !== this.$refs.tagScroll) return;
+      const scrollTop = el.scrollTop;
+      const scrollHeight = el.scrollHeight;
+      const clientHeight = el.clientHeight;
+      if (clientHeight + scrollTop >= scrollHeight - 10) {
+        this.loadTagPage(true);
+      }
+    },
+    onTagIdsChange(val) {
+      if (val && val.length > 3) {
+        this.$message.warning('商品标签最多选择3个');
+        this.$set(this.form, 'tagIds', val.slice(0, 3));
+      }
+    },
+    isTagDisabled(tagId) {
+      const ids = this.form.tagIds || [];
+      const n = Number(tagId);
+      if (ids.some(id => Number(id) === n)) return false;
+      return ids.length >= 3;
+    },
+    loadTagOptions() {
+      this.tagPageNum = 1;
+      this.tagList = [];
+      this.tagTotal = 0;
+      return this.loadTagPage(false);
+    },
     // 取消按钮
     cancel() {
       this.open = false;
@@ -1606,7 +1776,9 @@ export default {
         adverseReactions: null, // 不良反应
         contraindications: null, // 禁忌
         precautions: null ,// 注意事项
-        purchaseLimit: null // 限购数量
+        purchaseLimit: null, // 限购数量
+        userEndCategoryIds: [],
+        tagIds: []
       };
       // 重置药品展示图
       this.drugImageArr = [];
@@ -1658,13 +1830,16 @@ export default {
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
-
-      this.open = true;
-      this.title = "添加商品";
-      setTimeout(() => {
-        this.$refs.myeditor.setText("");
-      }, 500);
-
+      Promise.all([
+        this.loadTagOptions(),
+        this.loadUserEndCategoryForProduct(this.form.storeId)
+      ]).then(() => {
+        this.open = true;
+        this.title = "添加商品";
+        this.$nextTick(() => {
+          if (this.$refs.myeditor) this.$refs.myeditor.setText("");
+        });
+      });
     },
     /** 修改按钮操作 */
     handleUpdate(row) {
@@ -1706,6 +1881,26 @@ export default {
         if (response.data.companyIds != null && response.data.companyIds != undefined && response.data.companyIds.length > 0) {
           this.form.companyIds = response.data.companyIds.split(',').map(Number);
         }
+        this.$set(this.form, 'userEndCategoryIds', (response.userEndCategoryIds || []).map(id => Number(id)).filter(id => !Number.isNaN(id)));
+        this.$set(this.form, 'tagIds', (response.tagIds || []).map(id => Number(id)).filter(id => !Number.isNaN(id)));
+        Promise.all([
+          this.loadTagOptions(),
+          this.loadUserEndCategoryForProduct(this.form.storeId)
+        ]).then(() => {
+          this.open = true;
+          this.title = "修改商品";
+          this.$nextTick(() => {
+            setTimeout(() => {
+              if (this.$refs.myeditor) {
+                if (this.form.description == null) {
+                  this.$refs.myeditor.setText("");
+                } else {
+                  this.$refs.myeditor.setText(this.form.description);
+                }
+              }
+            }, 200);
+          });
+        });
         setTimeout(() => {
           that.generate();
         }, 200);
@@ -1730,28 +1925,21 @@ export default {
             }
           ]
         }
-        setTimeout(() => {
-          if(this.form.description==null){
-            this.$refs.myeditor.setText("");
-          }
-          else{
-            this.$refs.myeditor.setText(this.form.description);
-          }
-        }, 200);
         if(this.form.image!=null){
           this.imageArr=this.form.image.split(",");
         }
         if(this.form.sliderImage!=null){
           this.photoArr=this.form.sliderImage.split(",");
         }
-        this.open = true;
-        this.title = "修改商品";
       });
     },
     /** 提交按钮 */
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
+          if (this.form.tagIds && this.form.tagIds.length > 3) {
+            return this.$message.warning('商品标签最多选择3个');
+          }
           if(this.form.specType ===0 ){
             this.form.items = [];
             this.form.values = this.oneFormValidate;
@@ -1851,3 +2039,15 @@ export default {
   }
 };
 </script>
+<style scoped>
+.text-danger { color: #F56C6C; font-size: 12px; }
+.scroll-select-wrap { width: 100%; }
+.quick-select-row { margin-bottom: 6px; }
+.quick-select-row .el-button { padding: 0 8px; }
+.scroll-box { max-height: 200px; overflow-y: auto; border: 1px solid #DCDFE6; border-radius: 4px; padding: 8px; background: #FAFAFA; }
+.scroll-options { display: flex; flex-wrap: wrap; gap: 8px; }
+.scroll-option-item { margin-right: 0; }
+.scroll-loading, .scroll-end { text-align: center; color: #909399; font-size: 12px; padding: 8px 0; }
+.scroll-more { text-align: center; padding: 8px 0; cursor: pointer; font-size: 12px; color: #409EFF; }
+.scroll-more:hover { text-decoration: underline; }
+</style>