Day 5:三栏布局——左账号 / 中聊天 / 右工具

Day 5 搭三栏工作台:El-Splitter 分栏、Pinia 管账号与当前页,中间先留 webview 位。


开场:登录进来了,不能还是一整块白屏

昨天,登录成功,router.replace('/main')------老王第一次进 AnchorChat 主界面,预期是day 规划中白板上那三个格子:左选账号、中间聊天、右边工具

今天把 布局骨架 搭出来。中间会挂上 ChatHost 组件占位,但 <webview> 怎么嵌 Telegram Web 留到明天------别贪,先把 Split 面板和 Pinia 状态理顺,后面 25 天都是往格子里填东西。

整体结构:WorkspaceView 一个页面扛大旗

登录后进 /main,渲染 WorkspaceView.vue(主工作台)。它比 Day 2 的 Hello World 复杂,但顶层就四层:

text 复制代码
AppTitleBar(Day 3)
└── el-splitter(横向或纵向,可拖拽)
    ├── 左 panel → AccountSidebar(账号 / 平台)
    └── 右 panel → el-main(中间工作区)+ el-aside(ToolSidebar)

用 Element Plus 的 el-splitter 做可拖拽分栏------比手写 mousemove 监听省头发🐶:

vue 复制代码
<el-splitter
  ref="layoutSplitter"
  :layout="layoutHorizontal ? 'horizontal' : 'vertical'"
  @resize="onSidebarResize"
  @resize-end="notifyChatHostLayout"
>
  <!-- 左:账号栏 -->
  <el-splitter-panel :size="sidebarSize" :min="252">
    <AccountSidebar
      @platform-change="onPlatformChange"
      @account-select="onAccountSelect"
      @open-translate-drawer="openTranslateDrawer"
    />
  </el-splitter-panel>

  <!-- 右:中间 + 右侧工具 -->
  <el-splitter-panel min="50%">
    <el-main>
      <ChatHost
        v-for="host in visibleChatHosts"
        :key="host.id"
        :account="host"
        v-show="host.isActive"
      />
      <DashboardView v-show="workspaceView === 'dashboard'" />
    </el-main>

    <el-aside v-show="workspaceView !== 'dashboard'">
      <ToolSidebar
        :active-account="selectedAccount"
        :collapsed="toolPanelCollapsed"
        @close="closeToolPanel"
      />
    </el-aside>
  </el-splitter-panel>
</el-splitter>

为什么中间和右工具在同一个 splitter-panel 里?

左栏独立拖拽宽度;中间 webview 和右侧翻译/CRM 工具 共享剩余空间 ,右侧 el-aside 自己再 collapse。

三栏分别干什么

区域 组件(连载名) Day 5 职责
AccountSidebar 平台入口、账号列表、添加 / 休眠 / 激活
ChatHost + 若干 v-show 子页 当前激活 IM 的 webview 容器
ToolSidebar 翻译、快捷回复、客户备注等 Tab

Pinia:谁 active、当前在哪个「子页面」

三栏联动靠 Pinia,少层层 props

1. 账号列表 --- useAccountStore

typescript 复制代码
export const useAccountStore = defineStore('accounts', () => {
  const accounts = ref<ChatAccount[]>([])
  const loading = ref(true)

  function setAccounts(list: ChatAccount[]) {
    loading.value = false
    accounts.value = list
  }

  return { accounts, loading, setAccounts }
})

登录后拉「我的 IM 实例」写进 accounts。每项大致含:idplatformKeytelegram / whatsapp...)、isActiveisSleeping

2. 当前挂载哪些 ChatHost --- visibleChatHosts

typescript 复制代码
const accountStore = useAccountStore()
const mountBatchLimit = ref(3)

const visibleChatHosts = computed(() => {
  return accountStore.accounts
    .filter((a) => !a.pendingReload)
    .filter((a) => !a.isSleeping)
    .filter((a) => a.isActive || a.wasOpened)
    .slice(0, mountBatchLimit.value)
})

