Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front

This commit is contained in:
X1627315083@163.com
2026-03-16 15:28:06 +08:00
9 changed files with 79 additions and 133 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

View File

@@ -151,7 +151,8 @@ export default {
copySuccess: 'Text copied to clipboard',
copyFaild: 'Copy failed. Your browser may be restricting clipboard access. Please try copying manually.',
Download: 'Download',
deleteSuccess: 'Successfully deleted'
deleteSuccess: 'Successfully deleted',
thinking: 'Thinking...',
},
// Version Tree

View File

@@ -146,7 +146,8 @@ export default {
copyFaild:
'复制失败。您的浏览器可能限制了剪贴板访问,请允许浏览器访问剪贴板或尝试手动复制。',
Download: '下载',
deleteSuccess:'删除成功'
deleteSuccess: '删除成功',
thinking:'已思考'
},
// Version Tree
@@ -186,6 +187,6 @@ export default {
//3d面板
threeModel: {
loading: '加载中',
download: '下载',
download: '下载'
}
}

View File

@@ -1,92 +0,0 @@
import { ref } from 'vue'
export const useWebsiteTitle = () => {
const titles = ref<Map<string, string>>(new Map())
const loading = ref(new Set<string>())
const errors = ref(new Map<string, string>())
// 新增:重试配置
const MAX_RETRY = 3
const BASE_DELAY = 800 // 毫秒
const getCache = (url: string) => {
const cached = sessionStorage.getItem(`title_cache_${url}`)
if (!cached) return null
const { title, expire } = JSON.parse(cached)
return Date.now() < expire ? title : null
}
const setCache = (url: string, title: string) => {
const data = { title, expire: Date.now() + 7 * 24 * 60 * 60 * 1000 }
sessionStorage.setItem(`title_cache_${url}`, JSON.stringify(data))
}
const fetchWithRetry = async (url: string, retryCount = 0): Promise<string> => {
const proxyUrl = `https://api.allorigins.win/raw?url=${encodeURIComponent(url)}`
try {
const res = await fetch(proxyUrl)
if (!res.ok) {
throw new Error(`HTTP ${res.status}`)
}
const html = await res.text()
const parser = new DOMParser()
const doc = parser.parseFromString(html, 'text/html')
const title =
doc.querySelector('title')?.textContent?.trim() || new URL(url).hostname || '无标题'
return title
} catch (err) {
if (retryCount >= MAX_RETRY) {
throw err // 达到最大重试次数,抛出错误
}
// 第1次等800ms第2次等1600ms第3次等3200ms
const delay = BASE_DELAY * Math.pow(2, retryCount)
console.warn(
`获取标题失败,${retryCount + 1}/${MAX_RETRY} 次重试,等待 ${delay}ms`,
url
)
await new Promise((resolve) => setTimeout(resolve, delay))
return fetchWithRetry(url, retryCount + 1)
}
}
const fetchTitle = async (url: string): Promise<string> => {
if (titles.value.has(url)) return titles.value.get(url)!
const cached = getCache(url)
if (cached) {
titles.value.set(url, cached)
return cached
}
if (loading.value.has(url)) return ''
loading.value.add(url)
errors.value.delete(url)
try {
const title = await fetchWithRetry(url)
titles.value.set(url, title)
setCache(url, title)
return title
} catch (err) {
const msg = `获取标题失败(已重试 ${MAX_RETRY} 次)`
errors.value.set(url, msg)
console.error(err)
return msg
} finally {
loading.value.delete(url)
}
}
const fetchAll = async (urls: string[]) => {
await Promise.allSettled(urls.map(fetchTitle))
}
return { titles, loading, errors, fetchTitle, fetchAll }
}

View File

