feat: 完善对话功能,自动设置默认模型账户并优化界面布局

This commit is contained in:
DEV_DSW
2026-04-14 20:54:54 +08:00
parent c61e41049f
commit fbec7088c6
11 changed files with 39 additions and 50 deletions

View File

@@ -550,7 +550,10 @@ contextBridge.exposeInMainWorld('api', {
#### 4.5.2 Chat 页面移除模型选择器
-`ChatBox.vue` 中移除 `ModelSelector` 组件及其引用;
- 对话功能直接使用 provider 管理中「设置为默认」的模型账户;
- 若未设置默认模型,提示用户前往模型管理页面配置。
- 对话页面 `onMounted` 时主动调用 `providerStore.init()` 加载模型配置。
#### 4.5.3 自动默认模型兜底
`providerStore.init()` 初始化后发现用户已存在 provider accounts 但未设置 `defaultAccountId` 时,自动将第一个 account 设为默认,确保用户无需手动点击"设为默认"即可直接开始对话。若用户没有任何账户,则保持原有提示行为。
---

View File

@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' http://8.138.234.141 https://one-feel-bucket.oss-cn-guangzhou.aliyuncs.com; connect-src 'self' http://8.138.234.141 https://api.iconify.design wss://onefeel.brother7.cn"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: http://8.138.234.141 https://one-feel-bucket.oss-cn-guangzhou.aliyuncs.com; connect-src 'self' http://8.138.234.141 https://api.iconify.design wss://onefeel.brother7.cn"
/>
</head>
<body>

View File

@@ -5,17 +5,10 @@ export interface taskCenterItem {
desc: string,
id: string,
icon: string,
type: 'sale' | 'close' | 'open' | 'channel'
type?: 'channel'
}
export const taskCenterList: taskCenterItem[] = [
{
title: '每日销售数据',
desc: '分析用于销售渠道每日数据汇总及简要展示',
id: uuidv4(),
icon: '销',
type: 'sale'
},
{
title: '一键打开各渠道',
desc: '人工账号登录,为自动化操作做好准备',
@@ -24,17 +17,9 @@ export const taskCenterList: taskCenterItem[] = [
type: 'channel'
},
{
title: '渠道房型',
desc: '关闭销售渠道下的指定房型',
title: '渠道房型',
desc: '销售渠道下的指定房型,管理开关房型',
id: uuidv4(),
icon: '',
type: 'close'
},
{
title: '开渠道房型',
desc: '开启销售渠道下的指定房型',
id: uuidv4(),
icon: '开',
type: 'open'
icon: ''
},
]

View File

@@ -1,12 +1,8 @@
<template>
<div class="flex flex-col h-full py-6 px-6 overflow-hidden">
<div class="flex flex-col flex-1 py-6 px-6 overflow-hidden">
<!-- 空状态 -->
<template v-if="isEmpty">
<ChatEmpty @click-tag="onQuickTag">
<template #task-center>
<TaskCenter />
</template>
</ChatEmpty>
<ChatEmpty @click-tag="onQuickTag" />
</template>
<!-- 消息列表 -->
@@ -35,11 +31,6 @@
<!-- 输入区 -->
<div class="flex flex-col gap-3 mt-4">
<div class="flex items-center gap-3">
<div class="inline-flex items-center justify-center w-[108px] px-3 py-1.5 rounded-2xl border border-[#E5E8EE] text-[13px] text-[#333] cursor-pointer hover:bg-[#2B7FFF] hover:text-[#fff] hover:border-[#2B7FFF]">
智能问数
</div>
</div>
<ChatInput
v-model="inputMessage"
:is-sending="chatStore.sending"
@@ -69,7 +60,6 @@ import ChatEmpty from './components/chat/ChatEmpty.vue'
import ChatErrorBar from './components/chat/ChatErrorBar.vue'
import ChatTypingIndicator from './components/chat/ChatTypingIndicator.vue'
import ChatActivityIndicator from './components/chat/ChatActivityIndicator.vue'
import TaskCenter from './TaskCenter.vue'
const chatStore = useChatStore()
const inputMessage = ref('')

View File

@@ -1,6 +1,6 @@
<template>
<div class="h-full">
<aside :class="['h-full box-border flex flex-col transition-all duration-300', sidebarCollapsed ? 'w-16' : 'w-50']">
<div :class="['h-full transition-all duration-300', sidebarCollapsed ? 'w-16' : 'w-50']">
<aside class="h-full box-border flex flex-col w-full">
<div class="flex items-center justify-center m-2">
<img v-if="!sidebarCollapsed" class="w-10 h-10 rounded-md" src="@assets/images/login/white_logo.png" />
<div v-if="!sidebarCollapsed" class="font-bold text-gray-80">YINIAN</div>

View File

@@ -1,10 +1,7 @@
<template>
<div class="flex-1 pb-6">
<div class="flex justify-between items-center py-4">
<div class="pl-6 pr-6 pb-6">
<div class="flex justify-between items-center pb-4">
<h3 class="text-base font-semibold">任务中心</h3>
<!-- <a class="text-[#3b82f6] text-[13px] cursor-pointer">
编辑
</a> -->
</div>
<div class="grid grid-cols-2 gap-4 max-[800px]:grid-cols-1">

View File

@@ -17,7 +17,7 @@
</div>
</div>
<div class="mt-auto">
<slot name="task-center" />
<slot />
</div>
</div>
</template>

View File

@@ -46,7 +46,7 @@
<!-- Markdown text -->
<div
v-if="markdownHtml"
class="bg-[#f7f9fc] rounded-md px-3 py-2 prose prose-sm max-w-none"
class="bg-[#f7f9fc] rounded-md px-3 py-2 prose prose-sm"
v-html="markdownHtml"
/>

View File

@@ -1,9 +1,10 @@
<template>
<layout>
<div class="flex h-full w-full flex-col md:flex-row">
<ChatHistory class="flex-none w-50" @new-chat="handleNewChat" @select-chat="handleSelectChat" />
<div class="flex-1 mr-2 overflow-hidden bg-white rounded-xl">
<ChatBox />
<ChatHistory class="flex-none" @new-chat="handleNewChat" @select-chat="handleSelectChat" />
<div class="flex-1 mr-2 overflow-hidden bg-white rounded-xl flex flex-col">
<ChatBox class="flex-1" />
<TaskCenter />
</div>
<TaskList />
</div>
@@ -18,20 +19,23 @@ import TaskList from '@src/components/TaskList/index.vue'
import TaskOperationDialog from './components/TaskOperationDialog.vue'
import ChatHistory from './ChatHistory.vue'
import ChatBox from './ChatBox.vue'
import TaskCenter from './TaskCenter.vue'
import { useChatStore } from '@store/chat'
import { useProviderStore } from '@store/providers'
import emitter from '@src/utils/emitter'
const chatStore = useChatStore()
const providerStore = useProviderStore()
const taskOperationDialog = ref()
onMounted(() => {
onMounted(async () => {
await providerStore.init()
chatStore.loadSessions()
chatStore.initConnection()
chatStore.subscribeToGateway()
})
onBeforeUnmount(() => {
chatStore.cleanupEmptySession()
chatStore.closeConnection()
})
const handleNewChat = () => {

View File

@@ -323,13 +323,14 @@ export async function stageBuffer(base64: string, fileName: string, mimeType: st
if (result && result.stagedPath) return result
} catch { /* fallback */ }
const dataUrl = `data:${mimeType};base64,${base64}`
return {
id: crypto.randomUUID(),
fileName,
mimeType,
fileSize: Math.ceil(base64.length * 0.75),
stagedPath: '',
preview: mimeType.startsWith('image/') ? `data:${mimeType};base64,${base64}` : null,
stagedPath: dataUrl,
preview: mimeType.startsWith('image/') ? dataUrl : null,
}
}

View File

@@ -19,6 +19,15 @@ export const useProviderStore = defineStore('providers', () => {
const init = async () => {
await refreshProviderSnapshot();
// 自动兜底:如果有账户但没设默认,自动将第一个设为默认
if (accounts.value.length > 0 && !defaultAccountId.value) {
try {
await setDefaultAccount(accounts.value[0].id);
} catch (err) {
console.error('Auto-set default account failed:', err);
}
}
};
const refreshProviderSnapshot = async () => {