@templmf/temp-solf-lmf 0.0.55 → 0.0.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/package.json +1 -1
  2. package//345/242/236/351/207/217/351/234/200/346/261/202prompt +72 -0
  3. package/guanwang/README.md +0 -95
  4. package/guanwang/docs/changelog.md +0 -145
  5. package/guanwang/docs/doc-maintenance.md +0 -229
  6. package/guanwang/docs/product.md +0 -181
  7. package/guanwang/docs/test-cases.md +0 -395
  8. package/guanwang/docs/usage.md +0 -291
  9. package/guanwang/env.example +0 -27
  10. package/guanwang/index.html +0 -13
  11. package/guanwang/package-lock.json +0 -3825
  12. package/guanwang/package.json +0 -32
  13. package/guanwang/public/favicon.svg +0 -4
  14. package/guanwang/public/react-runtime/babel.min.js +0 -4
  15. package/guanwang/public/react-runtime/react-dom.min.js +0 -267
  16. package/guanwang/public/react-runtime/react.min.js +0 -31
  17. package/guanwang/public/vue-repl-assets/compiler-sfc.esm-browser.js +0 -50795
  18. package/guanwang/public/vue-repl-assets/runtime-dom.esm-browser.js +0 -12758
  19. package/guanwang/public/vue-repl-assets/server-renderer.esm-browser.js +0 -8600
  20. package/guanwang/public/vue-repl-assets/vue.esm-browser.js +0 -18672
  21. package/guanwang/src/App.vue +0 -61
  22. package/guanwang/src/chat-sdk/core/components/ChatBox.vue +0 -305
  23. package/guanwang/src/chat-sdk/core/components/ChatSidebar.vue +0 -84
  24. package/guanwang/src/chat-sdk/core/components/InputBar.vue +0 -354
  25. package/guanwang/src/chat-sdk/core/components/MessageBubble.vue +0 -703
  26. package/guanwang/src/chat-sdk/core/useTheme.js +0 -31
  27. package/guanwang/src/chat-sdk/features/artifact/ArtifactCard.vue +0 -172
  28. package/guanwang/src/chat-sdk/features/artifact/ArtifactPanel.vue +0 -963
  29. package/guanwang/src/chat-sdk/features/artifact/index.js +0 -13
  30. package/guanwang/src/chat-sdk/features/artifact/useArtifactStore.js +0 -275
  31. package/guanwang/src/chat-sdk/features/codepreview/CodePreview.vue +0 -523
  32. package/guanwang/src/chat-sdk/features/codepreview/index.js +0 -7
  33. package/guanwang/src/chat-sdk/features/markdown/index.js +0 -13
  34. package/guanwang/src/chat-sdk/features/markdown/useMarkdown.js +0 -724
  35. package/guanwang/src/chat-sdk/features/mermaid/MermaidZoom.vue +0 -254
  36. package/guanwang/src/chat-sdk/features/upload/FileAttachment.vue +0 -142
  37. package/guanwang/src/chat-sdk/features/upload/index.js +0 -17
  38. package/guanwang/src/chat-sdk/features/upload/useFileHandler.js +0 -336
  39. package/guanwang/src/chat-sdk/headless/api/adapters/openai.js +0 -76
  40. package/guanwang/src/chat-sdk/headless/api/chatApi.js +0 -126
  41. package/guanwang/src/chat-sdk/headless/buildSystemPrompt.js +0 -351
  42. package/guanwang/src/chat-sdk/headless/index.js +0 -15
  43. package/guanwang/src/chat-sdk/headless/useChat.js +0 -77
  44. package/guanwang/src/chat-sdk/headless/useChatDB.js +0 -147
  45. package/guanwang/src/chat-sdk/headless/useChatStore.js +0 -529
  46. package/guanwang/src/chat-sdk/index.js +0 -79
  47. package/guanwang/src/chat-sdk/modes/architect.js +0 -27
  48. package/guanwang/src/chat-sdk/modes/ask.js +0 -26
  49. package/guanwang/src/chat-sdk/modes/code.js +0 -25
  50. package/guanwang/src/chat-sdk/modes/index.js +0 -36
  51. package/guanwang/src/chat-sdk/modes/requirements.js +0 -175
  52. package/guanwang/src/chat-sdk/settings/SettingsPanel.vue +0 -170
  53. package/guanwang/src/chat-sdk/settings/index.js +0 -9
  54. package/guanwang/src/chat-sdk/settings/useSettings.js +0 -122
  55. package/guanwang/src/chat-sdk/tools/defaults.js +0 -89
  56. package/guanwang/src/chat-sdk/tools/index.js +0 -16
  57. package/guanwang/src/chat-sdk/tools/parser.js +0 -116
  58. package/guanwang/src/components/CustomCursor.vue +0 -69
  59. package/guanwang/src/components/Footer.vue +0 -24
  60. package/guanwang/src/components/LoginModal.vue +0 -109
  61. package/guanwang/src/components/Navbar.vue +0 -193
  62. package/guanwang/src/components/ThemeToggle.vue +0 -25
  63. package/guanwang/src/composables/useArtifactStore.js +0 -253
  64. package/guanwang/src/composables/useAuth.js +0 -88
  65. package/guanwang/src/composables/useChatDB.js +0 -147
  66. package/guanwang/src/composables/useCountUp.js +0 -24
  67. package/guanwang/src/composables/useFileHandler.js +0 -345
  68. package/guanwang/src/composables/useTheme.js +0 -31
  69. package/guanwang/src/config/api.js +0 -71
  70. package/guanwang/src/main.js +0 -23
  71. package/guanwang/src/router/index.js +0 -23
  72. package/guanwang/src/services/authApi.js +0 -27
  73. package/guanwang/src/services/chatApi.js +0 -66
  74. package/guanwang/src/styles/global.css +0 -478
  75. package/guanwang/src/tracker/analyze.js +0 -73
  76. package/guanwang/src/tracker/config.js +0 -82
  77. package/guanwang/src/tracker/index.js +0 -18
  78. package/guanwang/src/tracker/service.js +0 -102
  79. package/guanwang/src/tracker/useChatTracker.js +0 -179
  80. package/guanwang/src/tracker/useTracker.js +0 -45
  81. package/guanwang/src/views/ChatView.vue +0 -65
  82. package/guanwang/src/views/HomeView.vue +0 -156
  83. package/guanwang/src/views/MarketView.vue +0 -143
  84. package/guanwang/src/views/PracticesView.vue +0 -190
  85. package/guanwang/src/views/SkillsView.vue +0 -129
  86. package/guanwang/temp +0 -19
  87. package/guanwang/vite.config.js +0 -6
