Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

语音日历 (Voice Calendar)

一款智能语音驱动的日程管理应用。支持语音/文字输入创建日程、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 环形进度条,工作/休息/暂停三色区分
  • 完成后自动在日历记录专注时段
  • 日/周专注时长统计(本地存储)

AI 助手「小历」

  • 基于 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

1. 初始化数据库

在 MySQL 中执行项目根目录下的 init.sql

mysql -u root -p < init.sql

或者将 init.sql 内容复制到 MySQL 客户端中执行。

2. 配置后端

编辑 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。

3. 启动后端

cd springboot
mvn spring-boot:run

后端运行在 http://localhost:9090

4. 启动前端

cd vue
npm install
npm run dev

前端开发服务器默认运行在 http://localhost:5173

5. 访问应用

浏览器打开 http://localhost:5173,注册账号后即可使用。

使用指南

日程操作

  • 创建日程:在日历上点击时间槽,或在 AI 助手中说"明天下午三点开会"
  • 修改日程:点击已有事件编辑,或对 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发展带来的新机遇。

About

一个可以用语音操作的智能日历

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages