前端代码在git地址: https://github.com/178600803/-.git
如果需要后端服务请私信我,或者留言
项目总体实现的就是对数据库的增删改查,以及对前台模块的管理
截图:
1.打开后端
1.umall-api 安装依赖包
cnpm i
2.navicat for mysql 创建一个usmall数据库 ,右键--》运行sql文件--》选择 无数据.sql
3.umall-api/config/global.js
exports.dbConfig = {
host: 'localhost', //数据库地址
user: 'root',//数据库用户名
password: '123',//数据库用户密码
port: 3306,
database: 'usmall' // 数据库名字
}
4.启动后端 ,最后启动在localhost:3000
npm start
2.前端工作
1.创建项目
vue init webpack umall_admin
2.清空工作
1.assets 删完了
2.components删完了
3.router/index.js 删除了helloword相关的
4.app.vue重置
3.配置目录
-src
-assets 静态资源
-css
-js
-components 公共组件
-filters 过滤器
-pages 路由组件
-menu
menu.vue
-components 路由组件的子组件
-router 路由
-store 仓库
-util 工具类
App.vue
main.js
4.项目搭建
1.reset.css
1.assets/css/reset.css
2.main.js引入
//1.reset.css
import "./assets/css/reset.css"
2.公共组件
1.components/index.js
export default {
}
2.main.js处理
//2.处理公共组件
import Components from "./components"
for(let i in Components){
Vue.component(i,Components[i])
}
3.过滤器
1.filters/index.js
export default {
}
2.main.js处理
// 3.处理过滤器
import Filters from "./filters"
for(let i in Filters){
Vue.filter(i,Filters[i])
}
4.仓库
1.安装vuex
cnpm i vuex --save
2.配置目录
-store
index.js 导出仓库
actions.js 根级别下的actions
mutations.js 根级别下的mutations state getters
-modules 模块
3.导出仓库
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
import actions from "./actions"
import {state,mutations,getters} from "./mutations"
export default new Vuex.Store({
state,
mutations,
getters,
actions,
modules:{
}
})
4.main.js引入仓库
//4.处理仓库
import store from "./store"
new Vue({
store,
})
5.数据请求
1.安装模块
cnpm i axios qs --save
2.配置代理 config/index.js
proxyTable: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
pathRewrite: {
"^/api": "http://localhost:3000"
}
}
},
3.util/request.js
import axios from "axios"
import qs from "qs"
let baseUrl = "/api";
//响应拦截
axios.interceptors.response.use(res => {
console.group("====本次请求的地址是:" + res.config.url + "======");
console.log(res);
console.groupEnd()
return res;
})
6.UI
1.安装
cnpm i element-ui --save
2.main.js引入
// 6.处理element-ui
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI)
7.弹框封装 util/alert.js
import Vue from "vue"
let vm = new Vue()
//成功弹框
export const successAlert = (msg) => {
vm.$message({
message: msg,
type: 'success'
});
}
//警告弹框
export const warningAlert = msg => {
vm.$message({
message: msg,
type: 'warning'
});
}
5.安装依赖包
cnpm i vuex axios qs element-ui --save
6.配置1级路由
1.pages下创建了login index
2.router/index.配置规则
const login = () => import("../pages/login/login")
const index = () => import("../pages/index/index")
export default new Router({
routes: [{
path: "/login",
component: login
},
{
path: "/",
component: index
}
]
})
3.app.vue定义路由出口
7.login.vue
画了静态页
登录
登录
8.index.vue
1.粘贴布局
Header
Main
2.粘贴导航
首页
系统设置
菜单管理
角色管理
管理员管理
商城管理
商品分类
商品规格
商品管理
会员管理
轮播图管理
秒杀活动
9.二级路由规则
1.pages下创建 home menu role manage classify spec goods member banner seckill
2.路由规则
//首页下面的二级路由规则
const indexRoutes=[
{
path:"menu",
component:menu
},
{
path:"role",
component:role
},
{
path:"manage",
component:manage
},
{
path:"classify",
component:classify
},
{
path:"spec",
component:spec
},
{
path:"goods",
component:goods
},
{
path:"banner",
component:banner
},
{
path:"member",
component:member
},
{
path:"seckill",
component:seckill
},
]
{
path: "/",
component: index,
children:[
{
path:"",
component:home
},
...indexRoutes
]
}
2.index.vue 定义二级路由出口
3.将导航改用路由模式,index对应path
首页
系统设置
菜单管理
角色管理
管理员管理
10.菜单管理
1.拆分组件
添加
2.绘制添加组件
目录
菜单
3.弹框状态
1.定义数据 menu.vue
data() {
return {
//传递给子组件的信息
info:{
//添加弹框出现状态
isShow:false,
title:"添加菜单",
isAdd:true
}
}
},
2.info传递给add.vue
3.add.vue接收
props:["info"],
4.使用info
5.add.vue 点击了取消
取 消
//点击了取消
cancel(){
this.$emit("hide")
},
menu.vue
// 弹框消失
hide(){
this.info.isShow=false;
},
4.添加操作
1.定义初始数据
//表单数据
form:{
pid:0,
title:"",
icon:"",
type:1,
url:"",
status:1
}
2.通过v-model绑定到view上
3.点击了“添加” 按钮,准备发送添加请求
request.js
//菜单添加
export const reqAddMenu = (form) => {
return axios({
url: baseUrl + "/api/menuadd",
method: "post",
data: qs.stringify(form)
})
}
发送添加请求
//添加
add(){
reqAddMenu(this.form).then(res=>{
if(res.data.code==200){
//添加成功
successAlert(res.data.msg)
//弹框消失
this.$emit("hide")
//数据重置
this.empty()
//重新获取list
this.reqList()
}else{
warningAlert(res.data.msg)
}
})
},
//重置form数据
empty(){
this.form={
pid:0,
title:"",
icon:"",
type:1,
url:"",
status:1
}
},
5.列表获取
由于很多组件都会使用到列表数据,所以决定将列表数据写在vuex
1.store/modules/menu.js
import {
reqMenuList
} from "@/util/request.js"
const state = {
//菜单列表
list: []
}
const mutations = {
//修改list
changeList(state, arr) {
state.list = arr;
}
}
const actions = {
//请求
reqListAction(context){
reqMenuList({istree:true}).then(res=>{
context.commit("changeList",res.data.list)
})
}
}
const getters = {
list(state){
return state.list
}
}
export default {
state,
mutations,
actions,
getters,
namespaced: true
}
request.js
//菜单列表
export const reqMenuList = (params) => {
return axios({
url: baseUrl + "/api/menulist",
method: "get",
params: params
})
}
2.在store中添加menu模块
import menu from "./modules/menu"
export default new Vuex.Store({
state,
mutations,
getters,
actions,
modules:{
menu
}
})
3.menu/components/list.vue
import {mapGetters,mapActions} from "vuex"
export default {
computed:{
...mapGetters({
list:"menu/list"
})
},
methods: {
...mapActions({
reqList:"menu/reqListAction"
})
},
mounted() {
//一进来 走请求
this.reqList()
}
}
6.编辑
1.list.vue触发了编辑按钮
编辑
//点击了编辑按钮,通知menu,点了编辑
edit(id){
this.$emit("emit",id)
}
2.menu.vue接收事件
//触发了编辑
emit(id){
this.info={
isShow:true,
title:"修改菜单",
isAdd:false
}
//父组件调用子组件的方法
this.$refs.add.look(id)
}
3.add.vue look执行,查询某一条数据
request.js
//菜单详情
export const reqMenuDetail = (params) => {
return axios({
url: baseUrl + "/api/menuinfo",
method: "get",
params: params
})
}
add.vue
//查看一条数据
look(id){
reqMenuDetail({id:id}).then(res=>{
this.form=res.data.list
this.form.id=id;
})
},
4.点击了修改按钮
request.js
//菜单修改
export const reqMenuUpdate = (form) => {
return axios({
url: baseUrl + "/api/menuedit",
method: "post",
data: qs.stringify(form)
})
}
add.vue
//点击了修改
update(){
reqMenuUpdate(this.form).then(res=>{
if(res.data.code==200){
successAlert("更新成功")
this.$emit("hide")
this.empty()
this.reqList()
}else{
warningAlert(res.data.msg)
}
})
}
7.做完编辑,点击编辑--》取消--》添加,数据存在 bug
//弹框关闭完成
close(){
// 如果是编辑,取消了,就要清空
if(!this.info.isAdd){
this.empty()
}
},
8.删除
1.list.vue 点击了删除按钮
删除
2.request.js
//菜单删除 params={id:1}
export const reqMenuDel = (params) => {
return axios({
url: baseUrl + "/api/menudelete",
method: "post",
data:qs.stringify(params)
})
}
3.二次确认是否删除
//删除
del(id){
this.$confirm('你确定要删除吗?', '提示', {
confirmButtonText: '删除',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//点击了确定,发起删除请求
reqMenuDel({id:id}).then(res=>{
if(res.data.code==200){
successAlert("删除成功")
this.reqList()
}else{
warningAlert(res.data.msg)
}
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
}
11.角色管理
1.拆分组件 role.vue
添加
2.绘制list.vue
启用
禁用
编辑
删除
3.add.vue 弹框的出现和消失
1.role.vue 定义控制弹框的变量 info
data() {
return {
//控制add的状态
info:{
isShow:false,
title:"添加角色",
isAdd:true
}
}
},
2.role.vue 点击了添加按钮
methods: {
//点击了添加按钮
willadd(){
this.info={
isShow:true,
title:"添加角色",
isAdd:true
}
}
},
3.role.vue 将info传递给add.vue
4.add.vue接收
props:["info"]
5.点击了取消按钮
//点击了取消
cancel(){
this.info.isShow=false;
}
4.角色添加
1.画了静态页
2.request.js
//角色添加
export const reqRoleAdd = (form) => {
return axios({
url: baseUrl + "/api/roleadd",
method: "post",
data: qs.stringify(form)
})
}
//角色列表
export const reqRoleList = () => {
return axios({
url: baseUrl + "/api/rolelist",
method: "get",
})
}
//角色详情
export const reqRoleDetail = (params) => {
return axios({
url: baseUrl + "/api/roleinfo",
method: "get",
params: params
})
}
//角色修改
export const reqRoleUpdate = (form) => {
return axios({
url: baseUrl + "/api/roleedit",
method: "post",
data: qs.stringify(form)
})
}
//角色删除 params={id:1}
export const reqRoleDel = (params) => {
return axios({
url: baseUrl + "/api/roledelete",
method: "post",
data:qs.stringify(params)
})
}
3.从vuex中取出menu的list,以及修改menuList的动作 ,在tree上展示
import {mapGetters,mapActions} from "vuex"
export default {
computed:{
...mapGetters({
"menuList":"menu/list"
})
},
methods: {
...mapActions({
"reqMenuList":"menu/reqListAction"
}),
},
mounted() {
//如果menuList数组是个空的,要发起请求得到
if(this.menuList.length==0){
this.reqMenuList()
}
}
}
4.点击了添加按钮
//数据重置
empty(){
this.form={
rolename:"",
menus:[],
status:1
}
//重置树形控件
this.$refs.tree.setCheckedKeys([])
},
//点击了添加按钮
add(){
// this.$refs.tree.getCheckedKeys() 获取树形控件上的选中的key
this.form.menus=JSON.stringify(this.$refs.tree.getCheckedKeys())
reqRoleAdd(this.form).then(res=>{
if(res.data.code==200){
successAlert("添加成功");
//弹框消失
this.cancel();
//数据重置
this.empty()
//刷新角色列表的数据
}else{
warningAlert(res.data.msg)
}
})
}
5.列表
1.列表决定放到vuex中,store/modules/role.js
import {
reqRoleList
} from "@/util/request.js"
const state = {
//角色列表
list: []
}
const mutations = {
//修改list
changeList(state, arr) {
state.list = arr;
}
}
const actions = {
//请求
reqListAction(context) {
reqRoleList().then(res => {
let arr= res.data.list? res.data.list:[]
context.commit("changeList", res.data.list)
})
}
}
const getters = {
list(state) {
return state.list
}
}
export default {
state,
mutations,
actions,
getters,
namespaced: true
}
2.role 放到store
import role from "./modules/role"
export default new Vuex.Store({
state,
mutations,
getters,
actions,
modules:{
menu,
role
}
})
3.list.vue中取数据和方法
import {mapGetters,mapActions} from "vuex"
export default {
computed:{
...mapGetters({
list:"role/list"
})
},
methods: {
...mapActions({
"reqList":"role/reqListAction"
})
},
mounted() {
//一进来就请求了角色列表数据
this.reqList()
}
}
4.添加成功要重新请求列表数据
...mapActions({
"reqMenuList":"menu/reqListAction",
"reqRoleList":"role/reqListAction"
}),
reqRoleAdd(this.form).then(res=>{
if(res.data.code==200){
successAlert("添加成功");
//弹框消失
this.cancel();
//数据重置
this.empty()
//刷新角色列表的数据
this.reqRoleList()
}else{
warningAlert(res.data.msg)
}
})
6.编辑
1.list.vue 点击了编辑,通知role.vue点了编辑
编辑
//点击了编辑
edit(id){
this.$emit("edit",id)
}
2.role.vue收到自定义事件,弹框出现,add发送获取详情的请求
//编辑
edit(id){
this.info={
isShow:true,
title:"修改角色",
isAdd:false
}
this.$refs.add.look(id)
}
3.add.vue查询某一条数据,id给form,树形控件单独处理
//查看详情
look(id){
reqRoleDetail({id:id}).then(res=>{
this.form=res.data.list;
this.form.id=id;
this.$refs.tree.setCheckedKeys(JSON.parse(res.data.list.menus))
})
},
4.点了修改按钮
//修改
update(){
// this.$refs.tree.getCheckedKeys() 获取树形控件上的选中的key
this.form.menus=JSON.stringify(this.$refs.tree.getCheckedKeys())
reqRoleUpdate(this.form).then(res=>{
if(res.data.code==200){
successAlert("修改成功");
//弹框消失
this.cancel();
//数据重置
this.empty()
//刷新角色列表的数据
this.reqRoleList()
}else{
warningAlert(res.data.msg)
}
})
}
7.删除
1.因为每次都要二次确认,所以封装了一个全局的删除组件 components/vDel.vue
删除
2.components/index.js 引入vDel
import vDel from "./vDel"
export default {
vDel
}
3.list.vue调用 v-del
//删除
del(id){
//点击了确定,发起删除请求
reqRoleDel({id:id}).then(res=>{
if(res.data.code==200){
successAlert("删除成功")
this.reqList()
}else{
warningAlert(res.data.msg)
}
})
}
12.管理员管理
1.添加、列表({page:1.size:20})、编辑、修改、删除 如上。
2.分页组件
total是总的数量,
page-size是一页的数量,
分页组件会自动计算有几页。 eg:total=10,page-size=2,那么就有5页
3.取管理员的总数量
1.request.js
export const reqUserNum=()=>{
return axios({
url:baseUrl+"/api/usercount",
method:"get"
})
}
2.store/modules/manage.js
定义了一个状态 total,用来保存管理员的总数
const state = {
//管理员总数
total: 0,
}
const mutations = {
//修改total
changeTotal(state, num) {
state.total = num;
},
}
const actions = {
//获取总数的请求
reqListNum(context) {
reqUserNum().then(res => {
context.commit("changeTotal", res.data.list[0].total)
})
},
}
const getters = {
total(state) {
return state.total
},
}
3.list.vue 取出total,并且要触发获取总数的请求
computed:{
...mapGetters({
total:"manage/total",
})
},
methods: {
...mapActions({
reqTotal:"manage/reqListNum",
}),
}
mounted(){
this.reqList()
//一进来页面请求数据的总数
this.reqTotal()
}
4.设置一下当前一页的数量
vuex size
const state = {
//一页的条数
size: 2,
}
const mutations = {
}
const actions = {
//请求
reqListAction(context) {
reqUserList({
page: 1,
size: context.state.size
}).then(res => {
let arr = res.data.list ? res.data.list : []
context.commit("changeList", arr)
})
},
}
const getters = {
size(state) {
return state.size
},
}
list.vue
computed:{
...mapGetters({
size:"manage/size",
})
},
5.页码发生改变,需要重新请求数据,重新请求数据需要知道是第几页。
1.在vuex中定义了一个变量page,用来记录当前是第几页
const state = {
//当前访问的页码
page: 1,
}
const mutations = {
//修改页面
changePage(state, page) {
state.page = page
}
}
const actions = {
//请求
reqListAction(context) {
reqUserList({
page: context.state.page,
size: context.state.size
}).then(res => {
let arr = res.data.list ? res.data.list : []
context.commit("changeList", arr)
})
},
//修改了page
changePageAction(context, page) {
context.commit("changePage", page)
//重新请求列表数据
context.dispatch("reqListAction")
}
}
const getters = {
page(state) {
return state.page
},
}
2.list.vue 点击了页码、上一页、下一页,修改页面
computed:{
...mapGetters({
page:"manage/page",
})
},
methods: {
...mapActions({
changePageAction:"manage/changePageAction"
}),
}
在分页组件上触发修改页码
//修改了当前的页码
changeCurrentPage(p){
//修改一下vuex里面的page
this.changePageAction(p)
}
6.add.vue添加成功 总数会发生改变,重新请求总数
add(){
reqUserAdd(this.form).then(res=>{
if(res.data.code==200){
successAlert("添加成功");
//弹框消失
this.cancel();
//数据重置
this.empty()
//刷新角色列表的数据
this.reqUserList()
//重新请求总数
this.reqTotal()
}else{
warningAlert(res.data.msg)
}
})
},
7.list.vue删除成功 ,总数也会发生改变,而且页码也有可能变,而且最后一页有可能都已经没有了,所以页码重置
del(id){
//点击了确定,发起删除请求
reqUserDel({uid:id}).then(res=>{
if(res.data.code==200){
successAlert("删除成功")
//删除完成,重新取总数,page设置为1
this.reqTotal()
this.changePageAction(1)
}else{
warningAlert(res.data.msg)
}
})
},
13.商品分类之 上传文件
1.原生上传文件
1.绘制html
+
.upload-box{
width: 150px;
height: 150px;
border: 1px dashed #cccccc;
position: relative;
}
.upload-add{
width: 150px;
height: 150px;
text-align: center;
line-height: 150px;
font-size: 40px;
color: #666;
font-weight: 400;
}
.upload-img{
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
.upload-file{
width: 150px;
height: 150px;
position: absolute;
left: 0;
top: 0;
opacity: 0;
}
2.在input上绑定一个事件 change,可以通过事件对象获取到文件,通过URL对象将file生成一个地址,图片展示
//上传文件开始
selectImg(e){
var file=e.target.files[0];
this.imgUrl=URL.createObjectURL(file) ;//imgUrl要在data中声明
},
//上传文件结束
3判断了文件的大小和格式是否正确
//上传文件开始
selectImg(e){
var file=e.target.files[0];
//2M
if(file.size>2*1024*1024){
warningAlert("文件格式不能超过2M")
return;
}
//图片类型
let imgTypeArr=[".jpg",".jpeg",".png",".gif"]
//获取到后缀名
var extname=file.name.slice(file.name.lastIndexOf("."));
//判断文件格式是否正确
if(!imgTypeArr.includes(extname)){
warningAlert("请上传正确的图片格式")
return;
}
this.imgUrl=URL.createObjectURL(file)
},
//上传文件结束
4.写了数据请求,因为参数中带有文件,所以需要使用formData
//分类添加
export const reqCateAdd = (form) => {
//{z:1,a:2,v:4,d:file}
var data=new FormData()
for(let i in form){
data.append(i,form[i])
}
return axios({
url: baseUrl + "/api/cateadd",
method: "post",
data: data
})
}
5.在add.vue
data() {
return {
imgUrl:"",
//label占的宽度
width:"100px",
//表单数据
form:{
pid:0,
catename:"",
img:null,
status:1
}
}
},
在选择了图片之后,需要将文件保存在form.img
selectImg(e){
//判断大小 判断文件格式
this.imgUrl=URL.createObjectURL(file)
this.form.img=file;
}
6.在list.vue取出来数据要展示图片,在原型上加$preImg
Vue.prototype.$preImg="http://localhost:3000"
7.展示图片 list.vue
8.编辑 取出来详情 add.vue
//查看一条数据
look(id){
reqCateDetail({id:id}).then(res=>{
this.form=res.data.list;
this.form.id=id
this.imgUrl=this.$preImg+ res.data.list.img
})
},
9.清空 add.vue
//重置form数据
empty(){
this.form={
pid:0,
catename:"",
img:null,
status:1
}
this.imgUrl=""
},
2.element-ui 上传文件
1.粘贴 el-upload,绑定了on-change属性
2.处理changeImg
//element-ui 上传文件
changeImg(e){
var file=e.raw;
this.imgUrl=URL.createObjectURL(file)
this.form.img=file;
},
3.如果在UI框架中想修改样式,不起作用
1.important
.upload-add{
width: 150px !important;
height: 150px;
text-align: center;
line-height: 150px;
font-size: 40px;
color: #666;
font-weight: 400;
}
2.如果1的方法也不行,那么就使用stylus穿透
cnpm i stylus stylus-loader --save
在页面找一个一定存在的标签
.add >>> .el-upload{
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
14.首页-图表
1.安装
cnpm i echarts --save
2.引入 home.vue
import echarts from "echarts"
3.从vuex中取出数据
export default {
computed:{
...mapGetters({
list:"cate/list"
})
},
methods: {
...mapActions({
reqList:"cate/reqListAction"
}),
},
mounted() {
this.reqList();
},
}
4.数据变了,绘制图标
watch:{
list:{
handler(){
if(this.list.length>0){
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {
title: {
text: '商品分类概况'
},
tooltip: {},
legend: {
data:['分类数量']
},
xAxis: {
data: this.list.map(item=>item.catename)
},
yAxis: {},
series: [{
name: '分数数量',
type: 'line',
data: this.list.map(item=>item.children?item.children.length:0)
}]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
}
},
deep:true
}
}
15.商品管理- 二级联动 富文本
1.二级联动
1.粘贴list.vue add.vue
2.绘制add.vue静态页面
3.写了商品的请求 6个 ,添加 删除 详情 修改 总数 列表
4.添加 初始化数据
return {
//二级分类的列表
secondCateList:[],
//规格属性的列表
attrList:[],
//图片临时地址
imgUrl:"",
//label占的宽度
width:"100px",
//表单数据
form:{
first_cateid:"",
second_cateid:"",
goodsname:"",
price:"",
market_price:"",
img:null,
description:"",
specsid:"",
specsattr:[],
isnew:1,
ishot:1,
status:1
}
}
5.一级分类数据从vuex中取出,如果之前没有取过,就发一次请求
computed: {
...mapGetters({
//分类列表
cateList:"cate/list",
})
},
methods:{
...mapActions({
//获取分类列表
reqCateList:"cate/reqListAction",
}),
}
mounted(){
//如果没有分类就请求
if(this.cateList.length==0){
this.reqCateList()
}
}
6.修改了一级分类,需要根据一级分类计算出二级分类的列表数组
//修改了一级分类
changeFirstId(){
//找到当前first_cateid对应的那条数据的children,赋值给secondCateList,遍历
this.secondCateList=this.cateList.find(item=>item.id==this.form.first_cateid).children
//因为更换了一级分类,二级分类重置
this.form.second_cateid="";
},
7.商品规格也是从vuex中取出
computed: {
...mapGetters({
//规格列表
specList:"spec/list",
})
},
methods:{
...mapActions({
//获取规格列表
reqSpecList:"spec/reqListAction",
}),
}
mounted(){
//请求全部的规格
this.reqSpecList(true)
}
由于之前在商品规格中,使用了分页,但是在商品添加时要的是全部的规格,不能分页,所以在请求的时候,参数变成变量,根据要不要做分页,通过一个变量来决定
const actions = {
//请求
reqListAction(context,bool) {
//传递一个bool,如果是true,那么就请求全部的规格,如果是false,就请求分页
let params=bool?{}:{
page: context.state.page,
size: context.state.size
}
reqspecsList(params).then(res => {
let arr = res.data.list ? res.data.list : []
arr.forEach(item=>{
item.attrs=JSON.parse(item.attrs)
})
context.commit("changeList", arr)
})
},
}
8.修改了商品规格,需要计算一下商品属性的数组
//修改了商品规格
changeSpecId(){
//当商品规格变了,就计算一下商品属性要展示的数组列表
this.attrList=this.specList.find(item=>item.id==this.form.specsid).attrs;
//选中的商品属性重置
this.form.specsattr=[]
},
9.点击了编辑按钮,查看某一条数据,这个时候二级分类会出现没有下拉框选项,需要手动触发
//查看一条数据
look(id){
reqgoodsDetail({id:id}).then(res=>{
this.form=res.data.list;
this.form.id=id
this.imgUrl=this.$preImg+ res.data.list.img
this.form.specsattr=this.form.specsattr.split(",")
//计算二级分类的列表
this.secondCateList=this.cateList.find(item=>item.id==this.form.first_cateid).children
//计算商品属性列表
this.attrList=this.specList.find(item=>item.id==this.form.specsid).attrs;
})
},
2.富文本
1.官网:http://www.wangeditor.com/
2.安装
cnpm i wangeditor --save
3.引入
import E from "wangeditor"
//创建编辑器
createEditor(){
//创建编辑器
let editor=new E("#editor");
editor.create()
},
4.取值
editor.txt.html()
5.设值
editor.txt.html('123
')
6.使用
1.引入
import E from "wangeditor"
2.创建一个富文本编辑器
//创建编辑器
createEditor(){
//创建编辑器
this.editor=new E("#editor");
this.editor.create()
},
3.这时候出现一个bug,取消,再添加,就会多一个富文本编辑器
4.添加或者修改之前,应该先将富文本编辑器内容取出,赋值给form.description
//添加
add(){
//取出富文本编辑器的内容,赋值给form的description
this.form.description=this.editor.txt.html()
reqgoodsAdd(this.form).then(res=>{})
},
5.当查看某一条数据的时候,数据赋值给了form,需要将form.description赋值给编辑器,如果在请求完成的时候赋值,这个时候编辑器还没有,所以赋不上,需要等编辑器创建好了再赋值
//创建编辑器
createEditor(){
//创建编辑器
this.editor=new E("#editor");
this.editor.create()
//给富文本编辑器赋值
this.editor.txt.html(this.form.description)
},
16.登录
1.发起登录请求 login.vue
login(){
//登录请求
reqUserLogin(this.user).then(res=>{
if(res.data.code==200){//登录成功
//弹成功
successAlert("登录成功");
//将用户信息保存 vuex
this.changeInfoAction(res.data.list)
//跳转
this.$router.push("/")
}else{
warningAlert(res.data.msg)
}
})
}
2.将用户信息存入vuex
1.store/modules/user.js
const state = {
//用户信息
info: sessionStorage.getItem("info") ? JSON.parse(sessionStorage.getItem("info")) : {}
}
const mutations = {
//修改用户信息
changeInfo(state, info) {
state.info = info;
if (info.username) {
//数据同步到本地存储中
sessionStorage.setItem("info", JSON.stringify(info))
} else {
sessionStorage.removeItem("info")
}
}
}
const actions = {
//页面触发的修改用户信息
changeInfoAction({
commit
}, info) {
commit("changeInfo", info)
}
}
const getters = {
//导出
info(state) {
return state.info
}
}
export default {
state,
mutations,
actions,
getters,
namespaced: true
}
2.store/index.js引入
3.登录成功的时候调用修改info的action
if(res.data.code==200){//登录成功
//弹成功
successAlert("登录成功");
//将用户信息保存 vuex
this.changeInfoAction(res.data.list)
//跳转
this.$router.push("/")
}
4.首页的头部取出数据展示 index.vue
{{info.username}}
computed:{
...mapGetters({
info:"user/info"
})
},
3.数据刷新就没有了,所以希望info存入的时候,本地存储也存一份
const mutations = {
//修改用户信息
changeInfo(state, info) {
state.info = info;
if (info.username) {
//数据同步到本地存储中
sessionStorage.setItem("info", JSON.stringify(info))
} else {
sessionStorage.removeItem("info")
}
}
}
如果用户刷新,就看一下本地存储有没有,有就将本地存储的info取出赋值给vuex,如果没有,就赋值一个{}
const state = {
//用户信息
info: sessionStorage.getItem("info") ? JSON.parse(sessionStorage.getItem("info")) : {}
}
4.退出登录
logout(){
this.changeInfoAction({})
this.$router.push("/login")
}
5.登录拦截
除了登录路由,其他的路由,都需要登录之后才能访问 router/index.js
//登录拦截
router.beforeEach((to,from,next)=>{
console.log(store);
//如果去登录 next
if(to.path==="/login"){
next()
return;
}
//去的不是登录,判断是否登录,如果登录过了,就next
if(store.state.user.info.id){
next()
return
}
//去的不是登录,也没有登录过
next("/login")
})
6.侧边栏遍历数据 index.vue
{{item.title}}
{{i.title}}
{{item.title}}
7.路由独享守卫 router/index.js
//路由独享守卫判断
function beforeEnter(url, next) {
store.state.user.info.menus_url.some(item => item == url) ? next() : next("/")
}
//首页下面的二级路由规则
export const indexRoutes = [{
path: "menu",
component: menu,
name: "菜单管理",
beforeEnter(to, from, next) {
beforeEnter("/menu", next)
}
},
{
path: "role",
component: role,
name: "角色管理",
beforeEnter(to, from, next) {
beforeEnter("/role", next)
}
},
{
path: "manage",
component: manage,
name: "管理员管理",
beforeEnter(to, from, next) {
beforeEnter("/manage", next)
}
},
{
path: "classify",
component: classify,
name: "商品分类",
beforeEnter(to, from, next) {
beforeEnter("/classify", next)
}
},
{
path: "spec",
component: spec,
name: "商品规格",
beforeEnter(to, from, next) {
beforeEnter("/spec", next)
}
},
{
path: "goods",
component: goods,
name: "商品管理",
beforeEnter(to, from, next) {
beforeEnter("/goods", next)
}
},
{
path: "banner",
component: banner,
name: "轮播图管理",
beforeEnter(to, from, next) {
beforeEnter("/banner", next)
}
},
{
path: "member",
component: member,
name: "会员管理",
beforeEnter(to, from, next) {
beforeEnter("/member", next)
}
},
{
path: "seckill",
component: seckill,
name: "秒杀活动",
beforeEnter(to, from, next) {
beforeEnter("/seckill", next)
}
},
]
8.修改 umall_api 里面的app.js 下面这段后端路由拦截解开
app.use(async (req, res, next) => {
if (!req.headers.authorization) {
res.send(MError("请设置请求头,并携带验证字符串"));
} else {
if (!await checkToken(req)) { // 过期
res.send(Guest([],"登录已过期或访问权限受限"));
} else {
next();
}
}
});
9.请求拦截
每次发起请求,除了登录接口之外,其他接口都需要携带一个token
//请求拦截
axios.interceptors.request.use(config=>{
//登录
if(config.url==baseUrl+"/api/userlogin"){
return config;
}
config.headers.authorization=store.state.user.info.token;
return config;
})
10.响应拦截
每次请求回来的数据,都有可能已经显示token过期,所以如果过期了,就应该 退出登录,跳到登录页面
//响应拦截
axios.interceptors.response.use(res => {
console.group("====本次请求的地址是:" + res.config.url + "======");
console.log(res);
console.groupEnd()
if(res.data.msg==="登录已过期或访问权限受限"){
warningAlert("登录已过期或访问权限受限")
//清空info
store.dispatch("user/changeInfoAction",{})
//跳转到登录
router.push("/login")
}
return res;
})