首页 文章 精选 留言 我的

精选列表

搜索[加密工具],共10000篇文章
优秀的个人博客,低调大师

Thunderbird 68.4.2 发布,邮件工具

Thunderbird 68.4.2已发布,该版本提供了 Thunderbird 60版的自动更新。如果您安装了 Thunderbird 的日历附件 Lightning,它将自动更新以匹配 Thunderbird的新版本。 具体更新内容如下: Changes 日历:针对深色主题调整了任务和事件树的颜色 Fixes 从 LDAP检索 S / MIME 证书失败 设置首选项 mail.imap.use_envelope_cmd 时,某些 IMAP 服务器上的地址解析崩溃 HTML 邮件的不正确转发导致 SMTP 服务器响应超时 日历:打开第二个 Thunderbird 窗口时,日历 UI 的各个部分停止工作 各种安全修复 更多详情可查看发布说明。

优秀的个人博客,低调大师

Thunderbird 68.2.0 发布,邮件工具

Thunderbird 68.2.0已经发布了,该版本主要是一个 bug 修复版本。它引入了对两个 WebExtenance API、消息显示(Message Display)和消息搜索(Message Search)的支持。 Message Display:消息显示在 3-pane 选项卡、自己的选项卡或自己的窗口中 Message Search:获取具有指定属性的所有消息,或者如果没有指定属性,则获取所有消息 其他改进内容有: 当使用电子邮件客户端的暗主题时,对未读消息有更好的视觉反馈 修复编辑邮件列表时的各种问题 修复应用程序窗口在重新启动后不保持其大小 修复从 32 位版本升级到 64 位版本时的问题 下载地址: https://www.thunderbird.net/

优秀的个人博客,低调大师

Mock工具wiremock-py

