进入uniapp

This commit is contained in:
2023-12-01 23:57:44 +08:00
parent a78893ea1f
commit d61f5e6c92
524 changed files with 88081 additions and 12 deletions

View File

@@ -0,0 +1,319 @@
<template>
<view @touchmove.stop.prevent="moveHandle" class="moveWrap">
<movable-area class="movarea" ref="areaBox" id="areaBox" :style="{height:imgSize*rowNum +200+ 'rpx'}">
<view class="imgBox" :style="{height:imgSize*rowNum + 'rpx'}">
<view :id="'img' + idx" class="imgItem" v-for="(item, idx) in imgList" :key="idx"
:style="{transition:addJump?' all 0.5s':'',opacity:idx===selectIdx?'0':'1', width: imgSize + 'rpx', height: imgSize + 'rpx', borderRadius:imgRadius+'rpx',padding:imgPadding+'rpx',left:(hoverImgIdx==='img'+idx?curHoverBoxMove.x+'rpx':''),top:(hoverImgIdx==='img'+idx?curHoverBoxMove.y+'rpx':'')}">
<view class="imgItem-img" @tap="itemclick(item,idx)" @touchstart="tstr(idx, $event)" @touchmove="tsmo" @touchend="toend">
<image v-if="item.videoUrl" class="imgItem-play" src="../../static/img/bf.png" mode="aspectFill"></image>
<image :style="{borderRadius:imgRadius+'rpx' }" :ref="'img' + idx" :src="item.url" mode="aspectFill"></image>
</view>
</view>
<slot></slot>
</view>
<movable-view v-if="moveSrc" :animation="false" class="moveV" :x="x" :y="y" direction="all"
@change="onChange"
:style="{ width: imgSize + 'rpx', height: imgSize + 'rpx',padding:imgPadding+'rpx' }">
<image v-if="moveSrc.videoUrl" class="imgItem-play" src="../../static/img/bf.png" mode="aspectFill"></image>
<image :style="{borderRadius:imgRadius+'rpx' }" :src="moveSrc.url" mode="aspectFill"></image>
</movable-view>
</movable-area>
<view v-if="showDelete">
<view class="delete" :class="{'deleteType':deleteType}">
<text v-if="deleteType">松开删除</text>
<text v-else>拖动到此处删除</text>
</view>
</view>
</view>
</template>
<script>
export default {
emits:['itemclick','update:imgList','deleteImage','change','moveEndList'],
props: {
//图片列表
imgList: {
type: Array,
default: function() {
return [];
}
},
//图片大小
imgSize: {
type: Number
},
//图片间距
imgPadding: {
type: Number
},
//图片行数
rowNum: {
type: Number
},
//图片圆角
imgRadius: {
type: Number
}
},
components: {},
data() {
return {
showDelete:false,
touchobj:null,
deleteType:false,
addJump:false,
areaBoxInfo: {},
x: 0,
y: 0,
selectIdx: null,
moveSrc: null,
areaBoxTop: 0,
hoverImgIdx: '',
inBoxXY: {},
curHoverBoxMove: {
x: 0,
y: 0
}
};
},
watch: {
hoverImgIdx(e) {
let idx = this.selectIdx
let hoverIdx = parseInt(e.split('img')[1]);
if (this.imgList[idx]) {
let selectRow = this.imgList[idx].y / uni.upx2px(this.imgSize)
let selectColum= this.imgList[idx].x / uni.upx2px(this.imgSize)
let hoverRow = this.imgList[hoverIdx].y / uni.upx2px(this.imgSize)
let hoverColum= this.imgList[hoverIdx].x / uni.upx2px(this.imgSize)
let left = -(this.imgSize * (hoverColum - selectColum))
let top= -(this.imgSize * (hoverRow - selectRow))
this.curHoverBoxMove = {
x: left,
y: top,
}
}
},
// imgList(e){
// console.log('变化了item')
// this.$nextTick(()=>{
// this.setImgXy()
// })
// }
imgList: {
deep: true,//深度监听可见听对象中的元素变化例:obj.id
immediate:false,//在进入页面时第一次绑定值不会立刻执行监听只有数据发生改变才会执行handler中的操作
handler(val) {//watch事件
// console.log('变化了item')
this.$nextTick(()=>{
this.setImgXy()
})
}
}
},
methods: {
itemclick(e,i){
this.$emit('itemclick',e,i)
},
moveHandle() {
return;
},
deleteImage: function(e) {
var index = e
var deletedImage = this.imgList[index]
this.imgList.splice(index, 1)
this.$emit('deleteImage', deletedImage)
},
onChange(e) {
var boxHeight=this.imgSize * this.rowNum
var deleteHeight=(boxHeight-80)/2
if(e.detail.y>deleteHeight){
this.deleteType=true
}else{
this.deleteType=false
}
this.showDelete=true
this.$emit('change',e)
},
tstr(e, s) {
this.touchobj=this.imgList[e]
this.addJump=true
this.getDomInfo('areaBox', info => {
this.areaBoxInfo = info;
//获取拖拽区域的上边距和下边距
let areaBoxTop = this.areaBoxInfo.top;
let areaBoxLeft = this.areaBoxInfo.left;
// 设置可移动方块的初始位置为当前所选中图片的位置坐标
this.x = this.imgList[e].x;
this.y = this.imgList[e].y;
//显示可移动方块
this.moveSrc = this.imgList[e];
//保存当前所选择的图片索引
this.selectIdx = e;
var x = s.changedTouches[0].clientX - areaBoxLeft;
var y = s.changedTouches[0].clientY - areaBoxTop;
// 保存鼠标在图片内的坐标
this.inBoxXY = {
x: x - this.imgList[e].x,
y: y - this.imgList[e].y,
}
});
},
tsmo(e) {
let areaBoxTop = this.areaBoxInfo.top;
let areaBoxLeft = this.areaBoxInfo.left;
let imgSize = this.imgSize;
//重置为以拖拽盒子左上角为坐标原点
var x = e.changedTouches[0].clientX - areaBoxLeft;
var y = e.changedTouches[0].clientY - areaBoxTop;
this.x = x - this.inBoxXY.x;
this.y = y - this.inBoxXY.y;
this.imgList.forEach((item, idx) => {
if (x > item.x && x < item.x + imgSize && y > item.y && y < item.y + imgSize) {
this.hoverImgIdx = 'img' + idx
}
});
},
toend(e) {
this.addJump=false
if(this.deleteType){
this.deleteImage(this.selectIdx)
}else{
// 移动结束隐藏可移动方块
let beforeIdx = this.selectIdx;
let afterIdx = parseInt(this.hoverImgIdx.split('img')[1]);
if (this.hoverImgIdx !== '' && beforeIdx !== afterIdx) {
this.imgList[beforeIdx] = this.imgList[afterIdx];
this.imgList[afterIdx] = this.moveSrc;
this.$emit('moveEndList', this.imgList);
}
}
this.moveSrc = '';
this.hoverImgIdx = ''
this.selectIdx = null
this.deleteType=false
this.showDelete=false
this.$nextTick(()=>{
this.setImgXy()
})
},
getDomInfo(id, callBack) {
const query = uni.createSelectorQuery().in(this);
query.select('#' + id)
.boundingClientRect()
.exec(function(res) {
callBack(res[0]);
});
},
setImgXy(){//设置每个图片的基础xy轴
this.getDomInfo('areaBox', info => {
this.areaBoxInfo = info;
// 设置区域内所有图片的左上角坐标
this.imgList.forEach((item, idx) => {
this.getDomInfo('img' + idx, res => {
item.x = res.left - info.left;
});
this.getDomInfo('img' + idx, res => {
item.y = res.top - info.top;
});
});
});
}
},
mounted() {
this.setImgXy()
}
};
</script>
<style lang="scss" scoped>
.imgItem-play{
z-index: 1;
position: absolute;
left: 50%;
top: 50%;
width: 80rpx !important;
height: 80rpx !important;
transform: translate(-50%,-50%);
}
.movarea {
width: 100%;
height: 320rpx;
display: flex;
flex-direction: row;
}
.imgBox {
position: relative;
z-index: 1;
width: 100%;
height: 320rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
.imgItem {
position: relative;
box-sizing: border-box;
left: 0;
top: 0;
box-sizing: border-box;
image {
width: 100%;
height: 100%;
// transition: all 0.2s;
vertical-align: top;
}
}
}
.moveV {
opacity: 0.6;
z-index: 0;
box-sizing: border-box;
image {
width: 100%;
height: 100%;
}
}
.select {
opacity: 0;
}
.zmm-upload-image-deleteIcon {
right: 0rpx;
top: 0rpx;
position: absolute;
background-color: rgba(0, 0, 0, 0.3);
width: 36rpx;
height: 36rpx;
text-align: center;
border-radius: 50%;
color: white;
font-size: 30rpx;
z-index: 2;
display: flex;flex-direction: row;align-items: center;justify-content: center;
}
.delete{
position: absolute;
bottom: 0rpx;
left: 0;
width: 100%;
height: 80rpx;
line-height: 80rpx;
background: #C04A42;
text-align: center;
transition: all 0.3s;
}
.delete text{
color: #fff;
}
.deleteType{
transform: scale(1.1);
}
.moveWrap{
position: relative;
}
.imgItem-img{
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,437 @@
<template>
<view class="zmm-upload-image" v-if="show">
<zmm-upload-image-drag :key="key" :rowNum="rows" :imgRadius="imgRadius" :imgSize="imgSize" :imgPadding="imgPadding" :imgList="fileList" @deleteImage="deleteImage" @itemclick="itemclick">
<view v-if="showAdd && fileList.length < limit" @tap="chooseTap" :style="{ width: imgSize + 'rpx', height: imgSize + 'rpx',padding:imgPadding+'rpx' }" class="zmm-upload-image-item-slot">
<view class="zmm-upload-image-item-slotIcon">
<uni-icons type="plusempty" color="#6E6E6E" size="24" />
</view>
</view>
</zmm-upload-image-drag>
</view>
</template>
<script>
import zmmUploadImageDrag from './zmm-upload-image-drag.vue';
import http from '@/common/request.js';
import customHttp from "@/common/customHttp.js"
import config from "@/common/config.js"
export default {
components: {
zmmUploadImageDrag
},
name: 'zmm-upload-image',
emits: ['update:modelValue', 'deleteImage','allComplete','oneComplete'],
props: {
modelValue: {
type: [Array]
},
chooseType:{
type:String,
default:'chooseImage'//chooseImage图片 chooseVideo视频 chooseMedia图片或视频
},
imgSize: {
//图片大小
type: Number,
default: 214
},
imgPadding: {
//图片间距
type: Number,
default: 6
},
imgRadius: {
//图片圆角
type: Number,
default: 4
},
fileList: {
//图片数据
type: Array,
default(){
return []
}
},
rowNumber: {
//一行多少个图片
type: Number,
default: 3
},
showAdd: {
//增加按钮
type: Boolean,
default: true
},
show: {
//是否显示
type: Boolean,
default: true
},
fileAction: {
//后台文件上传接口
type: String,
default: config.getImApiUrl() + '/file/upload'
},
videoAction: {
//后台上传接口
type: String,
default: config.getImApiUrl() + '/file/uploadVideo'
},
formData: {
//上传所附带数据
type: Object
},
header: {
//自定义头
type: Object,
default() {
return {
Authorization: uni.getStorageSync('Authorization'),
device: uni.getStorageSync('device'),
version: uni.getStorageSync('version')
};
}
},
limit: {
//图片数量
type: Number,
default: 9
},
fileKey: {
//后端接受的filekey
type: String,
default: 'file'
}
},
data() {
return {
key:0,
isDestroyed: false,
showUploadProgress:true
};
},
watch: {
modelValue(val) {
console.log(val);
},
fileList(e){
// console.log('变化了list')
this.key++
},
rows(e){
this.key++
}
// fileList: {
// deep: true,//深度监听可见听对象中的元素变化例:obj.id
// immediate:false,//在进入页面时第一次绑定值不会立刻执行监听只有数据发生改变才会执行handler中的操作
// handler(val) {//watch事件
// console.log('变化了list')
// this.key++
// }
// }
},
mounted: function() {},
destroyed: function() {
this.isDestroyed = true;
},
computed: {
rows() {
var buzhu=this.showAdd && this.fileList.length < this.limit ? 1 : 0;
var rows=Math.ceil((this.fileList.length+buzhu) / this.rowNumber);
return rows
}
},
methods: {
itemclick(e,i){
if(!e.videoUrl){
var imgs=[]
for (var i = 0; i < this.fileList.length; i++) {
var item=this.fileList[i]
if(!item.videoUrl){
imgs.push(item.url)
}
}
this.previewImage(imgs,i)
}else{
this.$fc.plusDownload({onlinePath:e.videoUrl}).then(res=>{
this.$fc.plusOpenFile({filePath:res})
})
}
},
chooseTap(){
switch (this.chooseType){
case 'chooseImage':
this.selectImage()
break;
case 'chooseVideo':
this.selectVideo()
break;
case 'chooseMedia':
uni.showActionSheet({
itemList: ['图片'],
// itemList: ['图片', '视频'],
success: (res) => {
switch (res.tapIndex){
case 0:
this.selectImage()
break;
case 1:
this.selectVideo()
break;
default:
break;
}
}
});
break;
default:
break;
}
},
selectImage: function() {
var _self = this;
if (!_self.fileList) {
_self.fileList = [];
}
uni.chooseImage({
count: _self.limit ? _self.limit - _self.fileList.length : 999,
sourceType: ['album', 'camera'],
sizeType:['original','compressed'],
success: function(e) {
var imagePathArr = e.tempFilePaths;
//如果设置了limit限制在web上count参数无效这里做判断控制选择的数量是否合要求
//在非微信小程序里,虽然可以选多张,但选择的结果会被截掉
//在app里会自动做选择数量的限制
if (_self.limit) {
var availableImageNumber = _self.limit - _self.fileList.length;
if (availableImageNumber < imagePathArr.length) {
uni.showToast({
title: '图片总数限制为' + _self.limit + '张,当前还可以选' + availableImageNumber + '张',
icon: 'none',
mask: false,
duration: 2000
});
return;
}
}
//检查服务器地址是否设置,设置即表示图片要上传到服务器
if (_self.fileAction) {
uni.showToast({
title: '上传进度0/' + imagePathArr.length,
icon: 'none',
mask: false
});
var remoteIndexStart = _self.fileList.length - imagePathArr.length;
var promiseWorkList = [];
var keyname = _self.fileKey ? _self.fileKey : 'upload-images';
var completeImages = 0;
for (let i = 0; i < imagePathArr.length; i++) {
promiseWorkList.push(
new Promise((resolve, reject) => {
let remoteUrlIndex = remoteIndexStart + i;
uni.uploadFile({
url: _self.fileAction,
fileType: 'image',
header: _self.header,
formData: _self.formData,
filePath: imagePathArr[i],
name: keyname,
success: function(res) {
if (res.statusCode === 200) {
if (_self.isDestroyed) {
return;
}
completeImages++;
if (_self.showUploadProgress) {
uni.showToast({
title: '上传进度:' + completeImages + '/' + imagePathArr.length,
icon: 'none',
mask: false,
duration: 500
});
}
// console.log('success to upload image: ' + res.data)
var resItem=JSON.parse(res.data)
console.error(resItem)
_self.$emit('oneComplete', {
name: resItem.data,
url: resItem.data,
type:'IMAGE'
},_self.chooseType);
resolve(res.data);
} else {
console.log('fail to upload image:' + res.data);
reject('fail to upload image:' + remoteUrlIndex);
}
},
fail: function(res) {
uni.showToast({
title: '上传失败请检查网络',
icon: 'none'
});
console.log('fail to upload image:' + res);
reject('fail to upload image:' + remoteUrlIndex);
}
});
})
);
}
Promise.all(promiseWorkList).then(result => {
if (_self.isDestroyed) {
return;
}
var fresult=[]
for (let i = 0; i < result.length; i++) {
var resItem=JSON.parse(result[i])
_self.fileList.push({
name: resItem.data.fileName,
url: resItem.data.fullPath,
type:'IMAGE'
});
fresult.push({
name: resItem.data.fileName,
url: resItem.data.fullPath,
type:'IMAGE'
})
}
_self.$emit('allComplete', fresult,_self.chooseType);
});
} else {
var testImg='图片地址'
for (let i = 0; i < imagePathArr.length; i++) {
_self.fileList.push({
name: 'avatar'+i+'.jpg',
url: testImg
});
}
}
}
});
},
selectVideo: function() {
var _self = this;
if (!_self.fileList) {
_self.fileList = [];
}
uni.chooseVideo({
sourceType: ['album', 'camera'],
compressed:true,
success: function(e) {
if (_self.showUploadProgress) {
uni.showLoading({
title:'上传中...'
})
}
uploadVideo(e).then(res=>{
uni.hideLoading()
_self.fileList.push(res);
_self.$emit('allComplete', res ,_self.chooseType);
})
function uploadVideo(e){
return new Promise((resolve, reject) => {
uni.uploadFile({
url: _self.videoAction,
fileType: 'video',
header: _self.header,
formData: _self.formData,
filePath: e.tempFilePath,
name: _self.fileKey,
success: function(res) {
if (res.statusCode === 200) {
if (_self.isDestroyed) {
return;
}
var resItem=JSON.parse(res.data)
var fresult={
name: resItem.data.fileName,
videoUrl: resItem.data.fullPath,
url: resItem.data.screenShot,
type:'VIDEO'
}
_self.$emit('oneComplete', {
name: resItem.data.fileName,
videoUrl: resItem.data.fullPath,
url: resItem.data.screenShot,
type:'VIDEO'
},_self.chooseType);
resolve(fresult)
} else {
reject(res)
}
},
fail: function(res) {
uni.showToast({
title: '上传失败请检查网络',
icon: 'none'
});
reject(res)
}
});
})
}
}
});
},
deleteImage: function(e) {
this.$emit('deleteImage', e);
},
previewImage: function(arr,index) {
uni.previewImage({
current: index,
indicator: 'number',
loop: true,
urls: arr
});
},
}
};
</script>
<style scoped>
.zmm-upload-image {
width: 100%;
position: relative;
/* display: flex;flex-direction: row;justify-content: center; */
}
.zmm-upload-image-list {
/* width: 714rpx; */
display: flex;
flex-wrap: wrap;
flex-direction: row;
}
.zmm-upload-image-item {
width: 214rpx;
height: 214rpx;
margin: 12rpx;
box-sizing: border-box;
position: relative;
border-radius: 4rpx;
}
.zmm-upload-image-item-img {
width: 100%;
height: 100%;
border-radius: 4rpx;
}
.zmm-upload-image-item-slot{
box-sizing: border-box;
}
.zmm-upload-image-item-slotIcon {
box-sizing: border-box;
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
background-color: #f7f7f7;
}
</style>