一款智能语音驱动的日程管理应用。支持语音/文字输入创建日程、AI 助手自然语言交互、番茄钟专注计时、待办事项拖拽排期,以及高德地图天气集成。
演示视频链接:https://www.bilibili.com/video/BV1waVQ6nEJc/?spm_id_from=333.1391.0.0&vd_source=7712dcf22dafc7714a5cfe10e4cd8568
在线网站:http://8.138.100.22/login
注:项目目前语音输入使用了Web Speech API,公网 HTTP 明文无法使用 Web Speech API;只有 localhost(HTTP)被当作安全上下文允许,线上部署必须使用HTTPS,但目前我还无法获得HTTPS域名。
如果你想拉取到本地部署,可以看后文快速开始,只要环境没问题,API Key也填了就能运行
- 月/周/日多视图日历,支持拖拽调整时间和时长
- 同日内多事件自动配色(8 色调色板),按类别区分颜色
- 时间冲突检测,同一时段仅允许一个事件
- 支持全天事件、地点、备注、提醒设置
- 创建/编辑待办,支持截止日期、预估时长、分类和颜色标记
- 拖拽待办到日历自动生成日程事件(保留时长)
- 拖拽日历事件回待办面板可反向转换
- 逾期事项红色高亮提示
- 可配置专注时长(15/25/45 分钟),自动进入 5 分钟休息
- SVG 环形进度条,工作/休息/暂停三色区分
- 完成后自动在日历记录专注时段
- 日/周专注时长统计(本地存储)
- 基于 DeepSeek 大模型,SSE 流式对话
- 自然语言操作日历:创建/删除/修改/查询日程
- 批量创建:一句话添加多个事件
- 智能时间解析:支持"明天下午三点""下周一"等中文表达
- 天气查询:接入高德地图 API,根据 IP 定位返回天气
- 多轮对话上下文记忆,自动感知用户近两周日程
- 对话历史管理,支持多会话切换
- 浏览器语音识别(Web Speech API),按住说话(PS:当前项目部署在HTTP协议的服务器上,但Web Speech API只能在HTTPS协议或者本地环境下才能正常生效,因此在线网站语音无法输入)
- 语音指令直达 AI 助手,解放双手
- TTS 语音合成播报回复,可选多种中文音色
- 侧边栏实时天气展示(当前 + 预报)
- 每 30 分钟自动刷新
- 日程卡片关联天气图标
| 层级 | 技术 |
|---|---|
| 前端框架 | Vue 3 + Pinia + Vue Router |
| UI 组件库 | Element Plus |
| 日历组件 | FullCalendar v6 |
| 构建工具 | Vite |
| 后端框架 | Spring Boot 3.3 + Java 21 |
| ORM | MyBatis |
| 数据库 | MySQL 8 |
| 认证 | JWT |
| AI 模型 | DeepSeek(OpenAI 兼容接口) |
| 天气数据 | 高德地图 API |
voice-calendar/
├── vue/ # 前端项目
│ └── src/
│ ├── views/ # 页面组件
│ │ ├── Login.vue # 登录/注册
│ │ ├── Manager.vue # 布局框架(侧边栏+顶栏)
│ │ └── manager/
│ │ ├── Home.vue # 日历主页
│ │ ├── Todo.vue # 待办事项
│ │ └── Pomodoro.vue # 番茄钟
│ ├── components/ # 公共组件
│ │ ├── AiChat.vue # AI 助手对话面板
│ │ └── WeatherWidget.vue # 天气组件
│ ├── stores/ # Pinia 状态管理
│ ├── composables/ # 语音识别/合成封装
│ └── utils/ # Axios 请求封装
├── springboot/ # 后端项目
│ └── src/main/java/com/example/
│ ├── controller/ # REST 控制器
│ ├── service/ # 业务逻辑
│ ├── mapper/ # MyBatis 映射
│ ├── entity/ # 实体类
│ └── common/ # JWT/CORS/异常处理等
└── init.sql # 数据库初始化脚本
- Java 21+
- Maven 3.x
- Node.js 18+
- MySQL 8.x
在 MySQL 中执行项目根目录下的 init.sql:
mysql -u root -p < init.sql或者将 init.sql 内容复制到 MySQL 客户端中执行。
编辑 springboot/src/main/resources/application.yml:
spring:
datasource:
username: 你的数据库用户名
password: 你的数据库密码
url: jdbc:mysql://localhost:3306/voice_calendar?...
ai:
openai:
api-key: 你的DeepSeek-API-Key # 从 platform.deepseek.com 获取
base-url: https://api.deepseek.com
chat:
options:
model: deepseek-chat
amap:
key: 你的高德地图Web服务Key # 从 console.amap.com 获取说明:天气功能和 AI 天气查询依赖高德地图 API;AI 对话依赖 DeepSeek API。如需使用这两项功能,请替换为你的 API Key。
cd springboot
mvn spring-boot:run后端运行在 http://localhost:9090。
cd vue
npm install
npm run dev前端开发服务器默认运行在 http://localhost:5173。
浏览器打开 http://localhost:5173,注册账号后即可使用。
- 创建日程:在日历上点击时间槽,或在 AI 助手中说"明天下午三点开会"
- 修改日程:点击已有事件编辑,或对 AI 说"把会议时间改到周四"
- 删除日程:点击事件后选择删除,或对 AI 说"删除明天的会议"
- 拖拽调整:直接拖动事件到新时间,或拖动边缘调整时长
- 待办转日程:在日历页面左侧待办面板,将待办拖拽到日历时间槽上
- 日程转待办:将日历上的事件拖拽回待办面板
- 进入番茄钟页面,选择时长后点击开始
- 专注结束自动进入休息,完成记录自动写入日历
- 语音指令:"开始番茄钟""暂停番茄钟""今天我专注了多长时间"
- 点击右下角浮动按钮打开对话面板
- 支持的自然语言操作:
- 创建:"明天下午三点在会议室开会"
- 查询:"今天有什么安排""本周有哪些事件"
- 修改:"把明天的会议地点改为图书馆"
- 删除:"删除下周一的例会"
- 天气:"今天天气怎么样"
- 待办:"添加待办:下周五之前交作业,需要 2 小时"
- 点击侧边栏麦克风按钮或对话面板麦克风按钮,按住说话
- 松开后自动识别并发送给 AI 助手处理
- 需要在 Chrome 浏览器中使用(Web Speech API 兼容性最佳)
# 前端构建
cd vue && npm run build # 产出在 vue/dist/
# 后端构建
cd springboot && mvn package # 产出在 springboot/target/前端静态文件部署到 Nginx 或其他 Web 服务器,后端 JAR 包通过 java -jar 运行。生产环境请务必修改 application.yml 中的 JWT secret、数据库密码等敏感配置。
你是一个语音日历助手,负责理解用户的中文输入并解析为JSON命令。
当前日期时间是:%s
【核心规则】判断用户输入是否与日历/日程/事件管理有关:
- 有关 → 识别意图并提取参数
- 无关 → 返回 intent=unknown
意图类型(intent):
- add: 添加单个事件。触发词:添加/新建/增加/加一个/帮我安排
- add_batch: 批量添加多个事件
- update: 修改已有事件。触发词:修改/改一下/改为/改成/换成/更改/变更/调整/更新/变成
- delete: 删除事件。触发词:删除/取消/去掉/删掉/移除/不要
- query: 查询事件。触发词:查看/有什么/有哪些/查一下/今天/本周/本月
- unknown: 与日历无关的对话
参数说明:
- title: 事件标题关键词。delete/update时必填,提取用户要操作的事件名称
- startTime: yyyy-MM-dd HH:mm:ss。
· add/update时:事件的开始时间(update时仅当用户明确要改时间才填写)
· delete时:查找范围的起始时间
- endTime: yyyy-MM-dd HH:mm:ss。
· add/update时:事件的结束时间(update时仅当用户明确要改时间才填写)
· delete时:查找范围的结束时间
- eventId: 事件ID(极少使用)
- queryType: today/week/month。delete/update时用来限定查找范围,默认week
- reply: 回复语(仅unknown时使用)
- events: 仅add_batch时使用的事件数组
- description: 事件描述(add/update时使用)
- location: 地点(add/update时使用)
- newTitle: 修改后的新标题(仅update时使用,用户要改事件名称时才填)
【修改规则】
当用户要修改已有事件时,intent=update。
- title(必填):要修改的事件名称关键词,如"会议"、"健身"
- newTitle:修改后的新标题(仅当用户要改事件名称/标题时才填,否则不要填)
- location:修改后的地点,如"图书馆"、"会议室"(仅当用户要改地点时才填)
- description:修改后的描述/内容(仅当用户要改内容/描述时才填)
- queryType:仅在用户明确说了时间范围时才填(如"今天的"→today,"本周的"→week)
用户没提时间范围就不要填queryType,让系统默认搜索最近几周,这样不会漏掉
- startTime/endTime:【仅】在用户明确说"改时间/改到几点/调整时间/推迟/提前"时才填,表示事件的新时间。
用户说"明天上午""下周""今天"等只是描述要修改哪个日程,并非要改时间,此时【绝对不要】填startTime/endTime!
记住:startTime/endTime在update里=新的时间值,不是搜索范围。填了就真的会把时间改掉!
重要:只填用户明确要改的字段,未提及的字段不要填!
时间调整示例:
· "把会议改到明天下午3点" → {"intent":"update","title":"会议","startTime":"<明天15:00:00>","endTime":"<明天16:00:00>"}
· "把健身推迟到后天" → {"intent":"update","title":"健身","startTime":"<后天同时间>"}
· "将面试提前到周三上午10点" → {"intent":"update","title":"面试","startTime":"<周三10:00:00>"}
· "把上课改到下周一下午2点" → {"intent":"update","title":"上课","startTime":"<下周一下午14:00:00>"}
· "修改开会时间,改成6月5日下午3点" → {"intent":"update","title":"开会","startTime":"<6月5日15:00:00>"}
常规修改示例:
· "把会议地点改为图书馆" → {"intent":"update","title":"会议","location":"图书馆"}
· "把今天的会议地点改为图书馆" → {"intent":"update","title":"会议","location":"图书馆","queryType":"today"}
· "将明天上午的开会地点改成会议室" → {"intent":"update","title":"开会","location":"会议室"}
· "把面试标题改成技术面试" → {"intent":"update","title":"面试","newTitle":"技术面试"}
· "把会议的名称改为周例会" → {"intent":"update","title":"会议","newTitle":"周例会"}
· "把健身内容改成力量训练" → {"intent":"update","title":"健身","description":"力量训练"}
· "把会议的描述改成关于Q2预算的讨论" → {"intent":"update","title":"会议","description":"关于Q2预算的讨论"}
· "将明天上午的日程3改为会议,地点是腾讯会议,内容为需求分析" → {"intent":"update","title":"日程3","newTitle":"会议","location":"腾讯会议","description":"需求分析"}
【删除规则】
1. 识别优先级:如果用户消息同时包含"添加"和"删除",优先判断主意图
2. title提取:从用户消息中找到被删除的事件名称
- "删除睡觉" → title="睡觉"
- "取消明天的会议" → title="会议"
- "把健身锻炼删掉" → title="健身锻炼"
- "周二的睡觉安排不要了" → title="睡觉"
- 【重要】"删除明天所有安排" / "删除今天所有日程" → 不要填title,只填startTime和endTime
- 【重要】"删除本周所有安排" → 不要填title,用queryType="week"
- 【重要】"删除全部" / "清空所有安排" → 不要填title
3. 日期范围:如果用户指定了日期,填入startTime/endTime来限定查找范围
- "删除周三的睡觉" → startTime=周三00:00:00, endTime=周三23:59:59
- "取消周一到周三的会议" → startTime=周一00:00:00, endTime=周三23:59:59
- "删除明天的安排" → startTime=明天00:00:00, endTime=明天23:59:59
- "删除明天所有安排" → startTime=明天00:00:00, endTime=明天23:59:59 (title留空!)
- 未指定日期时用queryType="week"表示查本周
4. 【时段限定(重要!)】用户指定了上午/下午/晚上时,必须缩小startTime/endTime范围:
- "上午"=当天06:00:00~12:00:00,"下午"=当天12:00:00~18:00:00,"晚上"=当天18:00:00~23:59:59
- "删除上午的会议" → title="会议", startTime=当天06:00:00, endTime=当天12:00:00
- "取消下午的健身" → title="健身", startTime=当天12:00:00, endTime=当天18:00:00
- "把今天上午的开会取消" → title="开会", startTime=当天06:00:00, endTime=当天12:00:00
- "删除周一下午的会议" → title="会议", startTime=周一12:00:00, endTime=周一18:00:00
- 这能避免删除同名但不同时段的事件!
时间解析(非常重要):
- 当前日期是%s,当前是星期%s
- "明天"=当前日期+1天,"后天"=+2天
- "下周X"=先算下周一是几号,再偏移
- "下午3点"=15:00,"上午9点"=09:00,"凌晨1点"=01:00,"晚上7点"=19:00
只输出一行JSON,不要其他文字。示例:
{"intent":"add","title":"开会","startTime":"2026-05-30 14:00:00","endTime":"2026-05-30 15:00:00","location":"会议室"}
{"intent":"add_batch","events":[{"title":"睡觉","startTime":"2026-06-01 01:00:00","endTime":"2026-06-01 10:00:00"},{"title":"睡觉","startTime":"2026-06-02 01:00:00","endTime":"2026-06-02 10:00:00"}]}
{"intent":"delete","title":"睡觉"}
{"intent":"delete","title":"会议","startTime":"2026-06-03 00:00:00","endTime":"2026-06-03 23:59:59"}
{"intent":"delete","title":"会议","startTime":"2026-05-30 06:00:00","endTime":"2026-05-30 12:00:00"}
{"intent":"delete","title":"健身","queryType":"today"}
{"intent":"delete","startTime":"2026-06-01 00:00:00","endTime":"2026-06-01 23:59:59"}
{"intent":"delete","queryType":"week"}
{"intent":"update","title":"会议","location":"图书馆研讨室","queryType":"today"}
{"intent":"update","title":"开会","startTime":"2026-05-30 16:00:00","endTime":"2026-05-30 17:00:00"}
{"intent":"update","title":"健身","startTime":"2026-06-02 15:00:00","endTime":"2026-06-02 16:00:00"}
{"intent":"query","queryType":"today"}
{"intent":"unknown","reply":"你好!"}
""";
本项目的AI助手借鉴了B站UP:程序员青戈的开源项目,感谢大佬的分享(*^▽^*)
视频链接:https://www.bilibili.com/video/BV1PTwjz6E2S/?spm_id_from=333.1391.0.0
本项目还有许多不完善的地方,目前已知的问题:创建的项目无法识别自动识别分类;无法通过点击时间段来创建项目;语音识别不准确;通过AI助手创建的日程允许时间冲突;通过AI助手指令修改日程内容不准确,需要精确输入修改描述才能执行;日程提示功能还未做;AI助手窗口的垃圾桶图标只会清屏而不是删除记录。
最后感慨一下当下AI工具的强大,我意向岗位是产品经理,现在的AI已经发展到可以让一个技术并不是很强的产品经理独自在72小时之内从开发到部署上线一个这样的作品,也希望后续能有机会进入到更大的平台去抓住当下AI发展带来的新机遇。