ソースを参照

商品管理: 初步完成相关组件

puhui999 2 年 前
コミット
61218ae711

+ 0 - 0
src/api/mall/product/management/sku.ts


+ 15 - 0
src/api/mall/product/management/spu.ts

@@ -0,0 +1,15 @@
+import request from '@/config/axios'
+import type { SpuType } from './type/spuType'
+
+// 获得sku列表
+export const getSkuList = (params: any) => {
+  return request.get({ url: '/product/sku/list', params })
+}
+// 创建商品spu
+export const createSpu = (data: SpuType) => {
+  return request.post({ url: '/product/spu/create', data })
+}
+// 更新商品spu
+export const updateSpu = (data: SpuType) => {
+  return request.put({ url: '/product/spu/update', data })
+}

+ 75 - 0
src/api/mall/product/management/type/skuType.ts

@@ -0,0 +1,75 @@
+export interface Property {
+  /**
+   * 属性编号
+   *
+   * 关联 {@link ProductPropertyDO#getId()}
+   */
+  propertyId?: number
+  /**
+   * 属性值编号
+   *
+   * 关联 {@link ProductPropertyValueDO#getId()}
+   */
+  valueId?: number
+}
+
+export interface SkuType {
+  /**
+   * 商品 SKU 编号,自增
+   */
+  id?: number
+  /**
+   * SPU 编号
+   */
+  spuId?: number
+  /**
+   * 属性数组,JSON 格式
+   */
+  properties?: Property[]
+  /**
+   * 商品价格,单位:分
+   */
+  price?: number
+  /**
+   * 市场价,单位:分
+   */
+  marketPrice?: number
+  /**
+   * 成本价,单位:分
+   */
+  costPrice?: number
+  /**
+   * 商品条码
+   */
+  barCode?: string
+  /**
+   * 图片地址
+   */
+  picUrl?: string
+  /**
+   * 库存
+   */
+  stock?: number
+  /**
+   * 商品重量,单位:kg 千克
+   */
+  weight?: number
+  /**
+   * 商品体积,单位:m^3 平米
+   */
+  volume?: number
+
+  /**
+   * 一级分销的佣金,单位:分
+   */
+  subCommissionFirstPrice?: number
+  /**
+   * 二级分销的佣金,单位:分
+   */
+  subCommissionSecondPrice?: number
+
+  /**
+   * 商品销量
+   */
+  salesCount?: number
+}

+ 3 - 0
src/api/mall/product/management/type/index.ts → src/api/mall/product/management/type/spuType.ts

