168 lines
4.4 KiB
Vue
168 lines
4.4 KiB
Vue
<!--
|
||
图片上传组件
|
||
@author terrfly
|
||
@site https://www.jeequan.com
|
||
@date 2022/11/30 18:18
|
||
-->
|
||
<template>
|
||
<view style="flex-grow: 1">
|
||
<!-- 图片内 带 x 号的模式。 -->
|
||
<template v-if="props.mode == 'img'">
|
||
<!-- 包含图片 -->
|
||
<template v-if="props.src">
|
||
<view class="image-wrapper">
|
||
<image v-if="!props.readonly" @tap="delImg" class="del-image" src="/static/iconImg/icon-x-white.svg" mode="scaleToFill" />
|
||
<image class="default-img" :src="props.src" @tap="preview"></image>
|
||
</view>
|
||
</template>
|
||
<template v-else>
|
||
<!-- 不包含图片 -->
|
||
<view @tap="chooseImageAndUpload" style="flex-grow: 1; display: flex; justify-content: space-between; align-items: center">
|
||
<image
|
||
v-if="!props.readonly"
|
||
style="width: 150rpx; height: 150rpx; background-color: #f7f7f7; border-radius: 15rpx"
|
||
src="/static/iconImg/default-img.svg"
|
||
mode="scaleToFill"
|
||
/>
|
||
<image src="/pageDevice/static/devIconImg/icon-arrow-sex.svg" mode="scaleToFill" style="width: 120rpx; height: 120rpx" />
|
||
</view>
|
||
</template>
|
||
</template>
|
||
|
||
<!-- 页面图片 加入 切换按钮 -->
|
||
<template v-if="props.mode == 'viewbtn'">
|
||
<image class="default-img" :src="props.src" @tap="preview" mode="aspectFill"></image>
|
||
|
||
<!-- 图片预览(手写非原生) -->
|
||
<enlarge v-if="vdata.showEnlarge" :imgs="props.src" :changeIsShow="!props.readonly" @chooseImg="chooseImageAndUpload" @enlargeClose="vdata.showEnlarge = false" />
|
||
</template>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive } from 'vue';
|
||
import http from '@/http/http.js';
|
||
import infoBox from '@/commons/utils/infoBox.js';
|
||
import enlarge from './enlarge.vue'; // 图片预览
|
||
import { API_URL_SINGLE_FILE_UPLOAD, $ossFilesForm } from '@/http/apiManager.js';
|
||
|
||
// emit 父组件使用: v-model:src="val" 进行双向绑定。
|
||
const emit = defineEmits(['update:src', 'change']);
|
||
|
||
// 定义 父组件传参
|
||
const props = defineProps({
|
||
src: { type: String, default: '' }, // 双向绑定 文件地址
|
||
bizType: { type: String, default: '' }, // 业务类型
|
||
imgSize: { type: Number, default: 5 }, // 上传图片大小限制 默认 5 M
|
||
|
||
// 两种模式: img - 图片包含删除按钮支持删除, viewbtn-图片内支预览按钮切换。
|
||
mode: { type: String, default: 'img' },
|
||
|
||
// 预览模式, 不支持切换图片。
|
||
readonly: { type: Boolean, default: false }
|
||
});
|
||
|
||
// 定义响应式数据
|
||
const vdata = reactive({
|
||
showEnlarge: false,
|
||
|
||
action: '', // 文件form表单请求地址
|
||
|
||
uploadForm: {
|
||
action: '', // 请求地址
|
||
header: {}, // 请求头
|
||
params: {} // 参数
|
||
}
|
||
});
|
||
|
||
// 预览图片
|
||
function preview() {
|
||
if (props.mode == 'img') {
|
||
// 原生图片预览
|
||
|
||
uni.previewImage({ urls: [props.src] });
|
||
} else {
|
||
// 组件模式
|
||
|
||
vdata.showEnlarge = true;
|
||
}
|
||
}
|
||
|
||
// 删除图片
|
||
function delImg() {
|
||
emit('update:src', '');
|
||
emit('change', '');
|
||
}
|
||
|
||
// 选择图片 and 上传
|
||
function chooseImageAndUpload() {
|
||
// 最多选择一张图片 && 压缩图片 && 支持相册 和 相机
|
||
uni.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'] }).then((res) => {
|
||
let file = res.tempFiles[0];
|
||
|
||
// 预先检查
|
||
if (!beforeCheck(file)) {
|
||
return false;
|
||
}
|
||
|
||
// 检查通过
|
||
|
||
$ossFilesForm(props.bizType, file).then(({ bizData }) => {
|
||
// 本地方式
|
||
if (bizData.formActionUrl === 'LOCAL_SINGLE_FILE_URL') {
|
||
return http.upload(API_URL_SINGLE_FILE_UPLOAD, { bizType: props.bizType }, file).then(({ bizData }) => {
|
||
emit('update:src', bizData);
|
||
emit('change', bizData);
|
||
});
|
||
}
|
||
|
||
// oss 直传
|
||
uni.uploadFile({ url: bizData.formActionUrl, filePath: file.path, name: 'file', formData: bizData.formParams }).then((ossRes) => {
|
||
if (ossRes.statusCode == 200) {
|
||
// 上传成功
|
||
emit('update:src', bizData.ossFileUrl);
|
||
emit('change', bizData.ossFileUrl);
|
||
return false;
|
||
}
|
||
|
||
infoBox.showToast('oss服务响应异常');
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
function beforeCheck(file) {
|
||
return true;
|
||
}
|
||
|
||
defineExpose({
|
||
preview
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.default-img {
|
||
display: block;
|
||
width: 150rpx;
|
||
height: 150rpx;
|
||
background-color: #f7f7f7;
|
||
border-radius: 15rpx;
|
||
}
|
||
.image-wrapper {
|
||
position: relative;
|
||
width: 150rpx;
|
||
height: 150rpx;
|
||
margin-bottom: 20upx;
|
||
.del-image {
|
||
position: absolute;
|
||
top: -20rpx;
|
||
right: -20rpx;
|
||
z-index: 10;
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border-radius: 50%;
|
||
background-color: tomato;
|
||
}
|
||
}
|
||
</style>
|