引言

当剧本杀从线下桌游吧进化为潮流社交方式,一场关于"维多利亚侦探事务所"与"暗黑卷宗"的视觉革命正在推理圈蔓延。CLUE MANSION 谜馆正是这样一款概念应用——它将搜证的专注、推理的烧脑、拼车的社交、战绩的荣耀、线索的收藏、侦探档的成长,全部浓缩进一个 ArkTS 单文件之中。

不同于传统剧本杀 App 的工具路线,CLUE MANSION 选择了"潮流剧本杀·推理社交馆"的定位:深褐底色 #171310 上,暗红 #A93226 与鎏金 #C9A227 对撞,羊皮纸 #E8D5A3 铺底,墨黑 #1B140D 勾勒,如同维多利亚时代侦探事务所的昏暗烛光。烛光脉动、印章摆动、放大镜摇摆、徽章呼吸——4 组属性动画让整个界面如同迷雾中的案发现场。

6 个 Tab 覆盖完整业务链路:案件大厅查看本周悬疑榜与馆内快讯、剧本库浏览 14 部卷宗并查阅详情、拼车场查看 9 车拼车并加入/发起、战绩厅查看生涯面板与徽章墙、线索铺浏览 12 件道具并兑换、侦探档管理个人属性与成就。6 套弹框各具特色:新建拼车(表单式+难度宫格+人数stepper)、加入拼车(确认卡片式+费用明细+上车进度)、剧本详情(卷宗档案式+案情简介+近期场次)、编辑侦探档案(底部抽屉式+擅长位置多选+恐怖承受度)、删除战绩(血红警告式)、线索兑换(宫格选择式+数量stepper+积分校验)。

本文将从项目概述、色彩系统、架构设计、数据模型、六大 Tab、六大弹框、属性动画、案件大厅与剧本库、拼车场与战绩厅、线索铺与侦探档、性能优化、剧本杀文化、推理社交、开发环境搭建共十五个维度,对 CLUE MANSION 谜馆进行深度解析,文末附完整 ArkTS 源码。

CLUE MANSION 谜馆主界面


第一章 项目概述与设计理念

1.1 项目定位

CLUE MANSION 谜馆是一款面向潮流剧本杀·推理社交的概念应用,核心定位是"剧本库+拼车场+战绩厅+线索铺"四位一体的潮流社交 App 风格。目标用户是 18-35 岁的剧本杀爱好者、推理小说迷、密室逃脱玩家、Z 世代潮流消费者。应用场景包括:浏览剧本库、查阅卷宗详情、发起拼车、加入拼车、查看生涯战绩、收集徽章、兑换线索道具、编辑侦探档案。

1.2 设计理念

维多利亚暗黑卷宗美学:以维多利亚时代侦探事务所为视觉灵感来源,暗红 #A93226 作为主强调色贯穿按钮、Tab 激活、紧急标签、血红警告,鎏金 #C9A227 作为辅助强调色用于积分、胜率、徽章、兑换按钮,羊皮纸 #E8D5A3 用于标题文字和印章,深褐 #171310 铺底构建暗黑氛围,墨黑 #1B140D 用于输入框和深层卡片,如同侦探事务所烛光下的卷宗档案。

潮流社交风:顶部馆名+搜索栏+消息铃铛角标、案件大厅本周悬疑榜横幅、剧本库分类chips+恐怖度分布色条、拼车场时间轴+紧急标签、战绩厅生涯面板+徽章墙+玩家排行,完全复现潮流社交 App 的体验。加入拼车弹框的费用明细+上车进度如同电商下单流程。

暗黑氛围与悬疑感:大量使用暗红渐变横幅、烛光脉动动画、印章摆动动画、放大镜摇摆动画、徽章呼吸动画,营造悬疑推理的氛围。卷宗档案式弹框的装订孔设计、血红警告式删除弹框的💀图标,增加暗黑卷宗的仪式感。

1.3 核心功能矩阵

模块 核心功能 数据量
案件大厅 本周悬疑榜、馆内快讯、热门剧本双列、快速拼车横滚、本周场次柱状图 14剧本/8快讯/9拼车
剧本库 分类chips、恐怖度分布色条、卷宗列表 14剧本/7分类/5恐怖度
拼车场 拼车时间轴、玩家头像、紧急标签、发起拼车 9拼车
战绩厅 生涯面板、徽章墙、评分分布条形、战绩表格、玩家排行 12战绩/12徽章/8排行
线索铺 积分余额卡、道具表格、兑换须知 12道具
侦探档 头像大卡、属性进度条、成就列表、设置项 4属性/6成就/5设置

1.4 技术亮点

  • 单文件 ArkTS 零依赖:约 2700 行代码在一个 .ets 文件中,复制即运行
  • 4 组属性动画:烛光脉动(scale Alternate)、印章摆动(rotate Alternate)、放大镜摇摆(rotate Alternate)、徽章呼吸(opacity Alternate)
  • 17 个全局纯函数:cmMaxWeek、cmMaxRating、cmStars、cmFilterScripts、cmScriptById、cmCarpoolById、cmRecordById、cmClueById、cmClueAt、cmBadgeAt、cmToggleRole、cmSlice、cmMissingTotal、cmWinCount、cmLitBadgeCount
  • 6 差异化弹框:新建拼车表单式 / 加入拼车确认卡片式 / 剧本详情卷宗档案式 / 编辑侦探档案底部抽屉式 / 删除战绩血红警告式 / 线索兑换宫格选择式
  • 侦探属性进度条:推理力/演技/胆量/体力四维属性,S级综合评级
  • 恐怖度五星显示:cmStars 纯函数生成★☆组合,5级恐怖度分布
  • 评分分布条形图:5档评分占比条形,动态宽度计算
  • 本周场次柱状图:7天场次柱状,周六峰值高亮
  • 拼车时间轴:左侧时间+圆点+连接线,右侧拼车卡片

第二章 色彩系统与视觉风格

2.1 色彩体系

CLUE MANSION 谜馆定义了 15 色完整调色板(CMColorPalette 接口),分为五个层级:

基础层(暗黑深褐)

  • bg #171310:应用背景,深褐色
  • card #221B15:卡片背景,稍浅深褐
  • deep #0F0C09:深层背景,墨黑近黑
  • line #3A2F24:分割线,深褐灰

强调层(暗红 × 鎏金 × 羊皮纸)

  • blood #A93226:暗红,主按钮、紧急标签、血红警告、Tab激活指示条
  • bloodDark #7B241C:深暗红,渐变终点、横幅底色
  • bloodGlow #C0392B:亮暗红,高亮文字、紧急数字、烛光
  • parchment #E8D5A3:羊皮纸,主标题文字、印章、按钮文字
  • ink #1B140D:墨黑,输入框背景、选中态背景
  • gold #C9A227:鎏金,积分、胜率、徽章、兑换按钮、分类选中
  • goldLight #E3C05A:亮鎏金,高亮数字、进度条、排行前三

文字层

  • textMain #F0E6D2:主文字,暖白近羊皮纸
  • textSub #A99C86:次要文字,中褐灰
  • textHint #6E6353:提示文字,浅褐灰

2.2 色彩应用策略

暗红 #A93226 是应用的灵魂色,承担"行动召唤与悬疑警示"角色:底部 Tab 激活指示条、主操作按钮(发起拼车/加入拼车/确认兑换)、紧急标签(急)、血红警告删除弹框、本周悬疑榜横幅渐变。暗红在深褐背景上具有强烈的视觉穿透力,如同案发现场的血迹。

鎏金 #C9A227 承担"荣耀与价值"角色:积分余额、胜率、MVP标记、徽章、兑换按钮、分类选中态、排行前三。鎏金与暗红形成冷暖对撞,在数据可视化中区分"价值/荣耀"与"行动/警示"。

羊皮纸 #E8D5A3 承担"标题与仪式感"角色:主标题文字、馆名、印章文字、按钮文字、卷宗标签。羊皮纸色在暗黑背景上如同烛光下的旧纸张,增加维多利亚时代的复古仪式感。

2.3 暗黑渐变与卷宗质感

应用大量使用 linearGradient 构建视觉层次:

  • 本周悬疑榜横幅:#C0392B → #A93226 → #7B241C 135° 三色渐变,暗红如同案发现场
  • 剧本封面:cover1 + cover2 双色拼接,模拟卷宗封面
  • 拼车卡片左侧色条:cover1 单色,6vp 宽竖条,模拟卷宗装订
  • 积分余额卡:#0F0C09 → #7B241C 90° 双色渐变,深褐到暗红
  • 侦探档头像卡:#0F0C09 → #7B241C 135° 双色渐变

卷宗装订孔设计是 CLUE MANSION 的标志性设计:剧本详情弹框的封面左侧有 3 个装订孔(7vp 圆,深褐背景),模拟真实卷宗的装订孔。拼车卡片左侧 6vp 宽竖条色带,模拟卷宗书脊。这些设计增加暗黑卷宗的仪式感和真实感。

不对称圆角贯穿全应用:卡片使用 12-16vp 统一圆角,弹框使用 16vp 圆角,编辑档案弹框使用顶部 20vp 圆角(底部抽屉式)。

2.4 烛光脉动与印章摆动

项目通过属性动画模拟侦探事务所场景:

  • 烛光脉动:案件大厅横幅的 3 根蜡烛(🕯️),scale 从 1.0 到 1.12,1600ms Alternate+EaseInOut,如同烛光摇曳
  • 印章摆动:案件大厅横幅的"已封案"印章,rotate 从 0° 到 6°,2400ms Alternate+EaseInOut,如同印章悬挂摆动
  • 放大镜摇摆:剧本库顶部的放大镜(🔍),rotate 从 0° 到 12°,2000ms Alternate+EaseInOut,如同侦探手持放大镜观察
  • 徽章呼吸:战绩厅徽章墙的已点亮徽章,opacity 从 1.0 到 0.45,1800ms Alternate+EaseInOut,如同徽章金属光泽呼吸

这些动画共同营造维多利亚侦探事务所的真实场景感,让静态界面"活"起来。烛光脉动是本应用的特色动画,3 根蜡烛不同大小(22vp/20vp/15vp)错落排列,模拟事务所桌上的蜡烛群。

CLUE MANSION 色彩与视觉风格


第三章 项目架构与文件组织

3.1 单文件组件化架构

CLUE MANSION 谜馆采用"单文件组件化"架构,所有代码在一个 Index.ets 文件中,通过 @Component 装饰器划分为约 14 个独立组件(6 Tab + 6 弹框 + 1 根入口 + 内联内容区)。代码量约 2700 行,处于单文件架构的合理范围。

3.2 组件层次结构

CMMansionApp (@Entry 根组件)
├── 顶部标题栏(🕵️馆名 + CLUE MANSION + 搜索 + 铃铛角标3)
├── 内容区 Stack
│   ├── 6 个 Tab 内容组件(6 选 1)
│   │   ├── CMHomeContent(案件大厅)
│   │   ├── CMScriptContent(剧本库)
│   │   ├── CMCarpoolContent(拼车场)
│   │   ├── CMRecordContent(战绩厅)
│   │   ├── CMClueContent(线索铺)
│   │   └── CMProfileContent(侦探档)
│   └── 6 个弹框(按需显示,zIndex 999)
│       ├── CMAddCarpoolForm(新建拼车·表单式)
│       ├── CMJoinCarpoolForm(加入拼车·确认卡片式)
│       ├── CMScriptDetailForm(剧本详情·卷宗档案式)
│       ├── CMEditProfileForm(编辑侦探档案·底部抽屉式)
│       ├── CMDeleteRecordForm(删除战绩·血红警告式)
│       └── CMClueExchangeForm(线索兑换·宫格选择式)
└── 底部 Tab 导航栏(6 项)

根组件管理当前 Tab(activeTab)、6 个弹框显隐状态(showAddCarpool/showJoinCarpool/showScriptDetail/showEditProfile/showDeleteRecord/showClueExchange)、弹框间传递数据(joinId/detailId/deleteId)。

3.3 @Builder 自定义构建函数

项目使用 7 个 @Builder 方法封装可复用 UI 片段:modalOverlay(弹框遮罩层)、addCarpoolModal/joinCarpoolModal/scriptDetailModal/editProfileModal/deleteRecordModal/clueExchangeModal(6 个弹框结构)、contentArea(内容区分发)。

3.4 弹框架构设计