作者 | 咪咪 wiremock-py wiremock-py 是基于WireMock实现的, 使用Python批量生成不同 测试场景 下不同HTTP API的 mock 数据, 然后作为mock server快速全面地对 API 进行测试。 背景 在数澜地产应用的前端测试中, 前端一般依赖于后端的数据, 前端通过后端在网关上发布的 HTTP API 获取数据. 要对前端进行充分的测试, 理想的做法是, 等待后端部署完成, 并且在数据层直接输入不同类型的数据源, 然后前端直接调用后端发布在网关上的 API 进行测试。 然而现实的情况是, 前端和后端的开发进度不完全一致, 如果前端先开发完成了, 必须要等后端对应的 API 开发完成后才能开始测试, 而且数据层的数据也不容易构造。 为了解决这个问题, 网关平台做了简单的 mock 功能, 每个 API 可以填写一个 mock数据, 然后前端调用 API 时直接使用这个 mock数据: 这种方式下, 网关充当了mock server: 但由于大家都使用同一个网关, 一个 API 只能保存一份 mock 数据, 所以有以下一些缺点: 不同的测试场景需要不同的 mock 数据来测试, 此时需要删掉上个测试场景的 mock 数据, 再创建新场景的 mock 数据才能进行测试 不能根据测试场景来按照一定的规则动态生成 API 对应的 mock 数据 不能多人同时使用测试同一个 API时, 只能都使用同一份 mock 数据, 不能各用各的 wiremock-py 可以解决上述这些问题: wiremock-py 通过传入不同的测试场景参数来生成不同的 mock 数据, 同时不同测试场景下使用的 mock 数据可以保存起来; 生成 mock 数据时, wiremock-py 支持使用Python和js代码来动态生成 mock 数据(也支持直接使用 json 数据, 如果 mock 数据中的数据量很大, 人工手写 mock 时的数据量会很大, 使用代码生成则比较容易); 不同的测试人员使用各自自己的 mock server, 不会影响到其他测试人员的测试。 测试人员需要做的是: 确定哪些 API 需要进行 mock 以及不同测试场景下对应的 mock 规则是什么。 依赖环境 Java 1.8.0_144 Node v8.6.0 Python 3.4.3 演示 快速开始 以贸数v1.1.0版本 测试环境为例演示使用 wiremock-py 对楼层客流分布和店铺客流分布两张图分布在3种场景下的测试方法 先确定本地浏览器能过正常访问 http://mall-data.com:9012 准备 克隆代码 git clone http://git.dtwave-inc.com:30000/baomi.wbm/wiremock-py.git 安装依赖 cd wiremock-py pip install -r requirements.txt npm install mockjs 生成目录 python mock.py -g "demo" wiremock-py git:(master) python mock.py -g "demo" DEBUG:root:mockdir=, scene=, target=, proxy_port=5506, generate=demo, wiremock=False, rewrite=False DEBUG:root:正在生成目录 /Users/wangbaomi/autotest/wiremock-py/demo DEBUG:root:创建目录成功: demo DEBUG:root:创建目录成功: demo/js DEBUG:root:创建目录成功: demo/json DEBUG:root:创建目录成功: demo/python DEBUG:root:创建目录成功: demo/wiremock DEBUG:root:创建文件成功: demo/mappings.json DEBUG:root:生成目录完成: /Users/wangbaomi/autotest/wiremock-py/demo 填写 mappings.json、json、python、js 数据 mappings.json 中填写内容: [ { "response": { "default": { "proxyBaseUrl": "target" } }, "mapping_name": "request url not start with /api", "request": { "method": "ANY", "urlPattern": "/(?!api).*" } }, { "mapping_name": "楼层客流分布", "request": { "urlPattern": "/api/v1/mall_data/customer_flow/every_floor\\?(.*)", "method": "POST" }, "response": { "default": { "proxyBaseUrl": "target" }, "测试场景1": { "bodyFileName": { "json": "楼层客流分布.json" } }, "测试场景2": { "bodyFileName": { "python": "楼层客流分布.py", "python_args": "测试场景2" } }, "测试场景3": { "bodyFileName": { "js": "楼层客流分布.js" } } } }, { "mapping_name": "店铺客流分布", "request": { "urlPattern": "/api/v1/mall_data/customer_flow/every_shop\\?(.*)", "method": "POST" }, "response": { "default": { "proxyBaseUrl": "target" }, "测试场景1": { "bodyFileName": { "js": "店铺客流分布.js" } }, "测试场景2": { "bodyFileName": { "json": "店铺客流分布.json" } }, "测试场景3": { "bodyFileName": { "python": "店铺客流分布.py", "python_args": "测试场景3" } } } } ] js 文件夹中新建店铺客流分布.js文件, 内容为: var r = { "success": true, "code": null, "message": null, "content": { "meta": {}, "multi": { "group": [ { "id": "rank", "name": "排名", "value": [ 1, 2, 3, 4 ] } ], "result": [ { "id": "the_shop", "name": "店铺", "value": [ "店铺1", "店铺2", "店铺3", "第4个店铺" ] }, { "id": "customer_count", "name": "人数", "value": [ 10, 100, 1000, 3242 ] } ] }, "single": [] } }; console.log(JSON.stringify(r)); js 文件夹中新建楼层客流分布.js文件, 内容为: var r = { "success": true, "code": null, "message": null, "content": {"meta": {}, "multi": { "group": [ { "id": "the_floor", "name": "楼层", "value": [ "-1楼", "1楼", "2楼", "3楼", ] } ], "result": [ { "id": "customer_count", "name": "人数", "value": [ 100, 1000, 5000, 567 ] } ] }, "single": [] } }; console.log(JSON.stringify(r)); json 文件夹中新建店铺客流分布.json, 内容为: { "success": true, "code": null, "message": null, "content": { "meta": {}, "multi": {

优秀的个人博客,低调大师

Docker编排工具RANCHER安装

**安装要求** 对应操作系统及Docker版本、计算资源、网络端口の前置要求(本次以阿里云5台2核8G_CentOS7のECS测试) ·Ubuntu 16.04 (64-bit x86) ·Docker 17.03.x, 18.06.x, 18.09.x·Ubuntu 18.04 (64-bit x86) ·Docker 18.06.x, 18.09.x·Red Hat Enterprise Linux (RHEL)/CentOS 7.6 (64-bit x86) ·RHEL Docker 1.13·Docker 17.03.x, 18.06.x, 18.09.x ·RancherOS 1.5.1 (64-bit x86) ·Docker 17.03.x, 18.06.x, 18.09.x·Windows Server 2019 (64-bit x86) ·Docker 18.09 ·Experimental, see Configuring Custom Clusters for Windows networkNode IP Address 使用的每个节点(单节点安装,高可用性(HA)安装或群集中使用的节点)应配置静态IP。在DHCP的情况下,节点应具有DHCP保留以确保节点被分配到相同的IP。 Port Requirements Rancher nodes-入方向

优秀的个人博客,低调大师

机器学习工具、平台汇总

