xw před 4 dny
rodič
revize
1b683dde92

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

@@ -0,0 +1,68 @@
+import request from '@/utils/request'
+
+// 分页列表(一页10条)
+export function listUserEndCategory(query) {
+  return request({
+    url: '/store/store/userEndCategory/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 添加/编辑商品时拉取用户端分类(金刚区、瀑布流)
+export function listUserEndCategoryForProduct(storeId) {
+  return request({
+    url: '/store/store/userEndCategory/listForProduct',
+    method: 'get',
+    params: { storeId }
+  })
+}
+
+export function getUserEndCategory(id) {
+  return request({
+    url: '/store/store/userEndCategory/' + id,
+    method: 'get'
+  })
+}
+
+export function addUserEndCategory(data) {
+  return request({
+    url: '/store/store/userEndCategory',
+    method: 'post',
+    data
+  })
+}
+
+export function updateUserEndCategory(data) {
+  return request({
+    url: '/store/store/userEndCategory',
+    method: 'put',
+    data
+  })
+}
+
+export function delUserEndCategory(ids) {
+  return request({
+    url: '/store/store/userEndCategory/' + ids,
+    method: 'delete'
+  })
+}
+
+// 按用户端分类ID分页查询关联商品(商品ID、名称、售价、原价、销量、产品标签)
+export function listCategoryProducts(id, params) {
+  return request({
+    url: '/store/store/userEndCategory/products',
+    method: 'get',
+    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
+  })
+}

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

@@ -127,12 +127,12 @@
          </template>
       </el-table-column>
       <el-table-column label="用户备注" align="center" prop="remark" />
-      <el-table-column label="管理状态" align="center" prop="manageStatusName" width="130px">
+      <el-table-column label="是否被拉黑" align="center" prop="manageStatusName" width="130px">
         <template slot-scope="scope">
-          {{ scope.row.manageStatusName || (scope.row.manageStatus === 2 ? '已被总部拉黑' : scope.row.manageStatus === 1 ? '正常' : '-') }}
+          {{ scope.row.manageStatusName || (scope.row.manageStatus === 2 ? '是' : scope.row.manageStatus === 1 ? '否' : '-') }}
         </template>
       </el-table-column>
-      <el-table-column label="管理备注" align="center" prop="manageRemark" show-overflow-tooltip />
+      <el-table-column label="拉黑原因" align="center" prop="manageRemark" show-overflow-tooltip />
       <!--  会员等级    -->
       <el-table-column label="会员等级" align="center" prop="level" width="100">
         <template slot-scope="scope">
@@ -190,9 +190,6 @@
     <!-- 添加或修改用户对话框 -->
     <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="用户昵称" prop="nickName">
-          <el-input v-model="form.nickName" placeholder="请输入用户昵称" />
-        </el-form-item>
         <el-form-item label="用户状态" prop="status">
             <el-select v-model="form.status" placeholder="请选择状态" clearable size="small">
                   <el-option
@@ -247,14 +244,14 @@
         <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-form-item label="是否拉黑" prop="manageStatus">
           <el-select v-model="form.manageStatus" placeholder="请选择管理状态" clearable size="small">
-            <el-option label="正常" :value="1" />
-            <el-option label="已被总部拉黑" :value="2" />
+            <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 label="拉黑原因" prop="manageRemark">
+          <el-input v-model="form.manageRemark" placeholder="请输入拉黑原因" type="textarea"/>
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">

+ 347 - 0
src/views/hisStore/userEndCategory/index.vue

@@ -0,0 +1,347 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="90px">
+      <el-form-item label="分类名称" prop="categoryName">
+        <el-input
+          v-model="queryParams.categoryName"
+          placeholder="请输入分类名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="分类位置" prop="position">
+        <el-select v-model="queryParams.position" placeholder="请选择" clearable size="small">
+          <el-option label="金刚区" :value="1" />
+          <el-option label="瀑布流" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['store:userEndCategory:add']">新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="list" border>
+      <el-table-column label="序号" type="index" width="55" align="center" />
+      <el-table-column label="分类名称" align="center" prop="categoryName" />
+      <el-table-column label="位置" align="center" prop="position" width="100">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.position === 1" type="primary">金刚区</el-tag>
+          <el-tag v-else type="success">瀑布流</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="Icon" align="center" width="80">
+        <template slot-scope="scope">
+          <el-image v-if="scope.row.icon" style="width:40px;height:40px" :src="scope.row.icon" fit="contain" />
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status" width="80">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.status === 1">显示</el-tag>
+          <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" width="120">
+        <template slot-scope="scope">
+          <el-button type="text" @click="showCategoryProducts(scope.row)">查看</el-button>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
+        <template slot-scope="scope">
+          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['store:userEndCategory:edit']">编辑</el-button>
+          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['store:userEndCategory:remove']">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
+
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="分类名称" prop="categoryName">
+          <el-input v-model="form.categoryName" placeholder="请输入分类名称" maxlength="32" show-word-limit />
+        </el-form-item>
+        <el-form-item label="分类位置" prop="position">
+          <el-radio-group v-model="form.position">
+            <el-radio :label="1">金刚区</el-radio>
+            <el-radio :label="2">瀑布流</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item v-if="form.position === 1" label="分类icon" prop="icon">
+          <Material v-model="iconArr" type="image" :num="1" :width="150" :height="150" />
+        </el-form-item>
+        <el-form-item label="分类状态" prop="status">
+          <el-radio-group v-model="form.status">
+            <el-radio :label="1">显示</el-radio>
+            <el-radio :label="0">隐藏</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input-number v-model="form.sort" :min="0" placeholder="排序" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 关联商品弹窗 -->
+    <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="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>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="原价" align="center" width="100">
+          <template slot-scope="scope">
+            <span v-if="scope.row.otPrice != null">¥{{ scope.row.otPrice.toFixed(2) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="销量" align="center" prop="sales" width="80" />
+        <el-table-column label="产品标签" align="center" min-width="120">
+          <template slot-scope="scope">
+            <template v-if="scope.row.tagList && scope.row.tagList.length">
+              <el-tag v-for="(tag, i) in scope.row.tagList" :key="i" size="small" class="mr4">{{ tag }}</el-tag>
+            </template>
+            <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>
+  </div>
+</template>
+
+<script>
+import { listUserEndCategory, getUserEndCategory, addUserEndCategory, updateUserEndCategory, delUserEndCategory, listCategoryProducts, saveCategoryProductsSort } from '@/api/hisStore/userEndCategory'
+import Material from '@/components/Material'
+
+export default {
+  name: 'UserEndCategory',
+  components: { Material },
+  watch: {
+    iconArr(val) {
+      this.form.icon = val && val.length ? val.join(',') : ''
+    }
+  },
+  data() {
+    return {
+      loading: true,
+      showSearch: true,
+      list: [],
+      total: 0,
+      title: '',
+      open: false,
+      iconArr: [],
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        categoryName: null,
+        position: null,
+        status: null
+      },
+      form: {},
+      rules: {
+        categoryName: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }],
+        position: [{ required: true, message: '请选择分类位置', trigger: 'change' }],
+        status: [{ required: true, message: '请选择状态', trigger: 'change' }],
+        sort: [{ required: true, message: '请输入排序', trigger: 'blur' }]
+      },
+      productsOpen: false,
+      productsLoading: false,
+      productsList: [],
+      productsTotal: 0,
+      productsQuery: { pageNum: 1, pageSize: 10 },
+      currentCategoryId: null,
+      productsSortEditMode: false
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    getList() {
+      this.loading = true
+      listUserEndCategory(this.queryParams).then(response => {
+        this.list = response.rows || []
+        this.total = response.total || 0
+        this.loading = false
+      })
+    },
+    cancel() {
+      this.open = false
+      this.reset()
+    },
+    reset() {
+      this.form = { id: null, storeId: null, categoryName: null, position: 1, icon: null, status: 1, sort: 0 }
+      this.iconArr = []
+      this.resetForm('form')
+    },
+    handleQuery() {
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+    resetQuery() {
+      this.resetForm('queryForm')
+      this.handleQuery()
+    },
+    handleAdd() {
+      this.reset()
+      this.open = true
+      this.title = '添加用户端分类'
+    },
+    handleUpdate(row) {
+      this.reset()
+      getUserEndCategory(row.id).then(response => {
+        this.form = { ...response.data }
+        if (this.form.icon) this.iconArr = this.form.icon.split(',')
+        this.open = true
+        this.title = '编辑用户端分类'
+      })
+    },
+    submitForm() {
+      this.$refs['form'].validate(valid => {
+        if (!valid) return
+        if (this.form.position === 1 && !this.form.icon) {
+          this.$message.warning('金刚区需上传分类icon')
+          return
+        }
+        if (this.form.id != null) {
+          updateUserEndCategory(this.form).then(() => {
+            this.msgSuccess('修改成功')
+            this.open = false
+            this.getList()
+          })
+        } else {
+          addUserEndCategory(this.form).then(() => {
+            this.msgSuccess('新增成功')
+            this.open = false
+            this.getList()
+          })
+        }
+      })
+    },
+    handleDelete(row) {
+      this.$confirm('是否确认删除该用户端分类?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        return delUserEndCategory(row.id)
+      }).then(() => {
+        this.getList()
+        this.msgSuccess('删除成功')
+      }).catch(() => {})
+    },
+    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 => {
+        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
+      })
+    }
+  }
+}
+</script>
+
+<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>

+ 72 - 1
src/views/qw/externalContact/index.vue

@@ -127,6 +127,12 @@
         <dict-tag :options="transferStatusOptions" :value="scope.row.transferStatus"/>
       </template>
     </el-table-column>
+    <el-table-column label="是否被拉黑" align="center" prop="manageStatusName" width="130px">
+      <template slot-scope="scope">
+        {{ scope.row.manageStatusName || (scope.row.manageStatus === 2 ? '是' : scope.row.manageStatus === 1 ? '否' : '-') }}
+      </template>
+    </el-table-column>
+    <el-table-column label="拉黑原因" align="center" prop="manageRemark" show-overflow-tooltip />
     <el-table-column label="企业id" align="center" prop="corpId" />
     <el-table-column label="重粉看课历史" width="140px" align="center" fixed="right">
       <template slot-scope="scope">
@@ -148,6 +154,17 @@
         <el-tag v-else type="info"> 未绑定</el-tag>
       </template>
     </el-table-column>
+    <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="80px" fixed="right">
+      <template slot-scope="scope">
+        <el-button
+          size="mini"
+          type="text"
+          icon="el-icon-edit"
+          @click="handleUpdate(scope.row)"
+          v-hasPermi="['qw:externalContact:edit']"
+        >修改</el-button>
+      </template>
+    </el-table-column>
     </el-table>
 
     <pagination-more
@@ -159,11 +176,29 @@
 
     <repeat-course-history-drawer ref="repeatCourseHistoryDrawer" />
 
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" label-width="100px">
+        <el-form-item label="是否拉黑" prop="manageStatus">
+          <el-select v-model="form.manageStatus" placeholder="请选择管理状态" clearable size="small">
+            <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>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
   </div>
 </template>
 
 <script>
-import { listExternalContact } from "@/api/qw/externalContact";
+import { listExternalContact, getExternalContact, updateExternalContact } from "@/api/qw/externalContact";
 import PaginationMore from '@/components/PaginationMore/index.vue'
 import RepeatCourseHistoryDrawer from './RepeatCourseHistoryDrawer.vue'
 
@@ -192,6 +227,9 @@ export default {
         fsUserId: null,
         extId: null,
       },
+      open: false,
+      title: "",
+      form: {},
     };
   },
   created() {
@@ -271,6 +309,39 @@ export default {
       this.single = selection.length!==1
       this.multiple = !selection.length
     },
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    reset() {
+      this.form = {
+        id: null,
+        manageStatus: 1,
+        manageRemark: null,
+      };
+      this.resetForm("form");
+    },
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id;
+      getExternalContact(id).then(response => {
+        this.form = response.data;
+        this.form.manageStatus = this.form.manageStatus != null ? Number(this.form.manageStatus) : 1;
+        this.open = true;
+        this.title = "修改企微客户";
+      });
+    },
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid && this.form.id != null) {
+          updateExternalContact(this.form).then(() => {
+            this.msgSuccess("修改成功");
+            this.open = false;
+            this.getList();
+          });
+        }
+      });
+    },
   }
 };
 </script>