首页 文章 精选 留言 我的

精选列表

搜索[创意玩法],共3061篇文章
优秀的个人博客,低调大师

Android wifi无线调试App新玩法ADB WIFI

Wifi 调试App已经不是什么新鲜的事情了,之前也看过不少,不是使用麻烦就是需要root权限,今个我给大家介绍一款好用的android studio 插件--ADB WIFI。 安装 settings->plugins->Browse repositories 搜索框输入ADB WIFI找到插件,安装,重启android Studio后可以看到 导航菜单下Tools->Android->ADB WIFI就算安装成功了。 使用 第一步:首先还是需要用数据线连接电脑,如下图: 第二步:找到ADB WIFI菜单,如下图: 第三步:运行ADB Restart(Ctrl+Alt+Shift+R)第四步:运行ADB USB to WIFI(Ctrl+Alt+Shift+W)第五步:最后拔掉数据线。 打开LogCat,看看有没有闪动的日志信息,如果有就是成功了。 另外Android WiFi ADB这个插件也很好用github地址AndroidWiFiADB 作者:dongjunkun 链接:http://www.jianshu.com/p/21d1b65d92a4 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

【云计算的1024种玩法】搭建个人博客

好吧,我承认您看了题目以后,可能就会很嫌弃的说,不就是一个博客吗,谁不会搞啊,某浪,某讯都有博客,注册一个博客谁还不会,用得着你教我嘛。您先静一静,喝口水压压惊,我们这里是帮您拥有一个属于您自己的不会冠以某浪某讯的title并且拥有更多freestyle的博客界面的博客,有点小心动的话,就去动手做一个吧。我和大家一起搭建博客平台,并发现一些小的技巧来丰富博客内容。 一、搭建一个博客平台1、 开通云服务器阿里巴巴财大气粗,对在校学生也是相当慷慨。阿里云账户完成实名认证和学生身份认证,购买云产品即可享受优惠,链接如下:https://promotion.aliyun.com/ntms/campus2017.html一定要保证已经实名认证和学生认证。 2、搭建镜像LAMP环境(Ubuntu16.04 Apache PHP7.1)第一种方法是在购买ECS是选择镜像: 第二种方法是更换镜像:强制停止服务器配置信息更换系统盘 第三种方法是直接购买:链接如下https://market.aliyun.com/products/53398003/cmjj016263.html 3、安装FileZiila和PuTTYFileZilla下载链接:https://filezilla-project.org/download.php?type=clientFileZilla从虚拟机上上传文件。 PuTTY下载地址:https://github.com/larryli/PuTTY/releases PuTTY 是自由的跨平台 Telnet/SSH 客户端,同时在 Win32 和 Unix 系统下模拟 xterm 终端。其主要作者是 Simon Tatham。4、下载WordPress并上传下载 WordPress地址:https://cn.wordpress.org/wordpress-4.8-zh_CN.zip并解压缩用FileZilla将WordPress上传至 /data/wwwroot/default/ 目录: 二、博客展示1、用阿里云服务器搭建个人博客的优势目前,除了用阿里云服务器搭建个人博客之外,常用的方法是使用hexo+githubpages搭建博客,该方法需要购买域名、安装hexo软件和配置各种参数等等;还有使用ubuntu14. 04+Nginx1.8+uWSGI来部署Django应用然后再购买域名,使用MySQL数据库,费时费力大概需要一个月左右的时间,可能还要备案一些东西,步骤繁琐,且安全性也得不到保证。阿里云以它超强的资源整合能力,以及完整的服务体系,搭建个人博客基于WordPress,从域名的申请到各种文件的配置再到最后写成第一篇博文,都有详细的解决方案,能够满足从零开始的使用者轻松快捷的使用,但是该方法提供的博文模式相对较少,后期还要开发人员多上线一些新的模板,满足不同需求的使用者。总体来说,使用阿里云搭建个人博客,简单易用,省时省力,只需要博主把精力放在博文的内容上,后台运行就交给阿里云,做一个“甩手掌柜”。2、亲身体验(1)在登录个人博客之后,点击左侧的文章---写文章,就可以开始写作了。在标题和正文的相应位置编写。 (2)内容编辑好之后,我们来预览一下,点击,预览更改,会出现预览失败的问题。这时可以点击左侧一栏中的设置----固定链接----保存更改,就解决预览失败的问题了。 (3)编写检查完成之后,点击,更新,就可以上传到主页上了 (4)登录http://外网IP,就可以看到更新的文章 (5)如果想再发文章,在首页上,点击,新建----文章,编辑完成以后,点击,发布,就完成了。 (6)还可以新建----媒体、页面和用户等等 ![_](https://yqfile.alicdn.com/ccaed6ef437fa8644eb9829b2d6c16c039be37f9.png) (7)个性化设置,下面我自己设置的,额,感觉不是很好看呀,快来发挥你的美学才华吧,把自己的博客装饰的更加多彩。 通过以上方法,可以搭建一个个人博客并且可以丰富页面内容,完善页面的功能,私人订制页面(个性化设置)等等,能够满足大部分功能需求。 更详细操作请见操作文档

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

美国智能家居产品又添新玩法

