上传器oss图片上传以及生成二维码海报

一、图片上传至oss

  1. 通过接口获取oss上传签名
  1. 图片上传至oss,接口成功但不会有返回信息,因此要在全局axios请求的响应拦截器做弹出提示框的限制
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 添加响应拦截器
    service.interceptors.response.use(
    (response: any) => {
    const res = response.data;

    // 统一错误处理
    if (res.code === StatusCode.ERROR && res.msg == '用户未登录!') {
    // ElMessage.error(res.msg);
    storage.removeToken();
    router.push({ path: '/login' });
    closeLoading();
    return Promise.reject(res);
    }
    if (res.code != StatusCode.SUCCESS || res.code == StatusCode.ERROR) {
    closeLoading();
    // 处理oss上传图片后成功但不返参问题
    if (response.config.url.indexOf("aliyuncs.com") != -1) return;
    ElMessage.error(res.msg);
    return Promise.reject(res);
    }
    closeLoading();
    return res;
    },
    (error: any) => {
    ElMessage('系统繁忙 请刷新重新!');
    closeLoading();
    return Promise.reject(error);
    }
    );

二、qrcode.vue + html2canvas 生成二维码海报

  1. qrcode.vue的两种使用方式:

    ​ 组件方式使用:https://github.com/scopewu/qrcode.vue/blob/main/README-zh_cn.md

    ​ canvas操作方式: https://www.jianshu.com/p/d99e1f6c6a85

  2. html2canvas使用: 它的原理就是先生成了二维码,再通过布局方式,使二维码图片放置在背景图的合适位置,然后截屏生成图片,需要注意的是其通过canvas.toDataURL生成的是base64格式的图片文件,如果需要上传oss,那么需要先转换成文件流

