由于咱们的项目是2C的,而XP系统是最大的用户量占比,因此只能使用nw开发而不能用Electron,本文谨记开发nw过程当中遇到的各类问题以及解决方案html
nw.Window.open
打开新窗口API中的参数option中position
字段只能指定为center
或mouse
。如字面含义:center
为屏幕正中央,mouse
为鼠标当前位置。
几乎能够推测,nw的鼠标右键菜单应该也是使用此接口,明显是为了弹出右键菜单用的,除此以外几乎没有别的应用场景能够用到新打开窗口在鼠标位置的。
因此,在nw的打开新窗口功能中,其实能够说 只能显示在屏幕正中央。node
nw.Window.open('http://xxcanghai.cnblogs.com/', { //打开新窗口的参数Option width:500, height:500, show:true,//是否显示新窗口 position:"center"//新窗口显示位置,只能使用center或mouse }, function(new_win) { console.log('已打开新窗口'); });
官网对position的描述:git
position
{String}
benull
orcenter
ormouse
, controls where window will be putgithub
nw.Window.open文档:http://docs.nwjs.io/en/latest/References/Window/#windowopenurl-options-callback
Window Subfields窗口属性position字段文档:http://docs.nwjs.io/en/latest/References/Manifest%20Format/#positionchrome
一句话就是,先open一个隐藏窗口,以后在callbcal里面再重设其位置,再显示出来shell
详细步骤:
一、从新封装nw.Window.open方法,在原有的position字段上扩充四个属性,分别是 左上角,左下角,右上角,右下角,这里使用枚举对象来定义。npm
/** * 扩充打开新窗口参数 * * @export * @interface openWindowOption * @extends {NWJS_Helpers.WindowOpenOption} */ export interface openWindowOption extends NWJS_Helpers.WindowOpenOption { /** * 控制打开的新窗口的所在位置 */ position?: "left_top" | "left_bottom" | "right_top" | "right_bottom" | "center" | "mouse" | null; } /** * 打开一个新窗口 * * @export * @param {string} url 新窗口的url * @param {openWindowOption} [option] 新窗口的参数 * @param {(new_win?: NWJS_Helpers.win) => void} [callback] 打开成功后的回调函数,返回新窗口的nwWindow对象 */ export function openWindow(url: string, option: openWindowOption = {}, callback: (new_win?: NWJS_Helpers.win) => void = function () { }) { /** 新增支持的窗口位置值,左上角,左下角,右上角,右下角 */ enum winPositionEnum { left_top = "left_top", left_bottom = "left_top", right_top = "right_top", right_bottom = "right_bottom" }; /** 新窗口位置的4个英文字符串数组 */ const winPosiEnumArr: string[] = Object.keys(winPositionEnum); }
二、虽然扩充了默认position属性,但真正传给nw的还得是他支持的,因此增长判断若是使用的是新增字段,则保存用户自定义设置,同时改写option参数。
除此以外,由于要统一隐藏窗口,因此还要改写默认的show属性,保存用户设定的是否显示窗口,隐藏打开窗口后,当设定完位置后再手动设置用户初始设定的show选项。windows
option = Object.assign(<openWindowOption>{ show: true }, option); /** 用户传过来的窗口位置参数字符串 */ var optPosi: string = ""; //若是用户传过来的position参数为我自定义的,则移除原有值 if (typeof option.position === "string" && winPosiEnumArr.indexOf(option.position) >= 0) { optPosi = option.position; delete option.position; } /** 用户传过来的窗口是否隐藏选项 */ var optShow: boolean = option.show; option.show = false;
三、执行真正的nw.Window.open,同时在打开后的callback中根据自定义位置选项从新设定窗口位置。
最后再还原用户本来设定的show属性,以及触发用户本来传进来的callback回调函数。api
nw.Window.open(url, option, function (nwWin: NWJS_Helpers.win) { /** 打开的隐藏窗口的宽度和高度 */ var { width, height } = nwWin; /** 获取第一个显示器对象 */ const screen: NWJS_Helpers.screen = nw.Screen.screens[0]; /** 获取显示器的可用工做区域 */ const area = screen.work_area; /** nw的chrome壳子的四个边框高度 */ const border = { left: 5 * screen.scaleFactor, right: 5 * screen.scaleFactor, top: 24 * screen.scaleFactor, bottom: 5 * screen.scaleFactor } if (optPosi.length > 0) { let x: number = nwWin.x; let y: number = nwWin.y; if (option.frame == undefined || option.frame == true) { width += border.left + border.right; height += border.top + border.bottom; } if (optPosi == winPositionEnum.left_top) { x = 0; y = 0; } else if (optPosi == winPositionEnum.left_bottom) { x = 0; y = area.height - height; } else if (optPosi == winPositionEnum.right_top) { x = area.width - width; y = 0; } else if (optPosi == winPositionEnum.right_bottom) { x = area.width - width; y = area.height - height; } nwWin.x = Math.round(x); nwWin.y = Math.round(y); } //还原用户默认设定是否显示窗口 if (optShow) { nwWin.show(); } //触发用户传进来的callback return callback.apply(this, Array.prototype.slice.call(arguments)); });
四、由于我只须要在四个角显示,因此只扩充了4个枚举类型,若是须要在指定坐标(x,y)显示窗口,以上同理增长对position的对象类型{x:number,y:number}
检测处理便可。数组
nwjs官方提供了有关Shell相关的API,提供了简单桌面相关操做的接口。之所说他简单,是由于简直太太太简单甚至寒酸了,只有3个API分别是:
Shell.openExternal(uri)
打开外部连接;
Shell.openItem(file_path)
使用系统默认打开方式打开文件;
Shell.showItemInFolder(file_path)
和在资源管理器中显示某文件
官方文档:http://docs.nwjs.io/en/latest/References/Shell/
// Open URL with default browser.
nw.Shell.openExternal('https://github.com/nwjs/nw.js');
其中openExternal
接口可使用系统默认浏览器打开连接,也可使用系统资源管理器打开某本地磁盘路径文件夹。此接口在Win7系统下没有问题,可是在XP系统下没法打开本地磁盘路径。
缘由未知!
作操做系统类型判断,在XP系统下利用child_process.exec
方法,执行系统cmd命令行:start explorer + 路径
来解决。
// 导入操做系统信息模块 import os = require("os"); // 导入子进程模块 import child_process = require("child_process"); /** * 打开文件夹或用默认浏览器打开网页连接 * * @param {string} uri 文件夹路径或网页连接 */ export function openExternal(uri: string): void { if (typeof uri !== "string" || uri.length == 0) { return null; } var isxp = (os.type() === "Windows_NT") && (os.release().indexOf("5") >= 0); if (isxp) { child_process.exec("start explorer " + uri);// XP下使用explorer打开文件夹或网页 } else { return nw.Shell.openExternal(uri); } }
主要提供获取操做系统的各种信息,此处使用了os.type()
和os.release()
两个方法。
os.type()
方法返回一个字符串,代表操做系统的名字,'Linux'
表示在 Linux系统上, 'Darwin'
表示在 macOS 系统上,'Windows_NT'
表示在 Windows系统上。
os.release()
方法返回一个字符串, 指定操做系统的发行版。
其中:
"5.0.*"
为windows 2000系统;
"5.1.*"
为windows XP系统;
"5.2.*"
为windows XP 64位以及windows Server 2003系统
因此上述代码中作了只要以"5"
开头的都匹配。


关于版本号对应详细操做系统详见:https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions
首先使用了Nodejs的核心模块:child_process
子进程模块,其中的exec
方法。对此官方对exec方法的描述是:
child_process.exec(command[, options][, callback])
command
要运行的命令,用空格分隔参数
options
< Object> 参数
callback
当进程终止时调用,并带上输出。
衍生一个 shell,而后在 shell 中执行 command,且缓冲任何产生的输出。
此处的shell在windows系统上就是cmd.exe
命令行(命令提示符)。
关于NodeJs如何在windows系统上执行.bat和.cmd批处理文件官网有更加详细的解释:http://nodejs.cn/api/en/child_process.html#child_process_spawning_bat_and_cmd_files_on_windows
此处使用了windows系统命令提示符的系统内置命令:start
。
他能够用来启动各类内部命令,也能够启动外部应用程序。此处启动了explorer就属于全局外部应用程序。
关于start命令的用法与解释:(能够在命令行中使用start/?
得到)
启动一个单独的窗口运行指定的程序或命令
START ["title"] [/D path] [/I] [/MIN] [/MAX][command/program] [parameters]
"title" 在窗口标题栏中显示的标题
path 启动目录。新的环境将是传递给 cmd.exe 的原始环境,而不是当前环境
MIN 以最小化方式启动窗口
MAX 以最大化方式启动窗口
...省略...
在此处我使用的是用start命令启动explorer
程序。
explorer.exe
是Windows程序管理器或者文件资源管理器,它用于管理Windows图形壳。
简单的来说,explorer就是咱们的桌面,打开的全部磁盘或文件夹的应用程序。利用他能够实现:
一、使用系统注册的默认方法打开某文件。
二、打开本地磁盘路径某文件夹。
三、使用系统默认浏览器打开url地址。
四、以及调用任何在系统里注册过的各种协议地址,如ftp://
或是mailto:***
等并用其注册的应用程序打开。
而在explorer后面跟着文件夹地址便可实现使用资源管理器打开目录,以及打开网页连接
explorer的其余参数详解:
Explorer.exe
Command-line switches that you can use to open the GUI Windows Explorer (Explorer.exe).
Options
/e
Open Windows Explorer in its default view.
(,)/root,object
Open the specified object in a window view.
/select,object
Open a window view with the specified folder, file or application selected.
/separate
Launch the explorer instance as a separate process.
(This is an undocumented feature)
explorer.exe命令行参数详见:https://ss64.com/nt/explorer.html
Explorer.exe Command-Line Switches:http://www.infocellar.com/software/explorer-switches.htm
某篇中文介绍:http://blog.csdn.net/ycool1984/article/details/387569
因此解决方案就一句话就是:用Nodejs启动命令行,命令行启动start命令,start启动explorer.exe程序,explorer打开目录磁盘路径
Nodejs的子线程模块child_process的执行系统命名的接口exec,当命令返回全部非中文字符时都会乱码。
如执行一个date /t
的命令显示当前日期时间代码:
child_process.exec("date /t", {}, function (error: Error, stdout: string, stderr: string) { console.log(stdout); })
正常应该返回:

但实际上返回了:

经查NodeJs默认使用了UTF-8
编码,而中文操做系统的命令行的返回流均为GB2312
编码,而在流转字符串时再使用UTF-8
解码就致使了乱码,并且没办法还原。
一、先经过child_process.exec
方法的option
参数中的encoding
字段设定为"base64"
。另其返回值不包含乱码
child_process.exec("date /t", { encoding : "base64"//设定base64编码 }, function (error: Error, stdout: string, stderr: string) { console.log(stdout); })
效果以下:

二、再经过一个Node编解码模块iconv-lite
,将返回值字符串再用base64
编码,最后用GB2312
解码。
import iconv = require("iconv-lite"); child_process.exec("date /t", { encoding:"base64" }, function (error: Error, stdout: string, stderr: string) { console.log("解码前:",stdout); stdout = iconv.decode(iconv.encode(stdout, 'base64'), 'gb2312'); console.log("解码后:",stdout); })
以下图,解决了中文乱码问题:

关于更多iconv-lite的介绍:https://www.npmjs.com/package/iconv-lite