Преглед на файлове

feat(product): 添加分类管理功能并优化产品分类组件

- 在产品分类API中新增平台参数支持和大客户ID字段
- 添加categoryA10分类管理和categoryBinding绑定相关API接口
- 新增商品管理路由页面用于分类下的商品管理功能
- 在VIP站点页面添加分类管理入口链接
- 修复多处产品状态比较逻辑,统一使用数字类型判断
- 移除产品分类页面中的平台列显示
- 添加商品管理按钮到分类表格操作列
- 集成A10分类选择树形组件到分类表单
- 实现路由参数传递平台和大客户ID功能
- 添加分类管理相关类型定义文件
肖路 преди 17 часа
родител
ревизия
a0e92c0016
променени са 1 файла, в които са добавени 434 реда и са изтрити 0 реда
  1. 434 0
      src/views/product/category/categoryProduct.vue

+ 434 - 0
src/views/product/category/categoryProduct.vue

@@ -0,0 +1,434 @@
+<template>
+  <div class="p-2">
+    <!-- 返回按钮 + 标题 -->
+    <el-card shadow="never" class="mb-2">
+      <div class="flex items-center">
+        <el-button link @click="handleBack">
+          <el-icon><ArrowLeft /></el-icon>
+          返回
+        </el-button>
+        <el-divider direction="vertical" />
+        <span class="text-base font-medium">商品管理</span>
+      </div>
+    </el-card>
+
+    <!-- 搜索区域 -->
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="70px">
+            <el-form-item label="商品编号" prop="productNo">
+              <el-input v-model="queryParams.productNo" placeholder="请输入商品编号" clearable style="width: 180px" @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="商品名称" prop="itemName">
+              <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable style="width: 180px" @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="商品品牌" prop="brandId">
+              <el-select v-model="queryParams.brandId" placeholder="请选择" clearable style="width: 120px">
+                <el-option v-for="item in brandOptions" :key="item.id" :label="item.brandName" :value="item.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <!-- 表格区域 -->
+    <el-card shadow="never">
+      <template #header>
+        <div class="flex justify-between items-center">
+          <span class="font-bold text-[#409EFF]">商品列表信息列表</span>
+          <div class="flex gap-2">
+            <el-button type="primary" icon="Plus" @click="handleAddProduct">添加商品</el-button>
+          </div>
+        </div>
+      </template>
+
+      <el-table v-loading="loading" border :data="productList">
+        <el-table-column label="商品编号" align="center" prop="productNo" width="100" />
+        <el-table-column label="商品图片" align="center" width="100">
+          <template #default="scope">
+            <image-preview :src="scope.row.productImage" :width="60" :height="60"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品信息" align="center" min-width="400">
+          <template #default="scope">
+            <div class="text-left" style="font-size: 12px;">
+              <div>{{ scope.row.itemName }}</div>
+              <div class="text-gray-500">品牌:{{ scope.row.brandName || '-' }}</div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品类别" align="center" prop="categoryName" width="120" />
+        <el-table-column label="单位" align="center" width="100">
+          <template #default="scope">
+            <div style="font-size: 12px;">
+              <div>单位:{{ scope.row.unitName || '个' }}</div>
+              <div>起订量:{{ scope.row.minOrderQuantity || 1 }}</div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="SKU价格" align="center" width="130">
+          <template #default="scope">
+            <div class="text-left" style="font-size: 12px;">
+              <div>市场价:¥{{ scope.row.marketPrice || '0.00' }}</div>
+              <div class="text-[#f56c6c]">官网价:¥{{ scope.row.memberPrice || '0.00' }}</div>
+              <div>最低售价:¥{{ scope.row.minSellingPrice || '0.00' }}</div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="采购价" align="center" prop="purchasingPrice" width="100" />
+        <el-table-column label="商品状态" align="center" width="100">
+          <template #default="scope">
+            <el-tag v-if="scope.row.productStatus === 1" type="success" size="small">上架</el-tag>
+            <el-tag v-else type="info" size="small">下架</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="100" fixed="right">
+          <template #default="scope">
+            <div class="flex flex-col items-center gap-1">
+              <el-link type="danger" :underline="false" @click="handleDelete(scope.row)">删 除</el-link>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+
+    <!-- 添加商品弹窗 -->
+    <el-dialog title="添加商品" v-model="addProductDialog.visible" width="1500px" append-to-body top="5vh">
+      <div class="add-product-dialog">
+        <el-form :model="addProductQuery" :inline="true" class="mb-4">
+          <el-form-item>
+            <el-button type="primary" icon="Plus" @click="handleBatchAdd">加入清单</el-button>
+          </el-form-item>
+          <el-form-item label="商品名称:">
+            <el-input v-model="addProductQuery.itemName" placeholder="商品名称" clearable style="width: 180px" />
+          </el-form-item>
+          <el-form-item label="商品编号:">
+            <el-input v-model="addProductQuery.productNo" placeholder="商品编号" clearable style="width: 180px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleSearchProducts">搜索</el-button>
+          </el-form-item>
+        </el-form>
+
+        <el-table
+          ref="addProductTableRef"
+          v-loading="addProductDialog.loading"
+          :data="addProductDialog.productList"
+          border
+          @selection-change="handleSelectionChange"
+          max-height="500"
+        >
+          <el-table-column type="selection" width="55" align="center" />
+          <el-table-column label="商品编号" align="center" prop="productNo" width="100" />
+          <el-table-column label="商品图片" align="center" width="100">
+            <template #default="scope">
+              <image-preview :src="scope.row.productImage" :width="60" :height="60"/>
+            </template>
+          </el-table-column>
+          <el-table-column label="商品信息" align="center" min-width="400">
+            <template #default="scope">
+              <div class="text-left" style="font-size: 12px;">
+                <div>{{ scope.row.itemName }}</div>
+                <div class="text-gray-500">品牌:{{ scope.row.brandName || '-' }}</div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="商品分类" align="center" prop="categoryName" width="120" />
+          <el-table-column label="单位" align="center" width="100">
+            <template #default="scope">
+              <div class="text-left" style="font-size: 12px;">
+                <div>单位:{{ scope.row.unitName || '个' }}</div>
+                <div>起订量:{{ scope.row.minOrderQuantity || 1 }}</div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="SKU价格" align="center" width="130">
+            <template #default="scope">
+              <div class="text-left" style="font-size: 12px;">
+                <div>市场价:¥{{ scope.row.marketPrice || '0.00' }}</div>
+                <div class="text-[#f56c6c]">平台价:¥{{ scope.row.memberPrice || '0.00' }}</div>
+                <div>最低价:¥{{ scope.row.minSellingPrice   || '0.00' }}</div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="库存情况" align="center" width="130">
+            <template #default="scope">
+              <div class="text-left" style="font-size: 12px;">
+                <div class="text-[#f56c6c]">库存总数:{{ scope.row.stock || 0 }}</div>
+                <div>现有库存:{{ scope.row.availableStock || 0 }}</div>
+                <div>虚拟库存:{{ scope.row.virtualStock || 0 }}</div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="采购价" align="center" prop="purchasingPrice" width="80" />
+          <el-table-column label="商品状态" align="center" prop="productStatus" width="80" >
+            <template #default="scope">
+              <el-tag v-if="scope.row.productStatus === 1" type="success" size="small">上架</el-tag>
+              <el-tag v-else type="info" size="small">下架</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" width="100" fixed="right">
+            <template #default="scope">
+              <el-link type="primary" :underline="false" @click="handleAddSingleProduct(scope.row)">加入清单</el-link>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <pagination
+          v-show="addProductDialog.productList.length > 0"
+          v-model:page="addProductQuery.pageNum"
+          v-model:limit="addProductQuery.pageSize"
+          :total="addProductDialog.total"
+          @pagination="getProductList"
+        />
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="CategoryProduct" lang="ts">
+import { ref, reactive, onMounted, getCurrentInstance, computed } from 'vue';
+import type { ComponentInternalInstance } from 'vue';
+import type { FormInstance } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { ArrowLeft } from '@element-plus/icons-vue';
+import { listBase } from '@/api/pmsProduct/base';
+import { listProductCategory } from '@/api/customerOperation/customerBlacklist';
+import { listBrand } from '@/api/product/brand';
+import {
+  getCategoryBindingProductPage,
+  addCategoryBinding,
+  delCategoryBinding
+} from '@/api/product/categoryBinding';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const route = useRoute();
+const router = useRouter();
+
+// 从路由参数中获取分类 ID
+const routeCategoryId = computed(() => {
+  const id = route.query.categoryId;
+  return id !== undefined && id !== '' ? Number(id) : undefined;
+});
+
+// 从路由参数中获取 platform
+const routePlatform = computed(() => {
+  const p = route.query.platform;
+  return p !== undefined && p !== '' ? Number(p) : undefined;
+});
+
+// 从路由参数中获取 customerId(大客户分类场景)
+const routeCustomerId = computed(() => {
+  const cid = route.query.customerId;
+  return cid !== undefined && cid !== '' ? String(cid) : undefined;
+});
+
+const productList = ref<any[]>([]);
+const loading = ref(false);
+const showSearch = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+
+// 品牌选项
+const brandOptions = ref<any[]>([]);
+
+// 查询参数
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  productNo: undefined as string | undefined,
+  itemName: undefined as string | undefined,
+  brandId: undefined as number | undefined
+});
+
+// 添加商品弹窗
+const addProductDialog = reactive({
+  visible: false,
+  loading: false,
+  productList: [] as any[],
+  total: 0
+});
+const addProductQuery = ref({
+  pageNum: 1,
+  pageSize: 10,
+  isSelf: 0,
+  productNo: undefined as string | undefined,
+  itemName: undefined as string | undefined
+});
+const selectedProducts = ref<any[]>([]);
+const addProductTableRef = ref<any>();
+
+/** 初始化 */
+const initData = () => {
+  getList();
+};
+
+/** 查询商品列表 */
+const getList = async () => {
+  loading.value = true;
+  try {
+    const res = await getCategoryBindingProductPage({
+      ...queryParams.value,
+      categoryIds: routeCategoryId.value
+    });
+    productList.value = (res as any).rows || [];
+    total.value = (res as any).total || 0;
+  } catch (error) {
+    console.error('获取商品列表失败:', error);
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 加载品牌列表 */
+const loadBrandOptions = async () => {
+  try {
+    const res = await listBrand({ pageNum: 1, pageSize: 1000 });
+    brandOptions.value = res.rows || [];
+  } catch (error) {
+    console.error('获取品牌列表失败:', error);
+  }
+};
+
+/** 返回 */
+const handleBack = () => {
+  router.back();
+};
+
+/** 搜索 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value = {
+    pageNum: 1,
+    pageSize: 10,
+    productNo: undefined,
+    itemName: undefined,
+    brandId: undefined
+  };
+  handleQuery();
+};
+
+/** 删除商品 */
+const handleDelete = async (row: any) => {
+  await proxy?.$modal.confirm(`确认要删除商品"${row.itemName}"吗?`);
+  await delCategoryBinding(row.id);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 添加商品 */
+const handleAddProduct = () => {
+  addProductDialog.visible = true;
+  addProductQuery.value = {
+    pageNum: 1,
+    pageSize: 10,
+    isSelf: 1,
+    productNo: undefined,
+    itemName: undefined
+  };
+  selectedProducts.value = [];
+  getProductList();
+};
+
+/** 获取可添加的商品列表 */
+const getProductList = async () => {
+  addProductDialog.loading = true;
+  try {
+    const res = await listBase(addProductQuery.value);
+    addProductDialog.productList = res.rows || [];
+    addProductDialog.total = res.total || 0;
+  } catch (error) {
+    console.error('获取商品列表失败:', error);
+    addProductDialog.productList = [];
+    addProductDialog.total = 0;
+  } finally {
+    addProductDialog.loading = false;
+  }
+};
+
+/** 搜索商品 */
+const handleSearchProducts = () => {
+  addProductQuery.value.pageNum = 1;
+  getProductList();
+};
+
+/** 选择变化 */
+const handleSelectionChange = (selection: any[]) => {
+  selectedProducts.value = selection;
+};
+
+/** 批量加入清单 */
+const handleBatchAdd = async () => {
+  if (selectedProducts.value.length === 0) {
+    proxy?.$modal.msgWarning('请先选择要添加的商品');
+    return;
+  }
+  try {
+    await Promise.all(
+      (selectedProducts.value as any[]).map((item: any) =>
+        addCategoryBinding({
+          productId: item.id,
+          productNo: item.productNo,
+          bottomCategoryId: routeCategoryId.value,
+          platform: routePlatform.value,
+          customerId: routeCustomerId.value
+        })
+      )
+    );
+    proxy?.$modal.msgSuccess(`成功添加 ${selectedProducts.value.length} 个商品`);
+    addProductDialog.visible = false;
+    selectedProducts.value = [];
+    if (addProductTableRef.value) {
+      addProductTableRef.value.clearSelection();
+    }
+    await getList();
+  } catch (error) {
+    console.error('添加商品失败:', error);
+  }
+};
+
+/** 添加单个商品 */
+const handleAddSingleProduct = async (row: any) => {
+  try {
+    await addCategoryBinding({
+      productId: row.id,
+      productNo: row.productNo,
+      bottomCategoryId: routeCategoryId.value,
+      platform: routePlatform.value,
+      customerId: routeCustomerId.value
+    });
+    proxy?.$modal.msgSuccess('添加成功');
+    await getList();
+  } catch (error) {
+    console.error('添加商品失败:', error);
+  }
+};
+
+onMounted(() => {
+  initData();
+  loadBrandOptions();
+});
+</script>
+
+<style scoped lang="scss">
+.add-product-dialog {
+  :deep(.el-form--inline .el-form-item) {
+    margin-right: 10px;
+  }
+}
+</style>