上一篇案例中也是查询到了表单的内容–>前后端分离学习笔记(4) —[路由嵌套, 查询表单显示]

文章目录

  • 1.前端组件页面
    • 添加管理员操作
    • 修改管理员信息
    • 删除管理员
    • 为管理员上传头像
  • 修改密码
  • 2.后端处理
    • 管理员控制层
    • 管理员服务层
    • 管理员持久层
    • 头像上传时的后端全局配置类
    • 头像上传时重命名格式的工具类
    • 管理员对应的映射SQl

1.前端组件页面

添加管理员操作

要达到点击新增按钮弹出对话框的效果

(1)打开对话框时,会去查询角色信息表,生成角色复选框;

(2)提交表单时,有简单的表单盐验证;
注意最后保存数据时,分两步;首先把管理员信息存入管理员表;这时会得到添加的管理员Id;
然后根据Id和填写的角色ID;将管理员与角色的关联关系存入到管理角色关联表

添加管理员时的表单验证效果

首先新建Add.vue作为添加管理员的组件页面
Add.vue组件代码;其实就是对话框中嵌套了表单

<template><!-- 在此组件中 制作一个对话框 --><el-dialog title="新增管理员" :visible.sync="dialogVisible" width="50%"><!-- 添加管理员的表单信息--><el-form :model="form" :rules="rules" ref="form" label-width="100px" class="demo-ruleForm"><el-form-item label="账号" prop="account"><el-input v-model="form.account"></el-input></el-form-item><el-form-item label="手机" prop="phone"><el-input v-model="form.phone"></el-input></el-form-item><el-form-item label="性别" prop="sex"><el-radio v-model="form.sex" label="男">男</el-radio><el-radio v-model="form.sex" label="女">女</el-radio></el-form-item><!-- <el-form-item label="时间"><el-date-picker  type="date" v-model="form.birthday"  placeholder="选择日期"> </el-date-picker></el-form-item> --><el-form-item label="角色" prop="roleId"><el-checkbox-group v-model="form.roleId"><!--复选框存入不同的角色 --><el-checkbox v-for="role in roles" :label="role.id" :key="role.id">{{role.name}}</el-checkbox></el-checkbox-group></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false">取 消</el-button><el-button type="primary" @click="save('form')">保存</el-button><el-button @click="resetForm('form')">重置</el-button></span></el-dialog>
</template><script>export default {//当前组件名称;name: "AddDialog",data() {return {//默认对话组件是不显示的;dialogVisible: false,//表单绑定的数据;form: {account: "",sex: "男",phone: "",//birthday:"",roleId: []},//角色复选框的信息;从后端加载数据roles: [],//表单数据的校验规则;rules: {account: [{required: true,message: '请输入账户名',trigger: 'blur'},{min: 3,max: 5,message: '长度在 3 到 5 个字符',trigger: 'blur'}],phone: [{required: true,message: '请输入手机号',trigger: 'blur'},{min: 11,max:11,message: '手机号限制11位',trigger: 'blur'}],roleId: [{type: 'array',required: true,message: '请至少选择一个角色',trigger: 'change'}]},};},methods: {//保存管理员信息;save(form) {var _this = this;//先进行表单验证;this.$refs[form].validate((valid) => {if (valid) {//保存信息;this.$http.post("admin/addAdmin", this.form).then(function(resp) {if (resp.data.code === 200) {_this.$message({message: resp.data.msg,type: 'success'});//关闭对话框;window.location.reload();}});} else {console.log('error submit!!');return false;}});},//重置表单;resetForm(formName) {this.$refs[formName].resetFields();}},created() {var _this = this;//加载页面时就把角色的列表查询过来,存入到复选框中;this.$http.get("admin/roles").then(function(resp) {_this.roles = resp.data.data;})}};
</script><style>
</style>

注意这个组件要给个命名;

然后,需要在管理员列表组件AdminList.vue组件中打开这个对话框;那么
AdminList.vue组件 就是个父级组件;而新增管理员的Add.vue组件就是子级组件;

注意;点击新增按钮时,调用子级组件Add.vue中的属性;让对话框显示出现

AdminList.vue组件代码

<template><el-card class="box-card" style="background-color: #E9EEF3;"><div slot="header" class="clearfix"><span>管理员列表</span><el-button style="float: right; padding: 10px 10px" type="success" @click="openAddAdmin()">新增</el-button></div><div class="text"><!-- 查询条件框--><div class="clearfix" style="padding-bottom: 10px;"><!-- 查询条件--><el-row :gutter="20"><el-col :span="6"><el-input placeholder="账号" v-model="query.account"></el-input></el-col><el-col :span="6"><el-input placeholder="性别" v-model="query.sex"></el-input></el-col><el-col :span="6"><el-button type="primary" icon="el-icon-search" @click="search()">搜索</el-button></el-col></el-row></div><!-- 数据表格位置--><el-table  v-loading="loading"element-loading-text="拼命加载中"element-loading-spinner="el-icon-loading"element-loading-background="rgba(0, 0, 0, 0.8)":data="tableData" style="width: 100%" border max-height="450"><el-table-column fixed type="index" label="编号" width="60"></el-table-column><el-table-column prop="account" label="账号" width="100"></el-table-column><el-table-column prop="newFileName" label="头像" width="60"><template slot-scope="scope"><!-- 若没有头像,则显示默认头像 --><img v-bind:src="http+'default/admin.png'" width="30" height="30"v-if="scope.row.newFileName === null " /><!-- 否则显示管理员的头像 --><img v-bind:src="http+scope.row.account+'/'+scope.row.newFileName" width="30" height="30"v-else /></template></el-table-column><el-table-column prop="sex" label="性别" width="50"></el-table-column><el-table-column prop="phone" label="手机" width="120"></el-table-column><el-table-column prop="type" label="类型" width="120" :formatter="formatType"></el-table-column><el-table-column prop="roleList" label="角色" width="250" align="center"><template slot-scope="scope"><span v-for="(role,index) in scope.row.roleList" :key="index">{{role.name}}-</span></template></el-table-column><!-- 时间加上排序 --><el-table-column prop="operTime" label="时间" width="200" :formatter="formatTime" sortable></el-table-column><el-table-column fixed="right" label="操作" width="200"><template slot-scope="scope"><el-button size="mini" @click="toUpdate(scope.row.id)">编辑</el-button><el-button size="mini" type="danger" @click="toDelAdmin(scope.row.id,scope.row.account)">删除</el-button></template></el-table-column></el-table><!-- 分页组件--><!-- current-page="1" 默认是第一页page-sizes="[2, 4, 6, 8]" 生成下拉框page-size="2" 默认的每页大小layout="total, sizes, prev, pager, next, jumper" 布局total="400" 总条数@size-change="handleSizeChange" 当下拉框改变页数大小时触发@current-change="handleCurrentChange" 当改变页码时触发--><div class="block"><el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange":page-sizes="[2, 4, 6, 8,10,15]" :page-size="8" layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div></div><!-- 添加管理员的组件显示视图位置  默认是隐藏的 --><AddDialog ref="AddDialog"></AddDialog><!-- 修改管理员的1组件显示位置;默认隐藏 --><UpdateDialog ref="UpdateDialog"></UpdateDialog></el-card>
</template><script>//导入管理员添加的组件;import AddDialog from './Add.vue';//导入管理员更新的组件;import UpdateDialog from './Update.vue';export default {//添加管理员的对话框组件;components: {AddDialog,UpdateDialog},data: function() {return {tableData: [],//定义的图片服务器位置;http: "http://127.0.0.1:5927/studyspringbootvue/",//根据条件搜索时的绑定参数;query: {account: '',sex: '',//当前页码;pageNum: 1,//当前页面数据量;pageSize: 8},//数据总条数;total: 0,loading:true}},methods: {//管理员类型的自定义绑定事件;//row表示此行数据  row.列名 得到当前列的数据formatType(row, column) {return (row.type == 1 ? "普通管理员" : "超管"); //最终表格中以返回的数据为准进行显示},//时间的格式化;formatTime(row, column) {var date = new Date(row.operTime);return date.toLocaleString();},//公用的搜索管理员列表方法;getAdminList() {//向后端发送管理员的数据;var _this = this;this.$http.post("admin/getAllAdmin", this.query).then(function(resp) {//console.log(resp);_this.tableData = resp.data.data;//总数据量;_this.total = resp.data.total;//console.log(resp.data.total)_this.loading = false;});},//根据条件搜索管理员;search() {this.query.pageNum = 1;this.getAdminList();},//修改当前页面的显示数据量;handleSizeChange(val) {console.log(`每页 ${val} 条`);this.query.pageSize = val;this.getAdminList();},//修改当前页面的显示页码;handleCurrentChange(val) {console.log(`当前页: ${val}`);this.query.pageNum = val;this.getAdminList();},//跳转到新增管理员窗口;openAddAdmin() {//让新增对话框组件显示出来, 在父组件中调用子组件的数据;this.$refs.AddDialog.dialogVisible = true;},//修改管理员信息;toUpdate(id) {//打开修改当前管理员的对话框组件;this.$refs.UpdateDialog.dialogVisible = true;//调用子组件的方法;将Id传递进去;this.$refs.UpdateDialog.findAdminByID(id);//console.log(id)},//删除管理员toDelAdmin(id, account) {//console.log(id);var _this = this;this.$confirm('您确定删除管理员' + account + '吗?', '警告', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {//发出请求,删除信息;this.$http.delete("admin/deleteAdmin/" + id).then(function(resp) {if (resp.data.code === 200) {_this.$message({message: resp.data.msg,type: 'success'});//刷新页面;window.location.reload();}})})},},//加载页面时触发;created() {//调用公用方法;this.getAdminList();}}
</script><style>body {margin: 0;}
</style>

修改管理员信息

点击编辑,同样是以对话框的形式打开;
显示管理员原来的信息;填充到表单;
实际需要分为三步;
(1)首先在角色表查询到角色的信息;拼接生成复选框;

(2)然后再通过当前的管理员Id查询到管理员的信息;注意其中的关联查询;
然后把信息拼接到表单中;

注意提交时,这里按照ElementUI官网的表单验证案例试着编写了一下;
毕竟平时主要学习后端开发知识;
表单验证这方面的效果不是很好;

(3)提交数据后,我会首先把这里的管理员关联的角色关联关系先删除;
然后再根据表单填写的角色内容重新将关联关系存进 管理员与角色的关联表;

然后在管理员修改信息时;由于我上传头像那块是根据管理员的账户名作为头像的文件夹;
考虑到更改账户名之后,服务器上存图片的地址不对应了;所以利用了File类的renameTo方法;将原文件夹重命名处理;


在点击编辑按钮时,会打开对话框,且为方法传入Id值;

修改管理员信息的Update.vue组件代码

<template><!-- 在此组件中 制作一个对话框 --><el-dialog title="修改管理员" :visible.sync="dialogVisible" width="50%"><!-- 修改管理员信息的表单--><el-form ref="form" :model="form" :rules="rules" label-width="100px" class="demo-ruleForm"><!-- Id列隐藏 --><el-input v-model="form.id" v-if="false"></el-input><!-- 初始账号名隐藏 --><el-input v-model="form.account" v-if="false"></el-input><el-form-item label="账号" prop="account"><el-input v-model="form.account"></el-input></el-form-item><el-form-item label="手机" prop="phone"><el-input v-model="form.phone"></el-input></el-form-item><el-form-item label="性别"><el-radio v-model="form.sex" label="男">男</el-radio><el-radio v-model="form.sex" label="女">女</el-radio></el-form-item><el-form-item label="角色" prop="roleId"><el-checkbox-group v-model="form.roleId"><el-checkbox v-for="role in roles" :label="role.id" :key="role.id">{{role.name}}</el-checkbox></el-checkbox-group></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false">取 消</el-button><el-button type="primary" @click="save('form')">保存</el-button></span></el-dialog>
</template><script>export default {//组件名称;name: "UpdateDialog",data() {return {//默认对话组件是不显示的dialogVisible: false,form: {id: "",initAccount:"",account: "",sex: "男",phone: "",roleId: [],},//从后端加载数据;角色的id集合;roles: [],rules: {account: [{required: true,message: '请输入账户名',trigger: 'blur'},{min: 2,max: 5,message: '长度在 2 到 5 个字符',trigger: 'blur'}],phone: [{required: true,message: '请输入手机号',trigger: 'blur'},{min: 11,max: 11,message: '手机号限制11位',trigger: 'blur'}],roleId: [{type: 'array',required: true,message: '请至少选择一个角色',trigger: 'change'}]},};},methods: {//根据Id查询出当前管理员信息;findAdminByID(id) {//向后端发起请求查询;var _this = this;//使用restful风格;传递参数;this.$http.get("admin/findAdmin/" + id).then(function(resp) {//console.log(resp)_this.form.initAccount=resp.data.data.account;_this.form.id = resp.data.data.id;_this.form.account = resp.data.data.account;_this.form.sex = resp.data.data.sex;_this.form.phone = resp.data.data.phone;_this.form.roleId = resp.data.data.roleId;})},//保存修改后的管理员信息;save(form) {var _this = this;//先进行表单验证;this.$refs[form].validate((valid) => {if (valid) {//发送请求;根据请求,修改管理员的信息;this.$http.post("admin/updateAdmin", this.form).then(function(resp) {//console.log(resp);if (resp.data.code === 200) {_this.$message({message: resp.data.msg,type: 'success'});//关闭对话框;window.location.reload();}})} else {console.log('error submit!!');return false;}});},},created() {var _this = this;//查询所有角色this.$http.get("admin/roles").then(function(resp) {_this.roles = resp.data.data;})}};
</script><style>
</style>

删除管理员

点击时,触发弹框即可;

这里后端到时候处理的时候,需要先删除这个管理员在管理角色关联表的数据;然后再删除管理员表的数据;

就在AdminLst.vue组件页面;代码在上面;

为管理员上传头像

右上角设置中点击上传头像;

即可打开这个对话框;

ElementUI也是初步学习,刚开始还没搞懂这里面的内置函数和内置参数;文档看了一点;大概是了解到如何调用的了;

上传头像成功后,提示信息;然后图片会被回显到这里;如果不满意,

如果又觉得上传的头像不满意,没关系,点击图片还可以继续操作,上传头像;

  • 在上传图片时,其实只要给参数action:上传地址写好就行;因为这个管理员Id和账号account在token令牌中就有,然后后端接收时,会从请求头中的token中解析获取到管理员的Id和账号account;然后;根据账号名,使用工具类对头像文件名进行重命名;将图片存到对应的服务器地址;
  • 数据库存入对应Id的头像名称以及上传头像时的文件名即可;

当前案例学习时所用的图片暂存服务器;
(关于为什么要这样单独开一个tomcat服务器放置管理员头像;在之前的笔记中有提到;—>
前后端分离学习笔记(4) —[路由嵌套, 查询表单显示]之 显示管理员的头像)


这个上传头像的组件页面需要内嵌到主框架 Main.vue

在上传头像之前,对文件进行了一个简单的限制;
格式和内存大小

上传头像的组件UpLodeImg.vue代码

<template><!-- 居中的对话框 --><el-dialogtitle="上传头像":visible.sync="centerDialogVisible"width="40%"center><span><el-uploadclass="avatar-uploader"action="http://127.0.0.1:5277/api/admin/upAdminAvatar":headers={token:headers}:show-file-list="false":on-success="handleAvatarSuccess":before-upload="beforeAvatarUpload"><img v-if="imageUrl" :src="data:imageUrl" class="avatar"><i v-else class="el-icon-plus avatar-uploader-icon"></i></el-upload></span><span slot="footer" class="dialog-footer"><el-button @click="centerDialogVisible = false" type="danger">取 消 上 传</el-button><!-- <el-button type="primary" @click="upLodeAvatar()">上传</el-button> --></span></el-dialog>
</template><script>export default {//组件名称;name:'UpLoadImgDialog',//数据域;data() {return {//对话框是否显示;centerDialogVisible: false,//管理员存储头像的服务器地址;imageUrl: '',//管理员用户名;可以不用; 本来可以用:data={account:account} 绑定传递附带参数;发现没必要;//account:window.sessionStorage.getItem('account'),//这边给请求头存入token令牌;headers:window.sessionStorage.getItem('token')};},methods: {//上传文件成功时回调;handleAvatarSuccess(res, file) {//生成图片在服务器的地址;this.imageUrl = "http://127.0.0.1:5927/studyspringbootvue/"+res.data.account+"/"+res.data.newFileName;//提示信息;this.$message({message:res.msg, showClose: true, type: 'success'});},//上传文件之前;beforeAvatarUpload(file) {const isImg = file.type === 'image/png' || file.type == 'image/jpg' ||  file.type == 'image/jpeg';const isLt2M = file.size / 1024 / 1024 < 2;if (!isImg) {this.$message.error('上传头像图片限制 JPG 格式!PNG格式!JPEG格式!');}if (!isLt2M) {this.$message.error('上传头像图片大小不能超过 2MB!');}return isImg && isLt2M;},/* //上传头像;upLodeAvatar(){} */},};
</script><style>.avatar-uploader .el-upload {border: 1px dashed #d9d9d9;border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;}.avatar-uploader .el-upload:hover {border-color: #409EFF;}.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;line-height: 178px;text-align: center;}.avatar {width: 178px;height: 178px;display: block;}
</style>

主页面
Main.vue

<template><el-container style="height:100%"><!-- 上面的一整条导航栏 --><el-header style="text-align: right; font-size: 12px"><el-dropdown><i class="el-icon-setting" style="margin-right: 15px"></i><!-- 右上角个人中心的几个选项--><el-dropdown-menu slot="dropdown"><el-dropdown-item @click.native="openUpPassWordDialog()">修改密码</el-dropdown-item><el-dropdown-item @click.native="openUpLoadImgDialog()">上传头像</el-dropdown-item><el-dropdown-item @click.native="exit()">退出登录</el-dropdown-item></el-dropdown-menu></el-dropdown><span>{{account}}</span></el-header><el-container><!-- 左侧的侧边栏--><el-aside width="200px"><!-- :default-openeds菜单的默认打开数;这里需要加入路由router --><el-menu :default-openeds="['1', '0']" router=""><el-submenu index="1"><template slot="title"><i class="el-icon-setting"></i>操作</template><el-menu-item-group><!-- 这里的文字会绑定上菜单的路径地址,点击即可跳转 --> <el-menu-item v-bind:index="menu.url" v-for="(menu,index) in menuList" :key="index">{{menu.name}}</el-menu-item><!-- <el-menu-item index="3-2">角色管理</el-menu-item> --></el-menu-item-group></el-submenu></el-menu></el-aside><el-container><!-- 中间的主信息页面 --><el-main><!-- 这里会显示路由跳转过来的视图--><router-view></router-view></el-main><!-- 底部栏的话本次案例练习暂时不用--><!-- <el-footer>Footer</el-footer> --></el-container></el-container><!-- 修改头像的对话框弹出 --><UpLoadImgDialog  ref="UpLoadImgDialog"></UpLoadImgDialog><!-- 修改密码的对话框弹出--><UpdatePassWord   ref="UpdatePassWord"></UpdatePassWord></el-container>
</template><script>//导入上传头像的对话框;import UpLoadImgDialog from './UpLodeImg.vue';//导入修改密码的对话框;import UpdatePassWord from './UpdatePassWord.vue';export default{//当前组件中内嵌的子组件;components:{UpLoadImgDialog,UpdatePassWord},data:function(){return{//账号名;account:"",//菜单列表;menuList:[],}},methods:{//安全退出exit(){this.$confirm('您确定退出吗?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {//清除session即可;window.sessionStorage.clear();//跳转到登录页面;this.$router.push("/login");})},//点击弹出修改头像的对话框;openUpLoadImgDialog(){//打开修改当前管理员的对话框组件;this.$refs.UpLoadImgDialog.centerDialogVisible = true;},//点击弹出修改密码的对话框;openUpPassWordDialog(){//打开修改密码的对话框组件;this.$refs.UpdatePassWord.dialogVisible = true;}},//加载页面时就触发created(){//从session中取到管理员的账户名; 赋值给数据域的定义模型变量 accountthis.account = window.sessionStorage.getItem("account");//注意this是vue对象;要是直接在请求里面写this的话表示的是axios对象;var _this = this;//发出请求,查询该管理员负责的菜单;注意不用带参数,请求头中的token里面就有用户的信息;this.$http.get("login/findMenuList").then(function(resp){//console.log(resp);_this.menuList = resp.data.data;});}}
</script><style>.el-header {background-color: #00FFFF;color: #333;text-align: center;line-height: 60px;}.el-aside {background-color: #67C23A;color: #333;}.el-main {background-color: #E9EEF3;color: #333;}
</style>

修改密码

有简单的密码验证

修改之后会清除浏览器的token缓存;强制路由到登录页面;

修改密码的对话框组件也需要内嵌到主页面中;

UpdatePassWord.vue修改密码的组件

<template><el-dialog title="修改密码" :visible.sync="dialogVisible" width="30%"><span><el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px"class="demo-ruleForm"><el-form-item label="旧密码" prop="oldPass"><el-input type="password" v-model="ruleForm.oldPass" autocomplete="off"></el-input></el-form-item><el-form-item label="新密码" prop="newPass"><el-input type="password" v-model="ruleForm.newPass" autocomplete="off"></el-input></el-form-item><el-form-item label="确认密码" prop="checkPass"><el-input type="password" v-model="ruleForm.checkPass" autocomplete="off"></el-input></el-form-item><el-form-item><el-button type="primary" @click="submitForm('ruleForm')">修改</el-button><el-button @click="resetForm('ruleForm')">重置</el-button></el-form-item></el-form></span></el-dialog>
</template><script>export default {data() {var validateoldPass = (rule, value, callback) => {if (value === '') {callback(new Error('请输入旧密码'));} else if (value.length > 6) {callback(new Error('密码长度不超过6位!'));} else if (this.checkOldPassWord(value) == false) {callback(new Error('旧密码输入错误'));} else {callback();}};var validateneWPass = (rule, value, callback) => {if (value === '') {callback(new Error('请输入新密码'));} else if (value.length > 6) {callback(new Error('密码长度不超过6位!'));} else {if (this.ruleForm.checkPass !== '') {this.$refs.ruleForm.validateField('checkPass');}callback();}};var validatePass2 = (rule, value, callback) => {if (value === '') {callback(new Error('请再次输入密码'));} else if (value.length > 6) {callback(new Error('密码长度不超过6位!'));} else if (value !== this.ruleForm.newPass) {callback(new Error('两次输入密码不一致!'));} else {callback();}};return {dialogVisible: false,//表单数据;ruleForm: {oldPass: '',newPass: '',checkPass: '',},//规则数组;rules: {oldPass: [{validator: validateoldPass,trigger: 'blur'}],newPass: [{validator: validateneWPass,trigger: 'blur'}],checkPass: [{validator: validatePass2,trigger: 'blur'}],},};},methods: {//验证旧密码;checkOldPassWord(oldPass) {var result = this.$http.get("admin/checkAdminPass/" + oldPass).then(function(resp) {//这里将响应的信息返回;true/false;return resp.data.data;});return result;},submitForm(formName) {this.$refs[formName].validate((valid) => {if (valid) {//发出请求;修改密码;var _this = this;this.$http.put("admin/updatePassword/" + this.ruleForm.newPass).then(function(resp) {//显示成功;_this.$message({message: resp.data.msg,type: 'success'});//清除session信息;window.sessionStorage.removeItem("token");_this.$router.replace("/login");});} else {//否则提示验证信息;console.log('error submit!!');return false;}});},//重置当前表单;resetForm(formName) {this.$refs[formName].resetFields();}}};
</script><style>
</style>

实际上,我这里的旧密码验证还是有点问题的;
本来想着要利用外部方法checkOldPassWord(oldPass) 开启async;然后让里面的异步方法开启await变为同步方法的,但是返回值为promise对象;我需要取到其中[[PromiseValue]]的值,试过了,虽然可以取到;但是在实际验证时,还是有问题的;


验证时调用方法;

2.后端处理

管理员控制层

AdminController

package com.xiaozhi.backserver.startspringboot.controller;import com.auth0.jwt.interfaces.DecodedJWT;
import com.github.pagehelper.PageInfo;
import com.xiaozhi.backserver.startspringboot.model.Admin;
import com.xiaozhi.backserver.startspringboot.model.Role;
import com.xiaozhi.backserver.startspringboot.service.AdminService;
import com.xiaozhi.backserver.startspringboot.util.CommonResult;
import com.xiaozhi.backserver.startspringboot.util.JWTUtil;
import com.xiaozhi.backserver.startspringboot.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;/*** @author by 信计1801班 李智青(小智RE0)* @date 2021-12-29 18:54*/
@RestController
@RequestMapping(value = "/api/admin")
public class AdminController {CommonResult commonResult;@AutowiredAdminService adminService;//查询所有的管理员,且查看下属的角色@PostMapping(path = "/getAllAdmin")public CommonResult getAllAdmin(@RequestBody Admin admin) {try {//调用方法查询所有的管理员;PageInfo<Admin> allAdmin = adminService.getAllAdmin(admin);// getList() 当前分页查询到的数据; getTotal() 当前数据列表的总数据量;commonResult = new CommonResult(200, "查询管理员列表信息成功", allAdmin.getList(), allAdmin.getTotal());} catch (Exception e) {e.printStackTrace();commonResult = new CommonResult(500, "服务器异常", null);}return commonResult;}//查询到角色的列表;@GetMapping(value = "/roles")public CommonResult getRoleS() {try {List<Role> roleList = adminService.getRoleList();if (!roleList.isEmpty()) {commonResult = new CommonResult(200, "查询角色列表成功", roleList);} else {commonResult = new CommonResult(201, "角色列表查询失败", null);}} catch (Exception e) {e.printStackTrace();commonResult = new CommonResult(500, "服务器异常", null);}return commonResult;}//添加管理员信息;@PostMapping(value = "/addAdmin")public CommonResult addAdmin(@RequestBody Admin admin) {//System.out.println("----------添加管理员----------------");try {adminService.addAdminInfo(admin);commonResult = new CommonResult(200, "添加管理员成功", null);} catch (Exception e) {e.printStackTrace();commonResult = new CommonResult(500, "服务器异常", null);}return commonResult;}//根据Id查询到管理员的信息;@GetMapping(value = "/findAdmin/{id}")public CommonResult findAdmin(@PathVariable("id") Integer id) {try {Admin admin = adminService.findAdmin(id);commonResult = new CommonResult(200, "查询管理员信息成功", admin);} catch (Exception e) {e.printStackTrace();commonResult = new CommonResult(500, "服务器异常", null);}return commonResult;}//更新管理员信息;@PostMapping(value = "/updateAdmin")public CommonResult updateAdmin(@RequestBody Admin admin) {try {//System.out.println("----------当前修改信息的管理员是------------"+admin.getId());adminService.updateAdmin(admin);commonResult = new CommonResult(200, "管理员信息修改成功", null);} catch (Exception e) {e.printStackTrace();commonResult = new CommonResult(500, "服务器异常", null);}return commonResult;}//删除管理员的信息;@DeleteMapping(value = "/deleteAdmin/{id}")public CommonResult deleteAdmin(@PathVariable("id") Integer id) {try {adminService.deleteAdmin(id);commonResult = new CommonResult(200, "删除成功", null);} catch (Exception e) {e.printStackTrace();commonResult = new CommonResult(500, "服务器异常", null);}return commonResult;}//管理员上传头像;@PostMapping(value = "/upAdminAvatar")public CommonResult upAdminAvatar(@RequestHeader("token") String token, @RequestParam("file") CommonsMultipartFile file) {//System.out.println("触发上传头像案例--->");try {//先从token令牌中取得管理员的信息;//由token中获取Id和type;DecodedJWT info = JWTUtil.getTokenInfo(token);Integer id = info.getClaim("id").asInt();String account = info.getClaim("account").asString();//会根据用户的账户名创建文件夹,将头像存进去;File f0 = new File("D:\\项目图片服务器\\apache-tomcat-9.0.43\\webapps\\ROOT\\studyspringbootvue\\" + account);//若不存在就为用户创建文件夹;if (!f0.exists()) {f0.mkdir();}//使用工具类;将头像名称进行重命名;String newFileName = StringUtil.getNewFileName(file.getOriginalFilename());//将上传的头像放进创建好的文件夹;File f1 = new File(f0, newFileName);//文件存入执行;file.transferTo(f1);//根据Id信息;将管理员的头像信息存入数据库;adminService.insertAdminAvatar(id, newFileName, file.getOriginalFilename());//实际回显的管理员信息;  由于是按照账户名创建的文件夹;所以这里就传回账户名和头像名;Admin admin = new Admin();admin.setAccount(account);admin.setNewFileName(newFileName);commonResult = new CommonResult(200, "管理员头像上传成功", admin);} catch (Exception e) {e.printStackTrace();commonResult = new CommonResult(500, "服务器异常", null);}return commonResult;}//验证旧密码;@GetMapping(value = "/checkAdminPass/{oldPass}")public CommonResult checkAdminPass(@PathVariable("oldPass") String oldPass,@RequestHeader("token") String token) {try {//根据Id查询一下管理员密码;Admin admin = adminService.checkAdminPass(token);//检验密码;if (oldPass.equals(admin.getPassword())) {commonResult = new CommonResult(200, "旧密码正确", true);} else {commonResult = new CommonResult(200, "旧密码错误", false);}} catch (Exception e) {e.printStackTrace();commonResult = new CommonResult(500, "服务器异常", null);}return commonResult;}//保存修改后的新密码;@PutMapping(value = "/updatePassword/{newPass}")public CommonResult updatePassword(@PathVariable("newPass") String password,@RequestHeader("token") String token){try{//根据Id修改管理员的密码;adminService.updatePassword(token,password);commonResult = new CommonResult(200, "修改密码成功", null);}catch (Exception e){e.printStackTrace();commonResult = new CommonResult(500, "服务器异常", null);}return commonResult;}
}

调用的服务层

package com.xiaozhi.backserver.startspringboot.service;import com.auth0.jwt.interfaces.DecodedJWT;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xiaozhi.backserver.startspringboot.dao.AdminDao;
import com.xiaozhi.backserver.startspringboot.model.Admin;
import com.xiaozhi.backserver.startspringboot.model.Role;
import com.xiaozhi.backserver.startspringboot.util.JWTUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.io.File;
import java.util.List;/*** @author by 信计1801班 李智青(小智RE0)* @date 2021-12-29 18:57*/
@Transactional
@Service
public class AdminService {@AutowiredAdminDao adminDao;/***查询得到所有的管理员列表*/public PageInfo<Admin> getAllAdmin(Admin admin) {PageHelper.startPage(admin.getPageNum(), admin.getPageSize());List<Admin> allAdmin = adminDao.getAllAdmin(admin);PageInfo<Admin> info = new PageInfo<>(allAdmin);return info;}//查询角色的列表;public List<Role> getRoleList() {return adminDao.getRoleList();}//添加管理员信息;public void addAdminInfo(Admin admin){//为管理员设置默认密码,admin.setPassword("123");adminDao.addAdminInfo(admin);//System.out.println("--------------------");//System.out.println(Arrays.stream(admin.getRoleId()));// System.out.println("--------------------");//若为管理员选择了角色信息;再向数据表添加数据;if(admin.getRoleId()!=null){for (Integer rId : admin.getRoleId()) {adminDao.addAdminAndRoleId(admin.getId(),rId);}}}//根据Id查询管理员信息;public Admin findAdmin(Integer id) {Admin admin = adminDao.findAdmin(id);//将查询到的管理员Id存入到Id数组中;Integer[] roleIds = new Integer[admin.getRoleList().size()];for (int i = 0; i < admin.getRoleList().size(); i++) {roleIds[i] = admin.getRoleList().get(i).getId();}admin.setRoleId(roleIds);return admin;}//更新管理员信息;public void updateAdmin(Admin admin) {//首先把管理员表的信息修改了;adminDao.updateAdmin(admin);//考虑可能会更新管理员姓名,头像的文件夹需要移动;//先判断是否修改了姓名; initAccount:原来的账户名; account:现在的账户名;if(!admin.getInitAccount().equals(admin.getAccount())){//原来的头像文件夹;File f = new File("D:\\项目图片服务器\\apache-tomcat-9.0.43\\webapps\\ROOT\\studyspringbootvue\\"+admin.getInitAccount());//会根据用户的账户名创建文件夹,将头像存进去;File f0 =new File("D:\\项目图片服务器\\apache-tomcat-9.0.43\\webapps\\ROOT\\studyspringbootvue\\"+admin.getAccount());//将原先的文件夹重命名,文件也会转移过来;f.renameTo(f0);}//然后删除掉原先的 管理员与角色 的关联信息;adminDao.deleteAIdAndRId(admin.getId());//再将新的关联信息存入;//System.out.println("--------------------");//System.out.println(Arrays.stream(admin.getRoleId()));//System.out.println("--------------------");if(admin.getRoleId()!=null){for (Integer rId : admin.getRoleId()) {adminDao.addAdminAndRoleId(admin.getId(),rId);}}}//删除管理员信息;public void deleteAdmin(Integer id) {//首先把管理员与角色信息关联表的信息删除;adminDao.deleteAIdAndRId(id);//然后删除管理员表;adminDao.deleteAdmin(id);}//将管理员的头像信息保存到数据库;public void insertAdminAvatar(Integer id, String newFileName,String oldFileName) {Admin admin = new Admin();admin.setId(id);admin.setNewFileName(newFileName);admin.setOldFileName(oldFileName);adminDao.insertAdminAvatar(admin);}//根据密码查询管理员是否存在;public Admin checkAdminPass(String token) {//这里先从token中解析到管理员的Id;DecodedJWT info = JWTUtil.getTokenInfo(token);Integer id = info.getClaim("id").asInt();//根据ID查询管理员信息;return adminDao.checkAdminPass(id);}//根据Id修改密码;public void updatePassword(String token, String password) {//这里先从token中解析到管理员的Id;DecodedJWT info = JWTUtil.getTokenInfo(token);Integer id = info.getClaim("id").asInt();//根据Id修改信息;adminDao.updatePassword(id,password);}
}

管理员服务层

package com.xiaozhi.backserver.startspringboot.service;import com.auth0.jwt.interfaces.DecodedJWT;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xiaozhi.backserver.startspringboot.dao.AdminDao;
import com.xiaozhi.backserver.startspringboot.model.Admin;
import com.xiaozhi.backserver.startspringboot.model.Role;
import com.xiaozhi.backserver.startspringboot.util.JWTUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.io.File;
import java.util.List;/*** @author by 信计1801班 李智青(小智RE0)* @date 2021-12-29 18:57*/
@Transactional
@Service
public class AdminService {@AutowiredAdminDao adminDao;/***查询得到所有的管理员列表*/public PageInfo<Admin> getAllAdmin(Admin admin) {PageHelper.startPage(admin.getPageNum(), admin.getPageSize());List<Admin> allAdmin = adminDao.getAllAdmin(admin);PageInfo<Admin> info = new PageInfo<>(allAdmin);return info;}//查询角色的列表;public List<Role> getRoleList() {return adminDao.getRoleList();}//添加管理员信息;public void addAdminInfo(Admin admin){//为管理员设置默认密码,admin.setPassword("123");adminDao.addAdminInfo(admin);//System.out.println("--------------------");//System.out.println(Arrays.stream(admin.getRoleId()));// System.out.println("--------------------");//若为管理员选择了角色信息;再向数据表添加数据;if(admin.getRoleId()!=null){for (Integer rId : admin.getRoleId()) {adminDao.addAdminAndRoleId(admin.getId(),rId);}}}//根据Id查询管理员信息;public Admin findAdmin(Integer id) {Admin admin = adminDao.findAdmin(id);//将查询到的管理员Id存入到Id数组中;Integer[] roleIds = new Integer[admin.getRoleList().size()];for (int i = 0; i < admin.getRoleList().size(); i++) {roleIds[i] = admin.getRoleList().get(i).getId();}admin.setRoleId(roleIds);return admin;}//更新管理员信息;public void updateAdmin(Admin admin) {//首先把管理员表的信息修改了;adminDao.updateAdmin(admin);//考虑可能会更新管理员姓名,头像的文件夹需要移动;//先判断是否修改了姓名; initAccount:原来的账户名; account:现在的账户名;if(!admin.getInitAccount().equals(admin.getAccount())){//原来的头像文件夹;File f = new File("D:\\项目图片服务器\\apache-tomcat-9.0.43\\webapps\\ROOT\\studyspringbootvue\\"+admin.getInitAccount());//会根据用户的账户名创建文件夹,将头像存进去;File f0 =new File("D:\\项目图片服务器\\apache-tomcat-9.0.43\\webapps\\ROOT\\studyspringbootvue\\"+admin.getAccount());//将原先的文件夹重命名,文件也会转移过来;f.renameTo(f0);}//然后删除掉原先的 管理员与角色 的关联信息;adminDao.deleteAIdAndRId(admin.getId());//再将新的关联信息存入;//System.out.println("--------------------");//System.out.println(Arrays.stream(admin.getRoleId()));//System.out.println("--------------------");if(admin.getRoleId()!=null){for (Integer rId : admin.getRoleId()) {adminDao.addAdminAndRoleId(admin.getId(),rId);}}}//删除管理员信息;public void deleteAdmin(Integer id) {//首先把管理员与角色信息关联表的信息删除;adminDao.deleteAIdAndRId(id);//然后删除管理员表;adminDao.deleteAdmin(id);}//将管理员的头像信息保存到数据库;public void insertAdminAvatar(Integer id, String newFileName,String oldFileName) {Admin admin = new Admin();admin.setId(id);admin.setNewFileName(newFileName);admin.setOldFileName(oldFileName);adminDao.insertAdminAvatar(admin);}//根据密码查询管理员是否存在;public Admin checkAdminPass(String token) {//这里先从token中解析到管理员的Id;DecodedJWT info = JWTUtil.getTokenInfo(token);Integer id = info.getClaim("id").asInt();//根据ID查询管理员信息;return adminDao.checkAdminPass(id);}//根据Id修改密码;public void updatePassword(String token, String password) {//这里先从token中解析到管理员的Id;DecodedJWT info = JWTUtil.getTokenInfo(token);Integer id = info.getClaim("id").asInt();//根据Id修改信息;adminDao.updatePassword(id,password);}
}

管理员持久层

package com.xiaozhi.backserver.startspringboot.dao;import com.xiaozhi.backserver.startspringboot.model.Admin;
import com.xiaozhi.backserver.startspringboot.model.Role;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;import java.util.List;/*** @author by 信计1801班 李智青(小智RE0)* @date 2021-12-29 18:58*/
@Repository
public interface AdminDao {//获取到所有的管理员;List<Admin> getAllAdmin(Admin admin);//查询角色列表;List<Role> getRoleList();//根据管理员Id查询信息;Admin findAdmin(Integer id);//添加管理员信息void addAdminInfo(Admin admin);//保存管理员与角色Id;到中间的关系表中;void addAdminAndRoleId(@Param("aId") Integer aId, @Param("rId") Integer rID);//修改管理员的信息void updateAdmin(Admin admin);//删除管理员与角色的关联信息void deleteAIdAndRId(Integer id);//删除管理员表的信息void deleteAdmin(Integer id);//将管理员的头像信息存入数据库;void insertAdminAvatar(Admin admin);//查询管理员密码Admin checkAdminPass(Integer id);//根据Id修改密码;void updatePassword(Integer id, String password);
}

头像上传时的后端全局配置类

package com.xiaozhi.backserver.startspringboot.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;//上传文件的配置类;
@Configuration
public class MultipartConfig {/*** 文件上传配置* @return*/@Beanpublic CommonsMultipartResolver multipartConfigElement() {CommonsMultipartResolver multipartresolver = new CommonsMultipartResolver();multipartresolver.setMaxUploadSize(1024*1024*5);return multipartresolver;}
}

头像上传时重命名格式的工具类

package com.xiaozhi.backserver.startspringboot.util;import java.text.SimpleDateFormat;
import java.util.Date;/***  自定义的String工具类*/
public class StringUtil {/*** 截取文件扩展名* @param fileName 文件名* @return*/public static String subFileType(String fileName){if(fileName!=null){return fileName.substring(fileName.lastIndexOf(".")+1);}return null;}/*** 生成新的文件名;* @param oldFileName 旧的文件名* @return            新的文件名*/public static String getNewFileName(String oldFileName) {//利用日期类生成;Date date = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");//拼接进去;return sdf.format(date) + "." + subFileType(oldFileName);}
}

管理员对应的映射SQl

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--注意 这里对应空间为持久层映射接口-->
<mapper namespace="com.xiaozhi.backserver.startspringboot.dao.AdminDao"><!--自定义的结果集,其中需要关联角色--><resultMap id="adminmap" type="admin"><id property="id" column="id"/><result property="account" column="account"/><result property="password" column="password"/><result property="sex" column="sex"/><result property="phone" column="phone"/><result property="newFileName" column="new_file_name"/><result property="operTime" column="oper_time"/><result property="type" column="type"/><collection property="roleList" javaType="list" ofType="role"select="getRoleBYAdminId" column="id"><result property="name" column="name"/><result property="id"   column="role_id"/></collection></resultMap><!--查询所有的管理员;--><select id="getAllAdmin" parameterType="admin" resultMap="adminmap">SELECT`id`,`account`,`sex`,`phone`,`new_file_name`,`type`,`oper_time`FROM t_adminWHERE  `type` = 1<if test="account!=''">and `account` =#{account}</if><if test="sex!=''">and `sex` =#{sex}</if></select><!--根据管理员的id查询到对应的角色信息--><select id="getRoleBYAdminId" resultType="role">selectr.namefromt_admin_role arleft join t_role r on ar.`role_id`=r.`id`where ar.`admin_id` =#{id}</select><!--查询角色的列表--><select id="getRoleList" resultType="role">select id,name,role_desc  from t_role</select><!--添加管理员信息,会立即返回这位管理员的主键Id--><insert id="addAdminInfo" parameterType="admin" useGeneratedKeys="true" keyProperty="id" keyColumn="id">insert into t_admin(`account`,`sex`,`phone`,`password`,`oper_time`)values (#{account},#{sex},#{phone},#{password},now())</insert><!--保存管理员与角色的关系--><insert id="addAdminAndRoleId">insert into t_admin_role(admin_id,role_id)values (#{aId},#{rId})</insert><!--结果集集合2--><resultMap id="adminmap1" type="admin"><id column="id" property="id"></id><result column="account" property="account"></result><result column="phone" property="phone"></result><result column="sex" property="sex"></result><collection property="roleList" javaType="list" ofType="role"><result column="role_id" property="id"></result></collection></resultMap><!--根据管理员的Id查询管理员的信息--><select id="findAdmin" resultMap="adminmap1">SELECTa.`id`,a.`account`,a.`sex`,a.`phone`,ar.`role_id`FROM t_admin aLEFT JOIN t_admin_role ar ON ar.`admin_id`=a.`id`WHERE id = #{id}</select><!--根据Id修改管理员的信息--><update id="updateAdmin">update t_admin set account=#{account},sex=#{sex},phone=#{phone},oper_time=now()where id = #{id}</update><!--删除管理员与角色表的关联--><delete id="deleteAIdAndRId">delete from t_admin_role where admin_id= #{id}</delete><!--删除管理员表的信息--><delete id="deleteAdmin">delete from t_admin where id= #{id}</delete><!--管理员的头像信息存入数据库--><insert id="insertAdminAvatar">update t_admin set `new_file_name`=#{newFileName},`old_file_name`=#{oldFileName},oper_time=now()where `id`=#{id}</insert><!--查询管理员的密码--><select id="checkAdminPass" resultType="admin">select `password` from t_admin where id= #{id}</select><!--根据Id修改管理员密码--><update id="updatePassword">update t_admin set `password`= #{password}  where id= #{id}</update></mapper>

前后端分离学习笔记(5) ---[表单的增删改操作;以及为管理员上传头像]相关推荐

  1. java前后端分离的实现方式_采用前后端分离的方式进行开发,实现了几种常用的文件上传功能...

    MyUploader-Backend 单文件上传,多文件上传,大文件上传,断点续传,文件秒传,图片上传 简介 采用前后端分离的方式进行开发,实现了几种常用的文件上传功能. 前端采用 vue.js + ...

  2. 若依前后端分离版生成代码实现仓库的增删改查

    写在前面 RuoYi-Vue 是一个 Java EE 企业级快速开发平台,基于经典技术组合(Spring Boot.Spring Security.MyBatis.Jwt.Vue),内置模块如:部门管 ...

  3. python表单提交的两种方式_Flask框架学习笔记之表单基础介绍与表单提交方式

    本文实例讲述了Flask框架学习笔记之表单基础介绍与表单提交方式.分享给大家供大家参考,具体如下: 表单介绍 表单是HTML页面中负责数据采集功能的部件.由表单标签,表单域和表单按钮组成.通过表单,将 ...

  4. ASP.NET MVC 2 学习笔记二: 表单的灵活提交

    ASP.NET MVC 2 学习笔记二:  表单的灵活提交 前面说到有做到公司内部的一个请假系统,用的是ASP.NET MVC 2+Entity Framework.虽然EF(Entity Frame ...

  5. Spring+SpringMVC+MyBatis明日方舟版人员信息管理系统前端页面代码前后端交互+SSM框架 管理员登录 游客登录 普通用户登录 人员的增删改查 信息更新 图片上传 分页查询)

    Spring+SpringMVC+MyBatis明日方舟版人员信息管理系统前端页面代码(前后端交互+SSM框架 管理员登录 游客登录 普通用户登录 人员的增删改查 信息更新 图片上传 分页查询 修改密 ...

  6. SpringBoot +Vue前后端分离(笔记)

    前后端分离简介 前后端分离 前后端分离就是将⼀个应⽤的前端代码和后端代码分开写,为什么要这样做? 如果不使⽤前后端分离的⽅式,会有哪些问题? 传统的 Java Web 开发中,前端使⽤ JSP 开发, ...

  7. 阿ken的HTML、CSS的学习笔记_表单的应用(笔记七)

    欢迎光临 你好 我是阿ken 文章目录 7.1_认识表单 7.1.1_表单的构成 7.1.2_创建表单 ( < form> 标记 ) 7.2_表单属性 1. action 属性 2. me ...

  8. 若依前后端分离版获取部门表所有最子级部门并匹配部门名称生成excel

    场景 若依前后端分离版手把手教你本地搭建环境并运行项目: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662 在上面 ...

  9. react学习笔记(9)表单控件

    1.表单控件的操作 1.数据双向绑定 demo1: 在输入框中输入或者删除数据时,可以实时监控. class App extends React.Component {state = {val : & ...

最新文章

  1. 计算机windows8黑屏怎么办,老司机为你解说win8系统电脑黑屏开不了机的处理办法...
  2. 【C/C++16】_public.h,_public.cpp,_cmpublic.h
  3. 时间设置偏移秒_零偏移有源低通滤波器,第2部分
  4. Linux shell 内部命令与外部命令有什么区别以及怎么辨别
  5. Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine))
  6. java学习(74):GUL面板
  7. python3 列表生成式
  8. Less(v3.9.0)使用详解—变量
  9. nginx热升级实现
  10. vmware 下centos7配置网络
  11. win10下安装STEP7
  12. otc焊接机器人编程模拟软件_OTC机器人编程
  13. Python爬取房天下二手房信息
  14. 前端中适配各种手机模式的一种解决办法
  15. 32位电脑和64位电脑
  16. 树莓派4B 编译安装rtl8192eu usb网卡驱动
  17. Excel基础—文件菜单之设置选项
  18. 为什么要早点进入软件测试行业?现在加入晚了吗?
  19. matlab求解二维矩阵并画图,Matlab教程2_ 绘图 _ 二维(2)
  20. 获取网页链接,比正则表达式更灵活更大众化.

热门文章

  1. 计算机操作题ppt考试题库,计算机二级考试MSOffice考试题库ppt操作题附答案
  2. Win 7系统倒计时!
  3. 【MM小贴士】SAP内询报价标准功能的使用
  4. 函数调用关系/结构图Callgraph
  5. 为什么机器学习不是人工智能?
  6. 大疆览沃浩界(Livox Horizon)激光雷达测评
  7. python中多个print如何打印在一行中,Python 让两个print()函数的输出打印在一行内...
  8. dropbox_Plerd:兼容Dropbox的Markdown博客平台
  9. ArcGIS_基于DEM的地形起伏度计算
  10. 〖编程初学者的自我修养 - 职业规划篇④〗- 优秀职人必懂、必会的职业规划