Knockout Mvc Compoment FrameSet With Typescript

Knockout Mvc Compoment FrameSet With Typescript

框架簡介

如下簡稱爲 kot。kot是一套基於knockout jquery bootstrap(Metronic) typescript 的可快速推廣的完整前端框架。 javascript

框架優點

  1. 使用knockout mvvm模式,頁面思路更清晰,一個viewModel只負責一件事。
  2. Compoment組件模式,文件結構明瞭,調用簡單思路清晰,靈活性強,可複用,低耦合。
  3. 基於Metronic響應式樣式表,可實現移動端和pc端的適配。
  4. 沿用成熟的jquery類庫,與當前全部類庫不衝突,使用方法簡單,可快速上手。
  5. 應用typescript2.0強類型javascript,動態編輯js提高代碼可讀性,還能配合vs作類型檢查,下降js編寫出錯概率和維護難度。
  6. 嚴格的代碼規範,能約束開發人員,保證代碼統一性。
  7. 包含豐富的通用方法(webUtil.js)和經常使用控件(如分頁,彈窗,上傳,提示框等),包含完整的demo和api (Kot.Plugin.TypeScript.DefinitelyTyped),能進行快速開發。

框架文件結構

  1. 網站(表現層),mvc主要做用視圖展現。
  2. 模型(Model),主要做用承載視圖數據結構,網站先後臺數據交互模型。
  3. 業務(Business),業務邏輯層(含Ibusiness),視圖接口定義,從服務過來的Dto經過Business轉換成Model。
  4. 單元測試(test),Controller中方法的單元測試。

一、網站文件結構

App_Data

App_Start

應用程序啓動時自動調用的配置。 css

BundleConfig.cs mvc壓縮css和js的配置文件。 html

FilterConfig.cs mvc過濾器配置,站點地圖權限登陸菜單管理的入口。 前端

RotuteConfig.cs 路由規則配置。 java

Webstack 文件夾下含有 jquery

ExceptionFilter.cs 網站程序異常處理過濾器。 web

        MvcMenuFilter.cs 框架權限驗證和菜單處理主要程序。 ajax

        WebSiteExcetpion.cs 自定義網站錯誤類型。用於拋出登信息丟失等自定義業務異常。 typescript

assest

網站主要引用的資源文件(樣式,經常使用組件等)。現應用的是metronic_v4.1.0。 json

Admin 管理框架模板

Global bootstrap全局引用資源

Public 網站經常使用資源,如暫無圖片和一些補丁css

Controller

網站控制器

DictionaryFiles

安裝盤古分詞的字典,如不用分詞搜索則能夠刪除。

Filters

Mvc過濾器文件夾,裏面能夠自定義一些過濾器,自定義的過濾器須要在App_Start下的FilterConfig.cs中註冊。

Scripts

框架依賴js文件和ts文件。

UserControls

業務須要調用的用戶控件。

Views

Mvc 視圖文件夾,母版頁在 Shared 爲 _Layout.cshtml

其餘爲業務視圖。

BaseHttpApplication.cs

定義一些 HttpModule的自定義方法,目前在框架應用中,主要用於處理通用上傳和下載。

Global.asax

全局配置入口。

SiteInfo.cs

網站通用信息快捷調用方式,好比系統id、當前登陸用戶等。

Web.config

網站配置文件。

 

二、模型文件結構

經過業務區分業務文件夾。

三、業務文件結構

  1. IBusiness,業務接口。
  2. Buniness,業務實現。

四、單元測試文件結構

框架基礎

登陸原理

登陸流程:

一、【統一登陸】像Session中添加登陸信息

二、【網站過濾器驗證】 (MvcMenuFilter.cs 須要在FilterConfig中註冊)

每個action請求時會判斷當前Session中是否有登陸信息。

若是登陸信息爲空,判斷請求方式是同步請求仍是異步請求。

同步請求,則經過Redrect 跳轉到登陸頁。

異步請求,則經過throw new WebSiteException ,錯誤碼爲1000.

三、異常處理。

全部網站異常都會被ExceptionFilter.cs捕獲。

ExceptionFilter 能夠記錄錯誤日誌,日誌路徑 在網站跟目錄的Log文件夾下。

自定義異常,字典也聲明在這個文件中。

public static Dictionary<string, string> ErrorDictionary = new Dictionary<string, string>()

{

{ "1000","登陸信息丟失"}

};

若是拋出的是WebSiteException則會經過字典方式相應。

這個拋出的結果會被框架通WebUtil.js響應。(後面會詳細說明)

菜單生成原理

登陸獲取的用戶信息中會包含菜單信息。

獲取當前系統編號後,能夠經過系統編號拿到該用戶當前登陸系統的菜單,並轉換成List<LoginMenuInfo>這個數組。

這個數組會經過ViewBag.treeData (json格式)傳遞到頁面。

頁面接收到數據時會經過Menu.js 響應,來生成菜單和麪包屑導航。

對應的模板在 /Views/ Shared/ PartialViews/ _Left.cshtml 中。

自定義麪包屑顯示

好比標籤管理這個三級麪包屑在 菜單中並無

能夠經過sitemap 這個固定參數來傳遞。

<a data-bind="attr:{href:formatUrl('/PackageConfig/TagManage?packageId='+item.Id()+'&sitemap='+encodeURI('套餐研發~套餐設計器~標籤管理'))}"標籤管理</a>

以上會配合 下面 Menu.js使用

js控件

  1. webUtil.js

通用Web工具,經常使用js方法等。經常使用方法有WebUtil.ajax();參數同$.ajax參數,比他多2個參數爲 loading和loadmsg。實現loading效果,依賴loading.js和jquery.sh.popups.js。

列舉一下經常使用方法:

下面列舉ts版本api

