zStack 扩展开发流程-zstack-dashboard部分

来源:互联网 发布:php支付宝sdk下载 编辑:程序博客网 时间:2024/06/10 00:34

本流程是基于开源项目zstack的扩展开发流程。以一个具体业务为例进行说明

目标

添加一个新的业务模块:公有云账户管理。实现效果为在界面的左边计算选项添加一个账户管理。在账户管理中可以添加、删除账户信息。

流程

前端zstack-dashboard

在前端主要是界面UI的开发,以及相关API的设计。

一、创建html文件
编辑位置:[zStack-dashboard/zstack_dashboard/static/templates]

在此路径下添加需要添加的文件夹,命名为pubAccount。文件夹下面创建pubAccount.html文件。此文件为主界面的显示文件。createPubAccount.html,为创建账户信息的显示界面。

pubAccount.html

    <div style="display: none">        <div z-create-pubaccount="winNewPubAccount" z-options="optionsCreateCluster"></div>       //!!!!!这里的命名需要注意,z-create-pubaccount需要与js部分的命名一直        <div z-search="search" z-options="optionsSearch"></div>         </div>    <div>      <h3 class="z-h3">账户管理</h3>     <div class="btn-group-sm z-btn-bar">    <button type="button" class="btn btn-default btn-sm" ng-click="funcRefresh()">            <i class="fa fa-refresh"></i>        </button>    <button type="button" class="btn btn-default btn-sm" ng-click="funcSearch(search)">            <i class="fa fa-search"></i>        </button>        <div z-sort-by z-options="optionsSortBy"></div>        <button z-popover="popoverFilterBy" type="button" id="vmInstanceFilter" z-content-id="vmInstanceFilterContent" class="btn btn-default btn-sm" ng-click="filterBy.open(popoverFilterBy)">            <i class="fa fa-filter"></i><span>&nbsp; {{filterBy.getButtonName()}}</span>        </button>        <div class="btn-group" >            <button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown"  >               添加账户信息<span class="caret"></span>            </button>            <ul class="dropdown-menu" role="menu">                <li><a href ng-click="funcCreatePubAccount(winNewPubAccount)" >添加云厂商信息</a></li>         //!!!!功能函数在js部分实现,传入参数命名和上面的一样                 <li class="divider"></li>            </ul>        </div>        <div id="vmInstanceFilterContent" style="display: none">            <form class="form">                <div class="form-group">                    <label for="fieldList" class="z-block-label">{{"vm.vm.FILTER BY" | translate}}</label>                    <select id="fieldList" kendo-drop-down-list k-options="filterBy.fieldList" ng-model="filterBy.field"></select>                </div>                <div class="form-group">                    <label class="z-block-label" for="valueList">{{"vm.vm.VALUE" | translate}}</label>                    <select kendo-drop-down-list id="valueList" ng-disabled="filterBy.isValueListDisabled()" k-options="filterBy.valueList" ng-model="filterBy.value"></select>                </div>                <button type="button" class="btn btn-sm btn-primary" ng-click="filterBy.confirm(popoverFilterBy)">{{"vm.vm.Confirm" | translate}}</button>            </form>        </div>        <div class="btn-group" ng-show="funcIsActionShow()">            <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" ng-disabled="funcIsActionDisabled()">                {{"vm.vm.Action" | translate}} <span class="caret"></span>            </button>            <ul class="dropdown-menu" role="menu">                <li><a href ng-click="action.stop()" ng-show="action.isActionShow('stop')">修改</a></li>                <li class="divider"></li>                <li><a href style="color:red" ng-click="funcDeletepubAccount(pubAccountDelete)"  ng-show="action.isActionShow('delete')">{{"vm.vm.Delete" | translate}}</a></li>            </ul>        </div>    </div>    <div kendo-grid="pubAccountGrid" k-options="oPubAccountGrid.options" z-grid-double-click="funcGridDoubleClick" class="z-flat-table"></div>   //!!!此处是js部分的,描述表格信息的,k-options需要和js部分一致    <p class="z-hint">{{"vm.vm.HINT1" | translate}}</p>    <p class="z-hint">{{"vm.vm.HINT2" | translate}}</p>    </div>

createPubAccount.html

<div kendo-window="winCreatePubAccount__" k-visible="false"   k-options="winCreatePubAccountOptions__">       //修改window参数为js部分    <div class="row">        <div class="col-sm-15">            <div class="tab-content">                <div id="createClusterInfo" class="tab-pane active">                    <div class="alert alert-warning col-sm-21" ng-show="!infoPage.hasPubCloudType()">                       没有已经注册的云类型                    </div>                    <h4 class="z-win-h4">账户管理</h4>                    <form class="form">                        <div class="form-group col-sm-24">                            <label for="PubCloudType">云厂商</label>                            <select id="PubCloudType" kendo-drop-down-list k-options="PubCloudTypeList" class="z-win-dropdown" ng-model="infoPage.PubCloudType"></select>                        </div>                        <div class="form-group col-lg-18">                        <p class="z-hint">请根据该类型资源所需的必填项进行填写</p>                            <label for="name">用户名</label>                            <input class="form-control" type="text" id="name" placeholder="(Required) max length of 255 characters" ng-model="modelCreateZone.name" required data-message="name is required">                        </div>                        <div class="form-group col-lg-18">                            <label for="name">密码</label>                            <input class="form-control" type="text" id="name" placeholder="(Required) max length of 255 characters" ng-model="modelCreateZone.name" required data-message="name is required">                        </div>                        <div class="form-group col-lg-18">                            <label for="name">accesskeyID</label>                            <input class="form-control" type="text" id="name" placeholder="(Required) max length of 255 characters" ng-model="modelCreateZone.name" required data-message="name is required">                        </div>                        <div class="form-group col-lg-18">                            <label for="name">accesskeyKey</label>                            <input class="form-control" type="text" id="name" placeholder="(Required) max length of 255 characters" ng-model="modelCreateZone.name" required data-message="name is required">                        </div>                        <div class="form-group col-lg-18">                            <label for="name">token</label>                            <input class="form-control" type="text" id="name" placeholder="(Required) max length of 255 characters" ng-model="modelCreateZone.name" required data-message="name is required">                        </div>                        <div class="form-group col-lg-18">                            <label for="description">{{"zone.zone.DESCRIPTION" | translate}}</label>                            <textarea  class="form-control" rows="5" id="description" placeholder="(Optional) max length of 2048 characters" ng-model="modelCreateZone.description"></textarea>                        </div>                    </form>                </div>                <div class="form-group col-sm-18">                    <button type="button" class="btn btn-default" ng-click="button.previousClick()" ng-disabled="!button.canPreviousProceed()">{{"cluster.createCluster.Previous" | translate}}</button>                    <button type="button" class="btn btn-primary" ng-disabled="!button.canNextProceed()" ng-click="button.nextClick()">{{button.nextButtonName()}}</button>                </div>            </div>        </div>        <div class="col-sm-7 z-wizard-bar">            <ul class="nav">            <!-- 注意根据具体有几个页面进行修改-->                <li class="active"><a data-target="#createClusterInfo" ng-click="button.pageClick('createClusterInfo')">{{"cluster.createCluster.CLUSTER INFO" | translate}}</a></li>            </ul>        </div>    </div></div>

二、添加JS部分
编辑文件:[vStack-dashboard/zstack_dashboard/static/app/app.js]
js部分需要实现angularJS的代码.

