From fe5dd786328ac1d410bc314523695aac8077c9be Mon Sep 17 00:00:00 2001
From: DEV_DSW <562304744@qq.com>
Date: Wed, 13 May 2026 14:06:43 +0800
Subject: [PATCH] feat: add RecommendationListCard and RoutePlanCard components
with styles and mocks
- Implemented RecommendationListCard component for displaying a list of recommendations with titles, descriptions, and badges.
- Created RoutePlanCard component to show route details, including nodes and tips, with a detailed view toggle.
- Added ScenicImageCard component for showcasing images with optional captions.
- Developed SharedVisual components: CardShell, BadgePill, MediaFrame, ActionRow, and DetailShell for reusable UI elements.
- Introduced SCSS styles for all new components and updated existing styles for consistency.
- Created test page to preview and interact with all components using mock data.
- Added new utility classes for background colors, borders, colors, display, flex, font sizes, font weights, heights, positions, rounded corners, and widths.
---
.gitignore | 1 +
.../14136-1778668898.44814/state/server.pid | 1 +
.../2672-1778668863.69592/state/server.pid | 0
.../30768-1778668817.27797/state/server.err | 0
.../30768-1778668817.27797/state/server.pid | 0
.../content/component-scope-approach.html | 53 +++++
.../3476-1778668926.99711/state/server-info | 1 +
.../43412-1778668874.64192/state/server.pid | 1 +
AGENTS.md | 136 +++++++++++++
src/pages.json | 30 +--
src/pages/ChatModule/AigcPhotoCard/index.vue | 53 +++++
src/pages/ChatModule/AigcPhotoCard/mocks.js | 11 +
.../AigcPhotoCard/styles/index.scss | 35 ++++
.../ChatModule/FacilityLocationCard/index.vue | 60 ++++++
.../ChatModule/FacilityLocationCard/mocks.js | 11 +
.../FacilityLocationCard/styles/index.scss | 58 ++++++
src/pages/ChatModule/FaqHelpCard/index.vue | 60 ++++++
src/pages/ChatModule/FaqHelpCard/mocks.js | 10 +
.../ChatModule/FaqHelpCard/styles/index.scss | 36 ++++
.../ChatModule/LongTextGuideCard/index.vue | 98 +++++++++
.../ChatModule/LongTextGuideCard/mocks.js | 17 ++
.../LongTextGuideCard/styles/index.scss | 58 ++++++
.../LongTextGuideComboCard/index.vue | 49 +++++
.../LongTextGuideComboCard/mocks.js | 22 ++
.../LongTextGuideComboCard/styles/index.scss | 8 +
.../ChatModule/MapNavigationCard/index.vue | 45 +++++
.../ChatModule/MapNavigationCard/mocks.js | 7 +
.../MapNavigationCard/styles/index.scss | 39 ++++
.../MultiPoiRecommendationCard/index.vue | 37 ++++
.../MultiPoiRecommendationCard/mocks.js | 34 ++++
.../styles/index.scss | 10 +
src/pages/ChatModule/NoticeCard/index.vue | 86 ++++++++
src/pages/ChatModule/NoticeCard/mocks.js | 24 +++
.../ChatModule/NoticeCard/styles/index.scss | 60 ++++++
src/pages/ChatModule/PoiCompareCard/index.vue | 85 ++++++++
src/pages/ChatModule/PoiCompareCard/mocks.js | 29 +++
.../PoiCompareCard/styles/index.scss | 56 +++++
src/pages/ChatModule/PoiDetailCard/index.vue | 62 ++++++
src/pages/ChatModule/PoiDetailCard/mocks.js | 14 ++
.../PoiDetailCard/styles/index.scss | 48 +++++
.../RecommendationListCard/index.vue | 58 ++++++
.../RecommendationListCard/mocks.js | 21 ++
.../RecommendationListCard/styles/index.scss | 46 +++++
src/pages/ChatModule/RoutePlanCard/index.vue | 91 +++++++++
src/pages/ChatModule/RoutePlanCard/mocks.js | 14 ++
.../RoutePlanCard/styles/index.scss | 80 ++++++++
.../ChatModule/ScenicImageCard/index.vue | 45 +++++
src/pages/ChatModule/ScenicImageCard/mocks.js | 7 +
.../ScenicImageCard/styles/index.scss | 30 +++
.../ChatModule/SharedVisual/ActionRow.vue | 62 ++++++
.../ChatModule/SharedVisual/BadgePill.vue | 26 +++
.../ChatModule/SharedVisual/CardShell.vue | 41 ++++
.../ChatModule/SharedVisual/DetailShell.vue | 36 ++++
.../ChatModule/SharedVisual/MediaFrame.vue | 33 +++
.../ChatModule/SharedVisual/styles/index.scss | 134 ++++++++++++
src/pages/test/index.vue | 191 ++++++++++++++++++
src/static/scss/background.scss | 42 +++-
src/static/scss/border.scss | 8 +
src/static/scss/colors.scss | 48 +++++
src/static/scss/display.scss | 8 +
src/static/scss/flex.scss | 24 +++
src/static/scss/font-size.scss | 12 ++
src/static/scss/font-weight.scss | 8 +
src/static/scss/height.scss | 44 ++++
src/static/scss/position.scss | 4 +
src/static/scss/rounded.scss | 4 +
src/static/scss/width.scss | 18 +-
67 files changed, 2566 insertions(+), 14 deletions(-)
create mode 100644 .superpowers/brainstorm/14136-1778668898.44814/state/server.pid
create mode 100644 .superpowers/brainstorm/2672-1778668863.69592/state/server.pid
create mode 100644 .superpowers/brainstorm/30768-1778668817.27797/state/server.err
create mode 100644 .superpowers/brainstorm/30768-1778668817.27797/state/server.pid
create mode 100644 .superpowers/brainstorm/3476-1778668926.99711/content/component-scope-approach.html
create mode 100644 .superpowers/brainstorm/3476-1778668926.99711/state/server-info
create mode 100644 .superpowers/brainstorm/43412-1778668874.64192/state/server.pid
create mode 100644 AGENTS.md
create mode 100644 src/pages/ChatModule/AigcPhotoCard/index.vue
create mode 100644 src/pages/ChatModule/AigcPhotoCard/mocks.js
create mode 100644 src/pages/ChatModule/AigcPhotoCard/styles/index.scss
create mode 100644 src/pages/ChatModule/FacilityLocationCard/index.vue
create mode 100644 src/pages/ChatModule/FacilityLocationCard/mocks.js
create mode 100644 src/pages/ChatModule/FacilityLocationCard/styles/index.scss
create mode 100644 src/pages/ChatModule/FaqHelpCard/index.vue
create mode 100644 src/pages/ChatModule/FaqHelpCard/mocks.js
create mode 100644 src/pages/ChatModule/FaqHelpCard/styles/index.scss
create mode 100644 src/pages/ChatModule/LongTextGuideCard/index.vue
create mode 100644 src/pages/ChatModule/LongTextGuideCard/mocks.js
create mode 100644 src/pages/ChatModule/LongTextGuideCard/styles/index.scss
create mode 100644 src/pages/ChatModule/LongTextGuideComboCard/index.vue
create mode 100644 src/pages/ChatModule/LongTextGuideComboCard/mocks.js
create mode 100644 src/pages/ChatModule/LongTextGuideComboCard/styles/index.scss
create mode 100644 src/pages/ChatModule/MapNavigationCard/index.vue
create mode 100644 src/pages/ChatModule/MapNavigationCard/mocks.js
create mode 100644 src/pages/ChatModule/MapNavigationCard/styles/index.scss
create mode 100644 src/pages/ChatModule/MultiPoiRecommendationCard/index.vue
create mode 100644 src/pages/ChatModule/MultiPoiRecommendationCard/mocks.js
create mode 100644 src/pages/ChatModule/MultiPoiRecommendationCard/styles/index.scss
create mode 100644 src/pages/ChatModule/NoticeCard/index.vue
create mode 100644 src/pages/ChatModule/NoticeCard/mocks.js
create mode 100644 src/pages/ChatModule/NoticeCard/styles/index.scss
create mode 100644 src/pages/ChatModule/PoiCompareCard/index.vue
create mode 100644 src/pages/ChatModule/PoiCompareCard/mocks.js
create mode 100644 src/pages/ChatModule/PoiCompareCard/styles/index.scss
create mode 100644 src/pages/ChatModule/PoiDetailCard/index.vue
create mode 100644 src/pages/ChatModule/PoiDetailCard/mocks.js
create mode 100644 src/pages/ChatModule/PoiDetailCard/styles/index.scss
create mode 100644 src/pages/ChatModule/RecommendationListCard/index.vue
create mode 100644 src/pages/ChatModule/RecommendationListCard/mocks.js
create mode 100644 src/pages/ChatModule/RecommendationListCard/styles/index.scss
create mode 100644 src/pages/ChatModule/RoutePlanCard/index.vue
create mode 100644 src/pages/ChatModule/RoutePlanCard/mocks.js
create mode 100644 src/pages/ChatModule/RoutePlanCard/styles/index.scss
create mode 100644 src/pages/ChatModule/ScenicImageCard/index.vue
create mode 100644 src/pages/ChatModule/ScenicImageCard/mocks.js
create mode 100644 src/pages/ChatModule/ScenicImageCard/styles/index.scss
create mode 100644 src/pages/ChatModule/SharedVisual/ActionRow.vue
create mode 100644 src/pages/ChatModule/SharedVisual/BadgePill.vue
create mode 100644 src/pages/ChatModule/SharedVisual/CardShell.vue
create mode 100644 src/pages/ChatModule/SharedVisual/DetailShell.vue
create mode 100644 src/pages/ChatModule/SharedVisual/MediaFrame.vue
create mode 100644 src/pages/ChatModule/SharedVisual/styles/index.scss
create mode 100644 src/pages/test/index.vue
diff --git a/.gitignore b/.gitignore
index b8a25e7..38da8f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,4 @@ dist
unpackage
.hbuilderx
.claude
+docs
\ No newline at end of file
diff --git a/.superpowers/brainstorm/14136-1778668898.44814/state/server.pid b/.superpowers/brainstorm/14136-1778668898.44814/state/server.pid
new file mode 100644
index 0000000..afae555
--- /dev/null
+++ b/.superpowers/brainstorm/14136-1778668898.44814/state/server.pid
@@ -0,0 +1 @@
+15336
diff --git a/.superpowers/brainstorm/2672-1778668863.69592/state/server.pid b/.superpowers/brainstorm/2672-1778668863.69592/state/server.pid
new file mode 100644
index 0000000..e69de29
diff --git a/.superpowers/brainstorm/30768-1778668817.27797/state/server.err b/.superpowers/brainstorm/30768-1778668817.27797/state/server.err
new file mode 100644
index 0000000..e69de29
diff --git a/.superpowers/brainstorm/30768-1778668817.27797/state/server.pid b/.superpowers/brainstorm/30768-1778668817.27797/state/server.pid
new file mode 100644
index 0000000..e69de29
diff --git a/.superpowers/brainstorm/3476-1778668926.99711/content/component-scope-approach.html b/.superpowers/brainstorm/3476-1778668926.99711/content/component-scope-approach.html
new file mode 100644
index 0000000..7bf3577
--- /dev/null
+++ b/.superpowers/brainstorm/3476-1778668926.99711/content/component-scope-approach.html
@@ -0,0 +1,53 @@
+
ChatModule 组件还原范围确认
+我已扫描 14 个 HTML 视觉稿。下面按实现视角分组:相同视觉结构会抽共享基础卡片/媒体/详情页容器,组件只还原 到 之间的内容。
+
+
+
Detected component families
+
+
+
+
内容/详情卡
+
C0 长文本卡、C0+ 组合卡、C13 公告卡:列表态 + 详情态,复用详情页头部、返回、富文本正文、行动区。
+
+
+
+
+
POI / 列表 / 路线
+
C1 POI 详情、C2 列表、C2 多 POI、C3 对比、C4 路线、C5 设施位置:复用卡片壳、标签、图片缩略图、信息行。
+
+
+
+
+
媒体 / 操作卡
+
C7 大图、C8 地图导航、C10 AIGC 合照、C12 FAQ:复用媒体卡壳、底部说明、主按钮/入口行。
+
+
+
+
+
+
+
Recommended implementation direction
+
+
+
A
+
+
Props-first 组件库(推荐)
+
每个新 ChatModule 组件只接收 props,不在组件内拉接口;示例数据放到独立 mock 文件或父级传入,避免硬编码。
+
+
+
+
B
+
+
容器组件 + 展示组件
+
每个视觉组件拆成容器和纯展示两层;适合马上接真实接口,但文件数量和接线工作更多。
+
+
+
+
C
+
+
静态视觉还原优先
+
先按 HTML 固定数据还原,再二次抽数据;最快看见 UI,但违反“mock 数据不要硬编码”的风险最高。
+
+
+
+
diff --git a/.superpowers/brainstorm/3476-1778668926.99711/state/server-info b/.superpowers/brainstorm/3476-1778668926.99711/state/server-info
new file mode 100644
index 0000000..66c49e0
--- /dev/null
+++ b/.superpowers/brainstorm/3476-1778668926.99711/state/server-info
@@ -0,0 +1 @@
+{"type":"server-started","port":63006,"host":"127.0.0.1","url_host":"localhost","url":"http://localhost:63006","screen_dir":"E:\\zn\\YGChatCS\\.superpowers\\brainstorm\\3476-1778668926.99711\\content","state_dir":"E:\\zn\\YGChatCS\\.superpowers\\brainstorm\\3476-1778668926.99711\\state"}
diff --git a/.superpowers/brainstorm/43412-1778668874.64192/state/server.pid b/.superpowers/brainstorm/43412-1778668874.64192/state/server.pid
new file mode 100644
index 0000000..9f35f8e
--- /dev/null
+++ b/.superpowers/brainstorm/43412-1778668874.64192/state/server.pid
@@ -0,0 +1 @@
+10800
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..448d288
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,136 @@
+# YGChatCS 项目速览
+
+本文档面向接手本仓库的 AI 编程助手。开始改代码前请先读这里,再按需深入具体文件。
+
+## 项目定位
+
+YGChatCS 是一个基于 uni-app + Vue 3 的多端应用,当前配置重点面向微信小程序。业务上以酒店/文旅场景的 AI 聊天助手为主,包含首页聊天、发现内容、商品/房型详情、订单、工单服务、快捷预订、WebView 桥接等模块。
+
+## 技术栈
+
+- 框架:uni-app 3、Vue 3、Vite 5。
+- 状态管理:Pinia 3,使用 `pinia-plugin-unistorage` 做持久化。
+- 样式:SCSS,公共工具类在 `src/static/scss/`,全局变量在 `src/uni.scss`。
+- 多端:保留 H5、App、各类小程序脚本;当前微信小程序配置最完整。
+- 组件生态:`src/uni_modules/` 中包含 DCloud 插件和第三方插件,如 `uni-popup`、`uni-icons`、`z-paging`、`zero-markdown-view`。
+
+## 常用命令
+
+包管理器字段声明为 Yarn 1:
+
+```bash
+yarn install
+yarn dev:mp-weixin
+yarn build:mp-weixin
+yarn dev:h5
+yarn build:h5
+yarn switch-client zhinian
+```
+
+`package-lock.json` 和 `yarn.lock` 同时存在。除非任务明确要求,不要随意重生成或混改锁文件;优先遵循 `package.json` 的 `packageManager`。
+
+## 关键入口
+
+- `src/main.js`:创建 Vue/uni-app 应用,注册 Pinia;微信小程序环境注册 `utils/share`。
+- `src/App.vue`:应用生命周期入口,`onLaunch` 会调用 `getEvnUrl()` 获取/设置服务地址,并调用 `refreshToken()` 刷新登录态。
+- `src/pages.json`:页面与分包路由。主页面是 `pages/index/index`,分包包括 `pages-order`、`pages-service`、`pages-quick`、`pages-booking`、`pages-bridge`。
+- `src/manifest.json`:uni-app 多端配置;微信小程序 `appid`、隐私权限、插件配置在这里。
+- `project.config.json`:微信开发者工具项目配置,根级 `appid` 需要与 `src/manifest.json` 保持一致。
+- `vite.config.js`:配置 `@` 指向 `src`,启用 `@dcloudio/vite-plugin-uni`,并对构建资源名做 md5/hash 处理。
+
+## 目录地图
+
+- `src/pages/`:主包页面和聊天相关组件。`pages/index/index.vue` 是首页壳,组合聊天主列表、日历、更多服务和抽屉。
+- `src/pages/ChatMain/`:聊天主体验,包括顶部导航、欢迎区、输入区、消息卡片、长回答页、通知消息等。
+- `src/pages/ChatModule/`:AI 工具调用渲染模块,例如快捷预订、发现卡片、地图、活动、推荐内容、长答案、图片/商品卡等。
+- `src/pages/Discovery/`:发现页内容流和快捷问题。
+- `src/pages/goods/`:商品/房型详情、相册、套餐、设施、日期选择和确认组件。
+- `src/pages-order/`:订单列表、订单详情及订单卡片、二维码、状态、用户信息等组件。
+- `src/pages-service/`:服务/工单订单列表。
+- `src/pages-quick/`:快捷入口列表及卡片。
+- `src/pages-booking/`:预订页面。
+- `src/pages-bridge/`:上传图片、保存图片等桥接页面。
+- `src/pages/webview/`:内嵌 WebView 页面及桥接脚本,`src/router/index.js` 会把外部 URL 包装到这里。
+- `src/components/`:跨页面通用组件,如 `TopNavBar`、`Calender`、`ImageSwiper`、`TagsGroup`、`Stepper`、`CreateServiceOrder`、`Feedback` 等。
+- `src/request/`:请求封装和 API 分组。
+- `src/store/`:Pinia store,模块包括 `app`、`location`、`picture`、`selectedDate`。
+- `src/constant/`:客户端配置、token key、事件常量、业务类型常量。
+- `src/utils/`:WebSocket、流式/打字机、URL 参数、分享、更新等工具。
+- `scripts/`:客户配置切换脚本。
+
+## 请求、环境与登录
+
+- `src/request/base/request.js` 是统一请求封装。相对 URL 会拼接 `useAppStore().serverConfig.baseUrl`,请求头会带 `clientId`、token 和位置信息。
+- `src/request/base/baseUrl.js` 定义生产/测试 HTTP 和 WSS 地址。
+- `src/request/base/config.js` 控制环境地址。`developVersion = true` 时直接使用测试地址;发布前需关注 `versionValue` 和 `developVersion`。
+- `src/request/api/` 按业务拆分 API:登录、主页面数据、会话、商品、订单、工单、反馈、服务地址、文件上传、AI 流式接口等。
+- 登录与 token 逻辑在 `src/hooks/useGoLogin.js`、`src/manager/LoginManager.js`、`src/constant/token.js`。token key 会按当前客户端类型隔离。
+- HTTP 返回 `424` 时请求层会移除 token、发出登出事件,并跳转登录。
+
+## AI 聊天与实时通信
+
+- 主聊天列表在 `src/pages/ChatMain/ChatMainList/index.vue`,文件较大,是聊天状态、消息渲染、WebSocket 初始化和发送的核心。
+- `src/utils/WebSocketManager.js` 是跨端 WebSocket 管理器,负责连接、重连、队列、发送和销毁。
+- `src/request/api/AgentChatStream.js` 是微信小程序端的分块/流式请求封装,使用 `uni.request({ enableChunked: true })` 解析 SSE 风格数据;也提供 `stopAbortTask()` 终止当前请求。
+- AI 返回的 `toolCall.componentName` 会在聊天列表中映射到 `ChatModule` 或通用组件。新增工具卡片时,通常需要同时改组件、渲染分支和数据结构处理。
+
+## 多客户端配置
+
+根目录 `client-configs.json` 保存多客户端配置:`zhinian`、`nianhelper`、`duohua`、`tianmu`。当前 `src/constant/base.js` 的 `getCurrentConfig()` 返回 `CLIENT_CONFIGS.zhinian`。
+
+切换客户配置请使用:
+
+```bash
+yarn switch-client
+```
+
+脚本 `scripts/update-appid.js` 会更新:
+
+- `src/constant/base.js` 的当前客户端。
+- `src/manifest.json` 的 `mp-weixin.appid`。
+- `project.config.json` 的根级 `appid`。
+- `src/uni.scss` 的主题色变量。
+
+改配置时要保证这些文件同步,尤其是微信小程序 appid 与主题色。
+
+## 样式约定
+
+- `.editorconfig` 要求 2 空格、LF、UTF-8、去尾随空格;Markdown 不强制去尾随空格且不强制最终换行。
+- 公共 SCSS 工具类集中在 `src/static/scss/index.scss` 及其拆分文件,很多页面依赖 `flex-*`、`w-*`、`h-*`、`px-*` 等类。
+- 主题色变量在 `src/uni.scss`,会被 `switch-client` 自动改写。不要手工改一处忘记同步配置。
+- 页面基本使用 `navigationStyle: "custom"`,导航栏通常由自定义组件实现。
+
+## 编码与文本注意事项
+
+当前终端读取部分中文注释、README 和字符串时显示为乱码,但项目文件声明为 UTF-8。修改这些文件时:
+
+- 不要因为显示乱码就大面积重写注释或中文文案。
+- 尽量只改任务相关代码块。
+- 涉及中文展示文案时,在编辑器中确认真实编码和页面效果。
+
+## 开发注意事项
+
+- 优先使用 `@/` 别名引入 `src` 下模块。
+- 不要直接改 `dist/`,它是构建产物。
+- 不要随意改 `src/uni_modules/` 中第三方插件源码,除非任务就是修补插件行为。
+- 新增页面必须同步 `src/pages.json`;新增分包页面要放入对应 `subPackages`。
+- 需要持久化的全局状态优先放在 Pinia store,并按现有模块风格使用 `unistorage: true`。
+- 网络请求优先新增到 `src/request/api/`,复用 `src/request/base/request.js`,不要在页面里散落裸 `uni.request`,流式/分块场景除外。
+- 跳转外部 H5 优先使用 `src/router/index.js` 的 WebView 包装方式。
+- 需要登录态的交互参考 `checkToken()`、`goLogin()` 和现有事件常量。
+
+## 验证建议
+
+仓库当前没有明确的测试脚本。完成修改后至少做对应平台构建或运行:
+
+```bash
+yarn build:mp-weixin
+```
+
+如果只改 H5 可补充:
+
+```bash
+yarn build:h5
+```
+
+涉及客户切换时,运行 `yarn switch-client ` 后检查 `src/manifest.json`、`project.config.json`、`src/constant/base.js`、`src/uni.scss` 的同步变化。
diff --git a/src/pages.json b/src/pages.json
index 359998d..54d45b4 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -1,16 +1,16 @@
{
"pages": [
- {
- "path": "pages/index/index",
- "style": {
- "navigationStyle": "custom",
- "disableScroll": true,
- "app-plus": {
- "bounce": "none",
- "scrollIndicator": "none"
- }
- }
- },
+ {
+ "path": "pages/index/index",
+ "style": {
+ "navigationStyle": "custom",
+ "disableScroll": true,
+ "app-plus": {
+ "bounce": "none",
+ "scrollIndicator": "none"
+ }
+ }
+ },
{
"path": "pages/login/index",
"style": {
@@ -49,6 +49,12 @@
"style": {
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "pages/test/index",
+ "style": {
+ "navigationStyle": "custom"
+ }
}
],
"subPackages": [
@@ -127,4 +133,4 @@
"backgroundColor": "#F8F8F8"
},
"uniIdRouter": {}
-}
+}
diff --git a/src/pages/ChatModule/AigcPhotoCard/index.vue b/src/pages/ChatModule/AigcPhotoCard/index.vue
new file mode 100644
index 0000000..fa49b33
--- /dev/null
+++ b/src/pages/ChatModule/AigcPhotoCard/index.vue
@@ -0,0 +1,53 @@
+
+
+
+ {{ data.icon }}
+
+ {{ data.title }}
+ {{ data.subtitle }}
+
+
+
+
+
+
+ {{ data.buttonText }}
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/AigcPhotoCard/mocks.js b/src/pages/ChatModule/AigcPhotoCard/mocks.js
new file mode 100644
index 0000000..92bc76c
--- /dev/null
+++ b/src/pages/ChatModule/AigcPhotoCard/mocks.js
@@ -0,0 +1,11 @@
+export default {
+ icon: "AI",
+ title: "生成一张景区合照",
+ subtitle: "上传照片后生成同框纪念照",
+ buttonText: "开始生成",
+ images: [
+ "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=400&q=80",
+ "https://images.unsplash.com/photo-1529156069898-49953e39b3ac?auto=format&fit=crop&w=400&q=80",
+ "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=400&q=80",
+ ],
+};
diff --git a/src/pages/ChatModule/AigcPhotoCard/styles/index.scss b/src/pages/ChatModule/AigcPhotoCard/styles/index.scss
new file mode 100644
index 0000000..aa2dade
--- /dev/null
+++ b/src/pages/ChatModule/AigcPhotoCard/styles/index.scss
@@ -0,0 +1,35 @@
+.aigc-photo-card {
+ background: linear-gradient(145deg, #ffffff 0%, #f0fdfa 100%);
+}
+
+.aigc-photo-card__body {
+}
+
+.aigc-photo-card__icon {
+ color: #0f766e;
+}
+
+.aigc-photo-card__content {
+ min-width: 0;
+}
+
+.aigc-photo-card__title {
+ color: #134e4a;
+}
+
+.aigc-photo-card__subtitle {
+ margin-top: 5px;
+}
+
+.aigc-photo-card__preview {
+}
+
+.aigc-photo-card__image {
+}
+
+.aigc-photo-card__button {
+}
+
+.aigc-photo-card__button.is-disabled {
+ opacity: 0.55;
+}
diff --git a/src/pages/ChatModule/FacilityLocationCard/index.vue b/src/pages/ChatModule/FacilityLocationCard/index.vue
new file mode 100644
index 0000000..d9b0a00
--- /dev/null
+++ b/src/pages/ChatModule/FacilityLocationCard/index.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+ {{ data.title }}
+
+
+
+ {{ data.description }}
+
+
+
+
+ {{ item.name }}
+ {{ item.meta }}
+
+ {{ item.distance }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/FacilityLocationCard/mocks.js b/src/pages/ChatModule/FacilityLocationCard/mocks.js
new file mode 100644
index 0000000..44cbdf6
--- /dev/null
+++ b/src/pages/ChatModule/FacilityLocationCard/mocks.js
@@ -0,0 +1,11 @@
+export default {
+ title: "附近设施位置",
+ badge: "设施引导",
+ cover: "https://images.unsplash.com/photo-1518005020951-eccb494ad742?auto=format&fit=crop&w=900&q=80",
+ description: "根据你当前位置推荐最近的服务点和补给点。",
+ facilities: [
+ { id: "f1", name: "游客中心", meta: "咨询、补给、卫生间", distance: "120m" },
+ { id: "f2", name: "观光车站", meta: "前往水上森林", distance: "260m" },
+ { id: "f3", name: "咖啡轻食", meta: "可短暂停留", distance: "360m" },
+ ],
+};
diff --git a/src/pages/ChatModule/FacilityLocationCard/styles/index.scss b/src/pages/ChatModule/FacilityLocationCard/styles/index.scss
new file mode 100644
index 0000000..a6a438c
--- /dev/null
+++ b/src/pages/ChatModule/FacilityLocationCard/styles/index.scss
@@ -0,0 +1,58 @@
+.facility-location-card__hero {
+ position: relative;
+ padding: 8px;
+}
+
+.facility-location-card__image {
+}
+
+.facility-location-card__overlay {
+ position: absolute;
+ left: 20px;
+ right: 20px;
+ bottom: 20px;
+}
+
+.facility-location-card__title {
+ margin-top: 8px;
+ color: #fff;
+ font-size: 18px;
+ font-weight: 900;
+ text-shadow: 0 2px 8px rgba(0, 0, 0, 0.24);
+}
+
+.facility-location-card__body {
+ padding-top: 6px;
+}
+
+.facility-location-card__desc {
+ line-height: 20px;
+}
+
+.facility-location-card__list {
+ margin-top: 14px;
+}
+
+.facility-location-card__item {
+ padding: 11px 0;
+}
+
+.facility-location-card__dot {
+ width: 8px;
+ height: 8px;
+ background: #06b6d4;
+}
+
+.facility-location-card__info {
+ min-width: 0;
+}
+
+.facility-location-card__name {
+}
+
+.facility-location-card__meta {
+ margin-top: 3px;
+}
+
+.facility-location-card__distance {
+}
diff --git a/src/pages/ChatModule/FaqHelpCard/index.vue b/src/pages/ChatModule/FaqHelpCard/index.vue
new file mode 100644
index 0000000..6adbbde
--- /dev/null
+++ b/src/pages/ChatModule/FaqHelpCard/index.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+ {{ item.question }}
+ {{ openedId === item.id ? "−" : "+" }}
+
+
+ {{ item.answer }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/FaqHelpCard/mocks.js b/src/pages/ChatModule/FaqHelpCard/mocks.js
new file mode 100644
index 0000000..e2778b7
--- /dev/null
+++ b/src/pages/ChatModule/FaqHelpCard/mocks.js
@@ -0,0 +1,10 @@
+export default {
+ title: "常见问题",
+ subtitle: "快速了解游览和服务信息",
+ badge: "FAQ",
+ questions: [
+ { id: "q1", question: "景区几点停止入园?", answer: "通常闭园前 1 小时停止入园,节假日以现场公告为准。" },
+ { id: "q2", question: "可以携带宠物吗?", answer: "多数室内区域不支持宠物进入,导盲犬等服务犬除外。" },
+ { id: "q3", question: "下雨还能游玩吗?", answer: "小雨可游览,雨后水景更好,但请注意栈道湿滑。" },
+ ],
+};
diff --git a/src/pages/ChatModule/FaqHelpCard/styles/index.scss b/src/pages/ChatModule/FaqHelpCard/styles/index.scss
new file mode 100644
index 0000000..f60d7be
--- /dev/null
+++ b/src/pages/ChatModule/FaqHelpCard/styles/index.scss
@@ -0,0 +1,36 @@
+.faq-help-card {
+}
+
+.faq-help-card__header {
+ margin-bottom: 12px;
+}
+
+.faq-help-card__title {
+}
+
+.faq-help-card__subtitle {
+ margin-top: 4px;
+}
+
+.faq-help-card__list {
+}
+
+.faq-help-card__item {
+}
+
+.faq-help-card__item.is-open {
+ background: #eff6ff;
+}
+
+.faq-help-card__question {
+}
+
+.faq-help-card__arrow {
+ font-size: 18px;
+ line-height: 14px;
+}
+
+.faq-help-card__answer {
+ margin-top: 8px;
+ line-height: 20px;
+}
diff --git a/src/pages/ChatModule/LongTextGuideCard/index.vue b/src/pages/ChatModule/LongTextGuideCard/index.vue
new file mode 100644
index 0000000..dbbbb6d
--- /dev/null
+++ b/src/pages/ChatModule/LongTextGuideCard/index.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+ {{ item.title }}
+ {{ item.summary }}
+
+
+
+
+
+
+
+
+ {{ section.title }}
+ {{ section.content }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/LongTextGuideCard/mocks.js b/src/pages/ChatModule/LongTextGuideCard/mocks.js
new file mode 100644
index 0000000..2ca5ec1
--- /dev/null
+++ b/src/pages/ChatModule/LongTextGuideCard/mocks.js
@@ -0,0 +1,17 @@
+export default {
+ cards: [
+ {
+ id: "guide-waterfall",
+ badge: "攻略",
+ time: "约 3 分钟阅读",
+ title: "瀑布观景路线怎么走更顺",
+ summary: "从入口到核心观景台的轻量攻略,适合第一次到访时快速决策。",
+ footer: "查看完整攻略",
+ sections: [
+ { title: "推荐顺序", content: "先到主观景台确认水量,再沿木栈道前往侧面机位,回程避开逆光时段。" },
+ { title: "体验提醒", content: "雨后地面湿滑,建议预留 40 分钟,不要在低洼处长时间停留。" },
+ ],
+ action: { title: "查看景点详情", subtitle: "开放时间、距离与导航", icon: "i" },
+ },
+ ],
+};
diff --git a/src/pages/ChatModule/LongTextGuideCard/styles/index.scss b/src/pages/ChatModule/LongTextGuideCard/styles/index.scss
new file mode 100644
index 0000000..3e1dc88
--- /dev/null
+++ b/src/pages/ChatModule/LongTextGuideCard/styles/index.scss
@@ -0,0 +1,58 @@
+.long-text-guide-card,
+.long-text-guide-card__list {
+}
+
+.long-text-guide-card__item {
+ margin-bottom: 12px;
+}
+
+.long-text-guide-card__body {
+}
+
+.long-text-guide-card__meta {
+ margin-bottom: 10px;
+}
+
+.long-text-guide-card__time {
+ color: #94a3b8;
+ font-size: 11px;
+ font-weight: 700;
+}
+
+.long-text-guide-card__title {
+ line-height: 20px;
+}
+
+.long-text-guide-card__summary {
+ margin-top: 8px;
+ line-height: 19px;
+}
+
+.long-text-guide-card__footer {
+}
+
+.long-text-guide-card__arrow {
+ font-size: 22px;
+}
+
+.long-text-guide-card__detail {
+}
+
+.long-text-guide-card__section {
+ margin-bottom: 16px;
+}
+
+.long-text-guide-card__section-title {
+ margin-bottom: 8px;
+}
+
+.long-text-guide-card__section-text {
+ line-height: 22px;
+}
+
+.long-text-guide-card__action {
+ margin-top: 18px;
+ padding: 8px;
+ border-radius: 20px;
+ background: #fffbeb;
+}
diff --git a/src/pages/ChatModule/LongTextGuideComboCard/index.vue b/src/pages/ChatModule/LongTextGuideComboCard/index.vue
new file mode 100644
index 0000000..81599ef
--- /dev/null
+++ b/src/pages/ChatModule/LongTextGuideComboCard/index.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/LongTextGuideComboCard/mocks.js b/src/pages/ChatModule/LongTextGuideComboCard/mocks.js
new file mode 100644
index 0000000..bf93d5f
--- /dev/null
+++ b/src/pages/ChatModule/LongTextGuideComboCard/mocks.js
@@ -0,0 +1,22 @@
+export default {
+ cards: [
+ {
+ id: "combo-guide",
+ badge: "组合攻略",
+ time: "约 5 分钟阅读",
+ title: "一天内拍照、游览和用餐怎么安排",
+ summary: "把景点详情、大图机位、导航和票务入口组合到同一张攻略卡后续行动区。",
+ footer: "展开攻略和行动",
+ sections: [
+ { title: "上午", content: "先拍远景与人像,再进入主步道游览。" },
+ { title: "下午", content: "根据光线选择二号机位,最后去游客中心附近补给。" },
+ ],
+ action: { title: "查看景点详情", subtitle: "开放时间与路线", icon: "i" },
+ },
+ ],
+ actions: [
+ { title: "查看机位图", subtitle: "高清大图与拍摄角度", icon: "▣", tone: "blue" },
+ { title: "带我去这里", subtitle: "地图路线与步行距离", icon: "⌖", tone: "green" },
+ { title: "相关票务", subtitle: "门票和套票入口", icon: "¥", tone: "amber" },
+ ],
+};
diff --git a/src/pages/ChatModule/LongTextGuideComboCard/styles/index.scss b/src/pages/ChatModule/LongTextGuideComboCard/styles/index.scss
new file mode 100644
index 0000000..262ab28
--- /dev/null
+++ b/src/pages/ChatModule/LongTextGuideComboCard/styles/index.scss
@@ -0,0 +1,8 @@
+.long-text-guide-combo-card {
+ width: 100%;
+}
+
+.long-text-guide-combo-card__actions {
+ background: rgba(255, 255, 255, 0.62);
+ box-shadow: 0 8px 26px rgba(15, 23, 42, 0.04);
+}
diff --git a/src/pages/ChatModule/MapNavigationCard/index.vue b/src/pages/ChatModule/MapNavigationCard/index.vue
new file mode 100644
index 0000000..3b4ebf1
--- /dev/null
+++ b/src/pages/ChatModule/MapNavigationCard/index.vue
@@ -0,0 +1,45 @@
+
+
+
+
+ {{ data.pinText }}
+
+
+
+ {{ data.title }}
+ {{ data.distance }}
+
+
+ {{ data.buttonText }}
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/MapNavigationCard/mocks.js b/src/pages/ChatModule/MapNavigationCard/mocks.js
new file mode 100644
index 0000000..c9785cf
--- /dev/null
+++ b/src/pages/ChatModule/MapNavigationCard/mocks.js
@@ -0,0 +1,7 @@
+export default {
+ mapImage: "https://images.unsplash.com/photo-1524661135-423995f22d0b?auto=format&fit=crop&w=900&q=80",
+ pinText: "目的地",
+ title: "前往翠谷瀑布",
+ distance: "距你 500m · 步行约 8 分钟",
+ buttonText: "导航",
+};
diff --git a/src/pages/ChatModule/MapNavigationCard/styles/index.scss b/src/pages/ChatModule/MapNavigationCard/styles/index.scss
new file mode 100644
index 0000000..f8a3592
--- /dev/null
+++ b/src/pages/ChatModule/MapNavigationCard/styles/index.scss
@@ -0,0 +1,39 @@
+.map-navigation-card__map {
+ position: relative;
+ padding: 8px;
+}
+
+.map-navigation-card__image {
+}
+
+.map-navigation-card__pin {
+ position: absolute;
+ top: 18px;
+ left: 18px;
+ padding: 5px 9px;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.9);
+}
+
+.map-navigation-card__bar {
+ padding: 0 14px 14px;
+}
+
+.map-navigation-card__info {
+ min-width: 0;
+}
+
+.map-navigation-card__title {
+}
+
+.map-navigation-card__distance {
+ margin-top: 4px;
+}
+
+.map-navigation-card__button {
+ padding: 9px 14px;
+}
+
+.map-navigation-card__button.is-disabled {
+ opacity: 0.55;
+}
diff --git a/src/pages/ChatModule/MultiPoiRecommendationCard/index.vue b/src/pages/ChatModule/MultiPoiRecommendationCard/index.vue
new file mode 100644
index 0000000..2ea762f
--- /dev/null
+++ b/src/pages/ChatModule/MultiPoiRecommendationCard/index.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/MultiPoiRecommendationCard/mocks.js b/src/pages/ChatModule/MultiPoiRecommendationCard/mocks.js
new file mode 100644
index 0000000..d96845d
--- /dev/null
+++ b/src/pages/ChatModule/MultiPoiRecommendationCard/mocks.js
@@ -0,0 +1,34 @@
+export default {
+ items: [
+ {
+ id: "poi-1",
+ title: "翠谷瀑布",
+ category: "自然景观",
+ cover: "https://images.unsplash.com/photo-1432405972618-c60b0225b8f9?auto=format&fit=crop&w=900&q=80",
+ description: "主线步道上的核心停留点,适合拍摄水景。",
+ facts: [
+ { label: "距离", value: "500m" },
+ { label: "推荐", value: "高" },
+ { label: "耗时", value: "30m" },
+ ],
+ tags: ["亲水", "拍照"],
+ actionTitle: "导航到瀑布",
+ actionSubtitle: "步行约 8 分钟",
+ },
+ {
+ id: "poi-2",
+ title: "水上森林",
+ category: "亲子友好",
+ cover: "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=900&q=80",
+ description: "树影和水面交错,适合慢游和家庭出行。",
+ facts: [
+ { label: "距离", value: "1.2km" },
+ { label: "推荐", value: "中高" },
+ { label: "耗时", value: "45m" },
+ ],
+ tags: ["轻徒步", "观景"],
+ actionTitle: "导航到水上森林",
+ actionSubtitle: "观光车约 6 分钟",
+ },
+ ],
+};
diff --git a/src/pages/ChatModule/MultiPoiRecommendationCard/styles/index.scss b/src/pages/ChatModule/MultiPoiRecommendationCard/styles/index.scss
new file mode 100644
index 0000000..5f41bba
--- /dev/null
+++ b/src/pages/ChatModule/MultiPoiRecommendationCard/styles/index.scss
@@ -0,0 +1,10 @@
+.multi-poi-recommendation-card {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ width: 100%;
+}
+
+.multi-poi-recommendation-card__item {
+ flex-shrink: 0;
+}
diff --git a/src/pages/ChatModule/NoticeCard/index.vue b/src/pages/ChatModule/NoticeCard/index.vue
new file mode 100644
index 0000000..ff1c7d3
--- /dev/null
+++ b/src/pages/ChatModule/NoticeCard/index.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+ {{ item.title }}
+ {{ item.summary }}
+
+ {{ item.date }}
+
+
+
+
+
+
+ {{ activeNotice.date }}
+
+ {{ paragraph }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/NoticeCard/mocks.js b/src/pages/ChatModule/NoticeCard/mocks.js
new file mode 100644
index 0000000..a01e994
--- /dev/null
+++ b/src/pages/ChatModule/NoticeCard/mocks.js
@@ -0,0 +1,24 @@
+export default {
+ title: "景区公告",
+ subtitle: "重要通知与运营提醒",
+ badge: "公告",
+ notices: [
+ {
+ id: "notice-1",
+ title: "观光车运营时间调整",
+ summary: "因道路维护,今日 16:30 后部分站点临时调整。",
+ date: "今日",
+ type: "运营",
+ cover: "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=900&q=80",
+ paragraphs: ["今日 16:30 后,水上森林站点将临时调整至游客中心东侧。", "已购票游客可凭原票乘坐接驳车,请根据现场指引有序排队。"],
+ },
+ {
+ id: "notice-2",
+ title: "雨后栈道防滑提醒",
+ summary: "部分亲水路段地面湿滑,请穿防滑鞋并照看儿童。",
+ date: "昨天",
+ type: "安全",
+ paragraphs: ["雨后栈道区域湿滑,请勿奔跑或翻越护栏。", "如遇临时封闭,请服从现场工作人员安排。"],
+ },
+ ],
+};
diff --git a/src/pages/ChatModule/NoticeCard/styles/index.scss b/src/pages/ChatModule/NoticeCard/styles/index.scss
new file mode 100644
index 0000000..ed8f326
--- /dev/null
+++ b/src/pages/ChatModule/NoticeCard/styles/index.scss
@@ -0,0 +1,60 @@
+.notice-card {
+ width: 100%;
+}
+
+.notice-card__list {
+}
+
+.notice-card__header {
+ margin-bottom: 12px;
+}
+
+.notice-card__title {
+}
+
+.notice-card__subtitle {
+ margin-top: 4px;
+}
+
+.notice-card__item {
+ padding: 13px 0;
+}
+
+.notice-card__item:last-child {
+ border-bottom: none;
+}
+
+.notice-card__item.is-disabled {
+ opacity: 0.55;
+}
+
+.notice-card__item-body {
+ min-width: 0;
+}
+
+.notice-card__item-title {
+}
+
+.notice-card__item-desc {
+ margin-top: 5px;
+ line-height: 17px;
+}
+
+.notice-card__item-date {
+}
+
+.notice-card__detail {
+}
+
+.notice-card__cover {
+ margin-bottom: 14px;
+}
+
+.notice-card__meta {
+ margin-bottom: 12px;
+}
+
+.notice-card__paragraph {
+ margin-bottom: 12px;
+ line-height: 22px;
+}
diff --git a/src/pages/ChatModule/PoiCompareCard/index.vue b/src/pages/ChatModule/PoiCompareCard/index.vue
new file mode 100644
index 0000000..738cb66
--- /dev/null
+++ b/src/pages/ChatModule/PoiCompareCard/index.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+ {{ item.title }}
+ {{ item.score }}
+
+
+
+
+
+
+
+ {{ row.label }}
+ {{ row.value }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/PoiCompareCard/mocks.js b/src/pages/ChatModule/PoiCompareCard/mocks.js
new file mode 100644
index 0000000..228b0d2
--- /dev/null
+++ b/src/pages/ChatModule/PoiCompareCard/mocks.js
@@ -0,0 +1,29 @@
+export default {
+ title: "两个目的地怎么选",
+ subtitle: "按距离、拍照和亲子友好度对比",
+ badge: "对比",
+ items: [
+ {
+ id: "compare-1",
+ title: "翠谷瀑布",
+ score: "更适合拍照",
+ cover: "https://images.unsplash.com/photo-1432405972618-c60b0225b8f9?auto=format&fit=crop&w=800&q=80",
+ details: [
+ { label: "优势", value: "景观集中、出片快" },
+ { label: "距离", value: "约 500m" },
+ { label: "建议", value: "上午优先前往" },
+ ],
+ },
+ {
+ id: "compare-2",
+ title: "水上森林",
+ score: "更适合慢游",
+ cover: "https://images.unsplash.com/photo-1441974231531-c6227db76b6e?auto=format&fit=crop&w=800&q=80",
+ details: [
+ { label: "优势", value: "路线平缓、停留舒服" },
+ { label: "距离", value: "约 1.2km" },
+ { label: "建议", value: "下午光线更柔和" },
+ ],
+ },
+ ],
+};
diff --git a/src/pages/ChatModule/PoiCompareCard/styles/index.scss b/src/pages/ChatModule/PoiCompareCard/styles/index.scss
new file mode 100644
index 0000000..6b9efb9
--- /dev/null
+++ b/src/pages/ChatModule/PoiCompareCard/styles/index.scss
@@ -0,0 +1,56 @@
+.poi-compare-card {
+ width: 100%;
+}
+
+.poi-compare-card__overview {
+}
+
+.poi-compare-card__header {
+ margin-bottom: 14px;
+}
+
+.poi-compare-card__title {
+}
+
+.poi-compare-card__subtitle {
+ margin-top: 4px;
+}
+
+.poi-compare-card__grid {
+}
+
+.poi-compare-card__option {
+}
+
+.poi-compare-card__option:active {
+ opacity: 0.86;
+}
+
+.poi-compare-card__option.is-disabled {
+ opacity: 0.55;
+}
+
+.poi-compare-card__image {
+ margin-bottom: 8px;
+}
+
+.poi-compare-card__option-title {
+}
+
+.poi-compare-card__score {
+ margin-top: 4px;
+}
+
+.poi-compare-card__detail {
+}
+
+.poi-compare-card__row {
+ padding: 12px 0;
+}
+
+.poi-compare-card__row-label {
+}
+
+.poi-compare-card__row-value {
+ text-align: right;
+}
diff --git a/src/pages/ChatModule/PoiDetailCard/index.vue b/src/pages/ChatModule/PoiDetailCard/index.vue
new file mode 100644
index 0000000..b9f223a
--- /dev/null
+++ b/src/pages/ChatModule/PoiDetailCard/index.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+ {{ data.title }}
+ {{ data.description }}
+
+
+ {{ fact.value }}
+ {{ fact.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/PoiDetailCard/mocks.js b/src/pages/ChatModule/PoiDetailCard/mocks.js
new file mode 100644
index 0000000..01010d8
--- /dev/null
+++ b/src/pages/ChatModule/PoiDetailCard/mocks.js
@@ -0,0 +1,14 @@
+export default {
+ title: "翠谷瀑布观景台",
+ category: "必看 POI",
+ cover: "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=900&q=80",
+ description: "靠近主步道的核心观景点,适合拍摄瀑布全景,也适合短暂停留休息。",
+ facts: [
+ { label: "步行", value: "8 分钟" },
+ { label: "热度", value: "4.8" },
+ { label: "停留", value: "30 分钟" },
+ ],
+ tags: ["亲子友好", "拍照", "近游客中心"],
+ actionTitle: "打开导航地图",
+ actionSubtitle: "查看路线与距离",
+};
diff --git a/src/pages/ChatModule/PoiDetailCard/styles/index.scss b/src/pages/ChatModule/PoiDetailCard/styles/index.scss
new file mode 100644
index 0000000..7d09643
--- /dev/null
+++ b/src/pages/ChatModule/PoiDetailCard/styles/index.scss
@@ -0,0 +1,48 @@
+.poi-detail-card {
+ position: relative;
+}
+
+.poi-detail-card__hero {
+ position: relative;
+ padding: 8px 8px 0;
+}
+
+.poi-detail-card__media {
+}
+
+.poi-detail-card__tag {
+ position: absolute;
+ left: 18px;
+ bottom: 10px;
+}
+
+.poi-detail-card__body {
+}
+
+.poi-detail-card__title {
+ line-height: 24px;
+}
+
+.poi-detail-card__desc {
+ margin-top: 8px;
+ line-height: 20px;
+}
+
+.poi-detail-card__facts {
+}
+
+.poi-detail-card__fact {
+}
+
+.poi-detail-card__fact-value {
+}
+
+.poi-detail-card__fact-label {
+ margin-top: 3px;
+}
+
+.poi-detail-card__chips {
+}
+
+.poi-detail-card__action {
+}
diff --git a/src/pages/ChatModule/RecommendationListCard/index.vue b/src/pages/ChatModule/RecommendationListCard/index.vue
new file mode 100644
index 0000000..62a613b
--- /dev/null
+++ b/src/pages/ChatModule/RecommendationListCard/index.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+ {{ item.title }}
+ {{ item.description }}
+ {{ item.meta }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/RecommendationListCard/mocks.js b/src/pages/ChatModule/RecommendationListCard/mocks.js
new file mode 100644
index 0000000..1eff210
--- /dev/null
+++ b/src/pages/ChatModule/RecommendationListCard/mocks.js
@@ -0,0 +1,21 @@
+export default {
+ title: "为你推荐",
+ subtitle: "按距离和热度排序",
+ badge: "3 个地点",
+ items: [
+ {
+ id: "list-1",
+ title: "水上森林步道",
+ description: "轻松步行即可到达,适合拍照和短途散步。",
+ meta: "距你 620m",
+ cover: "https://images.unsplash.com/photo-1441974231531-c6227db76b6e?auto=format&fit=crop&w=600&q=80",
+ },
+ {
+ id: "list-2",
+ title: "游客中心咖啡吧",
+ description: "可补水休息,靠近返程接驳点。",
+ meta: "步行 5 分钟",
+ cover: "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?auto=format&fit=crop&w=600&q=80",
+ },
+ ],
+};
diff --git a/src/pages/ChatModule/RecommendationListCard/styles/index.scss b/src/pages/ChatModule/RecommendationListCard/styles/index.scss
new file mode 100644
index 0000000..f53cd5a
--- /dev/null
+++ b/src/pages/ChatModule/RecommendationListCard/styles/index.scss
@@ -0,0 +1,46 @@
+.recommendation-list-card {
+}
+
+.recommendation-list-card__header {
+ margin-bottom: 12px;
+}
+
+.recommendation-list-card__title {
+}
+
+.recommendation-list-card__subtitle {
+ margin-top: 4px;
+}
+
+.recommendation-list-card__list {
+}
+
+.recommendation-list-card__item {
+}
+
+.recommendation-list-card__item:active {
+ opacity: 0.86;
+}
+
+.recommendation-list-card__item.is-disabled {
+ opacity: 0.55;
+}
+
+.recommendation-list-card__thumb {
+}
+
+.recommendation-list-card__content {
+ min-width: 0;
+}
+
+.recommendation-list-card__name {
+}
+
+.recommendation-list-card__desc {
+ margin-top: 5px;
+ line-height: 16px;
+}
+
+.recommendation-list-card__meta {
+ margin-top: 7px;
+}
diff --git a/src/pages/ChatModule/RoutePlanCard/index.vue b/src/pages/ChatModule/RoutePlanCard/index.vue
new file mode 100644
index 0000000..f5f1682
--- /dev/null
+++ b/src/pages/ChatModule/RoutePlanCard/index.vue
@@ -0,0 +1,91 @@
+
+
+
+
+
+ {{ data.title }}
+
+
+
+
+ ›
+
+
+
+
+
+
+ {{ index + 1 }}
+
+
+ {{ node.title }}
+ {{ node.description }}
+
+
+
+
+
+ {{ connectors[index] }}
+
+
+
+ {{ data.tipsTitle }}
+ {{ tip }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/RoutePlanCard/mocks.js b/src/pages/ChatModule/RoutePlanCard/mocks.js
new file mode 100644
index 0000000..cb65ac8
--- /dev/null
+++ b/src/pages/ChatModule/RoutePlanCard/mocks.js
@@ -0,0 +1,14 @@
+export default {
+ title: "半日轻松游览路线",
+ duration: "约 2.5 小时",
+ cover: "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=600&q=80",
+ tags: ["亲子友好", "少走回头路", "含观光车"],
+ tipsTitle: "出行贴士",
+ nodes: [
+ { id: "n1", title: "游客中心", description: "补给和领取地图", cover: "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?auto=format&fit=crop&w=400&q=80" },
+ { id: "n2", title: "翠谷瀑布", description: "核心观景点", cover: "https://images.unsplash.com/photo-1432405972618-c60b0225b8f9?auto=format&fit=crop&w=400&q=80" },
+ { id: "n3", title: "水上森林", description: "慢游和拍照", cover: "https://images.unsplash.com/photo-1441974231531-c6227db76b6e?auto=format&fit=crop&w=400&q=80", tip: "推荐停留" },
+ ],
+ connectors: ["步行约 5 分钟", "观光车约 6 分钟"],
+ tips: ["雨天注意栈道防滑。", "带老人小孩建议优先使用观光车。"],
+};
diff --git a/src/pages/ChatModule/RoutePlanCard/styles/index.scss b/src/pages/ChatModule/RoutePlanCard/styles/index.scss
new file mode 100644
index 0000000..f4ea6fd
--- /dev/null
+++ b/src/pages/ChatModule/RoutePlanCard/styles/index.scss
@@ -0,0 +1,80 @@
+.route-plan-card {
+ width: 100%;
+}
+
+.route-plan-card__summary {
+}
+
+.route-plan-card__cover {
+}
+
+.route-plan-card__summary-body {
+ min-width: 0;
+}
+
+.route-plan-card__title {
+}
+
+.route-plan-card__chips {
+ margin-top: 8px;
+}
+
+.route-plan-card__arrow {
+ font-size: 24px;
+}
+
+.route-plan-card__flow {
+}
+
+.route-plan-card__node {
+}
+
+.route-plan-card__num {
+ background: #10b981;
+}
+
+.route-plan-card__node-img {
+}
+
+.route-plan-card__node-body {
+ min-width: 0;
+}
+
+.route-plan-card__node-title {
+}
+
+.route-plan-card__node-desc {
+ margin: 4px 0 6px;
+}
+
+.route-plan-card__connector {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 8px 0 8px 22px;
+}
+
+.route-plan-card__line {
+ width: 2px;
+ height: 24px;
+ border-radius: 4px;
+ background: #d1fae5;
+}
+
+.route-plan-card__connector-text {
+}
+
+.route-plan-card__tips {
+}
+
+.route-plan-card__tips-title {
+ color: #92400e;
+ margin-bottom: 8px;
+}
+
+.route-plan-card__tip {
+ color: #b45309;
+ font-size: 11px;
+ font-weight: 700;
+ line-height: 18px;
+}
diff --git a/src/pages/ChatModule/ScenicImageCard/index.vue b/src/pages/ChatModule/ScenicImageCard/index.vue
new file mode 100644
index 0000000..d056203
--- /dev/null
+++ b/src/pages/ChatModule/ScenicImageCard/index.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+ {{ data.caption.title }}
+
+ {{ data.caption.subtitle }}
+
+
+ ⛶
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/ScenicImageCard/mocks.js b/src/pages/ChatModule/ScenicImageCard/mocks.js
new file mode 100644
index 0000000..f525d9e
--- /dev/null
+++ b/src/pages/ChatModule/ScenicImageCard/mocks.js
@@ -0,0 +1,7 @@
+export default {
+ image: "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429?auto=format&fit=crop&w=1000&q=80",
+ caption: {
+ title: "水上森林二号机位",
+ subtitle: "下午逆光较弱,适合拍摄树影和水面反射",
+ },
+};
diff --git a/src/pages/ChatModule/ScenicImageCard/styles/index.scss b/src/pages/ChatModule/ScenicImageCard/styles/index.scss
new file mode 100644
index 0000000..18d4a81
--- /dev/null
+++ b/src/pages/ChatModule/ScenicImageCard/styles/index.scss
@@ -0,0 +1,30 @@
+.scenic-image-card__image-wrap {
+}
+
+.scenic-image-card__image {
+ border-radius: 0;
+}
+
+.scenic-image-card__caption {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ padding: 42px 16px 16px;
+ background: linear-gradient(180deg, rgba(15, 23, 42, 0) 0%, rgba(15, 23, 42, 0.72) 100%);
+}
+
+.scenic-image-card__caption-title {
+ line-height: 20px;
+}
+
+.scenic-image-card__caption-subtitle {
+ margin-top: 3px;
+ color: rgba(255, 255, 255, 0.72);
+}
+
+.scenic-image-card__expand {
+ top: 12px;
+ right: 12px;
+ background: rgba(0, 0, 0, 0.32);
+}
diff --git a/src/pages/ChatModule/SharedVisual/ActionRow.vue b/src/pages/ChatModule/SharedVisual/ActionRow.vue
new file mode 100644
index 0000000..66dd07a
--- /dev/null
+++ b/src/pages/ChatModule/SharedVisual/ActionRow.vue
@@ -0,0 +1,62 @@
+
+
+
+ {{ icon }}
+
+
+ {{ title }}
+
+ {{ subtitle }}
+
+
+ ›
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/SharedVisual/BadgePill.vue b/src/pages/ChatModule/SharedVisual/BadgePill.vue
new file mode 100644
index 0000000..2d1cb41
--- /dev/null
+++ b/src/pages/ChatModule/SharedVisual/BadgePill.vue
@@ -0,0 +1,26 @@
+
+
+ {{ label }}
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/SharedVisual/CardShell.vue b/src/pages/ChatModule/SharedVisual/CardShell.vue
new file mode 100644
index 0000000..c3938f6
--- /dev/null
+++ b/src/pages/ChatModule/SharedVisual/CardShell.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/SharedVisual/DetailShell.vue b/src/pages/ChatModule/SharedVisual/DetailShell.vue
new file mode 100644
index 0000000..8596d32
--- /dev/null
+++ b/src/pages/ChatModule/SharedVisual/DetailShell.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/SharedVisual/MediaFrame.vue b/src/pages/ChatModule/SharedVisual/MediaFrame.vue
new file mode 100644
index 0000000..68fa8cf
--- /dev/null
+++ b/src/pages/ChatModule/SharedVisual/MediaFrame.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/ChatModule/SharedVisual/styles/index.scss b/src/pages/ChatModule/SharedVisual/styles/index.scss
new file mode 100644
index 0000000..63c771c
--- /dev/null
+++ b/src/pages/ChatModule/SharedVisual/styles/index.scss
@@ -0,0 +1,134 @@
+.visual-card-shell {
+ box-shadow: 0 8px 24px rgba(15, 23, 42, 0.05);
+}
+
+.visual-card-shell--soft {
+ background: rgba(255, 255, 255, 0.84);
+ box-shadow: 0 9px 34px rgba(27, 9, 91, 0.07);
+}
+
+.visual-card-shell--detail {
+ border-radius: 20px;
+}
+
+.visual-card-shell.is-pressable {
+ transition: transform 0.18s ease, opacity 0.18s ease;
+}
+
+.visual-card-shell.is-pressable:active {
+ transform: scale(0.98);
+ opacity: 0.92;
+}
+
+.visual-card-shell.is-disabled,
+.visual-action-row.is-disabled {
+ opacity: 0.55;
+}
+
+.visual-badge {
+ min-height: 20px;
+ padding: 2px 8px;
+ box-sizing: border-box;
+ line-height: 16px;
+}
+
+.visual-badge--slate {
+ color: #475569;
+ background: #f1f5f9;
+}
+
+.visual-badge--green {
+ color: #047857;
+ background: #ecfdf5;
+}
+
+.visual-badge--amber {
+ color: #b45309;
+ background: #fffbeb;
+}
+
+.visual-badge--rose {
+ color: #be123c;
+ background: #fff1f2;
+}
+
+.visual-badge--blue {
+ color: #2563eb;
+ background: #eff6ff;
+}
+
+.visual-badge--purple {
+ color: #7c3aed;
+ background: #f5f3ff;
+}
+
+.visual-media {
+}
+
+.visual-media__image,
+.visual-media__empty {
+}
+
+.visual-media__empty {
+ min-height: 96px;
+ background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
+}
+
+.visual-detail__header {
+ min-height: 48px;
+}
+
+.visual-detail__back {
+ width: 30px;
+ height: 30px;
+ font-size: 22px;
+ line-height: 28px;
+}
+
+.visual-detail__title {
+ min-width: 0;
+}
+
+.visual-action-row {
+}
+
+.visual-action-row__icon {
+}
+
+.visual-action-row__icon--green {
+ color: #047857;
+ background: #ecfdf5;
+}
+
+.visual-action-row__icon--blue {
+ color: #2563eb;
+ background: #eff6ff;
+}
+
+.visual-action-row__icon--amber {
+ color: #b45309;
+ background: #fffbeb;
+}
+
+.visual-action-row__icon--rose {
+ color: #be123c;
+ background: #fff1f2;
+}
+
+.visual-action-row__body {
+ min-width: 0;
+}
+
+.visual-action-row__title {
+}
+
+.visual-action-row__subtitle {
+ margin-top: 4px;
+ color: #94a3b8;
+ font-size: 11px;
+ font-weight: 700;
+}
+
+.visual-action-row__arrow {
+ font-size: 24px;
+}
diff --git a/src/pages/test/index.vue b/src/pages/test/index.vue
new file mode 100644
index 0000000..830a26a
--- /dev/null
+++ b/src/pages/test/index.vue
@@ -0,0 +1,191 @@
+
+
+
+
+
+ LongTextGuideCard
+
+
+
+
+ LongTextGuideComboCard
+
+
+
+
+ PoiDetailCard
+
+
+
+
+ RecommendationListCard
+
+
+
+
+ MultiPoiRecommendationCard
+
+
+
+
+ PoiCompareCard
+
+
+
+
+ RoutePlanCard
+
+
+
+
+ FacilityLocationCard
+
+
+
+
+ ScenicImageCard
+
+
+
+
+ MapNavigationCard
+
+
+
+
+ AigcPhotoCard
+
+
+
+
+ FaqHelpCard
+
+
+
+
+ NoticeCard
+
+
+
+
+
+
+
+
diff --git a/src/static/scss/background.scss b/src/static/scss/background.scss
index c187620..8e9a4fd 100644
--- a/src/static/scss/background.scss
+++ b/src/static/scss/background.scss
@@ -27,6 +27,46 @@
background-color: #f2f5f8;
}
+.bg-F8FAFC {
+ background-color: #f8fafc;
+}
+
+.bg-F1F5F9 {
+ background-color: #f1f5f9;
+}
+
+.bg-ECFDF5 {
+ background-color: #ecfdf5;
+}
+
+.bg-FFFBEB {
+ background-color: #fffbeb;
+}
+
+.bg-EFF6FF {
+ background-color: #eff6ff;
+}
+
+.bg-F5F3FF {
+ background-color: #f5f3ff;
+}
+
+.bg-FFF1F2 {
+ background-color: #fff1f2;
+}
+
+.bg-CCFBF1 {
+ background-color: #ccfbf1;
+}
+
+.bg-0F172A {
+ background-color: #0f172a;
+}
+
+.bg-0F766E {
+ background-color: #0f766e;
+}
+
.bg-FF3D60 {
background-color: #ff3d60;
}
@@ -58,4 +98,4 @@
.bg-transparent {
background-color: transparent;
-}
\ No newline at end of file
+}
diff --git a/src/static/scss/border.scss b/src/static/scss/border.scss
index 6d496ac..51d8fff 100644
--- a/src/static/scss/border.scss
+++ b/src/static/scss/border.scss
@@ -19,6 +19,14 @@
border: 1px solid #fff;
}
+.border-F1F5F9 {
+ border: 1px solid #f1f5f9;
+}
+
+.border-bottom-F1F5F9 {
+ border-bottom: 1px solid #f1f5f9;
+}
+
.border-none {
border: none;
diff --git a/src/static/scss/colors.scss b/src/static/scss/colors.scss
index 2ea82a1..6bac767 100644
--- a/src/static/scss/colors.scss
+++ b/src/static/scss/colors.scss
@@ -68,6 +68,54 @@
color: #d97706;
}
+.color-0F172A {
+ color: #0f172a;
+}
+
+.color-1E293B {
+ color: #1e293b;
+}
+
+.color-334155 {
+ color: #334155;
+}
+
+.color-475569 {
+ color: #475569;
+}
+
+.color-64748B {
+ color: #64748b;
+}
+
+.color-CBD5E1 {
+ color: #cbd5e1;
+}
+
+.color-2563EB {
+ color: #2563eb;
+}
+
+.color-7C3AED {
+ color: #7c3aed;
+}
+
+.color-E11D48 {
+ color: #e11d48;
+}
+
+.color-0F766E {
+ color: #0f766e;
+}
+
+.color-047857 {
+ color: #047857;
+}
+
+.color-0891B2 {
+ color: #0891b2;
+}
+
// text 颜色
.text-color-900 {
color: $text-color-900; // #181B25
diff --git a/src/static/scss/display.scss b/src/static/scss/display.scss
index f307efe..e4fbf50 100644
--- a/src/static/scss/display.scss
+++ b/src/static/scss/display.scss
@@ -14,3 +14,11 @@
.grid {
display: grid;
}
+
+.grid-cols-2 {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
+.grid-cols-3 {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+}
diff --git a/src/static/scss/flex.scss b/src/static/scss/flex.scss
index 248dfa5..7875f82 100644
--- a/src/static/scss/flex.scss
+++ b/src/static/scss/flex.scss
@@ -38,3 +38,27 @@
.flex-shrink-0 {
flex-shrink: 0;
}
+
+.gap-6 {
+ gap: 6px;
+}
+
+.gap-8 {
+ gap: 8px;
+}
+
+.gap-10 {
+ gap: 10px;
+}
+
+.gap-12 {
+ gap: 12px;
+}
+
+.gap-14 {
+ gap: 14px;
+}
+
+.gap-16 {
+ gap: 16px;
+}
diff --git a/src/static/scss/font-size.scss b/src/static/scss/font-size.scss
index 297bb94..4e53c0c 100644
--- a/src/static/scss/font-size.scss
+++ b/src/static/scss/font-size.scss
@@ -19,10 +19,18 @@
font-size: 12px;
}
+.font-size-13 {
+ font-size: 13px;
+}
+
.font-size-14 {
font-size: 14px;
}
+.font-size-15 {
+ font-size: 15px;
+}
+
.font-size-16 {
font-size: 16px;
}
@@ -39,6 +47,10 @@
font-size: 20px;
}
+.font-size-22 {
+ font-size: 22px;
+}
+
.font-size-24 {
font-size: 24px;
}
diff --git a/src/static/scss/font-weight.scss b/src/static/scss/font-weight.scss
index be056e7..b1020c6 100644
--- a/src/static/scss/font-weight.scss
+++ b/src/static/scss/font-weight.scss
@@ -18,3 +18,11 @@
.font-700 {
font-weight: 700;
}
+
+.font-800 {
+ font-weight: 800;
+}
+
+.font-900 {
+ font-weight: 900;
+}
diff --git a/src/static/scss/height.scss b/src/static/scss/height.scss
index c4b2cc2..878a1e9 100644
--- a/src/static/scss/height.scss
+++ b/src/static/scss/height.scss
@@ -46,6 +46,18 @@
height: 44px;
}
+.h-42 {
+ height: 42px;
+}
+
+.h-54 {
+ height: 54px;
+}
+
+.h-60 {
+ height: 60px;
+}
+
.h-64 {
height: 64px;
}
@@ -54,10 +66,42 @@
height: 72px;
}
+.h-72 {
+ height: 72px;
+}
+
.h-80 {
height: 80px;
}
+.h-82 {
+ height: 82px;
+}
+
.h-96 {
height: 96px;
}
+
+.h-108 {
+ height: 108px;
+}
+
+.h-154 {
+ height: 154px;
+}
+
+.h-164 {
+ height: 164px;
+}
+
+.h-170 {
+ height: 170px;
+}
+
+.h-178 {
+ height: 178px;
+}
+
+.h-260 {
+ height: 260px;
+}
diff --git a/src/static/scss/position.scss b/src/static/scss/position.scss
index d5cb467..0d064fa 100644
--- a/src/static/scss/position.scss
+++ b/src/static/scss/position.scss
@@ -18,3 +18,7 @@
.bottom-0 {
bottom: 0;
}
+
+.top-0 {
+ top: 0;
+}
diff --git a/src/static/scss/rounded.scss b/src/static/scss/rounded.scss
index 2de66d5..d962c57 100644
--- a/src/static/scss/rounded.scss
+++ b/src/static/scss/rounded.scss
@@ -31,6 +31,10 @@
border-radius: 16px;
}
+.rounded-18 {
+ border-radius: 18px;
+}
+
.rounded-20 {
border-radius: 20px;
}
diff --git a/src/static/scss/width.scss b/src/static/scss/width.scss
index e5bb9ee..1259961 100644
--- a/src/static/scss/width.scss
+++ b/src/static/scss/width.scss
@@ -34,6 +34,22 @@
width: 32px;
}
+.w-42 {
+ width: 42px;
+}
+
+.w-44 {
+ width: 44px;
+}
+
+.w-54 {
+ width: 54px;
+}
+
+.w-72 {
+ width: 72px;
+}
+
.w-50 {
width: 50%;
}
@@ -52,4 +68,4 @@
.w-102 {
width: 102px;
-}
\ No newline at end of file
+}