Эх сурвалжийг харах

✨ feat(DishManagement): 新增商品管理页面,包含搜索、列表展示及操作功能

Mcal 2 долоо хоног өмнө
parent
commit
45680d6b22

+ 503 - 0
src/views/dishManagement/dishCategory.vue

@@ -0,0 +1,503 @@
+<template>
+    <div class="app-container p-5">
+      <el-form
+        :model="queryParams"
+        ref="queryForm"
+        class="main-search"
+            :inline="true"
+        v-show="showSearch"
+      >
+        <el-form-item label="商品分类名称" prop="name">
+          <el-input
+            v-model="queryParams.name"
+            placeholder="请输入商品分类名称"
+            clearable
+            style="width: 240px"
+            @keyup.enter.native="handleQuery"
+          />
+        </el-form-item>
+        <el-form-item label="所属店铺" prop="store">
+          <el-select
+            v-model="queryParams.storeId"
+            placeholder="所属店铺"
+            clearable
+            style="width: 180px"
+          >
+            <el-option
+              :key="0"
+              label="公共分类"
+              v-if="!storeId"
+              :value="0"
+            />
+            <el-option
+              v-for="storeInfo in storeOptions"
+              :key="storeInfo.id"
+              :label="storeInfo.name"
+              :value="storeInfo.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+          <el-select
+            v-model="queryParams.status"
+            placeholder="状态"
+            clearable
+            style="width: 240px"
+          >
+            <el-option key="A" label="启用" value="A" />
+            <el-option key="N" label="禁用" value="N" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" size="mini" @click="handleQuery">
+            搜索
+          </el-button>
+          <el-button size="mini" @click="resetQuery">
+            重置
+          </el-button>
+          <el-button
+            type="primary"
+            plain
+            size="mini"
+            @click="handleAdd"
+          >
+            新增
+          </el-button>
+        </el-form-item>
+      </el-form>
+  
+      <el-table
+        ref="tables"
+        v-loading="loading"
+        :data="list"
+        @selection-change="handleSelectionChange"
+        :default-sort="defaultSort"
+        @sort-change="handleSortChange"
+      >
+        <el-table-column label="商品分类 ID" prop="id" width="80">
+          <template #default="scope">
+            <span>{{ showIdLastFour(scope.row.id) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="所属店铺" align="center">
+          <template #default="scope">
+            <span v-if="scope.row.storeName">{{ scope.row.storeName }}</span>
+            <span v-else>全部分类</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="名称" align="center" prop="name" />
+        <el-table-column label="图片" align="center" width="200">
+          <template #default="scope">
+            <img class="list-img" :src="scope.row.logo" />
+          </template>
+        </el-table-column>
+        <el-table-column label="创建时间" align="center" prop="createTime">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.createTime) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="更新时间" align="center" prop="updateTime">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.updateTime) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="状态" align="center" prop="status">
+          <template #default="scope">
+            <el-switch
+              v-model="scope.row.status"
+              active-value="A"
+              inactive-value="N"
+              @change="handleStatusChange(scope.row)"
+            ></el-switch>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="操作"
+          align="center"
+          class-name="small-padding fixed-width"
+        >
+          <template #default="scope">
+            <el-button
+              size="mini"
+              type="text"
+              icon="el-icon-edit"
+              v-if="storeId == scope.row.storeId || storeId == 0"
+              @click="handleUpdate(scope.row)"
+            >
+              修改
+            </el-button>
+            <el-button
+              size="mini"
+              type="text"
+              icon="el-icon-delete"
+              v-if="storeId == scope.row.storeId || storeId == 0"
+              @click="handleDelete(scope.row)"
+            >
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+  
+      <el-pagination
+        v-show="total > 0"
+        :total="total"
+        :page.sync="queryParams.page"
+        :limit.sync="queryParams.pageSize"
+        @pagination="getList"
+      />
+  
+      <!-- 添加或修改对话框 -->
+      <el-dialog
+        :title="title"
+        v-model="open"
+        class="common-dialog"
+        width="700px"
+        append-to-body
+      >
+        <el-form ref="addForm" :model="form" :rules="rules" >
+          <el-row>
+            <el-col :span="24">
+              <el-form-item label="商品分类名称" prop="name">
+                <el-input
+                  v-model="form.name"
+                  placeholder="请输入商品分类名称"
+                  maxlength="30"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="24">
+              <el-form-item label="排序序号" prop="sort">
+                <el-input-number v-model="form.sort" :min="0" />
+                <div class="form-tips">提示:序号越大越靠前</div>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="9">
+              <el-form-item label="图片" prop="image">
+                <el-upload
+                  :action="uploadAction"
+                  :before-upload="beforeUpload"
+                  accept="image/*"
+                  list-type="picture-card"
+                  :class="{ hide: hideUpload }"
+                  :file-list="uploadFiles"
+                  :auto-upload="true"
+                  :show-file-list="false"
+                  :headers="uploadHeader"
+                  :on-success="handleUploadSuccess"
+                >
+                  <img v-if="form.logo" :src="form.logo" class="list-img" />
+                  <i class="el-icon-plus"></i>
+                </el-upload>
+              </el-form-item>
+            </el-col>
+            <p class="form-tips">提示:请上传图片,仅支持图片格式,大小不超过 20MB</p>
+          </el-row>
+          <el-row>
+            <el-col :span="24">
+              <el-form-item label="备注">
+                <el-input
+                  v-model="form.description"
+                  type="textarea"
+                  placeholder="请输入备注内容"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="24">
+              <el-form-item label="状态">
+                <el-radio-group v-model="form.status">
+                  <el-radio key="A" label="A" value="A">启用</el-radio>
+                  <el-radio key="N" label="N" value="N">禁用</el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+          <el-button type="primary" @click="submitForm">确定</el-button>
+          <el-button @click="cancel">取消</el-button>
+        </div>
+      </el-dialog>
+    </div>
+  </template>
+  
+  <script setup>
+  import { ref, reactive, onMounted } from 'vue'
+  import { ElMessage, ElMessageBox } from 'element-plus'
+  
+  // 从 Vuex 中获取 storeId,这里假设 Vuex 配置正确
+  const storeId = ref(0)
+  
+  // 遮罩层
+  const loading = ref(true)
+  // 标题
+  const title = ref('')
+  // 选中数组
+  const ids = ref([])
+  // 非多个禁用
+  const multiple = ref(true)
+  // 显示搜索条件
+  const showSearch = ref(true)
+  // 图片根目录
+  const imagePath = ref('')
+  // 总条数
+  const total = ref(0)
+  // 表格数据
+  const list = ref([])
+  // 店铺列表
+  const storeOptions = ref([])
+  // 是否显示弹出层
+  const open = ref(false)
+  // 默认排序
+  const defaultSort = ref({ prop: 'sort', order: 'descending' })
+  // 表单参数
+  const form = ref({
+    storeId: 0,
+    id: '',
+    name: '',
+    logo: '',
+    sort: 0,
+    status: 'A'
+  })
+  // 上传地址
+  const uploadAction = ref('/backendApi/file/upload')
+  const uploadHeader = ref({ 'Access-Token': '假token' })
+  // 隐藏上传
+  const hideUpload = ref(false)
+  // 上传文件列表
+  const uploadFiles = ref([])
+  // 查询参数
+  const queryParams = reactive({
+    page: 1,
+    pageSize: 10,
+    name: '',
+    status: ''
+  })
+  // 表单校验
+  const rules = ref({
+    name: [
+      { required: true, message: '商品分类名称为必填项', trigger: 'blur' },
+      {
+        min: 2,
+        max: 200,
+        message: '商品分类名称长度应在 2 到 200 个字符之间',
+        trigger: 'blur'
+      }
+    ],
+    logo: [
+      { required: true, message: '请上传图片', trigger: 'blur' }
+    ]
+  })
+  
+  // 假数据模拟
+  const fakeGoodsCateList = [
+    {
+      id: 1,
+      storeName: '店铺1',
+      name: '分类1',
+      logo: 'https://example.com/logo1.png',
+      createTime: '2023-01-01 00:00:00',
+      updateTime: '2023-01-01 00:00:00',
+      status: 'A'
+    },
+    {
+      id: 2,
+      storeName: null,
+      name: '分类2',
+      logo: 'https://example.com/logo2.png',
+      createTime: '2023-01-02 00:00:00',
+      updateTime: '2023-01-02 00:00:00',
+      status: 'N'
+    }
+  ]
+  
+  const fakeStoreOptions = [
+    { id: 1, name: '店铺1' },
+    { id: 2, name: '店铺2' }
+  ]
+  
+  onMounted(() => {
+    getList()
+  })
+  
+  // 查询分类列表
+  const getList = () => {
+    loading.value = true
+    // 使用假数据
+    list.value = fakeGoodsCateList
+    total.value = fakeGoodsCateList.length
+    storeOptions.value = fakeStoreOptions
+    loading.value = false
+  }
+  
+  // 搜索按钮操作
+  const handleQuery = () => {
+    queryParams.page = 1
+    getList()
+  }
+  
+  // 重置按钮操作
+  const resetQuery = () => {
+    // 这里简单重置表单数据,未实现完整的表单重置
+    queryParams.name = ''
+    queryParams.status = ''
+    queryParams.page = 1
+    getList()
+  }
+  
+  // 状态修改
+  const handleStatusChange = (row) => {
+    let text = row.status == 'A' ? '启用' : '禁用'
+    ElMessageBox.confirm(`确认要将 ${row.name} 设置为 ${text} 吗?`, '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning'
+    }).then(() => {
+      // 模拟状态修改成功
+      ElMessage.success(`${text} 成功`)
+    }).catch(() => {
+      row.status = row.status === 'N' ? 'A' : 'N'
+    })
+  }
+  
+  // 多选框选中数据
+  const handleSelectionChange = (selection) => {
+    ids.value = selection.map((item) => item.id)
+    multiple.value = !selection.length
+  }
+  
+  // 排序触发事件
+  const handleSortChange = (column, prop, order) => {
+    queryParams.orderByColumn = column.prop
+    queryParams.isAsc = column.order
+    getList()
+  }
+  
+  // 新增按钮操作
+  const handleAdd = () => {
+    reset()
+    open.value = true
+    title.value = '新增商品分类'
+  }
+  
+  // 表单重置
+  const reset = () => {
+    form.value = {
+      id: '',
+      storeId: storeId.value,
+      name: '',
+      status: 'A',
+      logo: '',
+      sort: 0,
+      description: ''
+    }
+    uploadFiles.value = []
+  }
+  
+  // 取消按钮
+  const cancel = () => {
+    open.value = false
+    reset()
+  }
+  
+  const addForm = ref(null)
+  // 提交按钮
+  const submitForm = () => {
+    // const formEl = getCurrentInstance().refs.form
+    addForm.value.validate((valid) => {
+      if (valid) {
+        if (form.value.logo.length < 1) {
+          form.value.logo = '/static/defaultImage/none.png'
+        }
+        if (form.value.id) {
+          // 模拟修改成功
+          ElMessage.success('修改成功')
+          open.value = false
+          getList()
+        } else {
+          // 模拟新增成功
+          ElMessage.success('新增成功')
+          open.value = false
+          getList()
+        }
+      }
+    })
+  }
+  
+  // 修改按钮操作
+  const handleUpdate = (row) => {
+    reset()
+    const id = row.id
+    // 模拟获取数据成功
+    form.value = {
+      id: id,
+      storeId: row.storeId,
+      name: row.name,
+      logo: row.logo,
+      sort: row.sort,
+      status: row.status,
+      description: '模拟描述'
+    }
+    uploadFiles.value = [{ url: form.value.logo, status: 'finished' }]
+    open.value = true
+    title.value = '编辑商品分类'
+  }
+  
+  // 删除按钮操作
+  const handleDelete = (row) => {
+    const name = row.name
+    ElMessageBox.confirm(`确认要删除 ${name} 吗?`, '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning'
+    }).then(() => {
+      // 模拟删除成功
+      getList()
+      ElMessage.success('删除成功')
+    }).catch(() => {})
+  }
+  
+  // 图片上传之前的大小和类型判断
+  const beforeUpload = (file) => {
+    // 限制文件大小为 20MB
+    const isImage = file.type.startsWith('image/') // 判断文件类型是否为图片
+    const isLt2M = file.size / 1024 / 1024 < 20 // 判断文件大小是否小于 20MB
+  
+    if (!isImage) {
+      ElMessage.error('仅支持上传图片')
+    }
+  
+    if (!isLt2M) {
+      ElMessage.error('图片大小不能超过 20MB')
+    }
+  
+    return isImage && isLt2M // 返回 true 则允许上传,返回 false 则阻止上传
+  }
+  
+  const handleUploadSuccess = (file) => {
+    form.value.logo = file.data.url
+  }
+  
+  const showIdLastFour = (id) => {
+    return String(id).slice(-4)
+  }
+  
+  const parseTime = (time) => {
+    return time
+  }
+  
+  </script>
+  
+  <style scoped>
+  .common-dialog >>> .el-upload--picture-card {
+    width: 60px;
+    height: 50px;
+    line-height: 60px;
+  }
+  </style>    

