商城后台管理系统 03 规格参数配置

规格参数配置 实现代码如下
复制代码
1, src/views/Params/ParamsInfo/Specifications.vue

<template>
	<div class="params">
		<!-- 二级菜单 -->
		<!-- 1, 目录位置 -->
		<div class="nav">
			<el-breadcrumb separator="/">
				<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
				<el-breadcrumb-item><a href="/">规格管理</a></el-breadcrumb-item>
				<el-breadcrumb-item :to="{ path: '/params' }">规格参数</el-breadcrumb-item>
				<el-breadcrumb-item :to="{ path: '/params/specifications' }">规格包装</el-breadcrumb-item>
			</el-breadcrumb>
		</div>
		<!-- 2, 搜索框 -->
		<div class="header">
			<el-input v-model="inp" />
			<el-button type="primary">查看</el-button>
			<el-button type="primary" @click="showParams">添加</el-button>
		</div>

		<!-- 3, 表格区域 展示视图数据 -->
		<el-table :data="tableData" class="my-table">
			<el-table-column prop="itemCatId" label="规格参数ID" width="120">
			</el-table-column>
			<el-table-column prop="id" label="类目ID" width="120">
			</el-table-column>
			<el-table-column prop="specsName" label="规格名称" width="120">
			</el-table-column>
			<el-table-column prop="paramsData" label="规格参数" show-overflow-tooltip>
			</el-table-column>

			<!-- <el-table-column prop="date" label="日期" width="180">
			</el-table-column>
			<el-table-column prop="name" label="姓名" width="180">
			</el-table-column>
			<el-table-column prop="address" label="地址">
			</el-table-column>
			<el-table-column prop="email" label="邮件">
			</el-table-column> -->
			<el-table-column label="操作" width="280">
				<template slot-scope="scope">
					<el-button type="info" size="mini">查看</el-button>
					<el-button type="primary" size="mini" @click="handleEdit(scope.$index, scope.row)"
						icon="el-icon-edit">编辑</el-button>
					<el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)"
						icon="el-icon-delete">删除</el-button>
				</template>
			</el-table-column>
		</el-table>

		<!-- 4, 分页展示  接收页码- -->
		<MyPagination :total="total" :pageSize="pageSize" @changePage="changePage" :currentPage="currentPage" />

		<!-- 5, 弹框  dialog是页面变量-->
		<ParamsDialog ref="dialog" />
	</div>
</template>

<script>
	import MyPagination from '@/components/MyPagination.vue';
	import ParamsDialog from '@/views/Params/ParamsInfo/ParamsDialog.vue';
	export default {
		components: {
			MyPagination,
			ParamsDialog
		},
		data() {
			return {
				inp: '', // 输入框
				tableData: [],
				total: 10,
				pageSize: 1,
				currentPage: 1,

				// tableData: [{
				// 	id: '2016-05-02',
				// 	itemCatId: '王小虎',
				// 	specsName: '上海市普陀区金沙江路 1518 弄',
				// 	paramsData: '2570650096@qq.com'
				// }, {
				// 	id: '2016-05-02',
				// 	itemCatId: '王小虎',
				// 	specsName: '上海市普陀区金沙江路 1518 弄',
				// 	paramsData: '2570650096@qq.com'
				// }, {
				// 	id: '2016-05-02',
				// 	itemCatId: '王小虎',
				// 	specsName: '上海市普陀区金沙江路 1518 弄',
				// 	paramsData: '2570650096@qq.com'
				// }, {
				// 	id: '2016-05-02',
				// 	itemCatId: '王小虎',
				// 	specsName: '上海市普陀区金沙江路 1518 弄',
				// 	paramsData: '2570650096@qq.com'
				// }], // 
			}
		},
		methods: {
			// 点击显示弹框--配置规格参数
			showParams() {
				this.$refs.dialog.dialogVisible = true;
			},
			// 点击分页 切换
			changePage(num) {
				this.http(num);
			},
			// 获取规格参数列表

			http(page) {
				this.$api.getParams({
						page
					})
					.then(res => {
						console.log(res.data);
						if (res.data.status === 200) {
							this.tableData = res.data.data;
							// 获取分页
							this.total = res.data.total;
							this.pageSize = res.data.pageSize;
						}
					})
			}
		},
		created() {
			this.http(1)
		}
	}
</script>

<style lang="less" scoped>
	.params {
		margin: 10px;
	}

	.nav {
		padding: 10px;
	}

	.header {
		display: flex;
		background: #fff;
		padding: 10px;
		border: 1px solid #eee;

		button {
			margin-left: 20px;
		}
	}

	.my-table {
		margin: 10px auto;
	}
</style>