接口

interface WebUtilAjaxOption extends JQueryAjaxSettings {

/**

* 是否顯示等待 默認 true

*/

loading?: boolean;

/**

* 等待時的提示信息 默認 加載中...

*/

loadmsg?: string;

}

 

interface WebUtilStatic {

/**

* 轉義html字符

* @param str

* @returns string

*/

encodeHtml(str: string): string;

WebUtil.encodeHtml("<div></div>"); 返回string類型。

結果會把 < 這種尖角號 轉換爲 &lt; 這種符號。

/**

* 中止冒泡事件

* @param {} event

* @returns {}

* 做者:崔園清

* 小組:山河web

* 說明:中止冒泡事件;

* 建立日期:2014-9-27 14:59:17

* 版本號:v1.0

*/

stopEvent(event: any): void;

 

/**

* 將一個string值轉換爲時間

* @param {} value

* @returns {}

*/

  1. 在普通js上下文中直接調用,會默認獲取window.event ,調用方式WebUtil.stopEvent();
  2. 在JQuery上下文中須要傳入event對象,$("btn1").click(function(e){ window.stopEvent(e); });

parseDate(value: string): Date;

把一個字符串轉換成時間類型。

var str="2015-09-10 18:30"; var date = WebUtil.parseDate(str);

/**

* 獲取url的參數

* @param {} name

* @returns {}

*/

getQueryString(name: string): string;

獲取連接中的QueryString參數。

  1. var url="www.baidu.com?id=001&name=bob";

    WebUtil.getQueryString("id"); // 返回 001

    WebUtil.getQueryString("name");// 返回 bob

    WebUtil.getQueryString("ddd");// 返回 空字符串""

/**

* ajax

* @param option

* @returns {}

*/

ajax(option: WebUtilAjaxOption);

ajax中定義了一個錯誤的Handler 其中包含一些通用的自定義錯誤碼

errorHandler = {

"1000": function () {

sh.alert("您的登陸已失效,請從新登陸。", function () {

location.href = "/Account/Login";

});

},

"1001": function () {

sh.alert("您的企業信息丟失,請從新選擇企業。", function () {

location.href = formatUrl("/Home/EnterpriseSet");

});

},

"1002": function () {

sh.alert("您已在當前企業離職。", function () {

location.href = formatUrl("/Home/EnterpriseSet");

});

}

};

是以用WebUtil.ajax調用後臺方法時如後臺程序拋出異常,則會判斷錯誤碼,

若是錯誤碼中的ErrorCode 符合通用Handler時則會調用Halder的方法

若是不包含errorCode可是包含 ErrorMessage 則是wcf接口拋出的通用異常,會直接彈出提示。

果即不包含錯誤碼也不包含ErrorMessage則是404之類的調用異常。

拋出 調用ajax異常時 則是前端controller或者business出錯,若是直接提示的錯誤信息則是wcf服務異常。

 

//輸出錯誤信息到控制檯

console.log(xhr.responseText);

//默認行爲,彈出提示

try {

var errorJson = $.parseJSON(xhr.responseText);

if (errorJson.errorCode != null) {

var errorFun = errorHandler[errorJson.errorCode];

if (errorFun != null) {

errorFun();

} else {

try {

var firstMsgJson = errorJson.errorMessage.match(/\{[^{}]+\}/)[0];

var serviceError = $.parseJSON(firstMsgJson);

sh.alert(serviceError.ErrorMessage);

} catch (e) {

sh.alert("調用ajax異常,請查看程序日誌:" + errorJson.errorMessage);

}

}

} else {

sh.alert('服務調用錯誤,請查看控制檯。');

}

} catch (e) {

sh.alert('服務調用錯誤,詳情請見錯誤日誌。');

}

 

 

/**

* 獲取序號方法

* @param {} index

* @param {} pageIndex

* @param {} pageSize

* @returns {}

*/

getNum(index: number, pageIndex: number, pageSize: number): number;

一個在分頁狀態下獲取連續序號的方法。這裏$parent 是knockout上下文對象。$index是knockout循環上下文中的索引。

<tbody data-bind="foreach:{data:dataList,as:'item'}">

<tr>

<td>

<!--通用獲取序號方法-->

 

<p class="form-control-static"><!--ko text:WebUtil.getNum($index(),$parent.dataList.pageIndex(),$parent.dataList.pageSize())--><!--/ko--></p>

</td>

 

declare var WebUtil: WebUtilStatic;

 

interface shStatic {

/**

* 通用提示框方法

* @param msg

* @param callback

* @param msgtitle

* @returns {}

*/

alert(msg: string, callback?: () => void, msgtitle?: string): void;

普通提示框: title 默認爲 "系統提示"

1)sh.alert("操做成功!");

2)sh.alert("操做完成!",function(){console.log("數據操做完成") });

3)sh.alert("操做完成!",function(){console.log("數據操做完成") },"DaTree提示");

confirm(msg: string, yescallback: () => void, nocallback?: () => void, msgtitle?:
string): void;

包含肯定取消按鈕的提示框:title 默認爲"系統提示"

1)sh.confirm("肯定要這麼作嗎?",function(){ console.log("點擊了確認") });

2)sh.confirm("肯定要這麼作嗎?",function(){ console.log("點擊了確認") },function(){console.log("點擊了取消")});

3) sh.confirm("肯定要這麼作嗎?",function(){ console.log("點擊了確認") },function(){console.log("點擊了取消")},"DaTree提示自定義標題");

4) 不要取消事件能夠傳null

sh.confirm("肯定要這麼作嗎?",function(){ console.log("點擊了確認") },null,"DaTree提示自定義標題");

declare var sh: shStatic;

 

