轮播图添加删除

文章展示了如何在Vue前端和Flask后端实现轮播图的上传、验证、展示和管理功能。前端包括轮播图的添加、编辑和删除界面,使用ElementUI组件库;后端涉及上传图片的验证、存储和返回URL,以及轮播图的CRUD操作。同时,文章还涵盖了跨域问题的解决和前端数据回显。
摘要由CSDN通过智能技术生成

轮播图页面和对话框搭建

页面简单布局

<template>
  <div id="banner">
    <el-space direction="vertical" :size="20" style="width: 100%">
      <h1>轮播图管理</h1>
      <div style="text-align: right">
        <el-button type="primary" @click="onAddButtonClick">
          <el-icon><plus /></el-icon>
          添加轮播图
        </el-button>
      </div>
    </el-space>
    <el-dialog v-model="bannerDialogVisible" title="添加修改轮播图" width="30%">
      <el-form :model="form">
        <el-form-item label="名称" >
          <el-input v-model="form.name" autocomplete="off" />
        </el-form-item>
        <el-form-item label="图片" >
          <div style="display: flex">
            <el-input v-model="form.image_url" autocomplete="off" style="margin-right:10px"/>
            <!-- 上传的地址 -->
            <el-upload
              :show-file-list="false"
              :action="$http.server_host + '/cmsapi/banner/image/upload'"
              :headers="{'Authorization': 'Bearer '+$auth.token}"
              name="image"
              accept="image/jpeg, image/png"
              :on-success="onImageUploadSuccess"
              :on-error="onImageUploadError"
            >
              <el-button type="primary" size="small">上传图片</el-button>
            </el-upload>
          </div>
        </el-form-item>
        <el-form-item label="跳转" >
          <el-input v-model="form.link_url" autocomplete="off" />
        </el-form-item>
        <el-form-item label="优先级" >
          <el-input v-model="form.priority" autocomplete="off" type="number" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="bannerDialogVisible = false">取消</el-button>
          <el-button type="primary"> Confirm </el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script>
import { Plus } from "@element-plus/icons-vue";
export default {
  name: "Banner",
  components: {
    Plus,
  },
  data() {
    return {
      bannerDialogVisible: false,
      form: {
        name: "",
        image_url: "",
        link_url: "",
        priority: "",
      },
    };
  },
  mounted() {},
  methods: {
    onAddButtonClick() {
      this.bannerDialogVisible = true;
    },
    onImageUploadSuccess(response){
      if(response['code'] == 200){
        var image_name = response['data']['image_url'];
        var image_url = "/media/banner/" + image_name
        this.form.image_url = image_url;
      }
    },
    onImageUploadError(err, file, fileList){
      console.log(err);
      console.log(file);
      console.log(fileList);
    },
  },
};
</script>

<style scoped>
.el-space {
  display: block;
}
</style>

在这里插入图片描述

上传轮播图

新建目录存储轮播图
在这里插入图片描述
配置路径
在这里插入图片描述
配置图片前缀
在这里插入图片描述

上传文件校验
在这里插入图片描述

from flask_wtf.file import FileAllowed, FileSize
from wtforms import FileField

from apps.front.forms import BaseForm


class UploadImageForm(BaseForm):
    image = FileField(validators=[FileAllowed(['jpg', 'jpeg', 'png'], message="图片格式不符合要求!"), FileSize(max_size=1024*1024*5, message="图片最大不能超过5M!")])

上传接口
在这里插入图片描述

'''
-*- coding: utf-8 -*-
@File  : views.py
@author: Lx
@Time  : 2023/07/17 15:08
'''
import time
from hashlib import md5

from flask import Blueprint, request, current_app, g

from apps.cmsapi.forms import UploadImageForm
from utils import restful
from flask_jwt_extended import jwt_required,get_jwt_identity
import os
bp = Blueprint("cmsapi", __name__, url_prefix="/cmsapi")

@bp.get('/')
@jwt_required()
def mytest():
    # 这个identity是通过create_access_token传入的identity 获取的是User.id
    identity = get_jwt_identity()
    return restful.ok(message="成功!")


