This commit is contained in:
2026-02-06 16:23:22 +08:00
parent cafe7b0d99
commit e1edefbfab
43 changed files with 725 additions and 117 deletions

View File

@@ -0,0 +1,153 @@
<template>
<div class="card">
<div class="header">
<svg-icon :name="currentComponent.type" color="#fff" />
<span>{{ currentComponent.title }}</span>
<div class="add" @click="emit('add')"><svg-icon name="add" size="14" /></div>
</div>
<div class="body">
<component :is="currentComponent.component" ref="componentRef" />
</div>
<div class="footer">
<button @click="onGenerateClick">
<svg-icon name="xingxing" size="16" />
<span>Generate</span>
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, markRaw, onMounted } from 'vue'
import ToRealStyle from './cards/to-real-style.vue'
import SceneComposition from './cards/scene-composition.vue'
const components = [
{
type: 'to-real-style',
title: 'To Real Style',
component: ToRealStyle
},
{
type: 'scene-composition',
title: 'Scene Composition',
component: SceneComposition
},
{
type: 'color-palette',
title: 'Color Palette',
component: SceneComposition
},
{
type: 'to-video',
title: 'To Video',
component: SceneComposition
},
{
type: 'to-3d-model',
title: 'To 3D Model',
component: SceneComposition
},
{
type: 'add-print',
title: 'Add Print',
component: SceneComposition
},
{
type: 'edit-material',
title: 'Edit Material',
component: SceneComposition
}
]
const emit = defineEmits(['add', 'generate'])
const props = defineProps({
type: {
type: String,
default: 'to-real-style'
}
})
const currentComponent = computed(() => {
return components.find((item) => item.type === props.type)
})
const componentRef = ref(null)
const onGenerateClick = () => {
const data = { ...componentRef.value.data }
console.log(data)
emit('generate')
}
</script>
<style lang="less" scoped>
.card {
width: 25rem;
height: auto;
--border-radius: 1.6rem;
border-radius: var(--border-radius);
background-color: #fff;
> .header {
border-radius: var(--border-radius) var(--border-radius) 0 0;
height: 5rem;
background: #ff7a51;
display: flex;
align-items: center;
padding-left: 1.6rem;
position: relative;
> .c-svg {
width: auto;
height: auto;
margin-right: 0.6rem;
}
> span {
font-family: Semibold;
font-size: 1.6rem;
color: #fff;
}
> .add {
position: absolute;
width: 3.2rem;
height: 3.2rem;
border: 0.2rem solid #fff;
bottom: -1.6rem;
right: -1.6rem;
background-color: #ed8936;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 2.5rem;
box-shadow: 0 0.8rem 2rem 0 #71809633;
cursor: pointer;
}
}
> .body {
padding: 1.6rem 1.3rem;
}
> .footer {
margin-bottom: 1.6rem;
display: flex;
flex-direction: row-reverse;
padding: 0 1.3rem;
> button {
display: flex;
align-items: center;
justify-content: center;
width: 11rem;
height: 2.8rem;
border-radius: 0.5rem;
background-color: #fffcf4;
border: 1px solid #ffcf90;
font-size: 1.2rem;
font-family: SemiBold;
cursor: pointer;
&:active {
opacity: 0.5;
}
> .c-svg {
width: auto;
height: auto;
margin-right: 0.4rem;
}
}
}
}
</style>

View File

@@ -0,0 +1,22 @@
<template>
<!-- 场景构图 -->
<div class="scene-composition"></div>
</template>
<script setup lang="ts">
import { computed, ref, markRaw, onMounted } from 'vue'
import { useGlobalStore } from '@/stores'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const globalStore = useGlobalStore()
onMounted(() => {
globalStore.setHomeLeftNavCollapse(true)
})
</script>
<style lang="less" scoped>
.scene-composition {
width: 100%;
}
</style>

View File

