186 lines
6.8 KiB
TypeScript
186 lines
6.8 KiB
TypeScript
export type MeetingStatus =
|
||
| 'pending'
|
||
| 'uploading'
|
||
| 'transcribing'
|
||
| 'summarizing'
|
||
| 'done'
|
||
| 'failed'
|
||
|
||
export type Meeting = {
|
||
id: string
|
||
title: string
|
||
date: string // ISO
|
||
duration: number // seconds
|
||
participants?: string[]
|
||
status: MeetingStatus
|
||
progress?: number // 0-100
|
||
chunksDone?: number
|
||
chunksTotal?: number
|
||
summary?: {
|
||
keyPoints: string[]
|
||
todos: { text: string; owner?: string; due?: string }[]
|
||
decisions: string[]
|
||
keywords: string[]
|
||
preview: string
|
||
}
|
||
transcript?: Array<{
|
||
time: number // seconds
|
||
speaker: string
|
||
text: string
|
||
}>
|
||
}
|
||
|
||
export const mockMeetings: Meeting[] = [
|
||
{
|
||
id: 'm1',
|
||
title: '与客户对齐定价',
|
||
date: '2026-04-13T14:30:00',
|
||
duration: 4980,
|
||
participants: ['张三', '李四', '客户王总'],
|
||
status: 'transcribing',
|
||
progress: 60,
|
||
chunksDone: 3,
|
||
chunksTotal: 5,
|
||
},
|
||
{
|
||
id: 'm2',
|
||
title: '团队周会',
|
||
date: '2026-04-13T10:00:00',
|
||
duration: 2700,
|
||
participants: ['张三', '李四', '王五'],
|
||
status: 'done',
|
||
summary: {
|
||
keyPoints: [
|
||
'A 项目完成 80%,进入最后测试阶段',
|
||
'B 项目本周进入功能测试',
|
||
'下周聚焦 A 项目上线准备,需对齐运营',
|
||
],
|
||
todos: [
|
||
{ text: '整理 A 项目上线 checklist', owner: '张三', due: '周五前' },
|
||
{ text: '协调运营团队对接会', owner: '李四', due: '明天' },
|
||
{ text: 'B 项目性能压测报告', owner: '王五' },
|
||
],
|
||
decisions: [
|
||
'A 项目上线时间定为下周三',
|
||
'B 项目延后一周,优先保质量',
|
||
],
|
||
keywords: ['周会', '进度', '上线', 'A项目', 'B项目'],
|
||
preview:
|
||
'本周关键进展:A 项目完成 80%,进入最后测试阶段;B 项目本周进入功能测试。下周聚焦 A 项目上线准备,需要协调运营团队对接...',
|
||
},
|
||
},
|
||
{
|
||
id: 'm3',
|
||
title: '产品 Review',
|
||
date: '2026-04-12T16:00:00',
|
||
duration: 3120,
|
||
participants: ['产品经理', '设计师', '工程团队'],
|
||
status: 'done',
|
||
summary: {
|
||
keyPoints: [
|
||
'确定 Q2 产品路线图三个重点方向',
|
||
'移动端体验优化优先级最高',
|
||
'AI 功能纳入 Q3 规划',
|
||
],
|
||
todos: [
|
||
{ text: '出 Q2 PRD 初稿', owner: '产品经理', due: '下周一' },
|
||
{ text: '设计 Mobile 重构方案', owner: '设计师' },
|
||
],
|
||
decisions: ['Q2 聚焦移动端', 'AI 功能推迟到 Q3'],
|
||
keywords: ['产品', 'Q2', '路线图', '移动端', 'AI'],
|
||
preview:
|
||
'讨论 Q2 产品路线图,确定三个重点:1) 移动端体验优化作为最高优先级;2) 后台管理 UI 重构;3) AI 辅助功能纳入 Q3 规划...',
|
||
},
|
||
},
|
||
{
|
||
id: 'm4',
|
||
title: '架构评审 · API 网关方案',
|
||
date: '2026-04-11T14:00:00',
|
||
duration: 5400,
|
||
participants: ['技术负责人', '后端团队'],
|
||
status: 'done',
|
||
summary: {
|
||
keyPoints: [
|
||
'选型确定 Kong + 自研插件方案',
|
||
'Rate limiting 复用 Redis 集群',
|
||
'灰度发布走 Canary',
|
||
],
|
||
todos: [
|
||
{ text: '写 Kong POC', owner: '小明', due: '下周五' },
|
||
{ text: '梳理现有 API 清单', owner: '小红' },
|
||
],
|
||
decisions: ['不走 Envoy,Kong 生态更成熟', '灰度走 Canary 5%/25%/50%/100%'],
|
||
keywords: ['架构', 'API网关', 'Kong', '灰度', 'Redis'],
|
||
preview:
|
||
'今天主要对齐 API 网关的选型方向,在 Kong 和 Envoy 之间做了对比。最终决定 Kong + 自研插件,主要考虑到 Kong 的生态更成熟,团队学习成本更低...',
|
||
},
|
||
},
|
||
{
|
||
id: 'm5',
|
||
title: '用户访谈 · A 类客户',
|
||
date: '2026-04-10T15:30:00',
|
||
duration: 3600,
|
||
status: 'failed',
|
||
},
|
||
]
|
||
|
||
export function getMeeting(id: string): Meeting | undefined {
|
||
return mockMeetings.find((m) => m.id === id)
|
||
}
|
||
|
||
export function formatDuration(seconds: number): string {
|
||
const h = Math.floor(seconds / 3600)
|
||
const m = Math.floor((seconds % 3600) / 60)
|
||
const s = seconds % 60
|
||
if (h > 0) return `${h}h ${m}min`
|
||
return `${m}min`
|
||
}
|
||
|
||
export function formatTime(seconds: number): string {
|
||
const h = Math.floor(seconds / 3600)
|
||
const m = Math.floor((seconds % 3600) / 60)
|
||
const s = Math.floor(seconds % 60)
|
||
if (h > 0) {
|
||
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
|
||
}
|
||
return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
|
||
}
|
||
|
||
export function groupByDate(meetings: Meeting[]): Record<string, Meeting[]> {
|
||
const groups: Record<string, Meeting[]> = {}
|
||
for (const m of meetings) {
|
||
const day = m.date.slice(0, 10)
|
||
if (!groups[day]) groups[day] = []
|
||
groups[day].push(m)
|
||
}
|
||
return groups
|
||
}
|
||
|
||
export function dateLabel(day: string): string {
|
||
const today = new Date()
|
||
const todayStr = today.toISOString().slice(0, 10)
|
||
const yesterday = new Date(today)
|
||
yesterday.setDate(today.getDate() - 1)
|
||
const yesterdayStr = yesterday.toISOString().slice(0, 10)
|
||
|
||
if (day === todayStr) return '今天 · ' + day
|
||
if (day === yesterdayStr) return '昨天 · ' + day
|
||
return day
|
||
}
|
||
|
||
export const mockTranscript = [
|
||
{ time: 0, speaker: '张三', text: '大家好,今天主要是对齐一下 Q2 的定价方案,pipeline 基本已经 ready 了。' },
|
||
{ time: 15, speaker: '李四', text: 'OK, 我这边准备了三个档位的方案,分别是 Basic / Pro / Ultra,对应不同的客户规模。' },
|
||
{ time: 35, speaker: '客户王总', text: '我们比较关心 Pro 档的 SLA 具体怎么定义,还有超出 quota 之后的处理方式。' },
|
||
{ time: 58, speaker: '李四', text: 'Pro 档 SLA 我们承诺 99.9% 可用性,超出 quota 按标准价格 0.01 per request 计费。' },
|
||
{ time: 90, speaker: '张三', text: '关于 SLA,我建议我们再明确一下 downtime 的定义,尤其是计划内维护是否算。' },
|
||
{ time: 110, speaker: '客户王总', text: '同意,计划内维护应该提前通知,并且不计入 SLA。' },
|
||
{ time: 130, speaker: '李四', text: '好的,这块我回去更新一下合同模板,会议纪要里也会明确写出来。' },
|
||
{ time: 155, speaker: '张三', text: '另外 Pro 档的折扣,我们能给到什么力度?客户这边期望是 15%。' },
|
||
{ time: 180, speaker: '客户王总', text: '15% 是我们的底线,毕竟首年采购量会比较大。' },
|
||
{ time: 205, speaker: '李四', text: '10% 是我们目前政策能给到的上限,15% 需要走特批流程。' },
|
||
{ time: 230, speaker: '张三', text: '那就走特批,客户关系比单次毛利重要。我今天去找 CEO 签字。' },
|
||
{ time: 260, speaker: '客户王总', text: '感谢。那就这样,合同草稿我们希望周五之前能收到。' },
|
||
{ time: 280, speaker: '李四', text: '没问题,周五前一定出。' },
|
||
]
|