Merge branch 'main' of http://18.167.251.121:10003/aidlab/FiDA_Front
This commit is contained in:
BIN
src/assets/images/assistant-trigger.png
Normal file
BIN
src/assets/images/assistant-trigger.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 978 B |
@@ -1,84 +1,137 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="assistant-container flex flex-col space-between" ref="containerRef">
|
<div
|
||||||
<div class="assistant-header flex space-between align-center" @mousedown="handleMouseDown">
|
class="trigger flex flex-center"
|
||||||
|
:class="{ 'has-unread': hasUnread }"
|
||||||
|
ref="triggerRef"
|
||||||
|
@mousedown="handleTriggerMouseDown"
|
||||||
|
@click="handleTriggerClick"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="@/assets/images/assistant-trigger.png"
|
||||||
|
class="trigger-icon"
|
||||||
|
alt=""
|
||||||
|
@dragstart.prevent
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-show="showAssistant"
|
||||||
|
class="assistant-container flex flex-col space-between"
|
||||||
|
ref="containerRef"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="assistant-header flex space-between align-center"
|
||||||
|
@mousedown="handleContainerMouseDown"
|
||||||
|
>
|
||||||
<div class="assistant-header-title">AI Assistant</div>
|
<div class="assistant-header-title">AI Assistant</div>
|
||||||
<SvgIcon name="canvas-assistant-menu" class="menu-icon" color="#D58C4D" />
|
<SvgIcon name="canvas-assistant-menu" class="menu-icon" color="#D58C4D" />
|
||||||
</div>
|
</div>
|
||||||
<div class="assistant-body flex-1">
|
<div class="assistant-body flex-1">
|
||||||
<List :messageList="messageList" />
|
<List :messageList="messageList" />
|
||||||
</div>
|
</div>
|
||||||
<div class="assistant-input flex align-center">
|
<!-- <div class="assistant-input flex align-center">
|
||||||
<el-input v-model="inputMessage" :placeholder="$t('assistant.inputPlaceholder')" />
|
<el-input v-model="inputMessage" :placeholder="$t('assistant.inputPlaceholder')" />
|
||||||
<div class="sender-btn flex flex-center" @click="handleSendAgent">
|
<div class="sender-btn flex flex-center" @click="handleSendAgent">
|
||||||
<img src="@/assets/images/sender.png" class="sender-icon" />
|
<img src="@/assets/images/sender.png" class="sender-icon" />
|
||||||
<!-- <div class="sender-pause" /> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, onMounted, watch } from 'vue'
|
||||||
import List from './component/List.vue'
|
import List from './component/List.vue'
|
||||||
import { useDraggable } from './component/useDraggable'
|
import { useDraggable } from './component/useDraggable'
|
||||||
|
import MyEvent from '@/utils/myEvent'
|
||||||
|
|
||||||
|
interface Message {
|
||||||
|
nodeType: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
const inputMessage = ref('')
|
const inputMessage = ref('')
|
||||||
const containerRef = ref<HTMLElement>()
|
const containerRef = ref<HTMLElement>()
|
||||||
|
const triggerRef = ref<HTMLElement>()
|
||||||
|
|
||||||
// 使用拖拽 hook
|
const { handleMouseDown: handleContainerMouseDown } = useDraggable(containerRef)
|
||||||
const { handleMouseDown } = useDraggable(containerRef)
|
const {
|
||||||
|
handleMouseDown: handleTriggerMouseDown,
|
||||||
|
consumeClickSuppression: consumeTriggerClickSuppression
|
||||||
|
} = useDraggable(triggerRef, {
|
||||||
|
boundary: 'parent'
|
||||||
|
})
|
||||||
|
|
||||||
const messageList = ref([
|
const showAssistant = ref(false)
|
||||||
{
|
|
||||||
content: 'Hi! How can I help you, today?',
|
const handleTriggerClick = () => {
|
||||||
id: 1,
|
if (consumeTriggerClickSuppression()) {
|
||||||
role: 'assistant'
|
return
|
||||||
},
|
|
||||||
{
|
|
||||||
content: 'Please recommend some .',
|
|
||||||
id: 2,
|
|
||||||
role: 'user'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: 'I noticed that you inputed a new sketch.',
|
|
||||||
id: 3,
|
|
||||||
role: 'system',
|
|
||||||
type: 'info'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: 'Hi! How can I help you, today?',
|
|
||||||
id: 4,
|
|
||||||
role: 'assistant'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: 'Please recommend some .',
|
|
||||||
id: 5,
|
|
||||||
role: 'user'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: 'I noticed that you inputed a new sketch.',
|
|
||||||
id: 36,
|
|
||||||
role: 'system',
|
|
||||||
type: 'info'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: 'Hi! How can I help you, today?',
|
|
||||||
id: 31,
|
|
||||||
role: 'assistant'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: 'Please recommend some .',
|
|
||||||
id: 22,
|
|
||||||
role: 'user'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: 'I noticed that you inputed a new sketch.',
|
|
||||||
id: 63,
|
|
||||||
role: 'system',
|
|
||||||
type: 'info'
|
|
||||||
}
|
}
|
||||||
])
|
showAssistant.value = !showAssistant.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageList = ref<Message[]>([])
|
||||||
|
const hasUnread = ref(false)
|
||||||
|
watch(
|
||||||
|
() => showAssistant.value,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
hasUnread.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const listenAssistantPushChat = (message: Message) => {
|
||||||
|
// console.log('有新消息--');
|
||||||
|
|
||||||
|
const exist = messageList.value.find((item: Message) => item.nodeType === message.nodeType)
|
||||||
|
if (!exist) {
|
||||||
|
messageList.value.push(message)
|
||||||
|
hasUnread.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
MyEvent.add('assistantPushChat', listenAssistantPushChat)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.trigger {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
width: 5rem;
|
||||||
|
height: 5rem;
|
||||||
|
bottom: 50%;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: grab;
|
||||||
|
user-select: none;
|
||||||
|
box-shadow: 0px 13.86px 19.43px 0px #0000000d;
|
||||||
|
border: 0.18rem solid #ebebeb;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
&.has-unread {
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0.5rem;
|
||||||
|
right: 0.4rem;
|
||||||
|
width: 0.6rem;
|
||||||
|
height: 0.6rem;
|
||||||
|
background-color: #ff4747;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.trigger-icon {
|
||||||
|
width: 2.7rem;
|
||||||
|
height: 2.7rem;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.assistant-container {
|
.assistant-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 6.2rem;
|
right: 6.2rem;
|
||||||
@@ -95,6 +148,7 @@
|
|||||||
rgba(255, 207, 144, 0.3) 101.01%
|
rgba(255, 207, 144, 0.3) 101.01%
|
||||||
);
|
);
|
||||||
border-radius: 1.69rem;
|
border-radius: 1.69rem;
|
||||||
|
|
||||||
.assistant-header {
|
.assistant-header {
|
||||||
height: 5.66rem;
|
height: 5.66rem;
|
||||||
font-family: 'Regular';
|
font-family: 'Regular';
|
||||||
@@ -109,17 +163,21 @@
|
|||||||
border-top-right-radius: 1.69rem;
|
border-top-right-radius: 1.69rem;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon {
|
.menu-icon {
|
||||||
width: 1.8rem;
|
width: 1.8rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.assistant-body {
|
.assistant-body {
|
||||||
margin: 1.6rem 0 2.9rem;
|
margin: 1.6rem 0 2.9rem;
|
||||||
padding: 0 1.95rem 0 2.5rem;
|
padding: 0 1.95rem 0 2.5rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 0;
|
width: 0;
|
||||||
display: none;
|
display: none;
|
||||||
@@ -131,11 +189,13 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.assistant-input {
|
.assistant-input {
|
||||||
border: 1px solid #0000001a;
|
border: 1px solid #0000001a;
|
||||||
margin: 0 2rem 3rem;
|
margin: 0 2rem 3rem;
|
||||||
border-radius: 3.53rem;
|
border-radius: 3.53rem;
|
||||||
padding: 0 1rem 0 1.7rem;
|
padding: 0 1rem 0 1.7rem;
|
||||||
|
|
||||||
.el-input {
|
.el-input {
|
||||||
:deep(.el-input__wrapper) {
|
:deep(.el-input__wrapper) {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@@ -143,6 +203,7 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sender-btn {
|
.sender-btn {
|
||||||
width: 2.2rem;
|
width: 2.2rem;
|
||||||
height: 2.2rem;
|
height: 2.2rem;
|
||||||
@@ -154,6 +215,7 @@
|
|||||||
width: 0.9rem;
|
width: 0.9rem;
|
||||||
height: 0.9rem;
|
height: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sender-pause {
|
.sender-pause {
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<img src="@/assets/images/assistant-head.png" class="assistant-head" />
|
<img src="@/assets/images/assistant-head.png" class="assistant-head" />
|
||||||
</div>
|
</div>
|
||||||
<div class="list-item-content-text">
|
<div class="list-item-content-text">
|
||||||
{{$t('Assistant.greeting')}}
|
{{ $t('Assistant.greeting') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -21,7 +21,13 @@
|
|||||||
class="assistant-head"
|
class="assistant-head"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-item-content-text">{{ item.content }}</div>
|
<div class="list-item-content-text">
|
||||||
|
<VueMarkdown
|
||||||
|
:custom-attrs="customAttrs"
|
||||||
|
:markdown="item.content"
|
||||||
|
:rehype-plugins="[rehypeRaw]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,32 +1,55 @@
|
|||||||
import { ref, onUnmounted, type Ref } from 'vue'
|
import { ref, onUnmounted, type Ref } from 'vue'
|
||||||
|
|
||||||
interface UseDraggableOptions {
|
interface UseDraggableOptions {
|
||||||
|
boundary?: 'none' | 'parent'
|
||||||
|
threshold?: number
|
||||||
onDragStart?: () => void
|
onDragStart?: () => void
|
||||||
onDragEnd?: () => void
|
onDragEnd?: (moved: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDraggable(targetRef: Ref<HTMLElement | undefined>, options?: UseDraggableOptions) {
|
export function useDraggable(
|
||||||
|
targetRef: Ref<HTMLElement | undefined>,
|
||||||
|
options: UseDraggableOptions = {}
|
||||||
|
) {
|
||||||
const isDragging = ref(false)
|
const isDragging = ref(false)
|
||||||
|
const hasMoved = ref(false)
|
||||||
|
const suppressClick = ref(false)
|
||||||
const startX = ref(0)
|
const startX = ref(0)
|
||||||
const startY = ref(0)
|
const startY = ref(0)
|
||||||
const startLeft = ref(0)
|
const startLeft = ref(0)
|
||||||
const startTop = ref(0)
|
const startTop = ref(0)
|
||||||
|
|
||||||
const handleMouseDown = (e: MouseEvent) => {
|
const boundary = options.boundary ?? 'none'
|
||||||
// 只允许左键拖拽
|
const threshold = options.threshold ?? 3
|
||||||
if (e.button !== 0) return
|
|
||||||
|
|
||||||
isDragging.value = true
|
const getParent = (el: HTMLElement) => {
|
||||||
startX.value = e.clientX
|
return (el.offsetParent as HTMLElement | null) ?? document.body
|
||||||
startY.value = e.clientY
|
|
||||||
|
|
||||||
if (targetRef.value) {
|
|
||||||
const rect = targetRef.value.getBoundingClientRect()
|
|
||||||
startLeft.value = rect.left
|
|
||||||
startTop.value = rect.top
|
|
||||||
}
|
}
|
||||||
|
|
||||||
options?.onDragStart?.()
|
const cleanup = () => {
|
||||||
|
isDragging.value = false
|
||||||
|
document.removeEventListener('mousemove', handleMouseMove)
|
||||||
|
document.removeEventListener('mouseup', handleMouseUp)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
|
if (e.button !== 0 || !targetRef.value) return
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const target = targetRef.value
|
||||||
|
const targetRect = target.getBoundingClientRect()
|
||||||
|
const parentRect = getParent(target).getBoundingClientRect()
|
||||||
|
|
||||||
|
isDragging.value = true
|
||||||
|
hasMoved.value = false
|
||||||
|
suppressClick.value = false
|
||||||
|
startX.value = e.clientX
|
||||||
|
startY.value = e.clientY
|
||||||
|
startLeft.value = targetRect.left - parentRect.left
|
||||||
|
startTop.value = targetRect.top - parentRect.top
|
||||||
|
|
||||||
|
options.onDragStart?.()
|
||||||
|
|
||||||
document.addEventListener('mousemove', handleMouseMove)
|
document.addEventListener('mousemove', handleMouseMove)
|
||||||
document.addEventListener('mouseup', handleMouseUp)
|
document.addEventListener('mouseup', handleMouseUp)
|
||||||
@@ -35,27 +58,51 @@ export function useDraggable(targetRef: Ref<HTMLElement | undefined>, options?:
|
|||||||
const handleMouseMove = (e: MouseEvent) => {
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
if (!isDragging.value || !targetRef.value) return
|
if (!isDragging.value || !targetRef.value) return
|
||||||
|
|
||||||
|
const target = targetRef.value
|
||||||
|
const parent = getParent(target)
|
||||||
const deltaX = e.clientX - startX.value
|
const deltaX = e.clientX - startX.value
|
||||||
const deltaY = e.clientY - startY.value
|
const deltaY = e.clientY - startY.value
|
||||||
|
|
||||||
targetRef.value.style.left = `${startLeft.value + deltaX}px`
|
if (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold) {
|
||||||
targetRef.value.style.top = `${startTop.value + deltaY}px`
|
hasMoved.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextLeft = startLeft.value + deltaX
|
||||||
|
let nextTop = startTop.value + deltaY
|
||||||
|
|
||||||
|
if (boundary === 'parent') {
|
||||||
|
const maxLeft = Math.max(parent.clientWidth - target.offsetWidth, 0)
|
||||||
|
const maxTop = Math.max(parent.clientHeight - target.offsetHeight, 0)
|
||||||
|
nextLeft = Math.min(Math.max(nextLeft, 0), maxLeft)
|
||||||
|
nextTop = Math.min(Math.max(nextTop, 0), maxTop)
|
||||||
|
}
|
||||||
|
|
||||||
|
target.style.left = `${nextLeft}px`
|
||||||
|
target.style.top = `${nextTop}px`
|
||||||
|
target.style.right = 'auto'
|
||||||
|
target.style.bottom = 'auto'
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMouseUp = () => {
|
const handleMouseUp = () => {
|
||||||
isDragging.value = false
|
suppressClick.value = hasMoved.value
|
||||||
document.removeEventListener('mousemove', handleMouseMove)
|
cleanup()
|
||||||
document.removeEventListener('mouseup', handleMouseUp)
|
options.onDragEnd?.(hasMoved.value)
|
||||||
options?.onDragEnd?.()
|
}
|
||||||
|
|
||||||
|
const consumeClickSuppression = () => {
|
||||||
|
const value = suppressClick.value
|
||||||
|
suppressClick.value = false
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
document.removeEventListener('mousemove', handleMouseMove)
|
cleanup()
|
||||||
document.removeEventListener('mouseup', handleMouseUp)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isDragging,
|
isDragging,
|
||||||
handleMouseDown
|
hasMoved,
|
||||||
|
handleMouseDown,
|
||||||
|
consumeClickSuppression
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
const onClickStyle = (value: string) => {
|
const onClickStyle = (value: string) => {
|
||||||
if (data.styles.includes(value)) {
|
if (data.styles.includes(value)) {
|
||||||
data.styles = data.styles.filter((v) => v !== value)
|
data.styles = data.styles.filter((v) => v !== value)
|
||||||
} else {
|
} else if(data.styles.length < 5){
|
||||||
data.styles.push(value)
|
data.styles.push(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<span class="icon" @click="onPreview(item)">
|
<span class="icon" @click="onPreview(item)">
|
||||||
<svg-icon name="expand-lg" size="20" size-unit="px" />
|
<svg-icon name="expand-lg" size="20" size-unit="px" />
|
||||||
</span>
|
</span>
|
||||||
<span class="icon" @click="onDownload(item?.url)">
|
<span class="icon" @click="onDownload(item)">
|
||||||
<svg-icon name="download" size="20" size-unit="px" />
|
<svg-icon name="download" size="20" size-unit="px" />
|
||||||
</span>
|
</span>
|
||||||
<button class="edit" @click="onEdit(item)">
|
<button class="edit" @click="onEdit(item)">
|
||||||
@@ -190,8 +190,12 @@
|
|||||||
openImagePreview(item.url)
|
openImagePreview(item.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const onDownload = (url: string) => {
|
const onDownload = (item: any) => {
|
||||||
downloadImage(url, 'image.png')
|
if(data.superiorNodeType == NODE_DATATYPE.TO_3D_MODEL){
|
||||||
|
if(item?.glbPath)downloadImage(item.glbPath, 'model.glb')
|
||||||
|
}else{
|
||||||
|
downloadImage(item.url, 'image.png')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const onMenuItem = (v) => {
|
const onMenuItem = (v) => {
|
||||||
if (v.disabled) return
|
if (v.disabled) return
|
||||||
|
|||||||
@@ -164,7 +164,11 @@ export default {
|
|||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
edit: 'Edit',
|
edit: 'Edit',
|
||||||
generatingReport:
|
generatingReport:
|
||||||
'We are generating your report. This may take a few minutes, but you can continue with other tasks while it’s being generated.'
|
'We are generating your report. This may take a few minutes, but you can continue with other tasks while it’s being generated.',
|
||||||
|
deleteSketchTip:
|
||||||
|
'Are you sure you want to delete this sketch? This action cannot be undone.',
|
||||||
|
confirm: 'Confirm',
|
||||||
|
cancel: 'Cancel'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Version Tree
|
// Version Tree
|
||||||
@@ -195,14 +199,20 @@ export default {
|
|||||||
cannotFindSuperiorImage: 'Cannot find the superior image',
|
cannotFindSuperiorImage: 'Cannot find the superior image',
|
||||||
deleteSubordinateCard: 'After deletion, all the function cards will also be deleted.',
|
deleteSubordinateCard: 'After deletion, all the function cards will also be deleted.',
|
||||||
initialNodeProhibited: 'Initial node is prohibited from being deleted.',
|
initialNodeProhibited: 'Initial node is prohibited from being deleted.',
|
||||||
toRealStyleDesignAssistant: "🎨 This is one of my favorite parts — just bring in your sketch and I'll take care of the lighting, materials, and rendering to give you a photorealistic result. If the first one doesn't feel quite right, let me try a few more times — I'll give you something a little different each time.",
|
toRealStyleDesignAssistant:
|
||||||
surfaceEditCanvasDesignAssistant: "🪡 If you have something specific in mind for the materials, this is the mode for that. I can help you swap to fabric, leather, wood, or whatever fits your vision — and if you have your own print, you can upload it directly. This mode gives you full manual control so you can fine-tune exactly what you want. If you'd rather move faster, just switch over and I'll handle it with AI Smart Mapping.",
|
"🎨 This is one of my favorite parts — just bring in your sketch and I'll take care of the lighting, materials, and rendering to give you a photorealistic result. If the first one doesn't feel quite right, let me try a few more times — I'll give you something a little different each time.",
|
||||||
surfaceEditAIDesignAssistant: "🪡 Want to see how a different material looks without the extra steps? Tell me what you're going for and I'll apply it using AI Smart Mapping — quick and automatic. If you want to dial in the details afterward, just switch to Canvas Mode and we can keep refining.",
|
surfaceEditCanvasDesignAssistant:
|
||||||
colorPaletteDesignAssistant: "🎨 Let me handle the color combinations for you — just pick a few colors you like, and I'll mix and apply them to your product automatically. I can generate several options at once so you can compare and go with the one that feels right.",
|
"🪡 If you have something specific in mind for the materials, this is the mode for that. I can help you swap to fabric, leather, wood, or whatever fits your vision — and if you have your own print, you can upload it directly. This mode gives you full manual control so you can fine-tune exactly what you want. If you'd rather move faster, just switch over and I'll handle it with AI Smart Mapping.",
|
||||||
sceneCompositionDesignAssistant: "🛋️ I'll place your product into a real interior scene and match the lighting automatically so everything looks like it belongs together. My suggestion: let me make one clean hero shot with a simple background first, then one lifestyle image with more atmosphere — the two together tend to work really well for presentations.",
|
surfaceEditAIDesignAssistant:
|
||||||
threeModelDesignAssistant: "🔄 I'll turn your render into a 3D model you can rotate and look at from any angle. I'd recommend paying close attention to the corner joints, leg proportions, and seat depth — these are the spots that are easy to miss in a sketch but tend to cause the most trouble during prototyping. Better to catch them now while it's easy to fix.",
|
"🪡 Want to see how a different material looks without the extra steps? Tell me what you're going for and I'll apply it using AI Smart Mapping — quick and automatic. If you want to dial in the details afterward, just switch to Canvas Mode and we can keep refining.",
|
||||||
to3DViewDesignAssistant: "📐 We're at the final step! I'll export your 3D model as front, side, and top view!",
|
colorPaletteDesignAssistant:
|
||||||
|
"🎨 Let me handle the color combinations for you — just pick a few colors you like, and I'll mix and apply them to your product automatically. I can generate several options at once so you can compare and go with the one that feels right.",
|
||||||
|
sceneCompositionDesignAssistant:
|
||||||
|
"🛋️ I'll place your product into a real interior scene and match the lighting automatically so everything looks like it belongs together. My suggestion: let me make one clean hero shot with a simple background first, then one lifestyle image with more atmosphere — the two together tend to work really well for presentations.",
|
||||||
|
threeModelDesignAssistant:
|
||||||
|
"🔄 I'll turn your render into a 3D model you can rotate and look at from any angle. I'd recommend paying close attention to the corner joints, leg proportions, and seat depth — these are the spots that are easy to miss in a sketch but tend to cause the most trouble during prototyping. Better to catch them now while it's easy to fix.",
|
||||||
|
to3DViewDesignAssistant:
|
||||||
|
"📐 We're at the final step! I'll export your 3D model as front, side, and top view!"
|
||||||
},
|
},
|
||||||
assistant: {
|
assistant: {
|
||||||
inputPlaceholder: 'Ask anything'
|
inputPlaceholder: 'Ask anything'
|
||||||
|
|||||||
@@ -155,7 +155,10 @@ export default {
|
|||||||
quote: '引用',
|
quote: '引用',
|
||||||
delete: '删除',
|
delete: '删除',
|
||||||
edit: '编辑',
|
edit: '编辑',
|
||||||
generatingReport: '正在为您生成报告,可能需要几分钟时间,生成期间你可以继续进行其他任务'
|
generatingReport: '正在为您生成报告,可能需要几分钟时间,生成期间你可以继续进行其他任务',
|
||||||
|
deleteSketchTip: '确定要删除这个草图吗?此操作无法撤销。',
|
||||||
|
confirm: '确认',
|
||||||
|
cancel: '取消'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Version Tree
|
// Version Tree
|
||||||
@@ -191,13 +194,20 @@ export default {
|
|||||||
confirmLeave: '您可能有未保存的更改,确定要离开吗?',
|
confirmLeave: '您可能有未保存的更改,确定要离开吗?',
|
||||||
cannotFindSuperiorImage: '找不到上级图片',
|
cannotFindSuperiorImage: '找不到上级图片',
|
||||||
initialNodeProhibited: '初始节点不能删除',
|
initialNodeProhibited: '初始节点不能删除',
|
||||||
toRealStyleDesignAssistant: "🎨 这个功能我很喜欢——你只需要把草图丢进来,我来帮你把光影和材质都处理好,直接生成真实感效果图。出来不满意的话就多试几次,每次我都会给你不一样的结果。",
|
toRealStyleDesignAssistant:
|
||||||
surfaceEditCanvasDesignAssistant: "🪡 如果你对材质有具体想法,可以用这个模式来做。布艺、皮革、木材这些都可以换,也可以把你自己的印花上传进来。这个模式支持你手动精细编辑,想细调哪里都可以——当然如果想快一点,也可以切换到 AI 智能贴图帮你完成。",
|
'🎨 这个功能我很喜欢——你只需要把草图丢进来,我来帮你把光影和材质都处理好,直接生成真实感效果图。出来不满意的话就多试几次,每次我都会给你不一样的结果。',
|
||||||
surfaceEditAIDesignAssistant: "🪡 想快速看看换材质之后的效果?用这个模式,把你想要的材质或印花告诉我,AI 智能贴图帮你一步到位。如果觉得还想再调整细节,随时可以切换到 Canvas 模式继续编辑。",
|
surfaceEditCanvasDesignAssistant:
|
||||||
colorPaletteDesignAssistant: "🎨 配色交给我来帮你——你选几种喜欢的颜色,我来帮你搭配应用到产品上。我可以一次给你生成好几个方案,你对比着挑就好。",
|
'🪡 如果你对材质有具体想法,可以用这个模式来做。布艺、皮革、木材这些都可以换,也可以把你自己的印花上传进来。这个模式支持你手动精细编辑,想细调哪里都可以——当然如果想快一点,也可以切换到 AI 智能贴图帮你完成。',
|
||||||
sceneCompositionDesignAssistant: "🛋️ 我来帮你把产品放进一个真实的空间场景里,光影我会自动帮你匹配。我的建议是先出一张背景干净的主图,再出一张有生活感的氛围图,两张配合着用,展示效果会好很多。",
|
surfaceEditAIDesignAssistant:
|
||||||
threeModelDesignAssistant: "🔄 我把你的效果图变成可以转着看的立体模型,你可以从各个角度检查一下结构。我建议重点看看转角、腿脚比例和座面厚度——这几个地方在草图里不容易发现问题,但打样的时候最容易出偏差,现在发现比较好改。",
|
'🪡 想快速看看换材质之后的效果?用这个模式,把你想要的材质或印花告诉我,AI 智能贴图帮你一步到位。如果觉得还想再调整细节,随时可以切换到 Canvas 模式继续编辑。',
|
||||||
to3DViewDesignAssistant: "📐 我们到最后一步了!我来帮你把 3D 模型导出为前视图、侧视图和俯视图!",
|
colorPaletteDesignAssistant:
|
||||||
|
'🎨 配色交给我来帮你——你选几种喜欢的颜色,我来帮你搭配应用到产品上。我可以一次给你生成好几个方案,你对比着挑就好。',
|
||||||
|
sceneCompositionDesignAssistant:
|
||||||
|
'🛋️ 我来帮你把产品放进一个真实的空间场景里,光影我会自动帮你匹配。我的建议是先出一张背景干净的主图,再出一张有生活感的氛围图,两张配合着用,展示效果会好很多。',
|
||||||
|
threeModelDesignAssistant:
|
||||||
|
'🔄 我把你的效果图变成可以转着看的立体模型,你可以从各个角度检查一下结构。我建议重点看看转角、腿脚比例和座面厚度——这几个地方在草图里不容易发现问题,但打样的时候最容易出偏差,现在发现比较好改。',
|
||||||
|
to3DViewDesignAssistant:
|
||||||
|
'📐 我们到最后一步了!我来帮你把 3D 模型导出为前视图、侧视图和俯视图!'
|
||||||
},
|
},
|
||||||
assistant: {
|
assistant: {
|
||||||
inputPlaceholder: '请输入'
|
inputPlaceholder: '请输入'
|
||||||
|
|||||||
@@ -236,16 +236,22 @@
|
|||||||
MyEvent.emit('quote', url)
|
MyEvent.emit('quote', url)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClickDelete = (item: string | Object) => {
|
const handleClickDelete = (item: { id: string; url: string }) => {
|
||||||
|
ElMessageBox.confirm(t('agent.deleteSketchTip'), t('agent.confirm'), {
|
||||||
|
confirmButtonText: t('agent.confirm'),
|
||||||
|
cancelButtonText: t('agent.cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
deleteSketchFlowCanvas({
|
deleteSketchFlowCanvas({
|
||||||
id: Object.keys(item)[0],
|
id: item.id,
|
||||||
versionNodeId: projectStore.state.nodeId
|
versionNodeId: projectStore.state.nodeId
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
ElMessage.success(t('agent.deleteSuccess'))
|
ElMessage.success(t('agent.deleteSuccess'))
|
||||||
emits('deleteSketch', Object.keys(item)[0])
|
emits('deleteSketch', item.id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDownloadMd = () => {
|
const handleDownloadMd = () => {
|
||||||
|
|||||||
@@ -57,13 +57,15 @@
|
|||||||
// VersionTreeIndexRef.value.getVersionTree()
|
// VersionTreeIndexRef.value.getVersionTree()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDeleteSketch = (deletedId: string) => {
|
const handleDeleteSketch = (id) => {
|
||||||
sketchList.value = sketchList.value.filter((item) => {
|
sketchList.value = sketchList.value
|
||||||
if (typeof item === 'object') {
|
.map((sketchItem) => {
|
||||||
return Object.keys(item)[0] !== deletedId
|
if (sketchItem.hasOwnProperty(id)) {
|
||||||
|
delete sketchItem[id]
|
||||||
}
|
}
|
||||||
return true
|
return sketchItem
|
||||||
})
|
})
|
||||||
|
.filter((sketchItem) => Object.keys(sketchItem).length > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSetTitle = (title: string) => {
|
const handleSetTitle = (title: string) => {
|
||||||
@@ -93,7 +95,7 @@
|
|||||||
const handleGetProjectInfoAndHistory = () => {
|
const handleGetProjectInfoAndHistory = () => {
|
||||||
handleOpenSketch()
|
handleOpenSketch()
|
||||||
getProjectInfo({ id: route.params.id }).then((res) => {
|
getProjectInfo({ id: route.params.id }).then((res) => {
|
||||||
if(!res) {
|
if (!res) {
|
||||||
router.push({ name: 'mainInput' })
|
router.push({ name: 'mainInput' })
|
||||||
ElMessage.warning(t('Home.notFound'))
|
ElMessage.warning(t('Home.notFound'))
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user