VUE 圖片上傳/剪切(vue-cropper)


1. 概述

1.1 說明

  項目中為了保證圖片展示效果以及分辨率高度匹配,需對圖片的尺寸、大小、類型等進行控制;最大限度保證圖片在網站、小程序、app端的展示效果保持一致。

1.2 思路

  使用vue-cropper進行圖片裁剪功能,使用iview組件Upload進行圖片上傳。

  1.2.1 功能選擇

    使用下拉框進行功能選擇,如圖片直接上傳、圖片剪裁等

    

  1.2.2 圖片上傳與展現

    使用Upload進行圖片上傳,根據所需上傳圖片的大小、格式等進行圖片驗證;所有驗證通過后再進行圖片上傳操作。

    

  1.2.3 最終組件樣式

   

  1.2.4 組件使用樣式

   

  1.3 vue-cropper使用

    1.  安裝  npm install vue-cropper 或者 yarn add vue-cropper
    2.  main.js引用注入 
      1.   import VueCropper from 'vue-cropper'
      2.   Vue.use(VueCropper)
    3.  頁面使用  <vueCropper></vueCropper>

  1.4 圖片規則

2. 代碼

2.1 示例代碼結構:

2.2 圖片上傳、剪裁、展現公用組件(common)

2.2.1  圖片剪裁組件代碼(imageCropper.vue)

<!-- 圖片上傳剪裁 -->
<template>
    <div ref="refCommonImageCropper" class="image-cropper-wrapper">
        <div class="cropper-content">
            <div class="operate-content">
                <div class="scope-btn">
                    <label class="btn" for="uploads">{{ options.imgUrl ? '更換圖片' : '上傳圖片'}}</label>
                    <input
                        type="file"
                        id="uploads"
                        style="position:absolute; clip:rect(0 0 0 0);"
                        accept="image/png, image/jpeg, image/gif, image/jpg"
                        @change="uploadImg($event)"
                    />
                    <Button @click="changeScale(1)" title="放大">+</Button>
                    <Button @click="changeScale(-1)" title="縮小">-</Button>
                    <Button @click="rotateLeft" title="左旋轉"></Button>
                    <Button @click="rotateRight" title="右旋轉"></Button>
                    <!-- <Button @click="imageCropperSubmit('blob')">確定</Button> -->
                  </div>
              </div>
              <div :style="{'width': options.imgWidth + 'px', 'height': options.imgHeight + 'px'}">
                  <vueCropper
                      ref="cropper"
                      :img="options.imgUrl"
                      :outputSize="options.size"
                      :outputType="option.outputType"
                      :info="true"
                      :full="option.full"
                      :canMove="option.canMove"
                      :canMoveBox="option.canMoveBox"
                      :original="option.original"
                      :autoCrop="option.autoCrop"
                      :autoCropWidth="options.cropWidth"
                      :autoCropHeight="options.cropHeight"
                      :fixedBox="option.fixedBox"
                      @realTime="realTime"
                      @imgLoad="imgLoad"
                  ></vueCropper>
            </div>
        </div>
        <div class="cropper-operate" v-if="isShowView">
            <div style="height:42px;padding:6px;">剪裁圖片預覽</div>
            <div
                class="show-preview"
                :style="{'width': options.cropWidth + 'px', 'height': options.cropHeight + 'px',  'overflow': 'hidden', 'margin': '5px'}"
            >
                <div :style="previews.div" class="preview">
                    <img :src="previews.url" :style="previews.img" />
                </div>
            </div>
        </div>
    </div>