interface KnockoutStatic {

/**

* 註冊控件通用方法

* @param controlName

* @param viewModel

* @param templateUrl

* @returns {}

*/

RegisterControl(controlName: string, viewModel: any, templateUrl: string): void;

1)通用註冊控件方法

//註冊控件

ko.RegisterControl("priceconfigcontrol", PriceConfigControlViewModel, formatUrl("/UserControls/PackageConfig/PriceConfigControl/PriceConfigControlView.html"));

2)registerControl方法會形成不少次異步html請求,正在想辦法解決。

formatCurrency

/**

* 將數字轉換爲 格式化後的金錢字符串

* @param num

*/

declare function formatCurrency(num: number): string;

  1. 使用場景通常是在html綁定一個金錢格式的數字時用的。

    <div class="col-md-3 ">

    <p class="form-control-static">

    套餐必選成本合計:<!--ko text:formatCurrency(priceConfigModel().RequiredCostTotalPrice())--><!--/ko-->

    </p>

    </div>

formatUrl

全局處理虛擬目錄的方法。在layout上實現。

/**

* 處理虛擬目錄格式化地址的方法 在Layout上實現

* var appRoot = "@Request.ApplicationPath";

* if (!appRoot) {

* throw new Error("請設置全局變量.");

* }

*

* function formatUrl(url) {

* if (url == null) {

* return url;

* }

* if (window.appRoot && window.appRoot != '/' && url.indexOf("/") == 0) {

* if (url.indexOf(appRoot + "/") != 0) {

* url = appRoot + url;

* }

* }

* return url;

* }

* @param url

*/

declare function formatUrl(url: string): string;

好比當前網站是在虛擬目錄下

http://www.baidu.com/myWebSite/

那麼在調用url時 須要加上 formatUrl();

$.ajax({

    url:formatUrl("/package/getpackage")

data:{}

});

這裏 formatUrl返回爲 /myWebSite/package/getpackage

Guid

/**

* 定義一個Guid接口

*/

interface GuidStatic {

Empty: string;

}

/**

* 定義一個Guid靜態類

*/

declare var Guid: GuidStatic;

Guid在js中沒有默認值,有的時候後臺參數須要 反序列化一個 Id類型,因此添加了一個Guid默認值

下面爲demo

constructor(model?) {

this.Id = ko.observable(model && model.Id != null ? model.Id : Guid.Empty);

}

string.format

/**

* 定義String靜態方法

*/

interface StringConstructor {

format: (...args: any[]) => string;

}

這裏與C#中靜態調用稍有不一樣。

C#中 : string str=string.Format("今天是{0}國慶節","10月1號");

js中: var str="今天是{0}國慶節".format("10月1號");

Date通用

簡單例子: var date1=new Date();

date1.format("yyyy-mm-dd HH:mm");

/**

* 時間通用處理

*/

interface Date {

/**

* 格式化時間

* @param format

* @returns {}

*/

format(format: string): string;

/**

* 添加年

* @param value

* @returns {}

*/

addYear(value: number): Date;

/**

* 添加月

* @param value

* @returns {}

*/

addMonth(value: number): Date;

/**

* 添加天

* @param value

* @returns {}

*/

addDays(value: number): Date;

/**

* 添加小時

* @param value

* @returns {}

*/

addHours(value: number): Date;

/**

* 添加分

* @param value

* @returns {}

*/

addMinutes(value: number): Date;

/**

* 獲取今天

* @param value

* @returns {}

*/

getToday(): Date;

}

KnockoutPaging擴展

/**

* 爲kopaging 插件作的擴展

*/

interface KnockoutObservableArrayFunctions<T> {

/**

* 擴展了ko paging以後纔有的屬性

*/

pageIndex: KnockoutObservable<number>;

/**

* 擴展了ko paging以後纔有的屬性

*/

pageSize: KnockoutObservable<number>;

/**

* 擴展了ko paging以後纔有的屬性

*/

callback: () => void;

/**

* 設置數據總條數

* @param count

* @returns {}

*/

SetPageTotal: (count: number) => void;

}

KeyValuePair

 

通常用於字典類型的數據處理

Demo

/**

* 選區類型字典

*/

categoryAreaDic: KnockoutObservableArray<KeyValuePair2<string, string>>;

api

/**

* 鍵值對

*/

interface KeyValuePair2<TKey, TValue> {

Key: TKey;

Value: TValue;

}

 

/**

* 鍵值對參數對象

*/

interface IKeyVaulePair {

Key: any;

Value: any;

}

 

/**

* 鍵值對委託方法

*/

declare var KeyValuePair2: (obj: IKeyVaulePair) => void;

EventBus事件總線

/**

* 事件總線接口

*/

interface EventBusStatic {

/**

* 註冊事件

* @param option

* @returns {}

*/

registerEvent(option: EventBusOption): EventBusStatic;

 

/**

* 調用事件

* @param eventID

* @param args 參數列表

* @returns void

*/

callEvent(eventID: string, ...args: Array<any>): void;

/**

* 打印事件列表

* @returns {}

*/

print(): Array<EventBusOption>;

/**

* 刪除事件

* @param eventID

* @returns {}

*/

removeEvent: (eventID: string) => void;

/**

* 驗證事件是否存在

* @param eventID

* @returns true存在

*/

eventExist: (eventID: string) => boolean;

}

/**

* 事件總線對象

*/

declare var EventBus: EventBusStatic;

demo

var obj1 = { name: "123", say: function (title, msg) { debugger; alert(title + ":" + msg + "name:" + this.name); } };

EventBus.registerEvent({ eventHandler: obj1, eventName: "say2", eventId: "s1", eventBody: obj1.say });

 

EventBus.registerEvent({ eventHandler: obj1, eventName: "say2", eventId: "s2", eventBody: obj1.say });

 

