Переглянути джерело

app2.0上传文件

娄底高职分支
ndbs 2 роки тому
джерело
коміт
c23bfac611
4 змінених файлів з 704 додано та 72 видалено
  1. +112
    -55
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-app/upload-file.vue
  2. +164
    -17
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-mpui/learun-ui-mp/upload.vue
  3. +217
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/upload-file.vue
  4. +211
    -0
      Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/upload-file2.vue

+ 112
- 55
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-app/upload-file.vue Переглянути файл

@@ -9,7 +9,8 @@

<view class="cu-form-group" style="border-top: none;">
<view class="grid col-4 grid-square flex-sub">
<view v-for="(file, index) in value" :key="index" class="bg-img">
<view v-for="(file, index) in value" :key="index" class="bg-img" style="position: relative;">
<view v-if="file.noUpdated" class="mask"></view>
<image
v-if="isImgFile(file.type)"
@click="fileClick(index)"
@@ -20,11 +21,14 @@
<view v-else-if="isDocFile(file.type)" @click="fileClick(index)" class="file-icon solids">
<l-icon type="text" />
</view>
<view v-else class="file-icon solids"><l-icon type="text" /></view>
<view v-else class="file-icon solids" @click="fileClick(index)"><l-icon type="text" /></view>

<view v-if="!readonly" @click.stop="delFile(index)" class="cu-tag bg-red" style="height: 24px; width: 24px;">
<view v-if="!readonly" @click.stop="delFile(index,file.uid)" class="cu-tag bg-red" style="height: 24px; width: 24px;">
<l-icon type="close" color="white" style="width: 18px; height: 24px; font-size: 24px;" />
</view>
<view class="fileName">
<text>{{file.name}}</text>
</view>
</view>

<view v-if="!readonly && value.length < Number(number)" @click="chooseFile" class="solids">
@@ -54,8 +58,7 @@ export default {

isImgFile(type) {
const typeString = (type || '').toLowerCase()
//return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'image'].includes(typeString)
return true;
return ['jpg','image/jpg','jpeg','image/jpeg', 'png', 'image/png','gif', 'image/gif','bmp', 'image/bmp','webp', 'image/webp','image'].includes(typeString)
},

isDocFile(type) {
@@ -63,12 +66,17 @@ export default {
return ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'].includes(typeString)
},

delFile(index) {
async delFile(index,fileId) {
if (!(await this.CONFIRM('删除文件', '确定要删除该文件吗?', true))) {
return
}
const newList = JSON.parse(JSON.stringify(this.value))
newList.splice(index, 1)
this.$emit('input', newList)
this.$emit('change')
this.$emit('del')
//物理删除
this.DELETE_FILE(fileId);
},

chooseFile() {
@@ -89,60 +97,84 @@ export default {
// #endif

// #ifndef MP-DINGTALK
uni.chooseImage({
count: Number(this.number),
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: ({ tempFilePaths }) => {
const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
tempFilePaths//.map(t => ({ path: t, type: this.getFileExt(t) }))
)
this.$emit('input', newList)
this.$emit('change', newList)
this.$emit('add')
}
})
// uni.chooseFile({
// count: Number(this.number),
// sizeType: ['original', 'compressed'],
// sourceType: ['album', 'camera'],
// success: ({ tempFilePaths }) => {
// const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
// tempFilePaths//.map(t => ({ path: t, type: this.getFileExt(t) }))
// )
// this.$emit('input', newList)
// this.$emit('change', newList)
// this.$emit('add')
// }
// })
uni.chooseFile({
count: Number(this.number),
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: ({ tempFilePaths,tempFiles }) => {
const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
// tempFilePaths//.map(t => ({ path: t, type: this.getFileExt(t) }))
tempFilePaths.map((t,i) => ({ path: t, type: tempFiles[i].type, size:tempFiles[i].size, name:tempFiles[i].name, noUpdated:true} ))
)
this.$emit('input', newList)
this.$emit('change', newList)
this.$emit('add')
}
})
// #endif
},

async fileClick(index) {
// if(typeof this.value[index] == "string"){
// uni.previewImage({ urls: this.value.filter(item=>typeof this.value[index] == "string"), current: this.value[index] })
// return
// }
const { path, type, uid, size = 0 } = this.value[index]
if (this.isImgFile(type)) {
uni.previewImage({ urls: [path], current: path })
} else if (this.isDocFile(type)) {
// #ifndef H5 || MP-DINGTALK
if (size >= 50 * 1024 * 1024) {
this.TOAST('小程序端无法下载超过50MB的文件,此文件大小为${size}KB,超过限制')
return
}
// #endif

// #ifndef MP-DINGTALK
const tempFilePath = await this.HTTP_DOWNLOAD(uid)
uni.openDocument({ filePath: tempFilePath, fileType: type })
// #endif

// #ifdef MP-DINGTALK
this.TOAST('钉钉小程序只支持查看图片文件')
// #endif
} else {
// #ifndef MP-DINGTALK
this.TOAST('小程序端只支持打开图片和文档(word、pdf等)文件')
// #endif

// #ifdef MP-DINGTALK
this.TOAST('钉钉小程序只支持查看图片文件')
// #endif

// #ifdef APP-VUE
const tempFilePath = await this.HTTP_DOWNLOAD(uid)
uni.openDocument({ filePath: tempFilePath, fileType: type })
// #endif

// #ifdef H5
await this.HTTP_DOWNLOAD(uid)
// #endif
}
if (this.isImgFile(type)) {
uni.previewImage({ urls: [path], current: path })
}else{
uni.openDocument({ filePath: path, fileType: type })
}
// if (this.isImgFile(type)) {
// uni.previewImage({ urls: [path], current: path })
// } else if (this.isDocFile(type)) {
// // #ifndef H5 || MP-DINGTALK
// if (size >= 50 * 1024 * 1024) {
// this.TOAST('小程序端无法下载超过50MB的文件,此文件大小为${size}KB,超过限制')
// return
// }
// // #endif

// // #ifndef MP-DINGTALK
// const tempFilePath = await this.HTTP_DOWNLOAD(uid)
// uni.openDocument({ filePath: tempFilePath, fileType: type })
// // #endif

// // #ifdef MP-DINGTALK
// this.TOAST('钉钉小程序只支持查看图片文件')
// // #endif
// } else {
// // #ifndef MP-DINGTALK
// this.TOAST('小程序端只支持打开图片和文档(word、pdf等)文件')
// // #endif

// // #ifdef MP-DINGTALK
// this.TOAST('钉钉小程序只支持查看图片文件')
// // #endif

// // #ifdef APP-VUE
// const tempFilePath = await this.HTTP_DOWNLOAD(uid)
// uni.openDocument({ filePath: tempFilePath, fileType: type })
// // #endif

// // #ifdef H5
// await this.HTTP_DOWNLOAD(uid)
// // #endif
// }
}
}
}
@@ -153,4 +185,29 @@ export default {
line-height: 100%;
position: static;
}
.fileName{
padding: 2px 2px;
margin-bottom: 2px;
text-align: center;
position: absolute;
bottom: 0px;
width: 100%;
background: rgba(0,0,0,0.2);
color: #fff;
font-size: 12px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.mask{
position: absolute;
top: 0;left: 0;
width: 100%;
height: 100%;
// background: rgba(255,252,153,0.2);
background: rgba(0,0,0,0.7);
z-index: 100;
pointer-events: none;
}
</style>

+ 164
- 17
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/learun-mpui/learun-ui-mp/upload.vue Переглянути файл

@@ -2,26 +2,27 @@
<view class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view
v-for="(path, index) in value"
@tap="viewImg"
v-for="(item, index) in imgList"
@tap="viewImg(index)"
:key="index"
class="bg-img"
>
<!-- {{item.id?CONFIG('webHost')+item.url:item.url}} -->
<image
v-if="showfile()"
:src="path.path ? path.path : path"
:src="item.id?CONFIG('webHost')+item.url:item.url"
mode="aspectFill"
></image>
<view v-if="!readonly" @tap.stop="delImg(index)" class="cu-tag bg-red">
<view v-if="!readonly" @tap.stop="delImg(index)" class="cu-tag bg-red" style="width: 18px; height: 18px; font-size: 24px">
<l-icon
type="close"
style="width: 18px; height: 24px; font-size: 24px"
style="width: 18px; height: 18px; font-size: 12px"
/>
</view>
</view>

<view
v-if="!readonly && value.length < Number(number)"
v-if="!readonly && imgList.length < Number(number)"
@tap="chooseImg"
class="solids"
>
@@ -40,44 +41,190 @@ export default {
readonly: {},
value: { default: () => [] },
},
data(){
return{
imgList:[],
}
},

methods: {
delImg(index) {
const newList = JSON.parse(JSON.stringify(this.value));
const newList = JSON.parse(JSON.stringify(this.imgList));
newList.splice(index, 1);
this.imgList = newList
this.$emit("update:value", newList);
this.$emit("input", newList);
this.$emit("change");
this.$emit("del");
},
showfile() {
console.log(this.value);
return true;
},

chooseImg() {
uni.chooseImage({
count: Number(this.number),
count: (Number(this.number) - this.imgList.length),
sizeType: ["original", "compressed"],
sourceType: ["album", "camera"],
success: ({ tempFilePaths }) => {
const newList = JSON.parse(JSON.stringify(this.value || [])).concat(
tempFilePaths
success: (res) => {
let {tempFilePaths,tempFiles} = res
if(!this.validate(tempFiles))return
const newList = JSON.parse(JSON.stringify(this.imgList || [])).concat(
tempFilePaths.map(item=>{
return{
url:item,
id:''
}
})
);
//this.$parent.HTTP_UPLOAD(tempFilePaths[0]);
this.imgList = newList
this.$emit("update:value", newList);
this.$emit("input", newList);
this.$emit("change");
this.$emit("change",newList);
this.$emit("add");
},
});
},
uploadImage(){
// 单图上传
return new Promise(async (reslove,reject)=>{
let hasNotUpdatedList = this.imgList.every(item=>item.id)
if(hasNotUpdatedList){
reslove(this.imgList)
}
if(this.imgList.length){
var postData = {
Base64Url: await this.imgToBase64(this.imgList[0].url)
}
this.HTTP_POST('StuInfoFresh/savePhoto', postData, '图片上传失败!').then((data) => {
if (data) {
reslove([{
url:data.Url,
id:data.AnnexesFileId
}])
}else{
reject('图片上传失败!')
}
})
}else{
reslove("")
}
})
},
/**
* @description 控制promise.all并发数量
* @param limit 并发数
* @param array 参数列表
* @param apiFn 执行函数
* @returns {Promise<Awaited<unknown>[]>}
*/
async promiseAllLimit(limit, array, apiFn) {
const ret = [] // 用于存放所有的promise实例
const executing = [] // 用于存放目前正在执行的promise
for (const item of array) {
const p = apiFn(item)
ret.push(p)
if (limit <= array.length) {
// then回调中,当这个promise状态变为fulfilled后,将其从正在执行的promise列表executing中删除
const e = p.then(() => executing.splice(executing.indexOf(e), 1))
executing.push(e)
if (executing.length >= limit) {
// 一旦正在执行的promise列表数量等于限制数,就使用Promise.race等待某一个promise状态发生变更,
// 状态变更后,就会执行上面then的回调,将该promise从executing中删除,
// 然后再进入到下一次for循环,生成新的promise进行补充
await Promise.race(executing)
}
}
}
return Promise.all(ret)
},
imgToBase64(url){
return new Promise((resolve,reject)=>{
if(!url){
resolve("")
}
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
img = new Image;
img.crossOrigin = 'Anonymous';
img.onload = function () {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL('image/png');
canvas = null;
resolve(dataURL)
};
img.src = url;
})
},
validate(array){
// let type = array.every(item=>{
// return item.type && item.type.substring(0,6) == "image/"
// })
// if(!type){
// this.TOAST('文件类型错误');
// return false
// }
let size = array.every(item=>{
return item.size && item.size <= 100 * 1024 * 1024
})
if(!size){
this.TOAST('文件大小不得超过100M');
return false
}
return true
},

viewImg(index) {
uni.previewImage({
urls: this.value,
current: this.value[index],
urls: this.imgList.map(item=>item.id?this.CONFIG('webHost')+item.url:item.url),
current: this.imgList[index].id?this.CONFIG('webHost')+this.imgList[index].url:this.imgList[index].url,
});
},
// previewFile() {
// var file = document.querySelector('input[type=file]').files[0];
// var reader = new FileReader();
// // fileReader.readAsDataURL(blob);
// // fileReader.onerror = () => {
// // reject(new Error('blobToBase64 error'));
// // };
// // var encodedData = window.btoa("Hello, world");
// reader.onloadend = function () {
// //$('#PhotoImg').attr('src', reader.result);
// var postData = {
// Base64Url: reader.result
// }
// this.HTTP_POST(config.webapi + "StuInfoFresh/savePhoto", postData, (data) => {
// if (data) {
// $('#Photo').val(data.AnnexesFileId);
// $('#PhotoImg').attr('src', config.web + data.Url);
// } else {
// learun.layer.toast('采集照片信息失败!');
// }
// });
// }
// if (file) {
// reader.readAsDataURL(file);
// }
// },
},
created() {
console.log(123)
this.imgList = JSON.parse(JSON.stringify(this.value.map(item=>{
item.isUploaded = true
return item
})))
// [{url:"",id:""}]
}
};
</script>

+ 217
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/upload-file.vue Переглянути файл

@@ -0,0 +1,217 @@
<template>
<view>
<view class="cu-form-group" style="border-bottom: none; padding-bottom: 0;">
<view class="title">
<text v-if="required" class="lr-required">*</text>
{{ title || '' }}
</view>
</view>
<view class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view v-for="(item, index) in imgList" @tap="viewImg(item)" :key="index" class="bg-img">
<image v-if="showfile()&&item&&(typeof item == 'string'||isImage(item.type))" :src="item.url?CONFIG('webHost') + item.url:item" mode="aspectFill">
</image>
<l-icon v-if="showfile()&&!isImage(item.type)" type="text" />
<text class="file-name">{{item.name}}</text>
<view v-if="!readonly" @tap.stop="delImg(index)" class="cu-tag bg-red"
style="width: 18px; height: 18px; font-size: 24px">
<l-icon type="close" style="width: 18px; height: 18px; font-size: 12px" />
</view>
</view>

<view v-show="!readonly && imgList.length < Number(number)&&isShow" class="solids">
<l-icon type="file" />
<lsj-upload ref="lsjUpload" height="80px" width="100%" :size="20" :option="{}" :count="1"
@change="chooseChange" style="opacity: 0;"></lsj-upload>
</view>

</view>
</view>
</view>
</template>

<script>
/**
* 附件上传组件
* 直接选择手机文件
*
* 注意:可以是图片、文档、等;
*/
export default {

props: {
number: {
default: 1
},
readonly: {},
value: {
default: () => []
},
folderId: {},
title: {},
required: {}
},

data() {
return {
isShow: false,
imgList: [],
}
},

methods: {
chooseChange(files) {
let array = Array.from(files);
if (array.length) {
this.$refs.lsjUpload.clear()
}
let tempFilePaths = [],
tempFiles = [];
array.forEach(item => {
tempFilePaths.push(item[1].path)
tempFiles.push(item[1].file)
})
this.chooseChangeback(tempFilePaths, tempFiles)
},
delImg(index) {
this.CONFIRM("", "是否确认删除?", true).then(res => {
if(!res)return
this.LOADING('正在删除…');
const newList = JSON.parse(JSON.stringify(this.imgList));
this.HTTP_POST('/learun/adms/annexes/wxdelete', this.imgList[index].id, "文件删除失败").then((data) => {
this.HIDE_LOADING();
if (data) {
newList.splice(index, 1);
this.imgList = newList

this.$emit("update:value", newList);
this.$emit("input", newList);
this.$emit("change");
this.$emit("del");
}
})
})

},
showfile() {
return true;
},

async chooseChangeback(tempFilePaths, tempFiles) {
let uploadImageRes = await this.uploadImage(tempFilePaths[0], tempFiles[0] ? tempFiles[0].name : "")
let newList = this.imgList || []
if (uploadImageRes) {
//请求‘获取附件列表’接口
let data = await this.FETCH_FILEList(uploadImageRes);
if(data){
newList = data.map(t=>({
id: t.F_Id,
name: t.F_FileName,
url: t.F_FilePath.substring(t.F_FilePath.indexOf('Resource')),
type: t.F_FileType,
folderid:t.F_FolderId,
}))
}
}

this.imgList = newList

this.$emit("update:value", newList);
this.$emit("input", newList);
this.$emit("change", newList);
},

uploadImage(url, name) {
if (!url) return
// 文件上传
return new Promise(async (reslove, reject) => {
this.LOADING('正在上传…');
let params = name ? {
folderId: this.folderId,
name
} : {
folderId: this.folderId
}
this.HTTP_UPLOAD2('/learun/adms/annexes/wxuploadinsingle', url, params).then((data) => {
this.HIDE_LOADING();
this.$refs.lsjUpload.show()
if (data) {
reslove(data)
} else {
reject('上传失败!')
}
})
})

},
validate(array) {
// let type = array.every(item=>{
// return item.type && item.type.substring(0,6) == "image/"
// })
// if(!type){
// this.TOAST('文件类型错误');
// return false
// }
let size = array.every(item => {
return item.size && item.size <= 200 * 1024 * 1024
})
if (!size) {
this.TOAST('文件大小不得超过200M');
return false
}
return true
},

isImage(type) {
if (type && type.length) {
return ["png", "jpg"].includes(type.substring(type.length - 3, type.length))
} else {
return false
}
},

viewImg(item) {
if (!this.isImage(item.type)) {
window.location.href = this.CONFIG("webHost") + item.url
} else {
uni.previewImage({
urls: [this.CONFIG('webHost') + item.url],
current: this.CONFIG('webHost') + item.url
});
}
},
},
created() {
// console.log(this.value)
this.imgList = JSON.parse(JSON.stringify(this.value.map(item => {
return {
id: item.F_Id,
name: item.F_FileName,
url: item.F_FilePath.substring(item.F_FilePath.indexOf('Resource')),
type: item.F_FileType,
folderid:item.F_FolderId,
}
})))
this.$nextTick(() => {
this.isShow = true
})
}
};
</script>

<style scoped>
.file-name {
position: absolute;
bottom: 0;
width: 100%;
color: #606266;
font-size: 12px;
text-align: center;
background-color: rgba(255, 255, 255, 0.6);

text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
</style>

+ 211
- 0
Learun.Framework.Ultimate V7/LearunApp-2.2.0/components/upload-file2.vue Переглянути файл

@@ -0,0 +1,211 @@
<template>
<view>
<view class="cu-form-group" style="border-bottom: none; padding-bottom: 0;">
<view class="title">
<text v-if="required" class="lr-required">*</text>
{{ title || '' }}
</view>
</view>
<view class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view v-for="(item, index) in imgList" @tap="viewImg(item)" :key="index" class="bg-img">
<image v-if="showfile()&&item&&(typeof item == 'string'||isImage(item.type))" :src="item.url?CONFIG('webHost') + item.url:item" mode="aspectFill">
</image>
<l-icon v-if="showfile()&&!isImage(item.type)" type="text" />
<text class="file-name">{{item.name}}</text>
<view v-if="!readonly" @tap.stop="delImg(index)" class="cu-tag bg-red"
style="width: 18px; height: 18px; font-size: 24px">
<l-icon type="close" style="width: 18px; height: 18px; font-size: 12px" />
</view>
</view>
<view v-if="!readonly && imgList.length < Number(number)" @click="chooseFile" class="solids">
<l-icon type="file" />
</view>
</view>
</view>
</view>

</template>

<script>
/**
* 附件上传组件
* 使用相机拍摄 和 从相册选图
*
* 注意:可以选择图片;如果选择文档的话则是从手机的‘文件管理’里面去选择,有可能会找不到文档;
*/
export default {

props: {
number: {
default: 1
},
readonly: {},
value: {
default: () => []
},
folderId: {},
title: {},
required: {}
},

data() {
return {
isShow: false,
imgList: [],
}
},

methods: {
chooseFile() {
uni.chooseImage({
count: Number(this.number),
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: ({ tempFilePaths,tempFiles }) => {
this.chooseChangeback(tempFilePaths,tempFiles)
}
})
},
delImg(index) {
this.CONFIRM("", "是否确认删除?", true).then(res => {
if(!res)return
this.LOADING('正在删除…');
const newList = JSON.parse(JSON.stringify(this.imgList));
this.HTTP_POST('/learun/adms/annexes/wxdelete', this.imgList[index].id, "文件删除失败").then((data) => {
this.HIDE_LOADING();
if (data) {
newList.splice(index, 1);
this.imgList = newList

this.$emit("update:value", newList);
this.$emit("input", newList);
this.$emit("change");
this.$emit("del");
}
})
})

},
showfile() {
return true;
},

async chooseChangeback(tempFilePaths, tempFiles) {
let uploadImageRes = await this.uploadImage(tempFilePaths[0], tempFiles[0] ? tempFiles[0].name : "")
let newList = this.imgList || []
if (uploadImageRes) {
//请求‘获取附件列表’接口
let data = await this.FETCH_FILEList(uploadImageRes);
if(data){
newList = data.map(t=>({
id: t.F_Id,
name: t.F_FileName,
url: t.F_FilePath.substring(t.F_FilePath.indexOf('Resource')),
type: t.F_FileType,
folderid:t.F_FolderId,
}))
}
}

this.imgList = newList

this.$emit("update:value", newList);
this.$emit("input", newList);
this.$emit("change", newList);
},

uploadImage(url, name) {
if (!url) return
// 文件上传
return new Promise(async (reslove, reject) => {
this.LOADING('正在上传…');
let params = name ? {
folderId: this.folderId,
name
} : {
folderId: this.folderId
}
this.HTTP_UPLOAD2('/learun/adms/annexes/wxuploadinsingle', url, params).then((data) => {
this.HIDE_LOADING();
if (data) {
reslove(data)
} else {
reject('上传失败!')
}
})
})

},
validate(array) {
// let type = array.every(item=>{
// return item.type && item.type.substring(0,6) == "image/"
// })
// if(!type){
// this.TOAST('文件类型错误');
// return false
// }
let size = array.every(item => {
return item.size && item.size <= 200 * 1024 * 1024
})
if (!size) {
this.TOAST('文件大小不得超过200M');
return false
}
return true
},

isImage(type) {
if (type && type.length) {
return ["png", "jpg"].includes(type.substring(type.length - 3, type.length))
} else {
return false
}
},

viewImg(item) {
if (!this.isImage(item.type)) {
window.location.href = this.CONFIG("webHost") + item.url
} else {
uni.previewImage({
urls: [this.CONFIG('webHost') + item.url],
current: this.CONFIG('webHost') + item.url
});
}
},
},
created() {
// console.log(this.value)
this.imgList = JSON.parse(JSON.stringify(this.value.map(item => {
return {
id: item.F_Id,
name: item.F_FileName,
url: item.F_FilePath.substring(item.F_FilePath.indexOf('Resource')),
type: item.F_FileType,
folderid:item.F_FolderId,
}
})))
this.$nextTick(() => {
this.isShow = true
})
}
};
</script>

<style scoped>
.file-name {
position: absolute;
bottom: 0;
width: 100%;
color: #606266;
font-size: 12px;
text-align: center;
background-color: rgba(255, 255, 255, 0.6);

text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
</style>

Завантаження…
Відмінити
Зберегти