Browse Source

商品排序功能

yuhongqi 2 weeks ago
parent
commit
f81de5243c

+ 10 - 0
src/api/hisStore/userEndCategory.js

@@ -56,3 +56,13 @@ export function listCategoryProducts(id, params) {
     params: { id, ...params }
   })
 }
+
+/** 批量保存关联排序(当前页商品 productId + sort,id 为用户分端类 ID) */
+export function saveCategoryProductsSort(id, items) {
+  return request({
+    url: '/store/store/userEndCategory/products/sort',
+    method: 'put',
+    params: { id },
+    data: items
+  })
+}

+ 89 - 1
src/views/course/userCoursePeriod/index.vue

@@ -163,7 +163,6 @@
         <el-table v-loading="loading" :data="periodList" @selection-change="handleSelectionChange" border>
           <el-table-column type="selection" width="55" align="center" />
           <el-table-column label="营期名称" align="center" prop="periodName" />
-          <el-table-column label="公司名称" align="center" prop="companyName" />
           <el-table-column label="营期状态" align="center" prop="periodStatus" width="100" :formatter="periodStatusFormatter" />
           <el-table-column label="营期线" align="center" prop="periodLine" width="180" />
           <el-table-column label="开营开始时间" align="center" prop="periodStartingTime" width="180" />
@@ -204,6 +203,12 @@
                 @click="handleClosePeriod(scope.row)"
                 v-hasPermi="['course:period:close']"
               >结束营期</el-button>
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-office-building"
+                @click="openPeriodCompanyCards(scope.row)"
+              >公司列表</el-button>
               <el-button
                 size="mini"
                 type="text"
@@ -225,6 +230,35 @@
       </el-main>
     </el-container>
 
+    <!-- 当前营期关联公司(按行 companyId / companyName 解析) -->
+    <el-dialog
+      :title="periodCompanyCardTitle"
+      :visible.sync="periodCompanyCardVisible"
+      width="720px"
+      append-to-body
+    >
+      <div class="period-company-card-scroll">
+        <el-row v-if="periodCompanyCardList && periodCompanyCardList.length > 0" :gutter="12">
+          <el-col
+            v-for="(item, idx) in periodCompanyCardList"
+            :key="item.companyId != null ? String(item.companyId) + '-' + idx : 'noid-' + idx"
+            :xs="24"
+            :sm="12"
+            :md="8"
+          >
+            <el-card shadow="hover" class="period-company-name-card">
+              <div class="period-company-name-card__title">{{ item.companyName || '—' }}</div>
+              <div v-if="item.companyId != null && item.companyId !== ''" class="period-company-name-card__id">ID:{{ item.companyId }}</div>
+            </el-card>
+          </el-col>
+        </el-row>
+        <el-empty v-else description="该营期未关联公司" />
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="periodCompanyCardVisible = false">关 闭</el-button>
+      </span>
+    </el-dialog>
+
     <!-- 添加或修改会员营期对话框-->
     <el-drawer :title="title" :visible.sync="open" size="700px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="120px">
@@ -904,6 +938,10 @@ export default {
       batchSetRedPacketDisabled: true,
       // 批量设置红包弹出框
       batchRedPacketVisible: false,
+      // 营期行 — 关联公司卡片弹窗
+      periodCompanyCardVisible: false,
+      periodCompanyCardTitle: '公司列表',
+      periodCompanyCardList: [],
     };
   },
   created() {
@@ -1714,6 +1752,33 @@ export default {
         })
       }).catch(() => {})
     },
