Files
FiDA_Front/src/components/Canvas/FlowCanvas/components/result/result-image.vue
2026-02-27 14:58:36 +08:00

195 lines
4.3 KiB
Vue

<template>
<!-- 结果图片 -->
<div class="result-image">
<div class="header" v-show="showHeader">
<span class="icon">
<svg-icon name="chat-compose" size="24" size-unit="px" />
</span>
<span class="icon">
<svg-icon name="expand-lg" size="24" size-unit="px" />
</span>
<span class="icon">
<svg-icon name="download" size="24" size-unit="px" />
</span>
<button class="edit">
<span class="icon"><svg-icon name="edit" size="11" /></span>
<span class="text">Edit</span>
</button>
</div>
<img class="image" :src="data.url" />
<div class="more" @click="showMenu = !showMenu" @mousedown.stop>
<svg-icon name="more" size="24" size-unit="px" color="#C9C9C9" />
</div>
<div class="menu" v-show="showMenu" @mousedown.stop>
<div
v-for="(v, i) in menus"
:key="i"
:class="[v.isDivide ? 'divide' : 'item']"
@click="onMenuItem(v)"
>
<template v-if="!v.isDivide">
<span class="label">{{ v.label }}</span>
<span class="tip">{{ v.tip }}</span>
</template>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onBeforeUnmount, useAttrs } from 'vue'
const props = defineProps({
data: {
type: Object,
default: () => ({})
}
})
const attrs = useAttrs()
const showHeader = ref(!!attrs.node?.data?.isHeader)
const showMenu = ref(false)
const data = reactive({
url: props.data?.url || ''
})
const menus = ref([
{ label: 'Copy', tip: 'Ctrl+C', on: () => {} },
{ label: 'Paste', tip: 'Ctrl+V', on: () => {} },
{ label: 'Duplicate', tip: 'Ctrl+D', on: () => {} },
{ label: 'Delete', tip: 'Del', on: () => {} },
{ isDivide: true },
{ label: 'Bring to font', tip: 'Del', on: () => {} },
{ label: 'Send to back', tip: 'Del', on: () => {} },
{ isDivide: true },
{ label: 'Flip horizontal', tip: '', on: () => {} },
{ label: 'Flip vertical', tip: '', on: () => {} }
])
const onMenuItem = (v) => {
v.on && v.on()
hideMenu()
}
const hideMenu = () => {
showMenu.value = false
}
document.addEventListener('mousedown', hideMenu)
onBeforeUnmount(() => {
document.removeEventListener('mousedown', hideMenu)
})
defineExpose({ data })
</script>
<style lang="less" scoped>
.result-image {
width: 244px;
border-radius: 16px;
border: 3px solid #d9d9d9;
box-shadow: 0px 15px 21px 0px #0000000d;
padding: 25px 6px;
user-select: none;
background-color: #fff;
> .header {
position: absolute;
top: -20px;
left: 50%;
transform: translate(-50%, -100%);
width: auto;
height: 50px;
border-radius: 10px;
border: 2px solid #ebebeb;
box-shadow: 0px 15px 21px 0px #0000000d;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
padding: 0 19px;
gap: 12px;
background-color: #fff;
> .icon {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
--svg-icon-color: #000;
border-radius: 4px;
&:hover {
background-color: #dfdfdf;
}
}
> .edit {
width: 80px;
height: 30px;
border-radius: 4px;
border: none;
background-color: #ff7a51;
color: #fff;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
&:active {
opacity: 0.8;
}
}
}
> .image {
width: 100%;
height: auto;
}
position: relative;
> .more {
position: absolute;
top: 5px;
right: 9px;
width: 24px;
height: 24px;
cursor: pointer;
}
> .menu {
position: absolute;
width: 244px;
height: auto;
top: 0;
right: -10px;
transform: translate(100%, 0);
border-radius: 16px;
padding: 16px 14px;
box-shadow: 0px 15px 21px 0px rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
background-color: #fff;
z-index: 50;
> .item {
width: 100%;
height: 27px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
&:hover {
background: #f3f3f3;
}
> .label {
color: #000;
}
> .tip {
color: rgba(0, 0, 0, 0.3);
}
}
> .divide {
width: 100%;
height: 0;
border-bottom: 1px solid #e5e5e5;
}
}
}
</style>