开始尝试从0写一个项目--前端(三)

器材管理板块

添加器材管理导航

src\views\home\Home.vue

src\router\index.js

src\views\equipment\Equipment.vue

复制代码
<template>
    <div>
        hello!
    </div>

</template>

测试

搜索导航+分页查询

src\views\equipment\Equipment.vue

复制代码
<template>
    <div>
        <!-- 导航 -->
        <el-form :inline="true" class="demo-form-inline">
            <div style="float: left">
                <label style="margin-right: 5px">器材名称: </label>
                <el-input  v-model="name" placeholder="请输入器材名称" style="width: 40%" />
                <el-button type="primary" style="margin-left: 20px" >查询</el-button>
            </div>
            <div>
                <el-button type="primary" style="float: right" >+添加器材</el-button>
            </div>
        </el-form>

        <!-- 分页查询 -->
        <div>
            <el-table :data="records" stripe style="width: 100%">
                <el-table-column prop="name" label="器材名称" width="180">
                </el-table-column>
                <el-table-column prop="img" label="图片" width="180">
                </el-table-column>
                <el-table-column prop="number" label="数量" width="180">
                </el-table-column>
                <el-table-column prop="comment" label="描述" width="180">
                </el-table-column>
                <el-table-column prop="status" label="器材状态">
                    <template slot-scope="scope">{{ scope.row.status === 0 ? "禁用" : "启用" }}</template>
                </el-table-column>
                <el-table-column prop="updateTime" label="最后操作时间"></el-table-column>

                <el-table-column label="操作">
                    <template slot-scope="scope">
                        <el-button type="text" @click="handleUpdateStu(scope.row)">修改</el-button>
                        <el-button type="text" @click="handleStartOrStop(scope.row)">{{ scope.row.status === 0 ? "启用" :
            "禁用"
                            }}</el-button>
                    </template>
                </el-table-column>
            </el-table>
        </div>
    </div>
    

</template>


<script>

export default {
    data() {
        return {
            name: '',        //器材名称,对应上面的输入框
            page: 1,         //页码
            pageSize: 10,    // 每页记录数
            total: 0,         //总记录数
            records: []      //当前页要展示的数据集合
        }
    },    
}

</script>

src\views\equipment\Equipment.vue

复制代码
<template>
    <div>
        <el-form :inline="true" :model="formInline" class="demo-form-inline">
            <div style="float: left">
                <label style="margin-right: 5px">学生姓名: </label>
                <el-input v-model="name" placeholder="请输入学生姓名" style="width: 40%" />
                <el-button type="primary" style="margin-left: 20px" @click="pageQuery()">查询</el-button>
            </div>
            <div>
                <el-button type="primary" style="float: right" @click="handleAddStu">+添加学生</el-button>
            </div>
        </el-form>
        <br>
        <br>
        <br>
        <div>
            <el-table :data="records" stripe style="width: 100%">
                <el-table-column prop="name" label="学生姓名" width="180">
                </el-table-column>
                <el-table-column prop="username" label="账号" width="180">
                </el-table-column>
                <el-table-column prop="phone" label="手机号">
                </el-table-column>
                <el-table-column prop="status" label="账号状态">
                    <template slot-scope="scope">{{ scope.row.status === 0 ? "禁用" : "启用" }}</template>
                </el-table-column>
                <el-table-column prop="updateTime" label="最后操作时间">
                </el-table-column>
                <el-table-column label="操作">
                    <template slot-scope="scope">
                        <el-button type="text" @click="handleUpdateStu(scope.row)">修改</el-button>
                        <el-button type="text" @click="handleStartOrStop(scope.row)">{{ scope.row.status === 0 ? "启用" :
            "禁用"
                            }}</el-button>
                    </template>
                </el-table-column>
            </el-table>
        </div>
        <br>
        <div>
            <el-pagination class="pageList" @size-change="handleSizeChange" @current-change="handleCurrentChange"
                :current-page="page" :page-sizes="[10, 20, 30, 40]" :page-size="pageSize"
                layout="total, sizes, prev, pager, next, jumper" :total="total">
            </el-pagination>
        </div>
    </div>
</template>


<script>
// import request from '@/utils/request'
import { page, startOrStopStatus } from '@/api/Student'

export default {
    data() {
        return {
            name: '',        //学生姓名,对应上面的输入框
            page: 1,         //页码
            pageSize: 10,    // 每页记录数
            total: 0,         //总记录数
            records: []      //当前页要展示的数据集合
        }
    },
    created() {
        this.pageQuery()
    },
    methods: {
        pageQuery() {
            //准备参数
            const params = {
                page: this.page,
                pageSize: this.pageSize,
                name: this.name
            }

            /* request({
                url: "/api/admin/student/page",               // 请求地址
                method: "get",                      // 请求方法
                params: params,                       
                headers: {                            // 请求头
                    "Content-Type": "application/json",
                },
            }) */
            page(params)
                .then((res) => {
                    //解析结果
                    if (res.data.code === 1) {
                        this.total = res.data.data.total
                        this.records = res.data.data.records
                    }
                }).catch(err => {
                    this.$router.push("/login");
                })
        },
        //每页记录数发生变化时触发
        handleSizeChange(pageSize) {
            this.pageSize = pageSize
            this.pageQuery()
        },
        //page发生变化时触发
        handleCurrentChange(page) {
            this.page = page
            this.pageQuery()
        },

        //新增员工
        handleAddStu() {
            this.$router.push('/student/addStudent')
        },

        //启用禁用员工状态
        handleStartOrStop(row) {
            //判断账号是否是管理员账号,不能更改管理员账号
            if (row.username === 'admin') {
                this.$message.error("这是管理员账号,不允许更改!")
                return
            }

            this.$confirm('是否确认修改员工状态?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                const p = {
                    id: row.id,
                    status: !row.status ? 1 : 0
                }
                
                startOrStopStatus(p)
                .then(res =>{
                    if(res.data.code === 1){
                        this.$message.success("状态修改成功!")
                        this.pageQuery()
                    }
                })

            })
        },
        //修改编辑学生信息
        handleUpdateStu(row){
            if(row.username === 'admin'){
                this.$message.error("这是管理员账号,不允许修改!!")
                return
            }
            //跳转到修改页面,通过地址栏传递参数
            this.$router.push({ path: '/student/addStudent', query: {id: row.id}})
        }

    }
}
</script>

src\api\Equipment.js

复制代码
import request from '@/utils/request'

/* 分页查询 */
export const pageEquipment = (params) =>
    request({
        'url': '/api/admin/equipment/page',
        'method': 'get',
        params: params
    })

新增器材

src\router\index.js

src\views\equipment\Equipment.vue

src\views\equipment\addEquipment.vue

复制代码
<template>
    <div>hello</div>
</template>

测试

完善表单

请求

src\api\Equipment.js

复制代码
import request from '@/utils/request'

/* 分页查询 */
export const pageEquipment = (params) =>
    request({
        'url': '/api/admin/equipment/page',
        'method': 'get',
        params: params
    })



    /* 新增器材 */
export const addEquipment = (params) =>
    request({
        'url': '/api/admin/equipment',
        'method': 'post',
        data: params
    })

新增板块的界面

src\views\equipment\addEquipment.vue

复制代码
<template>
    <div class="form-container">
        <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
            <el-form-item label="器材名称:" required prop="name">
                <el-input v-model="ruleForm.name"></el-input>
            </el-form-item>

            <el-form-item label="数量:" required prop="number">
                <el-input v-model="ruleForm.number"></el-input>
            </el-form-item>

            <el-form-item label="描述" prop="comment">
                <el-input v-model="ruleForm.comment"></el-input>
            </el-form-item>

            <el-form-item label="器材图片:" prop="img">
                <div class="img-upload-container">
                    <!-- 监听 update:imageUrl 事件并更新 ruleForm.img -->
                    <img-upload @update:imageUrl="handleImageUrlUpdate" />
                    <!-- <img-upload :prop-image-url="ruleForm.img"></img-upload> -->
                    <span class="img-upload-instructions">图片大小不超过2M<br>仅能上传 PNG JPEG
                        JPG类型图片<br>建议上传200*200或300*300尺寸的图片</span>
                </div>
            </el-form-item>

            <el-form-item>
                <el-button type="primary" @click="submitForm('ruleForm')">保存</el-button>
                <el-button @click="$router.push('/equipment');">返回</el-button>
            </el-form-item>



        </el-form>

    </div>

</template>

<script>
import ImgUpload from '@/components/img-upload/img-upload.vue'
import { addEquipment } from '@/api/Equipment'

export default {

    components: {
        ImgUpload,
    },

    data() {

        return {
            // imageUrl: '',
            ruleForm: {
                name: '',
                img: '',
                number: '',
                comment: ''
            },
            rules: {
                name: [
                    { required: true, message: '请输入器材名称', trigger: 'blur' }],
                number: [
                    { required: true, message: '请输入器材数量', trigger: 'blur' }],
            },

        }
    },
    methods: {
        submitForm(formName) {
            this.$refs[formName].validate((valid) => {
                if (valid) {
                    alert(this.ruleForm.img)
                    if (!this.ruleForm.img)
                        return this.$message.error('套餐图片不能为空')
                    addEquipment(this.ruleForm)
                        .then((res) => {
                            if (res.data.code === 1) {
                                this.$message.success("添加成功!")
                                this.$router.push('/equipment')
                            } else {
                                this.$message.error("res.data.msg")
                            }
                        })
                } else {
                    console.log('error submit!!');
                    return false;
                }
            });
        },
        handleImageUrlUpdate(newImageUrl) {
            alert(newImageUrl)
            this.ruleForm.img = newImageUrl;
        }
    },
}

</script>

<style scoped>
.form-container {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 70vh;
    /* 或者你想要的任何高度 */
    width: 100%;
    max-width: 600px;
    /* 限制最大宽度以适应较小的屏幕 */
    margin: 0 auto;
    /* 水平居中 */
    padding: 20px;
    /* 内边距 */
    background-color: #ffffff;
    /* 背景颜色 */
    border-radius: 8px;
    /* 圆角 */
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    /* 阴影效果 */
}



/* 为提示文字设置样式 */
.img-upload-instructions {
    font-size: 12px;
    /* 根据需要调整字体大小 */
    color: #666;
    /* 根据需要调整颜色 */
    margin-bottom: 5px;
    /* 可选: 添加底部边距 */
}

/* 为整个上传组件设置样式 */
.img-upload-container {
    display: flex;
    /* 使用Flex布局 */
    align-items: center;
    /* 垂直居中 */
    gap: 10px;
    /* 间距 */
}
</style>

上传文件OSS的逻辑

src\components\img-upload\img-upload.vue

复制代码
<template>
  <div class="upload-item">
    <el-upload ref="uploadfiles" :accept="type" :class="{ borderNone: imageUrl }" class="avatar-uploader"
      action="/api/admin/common/upload" :show-file-list="false" :on-success="handleAvatarSuccess"
      :on-remove="handleRemove" :on-error="handleError" :before-upload="beforeAvatarUpload" :headers="headers">
      <img v-if="imageUrl" :src="imageUrl" class="avatar">

      <i v-else class="el-icon-plus avatar-uploader-icon" />
      <span v-if="imageUrl" class="el-upload-list__item-actions">
        <span class="el-upload-span" @click.stop="oploadImgDel">
          删除图片
        </span>
        <span class="el-upload-span"> 重新上传 </span>
      </span>
    </el-upload>
    <p class="upload-tips">
      <slot />
    </p>
  </div>
</template>

<script>
import { getToken } from '@/utils/cookies'

export default {
  name: 'UploadImage',
  props: {
    type: {
      type: String,
      default: '.jpg,.jpeg,.png'
    },
    size: {
      type: Number,
      default: 2
    },
    propImageUrl: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      headers: {
        token: getToken()
      },
      imageUrl: ''
    };
  },
  methods: {
    handleRemove() {
      // 方法实现
    },
    oploadImgDel() {
      this.imageUrl = '';
      this.$emit('imageChange', this.imageUrl);
    },
    beforeAvatarUpload(file) {
      const isLt2M = file.size / 1024 / 1024 < this.size;
      if (!isLt2M) {
        this.$message({
          message: `上传文件大小不能超过${this.size}M!`,
          type: 'error'
        });
        return false;
      }
    },
    handleError(err, file, fileList) {
      console.log(err, file, fileList, 'handleError');
      this.$message({
        message: '图片上传失败',
        type: 'error'
      });
    },
    handleAvatarSuccess(response) {
      this.imageUrl = `${response.data}`;
      // 发出一个事件,包含新的图片 URL  
      this.$emit('update:imageUrl', this.imageUrl);  
    }
  },
  watch: {
    propImageUrl: function (val) {
      this.imageUrl = val;
    }
  }
};
</script>


<style lang='scss'>
.borderNone {
  .el-upload {
    border: 1px solid #d9d9d9 !important;
  }
}
</style>
<style scoped lang="scss">
.avatar-uploader .el-icon-plus:after {
  position: absolute;
  display: inline-block;
  content: ' ' !important;
  left: calc(50% - 20px);
  top: calc(50% - 40px);
  width: 40px;
  height: 40px;
  // background: url('./../../assets/icons/icon_upload@2x.png') center center no-repeat;
  background-size: 20px;
}

.el-upload-list__item-actions:hover .upload-icon {
  display: inline-block;
}

.el-icon-zoom-in:before {
  content: '\E626';
}

.el-icon-delete:before {
  content: '\E612';
}

.el-upload-list__item-actions:hover {
  opacity: 1;
}

.upload-item {


  .el-form-item__content {
    width: 500px !important;
  }

  display: flex;
  align-items: center;
  border: 1px solid #ccc;
  /* 添加边框*/
  width: 200px;
  /* 设置宽度 */
  height: 200px;
  /* 设置高度,使之与宽度相同 */
}

.upload-tips {
  font-size: 12px;
  color: #666666;
  display: inline-block;
  line-height: 17px;
  margin-left: 36px;
}

.el-upload-list__item-actions {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  cursor: default;
  text-align: center;
  color: #fff;
  opacity: 0;
  font-size: 20px;
  background-color: rgba(0, 0, 0, 0.5);
  transition: opacity 0.3s;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}

.avatar-uploader {
  display: inline-block;
}

.avatar-uploader .el-upload:hover {
  border-color: #ffc200;
}

.el-upload-span {
  width: 100px;
  height: 30px;
  border: 1px solid #ffffff;
  border-radius: 4px;
  font-size: 14px;
  text-align: center;
  line-height: 30px;
}

.el-upload-span:first-child {
  margin-bottom: 20px;
}

.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 200px;
  height: 160px;
  line-height: 160px;
  text-align: center;
}

.avatar {
  width: 200px;
  height: 160px;
  display: block;
}
</style>

上传oss图片文件时需要的jwt令牌获取

src\utils\cookies.js

复制代码
import Cookies from 'js-cookie';


// 获取令牌
export const getToken = () => sessionStorage.getItem('jwtToken');

ps:如果出现模块找不到,不存在的时候,直接

复制代码
npm install 模块

例如:

Module not found: Error: Can't resolve 'js-cookie' in 'D:\bishe\project\sems-front\src\utils'

解决方法:

这个错误表明你的项目无法找到js-cookie模块,这意味着你可能还没有安装它或者路径配置有问题。js-cookie是一个用于操作浏览器Cookies的小型JavaScript库。

解决方案

安装 js-cookie

确保你已经安装了js-cookie。你可以通过运行以下命令来安装它:

复制代码
npm install js-cookie --save

或者如果你使用的是Yarn:

复制代码
yarn add js-cookie

测试:

ps:OSS折磨死我了,踩了无数的坑,全靠各种搜索资料,卡了我2天,呜呜呜,麻了,有什么不知道的真可以问我,呜呜呜,你们踩的坑我应该都踩过,麻了

未完。。。

相关推荐
崔庆才丨静觅12 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606112 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了13 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅13 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅13 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅13 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment13 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅14 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊14 小时前
jwt介绍
前端
爱敲代码的小鱼14 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax