lex 1 år sedan
förälder
incheckning
85ea348c15

+ 428 - 428
src/components/col-cropper/index.tsx

@@ -1,428 +1,428 @@
-import { defineComponent } from 'vue'
-import 'vue-cropper/dist/index.css'
-import { VueCropper } from 'vue-cropper'
-import styles from './index.module.less'
-
-export default defineComponent({
-  name: 'col-cropper',
-  components: { VueCropper },
-  props: {
-    hideInput: {
-      type: Boolean,
-      default: false
-    },
-    option: {
-      type: Object
-    },
-    onCancelTailor: {
-      type: Function,
-      default: () => {}
-    }, // 取消
-    getBase64Data: {
-      type: Function,
-      default: () => {}
-    },
-    getBlob: {
-      type: Function,
-      default: () => {}
-    },
-    getFile: {
-      type: Function,
-      default: () => {}
-    },
-    imgOriginF: {
-      type: Function,
-      default: () => {}
-    }
-  },
-  data() {
-    return {
-      img: '',
-      config: {
-        ceilbutton: false, //顶部按钮,默认底部
-        outputSize: 1, //裁剪生成图片的质量
-        outputType: 'png', //裁剪生成图片的格式,默认png
-        info: false, //裁剪框的大小信息
-        canScale: true, //图片是否允许滚轮缩放
-        autoCrop: false, //是否默认生成截图框
-        autoCropWidth: 0, //默认生成截图框宽度
-        autoCropHeight: 0, //默认生成截图框高度
-        fixed: true, //是否开启截图框宽高固定比例
-        fixedNumber: [1, 1], //截图框的宽高比例
-        full: false, //是否输出原图比例的截图
-        fixedBox: true, //固定截图框大小 不允许改变
-        canMove: true, //上传图片是否可以移动
-        canMoveBox: false, //截图框能否拖动
-        original: false, //上传图片按照原始比例渲染
-        centerBox: true, //截图框是否被限制在图片里面
-        high: true, //是否按照设备的dpr 输出等比例图片
-        infoTrue: false, //true 为展示真实输出图片宽高 false 展示看到的截图框宽高
-        maxImgSize: 2000, //限制图片最大宽度和高度
-        enlarge: 1, //图片根据截图框输出比例倍数
-        mode: '100%', //图片默认渲染方式
-        cancelButtonText: '取消', //取消按钮文本
-        confirmButtonText: '确定', //确定按钮文本
-        cancelButtonBackgroundColor: '#606266', //取消按钮背景色
-        confirmButtonBackgroundColor: '#ed594c', //确定按钮背景色
-        cancelButtonTextColor: '#ffffff', //取消按钮字体色
-        confirmButtonTextColor: '#ffffff' //确定按钮字体色
-      }
-    }
-  },
-  mounted() {
-    this.config = Object.assign(this.config, this.option)
-  },
-  methods: {
-    //添加网格线
-    addSlide() {
-      if (document.getElementById('vertical') == null) {
-        let box = document.getElementsByClassName('cropper-crop-box')[0]
-        //左网格线
-        let verticalLeft = document.createElement('div')
-        verticalLeft.id = 'vertical'
-        verticalLeft.style.width = '1px'
-        verticalLeft.style.height = '100%'
-        verticalLeft.style.top = '0px'
-        verticalLeft.style.left = '33%'
-        verticalLeft.style.position = 'absolute'
-        verticalLeft.style.backgroundColor = '#fff'
-        verticalLeft.style.zIndex = '522'
-        verticalLeft.style.opacity = '0.5'
-        //右网格线
-        let verticalRight = document.createElement('div')
-        verticalRight.style.width = '1px'
-        verticalRight.style.height = '100%'
-        verticalRight.style.top = '0px'
-        verticalRight.style.right = '33%'
-        verticalRight.style.position = 'absolute'
-        verticalRight.style.backgroundColor = '#fff'
-        verticalRight.style.zIndex = '522'
-        verticalRight.style.opacity = '0.5'
-        //上网格线
-        let verticalTop = document.createElement('div')
-        verticalTop.style.width = '100%'
-        verticalTop.style.height = '1px'
-        verticalTop.style.top = '33%'
-        verticalTop.style.left = '0px'
-        verticalTop.style.position = 'absolute'
-        verticalTop.style.backgroundColor = '#fff'
-        verticalTop.style.zIndex = '522'
-        verticalTop.style.opacity = '0.5'
-        //下网格线
-        let verticalBottom = document.createElement('div')
-        verticalBottom.style.width = '100%'
-        verticalBottom.style.height = '1px'
-        verticalBottom.style.bottom = '33%'
-        verticalBottom.style.left = '0px'
-        verticalBottom.style.position = 'absolute'
-        verticalBottom.style.backgroundColor = '#fff'
-        verticalBottom.style.zIndex = '522'
-        verticalBottom.style.opacity = '0.5'
-        //左上边线
-        let LeftTopSide = document.createElement('div')
-        LeftTopSide.style.width = '30px'
-        LeftTopSide.style.height = '4px'
-        LeftTopSide.style.top = '-4px'
-        LeftTopSide.style.left = '-4px'
-        LeftTopSide.style.position = 'absolute'
-        LeftTopSide.style.backgroundColor = '#fff'
-        LeftTopSide.style.zIndex = '522'
-        LeftTopSide.style.opacity = '1'
-        //上左边线
-        let TopListSide = document.createElement('div')
-        TopListSide.style.width = '4px'
-        TopListSide.style.height = '30px'
-        TopListSide.style.top = '-4px'
-        TopListSide.style.left = '-4px'
-        TopListSide.style.position = 'absolute'
-        TopListSide.style.backgroundColor = '#fff'
-        TopListSide.style.zIndex = '522'
-        TopListSide.style.opacity = '1'
-        //右上边线
-        let RightTopSide = document.createElement('div')
-        RightTopSide.style.width = '30px'
-        RightTopSide.style.height = '4px'
-        RightTopSide.style.top = '-4px'
-        RightTopSide.style.right = '-4px'
-        RightTopSide.style.position = 'absolute'
-        RightTopSide.style.backgroundColor = '#fff'
-        RightTopSide.style.zIndex = '522'
-        RightTopSide.style.opacity = '1'
-        //上右边线
-        let TopRightSide = document.createElement('div')
-        TopRightSide.style.width = '4px'
-        TopRightSide.style.height = '30px'
-        TopRightSide.style.top = '-4px'
-        TopRightSide.style.right = '-4px'
-        TopRightSide.style.position = 'absolute'
-        TopRightSide.style.backgroundColor = '#fff'
-        TopRightSide.style.zIndex = '522'
-        TopRightSide.style.opacity = '1'
-        //左下边线
-        let LeftBottomSide = document.createElement('div')
-        LeftBottomSide.style.width = '30px'
-        LeftBottomSide.style.height = '4px'
-        LeftBottomSide.style.bottom = '-4px'
-        LeftBottomSide.style.left = '-4px'
-        LeftBottomSide.style.position = 'absolute'
-        LeftBottomSide.style.backgroundColor = '#fff'
-        LeftBottomSide.style.zIndex = '522'
-        LeftBottomSide.style.opacity = '1'
-        //下左边线
-        let BottomListSide = document.createElement('div')
-        BottomListSide.style.width = '4px'
-        BottomListSide.style.height = '30px'
-        BottomListSide.style.bottom = '-4px'
-        BottomListSide.style.left = '-4px'
-        BottomListSide.style.position = 'absolute'
-        BottomListSide.style.backgroundColor = '#fff'
-        BottomListSide.style.zIndex = '522'
-        BottomListSide.style.opacity = '1'
-        //右下边线
-        let RightBottomSide = document.createElement('div')
-        RightBottomSide.style.width = '30px'
-        RightBottomSide.style.height = '4px'
-        RightBottomSide.style.bottom = '-4px'
-        RightBottomSide.style.right = '-4px'
-        RightBottomSide.style.position = 'absolute'
-        RightBottomSide.style.backgroundColor = '#fff'
-        RightBottomSide.style.zIndex = '522'
-        RightBottomSide.style.opacity = '1'
-        //下右边线
-        let BottomRightSide = document.createElement('div')
-        BottomRightSide.style.width = '4px'
-        BottomRightSide.style.height = '30px'
-        BottomRightSide.style.bottom = '-4px'
-        BottomRightSide.style.right = '-4px'
-        BottomRightSide.style.position = 'absolute'
-        BottomRightSide.style.backgroundColor = '#fff'
-        BottomRightSide.style.zIndex = '522'
-        BottomRightSide.style.opacity = '1'
-        //一起生成
-        box.appendChild(verticalLeft)
-        box.appendChild(verticalRight)
-        box.appendChild(verticalTop)
-        box.appendChild(verticalBottom)
-        box.appendChild(LeftTopSide)
-        box.appendChild(TopListSide)
-        box.appendChild(RightTopSide)
-        box.appendChild(TopRightSide)
-        box.appendChild(LeftBottomSide)
-        box.appendChild(BottomListSide)
-        box.appendChild(RightBottomSide)
-        box.appendChild(BottomRightSide)
-      }
-    },
-    //异步onload图片
-    onLoadImg(photoUrl: any) {
-      return new Promise(function (resolve, reject) {
-        let reader = new FileReader()
-        reader.readAsDataURL(photoUrl)
-        reader.onload = (e: any) => {
-          resolve(e.target['result'])
-        }
-      })
-    },
-    /**
-     * 载入文件
-     * template:
-     *    <h5-cropper hide-input ref="cropper">
-     *
-     * javascript:
-     *    this.$refs.cropper.loadFile()
-     *
-     * @param file
-     */
-    loadFile(file: any) {
-      if (file instanceof File) {
-        this.onLoadImg(file).then((base64: any) => {
-          this.img = base64
-          setTimeout(() => {
-            this.config.autoCrop = true
-            this.addSlide()
-          }, 10)
-        })
-      } else {
-        throw new Error('Arguments file is not File')
-      }
-    },
-    /**
-     *
-     * @param base64
-     */
-    loadBase64(base64: string) {
-      if (typeof base64 !== 'string') {
-        throw new Error('Arguments base64 is not string')
-      }
-      const base = base64.split(',')
-      if (!/^data:image\/(.*?);base64$/.test(base[0])) {
-        throw new Error('Arguments base64 MIME is not image/*')
-      }
-      // Base64 Regex @see https://learnku.com/articles/42295
-      if (
-        !/^[\/]?([\da-zA-Z]+[\/+]+)*[\da-zA-Z]+([+=]{1,2}|[\/])?$/.test(base[1])
-      ) {
-        throw new Error('Not standard base64')
-      }
-      this.img = base64
-      setTimeout(() => {
-        this.config.autoCrop = true
-        this.addSlide()
-      }, 10)
-    },
-    rotating(e: any) {
-      ;(this as any).$refs.cropper.rotateRight()
-      // document.getElementsByClassName("cropper-modal")[0].style = "background-color: rgba(0,0,0,0.5);transition: 0.88s";
-    },
-    canceltailor() {
-      this.img = ''
-      this.onCancelTailor()
-    },
-    tailoring() {
-      // 获取截图的base64数据
-      ;(this as any).$refs.cropper.getCropData((data: any) => {
-        this.getBase64Data(data)
-        this.getBlob(data)
-        this.img = ''
-        this.config.autoCrop = false
-      })
-      // 获取截图的blob数据
-      ;(this as any).$refs.cropper.getCropBlob((data: BlobPart) => {
-        this.getBase64Data(data)
-        this.getBlob(data)
-        // Blob 转 File
-        const suffix = {
-          jpeg: 'jpg',
-          png: 'png',
-          webp: 'webp'
-        }[this.config.outputType]
-        const time = new Date().getTime()
-        const file = new File([data], `${time}.${suffix}`, {
-          type: `image/${this.config.outputType}`
-        })
-        this.getFile(file)
-        this.img = ''
-        this.config.autoCrop = false
-      })
-    },
-    async upPhoto(e: any) {
-      let photoUrl = e.target.files[0]
-      ;(this as any).$refs.headInput.value = null
-      if (photoUrl != undefined) {
-        this.imgOriginF(photoUrl)
-        this.img = (await this.onLoadImg(photoUrl)) as string
-        this.config.autoCrop = true
-        setTimeout(() => {
-          this.addSlide()
-        }, 20)
-      }
-    },
-    onCropMoving(e: any) {
-      // console.log('onCropMoving')
-    },
-    onImgMoving(e: any) {
-      // console.log('onCropMoving')
-    }
-  },
-  render() {
-    return (
-      <div class={[styles.upbtn, styles.uploadWarper]}>
-        {this.hideInput}
-        {!this.hideInput ? (
-          <input
-            style="opacity: 0;"
-            class={styles.upbtn}
-            type="file"
-            accept="image/*"
-            onChange={this.upPhoto}
-            ref="headInput"
-          />
-        ) : null}
-        {this.img != '' ? (
-          <div class={styles.bg}>
-            {this.config.ceilbutton ? (
-              <div class={styles.btndiv}>
-                <div
-                  class={styles.btn}
-                  onClick={this.canceltailor}
-                  style={{
-                    backgroundColor: this.config.cancelButtonBackgroundColor,
-                    color: this.config.cancelButtonTextColor
-                  }}
-                >
-                  {this.config.cancelButtonText}
-                </div>
-                <div class={styles.img} onClick={this.rotating}></div>
-                <div
-                  class={styles.btn}
-                  onClick={this.tailoring}
-                  style={{
-                    backgroundColor: this.config.confirmButtonBackgroundColor,
-                    color: this.config.confirmButtonTextColor
-                  }}
-                >
-                  {this.config.confirmButtonText}
-                </div>
-              </div>
-            ) : null}
-
-            <div class={styles.wrapper}>
-              <VueCropper
-                ref="cropper"
-                img={this.img}
-                outputSize={this.config.outputSize}
-                outputType={this.config.outputType}
-                info={this.config.info}
-                canScale={this.config.canScale}
-                autoCrop={this.config.autoCrop}
-                autoCropWidth={this.config.autoCropWidth}
-                autoCropHeight={this.config.autoCropHeight}
-                fixedBox={this.config.fixedBox}
-                fixed={this.config.fixed}
-                fixedNumber={this.config.fixedNumber}
-                full={this.config.full}
-                canMove={this.config.canMove}
-                canMoveBox={this.config.canMoveBox}
-                original={this.config.original}
-                centerBox={this.config.centerBox}
-                high={this.config.high}
-                infoTrue={this.config.infoTrue}
-                maxImgSize={this.config.maxImgSize}
-                enlarge={this.config.enlarge}
-                mode={this.config.mode}
-                onCropMoving={this.onCropMoving}
-                onImgMoving={this.onImgMoving}
-              ></VueCropper>
-            </div>
-
-            {!this.config.ceilbutton ? (
-              <div class={styles.btndiv}>
-                <div
-                  class={styles.btn}
-                  onClick={this.canceltailor}
-                  style={{
-                    backgroundColor: this.config.cancelButtonBackgroundColor,
-                    color: this.config.cancelButtonTextColor
-                  }}
-                >
-                  {this.config.cancelButtonText}
-                </div>
-                <div class={styles.img} onClick={this.rotating}></div>
-                <div
-                  class={styles.btn}
-                  onClick={this.tailoring}
-                  style={{
-                    backgroundColor: this.config.confirmButtonBackgroundColor,
-                    color: this.config.confirmButtonTextColor
-                  }}
-                >
-                  {this.config.confirmButtonText}
-                </div>
-              </div>
-            ) : null}
-          </div>
-        ) : null}
-      </div>
-    )
-  }
-})
+import { defineComponent } from 'vue'
+import 'vue-cropper/dist/index.css'
+import { VueCropper } from 'vue-cropper'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'col-cropper',
+  components: { VueCropper },
+  props: {
+    hideInput: {
+      type: Boolean,
+      default: false
+    },
+    option: {
+      type: Object
+    },
+    onCancelTailor: {
+      type: Function,
+      default: () => {}
+    }, // 取消
+    getBase64Data: {
+      type: Function,
+      default: () => {}
+    },
+    getBlob: {
+      type: Function,
+      default: () => {}
+    },
+    getFile: {
+      type: Function,
+      default: () => {}
+    },
+    imgOriginF: {
+      type: Function,
+      default: () => {}
+    }
+  },
+  data() {
+    return {
+      img: '',
+      config: {
+        ceilbutton: false, //顶部按钮,默认底部
+        outputSize: 1, //裁剪生成图片的质量
+        outputType: 'png', //裁剪生成图片的格式,默认png
+        info: false, //裁剪框的大小信息
+        canScale: true, //图片是否允许滚轮缩放
+        autoCrop: false, //是否默认生成截图框
+        autoCropWidth: 0, //默认生成截图框宽度
+        autoCropHeight: 0, //默认生成截图框高度
+        fixed: true, //是否开启截图框宽高固定比例
+        fixedNumber: [1, 1], //截图框的宽高比例
+        full: false, //是否输出原图比例的截图
+        fixedBox: true, //固定截图框大小 不允许改变
+        canMove: true, //上传图片是否可以移动
+        canMoveBox: false, //截图框能否拖动
+        original: false, //上传图片按照原始比例渲染
+        centerBox: true, //截图框是否被限制在图片里面
+        high: true, //是否按照设备的dpr 输出等比例图片
+        infoTrue: false, //true 为展示真实输出图片宽高 false 展示看到的截图框宽高
+        maxImgSize: 2000, //限制图片最大宽度和高度
+        enlarge: 1, //图片根据截图框输出比例倍数
+        mode: '100%', //图片默认渲染方式
+        cancelButtonText: '取消', //取消按钮文本
+        confirmButtonText: '确定', //确定按钮文本
+        cancelButtonBackgroundColor: '#606266', //取消按钮背景色
+        confirmButtonBackgroundColor: '#ed594c', //确定按钮背景色
+        cancelButtonTextColor: '#ffffff', //取消按钮字体色
+        confirmButtonTextColor: '#ffffff' //确定按钮字体色
+      }
+    }
+  },
+  mounted() {
+    this.config = Object.assign(this.config, this.option)
+  },
+  methods: {
+    //添加网格线
+    addSlide() {
+      if (document.getElementById('vertical') == null) {
+        const box = document.getElementsByClassName('cropper-crop-box')[0]
+        //左网格线
+        const verticalLeft = document.createElement('div')
+        verticalLeft.id = 'vertical'
+        verticalLeft.style.width = '1px'
+        verticalLeft.style.height = '100%'
+        verticalLeft.style.top = '0px'
+        verticalLeft.style.left = '33%'
+        verticalLeft.style.position = 'absolute'
+        verticalLeft.style.backgroundColor = '#fff'
+        verticalLeft.style.zIndex = '522'
+        verticalLeft.style.opacity = '0.5'
+        //右网格线
+        const verticalRight = document.createElement('div')
+        verticalRight.style.width = '1px'
+        verticalRight.style.height = '100%'
+        verticalRight.style.top = '0px'
+        verticalRight.style.right = '33%'
+        verticalRight.style.position = 'absolute'
+        verticalRight.style.backgroundColor = '#fff'
+        verticalRight.style.zIndex = '522'
+        verticalRight.style.opacity = '0.5'
+        //上网格线
+        const verticalTop = document.createElement('div')
+        verticalTop.style.width = '100%'
+        verticalTop.style.height = '1px'
+        verticalTop.style.top = '33%'
+        verticalTop.style.left = '0px'
+        verticalTop.style.position = 'absolute'
+        verticalTop.style.backgroundColor = '#fff'
+        verticalTop.style.zIndex = '522'
+        verticalTop.style.opacity = '0.5'
+        //下网格线
+        const verticalBottom = document.createElement('div')
+        verticalBottom.style.width = '100%'
+        verticalBottom.style.height = '1px'
+        verticalBottom.style.bottom = '33%'
+        verticalBottom.style.left = '0px'
+        verticalBottom.style.position = 'absolute'
+        verticalBottom.style.backgroundColor = '#fff'
+        verticalBottom.style.zIndex = '522'
+        verticalBottom.style.opacity = '0.5'
+        //左上边线
+        const LeftTopSide = document.createElement('div')
+        LeftTopSide.style.width = '30px'
+        LeftTopSide.style.height = '4px'
+        LeftTopSide.style.top = '-4px'
+        LeftTopSide.style.left = '-4px'
+        LeftTopSide.style.position = 'absolute'
+        LeftTopSide.style.backgroundColor = '#fff'
+        LeftTopSide.style.zIndex = '522'
+        LeftTopSide.style.opacity = '1'
+        //上左边线
+        const TopListSide = document.createElement('div')
+        TopListSide.style.width = '4px'
+        TopListSide.style.height = '30px'
+        TopListSide.style.top = '-4px'
+        TopListSide.style.left = '-4px'
+        TopListSide.style.position = 'absolute'
+        TopListSide.style.backgroundColor = '#fff'
+        TopListSide.style.zIndex = '522'
+        TopListSide.style.opacity = '1'
+        //右上边线
+        const RightTopSide = document.createElement('div')
+        RightTopSide.style.width = '30px'
+        RightTopSide.style.height = '4px'
+        RightTopSide.style.top = '-4px'
+        RightTopSide.style.right = '-4px'
+        RightTopSide.style.position = 'absolute'
+        RightTopSide.style.backgroundColor = '#fff'
+        RightTopSide.style.zIndex = '522'
+        RightTopSide.style.opacity = '1'
+        //上右边线
+        const TopRightSide = document.createElement('div')
+        TopRightSide.style.width = '4px'
+        TopRightSide.style.height = '30px'
+        TopRightSide.style.top = '-4px'
+        TopRightSide.style.right = '-4px'
+        TopRightSide.style.position = 'absolute'
+        TopRightSide.style.backgroundColor = '#fff'
+        TopRightSide.style.zIndex = '522'
+        TopRightSide.style.opacity = '1'
+        //左下边线
+        const LeftBottomSide = document.createElement('div')
+        LeftBottomSide.style.width = '30px'
+        LeftBottomSide.style.height = '4px'
+        LeftBottomSide.style.bottom = '-4px'
+        LeftBottomSide.style.left = '-4px'
+        LeftBottomSide.style.position = 'absolute'
+        LeftBottomSide.style.backgroundColor = '#fff'
+        LeftBottomSide.style.zIndex = '522'
+        LeftBottomSide.style.opacity = '1'
+        //下左边线
+        const BottomListSide = document.createElement('div')
+        BottomListSide.style.width = '4px'
+        BottomListSide.style.height = '30px'
+        BottomListSide.style.bottom = '-4px'
+        BottomListSide.style.left = '-4px'
+        BottomListSide.style.position = 'absolute'
+        BottomListSide.style.backgroundColor = '#fff'
+        BottomListSide.style.zIndex = '522'
+        BottomListSide.style.opacity = '1'
+        //右下边线
+        const RightBottomSide = document.createElement('div')
+        RightBottomSide.style.width = '30px'
+        RightBottomSide.style.height = '4px'
+        RightBottomSide.style.bottom = '-4px'
+        RightBottomSide.style.right = '-4px'
+        RightBottomSide.style.position = 'absolute'
+        RightBottomSide.style.backgroundColor = '#fff'
+        RightBottomSide.style.zIndex = '522'
+        RightBottomSide.style.opacity = '1'
+        //下右边线
+        const BottomRightSide = document.createElement('div')
+        BottomRightSide.style.width = '4px'
+        BottomRightSide.style.height = '30px'
+        BottomRightSide.style.bottom = '-4px'
+        BottomRightSide.style.right = '-4px'
+        BottomRightSide.style.position = 'absolute'
+        BottomRightSide.style.backgroundColor = '#fff'
+        BottomRightSide.style.zIndex = '522'
+        BottomRightSide.style.opacity = '1'
+        //一起生成
+        box.appendChild(verticalLeft)
+        box.appendChild(verticalRight)
+        box.appendChild(verticalTop)
+        box.appendChild(verticalBottom)
+        box.appendChild(LeftTopSide)
+        box.appendChild(TopListSide)
+        box.appendChild(RightTopSide)
+        box.appendChild(TopRightSide)
+        box.appendChild(LeftBottomSide)
+        box.appendChild(BottomListSide)
+        box.appendChild(RightBottomSide)
+        box.appendChild(BottomRightSide)
+      }
+    },
+    //异步onload图片
+    onLoadImg(photoUrl: any) {
+      return new Promise(function (resolve, reject) {
+        const reader = new FileReader()
+        reader.readAsDataURL(photoUrl)
+        reader.onload = (e: any) => {
+          resolve(e.target['result'])
+        }
+      })
+    },
+    /**
+     * 载入文件
+     * template:
+     *    <h5-cropper hide-input ref="cropper">
+     *
+     * javascript:
+     *    this.$refs.cropper.loadFile()
+     *
+     * @param file
+     */
+    loadFile(file: any) {
+      if (file instanceof File) {
+        this.onLoadImg(file).then((base64: any) => {
+          this.img = base64
+          setTimeout(() => {
+            this.config.autoCrop = true
+            this.addSlide()
+          }, 10)
+        })
+      } else {
+        throw new Error('Arguments file is not File')
+      }
+    },
+    /**
+     *
+     * @param base64
+     */
+    loadBase64(base64: string) {
+      if (typeof base64 !== 'string') {
+        throw new Error('Arguments base64 is not string')
+      }
+      const base = base64.split(',')
+      if (!/^data:image\/(.*?);base64$/.test(base[0])) {
+        throw new Error('Arguments base64 MIME is not image/*')
+      }
+      // Base64 Regex @see https://learnku.com/articles/42295
+      if (
+        !/^[\/]?([\da-zA-Z]+[\/+]+)*[\da-zA-Z]+([+=]{1,2}|[\/])?$/.test(base[1])
+      ) {
+        throw new Error('Not standard base64')
+      }
+      this.img = base64
+      setTimeout(() => {
+        this.config.autoCrop = true
+        this.addSlide()
+      }, 10)
+    },
+    rotating(e: any) {
+      ;(this as any).$refs.cropper.rotateRight()
+      // document.getElementsByClassName("cropper-modal")[0].style = "background-color: rgba(0,0,0,0.5);transition: 0.88s";
+    },
+    canceltailor() {
+      this.img = ''
+      this.onCancelTailor()
+    },
+    tailoring() {
+      // 获取截图的base64数据
+      ;(this as any).$refs.cropper.getCropData((data: any) => {
+        this.getBase64Data(data)
+        this.getBlob(data)
+        this.img = ''
+        this.config.autoCrop = false
+      })
+      // 获取截图的blob数据
+      ;(this as any).$refs.cropper.getCropBlob((data: BlobPart) => {
+        this.getBase64Data(data)
+        this.getBlob(data)
+        // Blob 转 File
+        const suffix = {
+          jpeg: 'jpg',
+          png: 'png',
+          webp: 'webp'
+        }[this.config.outputType]
+        const time = new Date().getTime()
+        const file = new File([data], `${time}.${suffix}`, {
+          type: `image/${this.config.outputType}`
+        })
+        this.getFile(file)
+        this.img = ''
+        this.config.autoCrop = false
+      })
+    },
+    async upPhoto(e: any) {
+      const photoUrl = e.target.files[0]
+      ;(this as any).$refs.headInput.value = null
+      if (photoUrl != undefined) {
+        this.imgOriginF(photoUrl)
+        this.img = (await this.onLoadImg(photoUrl)) as string
+        this.config.autoCrop = true
+        setTimeout(() => {
+          this.addSlide()
+        }, 20)
+      }
+    },
+    onCropMoving(e: any) {
+      // console.log('onCropMoving')
+    },
+    onImgMoving(e: any) {
+      // console.log('onCropMoving')
+    }
+  },
+  render() {
+    return (
+      <div class={[styles.upbtn, styles.uploadWarper]}>
+        {this.hideInput}
+        {!this.hideInput ? (
+          <input
+            style="opacity: 0;"
+            class={styles.upbtn}
+            type="file"
+            accept="image/*"
+            onChange={this.upPhoto}
+            ref="headInput"
+          />
+        ) : null}
+        {this.img != '' ? (
+          <div class={styles.bg}>
+            {this.config.ceilbutton ? (
+              <div class={styles.btndiv}>
+                <div
+                  class={styles.btn}
+                  onClick={this.canceltailor}
+                  style={{
+                    backgroundColor: this.config.cancelButtonBackgroundColor,
+                    color: this.config.cancelButtonTextColor
+                  }}
+                >
+                  {this.config.cancelButtonText}
+                </div>
+                <div class={styles.img} onClick={this.rotating}></div>
+                <div
+                  class={styles.btn}
+                  onClick={this.tailoring}
+                  style={{
+                    backgroundColor: this.config.confirmButtonBackgroundColor,
+                    color: this.config.confirmButtonTextColor
+                  }}
+                >
+                  {this.config.confirmButtonText}
+                </div>
+              </div>
+            ) : null}
+
+            <div class={styles.wrapper}>
+              <VueCropper
+                ref="cropper"
+                img={this.img}
+                outputSize={this.config.outputSize}
+                outputType={this.config.outputType}
+                info={this.config.info}
+                canScale={this.config.canScale}
+                autoCrop={this.config.autoCrop}
+                autoCropWidth={this.config.autoCropWidth}
+                autoCropHeight={this.config.autoCropHeight}
+                fixedBox={this.config.fixedBox}
+                fixed={this.config.fixed}
+                fixedNumber={this.config.fixedNumber}
+                full={this.config.full}
+                canMove={this.config.canMove}
+                canMoveBox={this.config.canMoveBox}
+                original={this.config.original}
+                centerBox={this.config.centerBox}
+                high={this.config.high}
+                infoTrue={this.config.infoTrue}
+                maxImgSize={this.config.maxImgSize}
+                enlarge={this.config.enlarge}
+                mode={this.config.mode}
+                onCropMoving={this.onCropMoving}
+                onImgMoving={this.onImgMoving}
+              ></VueCropper>
+            </div>
+
+            {!this.config.ceilbutton ? (
+              <div class={styles.btndiv}>
+                <div
+                  class={styles.btn}
+                  onClick={this.canceltailor}
+                  style={{
+                    backgroundColor: this.config.cancelButtonBackgroundColor,
+                    color: this.config.cancelButtonTextColor
+                  }}
+                >
+                  {this.config.cancelButtonText}
+                </div>
+                <div class={styles.img} onClick={this.rotating}></div>
+                <div
+                  class={styles.btn}
+                  onClick={this.tailoring}
+                  style={{
+                    backgroundColor: this.config.confirmButtonBackgroundColor,
+                    color: this.config.confirmButtonTextColor
+                  }}
+                >
+                  {this.config.confirmButtonText}
+                </div>
+              </div>
+            ) : null}
+          </div>
+        ) : null}
+      </div>
+    )
+  }
+})

