快速搭建SpringBoot3+Vue3+ElementPlus管理系统

快速搭建SpringBoot3+Vue3管理系统

  • 前端项目搭建(默认开发环境:node20,Jdk17)
    • 创建项目并下载依赖--执行以下命令

前端项目搭建(默认开发环境:node20,Jdk17)

创建项目并下载依赖–执行以下命令

  1. 创建项目
yarn create vite admin-vue --template

2.下载依赖(也可按需下载)

yarn add echarts --save
yarn add vue-router --save
yarn add element-plus --save
yarn add axios --save
yarn add sass--save

3.导入IDEA
在这里插入图片描述
4.配置快速启动
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
也可以在终端中使用命令启动,如下:
在这里插入图片描述
在这里插入图片描述

5.配置Axios
在src目录下新建axios目录,并创建request.js文件

import axios from 'axios'
const request = axios.create({timeout: 20000,baseURL: 'http://127.0.0.1:8080',
})
export default request

6.配置路由
在src目录下新建axios目录,并创建router.js文件

import {createRouter,createWebHashHistory} from "vue-router";
// 在router.js文件中创建路由实例对象
const router=createRouter({history:createWebHashHistory(),routes:[//路由规则]
})
// 输出实例对象
export default router;

7.挂载ElementPlus和路由
在main.js中挂载

import { createApp } from 'vue'
import './style.css'
// 导入element-plu
import ElementPlus from 'element-plus'
// 导入路由
import router from './router/router.js'
// 导入css样式
import 'element-plus/dist/index.css'
import App from './App.vue'
// 挂载element-plu和router
createApp(App).use(ElementPlus).use(router).mount('#app')

8.绝对路径配置
在vite.config.js中配置绝对路径别名–@

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'
// https://vite.dev/config/
export default defineConfig({plugins: [vue()],// 绝对路径别名resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}}
})

8.创建布局组件
8.1 顶部水平导航

<script setup>
import {onMounted, ref} from "vue";
import router from "@/router/router.js";
// 图片地址
const url=ref("/element-plus-logo.svg")
// 将用户信息从浏览器缓存中取出
const user=ref(JSON.parse(localStorage.getItem('xm-user') || '{}'))
// 退出登录
const logout=()=>{localStorage.removeItem('xm-user');router.push("/login");
}
</script><template><!--  顶部导航栏--><el-menurouterclass="el-menu-demo"mode="horizontal"text-color="grey"background-color="white":ellipsis="false"><el-menu-item index="1"><el-image style="width: 100px; height: 100px" :src="url"  />|商品信息管理系统</el-menu-item><el-menu-item index="/">首页</el-menu-item><el-menu-item index="/TeachingCenter"><span>教学中心</span></el-menu-item><el-menu-item index="/TeachingCenter"><span>教学资源</span></el-menu-item><el-menu-item index="/Goods"><span>商品信息管理</span></el-menu-item><el-menu-item index="/TeachingCenter"><span>教学实践</span></el-menu-item><el-menu-item index="/TeachingCenter"><span>问答社区</span></el-menu-item><el-sub-menu index="10" v-if="user.id"><template #title><img :src="user.avatar" width="40px" height="40px" style="border-radius: 50%;margin-right: 5px" />{{ user.name }}</template><el-menu-item index="/manage/personal">个人中心</el-menu-item><el-menu-item index="10-2">修改密码</el-menu-item><el-menu-item index="10-3" @click="logout">退出登录</el-menu-item></el-sub-menu><el-menu-item index="/login" v-else><el-button type="primary" round>登录</el-button></el-menu-item></el-menu>
</template><style scoped>
.el-menu--horizontal > .el-menu-item:nth-child(1) {margin-right: auto;
}
</style>

8.2 侧边导航栏

<script setup>import {Goods, House, Promotion, User,Avatar} from "@element-plus/icons-vue";
</script><template><!--      侧边导航栏--><el-aside width="200px" ><el-menu default-active="1" style="min-height: 100%;overflow-x: hidden;"class="el-menu"background-color="white"text-color="grey"router><!--            <div style="height: 60px; line-height: 60px; text-align: center;"><img src="../assets/logo.png" alt="" style="width: 20px;position: relative; top: 5px;margin-right: 5px;"><b style="color: white;">后台管理系统</b></div>--><el-menu-item index="/manage/index" ><el-icon><House /></el-icon>主页</el-menu-item><el-menu-item index="/manage/goods"><el-icon><Goods /></el-icon><span>商品信息管理</span></el-menu-item><el-sub-menu index="2"><template #title><el-icon><User /></el-icon>用户管理</template><el-menu-item ><el-icon><User /></el-icon>用户信息</el-menu-item><el-menu-item index="/manage/admin"><el-icon><Avatar /></el-icon><span>管理员信息</span></el-menu-item></el-sub-menu><el-menu-item ><el-icon><Promotion /></el-icon>订单管理</el-menu-item></el-menu></el-aside>
</template><style scoped>
.el-menu{height: 100vh;
}
</style>

8.3 底部导航栏

<script setup></script><template><p alog-group="copy">©2024 商品信息管理系统<a rel="noopener" href="//www.baidu.com/duty/">使用必读</a><a rel="noopener" href="https://tieba.baidu.com/tb/eula.html">协议</a><a href="https://tieba.baidu.com/tb/cms/tieba-fe/tieba_promise.html">隐私政策</a><a rel="noopener" href="//tieba.baidu.com/hermes/feedback">投诉反馈</a>信息网络传播视听节目许可证 0110516 违法和不良信息举报电话:400-921-5119<a rel="noopener" href="http://net.china.cn/chinese/index.htm" target="_blank"><img src="//tb2.bdstatic.com/tb/static-common/img/net_7a8d27a.png" width="20"></a><a rel="noopener" href="http://www.bj.cyberpolice.cn/index.htm" target="_blank"><img title="首都网络110报警服务" src="//tb2.bdstatic.com/tb/static-common/img/110_97c9232.png" width="20"></a></p>
</template><style scoped></style>

9.创建整体布局,引用子组件

<script setup>
// 注册子组件
import Header from "@/components/Header.vue";
import Footer from "@/components/Footer.vue";
import Aside from "@/components/Aside.vue";
import {ref} from "vue";
import {useRouter} from "vue-router";
// 获取全局路由
const router=useRouter();
// 判断是否登录
// 将用户信息从浏览器缓存中取出
const user=ref(JSON.parse(localStorage.getItem('xm-user') || '{}'))
if (user.value.name==null){//如果没有数据router.push("/login");
}
</script><template><!-- container布局 --><div class="common-layout"><el-container><!--      顶部导航栏--><el-header><Header></Header></el-header><el-container><!--        侧边导航栏--><el-aside width="200px"><Aside></Aside></el-aside><el-container><!--          内容显示区域--><el-main><!--            路由视图--><router-view></router-view></el-main><!--          底部导航栏--><el-footer><Footer></Footer></el-footer></el-container></el-container></el-container></div>
</template><style scoped></style>

10.创建商品信息组件:Goods.vue

<script setup>
import {onMounted, ref} from "vue";
import request from "../../axios/request.js";
import {ElMessage, ElMessageBox} from "element-plus";
import {Delete,Edit,Message,Search,Plus,House,User,Promotion,Goods,Download,UploadFilled
} from "@element-plus/icons-vue";const list = ref([])
// 查询条件
const name = ref("")
// 模态框是否显示
const dialogVisible = ref(false);
// 商品的信息
const goods = ref({id: "",name: "",price: "",intro: "",amount: "",goodsType: "",imgurl: "",remark: "未上架"
})// 表单校验规则
const rules = {name: [{required: true, message: "请输入名称", trigger: "blur"}],price: [{required: true, message: "请输入价格", trigger: "blur"}],intro: [{required: true, message: "请输入简介", trigger: "blur"}],amount: [{required: true, message: "请输入库存", trigger: "blur"}],goodsType: [{required: true, message: "请选择类别", trigger: "blur"}],remark: [{required: true, message: "请选择状态", trigger: "blur"}]
}// 表单引用
const goodsForm = ref();
// 分页参数
const pageNum = ref(1)
const pageSize = ref(10)
const total = ref(0)// 保存选中的数据
const multipleSelection = ref([])// 查询商品信息
const getList = () => {request({url: '/goods/page',method: 'get',params: {page: pageNum.value,limit: pageSize.value,goodsName: name.value,}}).then((res) => {// console.log(res)list.value = res.data.data;total.value = res.data.count;// name.value=""})
}// 改变每页数据量
const handleSizeChange = (pSize) => {// console.log(pSize+`items per page`)pageSize.value = pSize;getList()
}
// 改变当前页
const handleCurrentChange = (pNum) => {// console.log(`current page: `+pNum)pageNum.value = pNum;getList()
}
// 根据id删除
const del = (id) => {// console.log(id)ElMessageBox.confirm('是否确认删除?','Warning',{confirmButtonText: 'OK',cancelButtonText: 'Cancel',type: 'warning',}).then(() => {request({url: '/goods/del',method: 'get',params: {id: id}}).then(res => {if (res.data.code == 200) {ElMessage({type: 'success',message: '删除成功!',})} else {ElMessage({type: 'error',message: '删除失败!',})}getList()})}).catch(() => {ElMessage({type: 'warning',message: '取消删除!',})})
}// 搜索
const search = () => {getList()
}// 新增
const add = () => {dialogVisible.value = true
}
// 数据提交
const save = () => {
//   校验表单是否合法goodsForm.value.validate((flag, err) => {if (flag) {//验证通过request({url: '/goods/save',method: 'post',data: goods.value}).then(res => {getList()goods.value = ""dialogVisible.value = falseElMessage({type: 'success',message: '保存成功',})})} else {ElMessage({type: 'error',message: '验证没通过!',})}})
}
// 修改
const edit = (item) => {// 深拷贝const newObj = Object.assign({}, item)dialogVisible.value = truegoods.value = newObj
}
// 获取已选择的数据
const handleSelectionChange = (val) => {// console.log(val)multipleSelection.value = val;
}
// 批量删除
const delBatch = () => {
// 获取需要删除的数据的id--数组let ids = multipleSelection.value.map(item => item.id);// console.log("ids",ids)if (ids.length != 0) {ElMessageBox.confirm('是否确认删除?','Warning',{confirmButtonText: 'OK',cancelButtonText: 'Cancel',type: 'warning',}).then(() => {request({url: '/goods/delBatch',method: 'post',data: ids}).then(res => {ElMessage({type: 'success',message: '删除成功!',})getList()// console.log(res)})}).catch(() => {ElMessage({type: 'warning',message: '取消删除!',})})} else {ElMessage({type: 'error',message: '请先选择数据!'})}
}
// 导入成功后重新加载数据
const handleExcelImportSuccess = () => {getList();ElMessage({type: 'success',message: '上传成功!',})
}// 导出数据
const exp = () => {window.open(`http://192.168.31.242:8080/goods/export`)
}// excle导入前验证-->文件类别、文件大小
const cheackExcle = (rawFile) => {// console.log(rawFile.size)// 获取文件后缀let testmsg = rawFile.name.substring(rawFile.name.lastIndexOf('.') + 1)if (testmsg !== 'xlsx') {ElMessage.error('只能上传后缀为.xlsx的excle文件')return false} else if (rawFile.size / 1024 / 1024 > 2) {ElMessage.error('文件大小不能超过2M')return false}return true
}// 图片上传之前的钩子--》判断文件是否是图片、图片大小是否符合要求
const beforeAvatarUpload = (rawFile) => {console.log("类型:", rawFile.type)console.log(rawFile.type !== 'image/png');// 获取文件后缀let testmsg = rawFile.name.substring(rawFile.name.lastIndexOf('.') + 1)console.log("后缀:", testmsg)// 判断文件类型const extension = testmsg === 'jpg' || testmsg === 'png' || testmsg === 'jpeg' || testmsg === 'gif'if (!extension) {//不符合要求ElMessage.error('图片必须是jpg/jpeg/png格式!')return false}// 校验文件大小if (rawFile.size / 1024 / 1024 > 2) {ElMessage.error('图片大小不能超过2MB!')return false}return true
}
// 图片上传后的钩子函数-->回写图片地址
const handleAvatarSuccess = (res) => {console.log(res)goods.value.imgurl = res.msg
}//生命周期函数
onMounted(() => {getList();
})
</script><template><!--  数据表格--><el-form-item label="名称" style="margin-top: 20px"><el-input v-model="name" style="width: 200px" placeholder="请输入商品名称"></el-input><el-button type="primary" :icon="Search" style="margin-left: 10px" @click="search">搜索</el-button><el-button type="primary" :icon="Plus" style="margin-left: 10px" @click="add">新增</el-button><el-button type="danger" :icon="Delete" style="margin-left: 10px" @click="delBatch">批量删除</el-button><el-upload :action="'http://' + '192.168.31.242:8080/goods/import'":show-file-list="false"accept="xlsx":before-upload="cheackExcle":on-success="handleExcelImportSuccess"style="display: inline-block"><el-button type="primary" style="margin-left: 10px" :icon="UploadFilled">导入</el-button></el-upload><el-button type="warning" style="margin-left: 10px" :icon="Download" @click="exp">导出</el-button></el-form-item><el-table :data="list" width="100%" stripe @selection-change="handleSelectionChange"><el-table-column type="selection" width="55"/><el-table-column type="index" label="序号" width="80" :index="1"></el-table-column><!--  <el-table-column prop="id" label="ID"></el-table-column>--><el-table-column prop="name" label="商品名称"></el-table-column><el-table-column prop="price" label="单价"></el-table-column><el-table-column prop="intro" label="简介" width="100" show-overflow-tooltip></el-table-column><el-table-column prop="amount" label="库存"></el-table-column><el-table-column prop="goodsType" label="类别"></el-table-column><el-table-column prop="remark" label="状态"></el-table-column><el-table-column label="图片" width="120"><template #default="scope"><el-image style="width: 100px; height: 80px; padding: 5px;" :src="scope.row.imgurl" :zoom-rate="1.2":max-scale="7" :min-scale="0.2" :preview-src-list="[scope.row.imgurl]" :preview-teleported="true"/></template></el-table-column><el-table-column label="操作" width="300" fixed="right"><template #default="scope"><el-button type="primary" :icon="Edit" @click="edit(scope.row)">修改</el-button><el-button type="danger" :icon="Delete" @click="del(scope.row.id)">删除</el-button></template></el-table-column></el-table>
<!--  数据分页--><div style="padding: 10px"><el-paginationv-model:current-page="pageNum"v-model:page-size="pageSize":page-sizes="[10, 20, 30, 40]"size="large"backgroundlayout="total, sizes, prev, pager, next, jumper":total="total"@size-change="handleSizeChange"@current-change="handleCurrentChange"/></div><!--模态框--><el-dialogv-model="dialogVisible":title="goods.id ? '修改商品' : '新增商品'"width="500"ref="goodsForm"><el-form :model="goods" :rules="rules" ref="goodsForm"><el-form-item label="商品名称" prop="name"><el-input v-model="goods.name" autocomplete="off"/></el-form-item><el-form-item label="商品单价" prop="price"><el-input v-model="goods.price" autocomplete="off"/></el-form-item><el-form-item label="商品简介" prop="intro"><el-input v-model="goods.intro" autocomplete="off"/></el-form-item><el-form-item label="商品库存" prop="amount"><el-input v-model="goods.amount" autocomplete="off"/></el-form-item><el-form-item label="商品类别" prop="goodsType"><el-select v-model="goods.goodsType" placeholder="请选择商品类别"><el-option label="手机" value="手机"/><el-option label="电脑" value="电脑"/><el-option label="水果" value="水果"/></el-select></el-form-item><!--  单选框--><el-form-item label="状态" prop="remark"><el-radio-group v-model="goods.remark"><el-radio value="上架">上架</el-radio><el-radio value="未上架">未上架</el-radio></el-radio-group></el-form-item><!--      图片上传--><el-form-item label="商品图片"><el-uploadclass="avatar-uploader":action="'http://' + '192.168.31.242:8080/files/upload'":show-file-list="false":on-success="handleAvatarSuccess":before-upload="beforeAvatarUpload"><img v-if="goods.imgurl" :src="goods.imgurl" class="avatar" style="width: 300px"/><el-icon v-else class="avatar-uploader-icon"><Plus/></el-icon></el-upload></el-form-item></el-form><template #footer><div class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="save">提交</el-button></div></template></el-dialog>
</template><style scoped>
.avatar-uploader .el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);
}.avatar-uploader .el-upload:hover {border-color: var(--el-color-primary);
}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;
}
</style>