EventBus.registerEvent({ eventHandler: obj1, eventName: "say2", eventId: "s3", eventBody: obj1.say });

EventBus.callEvent("s2", "標題", "消息");

  1. loading.js

接口

顯示等待框方法

/**

* loading插件接口

*/

interface loadingStatic {

/**

* 打卡loading

* @param text 顯示的文字

* @returns $loading

*/

open(text?: string): JQuery;

/**

* 關閉等待框

* @returns $loading

*/

close(): JQuery;

/**

* 必定要在 dom ready以前調用,不然無效。

* @param url loading 圖片的路徑 默認爲 imgs/loading.gif

* @returns $loading

*/

setImageUrl(url: string): JQuery;

}

 

declare var loading: loadingStatic;

例子

/*

* 開啓 loading.open();

* 關閉 loading.close()

* 設置loading圖片 loading.setImageUrl("/Content/Images/loading1.gif");

  1. jquery.sh.popups.js

接口

/**

* 彈窗組件參數

*/

interface popupsOptions {

listeners?: {

show?: () => void;

hide?: () => void;

};

width?: number;

}

 

/**

* 彈窗組件對象

*/

interface popups {

show: () => popups;

hide: () => void;

}

 

interface JQuery {

/**

* 彈窗插件

* @param options

* @returns {}

*/

popModal(options?: popupsOptions): popups;

 

}

例子

/*

var modalChooseBuilding = $("#modalChooseBuilding").popModal({

listeners: {

show:function() {

},

hide:function() {

}

},

width: 1200

});

modalChooseBuilding.show(); 顯示彈窗

modalChooseBuilding.hide(); 關閉彈窗

*<div id="modalChooseBuilding" class="pop-modal">

<div class="modal-header">

<h4 class="modal-title">標題</h4>

</div>

<div class="modal-body">

 

</div>

<div class="modal-footer">

<button class="btn btn-primary" >肯定</button>

<button type="button" class="btn btn-default btn_enter" onclick="modalChooseBuilding.hide();">關閉</button>

</div>

</div>

*/

  1. simpleValidate.js

接口

 

/// <reference path="../../jquery/jquery.d.ts" />

 

 

/**

* 參數接口

*/

interface SimpleValidateOption {

 

 

/**

* 成功的樣式

*/

successClass?: string;

/**

* 失敗的樣式

*/

errorClass?: string;

/**

* 失敗元素的樣式

*/

errorMessageClass?: string;

/**

* 遠程驗證元素呈現的樣式

*/

remoteClass?: string;

 

 

}

 

/**

* simpleValidate 靜態方法

*/

interface SimpleValidate {

/**

* 添加規則

* @param rule

* @param message

* @returns void

*/

addRuleMessage(rule: any, message: any);

/**

* 初始化方法

* @param element

* @param options

* @returns {}

*/

init(element: JQuery, options?: SimpleValidateOption);

/**

* 重置方法

* @param element

* @returns {}

*/

reset(element: JQuery);

}

 

interface JQueryStatic {

/**

* 初始化全局JQuery靜態變量

*/

simpleValidate: SimpleValidate;

}

 

interface JQuery {

/**

* 驗證方法

* @param options

* @returns {}

*/

simpleValidate(options?: SimpleValidateOption): JQuery;

}

例子

初始化驗證方法 $.simpleValidate.init($("#modalCopySpaceScheme"));

重置 $.simpleValidate.reset($("#modalCopySpaceScheme"));

驗證 if (!$("#modalCopySpaceScheme").simpleValidate()) {

return;

}

  1. jquery.sh.webuploader.js

通用上傳控件

Api接口

 

interface ShUploaderServerFile {

responseVal: string;

name: string;

ext: string;

}

 

interface ShUploaderOption {

//對應錯誤處理時使用的提示信息

errorMessage: any;

//生成的input所使用的NAME

inputName: string;

//此處設爲flash時會只支持flash方式,不啓用HTML5方式

runtimeOrder?: string;

//服務器回傳數據中表明文件的字段名

responseVal: string;

//上傳控件備註名稱

info?: string;

//文件上傳路徑(接口地址)

server: string;

//預覽上傳後文件的根目錄或接口地址

previewURL: string;

//MD5秒傳設置,爲真時會把體積大小超過md5SizeLimit的文件向md5URL發送文件信息並根據結果絕定是否是須要上傳文件

md5Check: boolean;

//秒傳驗證的url

md5URL?: string;

//文件上傳域,即在回傳POST(GET)的內容中,哪一個參數名包含文件

fileVal: string,

//falsh插件路徑,初始化插件時需配置此參數,不然FLASH插件會失效

swf: string;

//能夠上傳文件的總數量限制,默認爲1

fileNumLimit: number;

//是否顯示能夠上傳文件的總數量限制文本 默認爲 true 顯示

isShowfileNumLimit?:boolean;

//單個文件大小限制(此處默認爲10M)

fileSingleSizeLimit: number;

//插件總計能夠上傳多少字節的文件(100M)

fileSizeLimit: number;

//根據服務器回傳值建立預覽文件服務端地址的URL方法

createFileUrl: (responseVal: string) => string;

//自動開始上傳

auto: boolean;

//文件上傳方式 false爲常規方式,true爲啓用二進制流

sendAsBinary: boolean;

//[默認值:false] 是否要分片處理大文件上傳。

chunked?: boolean;

// [可選] [默認值:5242880] 若是要分片,分多大一片? 默認大小爲5M.

chunkSize?: number;

// [可選] [默認值:2] 若是某個分片因爲網絡問題出錯,容許自動重傳多少次?

chunkRetry?: number;

//併發上傳,默認就讓一次傳一個 多個須要服務支持

threads: number;

//圖片模式

imageMode: boolean;

//支持拖拽模式

dndMode: boolean;

//支持剪切板粘貼

pasteMode: boolean;

//事件處理

listeners: {

//文件上傳成功

uploadSuccess: (file: any, response: any) => void;

//文件上傳錯誤

error: (msg: string) => void;

//總體上傳完成

complate: () => void;

//結束事件 此處添加上傳結束的回調處理函數

finished: () => void;

//此處放置開始上傳時調用的事件

startUploader: () => void;

//刪除文件事件

removeUploadedFile: (file: any) => void;

};

//容許的文件類型

accept: {

title: string;

extensions: string;

mimeTypes: string;

};

//隨上傳文件一塊兒回傳的參數

formData: any;

//把已存在的文件顯示出來,用於在編輯狀態下顯示已存 的文件

serverFiles: Array<ShUploaderServerFile>;

}

