添加新组件
This commit is contained in:
parent
72a6e436b2
commit
ddb1c367b5
|
|
@ -26,6 +26,7 @@ declare module 'vue' {
|
||||||
ImageUploader: typeof import('./src/components/dialogBox/imageUploader/index.vue')['default']
|
ImageUploader: typeof import('./src/components/dialogBox/imageUploader/index.vue')['default']
|
||||||
Img: typeof import('./src/components/Img/index.vue')['default']
|
Img: typeof import('./src/components/Img/index.vue')['default']
|
||||||
Model: typeof import('./src/components/dialogBox/model/index.vue')['default']
|
Model: typeof import('./src/components/dialogBox/model/index.vue')['default']
|
||||||
|
Popover: typeof import('./src/components/Popover/index.vue')['default']
|
||||||
Proportion: typeof import('./src/components/dialogBox/proportion/index.vue')['default']
|
Proportion: typeof import('./src/components/dialogBox/proportion/index.vue')['default']
|
||||||
Quantity: typeof import('./src/components/dialogBox/quantity/index.vue')['default']
|
Quantity: typeof import('./src/components/dialogBox/quantity/index.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
<template>
|
||||||
|
<div class="custom-popover" ref="popoverRef">
|
||||||
|
<div class="popover-trigger" ref="triggerRef" @click.stop="togglePopover">
|
||||||
|
<slot name="reference" />
|
||||||
|
</div>
|
||||||
|
<Teleport to="body">
|
||||||
|
<div
|
||||||
|
v-if="visible"
|
||||||
|
ref="contentRef"
|
||||||
|
class="popover-content"
|
||||||
|
:class="[placement]"
|
||||||
|
:style="contentStyle"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
placement: {
|
||||||
|
type: String,
|
||||||
|
default: 'top'
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 'auto'
|
||||||
|
},
|
||||||
|
trigger: {
|
||||||
|
type: String,
|
||||||
|
default: 'click'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const popoverRef = ref(null)
|
||||||
|
const triggerRef = ref(null)
|
||||||
|
const contentRef = ref(null)
|
||||||
|
const visible = ref(props.modelValue)
|
||||||
|
const position = ref({ top: 0, left: 0 })
|
||||||
|
const popoverId = ref(Math.random().toString(36).substr(2, 9))
|
||||||
|
|
||||||
|
if (!window.__currentOpenPopoverId__) {
|
||||||
|
window.__currentOpenPopoverId__ = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentStyle = computed(() => ({
|
||||||
|
width: typeof props.width === 'number' ? `${props.width}px` : props.width,
|
||||||
|
...position.value
|
||||||
|
}))
|
||||||
|
|
||||||
|
const togglePopover = async () => {
|
||||||
|
if (visible.value) {
|
||||||
|
visible.value = false
|
||||||
|
window.__currentOpenPopoverId__ = null
|
||||||
|
} else {
|
||||||
|
if (window.__currentOpenPopoverId__ && window.__currentOpenPopoverId__ !== popoverId.value) {
|
||||||
|
window.dispatchEvent(new CustomEvent('close-other-popovers', { detail: { excludeId: popoverId.value } }))
|
||||||
|
}
|
||||||
|
if (window.__currentOpenSelectId__) {
|
||||||
|
window.dispatchEvent(new CustomEvent('close-other-selects', { detail: { excludeId: null } }))
|
||||||
|
}
|
||||||
|
visible.value = true
|
||||||
|
window.__currentOpenPopoverId__ = popoverId.value
|
||||||
|
await nextTick()
|
||||||
|
updatePosition()
|
||||||
|
}
|
||||||
|
emit('update:modelValue', visible.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatePosition = () => {
|
||||||
|
if (!triggerRef.value || !contentRef.value) return
|
||||||
|
|
||||||
|
const triggerRect = triggerRef.value.getBoundingClientRect()
|
||||||
|
const contentRect = contentRef.value.getBoundingClientRect()
|
||||||
|
const gap = 25
|
||||||
|
|
||||||
|
let top = 0
|
||||||
|
let left = 0
|
||||||
|
|
||||||
|
switch (props.placement) {
|
||||||
|
case 'top':
|
||||||
|
top = triggerRect.top - contentRect.height - gap
|
||||||
|
left = triggerRect.left + (triggerRect.width - contentRect.width) / 2
|
||||||
|
break
|
||||||
|
case 'bottom':
|
||||||
|
top = triggerRect.bottom + gap
|
||||||
|
left = triggerRect.left + (triggerRect.width - contentRect.width) / 2
|
||||||
|
break
|
||||||
|
case 'left':
|
||||||
|
top = triggerRect.top + (triggerRect.height - contentRect.height) / 2
|
||||||
|
left = triggerRect.left - contentRect.width - gap
|
||||||
|
break
|
||||||
|
case 'right':
|
||||||
|
top = triggerRect.top + (triggerRect.height - contentRect.height) / 2
|
||||||
|
left = triggerRect.right + gap
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
position.value = {
|
||||||
|
top: `${top}px`,
|
||||||
|
left: `${left}px`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClickOutside = (e) => {
|
||||||
|
if (!visible.value) return
|
||||||
|
|
||||||
|
const triggerEl = popoverRef.value
|
||||||
|
const contentEl = contentRef.value
|
||||||
|
|
||||||
|
if (
|
||||||
|
triggerEl &&
|
||||||
|
!triggerEl.contains(e.target) &&
|
||||||
|
contentEl &&
|
||||||
|
!contentEl.contains(e.target)
|
||||||
|
) {
|
||||||
|
visible.value = false
|
||||||
|
window.__currentOpenPopoverId__ = null
|
||||||
|
emit('update:modelValue', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseOtherPopovers = (e) => {
|
||||||
|
if (e.detail.excludeId !== popoverId.value) {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseOtherSelects = () => {
|
||||||
|
visible.value = false
|
||||||
|
window.__currentOpenPopoverId__ = null
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.modelValue, async (val) => {
|
||||||
|
visible.value = val
|
||||||
|
if (val) {
|
||||||
|
await nextTick()
|
||||||
|
updatePosition()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener('click', handleClickOutside)
|
||||||
|
window.addEventListener('resize', updatePosition)
|
||||||
|
window.addEventListener('scroll', updatePosition, true)
|
||||||
|
window.addEventListener('close-other-popovers', handleCloseOtherPopovers)
|
||||||
|
window.addEventListener('close-other-selects', handleCloseOtherSelects)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener('click', handleClickOutside)
|
||||||
|
window.removeEventListener('resize', updatePosition)
|
||||||
|
window.removeEventListener('scroll', updatePosition, true)
|
||||||
|
window.removeEventListener('close-other-popovers', handleCloseOtherPopovers)
|
||||||
|
window.removeEventListener('close-other-selects', handleCloseOtherSelects)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.custom-popover {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-trigger {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content {
|
||||||
|
position: fixed;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08);
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
z-index: 2000;
|
||||||
|
animation: popoverFadeIn 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content.top {
|
||||||
|
transform-origin: bottom center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content.bottom {
|
||||||
|
transform-origin: top center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content.left {
|
||||||
|
transform-origin: right center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content.right {
|
||||||
|
transform-origin: left center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes popoverFadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
>
|
>
|
||||||
<slot name="prefix" />
|
<slot name="prefix" />
|
||||||
<span class="select-text">{{ selectedOption || placeholder }}</span>
|
<span class="select-text">{{ selectedOption || placeholder }}</span>
|
||||||
<!-- <div v-if="props.isArrow" class="arrow-icon" :class="{ rotate: isOpen }"><i-ep-ArrowDown /></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
@ -19,20 +18,45 @@
|
||||||
:class="position"
|
:class="position"
|
||||||
>
|
>
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
<div
|
<template v-if="groupedOptions.length > 0">
|
||||||
v-for="(option, index) in options"
|
<div
|
||||||
:key="option.value || index"
|
v-for="(group, groupIndex) in groupedOptions"
|
||||||
class="dropdown-item"
|
:key="groupIndex"
|
||||||
:class="{ selected: option.value === selectedValue }"
|
class="option-group"
|
||||||
@click.stop="selectOption(option)"
|
>
|
||||||
>
|
<div v-if="group.label" class="group-title">{{ group.label }}</div>
|
||||||
<slot name="option" :option="option" :selected="option.value === selectedValue">
|
<div
|
||||||
<div class="option-content">
|
v-for="(option, index) in group.options"
|
||||||
<span class="option-label">{{ option.label }}</span>
|
:key="option.value || index"
|
||||||
<span v-if="option.value === selectedValue" class="option-check">✓</span>
|
class="dropdown-item"
|
||||||
|
:class="{ selected: option.value === selectedValue }"
|
||||||
|
@click.stop="selectOption(option)"
|
||||||
|
>
|
||||||
|
<slot name="option" :option="option" :selected="option.value === selectedValue">
|
||||||
|
<div class="option-content">
|
||||||
|
<span class="option-label">{{ option.label }}</span>
|
||||||
|
<span v-if="option.value === selectedValue" class="option-check">✓</span>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
v-for="(option, index) in options"
|
||||||
|
:key="option.value || index"
|
||||||
|
class="dropdown-item"
|
||||||
|
:class="{ selected: option.value === selectedValue }"
|
||||||
|
@click.stop="selectOption(option)"
|
||||||
|
>
|
||||||
|
<slot name="option" :option="option" :selected="option.value === selectedValue">
|
||||||
|
<div class="option-content">
|
||||||
|
<span class="option-label">{{ option.label }}</span>
|
||||||
|
<span v-if="option.value === selectedValue" class="option-check">✓</span>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -53,6 +77,10 @@ const props = defineProps({
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
|
groupedOptions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '请选择'
|
default: '请选择'
|
||||||
|
|
@ -96,7 +124,13 @@ const headerWidth = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectedOption = computed(() => {
|
const selectedOption = computed(() => {
|
||||||
const option = props.options.find((opt) => opt.value === selectedValue.value)
|
let option = props.options.find((opt) => opt.value === selectedValue.value)
|
||||||
|
if (!option && props.groupedOptions.length > 0) {
|
||||||
|
for (const group of props.groupedOptions) {
|
||||||
|
option = group.options.find((opt) => opt.value === selectedValue.value)
|
||||||
|
if (option) break
|
||||||
|
}
|
||||||
|
}
|
||||||
return option ? option.label : ''
|
return option ? option.label : ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -108,6 +142,9 @@ const toggleDropdown = async () => {
|
||||||
if (window.__currentOpenSelectId__ && window.__currentOpenSelectId__ !== selectId.value) {
|
if (window.__currentOpenSelectId__ && window.__currentOpenSelectId__ !== selectId.value) {
|
||||||
window.dispatchEvent(new CustomEvent('close-other-selects', { detail: { excludeId: selectId.value } }))
|
window.dispatchEvent(new CustomEvent('close-other-selects', { detail: { excludeId: selectId.value } }))
|
||||||
}
|
}
|
||||||
|
if (window.__currentOpenPopoverId__) {
|
||||||
|
window.dispatchEvent(new CustomEvent('close-other-popovers', { detail: { excludeId: null } }))
|
||||||
|
}
|
||||||
isOpen.value = true
|
isOpen.value = true
|
||||||
window.__currentOpenSelectId__ = selectId.value
|
window.__currentOpenSelectId__ = selectId.value
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
@ -141,12 +178,19 @@ const handleCloseOtherSelects = (e) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleCloseOtherPopovers = () => {
|
||||||
|
isOpen.value = false
|
||||||
|
window.__currentOpenSelectId__ = null
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('click', handleClickOutside)
|
document.addEventListener('click', handleClickOutside)
|
||||||
window.addEventListener('close-other-selects', handleCloseOtherSelects)
|
window.addEventListener('close-other-selects', handleCloseOtherSelects)
|
||||||
|
window.addEventListener('close-other-popovers', handleCloseOtherPopovers)
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
document.removeEventListener('click', handleClickOutside)
|
document.removeEventListener('click', handleClickOutside)
|
||||||
window.removeEventListener('close-other-selects', handleCloseOtherSelects)
|
window.removeEventListener('close-other-selects', handleCloseOtherSelects)
|
||||||
|
window.removeEventListener('close-other-popovers', handleCloseOtherPopovers)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -317,4 +361,20 @@ onBeforeUnmount(() => {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.option-group {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-title {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #666;
|
||||||
|
padding: 5px 20px 8px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="localPreviewList.length < limit" class="upload-trigger" @click="triggerUpload">
|
<div v-if="localPreviewList.length < limit" class="upload-trigger" @click="triggerUpload">
|
||||||
<i-ep-plus />
|
<i-ep-plus color="#333333" />
|
||||||
<span>参考内容</span>
|
<span>参考内容</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -219,7 +219,8 @@ defineExpose({
|
||||||
width: 56px;
|
width: 56px;
|
||||||
height: 56px;
|
height: 56px;
|
||||||
// border: 2px dashed #d1d5db;
|
// border: 2px dashed #d1d5db;
|
||||||
background-color: #F8F9FA;
|
background: rgba(0, 15, 51, 0.04);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -231,7 +232,7 @@ defineExpose({
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
color: #999;
|
color: #333;
|
||||||
font-family: "Microsoft YaHei";
|
font-family: "Microsoft YaHei";
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<Transition name="slide-up">
|
<Transition name="slide-up">
|
||||||
<div class="input-container" :class="{ generate : !props.isGenerate }" @click="handleContainerClick">
|
<div class="input-container" :class="{ generate : !props.isGenerate }" @click="handleContainerClick">
|
||||||
<div v-if="!props.isGenerate" class="title">AI绘画2026</div>
|
<div v-if="!props.isGenerate && props.type === 'painting'" class="title">AI绘画2026</div>
|
||||||
|
<div v-if="!props.isGenerate && props.type === 'video'" class="title">AI视频2026</div>
|
||||||
|
|
||||||
<div class="sender-top">
|
<div class="sender-top">
|
||||||
<div v-if="useDisplay.Sender_variant === 'default'" class="scroll-to-bottom-text" @click.stop="handleScrollToBottom">回到底部<img src="@/assets/dialog/ArrowDown.svg"></div>
|
<div v-if="useDisplay.Sender_variant === 'default'" class="scroll-to-bottom-text" @click.stop="handleScrollToBottom">回到底部<img src="@/assets/dialog/ArrowDown.svg"></div>
|
||||||
|
|
@ -15,7 +16,13 @@
|
||||||
|
|
||||||
<Sender :key="useDisplay.Sender_variant" v-model="prompt" :variant="useDisplay.Sender_variant" :placeholder="promptPlaceholder" :submit-btn-disabled="isgerenate.value" :auto-size="autoSizeConfig">
|
<Sender :key="useDisplay.Sender_variant" v-model="prompt" :variant="useDisplay.Sender_variant" :placeholder="promptPlaceholder" :submit-btn-disabled="isgerenate.value" :auto-size="autoSizeConfig">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<div v-show="useDisplay.Sender_variant !== 'default'" class="prefix-self-wrap">
|
<div v-show="useDisplay.Sender_variant !== 'default' && props.type === 'painting'" class="prefix-self-wrap">
|
||||||
|
<Model v-model="model" v-model:typeValue="type" />
|
||||||
|
<Proportion v-model="proportion" v-model:resolution="resolution" />
|
||||||
|
<Quantity v-model="quantity" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="useDisplay.Sender_variant !== 'default' && props.type === 'video'" class="prefix-self-wrap">
|
||||||
<Model v-model="model" v-model:typeValue="type" />
|
<Model v-model="model" v-model:typeValue="type" />
|
||||||
<Proportion v-model="proportion" v-model:resolution="resolution" />
|
<Proportion v-model="proportion" v-model:resolution="resolution" />
|
||||||
<Quantity v-model="quantity" />
|
<Quantity v-model="quantity" />
|
||||||
|
|
@ -58,6 +65,10 @@ const props = defineProps({
|
||||||
generate: {
|
generate: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'painting'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
@ -249,6 +260,10 @@ watch(() => type.value, (newValue) => {
|
||||||
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-popover.el-popper){
|
||||||
|
border-radius: 20px;
|
||||||
|
|
||||||
|
}
|
||||||
// 时间选择器
|
// 时间选择器
|
||||||
.select{
|
.select{
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<el-popover trigger="click" placement="top" :width="180">
|
<Select
|
||||||
<div class="select">
|
v-model="model"
|
||||||
<div class="model-group">
|
:grouped-options="modelGroups"
|
||||||
<div class="group-title">生成模型</div>
|
class="model-select"
|
||||||
<div
|
position="top"
|
||||||
v-for="item in generateModels"
|
>
|
||||||
:key="item.value"
|
<template #prefix>
|
||||||
class="model-item"
|
<img src="@/assets/dialog/model.svg" alt="" style="width: 16px;">
|
||||||
:class="{ active: model === item.value }"
|
|
||||||
@click="selectModel(item.value)"
|
|
||||||
>
|
|
||||||
{{ item.label }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="model-group">
|
|
||||||
<div class="group-title">编辑模型</div>
|
|
||||||
<div
|
|
||||||
v-for="item in editModels"
|
|
||||||
:key="item.value"
|
|
||||||
class="model-item"
|
|
||||||
:class="{ active: model === item.value }"
|
|
||||||
@click="selectModel(item.value)"
|
|
||||||
>
|
|
||||||
{{ item.label }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="model-group">
|
|
||||||
<div class="group-title">视觉理解模型</div>
|
|
||||||
<div
|
|
||||||
v-for="item in visionModels"
|
|
||||||
:key="item.value"
|
|
||||||
class="model-item"
|
|
||||||
:class="{ active: model === item.value }"
|
|
||||||
@click="selectModel(item.value)"
|
|
||||||
>
|
|
||||||
{{ item.label }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<template #reference>
|
|
||||||
<div class="choice-btn">
|
|
||||||
<img src="@/assets/dialog/model.svg" alt="" style="width: 16px;">
|
|
||||||
<span>{{ getModelLabel(model) }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</el-popover>
|
</Select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import Select from '@/components/Select/index.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
@ -90,9 +54,20 @@ const visionModels = [
|
||||||
{ value: 'Qwen3.5plus', label: 'Qwen3.5plus' }
|
{ value: 'Qwen3.5plus', label: 'Qwen3.5plus' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const selectModel = (value) => {
|
const modelGroups = [
|
||||||
model.value = value
|
{
|
||||||
}
|
label: '生成模型',
|
||||||
|
options: generateModels
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '编辑模型',
|
||||||
|
options: editModels
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '视觉理解模型',
|
||||||
|
options: visionModels
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
const getModelType = (value) => {
|
const getModelType = (value) => {
|
||||||
if (generateModels.find(m => m.value === value)) {
|
if (generateModels.find(m => m.value === value)) {
|
||||||
|
|
@ -106,74 +81,39 @@ const getModelType = (value) => {
|
||||||
}
|
}
|
||||||
return 'text'
|
return 'text'
|
||||||
}
|
}
|
||||||
|
|
||||||
const getModelLabel = (value) => {
|
|
||||||
const allModels = [...generateModels, ...editModels, ...visionModels]
|
|
||||||
const model = allModels.find(item => item.value === value)
|
|
||||||
return model ? model.label : value
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.choice-btn{
|
.model-select {
|
||||||
display: flex;
|
:deep(.select-header) {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
justify-content: center;
|
border-radius: 10px;
|
||||||
align-items: center;
|
border: 1px solid rgba(0, 0, 0, 0.10);
|
||||||
gap: 5px;
|
background: #ffffff;
|
||||||
border-radius: 10px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.10);
|
|
||||||
background: #ffffff;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.choice-btn:hover{
|
|
||||||
background: #E5E7EB;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select{
|
&:hover {
|
||||||
padding: 10px;
|
background: #E5E7EB;
|
||||||
max-height: 510px;
|
}
|
||||||
overflow-y: auto;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.model-group{
|
:deep(.select-text) {
|
||||||
margin-bottom: 15px;
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
&:last-child{
|
:deep(.dropdown-menu) {
|
||||||
margin-bottom: 0;
|
max-height: 510px;
|
||||||
}
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-title{
|
:deep(.dropdown-item) {
|
||||||
font-size: 12px;
|
min-width: 120px;
|
||||||
font-weight: 600;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.model-item{
|
&.active {
|
||||||
padding: 8px 10px;
|
background: rgba(0, 15, 51, 0.10);
|
||||||
border-radius: 6px;
|
color: #000F33;
|
||||||
cursor: pointer;
|
font-weight: 600;
|
||||||
font-size: 14px;
|
}
|
||||||
margin-bottom: 4px;
|
}
|
||||||
transition: all 0.2s ease;
|
|
||||||
|
|
||||||
&:last-child{
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover{
|
|
||||||
background: #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active{
|
|
||||||
background: #e6f7ff;
|
|
||||||
color: #1890ff;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<el-popover trigger="click" placement="top" :width="400">
|
<Popover placement="top" :width="400">
|
||||||
<div class="proportion-container">
|
<div class="proportion-container">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h3>选择比例</h3>
|
<h3>选择比例</h3>
|
||||||
|
|
@ -53,11 +53,12 @@
|
||||||
<span>{{ proportion }}</span>
|
<span>{{ proportion }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-popover>
|
</Popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import Popover from '@/components/Popover/index.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<el-popover trigger="click" placement="top" :width="330">
|
<Select
|
||||||
<div class="quantity-container">
|
v-model="quantity"
|
||||||
<div v-for="item in 4" :key="item" class="quantity-item" :class="{'selected': item == quantity}" @click="selectQuantity(item)">{{ item }}张</div>
|
:options="quantityOptions"
|
||||||
</div>
|
class="quantity-select"
|
||||||
<template #reference>
|
position="top"
|
||||||
<div class="choice-btn">
|
>
|
||||||
<img src="@/assets/dialog/quantity.svg" alt="" style="width: 16px;">
|
<template #prefix>
|
||||||
<span>{{ quantity }} 张</span>
|
<img src="@/assets/dialog/quantity.svg" alt="" style="width: 16px;">
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</el-popover>
|
</Select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue'
|
import Select from '@/components/Select/index.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|
@ -29,50 +28,39 @@ const quantity = computed({
|
||||||
set: (value) => emit('update:modelValue', value)
|
set: (value) => emit('update:modelValue', value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectQuantity = (value) => {
|
const quantityOptions = [
|
||||||
quantity.value = value
|
{ value: 1, label: '1 张' },
|
||||||
}
|
{ value: 2, label: '2 张' },
|
||||||
|
{ value: 3, label: '3 张' },
|
||||||
|
{ value: 4, label: '4 张' }
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.choice-btn{
|
.quantity-select {
|
||||||
display: flex;
|
:deep(.select-header) {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
justify-content: center;
|
border-radius: 10px;
|
||||||
align-items: center;
|
border: 1px solid rgba(0, 0, 0, 0.10);
|
||||||
gap: 5px;
|
background: #ffffff;
|
||||||
border-radius: 10px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.10);
|
&:hover {
|
||||||
background: #ffffff;
|
background: #E5E7EB;
|
||||||
cursor: pointer;
|
}
|
||||||
position: relative;
|
}
|
||||||
}
|
|
||||||
.choice-btn:hover{
|
:deep(.select-text) {
|
||||||
background: #E5E7EB;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
.quantity-container{
|
|
||||||
display: flex;
|
:deep(.dropdown-menu) {
|
||||||
padding: 5px;
|
min-width: 80px;
|
||||||
align-items: center;
|
}
|
||||||
align-self: stretch;
|
|
||||||
border-radius: 10px;
|
:deep(.dropdown-item) {
|
||||||
background: #F8F9FA;
|
min-width: 80px;
|
||||||
}
|
justify-content: center;
|
||||||
.quantity-item{
|
}
|
||||||
display: flex;
|
|
||||||
width: 80px;
|
|
||||||
height: 32px;
|
|
||||||
padding: 0 10px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.quantity-item.selected{
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
.quantity-item:hover{
|
|
||||||
background: #E5E7EB;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -7,13 +7,14 @@ const route = useRoute()
|
||||||
const shouldShowDisplay = route.path === '/home'
|
const shouldShowDisplay = route.path === '/home'
|
||||||
const loading = route.query.loading ? false : (route.path === '/home')
|
const loading = route.query.loading ? false : (route.path === '/home')
|
||||||
const Generate = route.query.Generate || false
|
const Generate = route.query.Generate || false
|
||||||
|
const type = route.query.type || 'painting'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<dialogBox :is-generate="shouldShowDisplay" :generate="Generate" />
|
<dialogBox :is-generate="shouldShowDisplay" :type="type" :generate="Generate" />
|
||||||
|
|
||||||
<display :if="shouldShowDisplay" :loading="loading" />
|
<display :if="shouldShowDisplay" :type="type" :loading="loading" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue