Vue3商城项目实战

目录

一、Vue3环境和项目创建

1、Vue3安装和项目创建

2、Vue3基础和Element-plus

模版语法

生命周期和事件绑定

 路由

element-plus 

 二、登录

1、Login.vue

2、Vuex基础

1) vuex语法结构

2)案例

3)Vuex的读取和更改状态

3、路由守卫和登录

4、登录状态存储和退出登录

三、axios封装和登录完善

1、axios安装

2、service.js封装

3、request.js封装登录接口

4、Login.vue登录代码完善

四、首页

1、index.vue

2、Login.vue登录跳转首页

3、router.js

4、Layout 布局和导航

五、账号管理

1、router.js

2、UserList.vue

3、request.js添加账号管理API

六、角色管理

1、列表、新建、编辑、删除 API

 2、角色新增和编辑提交

3、判断弹窗是添加还是编辑

​4、编辑角色​编辑

5、清除角色表单

6、删除角色

 七、商品管理

1、商品查询

 2、商品状态转换


一、Vue3环境和项目创建

1、Vue3安装和项目创建

 

vue/cli初始化项目工程

vue create shop

去掉代码检查

2、Vue3基础和Element-plus

  • 模版语法

v-text

{{}}

v-html

v-bind:属性名

v-for

v-if

v-show


 

  • 生命周期和事件绑定

  •  路由

history 和hash模式

 

懒加载 

路由跳转 router-to / router-view

  • element-plus 

elementIcon引入 

npm install @element-plus/icons-vue
// 如果您正在使用CDN引入,请删除下面一行。
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

 二、登录

1、Login.vue

<template>
  <div class="login_wrap">
    <div class="form_wrap">
      <el-form ref="ruleFormRef" :model="loginData" label-width="100px" class="demo-ruleForm"
        :size="formSize">
        <el-form-item label="用户名" prop="username" :rules="[{ required: true, message: '请输入用户名', trigger: 'blur' }]">
          <el-input v-model="loginData.username" />
        </el-form-item>
        <el-form-item label="密码" prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
          <el-input type="password" v-model="loginData.password" />
        </el-form-item>
      </el-form>
      <div class="display:flex">
        <el-button type="primary" class="login_btn">登录</el-button>
      </div>

 

    </div>

  </div>
</template>
<script>
import { reactive, toRefs } from 'vue';
export default {
  name: "login",
  setup() {
    const data = reactive({
      loginData:{
        username:"",
        password:""
      },
      rules:[]

    })
    return {
      ...toRefs(data)
    }
  }

}
</script>
<style>
.login_wrap {
  width: 100%;
  height: 100vh;
  background: #726386;
}

.form_wrap {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #fff;
  padding: 30px 50px;
  border-radius: 5px;
}
.login_btn{
  display: block;
  margin: 10px auto;
}
</style>

2、Vuex基础

1) vuex语法结构

2)案例

import { resolve } from 'core-js/fn/promise'
import { createStore } from 'vuex'

export default createStore({
  //全局状态初始化
  state: {
    count:1,

  },
  //计算state
  getters:{

  },
  //更新状态的方法,更新的唯一方法commit mutations
  mutations: {
    setCount(state,num){
      state.count = num
    }

  },
  //可以异步操作,可以返回promise,更改数据还是传递到mutations去更改
  actions: {
    setcountPromise(context,num){
      return new Promise((resolve,reject)=>{
        if(num>100){
          reject("值不能大于100")
        }else{
          context.commit("setCount",num)
          resolve
        }
      })

    }
  },
  //数据比较多,分模块
  modules: {
  }
})

3)Vuex的读取和更改状态

不分模块

import { createStore } from 'vuex'

export default createStore({
  //全局状态初始化
  state: {
    count:1,

  },
  //计算state
  getters:{
    countStatus(state){
      return state.count>=1
    }

  },
  //更新状态的方法,更新的唯一方法commit mutations
  mutations: {
    setCount(state,num){
      state.count = num
    }

  },
  //可以异步操作,可以返回promise,更改数据还是传递到mutations去更改
  actions: {
    setcountPromise(context,num){
      return new Promise((resolve,reject)=>{
        if(num>100){
          reject("值不能大于100")
        }else{
          context.commit("setCount",num)
          resolve
        }

      })

    }
  },
  //数据比较多,分模块
  modules: {
  }
})

分模块读取状态

3、路由守卫和登录

router.js

router.beforeEach((to,from,next)=>{
  const uInfo = store.state.uInfo.userinfo
  if(!uInfo.username){
    if(to.path==="/login"){
      next()
      return
    }
    //未登录
    next("/login")

  }else{
    next()
  }
})

login.vue



    const handleLogin = ()=>{
      store.commit('setUserInfo',data.loginData)
      localStorage.setItem("loginData",JSON.stringify(data.loginData))
      router.push({
        path:"/user"
      })
    }

userinfo.state.js

export default{
  state:{
    userinfo:(localStorage.getItem("loginData")&&JSON.parse(localStorage.getItem("loginData")))||{}
  },
  mutations:{
    setUserInfo(state,uInfo){
      state.userinfo= uInfo
    }
  }
}

4、登录状态存储和退出登录

    const loginOut = () => {
      localStorage.removeItem("loginData")
      store.commit("setUserInfo", {})
      router.push(
        {
          path: "/login"
        })
    }

三、axios封装和登录完善

1、axios安装

npm install axios --save

2、service.js封装

import axios from "axios"
import { ElLoading, ElMessage } from "element-plus"
import store from "../store/index.js"

let loadingObj = null

const Service = axios.create({
  timeout: 5000,
  baseURL: 'http://localhost:8080',
  headers: {
    "Content-type": "application/json;charset=utf-8",
    "Authorization":store.state.uInfo.userinfo.token
  }
})

//请求拦截
Service.interceptors.request.use(config => {
  loadingObj = ElLoading.service({
    lock: true,
    text: 'loading',
    background: 'rgba(0,0,0,0.7)'
  })
  return config
})


//响应拦截
Service.interceptors.response.use(response => {
  console.log(response)
  loadingObj.close()
  const data = response.data 
  if (!data.data) {
    //请求出错
    ElMessage.error(data.meta.msg || "服务器出错")
    return data
  }
  return response.data
}, error => {
  loadingObj.close()
  ElMessage({
    message: "服务器错误",
    type: error,
    duration: 2000
  })
})
//post 请求
export const post = config => {
  return Service({
    ...config,
    method: "post",
    data: config.data

  })
}
//get 请求
export const get = config => {
  return Service({
    ...config,
    method: "get",
    params: config.data
  })
}
//put 请求
export const put = config => {
  return Service({
    ...config,
    method: "put",
    data: config.data
  })
}

//delet 请求
export const del= config => {
  return Service({
    ...config,
    method: "delete",
  })
}


3、request.js封装登录接口

export const loginApi=data=>{
  return post({
    url:"/login",
    data
  })
}

4、Login.vue登录代码完善

    const handleLogin = () => {
      //请求后台接口
      //默认用户:admin/123456
      loginApi(data.loginData).then(res => {
        if (res.data) {
          store.commit('setUserInfo', data.loginData)
          localStorage.setItem("loginData", JSON.stringify(data.loginData))
          router.push({
            path: "/login"
          })

        }
      })

    }

四、首页

1、index.vue

<template>
  <div>
    <h1>欢迎来到用户管理系统</h1>
  </div>
</template>
<script>
export default {
  name: "首页"
}
</script>

2、Login.vue登录跳转首页

    const handleLogin = () => {
      //请求后台接口
      //默认用户:admin/123456
      loginApi(data.loginData).then(res => {
        console.log(res)
        // if (res.data) {
          store.commit('setUserInfo', data.loginData)
          localStorage.setItem("loginData", JSON.stringify(data.loginData))
          router.push({
            path: "/"
          })

        // }
      })

    }

3、router.js

  {
    path: '/',
    name: 'layout',
    component: LayOut,
    redirect: "/index",
    children: [
      {
        path: "/index",
        name: "index",
        component: () => import("../views/pages/index.vue")
      },
      ]
      }

4、Layout 布局和导航

<template>
  <div class="common-layout">
    <el-container>
      <el-header class="common-header flex-float">
        <div class="flex">
          <img class="logo" src="../../assets/logo.png" alt="">
          <h1 class="title">商铺后台管理系统</h1>
        </div>
        <el-button type="danger" @click="loginOut">退出</el-button>

      </el-header>
      <el-container>
        <el-aside class="common-aside" width="200px">
          <el-menu background-color="none" text-color="#fff" :router="true">
            <el-sub-menu index="1">
              <template #title>
                <el-icon>
                  <location />
                </el-icon>
                <span>账号管理</span>
              </template>
              <el-menu-item-group>
                <el-menu-item index="/user">账号列表</el-menu-item>
              </el-menu-item-group>
            </el-sub-menu>
            <el-sub-menu index="2">
              <template #title>
                <el-icon>
                  <location />
                </el-icon>
                <span>角色管理</span>
              </template>
              <el-menu-item-group>
                <el-menu-item index="/roles">角色列表</el-menu-item>
              </el-menu-item-group>
            </el-sub-menu>

          </el-menu>

        </el-aside>
        <el-main>
          <router-view />
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>
<script>
import { useStore } from 'vuex'
import { useRouter } from 'vue-router'
export default {
  name: "layout",
  setup() {
    const store = useStore()
    const router = useRouter()
    const loginOut = () => {
      localStorage.removeItem("loginData")
      store.commit("setUserInfo", {})
      router.push(
        {
          path: "/login"
        })
    }
    return {
      loginOut

    }
  }

}
</script>
<style>
.el-container {
  height: 100vh;
}

.common-header {
  background: rgb(47, 44, 54);
  display: flex;
}

.common-aside {
  background: rgb(88, 85, 96);
}

.logo {
  width: 80px;
}

.title {
  color: #fff;
}
.el-main{
  background: #efefef;

}
</style>

五、账号管理

用户列表查询/分页、新建用户/编辑用户/表单正则、用户状态更改、删除

1、router.js

2、UserList.vue

<template>
  <div>
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>账号列表</el-breadcrumb-item>
    </el-breadcrumb>
    <div class="page_content">
      <div class="flex">
        <div class="input_box">
          <el-input v-model="keyWord" placeholder="搜索关键字" class="" clearable>
            <template #append>
              <el-button @click="searchList"><el-icon>
                  <Search />
                </el-icon></el-button>
            </template>
          </el-input>
        </div>

        <el-button type="primary" @click="addUser">新建用户</el-button>
      </div>

    </div>
    <!-- 表格 -->
    <el-table :data="userList" style="width: 100%">
      <el-table-column prop="username" label="姓名" width="180" />
      <el-table-column prop="email" label="邮箱" width="180" />
      <el-table-column prop="mobile" label="电话" width="180" />
      <el-table-column prop="role_name" label="角色" />
      <el-table-column prop="mg_state" label="状态">
        <template #default="scope">
          <el-switch v-model="scope.row.mg_state" @change="switchChange(scope.row)" />
        </template>
      </el-table-column>
      <el-table-column label="操作">
        <template #default="scope">
          <el-button type="primary" @click="editRow(scope.row)">编辑</el-button>
          <el-button type="danger" @click="deleteRow(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination background layout="prev, pager, sizes, next, jumper, ->, total"
      v-model:page-size="searchParams.pagesize" v-model:currentPage="searchParams.pagenum" :page-sizes="[2, 3, 4, 5]"
      @size-change="searchList" @current-change="searchList" :total="total">
    </el-pagination>
    <!-- 添加弹窗 -->
    <el-dialog title="新增用户" v-model="dialogFormVisible">
      <el-form :model="formData" :rules="rules" ref="userForm">
        <el-form-item label="活动名称" prop="username">
          <el-input v-model="formData.username" placeholder="请输入用户名称"></el-input>
        </el-form-item>
        <el-form-item label="用户密码" prop="password">
          <el-input type="password" v-model="formData.password" placeholder="请输入用户密码"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="formData.email" placeholder="请输入用户邮箱"></el-input>
        </el-form-item>
        <el-form-item label="手机号" prop="mobile">
          <el-input v-model="formData.mobile" placeholder="请输入用户手机号"></el-input>
        </el-form-item>
      </el-form>
      <div class="flex">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="submitForm(userForm)">确 定</el-button>
      </div>
    </el-dialog>

    <!-- 编辑弹窗 -->
    <el-dialog title="编辑用户" v-model="dialogFormEVisible">
      <el-form :model="formData2" :rules="rules2" ref="userForm2">
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="formData2.email" placeholder="请输入用户邮箱"></el-input>
        </el-form-item>
        <el-form-item label="手机号" prop="mobile">
          <el-input v-model="formData2.mobile" placeholder="请输入用户手机号"></el-input>
        </el-form-item>
      </el-form>
      <div class="flex">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="submitEForm(userForm2)">确 定</el-button>
      </div>
    </el-dialog>

  </div>
</template>
<script>
import { onMounted, reactive, toRefs, ref } from 'vue';
import { userListApi, userAddApi, userChangeStateApi, userChangeInfoApi, userDeleteApi } from "@/util/request.js"

export default {
  name: "users",
  setup() {
    const data = reactive({
      keyWord: "",
      searchParams: {
        query: "",
        pagesize: 5,
        pagenum: 1
      },
      total: 0,
      userList: [],
      dialogFormVisible: false,
      dialogFormEVisible: false,
      formData: {
        username: '',
        password: '',
        email: '',
        mobile: ''

      },
      formData2: {
        email: '',
        mobile: '',
        id: ''
      },
      rules: {
        username: [
          { required: "true", message: "此项为必填项", trigger: "blur" },
        ],
        password: [
          { required: "true", message: "此项为必填项", trigger: "blur" },
        ],
      }

    })
    const searchList = () => {

      userListApi(data.searchParams).then(res => {
        if (res.data) {
          console.log("用户数据", res)
          data.userList = res.data.users
          data.total = res.data.total
        }
      })
    }
    const userForm = ref()
    const userForm2 = ref()

    onMounted(() => {
      {
        data.userList = [
          {
            username: 'Tom',
            email: 'Tom@qq.com',
            mobile: '234143324',
            role_name: '普通操作员'

          },
          {
            username: 'Tom',
            email: 'Tom@qq.com',
            mobile: '234143324',
            role_name: '普通操作员'
          },
          {
            username: 'Tom',
            email: 'Tom@qq.com',
            mobile: '234143324',
            role_name: '普通操作员'
          },
          {
            username: 'Tom',
            email: 'Tom@qq.com',
            mobile: '234143324',
            role_name: '普通操作员'
          },
        ]

      }
    })
    const addUser = () => {
      data.dialogFormVisible = true
    }
    const submitForm = (formEl) => {
      formEl.validate(res => {
        if (!res) {
          return
        }
        userAddApi(data.formData).then(res => {
          if (res.data) {
            data.dialogFormVisible = false
            data.formData = {
              username: '',
              password: '',
              email: '',
              mobile: ''
            }
            searchList()
          }

        })
        alert("通过")
      })
    }
    const submitEForm = (formEl) => {
      formEl.validate(res => {
        if (!res) {
          return
        }
        userChangeInfoApi(data.formData2).then(res => {
          if (res.data) {
            data.dialogFormEVisible = false
            searchList()


          }
        })


      })
    }
    const switchChange = row => {
      userChangeStateApi(row).then(res => {
        if (res.data) {
          searchList()
        }
      })
    }

    const editRow = row => {
      const { email, mobile, id } = row
      data.dialogFormEVisible = true
      data.formData2.email = email
      data.formData2.mobile = mobile
      data.formData2.id = id

    }
    const deleteRow = row => {
      userDeleteApi(row).then(res => {
        if (res.data) {
          searchList()
        }
      })
    }

    return {
      ...toRefs(data),
      searchList,
      addUser,
      submitForm,
      submitEForm,
      userForm,
      switchChange,
      editRow,
      userForm2,
      deleteRow
    }
  }
}
</script>
<style scoped>
.input_box {
  width: 200px;
  margin-right: 15px;
}
</style>

3、request.js添加账号管理API

//获取用户列表
export const userListApi=data=>{
  return get({
    url:"/users",
    data
  })
}

//添加用户
export const userAddApi=data=>{
  return post({
    url:"/users",
    data
  })
}

//更新状态
export const userChangeStateApi=data=>{
  return put({
    url:`users/${data.id}/state/${data.mg_state}`,
    data
  })
}

//更改用户信息
export const userChangeInfoApi=data=>{
  return put({
    url:`users/${data.id}`,
    data
  })
}

//删除用户信息
export const userDeleteApi=data=>{
  return del({
    url:`users/${data.id}`,
  })
}

六、角色管理

1、列表、新建、编辑、删除 API

 

 2、角色新增和编辑提交

3、判断弹窗是添加还是编辑

 4、编辑角色

5、清除角色表单

6、删除角色

 七、商品管理

1、商品查询

request.js

goods.vue

 2、商品状态转换

 

  • 36
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值