【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈

简介: 【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈

【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈

章节内容【06】

flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且打包demo做演示

开发背景

上篇我们做了自定义组件,本文继续完善注册相关页面并且实现跳转

闲话不多,开源仓库地址,可以观摩已经写好的代码:

https://gitee.com/youyacao/ff-flutter

demo下载

实战开始


上一篇我们在完成后发现个报错问题,

Target of URI doesn't exist: 'package:ff_flutter/lib/widgets/pinkbutton.dart'.
Try creating the file referenced by the URI, or try using a URI for a file that does exist.

这是提示目录中没有找到pinkbutton.dart文件,相关报错还有很多,但是优雅草卓伊凡的路径是正确的,其实就是package:ff_flutter识别不了了。

这是因为包依赖问题:

如果 ff_flutter 是一个自定义包,确保它已经在 pubspec.yaml 中正确声明。
运行 flutter pub get 更新依赖。

dependencies:
  ff_flutter:
    path: ../ff_flutter  # 根据实际情况调整路径

清理和重建项目:

运行 flutter clean 清理构建缓存。
运行 flutter pub get 获取最新依赖。
重新启动 IDE 或编辑器以确保所有更改生效。

可是当我们运行调试的时候还是报错了,这时候我们再来看

发现问题了,name应该是 name: ff_flutter 才对。

一切就绪后还是报错,依赖也是也正确的,终于我测试,把lib目录去掉,就成功了

想了一下,那么为啥呢,原来我定义目录已经是path: ../ff_flutter 那么package:ff_flutter 已经就到了lib 目录下了,我再去加lib目录那当然要出错啦,问题解决,我们进行下一步

有了注册页面我们做登录页面

新建login.dart

这个应该是登录页面了,但是login画错了,而且下面有切换用户密码登录,那么这里就是短信登录,因此我反馈给ui了 让去整改下,其次login文件名改为smslogin这样会方便知道。

观察了下样式基本一致因此我们复制注册页面过来,这里有很多地方细节都需要修改,特别是社交登录下方那个是图片才可以实现,我们先增加一下login按钮点击 后跳转到 smslogin.dart

要给 login 按钮增加点击跳转到 smslogin.dart 页面的功能,你需要在 onPressed 回调中使用 Navigator 进行页面跳转,我们需要以下步骤

在注册页面修改登录按钮的代码:

BlackButton(
  label: 'login',
  onPressed: () {
    // 登录按钮点击事件
    logger.info('登录按钮被点击');
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => SmsLoginScreen()),
    );
  },
),

并且增加引入:

import 'package:ff_flutter/screens/smslogin.dart'; // 假设 smslogin.dart 文件位于 screens 目录下

在smslogin.dart页面增加

// smslogin.dart
class SmsLoginScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SMS Login'),
      ),
      body: Center(
        child: const Text('SMS Login Screen'),
      ),
    );
  }
}

完成后,但是我们得到一个警告

Constructors for public widgets should have a named 'key' parameter.
Try adding a named parameter to the constructor.

大意是

Flutter 建议为公共小部件的构造函数添加一个命名的 key 参数。为了符合这个建议,你需要在 SmsLoginScreen 和 RegisterScreen 的构造函数中添加 Key 参数。

那么扩展知识又来了

扩展知识

在Flutter中,为小部件的构造函数添加一个命名的 key 参数有以下几个主要作用:

1. 唯一标识小部件

每个小部件都可以通过 key 参数在树结构中唯一标识。这在重建部分树时特别有用,因为它有助于Flutter引擎高效地更新和重用小部件,而不是销毁和重建它们。

2. 保持状态

当你需要保持某个状态时(例如在列表中拖动排序项目),key 参数可以确保小部件在重建时保持其状态。例如,在一个可变顺序的列表中,如果每个项目都有唯一的 key,那么在列表项被重新排列时,它们的状态仍能正确保持。

3. 控制小部件重建

key 参数可以帮助Flutter引擎决定是否需要重建小部件。通过比较 key 值,Flutter可以在更新UI时更智能地选择重建哪些部分,从而提高性能。

代码示例

以下是如何为小部件添加一个命名的 key 参数的示例

import 'package:flutter/material.dart';
class CustomWidget extends StatelessWidget {
  final String title;
  const CustomWidget({Key? key, required this.title}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(title),
    );
  }
}

在上面的示例中,CustomWidget 有一个可选的 key 参数,并在构造函数中使用 super(key: key) 进行初始化。当你创建这个小部件的实例时,可以传递一个 key

CustomWidget(
  key: ValueKey('unique_key'),
  title: 'My Custom Widget',
)

ok 我们照做,

Key 类型

在Flutter中,有几种不同类型的 Key,你可以根据具体需求选择使用:

ValueKey<T>: 通过值来唯一标识小部件,适用于简单数据类型(如字符串或数字)。

ObjectKey: 通过对象来唯一标识小部件,适用于复杂数据类型。

UniqueKey: 保证每次创建时都唯一,适用于需要绝对唯一性的场景(但不能用于状态保持)。

也就是说,接下来需要在 register_screen.dart 中为 RegisterScreen 添加一个 key 值 reg,在 smslogin.dart 中为 SmsLoginScreen 添加一个 key 值 slogin。

以下是修改后的代码

在顶部加入

const RegisterScreen({Key? key}) : super(key: key); // 添加 key 参数

在注册处:

key: const Key('reg'), // 添加 key 值 'reg'

在登录按钮处:

MaterialPageRoute(builder: (context) => SmsLoginScreen(key: const Key('slogin'))), // 添加 key 值 'slogin'

注册页面我们点击登录成功跳转到了登录页面,成功实现了 跳转,本来打算本篇幅 写完所有注册页面,但是看来过长 需要下一篇了,其次有个大一点的原因是注册逻辑有问题,因此我们先去做其他页面。

完整页面供参考,

注册页面

import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:ff_flutter/widgets/pinkbutton.dart'; // 引入 PinkButton
import 'package:ff_flutter/widgets/blackbutton.dart'; // 引入 BlackButton
import 'package:ff_flutter/screens/smslogin.dart'; // 假设 smslogin.dart 文件位于 screens 目录下
class RegisterScreen extends StatefulWidget {
  const RegisterScreen({Key? key}) : super(key: key); // 添加 key 参数
  @override
  State<RegisterScreen> createState() => _RegisterScreenState();
}
class _RegisterScreenState extends State<RegisterScreen> {
  // 示例国家地区号列表
  final List<String> countryCodes = ['+1', '+86', '+91', '+44', '+33'];
  // 默认选择的国家地区号
  String selectedCountryCode = '+1';
  // Checkbox 状态
  bool _agreedToTerms = false;
  @override
  Widget build(BuildContext context) {
    final logger = Logger('RegisterScreen');
    logger.info('Building RegisterScreen');
    return Scaffold(
      key: const Key('reg'), // 添加 key 值 'reg'
      backgroundColor: const Color(0xFF1E1E1E), // 设置背景色为 #1E1E1E
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              "Free Friend",
              style: TextStyle(
                color: Colors.white,
                fontSize: 61.87,
                fontFamily: "PingFang SC",
                fontWeight: FontWeight.w800,
              ),
            ),
            const SizedBox(height: 16.0),
            const Text(
              "Please register your account",
              style: TextStyle(
                color: Colors.white,
                fontSize: 32,
                fontFamily: "PingFang SC",
                fontWeight: FontWeight.w800,
              ),
            ),
            const SizedBox(height: 16.0),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,  // 使用 spaceBetween 对齐方式
              children: [
                Flexible(
                  flex: 1,  // 给 DropdownButtonFormField 分配一部分空间
                  child: DropdownButtonFormField<String>(
                    value: selectedCountryCode,
                    onChanged: (String? newValue) {
                      if (newValue != null) {
                        setState(() {
                          selectedCountryCode = newValue;
                        });
                      }
                    },
                    items: countryCodes.map<DropdownMenuItem<String>>((String value) {
                      return DropdownMenuItem<String>(
                        value: value,
                        child: Text(
                          value,
                          style: const TextStyle(color: Colors.white), // 设置文字颜色为 FFFFFF
                        ),
                      );
                    }).toList(),
                    decoration: const InputDecoration(
                      labelText: '选择国家地区号',
                      border: OutlineInputBorder(),
                    ),
                    style: const TextStyle(
                      fontSize: 16,
                      color: Colors.white, // 设置文字颜色为 FFFFFF
                    ),
                    dropdownColor: const Color(0xFF1E1E1E), // 设置弹窗背景色为 #1E1E1E
                  ),
                ),
                const SizedBox(width: 8.0),
                const Expanded(
                  flex: 2,  // 给 TextField 分配更多的空间
                  child: TextField(
                    decoration: InputDecoration(
                      labelText: '请输入手机号',
                      border: OutlineInputBorder(),
                      hintStyle: TextStyle(color: Color(0xffa9a9a9)),
                    ),
                    style: TextStyle(color: Colors.white), // 设置输入文字颜色为 FFFFFF
                    keyboardType: TextInputType.phone,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 16.0),
            const TextField(
              decoration: InputDecoration(
                labelText: '请输入密码',
                hintStyle: TextStyle(color: Color(0xffa9a9a9)),
                border: OutlineInputBorder(),
              ),
              obscureText: true,
              style: TextStyle(color: Colors.white), // 设置输入文字颜色为 FFFFFF
            ),
            Row(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Checkbox(
                  value: _agreedToTerms,
                  onChanged: (bool? value) {
                    setState(() {
                      _agreedToTerms = value ?? false;
                    });
                  },
                ),
                const SizedBox(width: 20),
                const Text(
                  "You agree to our Terms",
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 32,
                    fontFamily: "PingFang SC",
                    fontWeight: FontWeight.w500,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 24.0),
            PinkButton(
              label: 'Register',
              onPressed: () {
                // 注册按钮点击事件
                logger.info('注册按钮被点击');
              },
            ),
            const SizedBox(height: 8.0),
            Expanded(
              child: Align(
                alignment: Alignment.bottomCenter,
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    const Text(
                      "Already have an account?",
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 32,
                        fontFamily: "PingFang SC",
                        fontWeight: FontWeight.w800,
                      ),
                    ),
                    const SizedBox(height: 8.0),
                  BlackButton(
  label: 'login',
  onPressed: () {
    // 登录按钮点击事件
    logger.info('登录按钮被点击');
    Navigator.push(
      context,
       MaterialPageRoute(builder: (context) => SmsLoginScreen(key: const Key('slogin'))), // 添加 key 值 'slogin'
    );
  },
),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

登录页面

import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:ff_flutter/widgets/pinkbutton.dart'; // 引入 PinkButton
import 'package:ff_flutter/widgets/blackbutton.dart'; // 引入 BlackButton
// smslogin.dart
class SmsLoginScreen extends StatelessWidget {
  const SmsLoginScreen({Key? key}) : super(key: key); // 添加 key 参数
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SMS Login'),
      ),
      body: Center(
        child: const Text('SMS Login Screen'),
      ),
    );
  }
}
class RegisterScreen extends StatefulWidget {
  const RegisterScreen({super.key});
  @override
  State<RegisterScreen> createState() => _RegisterScreenState();
}
class _RegisterScreenState extends State<RegisterScreen> {
  // 示例国家地区号列表
  final List<String> countryCodes = ['+1', '+86', '+91', '+44', '+33'];
  // 默认选择的国家地区号
  String selectedCountryCode = '+1';
  // Checkbox 状态
  bool _agreedToTerms = false;
  @override
  Widget build(BuildContext context) {
    final logger = Logger('RegisterScreen');
    logger.info('Building RegisterScreen');
    return Scaffold(
      backgroundColor: const Color(0xFF1E1E1E), // 设置背景色为 #1E1E1E
      // appBar: AppBar(
      //   title: const Text(
      //     'Free Friend',
      //     style: TextStyle(
      //       fontSize: 24.0, // 设置字体大小
      //       fontFamily: 'PingFang SC', // 设置字体为 PingFang SC
      //     ),
      //   ),
      //   centerTitle: true, // 居中标题
      // ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              "Free Friend",
              style: TextStyle(
                color: Colors.white,
                fontSize: 61.87,
                fontFamily: "PingFang SC",
                fontWeight: FontWeight.w800,
              ),
            ),
            const SizedBox(height: 16.0),
            const Text(
              "Please login your account",
              style: TextStyle(
                color: Colors.white,
                fontSize: 32,
                fontFamily: "PingFang SC",
                fontWeight: FontWeight.w800,
              ),
            ),
            const SizedBox(height: 16.0),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,  // 使用 spaceBetween 对齐方式
              children: [
                Flexible(
                  flex: 1,  // 给 DropdownButtonFormField 分配一部分空间
                  child: DropdownButtonFormField<String>(
                    value: selectedCountryCode,
                    onChanged: (String? newValue) {
                      if (newValue != null) {
                        setState(() {
                          selectedCountryCode = newValue;
                        });
                      }
                    },
                    items: countryCodes.map<DropdownMenuItem<String>>((String value) {
                      return DropdownMenuItem<String>(
                        value: value,
                        child: Text(
                          value,
                          style: const TextStyle(color: Colors.white), // 设置文字颜色为 FFFFFF
                        ),
                      );
                    }).toList(),
                    decoration: const InputDecoration(
                      labelText: '选择国家地区号',
                      border: OutlineInputBorder(),
                    ),
                    style: const TextStyle(
                      fontSize: 16,
                      color: Colors.white, // 设置文字颜色为 FFFFFF
                    ),
                    dropdownColor: const Color(0xFF1E1E1E), // 设置弹窗背景色为 #1E1E1E
                  ),
                ),
                const SizedBox(width: 8.0),
                const Expanded(
                  flex: 2,  // 给 TextField 分配更多的空间
                  child: TextField(
                    decoration: InputDecoration(
                      labelText: '请输入手机号',
                      border: OutlineInputBorder(),
                      hintStyle: TextStyle(color: Color(0xffa9a9a9)),
                    ),
                    style: TextStyle(color: Colors.white), // 设置输入文字颜色为 FFFFFF
                    keyboardType: TextInputType.phone,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 16.0),
            const TextField(
              decoration: InputDecoration(
                labelText: '请输入密码',
                hintStyle: TextStyle(color: Color(0xffa9a9a9)),
                border: OutlineInputBorder(),
              ),
              obscureText: true,
              style: TextStyle(color: Colors.white), // 设置输入文字颜色为 FFFFFF
            ),
            Row(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Checkbox(
                  value: _agreedToTerms,
                  onChanged: (bool? value) {
                    setState(() {
                      _agreedToTerms = value ?? false;
                    });
                  },
                ),
                const SizedBox(width: 20),
                const Text(
                  "You agree to our Terms",
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 32,
                    fontFamily: "PingFang SC",
                    fontWeight: FontWeight.w500,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 24.0),
            PinkButton(
              label: 'Register',
              onPressed: () {
                // 注册按钮点击事件
                logger.info('注册按钮被点击');
              },
            ),
            const SizedBox(height: 8.0),
            Expanded(
              child: Align(
                alignment: Alignment.bottomCenter,
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    const Text(
                      "Already have an account?",
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 32,
                        fontFamily: "PingFang SC",
                        fontWeight: FontWeight.w800,
                      ),
                    ),
                    const SizedBox(height: 8.0),
                    BlackButton(
                      label: 'login',
                      onPressed: () {
                        // 登录按钮点击事件
                        logger.info('登录按钮被点击');
                      },
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

其次我改了下名字,register_screen.dart改为register.dart,其次打包了apk 供下载给大家看。

目录
相关文章
|
9月前
|
存储 Java PHP
轻量化短视频电商直播带货APP源码全解析:核心功能与设计流程​
在电商直播热潮下,开发专属直播带货APP成为抢占市场关键。本文详解原生开发轻量化APP的核心功能与全流程设计,涵盖用户登录、商品浏览、直播互动、购物车、订单及售后功能,并介绍安卓端Java、苹果端Object-C、后台PHP的技术实现,助力打造高效优质的直播电商平台。
|
前端开发 安全 开发工具
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
995 90
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
存储 算法 API
【01】整体试验思路,如何在有UID的情况下获得用户手机号信息,python开发之理论研究试验,如何通过抖音视频下方的用户的UID获得抖音用户的手机号-本系列文章仅供学习研究-禁止用于任何商业用途-仅供学习交流-优雅草卓伊凡
【01】整体试验思路,如何在有UID的情况下获得用户手机号信息,python开发之理论研究试验,如何通过抖音视频下方的用户的UID获得抖音用户的手机号-本系列文章仅供学习研究-禁止用于任何商业用途-仅供学习交流-优雅草卓伊凡
3186 82
|
安全 算法 小程序
【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
951 28
【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
|
小程序
【04】微信支付商户申请下户到配置完整流程-微信开放平台移动APP应用通过-微信商户继续申请-微信开户函-视频声明-以及对公打款验证-申请+配置完整流程-优雅草卓伊凡
【04】微信支付商户申请下户到配置完整流程-微信开放平台移动APP应用通过-微信商户继续申请-微信开户函-视频声明-以及对公打款验证-申请+配置完整流程-优雅草卓伊凡
972 1
【04】微信支付商户申请下户到配置完整流程-微信开放平台移动APP应用通过-微信商户继续申请-微信开户函-视频声明-以及对公打款验证-申请+配置完整流程-优雅草卓伊凡
|
存储 文件存储 Android开发
仿第八区APP分发下载打包封装系统源码
该系统为仿第八区APP分发下载打包封装系统源码,支持安卓、iOS及EXE程序分发,自动判断并稳定安装。智能提取应用信息,自动生成PLIST文件和图标,提供合理的点数扣除机制。支持企业签名在线提交、专属下载页面生成、云端存储(阿里云、七牛云),并优化签名流程,支持中文包及合并分发,确保高效稳定的下载体验。 [点击查看源码](https://download.csdn.net/download/huayula/90463452)
812 22
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
479 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
存储 API 数据安全/隐私保护
【02】整体试验思路,在这之前我们发现sec_uid,sec_uid是什么和uid的关系又是什么?相互如何转换?python开发之理论研究试验,如何通过抖音视频下方的用户的UID获得抖音用户的手机号-本系列文章仅供学习研究-禁止用于任何商业用途-仅供学习交流-优雅草卓伊凡
【02】整体试验思路,在这之前我们发现sec_uid,sec_uid是什么和uid的关系又是什么?相互如何转换?python开发之理论研究试验,如何通过抖音视频下方的用户的UID获得抖音用户的手机号-本系列文章仅供学习研究-禁止用于任何商业用途-仅供学习交流-优雅草卓伊凡
2587 6
|
小程序 数据安全/隐私保护 开发者
【02】微信支付商户申请下户到配置完整流程-微信开放平台申请APP应用-微信商户支付绑定appid-公众号和小程序分别申请appid-申请+配置完整流程-优雅草卓伊凡
【02】微信支付商户申请下户到配置完整流程-微信开放平台申请APP应用-微信商户支付绑定appid-公众号和小程序分别申请appid-申请+配置完整流程-优雅草卓伊凡
1209 3

热门文章

最新文章