</template>
<script>
    import { uploadImage } from '../../../api/account.js'
    export default {
        name: 'image-cropper',
        props: {
            /**
             * 裁剪配置
             */
            setting: {
                type: Object,
                default () {
                    return {
                        imgUrl: '',
                        cropWidth: 200,
                        cropHeight: 200,
                        imgWidth: 350,
                        imgHeight: 300,
                        size: 1
                    }
                }
            },
            /**
             * 預覽區域是否展示
             */
            isShowView: {
                type: Boolean,
                default: true
            }
        },
        data () {
            return {
                crap: false,
                previews: {},
                option: {
                    full: false,
                    outputType: 'png',
                    canMove: true,
                    original: false,
                    canMoveBox: true,
                    autoCrop: true,
                    fixedBox: true
                },
                fileName: ''
            }
        },
        computed: {
            options () {
                return this.setting
            }
        },
        watch: {
            options: {
                // eslint-disable-next-line
                handler (newVal, oldVal) {
                    this.setting = newVal
                },
                deep: true
            }
        },
        methods: {
            /**
             * 更改比例
             */
            changeScale (num) {
                num = num || 1
                this.$refs.cropper.changeScale(num)
            },
            /**
             * 左旋轉
             */
            rotateLeft () {
                this.$refs.cropper.rotateLeft()
            },
            /**
             * 右旋轉
             */
            rotateRight () {
                this.$refs.cropper.rotateRight()
            },
            /**
             * 實時預覽函數
             */
            realTime (data) {
                this.previews = data
            },
            /**
             * 被剪裁圖片的上傳以及更改
             */
            uploadImg (e) {
                // 上傳圖片
                var file = e.target.files[0]
                if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(e.target.value)) {
                    this.$Message.warning('圖片類型必須是jpeg,jpg,png中的一種')
                    return false
                }
                this.fileName = file.name
                var reader = new FileReader()
                let type = file.type
                reader.onload = (e) => {
                    let data
                    if (typeof e.target.result === 'object') {
                        // 把Array Buffer轉化為blob 如果是base64不需要
                        data = URL.createObjectURL(new Blob([e.target.result], { type }))
                    } else {
                        data = e.target.result
                    }
                    this.options.imgUrl = data
                }
                // 轉化為base64
                reader.readAsDataURL(file)
                // 轉化為blob
                // reader.readAsArrayBuffer(file)
            },
            imgLoad (msg) {
                console.log(msg)
            },
            /**
             * 剪裁圖片上傳
             */
            imageCropperSubmit (type) {
                if (this.options.imgUrl) {
                    if (type === 'blob') {
                        this.$refs.cropper.getCropBlob(data => {
                            let formData = new FormData()
                            formData.append('file', data, this.fileName)
                            uploadImage(formData).then(data => {
                                this.$Message.success('上傳成功')
                                let cropperData = {
                                    httpOriginalFileUri: data.httpOriginalFileUri,
                                    originalFileUri: data.originalFileUri,
                                    converFileUri: data.converFileUri,
                                    fileName: this.fileName
                                }
                                this.$emit('fileSuccess', cropperData)
                            }).catch(err => {
                                console.log(err)
                            })
                        })
                    } else {
                        // 此處得到的是base64位圖片數據,暫無用
                        this.$refs.cropper.getCropData(data => {
                            console.log(data)
                        })
                    }
                } else {
                    this.$Message.warning('請上傳需裁剪圖片')
                }
            }
        }
    }
</script>
<style lang="scss" scoped>
.image-cropper-wrapper{
  display: flex;
  flex-direction: row;
  justify-content: center;
  .cropper-content {
    display: flex;
    display: -webkit-flex;
    flex-direction: column;
      .operate-content {
        margin-bottom: 10px;
        display: flex;
        display: -webkit-flex;
        .scope-btn {
          display: flex;
          display: -webkit-flex;
          >button {
            margin-left: 6px;
          }
        }
        .btn {
          outline: none;
          display: inline-block;
          line-height: 1;
          white-space: nowrap;
          cursor: pointer;
          -webkit-appearance: none;
          text-align: center;
          -webkit-box-sizing: border-box;
          box-sizing: border-box;
          outline: 0;
          margin: 0;
          -webkit-transition: 0.1s;
          transition: 0.1s;
          font-weight: 500;
          padding: 8px 15px;
          font-size: 12px;
          border-radius: 3px;
          color: #fff;
          background-color: #67c23a;
          border-color: #67c23a;
        }
      }
  }
  .cropper-operate{
      display: flex;
      flex-direction: column;
      padding-left: 20px;
    }
    .show-preview {
      flex: 1;
      -webkit-flex: 1;
      display: flex;
      display: -webkit-flex;
      justify-content: center;
      align-items: center;
      -webkit-justify-content: center;
      .preview {
        overflow: hidden;
        border: 1px solid #cccccc;
        background: #cccccc;
      }
    }
}
</style>

2.2.1  圖片上傳、展現、裁剪組件代碼(imageOperate.vue)

