首页 > 代码库 > Knockout Mvc Compoment FrameSet

Knockout Mvc Compoment FrameSet

Knockout Mvc Compoment FrameSet

框架文件结构

 技术分享

 

01-   网站(表现层),mvc主要作用视图展示。

02-   模型(Model),主要作用承载视图数据结构,网站前后台数据交互模型。

03-   业务(Business),业务逻辑层(含Ibusiness),视图接口定义,从服务过来的Dto通过Business转换成Model。

04-   单元测试(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,业务实现。

 

4、     单元测试文件结构

框架基础

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响应。(后面会详细说明)

2、菜单生成原理

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

 技术分享

 

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

3、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;

 

 

2、      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");

3、      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>

*/

4、      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;

        }

5、      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;

            })()

        });

6、      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="http://www.mamicode.com/${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="http://www.mamicode.com/${Name}">

        <a href="http://www.mamicode.com/${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>

 

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

3)   开头字母大写,与后台模型属性名想对应.

4)   构造函数要添加可空any类型参数。

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

2、html/cshtml规范

html书写规范遵循语义化的标准写法

比如声明一个按钮<button  class=”btn btn-default”>确定</button>

尽量不要写 <a href=”javascript:;” class=”btn btn-default”>确定</a>

 

1)  容器布局:遵循bootstrap的标签套用原则,不应有多余标签。

 技术分享

 

所有内容都应放在row下的col里。

2)  标签页:在Metronic的布局标准下,标签页的容器应为row和col。

 技术分享

 

代码详见Metronic模版。

3)  页面html行数较多时要添加region标签。Ctrl+k,s

技术分享

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

 

 

3、controller business model 规范

1)       Controller 负责相应页面请求,给页面传入字典和接收页面返回值的作用。

2)Bussiness调用服务接口,简单逻辑处理,模型转换等。

4、 knockout 组件规范

组件 在项目中被命名为UserControl

1)         userControl编码规则详见viewmodel,他们的声明方式类似,传入参数上只有prarms。

2)         userControl最下面一行需要调用控件注册,并指定模版路径。

以下是通用注册方法,在webutil中声明。

 技术分享

 

5、 viewModel规范

下图是一个ts版的demo

viewModel 由4部分组成

1)  属性:包含页面所有需要的数据源,临时属性,搜索条件属性等

2)  构造:构造中会初始化所有属性声明、字典数据、页面init方法、验证控件、模态框等。

3)  方法:方法声明的原则为数据交互使用,所有请求controler获取或设置数据的方法(WebUtil.ajax)都写在方法里。

4)  事件:页面所有元素的事件绑定usercontrol回调,如搜索按钮点击,下拉框等。

 技术分享

 

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

 技术分享

 

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

 技术分享

 

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

 技术分享

 

控件回调事件写法

 技术分享

 

Knockout Mvc Compoment FrameSet