1
This commit is contained in:
@@ -0,0 +1,875 @@
|
||||
<script lang="ts">
|
||||
import { type PropType,toRaw } from "vue"
|
||||
import { getUid, rpx2px } from "../../core/util/xCoreUtil.uts"
|
||||
import { getDefaultColor } from "../../core/util/xCoreColorUtil.uts"
|
||||
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
|
||||
import { xUploadMedia } from "../../core/util/xUploadFileMedia.uts"
|
||||
import { XUPLOADFILE_INFO, XUPLOADFILE_FILE_INFO, XUPLOADFILE_FILE_VALUE } from "../../interface.uts"
|
||||
import { xConfig } from "../../config/xConfig.uts"
|
||||
type funcalldel = (index : number, item : XUPLOADFILE_FILE_INFO) => Promise<boolean>
|
||||
type funbeforeCompelte = (item : XUPLOADFILE_FILE_INFO) => XUPLOADFILE_FILE_INFO
|
||||
type beforeUploadType = (item : XUPLOADFILE_FILE_INFO) => Promise<XUPLOADFILE_FILE_INFO>
|
||||
|
||||
/**
|
||||
* @name 图片上传 xUploadMedia
|
||||
* @description 可以上传视频或者图片,注意不要混搭.要么上传视频,要么上传图片.当用户自行添加默认文件时可以给你的文件对象加status:0表示待上传,要确保你的上传文件路径是正确的
|
||||
* 否则安卓会引发io错误,并且uni.upload是无法捕捉到fail事件中,会导致整个程序不可用
|
||||
* @page /pages/index/upload-media
|
||||
* @category 表单组件
|
||||
* @constant 平台兼容
|
||||
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
|
||||
*/
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
nowFileList: [] as XUPLOADFILE_FILE_INFO[],
|
||||
itemWidth: 0,
|
||||
spaceGap: 4,
|
||||
xupload: new xUploadMedia() as xUploadMedia | null,
|
||||
mouse_x: 0,
|
||||
mouse_y: 0,
|
||||
_totalHeight: 0,
|
||||
_nowIndex: -1,
|
||||
// 是否已经长按触发了拖动排序功能。
|
||||
_isLongPressCanDisableMove: false,
|
||||
movexitem_x: 0,
|
||||
movexitem_y: 0,
|
||||
_newIndex: 0,
|
||||
showMoveItem: false,
|
||||
tempMoveIndex: -1,
|
||||
diffY: 0,
|
||||
diffX: 0,
|
||||
tid2: 0,
|
||||
nowshowClickMedioSrc:'',
|
||||
showVideo:false,
|
||||
tid3:66
|
||||
}
|
||||
},
|
||||
emits: [
|
||||
/**
|
||||
* 每次全部上传完时触发
|
||||
* @param {XUPLOADFILE_FILE_VALUE[]} 列表数据
|
||||
*/
|
||||
'complete',
|
||||
/**
|
||||
* 变化时触发
|
||||
* @param {XUPLOADFILE_FILE_VALUE[]} 列表数据
|
||||
*/
|
||||
'change',
|
||||
/**
|
||||
* 图片被删除时触发
|
||||
* @param {XUPLOADFILE_FILE_VALUE[]} 列表数据
|
||||
*/
|
||||
'delete',
|
||||
'update:modelValue'
|
||||
],
|
||||
props: {
|
||||
/**
|
||||
* 上传的类型,video,photo,默认是上传图片
|
||||
*/
|
||||
mode:{
|
||||
type:String,
|
||||
default:'photo'
|
||||
},
|
||||
/**
|
||||
* 当mode=video时,选择视频上传的参数,
|
||||
* 些参数为utsjson,所有字段为可选,具体字段为uni.chooseVideo(options)中的部分有效字段
|
||||
* pageOrientation,albumMode,compressed,compressed,maxDuration,camera
|
||||
*/
|
||||
videoOps:{
|
||||
type:Object as PropType<UTSJSONObject>,
|
||||
default:():UTSJSONObject =>{
|
||||
return {
|
||||
pageOrientation:'auto',
|
||||
albumMode:'system',
|
||||
sourceType:['album', 'camera'] as string[],
|
||||
compressed:true,
|
||||
maxDuration:60,
|
||||
camera:'back'
|
||||
} as UTSJSONObject
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 最大的可上传数量
|
||||
*/
|
||||
maxCount: {
|
||||
type: Number,
|
||||
default: 9
|
||||
},
|
||||
/**
|
||||
* 上传地址
|
||||
*/
|
||||
url: {
|
||||
type: String,
|
||||
default: "https://mockapi.eolink.com/LRViGGZ8e6c1e8b4a636cd82bca1eb15d2635ed8c74e774/admin/upload_pic/"
|
||||
},
|
||||
/**
|
||||
* 上传到服务器的名称字段
|
||||
*/
|
||||
name: {
|
||||
type: String,
|
||||
default: "file"
|
||||
},
|
||||
/**
|
||||
* 上传到服务器的头文件
|
||||
*/
|
||||
header: {
|
||||
type: Object as PropType<UTSJSONObject>,
|
||||
default: () : UTSJSONObject => {
|
||||
return {} as UTSJSONObject
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 额外的表单数据。
|
||||
*/
|
||||
formData: {
|
||||
type: Object as PropType<UTSJSONObject | null>,
|
||||
default: () : UTSJSONObject => {
|
||||
return {} as UTSJSONObject
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 图片高,此处不可使用%单位
|
||||
*/
|
||||
imgHeight: {
|
||||
type: String,
|
||||
default: "80"
|
||||
},
|
||||
/**
|
||||
* 一行显示几列
|
||||
*/
|
||||
column: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
/**
|
||||
* 上传成功的文件是否允许删除
|
||||
*/
|
||||
okFileIsDelete: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/**
|
||||
* 上传中的文件是否允许删除
|
||||
*/
|
||||
uploadingFileIsDelete: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/**
|
||||
* 等同v-model
|
||||
*/
|
||||
modelValue: {
|
||||
type: Array as PropType<XUPLOADFILE_FILE_VALUE[]>,
|
||||
default: () : XUPLOADFILE_FILE_VALUE[] => [] as XUPLOADFILE_FILE_VALUE[]
|
||||
},
|
||||
/**
|
||||
* 图片被删除时触发
|
||||
* 如果返回Promise<false>删除失败否则成功
|
||||
* 类型null|(index:number,item:XUPLOADFILE_FILE_INFO)=>Promise<boolean>
|
||||
*/
|
||||
beforeDel: {
|
||||
type: Function as PropType<funcalldel | null>,
|
||||
default: null
|
||||
},
|
||||
/**
|
||||
* 你需要原路返回参数提供的item
|
||||
* item可以自行修改响应内容,响应类型这样可以自己根据服务的内容判断
|
||||
* 是成功还是失败或者没有权限。示例见demo使用。
|
||||
*/
|
||||
beforeComplete:{
|
||||
type:Function as PropType<funbeforeCompelte|null>,
|
||||
default:null
|
||||
},
|
||||
/**
|
||||
* 上传前的最后一步执行,异步回调,函数返回一个item:XUPLOADFILE_FILE_INFO
|
||||
* 返回时要原样类型返回Promise<XUPLOADFILE_FILE_INFO>,你可以在这里修改文件参数跳过上传或者
|
||||
* 进行最后的头参数设置.demo有参考.
|
||||
*/
|
||||
beforeUpload:{
|
||||
type:Function as PropType<beforeUploadType|null>,
|
||||
default:null
|
||||
},
|
||||
/**
|
||||
* 是否自动上传
|
||||
*/
|
||||
autoStart: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/**
|
||||
* 图片来源同官方的sourceType:
|
||||
* 'album','camera'
|
||||
*/
|
||||
sourceType: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () : string[] => ['album', 'camera'] as string[]
|
||||
},
|
||||
/**
|
||||
* 是否压缩
|
||||
*/
|
||||
compress: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/**
|
||||
* 压缩后的缩放高,0表示不压缩高
|
||||
*/
|
||||
compressedHeight: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/**
|
||||
* 压缩后的缩放高,0表示不压缩宽
|
||||
*/
|
||||
compressedWidth: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/**
|
||||
* 压缩质量
|
||||
*/
|
||||
quality: {
|
||||
type: Number,
|
||||
default: 80
|
||||
},
|
||||
/**
|
||||
* 添加图片的位置
|
||||
* 'before'出现在前面
|
||||
* 'after'出现在后面
|
||||
*/
|
||||
addPos:{
|
||||
type:String,
|
||||
default:"after"
|
||||
},
|
||||
/**
|
||||
* 子项对齐方式,left左对齐默认,right:右对齐
|
||||
*/
|
||||
align:{
|
||||
type:String,
|
||||
default:"left"
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
_fileList() : XUPLOADFILE_FILE_INFO[] {
|
||||
return this.nowFileList
|
||||
},
|
||||
_imgHeight() : string {
|
||||
return checkIsCssUnit(this.imgHeight, xConfig.unit)
|
||||
},
|
||||
_okFileIsDelete() : boolean {
|
||||
return this.okFileIsDelete
|
||||
},
|
||||
_uploadingFileIsDelete() : boolean {
|
||||
return this.uploadingFileIsDelete
|
||||
},
|
||||
_itemHeight() : number {
|
||||
let item = this._imgHeight;
|
||||
let h = 0;
|
||||
if (item.lastIndexOf('rpx') > -1) {
|
||||
h = parseFloat(item.replace(/[rpx]/g, ''));
|
||||
h = rpx2px(h)
|
||||
} else {
|
||||
h = parseFloat(item.replace(/[px,%]/g, ''));
|
||||
}
|
||||
return h;
|
||||
},
|
||||
_borderColor() : string {
|
||||
if (xConfig.dark == 'dark') return xConfig.borderDarkColor
|
||||
return '#eeeeee';
|
||||
},
|
||||
_bgColor() : string {
|
||||
if (xConfig.dark == 'dark') return xConfig.inputDarkColor
|
||||
return '#f5f5f5';
|
||||
},
|
||||
_columnRatio() : string {
|
||||
return (100 / this.column).toString() + '%'
|
||||
},
|
||||
_formData() : UTSJSONObject | null {
|
||||
return this.formData
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue(newValue : XUPLOADFILE_FILE_VALUE[]) {
|
||||
if(this._fileList.length==0&&newValue.length==0) return;
|
||||
|
||||
if(newValue.length==0){
|
||||
this.xupload!.stop()
|
||||
this.xupload!.clear()
|
||||
this.nowFileList = []
|
||||
return;
|
||||
}
|
||||
this.xupload!.addFile(JSON.parseArray<XUPLOADFILE_FILE_VALUE>(JSON.stringify(newValue)!)!)
|
||||
clearTimeout(this.tid3)
|
||||
let t = this;
|
||||
this.tid3 = setTimeout(()=>{
|
||||
t.getNodeInfo()
|
||||
},200)
|
||||
},
|
||||
header() {
|
||||
this.setConfig()
|
||||
},
|
||||
maxCount() {
|
||||
this.setConfig()
|
||||
},
|
||||
url() {
|
||||
this.setConfig()
|
||||
},
|
||||
name() {
|
||||
this.setConfig()
|
||||
},
|
||||
formData() {
|
||||
this.setConfig()
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
let t = this;
|
||||
|
||||
this.setConfig()
|
||||
|
||||
this.xupload!.beforeUpload = (res : XUPLOADFILE_FILE_INFO) : Promise<XUPLOADFILE_FILE_INFO> => {
|
||||
let call = this.beforeUpload as beforeUploadType|null
|
||||
if(call==null||res.status==1||res.status==2||res.status==4||res.status==5){
|
||||
return Promise.resolve(toRaw(res) as XUPLOADFILE_FILE_INFO)
|
||||
}
|
||||
let realcall = call! as beforeUploadType
|
||||
|
||||
return realcall(toRaw(res) as XUPLOADFILE_FILE_INFO )
|
||||
}
|
||||
this.xupload!.addListenEvent('complete', (res : any) : Promise<any> => {
|
||||
let result = res as XUPLOADFILE_FILE_INFO[];
|
||||
|
||||
let clonelist = t.cloneListData(result)
|
||||
/**
|
||||
* 全部上传完时触发
|
||||
* @parsm {XUPLOADFILE_FILE_VALUE[]} result - 列表数据
|
||||
*/
|
||||
t.$emit('complete', clonelist)
|
||||
t.$emit("update:modelValue", clonelist)
|
||||
return Promise.resolve(res)
|
||||
})
|
||||
|
||||
|
||||
this.xupload!.beforeComplete = (res : XUPLOADFILE_FILE_INFO) : XUPLOADFILE_FILE_INFO => {
|
||||
let result = res as XUPLOADFILE_FILE_INFO;
|
||||
if(t.beforeComplete!=null){
|
||||
let bef = t.beforeComplete! as funbeforeCompelte
|
||||
result = bef(isProxy(result)?toRaw(result):result )
|
||||
}
|
||||
return result
|
||||
}
|
||||
this.xupload!.setChangeSync(function (res : XUPLOADFILE_FILE_INFO[]) {
|
||||
t.nowFileList = res;
|
||||
t._totalHeight = t.totalHeight()
|
||||
let clonelist = t.cloneListData(res)
|
||||
/**
|
||||
* 列表操作变化时触发,删除,上传成功并添加到列表中(选择图片,不上传不触发)
|
||||
* @parsm {XUPLOADFILE_FILE_INFO[]} result - 列表数据
|
||||
*/
|
||||
t.$emit('change', clonelist)
|
||||
t.$emit("update:modelValue", clonelist)
|
||||
|
||||
})
|
||||
if (this.modelValue.length > 0) {
|
||||
let clonelist = t.cloneListDataByValue(this.modelValue)
|
||||
this.xupload!.addFile(clonelist)
|
||||
}
|
||||
clearTimeout(this.tid3)
|
||||
this.tid3 = setTimeout(()=>{
|
||||
t.getNodeInfo()
|
||||
},200)
|
||||
uni.$on('onResize', this.resetInit)
|
||||
|
||||
|
||||
},
|
||||
beforeUnmount() {
|
||||
clearTimeout(this.tid3)
|
||||
uni.$off('onResize', this.resetInit)
|
||||
this.xupload = null;
|
||||
},
|
||||
methods: {
|
||||
cloneListData(listReal:XUPLOADFILE_FILE_INFO[]):XUPLOADFILE_FILE_VALUE[]{
|
||||
let list = listReal.map((el : XUPLOADFILE_FILE_INFO) : XUPLOADFILE_FILE_VALUE => {
|
||||
return {
|
||||
id: el.id,
|
||||
url: el.path,
|
||||
response: el.response
|
||||
} as XUPLOADFILE_FILE_VALUE
|
||||
})
|
||||
return JSON.parseArray<XUPLOADFILE_FILE_VALUE>(JSON.stringify(list)!)!
|
||||
},
|
||||
cloneListDataByValue(list:any):XUPLOADFILE_FILE_VALUE[]{
|
||||
return JSON.parseArray<XUPLOADFILE_FILE_VALUE>(JSON.stringify(list)!)!
|
||||
},
|
||||
setConfig() {
|
||||
this.xupload!.setConfig({
|
||||
autoUpload: this.autoStart,
|
||||
count: this.maxCount,
|
||||
header: this.header as UTSJSONObject,
|
||||
hostUrl: this.url,
|
||||
name: this.name,
|
||||
formData: this._formData,
|
||||
sourceType: this.sourceType,
|
||||
compress: this.compress,
|
||||
quality:this.quality,
|
||||
compressedWidth: this.compressedWidth == 0 ? null : this.compressedWidth,
|
||||
compressedHeight: this.compressedHeight == 0 ? null : this.compressedHeight
|
||||
} as XUPLOADFILE_INFO)
|
||||
},
|
||||
resetInit() {
|
||||
this.diffY = 0
|
||||
this.diffX = 0
|
||||
|
||||
let t = this;
|
||||
clearTimeout(this.tid3)
|
||||
this.tid3 = setTimeout(()=>{
|
||||
t.getNodeInfo()
|
||||
},200)
|
||||
},
|
||||
totalHeight() : number {
|
||||
let realItemHeight = this._itemHeight + this.spaceGap
|
||||
let row = Math.ceil(this.nowFileList.length / this.column);
|
||||
let height = row * realItemHeight
|
||||
let total = Math.max(height, realItemHeight)
|
||||
return total;
|
||||
},
|
||||
chooseFile() {
|
||||
this.xupload!.setVideoOps(this.videoOps,this.mode)
|
||||
this.xupload!.chooseMedia()
|
||||
},
|
||||
/**
|
||||
* 手动上传图片
|
||||
* @public
|
||||
*/
|
||||
upload() {
|
||||
this.xupload!.start()
|
||||
},
|
||||
getNodeInfo() {
|
||||
uni.createSelectorQuery().in(this)
|
||||
.select(".xUploadMedia")
|
||||
.boundingClientRect().exec((ret) => {
|
||||
let nodeinfo = ret[0] as NodeInfo
|
||||
|
||||
let space = (this.column - 1) * this.spaceGap;
|
||||
// #ifdef APP-IOS
|
||||
this.itemWidth = (nodeinfo.width! - space) / this.column - 0.28
|
||||
// #endif
|
||||
// #ifndef APP-IOS
|
||||
this.itemWidth = (nodeinfo.width! - space) / this.column
|
||||
// #endif
|
||||
this._totalHeight = this.totalHeight()
|
||||
})
|
||||
},
|
||||
getStatusColor(item : XUPLOADFILE_FILE_INFO) : string {
|
||||
if (item.status == 1) return 'rgba(99, 83, 4, 0.5)'
|
||||
if (item.status == 2) return 'rgba(0, 0, 0, 0.5)'
|
||||
if (item.status == 3 || item.status == 4 || item.status == 5) return 'rgba(115, 26, 4, 0.7)'
|
||||
return 'rgba(0, 0, 0, 0.5)'
|
||||
},
|
||||
getTextStatus(status : number) : string {
|
||||
// 0未上传,1上传中,2上传成功,3上传失败(网络错误),4已取消(终止,上到一半取消了),5超过大小
|
||||
let text = "";
|
||||
if (status == 0) {
|
||||
text = this!.i18n.t("tmui4x.uploadFile.uploadStatus",0);//"待上传"
|
||||
} else if (status == 3) {
|
||||
text = this!.i18n.t("tmui4x.uploadFile.uploadStatus",2);//"上传失败"
|
||||
} else if (status == 2) {
|
||||
text = this!.i18n.t("tmui4x.uploadFile.uploadStatus",3);//"上传成功"
|
||||
} else if (status == 1) {
|
||||
text = this!.i18n.t("tmui4x.uploadFile.uploadStatus",1);//"上传中..."
|
||||
} else if (status == 5) {
|
||||
text = this!.i18n.t("tmui4x.uploadFile.uploadStatus",4);//"超过大小"
|
||||
}
|
||||
|
||||
|
||||
return text
|
||||
},
|
||||
delRemove(item : XUPLOADFILE_FILE_INFO, index : number) {
|
||||
let _this = this;
|
||||
if (!this._okFileIsDelete) {
|
||||
// "不允许删除"
|
||||
uni.showToast({ title: this!.i18n.t("tmui4x.uploadFile.tips2"), mask: true, icon: 'none' })
|
||||
return;
|
||||
}
|
||||
if (item.status == 1 && !this._uploadingFileIsDelete) {
|
||||
// "上传中,不允许删除"
|
||||
uni.showToast({ title: this!.i18n.t("tmui4x.uploadFile.tips1"), mask: true, icon: 'none' })
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.beforeDel == null) {
|
||||
_this.xupload!.delFile(item.id)
|
||||
let clonelist = this.cloneListData(_this.xupload!.fileList)
|
||||
_this.$emit("update:modelValue", clonelist)
|
||||
return;
|
||||
}
|
||||
let call = this.beforeDel! as funcalldel
|
||||
call(index, item).then((res : boolean) => {
|
||||
if (res) {
|
||||
_this.xupload!.delFile(item.id)
|
||||
|
||||
let recorelist = _this.xupload!.fileList.slice(0)
|
||||
let clonelist = this.cloneListData(recorelist)
|
||||
_this.$emit('delete',clonelist)
|
||||
_this.$emit("update:modelValue", clonelist)
|
||||
|
||||
}
|
||||
}).catch(() => { })
|
||||
|
||||
},
|
||||
mStart(index : number) {
|
||||
this._nowIndex = index
|
||||
this._newIndex = index
|
||||
},
|
||||
mMove(evt : UniTouchEvent) {
|
||||
if (!this._isLongPressCanDisableMove) return;
|
||||
evt.stopPropagation()
|
||||
evt.preventDefault()
|
||||
let selfEle = this.$refs['xUploadMedia'] as UniElement
|
||||
let selfEeleBox = selfEle.getBoundingClientRect()
|
||||
let x = evt.changedTouches[0].clientX - selfEeleBox.left
|
||||
let y = evt.changedTouches[0].clientY - selfEeleBox.top
|
||||
|
||||
let moveItemel = this.$refs['moveItem'] as UniElement
|
||||
|
||||
let offset_x = (x - this.itemWidth / 2) + this.spaceGap / 2
|
||||
let offset_y = (y - this._itemHeight / 2) + this.spaceGap / 2
|
||||
|
||||
moveItemel.style.setProperty('left', offset_x.toString() + 'px')
|
||||
moveItemel.style.setProperty('top', offset_y.toString() + 'px')
|
||||
this.mouse_x = offset_x
|
||||
this.mouse_y = offset_y
|
||||
|
||||
this.tempXy(this.mouse_x, this.mouse_y)
|
||||
},
|
||||
mEnd(evt : UniTouchEvent) {
|
||||
if (!this._isLongPressCanDisableMove) return;
|
||||
this._isLongPressCanDisableMove = false;
|
||||
let maxxy = Math.max(Math.abs(evt.changedTouches[0].clientX - this.diffX), Math.abs(evt.changedTouches[0].clientY - this.diffY))
|
||||
|
||||
|
||||
this.movexitem_x = this.mouse_x
|
||||
this.movexitem_y = this.mouse_y
|
||||
|
||||
this.coverXy(maxxy > 10);
|
||||
|
||||
},
|
||||
// 将当前的x,y坐标转换为 对应的item的index的坐标。
|
||||
coverXy(isJiaoHuan : boolean) {
|
||||
// 计算最大行数
|
||||
let maxRow = Math.ceil((this.nowFileList.length) / this.column) - 1;
|
||||
// 当前理论值的列数
|
||||
let col = Math.ceil((this.movexitem_x - this.itemWidth / 2) / this.itemWidth)
|
||||
// 当前理论值的行数
|
||||
let row = Math.ceil((this.movexitem_y - this._itemHeight / 2) / this._itemHeight)
|
||||
// 比较不能让col越界超过最大值和最小值的列数
|
||||
col = Math.min(Math.max(col, 0), this.column - 1)
|
||||
// 比较不能让row越界超过最大值和最小值的行数
|
||||
row = Math.min(Math.max(row, 0), maxRow)
|
||||
// 理论值的index,类似一个网格中的x,y坐标
|
||||
let index = (col * row);
|
||||
// index不能越界超过最大文件长度值的索引
|
||||
index = Math.max(Math.min(0, index), this.nowFileList.length - 1)
|
||||
|
||||
let realCol = col;
|
||||
|
||||
// 理论值的col可能会超过实际的col,比如计算col为第3列,但这个值是最大值,并不是真实的最大值
|
||||
// 如果有两行,第2行只有1列,那其实col只能为1,而不是鼠标xy计算出的col
|
||||
if (row >= maxRow) {
|
||||
realCol = Math.min(col, (this.nowFileList.length - 1) % this.column)
|
||||
}
|
||||
let realRow = row
|
||||
let x = realCol * this.itemWidth - this.spaceGap * realCol
|
||||
let y = realRow * this._itemHeight - this.spaceGap * realRow
|
||||
// 经过比较得出真实的index值。
|
||||
this._newIndex = realCol + realRow * (this.column)
|
||||
|
||||
if (isJiaoHuan) {
|
||||
this.setItem()
|
||||
} else {
|
||||
this._nowIndex = -1
|
||||
this._newIndex = -1;
|
||||
}
|
||||
this.movexitem_x = x
|
||||
this.movexitem_y = y
|
||||
this.showMoveItem = false;
|
||||
this.tempMoveIndex = -1;
|
||||
},
|
||||
tempXy(mvx : number, mvy : number) {
|
||||
// 计算最大行数
|
||||
let maxRow = Math.ceil((this.nowFileList.length) / this.column) - 1;
|
||||
// 当前理论值的列数
|
||||
let col = Math.ceil((mvx - this.itemWidth / 2 - this.spaceGap) / this.itemWidth)
|
||||
// 当前理论值的行数
|
||||
let row = Math.ceil((mvy - this._itemHeight / 2) / this._itemHeight)
|
||||
// 比较不能让col越界超过最大值和最小值的列数
|
||||
col = Math.min(Math.max(col, 0), this.column - 1)
|
||||
// 比较不能让row越界超过最大值和最小值的行数
|
||||
row = Math.min(Math.max(row, 0), maxRow)
|
||||
// 理论值的index,类似一个网格中的x,y坐标
|
||||
let index = (col * row);
|
||||
// index不能越界超过最大文件长度值的索引
|
||||
index = Math.max(Math.min(0, index), this.nowFileList.length - 1)
|
||||
|
||||
let realCol = col;
|
||||
|
||||
// 理论值的col可能会超过实际的col,比如计算col为第3列,但这个值是最大值,并不是真实的最大值
|
||||
// 如果有两行,第2行只有1列,那其实col只能为1,而不是鼠标xy计算出的col
|
||||
if (row >= maxRow) {
|
||||
realCol = Math.min(col, (this.nowFileList.length - 1) % this.column)
|
||||
}
|
||||
let realRow = row
|
||||
|
||||
// 经过比较得出真实的index值。
|
||||
this.tempMoveIndex = realCol + realRow * (this.column)
|
||||
|
||||
},
|
||||
setItem() {
|
||||
// 新旧项目交换。
|
||||
let newIndexItem = this._fileList.slice(0)[this._newIndex]
|
||||
let oldindexItem = this._fileList.slice(0)[this._nowIndex]
|
||||
this._fileList.splice(this._newIndex, 1, oldindexItem)
|
||||
this._fileList.splice(this._nowIndex, 1, newIndexItem)
|
||||
this.$nextTick(function () {
|
||||
this.xupload!._addFilesByself(this._fileList)
|
||||
// 结束交换后,把索引归-1,即为初始状态值。
|
||||
this._nowIndex = -1
|
||||
this._newIndex = -1;
|
||||
let list = this._fileList.slice(0);
|
||||
let clonelist = this.cloneListData(list)
|
||||
// 最后同步到外层的排序列表。
|
||||
this.$emit("update:modelValue", clonelist)
|
||||
|
||||
})
|
||||
},
|
||||
getUploadIngStatus() : boolean {
|
||||
let UploadingOk = false;
|
||||
for (let i = 0; i < this._fileList.length; i++) {
|
||||
if (this._fileList[i].status == 1) {
|
||||
UploadingOk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return UploadingOk;
|
||||
},
|
||||
|
||||
mLongStart(evt : UniTouchEvent) {
|
||||
let t = this;
|
||||
if (this.getUploadIngStatus()) {
|
||||
// '上传中不允许拖动排序'
|
||||
uni.showToast({ title: this!.i18n.t("tmui4x.uploadMedia.tips1"), icon: 'none' })
|
||||
return;
|
||||
}
|
||||
this._isLongPressCanDisableMove = true;
|
||||
let selfEle = this.$refs['xUploadMedia'] as UniElement
|
||||
let selfEeleBox = selfEle.getBoundingClientRect()
|
||||
let x = evt.changedTouches[0].clientX
|
||||
let y = evt.changedTouches[0].clientY
|
||||
|
||||
this.mouse_x = x - selfEeleBox.left
|
||||
this.mouse_y = y - selfEeleBox.top
|
||||
this.diffY = y
|
||||
this.diffX = x
|
||||
let offset_x = (this.mouse_x - this.itemWidth / 2) + this.spaceGap
|
||||
let offset_y = (this.mouse_y - this._itemHeight / 2) + this.spaceGap
|
||||
|
||||
this.movexitem_x = offset_x
|
||||
this.movexitem_y = offset_y
|
||||
this.$nextTick(function () {
|
||||
t.showMoveItem = true;
|
||||
})
|
||||
},
|
||||
imgClickPrve(index : number) {
|
||||
let urls = this.nowFileList.map((el) : string => el.path);
|
||||
if(this.mode == 'photo'){
|
||||
uni.previewImage({
|
||||
current: index,
|
||||
urls
|
||||
})
|
||||
}else if(this.mode == 'video'){
|
||||
this.nowshowClickMedioSrc = this.nowFileList[index].path;
|
||||
this.$nextTick(()=>{
|
||||
this.showVideo = true;
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<view>
|
||||
<view @longpress="mLongStart" @touchmove="mMove" @touchend="mEnd" @touchcancel="mEnd"
|
||||
ref="xUploadMedia"
|
||||
class="xUploadMedia"
|
||||
:style="{justifyContent: align=='left'?'flex-start':'flex-end'}"
|
||||
>
|
||||
<view v-for="(item,index) in _fileList" :key="index" :style="{width:_columnRatio}"
|
||||
class="xUploadMediaItemParent">
|
||||
<view class="xUploadMediaItem" @click="imgClickPrve(index)" @touchstart="mStart(index)" :style="{
|
||||
height:_imgHeight,
|
||||
marginRight:((index+1)%column)==0?'0px':spaceGap+'px',
|
||||
marginBottom:spaceGap+'px'
|
||||
}">
|
||||
<view @click.stop="delRemove(item,index)" class="xUploadMediaClose" >
|
||||
<x-icon color="rgba(255,255,255,0.6)" font-size="18" name="close-line"></x-icon>
|
||||
</view>
|
||||
<image v-if="mode=='photo'" class="xUploadMediaImg" :src="item.path" :key="item.id"></image>
|
||||
<video v-if="mode=='video'" object-fit='fill' :enable-progress-gesture="false" :show-loading="false" :show-progress="false" :show-fullscreen-btn="false" :show-play-btn="false" class="xUploadMediaImg" width="100%" height="100%" :src="item.path" :key="item.id"></video>
|
||||
<view class="xUploadMediaLabel" :style="{backgroundColor:getStatusColor(item)}">
|
||||
<text class="xUploadMediaLabelText">
|
||||
<!-- {{item.statusText}} -->
|
||||
{{getTextStatus(item.status)}}
|
||||
</text>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- hover -->
|
||||
<view v-if="tempMoveIndex==index" class="xUploadMediaItemHover"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="_fileList.length < maxCount " @click="chooseFile" class="xUploadMediaItem"
|
||||
:style="{width:itemWidth+'px',height:_imgHeight}">
|
||||
<!--
|
||||
@slot 上传图片按钮插槽。
|
||||
-->
|
||||
<slot>
|
||||
<view class="xUploadMediaAddSlot"
|
||||
:style="{border: `1px solid ${_borderColor}`,'backgroundColor': _bgColor}">
|
||||
<x-icon font-size="36" color="#d6d6d6" dark-color="#4a4a4a" name="add-line"></x-icon>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
<!-- 备用移动块。 -->
|
||||
<!-- transitionDuration:_isLongPressCanDisableMove?'0ms':'200ms', -->
|
||||
<view ref="moveItem" class="xUploadMediaMoveitem" v-show="_nowIndex>-1"
|
||||
:style="{
|
||||
left:movexitem_x+'px',top:movexitem_y+'px',width:itemWidth+'px',height:_imgHeight,marginRight:((_nowIndex+1)%column)==0?'0px':spaceGap+'px',marginBottom:spaceGap+'px'}">
|
||||
<view v-if="showMoveItem"
|
||||
:style="{backgroundColor:'rgba(0,0,0,0.5)',position: 'relative',width:'100%',height:'100%'}">
|
||||
<view class="xUploadMediaClose">
|
||||
<x-icon style="opacity: 0.5;" color="white" font-size="18" name="close-line"></x-icon>
|
||||
</view>
|
||||
<x-image v-if="mode=='photo'" style="opacity: 0.5;" width="100%" height="100%" :src="_fileList[_nowIndex].path"
|
||||
:key="_fileList[_nowIndex].id"></x-image>
|
||||
<view :style="{opacity: 0.5,backgroundColor:getStatusColor(_fileList[_nowIndex])}" class="xUploadMediaLabel">
|
||||
<text class="xUploadMediaLabelText">
|
||||
<!-- {{_fileList[_nowIndex].statusText}} -->
|
||||
{{getTextStatus(_fileList[_nowIndex].status)}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<!-- 视频预览 关闭-->
|
||||
<x-modal :title="i18n!.t('tmui4x.uploadMedia.videoPreview')"
|
||||
:confirm-text="i18n!.t('tmui4x.uploadMedia.close')" v-model:show="showVideo" height="360" :showCancel="false" :overlay-click="false" :show-close="false" >
|
||||
<video :src="nowshowClickMedioSrc" style="width: 100%;height: 200px;" v-if="nowshowClickMedioSrc!=''&&showVideo"></video>
|
||||
</x-modal>
|
||||
</view>
|
||||
</template>
|
||||
<style scoped>
|
||||
.xUploadMediaAddSlot {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
/* border: 1px solid #eeeeee; */
|
||||
/* background-color: #f5f5f5; */
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.xUploadMediaImg {
|
||||
pointer-events: none;
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.xUploadMediaMoveitem {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
z-index: 8;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.xUploadMediaLabel {
|
||||
position: absolute;
|
||||
left: 0rpx;
|
||||
bottom: 0rpx;
|
||||
z-index: 5;
|
||||
width: 100%;
|
||||
padding: 4px 6px;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
/* #ifdef WEB || MP-WEIXIN */
|
||||
box-sizing:border-box;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.xUploadMediaLabelText {
|
||||
lines: 1;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 10px;
|
||||
color: rgba(255,255,255,0.8);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.xUploadMediaClose {
|
||||
position: absolute;
|
||||
right: 0rpx;
|
||||
top: 0rpx;
|
||||
padding:0px;
|
||||
width:20px;
|
||||
height:20px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 5;
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
|
||||
.xUploadMediaItemParent {
|
||||
position: relative;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.xUploadMediaItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 6px;
|
||||
position: relative;
|
||||
/* #ifdef WEB || MP-WEIXIN */
|
||||
overflow:hidden;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.xUploadMedia {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
align-items: flex-start;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.xUploadMediaItemHover {
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 3px solid #1C84FF;
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user