<!-- 圖片上傳 -->
<template>
    <div class="image-upload-wrapper">
        <div class="default-upload-list" v-if="formInfo.fileUrl">
            <img :src="formInfo.fileUrl">
            <div class="default-upload-list-cover">
                <Icon type="ios-eye-outline" @click.native="avatarModal=true"></Icon>
                <Icon v-if="!isDisable" type="ios-trash-outline" @click.native="handleFileRemove"></Icon>
            </div>
        </div>
        <div v-else class="image-upload-item">
            <Select
                v-model="formInfo.uploadTypeSelected"
                placeholder="請選擇圖片上傳方式"
                :disabled="`${imgHeight}`=='0'"
                :title="`${imgHeight}`=='0'?'尺寸不限時只能進行直接上傳操作':''"
                @on-change="onChangeImageUploadType">
                <Option
                  v-for="item in imageUploadType"
                  :value="item.value"
                  :key="item.value"
                >{{ item.label }}</Option>
            </Select>
            <div class="image-upload-content">
                <div v-if="formInfo.uploadTypeSelected=='cropperUpload'" class="cut-img-wrapper">
                    <div style="width: 58px;height:58px;line-height: 58px;" @click="uploadImage">
                        <Icon
                            type="ios-cut"
                            size="20"
                            style="color: #3399ff"
                        ></Icon>
                    </div>
                </div>
                <!-- 直接上傳 -->
                <Upload
                    v-else
                    ref="refWholeUpload"
                    type="drag"
                    action="/api/admin/attach/uploadAttach"
                    style="width: 58px;height:58px;"
                    :show-upload-list="false"
                    :on-success="fileSuccess"
                    :before-upload="handleBeforeUpload"
                    :on-progress="uploadProgress"
                    :on-error="uploaderror">
                    <div style="width: 58px;height:58px;line-height: 58px;">
                        <Icon
                            type="ios-cloud-upload"
                            size="20"
                            style="color: #3399ff"
                        ></Icon>
                      </div>
                </Upload>
                <Alert
                    v-if="!formInfo.fileUrl"
                    :type="imageError?'error':'info'"
                    class="image-info">
                    <div>寬高[{{(`${imgWidth}`!='0'?imgWidth:'不限')}}*{{(`${imgHeight}`!='0'?imgHeight:'不限')}}]&nbsp;&nbsp;格式[{{formInfo.uploadTypeSelected=='IconUpload'?'.ico':'.png/.jpg/.jpeg'}}]&nbsp;&nbsp;最大[{{imgSizeTip}}]</div>
                    <div v-if="imageError" style="color:red;">{{imageError}}</div>
                </Alert>
            </div>
        </div>
        <Modal
            v-model="avatarModal"
            :mask-closable="false"
            :closable='false'
            :z-index='1001'>
            <img :src="formInfo.fileUrl" style="width: 100%">
            <div slot="footer">
                <Button
                    type="primary"
                    @click="avatarModal = false"
                >關閉</Button>
            </div>
        </Modal>
        <Modal
            :width="modalCropperWidth"
            v-model="modalImageCropper"
            title='圖片剪切處理'
            :mask-closable="false"
            :z-index='2001'
            class-name="vertical-center-modal"
            draggable>
            <image-cropper
                ref="refImageCropper"
                @fileSuccess="imageCropperSuccess"
                :setting="setOptions"
                :isShowView="isShowViewImg"
                v-if="modalImageCropper" />
            <div slot="footer">
                <Button @click="modalImageCropper = false">取消</Button>
                <Button type="primary" @click="submitImageCropper">確定</Button>
            </div>
        </Modal>
        <Spin
            fix
            style="z-index:9999"
            v-if='isLoading'
            class="spin"
        >圖片上傳中...</Spin>
    </div>
