低代码可视化-商城管理系统-商品管理-代码生成器

简介: 低代码可视化-商城管理系统-商品管理-代码生成器

产品管理是商城管理系统的核心模块之一,它直接关系到商品的流通效率、库存管理和销售策略。通过高效的产品管理,商城能够实时掌握商品信息,优化库存结构,提高销售业绩。

新增数据库表

拷贝shop_cate修改为shop_goods表


修改表结构

其中包括一个自增ID字段,及三个时间类型的字段,其余的字段按照需求增加,这里title,remark,cate_id,price,line_price,amount,sellamount,skus,sku_type等个字段。其中我们用sku_type来区分是单规格还是多规格字段。

生成API

输入你安装的PHP时的域名/super/index.html,登录进去找到代码生成器界面。


新增页面

可以直接复制分类页面然后修改页面页面标识、页面名称、字体图标.


修改API

结合表shop_goods表,那么我们在后台API属性设置即为shop/goods即可。

修改表格字段

 


表单编辑字段

由于商品信息比较多,我们采用选项卡的方式来设计表单,基本信息、规格库存、商品详情。基本基本信息拖入单行文本字段进编辑表单设计区,输入字段标识、字段标识标题。


产品分类

产品分类字段比较特符,数据来源产品分类表的数据。这时要采用API把分类数据加载出来。


规格库存


规格库存用来设计价格等。其中规格类型我们用单选框组件,然后用FLEX组件把所有单规格输入组件包裹起来,增加显示判断,只有但单规格的时候才显示。

点击保存源码至本地

保存源码至本地后打开页面即可访问商品分类管理界面。

进入页面

启动环境进进入产品管理,在右上角可以设计不同主题效果。

这时增删改查已经完成。


数据查询

输入单选文本,字段名称改为title_like支持模板查询。PHP更多扩展查询参照此地址:PHP查询用法扩展-可视化设计工具

至此我们的产品管理增删改查已经完成

切换源码类型

低代码快速可视化完成商品增删改查代码生成器,大家根据自己喜欢可以切换选项式或组合式代码。

vue选项式代码