@@ -1,345 +0,0 @@
1
- /**
2
- * useFileHandler.js
3
- * 附件处理核心
4
- *
5
- * 文件类型路由:
6
- * 图片(jpg/png/webp/gif/svg)→ base64 → image_url content block → 多模态模型
7
- * PDF → 每页转图片 base64 → 多模态模型
8
- * 代码/文本/json/csv/md → 读文本 → 拼进 text block → LLM
9
- * Excel(xlsx/xls) → SheetJS 提取 → text block → LLM
10
- * Word(docx) → mammoth 提取 → text block → LLM
11
- * 其他 → 拒绝,给出提示
12
- */
13
-
14
- // ── 类型分组 ──────────────────────────────────────────────────────
15
- const IMAGE_TYPES = new Set(['image/jpeg','image/png','image/webp','image/gif','image/svg+xml'])
16
- const PDF_TYPE = 'application/pdf'
17
- const TEXT_EXTS = new Set([
18
- 'txt','md','markdown','js','mjs','cjs','ts','jsx','tsx',
19
- 'vue','html','htm','css','scss','sass','less',
20
- 'json','jsonc','yaml','yml','toml','xml','svg',
21
- 'sh','bash','zsh','fish','py','go','rs','java','kt','swift','c','cpp','h','cs','php','rb',
22
- 'sql','graphql','gql','env','gitignore','dockerfile',
23
- ])
24
- const EXCEL_EXTS = new Set(['xlsx','xls','csv'])
25
- const WORD_EXTS = new Set(['docx'])
26
-
27
- // 单文件大小上限
28
- const MAX_IMAGE_SIZE = 20 * 1024 * 1024 // 20MB
29
- const MAX_TEXT_SIZE = 2 * 1024 * 1024 // 2MB
30
- const MAX_PDF_SIZE = 50 * 1024 * 1024 // 50MB
31
- const MAX_PDF_PAGES = 20 // PDF 最多处理前 N 页(防止 token 超限)
32
-
33
- // ── 工具函数 ──────────────────────────────────────────────────────
34
- function getExt(name) {
35
- return name.split('.').pop()?.toLowerCase() || ''
36
- }
37
-
38
- function formatSize(bytes) {
39
- if (bytes < 1024) return `${bytes} B`
40
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
41
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`
42
- }
43
-
44
- function readAsDataURL(file) {
45
- return new Promise((resolve, reject) => {
46
- const reader = new FileReader()
47
- reader.onload = e => resolve(e.target.result)
48
- reader.onerror = () => reject(new Error('文件读取失败'))
49
- reader.readAsDataURL(file)
50
- })
51
- }
52
-
53
- function readAsText(file) {
54
- return new Promise((resolve, reject) => {
55
- const reader = new FileReader()
56
- reader.onload = e => resolve(e.target.result)
57
- reader.onerror = () => reject(new Error('文件读取失败'))
58
- reader.readAsText(file, 'utf-8')
59
- })
60
- }
61
-
62
- function readAsArrayBuffer(file) {
63
- return new Promise((resolve, reject) => {
64
- const reader = new FileReader()
65
- reader.onload = e => resolve(e.target.result)
66
- reader.onerror = () => reject(new Error('文件读取失败'))
67
- reader.readAsArrayBuffer(file)
68
- })
69
- }
70
-
71
- // dataURL → { mediaType, base64 }
72
- function parseDataURL(dataURL) {
73
- const [header, data] = dataURL.split(',')
74
- const mediaType = header.match(/:(.*?);/)?.[1] || 'application/octet-stream'
75
- return { mediaType, base64: data }
76
- }
77
-
78
- // ── 各类型处理器 ──────────────────────────────────────────────────
79
-
80
- /** 图片 → image_url content block */
81
- async function processImage(file) {
82
- if (file.size > MAX_IMAGE_SIZE) {
83
- throw new Error(`图片过大(${formatSize(file.size)}),上限 20MB`)
84
- }
85
- const dataURL = await readAsDataURL(file)
86
- const { mediaType, base64 } = parseDataURL(dataURL)
87
-
88
- return {
89
- type: 'visual', // 标记需要多模态模型
90
- preview: dataURL, // 用于 UI 预览缩略图
91
- fileName: file.name,
92
- fileSize: formatSize(file.size),
93
- // OpenAI 兼容格式的 image_url block
94
- contentBlock: {
95
- type: 'image_url',
96
- image_url: { url: `data:${mediaType};base64,${base64}` },
97
- },
98
- }
99
- }
100
-
101
- /** PDF → 每页转 canvas → base64 图片数组 → image_url blocks */
102
- async function processPDF(file) {
103
- if (file.size > MAX_PDF_SIZE) {
104
- throw new Error(`PDF 过大(${formatSize(file.size)}),上限 50MB`)
105
- }
106
-
107
- const arrayBuffer = await readAsArrayBuffer(file)
108
-
109
- // 动态加载 pdfjs
110
- const pdfjsLib = await import('pdfjs-dist').catch(() => null)
111
- if (!pdfjsLib) throw new Error('PDF 解析库加载失败,请刷新重试')
112
-
113
- // 设置 worker(使用 CDN)
114
- if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
115
- pdfjsLib.GlobalWorkerOptions.workerSrc =
116
- 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.4.168/pdf.worker.min.mjs'
117
- }
118
-
119
- const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise
120
- const pageCount = Math.min(pdf.numPages, MAX_PDF_PAGES)
121
- const blocks = []
122
- const canvas = document.createElement('canvas')
123
- const ctx = canvas.getContext('2d')
124
-
125
- // 首页作为预览缩略图
126
- let previewDataURL = ''
127
-
128
- for (let i = 1; i <= pageCount; i++) {
129
- const page = await pdf.getPage(i)
130
- const viewport = page.getViewport({ scale: 1.5 })
131
- canvas.width = viewport.width
132
- canvas.height = viewport.height
133
- await page.render({ canvasContext: ctx, viewport }).promise
134
- const dataURL = canvas.toDataURL('image/jpeg', 0.85)
135
- if (i === 1) previewDataURL = dataURL
136
- const { mediaType, base64 } = parseDataURL(dataURL)
137
- blocks.push({
138
- type: 'image_url',
139
- image_url: { url: `data:${mediaType};base64,${base64}` },
140
- })
141
- }
142
-
143
- const truncated = pdf.numPages > MAX_PDF_PAGES
144
- ? `\n(PDF 共 ${pdf.numPages} 页,已截取前 ${MAX_PDF_PAGES} 页)` : ''
145
-
146
- return {
147
- type: 'visual',
148
- preview: previewDataURL,
149
- fileName: file.name,
150
- fileSize: formatSize(file.size),
151
- pageCount: pdf.numPages,
152
- processedPages: pageCount,
153
- // 多个 image_url blocks + 一个说明 text block
154
- contentBlocks: [
155
- { type: 'text', text: `[PDF 文件:${file.name},共 ${pageCount} 页${truncated}]` },
156
- ...blocks,
157
- ],
158
- }
159
- }
160
-
161
- /** 文本类文件 → text content block */
162
- async function processText(file) {
163
- if (file.size > MAX_TEXT_SIZE) {
164
- throw new Error(`文件过大(${formatSize(file.size)}),文本文件上限 2MB`)
165
- }
166
- const text = await readAsText(file)
167
- const ext = getExt(file.name)
168
-
169
- // 推断语言标识,用于代码块包裹
170
- const LANG_MAP = {
171
- js:'javascript', mjs:'javascript', cjs:'javascript',
172
- ts:'typescript', jsx:'jsx', tsx:'tsx',
173
- py:'python', go:'go', rs:'rust', java:'java', kt:'kotlin',
174
- swift:'swift', c:'c', cpp:'cpp', cs:'csharp', php:'php', rb:'ruby',
175
- sh:'bash', bash:'bash', zsh:'bash',
176
- html:'html', htm:'html', css:'css', scss:'scss', sass:'sass', less:'less',
177
- vue:'vue', sql:'sql', graphql:'graphql', gql:'graphql',
178
- json:'json', jsonc:'json', yaml:'yaml', yml:'yaml', toml:'toml', xml:'xml',
179
- md:'markdown', markdown:'markdown',
180
- }
181
- const lang = LANG_MAP[ext] || ''
182
-
183
- const wrapped = lang
184
- ? `\`\`\`${lang}\n${text}\n\`\`\``
185
- : text
186
-
187
- return {
188
- type: 'text',
189
- preview: null,
190
- fileName: file.name,
191
- fileSize: formatSize(file.size),
192
- ext,
193
- contentBlock: {
194
- type: 'text',
195
- text: `[文件:${file.name}]\n${wrapped}`,
196
- },
197
- }
198
- }
199
-
200
- /** Excel/CSV → SheetJS → Markdown 表格 → text block */
201
- async function processExcel(file) {
202
- if (file.size > MAX_TEXT_SIZE) {
203
- throw new Error(`文件过大(${formatSize(file.size)}),上限 2MB`)
204
- }
205
-
206
- const XLSX = await import('xlsx').catch(() => null)
207
- if (!XLSX) throw new Error('Excel 解析库加载失败')
208
-
209
- const ab = await readAsArrayBuffer(file)
210
- const wb = XLSX.read(ab, { type: 'array' })
211
- const results = []
212
-
213
- for (const sheetName of wb.SheetNames.slice(0, 5)) {
214
- const ws = wb.Sheets[sheetName]
215
- const csv = XLSX.utils.sheet_to_csv(ws)
216
- results.push(`### Sheet: ${sheetName}\n\`\`\`csv\n${csv}\n\`\`\``)
217
- }
218
-
219
- const text = results.join('\n\n')
220
- return {
221
- type: 'text',
222
- preview: null,
223
- fileName: file.name,
224
- fileSize: formatSize(file.size),
225
- ext: getExt(file.name),
226
- contentBlock: {
227
- type: 'text',
228
- text: `[Excel 文件:${file.name}]\n${text}`,
229
- },
230
- }
231
- }
232
-
233
- /** Word docx → mammoth → 纯文本 → text block */
234
- async function processWord(file) {
235
- if (file.size > MAX_TEXT_SIZE) {
236
- throw new Error(`文件过大(${formatSize(file.size)}),上限 2MB`)
237
- }
238
-
239
- const mammoth = await import('mammoth').catch(() => null)
240
- if (!mammoth) throw new Error('Word 解析库加载失败')
241
-
242
- const ab = await readAsArrayBuffer(file)
243
- const result = await mammoth.extractRawText({ arrayBuffer: ab })
244
- const text = result.value
245
-
246
- return {
247
- type: 'text',
248
- preview: null,
249
- fileName: file.name,
250
- fileSize: formatSize(file.size),
251
- ext: 'docx',
252
- contentBlock: {
253
- type: 'text',
254
- text: `[Word 文件:${file.name}]\n${text}`,
255
- },
256
- }
257
- }
258
-
259
- // ── 主入口 ────────────────────────────────────────────────────────
260
-
261
- /**
262
- * 处理单个 File 对象,返回附件描述对象
263
- *
264
- * 返回结构:
265
- * {
266
- * id: string, // 唯一标识
267
- * type: 'visual'|'text',
268
- * fileName: string,
269
- * fileSize: string,
270
- * preview: string|null, // 图片 dataURL,用于缩略图
271
- * ext: string,
272
- * // 单 block 文件
273
- * contentBlock: object,
274
- * // 多 block 文件(PDF)
275
- * contentBlocks: object[],
276
- * }
277
- */
278
- export async function processFile(file) {
279
- const ext = getExt(file.name)
280
-
281
- let result
282
-
283
- if (IMAGE_TYPES.has(file.type) || file.type.startsWith('image/')) {
284
- result = await processImage(file)
285
- } else if (file.type === PDF_TYPE || ext === 'pdf') {
286
- result = await processPDF(file)
287
- } else if (EXCEL_EXTS.has(ext) || file.type.includes('spreadsheet') || file.type.includes('csv')) {
288
- result = await processExcel(file)
289
- } else if (WORD_EXTS.has(ext) || file.type.includes('wordprocessingml')) {
290
- result = await processWord(file)
291
- } else if (TEXT_EXTS.has(ext) || file.type.startsWith('text/')) {
292
- result = await processText(file)
293
- } else {
294
- throw new Error(`暂不支持 .${ext} 类型的文件`)
295
- }
296
-
297
- return {
298
- id: `file-${Date.now()}-${Math.random().toString(36).slice(2)}`,
299
- ...result,
300
- }
301
- }
302
-
303
- /**
304
- * 把附件列表转成 API content blocks
305
- * 用于拼进 user message 的 content 数组
306
- *
307
- * @param {string} text - 用户输入的文字
308
- * @param {Array} attachments - processFile 返回的附件数组
309
- * @returns {string | Array} - 无附件时返回纯字符串;有附件时返回 content 数组
310
- */
311
- export function buildMessageContent(text, attachments) {
312
- if (!attachments || attachments.length === 0) return text
313
-
314
- const blocks = []
315
-
316
- // 先放所有附件内容
317
- for (const att of attachments) {
318
- if (att.contentBlocks) {
319
- // PDF:多个 block
320
- blocks.push(...att.contentBlocks)
321
- } else if (att.contentBlock) {
322
- blocks.push(att.contentBlock)
323
- }
324
- }
325
-
326
- // 最后放用户文字(符合多模态模型的习惯:图在前,问题在后)
327
- if (text) {
328
- blocks.push({ type: 'text', text })
329
- }
330
-
331
- return blocks
332
- }
333
-
334
- /**
335
- * 支持的文件类型说明(用于 input accept 属性和提示文案)
336
- */
337
- export const ACCEPT_TYPES = [
338
- 'image/jpeg','image/png','image/webp','image/gif',
339
- 'application/pdf',
340
- '.txt','.md','.js','.ts','.jsx','.tsx','.vue','.html','.css','.scss',
341
- '.json','.yaml','.yml','.toml','.xml','.sql','.py','.go','.rs','.java',
342
- '.sh','.bash','.env','.csv','.xlsx','.xls','.docx',
343
- ].join(',')
344
-
345
- export const FILE_TYPE_HINT = '支持图片、PDF、代码、文本、Excel、Word 等格式'
@@ -1,31 +0,0 @@
1
- /**
2
- * useTheme.js
3
- * 主题(深色/浅色)管理,provide/inject 模式
4
- */
5
- import { ref, watch, provide, inject } from 'vue'
6
-
7
- const THEME_KEY = Symbol('theme')
8
-
9
- export function createTheme() {
10
- const theme = ref('light')
11
-
12
- watch(theme, val => {
13
- document.documentElement.setAttribute('data-theme', val)
14
- }, { immediate: true })
15
-
16
- function toggleTheme() {
17
- theme.value = theme.value === 'dark' ? 'light' : 'dark'
18
- }
19
-
20
- return { theme, toggleTheme }
21
- }
22
-
23
- export function provideTheme() {
24
- const themeStore = createTheme()
25
- provide(THEME_KEY, themeStore)
26
- return themeStore
27
- }
28
-
29
- export function useTheme() {
30
- return inject(THEME_KEY)
31
- }
@@ -1,71 +0,0 @@
1
- /**
2
- * API 配置
3
- * 在项目根目录创建 .env.local,填入以下变量:
4
- *
5
- * # 文本 LLM
6
- * VITE_API_BASE_URL=https://your-api-host.com
7
- * VITE_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
8
- * VITE_MODEL=gpt-4o
9
- *
10
- * # 多模型选择(逗号分隔,不填则只显示默认模型)
11
- * VITE_MODELS=gpt-4o,gpt-4o-mini,claude-3-5-sonnet-20241022,deepseek-chat
12
- *
13
- * # 多模态视觉模型(独立 endpoint / key,不填则复用上面的配置)
14
- * VITE_VL_API_BASE_URL=https://your-vl-api-host.com
15
- * VITE_VL_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
16
- * VITE_VL_MODEL=qwen3-vl-8b-instruct-k100
17
- *
18
- * # 调试:在气泡底部显示原始文本
19
- * VITE_SHOW_RAW_TEXT=true
20
- */
21
- export const API_CONFIG = {
22
- baseURL: import.meta.env.VITE_API_BASE_URL || 'https://api.openai.com',
23
- apiKey: import.meta.env.VITE_API_KEY || 'sk-your-key-here',
24
- model: import.meta.env.VITE_MODEL || 'gpt-4o',
25
-
26
- vlBaseURL: import.meta.env.VITE_VL_API_BASE_URL || null,
27
- vlApiKey: import.meta.env.VITE_VL_API_KEY || null,
28
- vlModel: import.meta.env.VITE_VL_MODEL || null,
29
- }
30
-
31
- /**
32
- * 从 VITE_MODELS 读取可选模型列表
33
- * 格式:逗号分隔的模型 id,例如:gpt-4o,gpt-4o-mini,claude-3-5-sonnet-20241022
34
- * 若未配置则返回仅含默认模型的数组
35
- */
36
- export function getAvailableModels() {
37
- const raw = import.meta.env.VITE_MODELS || ''
38
- const list = raw.split(',').map(s => s.trim()).filter(Boolean)
39
- if (list.length === 0) return [API_CONFIG.model]
40
- // 确保默认模型在列表中
41
- if (!list.includes(API_CONFIG.model)) list.unshift(API_CONFIG.model)
42
- return list
43
- }
44
-
45
- /**
46
- * 根据消息内容判断是否需要视觉模型
47
- */
48
- export function isVisualRequest(messages) {
49
- return messages.some(m => {
50
- const c = m.content
51
- return Array.isArray(c) && c.some(b => b.type === 'image_url' || b.type === 'image')
52
- })
53
- }
54
-
55
- /**
56
- * 根据消息内容选择对应的请求配置(model / baseURL / apiKey)
57
- */
58
- export function selectConfig(messages) {
59
- const useVl = isVisualRequest(messages) && API_CONFIG.vlModel
60
- return useVl
61
- ? {
62
- model: API_CONFIG.vlModel,
63
- baseURL: API_CONFIG.vlBaseURL || API_CONFIG.baseURL,
64
- apiKey: API_CONFIG.vlApiKey || API_CONFIG.apiKey,
65
- }
66
- : {
67
- model: API_CONFIG.model,
68
- baseURL: API_CONFIG.baseURL,
69
- apiKey: API_CONFIG.apiKey,
70
- }
71
- }
@@ -1,23 +0,0 @@
1
- import { createApp } from 'vue'
2
- import App from './App.vue'
3
- import router from './router/index.js'
4
- import ElementPlus from 'element-plus'
5
- import 'element-plus/dist/index.css'
6
- import * as ElementPlusIconsVue from '@element-plus/icons-vue'
7
- import 'katex/dist/katex.min.css'
8
- import './styles/global.css' // 在 EP css 之后引入,覆盖主题变量
9
-
10
- // 挂载 KaTeX 到 window,供 useMarkdown 同步调用
11
- import katex from 'katex'
12
- window.katex = katex
13
-
14
- const app = createApp(App)
15
-
16
- // 注册所有 Element Plus 图标
17
- for (const [name, comp] of Object.entries(ElementPlusIconsVue)) {
18
- app.component(name, comp)
19
- }
20
-
21
- app.use(router)
22
- app.use(ElementPlus)
23
- app.mount('#app')
@@ -1,23 +0,0 @@
1
- import { createRouter, createWebHistory } from 'vue-router'
2
- import HomeView from '../views/HomeView.vue'
3
- import ChatView from '../views/ChatView.vue'
4
- import MarketView from '../views/MarketView.vue'
5
- import SkillsView from '../views/SkillsView.vue'
6
- import PracticesView from '../views/PracticesView.vue'
7
-
8
- const routes = [
9
- { path: '/', component: HomeView },
10
- { path: '/chat', component: ChatView },
11
- { path: '/market', component: MarketView },
12
- { path: '/skills', component: SkillsView },
13
- { path: '/practices', component: PracticesView },
14
- { path: '/:pathMatch(.*)*', redirect: '/' },
15
- ]
16
-
17
- const router = createRouter({
18
- history: createWebHistory(),
19
- routes,
20
- scrollBehavior: () => ({ top: 0 }),
21
- })
22
-
23
- export default router
@@ -1,27 +0,0 @@
1
- /**
2
- * authApi.js
3
- * 当前:mock 本地账号验证
4
- * 后续对接后端:将 login() 替换为真实 fetch 调用,返回格式保持 { user, token }
5
- */
6
-
7
- const MOCK_USERS = [
8
- { id: 1, username: 'admin', password: 'admin123', name: '管理员', avatar: null, role: 'admin' },
9
- { id: 2, username: 'dev', password: 'dev123', name: '开发者', avatar: null, role: 'developer' },
10
- ]
11
-
12
- function delay(ms) {
13
- return new Promise(resolve => setTimeout(resolve, ms))
14
- }
15
-
16
- export async function login(username, password) {
17
- await delay(600)
18
- const matched = MOCK_USERS.find(u => u.username === username && u.password === password)
19
- if (!matched) throw new Error('账号或密码错误')
20
- const { password: _pwd, ...user } = matched
21
- const token = `mock-token-${user.id}-${Date.now()}`
22
- return { user, token }
23
- }
24
-
25
- export async function logout() {
26
- await delay(200)
27
- }
@@ -1,66 +0,0 @@
1
- /**
2
- * chatApi.js
3
- * 封装 OpenAI 兼容格式的流式对话接口(/v1/chat/completions)
4
- * 支持多模态 content 数组格式(图片 / 文本混合)
5
- */
6
- import { selectConfig } from '../config/api.js'
7
-
8
- /**
9
- * 发起流式对话请求
10
- * @param {object} options
11
- * @param {Array} options.messages - 消息历史,content 可为字符串或数组(多模态)
12
- * @param {string} [options.modelOverride] - 手动指定模型,不填则使用配置默认值
13
- * @param {Function} options.onToken - (token: string) => void
14
- * @param {Function} options.onDone - 流结束
15
- * @param {Function} options.onError - (error: Error) => void
16
- * @param {AbortSignal} options.signal
17
- */
18
- export async function streamChat({ messages, modelOverride, onToken, onDone, onError, signal }) {
19
- try {
20
- const cfg = selectConfig(messages)
21
- const model = modelOverride || cfg.model
22
- const { baseURL, apiKey } = cfg
23
-
24
- const res = await fetch(`${baseURL}/v1/chat/completions`, {
25
- method: 'POST',
26
- headers: {
27
- 'Content-Type': 'application/json',
28
- 'Authorization': `Bearer ${apiKey}`,
29
- },
30
- body: JSON.stringify({ model, stream: true, messages }),
31
- signal,
32
- })
33
-
34
- if (!res.ok) {
35
- const errBody = await res.text()
36
- throw new Error(`API 请求失败 ${res.status}: ${errBody}`)
37
- }
38
-
39
- const reader = res.body.getReader()
40
- const decoder = new TextDecoder('utf-8')
41
- let buffer = ''
42
-
43
- while (true) {
44
- const { done, value } = await reader.read()
45
- if (done) break
46
- buffer += decoder.decode(value, { stream: true })
47
- const lines = buffer.split('\n')
48
- buffer = lines.pop()
49
-
50
- for (const line of lines) {
51
- const trimmed = line.trim()
52
- if (!trimmed || trimmed === 'data: [DONE]') continue
53
- if (!trimmed.startsWith('data: ')) continue
54
- try {
55
- const json = JSON.parse(trimmed.slice(6))
56
- const token = json.choices?.[0]?.delta?.content
57
- if (token) onToken(token)
58
- } catch { /* 非 JSON 行跳过 */ }
59
- }
60
- }
61
- onDone?.()
62
- } catch (err) {
63
- if (err.name === 'AbortError') return
64
- onError?.(err)
65
- }
66
- }