feat(ai): 助手页支持写作/生图等记录的历史列表与按类型筛选
- 三模式各自独立工作区:切模式不丢消息、草稿与当前记录 - 历史面板加类型徽标与筛选(全部/聊天/写作/图片/创意/活动策划),文案改「历史记录」 - 生成成功认领末帧 chatId 并刷新列表;聊天 __END__ 帧的 chatId 同样接受 - 写作/图片记录支持「按原参数再来一次」(只回填、不自动发送) - 文稿/创意/活动策划三页加历史入口,经 storage 传递筛选后 switchTab - 面板提示仅展示最近 30 条
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
<view class="hero-action" @tap="fillExample('volunteer')">志愿服务</view>
|
<view class="hero-action" @tap="fillExample('volunteer')">志愿服务</view>
|
||||||
<view class="hero-action" @tap="fillExample('salon')">主题沙龙</view>
|
<view class="hero-action" @tap="fillExample('salon')">主题沙龙</view>
|
||||||
<view class="hero-action" @tap="fillExample('sports')">校园赛事</view>
|
<view class="hero-action" @tap="fillExample('sports')">校园赛事</view>
|
||||||
|
<view class="hero-action" @tap="goHistoryPage">历史记录</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -458,6 +459,18 @@
|
|||||||
this.form.event_type = '校园赛事 + 开幕仪式 + 分组淘汰赛'
|
this.form.event_type = '校园赛事 + 开幕仪式 + 分组淘汰赛'
|
||||||
this.form.event_location = '学校室外篮球场'
|
this.form.event_location = '学校室外篮球场'
|
||||||
this.form.event_meme = '需要体现赛事组织、赛程安排、安全保障、医疗预案和宣传报道计划。'
|
this.form.event_meme = '需要体现赛事组织、赛程安排、安全保障、医疗预案和宣传报道计划。'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 历史记录统一在助手页查看。
|
||||||
|
*
|
||||||
|
* 助手页是 tabBar 页,`switchTab` 不能带参数,所以先把类型筛选
|
||||||
|
* 写进 storage,助手页 onShow 读取后立即清除。
|
||||||
|
*/
|
||||||
|
goHistoryPage() {
|
||||||
|
uni.setStorageSync('ai_history_type', 'activity_outline')
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/assistant/index'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -370,6 +370,14 @@ export function apiRequest({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 历史记录列表。
|
||||||
|
*
|
||||||
|
* @param {object} [params]
|
||||||
|
* @param {string} [params.chatType] 可选,按记录类型精确筛选:
|
||||||
|
* `chat` / `writing` / `image` / `creative` / `activity_outline`;
|
||||||
|
* 不传则返回该用户全部类型的记录(助手页即拉全量后在前端过滤)。
|
||||||
|
*/
|
||||||
export function listAiChatList(params) {
|
export function listAiChatList(params) {
|
||||||
return apiRequest({
|
return apiRequest({
|
||||||
url: `${MODULES_API_URL}/ai/ai-chat-list`,
|
url: `${MODULES_API_URL}/ai/ai-chat-list`,
|
||||||
|
|||||||
+583
-53
@@ -45,7 +45,7 @@
|
|||||||
>
|
>
|
||||||
<view v-if="!currentUser.userId" class="notice-card">
|
<view v-if="!currentUser.userId" class="notice-card">
|
||||||
<view class="notice-title">请先登录后使用 AI 助手</view>
|
<view class="notice-title">请先登录后使用 AI 助手</view>
|
||||||
<view class="notice-desc">当前页面已接入真实会话逻辑,发送消息和历史会话依赖登录态。</view>
|
<view class="notice-desc">当前页面已接入真实会话逻辑,发送消息和历史记录依赖登录态。</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-else-if="!messageList.length" class="empty-card">
|
<view v-else-if="!messageList.length" class="empty-card">
|
||||||
@@ -262,38 +262,91 @@
|
|||||||
<view class="history-panel" @tap.stop>
|
<view class="history-panel" @tap.stop>
|
||||||
<view class="history-head">
|
<view class="history-head">
|
||||||
<view>
|
<view>
|
||||||
<view class="history-title">历史会话</view>
|
<view class="history-title">历史记录</view>
|
||||||
<view class="history-subtitle">按最近更新时间展示</view>
|
<view class="history-subtitle">仅展示最近 30 条,按更新时间排序</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="history-close" @tap="closeHistoryPanel">关闭</view>
|
<view class="history-close" @tap="closeHistoryPanel">关闭</view>
|
||||||
</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">
|
<scroll-view scroll-y class="history-scroll">
|
||||||
<view v-if="historySections.length">
|
<view v-if="historySections.length">
|
||||||
<view v-for="section in historySections" :key="section.label" class="history-section">
|
<view v-for="section in historySections" :key="section.label" class="history-section">
|
||||||
<view class="history-label">{{ section.label }}</view>
|
<view class="history-label">{{ section.label }}</view>
|
||||||
<view
|
<view v-for="item in section.items" :key="getHistoryKey(item)" class="history-item-wrap">
|
||||||
v-for="item in section.items"
|
<view
|
||||||
:key="getHistoryKey(item)"
|
class="history-item"
|
||||||
class="history-item"
|
:class="{ 'history-item--active': activeHistoryKey === getHistoryKey(item) }"
|
||||||
:class="{ 'history-item--active': activeHistoryKey === getHistoryKey(item) }"
|
>
|
||||||
>
|
<view class="history-item-main" @tap="selectChat(item)">
|
||||||
<view class="history-item-main" @tap="selectChat(item)">
|
<view class="history-item-head">
|
||||||
<view class="history-item-title">{{ getHistoryTitle(item) }}</view>
|
<text
|
||||||
<view class="history-item-time">
|
class="history-type-badge"
|
||||||
{{ formatHistoryItemTime(item.createTime) }}
|
: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>
|
</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>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view v-else class="history-empty">暂无历史对话</view>
|
<view v-else class="history-empty">暂无历史记录</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -334,6 +387,36 @@
|
|||||||
{ label: '写作', value: 'writing' },
|
{ label: '写作', value: 'writing' },
|
||||||
{ label: '图片', value: 'image' }
|
{ 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_content /
|
||||||
// user_files),前端只能把它拼进 user_content —— 那是替上游写 prompt。
|
// user_files),前端只能把它拼进 user_content —— 那是替上游写 prompt。
|
||||||
// 要恢复这个功能,先在上游开始节点加变量,再按变量契约接上来。
|
// 要恢复这个功能,先在上游开始节点加变量,再按变量契约接上来。
|
||||||
@@ -362,8 +445,13 @@
|
|||||||
participants: ''
|
participants: ''
|
||||||
},
|
},
|
||||||
statusBarHeight: 0,
|
statusBarHeight: 0,
|
||||||
draft: '',
|
|
||||||
activeChatMode: 'chat',
|
activeChatMode: 'chat',
|
||||||
|
// 三个模式各自独立的工作区:切换模式不丢消息、当前记录与草稿
|
||||||
|
workspaces: {
|
||||||
|
chat: createWorkspace(),
|
||||||
|
writing: createWorkspace(),
|
||||||
|
image: createWorkspace()
|
||||||
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
wsConnected: false,
|
wsConnected: false,
|
||||||
scrollIntoView: '',
|
scrollIntoView: '',
|
||||||
@@ -374,11 +462,12 @@
|
|||||||
'优化竞赛申报书摘要',
|
'优化竞赛申报书摘要',
|
||||||
'整理五四评优申报材料清单'
|
'整理五四评优申报材料清单'
|
||||||
],
|
],
|
||||||
messageList: [],
|
|
||||||
historyList: [],
|
historyList: [],
|
||||||
activeChatId: null,
|
// 历史面板的类型筛选,默认「全部」
|
||||||
activeConversationId: '',
|
historyTypeFilter: 'all',
|
||||||
streamingMessageId: '',
|
historyTypeOptions: HISTORY_TYPE_OPTIONS,
|
||||||
|
// 面板内展开的非聊天记录详情(含写作 / 图片的「再来一次」)
|
||||||
|
historyPreview: null,
|
||||||
showHistoryPanel: false,
|
showHistoryPanel: false,
|
||||||
deletingChatId: null,
|
deletingChatId: null,
|
||||||
currentUser: {
|
currentUser: {
|
||||||
@@ -394,6 +483,50 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
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() {
|
navbarTotalHeight() {
|
||||||
return this.statusBarHeight + 44
|
return this.statusBarHeight + 44
|
||||||
},
|
},
|
||||||
@@ -472,6 +605,9 @@
|
|||||||
return 'AI 助手待命中'
|
return 'AI 助手待命中'
|
||||||
},
|
},
|
||||||
activeHistoryKey() {
|
activeHistoryKey() {
|
||||||
|
if (this.historyPreview && this.historyPreview.key) {
|
||||||
|
return this.historyPreview.key
|
||||||
|
}
|
||||||
if (this.activeChatId) {
|
if (this.activeChatId) {
|
||||||
return `chat-${this.activeChatId}`
|
return `chat-${this.activeChatId}`
|
||||||
}
|
}
|
||||||
@@ -491,9 +627,18 @@
|
|||||||
)
|
)
|
||||||
return target ? this.getHistoryTitle(target) : '当前会话'
|
return target ? this.getHistoryTitle(target) : '当前会话'
|
||||||
},
|
},
|
||||||
|
/** 类型筛选之后的历史记录(全部类型时不复制数组) */
|
||||||
|
historyFilteredList() {
|
||||||
|
if (this.historyTypeFilter === 'all') {
|
||||||
|
return this.historyList
|
||||||
|
}
|
||||||
|
return this.historyList.filter(
|
||||||
|
(item) => this.getHistoryType(item) === this.historyTypeFilter
|
||||||
|
)
|
||||||
|
},
|
||||||
historySections() {
|
historySections() {
|
||||||
const groupMap = {}
|
const groupMap = {}
|
||||||
;[...this.historyList]
|
;[...this.historyFilteredList]
|
||||||
.sort((left, right) => this.getTimestamp(right.updateTime || right.createTime) - this.getTimestamp(left.updateTime || left.createTime))
|
.sort((left, right) => this.getTimestamp(right.updateTime || right.createTime) - this.getTimestamp(left.updateTime || left.createTime))
|
||||||
.forEach((item) => {
|
.forEach((item) => {
|
||||||
const label = this.getHistoryDateLabel(item.createTime || item.updateTime)
|
const label = this.getHistoryDateLabel(item.createTime || item.updateTime)
|
||||||
@@ -502,10 +647,6 @@
|
|||||||
}
|
}
|
||||||
groupMap[label].push(item)
|
groupMap[label].push(item)
|
||||||
})
|
})
|
||||||
console.log(Object.keys(groupMap).map((label) => ({
|
|
||||||
label,
|
|
||||||
items: groupMap[label]
|
|
||||||
})))
|
|
||||||
return Object.keys(groupMap).map((label) => ({
|
return Object.keys(groupMap).map((label) => ({
|
||||||
label,
|
label,
|
||||||
items: groupMap[label]
|
items: groupMap[label]
|
||||||
@@ -518,13 +659,15 @@
|
|||||||
},
|
},
|
||||||
onShow() {
|
onShow() {
|
||||||
this.refreshUser()
|
this.refreshUser()
|
||||||
|
// 独立页经 storage 传来的类型:预置筛选并直接打开历史面板
|
||||||
|
const fromHistoryEntry = this.applyPendingHistoryType()
|
||||||
if (!this.initialized) {
|
if (!this.initialized) {
|
||||||
this.initAssistant()
|
this.initAssistant(!fromHistoryEntry)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.currentUser.userId) {
|
if (this.currentUser.userId) {
|
||||||
this.loadHistoryList(false).catch((error) => {
|
this.loadHistoryList(false).catch((error) => {
|
||||||
this.showError(error, '刷新历史会话失败')
|
this.showError(error, '刷新历史记录失败')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -542,19 +685,45 @@
|
|||||||
}
|
}
|
||||||
return this.currentUser
|
return this.currentUser
|
||||||
},
|
},
|
||||||
async initAssistant() {
|
async initAssistant(autoSelect = true) {
|
||||||
try {
|
try {
|
||||||
this.refreshUser()
|
this.refreshUser()
|
||||||
if (!this.currentUser.userId) {
|
if (!this.currentUser.userId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await this.loadHistoryList(true)
|
await this.loadHistoryList(autoSelect)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.showError(error, '初始化聊天页失败')
|
this.showError(error, '初始化聊天页失败')
|
||||||
} finally {
|
} finally {
|
||||||
this.initialized = true
|
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) {
|
showToast(title) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title,
|
title,
|
||||||
@@ -649,6 +818,33 @@
|
|||||||
}
|
}
|
||||||
return '未命名对话'
|
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) {
|
parseAssistantContent(value) {
|
||||||
if (!value || value === ASSISTANT_PENDING_TEXT) {
|
if (!value || value === ASSISTANT_PENDING_TEXT) {
|
||||||
return {
|
return {
|
||||||
@@ -737,6 +933,8 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.activeChatMode = mode
|
this.activeChatMode = mode
|
||||||
|
// 切回某个模式时,该模式自己的消息区滚到底部
|
||||||
|
this.scrollToBottom()
|
||||||
},
|
},
|
||||||
scrollToBottom() {
|
scrollToBottom() {
|
||||||
this.bottomAnchorId = `chat-bottom-${Date.now()}`
|
this.bottomAnchorId = `chat-bottom-${Date.now()}`
|
||||||
@@ -806,10 +1004,19 @@
|
|||||||
target.content === ASSISTANT_PENDING_TEXT ? answer : `${target.content}${answer}`
|
target.content === ASSISTANT_PENDING_TEXT ? answer : `${target.content}${answer}`
|
||||||
this.scrollToBottom()
|
this.scrollToBottom()
|
||||||
},
|
},
|
||||||
async finishAssistantMessage(conversationId) {
|
/**
|
||||||
|
* 聊天收尾:刷新历史并认领后端回传的记录 id。
|
||||||
|
*
|
||||||
|
* @param {string} [conversationId] 上游会话标识
|
||||||
|
* @param {number|string} [chatId] `__END__` 帧新增的记录 id(仅成功写入时出现)
|
||||||
|
*/
|
||||||
|
async finishAssistantMessage(conversationId, chatId) {
|
||||||
if (conversationId) {
|
if (conversationId) {
|
||||||
this.activeConversationId = conversationId
|
this.activeConversationId = conversationId
|
||||||
}
|
}
|
||||||
|
if (chatId !== undefined && chatId !== null && chatId !== '') {
|
||||||
|
this.activeChatId = chatId
|
||||||
|
}
|
||||||
const target = this.messageList.find((item) => item.id === this.streamingMessageId)
|
const target = this.messageList.find((item) => item.id === this.streamingMessageId)
|
||||||
if (target) {
|
if (target) {
|
||||||
target.loading = false
|
target.loading = false
|
||||||
@@ -846,7 +1053,8 @@
|
|||||||
try {
|
try {
|
||||||
const data = JSON.parse(raw)
|
const data = JSON.parse(raw)
|
||||||
if (data && data.answer === '__END__') {
|
if (data && data.answer === '__END__') {
|
||||||
await this.finishAssistantMessage(data.conversationId)
|
// 末帧带后端刚写入的记录 id(失败态没有);带上它才能高亮对应记录
|
||||||
|
await this.finishAssistantMessage(data.conversationId, data.chatId)
|
||||||
this.disconnectWebSocket()
|
this.disconnectWebSocket()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -963,40 +1171,186 @@
|
|||||||
!this.activeConversationId &&
|
!this.activeConversationId &&
|
||||||
this.historyList.length
|
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 { userId } = this.refreshUser()
|
||||||
const response = await listAiChatHistory({
|
const response = await listAiChatHistory({
|
||||||
chatId: item.id,
|
chatId: item.id,
|
||||||
conversationId: item.conversationId,
|
conversationId: item.conversationId,
|
||||||
userId
|
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()
|
this.scrollToBottom()
|
||||||
},
|
},
|
||||||
async selectChat(item, silent = false) {
|
async selectChat(item, silent = false) {
|
||||||
if (this.loading && !silent) {
|
if (this.loading && !silent) {
|
||||||
this.showToast('当前正在生成回复,请稍后再切换会话')
|
this.showToast('当前正在生成回复,请稍后再切换记录')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 聊天记录打开会话;其余类型在面板内展开详情
|
||||||
|
if (this.getHistoryType(item) !== 'chat') {
|
||||||
|
await this.openHistoryPreview(item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.historyPreview = null
|
||||||
|
this.activeChatMode = 'chat'
|
||||||
this.activeChatId = item.id || null
|
this.activeChatId = item.id || null
|
||||||
this.activeConversationId = item.conversationId || ''
|
this.activeConversationId = item.conversationId || ''
|
||||||
this.showHistoryPanel = false
|
this.showHistoryPanel = false
|
||||||
await this.loadChatHistory(item)
|
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() {
|
startNewChat() {
|
||||||
if (this.loading) {
|
if (this.loading) {
|
||||||
this.showToast('当前正在生成回复,请稍后再开启新对话')
|
this.showToast('当前正在生成回复,请稍后再开启新对话')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.disconnectWebSocket()
|
if (this.activeChatMode === 'chat') {
|
||||||
|
this.disconnectWebSocket()
|
||||||
|
}
|
||||||
this.activeChatId = null
|
this.activeChatId = null
|
||||||
this.activeConversationId = ''
|
this.activeConversationId = ''
|
||||||
this.streamingMessageId = ''
|
this.streamingMessageId = ''
|
||||||
this.messageList = []
|
this.messageList = []
|
||||||
this.draft = ''
|
this.draft = ''
|
||||||
|
this.historyPreview = null
|
||||||
this.showHistoryPanel = false
|
this.showHistoryPanel = false
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -1318,6 +1672,8 @@
|
|||||||
}).promise
|
}).promise
|
||||||
if (finalPayload && Number(finalPayload.code) === 0) {
|
if (finalPayload && Number(finalPayload.code) === 0) {
|
||||||
setResult(this.formatWritingResult(finalPayload.data))
|
setResult(this.formatWritingResult(finalPayload.data))
|
||||||
|
// 末帧带上后端刚写入的记录 id(失败态没有)
|
||||||
|
await this.claimGenerationRecord(finalPayload)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (finalPayload) {
|
if (finalPayload) {
|
||||||
@@ -1359,32 +1715,35 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
setResult(this.formatCreativeResult(finalPayload.data))
|
setResult(this.formatCreativeResult(finalPayload.data))
|
||||||
|
await this.claimGenerationRecord(finalPayload)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openHistoryPanel() {
|
openHistoryPanel() {
|
||||||
|
this.historyPreview = null
|
||||||
this.showHistoryPanel = true
|
this.showHistoryPanel = true
|
||||||
if (this.currentUser.userId) {
|
if (this.currentUser.userId) {
|
||||||
this.loadHistoryList(false).catch((error) => {
|
this.loadHistoryList(false).catch((error) => {
|
||||||
this.showError(error, '加载历史会话失败')
|
this.showError(error, '加载历史记录失败')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
closeHistoryPanel() {
|
closeHistoryPanel() {
|
||||||
this.showHistoryPanel = false
|
this.showHistoryPanel = false
|
||||||
|
this.historyPreview = null
|
||||||
},
|
},
|
||||||
async removeChat(item) {
|
async removeChat(item) {
|
||||||
if (this.loading) {
|
if (this.loading) {
|
||||||
this.showToast('当前正在生成回复,请稍后再删除会话')
|
this.showToast('当前正在生成回复,请稍后再删除记录')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!item.id) {
|
if (!item.id) {
|
||||||
this.showToast('当前会话缺少标识,无法删除')
|
this.showToast('当前记录缺少标识,无法删除')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await new Promise((resolve) => {
|
const result = await new Promise((resolve) => {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '删除会话',
|
title: '删除记录',
|
||||||
content: `确定删除“${this.getHistoryTitle(item)}”吗?`,
|
content: `确定删除“${this.getHistoryTitle(item)}”吗?`,
|
||||||
success: resolve,
|
success: resolve,
|
||||||
fail: () => resolve({ confirm: false })
|
fail: () => resolve({ confirm: false })
|
||||||
@@ -1394,15 +1753,36 @@
|
|||||||
return
|
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
|
this.deletingChatId = item.id
|
||||||
try {
|
try {
|
||||||
const isActive =
|
|
||||||
this.activeChatId === item.id ||
|
|
||||||
(item.conversationId && this.activeConversationId === item.conversationId)
|
|
||||||
await removeAiChatList(item.id)
|
await removeAiChatList(item.id)
|
||||||
|
if (this.historyPreview && this.historyPreview.key === itemKey) {
|
||||||
|
this.historyPreview = null
|
||||||
|
}
|
||||||
await this.loadHistoryList(false)
|
await this.loadHistoryList(false)
|
||||||
if (isActive) {
|
affectedModes.forEach((mode) => {
|
||||||
const nextChat = this.historyList[0]
|
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) {
|
if (nextChat) {
|
||||||
this.activeChatId = nextChat.id || null
|
this.activeChatId = nextChat.id || null
|
||||||
this.activeConversationId = nextChat.conversationId || ''
|
this.activeConversationId = nextChat.conversationId || ''
|
||||||
@@ -2087,6 +2467,37 @@
|
|||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 类型筛选:与写作参数一致,单行横向滑动,不换行堆叠 */
|
||||||
|
.history-filter {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-filter-track {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-filter-chip {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 10rpx 18rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
border: 1rpx solid rgba(20, 150, 242, 0.18);
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
color: #1496f2;
|
||||||
|
font-size: 22rpx;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-filter-chip--active {
|
||||||
|
border-color: transparent;
|
||||||
|
background: linear-gradient(145deg, #0f7dd1 0%, #1496f2 100%);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.history-scroll {
|
.history-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
@@ -2104,6 +2515,10 @@
|
|||||||
color: #8b817b;
|
color: #8b817b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.history-item-wrap + .history-item-wrap {
|
||||||
|
margin-top: 14rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.history-item {
|
.history-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -2115,10 +2530,6 @@
|
|||||||
box-shadow: 0 10rpx 28rpx rgba(76, 49, 35, 0.06);
|
box-shadow: 0 10rpx 28rpx rgba(76, 49, 35, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-item + .history-item {
|
|
||||||
margin-top: 14rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-item--active {
|
.history-item--active {
|
||||||
border-color: rgba(20, 150, 242, 0.2);
|
border-color: rgba(20, 150, 242, 0.2);
|
||||||
background: #fff1ec;
|
background: #fff1ec;
|
||||||
@@ -2129,7 +2540,48 @@
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.history-item-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 类型徽标:聊天 / 写作 / 图片 / 创意 / 活动策划 */
|
||||||
|
.history-type-badge {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
padding: 2rpx 12rpx;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
background: rgba(20, 150, 242, 0.1);
|
||||||
|
color: #1496f2;
|
||||||
|
font-size: 20rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-type-badge--writing {
|
||||||
|
background: rgba(31, 143, 73, 0.1);
|
||||||
|
color: #1f8f49;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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: #6a53d6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-type-badge--activity_outline {
|
||||||
|
background: rgba(20, 150, 242, 0.08);
|
||||||
|
color: #0f7dd1;
|
||||||
|
}
|
||||||
|
|
||||||
.history-item-title {
|
.history-item-title {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #2f3136;
|
color: #2f3136;
|
||||||
@@ -2151,6 +2603,84 @@
|
|||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 面板内的记录详情:仅非聊天类型出现 */
|
||||||
|
.history-preview {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
padding: 18rpx 20rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
border: 1rpx 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: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-preview-caption {
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1496f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-preview-close {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #8a7f79;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-preview-hint {
|
||||||
|
padding: 14rpx 0;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #978b84;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-preview-row {
|
||||||
|
margin-top: 14rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-preview-role {
|
||||||
|
margin-bottom: 6rpx;
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #8e837d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-preview-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: #2f3136;
|
||||||
|
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: 12rpx;
|
||||||
|
margin-top: 18rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-preview-replay {
|
||||||
|
padding: 12rpx 22rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
background: linear-gradient(145deg, #0f7dd1 0%, #1496f2 100%);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-preview-tip {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #978b84;
|
||||||
|
}
|
||||||
|
|
||||||
.history-empty {
|
.history-empty {
|
||||||
padding: 40rpx 20rpx;
|
padding: 40rpx 20rpx;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<view class="hero-action" @tap="fillExample('video')">短视频创意</view>
|
<view class="hero-action" @tap="fillExample('video')">短视频创意</view>
|
||||||
<view class="hero-action" @tap="fillExample('script')">脚本创作</view>
|
<view class="hero-action" @tap="fillExample('script')">脚本创作</view>
|
||||||
<view class="hero-action" @tap="fillExample('image')">海报排版</view>
|
<view class="hero-action" @tap="fillExample('image')">海报排版</view>
|
||||||
|
<view class="hero-action" @tap="goHistoryPage">历史记录</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -405,6 +406,18 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.content = '请根据上传的活动照片、LOGO 和文案素材,生成一套公众号头图与活动海报排版,风格青春、明亮,输出图片文件。'
|
this.content = '请根据上传的活动照片、LOGO 和文案素材,生成一套公众号头图与活动海报排版,风格青春、明亮,输出图片文件。'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 历史记录统一在助手页查看。
|
||||||
|
*
|
||||||
|
* 助手页是 tabBar 页,`switchTab` 不能带参数,所以先把类型筛选
|
||||||
|
* 写进 storage,助手页 onShow 读取后立即清除。
|
||||||
|
*/
|
||||||
|
goHistoryPage() {
|
||||||
|
uni.setStorageSync('ai_history_type', 'creative')
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/assistant/index'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<view class="hero-action" @tap="fillDemo('wechat')">公众号推文</view>
|
<view class="hero-action" @tap="fillDemo('wechat')">公众号推文</view>
|
||||||
<view class="hero-action" @tap="fillDemo('notice')">通知公告</view>
|
<view class="hero-action" @tap="fillDemo('notice')">通知公告</view>
|
||||||
<view class="hero-action" @tap="fillDemo('free')">自由创作示例</view>
|
<view class="hero-action" @tap="fillDemo('free')">自由创作示例</view>
|
||||||
|
<view class="hero-action" @tap="goHistoryPage">历史记录</view>
|
||||||
<view class="hero-action hero-action--primary" @tap="goChatPage">进入问答助手</view>
|
<view class="hero-action hero-action--primary" @tap="goChatPage">进入问答助手</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -743,6 +744,18 @@
|
|||||||
uni.switchTab({
|
uni.switchTab({
|
||||||
url: '/pages/assistant/index'
|
url: '/pages/assistant/index'
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 历史记录统一在助手页查看。
|
||||||
|
*
|
||||||
|
* 助手页是 tabBar 页,`switchTab` 不能带参数,所以先把类型筛选
|
||||||
|
* 写进 storage,助手页 onShow 读取后立即清除。
|
||||||
|
*/
|
||||||
|
goHistoryPage() {
|
||||||
|
uni.setStorageSync('ai_history_type', 'writing')
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/assistant/index'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user