feat: url link

This commit is contained in:
2026-03-16 11:43:19 +08:00
parent 3400dcf9af
commit 5b7ee903e8
10 changed files with 255 additions and 68 deletions

View File

@@ -210,7 +210,6 @@
// 流式响应处理
let contentBody = ''
let buffer = ''
const webAddressList = []
const reader = response.body?.getReader()
if (!reader) throw new Error('无法获取流读取器')
@@ -248,10 +247,8 @@
?.trim() || ''
if (!hasReportStarted && eventName === 'report') {
console.log('开始生成报告--------')
isGeneratingReport.value = true
contentBody += `<slot slot-name="card" title="123">123</slot>`
contentBody += `<slot slot-name="card" title="123" content="123">123</slot>`
hasReportStarted = true
}
@@ -298,9 +295,9 @@
params.versionID = dataLines[0]
projectStore.setProject({ nodeId: dataLines[0] })
}
if (eventName === 'webAddress') {
console.log('webAddress-----', eventName, dataLines)
}
// if (eventName === 'webAddress') {
// console.log('webAddress111111111111111', eventName, dataLines)
// }
if (eventName === 'tool') {
MyEvent.emit('loading-sketch')
@@ -313,7 +310,7 @@
const jsonData = JSON.parse(jsonText)
// console.log('jsonData', jsonData)
if (jsonData.webAddress) {
console.log('webAddress-----', jsonData)
aiMessage.webAddress = JSON.parse(jsonData.webAddress)
}
if (jsonData.title) {
emits('setTitle', jsonData.title)
@@ -439,7 +436,10 @@
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({
@@ -496,7 +496,7 @@
for (let i = 0; i < session.dialogue.length; i++) {
if (session.dialogue[i].report) {
session.dialogue[i].content =
`<slot slot-name="card" title="123">123</slot>` +
`<slot slot-name="card" title="123" content="123">123</slot>` +
(session.dialogue[i].content || '')
break
}
@@ -516,7 +516,7 @@
// 3. 收集 sketchIDAndUrl 到 imgList
if (session.sketchIDAndUrl) {
imgList.push(...session.sketchIDAndUrl)
imgList.push(session.sketchIDAndUrl)
}
// 4. 处理 dialogue

View File

@@ -40,10 +40,21 @@
<template v-slot:s-card="{ children: children, ...attrs }">
<Card :title="attrs.title" @click.native="handleClickReport" />
</template>
<template v-slot:url-card="{ 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"
>
<img src="@/assets/images/search.png" class="search-icon" />
<span>{{ content.webAddress?.length }} web pages have been retrieved.</span>
</div>
</div>
<div
v-show="!content.thinking"
v-show="!content.streaming"
class="operate flex"
:class="{ 'is-user': content.isUser }"
>
@@ -96,13 +107,13 @@
isLast: Boolean
}>()
watch(
() => props.content,
(newVal) => {
console.log('newVal-----', newVal)
},
{ immediate: true }
)
// watch(
// () => props.content,
// (newVal) => {
// console.log('newVal-----', newVal)
// },
// { immediate: true }
// )
const emit = defineEmits(['regenerate'])
@@ -215,6 +226,7 @@
// 点击显示报告
}
const handleClickUrls = (data) => {
MyEvent.emit('openUrls', props.content.webAddress)
// 点击显示来源
}
</script>
@@ -260,6 +272,24 @@
width: fit-content;
max-width: 82%;
}
.web-address {
width: fit-content;
min-width: 22.5rem;
line-height: 2.6rem;
padding: 0 1rem;
border-radius: 1.5rem;
color: #000000a6;
border: 0.1rem solid #0000001a;
font-family: 'Regular';
font-weight: 400;
font-size: 1.2rem;
margin-top: 1rem;
column-gap: 0.8rem;
.search-icon {
width: 1.4rem;
height: 1.4rem;
}
}
}
.operate {
margin-top: 1.3rem;

View File

@@ -52,27 +52,34 @@
</template>
<div v-else class="reportBorder">
<div class="report">
<div v-if="false" class="report-content-null">
<!-- <div v-if="false" class="report-content-null">
<img :src="reportNull" alt="" />
</div>
<div v-else class="report-content">
<div class="downBtnBox">
<div class="downBtn" @click="handleDownloadMd">
<div class="icon">
<SvgIcon name="reportDown" size="16"></SvgIcon>
</div> -->
<template v-if="reportType === 'report'">
<div class="report-content">
<div class="downBtnBox">
<div class="downBtn" @click="handleDownloadMd">
<div class="icon">
<SvgIcon name="reportDown" size="16"></SvgIcon>
</div>
<span>{{ $t('agent.Download') }}</span>
</div>
<span>{{ $t('agent.Download') }}</span>
</div>
<div class="content">
<VueMarkdown
:custom-attrs="customAttrs"
:markdown="markdownContent"
:rehype-plugins="[rehypeRaw]"
>
</VueMarkdown>
</div>
</div>
<div class="content">
<VueMarkdown
:custom-attrs="customAttrs"
:markdown="markdownContent"
:rehype-plugins="[rehypeRaw]"
>
</VueMarkdown>
</template>
<template v-else>
<div class="url-list">
<div class="url-item" v-for="item in urlList" :key="item"></div>
</div>
</div>
</template>
</div>
</div>
</div>
@@ -92,6 +99,9 @@
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'])
@@ -125,17 +135,29 @@
const sessionId = ref('')
const markdownContent = ref('')
const setSessionId = (id: string) => {
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)
}
watch(
() => sessionId.value,
(newVal) => {
if (newVal) {
markdownContent.value = localStorage.getItem(`reportsContent_${newVal}`)
console.log('markdownContent-----', markdownContent.value);
console.log(`报告key值:reportsContent_${newVal}`, markdownContent.value)
}
}
)
@@ -143,9 +165,7 @@ const setSessionId = (id: string) => {
// 图片加载完成时触发
const handleImageLoad = (index: number) => {
loadedStatus[index] = true
if (index === props.sketchList.length - 1) {
showLoading.value = false
}
showLoading.value = false
}
// 获取当前显示的图片源
@@ -209,6 +229,8 @@ const setSessionId = (id: string) => {
watch(
() => props.sketchList,
(val) => {
console.log('sketchList-----', val);
if (val.length > 0) {
showLoading.value = false
}
@@ -223,7 +245,8 @@ const setSessionId = (id: string) => {
})
defineExpose({
setSessionId
setSessionId,
setUrls
})
</script>
@@ -289,6 +312,7 @@ const setSessionId = (id: string) => {
flex: 1;
overflow: hidden;
height: 100%;
&::before {
content: '';
position: absolute;
@@ -348,6 +372,7 @@ const setSessionId = (id: string) => {
white-space: pre-wrap;
overflow-y: auto;
margin: 2rem;
padding: 0 8.8rem 8.8rem;
}
}
}

View File

@@ -1,49 +1,79 @@
<template>
<div class="report-card">
<div class="report-card" :class="{ 'is-url': isUrl }">
<div class="report-card-header">
<span>{{ title }}</span>
<span v-if="!isUrl">{{ title }}</span>
<div v-else class="web-sources flex align-center">
<span>Web Sources</span>
<img src="@/assets/images/link.png" class="link-icon" />
</div>
</div>
<div class="report-card-content">
<span>{{ title }}</span>
<span v-if="!isUrl">{{ content }}</span>
<span v-else>Destination URL</span>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{
title: string
}>()
// const props = defineProps<{
// title: string
// isUrl?: boolean
// }>()
const props = withDefaults(
defineProps<{
title?: string
content?: string
isUrl?: boolean
}>(),
{
isUrl: false,
title: '',
content: ''
}
)
</script>
<style lang="less" scoped>
.report-card {
cursor: pointer;
width:100%;
width: 100%;
margin: 2.4rem 0;
min-height: 11.2rem;
background: url('@/assets/images/report-card.png') no-repeat;
background-size: 100% 100%;
padding: 2.9rem;
padding: 2.9rem;
overflow: hidden;
&.is-url {
background: url('@/assets/images/link-card.png') no-repeat;
background-size: 100% 100%;
}
// &:first-of-type{
// margin-top: 0;
// }
&-header {
font-family: 'Medium';
font-size: 1.6rem;
margin-bottom: 1.3rem;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
line-clamp: 3;
font-size: 1.6rem;
margin-bottom: 1.3rem;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
line-clamp: 3;
.web-sources {
column-gap: 0.8rem;
.link-icon {
width: 1.6rem;
height: 1.6rem;
}
}
}
&-content {
font-family: 'Regular';
font-weight: 300;
font-size: 1.6rem;
color: #7c7c7c;
}
&-content{
font-family: 'Regular';
font-weight: 300;
font-size: 1.6rem;
color: #7c7c7c;
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<ReportCard :report="{title: 'WebSources', content: 'Destination URL'}"/>
<ReportCard is-url :report="{title: 'WebSources', content: 'Destination URL'}"/>
</template>
<script setup lang="ts">

View File

@@ -103,11 +103,19 @@
const proJectId = computed(() => route.params.id)
const handleOpenReport = (data) => {
previewRef.value.setSessionId(data)
const handleOpenReport = (data, isUrls = false) => {
if (isUrls) {
previewRef.value.setUrls(data)
} else {
previewRef.value.setSessionId(data)
}
previewType.value = 'report'
}
const handleOpenUrls = (data) => {
handleOpenReport(data, true)
}
watch(
() => proJectId.value,
(newVal, oldVal) => {
@@ -120,6 +128,7 @@ const handleOpenReport = (data) => {
onMounted(() => {
MyEvent.add('openReport', handleOpenReport)
MyEvent.add('openUrls', handleOpenUrls)
projectStore.clearProject()
if (proJectId.value) {
handleGetProjectInfoAndHistory()
@@ -127,6 +136,7 @@ const handleOpenReport = (data) => {
})
onUnmounted(() => {
MyEvent.remove('openReport', handleOpenReport)
MyEvent.remove('openUrls', handleOpenUrls)
})
</script>