Files
FiDA_Front/src/views/home/components/Input.vue

1099 lines
26 KiB
Vue
Raw Normal View History

2026-02-03 13:15:39 +08:00
<template>
2026-02-10 13:05:24 +08:00
<div class="assist-input-wrapper flex flex-col" :class="{ agent: isAgentMode }">
2026-03-05 11:46:41 +08:00
<div class="animate-container flex-1 flex flex-col">
<div class="scroll-content flex-col">
<!-- 图片预览区域 -->
<div v-if="uploadedImages.length > 0" class="image-preview-list flex wrap">
<div
v-for="(image, index) in uploadedImages"
:key="index"
class="image-preview-item"
>
<img :src="image.url" :alt="image.name" class="preview-image" />
<div class="image-remove-btn" @click="removeImage(index)">
<SvgIcon name="delete" size="16" />
</div>
2026-02-10 13:05:24 +08:00
</div>
</div>
2026-03-05 11:46:41 +08:00
<!-- 编辑区域 -->
<div
ref="editorRef"
class="editor"
contenteditable="true"
:placeholder="$t('Input.placeholder')"
@input="handleEditorInput"
@paste="handleEditorPaste"
@keydown="handleKeyDown"
></div>
2026-02-10 13:05:24 +08:00
</div>
2026-03-05 11:46:41 +08:00
<div class="operate flex align-center space-between">
<div class="left flex align-center">
<div class="attach flex flex-center" @click="triggerFileUpload">
<img src="@/assets/icons/attach.svg" alt="" />
</div>
<input
ref="fileInputRef"
type="file"
accept="image/*"
style="display: none"
@change="handleFileChange"
2026-02-10 13:05:24 +08:00
/>
<el-select
2026-03-05 11:46:41 +08:00
v-if="!isAgentMode"
v-model="typeValue"
:placeholder="$t('Input.typePlaceholder')"
>
<el-option
v-for="item in typeOptions"
class="input-option"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
<el-select
v-if="!isAgentMode"
v-model="areaValue"
:placeholder="$t('Input.areaPlaceholder')"
>
<el-option
v-for="item in areaOptions"
class="input-option"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
<div v-if="!isAgentMode" class="fida-style-select-wrapper">
<el-select
v-model="styleValue"
:placeholder="$t('Input.stylePlaceholder')"
@focus="openStylePopup"
/>
<el-popover
v-model:visible="stylePopupVisible"
placement="top"
:width="342"
:show-arrow="false"
trigger="click"
popper-class="fida-style-select-popover"
>
<template #reference>
<div class="fida-style-select-trigger"></div>
</template>
<div class="fida-style-popover-content flex flex-col">
<div class="fida-style-popover-header">
{{ $t('Input.chooseStyle') }}
</div>
<div class="fida-style-popover-grid">
<div
v-for="item in styleOptions"
:key="item.value"
class="fida-style-popover-item flex flex-center"
:class="{ 'is-selected': tempSelectedValue === item.value }"
@click="selectStyle(item.value)"
>
<img
:src="getStyleImage(typeValue, item.value)"
class="style-bg"
/>
<span class="fida-option-label flex flex-center">{{
item.label
}}</span>
<img
v-show="tempSelectedValue === item.value"
src="@/assets/images/checked.png"
class="checked-item-icon"
/>
</div>
</div>
<div class="fida-style-popover-footer flex flex-center">
<button class="fida-confirm-btn" @click="confirmStyle">
{{ $t('Input.confirm') }}
</button>
</div>
</div>
</el-popover>
</div>
2026-02-10 13:05:24 +08:00
<el-popover
2026-03-05 11:46:41 +08:00
v-model:visible="settingPopupVisible"
2026-02-10 13:05:24 +08:00
placement="top"
:width="342"
:show-arrow="false"
trigger="click"
2026-03-05 11:46:41 +08:00
popper-class="fida-setting-popover"
2026-02-10 13:05:24 +08:00
>
<template #reference>
2026-03-05 11:46:41 +08:00
<img src="@/assets/images/setting.png" class="setting-icon" />
2026-02-10 13:05:24 +08:00
</template>
2026-03-05 11:46:41 +08:00
<div class="fida-setting-popover-content flex flex-col">
<div class="fida-setting-popover-header">
{{ $t('Input.styleTitle') }}
2026-02-10 13:05:24 +08:00
</div>
2026-03-05 11:46:41 +08:00
<div class="fida-setting-slider-list">
2026-02-10 13:05:24 +08:00
<div
2026-03-05 11:46:41 +08:00
v-for="item in settingOptions"
:key="item.label"
class="fida-setting-slider-item"
2026-02-10 13:05:24 +08:00
>
2026-03-05 11:46:41 +08:00
<div class="fida-slider-label">{{ $t(item.label) }}</div>
<div class="fida-slider-row flex align-center">
<el-slider
class="setting-popover-slider"
v-model="item.value"
:show-tooltip="false"
/>
<span class="fida-slider-value">{{ item.value }}%</span>
</div>
2026-02-10 13:05:24 +08:00
</div>
</div>
</div>
</el-popover>
</div>
2026-03-05 11:46:41 +08:00
<div class="right">
<div
class="create-btn flex flex-center"
v-if="!isAgentMode"
@click="handleCreateProject"
>
<img src="@/assets/images/shining.png" class="shining-icon" alt="" />
<span class="create-btn-text">{{ $t('Input.createProject') }}</span>
2026-02-10 13:05:24 +08:00
</div>
2026-02-25 11:00:31 +08:00
2026-03-05 11:46:41 +08:00
<div v-else class="sender-btn flex flex-center" @click="handleSendAgent">
<img
v-show="!generating"
src="@/assets/images/sender.png"
alt=""
class="sender-icon"
/>
<div v-show="generating" class="sender-pause" />
</div>
2026-02-25 11:00:31 +08:00
</div>
2026-02-10 13:05:24 +08:00
</div>
</div>
2026-03-05 11:46:41 +08:00
2026-02-10 13:05:24 +08:00
<div v-if="!isAgentMode" class="report-btn flex flex-center" @click="toogltReportTag">
<SvgIcon class="light-icon" name="light" size="16" />
<span>{{ $t('Input.trendingReport') }}</span>
</div>
</div>
2026-02-03 13:15:39 +08:00
</template>
<script setup lang="ts">
2026-02-10 13:05:24 +08:00
import { computed, ref, watch, nextTick, onMounted } from 'vue'
import { areaList } from '@/utils/area'
import { useI18n } from 'vue-i18n'
2026-02-24 13:53:01 +08:00
import { useRouter } from 'vue-router'
2026-02-25 11:00:31 +08:00
import { useAgentStore, useProjectStore } from '@/stores'
2026-02-23 14:53:29 +08:00
import lightIcon from '@/assets/images/light-icon.png'
import closeIcon from '@/assets/images/close-icon.png'
2026-02-26 16:48:08 +08:00
import restoreIcon from '@/assets/images/restore.png'
import restoreCloseIcon from '@/assets/images/tag-close.png'
2026-02-25 11:00:31 +08:00
import { createProject } from '@/api/agent'
2026-02-26 13:04:52 +08:00
import { getStyleImage } from './style'
2026-03-02 13:40:34 +08:00
import { uploadImage } from '@/api/upload'
import MyEvent from '@/utils/myEvent'
2026-02-10 13:05:24 +08:00
// import Tag from './Tag.vue'
2026-02-24 13:53:01 +08:00
const router = useRouter()
const agentStore = useAgentStore()
2026-02-25 11:00:31 +08:00
const projectStore = useProjectStore()
2026-02-24 13:53:01 +08:00
2026-02-10 13:05:24 +08:00
const props = withDefaults(
defineProps<{
isAgentMode?: boolean
2026-02-25 11:00:31 +08:00
generating?: boolean
2026-02-10 13:05:24 +08:00
}>(),
{
2026-02-25 11:00:31 +08:00
isAgentMode: false,
generating: false
2026-02-10 13:05:24 +08:00
}
)
2026-02-25 11:00:31 +08:00
const emits = defineEmits(['send', 'pause'])
2026-02-10 13:05:24 +08:00
const { t } = useI18n()
// 图片上传相关
const fileInputRef = ref<HTMLInputElement | null>(null)
const uploadedImages = ref<Array<{ url: string; name: string }>>([])
// 触发文件上传
const triggerFileUpload = () => {
fileInputRef.value?.click()
}
// 处理文件选择
const handleFileChange = (event: Event) => {
const input = event.target as HTMLInputElement
2026-03-02 13:40:34 +08:00
2026-02-10 13:05:24 +08:00
if (input.files) {
Array.from(input.files).forEach((file) => {
// 只处理图片文件
if (file.type.startsWith('image/')) {
2026-03-02 13:40:34 +08:00
const formData = new FormData()
formData.append('file', file)
2026-03-02 15:08:03 +08:00
2026-03-02 13:40:34 +08:00
uploadImage(formData).then((res) => {
const reader = new FileReader()
reader.onload = (e) => {
uploadedImages.value.push({
url: e.target?.result as string,
name: file.name,
path: res
})
}
reader.readAsDataURL(file)
2026-03-02 13:40:34 +08:00
})
2026-02-10 13:05:24 +08:00
}
})
}
2026-03-02 15:08:03 +08:00
nextTick(() => {
editorRef.value?.focus()
})
2026-02-10 13:05:24 +08:00
input.value = ''
}
// 移除图片
const removeImage = (index: number) => {
uploadedImages.value.splice(index, 1)
}
const styleKeys: string[] = [
2026-02-26 13:04:52 +08:00
'Venetian Modern',
2026-02-10 13:05:24 +08:00
'Coastal',
2026-02-26 13:04:52 +08:00
'Maximalism',
'Memphis',
2026-02-10 13:05:24 +08:00
'Verdant',
2026-02-26 13:04:52 +08:00
'Century Chrome',
'Modern Revival',
'Transitional',
"Tuscan 2000's",
'Kitsch-core',
2026-02-10 13:05:24 +08:00
'Bauhaus',
'Constructivism',
2026-02-26 13:04:52 +08:00
'Nordic Noir',
'Dopamine',
'Squiggle'
2026-02-10 13:05:24 +08:00
]
const editorRef = ref<HTMLDivElement | null>(null)
const inputValue = ref<string>('')
2026-02-23 14:53:29 +08:00
const reportTags = ref([])
2026-02-26 16:48:08 +08:00
// 导出给父组件调用的方法
const addReportTag = (text?: string) => {
// 使用传入的文本,如果没有传入则使用默认的翻译文本
const tagText = text || t('Input.trendingReport')
2026-02-23 14:53:29 +08:00
// create container matching static structure: <div class="editor-tag report-btn flex-center" contenteditable="false">...
const tag = document.createElement('div')
tag.contentEditable = 'false'
const imgLeft = document.createElement('img')
const imgClose = document.createElement('img')
2026-02-26 16:48:08 +08:00
const textSpan = document.createElement('span')
2026-02-23 14:53:29 +08:00
imgClose.className = 'close-icon'
2026-02-26 16:48:08 +08:00
if (text) {
tag.className = 'editor-tag restore flex-center'
imgLeft.className = 'restore-icon'
imgLeft.src = restoreIcon as unknown as string
imgClose.src = restoreCloseIcon as unknown as string
imgClose.className = 'close-icon restore'
textSpan.className = 'restore-text'
} else {
tag.className = 'editor-tag report-btn flex-center'
imgLeft.className = 'light-icon'
imgLeft.src = lightIcon as unknown as string
imgClose.src = closeIcon as unknown as string
}
textSpan.innerText = tagText
2026-02-23 14:53:29 +08:00
imgClose.addEventListener('click', (ev) => {
ev.stopPropagation()
// remove tag when close clicked
tag.remove()
const idx = reportTags.value.indexOf(tag)
if (idx > -1) reportTags.value.splice(idx, 1)
})
// assemble
tag.appendChild(imgLeft)
tag.appendChild(textSpan)
tag.appendChild(imgClose)
// Insert tag at the current cursor position
const selection = window.getSelection()
if (selection && selection.rangeCount > 0) {
const range = selection.getRangeAt(0)
range.insertNode(tag)
// Insert a zero-width space text node after the tag so the caret can be placed there
const zwsp = document.createTextNode('\u200B')
if (tag.parentNode) tag.parentNode.insertBefore(zwsp, tag.nextSibling)
// Create a new collapsed range positioned inside the zwsp (after the tag)
const newRange = document.createRange()
newRange.setStart(zwsp, 1)
newRange.collapse(true)
selection.removeAllRanges()
selection.addRange(newRange)
// ensure editor has focus
editorRef.value && (editorRef.value as HTMLElement).focus()
} else if (editorRef.value) {
// If no selection, append directly to editor and place caret after
editorRef.value.appendChild(tag)
const zwsp = document.createTextNode('\u200B')
editorRef.value.appendChild(zwsp)
const sel = window.getSelection()
if (sel) {
const r = document.createRange()
r.setStart(zwsp, 1)
r.collapse(true)
sel.removeAllRanges()
sel.addRange(r)
}
editorRef.value && (editorRef.value as HTMLElement).focus()
}
2026-02-10 13:05:24 +08:00
2026-02-23 14:53:29 +08:00
reportTags.value.push(tag)
}
const toogltReportTag = () => {
// 清理掉已被删除的标签引用(从 DOM 中移除的元素)
reportTags.value = reportTags.value.filter((tag) => tag.parentNode !== null)
if (reportTags.value.length > 0) {
// 移除所有标签及其关联的零宽空格
reportTags.value.forEach((tag) => {
2026-02-24 13:53:01 +08:00
if (
tag.nextSibling &&
tag.nextSibling.nodeType === Node.TEXT_NODE &&
tag.nextSibling.textContent === '\u200B'
) {
2026-02-23 14:53:29 +08:00
tag.nextSibling.remove()
}
tag.remove()
})
reportTags.value = []
} else {
// 添加标签
addReportTag()
}
2026-02-10 13:05:24 +08:00
}
const handleEditorInput = () => {
if (!editorRef.value) return
2026-02-23 14:53:29 +08:00
// 提取纯文本排除插入的report标签
2026-02-10 13:05:24 +08:00
let text = ''
const walker = document.createTreeWalker(editorRef.value, NodeFilter.SHOW_TEXT, null)
let node: Node | null
while ((node = walker.nextNode())) {
text += node.textContent
}
2026-02-23 14:53:29 +08:00
// 移除末尾的空格
2026-02-10 13:05:24 +08:00
text = text.replace(/\s+$/, '')
inputValue.value = text
// 自动调整高度
autoResizeEditor()
}
const handleEditorPaste = (e: ClipboardEvent) => {
e.preventDefault()
const text = e.clipboardData?.getData('text/plain') || ''
document.execCommand('insertText', false, text)
}
const autoResizeEditor = () => {
const editor = editorRef.value
if (editor) {
if (props.isAgentMode) {
// editor.style.height = '6rem'
// editor.style.overflowY = 'auto'
return
} else {
2026-02-23 14:53:29 +08:00
// editor.style.height = 'auto'
// const maxHeight =
// 20 * parseFloat(getComputedStyle(document.documentElement).fontSize || '16')
// editor.style.height = Math.min(editor.scrollHeight, maxHeight) + 'px'
2026-02-10 13:05:24 +08:00
}
}
}
2026-02-26 16:48:08 +08:00
const handleKeyDown = (e) => {
2026-02-11 16:32:38 +08:00
// 检测回车
2026-02-23 14:53:29 +08:00
if (e.key === 'Enter') {
2026-02-11 16:32:38 +08:00
e.preventDefault()
2026-02-26 16:48:08 +08:00
if (props.isAgentMode) {
handleSendAgent()
} else {
handleCreateProject()
}
2026-02-23 14:53:29 +08:00
return
}
if (e.key === 'Backspace') {
const selection = window.getSelection()
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0)
if (range.collapsed) {
let nodeToDelete = null
const startContainer = range.startContainer
const startOffset = range.startOffset
if (startContainer.nodeType === Node.TEXT_NODE) {
// Cursor at the end of a text node, check next sibling
if (startOffset === startContainer.length) {
nodeToDelete = startContainer.nextSibling
}
} else if (startContainer.nodeType === Node.ELEMENT_NODE) {
// Cursor positioned between child nodes
nodeToDelete = startContainer.childNodes[startOffset]
}
if (
nodeToDelete &&
nodeToDelete.nodeType === Node.ELEMENT_NODE &&
(nodeToDelete as Element).classList &&
((nodeToDelete as Element).classList.contains('editor-tag') ||
(nodeToDelete as Element).classList.contains('report-tag'))
) {
e.preventDefault()
;(nodeToDelete as Element).remove()
// Optional: remove from reportTags if tracking
const index = reportTags.value.indexOf(nodeToDelete)
if (index > -1) {
reportTags.value.splice(index, 1)
}
return
}
}
}
2026-02-11 16:32:38 +08:00
}
}
2026-02-25 11:00:31 +08:00
const handleSendAgent = async () => {
if (props.generating) {
emits('pause')
return
}
2026-02-11 16:32:38 +08:00
if (!inputValue.value.trim()) return
2026-03-02 13:40:34 +08:00
const imageUrlList = uploadedImages.value.map((item) => item.path)
2026-02-26 16:48:08 +08:00
2026-03-02 15:08:03 +08:00
const payload = {
text: inputValue.value.trim(),
images: imageUrlList,
tempImages: uploadedImages.value
}
2026-02-26 16:48:08 +08:00
emits('send', payload)
2026-03-02 15:08:03 +08:00
// 发送后清空图片列表
uploadedImages.value = []
2026-02-11 16:32:38 +08:00
// 发送后清空输入框
if (editorRef.value) {
editorRef.value.innerHTML = ''
}
inputValue.value = ''
}
2026-02-10 13:05:24 +08:00
// 监听 inputValue 外部变化
watch(inputValue, () => {
nextTick(() => {
autoResizeEditor()
})
})
// 初始化编辑器高度
onMounted(() => {
autoResizeEditor()
})
const typeValue = ref<string>('')
const areaValue = ref<string>('')
const styleValue = ref<string>('')
const tempSelectedValue = ref<string>('')
const stylePopupVisible = ref(false)
const settingPopupVisible = ref(false)
const settingOptions = ref([
{ label: 'Input.settingOptions.creativity', value: 50 },
2026-03-04 14:02:11 +08:00
{ label: 'Input.settingOptions.diversity', value: 50 },
{ label: 'Input.settingOptions.relevance', value: 50 }
2026-02-10 13:05:24 +08:00
])
const openStylePopup = () => {
// 打开弹窗时初始化临时选中值为当前选中值
tempSelectedValue.value = styleValue.value
stylePopupVisible.value = true
}
const selectStyle = (value: string) => {
tempSelectedValue.value = value
}
const confirmStyle = () => {
// 点击确认后才真正赋值
styleValue.value = tempSelectedValue.value
stylePopupVisible.value = false
}
const confirmSetting = () => {
settingPopupVisible.value = false
}
const typeOptions = ref<any[]>([
{
label: 'Input.types.sofa',
value: 'Sofa'
},
{
label: 'Input.types.desk',
value: 'Desk'
},
{
label: 'Input.types.chair',
value: 'Chair'
}
])
const areaOptions = ref<any[]>(areaList)
const styleOptions = ref<any[]>(
styleKeys.map((key) => ({
2026-02-26 13:04:52 +08:00
label: key,
// label: `Input.styles.${key}`,
2026-02-10 13:05:24 +08:00
value: key
}))
)
2026-02-24 13:53:01 +08:00
2026-02-25 11:00:31 +08:00
const handleCreateProject = async () => {
2026-02-24 13:53:01 +08:00
// 这里可以添加创建项目的逻辑
if (!inputValue.value.trim()) {
return
}
2026-02-24 13:53:01 +08:00
const params = {
type: typeValue.value,
area: areaValue.value,
2026-02-25 11:00:31 +08:00
style: styleValue.value,
temperature: 0.7
2026-02-24 13:53:01 +08:00
}
2026-02-25 11:00:31 +08:00
const projectres = await createProject(params)
2026-02-26 16:48:08 +08:00
// console.log('projectres', projectres)
2026-02-25 11:00:31 +08:00
projectStore.setId(projectres)
MyEvent.emit('updateProjectList')
2026-02-24 13:53:01 +08:00
// 保存初始数据到 store
agentStore.setInitialProjectData({
text: inputValue.value.trim(),
2026-03-02 15:08:03 +08:00
images: uploadedImages.value.map((item) => item.path),
tempImages: uploadedImages.value,
2026-02-25 11:00:31 +08:00
...params
2026-02-24 13:53:01 +08:00
})
2026-02-26 16:48:08 +08:00
// console.log('Create project with:', params)
2026-03-02 11:29:07 +08:00
router.push(`/home/agent/${projectres}`, { query: params })
2026-03-02 15:08:03 +08:00
uploadedImages.value = []
2026-02-24 13:53:01 +08:00
}
2026-02-26 16:48:08 +08:00
// 暴露方法给父组件
defineExpose({
addReportTag
})
2026-02-03 13:15:39 +08:00
</script>
<style lang="less" scoped>
.report-btn {
position: absolute;
bottom: -7.4rem;
height: 4.4rem;
border-radius: 2.2rem;
width: 20rem;
background-color: #fff;
border: 1px solid #f6f4ef;
column-gap: 1.2rem;
cursor: pointer;
.c-svg {
width: 1.5rem;
height: 1.9rem;
}
}
2026-02-10 13:05:24 +08:00
.assist-input-wrapper {
min-height: 23.5rem;
max-height: 43.5rem;
width: 106.3rem;
2026-03-05 11:46:41 +08:00
2026-02-10 13:05:24 +08:00
margin: 0 auto;
padding: 0;
position: relative;
2026-03-05 11:46:41 +08:00
&:not(.agent) .animate-container {
box-shadow: 0px 0.5rem 1.4rem 0px #0000001a;
transition: all 0.3s ease;
2026-03-05 11:46:41 +08:00
border-radius: 2.8rem;
background-color: #fff;
border: 0.1rem solid #00000005;
&:not(.agent):hover {
box-shadow: 0px 0.5rem 3.36rem 2.2rem #f1ede999;
transform: translateY(-1rem);
2026-02-10 13:05:24 +08:00
}
}
.scroll-content {
2026-02-24 11:17:01 +08:00
display: flex;
2026-02-23 14:53:29 +08:00
flex: 1;
overflow-y: auto;
2026-02-10 13:05:24 +08:00
padding: 3.4rem 1.7rem 1.7rem;
}
.editor {
width: 100%;
2026-02-23 14:53:29 +08:00
flex: 1;
2026-02-10 13:05:24 +08:00
border: none;
outline: none;
padding: 0 1.4rem 1.4rem;
font-size: 2rem;
font-family: 'InterRegular';
font-weight: 400;
color: #000000;
overflow-y: auto;
overflow-x: hidden;
line-height: 1.5;
white-space: pre-wrap;
word-wrap: break-word;
// 占位符
&:empty::before {
content: attr(placeholder);
color: #999;
pointer-events: none;
}
}
// 图片预览区域样式
.image-preview-list {
padding: 0 1.4rem 1rem;
column-gap: 1rem;
max-height: 15rem;
overflow-y: auto;
flex-shrink: 0;
.image-preview-item {
position: relative;
width: 8.6rem;
height: 8.6rem;
border-radius: 1.5rem;
overflow: hidden;
flex-shrink: 0;
border: 0.1rem solid #cdcdcd;
.preview-image {
width: 100%;
height: 100%;
2026-03-03 10:05:11 +08:00
object-fit: contain;
2026-02-10 13:05:24 +08:00
border-radius: 0.8rem;
}
.image-remove-btn {
position: absolute;
top: 0.2rem;
right: 0.2rem;
width: 1.6rem;
height: 1.6rem;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s ease;
}
&:hover .image-remove-btn {
opacity: 1;
}
}
}
.operate {
flex-shrink: 0;
margin-top: auto;
padding: 0 1.7rem 1.7rem;
.left {
column-gap: 2rem;
}
.attach {
width: 4rem;
height: 4rem;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
cursor: pointer;
2026-03-03 14:40:49 +08:00
img {
width: 1.65rem;
height: 1.86rem;
}
2026-02-10 13:05:24 +08:00
}
.el-select {
width: 13.9rem;
height: 4rem;
:deep(.el-select__wrapper) {
border-radius: 0.8rem;
height: 100%;
box-shadow: none;
border: 0.1rem solid rgba(0, 0, 0, 0.1);
font-weight: 500;
font-size: 1.4rem;
min-height: initial;
2026-02-10 13:05:24 +08:00
.el-select__placeholder {
color: #000;
}
.el-select__icon {
color: #000;
}
}
}
.fida-style-select-wrapper {
position: relative;
width: 13.9rem;
height: 4rem;
}
.fida-style-select-trigger {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
cursor: pointer;
}
.setting-icon {
width: 2.4rem;
height: 2.4rem;
cursor: pointer;
}
.create-btn {
background-color: #ff7a51;
height: 4rem;
width: 13rem;
color: #fff;
border-radius: 4.2rem;
font-family: 'Mazzard';
font-weight: 600;
font-size: 1.28rem;
cursor: pointer;
.shining-icon {
width: 1.4rem;
height: 1.4rem;
}
}
}
}
.input-option {
// padding: 0 1rem;
margin: 0 0.6rem;
padding: 0 0.8rem 0 1rem;
color: #0d0d0d;
font-weight: 510;
font-size: 1.3rem;
height: 3rem;
line-height: 3rem;
&.el-select-dropdown__item.is-hovering {
background-color: rgba(13, 13, 13, 0.02);
// border-radius: 0.6rem;
}
}
.agent {
padding: 1.2rem;
box-shadow: none;
2026-02-11 16:32:38 +08:00
border-radius: 1.5rem;
2026-02-10 13:05:24 +08:00
border: 0.1rem solid #0000001a;
.scroll-content {
padding: 0;
flex: 1;
overflow: auto;
.editor {
font-family: 'Regular';
font-weight: 400;
2026-03-05 11:24:28 +08:00
font-size: 1.4rem;
2026-02-10 13:05:24 +08:00
min-height: initial;
max-height: initial;
padding: 0;
2026-02-11 16:32:38 +08:00
height: 100%;
2026-03-02 15:08:03 +08:00
min-height: 5rem;
2026-02-10 13:05:24 +08:00
}
}
.operate {
padding: 1.2rem 0 0;
margin: 0;
.right {
display: flex;
align-items: center;
2026-02-25 11:00:31 +08:00
.sender-btn {
2026-02-10 13:05:24 +08:00
width: 3.2rem;
height: 3.2rem;
cursor: pointer;
2026-02-25 11:00:31 +08:00
background-color: #ff7a51;
border-radius: 50%;
&:hover {
background-color: #f8693d;
}
2026-02-25 11:00:31 +08:00
.sender-icon {
width: 1.3rem;
height: 1.3rem;
}
.sender-pause {
width: 1rem;
height: 1rem;
background-color: #fff;
}
2026-02-10 13:05:24 +08:00
}
}
}
}
2026-02-03 13:15:39 +08:00
</style>
2026-02-03 15:46:05 +08:00
<style lang="less">
2026-02-10 13:05:24 +08:00
.fida-style-select-popover {
width: 34.2rem !important;
padding: 0 !important;
border-radius: 0.6rem !important;
box-shadow: 0px 5px 20px 0px rgba(0, 0, 0, 0.15) !important;
background-color: #fff !important;
border: none !important;
}
.fida-style-popover-content {
padding: 2rem 2.4rem 2.4rem;
}
.fida-style-popover-header {
font-weight: 500;
font-size: 1.6rem;
color: #000;
margin-bottom: 2rem;
2026-02-24 13:53:01 +08:00
padding: 2rem 2.4rem !important;
2026-02-10 13:05:24 +08:00
// padding: 1.8rem 2rem 1.5rem;
// border-bottom: 0.1rem solid #f0f0f0;
}
.fida-style-popover-grid {
2026-02-24 13:53:01 +08:00
display: flex;
flex-wrap: wrap;
2026-02-10 13:05:24 +08:00
gap: 1rem;
2026-02-24 13:53:01 +08:00
justify-content: center;
height: 28.5rem;
overflow-y: auto;
// display: grid;
// grid-template-columns: repeat(3, 1fr);
// gap: 1rem;
2026-02-10 13:05:24 +08:00
}
.fida-style-popover-item {
height: 9.1rem;
width: 9.1rem;
2026-02-26 13:04:52 +08:00
border-radius: 1.4rem;
2026-02-10 13:05:24 +08:00
cursor: pointer;
2026-02-24 13:53:01 +08:00
position: relative;
2026-02-26 13:04:52 +08:00
border: none;
2026-03-03 14:40:49 +08:00
2026-02-24 13:53:01 +08:00
.checked-item-icon {
position: absolute;
bottom: 0;
right: 0;
transform: translate(50%, 50%);
width: 2.4rem;
height: 2.4rem;
}
2026-02-26 13:04:52 +08:00
.style-bg {
width: 100%;
height: 100%;
border-radius: 1.4rem;
}
2026-02-10 13:05:24 +08:00
.fida-option-label {
2026-03-03 14:40:49 +08:00
font-weight: 500;
font-size: 1.2rem;
color: #fff;
text-align: center;
padding: 0.5rem;
position: absolute;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 1.4rem;
}
&.is-selected {
border: 0.3rem solid #000;
.fida-option-label {
display: none;
}
2026-02-10 13:05:24 +08:00
}
}
.fida-style-popover-footer {
// border-top: 0.1rem solid #f0f0f0;
2026-02-24 13:53:01 +08:00
padding: 2.4rem 0 !important;
2026-02-10 13:05:24 +08:00
margin-top: 2.4rem;
.fida-confirm-btn {
margin: 0 auto;
width: 15.7rem;
height: 3.4rem;
line-height: 3.4rem;
background-color: #ff7a51;
color: #fff;
border: none;
border-radius: 3.8rem;
font-weight: 500;
font-size: 1.4rem;
cursor: pointer;
}
}
.fida-setting-popover {
padding: 0 !important;
border-radius: 0.6rem !important;
background-color: #fff !important;
border: none !important;
width: 25.6rem;
height: 23.9rem;
box-shadow: 0px 11px 20px 0px #0000001a;
border-radius: 0.6rem;
}
// .fida-setting-popover-content {
// padding: 2rem 2.4rem 2.4rem;
// }
.fida-setting-popover-header {
font-weight: 400;
font-size: 1.4rem;
color: #000;
margin-bottom: 2rem !important;
}
.fida-setting-popover-content {
padding: 1.6rem 1.4rem 2.2rem !important;
}
.fida-setting-slider-list {
display: flex;
flex-direction: column;
row-gap: 1rem;
}
.fida-setting-slider-item {
.fida-slider-label {
font-weight: 400;
font-size: 1.2rem;
color: #000;
margin-bottom: 1rem;
}
.fida-slider-row {
column-gap: 2.6rem;
.el-slider {
flex: 1;
}
.fida-slider-value {
font-weight: 400;
font-size: 1.4rem;
color: #000;
}
}
}
.setting-popover-slider {
--el-slider-height: 0.4rem;
height: fit-content;
.el-slider__runway {
height: var(--el-slider-height);
background-color: #e8e8e8;
border-radius: 0.2rem;
}
.el-slider__bar {
height: var(--el-slider-height);
background-color: #000;
border-radius: 0.2rem;
}
.el-slider__button-wrapper {
width: fit-content;
height: fit-content;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
align-items: center;
}
.el-slider__button {
width: 1rem;
height: 1rem;
background-color: #000;
border-radius: 50%;
border: none;
}
.el-slider__stop {
display: none;
}
}
2026-02-23 14:53:29 +08:00
/* 动态添加的编辑器标签样式 */
.assist-input-wrapper .editor .editor-tag {
width: 21.8rem;
height: 4.4rem;
display: inline-flex;
2026-03-03 14:40:49 +08:00
border: 0.11rem solid #bfbfbf;
2026-02-23 14:53:29 +08:00
font-weight: 500;
font-size: 1.8rem;
column-gap: 0;
margin: 0 0.5rem;
vertical-align: middle;
border-radius: 2.2rem;
2026-02-26 16:48:08 +08:00
&.restore {
width: auto;
max-width: 100%;
display: inline-flex;
border: none;
border-radius: 0.4rem;
background-color: rgba(0, 0, 0, 0.05);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
align-items: center;
justify-content: space-between;
padding: 0 0.9rem 0 0.7rem;
box-sizing: border-box;
}
2026-02-23 14:53:29 +08:00
span {
margin: 0 0.7rem 0 1.2rem;
2026-02-26 16:48:08 +08:00
&.restore-text {
flex: 1;
min-width: 0;
max-width: 52rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
2026-02-23 14:53:29 +08:00
}
.light-icon {
width: 1.5rem;
height: 1.9rem;
flex-shrink: 0;
}
.close-icon {
width: 1rem;
height: 1rem;
cursor: pointer;
flex-shrink: 0;
2026-03-02 13:40:34 +08:00
&.restore {
2026-02-26 16:48:08 +08:00
width: 0.5rem;
height: 0.5rem;
}
}
2026-03-02 13:40:34 +08:00
.restore-icon {
2026-02-26 16:48:08 +08:00
width: 1.2rem;
height: 1.2rem;
2026-02-23 14:53:29 +08:00
}
}
2026-02-03 15:46:05 +08:00
</style>