feat: trending report不再作为Placeholder
This commit is contained in:
@@ -53,7 +53,7 @@
|
||||
<span>Upload files</span>
|
||||
</div>
|
||||
<div class="gap"></div>
|
||||
<div class="report flex align-center" @click="toogltReportTag">
|
||||
<div class="report flex align-center" @click="toogltReportTag()">
|
||||
<SvgIcon color="#5A5A5A" name="light" size="11" />
|
||||
<span>Trending report</span>
|
||||
</div>
|
||||
@@ -215,7 +215,7 @@
|
||||
v-if="!isAgentMode"
|
||||
class="report-btn flex space-between align-center outer"
|
||||
:class="{ 'is-cn': isCn }"
|
||||
@click="toogltReportTag"
|
||||
@click="toogltReportTag()"
|
||||
>
|
||||
<SvgIcon class="light-icon" color="#FFDB56" name="light" size="16" />
|
||||
<span>{{ $t('Input.trendingReport') }}</span>
|
||||
@@ -337,8 +337,61 @@
|
||||
|
||||
const editorRef = ref<HTMLDivElement | null>(null)
|
||||
const inputValue = ref<string>('')
|
||||
const reportTags = ref([])
|
||||
const customPlaceholder = ref<HTMLElement | null>(null)
|
||||
const reportTags = ref<HTMLElement[]>([])
|
||||
const reportPromptText = ref<Text | null>(null)
|
||||
let reportTypewriterTimeout: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
const stopReportTypewriter = () => {
|
||||
if (reportTypewriterTimeout) {
|
||||
clearTimeout(reportTypewriterTimeout)
|
||||
reportTypewriterTimeout = null
|
||||
}
|
||||
}
|
||||
|
||||
const moveCaretToTextEnd = (textNode: Text) => {
|
||||
const selection = window.getSelection()
|
||||
if (!selection || selection.anchorNode !== textNode) return
|
||||
|
||||
const range = document.createRange()
|
||||
range.setStart(textNode, textNode.data.length)
|
||||
range.collapse(true)
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
}
|
||||
|
||||
const typeReportPromptText = (textNode: Text, text: string, index = 0) => {
|
||||
if (reportPromptText.value !== textNode) return
|
||||
|
||||
if (index >= text.length) {
|
||||
reportTypewriterTimeout = null
|
||||
return
|
||||
}
|
||||
|
||||
textNode.textContent = `${textNode.textContent || ''}${text.charAt(index)}`
|
||||
handleEditorInput()
|
||||
moveCaretToTextEnd(textNode)
|
||||
|
||||
reportTypewriterTimeout = setTimeout(() => {
|
||||
typeReportPromptText(textNode, text, index + 1)
|
||||
}, 30)
|
||||
}
|
||||
|
||||
const removeReportPromptText = () => {
|
||||
stopReportTypewriter()
|
||||
const promptText = reportPromptText.value
|
||||
if (promptText?.parentNode) {
|
||||
const nextNode = promptText.nextSibling
|
||||
if (
|
||||
nextNode &&
|
||||
nextNode.nodeType === Node.TEXT_NODE &&
|
||||
nextNode.textContent === '\u200B'
|
||||
) {
|
||||
nextNode.remove()
|
||||
}
|
||||
promptText.parentNode.removeChild(promptText)
|
||||
}
|
||||
reportPromptText.value = null
|
||||
}
|
||||
|
||||
// 控制占位符显示
|
||||
const showPlaceholder = computed(() => {
|
||||
@@ -356,25 +409,6 @@
|
||||
return textContent === '' && !hasMeaningfulChildren && isEmptyHTML
|
||||
})
|
||||
|
||||
// 打字机效果显示placeholder
|
||||
let typewriterTimeout: ReturnType<typeof setTimeout> | null = null
|
||||
const typeWriterEffect = (element: HTMLElement, text: string, index: number = 0) => {
|
||||
if (index < text.length) {
|
||||
element.innerText += text.charAt(index)
|
||||
typewriterTimeout = setTimeout(() => {
|
||||
typeWriterEffect(element, text, index + 1)
|
||||
}, 30) // 每个字符间隔30ms
|
||||
}
|
||||
}
|
||||
|
||||
// 停止打字机效果
|
||||
const stopTypewriter = () => {
|
||||
if (typewriterTimeout) {
|
||||
clearTimeout(typewriterTimeout)
|
||||
typewriterTimeout = null
|
||||
}
|
||||
}
|
||||
|
||||
// 导出给父组件调用的方法
|
||||
const addReportTag = (text?: string) => {
|
||||
// 使用传入的文本,如果没有传入则使用默认的翻译文本
|
||||
@@ -404,15 +438,12 @@
|
||||
|
||||
imgClose.addEventListener('click', (ev) => {
|
||||
ev.stopPropagation()
|
||||
stopTypewriter() // 关闭标签时停止打字机效果
|
||||
// remove tag when close clicked
|
||||
removeReportPromptText()
|
||||
tag.remove()
|
||||
const idx = reportTags.value.indexOf(tag)
|
||||
if (idx > -1) reportTags.value.splice(idx, 1)
|
||||
if (customPlaceholder.value) {
|
||||
customPlaceholder.value.remove()
|
||||
customPlaceholder.value = null
|
||||
}
|
||||
handleEditorInput()
|
||||
})
|
||||
|
||||
// assemble
|
||||
@@ -431,75 +462,48 @@
|
||||
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 in editor, 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 placeholderSpan = document.createElement('span')
|
||||
placeholderSpan.className = 'custom-placeholder'
|
||||
// 初始为空字符串,稍后通过打字机效果填充
|
||||
placeholderSpan.innerText = ''
|
||||
const promptText = document.createTextNode('')
|
||||
if (tag.parentNode) {
|
||||
tag.parentNode.insertBefore(placeholderSpan, tag.nextSibling)
|
||||
tag.parentNode.insertBefore(promptText, tag.nextSibling)
|
||||
const zwsp = document.createTextNode('\u200B')
|
||||
tag.parentNode.insertBefore(zwsp, placeholderSpan.nextSibling)
|
||||
tag.parentNode.insertBefore(zwsp, promptText.nextSibling)
|
||||
const newRange = document.createRange()
|
||||
newRange.setStart(placeholderSpan, 0)
|
||||
newRange.setStart(promptText, promptText.data.length)
|
||||
newRange.collapse(true)
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(newRange)
|
||||
selection.addRange(newRange)
|
||||
const currentSelection = window.getSelection()
|
||||
currentSelection?.removeAllRanges()
|
||||
currentSelection?.addRange(newRange)
|
||||
}
|
||||
customPlaceholder.value = placeholderSpan
|
||||
|
||||
// 打字机效果显示placeholder文本
|
||||
const placeholderText = t('Input.reportPlaceholder')
|
||||
typeWriterEffect(placeholderSpan, placeholderText)
|
||||
|
||||
const removePlaceholderOnInput = () => {
|
||||
stopTypewriter() // 用户输入时停止打字机效果
|
||||
if (placeholderSpan.parentNode) {
|
||||
placeholderSpan.remove()
|
||||
customPlaceholder.value = null
|
||||
}
|
||||
editorRef.value?.removeEventListener('input', removePlaceholderOnInput)
|
||||
}
|
||||
editorRef.value?.addEventListener('input', removePlaceholderOnInput)
|
||||
reportPromptText.value = promptText
|
||||
typeReportPromptText(promptText, t('Input.reportPlaceholder'))
|
||||
}
|
||||
|
||||
const toogltReportTag = (clear = false) => {
|
||||
stopTypewriter() // 移除标签时停止打字机效果
|
||||
const shouldClear = clear === true
|
||||
|
||||
// 清理掉已被删除的标签引用(从 DOM 中移除的元素)
|
||||
reportTags.value = reportTags.value.filter((tag) => tag.parentNode !== null)
|
||||
|
||||
if (shouldClear) {
|
||||
removeReportPromptText()
|
||||
reportTags.value.forEach((tag) => {
|
||||
tag.remove()
|
||||
})
|
||||
reportTags.value = []
|
||||
return
|
||||
}
|
||||
|
||||
if (reportTags.value.length > 0) {
|
||||
removeReportPromptText()
|
||||
// 移除所有标签及其关联的零宽空格
|
||||
reportTags.value.forEach((tag) => {
|
||||
if (
|
||||
@@ -512,10 +516,7 @@
|
||||
tag.remove()
|
||||
})
|
||||
reportTags.value = []
|
||||
if (customPlaceholder.value) {
|
||||
customPlaceholder.value.remove()
|
||||
customPlaceholder.value = null
|
||||
}
|
||||
handleEditorInput()
|
||||
} else {
|
||||
// 添加标签
|
||||
addReportTag()
|
||||
@@ -538,8 +539,8 @@
|
||||
editor.innerHTML = ''
|
||||
editor.textContent = ''
|
||||
reportTags.value = []
|
||||
customPlaceholder.value = null
|
||||
stopTypewriter()
|
||||
stopReportTypewriter()
|
||||
reportPromptText.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -554,7 +555,6 @@
|
||||
let node: Node | null
|
||||
while ((node = walker.nextNode())) {
|
||||
// 使用 closest() 检查当前节点的祖先元素是否包含需要排除的 class
|
||||
if (node.parentElement?.closest('.custom-placeholder')) continue
|
||||
if (node.parentElement?.closest('.editor-tag')) continue
|
||||
text += node.textContent
|
||||
}
|
||||
@@ -569,12 +569,12 @@
|
||||
const hasTextContent = editor.textContent?.replace(/[\s\u200B]/g, '').trim().length > 0
|
||||
|
||||
// 如果编辑器完全为空,清空它以显示占位符
|
||||
// 同时也要清空 reportTags 和 customPlaceholder
|
||||
// 同时也要清空 reportTags 和 reportPromptText
|
||||
if (!hasChildElements && !hasTextContent) {
|
||||
editor.innerHTML = ''
|
||||
reportTags.value = []
|
||||
customPlaceholder.value = null
|
||||
stopTypewriter()
|
||||
stopReportTypewriter()
|
||||
reportPromptText.value = null
|
||||
}
|
||||
|
||||
// 自动调整高度
|
||||
@@ -642,20 +642,14 @@
|
||||
const element = nodeToDelete as Element
|
||||
const isEditorTag = element.classList.contains('editor-tag')
|
||||
const isReportTag = element.classList.contains('report-tag')
|
||||
const isCustomPlaceholder = element.classList.contains('custom-placeholder')
|
||||
|
||||
if (isEditorTag || isReportTag || isCustomPlaceholder) {
|
||||
if (isEditorTag || isReportTag) {
|
||||
e.preventDefault()
|
||||
removeReportPromptText()
|
||||
element.remove()
|
||||
|
||||
// 如果删除的是 customPlaceholder,停止打字机效果
|
||||
if (isCustomPlaceholder) {
|
||||
stopTypewriter()
|
||||
customPlaceholder.value = null
|
||||
}
|
||||
|
||||
// 从 reportTags 中移除
|
||||
const index = reportTags.value.indexOf(nodeToDelete as Element)
|
||||
const index = reportTags.value.indexOf(nodeToDelete as HTMLElement)
|
||||
if (index > -1) {
|
||||
reportTags.value.splice(index, 1)
|
||||
}
|
||||
@@ -663,6 +657,7 @@
|
||||
// 删除标签后清理零宽字符并检查是否为空
|
||||
nextTick(() => {
|
||||
cleanupEditor()
|
||||
handleEditorInput()
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -677,10 +672,10 @@
|
||||
// 检查是否完全为空,需要显示占位符
|
||||
if (editorRef.value.children.length === 0) {
|
||||
reportTags.value = []
|
||||
customPlaceholder.value = null
|
||||
stopTypewriter()
|
||||
reportPromptText.value = null
|
||||
}
|
||||
}
|
||||
handleEditorInput()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -837,6 +832,7 @@
|
||||
})
|
||||
})
|
||||
onUnmounted(() => {
|
||||
stopReportTypewriter()
|
||||
MyEvent.remove('quote', handleQuote)
|
||||
MyEvent.remove('projectChange', handleInitInput)
|
||||
})
|
||||
@@ -1161,11 +1157,6 @@
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.custom-placeholder {
|
||||
color: #999;
|
||||
margin-left: 0.5rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
.fida-style-select-popover {
|
||||
width: 34.2rem !important;
|
||||
padding: 0 !important;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="main-input-container flex-1">
|
||||
<div class="slogan">
|
||||
<p>Creating Things with <span class="fiDA">FiDA</span> that</p>
|
||||
<p>Creating Works with <span class="fiDA">FiDA</span> that</p>
|
||||
<p>Bloom Your Creativity</p>
|
||||
</div>
|
||||
<Input />
|
||||
|
||||
Reference in New Issue
Block a user