</template>
<script>
    import imageCropper from './imageCropper'
    export default {
        name: 'image-operate',
        props: {
            // 圖片路徑
            imageUrl: {
                type: String,
                default () {
                    return ''
                }
            },
            // 圖片大小
            imageSize: {
                type: Number,
                default () {
                    return 500
                }
            },
            // 圖片尺寸——寬
            imgWidth: {
                type: String | Number,
                default () {
                    return 100
                }
            },
            // 圖片尺寸——高
            imgHeight: {
                type: String | Number,
                default () {
                    return 100
                }
            },
            // 圖片大小文字內容
            imgSizeTip: {
                type: String,
                default () {
                    return '500KB'
                }
            },
            // 是否啟用
            isDisable: {
                type: Boolean,
                default: false
            },
            // 是否支持icon直接上傳
            isIconUpload: {
                type: Boolean,
                default: false
            }
        },
        components: {
            'image-cropper': imageCropper
        },
        data () {
            return {
                isLoading: false,
                // 圖片上傳類型選擇
                imageUploadType: [],
                formInfo: {
                    uploadTypeSelected: 'wholeUpload',
                    fileUrl: '',
                    submitUrl: ''
                },
                // 圖片預覽
                avatarModal: false,
                attach: {
                    fileName: '',
                    originalFileUri: '',
                    converFileUri: ''
                },
                // 圖片直接上傳錯誤
                imageError: '',
                // 圖片裁剪
                setOptions: {
                    imgUrl: '',
                    size: 1,
                    cropWidth: 200,
                    cropHeight: 200,
                    imgWidth: 350,
                    imgHeight: 300
                },
                isShowViewImg: true,
                modalCropperWidth: 700,
                modalImageCropper: false
            }
        },
        mounted () {
            if (this.isIconUpload) {
                this.imageUploadType = [
                    { label: '圖片直接上傳', value: 'wholeUpload' },
                    { label: '圖片剪裁上傳', value: 'cropperUpload' },
                    { label: 'ICON圖片直接上傳', value: 'IconUpload' }
                ]
            } else {
                this.imageUploadType = [
                    { label: '圖片直接上傳', value: 'wholeUpload' },
                    { label: '圖片剪裁上傳', value: 'cropperUpload' }
                ]
            }
            this.imageError = ''
            this.formInfo.fileUrl = this.imageUrl
        },
        methods: {
            /**
             * 圖片上傳類型選擇
             */
            onChangeImageUploadType (val) {
                this.imageError = ''
                this.handleFileRemove()
            },
            /**
             * 裁剪圖片
             */
            uploadImage () {
                this.setOptions.imgUrl = ''
                this.setOptions.size = 1
                this.setOptions.cropWidth = this.imgWidth
                this.setOptions.cropHeight = this.imgHeight
                this.setOptions.imgWidth = this.imgWidth + 20
                this.setOptions.imgHeight = this.imgHeight + 20
                let screenWidth = document.body.clientWidth
                let maxWidth = screenWidth / 2 - 100
                if (maxWidth >= this.imgWidth + 50) {
                    this.isShowViewImg = true
                    this.modalCropperWidth = (this.imgWidth + 40) * 2 > 500 ? (this.imgWidth + 50) * 2 : 500
                } else {
                    this.isShowViewImg = false
                    if ((this.imgWidth + 50) * 2 <= 500) {
                        this.modalCropperWidth = 500
                    } else {
                        this.modalCropperWidth = this.imgWidth + 100
                    }
                }
                this.modalImageCropper = true
            },
            /**
             * 圖片確定上傳
             */
            submitImageCropper () {
                this.$refs.refImageCropper.imageCropperSubmit('blob')
            },
            /**
             * 圖片上傳成功后返回對應圖片信息
             */
            imageCropperSuccess (val) {
                this.formInfo.fileUrl = val.httpOriginalFileUri
                this.formInfo.submitUrl = val.httpOriginalFileUri
                const data = {
                    fileName: val.fileName,
                    originalFileUri: val.originalFileUri,
                    converFileUri: val.converFileUri,
                    fileUrl: val.httpOriginalFileUri,
                    submitUrl: val.originalFileUri
                }
                this.$emit('imageUploadEmit', data)
                this.modalImageCropper = false
            },
            /**
             * 刪除圖片
             */
            handleFileRemove () {
                this.attach = {
                    fileName: this.attach.fileName,
                    originalFileUri: '',
                    converFileUri: ''
                }
                this.formInfo.fileUrl = ''
                this.formInfo.submitUrl = ''
                const data = {
                    fileName: this.attach.fileName,
                    originalFileUri: '',
                    converFileUri: '',
                    fileUrl: '',
                    submitUrl: ''
                }
                this.$emit('imageUploadEmit', data)
            },
            /**
             * 附件上傳成功返回值
             */
            fileSuccess (res, file) {
                if (res.result) {
                    const { httpOriginalFileUri, originalFileUri, converFileUri } = res.data
                    this.attach.fileName = file.name
                    this.attach.originalFileUri = originalFileUri
                    this.attach.converFileUri = converFileUri
                    this.formInfo.fileUrl = httpOriginalFileUri
                    this.formInfo.submitUrl = originalFileUri
                    const data = {
                        fileName: file.name,
                        originalFileUri: originalFileUri,
                        converFileUri: converFileUri,
                        fileUrl: httpOriginalFileUri,
                        submitUrl: originalFileUri
                    }
                    this.isLoading = false
                    this.$emit('imageUploadEmit', data)
                } else {
                    this.formInfo.fileUrl = ''
                    this.imageError = '圖片上傳失敗,請重新上傳。'
                    this.isLoading = false
                }
            },
            uploadProgress (file) {
                this.isLoading = true
            },
            /**
             * 附件上傳判斷
             */
            handleBeforeUpload (file) {
                this.isLoading = true
                this.imageError = ''
                let check = this.$refs.refWholeUpload.fileList.length < 1
                let isIcontype = file.type === 'image/x-icon'
                if (this.formInfo.uploadTypeSelected === 'IconUpload') {
                    if (!isIcontype) {
                        this.imageError = '請選擇icon類型的圖片上傳。'
                        this.isLoading = false
                        return false
                    }
                }
                if (!check) {
                    this.imageError = '限制上傳一張圖片,請刪除后重新上傳。'
                    this.isLoading = false
                    return false
                } else {
                    return this.checkImageWH(file, this.imgWidth, this.imgHeight)
                }
            },
            /**
             * 判斷上傳文件類型
             */
            judgeFileType (type) {
                let typeList = ['image/jpeg', 'image/png', 'image/jpg', 'image/x-icon']
                let hasIndex = typeList.findIndex(item => item.indexOf(type) > -1)
                if (hasIndex > -1) {
                    return true
                } else return false
            },
            /**
             * 返回一個promise  檢測通過返回resolve  失敗返回reject組織圖片上傳
             */
            checkImageWH (file, width, height) {
                let self = this
                return new Promise(function (resolve, reject) {
                    let accordType = self.judgeFileType(file.type)
                    if (!accordType) {
                        self.imageError = '上傳的文件為非圖片格式,請選擇圖片格式文件上傳'
                        self.isLoading = false
                        reject(new Error(self.imageError))
                    } else if (file.size / 1024 > self.imageSize) {
                        self.imageError = `上傳圖片不能超過${self.imgSizeTip}`
                        self.isLoading = false
                        reject(new Error(self.imageError))
                    } else {
                        if (`${width}` !== '0') {
                            let filereader = new FileReader()
                            filereader.onload = e => {
                                let src = e.target.result
                                const image = new Image()
                                image.onload = function () {
                                    let errorInfo = ''
                                    if ((width && this.width !== width)) {
                                        errorInfo = `寬(${this.width})、`
                                    }
                                    if (height && `${height}` !== '0' && this.height !== height) {
                                        errorInfo = `${errorInfo}高(${this.height})`
                                    }
                                    if (errorInfo) {
                                        self.imageError = `上傳圖片錯誤:${errorInfo}`
                                        self.isLoading = false
                                        reject(new Error(self.imageError))
                                    } else {
                                        self.isLoading = false
                                        resolve(true)
                                    }
                                }
                                image.onerror = reject
                                image.src = src
                            }
                            filereader.readAsDataURL(file)
                        } else {
                            self.isLoading = false
                            resolve(true)
                        }
                    }
                })
            },
            /**
             * 附件上傳失敗
             */
            uploaderror (file) {
                this.isLoading = false
                this.imageError = '圖片上傳失敗,請重新上傳。'
            }
        }
    }
