@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.
- package/package.json +1 -1
- package//345/242/236/351/207/217/351/234/200/346/261/202prompt +72 -0
- package/guanwang/README.md +0 -95
- package/guanwang/docs/changelog.md +0 -145
- package/guanwang/docs/doc-maintenance.md +0 -229
- package/guanwang/docs/product.md +0 -181
- package/guanwang/docs/test-cases.md +0 -395
- package/guanwang/docs/usage.md +0 -291
- package/guanwang/env.example +0 -27
- package/guanwang/index.html +0 -13
- package/guanwang/package-lock.json +0 -3825
- package/guanwang/package.json +0 -32
- package/guanwang/public/favicon.svg +0 -4
- package/guanwang/public/react-runtime/babel.min.js +0 -4
- package/guanwang/public/react-runtime/react-dom.min.js +0 -267
- package/guanwang/public/react-runtime/react.min.js +0 -31
- package/guanwang/public/vue-repl-assets/compiler-sfc.esm-browser.js +0 -50795
- package/guanwang/public/vue-repl-assets/runtime-dom.esm-browser.js +0 -12758
- package/guanwang/public/vue-repl-assets/server-renderer.esm-browser.js +0 -8600
- package/guanwang/public/vue-repl-assets/vue.esm-browser.js +0 -18672
- package/guanwang/src/App.vue +0 -61
- package/guanwang/src/chat-sdk/core/components/ChatBox.vue +0 -305
- package/guanwang/src/chat-sdk/core/components/ChatSidebar.vue +0 -84
- package/guanwang/src/chat-sdk/core/components/InputBar.vue +0 -354
- package/guanwang/src/chat-sdk/core/components/MessageBubble.vue +0 -703
- package/guanwang/src/chat-sdk/core/useTheme.js +0 -31
- package/guanwang/src/chat-sdk/features/artifact/ArtifactCard.vue +0 -172
- package/guanwang/src/chat-sdk/features/artifact/ArtifactPanel.vue +0 -963
- package/guanwang/src/chat-sdk/features/artifact/index.js +0 -13
- package/guanwang/src/chat-sdk/features/artifact/useArtifactStore.js +0 -275
- package/guanwang/src/chat-sdk/features/codepreview/CodePreview.vue +0 -523
- package/guanwang/src/chat-sdk/features/codepreview/index.js +0 -7
- package/guanwang/src/chat-sdk/features/markdown/index.js +0 -13
- package/guanwang/src/chat-sdk/features/markdown/useMarkdown.js +0 -724
- package/guanwang/src/chat-sdk/features/mermaid/MermaidZoom.vue +0 -254
- package/guanwang/src/chat-sdk/features/upload/FileAttachment.vue +0 -142
- package/guanwang/src/chat-sdk/features/upload/index.js +0 -17
- package/guanwang/src/chat-sdk/features/upload/useFileHandler.js +0 -336
- package/guanwang/src/chat-sdk/headless/api/adapters/openai.js +0 -76
- package/guanwang/src/chat-sdk/headless/api/chatApi.js +0 -126
- package/guanwang/src/chat-sdk/headless/buildSystemPrompt.js +0 -351
- package/guanwang/src/chat-sdk/headless/index.js +0 -15
- package/guanwang/src/chat-sdk/headless/useChat.js +0 -77
- package/guanwang/src/chat-sdk/headless/useChatDB.js +0 -147
- package/guanwang/src/chat-sdk/headless/useChatStore.js +0 -529
- package/guanwang/src/chat-sdk/index.js +0 -79
- package/guanwang/src/chat-sdk/modes/architect.js +0 -27
- package/guanwang/src/chat-sdk/modes/ask.js +0 -26
- package/guanwang/src/chat-sdk/modes/code.js +0 -25
- package/guanwang/src/chat-sdk/modes/index.js +0 -36
- package/guanwang/src/chat-sdk/modes/requirements.js +0 -175
- package/guanwang/src/chat-sdk/settings/SettingsPanel.vue +0 -170
- package/guanwang/src/chat-sdk/settings/index.js +0 -9
- package/guanwang/src/chat-sdk/settings/useSettings.js +0 -122
- package/guanwang/src/chat-sdk/tools/defaults.js +0 -89
- package/guanwang/src/chat-sdk/tools/index.js +0 -16
- package/guanwang/src/chat-sdk/tools/parser.js +0 -116
- package/guanwang/src/components/CustomCursor.vue +0 -69
- package/guanwang/src/components/Footer.vue +0 -24
- package/guanwang/src/components/LoginModal.vue +0 -109
- package/guanwang/src/components/Navbar.vue +0 -193
- package/guanwang/src/components/ThemeToggle.vue +0 -25
- package/guanwang/src/composables/useArtifactStore.js +0 -253
- package/guanwang/src/composables/useAuth.js +0 -88
- package/guanwang/src/composables/useChatDB.js +0 -147
- package/guanwang/src/composables/useCountUp.js +0 -24
- package/guanwang/src/composables/useFileHandler.js +0 -345
- package/guanwang/src/composables/useTheme.js +0 -31
- package/guanwang/src/config/api.js +0 -71
- package/guanwang/src/main.js +0 -23
- package/guanwang/src/router/index.js +0 -23
- package/guanwang/src/services/authApi.js +0 -27
- package/guanwang/src/services/chatApi.js +0 -66
- package/guanwang/src/styles/global.css +0 -478
- package/guanwang/src/tracker/analyze.js +0 -73
- package/guanwang/src/tracker/config.js +0 -82
- package/guanwang/src/tracker/index.js +0 -18
- package/guanwang/src/tracker/service.js +0 -102
- package/guanwang/src/tracker/useChatTracker.js +0 -179
- package/guanwang/src/tracker/useTracker.js +0 -45
- package/guanwang/src/views/ChatView.vue +0 -65
- package/guanwang/src/views/HomeView.vue +0 -156
- package/guanwang/src/views/MarketView.vue +0 -143
- package/guanwang/src/views/PracticesView.vue +0 -190
- package/guanwang/src/views/SkillsView.vue +0 -129
- package/guanwang/temp +0 -19
- package/guanwang/vite.config.js +0 -6
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tracker/service.js
|
|
3
|
-
*
|
|
4
|
-
* Ackee 上报服务,管理 ackee-tracker 实例和上报队列。
|
|
5
|
-
* 所有事件通过 track() 统一入口,TRACK_ENABLED=false 时静默丢弃。
|
|
6
|
-
*/
|
|
7
|
-
import { ACKEE_URL, ACKEE_DOMAIN, TRACK_ENABLED, EVENTS } from './config.js'
|
|
8
|
-
|
|
9
|
-
// ── Ackee tracker 实例(懒加载)──────────────────────────────────
|
|
10
|
-
let _instance = null
|
|
11
|
-
let _loading = false
|
|
12
|
-
|
|
13
|
-
async function getInstance() {
|
|
14
|
-
if (_instance) return _instance
|
|
15
|
-
if (!TRACK_ENABLED) return null
|
|
16
|
-
if (_loading) {
|
|
17
|
-
// 等待加载完成
|
|
18
|
-
return new Promise(resolve => {
|
|
19
|
-
const timer = setInterval(() => {
|
|
20
|
-
if (!_loading) { clearInterval(timer); resolve(_instance) }
|
|
21
|
-
}, 50)
|
|
22
|
-
})
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
_loading = true
|
|
26
|
-
try {
|
|
27
|
-
const { default: ackeeTracker } = await import('ackee-tracker')
|
|
28
|
-
_instance = ackeeTracker.create(ACKEE_URL, {
|
|
29
|
-
detailed: false, // 不收集详细浏览器信息,保护隐私
|
|
30
|
-
ignoreLocalhost: false, // 内网环境需要设为 false
|
|
31
|
-
ignoreOwnVisits: false,
|
|
32
|
-
})
|
|
33
|
-
// 上报一次 page view(domain 维度)
|
|
34
|
-
_instance.record(ACKEE_DOMAIN)
|
|
35
|
-
} catch (e) {
|
|
36
|
-
console.warn('[tracker] ackee-tracker 加载失败', e)
|
|
37
|
-
}
|
|
38
|
-
_loading = false
|
|
39
|
-
return _instance
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ── 上报队列(页面加载初期 Ackee 未就绪时缓存事件)────────────────
|
|
43
|
-
const _queue = []
|
|
44
|
-
let _flushing = false
|
|
45
|
-
|
|
46
|
-
async function flush() {
|
|
47
|
-
if (_flushing || _queue.length === 0) return
|
|
48
|
-
_flushing = true
|
|
49
|
-
|
|
50
|
-
const instance = await getInstance()
|
|
51
|
-
if (!instance) {
|
|
52
|
-
_queue.length = 0 // 无实例时清空队列(disabled 状态)
|
|
53
|
-
_flushing = false
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
while (_queue.length > 0) {
|
|
58
|
-
const { eventId, key, value } = _queue.shift()
|
|
59
|
-
try {
|
|
60
|
-
instance.action(eventId, { key: String(key), value: Number(value) || 1 })
|
|
61
|
-
} catch (e) {
|
|
62
|
-
console.warn('[tracker] action 上报失败', eventId, e)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
_flushing = false
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ── 核心上报函数 ─────────────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 上报一个 Ackee action
|
|
72
|
-
* @param {string} eventId EVENTS 里的 UUID
|
|
73
|
-
* @param {string} key 分组维度
|
|
74
|
-
* @param {number} value 数值,默认 1
|
|
75
|
-
*/
|
|
76
|
-
export function track(eventId, key, value = 1) {
|
|
77
|
-
if (!TRACK_ENABLED) return
|
|
78
|
-
if (!eventId || !key) return
|
|
79
|
-
|
|
80
|
-
_queue.push({ eventId, key: String(key), value })
|
|
81
|
-
flush()
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* 批量上报多个 action(同一事件触发多条记录时使用)
|
|
86
|
-
* @param {Array<{eventId, key, value?}>} actions
|
|
87
|
-
*/
|
|
88
|
-
export function trackBatch(actions) {
|
|
89
|
-
if (!TRACK_ENABLED) return
|
|
90
|
-
actions.forEach(({ eventId, key, value = 1 }) => {
|
|
91
|
-
if (eventId && key) _queue.push({ eventId, key: String(key), value })
|
|
92
|
-
})
|
|
93
|
-
flush()
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// 页面加载后预热实例
|
|
97
|
-
if (TRACK_ENABLED) {
|
|
98
|
-
getInstance()
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// 导出 EVENTS 方便外部引用,避免重复 import config
|
|
102
|
-
export { EVENTS }
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tracker/useChatTracker.js
|
|
3
|
-
*
|
|
4
|
-
* AI 对话框埋点 composable,覆盖:
|
|
5
|
-
* - 对话行为(发消息、回复、中止、报错、模式/模型切换)
|
|
6
|
-
* - 采纳行为(复制、下载)
|
|
7
|
-
* - 提问内容分析(方向、意图、复杂度、附件,无用户关联)
|
|
8
|
-
* - 工具调用
|
|
9
|
-
*
|
|
10
|
-
* 用法:在 useChatStore 或 ChatBox 里引入
|
|
11
|
-
* import { useChatTracker } from '@/tracker/useChatTracker.js'
|
|
12
|
-
* const chatTracker = useChatTracker({ getUserName: () => user.value?.name })
|
|
13
|
-
*/
|
|
14
|
-
import { track, trackBatch, EVENTS } from './service.js'
|
|
15
|
-
import { getDirection, getIntent, getComplexity, getAttachmentType } from './analyze.js'
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @param {object} opts
|
|
19
|
-
* @param {() => string} opts.getUserName 获取当前用户名的函数,未登录返回空字符串
|
|
20
|
-
*/
|
|
21
|
-
export function useChatTracker({ getUserName = () => '' } = {}) {
|
|
22
|
-
|
|
23
|
-
// ── 对话行为 ───────────────────────────────────────────────────
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 用户发送消息
|
|
27
|
-
* 同时触发:模式分布、模型分布、用户用量、提问分析(4个方向)
|
|
28
|
-
*
|
|
29
|
-
* @param {object} opts
|
|
30
|
-
* @param {string} opts.mode 当前模式
|
|
31
|
-
* @param {string} opts.modelName 模型显示名
|
|
32
|
-
* @param {string} opts.text 提问文本(本地分析后不上报原文)
|
|
33
|
-
* @param {Array} opts.attachments 附件列表
|
|
34
|
-
*/
|
|
35
|
-
function messageSent({ mode, modelName, text = '', attachments = [] }) {
|
|
36
|
-
const userName = getUserName()
|
|
37
|
-
|
|
38
|
-
// 对话行为:模式、模型、用户
|
|
39
|
-
const actions = [
|
|
40
|
-
{ eventId: EVENTS.MSG_MODE, key: mode || 'unknown' },
|
|
41
|
-
{ eventId: EVENTS.MSG_MODEL, key: modelName || 'unknown' },
|
|
42
|
-
]
|
|
43
|
-
if (userName) {
|
|
44
|
-
actions.push({ eventId: EVENTS.MSG_USER, key: userName })
|
|
45
|
-
}
|
|
46
|
-
trackBatch(actions)
|
|
47
|
-
|
|
48
|
-
// 提问内容分析(无用户关联,单独上报)
|
|
49
|
-
_trackQuestionAnalysis(text, mode, attachments)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* AI 回复开始(记录开始时间,和 responseDone 配合计算耗时)
|
|
54
|
-
* @returns {number} startTime 用于传入 responseDone
|
|
55
|
-
*/
|
|
56
|
-
function responseStart() {
|
|
57
|
-
return Date.now()
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* AI 回复完成
|
|
62
|
-
* @param {object} opts
|
|
63
|
-
* @param {number} opts.startTime responseStart() 返回的时间戳
|
|
64
|
-
* @param {number} opts.ttfbMs 首字节延迟(ms)
|
|
65
|
-
* @param {string} opts.modelName 模型显示名
|
|
66
|
-
* @param {boolean} opts.hasError 是否报错
|
|
67
|
-
* @param {string} opts.errorType 报错类型,hasError=true 时有值
|
|
68
|
-
* @param {string[]} opts.toolNames 触发的工具名列表
|
|
69
|
-
*/
|
|
70
|
-
function responseDone({ startTime, ttfbMs, modelName, hasError, errorType, toolNames = [] }) {
|
|
71
|
-
const duration = startTime ? Date.now() - startTime : 0
|
|
72
|
-
const name = modelName || 'unknown'
|
|
73
|
-
|
|
74
|
-
if (hasError) {
|
|
75
|
-
track(EVENTS.RESP_ERROR, errorType || 'unknown')
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const actions = [
|
|
80
|
-
{ eventId: EVENTS.RESP_DURATION, key: name, value: duration },
|
|
81
|
-
]
|
|
82
|
-
if (ttfbMs > 0) {
|
|
83
|
-
actions.push({ eventId: EVENTS.RESP_TTFB, key: name, value: ttfbMs })
|
|
84
|
-
}
|
|
85
|
-
trackBatch(actions)
|
|
86
|
-
|
|
87
|
-
// 工具调用
|
|
88
|
-
toolNames.forEach(toolName => {
|
|
89
|
-
track(EVENTS.TOOL_EXECUTED, toolName)
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* 用户中止回复
|
|
95
|
-
* @param {string} userName
|
|
96
|
-
*/
|
|
97
|
-
function responseAbort(userName) {
|
|
98
|
-
track(EVENTS.RESP_ABORT, userName || getUserName() || 'anonymous')
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* 模式切换
|
|
103
|
-
* @param {string} fromMode
|
|
104
|
-
* @param {string} toMode
|
|
105
|
-
* @param {'manual'|'ai_tool'} trigger 触发来源
|
|
106
|
-
*/
|
|
107
|
-
function modeSwitch(fromMode, toMode, trigger = 'manual') {
|
|
108
|
-
track(EVENTS.MODE_SWITCH, `${fromMode}→${toMode}:${trigger}`)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// ── 采纳行为 ───────────────────────────────────────────────────
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* 内容复制
|
|
115
|
-
* @param {object} opts
|
|
116
|
-
* @param {'code_block'|'artifact_panel'|'bubble_text'|'bubble_btn'} opts.source
|
|
117
|
-
* @param {string} opts.lang 代码语言,非代码时传空字符串
|
|
118
|
-
*/
|
|
119
|
-
function contentCopy({ source, lang }) {
|
|
120
|
-
const userName = getUserName()
|
|
121
|
-
const actions = [
|
|
122
|
-
{ eventId: EVENTS.COPY_SOURCE, key: source || 'unknown' },
|
|
123
|
-
]
|
|
124
|
-
if (userName) {
|
|
125
|
-
actions.push({ eventId: EVENTS.COPY_USER, key: userName })
|
|
126
|
-
}
|
|
127
|
-
if (lang) {
|
|
128
|
-
actions.push({ eventId: EVENTS.COPY_LANG, key: lang })
|
|
129
|
-
}
|
|
130
|
-
trackBatch(actions)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* 文件下载
|
|
135
|
-
* @param {object} opts
|
|
136
|
-
* @param {string} opts.lang 文件语言
|
|
137
|
-
*/
|
|
138
|
-
function fileDownload({ lang }) {
|
|
139
|
-
const userName = getUserName()
|
|
140
|
-
const actions = [
|
|
141
|
-
{ eventId: EVENTS.DOWNLOAD_LANG, key: lang || 'unknown' },
|
|
142
|
-
]
|
|
143
|
-
if (userName) {
|
|
144
|
-
actions.push({ eventId: EVENTS.DOWNLOAD_USER, key: userName })
|
|
145
|
-
}
|
|
146
|
-
trackBatch(actions)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// ── 提问内容分析(内部,无用户关联)──────────────────────────
|
|
150
|
-
function _trackQuestionAnalysis(text, mode, attachments) {
|
|
151
|
-
const direction = getDirection(mode)
|
|
152
|
-
const intent = getIntent(text, mode)
|
|
153
|
-
const complexity = getComplexity(text)
|
|
154
|
-
const attachmentType = getAttachmentType(attachments)
|
|
155
|
-
|
|
156
|
-
const actions = [
|
|
157
|
-
{ eventId: EVENTS.Q_DIRECTION, key: direction },
|
|
158
|
-
{ eventId: EVENTS.Q_COMPLEXITY, key: complexity },
|
|
159
|
-
{ eventId: EVENTS.Q_ATTACHMENT, key: attachmentType },
|
|
160
|
-
]
|
|
161
|
-
|
|
162
|
-
// 意图只在代码方向触发
|
|
163
|
-
if (intent) {
|
|
164
|
-
actions.push({ eventId: EVENTS.Q_INTENT, key: intent })
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
trackBatch(actions)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return {
|
|
171
|
-
messageSent,
|
|
172
|
-
responseStart,
|
|
173
|
-
responseDone,
|
|
174
|
-
responseAbort,
|
|
175
|
-
modeSwitch,
|
|
176
|
-
contentCopy,
|
|
177
|
-
fileDownload,
|
|
178
|
-
}
|
|
179
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tracker/useTracker.js
|
|
3
|
-
*
|
|
4
|
-
* 官网页面行为埋点 composable。
|
|
5
|
-
* 在 App.vue 或各页面组件里引入使用。
|
|
6
|
-
*
|
|
7
|
-
* 用法:
|
|
8
|
-
* import { useTracker } from '@/tracker/useTracker.js'
|
|
9
|
-
* const tracker = useTracker()
|
|
10
|
-
* tracker.pageView('/chat')
|
|
11
|
-
* tracker.btnClick('hero_cta', '立即体验')
|
|
12
|
-
*/
|
|
13
|
-
import { track, EVENTS } from './service.js'
|
|
14
|
-
|
|
15
|
-
export function useTracker() {
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* 页面访问
|
|
19
|
-
* 在路由 afterEach 里调用
|
|
20
|
-
* @param {string} path 路由路径,如 '/chat'
|
|
21
|
-
*/
|
|
22
|
-
function pageView(path) {
|
|
23
|
-
track(EVENTS.PAGE_VIEW, path)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* 按钮点击
|
|
28
|
-
* @param {string} btnId 按钮唯一标识,如 'hero_cta' / 'nav_chat'
|
|
29
|
-
* @param {string} [label] 按钮文案,可选,用于拼接 key 更易读
|
|
30
|
-
*/
|
|
31
|
-
function btnClick(btnId, label) {
|
|
32
|
-
const key = label ? `${btnId}:${label}` : btnId
|
|
33
|
-
track(EVENTS.BTN_CLICK, key)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* 登录成功
|
|
38
|
-
* @param {string} userName
|
|
39
|
-
*/
|
|
40
|
-
function loginSuccess(userName) {
|
|
41
|
-
track(EVENTS.LOGIN_SUCCESS, userName || 'unknown')
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return { pageView, btnClick, loginSuccess }
|
|
45
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="chat-page">
|
|
3
|
-
<ChatBox
|
|
4
|
-
:config="sdkConfig"
|
|
5
|
-
:chat-store="chatStore"
|
|
6
|
-
:user="user"
|
|
7
|
-
/>
|
|
8
|
-
</div>
|
|
9
|
-
</template>
|
|
10
|
-
|
|
11
|
-
<script setup>
|
|
12
|
-
import { computed } from 'vue'
|
|
13
|
-
import { useAuth } from '../composables/useAuth.js'
|
|
14
|
-
import ChatBox from '../chat-sdk/core/components/ChatBox.vue'
|
|
15
|
-
|
|
16
|
-
const props = defineProps({
|
|
17
|
-
chatStore: { type: Object, required: true },
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const { user } = useAuth()
|
|
21
|
-
|
|
22
|
-
const sdkConfig = computed(() => ({
|
|
23
|
-
title: 'Frontend AI 助手',
|
|
24
|
-
placeholder: '向 AI 发消息... (Enter 发送,Shift+Enter 换行)',
|
|
25
|
-
|
|
26
|
-
// 注入用户信息,供埋点使用
|
|
27
|
-
getUser: () => user.value,
|
|
28
|
-
|
|
29
|
-
models: [
|
|
30
|
-
{
|
|
31
|
-
id: import.meta.env.VITE_MODEL || 'gpt-4o',
|
|
32
|
-
name: import.meta.env.VITE_MODEL || 'gpt-4o',
|
|
33
|
-
baseURL: import.meta.env.VITE_API_BASE_URL || 'https://api.openai.com',
|
|
34
|
-
apiKey: import.meta.env.VITE_API_KEY || '',
|
|
35
|
-
vision: false,
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
defaultModel: import.meta.env.VITE_MODEL || 'gpt-4o',
|
|
39
|
-
autoVision: true,
|
|
40
|
-
|
|
41
|
-
modes: ['code', 'ask', 'architect', 'requirements'],
|
|
42
|
-
defaultMode: 'code',
|
|
43
|
-
|
|
44
|
-
features: {
|
|
45
|
-
artifact: true,
|
|
46
|
-
markdown: true,
|
|
47
|
-
upload: true,
|
|
48
|
-
codepreview: true,
|
|
49
|
-
modelSelector: true
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
userControls: {
|
|
53
|
-
modelSelector: true,
|
|
54
|
-
artifact: true,
|
|
55
|
-
upload: true,
|
|
56
|
-
},
|
|
57
|
-
}))
|
|
58
|
-
</script>
|
|
59
|
-
|
|
60
|
-
<style scoped>
|
|
61
|
-
.chat-page {
|
|
62
|
-
height: calc(100vh - 64px);
|
|
63
|
-
overflow: hidden;
|
|
64
|
-
}
|
|
65
|
-
</style>
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="dot-grid" style="position: relative;">
|
|
3
|
-
<div class="glow-orb" style="width: 400px; height: 400px; background: rgba(26,111,196,0.2); top: 0; left: 25%;" />
|
|
4
|
-
<div class="glow-orb" style="width: 260px; height: 260px; background: rgba(96,165,250,0.1); top: 140px; right: 25%;" />
|
|
5
|
-
|
|
6
|
-
<!-- Hero -->
|
|
7
|
-
<section style="position: relative; max-width: 860px; margin: 0 auto; padding: 80px 24px 96px; display: flex; flex-direction: column; align-items: center; text-align: center;">
|
|
8
|
-
<div class="animate-fade-up" style="display: inline-flex; align-items: center; gap: 8px; padding: 6px 16px; border-radius: 999px; border: 1px solid var(--tag-border); background: var(--tag-bg); margin-bottom: 28px;">
|
|
9
|
-
<span class="animate-pulse-slow" style="width: 7px; height: 7px; border-radius: 50%; background: #4ade80; display: inline-block;" />
|
|
10
|
-
<span class="font-mono" style="font-size: 12px; color: var(--brand-400);">v1.0 现已上线 · 对话即服务</span>
|
|
11
|
-
</div>
|
|
12
|
-
|
|
13
|
-
<h1 class="font-display animate-fade-up stagger-1" style="font-weight: 800; line-height: 1.1; margin-bottom: 16px; font-size: clamp(2.4rem, 5.5vw, 3.8rem);">
|
|
14
|
-
<span class="gradient-text">Citic Frontend</span><br />
|
|
15
|
-
</h1>
|
|
16
|
-
|
|
17
|
-
<p class="animate-fade-up stagger-2" style="color: var(--text-secondary); margin-bottom: 36px; max-width: 480px; font-size: 1rem; line-height: 1.7; font-family: Inter, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;">
|
|
18
|
-
前端开发全流程智能伙伴
|
|
19
|
-
</p>
|
|
20
|
-
|
|
21
|
-
<div class="glass animate-fade-up stagger-3" style="width: 100%; max-width: 600px; border-radius: 16px; padding: 18px 18px 14px; box-shadow: 0 0 60px rgba(26,111,196,0.08), 0 20px 40px rgba(0,0,0,0.12);">
|
|
22
|
-
<div style="display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px;">
|
|
23
|
-
<button
|
|
24
|
-
v-for="s in QUICK"
|
|
25
|
-
:key="s"
|
|
26
|
-
class="quick-btn"
|
|
27
|
-
style="font-size: 12px; padding: 5px 10px; border-radius: 999px; border: 1px solid var(--border-subtle); background: var(--bg-secondary); color: var(--text-secondary); cursor: pointer; font-family: Inter, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; transition: all 0.15s;"
|
|
28
|
-
@click="handleSend(s)"
|
|
29
|
-
>{{ s }}</button>
|
|
30
|
-
</div>
|
|
31
|
-
<InputBar
|
|
32
|
-
:is-loading="false"
|
|
33
|
-
placeholder="描述您的需求,回车即进入 AI 对话..."
|
|
34
|
-
:available-models="props.chatStore.availableModels.filter(m => !m.vision)"
|
|
35
|
-
:selected-model-id="props.chatStore.selectedModel.value?.id || ''"
|
|
36
|
-
:show-model-selector="true"
|
|
37
|
-
@send="handleSend"
|
|
38
|
-
@select-model="props.chatStore.selectModel"
|
|
39
|
-
/>
|
|
40
|
-
<p class="font-mono" style="font-size: 11px; color: var(--text-muted); text-align: center; margin-top: 10px;">
|
|
41
|
-
发送后自动跳转到 AI 对话页面
|
|
42
|
-
</p>
|
|
43
|
-
</div>
|
|
44
|
-
</section>
|
|
45
|
-
|
|
46
|
-
<!-- Features -->
|
|
47
|
-
<section style="max-width: 960px; margin: 0 auto; padding: 0 24px 96px;">
|
|
48
|
-
<h2 class="section-title" style="text-align: center; margin-bottom: 10px;">核心能力</h2>
|
|
49
|
-
<p style="color: var(--text-secondary); text-align: center; margin-bottom: 40px; font-family: Inter, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;">四大场景,全部通过对话驱动</p>
|
|
50
|
-
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;">
|
|
51
|
-
<div v-for="f in FEATURES" :key="f.title" class="card">
|
|
52
|
-
<div style="width: 44px; height: 44px; border-radius: 12px; background: var(--tag-bg); border: 1px solid var(--tag-border); display: flex; align-items: center; justify-content: center; margin-bottom: 14px;">
|
|
53
|
-
<component :is="f.Icon" :size="22" color="var(--brand-400)" :stroke-width="1.75" />
|
|
54
|
-
</div>
|
|
55
|
-
<h3 class="font-display" style="font-weight: 600; color: var(--text-primary); font-size: 15px; margin-bottom: 8px;">{{ f.title }}</h3>
|
|
56
|
-
<p style="color: var(--text-secondary); font-size: 13px; line-height: 1.6; font-family: Inter, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;">{{ f.desc }}</p>
|
|
57
|
-
<div style="margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border-subtle);">
|
|
58
|
-
<span class="font-mono" style="font-size: 12px; color: var(--brand-400);">{{ f.example }}</span>
|
|
59
|
-
</div>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
</section>
|
|
63
|
-
|
|
64
|
-
<!-- Stats -->
|
|
65
|
-
<section ref="statsRef" style="max-width: 960px; margin: 0 auto; padding: 0 24px 96px;">
|
|
66
|
-
<div class="glass" style="border-radius: 16px; padding: 32px; display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 32px; text-align: center;">
|
|
67
|
-
<div v-for="(stat, i) in RAW_STATS" :key="stat.label" style="display: flex; flex-direction: column; align-items: center; gap: 4px;">
|
|
68
|
-
<div class="font-display" style="font-weight: 700; color: var(--text-primary); font-size: 2rem;">
|
|
69
|
-
{{ counters[i].current.value.toLocaleString() }}<span style="color: var(--brand-400);">{{ stat.suffix }}</span>
|
|
70
|
-
</div>
|
|
71
|
-
<div style="color: var(--text-muted); font-size: 14px; font-family: Inter, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;">{{ stat.label }}</div>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
</section>
|
|
75
|
-
|
|
76
|
-
<!-- CTA -->
|
|
77
|
-
<section style="max-width: 960px; margin: 0 auto; padding: 0 24px 112px; text-align: center;">
|
|
78
|
-
<div class="glass" style="border-radius: 16px; padding: 48px 32px; position: relative; overflow: hidden;">
|
|
79
|
-
<div class="glow-orb" style="width: 256px; height: 256px; background: rgba(26,111,196,0.15); top: -40px; left: 50%; transform: translateX(-50%);" />
|
|
80
|
-
<h2 class="section-title" style="margin-bottom: 14px; position: relative; z-index: 1;">立即体验对话式开发</h2>
|
|
81
|
-
<p style="color: var(--text-secondary); margin-bottom: 28px; font-family: Inter, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; position: relative; z-index: 1;">无需配置,直接对话,从第一个消息开始提速</p>
|
|
82
|
-
<div style="display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; position: relative; z-index: 1;">
|
|
83
|
-
<button class="btn-primary" style="padding: 12px 32px; font-size: 15px;" @click="router.push('/chat')">
|
|
84
|
-
<MessageSquare :size="16" /> 开始 AI 对话
|
|
85
|
-
</button>
|
|
86
|
-
<button class="btn-ghost" style="padding: 12px 32px; font-size: 15px; border: 1px solid var(--border-default); border-radius: 8px;" @click="router.push('/market')">
|
|
87
|
-
<Layers :size="16" /> 浏览组件市场
|
|
88
|
-
</button>
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
</section>
|
|
92
|
-
</div>
|
|
93
|
-
</template>
|
|
94
|
-
|
|
95
|
-
<script setup>
|
|
96
|
-
import { ref, onMounted, onUnmounted } from 'vue'
|
|
97
|
-
import { useRouter } from 'vue-router'
|
|
98
|
-
import { Rocket, Package, BarChart2, Wrench, MessageSquare, Layers } from 'lucide-vue-next'
|
|
99
|
-
import InputBar from '../chat-sdk/core/components/InputBar.vue'
|
|
100
|
-
import { useCountUp } from '../composables/useCountUp.js'
|
|
101
|
-
|
|
102
|
-
const props = defineProps({
|
|
103
|
-
chatStore: { type: Object, required: true },
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
const router = useRouter()
|
|
107
|
-
|
|
108
|
-
const FEATURES = [
|
|
109
|
-
{ Icon: Rocket, title: '脚手架生成', desc: '描述项目类型和技术栈,AI 即时生成可运行的工程模板', example: '「生成 Vue 3 + TS + Pinia 项目」' },
|
|
110
|
-
{ Icon: Package, title: '组件搜索', desc: '用自然语言找到最合适的组件,直接获取安装命令', example: '「找一个企业级 Table 组件」' },
|
|
111
|
-
{ Icon: BarChart2, title: '流水线监控', desc: '对话查询 CI/CD 状态,异常时自动推送修复建议', example: '「查看 main 分支构建状态」' },
|
|
112
|
-
{ Icon: Wrench, title: '异常修复', desc: '粘贴报错信息,获取精准定位分析与可执行修复方案', example: '「分析这个 TypeScript 报错」' },
|
|
113
|
-
]
|
|
114
|
-
|
|
115
|
-
const RAW_STATS = [
|
|
116
|
-
{ label: '接入开发者', target: 12000, suffix: '+' },
|
|
117
|
-
{ label: '组件总数', target: 380, suffix: '+' },
|
|
118
|
-
{ label: 'Skills 数量', target: 95, suffix: '+' },
|
|
119
|
-
{ label: 'GitHub Stars', target: 2100, suffix: '★' },
|
|
120
|
-
]
|
|
121
|
-
|
|
122
|
-
const QUICK = ['生成 Vue 3 + TS 脚手架', '查找 Table 组件', '查看流水线状态', '分析构建失败原因']
|
|
123
|
-
|
|
124
|
-
const counters = RAW_STATS.map(s => useCountUp(s.target))
|
|
125
|
-
const statsRef = ref(null)
|
|
126
|
-
let observer = null
|
|
127
|
-
|
|
128
|
-
function handleSend(val) {
|
|
129
|
-
const sid = props.chatStore.newSession(val)
|
|
130
|
-
router.push('/chat')
|
|
131
|
-
setTimeout(() => props.chatStore.sendMessage(val, sid), 80)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
onMounted(() => {
|
|
135
|
-
observer = new IntersectionObserver(
|
|
136
|
-
([entry]) => {
|
|
137
|
-
if (entry.isIntersecting) {
|
|
138
|
-
counters.forEach(c => c.start())
|
|
139
|
-
observer.disconnect()
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
{ threshold: 0.3 }
|
|
143
|
-
)
|
|
144
|
-
if (statsRef.value) observer.observe(statsRef.value)
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
onUnmounted(() => observer?.disconnect())
|
|
148
|
-
</script>
|
|
149
|
-
|
|
150
|
-
<style scoped>
|
|
151
|
-
.quick-btn:hover {
|
|
152
|
-
border-color: rgba(26,111,196,0.4) !important;
|
|
153
|
-
color: var(--brand-400) !important;
|
|
154
|
-
background: var(--tag-bg) !important;
|
|
155
|
-
}
|
|
156
|
-
</style>
|