<template>
  <div class="container">
    <div class="el-card is-always-shadow diygw-col-24">
      <div class="el-card__body">
        <div class="mb8">
          <el-form class="flex flex-direction-row flex-wrap" :model="queryParams" :rules="queryParamsRules" ref="queryForm" label-width="80px">
            <div class="flex diygw-col-0">
              <el-form-item class="diygw-el-rate" prop="title_like" label="标题">
                <el-input type="text" placeholder="请输入标题查询" v-model="queryParams.title_like"> </el-input>
              </el-form-item>
            </div>
            <div class="flex diygw-col-0">
              <el-button type="primary" @click="handleQuery"> <SvgIcon name="ele-Search" /> 搜索 </el-button> <el-button @click="resetQuery"> <SvgIcon name="ele-Refresh" /> 重置 </el-button>
            </div>
          </el-form>
        </div>
        <div class="mb8">
          <el-button type="primary" plain @click="onOpenAddModule"> <SvgIcon name="ele-Plus" />新增 </el-button> <el-button type="danger" plain :disabled="multiple" @click="onTabelRowDel"> <SvgIcon name="ele-Delete" />删除 </el-button>
        </div>
        <el-table @selection-change="handleSelectionChange" v-loading="loading" :data="tableData" stripe border current-row-key="id" empty-text="没有数据" style="width: 100%">
          <el-table-column type="selection" width="55" align="center"></el-table-column>
          <el-table-column label="置顶" prop="title" align="center">
            <template #default="scope"> <el-tag style="cursor: pointer" v-if="scope.row.sortnum > 0" type="error" @click="updatenumApi(scope.row)"> 取消置顶 </el-tag><el-tag style="cursor: pointer" @click="ordernumApi(scope.row)" v-else> 置顶 </el-tag> </template>
          </el-table-column>
          <el-table-column label="商品图片" prop="img" align="center">
            <template #default="scope"> <diy-img style="width: 80px; height: 80px" :src="scope.row.img" fit="contain"></diy-img> </template>
          </el-table-column>
          <el-table-column label="商品名称" prop="title" align="center"> </el-table-column>
          <el-table-column label="商品价格" prop="price" align="center"> </el-table-column>
          <el-table-column label="总销量" prop="sellamonut" align="center"> </el-table-column>
          <el-table-column label="库存量" prop="amount" align="center"> </el-table-column>
          <el-table-column label="备注" prop="remark" align="center"> </el-table-column>
          <el-table-column label="数据状态" prop="status" align="center">
            <template #default="scope"> <el-tag v-if="scope.row.status == 1"> 上架 </el-tag><el-tag v-else> 下架 </el-tag> </template>
          </el-table-column>
          <el-table-column label="操作" align="center" fixed="right" width="180">
            <template #default="scope">
              <el-button type="primary" plain size="small" @click="onOpenEditModule(scope.row)"> <SvgIcon name="ele-Edit" />修改 </el-button> <el-button v-if="scope.row.id != 0" type="danger" plain size="small" @click="onTabelRowDel(scope.row)"> <SvgIcon name="ele-Delete" />删除 </el-button>
            </template>
          </el-table-column>
        </el-table>
        <!-- 分页设置-->
        <div v-show="total > 0"><el-divider></el-divider> <el-pagination background :total="total" :current-page="queryParams.pageNum" :page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange" /></div>
      </div>
      <!-- 添加或修改参数配置对话框 -->
      <el-dialog :fullscreen="isFullscreen" width="1000px" v-model="isShowDialog" destroy-on-close title="商品" draggable center>
        <template #header="{ close, titleId, titleClass }">
          <h4 :id="titleId" :class="titleClass">商品</h4>
          <el-tooltip v-if="!isFullscreen" content="最大化" placement="bottom">
            <el-button class="diygw-max-icon el-dialog__headerbtn">
              <el-icon @click="isFullscreen = !isFullscreen">
                <FullScreen />
              </el-icon>
            </el-button>
          </el-tooltip>
          <el-tooltip v-if="isFullscreen" content="返回" placement="bottom">
            <el-button class="diygw-max-icon el-dialog__headerbtn">
              <el-icon @click="isFullscreen = !isFullscreen">
                <Remove />
              </el-icon>
            </el-button>
          </el-tooltip>
        </template>
        <el-form class="flex flex-direction-row flex-wrap" :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="90px">
          <div class="flex diygw-col-24">
            <el-tabs v-model="tabsTab" tab-position="top" @tab-click="handleClick">
              <el-tab-pane label="基本信息" name="1">
                <div class="flex flex-direction-row flex-wrap">
                  <div class="flex diygw-col-24">
                    <el-form-item
                      :rules="[
                        {
                          required: true,
                          trigger: 'blur',
                          message: '商品名称不能为空'
                        }
                      ]"
                      class="diygw-el-rate"
                      prop="title"
                      label="商品名称"
                    >
                      <el-input type="text" placeholder="请输入商品名称" v-model="editForm.title"> </el-input>
                    </el-form-item>
                  </div>
                  <div class="flex diygw-col-24">
                    <el-form-item prop="cateId" class="diygw-el-rate" label="商品分类">
                      <el-select clear-icon="CircleClose" suffix-icon="ArrowUp" placeholder="请选择" v-model="editForm.cateId" style="width: 100% !important">
                        <el-option v-for="item in cates.rows" :key="item.id" :label="item.title" :value="item.id" />
                      </el-select>
                    </el-form-item>
                  </div>
                  <div class="flex diygw-col-24">
                    <el-form-item
                      :rules="[
                        {
                          required: true,
                          trigger: 'blur',
                          message: '商品主图不能为空'
                        }
                      ]"
                      prop="img"
                      class="diygw-el-rate"
                      label="商品主图"
                    >
                      <diy-uploadinput v-model="editForm.img" title="商品主图"></diy-uploadinput>
                    </el-form-item>
                  </div>
                  <div class="flex diygw-col-24">
                    <el-form-item prop="imgs" class="diygw-el-rate" label="商品轮播图">
                      <diy-photo v-model="editForm.imgs"></diy-photo>
                    </el-form-item>
                  </div>
                  <div class="flex diygw-col-24">
                    <el-form-item prop="status" class="diygw-el-rate" label="商品状态">
                      <el-radio-group v-model="editForm.status">
                        <el-radio v-for="item in editFormData.statusDatas" :key="item.value" :label="item.value">
                          {{ item.label }}
                        </el-radio>
                      </el-radio-group>
                    </el-form-item>
                  </div>
                  <div class="flex diygw-col-24">
                    <el-form-item prop="remark" class="diygw-el-rate" label="商品描述">
                      <el-input type="textarea" rows="3" placeholder="请输入描述" v-model="editForm.remark"> </el-input>
                    </el-form-item>
                  </div>
                </div>
              </el-tab-pane>
              <el-tab-pane label="规格库存" name="2">
                <div class="flex flex-direction-row flex-wrap">
                  <div class="flex diygw-col-24">
                    <el-form-item prop="skuType" class="diygw-el-rate" label="规格类型">
                      <el-radio-group v-model="editForm.skuType">
                        <el-radio v-for="item in editFormData.skuTypeDatas" :key="item.value" :label="item.value">
                          {{ item.label }}
                        </el-radio>
                      </el-radio-group>
                    </el-form-item>
                  </div>
                  <div v-if="editForm.skuType == 1" class="flex flex-wrap diygw-col-24 flex-direction-column">
                    <div class="flex diygw-col-24">
                      <el-form-item
                        :rules="[
                          {
                            type: 'number',
                            required: true,
                            trigger: 'blur',
                            message: '请输入数字'
                          }
                        ]"
                        prop="price"
                        class="diygw-el-rate"
                        label="商品价格"
                      >
                        <el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入商品价格" v-model="editForm.price"> </el-input-number>
                        <div class="diygw-help-text">商品的实际购买金额,最低0.01</div>
                      </el-form-item>
                    </div>
                    <div class="flex diygw-col-24">
                      <el-form-item
                        :rules="[
                          {
                            type: 'number',
                            required: true,
                            trigger: 'blur',
                            message: '请输入数字'
                          }
                        ]"
                        prop="linePrice"
                        class="diygw-el-rate"
                        label="切线价格"
                      >
                        <el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入切线价格" v-model="editForm.linePrice"> </el-input-number>
                        <div class="diygw-help-text">划线价仅用于商品页展示</div>
                      </el-form-item>
                    </div>
                    <div class="flex diygw-col-24">
                      <el-form-item
                        :rules="[
                          {
                            type: 'number',
                            required: true,
                            trigger: 'blur',
                            message: '请输入数字'
                          }
                        ]"
                        prop="amount"
                        class="diygw-el-rate"
                        label="库存数量"
                      >
                        <el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入库存数量" v-model="editForm.amount"> </el-input-number>
                        <div class="diygw-help-text">商品的实际库存数量,为0时用户无法下单</div>
                      </el-form-item>
                    </div>
                  </div>
                  <div v-else class="flex diygw-col-24">
                    <el-form-item prop="skus" label="多规格">
                      <diy-sku :columns="editFormData.skusColumns" v-model:skus="editForm.skus.skus" v-model:specs="editForm.skus.specs"></diy-sku>
                    </el-form-item>
                  </div>
                </div>
              </el-tab-pane>
              <el-tab-pane label="商品详情" name="3">
                <div class="flex flex-direction-row flex-wrap">
                  <div class="flex diygw-col-24">
                    <el-form-item
                      :rules="[
                        {
                          required: true,
                          trigger: 'blur',
                          message: '商品详情不能为空哟'
                        }
                      ]"
                      prop="content"
                      class="diygw-el-rate"
                      label="商品详情"
                    >
                      <diy-editor v-model="editForm.content"></diy-editor>
                    </el-form-item>
                  </div>
                </div>
              </el-tab-pane>
            </el-tabs>
          </div>
        </el-form>
        <template #footer>
          <div class="dialog-footer flex justify-center"><el-button @click="closeDialog"> 取 消 </el-button> <el-button type="primary" @click="onSubmit" :loading="saveloading"> 保 存 </el-button></div>
        </template>
      </el-dialog>
    </div>
    <div class="clearfix"></div>
  </div>
