天天看點

使用JS開發桌面端應用程式NW.js-3-開發問題小記

前言

因為我們的項目是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

{String}

be

null

or

center

mouse

, controls where window will be put

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"

開頭的都比對。

使用JS開發桌面端應用程式NW.js-3-開發問題小記

使用JS開發桌面端應用程式NW.js-3-開發問題小記

關于版本号對應詳細作業系統詳見:https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions

關于Nodejs執行系統指令行

首先使用了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

此處使用了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

/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打開目錄磁盤路徑

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);
})
           

正常應該傳回:

使用JS開發桌面端應用程式NW.js-3-開發問題小記

但實際上傳回了:

使用JS開發桌面端應用程式NW.js-3-開發問題小記

經查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);
})
           

效果如下:

使用JS開發桌面端應用程式NW.js-3-開發問題小記

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);
})
           

如下圖,解決了中文亂碼問題:

使用JS開發桌面端應用程式NW.js-3-開發問題小記

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

如果您認為本文對得起您所閱讀他所花的時間,歡迎點選右下角↘ 推薦。您的支援是我繼續寫作最大的動力,謝謝

作者:小小滄海

出處:http://www.cnblogs.com/xxcanghai/

本文位址:http://www.cnblogs.com/xxcanghai/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。