我们将数据做一个显示,回显,我们将规格参数和我们的商品列表做一个关联。
有类目规格配置参数 将数据存储,在页面做回显
监听的时候去读取编辑的数据
商品添加-规格参数回显 实现代码如下
1, src/views/Goods/GoodsList/GoodsDialog.vue <template> <div> <!-- title="添加商品" 弹框的标题 :visible.sync="dialogVisible" 控制弹框的显示与隐藏 boolean true 表示显示 width="70%" 宽度 大小 --> <el-dialog :title="title" :visible.sync="dialogVisible" width="70%" :before-close="clearForm"> <!-- 中间弹框内容区域 添加(修改)商品表单数据--> <el-form :model="goodsForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"> <el-form-item label="类目选择" prop="category"> <el-button type="primary" @click="innerVisible=true">类目选择</el-button> <span style="margin-left: 10px;">{{goodsForm.category}}</span> </el-form-item> <el-form-item label="商品名称" prop="title"> <el-input v-model="goodsForm.title"></el-input> </el-form-item> <el-form-item label="商品价格" prop="price"> <el-input v-model="goodsForm.price"></el-input> </el-form-item> <el-form-item label="商品数量" prop="num"> <el-input v-model="goodsForm.num"></el-input> </el-form-item> <el-form-item label="发布时间" required> <el-col :span="11"> <el-form-item prop="date1"> <el-date-picker type="date" placeholder="选择日期" v-model="goodsForm.date1" style="width: 100%;"></el-date-picker> </el-form-item> </el-col> <el-col class="line" :span="2">-</el-col> <el-col :span="11"> <el-form-item prop="date2"> <el-time-picker placeholder="选择时间" v-model="goodsForm.date2" style="width: 100%;"></el-time-picker> </el-form-item> </el-col> </el-form-item> <el-form-item label="商品卖点" prop="sellPoint"> <el-input v-model="goodsForm.sellPoint"></el-input> </el-form-item> <el-form-item label="商品图片" prop="image"> <el-button type="primary" @click="innerVisibleImg=true">上传图片</el-button> <img :src="goodsForm.image" height="200px" style="margin-left: 10px;" alt="" /> </el-form-item> <el-form-item label="商品描述" prop="descs"> <!-- 父组件接收 sendEditor 数据 --> <WangEditor ref="myEditor" @sendEditor="sendEditor" /> </el-form-item> <!-- 规格参数配置 --> <el-form-item label="规格参数配置" v-show="isShow"> <!-- 表单里面套表单 start--> <el-form ref="dynamicValidateForm" label-width="100px" class="demo-dynamic"> <el-form-item v-for="(item, index) in groups" :label="item.title" :key="index" :prop="item.value"> <div class="item"> <el-input v-model="item.value"></el-input> </div> <!-- 内层的表单项 --> <el-form-item v-for="(ele, i) in item.children" :label="ele.title" :key="i" :prop="ele.value"> <div class="item"> <el-input v-model="ele.value"></el-input> </div> </el-form-item> </el-form-item> </el-form> <!-- 表单里面套表单 end--> </el-form-item> <el-form-item label="活动区域" prop="region"> <el-select v-model="goodsForm.region" placeholder="请选择活动区域"> <el-option label="区域一" value="shanghai"></el-option> <el-option label="区域二" value="beijing"></el-option> </el-select> </el-form-item> <el-form-item label="即时配送" prop="delivery"> <el-switch v-model="goodsForm.delivery"></el-switch> </el-form-item> <el-form-item label="活动性质" prop="type"> <el-checkbox-group v-model="goodsForm.type"> <el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox> <el-checkbox label="地推活动" name="type"></el-checkbox> <el-checkbox label="线下主题活动" name="type"></el-checkbox> <el-checkbox label="单纯品牌曝光" name="type"></el-checkbox> </el-checkbox-group> </el-form-item> <el-form-item label="特殊资源" prop="resource"> <el-radio-group v-model="goodsForm.resource"> <el-radio label="线上品牌商赞助"></el-radio> <el-radio label="线下场地免费"></el-radio> </el-radio-group> </el-form-item> <el-form-item label="活动形式" prop="desc"> <el-input type="textarea" v-model="goodsForm.desc"></el-input> </el-form-item> <!-- <el-form-item> <el-button type="primary" @click="submitForm('goodsForm')">确定</el-button> <el-button @click="resetForm('goodsForm')">重置</el-button> </el-form-item> --> </el-form> <!-- 弹框的底部区域 --> <span slot="footer" class="dialog-footer"> <!-- ref --> <el-button @click="clearForm">取消</el-button> <el-button type="primary" @click="submitForm">确 定</el-button> <!-- 父传子 --> <!-- <el-button @click="close">取消</el-button> <el-button type="primary" @click="close">确 定</el-button> --> </span> <!-- 1, 内弹框 --类目选择--> <el-dialog width="40%" title="类目选择" :visible.sync="innerVisible" append-to-body> <!-- 父组件接收 sendTreeData 数据 --> <TreeGoods @sendTreeData="sendTreeData" /> <!-- 内弹框的底部区域 --> <span slot="footer" class="dialog-footer"> <!-- ref --> <!-- <el-button @click="innerVisible = false">取 消</el-button> <el-button type="primary" @click="innerVisible = false">确 定</el-button> --> <!-- 父传子 --> <el-button @click="close">取消</el-button> <el-button type="primary" @click="showTreeData">确 定</el-button> </span> </el-dialog> <!-- 2, 内弹框 --上传图片--> <el-dialog width="40%" title="上传图片" :visible.sync="innerVisibleImg" append-to-body> <!-- 父组件接收 sendImg 数据 --> <UploadImg @sendImg="sendImg" /> <!-- 内弹框的底部区域 --> <span slot="footer" class="dialog-footer"> <!-- ref --> <el-button @click="innerVisibleImg = false">取 消</el-button> <el-button type="primary" @click="showImg">确 定</el-button> <!-- <el-button type="primary" @click="innerVisibleImg = false">确 定</el-button> --> <!-- 父传子 --> <!-- <el-button @click="close">取消</el-button> <el-button type="primary" @click="showTreeData">确 定</el-button> --> </span> </el-dialog> </el-dialog> </div> </template> <script> import TreeGoods from '@/views/Goods/GoodsList/TreeGoods.vue'; import UploadImg from '@/views/Goods/GoodsList/UploadImg.vue'; import WangEditor from '@/views/Goods/GoodsList/WangEditor.vue'; export default { props: { title: { type: String, default: '添加商品' }, rowData: { type: Object, default: function() { return {} } } }, // mounted() { // 不行 // console.log('生命周期--'); // this.goodsForm = this.rowData; // }, components: { TreeGoods, UploadImg, WangEditor }, // 接收父组件(Goods.vue)传值dialogVisible // props: ['dialogVisible'], data() { return { isShow: false, // 是否显示规格参数配置 默认不显示 dialogVisible: false, // 外弹框 innerVisible: false, // 内弹框 innerVisibleImg: false, // 图片弹框 treeData: {}, // 接收 tree 数据 imgUrl: '', // 图片地址 goodsForm: { // 表单容器-对象 id: '', title: '', // 商品的名称 price: '', // 商品的价格 num: '', // 商品的数量 sellPoint: '', // 商品的卖点 image: '', // 商品的图片 descs: '', // 商品的描述 cid: '', // 类目的id category: '', // 商品的类目 // time: '', // 商品发布时间 date1: '', date2: '', // region: '', // delivery: false, // type: [], // resource: '', // desc: '' }, groups: [], // 规格参数 独立的 rules: { // 效验规则 title: [{ required: true, message: '请输入商品名称', trigger: 'blur' }, { min: 2, max: 8, message: '长度在 2 到 8 个字符', trigger: 'blur' } ], price: [{ required: true, message: '请输入商品价格', trigger: 'blur' } // { // min: 3, // max: 5, // message: '长度在 3 到 5 个字符', // trigger: 'blur' // } ], num: [{ required: true, message: '请输入商品数量', trigger: 'blur' } // { // min: 3, // max: 5, // message: '长度在 3 到 5 个字符', // trigger: 'blur' // } ], name: [{ required: true, message: '请输入活动名称', trigger: 'blur' }, { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' } ], region: [{ // required: true, message: '请选择活动区域', trigger: 'change' }], date1: [{ type: 'date', required: true, message: '请选择日期', trigger: 'change' }], date2: [{ type: 'date', required: true, message: '请选择时间', trigger: 'change' }], type: [{ type: 'array', // required: true, message: '请至少选择一个活动性质', trigger: 'change' }], resource: [{ // required: true, message: '请选择活动资源', trigger: 'change' }], desc: [{ // required: true, message: '请填写活动形式', trigger: 'blur' }] } }; }, // 监听器----- watch: { // 不要考虑老数据,直接拿当前的数据 rowData(val) { console.log('监听到数据变化', val); // 赋值 this.goodsForm = val; // 设置富文本编辑的数据内容 ref就是操作DOM // console.log('this.$refs.myEditor', this.$refs.myEditor); // underfine this.$nextTick(() => { this.$refs.myEditor.editor.txt.html(val.descs); // console.log('this.$refs.myEditor', this.$refs.myEditor); }) // 是否显示规格配置参数------ if (val.paramsaInfo) { //不为空显示规格参数 this.isShow = true; this.groups = JSON.parse(val.paramsInfo); // 转成对象 } else { this.isShow = false; } } }, methods: { // 接收 wangEditor 数据 sendEditor(val) { // 存储 this.goodsForm.descs = val; }, // 显示图片地址 sendImg(val) { console.log('显示图片地址', val); this.imgUrl = val; }, // 显示图片---确定按钮 showImg() { // 让内弹框隐藏 this.innerVisibleImg = false; // 渲染图片到页面 this.goodsForm.image = this.imgUrl; }, // 显示 tree 的数据 showTreeData() { // 关闭内弹框 this.innerVisible = false; // 显示 tree 数据 this.goodsForm.category = this.treeData.name; this.goodsForm.cid = this.treeData.cid; // 显示规格参数--获取--向后台(数据库) // this.isShow = true; this.$api.categoryData({ cid: this.treeData.cid }).then(res => { console.log('显示规格参数--获取-', res.data); if (res.data.status === 200) { // 有类目规格配置参数---- this.isShow = true; // 存储规格参数 let result = res.data.result[res.data.result.length - 1]; console.log(result.paramData); this.groups = JSON.parse(result.paramData); } else { this.isShow = false; } }) }, // 获取 tree 数据 sendTreeData(val) { console.log('tree数据', val); this.treeData = val; }, // 自定义事件--通知父组件--修改变量 dialogVisible close() { // 赋值 // this.$emit('changeDialog', false); this.$emit('changeDialog'); }, submitForm() { this.$refs.ruleForm.validate((valid) => { if (valid) { console.log('获取输入的信息', this.goodsForm); console.log('规格参数配置这信息', this.groups); // 结构赋值 // title cid category sellPoint price num descs paramsInfo image let { title, cid, category, sellPoint, price, num, descs, // paramsInfo, image, id } = this.goodsForm; // 判断当前的确定按钮类型 if (this.title === '添加商品') { console.log('添加商品'); this.$api .addGoods({ title, cid, category, sellPoint, price, num, descs, // paramsInfo, // 规格参数 paramsInfo: JSON.stringify(this.groups) image, id }) .then(res => { console.log('添加--实现--', res.data); if (res.data.status === 200) { // 成功 this.$parent.http(1); // 2,更新父组件列表数据 this.$message({ // 3. 消息提示 message: "恭喜你,添加商品成功", type: "success" }); // 清空表单 this.clearForm(); } else { // 失败 this.$message.error('错了,这是一条错误的消息'); } }); } else { console.log('编辑商品'); this.$api.updateGoods({ id, title, cid, category, sellPoint, price, num, descs, // paramsInfo, paramsInfo: JSON.stringify(this.groups) image, id }) .then(res => { console.log(res.data); if (res.data.status === 200) { // 成功 this.$parent.http(1); // 2,更新父组件列表数据 this.$message({ // 3. 消息提示 message: "恭喜你,修改商品成功", type: "success" }); // 清空表单 this.clearForm(); } else { // 修改失败 this.$message.error('错了,这是一条错误的消息'); } }) } } else { console.log('error submit!!'); return false; } }); }, /** * 清空表单数据列表 */ clearForm() { this.dialogVisible = false; // 1,关闭弹框按钮 // 4,清空表单 // 4.1 使用 element 里面的重置表单 // 4.2 自己手动初始化 goodsForm 表格数据 // this.$refs.ruleForm.resetFields(); // 问题将商品描述里面的数据清空了 this.goodsForm = { title: '', // 商品的名称 price: '', // 商品的价格 num: '', // 商品的数量 sellPoint: '', // 商品的卖点 image: '', // 商品的图片 descs: '', // 商品的描述 cid: '', // 类目的id category: '', // 商品的类目 date1: '', // 商品发布时间 date2: '', // 商品发布时间 } // 单独--清空编辑器内容--editor.txt.clear() this.$refs.myEditor.editor.txt.clear(); // 清空规格参数 this.groups = []; this.isShow = false; // 隐藏 } // resetForm(formName) { // this.$refs[formName].resetFields(); // } } } </script> <style lang="less" scoped> .myform { background: #fff; padding: 20px; padding-right: 30px; } .line { text-align: center; } .item { margin: 10px; } </style> 2, src/views/Goods/GoodsList/TreeGoods.vue <template> <!-- props="props" 渲染的数据 配置选项: label: 'name', // 指定节点标签为节点对象的某个属性值 children: 'zones', // 指定子树为节点对象的某个属性值 isLeaf: 'leaf' // 指定节点是否为叶子节点,仅在指定了 lazy 属性的情况下生效 :load="loadNode" // 加载子树数据的方法,仅当 lazy 属性为true 时生效 自动执行函数 -- 异步请求数据 lazy // 是否懒加载子节点,需与 load 方法结合使用 show-checkbox> // 节点是否可被选择 选择框 accordion // 是否每次只打开一个同级树节点展开 node-click // 节点被点击时的回调 共三个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身。 --> <el-tree :props="props" :load="loadNode" lazy accordion @node-click="nodeClick"> </el-tree> </template> <script> export default { data() { return { props: { label: 'name', // 指定节点标签为节点对象的某个属性值 children: 'zones', // 指定子树为节点对象的某个属性值 isLeaf: 'leaf' // 指定节点是否为叶子节点,仅在指定了 lazy 属性的情况下生效 }, }; }, methods: { // 点击 tree 获取数据 nodeClick(data, node) { console.log(data, node); // 传递数据给父组件 this.$emit('sendTreeData', data) }, loadNode(node, resolve) { // resolve() 成功的返回数据结果 // console.log('load--自动执行', node); if (node.level === 0) { // 进入页面 获取第一层的tree数据 // this.$api.getSelectCategory() // .then(res => { // console.log('一级tree', res.data); // return resolve(res.data.result); // }) return resolve([{ name: '家用电器' }, { name: '手机/运营商/数码' }, { name: '电脑/办公' }, { name: '家具/家居' }]); } // 合并 所有级别(level)大于等1 // if (node.level >= 1) { // 合并 // // 请求当前的点击的 tree 下面的数据 // this.$api.getSelectCategory({ // id: node.data.cid // }) // .then(res => { // console.log('二级tree', res.data); // if (res.data.status === 200) { // return resolve(res.data.result); // } else { // return resolve([]) // } // }) // } if (node.level == 1) { // 请求当前的点击的 tree 下面的数据 // this.$api.getSelectCategory({ // 动态从数据库中拿数据 // id: node.data.cid // }) // .then(res => { // console.log('二级tree', res.data); // if (res.data.status === 200) { // return resolve(res.data.result); // } else { // return resolve([]) // } // }) return resolve([{ name: '电视' }, { name: '空调' }, { name: '洗衣机' }, { name: '冰箱' }], [{ name: '手机通讯' }, { name: '运营商' }, { name: '摄影' }, { name: '摄像' }], [{ name: '电脑整机' }, { name: '电脑配件' }, { name: '外设产品' }, { name: '游戏设备' }], [{ name: '厨具' }, { name: '家纺' }, { name: '灯具' }, { name: '家具' }]); } if (node.level == 2) { // // 请求当前的点击的 tree 下面的数据 // this.$api.getSelectCategory({ // id: node.data.cid; // }) // .then(res => { // console.log('三级tree', res.data); // if (res.data.status === 200) { // return resolve(res.data.result); // } else { // return resolve([]) // } // }) return resolve([{ name: '超薄电视' }, { name: '全屏电视' }]); } } } }; </script> <style> </style> 3, src/views/Params/ParamsInfo/ParamsDialog.vue <template> <el-dialog title="添加规格参数" :visible.sync="dialogVisible" width="50%"> <!-- 显示规格类目 --> <TreeGoods @sendTreeData="sendTreeData" /> <!-- 外弹框底部 --> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible = false">取 消</el-button> <el-button type="primary" @click="innerVisible=true" :disabled="isDisabled">确定并添加分组</el-button> </span> <!-- 二级弹框--嵌套 --> <el-dialog width="45%" title="商品规格参数配置" :visible.sync="innerVisible" append-to-body> <div class="title">当前选中的商品: {{ treeData.name }}</div> <el-button type="primary" @click="addDomain">新增规格列表</el-button> <hr /> <!-- groups = [{title: '',value: '', children:[]},...] --> <!-- 动态增减表单项 start--> <el-form :model="dynamicValidateForm" ref="dynamicValidateForm" label-width="100px" class="demo-dynamic"> <el-form-item v-for="(item, index) in dynamicValidateForm.groups" :label="item.title + index" :key="index" :prop="item.title" :rules="{ required: true, message: '域名不能为空', trigger: 'blur' }"> <div class="item"> <el-input v-model="item.title"></el-input> <el-button type="primary" @click.prevent="addChildDomain(index)">添加子组</el-button> <el-button type="warning" @click.prevent="removeDomain(index)">删除</el-button> </div> <!-- 内层的表单项 --> <el-form-item v-for="(ele, i) in item.children" :label="ele.title + i" :key="i" :prop="ele.title" :rules="{ required: true, message: '域名不能为空', trigger: 'blur' }"> <div class="item"> <el-input v-model="ele.title"></el-input> <el-button type="warning" @click.prevent="removeChildDomain(index,i)">删除</el-button> </div> </el-form-item> </el-form-item> </el-form> <!-- 动态增减表单项 end--> <!-- 内弹框底部 --> <span slot="footer" class="dialog-footer"> <el-button type="primary" @click="submitForm('dynamicValidateForm')">确 定</el-button> <el-button @click="resetForm('dynamicValidateForm')">重 置</el-button> </span> </el-dialog> </el-dialog> </template> <script> import TreeGoods from '@/views/Goods/GoodsList/TreeGoods.vue' export default { components: { TreeGoods }, data() { return { dialogVisible: false, innerVisible: false, isDisabled: true, // 默认是不可以点击 treeData: {}, // 接收 tree 数据 dynamicValidateForm: { // 动态表单数据 groups: [], // groups: [{ // value: '', // title: '', // children: [{ // value: '', // title: '', // }] // }, { // value: '', // title: '', // children: [] // }] }, }; }, methods: { // 获取点击 tree 的数据 sendTreeData(val) { console.log('获取 tree 的数据', val); this.treeData = val; this.isDisabled = false; }, // 增加子组 addChildDomain(index) { this.dynamicValidateForm.groups[index].children.push({ value: '', title: '' }) }, // 删除当前组 removeDomain(index) { this.dynamicValidateForm.groups.splice(index, 1) // var index = this.dynamicValidateForm.groups.indexOf(item) // if (index !== -1) { // this.dynamicValidateForm.groups.splice(index, 1) // } }, // 删除子组 removeChildDomain(index, i) { this.dynamicValidateForm.groups[index].children.splice(i, 1); }, // 新增列表---增加大组说明规格配置 addDomain() { this.dynamicValidateForm.groups.push({ value: '', title: '', children: [], }); }, // 提交事件 submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { console.log('提交规格参数', this.dynamicValidateForm.groups); // 参数: itemCatId,content,specsName this.$api.insertItemParams({ itemCatId: this.treeData.cid, specsName: this.treeData.name, content: JSON.stringify(this.dynamicValidateForm.groups), }) .then(res => { console.log('====', res.data); if (res.data.status === 200) { // 添加成功 隐藏弹框 更新规格列表 this.innerVisible = false; this.dialogVisible = false; // 清空数据 this.dynamicValidateForm.groups = []; this.isDisabled = true; this.$parent.http(1); } else { // 最后用弹框 console.log('信息提示失败了--数据库没有去重'); } }) } else { console.log('error submit!!'); return false; } }); }, // 重置 resetForm(formName) { this.$refs[formName].resetFields(); }, }, }; </script> <style lang="less" scoped> .demo-dynamic { margin: 10px; } .item { display: flex; margin-bottom: 10px; button { margin-left: 10px; } } .child_item { display: flex; margin: 10px; } </style> 4, src/api/base.js /** * 接口的路径配置: * 一般文件目录: base.js index.js * base.js : 放所有路径的配置 * index.js: 放所有请求的方法 */ const base = { host: 'http://localhost:8989', // 基础域名 goodsList: '/api/api/projectList', // 商品列表 search: '/api/api/search', // 商品的搜索功能 selectCategory: '/api/api/backend/itemCategory/selectItemCategoryByParentId', // 类目选择 uploadUrl: '/api/api/upload', // 图片上传 post请求 addGoods: '/api/api/backend/item/insertTbItem', // 添加商品 deleteGoods: '/api/api/backend/item/deleteItemById', // 删除商品 updateGoods: '/api/api/backend/item/updateTbItem', // 编辑商品 login: '/api/api/login', // 登录接口 params: '/api/api/backend/itemParam/selectItemParamAll', // 规格参数列表获取 statistical: '/api/api/statistical', // 统计数据 sellTotal: '/api/api/sellTotal', // 统计数据 orderList: '/api/api/order-list', // 订单列表 insertItemParam: '/api/api/backend/itemParam/insertItemParam', // 规格参数的配置--添加 categoryData: '/api/api/category/data', // 规格参数配置 } export default base; 5, src/api/index.js /** * 所有请求的方法 */ import axios from "axios"; import base from "./base"; // node>js const qs = require('querystring'); const api = { /** * 登录接口 */ getLogin(params) { // params={username:'',password:''} // console.log('=====', params, qs.stringify(params)); return axios.post(base.login, qs.stringify(params)) }, /** * 商品列表方法 */ getGoodsList(params) { // {page:xx} return axios.get(base.goodsList, { params }) }, /** * 搜索商品数据方法 * search */ getSearch(params) { // {search: xx} return axios.get(base.search, { params }) }, /** * 获取类目选择 * {id: cid} */ getSelectCategory(params) { return axios.get(base.selectCategory, { params }) }, /** * 添加商品 * 参数: title cid category sellPoint price num desc paramsInfo image */ addGoods(params) { // = {} return axios.get(base.addGoods, { params }) }, /** * 删除商品 id */ deleteGoods(params) { return axios.get(base.deleteGoods, { params }) }, /** * 编辑商品 id */ updateGoods(params) { return axios.get(base.updateGoods, { params }) }, /** * 规格参数获取列表 * params: xx */ getParams(params) { return axios.get(base.params, { params }) }, /** * 获取订单数据 * currPage: xx */ orderList(params) { return axios.get(base.orderList, { params }) }, /** * 规格参数 新增 * 参数: itemCatId,content,specsName */ insertItemParams(params) { return axios.get(base.insertItemParam, { params }) }, /** * 商品列表--获取类目规格配置 * cid */ categoryData(params) { return axios.get(base.categoryData, { params }) } } export default api; 6, server/router.js // 专门放所有的接口 这里只写一部分大约有二十几个接口 // 导入 express const express = require('express') // 使用里面的 Router() 这个方法 const router = express.Router() // token 导入模块 jsonwebtoken 秘钥 const jwt = require('jsonwebtoken') // 秘钥 config.jwtSecert const config = require('./secert.js') // 导入数据库 sqlFn('sql',[],res=>{}) const sqlFn = require('./mysql.js') // 图片上传支持的模块 导入 multer 导入 fs const multer = require('multer') const fs = require('fs') // 导入 mockjs 模块 const Mock = require('mockjs'); // 测试接口 // router.get('/', (req, res) => { // res.send('hello') // }) // 路由接口 // 登录接口 /** * 语法 * 如 60,'2 day','10h','7d',expiration time 过期时间 * jwt.sign({},'秘钥','过期时间',{expiresIn: 20*1,'1 day','1h'}) */ /** * 登录 login * 接收的字段: username password * postman */ router.post('/login', (req, res) => { console.log('获取前端传递的参数', username, password); let { username, password } = req.body // 请求数据库 let sql = "select * from userinfo where username=? and password=?"; let arr = [username, password] console.log(arr); sqlFn(sql, arr, result => { if (result.length > 0) { let token = jwt.sign({ username: result[0].username, id: result[0].id }, config.jwtSecert, { expiresIn: 20 * 1 }) res.send({ status: 200, data: token }) } else { res.send({ status: 404, msg: '信息错误' }) } }) }) // router.post("/login", (req, res) => { // let { // username, // password // } = req.body // // 请求数据库 // let sql = "select * from userinfo where username=? and password=?"; // let arr = [username, password] // sqlFn(sql, arr, result => { // if (result.length > 0) { // let token = jwt.sign({ // username: result[0].username, // id: result[0].id // }, config.jwtSecert, { // expiresIn: 20 * 1 // }) // res.send({ // status: 200, // data: token // }) // } else { // res.send({ // status: 404, // msg: '信息错误' // }) // } // }) // }) /** * 注册接口 /register */ /** * 注册接口 /register */ router.post("/register", (req, res) => { const { username, password } = req.body; const sql = "insert into userinfo values(null,?,?)"; const arr = [username, password]; sqlFn(sql, arr, (result) => { if (result.affectedRows > 0) { res.send({ msg: "注册成功", status: 200 }) } else { res.status(401).json({ errors: "用户名密码错误" }) } }) }) /** * 商品列表:获取分页 {total: '',arr:[{},{},{}],pagesize:8,} * 参数:page 页码 */ router.get('/projectList', (req, res) => { const page = req.query.page || 1; const sqlLen = "select * from project where id"; sqlFn(sqlLen, null, data => { let len = data.length; const sql = "select * from project order by id desc limit 8 offset" + (page - 1) * 8; sqlFn(sql, null, result => { if (result.length > 0) { res.send({ status: 200, data: result, pageSize: 8, total: len }) } else { res.send({ status: 200, msg: "暂无数据" }) } }) }) }) // router.get('/projectList', (req, res) => { // // 接收页码 可以不传 默认为1 // const page = req.query.page || 1; // // 根据 id 去查 project 表 // const sqlLen = "select * from project where id"; // sqlFn(sqlLen, null, data => { // let len = data.length; // const sql = "select * from project order by id desc limit 8 offset" + (page - 1) * 8; // sqlFn(sql, null, result => { // if (result.length > 0) { // // 返回数据 // res.send({ // status: 200, // data: result, // pageSize: 8, // total: len // }) // } else { // // 返回数据 // res.send({ // status: 500, // msg: "暂无数据" // }) // } // }) // }) // }) /** * 商品查询接口 search * 参数: search */ router.get("/search", (req, res) => { var search = req.query.search; const sql = "select * from project where concat(`title`,`sellPoint`,`descs`) like '%" + search + "%'"; sqlFn(sql, null, (result) => { if (result.length > 0) { res.send({ status: 200, data: result }) } else { res.send({ status: 500, msg: '暂无数据' }) } }) }) /** 类目选择 * 接口说明:接口不同的参数 cid 返回不同的类目数据,后台接受变量 id */ router.get('/backend/itemCategory/selectItemCategoryByParentId', (req, res) => { const id = req.query.id || 1; const sql = 'select * from category where id=?' var arr = [id]; sqlFn(sql, arr, result => { if (result.length > 0) { res.send({ status: 200, result // data: result }) } else { res.send({ status: 500, msg: '暂无数据' }) } }) }) /** * 类目结构数据获取 */ router.get('/category/data', (req, res) => { var cid = req.query.cid; var sql = "select * from params where itemCatId=?"; sqlFn(sql, [cid], result => { if (result.length > 0) { res.send({ status: 200, result // data: result }) } else { res.send({ status: 500, msg: '暂无数据' }) } }) }) /** * 上传图片 post 请求 upload * 说明: * 1, 后台安装 multer 图片模块 同时引入 fs 文件模块 * 2,router.js 入口文件导入 模块 * const fs = require('fs') //fs是属于nodejs,只需引入即可 * const multer=require('multer') // multer是需要安装的 * 3, 上传图片 可以跨域 需要配置 cors index.js 导入文件,并配置 cors跨域 * 4, 在服务端 server 根目录下创建 upload 文件夹,专门装图片的文件 */ var storage = multer.diskStorage({ destination: function(req, file, cb) { cb(null, './upload/') }, filename: function(req, file, cb) { cb(null, Date.now() + "-" + file.originalname) } }) var createFolder = function(folder) { try { fs.accessSync(folder); } catch (e) { fs.mkdirSync(folder); } } var uploadFolder = './upload'; createFolder(uploadFolder); var upload = multer({ storage: storage }); router.post('/upload', upload.single('file'), function(req, res, next) { var file = req.file; console.log('文件类型,%s', file.mimetype); console.log('原始文件名,%s', file.originalname); console.log('文件大小,%s', file.size); console.log('文件保存路径,%s', file.path); res.json({ res_code: '0', name: file.originalname, url: file.path }); }); /** * 商品添加接口 * 参数: title cid category sellPoint price num descs paramsInfo image */ router.get('/backend/item/insertTbItem', (req, res) => { // 获取参数 var title = req.query.title || ""; var cid = req.query.cid || ""; var category = req.query.category || ""; var sellPoint = req.query.sellPoint || ""; var price = req.query.price || ""; var num = req.query.num || ""; var desc = req.query.descs || ""; var paramsInfo = req.query.paramsInfo || ""; var image = req.query.image || ""; const sql = "insert into project values (null,?,?,?,?,?,?,?,'',1,'','',?,?)" var arr = [title, image, sellPoint, price, cid, category, num, desc, paramsInfo]; sqlFn(sql, arr, result => { if (result.affectedRows > 0) { res.send({ status: 200, msg: "添加成功" }) } else { res.send({ status: 500, msg: "添加失败" }) } }) }) /** * 商品删除 接口 id */ router.get("/backend/item/deleteItemById", (req, res) => { // 后端接收前端传递的数据 var id = req.query.id; const sql = "delete from project where id=?" const arr = [id]; sqlFn(sql, arr, result => { if (result.affectedRows > 0) { res.send({ status: 200, msg: "删除成功" }) } else { res.send({ status: 500, msg: '删除失败' }) } }) }) /** * 批量删除: batchDelete idArr id 标识 * sql = "delete from A where in in (1,2,3)" */ router.get("/batchDelete", (req, res) => { let arr = req.query.idArr; // []数组格式 需要传递数据是 离散的数字格式 // const sql = 'delete from project where id in (?)'; let sql = ''; function fun(arr) { // sql=`delete from project where id in (101,102,103`; sql = `delete from project where id in (` for (let i = 0; i < arr.length; i++) { sql += arr[i] + ',' // 101,102, } sql = sql.slice(0, -1) sql = sql + ')' // console.log(sql); } fun(arr) sqlFn(sql, null, result => { if (result.affectedRows > 0) { res.send({ status: 200, msg: "删除成功" }) } else { res.send({ status: 500, msg: "删除失败" }) } }) /** * 修改商品 */ router.get("/backend/item/updateTbItem", (req, res) => { var id = req.query.id; var title = req.query.title || ""; var sellPoint = req.query.sellPoint || ""; var price = req.query.price || ""; var cid = req.query.cid || ""; var category = req.query.category || ""; var num = req.query.num || ""; var desc = req.query.descs || ""; var paramsInfo = req.query.paramsInfo || ""; var image = req.query.image || ""; var sql = "update project set title=?,sellPoint=?,price=?,cid=?,category=?,num=?,descs=?,paramsInfo=?,image=?" var arr = [title, sellPoint, price, cid, category, num, descs, paramsInfo, image, id]; sqlFn(sql, arr, result => { if (result.affectedRows > 0) { res.send({ status: 200, msg: "修改成功" }) } else { res.send({ status: 500, msg: "修改失败" }) } }) }) }) /** * 规格参数列表 参数 page */ router.get("/backend/itemParam/selectItemParamAll", (req, res) => { const page = req.query.page || 1; const sqlLen = "select * from params where id"; sqlFn(sqlLen, null, data => { let len = data.length; const sql = "select * from params order by id desc limit 8 offset" + (page - 1) * 8; sqlFn(sql, null, result => { if (result.length > 0) { res.send({ status: 200, data: result, pageSize: 8, total: len }) } else { res.send({ status: 500, msg: '暂无数据' }) } }) }) }) /** * 规格参数 模糊查询 参数; search */ router.get('/params/search', (req, res) => { var search = req.query.search; const sql = "select * from params where concat('paramData') like '%" + search + "%' "; sqlFn(sql, [search], result => { if (result.length > 0) { res.send({ status: 200, result }) } else { res.send({ status: 500, msg: '暂无数据' }) } }) }) /** * 规格参数 添加 * 参数: itemCatId,content,specsName */ router.get('/backend/itemParam/insertItemParam', (req, res) => { var itemCatId = req.query.itemCatId; var paramsContent = req.query.content; var specsName = req.query.specsName; // console.log(itemCatId,paramsContent,specsName); var sql = "insert into params values (null,?,?,?)"; sqlFn(sql, [itemCatId, paramsContent, specsName], result => { if (result.affectedRows > 0) { res.send({ status: 200, msg: '添加成功' }) } else { res.send({ status: 500, msg: '添加失败' }) } }) }) /** * 修改规格参数 cid content id specsnName */ router.get('/update/category', (req, res) => { var cid = req.query.cid; var content = req.query.content; var id = req.query.id; var specsName = req.query.specsName; var sql = "update params set paramData=?,itemCatId=?,specsName=? where id=?"; sqlFn(sql, [content, cid, specsName, id], result => { if (result.affectedRows > 0) { res.send({ status: 200, msg: '修改成功' }) } else { res.send({ status: 500, msg: '修改失败' }) } }) }) /** * 规格参数 删除 */ router.get('/params/delete', (req, res) => { var id = req.query.id; const sql = "delete from params where id=?" const arr = [id]; sqlFn(sql, arr, result => { if (result.affectedRows > 0) { res.send({ status: 200, msg: '删除成功' }) } else { res.send({ status: 500, msg: '删除失败' }) } }) }) /** * 规格参数类目结构数据获取 cid */ router.get('/category/data', (req, res) => { var cid = req.query.cid; var sql = "select * from params where itemCatId=?"; sqlFn(sql, [cid], result => { if (result.length > 0) { res.send({ status: 200, result }) } else { res.send({ status: 500, msg: '暂无数据' }) } }) }) /** * 内容分类管理 导航 */ router.get('/content/selectContentCategoryByParentId', (req, res) => { const id = req.query.id || 1; const sql = "select * from content where id=?"; sqlFn(sql, [id], result => { if (result.length > 0) { res.send({ status: 200, result }) } else { res.send({ status: 500, msg: '暂无数据' }) } }) }) /** * 统计数据--销售信息 */ router.get('/statistical', (req, res) => { res.send(Mock.mock({ success: true, status: 200, "list|4": [{ 'id|+1': 100, "title|+1": ['总销售额', '访问量', '支付总量', '收藏量'], "current|0-2000": 100, "total|100-999999": 200 }] })) }) /** * 统计 半年 月销量对比数据 * 月度销售额 */ router.get('/sellTotal', (req, res) => { res.send(Mock.mock({ success: true, status: 200, info: { // (property)'id|+1': numer 'id|+1': 100, date: function() { var category = []; var dottedBase = +new Date(); for (var i = 30; i > 0; i--) { var date = new Date((dottedBase -= 1000 * 3600 * 24 * 30)); category.push([date.getFullYear(), date.getMonth() + 1].join()); } return category.slice(0, 6); }, "xResult|3": [{ 'xName|+1': ["家具", "手机", "家电"], "data|6": [{ 'num|100-1000': 10 }] }, ], } })) }) // 测试 mockjs 数据 router.get('/test', (req, res) => { // 使用 mock 生成数据 let data = Mock.mock({ info: '我是一个单纯的对象', status: 200, // 生成list字段:数组类型 内容是6个数据 = {} 就是6个对象 +1 id会累加 "list|6": [{ "id|+1": 100, // id 自增的格式 若 id为100 你的起始值就是100 "flag|1-2": true, // 写成如下对象,表示随机从里面取两个 "province|2": { // 获取两个省份的数据 "310000": "上海市", "320000": "江苏省", "330000": "浙江省", "340000": "安徽省" }, "arr|+1": [ // 依次获取一个数据值,依次获取 数组加1 表示一个一个依次取值 "AMD", "CMD", "UMD", "CLS", "CLEAR", "CLOSE" ], // 随机汉字 "desc": '@cword(20,80)', // 图片 // "imgUrl": '@image()', "imgUrl": '@Image()', // 过滤数据 或者拼接 'foo': 'Syntax Demo', 'name': function() { return this.foo }, // 正则 'regexp': /[a-z][A-Z][0-9]/, // Path 路径 "foo1": "Hello", "nested": { "a": { "b": { "c": "Mock.js" } } }, "absolutePath": "@/foo1 @/nested/a/b/c" // date }], }) res.send(data) }) // ===================== /** * 内容分类管理 内容查询 */ router.get("/content/selectTbContentAllByCategoryId", (req, res) => { const pid = req.query.pid; const sql = "select * from contentinfo where pid=?" sqlFn(sql, [pid], result => { if (result.length > 0) { res.send({ status: 200, result }) } else { res.send({ status: 500, msg: "暂无数据" }) } }) }) module.exports = router
![]()
动态增减表单项
<el-form :model="dynamicValidateForm" ref="dynamicValidateForm" label-width="100px" class="demo-dynamic"> <el-form-item prop="email" label="邮箱" :rules="[ { required: true, message: '请输入邮箱地址', trigger: 'blur' }, { type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] } ]" > <el-input v-model="dynamicValidateForm.email"></el-input> </el-form-item> <el-form-item v-for="(domain, index) in dynamicValidateForm.domains" :label="'域名' + index" :key="domain.key" :prop="'domains.' + index + '.value'" :rules="{ required: true, message: '域名不能为空', trigger: 'blur' }" > <el-input v-model="domain.value"></el-input><el-button @click.prevent="removeDomain(domain)">删除</el-button> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('dynamicValidateForm')">提交</el-button> <el-button @click="addDomain">新增域名</el-button> <el-button @click="resetForm('dynamicValidateForm')">重置</el-button> </el-form-item> </el-form> <script> export default { data() { return { dynamicValidateForm: { domains: [{ value: '' }], email: '' } }; }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { alert('submit!'); } else { console.log('error submit!!'); return false; } }); }, resetForm(formName) { this.$refs[formName].resetFields(); }, removeDomain(item) { var index = this.dynamicValidateForm.domains.indexOf(item) if (index !== -1) { this.dynamicValidateForm.domains.splice(index, 1) } }, addDomain() { this.dynamicValidateForm.domains.push({ value: '', key: Date.now() }); } } } </script>
![]()
![]()