</template>
 
<script>
  import { useRouter, useRoute } from 'vue-router';
  import { storeToRefs } from 'pinia';
  import { useUserInfo } from '@/stores/userInfo';
 
  import { ElMessageBox, ElMessage } from 'element-plus';
  import { deepClone, changeRowToForm } from '@/utils/other';
  import { addData, updateData, delData, listData } from '@/api';
 
  import DiyUploadinput from '@/components/upload/uploadinput.vue';
  import DiyPhoto from '@/components/upload/photo.vue';
  import DiySku from '@/components/sku/index.vue';
  import DiyEditor from '@/components/editor/index.vue';
 
  export default {
    components: {
      DiyUploadinput,
      DiyPhoto,
      DiySku,
      DiyEditor
    },
    data() {
      return {
        globalOption: {},
        userInfo: {},
        ordernum: {
          code: 200,
          msg: '设置成功'
        },
        updatenum: {
          code: 200,
          msg: '修改成功',
          data: {
            id: '1',
            sortnum: '0'
          }
        },
        cates: {
          rows: [
            {
              id: 0,
              title: '',
              remark: null,
              img: '',
              sortnum: null,
              status: '',
              userId: 0,
              createTime: '',
              updateTime: '',
              deleteTime: null
            }
          ],
          total: 0,
          code: 0,
          msg: ''
        },
        // 遮罩层
        loading: true,
        // 选中数组
        ids: [],
        // 非单个禁用
        single: true,
        // 非多个禁用
        multiple: true,
        // 弹出层标题
        title: '',
        // 总条数
        total: 0,
        tableData: [],
        // 是否显示弹出层
        isFullscreen: false,
        isShowDialog: false,
        saveloading: false,
        tabsTab: '1',
        editFormInit: {},
        queryParamsInit: {},
 
        editFormData: {
          statusDatas: [
            { value: '1', label: '上架' },
            { value: '2', label: '下架' }
          ],
          skuTypeDatas: [
            { value: '1', label: '单规格' },
            { value: '2', label: '多规格' }
          ],
          skusColumns: [
            { title: '图片地址', id: 'thumb', type: 'img' },
            { title: '价格', id: 'price', type: 'number' },
            { title: '划线价格', id: 'linePrice', type: 'number' },
            { title: '库存', id: 'amount', type: 'number' },
            { title: '备注', id: 'sku', type: 'text' }
          ]
        },
 
        queryParams: {
          pageNum: 1,
          pageSize: 10,
          title_like: ''
        },
 
        queryParamsRules: {},
 
        editForm: {
          id: undefined,
          title: '',
          cateId: '',
          img: '',
          imgs: [],
          status: '1',
          remark: '',
          skuType: '1',
          price: '',
          linePrice: '',
          amount: '',
          skus: {
            skus: [],
            specs: []
          },
          content: ''
        },
 
        editFormRules: {}
      };
    },
    methods: {
      getParamData(e, row) {
        row = row || {};
        let dataset = e.currentTarget ? e.currentTarget.dataset : e;
        if (row) {
          dataset = Object.assign(dataset, row);
        }
        return dataset;
      },
      navigateTo(e, row) {
        let dataset = this.getParamData(e, row);
        let { type } = dataset;
        if (type == 'page' || type == 'inner' || type == 'href') {
          this.router.push({
            path: dataset.url,
            query: dataset
          });
        } else if (typeof this[type] == 'function') {
          this[type](dataset);
        } else {
          ElMessage.error('待实现点击事件');
        }
      },
      async init() {
        await this.catesApi();
      },
      // 置顶 API请求方法
      async ordernumApi(param) {
        param = param || {};
        param = this.getParamData(param);
        let http_url = '/shop/goods/sortnum';
        let http_data = {};
        let http_header = {};
 
        http_data.id = param.id;
        let flag = await this.confirmFunction({ title: '确定置顶吗' });
        if (!flag) {
          ElMessage.error('你已取消');
          return;
        }
        let ordernum = await this.$http.post(http_url, http_data, http_header, 'json');
        this.ordernum = ordernum;
        this.resetQuery();
      },
      // 设置为0 API请求方法
      async updatenumApi(param) {
        param = param || {};
        param = this.getParamData(param);
        let http_url = '/shop/goods/update';
        let http_data = {};
        let http_header = {};
 
        http_data.id = param.id;
        http_data.sortnum = 0;
        let flag = await this.confirmFunction({ title: '确定取消置顶吗' });
        if (!flag) {
          ElMessage.error('你已取消');
          return;
        }
        let updatenum = await this.$http.post(http_url, http_data, http_header, 'json');
        this.updatenum = updatenum;
        this.resetQuery();
      },
      // 分类 API请求方法
      async catesApi(param) {
        param = param || {};
        param = this.getParamData(param);
        let http_url = '/shop/cate/all';
        let http_data = {};
        let http_header = {};
 
        let cates = await this.$http.post(http_url, http_data, http_header, 'json');
        this.cates = cates;
      },
 
      // confirm 自定义方法
      async confirmFunction(param) {
        param = param || {};
        let thiz = this;
        let title = param && (param.title || param.title == 0) ? param.title : thiz.title || '';
        return new Promise((resolve) => {
          ElMessageBox({
            message: title,
            title: '警告',
            showCancelButton: true,
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          })
            .then(() => {
              resolve(true);
            })
            .catch(() => {
              resolve(false);
            });
        });
      },
      // 打开弹窗
      openDialog(row) {
        if (row.id && row.id != undefined && row.id != 0) {
          this.editForm = changeRowToForm(row, this.editForm);
        } else {
          this.initForm();
        }
        this.isShowDialog = true;
        this.saveloading = false;
      },
 
      // 关闭弹窗
      closeDialog(row) {
        this.mittBus.emit('onEditAdmintableModule', row);
        this.isShowDialog = false;
      },
 
      // 保存
      onSubmit() {
        const formWrap = this.$refs.editFormRef;
        if (!formWrap) return;
        formWrap.validate((valid, fields) => {
          if (valid) {
            this.saveloading = true;
            if (this.editForm.skuType == 2 && this.editForm.skus && this.editForm.skus.skus.length == 0) {
              ElMessage.error('请设置多规格');
              return;
            }
            if (this.editForm.id != undefined && this.editForm.id != 0) {
              updateData('/shop/goods', this.editForm)
                .then(() => {
                  ElMessage.success('修改成功');
                  this.saveloading = false;
                  this.closeDialog(this.editForm); // 关闭弹窗
                })
                .finally(() => {
                  this.saveloading = false;
                });
            } else {
              addData('/shop/goods', this.editForm)
                .then(() => {
                  ElMessage.success('新增成功');
                  this.saveloading = false;
                  this.closeDialog(this.editForm); // 关闭弹窗
                })
                .finally(() => {
                  this.saveloading = false;
                });
            }
          } else {
            let message = '';
            if (fields && Object.keys(fields).length > 0) {
              let field = fields[Object.keys(fields)[0]];
              let messages = field.map((item) => {
                return item.message;
              });
              message = messages.join(',');
            }
            ElMessage.error('验证未通过!' + message);
          }
        });
      },
      // 表单初始化,方法:`resetFields()` 无法使用
      initForm() {
        this.editForm = deepClone(this.editFormInit);
      },
      /** 查询商品列表 */
      handleQuery() {
        this.loading = true;
        listData('/shop/goods', this.queryParams).then((response) => {
          this.tableData = response.rows;
          this.total = response.total;
          this.loading = false;
        });
      },
      /** 重置按钮操作 */
      resetQuery() {
        this.queryParams = deepClone(this.queryParamsInit);
        this.handleQuery();
      },
 
      // 打开新增商品弹窗
      onOpenAddModule(row) {
        row = [];
        this.title = '添加商品';
        this.openDialog(row);
      },
      // 打开编辑商品弹窗
      onOpenEditModule(row) {
        this.title = '修改商品';
        this.initForm();
        this.openDialog(row);
      },
      /** 删除按钮操作 */
      onTabelRowDel(row) {
        let thiz = this;
        const id = row.id || this.ids;
        ElMessageBox({
          message: '是否确认删除选中的商品?',
          title: '警告',
          showCancelButton: true,
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(function () {
          return delData('/shop/goods', id).then(() => {
            thiz.handleQuery();
            ElMessage.success('删除成功');
          });
        });
      },
      // 多选框选中数据
      handleSelectionChange(selection) {
        this.ids = selection.map((item) => item.id);
        this.single = selection.length != 1;
        this.multiple = !selection.length;
      },
 
      //分页页面大小发生变化
      handleSizeChange(val) {
        this.queryParams.pageSize = val;
        this.handleQuery();
      },
      //当前页码发生变化
      handleCurrentChange(val) {
        this.queryParams.pageNum = val;
        this.handleQuery();
      }
    },
    async mounted() {
      this.router = useRouter();
      this.globalOption = useRoute().query;
      const stores = useUserInfo();
      const { userInfos } = storeToRefs(stores);
      this.userInfo = userInfos;
      await this.init();
      this.editFormInit = deepClone(this.editForm);
      this.queryParamsInit = deepClone(this.queryParams);
      this.handleQuery();
      this.mittBus.on('onEditAdmintableModule', () => {
        this.handleQuery();
      });
    },
    beforeUnmount() {
      this.mittBus.off('onEditAdmintableModule');
    }
  };
</script>
 
<style lang="scss" scoped>
  .container {
    font-size: 12px;
  }
</style>


vue组合式代码

<template>
  <div class="container">
    <div class="el-card is-always-shadow diygw-col-24">
      <div class="el-card__body">
        <div class="mb8">
          <el-form class="flex flex-direction-row flex-wrap" :model="state.queryParams" :rules="queryParamsRules" ref="queryForm" label-width="80px">
            <div class="flex diygw-col-0">
              <el-form-item class="diygw-el-rate" prop="title_like" label="标题">
                <el-input type="text" placeholder="请输入标题查询" v-model="queryParams.title_like"> </el-input>
              </el-form-item>
            </div>
            <div class="flex diygw-col-0">
              <el-button type="primary" @click="handleQuery"> <SvgIcon name="ele-Search" /> 搜索 </el-button> <el-button @click="resetQuery"> <SvgIcon name="ele-Refresh" /> 重置 </el-button>
            </div>
          </el-form>
        </div>
        <div class="mb8">
          <el-button type="primary" plain @click="onOpenAddModule"> <SvgIcon name="ele-Plus" />新增 </el-button> <el-button type="danger" plain :disabled="state.multiple" @click="onTabelRowDel"> <SvgIcon name="ele-Delete" />删除 </el-button>
        </div>
        <el-table @selection-change="handleSelectionChange" v-loading="state.loading" :data="state.tableData" stripe border current-row-key="id" empty-text="没有数据" style="width: 100%">
          <el-table-column type="selection" width="55" align="center"></el-table-column>
          <el-table-column label="置顶" prop="title" align="center">
            <template #default="scope"> <el-tag style="cursor: pointer" v-if="scope.row.sortnum > 0" type="error" @click="updatenumApi(scope.row)"> 取消置顶 </el-tag><el-tag style="cursor: pointer" @click="ordernumApi(scope.row)" v-else> 置顶 </el-tag> </template>
          </el-table-column>
          <el-table-column label="商品图片" prop="img" align="center">
            <template #default="scope"> <diy-img style="width: 80px; height: 80px" :src="scope.row.img" fit="contain"></diy-img> </template>
          </el-table-column>
          <el-table-column label="商品名称" prop="title" align="center"> </el-table-column>
          <el-table-column label="商品价格" prop="price" align="center"> </el-table-column>
          <el-table-column label="总销量" prop="sellamonut" align="center"> </el-table-column>
          <el-table-column label="库存量" prop="amount" align="center"> </el-table-column>
          <el-table-column label="备注" prop="remark" align="center"> </el-table-column>
          <el-table-column label="数据状态" prop="status" align="center">
            <template #default="scope"> <el-tag v-if="scope.row.status == 1"> 上架 </el-tag><el-tag v-else> 下架 </el-tag> </template>
          </el-table-column>
          <el-table-column label="操作" align="center" fixed="right" width="180">
            <template #default="scope">
              <el-button type="primary" plain size="small" @click="onOpenEditModule(scope.row)"> <SvgIcon name="ele-Edit" />修改 </el-button> <el-button v-if="scope.row.id != 0" type="danger" plain size="small" @click="onTabelRowDel(scope.row)"> <SvgIcon name="ele-Delete" />删除 </el-button>
            </template>
          </el-table-column>
        </el-table>
        <!-- 分页设置-->
        <div v-show="state.total > 0"><el-divider></el-divider> <el-pagination background :total="state.total" :current-page="state.queryParams.pageNum" :page-size="state.queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange" /></div>
      </div>
      <!-- 添加或修改参数配置对话框 -->
      <el-dialog :fullscreen="isFullscreen" width="1000px" v-model="isShowDialog" destroy-on-close title="商品" draggable center>
        <template #header="{ close, titleId, titleClass }">
          <h4 :id="titleId" :class="titleClass">商品</h4>
          <el-tooltip v-if="!isFullscreen" content="最大化" placement="bottom">
            <el-button class="diygw-max-icon el-dialog__headerbtn">
              <el-icon @click="isFullscreen = !isFullscreen">
                <FullScreen />
              </el-icon>
            </el-button>
          </el-tooltip>
          <el-tooltip v-if="isFullscreen" content="返回" placement="bottom">
            <el-button class="diygw-max-icon el-dialog__headerbtn">
              <el-icon @click="isFullscreen = !isFullscreen">
                <Remove />
              </el-icon>
            </el-button>
          </el-tooltip>
        </template>
        <el-form class="flex flex-direction-row flex-wrap" :model="state.editForm" :rules="state.editFormRules" ref="editFormRef" label-width="90px">
          <div class="flex diygw-col-24">
            <el-tabs v-model="tabsTab" tab-position="top" @tab-click="handleClick">
              <el-tab-pane label="基本信息" name="1">
                <div class="flex flex-direction-row flex-wrap">
                  <div class="flex diygw-col-24">
                    <el-form-item
                      :rules="[
                        {
                          required: true,
                          trigger: 'blur',
                          message: '商品名称不能为空'
                        }
                      ]"
                      class="diygw-el-rate"
                      prop="title"
                      label="商品名称"
                    >
                      <el-input type="text" placeholder="请输入商品名称" v-model="editForm.title"> </el-input>
                    </el-form-item>
                  </div>
                  <div class="flex diygw-col-24">
                    <el-form-item prop="cateId" class="diygw-el-rate" label="商品分类">
                      <el-select clear-icon="CircleClose" suffix-icon="ArrowUp" placeholder="请选择" v-model="editForm.cateId" style="width: 100% !important">
                        <el-option v-for="item in cates.rows" :key="item.id" :label="item.title" :value="item.id" />
                      </el-select>
                    </el-form-item>
                  </div>
                  <div class="flex diygw-col-24">
                    <el-form-item
                      :rules="[
                        {
                          required: true,
                          trigger: 'blur',
                          message: '商品主图不能为空'
                        }
                      ]"
                      prop="img"
                      class="diygw-el-rate"
                      label="商品主图"
                    >
                      <diy-uploadinput v-model="editForm.img" title="商品主图"></diy-uploadinput>
                    </el-form-item>
                  </div>
                  <div class="flex diygw-col-24">
                    <el-form-item prop="imgs" class="diygw-el-rate" label="商品轮播图">
                      <diy-photo v-model="editForm.imgs"></diy-photo>
                    </el-form-item>
                  </div>
                  <div class="flex diygw-col-24">
                    <el-form-item prop="status" class="diygw-el-rate" label="商品状态">
                      <el-radio-group v-model="editForm.status">
                        <el-radio v-for="item in editFormData.statusDatas" :key="item.value" :label="item.value">
                          {{ item.label }}
                        </el-radio>
                      </el-radio-group>
                    </el-form-item>
                  </div>
                  <div class="flex diygw-col-24">
                    <el-form-item prop="remark" class="diygw-el-rate" label="商品描述">
                      <el-input type="textarea" rows="3" placeholder="请输入描述" v-model="editForm.remark"> </el-input>
                    </el-form-item>
                  </div>
                </div>
              </el-tab-pane>
              <el-tab-pane label="规格库存" name="2">
                <div class="flex flex-direction-row flex-wrap">
                  <div class="flex diygw-col-24">
                    <el-form-item prop="skuType" class="diygw-el-rate" label="规格类型">
                      <el-radio-group v-model="editForm.skuType">
                        <el-radio v-for="item in editFormData.skuTypeDatas" :key="item.value" :label="item.value">
                          {{ item.label }}
                        </el-radio>
                      </el-radio-group>
                    </el-form-item>
                  </div>
                  <div v-if="editForm.skuType == 1" class="flex flex-wrap diygw-col-24 flex-direction-column">
                    <div class="flex diygw-col-24">
                      <el-form-item
                        :rules="[
                          {
                            type: 'number',
                            required: true,
                            trigger: 'blur',
                            message: '请输入数字'
                          }
                        ]"
                        prop="price"
                        class="diygw-el-rate"
                        label="商品价格"
                      >
                        <el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入商品价格" v-model="editForm.price"> </el-input-number>
                        <div class="diygw-help-text">商品的实际购买金额,最低0.01</div>
                      </el-form-item>
                    </div>
                    <div class="flex diygw-col-24">
                      <el-form-item
                        :rules="[
                          {
                            type: 'number',
                            required: true,
                            trigger: 'blur',
                            message: '请输入数字'
                          }
                        ]"
                        prop="linePrice"
                        class="diygw-el-rate"
                        label="切线价格"
                      >
                        <el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入切线价格" v-model="editForm.linePrice"> </el-input-number>
                        <div class="diygw-help-text">划线价仅用于商品页展示</div>
                      </el-form-item>
                    </div>
                    <div class="flex diygw-col-24">
                      <el-form-item
                        :rules="[
                          {
                            type: 'number',
                            required: true,
                            trigger: 'blur',
                            message: '请输入数字'
                          }
                        ]"
                        prop="amount"
                        class="diygw-el-rate"
                        label="库存数量"
                      >
                        <el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入库存数量" v-model="editForm.amount"> </el-input-number>
                        <div class="diygw-help-text">商品的实际库存数量,为0时用户无法下单</div>
                      </el-form-item>
                    </div>
                  </div>
                  <div v-else class="flex diygw-col-24">
                    <el-form-item prop="skus" label="多规格">
                      <diy-sku :columns="editFormData.skusColumns" v-model:skus="editForm.skus.skus" v-model:specs="editForm.skus.specs"></diy-sku>
                    </el-form-item>
                  </div>
                </div>
              </el-tab-pane>
              <el-tab-pane label="商品详情" name="3">
                <div class="flex flex-direction-row flex-wrap">
                  <div class="flex diygw-col-24">
                    <el-form-item
                      :rules="[
                        {
                          required: true,
                          trigger: 'blur',
                          message: '商品详情不能为空哟'
                        }
                      ]"
                      prop="content"
                      class="diygw-el-rate"
                      label="商品详情"
                    >
                      <diy-editor v-model="editForm.content"></diy-editor>
                    </el-form-item>
                  </div>
                </div>
              </el-tab-pane>
            </el-tabs>
          </div>
        </el-form>
        <template #footer>
          <div class="dialog-footer flex justify-center"><el-button @click="closeDialog"> 取 消 </el-button> <el-button type="primary" @click="onSubmit" :loading="state.saveloading"> 保 存 </el-button></div>
        </template>
      </el-dialog>
    </div>
    <div class="clearfix"></div>
  </div>