11.创建首页组件:Index.vue

<script setup>
import { reactive, onMounted,ref} from 'vue'
import { Memo } from '@element-plus/icons-vue'
import * as echarts from 'echarts'
import request from "@/axios/request.js";// 格式化日期时间
const formattedDate=()=>{const date = new Date();const year = date.getFullYear();const month = (date.getMonth() + 1).toString().padStart(2, '0');const day = date.getDate().toString().padStart(2, '0');const hour = date.getHours().toString().padStart(2, '0');const minute = date.getMinutes().toString().padStart(2, '0');const second = date.getSeconds().toString().padStart(2, '0');const formattedDate = `${year}-${month}-${day} ${hour}:${minute}:${second}`;console.log(formattedDate);return formattedDate;}
let date=formattedDate();
const loginInfo=ref({loginTime: date,loginPlace: "云南-昆明"
})
const data = reactive({user: JSON.parse(localStorage.getItem('xm-user') || '{}'),noticeData: []
})// 查询公告信息
const getNoticeList = () => {request({url: '/notice/page',method: 'get',params: {page: 1,limit: 4,}}).then((res) => {// console.log(res)data.noticeData = res.data.data;})
}// 商品数量
const total=ref(0)
// 将用户信息从浏览器缓存中取出
const user=ref(JSON.parse(localStorage.getItem('xm-user') || '{}'))
// 查询商品信息
const getList = () => {request({url: '/goods/page',method: 'get',params: {page: 1,limit: 10,goodsName: name.value,}}).then((res) => {// console.log(res)// list.value = res.data.data;total.value = res.data.count;console.log(total.value)// name.value=""})
}
// 图表1:月度销售额
const initCharts1 = () => {const myChart = echarts.init(document.getElementById('salesVolume'))myChart.setOption({color: ['#1493fa'],title: { text: '2024年月度销售额' },xAxis: {data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],name: '月份',axisLabel: {interval: 0},},yAxis: {name: '单位(千万元)',},grid: {left: '3%',right: '8%',bottom: '5%',containLabel: true,},legend: {},series: [{data: [6, 7, 8.5, 8, 9, 10, 13, 12, 10, 16, 15, 14],type: 'line',name: '销售额',smooth: true,label: {show: true,position: 'top',}}]})// 图表自适应大小window.onresize = () => {myChart.resize()}
}
// 图表2:2024年订单数量
const initCharts2 = () => {const myChart = echarts.init(document.getElementById('orderQuantity'))myChart.setOption({title: { text: '2024年订单数量' },color: ['#1493fa'],grid: {left: '3%',right: '8%',bottom: '3%',containLabel: true,},xAxis: {type: 'category',data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],name: '月份',// 类目轴中在 boundaryGap 为 true 的时候有效,可以保证刻度线和标签对齐axisTick: {alignWithLabel: true,},axisLabel: {interval: 0,rotate: 45 // 设置刻度标签旋转角度为45度},},legend: {},yAxis: {name: '单位(个)',},series: [{data: [400, 450, 300, 230, 250, 300, 400, 350, 160, 350, 380, 400],type: 'bar',barWidth: '60%',name: '订单总数',label: {show: true,position: 'top',}}]})// 图表自适应大小window.onresize = () => {myChart.resize()}
}//生命周期函数
onMounted(() => {getList();getNoticeList()// 初始化图表initCharts1()initCharts2()
})
</script><template><el-row :gutter="20"><!--    登录信息--><el-col :span="6"><el-card class="box-card">
<!--      <el-card class="box-card"><template #header><div class="card-header"><img :src="user.avatar" style="width: 40px; height: 40px;">
&lt;!&ndash;            <el-avatar class="avatar" src="@/assets/img.png" shape="square" :size="40"> </el-avatar>&ndash;&gt;<span style="font-size: 24px;">{{ user.name }} </span></div></template><div class="info"><p>登录时间:{{ loginInfo.loginTime }}</p><p>登录地点:{{ loginInfo.loginPlace }}</p></div></el-card>--><div style="display: flex"><div class="card" style="flex: 50%; height: 400px;width: 200px"><div style="font-weight: bold; font-size: 18px; padding: 10px 0 30px 10px">系统公告</div><el-timeline style="max-width: 600px"><el-timeline-itemv-for="(item, index) in data.noticeData":key="index":timestamp="item.time">{{ item.title }}</el-timeline-item></el-timeline></div><div style="flex: 50%"></div></div></el-card></el-col><!-- 单月统计信息展示 --><el-col :span="18" ><el-card class="box-card"><template #header><div class="card-header">6月统计信息</div></template><div class="info" style="height: 340px"><el-row :gutter="24"><!-- 商品数量 --><el-col :span="8"><div class="card-container"><div class="card-left-container" style="background-color: #EEAD0E;"><el-icon :size="90"><Memo /></el-icon></div><div class="card-right-container"><p class="number">{{ total }}</p><p>商品数量()</p></div></div></el-col><!-- 商品分类数量 --><el-col :span="8"><div class="card-container"><div class="card-left-container" style="background-color: #AB82FF;"><el-icon :size="90"><Memo /></el-icon></div><div class="card-right-container"><p class="number">20</p><p>商品分类数量()</p></div></div></el-col><!-- 用户访问次数 --><el-col :span="8"><div class="card-container"><div class="card-left-container" style=" background-color: #63B8FF;"><el-icon :size="90"><Memo /></el-icon></div><div class="card-right-container"><p class="number">121</p><p>用户访问次数()</p></div></div></el-col></el-row></div></el-card></el-col></el-row><!-- 图表区域 --><el-row :gutter="20" style="margin-top: 10px"><el-col :span="12"><!-- 通过折线图展示2024年月度销售额 --><el-card class="box-card"><div id="salesVolume" style="width: auto; height:400px;"></div></el-card></el-col><el-col :span="12"><!-- 通过柱状图展示2024年订单数量 --><el-card class="box-card"><div id="orderQuantity" style="width: auto; height:400px;"></div></el-card></el-col></el-row>
</template><style scoped></style>

12.创建登录组件:Login.vue

<script setup>
import {ref} from "vue";
import {User,Lock} from "@element-plus/icons-vue";
import request from "@/axios/request.js";
import router from "@/router/router.js";
import {ElMessage} from "element-plus";
import {useRouter} from "vue-router";
// const router=useRouter()const user=ref({username:"",password:"",role:""
})
const rules={username:[{required:true,message:"请输入用户名",trigger:"blur"}],password:[{required:true,message:"请输入密码",trigger:"blur"}],role:[{required:true,message:"请选择角色",trigger:"blur"}]
}
// 表单引用
const formRef=ref()
// 登录验证
const login=()=>{formRef.value.validate((flag)=>{if(flag){request({url:"/login",method:"post",data:user.value}).then(res => {console.log(res)if (res.data.code==200){ElMessage.success("登录成功!")// 将用户信息保存到浏览器缓存中localStorage.setItem('xm-user', JSON.stringify(res.data.data))// 跳转首页router.push("/manage/index")}else {ElMessage.error(res.data.msg)}})}})
}</script><template><div class="login-container"><div class="login-box"><div style="font-weight: bold; font-size: 24px; text-align: center; margin-bottom: 30px; color: #1450aa">欢 迎 登 录</div><el-form ref="formRef" :model="user" :rules="rules"><el-form-item prop="username"><el-input :prefix-icon="User" size="large" v-model="user.username" placeholder="请输入账号"></el-input></el-form-item><el-form-item prop="password"><el-input show-password :prefix-icon="Lock" size="large" v-model="user.password" placeholder="请输入密码"></el-input></el-form-item><el-form-item prop="role"><el-select size="large" v-model="user.role" placeholder="请选择角色"><el-option value="ADMIN" label="管理员"></el-option></el-select></el-form-item><el-form-item><el-button size="large" type="primary" style="width: 100%" @click="login">登 录</el-button></el-form-item><div style="text-align: right">还没有账号?请 <router-link to="/register"><b style="color: dodgerblue">注册</b></router-link></div></el-form></div></div>
</template><style scoped>
.login-container {height: 100vh;overflow: hidden;display: flex;justify-content: center;align-items: center;background: linear-gradient(to top, #7f7fd5, #86a8e7, #91eae4);
}
.login-box {width: 350px;padding: 30px;border-radius: 5px;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);background-color: rgba(255, 255, 255, 0.5);
}
</style>

13.添加路由规则–router.js

import {createRouter,createWebHashHistory} from "vue-router";
// 在router.js文件中创建路由实例对象
const router=createRouter({history:createWebHashHistory(),routes:[//路由规则{path:"/",redirect:"/login"},{path:"/manage",component:()=>import('@/views/Manage.vue'),children:[//子路由{path:"index",component:()=>import('@/views/manage/Index.vue')},{path:"goods",component:()=>import('@/views/manage/Goods.vue')},{path:"admin",component:()=>import('@/views/manage/Admin.vue')},{path:"personal",component:()=>import('@/views/manage/Personal.vue')},{path:"password",component:()=>import('@/views/manage/Password.vue')},{path:"notice",component:()=>import('@/views/manage/Notice.vue')},]},{path:"/login",component:()=>import('@/views/Login.vue')},{path:"/404",component:()=>import('@/views/404.vue')},{path:"/register",component:()=>import('@/views/Register.vue')},{path: '/:pathMatch(.*)', redirect: '/404' }]
})
// 输出实例对象
export default router;

效果图:
在这里插入图片描述
首页:
在这里插入图片描述
商品信息管理:
在这里插入图片描述

完整项目可以从资源下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/487539.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Hadoop大数据音乐推荐系统的设计与实现

摘 要 各种主流的音乐平台都为用户提供了的大量的音乐&#xff0c;让他们时刻都能沉浸在音乐的海洋之中。然而&#xff0c;过多的音乐往往使用户眼花缭乱&#xff0c;很难发现他们真正所需要的。一套优秀的推荐系统&#xff0c;可以很好地解决这个问题&#xff0c;既能帮助用户…

IDEA遇到EasyConnect中的网络资源无法访问的问题

IDEA遇到EasyConnect中的网络资源无法访问的问题 摘要由CSDN通过智能技术生成 点击编辑IDEA的 启动配置&#xff0c;然后在启动器下面的新增一个请求参数然后重新启动项目&#xff0c; java.net.preferIPv4Stack true IDEA就能连接到EasyConnect代理的网络服务 wanshanyu_ 关…

IP研究 | 大数据洞察黄油小熊的爆火之路

一只来自泰国的小熊在国内红成了顶流。 今年&#xff0c;黄油小熊以烘焙店“打工人”的超萌形象迅速走红&#xff0c;2个月内火遍中国的社交媒体&#xff0c;泰国门店挤满飘洋过海求合影的中国粉丝&#xff0c;根据数说故事全网大数据洞察&#xff0c;黄油小熊2024年度的线上声…

分数求和ᅟᅠ        ‌‍‎‏

分数求和 C语言代码C 代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 输入n个分数并对他们求和&#xff0c;并用最简形式表示。所谓最简形式是指&#xff1a;分子分母的最大公约数为1&#xff1b;若最终结果的分母为…

5G中的随机接入过程可以不用收RAR?

有朋友提到了一种不用接收RAR的RA过程&#xff0c;问这个是怎么回事。其实在刚刚写过的LTM cell switch篇章中就有提到&#xff0c;这里把所有相关的内容整理如下。 在RACH-less LTM场景&#xff0c;在进行LTM cell switch之前就要先知道target cell的TA信息&#xff0c;进而才…

Ubuntu安装grafana

需求背景&#xff1a;管理服务器&#xff0c;并在线预警&#xff0c;通知 需求目的&#xff1a; 及时获取服务器状态 技能要求&#xff1a; 1、ubuntu 2、grafana 3、prometheus 4、https://img-home.csdnimg.cn/images/20230724024159.png?origin_urlhttps%3A%2F%2Fimg…

vue3获取、设置元素高度

前言 在web端常见的需求场景中&#xff0c;会经常遇到table表格需要根据页面可视区域使高度自适应的情况。 傻喵(作者本人)昨天在尝试使用vue3实现这个需求时&#xff0c;看了几篇网上写的回答&#xff0c;都不太全面&#xff0c;所以干脆自己写个总结吧.(第一次写&#xff0c…

美畅物联丨观看实时视频对服务器带宽有什么要求?

​随着互联网的迅猛发展&#xff0c;实时视频观看已然成为人们日常生活中不可或缺的一部分。不管是视频会议、在线教育&#xff0c;还是在线娱乐&#xff0c;实时视频都起到了极为重要的作用。不过&#xff0c;实时视频的流畅播放对服务器的带宽有着极高的要求。本文将深入探究…

MongoDB-固定集合(Capped Collection)

在 MongoDB 中&#xff0c;固定集合&#xff08;Capped Collection&#xff09;是一种具有特殊属性的集合。固定集合具有一个固定的最大大小&#xff0c;并且一旦达到该大小时&#xff0c;最早插入的文档将会被自动删除&#xff0c;以便为新的文档腾出空间。固定集合的这种特性…

EasyExcel注解使用

上接《Springboot下导入导出excel》&#xff0c;本篇详细介绍 EasyExcel 注解使用。 1. ExcelProperty value&#xff1a;指定写入的列头&#xff0c;如果不指定则使用成员变量的名字作为列头&#xff1b;如果要设置复杂的头&#xff0c;可以为value指定多个值order&#xff…

yolo-V3

1、研究背景及意义 1&#xff09;对yolo进行创新&#xff0c;准确度更高。 2、创新点 1&#xff09;主要是更换了主干网络&#xff0c;使用了多尺度特征融合。 3、网络结构 yolo-V3以Darket-Net-53为主干网络。网络输入一张尺寸为416416的图片&#xff0c;经过多层卷积分别…

零基础如何使用ChatGPT快速学习Python

引言 AI编程时代来临&#xff0c;没有编程基础可以快速上车享受时代的红利吗&#xff1f;答案是肯定的。本文旨在介绍零基础如何利用ChatGPT快速学习Python编程语言&#xff0c;开启AI编程之路。解决的问题包括&#xff1a;传统学习方式效率低、缺乏互动性以及学习资源质量参差…

重生之我在异世界学编程之C语言:枚举联合篇

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言正文枚举&#xff08;Enum&#xff0…

MYSQL索引的分类和创建

目录 1、聚簇索引和非聚簇索引 tips&#xff1a; 小问题&#xff1a;主键为什么建议使用自增id? 2、普通索引 &#xff08;常规索引&#xff09;(normal) 3、唯一索引&#xff08;UNIQUE &#xff09; 唯一索引和主键的区别&#xff1a; 唯一约束和唯一索引的区别&#…

Robust Depth Enhancement via Polarization Prompt Fusion Tuning

paper&#xff1a;论文地址 code&#xff1a;github项目地址 今天给大家分享一篇2024CVPR上的文章&#xff0c;文章是用偏振做提示学习&#xff0c;做深度估计的。模型架构图如下 这篇博客不是讲这篇论文的内容&#xff0c;感兴趣的自己去看paper&#xff0c;主要是分享环境&…

.NET 一款获取主机远程桌面端口的工具

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

【开源】A066—基于JavaWeb的农产品直卖平台的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看项目链接获取⬇️&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600个选题ex…

vue3+vite+ts 使用webrtc-streamer播放海康rtsp监控视频

了解webrtc-streamer webrtc-streamer 是一个使用简单机制通过 WebRTC 流式传输视频捕获设备和 RTSP 源的项目&#xff0c;它内置了一个小型的 HTTP server 来对 WebRTC需要的相关接口提供支持。相对于ffmpegflv.js的方案&#xff0c;延迟降低到了0.4秒左右&#xff0c;画面的…

C语言基础六:循环结构及面试上机题

Day06&#xff1a;循环结构 定义 代码的重复执行&#xff0c;就叫做循环 循环的分类 无限循环:其实就是死循环&#xff0c;程序设计中尽量避免无限循环。程序中的无限循环必须可控。有限循环:循环限定循环次数或者循环的条件。 循环的构成 循环条件循环体 当型循环的实现…

树莓集团:探索打造数字影像产业新发展

在当今数字化高速发展的时代背景下&#xff0c;树莓集团始终勇立潮头&#xff0c;坚定地踏上了探索打造产业新发展的非凡征程。 产业生态构建是树莓集团战略布局中的关键一环。集团积极与上下游企业开展广泛而深入的合作&#xff0c;建立起一个互利共赢、协同发展的产业生态系…