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

1099 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="assist-input-wrapper flex flex-col" :class="{ agent: isAgentMode }">
<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>
</div>
</div>
<!-- 编辑区域 -->
<div
ref="editorRef"
class="editor"
contenteditable="true"
:placeholder="$t('Input.placeholder')"
@input="handleEditorInput"
@paste="handleEditorPaste"
@keydown="handleKeyDown"
></div>
</div>
<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"
/>
<el-select
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>
<el-popover
v-model:visible="settingPopupVisible"
placement="top"
:width="342"
:show-arrow="false"
trigger="click"
popper-class="fida-setting-popover"
>
<template #reference>
<img src="@/assets/images/setting.png" class="setting-icon" />
</template>
<div class="fida-setting-popover-content flex flex-col">
<div class="fida-setting-popover-header">
{{ $t('Input.styleTitle') }}
</div>
<div class="fida-setting-slider-list">
<div
v-for="item in settingOptions"
:key="item.label"
class="fida-setting-slider-item"
>
<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>
</div>
</div>
</div>
</el-popover>
</div>
<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>
</div>
<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>
</div>
</div>
</div>
<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>
</template>
<script setup lang="ts">
import { computed, ref, watch, nextTick, onMounted } from 'vue'
import { areaList } from '@/utils/area'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useAgentStore, useProjectStore } from '@/stores'
import lightIcon from '@/assets/images/light-icon.png'
import closeIcon from '@/assets/images/close-icon.png'
import restoreIcon from '@/assets/images/restore.png'
import restoreCloseIcon from '@/assets/images/tag-close.png'
import { createProject } from '@/api/agent'
import { getStyleImage } from './style'
import { uploadImage } from '@/api/upload'
import MyEvent from '@/utils/myEvent'
// import Tag from './Tag.vue'
const router = useRouter()
const agentStore = useAgentStore()
const projectStore = useProjectStore()
const props = withDefaults(
defineProps<{
isAgentMode?: boolean
generating?: boolean
}>(),
{
isAgentMode: false,
generating: false
}
)
const emits = defineEmits(['send', 'pause'])
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
if (input.files) {
Array.from(input.files).forEach((file) => {
// 只处理图片文件
if (file.type.startsWith('image/')) {
const formData = new FormData()
formData.append('file', file)
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)
})
}
})
}
nextTick(() => {
editorRef.value?.focus()
})
input.value = ''
}
// 移除图片
const removeImage = (index: number) => {
uploadedImages.value.splice(index, 1)
}
const styleKeys: string[] = [
'Venetian Modern',
'Coastal',
'Maximalism',
'Memphis',
'Verdant',
'Century Chrome',
'Modern Revival',
'Transitional',
"Tuscan 2000's",
'Kitsch-core',
'Bauhaus',
'Constructivism',
'Nordic Noir',
'Dopamine',
'Squiggle'
]
const editorRef = ref<HTMLDivElement | null>(null)
const inputValue = ref<string>('')
const reportTags = ref([])
// 导出给父组件调用的方法
const addReportTag = (text?: string) => {
// 使用传入的文本,如果没有传入则使用默认的翻译文本
const tagText = text || t('Input.trendingReport')
// 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')
const textSpan = document.createElement('span')
imgClose.className = 'close-icon'
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
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()
}
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) => {
if (
tag.nextSibling &&
tag.nextSibling.nodeType === Node.TEXT_NODE &&
tag.nextSibling.textContent === '\u200B'
) {
tag.nextSibling.remove()
}
tag.remove()
})
reportTags.value = []
} else {
// 添加标签
addReportTag()
}
}
const handleEditorInput = () => {
if (!editorRef.value) return
// 提取纯文本排除插入的report标签
let text = ''
const walker = document.createTreeWalker(editorRef.value, NodeFilter.SHOW_TEXT, null)
let node: Node | null
while ((node = walker.nextNode())) {
text += node.textContent
}
// 移除末尾的空格
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 {
// editor.style.height = 'auto'
// const maxHeight =
// 20 * parseFloat(getComputedStyle(document.documentElement).fontSize || '16')
// editor.style.height = Math.min(editor.scrollHeight, maxHeight) + 'px'
}
}
}
const handleKeyDown = (e) => {
// 检测回车
if (e.key === 'Enter') {
e.preventDefault()
if (props.isAgentMode) {
handleSendAgent()
} else {
handleCreateProject()
}
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
}
}
}
}
}
const handleSendAgent = async () => {
if (props.generating) {
emits('pause')
return
}
if (!inputValue.value.trim()) return
const imageUrlList = uploadedImages.value.map((item) => item.path)
const payload = {
text: inputValue.value.trim(),
images: imageUrlList,
tempImages: uploadedImages.value
}
emits('send', payload)
// 发送后清空图片列表
uploadedImages.value = []
// 发送后清空输入框
if (editorRef.value) {
editorRef.value.innerHTML = ''
}
inputValue.value = ''
}
// 监听 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 },
{ label: 'Input.settingOptions.diversity', value: 50 },
{ label: 'Input.settingOptions.relevance', value: 50 }
])
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) => ({
label: key,
// label: `Input.styles.${key}`,
value: key
}))
)
const handleCreateProject = async () => {
// 这里可以添加创建项目的逻辑
if (!inputValue.value.trim()) {
return
}
const params = {
type: typeValue.value,
area: areaValue.value,
style: styleValue.value,
temperature: 0.7
}
const projectres = await createProject(params)
// console.log('projectres', projectres)
projectStore.setId(projectres)
MyEvent.emit('updateProjectList')
// 保存初始数据到 store
agentStore.setInitialProjectData({
text: inputValue.value.trim(),
images: uploadedImages.value.map((item) => item.path),
tempImages: uploadedImages.value,
...params
})
// console.log('Create project with:', params)
router.push(`/home/agent/${projectres}`, { query: params })
uploadedImages.value = []
}
// 暴露方法给父组件
defineExpose({
addReportTag
})
</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;
}
}
.assist-input-wrapper {
min-height: 23.5rem;
max-height: 43.5rem;
width: 106.3rem;
margin: 0 auto;
padding: 0;
position: relative;
&:not(.agent) .animate-container {
box-shadow: 0px 0.5rem 1.4rem 0px #0000001a;
transition: all 0.3s ease;
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);
}
}
.scroll-content {
display: flex;
flex: 1;
overflow-y: auto;
padding: 3.4rem 1.7rem 1.7rem;
}
.editor {
width: 100%;
flex: 1;
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%;
object-fit: contain;
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;
img {
width: 1.65rem;
height: 1.86rem;
}
}
.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;
.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;
border-radius: 1.5rem;
border: 0.1rem solid #0000001a;
.scroll-content {
padding: 0;
flex: 1;
overflow: auto;
.editor {
font-family: 'Regular';
font-weight: 400;
font-size: 1.4rem;
min-height: initial;
max-height: initial;
padding: 0;
height: 100%;
min-height: 5rem;
}
}
.operate {
padding: 1.2rem 0 0;
margin: 0;
.right {
display: flex;
align-items: center;
.sender-btn {
width: 3.2rem;
height: 3.2rem;
cursor: pointer;
background-color: #ff7a51;
border-radius: 50%;
&:hover {
background-color: #f8693d;
}
.sender-icon {
width: 1.3rem;
height: 1.3rem;
}
.sender-pause {
width: 1rem;
height: 1rem;
background-color: #fff;
}
}
}
}
}
</style>
<style lang="less">
.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;
padding: 2rem 2.4rem !important;
// padding: 1.8rem 2rem 1.5rem;
// border-bottom: 0.1rem solid #f0f0f0;
}
.fida-style-popover-grid {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
height: 28.5rem;
overflow-y: auto;
// display: grid;
// grid-template-columns: repeat(3, 1fr);
// gap: 1rem;
}
.fida-style-popover-item {
height: 9.1rem;
width: 9.1rem;
border-radius: 1.4rem;
cursor: pointer;
position: relative;
border: none;
.checked-item-icon {
position: absolute;
bottom: 0;
right: 0;
transform: translate(50%, 50%);
width: 2.4rem;
height: 2.4rem;
}
.style-bg {
width: 100%;
height: 100%;
border-radius: 1.4rem;
}
.fida-option-label {
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;
}
}
}
.fida-style-popover-footer {
// border-top: 0.1rem solid #f0f0f0;
padding: 2.4rem 0 !important;
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;
}
}
/* 动态添加的编辑器标签样式 */
.assist-input-wrapper .editor .editor-tag {
width: 21.8rem;
height: 4.4rem;
display: inline-flex;
border: 0.11rem solid #bfbfbf;
font-weight: 500;
font-size: 1.8rem;
column-gap: 0;
margin: 0 0.5rem;
vertical-align: middle;
border-radius: 2.2rem;
&.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;
}
span {
margin: 0 0.7rem 0 1.2rem;
&.restore-text {
flex: 1;
min-width: 0;
max-width: 52rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.light-icon {
width: 1.5rem;
height: 1.9rem;
flex-shrink: 0;
}
.close-icon {
width: 1rem;
height: 1rem;
cursor: pointer;
flex-shrink: 0;
&.restore {
width: 0.5rem;
height: 0.5rem;
}
}
.restore-icon {
width: 1.2rem;
height: 1.2rem;
}
}
</style>