Forráskód Böngészése

feat(product): 添加产品属性和品牌管理功能

- 新增产品属性编辑页面,支持品牌名称、首字母、分类选择等字段
- 实现产品品牌编辑页面,包含品牌基本信息、LOGO上传、品牌故事等功能
- 添加物流公司管理API接口,支持增删改查和状态变更
- 集成第三方对接项目管理相关API,包括商品、品牌、分类等模块
- 实现产品基础信息管理API,支持分类树、品牌列表、属性查询等功能
- 新增产品属性定义API,用于动态属性配置管理
- 完善产品分类管理功能,支持层级分类和完整路径显示
肖路 2 hónapja
szülő
commit
6cda4f9893
45 módosított fájl, 9014 hozzáadás és 12 törlés
  1. 75 0
      src/api/company/logisticsCompany/index.ts
  2. 110 0
      src/api/company/logisticsCompany/types.ts
  3. 63 0
      src/api/external/item/index.ts
  4. 131 0
      src/api/external/item/types.ts
  5. 111 0
      src/api/external/product/index.ts
  6. 423 0
      src/api/external/product/types.ts
  7. 67 0
      src/api/external/productBrand/index.ts
  8. 69 0
      src/api/external/productBrand/types.ts
  9. 93 0
      src/api/external/productCategory/index.ts
  10. 221 0
      src/api/external/productCategory/types.ts
  11. 67 0
      src/api/external/productChangeLog/index.ts
  12. 172 0
      src/api/external/productChangeLog/types.ts
  13. 67 0
      src/api/external/pushPoolLog/index.ts
  14. 136 0
      src/api/external/pushPoolLog/types.ts
  15. 63 0
      src/api/product/attributes/index.ts
  16. 171 0
      src/api/product/attributes/types.ts
  17. 181 0
      src/api/product/base/index.ts
  18. 771 0
      src/api/product/base/types.ts
  19. 63 0
      src/api/product/brand/index.ts
  20. 315 0
      src/api/product/brand/types.ts
  21. 74 0
      src/api/product/category/index.ts
  22. 402 0
      src/api/product/category/types.ts
  23. 63 0
      src/api/product/unit/index.ts
  24. 101 0
      src/api/product/unit/types.ts
  25. 63 0
      src/api/product/warehouseInventory/index.ts
  26. 176 0
      src/api/product/warehouseInventory/types.ts
  27. 74 3
      src/components/Pagination/index.vue
  28. 1 1
      src/layout/components/Sidebar/Logo.vue
  29. 14 0
      src/router/index.ts
  30. 220 0
      src/views/company/logisticsCompany/index.vue
  31. 261 0
      src/views/external/item/index.vue
  32. 425 0
      src/views/external/product/index.vue
  33. 251 0
      src/views/external/productBrand/index.vue
  34. 307 0
      src/views/external/productCategory/index.vue
  35. 319 0
      src/views/external/productChangeLog/index.vue
  36. 299 0
      src/views/external/pushPoolLog/index.vue
  37. 8 8
      src/views/login.vue
  38. 258 0
      src/views/product/attributes/edit.vue
  39. 283 0
      src/views/product/attributes/index.vue
  40. 626 0
      src/views/product/base/index.vue
  41. 249 0
      src/views/product/brand/edit.vue
  42. 354 0
      src/views/product/brand/index.vue
  43. 294 0
      src/views/product/category/index.vue
  44. 216 0
      src/views/product/unit/index.vue
  45. 307 0
      src/views/product/warehouseInventory/index.vue

+ 75 - 0
src/api/company/logisticsCompany/index.ts

