图片、视频加载方式

This commit is contained in:
zhujingzhou
2025-07-20 11:36:46 +07:00
parent 2ab6513b73
commit fa06b597e5
17 changed files with 1204 additions and 375 deletions
+60 -17
View File
@@ -8,13 +8,17 @@
-->
<template>
<div>
<quill-editor v-model="content" ref="myQuillEditor" :options="editorOption" @ready="onEditorReady($event)"></quill-editor>
<quill-editor
v-model="content"
ref="myQuillEditor"
:options="editorOption"
@ready="onEditorReady($event)"></quill-editor>
<!-- el-uploader不好手动触发上传功能 -->
<uploader
:style="{ display: 'none' }"
:options="{ ...options, target: '/api/web/file/uploadImg' }"
:style="{ display: 'none' }"
:options="{ ...options, target: '/api/web/file/uploadImg' }"
class="uploader-example"
@file-success="imageSuccess">
@file-success="imageSuccess">
<uploader-drop>
<uploader-btn ref="editorUploadImg" :attrs="attrsImg"></uploader-btn>
</uploader-drop>
@@ -22,6 +26,7 @@
</div>
</template>
<script>
import { handleVerAutoImg } from '@/utils/index.js'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
@@ -31,6 +36,7 @@ Link.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel', 'yinseinner']
import { quillEditor } from 'vue-quill-editor'
import { ImageDrop } from 'quill-image-drop-module'
import ImageResize from 'quill-image-resize-module'
import { data } from 'qrcode.vue'
Quill.register('modules/imageDrop', ImageDrop)
Quill.register('modules/imageResize', ImageResize)
@@ -49,6 +55,7 @@ export default {
textNode: {
default: ''
}
},
computed: {
options() {
@@ -69,7 +76,8 @@ export default {
const _that = this
return {
content: '',
domain: this.$store.getters.getSystemConfig.webImgReq,
imgPath: [],
domain: this.$store.getters.getSystemConfig.webImgReq, // 不能暴露源站地址
attrsImg: {
accept: ['image/*']
},
@@ -105,7 +113,7 @@ export default {
}
},
// //配置富文本框图片大小调整功能
// imageDrop: true,
imageDrop: true,
imageResize: {
displayStyles: {
backgroundColor: 'black',
@@ -114,6 +122,7 @@ export default {
},
modules: ['Resize', 'DisplaySize', 'Toolbar']
}
}
}
@@ -121,33 +130,67 @@ export default {
}
},
mounted() {},
methods: {
// 父组件获取数据
getdata() {
const str = this.content.replace(this.domain, '')
// 获取 Quill 编辑器中的所有 img 标签
const imgElements = this.$refs.myQuillEditor.quill.container.querySelectorAll('img')
// 存储自定义属性值
const customAttrValues = []
// 遍历所有 img 标签并获取自定义属性值
imgElements.forEach(img => {
const customAttrValue = img.getAttribute('data-custom') // 假设自定义属性是 data-custom
if (customAttrValue) {
customAttrValues.push(customAttrValue) // 添加到数组中
}
})
// 替换 img 标签的 src
for (let i = 0; i < imgElements.length; i++) {
const newSrc = customAttrValues[i]
const regex = new RegExp(`<img\\s+([^>]*?)src="([^"]*)"([^>]*?)>`, 'i')
return str
this.content = this.content.replace(regex, (match, beforeSrc, oldSrc, afterSrc) => {
// 使用 oldSrc 设置 data-custom 属性,同时替换 src
return `<img ${beforeSrc} src="${newSrc}" data-custom="${oldSrc}"${afterSrc}>`
})
}
return this.content
},
// 当文本框加载完成时,编译插入数据
async onEditorReady(e) {
const data = this.textNode.replace('img src="', 'img src="' + this.domain)
e.container.querySelector('.ql-blank').innerHTML = data
this.content = this.textNode.replace(/<img\s+([^>]*?)src="([^"]*?)"[^>]*data-custom="([^"]*?)"([^>]*?)>/g, (match, before, srcValue, customValue, after) => {
// 互换 src 和 data-custom 的值
return `<img ${before}src="${customValue}" data-custom="${srcValue}"${after}>`
})
},
imageSuccess(rootFile, file, message) {
async imageSuccess(rootFile, file, message) {
const { code, data } = JSON.parse(message)
if (code === 200) {
this.storageBase64 = await handleVerAutoImg(this.domain + data.path)
const reader = new FileReader()
reader.readAsDataURL(file.file)
reader.onload = e => {
reader.onload = async(e) => {
const base64Data = e.target.result // 获取转换后的 Data URL
const myeditor = this.$refs.myQuillEditor.quill
// editor方法获取当前光标位置
// 获取当前光标位置
const imgElement = document.createElement('img')
imgElement.src = base64Data
// 将 imgElement 插入到编辑器中
const index = myeditor.getSelection().index
myeditor.insertEmbed(index, 'image', this.domain + data.path)
// 调整光标到图片之后的位置上
myeditor.insertEmbed(index, 'image', imgElement.src)
// 通过 Quill 的 API 将图片插入后,直接修改 DOM 来添加自定义属性
const imgNode = myeditor.container.querySelector(`img[src="${imgElement.src}"]`)
if (imgNode) {
imgNode.setAttribute('data-custom', data.path) // 设置自定义属性
}
myeditor.setSelection(index + 1)
}
// 读取 Blob 数据
reader.readAsDataURL(file.file)
}
},
+12 -11
View File
@@ -6,10 +6,10 @@
* @LastEditTime: 2020-11-24 17:04:24
-->
<template>
<el-dialog append-to-body class="img_dialog" :title="title" align="center" :visible.sync="dialogVisible" width="40%" :before-close="handleClose" :close-on-click-modal="false" :modal-append-to-body='false'>
<el-dialog class="img_dialog" append-to-body :title="title" align="center" :visible.sync="dialogVisible" width="40%" :before-close="handleClose" :close-on-click-modal="false" :modal-append-to-body='false'>
<el-carousel v-if="isArry" indicator-position="outside">
<el-carousel-item v-for="item in mediaUrl" :key="item">
<div class="media-box" id="img-box" > <img :src="domain+item" /></div>
<div class="media-box" id="img-box" > <img :src="changeImg(item)" /></div>
</el-carousel-item>
</el-carousel>
<div v-else class="media-box" id="img-box" > <img :src="mediaUrl" /></div>
@@ -17,6 +17,7 @@
</template>
<script>
import { handleVerAutoImg } from '@/utils/index.js'
export default {
data() {
return {
@@ -32,10 +33,14 @@ export default {
token() {
return this.$store.state.token
}
},
mounted() {
},
methods: {
async changeImg(url) {
return await handleVerAutoImg(this.domain + url)
},
handleClose() {
if (this.player) {
this.player.dispose()
@@ -58,31 +63,26 @@ export default {
</script>
<style scoped lang="scss">
.my_data{
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.img_dialog{
::v-deep{
.el-dialog__body{
.el-dialog__body{
height: auto;
.el-carousel__item{
overflow: auto !important;
}
}
}
}
.media-box {
width: 100%;
height: 400px;
text-align: center;
overflow: hidden;
img {
height: 100%;
max-width: 100%;
width: 100%;
}
#my-video {
width: 100% !important;
@@ -90,4 +90,5 @@ export default {
border: none !important;
}
}
</style>
+13 -15
View File
@@ -13,7 +13,7 @@
</div>
<div class="showImg">
<div v-if="showNoImg">暂无</div>
<el-image v-else :src="src" style="width: 100%, height: 100%" @click="checkMedia()" lazy >
<el-image v-else :src="src" @click="checkMedia()" lazy >
<div slot="placeholder" class="image-slot" :style="{width: width, height: height, lineHeight: height}">
<i class="el-icon-loading"></i>加载中
</div>
@@ -28,6 +28,7 @@
</template>
<script>
import Dialog from './components/dialog'
import { handleVerAutoImg } from '@/utils/index.js'
export default {
data() {
return {
@@ -43,15 +44,12 @@ export default {
},
props: ['width', 'height', 'urls'],
watch: {
urls(newval, oldval) {
async urls(newval, oldval) {
this.urls = newval
// if (this.type && this.type === 'daichong') {
// this.domain = ''
// }
this.imgsUrl = []
if (this.urls) { // 判断是否存在
if (typeof (this.urls) === 'string') { // 判断是单张图片还是数组
this.src = this.domain + this.urls
this.src = await handleVerAutoImg(this.domain + this.urls)
this.showNoImg = false
} else {
if (this.urls.length) { // 判断是否为空数组
@@ -60,8 +58,8 @@ export default {
} else {
this.showChangeBtn = true
}
this.urls.forEach(item => {
this.imgsUrl.push(this.domain + item)
this.urls.forEach(async item => {
this.imgsUrl.push(await handleVerAutoImg(this.domain + this.urls))
})
this.src = this.imgsUrl[0]
this.showNoImg = false
@@ -75,11 +73,11 @@ export default {
}
}
},
mounted() {
async mounted() {
this.imgsUrl = []
if (this.urls) { // 判断是否存在
if (typeof (this.urls) === 'string') { // 判断是单张图片还是数组
this.src = this.domain + this.urls
this.src = await handleVerAutoImg(this.domain + this.urls)
} else {
if (this.urls.length) { // 判断是否为空数组
if (this.urls.length === 1) { // 判断数组中是否只有一张图片
@@ -87,8 +85,8 @@ export default {
} else {
this.showChangeBtn = true
}
this.urls.forEach(item => {
this.imgsUrl.push(this.domain + item)
this.urls.forEach(async item => {
this.imgsUrl.push(await handleVerAutoImg(this.domain + item))
})
this.src = this.imgsUrl[0]
} else {
@@ -118,12 +116,12 @@ export default {
}
},
// 查看媒体
checkMedia() {
async checkMedia() {
if (this.urls) { // 判断是否存在
if (typeof (this.urls) === 'string') { // 判断是单张图片还是数组
this.$refs.dialog.open(this.domain + this.urls, 'img')
this.$refs.dialog.open(await handleVerAutoImg(this.domain + this.urls), 'img')
} else {
this.$refs.dialog.open(this.urls, 'img')
this.$refs.dialog.open(this.imgsUrl, 'img')
}
}
}
+26 -8
View File
@@ -14,12 +14,12 @@
<span>视频时长:{{ this.myData.playTime + "s" }}</span>
</div>
<!-- <div class="media-box" id="media-box"></div> -->
<div v-if="mediaUrl" style="width: 400px; heigth: 130px; background: red;margin-top:10px">
<div v-if="mediaUrl" style="width: 100%; heigth: 200px; background: red">
<DPlayer
ref="player"
:video="{
url: mediaUrl,
type: type === 'mp4' ? '' : 'hls',
type: type=== 'mp4' ?'':'hls',
pic: '',
}"
hotkey
@@ -53,7 +53,7 @@ export default {
title: '封面图查看',
params: {},
myData: {},
type: undefined,
type: undefined,
domain: '/api/web/media/m3u8/' // 存放图片的域名
}
},
@@ -79,6 +79,7 @@ export default {
// 视频审查列表
videoFeviewList() {
videoFeviewList(this.params, this.id).then((res) => {
// this.myData = res.data,
})
},
@@ -93,18 +94,35 @@ export default {
this.myData = { ...data }
this.dialogVisible = true
this.title = '视频查看'
this.mediaUrl =
this.domain + (url.match(RegExp(/.m3u8/)) ? url : url + '.m3u8')
if (url.indexOf('mp4') !== -1) {
this.mediaUrl = this.$store.getters.getSystemConfig.webImgReq + url
if (url.indexOf('mp4') !== -1) {
this.mediaUrl = this.$store.getters.getSystemConfig.mp4Url + url
this.type = 'mp4'
} else {
this.mediaUrl =
this.domain + (url.match(RegExp(/.m3u8/)) ? url : url + '.m3u8')
this.videoFeviewList()
}
// + '&token=' + this.token
// this.id = id;
// this.$nextTick(function () {
// document.getElementById("media-box").innerHTML =
// '<video id="my-video" class="video-js vjs-default-skin" controls preload="auto" style="width:100%;height:100%"></video>';
// this.player = videojs(
// "my-video",
// {
// bigPlayButton: false,
// textTrackDisplay: false,
// posterImage: true,
// errorDisplay: true,
// controlBar: true,
// },
// function () {
// this.play();
// }
// );
// this.player.src(this.mediaUrl);
// });
}
}
}
+50 -18
View File
@@ -2,28 +2,35 @@
* @Author: 王涛
* @Mail: no
* @Date: 2020-09-29 19:56:44
* @LastEditors: Please set LastEditors
* @LastEditTime: 2022-06-21 20:05:54
* @LastEditors: Administrator admin@example.com
* @LastEditTime: 2024-11-19 10:14:08
-->
<template>
<!-- 使用方法 <UploadImg @changeImgUrl='changeMobileAvatar' :img='form.mobileAvatar' :width='"100px"' :height='"100px"'></UploadImg>
changeMobileAvatar(data) {
this.form.mobileAvatar = data
}, -->
<el-upload class="avatar-uploader" action="/api/web/file/uploadImg" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload">
<el-upload
class="avatar-uploader"
action="/api/web/file/uploadImg"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
<div ref="uploader">
<img v-if="imageUrl" :src="domain + imageUrl" class="avatar" />
<img v-if="newImg" :src="newImg" class="avatar" />
<i v-if="imageUrl" @click.stop="handleRemove" class="el-icon-delete"></i>
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</div>
</el-upload>
</template>
<script>
import { handleVerAutoImg } from '@/utils/index.js'
export default {
data() {
return {
domain: this.$store.getters.getSystemConfig.webImgReq,
imageUrl: ''
imageUrl: '',
newImg: ''
}
},
props: {
@@ -38,11 +45,23 @@ export default {
height: {
type: String,
default: '180px'
},
index: {
type: Number,
default: 0
},
showIndex: {
type: Boolean,
default: false
}
},
watch: {
img: function(val, oldval) {
this.imageUrl = val
},
imageUrl: async function(val, oldval) {
const a = await handleVerAutoImg(this.domain + val)
this.newImg = a
}
},
mounted() {
@@ -56,6 +75,9 @@ export default {
}
},
methods: {
async changeImg(url) {
},
handleAvatarSuccess(res, file) {
if (res.code === 200) {
this.$message({
@@ -74,11 +96,19 @@ export default {
}
},
changeImgUrl() {
this.$emit('changeImgUrl', this.imageUrl)
if (this.showIndex) {
this.$emit('changeImgUrl', { url: this.imageUrl, index: this.index })
} else {
this.$emit('changeImgUrl', this.imageUrl)
}
},
handleRemove(f) {
this.imageUrl = ''
this.$emit('onRemove', f)
if (this.showIndex) {
this.$emit('onRemove', { key: f, index: this.index })
} else {
this.$emit('onRemove', f)
}
},
// 上传前进行压缩
beforeAvatarUpload(file) {
@@ -100,7 +130,7 @@ export default {
// message: '上传图片大小不能超过 2MB!!'
// })
// }
return isJPG
return isJPG
},
// 图片等比压缩
@@ -142,19 +172,21 @@ export default {
}
</script>
<style scoped>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
.avatar-uploader {
.el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.el-upload:hover {
border-color: #409eff;
}
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
color: #d9d9d9;
width: 100%;
height: 100%;
line-height: 100%;
+16 -4
View File
@@ -9,11 +9,11 @@
<div>
<draggable v-model="locaImg" class="list-group" tag="ul" v-bind="dragOptions" @start="drag = true" @end="drag = false">
<div v-for="(img,index) in locaImg" :key="index" class="image-list" style="float: left;">
<img class="el-upload-list__item-thumbnail image-detail" :src="domain+img" alt="">
<img class="el-upload-list__item-thumbnail image-detail" :src="imgSrc[index]" alt="">
<!-- 遮罩层 -->
<a href="#">
<div class="image-mask">
<span class="add" @click="handlePictureCardPreview(domain+img)">
<span class="add" @click="handlePictureCardPreview(imgSrc[index])">
<i class="el-icon-view"></i>
</span>
<span class="delete" @click="removeImage(index)">
@@ -33,6 +33,7 @@
</template>
<script>
import draggable from 'vuedraggable'
import { handleVerAutoImg } from '@/utils/index.js'
export default {
components: { draggable },
data() {
@@ -46,7 +47,8 @@ export default {
// { name: 'food2.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100' }
],
drag: false, // 拖拽效果是否开启
locaImg: []
locaImg: [],
imgSrc: []
}
},
props: {
@@ -57,6 +59,7 @@ export default {
}
}
},
computed: {
dragOptions() {
return {
@@ -66,6 +69,7 @@ export default {
ghostClass: 'ghost'
}
}
},
watch: {
// 监听拖动事件改变时,传出新的图片数组
@@ -80,15 +84,23 @@ export default {
}
},
mounted() {
async mounted() {
this.locaImg = []
if (this.img) {
this.img.filter((item) => {
this.locaImg.push(item)
})
}
// 在组件挂载时加载图像 URL
this.imgSrc = await Promise.all(this.locaImg.map(img => this.changeImg(img)))
},
methods: {
async changeImg(img) {
// 这里应该替换为真实的异步代码
const a = await handleVerAutoImg(this.domain + img)
return a
},
handleChange(file, fileList) {
if (file.status === 'success') {
this.locaImg.push(file.response.data.path)
+8 -1
View File
@@ -8,7 +8,7 @@
<template>
<div class="box">
<div v-on:paste="handlePaste" v-drag="handleDrag" >
<img v-if='imageUrl' ref="preview" :src="domain+imageUrl" alt="" style="width:300px;height:300px;">
<img v-if='newImg' ref="preview" :src="newImg" alt="" style="width:300px;height:300px;">
<div v-else style="width:300px;height:300px;border:1px solid;padding:20px;">
<div style="text-indent:2em;">将图片按<span style="color:red">cmd+C复制</span>到粘贴板<span style="color:red">cmd+V粘贴</span>至此处</div>
<div style="text-indent:2em;">或者将图片拖拽至此处</div>
@@ -18,6 +18,8 @@
</template>
<script>
import { mediaUploadImg } from '@/api/components/UploadImgPaste'
import { handleVerAutoImg } from '@/utils/index.js'
export default {
data() {
return {
@@ -34,6 +36,10 @@ export default {
watch: {
img: function(val) {
this.imageUrl = val
},
imageUrl: async function(val, oldval) {
const a = await handleVerAutoImg(this.domain + val)
this.newImg = a
}
},
mounted() {
@@ -44,6 +50,7 @@ export default {
}
},
methods: {
handleDrag(event) {
const items = Array.from(event.dataTransfer.items)
let file = null