20
20
21
21
<!-- Content -->
22
22
<div class =" p-6" >
23
- <div v-if =" loading" class =" text-gray-400 flex items-center" >
24
- <Loader2 class =" w-5 h-5 animate-spin" />
25
- <span class =" ml-2 " >正在生成摘要...</span >
23
+ <div v-if =" loading" class =" flex flex-col items-center justify-center space-y-3 py-4 " >
24
+ <Loader2 class =" w-6 h-6 animate-spin text-[#E6A53F] " />
25
+ <span class =" text-gray-400 text-sm " >正在生成摘要...</span >
26
26
</div >
27
27
28
- <div v-else-if =" error" class =" text-red-400" >
29
- {{ error }}
28
+ <div v-else-if =" error" class =" space-y-4" >
29
+ <div class =" flex items-center justify-center text-red-400 bg-red-400/10 rounded-lg p-4" >
30
+ <AlertCircle class =" w-5 h-5 mr-2" />
31
+ <span >{{ error }}</span >
32
+ </div >
33
+ <button
34
+ @click =" generateSummary"
35
+ class =" w-full flex items-center justify-center space-x-2 bg-gray-800 text-white px-4 py-2 rounded-lg hover:bg-gray-700 transition-colors"
36
+ >
37
+ <RefreshCw class =" w-4 h-4" />
38
+ <span >重试</span >
39
+ </button >
30
40
</div >
31
41
32
- <div v-else-if =" summary" class =" text-white text-base leading-relaxed" >
33
- {{ summary }}
42
+ <div v-else-if =" summary" class =" space-y-4" >
43
+ <div class =" text-white text-base leading-relaxed" >
44
+ {{ summary }}
45
+ </div >
46
+ <button
47
+ @click =" generateSummary"
48
+ :disabled =" isRateLimited"
49
+ class =" w-full flex items-center justify-center space-x-2 bg-gray-800 text-white px-4 py-2 rounded-lg hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
50
+ >
51
+ <RefreshCw class =" w-4 h-4" />
52
+ <span >重新生成</span >
53
+ </button >
34
54
</div >
35
55
36
- <button
37
- @click =" generateSummary"
38
- :disabled =" loading"
39
- class =" mt-4 bg-[#E6A53F] text-black px-4 py-2 rounded-lg hover:bg-[#D49530] disabled:opacity-50"
40
- >
41
- 生成摘要
42
- </button >
56
+ <div v-else class =" space-y-4" >
57
+ <div class =" text-gray-400 text-sm" >
58
+ 点击下方按钮,AI 将自动分析当前文章内容并生成摘要
59
+ </div >
60
+ <button
61
+ @click =" generateSummary"
62
+ :disabled =" isRateLimited"
63
+ class =" w-full bg-[#E6A53F] text-black px-4 py-2 rounded-lg hover:bg-[#D49530] disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
64
+ >
65
+ 生成摘要
66
+ </button >
67
+ </div >
43
68
</div >
44
69
45
70
<!-- Footer -->
55
80
56
81
<script setup>
57
82
import { ref , onMounted } from ' vue'
58
- import { Bot , ChevronRight , Loader2 } from ' lucide-vue-next'
83
+ import { Bot , ChevronRight , Loader2 , AlertCircle , RefreshCw } from ' lucide-vue-next'
59
84
60
85
const summary = ref (' ' )
61
86
const loading = ref (false )
62
87
const error = ref (null )
63
88
const articleContent = ref (' ' )
89
+ const lastRequestTime = ref (0 )
90
+ const RATE_LIMIT_DELAY = 10000 // 10 seconds between requests
91
+
92
+ const isRateLimited = computed (() => {
93
+ return Date .now () - lastRequestTime .value < RATE_LIMIT_DELAY
94
+ })
95
+
96
+ const extractArticleContent = () => {
97
+ // 获取文章主要内容,优先使用 article 标签
98
+ const article = document .querySelector (' article' ) || document .querySelector (' .vp-doc' )
99
+ if (! article) return ' '
100
+
101
+ // 移除不需要的元素内容
102
+ const clonedArticle = article .cloneNode (true )
103
+ const elementsToRemove = clonedArticle .querySelectorAll (' pre, code, script, style, .article-summarizer' )
104
+ elementsToRemove .forEach (el => el .remove ())
105
+
106
+ // 获取纯文本内容
107
+ return clonedArticle .textContent .trim ()
108
+ }
64
109
65
110
onMounted (() => {
66
- // 获取当前页面的主要内容
67
- const mainContent = document .querySelector (' .vp-doc' )? .textContent || ' '
68
- articleContent .value = mainContent
111
+ articleContent .value = extractArticleContent ()
69
112
})
70
113
71
114
const generateSummary = async () => {
72
- if (! articleContent .value ) return
115
+ if (! articleContent .value || loading . value || isRateLimited . value ) return
73
116
74
117
loading .value = true
75
118
error .value = null
119
+ lastRequestTime .value = Date .now ()
76
120
77
121
try {
78
122
const response = await fetch (' https://api.deepseek.com/v1/summarize' , {
@@ -86,7 +130,12 @@ const generateSummary = async () => {
86
130
messages: [
87
131
{
88
132
role: 'user',
89
- content: ` 请对以下文章内容进行摘要总结:\n\n${articleContent .value }`
133
+ content: ` 请对以下文章内容进行摘要总结,要求:
134
+ 1. 提炼文章的核心观点和主要内容
135
+ 2. 保持语言简洁清晰
136
+ 3. 总结控制在300 字以内
137
+
138
+ 文章内容:${articleContent .value }`
90
139
}
91
140
]
92
141
})
@@ -99,8 +148,8 @@ const generateSummary = async () => {
99
148
const data = await response.json()
100
149
summary.value = data.choices[0].message.content
101
150
} catch (err) {
102
- error.value = '生成摘要时出错,请稍后重试'
103
151
console.error('Error:', err)
152
+ error.value = '生成摘要时出错,请稍后重试'
104
153
} finally {
105
154
loading.value = false
106
155
}
0 commit comments