vue_zhongyou 1.0.23 → 1.0.25
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/212/237/350/203/275/344/273/243/347/240/201/AI/345/257/271/350/257/235/aiChatPage.vue +479 -186
- package//345/212/237/350/203/275/344/273/243/347/240/201//351/241/265/351/235/242/345/223/215/345/272/224/345/274/217/351/200/202/351/205/215/vite.config.js +20 -0
- package//346/217/222/344/273/266/mkcert-v1.4.4-linux-amd64 +0 -0
package/package.json
CHANGED
package//345/212/237/350/203/275/344/273/243/347/240/201/AI/345/257/271/350/257/235/aiChatPage.vue
CHANGED
|
@@ -30,10 +30,13 @@
|
|
|
30
30
|
<!-- 文字介绍 -->
|
|
31
31
|
<div v-if="message.output" class="message-output" v-html="formatMessage(message.output)"></div>
|
|
32
32
|
<!-- 表单内容 -->
|
|
33
|
-
<
|
|
33
|
+
<DynamicMobileForm
|
|
34
|
+
v-model="message.formData"
|
|
34
35
|
:schema="message.formSchema"
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
submit-button-label="确认"
|
|
37
|
+
reset-button-label="重置"
|
|
38
|
+
@submit="handleSubmit"
|
|
39
|
+
@change="handleChange"
|
|
37
40
|
/>
|
|
38
41
|
</div>
|
|
39
42
|
|
|
@@ -81,7 +84,12 @@
|
|
|
81
84
|
</div>
|
|
82
85
|
|
|
83
86
|
<!-- 输入区域 -->
|
|
84
|
-
<
|
|
87
|
+
<inputCom
|
|
88
|
+
ref="inputComRef"
|
|
89
|
+
:isLoading="isLoading"
|
|
90
|
+
@sendMessage="sendMessage"
|
|
91
|
+
></inputCom>
|
|
92
|
+
<!-- <div class="input-area">
|
|
85
93
|
<div class="input-wrapper">
|
|
86
94
|
<van-field
|
|
87
95
|
v-model="inputText"
|
|
@@ -106,15 +114,41 @@
|
|
|
106
114
|
发送
|
|
107
115
|
</van-button>
|
|
108
116
|
</div>
|
|
109
|
-
</div>
|
|
117
|
+
</div> -->
|
|
110
118
|
</div>
|
|
111
119
|
</template>
|
|
112
120
|
|
|
113
121
|
<script setup>
|
|
114
|
-
import { ref, nextTick, onMounted, watch } from 'vue'
|
|
122
|
+
import { ref, nextTick, onMounted, watch, onUnmounted } from 'vue'
|
|
115
123
|
import { showToast } from 'vant'
|
|
116
124
|
import ChatForm from '@/components/ChatForm.vue'
|
|
117
125
|
import NewChatCardList from '@/components/newChatCardList.vue'
|
|
126
|
+
import DynamicMobileForm from '@/components/dynamicMobileForm.vue'
|
|
127
|
+
import inputCom from '@/components/inputCom.vue'
|
|
128
|
+
import request from '@/api/request'
|
|
129
|
+
// 引入核心库
|
|
130
|
+
import { marked } from 'marked';
|
|
131
|
+
import hljs from 'highlight.js';
|
|
132
|
+
// 引入高亮样式(任选一种)
|
|
133
|
+
import 'highlight.js/styles/github.css';
|
|
134
|
+
|
|
135
|
+
// 配置marked
|
|
136
|
+
marked.setOptions({
|
|
137
|
+
renderer: new marked.Renderer(),
|
|
138
|
+
highlight: function(code, lang) {
|
|
139
|
+
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
|
|
140
|
+
return hljs.highlight(code, { language }).value;
|
|
141
|
+
},
|
|
142
|
+
langPrefix: 'hljs language-',
|
|
143
|
+
pedantic: false,
|
|
144
|
+
gfm: true,
|
|
145
|
+
breaks: true,
|
|
146
|
+
sanitize: false,
|
|
147
|
+
smartLists: true,
|
|
148
|
+
smartypants: false,
|
|
149
|
+
xhtml: false
|
|
150
|
+
});
|
|
151
|
+
|
|
118
152
|
|
|
119
153
|
// 消息列表
|
|
120
154
|
const messages = ref([])
|
|
@@ -126,6 +160,13 @@ const isLoading = ref(false)
|
|
|
126
160
|
const chatContainerRef = ref(null)
|
|
127
161
|
// 会话ID
|
|
128
162
|
const sessionId = ref('')
|
|
163
|
+
// 输入组件引用
|
|
164
|
+
const inputComRef = ref(null)
|
|
165
|
+
// Axios取消请求控制器
|
|
166
|
+
const abortControllerRef = ref(null)
|
|
167
|
+
// 当前正在接收流式响应的消息索引
|
|
168
|
+
const streamingMessageIndex = ref(-1)
|
|
169
|
+
|
|
129
170
|
|
|
130
171
|
// 判断消息类型
|
|
131
172
|
const isTextMessage = (message) => {
|
|
@@ -142,6 +183,7 @@ const isCardListMessage = (message) => {
|
|
|
142
183
|
|
|
143
184
|
// 初始化欢迎消息
|
|
144
185
|
onMounted(() => {
|
|
186
|
+
|
|
145
187
|
addMessage('assistant', '您好!有什么可以帮助您的吗?', false)
|
|
146
188
|
scrollToBottom()
|
|
147
189
|
})
|
|
@@ -190,10 +232,8 @@ const generateSuggestions = (content) => {
|
|
|
190
232
|
// 这里可以根据实际内容生成相关的推荐信息
|
|
191
233
|
// 目前先使用一些示例推荐信息
|
|
192
234
|
const sampleSuggestions = [
|
|
193
|
-
'
|
|
194
|
-
'
|
|
195
|
-
'查看相关文档',
|
|
196
|
-
'获取更多帮助'
|
|
235
|
+
'查找当前待办',
|
|
236
|
+
'发起出差流程',
|
|
197
237
|
]
|
|
198
238
|
|
|
199
239
|
// 根据内容类型生成不同的推荐信息
|
|
@@ -206,9 +246,9 @@ const generateSuggestions = (content) => {
|
|
|
206
246
|
return ['比较不同产品', '查看用户评价', '获取优惠信息']
|
|
207
247
|
}
|
|
208
248
|
} else if (content && content.type === 'form') {
|
|
209
|
-
return ['
|
|
249
|
+
return ['查找当前待办', '查看我发起的流程',]
|
|
210
250
|
} else if (content && content.type === 'cardList') {
|
|
211
|
-
return ['
|
|
251
|
+
return ['创建出差流程', '创建请假流程', '查看待办列表']
|
|
212
252
|
}
|
|
213
253
|
|
|
214
254
|
// 默认推荐信息
|
|
@@ -217,173 +257,227 @@ const generateSuggestions = (content) => {
|
|
|
217
257
|
|
|
218
258
|
// 发送消息
|
|
219
259
|
const sendMessage = async () => {
|
|
220
|
-
const text =
|
|
260
|
+
const text = inputComRef.value.inputText.trim()
|
|
221
261
|
if (!text || isLoading.value) return
|
|
222
262
|
|
|
263
|
+
// 取消之前的请求
|
|
264
|
+
if (abortControllerRef.value) {
|
|
265
|
+
abortControllerRef.value.abort()
|
|
266
|
+
}
|
|
267
|
+
|
|
223
268
|
// 添加用户消息
|
|
224
269
|
addMessage('user', text)
|
|
225
|
-
|
|
270
|
+
inputComRef.value.inputText = ''
|
|
226
271
|
|
|
227
272
|
// 添加AI加载消息
|
|
228
273
|
addMessage('assistant', '', true)
|
|
229
274
|
isLoading.value = true
|
|
230
275
|
|
|
231
|
-
//
|
|
232
|
-
|
|
276
|
+
// 记录当前消息索引
|
|
277
|
+
streamingMessageIndex.value = messages.value.length - 1
|
|
278
|
+
|
|
279
|
+
// 拼装请求体
|
|
280
|
+
const requestBody = {
|
|
233
281
|
sessionId: sessionId.value,
|
|
234
282
|
input: text,
|
|
235
283
|
payload: {
|
|
236
|
-
sceneType
|
|
237
|
-
queryType
|
|
238
|
-
processType
|
|
284
|
+
sceneType: "QUERY",
|
|
285
|
+
queryType: "CREATOR",
|
|
286
|
+
processType: "BUSINESS_TRIP",
|
|
239
287
|
queryBackLogReq: {
|
|
240
288
|
userId: '123456',
|
|
241
|
-
pageNum:1,
|
|
289
|
+
pageNum: 1,
|
|
242
290
|
pageSize: 10
|
|
243
291
|
}
|
|
244
292
|
}
|
|
245
293
|
}
|
|
246
294
|
|
|
295
|
+
// 创建新的AbortController
|
|
296
|
+
abortControllerRef.value = new AbortController()
|
|
297
|
+
|
|
247
298
|
try {
|
|
248
|
-
//
|
|
249
|
-
|
|
299
|
+
// 使用fetch API发送SSE请求
|
|
300
|
+
const baseURL = import.meta.env.VITE_API_BASE_URL || ''
|
|
301
|
+
const url = `http://localhost:8080/ai/chat`
|
|
302
|
+
|
|
303
|
+
const response = await fetch(url, {
|
|
304
|
+
method: 'POST',
|
|
305
|
+
headers: {
|
|
306
|
+
'Content-Type': 'application/json',
|
|
307
|
+
'Accept': 'text/event-stream',
|
|
308
|
+
'Cache-Control': 'no-cache'
|
|
309
|
+
},
|
|
310
|
+
body: JSON.stringify({
|
|
311
|
+
message: text
|
|
312
|
+
}),
|
|
313
|
+
signal: abortControllerRef.value.signal
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
if (!response.ok) {
|
|
317
|
+
throw new Error(`HTTP error! status: ${response.status}`)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// 使用ReadableStream处理流式响应
|
|
321
|
+
const reader = response.body.getReader()
|
|
322
|
+
const decoder = new TextDecoder()
|
|
323
|
+
let buffer = ''
|
|
324
|
+
|
|
325
|
+
while (true) {
|
|
326
|
+
const { done, value } = await reader.read()
|
|
327
|
+
|
|
328
|
+
if (done) {
|
|
329
|
+
console.log('SSE流读取完成')
|
|
330
|
+
break
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 解码数据块
|
|
334
|
+
const chunk = decoder.decode(value, { stream: true })
|
|
335
|
+
console.log(chunk);
|
|
336
|
+
|
|
337
|
+
buffer += chunk
|
|
338
|
+
|
|
339
|
+
// 处理buffer中的完整消息
|
|
340
|
+
const lines = buffer.split('\n')
|
|
341
|
+
buffer = lines.pop() || '' // 保留最后一个不完整的行
|
|
342
|
+
|
|
343
|
+
for (const line of lines) {
|
|
344
|
+
if (line.startsWith('data: ')) {
|
|
345
|
+
const dataStr = line.slice(6).trim()
|
|
346
|
+
if (dataStr) {
|
|
347
|
+
try {
|
|
348
|
+
const data = JSON.parse(dataStr)
|
|
349
|
+
handleSSEMessage(data)
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.error('解析SSE消息失败:', error, dataStr)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 处理buffer中剩余的数据
|
|
359
|
+
if (buffer.trim()) {
|
|
360
|
+
const lines = buffer.split('\n')
|
|
361
|
+
for (const line of lines) {
|
|
362
|
+
if (line.startsWith('data: ')) {
|
|
363
|
+
const dataStr = line.slice(6).trim()
|
|
364
|
+
if (dataStr) {
|
|
365
|
+
try {
|
|
366
|
+
const data = JSON.parse(dataStr)
|
|
367
|
+
handleSSEMessage(data)
|
|
368
|
+
} catch (error) {
|
|
369
|
+
console.error('解析SSE消息失败:', error, dataStr)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// 请求完成
|
|
377
|
+
console.log('SSE请求完成')
|
|
250
378
|
} catch (error) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
379
|
+
if (error.name === 'AbortError') {
|
|
380
|
+
console.log('请求已取消')
|
|
381
|
+
} else {
|
|
382
|
+
console.error('SSE请求失败:', error)
|
|
383
|
+
handleSSEError('连接失败,请重试')
|
|
384
|
+
}
|
|
257
385
|
}
|
|
258
386
|
}
|
|
259
387
|
|
|
260
|
-
//
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
//
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
response = '我可以帮您解答问题、提供建议、分析数据等。请告诉我您需要什么帮助?'
|
|
274
|
-
} else if (userMessage.includes('时间')) {
|
|
275
|
-
response = `当前时间是:${new Date().toLocaleString('zh-CN')}`
|
|
276
|
-
} else if (userMessage.includes('表单')) {
|
|
277
|
-
// 返回表单类型消息,带介绍文字
|
|
278
|
-
response = {
|
|
279
|
-
type: 'form',
|
|
280
|
-
output: '请您填写以下信息,以便我们为您提供更好的服务。所有信息都将严格保密,请放心填写。',
|
|
281
|
-
schema: [
|
|
282
|
-
{
|
|
283
|
-
field: 'name',
|
|
284
|
-
label: '姓名',
|
|
285
|
-
type: 'input',
|
|
286
|
-
placeholder: '请输入您的姓名',
|
|
287
|
-
rules: [{ required: true, message: '请输入姓名' }]
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
field: 'email',
|
|
291
|
-
label: '邮箱',
|
|
292
|
-
type: 'input',
|
|
293
|
-
placeholder: '请输入您的邮箱',
|
|
294
|
-
rules: [{ required: true, message: '请输入邮箱' }, { pattern: /\w+@\w+\.\w+/, message: '邮箱格式不正确' }]
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
field: 'age',
|
|
298
|
-
label: '年龄',
|
|
299
|
-
type: 'input',
|
|
300
|
-
placeholder: '请输入您的年龄'
|
|
301
|
-
},
|
|
302
|
-
{
|
|
303
|
-
field: 'gender',
|
|
304
|
-
label: '性别',
|
|
305
|
-
type: 'radio',
|
|
306
|
-
options: [
|
|
307
|
-
{ label: '男', value: 'male' },
|
|
308
|
-
{ label: '女', value: 'female' }
|
|
309
|
-
]
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
field: 'hobbies',
|
|
313
|
-
label: '爱好',
|
|
314
|
-
type: 'checkbox',
|
|
315
|
-
options: [
|
|
316
|
-
{ label: '阅读', value: 'reading' },
|
|
317
|
-
{ label: '运动', value: 'sports' },
|
|
318
|
-
{ label: '音乐', value: 'music' },
|
|
319
|
-
{ label: '旅行', value: 'travel' }
|
|
320
|
-
]
|
|
321
|
-
},
|
|
322
|
-
{
|
|
323
|
-
field: 'bio',
|
|
324
|
-
label: '个人简介',
|
|
325
|
-
type: 'textarea',
|
|
326
|
-
placeholder: '请简单介绍一下自己',
|
|
327
|
-
rows: 3
|
|
328
|
-
}
|
|
329
|
-
]
|
|
388
|
+
// 处理SSE消息
|
|
389
|
+
const handleSSEMessage = (data) => {
|
|
390
|
+
|
|
391
|
+
const messageIndex = streamingMessageIndex.value
|
|
392
|
+
|
|
393
|
+
if (messageIndex === -1 || !messages.value[messageIndex]) return
|
|
394
|
+
|
|
395
|
+
const currentMessage = messages.value[messageIndex]
|
|
396
|
+
// 根据消息类型处理
|
|
397
|
+
if (data.type === 'text' || data.type === 'message') {
|
|
398
|
+
// 文本流式更新
|
|
399
|
+
if (typeof currentMessage.content !== 'string') {
|
|
400
|
+
currentMessage.content = ''
|
|
330
401
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
{
|
|
354
|
-
title: '信息科技部xxxaaaa啊啊啊你还是低哦是大家扫IDiOS滴哦啊啊大家送啊点喝酒哦洒几滴撒娇都i爱睡觉',
|
|
355
|
-
subtitle: '人工智能平台',
|
|
356
|
-
statusName : '待审',
|
|
357
|
-
creator : '刘松煜',
|
|
358
|
-
createTime : '2023-10-01',
|
|
359
|
-
currentNode : '节点1',
|
|
360
|
-
dpName : '信息科技部',
|
|
361
|
-
}
|
|
362
|
-
];
|
|
363
|
-
const total = 10
|
|
402
|
+
currentMessage.content += data.content || ''
|
|
403
|
+
scrollToBottom()
|
|
404
|
+
} else if (data.type === 'form') {
|
|
405
|
+
// 表单消息
|
|
406
|
+
currentMessage.type = 'form'
|
|
407
|
+
currentMessage.formSchema = data.schema
|
|
408
|
+
currentMessage.formData = data.data || {}
|
|
409
|
+
if (data.output) {
|
|
410
|
+
currentMessage.output = data.output
|
|
411
|
+
}
|
|
412
|
+
} else if (data.type === 'cardList') {
|
|
413
|
+
// 列表卡片消息
|
|
414
|
+
for (let key in data) {
|
|
415
|
+
currentMessage[key] = data[key]
|
|
416
|
+
}
|
|
417
|
+
} else if (data.type === 'end') {
|
|
418
|
+
// 流式传输完成
|
|
419
|
+
currentMessage.loading = false
|
|
420
|
+
isLoading.value = false
|
|
421
|
+
|
|
422
|
+
// 为AI助手消息添加推荐信息
|
|
423
|
+
currentMessage.suggestions = generateSuggestions(currentMessage.content)
|
|
364
424
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
showLoadMore: items.length < total, // 根据当前items长度与总数的比较来确定是否显示"查看更多"
|
|
369
|
-
currentPage: 1,
|
|
370
|
-
pageSize: 10,
|
|
371
|
-
total: total,
|
|
372
|
-
items: items,
|
|
373
|
-
loading: false // 初始化加载状态
|
|
425
|
+
// 清理请求控制器
|
|
426
|
+
if (abortControllerRef.value) {
|
|
427
|
+
abortControllerRef.value = null
|
|
374
428
|
}
|
|
375
|
-
|
|
376
|
-
|
|
429
|
+
streamingMessageIndex.value = -1
|
|
430
|
+
} else if (data.type === 'error') {
|
|
431
|
+
// 错误消息
|
|
432
|
+
handleSSEError(data.message || '服务器错误')
|
|
377
433
|
}
|
|
434
|
+
console.log(messages.value);
|
|
435
|
+
|
|
436
|
+
}
|
|
378
437
|
|
|
379
|
-
|
|
380
|
-
|
|
438
|
+
// 处理SSE错误
|
|
439
|
+
const handleSSEError = (errorMessage = '连接中断,请重试') => {
|
|
440
|
+
console.error('SSE错误:', errorMessage)
|
|
441
|
+
|
|
442
|
+
const messageIndex = streamingMessageIndex.value
|
|
443
|
+
if (messageIndex !== -1 && messages.value[messageIndex]) {
|
|
444
|
+
messages.value[messageIndex].loading = false
|
|
445
|
+
if (!messages.value[messageIndex].content) {
|
|
446
|
+
messages.value[messageIndex].content = errorMessage
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
isLoading.value = false
|
|
451
|
+
showToast(errorMessage)
|
|
452
|
+
|
|
453
|
+
// 清理请求控制器
|
|
454
|
+
if (abortControllerRef.value) {
|
|
455
|
+
abortControllerRef.value = null
|
|
456
|
+
}
|
|
457
|
+
streamingMessageIndex.value = -1
|
|
381
458
|
}
|
|
382
459
|
|
|
383
|
-
//
|
|
460
|
+
// 组件卸载时清理SSE连接
|
|
461
|
+
onUnmounted(() => {
|
|
462
|
+
if (abortControllerRef.value) {
|
|
463
|
+
abortControllerRef.value.abort()
|
|
464
|
+
abortControllerRef.value = null
|
|
465
|
+
}
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
// 格式化消息内容(支持Markdown)
|
|
384
469
|
const formatMessage = (content) => {
|
|
385
470
|
if (!content) return ''
|
|
386
|
-
|
|
471
|
+
|
|
472
|
+
try {
|
|
473
|
+
// 使用marked将Markdown转换为HTML
|
|
474
|
+
const html = marked(content)
|
|
475
|
+
return html
|
|
476
|
+
} catch (error) {
|
|
477
|
+
console.error('Markdown转换失败:', error)
|
|
478
|
+
// 如果转换失败,回退到简单的换行处理
|
|
479
|
+
return content.replace(/\n/g, '<br>')
|
|
480
|
+
}
|
|
387
481
|
}
|
|
388
482
|
|
|
389
483
|
// 格式化时间
|
|
@@ -440,7 +534,7 @@ const handleShiftEnter = () => {
|
|
|
440
534
|
}
|
|
441
535
|
|
|
442
536
|
// 处理表单提交
|
|
443
|
-
const
|
|
537
|
+
const handleSubmit = (formData) => {
|
|
444
538
|
// 在实际应用中,这里应该将表单数据发送到服务器
|
|
445
539
|
console.log('表单提交数据:', formData)
|
|
446
540
|
|
|
@@ -463,7 +557,7 @@ const handleCardActionClick = (index, { action, item, index: itemIndex }) => {
|
|
|
463
557
|
const handleSuggestionClick = (suggestion) => {
|
|
464
558
|
console.log('Suggestion clicked:', suggestion)
|
|
465
559
|
// 将推荐信息作为用户消息发送
|
|
466
|
-
|
|
560
|
+
inputComRef.value.inputText = suggestion
|
|
467
561
|
sendMessage()
|
|
468
562
|
}
|
|
469
563
|
|
|
@@ -612,6 +706,7 @@ const handleLoadMore = (message) => {
|
|
|
612
706
|
display: flex;
|
|
613
707
|
flex-direction: column;
|
|
614
708
|
// max-width: 75%;
|
|
709
|
+
flex: 1;
|
|
615
710
|
gap: 6px;
|
|
616
711
|
}
|
|
617
712
|
|
|
@@ -625,6 +720,190 @@ const handleLoadMore = (message) => {
|
|
|
625
720
|
.message-text {
|
|
626
721
|
font-size: 15px;
|
|
627
722
|
white-space: pre-wrap;
|
|
723
|
+
|
|
724
|
+
// Markdown样式隔离 - 使用深度选择器控制所有Markdown元素
|
|
725
|
+
:deep(*) {
|
|
726
|
+
margin: 0;
|
|
727
|
+
padding: 0;
|
|
728
|
+
box-sizing: border-box;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// 标题样式
|
|
732
|
+
:deep(h1), :deep(h2), :deep(h3), :deep(h4), :deep(h5), :deep(h6) {
|
|
733
|
+
margin: 0.5em 0 0.3em 0;
|
|
734
|
+
font-weight: 600;
|
|
735
|
+
line-height: 1.4;
|
|
736
|
+
color: inherit;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
:deep(h1) {
|
|
740
|
+
font-size: 1.5em;
|
|
741
|
+
border-bottom: 1px solid #eaecef;
|
|
742
|
+
padding-bottom: 0.3em;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
:deep(h2) {
|
|
746
|
+
font-size: 1.3em;
|
|
747
|
+
border-bottom: 1px solid #eaecef;
|
|
748
|
+
padding-bottom: 0.3em;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
:deep(h3) {
|
|
752
|
+
font-size: 1.15em;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
:deep(h4) {
|
|
756
|
+
font-size: 1.05em;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// 段落样式
|
|
760
|
+
:deep(p) {
|
|
761
|
+
margin: 0.3em 0;
|
|
762
|
+
line-height: 1.6;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// 列表样式
|
|
766
|
+
:deep(ul), :deep(ol) {
|
|
767
|
+
margin: 0.5em 0;
|
|
768
|
+
padding-left: 1.5em;
|
|
769
|
+
list-style-position: outside;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
:deep(ul) {
|
|
773
|
+
list-style-type: disc;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
:deep(ol) {
|
|
777
|
+
list-style-type: decimal;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
:deep(li) {
|
|
781
|
+
margin: 0.25em 0;
|
|
782
|
+
line-height: 1.6;
|
|
783
|
+
padding-left: 0.3em;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
:deep(ul ul), :deep(ol ul), :deep(ul ol), :deep(ol ol) {
|
|
787
|
+
margin: 0.25em 0;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// 嵌套列表样式
|
|
791
|
+
:deep(ul ul) {
|
|
792
|
+
list-style-type: circle;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
:deep(ul ul ul) {
|
|
796
|
+
list-style-type: square;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// 代码样式
|
|
800
|
+
:deep(code) {
|
|
801
|
+
font-family: 'Courier New', Courier, monospace;
|
|
802
|
+
background-color: rgba(0, 0, 0, 0.06);
|
|
803
|
+
padding: 0.2em 0.4em;
|
|
804
|
+
border-radius: 3px;
|
|
805
|
+
font-size: 0.9em;
|
|
806
|
+
color: #e83e8c;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
:deep(pre) {
|
|
810
|
+
background-color: #f6f8fa;
|
|
811
|
+
border: 1px solid #e1e4e8;
|
|
812
|
+
border-radius: 6px;
|
|
813
|
+
padding: 12px;
|
|
814
|
+
margin: 0.5em 0;
|
|
815
|
+
overflow-x: auto;
|
|
816
|
+
font-size: 0.9em;
|
|
817
|
+
line-height: 1.5;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
:deep(pre code) {
|
|
821
|
+
background-color: transparent;
|
|
822
|
+
padding: 0;
|
|
823
|
+
border-radius: 0;
|
|
824
|
+
font-size: inherit;
|
|
825
|
+
color: inherit;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// 引用样式
|
|
829
|
+
:deep(blockquote) {
|
|
830
|
+
margin: 0.5em 0;
|
|
831
|
+
padding: 0.5em 1em;
|
|
832
|
+
border-left: 4px solid #dfe2e5;
|
|
833
|
+
background-color: #f6f8fa;
|
|
834
|
+
color: #6a737d;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// 表格样式
|
|
838
|
+
:deep(table) {
|
|
839
|
+
border-collapse: collapse;
|
|
840
|
+
width: 100%;
|
|
841
|
+
margin: 0.5em 0;
|
|
842
|
+
font-size: 0.95em;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
:deep(table th), :deep(table td) {
|
|
846
|
+
border: 1px solid #dfe2e5;
|
|
847
|
+
padding: 6px 12px;
|
|
848
|
+
text-align: left;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
:deep(table th) {
|
|
852
|
+
background-color: #f6f8fa;
|
|
853
|
+
font-weight: 600;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
:deep(table tr:nth-child(even)) {
|
|
857
|
+
background-color: #fafbfc;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// 链接样式
|
|
861
|
+
:deep(a) {
|
|
862
|
+
color: #1989fa;
|
|
863
|
+
text-decoration: none;
|
|
864
|
+
border-bottom: 1px solid transparent;
|
|
865
|
+
transition: border-bottom-color 0.2s;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
:deep(a:hover) {
|
|
869
|
+
border-bottom-color: #1989fa;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// 图片样式
|
|
873
|
+
:deep(img) {
|
|
874
|
+
max-width: 100%;
|
|
875
|
+
height: auto;
|
|
876
|
+
border-radius: 4px;
|
|
877
|
+
margin: 0.5em 0;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// 分隔线样式
|
|
881
|
+
:deep(hr) {
|
|
882
|
+
border: none;
|
|
883
|
+
border-top: 2px solid #e1e4e8;
|
|
884
|
+
margin: 1em 0;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// 强调样式
|
|
888
|
+
:deep(strong), :deep(b) {
|
|
889
|
+
font-weight: 600;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
:deep(em), :deep(i) {
|
|
893
|
+
font-style: italic;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// 删除线样式
|
|
897
|
+
:deep(del), :deep(s) {
|
|
898
|
+
text-decoration: line-through;
|
|
899
|
+
color: #6a737d;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// 任务列表样式
|
|
903
|
+
:deep(input[type="checkbox"]) {
|
|
904
|
+
margin-right: 0.5em;
|
|
905
|
+
vertical-align: middle;
|
|
906
|
+
}
|
|
628
907
|
}
|
|
629
908
|
|
|
630
909
|
.message-output {
|
|
@@ -636,6 +915,57 @@ const handleLoadMore = (message) => {
|
|
|
636
915
|
background-color: #f8f9fa;
|
|
637
916
|
border-radius: 8px;
|
|
638
917
|
// border-left: 3px solid #1989fa;
|
|
918
|
+
|
|
919
|
+
// Markdown样式隔离
|
|
920
|
+
:deep(*) {
|
|
921
|
+
margin: 0;
|
|
922
|
+
padding: 0;
|
|
923
|
+
box-sizing: border-box;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
:deep(p) {
|
|
927
|
+
margin: 0.3em 0;
|
|
928
|
+
line-height: 1.6;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
:deep(ul), :deep(ol) {
|
|
932
|
+
margin: 0.5em 0;
|
|
933
|
+
padding-left: 1.5em;
|
|
934
|
+
list-style-position: outside;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
:deep(ul) {
|
|
938
|
+
list-style-type: disc;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
:deep(ol) {
|
|
942
|
+
list-style-type: decimal;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
:deep(li) {
|
|
946
|
+
margin: 0.25em 0;
|
|
947
|
+
line-height: 1.6;
|
|
948
|
+
padding-left: 0.3em;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
:deep(code) {
|
|
952
|
+
font-family: 'Courier New', Courier, monospace;
|
|
953
|
+
background-color: rgba(0, 0, 0, 0.06);
|
|
954
|
+
padding: 0.2em 0.4em;
|
|
955
|
+
border-radius: 3px;
|
|
956
|
+
font-size: 0.9em;
|
|
957
|
+
color: #e83e8c;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
:deep(a) {
|
|
961
|
+
color: #1989fa;
|
|
962
|
+
text-decoration: none;
|
|
963
|
+
border-bottom: 1px solid transparent;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
:deep(a:hover) {
|
|
967
|
+
border-bottom-color: #1989fa;
|
|
968
|
+
}
|
|
639
969
|
}
|
|
640
970
|
|
|
641
971
|
.message-time {
|
|
@@ -688,45 +1018,7 @@ const handleLoadMore = (message) => {
|
|
|
688
1018
|
}
|
|
689
1019
|
}
|
|
690
1020
|
|
|
691
|
-
.input-area {
|
|
692
|
-
position: fixed;
|
|
693
|
-
bottom: 0;
|
|
694
|
-
left: 0;
|
|
695
|
-
right: 0;
|
|
696
|
-
background: #fff;
|
|
697
|
-
border-top: 1px solid #ebedf0;
|
|
698
|
-
padding: 8px 12px;
|
|
699
|
-
padding-bottom: calc(8px + env(safe-area-inset-bottom));
|
|
700
|
-
z-index: 100;
|
|
701
|
-
|
|
702
|
-
.input-wrapper {
|
|
703
|
-
display: flex;
|
|
704
|
-
align-items: flex-end;
|
|
705
|
-
gap: 8px;
|
|
706
|
-
max-width: 100%;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
.chat-input {
|
|
710
|
-
flex: 1;
|
|
711
|
-
background: #f7f8fa;
|
|
712
|
-
border-radius: 20px;
|
|
713
|
-
padding: 8px 12px;
|
|
714
|
-
min-height: 40px;
|
|
715
|
-
max-height: 120px;
|
|
716
1021
|
|
|
717
|
-
:deep(.van-field__control) {
|
|
718
|
-
font-size: 15px;
|
|
719
|
-
line-height: 1.5;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
.send-btn {
|
|
724
|
-
flex-shrink: 0;
|
|
725
|
-
height: 40px;
|
|
726
|
-
padding: 0 20px;
|
|
727
|
-
border-radius: 20px;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
1022
|
|
|
731
1023
|
|
|
732
1024
|
/* 推荐信息样式 */
|
|
@@ -745,6 +1037,7 @@ const handleLoadMore = (message) => {
|
|
|
745
1037
|
.suggestion-tag {
|
|
746
1038
|
margin-right: 8px;
|
|
747
1039
|
margin-bottom: 8px;
|
|
1040
|
+
padding: 4px;
|
|
748
1041
|
background-color: #f0f0f0;
|
|
749
1042
|
border-color: #ddd;
|
|
750
1043
|
color: #666;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import { createVuePlugin } from 'vite-plugin-vue2'
|
|
3
|
+
import px2rem from 'postcss-pxtorem'
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [
|
|
7
|
+
createVuePlugin()
|
|
8
|
+
],
|
|
9
|
+
css: {
|
|
10
|
+
postcss: {
|
|
11
|
+
plugins: [
|
|
12
|
+
px2rem({
|
|
13
|
+
rootValue: 16, // 设计稿为375px时,rootValue设为16(16px * 375/375 = 16px)
|
|
14
|
+
propList: ['*'],
|
|
15
|
+
minPixelValue: 1,
|
|
16
|
+
})
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
})
|
|
Binary file
|