@@ -45,7 +45,7 @@
>
< view v-if = "!currentUser.userId" class="notice-card" >
< view class = "notice-title" > 请先登录后使用 AI 助手 < / view >
< view class = "notice-desc" > 当前页面已接入真实会话逻辑 , 发送消息和历史会话 依赖登录态 。 < / view >
< view class = "notice-desc" > 当前页面已接入真实会话逻辑 , 发送消息和历史记录 依赖登录态 。 < / view >
< / view >
< view v-else-if = "!messageList.length" class="empty-card" >
@@ -262,38 +262,91 @@
< view class = "history-panel" @tap.stop >
< view class = "history-head" >
< view >
< view class = "history-title" > 历史会话 < / view >
< view class = "history-subtitle" > 按最近 更新时间展示 < / view >
< view class = "history-title" > 历史记录 < / view >
< view class = "history-subtitle" > 仅展示最近 30 条 , 按更新时间排序 < / view >
< / view >
< view class = "history-close" @tap ="closeHistoryPanel" > 关闭 < / view >
< / view >
< ! - - 类型筛选 : 默认 「 全部 」 , 前端过滤 , 不额外请求 - - >
< scroll-view scroll -x :show-scrollbar = "false" class = "history-filter" >
< view class = "history-filter-track" >
< view
v-for = "option in historyTypeOptions"
:key = "option.value"
class = "history-filter-chip"
: class = "{ 'history-filter-chip--active': historyTypeFilter === option.value }"
@tap ="pickHistoryType(option.value)"
> { { option . label } } < / view >
< / view >
< / scroll-view >
< scroll-view scroll -y class = "history-scroll" >
< view v-if = "historySections.length" >
< view v-for = "section in historySections" :key="section.label" class="history-section" >
< view class = "history-label" > { { section . label } } < / view >
< view
v-for = "item in section.items"
:key = "getH istoryKey( item) "
class = "history-item"
: class = "{ 'history-item--active': activeHistoryKey === getHistoryKey(item) }"
>
< view class = "history-item-main" @tap ="selectChat(item) ">
< view class = "history-item-title" > { { getHistoryTitle ( item ) } } < / view >
< view class = "history-item-time" >
{ { formatHistoryItemTime ( item . createTime ) } }
< view v-for = "item in section.items" :key="getHistoryKey(item)" class="history-item-wrap" >
< view
class = "h istory- item"
: class = "{ ' history-item--active': activeHistoryKey === getHistoryKey(item) } "
>
< view class = "history-item-main" @tap ="selectChat(item)" >
< view class = "history-item-head " >
< text
class = "history-type-badge"
:class = "`history-type-badge--${getHistoryType(item)}`"
> { { getHistoryTypeLabel ( item ) } } < / text >
< view class = "history-item-title" > { { getHistoryTitle ( item ) } } < / view >
< / view >
< view class = "history-item-time" >
{ { formatHistoryItemTime ( item . createTime ) } }
< / view >
< / view >
< view
class = "history-delete"
@tap.stop ="removeChat(item)"
>
{ { deletingChatId === item . id ? '删除中' : '删除' } }
< / view >
< / view >
< view
class = "history-delete"
@tap.stop ="removeChat(item)"
>
{ { deletingChatId === item . id ? '删除中' : '删除' } }
<!--
非聊天记录在面板内展开查看 : 写作 / 图片可 「 按原参数再来一次 」 ,
创意 / 活动策划只读 ( 契约里没有可回填的界面参数 ) 。
-- >
< view v-if = "isHistoryPreview(item)" class="history-preview" @tap.stop >
< view class = "history-preview-head" >
< text class = "history-preview-caption" > 记录详情 < / text >
< text class = "history-preview-close" @tap.stop ="closeHistoryPreview" > 收起 < / text >
< / view >
< view v-if = "historyPreview.loading" class="history-preview-hint" > 正在加载记录 … < / view >
< template v-else >
< view
v-for = "message in historyPreview.messages"
:key = "message.id"
class = "history-preview-row"
>
< view class = "history-preview-role" > { { isUserMessage ( message ) ? '你' : 'AI' } } < / view >
< view v-if = "isUserMessage(message)" class="history-preview-text" > {{ message.content }} < / view >
< zero -markdown -view
v-else
class = "history-preview-text history-preview-markdown"
:markdown = "getAssistantDisplayContent(message.content)"
/ >
< / view >
< view v-if = "canReplayHistory(item)" class="history-preview-actions" >
< view class = "history-preview-replay" @tap.stop ="replayHistoryItem(item)" > 按原参数再来一次 < / view >
< text class = "history-preview-tip" > 不会自动发送 , 回填后可修改 < / text >
< / view >
< view v-else class = "history-preview-actions" >
< text class = "history-preview-tip" > 该记录仅可查看 , 暂不支持再来一次 < / text >
< / view >
< / template >
< / view >
< / view >
< / view >
< / view >
< view v-else class = "history-empty" > 暂无历史对话 < / view >
< view v-else class = "history-empty" > 暂无历史记录 < / view >
< / scroll-view >
< / view >
< / view >
@@ -334,6 +387,36 @@
{ label : '写作' , value : 'writing' } ,
{ label : '图片' , value : 'image' }
]
/*
历史记录的类型(后端 chat_type)。旧数据没有 chat_type,按 chat 处理。
「全部」只用于筛选,不是一种记录类型。
*/
const HISTORY _TYPE _OPTIONS = [
{ label : '全部' , value : 'all' } ,
{ label : '聊天' , value : 'chat' } ,
{ label : '写作' , value : 'writing' } ,
{ label : '图片' , value : 'image' } ,
{ label : '创意' , value : 'creative' } ,
{ label : '活动策划' , value : 'activity_outline' }
]
const HISTORY _TYPE _META = {
chat : { label : '聊天' , replayable : false } ,
writing : { label : '写作' , replayable : true } ,
image : { label : '图片' , replayable : true } ,
creative : { label : '创意' , replayable : false } ,
activity _outline : { label : '活动策划' , replayable : false }
}
/* 独立页 switchTab 不能带参,类型经 storage 传进来(见接口契约 §6) */
const HISTORY _TYPE _STORAGE _KEY = 'ai_history_type'
/** 每个模式各自的工作区:消息、当前记录、草稿互不干扰 */
const createWorkspace = ( ) => ( {
messageList : [ ] ,
activeChatId : null ,
activeConversationId : '' ,
streamingMessageId : '' ,
draft : ''
} )
// 图片比例选择器已撤掉:上游创意助手开始节点没有比例变量(实测只有 user_content /
// user_files),前端只能把它拼进 user_content —— 那是替上游写 prompt。
// 要恢复这个功能,先在上游开始节点加变量,再按变量契约接上来。
@@ -362,8 +445,13 @@
participants : ''
} ,
statusBarHeight : 0 ,
draft : '' ,
activeChatMode : 'chat' ,
// 三个模式各自独立的工作区:切换模式不丢消息、当前记录与草稿
workspaces : {
chat : createWorkspace ( ) ,
writing : createWorkspace ( ) ,
image : createWorkspace ( )
} ,
loading : false ,
wsConnected : false ,
scrollIntoView : '' ,
@@ -374,11 +462,12 @@
'优化竞赛申报书摘要' ,
'整理五四评优申报材料清单'
] ,
messageList : [ ] ,
historyList : [ ] ,
activeChatId : null ,
activeConversationId : '' ,
streamingMessageId : '' ,
// 历史面板的类型筛选,默认「全部」
historyTypeFilter : 'all ' ,
historyTypeOptions : HISTORY _TYPE _OPTIONS ,
// 面板内展开的非聊天记录详情(含写作 / 图片的「再来一次」)
historyPreview : null ,
showHistoryPanel : false ,
deletingChatId : null ,
currentUser : {
@@ -394,6 +483,50 @@
}
} ,
computed : {
/** 当前模式的工作区;模板里的 messageList / draft 等都指向它 */
workspace ( ) {
return this . workspaces [ this . activeChatMode ] || this . workspaces . chat
} ,
messageList : {
get ( ) {
return this . workspace . messageList
} ,
set ( value ) {
this . workspace . messageList = value
}
} ,
draft : {
get ( ) {
return this . workspace . draft
} ,
set ( value ) {
this . workspace . draft = value
}
} ,
activeChatId : {
get ( ) {
return this . workspace . activeChatId
} ,
set ( value ) {
this . workspace . activeChatId = value
}
} ,
activeConversationId : {
get ( ) {
return this . workspace . activeConversationId
} ,
set ( value ) {
this . workspace . activeConversationId = value
}
} ,
streamingMessageId : {
get ( ) {
return this . workspace . streamingMessageId
} ,
set ( value ) {
this . workspace . streamingMessageId = value
}
} ,
navbarTotalHeight ( ) {
return this . statusBarHeight + 44
} ,
@@ -472,6 +605,9 @@
return 'AI 助手待命中'
} ,
activeHistoryKey ( ) {
if ( this . historyPreview && this . historyPreview . key ) {
return this . historyPreview . key
}
if ( this . activeChatId ) {
return ` chat- ${ this . activeChatId } `
}
@@ -491,9 +627,18 @@
)
return target ? this . getHistoryTitle ( target ) : '当前会话'
} ,
/** 类型筛选之后的历史记录(全部类型时不复制数组) */
historyFilteredList ( ) {
if ( this . historyTypeFilter === 'all' ) {
return this . historyList
}
return this . historyList . filter (
( item ) => this . getHistoryType ( item ) === this . historyTypeFilter
)
} ,
historySections ( ) {
const groupMap = { }
; [ ... this . historyList ]
; [ ... this . historyFilteredList ]
. sort ( ( left , right ) => this . getTimestamp ( right . updateTime || right . createTime ) - this . getTimestamp ( left . updateTime || left . createTime ) )
. forEach ( ( item ) => {
const label = this . getHistoryDateLabel ( item . createTime || item . updateTime )
@@ -502,10 +647,6 @@
}
groupMap [ label ] . push ( item )
} )
console . log ( Object . keys ( groupMap ) . map ( ( label ) => ( {
label ,
items : groupMap [ label ]
} ) ) )
return Object . keys ( groupMap ) . map ( ( label ) => ( {
label ,
items : groupMap [ label ]
@@ -518,13 +659,15 @@
} ,
onShow ( ) {
this . refreshUser ( )
// 独立页经 storage 传来的类型:预置筛选并直接打开历史面板
const fromHistoryEntry = this . applyPendingHistoryType ( )
if ( ! this . initialized ) {
this . initAssistant ( )
this . initAssistant ( ! fromHistoryEntry )
return
}
if ( this . currentUser . userId ) {
this . loadHistoryList ( false ) . catch ( ( error ) => {
this . showError ( error , '刷新历史会话 失败' )
this . showError ( error , '刷新历史记录 失败' )
} )
}
} ,
@@ -542,19 +685,45 @@
}
return this . currentUser
} ,
async initAssistant ( ) {
async initAssistant ( autoSelect = true ) {
try {
this . refreshUser ( )
if ( ! this . currentUser . userId ) {
return
}
await this . loadHistoryList ( true )
await this . loadHistoryList ( autoSelect )
} catch ( error ) {
this . showError ( error , '初始化聊天页失败' )
} finally {
this . initialized = true
}
} ,
/**
* 读取独立页写入的类型筛选。
*
* 小程序 `switchTab` 不能带参,所以「文稿 / 创意 / 活动策划」页先
* `setStorageSync('ai_history_type', ...)` 再跳过来;这里读完立即清掉,
* 避免下次从 tabBar 进来又莫名带上筛选。
*
* @returns {boolean} 是否由独立页的历史入口跳转而来
*/
applyPendingHistoryType ( ) {
let pendingType = ''
try {
pendingType = uni . getStorageSync ( HISTORY _TYPE _STORAGE _KEY )
} catch ( error ) {
pendingType = ''
}
if ( ! pendingType ) {
return false
}
uni . removeStorageSync ( HISTORY _TYPE _STORAGE _KEY )
const matched = Object . keys ( HISTORY _TYPE _META ) . indexOf ( pendingType ) > - 1
this . historyTypeFilter = matched ? pendingType : 'all'
this . historyPreview = null
this . showHistoryPanel = true
return true
} ,
showToast ( title ) {
uni . showToast ( {
title ,
@@ -649,6 +818,33 @@
}
return '未命名对话'
} ,
/** 记录类型;旧数据没有 chatType,按 chat 处理 */
getHistoryType ( item ) {
const value = String ( ( item && item . chatType ) || 'chat' ) . trim ( )
return HISTORY _TYPE _META [ value ] ? value : 'chat'
} ,
getHistoryTypeLabel ( item ) {
const type = this . getHistoryType ( item )
return ( HISTORY _TYPE _META [ type ] && HISTORY _TYPE _META [ type ] . label ) || '聊天'
} ,
/** 只有写作 / 图片记录能回填原参数「再来一次」 */
canReplayHistory ( item ) {
const type = this . getHistoryType ( item )
return ! ! ( HISTORY _TYPE _META [ type ] && HISTORY _TYPE _META [ type ] . replayable )
} ,
pickHistoryType ( value ) {
if ( this . historyTypeFilter === value ) {
return
}
this . historyTypeFilter = value
this . historyPreview = null
} ,
isHistoryPreview ( item ) {
return ! ! this . historyPreview && this . historyPreview . key === this . getHistoryKey ( item )
} ,
closeHistoryPreview ( ) {
this . historyPreview = null
} ,
parseAssistantContent ( value ) {
if ( ! value || value === ASSISTANT _PENDING _TEXT ) {
return {
@@ -737,6 +933,8 @@
return
}
this . activeChatMode = mode
// 切回某个模式时,该模式自己的消息区滚到底部
this . scrollToBottom ( )
} ,
scrollToBottom ( ) {
this . bottomAnchorId = ` chat-bottom- ${ Date . now ( ) } `
@@ -806,10 +1004,19 @@
target . content === ASSISTANT _PENDING _TEXT ? answer : ` ${ target . content } ${ answer } `
this . scrollToBottom ( )
} ,
async finishAssistantMessage ( conversationId ) {
/**
* 聊天收尾:刷新历史并认领后端回传的记录 id。
*
* @param {string} [conversationId] 上游会话标识
* @param {number|string} [chatId] `__END__` 帧新增的记录 id(仅成功写入时出现)
*/
async finishAssistantMessage ( conversationId , chatId ) {
if ( conversationId ) {
this . activeConversationId = conversationId
}
if ( chatId !== undefined && chatId !== null && chatId !== '' ) {
this . activeChatId = chatId
}
const target = this . messageList . find ( ( item ) => item . id === this . streamingMessageId )
if ( target ) {
target . loading = false
@@ -846,7 +1053,8 @@
try {
const data = JSON . parse ( raw )
if ( data && data . answer === '__END__' ) {
await this . finishAssistantMessage ( data . conversationId )
// 末帧带后端刚写入的记录 id(失败态没有);带上它才能高亮对应记录
await this . finishAssistantMessage ( data . conversationId , data . chatId )
this . disconnectWebSocket ( )
return
}
@@ -963,40 +1171,186 @@
! this . activeConversationId &&
this . historyList . length
) {
await this . selectChat ( this . historyList [ 0 ] , true )
// 只自动打开聊天记录:其他类型没有会话,进面板看详情即可
const firstChat = this . historyList . find (
( item ) => this . getHistoryType ( item ) === 'chat'
)
if ( firstChat ) {
await this . selectChat ( firstChat , true )
}
}
} ,
async loadChatHistory ( item ) {
async fetchHistoryMessages ( item ) {
const { userId } = this . refreshUser ( )
const response = await listAiChatHistory ( {
chatId : item . id ,
conversationId : item . conversationId ,
userId
} )
this . messageList = this . formatHistoryMessages ( Array . isArray ( response ) ? response : [ ] )
return this . formatHistoryMessages ( Array . isArray ( response ) ? response : [ ] )
} ,
async loadChatHistory ( item ) {
this . messageList = await this . fetchHistoryMessages ( item )
this . scrollToBottom ( )
} ,
async selectChat ( item , silent = false ) {
if ( this . loading && ! silent ) {
this . showToast ( '当前正在生成回复,请稍后再切换会话 ' )
this . showToast ( '当前正在生成回复,请稍后再切换记录 ' )
return
}
// 聊天记录打开会话;其余类型在面板内展开详情
if ( this . getHistoryType ( item ) !== 'chat' ) {
await this . openHistoryPreview ( item )
return
}
this . historyPreview = null
this . activeChatMode = 'chat'
this . activeChatId = item . id || null
this . activeConversationId = item . conversationId || ''
this . showHistoryPanel = false
await this . loadChatHistory ( item )
} ,
/** 非聊天记录的面板内详情(写作 / 图片另带「再来一次」) */
async openHistoryPreview ( item ) {
const key = this . getHistoryKey ( item )
// 再点同一条即收起
if ( this . historyPreview && this . historyPreview . key === key && ! this . historyPreview . loading ) {
this . historyPreview = null
return
}
this . historyPreview = {
key ,
item ,
messages : [ ] ,
loading : true
}
try {
const messages = await this . fetchHistoryMessages ( item )
if ( ! this . historyPreview || this . historyPreview . key !== key ) {
return
}
this . historyPreview . messages = messages
} catch ( error ) {
if ( this . historyPreview && this . historyPreview . key === key ) {
this . historyPreview = null
this . showError ( error , '加载历史记录失败' )
}
} finally {
if ( this . historyPreview && this . historyPreview . key === key ) {
this . historyPreview . loading = false
}
}
} ,
/** 解析后端 `params`(入参快照 JSON,老记录为空) */
parseHistoryParams ( value ) {
if ( ! value ) {
return { }
}
if ( typeof value === 'object' ) {
return value
}
try {
const parsed = JSON . parse ( String ( value ) )
return parsed && typeof parsed === 'object' ? parsed : { }
} catch ( error ) {
return { }
}
} ,
/** 老记录没有 params 时,退回详情里的用户原话(再退回标题) */
historyRecordInput ( item ) {
const messages = ( this . historyPreview && this . historyPreview . messages ) || [ ]
const userMessage = messages . find ( ( message ) => this . isUserMessage ( message ) )
if ( userMessage && String ( userMessage . content || '' ) . trim ( ) ) {
return String ( userMessage . content || '' ) . trim ( )
}
return String ( ( item && item . title ) || '' ) . trim ( )
} ,
/**
* 按原参数再来一次:只回填参数与输入框,不自动发送。
*
* 写作回填文体 / 字数 / 写作参数 / 正文(自由创作回填 userContent),
* 图片回填提示词;创意与活动策划没有可回填的界面参数,只读。
*/
replayHistoryItem ( item ) {
const type = this . getHistoryType ( item )
if ( ! this . canReplayHistory ( item ) ) {
this . showToast ( '该记录仅可查看' )
return
}
const params = this . parseHistoryParams ( item . params )
if ( type === 'writing' ) {
this . fillWritingFromHistory ( item , params )
} else {
this . fillImageFromHistory ( item , params )
}
this . activeChatMode = type
this . showHistoryPanel = false
this . historyPreview = null
this . showToast ( '已按原参数回填,可修改后发送' )
} ,
fillWritingFromHistory ( item , params ) {
const docType = String ( params . docType || params . doc _type || '' ) . trim ( )
this . writingDocType =
docType && docType !== WRITING _DOC _TYPE _FREE ? docType : WRITING _DOC _TYPE _FREE
const wordCount = params . number
this . writingWordCount =
wordCount !== undefined && wordCount !== null && String ( wordCount ) . trim ( )
? String ( wordCount )
: 'auto'
const nextParams = {
eventType : String ( params . eventType || params . event _type || '' ) . trim ( ) ,
theme : String ( params . theme || '' ) . trim ( ) ,
time : String ( params . time || '' ) . trim ( ) ,
location : String ( params . location || '' ) . trim ( ) ,
participants : String ( params . participants || '' ) . trim ( )
}
this . writingParams = nextParams
// 有参数就展开,方便用户看到回填结果
this . writingParamsOpen = [ 'theme' , 'time' , 'location' , 'participants' ] . some (
( key ) => ! ! nextParams [ key ]
)
const input = String (
params . highlights || params . userContent || params . user _content || ''
) . trim ( )
this . workspaces . writing . draft = input || this . historyRecordInput ( item )
} ,
fillImageFromHistory ( item , params ) {
const input = String ( params . user _content || params . userContent || '' ) . trim ( )
this . workspaces . image . draft = input || this . historyRecordInput ( item )
} ,
/**
* 认领生成记录:取末帧 `chatId` 设为当前模式的高亮项,并刷新历史。
* 失败态没有 chatId,调用方只在成功分支调用。
*/
async claimGenerationRecord ( payload ) {
const chatId =
payload && payload . chatId !== undefined && payload . chatId !== null
? payload . chatId
: payload && payload . data && payload . data . chatId
if ( chatId !== undefined && chatId !== null && chatId !== '' ) {
this . activeChatId = chatId
}
try {
await this . loadHistoryList ( false )
} catch ( error ) {
// 列表刷新失败不该把已成功的生成提示成「发送失败」
console . warn ( 'refresh history after generation failed' , error )
}
} ,
startNewChat ( ) {
if ( this . loading ) {
this . showToast ( '当前正在生成回复,请稍后再开启新对话' )
return
}
this . disconnectWebSocket ( )
if ( this . activeChatMode === 'chat' ) {
this . disconnectWebSocket ( )
}
this . activeChatId = null
this . activeConversationId = ''
this . streamingMessageId = ''
this . messageList = [ ]
this . draft = ''
this . historyPreview = null
this . showHistoryPanel = false
} ,
/**
@@ -1318,6 +1672,8 @@
} ) . promise
if ( finalPayload && Number ( finalPayload . code ) === 0 ) {
setResult ( this . formatWritingResult ( finalPayload . data ) )
// 末帧带上后端刚写入的记录 id(失败态没有)
await this . claimGenerationRecord ( finalPayload )
return
}
if ( finalPayload ) {
@@ -1359,32 +1715,35 @@
return
}
setResult ( this . formatCreativeResult ( finalPayload . data ) )
await this . claimGenerationRecord ( finalPayload )
}
} ,
openHistoryPanel ( ) {
this . historyPreview = null
this . showHistoryPanel = true
if ( this . currentUser . userId ) {
this . loadHistoryList ( false ) . catch ( ( error ) => {
this . showError ( error , '加载历史会话 失败' )
this . showError ( error , '加载历史记录 失败' )
} )
}
} ,
closeHistoryPanel ( ) {
this . showHistoryPanel = false
this . historyPreview = null
} ,
async removeChat ( item ) {
if ( this . loading ) {
this . showToast ( '当前正在生成回复,请稍后再删除会话 ' )
this . showToast ( '当前正在生成回复,请稍后再删除记录 ' )
return
}
if ( ! item . id ) {
this . showToast ( '当前会话 缺少标识,无法删除' )
this . showToast ( '当前记录 缺少标识,无法删除' )
return
}
const result = await new Promise ( ( resolve ) => {
uni . showModal ( {
title : '删除会话 ' ,
title : '删除记录 ' ,
content : ` 确定删除“ ${ this . getHistoryTitle ( item ) } ”吗? ` ,
success : resolve ,
fail : ( ) => resolve ( { confirm : false } )
@@ -1394,15 +1753,36 @@
return
}
const itemKey = this . getHistoryKey ( item )
const itemType = this . getHistoryType ( item )
// 同一条记录可能正开在某个模式的工作区里(三模式各自独立)
const affectedModes = Object . keys ( this . workspaces ) . filter ( ( mode ) => {
const workspace = this . workspaces [ mode ]
return (
workspace . activeChatId === item . id ||
( item . conversationId && workspace . activeConversationId === item . conversationId )
)
} )
this . deletingChatId = item . id
try {
const isActive =
this . activeChatId === item . id ||
( item . conversationId && this . activeConversationId === item . conversationId )
await removeAiChatList ( item . id )
if ( this . historyPreview && this . historyPreview . key === itemKey ) {
this . historyPreview = null
}
await this . loadHistoryList ( false )
if ( isActive ) {
const nextChat = this . historyList [ 0 ]
affectedModes . forEach ( ( mode ) => {
const workspace = this . workspaces [ mode ]
workspace . activeChatId = null
workspace . activeConversationId = ''
workspace . streamingMessageId = ''
workspace . messageList = [ ]
} )
// 聊天记录被删且正开在聊天模式:接上最近的一条聊天记录
if ( itemType === 'chat' && this . activeChatMode === 'chat' && affectedModes . indexOf ( 'chat' ) > - 1 ) {
const nextChat = this . historyList . find (
( candidate ) => this . getHistoryType ( candidate ) === 'chat'
)
if ( nextChat ) {
this . activeChatId = nextChat . id || null
this . activeConversationId = nextChat . conversationId || ''
@@ -2087,6 +2467,37 @@
font - size : 24 rpx ;
}
/* 类型筛选:与写作参数一致,单行横向滑动,不换行堆叠 */
. history - filter {
flex - shrink : 0 ;
width : 100 % ;
margin - top : 20 rpx ;
white - space : nowrap ;
}
. history - filter - track {
display : inline - flex ;
align - items : center ;
gap : 10 rpx ;
}
. history - filter - chip {
flex - shrink : 0 ;
padding : 10 rpx 18 rpx ;
border - radius : 999 rpx ;
border : 1 rpx solid rgba ( 20 , 150 , 242 , 0.18 ) ;
background : rgba ( 255 , 255 , 255 , 0.9 ) ;
color : # 1496 f2 ;
font - size : 22 rpx ;
line - height : 1.3 ;
}
. history - filter - chip -- active {
border - color : transparent ;
background : linear - gradient ( 145 deg , # 0 f7dd1 0 % , # 1496 f2 100 % ) ;
color : # fff ;
}
. history - scroll {
flex : 1 ;
min - height : 0 ;
@@ -2104,6 +2515,10 @@
color : # 8 b817b ;
}
. history - item - wrap + . history - item - wrap {
margin - top : 14 rpx ;
}
. history - item {
display : flex ;
align - items : center ;
@@ -2115,10 +2530,6 @@
box - shadow : 0 10 rpx 28 rpx rgba ( 76 , 49 , 35 , 0.06 ) ;
}
. history - item + . history - item {
margin - top : 14 rpx ;
}
. history - item -- active {
border - color : rgba ( 20 , 150 , 242 , 0.2 ) ;
background : # fff1ec ;
@@ -2129,7 +2540,48 @@
min - width : 0 ;
}
. history - item - head {
display : flex ;
align - items : flex - start ;
gap : 10 rpx ;
}
/* 类型徽标:聊天 / 写作 / 图片 / 创意 / 活动策划 */
. history - type - badge {
flex - shrink : 0 ;
margin - top : 4 rpx ;
padding : 2 rpx 12 rpx ;
border - radius : 10 rpx ;
background : rgba ( 20 , 150 , 242 , 0.1 ) ;
color : # 1496 f2 ;
font - size : 20 rpx ;
font - weight : 600 ;
line - height : 1.6 ;
}
. history - type - badge -- writing {
background : rgba ( 31 , 143 , 73 , 0.1 ) ;
color : # 1 f8f49 ;
}
. history - type - badge -- image {
background : rgba ( 240 , 122 , 61 , 0.12 ) ;
color : # c1652d ;
}
. history - type - badge -- creative {
background : rgba ( 123 , 97 , 255 , 0.1 ) ;
color : # 6 a53d6 ;
}
. history - type - badge -- activity _outline {
background : rgba ( 20 , 150 , 242 , 0.08 ) ;
color : # 0 f7dd1 ;
}
. history - item - title {
flex : 1 ;
min - width : 0 ;
font - size : 26 rpx ;
font - weight : 700 ;
color : # 2 f3136 ;
@@ -2151,6 +2603,84 @@
font - size : 22 rpx ;
}
/* 面板内的记录详情:仅非聊天类型出现 */
. history - preview {
margin - top : 10 rpx ;
padding : 18 rpx 20 rpx ;
border - radius : 20 rpx ;
border : 1 rpx dashed rgba ( 20 , 150 , 242 , 0.24 ) ;
background : rgba ( 255 , 255 , 255 , 0.86 ) ;
}
. history - preview - head {
display : flex ;
align - items : center ;
justify - content : space - between ;
gap : 12 rpx ;
}
. history - preview - caption {
font - size : 22 rpx ;
font - weight : 600 ;
color : # 1496 f2 ;
}
. history - preview - close {
font - size : 20 rpx ;
color : # 8 a7f79 ;
}
. history - preview - hint {
padding : 14 rpx 0 ;
font - size : 22 rpx ;
color : # 978 b84 ;
}
. history - preview - row {
margin - top : 14 rpx ;
}
. history - preview - role {
margin - bottom : 6 rpx ;
font - size : 20 rpx ;
color : # 8 e837d ;
}
. history - preview - text {
font - size : 24 rpx ;
line - height : 1.7 ;
color : # 2 f3136 ;
word - break : break - all ;
white - space : pre - wrap ;
}
. history - preview - markdown {
display : block ;
white - space : normal ;
}
. history - preview - actions {
display : flex ;
align - items : center ;
flex - wrap : wrap ;
gap : 12 rpx ;
margin - top : 18 rpx ;
}
. history - preview - replay {
padding : 12 rpx 22 rpx ;
border - radius : 999 rpx ;
background : linear - gradient ( 145 deg , # 0 f7dd1 0 % , # 1496 f2 100 % ) ;
color : # fff ;
font - size : 22 rpx ;
font - weight : 600 ;
}
. history - preview - tip {
font - size : 20 rpx ;
color : # 978 b84 ;
}
. history - empty {
padding : 40 rpx 20 rpx ;
text - align : center ;