2, 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>




3, 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>




4, src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/views/Layout/index.vue'
import Home from '@/views/Home/index.vue'
import Login from '@/views/Login/Login.vue'

// 异步
const Goods = () => import('../views/Goods/GoodsList/Goods.vue')
const Params = () => import('../views/Params/Params.vue')
const Specifications = () => import('../views/Params/ParamsInfo/Specifications.vue')
const Advert = () => import('../views/Advert/Advert.vue')
const My = () => import('../views/My/My.vue')
const Logistics = () => import('../views/Logistics/Logistics.vue')
const Order = () => import('../views/Order/index.vue')
const OrderList = () => import('../views/Order/OrderList/index.vue')
const OrderBack = () => import('../views/Order/OrderBack/index.vue')

// 子级路由
const AddGoods = () => import('../views/Goods/GoodsList/AddGoods.vue')


Vue.use(VueRouter)

const routes = [{
		path: '',
		component: Layout,
		// 路由的元信息
		meta: {
			isLogin: false
		},
		children: [{
			path: '/',
			name: 'Home',
			component: Home
		}, {
			path: '/goods',
			name: 'Goods',
			component: Goods
		}, {
			path: '/add-goods',
			name: 'AddGoods',
			component: AddGoods
		}, {
			path: '/params',
			name: 'Params',
			component: Params,
			redirect: '/params/specifications',
			children: [{
				path: 'specifications',
				name: 'Specifications',
				component: Specifications
			}]
		}, {
			path: '/advert',
			name: 'Advert',
			component: Advert
		}, {
			path: '/my',
			name: 'My',
			component: My
		}, {
			path: '/logistics',
			name: 'Logistics',
			component: Logistics
		}, {
			path: '/order',
			name: 'Order',
			component: Order,
			redirect: '/order/order-list',
			children: [{
				path: 'order-list',
				component: OrderList
			}, {
				path: 'order-back',
				component: OrderBack
			}]
		}]
	},
	{
		path: '/login',
		name: 'Login',
		component: Login,
	}
]

const router = new VueRouter({
	mode: 'history',
	base: process.env.BASE_URL,
	routes
})



export default router




5, 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' // 规格参数的配置--添加 
}

export default base;





6, 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
		})
	}
}

export default api;





7, 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
订单接口模拟
复制代码
/**
* 订单列表
*/
var MockRandom = Mock.Random;
var pageCount = MockRandom.integer(1, 10); // 7
var haseMore = true; // 是否还有更多数据true有数据,false无更多数据 结束分页展示
var ids = 100; // 自增长 id
var result = {}; // 数据模板

router.get('/order-list', (req, res) => {
	var currPage = parseInt(req.query.currPage || 1); // 页码
	ids = currPage * 100;
	if (currPage === pageCount) {
		//
	} else {
		//
	}
	// 返回数据
	let mockData = Mock.mock(result);
	if (pageCount >= currPage ) {
		res.send({
			//
		})
	}
})
JS里面数据结构操作已经写完,加完以后,点击 '确定' 提交,把这些数据提交到后台接口里面,效验通过,点击 '重置',将他们存入到数据库里面。
确认事件 方法
复制代码
 :before-close="handleClose"
 
<!-- 内弹框底部 -->
<span slot="footer" class="dialog-footer">
    <el-button @click="dialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="innerVisible=false">确 定</el-button>
</span>


handleClose(done) {
    this.$confirm('确认关闭?')
        .then(_ => {
            done();
        })
        .catch(_ => {});
},
原本事件备份
复制代码
<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> 
相关推荐
知其然亦知其所以然6 小时前
JavaScript 变量的江湖恩怨:一篇文章彻底讲清楚
前端·javascript·面试
小番茄夫斯基6 小时前
使用 pnpm + Workspaces 构建 Monorepo 的完整指南
前端·javascript·vue.js
shuaijie05186 小时前
两个表格进行相互联动
前端·javascript·vue.js
JS_GGbond6 小时前
让代码学会“等外卖”:JavaScript异步编程趣谈
前端·javascript
随风一样自由6 小时前
React编码时,什么时候用js文件,什么时候用jsx文件?
开发语言·javascript·react.js
by__csdn7 小时前
Vue3 生命周期全面解析:从创建到销毁的完整指南
开发语言·前端·javascript·vue.js·typescript·前端框架·ecmascript
小年糕是糕手7 小时前
【C++同步练习】模板初阶
服务器·开发语言·前端·javascript·数据库·c++·改行学it
醒了接着睡7 小时前
Vue3 插槽的本质
vue.js
进击的野人7 小时前
Vue生命周期详解:从创建到销毁的全过程
前端·vue.js·面试