/**

* 上傳插件

*/

interface ShUploader {

//控件銷燬方法

destroy: () => void;

}

interface JQueryStatic {

/**

* 初始化全局JQuery靜態變量

*/

sh: {

uploader: ShUploader;

};

}

 

 

interface JQuery {

shUploader(options?: ShUploaderOption): ShUploader;

}

例子

//上傳控件配置1

this.upload1 = $("#file_uploaer_1").shUploader({

//對應錯誤處理時使用的提示信息

errorMessage: {

"Q_EXCEED_NUM_LIMIT": "只能上傳999張圖片",

"Q_EXCEED_SIZE_LIMIT": "請上傳2M如下的圖片",

"Q_TYPE_DENIED": "上傳圖片格式爲: gif jpg png",

"F_DUPLICATE": "您選擇了重複的文件",

"F_EXCEED_SIZE": "請上傳2M如下的圖片"

},

//runtimeOrder: 'flash', //此處設爲flash時會只支持flash方式,不啓用HTML5方式

inputName: "sh_uploader_val", //生成的input所使用的NAME

responseVal: "revisionId", //服務器回傳數據中表明文件的字段名

info: '上傳控件1',

server: formatUrl("/Uploads"), //文件上傳路徑(接口地址)

previewURL: formatUrl("/Files/R"), //預覽上傳後文件的根目錄或接口地址

//MD5秒傳設置,爲真時會把體積大小超過md5SizeLimit的文件向md5URL發送文件信息並根據結果絕定是否是須要上傳文件

md5Check: false,

md5URL: formatUrl("/CheckRepeat"),

fileVal: 'file', //文件上傳域,即在回傳POST(GET)的內容中,哪一個參數名包含文件

swf: formatUrl("/Scripts/SH.Plugin/uploader/webuploader-0.1.5/Uploader.swf"), //falsh插件路徑,初始化插件時需配置此參數,不然FLASH插件會失效

fileNumLimit: 999, //能夠上傳文件的總數量限制,默認爲1

fileSingleSizeLimit: 2 * 1048576, //單個文件大小限制(此處默認爲10M)

fileSizeLimit: 10000 * 10485764, //插件總計能夠上傳多少字節的文件(100M)

//根據服務器回傳值建立預覽文件服務端地址的URL方法

createFileUrl(responseVal) {

return this.previewURL + "/" + responseVal;

},

auto: true, //自動開始上傳

sendAsBinary: true, //文件上傳方式 false爲常規方式,true爲啓用二進制流

chunked: true, //[默認值:false] 是否要分片處理大文件上傳。

chunkSize: 1048576, // [可選] [默認值:5242880] 若是要分片,分多大一片? 默認大小爲5M.

chunkRetry: 2, // [可選] [默認值:2] 若是某個分片因爲網絡問題出錯,容許自動重傳多少次?

threads: 1, //併發上傳,默認就讓一次傳一個\

//圖片模式

imageMode: true,

//支持拖拽模式

dndMode: true,

//支持剪切板粘貼

pasteMode: true,

//事件處理

listeners: {

uploadSuccess(file, response) {

file.filePath = response.data.revisionId;

//self.EdittingPlan().Spaces()[index].SpaceImages.push(response.data.revisionId);

 

self.EdittingPlan().FirstImage(response.data.revisionId);

},

error(msg) {

sh.alert(msg);

},

complate() {

//此處添加上傳成功的回調處理函數

},

//結速事件

finished() {

//此處添加上傳結束的回調處理函數

loading.close();

},

startUploader() {

//此處放置開始上傳時調用的事件

loading.open("文件上傳中...");

},

removeUploadedFile(file) {

self.EdittingPlan().FirstImage("");

}

},

//容許的文件類型

accept: {

title: 'Images',

extensions: 'gif,jpg,png',

mimeTypes: 'image/*'

},

//隨上傳文件一塊兒回傳的參數

formData: {},

//把已存在的文件顯示出來,用於在編輯狀態下顯示已存 的文件

serverFiles: (() => {

var result = [];

if (this.EdittingPlan().FirstImage() !== "") {

result.push({

responseVal: this.EdittingPlan().FirstImage(),

name: "",

ext: "jpg"

});

}

return result;

})()

});

  1. Menu.js

經過viewbag 中取過來的數據來初始化菜單

例子

須要引用 jquery tmpl.js

<script>

$(document).ready(function () {

initSitemap(@Html.Raw(ViewBag.treeData),@Html.Raw(ViewBag.SiteMapKeys));

});

</script>

<script id="one" type="text/x-jquery-tmpl">

<li data-mapdata="${Name}">

<a href="javascript:;">

<i class="${Icon}"></i>

<span class="title">

${Name}

</span>

<span class="selected"></span>

<span class="arrow"></span>

</a>

{{if ChildMenuInfos!=null && ChildMenuInfos.length>0}}

<ul class="sub-menu">

