feat: remaining frontend views (groups/calls/sms/sync) + messageout media fields
Backend (Part A): - MessageOut: add file_th_url, file_hd_url, duration, transcription_text, md5 (room_id + sender_head_url already existed) so the chat timeline can render image thumbnails, voice duration/transcription, and avatars from a single response. - PhoneCallOut: add record_file so the Calls view can offer recording playback. - SmsOut: add content (full body) so the SMS detail dialog shows the whole text. - Tests: red->green for each new field (media fields default None + populated, record_file round-trip, content round-trip). Suite 267 -> 271. Frontend (Part B): - api/index.ts: wrappers getGroups/getGroupMembers/getCalls/getSms/getSyncStatus + Group/GroupMember/PhoneCall/Sms/SyncState/SyncRun/SyncStatus types in types.ts (mirror the *Out pydantic schemas). - views/GroupList.vue (/groups): table + channel filter + pagination; row click opens the group timeline. - views/CallList.vue (/calls): table + call-type filter (phone_call/voice_call/ all); "播放录音" action opens an el-dialog with <audio controls>. - views/SmsList.vue (/sms): table; row click opens a detail dialog with the full content. - views/SyncStatus.vue (/sync): two tables (states + recent_runs) with a manual refresh; run status color-coded. - ChatTimeline.vue: reused for both 1:1 DM (talkerId) and group (roomId) by branching the query; group title + per-member sender already handled by MessageBubble. - router: add /groups /calls /sms /sync + /group/:channel/:ownerId/:roomId. - layouts/MainLayout.vue: replace the single 客户列表 button with an el-menu (客户 / 群 / 通话 / 短信 / 同步); org tree kept on the left. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,8 @@ import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
|
||||
|
||||
// `/` redirects to `/customers`. Chat timeline params are path segments so the
|
||||
// URL is shareable + survives refresh:
|
||||
// /chat/:channel/:ownerId/:talkerId
|
||||
// /chat/:channel/:ownerId/:talkerId (1:1 DM)
|
||||
// /group/:channel/:ownerId/:roomId (group chat — same component, room_id)
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/',
|
||||
@@ -14,6 +15,30 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () => import('@/views/CustomerList.vue'),
|
||||
meta: { title: '客户列表' },
|
||||
},
|
||||
{
|
||||
path: '/groups',
|
||||
name: 'groups',
|
||||
component: () => import('@/views/GroupList.vue'),
|
||||
meta: { title: '群列表' },
|
||||
},
|
||||
{
|
||||
path: '/calls',
|
||||
name: 'calls',
|
||||
component: () => import('@/views/CallList.vue'),
|
||||
meta: { title: '通话记录' },
|
||||
},
|
||||
{
|
||||
path: '/sms',
|
||||
name: 'sms',
|
||||
component: () => import('@/views/SmsList.vue'),
|
||||
meta: { title: '短信记录' },
|
||||
},
|
||||
{
|
||||
path: '/sync',
|
||||
name: 'sync',
|
||||
component: () => import('@/views/SyncStatus.vue'),
|
||||
meta: { title: '同步状态' },
|
||||
},
|
||||
{
|
||||
path: '/chat/:channel/:ownerId/:talkerId',
|
||||
name: 'chat',
|
||||
@@ -21,6 +46,15 @@ const routes: RouteRecordRaw[] = [
|
||||
props: true,
|
||||
meta: { title: '聊天记录' },
|
||||
},
|
||||
{
|
||||
// Group chat timeline — same component, but with a `roomId` prop so it
|
||||
// issues a room_id query instead of a talker_id query.
|
||||
path: '/group/:channel/:ownerId/:roomId',
|
||||
name: 'group-chat',
|
||||
component: () => import('@/views/ChatTimeline.vue'),
|
||||
props: true,
|
||||
meta: { title: '群聊记录' },
|
||||
},
|
||||
{
|
||||
// Anything else → back to the list.
|
||||
path: '/:pathMatch(.*)*',
|
||||
|
||||
Reference in New Issue
Block a user