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

发布于:2024-08-03 ⋅ 阅读:(162) ⋅ 点赞:(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天,呜呜呜,麻了,有什么不知道的真可以问我,呜呜呜,你们踩的坑我应该都踩过,麻了

禁用启用器材

因为vscode不好用,我将前端代码转移到了idea中去

问题:ESLint: Component name "Equipment" should always be multi-word.(vue/multi-word-component-names

解决:

package.json

{
  "rules": {
    "vue/multi-word-component-names": "off"
  }
}

请求

src/api/Equipment.js

/* 禁用启用器材状态 */
export const startOrStopStatus = (params) =>
    request({
        'url': `/api/admin/equipment/status/${params.status}`,
        'method': 'post',
        params: {id: params.id}
    })

绑定方法

src/views/equipment/Equipment.vue

<template>
    <div>
        <!-- 导航 -->
        <el-form :inline="true">
            <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="handleAddEquipment">+添加器材</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="img" label="图片" width="180">
                    <template #default="scope">
                        <div class="cell-content">
                            <img :src="scope.row.img" alt="图片" style="max-width: 100%; width: 100%;">
                        </div>
                    </template>
                </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>

        <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 { pageEquipment } from '@/api/Equipment'
import { startOrStopStatus } from '@/api/Equipment'

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
            }
            pageEquipment(params)
                .then(res => {
                    if (res.data.code === 1) {
                        this.total = res.data.data.total
                        this.records = res.data.data.records
                    }
                }).catch(() => {
                    this.$router.push("/login")
                })
        },
        //每页记录数发生变化时触发
        handleSizeChange(pageSize) {
            this.pageSize = pageSize
            this.pageQuery()
        },
        //page发生变化时触发
        handleCurrentChange(page) {
            this.page = page
            this.pageQuery()
        },
        //新增器材
        handleAddEquipment() {
            this.$router.push('/equipment/addEquipment')
        },

        //修改器材状态
        handleStartOrStop(row){

            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()
                    }
                }).catch(err =>{
                    this.$message.error("请求出错了~");
                    alert(err);
                })
            })
        }
    }
}

</script>

<style scoped>
/* 使表格单元格内的内容垂直居中 */
.el-table__body td {
  display: flex;
  align-items: center; /* 垂直居中对齐 */
  justify-content: center; /* 水平居中对齐 */
}

/* 使图片容器在单元格内居中 */
.cell-content {
  display: flex;
  align-items: center; /* 垂直居中对齐 */
  justify-content: center; /* 水平居中对齐 */
  height: 100%; /* 占满单元格高度 */
}

/* 使图片在容器内居中 */
.cell-content img {
  max-width: 100%; /* 确保图片宽度不超过容器宽度 */
  width: auto; /* 自动调整宽度 */
  height: auto; /* 自动调整高度 */
}
</style>

bug修复

我只写了点击确定后的逻辑,如果点击取消就会报错,所以改一下这个bug

vue.config.js

添加这个代码,作用是:禁用 overlay 功能

client: {
            overlay: false,
        },

修改器材

请求

src/api/Equipment.js

/* 根据器材id查询信息 */
export const queryEquipmentById = (id) =>
    request({
        'url': `/api/admin/equipment/${id}`,
        'method': 'get'
    })

/* 编辑器材 */
export const updateEquipment = (params) =>
    request({
        'url': '/api/admin/equipment',
        'method': 'put',
        data: params
    })

回显

src/views/equipment/addEquipment.vue

  created() {
    //获取路由参数,如果有就是修改,没有就是新增操作
    this.optType = this.$route.query.id ? 'update' : 'add'
    if (this.optType === 'update') {
      //修改操作,根据id查询原始数据,用于回显
      queryEquipmentById(this.$route.query.id)
          .then((res) => {
            if (res.data.code === 1) {
              this.ruleForm = res.data.data
            }
          })
    }
  },

问题来了,图片不回显了~~~

啊啊啊啊啊

过了30分钟.........

解决了!!

src/views/equipment/addEquipment.vue

代码开发

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">

          <!-- 监听 imageChange 事件并更新 ruleForm.img -->
          <img-upload :propImageUrl="ruleForm.img" @imageChange="handleImageUrlUpdate"/>
          <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, queryEquipmentById, updateEquipment} from '@/api/Equipment'