弹框采用"遮罩层 + 内容层"Stack 叠加模式,统一在根组件管理。6 套弹框各具特色:

  1. 新建拼车(表单式):剧本名输入 + 人数stepper(3-8人)+ 时间chips(5选1)+ 难度宫格(4选1)+ 门店chips(4选1)+ 备注TextArea
  2. 加入拼车(确认卡片式):剧本封面+信息回显 + 上车进度条 + 车上侦探列表(车头/队员标签)+ 费用明细(剧本车费+道具服装+平台服务费)+ 合计
  3. 剧本详情(卷宗档案式):卷宗封面(装订孔+emoji)+ 剧本信息 + 案情简介 + 标签 + 近期场次表格(日期/时间/DM/余位/操作)
  4. 编辑侦探档案(底部抽屉式):昵称输入 + 擅长位置多选(6选多)+ 恐怖承受度stepper(1-5星)+ 个性签名TextArea
  5. 删除战绩(血红警告式):💀图标 + 焚毁警告 + 战绩回显卡 + 手滑了/忍痛焚毁
  6. 线索兑换(宫格选择式):积分余额 + 3×4道具宫格 + 数量stepper + 所需积分合计 + 积分校验

第四章 数据模型与全局状态管理

4.1 接口定义体系

项目定义 20 个 TypeScript 接口描述数据模型:CMColorPalette(色彩)、CMTabItem(导航)、CMScript(剧本)、CMPlayer(玩家)、CMCarpool(拼车)、CMRecord(战绩)、CMClue(线索道具)、CMBadge(徽章)、CMWeekDay(周场次)、CMRank(排行)、CMNews(快讯)、CMHorrorBand(恐怖度分段)、CMRatingBand(评分分段)、CMStatCell(统计单元)、CMAttrBar(属性条)、CMAchievement(成就)、CMProfileSetting(设置项)、CMSession(场次)、CMDiffItem(难度项)。

4.2 全局数据常量

约 20 组全局 const 常量:核心业务数据(CM_SCRIPTS 14 部 / CM_HOME_SCRIPTS 8 部首页精选 / CM_CARPOOLS 9 车 / CM_RECORDS 12 条 / CM_CLUES 12 件 / CM_BADGES 12 个 / CM_WEEK 7 天 / CM_RANKS 8 人 / CM_NEWS 8 条 / CM_HORROR_BANDS 5 段 / CM_RATING_BANDS 5 段 / CM_STAT_CELLS 4 项 / CM_ATTRS 4 维 / CM_ACHIEVEMENTS 6 项 / CM_SETTINGS 5 项 / CM_SESSIONS 5 场)、配置数据(CM_TABS 6 项 / CM_CATS 7 分类 / CM_TIMES 5 时段 / CM_DIFFS 4 难度 / CM_STORES 4 门店 / CM_ROLES 6 位置)。

4.3 三级状态管理策略

  • 第一级 全局常量(const):业务数据和配置,任何组件可直接读取
  • 第二级 组件局部状态(@State):组件内交互状态,如 scriptName/people/pickTime/pickDiff(新建拼车)、pickId/qty/balance(线索兑换)、nick/roles/tol/sign(编辑档案)、candleScale/stampAngle(案件大厅动画)、magAngle(剧本库动画)、badgeOpacity(战绩厅动画)
  • 第三级 根组件协调状态(@State):跨组件状态,如 activeTab(当前 Tab)、6 个弹框显隐开关、弹框间传递数据(joinId/detailId/deleteId)

4.4 全局纯函数

17 个全局纯函数处理数据转换和业务逻辑:

  • cmMaxWeek():返回本周场次峰值(46,周六)
  • cmMaxRating():返回评分分段峰值(41,7.0-7.9分)
  • cmStars(n):生成 n 星★和 (5-n)☆的组合字符串
  • cmFilterScripts(cat):按分类筛选剧本
  • cmScriptById(id):按 ID 查找剧本
  • cmCarpoolById(id):按 ID 查找拼车
  • cmRecordById(id):按 ID 查找战绩
  • cmClueById(id):按 ID 查找线索道具
  • cmClueAt(i):按索引安全获取线索道具(越界返回第0个)
  • cmBadgeAt(i):按索引安全获取徽章
  • cmToggleRole(arr, r):切换擅长位置数组中的元素(有则删,无则加)
  • cmSlice(arr, start, end):安全截取数组片段
  • cmMissingTotal():计算所有拼车的总缺人数
  • cmWinCount():计算近12场战绩的胜场数
  • cmLitBadgeCount():计算已点亮徽章数

纯函数相同输入永远产生相同输出,无副作用,易于测试和复用。cmStars 纯函数是本应用的特色,通过循环生成★☆组合字符串,避免硬编码恐怖度显示。cmToggleRole 纯函数实现多选切换逻辑,有则删除无则添加。cmClueAt/cmBadgeAt 安全索引函数避免越界访问。


第五章 六大 Tab 页面深度解析

5.1 案件大厅 Tab(CMHomeContent)

案件大厅是入口,承担"查看本周悬疑榜→浏览快讯→热门剧本→快速拼车→发起拼车"路径。页面结构:

  1. 本周悬疑榜横幅(暗红三色渐变 + 烛光脉动 + 印章摆动)

    • 135° 三色渐变 #C0392B→#A93226→#7B241C
    • THIS WEEK SUSPENSE 标签(羊皮纸色,letterSpacing 2)
    • 本周悬疑榜标题(羊皮纸色 20vp 加粗)
    • 「雾山庄的第七位客人」剧本名(鎏金色 14vp 加粗)
    • 三组数据:🔥98热度指数 / 7人本适合人数 / 恐怖★★★
    • 查看卷宗按钮(羊皮纸色背景墨黑字)
    • 3 根蜡烛🕯️(22/20/15vp,错落排列,candleScale 脉动 1600ms Alternate)
    • 已封案印章(羊皮纸色描边,stampAngle 摆动 2400ms Alternate)
  2. 馆内快讯(8 条):上新/活动/公告/拼车/周边/赛事/提醒标签,热门🔥标记

  3. 热门剧本双列卡片(8 部首页精选):每卡含封面渐变+emoji+HOT标签+剧本名+恐怖星+人数时长+价格+查阅卷宗按钮

  4. 快速拼车横滚(9 车):每卡含时间+剧本名+门店+缺人数+去拼车按钮,紧急车暗红描边

  5. 发起拼车入口:想自己开车?+发起拼车按钮(暗红背景)

  6. 本周场次柱状图(7 天):周一12/周二18/周三9/周四22/周五38/周六46/周日31,周六峰值暗红高亮,其他鎏金

三组特效动画是案件大厅的核心视觉亮点:烛光脉动模拟事务所桌上的蜡烛摇曳、印章摆动模拟悬挂的封案印章、暗红渐变横幅如同案发现场。本周场次柱状图周六峰值高亮,直观展示周末高峰。

案件大厅Tab

5.2 剧本库 Tab(CMScriptContent)

剧本库是剧本浏览中心,承担"分类筛选→查看恐怖度分布→浏览卷宗→查阅详情"路径。页面结构:

  1. 卷宗书架标题:卷宗书架 18vp 加粗 + DOSSIER SHELF·共14部(letterSpacing 2)
  2. 放大镜摇摆动画:🔍 26vp,magAngle 0°→12° 摇摆 2000ms Alternate
  3. 分类 chips 横滚(7 分类):全部/情感/恐怖/推理/机制/阵营/欢乐,选中鎏金背景墨黑字
  4. 恐怖度分布色条(5 段):1星轻松2/2星微恐5/3星中恐4/4星重恐2/5星极恐1,横向比例条+图例
  5. 卷宗列表(14 部,按分类筛选):每卡含左侧 6vp 色条+CASE编号+重恐标签+剧本名+恐怖星+分类标签+标签+人数时长价格+查阅按钮

放大镜摇摆动画是剧本库的特色,🔍图标缓慢摇摆如同侦探手持放大镜观察卷宗。恐怖度分布色条直观展示全馆剧本的恐怖度构成,3星中恐以4部居首。分类chips支持快速筛选,卷宗列表左侧色条模拟卷宗书脊。

剧本库Tab

5.3 拼车场 Tab(CMCarpoolContent)

拼车场是社交中心,承担"浏览拼车→查看玩家→加入拼车→发起拼车"路径。页面结构:

  1. 拼车发车表标题:🚕拼车发车表 18vp 加粗 + 在拼9车·总缺X人

  2. 发起拼车按钮:+发起拼车(暗红背景羊皮纸字)

  3. 拼车时间轴列表(9 车):

    • 左侧时间轴:时间+圆点(紧急暗红/普通鎏金)+连接线
    • 右侧拼车卡:剧本名+紧急标签+门店时间+玩家头像群+已上车数+缺人数+价格+加入/已满员按钮
    • 紧急车暗红描边,普通车深褐描边
    • 缺人>2鎏金色,缺人≤2暗红高亮
  4. 车辆出行提示:提前15分钟到店换装/跳车扣20积分/重恐本带勇气

拼车时间轴是拼车场的核心设计,左侧时间+圆点+连接线模拟车站发车表,右侧拼车卡展示完整信息。玩家头像群展示已上车玩家,车头/队员标签区分角色。紧急标签(急)暗红背景增加紧迫感。缺人数高亮引导用户快速补位。

拼车场Tab

5.4 战绩厅 Tab(CMRecordContent)

战绩厅是成就中心,承担"查看生涯面板→徽章墙→评分分布→战绩记录→玩家排行"路径。页面结构:

  1. 侦探生涯面板(4 项统计):总场次128/胜率68%/拿凶次数21/MVP次数17,本月+12/高于馆均9%/逃脱率76%/全馆前5%
  2. 徽章墙(12 个,7 已点亮/5 未解锁):每格含 emoji+徽章名+已点亮/未解锁,已点亮徽章 badgeOpacity 呼吸 1800ms Alternate,鎏金描边;未解锁深褐描边
  3. 评分分布条形图(5 档):9.0+18场/8.0-8.9 32场/7.0-7.9 41场/6.0-6.9 24场/6.0以下13场,动态宽度条形
  4. 最近战绩表格(12 行,斑马纹):日期/剧本/身份/结果/评分/操作,胜鎏金/负褐灰,删除按钮暗红
  5. 全馆侦探榜(8 人):排名(1鎏金/2羊皮纸/3暗红/其他褐灰)+头像+昵称+场次+胜率+胜率进度条

徽章呼吸动画是战绩厅的特色,已点亮徽章 opacity 呼吸如同金属光泽闪烁。生涯面板 4 项统计展示核心数据,徽章墙 12 个徽章展示成就收集。评分分布条形图展示生涯评分构成,7.0-7.9 以 41 场居首。玩家排行前三特殊配色,胜率进度条直观展示。

战绩厅Tab

5.5 线索铺 Tab(CMClueContent)

线索铺是道具商城,承担"查看积分余额→浏览道具→兑换道具"路径。页面结构:

  1. 积分余额卡(深褐→暗红渐变):我的线索积分 + 2,680分(34vp 加粗鎏金)+ 本周+120/累计兑换34件/过期规则 + 🧩emoji
  2. 线索道具铺表格(12 件):道具(emoji+名称+描述)/所需积分/库存/操作,售罄显示0暗红,兑换按钮鎏金
  3. 兑换须知:积分获取方式/实体道具门店领取/限定道具兑完即止

积分余额卡是线索铺的核心视觉,深褐→暗红 90° 渐变如同旧钱包,2,680 分 34vp 加粗鎏金展示余额。道具表格 12 件道具覆盖侦探周边,黄铜放大镜挂件180分/烟斗香料补充包120分/袖珍显微镜片260分/怀表延长链320分/火漆密写信封80分/血迹显影剂450分(库存5)/指纹提取粉套装150分/侦探笔记封皮220分/复古烛灯200分/雾都晚报号外90分/黄铜侦探徽章500分(已兑完)/闻香瓶·雨后石板280分。

线索铺Tab

5.6 侦探档 Tab(CMProfileContent)

侦探档是个人中心,承担"查看个人信息→属性进度→成就列表→设置"路径。页面结构:

  1. 头像大卡(深褐→暗红渐变):🕵️头像 78vp 圆(羊皮纸背景+鎏金3vp描边)+ 昵称雾都小侦探 19vp + Lv.36标签+称号「雾都名侦探」+ ID+入馆412天 + 徽章数/积分/常驻门店 + 编辑按钮
  2. 侦探属性进度条(4 维):推理力88鎏金/演技72暗红/胆量64羊皮纸/体力90褐灰,综合评级 S级·全馆前3%
  3. 成就进度(6 项,4 已达成/2 进行中):卷宗入门/完美犯罪/守夜人/车头老手(已达成)/线索猎人(进行中9/15)/传奇侦探(进行中)
  4. 馆务设置(5 项):拼车提醒/夜间模式/战绩隐私/常驻门店/关于谜馆 + 退出登录(暗红)

侦探属性进度条是侦探档的核心设计,4 维属性(推理力/演技/胆量/体力)以不同颜色进度条展示,综合评级 S 级。头像大卡深褐→暗红渐变如同侦探档案封面,鎏金描边头像增加仪式感。成就列表 6 项展示成长进度,已达成鎏金标签/进行中深褐标签。

侦探档Tab


第六章 六大弹框组件设计

弹框是 CLUE MANSION 谜馆交互体系的核心组成部分。项目共设计了 6 套弹框组件,覆盖新建拼车、加入拼车、剧本详情、编辑侦探档案、删除战绩、线索兑换六大业务场景。

6.1 新建拼车弹框(CMAddCarpoolForm·表单式)

定位:发起拼车入口,表单式+难度宫格+人数stepper设计。

核心功能

  1. 剧本名称:文本输入
  2. 需要人数:−/+ stepper(3-8人),车费预填¥128/人
  3. 开本时间:5 个时段 chips(今晚19:30/今晚21:00/明午14:00/明晚19:30/周六18:00),分两行排列
  4. 难度宫格(4 选 1):新手友好/进阶推理/硬核烧脑/重恐沉浸,宫格卡片选中鎏金背景墨黑字
  5. 门店选择(4 选 1):雾山旗舰店/唐人街分馆/码头体验馆/老城地下室,分两行
  6. 备注:TextArea 多行输入
  7. 底部操作:取消 + 发布拼车(暗红)

难度宫格是本弹框的特色设计,4 个难度以 2×2 宫格展示,选中鎏金背景墨黑字,每个难度含名称+描述。人数 stepper 边界 3-8 人,车费预填增加透明度。时间 chips 分两行排列适配 5 个选项。

6.2 加入拼车弹框(CMJoinCarpoolForm·确认卡片式)

定位:加入拼车确认入口,确认卡片式+费用明细+上车进度设计。

核心功能

  1. 剧本信息回显:封面渐变+emoji + 剧本名 + 门店时间 + 缺人数+价格
  2. 上车进度:已上车X/Y人 + Progress 线性进度条(鎏金)
  3. 车上侦探列表(X 人):头像+昵称+等级+胜场,第1人标"车头",其他标"队员"
  4. 费用明细:剧本车费¥128/道具与服装¥10/平台服务费¥5 + 合计¥143
  5. 底部操作:再想想 + 立即上车(暗红)

上车进度条是本弹框的特色设计,Progress 线性组件展示已上车比例,鎏金色增加价值感。车上侦探列表展示玩家信息,车头/队员标签区分角色。费用明细如同电商下单流程,合计金额加粗鎏金。

6.3 剧本详情弹框(CMScriptDetailForm·卷宗档案式)

定位:剧本详情查阅入口,卷宗档案式+案情简介+近期场次设计。

核心设计——卷宗封面:88×122vp 封面渐变,左侧 3 个装订孔(7vp 圆深褐),emoji 居中,CASE 编号+剧本名+恐怖星+分类标签+人数时长+DM信息+价格热度。

核心功能

  1. 案情简介:浓雾封锁的山庄里,六位旧识收到同一位失踪主人的邀请函…(3段描述)+ 标签chips
  2. 近期场次表格(5 场):日期/时间/DM/余位/操作,余位>0鎏金预约按钮,余位=0满员
  3. 底部操作:收起卷宗 + 预约本场(鎏金背景墨黑字)

卷宗封面的装订孔设计是本弹框的特色,3 个 7vp 圆深褐孔纵向排列在封面左侧,模拟真实卷宗的装订孔。案情简介 3 段描述增加沉浸感,近期场次表格支持直接预约。

6.4 编辑侦探档案弹框(CMEditProfileForm·底部抽屉式)

定位:编辑个人档案入口,底部抽屉式+擅长位置多选+恐怖承受度设计。

核心设计——底部抽屉:顶部 44×4vp 拖拽条,顶部 20vp 圆角,从底部弹出。

核心功能

  1. 侦探代号(昵称):TextInput 回显可修改(2-12字符)
  2. 擅长位置(多选,6 选多):凶手位/侦探位/奶妈位/气氛组/硬核推理/戏精本精,选中鎏金背景墨黑字,cmToggleRole 纯函数切换
  3. 恐怖承受度:−/+ stepper(1-5星),cmStars 显示★☆组合,根据星级显示重恐玩家/中恐玩家/轻松玩家
  4. 个性签名:TextArea 多行输入
  5. 底部操作:放弃修改 + 保存档案(鎏金背景墨黑字)

擅长位置多选是本弹框的特色设计,6 个位置以 chips 展示,支持多选(cmToggleRole 纯函数实现有则删无则加)。恐怖承受度 stepper 1-5 星,cmStars 纯函数生成★☆组合,根据星级动态显示玩家类型标签。

6.5 删除战绩弹框(CMDeleteRecordForm·血红警告式)

定位:删除战绩的二次确认,血红警告式设计。

核心设计——血红警告:💀图标 52vp + 焚毁这份卷宗?标题(暗红高亮)+ 删除后该战绩将从生涯记录中永远消失 + 相关的MVP标记与积分奖励也将一并扣除。

战绩回显卡(深褐背景+暗红描边):日期+剧本名+身份+结果+MVP + ★评分。

底部操作:手滑了(深褐)+ 忍痛焚毁(暗红)。血红警告式设计与删除操作的"风险"属性匹配,💀图标增加暗黑仪式感,不可恢复说明增加透明度。

6.6 线索兑换弹框(CMClueExchangeForm·宫格选择式)

定位:线索道具兑换入口,宫格选择式+数量stepper+积分校验设计。

核心功能

  1. 当前积分:2,680 分(每完成一场+15,MVP再+30)
  2. 道具宫格(3×4,12 件):每格含 emoji+名称+积分,选中鎏金2vp描边+深褐背景,未选中深褐1vp描边,售罄不可选
  3. 兑换数量:−/+ stepper(1-库存),库存显示
  4. 所需积分合计:道具单价×数量,动态计算
  5. 积分校验:积分不足显示⚠积分不足,去打两场本再回来(暗红);积分充足显示兑换后余额X积分
  6. 底部操作:再逛逛 + 确认兑换(鎏金背景墨黑字)

道具宫格是本弹框的特色设计,12 件道具以 3×4 宫格展示,选中鎏金描边高亮。数量 stepper 边界 1-库存,所需积分合计动态计算。积分校验逻辑增加用户体验,积分不足时暗红警告,积分充足时显示兑换后余额。


第七章 属性动画与交互体验

7.1 ArkTS 属性动画机制

ArkTS 通过 animateTo() 实现属性动画,基本用法:

this.getUIContext().animateTo({
  duration: 1600,
  iterations: -1,
  playMode: PlayMode.Alternate,
  curve: Curve.EaseInOut
}, () => { this.candleScale = 1.12 })

iterations: -1 表示无限循环。PlayMode.Alternate 到达目标值后反向执行回初始值,形成往返效果(如烛光脉动、印章摆动、放大镜摇摆、徽章呼吸)。本应用 4 组动画全部使用 Alternate 模式。

7.2 四组属性动画详解

动画 组件 属性 时长 模式 效果
烛光脉动 案件大厅 candleScale(scale) 1600ms Alternate 3蜡烛 1.0↔1.12 缩放
印章摆动 案件大厅 stampAngle(rotate) 2400ms Alternate 已封案印章 0°↔6° 摆动
放大镜摇摆 剧本库 magAngle(rotate) 2000ms Alternate 🔍 0°↔12° 摇摆
徽章呼吸 战绩厅 badgeOpacity(opacity) 1800ms Alternate 已点亮徽章 1.0↔0.45 呼吸

烛光脉动动画:案件大厅横幅的 3 根蜡烛(🕯️),scale 从 1.0 到 1.12,1600ms 较快时长,Alternate+EaseInOut。3 根蜡烛大小不同(22vp/20vp/15vp)错落排列,模拟事务所桌上的蜡烛群。scale 缩放模拟烛光摇曳时的明暗变化,EaseInOut 曲线模拟烛光的自然呼吸节奏。

印章摆动动画:案件大厅横幅的"已封案"印章,rotate 从 0° 到 6°,2400ms 中等时长,Alternate+EaseInOut。印章以羊皮纸色描边悬挂在横幅右侧,缓慢摆动如同悬挂的封案印章在微风中摇曳。6° 小角度摆动增加真实感,不会过于夸张。

放大镜摇摆动画:剧本库顶部的放大镜(🔍),rotate 从 0° 到 12°,2000ms 中等时长,Alternate+EaseInOut。放大镜 26vp 位于卷宗书架标题右侧,缓慢摇摆如同侦探手持放大镜观察卷宗。12° 较大角度摆动增加观察的动态感。

徽章呼吸动画:战绩厅徽章墙的已点亮徽章,opacity 从 1.0 到 0.45,1800ms 较快时长,Alternate+EaseInOut。已点亮徽章鎏金描边,opacity 呼吸如同金属徽章在烛光下的光泽闪烁。未解锁徽章深褐描边,不参与呼吸动画,形成已点亮/未解锁的视觉区分。

7.3 无定时器循环动画的优势

传统循环动画使用 setInterval 逐帧更新,需手动管理定时器、频繁状态更新可能导致性能问题、组件销毁时需手动清除定时器否则内存泄漏。ArkTS 属性动画通过 iterations:-1 实现零定时器循环动画:框架底层自动调度动画帧、组件销毁时动画自动停止、多个动画可同时运行互不干扰。CLUE MANSION 4 组动画全部采用此方式。

7.4 交互反馈设计

项目通过多种方式提供即时交互反馈:

  • 按钮点击:立即触发状态变更,响应式机制自动更新 UI
  • 选中态:明确视觉变化(鎏金背景墨黑字/暗红背景羊皮纸字/鎏金描边)
  • Tab 切换:图标透明度(1.0/0.45)+ 文字颜色(鎏金/褐灰)+ 底部指示条(暗红/深褐)三重反馈
  • stepper:−/+ 点击后数字立即变化,边界值自动限制(人数3-8/恐怖度1-5/兑换数量1-库存)
  • 多选 chips:点击后立即切换选中态,cmToggleRole 纯函数实现有则删无则加
  • 紧急标签:暗红背景+急字,增加拼车紧迫感
  • 积分校验:积分不足时暗红警告,积分充足时显示兑换后余额
  • 售罄状态:库存0暗红显示,兑换按钮变售罄深褐不可点击
  • 删除警告:💀图标+暗红警告+战绩回显,增加操作谨慎性

这些即时反馈确保用户操作的"确定性",提升交互流畅度和沉浸感。恐怖承受度 stepper 的★☆动态显示是本应用的特色交互,cmStars 纯函数根据数值生成★☆组合,拖动 stepper 时星级同步变化。


第八章 案件大厅与剧本库系统

8.1 剧本数据模型

每部剧本(CMScript)包含:ID、CASE编号(no)、剧本名(name)、分类(category:情感/恐怖/推理/机制/阵营/欢乐)、恐怖度(horror:1-5星)、人数(players)、时长(duration)、价格(price,元/人)、热度(hot)、标签数组(tags)、封面主色 c1(cover1)、封面副色 c2(cover2)、emoji(图标)。

14 部剧本覆盖多种分类:雾山庄的第七位客人(推理/3星/7人/5小时/¥128/热度98/烧脑密室本格)、雨夜诊所(恐怖/4星/6人/4小时/¥108/热度92/惊悚追逐换装)、唐人街理发师(阵营/2星/8人/5小时/¥138/热度90/对抗阵营嘴炮)、午夜电车之谜(推理/3星/6人/4.5小时/¥118/热度86/本格时空反转)、血色婚礼(情感/1星/5人/4小时/¥98/热度84/催泪沉浸换装)、蜡像馆的最后一夜(恐怖/5星/6人/4.5小时/¥128/热度96/重恐单线NPC)、时空信使(机制/2星/7人/5小时/¥148/热度88/机制轮抽高能)、疯人院日记(恐怖/4星/7人/5小时/¥138/热度94/沉浸医务演绎)、绅士俱乐部疑案(阵营/3星/8人/5.5小时/¥158/热度91/阵营推理礼仪)、双面照相馆(推理/2星/6人/4小时/¥108/热度82/还原照片温情)、月光码头谋杀案(推理/3星/7人/5小时/¥128/热度87/海港连环硬核)、无声马戏团(欢乐/1星/8人/4小时/¥88/热度80/欢乐整活新手)、摆渡人的账本(机制/2星/6人/4.5小时/¥118/热度83/经济博弈策略)、雾都孤儿失踪案(情感/2星/6人/4小时/¥108/热度85/温情维多利亚沉浸)。

8.2 本周悬疑榜横幅

本周悬疑榜横幅是案件大厅的视觉核心,包含:

  • 135° 三色渐变 #C0392B→#A93226→#7B241C,暗红如同案发现场
  • THIS WEEK SUSPENSE 标签(羊皮纸色,letterSpacing 2)
  • 本周悬疑榜标题(羊皮纸色 20vp 加粗)
  • 「雾山庄的第七位客人」剧本名(鎏金色 14vp 加粗)
  • 三组数据:🔥98热度指数 / 7人本适合人数 / 恐怖★★★
  • 查看卷宗按钮(羊皮纸色背景墨黑字)
  • 3 根蜡烛🕯️(22/20/15vp,错落排列,candleScale 脉动 1600ms Alternate)
  • 已封案印章(羊皮纸色描边,stampAngle 摆动 2400ms Alternate)

两组特效动画(烛光脉动+印章摆动)共同营造维多利亚侦探事务所的真实场景感,暗红渐变横幅如同案发现场,3 根蜡烛错落排列模拟桌上蜡烛群,已封案印章悬挂摆动增加仪式感。本周悬疑榜是用户进入应用的第一视觉印象,两组特效确保"第一眼就被吸引"。

8.3 馆内快讯与热门剧本

馆内快讯展示 8 条最新动态:上新(《绅士俱乐部疑案》本周上架)、活动(周五恐怖夜五星本立减30)、公告(老城地下室门店完成隔音升级)、拼车(周末黄金场拼车缺口23人)、周边(黄铜侦探徽章第二批到货)、赛事(馆内推理大赛九月开启)、上新(《月光码头谋杀案》2.0修订结局)、提醒(雨季来临码头体验馆备好雨伞)。热门标签🔥标记 3 条。

热门剧本双列卡片展示 8 部首页精选(CM_HOME_SCRIPTS):每卡含封面渐变+emoji+HOT标签(热度≥90)+剧本名+恐怖星+人数时长+价格+查阅卷宗按钮。双列布局增加浏览效率,HOT标签暗红背景突出热门剧本。

8.4 快速拼车与本周场次

快速拼车横滚展示 9 车拼车:每卡含时间+剧本名+门店+缺人数+去拼车按钮,紧急车暗红描边。横滚 Scroll+Row 支持左右滑动,cmMissingTotal() 纯函数计算总缺人数。发起拼车入口卡片:想自己开车?+发起拼车按钮(暗红背景),引导用户主动发起拼车。

本周场次柱状图展示 7 天场次:周一12/周二18/周三9/周四22/周五38/周六46/周日31,合计176场。柱子高度 count/cmMaxWeek()*92 动态计算,cmMaxWeek() 返回46(周六峰值)。周六峰值暗红高亮,其他鎏金。柱状图直观展示周末高峰,周六以46场居首,周五38场紧随其后。

8.5 剧本库分类与恐怖度分布

剧本库顶部:卷宗书架标题 + 放大镜摇摆动画(🔍 26vp,magAngle 0°→12° 2000ms Alternate)。分类 chips 横滚展示 7 分类:全部/情感/恐怖/推理/机制/阵营/欢乐,选中鎏金背景墨黑字,cmFilterScripts(cat) 纯函数按分类筛选。

恐怖度分布色条展示 5 段:1星轻松2部/2星微恐5部/3星中恐4部/4星重恐2部/5星极恐1部。横向比例条每段 layoutWeight=count,动态宽度。底部图例每段含色点+分段名+部数。2星微恐以5部居首,3星中恐4部紧随其后,5星极恐仅1部(蜡像馆的最后一夜)。恐怖度分布帮助用户根据心理承受能力选择剧本。

8.6 卷宗列表

卷宗列表展示 14 部剧本(按分类筛选 cmFilterScripts):每卡含左侧 6vp 宽色条(cover1 单色,模拟卷宗书脊)+ CASE编号+重恐标签(恐怖度≥4)+剧本名+恐怖星(cmStars 纯函数)+分类标签(鎏金描边)+标签chips+人数时长价格+查阅按钮。

左侧色条是卷宗列表的特色设计,6vp 宽竖条使用剧本封面主色,模拟真实卷宗的书脊颜色。重恐标签(恐怖度≥4)暗红背景羊皮纸字,快速识别高恐怖度剧本。CASE编号(如CASE-041)增加卷宗档案的仪式感。查阅按钮暗红背景羊皮纸字,点击触发剧本详情弹框。


第九章 拼车场与战绩厅系统

9.1 拼车数据模型

每车拼车(CMCarpool)包含:ID、时间(time)、剧本名(scriptName)、门店(store)、总人数(total)、已上车(joined)、车费(fee,元/人)、是否紧急(urgent)、玩家数组(players:CMPlayer[])。

每位玩家(CMPlayer)包含:昵称(name)、等级(level)、颜色(color,头像背景色)、emoji(头像图标)。

9 车拼车覆盖多种场景:今天19:30雾山庄的第七位客人(雾山旗舰店/7人/已上5/缺2/¥128/紧急)、今天20:00雨夜诊所(老城地下室/6人/已上4/缺2/¥108/紧急)、今晚19:00蜡像馆的最后一夜(码头体验馆/6人/已上3/缺3/¥128)、今晚20:30唐人街理发师(唐人街分馆/8人/已上6/缺2/¥138)、明天14:00时空信使(雾山旗舰店/7人/已上2/缺5/¥148)、明晚19:30疯人院日记(老城地下室/7人/已上5/缺2/¥138/紧急)、周六13:30无声马戏团(码头体验馆/8人/已上3/缺5/¥88)、周六19:00绅士俱乐部疑案(唐人街分馆/8人/已上7/缺1/¥158/紧急)、周日18:30双面照相馆(雾山旗舰店/6人/已上4/缺2/¥108)。

9.2 拼车时间轴

拼车时间轴是拼车场的核心设计:左侧时间轴+右侧拼车卡片。左侧时间轴:时间(10vp)+圆点(紧急暗红/普通鎏金,10vp圆)+连接线(2vp宽深褐,layoutWeight(1))。右侧拼车卡:剧本名+紧急标签(急,暗红背景)+门店时间+玩家头像群(26vp圆,已上车玩家)+已上车数+缺人数(缺人>2鎏金/缺人≤2暗红高亮)+价格+加入/已满员按钮。

紧急车暗红描边,普通车深褐描边。玩家头像群展示已上车玩家,第1人标"车头"(在加入拼车弹框中)。缺人数高亮引导用户快速补位,缺人≤2暗红高亮增加紧迫感。cmMissingTotal() 纯函数计算所有拼车的总缺人数,标题显示"在拼9车·总缺X人"。

9.3 加入拼车确认

加入拼车弹框(CMJoinCarpoolForm)是拼车场的核心交互:剧本信息回显(封面渐变+emoji+剧本名+门店时间+缺人数+价格)+上车进度条(Progress 线性,鎏金,已上车/总人数)+车上侦探列表(头像+昵称+等级+胜场,第1人标"车头"其他标"队员")+费用明细(剧本车费¥128+道具与服装¥10+平台服务费¥5=合计¥143)+再想想/立即上车按钮。

上车进度条是本弹框的特色,Progress 线性组件展示已上车比例,鎏金色增加价值感。车上侦探列表展示玩家信息,帮助用户了解同车玩家。费用明细如同电商下单流程,合计金额加粗鎏金,增加透明度。

9.4 战绩数据模型

每条战绩(CMRecord)包含:ID、日期(date)、剧本名(scriptName)、身份(role)、结果(result:胜/负)、评分(score)、是否MVP(mvp)。

12 条战绩覆盖:08-25雾山庄的第七位客人(侦探/胜/9.2/MVP)、08-24雨夜诊所(值夜医生/胜/8.8)、08-23唐人街理发师(理发师/负/7.6)、08-22蜡像馆的最后一夜(守夜人/胜/9.5/MVP)、08-21血色婚礼(新郎/胜/8.2)、08-19疯人院日记(主治医师/负/7.1)、08-18午夜电车之谜(售票员/胜/8.9)、08-16绅士俱乐部疑案(凶手/胜/9.8/MVP)、08-15时空信使(信使/负/6.8)、08-13月光码头谋杀案(船长/胜/8.4)、08-12无声马戏团(驯兽师/胜/8.0)、08-10摆渡人的账本(摆渡人/负/6.5)。

9.5 生涯面板与徽章墙

侦探生涯面板展示 4 项核心统计:总场次128(本月+12)、胜率68%(高于馆均9%)、拿凶次数21(逃脱率76%)、MVP次数17(全馆前5%)。近12场 cmWinCount()=8 胜,最长连胜7场。

徽章墙展示 12 个徽章(cmLitBadgeCount()=7 已点亮/5 未解锁):初出茅庐🗝️/首案告破🧷/三连胜🔥/拿凶高手🔪/MVP收割机🏆/百场老侦探🎩(已点亮)、密室克星🔓/恐怖不眨眼🗿/全类型通吃🗂️/拼车之王🚕/线索收藏家🧩/传奇侦探👑(未解锁)。已点亮徽章 badgeOpacity 呼吸 1800ms Alternate,鎏金描边;未解锁深褐描边。

徽章呼吸动画是战绩厅的特色,已点亮徽章 opacity 呼吸如同金属徽章在烛光下的光泽闪烁。7 个已点亮徽章展示用户成就,5 个未解锁徽章提供成长目标。MVP收割机(获得十次最佳演绎)和百场老侦探(累计百场)是高阶成就。

9.6 评分分布与战绩表格

评分分布条形图展示 5 档:9.0分以上18场(鎏金)、8.0-8.9分32场(亮鎏金)、7.0-7.9分41场(褐灰)、6.0-6.9分24场(浅褐灰)、6.0分以下13场(深褐)。条形宽度 count/cmMaxRating()*100% 动态计算,cmMaxRating() 返回41(7.0-7.9分峰值)。7.0-7.9分以41场居首,8.0-8.9分32场紧随其后。

最近战绩表格展示 12 行(斑马纹:偶数行#221B15/奇数行#1A140E):日期/剧本/身份/结果(胜鎏金/负褐灰)/评分(鎏金★)/操作(删除暗红描边)。表格头部深褐背景,列宽通过 layoutWeight 分配。删除按钮触发血红警告删除弹框。

9.7 玩家排行

全馆侦探榜展示 8 人:排名(1鎏金/2羊皮纸/3暗红/其他褐灰,18vp加粗)+头像emoji+昵称+场次+胜率(15vp加粗,前三鎏金/其他褐灰)+胜率进度条(58vp宽背景+动态宽度前景,前三鎏金/其他深褐)。波洛酱412场74%胜率居首,第七病患388场72%第二,福尔摩鸭356场71%第三。排行前三特殊配色增加荣誉感,胜率进度条直观展示。


第十章 线索铺与侦探档系统

10.1 线索道具数据模型

每件线索道具(CMClue)包含:ID、名称(name)、emoji(图标)、所需积分(cost)、库存(stock)、描述(desc)。

12 件道具覆盖侦探周边:黄铜放大镜挂件🔍(180分/库存24/馆徽周边可刻字)、烟斗香料补充包🚬(120分/库存46/樱桃木香无烟)、袖珍显微镜片🔬(260分/库存12/观察纤维与尘土)、怀表延长链⌚(320分/库存8/维多利亚复刻款)、火漆密写信封✉️(80分/库存88/含血红色火漆印)、血迹显影剂🧪(450分/库存5/鲁米诺道具夜光)、指纹提取粉套装🖌️(150分/库存30/含毛刷与胶带)、侦探笔记封皮📒(220分/库存16/牛皮材质可插笔)、复古烛灯🕯️(200分/库存21/LED烛芯安全)、雾都晚报号外📰(90分/库存60/案件主题旧报纸)、黄铜侦探徽章🎖️(500分/库存0/限量款已兑完)、闻香瓶·雨后石板🫙(280分/库存11/限定香型氛围道具)。

10.2 积分余额卡

积分余额卡是线索铺的核心视觉:深褐→暗红 90° 渐变(#0F0C09→#7B241C),118vp高,16vp圆角。我的线索积分标签 + 2,680分(34vp加粗鎏金)+ 分(12vp鎏金)+ 本周+120(鎏金)/累计兑换34件(褐灰)/过期规则(褐灰)+ 🧩emoji(30vp,右上角)。

深褐→暗红渐变如同旧钱包,2,680 分 34vp 加粗鎏金展示余额,积分获取规则(每完成一场+15,MVP再+30)增加透明度。🧩emoji 位于右上角增加装饰感。

10.3 道具表格与兑换

线索道具铺表格展示 12 件道具:表头(道具/所需积分/库存/操作,鎏金字,深褐背景)+ 12 行(斑马纹偶数行#221B15/奇数行#1A140E)。每行:emoji+名称+描述(layoutWeight 2.1)+所需积分(48vp右对齐,鎏金加粗)+库存(34vp右对齐,售罄0暗红)+操作(50vp,兑换鎏金按钮/售罄深褐不可点击)。

血迹显影剂450分库存5是高价稀有道具,黄铜侦探徽章500分已兑完是限量款。兑换按钮鎏金背景墨黑字,点击触发线索兑换弹框。售罄道具库存显示0暗红,按钮变"售罄"深褐不可点击。

10.4 线索兑换宫格

线索兑换弹框(CMClueExchangeForm)是线索铺的核心交互:当前积分2,680(每完成一场+15,MVP再+30)+ 3×4道具宫格(12件,每格emoji+名称+积分,选中鎏金2vp描边+深褐背景,未选中深褐1vp描边,售罄不可选)+兑换数量stepper(1-库存)+所需积分合计(道具单价×数量动态计算)+积分校验(积分不足⚠暗红警告/积分充足显示兑换后余额)+再逛逛/确认兑换(鎏金背景墨黑字)。

道具宫格是本弹框的特色设计,12 件道具以 3×4 宫格展示,选中鎏金描边高亮。数量 stepper 边界 1-库存,所需积分合计动态计算。积分校验逻辑增加用户体验:积分不足时暗红警告"⚠ 积分不足,去打两场本再回来",积分充足时显示"兑换后余额 X 积分"。balance 状态变量管理当前积分,兑换时校验。

10.5 侦探档数据模型

侦探属性(CMAttrBar)4 维:推理力88(鎏金)、演技72(暗红)、胆量64(羊皮纸)、体力90(褐灰)。

成就(CMAchievement)6 项:卷宗入门🗝️(完成新手三案·已达成)、完美犯罪🔪(凶手身份零票逃脱·已达成)、守夜人🕯️(连打三场重恐本·已达成)、车头老手🚕(发起拼车三十次·已达成)、线索猎人🧩(兑换十五件道具·进行中9/15)、传奇侦探👑(全馆胜率登顶·进行中)。

设置项(CMProfileSetting)5 项:拼车提醒(开拼即通知)、夜间模式(跟随系统)、战绩隐私(仅好友可见)、常驻门店(雾山旗舰店)、关于谜馆(v3.2.1)。

10.6 头像大卡与属性进度

头像大卡是侦探档的核心视觉:深褐→暗红 135° 渐变(#0F0C09→#7B241C),150vp高,18vp圆角。🕵️头像 78vp 圆(羊皮纸背景+鎏金3vp描边)+ 昵称雾都小侦探 19vp 加粗 + Lv.36标签(鎏金背景墨黑字)+称号「雾都名侦探」(鎏金)+ ID:CM-2026-0421·入馆412天(褐灰)+ 徽章7/12(褐灰)/积分2,680(鎏金)/常驻雾山旗舰店(褐灰)+ 编辑按钮(鎏金描边羊皮纸字)+ 🕯️蜡烛(22vp,右上角)。

侦探属性进度条展示 4 维:推理力88鎏金/演技72暗红/胆量64羊皮纸/体力90褐灰。每维:标签(46vp)+背景条(60%宽10vp深褐)+前景条(value*0.54宽10vp对应色,position定位)+数值(加粗对应色)。综合评级 S级·全馆前3%(鎏金加粗)。

属性进度条是侦探档的特色设计,4 维属性以不同颜色进度条展示,推理力88鎏金最高,胆量64羊皮纸最低。综合评级 S 级增加成就感,全馆前3%增加稀缺感。前景条使用 position 定位覆盖在背景条上,实现进度效果。

10.7 成就列表与设置

成就列表展示 6 项:emoji+名称+描述(layoutWeight 1)+已达成(鎏金背景墨黑字)/进行中(深褐背景褐灰字)。4 项已达成(卷宗入门/完美犯罪/守夜人/车头老手),2 项进行中(线索猎人9/15/传奇侦探)。

馆务设置展示 5 项:icon+标签(layoutWeight 1)+值(褐灰)+›箭头。退出登录(暗红文字,无箭头)。设置项列表简洁明了,退出登录暗红文字增加警示。


第十一章 性能优化与扩展方向

11.1 性能优化策略

  1. 合理使用 @State:只有需触发 UI 更新的状态用 @State,静态数据用 const。项目中约 20 个 @State 变量,均为必要的交互状态和动画属性。
  2. 列表渲染优化:使用 ForEach 框架自动 diff 复用,列表项固定尺寸避免布局抖动。14 剧本/9 拼车/12 战绩/12 道具/12 徽章等列表均使用 ForEach。
  3. 动画性能:使用属性动画而非逐帧动画,动画属性限制在 opacity/rotate/scale 等合成层属性避免重排,无限循环用 iterations:-1 零定时器。4 组动画全部采用此方式。
  4. 减少重渲染:弹框条件渲染未显示不渲染,避免 build() 中复杂计算。6 个弹框均通过 if 条件渲染。
  5. 资源优化:使用 emoji 作图标无需图片,渐变背景用 linearGradient 无需图片,双色渐变色块模拟剧本封面无需真实图片。
  6. 纯函数封装:17 个纯函数处理数据转换和业务逻辑,无副作用易测试。cmStars/cmToggleRole/cmClueAt/cmBadgeAt 等纯函数减少重复代码。

11.2 代码规范

命名规范:组件名大驼峰(CMHomeContent)、变量函数小驼峰(activeTab)、常量大写下划线(CM_COLORS)、接口大驼峰(CMScript)、统一 CM 前缀。文件结构:接口→全局常量→纯函数→弹框组件→Tab 组件→根入口。代码风格:2 空格缩进、语句末尾不加分号、对象字面量每行一属性。

11.3 可维护性与可扩展性

组件化拆分约 14 个独立组件职责单一。数据与视图分离(数据在 const 常量,UI 只读渲染)。逻辑与视图分离(业务逻辑封装在 17 个纯函数中)。配置化设计(Tab/分类/难度/门店/位置/时段均通过配置数据驱动)。新增剧本/拼车/战绩/道具只需在对应常量数组中添加数据,无需修改 UI 代码。

11.4 功能扩展方向

案件大厅深化:实时拼车推送、剧本推荐算法、好友动态、活动专题页、VR探案体验。
剧本库深化:剧本详情页扩展、用户评价与评分、剧本收藏、剧本对比、DM评价、剧本预告。
拼车场深化:拼车筛选、好友拼车、拼车聊天室、拼车评价、自动补位、拼车保险。
战绩厅深化:生涯详细统计、胜率趋势图、MVP集锦、对手分析、赛季排行、成就解锁动画。
线索铺深化:道具详情页、道具使用效果、限量抢购、积分商城、道具交易、盲盒抽取。
侦探档深化:个人主页、好友系统、私信、动态发布、粉丝关注、个人作品集。

11.5 技术演进方向

短期(1-3 月):对接后端 API 替换硬编码、引入状态管理库、增加本地存储、完善错误处理、真机性能测试、支付 SDK 集成。
中期(3-6 月):引入路由管理、推送通知拼车提醒、接入支付 SDK 实现在线拼车支付和道具购买、性能优化懒加载、适配平板折叠屏、多语言国际化、图片上传 CDN。
长期(6-12 月):AI 剧本推荐(基于偏好和历史)、AR 搜证(在真实环境中搜索虚拟线索)、语音推理(语音输入推理过程)、直播开本(DM 直播带本)、跨设备协同(手机预约/平板看剧本/智慧屏看线索)、云存档同步。

11.6 鸿蒙生态融合

原子化服务:将预约拼车/查看剧本/兑换道具封装为原子化服务,无需安装即用。万能卡片:提供今日拼车/本周剧本/积分余额/战绩排行卡片,桌面实时查看。多设备协同:手机预约拼车/平板看剧本/手表到店提醒/智慧屏看线索直播。小艺语音:语音查询剧本/预约拼车/提醒到店。健康数据:与华为运动健康 App 数据互通,剧本杀的脑力活动数据自动同步。


第十二章 剧本杀文化与安全提示

12.1 剧本杀文化

剧本杀(Murder Mystery Game)起源于 19 世纪的英国派对游戏,20 世纪在美国发展为商业化的桌面推理游戏,2016 年前后传入中国并迅速爆发。剧本杀的核心玩法是:玩家扮演剧本中的角色,通过搜证、推理、交流、演绎,找出隐藏在玩家中的凶手,还原案件真相。

剧本杀的类型丰富多样:本格推理(逻辑推理为主,如雾山庄的第七位客人)、变格推理(超自然元素,如时空信使)、情感本(情感沉浸为主,如血色婚礼)、恐怖本(惊悚恐怖为主,如蜡像馆的最后一夜)、机制本(游戏机制为主,如摆渡人的账本)、阵营本(阵营对抗为主,如唐人街理发师)、欢乐本(轻松欢乐为主,如无声马戏团)。

CLUE MANSION 谜馆正是这一文化的产物:14 部剧本覆盖 7 种分类,恐怖度从 1 星到 5 星全覆盖,人数从 5 人到 8 人,时长从 4 小时到 5.5 小时,价格从 ¥88 到 ¥158。6 个 Tab 覆盖从剧本浏览到拼车社交、从战绩成就到道具收藏的完整剧本杀生活链路。

12.2 剧本杀类型与特点

本格推理:以逻辑推理为核心,案件在现实逻辑框架内可解,强调线索链和推理过程。优点是烧脑过瘾、推理成就感强,适合喜欢逻辑推理的玩家。CLUE MANSION 中雾山庄的第七位客人(3星/7人/5小时/¥128)、午夜电车之谜(3星/6人/4.5小时/¥118)、月光码头谋杀案(3星/7人/5小时/¥128)属于本格推理。

恐怖本:以惊悚恐怖氛围为核心,通过 NPC 演绎、单线任务、恐怖场景营造恐惧感。优点是沉浸感强、刺激过瘾,适合喜欢挑战胆量的玩家。CLUE MANSION 中蜡像馆的最后一夜(5星/6人/4.5小时/¥128,重恐单线NPC)、雨夜诊所(4星/6人/4小时/¥108,惊悚追逐换装)、疯人院日记(4星/7人/5小时/¥138,沉浸医务演绎)属于恐怖本。

情感本:以情感沉浸为核心,通过角色故事和情感冲突引发玩家情感共鸣。优点是催泪感人、社交属性强,适合喜欢情感体验和社交的玩家。CLUE MANSION 中血色婚礼(1星/5人/4小时/¥98,催泪沉浸换装)、雾都孤儿失踪案(2星/6人/4小时/¥108,温情维多利亚沉浸)属于情感本。

阵营本:以阵营对抗为核心,玩家分为不同阵营,通过博弈、欺骗、合作达成阵营目标。优点是对抗性强、嘴炮过瘾,适合喜欢社交博弈的玩家。CLUE MANSION 中唐人街理发师(2星/8人/5小时/¥138,对抗阵营嘴炮)、绅士俱乐部疑案(3星/8人/5.5小时/¥158,阵营推理礼仪)属于阵营本。

机制本:以游戏机制为核心,通过特殊规则和机制增加游戏趣味性。优点是玩法新颖、策略性强,适合喜欢游戏机制的玩家。CLUE MANSION 中时空信使(2星/7人/5小时/¥148,机制轮抽高能)、摆渡人的账本(2星/6人/4.5小时/¥118,经济博弈策略)属于机制本。

欢乐本:以轻松欢乐为核心,通过搞笑剧情和互动营造欢乐氛围。优点是轻松愉快、适合新手和团建,CLUE MANSION 中无声马戏团(1星/8人/4小时/¥88,欢乐整活新手)属于欢乐本。

12.3 剧本杀安全提示

  • 心理安全:恐怖本可能引发强烈的情绪反应,如有心脏病、高血压、癫痫、恐惧症等疾病或孕妇,不建议玩恐怖本。游戏中如感到不适,可随时喊"停"要求暂停或退出。DM 应在游戏前告知恐怖程度和可能的触发点。
  • 人身安全:恐怖本的单线任务和追逐场景可能存在碰撞风险,注意场地安全,不要奔跑推搡。换装时注意服装和道具的安全性,避免尖锐道具伤人。游戏中如感到身体不适,立即告知 DM。
  • 社交安全:剧本杀是社交游戏,注意个人隐私保护,不要在游戏中透露真实个人信息。拼车时注意与陌生人保持适当距离,如遇到骚扰或不当行为,及时告知店家或报警。不要单独与陌生玩家前往偏僻场所。
  • 时间安全:剧本杀通常时长 4-6 小时,注意合理安排时间,避免影响工作和休息。深夜场结束后注意交通安全,建议结伴而行或使用正规交通工具。
  • 消费安全:选择正规剧本杀门店,确认价格包含内容(是否含服装、道具、饮料等),避免隐形消费。拼车前确认车费分摊方式,避免事后纠纷。
  • 版权安全:支持正版剧本,拒绝盗版剧本。正版剧本经过作者授权和质量把控,游戏体验更有保障。盗版剧本可能存在内容缺失、印刷质量差等问题。

12.4 剧本选择与 DM 重要性

剧本选择建议

  • 新手建议从欢乐本或情感本入门(如无声马戏团¥88/血色婚礼¥98),避免一开始就玩硬核推理或重恐本。
  • 根据团队人数选择剧本,5 人本(血色婚礼)适合小团体,8 人本(唐人街理发师/无声马戏团/绅士俱乐部疑案)适合大团体。
  • 根据团队偏好选择类型,推理爱好者选本格本,社交爱好者选阵营本,胆量挑战者选恐怖本。
  • 恐怖度根据心理承受能力选择,1-2 星适合大多数玩家,3-4 星需要一定心理承受能力,5 星(蜡像馆的最后一夜)仅推荐重恐玩家。
  • 可以先查看剧本评价和评分,选择口碑较好的剧本。

DM(Dungeon Master)的重要性

  • DM 是剧本杀游戏的主持人,负责引导游戏流程、扮演 NPC、控制节奏、判定规则。
  • 一个好的 DM 可以让普通剧本变得精彩,一个差的 DM 可以让好剧本变得无聊。
  • CLUE MANSION 中剧本详情展示 DM 信息(如 DM 阿鸦·从业5年·演绎分9.6),近期场次表格展示每场的 DM,帮助玩家选择。
  • 建议选择经验丰富、演绎能力强的 DM,特别是恐怖本和情感本对 DM 演绎能力要求较高。
  • 游戏后可以对 DM 进行评价,帮助其他玩家选择。

第十三章 推理社交与剧本杀生活方式

13.1 剧本杀生活方式

剧本杀正在成为当代年轻人的主流社交方式。在快节奏的数字时代,剧本杀提供了一种"慢下来"的可能性——用 4-6 小时沉浸在另一个角色的人生中,通过推理和交流与陌生人建立连接,这个过程本身就是一种社交疗愈。

CLUE MANSION 谜馆的设计理念正是"推理社交生活方式":维多利亚暗黑卷宗配色营造悬疑推理的氛围,烛光脉动动画模拟事务所的真实场景,卷宗档案式弹框增加推理仪式感,拼车场提供社交连接,战绩厅提供成就激励,线索铺提供收藏乐趣,侦探档提供个人成长。6 个 Tab 覆盖从剧本浏览到拼车社交、从战绩成就到道具收藏的完整剧本杀生活链路。

13.2 推理社交的价值

推理社交具有独特的社交价值:

  • 深度连接:剧本杀需要玩家在 4-6 小时内深度互动,通过角色交流和推理合作建立比普通社交更深的连接。一场剧本杀下来,陌生人可以变成朋友。
  • 角色扮演:玩家可以在剧本中体验不同的人生,扮演与自己性格迥异的角色,释放压力,拓展自我认知。
  • 逻辑思维:推理过程锻炼逻辑思维能力,通过线索链分析、动机分析、时间线分析等提升推理能力。
  • 表达能力:剧本杀需要玩家表达自己的观点和推理,锻炼口头表达和说服能力。
  • 情绪体验:情感本可以体验不同的情感,恐怖本可以体验刺激和恐惧,欢乐本可以体验轻松和快乐,丰富情感体验。
  • 成就感:成功推理出凶手、获得 MVP、完成高难度剧本,都能带来强烈的成就感。

CLUE MANSION 的拼车场提供社交连接平台,玩家可以通过拼车认识志同道合的朋友。战绩厅记录推理成就,徽章墙收集成长里程碑。侦探档展示个人推理属性(推理力/演技/胆量/体力),帮助玩家了解自己的推理风格。

13.3 维多利亚美学与暗黑仪式感

维多利亚时代(1837-1901)是英国历史上的黄金时代,也是侦探小说的黄金时代。柯南·道尔的福尔摩斯、阿加莎·克里斯蒂的波洛、切斯特顿的布朗神父,都诞生于这个时代。维多利亚美学的特点是:复古优雅、神秘浪漫、暗黑悬疑、仪式感强。

CLUE MANSION 谜馆的设计中融入了维多利亚美学:

  • 暗黑卷宗配色:深褐 #171310 背景、暗红 #A93226 强调、鎏金 #C9A227 荣耀、羊皮纸 #E8D5A3 标题,如同维多利亚时代侦探事务所烛光下的卷宗档案。
  • 卷宗装订孔:剧本详情弹框的封面左侧有 3 个装订孔,模拟真实卷宗的装订孔,增加复古仪式感。
  • CASE 编号:每部剧本有 CASE 编号(如 CASE-041),如同侦探事务所的案件档案编号。
  • 已封案印章:案件大厅横幅的"已封案"印章,如同案件结案后的盖章仪式。
  • 烛光脉动:3 根蜡烛错落排列,模拟事务所桌上的蜡烛群,增加维多利亚时代的氛围。
  • 放大镜摇摆:剧本库顶部的放大镜缓慢摇摆,如同侦探手持放大镜观察卷宗。
  • 侦探属性:推理力/演技/胆量/体力四维属性,综合评级 S 级,如同侦探执照评级。

这些设计元素共同营造了维多利亚侦探事务所的暗黑仪式感,让用户在使用应用时仿佛置身于 19 世纪的侦探事务所,增加沉浸感和仪式感。

13.4 剧本杀入门建议

  • 从体验课开始:建议先玩欢乐本或情感本(如无声马戏团¥88/血色婚礼¥98),感受剧本杀的乐趣和自己的兴趣程度,不要一开始就玩硬核推理或重恐本。
  • 选择适合的类型:推理爱好者选本格本,社交爱好者选阵营本,胆量挑战者选恐怖本,情感丰富者选情感本,轻松娱乐者选欢乐本。可以多尝试几种类型,找到自己最喜欢的方向。
  • 选择靠谱的 DM:DM 是剧本杀体验的关键,建议选择经验丰富、演绎能力强的 DM。可以通过朋友推荐、线上评价、店家推荐等方式选择好 DM。
  • 积极参与:剧本杀需要玩家积极参与,主动表达观点和推理,不要害怕说错。积极参与才能获得更好的体验和更多的乐趣。
  • 尊重其他玩家:剧本杀是团队游戏,尊重其他玩家的观点和感受,不要打断别人发言,不要剧透,不要贴脸(用场外信息攻击其他玩家)。
  • 找到固定车队:剧本杀不是一次性活动,找到志同道合的固定车队可以获得更稳定的体验和更深的友谊。可以通过拼车场认识新朋友,逐渐建立固定车队。
  • 安全第一:剧本杀涉及恐怖场景、情绪波动、长时间游戏等,务必注意心理和人身安全,感到不适随时喊停,选择正规门店和靠谱 DM。

第十四章 开发环境搭建与运行指南

本章介绍如何在本地搭建 HarmonyOS 开发环境并运行 CLUE MANSION 谜馆项目。从 DevEco Studio 安装到项目创建、SDK 配置、预览调试,完整覆盖从零到运行的全流程。

14.1 安装 DevEco Studio

DevEco Studio 是华为官方推出的 HarmonyOS 应用开发 IDE,基于 IntelliJ IDEA Community 开源版本构建,提供代码编辑、编译构建、调试运行、模拟器等完整开发工具链。

系统要求

  • 操作系统:Windows 10/11(64位)、macOS 12+、Ubuntu 20.04+
  • 内存:最低 8GB,推荐 16GB 及以上
  • 硬盘:至少 10GB 可用空间(含 SDK 和模拟器)
  • 分辨率:1920×1080 及以上

安装步骤

  1. 访问华为开发者联盟官网(developer.huawei.com),进入"开发"→"DevEco Studio"下载页面
  2. 选择对应操作系统的安装包下载(Windows .exe / macOS .dmg / Linux .tar.gz)
  3. Windows 双击 .exe 安装包,按照安装向导逐步完成安装
  4. 首次启动 DevEco Studio,会提示选择 UI 主题(Darcula 深色 / Light 浅色)
  5. 启动后进入欢迎界面,可选择创建新项目、打开已有项目、配置 SDK 等

DevEco Studio安装界面

14.2 配置 HarmonyOS SDK

DevEco Studio 安装完成后,需要配置 HarmonyOS SDK 才能进行应用开发。SDK 包含编译器、工具链、系统镜像、API 文档等开发所需的全部组件。

配置步骤

  1. 打开 DevEco Studio,进入欢迎界面
  2. 点击"Configure"→"SDK Manager"(或在已打开项目中通过 File→Settings→SDK Manager 进入)
  3. 在 SDK Manager 中选择 HarmonyOS SDK 标签页
  4. 选择需要安装的 API 版本(推荐 API 9 及以上,支持 ArkTS 声明式开发)
  5. 勾选需要的 SDK 组件:HarmonyOS SDK(核心)、Toolchains(工具链)、Emulator(模拟器镜像)、Samples(示例代码)
  6. 点击"Apply"开始下载和安装,等待安装完成
  7. 安装完成后,SDK 路径会自动配置,可在 SDK Manager 中查看和修改

SDK 版本说明

  • API 9:支持 ArkTS 声明式 UI 开发,Stage 模型,是当前主流开发版本
  • API 10/11:新增更多系统能力和组件,推荐使用最新稳定版
  • 本项目 CLUE MANSION 使用 ArkTS 声明式 UI,建议 API 9 及以上版本运行

SDK配置界面

14.3 创建 HarmonyOS 项目

SDK 配置完成后,可以创建新的 HarmonyOS 项目来运行 CLUE MANSION 谜馆。

创建步骤

  1. 在 DevEco Studio 欢迎界面点击"Create Project"
  2. 选择项目模板:选择"Application"→"Empty Ability"(空能力模板),点击"Next"
  3. 配置项目信息:
    • Project name:项目名称,如"ClueMansion"
    • Bundle name:包名,如"com.example.cluemansion"
    • Save location:项目保存路径
    • Compile SDK:选择已安装的 API 版本(推荐 API 9 及以上)
    • Model:选择"Stage"模型(ArkTS 声明式开发推荐)
    • Language:选择"ArkTS"
  4. 点击"Finish"创建项目,等待项目初始化和依赖下载完成
  5. 项目创建完成后,在左侧项目树中找到 entry/src/main/ets/pages/Index.ets 文件
  6. 将本文末尾的完整 ArkTS 源码复制到 Index.ets 文件中,覆盖原有内容
  7. 保存文件(Ctrl+S / Cmd+S)

项目结构说明

  • entry/:主模块目录
  • entry/src/main/ets/:ArkTS 源码目录
  • entry/src/main/ets/pages/:页面目录,Index.ets 是入口页面
  • entry/src/main/resources/:资源目录(图片、字符串、配置等)
  • entry/src/main/module.json5:模块配置文件
  • build-profile.json5:构建配置文件

创建项目界面

14.4 运行应用

项目代码编写完成后,可以在模拟器或真机上运行 CLUE MANSION 谜馆。

在模拟器上运行

  1. 点击 DevEco Studio 工具栏的"Device Manager"图标(或通过 Tools→Device Manager 打开)
  2. 在 Device Manager 中点击"Create Device"创建新模拟器
  3. 选择设备型号(如 Phone→Mate 60 Pro),点击"Next"
  4. 选择系统镜像(对应已安装的 API 版本),点击"Next"
  5. 配置模拟器参数(名称、内存、存储等),点击"Finish"
  6. 创建完成后,点击模拟器旁的"启动"按钮,等待模拟器启动
  7. 模拟器启动后,在 DevEco Studio 工具栏选择该模拟器作为运行目标
  8. 点击"Run"按钮(绿色三角形)或按 Shift+F10 运行应用
  9. 等待编译构建完成,应用会自动安装并启动在模拟器上

在真机上运行

  1. 将 HarmonyOS 手机通过 USB 连接到电脑
  2. 在手机上开启"开发者模式"(设置→关于手机→连续点击版本号 7 次)
  3. 开启"USB 调试"(设置→系统和更新→开发人员选项→USB 调试)
  4. 在 DevEco Studio 工具栏选择连接的真机作为运行目标
  5. 点击"Run"按钮运行应用,应用会自动安装到手机并启动
  6. 首次运行可能需要在手机上确认安装和调试授权

常见问题

  • 编译报错:检查 SDK 版本是否匹配,检查代码语法是否正确,查看 Build 窗口的错误信息
  • 模拟器启动慢:首次启动需要解压系统镜像,后续启动会快很多;建议分配足够内存
  • 真机连接失败:检查 USB 线是否支持数据传输,检查 USB 调试是否开启,尝试更换 USB 端口
  • 应用闪退:检查 Logcat 窗口的崩溃日志,定位空指针或资源缺失等问题

运行应用界面

14.5 预览调试

DevEco Studio 提供了强大的预览器(Previewer)功能,可以在不运行模拟器的情况下实时预览 ArkTS 声明式 UI 的渲染效果,支持多设备、多语言、深色模式等预览场景。

使用预览器

  1. 打开 Index.ets 文件,点击编辑器右上角的"Previewer"标签页
  2. 预览器会自动编译并渲染当前页面的 UI
  3. 修改代码后保存,预览器会自动热更新渲染结果
  4. 可在预览器工具栏切换设备型号、屏幕方向、深色/浅色模式、语言等
  5. 可在预览器中直接点击交互元素,测试按钮点击、Tab 切换等交互

预览器优势

  • 实时预览:代码修改后秒级更新,无需等待完整编译和模拟器启动
  • 多设备适配:可同时预览多种设备型号,快速验证响应式布局
  • 交互测试:支持点击、滑动等基础交互测试,可验证弹框、Tab 切换等
  • 组件树查看:可查看 UI 组件树结构,定位布局问题
  • 性能分析:可查看渲染帧率、组件重建次数等性能指标

调试技巧

  • 使用 console.log() 在 Logcat 窗口输出调试信息
  • 在代码行号旁点击设置断点,运行时可暂停查看变量值
  • 使用 DevEco Studio 的 Profiler 工具分析 CPU、内存、网络性能
  • 对于动画效果,建议在真机上测试以获得最真实的体验
  • 预览器中动画可能以简化方式渲染,完整动画效果请在模拟器或真机上验证

CLUE MANSION 谜馆的 4 组属性动画(烛光脉动、印章摆动、放大镜摇摆、徽章呼吸)在预览器中可基本预览,在模拟器或真机上可获得完整的流畅动画体验。建议在真机上测试以获得最佳的视觉效果和交互体验。

预览调试界面


总结与展望

CLUE MANSION 谜馆是一款融合了维多利亚暗黑卷宗美学、潮流剧本杀社交、侦探事务所真实场景的 HarmonyOS ArkTS 概念应用。通过约 2700 行单文件代码,实现了 6 Tab 完整业务链路、6 套差异化弹框、4 组属性动画、17 个纯函数业务逻辑、20 个数据接口、约 20 组全局数据常量,覆盖了从剧本浏览到拼车社交、从战绩成就到道具收藏、从个人成长到推理社交的完整剧本杀生活场景。

核心价值

  • 视觉设计:维多利亚暗黑卷宗 15 色调色板,暗红×鎏金×羊皮纸三色对撞,卷宗装订孔与书脊色带,营造暗黑仪式感
  • 交互体验:6 Tab 导航+6 弹框交互,难度宫格/人数stepper/擅长位置多选/恐怖承受度/费用明细/积分校验等丰富交互组件
  • 动画特效:烛光脉动(scale)、印章摆动(rotate)、放大镜摇摆(rotate)、徽章呼吸(opacity),4 组属性动画营造事务所场景
  • 数据可视化:本周场次柱状图、恐怖度分布色条、评分分布条形图、胜率进度条、侦探属性进度条,5 类数据可视化增强信息表达
  • 工程架构:单文件组件化架构,@Builder 封装复用,三级状态管理,17 纯函数业务逻辑,配置化数据驱动,易于维护和扩展

技术亮点

  • 零定时器循环动画(iterations:-1),框架自动调度,组件销毁自动停止
  • cmStars 纯函数生成★☆组合,避免硬编码恐怖度显示
  • cmToggleRole 纯函数实现多选切换,有则删无则加
  • cmClueAt/cmBadgeAt 安全索引函数避免越界访问
  • 卷宗装订孔设计(3个7vp圆)模拟真实卷宗装订
  • 拼车时间轴(时间+圆点+连接线)模拟车站发车表
  • 侦探属性四维进度条,S级综合评级
  • 积分校验逻辑(不足暗红警告/充足显示余额)

未来展望
CLUE MANSION 谜馆作为概念应用,已具备完整的 UI 框架和交互体系。未来可从以下方向深化:对接后端 API 实现真实数据、引入支付 SDK 实现在线拼车支付和道具购买、增加图片/视频上传实现真实剧本封面、AI 剧本推荐(基于偏好和历史)、AR 搜证(在真实环境中搜索虚拟线索)、语音推理(语音输入推理过程)、直播开本(DM 直播带本)、鸿蒙原子化服务和万能卡片适配、跨设备协同(手机/平板/手表/智慧屏)。

剧本杀是一种融合了推理、社交、演绎、情感的沉浸式娱乐方式,在数字时代焕发新的生命力。CLUE MANSION 谜馆希望通过技术与美学的结合,让更多人感受到推理的乐趣、社交的温暖、演绎的魅力、以及维多利亚侦探事务所的暗黑仪式感。真相只有一个,但通往真相的路,有无数条。

CLUE MANSION 谜馆总结


附录:完整 ArkTS 源码

以下是 CLUE MANSION 谜馆的完整 ArkTS 源码,约 2700 行,单文件结构。将代码复制到 HarmonyOS 项目的 entry/src/main/ets/pages/Index.ets 文件中即可运行。

// ============================================================
// CLUE MANSION 谜馆(精简版)
// 保留了6大核心Tab与核心弹窗,删减了大量重复UI与复杂图表
// ============================================================

// ============ 配色(维多利亚暗黑卷宗风) ============
interface CMColorPalette {
  bg: string
  card: string
  deep: string
  line: string
  blood: string
  parchment: string
  gold: string
  textMain: string
  textSub: string
  textHint: string
}

const CM_COLORS: CMColorPalette = {
  bg: '#171310',
  card: '#221B15',
  deep: '#0F0C09',
  line: '#3A2F24',
  blood: '#A93226',
  parchment: '#E8D5A3',
  gold: '#C9A227',
  textMain: '#F0E6D2',
  textSub: '#A99C86',
  textHint: '#6E6353'
}

// ============ Tab 定义 ============
enum CMTab {
  Hall = 0,
  Scripts = 1,
  Carpool = 2,
  Records = 3,
  Clue = 4,
  Profile = 5
}

interface CMTabItem {
  tab: CMTab
  icon: string
  label: string
}

const CM_TABS: CMTabItem[] = [
  { tab: CMTab.Hall, icon: '🕵️', label: '案件大厅' },
  { tab: CMTab.Scripts, icon: '📜', label: '剧本库' },
  { tab: CMTab.Carpool, icon: '🚕', label: '拼车场' },
  { tab: CMTab.Records, icon: '🏆', label: '战绩厅' },
  { tab: CMTab.Clue, icon: '🧩', label: '线索铺' },
  { tab: CMTab.Profile, icon: '👤', label: '侦探档' }
]

// ============ 简化数据 ============
interface CMScript {
  id: number
  no: string
  name: string
  category: string
  players: string
  duration: string
  price: number
  emoji: string
  cover: string
}

const CM_SCRIPTS: CMScript[] = [
  { id: 1, no: 'CASE-041', name: '雾山庄的第七位客人', category: '推理', players: '7人', duration: '5小时', price: 128, emoji: '🏰', cover: '#A93226' },
  { id: 2, no: 'CASE-037', name: '雨夜诊所', category: '恐怖', players: '6人', duration: '4小时', price: 108, emoji: '💉', cover: '#6E6353' },
  { id: 3, no: 'CASE-052', name: '唐人街理发师', category: '阵营', players: '8人', duration: '5小时', price: 138, emoji: '💈', cover: '#C9A227' }
]

interface CMNews { tag: string, text: string, hot: boolean }
const CM_NEWS: CMNews[] = [
  { tag: '上新', text: '《绅士俱乐部疑案》上架', hot: true },
  { tag: '活动', text: '周五恐怖夜立减', hot: true },
  { tag: '公告', text: '隔音升级完成', hot: false }
]

interface CMCarpool { id: number, script: string, time: string, total: number, joined: number, fee: number }
const CM_CARPOOLS: CMCarpool[] = [
  { id: 1, script: '雾山庄的第七位客人', time: '今天 19:30', total: 7, joined: 5, fee: 128 },
  { id: 2, script: '雨夜诊所', time: '今天 20:00', total: 6, joined: 4, fee: 108 }
]

interface CMRecord { id: number, date: string, script: string, result: string, score: number }
const CM_RECORDS: CMRecord[] = [
  { id: 1, date: '08-25', script: '雾山庄的第七位客人', result: '胜', score: 9.2 },
  { id: 2, date: '08-24', script: '雨夜诊所', result: '胜', score: 8.8 }
]

interface CMClue { id: number, name: string, emoji: string, cost: number, stock: number }
const CM_CLUES: CMClue[] = [
  { id: 1, name: '黄铜放大镜', emoji: '🔍', cost: 180, stock: 24 },
  { id: 2, name: '烟斗香料包', emoji: '🚬', cost: 120, stock: 46 },
  { id: 3, name: '袖珍显微镜', emoji: '🔬', cost: 260, stock: 12 }
]

// ============ 弹框组件1:剧本详情 ============
@Component
struct CMScriptModal {
  script: CMScript = CM_SCRIPTS[0]
  onClose: () => void = () => {}

  build() {
    Column() {
      Row() {
        Text('📜 卷宗档案').fontSize(18).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment)
        Column().layoutWeight(1)
        Text('✕').fontSize(18).fontColor(CM_COLORS.textSub).onClick(() => { this.onClose() })
      }.width('100%').padding({ left: 20, right: 20, top: 18 })

      Row() {
        Column().width(80).height(110)
          .linearGradient({ direction: GradientDirection.RightBottom, colors: [[this.script.cover, 0], [CM_COLORS.deep, 1]] })
          .borderRadius(8)
        Text(this.script.emoji).fontSize(32).position({ x: 24, y: 36 })
        Column() {
          Text(this.script.no).fontSize(9).fontColor(CM_COLORS.gold)
          Text(this.script.name).fontSize(14).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.textMain).margin({ top: 4 })
          Text(this.script.category + ' · ' + this.script.players + ' · ' + this.script.duration)
            .fontSize(10).fontColor(CM_COLORS.textSub).margin({ top: 4 })
          Text('¥' + this.script.price + ' /人').fontSize(15).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.gold).margin({ top: 6 })
        }.alignItems(HorizontalAlign.Start).layoutWeight(1).padding({ left: 12 })
      }.width('100%').padding({ left: 20, right: 20, top: 10 }).alignItems(VerticalAlign.Center)

      Column() {
        Text('案情简介').fontSize(12).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment).width('100%')
        Text('浓雾封锁的山庄里,六位旧识收到邀请函,而你是第七位不请自来的客人...')
          .fontSize(11).fontColor(CM_COLORS.textSub).margin({ top: 6 }).lineHeight(18)
      }.width('100%').padding(14).backgroundColor(CM_COLORS.deep).borderRadius(10).margin({ top: 14 })

      Text('预约本场').fontSize(14).fontColor(CM_COLORS.ink).backgroundColor(CM_COLORS.gold)
        .borderRadius(16).padding({ left: 24, right: 24, top: 10, bottom: 10 })
        .margin({ top: 16 }).onClick(() => { this.onClose() })
    }
    .width('85%').backgroundColor(CM_COLORS.card).borderRadius(16).alignItems(HorizontalAlign.Center)
    .padding({ bottom: 20 })
  }
}