美国综合报道 最近,美国智能家居产品Nest增加了两个新的功能,一个是可以从手机上设置Nest,第二个可以在Nest增加一个账户。 不要小看这两个新的功能,这可以说是一次思维的转变。随着互联网时代的发展,人们越来越希望能够将一个设备与家庭进行有效连接,随着物联网的发展,设备制造商和运用商也不断在做出这种尝试,包括将设备与整个家庭的链接,和一个设备可以让不同人或者账号使用。 这两个功能只是一个开始。首先,设立一个家庭账户,Nest最多可以让10个人加入同一个账户,来控制家里的产品。在之前,如果你的家人,比如妻子想要控制恒温器、摄像头或者烟雾探测器,需要登录Nest的app,这样就必须要共享一个密码,让人觉得不是很方便。这个解决办法就是跟你一个电子邮件的账户,这样大家都可以都来使用这个账户了。 第二个特点就是解决了Nest可以在人离开房屋之后的工作模式。之前Nest的模式是当家里有人时,工作启动,进行温度的调节。如果将Nest装到比较偏僻的地方,无法感知人在房间时,就无法更好地工作。而这个功能添加以后,即便是人们离开了,它也能够进行判断,从而关闭加热系统。 之前有几家公司希望向Nest提供如何判断大家是否在家,以此来解决这个问题。Nest决定自己来解决,新增了一个手机地理定位的功能,如果定位在家里的话,就此可以判断在家里,如果不在家里的话,就会自动关闭加热系统。这样无论遥感器是否能够感应到你,都可以多一个判断你是否在家的工具。 Nest后期还想将这两个简单的功能进行扩展,通过家庭账户和地理定位的结合,将知道谁在家里,根据每个人的喜好,将温度调整到在家里那个人的喜好。换句话说,比如如果“我”在家的时候,Nest将根据“我”的喜好来调整温度,同时让整个照明系统也来适应“我”的需求,这将真正实现智能化。 本文转自d1net(转载)

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

极具个性创意机器人带你走入完美生活

随着节日的到来,各大厂商纷纷推出自己的新产品就不足为奇了,今天为大家带来的这个机器人叫Sakura,穿着粉色装扮的她,完全一副忧郁的表情,而且还踩着一个未来派的滑板。她可以发出一些好听的笑声,还可以播放五首不同的歌曲。除此之外,脚底的滑板可以由遥控器控制,当你们相处时间久了之后,获得了彼此之间的信任,你可以把你的小秘密告诉她哦,相信她是绝对不会泄密的。嗯,不过呢,这个漂亮的东东要花去你大约50英镑才可以买到哦。 在最近的一次发布会上,日立展出了这个叫做EMIEW 2的机器人。这个两轮的机器人可以在办公室里接受无线信号,并且可以没有任何障碍的执行命令。EMIEW 2身高31.5英尺,重29磅,当人们热情参观的时候,机器人突然变的像个木头一样静止不动了,设计者告诉观众机器人还在调试当中。然而,在通常情况下,EMIEW 2可以轻松的避开障碍物,并且可以理解和回应人类的语言,如果充一次点的话,至少可以不间断的工作一个小时。公司方面虽然还没有透露具体的价格和发布日期,但总是值得人们期待的。 这个身高1米的瓢虫机器人预计在2009年正式发行,她可以在你不在家的时候帮你清洗地板,打扫房间,在他的身上装有一个超级大的水箱,还有打扫卫生用的刷子。不仅如此,身上的障碍探测器可以避免在工作的时候碰到房间中的家具,甚至如果有人想要和他说话的话,他身上装的语音识别软件可以解决问题。不过官方没有给出机器人到底怎么打扫房屋的视频,但是如果一切顺利的话,这个小机器人将会卖到31,713美元。 原文出处:科技行者 转载请与作者联系,同时请务必标明文章原始出处和原文链接及本声明。

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

甘特图的这些新玩法,你都知道吗?