点左侧账号 → 对应项 isActive = true → 中间 ChatHost 显示。休眠账号不进列表,省内存。

3. 中间显示聊天还是设置 --- useWorkspaceStore

typescript 复制代码
export const useWorkspaceStore = defineStore('workspace', () => {
  // 'dashboard' | 'chat' | 'settings' | ...
  const view = ref<'dashboard' | 'chat' | 'settings'>('dashboard')
  return { view }
})

view === 'dashboard' 出首页仪表盘;'chat' 出 IM 工作区;'settings' 切系统设置------同一 WorkspaceViewv-show 切换,不必每个设置页单独加子路由,桌面应用里很常见。

登录成功后会清掉残留 isActive,并 view = 'dashboard',避免一进来闪上次打开的 Telegram。

Splitter 拖拽:左栏宽度与 webview 尺寸

拖左栏时两件事:

  1. 紧凑模式 :宽度小于阈值 → sidebarCompact = true,只露图标
  2. 通知 webview 重排 :Split 结束调 notifyChatHostLayout(),否则 IM Web 版布局会歪
typescript 复制代码
const sidebarCompact = ref(true)
const sidebarSize = ref(252)

function onSidebarResize(_index: number, sizes: number[]) {
  sidebarCompact.value = sizes[0] <= 265
  sidebarSize.value = Math.max(sizes[0], 252)
  // translateDrawer 的 left/top 跟随 sidebarSize ...
}

竖版布局时左栏变顶栏,sidebarSize 最小高度约 90px------有的客服用竖屏,我们留了 layoutHorizontal 开关。

启动遮罩:别让用户看布局抖三秒

bootLoading 为 true 时盖一层「加载中」:AccountSidebar 拉完平台 + 账号列表后触发 onAccountsReady,再隐藏。否则用户看见 Splitter 从 0 宽度弹出来------像未完成的 PPT

踩坑与思考

  • 一次 mount 太多 ChatHost :即使用 v-show 也吃内存。mountBatchLimit 渐进加载 + isSleeping 休眠
  • v-show vs v-if :常切换用 v-show 保登录态;彻底卸载用 pendingReload 过滤------别混用把 Cookie 弄丢
  • Splitter 最小宽度 252 :改主题时同步改 onSidebarResize 里的阈值,否则左栏「折叠了还能拖出 1px 缝」
  • 翻译抽屉 vs 右栏动画:两套 UI 状态可能互斥,打开抽屉时记得收右栏

明日预告

ChatHost.vue 里写第一个 <webview>,解析 preload 路径,让 Telegram Web 第一次在 AnchorChat 中间栏亮起来------至少能看见登录二维码。

相关推荐
用户1733598075372 小时前
Vue 3 SPA 首屏优化:从 3s 到 1.2s 的 5 个实践
前端·vue.js
Mahut19 小时前
我用 Electron + FFmpeg 做了一个本地视频处理工作站 ClipForge
前端·ffmpeg·electron
锋行天下20 小时前
我试图优化 Vite 的拆包,结果首屏慢了 10 倍
前端·vue.js·架构
ZhengEnCi1 天前
Q02-Vue-React-index.html完全指南
vue.js·react.js·html
晴虹1 天前
vue3-scroll-more:横向滚动条-元素或页签过多滚动显示处理的组件
前端·vue.js
Forever7_1 天前
尤雨溪转发:Vue-tui 0.1 发布!Vue 终于杀进终端!
vue.js
dkbnull1 天前
Vue 虚拟 DOM Diff 算法与 key 机制原理
vue.js
前端切图崽_小郭2 天前
虚拟滚动:静态 vs 动态的核心差异与实现?
vue.js
白鲸开源2 天前
Apache SeaTunnel Zeta Engine 的 Basic Auth 是怎么工作的?
java·vue.js·github