vue-cropper图片裁剪(vue2与vue3)

  在项目中,前端开发经常会遇到有图片上传的需求,而别人的组件大多都满足不了当下产品的需求,这是往往我们得去依靠组件自己自定义一个项目通用的裁剪组件,废话不多说,上货…

在这里插入图片描述

  1. 安装依赖:
    vue2:

    vue2:npm install vue-cropper  或  yarn add vue-cropper
    

    vue3:

    npm install vue-cropper@next  或  yarn add vue-cropper@next
    
  2. 引入相关文件

    // main.ts
    import 'vue-cropper/dist/index.css'  // vue3才需要引入样式,vue2不要
    

    注意:这个样式一定记得引啊,我就是忘引入,裁剪框一直弄不出来,白白浪费好几个小时,还烧脑

  3. 自定义封装裁剪组件

vue2

  • 父组件

    // 表单中
    <template slot="imageUrlForm" slot-scope="scope">
    	<Cropper :getPropsUrl="getPropsUrl" :imageUrl="imageUrl" :cropOption="cropOption"/>
    </template>
    
    import Cropper from '@/components/cropper/cropper'
    export default {
    	// 其他...
    	data() {
    		return {
    			imageUrl:'', // 上传图片地址
    			cropOption:{  // 如果只是单组件使用的话,可以直接写在cropper组件中,这里是多组件使用,如:头像上传,广告上传,头像与广告的上传框宽高不一样,还有查看弹窗禁用上传功能等
    				autoCropWidth: '',
    				autoCropHeight: '',
    				width: 448,
    				deleteLeft:230,
    				updateLeft:180,
    				disabled:false
    			}
    		}
    	},
    	components:{Cropper},
    	methods: {
    	  /**
    	  	 * @函数描述: 父组件获取子组件的值 - 【截图组件】
    	  	 **/
    	  	getPropsUrl(data){
    			this.imageUrl = data
    		},
    	}
    }
    
  • cropper组件:

    <template>
     <div>
       <div v-if="imageUrl!==''" class="btnUp1" :style="{'width': cropOption.width + 'px'}">
         <img :src="imageUrl" alt=""/>
         <span v-show="!cropOption.disabled" class="deleteBtn el-icon-delete" @click.stop="deleteFn" :style="{'left': cropOption.deleteLeft + 'px'}"></span>
         <span class="updateBtn el-icon-zoom-in" @click.stop="()=>updateVisible=true" :style="{'left': cropOption.updateLeft + 'px'}"></span>
       </div>
       <div v-else class="btnUp">
         <label for="uploads"><i class="el-icon-plus" style="cursor: pointer"></i></label>
         <input :disabled="cropOption.disabled" type="file" id="uploads" accept="image/png, image/jpeg, image/gif, image/jpg" @change="uploadImg($event, 1)">
       </div>
    
       <!-- 若有点击图片放大的需求可使用 -->
       <el-dialog :visible.sync="updateVisible" :close-on-click-modal="false" append-to-body>
         <img width="100%" :src="imageUrl" alt=""/>
       </el-dialog>
    
       <el-dialog id="appBox" v-if="show" :visible.sync="show" width="800px" :close-on-click-modal="true" append-to-body>
         <div style="display: flex">
           <div class="cut">
             <vue-cropper
              	ref="cropper"
              :img="option.img"
              :output-size="option.size" 
              :output-type="option.outputType"
                          :info="true" 
                          :full="option.full" 
                          :fixed="fixed" 
                          :fixed-number="fixedNumber"
                          :can-move="option.canMove" 
                          :can-move-box="option.canMoveBox" 
                          :fixed-box="option.fixedBox"
                          :original="option.original"
                          :auto-crop="option.autoCrop" 
                          :auto-crop-width="cropOption.autoCropWidth"
                          :auto-crop-height="cropOption.autoCropHeight" 
                          :center-box="option.centerBox"
                          @real-time="realTime" 
                          :high="option.high"
                          mode="cover" 
                          :max-img-size="option.max"
                          >
             </vue-cropper>
           </div>
           <div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px',  'overflow': 'hidden', 'margin-left': '5px'}">
             <div :style="previews.div">
               <img :src="previews.url" :style="previews.img" alt="">
             </div>
           </div>
         </div>
         <div class="menuBox">
           <div class="menuBtn el-icon-zoom-in" @click="changeScale(1)"></div>
           <div class="menuBtn el-icon-zoom-out" @click="changeScale(-1)"></div>
           <div class="menuBtn el-icon-back" @click="rotateLeft"></div>
           <div class="menuBtn el-icon-right" @click="rotateRight"></div>
         </div>
         <div class="test-button">
           <el-button v-if="!isLoading" class="sureBtn" @click="clickSure" type="primary">确 定</el-button>
           <el-button v-else class="sureBtn" type="primary" :loading="isLoading">上传中</el-button>
           <el-button class="closeBtn" @click="closeShow">取 消</el-button>
         </div>
       </el-dialog>
     </div>
    </template>
    
    import {VueCropper} from 'vue-cropper'
    import {imageUpload} from "@/components/cropper/api/cropper";  // 上传图片的接口 生成url地址
    
    export default {
      name: "cropper",
      components: {
        VueCropper
      },
      data() {
        return {
          previews: {},
          option: {
    		  img: '',  // 上传图片地址
    		  size: 1,  // 输出图片大小
    		  full: true,  // 是否输出原图比例
    		  outputType: 'png',  // 输出图片格式
    		  canMove: true, // 图片是否可移动
    		  fixedBox: true,  // 截图框是否固定
    		  original: true,  // 图片是否按比例
    		  canMoveBox: false, // 截图框是否可移动
    		  autoCrop: true,  // 是否开启截图
    		  // 只有自动截图开启 宽度高度才生效
    		  autoCropWidth: '',  // 输出图片宽高
    		  autoCropHeight: '',  // 输出图片宽高
    		  centerBox: true,  // 是否开启截图框固定宽高
    		  high: true,  // 是否开启图片移动
    		  max: 99999  // 输出图片大小限制
          },
          show: false,  // 显示裁剪弹窗
          fixed: false,  // 上传图片时的过渡效果
          fixedNumber: [16, 9],  // 截图框固定宽高
          imageUrl: '',  // 图片地址
          updateVisible: false,  // 显示预览弹窗
          isLoading: false,  // 是否在上传
        }
      },
      props: ['getPropsUrl', 'imageUrl', 'cropOption'],
      methods: {
        closeShow() {
          this.option.img = ''
          this.show = false
          this.getPropsUrl('')
          document.getElementById('uploads').value = ''
        },
        deleteFn() {
          this.imageUrl = ''
          this.getPropsUrl('')
        },
    
        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
        },
        clickSure() {
          this.isLoading = true  //  上传图片时的过渡效果
          this.$refs.cropper.getCropBlob(async (data) => {
            var img = window.URL.createObjectURL(data)
    
            const response = await fetch(img);
            const blobData = await response.blob();
    
            const formData = new FormData();
            formData.append('file', blobData, 'filename.png');
            imageUpload(formData).then(res => {  // 调用图片上传接口,获取url地址
              this.isLoading = false
              this.show = false
              this.imageUrl = res.data.data.url
              this.getPropsUrl(res.data.data.url)
              document.getElementById('uploads').value = ''
            });
          })
        },
    
        uploadImg(e, num) {
          //上传图片
          var file = e.target.files[0]
          this.show = true
          if (!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(e.target.value)) {
            alert('图片类型必须是.gif,jpeg,jpg,png,bmp中的一种,请重新选择')
            return false
          }
          var reader = new FileReader()
          //
          reader.onload = (e) => {
            let data
            if (typeof e.target.result === 'object') {
              // 把Array Buffer转化为blob 如果是base64不需要
              data = window.URL.createObjectURL(new Blob([e.target.result]))
            } else {
              data = e.target.result
            }
            if (num === 1) {
              this.option.img = data
            } else if (num === 2) {
              this.example2.img = data
            }
          }
          // 转化为base64
          reader.readAsDataURL(file)
          // 转化为blob
          // reader.readAsArrayBuffer(file)
        },
      },
    }
    </script>
    