</template>
 
<script setup name="goodsgoods">
  import { ElMessageBox, ElMessage } from 'element-plus';
  import { ref, toRefs, reactive, onMounted, getCurrentInstance, onUnmounted, unref } from 'vue';
  import { deepClone, changeRowToForm } from '@/utils/other';
  import { addData, updateData, delData, listData } from '@/api';
 
  import { useRouter, useRoute } from 'vue-router';
  import { storeToRefs } from 'pinia';
  import { useUserInfo } from '@/stores/userInfo';
 
  import DiyUploadinput from '@/components/upload/uploadinput.vue';
  import DiyPhoto from '@/components/upload/photo.vue';
  import DiySku from '@/components/sku/index.vue';
  import DiyEditor from '@/components/editor/index.vue';
 
  const stores = useUserInfo();
  const { userInfos } = storeToRefs(stores);
  const { proxy } = getCurrentInstance();
  const router = useRouter();
  const route = useRoute();
  const globalOption = ref(route.query);
  const getParamData = (e, row) => {
    row = row || {};
    let dataset = e.currentTarget ? e.currentTarget.dataset : e;
    if (row) {
      dataset = Object.assign(dataset, row);
    }
    return dataset;
  };
 
  const navigateTo = (e, row) => {
    let dataset = getParamData(e, row);
    let { type } = dataset;
    if ((type == 'page' || type == 'inner' || type == 'href') && dataset.url) {
      router.push({
        path: (dataset.url.startsWith('/') ? '' : '/') + dataset.url,
        query: dataset
      });
    } else {
      ElMessage.error('待实现点击事件');
    }
  };
 
  const state = reactive({
    userInfo: userInfos.value,
    ordernum: {
      code: 200,
      msg: '设置成功'
    },
    updatenum: {
      code: 200,
      msg: '修改成功',
      data: {
        id: '1',
        sortnum: '0'
      }
    },
    cates: {
      rows: [
        {
          id: 0,
          title: '',
          remark: null,
          img: '',
          sortnum: null,
          status: '',
          userId: 0,
          createTime: '',
          updateTime: '',
          deleteTime: null
        }
      ],
      total: 0,
      code: 0,
      msg: ''
    },
    // 遮罩层
    loading: true,
    // 选中数组
    ids: [],
    // 非单个禁用
    single: true,
    // 非多个禁用
    multiple: true,
    // 弹出层标题
    title: '',
    // 总条数
    total: 0,
    tableData: [],
    // 是否显示弹出层
    isFullscreen: false,
    isShowDialog: false,
    saveloading: false,
    tabsTab: '1',
 
    editFormData: {
      statusDatas: [
        { value: '1', label: '上架' },
        { value: '2', label: '下架' }
      ],
      skuTypeDatas: [
        { value: '1', label: '单规格' },
        { value: '2', label: '多规格' }
      ],
      skusColumns: [
        { title: '图片地址', id: 'thumb', type: 'img' },
        { title: '价格', id: 'price', type: 'number' },
        { title: '划线价格', id: 'linePrice', type: 'number' },
        { title: '库存', id: 'amount', type: 'number' },
        { title: '备注', id: 'sku', type: 'text' }
      ]
    },
 
    queryParams: {
      pageNum: 1,
      pageSize: 10,
      title_like: ''
    },
 
    queryParamsRules: {},
 
    editForm: {
      id: undefined,
      title: '',
      cateId: '',
      img: '',
      imgs: [],
      status: '1',
      remark: '',
      skuType: '1',
      price: '',
      linePrice: '',
      amount: '',
      skus: {
        skus: [],
        specs: []
      },
      content: ''
    },
 
    editFormRules: {}
  });
  const { userInfo, cates, tabsTab, editFormData, queryParams, multiple, ordernum, tableData, updatenum, loading, title, single, total, isShowDialog, editForm, ids, saveloading, isFullscreen } = toRefs(state);
  // 置顶 API请求方法
  const ordernumApi = async (param) => {
    param = param || {};
    param = getParamData(param);
    let http_url = '/shop/goods/sortnum';
    let http_data = {};
 
    let http_header = {};
 
    http_data.id = param.id;
    let flag = await confirmFunction({ title: '确定置顶吗' });
    if (!flag) {
      ElMessage.error('你已取消');
      return;
    }
    let ordernum = await proxy.$http.post(http_url, http_data, http_header, 'json');
 
    state.ordernum = ordernum;
    state.resetQuery();
  };
  // 设置为0 API请求方法
  const updatenumApi = async (param) => {
    param = param || {};
    param = getParamData(param);
    let http_url = '/shop/goods/update';
    let http_data = {};
 
    let http_header = {};
 
    http_data.id = param.id;
    http_data.sortnum = 0;
    let flag = await confirmFunction({ title: '确定取消置顶吗' });
    if (!flag) {
      ElMessage.error('你已取消');
      return;
    }
    let updatenum = await proxy.$http.post(http_url, http_data, http_header, 'json');
 
    state.updatenum = updatenum;
    state.resetQuery();
  };
  // 分类 API请求方法
  const catesApi = async (param) => {
    param = param || {};
    param = getParamData(param);
    let http_url = '/shop/cate/all';
    let http_data = {};
 
    let http_header = {};
 
    let cates = await proxy.$http.post(http_url, http_data, http_header, 'json');
 
    state.cates = cates;
  };
 
  //confirm 自定义方法
  const confirmFunction = async (param) => {
    let title = param && (param.title || param.title == 0) ? param.title : state.title || '';
    return new Promise((resolve) => {
      ElMessageBox({
        message: title,
        title: '警告',
        showCancelButton: true,
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
        .then(() => {
          resolve(true);
        })
        .catch(() => {
          resolve(false);
        });
    });
  };
  const editFormRef = ref(null);
 
  const editFormInit = deepClone(state.editForm);
 
  // 打开弹窗
  const openDialog = (row) => {
    if (row.id && row.id != undefined && row.id != 0) {
      state.editForm = changeRowToForm(row, state.editForm);
    } else {
      initForm();
    }
    state.isShowDialog = true;
    state.saveloading = false;
  };
 
  // 关闭弹窗
  const closeDialog = (row) => {
    proxy.mittBus.emit('onEditAdmintableModule', row);
    state.isShowDialog = false;
  };
 
  // 保存
  const onSubmit = () => {
    const formWrap = unref(editFormRef);
    if (!formWrap) return;
    formWrap.validate((valid, fields) => {
      if (valid) {
        state.saveloading = true;
        if (editForm.skuType == 2 && editForm.skus && editForm.skus.skus.length == 0) {
          ElMessage.error('请设置多规格');
          return;
        }
        if (state.editForm.id != undefined && state.editForm.id != 0) {
          updateData('/shop/goods', state.editForm)
            .then(() => {
              ElMessage.success('修改成功');
              state.saveloading = false;
              closeDialog(state.editForm); // 关闭弹窗
            })
            .finally(() => {
              state.saveloading = false;
            });
        } else {
          addData('/shop/goods', state.editForm)
            .then(() => {
              ElMessage.success('新增成功');
              state.saveloading = false;
              closeDialog(state.editForm); // 关闭弹窗
            })
            .finally(() => {
              state.saveloading = false;
            });
        }
      } else {
        let message = '';
        if (fields && Object.keys(fields).length > 0) {
          let field = fields[Object.keys(fields)[0]];
          let messages = field.map((item) => {
            return item.message;
          });
          message = messages.join(',');
        }
        ElMessage.error('验证未通过!' + message);
      }
    });
  };
  // 表单初始化,方法:`resetFields()` 无法使用
  const initForm = () => {
    state.editForm = deepClone(editFormInit);
  };
  const queryParamsInit = deepClone(state.queryParams);
  /** 查询商品列表 */
  const handleQuery = () => {
    state.loading = true;
    listData('/shop/goods', state.queryParams).then((response) => {
      state.tableData = response.rows;
      state.total = response.total;
      state.loading = false;
    });
  };
  /** 重置按钮操作 */
  const resetQuery = () => {
    state.queryParams = deepClone(queryParamsInit);
    handleQuery();
  };
 
  // 打开新增商品弹窗
  const onOpenAddModule = (row) => {
    row = [];
    state.title = '添加商品';
    initForm();
    openDialog(row);
  };
  // 打开编辑商品弹窗
  const onOpenEditModule = (row) => {
    state.title = '修改商品';
    openDialog(row);
  };
  /** 删除按钮操作 */
  const onTabelRowDel = (row) => {
    const id = row.id || state.ids;
 
    ElMessageBox({
      message: '是否确认删除选中的商品?',
      title: '警告',
      showCancelButton: true,
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }).then(function () {
      return delData('/shop/goods', id).then(() => {
        handleQuery();
        ElMessage.success('删除成功');
      });
    });
  };
  // 多选框选中数据
  const handleSelectionChange = (selection) => {
    state.ids = selection.map((item) => item.id);
    state.single = selection.length != 1;
    state.multiple = !selection.length;
  };
 
  //分页页面大小发生变化
  const handleSizeChange = (val) => {
    state.queryParams.pageSize = val;
    handleQuery();
  };
  //当前页码发生变化
  const handleCurrentChange = (val) => {
    state.queryParams.pageNum = val;
    handleQuery();
  };
  const init = async () => {
    await catesApi();
  };
  // 页面加载时
  onMounted(async () => {
    await init();
    handleQuery();
    proxy.mittBus.on('onEditAdmintableModule', () => {
      handleQuery();
    });
  });
 
  // 页面卸载时
  onUnmounted(() => {
    proxy.mittBus.off('onEditAdmintableModule');
  });
</script>
 
<style lang="scss" scoped>
  .container {
    font-size: 12px;
  }
</style>


扩展阅读

产品录入:


管理员可以通过产品管理模块录入新的产品信息,包括商品名称、价格、描述、分类、品牌、图片等。

录入时需要进行信息的校验和合法性验证,确保数据的准确性和完整性。

产品编辑:


管理员可以对已有产品的信息进行编辑和更新,如修改商品名称、价格、描述等。

编辑界面应友好且操作便捷,以便管理员轻松完成更新。

产品上架与下架:


管理员可以灵活地将产品上架或下架。

上架后,用户可以在商城中浏览和购买该产品;下架后,用户将无法再找到该产品进行购买。

上架和下架操作应提供明确的提示和确认机制,避免误操作。

库存管理:


产品管理模块应提供库存管理功能,管理员可以查看和管理各个产品的库存情况。

包括库存数量的增加、减少、库存警戒值的设置等。

当库存数量低于警戒值时,系统应自动提醒管理员进行补货或下架操作。

目录
相关文章
|
2天前
|
编解码 Java 程序员
写代码还有专业的编程显示器?
写代码已经十个年头了, 一直都是习惯直接用一台Mac电脑写代码 偶尔接一个显示器, 但是可能因为公司配的显示器不怎么样, 还要接转接头 搞得桌面杂乱无章,分辨率也低,感觉屏幕还是Mac自带的看着舒服
|
4天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1540 5
|
1月前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
7天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
581 22
|
4天前
|
存储 SQL 关系型数据库
彻底搞懂InnoDB的MVCC多版本并发控制
本文详细介绍了InnoDB存储引擎中的两种并发控制方法:MVCC(多版本并发控制)和LBCC(基于锁的并发控制)。MVCC通过记录版本信息和使用快照读取机制,实现了高并发下的读写操作,而LBCC则通过加锁机制控制并发访问。文章深入探讨了MVCC的工作原理,包括插入、删除、修改流程及查询过程中的快照读取机制。通过多个案例演示了不同隔离级别下MVCC的具体表现,并解释了事务ID的分配和管理方式。最后,对比了四种隔离级别的性能特点,帮助读者理解如何根据具体需求选择合适的隔离级别以优化数据库性能。
201 3
|
10天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
11天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
580 5
|
23天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
7天前
|
XML 安全 Java
【Maven】依赖管理,Maven仓库,Maven核心功能
【Maven】依赖管理,Maven仓库,Maven核心功能
233 3
|
9天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
327 2