</script>
<style lang="scss" scoped>
.image-upload-wrapper .image-upload-item{
  display:flex;
  flex-direction: column;
}
.image-upload-wrapper .image-upload-content{
  margin-top: 10px;
  display: flex;
  .image-info {
    margin-left:10px;
    text-align: left;
    display:flex;
    flex-direction:column;
    justify-content:center;
    padding: 0 10px;
    font-size: 13px;
    height: 60px;
  }
}
.cut-img-wrapper{
    background: #fff;
    border: 1px dashed #dcdee2;
    display: felx;
    width: 58px;
    height: 58px;
    border-radius: 4px;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.2s;
}
.cut-img-wrapper:hover{
    border: 1px dashed #2d8cf0;
}
.default-upload-list{
  display: inline-block;
  width: 60px;
  height: 60px;
  text-align: center;
  line-height: 60px;
  border: 1px solid transparent;
  border-radius: 4px;
  overflow: hidden;
  background: #fff;
  position: relative;
  box-shadow: 0 1px 1px rgba(0,0,0,.2);
  margin-right: 4px;
}
.default-upload-list img{
  width: 100%;
  height: 100%;
}
.default-upload-list-cover{
  display: none;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0,0,0,.6);
}
.default-upload-list:hover .default-upload-list-cover{
  display: block;
}
.default-upload-list-cover i{
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  margin: 0 2px;
}
</style>