@@ -1,3 +1,5 @@
+import { SkuType } from './skuType'
+
 export interface SpuType {
   name?: string // 商品名称
   categoryId?: number | undefined // 商品分类
@@ -10,6 +12,7 @@ export interface SpuType {
   selectRule?: string // 选择规格 TODO 暂时定义
   specType?: boolean // 商品规格
   subCommissionType?: boolean // 分销类型
+  skus?: SkuType[] // sku数组
   description?: string // 商品详情
   sort?: string // 商品排序
   giveIntegral?: number // 赠送积分

+ 27 - 7
src/views/mall/product/management/addForm.vue

@@ -34,12 +34,14 @@
 <script lang="ts" name="ProductManagementForm" setup>
 import { useTagsViewStore } from '@/store/modules/tagsView'
 import { BasicInfoForm, DescriptionForm, OtherSettingsForm } from './components'
-import { SpuType } from '@/api/mall/product/management/type' // const { t } = useI18n() // 国际化
+import type { SpuType } from '@/api/mall/product/management/type/spuType'
+// 业务api
+import * as managementApi from '@/api/mall/product/management/spu'
 
-// const { t } = useI18n() // 国际化
-// const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
 const { push, currentRoute } = useRouter() // 路由
-// const { query } = useRoute() // 查询参数
+const { query } = useRoute() // 查询参数
 const { delView } = useTagsViewStore() // 视图操作
 
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
@@ -70,10 +72,17 @@ const formData = ref<SpuType>({
   recommendGood: false // 是否优品
 })
 /** 获得详情 */
-const getDetail = async () => {}
+const getDetail = async () => {
+  const id = query.id as unknown as number
+  if (!id) {
+    return
+  }
+}
 
 /** 提交按钮 */
 const submitForm = async () => {
+  // 提交请求
+  formLoading.value = true
   // TODO 三个表单逐一校验,如果有一个表单校验不通过则切换到对应表单,如果有两个及以上的情况则切换到最前面的一个并弹出提示消息
   // 校验各表单
   try {
@@ -81,9 +90,20 @@ const submitForm = async () => {
     await unref(DescriptionRef)?.validate()
     await unref(OtherSettingsRef)?.validate()
     // 校验都通过后提交表单
-    console.log(formData.value)
-  } catch {}
+    const data = formData.value as SpuType
+    const id = query.id as unknown as number
+    if (!id) {
+      await managementApi.createSpu(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await managementApi.updateSpu(data)
+      message.success(t('common.updateSuccess'))
+    }
+  } finally {
+    formLoading.value = false
+  }
 }
+
 /**
  * 重置表单
  */

+ 78 - 31
src/views/mall/product/management/components/BasicInfoForm.vue

@@ -58,7 +58,6 @@
       <el-col :span="12">
         <el-button class="ml-20px">运费模板</el-button>
       </el-col>
-      <!-- TODO 商品规格和分销类型切换待定    -->
       <el-col :span="12">
         <el-form-item label="商品规格" props="specType">
           <el-radio-group v-model="formData.specType" @change="changeSpecType(formData.specType)">
@@ -67,45 +66,41 @@
           </el-radio-group>
         </el-form-item>
       </el-col>
-      <!-- TODO 商品规格和分销类型切换待定    -->
       <el-col :span="12">
         <el-form-item label="分销类型" props="subCommissionType">
-          <el-radio-group
-            v-model="formData.subCommissionType"
-            @change="changeSubCommissionType(formData.subCommissionType)"
-          >
+          <el-radio-group v-model="formData.subCommissionType" @change="changeSubCommissionType">
             <el-radio :label="false">默认设置</el-radio>
             <el-radio :label="true" class="radio">自行设置</el-radio>
           </el-radio-group>
         </el-form-item>
       </el-col>
       <!-- 多规格添加-->
-      <el-col v-if="formData.specType" :span="24">
-        <el-form-item label="选择规格" prop="">
-          <div class="acea-row">
-            <el-select v-model="formData.selectRule">
-              <el-option
-                v-for="item in []"
-                :key="item.id"
-                :label="item.ruleName"
-                :value="item.id"
-              />
-            </el-select>
-            <el-button class="mr-20px" type="primary" @click="confirm">确认</el-button>
-            <el-button class="mr-15px" @click="addRule">添加规格</el-button>
-          </div>
+      <el-col :span="24">
+        <el-form-item v-if="formData.specType" label="商品属性" prop="">
+          <el-button class="mr-15px" @click="AttributesAddFormRef.open()">添加规格</el-button>
+          <ProductAttributes :attribute-data="attributeList" />
+        </el-form-item>
+        <el-form-item>
+          <SkuList :sku-data="formData.skus" :subCommissionType="formData.subCommissionType" />
         </el-form-item>
       </el-col>
     </el-row>
   </el-form>
+  <ProductAttributesAddForm ref="AttributesAddFormRef" @success="addAttribute" />
 </template>
 <script lang="ts" name="ProductManagementBasicInfoForm" setup>
 import { PropType } from 'vue'
-import type { SpuType } from '@/api/mall/product/management/type'
+import type { SpuType } from '@/api/mall/product/management/type/spuType'
 import { UploadImg, UploadImgs } from '@/components/UploadFile'
+import SkuList from './SkuList/index.vue'
+import ProductAttributesAddForm from './ProductAttributesAddForm.vue'
+import ProductAttributes from './ProductAttributes.vue'
 import { copyValueToTarget } from '@/utils/object'
+// 业务Api
 import * as ProductCategoryApi from '@/api/mall/product/category'
+import * as PropertyApi from '@/api/mall/product/property'
 import { defaultProps, handleTree } from '@/utils/tree'
+import { ElInput } from 'element-plus'
 
 const message = useMessage() // 消息弹窗
 const props = defineProps({
@@ -114,9 +109,21 @@ const props = defineProps({
     default: () => {}
   }
 })
-
+const AttributesAddFormRef = ref() // 添加商品属性表单
 const ProductManagementBasicInfoRef = ref() // 表单Ref
-const formData = ref<SpuType>({
+// 属性列表
+const attributeList = ref([
+  {
+    id: 1,
+    name: '颜色',
+    attributeValues: [{ id: 1, name: '白色' }]
+  }
+])
+const addAttribute = async (propertyId: number) => {
+  const data = await PropertyApi.getPropertyValuePage({ id: propertyId })
+  console.log(data)
+}
+const formData = reactive<SpuType>({
   name: '', // 商品名称
   categoryId: undefined, // 商品分类
   keyword: '', // 关键字
@@ -124,10 +131,46 @@ const formData = ref<SpuType>({
   picUrl: '', // 商品封面图
   sliderPicUrls: [], // 商品轮播图
   introduction: '', // 商品简介
-  deliveryTemplateId: '', // 运费模版
+  deliveryTemplateId: 1, // 运费模版
   selectRule: '', // 选择规则 TODO 暂定
   specType: false, // 商品规格
-  subCommissionType: false // 分销类型
+  subCommissionType: false, // 分销类型
+  skus: [
+    {
+      /**
+       * 商品价格,单位:分
+       */
+      price: 0,
+      /**
+       * 市场价,单位:分
+       */
+      marketPrice: 0,
+      /**
+       * 成本价,单位:分
+       */
+      costPrice: 0,
+      /**
+       * 商品条码
+       */
+      barCode: '',
+      /**
+       * 图片地址
+       */
+      picUrl: '',
+      /**
+       * 库存
+       */
+      stock: 0,
+      /**
+       * 商品重量,单位:kg 千克
+       */
+      weight: 0,
+      /**
+       * 商品体积,单位:m^3 平米
+       */
+      volume: 0
+    }
+  ]
 })
 const rules = reactive({
   name: [required],
@@ -148,7 +191,7 @@ watch(
   () => props.propFormData,
   (data) => {
     if (!data) return
-    copyValueToTarget(formData.value, data)
+    copyValueToTarget(formData, data)
   },
   {
     deep: true,
@@ -170,7 +213,7 @@ const validate = async () => {
       throw new Error('商品信息未完善!!')
     } else {
       // 校验通过更新数据
-      Object.assign(props.propFormData, formData.value)
+      Object.assign(props.propFormData, formData)
     }
   })
 }
@@ -180,13 +223,17 @@ const changeSpecType = (specType) => {
   console.log(specType)
 }
 // 分销类型
-const changeSubCommissionType = (subCommissionType) => {
-  console.log(subCommissionType)
+const changeSubCommissionType = () => {
+  // 默认为零,类型切换后也要重置为零
+  for (const item of formData.skus) {
+    item.subCommissionFirstPrice = 0
+    item.subCommissionSecondPrice = 0
+  }
 }
 // 选择属性确认
-const confirm = () => {}
+// const confirm = () => {}
 // 添加规格
-const addRule = () => {}
+// const addRule = () => {}
 const categoryList = ref() // 分类树
 onMounted(async () => {
   // 获得分类树

+ 1 - 1
src/views/mall/product/management/components/DescriptionForm.vue

@@ -7,7 +7,7 @@
   </el-form>
 </template>
 <script lang="ts" name="DescriptionForm" setup>
-import type { SpuType } from '@/api/mall/product/management/type'
+import type { SpuType } from '@/api/mall/product/management/type/spuType'
 import { Editor } from '@/components/Editor'
 import { PropType } from 'vue'
 import { copyValueToTarget } from '@/utils/object'

+ 1 - 1
src/views/mall/product/management/components/OtherSettingsForm.vue

@@ -50,7 +50,7 @@
 </template>
 <script lang="ts" name="OtherSettingsForm" setup>
 // 商品推荐
-import type { SpuType } from '@/api/mall/product/management/type'
+import type { SpuType } from '@/api/mall/product/management/type/spuType'
 import { PropType } from 'vue'
 import { copyValueToTarget } from '@/utils/object'
 

+ 82 - 0
src/views/mall/product/management/components/ProductAttributes.vue

@@ -0,0 +1,82 @@
+<template>
+  <el-col v-for="(item, index) in attributeList" :key="index">
+    <div>
+      <el-text class="mx-1">属性名:</el-text>
+      <el-text class="mx-1">{{ item.name }}</el-text>
+    </div>
+    <div>
+      <el-text class="mx-1">属性值:</el-text>
+      <el-tag
+        v-for="(value, valueIndex) in item.attributeValues"
+        :key="value.name"
+        :disable-transitions="false"
+        class="mx-1"
+        closable
+        @close="handleClose(index, valueIndex)"
+      >
+        {{ value.name }}
+      </el-tag>
+      <el-input
+        v-if="inputVisible"
+        ref="InputRef"
+        v-model="inputValue"
+        class="!w-20"
+        size="small"
+        @blur="handleInputConfirm(index)"
+        @keyup.enter="handleInputConfirm(index)"
+      />
+      <el-button v-else class="button-new-tag ml-1" size="small" @click="showInput(index)">
+        + 添加
+      </el-button>
+    </div>
+    <el-divider class="my-10px" />
+  </el-col>
+</template>
+
+<script lang="ts" name="ProductAttributes" setup>
+import { ElInput } from 'element-plus'
+
+const inputValue = ref('') // 输入框值
+const inputVisible = ref(false) // 输入框显隐控制
+const InputRef = ref<InstanceType<typeof ElInput>>() //标签输入框Ref
+const attributeList = ref([])
+const props = defineProps({
+  attributeData: {
+    type: Object,
+    default: () => {}
+  }
+})
+
+watch(
+  () => props.attributeData,
+  (data) => {
+    if (!data) return
+    attributeList.value = data
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+/** 删除标签 tagValue 标签值*/
+const handleClose = (index, valueIndex) => {
+  const av = attributeList.value[index].attributeValues
+  av.splice(valueIndex, 1)
+}
+/** 显示输入框并获取焦点 */
+const showInput = (index) => {
+  inputVisible.value = true
+  nextTick(() => {
+    InputRef.value[index]!.input!.focus()
+  })
+}
+/** 输入框失去焦点或点击回车时触发 */
+const handleInputConfirm = (index) => {
+  if (inputValue.value) {
+    // 因为ref再循环里,所以需要index获取对应的ref
+    attributeList.value[index].attributeValues.push({ name: inputValue.value })
+  }
+  inputVisible.value = false
+  inputValue.value = ''
+}
+</script>

+ 82 - 0
src/views/mall/product/management/components/ProductAttributesAddForm.vue

@@ -0,0 +1,82 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle">
+    <el-form
+      ref="formRef"
+      v-loading="formLoading"
+      :model="formData"
+      :rules="formRules"
+      label-width="80px"
+    >
+      <el-form-item label="名称" prop="name">
+        <el-input v-model="formData.name" placeholder="请输入名称" />
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input v-model="formData.remark" placeholder="请输入内容" type="textarea" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script lang="ts" name="ProductPropertyForm" setup>
+import * as PropertyApi from '@/api/mall/product/property'
+
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('添加商品属性') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formData = ref({
+  name: '',
+  remark: ''
+})
+const formRules = reactive({
+  name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+
+/** 打开弹窗 */
+const open = async () => {
+  dialogVisible.value = true
+  resetForm()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as PropertyApi.PropertyVO
+    // 检查属性是否已存在,如果有则返回属性和其下属性值
+    const res = await PropertyApi.getPropertyListAndValue({ name: data.name })
+    if (res.length === 0) {
+      const propertyId = await PropertyApi.createProperty(data)
+      emit('success', { id: propertyId, ...formData.value, values: [] })
+    } else {
+      emit(res[0]) // 因为只用一个
+    }
+    message.success(t('common.createSuccess'))
+    dialogVisible.value = false
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    name: '',
+    remark: ''
+  }
+  formRef.value?.resetFields()
+}
+</script>

+ 86 - 0
src/views/mall/product/management/components/SkuList/index.vue

@@ -0,0 +1,86 @@
+<template>
+  <el-table :data="SkuData" border class="tabNumWidth" size="small">
+    <el-table-column align="center" fixed="left" label="图片" min-width="100">
+      <template #default="{ row }">
+        <UploadImg v-model="row.picUrl" height="80px" width="100%" />
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="商品条码" min-width="120">
+      <template #default="{ row }">
+        <el-input v-model="row.barCode" :min="0" class="w-100%" />
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="销售价(分)" min-width="120">
+      <template #default="{ row }">
+        <el-input v-model="row.price" :min="0" class="w-100%" type="number" />
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="市场价(分)" min-width="120">
+      <template #default="{ row }">
+        <el-input v-model="row.marketPrice" :min="0" class="w-100%" type="number" />
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="成本价(分)" min-width="120">
+      <template #default="{ row }">
+        <el-input v-model="row.costPrice" :min="0" class="w-100%" type="number" />
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="库存" min-width="120">
+      <template #default="{ row }">
+        <el-input v-model="row.stock" :min="0" class="w-100%" type="number" />
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="重量(kg)" min-width="120">
+      <template #default="{ row }">
+        <el-input v-model="row.weight" :min="0" class="w-100%" type="number" />
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="体积(m^3)" min-width="120">
+      <template #default="{ row }">
+        <el-input v-model="row.volume" :min="0" class="w-100%" type="number" />
+      </template>
+    </el-table-column>
+    <template v-if="subCommissionType">
+      <el-table-column align="center" label="一级返佣(分)" min-width="120">
+        <template #default="{ row }">
+          <el-input v-model="row.subCommissionFirstPrice" :min="0" class="w-100%" type="number" />
+        </template>
+      </el-table-column>
+      <el-table-column align="center" label="二级返佣(分)" min-width="120">
+        <template #default="{ row }">
+          <el-input v-model="row.subCommissionSecondPrice" :min="0" class="w-100%" type="number" />
+        </template>
+      </el-table-column>
+    </template>
+  </el-table>
+</template>
+
+<script lang="ts" name="index" setup>
+import { propTypes } from '@/utils/propTypes'
+import { UploadImg } from '@/components/UploadFile'
+import { PropType } from 'vue'
+import type { SkuType } from '@/api/mall/product/management/type/skuType'
+
+const props = defineProps({
+  skuData: {
+    type: Array as PropType<SkuType>,
+    default: () => []
+  },
+  subCommissionType: propTypes.bool.def(false) // 分销类型
+})
+const SkuData = ref<SkuType[]>([])
+/**
+ * 将传进来的值赋值给SkuData
+ */
+watch(
+  () => props.skuData,
+  (data) => {
+    if (!data) return
+    SkuData.value = data
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+</script>

+ 10 - 12
src/views/mall/product/management/index.vue

@@ -47,12 +47,7 @@
           <Icon class="mr-5px" icon="ep:refresh" />
           重置
         </el-button>
-        <el-button
-          v-hasPermi="['product:brand:create']"
-          plain
-          type="primary"
-          @click="openForm('create')"
-        >
+        <el-button v-hasPermi="['product:brand:create']" plain type="primary" @click="openForm">
           <Icon class="mr-5px" icon="ep:plus" />
           新增
         </el-button>
@@ -133,8 +128,8 @@
 </template>
 <script lang="ts" name="ProductManagement" setup>
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
-
+import { dateFormatter } from '@/utils/formatTime' // 业务api
+import * as managementApi from '@/api/mall/product/management/spu' // const message = useMessage() // 消息弹窗
 // const message = useMessage() // 消息弹窗
 // const { t } = useI18n() // 国际化
 const { push } = useRouter() // 路由跳转
@@ -182,9 +177,9 @@ const queryFormRef = ref() // 搜索的表单
 const getList = async () => {
   loading.value = true
   try {
-    // const data = await ProductBrandApi.getBrandParam(queryParams)
-    // list.value = data.list
-    // total.value = data.total
+    const data = await managementApi.getSkuList(queryParams)
+    list.value = data.list
+    total.value = data.total
   } finally {
     loading.value = false
   }
@@ -201,7 +196,10 @@ const resetQuery = () => {
   handleQuery()
 }
 
-const openForm = () => {
+const openForm = (id?: number) => {
+  if (typeof id === 'number') {
+    push('/product/productManagementAdd?id=' + id)
+  }
   push('/product/productManagementAdd')
 }
 

+ 31 - 23
src/views/mall/product/property/index.vue

@@ -2,42 +2,49 @@
   <!-- 搜索工作栏 -->
   <ContentWrap>
     <el-form
-      class="-mb-15px"
-      :model="queryParams"
       ref="queryFormRef"
       :inline="true"
+      :model="queryParams"
+      class="-mb-15px"
       label-width="68px"
     >
       <el-form-item label="名称" prop="name">
         <el-input
           v-model="queryParams.name"
-          placeholder="请输入名称"
+          class="!w-240px"
           clearable
+          placeholder="请输入名称"
           @keyup.enter="handleQuery"
-          class="!w-240px"
         />
       </el-form-item>
       <el-form-item label="创建时间" prop="createTime">
         <el-date-picker
           v-model="queryParams.createTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
           :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
           class="!w-240px"
+          end-placeholder="结束日期"
+          start-placeholder="开始日期"
+          type="daterange"
+          value-format="YYYY-MM-DD HH:mm:ss"
         />
       </el-form-item>
       <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        <el-button @click="handleQuery">
+          <Icon class="mr-5px" icon="ep:search" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon class="mr-5px" icon="ep:refresh" />
+          重置
+        </el-button>
         <el-button
+          v-hasPermi="['product:property:create']"
           plain
           type="primary"
           @click="openForm('create')"
-          v-hasPermi="['product:property:create']"
         >
-          <Icon icon="ep:plus" class="mr-5px" /> 新增
+          <Icon class="mr-5px" icon="ep:plus" />
+          新增
         </el-button>
       </el-form-item>
     </el-form>
@@ -46,23 +53,23 @@
   <!-- 列表 -->
   <ContentWrap>
     <el-table v-loading="loading" :data="list">
-      <el-table-column label="编号" align="center" prop="id" />
-      <el-table-column label="名称" align="center" />
-      <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
+      <el-table-column align="center" label="编号" prop="id" />
+      <el-table-column align="center" label="名称" prop="name" />
+      <el-table-column :show-overflow-tooltip="true" align="center" label="备注" prop="remark" />
       <el-table-column
-        label="创建时间"
+        :formatter="dateFormatter"
         align="center"
+        label="创建时间"
         prop="createTime"
         width="180"
-        :formatter="dateFormatter"
       />
-      <el-table-column label="操作" align="center">
+      <el-table-column align="center" label="操作">
         <template #default="scope">
           <el-button
+            v-hasPermi="['product:property:update']"
             link
             type="primary"
             @click="openForm('update', scope.row.id)"
-            v-hasPermi="['product:property:update']"
           >
             编辑
           </el-button>
@@ -70,10 +77,10 @@
             <router-link :to="'/property/value/' + scope.row.id">属性值</router-link>
           </el-button>
           <el-button
+            v-hasPermi="['product:property:delete']"
             link
             type="danger"
             @click="handleDelete(scope.row.id)"
-            v-hasPermi="['product:property:delete']"
           >
             删除
           </el-button>
@@ -82,9 +89,9 @@
     </el-table>
     <!-- 分页 -->
     <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
       v-model:limit="queryParams.pageSize"
+      v-model:page="queryParams.pageNo"
+      :total="total"
       @pagination="getList"
     />
   </ContentWrap>
@@ -92,10 +99,11 @@
   <!-- 表单弹窗:添加/修改 -->
   <PropertyForm ref="formRef" @success="getList" />
 </template>
-<script setup lang="ts" name="ProductProperty">
+<script lang="ts" name="ProductProperty" setup>
 import { dateFormatter } from '@/utils/formatTime'
 import * as PropertyApi from '@/api/mall/product/property'
 import PropertyForm from './PropertyForm.vue'
+
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化