技术栈
Vue全家桶:Vue + VueRouter + Vuex + Axios +ElementUI
依赖安装
网络请求:npm install --save axios --no-fund
Element:vue add element
后端相关依赖:npm install --save express cors mysql --no-fund
token:npm install --save jsonwebtoken --no-fund
对象转换:npm install querystring --no-fund
文件上传:npm install --save multer --no-fund
富文本编辑器:npm install wangeditor --save --no-fund
快捷运行方案
npm install -g concurrently --no-fund
npm install -g nodemon --no-fund
项目框架搭建
1.使用vue create xxx
命令创建项目
2.使用VS Code打开项目
端口号修改:修改vue.config.js文件,增加如下代码
添加elementUI
1.终端输入vue add element
添加element
添加完毕后,项目新增element文件
2.运行项目,查看项目是否可正常运行
注:因为版本问题,可能运行时会报错,以及element组件无法根据使用自动追加对应的库,所以这里element.js文件我改了,如上图所示
数据库准备
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for category
-- ----------------------------
DROP TABLE IF EXISTS `category`;
CREATE TABLE `category` (`id` int(11) NOT NULL,`cid` int(11) NULL DEFAULT NULL,`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`pid` int(11) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of category
-- ----------------------------
INSERT INTO `category` VALUES (1, 1001, '家用电器', 1);
INSERT INTO `category` VALUES (2, 1002, '手机/运营商/数码', 1);
INSERT INTO `category` VALUES (3, 1003, '电脑/办公', 1);
INSERT INTO `category` VALUES (4, 1004, '家具/家居', 1);
INSERT INTO `category` VALUES (5, 10001, '电视', 1001);
INSERT INTO `category` VALUES (6, 10001, '手机通讯', 1002);
INSERT INTO `category` VALUES (7, 10001, '电脑整机', 1003);
INSERT INTO `category` VALUES (8, 10001, '厨具', 1004);
INSERT INTO `category` VALUES (9, 10002, '空调', 1001);
INSERT INTO `category` VALUES (10, 10002, '运营商', 1002);
INSERT INTO `category` VALUES (11, 10002, '电脑配件', 1003);
INSERT INTO `category` VALUES (12, 10002, '家纺', 1004);
INSERT INTO `category` VALUES (13, 10003, '洗衣机', 1001);
INSERT INTO `category` VALUES (14, 10003, '摄影', 1002);
INSERT INTO `category` VALUES (15, 10003, '外设产品', 1003);
INSERT INTO `category` VALUES (16, 10003, '灯具', 1004);
INSERT INTO `category` VALUES (17, 10004, '冰箱', 1001);
INSERT INTO `category` VALUES (18, 10004, '摄像', 1002);
INSERT INTO `category` VALUES (19, 10004, '游戏设备', 1003);
INSERT INTO `category` VALUES (20, 10004, '家具', 1004);
INSERT INTO `category` VALUES (21, 100001, '超薄电视', 10001);
INSERT INTO `category` VALUES (22, 100002, '全面屏电视', 10001);-- ----------------------------
-- Table structure for project
-- ----------------------------
DROP TABLE IF EXISTS `project`;
CREATE TABLE `project` (`id` int(11) NOT NULL AUTO_INCREMENT,`title` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '标题',`image` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图片路径',`sellPoint` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`price` int(10) NULL DEFAULT NULL COMMENT '价格',`cid` int(11) NULL DEFAULT NULL,`num` int(11) NULL DEFAULT NULL,`barcode` tinyint(4) NULL DEFAULT NULL,`status` tinyint(4) NULL DEFAULT NULL,`created` datetime(0) NULL DEFAULT NULL,`updated` datetime(0) NULL DEFAULT NULL,`descs` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 31 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of project
-- ----------------------------
INSERT INTO `project` VALUES (1, '隐形的守护', NULL, '互动游戏', 128, 1, 4, NULL, 1, NULL, NULL, '这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!');
INSERT INTO `project` VALUES (2, '饥饿游戏', NULL, '影视作品', 58, 1, 5, NULL, 1, NULL, NULL, NULL);
INSERT INTO `project` VALUES (3, '家族4', NULL, 'slg游戏', 18, NULL, 123, NULL, 1, NULL, NULL, '这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!');
INSERT INTO `project` VALUES (4, '家族1', NULL, 'slg游戏', 12, NULL, 123, NULL, 1, NULL, NULL, '这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!');
INSERT INTO `project` VALUES (5, '家族2', NULL, 'slg游戏', 14, NULL, 22, NULL, 1, NULL, NULL, '这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!');
INSERT INTO `project` VALUES (6, '家族3', NULL, 'slg游戏', 18, NULL, 42, NULL, 1, NULL, NULL, '这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!这是描述!!!!');
INSERT INTO `project` VALUES (7, '三国群英传1', NULL, 'slg游戏', 12, NULL, 421, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (9, '三国群英传3', NULL, 'slg游戏', 12, NULL, 421, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (10, '三国群英传4', NULL, 'slg游戏', 12, NULL, 421, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (11, '三国群英传5', NULL, 'slg游戏', 12, NULL, 421, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (12, '三国群英传6', NULL, 'slg游戏', 12, NULL, 421, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (13, '三国群英传7', NULL, 'slg游戏', 12, NULL, 421, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (14, '三国群英传8', NULL, 'slg游戏', 12, NULL, 421, NULL, 1, NULL, NULL, '毫无亮点,走网游路线了');
INSERT INTO `project` VALUES (15, '三国志1', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (16, '三国志2', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (17, '三国志3', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (18, '三国志4', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (19, '三国志5', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (20, '三国志6', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (21, '三国志7', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (22, '三国志8', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (23, '三国志9', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (24, '三国志10', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (25, '三国志11', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (26, '三国志12', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (27, '三国志13', NULL, 'slg游戏', 32, NULL, 32, NULL, 1, NULL, NULL, '光荣只会卖情怀');
INSERT INTO `project` VALUES (28, '三国无双1', 'http://localhost:3000\\1732370097961-1.jpg', '任务强度高', 58, 10002, 500, NULL, 1, NULL, NULL, '<p>方式的监护人突然<strike>让我如很高的发挥</strike><font color=\"#f9963b\">地方规划人都热</font>帖该问题公司的归属感和无人</p>');
INSERT INTO `project` VALUES (30, '三国无双4', 'http://localhost:3000\\1732702595899-1.jpg', '情怀', 58, 10003, 12, NULL, 1, NULL, NULL, '<p>是的话<font color=\"#c24f4a\">标点符号的分</font>工</p>');-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (7, 'cy', '123456', '123456');
INSERT INTO `user` VALUES (8, 'admin', '123456', 'admin@123.com');
INSERT INTO `user` VALUES (9, 'admin1', '123456', 'admin@123.com');
INSERT INTO `user` VALUES (10, 'admin2', '123456', 'admin@123.com');SET FOREIGN_KEY_CHECKS = 1;
项目开发
1.准备页签栏
在src/views
文件夹内创建main/ADCategory.vue
、main/ParamsView.vue
、main/ProductView.vue
、LayoutView.vue
文件
修改router/index.js
文件,增加对应路由跳转配置
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
import Layout from '../views/LayoutView.vue'Vue.use(VueRouter)const routes = [{path: '/',name: 'Layout',component: Layout,children:[{path: '',name: 'home',component: HomeView},{path:"product",name:"Product",component:() => import("../views/main/ProductView.vue")},{path:"params",name:"Params",component:() => import("../views/main/ParamsView.vue")},{path:"ad",name:"ADCategory",component:() => import("../views/main/ADCategory.vue")}]},
]
const router = new VueRouter({routes,// 去除路径上的#号mode:"history"
})
export default router
2.登录拦截
在router
文件夹下新建permission.js文件,编写拦截代码
import router from "./index"
import store from "../store"// 访问路径前判断权限
router.beforeEach((to,from,next) =>{if(to.meta.isLogin){// 暂未实现tokenlet token = false;console.log(token);if(token){next();}else{next({name:"Login"})}}else{next();}
})
修改router/index.js
文件,给需要登录才能访问的路由设置isLogin参数
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
import Layout from '../views/LayoutView.vue'Vue.use(VueRouter)const routes = [{path: '/',name: 'Layout',component: Layout,children:[{path: '',name: 'home',component: HomeView,meta:{isLogin:true}},{path:"product",name:"Product",component:() => import("../views/main/ProductView.vue"),meta:{isLogin:true}},{path:"params",name:"Params",component:() => import("../views/main/ParamsView.vue"),meta:{isLogin:true}},{path:"ad",name:"ADCategory",component:() => import("../views/main/ADCategory.vue"),meta:{isLogin:true}}]},{path:'/login',name:'Login',component:() => import("../views/LoginView.vue")}
]
const router = new VueRouter({routes,// 去除路径上的#号mode:"history"
})
export default router
修改main.js
文件,导入permission.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './plugins/element.js'
// 导入permission.js文件
import './router/permission'Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App)
}).$mount('#app')
编写LoginView.vue
文件
<template><div>登录注册</div>
</template><script>
export default {
}
</script><style>
</style>
运行效果
3.编写登录注册页
编写LoginView.vue
文件
<template><div class="login"><el-card class="box-card"><div slot="header" class="clearfix"><span>Ego商城后台管理系统</span></div><div><el-tabs v-model="currentIndex" stretch type="border-card" @tab-click="handleTabClick"><el-tab-pane label="登录" name="login"><el-form :model="loginForm" status-icon ref="loginForm" :rules="rules"><el-form-item label="用户名:" label-width="80px" prop="username"><el-input type="text" v-model="loginForm.username" /></el-form-item><el-form-item label="密码:" label-width="80px" prop="password"><el-input type="password" v-model="loginForm.password" /></el-form-item><el-form-item ><el-button type="primary" @click="submitForm('loginForm')">登录</el-button></el-form-item></el-form></el-tab-pane><el-tab-pane label="注册" name="register"><el-form :model="registerForm" status-icon ref="registerForm" :rules="rules"><el-form-item label="用户名:" label-width="80px" prop="username"><el-input type="text" v-model="registerForm.username" /></el-form-item><el-form-item label="邮箱:" label-width="80px" prop="email"><el-input type="text" v-model="registerForm.email" /></el-form-item><el-form-item label="密码:" label-width="80px" prop="password"><el-input type="password" v-model="registerForm.password" /></el-form-item><el-form-item label="确认密码:" label-width="80px" prop="configurePassword"><el-input type="password" v-model="registerForm.configurePassword" /></el-form-item><el-form-item ><el-button type="primary" @click="submitForm('registerForm')">注册</el-button></el-form-item></el-form></el-tab-pane></el-tabs></div></el-card></div></template><script>
export default {data(){// 验证规则var validateUsername = (rule,value,callback) =>{if(value === ""){callback(new Error("请输入用户名"));}else if(value.length < 6){callback(new Error("长度不足6位"));}else{callback();}}var validatePassword = (rule,value,callback) =>{if(value === ""){callback(new Error("请输入密码"));}else{callback();}}var validateConfigurePassword = (rule,value,callback) =>{if(value === ""){callback(new Error("请输入密码"));}else if(value !== this.registerForm.password){callback(new Error("两次密码不一致"));}else{callback();}}return{currentIndex:"login",loginForm:{username:"",password:""},registerForm:{username:"",password:"",email:"",configurePassword:""},activeTab:"login",rules:{username:[{validator:validateUsername,trigger:'blur'}],password:[{validator:validatePassword,trigger:'blur'}],configurePassword:[{validator:validateConfigurePassword,trigger:'blur'}]}}},methods:{submitForm( formName ){this.$refs[formName].validate((valid) =>{if(valid){// 根据当前所在tabs页签的name的值去执行对应页签的逻辑if(this.activeTab === "login"){// 提交注册表单逻辑console.log(this.loginForm);}else if(this.activeTab === "register"){// 提交注册表单逻辑console.log(this.registerForm);}else{return;}}})},// 获取当前所在tabs页签的namehandleTabClick(tab){this.activeTab = tab.name;}}
}
</script><style scoped lang="less">
.login{width:1200px;margin: 0 auto;.box-card{width: 500px;margin: 100px auto;}
}
</style>
运行效果
4.nodeJS后台实现
在项目目录下新建server
目录(与src目录同级),并新建index.js
、router.js
、config.js
文件
编写index.js
,实现服务器配置
const express = require("express");
const app = express();
const cors = require("cors");
const bodyParser = require("body-parser");
const router = require("./router")app.use(cors());
app.use(bodyParser.urlencoded({extended:true
}))
app.use("/api",router);
// 监听端口号
app.listen(3000,() =>{console.log(3000)
})
编写config.js
,配置数据库链接配置
const mysql = require("mysql");
const client = mysql.createConnection({host:"localhost",user:"root",password:"123456",database:"ego_shop_one"
})
const sqlClient = (sql,arr,callback) =>{client.query(sql,arr,(error,result)=>{if(error){console.log(error);return;}callback(result);})
}module.exports = sqlClient;
编写router.js
,编写服务器数据操作逻辑
const express = require("express");
const router = express.Router();
const sqlClient = require("./config")
const jwt = require("jsonwebtoken")/*** 注册*/
router.post("/register",(req,res) =>{const {username,password,email} = req.body;const sql = "INSERT INTO user VALUES(null,?,?,?)";const arr = [username,password,email];sqlClient(sql,arr,result=>{if(result.affectedRows > 0){res.send({status:200,msg:"注册成功"})}else{res.send({status:401,msg:"注册失败"})}})
})/*** 登录*/
router.post("/login",(req,res) =>{const {username,password} = req.body;const sql = "SELECT * FROM user WHERE username = ? and password = ?";const arr = [username,password];sqlClient(sql,arr,result =>{if(result.length > 0){let token = jwt.sign({username,id:result[0].id},'soomekeys')res.send({status:200,token,username})}else{res.send({status:401,msg:"登录失败"})}})
})
module.exports = router;
修改package.json
文件,增加快捷运行脚本"dev": "concurrently \"npm run serve\" \"nodemon server/index.js\""
{"name": "my_shopyigou","version": "0.1.0","private": true,"scripts": {"serve": "vue-cli-service serve","build": "vue-cli-service build","lint": "vue-cli-service lint","dev": "concurrently \"npm run serve\" \"nodemon server/index.js\"" },......
}
运行npm run dev
,可同步运行服务器和客户端;使用postman测试后端接口
5.前后端登录注册对接
在src目录下新建api目录,新建api/base.js
、api/index.js
文件
编写api/index.js
,实现请求跳转
import axios from "../utils/request"
import base from "./base"const api = {/*** 注册*/register(params){return axios.post(base.baseUrl + base.register,params)},/*** 登录*/login(params){return axios.post(base.baseUrl + base.login,params)}
}
export default api;
编写api/base.js
const base = {baseUrl:"http://localhost:3000",register:"/api/register",login:"/api/login"
}
export default base;
修改router/permission.js
import router from "./index"
import store from "../store"// 访问路径前判断权限
router.beforeEach((to,from,next) =>{if(to.meta.isLogin){// 获取tokenlet token = store.state.login.user.token;console.log(token);if(token){next();}else{next({name:"Login"})}}else{next();}
})
新建store/modules/login.js
文件
export default{namespaced:true,state:{user:{username:"",token:""}},mutations:{setUser(state,user){state.user = user;}}
}
修改store/index.js
文件
import Vue from 'vue'
import Vuex from 'vuex'
import login from './modules/login'Vue.use(Vuex)export default new Vuex.Store({modules: {login}
})
在src目录下新建utils目录,新建utils/request.js
、utils/init.js
文件
编写utils/request.js
文件
import axios from 'axios'
import qs from "querystring"
import store from "../store"/*** 处理失败的方法*/
const errorHandle = (status,info) =>{switch(status){case 400:console.log("语义有误,当前请求无法被服务器理解。");break;case 401:// token:令牌console.log("服务器认证失败!");break;case 403:console.log("服务器已经理解请求,但是拒绝执行它!");break;case 404:console.log("请检查网络请求地址!");break; case 500:console.log("服务器遇到了一个未曾预料的状态,导致它无法完成对请求的处理");break; case 502:console.log("作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到的请求异常");break;default:console.log(info);break;}
}/*** 创建axios实例对象*/
const instance = axios.create({// 公共配置timeout:5000
})/*** 请求拦截*/
instance.interceptors.request.use((config) => {if(config.method === "post"){config.data = qs.stringify(config.data)}return config},(error) => {return Promise.reject(error);}
);
/*** 响应拦截*/
instance.interceptors.response.use(//完成了(response) => {return response.status === 200 ? Promise.resolve(response) : Promise.reject(response);},(error) => {const {response} = error;errorHandle(response.status,response.info);}
);export default instance
编写utils/init.js
文件
import store from "../store"
if(localStorage.getItem("ego")){store.commit("login/setUser",JSON.parse(localStorage.getItem("ego")))
}
修改main.js
文件,导入使用./utils/init
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './plugins/element.js'
import './router/permission'
import './utils/init'Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App)
}).$mount('#app')
修改LoginView.vue代码,添加实际登录注册请求逻辑
<script>
import { mapMutations } from "vuex";
import api from "../api"
export default {......methods:{...mapMutations("login",["setUser"]),submitForm( formName ){this.$refs[formName].validate((valid) =>{if(valid){if(this.activeTab === "login"){// 提交登录表单逻辑api.login(this.loginForm).then(res =>{// console.log(res.data);if(res.data.status === 200){this.setUser(res.data);localStorage.setItem("ego",JSON.stringify(res.data));this.$router.push('/')}else{const h = this.$createElement;this.$notify({title:"登录失败",message:h("i","用户名密码错误")})}})}else if(this.activeTab === "register"){// 提交注册表单逻辑// console.log(this.registerForm);api.register(this.registerForm).then(res =>{if(res.data.status === 200){const h = this.$createElement;this.$notify({title:"注册成功",message:h("i","请前往登录页面登录")});}else{const h = this.$createElement;this.$notify({title:"注册失败",message:h("i","请重新注册")})}})}else{return;}}})},handleTabClick(tab){this.activeTab = tab.name;}}
}
</script>
运行效果
登录后,会自动跳转至首页且本地缓存中增加token
注册
暂未实现重复用户验证
6.导航栏实现
components目录下新建HeaderNav.vue
,编写导航栏组件
<template><el-menu :default-active="active" mode="horizontal"background-color="#545c64"text-color="#fff"active-text-color="#ffd04b"router ><el-menu-item index="/">首页</el-menu-item><el-menu-item index="/product">商品管理</el-menu-item><el-menu-item index="/params">规格参数</el-menu-item><el-menu-item index="/ad">广告管理</el-menu-item><el-menu-item class="user"><span class="user-name">{{ user.username }}</span><el-button @click="logoutHandle">退出</el-button></el-menu-item></el-menu>
</template><script>
import { mapState,mapMutations } from 'vuex'
export default {data(){return {active:"/"}},computed:{...mapState("login",["user"])},methods:{...mapMutations("login",["setUser"]),logoutHandle(){this.setUser({})localStorage.removeItem('ego')this.$router.push('/login')}}
}
</script><style lang="less" scoped>
.user{float: right !important;margin-right: 20px !important;line-height: 60px !important;.user-name{color:#fff;margin-right: 10px;font-size: 15px;border: 1px solid #fff;border-radius: 50%;width:40px;height: 40px;display: inline-block;line-height: 40px;overflow: hidden;}
}
.set-lang{float: right !important;
}
.el-dropdown-link{color:#fff;
}</style>
修改LayoutView.vue
文件,引入使用导航栏
<template><div><header-nav></header-nav><router-view></router-view></div>
</template><script>
import HeaderNav from "@/components/HeaderNav.vue"
export default {components:{HeaderNav}
}
</script><style>
</style>
运行效果
*.公共样式导入
在src/assets目录下新建css目录,编写common.css
文件
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,label
{margin: 0;padding: 0;
}
body{text-align: center;background: #f1f1f1;}
li{list-style: none;}
a{text-decoration: none;}
input,button,img{border: none;}
.active{color: #409eff;}
修改main.js
文件,导入使用公共样式
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './plugins/element.js'
import './router/permission'
import './utils/init'
import './assets/css/common.css'
Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App)
}).$mount('#app')
7.首页页面实现
在src/view/main目录下,新建HomePage目录,用于存放所有首页视图页面,将HomeView.vue
移动至该目录下
修改src/router目录下的index.js文件,更新HomeView的路径
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/main/HomePage/HomeView.vue'
import Layout from '../views/LayoutView.vue'
......
7.1.左导航栏
在HomePage目录下新建HomeNavView.vue
文件,编写左导航栏组件
<template><div class="index-warp"><div class="index-left"><div class="index-left-block"><h2>全部产品</h2><div v-for="(product,index) in productList" :key="index"><h3>{{ product.title }}</h3><ul><li v-for="(item,index) in product.list" :key="index"><a :href="item.url">{{ item.name }}</a><span class="hot-tag" v-if="item.hot">HOT</span></li></ul></div></div></div></div>
</template><script>
export default {data(){return{productList:[{title:"手机应用类",list:[{id:1,name:"baidu",url:"www.baidu.com",hot:true},{id:2,name:"baidu2",url:"www.baidu.com",hot:false},{id:3,name:"baidu3",url:"www.baidu.com",hot:false}]},{title:"PC产品类",list:[{id:1,name:"华为",url:"www.baidu.com",hot:true},{id:2,name:"mac",url:"www.baidu.com",hot:false},{id:3,name:"微软",url:"www.baidu.com",hot:false}]}]}}
}
</script><style scoped lang="less">.index-wrap{width: 1200px;margin:0 auto;overflow: hidden;
}.index-left{float: left;width: 300px;text-align: left;
}
.index-right{float:left;width:900px;
}
.index-left-block{margin: 15px;background: #fff;box-shadow: 0 0 1px #ddd;
}.index-left-block .hr{margin-bottom: 20px;height: 1px;width: 100%;background: #ddd;
}
.index-left-block h2{background: #4fc08d;color: #fff;padding: 10px 15px;margin-bottom: 20px;
}
.index-left-block h3{padding: 0 15px 5px 15px;font-weight: bold;color: #222;
}.index-left-block ul{padding: 10px 15px;
}.index-left-block li{padding:5px;a{color:#222;}
}
.index-board-list{overflow:hidden;margin-top: 15px;
}
.index-board-item{float: left;width: 400px;background: #fff;box-shadow: 0 0 1px #ddd;padding: 20px;margin-right: 20px;margin-bottom: 20px;
}.index-board-item-inner{min-height: 125px;padding-left: 120px;
}
/**
.index-board-openproduct .index-board-item-inner{background: url(../assets/images/1.png) no-repeat;
}
.index-board-logo .index-board-item-inner{background: url(../assets/images/2.jpeg) no-repeat;
}
.index-board-golife .index-board-item-inner{background: url(../assets/images/1.png) no-repeat;
}
.index-board-heigh .index-board-item-inner{background: url(../assets/images/2.jpeg) no-repeat;
}*/
.index-board-item h2{font-size: 18px;font-weight: bold;color: #000;margin-bottom: 15px;
}.line-last{margin-right: 0;
}.index-board-button{margin-top: 20px;
}
.lastest-news{min-height: 350px;
}.hot-tag{background: red;color: #fff;
}
.new-item{display: inline-block;width: 230px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;
}
.swiperimg{width: 100%;height: 350px;
}
.swiper-size{margin-block: 15px;
}
.button{background: #4fc08d;color:#fff;display: inline-block;padding: 10px 20px;cursor: pointer;
}
</style>
修改HomeView.vue
文件,导入使用左导航栏组件
<template><div class="home"><home-nav></home-nav></div>
</template><script>
import HomeNav from "./HomeNavView.vue"
export default {name: 'HomeView',components: {HomeNav}
}
</script>
运行效果
7.2.轮播图
安装轮播图插件:cnpm install swiper@5.x vue-awesome-swiper@3.1.3 --save --no-fund
注:使用新版本可以使用npm,但要下载使用旧版本,需使用cnpm
修改main.js
文件,引入使用轮播插件
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './plugins/element.js'
import './router/permission'
import './utils/init'
import './assets/css/common.css'
// 引入轮播图插件
import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
// 使用VueAwesomeSwiper
Vue.use(VueAwesomeSwiper)
Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App)
}).$mount('#app')
在src/assets目录下新建slideShow目录,存放需要轮播的图片
在HomePage目录下新建HomeSwiper.vue
文件,实现轮播组件
<template><div class="swiper-size"><swiper :options="swiperOption"><swiper-slide v-for="(imgSrc,index) in bannerImg" :key="index"><img class="swiperimg" :src="imgSrc" alt=""/></swiper-slide><!-- 轮播图下方轮播节点 --><div class="swiper-pagination" slot="pagination"></div><!-- 左右切换按钮 --><div class="swiper-button-prev" slot="button-prev"></div><div class="swiper-button-next" slot="button-next"></div></swiper></div>
</template><script>
export default {data(){return{swiperOption:{pagination:{el:".swiper-pagination",},autoplay:true,navigation:{nextEl:".swiper-button-next",prevEl:".swiper-button-prev"},loop:true},bannerImg:[require("@/assets/slideShow/1.png"),require("@/assets/slideShow/2.jpg"),require("@/assets/slideShow/3.jpg"),require("@/assets/slideShow/4.jpg"),]}}
}
</script><style scoped lang="less">
.swiper-size{margin-top: 15px;
}
.swiperimg{width: 100%;height: 450px;
}
</style>
修改HomeView.vue
文件,引入轮播组件
<template><div class="index-wrap"><div class="index-left"><home-nav></home-nav></div><div class="index-right"><home-swiper></home-swiper></div></div>
</template><script>
import HomeNav from "./HomeNavView.vue"
import HomeSwiper from "./HomeSwiper.vue"
export default {name: 'HomeView',components: {HomeNav,HomeSwiper}
}
</script>
<style lang="less" scoped>
.index-wrap{width: 1500px;margin:0 auto;overflow: hidden;
}.index-left{float: left;width: 300px;text-align: left;
}
.index-right{float:left;width:1200px;
}
</style>
运行效果
7.2.1.封装组件
在components目录下,新建SwiperView.vue
文件,用做通用轮播图组件
编辑SwiperView.vue
文件,跟HomeSwiper.vue
文件内容差不多,只是图片信息改用props接收
<template><div class="swiper-size"><swiper :options="swiperOption"><swiper-slide v-for="(imgSrc,index) in bannerSwiperImg" :key="index"><img class="swiper-img" :src="imgSrc" alt=""/></swiper-slide><!-- 轮播图下方轮播节点 --><div class="swiper-pagination" slot="pagination"></div><!-- 左右切换按钮 --><div class="swiper-button-prev" slot="button-prev"></div><div class="swiper-button-next" slot="button-next"></div></swiper></div>
</template><script>
export default {data(){return {// 轮播图选项swiperOption:{pagination:{el:".swiper-pagination"},// 是否自动轮播autoplay:true,// 切换组件navigation:{nextEl:".swiper-button-next",prevEl:".swiper-button-prev"},// 轮播进度loop:true}}},props:{bannerSwiperImg:{type:Array,default(){// 没有传入数据,则默认使用一下图片return [require("@/assets/slideShow/1.png"),]}}},}
</script><style scoped>
.swiper-size{margin-top: 15px;
}
.swiper-img{width: 100%;height: 450px;
}
</style>
修改HomeView.vue
文件,改为使用通用组件
<template><div class="index-wrap"><div class="index-left"><home-nav-view /></div><div class="index-right"><swiper-view :bannerSwiperImg="bannerSwiperImg"/></div></div>
</template><script>
import SwiperView from '@/components/SwiperView.vue'
import HomeNavView from './HomeNavView.vue'export default {name: 'HomeView',data(){return {bannerSwiperImg:[require("@/assets/slideShow/2.jpg"),require("@/assets/slideShow/1.png"),require("@/assets/slideShow/4.jpg"),require("@/assets/slideShow/3.jpg"),]}},components: {HomeNavView,SwiperView},}
</script><style scoped>
.index-wrap{width: 1500px;margin:0 auto;overflow: hidden;
}.index-left{float: left;width: 300px;text-align: left;
}
.index-right{float:left;width:1200px;
}
</style>
运行效果
7.3.信息列表
在HomePage目录下新建HomeProductList.vue
文件
<template><div class="index-board-list"><div class="index-board-item" v-for="(item,index) in buyData" :key="index":class="['index-board-' + item.url,{'line-last':index%2 !== 0}]"><div class="index-board-item-inner"><h2>{{ item.title }}</h2><p>{{ item.desc }}</p><div class="index-board-button"><router-link to="/details" class="button">立即购买</router-link></div></div></div></div>
</template><script>
export default {data(){return{buyData:[{title:"开放产品",desc:"开放产品描述",url:"openproduct"},{title:"品牌营销",desc:"品牌营销描述",url:"logo"},{title:"电子产品",desc:"电子产品描述",url:"golife"},{title:"酒水产品",desc:"酒水产品描述",url:"heigh"},]}}
}
</script><style scoped lang="less">
.index-board-list{overflow:hidden;margin-top: 15px;
}
.index-board-item{float: left;width: 550px;background: #fff;box-shadow: 0 0 1px #ddd;padding: 20px;margin-right: 20px;margin-bottom: 20px;
}.index-board-item-inner{min-height: 125px;padding-left: 120px;
}.index-board-openproduct .index-board-item-inner{background: url(@/assets/images/1.jpg) no-repeat;background-size: 120px 120px;
}
.index-board-logo .index-board-item-inner{background: url(@/assets/images/2.jpeg) no-repeat;background-size: 120px 120px;
}
.index-board-golife .index-board-item-inner{background: url(@/assets/images/3.jpg) no-repeat;background-size: 120px 120px;
}
.index-board-heigh .index-board-item-inner{background: url(@/assets/images/4.jpg) no-repeat;background-size: 120px 120px;
}.index-board-item h2{font-size: 18px;font-weight: bold;color: #000;margin-bottom: 15px;
}.line-last{margin-right: 0;
}.index-board-button{margin-top: 20px;
}
.lastest-news{min-height: 350px;
}
.new-item{display: inline-block;width: 230px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;
}
.button{background: #4fc08d;color:#fff;display: inline-block;padding: 10px 20px;cursor: pointer;
}
</style>
百度找几张图标,放在src/assets/images目录下
修改HomeView.vue
文件,添加使用HomeProductList组件
<template><div class="index-wrap"><div class="index-left"><home-nav></home-nav></div><div class="index-right"><home-swiper></home-swiper><home-product-list></home-product-list></div></div>
</template><script>
import HomeNav from "./HomeNavView.vue"
import HomeSwiper from "./HomeSwiper.vue"
import HomeProductList from "./HomeProductList.vue"
export default {name: 'HomeView',components: {HomeNav,HomeSwiper,HomeProductList}
}
</script>
<style lang="less" scoped>
.index-wrap{width: 1500px;margin:0 auto;overflow: hidden;
}.index-left{float: left;width: 300px;text-align: left;
}
.index-right{float:left;width:1200px;
}
</style>
运行效果
7.4.详情页实现
在src/views/main/HomePage目录下新建sub目录,用于存放子页面文件
新建DetailsView.vue
,编写详情页信息
<template><div class="detail-wrap"><div class="detail-left"><div class="product-board"><img src="@/assets/images/1.jpg" alt=""><ul><router-link tag="li" active-class="active"v-for="(item,index) in detailsNav" :key="index" :to="'/details/' + item.id">{{ item.title }}</router-link></ul></div></div><div class="detail-right"><router-view></router-view></div></div>
</template><script>
export default {data(){return {detailsNav:[{title:"开放产品",id:"openproduct"},{title:"品牌营销",id:"logo"},{title:"电子产品",id:"golife"},{title:"酒水产品",id:"heigh"},]}}
}
</script><style >
.detail-wrap{width: 1200px;margin: 0 auto;overflow: hidden;padding-top: 20px;
}
.detail-left {float: left;width: 200px;text-align: center;
}
.detail-right{float: left;width: 980px;margin-left: 20px;
}
.product-board{background: #fff;padding: 20px 0;
}
.product-board img{width: 120px;height: 120px;
}
.product-board ul{margin-top: 20px;
}
.product-board li{text-align: left;padding: 10px 15px;cursor: pointer;
}
.product-board li.active,
.product-board li:hover{background: #4fc08d;color:#fff;
}
.product-board li a{display: block;
}
/* 下方为右边区域子页面样式 */
.sales-board{background: #fff;
}
.sales-board-intro h2{font-size: 20px;padding: 20px;
}
.sales-board-intro p{background: #f7fcff;padding: 10px 20px;color: #999;line-height: 1.8;
}
.sales-board-form{padding: 10px 20px;font-size: 14px;
}
.sales-board-line{clear: both;padding-bottom: 20px
}
.sales-board-line-left{display: inline-block;margin-right: 10px;
}
.sales-board-line-right {display: inline-block;width: 25%;
}
.sales-board-des {border-top: 20px solid #fff;background: #fff;padding: 15px 20px;
}
.sales-board-des p {line-height: 1.6;
}
.sales-board-des h2{font-size: 20px;padding-bottom: 15px;
}
.sales-board-des h3{font-size: 18px;font-weight: bold;padding: 20px 0 10px 0;
}
.sales-board-des li{padding: 5px 0;
}
.sales-board-table{width:100%;margin-top: 20px;
}
.sales-board-table th{border: 1px solid #4fc08d;color:#fff;
}
.sales-board-table td{border: 1px solid #f0f2f5;padding:15px;
}
</style>
新建Openproduct.vue
、Golife.vue
、Heigh.vue
、Logo.vue
四个模块文件
编辑Openproduct.vue
<template>
<div class="open"><div class="sales-board"><div class="sales-board-intro"><h2>开放产品</h2><p>中国和拉美虽然相距遥远,但共同的梦想和追求将双方紧紧联系在一起。<br>在遥远的南美国家哥伦比亚,一场与中国的 “地铁之约” 正在精彩上演。<br>两年来,哥伦比亚首都波哥大地铁一号线项目建设热火朝天,50 名当地青年学员更是不远万里,先后来到中国西安进行专业培训。<br>这不仅仅是一次学习之旅,更是中拉基建合作的生动写照。<br>中国与哥伦比亚携手,共同为城市交通注入新活力。从规划到建设,每一个环节都凝聚着双方的智慧和努力。</p><div class="sales-board-form"><div class="sales-board-line-left">购买数量:</div><div class="sales-board-line-right"><!-- 购买数量组件 --><Counter :counterObj="counterObj"></Counter></div></div><div class="sales-board-form"><div class="sales-board-line-left">产品类型:</div><!-- 产品类型选择组件 --><Types :selecterData="selecterData"/></div><div class="sales-board-form"><div class="sales-board-line-left">有效时间:</div><!-- 有效时间选择组件 --><Timer :timerData="timerData"/></div></div><div class="sales-board-des"><h2>产品说明</h2><p>这是产品说明...这是产品说明......这是产品说明...这是产品说明......这是产品说明...这是产品说明......</p><h2>用户行为指标</h2><ul><li>用户行为指标...用户行为指标...用户行为指标...用户行为指标...</li><li>用户行为指标1</li><li>用户行为指标2</li><li>用户行为指标3</li><li>用户行为指标4</li></ul><h3>浏览网站方式</h3><ul><li>浏览网站方式1</li><li>浏览网站方式2</li><li>浏览网站方式3</li><li>浏览网站方式4</li><li>浏览网站方式5</li></ul></div></div>
</div>
</template><script>
import Counter from '@/components/HomePage/counter'
import Types from '@/components/HomePage/types'
import Timer from '@/components/HomePage/timer'
export default {data(){return {counterObj:{min:1,max:20},selecterData:[{value:"经典型",id:1},{value:"加强型",id:2},{value:"至尊豪华型",id:3},],timerData:[{value:'一个月',id:1},{value:'三个月',id:3},{value:'六个月',id:6},{value:'一年',id:12},]}},components:{Counter,Types,Timer}
}
</script><style scoped>
.open{text-align: left;
}
.buy-dialog-title{font-size: 16px;font-weight: bold;
}
.buy-dialog-btn{margin-top: 20px;
}
.buy-dialog-table{width:100%;margin-bottom: 20px;
}
.buy-dialog-table td,
.buy-dialog-table th{border: 1px solid #e3e3e3;text-align: center;padding: 5px 0;
}
.buy-dialog-table th{background: #4fc08d;color: #fff;border: 1px solid #4fc08d;
}
.button{background: #4fc08d;color: #fff;display: inline-block;padding: 10px 20px;cursor: pointer;
}
</style>
修改router/index.js文件,增加详情页路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/main/HomePage/HomeView.vue'
import Layout from '../views/LayoutView.vue'Vue.use(VueRouter)const routes = [{path: '/',name: 'Layout',component: Layout,children:[{path: '',name: 'home',component: HomeView,meta:{isLogin:true}},{path:"product",name:"Product",component:() => import("../views/main/ProductPage"),meta:{isLogin:true}},{path:"params",name:"Params",component:() => import("../views/main/ParamsView.vue"),meta:{isLogin:true}},{path:"ad",name:"ADCategory",component:() => import("../views/main/ADCategory.vue"),meta:{isLogin:true}},{path:"details",name:"Details",component:() => import("../views/main/HomePage/sub/DetailsView.vue"),children:[{path:"openproduct",name:"OpenProduct",component:() => import("../views/main/HomePage/sub/Openproduct.vue")},{path:"golife",name:"Golife",component:() => import("../views/main/HomePage/sub/Golife.vue")},{path:"logo",name:"Logo",component:() => import("../views/main/HomePage/sub/Logo.vue")},{path:"heigh",name:"Heigh",component:() => import("../views/main/HomePage/sub/Heigh.vue")},],meta:{isLogin:true}}]},{path:'/login',name:'Login',component:() => import("../views/LoginView.vue")}
]const router = new VueRouter({routes,// 去除路径上的#号mode:"history"
})export default router
修改HomeProductList.vue
文件,增加具体跳转路由信息
<template><div class="index-board-list"><div class="index-board-item" v-for="(item,index) in buyData" :key="index":class="['index-board-' + item.url,{'line-last':index%2 !== 0}]"><div class="index-board-item-inner"><h2>{{ item.title }}</h2><p>{{ item.desc }}</p><div class="index-board-button"><!-- 拼接具体跳转位置 --><router-link :to="'/details/' + item.url" class="button">立即购买</router-link></div></div></div></div>
</template>
7.3.1.购买数量组件
在src/components目录下新建HomePage目录,新建counter.vue
文件实现购买数量组件
<template><div class="counter-component"><div class="counter-btn" @click="minHandle">-</div><div class="counter-show"><input type="text" v-model="counter" @keyup="innerHeight"></div><div class="counter-btn" @click="addHandle">+</div></div>
</template><script>
export default {data(){return {counter : 1}},props:{counterObj:{type:Object,default(){return{ min:1,max:1}}}},methods:{minHandle(){if(this.counter <= this.counterObj.min){return;}this.counter--;},addHandle(){if(this.counter >= this.counterObj.max){return;}this.counter++;},innerHeight(){var fix;if(typeof this.counter === "string"){fix = Number(this.counter.replace(/\D/g,""));}else{// 如果用户输入的是字符串,则将最小值赋值给fixfix = this.counterObj.min;}if(fix <= this.counterObj.min){fix = this.counterObj.min;}if(fix > this.counterObj.max){fix = this.counterObj.max;}this.counter = fix;}}
}
</script><style scoped>
.counter-component{position: relative;display: inline-block;overflow: hidden;vertical-align: middle;
}
.counter-show{float: left;width: 50px;
}
.counter-show input{width: 50px;border: none;text-align: center;border-top: 1px solid #e3e3e3;border-bottom: 1px solid #e3e3e3;height: 23px;line-height: 23px;
}
.counter-btn{border: 1px solid #e3e3e3;float: left;height: 25px;line-height: 25px;width: 25px;text-align: center;cursor: pointer;
}
.counter-btn:hover{border-color: #4fc08d;background: #4fc08d;color: #fff;
}
</style>
7.4.2.产品类型选择组件
在src/components/HomePage目录下,新建types.vue
文件实现产品类型选择组件
<template><div class="selection-component"><div class="selection-show" @click="showListHendle"> <span>{{ selecterData[currentIndex].value }}</span><div class="arrow"></div></div><div class="selection-list" v-show="isOpen"><ul><li v-for="(item,index) in selecterData" :key="index"@click="selectHandle(index)">{{ item.value }}</li></ul></div></div>
</template><script>
export default {data(){return{isOpen:false,currentIndex:0,}},props:{selecterData:{type:Array,default(){return [{value:"test",id:1}];}}},methods:{showListHendle(){this.isOpen = !this.isOpen;},selectHandle(index){this.currentIndex = index;this.isOpen = false;}}
}
</script><style scoped>
.selection-component{position: relative;display: inline-block;
}
.selection-show{border: 1px solid #e3e3e3;padding: 0 20px 0 10px;display: inline-block;position: relative;cursor: pointer;height: 25px;line-height: 25px;border-radius: 3px;background: #fff;
}
.selection-show .arrow{display: inline-block;border-left: 4px solid transparent;border-right: 4px solid transparent;border-top: 5px solid transparent;width: 0;height: 0;margin-top: -1px;margin-left: 6px;margin-right: -14px;vertical-align: middle;
}
.selection-list{display: inline-block;position: absolute;left: 0;top: 25px;width: 100%;background: #fff;border-top: 1px solid #e3e3e3;border-bottom: 1px solid #e3e3e3;z-index: 5;
}
.selection-list li{padding: 5px 15px 5px 10px;border-left: 1px solid #e3e3e3;border-right: 1px solid #e3e3e3;cursor: pointer;background: #fff;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;
}
.selection-list li:hover{background: #e3e3e3;
}
</style>
7.4.3.有效时间选择组件
在src/components/HomePage目录下,新建timer.vue
文件实现产品类型选择组件
<template><div class="chooser-component"><ul class="chooser-list"><li @click="timerHandle(index)" :class="{active:nowIndex === index}" v-for="(item,index) in timerData" :key="index">{{ item.value }}</li></ul></div>
</template><script>
export default {data(){return {nowIndex:0,}},props:{timerData:{type:Array,default(){return [{value:"test",id:1}];}}},methods:{timerHandle(index){this.nowIndex = index}}
}
</script><style scoped>
.chooser-component{position: relative;display: inline-block;
}
.chooser-list li{display: inline-block;border:1px solid #e3e3e3;height: 25px;line-height: 25px;padding: 0 8px;margin-right: 5px;border-radius: 3px;text-align: center;cursor: pointer;
}
.chooser-list li.active{border-color: #4fc08d;background: #4fc08d;color: #fff;
}
</style>
另外三个模块暂时忽略
运行效果
8.商品管理页实现
src/views/main目录下新建ProductPage目录,用于存放商品管理页面的相关组件
ProductPage目录下,新建index.vue
文件,作为商品管理页主入口
<template><div class="product">商品管理</div>
</template><script>
export default {
}
</script><style>
.product{width: 1200px;margin: 0 auto;margin-top: 20px;
}
</style>
修改product的路由配置
{path:'product',name:'Product',component:() => import('../views/main/ProductPage'),meta:{isLogin:true}},
8.1.服务器数据接口
修改server/router.js
文件,增加商品管理相关操作接口
const url = require("url")
const fs = require("fs")
const multer = require("multer")/*** 商品查询*/
router.get("/backend/item/selectTbItemAllByPage",(req,res)=>{// 分页const page = url.parse(req.url,true).query.page || 1;const sql = "select * from project order by id desc limit 10 offset " + (page - 1) * 10;sqlClient(sql,null,result=>{if(result.length > 0){res.send({status:200,result})}else{res.send({status:401,msg:"暂无数据"})}})
})
/*** 商品总数*/
router.get("/total",(req,res)=>{const sql = "SELECT COUNT(*) AS count FROM project where id";sqlClient(sql,null,result =>{if(result.length > 0){res.send({status:200,result})}else{res.send({status:500,msg:"暂无更多数据"})}})
})/*** 模糊查询*/
router.get("/search",(req,res)=>{const search = url.parse(req.url,true).query.search;const sql = "SELECT * FROM project WHERE concat(`title`,`sellPoint`,`descs`) LIKE '%" + search + "%'";sqlClient(sql,null,result =>{if(result.length > 0){res.send({ status:200,result})}else{res.send({status:500,msg:"暂无数据"})}})
})/*** 类目选择*/
router.get("/backend/itemCategory/selectItemCategoryByParentId",(req,res)=>{const id = url.parse(req.url,true).query.id || 1;const sql = "SELECT cid,pid,name FROM category WHERE pid = ?";const arr = [id]sqlClient(sql,arr,result =>{if(result.length > 0){res.send({status:200,result})}else{res.send({status:500,msg:"暂无数据"})}})
})/*** 上传图片*/
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',Buffer.from(file.originalname, "latin1").toString("utf8"));console.log('文件大小:%s',file.size);console.log('文件保存路径:%s',Buffer.from(file.path, "latin1").toString("utf8"));res.json({res_code:'0',name:Buffer.from(file.originalname, "latin1").toString("utf8"),url:Buffer.from(file.path, "latin1").toString("utf8")});
})/*** 添加商品*/
router.get("/backend/item/insertTbItem",(req,res)=>{const cid = url.parse(req.url,true).query.cid || "";const title = url.parse(req.url,true).query.title || "";const sellPoint = url.parse(req.url,true).query.sellPoint || "";const price = url.parse(req.url,true).query.price || "";const num = url.parse(req.url,true).query.num || "";const image = url.parse(req.url,true).query.image || "";const desc = url.parse(req.url,true).query.desc || "";const sql = "INSERT INTO project(`title`,`image`,`sellPoint`,`price`,`cid`,`num`,`status`,`descs`) VALUES(?,?,?,?,?,?,1,?)"const arr = [title,image,sellPoint,price,cid,num,desc];sqlClient(sql,arr,result=>{if(result.affectedRows > 0){res.send({status:200,msg:"添加成功"})}else{res.send({status:500,msg:"添加失败"})}})
})/*** 商品删除*/
router.get("/backend/item/deleteItemById",(req,res) =>{const id = url.parse(req.url,true).query.id;const sql = "DELETE FROM project WHERE id = ?";const arr = [id];sqlClient(sql,arr,result =>{if(result.affectedRows > 0){res.send({status:200,msg:"删除成功"})}else{res.send({status:500,msg:"删除失败"})}})
})/*** 预更新*/
router.get("/backend/item/preUpdateItem",(req,res) =>{const id = url.parse(req.url,true).query.id;const sql = "SELECT * FROM project WHERE id = ?";sqlClient(sql,[id],result =>{if(result.length > 0){res.send({status:200,result})}else{res.send({status:500,msg:"预更新失败"})}})
})/*** 编辑商品信息*/
router.get("/backend/item/updateTbItem",(req,res) =>{const cid = url.parse(req.url,true).query.cid || "";const title = url.parse(req.url,true).query.title || "";const sellPoint = url.parse(req.url,true).query.sellPoint || "";const price = url.parse(req.url,true).query.price || "";const num = url.parse(req.url,true).query.num || "";const image = url.parse(req.url,true).query.image || "";const desc = url.parse(req.url,true).query.desc || "";const id = url.parse(req.url,true).query.id;const sql = "UPDATE project set title=?,sellPoint=?,cid=?,price=?,descs=?,image=?,num=? WHERE id=?";const arr = [title,sellPoint,cid,price,desc,image,num,id]sqlClient(sql,arr,(result) =>{if(result.affectedRows > 0){res.send({status:200,msg:"修改成功"})}else{res.send({status:500,msg:"修改失败"})}})
})
修改server/router.js
文件,增加静态文件位置
const express = require("express");
const app = express();
const cors = require("cors");
const bodyParser = require("body-parser");
const router = require("./router")app.use(cors());
app.use(bodyParser.urlencoded({extended:true
}))app.use("/api",router);
app.use(express.static("upload"))
// 监听端口号
app.listen(3000,() =>{console.log(3000)
})
在src/api/base.js
文件中增加对应信息
const base = {baseUrl:"http://localhost:3000",register:"/api/register", // 注册login:"/api/login", // 登录selectTbItemAllByPage:"/api/backend/item/selectTbItemAllByPage", // 商品列表total:"/api/total", // 商品总数search:"/api/search", // 模糊查询selectItemCategoryByParentId:"/api/backend/itemCategory/selectItemCategoryByParentId", // 类目选择insertTbItem:"/api/backend/item/insertTbItem", // 商品添加deleteItemById:"/api/backend/item/deleteItemById", //商品删除preUpdateItem:"/api/backend/item/preUpdateItem", // 预更新updateTbItem:"/api/backend/item/updateTbItem", // 修改商品
}
export default base;
修改src/api/index.js
文件,增加商品查询api
/*** 商品列表*/selectTbItemAllByPage(params){return axios.get(base.baseUrl + base.selectTbItemAllByPage,{params})},/*** 商品数量*/total(){return axios.get(base.baseUrl + base.total)},/*** 模糊查询*/search(params){return axios.get(base.baseUrl + base.search,{params})},/*** 类目选择*/selectItemCategoryByParentId(params){return axios.get(base.baseUrl + base.selectItemCategoryByParentId,{params})},/*** 添加商品*/insertTbItem(params){return axios.get(base.baseUrl + base.insertTbItem,{params})},/*** 商品删除*/deleteItemById(params){return axios.get(base.baseUrl + base.deleteItemById,{params})},/*** 预更新*/preUpdateItem(params){return axios.get(base.baseUrl + base.preUpdateItem,{params})},/*** 修改商品*/updateTbItem(params){return axios.get(base.baseUrl + base.updateTbItem,{params})}
8.2.eventbus工具类实现
src/utils目录下新建eventbut.js
文件,实现EventBus,方便组件之间的数据传递
import Vue from "vue"const EventBus = new Vue();
Object.defineProperties(Vue.prototype,{$bus:{get(){return EventBus;}}
})
修改main.js
文件,【这里顺便加api的引用,方便后面调用api】
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './plugins/element.js'
import './router/permission'
import './utils/init'
import './assets/css/common.css'
// 引入轮播图插件
import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
// 引入api
import api from "./api"
// 引入eventbus
import "./utils/eventbus"Vue.prototype.$api = api
// 使用VueAwesomeSwiper
Vue.use(VueAwesomeSwiper)
Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App)
}).$mount('#app')
8.3.商品列表信息
src/views/main/ProductPage目录下,新建ProductList.vue
文件,实现商品列表展示
<template><div><el-table :data="tableData"><el-table-column show-overflow-tooltip prop="id" label="产品id" width="100"></el-table-column><el-table-column show-overflow-tooltip prop="title" label="产品名称" width="150"></el-table-column><el-table-column show-overflow-tooltip prop="image" label="产品图片" width="100"></el-table-column><el-table-column show-overflow-tooltip prop="sellPoint" label="产品卖点" width="300"></el-table-column><el-table-column show-overflow-tooltip prop="price" label="产品价格" width="100"></el-table-column><el-table-column show-overflow-tooltip prop="num" label="产品数量" width="100"></el-table-column><el-table-column show-overflow-tooltip prop="descs" label="产品描述"><template v-slot="scope"><div v-html="scope.row.descs"></div></template></el-table-column><el-table-column label="操作"><template v-slot="scope"><el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button><el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button></template></el-table-column></el-table></div>
</template><script>
export default {data(){return {tableData:[]}},mounted(){this.http(1);// 接收分页组件传递的数据this.$bus.$on("page",page =>{this.http(page)})// 接收搜索框组件传递的数据this.$bus.$on("searchData",data=>{this.tableData = data})this.$bus.$on("refresh",flag =>{this.http(1)})},methods:{http(page){this.$api.selectTbItemAllByPage({page}).then(res =>{// 获取对应页的商品数据if(res.data.status === 200){this.tableData =res.data.result}})},// 编辑按钮handleEdit(index,row){this.$bus.$emit("onEditorEvent",row);},// 删除按钮handleDelete(index,row){this.$confirm("此操作会永久删除该数据,是否继续?","提示",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning",}).then(() =>{this.$api.deleteItemById({ id:row.id }).then(res =>{if(res.data.status === 200){this.$message({type:"success",message:"删除成功"})this.http(1);}else{this.$message({type:"error",message:"删除失败"})}})}).catch(() => {this.$message({type:"info",message:"已取消删除"})})},}
}
</script><style scoped></style>
修改index.vue文件,增加使用列表组件
<template><div class="product"><ProductList/></div></template><script>import ProductList from './ProductList.vue'export default {components:{ProductList}}</script><style>.product{width: 1200px;margin: 0 auto;margin-top: 20px;}</style>
运行效果
8.4.搜索框
ProductPage目录下新建ProductHeader.vue
文件
<template><div class="head"><el-form ref="searchForm" :model="search" @submit.native.prevent><el-form-item><el-input v-model="search.content" @keyup.enter.native="onSubmitSearch"/></el-form-item><el-form-item><el-button type="primary" @click="onSubmitSearch">查询</el-button></el-form-item><el-form-item><el-button type="primary" @click="addFormHandle">添加</el-button></el-form-item></el-form></div>
</template><script>
export default {data(){return {search:{content:""}}},methods:{onSubmitSearch(){// 查询this.$api.search({search:this.search.content}).then(res =>{// 将查询到的数据传递给接收对象this.$bus.$emit('searchData',res.data.result)}).catch(error =>{console.log(error);})},addFormHandle(){// 打开添加商品窗口this.$bus.$emit("onAddEvent",true)}}
}
</script><style scoped lang="less">
.el-form{overflow: hidden;clear: both;.el-form-item{float: left;margin-right: 10px;.el-input{width:1030px;}}
}
.head{margin-top: 20px;width: 100%;
}
</style>
修改index.vue文件,增加使用搜索框组件
<template><div class="product"><ProductHeader/><ProductList/></div></template><script>import ProductList from './ProductList.vue'import ProductHeader from './ProductHeader.vue'export default {components:{ProductList,ProductHeader}}</script><style>.product{width: 1200px;margin: 0 auto;margin-top: 20px;}</style>
运行效果
8.5.分页
ProductPage目录下新建ProductPagination.vue
文件
<template><!-- 分页 --><div class="pagination-container"><el-paginationlayout="prev,pager,next,jumper":current-page.sync="currentPage"@current-change="hangleCurrentChange":total="total" @size-change="handleSizechange"></el-pagination></div>
</template><script>
export default {data(){return {currentPage:1,total:0}},methods:{handleSizechange(){},hangleCurrentChange(val){this.$bus.$emit("page",val)}},mounted(){this.$api.total().then(res =>{if(res.data.status === 200){this.total = res.data.result[0]["count"]}})}
}
</script><style scoped>
.pagination-container{margin-top : 20px;
}
</style>
修改index.vue文件,增加使用分页组件
<template><div class="product"><product-header></product-header><product-list/><product-pagination/></div>
</template><script>
import ProductHeader from './ProductHeader.vue'
import ProductList from './ProductList'
import ProductPagination from './ProductPagination.vue'
export default {components:{ProductList,ProductPagination,ProductHeader}
}
</script><style>
.product{width: 1200px;margin: 0 auto;margin-top: 20px;
}
</style>
运行效果
8.6.添加商品窗口
ProductPage目录下新建ProductAdd.vue
、PriductUpload.vue
、ProductTree.vue
、ProductWangEditor.vue
文件
编写ProductAdd.vue
文件
<template><el-dialog title="添加产品":visible.sync="diaologAddVisible"width="60%":before-close="handleClose"><el-form label-width="80px":model="addForm"ref="addForm" ><el-form-item label="商品类目:"><span class="location tree">{{ treeData.name }}</span><el-button type="primary" class="location" @click="dialogCategoryHandle">类目选择</el-button><el-dialogwidth="50%"append-to-bodytitle="类目选择":visible.sync="dialogCategoryVisible"><product-tree @onTree="getTreeData"></product-tree><span slot="footer" class="dialog-footer"><el-button type="primary" @click="dialogCategoryClose">确认</el-button></span></el-dialog></el-form-item><el-form-item label="商品名称:"><el-input v-model="addForm.name"></el-input></el-form-item><el-form-item label="商品卖点:"><el-input v-model="addForm.sellPoint"></el-input></el-form-item><el-form-item label="商品价格:"><el-input v-model="addForm.price"></el-input></el-form-item><el-form-item label="商品数量:"><el-input v-model="addForm.num"></el-input></el-form-item><el-form-item label="商品图片:"><img class="upload-img" :src="uploadData.url" alt=""><el-button type="primary" class="location" @click="dialogUploadHandle">上传图片</el-button><el-dialogwidth="50%"append-to-bodytitle="图片上传":visible.sync="dialogUploadVisible"><priduct-upload @onUpload="getOnUpload"></priduct-upload><span slot="footer" class="dialog-footer"><el-button type="primary" @click="dialogUploadClose">确认</el-button></span></el-dialog></el-form-item><el-form-item label="商品描述:"><ProductWangEditor @onEditor="getEditor"></ProductWangEditor></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="addFormClose">取消</el-button><el-button type="primary" @click="addProductHandle">确认</el-button></span></el-dialog>
</template><script>
import PriductUpload from './PriductUpload.vue';
import ProductTree from './ProductTree.vue';
import ProductWangEditor from './ProductWangEditor.vue';
export default{components:{ProductTree,PriductUpload,ProductWangEditor},data(){return {diaologAddVisible : false,dialogCategoryVisible:false,dialogUploadVisible:false,addForm:{name:"",sellPoint:"",price:"",num:"",},treeData:{}, // 类目选择 uploadData:{}, // 存储图片editorData:"",}},mounted(){this.$bus.$on("onAddEvent",flag =>{this.diaologAddVisible = flag})},methods:{handleClose(done){this.$confirm("确认关闭").then(_ =>{done();}).catch(_ =>{});},addFormClose(){this.diaologAddVisible = false},dialogCategoryHandle(){this.dialogCategoryVisible = true},dialogCategoryClose(){this.dialogCategoryVisible = false},dialogUploadHandle(){this.dialogUploadVisible = true},dialogUploadClose(){this.dialogUploadVisible = false},// 读取类目选择数据getTreeData(data){// console.log(data);this.treeData = data;},// 读取图片地址getOnUpload(data){if(data.url){data.url = data.url.replace("upload","http://localhost:3000")}this.uploadData = data;},//接收富文本数据getEditor(data){this.editorData = data;},// 添加产品addProductHandle(){this.$api.insertTbItem({cid:this.treeData.cid,title:this.addForm.name,sellPoint:this.addForm.sellPoint,price:this.addForm.price,num:this.addForm.num,desc:this.editorData,image:this.uploadData.url}).then(res =>{if(res.data.status === 200){this.diaologAddVisible = false;this.$bus.$emit("refresh",true)}}).catch(error=>{console.log(error);})}}
}
</script><style scoped>
.location{float: left;
}
.tree{margin-right: 20px;
}
.upload-img{width: 300px;float: left;margin-right: 20px;
}
</style>
修改index.vue文件,增加使用添加商品组件
<template><div class="product"><product-header></product-header><product-list/><product-pagination/><product-add></product-add></div>
</template><script>
import ProductHeader from './ProductHeader.vue'
import ProductList from './ProductList'
import ProductPagination from './ProductPagination.vue'
import ProductAdd from './ProductAdd'
export default {components:{ProductList,ProductPagination,ProductHeader,ProductAdd}
}
</script><style>
.product{width: 1200px;margin: 0 auto;margin-top: 20px;
}
</style>
8.6.1.图片上传组件
编写PriductUpload.vue
文件
<template><!-- 图片上传一定给的是单独的地址,而且跨域是后台解决的 --><el-uploadclass="upload-demo"ref="upload"action="http://localhost:3000/api/upload":on-remove="handleRemove":file-list="fileList":auto-upload="false":on-success="handleSuccess"><el-button slot="trigger" size="small" type="primary">选取文件</el-button><el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上传到服务器</el-button><div slot="tip" class="el-upload__tip">目前仅支持单张图片上传</div></el-upload>
</template><script>
export default {data(){return {fileList:[]}},methods:{handleRemove(file, fileList) {return this.$confirm(`确定移除 ${ file.name }?`);},handleSuccess(response){// 上传成功后返回的数据responsethis.$emit("onUpload",response)},submitUpload(){this.$refs.upload.submit();}}
}
</script><style></style>
8.6.2.类目选择组件
编写ProductTree.vue
文件
<template><el-tree:props="defaultProps":load="loadNode"lazy@node-click="handleNodeClick"></el-tree>
</template><script>
export default {data() {return {data: [],defaultProps: {children: 'children',label: 'name'}};},mounted(){},methods: {handleNodeClick(data) {this.$emit("onTree",data)},loadNode(node, resolve){// 第一层数据if(node.level ===0){this.$api.selectItemCategoryByParentId().then(res =>{if(res.data.status === 200){return resolve(res.data.result)}else{return resolve([])}})}// 后续展开的数据if(node.level >= 1){this.$api.selectItemCategoryByParentId({id:node.data.cid}).then(res =>{if(res.data.status === 200){return resolve(res.data.result)}else{return resolve([])}}).catch(error =>{return resolve([])})}}}
}
</script><style></style>
8.6.3.富文本编辑器组件
编写ProductWangEditor.vue
文件
<template><div ref="editorWang"style="text-align: left;"></div>
</template><script>
import wangEditor from 'wangeditor'
export default {data(){return {editor:null, // editor对象editorData:'' // 承载编辑器数据}},props:{currentEditorData:{type:String,default:""}},watch:{currentEditorData(newVal, oldVal){this.editor.txt.html(newVal);}},mounted(){this.editor = new wangEditor(this.$refs.editorWang);// 配置 onchange 回调函数,将数据同步到vue中this.editor.config.onchange = (newHtml) =>{this.editorData = newHtml;this.$emit("onEditor",this.editorData);}// 自定义菜单配置this.editor.config.menus = ['head', // 标题'bold', // 粗体'fontSize', // 字号'fontName', // 字体'italic', // 斜体'underline', // 下划线'strikeThrough',// 删除线'foreColor', // 文字颜色'backColor', // 背景颜色'link', // 插入链接'list', // 列表'justify', // 对齐方式'quote', // 引用'emoticon', // 标签'image', // 插入图片'table', // 表格'code', // 插入代码'undo', // 撤销'redo', // 重复];this.editor.create(); // 创建编辑器},beforeDestroy(){// 调用销毁API对当前编辑器实例进行销毁this.editor.destroy();this.editor = null;}}
</script><style></style>
运行效果
8.7.编辑商品窗口
ProductPage目录下新建ProductEditor.vue
文件
<template><el-dialog title="编辑产品":visible.sync="diaologEditorVisible"width="60%":before-close="handleClose"><el-form label-width="80px":model="editorForm"ref="editorForm" ><el-form-item label="商品类目:"><span class="location tree">{{ treeData.name }}</span><el-button type="primary" class="location" @click="dialogCategoryHandle">类目选择</el-button><el-dialogwidth="50%"append-to-bodytitle="类目选择":visible.sync="dialogCategoryVisible"><product-tree @onTree="getTreeData"></product-tree><span slot="footer" class="dialog-footer"><el-button type="primary" @click="dialogCategoryClose">确认</el-button></span></el-dialog></el-form-item><el-form-item label="商品名称:"><el-input v-model="editorForm.name"></el-input></el-form-item><el-form-item label="商品卖点:"><el-input v-model="editorForm.sellPoint"></el-input></el-form-item><el-form-item label="商品价格:"><el-input v-model="editorForm.price"></el-input></el-form-item><el-form-item label="商品数量:"><el-input v-model="editorForm.num"></el-input></el-form-item><el-form-item label="商品图片:"><img class="upload-img" :src="uploadData.url" alt=""><el-button type="primary" class="location" @click="dialogUploadHandle">上传图片</el-button><el-dialogwidth="50%"append-to-bodytitle="图片上传":visible.sync="dialogUploadVisible"><priduct-upload @onUpload="getOnUpload"></priduct-upload><span slot="footer" class="dialog-footer"><el-button type="primary" @click="dialogUploadClose">确认</el-button></span></el-dialog></el-form-item><el-form-item label="商品描述:"><ProductWangEditor @onEditor="getEditor" :currentEditorData="editorData"></ProductWangEditor></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="editorFormClose">取消</el-button><el-button type="primary" @click="editorProductHandle">确认</el-button></span></el-dialog>
</template><script>
import PriductUpload from './PriductUpload.vue';
import ProductTree from './ProductTree.vue';
import ProductWangEditor from './ProductWangEditor.vue';
export default{components:{ProductTree,PriductUpload,ProductWangEditor},data(){return {diaologEditorVisible : false,dialogCategoryVisible:false,dialogUploadVisible:false,editorForm:{name:"",sellPoint:"",price:"",num:"",},treeData:{cid:"",name:""}, // 类目选择 uploadData:{url:""}, // 存储图片editorData:"",currentData:{}}},mounted(){this.$bus.$on("onEditorEvent",row =>{this.diaologEditorVisible = true;this.currentData = row;// 获取更新数据this.$api.preUpdateItem({id:row.id}).then((res)=>{// 赋值this.treeData.name = res.data.result[0].cid;this.treeData.cid = res.data.result[0].cid;this.editorForm.name = res.data.result[0].titlethis.editorForm.sellPoint = res.data.result[0].sellPointthis.editorForm.price = res.data.result[0].pricethis.editorForm.num = res.data.result[0].numthis.uploadData.url = res.data.result[0].imagethis.editorData = res.data.result[0].descs})})},methods:{handleClose(done){this.$confirm("确认关闭").then(_ =>{done();}).catch(_ =>{});},editorFormClose(){this.diaologEditorVisible = false},dialogCategoryHandle(){this.dialogCategoryVisible = true},dialogCategoryClose(){this.dialogCategoryVisible = false},dialogUploadHandle(){this.dialogUploadVisible = true},dialogUploadClose(){this.dialogUploadVisible = false},// 读取类目选择数据getTreeData(data){// console.log(data);this.treeData = data;},// 读取图片地址getOnUpload(data){if(data.url){data.url = data.url.replace("upload","http://localhost:3000")}this.uploadData = data;},//接收富文本数据getEditor(data){this.editorData = data;},// 编辑产品editorProductHandle(){this.$api.updateTbItem({id:this.currentData.id,cid:this.treeData.cid,title:this.editorForm.name,sellPoint:this.editorForm.sellPoint,price:this.editorForm.price,num:this.editorForm.num,desc:this.editorData,image:this.uploadData.url}).then(res =>{if(res.data.status === 200){this.diaologEditorVisible = false;this.$bus.$emit("refresh",true)}}).catch(error=>{console.log(error);})}}
}
</script><style scoped>
.location{float: left;
}
.tree{margin-right: 20px;
}
.upload-img{width: 300px;float: left;margin-right: 20px;
}
</style>
修改index.vue文件,增加使用修改商品组件
<template><div class="product"><product-header></product-header><product-list/><product-pagination/><product-add></product-add><ProductEditor/></div>
</template><script>
import ProductHeader from './ProductHeader.vue'
import ProductList from './ProductList'
import ProductPagination from './ProductPagination.vue'
import ProductAdd from './ProductAdd'
import ProductEditor from './ProductEditor.vue'
export default {components:{ProductList,ProductPagination,ProductHeader,ProductAdd,ProductEditor}
}
</script><style>
.product{width: 1200px;margin: 0 auto;margin-top: 20px;
}
</style>
运行效果
报错处理
1.找不到element模块
报错截图
处理方法
修改element.js文件,注释掉lang和locale的引用
import Vue from 'vue'
import element from 'element-ui'
//导入组件相关样式
import 'element-ui/lib/theme-chalk/index.css'
// import lang from 'element-ui/lib/locale/lang/'
// import locale from 'element-ui/lib/locale'// locale.use(lang)Vue.use(element)
2.找不到less-loader模块
报错截图
处理方法
使用
npm install less-loader less --save-dev
安装模块