332 lines
7.0 KiB
Vue
332 lines
7.0 KiB
Vue
<template>
|
|
<a-modal
|
|
class="image-clip-dialog generalModel"
|
|
:class="{ 'is-product': data.isProduct }"
|
|
v-model:visible="show"
|
|
:footer="null"
|
|
width="70%"
|
|
:maskClosable="false"
|
|
:centered="true"
|
|
:closable="false"
|
|
wrapClassName="#app"
|
|
:keyboard="false"
|
|
>
|
|
<div class="image-clip-dialog-box">
|
|
<div class="header" :class="{ 'is-product': data.isProduct }">
|
|
<div class="title">{{ data.title }}</div>
|
|
<div class="right flex">
|
|
<div v-if="coverOrigin.length > 1" class="origin-container flex align-center">
|
|
<span>Crop from: </span>
|
|
<div class="origin-select flex align-center">
|
|
<div
|
|
class="origin-item sketch"
|
|
:class="{ selected: currentOrigin === 'sketch' }"
|
|
@click="handleChangeOrigin('sketch')"
|
|
>
|
|
Sketch
|
|
</div>
|
|
<div
|
|
class="origin-item product"
|
|
:class="{ selected: currentOrigin === 'mainProducImage' }"
|
|
@click="handleChangeOrigin('mainProductImage')"
|
|
>
|
|
Main product image
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="submit" v-if="!data.isPreview" @click="onSubmit">
|
|
<svg-icon name="seller-dui" size="24" />
|
|
</div>
|
|
<button @click="onCancel">Cancel</button>
|
|
</div>
|
|
</div>
|
|
<div class="content" :class="{ 'is-product': data.isProduct }">
|
|
<div class="crop-wrapper">
|
|
<div v-if="data.isProduct" class="tips">{{ tips }}</div>
|
|
<image-clip
|
|
ref="imageClipRef"
|
|
v-bind="$attrs"
|
|
:ratio="data.ratio"
|
|
:isProduct="isProduct"
|
|
:url="data.url"
|
|
:type="type"
|
|
@change="(v) => (data.preview_url = v)"
|
|
/>
|
|
</div>
|
|
<div
|
|
class="preview"
|
|
:class="{
|
|
'is-product': data.isProduct,
|
|
'is-apparel': type === 'apparel',
|
|
'is-cover': type === 'cover'
|
|
}"
|
|
v-if="data.isPreview"
|
|
>
|
|
<div class="title">
|
|
<span class="icon"><svg-icon name="seller-preview" size="24" /></span>
|
|
<span class="label">Crop Preview</span>
|
|
</div>
|
|
<div class="preview-image">
|
|
<img :src="data.preview_url" />
|
|
</div>
|
|
<div class="submit" @click="onSubmit">
|
|
<svg-icon name="seller-dui" size="24" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</a-modal>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, reactive, computed } from "vue"
|
|
import ImageClip from "./image-clip.vue"
|
|
|
|
const props = defineProps({
|
|
type: {
|
|
type: String,
|
|
default: () => false
|
|
},
|
|
isProduct: {
|
|
type: Boolean,
|
|
default: () => false
|
|
}
|
|
})
|
|
|
|
const tips = computed(() => {
|
|
if (props.type === "cover") {
|
|
return "Align crown to top, mid-thigh to bottom for best results."
|
|
}
|
|
if (props.type === "mainProductImage") {
|
|
return "Align crown to top, foot base to bottom for best results."
|
|
}
|
|
if (props.type === "sketch") {
|
|
return "Align crown to top, foot base to bottom for best results."
|
|
}
|
|
if (props.type === "apparel") {
|
|
return "Trim whitespace and center your apparel sketch."
|
|
}
|
|
})
|
|
|
|
const data = reactive({
|
|
url: "",
|
|
title: "Crop Image",
|
|
preview_url: "",
|
|
ratio: [1, 1],
|
|
isPreview: true,
|
|
callback: null,
|
|
isProduct: false // 是否商品编辑
|
|
})
|
|
|
|
const currentOrigin = ref("sketch")
|
|
const coverOrigin = ref([])
|
|
const handleChangeOrigin = (type) => {
|
|
currentOrigin.value = type
|
|
data.url = coverOrigin.value.filter((el) => el.type === type)[0].url
|
|
}
|
|
|
|
const show = ref(false)
|
|
const open = (url, callback, options, origin) => {
|
|
if (!props.isProduct) {
|
|
if (!url || !callback) return
|
|
}
|
|
data.url = url
|
|
data.callback = callback
|
|
data.ratio = options.ratio || [1, 1]
|
|
data.isPreview = true
|
|
data.preview_url = ""
|
|
data.title = options.title || "Crop Image"
|
|
if (options) {
|
|
if (options.hasOwnProperty("isPreview")) data.isPreview = options.isPreview
|
|
data.isProduct = options.isProduct
|
|
}
|
|
if (origin?.length) {
|
|
coverOrigin.value = origin
|
|
data.url = origin[0].url
|
|
}
|
|
show.value = true
|
|
}
|
|
const onCancel = () => {
|
|
show.value = false
|
|
}
|
|
const imageClipRef = ref(null)
|
|
const onSubmit = () => {
|
|
imageClipRef.value.getCropBlob().then((blob) => {
|
|
if (data.callback) data.callback(blobToFile(blob, "image.png"))
|
|
onCancel()
|
|
})
|
|
}
|
|
// 将blob转换为file对象
|
|
const blobToFile = (blob, fileName) => {
|
|
return new File([blob], fileName, { type: blob.type })
|
|
}
|
|
defineExpose({
|
|
open
|
|
})
|
|
</script>
|
|
<style scoped lang="less">
|
|
.image-clip-dialog-box {
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
.submit {
|
|
width: 4rem;
|
|
height: 4rem;
|
|
border-radius: 50%;
|
|
background: #262626;
|
|
color: #fff;
|
|
cursor: pointer;
|
|
}
|
|
> .header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 5rem;
|
|
&.is-product {
|
|
margin-bottom: 2.4rem;
|
|
}
|
|
> .title {
|
|
font-family: pingfang_heavy;
|
|
font-size: 2.4rem;
|
|
color: #595959;
|
|
}
|
|
> .right {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 2rem;
|
|
> button {
|
|
width: 10rem;
|
|
height: 4.8rem;
|
|
border-radius: 4rem;
|
|
border: none;
|
|
background: #e4e5eb;
|
|
font-family: pingfang_heavy;
|
|
font-size: 1.6rem;
|
|
color: #000;
|
|
}
|
|
.origin-container {
|
|
font-weight: 400;
|
|
color: #000;
|
|
font-size: 1.4rem;
|
|
.origin-select {
|
|
margin-left: 1.2rem;
|
|
height: 4.8rem;
|
|
border: 1px solid #c7c7c7;
|
|
border-radius: 3rem;
|
|
column-gap: 1.2rem;
|
|
padding: 0.8rem;
|
|
.origin-item {
|
|
height: 3.2rem;
|
|
line-height: 3.2rem;
|
|
border-radius: 2rem;
|
|
&.selected {
|
|
background-color: #000;
|
|
color: #fff;
|
|
}
|
|
&.sketch {
|
|
padding: 0 1.9rem;
|
|
}
|
|
&.product {
|
|
padding: 0 2.5rem;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
> .content {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
.crop-wrapper {
|
|
width: 100%;
|
|
.tips {
|
|
text-align: center;
|
|
color: #585858;
|
|
font-size: 1.4rem;
|
|
font-weight: 400;
|
|
}
|
|
}
|
|
&.is-product {
|
|
column-gap: 18.6rem;
|
|
.crop-wrapper {
|
|
width: initial;
|
|
}
|
|
}
|
|
> .image-clip {
|
|
flex: 1;
|
|
&.is-product {
|
|
width: initial;
|
|
flex: none;
|
|
}
|
|
}
|
|
> .preview {
|
|
margin-left: 6rem;
|
|
width: 28rem;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
gap: 2.4rem;
|
|
min-height: 0;
|
|
|
|
> .title {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 1.2rem;
|
|
> .label {
|
|
font-family: pingfang_heavy;
|
|
font-size: 1.6rem;
|
|
}
|
|
}
|
|
> .preview-image {
|
|
width: 100%;
|
|
flex: 1;
|
|
min-height: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
> .preview-image > img {
|
|
width: 100%;
|
|
height: auto;
|
|
max-height: 100%;
|
|
}
|
|
> .submit {
|
|
margin-top: auto;
|
|
flex-shrink: 0;
|
|
}
|
|
&.is-product {
|
|
margin-left: 0;
|
|
> .preview-image > img {
|
|
width: 20.8rem;
|
|
height: 36.7rem;
|
|
box-shadow: 4px 4px 16px 0px #0000000f;
|
|
border: 1px solid #ededed;
|
|
}
|
|
&.is-cover {
|
|
> .preview-image > img {
|
|
width: 29.7rem;
|
|
height: 37.5rem;
|
|
}
|
|
}
|
|
&.is-apparel {
|
|
> .preview-image > img {
|
|
width: 100%;
|
|
height: auto;
|
|
max-height: 100%;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|