前言
- 这是一个适合初学Vue3的小白(比如本人,自学小白,目前大二,过完暑假就大三了,想要做一个项目,复习一些所学的内容)做的小型项目,项目是后台管理系统。所用涉及到的技术栈为Vue3、Vue–cli、Vue–router、Vuex、element–ui、echarts、axios、mysql、node.js其中***echarts、axios、mysql、node.js***只是简单的使用,不涉及复杂操作。***element–ui***里面的表单等,也没有进行相关检验,总体功能只是想让初学者有一个项目体验,或多或少美中不足。项目中的数据全是动态的,[若是没有node和mysql相关基础请去B站观看相关视频]黑马程序员Node.js全套入门教程,nodejs最新教程含es6模块化+npm+express+webpack+promise等_Nodejs实战案例详解_哔哩哔哩_bilibili。备注(不是打广告,本人是自学,没怎么写过文章,不进行商用等)。其中mysql下载教学在P59,相关sql语句以及在node中的使用在P60-P70
- 图片链接在CSDN显示失败,请在掘金上观看效果 点击链接掘金发表的次文章效果
Vue项目代码区
初始化项目
- vue create 项目名称(比如shop)
- 我们手动配置,选择***Manually select features***
- 选择Babel(默认自动选择) Roter Vuex CSS-processors 空格是可以选择或者取消选择的配置,此处我们不要选择Linter / Formatter
- 选择3.×
- Use history mode for router 选择 n
- Pick a CSS pre–processor 选择less
- Where do you prefer placing config for Babel, ESLint, etc.? 选择In dedicated config files
- Save this as a preset for future projects? 选择N
- 启动项目 进入创建的目录(比如shop)中 npm run serve
vue create 项目名称
下载相关包
//安装element-plus包
npm install element-plus --save
//axios包
npm install axios --save
// 安装vuex包
npm install axios --save
// 安装echarts包
npm install echarts --save
初始化组件
首先将views目录下的About.vue文件以及HomeView.vue文件和components目录下的HelloWorld.vue文件删除。然后清除App.vue文件里面代码,放置如下代码:
App.vue
//App.vue
<template>
<div>
<router-view></router-view>
</div>
</template>
<script>
</script>
<style lang="less">
* {
margin: 0;
padding: 0;
}
</style>
在components目录下新建一个Layout的vue文件,即Layout.vue。
Layout进行页面整体布局,利用Element Plus (element-plus.org)的Container布局容器以及Menu菜单。当用户点击退出时,会清除掉token,用户需要重新登录。为什么要在Layout.vue中就拿Map.vue中的数据而不是在Map.vue中去获取? 作者试过了,当我们启动这个项目时,第一次“表格展示“的”echarts“(router–link)时,展示不了图表,没有渲染上去(作者猜测,可能是使用了Vuex的原因)。当我们切换到”员工管理“的”员工列表后”我们再点击“表格展示“的”echarts“我们就会发现此时页面会出现表格,将数据渲染到DOM上了。为了避免这种情况,我们知道Vuex就是管理数据的,Vuex能够跨组件使用数据,所以我们就在Layout.vue中就请求数据,然后直接在Map.vue中进行使用。
Layout.vue
//Layout.vue
<template>
<div class="common-layout">
<el-container>
<el-header>
<div class="header">
<div class="header_left">
<div class="img">
<img src="../assets/logo.png" alt="" />
</div>
<div class="title">
<div>
<h1>最强节点人力资源管理系统</h1>
</div>
</div>
</div>
<div class="header_right">
<el-row>
<el-button type="primary" round @click="logOut">退出</el-button>
</el-row>
</div>
</div>
</el-header>
<el-container>
<el-aside width="200px">
<el-menu
default-active="2"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
background-color="none"
text-color="#fff"
router
>
<el-sub-menu index="1">
<template #title>
<el-icon><Avatar /></el-icon>
<span>员工管理</span>
</template>
<el-menu-item index="/layout/usersList">员工列表</el-menu-item>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<el-icon><Collection /></el-icon>
<span>表格展示</span>
</template>
<el-menu-item index="/layout/map">echarts</el-menu-item>
</el-sub-menu>
</el-menu>
</el-aside>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
import { useRouter } from "vue-router";
import { useStore } from "vuex";
export default {
setup() {
// 此处 直接请求Map中的数据,不然第一次直接点击Map组件,将得不到数据展示。·
// 因此就再点击map路由前,先获取到Vuex中的数据所以,Vuex本来就是用于跨组件使用数据,然后我们就可以在Map组件中可以使用
const store = useStore();
store.dispatch("asyncGetSex");
store.dispatch("asyncgetsalaryRate");
store.dispatch("asyncgetpersonalSalarytotals");
const router = useRouter();
function logOut() {
// 清除本地储存的token
localStorage.removeItem("token");
alert("退出成功");
// 跳转到登录页面
router.push("/login");
}
return {
logOut,
};
},
};
</script>
<style lang="less" scoped>
.el-header {
height: 6rem;
background-color: #242a31;
}
.el-aside {
height: 100vh;
background-color: #2d363f;
}
.el-header,
.el-aside {
color: #fff;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
.header_left {
display: flex;
.img {
width: 20%;
height: 30%;
margin-right: 5rem;
img {
display: block;
width: 100%;
height: 100%;
}
}
}
.title {
display: flex;
justify-content: center;
align-items: center;
}
}
a {
color: #fff;
text-decoration: none;
}
.header_right {
display: flex;
.img {
width: 20%;
height: 30%;
margin-right: 5rem;
img {
width: 100%;
height: 100%;
}
}
}
</style>
退出效果如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BWxtORGi-1659418267056)(https://p6-juejin.byteimg.com/tos–cn-i-k3u1fbpfcp/f77210c3946f477ea92cd267dbef0ced~tplv-k3u1fbpfcp-watermark.image?)]
在views目录下,新建三个vue文件,分别是Login.vue,Map.vue,Userlist.vue。
Login.vue是用于管理人员进行登录的。当管理人员登录成功后,我们生成相对应的token保存在localstrage中,这样子,管理人员哪怕是刷新都不会需要进行重新登陆、token是用了jsonwebtoken这个第三方包生成的动态数据。
Login.vue
//Login.vue
<template>
<div class="login-container">
<div class="center-form">
<el-form class="login-form">
<el-form-item prop="userNumber" label="登录账号">
<el-input v-model="userNumber" type="text" />
</el-form-item>
<el-form-item prop="password" label="登录密码">
<el-input v-model="password" type="password" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">登录</el-button>
<el-button>忘记密码</el-button>
<el-button>重置</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { reactive, toRefs, ref } from "vue";
import { login } from "@/util/api/employee.js";
import { useRouter } from "vue-router";
export default {
setup() {
let data = reactive({
userNumber: "",
password: "",
});
let router = useRouter();
async function submit() {
let res = await login("login", data.userNumber, data.password);
console.log(router);
console.log(res.data);
if (res.data.status == 200) {
alert(res.data.msg);
localStorage.setItem("token", res.data.token);
router.push("/");
} else {
localStorage.removeItem("token");
}
}
return {
...toRefs(data),
submit,
};
},
};
</script>
<style lang="less" scoped>
.login-container {
width: 100%;
height: 50rem;
background-color: skyblue;
position: relative;
.center-form {
position: absolute;
top: 50%;
left: 50%;
padding: 1rem;
transform: translate(-50%, -50%);
border-radius: 0.5rem;
background-color: aliceblue;
.login-form {
button {
display: block;
margin: 1rem;
}
}
}
}
</style>
Userlist.vue
Userlist.vue是用于展示员工数据,使用了Element Plus (element-plus.org)的Pagination分页组件以及Table表格组件。Userlist.vue是这个项目的核心,它展示了增删改查功能,以及对拿到的数据进行分页。笔者在这里将Element Plus一些功能删除了,进行了简化。
Userlist.vue并没有使用Vuex,为什么? 首先笔者想要换一种写法,就在Userlist.vue中单纯进行异步请求,通过axios拿到数据。其次,数据并没有跨组件进行使用,只是单单在Userlist.vue中使用了,就没必要使用Vuex了。为什么作者要将// let showUserList = ref([]); // let dataShow = ref();注释? 我们知道ref和reactive都可以对值进行操作。但是reactive只能对引用型数据操作,而ref不仅可以对基本数据类型操作,还可以对引用型数据操作。笔者两种方式都试了,因此为了传统上的使用,笔者将涉及到showUserList和dataShowref的写法代码都注释了。如果大家有兴趣,不妨也可以使用。特别注意,如果大家使用ref的写法,那么需要将 el-table :data=“showData.dataShow” border style=”width: 100%”改为 el-table :data=“dataShow” border style=“width: 100%” 将 :total=”showData.showUserList.length“改为:total=”showUserList.length“
//Userlist.vue
<template>
<div class="usersList-container">
<div class="sticky">
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<div class="left-input">
<el-form-item label="查询员工">
<el-input
v-model="formInline.user"
placeholder="请输入员工号或者员工名"
></el-input>
</el-form-item>
</div>
<el-form-item>
<el-button type="primary" @click="onSubmit">查询</el-button>
</el-form-item>
<el-form-item label="添加员工">
<el-button type="primary" @click="dialogTableVisible">添加</el-button>
</el-form-item>
</el-form>
</div>
<div class="block">
<span class="demonstration">完整功能</span>
<div class="tanchukuang" v-if="flag">
<form action="" class="form">
<div>
<label for="">工号<input type="text" v-model="I" /> <br /></label>
</div>
<div>
<label for="">姓名<input type="text" v-model="N" /> <br /></label>
</div>
<div>
<label for="">电话<input type="text" v-model="P" /> <br /></label>
</div>
<div>
<label for="">性别<input type="text" v-model="S" /> <br /></label>
</div>
<div>
<label for="">薪资<input type="text" v-model="Sa" /> <br /></label>
</div>
<div>
<label for="">职位<input type="text" v-model="Po" /> <br /></label>
</div>
<div class="btn-left">
<el-button @click="noSee">取消</el-button>
<el-button type="primary" @click="submit">提交</el-button>
</div>
</form>
</div>
<el-table :data="showData.dataShow" border style="width: 100%">
<el-table-column fixed prop="empID" label="ID" width="150">
</el-table-column>
<el-table-column prop="employeeName" label="姓名" width="120">
</el-table-column>
<el-table-column prop="employeePhone" label="手机号" width="120">
</el-table-column>
<el-table-column prop="employeeSex" label="性别" width="120">
</el-table-column>
<el-table-column prop="employeeSalary" label="薪资" width="120">
</el-table-column>
<el-table-column prop="employeePosition" label="职位" width="120">
</el-table-column>
<el-table-column fixed="right" label="操作" width="250">
<template #default="scope">
<el-button>查看</el-button>
<el-button
type="primary"
size="small"
@click="handleEdit(scope.$index, scope.row)"
>编辑</el-button
>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-sizes="[2, 4, 6, 7, 8]"
layout="total, sizes, prev, pager, next, jumper"
:total="showData.showUserList.length"
>
</el-pagination>
</div>
<div class="update" v-if="up_flag">
<form action="" class="form">
<div>
<label for=""
>电话<input type="text" v-model="upPhone" /> <br
/></label>
</div>
<div>
<label for=""
>薪资<input type="text" v-model="upSalary" /> <br
/></label>
</div>
<div>
<label for=""
>职位<input type="text" v-model="upPosition" /> <br
/></label>
</div>
<div class="btn-left">
<el-button @click="hiddenUpdate">取消</el-button>
<el-button type="primary" @click="updateEmp">提交</el-button>
</div>
</form>
</div>
</div>
</template>
<script>
import { reactive, ref, toRefs } from "vue";
import {
getEmployees,
addEmployees,
deleteEmployees,
updateEmployees,
} from "@/util/api/employee.js";
export default {
setup() {
const showData = reactive({
showUserList: [],
dataShow: [],
});
// let showUserList = ref([]);
// let dataShow = ref();
let data = getEmployees("employee");
data.then((res) => {
// dataShow.value = showUserList.value = res.data;
showData.dataShow = showData.showUserList = res.data;
});
let flag = ref(false);
let person = reactive({
I: "",
N: "",
P: "",
S: "",
Sa: "",
Po: "",
});
let formInline = reactive({
user: "",
region: "",
});
let dialogTableVisible = () => {
flag.value = true;
};
function submit() {
addEmployees("add", person).then(() => {
data = getEmployees("employee");
data.then((res) => {
// dataShow.value = showUserList.value = res.data;
showData.dataShow = showData.showUserList = res.data;
console.log(dataShow.value);
});
flag.value = false;
});
}
// 删除员工
function handleDelete(index, row) {
deleteEmployees("delete", row.empID).then(() => {
data = getEmployees("employee");
data.then((res) => {
// dataShow.value = showUserList.value = res.data;
showData.dataShow = showData.showUserList = res.data;
console.log(dataShow.value);
});
});
}
// 更新员工信息
let up_flag = ref(false);
function hiddenUpdate() {
up_flag.value = false;
}
let getID = ref();
let upPhone = ref();
let upSalary = ref();
let upPosition = ref();
function updateEmp() {
updateEmployees(
"update",
getID.value,
upPhone.value,
upSalary.value,
upPosition.value
).then(() => {
data = getEmployees("employee");
data.then((res) => {
// dataShow.value = showUserList.value = res.data;
showData.dataShow = showData.showUserList = res.data;
console.log(dataShow.value);
});
up_flag.value = false;
});
}
let page_number = ref();
const handleEdit = (index, row) => {
up_flag.value = true;
getID.value = row.empID;
};
function handleSizeChange(val) {
console.log(`每页 ${val} 条`);
page_number.value = val;
}
function handleCurrentChange(val) {
console.log(`这是第${val}页`);
// dataShow.value = showUserList.value.slice(
// (val - 1) * page_number.value,
// val * page_number.value
// );
showData.dataShow = showData.showUserList.slice(
(val - 1) * page_number.value,
val * page_number.value
);
}
function onSubmit() {
console.log();
// dataShow.value = showUserList.value.filter(
// (i) => i.empID == formInline.user || i.employeeName == formInline.user
// );
showData.dataShow = showData.showUserList.filter(
(i) => i.empID == formInline.user || i.employeeName == formInline.user
);
console.log(dataShow.value);
}
function noSee() {
flag.value = false;
}
return {
// dataShow,
// showUserList,
showData,
flag,
up_flag,
person,
page_number,
formInline,
upPhone,
upSalary,
upPosition,
handleEdit,
handleDelete,
...toRefs(person),
noSee,
onSubmit,
submit,
dialogTableVisible,
handleSizeChange,
handleCurrentChange,
hiddenUpdate,
updateEmp,
};
},
};
</script>
<style lang="less" scoped>
.usersList-container {
display: flex;
justify-content: center;
position: relative;
overflow-wrap: wrap;
}
.margin {
margin-bottom: 5rem;
}
.el-form-item__content {
margin-right: 5rem;
}
.tanchukuang {
z-index: 11;
position: fixed;
top: 30vh;
left: 30vh;
}
.update {
z-index: 12;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.form {
display: flex;
flex-direction: column;
justify-content: space-between;
align-content: center;
background-color: skyblue;
position: relative;
div {
padding: 1rem;
label {
color: #fff;
}
input {
color: #000;
font-size: 1rem;
}
}
.btn-left {
transform: translateX(30%);
}
}
</style>
Map.vue
Map.vue完全是echarts展示的可视化图,关于echarts的详细使用,请点击Apache ECharts 为什么作者在这里使用echarts?作者主要是看了ehcarts相关教学,但是一直都没有使用过,正好做Vue3项目,配合node.js通过mysql拿到动态数据进行使用。此处需要注意的是,只有等待DOM渲染完毕,才能使用echarts,因此,echarts的相关操作,必须要在onMounted里面才行。关于为什么不在Map.vue中请求数据,笔者在Layout.vue中就已经详细阐述了,此处不再赘述。
//Map.vue
<script>
import * as echarts from "echarts";
import { onMounted } from "vue";
import { useStore } from "vuex";
export default {
setup(props) {
const store = useStore();
onMounted(() => {
console.log(store.state.sex_totals.man_totals);
// 绘制图表
var myChart1 = echarts.init(document.querySelector("#left-main"));
myChart1.setOption({
title: {
text: "公司男女性别比",
subtext: "加油一起干,都是好伙伴",
left: "center",
},
tooltip: {
trigger: "item",
},
legend: {
orient: "vertical",
left: "left",
},
series: [
{
name: "男女性别比例",
type: "pie",
radius: "50%",
data: [
{
value: `${store.state.sex_totals.man_totals}`,
name: "男",
},
{
value: `${store.state.sex_totals.male_totals}`,
name: "女",
},
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
},
],
});
const arrSalaryRate = [];
const axisX = [
">=3k&&<4k",
">=4k&&<5k",
">=5k&&<6k",
">=6k&&<8k",
">=8k&&<10k",
">=10k&&<15k",
">=15k",
];
// 遍历store.state.salaryRate值,储存到arrSalaryRate数组中
Object.values(store.state.salaryRate).forEach((value, index) => {
arrSalaryRate[index] = value;
});
var myChart2 = echarts.init(document.querySelector("#center-main"));
myChart2.setOption({
title: {
text: "员工工资占比",
subtext: "努力工作,加油升资",
left: "center",
},
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)",
},
legend: {
left: "center",
top: "bottom",
data: axisX,
},
series: [
{
name: "Area Mode",
type: "pie",
radius: [20, 140],
center: ["50%", "50%"],
roseType: "area",
itemStyle: {
borderRadius: 5,
},
data: [
{
value: arrSalaryRate[0],
name: ">=3k&&<4k",
},
{
value: arrSalaryRate[1],
name: ">=4k&&<5k",
},
{
value: arrSalaryRate[2],
name: ">=5k&&<6k",
},
{
value: arrSalaryRate[3],
name: ">=6k&&<8k",
},
{
value: arrSalaryRate[4],
name: ">=8k&&<10k",
},
{
value: arrSalaryRate[5],
name: ">=10k&&<15k",
},
{
value: arrSalaryRate[6],
name: ">=15k",
},
],
},
],
});
var months = [];
// 存储每个月的工资
store.state.getPersonalSalaryTotal.arr.forEach((item, index) => {
months[index] = [
`${item.Jan}`,
`${item.Feb}`,
`${item.Mar}`,
`${item.Apr}`,
`${item.May}`,
`${item.Jun}`,
`${item.Jul}`,
`${item.Aug}`,
`${item.Sep}`,
`${item.Oct}`,
`${item.Nov}`,
`${item.Dec}`,
];
});
const myChart3_arr = [];
store.state.getPersonalSalaryTotal.arr.forEach((item, index) => {
myChart3_arr.push({
name: `${item.employeeName}`,
type: "line",
data: months[index],
});
});
var myChart3 = echarts.init(document.querySelector("#right-main"));
myChart3.setOption({
tooltip: {
trigger: "axis",
},
legend: {
data: [
`${store.state.getPersonalSalaryTotal.arr[0].employeeName}`,
`${store.state.getPersonalSalaryTotal.arr[1].employeeName}`,
`${store.state.getPersonalSalaryTotal.arr[2].employeeName}`,
`${store.state.getPersonalSalaryTotal.arr[3].employeeName}`,
],
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: {
type: "category",
data: [
"一月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"十一月",
"十二月",
],
},
yAxis: {
type: "value",
},
series: myChart3_arr,
});
});
},
};
</script>
<template>
<div class="rolesList-container">
<div class="left-main" id="left-main"></div>
<div class="center-main" id="center-main"></div>
<div class="right-main" id="right-main"></div>
</div>
</template>
<style lang="less" scoped>
.rolesList-container {
height: 100%;
width: 100%;
display: flex;
.left-main {
flex: 2;
}
.center-main {
flex: 3;
}
.right-main {
flex: 6;
}
}
</style>
首先将main.js中的代码清除,放置如下代码:
要引入import ElementPlus from “element–plus”;
import “element–plus/dist/index.css”;
以及注册全局组件
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
此外,还要注册ElementPlus,即app.use(ElementPlus)
//main.js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
const app = createApp(App);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.use(store).use(router).use(ElementPlus).mount("#app");
在src目录下新建一个util目录,在util目录下新建一个api目录以及一个index.js文件
index.js中的代码
import axios from "axios";
const service = axios.create({
baseURL: "http://localhost:80",
timeout: 3000,
});
export default service;
api下面新建echarts.js文件以及employee.js文件
employee.js中的代码
写关于employee的api接口
import service from "../index";
// 登录
export function login(url, userNumber, password) {
return service({
method: "post",
url: url,
data: {
userNumber,
password,
},
});
}
// 获取员工表
export function getEmployees(url) {
return service({
method: "get",
url: url,
});
}
// 添加员工
export function addEmployees(url, values) {
console.log(url, values);
return service({
method: "post",
url: url,
data: {
empID: values.I,
employeeName: values.N,
employeePhone: values.P,
employeeSex: values.S,
employeeSalary: values.Sa,
employeePosition: values.Po,
},
});
}
// 更新员工信息
export function updateEmployees(
url,
empID,
employeePhone,
employeeSalary,
employeePosition
) {
return service({
method: "post",
url: url,
data: {
empID,
employeePhone,
employeeSalary,
employeePosition,
},
});
}
// 删除员工信息
export function deleteEmployees(url, empID) {
return service({
method: "delete",
url: url,
params: {
empID,
},
});
}
echarts.js中的代码
写关于echarts的api接口。因为请求的接口几乎一致,所以写一个就好了
import service from "../index";
export function seemURL(url) {
return service({
method: "get",
url: url,
});
}
首先将store目录下的index.js代码清除,然后放置如下代码:
import { createStore } from "vuex";
import { seemURL } from "@/util/api/echarts";
export default createStore({
state: {
sex_totals: {
male_totals: "",
man_totals: "",
},
salaryRate: {
threeK: "",
fourK: "",
fiveK: "",
sixK: "",
eightK: "",
tenK: "",
firthK: "",
},
getPersonalSalaryTotal: {
arr: [],
},
},
getters: {},
mutations: {
getSex(state, value) {
state.sex_totals.male_totals = value.male_total;
state.sex_totals.man_totals = value.man_total;
},
getsalaryRate(state, value) {
state.salaryRate.threeK = value.threeK;
state.salaryRate.fourK = value.fourK;
state.salaryRate.fiveK = value.fiveK;
state.salaryRate.sixK = value.sixK;
state.salaryRate.eightK = value.eightK;
state.salaryRate.tenK = value.tenK;
state.salaryRate.firthK = value.firthK;
},
getpersonalSalarytotals(state, value) {
state.getPersonalSalaryTotal.arr = value;
},
},
actions: {
asyncGetSex(context) {
const res = seemURL("/map_image/1");
res.then((res) => {
context.commit("getSex", res.data);
});
},
asyncgetsalaryRate(context) {
const res = seemURL("/map_image/2");
res.then((res) => {
context.commit("getsalaryRate", res.data);
});
},
asyncgetpersonalSalarytotals(context) {
const res = seemURL("/map_image/3");
res.then((res) => {
context.commit("getpersonalSalarytotals", res.data);
});
},
},
modules: {},
});
首先将router目录下的index.js代码清除,然后放置如下代码:
import { createRouter, createWebHashHistory } from "vue-router";
const routes = [
{
path: "/",
name: "/",
component: () => import("@/components/Layout.vue"),
redirect: "/layout",
},
{
path: "/login",
name: "login",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("@/views/Login.vue"),
},
{
path: "/layout",
name: "layout",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("@/components/Layout.vue"),
redirect: "/layout/userslist",
children: [
{
path: "userslist",
name: "userslist",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("@/views/UsersList.vue"),
},
{
path: "map",
name: "map",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("@/views/Map.vue"),
},
],
},
];
const router = createRouter({
// 历史模式此处设置为 hash模式
history: createWebHashHistory(),
routes,
});
router.beforeEach((to, from, next) => {
// 如果跳转目标不是/login
if (to.path !== "/login") {
// 并且存在本地记录,则放行
if (localStorage.getItem("token")) {
next();
} else {
//如果不存在本地记录,则强制跳转到登录界面
next("/login");
}
} else {
// 如果是跳转目标是/login,需要放行
next();
}
});
export default router;
启动项目 在项目名称下(比如shop)
npm run serve
node服务器代码区
新建一个目录(比如backstage)
新建一个js目录,目录下新建app.js以及router.js
初始化backstage
//生成package.josn
npm init -y
下载相关包
npm i cors --save
npm i express --save
npm i express-jwt --save
npm i josnwebtoken --save
npm i mysql --save
npm i nodemon -D
app.js中的代码
- 为什么使用app.use(cors()); ? 处理跨域请求的问题。
- 为什么使用app.use(express.json()); ? 因为axios发送的是josn格式的数据,而node解析表单是x-www-form-urlencoded;charset=UTF-8格式,因此我们可以在node中自己配置解析josn格式的数据。axios官方也有相关措施,详细请看axios中文文档|axios中文网 | axios (axios-js.com)
const express = require("express");
const app = express();
// 解析axios发送的josn数据,axios发送的data,默认是josn格式,而node默认是urlencoded格式的
app.use(express.json());
// 配置跨越请求
const cors = require("cors");
app.use(cors());
// 解析表单
app.use(express.urlencoded({ extended: false }));
const router = require("./router.js");
app.use("/", router);
app.listen("80", function () {
console.log("为您服务");
});
router.js中的代码
为什么使用promise代码?本人在不使用promise时,造成了回调地狱的问题,出于美观考虑,笔者还是决定使用promise,正好复习一些promise相关内容
const express = require("express");
const secretKey = "strongest ^0^";
const jwt = require("jsonwebtoken");
// express-jwt 最新版本使用方式,用于解析生成的token
// 解码后的 JWT 有效负载现在可用作 req.auth 而不是 req.user
// 我们只是为了获取token,并不需要解析token,不配置这个中间件也行
var { expressjwt: expressJWT } = require("express-jwt");
express().use(expressJWT({ secret: secretKey, algorithms: ["HS256"] }));
const router = express.Router();
const mysql = require("mysql");
const db = mysql.createPool({
host: "127.0.0.1",
user: "root",//你自己的mysql登录用户名,一般都是root
password: "123456",//你自己的mysql登录密码,比如你设置的123456
port: "3306",
// 连接数据库,//hrmsystem是(database)库的名字
database: "hrmsystem",
});
// 员工查询操作
router.get("/employee", function (req, res) {
db.query("SELECT * FROM employee", function (err, result) {
console.log(result);
const employees = result;
if (err) return console.log(err.message);
res.send(employees);
});
});
// 登录操作
router.post("/login", function (req, res) {
const tokenStr = jwt.sign({}, secretKey, {
expiresIn: "10d",
});
db.query("SELECT * FROM register", function (err, result) {
if (err) return console.log(err.message);
if (result.some((item) => item.regID == req.body.userNumber)) {
if (result.some((item) => item.regPassword == req.body.password)) {
//发送token
res.send({ status: 200, msg: "登录成功", token: `Bearer ${tokenStr}` });
} else {
res.send({ status: "error", msg: "密码错误" });
}
} else {
res.send({ status: "error", msg: "账号不存在" });
}
});
});
// 添加操作
router.post("/add", function (req, res) {
db.query(`SELECT empID FROM employee `, function (err, result) {
if (err) return console.log(err.message);
if (
result.some((item) => {
item.empID == req.body.empID;
})
) {
res.send({ msg: "员工ID已经被注册过了,请更换ID" });
} else {
const sql = `insert into employee set ?`;
const loop = {
empID: req.body.empID,
employeeName: req.body.employeeName,
employeePhone: req.body.employeePhone,
employeeSex: req.body.employeeSex,
employeeSalary: req.body.employeeSalary,
employeePosition: req.body.employeePosition,
};
db.query(
sql,
[
loop,
loop.ID,
loop.name,
loop.phone,
loop.sex,
loop.salary,
loop.position,
],
function (err, result) {
if (err) {
return console.log(err.message);
}
if (result.affectedRows === 1) {
res.send({
person: req.body,
status: 200,
msg: "添加员工成功",
});
}
}
);
}
});
});
// 更新操作
router.post("/update", function (req, res) {
db.query(`SELECT empID FROM employee `, function (err, result) {
// if (err) return console.log(err.message);
if (result.some((item) => item.empID == req.body.empID)) {
const sql = "update employee set ? where empID=?";
// const sql = `update employee set employeePhone=${req.body.employeePhone} and employeeSalary=${req.body.employeeSalary} and employeePosition=${req.body.employeePosition} where empID=${req.body.empID}`;
const updateData = {
employeePhone: req.body.employeePhone,
employeeSalary: req.body.employeeSalary,
employeePosition: req.body.employeePosition,
};
// db.query(sql, function (err, result) {
db.query(sql, [updateData, req.body.empID], function (err, result) {
if (err) return console.log(err.message);
if (result.affectedRows === 1) {
res.send({ status: 200, msg: "更改员工成功" });
}
});
} else {
res.send({ msg: "员工ID不存在" });
}
});
});
// 删除员工
router.delete("/delete", function (req, res) {
db.query(
`SELECT empID FROM employee where empID= ${req.query.empID}`,
function (err, result) {
console.log(result);
console.log(req.query.empID);
console.log(result.length > 0);
if (result.length > 0) {
const sql = `delete from employee where empID=?`;
db.query(sql, (empID = req.query.empID), function (err, result) {
if (err) return console.log(err.message);
if (result.affectedRows === 1) {
console.log(1);
res.send({ status: 200, msg: "删除员工成功" });
}
});
} else {
res.send({ msg: "员工ID不存在" });
}
}
);
});
router.get("/map_image/1", function (req, res) {
let arr = [];
let p = new Promise(function (resolve, reject) {
db.query(
` SELECT * FROM employee where employeeSex="男"`,
function (err, result) {
arr.push(result.length);
if (err) return console.log(err.message);
resolve(arr);
}
);
});
p.then((result) => {
return new Promise((resolve, reject) => {
db.query(
` SELECT * FROM employee where employeeSex="女"`,
function (err, result) {
if (err) return console.log(err.message);
arr.push(result.length);
resolve(arr);
}
);
});
}).then(function (result) {
res.send({ man_total: result[0], male_total: result[1] });
}).catch((err) => {
throw new Error(err.message);
});;
});
router.get("/map_image/2", function (req, res) {
let arr = [];
let p = new Promise(function (resolve, reject) {
db.query(
` SELECT * FROM employee where employeeSalary>=3000 and employeeSalary<4000`,
function (err, result) {
arr.push(result.length);
if (err) return console.log(err.message);
resolve(arr);
}
);
});
p.then((result) => {
return new Promise((resolve, reject) => {
db.query(
` SELECT * FROM employee where employeeSalary>=4000 and employeeSalary<5000`,
function (err, result) {
if (err) return console.log(err.message);
arr.push(result.length);
resolve(arr);
}
);
});
})
.then((result) => {
return new Promise((resolve, reject) => {
db.query(
` SELECT * FROM employee where employeeSalary>=5000 and employeeSalary<6000`,
function (err, result) {
if (err) return console.log(err.message);
arr.push(result.length);
resolve(arr);
}
);
});
})
.then((result) => {
return new Promise((resolve, reject) => {
db.query(
` SELECT * FROM employee where employeeSalary>=6000 and employeeSalary<8000`,
function (err, result) {
if (err) return console.log(err.message);
arr.push(result.length);
resolve(arr);
}
);
});
})
.then((result) => {
return new Promise((resolve, reject) => {
db.query(
` SELECT * FROM employee where employeeSalary>=8000 and employeeSalary<10000`,
function (err, result) {
if (err) return console.log(err.message);
arr.push(result.length);
resolve(arr);
}
);
});
})
.then((result) => {
return new Promise((resolve, reject) => {
db.query(
` SELECT * FROM employee where employeeSalary>=10000 and employeeSalary<15000`,
function (err, result) {
if (err) return console.log(err.message);
arr.push(result.length);
resolve(arr);
}
);
});
})
.then((result) => {
return new Promise((resolve, reject) => {
db.query(
` SELECT * FROM employee where employeeSalary>=15000`,
function (err, result) {
if (err) return console.log(err.message);
arr.push(result.length);
resolve(arr);
}
);
});
})
.then(function (result) {
console.log(result);
res.send({
threeK: result[0],
fourK: result[1],
fiveK: result[2],
sixK: result[3],
eightK: result[4],
tenK: result[5],
firthK: result[6],
});
}).catch((err) => {
throw new Error(err.message);
});;
});
router.get("/map_image/3", function (req, res) {
db.query(` SELECT * FROM personal_salary`, function (err, result) {
if (err) return console.log(err.message);
res.send(result);
});
});
// db.query("SELECT * FROM employee", function (err, result) {
// if (err) return console.log(err.message);
// console.log(result);
// });
// db.query("SELECT * FROM register", function (err, result) {
// if (err) return console.log(err.message);
// console.log(result);
// });
module.exports = router;
启动服务器 在js目录下
nodemon app.js
mysql相关内容
新建一个hrmsystem数据库
在hrmsystem数据库中新建三张表,分别是employee和personal_salary以及register
相关字段如下图所示
employee字段即empID,employeeName,employeePhone,employeeSex,employeeSalary,employeePosition
register字段 regID,regPassword,registerName
personal_salary字段 empID Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec employeeName
总结
对于一个小白(特别是自学者)写一个前后端结合的项目很不容易,特别是看别人的代码,没有注释那就更加难受了。这个前后端结合项目,总体功能比较简单,就是对拿到的数据进行增删改查操作。用了echarts进行可视化。希望这个项目能够对初学者能够有一定帮助。如果觉得有作用的话,不妨对作者进行一键三连(关注,收藏,加点赞),谢谢各位啦!
原文地址:https://blog.csdn.net/qq_56889810/article/details/126120483
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_38758.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!