{{tmpl(ChildMenuInfos) '#tow'}}

</ul>

{{/if}}

</li>

 

</script>

<script id="maptree" type="text/x-jquery-tmpl">

<li>&nbsp;<a href="javascript:;"> ${$data}</a>&nbsp;</li>

<i class="fa fa-angle-right"></i>

</script>

 

<script id="tow" type="text/x-jquery-tmpl">

<li data-mapdata="${Name}">

<a href="${formatUrl(Url)}">

<i class="${Icon}"></i>

${Name}

</a>

{{if ChildMenuInfos!=null && ChildMenuInfos.length>0}}

<ul class="sub-menu">

{{tmpl(ChildMenuInfos) '#tow'}}

</ul>

{{/if}}

</li>

</script>

 

 

<!--站點地圖容器-->

<ul id="menuwarp" class="page-sidebar-menu" data-keep-expanded="false" data-auto-scroll="true" data-slide-speed="200"></ul>

 

  1. linq.js

框架 外部引用非必須組件

能夠在js中作 lambda 查詢

例子

具體用法請 linq.d.ts;

Enumerable.From(this.SpaceSchemesItems()).Where(x => x.IsDefault()).Sum(x => x.TotalCostPrice());

前端規範

  1. typescript/javascript規範

TSModel規範

  1. Model屬性聲明,要與後臺模型一致(須要提交到後臺反序列化的部分必定要一致。)
  2. 計算屬性聲明,要肯定返回值類型,在ts模式下有時候須要強制標識

this.DefaultCount = ko.computed<number>(() => {

return <number>Enumerable.From(this.SpaceSchemesItems()).Count(d => d.IsDefault());

}, this);

  1. 開頭字母大寫,與後臺模型屬性名想對應.
  2. 構造函數要添加可空any類型參數。
  3. KnockoutModel模型綁定中枚舉類型想綁定checked須要使用string類型。

下面附上標準demo

 

/**

* 套餐列表模型

*/

class PackageListModel {

/**

* 空間類型集合

*/

SpaceTypes: KnockoutObservableArray<string>;

/**

* 建立日期

*/

CreateDate: KnockoutObservable<string>;

/**

* 下架商品數量

*/

OffShelfCount: KnockoutObservable<number>;

/**

* 排序

*/

Sort: KnockoutObservable<number>;

/**

* 顏色

*/

Color: KnockoutObservable<string>;

/**

* id

*/

Id: KnockoutObservable<string>;

/**

* 狀態枚舉描述

*/

Status: KnockoutObservable<string>;

/**

* 狀態枚舉id

*/

StatusId: KnockoutObservable<number>;

/**

* 套餐類型 套餐/造型

*/

ModelType: KnockoutObservable<string>;

/**

* 套餐模式 基礎/成品/基礎+成品

*/

Mode: KnockoutObservable<string>;

 

constructor(model?: any) {

this.SpaceTypes = ko.observableArray([]);

if (model && model.SpaceTypes != null) {

for (var item of model.SpaceTypes) {

this.SpaceTypes.push(item);

}

}

this.CreateDate = ko.observable(model && model.CreateDate != null ? model.CreateDate : "");

this.OffShelfCount = ko.observable(model && model.OffShelfCount != null ? model.OffShelfCount : 0);

this.Sort = ko.observable(model && model.Sort != null ? model.Sort : 0);

this.Color = ko.observable(model && model.Color != null ? model.Color : "");

this.Id = ko.observable(model && model.Id != null ? model.Id : Guid.Empty);

this.Status = ko.observable<string>(model && model.Status != null ? model.Status : "未上架");

this.StatusId = ko.observable<number>(model && model.StatusId != null ? model.StatusId : 0);

}

}

命名規則

1)搜索關鍵字:小s開頭

sPackName,sKeywords,sBilPack。

2)字典:如枚舉狀態等dic開頭

dicShellStatus

3)普通屬性,私有變量:駝峯命名

packName

4)列表集合數據源屬性:list開頭

listPacks

5)臨時用緩存屬性,如編輯中商品等:temp開頭,必定要備註用途

/*

* 編輯商品彈窗綁定商品數據源對象

*/

tempProduct

6)彈窗:modal開頭

modalCopySpaceScheme

 

viewModel中也如此聲明

//複用選區彈窗對象

modalCopySpaceScheme: popups;

 

<div id="modalCopySpaceScheme" class="pop-modal">

<div class="modal-header">

<h4 class="modal-title">選區複用</h4>

</div>

<div class="modal-body">

</div>

<div class="modal-footer">

<a href="javascript:;" class="btn blue" data-bind="click:function(){eventConfirmCopy();}">肯定</a>

<button type="button" class="btn btn-default btn_enter" data-bind="click:modalCopySpaceScheme.hide">取消</button>

</div>

</div>

Typescript書寫規範

1)屬性與構造相對應:ts中聲明的屬性是抽象的,須要在構造中實例化。

class PackageListModel {

/**

* 空間類型集合

*/

SpaceTypes: KnockoutObservableArray<string>;

constructor(model?: any) {

    this.SpaceTypes=ko.observableArray([]);

}

2)this做用域:沒法區分this做用域時。

var self=this;

  1. html/cshtml規範

html書寫規範遵循語義化的標準寫法

好比聲明一個按鈕<button class="btn btn-default">肯定</button>

儘可能不要寫 <a href="javascript:;" class="btn btn-default">肯定</a>

 

1)  容器佈局:遵循bootstrap的標籤套用原則,不該有多餘標籤。

 

全部內容都應放在row下的col裏。

2)  標籤頁:在Metronic的佈局標準下,標籤頁的容器應爲rowcol

 

代碼詳見Metronic模版。

3)  頁面html行數較多時要添加region標籤。Ctrl+ks

 

