本节指标
- portainer 容器管理工具
- 数据库设计过程
- 数据库设计指标、标准、习惯
- graphql 条件查问、排序
- flutter 代码实现
视频
https://www.bilibili.com/vide...
代码
https://github.com/ducafecat/...
strapi 运行环境网盘下载
- 网盘
链接:https://pan.baidu.com/s/13Ujy...
明码:yu82
- 文件
名称 | 阐明 |
---|---|
strapi-docker-compose-00.zip | 洁净环境,已装置 graphql 插件 |
strapi-docker-compose-15.zip | 15 课内容 |
- 运行
须要用 docker-compose 启动
账号 admin
明码 123456
# 启动docker-compose up -d --remove-orphans# 敞开docker-compose down
工具
- https://www.portainer.io
- https://www.draw.io
- https://app.quicktype.io
注释
装置 portainer
- https://www.portainer.io
docker run -d -p 9000:9000 \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ --name portainer-local \ portainer/portainer
设计数据模型
- https://www.draw.io
- 规范数据库设计
ER 图 -> 设计范式 -> 数据库物理表
- ER
- 范式
https://zh.wikipedia.org/wiki...
- 设计规范 表前缀
sys* 零碎、用户、权限
dict* 字典表
bus_ 业务
- 设计数据 对象、属性、关系
创立 strapi 数据类型
- 创立外键表 新闻分类
- 创立外键表 新闻频道
- 创立业务表 新闻内容
- 创立链接外键 新闻内容、分类、频道
编写 graphql 查问
- 新闻
query News($category_code: String) { busNews(where: { dict_categories: { code: $category_code } }) { title dict_channel { code title icon { url } } dict_categories { code title } author url addtime thumbnail { url } }}
- 首页
query pageIndex { # 分类 dictCategories(sort: "sortNum:desc") { code title } # 频道 dictChannels(sort: "sortNum:desc") { code title icon { url } } # 热点 busNews(where: { dict_categories: { code: "news_hot" } }) { title dict_channel { code title icon { url } } dict_categories { code title } author url addtime thumbnail { url } }}
编写 flutter 代码
- 实例 entity
lib/common/entitys/gql_news.dart
// 首页class GqlIndexResponseEntity { GqlIndexResponseEntity({ this.dictCategories, this.dictChannels, this.busNews, }); List<DictCategoryEntity> dictCategories; List<DictChannelEntity> dictChannels; List<GqlNewsResponseEntity> busNews; factory GqlIndexResponseEntity.fromJson(Map<String, dynamic> json) => GqlIndexResponseEntity( dictCategories: List<DictCategoryEntity>.from( json["dictCategories"].map((x) => DictCategoryEntity.fromJson(x))), dictChannels: List<DictChannelEntity>.from( json["dictChannels"].map((x) => DictChannelEntity.fromJson(x))), busNews: List<GqlNewsResponseEntity>.from( json["busNews"].map((x) => GqlNewsResponseEntity.fromJson(x))), ); Map<String, dynamic> toJson() => { "dictCategories": List<dynamic>.from(dictCategories.map((x) => x.toJson())), "dictChannels": List<dynamic>.from(dictChannels.map((x) => x.toJson())), "busNews": List<dynamic>.from(busNews.map((x) => x.toJson())), };}// 新闻class GqlNewsResponseEntity { GqlNewsResponseEntity({ this.title, this.dictChannel, this.dictCategories, this.author, this.url, this.addtime, this.thumbnail, }); String title; DictChannelEntity dictChannel; List<DictCategoryEntity> dictCategories; String author; String url; DateTime addtime; ThumbnailEntity thumbnail; factory GqlNewsResponseEntity.fromJson(Map<String, dynamic> json) => GqlNewsResponseEntity( title: json["title"], dictChannel: DictChannelEntity.fromJson(json["dict_channel"]), dictCategories: List<DictCategoryEntity>.from( json["dict_categories"].map((x) => DictCategoryEntity.fromJson(x))), author: json["author"], url: json["url"], addtime: DateTime.parse(json["addtime"]), thumbnail: ThumbnailEntity.fromJson(json["thumbnail"]), ); Map<String, dynamic> toJson() => { "title": title, "dict_channel": dictChannel.toJson(), "dict_categories": List<dynamic>.from(dictCategories.map((x) => x.toJson())), "author": author, "url": url, "addtime": "${addtime.year.toString().padLeft(4, '0')}-${addtime.month.toString().padLeft(2, '0')}-${addtime.day.toString().padLeft(2, '0')}", "ThumbnailEntity": thumbnail.toJson(), };}// 分类class DictCategoryEntity { DictCategoryEntity({ this.code, this.title, }); String code; String title; factory DictCategoryEntity.fromJson(Map<String, dynamic> json) => DictCategoryEntity( code: json["code"], title: json["title"], ); Map<String, dynamic> toJson() => { "code": code, "title": title, };}// 频道class DictChannelEntity { DictChannelEntity({ this.code, this.title, this.icon, }); String code; String title; ThumbnailEntity icon; factory DictChannelEntity.fromJson(Map<String, dynamic> json) => DictChannelEntity( code: json["code"], title: json["title"], icon: ThumbnailEntity.fromJson(json["icon"]), ); Map<String, dynamic> toJson() => { "code": code, "title": title, "icon": icon.toJson(), };}// 图class ThumbnailEntity { ThumbnailEntity({ this.url, }); String url; factory ThumbnailEntity.fromJson(Map<String, dynamic> json) => ThumbnailEntity( url: json["url"], ); Map<String, dynamic> toJson() => { "url": url, };}
- api
lib/common/apis/gql_news.dart
/// 新闻class GqlNewsAPI { /// 首页 static Future<GqlIndexResponseEntity> indexPageInfo({ @required BuildContext context, Map<String, dynamic> params, }) async { QueryResult response = await GraphqlClientUtil.query(context: context, schema: GQL_INDEX_PAGE); return GqlIndexResponseEntity.fromJson(response.data); } /// 翻页 static Future<List<GqlNewsResponseEntity>> newsPageList({ @required BuildContext context, Map<String, dynamic> params, }) async { QueryResult response = await GraphqlClientUtil.query( context: context, schema: GQL_NEWS_LIST, variables: params); return response.data['busNews'] .map<GqlNewsResponseEntity>( (item) => GqlNewsResponseEntity.fromJson(item)) .toList(); }}
- 界面业务代码
lib/pages/main/main.dart
GqlIndexResponseEntity _indexPageData; // 首页数据 // 读取所有数据 _loadAllData() async { _indexPageData = await GqlNewsAPI.indexPageInfo(context: context); ... if (mounted) { setState(() {}); } } // 拉取举荐、新闻 _loadNewsData( categoryCode, { bool refresh = false, }) async { _selCategoryCode = categoryCode; _newsPageList = await GqlNewsAPI.newsPageList( context: context, params: {"category_code": categoryCode}); if (mounted) { setState(() {}); } } ...
详见 git
资源
设计稿蓝湖预览
https://lanhuapp.com/url/lYuz1
明码: gSKl
蓝湖当初免费了,所以查看标记还请本人上传 xd 设计稿
商业设计稿文件不好间接分享, 能够加微信分割 ducafecat
© 猫哥
https://ducafecat.tech