@@ -146,7 +146,7 @@
id: messageList.value.length + 1,
text: '',
isUser: false,
sessionId: 'projectStore.state.id',
sessionId: projectStore.state.id,
loading: true,
thinking: false,
thinkingText: '',
@@ -258,7 +258,7 @@
reportsContent.value
) {
isGeneratingReport.value = false
localStorage.setItem(
sessionStorage.setItem(
'reportsContent_' + projectStore.state.id,
reportsContent.value
)
@@ -297,6 +297,7 @@
}
// if (eventName === 'webAddress') {
// console.log('webAddress111111111111111', eventName, dataLines)
// debugger
// }
if (eventName === 'tool') {
@@ -311,17 +312,17 @@
// console.log('jsonData', jsonData)
if (jsonData.webAddress) {
aiMessage.webAddress = JSON.parse(jsonData.webAddress)
contentBody += `<slot slot-name="url">123</slot>`
}
if (jsonData.title) {
emits('setTitle', jsonData.title)
}
if (hasSketch) {
console.log('sketch事件-----', jsonData);
sketchList.value.push({
[Object.keys(jsonData)[0]]: jsonData[Object.keys(jsonData)[0]]
})
MyEvent.emit('OpenSketch')
}
if (eventName === 'report') {
reportsContent.value += jsonData.report
@@ -438,10 +439,6 @@
while (i < dialogue.length) {
const item = dialogue[i]
if (item.webAddress?.length > 0) {
console.log('item.webAddress-----', item.webAddress)
debugger
}
if (item.role === 'user') {
// user 角色直接添加
result.push({
@@ -513,7 +510,7 @@
}
})
if (reportStr && session.id) {
localStorage.setItem(`reportsContent_${session.id}`, reportStr)
sessionStorage.setItem(`reportsContent_${session.id}`, reportStr)
}
// 3. 收集 sketchIDAndUrl 到 imgList

View File

@@ -22,7 +22,7 @@
class="thinking-header flex align-center"
@click="toggleThinkingCollapsed"
>
<span>思考中</span>
<span>{{ t('agent.thinking') }}</span>
<!-- <SvgIcon :name="content.thinkingCollapsed ? 'arrowDown' : 'arrowUp'" size="16" color="#666" /> -->
</div>
<div class="thinking-content" v-show="!content.thinkingCollapsed">
@@ -40,11 +40,10 @@
<template v-slot:s-card="{ children: children, ...attrs }">
<Card :title="attrs.title" @click.native="handleClickReport" />
</template>
<template v-slot:url-card="{ children: children }">
<template v-slot:s-url="{ children: children }">
<Url :list="content.webAddress" @click.native="handleClickUrls" />
</template>
</VueMarkdown>
<!-- <Url @click.native="handleClickUrls" /> -->
<div
class="web-address flex align-center"
v-show="content.webAddress?.length > 0"

View File

@@ -50,6 +50,17 @@
<img src="@/assets/images/sketch-loading.gif" alt="loading" />
</div>
</template>
<template v-else-if="type === 'url'">
<div class="url-list flex">
<div class="url-item" v-for="item in urlList" :key="item">
<div class="url-title" @click="handleClickUrl(item)">
{{ item }}
<img src="@/assets/images/link-outer.png" class="link-outer" />
</div>
<div class="url-link">{{ item }}</div>
</div>
</div>
</template>
<div v-else class="reportBorder">
<div class="report">
<!-- <div v-if="false" class="report-content-null">
@@ -75,11 +86,6 @@
</div>
</div>
</template>
<template v-else>
<div class="url-list">
<div class="url-item" v-for="item in urlList" :key="item"></div>
</div>
</template>
</div>
</div>
</div>
@@ -99,9 +105,6 @@
import { VueMarkdown } from '@crazydos/vue-markdown'
import type { CustomAttrs } from '@crazydos/vue-markdown'
import rehypeRaw from 'rehype-raw'
import { useWebsiteTitle } from '@/utils/useWebTitle'
const { titles, loading, fetchAll } = useWebsiteTitle()
const { t } = useI18n()
const emits = defineEmits(['deleteSketch'])
@@ -111,7 +114,7 @@
const props = withDefaults(
defineProps<{
type: 'sketch' | 'report'
type: 'sketch' | 'report' | 'url'
sketchList: Array<string>
}>(),
{
@@ -138,26 +141,23 @@
const urlList = ref([])
const reportType = ref<'report' | 'urls'>('report')
const setSessionId = (id: string) => {
console.log('setSessionId-----', id)
reportType.value = 'report'
sessionId.value = id
}
const setUrls = (list: string[]) => {
console.log('setUrls-----', list)
reportType.value = 'urls'
urlList.value = [
'https://furnitureindustrynews.substack.com/p/what-2026-really-looks-like-for-furniture',
'https://furnitureindustrynews.substack.com/p/what-2026-really-looks-like-for-furniture',
'https://furnitureindustrynews.substack.com/p/what-2026-really-looks-like-for-furniture'
]
fetchAll(list)
urlList.value = list
// urlList.value = [
// 'https://furnitureindustrynews.substack.com/p/what-2026-really-looks-like-for-furniture',
// 'https://furnitureindustrynews.substack.com/p/what-2026-really-looks-like-for-furniture',
// 'https://furnitureindustrynews.substack.com/p/what-2026-really-looks-like-for-furniture'
// ]
}
watch(
() => sessionId.value,
(newVal) => {
if (newVal) {
markdownContent.value = localStorage.getItem(`reportsContent_${newVal}`)
console.log(`报告key值:reportsContent_${newVal}`, markdownContent.value)
markdownContent.value = sessionStorage.getItem(`reportsContent_${newVal}`)
}
}
)
@@ -226,6 +226,11 @@
const handleLoadingSketch = () => {
showLoading.value = true
}
const handleClickUrl = (item: string) => {
window.open(item, '_blank')
}
// watch(
// () => props.sketchList,
// (val) => {
@@ -301,6 +306,38 @@
}
}
}
.url-list {
flex: 1;
flex-wrap: wrap;
column-gap: 4rem;
row-gap: 3rem;
.url-item {
width: 24rem;
height: 28.7rem;
word-break: break-all;
background: url('@/assets/images/web-card.png') no-repeat;
background-size: 100% 100%;
padding: 5rem 1.5rem;
.url-title {
cursor: pointer;
font-family: 'Medium';
font-size: 1.6rem;
color: #232323;
padding-bottom: 0.6rem;
.link-outer {
width: 1.2rem;
height: 1.2rem;
}
}
.url-link {
font-family: 'Medium';
font-style: italic;
font-size: 1.2rem;
color: #7c7c7c;
user-select: text;
}
}
}
.reportBorder {
position: relative;
display: flex;

View File

@@ -8,7 +8,7 @@
</div>
</div>
<div class="report-card-content">
<span v-if="!isUrl">{{ content }}</span>
<span v-if="!isUrl">markdown.md</span>
<span v-else>Destination URL</span>
</div>
</div>

View File

@@ -104,16 +104,13 @@
const proJectId = computed(() => route.params.id)
const handleOpenReport = (data, isUrls = false) => {
if (isUrls) {
previewRef.value.setUrls(data)
} else {
previewRef.value.setSessionId(data)
}
previewRef.value.setSessionId(data)
previewType.value = 'report'
}
const handleOpenUrls = (data) => {
handleOpenReport(data, true)
previewRef.value.setUrls(data)
previewType.value = 'url'
}
watch(
@@ -129,6 +126,9 @@
onMounted(() => {
MyEvent.add('openReport', handleOpenReport)
MyEvent.add('openUrls', handleOpenUrls)
MyEvent.add('OpenSketch', ()=>{
previewType.value = 'sketch'
})
projectStore.clearProject()
if (proJectId.value) {
handleGetProjectInfoAndHistory()
@@ -137,6 +137,9 @@
onUnmounted(() => {
MyEvent.remove('openReport', handleOpenReport)
MyEvent.remove('openUrls', handleOpenUrls)
MyEvent.remove('OpenSketch', ()=>{
previewType.value = 'sketch'
})
})
</script>