1. 平台和系统 TensorFlow — TensorFlow 是谷歌的第二代机器学习系统,内建深度学习的扩展支持,任何能够用计算流图形来表达的计算,都可以使用 TensorFlow PaddlePaddle — 百度研发的深度学习平台,具有易用,高效,灵活和可伸缩等特点,为百度内部多项产品提供深度学习算法支持 Apache SINGA — SINGA 是基于大型数据集训练,大型深度学习模块的常规分布式学习平台。SINGA 支持各种流行的深度学习模块 Scikit Flow — TensorFlow 的简化接口,模仿 Scikit 学习,用户可在预测分析和数据挖掘中使用 VELES — 分布式深度学习应用系统,用户只需要提供参数,剩下的都可以交给 VELES。VELES 是三星开发的另一个 TensorFlow SpeeDO — 为通用硬件

优秀的个人博客,低调大师

时间处理工具

package com.skjd.util; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; public class DateUtil { private final static SimpleDateFormat sdfYear = new SimpleDateFormat("yyyy"); private final static SimpleDateFormat sdfDay = new SimpleDateFormat( "yyyy-MM-dd"); private final static SimpleDateFormat sdfMouth = new SimpleDateFormat( "yyyy-MM"); private final static SimpleDateFormat sdfDays = new SimpleDateFormat( "yyyyMMdd"); private final static SimpleDateFormat sdfTime = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); public static final String DATE_FORMAT_FULL = "yyyy-MM-dd HH:mm:ss"; public static final String DATE_FORMAT_FULL_MSEL = "yyyyMMddHHmmssSSSS"; /** * 获取YYYY格式 * * @return */ public static String getYear() { return sdfYear.format(new Date()); } /** * 获取YYYY-MM-DD格式 * * @return */ public static String getDay() { return sdfDay.format(new Date()); } /** * 获取YYYYMMDD格式 * * @return */ public static String getDays(){ return sdfDays.format(new Date()); } /** * 获取YYYY-MM-DD HH:mm:ss格式 * * @return */ public static String getTime() { return sdfTime.format(new Date()); } /** * 传入一个时间字符串 * 判断此时间在晚上23点到凌晨6点经过了多久分钟 * YYYY-MM-DD HH:mm:ss */ public static int getNightSum( String s) { Date date = fomatDate(s); String str = sdfTime.format(date); String str2 = str.substring(11, 13); int i = Integer.parseInt(str2); if(i>=23){ return Integer.parseInt(str.substring(14, 16)); }else if(i<6){ i=6-i-1; int j=60-Integer.parseInt(str.substring(14, 16)); return i*60+j; }else { return 0; } } /** * @Title: compareDate * @Description: TODO(日期比较,如果s>=e 返回true 否则返回false) * @param s * @param e * @return boolean * @throws * @author luguosui */ public static boolean compareDate(String s, String e) { if(fomatDate(s)==null||fomatDate(e)==null){ return false; } return fomatDate(s).getTime() >=fomatDate(e).getTime(); } /** * 得到时间戳 * * @param null * @return String 当前日期时间戳(yyyyMMddHHmmssSSSS) */ public static String getTimeStamp() { try { Calendar now = Calendar.getInstance(TimeZone.getDefault()); SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_FULL_MSEL); sdf.setTimeZone(TimeZone.getDefault()); return (sdf.format(now.getTime())); } catch (Exception e) { return getCurDateTime(); // 如果无法转化,则返回默认格式的时间。 } } /** * 得到当前日期 * * @return String 当前日期 yyyy-MM-dd HH:mm:ss格式 */ public static String getCurDateTime() { Calendar now = Calendar.getInstance(TimeZone.getDefault()); // String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; DateFormat sdf = new SimpleDateFormat(DATE_FORMAT_FULL); sdf.setTimeZone(TimeZone.getDefault()); return (sdf.format(now.getTime())); } /** * 格式化日期 * * @return */ public static Date fomatDate(String date) { DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { return fmt.parse(date); } catch (ParseException e) { e.printStackTrace(); return null; } } /** * 校验日期是否合法 * * @return */ public static boolean isValidDate(String s) { DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); try { fmt.parse(s); return true; } catch (Exception e) { // 如果throw java.text.ParseException或者NullPointerException,就说明格式不对 return false; } } public static int getDiffYear(String startTime,String endTime) { DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); try { long aa=0; int years=(int) (((fmt.parse(endTime).getTime()-fmt.parse(startTime).getTime())/ (1000 * 60 * 60 * 24))/365); return years; } catch (Exception e) { // 如果throw java.text.ParseException或者NullPointerException,就说明格式不对 return 0; } } /** * <li>功能描述:时间相减得到天数 * @param beginDateStr * @param endDateStr * @return * long * @author Administrator */ public static long getDaySub(String beginDateStr,String endDateStr){ long day=0; java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd"); java.util.Date beginDate = null; java.util.Date endDate = null; try { beginDate = format.parse(beginDateStr); endDate= format.parse(endDateStr); } catch (ParseException e) { e.printStackTrace(); } day=(endDate.getTime()-beginDate.getTime())/(24*60*60*1000); //System.out.println("相隔的天数="+day); return day; } /** * 得到n天之后的日期 * @param days * @return */ public static String getAfterDayDate(String days) { int daysInt = Integer.parseInt(days); Calendar canlendar = Calendar.getInstance(); // java.util包 canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动 Date date = canlendar.getTime(); SimpleDateFormat sdfd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = sdfd.format(date); return dateStr; } /** * 得到n天之后是周几 * @param days * @return */ public static String getAfterDayWeek(String days) { int daysInt = Integer.parseInt(days); Calendar canlendar = Calendar.getInstance(); // java.util包 canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动 Date date = canlendar.getTime(); SimpleDateFormat sdf = new SimpleDateFormat("E"); String dateStr = sdf.format(date); return dateStr; } /** * 将日期对象转为时间戳字符串 * @param time * @return * @throws ParseException */ public static String dateutil(String time) throws ParseException{ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); Date date = format.parse(time); time = ""+date.getTime(); return time; } /** * 将日期对象转为时间戳 * @param time * @return * @throws ParseException */ public static Long dateutilToLong(String time) throws ParseException{ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); Date date = format.parse(time); Long time2 = date.getTime(); return time2; } public static String parseDateToStr(Date thedate) { // String format = "yyyy-MM-dd"; return parseDateToString(thedate, DATE_FORMAT_FULL); } public static String parseDateToString(Date thedate, String format) { DateFormat df = new SimpleDateFormat(format); if (thedate != null) { return df.format(thedate.getTime()); } return ""; } /** * 获取当前时间前一个月的时间 * @param args */ public static String getDateLastMouth(){ Calendar c = Calendar.getInstance(); c.setTime(new Date()); c.add(Calendar.MONTH, -1); Date m = c.getTime(); return sdfTime.format(m); } /** * 获取当月第一天的日期 * @return */ public static String getMouthNoOne(){ return sdfMouth.format(new Date())+"-01 00:00"; } public static void main(String[] args) { boolean b = compareDate("2018-08-18 00:00", DateUtil.getTime()); System.out.println(b); } }

优秀的个人博客,低调大师

Android 日志工具

1). 实现效果图 效果图.png 2). 实现思路 绘制边框 打印线程名称 打印堆栈信息 打印消息体 3). 边框绘制 边框实际上也是以字符串的形式打印 4). 打印线程信息 Log.println(priority, mTag, HORIZONTAL_LINE + "Thread: " + Thread.currentThread().getName()); 5). 打印堆栈信息 builder.append(HORIZONTAL_LINE).append("").append(level).append(element.getClassName().substring(element.getClassName().lastIndexOf(".") + 1)) .append(".").append(element.getMethodName()).append(" ").append(" (").append(element.getFileName()) .append(":").append(element.getLineNumber()).append(")"); 6). 打印消息体 // 获取消息总长度 int tLen = msg.length(); // 获取消息长度打印长度的倍数 int multiple = tLen / LENGTH + 1; // 创建临时变量 String tmp; // 遍历倍数 for (int i = 0; i < multiple; i++) { // 获取字符内容 if (i != multiple - 1) { // 获取指定范围的数据 tmp = msg.substring(i * LENGTH, (i + 1) * LENGTH); } else { // 最后一段数据 tmp = msg.substring(i * LENGTH); } // 打印消息 Log.println(priority, mTag, HORIZONTAL_LINE + tmp); } 7). 代码下载 8). Gradle依赖 compile 'com.mazaiting:log:1.0.0'

优秀的个人博客,低调大师

linux中btt工具详解

在之前的文章中介绍了如何使用blktrace 以及其工作原理和架构。我们知道blktrace 跟踪块设备的统计信息,每个CPU会有一个文件存储,然后通过blkparse可以将这些文件整合成一个文件来显示。 不过blkparse显示的文件过于庞大,而通过btt分析后会发现监控数据变得更加有意义。 1. 示例 监控获得数据 #blktrace -d /dev/sda -o sdatest 合并多个文件 #blkparse -i sdatest -d sdatest.bin #btt -i sdatest.bin 2. 输出简要信息 第一部分平均时间 IO时间主要是分为三个区域: nÂÂ 插入或合并IO到请求队列的时间,计算从块层到插入,即Q2I(Q2I=Q2G+G2I) nÂÂ 请求队列到驱动的时间,即是I2D。 nÂÂ 驱动和设备时间,即是D2C. 此外还有还有Q2Q表示IO交到块层的时间。系统等待请求的时间在Q2G中。一般情况下Q2C=Q2I+I2D+D2C,说一般情况因为有些IO会会执行merge。 把大写字母函数的表,再次方便查找: Act Description A IO was remapped to a different device B IO bounced C IO completion D IO issued to driver F IO front merged with request on queue G Get request I IO inserted onto request queue M IO back merged with request on queue P Plug request Q IO handled by request queue code S Sleep request T Unplug due to timeout U Unplug request X Split 显示所有IO的平均时间,如下 ==================== All Devices ==================== ALL MIN AVG MAX N --------------- ------------- ------------- ------------- ----------- Q2Q 0.000001946 0.091683105 2.044408235 23 Q2G 0.000000414 0.000033828 0.000674679 25 G2I 0.000000102 0.000144889 0.002673577 25 Q2M 0.000000182 0.000000856 0.000002371 4 I2D 0.000000470 0.004403646 0.005128490 16 M2D 0.000004614 0.003767183 0.005123517 4 D2C 0.000103260 0.003153379 0.043896995 20 Q2C 0.000105885 0.007446862 0.043903980 20 第二部分设备损耗 第一部分中得到设备的平均延时,第二部分得到各个阶段消耗比例得到定性分析,如下图: ==================== Device Overhead ==================== DEV | Q2G G2I Q2M I2D D2C ---------- | --------- --------- --------- --------- --------- ( 8, 0) | 0.5678% 2.4320% 0.0023% 47.3074% 42.3451% ---------- | --------- --------- --------- --------- --------- Overall | 0.5678% 2.4320% 0.0023% 47.3074% 42.3451% 第三部分设备合并信息 ==================== Device Merge Information ==================== DEV | #Q #D Ratio | BLKmin BLKavg BLKmax Total ---------- | -------- -------- ------- | -------- -------- -------- -------- ( 8, 0) | 25 25 1.0 | 1 672 2560 16808 Q表示传入的IO请求,D表示合并后发出的请求,此外还能看到平均IO块大小为672。 第四部分磁盘寻道讯息 用于显示连续队列和提交IO之间的扇区距离。NSEEKS表示提交到驱动的IO寻道次数, MEAS表示IO之间距离,MEDIA为0表示向前和向后寻道次数一样,MODE中数值表示块IO中连续的扇区,这部分比较晦涩。 包含Q2Q和D2D两部分,Q2Q是到达的IO请求之间,D2D是驱动中处理的IO.请求 ==================== Device Q2Q Seek Information ==================== DEV | NSEEKS MEAN MEDIAN | MODE ---------- | --------------- --------------- --------------- | --------------- ( 8, 0) | 24 5406419.2 0 | 0(8) ---------- | --------------- --------------- --------------- | --------------- Overall | NSEEKS MEAN MEDIAN | MODE Average | 24 5406419.2 0 | 0(8) ==================== Device D2D Seek Information ==================== DEV | NSEEKS MEAN MEDIAN | MODE ---------- | --------------- --------------- --------------- | --------------- ( 8, 0) | 25 5190101.0 0 | 0(9) ---------- | --------------- --------------- --------------- | --------------- Overall | NSEEKS MEAN MEDIAN | MODE Average | 25 5190101.0 0 | 0(9) 第五部分请求队列阻塞信息 队列不是无限的的,必然存在队列阻塞情况,这个就是现实队列阻塞,不能被驱动处理。这里的统计信息就是现实不能被处理的比例,如下图: ==================== Plug Information ==================== DEV | # Plugs # Timer Us | % Time Q Plugged ---------- | ---------- ---------- | ---------------- ( 8, 0) | 12( 0) | 0.032358934% DEV | IOs/Unp IOs/Unp(to) ---------- | ---------- ---------- ( 8, 0) | 2.1 0.0 ---------- | ---------- ---------- Overall | IOs/Unp IOs/Unp(to) Average | 2.1 0.0 第六部分队列中IO调度 有时候需要关注请求在IO调度上花费的时间。 DEV | Avg Reqs @ Q ---------- | ------------- ( 8, 0) | 4.4 详细数据 使用--all-data或-A可以显示更详细信息。 可以显示每个设备的在各个阶段的统计数据。 3. 活动数据文件 活动数据文件的格式容易被画图和分析,如下: # Total System # Total System : q activity 0.000000624 0.0 0.000000624 0.4 0.004054842 0.4 0.004054842 0.0 2.048463077 0.0 2.048463077 0.4 2.108712044 0.4 2.108712044 0.0 文件中数据是划分成对的,每对包含队列信息和完成信息。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册