前言
因為我們的項目是2C的,而XP系統是最大的使用者量占比,是以隻能使用nw開發而不能用Electron,本文謹記開發nw過程中遇到的各種問題以及解決方案
nw.Window.open打開新視窗不能設定指定位置
問題描述
nw.Window.open
打開新視窗API中的參數option中
position
字段隻能指定為
center
或
mouse
。如字面含義:
center
為螢幕正中央,
mouse
為滑鼠目前位置。
幾乎可以推測,nw的滑鼠右鍵菜單應該也是使用此接口,明顯是為了彈出右鍵菜單用的,除此之外幾乎沒有别的應用場景可以用到新打開視窗在滑鼠位置的。
是以,在nw的打開新視窗功能中,其實可以說 隻能顯示在螢幕正中央。
nw.Window.open('http://xxcanghai.cnblogs.com/', {
//打開新視窗的參數Option
width:500,
height:500,
show:true,//是否顯示新視窗
position:"center"//新視窗顯示位置,隻能使用center或mouse
}, function(new_win) {
console.log('已打開新視窗');
});
官網對position的描述:
position
be
{String}
or
null
center
, controls where window will be put
mouse
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/#position
解決方案
一句話就是,先open一個隐藏視窗,之後在callbcal裡面再重設其位置,再顯示出來
詳細步驟:
1、重新封裝nw.Window.open方法,在原有的position字段上擴充四個屬性,分别是 左上角,左下角,右上角,右下角,這裡使用枚舉對象來定義。
/**
* 擴充打開新視窗參數
*
* @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);
}
2、雖然擴充了預設position屬性,但真正傳給nw的還得是他支援的,是以增加判斷如果使用的是新增字段,則儲存使用者自定義設定,同時改寫option參數。
除此之外,因為要統一隐藏視窗,是以還要改寫預設的show屬性,儲存使用者設定的是否顯示視窗,隐藏打開視窗後,當設定完位置後再手動設定使用者初始設定的show選項。
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;
3、執行真正的nw.Window.open,同時在打開後的callback中根據自定義位置選項重新設定視窗位置。
最後再還原使用者原本設定的show屬性,以及觸發使用者原本傳進來的callback回調函數。
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、因為我隻需要在四個角顯示,是以隻擴充了4個枚舉類型,如果需要在指定坐标(x,y)顯示視窗,以上同理增加對position的對象類型
{x:number,y:number}
檢測處理即可。
nw的系統API-openExternal在XP系統下無法打開本地磁盤路徑
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);
}
}
關于Nodejs的OS子產品
主要提供擷取作業系統的各類資訊,此處使用了
os.type()
和
os.release()
兩個方法。
os.type()
方法傳回一個字元串,表明作業系統的名字,
'Linux'
表示在 Linux系統上,
'Darwin'
表示在 macOS 系統上,
'Windows_NT'
表示在 Windows系統上。
os.release()
方法傳回一個字元串, 指定作業系統的發行版。
關于windows系統的發行版本号
其中:
"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執行系統指令行
首先使用了Nodejs的核心子產品:
child_process
子程序子產品,其中的
exec
方法。對此官方對exec方法的描述是:
child_process.exec(command[, options][, callback])要運作的指令,用空格分隔參數
command
< Object> 參數
options
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
此處使用了windows系統指令提示符的系統内置指令:
start
。
他可以用來啟動各種内部指令,也可以啟動外部應用程式。此處啟動了explorer就屬于全局外部應用程式。
關于start指令的用法與解釋:(可以在指令行中使用
start/?
獲得)
啟動一個單獨的視窗運作指定的程式或指令
START ["title"] [/D path] [/I] [/MIN] [/MAX][command/program] [parameters]
"title" 在視窗标題欄中顯示的标題
path 啟動目錄。新的環境将是傳遞給 cmd.exe 的原始環境,而不是目前環境
MIN 以最小化方式啟動視窗
MAX 以最大化方式啟動視窗
...省略...
在此處我使用的是用start指令啟動
explorer
程式。
關于windows系統的explorer.exe
explorer.exe
是Windows程式管理器或者檔案資料總管,它用于管理Windows圖形殼。
簡單的來講,explorer就是我們的桌面,打開的所有磁盤或檔案夾的應用程式。利用他可以實作:
1、使用系統注冊的預設方法打開某檔案。
2、打開本地磁盤路徑某檔案夾。
3、使用系統預設浏覽器打開url位址。
4、以及調用任何在系統裡注冊過的各類協定位址,如
ftp://
或是
mailto:***
等并用其注冊的應用程式打開。
而在explorer後面跟着檔案夾位址即可實作使用資料總管打開目錄,以及打開網頁連結
explorer的其他參數詳解:
Explorer.exe
Command-line switches that you can use to open the GUI Windows Explorer (Explorer.exe).
Options
Open Windows Explorer in its default view.
/e
Open the specified object in a window view.
(,)/root,object
Open a window view with the specified folder, file or application selected.
/select,object
/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打開目錄磁盤路徑
Node.js的child_process.exec執行指令的傳回值中文亂碼
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
解碼就導緻了亂碼,而且沒辦法還原。
1、先通過
child_process.exec
方法的
option
參數中的
encoding
字段設定為
"base64"
。另其傳回值不包含亂碼
child_process.exec("date /t", {
encoding : "base64"//設定base64編碼
}, function (error: Error, stdout: string, stderr: string) {
console.log(stdout);
})
效果如下:
2、再通過一個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
如果您認為本文對得起您所閱讀他所花的時間,歡迎點選右下角↘ 推薦。您的支援是我繼續寫作最大的動力,謝謝作者:小小滄海
出處:http://www.cnblogs.com/xxcanghai/
本文位址:http://www.cnblogs.com/xxcanghai/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。