复制代码
1, src/views/Login/Login.vue
<template>
<div>
<div class="login-box">
<h3 class="title">登录界面</h3>
<!-- <div>{{info}}</div> -->
<el-form :model="loginForm" status-icon :rules="rules" ref="ruleForm" label-width="60px"
class="demo-ruleForm">
<el-form-item label="账号" prop="username" required>
<el-input type="text" v-model="loginForm.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password" required>
<el-input type="password" v-model="loginForm.password" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import jwt from 'jwt-decode'
import {
mapMutations
} from 'vuex';
export default {
data() {
var validateUser = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入账号'));
} else {
callback();
}
};
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
callback();
}
};
return {
// info: '',
loginForm: {
username: '',
password: ''
},
rules: {
username: [{
validator: validateUser,
trigger: 'blur'
}],
password: [{
validator: validatePass,
trigger: 'blur'
}]
}
};
},
methods: {
...mapMutations('loginModule', ['setUser']),
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
console.log('效验通过', this.loginForm);
let {
username,
password
} = this.loginForm;
console.log(username, password);
// 请求登录接口-----
this.$api.getLogin({
username,
password
}).then(res => {
console.log('----', res.data);
if (res.data.status === 200) {
console.log(jwt(res.data.data));
// 登录成功后:
// 1,存储登录信息
let obj = {
user: jwt(res.data.data).username,
token: res.data.data
};
this.setUser(obj);
// 2, 存储本地 将数据持久化
localStorage.setItem('user', JSON.stringify(obj));
// 3, 跳转
this.$router.push('/');
// 3, 顶部区域显示用户信息
// 4, 跳转网页
// this.info = ''
} else {
this.$message.error('错了哦,这是一条错误消息');
// 用户名密码错误
// this.info = '用户名密码错误'
}
})
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
<style lang="less" scoped>
.login-box {
width: 600px;
height: 300px;
// 上下为100px,左右自由,掉在当中
margin: 150px auto;
// 添加阴影 10 px
padding: 20px;
border-radius: 10px;
border: 1px solid #eee;
background: #fff;
}
.title {
margin-bottom: 40px;
text-align: center;
color: #666;
}
</style>
2, src/views/Layout/Content.vue
<template>
<div>
<div class="header">
<i v-if="!isCollapse" @click="changeMenu" class="iconfont icon-right-indent"></i>
<i v-else @click="changeMenu" class="iconfont icon-left-indent"></i>
<div class="header-right">
<el-dropdown @command='clickLang'>
<span class="el-dropdown-link" style="color: #fff;">
选择语言<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command='zh'>中文</el-dropdown-item>
<el-dropdown-item command='en'>English</el-dropdown-item>
<!-- <el-dropdown-item>韩文</el-dropdown-item>
<el-dropdown-item disabled>FR:法语</el-dropdown-item>
<el-dropdown-item divided>LI:林堡语</el-dropdown-item> -->
</el-dropdown-menu>
</el-dropdown>
<div class="user">
欢迎: {{ userinfo.user }}
<i class="iconfont icon-icon-tuichu" @click="loginout"></i>
</div>
</div>
</div>
<!-- 右侧内容区域 --左侧 路由出口 -->
<div class="content">
<!-- 路由出口 -->
<router-view />
</div>
</div>
</template>
<script>
import {
mapState,
mapMutations
} from 'vuex';
export default {
props: ['isCollapse'],
// 利用 vuex 的计算属性,读取数据
computed: {
...mapState('loginModule', ['userinfo'])
},
methods: {
...mapMutations('loginModule', ['clearUser']),
changeMenu() {
// 点击切换按钮的时候,修改父组件的数据 isCollapse
this.$emit('changeCollapse')
},
clickLang(command) {
console.log(command);
// console.log(this); // 打印参数
this.$i18n.locale = command; // 切换语言
},
loginout() {
// 退出登录
// console.log(123);
// 清空 vuex 数据
this.clearUser()
// 清空本地存储
localStorage.removeItem('user')
// 返回登录
this.$router.push('/login')
}
},
};
</script>
<style lang="less" scoped>
.header {
height: 50px;
line-height: 50px;
color: #fff;
background: #1e78bf;
.iconfont {
font-size: 24px;
}
}
.header-right {
display: flex;
float: right;
padding-right: 20px;
.user {
margin-left: 20px;
}
}
</style>
3, src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import loginModule from '@/store/modules/loginModules.js'
Vue.use(Vuex)
export default new Vuex.Store({
state: {},
getters: {},
mutations: {},
actions: {},
// 注册
modules: {
loginModule
}
})
4, src/store/modules/loginModules.js
export default {
// 命名空间
namespaced: true,
// 数据
state: {
userinfo: {
user: '',
token: ''
}
},
// 设置
mutations: {
// 设置用户信息
setUser(state, payload) {
state.userinfo = payload;
},
// 清空
clearUser(state) {
state.userinfo = {
user: '',
token: ''
}
}
},
// 方法
actions: {
//
}
}
5, 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/Goods.vue')
const Params = () => import('../views/Params/Params.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/AddGoods.vue')
Vue.use(VueRouter)
const routes = [{
path: '',
component: Layout,
// 路由的元信息
meta: {
isLogin: true
},
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
}, {
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
6, src/router/permission.js
import router from '@/router/index.js';
// 获取 vuex 数据
import store from '@/store/index.js'
// 路由拦截
router.beforeEach((to, from, next) => {
// console.log('---to---', to);
// 1, 判断是否需要登录
if (to.matched.some(ele => ele.meta.isLogin)) {
// 2, 判断当前的用户是否已经登录
let token = store.state.loginModule.userinfo.token;
if (token) {
// 判断已登录
next();
} else { // 判断未登录
// 跳转到登录页面
next('/login');
}
} else { // 不需要登录
next();
}
})
7, src/utils/localStorage.js
import store from '@/store/index.js'
// 持久化
let user = localStorage.getItem('user')
if (user) {
user = JSON.parse(user);
store.commit('loginModule/setUser', user);
}
8, 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', // 登录接口
}
export default base;
9, 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
})
},
}
export default api;
10, src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/plugins/element.js'
import '@/assets/css/reset.css'
import '@/assets/css/iconfont.css'
// 挂载 api
import api from './api/index.js'
Vue.prototype.$api = api;
// 导入语言
import i18n from '@/lang/index.js'
import '@/router/permission.js'
import '@/utils/localStorage.js'
Vue.config.productionTip = false
// 持久化
// let user = localStorage.getItem('user')
// if (user) {
// user = JSON.parse(user);
// store.commit('loginModule/setUser', user);
// }
new Vue({
router,
store,
i18n,
render: function(h) {
return h(App)
}
}).$mount('#app')
11, server/secert.js
module.exports = {
jwtSecert: 'lalalahahahazhousil'
}
12, 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')
// 测试接口
// 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: "修改失败"
})
}
})
})
})
// =====================
/**
* 内容分类管理 内容查询
*/
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: "暂无数据"
})
}
})
})
/**
* 统计数据--销售信息
*/
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
}]
}))
})
module.exports = router
13, server/mysql.js
// 连接数据库 1,安装mysql 2, 创建连接
const mysql = require('mysql')
// 创建数据库连接
const client = mysql.createConnection({
host: 'localhost', // 数据域名 地址
user: 'longchi18', // 数据库用户名称
password: 'longchi18', // 数据库密码 xampp 集成
database: 'vue_ego',
port: '3306'
})
// 封装数据库操作语句 sql语句 参数数组 arr callback 成功函数结果 老师的
function sqlFn(sql, arr, callback) {
client.query(sql, arr, function(error, result) {
if (error) {
console.log('数据库语句错误');
return;
}
callback(result);
})
}
module.exports = sqlFn;