|
@@ -0,0 +1,427 @@
|
|
|
+// import { ElImage, ElMessage, ElUpload } from 'element-plus'
|
|
|
+import { defineComponent } from 'vue'
|
|
|
+import styles from './index.module.less'
|
|
|
+import iconUpload from '../col-upload/images/icon_upload.png'
|
|
|
+// import Cropper from './cropper'
|
|
|
+import { VueCropper } from 'vue-cropper'
|
|
|
+import umiRequest from 'umi-request'
|
|
|
+import 'vue-cropper/dist/index.css'
|
|
|
+import {
|
|
|
+ ElButton,
|
|
|
+ ElCol,
|
|
|
+ ElDialog,
|
|
|
+ ElIcon,
|
|
|
+ ElImage,
|
|
|
+ ElMessage,
|
|
|
+ ElUpload,
|
|
|
+ ElRow
|
|
|
+} from 'element-plus'
|
|
|
+import { CirclePlus, Remove } from '@element-plus/icons-vue'
|
|
|
+import iconRate from '../col-upload/images/icon_rate.png'
|
|
|
+import request from '@/helpers/request'
|
|
|
+
|
|
|
+export default defineComponent({
|
|
|
+ name: 'col-cropper',
|
|
|
+ props: {
|
|
|
+ modelValue: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ options: {
|
|
|
+ // 裁切需要参数
|
|
|
+ type: Object,
|
|
|
+ default: {
|
|
|
+ autoCrop: true, //是否默认生成截图框
|
|
|
+ enlarge: 1, // 图片放大倍数
|
|
|
+ autoCropWidth: 200, //默认生成截图框宽度
|
|
|
+ autoCropHeight: 200, //默认生成截图框高度
|
|
|
+ fixedBox: true, //是否固定截图框大小 不允许改变
|
|
|
+ previewsCircle: true, //预览图是否是原圆形
|
|
|
+ title: '上传图片'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 显示图片原始图片
|
|
|
+ showSize: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ disabled: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ bucket: {
|
|
|
+ type: String,
|
|
|
+ default: 'daya'
|
|
|
+ },
|
|
|
+ size: {
|
|
|
+ type: Number,
|
|
|
+ default: 5 // 默认5M
|
|
|
+ },
|
|
|
+ accept: {
|
|
|
+ type: String,
|
|
|
+ default: 'images/*'
|
|
|
+ },
|
|
|
+ tips: {
|
|
|
+ type: String,
|
|
|
+ default: '请上传图片'
|
|
|
+ },
|
|
|
+ extraTips: {
|
|
|
+ type: String,
|
|
|
+ default: '图片最大不能超过5MB'
|
|
|
+ },
|
|
|
+ cropUploadSuccess: {
|
|
|
+ type: Function,
|
|
|
+ default: (data: string) => {}
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ isStopRun: false,
|
|
|
+ loading: false,
|
|
|
+ ossUploadUrl: 'https://ks3-cn-beijing.ksyuncs.com/' + this.bucket,
|
|
|
+ dataObj: {
|
|
|
+ policy: '',
|
|
|
+ signature: '',
|
|
|
+ key: '',
|
|
|
+ KSSAccessKeyId: '',
|
|
|
+ acl: 'public-read',
|
|
|
+ name: ''
|
|
|
+ },
|
|
|
+ visible: false,
|
|
|
+ img: null,
|
|
|
+ optionsList: {
|
|
|
+ img: '', //裁剪图片的地址
|
|
|
+ autoCrop: true, //是否默认生成截图框
|
|
|
+ autoCropWidth: 180, //默认生成截图框宽度
|
|
|
+ autoCropHeight: 180, //默认生成截图框高度
|
|
|
+ fixedBox: false, //是否固定截图框大小 不允许改变
|
|
|
+ full: false,
|
|
|
+ enlarge: 1, // 是否按照截图框比例输出 默认为1
|
|
|
+ previewsCircle: true, //预览图是否是原圆形
|
|
|
+ centerBox: true,
|
|
|
+ outputType: 'png',
|
|
|
+ title: '修改头像',
|
|
|
+ name: null // 文件名称
|
|
|
+ },
|
|
|
+ previews: {} as any,
|
|
|
+ url: {
|
|
|
+ upload: '/sys/common/saveToImgByStr'
|
|
|
+ },
|
|
|
+ submitLoading: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ onDelete() {
|
|
|
+ // 删除图片
|
|
|
+ this.$emit('update:modelValue', '')
|
|
|
+ },
|
|
|
+ //从本地选择文件
|
|
|
+ async handleChange(info: any) {
|
|
|
+ if (this.isStopRun) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.loading = true
|
|
|
+ const options = this.options
|
|
|
+ this.getBase64(info.file, (imageUrl: any) => {
|
|
|
+ const target = Object.assign({}, options, {
|
|
|
+ img: imageUrl,
|
|
|
+ name: info.file.name // 上传文件名
|
|
|
+ })
|
|
|
+ // ;(this as any).$refs.CropperModal.edit(target)
|
|
|
+ this.edit(target)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 上传之前 格式与大小校验
|
|
|
+ beforeUpload(file) {
|
|
|
+ this.isStopRun = false
|
|
|
+ var fileType = file.type
|
|
|
+ if (fileType.indexOf('image') < 0) {
|
|
|
+ ElMessage.warning('请上传图片')
|
|
|
+ this.isStopRun = true
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ // const isJpgOrPng = this.acceptArray.includes(file.type)
|
|
|
+ // if (!isJpgOrPng) {
|
|
|
+ // ElMessage.error('你上传图片格式不正确!')
|
|
|
+ // this.isStopRun = true
|
|
|
+ // }
|
|
|
+ console.log(this.size)
|
|
|
+ const size = this.size || 0
|
|
|
+ const isLtSize = file.size < size * 1024 * 1024
|
|
|
+ if (!isLtSize) {
|
|
|
+ ElMessage.error('图片大小不能超过' + this.size + 'MB!')
|
|
|
+ this.isStopRun = true
|
|
|
+ }
|
|
|
+ return isLtSize
|
|
|
+ },
|
|
|
+ error() {
|
|
|
+ this.remove()
|
|
|
+ this.loading = false
|
|
|
+ },
|
|
|
+ remove() {
|
|
|
+ this.onDelete()
|
|
|
+ },
|
|
|
+ //获取服务器返回的地址
|
|
|
+ handleCropperSuccess(data: any) {
|
|
|
+ //将返回的数据回显
|
|
|
+ this.loading = false
|
|
|
+ console.log(data, 'success')
|
|
|
+ this.$emit('update:modelValue', data)
|
|
|
+ // this.cropUploadSuccess(data)
|
|
|
+ // this.$emit('cropUploadSuccess', data)
|
|
|
+ // console.log(this.modelValue, 'modelValue')
|
|
|
+ },
|
|
|
+ // 取消上传
|
|
|
+ handleCropperClose() {
|
|
|
+ this.loading = false
|
|
|
+ this.remove()
|
|
|
+ },
|
|
|
+ getBase64(img, callback) {
|
|
|
+ const reader = new FileReader()
|
|
|
+ reader.addEventListener('load', () => callback(reader.result))
|
|
|
+ reader.readAsDataURL(img)
|
|
|
+ },
|
|
|
+ edit(record: any) {
|
|
|
+ const { options } = this
|
|
|
+ this.visible = true
|
|
|
+ this.optionsList = Object.assign({}, options, record)
|
|
|
+ console.log(this.options)
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 取消截图
|
|
|
+ */
|
|
|
+ cancelHandel() {
|
|
|
+ this.visible = false
|
|
|
+ // this.cropperNo()
|
|
|
+ this.loading = false
|
|
|
+ this.remove()
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 确认截图
|
|
|
+ */
|
|
|
+ okHandel() {
|
|
|
+ ;(this as any).$refs.cropperRef.getCropBlob(async data => {
|
|
|
+ this.submitLoading = true
|
|
|
+ const options: any = this.options
|
|
|
+ const fileName =
|
|
|
+ (options.name ? options.name.split('.')[0] : +new Date()) + '.png'
|
|
|
+ try {
|
|
|
+ let key = new Date().getTime() + fileName
|
|
|
+ let obj = {
|
|
|
+ filename: fileName,
|
|
|
+ bucketName: this.bucket,
|
|
|
+ postData: {
|
|
|
+ filename: fileName,
|
|
|
+ acl: 'public-read',
|
|
|
+ key: key,
|
|
|
+ unknowValueField: []
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const res = await request.post('/api-website/getUploadSign', {
|
|
|
+ data: obj
|
|
|
+ })
|
|
|
+ this.dataObj = {
|
|
|
+ policy: res.data.policy,
|
|
|
+ signature: res.data.signature,
|
|
|
+ key: key,
|
|
|
+ KSSAccessKeyId: res.data.kssAccessKeyId,
|
|
|
+ acl: 'public-read',
|
|
|
+ name: fileName
|
|
|
+ }
|
|
|
+
|
|
|
+ let formData = new FormData()
|
|
|
+ for (let key in this.dataObj) {
|
|
|
+ formData.append(key, this.dataObj[key])
|
|
|
+ }
|
|
|
+ formData.append('file', this.blobToFile(data, fileName), fileName)
|
|
|
+ await umiRequest(this.ossUploadUrl, {
|
|
|
+ method: 'POST',
|
|
|
+ data: formData
|
|
|
+ })
|
|
|
+ console.log(this.ossUploadUrl + '/' + key)
|
|
|
+ const uploadUrl = this.ossUploadUrl + '/' + key
|
|
|
+ // this.cropperOk(uploadUrl)
|
|
|
+ this.$emit('update:modelValue', uploadUrl)
|
|
|
+ } catch (err: any) {
|
|
|
+ ElMessage.error(err)
|
|
|
+ } finally {
|
|
|
+ this.submitLoading = false
|
|
|
+ this.cancelHandel()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ //转成blob
|
|
|
+ blobToFile(Blob: any, fileName: any) {
|
|
|
+ //兼容IE
|
|
|
+ Blob.lastModifiedDate = new Date()
|
|
|
+ Blob.name = fileName
|
|
|
+ return Blob
|
|
|
+ },
|
|
|
+ base64ToFile(urlData: any, fileName: any) {
|
|
|
+ let arr = urlData.split(',')
|
|
|
+ let mime = arr[0].match(/:(.*?);/)[1]
|
|
|
+ let bytes = atob(arr[1]) // 解码base64
|
|
|
+ let n = bytes.length
|
|
|
+ let ia = new Uint8Array(n)
|
|
|
+ while (n--) {
|
|
|
+ ia[n] = bytes.charCodeAt(n)
|
|
|
+ }
|
|
|
+ return new File([ia], fileName, { type: mime })
|
|
|
+ },
|
|
|
+ //移动框的事件
|
|
|
+ realTime(data: any) {
|
|
|
+ this.previews = data
|
|
|
+ },
|
|
|
+ //图片缩放
|
|
|
+ changeScale(num: number) {
|
|
|
+ num = num || 1
|
|
|
+ ;(this as any).$refs.cropperRef.changeScale(num)
|
|
|
+ },
|
|
|
+ //向左旋转
|
|
|
+ rotateLeft() {
|
|
|
+ ;(this as any).$refs.cropperRef.rotateLeft()
|
|
|
+ },
|
|
|
+ //向右旋转
|
|
|
+ rotateRight() {
|
|
|
+ ;(this as any).$refs.cropperRef.rotateRight()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ render() {
|
|
|
+ return (
|
|
|
+ <div class={[styles.colUpload, 'w-full']}>
|
|
|
+ <ElUpload
|
|
|
+ disabled={this.disabled}
|
|
|
+ showFileList={false}
|
|
|
+ accept={this.accept}
|
|
|
+ beforeUpload={this.beforeUpload}
|
|
|
+ // @ts-ignore
|
|
|
+ httpRequest={this.handleChange}
|
|
|
+ // limit={1}
|
|
|
+ ref="uploadRef"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ ref="uploadDom"
|
|
|
+ class={[styles.uploadClass, 'w-full']}
|
|
|
+ style={{ height: '106px' }}
|
|
|
+ >
|
|
|
+ {this.modelValue ? (
|
|
|
+ <ElImage
|
|
|
+ src={this.modelValue}
|
|
|
+ fit="cover"
|
|
|
+ class={styles.uploadSection}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <div
|
|
|
+ class={[
|
|
|
+ styles.uploadSection,
|
|
|
+ 'flex items-center flex-col justify-center'
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ <img src={iconUpload} class="w-8 h-7 mb-3" />
|
|
|
+ <p>{this.tips}</p>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </ElUpload>
|
|
|
+
|
|
|
+ <p class="text-3 text-[#999999] leading-6 pt-1">{this.extraTips}</p>
|
|
|
+
|
|
|
+ <ElDialog
|
|
|
+ modelValue={this.visible}
|
|
|
+ onUpdate:modelValue={val => (this.visible = val)}
|
|
|
+ appendToBody
|
|
|
+ title={this.options.title}
|
|
|
+ closeOnClickModal={false}
|
|
|
+ width={'800px'}
|
|
|
+ v-slots={{
|
|
|
+ footer: () => (
|
|
|
+ <span class="dialog-footer !text-center block">
|
|
|
+ <ElButton
|
|
|
+ onClick={this.cancelHandel}
|
|
|
+ disabled={this.submitLoading}
|
|
|
+ >
|
|
|
+ 取消
|
|
|
+ </ElButton>
|
|
|
+ <ElButton
|
|
|
+ type="primary"
|
|
|
+ onClick={this.okHandel}
|
|
|
+ loading={this.submitLoading}
|
|
|
+ >
|
|
|
+ 保 存
|
|
|
+ </ElButton>
|
|
|
+ </span>
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <ElRow>
|
|
|
+ <ElCol xs={24} md={12} style={{ width: '350px' }}>
|
|
|
+ <VueCropper
|
|
|
+ ref="cropperRef"
|
|
|
+ img={this.optionsList.img}
|
|
|
+ info={true}
|
|
|
+ autoCrop={this.optionsList.autoCrop}
|
|
|
+ autoCropWidth={this.optionsList.autoCropWidth}
|
|
|
+ full={this.optionsList.full}
|
|
|
+ outputType={this.optionsList.outputType}
|
|
|
+ autoCropHeight={this.optionsList.autoCropHeight}
|
|
|
+ fixedBox={this.optionsList.fixedBox}
|
|
|
+ enlarge={this.optionsList.enlarge}
|
|
|
+ onRealTime={this.realTime}
|
|
|
+ style={{ height: '350px' }}
|
|
|
+ />
|
|
|
+ <div class="flex pt-2">
|
|
|
+ <div
|
|
|
+ onClick={() => {
|
|
|
+ this.changeScale(1)
|
|
|
+ }}
|
|
|
+ class="mr-2 cursor-pointer"
|
|
|
+ title="放大"
|
|
|
+ >
|
|
|
+ <ElIcon size={30} color="#333">
|
|
|
+ <CirclePlus />
|
|
|
+ </ElIcon>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ onClick={() => {
|
|
|
+ this.changeScale(-1)
|
|
|
+ }}
|
|
|
+ class="mr-2 cursor-pointer"
|
|
|
+ title="缩小"
|
|
|
+ >
|
|
|
+ <ElIcon size={30} color="#333">
|
|
|
+ <Remove />
|
|
|
+ </ElIcon>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ onClick={this.rotateRight}
|
|
|
+ title="向右旋转"
|
|
|
+ class="cursor-pointer"
|
|
|
+ >
|
|
|
+ <img src={iconRate} class="w-[30px] h-[30px]" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </ElCol>
|
|
|
+ <ElCol xs={24} md={12} style={{ height: '350px' }}>
|
|
|
+ <div class={styles.previewImg}>
|
|
|
+ <span>预览图片</span>
|
|
|
+ <div
|
|
|
+ class={
|
|
|
+ this.optionsList.previewsCircle
|
|
|
+ ? styles['avatar-upload-preview']
|
|
|
+ : styles['avatar-upload-preview_range']
|
|
|
+ }
|
|
|
+ style={{
|
|
|
+ width: this.optionsList.autoCropWidth + 'px',
|
|
|
+ height: this.optionsList.autoCropHeight + 'px'
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <ElImage src={this.previews.url} style={this.previews.img} />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </ElCol>
|
|
|
+ </ElRow>
|
|
|
+ </ElDialog>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+})
|