875 lines
26 KiB
Plaintext
875 lines
26 KiB
Plaintext
<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> |