@bp.post("/banner/image/upload")
def upload_banner_image():
    form = UploadImageForm(request.files)
    if form.validate():
        image = form.image.data
        # 不要使用用户上传上来的文件名,否则容易被黑客攻击
        filename = image.filename
        # xxx.png,xx.jpeg
        _, ext = os.path.splitext(filename)
        filename = md5((g.user.email + str(time.time())).encode("utf-8")).hexdigest() + ext
        image_path = os.path.join(current_app.config['BANNER_IMAGE_SAVE_PATH'], filename)
        image.save(image_path)
        return restful.ok(data={"image_url": filename})
    else:
        message = form.messages[0]
        return restful.params_error(message=message)

此时会报跨域错误
在这里插入图片描述

pip install flask-cors

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

此时上传文件会出现问题因为g.user 没有这个属性
在这里插入图片描述

# 调用cmsapi之前先获取jwt  通过之后绑定user属性
@bp.before_request
@jwt_required()
def cmsapi_before_request():
    if request.method == 'OPTIONS':
        return
    identity = get_jwt_identity()
    user = UserModel.query.filter_by(id=identity).first()
    if user:
        setattr(g, "user", user)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

	#做个判断
    if request.method == 'OPTIONS':
        return

在这里插入图片描述

onImageUploadSuccess(response){
      if(response['code'] == 200){
        var image_name = response['data']['image_url'];
        var image_url = "/media/banner/" + image_name
        this.form.image_url = image_url;
      }
    },
    onImageUploadError(err, file, fileList){
      console.log(err);
      console.log(file);
      console.log(fileList);
    },

添加轮播图
前端实现
在这里插入图片描述

轮播图添加
在这里插入图片描述

onDialogSubmitEvent(){
      this.$refs["dialogForm"].validate((valid) => {
        if(!valid){
          ElMessage.error("请确保数据输入正确!");
          return;
        }
          // 走添加操作
          this.$http.addBanner(this.form).then((result) => {
            let code = result['code'];
            if(code === 200){
              let banner = result['data'];
              // 返回的轮播图添加到数组中
              this.banners.push(banner);
              ElMessage.success("轮播图添加成功!");
              this.bannerDialogVisible = false;
            }
          }).catch(() => {
            ElMessage.error("服务器开小差了,请稍后再试!");
            this.bannerDialogVisible = false
          })
        
      })
    },

在这里插入图片描述

轮播图列表展示
后端

@bp.get("/banner/list")
def banner_list():
    banners = BannerModel.query.order_by(BannerModel.create_time.desc()).all()
    # BannerModel
    banner_dicts = [banner.to_dict() for banner in banners]
    return restful.ok(data=banner_dicts)

轮播图列表前端实现
前端
http.js

getBannerList(){
		const url = "/banner/list"
		return this.http.get(url);
	}

在这里插入图片描述

mounted() {
    this.$http.getBannerList().then(res => {
      if(res['code'] == 200){
        let banners = res['data'];
        this.banners = banners;
      }else{
        ElMessage.error(res['message']);
      }
    })
  },
<el-table :data="banners" style="width: 100%">
        <el-table-column prop="name" label="名称" width="250px" />
        <el-table-column label="图片">
          <template #default="scope">
            <img :src="formatImageUrl(scope.row.image_url)" style="width: 200px;height: 60px;" />
          </template>
        </el-table-column>
        <el-table-column label="跳转链接">
          <template #default="scope">
            <a :href="scope.row.link_url" target="_blank">{{scope.row.link_url}}</a>
          </template>
        </el-table-column>
        <el-table-column prop="priority" label="优先级" width="100px" />
        <el-table-column>
          <template #default="scope">
            <el-button type="primary" circle @click="onEditEvent(scope.$index)">
              <el-icon><edit /></el-icon>
            </el-button>
            <el-button type="danger" circle @click="onDeleteEvent(scope.$index)">
              <el-icon><delete /></el-icon>
            </el-button>
          </template>
        </el-table-column>
      </el-table>

在这里插入图片描述

添加轮播的时候上传本地需要拼接完整Url
如果是在输入框输入完整url则不需要拼接


<el-table-column label="图片">
          <template #default="scope">
            <img :src="formatImageUrl(scope.row.image_url)" style="width: 200px;height: 60px;" />
          </template>
        </el-table-column>


