注:
1. 个别状况不会让前端间接操作 oss(增删查), 可是我遇到了这样的需要
2. 默认曾经开明 oss,在 oss 官网实现了配置
3. 须要应用到 element-ui 的 upload 组件
点击跳转 oss 的官网 API
点击跳转 element 的官网 API
需要形容
- 前端间接操作 oss,类型能够是任何类型。本 demo 以图片和压缩包为例,图片能够预览、删除;压缩包能够下载、删除。
- 在表单填写时,一个表单可能有多个不同类型文件的须要上传;表单波及新增 / 查看 / 编辑性能,复用性要强。
- 新增表单的具体需要:表单新增之后 oss 即可编辑,一旦新增过文件后要么删除已上传文件,要么提交表单,提交不胜利依据后端提醒从新批改表单的谬误从新提交,或者删除放弃提交。
- 查看表单的具体需要:查看表单不可操作 oss,然而对于图片能够进行预览,压缩包能够进行下载。
- 批改表单的具体需要:一旦批改过文件(删除,新增,批改)必须提交表单,提交不胜利依据后端提醒从新批改表单的谬误从新提交,直到胜利为止。
- F5 强刷也要保障上述 5 条,私密麻生,我太难了,我没做
成果展现
新增
查看
、
编辑
图片预览(放大)
新增但不提交的提醒
删除但不提交的提醒
批改但不提交的提醒
后期筹备
- npm install ali-oss
- 须要运维提供
accessKeyId
,accessKeySecret
,bucket
- 其中
bucket
最好辨别开发环境和生产环境 - oss 官网举荐应用 STS 长期受权拜访,我这里没有用,有趣味的话能够移步 OSS 长期受权
utils.js(工具类)
import axios from 'axios'
import OSS from 'ali-oss'
import UUID4 from 'uuid/v4'
// 下载 oss 指定地址
export function downloadURL(url) {let link = document.createElement('a');
link.style.display = 'none';
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// 上传 oss
export function ossUpload(picForProject, fileBuffer) {
let bucketName = '';
// 判断环境 生产环境并且不为测试状态
process.env.NODE_ENV === 'production' ? bucketName = '填你本人申请的' : bucketName = '填你本人申请的';
let client = new OSS({
region: 'oss-cn-beijing',
secure: true, // https
accessKeyId: '填你本人申请的',
accessKeySecret: '填你本人申请的',
bucket: bucketName
});
const fileName = UUID4().toUpperCase().replace(/-/g, '')
const suffix = fileBuffer.name.substr(fileBuffer.name.indexOf('.'))
const url = `${picForProject}${fileName}${suffix}`
async function put() {
try {await client.put(url, fileBuffer)
// await xxx(url) 在这里也能够间接把地址传给后端
return {'code': 200, 'url': url}
} catch (e) {return e.message}
}
return put()};
// 获取 oss 地址
export function showPrivateOss(picName) {
// 这里和上传 oss 的 client 是一样的,应该放在 store 外面同一治理的,demo 这里写在这里了
let bucketName = '';
process.env.NODE_ENV === 'production' ? bucketName = '填你本人申请的' : bucketName = '填你本人申请的';
let client = new OSS({
secure: true, // https
accessKeyId: '填你本人申请的',
accessKeySecret: '填你本人申请的',
bucket: bucketName,
endpoint: 'oss-cn-beijing.aliyuncs.com'
});
async function getOssAddr() {
try {let signUrl = client.signatureUrl(picName, { expires: 1800}); // expires 单位为秒
return {'code': 200, 'url': signUrl}
} catch (e) {return e.message}
}
return getOssAddr()};
// 删除 oss
export function deletePrivateOss(picName) {
let bucketName = '';
process.env.NODE_ENV === 'production' ? bucketName = '填你本人申请的' : bucketName = '填你本人申请的';
let client = new OSS({
secure: true, // https
accessKeyId: '填你本人申请的',
accessKeySecret: '填你本人申请的',
bucket: bucketName,
endpoint: 'oss-cn-beijing.aliyuncs.com'
});
async function deleteOssAddr() {
try {return client.delete(picName);
} catch (e) {return e.message}
}
return deleteOssAddr()};
oss.vue(oss 组件)
<template>
<div>
<el-upload
class="avatar-uploader"
action="oss 地址"
:show-file-list="false"
:http-request="uploadToOss"
:before-upload="beforeAvatarUpload"
:disabled="disabled"
:accept="accept"
:typeName="typeName"
>
<div v-loading="loading">
<template v-if="haveUrl">
<img :src="imgUrl" class="avatar" />
<span class="show_icon" v-if="!disabled">
<i class="el-icon-view" @click.stop="handleView()" v-if="typeName===' 图片 '"></i>
<i class="el-icon-download" @click.stop="handleDownload()" v-else></i>
<i class="el-icon-delete" @click.stop="handleRemove()"></i>
</span>
<span class="show_icon" v-else>
<i class="el-icon-view" @click.stop="handleView()" v-if="typeName===' 图片 '"></i>
<i class="el-icon-download" @click.stop="handleDownload()" v-else></i>
</span>
</template>
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</div>
</el-upload>
<!-- 查看图片 -->
<el-dialog title="照片详情" :visible.sync="showBigPic" width="720px" append-to-body>
<img :src="imgUrl" width="100%" />
</el-dialog>
</div>
</template>
<script>
// 援用工具类(下载指定地址的 oss 文件、上传 oss、展现 oss 图片、删除指定地址的 oss 文件)import {
downloadURL,
ossUpload,
showPrivateOss,
deletePrivateOss,
} from "@/assets/scripts/utils";
// 二次封装的 element 的 message 组件,代码略
import {errorMsg, successMsg} from "@/components/message";
export default {
props: {
// 对文件的限度
limit: {
type: Object,
default: function () {
return {
size: 5,
types: ["image/jpeg", "image/png"],
};
},
},
// 是否有 url
haveUrl: {
type: Boolean,
default: false,
},
// 是否禁用
disabled: {
type: Boolean,
default: false,
},
// url 的名称
urlName: {
type: String,
default: "",
},
// 对应的后端字段名,同一页面可能有多个须要上传 oss 的
name: {
type: String,
default: "",
},
// 上传弹窗,过滤文件类型
accept: {
type: String,
default: "image/*",
},
// 上传文件类型(文件大小过大提醒语也会用到)typeName: {
type: String,
default: "图片",
},
},
data() {
return {
showBigPic: false, // 预览图片
imgUrl: null, // 图片地址
ossFileName: "", // oss 文件名
loading: false, // 是否上传中
};
},
watch: {
urlName: {handler(newValue, oldValue) {if (newValue) {if (this.typeName !== "图片" && this.haveUrl) {this.imgUrl = require("@/assets/images/uploaded.jpg");
} else {showPrivateOss(newValue).then((res) => {if (res && res.code) {this.imgUrl = res.url;} else {errorMsg(res);
}
});
}
}
},
immediate: true,
},
},
methods: {handleView() {this.showBigPic = true;},
handleDownload() {showPrivateOss(this.urlName).then((res) => {if (res && res.code) {downloadURL(res.url);
} else {errorMsg(res);
}
});
},
handleRemove() {
let _this = this;
deletePrivateOss(this.urlName).then((res) => {if (res.res.status === 204) {successMsg();
// ossDone 参数:url 文件地址, haveUrl 是否有地址, name 字段名
this.$emit("callback", null, false, _this.name);
} else {errorMsg(res);
}
});
},
// 上传 oss
uploadToOss(elUpload) {
this.loading = true;
let _this = this;
ossUpload("back-image/" + this.ossFileName, elUpload.file).then((res) => {if (res && res.code) {
// ossDone 参数:url 文件地址, haveUrl 是否有地址, name 字段名
this.$emit("callback", res.url, true, _this.name);
} else {errorMsg(res);
}
this.loading = false;
});
},
// 图片上传前 验证图片大小
beforeAvatarUpload(file) {
let isSize = false;
if (this.limit.sizeType === "kb") {isSize = file.size / 1024 < this.limit.size;} else {isSize = file.size / 1024 / 1024 < this.limit.size;}
if (!isSize) {
errorMsg(`${this.typeName}大小不能超过 ${this.limit.size}${this.limit.sizeType ? this.limit.sizeType : "MB"}!`
);
}
return isSize;
},
},
};
</script>
<style lang="less" scoped>
.show_icon {
display: inline-block;
position: absolute;
top: 0;
right: 0;
width: 108px;
height: 72px;
i {display: none;}
&:hover {background-color: rgba(0, 0, 0, 0.5);
i {
display: inline;
line-height: 72px;
color: #fff;
font-size: 20px;
&:not(:last-child) {margin-right: 10px;}
}
}
}
</style>
addOrEdit.vue(表单组件;新增 / 查看 / 编辑为同一个组件)
/*
** 这是一个弹窗组件,为了代码简洁,删除了其余表单内容
** 父组件传来 id,即断定是查看 / 编辑,须要申请后端详情
**:before-close 须要带参数,重要!!*/
<template>
<el-dialog
@open="setData"
:visible.sync="params.show"
:before-close="() => cancel(false)"
:close-on-click-modal="false"
width="1100px"
append-to-body
>
<div slot="title">
{{params.title}}
<span class="title_required">
<span>*</span> 为必填项
</span>
</div>
<el-form
:model="addForm"
:inline="true"
ref="addForm"
label-position="left"
label-width="125px"
>
// 其余 item 略
<el-form-item
label="营业执照照片"
prop="businessLicenseUrl"
:rules="[{required: true,message:' 不能为空 '}]"
>
<oss
@callback="ossDone"
:haveUrl="haveUrl_businessLicenseUrl"
:disabled="isDisabled"
:urlName="addForm.businessLicenseUrl"
name="businessLicenseUrl"
/>
</el-form-item>
<el-form-item label="附件">
<oss
@callback="ossDone"
:limit="limit"
:haveUrl="haveUrl_attachment"
:urlName="addForm.attachment"
:disabled="isDisabled"
accept=".zip, .rar"
name="attachment"
typeName="压缩包"
/>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="cancel(false)" class="cancelBtn"> 取 消 </el-button>
<el-button
@click="submit"
type="primary"
class="confirmBtn"
v-if="this.params.title !==' 查看 '"
> 确 定 </el-button>
</span>
</el-dialog>
</template>
<script>
import oss from "@/components/oss";
import {methodPost} from "@/api"; // 新增 / 批改用【代码略】import {GET_DETAILS} from "@/api/company"; // 详情【代码略】import {errorMsg, successMsg} from "@/components/message"; // 二次封装的 message【代码略】export default {components: { oss},
data() {
return {
// 是否有执照
haveUrl_businessLicenseUrl: false,
// 是否有附件
haveUrl_attachment: false,
// 编辑时,附件的返回值(作为 flag)flag_attachment: null,
// 编辑时,执照的返回值(作为 flag)flag_businessLicenseUrl: null,
// 附件的大小
limit: {size: 10}
};
},
methods: {
// 操作 oss 后的 $emit
ossDone(url, haveUrl, name) {this.$set(this.addForm, `${name}`, url);
this.$set(this, `haveUrl_${name}`, haveUrl);
},
// 弹窗初始化
setData() {this.addForm = {};
this.isDisabled = false;
if (this.params.title !== "新增") {
this.haveUrl_businessLicenseUrl = true;
this.getDetails({id: this.params.id});
} else {
this.haveUrl_attachment = false;
this.haveUrl_businessLicenseUrl = false;
this.flag_attachment = null;
this.flag_businessLicenseUrl = null;
}
},
getDetails(id) {GET_DETAILS(id).then((res) => {if (res.code === 10000) {
this.addForm = res.result;
this.flag_attachment = res.result.attachment;
this.flag_businessLicenseUrl = res.result.businessLicenseUrl;
if (res.result.attachment) {this.haveUrl_attachment = true;} else {this.haveUrl_attachment = false;}
}
if (this.params.title === "查看") {this.isDisabled = true;}
});
},
submit() {
// 新增 / 编辑
this.$refs.addForm.validate((valid) => {if (valid) {
let url = "/company/";
if (this.params.data) {url += "update";} else {url += "insert";}
methodPost(url, this.addForm, true).then((res) => {if (res.code === 10000) {successMsg();
this.cancel(true);
}
});
} else {errorMsg();
}
});
},
cancel(refresh) {if (this.params.title !== "新增") {if (!refresh) {if (this.flag_attachment !== this.addForm.attachment) {errorMsg("批改附件后,请点击‘确定’按钮");
return;
}
if (this.flag_businessLicenseUrl !== this.addForm.businessLicenseUrl) {errorMsg("批改营业执照后,请点击‘确定’按钮");
return;
}
}
} else {if (this.addForm.businessLicenseUrl || this.addForm.attachment) {errorMsg("点击‘确定’提交新增内容,或删除已上传文件");
return;
}
}
this.$refs.addForm.resetFields();
this.$emit("callback", refresh); // 告诉父组件刷新 list 页,按理论取舍,父组件代码略
},
},
};
</script>