goCli 完成90%

This commit is contained in:
sanfan.hx
2019-06-10 10:59:23 +08:00
parent a1e4ea4636
commit aabb30997e
20 changed files with 475 additions and 224 deletions

View File

@ -1,6 +1,6 @@
import 'package:args/args.dart'; // 使用其中两个类ArgParser和ArgResults
import 'package:args/command_runner.dart';
import '../src/cli-command-runder.dart';
import '../src/cli_command_runder.dart';
ArgResults argResults; // 声明ArgResults类型的顶级变量保存解析的参数结果
// 同时argResults也是ArgResults的实例

View File

@ -16,6 +16,7 @@ environment:
dependencies:
args: '^1.5.1'
dart_inquirer: '^1.0.0'
watcher: ^0.9.7+10
dev_dependencies:

View File

@ -1,11 +1,14 @@
import 'dart:io';
import 'dart:convert';
import '../../utils/util.dart';
import '../config.dart';
import '../exception/demo.dart';
Future<List> buildDemoListJson() async {
List<FileSystemEntity> childList = await readeDirChildren(TARGET_FILE_DIC, false);
List<FileSystemEntity> childList = await readeDirChildren(TARGET_DEMO_DIC, false);
List<String> demoPathList = [];
List<Map<String, dynamic>> detailList = []; // 存放所有demo的详情的列表
for(FileSystemEntity entity in childList) {
//文件、目录和链接都继承自FileSystemEntity
//FileSystemEntity.type静态函数返回值为FileSystemEntityType
@ -14,20 +17,67 @@ Future<List> buildDemoListJson() async {
//FileSystemEntity.isFile .isLink .isDerectory可用于判断类型
Uri url = entity.uri;
if (await FileSystemEntity.isDirectory(entity.path)) {
checkDemo(entity.path);
try {
await checkDemo(entity.path);
demoPathList.add(entity.path);
} on InvalidDemo catch (e) {
print("不存在 $e");
}
}
}
for (String dicPath in demoPathList) {
String target = '$dicPath/.demo.json';
String jsonString = await readeFile(target);
try {
Map<String, dynamic> item = json.decode(jsonString);
detailList.add(item);
} catch (err) {
print("err $err");
throw new Exception('$dicPath');
}
}
print("本次编译: 获取${detailList.length}条demo数据");
String demoTplString = renderDemosDart(detailList);
// 生成 page_demo_package/index.dart
writeContent2Path(TARGET_DEMO_DIC, 'index.dart', demoTplString.replaceAll(new RegExp('-'), '_'));
// 生成 page_demo_package/.demo.dart
writeContent2Path(TARGET_DEMO_DIC, '.demo.json', json.encode(detailList));
return Future(() => childList);
// return ;
}
String renderDemosDart(List<Map<String, dynamic>> data) {
// 自定义前缀 避免出现数字非法字符等
String pre = "StandardDemo";
String head = '';
String foot = 'var demoObjects = { \r\n';
for (int i = 0; i < data.length; i++) {
Map<String, dynamic> item = data[i];
String demoImportName = '${item['name']}_${item['id']}';
head += "import '${item['name']}_${item['author']}_${item['id']}/index.dart' as StandardDemo_$demoImportName;\r\n";
foot += " '${item['id']}': ${pre}_${demoImportName}.demoWidgets";
if (i != data.length - 1) {
foot += ',\r\n';
}
}
foot += "\r\n};";
return head + foot;
}
Future<bool> checkDemo(String path) async {
List files = [
'index.dart',
'.demo.json',
'src/'
];
bool success = true;
for (String name in files) {
bool isDic = name.indexOf('/') != -1;
bool isExist ;
@ -41,9 +91,5 @@ Future<bool> checkDemo(String path) async {
throw new InvalidDemo('$path$name not exit');
}
}
return Future(() => false);
return Future(() => true);
}
main() {
buildDemoListJson();
// checkDemo('lib/page_demo_package/button_sanfan_72af2949_22ae_4241_9c8a_5c9e1f92b096/');
}

View File

@ -0,0 +1,165 @@
import 'dart:io';
import 'dart:convert';
import 'package:path/path.dart' as p;
import '../../utils/util.dart';
import '../config.dart';
import '../exception/demo.dart';
Future<List> buildPageListJson() async {
List<FileSystemEntity> childList = await readeDirChildren(TARGET_PAGE_DIC, false);
List<String> pagePathList = [];
List<Map<String, dynamic>> detailList = []; // 存放所有demo的详情的列表
int errCount = 0;
int pageCount = 0;
for(FileSystemEntity entity in childList) {
//文件、目录和链接都继承自FileSystemEntity
//FileSystemEntity.type静态函数返回值为FileSystemEntityType
//FileSystemEntityType有三个常量
//Directory、FILE、LINK、NOT_FOUND
//FileSystemEntity.isFile .isLink .isDerectory可用于判断类型
if (await FileSystemEntity.isDirectory(entity.path)) {
pageCount++;
try {
await checkPage(entity.path);
pagePathList.add(entity.path);
} on InvalidDemo catch (e) {
errCount++;
print("不存在 $e");
}
}
}
for (String dicPath in pagePathList) {
String target = '$dicPath/.page.json';
String jsonString = await readeFile(target);
try {
Map<String, dynamic> item = json.decode(jsonString);
detailList.add(item);
} catch (err) {
print("err $err");
throw new Exception('$dicPath');
}
}
print("本次编译: 总${pageCount}个界面, 成功${detailList.length}条, 失败${errCount}");
String demoTplString = renderPagesDart(detailList);
//
// // 生成 page_demo_package/index.dart
writeContent2Path(TARGET_PAGE_DIC, 'index.dart', demoTplString.replaceAll(new RegExp('-'), '_'));
// // 生成 page_demo_package/.demo.dart
writeContent2Path(TARGET_PAGE_DIC, '.pages.json', json.encode(detailList));
return Future(() => childList);
// return ;
}
String renderPagesDart(List<Map<String, dynamic>> data) {
// 自定义前缀 避免出现数字非法字符等
String pre = "StandardPage";
String head = '';
String foot = """
class StandardPages {
Map<String, String> standardPages;
Map<String, String> getPages() {
return {
""";
for (int i = 0; i < data.length; i++) {
Map<String, dynamic> item = data[i];
String demoImportName = '${item['name']}_${item['id']}';
head += "import '${item['name']}_${item['author']}_${item['id']}/index.dart' as ${pre}_$demoImportName;\r\n";
foot += "\t\t\t'${item['id']}': ${pre}_${demoImportName}.getMd()";
if (i != data.length - 1) {
foot += ',\r\n';
}
}
foot += """\r
};
}
}
""";
return head + foot;
}
Future<bool> checkPage(String path) async {
List files = [
'index.dart',
'index.md',
'.page.json'
];
for (String name in files) {
bool isDic = name.indexOf('/') != -1;
bool isExist ;
String detailPath = '$path/$name';
if (isDic) {
isExist = await Directory(detailPath).exists();
} else {
isExist = await File(detailPath).exists();
}
if (!isExist) {
throw new InvalidDemo('$path$name not exit');
}
}
return Future(() => true);
}
Future<void> transformMd2Dart(String path) async{
print("pathLL: $path");
String mdContent = await readeFile(p.absolute(path,'index.md'));
String result = """
String getMd() {
return \"\"\"
${mdContent}\"\"\";
}
""";
writeContent2Path(p.absolute(path), 'index.dart', result);
return '';
}
//main() {
// buildPageListJson();
//}
////
//// Created with Android Studio.
//// User: 三帆
//// Date: 25/05/2019
//// Time: 21:23
//// email: sanfan.hx@alibaba-inc.com
//// target: 该文件用在打包后的代码中.日常开发的时候, 获取markdown不走该目录
////
//import 'package:flutter/material.dart';
//import 'page1_hanxu_172ba42f_0520_401e_b568_ba7f7f6835e4/index.dart' as page1_hanxu_172ba42f_0520_401e_b568_ba7f7f6835e4;
//
//
//class StandardPages {
// Map<String, String> standardPages;
// Map<String, String> getPages() {
// return {
// 'page1_hanxu_172ba42f_0520_401e_b568_ba7f7f6835e4': page1_hanxu_172ba42f_0520_401e_b568_ba7f7f6835e4.stringMd
// };
// }
//}
//var stringMd = """# page
//
//this is page markdown
//
//you can load demo like this
//
//```
//[demo: xxxid]
//```
//
//""";
//

View File

@ -3,7 +3,9 @@ import 'dart:async';
import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import './version.dart';
import './command/create-demo.dart';
import './command/create_demo.dart';
import './command/create_page.dart';
import './command/watch_md.dart';
@ -14,6 +16,8 @@ class _CommandRunner extends CommandRunner<int> {
argParser.addFlag('version',
negatable: false, help: 'Prints the version of goCi.');
addCommand(CreateDemoCommand());
addCommand(CreatePageCommand());
addCommand(WatchCommand());
}

View File

@ -1,8 +1,11 @@
import 'dart:io';
import 'package:args/args.dart'; // 使ArgParser和ArgResults
import 'package:dart_inquirer/dart_inquirer.dart';
import 'package:console/console.dart';
import 'package:path/path.dart' as p;
import 'package:args/command_runner.dart';
import '../build/build_demo_list.dart';
import '../config.dart';
import '../../utils/util.dart';
@ -17,11 +20,13 @@ class DemoDetail {
String desc;
String id;
DemoDetail.fromJson(Map<dynamic, dynamic> json) {
name = json['name'];
author = json['author'];
email = json['email'];
desc = json['desc'];
id = json['id'] ?? generateDemoId();
name = json['name'].trim();
author = json['author'].trim();
email = json['email'].trim();
desc = json['desc'].trim();
id = json['id'] ?? generateId();
}
}
// argResults也是ArgResults的实例
@ -34,18 +39,18 @@ void createDemo() async {
InputQuestion('desc', '请输入您demo的描述'),
];
Map answers = {};
Map<dynamic, dynamic> answers = {};
//
Prompt prompt = Prompt(questions);
DemoDetail demoDetail;
answers = await prompt.execute();
print(Seperator());
print('您新增的组件信息如下');
print(Seperator('='));
prettyPrintJson(answers);
print(Seperator('='));
questions =[ConfirmQuestion('confirm', 'Is this the config you want ?')];
prompt = Prompt(questions);
Map confirmAnswers = await prompt.execute();
@ -54,13 +59,13 @@ void createDemo() async {
}
demoDetail = DemoDetail.fromJson(answers);
var demoPath = '$TARGET_FILE_DIC/${demoDetail.name}_${demoDetail.author}_${demoDetail.id}';
var demoPath = '$TARGET_DEMO_DIC/${demoDetail.name}_${demoDetail.author}_${demoDetail.id}';
// root文件
await createFile(demoPath);
await createFile('$demoPath/src');
print("demoPath>>>> ${environmentVars['PWD']}/${demoPath}");
writeContent2Path('$demoPath/src/', '${demoDetail.name}.dart', """
// print("demoPath>>>> ${environmentVars['PWD']}/${demoPath}");
writeContent2Path('$demoPath/src/', 'index.dart', """
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
@ -98,13 +103,19 @@ class _State extends State<Demo> {
// desc: ${demoDetail.desc}
//
import 'src/${demoDetail.name}.dart';
import 'src/index.dart';
var demoWidgets = [
new Demo()
];
""");
// format('{color.red}Invalid demo happends: $details {color.normal}');
prettyPrintJson({
'新建的demo文件位于': p.absolute((demoPath)),
'demoId为': demoDetail.id,
'markdown中调用方式': '[demo:${demoDetail.id}]'
});
buildDemoListJson();
}

View File

@ -0,0 +1,112 @@
import 'dart:io';
import 'package:args/args.dart'; // 使用其中两个类ArgParser和ArgResults
import 'package:dart_inquirer/dart_inquirer.dart';
import 'package:args/command_runner.dart';
import '../build/build_demo_list.dart';
import '../config.dart';
import '../../utils/util.dart';
var pageMarkdown = """
# page
this is page markdown
you can load demo like this
```
[demo: xxxid]
```
""";
ArgResults argResults; // 声明ArgResults类型的顶级变量保存解析的参数结果
class PageDetail {
String name;
String author;
String email;
String desc;
String id;
PageDetail.fromJson(Map<dynamic, dynamic> json) {
name = json['name'];
author = json['author'];
email = json['email'];
desc = json['desc'];
id = json['id'] ?? generateId();
}
}
// 同时argResults也是ArgResults的实例
void createPage() async {
Map environmentVars = Platform.environment;
List<Question> questions = [
InputQuestion('name', '请输入新增加的界面名称?'),
InputQuestion('author', '请输入您的姓名(使用英文)'),
InputQuestion('email', '请输入您的email地址'),
InputQuestion('desc', '请输入您界面的简要'),
];
Map<dynamic, dynamic> answers = {};
// 获取初次信息
Prompt prompt = Prompt(questions);
PageDetail pageDetail;
answers = await prompt.execute();
print(Seperator());
print('您新增的界面信息如下');
print(Seperator('='));
prettyPrintJson(answers);
print(Seperator('='));
questions =[ConfirmQuestion('confirm', 'Is this the config you want ?')];
prompt = Prompt(questions);
Map confirmAnswers = await prompt.execute();
if (confirmAnswers['confirm'] != true) {
return createPage();
}
pageDetail = PageDetail.fromJson(answers);
var demoPath = '$TARGET_PAGE_DIC/${pageDetail.name}_${pageDetail.author}_${pageDetail.id}';
// 创建root文件
await createFile(demoPath);
print("demoPath>>>> ${environmentVars['PWD']}/${demoPath}");
writeContent2Path('$demoPath/', 'index.dart', """
String getMd() {
return \"\"\"
${pageMarkdown}\"\"\";
}
""");
writeContent2Path('$demoPath/', '.page.json', """
{
"name": "${pageDetail.name}",
"screenShot": "",
"author":"${pageDetail.author}",
"email": "${pageDetail.email}",
"desc": "${pageDetail.desc}",
"id": "${pageDetail.id}"
}
""");
writeContent2Path('$demoPath/', 'index.md', pageMarkdown);
}
class CreatePageCommand extends Command<int> {
@override
final name = 'createPage';
@override
final description = '新增flutter go page.';
CreateDemoCommand() {
}
@override
Future<int> run() async {
createPage();
return 0;
}
}

View File

@ -0,0 +1,102 @@
//
// Created with Android Studio.
// User: 三帆
// Date: 08/06/2019
// Time: 16:34
// email: sanfan.hx@alibaba-inc.com
// target: 监听demo与md
//
import 'package:path/path.dart' as p;
import 'package:args/command_runner.dart';
import '../build/build_demo_list.dart';
import '../build/build_page_list.dart';
import 'package:watcher/watcher.dart';
import '../config.dart';
import 'dart:async';
void watch() {
Timer _changeTimer;
List<Map<String, String>> config = [
{
"type": "demo",
"path": '$TARGET_DEMO_DIC'
}
];
List<String> demoIgnore = [
'.demo.json',
'index.dart',
'info.json',
'readme.md',
];
var watcherDemo = DirectoryWatcher(p.absolute('$TARGET_DEMO_DIC'));
watcherDemo.events.listen((event) {
if (isIgnore(demoIgnore, TARGET_DEMO_DIC, event.path)) {
return ;
}
if (event.type == ChangeType.ADD || event.type == ChangeType.REMOVE) {
if (_changeTimer != null && _changeTimer.isActive) {
_changeTimer.cancel();
}
_changeTimer = new Timer(Duration(milliseconds: 1000), () {
buildDemoListJson();
});
}
}, onError: (error) {
print('watch 发生错误: $error');
});
List<String> pageIgnore = [
'index.dart',
'.pages.json'
];
var watcherPage = DirectoryWatcher(p.absolute('$TARGET_PAGE_DIC'));
watcherPage.events.listen((event) {
if (isIgnore(pageIgnore, TARGET_PAGE_DIC, event.path)) {
return ;
}
if (event.type == ChangeType.ADD || event.type == ChangeType.REMOVE) {
print("buildPageListJson:::");
buildPageListJson();
return ;
}
if (event.type == ChangeType.MODIFY) {
if (event.path.contains('index.md')) {
transformMd2Dart(event.path.replaceAll('/index.md', ''));
}
return ;
}
print(event);
});
}
bool isIgnore (List<String> ignorePath, parentPath, currentPath) {
for (String ignore in ignorePath) {
String path = p.absolute(parentPath, ignore);
if (currentPath.contains(path)) {
// print("修改的文件, 是忽略列表中的文件, 跳过编译!");
return true;
}
}
return false;
}
class WatchCommand extends Command<int> {
@override
final name = 'watch';
@override
final description = '动态生成文档与demo相关';
CreateDemoCommand() {
}
@override
Future<int> run() async {
watch();
return 0;
}
}

View File

@ -1,2 +1,4 @@
// 生成Demo的目录位置
const TARGET_FILE_DIC = 'lib/page_demo_package';
const TARGET_PAGE_DIC = 'lib/standard_pages';
const TARGET_DEMO_DIC = 'lib/page_demo_package';

View File

@ -12,6 +12,11 @@ Future<bool> createFile(String path) async {
await tempDic.createSync(recursive: true);
return Future(() => true);
}
Future<String> readeFile(String path) async {
String content = await new File(path).readAsString();
return Future(() => content);
}
// 该文件调用的时候. 必须存在父级文件夹
Future<void> writeContent2Path(String path,String fileName, String content) async {
var file = File('$path/$fileName');
@ -21,7 +26,7 @@ Future<void> writeContent2Path(String path,String fileName, String content) asyn
await sink.close();
return Future.value();
}
String generateDemoId() {
String generateId() {
int d = DateTime.now().millisecondsSinceEpoch;
var random = new Random(1);
var template = 'xxxxxxxx_xxxx_4xxx_yxxx_xxxxxxxxxxxx';
@ -52,5 +57,8 @@ Future<List<FileSystemEntity>> readeDirChildren(String path, [bool recursive = f
print('读取文件失败 ${e.toString()}');
return Future(() => []);
}
}
//void main() {
// readeFile('/Users/ontwo/Documents/ali/flutter/flutter-common-widgets-app/LICENSE');
//}

View File

@ -1,8 +0,0 @@
{
"name": "button-red",
"screenShot": "",
"author": "sanfan",
"email": "sanfan.hx@alibaba-inc.com",
"desc": "desc",
"id": "70c429df-c27d-4843-8e28-1e6885c9276b"
}

View File

@ -1,15 +0,0 @@
//
// Created with Android Studio.
// User: 三帆
// Date: 16/05/2019
// Time: 17:08
// email: sanfan.hx@alibaba-inc.com
// target: xxx
//
import 'src/button-red.dart';
var demoWidgets = [
new Demo()
];

View File

@ -1,15 +0,0 @@
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
@override
_State createState() => _State();
}
class _State extends State<Demo> {
@override
Widget build(BuildContext context) {
return Container(
child: Text("this is a cli init demo change haha"),
);
}
}

View File

@ -1,9 +0,0 @@
{
"name": "button",
"screenShot": "",
"author":"sanfan",
"email": "sanfan@github.com",
"desc": "desc",
"id": "72af2949_22ae_4241_9c8a_5c9e1f92b096"
}

View File

@ -1,15 +0,0 @@
//
// Created with flutter go cli
// User: sanfan
// Time: 2019-05-28 15:33:53.535300
// email: sanfan@github.com
// desc: desc
//
import 'src/button.dart';
var demoWidgets = [
new Demo()
];

View File

@ -1,16 +0,0 @@
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
@override
_State createState() => _State();
}
class _State extends State<Demo> {
@override
Widget build(BuildContext context) {
return Container(
child: Text("this is flutter go init demo"),
);
}
}

View File

@ -1,94 +0,0 @@
const stringMd = '''
Language: [English](https://github.com/alibaba/flutter-go/blob/master/README-en.md) | [中文简体](https://github.com/alibaba/flutter-go/blob/master/README.md)
## Flutter Go
![https://img.alicdn.com/tfs/TB1OJkeHNYaK1RjSZFnXXa80pXa-229-229.png](https://img.alicdn.com/tfs/TB1OJkeHNYaK1RjSZFnXXa80pXa-229-229.png)
> 帮助开发者快速上手 Flutter **Flutter Go 1.0 Android版已正式发布**
## 版本更新历史
> 按时间顺序,展示重要的提交更新内容。
[地址](https://github.com/alibaba/flutter-go/blob/develop/CHANGE-LOG.md)
## 开发规范
> 由于类似 javascript, java, object-c,等开发者的语言习惯不同而产生歧义,我们依据官方提供的 [dart 语言规范](https://www.dartlang.org) 定制。
[<< Flutter Go 开发规范第一版 >>](https://github.com/alibaba/flutter-go/blob/develop/Flutter_Go%20%E4%BB%A3%E7%A0%81%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83.md)
## The Flutter-Go Roadmap路线图 for 2019
> 考虑到 Flutter 未来的变化和策略的可变性, roadmap 不排除有一定调整,但总体不会变化太大。
<img src="https://img.alicdn.com/tfs/TB19UahQQzoK1RjSZFlXXai4VXa-1500-1106.png" width="600px">
## Release安装包下载地址
android下载地址:
华为市场已上线master分支上面已上传【FlutterGo.apk】apk包可点击下载
iphone下载地址: AppStore上面进行搜索fluttego
<img src="https://img.alicdn.com/tfs/TB1trMgV7PoK1RjSZKbXXX1IXXa-620-1343.png" width=200> <img src="https://img.alicdn.com/tfs/TB1w_MbV7voK1RjSZFDXXXY3pXa-620-1343.png" width=200>
## 基础环境
本项目环境持续更新. 请定期更新各依赖包.
- dart(version: 2.0.0)
- flutter(version: v1.0.0)
### 背景
#### Flutter 是什么?
2018年6月21日Google发布Flutter首个release预览版,作为Google 大力推出的一种全新的响应式跨平台高性能的移动开发框架。Flutter是一个跨平台的移动UI框架旨在帮助开发者使用一套代码开发高性能、高保真的Android和iOS应用。
flutter优点主要包括
- 跨平台
- 开源
- Hot Reload、响应式框架、及其丰富的控件以及开发工具
- 灵活的界面设计以及控件组合
- 借助可以移植的GPU加速的渲染引擎以及高性能ARM代码运行时已达到高质量的用户体验
#### Flutter Go 的由来
- Flutter学习资料太少对于英文不好的同学相对来说比较困难
- 官网文档示例不够健全,不够直观
- 各个 widget 的用法各异,属性纷繁,要运行一个 widget 的 demo 往往要到处翻阅各种资料
#### Flutter Go 的优势
- 详解常用 widget 多达 **140+** 个
- 配套 Demo 详解 widget 常规用法
- 集中整合 widget 案例,一个 APP 搞定所有常用 widget 的用法
- 持续迭代 ‘追新’ 官方版本
### 版权说明
- 感谢大家对 `flutter go` 的支持和下载,但近期发现,有类似直接被发布到苹果 app store 上的行为并未注明真实来源copyright 和 项目的 github 地址,以及开发者的版权相关信息( 包括删除"首页栏的版权声明"和"关于我们"的行为 );
- 上述行为,打击了 `flutter go` 开发者的积极性,同时干扰了 flutter go app 的正常发布渠道,基于 app 开源项目的发布特殊性,特更新 [LICENSE](LICENSE) 「 开源许可证 」,由 MIT 协议 更改为 BSD 协议, 同时建议不要随意发布到公共渠道的应用商店,影响官方 `flutter go` 的app版本迭代;
- 大家可以继续放心的开源使用,但是要求注意和遵守以下许可前提:
```
* 版权声明样式
//Copyright (c) 2018-present, Alibaba Group Holding Limited. All rights reserved.
* 源代码的重新分发必须保留上述版权声明,条件清单和免责声明。
* 二进制形式的再分发必须复制上述版权声明,此条件列表以及文档中的以下免责声明和/或随分发提供的其他材料。
```
- 由于本开源项目是供大家学习和交流 flutter 之用,里面耗费了开发人员大量的心血,精力和热情,请尊重开发者的劳动成果,以及相关许可证之规定;
- 大家的互相信任,尊重与支持,才是开源社区前进的动力和来源.
Powered by [阿里拍卖前端团队](https://github.com/alibaba-paimai-frontend)
''';

View File

@ -1,8 +0,0 @@
{
"name": "这是一个标准页",
"desc": "详细描述, 不显示",
"auth": "sanfan",
"email": "sanfan.hx@alibaba-inc.com",
"type": "standard",
"version": "123123"
}

View File

@ -1 +0,0 @@
# this is a markdown in page12222;

View File

@ -1,19 +0,0 @@
[
{
"name": "这是一个标准页1",
"desc": "详细描述1, 不显示",
"auth": "sanfan",
"email": "sanfan.hx@alibaba-inc.com",
"type": "standard",
"id": "172ba42f_0520_401e_b568_ba3f7f6835e4"
},
{
"name": "这是一个标准页2",
"desc": "详细描述2, 不显示",
"auth": "hanxu",
"email": "sanfan.hx@alibaba-inc.com",
"type": "standard",
"id": "172ba42f_0520_401e_b568_ba7f7f6835e4",
"version": "123123"
}
]