cropper.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <template>
  2. <div>
  3. <Confirm :title="title" :func="clickHandler" :no-text="noText" :ok-text="okText">
  4. <template #content>
  5. <div>
  6. <div class="cropper-layer" :style="style">
  7. <VueCropper v-if="show" ref="vmRef" v-bind="option" v-on="on" />
  8. </div>
  9. <div v-if="showSize" class="size">
  10. <ui-input :label="`Logo-${longSize}`" type="radio" name="size" :model-value="sizeType == 1" @update:modelValue="changSize(1)" />
  11. <ui-input :label="`Logo-${squareSize}`" type="radio" name="size" :model-value="sizeType == 2" @update:modelValue="changSize(2)" />
  12. </div>
  13. </div>
  14. </template>
  15. </Confirm>
  16. </div>
  17. </template>
  18. <script setup lang="ts">
  19. import { computed, defineProps, nextTick, ref } from 'vue'
  20. import { VueCropper } from 'vue-cropper'
  21. import Confirm from '../dialog/confirm.vue'
  22. import 'vue-cropper/dist/index.css'
  23. // import { useI18n } from '@/i18n'
  24. // const { t } = useI18n({ useScope: 'global' })
  25. const sizeType = ref(1)
  26. const layerWidth = 500
  27. defineOptions({
  28. name: 'UICropper',
  29. })
  30. const props = defineProps({
  31. fixedNumber: {
  32. type: Array,
  33. default: () => [1, 1],
  34. },
  35. img: { type: String },
  36. cb: {
  37. type: Function,
  38. },
  39. showSize: {
  40. type: Boolean,
  41. default: false,
  42. },
  43. noText: {
  44. type: String,
  45. default: '取消',
  46. },
  47. okText: {
  48. type: String,
  49. default: '确认',
  50. },
  51. title: {
  52. type: String,
  53. default: '裁剪',
  54. },
  55. longSize: {
  56. type: String,
  57. default: '长型',
  58. },
  59. squareSize: {
  60. type: String,
  61. default: '方型',
  62. },
  63. })
  64. const show = ref(true)
  65. // const fixedNumber = props.fixedNumber
  66. const getHeight = width => (fixedNumber[1] / fixedNumber[0]) * width
  67. const option = {
  68. outputSize: 1,
  69. outputType: 'png',
  70. info: false,
  71. full: true,
  72. fixed: true,
  73. canScale: true,
  74. fixedNumber: props.fixedNumber,
  75. canMove: true,
  76. canMoveBox: true,
  77. fixedBox: false,
  78. original: false,
  79. autoCrop: true,
  80. autoCropWidth: layerWidth / 2,
  81. autoCropHeight: getHeight(layerWidth / 2),
  82. centerBox: false,
  83. mode: 'contain',
  84. maxImgSize: 400,
  85. // ...props,
  86. }
  87. const changSize = type => {
  88. if (sizeType.value != type) {
  89. sizeType.value = type
  90. show.value = false
  91. if (type == 1) {
  92. option.fixedNumber = [2, 1]
  93. } else {
  94. option.fixedNumber = [1, 1]
  95. }
  96. nextTick(() => {
  97. show.value = true
  98. })
  99. }
  100. }
  101. const style = computed(() => ({
  102. width: `${layerWidth}px`,
  103. height: `${getHeight(layerWidth)}px`,
  104. }))
  105. const vmRef = ref()
  106. const on = {
  107. imgLoad(status) {
  108. if (status !== 'success') {
  109. props.cb('图片加载失败')
  110. }
  111. },
  112. }
  113. const clickHandler = async status => {
  114. if (status === 'ok') {
  115. const data = await Promise.all([new Promise(resolve => vmRef.value.getCropBlob(resolve)), new Promise(resolve => vmRef.value.getCropData(resolve))])
  116. if (props.showSize) {
  117. data.push(sizeType.value)
  118. }
  119. props.cb(null, data)
  120. } else {
  121. props.cb()
  122. }
  123. }
  124. </script>
  125. <script>
  126. export default { name: 'UiCropper' }
  127. </script>
  128. <style lang="scss">
  129. .vue-cropper {
  130. background-repeat: repeat;
  131. }
  132. .cropper-view-box {
  133. outline-color: var(--color-main-normal) !important;
  134. }
  135. .crop-point {
  136. background-color: var(--color-main-normal) !important;
  137. }
  138. .size {
  139. width: 100%;
  140. display: flex;
  141. align-items: center;
  142. justify-content: center;
  143. margin-top: 20px;
  144. }
  145. .ui-input {
  146. margin-right: 30px;
  147. &:last-of-type {
  148. margin-right: 0;
  149. }
  150. }
  151. </style>