用户反馈组件实现(Vue3+ElementPlus)含图片拖拽上传

用户反馈组件实现(Vue3+ElementPlus)含图片拖拽上传

  • 1. 页面效果
    • 1.1 正常展示
    • 1.2 鼠标悬浮
    • 1.3 表单
  • 2. 代码部分
    • 1.2 html、ts
    • 1.2 less部分
  • 3. 编码过程遇到的问题

1. 页面效果

1.1 正常展示

在这里插入图片描述

1.2 鼠标悬浮

在这里插入图片描述

1.3 表单

在这里插入图片描述

2. 代码部分

1.2 html、ts

<template><Teleport><divclass="feedback"@mouseenter="() => (showText = true)"@mouseleave="() => (showText = false)"><el-popover :visible="visible" trigger="manual" placement="left" :width="510"><div class="feedback-content" @dragover="handleDragOver" @drop="handleDrop"><header class="flex"><strong>反馈中心</strong><el-link type="primary" @click="toJiraPage"> <strong>我的反馈</strong> </el-link></header><hr style="margin: 10px 0 0 -13px; border-top: 1px solid #dbdbdb" /><section><p style="margin-top: 10px; letter-spacing: 1px"><strong>尊敬的用户:</strong></p><p style="letter-spacing: 1px; text-indent: 4ch">感谢您提供诚挚的建议,我们将尽快帮您处理解决。</p><el-formref="refForm":model="fromData":rules="fromRules"label-position="top"size="large"style="margin-top: 20px"class="from-content"><el-form-itemlabel="问题类型"prop="issueType":rules="{ required: true, message: '请选择问题类型', trigger: ['blur', 'change'] }"><div class="card-list"><divv-for="t in feedbackType":key="t.name":class="['card-item', { active: fromData.issueType === t.id }]"@click="fromData.issueType = t.id">{{ t.name }}</div></div></el-form-item><el-form-item label="概述" prop="summary"><el-input v-model="fromData.summary"></el-input></el-form-item><el-form-item label="问题描述" prop="description"><el-input v-model="fromData.description" type="textarea" :rows="4"></el-input></el-form-item><el-uploadaction="none"list-type="picture-card":auto-upload="false":before-upload="beforeAvatarUpload":on-exceed="handleExceed":file-list="fromData.imgs":on-preview="handlePictureCardPreview"><el-icon><Plus /></el-icon><template #file="{ file }"><div><img class="el-upload-list__item-thumbnail" :src="file.url" alt="" /><span class="el-upload-list__item-actions"><spanclass="el-upload-list__item-preview"@click="handlePictureCardPreview(file)"><el-icon><zoom-in /></el-icon></span><spanv-if="!disabled"class="el-upload-list__item-delete"@click="handleRemove(file)"><el-icon><Delete /></el-icon></span></span></div></template></el-upload><div class="btn-row"><el-button class="btn-row-left" type="default" size="small" round @click="close">取 消</el-button><el-buttonclass="btn-row-right"size="small"type="primary"round:disabled="loading"@click="handleSubmit(refForm)">提 交</el-button></div></el-form></section><div class="dot"></div></div><template #reference><div v-if="visible" class="line"></div><div v-else class="slot-content" @click="visible = true"><ChatLineSquare class="feedback-icon" /><div v-if="showText" class="feedback-text">意见反馈 </div></div></template></el-popover></div><el-dialog v-model="dialogVisible"><img w-full :src="dialogImageUrl" alt="Preview Image" /></el-dialog></Teleport>
</template><script setup lang="ts">import { ElMessage } from 'element-plus';import type { UploadFile, ElForm, UploadProps } from 'element-plus';import { ChatLineSquare, Delete, Plus, ZoomIn } from '@element-plus/icons-vue';import { submitFeedback } from '@/api/config-center';+(() => {// 初始化数据准备。。。})();const handleDragOver = (event) => {event.preventDefault();};const allowedFormats = ['image/jpeg','image/png','image/gif','image/bmp','image/tiff','image/x-icon','image/svg+xml',] as const;const handleDrop = (event) => {event.preventDefault();const file = event.dataTransfer.files[0];if (!allowedFormats.includes(file.type)) {ElMessage.warning('只能上传 JPEG、PNG、GIF、BMP、TIFF、ICO 或 SVG 格式的图片');return;}if (fromData.value.imgs?.length >= 5) {ElMessage.warning('抱歉,最多只能上传5张图片!');return;}if (file.size > 2 * 1024 * 1024) {ElMessage.warning('图片大小不能超过 2MB');return;}const reader = new FileReader();reader.onload = () => {const image = {name: file.name,url: reader.result, // 用于页面回显raw: file, // 将图片的原始文件对象存储到 raw 属性中};fromData.value.imgs.push(image);};reader.readAsDataURL(file);};const showText = ref(false);const visible = ref(false);type FormInstance = InstanceType<typeof ElForm>;const refForm = ref<FormInstance>();const feedbackType = ref([]);const toJiraPage = () => {};const loading = ref(false);const handleSubmit = (formEl: FormInstance | undefined): void => {if (!formEl) return;formEl.validate((valid: any) => {if (valid) {loading.value = true;let fd = new FormData();fd.append('issueType', fromData.value.issueType);fd.append('summary', fromData.value.summary);fd.append('description', fromData.value.description);fromData.value.imgs.forEach((v) => fd.append('files', v.raw));submitFeedback(fd).then((res: any) => {if (res.code === 200) {ElMessage.success('反馈成功,感谢您的关注!');visible.value = false;fromData.value = {issueType: 0,summary: '',description: '',imgs: [],};} else {ElMessage.error('反馈失败:' + res.message);}}).catch((e) => ElMessage.error('反馈失败:' + e)).finally(() => (loading.value = false));} else {return false;}});};const fromData = ref({issueType: '', // 问题类型summary: '', // 概要description: '', // 描述imgs: [], // 图片});const close = () => {visible.value = false;showText.value = false;refForm.value?.resetFields();fromData.value = {issueType: '',summary: '',description: '',imgs: [],};};const dialogImageUrl = ref('');const dialogVisible = ref(false);const disabled = ref(false);const handlePictureCardPreview = (file: UploadFile) => {dialogImageUrl.value = file.url!;dialogVisible.value = true;};const handleRemove = (file: UploadFile) => {const index = fromData.value.imgs.findIndex((f: any) => f.uid === file.uid);fromData.value.imgs.splice(index, 1);};const fromRules = reactive({// issueType: [{ required: true, message: '请选择问题类型', trigger: 'blur' }],summary: [{ required: true, message: '请输入概要', trigger: 'blur' }],description: [{ required: true, message: '请输入描述', trigger: 'blur' }],});
</script>

由于我这边项目的需求,反馈组件我是和菜单组件放在一起
在这里插入图片描述

1.2 less部分

<style lang="less" scoped>::v-deep(.el-upload-list--picture-card .el-upload-list__item-actions span + span) {margin-left: 0.6rem !important;}::v-deep(.el-upload.el-upload--picture-card),::v-deep(li.el-upload-list__item) {width: 70px !important;height: 70px !important;}::v-deep .el-upload-dragger {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;}.feedback-content {height: 561px;position: relative;header {display: flex;justify-content: space-between;margin: 0 10px;}section {.from-content {height: 454px;// overflow-y: scroll;}.card-list {display: flex;gap: 20px;.card-item {padding: 0 20px;border-radius: 5px;background-color: #f2f3f5;border: 1px solid #dfdfdf;cursor: pointer;width: 100%;height: 35px;line-height: 35px;font-size: 12px;&.active {color: #fff;background-color: #4c7cee;}}}.upload {width: 60px;height: 60px;cursor: pointer;border: 1px dashed var(--el-border-color-darker);background-color: #fafafa;&:hover {border-color: var(--el-color-primary);color: var(--el-color-primary);}}}.dot {position: absolute;left: -12px;top: 0;width: 4px;height: 21px;border-radius: 5px;background-color: #4c7cee;}}.feedback {position: fixed;top: 50%;right: 0;color: #fff;cursor: pointer;border-radius: 6px;transform: translateY(-50%);background-color: #4c7cea;z-index: 999999999999;.line {width: 7px;height: 100px;border-radius: 6px;background-color: #4c7cea;}.feedback-text {letter-spacing: 0.3em;writing-mode: vertical-lr;text-orientation: upright;}@media only screen and (min-width: 1280px) {.slot-content {margin: 6px;.feedback-icon {width: 24px;height: 24px;margin-bottom: 5px;}.feedback-text {font-size: 16px;}}}@media only screen and (max-width: 1280px) {.slot-content {margin: 3px;.feedback-icon {width: 19px;height: 19px;margin-bottom: 3px;}.feedback-text {font-size: 13px;}}}}.btn-row {margin: 16px 8px 0;text-align: end;&-left {border-color: #4c7cee;color: #4c7cee;}}
</style>

3. 编码过程遇到的问题

  1. Teleport 是 Vue3 的一个内置组件,详细使用请查阅 Vue3官网
  2. 关于图片拖拽
    1. 最初的时候,是采用 el-uploaddrag 属性,来实现,但是后面有用户提出拖拽上传目标的框太小,建议可以把图片拖拽进整个表单,最开始时候的想法是在最外层的div加一个拖拽事件,但是实现起来有一个问题, el-upload 拖拽事件添加 .stop,会造成下方区域无法实现拖拽上传,其他区域OK,后采取的解决方式是, el-upload 去除拖拽属性,全部采用最外层的原生拖拽事件上传
      在这里插入图片描述
  3. 图片的上传
    图片需要和文字一起上传,最初的时候实在没有想到实现方式,后面查了好些文章,发现是通过 FormData 实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/210780.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大部分人都不知道微信语音是可以取消的

在微信聊天时&#xff0c;许多人都喜欢使用微信语音聊天&#xff0c;因为这样既省时又不需要打字&#xff0c;使用起来非常便捷。然而&#xff0c;不少人发现微信语音有一个小缺点&#xff0c;那就是一旦说错话&#xff0c;只要一松手语音就自动发送出去了&#xff0c;根本来不…

RT-Thread Studio文件消失不见或被排除构建

不得不说RT-Thread Studio里面配置真多&#xff0c;今天我同事的电脑发现根本没有被画斜杠的文件夹&#xff0c;导致我想移植f1的写内部flash这个&#xff08;可以看上一个文章&#xff09;时候不能直接点击属性排除构建&#xff0c;然后在网上查找的时候也没怎么找到说法&…

AIGC发展史

1 AIGC概况 1.1 AIGC定义 AIGC&#xff08;AI Generated Content&#xff09;是指利用人工智能技术生成的内容。它也被认为是继PGC,UGC之后的新型内容生产方式&#xff0c;AI绘画、AI写作等都属于AIGC的具体形式。2022年AIGC发展速度惊人&#xff0c;迭代速度更是呈现指数级发…

会声会影2024购买多少钱 会声会影在哪里购买

掌握视频编辑技术&#xff0c;能为我们的工作和生活带来很多帮助。例如&#xff1a;将我们精心编辑的视频&#xff0c;上传到抖音、快手等平台进行变现&#xff1b;通过天马行空的视频创意&#xff0c;摇身一变成为B站up主。因此&#xff0c;拥有一款像会声会影这样的视频编辑软…

树_二叉搜索树累加求和

//给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09;&#xff0c;使每个节点 node 的新值等于原树中大于或等于 // node.val 的值之和。 // // 提醒一下&#xff0c;二叉搜索树满足下列约束…

UData+StarRocks在京东物流的实践 | 京东物流技术团队

1 背景 数据服务与数据分析场景是数据团队在数据应用上两个大的方向&#xff0c;行业内大家有可能会遇到下面的问题&#xff1a; 1.1 数据服务 烟囱式开发模式&#xff1a;每来一个需求开发一个数据服务&#xff0c;数据服务无法复用&#xff0c;难以平台化&#xff0c;技术…

java项目日常运维需要的文档资料

一、前言 java项目开发完成&#xff0c;部署上线&#xff0c;进入项目运维阶段&#xff0c;日常工作需要准备哪些资料和文档?当项目上线后&#xff0c;运行一段时间&#xff0c;或多或少会遇到一些运维上的问题&#xff0c;比如服务器磁盘饱满&#xff0c;服务器CPU&#xff0…

也可Adobe Animate

Animate CC 由原Adobe Flash Professional CC 更名得来&#xff0c;2015年12月2日&#xff1a;Adobe 宣布Flash Professional更名为Animate CC&#xff0c;在支持Flash SWF文件的基础上&#xff0c;加入了对HTML5的支持。并在2016年1月份发布新版本的时候&#xff0c;正式更名为…

Java 设计模式系列:代理模式

文章目录 介绍静态代理基本介绍应用实例静态代理优缺点 动态代理基本介绍JDK 中生成代理对象的 API Cglib 代理基本介绍实现步骤 介绍 1&#xff09;代理模式&#xff1a;为一个对象提供一个替身&#xff0c;以控制对这个对象的访问。即通过代理对象访问目标对象 2&#xff09…

Java链接数据库

本文介绍的是Java链接数据库中的JDBC操作&#xff0c;JDBC虽然现在用的不多&#xff0c;但面试的时候会问道。需要有相应的了解。下面以链接MySQL为例子。 JDBC 什么jdbc Java DataBase Connectivity是一种用于执行SQL语句的Java API&#xff0c;它由一组用Java语言编写的类和…

服务器中启动和停止项目

服务器中启动和停止项目 一、前言二、使用命令启动和关闭项目1、启动项目2、停止项目 三、使用可执行脚本启动和关闭项目1、启动项目2、停止项目 一、前言 在服务器上部署项目&#xff0c;一般就是将项目挂在后台&#xff0c;如果是微服务首选docker-compose&#xff0c;可以看…

2022年高校大数据挑战赛A题工业机械设备故障预测求解全过程论文及程序

2022年高校大数据挑战赛 A题 工业机械设备故障预测 原题再现&#xff1a; 制造业是国民经济的主体&#xff0c;近十年来&#xff0c;嫦娥探月、祝融探火、北斗组网&#xff0c;一大批重大标志性创新成果引领中国制造业不断攀上新高度。作为制造业的核心&#xff0c;机械设备在…

一种新的基于物理的AlGaN/GaN HFET紧凑模型

标题&#xff1a;A new physics-based compact model for AlGaN/GaN HFETs (IEEE MTT-S International Microwave Symposium) 摘要 摘要 - 针对AlGaN/GaN HFET&#xff0c;提出了一种无拟合参数的物理解析模型。对于非饱和操作&#xff0c;建立了两个接入区和栅极下方I-V特性的…

关于队列的简单理解

1.队列(Queue) 1.1 关于队列 队列 &#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c; 队列具有先进先出 FIFO(First In First Out)的操作特性&#xff08;队列是个接口&#xff09;&#xff1b; 入队列&#x…

CMake中的CACHE关键字

2023年12月5日&#xff0c;周二晚上 在 CMake 中&#xff0c;CACHE 关键字用于在变量定义时将其值缓存起来&#xff0c;以便在后续的 CMake 运行中重用。这对于在多次构建过程中保持变量的持久性和一致性非常有用。 当使用 CACHE 关键字定义一个变量时&#xff0c;CMake 将会为…

【藏经阁一起读】(78)__《Apache Tomcat 的云原生演进》

【藏经阁一起读】&#xff08;78&#xff09; __《Apache Tomcat 的云原生演进》 目录 __《Apache Tomcat 的云原生演进》 一、读后感 二、文章知识点摘要 2.1、Tomcat的技术内幕和在喜马拉雅的实践 2.2、GraalVM static compilation in web container application&…

我最喜欢的白版应用,AI加持的新功能开源!强烈推荐

Excalidraw 把他们的文本到图表的功能开源了 Excalidraw是一个虚拟白板应用&#xff0c;专门用于绘制类似手绘的图表。它提供了一个无限的、基于画布的白板&#xff0c;具有手绘风格&#xff0c;支持多种功能。 之前我分享的&#xff1a;72张PNG&#xff0c;图解机器学习 里面…

对小程序的初了解

WXML和HTML的区别 标签名称不同 HTML&#xff1a;div、a、span、img WXML&#xff1a;view、text、image、navigator 属性节点不同 <a href"#">超链接</a> <navigator url"/pages/home/home"></navigator> 提供了类似vue的…

Unity WebGL通过URL的形式接收参数执行初始化

参考博客&#xff1a; http://t.csdnimg.cn/QnfhK 问题背景&#xff1a; 需要在外面的网页指定WebGL的打开初始化逻辑。 步骤&#xff1a; 1.配置jslib&#xff0c;用文本文件创建即可&#xff0c;"__Internal.jslib"。 2.加入一段代码&#xff1a; mergeInto(…

口袋参谋:如何选取标题中核心关键词?这招超简单!

​在淘宝爆款的标题中&#xff0c;关键词的选取是很重要的&#xff0c;买家通过搜索关键词来查找需要的商品&#xff0c;所以标题组合和优化的难度在于关键词的选取&#xff0c;让每个关键词恰到好处地发挥作用&#xff0c;带来更多更精准的流量。 1、选词逻辑 ①相关性 这是…