Browse Source

fix: 完善 SPU 查看详情

puhui999 1 year ago
parent
commit
39c92cb944

+ 1 - 1
src/api/mall/product/spu.ts

@@ -37,7 +37,7 @@ export interface Spu {
   brandId?: number | null // 商品品牌编号
   specType?: boolean // 商品规格
   subCommissionType?: boolean // 分销类型
-  skus: Sku[] // sku数组
+  skus?: Sku[] // sku数组
   description?: string // 商品详情
   sort?: number // 商品排序
   giveIntegral?: number // 赠送积分

+ 13 - 0
src/router/modules/remaining.ts

@@ -379,6 +379,19 @@ const remainingRouter: AppRouteRecordRaw[] = [
           title: '编辑商品',
           activeMenu: '/product/product-spu'
         }
+      },
+      {
+        path: 'productSpuDetail/:spuId(\\d+)',
+        component: () => import('@/views/mall/product/spu/addForm.vue'),
+        name: 'productSpuDetail',
+        meta: {
+          noCache: true,
+          hidden: true,
+          canTo: true,
+          icon: 'ep:view',
+          title: '商品详情',
+          activeMenu: '/product/product-spu'
+        }
       }
     ]
   }

+ 16 - 5
src/utils/tree.ts

@@ -312,26 +312,30 @@ export const handleTree2 = (data, id, parentId, children, rootId) => {
  * @param nodeId 需要判断在什么层级的数据
  * @param level 检查的级别, 默认检查到二级
  */
-export const checkSelectedNode = (tree: any[], nodeId, level = 2) => {
+export const checkSelectedNode = (tree: any[], nodeId: any, level = 2): boolean => {
   if (typeof tree === 'undefined' || !Array.isArray(tree) || tree.length === 0) {
     console.warn('tree must be an array')
     return false
   }
+
   // 校验是否是一级节点
   if (tree.some((item) => item.id === nodeId)) {
     return false
   }
+
   // 递归计数
   let count = 1
 
   // 深层次校验
-  function performAThoroughValidation(arr) {
+  function performAThoroughValidation(arr: any[]): boolean {
     count += 1
     for (const item of arr) {
       if (item.id === nodeId) {
         return true
       } else if (typeof item.children !== 'undefined' && item.children.length !== 0) {
-        performAThoroughValidation(item.children)
+        if (performAThoroughValidation(item.children)) {
+          return true
+        }
       }
     }
     return false
@@ -341,11 +345,15 @@ export const checkSelectedNode = (tree: any[], nodeId, level = 2) => {
     count = 1
     if (performAThoroughValidation(item.children)) {
       // 找到后对比是否是期望的层级
-      if (count >= level) return true
+      if (count >= level) {
+        return true
+      }
     }
   }
+
   return false
 }
+
 /**
  * 获取节点的完整结构
  * @param tree 树数据
@@ -369,7 +377,10 @@ export const treeToString = (tree: any[], nodeId) => {
         str += `/${item.name}`
         return true
       } else if (typeof item.children !== 'undefined' && item.children.length !== 0) {
-        performAThoroughValidation(item.children)
+        str += `/${item.name}`
+        if (performAThoroughValidation(item.children)) {
+          return true
+        }
       }
     }
     return false

+ 8 - 4
src/views/mall/product/spu/addForm.vue

@@ -5,6 +5,7 @@
         <BasicInfoForm
           ref="basicInfoRef"
           v-model:activeName="activeName"
+          :is-detail="isDetail"
           :propFormData="formData"
         />
       </el-tab-pane>
@@ -12,6 +13,7 @@
         <DescriptionForm
           ref="descriptionRef"
           v-model:activeName="activeName"
+          :is-detail="isDetail"
           :propFormData="formData"
         />
       </el-tab-pane>
@@ -19,6 +21,7 @@
         <OtherSettingsForm
           ref="otherSettingsRef"
           v-model:activeName="activeName"
+          :is-detail="isDetail"
           :propFormData="formData"
         />
       </el-tab-pane>
@@ -42,11 +45,12 @@ import { convertToInteger, formatToFraction } from '@/utils'
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
 const { push, currentRoute } = useRouter() // 路由
-const { params } = useRoute() // 查询参数
+const { params, name } = useRoute() // 查询参数
 const { delView } = useTagsViewStore() // 视图操作
 
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
 const activeName = ref('basicInfo') // Tag 激活的窗口
+const isDetail = ref(false) // 是否查看详情
 const basicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>() // 商品信息Ref
 const descriptionRef = ref<ComponentRef<typeof DescriptionForm>>() // 商品详情Ref
 const otherSettingsRef = ref<ComponentRef<typeof OtherSettingsForm>>() // 其他设置Ref
@@ -90,12 +94,13 @@ const formData = ref<ProductSpuApi.Spu>({
 
 /** 获得详情 */
 const getDetail = async () => {
+  console.log(name)
   const id = params.spuId as number
   if (id) {
     formLoading.value = true
     try {
       const res = (await ProductSpuApi.getSpu(id)) as ProductSpuApi.Spu
-      res.skus.forEach((item) => {
+      res.skus!.forEach((item) => {
         // 回显价格分转元
         item.price = formatToFraction(item.price)
         item.marketPrice = formatToFraction(item.marketPrice)
@@ -123,7 +128,7 @@ const submitForm = async () => {
     // 深拷贝一份, 这样最终 server 端不满足,不需要恢复,
     const deepCopyFormData = cloneDeep(unref(formData.value))
     // 兜底处理 sku 空数据
-    formData.value.skus.forEach((sku) => {
+    formData.value.skus!.forEach((sku) => {
       // 因为是空数据这里判断一下商品条码是否为空就行
       if (sku.barCode === '') {
         const index = deepCopyFormData.skus.findIndex(
@@ -171,7 +176,6 @@ const close = () => {
   delView(unref(currentRoute))
   push('/product/product-spu')
 }
-
 /** 初始化 */
 onMounted(async () => {
   await getDetail()

+ 74 - 6
src/views/mall/product/spu/components/BasicInfoForm.vue

@@ -1,5 +1,11 @@
 <template>
-  <el-form ref="productSpuBasicInfoRef" :model="formData" :rules="rules" label-width="120px">
+  <el-form
+    v-if="!isDetail"
+    ref="productSpuBasicInfoRef"
+    :model="formData"
+    :rules="rules"
+    label-width="120px"
+  >
     <el-row>
       <el-col :span="12">
         <el-form-item label="商品名称" prop="name">
@@ -115,18 +121,72 @@
     </el-row>
   </el-form>
   <ProductAttributesAddForm ref="attributesAddFormRef" :propertyList="propertyList" />
+  <!-- 详情跟表单放在一块可以共用已有功能,再抽离成组件有点过度封装的感觉 -->
+  <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema">
+    <template #categoryId="{ row }"> {{ categoryString(row.categoryId) }}</template>
+    <template #brandId="{ row }">
+      {{ brandList.find((item) => item.id === row.brandId)?.name }}
+    </template>
+    <template #specType="{ row }">
+      {{ row.specType ? '多规格' : '单规格' }}
+    </template>
+    <template #subCommissionType="{ row }">
+      {{ row.subCommissionType ? '自行设置' : '默认设置' }}
+    </template>
+    <template #picUrl="{ row }">
+      <el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" />
+    </template>
+    <template #sliderPicUrls="{ row }">
+      <el-image
+        v-for="(item, index) in row.sliderPicUrls"
+        :key="index"
+        :src="item.url"
+        class="w-60px h-60px mr-10px"
+        @click="imagePreview(row.sliderPicUrls)"
+      />
+    </template>
+    <template #skus>
+      <SkuList
+        ref="skuDetailListRef"
+        :is-detail="isDetail"
+        :prop-form-data="formData"
+        :propertyList="propertyList"
+      />
+    </template>
+  </Descriptions>
 </template>
 <script lang="ts" name="ProductSpuBasicInfoForm" setup>
 import { PropType } from 'vue'
 import { copyValueToTarget } from '@/utils'
 import { propTypes } from '@/utils/propTypes'
-import { checkSelectedNode, defaultProps, handleTree } from '@/utils/tree'
+import { checkSelectedNode, defaultProps, handleTree, treeToString } from '@/utils/tree'
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 import type { Spu } from '@/api/mall/product/spu'
 import { UploadImg, UploadImgs } from '@/components/UploadFile'
 import { ProductAttributes, ProductAttributesAddForm, SkuList } from './index'
+import { basicInfoSchema } from './spu.data'
+import { createImageViewer } from '@/components/ImageViewer'
 import * as ProductCategoryApi from '@/api/mall/product/category'
 import { getSimpleBrandList } from '@/api/mall/product/brand'
+import { isArray } from '@/utils/is'
+
+// ====== 商品详情相关操作 ======
+const { allSchemas } = useCrudSchemas(basicInfoSchema)
+/** 商品图预览 */
+const imagePreview = (args) => {
+  const urlList = []
+  if (isArray(args)) {
+    args.forEach((item) => {
+      urlList.push(item.url)
+    })
+  } else {
+    urlList.push(args)
+  }
+  createImageViewer({
+    urlList
+  })
+}
+// ====== end ======
 
 const message = useMessage() // 消息弹窗
 
@@ -135,7 +195,8 @@ const props = defineProps({
     type: Object as PropType<Spu>,
     default: () => {}
   },
-  activeName: propTypes.string.def('')
+  activeName: propTypes.string.def(''),
+  isDetail: propTypes.bool.def(false) // 是否作为详情组件
 })
 const attributesAddFormRef = ref() // 添加商品属性表单
 const productSpuBasicInfoRef = ref() // 表单 Ref
@@ -149,11 +210,11 @@ const formData = reactive<Spu>({
   name: '', // 商品名称
   categoryId: null, // 商品分类
   keyword: '', // 关键字
-  unit: '', // 单位
+  unit: null, // 单位
   picUrl: '', // 商品封面图
   sliderPicUrls: [], // 商品轮播图
   introduction: '', // 商品简介
-  deliveryTemplateId: 1, // 运费模版
+  deliveryTemplateId: null, // 运费模版
   brandId: null, // 商品品牌
   specType: false, // 商品规格
   subCommissionType: false, // 分销类型
@@ -273,9 +334,16 @@ const categoryList = ref([]) // 分类树
 const nodeClick = () => {
   if (!checkSelectedNode(categoryList.value, formData.categoryId)) {
     formData.categoryId = null
-    message.warning('必须选择二级节点!!')
+    message.warning('必须选择二级及以下节点!!')
   }
 }
+/**
+ * 获取分类的节点的完整结构
+ * @param categoryId 分类id
+ */
+const categoryString = (categoryId) => {
+  return treeToString(categoryList.value, categoryId)
+}
 const brandList = ref([]) // 精简商品品牌列表
 onMounted(async () => {
   // 获得分类树

+ 23 - 2
src/views/mall/product/spu/components/DescriptionForm.vue

@@ -1,10 +1,27 @@
 <template>
-  <el-form ref="descriptionFormRef" :model="formData" :rules="rules" label-width="120px">
+  <el-form
+    v-if="!isDetail"
+    ref="descriptionFormRef"
+    :model="formData"
+    :rules="rules"
+    label-width="120px"
+  >
     <!--富文本编辑器组件-->
     <el-form-item label="商品详情" prop="description">
       <Editor v-model:modelValue="formData.description" />
     </el-form-item>
   </el-form>
+  <Descriptions
+    v-if="isDetail"
+    :data="formData"
+    :schema="allSchemas.detailSchema"
+    class="descriptionFormDescriptions"
+  >
+    <!-- 展示 HTML 内容 -->
+    <template #description="{ row }">
+      <div style="width: 600px" v-html="row.description"></div>
+    </template>
+  </Descriptions>
 </template>
 <script lang="ts" name="DescriptionForm" setup>
 import type { Spu } from '@/api/mall/product/spu'
@@ -12,6 +29,9 @@ import { Editor } from '@/components/Editor'
 import { PropType } from 'vue'
 import { propTypes } from '@/utils/propTypes'
 import { copyValueToTarget } from '@/utils'
+import { descriptionSchema } from './spu.data'
+
+const { allSchemas } = useCrudSchemas(descriptionSchema)
 
 const message = useMessage() // 消息弹窗
 const props = defineProps({
@@ -19,7 +39,8 @@ const props = defineProps({
     type: Object as PropType<Spu>,
     default: () => {}
   },
-  activeName: propTypes.string.def('')
+  activeName: propTypes.string.def(''),
+  isDetail: propTypes.bool.def(false) // 是否作为详情组件
 })
 const descriptionFormRef = ref() // 表单Ref
 const formData = ref<Spu>({

+ 35 - 2
src/views/mall/product/spu/components/OtherSettingsForm.vue

@@ -1,5 +1,11 @@
 <template>
-  <el-form ref="otherSettingsFormRef" :model="formData" :rules="rules" label-width="120px">
+  <el-form
+    v-if="!isDetail"
+    ref="otherSettingsFormRef"
+    :model="formData"
+    :rules="rules"
+    label-width="120px"
+  >
     <el-row>
       <el-col :span="24">
         <el-row :gutter="20">
@@ -50,12 +56,38 @@
       </el-col>
     </el-row>
   </el-form>
+  <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema">
+    <template #recommendHot="{ row }">
+      {{ row.recommendHot ? '是' : '否' }}
+    </template>
+    <template #recommendBenefit="{ row }">
+      {{ row.recommendBenefit ? '是' : '否' }}
+    </template>
+    <template #recommendBest="{ row }">
+      {{ row.recommendBest ? '是' : '否' }}
+    </template>
+    <template #recommendNew="{ row }">
+      {{ row.recommendNew ? '是' : '否' }}
+    </template>
+    <template #recommendGood="{ row }">
+      {{ row.recommendGood ? '是' : '否' }}
+    </template>
+    <template #activityOrders>
+      <el-tag>默认</el-tag>
+      <el-tag class="ml-2" type="success">秒杀</el-tag>
+      <el-tag class="ml-2" type="info">砍价</el-tag>
+      <el-tag class="ml-2" type="warning">拼团</el-tag>
+    </template>
+  </Descriptions>
 </template>
 <script lang="ts" name="OtherSettingsForm" setup>
 import type { Spu } from '@/api/mall/product/spu'
 import { PropType } from 'vue'
 import { propTypes } from '@/utils/propTypes'
 import { copyValueToTarget } from '@/utils'
+import { otherSettingsSchema } from './spu.data'
+
+const { allSchemas } = useCrudSchemas(otherSettingsSchema)
 
 const message = useMessage() // 消息弹窗
 
@@ -64,7 +96,8 @@ const props = defineProps({
     type: Object as PropType<Spu>,
     default: () => {}
   },
-  activeName: propTypes.string.def('')
+  activeName: propTypes.string.def(''),
+  isDetail: propTypes.bool.def(false) // 是否作为详情组件
 })
 
 const otherSettingsFormRef = ref() // 表单Ref

+ 97 - 9
src/views/mall/product/spu/components/SkuList.vue

@@ -1,5 +1,6 @@
 <template>
   <el-table
+    v-if="!isDetail"
     :data="isBatch ? skuList : formData!.skus"
     border
     class="tabNumWidth"
@@ -21,8 +22,8 @@
         min-width="120"
       >
         <template #default="{ row }">
-          <!-- TODO puhui999:展示成蓝色,有点区分度哈 fix: 字体加粗,颜色使用 #99a9bf 蓝色有点不好看哈哈-->
-          <span style="font-weight: bold; color: #99a9bf">
+          <!-- TODO puhui999:展示成蓝色,有点区分度哈 fix-->
+          <span style="font-weight: bold; color: #40aaff">
             {{ row.properties[index]?.valueName }}
           </span>
         </template>
@@ -108,6 +109,84 @@
       </template>
     </el-table-column>
   </el-table>
+  <el-table
+    v-if="isDetail"
+    :data="formData!.skus"
+    border
+    max-height="500"
+    size="small"
+    style="width: 99%"
+  >
+    <el-table-column align="center" label="图片" min-width="80">
+      <template #default="{ row }">
+        <el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" />
+      </template>
+    </el-table-column>
+    <template v-if="formData!.specType && !isBatch">
+      <!--  根据商品属性动态添加 -->
+      <el-table-column
+        v-for="(item, index) in tableHeaders"
+        :key="index"
+        :label="item.label"
+        align="center"
+        min-width="80"
+      >
+        <template #default="{ row }">
+          <!-- TODO puhui999:展示成蓝色,有点区分度哈 fix-->
+          <span style="font-weight: bold; color: #40aaff">
+            {{ row.properties[index]?.valueName }}
+          </span>
+        </template>
+      </el-table-column>
+    </template>
+    <el-table-column align="center" label="商品条码" min-width="100">
+      <template #default="{ row }">
+        {{ row.barCode }}
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="销售价(元)" min-width="80">
+      <template #default="{ row }">
+        {{ row.price }}
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="市场价(元)" min-width="80">
+      <template #default="{ row }">
+        {{ row.marketPrice }}
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="成本价(元)" min-width="80">
+      <template #default="{ row }">
+        {{ row.costPrice }}
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="库存" min-width="80">
+      <template #default="{ row }">
+        {{ row.stock }}
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="重量(kg)" min-width="80">
+      <template #default="{ row }">
+        {{ row.weight }}
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="体积(m^3)" min-width="80">
+      <template #default="{ row }">
+        {{ row.volume }}
+      </template>
+    </el-table-column>
+    <template v-if="formData!.subCommissionType">
+      <el-table-column align="center" label="一级返佣(元)" min-width="80">
+        <template #default="{ row }">
+          {{ row.subCommissionFirstPrice }}
+        </template>
+      </el-table-column>
+      <el-table-column align="center" label="二级返佣(元)" min-width="80">
+        <template #default="{ row }">
+          {{ row.subCommissionSecondPrice }}
+        </template>
+      </el-table-column>
+    </template>
+  </el-table>
 </template>
 <script lang="ts" name="SkuList" setup>
 import { PropType, Ref } from 'vue'
@@ -115,6 +194,7 @@ import { copyValueToTarget } from '@/utils'
 import { propTypes } from '@/utils/propTypes'
 import { UploadImg } from '@/components/UploadFile'
 import type { Property, Sku, Spu } from '@/api/mall/product/spu'
+import { createImageViewer } from '@/components/ImageViewer'
 
 const props = defineProps({
   propFormData: {
@@ -125,7 +205,8 @@ const props = defineProps({
     type: Array,
     default: () => []
   },
-  isBatch: propTypes.bool.def(false) // 是否作为批量操作组件
+  isBatch: propTypes.bool.def(false), // 是否作为批量操作组件
+  isDetail: propTypes.bool.def(false) // 是否作为 sku 详情组件
 })
 const formData: Ref<Spu | undefined> = ref<Spu>() // 表单数据
 const skuList = ref<Sku[]>([
@@ -143,20 +224,27 @@ const skuList = ref<Sku[]>([
   }
 ]) // 批量添加时的临时数据
 
+/** 商品图预览 */
+const imagePreview = (imgUrl: string) => {
+  createImageViewer({
+    urlList: [imgUrl]
+  })
+}
+
 /** 批量添加 */
 const batchAdd = () => {
-  formData.value!.skus.forEach((item) => {
+  formData.value!.skus!.forEach((item) => {
     copyValueToTarget(item, skuList.value[0])
   })
 }
 
 /** 删除 sku */
 const deleteSku = (row) => {
-  const index = formData.value!.skus.findIndex(
+  const index = formData.value!.skus!.findIndex(
     // 直接把列表转成字符串比较
     (sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
   )
-  formData.value!.skus.splice(index, 1)
+  formData.value!.skus!.splice(index, 1)
 }
 const tableHeaders = ref<{ prop: string; label: string }[]>([]) // 多属性表头
 /**
@@ -221,13 +309,13 @@ const generateTableData = (propertyList: any[]) => {
       subCommissionSecondPrice: 0
     }
     // 如果存在属性相同的 sku 则不做处理
-    const index = formData.value!.skus.findIndex(
+    const index = formData.value!.skus!.findIndex(
       (sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
     )
     if (index !== -1) {
       continue
     }
-    formData.value!.skus.push(row)
+    formData.value!.skus!.push(row)
   }
 }
 
@@ -236,7 +324,7 @@ const generateTableData = (propertyList: any[]) => {
  */
 const validateData = (propertyList: any[]) => {
   const skuPropertyIds = []
-  formData.value!.skus.forEach((sku) =>
+  formData.value!.skus!.forEach((sku) =>
     sku.properties
       ?.map((property) => property.propertyId)
       .forEach((propertyId) => {

+ 105 - 0
src/views/mall/product/spu/components/spu.data.ts

@@ -0,0 +1,105 @@
+import { CrudSchema } from '@/hooks/web/useCrudSchemas'
+
+export const basicInfoSchema = reactive<CrudSchema[]>([
+  {
+    label: '商品名称',
+    field: 'name'
+  },
+  {
+    label: '关键字',
+    field: 'keyword'
+  },
+  {
+    label: '商品简介',
+    field: 'introduction'
+  },
+  {
+    label: '商品分类',
+    field: 'categoryId'
+  },
+  {
+    label: '商品品牌',
+    field: 'brandId'
+  },
+  {
+    label: '商品封面图',
+    field: 'picUrl'
+  },
+  {
+    label: '商品轮播图',
+    field: 'sliderPicUrls'
+  },
+  {
+    label: '商品视频',
+    field: 'videoUrl'
+  },
+  {
+    label: '单位',
+    field: 'unit',
+    dictType: DICT_TYPE.PRODUCT_UNIT
+  },
+  {
+    label: '规格类型',
+    field: 'specType'
+  },
+  {
+    label: '分销类型',
+    field: 'subCommissionType'
+  },
+  {
+    label: '物流模版',
+    field: 'deliveryTemplateId'
+  },
+  {
+    label: '商品属性列表',
+    field: 'skus'
+  }
+])
+export const descriptionSchema = reactive<CrudSchema[]>([
+  {
+    label: '商品详情',
+    field: 'description'
+  }
+])
+export const otherSettingsSchema = reactive<CrudSchema[]>([
+  {
+    label: '商品排序',
+    field: 'sort'
+  },
+  {
+    label: '赠送积分',
+    field: 'giveIntegral'
+  },
+  {
+    label: '虚拟销量',
+    field: 'virtualSalesCount'
+  },
+  {
+    label: '是否热卖推荐',
+    field: 'recommendHot'
+  },
+  {
+    label: '是否优惠推荐',
+    field: 'recommendBenefit'
+  },
+  {
+    label: '是否精品推荐',
+    field: 'recommendBest'
+  },
+  {
+    label: '是否新品推荐',
+    field: 'recommendNew'
+  },
+  {
+    label: '是否优品推荐',
+    field: 'recommendGood'
+  },
+  {
+    label: '赠送的优惠劵',
+    field: 'giveCouponTemplateIds'
+  },
+  {
+    label: '活动显示排序',
+    field: 'activityOrders'
+  }
+])

+ 15 - 8
src/views/mall/product/spu/index.vue

@@ -171,8 +171,13 @@
       </el-table-column>
       <el-table-column align="center" fixed="right" label="操作" min-width="200">
         <template #default="{ row }">
-          <!-- TODO @puhui999:【详情】,可以后面点做哈 -->
-          <el-button v-hasPermi="['product:spu:update']" link type="primary" @click="openDetail">
+          <!-- TODO @puhui999:【详情】,可以后面点做哈 fix-->
+          <el-button
+            v-hasPermi="['product:spu:update']"
+            link
+            type="primary"
+            @click="openDetail(row.id)"
+          >
             详情
           </el-button>
           <template v-if="queryParams.tabType === 4">
@@ -284,12 +289,14 @@ const getTabsCount = async () => {
 const queryParams = ref({
   pageNo: 1,
   pageSize: 10,
-  tabType: 0
+  tabType: 0,
+  name: '',
+  categoryId: null
 }) // 查询参数
 const queryFormRef = ref() // 搜索的表单Ref
 
 const handleTabClick = (tab: TabsPaneContext) => {
-  queryParams.value.tabType = tab.paneName
+  queryParams.value.tabType = tab.paneName as number
   getList()
 }
 
@@ -400,8 +407,8 @@ const openForm = (id?: number) => {
 /**
  * 查看商品详情
  */
-const openDetail = () => {
-  message.alert('查看详情未完善!!!')
+const openDetail = (id?: number) => {
+  push('/product/productSpuDetail' + id)
 }
 
 /** 导出按钮操作 */
@@ -436,12 +443,12 @@ const categoryString = (categoryId) => {
   return treeToString(categoryList.value, categoryId)
 }
 /**
- * 校验所选是否为二级节点
+ * 校验所选是否为二级及以下节点
  */
 const nodeClick = () => {
   if (!checkSelectedNode(categoryList.value, queryParams.value.categoryId)) {
     queryParams.value.categoryId = null
-    message.warning('必须选择二级节点!!')
+    message.warning('必须选择二级及以下节点!!')
   }
 }
 /** 初始化 **/