UNPKG

@cmstops/pro-compo

Version:

[物料平台文档中心](https://arco.design/docs/material/guide)

499 lines (498 loc) 18.4 kB
import { defineComponent, ref, watch, onBeforeUnmount, openBlock, createBlock, unref, withCtx, createTextVNode, toDisplayString, createElementVNode, createVNode, withDirectives, vShow, normalizeClass, createElementBlock, createCommentVNode, Fragment, renderList } from "vue"; import { Modal, Tooltip, Button, Space, Spin } from "@arco-design/web-vue"; import { IconZoomIn, IconZoomOut, IconRotateLeft, IconRotateRight, IconSync } from "@arco-design/web-vue/es/icon"; import Cropper from "cropperjs"; import ColorThief from "colorthief"; import { GifToCanvas } from "gif-to-canvas"; import GIF from "gif.js/dist/gif"; import { uploadByTUS } from "../utils/tusUpload.js"; import { DEFAULT_BASE_API, DEFAULT_UPLOAD_URL } from "../config.js"; import { replaceSuffix } from "../utils/index.js"; import { getFileRealUrl } from "./script/api.js"; import { useGenerateAssets } from "../hooks/assets.js"; const _hoisted_1 = { class: "edit-cropper" }; const _hoisted_2 = ["src"]; const _hoisted_3 = { key: 0, class: "tool" }; const _hoisted_4 = ["onClick"]; const _hoisted_5 = { key: 1, class: "tool" }; const _hoisted_6 = ["onClick"]; const _hoisted_7 = { class: "image-crop-modal-footer" }; const _hoisted_8 = { class: "left" }; const _hoisted_9 = { class: "right" }; const _sfc_main = defineComponent({ __name: "component", props: { BASE_API: {}, visible: { type: Boolean }, aspectRatioProp: {}, corpData: {}, userInfo: {}, generateAssets: { type: Boolean } }, emits: ["update:visible", "confirm"], setup(__props, { emit: __emit }) { const emit = __emit; const props = __props; const BASE_API = props.BASE_API || DEFAULT_BASE_API; const saveLoading = ref(false); const editImage = ref(""); const setVisible = (bool) => { emit("update:visible", bool); }; const cancelDialog = () => { setVisible(false); }; const croppering = ref(true); const aspectRatioList = [ { label: "\u81EA\u7531", value: null }, { label: "1:1", value: 1 / 1 }, { label: "4:3", value: 4 / 3 }, { label: "3:4", value: 3 / 4 }, { label: "16:9", value: 16 / 9 }, { label: "9:16", value: 9 / 16 } ]; const customizedAspectRatioList = ref([]); const aspectRatioActive = ref(null); const mosaicObj = ref(null); const cropperIndex = ref(0); const selectedData = ref([]); const loadImage = () => { const cropperImg = document.getElementById("cropper_img"); const editCropper = document.querySelector(".edit-cropper"); cropperImg.classList.remove("position"); if (cropperImg && editCropper) { editCropper.style.width = `${cropperImg.offsetWidth}px`; editCropper.style.height = `${cropperImg.offsetHeight}px`; } }; const getBase64 = (imgUrl) => { window.URL = window.URL || window.webkitURL; const xhr = new XMLHttpRequest(); const sparator = imgUrl.includes("?") ? "&" : "?"; xhr.open("get", `${imgUrl}${sparator}_t=${new Date().getTime()}`, true); xhr.responseType = "blob"; xhr.send(); xhr.onload = () => { if (xhr.status === 200) { const blob = xhr.response; const oFileReader = new FileReader(); oFileReader.onloadend = (e) => { const base64 = e.target.result; editImage.value = base64; setTimeout(() => { if (props.aspectRatioProp) { if (Array.isArray(props.aspectRatioProp)) { cropper(props.aspectRatioProp[0], 0); } else { cropper(props.aspectRatioProp, null); } } }, 300); }; oFileReader.onerror = (e) => { }; oFileReader.readAsDataURL(blob); } }; }; const cropper = (aspectRatio, index) => { const image = document.getElementById("cropper_img"); aspectRatioActive.value = index; cropperDestroy(); mosaicObj.value = new Cropper(image, { crop() { croppering.value = false; }, initialAspectRatio: aspectRatio, aspectRatio, touchDragZoom: false, autoCropArea: 1, rotatable: true, strict: false, guides: false }); }; const cropperZoom = (ratio) => { mosaicObj.value.zoom(ratio); }; const cropperRotate = (degree) => { mosaicObj.value.rotate(degree); }; const cropperReset = () => { mosaicObj.value.reset(); }; const cropperDestroy = () => { mosaicObj.value && mosaicObj.value.destroy(); }; const dataURLtoBlob = (dataurl) => { const arr = dataurl.split(","); const mime = arr[0].match(/:(.*?);/)[1]; const bstr = atob(arr[1]); let n = bstr.length; const u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], { type: mime }); }; const save = () => { saveLoading.value = true; if (mosaicObj.value) { if (mosaicObj.value.url.includes("data:image/gif")) { const url = URL.createObjectURL(dataURLtoBlob(mosaicObj.value.url)); const cropBoxData = mosaicObj.value.getCropBoxData(); const canvasData = mosaicObj.value.getCanvasData(); const gifToCanvas = new GifToCanvas(url, { targetOffset: { dx: cropBoxData.left - canvasData.left, dy: cropBoxData.top - canvasData.top, width: canvasData.width, height: canvasData.height, sWidth: cropBoxData.width, sHeight: cropBoxData.height } }); gifToCanvas.init(); let gifWorker = ""; if (window.location.href.includes("localhost")) { gifWorker = "./gif.worker.js"; } else { gifWorker = `${BASE_API}/static/js/gif.worker.js`; } const gifObj = new GIF({ workers: 4, quality: 10, width: cropBoxData.width, height: cropBoxData.height, workerScript: gifWorker }); const addFrame = (canvas, delay) => { gifObj.addFrame(canvas, { copy: true, delay }); }; gifToCanvas.on("progress", (canvas, delay) => { addFrame(canvas, delay); }); gifToCanvas.on("finished", (canvas, delay) => { addFrame(canvas, delay); gifObj.render(); }); gifObj.on("finished", function(blob) { handleUploadCropData(blob, "gif"); }); } else { const CroppedCanvas = mosaicObj.value.getCroppedCanvas({ fillColor: "#fff" }); CroppedCanvas.toBlob( async (blob) => { handleUploadCropData(blob, "png"); }, "image/jpeg", 0.75 ); } } else { saveLoading.value = false; if (selectedData.value.length === cropperIndex.value + 1) { emit("confirm", JSON.parse(JSON.stringify(selectedData.value))); cancelDialog(); } else { cropperIndex.value += 1; cropperDestroy(); getMediaRealUrl(); } } }; const getUploadFileUrl = (file) => { const pattern = /\+.*$/; const result = file.url.replace(pattern, "").replace("upload", "static"); return { url: result, size: file.size || file.file.size }; }; const handleUploadCropData = async (blob, fileType) => { const themeColors = getThemeColor(); const name = `\u88C1\u5207\u526F\u672C${new Date().getTime()}.${fileType}`; const file = new File([blob], name, { type: blob.type }); const result = await uploadByTUS(file); const resImg = getUploadFileUrl(result); const target = selectedData.value[cropperIndex.value]; if (props.userInfo) { const param = { url: resImg.url, size: resImg.size, alias: replaceSuffix(target.alias || target.url, "\u88C1\u5207\u526F\u672C"), repository_id: props.userInfo.repository_id, directory_id: target.directory_id, banner_theme_color: themeColors, parent_id: target.id || 0 }; if (target.source === "aigc") { param.made_by_ai = true; } if (!props.generateAssets) { afterCropperAndUploadFile({ url: resImg.url, banner_theme_color: themeColors }); } useGenerateAssets(BASE_API, param).then((response) => { response.message.banner_theme_color = themeColors; afterCropperAndUploadFile(response.message); }); } else if (!props.generateAssets) { afterCropperAndUploadFile({ url: resImg.url, banner_theme_color: themeColors }); } }; const afterCropperAndUploadFile = (data) => { selectedData.value[cropperIndex.value] = { ...data }; saveLoading.value = false; if (selectedData.value.length === cropperIndex.value + 1) { emit("confirm", JSON.parse(JSON.stringify(selectedData.value))); cancelDialog(); } else { cropperIndex.value += 1; cropperDestroy(); getMediaRealUrl(); } }; const getThemeColor = () => { const colorThief = new ColorThief(); const img = document.getElementById("cropper_img"); let themeColors = []; const Palette = colorThief.getPalette(img); if (Palette) { themeColors = Palette.slice(0, 8); } else { themeColors = [[255, 255, 255]]; } return themeColors; }; const getMediaRealUrl = () => { const item = selectedData.value[cropperIndex.value]; const flag = !item.url.includes("/poplar/v2"); const cape = item.url.includes("cape/v1/upload"); const extented = !item.url.includes(BASE_API) && !item.url.includes(DEFAULT_UPLOAD_URL); croppering.value = true; if (item.realUrl && flag || cape || extented) { getBase64(item.url); } else { getFileRealUrl(BASE_API, item.url).then((res) => { getBase64(res.message); }); } }; watch( () => props.visible, (newVal) => { if (newVal) { init(); } } ); const init = () => { cropperIndex.value = 0; selectedData.value = props.corpData; if (props.aspectRatioProp && Array.isArray(props.aspectRatioProp)) { const aspectRatioLists = aspectRatioList.map((item) => { item.buttonIndex = cropperIndex.value++; return item; }).filter((item) => { return props.aspectRatioProp.includes(item.value); }); if (aspectRatioLists.length) { customizedAspectRatioList.value = aspectRatioLists; } } getMediaRealUrl(); }; onBeforeUnmount(() => { cropperDestroy(); }); return (_ctx, _cache) => { return openBlock(), createBlock(unref(Modal), { visible: _ctx.visible, width: "986px", "mask-closable": false, "title-align": "center", "unmount-on-close": "", "modal-class": "image-crop-modal-wrapper", "body-class": "image-crop-modal-body", onCancel: _cache[4] || (_cache[4] = ($event) => setVisible(false)) }, { title: withCtx(() => [ createTextVNode(toDisplayString(_ctx.aspectRatioProp ? "\u88C1\u526A\u56FE\u7247" : "\u8BF7\u9009\u62E9\u88C1\u526A\u6BD4\u4F8B"), 1) ]), footer: withCtx(() => [ createElementVNode("span", _hoisted_7, [ createElementVNode("div", _hoisted_8, [ createVNode(unref(Tooltip), { content: "\u653E\u5927" }, { default: withCtx(() => [ createVNode(unref(Button), { class: "cropper-btn", shape: "circle", onClick: _cache[0] || (_cache[0] = ($event) => cropperZoom(0.1)) }, { default: withCtx(() => [ createVNode(unref(IconZoomIn)) ]), _: 1 }) ]), _: 1 }), createVNode(unref(Tooltip), { content: "\u7F29\u5C0F" }, { default: withCtx(() => [ createVNode(unref(Button), { class: "cropper-btn", shape: "circle", onClick: _cache[1] || (_cache[1] = ($event) => cropperZoom(-0.1)) }, { default: withCtx(() => [ createVNode(unref(IconZoomOut)) ]), _: 1 }) ]), _: 1 }), createVNode(unref(Tooltip), { content: "\u5411\u5DE6\u65CB\u8F6C45\u5EA6" }, { default: withCtx(() => [ createVNode(unref(Button), { class: "cropper-btn", shape: "circle", onClick: _cache[2] || (_cache[2] = ($event) => cropperRotate(-45)) }, { default: withCtx(() => [ createVNode(unref(IconRotateLeft)) ]), _: 1 }) ]), _: 1 }), createVNode(unref(Tooltip), { content: "\u5411\u53F3\u65CB\u8F6C45\u5EA6" }, { default: withCtx(() => [ createVNode(unref(Button), { class: "cropper-btn", shape: "circle", onClick: _cache[3] || (_cache[3] = ($event) => cropperRotate(45)) }, { default: withCtx(() => [ createVNode(unref(IconRotateRight)) ]), _: 1 }) ]), _: 1 }), createVNode(unref(Tooltip), { content: "\u91CD\u7F6E" }, { default: withCtx(() => [ createVNode(unref(Button), { class: "cropper-btn", shape: "circle", onClick: cropperReset }, { default: withCtx(() => [ createVNode(unref(IconSync)) ]), _: 1 }) ]), _: 1 }) ]), createElementVNode("div", _hoisted_9, [ createVNode(unref(Space), null, { default: withCtx(() => [ createVNode(unref(Button), { size: "medium", onClick: cancelDialog }, { default: withCtx(() => _cache[5] || (_cache[5] = [ createTextVNode("\u53D6\u6D88") ])), _: 1, __: [5] }), withDirectives(createVNode(unref(Button), { size: "medium", type: "primary", loading: saveLoading.value, onClick: save }, { default: withCtx(() => _cache[6] || (_cache[6] = [ createTextVNode("\u88C1\u526A") ])), _: 1, __: [6] }, 8, ["loading"]), [ [vShow, !croppering.value] ]) ]), _: 1 }) ]) ]) ]), default: withCtx(() => [ createVNode(unref(Spin), { loading: croppering.value, class: "image-cropper", style: { "width": "100%" } }, { default: withCtx(() => [ createElementVNode("div", { class: normalizeClass(["edit", { fullHeight: _ctx.aspectRatioProp && !Array.isArray(_ctx.aspectRatioProp) }]) }, [ createElementVNode("div", _hoisted_1, [ editImage.value ? (openBlock(), createElementBlock("img", { key: 0, id: "cropper_img", class: "position", crossorigin: "anonymous", src: editImage.value, onLoad: loadImage }, null, 40, _hoisted_2)) : createCommentVNode("v-if", true) ]) ], 2), !_ctx.aspectRatioProp ? (openBlock(), createElementBlock("div", _hoisted_3, [ (openBlock(), createElementBlock(Fragment, null, renderList(aspectRatioList, (item, i) => { return createElementVNode("div", { key: item.lable, class: normalizeClass(["btn btn" + (i + 1), { active: aspectRatioActive.value === i }]), onClick: ($event) => cropper(item.value, i) }, toDisplayString(item.label), 11, _hoisted_4); }), 64)) ])) : createCommentVNode("v-if", true), _ctx.aspectRatioProp && Array.isArray(_ctx.aspectRatioProp) && customizedAspectRatioList.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_5, [ (openBlock(true), createElementBlock(Fragment, null, renderList(customizedAspectRatioList.value, (item, i) => { return openBlock(), createElementBlock("div", { key: item.lable, class: normalizeClass([ "btn btn" + (item.buttonIndex + 1), { active: aspectRatioActive.value === i } ]), onClick: ($event) => cropper(item.value, i) }, toDisplayString(item.label), 11, _hoisted_6); }), 128)) ])) : createCommentVNode("v-if", true) ]), _: 1 }, 8, ["loading"]) ]), _: 1 }, 8, ["visible"]); }; } }); export { _sfc_main as default };