vue3

  • 父组件(html与vue2一样)

    <script setup lang="ts">
    import {ref, reactive} from "vue";
    import Cropper from "@/components/cropper.vue";
    /**
     * 截图配置
     */
    const cropOption = reactive({
      autoCropWidth: 250,
      autoCropHeight: 250,
      width: 140,
      deleteLeft: 80,
      updateLeft: 28,
      disabled: false
    })
    const imageUrl= ref<any>('')  // 头像
    
    /**
     * @函数描述: 父组件获取子组件的值 - 【截图组件】
     **/
    const getPropsUrl = (data: any) => {
      imageUrl.value = data
    }
    
  • cropper组件:

    <template>
     <div id="cropper-container">
       <div v-if="imageUrl!==''" class="showImg" :style="{'width': props.cropOption.width + 'px'}">
         <img :src="imageUrl" alt=""/>
         <span v-show="!props.cropOption.disabled" class="powerBtn" @click.stop="deleteFn" :style="{'left': props.cropOption.deleteLeft + 'px'}">
           <el-icon>
             <Delete/>
           </el-icon>
         </span>
         <span class="powerBtn" @click.stop="()=> updateVisible=true" :style="{'left': props.cropOption.updateLeft + 'px'}">
           <el-icon>
             <ZoomIn/>
           </el-icon>
         </span>
       </div>
       <div v-else class="btnUp">
         <label for="uploads">
           <el-icon style="cursor: pointer">
             <Plus/>
           </el-icon>
         </label>
         <input :disabled="props.cropOption.disabled" type="file" id="uploads" accept="image/png, image/jpeg, image/gif, image/jpg" @change="uploadImg($event, 1)">
       </div>
    
       <div class="tip">像素长为{{ props.cropOption.autoCropWidth }}px,宽{{ props.cropOption.autoCropHeight }}px</div>
    
       <!-- 若有点击图片放大的需求可使用 -->
       <el-dialog v-if="updateVisible" v-model="updateVisible" :close-on-click-modal="false" append-to-body>
         <img width="100%" :src="imageUrl" alt=""/>
       </el-dialog>
    
       <el-dialog id="appBox" v-model="show" width="800px" :close-on-click-modal="false" :close-on-press-escape="false" :append-to-body="true">
         <div style="display: flex">
           <div class="cut">
             <vue-cropper ref="cropper"
                          :img="option.img"
                          :output-size="option.size"
                          :output-type="option.outputType"
                          :info="true"
                          :full="option.full"
                          :fixed="fixed"
                          :fixed-number="fixedNumber"
                          :can-move="option.canMove"
                          :can-move-box="option.canMoveBox"
                          :fixed-box="option.fixedBox"
                          :original="option.original"
                          :auto-crop="option.autoCrop"
                          :auto-crop-width="props.cropOption.autoCropWidth"
                          :auto-crop-height="props.cropOption.autoCropHeight"
                          :center-box="option.centerBox"
                          :high="option.high"
                          mode="cover"
                          :max-img-size="option.max"
                          @real-time="realTime"
             >
             </vue-cropper>
           </div>
           <div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px',  'overflow': 'hidden', 'margin-left': '5px'}">
             <div :style="previews.div">
               <img :src="previews.url" :style="previews.img" alt="">
             </div>
           </div>
         </div>
         <div class="menuBox">
           <div class="menuBtn" @click="changeScale(1)">
             <el-icon>
               <ZoomIn/>
             </el-icon>
           </div>
           <div class="menuBtn" @click="changeScale(-1)">
             <el-icon>
               <ZoomOut/>
             </el-icon>
           </div>
           <div class="menuBtn" @click="rotateLeft">
             <el-icon>
               <RefreshLeft/>
             </el-icon>
           </div>
           <div class="menuBtn" @click="rotateRight">
             <el-icon>
               <RefreshRight/>
             </el-icon>
           </div>
         </div>
         <div class="test-button">
           <el-button v-if="!isLoading" class="sureBtn" @click="clickSure" type="primary">确 定</el-button>
           <el-button v-else class="sureBtn" type="primary" :loading="isLoading">上传中</el-button>
           <el-button class="closeBtn" @click="closeShow">取 消</el-button>
         </div>
       </el-dialog>
     </div>
    </template>
    
    <script setup lang="ts">
    import {VueCropper} from "vue-cropper"
    import {Plus, ZoomIn, ZoomOut, RefreshRight, RefreshLeft, Delete} from '@element-plus/icons-vue'
    import {ref, reactive, computed} from 'vue'
    import {imageUpload} from "@/components/cropper/api/cropper";  // 上传图片的接口 生成url地址
    
    const props = defineProps(['getPropsUrl', 'imageUrl', 'cropOption'])
    const cropper = ref(VueCropper)
    
    let previews = ref({})  // 即时预览
    
    const option = reactive({
    	img: '',  // 上传图片地址
    	 size: 1,  // 输出图片大小
    	 full: true,  // 是否输出原图比例
    	 outputType: 'png',  // 输出图片格式
    	 canMove: true, // 图片是否可移动
    	 fixedBox: true,  // 截图框是否固定
    	 original: true,  // 图片是否按比例
    	 canMoveBox: false, // 截图框是否可移动
    	 autoCrop: true,  // 是否开启截图
    	 // 只有自动截图开启 宽度高度才生效
    	 autoCropWidth: '',  // 输出图片宽高
    	 autoCropHeight: '',  // 输出图片宽高
    	 centerBox: true,  // 是否开启截图框固定宽高
    	 high: true,  // 是否开启图片移动
    	 max: 99999  // 输出图片大小限制
    })
    
    const show = ref(false)  // 显示裁剪弹窗
    const isLoading = ref(false)  // 上传图片时的过渡效果
    const updateVisible = ref(false)  // 显示预览弹窗
    const fixed = ref(false)  // 是否开启截图框固定宽高
    const imageUrl = computed(() => props.imageUrl) // 图片地址
    const fixedNumber = ref([16, 9])  // 截图框固定宽高
    
    /**
    * 关闭弹窗
    */
    const closeShow = () => {
    	 option.img = ''
    	 show.value = false
    	 props.getPropsUrl('')
    }
    
    /**
    * 删除图片
    */
    const deleteFn = () => {
    	 props.getPropsUrl('')
    }
    
    /**
    * 图片缩放
    * @param num
    */
    const changeScale = (num: any) => {
    	 num = num || 1
    	 cropper.value.changeScale(num)
    }
    
    /**
    * 图片旋转 - 顺时针
    */
    const rotateLeft = () => {
    	 cropper.value.rotateLeft()
    }
    
    /**
    * 图片旋转 - 逆时针
    */
    const rotateRight = () => {
     	cropper.value.rotateRight()
    }
    
    /**
    * 实时预览函数
    * @param data
    */
    const realTime = (data: any) => {
    	 previews.value = data
    }
    
    /**
    * 确定按钮
    */
    const clickSure = () => {
    	 isLoading.value = true  //  上传图片时的过渡效果
    	 cropper.value.getCropBlob(async (data: any) => {
    	   const imgUrl = window.URL.createObjectURL(data);
    
           const response = await fetch(imgUrl);
           const blobData = await response.blob();
    
           const formData = new FormData();
           formData.append('file', blobData, 'filename.png');
           imageUpload(formData).then(res => {
    		  isLoading.value = false
    		  show.value = false
    		  props.getPropsUrl(res.data.data.url)
            });
    	 })
    }
    
    /**
    * 上传图片
    * @param e
    * @param num
    */
    const uploadImg = (e: any, num: any) => {
    	 var file = e.target.files[0]
    	 show.value = true
    	 console.log(file, show.value)
    	 if (!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(e.target.value)) {
    	   alert('图片类型必须是.gif,jpeg,jpg,png,bmp中的一种,请重新选择')
    	   return false
    	 }
    	 var reader = new FileReader()
    	
    	 reader.onload = (e: any) => {
    	   let data
    	   if (typeof e.target.result === 'object') {
    	     // 把Array Buffer转化为blob 如果是base64不需要
    	     data = window.URL.createObjectURL(new Blob([e.target.result]))
    	   } else {
    	     data = e.target.result
    	   }
    	   if (num === 1) {
    	     option.img = data
    	   } else if (num === 2) {
    	     example2.value.img = data
    	   }
    	 }
    	
    	 // 转化为base64
    	 // reader.readAsDataURL(file)
    	 // 转化为blob
    	 reader.readAsArrayBuffer(file);
    }
    </script>
    

    vue2与vue3样式一样

    <style scoped>  
    * {
      margin: 0;
      padding: 0;
    }
    
    .cut {
      width: 480px;
      height: 300px;
      flex-shrink: 0;
      display: flex;
      margin-bottom: 20px;
      box-sizing: border-box;
      overflow: hidden;
    }
    
    .cropperModal {
      width: 4636px;
      height: 300px;
    }
    
    .c-item {
      max-width: 800px;
      margin: 20px auto 10px;
    }
    
    .content {
      max-width: 1200px;
      margin: auto auto 100px;
    }
    
    .test-button {
      display: flex;
      align-content: center;
      justify-content: flex-end;
    }
    
    .btnUp {
      width: 200px;
      height: 140px;
      display: inline-block;
      border-radius: 5px;
      border: 1px solid #DCDFE6;
      box-sizing: border-box;
      background-color: #fbfdff;
      cursor: pointer;
      text-align: center;
      line-height: 140px;
      font-size: 20px;
      color: #DCDFE6;
      position: relative;
    }
    
    .btnUp i {
      width: 100%;
      height: 100%;
      display: flex;
      position: absolute;
      top: 0;
      left: 0;
      align-items: center;
      justify-content: center;
    }
    
    .showImg {
      height: 140px;
      display: inline-block;
      border-radius: 5px;
      border: 1px solid #DCDFE6;
      box-sizing: border-box;
      background-color: #fbfdff;
      cursor: pointer;
      text-align: center;
      line-height: 140px;
      font-size: 20px;
      color: #DCDFE6;
      position: relative;
    }
    
    .showImg img {
      width: 100%;
      height: 100%;
    }
    
    .showImg:hover .powerBtn {
      visibility: visible;
    }
    
    #uploads {
      position: absolute;
      clip: rect(0 0 0 0)
    }
    
    .tip {
      color: #8c939d;
      display: flex;
      justify-content: center;
      font-size: 12px;
    }
    
    .powerBtn {
      visibility: hidden;
      position: absolute;
      font-size: 24px;
      width: 24px;
      height: 24px;
      color: #fefeff;
      cursor: pointer;
    }
    
    
    .sureBtn {
      display: inline-block;
      line-height: 1;
      white-space: nowrap;
      cursor: pointer;
      text-align: center;
      box-sizing: border-box;
      outline: none;
      margin: 20px 10px 0 0;
      padding: 9px 15px;
      font-size: 14px;
      border-radius: 4px;
      color: #fff;
      background-color: #409EFF;
      border-color: #409EFF;
      transition: all .2s ease;
      text-decoration: none;
      user-select: none;
    }
    
    .closeBtn {
      display: inline-block;
      line-height: 1;
      white-space: nowrap;
      cursor: pointer;
      background: #fff;
      border: 1px solid #c0ccda;
      color: #1f2d3d;
      text-align: center;
      box-sizing: border-box;
      outline: none;
      margin: 20px 10px 0 0;
      padding: 9px 15px;
      font-size: 14px;
      border-radius: 4px;
      transition: all .2s ease;
      text-decoration: none;
      user-select: none;
    }
    
    .menuBox {
      display: flex;
      box-sizing: border-box;
      align-items: center;
      height: 30px;
      width: 460px;
      justify-content: center;
    }
    
    .menuBtn {
      white-space: nowrap;
      cursor: pointer;
      background: #ff4d51;
      color: #fff;
      text-align: center;
      box-sizing: border-box;
      outline: none;
      margin-right: 10px;
      padding: 9px 15px;
      font-size: 14px;
      border-radius: 4px;
      transition: all .2s ease;
      text-decoration: none;
      user-select: none;
      height: 30px;
      width: 50px;
    }
    
    .des {
      line-height: 30px;
    }
    
    code.language-html {
      padding: 10px 20px;
      margin: 10px 0;
      display: block;
      background-color: #333;
      color: #fff;
      overflow-x: auto;
      font-family: Consolas, Monaco, Droid, Sans, Mono, Source, Code, Pro, Menlo, Lucida, Sans, Type, Writer, Ubuntu, Mono;
      border-radius: 5px;
      white-space: pre;
    }
    
    .show-info {
      margin-bottom: 50px;
    }
    
    .show-info h2 {
      line-height: 50px;
    }
    
    .title {
      display: block;
      text-decoration: none;
      text-align: center;
      line-height: 1.5;
      margin: 20px 0px;
      background-image: -webkit-linear-gradient(left, #3498db, #f47920 10%, #d71345 20%, #f7acbc 30%, #ffd400 40%, #3498db 50%, #f47920 60%, #d71345 70%, #f7acbc 80%, #ffd400 90%, #3498db);
      color: transparent;
      -webkit-background-clip: text;
      background-size: 200% 100%;
      animation: slide 5s infinite linear;
      font-size: 40px;
    }
    
    .test {
      height: 500px;
    }
    
    .model {
      position: fixed;
      z-index: 10;
      width: 100vw;
      height: 100vh;
      overflow: auto;
      top: 0;
      left: 0;
      background: rgba(0, 0, 0, 0.8);
    }
    
    .model-show {
      display: flex;
      justify-content: center;
      align-items: center;
      width: 100vw;
      height: 100vh;
    }
    
    .model img {
      display: block;
      margin: auto;
      max-width: 80%;
      user-select: none;
      background-position: 0 0, 10px 10px;
      background-size: 20px 20px;
      background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee 100%), linear-gradient(45deg, #eee 25%, white 25%, white 75%, #eee 75%, #eee 100%);
    }
    
    .c-item {
      display: block;
      user-select: none;
    }
    
    @keyframes slide {
      0% {
        background-position: 0 0;
      }
      100% {
        background-position: -100% 0;
      }
    }
    </style>
    

好了,以上就是vue2与vue3结合vue-cropper的图片裁剪组件的封装,下次结合饿了么el-upload再进行封装,欢迎评论留言!!

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现Vue裁剪上传,可以使用vue-cropper组件。以下是一个简单的实现过程: 1. 首先,在Vue项目中安装vue-cropper组件。可以使用npm或yarn来安装,命令如下: ``` npm install vue-cropper ``` 2. 在需要使用图裁剪上传的组件中,引入vue-cropper组件。可以在组件的template中添加以下代码: ```html <template> <div> <vue-cropper ref="cropper" :src="imageSrc" :guides="true" :view-mode="1" :auto-crop-area="0.8" ></vue-cropper> <button @click="cropImage">裁剪并上传</button> </div> </template> ``` 3. 在组件的script部分,添加必要的代码。首先,引入vue-cropper组件: ```javascript import VueCropper from 'vue-cropper' ``` 然后,在components中注册vue-cropper组件: ```javascript components: { VueCropper }, ``` 接下来,定义data中的imageSrc属性,用于展示需要裁剪的图: ```javascript data() { return { imageSrc: '图路径' } }, ``` 4. 实现裁剪并上传功能。在methods中,定义cropImage方法: ```javascript methods: { cropImage() { const cropper = this.$refs.cropper const imageData = cropper.getCroppedCanvas().toDataURL('image/jpeg') // 将imageData发送到后端进行上传处理 // ... } }, ``` 在cropImage方法中,通过this.$refs.cropper获取vue-cropper组件实例,并使用getCroppedCanvas方法获取裁剪后的图数据。最后,将图数据发送到后端进行上传处理。 这样,就实现了Vue裁剪上传的功能。你可以根据具体的需求,自定义vue-cropper组件的属性和方法,来实现更多的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值