formatImageUrl(image_url){
      if(image_url.startsWith("http")){
        return image_url;
      }else{
        return this.$http.server_host + image_url;
      }
    },

在这里插入图片描述

删除轮播图前后端实现

deleteBanner(banner_id){
		const url = "/banner/delete"
		return this._post(url, {"id": banner_id})
	}
 onDeleteEvent(index){
      this.deleteingIndex = index;
      this.deleteDialogVisible = true;
    },
    onConfirmDeleteEvent(){
      let banner = this.banners[this.deleteingIndex];
      this.$http.deleteBanner(banner.id).then(res => {
        let result = res['data'];
        let code = result['code'];
        if(code === 200){
          this.deleteDialogVisible = false;
          this.banners.splice(this.deleteingIndex, 1);
          ElMessage.success("轮播图删除成功!");
        }
      })
    }
@bp.post("/banner/delete")
def delete_banner():
    banner_id = request.form.get("id")
    if not banner_id:
        return restful.params_error(message="没有传入id!")
    try:
        banner_model = BannerModel.query.get(banner_id)
    except Exception as e:
        return restful.params_error(message="此轮播图不存在!")
    db.session.delete(banner_model)
    db.session.commit()
    return restful.ok()

编辑轮播图

forms

class EditBannerForm(AddBannerForm):
    id = IntegerField(validators=[InputRequired(message="请输入轮播图的id!")])
@bp.post("/banner/edit")
def edit_banner():
    form = EditBannerForm(request.form)
    if form.validate():
        banner_id = form.id.data
        try:
            banner_model = BannerModel.query.get(banner_id)
        except Exception as e:
            return restful.params_error(message="轮播图不存在!")
        name = form.name.data
        image_url = form.image_url.data
        link_url = form.link_url.data
        priority = form.priority.data

        banner_model.name = name
        banner_model.image_url = image_url
        banner_model.link_url = link_url
        banner_model.priority = priority
        db.session.commit()
        return restful.ok(data=banner_model.to_dict())
    else:
        return restful.params_error(message=form.messages[0])

前端页面点击编辑按钮,数据回显
在这里插入图片描述

在这里插入图片描述

// 如果有值说明是编辑轮播图
    initForm(banner){
      if(banner){
        // 这里加轮播图id 验证是添加轮播图还是编辑
        this.form.id = banner.id;
        this.form.name = banner.name;
        this.form.image_url = banner.image_url;
        this.form.link_url = banner.link_url;
        this.form.priority = banner.priority;
      }else{
        // 如果没有则说明是添加轮播图,则把表单清空
        this.form = {
          name: "",
          image_url: "",
          link_url: "",
          priority: 0
        }
      }
    },
    onEditEvent(index){
      this.editingIndex = index;
      let banner = this.banners[index];
      this.initForm(banner);
      this.bannerDialogVisible = true;
    },

添加轮播图做修改

onDialogSubmitEvent(){
      this.$refs["dialogForm"].validate((valid) => {
        if(!valid){
          ElMessage.error("请确保数据输入正确!");
          return;
        }
        if(this.form.id){
          // 走编辑操作
          this.$http.editBanner(this.form).then((result) => {
            let code = result['code'];
            if(code === 200){
              let banner = result['data'];
              // this.banners.push(banner);
              // 需要使用splice才能完成替换操作,不能直接通过下标修改元素
              // 直接通过下标修改元素,页面上不会自动改变
              this.banners.splice(this.editingIndex, 1, banner);
              ElMessage.success("轮播图编辑成功!");
              this.bannerDialogVisible = false;
            }
          })
        }else{
          // 走添加操作
          this.$http.addBanner(this.form).then((result) => {
            let code = result['code'];
            if(code === 200){
              let banner = result['data'];
              this.banners.push(banner);
              ElMessage.success("轮播图添加成功!");
              this.bannerDialogVisible = false;
            }
          }).catch(() => {
            ElMessage.error("服务器开小差了,请稍后再试!");
            this.bannerDialogVisible = false
          })
        }
      })
    },

http.js

editBanner(data){
		const url = "/banner/edit"
		return this._post(url, data);
	}

前台加载真实轮播图数据

