merchapp/java-mall-app-shop-admin/components/poiuy-uImgUpload/imgUpload.vue
2025-07-28 01:55:47 +08:00

951 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
项目图片上传插件
时间2020.9.24
备注接口的返回值是否符合插件所写的code=0如果不是请复制 upload asyncUpload 查找该方法 在注释的地方修改
-->
<template>
<view class="w-100 imgupload">
<view class="w-100 flex_wrap">
<view
:class="single ? 'single-css' : 'imgs-view'"
v-for="(v, i) in imgArray"
:key="i"
>
<image @click="preview(v, i)" :src="v.url"></image>
<view
v-if="!disabled"
:class="conf.style ? 'del-btn-style-' + conf.style : 'del-btn'"
@click="delImg(i)"
>
<image v-if="conf.delIcon" :src="conf.delIcon"></image>
<text v-if="!conf.delIcon && conf.style == 1" class="icon_upload">
&#xe785;
</text>
<text v-if="!conf.delIcon && conf.style == 2" class="icon_upload">
&#xe785;
</text>
</view>
<view
class="uploading flex_xy_center"
:class="single ? 'single-css' : 'imgs-view'"
v-if="!v.upload && loading && startUpload"
>
<view>
<image v-if="conf.loadIcon" :src="conf.loadIcon"></image>
<text v-if="!conf.loadIcon && conf.style == 1" class="icon_upload">
&#xe6a0;
</text>
<text v-if="!conf.loadIcon && conf.style == 2" class="icon_upload">
&#xe616;
</text>
<view class="upload-txt">{{ conf.loadText || "上传中..." }}</view>
</view>
</view>
<view class="result" v-if="!conf.style && conf.resultTip && v.upload">
<label class="success" v-if="v.result === true">上传成功</label>
<label class="error" v-if="v.result === false">上传失败</label>
</view>
<view
v-if="conf.style == 1 && conf.resultTip && v.upload && !disabled"
class="result-style"
:class="'result-' + (v.result ? 1 : 2)"
>
<view v-if="v.result == true">
<text class="icon_upload">&#xe666;</text>
上传成功
</view>
<view v-if="v.result == false">
<text class="icon_upload">&#xe785;</text>
上传失败
</view>
</view>
</view>
<view
v-if="imgArray.length < imgCount && !disabled"
:style="conf.borderStyle"
class="flex_xy_center"
:class="single ? 'single-add' : 'upload-img-view'"
@click="upPhoto"
>
<text v-if="conf.addStyle == '1'" class="icon_upload">&#xe615;</text>
<text v-if="conf.addStyle == '2'" class="icon_upload add">
&#xe606;
</text>
</view>
</view>
<view v-if="!closeTip && !tipObj.prompt" class="imgupload__tip">
* 最多上传{{ imgCount }}张图片(
<label>{{ imgArray.length }}</label>
/{{ imgCount }})
</view>
<view
class="imgupload__tip"
:style="{ color: tipObj.typeColor }"
v-show="tipObj.prompt || tipObj.must"
>
{{ tipObj.prompt }}
</view>
</view>
</template>
<script>
export default {
name: "imgUpload",
props: {
imgArr: {
//图片数组
type: [Array],
},
uploadImgCount: {
//一次上传图片数
type: String,
default: "3",
},
imgCount: {
//可上传图片总数
type: String,
default: "3",
},
imgSize: {
//图片大小 单位M
type: Number,
default: 2,
},
disabled: {
//禁用
type: Boolean,
default: false,
},
formData: {
type: Object,
default: function () {
return {};
},
},
imgType: {
//如果是小程序,这个值则没用作用
type: [Array],
default: function () {
return ["jpeg", "png", "jpg"];
},
},
single: {
//只上传一张图片
type: Boolean,
default: false,
},
closeTip: {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: true,
},
url: {
//上传图片Url
type: String,
},
async: {
type: Boolean,
default: false,
},
header: {
type: Array,
default: function () {
return [];
},
},
previewMany: {
//多图预览
type: Boolean,
default: false,
},
pressImg: {
//压缩图片 H5暂不支持压缩api
type: Number,
default: -1,
},
config: {
type: Object,
default: function () {
return {};
},
},
isAdd: {
typeof: Object,
return: true,
},
},
data() {
return {
sysInfo: {},
imgArray: [],
canUpCount: "",
startUpload: false,
tipObj: {
prompt: "",
typeColor: "#009100",
must: false, //必须要存在的时候
success: "#009100", //成功-#009100; 可自定义修改
// warning: '#bb9300', // 警告 -#bb9300; 可自定义修改
error: "#FF0000", // 失败--#FF0000; 可自定义修改
warning: "#FF0000",
},
conf: {
delIcon: "", //删除图片icon
resultTip: true, //结果提示
style: "1", //组件默认风格
loadIcon: "", //加载时的图标
loadText: "", //加载时的文字
borderStyle: "border:2px dotted #dadada",
addStyle: "2", //添加风格 1 图片标 2 加号标
},
headers: {},
curPlatform: "",
currIndex: null,
};
},
created() {
this.getSysInfo();
this.imgArray = this.imgArr || [];
this.changeImgArr(true);
this.canUpCount = this.single ? 1 : Number(this.uploadImgCount);
if (this.url) {
this.tipObj.prompt = "";
} else {
this.tipObj.prompt = "你没有传入上传url,请检查传入参数";
this.tipObj.typeColor = this.tipObj.error;
}
this.changeHeader(this.header);
// 参数合并
this.conf = Object.assign(this.conf, this.config);
console.log(this.isAdd);
},
watch: {
imgArr(n, o) {
this.imgArray = n || [];
this.changeImgArr(true);
},
imgCount(n, o) {
this.uploadImgCount = n;
this.canUpCount = this.single ? 1 : Number(this.uploadImgCount);
},
url(n, o) {
if (n) {
this.tipObj.prompt = "";
} else {
this.tipObj.prompt = "你没有传入上传url,请检查传入参数";
this.tipObj.typeColor = this.tipObj.error;
}
},
header(n, o) {
this.changeHeader(n);
},
config(n, o) {
this.conf = Object.assign(this.conf, this.config);
},
},
methods: {
upPhoto() {
let that = this;
if (!that.url) {
that.tipObj.prompt = "你没有传入上传url,请检查!";
that.tipObj.typeColor = that.tipObj.error;
return;
}
if (
Number(that.imgCount - that.imgArray.length) <
Number(that.uploadImgCount)
) {
that.canUpCount = Number(that.imgCount - that.imgArray.length);
}
if (!that.tipObj.must) {
that.tipObj.prompt = "";
}
that.tipObj.must = false;
uni.showActionSheet({
itemList: ["拍照上传", "从相册中选择"],
success: function (res) {
if (res.tapIndex == 0) {
uni.chooseImage({
count: Number(that.canUpCount),
sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
sourceType: ["camera"], //从相册选择
success: function (res) {
if (res) {
if (res.tempFiles) {
for (let item of res.tempFiles) {
if (item.size > that.imgSize * 1024 * 1024) {
uni.showToast({
title: `图片不能大于${that.imgSize}M`,
icon: "none",
});
return false;
}
if (item.type) {
let r = that.imgType.some((v) => {
let type = item.type.split("/");
if (type.length) return v === type[1];
});
if (!r) {
uni.showToast({
title: `只允许上传${that.imgType}的类型`,
icon: "none",
});
return false;
}
}
}
}
that.currIndex = that.imgArray.length;
that.imgArray = [...that.imgArray, ...res.tempFilePaths];
that.changeImgArr(false);
if (that.pressImg >= 0) {
//存在图片压缩 开启图片压缩
if (that.checkPlatform()) {
// H5环境下不支持
that.pressImages();
}
} else {
//正常异步提交
that.hasAysnc();
}
}
},
});
} else if (res.tapIndex == 1) {
uni.chooseImage({
count: Number(that.canUpCount),
sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
sourceType: ["album"], //从相册选择
success: function (res) {
if (res) {
if (res.tempFiles) {
for (let item of res.tempFiles) {
if (item.size > that.imgSize * 1024 * 1024) {
uni.showToast({
title: `图片不能大于${that.imgSize}M`,
icon: "none",
});
return false;
}
if (item.type) {
let r = that.imgType.some((v) => {
let type = item.type.split("/");
if (type.length) return v === type[1];
});
if (!r) {
uni.showToast({
title: `只允许上传${that.imgType}的类型`,
icon: "none",
});
return false;
}
}
}
}
that.currIndex = that.imgArray.length;
that.imgArray = [...that.imgArray, ...res.tempFilePaths];
that.changeImgArr(false);
if (that.pressImg >= 0) {
//存在图片压缩 开启图片压缩
if (that.checkPlatform()) {
// H5环境下不支持
that.pressImages();
}
} else {
//正常异步提交
that.hasAysnc();
}
}
},
});
}
},
fail: function (res) {
console.log(res.errMsg);
},
});
},
preview(obj, index) {
if (this.previewMany) {
let urls = [];
urls.push(obj.url);
this.imgArray.map((item) => {
if (obj.id != item.id) {
urls.push(item.url);
}
});
uni.previewImage({
urls: urls,
});
} else {
// 预览图片(单张)
uni.previewImage({
urls: [obj.url],
});
}
},
changeImgArr(type) {
for (let index in this.imgArray) {
let item = this.imgArray[index];
if (!item.upload) {
if (item.upload === false) {
this.imgArray[index].url = item.url;
} else {
this.imgArray[index] = {
id: index,
upload: type,
url: item,
result: type,
};
}
}
}
},
pressImages() {
const _this = this;
let success = 0;
let completeNum = 0;
for (let item of _this.imgArray) {
uni.compressImage({
src: item.url,
quality: _this.pressImg,
success: (res) => {
++success;
item.pressUrl = res.tempFilePath;
},
error: (res) => {
console.log(res);
},
complete: (res) => {
++completeNum;
//等待全部压缩完成
if (completeNum === _this.imgArray.length) {
console.log(success);
if (success >= _this.imgArray.length) {
_this.tipObj.prompt = "全部压缩成功!";
_this.tipObj.typeColor = _this.tipObj.success;
} else {
let errorNum = _this.imgArray.length - success;
_this.tipObj.prompt = `${errorNum}个文件压缩失败`;
_this.tipObj.typeColor = _this.tipObj.warning;
}
console.log("res", _this.imgArray);
if (_this.async) {
//直接上传
_this.asyncUpload();
}
}
},
});
}
},
asyncUpload() {
const _this = this;
if (_this.imgArray.length) {
let length = _this.imgArray.length;
_this.startUpload = true;
_this.tipObj.prompt = "";
let result = [];
//_this.currIndex;
for (let i = 0; i < length; i++) {
let item = _this.imgArray[i];
let subUrl = _this.pressImg >= 0 ? item.pressUrl : item.url;
if (!item.upload) {
_this.asyncUploadMethod(subUrl, i, (res) => {
if (res) {
result.push(res);
if (_this.single) {
_this.$emit("result", _this.imgArray[0]);
} else {
console.log("resultlist", _this.imgArray);
_this.$emit("result", {
all: _this.imgArray,
new: result,
});
}
}
});
}
}
}
},
asyncUploadMethod(url, i, callback) {
const _this = this;
uni.uploadFile({
url: _this.url,
filePath: url,
name: "upfile",
header: _this.headers,
formData: _this.formData,
success: (uploadFileRes) => {
if (uploadFileRes.statusCode == 200) {
let res = JSON.parse(uploadFileRes.data);
/*注意 如果返回的结果字段不一样,请在这里做修改[res.code]*/
if (res.code === 0 || res.code === "0") {
//成功情况下
_this.imgArray[i].result = true;
res.code = 0;
_this.imgArray[i].url = res.data.url; //这里
callback(res.data.url);
} else {
_this.imgArray[i].result = false;
_this.tipObj.prompt = "上传失败,请检查!";
_this.tipObj.typeColor = _this.tipObj.error;
}
} else {
_this.imgArray[i].result = false;
}
},
fail: (e) => {
_this.imgArray[i].result = false;
_this.tipObj.prompt = "上传失败,请检查!";
_this.tipObj.typeColor = _this.tipObj.error;
console.log(e);
},
complete: () => {
_this.imgArray[i].upload = true;
_this.startUpload = false;
_this.$forceUpdate();
},
});
},
delImg(i) {
const _this = this;
uni.showModal({
title: "提示",
content: "是否删除这张照片?",
success: function (res) {
if (res.confirm) {
_this.imgArray.splice(i, 1);
if (_this.async) {
_this.$emit("delImg", i);
}
} else if (res.cancel) {
}
},
});
},
upload(callback) {
const _this = this;
if (!_this.async) {
_this.tipObj.prompt = "";
if (_this.imgArray.length) {
this.startUpload = true;
let successNum = 0;
let urlArr = [];
for (let item of _this.imgArray) {
_this.uploadImg(item, (res) => {
/*注意 如果返回的结果字段不一样,请在这里做修改[res.code]*/
if (res.code == 0) {
successNum++;
urlArr.push(res.url); /*需要线上图片地址[res.fileVo]*/
item.result = true;
} else {
urlArr.push(res);
item.result = false;
}
item.upload = true;
_this.$forceUpdate();
_this.tipObj.prompt =
"正在上传...(成功" + successNum + "张/" + urlArr.length + "张)";
_this.tipObj.typeColor = _this.tipObj.success;
if (urlArr.length == _this.imgArray.length) {
_this.tipObj.prompt = "已全部上传完成!";
_this.tipObj.typeColor = _this.tipObj.success;
if (successNum > 0) {
_this.startUpload = false;
callback(_this.result(urlArr, successNum));
} else {
_this.startUpload = false;
callback(urlArr); //错误的返回
}
}
});
}
} else {
_this.tipObj.prompt = "请先选择图片后上传!";
_this.tipObj.typeColor = _this.tipObj.warning;
}
} else {
_this.tipObj.prompt = "在异步模式下无法调用upload方法。";
_this.tipObj.typeColor = _this.tipObj.warning;
}
},
result(urlArr, successNum) {
let result = {
result: "success",
code: 0,
urlArray: urlArr,
success: successNum,
};
return result;
},
uploadImg(item, callback) {
const _this = this;
let subUrl = _this.pressImg >= 0 ? item.pressUrl : item.url;
uni.uploadFile({
url: _this.url,
filePath: subUrl,
formData: _this.formData,
header: _this.headers,
name: "upfile",
success: (uploadFileRes) => {
if (uploadFileRes) {
let res = JSON.parse(uploadFileRes.data);
res.resCode = 0;
callback(res);
}
},
fail: (e) => {
callback({
resCode: 500,
msg: "图片上传失败",
localUrl: item,
reason: e,
});
},
});
},
changeHeader(header) {
const _this = this;
if (Array.isArray(header)) {
for (let item of header) {
for (let key in item) {
_this.headers[key] = item[key];
}
}
} else {
_this.tipObj.prompt = "请使用数组格式的Header传参";
_this.tipObj.typeColor = _this.tipObj.error;
}
},
//是异步方法的提交
hasAysnc() {
const that = this;
if (that.async) {
let startIndex = that.imgArray.length;
//直接上传
that.asyncUpload(startIndex);
} else {
that.$emit("result", {
code: 500,
msg: "同步模式下,我不会被调用哦~",
});
}
},
checkPlatform() {
const that = this;
let canUse = true;
//#ifdef H5
that.tipObj.prompt = "该环境不支持图片压缩,图片压缩功能不会生效。";
that.tipObj.must = true;
that.tipObj.typeColor = that.tipObj.warning;
canUse = false;
that.hasAysnc();
//#endif
return canUse;
},
getSysInfo() {
const that = this;
try {
const res = uni.getSystemInfoSync();
that.sysInfo = {
screenWidth: res.screenWidth,
screenHeight: res.screenHeight,
};
} catch (e) {
// error
}
},
},
};
</script>
<style scoped>
@font-face {
font-family: "icon_upload";
/* project id 2170343 */
src: url("./font/font_2170343_nij8dm8i18.eot");
src: url("./font/font_2170343_nij8dm8i18.eot?#iefix")
format("embedded-opentype"),
url("./font/font_2170343_nij8dm8i18.woff2") format("woff2"),
url("./font/font_2170343_nij8dm8i18.woff") format("woff"),
url("./font/font_2170343_nij8dm8i18.ttf") format("truetype"),
url("./font/font_2170343_nij8dm8i18.svg") format("svg");
}
.icon_upload {
font-family: "icon_upload" !important;
font-size: 30rpx;
font-style: normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale;
}
.w-100 {
width: 100%;
}
.flex {
/* 转为弹性盒模型*/
display: flex;
}
.flex_bet {
/* 两端左右*/
display: flex;
justify-content: space-between;
}
.flex_wrap {
/* 转为弹性盒模型并自动换行*/
display: flex;
flex-wrap: wrap;
}
.flex_xy_center {
display: flex;
justify-content: center;
align-items: center;
}
.imgupload image {
height: 100%;
width: 100%;
}
.upload-img-view {
height: 136rpx;
width: 136rpx;
border-radius: 10rpx;
border: 4rpx dotted #f1f1f1;
/* background-color: #F1F1F1; */
}
.single-add {
width: 100%;
height: inherit;
min-height: 200rpx;
border-radius: 10rpx;
border: 4rpx dotted #f1f1f1;
}
.upload-img-view > text,
.single-add > text {
font-size: 70rpx;
color: #949494;
}
.upload-img-view .add,
.single-add .add {
font-size: 55rpx;
}
.upload-txt {
font-size: 24rpx;
color: #ffffff;
}
.imgs-view {
height: 136rpx;
width: 136rpx;
border-radius: 10rpx;
margin-right: 1.8%;
margin-bottom: 16rpx;
border: 1rpx solid #f1f1f1;
box-sizing: border-box;
position: relative;
}
.single-css {
width: 100%;
height: inherit;
min-height: 200rpx;
border-radius: 10rpx;
border: 1rpx solid #f1f1f1;
box-sizing: border-box;
position: relative;
}
.result {
position: absolute;
bottom: 0;
width: 100%;
height: 45rpx;
font-size: 26rpx;
left: 0;
background-color: rgba(0, 0, 0, 0.6);
text-align: center;
line-height: 45rpx;
border-bottom-left-radius: 10rpx;
border-bottom-right-radius: 10rpx;
}
.result > .success {
color: #00b900;
}
.result > .error {
color: #b52e25;
}
.uploading {
position: absolute;
background-color: rgba(0, 0, 0, 0.5);
left: 0;
top: 0;
width: 100%;
height: 100%;
text-align: center;
line-height: 100%;
z-index: 988;
}
.uploading image {
width: 60rpx;
height: 60rpx;
z-index: 989;
animation: rotation 1s linear infinite;
-moz-animation: rotation 1s linear infinite;
-webkit-animation: rotation 1s linear infinite;
-o-animation: rotation 1s linear infinite;
}
.uploading text {
display: inline-block;
font-size: 50rpx;
color: #fffdef;
z-index: 989;
animation: rotation 1s linear infinite;
-moz-animation: rotation 1s linear infinite;
-webkit-animation: rotation 1s linear infinite;
-o-animation: rotation 1s linear infinite;
}
@keyframes rotation {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
-moz-transform: rotate(360deg);
-o-transform: rotate(360deg);
}
}
.imgs-view > image,
.single-css > image {
width: 100%;
height: 100%;
border-radius: 10rpx;
}
.imgupload__tip {
font-size: 24rpx;
color: #ff0000;
margin: 10rpx auto;
}
.imgupload__tip > label {
color: #009100;
}
.del-btn {
position: absolute;
top: 0;
right: 0;
width: 32rpx;
height: 32rpx;
z-index: 988;
}
.del-btn > image {
width: 100%;
height: 100%;
display: flex;
}
.del-btn-style-2 {
position: absolute;
top: 0;
right: 0;
width: 32rpx;
height: 32rpx;
z-index: 988;
}
.del-btn-style-1 {
position: absolute;
top: 0;
right: 0;
width: 44rpx;
height: 44rpx;
z-index: 988;
background: rgba(0, 0, 0, 0.3);
color: #ececec;
border-bottom-left-radius: 30rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 24rpx;
}
.result-style {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 44rpx;
border-top-right-radius: 50rpx;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.5s ease;
transform: translateX(-100%);
opacity: 0.4;
}
.result-1 {
background: rgba(7, 169, 19, 0.75);
color: white;
transform: translateX(0%);
font-size: 24rpx;
opacity: 1;
}
.result-2 {
background: rgba(239, 0, 5, 0.7);
color: #dadada;
font-size: 24rpx;
transform: translateX(0%);
opacity: 1;
}
</style>