2.3 使用示例(formOperate.vue/index.vue)

2.3.1  表單示例代碼(formOperate.vue)

<template>
  <Form ref="refForm" :model="formData" :rules="rules">
      <FormItem label="圖片" prop="fileUrl" label-for="fileUrl">
        <Input v-show="false" v-model="formData.fileUrl" />
        <image-upload
          :imageUrl="formData.fileUrl"
          :isIconUpload="true"
          @imageUploadEmit="imageUploadFunction"
          :imageSize="settingImageUpload.maxSize"
          :imgWidth="settingImageUpload.width"
          :imgHeight="settingImageUpload.height"
          :imgSizeTip="settingImageUpload.sizeTip"
        />
      </FormItem>
    </Form>
</template>
<script>
import ImageUpload from "./common/imageOperate";
export default {
  components: {
    ImageUpload,
  },
  data() {
    return {
      formData: {
        fileUrl: "", // 圖片路徑
      },
      settingImageUpload: {
        width: 100,
        height: 100,
        maxSize: 100,
        sizeTip: "100kb",
      },
      rules: {
        fileUrl: { required: true, message: '請上傳圖片' }
      }
    };
  },
  methods: {
    /**
     * 圖片上傳成功
     */
    imageUploadFunction(val) {
      this.formData.originalFileUri = val.originalFileUri;
      this.formData.obtainIconUrl = val.fileUrl;
    }
  },
};
</script>

2.3.2  入口示例代碼(index.vue)

<template>
  <div class="medo-wrapper">
    <Button type="primary" @click="handleAdd">新建</Button>
    <Drawer
      :title="`${propOperateData.id ? '修改' : '新增'}表單`"
      v-model="showInfoOperate"
      :width="drawerStyles.width"
      :styles="drawerStyles.style"
      :mask-closable="false"
      @on-close="showInfoOperate = false"
    >
      <form-operate
        v-if="showInfoOperate"
        :data="propOperateData"
        ref="refFormOperate"
      />
      <div class="default-drawer-footer">
        <Button type="text" @click="showInfoOperate = false"> 關閉 </Button>
        <Button type="primary" @click="handleSubmit"> 確定 </Button>
      </div>
    </Drawer>
  </div>
</template>
<script>
import formOperate from './formOperate'
export default {
  components: {
    formOperate
  },
  data() {
    return {
      showInfoOperate: false,
      propOperateData: {
        id: "",
        fileUrl: "",
      },
      drawerStyles: {
        // 內容樣式
        styles: {
            height: 'calc(100% - 55px)',
            overflow: 'auto',
            paddingBottom: '53px',
            position: 'static'
        },
        width: 720
      },
    };
  },
  methods: {
    handleAdd () {
      this.showInfoOperate = true
    },
    /**
     * 提交按鈕
     */
    handleSubmit() {
      this.$refs.refFormOperate.$refs.refForm.validate(validate => {
        if (validate) {
          console.log('驗證成功調用接口')
        }
      })
    }
  }
};
</script>
<style lang="scss" scoped>
.default-drawer-footer{
  width: 100%;
  position: absolute;
  bottom: 0;
  left: 0;
  border-top: 1px solid #e8e8e8;
  padding: 10px 16px;
  text-align: right;
  background: #fff;
}
</style>

2.3 上傳代碼接口返回:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM