欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

一、项目概述

运行效果图

image-20260409224213850

image-20260409224219835

image-20260409224238877

image-20260409224244333

image-20260409224250431

1.1 应用简介

家庭决策投票器是一款社交工具类应用,致力于帮助家庭以民主方式解决日常决策问题。无论是周末去哪里玩、晚饭吃什么、看什么电影,都可以通过投票来决定。支持多种投票分类、实时统计结果、匿名投票等功能,让家庭决策更加公平透明,避免争吵,增进家庭和谐。

应用以优雅的紫色为主色调,象征智慧与民主。涵盖首页导航、进行中投票、投票历史、家庭成员四大模块。用户可以发起投票、参与投票、查看结果,让每一个家庭成员都有发言权。

1.2 核心功能

功能模块 功能描述 实现方式
投票分类 6种投票类型分类 枚举定义
投票创建 自定义选项创建 表单提交
实时投票 家庭成员参与投票 状态更新
结果统计 实时统计投票结果 数据计算
投票历史 历史投票记录查看 列表展示
成员统计 成员投票参与统计 数据分析

1.3 投票分类定义

序号 分类名称 Emoji 色值
1 出行游玩 🚗 #4CAF50
2 餐饮美食 🍽️ #FF9800
3 影视娱乐 🎬 #E91E63
4 购物消费 🛒 #2196F3
5 家庭活动 🏠 #9C27B0
6 其他事项 📌 #607D8B

1.4 投票状态定义

序号 状态名称 Emoji 色值
1 进行中 #4CAF50
2 已结束 #9E9E9E
3 待开始 🕐 #FFC107

1.5 家庭成员定义

序号 成员名称 Emoji 色值
1 爸爸 👨 #2196F3
2 妈妈 👩 #E91E63
3 爷爷 👴 #9C27B0
4 奶奶 👵 #FF5722
5 儿子 👦 #00BCD4
6 女儿 👧 #FF9800

1.6 技术栈

技术领域 技术选型 版本要求
开发框架 Flutter >= 3.0.0
编程语言 Dart >= 2.17.0
设计规范 Material Design 3 -
动画控制 AnimationController -
状态管理 setState -
目标平台 鸿蒙OS / Web API 21+

1.7 项目结构

lib/
└── main_family_voting.dart
    ├── FamilyVotingApp            # 应用入口
    ├── VoteCategory               # 投票分类枚举
    ├── VoteStatus                 # 投票状态枚举
    ├── FamilyMember               # 家庭成员枚举
    ├── VoteOption                 # 投票选项模型
    ├── Vote                       # 投票模型
    ├── FamilyVotingHomePage       # 主页面(底部导航)
    ├── _buildHomePage             # 首页模块
    ├── _buildVotesPage            # 投票页模块
    ├── _buildHistoryPage          # 历史页模块
    ├── _buildMembersPage          # 成员页模块
    ├── VoteDetailPage             # 投票详情页面
    └── CreateVoteSheet            # 创建投票弹窗

二、系统架构

2.1 整体架构图

Data Layer

Business Layer

Presentation Layer

主页面
FamilyVotingHomePage

首页

投票页

历史页

成员页

快速操作

进行中投票

投票统计

投票列表

投票详情

参与投票

历史记录

结果回顾

成员列表

参与统计

投票管理器
VoteManager

结果计算器
ResultCalculator

成员管理器
MemberManager

Vote
投票

VoteOption
选项

FamilyMember
成员

2.2 类图设计

has

has

contains

voted by

FamilyVotingApp

+Widget build()

«enumeration»

VoteCategory

+String label

+String emoji

+Color color

+travel()

+food()

+movie()

+shopping()

+activity()

+other()

«enumeration»

VoteStatus

+String label

+String emoji

+Color color

+ongoing()

+ended()

+pending()

«enumeration»

FamilyMember

+String label

+String emoji

+Color color

+dad()

+mom()

+grandpa()

+grandma()

+son()

+daughter()

VoteOption

+String id

+String text

+List<FamilyMember> voters

+copyWith()

Vote

+String id

+String title

+String description

+VoteCategory category

+VoteStatus status

+List<VoteOption> options

+DateTime createdAt

+DateTime endTime

+bool isAnonymous

+bool allowMultiple

+int totalVotes

+copyWith()

2.3 页面导航流程

首页

投票

历史

成员

应用启动

首页

底部导航

快速操作

进行中列表

历史记录

成员管理

发起投票

创建投票表单

提交创建

点击投票

投票详情

选择选项

提交投票

查看历史

结果回顾

2.4 投票流程时序

详情页 投票页 创建页 首页 用户 详情页 投票页 创建页 首页 用户 点击发起投票 打开创建表单 填写投票信息 添加选项 提交创建 返回首页 点击投票卡片 显示投票详情 选择选项 更新投票状态 返回并刷新

三、核心模块设计

3.1 数据模型设计

3.1.1 投票分类枚举 (VoteCategory)
enum VoteCategory {
  travel('出行游玩', '🚗', Color(0xFF4CAF50)),
  food('餐饮美食', '🍽️', Color(0xFFFF9800)),
  movie('影视娱乐', '🎬', Color(0xFFE91E63)),
  shopping('购物消费', '🛒', Color(0xFF2196F3)),
  activity('家庭活动', '🏠', Color(0xFF9C27B0)),
  other('其他事项', '📌', Color(0xFF607D8B));

  final String label;
  final String emoji;
  final Color color;

  const VoteCategory(this.label, this.emoji, this.color);
}
3.1.2 投票状态枚举 (VoteStatus)
enum VoteStatus {
  ongoing('进行中', '⏳', Color(0xFF4CAF50)),
  ended('已结束', '✅', Color(0xFF9E9E9E)),
  pending('待开始', '🕐', Color(0xFFFFC107));

  final String label;
  final String emoji;
  final Color color;

  const VoteStatus(this.label, this.emoji, this.color);
}
3.1.3 投票选项模型 (VoteOption)
class VoteOption {
  final String id;              // 选项ID
  final String text;            // 选项文本
  final List<FamilyMember> voters; // 投票人列表

  VoteOption({
    required this.id,
    required this.text,
    this.voters = const [],
  });

  VoteOption copyWith({List<FamilyMember>? voters}) {
    return VoteOption(
      id: id,
      text: text,
      voters: voters ?? this.voters,
    );
  }
}
3.1.4 投票模型 (Vote)
class Vote {
  final String id;              // 投票ID
  final String title;           // 投票标题
  final String description;     // 投票描述
  final VoteCategory category;  // 投票分类
  final VoteStatus status;      // 投票状态
  final List<VoteOption> options; // 投票选项
  final DateTime createdAt;     // 创建时间
  final DateTime? endTime;      // 截止时间
  final bool isAnonymous;       // 是否匿名
  final bool allowMultiple;     // 是否允许多选

  int get totalVotes => options.fold(0, (sum, opt) => sum + opt.voters.length);
}
3.1.5 投票分类分布
30% 25% 15% 12% 10% 8% 投票分类分布示例 出行游玩 餐饮美食 影视娱乐 购物消费 家庭活动 其他事项

3.2 页面结构设计

3.2.1 主页面布局

FamilyVotingHomePage

IndexedStack

首页

投票页

历史页

成员页

NavigationBar

首页 Tab

投票 Tab

历史 Tab

成员 Tab

3.2.2 首页结构

首页

渐变背景

头部信息

快速操作

进行中投票

投票统计

应用图标

应用名称

进行中数量

发起投票

参与投票

投票统计

投票卡片列表

脉冲动画指示

总投票数

进行中

已结束

参与者

3.2.3 投票详情页结构

投票详情页

SliverAppBar

信息卡片

选项卡片

投票人卡片

底部按钮

渐变背景

分类图标

投票标题

分类标签

状态标签

描述文字

投票统计

选项列表

进度条

领先标识

投票人头像

3.2.4 历史页结构

历史页

历史卡片列表

分类图标

投票标题

获胜选项

参与人数

选项分布

3.3 投票逻辑流程

进行中

已结束

点击投票卡片

进入详情页

投票状态

显示投票选项

显示最终结果

选择选项

已投票?

显示已投票状态

高亮选中选项

点击提交

更新投票数据

刷新页面

显示成功提示

3.4 结果计算逻辑

获取所有选项

计算每个选项票数

找出最大票数

标记领先选项

计算百分比

更新进度条

投票结束?

确定获胜选项

更新状态为已结束

继续实时更新


四、UI设计规范

4.1 配色方案

应用以优雅的紫色为主色调,象征智慧与民主:

颜色类型 色值 用途
主色 #673AB7 (DeepPurple) 导航、主题元素
辅助色 #7E57C2 次要元素
强调色 #B39DDB 背景渐变
背景色 #EDE7F6 → #B39DDB 首页渐变背景
卡片背景 #FFFFFF 内容卡片

4.2 分类专属配色

分类 色值 视觉效果
出行游玩 #4CAF50 自然绿色
餐饮美食 #FF9800 温暖橙色
影视娱乐 #E91E63 活力粉色
购物消费 #2196F3 商务蓝色
家庭活动 #9C27B0 优雅紫色
其他事项 #607D8B 中性灰色

4.3 字体规范

元素 字号 字重 颜色
页面标题 22px Bold #000000
投票标题 16px Bold #000000
选项文字 16px Medium 分类色
统计数字 24px Bold 主题色
描述文字 12px Regular #757575

4.4 组件规范

4.4.1 投票卡片
┌─────────────────────────────────────┐
│  [🚗 出行游玩]              [⏳ 进行中]│
│                                     │
│  周末去哪里玩?                      │
│  这周末全家一起出去玩,大家投票决定  │
│                                     │
│  🗳️ 4票  ⏰ 剩余1天                 │
│                                     │
│  ┌─────────────────────────────┐   │
│  │ 🏆 领先:公园野餐      2票   │   │
│  └─────────────────────────────┘   │
└─────────────────────────────────────┘
4.4.2 投票选项卡片
┌─────────────────────────────────────┐
│  公园野餐              [🏆 领先] 2票 │
│  ████████████░░░░░░░░  50.0%       │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│  游乐场                         1票 │
│  ██████░░░░░░░░░░░░░░  25.0%       │
└─────────────────────────────────────┘
4.4.3 历史卡片
┌─────────────────────────────────────┐
│  🎬 看什么电影?     [🏆 喜剧片]    │
│  5人参与 · 1/15                     │
│                                     │
│  [动作片 40%] [喜剧片 60%] [动画片 0%]│
└─────────────────────────────────────┘
4.4.4 成员卡片
┌─────────────────────────────────────┐
│  ┌────┐  爸爸                [3票] │
│  │ 👨 │  参与投票 3 次              │
│  └────┘                             │
└─────────────────────────────────────┘
4.4.5 创建投票表单
┌─────────────────────────────────────┐
│  发起投票                      [×]  │
│                                     │
│  ┌─────────────────────────────┐   │
│  │ 投票标题                    │   │
│  └─────────────────────────────┘   │
│                                     │
│  ┌─────────────────────────────┐   │
│  │ 投票描述                    │   │
│  └─────────────────────────────┘   │
│                                     │
│  投票分类                           │
│  [🚗 出行] [🍽️ 美食] [🎬 影视]...   │
│                                     │
│  投票选项                    [+添加]│
│  ┌─────────────────────────────┐   │
│  │ 选项 1                    × │   │
│  └─────────────────────────────┘   │
│  ┌─────────────────────────────┐   │
│  │ 选项 2                    × │   │
│  └─────────────────────────────┘   │
│                                     │
│  [○] 允许多选                       │
│  [○] 匿名投票                       │
│                                     │
│  ┌─────────────────────────────┐   │
│  │        创建投票             │   │
│  └─────────────────────────────┘   │
└─────────────────────────────────────┘

五、核心功能实现

5.1 投票卡片实现

Widget _buildVoteCard(Vote vote) {
  final maxVotes = vote.options.map((o) => o.voters.length).reduce((a, b) => a > b ? a : b);
  final winningOption = vote.options.firstWhere(
    (o) => o.voters.length == maxVotes,
    orElse: () => vote.options.first,
  );

  return GestureDetector(
    onTap: () => _viewVote(vote),
    child: Container(
      margin: const EdgeInsets.only(bottom: 12),
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 8)],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                decoration: BoxDecoration(
                  color: vote.category.color.withValues(alpha: 0.2),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text(vote.category.emoji, style: const TextStyle(fontSize: 12)),
                    const SizedBox(width: 4),
                    Text(vote.category.label, style: TextStyle(fontSize: 10, color: vote.category.color)),
                  ],
                ),
              ),
              const Spacer(),
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                decoration: BoxDecoration(
                  color: vote.status.color.withValues(alpha: 0.2),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Text('${vote.status.emoji} ${vote.status.label}', style: TextStyle(fontSize: 10, color: vote.status.color)),
              ),
            ],
          ),
          const SizedBox(height: 12),
          Text(vote.title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
        ],
      ),
    ),
  );
}

5.2 进行中投票指示器

Widget _buildOngoingVotes(List<Vote> ongoingVotes) {
  return Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Row(
        children: [
          AnimatedBuilder(
            animation: _pulseController,
            builder: (context, child) {
              return Container(
                padding: const EdgeInsets.all(6),
                decoration: BoxDecoration(
                  color: Colors.green.withValues(alpha: 0.2 + _pulseController.value * 0.1),
                  shape: BoxShape.circle,
                ),
                child: const Icon(Icons.circle, color: Colors.green, size: 8),
              );
            },
          ),
          const SizedBox(width: 8),
          const Text('进行中的投票', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
        ],
      ),
      const SizedBox(height: 12),
      ...ongoingVotes.map((vote) => _buildVoteCard(vote)),
    ],
  );
}

