• SpringBoot+Vue实现的高校图书馆管理系统 附带详细运行指导视频


    一、项目演示

    项目演示地址: 视频地址

    二、项目介绍

    项目描述:这是一个基于SpringBoot+Vue框架开发的高校图书馆管理系统。首先,这是一个前后端分离的项目,代码简洁规范,注释说明详细,易于理解和学习。其次,这项目功能丰富,具有一个高校图书馆管理系统该有的所有功能。

    项目功能:此项目分为两个角色:学生管理员学生有登录注册、管理个人信息、浏览座位信息、预约选座、浏览图书信息、借阅图书、浏览借阅信息、管理预约信息等等功能。管理员有管理所有用户新息、管理所有座位信息、管理所有时刻信息、管理所有信誉积分信息、管理所有图书信息、管理所有预约选座、借阅信息等等功能。

    应用技术:SpringBoot + Vue + MySQL + MyBatis + Redis + ElementUI

    运行环境:IntelliJ IDEA2019.3.5 + MySQL5.7(项目压缩包中自带) + Redis5.0.5(项目压缩包中自带) + JDK1.8 + Gradle5.6.4(项目压缩包中自带)+ Node14.16.1(项目压缩包中自带)

    三、项目运行截图

    项目运行截图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    四、主要代码

    1.前端预约选座页面代码:

    <template>
        <div>
            
            <el-breadcrumb separator-class="el-icon-arrow-right">
                <el-breadcrumb-item :to="{ path: '/home/dash-board' }">首页el-breadcrumb-item>
                <el-breadcrumb-item>选座页面el-breadcrumb-item>
            el-breadcrumb>
            
            <el-form :inline="true" :model="searchParams" class="user-search">
                <el-form-item label="搜索:">
                    <el-date-picker
                            value-format="yyyy-MM-dd"
                            format="yyyy-MM-dd"
                            size="small"
                            v-model="searchParams.openTime"
                            type="date"
                            :picker-options="pickerOptions"
                            :clearable="false"
                            :editable="false">
                    el-date-picker>
                el-form-item>
                <el-form-item>
                    <el-select v-model="searchParams.scheduleId" size="small" placeholder="请选择时刻">
                        <el-option v-for="(item, index) in scheduleList" :key="index" :label="item.name + '(' + item.rangeTime + ')'" :value="item.id">el-option>
                    el-select>
                el-form-item>
                <el-form-item>
                    <el-button size="small" type="primary" icon="el-icon-search" @click="searchByTime">搜索el-button>
                    <el-button size="small" v-if="loginUser.roleId === 1" type="primary" icon="el-icon-date" @click="pickOrderSeat">预约el-button>
                el-form-item>
            el-form>
            <el-table :key="index" v-if="seat.row !== 0 && seat.col !== 0" size="small" :data="tableData" v-loading="loading" border element-loading-text="拼命加载中" style="width: 100%;">
                <el-table-column width="55">
                    <template slot-scope="scope">
                        <span style="color: #909399; font-weight: bold;">第{{scope.$index + 1}}行span>
                    template>
                el-table-column>
                <el-table-column :width="seat.col > 9 ? 150 : ''" v-for="n in seat.col" :key="n"  align="center" :label="'第' + n + '列'" >
                    <template slot-scope="scope">
    
                        <el-badge v-if="scope.row.picked[n-1] === 0" type="danger" value="占座" style="margin-top: 10px">
                            <img style="width: 30px; height: 30px; margin-top: 10px"
                                 src="../../assets/img/sold_seat.jpg" />
                            <div>第{{scope.$index + 1}}行第{{n}}列div>
                        el-badge>
                        <el-badge v-else-if="scope.row.picked[n-1] === 1" type="primary" value="可选" style="margin-top: 10px">
                            <div @click="pickSeat(scope.$index, n-1)">
                                <img style="width: 30px; height: 30px; margin-top: 10px"
                                     src="../../assets/img/selectable_seat.jpg" />
                                <div>第{{scope.$index + 1}}行第{{n}}列div>
                            div>
                        el-badge>
                        <el-badge v-else-if="scope.row.picked[n-1] === 2" type="success" value="选中" style="margin-top: 10px">
                            <div @click="cancelSeat(scope.$index + 1, n)">
                                <img style="width: 30px; height: 30px; margin-top: 10px"
                                     src="../../assets/img/selected_seat.jpg" />
                                <div>第{{scope.$index + 1}}行第{{n}}列div>
                            div>
                        el-badge>
                    template>
                el-table-column>
            el-table>
    
            <el-empty v-else :image-size="200">el-empty>
        div>
    template>
    
    <script>
        export default {
            name: 'PickList',
            data() {
                return {
                    pickerOptions: {
                        disabledDate: (time) => {
                            return time.getTime() < Date.now() - 8.64e7
                        }
                    },
                    searchParams: {
                        openTime: '',
                        scheduleId: ''
                    },
                    seat: {
                        row: 0,
                        col: 0
                    },
                    scheduleList: [],
                    loading: false,
                    tableData: [],
                    pickSeatList: [],
                    seatItemList: [],
                    index: 0,
                    loginUser: {}
                }
            },
            created() {
                this.searchParams.openTime = this.getNowDay();
                this.getLoginUser();
                this.getAllScheduleList();
            },
            methods: {
                getNowDay() {
                    let date = new Date();
                    let y = date.getFullYear();
                    let m = date.getMonth() + 1;
                    m = m < 10 ? ('0' + m) : m;
                    let d = date.getDate();
                    d = d < 10 ? ('0' + d) : d;
                    return  y + '-' + m + '-' + d;
                },
                getLoginUser() {
                    this.$ajax.post("/user/getLoginUser", {token: Tool.getLoginUser()}).then((response)=>{
                        let resp = response.data;
                        if(resp.code === 0){
                            if(resp.data) {
                                this.loginUser = resp.data;
                            } else {
                                this.$message.error(resp.msg);
                                this.$router.push("/login");
                            }
                        }
                    });
                },
                pickSeat(row, col) {
                    if(this.pickSeatList.length >= 1) {
                        this.$message.warning("一次最多只能选1个座位!");
                        return false;
                    }
                    this.tableData[row].picked[col] = 2;
                    // 动态刷新表格
                    this.index = this.index + 1;
                    this.pickSeatList.push({row: row + 1, col: col + 1});
                },
                cancelSeat(row, col) {
                    this.tableData[row-1].picked[col-1] = 1;
                    // 动态刷新表格
                    this.index = this.index + 1;
                    let nowSeatList = [];
                    this.pickSeatList.forEach(item => {
                        if(item.col !== col || item.row !== row) {
                            nowSeatList.push(item);
                        }
                    });
                    this.pickSeatList = nowSeatList;
                },
                getSeatBySearch() {
                    this.loading = true;
                    this.$ajax.post("/seat/getByTime", this.searchParams).then((response)=>{
                        let resp = response.data;
                        if(resp.code === 0){
                            if(resp.data) {
                                this.seat = resp.data;
                                this.getSeatItem();
                            } else {
                                this.seat = {row: 0, col: 0};
                            }
                        }
                    });
                },
                initSeatDetailList() {
                    this.tableData = [];
                    for(let i=1; i<=this.seat.row; i++) {
                        let item = {picked: []};
                        for(let j=1; j<=this.seat.col; j++) {
                            item.picked.push(this.isPicked(i, j));
                        }
                        this.tableData.push(item);
                    }
                    this.loading = false;
                },
                isPicked(row, col) {
                    let result = 1;
                    this.seatItemList.forEach(item => {
                        if(item.row === row && item.col === col) {
                            result =  0; // 已被占座
                        }
                    });
                    return result; // 未被占座
                },
                getSeatItem() {
                    this.$ajax.post("/seat/getItemBySeatId", {id: this.seat.id}).then((response)=>{
                        let resp = response.data;
                        if(resp.code === 0){
                            this.seatItemList = resp.data;
                            this.initSeatDetailList();
                        } else {
                            this.$message.error(resp.msg);
                        }
                    });
                },
                pickOrderSeat() {
                     if(this.pickSeatList.length === 0) {
                         this.$message.warning("请至少选择一个座位!");
                         return false;
                     }
                     let seat = this.pickSeatList[0];
                     this.$confirm('确定要预约第'+ seat.row +'行第'+ seat.col +'列的座位?', '提示', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        type: 'warning'
                     }).then(() => {
                         this.$ajax.post("/seat/pick", {...seat, seatId: this.seat.id, userId: this.loginUser.id}).then((response)=>{
                             let resp = response.data;
                             if(resp.code === 0){
                                 this.$message.success(resp.msg);
                                 this.getSeatBySearch();
                                 this.pickSeatList = [];
                             } else {
                                 this.$message.error(resp.msg);
                             }
                         });
                     });
                },
                searchByTime() {
                    this.$confirm('搜索将导致当前选中的记录丢失,确认继续此操作?', '提示', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        type: 'warning'
                    }).then(() => {
                        this.pickSeatList = [];
                        this.getSeatBySearch();
                    });
                },
                getAllScheduleList() {
                    this.$ajax.post("/schedule/all").then((response)=>{
                        let resp = response.data;
                        if(resp.code === 0){
                            this.scheduleList = resp.data;
                            this.searchParams.scheduleId = this.scheduleList[0] ? this.scheduleList[0].id : '';
                            this.getSeatBySearch();
                        }
                    });
                }
            }
        }
    script>
    
    <style scoped>
        .user-search {
            margin-top: 20px;
        }
        /deep/ .el-table tbody tr:hover>td {
            background-color:unset !important
        }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244

    2.借阅图书业务逻辑代码:

    	/**
         * 借阅图书操作
         * @param rentalItemDTO
         * @return
         */
        @Override
        public ResponseDTO<Boolean> rentalBook(RentalItemDTO rentalItemDTO) {
            // 进行统一表单验证
            CodeMsg validate = ValidateEntityUtil.validate(rentalItemDTO);
            if(!validate.getCode().equals(CodeMsg.SUCCESS.getCode())){
                return ResponseDTO.errorByMsg(validate);
            }
            User user = userMapper.selectByPrimaryKey(rentalItemDTO.getUserId());
            if(user == null) {
                return ResponseDTO.errorByMsg(CodeMsg.USER_NOT_EXIST);
            }
            if (user.getCreditRate() < 80) {
                return ResponseDTO.errorByMsg(CodeMsg.CREDIT_RENTAL_ERROR);
            }
            Book book = bookMapper.selectByPrimaryKey(rentalItemDTO.getBookId());
            if(book == null) {
                return ResponseDTO.errorByMsg(CodeMsg.BOOK_NOT_EXIST);
            }
            if(CommonUtil.differentDaysByMillisecond(new Date(), rentalItemDTO.getPredictTime()) > 180) {
                return  ResponseDTO.errorByMsg(CodeMsg.BOOK_RENTAL_TIME_OVER);
            }
            // 修改图书状态 乐观锁控制
            BookExample bookExample = new BookExample();
            bookExample.createCriteria().andIdEqualTo(book.getId()).andVersionEqualTo(book.getVersion());
            book.setState(BookStateEnum.RENTAL.getCode());
            book.setVersion(book.getVersion() + 1);
            if(bookMapper.updateByExampleSelective(book, bookExample) == 0) {
                return ResponseDTO.errorByMsg(CodeMsg.BOOK_EDIT_ERROR);
            }
            // 写入借阅详情信息
            RentalItem rentalItem = CopyUtil.copy(rentalItemDTO, RentalItem.class);
            rentalItem.setId(UuidUtil.getShortUuid());
            rentalItem.setCreateTime(new Date());
            rentalItem.setBookName(book.getName());
            rentalItem.setBookPhoto(book.getPhoto());
            rentalItem.setState(RentalItemStateEnum.RENTAL.getCode());
            if(rentalItemMapper.insertSelective(rentalItem) == 0) {
                throw new RuntimeException(CodeMsg.BOOK_RENTAL_ERROR.getMsg());
            }
            return ResponseDTO.successByMsg(true, "借阅成功!");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    3.用户登录业务逻辑代码:

     	/**
         * 用户登录操作
         * @param userDTO
         * @return
         */
        @Override
        public ResponseDTO<UserDTO> login(UserDTO userDTO) {
            // 进行是否为空判断
            if(CommonUtil.isEmpty(userDTO.getUsername())){
                return ResponseDTO.errorByMsg(CodeMsg.USERNAME_EMPTY);
            }
            if(CommonUtil.isEmpty(userDTO.getPassword())){
                return ResponseDTO.errorByMsg(CodeMsg.PASSWORD_EMPTY);
            }
            if(CommonUtil.isEmpty(userDTO.getCaptcha())){
                return ResponseDTO.errorByMsg(CodeMsg.CAPTCHA_EMPTY);
            }
            if(CommonUtil.isEmpty(userDTO.getCorrectCaptcha())){
                return ResponseDTO.errorByMsg(CodeMsg.CAPTCHA_EXPIRED);
            }
            // 比对验证码是否正确
            String value = stringRedisTemplate.opsForValue().get((userDTO.getCorrectCaptcha()));
            if(CommonUtil.isEmpty(value)){
                return ResponseDTO.errorByMsg(CodeMsg.CAPTCHA_EXPIRED);
            }
            if(!value.toLowerCase().equals(userDTO.getCaptcha().toLowerCase())){
                return ResponseDTO.errorByMsg(CodeMsg.CAPTCHA_ERROR);
            }
            // 对比昵称和密码是否正确
            UserExample userExample = new UserExample();
            // select * from user where username = ? and password = ?
            userExample.createCriteria().andUsernameEqualTo(userDTO.getUsername()).andPasswordEqualTo(userDTO.getPassword());
            List<User> userList = userMapper.selectByExample(userExample);
            if(userList == null || userList.size() != 1){
                return ResponseDTO.errorByMsg(CodeMsg.USERNAME_PASSWORD_ERROR);
            }
            // 生成登录token并存入Redis中
            UserDTO selectedUserDto = CopyUtil.copy(userList.get(0), UserDTO.class);
            String token = UuidUtil.getShortUuid();
            selectedUserDto.setToken(token);
            //把token存入redis中 有效期1小时
            stringRedisTemplate.opsForValue().set("USER_" + token, JSON.toJSONString(selectedUserDto), 3600, TimeUnit.SECONDS);
            return ResponseDTO.successByMsg(selectedUserDto, "登录成功!");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
  • 相关阅读:
    【微软技术栈】C#.NET 中的本地化
    MATLAB中uiresume函数用法
    C++入门刷题练习(附代码、思路以及相关拓展)
    java毕业生设计信管专业毕业生就业管理信息系统计算机源码+系统+mysql+调试部署+lw
    剑指offer 23. 反转链表
    安卓(Android)面试题库(含答案)
    用Roslyn玩转代码之一: 解析与执行字符串表达式
    四、《任务列表案例》后端程序实现和测试
    物理机服务器应该注意的事
    lua-arm平台交叉编译
  • 原文地址:https://blog.csdn.net/dgfdhgghd/article/details/127848612