+ 447 - 0
src/views/dishManagement/dishInfo/Sku.vue

@@ -0,0 +1,447 @@
+<template>
+    <div class="sku-list">
+      <template v-if="!disabled">
+        <div class="sku-list-head">
+          <el-button type="primary" size="mini" @click="addSkuRow">添加规格</el-button>
+        </div>
+        <div
+          class="sku-list-item"
+          v-for="(item, index) in skuData.attrList"
+          :key="index"
+        >
+          <div class="sku-list-item-main">
+            <div class="sku-list-item__layout">
+              <span class="span">规格名称</span>
+              <el-input
+                size="small"
+                v-model="item.name"
+                class="input"
+              ></el-input>
+            </div>
+            <div class="sku-list-item__layout">
+              <span class="span">规格值</span>
+              <div class="sku-list-item-tags">
+                <el-tag
+                  class="sku-list-item-tag"
+                  closable
+                  v-for="(subItem, i) in item.child"
+                  v-if="subItem.name"
+                  @close="removeSkuAttr(index, i)"
+                  :key="i"
+                >{{ subItem.name }}</el-tag>
+                <el-button
+                  size="small"
+                  icon="el-icon-plus"
+                  @click="addSkuAttr(index)"
+                >添加</el-button>
+              </div>
+            </div>
+          </div>
+          <el-button
+            type="text"
+            size="small"
+            class="sku-list-item-removeBtn"
+            @click="removeSkuRow(index)"
+          >删除规格</el-button>
+        </div>
+      </template>
+  
+      <div class="batch-setting">
+        <span class="label">批量设置</span>
+        <el-input size="small" v-model="batch.skuNo" placeholder="SKU 编号" class="input input-sn"></el-input>
+        <span class="create-sn" @click="createGoodsSn()">随机生成</span>
+        <el-input size="small" v-model="batch.price" placeholder="商品价格" class="input"></el-input>
+        <el-input size="small" v-model="batch.linePrice" placeholder="原价" class="input"></el-input>
+        <el-input size="small" v-model="batch.stock" placeholder="商品库存" class="input"></el-input>
+        <el-input size="small" v-model="batch.weight" placeholder="重量" class="input"></el-input>
+        <el-button size="small" type="danger" class="button" @click="batchSetSku()">确认设置</el-button>
+      </div>
+  
+      <el-table border :data="skuData.skuList">
+        <el-table-column label="序号" align="center" width="60">
+          <template #default="scope">
+            <span>{{ scope.$index + 1 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="图片" align="center" width="120">
+          <template #default="scope">
+            <el-upload
+              :disabled="disabled"
+              :action="uploadAction"
+              list-type="picture-card"
+              :auto-upload="true"
+              :headers="uploadHeader"
+              :file-list="[]"
+              :limit="1"
+              :on-success="(file) => onUploadImgSuccess(file, scope.$index)"
+              :show-file-list="false"
+            >
+              <img
+                v-if="scope.row.logo"
+                :src="uploadDomain + scope.row.logo"
+                class="sku-logo"
+              />
+              <i v-if="!scope.row.logo" class="el-icon-plus"></i>
+            </el-upload>
+          </template>
+        </el-table-column>
+        <el-table-column label="规格" align="center">
+          <template #default="scope">
+            <div class="spec-tag" v-for="spec in scope.row.specList">
+              <span class="i">{{ spec.value }}</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="SKU 编码" align="center" width="160">
+          <template #default="scope">
+            <el-input
+              :readonly="disabled"
+              v-model="scope.row.skuNo"
+            ></el-input>
+          </template>
+        </el-table-column>
+        <el-table-column label="销售价格" align="center">
+          <template #default="scope">
+            <el-input
+              :readonly="disabled"
+              v-model="scope.row.price"
+            ></el-input>
+          </template>
+        </el-table-column>
+        <el-table-column label="原价" align="center">
+          <template #default="scope">
+            <el-input
+              :readonly="disabled"
+              v-model="scope.row.linePrice"
+            ></el-input>
+          </template>
+        </el-table-column>
+        <el-table-column label="库存" align="center">
+          <template #default="scope">
+            <el-input :readonly="disabled" v-model="scope.row.stock"></el-input>
+          </template>
+        </el-table-column>
+        <el-table-column label="重量" align="center">
+          <template #default="scope">
+            <el-input :readonly="disabled" v-model="scope.row.weight"></el-input>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </template>
+  
+  <script setup>
+  import { ref, watch, defineProps, defineEmits } from 'vue';
+  import { ElMessage, ElMessageBox } from 'element-plus';
+  
+  // 模拟获取 token
+  const getToken = () => 'mockedToken';
+  
+  // 定义 props
+  const props = defineProps({
+    goodsId: {
+      type: String,
+      default: ""
+    },
+    uploadDomain: {
+      type: String,
+      default: ""
+    },
+    skuData: {
+      type: Object,
+      default: () => ({})
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    }
+  });
+  
+  // 定义 emits
+  const emits = defineEmits(['skuChange']);
+  
+  // 上传地址
+  const uploadAction = ref('/backendApi/file/upload');
+  const uploadHeader = ref({ 'Access-Token': getToken() });
+  
+  // 批量设置
+  const batch = ref({ price: '', skuNo: '', linePrice: '', weight: '', stock: '' });
+  
+  // 监听 skuData.attrList 变化
+  watch(() => props.skuData.attrList, () => {
+    if (!props.disabled) {
+      const newSkuList = getTable();
+      props.skuData.skuList = newSkuList;
+      emits('skuChange', props.skuData);
+    }
+  }, { deep: true, immediate: true });
+  
+  // 添加规格行
+  const addSkuRow = () => {
+    ElMessageBox.prompt('请输入规格名称', '添加规格', {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      inputPattern: /\S+/,
+      inputErrorMessage: '规格名称不能为空',
+      closeOnClickModal: false
+    }).then(({ value }) => {
+      // 模拟接口请求
+      const mockResponse = { data: { id: 1 } };
+      props.skuData.attrList.push({
+        id: mockResponse.data.id,
+        name: value,
+        child: []
+      });
+      emits('skuChange', props.skuData);
+    }).catch(() => {});
+  };
+  
+  // 删除规格行
+  const removeSkuRow = (i) => {
+    ElMessageBox.confirm('确认删除该规格行吗?', '删除确认', {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning'
+    }).then(() => {
+      // 模拟接口请求
+      const mockResponse = { data: {} };
+      props.skuData.attrList.splice(i, 1);
+      emits('skuChange', props.skuData);
+    }).catch(() => {});
+  };
+  
+  // 删除规格属性值
+  const removeSkuAttr = (a, b) => {
+    ElMessageBox.confirm('确认删除该规格属性值吗?', '删除确认', {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning'
+    }).then(() => {
+      // 模拟接口请求
+      const mockResponse = { data: {} };
+      props.skuData.attrList[a].child.splice(b, 1);
+      emits('skuChange', props.skuData);
+    }).catch(() => {});
+  };
+  
+  // 添加规格属性值
+  const addSkuAttr = (i) => {
+    ElMessageBox.prompt('请输入规格属性值', '添加规格属性值', {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      inputPattern: /\S+/,
+      inputErrorMessage: '规格属性值不能为空',
+      closeOnClickModal: false
+    }).then(({ value }) => {
+      // 模拟接口请求
+      const mockResponse = { data: { id: 1 } };
+      props.skuData.attrList[i].child.push({
+        id: mockResponse.data.id,
+        name: value,
+        value: value
+      });
+      emits('skuChange', props.skuData);
+    }).catch(() => {});
+  };
+  
+  // 批量设置 sku
+  const batchSetSku = () => {
+    props.skuData.skuList.forEach((skuInfo, index) => {
+      if (batch.value.skuNo) {
+        skuInfo.skuNo = batch.value.skuNo + index;
+      }
+      if (batch.value.price) {
+        if (batch.value.price > 0) {
+          skuInfo.price = batch.value.price;
+        } else {
+          ElMessage.error('价格必须大于零');
+          return;
+        }
+      }
+      if (batch.value.linePrice) {
+        if (batch.value.linePrice >= 0) {
+          skuInfo.linePrice = batch.value.linePrice;
+        } else {
+          ElMessage.error('原价必须大于等于零');
+          return;
+        }
+      }
+      if (batch.value.weight) {
+        if (batch.value.weight >= 0) {
+          skuInfo.weight = batch.value.weight;
+        } else {
+          ElMessage.error('重量必须大于等于零');
+          return;
+        }
+      }
+      if (batch.value.stock) {
+        if (batch.value.stock >= 0) {
+          skuInfo.stock = batch.value.stock;
+        } else {
+          ElMessage.error('库存必须大于等于零');
+          return;
+        }
+      }
+    });
+    emits('skuChange', props.skuData);
+  };
+  
+  // 生成随机条码
+  const createGoodsSn = () => {
+    let sn = (Math.random() + 1) * 10000000000000;
+    batch.value.skuNo = sn.toFixed(0);
+  };
+  
+  // 上传图片成功
+  const onUploadImgSuccess = (file, index) => {
+    if (!file) {
+      return;
+    }
+    const mockResponse = { data: { domain: 'https://example.com', fileName: 'image.jpg' } };
+    props.skuData.skuList[index].logo = mockResponse.data.fileName;
+    emits('skuChange', props.skuData);
+  };
+  
+  // 生成表格数据
+  const getTable = () => {
+    const table = [];
+    const attrValueAry = [];
+    const arr = [];
+    const tmpSkuData = (props.skuData.attrList || []).filter(
+      (d) => d.name != "" && d.child.length > 0
+    );
+    if (!tmpSkuData || tmpSkuData.length == 0) {
+      return [];
+    }
+  
+    tmpSkuData.forEach((item) => {
+      attrValueAry.push(item.child);
+    });
+  
+    function func(skuArr = [], i) {
+      for (let j = 0; j < attrValueAry[i].length; j++) {
+        if (i < attrValueAry.length - 1) {
+          skuArr[i] = attrValueAry[i][j];
+          func(skuArr, i + 1);
+        } else {
+          arr.push([...skuArr, attrValueAry[i][j]]);
+        }
+      }
+    }
+    func([], 0);
+  
+    arr.forEach((item) => {
+      let specIds = "",
+        specList = [],
+        findItem,
+        tableItem;
+      item.forEach((d) => {
+        specIds += specIds ? `-${d.id}` : `${d.id}`;
+        specList.push({ id: d.id, name: d.name, value: d.name });
+      });
+  
+      findItem =
+        props.skuData.initSkuList.find((item) => {
+          return specIds.includes(item.specIds);
+        }) || {};
+  
+      tableItem = Object.assign(
+        {
+          price: '',
+          linePrice: '',
+          skuNo: '',
+          stock: '',
+          logo: '',
+          weight: '0',
+          specList: specList
+        },
+        findItem,
+        {
+          specIds
+        }
+      );
+      tableItem.specList = specList;
+      table.push(tableItem);
+    });
+  
+    return table;
+  };
+  </script>
+  
+  <style lang="scss" scoped>
+  .sku-list {
+    &-head {
+      margin-bottom: 10px;
+    }
+    &-item {
+      display: flex;
+      align-items: center;
+      border: 1px solid #eee;
+      border-radius: 5px;
+      margin-bottom: 5px;
+      padding: 10px;
+      background: #f5f5f5;
+      &-main {
+        flex: 1;
+      }
+      &-removeBtn {
+        margin-left: 20px;
+        color: #f56c6c;
+      }
+      &__layout {
+        display: flex;
+        align-items: center;
+        margin-bottom: 5px;
+        &:last-child {
+          margin-bottom: 0;
+        }
+        .input {
+          width: 240px;
+        }
+        .span {
+          font-size: 13px;
+          font-weight: bold;
+          margin-right: 10px;
+          color: #666;
+        }
+      }
+      &-tags {
+        flex: 1;
+      }
+      &-tag {
+        margin-bottom: 10px;
+        margin-right: 10px;
+      }
+    }
+  }
+  .spec-tag {
+    margin: 10px 0px 10px 0px;
+    .i {
+      background: #cceeee;
+      color: #113a28;
+      padding: 5px;
+      border-radius: 12px;
+    }
+  }
+  .sku-logo {
+    height: 78px;
+    width: 78px;
+    border-radius: 6px;
+  }
+  .batch-setting {
+    margin: 20px 0px 3px 0px;
+    .input {
+      width: 100px;
+      margin-left: 10px;
+    }
+    .input-sn {
+      width: 140px;
+    }
+    .button {
+      margin-left: 5px;
+    }
+  }
+  .create-sn {
+    margin-left: 2px;
+  }
+  </style>    

+ 542 - 0
src/views/dishManagement/dishInfo/formUi.vue

@@ -0,0 +1,542 @@
+<template>
+    <div class="app-container">
+      <div class="main-panel">
+        <el-tabs v-model="activeTab" @tab-click="handleTabClick">
+          <el-tab-pane label="商品基本信息" name="base">
+            <div class="content">
+              <el-form ref="baseFormRef" :model="baseForm" :rules="baseRules">
+                <el-row>
+                  <el-col :span="24">
+                    <el-form-item label="商品名称" prop="name">
+                      <el-input
+                        class="input"
+                        v-model="baseForm.name"
+                        placeholder="请输入商品名称"
+                        maxlength="25"
+                      />
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row>
+                  <el-col :span="24">
+                    <el-form-item label="商品分类" prop="cateId">
+                      <el-select
+                        class="input"
+                        v-model="baseForm.cateId"
+                        placeholder="请选择商品分类"
+                      >
+                        <el-option
+                          v-for="item in cateOptions"
+                          :key="item.id"
+                          :label="item.name"
+                          :value="item.id"
+                          :disabled="item.status!== 'A'"
+                        ></el-option>
+                      </el-select>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row>
+                  <el-col :span="24">
+                    <el-form-item label="商品图片" prop="images">
+                      <el-upload
+                        class="form__head-icon-upload"
+                        :action="uploadAction"
+                        :before-upload="beforeUpload"
+                        accept="image/*"
+                        :class="{ hide: hideUpload }"
+                        list-type="picture-card"
+                        :file-list="uploadFiles"
+                        :limit="10"
+                        :auto-upload="true"
+                        :headers="uploadHeader"
+                        :on-success="handleUploadSuccess"
+                        :on-remove="handleRemove"
+                      >
+                        <i class="el-icon-plus"></i>
+                      </el-upload>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row>
+                  <el-col :span="24">
+                    <el-form-item label="展示排序" prop="sort">
+                      <el-input-number v-model="baseForm.sort" :min="0" />
+                      <div class="form-tips">提示:数值越小排名越靠前</div>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row>
+                  <el-col :span="24">
+                    <el-form-item label="商品状态">
+                      <el-radio-group v-model="baseForm.status">
+                        <el-radio key="A" label="上架" value="A"></el-radio>
+                        <el-radio key="N" label="下架" value="N"></el-radio>
+                      </el-radio-group>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+              </el-form>
+            </div>
+          </el-tab-pane>
+          <el-tab-pane label="商品扩展信息" name="extend">
+            <div class="content">
+              <el-form ref="extendFormRef" :model="extendForm" :rules="extendRules">
+                <el-row>
+                  <el-col :span="24">
+                    <el-form-item label="规格类型" prop="isSingleSpec">
+                      <el-radio-group v-model="extendForm.isSingleSpec">
+                        <el-radio key="Y" label="单一规格" value="Y"></el-radio>
+                        <el-radio key="N" label="多种规格" value="N"></el-radio>
+                      </el-radio-group>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row v-if="extendForm.isSingleSpec === 'N'">
+                  <el-col :span="24">
+                    <el-form-item label="商品规格" prop="goodsSpec">
+                      <Sku
+                        ref="Sku"
+                        :skuData="skuData"
+                        :goodsId="baseForm.goodsId"
+                        :uploadDomain="uploadDomain"
+                        @skuChange="skuChange"
+                      />
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row v-if="baseForm.type ==='service'">
+                  <el-col :span="24">
+                    <el-form-item label="服务时长" prop="serviceTime">
+                      <el-input
+                        v-model="extendForm.serviceTime"
+                        class="min-input"
+                        placeholder="请输入服务时长"
+                        maxlength="50"
+                      />
+                      <div class="form-tips">请输入服务时长,如 1 小时等</div>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row v-if="baseForm.type === 'coupon'">
+                  <el-col :span="24">
+                    <el-form-item label="优惠券 ID" prop="couponIds">
+                      <el-input
+                        v-model="extendForm.couponIds"
+                        class="input"
+                        rows="2"
+                        type="textarea"
+                        placeholder="请输入优惠券 ID"
+                        maxlength="1000"
+                      />
+                      <div class="form-tips">请输入有效的优惠券 ID,多个用逗号分隔</div>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row v-if="extendForm.isSingleSpec === 'Y'">
+                  <el-col :span="24">
+                    <el-form-item label="库存数量" prop="stock">
+                      <el-input
+                        v-model="extendForm.stock"
+                        class="min-input"
+                        placeholder="请输入库存数量"
+                        maxlength="50"
+                      />
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row v-if="extendForm.isSingleSpec === 'Y'">
+                  <el-col :span="24">
+                    <el-form-item label="商品价格" prop="price">
+                      <el-input
+                        v-model="extendForm.price"
+                        class="min-input"
+                        placeholder="请输入商品价格"
+                        maxlength="50"
+                      />
+                      <div class="form-tips">单位:元</div>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row v-if="extendForm.isSingleSpec === 'Y' && baseForm.type === 'product'">
+                  <el-col :span="24">
+                    <el-form-item label="商品重量" prop="weight">
+                      <el-input
+                        v-model="extendForm.weight"
+                        class="min-input"
+                        placeholder="请输入商品重量"
+                        maxlength="10"
+                      />
+                      <div class="form-tips">提示:输入数字,单位 kg</div>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+              </el-form>
+            </div>
+          </el-tab-pane>
+          <!-- <el-tab-pane label="商品描述" name="detail">
+            <div class="content" style="width: 375px; margin-left: 80px">
+              <el-form ref="detailFormRef" :model="detailForm" :rules="detailRules">
+                <editor v-model="detailForm.description" :min-height="550" />
+              </el-form>
+            </div>
+          </el-tab-pane> -->
+        </el-tabs>
+        <div slot="footer" class="footer">
+          <el-button type="primary" @click="submitForm">确定</el-button>
+          <el-button @click="cancel">取消</el-button>
+        </div>
+      </div>
+    </div>
+  </template>
+  
+  <script setup>
+  import { ref, onMounted } from 'vue';
+  import { ElMessage, ElMessageBox } from 'element-plus';
+  import Sku from './Sku.vue';
+  
+  // 模拟获取 token
+  const getToken = () => 'mockedToken';
+  
+  // 初始化数据
+  const storeId = ref(0);
+  const loading = ref(false);
+  const activeTab = ref('base');
+  const storeOptions = ref([]);
+  const cateOptions = ref([
+    { id: 1, name: '分类 1', status: 'A' },
+    { id: 2, name: '分类 2', status: 'A' }
+  ]);
+  const typeOptions = ref([
+    { key: 'product', name: '商品' },
+    { key: 'service', name: '服务' },
+    { key: 'coupon', name: '优惠券' }
+  ]);
+  const skuData = ref({ attrList: [], skuList: [], initSkuList: [] });
+  
+  const baseForm = ref({
+    type: 'product',
+    goodsId: '',
+    name: '',
+    storeId: 0,
+    cateId: '',
+    goodsNo: '',
+    images: [],
+    status: 'A',
+    sort: 0
+  });
+  
+  const extendForm = ref({
+    goodsId: '',
+    canUsePoint: 'Y',
+    isMemberDiscount: 'Y',
+    isSingleSpec: 'Y',
+    serviceTime: 0,
+    couponIds: '',
+    stock: '',
+    price: '',
+    linePrice: '',
+    salePoint: '',
+    initSale: '',
+    weight: '',
+    skuData: null
+  });
+  
+  const detailForm = ref({ goodsId: '', description: '' });
+  
+  const uploadAction = ref('/backendApi/file/upload');
+  const uploadHeader = ref({ 'Access-Token': getToken() });
+  const uploadDomain = ref('');
+  const hideUpload = ref(false);
+  const uploadFiles = ref([]);
+  
+  const baseRules = ref({
+    name: [
+      { required: true, message: '商品名称为必填项', trigger: 'blur' },
+      { min: 2, max: 30, message: '商品名称长度需在 2 - 30 个字符之间', trigger: 'blur' }
+    ],
+    cateId: [
+      { required: true, message: '请选择商品分类', trigger: 'blur' }
+    ],
+    images: [
+      { required: true, message: '请上传商品图片', trigger: 'blur' }
+    ]
+  });
+  
+  const extendRules = ref({
+    couponIds: [
+      { required: true, message: '优惠券 ID 为必填项', trigger: 'blur' },
+      { min: 1, max: 1000, message: '优惠券 ID 长度需在 1 - 1000 个字符之间', trigger: 'blur' }
+    ],
+    isSingleSpec: [
+      { required: true, message: '请选择规格类型', trigger: 'blur' }
+    ],
+    price: [
+      { required: true, message: '商品价格为必填项', trigger: 'blur' },
+      {
+        pattern: /(^[1-9]\d*(\.\d{1,2})?$)|(^0(\.\d{1,2})?$)/,
+        message: '商品价格需为正数',
+        trigger: 'blur'
+      }
+    ],
+    stock: [
+      { required: true, message: '库存数量为必填项', trigger: 'blur' },
+      { pattern: /^[0-9]{1,10}$/, message: '库存数量需为正整数', trigger: 'blur' }
+    ],
+    weight: [
+      {
+        pattern: /(^[1-9]\d*(\.\d{1,2})?$)|(^0(\.\d{1,2})?$)/,
+        message: '商品重量需为非负数',
+        trigger: 'blur'
+      }
+    ]
+  });
+  
+  const detailRules = ref({
+    description: [
+      { required: true, message: '商品描述为必填项', trigger: 'blur' }
+    ]
+  });
+  
+  const baseFormRef = ref(null);
+  const extendFormRef = ref(null);
+  const detailFormRef = ref(null);
+  
+  // 模拟获取商品信息
+  const getGoodsInfo = (goodsId) => {
+    return new Promise((resolve) => {
+      setTimeout(() => {
+        const mockResponse = {
+          data: {
+            goods: {
+              id: 1,
+              type: 'product',
+              name: '示例商品',
+              goodsNo: '123456',
+              cateId: 1,
+              storeId: 0,
+              sort: 0,
+              status: 'A',
+              canUsePoint: 'Y',
+              isMemberDiscount: 'Y',
+              isSingleSpec: 'Y',
+              stock: '10',
+              price: '9.99',
+              linePrice: '19.99',
+              initSale: '0',
+              salePoint: '优质商品',
+              weight: '1.5',
+              serviceTime: '',
+              couponIds: '',
+              description: '这是示例商品的描述'
+            },
+            imagePath: 'https://example.com/images/',
+            images: ['image1.jpg'],
+            specData: [],
+            skuData: [],
+            cateList: [
+              { id: 1, name: '分类 1', status: 'A' },
+              { id: 2, name: '分类 2', status: 'A' }
+            ],
+            storeList: [],
+            typeList: [
+              { key: 'product', name: '商品' },
+              { key: 'service', name: '服务' },
+              { key: 'coupon', name: '优惠券' }
+            ]
+          }
+        };
+  
+        const goodsInfo = mockResponse.data.goods;
+        const imagePath = mockResponse.data.imagePath;
+        uploadDomain.value = imagePath;
+  
+        baseForm.value.goodsId = goodsInfo.id + '';
+        baseForm.value.type = goodsInfo.type;
+        baseForm.value.name = goodsInfo.name;
+        baseForm.value.goodsNo = goodsInfo.goodsNo;
+        baseForm.value.cateId = goodsInfo.cateId;
+        baseForm.value.storeId = goodsInfo.storeId;
+        baseForm.value.sort = goodsInfo.sort;
+        baseForm.value.status = goodsInfo.status;
+        baseForm.value.images = mockResponse.data.images;
+        uploadFiles.value = mockResponse.data.images.map(url => ({ url: imagePath + url }));
+  
+        extendForm.value.goodsId = goodsInfo.id;
+        extendForm.value.canUsePoint = goodsInfo.canUsePoint;
+        extendForm.value.isMemberDiscount = goodsInfo.isMemberDiscount;
+        extendForm.value.isSingleSpec = goodsInfo.isSingleSpec;
+        extendForm.value.stock = goodsInfo.stock;
+        extendForm.value.price = goodsInfo.price;
+        extendForm.value.linePrice = goodsInfo.linePrice;
+        extendForm.value.initSale = goodsInfo.initSale;
+        extendForm.value.salePoint = goodsInfo.salePoint;
+        extendForm.value.weight = goodsInfo.weight;
+        extendForm.value.serviceTime = goodsInfo.serviceTime;
+        extendForm.value.couponIds = goodsInfo.couponIds;
+  
+        skuData.value.attrList = mockResponse.data.specData;
+        skuData.value.skuList = mockResponse.data.skuData;
+        skuData.value.initSkuList = mockResponse.data.skuData;
+  
+        detailForm.value.goodsId = goodsInfo.id;
+        detailForm.value.description = goodsInfo.description;
+  
+        cateOptions.value = mockResponse.data.cateList;
+        storeOptions.value = mockResponse.data.storeList;
+        typeOptions.value = mockResponse.data.typeList;
+  
+        resolve();
+      }, 500);
+    });
+  };
+  
+  const props = defineProps({
+    detailId:{
+        type:String,
+        default:0,
+    }
+  })
+  onMounted(async () => {
+    const goodsId = 1;
+    await getGoodsInfo(goodsId);
+  });
+  
+  const handleTabClick = () => {};
+  
+  const cancel = () => {
+    // 这里可添加返回上一页或其他取消操作的逻辑
+    console.log('取消操作');
+  };
+  
+  const skuChange = (newSkuData) => {
+    skuData.value = newSkuData;
+  };
+  
+  const submitForm = async () => {
+    if (activeTab.value === 'base') {
+      baseFormRef.value.validate(async (valid) => {
+        if (valid) {
+          // 模拟保存基础信息
+          ElMessage.success('商品基本信息保存成功');
+          await getGoodsInfo(baseForm.value.goodsId);
+        } else {
+          ElMessage.error('商品基本信息表单验证失败,请检查');
+        }
+      });
+    } else if (activeTab.value === 'extend') {
+      if (!extendForm.value.goodsId) {
+        ElMessage.error('请先提交商品基本信息');
+        activeTab.value = 'base';
+        return;
+      }
+  
+      if (skuData.value.skuList && skuData.value.skuList.length > 0) {
+        let isValidSkuNo = true;
+        let isValidStock = true;
+        let isValidPrice = true;
+  
+        skuData.value.skuList.forEach((item) => {
+          if (!item.skuNo || item.skuNo.length < 1) {
+            isValidSkuNo = false;
+          }
+          if (item.stock < 0) {
+            isValidStock = false;
+          }
+          if (item.price < 0) {
+            isValidPrice = false;
+          }
+        });
+  
+        if (!isValidSkuNo) {
+          ElMessageBox.alert('SKU 编号不合法,请检查');
+          return;
+        }
+        if (!isValidStock) {
+          ElMessageBox.alert('库存数量不能为负数,请检查');
+          return;
+        }
+        if (!isValidPrice) {
+          ElMessageBox.alert('商品价格不能为负数,请检查');
+          return;
+        }
+      }
+  
+      extendForm.value.skuData = skuData.value.skuList;
+      extendForm.value.specData = skuData.value.attrList;
+  
+      extendFormRef.value.validate(async (valid) => {
+        if (valid) {
+          // 模拟保存扩展信息
+          ElMessage.success('商品扩展信息保存成功');
+          await getGoodsInfo(extendForm.value.goodsId);
+        } else {
+          ElMessage.error('商品扩展信息表单验证失败,请检查');
+        }
+      });
+    } else {
+      if (!detailForm.value.goodsId) {
+        ElMessage.error('请先提交商品基本信息');
+        activeTab.value = 'base';
+        return;
+      }
+  
+      detailFormRef.value.validate(async (valid) => {
+        if (valid) {
+          // 模拟保存详情信息
+          ElMessage.success('商品描述信息保存成功');
+          await getGoodsInfo(detailForm.value.goodsId);
+        } else {
+          ElMessage.error('商品描述信息表单验证失败,请检查');
+        }
+      });
+    }
+  };
+  
+  const beforeUpload = (file) => {
+    const isImage = file.type.startsWith('image/');
+    const isLt20M = file.size / 1024 / 1024 < 20;
+  
+    if (!isImage) {
+      ElMessage.error('仅支持上传图片文件');
+    }
+    if (!isLt20M) {
+      ElMessage.error('文件大小不能超过 20MB');
+    }
+  
+    return isImage && isLt20M;
+  };
+  
+  const handleUploadSuccess = (response) => {
+    baseForm.value.images.push(response.data.url);
+  };
+  
+  const handleRemove = (file) => {
+    const newImages = baseForm.value.images.filter(item => file.url.indexOf(item) === -1);
+    baseForm.value.images = newImages;
+  };
+  
+  const createGoodsSn = () => {
+    let sn = (Math.random() + 1) * 100000000000000;
+    baseForm.value.goodsNo = sn.toFixed(0);
+  };
+  </script>
+  
+  <style scoped lang="scss">
+  .main-panel {
+    padding-top: 5px;
+    .content {
+      margin-top: 30px;
+      margin-left: 20px;
+    }
+    .footer {
+      margin-top: 20px;
+    }
+    .create-sn {
+      font-size: 12px;
+      color: blue;
+      cursor: pointer;
+      width: 100px;
+    }
+  }
+  </style>    

+ 362 - 0
src/views/dishManagement/dishInfo/index.vue

@@ -0,0 +1,362 @@
+<template>
+    <div class="app-container p-5">
+        <div class="h-full w-full" v-show="showInfo">
+            <el-form :model="queryParams" ref="queryForm" class="main-search" :inline="true" v-show="showSearch">
+                <el-form-item label="商品名称" prop="name">
+                    <el-input v-model="queryParams.name" placeholder="请输入商品名称" clearable style="width: 240px"
+                        @keyup.enter.native="handleQuery" />
+                </el-form-item>
+                <el-form-item label="所属店铺" prop="store">
+                    <el-select v-model="queryParams.storeId" placeholder="所属店铺" clearable style="width: 180px">
+                        <el-option :key="0" label="公共商品" v-if="!storeId" :value="0" />
+                        <el-option v-for="storeInfo in storeOptions" :key="storeInfo.id" :label="storeInfo.name"
+                            :value="storeInfo.id" />
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="商品分类" prop="cateId" style="width: 240px">
+                    <el-select class="input" v-model="queryParams.cateId" clearable placeholder="请选择商品分类">
+                        <el-option v-for="item in cateList" :key="item.id" :label="item.name" :value="item.id"
+                            :disabled="item.status !== 'A'"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="商品类型" prop="type" style="width: 240px">
+                    <el-select class="input" v-model="queryParams.type" clearable placeholder="请选择商品类型">
+                        <el-option v-for="item in typeOptions" :key="item.key" :label="item.name"
+                            :value="item.key"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="规格类型" prop="isSingleSpec" style="width: 200px">
+                    <el-select v-model="queryParams.isSingleSpec" placeholder="规格类型" clearable>
+                        <el-option key="Y" label="单一规格" value="Y" />
+                        <el-option key="N" label="多种规格" value="N" />
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="上架状态" prop="status">
+                    <el-select v-model="queryParams.status" placeholder="上架状态" style="width: 150px" clearable>
+                        <el-option key="A" label="上架" value="A" />
+                        <el-option key="N" label="下架" value="N" />
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="库存状态" prop="stock" style="width: 200px">
+                    <el-select v-model="queryParams.stock" placeholder="库存状态" clearable>
+                        <el-option key="Y" label="有库存" value="Y" />
+                        <el-option key="N" label="无库存" value="N" />
+                    </el-select>
+                </el-form-item>
+                <el-form-item>
+                    <el-button type="primary" size="mini" @click="handleQuery">
+                        搜索
+                    </el-button>
+                    <el-button size="mini" @click="resetQuery">
+                        重置
+                    </el-button>
+                    <el-button type="primary" plain size="mini" @click="handleAdd">
+                        新增
+                    </el-button>
+                </el-form-item>
+            </el-form>
+
+            <el-table ref="tables" v-loading="loading" :data="list" @selection-change="handleSelectionChange"
+                :default-sort="defaultSort" @sort-change="handleSortChange" class="p-3">
+                <el-table-column label="ID" prop="id" width="80">
+                    <template #default="scope">
+                        <span>{{ showIdLastFour(scope.row.id) }}</span>
+                    </template>
+                </el-table-column>
+                <el-table-column label="店铺名称" align="center">
+                    <template #default="scope">
+                        <span v-if="scope.row.storeInfo">{{ scope.row.storeInfo.name }}</span>
+                        <span v-else>公共所有</span>
+                    </template>
+                </el-table-column>
+                <el-table-column label="商品名称" align="center" min-width="200" prop="name" />
+                <el-table-column label="主图" align="center" width="100">
+                    <template #default="scope">
+                        <img class="list-img" :src="scope.row.logo" alt="" />
+                    </template>
+                </el-table-column>
+                <el-table-column label="剩余库存" align="center" prop="stock" width="100" />
+                <el-table-column label="商品分类" align="center">
+                    <template #default="scope">
+                        <span>{{ scope.row.cateInfo.name }}</span>
+                    </template>
+                </el-table-column>
+                <el-table-column label="价格" align="center" width="80">
+                    <template #default="scope">
+                        <span v-if="scope.row.price">{{ scope.row.price.toFixed(2) }}</span>
+                        <span v-else>0.00</span>
+                    </template>
+                </el-table-column>
+                <el-table-column label="上架状态" align="center" prop="status">
+                    <template #default="scope">
+                        <el-switch v-model="scope.row.status" active-value="A" inactive-value="N"
+                            @change="handleStatusChange(scope.row)"></el-switch>
+                    </template>
+                </el-table-column>
+                <el-table-column label="创建时间" align="center" width="150" prop="createTime">
+                    <template #default="scope">
+                        <span>{{ parseTime(scope.row.createTime) }}</span>
+                    </template>
+                </el-table-column>
+                <el-table-column label="更新时间" align="center" width="150" prop="updateTime">
+                    <template #default="scope">
+                        <span>{{ parseTime(scope.row.updateTime) }}</span>
+                    </template>
+                </el-table-column>
+                <el-table-column label="操作" align="center" width="180" fixed="right">
+                    <template #default="scope">
+                        <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
+                            v-if="storeId == scope.row.storeId || storeId == 0">
+                            编辑
+                        </el-button>
+                        <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
+                            v-if="storeId == scope.row.storeId || storeId == 0">
+                            删除
+                        </el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+
+            <el-pagination v-show="total > 0" :total="total" :page.sync="queryParams.page"
+                :limit.sync="queryParams.pageSize" @pagination="getList" class="p-3" />
+        </div>
+        <div class="h-full w-full " v-show="!showInfo">
+            <formUi :detailId="detailId"></formUi>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import formUi from './formUi.vue'
+// 从 Vuex 中获取 storeId,这里假设 Vuex 配置正确
+const storeId = ref(0)
+
+// 遮罩层
+const loading = ref(true)
+
+// 选中数组
+const ids = ref([])
+
+// 非多个禁用
+const multiple = ref(true)
+
+// 显示搜索条件
+const showSearch = ref(true)
+
+// 总条数
+const total = ref(0)
+
+// 表格数据
+const list = ref([])
+
+// 店铺列表
+const storeOptions = ref([])
+
+// 商品类型
+const typeOptions = ref([])
+
+// 商品分类
+const cateList = ref([])
+
+// 是否显示弹出层
+const open = ref(false)
+
+// 默认排序
+const defaultSort = ref({ prop: 'sort', order: 'descending' })
+
+// 表单参数
+const form = ref({ id: '', name: '', logo: '', sort: 0, status: 'A' })
+
+// 上传地址
+const uploadAction = ref('backendApi/file/upload')
+
+// 隐藏上传
+const hideUpload = ref(false)
+
+// 上传文件列表
+const uploadFiles = ref([])
+
+// 查询参数
+const queryParams = reactive({
+    page: 1,
+    pageSize: 10,
+    storeId: '',
+    cateId: '',
+    name: '',
+    isSingleSpec: '',
+    goodsNo: '',
+    stock: '',
+    status: ''
+})
+
+// 表单校验
+const rules = ref({
+    name: [
+        { required: true, message: '名称不能为空', trigger: 'blur' },
+        { min: 2, max: 200, message: '名称长度必须介于2 和 200 之间', trigger: 'blur' }
+    ],
+    logo: [{ required: true, message: '请上传图片', trigger: 'blur' }]
+})
+
+// 假数据模拟
+const fakeGoodsList = [
+    {
+        id: 1,
+        storeInfo: { name: '店铺1' },
+        name: '商品1',
+        logo: 'https://example.com/logo1.png',
+        stock: 'Y',
+        cateInfo: { name: '分类1' },
+        price: 10.99,
+        status: 'A',
+        createTime: '2023-01-01 00:00:00',
+        updateTime: '2023-01-01 00:00:00'
+    },
+    {
+        id: 2,
+        storeInfo: null,
+        name: '商品2',
+        logo: 'https://example.com/logo2.png',
+        stock: 'N',
+        cateInfo: { name: '分类2' },
+        price: 0,
+        status: 'N',
+        createTime: '2023-01-02 00:00:00',
+        updateTime: '2023-01-02 00:00:00'
+    }
+]
+
+const fakeStoreOptions = [
+    { id: 1, name: '店铺1' },
+    { id: 2, name: '店铺2' }
+]
+
+const fakeTypeOptions = [
+    { key: 'type1', name: '类型1' },
+    { key: 'type2', name: '类型2' }
+]
+
+const fakeCateList = [
+    { id: 1, name: '分类1', status: 'A' },
+    { id: 2, name: '分类2', status: 'A' }
+]
+
+onMounted(() => {
+    getList()
+})
+
+// 查询商品列表
+const getList = () => {
+    loading.value = true
+    // 使用假数据
+    list.value = fakeGoodsList
+    total.value = fakeGoodsList.length
+    storeOptions.value = fakeStoreOptions
+    typeOptions.value = fakeTypeOptions
+    cateList.value = fakeCateList
+    loading.value = false
+}
+
+// 搜索按钮操作
+const handleQuery = () => {
+    queryParams.page = 1
+    getList()
+}
+
+// 重置按钮操作
+const resetQuery = () => {
+    // 这里简单重置表单数据,未实现完整的表单重置
+    queryParams.name = ''
+    queryParams.status = ''
+    queryParams.page = 1
+    getList()
+}
+
+// 状态修改
+const handleStatusChange = (row) => {
+    let text = row.status === 'A' ? '上架' : '下架'
+    ElMessageBox.confirm(`确认要${text}“${row.name}”吗?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(() => {
+            // 模拟状态修改成功
+            ElMessage.success(`${text}成功`)
+        })
+        .catch(() => {
+            row.status = row.status === 'N' ? 'A' : 'N'
+        })
+}
+
+// 多选框选中数据
+const handleSelectionChange = (selection) => {
+    ids.value = selection.map((item) => item.id)
+    multiple.value = !selection.length
+}
+
+// 排序触发事件
+const handleSortChange = (column, prop, order) => {
+    queryParams.orderByColumn = column.prop
+    queryParams.isAsc = column.order
+    getList()
+}
+
+const showInfo = ref(true)
+const detailId = ref(0)
+// 新增按钮操作
+const handleAdd = () => {
+    // 这里简单模拟跳转,实际需根据路由配置调整
+    console.log('跳转到新增页面')
+    showInfo.value = false
+    detailId.value = 0
+}
+
+// 修改按钮操作
+const handleUpdate = (row) => {
+    // 这里简单模拟跳转,实际需根据路由配置调整
+    console.log(`跳转到编辑页面,商品ID: ${row.id}`)
+    showInfo.value = false
+    detailId.value = row.id
+}
+
+// 删除按钮操作
+const handleDelete = (row) => {
+    const name = row.name
+    ElMessageBox.confirm(`确认要删除“${name}”吗?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(() => {
+            // 模拟删除成功
+            getList()
+            ElMessage.success('删除成功')
+        })
+        .catch(() => { })
+}
+
+const handleUploadSuccess = (file) => {
+    form.value.logo = file.data.fileName
+}
+
+const handleRemove = (file, fileList) => {
+    setTimeout(() => {
+        hideUpload.value = fileList.length > 0
+    }, 520)
+}
+
+const showIdLastFour = (id) => {
+    return String(id).slice(-4)
+}
+
+const parseTime = (time) => {
+    return time
+}
+</script>
+
+<style scoped>
+/* 这里可以添加你的样式 */
+</style>