@@ -0,0 +1,75 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { LogisticsCompanyVO, LogisticsCompanyForm, LogisticsCompanyQuery } from '@/api/company/logisticsCompany/types';
+
+/**
+ * 查询物流公司列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listLogisticsCompany = (query?: LogisticsCompanyQuery): AxiosPromise<LogisticsCompanyVO[]> => {
+  return request({
+    url: '/system/logisticsCompany/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询物流公司详细
+ * @param id
+ */
+export const getLogisticsCompany = (id: string | number): AxiosPromise<LogisticsCompanyVO> => {
+  return request({
+    url: '/system/logisticsCompany/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增物流公司
+ * @param data
+ */
+export const addLogisticsCompany = (data: LogisticsCompanyForm) => {
+  return request({
+    url: '/system/logisticsCompany',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改物流公司
+ * @param data
+ */
+export const updateLogisticsCompany = (data: LogisticsCompanyForm) => {
+  return request({
+    url: '/system/logisticsCompany',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除物流公司
+ * @param id
+ */
+export const delLogisticsCompany = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/logisticsCompany/' + id,
+    method: 'delete'
+  });
+};
+
+export function changeStatus(id: string | number, isShow: string) {
+  const data = {
+    id,
+    isShow
+  };
+  return request({
+    url: '/system/logisticsCompany/changeStatus',
+    method: 'put',
+    data: data
+  });
+}

+ 110 - 0
src/api/company/logisticsCompany/types.ts

@@ -0,0 +1,110 @@
+export interface LogisticsCompanyVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 物流公司编码
+   */
+  logisticsCode: string;
+
+  /**
+   * 物流公司名称
+   */
+  logisticsName: string;
+
+  /**
+   * 物流公司描述
+   */
+  logisticsDescription: string;
+
+  /**
+   * 是否显示
+   */
+  isShow: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface LogisticsCompanyForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 物流公司编码
+   */
+  logisticsCode?: string;
+
+  /**
+   * 物流公司名称
+   */
+  logisticsName?: string;
+
+  /**
+   * 物流公司描述
+   */
+  logisticsDescription?: string;
+
+  /**
+   * 是否显示
+   */
+  isShow?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface LogisticsCompanyQuery extends PageQuery {
+  /**
+   * 物流公司编码
+   */
+  logisticsCode?: string;
+
+  /**
+   * 物流公司名称
+   */
+  logisticsName?: string;
+
+  /**
+   * 物流公司描述
+   */
+  logisticsDescription?: string;
+
+  /**
+   * 是否显示
+   */
+  isShow?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 63 - 0
src/api/external/item/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ItemVO, ItemForm, ItemQuery } from '@/api/external/item/types';
+
+/**
+ * 查询第三方对接项目管理列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listItem = (query?: ItemQuery): AxiosPromise<any> => {
+  return request({
+    url: '/external/item/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询第三方对接项目管理详细
+ * @param id
+ */
+export const getItem = (id: string | number): AxiosPromise<ItemVO> => {
+  return request({
+    url: '/external/item/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增第三方对接项目管理
+ * @param data
+ */
+export const addItem = (data: ItemForm) => {
+  return request({
+    url: '/external/item',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改第三方对接项目管理
+ * @param data
+ */
+export const updateItem = (data: ItemForm) => {
+  return request({
+    url: '/external/item',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除第三方对接项目管理
+ * @param id
+ */
+export const delItem = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/external/item/' + id,
+    method: 'delete'
+  });
+};

+ 131 - 0
src/api/external/item/types.ts

@@ -0,0 +1,131 @@
+export interface ItemVO {
+  /**
+   * 项目id
+   */
+  id: string | number;
+
+  /**
+   * 项目名
+   */
+  itemName: string;
+
+  /**
+   * 项目key
+   */
+  itemKey: string;
+
+  /**
+   * 项目用户名
+   */
+  userName: string;
+
+  /**
+   * 项目密码
+   */
+  password: string;
+
+  /**
+   * 项目url
+   */
+  url: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface ItemForm extends BaseEntity {
+  /**
+   * 项目id
+   */
+  id?: string | number;
+
+  /**
+   * 项目名
+   */
+  itemName?: string;
+
+  /**
+   * 项目key
+   */
+  itemKey?: string;
+
+  /**
+   * 项目用户名
+   */
+  userName?: string;
+
+  /**
+   * 项目密码
+   */
+  password?: string;
+
+  /**
+   * 项目url
+   */
+  url?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface ItemQuery extends PageQuery {
+
+  /**
+   * 项目名
+   */
+  itemName?: string;
+
+  /**
+   * 项目key
+   */
+  itemKey?: string;
+
+  /**
+   * 项目用户名
+   */
+  userName?: string;
+
+  /**
+   * 项目密码
+   */
+  password?: string;
+
+  /**
+   * 项目url
+   */
+  url?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 111 - 0
src/api/external/product/index.ts

@@ -0,0 +1,111 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ProductVO, ProductForm,ThirdProductVO, ProductQuery } from '@/api/external/product/types';
+
+/**
+ * 查询对外部推送商品列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProduct = (query?: ProductQuery): AxiosPromise<ProductVO[]> => {
+  return request({
+    url: '/external/product/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询对外部推送商品详细
+ * @param id
+ */
+export const getProduct = (id: string | number): AxiosPromise<ProductVO> => {
+  return request({
+    url: '/external/product/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增对外部推送商品
+ * @param data
+ */
+export const addProduct = (data: ProductForm) => {
+  return request({
+    url: '/external/product',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改对外部推送商品
+ * @param data
+ */
+export const updateProduct = (data: ProductForm) => {
+  return request({
+    url: '/external/product',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除对外部推送商品
+ * @param id
+ */
+export const delProduct = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/external/product/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 获取第三方商品列表
+ * @param query
+ * @returns {*}
+ */
+export const getThirdProductPage = (query?: ProductQuery): AxiosPromise<ThirdProductVO[]> => {
+  return request({
+    url: '/external/product/getThirdProductPage',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 根据商品ID查询对接信息
+ * @param productId
+ */
+export const getProductByProductId = (productId: string | number): AxiosPromise<ProductVO> => {
+  return request({
+    url: '/external/product/getByProductId/' + productId,
+    method: 'get'
+  });
+};
+
+/**
+ * 批量推送商品
+ * @param ids
+ */
+export const batchPushProduct = (ids: string | number | Array<string | number>) => {
+  return request({
+    url: `/external/product/batchPush/${ids}`,
+    method: 'post'
+  });
+};
+
+
+/**
+ * 商品上下架 状态变更
+ * @param data 审核信息(包含id、productStatus、shelfComments)
+ */
+export const shelfReview = (data: ProductForm) => {
+  return request({
+    url: '/external/product/shelfReview',
+    method: 'post',
+    data: data
+  });
+};

+ 423 - 0
src/api/external/product/types.ts

@@ -0,0 +1,423 @@
+export interface ProductVO {
+  /**
+   *
+   */
+  id: string | number;
+
+  /**
+   * 商品id
+   */
+  productId: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId: string | number;
+
+  /**
+   * 分类id
+   */
+  categoryId: string | number;
+
+  /**
+   * 外部分类id
+   */
+  externalCategoryId: string | number;
+
+  /**
+   * 第三方价格
+   */
+  externalPrice: number;
+
+  /**
+   * 推送状态 0未推送,1已推送
+   */
+  pushStatus: number;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface ProductForm extends BaseEntity {
+  /**
+   *
+   */
+  id?: string | number;
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId?: string | number;
+
+  /**
+   * 分类id
+   */
+  categoryId?: string | number;
+
+  /**
+   * 外部分类id
+   */
+  externalCategoryId?: string | number;
+
+  /**
+   * 第三方价格
+   */
+  externalPrice?: number;
+
+  /**
+   * 推送状态 0未推送,1已推送
+   */
+  pushStatus?: number;
+
+  /**
+   * 商品状态:1=已上架,0=下架
+   */
+  productStatus?: string | number;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface ProductQuery extends PageQuery {
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId?: string | number;
+
+  /**
+   * 项目key
+   */
+  itemKey?: string;
+
+  /**
+   * 分类id
+   */
+  categoryId?: string | number;
+
+  /**
+   * 产品分类
+   */
+  productNo?: string;
+
+  /**
+   * 商品名称
+   */
+  itemName?: string;
+
+  /**
+   * 外部分类id
+   */
+  externalCategoryId?: string | number;
+
+  /**
+   * 第三方价格
+   */
+  externalPrice: number;
+
+  /**
+   * 推送状态 0未推送,1已推送
+   */
+  pushStatus?: number;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+export interface ThirdProductVO {
+  /**
+   * 主键,自增ID
+   */
+  id: string | number;
+
+  /**
+   * 产品编号
+   */
+  productNo: string;
+
+  /**
+   * 项目名称
+   */
+  itemName: string;
+
+  /**
+   * 品牌id
+   */
+  brandId: string | number;
+
+  /**
+   * 顶级分类id
+   */
+  topCategoryId: string | number;
+
+  /**
+   * 中级分类id
+   */
+  mediumCategoryId: string | number;
+
+  /**
+   * 底层分类id
+   */
+  bottomCategoryId: string | number;
+
+  /**
+   * 单位id
+   */
+  unitId: string | number;
+
+  /**
+   * 产品图片URL
+   */
+  productImage: string;
+
+  /**
+   * 产品图片URLUrl
+   */
+  productImageUrl: string;
+  /**
+   * 是否自营(1=是,0=否)
+   */
+  isSelf: string;
+
+  /**
+   * 商品类型 1=默认类型,2精选商品,3=停售商品
+   * */
+  productCategory?: number;
+
+  /**
+   * 产品审核状态 0=待采购审核,1=审核通过,2=驳回,3=待营销审核
+   */
+  productReviewStatus: string;
+
+  /**
+   * 首页推荐:1=推荐,0=不推荐
+   */
+  homeRecommended: string;
+
+  /**
+   * 分类推荐:1=推荐,0=不推荐
+   */
+  categoryRecommendation: string;
+
+  /**
+   * 购物车推荐:1=推荐,0=不推荐
+   */
+  cartRecommendation: string;
+
+  /**
+   * 推荐产品顺序
+   */
+  recommendedProductOrder: number;
+
+  /**
+   * 是否热门:1=是,0=否
+   */
+  isPopular: string;
+
+  /**
+   * 是否新品:1=是,0=否
+   */
+  isNew: string;
+
+  /**
+   * 商品状态:1=已上架,0=下架,2=上架中
+   */
+  productStatus: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 市场价
+   */
+  marketPrice: number;
+
+  /**
+   * 会员价格
+   */
+  memberPrice: number;
+
+  /**
+   * 最低销售价格
+   */
+  minSellingPrice: number;
+
+  /**
+   * 采购价格
+   */
+  purchasingPrice: number;
+
+  /**
+   * 暂估毛利率
+   */
+  tempGrossMargin: number;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+  /**
+   * 主库简介
+   */
+  mainLibraryIntro?: string;
+
+  /**
+   * 售后服务
+   */
+  afterSalesService?: string;
+
+  /**
+   * 服务保障 支持多选,分隔 (存储服务保障ID列表,如: "1,2,3")
+   */
+  serviceGuarantee?: string;
+
+  /**
+   * 安装服务 - 免费安装
+   */
+  freeInstallation?: string;
+
+  /**
+   * 市场价
+   */
+  midRangePrice?: number;
+
+  /**
+   * 平档价
+   */
+  standardPrice?: number;
+
+  /**
+   * 最低售价
+   */
+  certificatePrice?: number;
+
+  /**
+   * 售价验证量
+   */
+  priceVerificationQuantity?: string;
+
+  /**
+   * 采购价
+   */
+  purchasePrice?: number;
+
+  /**
+   * 暂估采购价
+   */
+  estimatedPurchasePrice?: number;
+
+  /**
+   * 产品性质
+   */
+  productNature?: string;
+
+  /**
+   * 采购人员
+   */
+  purchasingPersonnel?: string;
+
+  /**
+   * 旧属性类型
+   */
+  oldAttributeType?: string;
+
+  /**
+   * 录入套数
+   */
+  entrySetCount?: string;
+
+  /**
+   * 商品主图
+   */
+  mainImage?: string;
+
+  /**
+   * 商品详情 - 电脑端
+   */
+  pcDetail?: string;
+
+  /**
+   * 商品详情 - 移动端
+   */
+  mobileDetail?: string;
+
+  /**
+   * 税率
+   */
+  taxRate?: number;
+
+  /**
+   * 币种
+   */
+  currency?: string;
+
+  /**
+   * 最低起订量
+   */
+  minOrderQuantity?: number;
+
+  /**
+   * 审核意见
+   */
+  reviewComments?: string;
+
+  /**
+   * 商品属性值(JSON字符串)
+   */
+  attributesList?: string;
+
+  /**
+   * 品牌名称
+   */
+  brandName?: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 单位名称
+   */
+  unitName?: string;
+
+  /**
+   * 可用库存数
+   */
+  availableStock?: number;
+
+  /**
+   * 第三方价格
+   */
+  externalPrice?: number;
+}
+
+
+
+

+ 67 - 0
src/api/external/productBrand/index.ts

@@ -0,0 +1,67 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ProductBrandVO, ProductBrandForm, ProductBrandQuery } from '@/api/external/productBrand/types';
+
+/**
+ * 查询第三方产品品牌信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProductBrand = (query?: ProductBrandQuery): AxiosPromise<ProductBrandVO[]> => {
+  return request({
+    url: '/external/productBrand/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询第三方产品品牌信息详细
+ * @param id
+ * @param itemKey
+ */
+export const getProductBrand = (id: string | number, itemKey?: string): AxiosPromise<ProductBrandVO> => {
+  return request({
+    url: '/external/productBrand/' + id,
+    method: 'get',
+    params: { itemKey }
+  });
+};
+
+/**
+ * 新增第三方产品品牌信息
+ * @param data
+ */
+export const addProductBrand = (data: ProductBrandForm) => {
+  return request({
+    url: '/external/productBrand',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改第三方产品品牌信息
+ * @param data
+ */
+export const updateProductBrand = (data: ProductBrandForm) => {
+  return request({
+    url: '/external/productBrand',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除第三方产品品牌信息
+ * @param id
+ * @param itemKey
+ */
+export const delProductBrand = (id: string | number | Array<string | number>, itemKey?: string) => {
+  return request({
+    url: '/external/productBrand/' + id,
+    method: 'delete',
+    params: { itemKey }
+  });
+};

+ 69 - 0
src/api/external/productBrand/types.ts

@@ -0,0 +1,69 @@
+export interface ProductBrandVO {
+  /**
+   * 第三方品牌id
+   */
+  id: string | number;
+
+  /**
+   * 官方品牌id
+   */
+  productBrandId: string | number;
+
+  /**
+   * 官方品牌名称
+   */
+  brandName: string;
+
+  /**
+   * 第三方品牌名称
+   */
+  thirdPartyBrandName: string;
+}
+
+export interface ProductBrandForm extends BaseEntity {
+  /**
+   * 第三方品牌id
+   */
+  id?: string | number;
+
+  /**
+   * 官方品牌id
+   */
+  productBrandId?: string | number;
+
+  /**
+   * 官方品牌名称
+   */
+  brandName?: string;
+
+  /**
+   * 第三方品牌名称
+   */
+  thirdPartyBrandName?: string;
+}
+
+export interface ProductBrandQuery extends PageQuery {
+
+  /**
+   * 官方品牌名称
+   */
+  brandName?: string;
+
+  /**
+   * 第三方品牌名称
+   */
+  thirdPartyBrandName?: string;
+
+  /**
+   * 项目key
+   */
+  itemKey?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}
+
+
+

+ 93 - 0
src/api/external/productCategory/index.ts

@@ -0,0 +1,93 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ProductCategoryVO, ProductCategoryForm, ProductCategoryQuery } from '@/api/external/productCategory/types';
+
+/**
+ * 查询产品分类列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProductCategory = (query?: Partial<ProductCategoryQuery>): AxiosPromise<ProductCategoryVO[]> => {
+  return request({
+    url: '/external/productCategory/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询产品分类详细
+ * @param id
+ * @param itemKey
+ */
+export const getProductCategory = (id: string | number, itemKey?: string): AxiosPromise<ProductCategoryVO> => {
+  return request({
+    url: '/external/productCategory/' + id,
+    method: 'get',
+    params: { itemKey }
+  });
+};
+
+/**
+ * 新增产品分类
+ * @param data
+ */
+export const addProductCategory = (data: ProductCategoryForm) => {
+  return request({
+    url: '/external/productCategory',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品分类
+ * @param data
+ */
+export const updateProductCategory = (data: ProductCategoryForm) => {
+  return request({
+    url: '/external/productCategory',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品分类
+ * @param id
+ * @param itemKey
+ */
+export const delProductCategory = (id: string | number | Array<string | number>, itemKey?: string) => {
+  return request({
+    url: '/external/productCategory/' + id,
+    method: 'delete',
+    params: { itemKey }
+  });
+};
+
+/**
+ * 查询产品分类列表(排除指定节点及其子节点)
+ * @param id
+ * @param itemKey
+ */
+export const listProductCategoryExcludeChild = (id: string | number, itemKey?: string): AxiosPromise<ProductCategoryVO[]> => {
+  return request({
+    url: '/external/productCategory/list/exclude/' + id,
+    method: 'get',
+    params: { itemKey }
+  });
+};
+
+/**
+ * 查询产品分类树结构
+ * @param query
+ * @returns {*}
+ */
+export const getProductCategoryTree = (query?: Partial<ProductCategoryQuery>): AxiosPromise<ProductCategoryVO[]> => {
+  return request({
+    url: '/external/productCategory/categoryTree',
+    method: 'get',
+    params: query
+  });
+};

+ 221 - 0
src/api/external/productCategory/types.ts

@@ -0,0 +1,221 @@
+export interface ProductCategoryVO {
+  /**
+   * 第三方系统id
+   */
+  id: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId: string | number;
+
+  /**
+   * 原系统分类id
+   */
+  productCategoryId: string | number;
+
+  /**
+   * 分类编号
+   */
+  categoryNo: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName: string;
+
+  /**
+   * 父级分类ID
+   */
+  parentId: string | number;
+
+  /**
+   * 祖籍列表
+   */
+  ancestors: string;
+
+  /**
+   * 分类层级(1=一级,2=二级, 3三级)
+   */
+  classLevel: number;
+
+  /**
+   * 拼音码(用于快速检索)
+   */
+  pyCode: string;
+
+  /**
+   * 分类描述
+   */
+  classDescription: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 所属平台(0=Web, 1=小程序)
+   */
+  platform: number;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+  /**
+   * 是否有子节点
+   */
+  hasChildren?: boolean;
+
+  /**
+   * 子节点
+   */
+  children?: ProductCategoryVO[];
+
+}
+
+export interface ProductCategoryForm extends BaseEntity {
+  /**
+   * 第三方系统id
+   */
+  id?: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId?: string | number;
+
+  /**
+   * 原系统分类id
+   */
+  productCategoryId?: string | number;
+
+  /**
+   * 分类编号
+   */
+  categoryNo?: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 父级分类ID
+   */
+  parentId?: string | number;
+
+  /**
+   * 祖籍列表
+   */
+  ancestors?: string;
+
+  /**
+   * 分类层级(1=一级,2=二级, 3三级)
+   */
+  classLevel?: number;
+
+  /**
+   * 拼音码(用于快速检索)
+   */
+  pyCode?: string;
+
+  /**
+   * 分类描述
+   */
+  classDescription?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 所属平台(0=Web, 1=小程序)
+   */
+  platform?: number;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface ProductCategoryQuery extends PageQuery {
+
+  /**
+   * 项目id
+   */
+  itemId?: string | number;
+
+  /**
+   * 原系统分类id
+   */
+  productCategoryId?: string | number;
+
+  /**
+   * 分类编号
+   */
+  categoryNo?: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 父级分类ID
+   */
+  parentId?: string | number;
+
+  /**
+   * 祖籍列表
+   */
+  ancestors?: string;
+
+  /**
+   * 分类层级(1=一级,2=二级, 3三级)
+   */
+  classLevel?: number;
+
+  /**
+   * 拼音码(用于快速检索)
+   */
+  pyCode?: string;
+
+  /**
+   * 分类描述
+   */
+  classDescription?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 所属平台(0=Web, 1=小程序)
+   */
+  platform?: number;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 项目key
+   */
+  itemKey?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 67 - 0
src/api/external/productChangeLog/index.ts

@@ -0,0 +1,67 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ProductChangeLogVO, ProductChangeLogForm, ProductChangeLogQuery } from '@/api/external/productChangeLog/types';
+
+/**
+ * 查询商品变更消息记录列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProductChangeLog = (query?: ProductChangeLogQuery): AxiosPromise<ProductChangeLogVO[]> => {
+  return request({
+    url: '/external/productChangeLog/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询商品变更消息记录详细
+ * @param id
+ * @param itemKey
+ */
+export const getProductChangeLog = (id: string | number, itemKey?: string): AxiosPromise<ProductChangeLogVO> => {
+  return request({
+    url: '/external/productChangeLog/' + id,
+    method: 'get',
+    params: { itemKey }
+  });
+};
+
+/**
+ * 新增商品变更消息记录
+ * @param data
+ */
+export const addProductChangeLog = (data: ProductChangeLogForm) => {
+  return request({
+    url: '/external/productChangeLog',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改商品变更消息记录
+ * @param data
+ */
+export const updateProductChangeLog = (data: ProductChangeLogForm) => {
+  return request({
+    url: '/external/productChangeLog',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除商品变更消息记录
+ * @param id
+ * @param itemKey
+ */
+export const delProductChangeLog = (id: string | number | Array<string | number>, itemKey?: string) => {
+  return request({
+    url: '/external/productChangeLog/' + id,
+    method: 'delete',
+    params: { itemKey }
+  });
+};

+ 172 - 0
src/api/external/productChangeLog/types.ts

@@ -0,0 +1,172 @@
+export interface ProductChangeLogVO {
+  /**
+   * 
+   */
+  id: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId: string | number;
+
+  /**
+   * 商品id
+   */
+  productId: string | number;
+
+  /**
+   * type=2商品
+价格变更,后
+续会调用价格
+接口。
+type=4代表
+商品上下架变
+更消息,后续
+会调用上下架
+状态接口。
+type=6代表
+添加、删除商
+品池内的商
+品,触发保存
+商品流程,依
+次调用商品详
+情等接口获取
+商品信息。
+type=16商品
+介绍及规格参
+数变更消息,
+调用商品详情
+等接口更新商
+品信息。
+   */
+  type: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface ProductChangeLogForm extends BaseEntity {
+  /**
+   * 
+   */
+  id?: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId?: string | number;
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * type=2商品
+价格变更,后
+续会调用价格
+接口。
+type=4代表
+商品上下架变
+更消息,后续
+会调用上下架
+状态接口。
+type=6代表
+添加、删除商
+品池内的商
+品,触发保存
+商品流程,依
+次调用商品详
+情等接口获取
+商品信息。
+type=16商品
+介绍及规格参
+数变更消息,
+调用商品详情
+等接口更新商
+品信息。
+   */
+  type?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface ProductChangeLogQuery extends PageQuery {
+
+  /**
+   * 项目id
+   */
+  itemId?: string | number;
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * type=2商品
+价格变更,后
+续会调用价格
+接口。
+type=4代表
+商品上下架变
+更消息,后续
+会调用上下架
+状态接口。
+type=6代表
+添加、删除商
+品池内的商
+品,触发保存
+商品流程,依
+次调用商品详
+情等接口获取
+商品信息。
+type=16商品
+介绍及规格参
+数变更消息,
+调用商品详情
+等接口更新商
+品信息。
+   */
+  type?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 项目key
+   */
+  itemKey?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 67 - 0
src/api/external/pushPoolLog/index.ts

@@ -0,0 +1,67 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { PushPoolLogVO, PushPoolLogForm, PushPoolLogQuery } from '@/api/external/pushPoolLog/types';
+
+/**
+ * 查询商品池推送记录列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listPushPoolLog = (query?: PushPoolLogQuery): AxiosPromise<PushPoolLogVO[]> => {
+  return request({
+    url: '/external/pushPoolLog/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询商品池推送记录详细
+ * @param id
+ * @param itemKey
+ */
+export const getPushPoolLog = (id: string | number, itemKey?: string): AxiosPromise<PushPoolLogVO> => {
+  return request({
+    url: '/external/pushPoolLog/' + id,
+    method: 'get',
+    params: { itemKey }
+  });
+};
+
+/**
+ * 新增商品池推送记录
+ * @param data
+ */
+export const addPushPoolLog = (data: PushPoolLogForm) => {
+  return request({
+    url: '/external/pushPoolLog',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改商品池推送记录
+ * @param data
+ */
+export const updatePushPoolLog = (data: PushPoolLogForm) => {
+  return request({
+    url: '/external/pushPoolLog',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除商品池推送记录
+ * @param id
+ * @param itemKey
+ */
+export const delPushPoolLog = (id: string | number | Array<string | number>, itemKey?: string) => {
+  return request({
+    url: '/external/pushPoolLog/' + id,
+    method: 'delete',
+    params: { itemKey }
+  });
+};

+ 136 - 0
src/api/external/pushPoolLog/types.ts

@@ -0,0 +1,136 @@
+export interface PushPoolLogVO {
+  /**
+   * 
+   */
+  id: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId: string | number;
+
+  /**
+   * 商品池id
+   */
+  poolId: string | number;
+
+  /**
+   * 操作人id
+   */
+  operatorId: string | number;
+
+  /**
+   * 推送状态 0成功 1失败
+   */
+  pushStatus: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 失败原因
+   */
+  reason: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface PushPoolLogForm extends BaseEntity {
+  /**
+   * 
+   */
+  id?: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId?: string | number;
+
+  /**
+   * 商品池id
+   */
+  poolId?: string | number;
+
+  /**
+   * 操作人id
+   */
+  operatorId?: string | number;
+
+  /**
+   * 推送状态 0成功 1失败
+   */
+  pushStatus?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 失败原因
+   */
+  reason?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface PushPoolLogQuery extends PageQuery {
+
+  /**
+   * 项目id
+   */
+  itemId?: string | number;
+
+  /**
+   * 商品池id
+   */
+  poolId?: string | number;
+
+  /**
+   * 操作人id
+   */
+  operatorId?: string | number;
+
+  /**
+   * 推送状态 0成功 1失败
+   */
+  pushStatus?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 失败原因
+   */
+  reason?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 项目key
+   */
+  itemKey?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 63 - 0
src/api/product/attributes/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { AttributesVO, AttributesForm, AttributesQuery } from '@/api/product/attributes/types';
+
+/**
+ * 查询产品属性定义(用于动态属性配置)列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listAttributes = (query?: AttributesQuery): AxiosPromise<AttributesVO[]> => {
+  return request({
+    url: '/product/attributes/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询产品属性定义(用于动态属性配置)详细
+ * @param id
+ */
+export const getAttributes = (id: string | number): AxiosPromise<AttributesVO> => {
+  return request({
+    url: '/product/attributes/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增产品属性定义(用于动态属性配置)
+ * @param data
+ */
+export const addAttributes = (data: AttributesForm) => {
+  return request({
+    url: '/product/attributes',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品属性定义(用于动态属性配置)
+ * @param data
+ */
+export const updateAttributes = (data: AttributesForm) => {
+  return request({
+    url: '/product/attributes',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品属性定义(用于动态属性配置)
+ * @param id
+ */
+export const delAttributes = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/attributes/' + id,
+    method: 'delete'
+  });
+};

+ 171 - 0
src/api/product/attributes/types.ts

@@ -0,0 +1,171 @@
+  export interface AttributesVO {
+  /**
+   * 主键,自增ID
+   */
+  id: string | number;
+
+  /**
+   * 关联的产品分类id
+   */
+  categoryId: string | number;
+
+  /**
+   * 产品分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 属性编码(用于系统识别)
+   */
+  productAttributesCode: string;
+
+  /**
+   * 属性显示名称
+   */
+  productAttributesName: string;
+
+  /**
+   * 是否可选:0=单选属性,1=唯一属性,2=复选属性
+   */
+  isOptional: string; 
+
+  /**
+   * 属性录入方式(1=手工录入,2=从列表中选择)
+   */
+  entryMethod: string;
+
+  /**
+   * 是否用于商品筛选:1=是,0=否
+   */
+  isFilter: string;
+
+  /**
+   * 预定义属性值列表(逗号分隔或JSON)
+   */
+  attributesList: string;
+
+  /**
+   * 是否必填: 1=是, 0=否
+   */
+  required: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface AttributesForm extends BaseEntity {
+  /**
+   * 主键,自增ID
+   */
+  id?: string | number;
+
+  /**
+   * 关联的产品分类id
+   */
+  categoryId?: string | number;
+
+  /**
+   * 属性编码(用于系统识别)
+   */
+  productAttributesCode?: string;
+
+  /**
+   * 属性显示名称
+   */
+  productAttributesName?: string;
+
+  /**
+   * 是否可选:0=单选属性,1=唯一属性,2=复选属性
+   */
+  isOptional?: string | number;
+
+  /**
+   * 属性录入方式(manual=手工录入,select=从列表中选择)
+   */
+  entryMethod?: string;
+
+  /**
+   * 是否用于商品筛选:1=是,0=否
+   */
+  isFilter?: string | number;
+
+  /**
+   * 预定义属性值列表(逗号分隔或JSON)
+   */
+  attributesList?: string;
+
+  /**
+   * 是否必填: 1=是, 0=否
+   */
+  required?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface AttributesQuery extends PageQuery {
+
+  /**
+   * 关联的产品分类id
+   */
+  categoryId?: string | number;
+
+  /**
+   * 分类名称(用于模糊搜索)
+   */
+  categoryName?: string;
+
+  /**
+   * 属性编码(用于系统识别)
+   */
+  productAttributesCode?: string;
+
+  /**
+   * 属性显示名称
+   */
+  productAttributesName?: string;
+
+  /**
+   * 是否可选:0=单选属性,1=唯一属性,2=复选属性
+   */
+  isOptional?: string;
+
+  /**
+   * 属性录入方式(manual=手工录入,select=从列表中选择)
+   */
+  entryMethod?: string;
+
+  /**
+   * 是否用于商品筛选:1=是,0=否
+   */
+  isFilter?: string;
+
+  /**
+   * 预定义属性值列表(逗号分隔或JSON)
+   */
+  attributesList?: string;
+
+  /**
+   * 是否必填: 1=是, 0=否
+   */
+  required?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 181 - 0
src/api/product/base/index.ts

@@ -0,0 +1,181 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { BaseVO, BaseForm, BaseQuery } from '@/api/product/base/types';
+import { CategoryQuery, categoryTreeVO, CategoryVO } from '../category/types';
+import { BrandQuery, BrandVO } from '../brand/types';
+import { AttributesVO } from '../attributes/types';
+import { EnsureQuery, EnsureVO } from '../ensure/types';
+import { AfterSalesQuery, AfterSalesVO } from '../afterSales/types';
+import { UnitQuery, UnitVO } from '../unit/types';
+
+/**
+ * 查询产品基础信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listBase = (query?: BaseQuery): AxiosPromise<BaseVO[]> => {
+  return request({
+    url: '/product/base/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询产品基础信息详细
+ * @param id
+ */
+export const getBase = (id: string | number): AxiosPromise<BaseVO> => {
+  return request({
+    url: '/product/base/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增产品基础信息
+ * @param data
+ */
+export const addBase = (data: BaseForm) => {
+  return request({
+    url: '/product/base',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品基础信息
+ * @param data
+ */
+export const updateBase = (data: BaseForm) => {
+  return request({
+    url: '/product/base',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品基础信息
+ * @param id
+ */
+export const delBase = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/base/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 获取产品分类树
+ * @param query
+ * @returns {*}
+ */
+export const categoryTree = (query?: CategoryQuery): AxiosPromise<categoryTreeVO[]> => {
+  return request({
+    url: '/product/base/categoryTree',
+    method: 'get',
+    params: query
+  });
+};
+/**
+ * 查询产品分类信息列表
+ * @param query
+ * @returns {*}
+ */
+export const categoryList = (query?: CategoryQuery): AxiosPromise<CategoryVO[]> => {
+  return request({
+    url: '/product/base/categoryList',
+    method: 'get',
+    params: query
+  });
+};
+/**
+ * 查询产品品牌信息列表
+ * @param query
+ * @returns {*}
+ */
+export const brandList = (query?: BrandQuery): AxiosPromise<BrandVO[]> => {
+  return request({
+    url: '/product/base/brandList',
+    method: 'get',
+    params: query
+  });
+};
+/**
+ * 查询产品分类下的属性列表
+ * @param id
+ * @returns {*}
+ */
+export const categoryAttributeList = (id: string | number): AxiosPromise<AttributesVO[]> => {
+  return request({
+    url: '/product/base/getProductAttributeList/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 查询产品服务保障列表
+ * @param query
+ * @returns {*}
+ */
+export const getServiceList = (query?: EnsureQuery): AxiosPromise<EnsureVO[]> => {
+  return request({
+    url: '/product/base/getServiceList',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 获取售后服务信息列表
+ * @param query
+ * @returns {*}
+ */
+export const getAfterSaleList = (query?: AfterSalesQuery): AxiosPromise<AfterSalesVO[]> => {
+  return request({
+    url: '/product/base/getAfterSalesList',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 获取单位信息列表
+ * @param query
+ * @returns {*}
+ */
+export const getUnitList = (query?: UnitQuery): AxiosPromise<UnitVO[]> => {
+  return request({
+    url: '/product/base/getUnitList',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 商品审核
+ * @param data 审核信息(包含id、productReviewStatus、reviewComments)
+ */
+export const reviewBase = (data: BaseForm) => {
+  return request({
+    url: '/product/base/review',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 商品上下架 状态变更
+ * @param data 审核信息(包含id、productStatus、shelfComments)
+ */
+export const shelfReview = (data: BaseForm) => {
+  return request({
+    url: '/product/base/shelfReview',
+    method: 'post',
+    data: data
+  });
+};
+

+ 771 - 0
src/api/product/base/types.ts

@@ -0,0 +1,771 @@
+export interface BaseVO {
+  /**
+   * 主键,自增ID
+   */
+  id: string | number;
+
+  /**
+   * 产品编号
+   */
+  productNo: string;
+
+  /**
+   * 项目名称
+   */
+  itemName: string;
+
+  /**
+   * 品牌id
+   */
+  brandId: string | number;
+
+  /**
+   * 顶级分类id
+   */
+  topCategoryId: string | number;
+
+  /**
+   * 中级分类id
+   */
+  mediumCategoryId: string | number;
+
+  /**
+   * 底层分类id
+   */
+  bottomCategoryId: string | number;
+
+  /**
+   * 单位id
+   */
+  unitId: string | number;
+
+  /**
+   * 产品图片URL
+   */
+  productImage: string;
+
+  /**
+   * 产品图片URLUrl
+   */
+  productImageUrl: string;
+  /**
+   * 是否自营(1=是,0=否)
+   */
+  isSelf: string;
+
+  /**
+   * 商品类型 1=默认类型,2精选商品,3=停售商品
+   * */
+  productCategory?: number;
+
+  /**
+   * 产品审核状态 0=待采购审核,1=审核通过,2=驳回,3=待营销审核
+   */
+  productReviewStatus: string;
+
+  /**
+   * 首页推荐:1=推荐,0=不推荐
+   */
+  homeRecommended: string;
+
+  /**
+   * 分类推荐:1=推荐,0=不推荐
+   */
+  categoryRecommendation: string;
+
+  /**
+   * 购物车推荐:1=推荐,0=不推荐
+   */
+  cartRecommendation: string;
+
+  /**
+   * 推荐产品顺序
+   */
+  recommendedProductOrder: number;
+
+  /**
+   * 是否热门:1=是,0=否
+   */
+  isPopular: string;
+
+  /**
+   * 是否新品:1=是,0=否
+   */
+  isNew: string;
+
+  /**
+   * 商品状态:1=已上架,0=下架,2=上架中
+   */
+  productStatus: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 市场价
+   */
+  marketPrice: number;
+
+  /**
+   * 会员价格
+   */
+  memberPrice: number;
+
+  /**
+   * 最低销售价格
+   */
+  minSellingPrice: number;
+
+  /**
+   * 采购价格
+   */
+  purchasingPrice: number;
+
+  /**
+   * 暂估毛利率
+   */
+  tempGrossMargin: number;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+  /**
+   * 主库简介
+   */
+  mainLibraryIntro?: string;
+
+  /**
+   * 售后服务
+   */
+  afterSalesService?: string;
+
+  /**
+   * 服务保障 支持多选,分隔 (存储服务保障ID列表,如: "1,2,3")
+   */
+  serviceGuarantee?: string;
+
+  /**
+   * 安装服务 - 免费安装
+   */
+  freeInstallation?: string;
+
+  /**
+   * 市场价
+   */
+  midRangePrice?: number;
+
+  /**
+   * 平档价
+   */
+  standardPrice?: number;
+
+  /**
+   * 最低售价
+   */
+  certificatePrice?: number;
+
+  /**
+   * 售价验证量
+   */
+  priceVerificationQuantity?: string;
+
+  /**
+   * 采购价
+   */
+  purchasePrice?: number;
+
+  /**
+   * 暂估采购价
+   */
+  estimatedPurchasePrice?: number;
+
+  /**
+   * 产品性质
+   */
+  productNature?: string;
+
+  /**
+   * 采购人员
+   */
+  purchasingPersonnel?: string;
+
+  /**
+   * 旧属性类型
+   */
+  oldAttributeType?: string;
+
+  /**
+   * 录入套数
+   */
+  entrySetCount?: string;
+
+  /**
+   * 商品主图
+   */
+  mainImage?: string;
+
+  /**
+   * 商品详情 - 电脑端
+   */
+  pcDetail?: string;
+
+  /**
+   * 商品详情 - 移动端
+   */
+  mobileDetail?: string;
+
+  /**
+   * 税率
+   */
+  taxRate?: number;
+
+  /**
+   * 币种
+   */
+  currency?: string;
+
+  /**
+   * 最低起订量
+   */
+  minOrderQuantity?: number;
+
+  /**
+   * 审核意见
+   */
+  reviewComments?: string;
+
+  /**
+   * 商品属性值(JSON字符串)
+   */
+  attributesList?: string;
+
+  /**
+   * 品牌名称
+   */
+  brandName?: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 单位名称
+   */
+  unitName?: string;
+
+  /**
+   * 可用库存数
+   */
+  availableStock?: number;
+
+  /**
+   * 实际库存
+   */
+  actualStock?: number;
+
+  /**
+   * 虚拟库存
+   */
+  virtualStock?: number;
+
+  /**
+   * 对接状态
+   */
+  connectStatus?: string;
+
+}
+
+export interface BaseForm extends BaseEntity {
+  /**
+   * 主键,自增ID
+   */
+  id?: string | number;
+
+  /**
+   * 产品编号
+   */
+  productNo?: string;
+
+  /**
+   * 项目名称
+   */
+  itemName?: string;
+
+  /**
+   * 品牌id
+   */
+  brandId?: string | number;
+
+  /**
+   * 顶级分类id
+   */
+  topCategoryId?: string | number;
+
+  /**
+   * 中级分类id
+   */
+  mediumCategoryId?: string | number;
+
+  /**
+   * 底层分类id
+   */
+  bottomCategoryId?: string | number;
+
+  /**
+   * 单位id
+   */
+  unitId?: string | number;
+
+  /**
+   * 产品图片URL
+   */
+  productImage?: string;
+
+  /**
+   * 是否自营(1=是,0=否)
+   */
+  isSelf?: number;
+
+  /**
+   * 商品类型 1=默认类型,2精选商品,3=停售商品
+   * */
+  productCategory?: number;
+
+  /**
+   * 产品审核状态 0=待采购审核,1=审核通过,2=驳回,3=待营销审核
+   */
+  productReviewStatus?: number;
+
+  /**
+   * 首页推荐:1=推荐,0=不推荐
+   */
+  homeRecommended?: number;
+
+  /**
+   * 分类推荐:1=推荐,0=不推荐
+   */
+  categoryRecommendation?: number;
+
+  /**
+   * 购物车推荐:1=推荐,0=不推荐
+   */
+  cartRecommendation?: number;
+
+  /**
+   * 推荐产品顺序
+   */
+  recommendedProductOrder?: number;
+
+  /**
+   * 是否热门:1=是,0=否
+   */
+  isPopular?: number;
+
+  /**
+   * 是否新品:1=是,0=否
+   */
+  isNew?: number;
+
+  /**
+   * 商品状态:1=已上架,0=下架,2=上架中
+   */
+  productStatus?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+  /**
+   * 款号
+   */
+  styleNo?: string;
+
+  /**
+   * A10产品名称
+   */
+  a10ProductName?: string;
+
+  /**
+   * 规格型号
+   */
+  specification?: string;
+
+  /**
+   * UPC(S)条码
+   */
+  upcBarcode?: string;
+
+  /**
+   * 发票名称
+   */
+  invoiceName?: string;
+
+  /**
+   * 发票规格
+   */
+  invoiceSpec?: string;
+
+  /**
+   * 产品品牌
+   */
+  productBrand?: string;
+
+  /**
+   * 段号
+   */
+  sectionNo?: string;
+
+  /**
+   * 包装规格
+   */
+  packagingSpec?: string;
+
+  /**
+   * 采用基准
+   */
+  adoptionStandard?: string;
+
+  /**
+   * 采品性质
+   */
+  purchaseNature?: string;
+
+  /**
+   * 参考链接
+   */
+  referenceLink?: string;
+
+  /**
+   * 商品重量
+   */
+  weight?: string;
+
+  /**
+   * 重量单位
+   */
+  weightUnit?: string;
+
+  /**
+   * 商品体积
+   */
+  volume?: string;
+
+  /**
+   * 体积单位
+   */
+  volumeUnit?: string;
+
+  /**
+   * 主库简介
+   */
+  mainLibraryIntro?: string;
+
+  /**
+   * 售后服务
+   */
+  afterSalesService?: string;
+
+  /**
+   * 服务保障 支持多选,分隔 (存储服务保障ID列表,如: "1,2,3")
+   */
+  serviceGuarantee?: string;
+
+  /**
+   * 安装服务 - 免费安装
+   */
+  freeInstallation?: string;
+
+  /**
+   * 市场价
+   */
+  midRangePrice?: number;
+
+  /**
+   * 平档价
+   */
+  standardPrice?: number;
+
+  /**
+   * 最低售价
+   */
+  certificatePrice?: number;
+
+  /**
+   * 售价验证量
+   */
+  priceVerificationQuantity?: string;
+
+  /**
+   * 采购价
+   */
+  purchasePrice?: number;
+
+  /**
+   * 暂估采购价
+   */
+  estimatedPurchasePrice?: number;
+
+  /**
+   * 产品性质
+   */
+  productNature?: string;
+
+  /**
+   * 采购人员
+   */
+  purchasingPersonnel?: string;
+
+  /**
+   * 旧属性类型
+   */
+  oldAttributeType?: string;
+
+  /**
+   * 录入套数
+   */
+  entrySetCount?: string;
+
+  /**
+   * 商品主图
+   */
+  mainImage?: string;
+
+  /**
+   * 商品详情 - 电脑端
+   */
+  pcDetail?: string;
+
+  /**
+   * 商品详情 - 移动端
+   */
+  mobileDetail?: string;
+
+  /**
+   * 税率
+   */
+  taxRate?: number;
+
+  /**
+   * 币种
+   */
+  currency?: string;
+
+  /**
+   * 最低起订量
+   */
+  minOrderQuantity?: number;
+
+  /**
+   * 是否可定制
+   */
+  customizable?: boolean;
+
+  /**
+   * 定制方式(逗号分隔)
+   */
+  customizedStyle?: string;
+
+  /**
+   * 定制工艺(逗号分隔)
+   */
+  customizedCraft?: string;
+
+  /**
+   * 定制说明
+   */
+  customDescription?: string;
+
+  /**
+   * 定制详情列表(JSON字符串)
+   */
+  customDetailsJson?: string;
+
+  /**
+   * 销售量/销量人气
+   */
+  salesVolume?: number;
+
+  /**
+   * 商品属性值(JSON字符串)
+   */
+  attributesList?: string;
+
+  /**
+   * 审核意见
+   */
+  reviewComments?: string;
+
+  /**
+   * 上下架审核意见
+   */
+  shelfComments?: string;
+
+}
+
+export interface BaseQuery extends PageQuery {
+
+  /**
+   * 搜索文本(商品名称/商品编号)
+   */
+  searchText?: string;
+
+  /**
+   * 产品编号
+   */
+  productNo?: string;
+
+  /**
+   * 项目名称
+   */
+  itemName?: string;
+
+  /**
+   * 品牌id
+   */
+  brandId?: string | number;
+
+  /**
+   * 商品品牌名称
+   */
+  brandName?: string;
+
+  /**
+   * 顶级分类id
+   */
+  topCategoryId?: string | number;
+
+  /**
+   * 中级分类id
+   */
+  mediumCategoryId?: string | number;
+
+  /**
+   * 底层分类id
+   */
+  bottomCategoryId?: string | number;
+
+  /**
+   * 单位id
+   */
+  unitId?: string | number;
+
+  /**
+   * 产品图片URL
+   */
+  productImage?: string;
+
+  /**
+   * 是否自营(1=是,0=否)
+   */
+  isSelf?: number;
+
+  /**
+   * 商品类型 1=默认类型,2精选商品,3=停售商品
+   * */
+  productCategory?: number;
+
+  /**
+   * 产品审核状态 0=待采购审核,1=审核通过,2=驳回,3=待营销审核
+   */
+  productReviewStatus?: number;
+
+  /**
+   * 首页推荐:1=推荐,0=不推荐
+   */
+  homeRecommended?: number;
+
+  /**
+   * 分类推荐:1=推荐,0=不推荐
+   */
+  categoryRecommendation?: number;
+
+  /**
+   * 购物车推荐:1=推荐,0=不推荐
+   */
+  cartRecommendation?: number;
+
+  /**
+   * 推荐产品顺序
+   */
+  recommendedProductOrder?: number;
+
+  /**
+   * 是否热门:1=是,0=否
+   */
+  isPopular?: number;
+
+  /**
+   * 是否新品:1=是,0=否
+   */
+  isNew?: number;
+
+  /**
+   * 商品状态:1=已上架,0=下架,2=上架中
+   */
+  productStatus?: number;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 商品标签
+   */
+  productTag?: string;
+
+  /**
+   * 采购性质
+   */
+  purchaseNature?: string;
+
+  /**
+   * 供应商类型
+   */
+  supplierType?: string;
+
+  /**
+   * 供应商性质
+   */
+  supplierNature?: string;
+
+  /**
+   * 项目组织
+   */
+  projectOrg?: string;
+
+  /**
+   * 游标分页 - 上次看到的ID
+   */
+  lastSeenId?: string | number;
+
+  /**
+   * 游标分页 - 第一个ID(用于上一页)
+   */
+  firstSeenId?: string | number;
+
+  /**
+   * 游标分页方向 0=上一页,1=下一页
+   */
+  way?: number;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+
+

+ 63 - 0
src/api/product/brand/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { BrandVO, BrandForm, BrandQuery } from '@/api/product/brand/types';
+
+/**
+ * 查询产品品牌信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listBrand = (query?: BrandQuery): AxiosPromise<BrandVO[]> => {
+  return request({
+    url: '/product/brand/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询产品品牌信息详细
+ * @param id
+ */
+export const getBrand = (id: string | number): AxiosPromise<BrandVO> => {
+  return request({
+    url: '/product/brand/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增产品品牌信息
+ * @param data
+ */
+export const addBrand = (data: BrandForm) => {
+  return request({
+    url: '/product/brand',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品品牌信息
+ * @param data
+ */
+export const updateBrand = (data: BrandForm) => {
+  return request({
+    url: '/product/brand',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品品牌信息
+ * @param id
+ */
+export const delBrand = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/brand/' + id,
+    method: 'delete'
+  });
+};

+ 315 - 0
src/api/product/brand/types.ts

@@ -0,0 +1,315 @@
+export interface BrandVO {
+  /**
+   * 主键
+   */
+  id: string | number;
+
+  /**
+   * 品牌编号(唯一标识)
+   */
+  brandNo: string;
+
+  /**
+   * 品牌中文名称
+   */
+  brandName: string;
+
+  /**
+   * 品牌首字母缩写(如拼音首字母)
+   */
+  brandInitials: string;
+
+  /**
+   * 品牌英文名称
+   */
+  brandEnglishName: string;
+
+  /**
+   * 推荐值(数值越大越靠前)
+   */
+  recommendValue: number;
+
+  /**
+   * 品牌Logo图片路径或URL
+   */
+  brandLogo: string;
+
+  /**
+   * 品牌标题(用于展示)
+   */
+  brandTitle: string;
+
+  /**
+   * 品牌大图(横幅/封面图)
+   */
+  brandBigImage: string;
+
+  /**
+   * 品牌大图(横幅/封面图)Url
+   */
+  brandBigImageUrl: string;
+  /**
+   * 品牌故事(简介文本)
+   */
+  brandStory: string;
+
+  /**
+   * 是否显示(1=显示,0=隐藏)
+   */
+  isShow: number;
+
+  /**
+   * 品牌注册人
+   */
+  brandRegistrant: string;
+
+  /**
+   * 许可证编号
+   */
+  license: string;
+
+  /**
+   * 注册证书编号
+   */
+  registrationCertificate: string;
+
+  /**
+   * 证书/许可过期时间
+   */
+  expireTime: string;
+
+  /**
+   * 品牌描述(较长文本)
+   */
+  brandDescribe: string;
+
+  /**
+   * 展示位置(如首页、分类页等)
+   */
+  position: string;
+
+  /**
+   * 关注度/收藏数(默认为0)
+   */
+  care: number;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface BrandForm extends BaseEntity {
+  /**
+   * 主键
+   */
+  id?: string | number;
+
+  /**
+   * 品牌编号(唯一标识)
+   */
+  brandNo?: string;
+
+  /**
+   * 品牌中文名称
+   */
+  brandName?: string;
+
+  /**
+   * 品牌首字母缩写(如拼音首字母)
+   */
+  brandInitials?: string;
+
+  /**
+   * 品牌英文名称
+   */
+  brandEnglishName?: string;
+
+  /**
+   * 推荐值(数值越大越靠前)
+   */
+  recommendValue?: number;
+
+  /**
+   * 品牌Logo图片路径或URL
+   */
+  brandLogo?: string;
+
+  /**
+   * 品牌标题(用于展示)
+   */
+  brandTitle?: string;
+
+  /**
+   * 品牌大图(横幅/封面图)
+   */
+  brandBigImage?: string;
+
+  /**
+   * 品牌故事(简介文本)
+   */
+  brandStory?: string;
+
+  /**
+   * 是否显示(1=显示,0=隐藏)
+   */
+  isShow?: number;
+
+  /**
+   * 品牌注册人
+   */
+  brandRegistrant?: string;
+
+  /**
+   * 许可证编号
+   */
+  license?: string;
+
+  /**
+   * 注册证书编号
+   */
+  registrationCertificate?: string;
+
+  /**
+   * 证书/许可过期时间
+   */
+  expireTime?: string;
+
+  /**
+   * 品牌描述(较长文本)
+   */
+  brandDescribe?: string;
+
+  /**
+   * 展示位置(如首页、分类页等)
+   */
+  position?: string;
+
+  /**
+   * 关注度/收藏数(默认为0)
+   */
+  care?: number;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface BrandQuery extends PageQuery {
+
+  /**
+   * 品牌编号(唯一标识)
+   */
+  brandNo?: string;
+
+  /**
+   * 品牌中文名称
+   */
+  brandName?: string;
+
+  /**
+   * 品牌首字母缩写(如拼音首字母)
+   */
+  brandInitials?: string;
+
+  /**
+   * 品牌英文名称
+   */
+  brandEnglishName?: string;
+
+  /**
+   * 推荐值(数值越大越靠前)
+   */
+  recommendValue?: number;
+
+  /**
+   * 品牌Logo图片路径或URL
+   */
+  brandLogo?: string;
+
+  /**
+   * 品牌标题(用于展示)
+   */
+  brandTitle?: string;
+
+  /**
+   * 品牌大图(横幅/封面图)
+   */
+  brandBigImage?: string;
+
+  /**
+   * 品牌故事(简介文本)
+   */
+  brandStory?: string;
+
+  /**
+   * 是否显示(1=显示,0=隐藏)
+   */
+  isShow?: number;
+
+  /**
+   * 品牌注册人
+   */
+  brandRegistrant?: string;
+
+  /**
+   * 许可证编号
+   */
+  license?: string;
+
+  /**
+   * 注册证书编号
+   */
+  registrationCertificate?: string;
+
+  /**
+   * 证书/许可过期时间
+   */
+  expireTime?: string;
+
+  /**
+   * 品牌描述(较长文本)
+   */
+  brandDescribe?: string;
+
+  /**
+   * 展示位置(如首页、分类页等)
+   */
+  position?: string;
+
+  /**
+   * 关注度/收藏数(默认为0)
+   */
+  care?: number;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 74 - 0
src/api/product/category/index.ts

@@ -0,0 +1,74 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { CategoryVO, CategoryForm, CategoryQuery } from '@/api/product/category/types';
+
+/**
+ * 查询产品分类列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listCategory = (query?: Partial<CategoryQuery>): AxiosPromise<CategoryVO[]> => {
+  return request({
+    url: '/product/category/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询产品分类详细
+ * @param id
+ */
+export const getCategory = (id: string | number): AxiosPromise<CategoryVO> => {
+  return request({
+    url: '/product/category/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增产品分类
+ * @param data
+ */
+export const addCategory = (data: CategoryForm) => {
+  return request({
+    url: '/product/category',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品分类
+ * @param data
+ */
+export const updateCategory = (data: CategoryForm) => {
+  return request({
+    url: '/product/category',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品分类
+ * @param id
+ */
+export const delCategory = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/category/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 查询产品分类列表(排除节点)
+ * @param id
+ */
+export const listCategoryExcludeChild = (id: string | number): AxiosPromise<CategoryVO[]> => {
+  return request({
+    url: '/product/category/tree/exclude/' + id,
+    method: 'get'
+  });
+};

+ 402 - 0
src/api/product/category/types.ts

@@ -0,0 +1,402 @@
+export interface CategoryVO {
+  /**
+   * 主键
+   */
+  id: string | number;
+
+  /**
+   * 分类编号
+   */
+  categoryNo: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName: string;
+
+  /**
+   * 父级分类ID
+   */
+  parentId: string | number;
+
+  /**
+   * 父级分类名称
+   */
+  parentName?: string;
+
+  /**
+   * 祖籍列表
+   */
+  ancestors: string;
+
+  /**
+   * 分类层级(1=一级,2=二级, 3三级)
+   */
+  classLevel: number;
+
+  /**
+   * 是否显示(1=显示,0=隐藏)
+   */
+  isShow: number;
+
+  /**
+   * 是否在GPS中显示
+   */
+  isShowGps: number;
+
+  /**
+   * 折扣率(可能为JSON或文本)
+   */
+  discountRate: number;
+
+  /**
+   * 拼音码(用于快速检索)
+   */
+  pyCode: string;
+
+  /**
+   * 分类描述
+   */
+  classDescription: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 自定义标签1
+   */
+  oneLable1: string;
+
+  /**
+   * 自定义标签2
+   */
+  oneLable2: string;
+
+  /**
+   * 自定义链接1
+   */
+  oneLink1: string;
+
+  /**
+   * 自定义链接2
+   */
+  oneLink2: string;
+
+  /**
+   * 排序值,默认为0
+   */
+  sort: number;
+
+  /**
+   * 颜色(如CSS颜色值)
+   */
+  color: string;
+
+  /**
+   * 采购编号
+   */
+  purchaseNo: string;
+
+  /**
+   * 采购名称
+   */
+  purchaseName: string;
+
+  /**
+   * 采购负责人编号
+   */
+  purchaseManagerNo: string;
+
+  /**
+   * 采购负责人姓名
+   */
+  purchaseManagerName: string;
+
+  /**
+   * 所属平台(0=Web, 1=小程序)
+   */
+  platform: number;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+  /**
+   * 子级分类
+   */
+  children?: CategoryVO[];
+
+  /**
+   * 是否有子节点(用于懒加载)
+   */
+  hasChildren?: boolean;
+}
+
+export interface CategoryForm extends BaseEntity {
+  /**
+   * 主键
+   */
+  id?: string | number;
+
+  /**
+   * 分类编号
+   */
+  categoryNo?: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 父级分类ID
+   */
+  parentId?: string | number;
+
+  /**
+   * 父级分类名称
+   */
+  parentName?: string;
+
+  /**
+   * 祖籍列表
+   */
+  ancestors?: string;
+
+  /**
+   * 分类层级(1=一级,2=二级, 3三级)
+   */
+  classLevel?: number;
+
+  /**
+   * 是否显示(1=显示,0=隐藏)
+   */
+  isShow?: number;
+
+  /**
+   * 是否在GPS中显示
+   */
+  isShowGps?: number;
+
+  /**
+   * 折扣率(可能为JSON或文本)
+   */
+  discountRate?: number;
+
+  /**
+   * 拼音码(用于快速检索)
+   */
+  pyCode?: string;
+
+  /**
+   * 分类描述
+   */
+  classDescription?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 自定义标签1
+   */
+  oneLable1?: string;
+
+  /**
+   * 自定义标签2
+   */
+  oneLable2?: string;
+
+  /**
+   * 自定义链接1
+   */
+  oneLink1?: string;
+
+  /**
+   * 自定义链接2
+   */
+  oneLink2?: string;
+
+  /**
+   * 排序值,默认为0
+   */
+  sort?: number;
+
+  /**
+   * 颜色(如CSS颜色值)
+   */
+  color?: string;
+
+  /**
+   * 采购编号
+   */
+  purchaseNo?: string;
+
+  /**
+   * 采购名称
+   */
+  purchaseName?: string;
+
+  /**
+   * 采购负责人编号
+   */
+  purchaseManagerNo?: string;
+
+  /**
+   * 采购负责人姓名
+   */
+  purchaseManagerName?: string;
+
+  /**
+   * 所属平台(0=Web, 1=小程序)
+   */
+  platform?: number;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface CategoryQuery extends PageQuery {
+
+  /**
+   * 分类编号
+   */
+  categoryNo?: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 父级分类ID
+   */
+  parentId?: string | number;
+
+  /**
+   * 祖籍列表
+   */
+  ancestors?: string;
+
+  /**
+   * 分类层级(1=一级,2=二级, 3三级)
+   */
+  classLevel?: number;
+
+  /**
+   * 是否显示(1=显示,0=隐藏)
+   */
+  isShow?: number;
+
+  /**
+   * 是否在GPS中显示
+   */
+  isShowGps?: number;
+
+  /**
+   * 折扣率(可能为JSON或文本)
+   */
+  discountRate?: number;
+
+  /**
+   * 拼音码(用于快速检索)
+   */
+  pyCode?: string;
+
+  /**
+   * 分类描述
+   */
+  classDescription?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 自定义标签1
+   */
+  oneLable1?: string;
+
+  /**
+   * 自定义标签2
+   */
+  oneLable2?: string;
+
+  /**
+   * 自定义链接1
+   */
+  oneLink1?: string;
+
+  /**
+   * 自定义链接2
+   */
+  oneLink2?: string;
+
+  /**
+   * 排序值,默认为0
+   */
+  sort?: number;
+
+  /**
+   * 颜色(如CSS颜色值)
+   */
+  color?: string;
+
+  /**
+   * 采购编号
+   */
+  purchaseNo?: string;
+
+  /**
+   * 采购名称
+   */
+  purchaseName?: string;
+
+  /**
+   * 采购负责人编号
+   */
+  purchaseManagerNo?: string;
+
+  /**
+   * 采购负责人姓名
+   */
+  purchaseManagerName?: string;
+
+  /**
+   * 所属平台(0=Web, 1=小程序)
+   */
+  platform?: number;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+/**
+ * 部门类型
+ */
+export interface categoryTreeVO extends BaseEntity {
+  id: number | string;
+  label: string;
+  parentId: number | string;
+  weight: number;
+  children: categoryTreeVO[];
+  isShow: '';
+}
+
+
+

+ 63 - 0
src/api/product/unit/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { UnitVO, UnitForm, UnitQuery } from '@/api/product/unit/types';
+
+/**
+ * 查询产品计量单位列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listUnit = (query?: UnitQuery): AxiosPromise<UnitVO[]> => {
+  return request({
+    url: '/product/unit/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询产品计量单位详细
+ * @param id
+ */
+export const getUnit = (id: string | number): AxiosPromise<UnitVO> => {
+  return request({
+    url: '/product/unit/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增产品计量单位
+ * @param data
+ */
+export const addUnit = (data: UnitForm) => {
+  return request({
+    url: '/product/unit',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品计量单位
+ * @param data
+ */
+export const updateUnit = (data: UnitForm) => {
+  return request({
+    url: '/product/unit',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品计量单位
+ * @param id
+ */
+export const delUnit = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/unit/' + id,
+    method: 'delete'
+  });
+};

+ 101 - 0
src/api/product/unit/types.ts

@@ -0,0 +1,101 @@
+export interface UnitVO {
+  /**
+   * 主键,自增ID
+   */
+  id: string | number;
+
+  /**
+   * 单位编号
+   */
+  unitNo: string;
+
+  /**
+   * 单位名称(如:件、箱、千克等)
+   */
+  unitName: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 是否显示:1=是,0=否
+   */
+  isShow: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface UnitForm extends BaseEntity {
+  /**
+   * 主键,自增ID
+   */
+  id?: string | number;
+
+  /**
+   * 单位编号
+   */
+  unitNo?: string;
+
+  /**
+   * 单位名称(如:件、箱、千克等)
+   */
+  unitName?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 是否显示:1=是,0=否
+   */
+  isShow?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface UnitQuery extends PageQuery {
+
+  /**
+   * 单位编号
+   */
+  unitNo?: string;
+
+  /**
+   * 单位名称(如:件、箱、千克等)
+   */
+  unitName?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 是否显示:1=是,0=否
+   */
+  isShow?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 63 - 0
src/api/product/warehouseInventory/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { WarehouseInventoryVO, WarehouseInventoryForm, WarehouseInventoryQuery } from '@/api/product/warehouseInventory/types';
+
+/**
+ * 查询仓库库存明细列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listWarehouseInventory = (query?: WarehouseInventoryQuery): AxiosPromise<WarehouseInventoryVO[]> => {
+  return request({
+    url: '/product/warehouseInventory/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询仓库库存明细详细
+ * @param id
+ */
+export const getWarehouseInventory = (id: string | number): AxiosPromise<WarehouseInventoryVO> => {
+  return request({
+    url: '/product/warehouseInventory/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增仓库库存明细
+ * @param data
+ */
+export const addWarehouseInventory = (data: WarehouseInventoryForm) => {
+  return request({
+    url: '/product/warehouseInventory',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改仓库库存明细
+ * @param data
+ */
+export const updateWarehouseInventory = (data: WarehouseInventoryForm) => {
+  return request({
+    url: '/product/warehouseInventory',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除仓库库存明细
+ * @param id
+ */
+export const delWarehouseInventory = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/warehouseInventory/' + id,
+    method: 'delete'
+  });
+};

+ 176 - 0
src/api/product/warehouseInventory/types.ts

@@ -0,0 +1,176 @@
+export interface WarehouseInventoryVO {
+  /**
+   * ID
+   */
+  id: string | number;
+
+  /**
+   * 商品ID
+   */
+  productId: string | number;
+
+  /**
+   * SKU ID
+   */
+  skuId: string | number;
+
+  /**
+   * 规格型号
+   */
+  specModel: string;
+
+  /**
+   * 仓库ID
+   */
+  warehouseId: string | number;
+
+  /**
+   * 仓库编号
+   */
+  warehouseNo: string;
+
+  /**
+   * 仓库名称
+   */
+  warehouseName: string;
+
+  /**
+   * 可用库存
+   */
+  nowInventory: number;
+
+  /**
+   * 锁定库存
+   */
+  lockInventory: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface WarehouseInventoryForm extends BaseEntity {
+  /**
+   * ID
+   */
+  id?: string | number;
+
+  /**
+   * 商品ID
+   */
+  productId?: string | number;
+
+  /**
+   * SKU ID
+   */
+  skuId?: string | number;
+
+  /**
+   * 规格型号
+   */
+  specModel?: string;
+
+  /**
+   * 仓库ID
+   */
+  warehouseId?: string | number;
+
+  /**
+   * 仓库编号
+   */
+  warehouseNo?: string;
+
+  /**
+   * 仓库名称
+   */
+  warehouseName?: string;
+
+  /**
+   * 可用库存
+   */
+  nowInventory?: number;
+
+  /**
+   * 锁定库存
+   */
+  lockInventory?: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface WarehouseInventoryQuery extends PageQuery {
+
+  /**
+   * 商品ID
+   */
+  productId?: string | number;
+
+  /**
+   * SKU ID
+   */
+  skuId?: string | number;
+
+  /**
+   * 规格型号
+   */
+  specModel?: string;
+
+  /**
+   * 仓库ID
+   */
+  warehouseId?: string | number;
+
+  /**
+   * 仓库编号
+   */
+  warehouseNo?: string;
+
+  /**
+   * 仓库名称
+   */
+  warehouseName?: string;
+
+  /**
+   * 可用库存
+   */
+  nowInventory?: number;
+
+  /**
+   * 锁定库存
+   */
+  lockInventory?: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 74 - 3
src/components/Pagination/index.vue

@@ -1,6 +1,33 @@
 <template>
   <div :class="{ hidden: hidden }" class="pagination-container">
+    <!-- 游标分页模式 -->
+    <div v-if="cursorMode" class="cursor-pagination">
+      <el-button
+        :disabled="currentPage === 1"
+        @click="handlePrevPage"
+      >
+        上一页
+      </el-button>
+      <span class="page-info">第 {{ currentPage }} 页</span>
+      <el-button
+        :disabled="!hasMore"
+        @click="handleNextPage"
+      >
+        下一页
+      </el-button>
+      <el-select v-model="pageSize" @change="handleSizeChange" style="width: 100px" class="ml-2">
+        <el-option
+          v-for="size in pageSizes"
+          :key="size"
+          :label="`${size}条/页`"
+          :value="size"
+        />
+      </el-select>
+    </div>
+
+    <!-- 传统分页模式 -->
     <el-pagination
+      v-else
       v-model:current-page="currentPage"
       v-model:page-size="pageSize"
       :background="background"
@@ -22,6 +49,7 @@ const props = defineProps({
   total: propTypes.number,
   page: propTypes.number.def(1),
   limit: propTypes.number.def(20),
+  way: propTypes.number.def(1),
   pageSizes: { type: Array<number>, default: () => [10, 20, 30, 50] },
   // 移动端页码按钮的数量端默认值5
   pagerCount: propTypes.number.def(document.body.clientWidth < 992 ? 5 : 7),
@@ -29,10 +57,14 @@ const props = defineProps({
   background: propTypes.bool.def(true),
   autoScroll: propTypes.bool.def(true),
   hidden: propTypes.bool.def(false),
-  float: propTypes.string.def('right')
+  float: propTypes.string.def('right'),
+  // 游标分页模式
+  cursorMode: propTypes.bool.def(false),
+  // 是否还有更多数据(游标分页使用)
+  hasMore: propTypes.bool.def(true)
 });
 
-const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
+const emit = defineEmits(['update:page', 'update:limit', 'update:way', 'pagination']);
 const currentPage = computed({
   get() {
     return props.page;
@@ -50,7 +82,7 @@ const pageSize = computed({
   }
 });
 function handleSizeChange(val: number) {
-  if (currentPage.value * val > props.total) {
+  if (!props.cursorMode && currentPage.value * val > props.total) {
     currentPage.value = 1;
   }
   emit('pagination', { page: currentPage.value, limit: val });
@@ -64,6 +96,28 @@ function handleCurrentChange(val: number) {
     scrollTo(0, 800);
   }
 }
+// 游标分页:下一页
+function handleNextPage() {
+  if (props.hasMore) {
+    currentPage.value += 1;
+    emit('update:way', 1);
+    emit('pagination', { page: currentPage.value, limit: pageSize.value, way: 1 });
+    if (props.autoScroll) {
+      scrollTo(0, 800);
+    }
+  }
+}
+// 游标分页:上一页
+function handlePrevPage() {
+  if (currentPage.value > 1) {
+    currentPage.value -= 1;
+    emit('update:way', 0);
+    emit('pagination', { page: currentPage.value, limit: pageSize.value, way: 0 });
+    if (props.autoScroll) {
+      scrollTo(0, 800);
+    }
+  }
+}
 </script>
 
 <style lang="scss" scoped>
@@ -71,6 +125,23 @@ function handleCurrentChange(val: number) {
   .el-pagination {
     float: v-bind(float);
   }
+
+  .cursor-pagination {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 16px;
+    float: v-bind(float);
+
+    .page-info {
+      font-size: 14px;
+      color: #606266;
+    }
+
+    .ml-2 {
+      margin-left: 8px;
+    }
+  }
 }
 .pagination-container.hidden {
   display: none;

+ 1 - 1
src/layout/components/Sidebar/Logo.vue

@@ -20,7 +20,7 @@
       <router-link key="collapse" class="sidebar-logo-link" to="/">
         <!-- <img v-if="logo" :src="logo" class="sidebar-logo" /> -->
         <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
-          优易达-OMS订单管理系统
+          优易达-API对接管理平台
         </h1>
       </router-link>
     </transition>

+ 14 - 0
src/router/index.ts

@@ -88,6 +88,20 @@ export const constantRoutes: RouteRecordRaw[] = [
         meta: { title: '个人中心', icon: 'user' }
       }
     ]
+  },
+  {
+    path: '/product',
+    component: Layout,
+    hidden: true,
+    redirect: 'noredirect',
+    children: [
+      {
+        path: 'base/detail/:id',
+        component: () => import('@/views/product/base/detail.vue'),
+        name: 'ProductBaseDetail',
+        meta: { title: '产品详情', activeMenu: '/product/base' }
+      }
+    ]
   }
 ];
 

+ 220 - 0
src/views/company/logisticsCompany/index.vue

@@ -0,0 +1,220 @@
+<template>
+  <div class="p-2">
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="22"><span>物流公司信息列表</span> </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" >新增</el-button>
+          </el-col>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="logisticsCompanyList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="物流公司编码" align="center" prop="logisticsCode" />
+        <el-table-column label="物流公司名称" align="center" prop="logisticsName" />
+        <el-table-column label="物流公司描述" align="center" prop="logisticsDescription" />
+        <el-table-column label="是否显示" align="center" prop="isShow">
+          <template #default="scope">
+            <el-switch v-model="scope.row.isShow" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
+          </template>
+        </el-table-column>
+        <el-table-column label="数据来源" align="center" prop="dataSource" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">编辑</el-button>
+          </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="dialog.title" v-model="dialog.visible" width="600px" append-to-body>
+      <el-form ref="logisticsCompanyFormRef" :model="form" :rules="rules" label-width="110px">
+        <el-form-item label="物流公司名称" prop="logisticsName">
+          <el-input v-model="form.logisticsName" placeholder="请输入物流公司名称" />
+        </el-form-item>
+        <el-form-item label="物流公司描述" prop="logisticsDescription">
+          <el-input v-model="form.logisticsDescription" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="是否显示" prop="isShow">
+          <el-radio-group v-model="form.isShow">
+            <el-radio v-for="dict in sys_platform_yes_no" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="LogisticsCompany" lang="ts">
+import {
+  listLogisticsCompany,
+  getLogisticsCompany,
+  delLogisticsCompany,
+  addLogisticsCompany,
+  updateLogisticsCompany,
+  changeStatus
+} from '@/api/company/logisticsCompany';
+import { LogisticsCompanyVO, LogisticsCompanyQuery, LogisticsCompanyForm } from '@/api/company/logisticsCompany/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_platform_yes_no } = toRefs<any>(proxy?.useDict('sys_platform_yes_no'));
+
+const logisticsCompanyList = ref<LogisticsCompanyVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const logisticsCompanyFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: LogisticsCompanyForm = {
+  id: undefined,
+  logisticsCode: undefined,
+  logisticsName: undefined,
+  logisticsDescription: undefined,
+  isShow: '0',
+  dataSource: undefined,
+  remark: undefined
+};
+const data = reactive<PageData<LogisticsCompanyForm, LogisticsCompanyQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    logisticsCode: undefined,
+    logisticsName: undefined,
+    logisticsDescription: undefined,
+    isShow: undefined,
+    dataSource: undefined,
+    platformCode: undefined,
+    params: {}
+  },
+  rules: {
+    logisticsName: [{ required: true, message: '物流公司名称不能为空', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询物流公司列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listLogisticsCompany(queryParams.value);
+  logisticsCompanyList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  logisticsCompanyFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+const handleStatusChange = async (row: LogisticsCompanyVO) => {
+  const oldValue = row.isShow; // 保存旧值(0 或 1)
+
+  try {
+    await changeStatus(row.id, row.isShow); // 传新值
+    proxy?.$modal.msgSuccess('操作成功');
+  } catch {
+    row.isShow = oldValue; // 失败回滚
+    proxy?.$modal.msgError('操作失败,请重试');
+  }
+};
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: LogisticsCompanyVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '添加物流公司';
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: LogisticsCompanyVO) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const res = await getLogisticsCompany(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = '修改物流公司';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  logisticsCompanyFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateLogisticsCompany(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await addLogisticsCompany(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: LogisticsCompanyVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除物流公司编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delLogisticsCompany(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'system/logisticsCompany/export',
+    {
+      ...queryParams.value
+    },
+    `logisticsCompany_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 261 - 0
src/views/external/item/index.vue

@@ -0,0 +1,261 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-form-item label="项目名" prop="itemName">
+              <el-input v-model="queryParams.itemName" placeholder="请输入项目名" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="项目key" prop="itemKey">
+              <el-input v-model="queryParams.itemKey" placeholder="请输入项目key" clearable @keyup.enter="handleQuery" />
+            </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>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="itemList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="项目id" align="center" prop="id" v-if="true" />
+        <el-table-column label="项目名" align="center" prop="itemName" />
+        <el-table-column label="项目key" align="center" prop="itemKey" />
+        <el-table-column label="项目用户名" align="center" prop="userName" />
+        <el-table-column label="项目密码" align="center" prop="password" />
+        <el-table-column label="项目url" align="center" prop="url" />
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
+            </el-tooltip>
+          </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="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="itemFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="项目名" prop="itemName">
+          <el-input v-model="form.itemName" placeholder="请输入项目名" />
+        </el-form-item>
+        <el-form-item label="项目key" prop="itemKey">
+          <el-input v-model="form.itemKey" placeholder="请输入项目key" />
+        </el-form-item>
+        <el-form-item label="项目用户名" prop="userName">
+          <el-input v-model="form.userName" placeholder="请输入项目用户名" />
+        </el-form-item>
+        <el-form-item label="项目密码" prop="password">
+          <el-input v-model="form.password" placeholder="请输入项目密码" />
+        </el-form-item>
+        <el-form-item label="项目url" prop="url">
+          <el-input v-model="form.url" placeholder="请输入项目url" />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Item" lang="ts">
+import { listItem, getItem, delItem, addItem, updateItem } from '@/api/external/item';
+import { ItemVO, ItemQuery, ItemForm } from '@/api/external/item/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const itemList = ref<ItemVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const itemFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ItemForm = {
+  id: undefined,
+  itemName: undefined,
+  itemKey: undefined,
+  userName: undefined,
+  password: undefined,
+  url: undefined,
+  status: undefined,
+  remark: undefined,
+}
+const data = reactive<PageData<ItemForm, ItemQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    itemName: undefined,
+    itemKey: undefined,
+    userName: undefined,
+    password: undefined,
+    url: undefined,
+    status: undefined,
+    platformCode: undefined,
+    params: {
+    }
+  },
+  rules: {
+    itemName: [
+      { required: true, message: "项目名不能为空", trigger: "blur" }
+    ],
+    itemKey: [
+      { required: true, message: "项目key不能为空", trigger: "blur" }
+    ],
+    userName: [
+      { required: true, message: "项目用户名不能为空", trigger: "blur" }
+    ],
+    password: [
+      { required: true, message: "项目密码不能为空", trigger: "blur" }
+    ],
+    url: [
+      { required: true, message: "项目url不能为空", trigger: "blur" }
+    ],
+    status: [
+      { required: true, message: "状态不能为空", trigger: "change" }
+    ],
+    remark: [
+      { required: true, message: "备注不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询第三方对接项目管理列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listItem(queryParams.value);
+  itemList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  itemFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ItemVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加第三方对接项目管理";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ItemVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getItem(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改第三方对接项目管理";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  itemFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateItem(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addItem(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ItemVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除第三方对接项目管理编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delItem(_ids);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('external/item/export', {
+    ...queryParams.value
+  }, `item_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 425 - 0
src/views/external/product/index.vue

@@ -0,0 +1,425 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <el-form-item label="项目" prop="itemKey">
+                  <el-select v-model="queryParams.itemKey" placeholder="请选择项目" clearable @change="handleQuery" style="width: 200px">
+                    <el-option
+                      v-for="item in itemList"
+                      :key="item.itemKey"
+                      :label="item.itemName"
+                      :value="item.itemKey"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="商品编号" prop="productNo">
+                  <el-input v-model="queryParams.productNo" placeholder="请输入商品编号" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="商品名称" prop="itemName">
+                  <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <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">
+      <el-table v-loading="loading" border :data="productList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="序号" type="index" width="80" align="center" />
+        <el-table-column label="图片" align="center" prop="productImage" width="100">
+          <template #default="scope">
+            <el-image
+              v-if="scope.row.productImage"
+              :src="scope.row.productImage"
+              :preview-src-list="[scope.row.productImage]"
+              fit="cover"
+              style="width: 50px; height: 50px"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column label="产品编号" align="center" prop="productNo" width="150" />
+        <el-table-column label="产品名称" align="center" prop="itemName" width="200" show-overflow-tooltip />
+        <el-table-column label="我方分类" align="center" prop="categoryName" width="120" show-overflow-tooltip />
+        <el-table-column label="第三方分类" align="center" width="120" show-overflow-tooltip>
+          <template #default="scope">
+            <span>{{ scope.row.categoryName || '-' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="品牌" align="center" prop="brandName" width="120" />
+        <el-table-column label="单位" align="center" prop="unitName" width="80" />
+        <el-table-column label="市场价" align="center" prop="marketPrice" width="100" />
+        <el-table-column label="平档价" align="center" prop="standardPrice" width="100" />
+        <el-table-column label="供应价" align="center" prop="purchasePrice" width="100" />
+        <el-table-column label="限定库存" align="center" prop="availableStock" width="100" />
+        <el-table-column label="可用库存" align="center" prop="availableStock" width="100" />
+        <el-table-column label="总订单" align="center" width="100">
+          <template #default="scope">
+            <span>0</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="上架状态" align="center" prop="productStatus" width="100">
+          <template #default="scope">
+            <el-tag v-if="scope.row.productStatus == '1'" type="success">已上架</el-tag>
+            <el-tag v-else-if="scope.row.productStatus == '0'" type="danger">已下架</el-tag>
+            <el-tag v-else type="info">上架中</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="推送状态" align="center" width="100">
+          <template #default="scope">
+            <el-tag v-if="scope.row.pushStatus == '1'" type="success">已推送</el-tag>
+            <el-tag v-else type="danger">未推送</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width" fixed="right">
+          <template #default="scope">
+            <el-tooltip content="编辑" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" >编辑</el-button>
+            </el-tooltip>
+            <el-tooltip content="上下架" placement="top">
+              <el-button link type="primary" @click="handleToggleStatus(scope.row)">{{ scope.row.productStatus == '1' ? '下架' : '上架' }}</el-button>
+            </el-tooltip>
+            <el-tooltip content="推送" placement="top">
+              <el-button link type="primary" @click="handlePush(scope.row)">推送</el-button>
+            </el-tooltip>
+          </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="dialog.title" v-model="dialog.visible" width="600px" append-to-body>
+      <el-form ref="productFormRef" :model="form" :rules="rules" label-width="130px">
+        <el-form-item label="第三方产品分类:" prop="externalCategoryId">
+          <el-cascader
+            v-model="form.externalCategoryId"
+            :options="externalCategoryList"
+            :props="cascaderProps"
+            placeholder="请选择第三方产品分类"
+            clearable
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="第三方平台售价:" prop="externalPrice">
+          <el-input-number 
+            v-model="form.externalPrice" 
+            :precision="2" 
+            :min="0" 
+            :controls="false" 
+            placeholder="请输入第三方平台售价"
+            style="width: 100%" 
+          />
+        </el-form-item>
+        <el-form-item label="备注:" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="warning" @click="cancel">取 消</el-button>
+          <el-button type="primary" :loading="buttonLoading" @click="submitForm">确 定</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Product" lang="ts">
+import { getThirdProductPage, getProduct, updateProduct, shelfReview, batchPushProduct } from '@/api/external/product';
+import { ThirdProductVO, ProductQuery, ProductVO, ProductForm } from '@/api/external/product/types';
+import { listItem } from '@/api/external/item';
+import { ItemVO } from '@/api/external/item/types';
+import { getProductCategoryTree } from '@/api/external/productCategory';
+import { ProductCategoryVO } from '@/api/external/productCategory/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const productList = ref<ThirdProductVO[]>([]);
+const itemList = ref<ItemVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const productFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+// 编辑模式标识
+const isEditMode = ref(false);
+
+const data = reactive<PageData<ProductForm, ProductQuery>>({
+  form: {
+    id: undefined,
+    productId: undefined,
+    itemId: undefined,
+    categoryId: undefined,
+    externalCategoryId: undefined,
+    externalPrice: 0,
+    pushStatus: 0,
+    remark: undefined
+  },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    productNo: undefined,
+    itemName: undefined,
+    itemKey: undefined,
+    params: {},
+    externalPrice: 0
+  },
+  rules: {
+    externalCategoryId: [{ required: true, message: '请选择第三方产品分类', trigger: 'change' }],
+    externalPrice: [{ required: true, message: '请输入第三方价格', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+// 外部分类列表(懒加载模式只需要初始化为空数组)
+const externalCategoryList = ref<ProductCategoryVO[]>([]);
+
+// 级联选择器配置(使用分类树接口全量加载)
+const cascaderProps = {
+  value: 'id',
+  label: 'label',  // 后端返回的是label字段,不是categoryName
+  children: 'children',
+  checkStrictly: true,
+  emitPath: false
+};
+
+/** 初始化第三方分类数据 */
+const initCategoryData = async () => {
+  try {
+    const res = await getProductCategoryTree();
+    externalCategoryList.value = res.data || [];
+  } catch (error) {
+    console.error('加载分类树失败:', error);
+    proxy?.$modal.msgError('加载分类数据失败');
+  }
+};
+
+
+/** 查询第三方商品列表 */
+const getList = async () => {
+  loading.value = true;
+  const params = {
+    ...queryParams.value
+  };
+  const res = await getThirdProductPage(params);
+  productList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 获取项目列表 */
+const getItemList = async () => {
+  const res = await listItem();
+  console.log('listItem response:', res);
+  // 兼容后端返回结构:rows 或 data
+  itemList.value = (res as any).rows || res.data || [];
+  console.log('itemList.value:', itemList.value);
+  // 默认选择第一个项目
+  if (itemList.value.length > 0 && !queryParams.value.itemKey) {
+    queryParams.value.itemKey = itemList.value[0].itemKey;
+    // 自动触发查询
+    await getList();
+  }
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {
+    id: undefined,
+    productId: undefined,
+    itemId: undefined,
+    categoryId: undefined,
+    externalCategoryId: undefined,
+    externalPrice: 0,
+    pushStatus: 0,
+    remark: undefined
+  };
+  productFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ThirdProductVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  proxy?.$modal.msgWarning("暂不支持新增");
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ThirdProductVO) => {
+  reset();
+  isEditMode.value = true;
+  dialog.title = '编辑对接信息';
+  
+  try {
+    // 确保分类数据已加载
+    if (externalCategoryList.value.length === 0) {
+      await initCategoryData();
+    }
+    
+    // 通过商品ID获取对接信息
+    const res = await getProduct(row!.id);
+    const productInfo = res.data;
+    
+    form.value = {
+      id: productInfo.id,
+      productId: productInfo.productId,
+      itemId: productInfo.itemId,
+      categoryId: productInfo.categoryId,
+      externalCategoryId: productInfo.externalCategoryId,
+      externalPrice: productInfo.externalPrice,
+      pushStatus: productInfo.pushStatus,
+      remark: productInfo.remark
+    };
+    
+    dialog.visible = true;
+  } catch (error) {
+    console.error('获取对接信息失败:', error);
+    proxy?.$modal.msgError('获取对接信息失败');
+  }
+}
+
+/** 提交按钮 */
+const submitForm = async () => {
+  if (!productFormRef.value) return;
+  
+  await productFormRef.value.validate(async (valid) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (isEditMode.value) {
+          // 编辑模式
+          await updateProduct(form.value);
+          proxy?.$modal.msgSuccess('编辑成功');
+        } else {
+          // 新增模式(如果需要)
+          proxy?.$modal.msgWarning('暂不支持新增');
+          return;
+        }
+        
+        dialog.visible = false;
+        await getList();
+      } catch (error) {
+        console.error('操作失败:', error);
+        proxy?.$modal.msgError(isEditMode.value ? '编辑失败' : '新增失败');
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ThirdProductVO) => {
+  proxy?.$modal.msgWarning("暂不支持删除");
+}
+
+/** 上下架操作 */
+const handleToggleStatus = async (row: ThirdProductVO) => {
+  const action = row.productStatus == '1' ? '下架' : '上架';
+  const newStatus = row.productStatus == '1' ? '0' : '1';
+  
+  try {
+    await proxy?.$modal.confirm(`确认${action}该商品吗?`);
+    
+    // 调用上下架接口
+    await shelfReview({
+      id: row.id,
+      productStatus: newStatus
+    });
+    
+    proxy?.$modal.msgSuccess(`${action}成功`);
+    
+    // 刷新列表
+    await getList();
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('上下架操作失败:', error);
+      proxy?.$modal.msgError(`${action}失败`);
+    }
+  }
+}
+
+/** 推送操作 */
+const handlePush = async (row: ThirdProductVO) => {
+  try {
+    await proxy?.$modal.confirm('确认推送该商品吗?');
+    
+    // 调用批量推送接口,传入单个商品ID
+    await batchPushProduct(row.id);
+    
+    proxy?.$modal.msgSuccess('推送成功');
+    
+    // 刷新列表
+    await getList();
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('推送操作失败:', error);
+      proxy?.$modal.msgError('推送失败');
+    }
+  }
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('external/product/export', {
+    ...queryParams.value
+  }, `product_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  // 先加载项目列表,内部会自动触发查询
+  getItemList();
+  // 初始化第三方分类数据
+  initCategoryData();
+});
+</script>

+ 251 - 0
src/views/external/productBrand/index.vue

@@ -0,0 +1,251 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-form-item label="项目" prop="itemKey">
+              <el-select v-model="queryParams.itemKey" placeholder="请选择项目" clearable @change="handleQuery" style="width: 200px">
+                <el-option
+                  v-for="item in itemList"
+                  :key="item.itemKey"
+                  :label="item.itemName"
+                  :value="item.itemKey"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="官方品牌名称" prop="brandName">
+              <el-input v-model="queryParams.brandName" placeholder="请输入官方品牌名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="第三方品牌名称" prop="thirdPartyBrandName">
+              <el-input v-model="queryParams.thirdPartyBrandName" placeholder="请输入第三方品牌名称" clearable @keyup.enter="handleQuery" />
+            </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>
+        <el-row :gutter="10" class="mb8">
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="productBrandList" @selection-change="handleSelectionChange">
+        <el-table-column type="index" label="序号" width="55" align="center" />
+        <el-table-column label="官方品牌Id" align="center" prop="productBrandId" />
+        <el-table-column label="官方品牌名称" align="center" prop="brandName" />
+        <el-table-column label="第三方品牌Id" align="center" prop="id" />
+        <el-table-column label="第三方品牌名称" align="center" prop="thirdPartyBrandName" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100">
+          <template #default="scope">
+            <el-tooltip content="绑定官方品牌" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
+            </el-tooltip>
+          </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="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="productBrandFormRef" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="第三方品牌名称">
+          <el-input v-model="form.thirdPartyBrandName" disabled />
+        </el-form-item>
+        <el-form-item label="绑定官方品牌" prop="productBrandId">
+          <el-select
+            v-model="form.productBrandId"
+            placeholder="请选择官方品牌"
+            filterable
+            clearable
+            style="width: 100%"
+            @change="handleBrandChange"
+          >
+            <el-option
+              v-for="item in brandList"
+              :key="item.id"
+              :label="item.brandName"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="官方品牌名称">
+          <el-input v-model="form.brandName" disabled placeholder="选择官方品牌后自动填充" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="ProductBrand" lang="ts">
+import { listProductBrand, getProductBrand, updateProductBrand } from '@/api/external/productBrand';
+import { ProductBrandVO, ProductBrandQuery, ProductBrandForm } from '@/api/external/productBrand/types';
+import { listBrand } from '@/api/product/brand';
+import { BrandVO } from '@/api/product/brand/types';
+import { listItem } from '@/api/external/item';
+import { ItemVO } from '@/api/external/item/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const productBrandList = ref<ProductBrandVO[]>([]);
+const brandList = ref<BrandVO[]>([]);
+const itemList = ref<ItemVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const productBrandFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ProductBrandForm = {
+  id: undefined,
+  productBrandId: undefined,
+  brandName: undefined,
+  thirdPartyBrandName: undefined,
+}
+const data = reactive<PageData<ProductBrandForm, ProductBrandQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    brandName: undefined,
+    thirdPartyBrandName: undefined,
+    itemKey: undefined,
+    params: {
+    }
+  },
+  rules: {
+    productBrandId: [
+      { required: true, message: "请选择官方品牌", trigger: "change" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询第三方产品品牌信息列表 */
+const getList = async () => {
+  loading.value = true;
+  const params = {
+    ...queryParams.value
+  };
+  const res = await listProductBrand(params);
+  productBrandList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 获取官方品牌列表 */
+const getBrandList = async () => {
+  const res = await listBrand();
+  brandList.value = res.data;
+}
+
+/** 获取项目列表 */
+const getItemList = async () => {
+  const res = await listItem();
+  // 兼容后端返回结构:rows 或 data
+  itemList.value = (res as any).rows || res.data || [];
+  // 默认选择第一个项目
+  if (itemList.value.length > 0 && !queryParams.value.itemKey) {
+    queryParams.value.itemKey = itemList.value[0].itemKey;
+    // 自动触发查询
+    await getList();
+  }
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  productBrandFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ProductBrandVO[]) => {
+  ids.value = selection.map(item => item.id);
+}
+
+/** 绑定官方品牌操作 */
+const handleUpdate = async (row?: ProductBrandVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getProductBrand(_id, queryParams.value.itemKey);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "绑定官方品牌";
+}
+
+/** 官方品牌选择变化 */
+const handleBrandChange = (brandId: string | number) => {
+  const selectedBrand = brandList.value.find(item => item.id === brandId);
+  if (selectedBrand) {
+    form.value.brandName = selectedBrand.brandName;
+  } else {
+    form.value.brandName = undefined;
+  }
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  productBrandFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      await updateProductBrand(form.value).finally(() => buttonLoading.value = false);
+      proxy?.$modal.msgSuccess("绑定成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('external/productBrand/export', {
+    ...queryParams.value
+  }, `productBrand_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  // 先加载项目列表,内部会自动触发查询
+  getItemList();
+  getBrandList();
+});
+</script>

+ 307 - 0
src/views/external/productCategory/index.vue

@@ -0,0 +1,307 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-form-item label="项目" prop="itemKey">
+              <el-select v-model="queryParams.itemKey" placeholder="请选择项目" clearable @change="handleQuery" style="width: 200px">
+                <el-option
+                  v-for="item in itemList"
+                  :key="item.itemKey"
+                  :label="item.itemName"
+                  :value="item.itemKey"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="分类名称" prop="categoryName">
+              <el-input v-model="queryParams.categoryName" placeholder="请输入分类名称" clearable @keyup.enter="handleQuery" />
+            </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="hover">
+      <template #header>
+        <el-row :gutter="10">
+          <el-col :span="1.5">
+            <el-button  type="primary" plain icon="Plus" @click="handleAdd()">新增分类</el-button>
+          </el-col>
+          <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
+        </el-row>
+      </template>
+      <el-table
+        ref="productCategoryTableRef"
+        v-loading="loading"
+        :data="productCategoryList"
+        row-key="id"
+        border
+        lazy
+        :load="loadChildren"
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+      >
+        <el-table-column prop="categoryName" label="分类名称" width="260"></el-table-column>
+        <el-table-column prop="classLevel" align="center" label="分类级别" width="150">
+          <template #default="scope">
+            <span>{{ scope.row.classLevel }}级</span>
+          </template>
+        </el-table-column>
+        
+        <el-table-column prop="platform" align="center" label="平台" width="150">
+          <template #default="scope">
+            <span>{{ scope.row.platform === 0 ? 'Web' : scope.row.platform === 1 ? '小程序' : '未知' }}</span>
+          </template>
+        </el-table-column>
+      
+        <el-table-column fixed="right" align="center" label="操作">
+          <template #default="scope">
+            <el-tooltip content="新增分类" placement="top">
+              <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
+            </el-tooltip>
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+    <!-- 添加或修改产品分类对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-body width="700px">
+      <el-form ref="productCategoryFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-row :gutter="20">
+          <el-col v-if="form.parentId !== 0" :span="24">
+            <el-form-item label="上级分类" prop="parentId">
+              <el-tree-select
+                v-model="form.parentId"
+                :data="categoryOptions"
+                :props="{ value: 'id', label: 'categoryName', children: 'children' } as any"
+                value-key="id"
+                placeholder="选择上级分类"
+                check-strictly
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="分类名称" prop="categoryName">
+              <el-input v-model="form.categoryName" placeholder="请输入分类名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="分类编号" prop="categoryNo">
+              <el-input v-model="form.categoryNo" placeholder="请输入分类编号" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">确 认</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="ProductCategory" lang="ts">
+import { listProductCategory, getProductCategory, delProductCategory, addProductCategory, updateProductCategory, listProductCategoryExcludeChild } from '@/api/external/productCategory';
+import { ProductCategoryVO, ProductCategoryQuery, ProductCategoryForm } from '@/api/external/productCategory/types';
+import { listItem } from '@/api/external/item';
+import { ItemVO } from '@/api/external/item/types';
+
+interface CategoryOptionsType {
+  id: number | string;
+  categoryName: string;
+  children: CategoryOptionsType[];
+}
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const productCategoryList = ref<ProductCategoryVO[]>([]);
+const itemList = ref<ItemVO[]>([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const categoryOptions = ref<CategoryOptionsType[]>([]);
+// 存储懒加载的子节点加载状态和 resolve 函数
+const lazyTreeNodeMap = ref<Map<string | number, { tree: any; treeNode: any; resolve: (data: ProductCategoryVO[]) => void }>>(new Map());
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const productCategoryTableRef = ref<ElTableInstance>();
+const queryFormRef = ref<ElFormInstance>();
+const productCategoryFormRef = ref<ElFormInstance>();
+
+const initFormData: ProductCategoryForm = {
+  id: undefined,
+  categoryName: undefined,
+  categoryNo: undefined,
+  parentId: undefined,
+  platform: undefined,
+  classDescription: undefined
+}
+const data = reactive<PageData<ProductCategoryForm, Partial<ProductCategoryQuery>>>({
+  form: {...initFormData},
+  queryParams: {
+    categoryName: undefined,
+    itemKey: undefined
+  },
+  rules: {
+    categoryName: [
+      { required: true, message: "分类名称不能为空", trigger: "blur" }
+    ]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品分类列表(只加载顶级分类) */
+const getList = async () => {
+  loading.value = true;
+  // 清空懒加载缓存
+  lazyTreeNodeMap.value.clear();
+  // 只加载顶级分类(parentId = 0)
+  const params = {
+    ...queryParams.value,
+    parentId: 0
+  };
+  const res = await listProductCategory(params);
+  const data = (res as any).rows || res.data || [];
+  // 根据 hasChildren 字段标记是否有子节点
+  productCategoryList.value = data.map((item: ProductCategoryVO) => ({
+    ...item,
+    hasChildren: item.hasChildren ?? (item.classLevel < 3) // 如果后端没返回 hasChildren,根据 classLevel 判断
+  }));
+  loading.value = false;
+}
+
+/** 获取项目列表 */
+const getItemList = async () => {
+  const res = await listItem();
+  // 兼容后端返回结构:rows 或 data
+  itemList.value = (res as any).rows || res.data || [];
+  // 默认选择第一个项目
+  if (itemList.value.length > 0 && !queryParams.value.itemKey) {
+    queryParams.value.itemKey = itemList.value[0].itemKey;
+    // 自动触发查询
+    await getList();
+  }
+}
+
+/** 懒加载子节点 */
+const loadChildren = async (row: ProductCategoryVO, treeNode: any, resolve: (data: ProductCategoryVO[]) => void) => {
+  // 存储加载状态,便于刷新时重新加载
+  lazyTreeNodeMap.value.set(row.id, { tree: row, treeNode, resolve });
+  try {
+    const res = await listProductCategory({ parentId: row.id, itemKey: queryParams.value.itemKey });
+    const data = (res as any).rows || res.data || [];
+    // 标记子节点是否还有子级
+    const children = data.map((item: ProductCategoryVO) => ({
+      ...item,
+      hasChildren: item.hasChildren ?? (item.classLevel < 3)
+    }));
+    resolve(children);
+  } catch (error) {
+    resolve([]);
+  }
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  productCategoryFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 刷新已展开的懒加载节点 */
+const refreshLazyNodes = () => {
+  lazyTreeNodeMap.value.forEach(async ({ tree, treeNode, resolve }, key) => {
+    // 重置已加载状态
+    if (treeNode.loaded) {
+      treeNode.loaded = false;
+    }
+    // 重新加载
+    await loadChildren(tree, treeNode, resolve);
+  });
+};
+
+/** 新增按钮操作 */
+const handleAdd = async (row?: ProductCategoryVO) => {
+  reset();
+  const res = await listProductCategory({ itemKey: queryParams.value.itemKey });
+  const responseData = (res as any).rows || res.data || [];
+  const data = proxy?.handleTree<CategoryOptionsType>(responseData, 'id');
+  if (data) {
+    categoryOptions.value = data;
+    if (row && row.id) {
+      form.value.parentId = row?.id;
+    }
+    dialog.visible = true;
+    dialog.title = '添加产品分类';
+  }
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row: ProductCategoryVO) => {
+  reset();
+  const res = await getProductCategory(row.id, queryParams.value.itemKey);
+  form.value = res.data;
+  const response = await listProductCategory({ itemKey: queryParams.value.itemKey });
+  // 根据后端返回结构,如果是 TableDataInfo 则用 rows,如果是直接数组则用 data
+  const responseData = (response as any).rows || response.data || [];
+  const data = proxy?.handleTree<CategoryOptionsType>(responseData, 'id');
+  if (data) {
+    categoryOptions.value = data;
+  }
+  dialog.visible = true;
+  dialog.title = '修改产品分类';
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  productCategoryFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      form.value.id ? await updateProductCategory(form.value) : await addProductCategory(form.value);
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row: ProductCategoryVO) => {
+  await proxy?.$modal.confirm('是否确认删除名称为"' + row.categoryName + '"的数据项?');
+  await delProductCategory(row.id, queryParams.value.itemKey);
+  await getList();
+  proxy?.$modal.msgSuccess('删除成功');
+}
+
+onMounted(() => {
+  // 先加载项目列表,内部会自动触发查询
+  getItemList();
+});
+</script>

+ 319 - 0
src/views/external/productChangeLog/index.vue

@@ -0,0 +1,319 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-form-item label="项目" prop="itemKey">
+              <el-select v-model="queryParams.itemKey" placeholder="请选择项目" clearable @change="handleQuery" style="width: 200px">
+                <el-option
+                  v-for="item in itemList"
+                  :key="item.itemKey"
+                  :label="item.itemName"
+                  :value="item.itemKey"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="项目id" prop="itemId">
+              <el-input v-model="queryParams.itemId" placeholder="请输入项目id" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="商品id" prop="productId">
+              <el-input v-model="queryParams.productId" placeholder="请输入商品id" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="平台标识" prop="platformCode">
+              <el-input v-model="queryParams.platformCode" placeholder="请输入平台标识" clearable @keyup.enter="handleQuery" />
+            </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>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['external:productChangeLog:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['external:productChangeLog:edit']">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['external:productChangeLog:remove']">删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['external:productChangeLog:export']">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="productChangeLogList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="" align="center" prop="id" v-if="true" />
+        <el-table-column label="项目id" align="center" prop="itemId" />
+        <el-table-column label="商品id" align="center" prop="productId" />
+        <el-table-column label="type=2商品
+价格变更,后
+续会调用价格
+接口。
+type=4代表
+商品上下架变
+更消息,后续
+会调用上下架
+状态接口。
+type=6代表
+添加、删除商
+品池内的商
+品,触发保存
+商品流程,依
+次调用商品详
+情等接口获取
+商品信息。
+type=16商品
+介绍及规格参
+数变更消息,
+调用商品详情
+等接口更新商
+品信息。" align="center" prop="type" />
+        <el-table-column label="状态" align="center" prop="status" />
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['external:productChangeLog:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['external:productChangeLog:remove']"></el-button>
+            </el-tooltip>
+          </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="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="productChangeLogFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="项目id" prop="itemId">
+          <el-input v-model="form.itemId" placeholder="请输入项目id" />
+        </el-form-item>
+        <el-form-item label="商品id" prop="productId">
+          <el-input v-model="form.productId" placeholder="请输入商品id" />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="ProductChangeLog" lang="ts">
+import { listProductChangeLog, getProductChangeLog, delProductChangeLog, addProductChangeLog, updateProductChangeLog } from '@/api/external/productChangeLog';
+import { ProductChangeLogVO, ProductChangeLogQuery, ProductChangeLogForm } from '@/api/external/productChangeLog/types';
+import { listItem } from '@/api/external/item';
+import { ItemVO } from '@/api/external/item/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const productChangeLogList = ref<ProductChangeLogVO[]>([]);
+const itemList = ref<ItemVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const productChangeLogFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ProductChangeLogForm = {
+  id: undefined,
+  itemId: undefined,
+  productId: undefined,
+  type: undefined,
+  status: undefined,
+  remark: undefined,
+}
+const data = reactive<PageData<ProductChangeLogForm, ProductChangeLogQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    itemId: undefined,
+    productId: undefined,
+    type: undefined,
+    status: undefined,
+    platformCode: undefined,
+    itemKey: undefined,
+    params: {
+    }
+  },
+  rules: {
+    itemId: [
+      { required: true, message: "项目id不能为空", trigger: "blur" }
+    ],
+    productId: [
+      { required: true, message: "商品id不能为空", trigger: "blur" }
+    ],
+    type: [
+      { required: true, message: "type=2商品
+价格变更,后
+续会调用价格
+接口。
+type=4代表
+商品上下架变
+更消息,后续
+会调用上下架
+状态接口。
+type=6代表
+添加、删除商
+品池内的商
+品,触发保存
+商品流程,依
+次调用商品详
+情等接口获取
+商品信息。
+type=16商品
+介绍及规格参
+数变更消息,
+调用商品详情
+等接口更新商
+品信息。不能为空", trigger: "change" }
+    ],
+    status: [
+      { required: true, message: "状态不能为空", trigger: "change" }
+    ],
+    remark: [
+      { required: true, message: "备注不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询商品变更消息记录列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listProductChangeLog(queryParams.value);
+  productChangeLogList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 获取项目列表 */
+const getItemList = async () => {
+  const res = await listItem();
+  // 兼容后端返回结构:rows 或 data
+  itemList.value = (res as any).rows || res.data || [];
+  // 默认选择第一个项目
+  if (itemList.value.length > 0 && !queryParams.value.itemKey) {
+    queryParams.value.itemKey = itemList.value[0].itemKey;
+    // 自动触发查询
+    await getList();
+  }
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  productChangeLogFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ProductChangeLogVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加商品变更消息记录";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ProductChangeLogVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getProductChangeLog(_id, queryParams.value.itemKey);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改商品变更消息记录";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  productChangeLogFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateProductChangeLog(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addProductChangeLog(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ProductChangeLogVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除商品变更消息记录编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delProductChangeLog(_ids, queryParams.value.itemKey);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('external/productChangeLog/export', {
+    ...queryParams.value
+  }, `productChangeLog_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  // 先加载项目列表,内部会自动触发查询
+  getItemList();
+});
+</script>

+ 299 - 0
src/views/external/pushPoolLog/index.vue

@@ -0,0 +1,299 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-form-item label="项目" prop="itemKey">
+              <el-select v-model="queryParams.itemKey" placeholder="请选择项目" clearable @change="handleQuery" style="width: 200px">
+                <el-option
+                  v-for="item in itemList"
+                  :key="item.itemKey"
+                  :label="item.itemName"
+                  :value="item.itemKey"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="项目id" prop="itemId">
+              <el-input v-model="queryParams.itemId" placeholder="请输入项目id" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="商品池id" prop="poolId">
+              <el-input v-model="queryParams.poolId" placeholder="请输入商品池id" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="操作人id" prop="operatorId">
+              <el-input v-model="queryParams.operatorId" placeholder="请输入操作人id" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="失败原因" prop="reason">
+              <el-input v-model="queryParams.reason" placeholder="请输入失败原因" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="平台标识" prop="platformCode">
+              <el-input v-model="queryParams.platformCode" placeholder="请输入平台标识" clearable @keyup.enter="handleQuery" />
+            </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>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['external:pushPoolLog:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['external:pushPoolLog:edit']">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['external:pushPoolLog:remove']">删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['external:pushPoolLog:export']">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="pushPoolLogList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="" align="center" prop="id" v-if="true" />
+        <el-table-column label="项目id" align="center" prop="itemId" />
+        <el-table-column label="商品池id" align="center" prop="poolId" />
+        <el-table-column label="操作人id" align="center" prop="operatorId" />
+        <el-table-column label="推送状态 0成功 1失败" align="center" prop="pushStatus" />
+        <el-table-column label="状态" align="center" prop="status" />
+        <el-table-column label="失败原因" align="center" prop="reason" />
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['external:pushPoolLog:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['external:pushPoolLog:remove']"></el-button>
+            </el-tooltip>
+          </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="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="pushPoolLogFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="项目id" prop="itemId">
+          <el-input v-model="form.itemId" placeholder="请输入项目id" />
+        </el-form-item>
+        <el-form-item label="商品池id" prop="poolId">
+          <el-input v-model="form.poolId" placeholder="请输入商品池id" />
+        </el-form-item>
+        <el-form-item label="操作人id" prop="operatorId">
+          <el-input v-model="form.operatorId" placeholder="请输入操作人id" />
+        </el-form-item>
+        <el-form-item label="失败原因" prop="reason">
+          <el-input v-model="form.reason" placeholder="请输入失败原因" />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="PushPoolLog" lang="ts">
+import { listPushPoolLog, getPushPoolLog, delPushPoolLog, addPushPoolLog, updatePushPoolLog } from '@/api/external/pushPoolLog';
+import { PushPoolLogVO, PushPoolLogQuery, PushPoolLogForm } from '@/api/external/pushPoolLog/types';
+import { listItem } from '@/api/external/item';
+import { ItemVO } from '@/api/external/item/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const pushPoolLogList = ref<PushPoolLogVO[]>([]);
+const itemList = ref<ItemVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const pushPoolLogFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: PushPoolLogForm = {
+  id: undefined,
+  itemId: undefined,
+  poolId: undefined,
+  operatorId: undefined,
+  pushStatus: undefined,
+  status: undefined,
+  reason: undefined,
+  remark: undefined,
+}
+const data = reactive<PageData<PushPoolLogForm, PushPoolLogQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    itemId: undefined,
+    poolId: undefined,
+    operatorId: undefined,
+    pushStatus: undefined,
+    status: undefined,
+    reason: undefined,
+    platformCode: undefined,
+    itemKey: undefined,
+    params: {
+    }
+  },
+  rules: {
+    itemId: [
+      { required: true, message: "项目id不能为空", trigger: "blur" }
+    ],
+    poolId: [
+      { required: true, message: "商品池id不能为空", trigger: "blur" }
+    ],
+    operatorId: [
+      { required: true, message: "操作人id不能为空", trigger: "blur" }
+    ],
+    pushStatus: [
+      { required: true, message: "推送状态 0成功 1失败不能为空", trigger: "change" }
+    ],
+    status: [
+      { required: true, message: "状态不能为空", trigger: "change" }
+    ],
+    reason: [
+      { required: true, message: "失败原因不能为空", trigger: "blur" }
+    ],
+    remark: [
+      { required: true, message: "备注不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询商品池推送记录列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listPushPoolLog(queryParams.value);
+  pushPoolLogList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 获取项目列表 */
+const getItemList = async () => {
+  const res = await listItem();
+  // 兼容后端返回结构:rows 或 data
+  itemList.value = (res as any).rows || res.data || [];
+  // 默认选择第一个项目
+  if (itemList.value.length > 0 && !queryParams.value.itemKey) {
+    queryParams.value.itemKey = itemList.value[0].itemKey;
+    // 自动触发查询
+    await getList();
+  }
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  pushPoolLogFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: PushPoolLogVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加商品池推送记录";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: PushPoolLogVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getPushPoolLog(_id, queryParams.value.itemKey);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改商品池推送记录";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  pushPoolLogFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updatePushPoolLog(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addPushPoolLog(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: PushPoolLogVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除商品池推送记录编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delPushPoolLog(_ids, queryParams.value.itemKey);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('external/pushPoolLog/export', {
+    ...queryParams.value
+  }, `pushPoolLog_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  // 先加载项目列表,内部会自动触发查询
+  getItemList();
+});
+</script>

+ 8 - 8
src/views/login.vue

@@ -5,12 +5,12 @@
         <h3 class="title">{{ title }}</h3>
         <lang-select />
       </div>
-      <el-form-item v-if="tenantEnabled" prop="tenantId">
-        <el-select v-model="loginForm.tenantId" filterable :placeholder="proxy.$t('login.selectPlaceholder')" style="width: 100%">
-          <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
-          <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
-        </el-select>
-      </el-form-item>
+<!--      <el-form-item v-if="tenantEnabled" prop="tenantId">-->
+<!--        <el-select v-model="loginForm.tenantId" filterable :placeholder="proxy.$t('login.selectPlaceholder')" style="width: 100%">-->
+<!--          <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>-->
+<!--          <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
       <el-form-item prop="username">
         <el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" :placeholder="proxy.$t('login.username')">
           <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
@@ -96,8 +96,8 @@ const { t } = useI18n();
 
 const loginForm = ref<LoginData>({
   tenantId: '000000',
-  username: 'admin',
-  password: 'admin123',
+  username: 'apiadmin',
+  password: '123456',
   rememberMe: false,
   code: '',
   uuid: ''

+ 258 - 0
src/views/product/attributes/edit.vue

@@ -0,0 +1,258 @@
+<template>
+  <div class="p-4">
+    <el-card shadow="never">
+      <template #header>
+        <div class="flex justify-between items-center">
+          <span class="text-lg font-bold">{{ pageTitle }}</span>
+          <el-button link type="primary" @click="goBack">返回</el-button>
+        </div>
+      </template>
+
+      <el-form ref="attributesFormRef" :model="form" :rules="rules" label-width="140px" class="max-w-5xl">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="品牌名称" prop="productAttributesName">
+              <el-input v-model="form.productAttributesName" placeholder="请输入品牌名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌首字母" prop="productAttributesCode">
+              <el-input v-model="form.productAttributesCode" placeholder="请输入品牌首字母" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="品牌名称(英文)" prop="categoryId">
+              <el-tree-select
+                v-model="form.categoryId"
+                :data="categoryOptions"
+                :props="{ value: 'id', label: 'label', children: 'children' }"
+                check-strictly
+                :render-after-expand="false"
+                clearable
+                placeholder="请选择关联类别(必须选择第三级)"
+              >
+                <template #default="{ node, data }">
+                  <span :style="{ color: !node.isLeaf && node.level < 3 ? '#999' : '' }">
+                    {{ getCategoryFullPath(data.id) }}
+                  </span>
+                </template>
+              </el-tree-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌推荐系数" prop="recommendValue">
+              <el-input-number 
+                v-model="extendFormData.recommendValue" 
+                :min="0" 
+                :max="9999" 
+                controls-position="right" 
+                style="width: 100%" 
+                placeholder="请输入推荐系数"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="品牌标题" prop="brandTitle">
+          <el-input v-model="extendFormData.brandTitle" placeholder="请输入内容" />
+        </el-form-item>
+
+        <el-form-item label="品牌LOGO" prop="brandLogo">
+          <image-upload v-model="extendFormData.brandLogo" />
+        </el-form-item>
+
+        <el-form-item label="品牌故事" prop="brandStory">
+          <el-input v-model="extendFormData.brandStory" type="textarea" :rows="3" placeholder="请输入内容" />
+        </el-form-item>
+
+        <el-form-item label="是否显示" prop="isFilter">
+          <el-radio-group v-model="form.isFilter">
+            <el-radio :value="1">是</el-radio>
+            <el-radio :value="0">否</el-radio>
+          </el-radio-group>
+          <div class="text-gray-500 text-sm mt-2">
+            当品牌下还没有商品的时候。分类的品牌区不会不显示品牌。
+          </div>
+        </el-form-item>
+
+        <el-form-item label="品牌介绍" prop="brandDescribe">
+          <editor v-model="extendFormData.brandDescribe" :min-height="192" />
+        </el-form-item>
+
+        <el-form-item>
+          <el-button type="primary" :loading="buttonLoading" @click="submitForm">保存</el-button>
+          <el-button @click="goBack">取消</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts" name="AttributesEdit">
+import { getAttributes, addAttributes, updateAttributes } from '@/api/product/attributes';
+import { AttributesForm } from '@/api/product/attributes/types';
+import { categoryTree } from '@/api/product/base';
+import { categoryTreeVO } from '@/api/product/category/types';
+
+const route = useRoute();
+const router = useRouter();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const attributesFormRef = ref<ElFormInstance>();
+const buttonLoading = ref(false);
+const categoryOptions = ref<categoryTreeVO[]>([]);
+
+const pageTitle = computed(() => {
+  return route.query.type === 'add' ? '新增属性' : '编辑属性';
+});
+
+const initFormData: AttributesForm = {
+  id: undefined,
+  categoryId: undefined,
+  productAttributesCode: undefined,
+  productAttributesName: undefined,
+  isOptional: 1,
+  entryMethod: '1',
+  isFilter: 1,
+  attributesList: undefined,
+  required: 0,
+  remark: undefined,
+}
+
+// 扩展表单字段(用于品牌风格的表单)
+const extendFormData = reactive({
+  brandTitle: undefined as string | undefined,
+  brandLogo: undefined as string | undefined,
+  brandStory: undefined as string | undefined,
+  brandDescribe: undefined as string | undefined,
+  recommendValue: undefined as number | undefined,
+})
+
+const form = ref<AttributesForm>({ ...initFormData });
+
+const rules = reactive({
+  categoryId: [
+    { required: true, message: "商品类别不能为空", trigger: "change" },
+    { 
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value) {
+          callback();
+          return;
+        }
+        // 检查是否为第三级类别
+        const isThirdLevel = checkIsThirdLevel(value);
+        if (!isThirdLevel) {
+          callback(new Error('请选择第三级类别'));
+        } else {
+          callback();
+        }
+      },
+      trigger: "change"
+    }
+  ],
+  productAttributesCode: [
+    { required: true, message: "品牌首字母不能为空", trigger: "blur" }
+  ],
+  productAttributesName: [
+    { required: true, message: "品牌名称不能为空", trigger: "blur" }
+  ],
+  brandTitle: [
+    { required: false, message: "品牌标题不能为空", trigger: "blur" }
+  ],
+  recommendValue: [
+    { required: false, message: "推荐系数不能为空", trigger: "blur" }
+  ]
+});
+
+/** 检查是否为第三级类别 */
+const checkIsThirdLevel = (categoryId: string | number): boolean => {
+  const findLevel = (nodes: categoryTreeVO[], targetId: string | number, level: number = 1): number | null => {
+    for (const node of nodes) {
+      if (node.id === targetId) {
+        return level;
+      }
+      if (node.children && node.children.length > 0) {
+        const result = findLevel(node.children, targetId, level + 1);
+        if (result !== null) return result;
+      }
+    }
+    return null;
+  };
+
+  const level = findLevel(categoryOptions.value, categoryId);
+  return level === 3;
+};
+
+/** 获取分类完整路径 */
+const getCategoryFullPath = (categoryId: string | number): string => {
+  const findPath = (nodes: categoryTreeVO[], targetId: string | number, path: string[] = []): string[] | null => {
+    for (const node of nodes) {
+      const currentPath = [...path, node.label];
+      if (node.id === targetId) {
+        return currentPath;
+      }
+      if (node.children && node.children.length > 0) {
+        const result = findPath(node.children, targetId, currentPath);
+        if (result) return result;
+      }
+    }
+    return null;
+  };
+
+  const pathArray = findPath(categoryOptions.value, categoryId);
+  return pathArray ? pathArray.join(' / ') : '';
+}
+
+/** 查询分类树 */
+const getCategoryTree = async () => {
+  const res = await categoryTree();
+  categoryOptions.value = res.data || [];
+}
+
+/** 获取属性详情 */
+const getDetail = async () => {
+  const id = route.query.id as string;
+  if (id && route.query.type !== 'add') {
+    const res = await getAttributes(id);
+    Object.assign(form.value, {
+      ...res.data,
+      isOptional: typeof res.data.isOptional === 'string' ? Number(res.data.isOptional) : res.data.isOptional,
+      isFilter: typeof res.data.isFilter === 'string' ? Number(res.data.isFilter) : res.data.isFilter,
+      required: typeof res.data.required === 'string' ? Number(res.data.required) : res.data.required,
+    });
+  }
+};
+
+/** 提交表单 */
+const submitForm = () => {
+  attributesFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (form.value.id) {
+          await updateAttributes(form.value);
+        } else {
+          await addAttributes(form.value);
+        }
+        proxy?.$modal.msgSuccess("操作成功");
+        goBack();
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 返回 */
+const goBack = () => {
+  router.back();
+};
+
+onMounted(() => {
+  getCategoryTree();
+  getDetail();
+});
+</script>

+ 283 - 0
src/views/product/attributes/index.vue

@@ -0,0 +1,283 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-form-item label="属性名称" prop="productAttributesName">
+              <el-input v-model="queryParams.productAttributesName" placeholder="请输入属性名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="商品类别" prop="categoryName">
+              <el-input v-model="queryParams.categoryName" placeholder="请输入分类名称" clearable @keyup.enter="handleQuery" />
+            </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">
+    
+
+      <el-table v-loading="loading" border :data="attributesList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="编号" align="center" prop="id" width="120" />
+        <el-table-column label="属性名称" align="center" prop="productAttributesName" />
+        <el-table-column label="商品类别" align="center" prop="categoryName" min-width="200" />
+        <el-table-column label="属性是否可选" align="center" prop="isOptional" width="120">
+          <template #default="scope">
+            <dict-tag :options="[
+              { label: '是', value: '1' },
+              { label: '否', value: '0' }
+            ]" :value="String(scope.row.isOptional)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="属性值的输入方式" align="center" prop="entryMethod" width="140">
+          <template #default="scope">
+            <dict-tag :options="[
+              { label: '唯一属性', value: '1' },
+              { label: '单选属性', value: '0' },
+              { label: '复选属性', value: '2' }
+            ]" :value="String(scope.row.entryMethod)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="可选值列表" align="center" prop="attributesList" show-overflow-tooltip />
+        <el-table-column label="是否必填" align="center" prop="required" width="100">
+          <template #default="scope">
+            <dict-tag :options="[
+              { label: '是', value: '1' },
+              { label: '否', value: '0' }
+            ]" :value="String(scope.row.required)" />
+          </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="dialog.title" v-model="dialog.visible" width="650px" append-to-body>
+      <el-form ref="attributesFormRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="属性名称" prop="productAttributesName">
+          <el-input v-model="form.productAttributesName" placeholder="请输入属性名称" />
+        </el-form-item>
+        <el-form-item label="关联类别" prop="categoryId">
+          <el-input v-model="form.categoryId" placeholder="请输入关联类别ID" />
+        </el-form-item>
+        <el-form-item label="是否可选" prop="isOptional">
+          <el-radio-group v-model="form.isOptional">
+            <el-radio value="1">唯一属性</el-radio>
+            <el-radio value="0">单选属性</el-radio>
+            <el-radio value="2">复选属性</el-radio>
+          </el-radio-group>
+          <div class="text-gray-500 text-sm mt-2">
+            选择"单选/复选属性"时,可以对商品属性设置多个值,同时还能对不同属性值设定不同的价格,用户购买商品时需要选择具体的属性值后才能加入购物车。用户购买商品时需要先选择具体的属性值。选择"唯一属性"时,商品的该属性值只能是唯一一个值,用户只能查看具体的属性值。
+          </div>
+        </el-form-item>
+        <el-form-item label="录入方式" prop="entryMethod">
+          <el-radio-group v-model="form.entryMethod">
+            <el-radio value="1">手工录入</el-radio>
+            <el-radio value="2">从下面的列表中选择</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="加入筛选" prop="isFilter">
+          <el-radio-group v-model="form.isFilter">
+            <el-radio value="1">是</el-radio>
+            <el-radio value="0">否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="是否必填" prop="required">
+          <el-radio-group v-model="form.required">
+            <el-radio value="1">是</el-radio>
+            <el-radio value="0">否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="可选值列表" prop="attributesList" v-if="form.entryMethod === 'select'">
+          <el-input 
+            v-model="form.attributesList" 
+            type="textarea" 
+            :rows="4" 
+            placeholder="请输入属性值,各属性值请用逗号隔开并来示意。例如:A4,70g,100g" 
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Attributes" lang="ts">
+import { listAttributes, getAttributes, delAttributes, addAttributes, updateAttributes } from '@/api/product/attributes';
+import { AttributesVO, AttributesQuery, AttributesForm } from '@/api/product/attributes/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
+
+const attributesList = ref<AttributesVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const attributesFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: AttributesForm = {
+  id: undefined,
+  categoryId: undefined,
+  productAttributesCode: undefined,
+  productAttributesName: undefined,
+  isOptional: '1',
+  entryMethod: '1',
+  isFilter: '1',
+  attributesList: undefined,
+  required: '0',
+  remark: undefined,
+}
+const data = reactive<PageData<AttributesForm, AttributesQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    categoryId: undefined,
+    categoryName: undefined,
+    productAttributesCode: undefined,
+    productAttributesName: undefined,
+    isOptional: undefined,
+    entryMethod: undefined,
+    isFilter: undefined,
+    attributesList: undefined,
+    required: undefined,
+    platformCode: undefined,
+    params: {
+    }
+  },
+  rules: {
+    categoryId: [
+      { required: true, message: "商品类别不能为空", trigger: "change" }
+    ],
+    productAttributesCode: [
+      { required: true, message: "属性编码不能为空", trigger: "blur" }
+    ],
+    productAttributesName: [
+      { required: true, message: "属性名称不能为空", trigger: "blur" }
+    ]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品属性定义(用于动态属性配置)列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listAttributes(queryParams.value);
+  attributesList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  attributesFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: AttributesVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  router.push({
+    path: '/product/attributes/edit',
+    query: {
+      type: 'add'
+    }
+  });
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: AttributesVO) => {
+  const _id = row?.id || ids.value[0];
+  router.push({
+    path: '/product/attributes/edit',
+    query: {
+      id: _id,
+      type: 'edit'
+    }
+  });
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  attributesFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateAttributes(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addAttributes(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: AttributesVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品属性定义(用于动态属性配置)编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delAttributes(_ids);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('product/attributes/export', {
+    ...queryParams.value
+  }, `attributes_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 626 - 0
src/views/product/base/index.vue

@@ -0,0 +1,626 @@
+<template>
+  <div class="p-2">
+    <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" label-width="90px">
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <el-form-item label="商品编号" prop="productNo">
+                  <el-input v-model="queryParams.productNo" placeholder="请输入商品编号" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="商品名称" prop="itemName">
+                  <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row>
+              <el-col :span="24" class="text-left">
+                <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+                <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+
+
+
+    <el-card shadow="never">
+      <el-table v-loading="loading" border :data="baseList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="图片" align="center" prop="productImage" width="100" fixed="left">
+          <template #default="scope">
+            <image-preview :src="scope.row.productImage" :width="60" :height="60" />
+          </template>
+        </el-table-column>
+        <el-table-column label="产品编号" align="center" prop="productNo" width="120">
+          <template #default="scope">
+            <el-link type="primary" @click="handleView(scope.row)">{{ scope.row.productNo }}</el-link>
+          </template>
+        </el-table-column>
+        <el-table-column label="产品名称" align="center" prop="itemName" width="250" show-overflow-tooltip />
+        <el-table-column label="类别" align="center" prop="categoryName" width="120" />
+        <el-table-column label="品牌" align="center" prop="brandName" width="100" />
+        <el-table-column label="单位" align="center" prop="unitName" width="80" />
+        <el-table-column label="市场价" align="center" prop="marketPrice" width="100">
+          <template #default="scope">
+            <span>{{ scope.row.marketPrice || '0.00' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="平台价" align="center" prop="memberPrice" width="100">
+          <template #default="scope">
+            <span>{{ scope.row.memberPrice || '0.00' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="最低售价" align="center" prop="minSellingPrice" width="100">
+          <template #default="scope">
+            <span>{{ scope.row.minSellingPrice || '0.00' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="可用库存数" align="center" prop="availableStock" width="100">
+          <template #default="scope">
+            <span>{{ (scope.row.actualStock || 0) + (scope.row.virtualStock || 0) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="起订量" align="center" prop="minOrderQuantity" width="80">
+          <template #default="scope">
+            <span>{{ scope.row.minOrderQuantity || '1' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="对接状态" align="center" prop="connectStatus" width="100">
+          <template #default="scope">
+            <el-tag v-if="scope.row.connectStatus === '已对接'" type="success">已对接</el-tag>
+            <el-tag v-else type="warning">未对接</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="180" fixed="right">
+          <template #default="scope">
+            <el-button link type="primary" @click="handleConnect(scope.row)">对接</el-button>
+            <el-button link type="primary" @click="handleView(scope.row)">查看详情</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 游标分页控制 -->
+      <pagination
+        v-show="baseList.length > 0"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        v-model:way="queryParams.way"
+        :cursor-mode="true"
+        :has-more="hasMore"
+        @pagination="getList"
+      />
+    </el-card>
+
+    <!-- 对接弹框 -->
+    <el-dialog v-model="connectDialog.visible" title="选择第三方产品分类" width="600px" append-to-body @close="cancelConnect">
+      <el-form ref="connectFormRef" :model="connectForm" :rules="connectRules" label-width="130px">
+        <el-form-item label="第三方产品分类:" prop="externalCategoryId">
+          <el-cascader
+            v-model="connectForm.externalCategoryId"
+            :options="externalCategoryList"
+            :props="cascaderProps"
+            placeholder="请选择"
+            clearable
+            style="width: 100%"
+            @change="handleCategoryChange"
+          />
+        </el-form-item>
+        <el-form-item label="第三方平台售价:" prop="externalPrice">
+          <el-input-number v-model="connectForm.externalPrice" :precision="2" :min="0" :controls="false" style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="折扣率:">
+          <el-text type="info">{{ discountRate }}</el-text>
+        </el-form-item>
+        <el-form-item label="市场价:">
+          <el-text>{{ connectForm.marketPrice }}</el-text>
+        </el-form-item>
+        <el-form-item label="平台价:">
+          <el-text>{{ connectForm.memberPrice }}</el-text>
+        </el-form-item>
+        <el-form-item label="起订量:">
+          <el-input-number v-model="connectForm.minOrderQuantity" :min="1" :controls="false" style="width: 100%" disabled/>
+        </el-form-item>
+        <el-form-item label="最低售价:">
+          <el-text type="danger">¥{{ connectForm.minSellingPrice }}</el-text>
+        </el-form-item>
+        <el-alert title="(当选价不能低于最低售价,不低于平台价)" type="warning" :closable="false" show-icon style="margin-bottom: 10px" />
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="warning" @click="cancelConnect">返回</el-button>
+          <el-button type="success" @click="submitConnect" :loading="connectLoading">
+            <el-icon><Check /></el-icon>
+            确认
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Base" lang="ts">
+import { listBase, getBase, delBase, brandList, categoryTree } from '@/api/product/base';
+import { BaseVO, BaseQuery, BaseForm } from '@/api/product/base/types';
+import { BrandVO } from '@/api/product/brand/types';
+import { categoryTreeVO } from '@/api/product/category/types';
+import { addProduct } from '@/api/external/product';
+import { ProductForm } from '@/api/external/product/types';
+import { listProductCategory } from '@/api/external/productCategory';
+import { ProductCategoryVO } from '@/api/external/productCategory/types';
+import { useRoute, useRouter } from 'vue-router';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
+const route = useRoute();
+
+const baseList = ref<BaseVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const categoryOptions = ref<categoryTreeVO[]>([]);
+const hasMore = ref(true); // 是否还有更多数据
+// 页面历史记录,存储每页的第一个id和最后一个id,用于支持双向翻页
+const pageHistory = ref([]);
+
+// 统计信息
+const statistics = ref({
+  total: 0,
+  configured: 0,
+  onShelf: 0,
+  reviewStatus: '待审核0条',
+  shelfStatus: '上架0条'
+});
+
+const queryFormRef = ref<ElFormInstance>();
+const baseFormRef = ref<ElFormInstance>();
+const connectFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+// 对接弹框状态
+const connectDialog = reactive({
+  visible: false
+});
+
+// 对接表单
+const connectForm = ref({
+  productId: undefined as string | number | undefined,
+  externalCategoryId: undefined as string | number | undefined,
+  externalPrice: 0,
+  marketPrice: 0,
+  memberPrice: 0,
+  minSellingPrice: 0,
+  minOrderQuantity: 1
+});
+
+// 外部分类列表(懒加载模式只需要初始化为空数组)
+const externalCategoryList = ref<ProductCategoryVO[]>([]);
+
+// 级联选择器懒加载配置
+const cascaderProps = {
+  value: 'id',
+  label: 'categoryName',
+  checkStrictly: false,
+  emitPath: false,
+  lazy: true,
+  lazyLoad: async (node: any, resolve: any) => {
+    try {
+      const { level, value } = node;
+      // level 0 表示根节点,加载一级分类
+      // level > 0 表示子节点,根据父节点ID加载子分类
+      const parentId = level === 0 ? 0 : value;
+      
+      const res = await listProductCategory({ parentId });
+      const responseData = (res as any).rows || res.data || [];
+      
+      // 为每个节点添加 leaf 属性,判断是否有子节点
+      const nodes = responseData.map((item: ProductCategoryVO) => ({
+        ...item,
+        leaf: !item.hasChildren && level >= 2 // 如果没有子节点或已经是三级分类,则标记为叶子节点
+      }));
+      
+      resolve(nodes);
+    } catch (error) {
+      console.error('加载分类数据失败:', error);
+      proxy?.$modal.msgError('加载分类数据失败');
+      resolve([]);
+    }
+  }
+};
+
+// 对接加载状态
+const connectLoading = ref(false);
+
+// 对接表单验证规则
+const connectRules = {
+  externalCategoryId: [{ required: true, message: '请选择第三方产品分类', trigger: 'change' }],
+  externalPrice: [
+    { required: true, message: '第三方平台售价不能为空', trigger: 'blur' },
+    {
+      validator: (rule: any, value: number, callback: any) => {
+        if (value < connectForm.value.minSellingPrice) {
+          callback(new Error('第三方价格不能低于最低售价'));
+        } else if (value < connectForm.value.memberPrice) {
+          callback(new Error('第三方价格不能低于平台价'));
+        } else {
+          callback();
+        }
+      },
+      trigger: 'blur'
+    }
+  ]
+};
+
+// 计算折扣率
+const discountRate = computed(() => {
+  if (!connectForm.value.externalCategoryId) {
+    return '请先选择第三方产品分类';
+  }
+  const rate = ((connectForm.value.externalPrice / connectForm.value.marketPrice) * 100).toFixed(2);
+  return rate + '%';
+});
+
+const initFormData: BaseForm = {
+  id: undefined,
+  productNo: undefined,
+  itemName: undefined,
+  brandId: undefined,
+  topCategoryId: undefined,
+  mediumCategoryId: undefined,
+  bottomCategoryId: undefined,
+  unitId: undefined,
+  productImage: undefined,
+  isSelf: undefined,
+  productReviewStatus: undefined,
+  homeRecommended: undefined,
+  categoryRecommendation: undefined,
+  cartRecommendation: undefined,
+  recommendedProductOrder: undefined,
+  isPopular: undefined,
+  isNew: undefined,
+  productStatus: undefined,
+  remark: undefined
+};
+const data = reactive<PageData<BaseForm, BaseQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    productNo: undefined,
+    itemName: undefined,
+    brandName: undefined,
+    productTag: undefined,
+    purchaseNature: undefined,
+    supplierType: undefined,
+    supplierNature: undefined,
+    projectOrg: undefined,
+    topCategoryId: undefined,
+    mediumCategoryId: undefined,
+    bottomCategoryId: undefined,
+    isSelf: undefined,
+    productReviewStatus: undefined,
+    productStatus: undefined,
+    lastSeenId: undefined, // 游标分页的lastSeenId
+    way: undefined,
+    params: {}
+  },
+  rules: {
+    productNo: [{ required: true, message: '产品编号不能为空', trigger: 'blur' }],
+    itemName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
+    brandId: [{ required: true, message: '品牌id不能为空', trigger: 'blur' }],
+    topCategoryId: [{ required: true, message: '顶级分类id不能为空', trigger: 'blur' }],
+    mediumCategoryId: [{ required: true, message: '中级分类id不能为空', trigger: 'blur' }],
+    bottomCategoryId: [{ required: true, message: '底层分类id不能为空', trigger: 'blur' }],
+    unitId: [{ required: true, message: '单位id不能为空', trigger: 'blur' }],
+    productImage: [{ required: true, message: '产品图片URL不能为空', trigger: 'blur' }],
+    productReviewStatus: [{ required: true, message: '产品审核状态 0=待采购审核,1=审核通过,2=驳回,3=待营销审核不能为空', trigger: 'change' }],
+    homeRecommended: [{ required: true, message: '首页推荐:1=推荐,0=不推荐不能为空', trigger: 'blur' }],
+    categoryRecommendation: [{ required: true, message: '分类推荐:1=推荐,0=不推荐不能为空', trigger: 'blur' }],
+    cartRecommendation: [{ required: true, message: '购物车推荐:1=推荐,0=不推荐不能为空', trigger: 'blur' }],
+    recommendedProductOrder: [{ required: true, message: '推荐产品顺序不能为空', trigger: 'blur' }],
+    isPopular: [{ required: true, message: '是否热门:1=是,0=否不能为空', trigger: 'blur' }],
+    isNew: [{ required: true, message: '是否新品:1=是,0=否不能为空', trigger: 'blur' }],
+    remark: [{ required: true, message: '备注不能为空', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+
+/** 查询产品基础信息列表 */
+const getList = async () => {
+  loading.value = true;
+  try {
+    const params = { ...queryParams.value };
+    const currentPageNum = queryParams.value.pageNum;
+
+    // 第一页不需要游标参数
+    if (currentPageNum === 1) {
+      delete params.lastSeenId;
+      delete params.way;
+    } else {
+      // way参数:0=上一页,1=下一页
+      if (queryParams.value.way === 0) {
+        // 上一页:使用目标页(即当前显示页)的firstId
+        const nextPageHistory = pageHistory.value[currentPageNum];
+        if (nextPageHistory) {
+          params.firstSeenId = nextPageHistory.firstId;
+          params.way = 0;
+        }
+      } else {
+        // 下一页:使用前一页的lastId作为lastSeenId
+        const prevPageHistory = pageHistory.value[currentPageNum - 1];
+        if (prevPageHistory) {
+          params.lastSeenId = prevPageHistory.lastId;
+          params.way = 1;
+        }
+      }
+    }
+
+    const res = await listBase(params);
+    baseList.value = res.rows || [];
+
+    // 判断是否还有更多数据
+    hasMore.value = baseList.value.length === queryParams.value.pageSize;
+
+    // 记录当前页的第一个id和最后一个id
+    if (baseList.value.length > 0) {
+      const firstItem = baseList.value[0];
+      const lastItem = baseList.value[baseList.value.length - 1];
+      //如果长度小于currentPageNum则创建
+
+      if (pageHistory.value.length <= currentPageNum) {
+        pageHistory.value[currentPageNum] = {
+          firstId: firstItem.id,
+          lastId: lastItem.id
+        };
+      }
+    }
+
+    total.value = res.total || 0;
+
+    // 更新统计信息
+    statistics.value.total = res.total || 0;
+    statistics.value.configured = res.rows?.filter((item: any) => item.isConfigured)?.length || 0;
+    statistics.value.onShelf = res.rows?.filter((item: any) => item.productStatus === '1')?.length || 0;
+  } catch (error) {
+    console.error('获取列表失败:', error);
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 初始化路由参数 */
+const initRouteParams = () => {
+  // 从路由参数中获取筛选条件
+  if (route.query.productReviewStatus) {
+    queryParams.value.productReviewStatus = Number(route.query.productReviewStatus);
+  }
+  if (route.query.brandName) {
+    queryParams.value.brandName = route.query.brandName as string;
+  }
+  if (route.query.bottomCategoryId) {
+    queryParams.value.bottomCategoryId = route.query.bottomCategoryId as string;
+  }
+  if (route.query.isSelf) {
+    queryParams.value.isSelf = Number(route.query.isSelf);
+  }
+  if (route.query.productCategory) {
+    queryParams.value.productCategory = Number(route.query.productCategory);
+  }
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  baseFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  // 同步查询参数到游标分页参数
+  queryParams.value = {
+    ...queryParams.value,
+    pageNum: 1,
+    productNo: queryParams.value.productNo,
+    itemName: queryParams.value.itemName,
+    brandName: queryParams.value.brandName,
+    bottomCategoryId: queryParams.value.bottomCategoryId,
+    isSelf: queryParams.value.isSelf,
+    productReviewStatus: queryParams.value.productReviewStatus,
+    productStatus: queryParams.value.productStatus,
+    lastSeenId: undefined
+  };
+  pageHistory.value = []; // 重置页面历史
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value.lastSeenId = undefined;
+  pageHistory.value = []; // 重置页面历史
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: BaseVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  router.push('/product/base/add');
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: BaseVO) => {
+  const _id = row?.id || ids.value[0];
+  router.push(`/product/base/edit/${_id}`);
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: BaseVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品基础信息编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delBase(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'product/base/export',
+    {
+      ...queryParams.value
+    },
+    `base_${new Date().getTime()}.xlsx`
+  );
+};
+
+/** 查看商品详情 */
+const handleView = (row: BaseVO) => {
+  router.push(`/product/base/detail/${row.id}`);
+};
+
+/** 对接操作 */
+const handleConnect = async (row: BaseVO) => {
+  // 重置表单
+  connectForm.value = {
+    productId: row.id,
+    externalCategoryId: undefined,
+    externalPrice: 0,
+    marketPrice: row.marketPrice || 0,
+    memberPrice: row.memberPrice || 0,
+    minSellingPrice: row.minSellingPrice || 0,
+    minOrderQuantity: row.minOrderQuantity || 1
+  };
+
+  // 懒加载模式不需要预先加载数据,打开弹框即可
+  connectDialog.visible = true;
+};
+
+
+
+/** 分类变化处理 */
+const handleCategoryChange = (value: string | number) => {
+  if (!value) {
+    connectForm.value.externalPrice = 0;
+    return;
+  }
+
+  // 根据选择的分类计算第三方价格(这里可以根据实际业务逻辑调整)
+  // 默认使用会员价作为第三方价格
+  connectForm.value.externalPrice = connectForm.value.memberPrice;
+};
+
+/** 取消对接 */
+const cancelConnect = () => {
+  connectDialog.visible = false;
+  connectFormRef.value?.resetFields();
+};
+
+/** 提交对接 */
+const submitConnect = async () => {
+  if (!connectFormRef.value) return;
+
+  await connectFormRef.value.validate(async (valid) => {
+    if (valid) {
+      connectLoading.value = true;
+      try {
+        const data: ProductForm = {
+          productId: connectForm.value.productId,
+          externalCategoryId: connectForm.value.externalCategoryId,
+          externalPrice: connectForm.value.externalPrice,
+          pushStatus: 0 // 0=未推送
+        };
+
+        await addProduct(data);
+        proxy?.$modal.msgSuccess('对接成功');
+        connectDialog.visible = false;
+
+        // 刷新列表
+        await getList();
+      } catch (error) {
+        console.error('对接失败:', error);
+        proxy?.$modal.msgError('对接失败');
+      } finally {
+        connectLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 上下架操作 */
+const handleShelf = async (row: BaseVO) => {
+  const action = row.productStatus === '1' ? '下架' : '上架';
+  await proxy?.$modal.confirm(`确认${action}该商品吗?`);
+  // TODO: 调用上下架API
+  proxy?.$modal.msgSuccess(`${action}成功`);
+  await getList();
+};
+
+/** 价格设置 */
+const handlePrice = (row: BaseVO) => {
+  console.log('设置价格', row);
+  // TODO: 打开价格设置对话框
+};
+
+/** 供货存管理 */
+const handleSupply = (row: BaseVO) => {
+  console.log('供货存管理', row);
+  // TODO: 打开供货存管理对话框
+};
+
+/** 停售操作 */
+const handleDiscontinue = async (row: BaseVO) => {
+  await proxy?.$modal.confirm('确认停售该商品吗?');
+  // TODO: 调用停售API
+  proxy?.$modal.msgSuccess('停售成功');
+  await getList();
+};
+
+/** 跳转到商品审核页面 */
+const handleGoReview = () => {
+  router.push({
+    path: '/product/base/review',
+    query: {
+      productReviewStatus: 1 // 默认显示待审核的商品
+    }
+  });
+};
+
+/** 查询分类树 */
+const getCategoryTree = async () => {
+  const res = await categoryTree();
+  categoryOptions.value = res.data || [];
+};
+
+onMounted(() => {
+  getList();
+  getCategoryTree();
+});
+</script>

+ 249 - 0
src/views/product/brand/edit.vue

@@ -0,0 +1,249 @@
+<template>
+  <div class="p-4">
+    <div class="mb-4">
+      <el-button link type="primary" @click="goBack">返回</el-button>
+    </div>
+    <el-card shadow="never">
+      <template #header>
+        <div class="flex justify-between items-center">
+          <span class="text-lg font-bold">{{ pageTitle }}</span>
+        </div>
+      </template>
+
+      <el-form ref="brandFormRef" :model="form" :rules="rules" label-width="140px" class="max-w-5xl">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="品牌名称" prop="brandName">
+              <el-input v-model="form.brandName" placeholder="京东方" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌首字母" prop="brandInitials">
+              <el-input v-model="form.brandInitials" placeholder="请输入品牌首字母" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="品牌名称(英文)" prop="brandEnglishName">
+              <el-input v-model="form.brandEnglishName" placeholder="请输入品牌英文名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌推荐系数" prop="recommendValue">
+              <el-input-number 
+                v-model="form.recommendValue" 
+                :min="0" 
+                :max="9999" 
+                controls-position="right" 
+                style="width: 100%" 
+                placeholder="请输入推荐系数"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="品牌标题" prop="brandTitle">
+          <el-input v-model="form.brandTitle" placeholder="请输入内容" />
+        </el-form-item>
+
+        <el-form-item label="品牌LOGO" prop="brandLogo">
+          <image-upload v-model="form.brandLogo" />
+        </el-form-item>
+
+        <el-form-item label="品牌故事" prop="brandStory">
+          <el-input v-model="form.brandStory" type="textarea" :rows="3" placeholder="请输入内容" />
+        </el-form-item>
+
+        <el-form-item label="是否显示" prop="isShow">
+          <el-radio-group v-model="form.isShow">
+            <el-radio :value="1">是</el-radio>
+            <el-radio :value="0">否</el-radio>
+          </el-radio-group>
+          <div class="text-gray-500 text-sm mt-2">
+            当品牌下还没有商品的时候。分类的品牌区不会不显示品牌。
+          </div>
+        </el-form-item>
+
+        <el-form-item label="品牌介绍" prop="brandDescribe">
+          <editor v-model="form.brandDescribe" :min-height="192" />
+        </el-form-item>
+
+        <el-form-item label="商标注册人" prop="brandRegistrant">
+          <el-input v-model="form.brandRegistrant" placeholder="请输入商标注册人" />
+        </el-form-item>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="上传营业执照" prop="license">
+              <image-upload v-model="form.license" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="上传商标注册证" prop="registrationCertificate">
+              <image-upload v-model="form.registrationCertificate" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="到期时间" prop="expireTime">
+          <el-date-picker 
+            v-model="form.expireTime"
+            type="datetime"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            placeholder="请选择"
+            style="width: 100%"
+          />
+        </el-form-item>
+
+        <el-divider content-position="left">授权信息</el-divider>
+
+        <el-table :data="authorizationList" border style="margin-bottom: 20px">
+          <el-table-column label="供应商名称" prop="supplierName" align="center" />
+          <el-table-column label="品牌名称" prop="brandName" align="center" />
+          <el-table-column label="一级类目" prop="firstCategory" align="center" />
+          <el-table-column label="二级类目" prop="secondCategory" align="center" />
+          <el-table-column label="三级类目" prop="thirdCategory" align="center" />
+          <el-table-column label="截止时间" prop="deadline" align="center" />
+          <el-table-column label="授权区域(省)" prop="provinceArea" align="center" />
+          <el-table-column label="授权区域(市)" prop="cityArea" align="center" />
+          <el-table-column label="状态" prop="status" align="center" />
+          <el-table-column label="操作" align="center">
+            <template #default="scope">
+              <el-button link type="primary" size="small" @click="handleEditAuth(scope.$index)">编辑</el-button>
+              <el-button link type="danger" size="small" @click="handleDeleteAuth(scope.$index)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <el-button type="primary" plain @click="handleAddAuth">添加授权</el-button>
+
+        <el-form-item>
+          <el-button type="primary" :loading="buttonLoading" @click="submitForm">保存</el-button>
+          <el-button @click="goBack">取消</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts" name="BrandEdit">
+import { getBrand, addBrand, updateBrand } from '@/api/product/brand';
+import { BrandForm } from '@/api/product/brand/types';
+
+const route = useRoute();
+const router = useRouter();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const brandFormRef = ref<ElFormInstance>();
+const buttonLoading = ref(false);
+const pageTitle = computed(() => {
+  return route.query.type === 'add' ? '新增品牌' : '编辑品牌';
+});
+
+// 授权信息列表
+const authorizationList = ref<any[]>([]);
+
+const initFormData: BrandForm = {
+  id: undefined,
+  brandNo: undefined,
+  brandName: undefined,
+  brandInitials: undefined,
+  brandEnglishName: undefined,
+  recommendValue: undefined,
+  brandLogo: undefined,
+  brandTitle: undefined,
+  brandBigImage: undefined,
+  brandStory: undefined,
+  isShow: 1,
+  brandRegistrant: undefined,
+  license: undefined,
+  registrationCertificate: undefined,
+  expireTime: undefined,
+  brandDescribe: undefined,
+  position: undefined,
+  care: undefined,
+  dataSource: undefined,
+  remark: undefined,
+}
+
+const form = ref<BrandForm>({ ...initFormData });
+
+const rules = reactive({
+  brandName: [
+    { required: true, message: "品牌名称不能为空", trigger: "blur" }
+  ],
+  brandInitials: [
+    { required: false, message: "品牌首字母缩写不能为空", trigger: "blur" }
+  ],
+  brandEnglishName: [
+    { required: false, message: "品牌英文名称不能为空", trigger: "blur" }
+  ]
+});
+
+/** 获取品牌详情 */
+const getDetail = async () => {
+  const id = route.query.id as string;
+  if (id && route.query.type !== 'add') {
+    const res = await getBrand(id);
+    form.value = res.data;
+    // TODO: 加载授权信息列表
+  }
+};
+
+/** 添加授权 */
+const handleAddAuth = () => {
+  authorizationList.value.push({
+    supplierName: '',
+    brandName: form.value.brandName,
+    firstCategory: '',
+    secondCategory: '',
+    thirdCategory: '',
+    deadline: '',
+    provinceArea: '',
+    cityArea: '',
+    status: ''
+  });
+};
+
+/** 编辑授权 */
+const handleEditAuth = (index: number) => {
+  // TODO: 实现编辑授权逻辑
+  proxy?.$modal.msgInfo('编辑授权功能待实现');
+};
+
+/** 删除授权 */
+const handleDeleteAuth = (index: number) => {
+  authorizationList.value.splice(index, 1);
+};
+
+/** 提交表单 */
+const submitForm = () => {
+  brandFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (form.value.id) {
+          await updateBrand(form.value);
+        } else {
+          await addBrand(form.value);
+        }
+        proxy?.$modal.msgSuccess("操作成功");
+        goBack();
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 返回 */
+const goBack = () => {
+  router.back();
+};
+
+onMounted(() => {
+  getDetail();
+});
+</script>

+ 354 - 0
src/views/product/brand/index.vue

@@ -0,0 +1,354 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-form-item label="品牌名称" prop="brandName">
+              <el-input v-model="queryParams.brandName" placeholder="请输入品牌名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="品牌编号" prop="brandNo">
+              <el-input v-model="queryParams.brandNo" placeholder="请输入品牌编号" clearable @keyup.enter="handleQuery" />
+            </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>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['product:brand:add']">新增品牌</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template> -->
+
+      <el-table v-loading="loading" border :data="brandList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="品牌编号" align="center" prop="brandNo" width="120" />
+        <el-table-column label="品牌名称" align="center" prop="brandName" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
+          <template #default="scope">
+            <!-- <el-tooltip content="查看" placement="top">
+              <el-button link type="primary" icon="View" @click="handleUpdate(scope.row)" v-hasPermi="['product:brand:query']">查看</el-button>
+            </el-tooltip> -->
+            <el-tooltip content="编辑" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">编辑</el-button>
+            </el-tooltip>
+            <!-- <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['product:brand:remove']">删除</el-button>
+            </el-tooltip> -->
+          </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="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="brandFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="品牌编号" prop="brandNo">
+          <el-input v-model="form.brandNo" placeholder="请输入品牌编号" />
+        </el-form-item>
+        <el-form-item label="品牌中文名称" prop="brandName">
+          <el-input v-model="form.brandName" placeholder="请输入品牌中文名称" />
+        </el-form-item>
+        <el-form-item label="品牌首字母缩写" prop="brandInitials">
+          <el-input v-model="form.brandInitials" placeholder="请输入品牌首字母缩写" />
+        </el-form-item>
+        <el-form-item label="品牌英文名称" prop="brandEnglishName">
+          <el-input v-model="form.brandEnglishName" placeholder="请输入品牌英文名称" />
+        </el-form-item>
+        <el-form-item label="推荐值" prop="recommendValue">
+          <el-input v-model="form.recommendValue" placeholder="请输入推荐值" />
+        </el-form-item>
+        <el-form-item label="品牌Logo图片路径或URL" prop="brandLogo">
+          <el-input v-model="form.brandLogo" placeholder="请输入品牌Logo图片路径或URL" />
+        </el-form-item>
+        <el-form-item label="品牌标题" prop="brandTitle">
+          <el-input v-model="form.brandTitle" placeholder="请输入品牌标题" />
+        </el-form-item>
+        <el-form-item label="品牌大图" prop="brandBigImage">
+          <image-upload v-model="form.brandBigImage"/>
+        </el-form-item>
+        <el-form-item label="品牌故事" prop="brandStory">
+            <el-input v-model="form.brandStory" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="是否显示" prop="isShow">
+          <el-input v-model="form.isShow" placeholder="请输入是否显示" />
+        </el-form-item>
+        <el-form-item label="品牌注册人" prop="brandRegistrant">
+          <el-input v-model="form.brandRegistrant" placeholder="请输入品牌注册人" />
+        </el-form-item>
+        <el-form-item label="许可证编号" prop="license">
+          <el-input v-model="form.license" placeholder="请输入许可证编号" />
+        </el-form-item>
+        <el-form-item label="注册证书编号" prop="registrationCertificate">
+          <el-input v-model="form.registrationCertificate" placeholder="请输入注册证书编号" />
+        </el-form-item>
+        <el-form-item label="证书/许可过期时间" prop="expireTime">
+          <el-date-picker clearable
+            v-model="form.expireTime"
+            type="datetime"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            placeholder="请选择证书/许可过期时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="品牌描述" prop="brandDescribe">
+            <el-input v-model="form.brandDescribe" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="展示位置" prop="position">
+          <el-input v-model="form.position" placeholder="请输入展示位置" />
+        </el-form-item>
+        <el-form-item label="关注度/收藏数" prop="care">
+          <el-input v-model="form.care" placeholder="请输入关注度/收藏数" />
+        </el-form-item>
+        <el-form-item label="数据来源" prop="dataSource">
+          <el-input v-model="form.dataSource" placeholder="请输入数据来源" />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Brand" lang="ts">
+import { listBrand, getBrand, delBrand, addBrand, updateBrand } from '@/api/product/brand';
+import { BrandVO, BrandQuery, BrandForm } from '@/api/product/brand/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
+
+const brandList = ref<BrandVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const brandFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: BrandForm = {
+  id: undefined,
+  brandNo: undefined,
+  brandName: undefined,
+  brandInitials: undefined,
+  brandEnglishName: undefined,
+  recommendValue: undefined,
+  brandLogo: undefined,
+  brandTitle: undefined,
+  brandBigImage: undefined,
+  brandStory: undefined,
+  isShow: undefined,
+  brandRegistrant: undefined,
+  license: undefined,
+  registrationCertificate: undefined,
+  expireTime: undefined,
+  brandDescribe: undefined,
+  position: undefined,
+  care: undefined,
+  dataSource: undefined,
+  remark: undefined,
+}
+const data = reactive<PageData<BrandForm, BrandQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    brandNo: undefined,
+    brandName: undefined,
+    brandInitials: undefined,
+    brandEnglishName: undefined,
+    recommendValue: undefined,
+    brandLogo: undefined,
+    brandTitle: undefined,
+    brandBigImage: undefined,
+    brandStory: undefined,
+    isShow: undefined,
+    brandRegistrant: undefined,
+    license: undefined,
+    registrationCertificate: undefined,
+    expireTime: undefined,
+    brandDescribe: undefined,
+    position: undefined,
+    care: undefined,
+    dataSource: undefined,
+    platformCode: undefined,
+    params: {
+    }
+  },
+  rules: {
+    brandInitials: [
+      { required: true, message: "品牌首字母缩写不能为空", trigger: "blur" }
+    ],
+    brandEnglishName: [
+      { required: true, message: "品牌英文名称不能为空", trigger: "blur" }
+    ],
+    recommendValue: [
+      { required: true, message: "推荐值不能为空", trigger: "blur" }
+    ],
+    brandLogo: [
+      { required: true, message: "品牌Logo图片路径或URL不能为空", trigger: "blur" }
+    ],
+    brandTitle: [
+      { required: true, message: "品牌标题不能为空", trigger: "blur" }
+    ],
+    brandBigImage: [
+      { required: true, message: "品牌大图不能为空", trigger: "blur" }
+    ],
+    brandStory: [
+      { required: true, message: "品牌故事不能为空", trigger: "blur" }
+    ],
+    isShow: [
+      { required: true, message: "是否显示不能为空", trigger: "blur" }
+    ],
+    brandRegistrant: [
+      { required: true, message: "品牌注册人不能为空", trigger: "blur" }
+    ],
+    license: [
+      { required: true, message: "许可证编号不能为空", trigger: "blur" }
+    ],
+    registrationCertificate: [
+      { required: true, message: "注册证书编号不能为空", trigger: "blur" }
+    ],
+    expireTime: [
+      { required: true, message: "证书/许可过期时间不能为空", trigger: "blur" }
+    ],
+    brandDescribe: [
+      { required: true, message: "品牌描述不能为空", trigger: "blur" }
+    ],
+    position: [
+      { required: true, message: "展示位置不能为空", trigger: "blur" }
+    ],
+    dataSource: [
+      { required: true, message: "数据来源不能为空", trigger: "blur" }
+    ],
+    remark: [
+      { required: true, message: "备注不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品品牌信息列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listBrand(queryParams.value);
+  brandList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  brandFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: BrandVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  router.push({
+    path: '/product/brand/edit',
+    query: {
+      type: 'add'
+    }
+  });
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: BrandVO) => {
+  const _id = row?.id || ids.value[0];
+  router.push({
+    path: '/product/brand/edit',
+    query: {
+      id: _id,
+      type: 'edit'
+    }
+  });
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  brandFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateBrand(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addBrand(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: BrandVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品品牌信息编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delBrand(_ids);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('product/brand/export', {
+    ...queryParams.value
+  }, `brand_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 294 - 0
src/views/product/category/index.vue

@@ -0,0 +1,294 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-form-item label="分类名称" prop="categoryName">
+              <el-input v-model="queryParams.categoryName" placeholder="请输入分类名称" clearable @keyup.enter="handleQuery" />
+            </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="hover">
+      <template #header>
+        <el-row :gutter="10">
+          <el-col :span="1.5">
+            <el-button  type="primary" plain icon="Plus" @click="handleAdd()">新增分类</el-button>
+          </el-col>
+
+          <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table
+        ref="categoryTableRef"
+        v-loading="loading"
+        :data="categoryList"
+        row-key="id"
+        border
+        lazy
+        :load="loadChildren"
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+      >
+        <el-table-column prop="categoryName" label="分类名称" width="260"></el-table-column>
+        <el-table-column prop="classLevel" align="center" label="分类级别" width="150">
+          <template #default="scope">
+            <span>{{ scope.row.classLevel }}级</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="sort" align="center" label="排序" width="150"></el-table-column>
+        <el-table-column prop="platform" align="center" label="平台" width="150">
+          <template #default="scope">
+            <span>{{ scope.row.platform === 0 ? '工业品' : scope.row.platform === 1 ? 'PC端' : '未知' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="isShow" align="center" label="是否提示" width="150">
+          <template #default="scope">
+            <span>{{ scope.row.isShow === 1 ? '是' : '否' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column fixed="right" align="center" label="操作">
+          <template #default="scope">
+            <el-tooltip content="新增分类" placement="top">
+              <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
+            </el-tooltip>
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
+            </el-tooltip>
+<!--            <el-tooltip content="删除" placement="top">-->
+<!--              <el-button v-hasPermi="['product:category:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />-->
+<!--            </el-tooltip>-->
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+    <!-- 添加或修改产品分类对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-body width="700px">
+      <el-form ref="categoryFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-row :gutter="20">
+          <el-col v-if="form.parentId !== 0" :span="24">
+            <el-form-item label="上级分类" prop="parentId">
+              <el-tree-select
+                v-model="form.parentId"
+                :data="categoryOptions"
+                :props="{ value: 'id', label: 'categoryName', children: 'children' } as any"
+                value-key="id"
+                placeholder="选择上级分类"
+                check-strictly
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="分类名称" prop="categoryName">
+              <el-input v-model="form.categoryName" placeholder="请输入分类名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="分类编号" prop="categoryNo">
+              <el-input v-model="form.categoryNo" placeholder="请输入分类编号" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">确 认</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Category" lang="ts">
+import { listCategory, getCategory, delCategory, addCategory, updateCategory } from '@/api/product/category';
+import { CategoryVO, CategoryQuery, CategoryForm } from '@/api/product/category/types';
+
+interface CategoryOptionsType {
+  id: number | string;
+  categoryName: string;
+  children: CategoryOptionsType[];
+}
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const categoryList = ref<CategoryVO[]>([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const categoryOptions = ref<CategoryOptionsType[]>([]);
+// 存储懒加载的子节点加载状态和 resolve 函数
+const lazyTreeNodeMap = ref<Map<string | number, { tree: any; treeNode: any; resolve: (data: CategoryVO[]) => void }>>(new Map());
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const categoryTableRef = ref<ElTableInstance>();
+const queryFormRef = ref<ElFormInstance>();
+const categoryFormRef = ref<ElFormInstance>();
+
+const initFormData: CategoryForm = {
+  id: undefined,
+  categoryName: undefined,
+  categoryNo: undefined,
+  parentId: undefined,
+  sort: 0,
+  platform: undefined,
+  isShow: 1,
+  isShowGps: 0,
+  oneLable1: undefined,
+  oneLable2: undefined,
+  oneLink1: undefined,
+  oneLink2: undefined,
+  classDescription: undefined
+}
+const data = reactive<PageData<CategoryForm, Partial<CategoryQuery>>>({
+  form: {...initFormData},
+  queryParams: {
+    categoryName: undefined
+  },
+  rules: {
+    categoryName: [
+      { required: true, message: "分类名称不能为空", trigger: "blur" }
+    ],
+    sort: [
+      { required: true, message: "显示排序不能为空", trigger: "blur" }
+    ]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品分类列表(只加载顶级分类) */
+const getList = async () => {
+  loading.value = true;
+  // 清空懒加载缓存
+  lazyTreeNodeMap.value.clear();
+  // 只加载顶级分类(parentId = 0)
+  const res = await listCategory({ ...queryParams.value, parentId: 0 });
+  const data = (res as any).rows || res.data || [];
+  // 根据 hasChildren 字段标记是否有子节点
+  categoryList.value = data.map((item: CategoryVO) => ({
+    ...item,
+    hasChildren: item.hasChildren ?? (item.classLevel < 3) // 如果后端没返回 hasChildren,根据 classLevel 判断
+  }));
+  loading.value = false;
+}
+
+/** 懒加载子节点 */
+const loadChildren = async (row: CategoryVO, treeNode: any, resolve: (data: CategoryVO[]) => void) => {
+  // 存储加载状态,便于刷新时重新加载
+  lazyTreeNodeMap.value.set(row.id, { tree: row, treeNode, resolve });
+  try {
+    const res = await listCategory({ parentId: row.id });
+    const data = (res as any).rows || res.data || [];
+    // 标记子节点是否还有子级
+    const children = data.map((item: CategoryVO) => ({
+      ...item,
+      hasChildren: item.hasChildren ?? (item.classLevel < 3)
+    }));
+    resolve(children);
+  } catch (error) {
+    resolve([]);
+  }
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  categoryFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 刷新已展开的懒加载节点 */
+const refreshLazyNodes = () => {
+  lazyTreeNodeMap.value.forEach(async ({ tree, treeNode, resolve }, key) => {
+    // 重置已加载状态
+    if (treeNode.loaded) {
+      treeNode.loaded = false;
+    }
+    // 重新加载
+    await loadChildren(tree, treeNode, resolve);
+  });
+};
+
+/** 新增按钮操作 */
+const handleAdd = async (row?: CategoryVO) => {
+  reset();
+  const res = await listCategory();
+  const responseData = (res as any).rows || res.data || [];
+  const data = proxy?.handleTree<CategoryOptionsType>(responseData, 'id');
+  if (data) {
+    categoryOptions.value = data;
+    if (row && row.id) {
+      form.value.parentId = row?.id;
+    }
+    dialog.visible = true;
+    dialog.title = '添加产品分类';
+  }
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row: CategoryVO) => {
+  reset();
+  const res = await getCategory(row.id);
+  form.value = res.data;
+  const response = await listCategory();
+  // 根据后端返回结构,如果是 TableDataInfo 则用 rows,如果是直接数组则用 data
+  const responseData = (response as any).rows || response.data || [];
+  const data = proxy?.handleTree<CategoryOptionsType>(responseData, 'id');
+  if (data) {
+    categoryOptions.value = data;
+  }
+  dialog.visible = true;
+  dialog.title = '修改产品分类';
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  categoryFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      form.value.id ? await updateCategory(form.value) : await addCategory(form.value);
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row: CategoryVO) => {
+  await proxy?.$modal.confirm('是否确认删除名称为"' + row.categoryName + '"的数据项?');
+  await delCategory(row.id);
+  await getList();
+  proxy?.$modal.msgSuccess('删除成功');
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 216 - 0
src/views/product/unit/index.vue

@@ -0,0 +1,216 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-form-item label="单位名称" prop="unitName">
+              <el-input v-model="queryParams.unitName" placeholder="请输入单位名称" clearable @keyup.enter="handleQuery" />
+            </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">
+
+      <el-table v-loading="loading" border :data="unitList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="单位编号" align="center" prop="unitNo" />
+        <el-table-column label="单位名称" align="center" prop="unitName" />
+      </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="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="unitFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="单位编号" prop="unitNo">
+          <el-input v-model="form.unitNo" placeholder="请输入单位编号" />
+        </el-form-item>
+        <el-form-item label="单位名称" prop="unitName">
+          <el-input v-model="form.unitName" placeholder="请输入单位名称" />
+        </el-form-item>
+        <el-form-item label="数据来源" prop="dataSource">
+          <el-input v-model="form.dataSource" placeholder="请输入数据来源" />
+        </el-form-item>
+        <el-form-item label="是否显示:1=是,0=否" prop="isShow">
+          <el-input v-model="form.isShow" placeholder="请输入是否显示:1=是,0=否" />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Unit" lang="ts">
+import { listUnit, getUnit, delUnit, addUnit, updateUnit } from '@/api/product/unit';
+import { UnitVO, UnitQuery, UnitForm } from '@/api/product/unit/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const unitList = ref<UnitVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const unitFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: UnitForm = {
+  id: undefined,
+  unitNo: undefined,
+  unitName: undefined,
+  dataSource: undefined,
+  isShow: undefined,
+  remark: undefined,
+}
+const data = reactive<PageData<UnitForm, UnitQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    unitNo: undefined,
+    unitName: undefined,
+    dataSource: undefined,
+    isShow: undefined,
+    platformCode: undefined,
+    params: {
+    }
+  },
+  rules: {
+    unitNo: [
+      { required: true, message: "单位编号不能为空", trigger: "blur" }
+    ],
+    unitName: [
+      { required: true, message: "单位名称不能为空", trigger: "blur" }
+    ],
+    dataSource: [
+      { required: true, message: "数据来源不能为空", trigger: "blur" }
+    ],
+    isShow: [
+      { required: true, message: "是否显示:1=是,0=否不能为空", trigger: "blur" }
+    ],
+    remark: [
+      { required: true, message: "备注不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品计量单位列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listUnit(queryParams.value);
+  unitList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  unitFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: UnitVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加产品计量单位";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: UnitVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getUnit(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改产品计量单位";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  unitFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateUnit(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addUnit(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: UnitVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品计量单位编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delUnit(_ids);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('product/unit/export', {
+    ...queryParams.value
+  }, `unit_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 307 - 0
src/views/product/warehouseInventory/index.vue

@@ -0,0 +1,307 @@
+<template>
+  <div class="p-2">
+    <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">
+            <el-form-item label="商品ID" prop="productId">
+              <el-input v-model="queryParams.productId" placeholder="请输入商品ID" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="SKU ID" prop="skuId">
+              <el-input v-model="queryParams.skuId" placeholder="请输入SKU ID" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="规格型号" prop="specModel">
+              <el-input v-model="queryParams.specModel" placeholder="请输入规格型号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="仓库ID" prop="warehouseId">
+              <el-input v-model="queryParams.warehouseId" placeholder="请输入仓库ID" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="仓库编号" prop="warehouseNo">
+              <el-input v-model="queryParams.warehouseNo" placeholder="请输入仓库编号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="仓库名称" prop="warehouseName">
+              <el-input v-model="queryParams.warehouseName" placeholder="请输入仓库名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="可用库存" prop="nowInventory">
+              <el-input v-model="queryParams.nowInventory" placeholder="请输入可用库存" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="锁定库存" prop="lockInventory">
+              <el-input v-model="queryParams.lockInventory" placeholder="请输入锁定库存" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="平台标识" prop="platformCode">
+              <el-input v-model="queryParams.platformCode" placeholder="请输入平台标识" clearable @keyup.enter="handleQuery" />
+            </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>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['product:warehouseInventory:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['product:warehouseInventory:edit']">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['product:warehouseInventory:remove']">删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['product:warehouseInventory:export']">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="warehouseInventoryList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="ID" align="center" prop="id" v-if="true" />
+        <el-table-column label="商品ID" align="center" prop="productId" />
+        <el-table-column label="SKU ID" align="center" prop="skuId" />
+        <el-table-column label="规格型号" align="center" prop="specModel" />
+        <el-table-column label="仓库ID" align="center" prop="warehouseId" />
+        <el-table-column label="仓库编号" align="center" prop="warehouseNo" />
+        <el-table-column label="仓库名称" align="center" prop="warehouseName" />
+        <el-table-column label="可用库存" align="center" prop="nowInventory" />
+        <el-table-column label="锁定库存" align="center" prop="lockInventory" />
+        <el-table-column label="状态" align="center" prop="status" />
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['product:warehouseInventory:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['product:warehouseInventory:remove']"></el-button>
+            </el-tooltip>
+          </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="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="warehouseInventoryFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="商品ID" prop="productId">
+          <el-input v-model="form.productId" placeholder="请输入商品ID" />
+        </el-form-item>
+        <el-form-item label="SKU ID" prop="skuId">
+          <el-input v-model="form.skuId" placeholder="请输入SKU ID" />
+        </el-form-item>
+        <el-form-item label="规格型号" prop="specModel">
+          <el-input v-model="form.specModel" placeholder="请输入规格型号" />
+        </el-form-item>
+        <el-form-item label="仓库ID" prop="warehouseId">
+          <el-input v-model="form.warehouseId" placeholder="请输入仓库ID" />
+        </el-form-item>
+        <el-form-item label="仓库编号" prop="warehouseNo">
+          <el-input v-model="form.warehouseNo" placeholder="请输入仓库编号" />
+        </el-form-item>
+        <el-form-item label="仓库名称" prop="warehouseName">
+          <el-input v-model="form.warehouseName" placeholder="请输入仓库名称" />
+        </el-form-item>
+        <el-form-item label="可用库存" prop="nowInventory">
+          <el-input v-model="form.nowInventory" placeholder="请输入可用库存" />
+        </el-form-item>
+        <el-form-item label="锁定库存" prop="lockInventory">
+          <el-input v-model="form.lockInventory" placeholder="请输入锁定库存" />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="WarehouseInventory" lang="ts">
+import { listWarehouseInventory, getWarehouseInventory, delWarehouseInventory, addWarehouseInventory, updateWarehouseInventory } from '@/api/product/warehouseInventory';
+import { WarehouseInventoryVO, WarehouseInventoryQuery, WarehouseInventoryForm } from '@/api/product/warehouseInventory/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const warehouseInventoryList = ref<WarehouseInventoryVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const warehouseInventoryFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: WarehouseInventoryForm = {
+  id: undefined,
+  productId: undefined,
+  skuId: undefined,
+  specModel: undefined,
+  warehouseId: undefined,
+  warehouseNo: undefined,
+  warehouseName: undefined,
+  nowInventory: undefined,
+  lockInventory: undefined,
+  status: undefined,
+  remark: undefined,
+}
+const data = reactive<PageData<WarehouseInventoryForm, WarehouseInventoryQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    productId: undefined,
+    skuId: undefined,
+    specModel: undefined,
+    warehouseId: undefined,
+    warehouseNo: undefined,
+    warehouseName: undefined,
+    nowInventory: undefined,
+    lockInventory: undefined,
+    status: undefined,
+    platformCode: undefined,
+    params: {
+    }
+  },
+  rules: {
+    skuId: [
+      { required: true, message: "SKU ID不能为空", trigger: "blur" }
+    ],
+    specModel: [
+      { required: true, message: "规格型号不能为空", trigger: "blur" }
+    ],
+    warehouseNo: [
+      { required: true, message: "仓库编号不能为空", trigger: "blur" }
+    ],
+    warehouseName: [
+      { required: true, message: "仓库名称不能为空", trigger: "blur" }
+    ],
+    nowInventory: [
+      { required: true, message: "可用库存不能为空", trigger: "blur" }
+    ],
+    lockInventory: [
+      { required: true, message: "锁定库存不能为空", trigger: "blur" }
+    ],
+    status: [
+      { required: true, message: "状态不能为空", trigger: "change" }
+    ],
+    remark: [
+      { required: true, message: "备注不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询仓库库存明细列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listWarehouseInventory(queryParams.value);
+  warehouseInventoryList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  warehouseInventoryFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: WarehouseInventoryVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加仓库库存明细";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: WarehouseInventoryVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getWarehouseInventory(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改仓库库存明细";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  warehouseInventoryFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateWarehouseInventory(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addWarehouseInventory(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: WarehouseInventoryVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除仓库库存明细编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delWarehouseInventory(_ids);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('product/warehouseInventory/export', {
+    ...queryParams.value
+  }, `warehouseInventory_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>