var MPubAccount;(function (MPubAccount) {  var PubAccount = (function (_super) {       __extends(PubAccount, _super);       function PubAccount() {           _super.apply(this, arguments);       }       PubAccount.prototype.progressOn = function () {           this.inProgress = true;       };       PubAccount.prototype.progressOff = function () {           this.inProgress = false;       };       PubAccount.prototype.isInProgress = function () {           return this.inProgress;       };       PubAccount.prototype.isEnableShow = function () {           return this.state == 'Disabled';       };       PubAccount.prototype.isDisableShow = function () {           return this.state == 'Enabled';       };       PubAccount.prototype.stateLabel = function () {           if (this.state == 'Enabled') {               return 'label label-success';           }           else if (this.state == 'Disabled') {               return 'label label-danger';           }           else {               return 'label label-default';           }       };       PubAccount.prototype.gridColumnLabel = function () {           if (this.state == 'Enabled') {               return 'z-color-box-green';           }           else if (this.state == 'Disabled') {               return 'z-color-box-red';           }       };//描述在账户管理中有哪些数据       PubAccount.prototype.updateObservableObject = function (inv) {           // self : ObservableObject           var self = this;           self.set('name', inv.name);           self.set('description', inv.description);           self.set('hypervisorType', inv.hypervisorType);           self.set('state', inv.state);           self.set('createDate', inv.createDate);           self.set('lastOpDate', inv.lastOpDate);            self.set('accesskey', inv.accesskey);       };       return PubAccount;   }(ApiHeader.PubAccountInventory));     //!!!!PubAccountInventory需要在前面创建   MPubAccount.PubAccount = PubAccount;   var PubAccountManager = (function () {       function PubAccountManager(api, $rootScope) {           this.api = api;           this.$rootScope = $rootScope;       }       PubAccountManager.prototype.setSortBy = function (sortBy) {           this.sortBy = sortBy;       };       PubAccountManager.prototype.wrap = function (pubAccount) {           return new kendo.data.ObservableObject(pubAccount);       };       PubAccountManager.prototype.create = function (pubAccount, done) {           var _this = this;           var msg = new ApiHeader.APICreatePubAccountMsg();           msg.name = pubAccount.name;           msg.description = pubAccount.description;           msg.pubCloudTyoe = pubAccount.pubCloudTyoe;           this.api.asyncApi(msg, function (ret) {           var c = new PubAccount();           angular.extend(c, ret.inventory);           done(_this.wrap(c));           _this.$rootScope.$broadcast(MRoot.Events.NOTIFICATION, {               msg: Utils.sprintf('Created new pubAccount: {0}', c.name),               link: Utils.sprintf('/#/pubAccount', c.uuid)            });           });         };       PubAccountManager.prototype.query = function (qobj, callback) {           var _this = this;           var msg = new ApiHeader.APIQueryPubAccountMsg(); //!!!!APIQueryPubAccountMsg需要在前面创建           msg.count = qobj.count === true;           msg.start = qobj.start;           msg.limit = qobj.limit;           msg.replyWithCount = true;           msg.conditions = qobj.conditions ? qobj.conditions : [];           if (Utils.notNullnotUndefined(this.sortBy) && this.sortBy.isValid()) {               msg.sortBy = this.sortBy.field;               msg.sortDirection = this.sortBy.direction;           }           this.api.syncApi(msg, function (ret) {               var clusters = [];               ret.inventories.forEach(function (inv) {                   var c = new PubAccount();                   angular.extend(c, inv);                   clusters.push(_this.wrap(c));               });               callback(clusters, ret.total);           });       };       PubAccountManager.prototype.delete = function (pubAccount, done) {           var _this = this;           pubAccount.progressOn();           var msg = new ApiHeader.APIDeletePubAccountMsg();   //!!!!APIDeletePubAccountMsg需要在前面创建           msg.uuid = pubAccount.uuid;           this.api.asyncApi(msg, function (ret) {               pubAccount.progressOff();               done(ret);               _this.$rootScope.$broadcast(MRoot.Events.NOTIFICATION, {                   msg: Utils.sprintf('Deleted cluster: {0}', pubAccount.name)               });           });       };       PubAccountManager.$inject = ['Api', '$rootScope'];       return PubAccountManager;   }());   MPubAccount.PubAccountManager = PubAccountManager;   var PubAccountModel = (function () {       function PubAccountModel() {           this.current = new PubAccount();       }       PubAccountModel.prototype.resetCurrent = function () {           this.current = null;       };       PubAccountModel.prototype.setCurrent = function ($scope, PubAccount) {           this.current = PubAccount;       };       return PubAccountModel;   }());   MPubAccount.PubAccountModel = PubAccountModel;   var oPubAccountGrid = (function (_super) {       __extends(oPubAccountGrid, _super);       function oPubAccountGrid($scope, pubAccountMgr) {           _super.call(this);           this.pubAccountMgr = pubAccountMgr;           _super.prototype.init.call(this, $scope, $scope.pubAccountGrid);           //描述表格格式           this.options.columns = [               {                   field: 'username',                   title: 'username',                   width: '15%',                   template: '<a href="/\\#/cluster/{{dataItem.uuid}}">{{dataItem.name}}</a>'               },                {                   field: 'accesskey',                   title: 'accesskey',                   width: '15%',                   template: '<a href="/\\#/cluster/{{dataItem.uuid}}">{{dataItem.name}}</a>'               },               {                   field: 'token',                   title: 'token',                   width: '15%'               },               {                   field: 'description',                   title: 'description',                   width: '25%'               },               {                   field: 'state',                   title: 'state',                   width: '15%',                   template: '<span class="{{dataItem.stateLabel()}}">{{dataItem.state}}</span>'               },               {                   field: 'uuid',                   title: '{{"cluster.ts.UUID" | translate}}',                   width: '30%'               }           ];           this.options.dataSource.transport.read = function (options) {               var qobj = new ApiHeader.QueryObject();               qobj.limit = options.data.take;               qobj.start = options.data.pageSize * (options.data.page - 1);               pubAccountMgr.query(qobj, function (pubAccounts, total) {                   options.success({                       data: pubAccounts,                       total: total                   });               });           };       }       return oPubAccountGrid;   }(Utils.OGrid));   var Action = (function () {       function Action($scope, pubAccountMgr) {           this.$scope = $scope;           this.pubAccountMgr = pubAccountMgr;       }       Action.prototype.enable = function () {           this.pubAccountMgr.enable(this.$scope.model.current);       };       Action.prototype.disable = function () {           this.pubAccountMgr.disable(this.$scope.model.current);       };       return Action;   }());   var FilterBy = (function () {       function FilterBy($scope, hypervisorTypes) {           var _this = this;           this.$scope = $scope;           this.hypervisorTypes = hypervisorTypes;           this.fieldList = {               dataSource: new kendo.data.DataSource({                   data: [                       {                           name: '{{"cluster.ts.None" | translate}}',                           value: FilterBy.NONE                       },                       {                           name: '{{"cluster.ts.State" | translate}}',                           value: FilterBy.STATE                       },                       {                           name: '{{"cluster.ts.Hypervisor" | translate}}',                           value: FilterBy.HYPERVISOR                       }                   ]               }),               dataTextField: 'name',               dataValueField: 'value'           };           this.valueList = {               dataSource: new kendo.data.DataSource({                   data: []               })           };           this.field = FilterBy.NONE;           $scope.$watch(function () {               return _this.field;           }, function () {               if (_this.field == FilterBy.NONE) {                   _this.valueList.dataSource.data([]);                   _this.value = null;               }               else if (_this.field == FilterBy.STATE) {                   _this.valueList.dataSource.data(['Enabled', 'Disabled']);               }               else if (_this.field == FilterBy.HYPERVISOR) {                   _this.valueList.dataSource.data(_this.hypervisorTypes);               }           });       }       FilterBy.prototype.confirm = function (popover) {           console.log(JSON.stringify(this.toKendoFilter()));           this.$scope.oPubAccountGrid.setFilter(this.toKendoFilter());           this.name = !Utils.notNullnotUndefined(this.value) ? null : Utils.sprintf('{0}:{1}', this.field, this.value);           popover.toggle();       };       FilterBy.prototype.open = function (popover) {           popover.toggle();       };       FilterBy.prototype.isValueListDisabled = function () {           return !Utils.notNullnotUndefined(this.value);       };       FilterBy.prototype.getButtonName = function () {           return this.name;       };       FilterBy.prototype.toKendoFilter = function () {           if (!Utils.notNullnotUndefined(this.value)) {               return null;           }           return {               field: this.field,               operator: 'eq',               value: this.value           };       };       FilterBy.NONE = 'none';       FilterBy.STATE = 'state';       FilterBy.HYPERVISOR = 'hypervisorType';       return FilterBy;   }());   var Controller = (function () {       function Controller($scope, pubAccountMgr, $location) {    //!!!根据具体的业务传入参数           this.$scope = $scope;           this.pubAccountMgr = pubAccountMgr;           this.$location = $location;           $scope.model = new  PubAccountModel();              $scope.oPubAccountGrid = new oPubAccountGrid($scope, pubAccountMgr);           $scope.action = new Action($scope, pubAccountMgr);           $scope.optionsSortBy = {               fields: [                   {                       name: '{{"cluster.ts.Name" | translate}}',                       value: 'name'                   },                   {                       name: '{{"cluster.ts.Description" | translate}}',                       value: 'Description'                   },                   {                       name: '{{"cluster.ts.State" | translate}}',                       value: 'state'                   },                   {                       name: '{{"cluster.ts.Hypervisor" | translate}}',                       value: 'hypervisorType'                   },                   {                       name: '{{"cluster.ts.Created Date" | translate}}',                       value: 'createDate'                   },                   {                       name: '{{"cluster.ts.Last Updated Date" | translate}}',                       value: 'lastOpDate'                   }               ],               done: function (ret) {                   pubAccountMgr.setSortBy(ret);                   $scope.oPubAccountGrid.refresh();               }           };           $scope.optionsSearch = {               fields: ApiHeader.ClusterInventoryQueryable,               name: 'CLUSTER',               schema: {                   state: {                       type: Directive.SearchBoxSchema.VALUE_TYPE_LIST,                       list: ['Enabled', 'Disabled']                   },                   hypervisorType: {                       type: Directive.SearchBoxSchema.VALUE_TYPE_LIST,                       list: this.hypervisorTypes                   },                   createDate: {                       type: Directive.SearchBoxSchema.VALUE_TYPE_TIMESTAMP                   },                   lastOpDate: {                       type: Directive.SearchBoxSchema.VALUE_TYPE_TIMESTAMP                   }               },               done: function (ret) {                   var qobj = new ApiHeader.QueryObject();                   qobj.conditions = ret;                   pubAccountMgr.query(qobj, function (clusters, total) {                       $scope.oPubAccountGrid.refresh(clusters);                   });               }           };           $scope.filterBy = new FilterBy($scope, this.hypervisorTypes);           $scope.funcSearch = function (win) {               win.open();           };           $scope.funcCreatePubAccount = function (win) {    //!!!!创建窗口函数,和html页面一致               win.open();           };           $scope.funcDeletePubAccount = function (win) {               $scope.deletePubAccount.open();           };           $scope.optionsDeletePubAccount = {               title: 'DELETE PubAccount',               description: 'Are you sure?',               confirm: function () {                   pubAccountMgr.delete($scope.model.current, function (ret) {                       $scope.oPubAccountGrid.deleteCurrent();                   });               }           };           $scope.funcRefresh = function () {               $scope.oPubAccountGrid.refresh();           };           $scope.funcIsActionShow = function () {               return !Utils.isEmptyObject($scope.model.current);           };           $scope.funcIsActionDisabled = function () {               return Utils.notNullnotUndefined($scope.model.current) && $scope.model.current.isInProgress();           };           $scope.optionsCreatePubAccount= {               done: function (cluster) {                   $scope.oPubAccountGrid.add(cluster);               }           };       }       Controller.$inject = ['$scope', 'PubAccountManager', '$location'];       return Controller;   }());   MPubAccount.Controller = Controller;   var CreatePubAccountOptions = (function () {       function CreatePubAccountOptions() {       }       return CreatePubAccountOptions;   }());   MPubAccount.CreatePubAccountOptions = CreatePubAccountOptions;   var CreatePubAccount = (function () {       function CreatePubAccount(api, pubAccountMgr) {           var _this = this;           this.api = api;           this.pubAccountMgr = pubAccountMgr;           this.scope = true;           this.link = function ($scope, $element, $attrs, $ctrl, $transclude) {               var instanceName = $attrs.zCreatePubaccount;    //!!!!!与html部分一致               var parentScope = $scope.$parent;               parentScope[instanceName] = _this;               _this.options = new CreatePubAccountOptions();   //注意修改               var optionName = $attrs.zOptions;               if (angular.isDefined(optionName)) {                   _this.options = parentScope[optionName];                   $scope.$watch(function () {                       return parentScope[optionName];                   }, function () {                       _this.options = parentScope[optionName];                   });               }               var infoPage = $scope.infoPage = {                   activeState: true,                   name: null,                   description: null,                   accesskey: null,                   canMoveToPrevious: function () {                       return false;                   },                   hasPubCloudType: function () {    //注意修改                       return $scope.PubCloudTypeList.dataSource.data().length > 0;                   },                   canMoveToNext: function () {                       return true;                   },                   show: function () {                       this.getAnchorElement().tab('show');                   },                   getAnchorElement: function () {                       return $('.nav a[data-target="#createPubAccountInfo"]');                   },                   active: function () {                       this.activeState = true;                   },                   isActive: function () {                       return this.activeState;                   },                   getPageName: function () {                       return 'createPubAccountInfo';                   },                   reset: function () {                       this.name = Utils.shortHashName("PubAccount");                       this.PubCloudType = null;                       this.description = null;                       this.username = null;                       this.password = null;                       this.accesskey = null;                       this.accessID = null;                       this.token = null;                       this.activeState = false;                   }               };               var mediator = $scope.mediator = {                   currentPage: infoPage,                   movedToPage: function (page) {                       $scope.mediator.currentPage = page;                   },                   finishButtonName: function () {                       return "Create";                   },                   finish: function () {                       var resultCluster;                       var chain = new Utils.Chain();                       chain.then(function () {                           pubAccountMgr.create(infoPage, function (ret) {                               resultCluster = ret;                               chain.next();                           });                       }).done(function () {                           if (Utils.notNullnotUndefined(_this.options.done)) {                               _this.options.done(resultCluster);                           }                           $scope.winCreatePubAccount__.close();    //注意修改                       }).start();                   }               };               $scope.button = new Utils.WizardButton([                   infoPage   //注意修改               ], mediator);               $scope.PubCloudTypeList = {    //注意修改                   dataSource: new kendo.data.DataSource({ data: [] }),                   dataTextField: "type",                   dataValueField: "type"               };               $scope.winCreatePubAccountOptions__ = {    //注意修改                   width: "700px",                   //height: "518px",                   animation: false,                   modal: true,                   draggable: false,                   resizable: false               };               _this.$scope = $scope;           };           this.restrict = 'EA';           this.replace = true;           this.templateUrl = '/static/templates/account/createPubAccount.html';    //注意修改       }        CreatePubAccount.prototype.open = function () {           var _this = this;           var win = this.$scope.winCreatePubAccount__;   //注意修改           this.$scope.button.reset();           var chain = new Utils.Chain();           chain.then(function () {               chain.next();//               _this.api.getPubCloudType(function (pubcloudTypes) {//                   var types = [];//                   angular.forEach(pubcloudTypes, function (item) {//                       types.push({ type: item });//                   });//                   _this.$scope.PubCloudTypeList.dataSource.data(new kendo.data.ObservableArray(types));//                   _this.$scope.model.pubCloudTypes = pubcloudTypes[0];//                   chain.next();//               });           }).done(function () {               win.center();               win.open();           }).start();       };       return CreatePubAccount;   }());   MPubAccount.CreatePubAccount = CreatePubAccount;})(MPubAccount || (MPubAccount = {}));//注意修改angular.module('root').factory('PubAccountManager', ['Api', '$rootScope', function (api, $rootScope) {       return new MPubAccount.PubAccountManager(api, $rootScope);   }]).directive('zCreatePubaccount', ['Api', 'PubAccountManager',   function (api, PubAccountManager) {       return new MPubAccount.CreatePubAccount(api, PubAccountManager);   }]).config(['$routeProvider', function (route) {       route.when('/pubAccount', {           templateUrl: '/static/templates/account/account.html',           controller: 'MPubAccount.Controller',       });   }]);

三、添加API
编辑文件:【zStack-dashboard/zstack_dashboard/api_messages.py】
在这个文件中将每个在js中所新添加的api放到这里

关于java部分请查看zStack 扩展开发流程-zstack-java部分

0 0