+ 5 - 3
src/components/col-upload/index.tsx

@@ -195,7 +195,7 @@ export default defineComponent({
             ) : (
               <div class={styles.uploader}>
                 <Icon name={iconUploader} size="32" />
-                <p class={styles.uploaderText}>{this.tips}</p>
+                {this.tips && <p class={styles.uploaderText}>{this.tips}</p>}
               </div>
             )}
             <ColCropper option={this.options} getFile={this.getFile} />
@@ -220,7 +220,7 @@ export default defineComponent({
             ) : (
               <div class={styles.uploader}>
                 <Icon name={iconUploader} size="32" />
-                <p class={styles.uploaderText}>{this.tips}</p>
+                {this.tips && <p class={styles.uploaderText}>{this.tips}</p>}
               </div>
             )}
           </div>
@@ -241,7 +241,9 @@ export default defineComponent({
                 ) : (
                   <div class={styles.uploader}>
                     <Icon name={iconUploader} size="32" />
-                    <p class={styles.uploaderText}>{this.tips}</p>
+                    {this.tips && (
+                      <p class={styles.uploaderText}>{this.tips}</p>
+                    )}
                   </div>
                 )
             }}

+ 89 - 0
src/views/creation/edit/index.module.less

@@ -1,3 +1,92 @@
+.sectionVideo {
+  position: relative;
+  line-height: 0;
+  overflow: visible !important;
+  margin-bottom: 24px !important;
+
+  .videoBg {
+    width: 100%;
+    height: 160px;
+    object-fit: cover;
+    border-radius: 10px;
+  }
+
+  .btnGroup {
+    position: absolute;
+    left: 50%;
+    bottom: -12px;
+    background: linear-gradient(180deg, rgba(128, 158, 200, 0.59) 0%, rgba(58, 101, 162, 0.59) 100%);
+    border-radius: 15px;
+    height: 30px;
+    // transform: translate(-50%);
+    display: flex;
+    align-items: center;
+    flex: 1;
+    font-size: 12px;
+    font-weight: 500;
+    color: #FFFFFF;
+    line-height: 17px;
+    width: 300px;
+    margin: 0 0 0 -150px;
+
+    &>div {
+      flex: 1;
+      text-align: center;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .btnCropper {
+      border-left: 1px solid #fff;
+
+      &::before {
+        content: '';
+        width: 16px;
+        height: 16px;
+        display: inline-block;
+        background: url('../images/icon-cropper.png') no-repeat center;
+        background-size: contain;
+        margin-right: 4px;
+      }
+    }
+
+    .btnImg {
+      margin: 0;
+      height: inherit;
+      border: 0;
+      border-radius: 0;
+
+      :global {
+        .van-icon {
+          display: flex;
+          align-items: center;
+
+          &::before {
+            content: '';
+            width: 16px;
+            height: 16px;
+            display: inline-block;
+            background: url('../images/icon-image.png') no-repeat center;
+            background-size: contain;
+            margin-right: 4px;
+          }
+
+          &::after {
+            content: '相册获取封面';
+            font-size: 12px;
+            color: #fff;
+          }
+
+          img {
+            display: none;
+          }
+        }
+      }
+    }
+  }
+}
+
 .section {
   margin: 12px 13px;
   background: #FFFFFF;

+ 50 - 0
src/views/creation/edit/index.tsx

@@ -6,6 +6,8 @@ import { Button, Field } from 'vant'
 import MUploader from '@/components/col-upload'
 import { api_userMusicDetail, api_userMusicSave } from '../api'
 import { useRoute, useRouter } from 'vue-router'
+import videoBg from '../images/video-bg.png'
+import { postMessage } from '@/helpers/native-message'
 
 export default defineComponent({
   name: 'creation-edit',
@@ -15,6 +17,7 @@ export default defineComponent({
     const state = reactive({
       id: route.query.id,
       musicDetail: {} as any,
+      playType: '',
       desc: '',
       videoImg: '', // 视频封面
       img: ''
@@ -44,6 +47,24 @@ export default defineComponent({
         //
       }
     }
+
+    // 截图
+    const onCropper = () => {
+      postMessage(
+        {
+          api: 'videoCrop',
+          content: {
+            url: state.musicDetail.videoUrl
+          }
+        },
+        res => {
+          if (res?.content.videoCover) {
+            state.videoImg = res.content.videoCover
+          }
+        }
+      )
+    }
+
     onMounted(async () => {
       try {
         const { data } = await api_userMusicDetail(state.id)
@@ -51,6 +72,12 @@ export default defineComponent({
         state.desc = data.desc
         state.videoImg = data.videoImg
         state.img = data.img
+
+        if (data?.videoUrl.lastIndexOf('mp4') !== -1) {
+          state.playType = 'Video'
+        } else {
+          state.playType = 'Audio'
+        }
       } catch {
         //
       }
@@ -60,6 +87,29 @@ export default defineComponent({
         <MSticky position="top">
           <MHeader border={false} />
         </MSticky>
+        {state.playType === 'Video' && (
+          <div class={[styles.section, styles.sectionVideo]}>
+            <img src={state.videoImg || videoBg} class={styles.videoBg} />
+            <div class={styles.btnGroup}>
+              <MUploader
+                class={styles.btnImg}
+                cropper
+                tips=""
+                deletable={false}
+                onUploadChange={img => {
+                  console.log(img, 'img')
+                  state.videoImg = img
+                }}
+                options={{
+                  fixedNumber: [16, 9]
+                }}
+              />
+              <div class={styles.btnCropper} onClick={onCropper}>
+                视频截取封面
+              </div>
+            </div>
+          </div>
+        )}
 
         <div class={styles.section}>
           <Field

BIN
src/views/creation/images/icon-cropper.png


BIN
src/views/creation/images/icon-image.png


BIN
src/views/creation/images/video-bg.png


+ 2 - 2
src/views/creation/index-share.tsx

@@ -16,6 +16,7 @@ import iconZanActive from './images/icon-zan-active.png'
 import iconZ from './images/icon-z.png'
 import iconPlay from './images/icon-play.png'
 import iconPause from './images/icon-pause.png'
+import videoBg from './images/video-bg.png'
 import {
   browser,
   getAuth,
@@ -235,7 +236,6 @@ export default defineComponent({
 
     const pageVisibility = usePageVisibility()
     watch(pageVisibility, value => {
-      console.log(value)
       if (value === 'hidden') {
         if (audioDom) {
           audioDom.pause()
@@ -272,7 +272,7 @@ export default defineComponent({
           {state.playType === 'Video' && (
             <MVideo
               src={state.musicDetail.videoUrl}
-              poster={state.musicDetail.img}
+              poster={state.musicDetail.videoImg || videoBg}
             />
           )}
           {state.playType === 'Audio' && (

+ 15 - 0
src/views/creation/index.module.less

@@ -1,5 +1,20 @@
 .playSection {
   min-height: 175px;
+
+  :global {
+    .vjs-poster {
+      background-size: cover;
+    }
+
+    .video-js .vjs-progress-control:hover .vjs-progress-holder {
+      font-size: inherit !important;
+    }
+
+    .video-js .vjs-slider:focus {
+      box-shadow: none !important;
+      text-shadow: none !important;
+    }
+  }
 }
 
 @keyframes rotateImg {

+ 2 - 1
src/views/creation/index.tsx

@@ -12,6 +12,7 @@ import iconZan from './images/icon-zan.png'
 import iconZanActive from './images/icon-zan-active.png'
 import iconPlay from './images/icon-play.png'
 import iconPause from './images/icon-pause.png'
+import videoBg from './images/video-bg.png'
 import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'
 import { browser, getGradeCh, getSecondRPM } from '@/helpers/utils'
 import { useRoute, useRouter } from 'vue-router'
@@ -270,7 +271,7 @@ export default defineComponent({
           {state.playType === 'Video' && (
             <MVideo
               src={state.musicDetail?.videoUrl}
-              poster={state.musicDetail?.img}
+              poster={state.musicDetail?.videoBg || videoBg}
             />
           )}
           {state.playType === 'Audio' && (