@bp.route('/')
def index():
    sort = request.args.get("st", type=int, default=1)
    board_id = request.args.get("bd", type=int, default=None)
    boards = BoardModel.query.order_by(BoardModel.priority.desc()).all()
    post_query = None
    if sort == 1:
        post_query = PostModel.query.order_by(PostModel.create_time.desc())
    else:
        # 根据评论数量进行排序
        post_query = db.session.query(PostModel).outerjoin(CommentModel).group_by(PostModel.id).order_by(func.count(CommentModel.id).desc(), PostModel.create_time.desc())

    page = request.args.get(get_page_parameter(), type=int, default=1)
    # 10-9
    # 210-19
    start = (page - 1) * current_app.config['PER_PAGE_COUNT']
    end = start + current_app.config['PER_PAGE_COUNT']

    if board_id:
        # "mapped class CommentModel->comment" has no property "board_id"
        # CommentModel中寻找board_id,然后进行过滤
        # post_query = post_query.filter_by(board_id=board_id)
        post_query = post_query.filter(PostModel.board_id==board_id)
    total = post_query.count()
    posts = post_query.slice(start, end)
    pagination = Pagination(bs_version=3, page=page, total=total, prev_label="上一页")

    banners = BannerModel.query.order_by(BannerModel.priority.desc()).all()
    context = {
        "boards": boards,
        "posts": posts,
        "pagination": pagination,
        "st": sort,
        "bd": board_id,
        "banners": banners
    }
    return render_template("front/index.html", **context)

在这里插入图片描述

前端轮播图展示功能
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

<template>
  <div id="banner">
    <el-space direction="vertical" :size="20" style="width: 100%">
      <h1>轮播图管理</h1>
      <div style="text-align: right">
        <el-button type="primary" @click="onAddButtonClick">
          <el-icon><plus /></el-icon>
          添加轮播图
        </el-button>
      </div>
      <el-table :data="banners" style="width: 100%">
        <el-table-column prop="name" label="名称" width="250px" />
        <el-table-column label="图片">
          <template #default="scope">
            <img :src="formatImageUrl(scope.row.image_url)" style="width: 200px;height: 60px;" />
          </template>
        </el-table-column>
        <el-table-column label="跳转链接">
          <template #default="scope">
            <a :href="scope.row.link_url" target="_blank">{{scope.row.link_url}}</a>
          </template>
        </el-table-column>
        <el-table-column prop="priority" label="优先级" width="100px" />
        <el-table-column>
          <template #default="scope">
            <el-button type="primary" circle @click="onEditEvent(scope.$index)">
              <el-icon><edit /></el-icon>
            </el-button>
            <el-button type="danger" circle @click="onDeleteEvent(scope.$index)">
              <el-icon><delete /></el-icon>
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-space>

    <el-dialog v-model="bannerDialogVisible" title="添加/修改轮播图" width="30%">
      <el-form :model="form" :rules="rules" ref="dialogForm">
        <el-form-item label="名称" prop="name">
          <el-input v-model="form.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="图片" prop="image_url">
          <div style="display: flex;">
            <el-input v-model="form.image_url" autocomplete="off" style="margin-right:10px;"></el-input>
            <el-upload
              :action="$http.server_host+'/cmsapi/banner/image/upload'"
              name="image"
              :headers="{'Authorization': 'Bearer '+$auth.token}"
              :show-file-list="false"
              accept="image/jpeg, image/png"
              :on-success="onImageUploadSuccess"
              :on-error="onImageUploadError"
            >
              <el-button size="small" type="primary">上传图片</el-button>
            </el-upload>
          </div>
        </el-form-item>
        <el-form-item label="跳转" prop="link_url">
          <el-input v-model="form.link_url" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="优先级" prop="priority">
          <el-input v-model="form.priority" autocomplete="off" type="number"></el-input>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="bannerDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="onDialogSubmitEvent">确认</el-button>
        </span>
      </template>
    </el-dialog>

    <!-- 删除轮播图确认对话框 -->
    <el-dialog
      v-model="deleteDialogVisible"
      title="提示"
      width="30%"
    >
      <span>您确定要删除这个轮播图吗?</span>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="deleteDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="onConfirmDeleteEvent">确定</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script>
import { Plus, Edit, Delete } from "@element-plus/icons";
import {ElMessage} from "element-plus";
export default {
  name: "Banner",
  components: {
    Plus,
    Edit,
    Delete
  },
  data(){
    return {
      bannerDialogVisible: false,
      deleteDialogVisible: false,
      deleteingIndex: 0,
      editingIndex: 0,
      banners: [],
      form: {
        name: "",
        image_url: "",
        link_url: "",
        priority: 0
      },
      rules: {
        name: [{required: true,message: '请输入轮播图名称!',trigger: 'blur'}],
        image_url: [{required: true,message: '请上传轮播图!',trigger: 'blur'}],
        link_url: [{required: true,message: '请输入轮播图跳转链接!',trigger: 'blur'}],
        priority: [
          {required: true,message: '请输入轮播图优先级!',trigger: 'blur'}
        ],
      }
    }
  },
  mounted() {
    this.$http.getBannerList().then(res => {
      if(res['code'] == 200){
        let banners = res['data'];
        this.banners = banners;
      }else{
        ElMessage.error(res['message']);
      }
    })
  },
  methods: {
    formatImageUrl(image_url){
      if(image_url.startsWith("http")){
        return image_url;
      }else{
        return this.$http.server_host + image_url;
      }
    },
    initForm(banner){
      if(banner){
        this.form.id = banner.id;
        this.form.name = banner.name;
        this.form.image_url = banner.image_url;
        this.form.link_url = banner.link_url;
        this.form.priority = banner.priority;
      }else{
        this.form = {
          name: "",
          image_url: "",
          link_url: "",
          priority: 0
        }
      }
    },
    onAddButtonClick(){
      this.initForm();
      this.bannerDialogVisible = true;
    },
    onImageUploadSuccess(response){
      if(response['code'] == 200){
        var image_name = response['data']['image_url'];
        var image_url = "/media/banner/" + image_name
        this.form.image_url = image_url;
      }
    },
    onImageUploadError(err, file, fileList){
      console.log(err);
      console.log(file);
      console.log(fileList);
    },
    onDialogSubmitEvent(){
      this.$refs["dialogForm"].validate((valid) => {
        if(!valid){
          ElMessage.error("请确保数据输入正确!");
          return;
        }
        if(this.form.id){
          // 走编辑操作
          this.$http.editBanner(this.form).then((result) => {
            let code = result['code'];
            if(code === 200){
              let banner = result['data'];
              // this.banners.push(banner);
              // 需要使用splice才能完成替换操作,不能直接通过下标修改元素
              // 直接通过下标修改元素,页面上不会自动改变
              this.banners.splice(this.editingIndex, 1, banner);
              ElMessage.success("轮播图编辑成功!");
              this.bannerDialogVisible = false;
            }
          })
        }else{
          // 走添加操作
          this.$http.addBanner(this.form).then((result) => {
            let code = result['code'];
            if(code === 200){
              let banner = result['data'];
              this.banners.push(banner);
              ElMessage.success("轮播图添加成功!");
              this.bannerDialogVisible = false;
            }
          }).catch(() => {
            ElMessage.error("服务器开小差了,请稍后再试!");
            this.bannerDialogVisible = false
          })
        }
      })
    },
    // 编辑轮播图
    onEditEvent(index){
      this.editingIndex = index;
      let banner = this.banners[index];
      this.initForm(banner);
      this.bannerDialogVisible = true;
    },
    // 每次点击添加轮播图就初始化表单
    onAddButtonClick(){
      this.initForm();
      this.bannerDialogVisible = true;
    },


    onDeleteEvent(index){
      this.deleteingIndex = index;
      this.deleteDialogVisible = true;
    },
    onConfirmDeleteEvent(){
      let banner = this.banners[this.deleteingIndex];
      this.$http.deleteBanner(banner.id).then(res => {
        let result = res['data'];
        let code = result['code'];
        if(code === 200){
          this.deleteDialogVisible = false;
          this.banners.splice(this.deleteingIndex, 1);
          ElMessage.success("轮播图删除成功!");
        }
      })
    }
  }
};
</script>

<style scoped>
.el-space {
  display: block;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

季布,

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值