摘要:本文由葡萄城技术团队于OSCHINA发布。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 前言 甘特图是项目管理、生产排程、节点管理中非常常见的一个功能。那么,有没有一种方法能够帮助将甘特图引入到系统中,让数据的进度、排程数据的展示更加直观,让管理更加高效。 本葡萄今天为大家带来一个新的插件——甘特图插件。该插件是结合 SpreadJS 本身强大电子表格能力提供的一种全新的可视化展示方案,能够有效地将生产计划、预算排期中的日程管理信息展示出来,显示关键路径上关键节点的进展或状态。其数据来自与集算表同源的数据管理器(DataManager),通过绑定 -> 视图的方式,将层级化的任务数据通过甘特图插件进行展示。 (通过甘特图插件展示层级化的数据) 1. 缩放和时间刻度滚动 (通过甘特图插件实现缩放及刻度滚动) JavaScript代码: var myTable; var ganttSheet; var adjustTierUnit = true; window.onload = function() { var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 }); initSpread(spread); initSplitView(spread); }; function initSpread(spread) { spread.suspendPaint(); initDataSource(spread); initGanttSheet(spread); spread.resumePaint(); } function initDataSource(spread) { var tableName = "Gantt_Id"; var baseApiUrl = getBaseApiUrl(); var apiUrl = baseApiUrl + "/" + tableName; var dataManager = spread.dataManager(); myTable = dataManager.addTable("myTable", { batch: true, remote: { read: { url: apiUrl } }, schema: { hierarchy: { type: "Parent", column: "parentId" }, columns: { id: { isPrimaryKey: true }, taskNumber: { dataType: "rowOrder" } } } }); } function initGanttSheet(spread) { ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet); var view = myTable.addView("ganttView", [ { value: "taskNumber", caption: "NO.", width: 60 }, { value: "name", caption: "Task Name", width: 200 }, { value: "duration", caption: "Duration", width: 90 }, { value: "predecessors", caption: "Predecessors", width: 120 } ]); view.fetch().then(function() { ganttSheet.bindGanttView(view); }).then(function() { ganttSheet.project.timescale.zoomOut(); }); initSidePanel(ganttSheet); } 2.任务栏和网格线的样式调整 (通过甘特图插件实现的任务栏和网格线的样式) JavaScript代码: /*REPLACE_MARKER*/ /*DO NOT DELETE THESE COMMENTS*/ var myTable; var ganttSheet; window.onload = function() { var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 }); initSpread(spread); initSplitView(spread); }; function initSpread(spread) { spread.suspendPaint(); initDataSource(spread); initGanttSheet(spread); spread.resumePaint(); } function initDataSource(spread) { var tableName = "Gantt_Id"; var baseApiUrl = getBaseApiUrl(); var apiUrl = baseApiUrl + "/" + tableName; var dataManager = spread.dataManager(); myTable = dataManager.addTable("myTable", { batch: true, remote: { read: { url: apiUrl } }, schema: { hierarchy: { type: "Parent", column: "parentId" }, columns: { id: { isPrimaryKey: true }, taskNumber: { dataType: "rowOrder" } } } }); } function initGanttSheet(spread) { ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet); var view = myTable.addView("ganttView", [ { value: "taskNumber", caption: "NO.", width: 60 }, { value: "name", caption: "Task Name", width: 200 }, { value: "duration", caption: "Duration", width: 90 }, { value: "predecessors", caption: "Predecessors", width: 120, visible: false } ]); view.fetch().then(function() { ganttSheet.bindGanttView(view); ganttSheet.project.timescale.tierMode = GC.Spread.Sheets.GanttSheet.TimescaleTierMode.topMiddleBottom; ganttSheet.project.timescale.topTier.unit = GC.Spread.Sheets.GanttSheet.TimescaleUnit.thirdsOfMonth; }).then(function() { ganttSheet.gridlines.bottomTierColumn = { lineType: GC.Spread.Sheets.GanttSheet.GanttGridlineType.dashed, lineColor: "#c85b11" }; ganttSheet.gridlines.ganttRows.lineType = GC.Spread.Sheets.GanttSheet.GanttGridlineType.thin; ganttSheet.gridlines.ganttRows.lineColor = "#abd08f"; ganttSheet.gridlines.ganttRows.interval = 2; ganttSheet.gridlines.ganttRows.intervalLineType = GC.Spread.Sheets.GanttSheet.GanttGridlineType.dashDot; ganttSheet.gridlines.ganttRows.intervalLineColor = "#9cc3e5"; }); initSidePanel(ganttSheet); } function initSidePanel(ganttSheet) { var setButton = document.getElementById("set-gridline"); var intervalPanel = document.getElementById("interval-panel"); var affectItemOption = document.getElementById("affectItem"); var lineTypeOption = document.getElementById("lineType"); var lineColorOption = document.getElementById("lineColor"); var intervalLineTypeOption = document.getElementById("intervalLineType"); var intervalLineColorOption = document.getElementById("intervalLineColor"); var intervalOption = document.getElementById("interval"); var option = convertToNormalOptions(ganttSheet, "ganttRows"); syncOptionToPanel(option); affectItemOption.addEventListener('change', function(e) { var target = affectItemOption.value; if (affectItemOption.value === "ganttRows") { intervalPanel.classList.remove("hide"); } else { intervalPanel.classList.add("hide"); } var option = convertToNormalOptions(ganttSheet, target); syncOptionToPanel(option); }); setButton.addEventListener('click', function() { var target = affectItemOption.value; var lineType = Number(lineTypeOption.value); var lineColor = lineColorOption.value; var intervalLineType = Number(intervalLineTypeOption.value); var intervalLineColor = intervalLineColorOption.value; var interval = Number(intervalOption.value); var option = convertToGanttGridlineOptions(lineType, lineColor, intervalLineType, intervalLineColor, interval); if (option) { ganttSheet.gridlines[target] = option; } }); 3.数据展示布局调整 (通过甘特图插件实现布局的调整) JavaScript代码: /*REPLACE_MARKER*/ /*DO NOT DELETE THESE COMMENTS*/ var myTable; var ganttSheet; var roundBarsToWholeDays = true; window.onload = function() { var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 }); initSpread(spread); initSplitView(spread); }; function initSpread(spread) { spread.suspendPaint(); initDataSource(spread); initGanttSheet(spread); spread.resumePaint(); } function initDataSource(spread) { var tableName = "Gantt_Id"; var baseApiUrl = getBaseApiUrl(); var apiUrl = baseApiUrl + "/" + tableName; var dataManager = spread.dataManager(); myTable = dataManager.addTable("myTable", { batch: true, remote: { read: { url: apiUrl } }, schema: { hierarchy: { type: "Parent", column: "parentId" }, columns: { id: { isPrimaryKey: true }, taskNumber: { dataType: "rowOrder" } } } }); } function initGanttSheet(spread) { ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet); var view = myTable.addView("ganttView", [ { value: "taskNumber", caption: "NO.", width: 60 }, { value: "name", caption: "Task Name", width: 200 }, { value: "duration", caption: "Duration", width: 90 }, { value: "predecessors", caption: "Predecessors", width: 120, visible: false } ]); view.fetch().then(function() { ganttSheet.bindGanttView(view); ganttSheet.suspendPaint(); var summaryStyleRule = ganttSheet.project.taskStyleRules.getRule("summary"); var summaryStyle = summaryStyleRule.style.taskbarStyle; summaryStyle.leftText = "start"; summaryStyle.rightText = "finish"; summaryStyleRule.style.taskbarStyle = summaryStyle; var layout = ganttSheet.project.layout; layout.linkLineMode = "noLinks"; layout.barHeight = 18; ganttSheet.resumePaint(); }); initSidePanel(ganttSheet); } function initSidePanel(ganttSheet) { var roundBarItem = document.getElementById("round-bar"); var linkTypeItem = document.getElementById("link-type"); var barHeightItem = document.getElementById("bar-height"); var dateFormatItem = document.getElementById("date-format"); var setLayoutItem = document.getElementById("set-layout"); roundBarItem.addEventListener("click", function() { if (roundBarItem.classList.contains("active")) { roundBarItem.classList.remove("active"); roundBarsToWholeDays = false; } else { roundBarItem.classList.add("active"); roundBarsToWholeDays = true; } }); setLayoutItem.addEventListener("click", function() { var layout = ganttSheet.project.layout; ganttSheet.suspendPaint(); layout.linkLineMode = linkTypeItem.value; layout.barHeight = Number(barHeightItem.value); layout.barTextDateFormat = dateFormatItem.value; layout.roundBarsToWholeDays = roundBarsToWholeDays; ganttSheet.resumePaint(); }); } 4.任务操作 (可在甘特图插件实现任务操作) JavaScript代码: /*REPLACE_MARKER*/ /*DO NOT DELETE THESE COMMENTS*/ var myTable; var ganttSheet; var roundBarsToWholeDays = true; window.onload = function () { var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 }); initSpread(spread); initSplitView(spread); }; function initSpread(spread) { spread.suspendPaint(); initDataSource(spread); initGanttSheet(spread); spread.resumePaint(); } function initDataSource(spread) { var tableName = "Gantt_Id"; var baseApiUrl = getBaseApiUrl(); var apiUrl = baseApiUrl + "/" + tableName; var dataManager = spread.dataManager(); myTable = dataManager.addTable("myTable", { batch: true, remote: { read: { url: apiUrl }, batch: { url: apiUrl + "Collection" } }, schema: { hierarchy: { type: "Parent", column: "parentId" }, columns: { id: { isPrimaryKey: true }, taskNumber: { dataType: "rowOrder" } } } }); } function initGanttSheet(spread) { ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet); var view = myTable.addView("ganttView", [ { value: "id", caption: "Id", width: 40 }, { value: "taskNumber", caption: "NO.", width: 60 }, { value: "name", caption: "Task Name", width: 200 }, { value: "duration", caption: "Duration", width: 90 }, { value: "predecessors", caption: "Predecessors", width: 120 } ]); view.fetch().then(function () { ganttSheet.bindGanttView(view); }); initSidePanel(ganttSheet); } function initSidePanel(ganttSheet) { var addTaskItem = document.getElementById("add-task"); var addSummayItem = document.getElementById("add-summary"); var addMilestoneItem = document.getElementById("add-milestone"); var deleteItem = document.getElementById("delete-task"); var indentTaskItem = document.getElementById("indent-task"); var outdentTaskItem = document.getElementById("outdent-task"); var linkTaskItem = document.getElementById("link-task"); var unlinkTaskItem = document.getElementById("unlink-task"); var submitItem = document.getElementById("submit-changes"); var cancelItem = document.getElementById("cancel-changes"); addTaskItem.addEventListener("click", function () { ganttSheet.suspendPaint() addTask(); ganttSheet.resumePaint(); }); addSummayItem.addEventListener("click", function () { ganttSheet.suspendPaint() addSummary(); ganttSheet.resumePaint(); }); addMilestoneItem.addEventListener("click", function () { ganttSheet.suspendPaint() addMilestone(); ganttSheet.resumePaint(); }); deleteItem.addEventListener("click", function () { ganttSheet.suspendPaint() deleteTask(); ganttSheet.resumePaint(); }); indentTaskItem.addEventListener("click", function () { ganttSheet.suspendPaint() indentTask(); ganttSheet.resumePaint(); }); outdentTaskItem.addEventListener("click", function () { ganttSheet.suspendPaint() outdentTask(); ganttSheet.resumePaint(); }); linkTaskItem.addEventListener("click", function () { ganttSheet.suspendPaint() linkTask(); ganttSheet.resumePaint(); }); unlinkTaskItem.addEventListener("click", function () { ganttSheet.suspendPaint() unlinkTask(); ganttSheet.resumePaint(); }); submitItem.addEventListener("click", function () { ganttSheet.submitChanges(); }); cancelItem.addEventListener("click", function () { ganttSheet.cancelChanges(); }); function addTask() { var project = ganttSheet.project; var selections = ganttSheet.getSelections(); var insertedRow = Math.min(...selections.map(r => r.row), project.tasks.length) var rowCount = selections.map(r => r.rowCount).reduce((pre, current) => { return pre + current }); var taskData = createTaskDataList(rowCount, () => ({ name: "<新任务>" })); project.insertTasksByRow(insertedRow, taskData); } function addSummary() { var project = ganttSheet.project; var selectedRange = ganttSheet.getSelections()[0]; var insertedRow = selectedRange.row; var insertedTaskNumbers = ganttSheet.getTaskByRow(insertedRow).taskNumber || project.count; var selectedTasks = getSelectedRowIndexes().map((row) => ganttSheet.getTaskByRow(row)).filter((row) => !!row); if (selectedTasks.length == 0) { project.insertTasks(insertedTaskNumbers, [{ name: "<新摘要任务>" }, { name: "<新任务>" }]); project.indentTasks(insertedTaskNumbers + 1); } else { project.insertTasks(insertedTaskNumbers, { name: "<新摘要任务>" }, selectedTasks[0].level); project.indentTasks(selectedTasks.map((t) => t.taskNumber)); } ganttSheet.setSelection(insertedRow, -1, 1, -1); } function addMilestone() { var project = ganttSheet.project; var selections = ganttSheet.getSelections(); var rowCount = selections.map(r => r.rowCount).reduce((pre, current) => { return pre + current }); var insertedRow = Math.min(...selections.map(r => r.row), project.tasks.length); var taskData = createTaskDataList(rowCount, () => ({ name: "<新里程碑>", duration: 0 })); project.insertTasksByRow(insertedRow, taskData); ganttSheet.setSelection(insertedRow, -1, rowCount, -1); } function deleteTask() { var project = ganttSheet.project; var rowIds = getSelectedTaskNumbers(); project.removeTasks(rowIds); } function indentTask() { var project = ganttSheet.project; var rowIds = getSelectedTaskNumbers(); project.indentTasks(rowIds); } function outdentTask() { var project = ganttSheet.project; var rowIds = getSelectedTaskNumbers(); project.outdentTasks(rowIds); } function linkTask() { var project = ganttSheet.project; var links = []; var selections = ganttSheet.getSelections(); var previous = -1; for (var range of selections) { if (previous != -1) { links.push([previous, range.row]); } for (var row = range.row + 1; row < range.row + range.rowCount; row++) { links.push([row - 1, row]); } previous = range.row + range.rowCount - 1; } if (links.length <= 0) { return; } project.suspendSchedule(); for (var link of links) { var [fromTaskNumber, toTaskNumber] = link; try { project.addDependency({ fromTaskNumber, toTaskNumber }); } catch { } } project.resumeSchedule(); } 5.日历 (可在甘特图插件内实现日历内容的调整) JavaScript代码: /*REPLACE_MARKER*/ /*DO NOT DELETE THESE COMMENTS*/ var myTable; var ganttSheet; var roundBarsToWholeDays = true; window.onload = function () { var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 }); initSpread(spread); initSplitView(spread); }; function initSpread(spread) { spread.suspendPaint(); initDataSource(spread); initGanttSheet(spread); spread.resumePaint(); } function initGanttSheet(spread) { ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet); var view = myTable.addView("ganttView", [ { value: "id", caption: "Id", width: 40 }, { value: "taskNumber", caption: "NO.", width: 60 }, { value: "name", caption: "Task Name", width: 200 }, { value: "duration", caption: "Duration", width: 90 }, { value: "predecessors", caption: "Predecessors", width: 120 } ]); view.fetch().then(function () { ganttSheet.bindGanttView(view); }); initSidePanel(ganttSheet); } function initSidePanel(ganttSheet) { var addTaskItem = document.getElementById("add-task"); var addSummayItem = document.getElementById("add-summary"); var addMilestoneItem = document.getElementById("add-milestone"); var deleteItem = document.getElementById("delete-task"); var indentTaskItem = document.getElementById("indent-task"); var outdentTaskItem = document.getElementById("outdent-task"); var linkTaskItem = document.getElementById("link-task"); var unlinkTaskItem = document.getElementById("unlink-task"); var submitItem = document.getElementById("submit-changes"); var cancelItem = document.getElementById("cancel-changes"); addTaskItem.addEventListener("click", function () { ganttSheet.suspendPaint() addTask(); ganttSheet.resumePaint(); }); addSummayItem.addEventListener("click", function () { ganttSheet.suspendPaint() addSummary(); ganttSheet.resumePaint(); }); addMilestoneItem.addEventListener("click", function () { ganttSheet.suspendPaint() addMilestone(); ganttSheet.resumePaint(); }); deleteItem.addEventListener("click", function () { ganttSheet.suspendPaint() deleteTask(); ganttSheet.resumePaint(); }); indentTaskItem.addEventListener("click", function () { ganttSheet.suspendPaint() indentTask(); ganttSheet.resumePaint(); }); outdentTaskItem.addEventListener("click", function () { ganttSheet.suspendPaint() outdentTask(); ganttSheet.resumePaint(); }); linkTaskItem.addEventListener("click", function () { ganttSheet.suspendPaint() linkTask(); ganttSheet.resumePaint(); }); unlinkTaskItem.addEventListener("click", function () { ganttSheet.suspendPaint() unlinkTask(); ganttSheet.resumePaint(); }); submitItem.addEventListener("click", function () { ganttSheet.submitChanges(); }); cancelItem.addEventListener("click", function () { ganttSheet.cancelChanges(); }); function addTask() { var project = ganttSheet.project; var selections = ganttSheet.getSelections(); var insertedRow = Math.min(...selections.map(r => r.row), project.tasks.length) var rowCount = selections.map(r => r.rowCount).reduce((pre, current) => { return pre + current }); var taskData = createTaskDataList(rowCount, () => ({ name: "<新任务>" })); project.insertTasksByRow(insertedRow, taskData); } function addSummary() { var project = ganttSheet.project; var selectedRange = ganttSheet.getSelections()[0]; var insertedRow = selectedRange.row; var insertedTaskNumbers = ganttSheet.getTaskByRow(insertedRow).taskNumber || project.count; var selectedTasks = getSelectedRowIndexes().map((row) => ganttSheet.getTaskByRow(row)).filter((row) => !!row); if (selectedTasks.length == 0) { project.insertTasks(insertedTaskNumbers, [{ name: "<新摘要任务>" }, { name: "<新任务>" }]); project.indentTasks(insertedTaskNumbers + 1); } else { project.insertTasks(insertedTaskNumbers, { name: "<新摘要任务>" }, selectedTasks[0].level); project.indentTasks(selectedTasks.map((t) => t.taskNumber)); } ganttSheet.setSelection(insertedRow, -1, 1, -1); } function addMilestone() { var project = ganttSheet.project; var selections = ganttSheet.getSelections(); var rowCount = selections.map(r => r.rowCount).reduce((pre, current) => { return pre + current }); var insertedRow = Math.min(...selections.map(r => r.row), project.tasks.length); var taskData = createTaskDataList(rowCount, () => ({ name: "<新里程碑>", duration: 0 })); project.insertTasksByRow(insertedRow, taskData); ganttSheet.setSelection(insertedRow, -1, rowCount, -1); } function deleteTask() { var project = ganttSheet.project; var rowIds = getSelectedTaskNumbers(); project.removeTasks(rowIds); } function indentTask() { var project = ganttSheet.project; var rowIds = getSelectedTaskNumbers(); project.indentTasks(rowIds); } function outdentTask() { var project = ganttSheet.project; var rowIds = getSelectedTaskNumbers(); project.outdentTasks(rowIds); } function linkTask() { var project = ganttSheet.project; var links = []; var selections = ganttSheet.getSelections(); var previous = -1; for (var range of selections) { if (previous != -1) { links.push([previous, range.row]); } for (var row = range.row + 1; row < range.row + range.rowCount; row++) { links.push([row - 1, row]); } previous = range.row + range.rowCount - 1; } if (links.length <= 0) { return; } project.suspendSchedule(); for (var link of links) { var [fromTaskNumber, toTaskNumber] = link; try { project.addDependency({ fromTaskNumber, toTaskNumber }); } catch { } } project.resumeSchedule(); } function unlinkTask() { var project = ganttSheet.project; var taskNumbers = getSelectedTaskNumbers(); var dependencies = []; if (taskNumbers.length == 1) { var task = project.getTaskByRow(taskNumbers[0]); dependencies = [...task.predecessorDependencies, ...task.successorDependencies]; } else { var taskNumberSet = {}; for (var taskNumber of taskNumbers) { taskNumberSet[taskNumber] = true; } dependencies = project.dependencies.filter(dp => taskNumberSet[dp.from.taskNumber] && taskNumberSet[dp.to.taskNumber]); } if (dependencies.length >= 1) { project.removeDependency(dependencies); } } function getSelectedRowIndexes() { var rows = []; var selections = ganttSheet.getSelections(); for (var range of selections) { for (var row = range.row; row < range.row + range.rowCount; row++) { rows.push(row); } } return rows; } 6.导入和导出操作 甘特图插件中展示的内容导出至PDF及Excel的格式。 (支持导入导出能力) JavaScript代码: /*REPLACE_MARKER*/ /*DO NOT DELETE THESE COMMENTS*/ var myTable; var ganttSheet; var roundBarsToWholeDays = true; window.onload = function() { var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 }); initSpread(spread); initSplitView(spread); }; function initSpread(spread) { spread.suspendPaint(); initDataSource(spread); initGanttSheet(spread); spread.resumePaint(); } function initDataSource(spread) { var tableName = "Gantt_Id"; var baseApiUrl = getBaseApiUrl(); var apiUrl = baseApiUrl + "/" + tableName; var dataManager = spread.dataManager(); myTable = dataManager.addTable("myTable", { batch: true, remote: { read: { url: apiUrl } }, schema: { hierarchy: { type: "Parent", column: "parentId" }, columns: { id: { isPrimaryKey: true }, taskNumber: { dataType: "rowOrder" } } } }); } function initGanttSheet(spread) { ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet); var view = myTable.addView("ganttView", [ { value: "taskNumber", caption: "NO.", width: 60 }, { value: "name", caption: "Task Name", width: 200 }, { value: "duration", caption: "Duration", width: 90 }, { value: "predecessors", caption: "Predecessors", width: 120, visible: false } ]); view.fetch().then(function() { ganttSheet.bindGanttView(view); }); initSidePanel(spread, ganttSheet); } function initSidePanel(spread, ganttSheet) { var excelIo = new GC.Spread.Excel.IO(); var fileInput = document.getElementById("file-input"); var openFileItem = document.getElementById("open-file"); var fileNameItem = document.getElementById("file-name"); var exportJSONItem = document.getElementById("export-to-json"); var exportSJSItem = document.getElementById("export-to-sjs"); var exportExcelItem = document.getElementById("export-to-excel"); var printItem = document.getElementById("print"); var pdfItem = document.getElementById("pdf"); var exportFileName = "export"; openFileItem.addEventListener('click', function() { fileInput.click(); }); fileInput.addEventListener('change', function(e) { fileNameItem.value = 'Loading file...'; var file = fileInput.files[0]; if (!file) { alert("Upload a file first."); return; } var fileName = file.name; var suffix = fileName.substr(fileName.lastIndexOf('.')); if (suffix === '.xlsx' || suffix === '.xlsm') { // here is excel IO API excelIo.open(file, function(json) { spread.fromJSON(json); fileNameItem.value = fileName; fileInput.value = ''; }, function(e) { // process error console.log(e); }); } else if (suffix === '.ssjson' || suffix === '.json') { var reader = new FileReader(); reader.onload = function() { var spreadJson = JSON.parse(this.result); spread.fromJSON(spreadJson); fileNameItem.value = fileName; fileInput.value = ''; }; reader.readAsText(file); } else if (suffix === '.sjs') { spread.open(file, () => { fileNameItem.value = fileName; fileInput.value = ''; }, (e) => { console.log(e); }, { openMode: GC.Spread.Sheets.OpenMode.normal, fullRecalc: true, }); } }); exportJSONItem.addEventListener('click', function () { var fileName = exportFileName + ".ssjson"; var json = spread.toJSON(); saveAs(new Blob([JSON.stringify(json)], { type: "text/plain;charset=utf-8" }), fileName); }); exportSJSItem.addEventListener('click', function () { var fileName = exportFileName + ".sjs"; spread.save(function (blob) { saveAs(blob, fileName); }, function (e) { // process error console.log(e); }, { fileType: GC.Spread.Sheets.FileType.ssjson, includeBindingSource: true, saveAsView: true }); }); exportExcelItem.addEventListener('click', function () { var fileName = exportFileName + ".xlsx"; var json = spread.toJSON({ includeBindingSource: true, saveAsView: true }); // here is excel IO API excelIo.save(json, function(blob) { saveAs(blob, fileName); }, function(e) { // process error console.log(e); }, { xlsxStrictMode: false }); }); printItem.addEventListener('click', function() { spread.print(); }); pdfItem.addEventListener('click', function() { spread.savePDF(function (blob) { var url = URL.createObjectURL(blob); pwin = window.open(url); }); }); } 总结 甘特图是一种强大的项目管理工具,通过直观的时间轴展示任务的安排和进度。它能帮助项目经理们创建详细的项目计划,有效跟踪进度,并合理分配资源。甘特图还可以管理任务间的依赖关系,及时调整时间表,并识别风险与关键路径。它在项目管理中扮演着重要的角色,提高了团队的协作效率和项目的成功率。通过使用甘特图,项目团队能够更好地掌握项目进展,作出决策,并确保项目按时交付。 如果您对该插件感兴趣,可以访问葡萄城官网下载SpreadJS产品。

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

WEB安全新玩法 [4] 防护邮箱密码重置漏洞

大部分具有账号系统的应用都会提供重置用户登录密码的功能,常见方式之一是:用户输入自己的邮箱地址或手机号,应用向这个邮箱或手机号发送验证码,用户将收到的验证码输入应用中即可完成密码重置。这一过程容易因设计不周全而被恶意利用。**iFlow 业务安全加固平台**可以为设计不当的应用打上动态虚拟补丁,使之防御可能的恶意利用。 ----- 以某网站为例,其邮箱密码重置功能就存在缺陷:获取验证码的邮箱和重置密码的邮箱可以不一致。恶意操作能够给任意邮箱所代表的用户设置新的登录密码,从而冒充受害者登录。当然,我们也会介绍如何在不修改网站源代码的前提下,使用 **iFlow** 实现业务逻辑缺陷的修补。 # 一、原始网站 ## 1.1 正常用户访问 在密码重置页面,正常用户「alice」在**手机/邮箱**中输入自己的邮箱地址,如 *alice@mail.com*,点击**获取验证码**按钮。 ![图1](https://img-blog.csdnimg.cn/2020091615281469.png#pic_center) 网站为避免邮件发送功能被滥用,弹出图形码进行验证。用户正确填写字符并**确认**后,网站系统后台发送邮件验证码到用户「alice」的邮件地址 *alice@mail.com* 中。 ![图2](https://img-blog.csdnimg.cn/20200916154337178.png#pic_center) 用户进入到邮件系统中收取寄给 *alice@mail.com* 的邮件,将邮件中的**验证码**和需要重置的**登录密码**填写到表单中并提交。 ![图3](https://img-blog.csdnimg.cn/20200916154554640.png#pic_center) 网站判断用户输入的邮箱验证码是正确的,就将 *alice@mail.com* 所代表的用户的登录密码设置为新的密码,操作成功。 各个实体的交互流程如下: ![chart1.png](https://s2.51cto.com/images/20210628/1624868875738925.png) ## 1.2 恶意访问 此处,网站在重置密码的业务处理上有个逻辑缺陷:并未确保发送邮箱验证码时的邮箱地址和设置新密码时的邮箱地址是一致的。 缺陷利用方法如下:在前面的步骤中,恶意访问者「mallory」使用自己的邮箱 *mallory@mail.com* 获取验证码。 ![图4](https://img-blog.csdnimg.cn/2020091615515810.png#pic_center) 在收到邮箱验证码并正确填写后,「mallory」将表单中的**手机/邮箱**内容改为 *alice@mail.com* (之前填的是 *mallory@mail.com* ),然后再填写新的登录密码并提交确认。 ![图5](https://img-blog.csdnimg.cn/20200916155238567.png#pic_center) 网站判断用户输入的邮箱验证码是正确的,就将 *alice@mail.com* 所代表的用户的登录密码设置为新的密码。如此一来,「mallory」成功重设了受害者「alice」的密码,并可以使用「alice」的身份登录网站。 各个实体的交互流程如下: ![chart2.png](https://s2.51cto.com/images/20210628/1624869324214753.png) # 二、iFlow虚拟补丁后的网站 我们在 Web 服务器前部署 **iFlow 业务安全加固平台**,它有能力拦截、计算和修改双向 HTTP 报文并具备存储能力,成为 Web 应用的虚拟补丁。在本例中,iFlow 可保存发送验证码时的邮箱地址,并在之后设置密码时将其与输入的邮箱地址作对比,发现并制止篡改行为。 ## 2.1 正常用户访问 iFlow 在图形验证码通过时,将请求中的邮箱地址保存在 IP 存储中,在设置新密码时进行以下检查: 1) 该 IP 进行过发送验证码的操作; 2) 该 IP 要设置密码的邮箱地址与发送验证码时的邮箱相同。 各个实体的交互流程如下: ![chart3.png](https://s2.51cto.com/images/20210628/1624869350745877.png) ## 2.2 恶意访问 如前所示,在收到邮箱验证码后,且未提交重设密码之前,「mallory」修改了邮箱地址。iFlow 会检查到这个变化,依此可判断这是一个恶意访问,于是终止此过程,不会让系统后端真正去修改受害者的登录密码。 恶意操作的 HTTP 协议交互过程如下: ![chart4.png](https://s2.51cto.com/images/20210628/1624869439964709.png) ## 2.3 代码 **iFlow 内置的 W2 语言是一种专门用于实现 Web 应用安全加固的类编程语言**。它介于配置和通用语言之间,具备编程的基本要素和针对 HTTP 协议的特有扩展,能为业务系统编写涉及复杂判断和动态修改的逻辑。 考虑到安全产品的使用者通常为非程序员,他们习惯面对配置文件而非一段代码。因此,W2 语言虽包含语言要素,仍以规则文件方式呈现,并采用可以体现层次结构和方便词法校验的 JSON 格式。 用 W2 语言实现上述虚拟补丁的代码如下: ```json [ { "if": [ "streq(REQUEST_FILENAME, '/shopx/index.php')", "streq(@ARGS.s, '/index/user/forgetpwdverifysend.html')" ], "then": { "if": "REAL_IP.the_mail", "then": { "verdict": { "action": "deny", "log": "There is a user having reset password." } }, "else": { "if": "contain(RESPONSE_BODY, '\"code\":0,')", "then": { "execution": "REAL_IP.the_mail@120=@ARGS.accounts" } } } }, { "if": [ "streq(REQUEST_FILENAME, '/shopx/index.php')", "streq(@ARGS.s, '/index/user/forgetpwd.html')" ], "then": { "if": { "or": [ "!REAL_IP.the_mail", "strne(@ARGS.accounts, REAL_IP.the_mail)" ] }, "then": { "verdict": { "action": "deny", "log": "Invalid ${@ARGS.accounts} access" } }, "else": { "execution": "REAL_IP.the_mail=null" } } } ] ``` 示例代码中有两条规则,分别作用如下: **第一条规则** 当服务器返回图形验证结果时,iFlow 拦截此响应。如果在同一个访问端 IP 上已经在进行重置密码操作,则终止这次操作 (避免重复及交叉操作);否则当验证结果为通过时,在访问者 IP (REAL_IP) 中创建存储变量 `the_mail` ,其值为用户输入邮箱地址。 **第二条规则** 当浏览器请求确认重置密码时,iFlow 拦截此请求。iFlow检查请求参数 `accounts` 与访问者 IP (REAL_IP) 中存储变量 `the_mail` 是否相等:如果相等则清除 `the_mail` ,以开放此访问者 IP 上的重置密码业务;如果不相等则阻止该用户的继续操作。 >**注意**:上述会话中的 `the_mail` 是保存在服务器端的 **iFlow** 存储中的,***者在浏览器端是看不到数据更无法进行修改的。 # 三、总结 iFlow 使用两条规则在不修改服务器端代码的前提下,透明地保证了重置密码时邮箱地址的一致性。从这个例子中我们可以看到,iFlow 适合构造前后报文相关联的复杂防护逻辑。(张戈 | 天存信息)

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

进阶玩法:针对ECS新购选型场景查询可用资源

接口名称(查询某一可用区的资源列表) DescribeAvailableResource 背景: 选购云服务ECS时,在指定条件下有哪些资源是可用的?如何更好的选择所需的资源?针对用户的ECS购买选型,介绍如何利用DescribeAvailableResource接口查询可用资源 针对选型场景做相应的查询 场景1. 用户在杭州地域下,包年包月(包含按周)ecs实例可以售卖实例规格列表场景2. 用户在杭州地域下,按量ecs实例可以售卖实例规格列表场景3. 用户在杭州地域下,Spot实例可以售卖实例规格列表场景4. 用户选购包年包月ecs.gn4.8xlarge规格的实例,在杭州地域下那个可用区可售卖场景5. 用户在杭州可用区E下,带vpc网络包年包月实例可以售卖实例规格列表 除了您现在看到的这文章,您还可以前往: DescribeAvailabl

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

让 Chrome 浏览器崩溃的新玩法来啦!

首先,你要有一部 Android 手机,其次你要安装一个Chrome。 接下来,你只要在 Chrome 地址栏输入“chrome://editbookmark”,就会发现调皮的Chrom已经自动罢工啦。 这个bug是由推特用户@oldfresher首先发现的。早在九月,雷锋网就报道了让Chrome浏览器崩溃的招数:胆小勿点!这16个字符可以导致Chrome崩溃。今天这个,可以算是玩坏Chrome的新方法。 不过,根据雷锋网(公众号:雷锋网)亲测,谷歌似乎已经火速修复了这个bug。但是无聊的小伙伴不要灰心,目前还可以通过作者提供的链接来间接触发崩溃动作,在谷歌注意到这个问题之前,还可以有时间调戏 Chrome 一下。时间有限,好奇的小伙伴快来试试吧~ 本文作者: 史中 本文转自雷锋网禁止二次转载, 原文链接

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

【云计算的1024种玩法】打造个人专属网盘

作为一个学生,需要经常看文献,积累知识,可是论文多了以后就有了论文保存和分类的相关问题。因此需要一个个人网盘存储论文,这样就可以做到通过在线登录个人网盘,实现对网盘文档的在线阅读和管理。 然而在自己的电脑上搭建网盘平台就得给自己的电脑购买一些好的配置以保证网盘使用的流畅度,有时还会有系统崩溃的风险,这些潜在的风险处理起来还是比较麻烦的。 所以我看中了阿里云ECS,只需要网页就可以登录个人网盘并且轻松做到对个人网盘的管理,ECS建立起来简单方便,玩起来更方便,而且以后也可以重装镜像将ECS用于其它功能。下面介绍下如何在阿里云ECS上搭建服务器使用网盘的一些使用体验,接下来就简要介绍一下: 一. 搭建服务器使用网盘 1、首先我们得开通云服务器 学生用户可以在阿里云官网上进行学生认证后购买一定配置的ECS,仅需9.9元每月,学生用户无需担心

资源下载

更多资源
Mario

Mario

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

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

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部分的功能。

用户登录
用户注册