5.3 投票选项实现

Widget _buildOptionsCard(int maxVotes) {
  return Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      const Text('投票选项', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
      const SizedBox(height: 12),
      ..._currentVote.options.map((option) {
        final percentage = _currentVote.totalVotes > 0
            ? option.voters.length / _currentVote.totalVotes
            : 0.0;
        final isLeading = option.voters.length == maxVotes && option.voters.isNotEmpty;
        final isSelected = _selectedOption == option.id || widget.myVotes.contains(option.id);

        return GestureDetector(
          onTap: _currentVote.status == VoteStatus.ongoing && !widget.myVotes.contains(option.id)
              ? () { setState(() { _selectedOption = option.id; }); }
              : null,
          child: Container(
            margin: const EdgeInsets.only(bottom: 12),
            padding: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: isSelected ? _currentVote.category.color.withValues(alpha: 0.1) : Colors.grey[100],
              borderRadius: BorderRadius.circular(12),
              border: Border.all(
                color: isSelected ? _currentVote.category.color : isLeading ? Colors.green : Colors.transparent,
                width: 2,
              ),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  children: [
                    Expanded(child: Text(option.text, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500))),
                    if (isLeading) Container(
                      padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
                      decoration: BoxDecoration(color: Colors.amber.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(8)),
                      child: const Row(children: [
                        Icon(Icons.emoji_events, color: Colors.amber, size: 14),
                        SizedBox(width: 2),
                        Text('领先', style: TextStyle(fontSize: 10, color: Colors.amber)),
                      ]),
                    ),
                    const SizedBox(width: 8),
                    Text('${option.voters.length}票', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: isLeading ? Colors.green : Colors.grey[700])),
                  ],
                ),
                const SizedBox(height: 8),
                ClipRRect(
                  borderRadius: BorderRadius.circular(4),
                  child: LinearProgressIndicator(
                    value: percentage,
                    backgroundColor: Colors.grey[300],
                    valueColor: AlwaysStoppedAnimation<Color>(isLeading ? Colors.green : _currentVote.category.color),
                    minHeight: 6,
                  ),
                ),
              ],
            ),
          ),
        );
      }),
    ],
  );
}

5.4 投票提交实现

void _castVote(Vote vote, String optionId) {
  setState(() {
    final voteIndex = _votes.indexWhere((v) => v.id == vote.id);
    if (voteIndex != -1) {
      final updatedOptions = vote.options.map((option) {
        if (option.id == optionId) {
          final newVoters = List<FamilyMember>.from(option.voters);
          newVoters.add(FamilyMember.dad);
          return option.copyWith(voters: newVoters);
        }
        return option;
      }).toList();

      _votes[voteIndex] = vote.copyWith(options: updatedOptions);
      _myVotes.add(optionId);
    }
  });

  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('投票成功!'), backgroundColor: Color(0xFF673AB7)),
  );
}

5.5 历史记录实现

Widget _buildHistoryCard(Vote vote) {
  final maxVotes = vote.options.map((o) => o.voters.length).reduce((a, b) => a > b ? a : b);
  final winningOption = vote.options.firstWhere(
    (o) => o.voters.length == maxVotes,
    orElse: () => vote.options.first,
  );

  return Container(
    margin: const EdgeInsets.only(bottom: 12),
    padding: const EdgeInsets.all(16),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(16),
      boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 8)],
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Text(vote.category.emoji, style: const TextStyle(fontSize: 20)),
            const SizedBox(width: 8),
            Expanded(child: Text(vote.title, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold))),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
              decoration: BoxDecoration(color: Colors.green.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(8)),
              child: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  const Icon(Icons.emoji_events, color: Colors.amber, size: 14),
                  const SizedBox(width: 4),
                  Text(winningOption.text, style: const TextStyle(fontSize: 10, color: Colors.green)),
                ],
              ),
            ),
          ],
        ),
        const SizedBox(height: 8),
        Text('${vote.totalVotes}人参与 · ${_formatDate(vote.createdAt)}', style: TextStyle(fontSize: 10, color: Colors.grey[600])),
        const SizedBox(height: 12),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: vote.options.map((option) {
            final percentage = vote.totalVotes > 0 ? (option.voters.length / vote.totalVotes * 100).toInt() : 0;
            final isWinner = option.voters.length == maxVotes && option.voters.isNotEmpty;
            return Container(
              padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
              decoration: BoxDecoration(
                color: isWinner ? Colors.green.withValues(alpha: 0.1) : Colors.grey[100],
                borderRadius: BorderRadius.circular(8),
                border: Border.all(color: isWinner ? Colors.green : Colors.transparent),
              ),
              child: Text('${option.text} $percentage%', style: TextStyle(fontSize: 10, color: isWinner ? Colors.green : Colors.grey[700])),
            );
          }).toList(),
        ),
      ],
    ),
  );
}

六、交互设计

6.1 投票创建流程

投票列表 创建表单 首页 用户 投票列表 创建表单 首页 用户 点击发起投票 打开底部弹窗 输入标题和描述 选择分类 添加选项 设置投票规则 点击创建 添加新投票 返回首页 显示成功提示

6.2 投票参与流程

进行中

已结束

查看投票列表

点击投票卡片

进入详情页

投票状态

显示投票选项

显示最终结果

选择选项

高亮选中

点击提交

更新投票数据

刷新页面

显示成功提示

6.3 结果统计流程

每次投票

继续投票

截止时间到

投票进行中

实时更新

计算百分比

更新进度条

标记领先项

投票结束

确定获胜项

更新历史记录


七、扩展功能规划

7.1 后续版本规划

2024-01-07 2024-01-14 2024-01-21 2024-01-28 2024-02-04 2024-02-11 2024-02-18 2024-02-25 2024-03-03 2024-03-10 2024-03-17 2024-03-24 2024-03-31 基础UI框架 投票创建功能 投票参与功能 评论讨论功能 投票提醒通知 数据统计分析 多家庭支持 权重投票 投票模板 V1.0 基础版本 V1.1 增强版本 V1.2 进阶版本 家庭决策投票器开发计划

7.2 功能扩展建议

7.2.1 评论讨论功能

评论功能:

  • 投票下发表意见
  • 回复他人评论
  • 点赞功能
  • 表情支持
7.2.2 投票提醒通知

通知功能:

  • 新投票提醒
  • 投票截止提醒
  • 结果公布通知
  • 未投票提醒
7.2.3 权重投票

权重功能:

  • 成员权重设置
  • 按年龄分配权重
  • 按角色分配权重
  • 权重结果计算

八、注意事项

8.1 开发注意事项

  1. 状态同步:投票结果需实时更新,保证数据一致性

  2. 性能优化:大量投票需分页加载,避免卡顿

  3. 动画控制:AnimationController需正确释放

  4. 用户体验:投票反馈需及时清晰

  5. 数据持久化:投票数据需本地存储

8.2 常见问题

问题 原因 解决方案
投票不更新 状态未同步 检查setState
选项无法选择 已投票限制 检查投票状态
进度条异常 数值计算错误 检查边界条件
历史记录缺失 数据未保存 检查数据持久化
成员统计错误 计算逻辑问题 检查遍历逻辑

8.3 使用技巧

🗳️ 家庭投票技巧 🗳️

发起投票

  • 标题简洁明了,让家人一眼看懂
  • 描述详细说明背景和目的
  • 选项设置全面,避免遗漏

参与投票

  • 认真考虑每个选项
  • 可以先讨论再投票
  • 尊重投票结果

投票管理

  • 设置合理的截止时间
  • 及时查看投票进度
  • 结果公布后执行决策

九、运行说明

9.1 环境要求

环境 版本要求
Flutter SDK >= 3.0.0
Dart SDK >= 2.17.0
鸿蒙OS API 21+
Web浏览器 Chrome 90+

9.2 运行命令

# 查看可用设备
flutter devices

# 运行到Web服务器
flutter run -d web-server -t lib/main_family_voting.dart --web-port 8143

# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_family_voting.dart

# 代码分析
flutter analyze lib/main_family_voting.dart

十、总结

家庭决策投票器通过首页导航、进行中投票、投票历史、家庭成员四大模块,为家庭提供了一个民主决策的平台。应用支持6种投票分类、6位家庭成员、多种投票规则,让家庭决策更加公平透明。

核心功能涵盖投票创建、投票参与、结果统计、历史记录、成员统计五大模块。投票创建支持自定义标题、描述、分类、选项;投票参与提供直观的选项选择和进度展示;结果统计实时计算百分比和领先项;历史记录保存所有已结束投票;成员统计展示每位成员的投票参与情况。

应用采用 Material Design 3 设计规范,以优雅的紫色为主色调,象征智慧与民主。通过本应用,希望能够帮助家庭以民主方式解决日常决策问题,避免争吵,增进家庭和谐,让每一个家庭成员都有发言权。

家庭决策投票器——民主决策,家庭和谐


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