三、具体代码

  1. 代码如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    <template>
    <el-dialog :close-on-click-modal="false" v-model="isDialog" title="速销海报" @close="clickClose">
    <div class="content">
    <!-- *产品图片: -->
    <el-upload action :http-request="picUpload" :before-upload="uploadBefore" :file-list="imgList" list-type="picture-card" :on-preview="handlePictureCardPreview" :on-remove="handleRemove" :limit="9">
    <el-icon>
    <plus />
    </el-icon>
    </el-upload>
    </div>
    <!-- 图片预览 -->
    <el-dialog v-model="dialogVisible">
    <img style="width: 100%; heigth: 100%" :src="dialogImageUrl" alt="" />
    </el-dialog>

    <template #footer>
    <span class="dialog-footer">
    <el-button @click="clickClose">取消</el-button>
    <el-button type="primary" @click="confirmSubmit">确认</el-button>
    </span>
    </template>
    </el-dialog>
    <div class="invitePosterPage">
    <!-- 这里是你生成完成的海报,背景图+二维码 -->
    <div id="poster" class="flex-row" style="position: relative">
    <img class="poster-bg" src="@/assets/avatar.jpg" />
    <!-- canvas里是你的二维码 -->
    <canvas class="qr" id="qrCode-canvas"></canvas>
    </div>
    </div>
    </template>

    <script>
    import { reactive, toRefs, onMounted, defineComponent } from 'vue'
    import { Plus } from '@element-plus/icons'
    import { uploadImage, imgSign, speed, waitSpeed, deleteSpeedImg } from '@/api/ButtJoint'
    import myUtils from '@/utils/myUtils'
    import { ElMessage, ElMessageBox } from "element-plus";
    import QRCode from 'qrcode'
    import html2canvas from 'html2canvas';

    export default defineComponent({
    name: '',
    components: {
    Plus
    },
    props: {
    productId: String,
    closeDialog: {
    type: Function,
    default: () => {
    console.log(`hah`);
    }
    }
    },
    setup(props) {
    const state = reactive({
    isDialog: true,
    // 上传图片列表数组
    imgList: [],
    // 上传图片的预览
    dialogImageUrl: '',
    dialogVisible: false,
    posterDataUrl: '',
    })

    // 关闭弹窗
    const clickClose = () => {
    props.closeDialog();
    };

    // 获取速销海报内容
    const getWaitSpeed = async () => {
    const res = await waitSpeed({ productId: props.productId });
    res.data.urls.forEach((itm) => {
    state.imgList.push({ url: itm })
    })
    };

    // 移除图片
    const handleRemove = async (file) => {
    // 删除图片
    await deleteSpeedImg({
    productId: props.productId,
    url: file.url
    });
    state.imgList = (state.imgList).filter((item) => item.url != file.url);
    }

    // 预览图片
    const handlePictureCardPreview = (file) => {
    state.dialogImageUrl = file.url
    state.dialogVisible = true;
    }

    // 上传图片之前
    const uploadBefore = (file) => {
    //上传文件之前校验图片格式和大小
    const isJPG = file.type === "image/jpeg" || file.type === "image/png" || file.type === "image/gif" || file.type === "image/jpg" || file.type === "image/x-icon" || file.type === "image/bmp";
    const isLt20M = file.size / 1024 / 1024 < 20;
    if (!isJPG) {
    ElMessageBox.confirm("上传图片只能是 JPG、JPEG、PNG、GIF、ICO、BMP 格式!", "提示", {
    confirmButtonText: "确认",
    type: "info",
    })
    return false
    }
    if (!isLt20M) {
    ElMessageBox.confirm("上传图片大小不能超过20MB!", "提示", {
    confirmButtonText: "确认",
    type: "info",
    })
    return false
    }
    };

    // 图片上传至oss
    const picUpload = async (file) => {
    // 获取oss上传签名
    const { data: signData } = await imgSign();
    const ossData = new FormData();
    // 通过工具类函数生成随机10位的图片名,避免图片名重复
    const filename = myUtils.randomName(file.file.name, 10);
    //图片名称
    const keyValue = signData.dir + filename;
    //文件名
    ossData.append("name", file.file.name);
    ossData.append("key", keyValue);
    ossData.append("policy", signData.policy);
    ossData.append("OSSAccessKeyId", signData.accessid);
    ossData.append("success_action_status", '200');
    ossData.append("signature", signData.signature);
    ossData.append("file", file.file);
    console.log(signData.host, "signData.host")
    uploadImage(signData.host, ossData).then(() => {
    const Url = signData.host + '/' + keyValue;
    const imageUrl = {
    url: Url
    }
    state.imgList.push(imageUrl);
    });
    };

    // 确认提交
    const confirmSubmit = async () => {
    if (state.imgList.length < 6) {
    return ElMessage.info('最少添加6张图片!');
    }
    const newList = [];
    state.imgList.forEach((itm) => {
    newList.push(itm.url)
    });
    const res = await speed({
    productId: props.productId,
    imagesUrl: newList
    })
    if (res.code == 200) {
    ElMessage.success('添加成功!');
    clickClose();
    }
    };

    const base64toFile = (dataurl, filename = 'wowangmouren') => {
    let arr = dataurl.split(',')
    let mime = arr[0].match(/:(.*?);/)[1]
    let suffix = mime.split('/')[1]
    let bstr = atob(arr[1])
    let n = bstr.length
    let u8arr = new Uint8Array(n)
    while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
    }
    return new File([u8arr], `${filename}.${suffix}`, {
    type: mime
    })
    }

    // base64图片上传至oss
    const base64Upload = async (file) => {
    // 获取oss上传签名
    const { data: signData } = await imgSign();
    const ossData = new FormData();
    const filename = myUtils.randomName(file.name, 10);
    //图片名称
    const keyValue = signData.dir + filename;
    //文件名
    ossData.append("name", file.name);
    ossData.append("key", keyValue);
    ossData.append("policy", signData.policy);
    ossData.append("OSSAccessKeyId", signData.accessid);
    ossData.append("success_action_status", '200');
    ossData.append("signature", signData.signature);
    ossData.append("file", file);
    uploadImage(signData.host, ossData).then(() => {
    const Url = signData.host + '/' + keyValue;
    console.log(Url, 'base64的图片')
    const imageUrl = {
    url: Url
    }
    state.imgList.push(imageUrl);
    });
    };
    // 进入页面创建二维码并结合背景图生成海报
    const createQRCode = () => {
    //先用 QRCode 生成二维码 canvas,然后用 html2canvas 合成整张海报并转成 base64 显示出来
    const canvas = document.getElementById('qrCode-canvas')
    QRCode.toCanvas(canvas, 'www.baidu.com', (error) => {
    if (error) {
    console.log(error)
    } else {
    //qrcode 生成的二维码会带有一些默认样式,需要调整下
    canvas.style.width = '10rem'
    canvas.style.height = '10rem'
    const poster = document.getElementById('poster')
    html2canvas(poster).then(canvas => {
    state.posterDataUrl = canvas.toDataURL('image/jpeg')
    // base64格式的图片转换成文件流
    const base64File = base64toFile(state.posterDataUrl);
    if (base64File) {
    base64Upload(base64File)
    }
    });
    }
    })
    }

    onMounted(() => {
    getWaitSpeed();
    // createQRCode();
    console.log(props, '3.-组件挂载到页面之后执行-------onMounted')
    })
    return {
    clickClose,
    uploadBefore,
    picUpload,
    handleRemove,
    handlePictureCardPreview,
    confirmSubmit,
    ...toRefs(state),
    }
    },
    })
    </script>
    <style scoped lang='scss'>
    .content {
    height: 35vh;
    overflow-y: scroll;
    text-align: left;
    }
    .invitePosterPage {
    // width: 10px;
    // height: 10px;
    // display: none;
    // .poster-bg {
    // position: relative;
    // }
    .qr {
    height: 1rem;
    position: absolute;
    bottom: 0.5rem;
    left: 1.4rem;
    }
    }
    </style>