@@ -0,0 +1,39 @@
<template>
<!-- 转换为真实图 -->
<div class="to-real-style">
<my-textarea v-model="data.prompt" />
<pixel-ratio-selection v-model="data.pixelRatio" />
<upload-file v-model="data.file" />
</div>
</template>
<script setup lang="ts">
import { computed, ref, reactive, onMounted, defineExpose } from 'vue'
import myTextarea from '../my-textarea.vue'
import pixelRatioSelection from '../pixel-ratio-selection.vue'
import uploadFile from '../upload-file.vue'
import { useGlobalStore } from '@/stores'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const globalStore = useGlobalStore()
onMounted(() => {
globalStore.setHomeLeftNavCollapse(true)
})
const data = reactive({
prompt: '123123',
pixelRatio: '1:1',
file: null
})
defineExpose({ data })
</script>
<style lang="less" scoped>
.to-real-style {
width: 100%;
> * {
margin-bottom: 1rem;
}
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<div class="my-textarea">
<textarea
:placeholder="placeholder"
:value="modelValue"
@input="onInput"
@change="onChange"
></textarea>
<div class="bths">
<button><svg-icon name="mobang" size="10" /></button>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, markRaw, onMounted } from 'vue'
const emit = defineEmits(['update:modelValue', 'input', 'change'])
const props = defineProps({
modelValue: { type: String },
placeholder: {
type: String,
default: 'Enter the scene you want to describe...'
}
})
const onInput = (e) => {
const value = e.target.value
emit('update:modelValue', value)
emit('input', value)
}
const onChange = (e) => {
emit('change', e.target.value)
}
</script>
<style lang="less" scoped>
.my-textarea {
width: 100%;
height: 11.5rem;
border: 0.1rem solid #e4e4e7;
border-radius: 1rem;
padding: 1rem 0.5rem;
display: flex;
flex-direction: column;
> textarea {
padding: 0 0.5rem;
width: 100%;
flex: 1;
border: none;
outline: none;
resize: none;
font-family: Medium;
font-size: 1rem;
color: #333;
&::placeholder {
color: #c9c9c9;
}
&::-webkit-scrollbar {
width: 0.4rem;
}
&::-webkit-scrollbar-thumb {
border-radius: 0.4rem;
background: rgba(0, 0, 0, 0.2);
}
&::-webkit-scrollbar-track {
border-radius: 0.4rem;
background: rgba(0, 0, 0, 0.1);
}
}
> .bths {
padding: 0.5rem 0.5rem 0;
> button {
width: 2rem;
height: 2rem;
padding: 0;
border-radius: 0.4rem;
background-color: #f0f0f0;
border: 1px solid #e4e4e7;
&:active {
opacity: 0.5;
}
}
}
}
</style>

View File

@@ -0,0 +1,67 @@
<template>
<div class="pixel-ratio-selection">
<div
v-for="v in list"
:key="v"
:class="{ active: v === modelValue }"
@click="onChange(v)"
:style="{ '--w': v.split(':')[0], '--h': v.split(':')[1] }"
>
{{ v }}
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, defineExpose } from 'vue'
const emit = defineEmits(['update:modelValue', 'change'])
const props = defineProps({
modelValue: { type: String },
list: {
type: Array,
default: () => ['1:1', '4:3', '3:4', '16:9']
}
})
const data = reactive({})
const onChange = (v) => {
emit('update:modelValue', v)
emit('change', v)
}
defineExpose({ data })
</script>
<style lang="less" scoped>
.pixel-ratio-selection {
width: 100%;
height: 3.4rem;
border-radius: 0.6rem;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
padding: 0 1.7rem;
> div {
text-align: center;
font-size: 1.2rem;
color: #7c7c7c;
height: 2.1rem;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
&.active {
border-radius: 0.4rem;
background-color: #fff;
}
&::before {
content: '';
border-radius: 0.1rem;
border: 0.1rem solid #7c7c7c;
width: calc(var(--w) / max(var(--w), var(--h)) * 1rem);
height: calc(var(--h) / max(var(--w), var(--h)) * 1rem);
margin-right: 0.4rem;
box-sizing: border-box;
}
}
}
</style>

View File

@@ -0,0 +1,59 @@
<template>
<div class="upload-file">
<div class="icon"><svg-icon name="upload" size="17" /></div>
<span class="txt">Upload your files</span>
<button class="btn">Select File</button>
</div>
</template>
<script setup lang="ts">
import { reactive, defineExpose } from 'vue'
const emit = defineEmits(['update:modelValue', 'change'])
const props = defineProps({
modelValue: { type: String },
list: {
type: Array,
default: () => ['1:1', '4:3', '3:4', '16:9']
}
})
const data = reactive({})
const onChange = (v) => {
emit('update:modelValue', v)
emit('change', v)
}
defineExpose({ data })
</script>
<style lang="less" scoped>
.upload-file {
width: 100%;
height: 9.9rem;
border-radius: 1rem;
background-color: #f0f0f0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
// padding: 0 1.7rem;
> .txt {
margin-top: 0.6rem;
margin-bottom: 0.8rem;
font-size: 0.8rem;
color: #7c7c7c;
}
> button {
box-shadow: 0px 0.75px 0px 0px #00000005;
min-width: 3.9rem;
height: 1.3rem;
border-radius: 0.23rem;
background-color: #fff;
font-size: 0.6rem;
color: #000;
border: 0.05rem solid #d9d9d9;
cursor: pointer;
&:active {
opacity: 0.6;
}
}
}
</style>

View File

@@ -0,0 +1,35 @@
<template>
<div class="canvas">
<card type="to-real-style" />
<card type="scene-composition" />
<card type="color-palette" />
<card type="to-video" />
<card type="to-3d-model" />
<card type="add-print" />
<card type="edit-material" />
</div>
</template>
<script setup lang="ts">
import card from './components/card.vue'
import { computed, ref, markRaw, onMounted } from 'vue'
import { useGlobalStore } from '@/stores'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const globalStore = useGlobalStore()
onMounted(() => {
globalStore.setHomeLeftNavCollapse(true)
})
</script>
<style lang="less" scoped>
.canvas {
background-color: #fcf8f1;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
padding: 2rem;
gap: 2rem;
}
</style>