商城后台管理系统 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> 
相关推荐
dsyyyyy11014 小时前
JavaScript变量
开发语言·javascript·ecmascript
kyriewen4 小时前
手写 Promise.all、race、any:不到 30 行代码,解决并发异步的所有姿势
前端·javascript·面试
胡志辉的博客6 小时前
深入浅出理解浏览器事件循环:从一道输出题讲到 Chrome 源码
前端·javascript·chrome·chromium·event loop
代码不加糖6 小时前
js中不会冒泡的事件有哪些?
前端·javascript·vue.js
懂懂tty6 小时前
Vue2与Vue3之间API差异
前端·javascript·vue.js
老毛肚7 小时前
软件测试期末考试
vue.js
小二·7 小时前
Next.js 15 全栈开发实战
开发语言·javascript·ecmascript
杨若瑜8 小时前
本地开发环境慢?localhost的锅!
vue.js
Rain5098 小时前
2.1 Nest.js 项目初始化与模块化架构
开发语言·前端·javascript·后端·架构·数据分析·node.js
拾年2759 小时前
从零手写 Ajax:用原生 XHR 搭建前后端交互全流程
前端·javascript·ajax