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

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

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

新增数据库表

拷贝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>


扩展阅读

产品录入:


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

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

产品编辑:


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

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

产品上架与下架:


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

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

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

库存管理:


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

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

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

目录
相关文章
|
SQL 供应链 JavaScript
订单管理系统(OMS)搭建实战 - 低代码拖拽定制订单管理系统
订单管理系统是很多公司,特别是电商公司最常用的内部系统之一。订单管理系统的使用者通常是仓管或者运营人员,它常被用于管理用户订单,比如添加或者修改一条发货记录,与快递 API 集成以便自动更新订单号等场景。
1013 0
【最佳实践】如何用宜搭做商品进销存
宜搭支持通过直接配置实现进销存场景。支持的常用进销存场景有: 图书管理系统、会议室预定系统、积分管理系统等。现在,就以商品进销存为例,示意操作过程。
【最佳实践】如何用宜搭做商品进销存
|
数据库 存储 关系型数据库
|
4月前
|
搜索推荐 安全 数据安全/隐私保护
构建高效网站后台会员管理系统:实战指南与代码示例
【7月更文挑战第5天】在当今的互联网时代,几乎每个网站或应用程序都需要一个强大的会员管理系统来维护用户信息、权限控制以及个性化体验。一个设计良好的会员管理系统不仅能够提升用户体验,还能增强数据安全性和运营效率。本文将深入探讨如何从零开始构建一个网站后台会员管理系统,涵盖系统设计思路、关键技术选型、功能模块实现,以及实战代码示例。
499 3
|
1月前
|
数据可视化 API
低代码可视化-uniapp购物车页面-代码生成器
低代码可视化-uniapp购物车页面-代码生成器
33 1
|
1月前
|
数据可视化 数据挖掘 API
低代码可视化-PC商城管理系统-产品分类--代码生成器
低代码可视化-PC商城管理系统-产品分类--代码生成器
21 0
|
1月前
|
小程序 数据可视化 API
低代码可视化-uniapp商城首页小程序-代码生成器
低代码可视化-uniapp商城首页小程序-代码生成器
29 0
|
1月前
|
缓存 数据可视化 小程序
低代码可视化-商品详情页面-代码生成器
低代码可视化-商品详情页面-代码生成器
24 0
|
6月前
|
JavaScript 前端开发
基于Jeecgboot前后端分离的ERP系统开发系列--出库单(2)
基于Jeecgboot前后端分离的ERP系统开发系列--出库单(2)
76 1
|
6月前
|
BI 数据库
基于Jeecgboot前后端分离的ERP系统开发系列--出库单(1)
基于Jeecgboot前后端分离的ERP系统开发系列--出库单(1)
81 1
下一篇
无影云桌面