4)  Layout佈局容器應在.container

 

5)搜索框應用panel包裹

3 HTML元素命名規範

標籤

命名

<input type="text" />

txtName

<select></select>

selProjectState

<texarta></texarea>

textProductDesc

<label></lable>

lbPrice

<div></div>

divProjectFile

<span></span>

spSKUPro

標籤頁

tabUserManage

模態框

modalAddUser

遮罩層

dialogLoading

   

 

  1. controller business model 規範

  1. Controller 負責相應頁面請求,給頁面傳入字典和接收頁面返回值的做用。

2Bussiness調用服務接口,簡單邏輯處理,模型轉換等。

  1. knockout 組件規範

組件 在項目中被命名爲UserControl

  1. userControl編碼規則詳見viewmodel,他們的聲明方式相似,傳入參數上只有prarms
  2. userControl最下面一行須要調用控件註冊,並指定模版路徑。

    如下是通用註冊方法,在webutil中聲明。

Demo見viewModel。

  1. viewModel規範

    下圖是一個ts版的demo

    viewModel 由4部分組成

    1. 屬性:包含頁面全部須要的數據源,臨時屬性,搜索條件屬性等
    2. 構造:構造中會初始化全部屬性聲明、字典數據、頁面init方法、驗證控件、模態框等。
    3. 方法:方法聲明的原則爲數據交互使用,全部請求controler獲取或設置數據的方法(WebUtil.ajax)都寫在方法裏。
    4. 事件:頁面全部元素的事件綁定usercontrol回調,如搜索按鈕點擊,下拉框等。

屬性上都應有註釋,若是有依賴關係 能夠用 region 擴起來。

下面是方法的例子,這些方法都用於數據交互。(ts中不寫返回類型默認爲 void)

事件必定要註明用途,控件回調的事件也在這個區域聲明。

下面有一個事件叫 eventShowRelation 顯示關聯套餐彈窗,其中有一行代碼,this.modalRelationPackage.show();

這行代碼 調用的就是彈窗控件的方法(詳見,js控件下的 jquery.sh.popups.js的api)。

控件回調事件寫法

控件初始化綁定方法方法。

 

Knockout的組件能夠實現自定義html標籤的功能。

  1. Knockout 控件庫

  1. PagingControl

    用於knockout通用分頁。

文件結構

paging.css 控件樣式表, PagingControlViewModel,主要邏輯文件,PagingControlView.html控件綁定的html模板。

例子

首先文件引用,在引用了 jquery.js、knockout.js、loading.js、popups.js、webutil.js、的基礎上,引用以下文件。

viewModel 構造裏面其實還有一些參數不過被我刪除了,通常都是字典的傳入,對控件自己沒影響。

文件引用

<!--paging control-->

<link href="~/UserControls/PagingControl/paging.css" rel="stylesheet" />

<script src="~/UserControls/PagingControl/PagingControlModel.js"></script>

<!--main viewModel 頁面主viewModel文件-->

<script src="~/ViewModels/PackageConfig/PackageConfigListViewModel.js"></script>

 

<script>

$(function () {

ko.applyBindings(new PackageListViewModel());

});

</script>

viewModel

首先是模型,沒必要關注具體有什麼,你要在列表上顯示哪些列就寫什麼屬性就能夠了。

而後主viewModel聲明 一個 KnockoutObservableArray 類型的屬性。

/**

* 列表數據源

*/

listPackage: KnockoutObservableArray<PackageListModel>;

在構造中實現聲明。

this. listPackage = ko.observableArray<PackageListModel>([]).extend({

paging: {

pageIndex: 1,

pageSize: 10,

callback() {

self.fnGetData();

}

}

});

 

注意後面的 extend 是擴展 這個 obarray的。

幾個必要的分頁參數 pageIndex pageSize,主要是這個callback,這個callback的做用就是當你點擊頁碼時執行的方法。

原理就是在點擊頁碼後執行callback裏的 fnGetData();方法來刷新 listPackage這個數據源(koarray,數據源更新頁面html也會自動更新。)

 

Ajax Data 參數中事一些搜索用的條件用於獲取數據用。分頁控件主要仍是關注 pageIndex和pageSize

/**

* 獲取套餐列表數據源

*/

fnGetData() {

var self = this;

WebUtil.ajax({

url: formatUrl("/PackageConfig/GetPackageList"),

data: {

pageIndex: self.dataList.pageIndex(),

pageSize: self.dataList.pageSize(),

keywords: self.keywords()

},

type: "post",

dataType: "json",

success(data) {

if (data) {

//設置總條數 須要後臺返回當前這個查詢有多少條數據 以便控件計算頁碼

self.listPackage.SetPageTotal(data.RowCount);

//清空以前的緩存

self. listPackage.removeAll();

         //循環添加

for (var item of data.Datas) {

             //記得必定不要忘記 new koModel

self.dataList.push(new PackageListModel(item));

}

}

}

});

}

Html綁定

我把完整的結構粘貼下來,實際上只有2行高亮顯示的代碼是用於pagingcontrol。

思路就是一個table 下的tbody 中 循環列出模型中的項。加一個分頁控件的綁定。

(注意代碼中 <!—ko *** --><!— /ko--> )是無容器綁定

<div class="table-responsive">

<table class="table table-bordered table-advance">

<thead>

<tr>

<th>

序號

</th>

<th>

模板名稱

</th>

<th>

總面積(㎡)

</th>

<th>

<select class="" data-bind="options:packageTypesDic,optionsText:'Value',optionsValue:'Key',optionsCaption:'套餐類型',value:packageType,event:{change:eventSearch}"></select>

</th>

<th>

<select class="" data-bind="options:packageStatesDic,optionsText:'Value',optionsValue:'Key',optionsCaption:'狀態',value:packageState,event:{change:eventSearch}"></select>

</th>

<th>

建立時間

</th>

<th>

下架工藝/商品

</th>

<th>

排序

</th>

<th>

競品標籤

</th>

<th>

套餐類別

</th>

<th>

套餐模式

</th>

<th style="width: 100px;">

操做

</th>

</tr>

</thead>

<tbody data-bind="foreach:{data:listPackage,as:'item'}">

<tr>

<td>

<!--通用獲取序號方法-->

<p class="form-control-static"><!--ko text:WebUtil.getNum($index(),$parent.dataList.pageIndex(),$parent.dataList.pageSize())--><!--/ko--></p>

</td>

<td>

<p class="form-control-static"> <!--ko text:item.Name--><!--/ko--></p>

</td>

<td>

<p class="form-control-static"> <!--ko text:formatCurrency(item.Area())--><!--/ko--></p>

</td>

<td>

<p class="form-control-static"><!--ko text:item.PackageType--><!--/ko--></p>

</td>

<td>

<p class="form-control-static"> <!--ko text:item.Status--><!--/ko--></p>

</td>

<td>

<p class="form-control-static"> <!--ko text:item.CreateDate--><!--/ko--></p>

</td>

<td>

<p class="form-control-static"><!--ko text:item.OffShelfCount--><!--/ko--></p>

</td>

<td style="width: 90px;">

<input type="text" style="width: 100%;" class="{required:true,integer:true,min:1,max:999999999}" data-bind="value:item.Sort,event:{change:function(){$parent.eventSaveSort($element,item.Id());}}" value="" />

</td>

<td>

<div class="colortag" data-bind="style:{'background-color':'#'+item.Color()}"></div>

</td>

<td>

<p class="form-control-static"><!--ko text:item.ModelType--><!--/ko--></p>

</td>

<td>

<p class="form-control-static"><!--ko text:item.Mode--><!--/ko--></p>

</td>

<td>

<div class="btn-group btn-group-solid btn-group-sm" style="position: absolute;">

<button type="button" class="btn blue dropdown-toggle" data-toggle="dropdown" aria-expanded="false">

<i class="fa fa-ellipsis-horizontal"></i> 操做 <i class="fa fa-angle-down"></i>

</button>

<ul class="dropdown-menu pull-right">

<li>

<!-- #region if-->

<!-- ko if:item.StatusId()==1 -->

<a href="javascript:;" data-bind="click:function(){$parent.eventOffShelf(item.Id())}">

下架

</a>

<!--/ko-->

<!-- #endregion endif-->

<!-- #region if -->

<!-- ko if:item.StatusId()!=1 -->

<a href="javascript:;" data-bind="click:function(){$parent.eventOnShelf(item.Id())}">

上架

</a>

<!--/ko-->

<!-- #endregion endif-->

</li>

<li>

<a href="javascript:;" data-bind="attr:{href:formatUrl('/PackageConfig/Edit?packageId='+item.Id()+'&sitemap='+encodeURI('套餐研發~套餐設計器~編輯套餐'))}">

編輯

</a>

</li>

<li>

<a href="javascript:;" data-bind="click:function(){$parent.eventShowCopy(item)}">

複製

</a>

</li>

<!-- #region if -->

<!-- ko if:item.StatusId()==0-->

<li>

<a href="javascript:;" data-bind="click:function(){$parent.eventDelPackage(item.Id())}">

刪除

</a>

</li>

<!--/ko-->

<!-- #endregion endif-->

<li>

<a data-bind="attr:{href:formatUrl('/PackageConfig/TagManage?packageId='+item.Id()+'&sitemap='+encodeURI('套餐研發~套餐設計器~標籤管理'))}">

標籤管理

</a>

</li>

<li>

<a href="javascript:;" data-bind="click:function(){$parent.eventShowRelation(item.Id(),item.Name())}">

套餐關聯

</a>

</li>

</ul>

</div>

</td>

</tr>

</tbody>

</table>

</div>

<pagingcontrol params="{paging:listPackage}"></pagingcontrol>

 

環境搭建

依賴包:

Install-Package jquery.TypeScript.DefinitelyTyped -Version 1.9.9

Install-Package jQuery -Version 1.11.1

Install-Package linq.TypeScript.DefinitelyTyped

Install-Package linq.js -Version 2.2.0.2

Install-Package knockout.TypeScript.DefinitelyTyped

Install-Package knockoutjs -Version 3.4.0

依賴插件:

vs2015及以上*

https://www.tslang.cn/index.html#download-links * 下載 對應版本

http://www.vswebessentials.com/ (經常使用工具)

resharper 10 以上(智能提示)

WebCompiler(less編譯)

問題彙總

  1. 若是是 固定標籤以內的組件綁定 須要使用無容器方式,不能使用自定義標籤方式。

  2. 組件綁定時在params上傳遞的值必須是監聽屬性(也就是說productId,切記不能 使用productId())這樣作在productId更新時,組件會被重置。

    若是有一天你跟斷點事遇到一個component被無限初始化的時候,先看一下是否是傳入參數有問題。

  3. 組件支持套用,能夠組件套組件,今天遇到了一個狀況(外層組件被初始化2次,而內部組件初始化不走),後來仔細檢查,發現內層組件的register時傳入的viewModel是外層組件的,這是個低級錯誤。

    寫組件時不要忘記檢查 組件名,viewModel和模板路徑。

更新日誌

2017-04-06 09:02:43 添加環境搭建教程,添加項目問題彙總,添加更新日誌記錄。

2017-04-19 10:57:17 問題彙總更新了2個問題。WebUtil添加事件總線機制,component之間的交互在也不用來回傳遞事件了。

相關文章
相關標籤/搜索