+    /** 当前营期行关联公司:解析 companyId、companyName(逗号分隔),卡片展示 */
+    openPeriodCompanyCards(row) {
+      const pname = row.periodName ? String(row.periodName) : ''
+      this.periodCompanyCardTitle = pname ? `公司列表 - ${pname}` : '公司列表'
+      const idStr = row.companyId != null && row.companyId !== '' ? String(row.companyId) : ''
+      const nameStr = row.companyName != null && row.companyName !== '' ? String(row.companyName) : ''
+      const ids = idStr ? idStr.split(',').map(s => s.trim()).filter(s => s !== '') : []
+      const names = nameStr ? nameStr.split(',').map(s => s.trim()) : []
+      const list = []
+      ids.forEach((id, i) => {
+        let companyName = names[i]
+        if (!companyName) {
+          const opt = (this.companyOptions || []).find(c => String(c.companyId) === String(id))
+          companyName = opt ? opt.companyName : ''
+        }
+        const num = Number(id)
+        list.push({
+          companyId: !isNaN(num) ? num : id,
+          companyName: companyName || '—'
+        })
+      })
+      if (list.length === 0 && names.length > 0) {
+        names.forEach(n => list.push({ companyId: null, companyName: n || '—' }))
+      }
+      this.periodCompanyCardList = list
+      this.periodCompanyCardVisible = true
+    },
     handleBatchRedPacketSuccess() {
       this.batchRedPacketVisible = false;
       this.getCourseList();
@@ -2108,4 +2173,27 @@ export default {
   display: flex;
   align-items: center;
 }
+
+.period-company-card-scroll {
+  max-height: 65vh;
+  overflow-y: auto;
+  padding-right: 4px;
+}
+
+.period-company-name-card {
+  margin-bottom: 12px;
+}
+
+.period-company-name-card__title {
+  font-weight: 600;
+  font-size: 14px;
+  line-height: 1.4;
+  word-break: break-all;
+}
+
+.period-company-name-card__id {
+  margin-top: 8px;
+  font-size: 12px;
+  color: #909399;
+}
 </style>

+ 12 - 2
src/views/hisStore/storeProduct/index.vue

@@ -763,8 +763,18 @@
           <el-col :span="24">
             <el-form-item label="运费模板:" prop="tempId">
               <div class="acea-row">
-                <el-select v-model="form.tempId"  class="mr20">
-                  <el-option v-for="(item,index) in templateList" :value="item.id" :key="index" :label="item.name">
+                <el-select
+                  v-model="form.tempId"
+                  class="mr20"
+                  filterable
+                  placeholder="可以输入文字进行过滤匹配"
+                >
+                  <el-option
+                    v-for="(item, index) in templateList"
+                    :value="item.id"
+                    :key="index"
+                    :label="item.name"
+                  >
                   </el-option>
                 </el-select>
               </div>

+ 13 - 3
src/views/hisStore/storeProduct/indexZm.vue

@@ -791,9 +791,19 @@
           </el-col>
           <el-col :span="24">
             <el-form-item label="运费模板:" prop="tempId">
-              <div class="acea-row">
-                <el-select v-model="form.tempId"  class="mr20">
-                  <el-option v-for="(item,index) in templateList" :value="item.id" :key="index" :label="item.name">
+             <div class="acea-row">
+                <el-select
+                  v-model="form.tempId"
+                  class="mr20"
+                  filterable
+                  placeholder="可以输入文字进行过滤匹配"
+                >
+                  <el-option
+                    v-for="(item, index) in templateList"
+                    :value="item.id"
+                    :key="index"
+                    :label="item.name"
+                  >
                   </el-option>
                 </el-select>
               </div>

+ 81 - 7
src/views/hisStore/userEndCategory/index.vue

@@ -50,7 +50,7 @@
           <el-tag v-else type="info">隐藏</el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="排序" align="center" prop="sort" width="80" />
+      <el-table-column label="分类排序" align="center" prop="sort" width="80" />
       <el-table-column label="关联商品" align="center" width="120">
         <template slot-scope="scope">
           <el-button type="text" @click="showCategoryProducts(scope.row)">查看</el-button>
@@ -97,11 +97,26 @@
     </el-dialog>
 
     <!-- 关联商品弹窗 -->
-    <el-dialog title="关联商品" :visible.sync="productsOpen" width="960px" append-to-body>
-      <div class="mb8">关联商品数量: {{ productsTotal }}个</div>
+    <el-dialog title="关联商品" :visible.sync="productsOpen" width="1000px" append-to-body @closed="onProductsDialogClosed">
+      <div class="products-toolbar mb8">
+        <el-button
+          v-hasPermi="['store:userEndCategory:edit']"
+          size="small"
+          :type="productsSortEditMode ? 'warning' : 'default'"
+          @click="toggleProductsSortEdit"
+        >{{ productsSortEditMode ? '取消调整' : '调整排序' }}</el-button>
+        <el-button
+          v-hasPermi="['store:userEndCategory:edit']"
+          type="primary"
+          size="small"
+          :disabled="!productsSortEditMode"
+          @click="saveProductsSortAction"
+        >保存排序</el-button>
+        <span class="products-count">关联商品数量: {{ productsTotal }}个</span>
+      </div>
       <el-table v-loading="productsLoading" :data="productsList" border>
         <el-table-column label="商品ID" align="center" prop="productId" width="80" />
-        <el-table-column label="商品名称" align="center" prop="productName" min-width="140" show-overflow-tooltip />
+        <el-table-column label="商品名称" align="center" prop="productName" min-width="100" show-overflow-tooltip />
         <el-table-column label="售价" align="center" width="100">
           <template slot-scope="scope">
             <span v-if="scope.row.price != null">¥{{ scope.row.price.toFixed(2) }}</span>
@@ -123,6 +138,20 @@
             <span v-else>-</span>
           </template>
         </el-table-column>
+        <el-table-column label="商品排序" align="center" width="140">
+          <template slot-scope="scope">
+            <el-input-number
+              v-model="scope.row.sort"
+              :min="0"
+              :max="9999"
+              :step="1"
+              size="small"
+              controls-position="right"
+              :disabled="!productsSortEditMode"
+              class="product-sort-input"
+            />
+          </template>
+        </el-table-column>
       </el-table>
       <pagination v-show="productsTotal>0" :total="productsTotal" :page.sync="productsQuery.pageNum" :limit.sync="productsQuery.pageSize" @pagination="loadCategoryProducts" />
     </el-dialog>
@@ -130,7 +159,7 @@
 </template>
 
 <script>
-import { listUserEndCategory, getUserEndCategory, addUserEndCategory, updateUserEndCategory, delUserEndCategory, listCategoryProducts } from '@/api/hisStore/userEndCategory'
+import { listUserEndCategory, getUserEndCategory, addUserEndCategory, updateUserEndCategory, delUserEndCategory, listCategoryProducts, saveCategoryProductsSort } from '@/api/hisStore/userEndCategory'
 import Material from '@/components/Material'
 
 export default {
@@ -169,7 +198,8 @@ export default {
       productsList: [],
       productsTotal: 0,
       productsQuery: { pageNum: 1, pageSize: 10 },
-      currentCategoryId: null
+      currentCategoryId: null,
+      productsSortEditMode: false
     }
   },
   created() {
@@ -252,14 +282,44 @@ export default {
     showCategoryProducts(row) {
       this.currentCategoryId = row.id
       this.productsQuery = { pageNum: 1, pageSize: 10 }
+      this.productsSortEditMode = false
       this.productsOpen = true
       this.loadCategoryProducts()
     },
+    onProductsDialogClosed() {
+      this.productsSortEditMode = false
+    },
+    toggleProductsSortEdit() {
+      if (this.productsSortEditMode) {
+        this.productsSortEditMode = false
+        this.loadCategoryProducts()
+      } else {
+        this.productsSortEditMode = true
+      }
+    },
+    saveProductsSortAction() {
+      if (!this.productsSortEditMode || !this.currentCategoryId) return
+      const payload = this.productsList.map(r => {
+        let s = r.sort != null && r.sort !== '' ? Number(r.sort) : 0
+        if (Number.isNaN(s)) s = 0
+        s = Math.min(9999, Math.max(0, s))
+        return { productId: r.productId, sort: s }
+      })
+      saveCategoryProductsSort(this.currentCategoryId, payload).then(() => {
+        this.msgSuccess('保存成功')
+        this.productsSortEditMode = false
+        this.loadCategoryProducts()
+      })
+    },
     loadCategoryProducts() {
       if (!this.currentCategoryId) return
       this.productsLoading = true
       listCategoryProducts(this.currentCategoryId, this.productsQuery).then(response => {
-        this.productsList = response.rows || []
+        const rows = response.rows || []
+        this.productsList = rows.map(r => ({
+          ...r,
+          sort: r.sort != null && r.sort !== '' ? Number(r.sort) : 0
+        }))
         this.productsTotal = response.total || 0
         this.productsLoading = false
       })
@@ -270,4 +330,18 @@ export default {
 
 <style scoped>
 .mr4 { margin-right: 4px; }
+.products-toolbar {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 8px;
+}
+.products-count {
+  margin-left: auto;
+  color: #606266;
+  font-size: 13px;
+}
+.product-sort-input >>> .el-input__inner {
+  text-align: left;
+}
 </style>