// ============ Tab1:案件大厅(首页) ============
@Component
struct CMHallContent {
  onDetail: (id: number) => void = (id: number) => {}

  @Builder card(s: CMScript) {
    Column() {
      Column().width('100%').height(90)
        .linearGradient({ direction: GradientDirection.RightBottom, colors: [[s.cover, 0], [CM_COLORS.deep, 1]] })
        .borderRadius({ topLeft: 14, topRight: 14 })
      Text(s.emoji).fontSize(30).position({ x: 52, y: 26 })
      Text(s.name).fontSize(12).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.textMain)
        .maxLines(1).width('100%').margin({ top: 6 })
      Text(s.category + ' · ' + s.players).fontSize(9).fontColor(CM_COLORS.textSub).margin({ top: 3 })
      Text('¥' + s.price + ' / 查阅').fontSize(11).fontColor(CM_COLORS.gold).margin({ top: 4 })
    }
    .width('48%').backgroundColor(CM_COLORS.card).borderRadius(14).padding(8)
    .margin({ top: 8 }).onClick(() => { this.onDetail(s.id) })
  }

  build() {
    Column() {
      Scroll() {
        Column() {
          Column().width('100%').height(120)
            .linearGradient({ direction: GradientDirection.RightBottom, colors: [[CM_COLORS.blood, 0], [CM_COLORS.deep, 1]] })
            .borderRadius(16)
          Text('🕯️ 本周悬疑榜').fontSize(16).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment)
            .padding({ left: 14, top: 14 })
          Text('雾山庄的第七位客人').fontSize(12).fontColor(CM_COLORS.gold).padding({ left: 14, top: 4 })
          Text('查看卷宗 >').fontSize(10).fontColor(CM_COLORS.ink).backgroundColor(CM_COLORS.parchment)
            .padding({ left: 10, right: 10, top: 5, bottom: 5 }).borderRadius(10)
            .position({ x: 240, y: 86 }).onClick(() => { this.onDetail(1) })
        }.width('94%').margin({ top: 10 })

        Text('📢 馆内快讯').fontSize(13).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment)
          .width('100%').padding({ left: 16, top: 12 })
        ForEach(CM_NEWS, (n: CMNews) => {
          Row() {
            Text(n.tag).fontSize(9).fontColor(n.hot ? CM_COLORS.parchment : CM_COLORS.textHint)
              .backgroundColor(n.hot ? CM_COLORS.blood : CM_COLORS.deep).padding({ left: 5, right: 5, top: 2, bottom: 2 }).borderRadius(5)
            Text(n.text).fontSize(10).fontColor(CM_COLORS.textSub).maxLines(1).margin({ left: 8 })
          }.width('94%').margin({ top: 8 })
        })

        Text('🔥 热门剧本').fontSize(13).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment)
          .width('100%').padding({ left: 16, top: 12 })

        Row() {
          this.card(CM_SCRIPTS[0])
          this.card(CM_SCRIPTS[1])
        }.width('100%').padding({ left: 12 })
      }
      .scrollable(ScrollDirection.Vertical).scrollBar(BarState.Off).layoutWeight(1)
    }.width('100%').height('100%')
  }
}

// ============ Tab2:剧本库 ============
@Component
struct CMScriptsContent {
  onDetail: (id: number) => void = (id: number) => {}

  build() {
    Column() {
      Scroll() {
        Column() {
          Text('📜 卷宗书架').fontSize(16).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment)
            .width('100%').padding({ left: 14, top: 12 })

          ForEach(CM_SCRIPTS, (s: CMScript) => {
            Row() {
              Column().width(6).height(72).backgroundColor(s.cover).borderRadius(3)
              Column() {
                Text(s.name).fontSize(13).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.textMain)
                Row() {
                  Text(s.category).fontSize(9).fontColor(CM_COLORS.gold)
                  Text(s.players + ' · ' + s.duration).fontSize(9).fontColor(CM_COLORS.textHint).margin({ left: 8 })
                }.margin({ top: 4 })
                Text('¥' + s.price + ' / 查阅').fontSize(11).fontColor(CM_COLORS.gold).margin({ top: 5 })
              }.layoutWeight(1).padding({ left: 10 })
              Text('🔍').onClick(() => { this.onDetail(s.id) })
            }.width('92%').padding(10).backgroundColor(CM_COLORS.card).borderRadius(10).margin({ top: 8 })
          })
        }.padding({ bottom: 16 })
      }
      .scrollable(ScrollDirection.Vertical).scrollBar(BarState.Off).layoutWeight(1)
    }.width('100%').height('100%')
  }
}

// ============ Tab3:拼车场 ============
@Component
struct CMCarpoolContent {
  build() {
    Column() {
      Scroll() {
        Column() {
          Text('🚕 拼车发车表').fontSize(16).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment)
            .width('100%').padding({ left: 14, top: 12 })

          ForEach(CM_CARPOOLS, (c: CMCarpool) => {
            Column() {
              Row() {
                Text(c.script).fontSize(12).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.textMain)
                Column().layoutWeight(1)
                Text(c.time).fontSize(10).fontColor(CM_COLORS.gold)
              }.width('100%')
              Row() {
                Text(c.joined + '/' + c.total + ' 已上车').fontSize(10).fontColor(CM_COLORS.textHint)
                Text('缺 ' + (c.total - c.joined) + ' 人').fontSize(10).fontColor(CM_COLORS.blood)
                  .margin({ left: 8 })
                Column().layoutWeight(1)
                Text('¥' + c.fee).fontSize(11).fontColor(CM_COLORS.gold).margin({ right: 8 })
                Text('加入').fontSize(10).fontColor(CM_COLORS.ink).backgroundColor(CM_COLORS.gold)
                  .padding({ left: 10, right: 10, top: 4, bottom: 4 }).borderRadius(8)
              }.width('100%').margin({ top: 8 })
            }.width('92%').padding(10).backgroundColor(CM_COLORS.card).borderRadius(10).margin({ top: 8 })
          })
        }.padding({ bottom: 16 })
      }
      .scrollable(ScrollDirection.Vertical).scrollBar(BarState.Off).layoutWeight(1)
    }.width('100%').height('100%')
  }
}

// ============ Tab4:战绩厅 ============
@Component
struct CMRecordsContent {
  build() {
    Column() {
      Scroll() {
        Column() {
          Text('🏆 侦探生涯').fontSize(16).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment)
            .width('100%').padding({ left: 14, top: 12 })
          Text('胜率 68% · 场次 128').fontSize(11).fontColor(CM_COLORS.gold).margin({ left: 14, top: 4 })
          Column() {
            Row() {
              Text('日期').fontSize(9).fontColor(CM_COLORS.textHint).layoutWeight(1)
              Text('剧本').fontSize(9).fontColor(CM_COLORS.textHint).layoutWeight(2)
              Text('结果').fontSize(9).fontColor(CM_COLORS.textHint).layoutWeight(1)
              Text('评分').fontSize(9).fontColor(CM_COLORS.textHint).layoutWeight(1)
            }.width('100%').padding({ left: 8, right: 8, top: 8 }).backgroundColor(CM_COLORS.deep).borderRadius(8)

            ForEach(CM_RECORDS, (r: CMRecord) => {
              Row() {
                Text(r.date).fontSize(10).fontColor(CM_COLORS.textSub).layoutWeight(1)
                Text(r.script).fontSize(10).fontColor(CM_COLORS.textMain).maxLines(1).layoutWeight(2)
                Text(r.result).fontSize(10).fontColor(r.result === '胜' ? CM_COLORS.gold : CM_COLORS.textHint).layoutWeight(1)
                Text('★' + r.score).fontSize(10).fontColor(CM_COLORS.gold).layoutWeight(1)
              }.width('100%').padding({ left: 8, right: 8, top: 9, bottom: 9 }).backgroundColor(r.id % 2 === 0 ? CM_COLORS.card : CM_COLORS.deep)
            })
          }.width('92%').margin({ top: 12 }).padding({ bottom: 10 })
        }.padding({ bottom: 16 })
      }
      .scrollable(ScrollDirection.Vertical).scrollBar(BarState.Off).layoutWeight(1)
    }.width('100%').height('100%')
  }
}

// ============ Tab5:线索铺 ============
@Component
struct CMClueContent {
  build() {
    Column() {
      Scroll() {
        Column() {
          Text('🧩 线索铺').fontSize(16).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment)
            .width('100%').padding({ left: 14, top: 12 })
          Row() {
            Text('2,680').fontSize(28).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.gold)
            Text('积分').fontSize(12).fontColor(CM_COLORS.textSub).margin({ left: 4 })
            Text('累计兑换 34 件').fontSize(10).fontColor(CM_COLORS.textHint).margin({ left: 20 })
          }.width('92%').padding(10).backgroundColor(CM_COLORS.deep).borderRadius(10).margin({ top: 10 })

          ForEach(CM_CLUES, (c: CMClue) => {
            Row() {
              Text(c.emoji).fontSize(20).width(30)
              Column() {
                Text(c.name).fontSize(12).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.textMain)
                Text(c.cost + ' 积分 · 库存 ' + c.stock).fontSize(9).fontColor(CM_COLORS.textHint).margin({ top: 3 })
              }.layoutWeight(1).margin({ left: 8 })
              Text('兑换').fontSize(10).fontColor(CM_COLORS.ink).backgroundColor(CM_COLORS.gold)
                .padding({ left: 10, right: 10, top: 5, bottom: 5 }).borderRadius(8)
            }.width('92%').padding(10).backgroundColor(CM_COLORS.card).borderRadius(10).margin({ top: 8 })
          })
        }.padding({ bottom: 16 })
      }
      .scrollable(ScrollDirection.Vertical).scrollBar(BarState.Off).layoutWeight(1)
    }.width('100%').height('100%')
  }
}

// ============ Tab6:侦探档 ============
@Component
struct CMProfileContent {
  build() {
    Column() {
      Scroll() {
        Column() {
          Row() {
            Column() {
              Text('🕵️').fontSize(40)
            }.width(64).height(64).borderRadius(32).backgroundColor(CM_COLORS.parchment)
              .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
            Column() {
              Text('雾都小侦探').fontSize(16).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.textMain)
              Text('Lv.36 · 徽章 6/12 · 积分 2,680').fontSize(10).fontColor(CM_COLORS.textSub).margin({ top: 4 })
            }.layoutWeight(1).padding({ left: 12 })
            Text('编辑').fontSize(10).fontColor(CM_COLORS.parchment).border({ width: 1, color: CM_COLORS.gold })
              .padding({ left: 10, right: 10, top: 5, bottom: 5 }).borderRadius(8)
          }.width('92%').padding(14).backgroundColor(CM_COLORS.card).borderRadius(12).margin({ top: 12 })

          Column() {
            Text('🧠 侦探属性').fontSize(13).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment).width('100%')
            Text('推理力 ★★★★').fontSize(11).fontColor(CM_COLORS.textSub).margin({ top: 8 })
            Text('演技 ★★★☆').fontSize(11).fontColor(CM_COLORS.textSub).margin({ top: 6 })
            Text('胆量 ★★☆☆').fontSize(11).fontColor(CM_COLORS.textSub).margin({ top: 6 })
          }.width('92%').padding(12).backgroundColor(CM_COLORS.card).borderRadius(12).margin({ top: 10 })

          Column() {
            Text('🏅 成就进度').fontSize(13).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment).width('100%')
            Text('🔪 完美犯罪 · 已达成').fontSize(11).fontColor(CM_COLORS.textMain).margin({ top: 8 })
            Text('🧩 线索猎人 · 9/15 进行中').fontSize(11).fontColor(CM_COLORS.textSub).margin({ top: 6 })
          }.width('92%').padding(12).backgroundColor(CM_COLORS.card).borderRadius(12).margin({ top: 10 })
        }.padding({ bottom: 16 })
      }
      .scrollable(ScrollDirection.Vertical).scrollBar(BarState.Off).layoutWeight(1)
    }.width('100%').height('100%')
  }
}

// ============ 主入口 ============
@Entry
@Component
struct CMMansionApp {
  @State activeTab: CMTab = CMTab.Hall
  @State showScriptDetail: boolean = false
  @State detailId: number = 1

  build() {
    Column() {
      // ===== 头部 =====
      Row() {
        Text('🕵️ CLUE').fontSize(16).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.parchment)
        Text('MANSION').fontSize(16).fontWeight(FontWeight.Bold).fontColor(CM_COLORS.gold).margin({ left: 4 })
        Text('谜馆').fontSize(12).fontColor(CM_COLORS.gold).margin({ left: 4 })
        Column().layoutWeight(1)
        Text('🔍').fontSize(20).margin({ right: 14 })
        Text('🔔').fontSize(20).margin({ right: 14 })
      }
      .width('100%').height(52).backgroundColor(CM_COLORS.deep).alignItems(VerticalAlign.Center)
      .padding({ left: 14 })

      // ===== 内容区 =====
      Stack() {
        if (this.activeTab === CMTab.Hall) {
          CMHallContent({ onDetail: (id: number) => { this.detailId = id; this.showScriptDetail = true } })
        } else if (this.activeTab === CMTab.Scripts) {
          CMScriptsContent({ onDetail: (id: number) => { this.detailId = id; this.showScriptDetail = true } })
        } else if (this.activeTab === CMTab.Carpool) {
          CMCarpoolContent()
        } else if (this.activeTab === CMTab.Records) {
          CMRecordsContent()
        } else if (this.activeTab === CMTab.Clue) {
          CMClueContent()
        } else {
          CMProfileContent()
        }

        // ===== 弹框:剧本详情 =====
        if (this.showScriptDetail) {
          Column() {
            Column().width('100%').height('100%')
              .backgroundColor('rgba(15,12,9,0.62)')
              .onClick(() => { this.showScriptDetail = false })
            Column() {
              CMScriptModal({
                script: CM_SCRIPTS.find(s => s.id === this.detailId) || CM_SCRIPTS[0],
                onClose: () => { this.showScriptDetail = false }
              })
            }.width('100%').justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
          }
          .width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
        }
      }
      .layoutWeight(1)

      // ===== 底部 Tab =====
      Row() {
        ForEach(CM_TABS, (item: CMTabItem) => {
          Column() {
            Text(item.icon).fontSize(18).opacity(this.activeTab === item.tab ? 1.0 : 0.45)
            Text(item.label).fontSize(9)
              .fontColor(this.activeTab === item.tab ? CM_COLORS.goldLight : CM_COLORS.textHint)
              .fontWeight(this.activeTab === item.tab ? FontWeight.Bold : FontWeight.Normal)
              .margin({ top: 2 })
            if (this.activeTab === item.tab) {
              Column().width(16).height(3).backgroundColor(CM_COLORS.blood).borderRadius(2).margin({ top: 2 })
            }
          }.layoutWeight(1).alignItems(HorizontalAlign.Center).padding({ top: 6, bottom: 6 })
          .onClick(() => { this.activeTab = item.tab })
        })
      }
      .width('100%').backgroundColor(CM_COLORS.card).padding({ top: 4, bottom: 6 })
    }
    .width('100%').height('100%').backgroundColor(CM_COLORS.bg)
  }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