[{"categories":null,"contents":"在应用运行期间，使用代码动态的生成可视组件并设置属性，不需要提前在设计界面拖入很多组件。\nCredit: This extension is inspired on the fabulous work of Yusuf Cihan: GitHub - ysfchn/DynamicComponents-AI2\n所有功能块 创建组件 component：是要创建的组件的类型（文本字串），这里推荐使用选项下拉框输入.\nin：是新组件的父容器，需要是个容器类组件。\nKnown issue:\nThe dynamic component will not be listed by \u0026ldquo;Every Component\u0026rdquo;, which is newly added in MIT AI2 2.66.\nBut you can use the ListComponents function of this extension.\n组件列表 可以返回同类型的组件列表\nrecursive: boolean. If true, it will return all components with same type. If false, it will return only the direct children.\n移除组件 component：要删除的组件，而不是他的类型。如果他是容器类，他的所有子组件也会被移除。\n设置属性 这个块的大部分功能可以使用内置的任意组件块来实现。\ncomponent：要设置属性的组件\nproperty：要设置的组件的属性。可以是以下3种情况:\n可以是组件的自带的属性，必须是英文（推荐使用下拉框输入）； 可以是“index”，用来设置组件的排列顺序； 可以是其他自定义的属性，比如id，parent之类的任意文本，中英皆可。相当于附在组件上的一个字典（键值对）。 value：新的属性值\n获取属性 这个块的大部分功能可以使用内置的任意组件块来实现。\n获取子组件列表和父组件 只适用于获取动态生成的子组件列表或他的父组件\n从8.4版本以后，可以支持所有组件（动态的或者手动添加的）获取子组件和父组件。\n已知问题：在伴侣中获取Screen的子部件会返回错误的列表。apk中没有问题。\n从模板创建 返回值是创建的最外层的容器组件\ntemplate: json字符串，创建一组组件需要的模板文本。\nin: 容器组件\nvalues: 列表。传入组件模板的值。\n可以根据以下格式手动编写，或者在MIT服务器上的设计界面手动添加需要的界面（一条记录），根据需要设置相应的组件属性。选中最外层组件，键盘上按下Ctrl + C，然后在任何文本编辑器里面按键盘上的Ctrl+V，就可以得到模板了\n{ \u0026#34;$components\u0026#34;: [ { \u0026#34;$Name\u0026#34;: \u0026#34;VerticalArrangement1\u0026#34;, \u0026#34;$Type\u0026#34;: \u0026#34;VerticalArrangement\u0026#34;, \u0026#34;$Version\u0026#34;: \u0026#34;4\u0026#34;, \u0026#34;Uuid\u0026#34;: \u0026#34;-444056707\u0026#34;, \u0026#34;$Components\u0026#34;: [ { \u0026#34;$Name\u0026#34;: \u0026#34;Button2\u0026#34;, \u0026#34;$Type\u0026#34;: \u0026#34;Button\u0026#34;, \u0026#34;$Version\u0026#34;: \u0026#34;7\u0026#34;, \u0026#34;Text\u0026#34;: \u0026#34;Text for Button2\u0026#34;, \u0026#34;TextColor\u0026#34;: \u0026#34;\u0026amp;HFFFF00FF\u0026#34;, \u0026#34;Uuid\u0026#34;: \u0026#34;12991510\u0026#34; } ] } ], \u0026#34;$blocks\u0026#34;: [] } 以下模板中的$Name,$Version,Uuid,$blocks几个键是从mit自动转换来的，模板中并不需要。你可以把它们删掉，也可以保留，扩展会自动忽略他们。\n$Components 和$Type是必须保留的。\n如果你想在运行时替换模板中的属性值，可以像下图这样，替换成{1}, {2}, {3}\u0026hellip;这样。\n{ \u0026#34;$components\u0026#34;: [ { \u0026#34;$Name\u0026#34;: \u0026#34;VerticalArrangement1\u0026#34;, \u0026#34;$Type\u0026#34;: \u0026#34;VerticalArrangement\u0026#34;, \u0026#34;$Version\u0026#34;: \u0026#34;4\u0026#34;, \u0026#34;Uuid\u0026#34;: \u0026#34;-444056707\u0026#34;, \u0026#34;$Components\u0026#34;: [ { \u0026#34;$Name\u0026#34;: \u0026#34;Button2\u0026#34;, \u0026#34;$Type\u0026#34;: \u0026#34;Button\u0026#34;, \u0026#34;$Version\u0026#34;: \u0026#34;7\u0026#34;, \u0026#34;Text\u0026#34;: \u0026#34;{1}\u0026#34;, \u0026#34;TextColor\u0026#34;: \u0026#34;{2}\u0026#34;, \u0026#34;Uuid\u0026#34;: \u0026#34;12991510\u0026#34; } ] } ], \u0026#34;$blocks\u0026#34;: [] } 然后这样使用模板,新生成的按钮就可以有新的文本和颜色了：\n添加删除点击事件 为新建组件添加或取消点击事件。\nlistener: 过程名。需要手动创建一个过程名，包含一个参数，该参数即为被点击的组件。\n下载地址： 扩展aix：\ncn.kevinkun.CompCreator-V8.9.aix\ncn.kevinkun.CompCreator-V8.10.aix\n示例aia:\ncompcreator-listview-by-template.aia\n更多示例及用法，可以参考这里\n","permalink":"https://wangsk789.github.io/compcreator/","tags":null,"title":"动态组件扩展"},{"categories":null,"contents":"表格视图扩展,可以用来直接在ai中显示表格，并可以设置表格样式\n更新记录 v9 as request of @TIMAI2, SetJavascript function added.It will inject javascript to end of body element before show the table. No script tag needed.\nv8 add event OnShowTable, fired after ShowTable is called.\nv7 add property TransparentBackground, designer only.\nV6\nSetStyleSheet now can accept url to an external css file. add SetClass block, which can set class to a tag or id selector add ClearStyle block, which can clear all styles, including the native basic style(table border) bring back ShowTable. since this will greatly enhance the performance of the extension. add RunJs function and AfterRunJs event. add SetStyleWithJs function v5\n增加了OnScroll事件 v4\n增加ScrollTo方法。 废弃ShowTable方法。 v3\n增加了ClearTable块。清除以前设置的数据、所有样式。 其他修改，每行数据可以长度不同 v2\n增加SetStyleSheet块，可以直接写样式表了。 增加ShowTable块，所有的数据和样式设置好以后，需要调用此块，否则表格不显示了。 增加了一个SourceCode属性块，调试时可能有用。 增加了HasHead和HasFoot属性，用来将第一行和最后一行数据设为表头或者表尾。默认是不包含的。 前言 本来有个教程，教大家如何显示表格，但是有网友反映还是不够简洁，现在我把这个方法进一步打包成了扩展，真正做到开箱即用。\n先上图看看效果如何： 相关代码块 初始化 指定表格要显示在哪个布局之中（可以是垂直布局或者水平布局均可）\n设置数据 data需要是一个列表的列表\n每次设置数据，会覆盖前面的数据。\n设置单个样式 样式设置完全是css的标准，具体请搜索如何写css的样式。\n这个可以使用多次，每次设置一个选择器的一个属性。\nselector 选择器\nattribute 属性名\nvalue 属性值\n常用的selector： \u0026ndash; \u0026ndash; \u0026ndash; 1. table 表格 2. th 表头 3. tr 行 4. td 单元格 5. tr:nth-child(1) 第n行 （所有偶数行可以写为tr:nth-child(2n)，奇数行就是tr:nth-child(2n+1)） 6. td:nth-child(1) 第n列 （奇数偶数列原理同上） 7. #r1c1 第n行第n列的单元格 （注意别漏掉#，因为我已经将每个单元格的id设置为了r1c1格式） 常用的attribute： |\u0026ndash;|\u0026ndash;|\u0026ndash;| |1. |background-color |背景色| |2. |color |文本颜色| |3. |padding |内边距| |4. |margin |外边距| |5. |width |宽度| |6. |height |高度| |7. |font-size |字号| |8. |border-width |边框宽度| |9. |border-color |边框颜色|\n批量设置样式 如果你有很多样式要写，用上面的SetStyle就需要多个，比较麻烦，可以使用这个块，直接把样式表写在文本中。\n这个块只能使用一次，后面调用会把前面的覆盖。所以把需要写的样式一次性都写在这里面。\n清除样式 原来设置的所有数据和样式都被清空。 滚动表格 将表格滑动到某行某列（让该单元格可见）\n内置常用的样式 返回网页源码 是否包含表头 是否包含表尾 以下块都可以通过SetStyle块来实现，只是为了方便写成内置块。\n设置单元格内边距 设置表头（第一行）背景色\n目前只支持例如red、green这样，或者#abcdef这种16进制，不支持AppInventer中的颜色。（下同） 设置边框颜色 设置边框宽度 设置所有文本字号 表格显示完成事件 js执行完成事件 表格被点击事件 表格被点击时，返回点击位置的行、列、内容 表格滚动事件 表格滚动时（手动或通过代码）引发 示例 例子1：基本用法\n例子2：高亮当前选中单元格\n例子3：固定表格第一行。这样在上下滚动时，表头固定在屏幕上方。\n例子4：合并单元格\n下载链接 cn.kevinkun.TableViewer.v8.aix\ncn.kevinkun.TableViewer.v9.aix\n","permalink":"https://wangsk789.github.io/tableviewer/","tags":null,"title":"表格视图扩展"},{"categories":null,"contents":"应用开发过程中，调试是必不可少的步骤。很多同学每次代码有点变动，就整个项目编译成apk安装到手机上进行调试，这是不对的。不仅费时费事，而且增加了编译服务器的压力。App Inventor提供了AI伴侣，让我们可以实时的调试。\n有哪些调试方式 要调试代码，首先需要将手机与开发服务器连接。AI提供了3种连接方式：AI伴侣、模拟器、USB模式。其中模拟器是使用系统自带的内置模拟器。这个模拟器版本较老，运行缓慢，不推荐。USB模式需要安装手机驱动，也不是很方便。推荐使用AI伴侣来调试代码。\n手机安装AI伴侣调试 通过帮助-Ai伴侣信息，弹出AI伴侣的下载链接二维码。 手机扫描二维码，下载并安装AI伴侣。 打开运行AI伴侣。第一次运行可能需要权限许可。 手机连接服务器 依次打开 连接-AI伴侣，弹出连接码。 使用AI伴侣扫描二维码（或手动输入连接码），等他同步素材、扩展，显示app界面就是连接成功了。 预览代码块 AI伴侣连接成功后，就可以调试了。我们在设计界面的每次改动，都会实时的反映在AI伴侣中。 如果想知道当前某个（全局）变量的值，可以在变量块上右键，预览代码块，就可以在代码块的注释中看到结果了。（没有连接AI伴侣时，预览代码块是灰色的，无法执行） 注意这里只能预览全局变量，不能预览局部变量。当然也可以预览运行一段代码（过程） 如果想知道局部变量的值，可以将他赋值给某个标签的文本，这样运行时就看到这个值的变化了。 电脑安装安卓模拟器 运行AI伴侣 如果手机不在身边又想调试，可以在电脑上安装安卓模拟器，在模拟器内安装AI伴侣来调试。\n安卓模拟器有非常多，推荐使用雷电模拟器。\n打开AI伴侣后，显示的ip地址可能跟你电脑的本地ip不在一个段，这样就没法连接。需要做如下设置：\n打开软件设置：\n开启网络桥接模式，安装相应驱动。安装3完成后请重启模拟器。否则4的位置不出现相关的网卡，ip设置设为DHCP就可以。 这样打开AI伴侣，显示的ip地址跟电脑就在一个ip段了（一般是192.168\u0026hellip;.）。\n开发界面，打开连接菜单，AI伴侣，将连接码输入ai伴侣，连接后就可以愉快的开发了。\n几个注意事项 服务器页面刷新或者更换语言后需要重新连接AI伴侣。 扩展插件升级后需要重新连接AI伴侣。 ","permalink":"https://wangsk789.github.io/debug/","tags":null,"title":"AI2中如何调试应用"},{"categories":null,"contents":"Why another extension about Airtable? Because this one is different. This one can select/ insert/ update/ delete according to your filter/condition.\nRelease history V2 Update history 2020/12/24 fix bug of \u0026ldquo;can not update value with space\u0026rdquo;, now in the formula, use \u0026quot; and \u0026quot; to quote the value with space. add perperty of FetchIdAndTime. if set to true, in the SelectFinished event you will get \u0026lsquo;id\u0026rsquo; and \u0026lsquo;createdTime\u0026rsquo; for each record. add method \u0026lsquo;UpdateById\u0026rsquo; and \u0026lsquo;DeleteById\u0026rsquo; for update or delete one record, which is faster than by filter. all the blocks Initialize the extension Same like other Airtable component, get your API Key, BaseId, TableName from the airtable website.\nupdate on 13/02/2024 Since 01/01/2024, Airtable deprecated ApiKey, and suggest to use token. You can set the ApiKey to the token, this extension still working.\nSelect records fields: String. or called \u0026ldquo;column names\u0026rdquo;, Format like \u0026ldquo;name,age,phone\u0026rdquo;. Leave it blank for all fields\nfilter: String. condition the records have to fit. Format like \u0026ldquo;age\u0026gt;30\u0026rdquo; or \u0026ldquo;OR(age\u0026gt;20, age\u0026lt;50)\u0026rdquo;. For more info, please refer to here\npageSize: Number. how much records returned one time.. Max value 100.\noffset: String. If the records fit the filter more than pageSize, you will get an offset at SelectFinished event, use it here for more records.\nsort: String. the records will be ordered on this field. Format like \u0026ldquo;age desc\u0026rdquo; or \u0026ldquo;age asc\u0026rdquo;\ncount: Number. how much records selected.\nrecords: String. Json format. it\u0026rsquo;s a json array of dictionaries.\nInsert records records: String. Json format. it\u0026rsquo;s a json array of dictionaries. Format like [{\u0026ldquo;name\u0026rdquo;:\u0026ldquo;Jasmine Lake\u0026rdquo;,\u0026ldquo;age\u0026rdquo;:43,\u0026ldquo;phone\u0026rdquo;:\u0026ldquo;513937\u0026rdquo;}, {\u0026ldquo;name\u0026rdquo;:\u0026ldquo;Ava Sharp\u0026rdquo;,\u0026ldquo;age\u0026rdquo;:21,\u0026ldquo;phone\u0026rdquo;:\u0026ldquo;293309\u0026rdquo;}]\nIMPORTANT: If you want to feed this param with List component, remember to check \u0026ldquo;Show List As Json\u0026rdquo; at Screen1 Properties panel.\ncount: Number. how much records inserted. if count=0, means no records inserted.\nUpdate records filter: pls refer to Select part NOTE: ONLY up to 10 records will be updated. if more than 10 records fit the filter, there will be Error Cccured.\nformula:String. How to change the data. Format like: \u0026ldquo;age += 1\u0026rdquo;. there are spaces before and after the “+=”。 Now the accepted operators are +=，-=, *=, /=, = for number field, and to for string field.\ncount: Number. how much records updated. if count=0, means no records updated.\nDelete records filter: pls refer to Select part NOTE: ONLY up to 10 records will be deleted. if more than 10 records fit the filter, there will be Error Cccured.\ncount: Number. how much records deleted. if count=0, means no records deleted\nError Occurred message: String. Error reason.\nDownload link here cn.kevinkun.KevinkunAirtable.aix\n","permalink":"https://wangsk789.github.io/airtable/","tags":null,"title":"AirTable extension "},{"categories":null,"contents":"一些跟Base64有关的功能的集合\nCredit: @TIMAI2的开源代码\nAttension: 下面提到的base64文本都是去掉了类似\u0026quot;data:image/png;base64,\u0026ldquo;前缀的文本。\n图像框有关 画布有关 根据base64设置画布背景，已经有专门的内置属性块了。\n文件有关 文件的位置必须是在ASD。 不仅适用于图像文件，可以是任意文件类型。\n文本字符串有关 文本的编码和解码。\n图像精灵有关 设置图像精灵的图像。\n其他可视组件有关 设置组件的背景图。\n下载链接： cn.kevinkun.Base64Util.aix\n","permalink":"https://wangsk789.github.io/base64util/","tags":null,"title":"Base64Util"},{"categories":null,"contents":"可以设置组件的背景色、背景图像、边框、圆角矩形、外边距、内边距、高程、使用自定义字体等\n示例截图 实时调整布局外观(感谢ldtxinkai提供截图)\n属性块 设置背景颜色及圆角 背景颜色fillColor、边框宽度borderWidth、边框颜色borderColor、圆角半径roundRadius。\n\u0026ldquo;圆角半径可以是一个数字（同时设置四个角），或者是四个数字用逗号隔开（顺时针分别设置左上、右上、右下、左下）。\n设置背景图片及圆角 背景图片imagePath、边框宽度borderWidth、边框颜色borderColor、圆角半径roundRadius。\nimagePath：以//开始表明是素材，以/sdcard/开始表明是在外部存储卡。\n\u0026ldquo;圆角半径可以是一个数字（同时设置四个角），或者是四个数字用逗号隔开（顺时针分别设置左上、右上、右下、左下）。\n设置外边距 设置组件的外边距margin。外边距可以是一个数字（同时设置四个方向），或者是四个数字用逗号隔开（分别设置左、上、右、下）。\n设置内边距 设置组件的内边距padding。内边距可以是一个数字（同时设置四个方向），或者是四个数字用逗号隔开（分别设置左、上、右、下）。\n设置高程 设置组件的高程elevation。会在组件周围显示阴影效果（必须提前设置背景）\n设置自定义字体 设置组件字体。fontName字体名以//开始表明字体在素材库，以/sdcard/开始表明在外部存储卡。\n其他 组件component那里不仅可以接布局，还可以接标签、按钮等。更多可能等你去发现~~~\n下载链接 cn.kevinkun.KevinkunEnhance.aix\n","permalink":"https://wangsk789.github.io/enhance/","tags":null,"title":"Enhance界面增强扩展"},{"categories":null,"contents":"直接读取素材或者sdcard上的excel文件，无需转为csv文件。\n仅支持xls格式，暂不支持xlsx格式.\n使用这个方法读取xlsx文件\n方法 读取工作表 读取行 读取列 读取单元格 写入单元格 不支持写入素材 关于文件名路径的简写方法 //开头的表明存放在素材里面 不用/开头的表明存放在ASD(专属目录) 下载链接 cn.kevinkun.ExcelUtil.aix\n","permalink":"https://wangsk789.github.io/excelutil/","tags":null,"title":"ExcelUtil扩展"},{"categories":null,"contents":"用LeanCloud作为后台，进行数据存储、用户管理、实时通讯等功能\n更少的代码，做更多的事。。。\n简介 包含6个部分，分别是\n组件名 中文 具体描述 LeanStorage 数据存储 添加、查询、更新、删除 （本组件已经单独免费发布） LeanUser 用户管理 用户注册、登录、修改密码、重置密码等 LeanLeaderBoard 排行榜功能 添加成绩、获取成绩、获取前10，获取我前后成绩等 LeanFile 上传文件 上传文件 LeanCaptcha 图形验证码 获取和验证图形验证码 LeanMessage 实时聊天 发送文消息、接受消息，获取历史消息。不只是聊天 准备工作 注册LeanCloud账号，网址是 https://www.leancloud.cn； 可能需要实名认证； 进入控制台，新建应用（相当于关系型数据库中的数据库）。； 进入应用，点设置，点应用Keys，将右侧的Appid，AppKey，Rest Api服务器地址记下，初始化时用到； 代码示例 LeanUser 用户管理 LeanLeaderBoard 排行榜 需要配合LeanUser先注册用户，需要事先创建排行榜：\n进入LeanCloud控制台，进入应用，点左边游戏，点排行榜下的数据，新建排行榜。按你的要求输入相关参数，完成创建。\n排行榜参数：\n1.名称，不解释\n2.排序，你的排行榜按升序ascending还是 降序descending排列\n3.更新策略，better（提交多次成绩只记录最好的），last（多次记录只记录最后提交的），sum（所有提交的记录相加。）\n4.自动重置频率，每个相应的时间，排行榜就重置。可以设为从不重置。 LeanFile 文件上传 仅支持Leancloud华北节点，限制10m以下文件。 LeanMessage 实时通讯 这个初始化，不能直接在屏幕初始化中进行，需要延时一段时间比如500毫秒。\n需要配合LeanUser先注册用户。 LeanCaptcha 验证码 购买链接 付费，需要的请直接qq或邮箱联系站长\n","permalink":"https://wangsk789.github.io/leancloud/","tags":null,"title":"LeanCloud扩展"},{"categories":null,"contents":"LeanDB扩展可以作为app inventor应用的后端数据支撑。进行数据的增加、修改、删除、查询等功能。\n准备工作 注册LeanCloud账号，网址是 https://www.leancloud.cn；可能需要实名认证；不想认证的，~~~请切换到国际版~~~ （国际版提供的域名不支持国内访问了）。 进入控制台，新建应用（相当于关系型数据库中的数据库）。我这里是新建了个应用 Jiaocheng； 进入应用，点设置，点应用Keys，将右侧的Appid，AppKey，Rest Api服务器地址记下； 创建一个新的Class（相当于关系型数据库中的表）。我这里新建一个Class，叫Scores。 注意权限设置那里，一定要选择“无限制”，否则后续可能无法修改或删除数据。 在Class中添加列时，一定要注意设置列的类型。否则后面如果把数字当成文本来更新，就会失败。 相关功能 初始化 使用上面准备工作中的数据初始化扩展。推荐在屏幕初始化中进行。\n插入数据 参数名 类型 说明 jsonData String 要写入的数据集 rows Number 成功插入的数据个数 假设我们要在Scores中保存班级里面学生的考试成绩。要添加学生的一条考试记录（包括学生姓名、语文成绩、数学成绩），jsonData可以这样写\n{\u0026#34;xingming\u0026#34;:\u0026#34;张三\u0026#34;,\u0026#34;yuwen\u0026#34;:89,\u0026#34;shuxue\u0026#34;:96} 如果同时添加多名学生成绩，可以这样写\n[\r{\u0026#34;xingming\u0026#34;:\u0026#34;张三\u0026#34;,\u0026#34;yuwen\u0026#34;:56,\u0026#34;shuxue\u0026#34;:89},\r{\u0026#34;xingming\u0026#34;:\u0026#34;李四\u0026#34;,\u0026#34;yuwen\u0026#34;:69,\u0026#34;shuxue\u0026#34;:83},\r{\u0026#34;xingming\u0026#34;:\u0026#34;王五\u0026#34;,\u0026#34;yuwen\u0026#34;:98,\u0026#34;shuxue\u0026#34;:87}\r] 注意要加方括号（一条记录可以不用）。\n查询数据 参数名 类型 说明 keys String 查询记录中包含的字段。比如你想返回姓名和数学，就写xingming,shuxue，中间用半角逗号分隔。如果是想取回全部字段，可以设为空字串。如果全部返回，返回记录中除去我们手动添加的xingming、shuxue、yuwen等字段外，还有三个系统自己添加的：objectId（每条记录的id号）、createdAt（记录添加时间）、updatedAt（记录修改事件） conditionJson String 记录符合的条件。为json格式。比如查询姓名是老王的纪录，可以写为{“xingming“:”老王“}。也可以使用字典组件构建，请参照leancloud文档查看写法。推荐用下面的conditionJsonBuilder来构造。如果想取回全部记录，可以设为空字串。 order String 返回的记录按照哪个字段排序。比如我们要按照数学成绩降序排列，可以这样写：-shuxue。负号表示降序，正号或者没有符号表示升序。多个字段用半角逗号分隔。 startIndex Number 从第几条开始返回。默认1 maxCount Number 一次最多返回记录的条数。默认100 rows Number 返回记录条数 result List 返回的记录列表 更新数据 参数名 类型 说明 conditionJson String 见上面查询数据部分 updateJson String 要修改的数据，json格式。例如 {\u0026ldquo;xinging\u0026rdquo;:\u0026ldquo;zhang san\u0026rdquo;} 表示将符合条件的记录xingming字段改为zhangsan， 或者{\u0026ldquo;shuxue\u0026rdquo;:{\u0026quot;__op\u0026quot;:\u0026ldquo;Increment\u0026rdquo;,\u0026ldquo;amount\u0026rdquo;:5}} 表示将shuxe字段的值自增加5。具体请查看leancloud文档学习更多写法。推荐使用下面的UpdateJsonBuilder来构建。 rows Number 更新的记录条数 删除数据 参数含义同上面的块。\n查询条件构造器 这个辅助块可以构造查询（或者更新或者删除）命令的conditionjson。可以更快速、更简洁的写出查询条件，减少出错率。\n比如我们要查询“数学成绩大于等于60 并且 语文成绩小于等于80”的学生记录，手动写json这样写：\n{\u0026#34;$and\u0026#34;:[{\u0026#34;shuxue\u0026#34;:{\u0026#34;$gte\u0026#34;:60}},{\u0026#34;yuwen\u0026#34;:{\u0026#34;$lte\u0026#34;:80}}]} 其中的括号，冒号，引号，逗号太多，很容易出错。\n如果使用这个生成器，可以这样写：\n是不是非常的精炼，更加的符合自然语言。\nconditionExpress：条件表达式。半角空格（至少一个空格）隔开的字符串表达式，必须为三段。第一段为字段名，两侧不要有引号，第三段为要比较的值(文本或者列表需要用半角双引号括起来。其中有\u0026quot;双引号要写为\\\u0026quot;。\\要转义为\\\\)，第二段为比较符号，数字时支持=,\u0026lt;,\u0026lt;=,\u0026gt;,\u0026gt;=,!=，文本时支持like（模糊）,is（精确），时间用before或者after，查询数组用in,nin,all。多个条件用and或者 or连接，并前后留有一个或以上空格。（暂不支持用括号改变比较顺序）中间的比较符号目前支持以下几种： 符号 意义 举例 备注 = 等于 shuxue = 80 比较数字 \u0026gt; 或 gt 或 $gt 大于 shuxue \u0026gt; 80 比较数字 \u0026gt;= 或 gte 或 $gte 大于等于 shuxue \u0026gt;= 80 比较数字 \u0026lt; 或 lt 或 $lt 小于 shuxue \u0026lt; 80 比较数字 \u0026lt;= 或 lte 或 $lte 小于等于 shuxue \u0026lt;= 80 比较数字 != 或 ne 或 $ne 不等于 shuxue != 80 比较数字 like 包含（模糊查找） xingming like \u0026ldquo;张\u0026rdquo; 比较文本 is 等于（精确查找） xingming is \u0026ldquo;张三\u0026rdquo; 比较文本 before \u0026hellip;时间之前 createdAt before 2020-02-20T00:00:00.000Z 比较时间 after \u0026hellip;时间之后 createdAt after 2020-02-20T00:00:00.000Z 比较时间 in 或 $in 包含任意一个数组值 id in \u0026ldquo;[2,3,4]\u0026rdquo; 数组查询 nin 或 $nin 不包含任意一个数组值 xingming nin \u0026ldquo;[\u0026quot;老张\u0026quot;,\u0026quot;老李\u0026quot;,\u0026quot;老王\u0026quot;]\u0026rdquo; 数组查询 all 或 $all 包括所有的数组值 hobby all \u0026ldquo;[\u0026quot;skating\u0026quot;,\u0026quot;reading\u0026quot;]\u0026rdquo; 数组查询 size 或 $size 列表长度 hobby size 3 数组查询 更新条件构造器 这个辅助块可以构造更新命令的更新json，更快速、简洁的写出数据更新json，减少出错率。\n比如，我们要将姓名为‘张三’的学生的yuwen成绩和shuxue成绩分别加10分。我们可以这样手动写json格式的更新json：\n{\u0026#34;shuxue\u0026#34;:{\u0026#34;__op\u0026#34;:\u0026#34;Increment\u0026#34;,\u0026#34;amount\u0026#34;:10},\u0026#34;yuwen\u0026#34;:{\u0026#34;__op\u0026#34;:\u0026#34;Increment\u0026#34;,\u0026#34;amount\u0026#34;:10}} 用本构造器来写，可以非常简单的用两个块来这样写：\n更新表达式: 半角空格（一个或以上）隔开的字符串表达式，需要为3段。第一段为字段名，第三段为要改变的值(文本或者列表需要用半角双引号括起来。其中有\u0026quot;双引号要写为\\\u0026quot;。\\要转义为\\\\)。第二段是修改符号。如：\u0026rsquo;=,+,-\u0026lsquo;操作数字。\u0026rsquo;to\u0026rsquo;操作字符串。\u0026lsquo;add,addunique,remove\u0026rsquo;操作数组。如果修改多个字段，多个表达式之间用一个或以上半角空格分开。\n中间部分的op操作符号支持以下：\n符号 意义 举例 备注 + 或者 += 原字段值累加 shuxue + 10 数字字段 - 或者 -= 原字段值累减 shuxue - 10 数字字段 = 原字段直接赋值 shuxue = 10 数字字段 is 或者 to 原字段直接赋值 xingming to 张三 文本字段 add 数组添加项 hobby add \u0026ldquo;[\\\u0026ldquo;sleeping\\\u0026rdquo;]\u0026rdquo; 数组字段 addunique 数组添加不重复项 hobby addunique \u0026ldquo;[\\\u0026ldquo;sleeping\\\u0026rdquo;]\u0026rdquo; 数组字段 remove 数组删除项 hobby remove \u0026ldquo;[\\\u0026ldquo;sleeping\\\u0026rdquo;]\u0026rdquo; 数组字段 发生错误事件 参数名 类型 说明 err String 错误原因 下载链接 cn.kevinkun.LeanDB.aix\n","permalink":"https://wangsk789.github.io/leandb/","tags":null,"title":"LeanDB扩展"},{"categories":null,"contents":"To view the pdf without opening 3th party app\nALL BLOCKS LoadPDF LoadPDF function will fire PdfLoaded event if success, or OnError event if failed.\nparameter type description layou t Arrangement where the pdf file to show pdfPath String path of the pdf file. start with // means from assets, start with /sdcard/ means from sdcard, start with no / means from ASD. totalPage Number pages of the pdf file Preview This needs to be called after PdfLoaded event. You can preview all pages or only partially.\nparameter type description pageRange String page range you want to show, like: 3, or 1-10, or 1,3,5, or 1-5,7-9 Goto scroll to page you want to view.\nparameter type description page Number page you want to show Scrolled fired when the view is scrolled.\nparameter type description pageInView Number page now in view OnError parameter type description error String reason of error SIMPLE DEMO FAQ How to view pdf online internet? You need to download the pdf to local (like ASD) first.\nDOWNLOAD LINK cn.kevinkun.PdfViewer.aix\n","permalink":"https://wangsk789.github.io/pdfviewer/","tags":null,"title":"Pdf文件查看扩展"},{"categories":null,"contents":"正则表达式(regular expression)描述了一种字符串匹配的模式（pattern），可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。（摘自 菜鸟教程）\n方法 All Methods ![2023-05-10T05:01:22.png][1]\nGetMatches方法可以返回符合条件的字符串列表\nSplitWith方法可以使用规则表达式对字符串进行分割，并返回列表\nGetMatchesWithPosition方法\nGetMatchesWithGroup方法\nGetMatchesWithGroupAndPosition方法\n示例文件解释了他们的作用. ReplaceFirst方法和ReplaceAll方法可以返回替换第一个或所有符合条件的子字符串后的新字符串 IndexOf方法可以返回符合条件的第一个字串的位置 IsMatch方法可以判断字符串是否符合规则表达式，返回真或假\nIsNumber方法可以判断字符串是否为数字格式\nIsEmail方法可以判断字符串是否为邮箱格式\nIsStrongPassword方法可以判断字符串是否为强密码（至少8位，只好有1位大写和1位小写和1为数字）\nIsEmail和IsStrongPassword可以用在用户注册、登录等情况\n下载地址 Download Links: cn.kevinkun.KevinkunRegEx-v3.aix\n","permalink":"https://wangsk789.github.io/regex/","tags":null,"title":"RegEx正则表达式扩展"},{"categories":null,"contents":"App Inventor 2允许我们自己开发扩展组件，但是MIT原生的编译环境搭建相对比较复杂，费时费力。本文介绍一个比较轻便的aix编译器。\n本编译器由Zhangzq网友开发，在此对他的辛苦付出表示感谢。\n下载java jdk，开发扩展只用jdk1.8就可以了。安装并配置好环境变量，参看这个教程。\n下载AIX Complier,并解压到电脑。下载地址，也是Zhangzq的Gitee仓库, 本例是解压到E盘aixcompiler文件夹下：\n双击aixc.bat文件，可以看到aixCompiler的用法如下： QRCode.zip 这个是我写的一个二维码生成器扩展源代码。解压后它的目录结构是 QRCode是项目名，下面有src，assets，jni，lib等目录。\nsrc文件夹是存放源码文件，在src内按照包名的顺序再建立文件夹和java文件，结构如图：\n其中，KevinkunQRCode.java就是扩展的源码，是个文本文件，可以用很多的文本编辑器打开（比如notepad++, editplus等等，并且保存为utf8格式的文本。切记不要用windows自带的记事本和写字板打开。）。你可以在这个模板基础上修改成你需要的代码。注意包名和目录结构的对应关系。\n如果要引用素材资源，需要把资源放在assets文件夹下，在源码中添加：\n@UsesAssets(fileNames = \u0026#34;assets1.html,assets2.js\u0026#34;) 如果要导入其他jar包，需要把jar文件放在lib文件夹下，在源码中添加：\n@UsesLibraries(libraries = \u0026#34;someJarFile.jar\u0026#34;) 如果要引用so包，需要把so文件放在jni文件夹下相应的类别中，并在源码中添加（官方文档是这样写的，但是测试并不成功）：\n@UsesNativeLibraries(v7aLibraries = \u0026#34;someSoFile.so\u0026#34;) 打开命令行窗口（按win+R，运行cmd命令），执行如下命令：\nE:\\aixcompiler\\aixc.bat E:\\kevinkunaix\\QRCode 如果不想输入代码或者怕输入错误，可以直接拖动TestAIX文件夹到aixc.bat文件上，等同于执行上述命令。\n如果没有错误的话，会显示编译完成及用时。如图： 如果看不到倒数第二行的“编译完成，累计用时”这几个字，说明有错误，请仔细查看窗口显示的提示信息，修改源码。\n编译完成，会在QRCode文件夹下生成一个build文件夹，aix扩展文件会保存在\\build\\outputs文件夹下。（同时还有个md文件，这个是同步生成的aix说明文件，可以配合gitbook生成html文件。有兴趣的自行研究，这里不作说明）\n现在可以将aix导入服务器（将aix文件直接拖动到开发服务器的组件设计界面就可以导入），扩展导入后如下图。你可以对照源码，看看这些块是对应的源码中的哪一部分。 记事本编辑源码虽然简便，但是没有代码提示，不能很好的组织文档，建议使用Eclipse或者其他高级的代码编写软件。 多多查看其他的app inventor组件的源码，可以更好的了解如何写aix。\n","permalink":"https://wangsk789.github.io/aixc/","tags":null,"title":"使用Aixc编译扩展"},{"categories":null,"contents":"很多同学在制作app过程中，需要有很多的素材如图像啊、视频啊。如果都放入素材库，APP会超过服务器限制（一般是10m）。如果你没有自己的文件存储服务器，可以将文件存储在gitee上。\n准备工作 注册Gitee账号点我，建议用邮箱号注册。 登录gitee，点右上角的加号，新建仓库。 仓库名称随便写，比如repo，或者store，这个随意。 是否开源选 公开 选中 使用Readme 文件初始化仓库 点 创建，完成创建仓库。 网页拉到最底下 打开OpenAPI这个链接，或者点这里进入api文档。 依次打开左侧的 api文档–-\u0026gt; 仓库 –\u0026gt; 新建文件，这里有新建文件（也就是上传文件到这个库）的接口文档。 点右上角 申请授权，同意授权，记下在access_token的值。 其他参数有：owner是注册时的用户名，repo就是仓库名，path就是你要上传的文件名，content就是要上传的文件的base64编码，message就是git中的commit，这里可以随便写。 在owner、repo、path、content、message中随便写上a/b/c/d/e ，点测试，下面会出现这个： 根据这个curl命令，就可以写出我们的http客户端请求了 逻辑设计 主要代码有：\n向服务器发送请求 使用图像选择器选择文件，取得文件路径名，和文件名。\n拼接http客户端网址\n设置请求头\n执行post文本。文本是json格式，可以用字典创建。\n从服务器返回响应： 将响应文件转为字典\n如果响应代码=201，标明是成功上传了。\n提取字典中的download_url的值，这个就是上传的文件的连接了。（其实返回值中还有其他几个网址，经试验，只有这个download_url是文件的直接连接。）\n有了文件url，就可以图像框显示出来，或者保存到数据库。\n其他 以上方法，只适用于图片。如果是其他格式，可以通过开启Gitee Pages来获取资源网址。\n","permalink":"https://wangsk789.github.io/gitee/","tags":null,"title":"使用Gitee作为图床"},{"categories":null,"contents":"MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。\nMongoDB 将数据存储为一个文档，数据结构由键值(key=\u0026gt;value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档，数组及文档数组。\n\u0026ndash;选自菜鸟教程\n宝塔安装mongodb 点击软件商店，搜索mongodb进行安装\n找到安装好的mongodb进行设置，将配置文件bindIp改为0.0.0.0\n安全 防火墙打开端口27017\n云服务器安全组入方向开放端口27017\n浏览器输入http://公网ip:27017 出现“It looks like you are trying to access MongoDB over HTTP on the native driver port.”说明配置成功\n终端连接服务器\ncd /www/server/mongodb\rmongo\ruse admin\rdb.createUser({user:\u0026#39;root\u0026#39;,pwd:\u0026#39;root\u0026#39;,roles:[\u0026#39;root\u0026#39;]}) //这里可以使用自己的用户名和密码。\rdb.auth(\u0026#39;root\u0026#39;,\u0026#39;root\u0026#39;) //返回1成功 电脑可以安装mongodbCompass，可以在本地查看、操作mongodb的数据\n宝塔安装php7并开启网站 具体安装步骤略过 服务器要支持php，本例中需要是php7以上。 将这个php文件下载后上传到服务器，并配置好php中的连接字符串。 在浏览器中输入http://你的网址/mongodb7.php,如果显示{\u0026ldquo;error\u0026rdquo;:\u0026quot;\u0026lsquo;collection\u0026rsquo; is missing\u0026quot;}说明配置成功。 使用Web组件操作数据 请求方式： GET或者POST，推荐POST\n参数列表： 插入数据\n参数名 必填 类型 用途 举例 action yes String 请求类型 必须是insert collection yes String 表名 collection document yes jsonArray 数据 [{\u0026ldquo;id\u0026rdquo;:1,\u0026ldquo;x\u0026rdquo;:25,\u0026ldquo;y\u0026rdquo;:34},{\u0026ldquo;id\u0026rdquo;:2,\u0026ldquo;x\u0026rdquo;:52,\u0026ldquo;y\u0026rdquo;:37}] 查询数据\n参数名 必填 类型 用途 举例 action yes String 请求类型 必须是query collection yes String 表名 filter no jsonObject 符合的条件 {\u0026ldquo;x\u0026rdquo;:{\u0026quot;$gt\u0026quot;:5}}，详细见下方说明 sort no String 排序字段 -x (负号表示逆序，正序不用加符号) key no String 返回字段 -id 或者x,y 正号负号不能同时使用 limit no number 限制数据个数 10 skip no number 跳过数据个数 10 更新数据\n参数名 必填 类型 用途 举例 action yes String 请求类型 必须是update collection yes String 表名 filter yes jsonObject 符合的条件 {\u0026ldquo;x\u0026rdquo;:{\u0026quot;$gt\u0026quot;:5}} 详细见下方说明 newobj yes jsonObject 新的数据 {\u0026quot;$set\u0026quot;:{\u0026ldquo;x\u0026rdquo;:2}} 详细见下方说明 upsert no boolean 是否自动添加数据 若为真，且没有符合条件的记录时，会自动添加 删除数据\n参数名 必填 类型 用途 举例 action yes String 请求类型 必须是delete collection yes String 表名 filter yes jsonObject 符合的条件 {\u0026ldquo;x\u0026rdquo;:{\u0026quot;$gt\u0026quot;:5}} 详细见下方说明 常用查询条件filter 示例 表示意义 {\u0026ldquo;x\u0026rdquo;:2} 等于 {\u0026ldquo;x\u0026rdquo;:{\u0026quot;$lt\u0026quot;:10}} 小于 {\u0026ldquo;x\u0026rdquo;:{\u0026quot;$lte\u0026quot;:10}} 小于等于 {\u0026ldquo;x\u0026rdquo;:{\u0026quot;$gt\u0026quot;:10}} 大于 {\u0026ldquo;x\u0026rdquo;:{\u0026quot;$gte\u0026quot;:10}} 大于等于 {\u0026ldquo;x\u0026rdquo;:{\u0026quot;$ne\u0026quot;:10}} 不等于 {\u0026ldquo;x\u0026rdquo;:{\u0026quot;$gt\u0026quot;:5, \u0026ldquo;$lt\u0026rdquo;:10}} 并且 {\u0026quot;$or\u0026quot;:[{\u0026ldquo;id\u0026rdquo;:{\u0026quot;$lt\u0026quot;:5}},{\u0026ldquo;id\u0026rdquo;:{\u0026quot;$gt\u0026quot;:8}}]} 或者 {\u0026ldquo;id\u0026rdquo;:{\u0026quot;$in\u0026quot;:[1,3,5]}} 在数组中 {\u0026ldquo;id\u0026rdquo;:{\u0026quot;$nin\u0026quot;:[1,3,5]}} 不在数组中 {\u0026ldquo;name\u0026rdquo;:{\u0026quot;$regex\u0026quot;:\u0026ldquo;wang\u0026rdquo;}} 文本包含 {\u0026ldquo;name\u0026rdquo;:{\u0026quot;$regex\u0026quot;:\u0026quot;^wang\u0026quot;}} 文本以xxx开始 {\u0026ldquo;name\u0026rdquo;:{\u0026quot;$exists\u0026quot;:true}} 是否有某个字段 常用更新数据newobj 示例 意义 {\u0026quot;$set\u0026quot;:{\u0026ldquo;x\u0026rdquo;:2}} 将x的值设为2,若不存在就创建 {\u0026quot;$unset\u0026quot;:{\u0026ldquo;x\u0026rdquo;:1}} 删除某个字段 {\u0026quot;$inc\u0026quot;:{\u0026ldquo;x\u0026rdquo;:2}} 将x增加2， 只能是数字类型 {\u0026quot;$push\u0026quot;:{\u0026ldquo;x\u0026rdquo;:5}} 向x中追加一个数。x必须是数组类型 {\u0026quot;$pull\u0026quot;:{\u0026ldquo;x\u0026rdquo;:5}} 将x中等于5的值删除。 {\u0026quot;$rename\u0026quot;:{\u0026ldquo;x\u0026rdquo;:\u0026ldquo;y\u0026rdquo;}} 将字段x名称修改为y 返回数据 返回数据为json格式。\n若出错，会有error字段\n若没有出错，总有count字段和action字段\n若是query操作，还有个records字段。\n附上php的内容 \u0026lt;?php /* * Writen by: Kevinkun * Date: 26/10/2022 * Contact: wangsk789@qq.com */ //error_reporting(0); $connectString = \u0026#34;mongodb://root:root@localhost:27017\u0026#34;;//change this to your connectString try{ $manager = new MongoDB\\Driver\\Manager($connectString); $result = []; // collection if(!isset($_REQUEST[\u0026#34;collection\u0026#34;])){ $result[\u0026#34;error\u0026#34;]=\u0026#34;\u0026#39;collection\u0026#39; is missing\u0026#34;; die(json_encode($result)); } $col = $_REQUEST[\u0026#34;collection\u0026#34;]; if(strpos($col,\u0026#34;.\u0026#34;)==false){ $col = \u0026#34;db.\u0026#34; . $col; } //action $action =strtolower(isset($_REQUEST[\u0026#34;action\u0026#34;])?$_REQUEST[\u0026#34;action\u0026#34;]:\u0026#34;\u0026#34;); if ($action == \u0026#34;query\u0026#34;) { //filter $filter = isset($_REQUEST[\u0026#34;filter\u0026#34;])?$_REQUEST[\u0026#34;filter\u0026#34;]:\u0026#34;{}\u0026#34;; $filter = json_decode($filter); if(!$filter){ $result[\u0026#34;error\u0026#34;]=\u0026#34;\u0026#39;filter\u0026#39; is not a well-formated json\u0026#34;; die(json_encode($result)); } $filter = (array)$filter; if(array_key_exists(\u0026#34;_id\u0026#34;, $filter)){ $id = $filter[\u0026#34;_id\u0026#34;]; $filter[\u0026#34;_id\u0026#34;] = new \\MongoDB\\BSON\\ObjectId($id); } //sort $sort = array(); $order = isset($_REQUEST[\u0026#34;sort\u0026#34;])?$_REQUEST[\u0026#34;sort\u0026#34;]:\u0026#34;_id\u0026#34;; $order = explode(\u0026#34;,\u0026#34;,$order); foreach($order as $k){ if(substr($k,0,1)==\u0026#34;-\u0026#34;){ $sort[substr($k,1)] = -1; }else if(substr($k,0,1)==\u0026#34;+\u0026#34;){ $sort[substr($k,1)] = 1; }else{ $sort[$k] = 1; } } //keys $projection = array(\u0026#34;_id\u0026#34;=\u0026gt;0); $keys = isset($_REQUEST[\u0026#34;key\u0026#34;])?$_REQUEST[\u0026#34;key\u0026#34;]:\u0026#34;-_id\u0026#34;; $keys = explode(\u0026#34;,\u0026#34;,$keys); foreach($keys as $k){ if(substr($k,0,1)==\u0026#34;-\u0026#34;){ $projection[substr($k,1)] = 0; }else if(substr($k,0,1)==\u0026#34;+\u0026#34;){ $projection[substr($k,1)] = 1; }else{ $projection[$k] = 1; } } //limit and skip $limit = isset($_REQUEST[\u0026#34;limit\u0026#34;])?$_REQUEST[\u0026#34;limit\u0026#34;]:\u0026#34;0\u0026#34;; $skip = isset($_REQUEST[\u0026#34;skip\u0026#34;])?$_REQUEST[\u0026#34;skip\u0026#34;]:\u0026#34;0\u0026#34;; $options = [ \u0026#39;projection\u0026#39; =\u0026gt; $projection, \u0026#39;sort\u0026#39; =\u0026gt; $sort, \u0026#39;limit\u0026#39; =\u0026gt; $limit, \u0026#39;skip\u0026#39; =\u0026gt; $skip, ]; $query = new MongoDB\\Driver\\Query($filter, $options); $cursor = $manager-\u0026gt;executeQuery(\u0026#34;$col\u0026#34;, $query); $records = $cursor-\u0026gt;toArray(); $result[\u0026#34;records\u0026#34;] = $records; $result[\u0026#34;count\u0026#34;] = count($records); $result[\u0026#34;action\u0026#34;]= \u0026#34;query\u0026#34;; echo json_encode($result); }else if ($action == \u0026#34;insert\u0026#34;) { $bulk = new MongoDB\\Driver\\BulkWrite; $body = isset($_REQUEST[\u0026#34;document\u0026#34;])?$_REQUEST[\u0026#34;document\u0026#34;]:\u0026#34;[]\u0026#34;; if(strpos($body, \u0026#34;[\u0026#34;) !== 0){ $body = \u0026#34;[\u0026#34;.$body.\u0026#34;]\u0026#34;; } $body = json_decode($body); if(!$body){ $result[\u0026#34;error\u0026#34;]=\u0026#34;\u0026#39;document\u0026#39; is not a well-formated jsonArray\u0026#34;; die(json_encode($result)); } foreach ($body as $b){ if(is_object($b)){ $bulk-\u0026gt;insert($b); } } $postresult = $manager-\u0026gt;executeBulkWrite(\u0026#34;$col\u0026#34;, $bulk); $result[\u0026#34;count\u0026#34;]= $postresult-\u0026gt;getInsertedCount(); $result[\u0026#34;action\u0026#34;]= \u0026#34;insert\u0026#34;; echo json_encode($result); }else if ($action == \u0026#34;delete\u0026#34;) { //filter $filter = isset($_REQUEST[\u0026#34;filter\u0026#34;])?$_REQUEST[\u0026#34;filter\u0026#34;]:\u0026#34;\u0026#34;; $filter = json_decode($filter); if(!$filter){ $result[\u0026#34;error\u0026#34;]=\u0026#34;\u0026#39;filter\u0026#39; is not a well-formated json\u0026#34;; die(json_encode($result)); } $filter = (array)$filter; if(array_key_exists(\u0026#34;_id\u0026#34;, $filter)){ $id = $filter[\u0026#34;_id\u0026#34;]; $filter[\u0026#34;_id\u0026#34;] = new \\MongoDB\\BSON\\ObjectId($id); } $bulk = new MongoDB\\Driver\\BulkWrite; $bulk-\u0026gt;delete($filter, [\u0026#39;limit\u0026#39; =\u0026gt; 0]); $writeConcern = new MongoDB\\Driver\\WriteConcern(MongoDB\\Driver\\WriteConcern::MAJORITY, 1000); $delresult = $manager-\u0026gt;executeBulkWrite(\u0026#34;$col\u0026#34;, $bulk, $writeConcern); $result[\u0026#34;count\u0026#34;]= $delresult-\u0026gt;getDeletedCount(); $result[\u0026#34;action\u0026#34;]= \u0026#34;delete\u0026#34;; echo json_encode($result); }else if ($action == \u0026#34;update\u0026#34;) { //filter $filter = isset($_REQUEST[\u0026#34;filter\u0026#34;])?$_REQUEST[\u0026#34;filter\u0026#34;]:\u0026#34;\u0026#34;; $filter = json_decode($filter); if(!$filter){ $result[\u0026#34;error\u0026#34;]=\u0026#34;\u0026#39;filter\u0026#39; is not a well-formated json\u0026#34;; die(json_encode($result)); } $filter = (array)$filter; if(array_key_exists(\u0026#34;_id\u0026#34;, $filter)){ $id = $filter[\u0026#34;_id\u0026#34;]; $filter[\u0026#34;_id\u0026#34;] = new \\MongoDB\\BSON\\ObjectId($id); } //newobj $newobj = isset($_REQUEST[\u0026#34;newobj\u0026#34;])?$_REQUEST[\u0026#34;newobj\u0026#34;]:\u0026#34;[]\u0026#34;; $newobj = json_decode($newobj); if(!$newobj){ $result[\u0026#34;error\u0026#34;]=\u0026#34;\u0026#39;newobj\u0026#39; is not a well-formated json\u0026#34;; die(json_encode($result)); } $upsert = isset($_REQUEST[\u0026#34;upsert\u0026#34;])?$_REQUEST[\u0026#34;upsert\u0026#34;]:\u0026#34;\u0026#34;; $upsert = strtolower($upsert)==\u0026#34;true\u0026#34;?true:false; $bulk = new MongoDB\\Driver\\BulkWrite; $bulk-\u0026gt;update($filter, $newobj, [\u0026#39;multi\u0026#39;=\u0026gt;true, \u0026#39;upsert\u0026#39;=\u0026gt;$upsert]); $writeConcern = new MongoDB\\Driver\\WriteConcern(MongoDB\\Driver\\WriteConcern::MAJORITY, 1000); $updateresult = $manager-\u0026gt;executeBulkWrite(\u0026#34;$col\u0026#34;, $bulk, $writeConcern); $result[\u0026#34;count\u0026#34;]= $updateresult-\u0026gt;getModifiedCount() + $updateresult-\u0026gt;getUpsertedCount(); $result[\u0026#34;action\u0026#34;]= \u0026#34;update\u0026#34;; echo json_encode($result); }else{ $result[\u0026#34;error\u0026#34;]=\u0026#34;\u0026#39;action\u0026#39; is not recoginzed\u0026#34;; die(json_encode($result)); } }catch (MongoDB\\Driver\\Exception\\Exception $e) { $result[\u0026#34;error\u0026#34;]= $e-\u0026gt;getMessage(); die(json_encode($result)); } ?\u0026gt; ","permalink":"https://wangsk789.github.io/mongodb/","tags":null,"title":"使用MongoDB作为后端数据库"},{"categories":null,"contents":"App Inventor 要连接MySQL数据库，一般是通过在服务器上部署一个php作为连接的接口，然后app inventor中使用http客户端连接接口，获取或发送数据。\n部署php文件 将以下内容的前几行中引号内文字修改为你的参数，保存为mysql.php文件，并上传到服务器。 请注意，此代码没有充分考虑安全性，请自行进行防注入等操作。\n\u0026lt;?php $servername = \u0026#34;这里输入你的mysql地址\u0026#34;; //若php文件和mysql在同一服务器，可以写为localhost 或者 127.0.0.1 $username = \u0026#34;这里输入用户名\u0026#34;; $password = \u0026#34;这里输入密码\u0026#34;; $database = \u0026#34;这里输入数据库名\u0026#34;; if (isset($_REQUEST[\u0026#39;sql\u0026#39;])){ $con = mysqli_connect($servername,$username,$password,$database); if ($con){ $sql =$_REQUEST[\u0026#39;sql\u0026#39;]; $sql =urldecode($sql); $startwith = strtolower(substr($sql,0,6)); $actions = array(\u0026#34;select\u0026#34;, \u0026#34;insert\u0026#34;,\u0026#34;update\u0026#34;,\u0026#34;delete\u0026#34;); if (in_array($startwith,$actions)){ $result=mysqli_query($con,$sql); if($result){ $data =array(); if($startwith ==\u0026#34;select\u0026#34;){ $array = array(); $inum = 0; while ($row=mysqli_fetch_assoc($result)){ $array[$inum]=$row; $inum++; } $data[\u0026#39;result\u0026#39;] = $array; } $data[\u0026#39;affected\u0026#39;] = mysqli_affected_rows($con); if (isset($_REQUEST[\u0026#39;action\u0026#39;])){ $data[\u0026#39;action\u0026#39;] = $_REQUEST[\u0026#39;action\u0026#39;]; } $return = json_encode($data); header(\u0026#34;HTTP/1.1 200 OK\u0026#34;); echo $return; } else{ header( \u0026#34;HTTP/1.1 400 Bad Request\u0026#34; ); echo \u0026#39;{\u0026#34;error\u0026#34;:\u0026#34;sql parse failed\u0026#34;}\u0026#39;; } }else{ header( \u0026#34;HTTP/1.1 400 Bad Request\u0026#34; ); echo \u0026#39;{\u0026#34;error\u0026#34;:\u0026#34;action not recognized\u0026#34;}\u0026#39;; } }else{ header(\u0026#34;HTTP/1.1 500 Internal Server Error\u0026#34;); echo \u0026#39;{\u0026#34;error\u0026#34;:\u0026#34;connection failed\u0026#34;}\u0026#39;; } mysqli_close($con); } else{ header( \u0026#34;HTTP/1.1 400 Bad Request\u0026#34; ); echo \u0026#39;{\u0026#34;error\u0026#34;:\u0026#34;no sql specified\u0026#34;}\u0026#39;; } ?\u0026gt; 请求参数 请求接受两个参数，\n一个是sql（必填），就是你要执行的sql语句（一次请求只能有一条sql语句）。为安全起见，以上代码只支持增改删查四项动作。 一个是action (可选)，如果你有多条sql要执行，就需要多次执行post请求，可以用此参数区别sql所执行的操作，他会在返回数据中原样返回， 请求方法 推荐post方法\n返回结果 返回结果是json格式。类似于： {\r\u0026#34;result\u0026#34;: [\r{\r\u0026#34;id\u0026#34;: \u0026#34;1\u0026#34;,\r\u0026#34;xingming\u0026#34;: \u0026#34;zhang san\u0026#34;,\r\u0026#34;xingbie\u0026#34;: \u0026#34;nan\u0026#34;,\r\u0026#34;shuxue\u0026#34;: \u0026#34;89\u0026#34;,\r\u0026#34;yuwen\u0026#34;: \u0026#34;68\u0026#34;\r}\r],\r\u0026#34;affected\u0026#34;: 1,\r\u0026#34;action\u0026#34;: \u0026#34;get\u0026#34;\r} 若有错误发生，返回值中会有error字段。 若没有错误，返回值中总会有affected字段，表明返回数据或者受到影响(添加或者修改或者删除)的数据条数。 若为select操作，返回值还包含result字段，包含所有返回记录的数组。 使用Web客户端与服务器交互 ","permalink":"https://wangsk789.github.io/mysql/","tags":null,"title":"使用MySQL作为后端数据库"},{"categories":null,"contents":"MAKE TETRIX GAME FROM SCRATCH IN APP INVENTOR 2\n## Designer view In Designer view we add\none Canvas, with width 240 and height 480. six Buttons, for StartGame, MoveLeft, MoveRight, RotateCounterClockwise, RotateClockwise, RapidDrop one Clock, set the TimerEnabled to false, set Interval to 200 one Notifier component one Web component Board grid We will divide our canvas into 20 x 10 small grids. Each grid has width 24 and height 24. Each combination will occupy 4 grids.\nThe white grids are on our canvas, the gray ones are the out edge of the canvas, the combinations can not go to the gray grids.\nWe initialize our board data, we fill the gray ones with 9, white one with 0. Later, if the grid is occupied, we will modify this board_data with color index.\n9,9,9,9,9,9,9,9,9,9,9,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,0,0,0,0,0,0,0,0,0,0,9,\r9,9,9,9,9,9,9,9,9,9,9,9 the board_data is a one-dimension list.\nDraw one piece on Canvas We need a procedure to convert location to row/col.\nInitialize variable COLORS for all the combination colors.\nTo draw one small piece:\nThe girds with width 24 and height 24, but we draw the piece with only 22 x 22, that will make a gap between the pieces.\nNow,it\u0026rsquo;s time to test our code:\nif a small piece drawn on the top left corner of the canvas, it means our code is good.\nChange 13 to 250 to see if it\u0026rsquo;s at bottom left corner.\nCombinations and shapes In Tetrix game, there are total 7 types of combinations, each has 4 small pieces.\nEach combination can rotate 0/90/180/270 degree, that makes total 4 shapes of each combination.\nWe calculate the location difference between each piece to its top left location as the offset.\nWe take type L for example.\nIf we put at 19 (top left corner), this combination will occupy location 19, 31, 43, and 44. the offset to 19 is 0,12,24,25.\nThen we rotate it 90 degree counter clockwise,\nNow the offset became 2,12,13,14.\nThis offset is only determined by combination type and rotate, has nothing to do with its location.\nFor example, we move the combination 2 grids left:\nNow the top left at 17, but offset still is 0,12,24,25.\nThe complete offset table:\nWith this method, we record all the offset of all combination and rotate. Some offsets of combination (type O and type I) is same when different rotate.\n[\r[\r[0,12,24,25],[2,12,13,14],[0,1,13,25],[0,1,2,12]\r],[\r[1,13,24,25],[0,1,2,14],[0,1,12,24],[0,12,13,14]\r],[\r[0,1,13,14],[1,12,13,24],[0,1,13,14],[1,12,13,24]\r],[\r[1,2,12,13],[0,12,13,25],[1,2,12,13],[0,12,13,25]\r],[\r[0,1,2,13],[0,12,13,24],[1,12,13,14],[1,12,13,25]\r],[\r[0,1,12,13],[0,1,12,13],[0,1,12,13],[0,1,12,13]\r],[\r[0,12,24,36],[0,1,2,3],[0,12,24,36],[0,1,2,3]\r]\r] ![2023-05-10T05:48:51.png][12]\nDraw a combination Now we can generate a random combination and draw it on the canvas.\nWe need some variable to record current combination\u0026rsquo;s state: top left, rotate, type, and offset.\n17 is the center of the first row. the current offset can be looked up in the OFFSET TABLE.\nAccording to the top left and offset, we can calculate location of each piece, then draw them one by one.\nNow, time to test our code:\nYou will see random combination (with different type and rotate) show up on top of the canvas when press Start Game button.\nMove the combination Now to manipulate a combination is very easy, we just need to change its top left, or its rotate, then redraw it.\nTo move left, we minus 1 from Top left location.\nTo right, add 1 to Top left location.\nTo move down, we add 12 to Top left location.\nTo rotate it counter clockwise, just add 1 to current rotate.\nTo rotate it clockwise, minus 1 from current rotate\nDon\u0026rsquo;t forget to update the current offset after change rotate. and initialize the board data when start game.\nNow test the code, you will find the combination is moving /rotating.\nRestrain the combination on canvas But it will move out of the canvas, which need to be avoided:\nFirst we need to check if a grid/location is empty:\n(Why we need to add 1 to location?)\nThen check if all the destination locations are empty:\nModify the button click event:\nYou can change the IF block in btnDown.Click event to WHILE TEST block, this will bring down the combination to bottom directly.\nNow test your code, if the combination can be moved outside the canvas?\nCombination move down automatically It\u0026rsquo;s time to let the combination move down by itself:\nWhen clocker timer fires, we try to move the combination down. if not possible (reach bottom or blocked by other combinations), we regenerate a new one.\nNow press Start game, your combination will falling down by it self. BUT the combination do not stay at bottom.\nAdd combination to canvas When combination reach bottom, add locations occupied by the combination to the board data.\nAnd we need to draw the board data on canvas:\nDon\u0026rsquo;t forget to test your code here.\nCheck game over now we can move the combination left/right/down, rotate, and falling down automatically.\nSo when will the game over? we can set game over if the new generated combination can not be placed at location 17. If game over, disable the timer, that will stop everything.\nStart game, and do nothing, wait until you see the game over alert.\nRemove fill-up rows Now the last important step.\nWhen the combination reach bottom or other combinations, before generate a new combination, we need to check if any rows can be removed.\nWe will only need to check the rows where the combination is at.\nThen we check one by one if it\u0026rsquo;s removable. If any one piece is empty, the answer is no.\nIf possible, we remove the row and add a new row.\nModify the clock.Timer event\nNow all the main function finished.\nOTHER Next you can add some other functions like update score, show preview of next combination, better UI.\nHave fun and keep coding\u0026hellip;\n","permalink":"https://wangsk789.github.io/tetrix/","tags":null,"title":"俄罗斯方块"},{"categories":null,"contents":"封装了webview和Apache ECharts来显示图表。\n使用方法 初始化 显示柱状图 显示折线图 显示饼图 显示仪表图 更换主题 图表点击事件 图表另存为图像 图像默认保存在ASD目录中\n下载链接 cn.kevinkun.KevinkunChart-v2.aix\nchart (2).aia\n","permalink":"https://wangsk789.github.io/chart/","tags":null,"title":"图表扩展"},{"categories":null,"contents":"群里有人提出如何将城市名称按照拼音排序，这里是一个解决方案：查字典法。就是我们提前准备（几乎）所有汉字和拼音的字典，使用AI2提供的排序块排序列表。\n准备工作 这里我准备了一个字典文件（pinyin.txt），请右键另存到本地，文件来源 https://gitee.com/liqiangit/jpinyin ， 我把内容格式改为了csv格式，并按照笔画顺序排序了。 字典包含了20903个汉字。\n文件格式为\n加载字典 将上面的字典文件导入项目的素材库中。 我们使用文件管理器读取文件。 读取后将格式转为字典格式。\n这样，我们就可以查字典，找到某个汉字的拼音了：\n注意这个字典只能查询单个汉字的拼音，如果需要转换多个汉字，需要用循环转换后拼接字串而成。 有些汉字可能有多个读音，这里多个拼音用|进行了分隔。\n按映射值排序列表块 在新版的AI2中，引入了多个对列表进行操作的块，其中一个是使用列表项映射的值进行排序：\n他的作用是，将列表项转换为一个相应的值，按照这个新的值进行排序，但是返回的是原列表的内容。 比如：\n这里，他会把所有数字都除以2求余数，按照余数的大小排序，然后返回每个余数对应的原来的数的列表。\n按拼音排序 这里我们将列表项转为拼音，然后让他按照拼音排序，然后返回的还是汉字。\n假设我们有一个城市列表：\n我们要对这个列表按照拼音顺序排序。\n这样，就得到了按照城市名第一个字的拼音排序的列表。\n按笔画排序 上面我已经说过，这个字典文件，我已经按照笔画顺序排序了，所以只要找到汉字所在位置的索引，比较索引就可以按笔画顺序排序了。\n一点小bug 实际测试会发现有时候“啊”这个读音的会排在最后面，这是因为这个字典文件的拼音字母使用的是āáǎàōóǒòēéěèīíǐìūúǔù 这种元音，这些跟abcde这种进行排序是会排在后面的。 解决办法就是替换字典中的这种带读音的元音符号。 请根据情况使用以下一种替换。\npinyin.without.tone.txt\npinyin.with.number.tone.txt\n","permalink":"https://wangsk789.github.io/pinyin/","tags":null,"title":"将汉字按照拼音排序"},{"categories":null,"contents":"跟AI2有关的一些常用链接。\n论坛交流 MIT官方论坛： https://community.appinventor.mit.edu/ 站长的qq群：822311219\n扩展插件 大神Taifun整理的网上最全的扩展插件网站 https://puravidaapps.com/extensions.php\n开发服务器 MIT官方服务器 http://login.appinventor.mit.edu/login http://code.appinventor.mit.edu\n离线版下载 AI2OFFLINE https://sourceforge.net/projects/ai2offline/\n国外大神打包的离线版，最接近MIT官方原版。\n这个服务器在国外，下载慢的可以从这里下：\nhttps://share.weiyun.com/4gmFjLIO\n","permalink":"https://wangsk789.github.io/links/","tags":null,"title":"常用链接"},{"categories":null,"contents":"因为有些原因，App Inventer2中的（谷歌）地图在国内不好用。本扩展可以让你在app inventor应用中使用微软必应地图。\n更新记录 v1 - 20230227\n第一次发布 所有属性 参数 类型 意义 Center jsonArray 中心坐标 [纬度,经度] EnablePanning 逻辑 允许平移 EnableZooming 逻辑 允许缩放 Key 文本 从必应地图申请的key。到这里申请 MapType 文本 地图类型 MaxZoom 数字 最大允许缩放等级 1-19 MinZoom 数字 最小允许缩放等级 1-19 ShowLocateMeButton 逻辑 是否显示‘我的位置’按钮 ShowMapTypeSelector 逻辑 是否显示地图类型选择器 ShowScalebar 逻辑 是否显示比例尺 ShowZoomButtons 逻辑 是否显示缩放按钮 Zoom 数字 初始缩放等级 1-19 相关代码块 地图初始化 将地图载入到垂直（或者水平）布局中。初始化之前，在设计窗口设置好所有初始参数。\n导入后效果如图：\n在地图被点击时，会触发MapClicked事件。\n参数 类型 意义 location 列表 点击位置坐标 平移地图和缩放地图 参数 类型 意义 center 列表 地图中心位置坐标 zoom 数字 （1-19） 缩放级别 在地图被平移或者缩放时，会触发MapViewChanged事件\n参数 类型 意义 location 列表 点击位置坐标 center 列表 视图中心坐标 bounds 列表 视图左上坐标和右下坐标 zoom 数字 当前缩放级别 图钉按钮 参数 类型 意义 id 文本 每个图钉的id location 列表 图钉位置 options 字典 图钉属性设置 \u0026ndash;title 文本 主文本 \u0026ndash;subTitle 文本 副文本 \u0026ndash;text 文本 显示在图钉上 \u0026ndash;color 文本 图钉颜色 \u0026ndash;draggable 逻辑 是否可以拖动 \u0026ndash;icon 文本 图标文件，可以是base64 options都是可选的，甚至可以是空字典。\n图钉按钮可能引发被点击事件和被拖动事件\n参数 类型 意义 id 文本 图钉id location 列表 图钉坐标 [纬度,经度] point 列表 图钉坐标 [屏幕x，屏幕Y] 线段 id 文本 线段的id locations 列表 线段顶点坐标的列表，长度最小为2 options 字典 线段属性设置 \u0026ndash;strokeColor 文本 线段颜色 \u0026ndash;strokeThickness 数字 线宽 \u0026ndash;strokeDashArray 列表 虚实线画法 [实线，虚线，实线，虚线,\u0026hellip;] 线段被点击会引发点击事件\n参数意义同图钉按钮。\n多边形 id 文本 多边形的id locations 列表 多边形顶点坐标的列表，长度最小为3 options 字典 多边形属性设置 \u0026ndash;fillColor 文本 填充色 \u0026ndash;strokeColor 文本 边线颜色 \u0026ndash;strokeThickness 数字 边线线宽 \u0026ndash;strokeDashArray 列表 虚实线画法 [实线，虚线，实线，虚线,\u0026hellip;] 多边形点击会引发点击事件：\n参数意义同线段\n规则多边形 id 文本 多边形的id center 列表 外切圆的中心坐标 radius 数字 外切圆的半径，单位km sides 整数 边数 angle 整数 旋转度数 options 字典 同多边形 点击会引发多边形点击事件（同上）\n删除形状 根据id删除图钉、线段、多边形，或者全部删除。\n信息窗 ShowInfobox: 显示信息窗口\nHideInfobox: 隐藏信息窗口\n参数 类型 意义 location 列表 坐标 title 文本 信息窗标题 description 文本 信息窗主要文本 （可以是html文本） actions 列表 按钮文本列表。可以是空列表 点击按钮文本会引发点击事件：\n路线规划 CalculateRoute: 计算路线。计算成功会在地图上绘制路线，并引发RouteUpdated事件。\nRemoveRoute：从地图上清除路线\nlocations 列表 坐标列表，长度至少为2，中间的坐标是途经点位置 mode 文本 行进方式：driving（开车）, transit（公共交通）,truck（货车）,walking（步行） total 数字 查找到的路线数量 index 数字 当前选中路线序号 route json型文本 线路详情。原始数据，里面包含线路上的点坐标，行进文本指示等。需要用字典自己解析。 位置和坐标转换 GetAddressFrom: 坐标 [纬度，经度] 转为地址。\nGetLocationsFrom: 地址转为坐标。（可能会返回多个坐标）\n计算距离 计算两点之间的距离。\n常见问题 如何知道地图加载完成？\n可以使用MapViewChanged事件。他第一次被调用时，作为加载完成事件。\n如何画圆？\n使用CreatePolygonRegular方法，适当增加边数，可以得到一个近似圆。\n下载连接： cn.kevinkun.BingMap-v1.aix\n","permalink":"https://wangsk789.github.io/bingmap/","tags":null,"title":"必应地图扩展"},{"categories":null,"contents":"我自己使用AI2写的一些小程序.\nAPK下载预览 https://kevinkun.lanzout.com/b02vbb3aj 密码:h7dv\n购买须知： 源码仅作为学习交流之用，不得用于其他目的。 购买前请先下载APK安装，查看是否符合你的要求。数码产品性质特殊，售出概不退货。 购买源码即被认为同意本须知。 购买流程： 价格 30元/个 联系作者qq：270988017 使用方法： 源码的扩展名为.aia，不要使用压缩软件对他解压缩。\n将源码文件名称改为全部英文字母，文件名不要用中文(扩展名.aia不要改动)\n打开开发服务器（在线版或者离线版）\n依次点击左上角菜单“项目”\u0026ndash;\u0026gt;\u0026ldquo;导入项目(.aia)\u0026rdquo;\n点\u0026quot;选择文件\u0026quot;,选中你的源码文件,点\u0026quot;确定\u0026quot;，就可以导入源码并查看了\n点右上角的“逻辑设计”，可以查看代码。\n若代码是折叠的，可代码上点鼠标右键，展开代码块。\n若代码有重叠，可在空白位置点鼠标右键，选整理块。\n若代码很少或者不全，可能是源码有多个屏幕。点左上角的Screen1,选择其他屏幕查看。\n","permalink":"https://wangsk789.github.io/appmarket/","tags":null,"title":"我的应用市场"},{"categories":null,"contents":"可以实时获取手柄的方向和力度\n代码块 可以自定义背景图、按钮图、按钮大小、按钮是否自动复位、按钮允许移动方向、按钮是否吸附边缘、事件响应频率等。鼠标放在块上有相关的说明。\n实际显示效果如下\n下载链接 cn.kevinkun.joystick.aix\n","permalink":"https://wangsk789.github.io/joystick/","tags":null,"title":"游戏手柄扩展"},{"categories":null,"contents":"WheelView, like number picker, but can be any strings.\noriginal code from WheelView: 效果类似android4.0以上原生的DatePicker 18\nThanks @wildcontrol to sponsor this extension\nDemo picture: this demo used 3 extensions\nAll the blocks Download link here; aix: cn.kevinkun.WheelView.aix\ndemo: WheelView.aia\n","permalink":"https://wangsk789.github.io/wheelview/","tags":null,"title":"滚轮选择框扩展"},{"categories":null,"contents":"使用百度云的接口进行翻译。\n方法 textToTranslate：待翻译的文字，语言种类自动识别。\nlanguageToTranslateTo：需要翻译到的语言，可以是zh,en,jp,fra,it等等，具体参看这里\n事件 成功事件：返回1个参数 translation文本 失败事件：返回1个参数error文本型 属性 属性描述：设置appid\n属性描述：设置appkey\n下载 cn.kevinkun.KevinkunBaiduFanyi.aix\n","permalink":"https://wangsk789.github.io/baidufanyi/","tags":null,"title":"百度翻译扩展"},{"categories":null,"contents":"MIT的App Inventor 2服务器放在美国，国内连接慢，甚至出现可能连不上的情况。\n使用离线版就可以愉快的在本地开发了。有能力的同学，甚至可以自己修改源码，打造自己的离线版本。\n本文以在Windows系统为例。\n准备工作 首先准备以下软件并安装：\ngit\nant\nopenJDK\npython\nGoogle Cloud SDK\n如果是自己找软件下载的话注意openjdk版本只要1.8版本就可以了，更高的话不支持。\n注意下载完成后，要在环境变量中设置好路径(请百度搜索如何设置环境变量)。\n以上软件我已经保存到这里，可以直接下载安装：\nhttps://wwu.lanzout.com/b02unvsid 密码:5veg\n下载源码 MIT App Inventor 2源码的官方仓库在这里：\nhttps://github.com/mit-cml/appinventor-sources\n国内连接非常慢，我们可以使用国内的gitee网站中转下。\n这样以后我们如果对源码有修改，还可以push到gitee进行保存。\n登录gitee.com，新建仓库，将上述源码从github导入仓库。\n同样的方法，将这两个仓库fork到gitee：\nhttps://github.com/mit-cml/blockly.git\nhttps://github.com/mit-cml/closure-library.git\n这两个是编译代码用到的子模块，也需要下载到本地。\n修改gitee上appinventor-sources仓库的.gitmodules文件内的url为相应的gitee的网址： 资源管理器 打开D盘，空白地方右键，运行 git bush here\n运行以下代码下载源码：\ngit clone https://gitee.com/你的用户名/appinventor-sources.git 运行以下代码下载子模块：\ncd appinventor-sources\rgit submodule update --init 开始编译 双击运行appinventor-sources下面的buildtools.cmd，\n输入B，执行Doctor命令，查看编译源码需要的软件是否已经准备好；\n输入2，执行Make Auth Key命令；\n输入3，执行Build App Inventor命令；这步主要很长时间，最后会有提示说编译成功Success之类\n开始开发 编译完成后，继续在buildtools.cmd运行界面\n输入7，运行开发服务器，就可以在浏览器输入网址 http://localhost:8888/， 进行开发了。 点击Click Here to use your Google Account to login，然后随便输入个邮箱号，就可以进入系统。\n输入9，运行编译服务器 （如果你暂时不想编译成apk，可以先不执行这一步） 安装AI伴侣 编译成功后，在appinventor-sources\\appinventor\\build\\buildserver下可以找到AI伴侣，将他安装到手机或者安卓模拟器内，就可以实时调试了。\n","permalink":"https://wangsk789.github.io/offline/","tags":null,"title":"离线运行App Inventor"},{"categories":null,"contents":"利用宝塔设置redis，将其作为云数据库的后台数据库。\n具体步骤如下： 购买腾讯云 轻量应用服务器 (其他服务器没有测试过，应该差不多)\n记得镜像那里要选择宝塔linux面板登录服务器（没有这个镜像的，请安装linux后自行安装宝塔）\n登录服务器\n进入应用管理\n应用内软件信息 那里，点登录\n输入登录按钮前面的那段代码并回车\n复制保存出现的文本（其中有网址和密码）\n进入防火墙界面，添加规则，开放8888端口。注意不是设置防火墙模板。\n浏览器中输入刚才的网址和密码，进入宝塔\n安装LNMP\n软件商店安装redis\n打开redis设置\n进入性能调整 bind设为0.0.0.0，port修改一个其他的端口，默认6379容易被攻击，requirepass设置一个密码，保存设置\n进入设置持久化，appendonly设为开启，appendfaync设为everysec，保存设置\n进入服务，重启下服务\n进入宝塔的安全页面，启用防火墙，放行端口6379\n进入腾讯云服务器的防火墙界面，添加规则，开放6379端口\nAppInventor组件设计界面添加redis组件（云数据库），redisport为6379（或你修改的端口），redisServer输入你的云服务器ip地址，注意前后不要有http等其他字母，就只要数字和点，token是密码，useSSL不要选中\n","permalink":"https://wangsk789.github.io/diyredis/","tags":null,"title":"自建Redis后台服务器"},{"categories":null,"contents":"系统自带网络微数据库使用国外的服务器，速度太慢？数据存在别人服务器上，不安全？下面介绍自己搭建简单的网络微数据库后端。不用复杂的mySQL知识。\n准备工作 首先你要有自己的服务器，可以把相关文件上传，服务器要支持php。\n下载文件myTinyWebDB.zip，解压，上传到你的服务器。\n我是在本机测试（win10+wamp64），文件结构如下：\nindex.php文件内容如下：\n\u0026lt;?php header(\u0026#34;Content-Type: application/json\u0026#34;); $file = \u0026#34;database.json\u0026#34;; if ($_SERVER[\u0026#39;REQUEST_METHOD\u0026#39;] != \u0026#34;POST\u0026#34; || !isset($_REQUEST[\u0026#39;tag\u0026#39;])) { die(\u0026#34;Bad Request\u0026#34;); } if (isset($_REQUEST[\u0026#39;value\u0026#39;])){ $tag = trim($_REQUEST[\u0026#39;tag\u0026#39;]); $value = trim($_REQUEST[\u0026#39;value\u0026#39;]); $f = fopen($file, \u0026#39;r\u0026#39;); $data = fgets($f); fclose($f); $parsedData = json_decode($data, true); $parsedData[$tag] = $value; $f = fopen($file, \u0026#39;w\u0026#39;) or die(\u0026#34;Can\u0026#39;t open file\u0026#34;); fwrite($f, json_encode($parsedData)); fclose($f); $result = array(\u0026#34;STORED\u0026#34;, $tag, $value); echo json_encode($result); }else{ $tag = trim($_REQUEST[\u0026#39;tag\u0026#39;]); $f = fopen($file, \u0026#39;r\u0026#39;); $data = json_decode(fgets($f), true); fclose($f); if(isset($data[$tag])){ $result = array(\u0026#34;VALUE\u0026#34;, $tag, $data[$tag]); }else{ $result = array(\u0026#34;VALUE\u0026#34;, $tag, \u0026#34;\u0026#34;); } echo json_encode($result); } ?\u0026gt; database.json文件内容如下(只有一对括号)：\n{} 这里需要注意： 必须给予database.json文件写权限，否则无法保存数据。 感谢@XM提醒。\n好了，运行你的服务器，在浏览器中输入http://你的网址/index.php,回车，如果返回“Bad Request”，说明设置成功。\n逻辑设计 ![2023-05-10T04:54:29.png][3]\n这里192.168.0.104是我本机的ip，你要换成你自己的服务器地址。\nok，网络微数据库就可以跟以前一样使用了。\n","permalink":"https://wangsk789.github.io/diytinywebdb/","tags":null,"title":"自建网络微数据库后台"},{"categories":null,"contents":"应用与外界交互，使用api接口是最常见的。现在利用wxbit解释下如何获取和风天气的天气预报api。\n准备工作 和风天气网站(https://www.heweather.com)注册账号。 进入控制台，应用管理，新建应用。（貌似免费用户只能建一个免费账号。） 记住应用的key。 组件设计 组件设计界面，拖入3个可视组件：文本输入框、按钮、标签，1个非可视组件：http客户端。（这个是wxbit服务器新改名的组件，赞一个。原名叫web服务器，翻译的有些不知所云）\n这里就不放组件设计图了，太简单了。主要是介绍原理，如何美化应用不在这里讨论。\n逻辑设计 要使用api接口，就必须要看api接口文档。重要的事说3遍，看文档~~ 看文档~~ 看文档~~\n我们打开这个文档（https://dev.heweather.com/docs/api/weather），有几个关键的点要看到：\n获取方式：GET 方式\n数据格式： JSON，这里是说返回的数据是JSON格式，就是键值对格式。\n免费版的请求URL: https://free-api.heweather.net/s6/weather/{weather-type}?{parameters} 花括号中的东西就是我们要按我们的要求替换的，连带花括号也要换掉。\nweather-type参数，我们这里选forecast， 就是获取未来3天的数据\nparameters参数，就是我们要告诉接口的请求参数，这里有location就是城市名称，中文或者拼音都可以，key就是准备工作中记下的key。\n我们现在开始来获取这个api，看看有什么返回值：\n初始化一个变量，记录key 构造请求URL 特别注意的就是url网址中的forecast后面有个?， key前面有个\u0026amp;。\n？用来分割主url和后面的参数，\u0026amp;是用来分割多个参数。而每个参数都是形如参数名=参数值这样的。\n设置好网址，就可以执行GET请求（还记得上面说的获取方式是GET吧）。\n因为关系到网络的传输，速度不确定，所以get请求这个动作，有个异步的HTTP客户端.获得文本这个事件，我们就用它来接受返回的结果,并显示在调试窗口。 现在就可以连接手机，在文本框中填入城市名称，运行app看看返回值如何。（尽管文档中明确说参数如果是中文的，需要进行url编码，但是这里我们直接输入中文城市名，貌似也可以）\n在设计界面右侧的调试信息窗口，显示如图： 哇~~什么鬼？密密麻麻的。其实这就是返回的json字符串了。\n如果你的显示跟我的相差很多，比如像这样很短的：{\u0026quot;HeWeather6\u0026quot;:[{\u0026quot;status\u0026quot;:\u0026quot;invalid param\u0026quot;}]}， 说明你的参数设置有误，请检查key是不是有错误，url是不是拼接错误，也有可能是你的每月免费额度用完了。这里是具体的状态码的意义（https://dev.heweather.com/docs/refer/status-code）。\n解析返回值 这里推荐一个网站，http://www.bejson.com/ 这个网站可以将json字串格式化，方便我们看清层级关系。\n将调试窗口的信息复制（那个时间戳不要复制），在这个网站进行格式化。\n格式化后的json字符串格式如下(数据太长了，后面省略一部分)：\n{\r\u0026#34;HeWeather6\u0026#34;: [{\r\u0026#34;basic\u0026#34;: {\r\u0026#34;cid\u0026#34;: \u0026#34;CN101010100\u0026#34;,\r\u0026#34;location\u0026#34;: \u0026#34;北京\u0026#34;,\r\u0026#34;parent_city\u0026#34;: \u0026#34;北京\u0026#34;,\r\u0026#34;admin_area\u0026#34;: \u0026#34;北京\u0026#34;,\r\u0026#34;cnty\u0026#34;: \u0026#34;中国\u0026#34;,\r\u0026#34;lat\u0026#34;: \u0026#34;39.90498734\u0026#34;,\r\u0026#34;lon\u0026#34;: \u0026#34;116.4052887\u0026#34;,\r\u0026#34;tz\u0026#34;: \u0026#34;+8.00\u0026#34;\r},\r\u0026#34;update\u0026#34;: {\r\u0026#34;loc\u0026#34;: \u0026#34;2019-05-25 14:58\u0026#34;,\r\u0026#34;utc\u0026#34;: \u0026#34;2019-05-25 06:58\u0026#34;\r},\r\u0026#34;status\u0026#34;: \u0026#34;ok\u0026#34;,\r\u0026#34;daily_forecast\u0026#34;: [{\r\u0026#34;cond_code_d\u0026#34;: \u0026#34;101\u0026#34;,\r\u0026#34;cond_code_n\u0026#34;: \u0026#34;302\u0026#34;,\r\u0026#34;cond_txt_d\u0026#34;: \u0026#34;多云\u0026#34;,\r\u0026#34;cond_txt_n\u0026#34;: \u0026#34;雷阵雨\u0026#34;,\r\u0026#34;date\u0026#34;: \u0026#34;2019-05-25\u0026#34;,\r\u0026#34;hum\u0026#34;: \u0026#34;64\u0026#34;,\r\u0026#34;mr\u0026#34;: \u0026#34;00:12\u0026#34;,\r\u0026#34;ms\u0026#34;: \u0026#34;10:17\u0026#34;,\r\u0026#34;pcpn\u0026#34;: \u0026#34;0.0\u0026#34;,\r\u0026#34;pop\u0026#34;: \u0026#34;0\u0026#34;,\r\u0026#34;pres\u0026#34;: \u0026#34;998\u0026#34;,\r\u0026#34;sr\u0026#34;: \u0026#34;04:51\u0026#34;,\r\u0026#34;ss\u0026#34;: \u0026#34;19:32\u0026#34;,\r\u0026#34;tmp_max\u0026#34;: \u0026#34;35\u0026#34;,\r\u0026#34;tmp_min\u0026#34;: \u0026#34;22\u0026#34;,\r\u0026#34;uv_index\u0026#34;: \u0026#34;6\u0026#34;,\r\u0026#34;vis\u0026#34;: \u0026#34;13\u0026#34;,\r\u0026#34;wind_deg\u0026#34;: \u0026#34;185\u0026#34;,\r\u0026#34;wind_dir\u0026#34;: \u0026#34;南风\u0026#34;,\r\u0026#34;wind_sc\u0026#34;: \u0026#34;3-4\u0026#34;,\r\u0026#34;wind_spd\u0026#34;: \u0026#34;15\u0026#34;\r}, {\r...\r}, {\r...\r}]\r}]\r} json字串看起来复杂，其实只要记住2条就可以了\n碰到{ 左侧花括号的，就用在键值列表...中查找... 提取 碰到 [ 左侧方括号，就用选择列表...第...项 提取 看上面的json字串，最外面是HeWeather6, 下面是个方括号，再里面是 4个平级的键basic，update，status，daily_forecast。\n我们关心的是后面两个。如果status是ok，说明没有出错，才能提取daily_forecast。\n接下来，我们从上到下，从外到内的一层层提取第一天的最低气温和最高气温。 把返回的json字串解码为键值对列表。 获取HeWeather6 的值，注意HeWeather6 前是花括号， 用在键值列表...中查找... 提取 注意HeWeather6的值是个方括号，要用选择列表...第...项提取 判断返回的数据是不是我们想要的 如果返回的格式正确，就提取3天的天气预报数据 注意这里daily_forecast后面又是个方括号，第一项就是第一天你的数据，第二项就是第二天的数据啦。 现在就可以直接提取最低温和最高温了。 这里可以多初始化几个变量，分别记录第二天、第三天的数据。\n有了这些数据，你就可以构造你的天气app啦\n更新 20190525\n我们可以自定义一个过程，更加方便的解析json字串： listInPairs 键值对列表\nkeyPath 要提取的键的路径，键名或索引值用半角逗号隔开\n像上面的json字串，我们要提取第3天的最高气温的值，\n最外层是HeWeather6, 然后是方括号要提取第一个，keyPath就是HeWeather6,1\n再提取daily_forecast下的第3项，keyPath就是HeWeather6,1,daily_forecast,3\n在往下就可以提取tmp_max的值，keyPath就是HeWeather6,1,daily_forecast,3,tmp_max\n可以这样组织代码： 更新 20200319\nwxibt已经更新增加了字典组件，有了以下两个改进：\n上面的keypath概念，就不用自己写自定义函数（原来的过程，现在改名叫函数）了\n解析json字串不用http客户端.解析json文本了，可以使用字典内置的json转字典\n现在，提取第3天的最高气温的值，可以这样写 ","permalink":"https://wangsk789.github.io/hefeng/","tags":null,"title":"获取和风天气"},{"categories":null,"contents":"补间动画允许你以平滑的方式更改对象的属性。你只需告诉它哪些属性要更改，当补间结束运行时它们应该具有哪些最终值，以及需要的时长，补间引擎将负责计算从起始点到结束点的值。\n就是说：指定动画持续的时间，开始状态和结束状态。扩展就可以（根据插值器）自动计算中间的状态，生成动画。\n目前的问题： 不支持画布、图像精灵、球形精灵。\nWHAT\u0026rsquo;S A TWEEN A tween (from in-between) is a concept that allows you to change the values of the properties of an object in a smooth way. You just tell it which properties you want to change, which final values should they have when the tween finishes running, and how long should this take, and the tweening engine will take care of finding the intermediate values from the starting to the ending point. (from http://tweenjs.github.io/tween.js/docs/user_guide.html)\nBLOCKS Add a animator to a set. this can be used to add animator on same component with defferent properties, or same properties of defferent componnet, event can use to tween color(backgroundColor, textColor)\nparam type description component visible component NOT working with ImageSprites or Balls.任何可见组件，但不包含图像精灵和球形精灵。 property string including but not limited to X, Y, TranslationX, TranslationY, ScaleX, ScaleY, RotationX, RotationY, RotationZ, BackgroundColor, TextColor, only if the property value is a number可以更改的属性名。 values list if length is 1, that means from this property\u0026rsquo;s current value to this number. if length \u0026gt;= 2, that means, from 1st number to 2nd, to 3rd 若列表长度为1，表示从原来的属性值变为这个值。若长度大于2， 表示从第一个值变为第二个值，然后变为第三个\u0026hellip; Start/Pause/Cancel/Resume/Reverse set 开始、取消、暂停、恢复、反转动画。\nAnimation End event 动画完成事件\nScreenDensity 屏幕分辨率\nuseful when you tween the x/y/translationX/translationY 在tween位置或者大小时有用。\nProperties name type desc Duration int how long the animation last, in millis, default 500动画持续的时间 Interpolator int animation type. :1.AccelerateDecelerate, 2.Accelerate,3.Decelerate,4.Bounce, 5.Linear, 6.AnticipateOvershoot, 7.Anticipate, 8.Overshoot 动画的类型，比如加速、减速、匀速等。可以自己试验下。默认是5 匀速 IsTogether boolean true for play together, false for play one by one. 如果有多个动画，是否同时播放还是依次播放 RepeatCount int number to repeat the animation. -1 for infinity动画重复次数。-1表示无限重复。默认为0表示不重复 RepeatMode int 1 for restart, 2 for reverse重复模式。1-从新开始 2-倒叙播放 StartDelay int how many millis to delay the animation动画延迟多久后才开始。单位毫秒 DOWNLOAD LINK: 下载链接 AIX: cn.kevinkun.Tween.v4.aix\nDemo 示例 侧边栏动画 sidebar.aia 其中使用了层叠布局扩展，thanks to @VSATISH13。 in this demo, extension Overlap by VSATISH13 used .\n","permalink":"https://wangsk789.github.io/tween/","tags":null,"title":"补间动画扩展"},{"categories":null,"contents":"首先读取excel文件的base64文本，在网页浏览器中借助sheetjs，将base64转为内容文本。支持xls文件和xlsx文件。\n准备工作 下载xlsx.full.min.js, 相关文档在这里\n构造转换函数,保存为sheetReader.html。\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;sheetjs\u0026lt;/title\u0026gt; \u0026lt;script src=\u0026#34;xlsx.full.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div id = \u0026#34;data\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;script\u0026gt; function read(base64str,sheetIndex) { let basestr = base64str.substring(base64str.indexOf(\u0026#39;,\u0026#39;)+1); const wb = XLSX.read(basestr,{type:\u0026#39;base64\u0026#39;}); let csv = XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[sheetIndex-1]]); window.AppInventor.setWebViewString(csv); } \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 将以上两个文件和你的xls文件上传到素材。要读取的文件不是必须是素材，可以放在ASD中或者其他有读取权限的地方都可以。本示例是读取ASD. 我们还需要一个Base64扩展，可以把xls文件转为base64.\n相关代码 首先让网页浏览器访问转换的网页\nFileToStringDirect方法可以把文件转为base64文本\n将base64文本用read函数读取出来。read函数需要两个参数，一个是base64文本，二是工作表在工作簿中的序号。示例中是第一个工作表。\n在网页交换字符串改变事件接收读取的xls内容，应该是个csv表\n运行结果如图： 示例文件 demo.aia\n","permalink":"https://wangsk789.github.io/excel/","tags":null,"title":"读取excel文件"},{"categories":null,"contents":"贪吃蛇是一款非常经典的手机游戏，相信每个人都玩过这个游戏。下面我们利用APP INVENTOR 2来制作一款自己的贪吃蛇。\n游戏运行界面： 界面设计 界面设计比较简单，根据上图布局添加组件\n组件及属性（其他属性都默认就可以）：\n名称1 名称2 宽度 高度 窗口大小 水平对齐 间隔 画布1 320 320 固定大小 水平布局1 充满 充满 居中 上按钮 30% 充满 水平布局2 充满 充满 居中 左按钮 30% 充满 开始按钮 30% 充满 右按钮 30% 充满 水平布局3 充满 充满 居中 下按钮 30% 充满 计时器 500 游戏代码： 几个自定义过程： 将画布分成20*20的小格子（上图中粗线方框区域），每个格子高16宽16。给每个格子编号。在显示区域之外也加上一层并编号，是为了更加方便的判断蛇头是否出界。行号（列号）从0开始到21结束。 根据编号求所在行或者列，根据行和列求出所在位置编号。 几个全局变量： 蛇身位置：记录当前状态下所有身体占据的格子位置。游戏开始时，蛇身位置列表（从蛇头到蛇尾的顺序）设为（250,249,248）\n当前方向：取值为1到4。 1代表向右，2是向上，3是向左，4是向下。\n偏移列表：分别是蛇头向右、向上、向左、向下时，新位置编号相对当前位置编号的变化。 显示蛇身 我们利用画布的画线方法，根据所在的位置编号来画出一个方框，并给他不同的颜色。灰色是蛇身，黑色是蛇头，红色是鸡蛋。 线宽设为14，x1和x2分别减去15和1，是为了每个方框中间空出2个单位。 点击开始按钮，运行后的画出的效果如图： 让蛇身动起来 我们以向右移动方向为例。当前蛇身位置（250,249,248），250是蛇头位置，若向右移动，就把右边的251加入列表第一项的位置，同时把尾巴的位置248从列表中删除，这样蛇身位置就变成了（251,250,249），再移动一格，就变成了（252,251,250）. 这样，每次移动，就是把蛇身位置列表的最后一个删除，在列表第一位置插入一个新的编号。这个新的编号是根据当前的方向决定的。 蛇头新位置：根据当前方向，求出蛇头的偏移量，加上当前的位置，就是新位置。 每次计时器计时，把画布清空，重新计算蛇身位置，并根据新位置画出蛇身。 当然，现在蛇只能向右移动，且可以穿越边界。下面加上修改方向的代码\n改变方向 因为蛇不允许原地180度转向，我们加一个判断，如果方向是4（向下），那么按向上的按钮就没有变化。只有在不等于4的时候，向上按钮才起作用。其他方向同理。 禁止穿越边界 只要判断蛇头位置是否出界就可以了。如果蛇头的所在行或者列等于0或者21，就说明出界了（参考上面的编号图）。 我们在计时器的计时事件中加上一个判断，如果出界，就停止计时，并给出提示。\n显示鸡蛋。 吃到鸡蛋 将移动蛇身的过程修改一下，加上判断语句。如果蛇头的新位置等于鸡蛋位置,就是吃到鸡蛋了，就把鸡蛋重新移动一个位置，并且不要删除蛇尾的位置，就相当于把鸡蛋的位置加到了原来蛇尾的位置了。如果没有碰到鸡蛋，就要把蛇尾删掉。 碰到自己 现在基本的功能都实现了，就差一个检测是否碰到自己身体了。 继续修改 移动蛇身 过程。如果蛇身位置包含蛇头要去的新位置，说明碰到自己的身体了，就要游戏结束。 附加功能 至此所有基本功能都实现了。 有兴趣的可以自己添加以下功能：\n游戏计时， 吃到鸡蛋加分并显示， 每隔一定时间没有吃到鸡蛋，鸡蛋就变换位置， 鸡蛋新出现的位置不能出现在蛇的身体上， 添加障碍物， 修改为图像精灵版本（蛇头、蛇身、蛇尾用不同的图片代替） 这个是写的图像精灵版本的： ","permalink":"https://wangsk789.github.io/snake/","tags":null,"title":"贪吃蛇"},{"categories":null,"contents":"","permalink":"https://wangsk789.github.io/search/search/","tags":null,"title":"Search Result"}]