export default {

  components: {
    ImgUpload,
  },

  data() {

    return {
      // imageUrl: '',
      optType: '',//当前操作的类型:add还是update
      ruleForm: {
        name: '',
        img: '',
        number: '',
        comment: ''
      },
      rules: {
        name: [
          {required: true, message: '请输入器材名称', trigger: 'blur'}],
        number: [
          {required: true, message: '请输入器材数量', trigger: 'blur'}],
      },

    }
  },
  created() {
    //获取路由参数,如果有就是修改,没有就是新增操作
    this.optType = this.$route.query.id ? 'update' : 'add'
    if (this.optType === 'update') {
      //修改操作,根据id查询原始数据,用于回显
      queryEquipmentById(this.$route.query.id)
          .then((res) => {
            if (res.data.code === 1) {
              this.ruleForm = res.data.data
            }
          })
    }
  },
  methods: {
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          if(this.optType === 'add'){
          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 {
            updateEquipment(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) {
      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>

测试

界面美化

src/views/equipment/Equipment.vue

<template>
  <div>
    <!-- 导航 -->
    <el-form :inline="true">
      <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="handleAddEquipment">+添加器材</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" align="center">
        </el-table-column>
        <el-table-column prop="img" label="图片" width="180" align="center">
          <template #default="scope">
            <div class="cell-content">
              <img :src="scope.row.img" alt="图片" style="max-width: 100%; width: 100%;">
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="number" label="数量" width="180" align="center">
        </el-table-column>
        <el-table-column prop="comment" label="描述" width="180" align="center">
        </el-table-column>
        <el-table-column prop="status" label="器材状态" align="center">
          <template slot-scope="scope">{{ scope.row.status === 0 ? "禁用" : "启用" }}</template>
        </el-table-column>
        <el-table-column prop="updateTime" label="最后操作时间" align="center"></el-table-column>

        <el-table-column label="操作" align="center">
          <template slot-scope="scope">
            <el-button type="text" @click="handleUpdateEquipment(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 {pageEquipment} from '@/api/Equipment'
import {startOrStopStatus} from '@/api/Equipment'

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
      }
      pageEquipment(params)
          .then(res => {
            if (res.data.code === 1) {
              this.total = res.data.data.total
              this.records = res.data.data.records
            }
          }).catch(() => {
        this.$router.push("/login")
      })
    },
    //每页记录数发生变化时触发
    handleSizeChange(pageSize) {
      this.pageSize = pageSize
      this.pageQuery()
    },
    //page发生变化时触发
    handleCurrentChange(page) {
      this.page = page
      this.pageQuery()
    },
    //新增器材
    handleAddEquipment() {
      this.$router.push('/equipment/addEquipment')
    },

    //修改器材状态
    handleStartOrStop(row) {

      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()
              }
            }).catch(err => {
          this.$message.error("请求出错了~");
          alert(err);
        })
      })
    },

    //修改器材
    handleUpdateEquipment(row){
      //跳转到修改页面,通过地址栏传递参数
      this.$router.push({ path: '/equipment/addEquipment', query: {id: row.id}})
    }
  }
}

</script>

<style scoped>
/* 使表格单元格内的内容垂直居中 */
.el-table__body td {
  display: flex;
  align-items: center; /* 垂直居中对齐 */
  justify-content: center; /* 水平居中对齐 */
}

/* 使图片容器在单元格内居中 */
.cell-content {
  display: flex;
  align-items: center; /* 垂直居中对齐 */
  justify-content: center; /* 水平居中对齐 */
  height: 100%; /* 占满单元格高度 */
}

/* 使图片在容器内居中 */
.cell-content img {
  max-width: 100%; /* 确保图片宽度不超过容器宽度 */
  width: auto; /* 自动调整宽度 */
  height: auto; /* 自动调整高度 */
}
</style>

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" alt="Preview">

      <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" @click.stop="reuploadImage"> 重新上传 </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: ''// 将 prop 的值赋给本地数据属性
    };
  },
  methods: {
    handleRemove() {
      // 方法实现
      this.imageUrl = '';
      this.$emit('imageChange', this.imageUrl);
    },
    oploadImgDel() {
      this.handleRemove();
    },
    reuploadImage() {
      this.handleRemove();
    },
    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('imageChange', this.imageUrl);
    }
  },
  watch: {
    propImageUrl(newVal) {
      this.imageUrl = newVal;
    }
  }
};
</script>



<style scoped lang="scss">
.borderNone {
  & {
    .el-upload {
      border: 1px solid #d9d9d9 !important;
    }
  }
}
.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 {
  position: relative;
  width: 200px; /* 或者你想要的任何宽度 */
  height: 200px; /* 或者你想要的任何高度 */
  overflow: hidden;
  border: 1px dashed #d9d9d9; /* 边框样式可自定义 */
}

.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: 100%;
  height: 100%;
  object-fit: cover;
}
</style>

src/views/student/Student.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" @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" align="center">
                </el-table-column>
                <el-table-column prop="username" label="账号" width="180" align="center">
                </el-table-column>
                <el-table-column prop="phone" label="手机号" align="center">
                </el-table-column>
                <el-table-column prop="status" label="账号状态" align="center">
                    <template slot-scope="scope">{{ scope.row.status === 0 ? "禁用" : "启用" }}</template>
                </el-table-column>
                <el-table-column prop="updateTime" label="最后操作时间" align="center">
                </el-table-column>
                <el-table-column label="操作" align="center">
                    <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(()=> {
                    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>

效果展示

处理一个bug

当我从equipment/addEquipment路径跳转到student路径时会自动跳转到404去

问题产生原因:

如果你没有为 <router-link>to 属性提供完整的路径(即以 / 开头的路径),那么 Vue Router 会将其视为相对于当前路由的路径。如果你的 <router-link> 位于某个嵌套路由下(例如 /equipment/addEquipment),那么 to="student" 实际上会被解析为 /equipment/student,而不是你期望的 /student

解决措施

src/views/home/Home.vue

在前面加上了一个”/“


网站公告

今日签到

点亮在社区的每一天
去签到