Vue3商城项目实战
本文介绍Vue3实现商品管理后台,内容包括项目构建、登录、账号管理、角色管理和商品管理等
·
目录
一、Vue3环境和项目创建
一、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、商品状态转换
更多推荐
已为社区贡献1条内容
所有评论(0)