首页 > 代码库 > Knockout Mvc Compoment FrameSet – auto publish

Knockout Mvc Compoment FrameSet – auto publish

Knockout Mvc Compoment FrameSet

框架文件结构

技术分享

  1. 网站(表现层),mvc主要作用视图展示。
  2. 模型(Model),主要作用承载视图数据结构,网站前后台数据交互模型。
  3. 业务(Business),业务逻辑层(含Ibusiness),视图接口定义,从服务过来的Dto通过Business转换成Model。
  4. 单元测试(test),Controller中方法的单元测试。

1、网站文件结构

技术分享

App_Data

App_Start

应用程序启动时自动调用的配置。

技术分享

BundleConfig.cs mvc压缩css和js的配置文件。

FilterConfig.cs mvc过滤器配置,站点地图权限登录菜单管理的入口。

RotuteConfig.cs 路由规则配置。

Webstack 文件夹下含有

ExceptionFilter.cs 网站程序异常处理过滤器。

        MvcMenuFilter.cs 框架权限验证和菜单处理主要程序。

        WebSiteExcetpion.cs 自定义网站错误类型。用于抛出登信息丢失等自定义业务异常。

assest

技术分享

网站主要引用的资源文件(样式,常用组件等)。现应用的是metronic_v4.1.0。

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

网站配置文件。

 

2、模型文件结构

技术分享

通过业务区分业务文件夹。

3、业务文件结构

技术分享

  1. IBusiness,业务接口。
  2. Buniness,业务实现。

 

  1. 单元测试文件结构

框架基础

  1. 登录原理

登录流程:

1、【统一登录】像Session中添加登录信息

2、【网站过滤器验证】 (MvcMenuFilter.cs 需要在FilterConfig中注册)

技术分享

每一个action请求时会判断当前Session中是否有登录信息。

如果登录信息为空,判断请求方式是同步请求还是异步请求。

同步请求,则通过Redrect 跳转到登录页。

异步请求,则通过throw new WebSiteException ,错误码为1000.

3、异常处理。

所有网站异常都会被ExceptionFilter.cs捕获。

ExceptionFilter 可以记录错误日志,日志路径 在网站跟目录的Log文件夹下。

自定义异常,字典也声明在这个文件中。

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

{

{ "1000","登录信息丢失"}

};

如果抛出的是WebSiteException则会通过字典方式相应。

这个抛出的结果会被框架通WebUtil.js响应。(后面会详细说明)

  1. 菜单生成原理

    登录获取的用户信息中会包含菜单信息。

    技术分享

    获取当前系统编号后,可以通过系统编号拿到该用户当前登录系统的菜单,并转换成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使用

  1. 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 {}

*/

encodeHtml(str: string): string;

/**

* 停止冒泡事件

* @param {} event

* @returns {}

* 作者:崔园清

* 小组:山河web

* 说明:停止冒泡事件;

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

* 版本号:v1.0

*/

stopEvent(event: any): void;

 

/**

* 将一个string值转换为时间

* @param {} value

* @returns {}

*/

parseDate(value: string): Date;

 

/**

* 获取url的参数

* @param {} name

* @returns {}

*/

getQueryString(name: string): string;

/**

* ajax

* @param option

* @returns {}

*/

ajax(option: WebUtilAjaxOption);

ajax中定义了一个错误的Handler 其中包含一些通用的自定义错误码

errorHandler = {

"1000": function () {

sh.alert("您的登录已失效,请重新登录。", function () {

location.href = http://www.mamicode.com/"/Account/Login";

});

},

"1001": function () {

sh.alert("您的企业信息丢失,请重新选择企业。", function () {

location.href = http://www.mamicode.com/formatUrl("/Home/EnterpriseSet");

});

},

"1002": function () {

sh.alert("您已在当前企业离职。", function () {

location.href = http://www.mamicode.com/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;

}

 

declare var WebUtil: WebUtilStatic;

 

interface shStatic {

/**

* 通用提示框方法

* @param msg

* @param callback

* @param msgtitle

* @returns {}

*/

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

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

 

}

 

declare var sh: shStatic;

 

interface KnockoutStatic {

/**

* 注册控件通用方法

* @param controlName

* @param viewModel

* @param templateUrl

* @returns {}

*/

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

}

registerControl方法会造成很多次异步html请求,正在想办法解决。

formatCurrency

/**

* 将数字转换为 格式化后的金钱字符串

* @param num

*/

declare function formatCurrency(num: number): string;

formatUrl

/**

* 处理虚拟目录格式化地址的方法 在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;

 

Guid

/**

* 定义一个Guid接口

*/

interface GuidStatic {

Empty: string;

}

/**

* 定义一个Guid静态类

*/

declare var Guid: GuidStatic;

/**

* 定义String静态方法

*/

interface StringConstructor {

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

}

 

 

Date通用

/**

* 时间通用处理

*/

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

/**

* 键值对

*/

interface KeyValuePair2<TKey, TValue> {

Key: TKey;

Value: TValue;

}

 

/**

* 键值对参数对象

*/

interface IKeyVaulePair {

Key: any;

Value: any;

}

 

/**

* 键值对委托方法

*/

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

 

 

  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中声明。

技术分享

  1. viewModel规范

    下图是一个ts版的demo

    viewModel 由4部分组成

    1. 属性:包含页面所有需要的数据源,临时属性,搜索条件属性等
    2. 构造:构造中会初始化所有属性声明、字典数据、页面init方法、验证控件、模态框等。
    3. 方法:方法声明的原则为数据交互使用,所有请求controler获取或设置数据的方法(WebUtil.ajax)都写在方法里。
    4. 事件:页面所有元素的事件绑定usercontrol回调,如搜索按钮点击,下拉框等。

    技术分享

属性上都应有注释,如果有依赖关系 可以用 region 扩起来。

技术分享

下面是方法的例子,这些方法都用于数据交互。(ts中不写返回类型默认为 void)

技术分享

事件一定要注明用途,控件回调的事件也在这个区域声明

技术分享

控件回调事件写法

技术分享

Knockout Mvc Compoment FrameSet – auto publish