Commit 41ca65f5 by Jason Song

Merge pull request #222 from lepdou/optimization

优化多处细节&bugfix&表格模式可以删除key
parents b8de6d75 b75a4359
......@@ -8,7 +8,7 @@ import com.ctrip.framework.apollo.biz.entity.Namespace;
public interface NamespaceRepository extends PagingAndSortingRepository<Namespace, Long> {
List<Namespace> findByAppIdAndClusterName(String appId, String clusterName);
List<Namespace> findByAppIdAndClusterNameOrderByIdAsc(String appId, String clusterName);
Namespace findByAppIdAndClusterNameAndNamespaceName(String appId, String clusterName, String namespaceName);
}
......@@ -49,7 +49,7 @@ public class NamespaceService {
}
public List<Namespace> findNamespaces(String appId, String clusterName) {
List<Namespace> groups = namespaceRepository.findByAppIdAndClusterName(appId, clusterName);
List<Namespace> groups = namespaceRepository.findByAppIdAndClusterNameOrderByIdAsc(appId, clusterName);
if (groups == null) {
return Collections.emptyList();
}
......
......@@ -121,6 +121,10 @@ public class AdminServiceAPI {
.format("apps/%s/clusters/%s/namespaces/%s/items", appId, clusterName, namespace),
item, ItemDTO.class).getBody();
}
public void deleteItem( Env env, long itemId){
restTemplate.delete(getAdminServiceHost(env) + "items/" + itemId);
}
}
@Service
......
......@@ -77,6 +77,14 @@ public class PortalConfigController {
return configService.createOrUpdateItem(appId, Env.valueOf(env), clusterName, namespaceName, item);
}
@RequestMapping(value = "/envs/{env}/items/{itemId}", method = RequestMethod.DELETE)
public void deleteItem(@PathVariable String env, @PathVariable long itemId){
if (itemId <= 0){
throw new BadRequestException("item id invalid");
}
configService.deleteItem(Env.valueOf(env), itemId);
}
@RequestMapping(value = "/apps/{appId}/env/{env}/clusters/{clusterName}/namespaces/{namespaceName}/release", method = RequestMethod.POST, consumes = {
"application/json"})
public ReleaseDTO createRelease(@PathVariable String appId,
......
......@@ -81,7 +81,10 @@ public class PortalConfigService {
item.setNamespaceId(namespace.getId());
return itemAPI.createOrUpdateItem(appId, env, clusterName, namespaceName, item);
}
public void deleteItem(Env env, long itemId){
itemAPI.deleteItem(env, itemId);
}
/**
* createRelease config items
......
......@@ -28,10 +28,6 @@ public class PropertyResolver implements ConfigTextResolver {
@Override
public ItemChangeSets resolve(long namespaceId, String configText, List<ItemDTO> baseItems) {
if (StringUtils.isEmpty(configText)){
throw new BadRequestException("config text can not be empty");
}
Map<Integer, ItemDTO> oldLineNumMapItem = BeanUtils.mapByKey("lineNum", baseItems);
Map<String, ItemDTO> oldKeyMapItem = BeanUtils.mapByKey("key", baseItems);
......
......@@ -198,7 +198,7 @@
<a data-tooltip="tooltip" data-placement="bottom" title="提交修改"
data-toggle="modal" data-target="#commitModal"
ng-show="namespace.isTextEditing && namespace.viewType == 'text'"
ng-click="saveDraft(namespace)">
ng-click="setCommitNamespace(namespace)">
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
</a>
......@@ -249,17 +249,17 @@
<tr ng-repeat="config in namespace.items" ng-class="{warning:config.isModified}"
ng-if="config.item.key">
<td width="20%">
<span ng-bind="config.item.key | limitTo: 20"></span>
<span ng-bind="config.item.key.length > 20 ? '...' :''"></span>
<td width="20%" title="{{config.item.key}}">
<span ng-bind="config.item.key | limitTo: 250"></span>
<span ng-bind="config.item.key.length > 250 ? '...' :''"></span>
</td>
<td width="25%">
<span ng-bind="config.item.value | limitTo: 20"></span>
<span ng-bind="config.item.value.length > 20 ? '...': ''"></span>
<td width="25%" title="{{config.item.value}}">
<span ng-bind="config.item.value | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="20%">
<span ng-bind="config.item.comment | limitTo: 20"></span>
<span ng-bind="config.item.comment.length > 20 ?'...' : ''"></span>
<td width="20%" title="{{config.item.comment}}">
<span ng-bind="config.item.comment | limitTo: 250"></span>
<span ng-bind="config.item.comment.length > 250 ?'...' : ''"></span>
</td>
<td width="10%" ng-bind="config.item.lastModifiedBy">
</td>
......@@ -279,6 +279,12 @@
data-toggle="modal" data-target="#itemModal"
ng-click="editItem(namespace, config.item)">
</span>
&nbsp;
<span class="glyphicon glyphicon-remove" aria-hidden="true"
data-toggle="modal" data-target="#deleteModal"
data-tooltip="tooltip" data-placement="bottom" title="删除"
ng-click="preDeleteItem(config.item.id)">
</span>
</td>
</tr>
......@@ -358,6 +364,29 @@
</div>
</div>
<!-- delete modal-->
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header panel-primary">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title">删除配置</h4>
</div>
<div class="modal-body">
确定要删除配置吗?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger" data-dismiss="modal"
ng-click="deleteItem()">
确定
</button>
</div>
</div>
</div>
</div>
<!--create release modal-->
<form class="modal fade form-horizontal" id="releaseModal" tabindex="-1" role="dialog"
ng-submit="release()">
......@@ -422,24 +451,22 @@
<div class="form-group">
<label class="col-sm-2 control-label"><apollorequiredfiled></apollorequiredfiled>Value</label>
<div class="col-sm-10">
<textarea type="text" class="form-control" rows="4" ng-model="item.value"
ng-required="true" ng-disabled="tableViewOperType == 'retrieve'">
<textarea type="text" class="form-control" rows="6" ng-model="item.value"
ng-required="true" ng-show="tableViewOperType != 'retrieve'">
</textarea>
<span ng-bind="item.value" ng-show="tableViewOperType == 'retrieve'"></span>
</div>
</div>
<div class="form-group" ng-show="tableViewOperType == 'retrieve'">
<label class="col-sm-2 control-label">Released Value</label>
<div class="col-sm-10">
<textarea type="text" class="form-control" rows="4" ng-model="item.oldValue"
ng-disabled="true" ng-show="item.oldVale">
</textarea>
<span ng-show="!item.oldVale">这是新增的配置</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Comment</label>
<div class="col-sm-10">
<textarea class="form-control" ng-model="item.comment" rows="4"
<textarea class="form-control" ng-model="item.comment" rows="2"
ng-disabled="tableViewOperType == 'retrieve'">
</textarea>
......@@ -490,11 +517,15 @@
<script src="vendor/angular/angular-resource.min.js"></script>
<script src="vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="vendor/angular/loading-bar.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-sanitize.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.3.2/ui-bootstrap-tpls.min.js"></script>
<script src="vendor/angular/angular-bootstrap-confirm.min.js"></script>
<!-- bootstrap.js -->
<script src="vendor/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="vendor/bootstrap/js/bootstrap-treeview.min.js" type="text/javascript"></script>
<!--biz script-->
<script type="application/javascript" src="scripts/app.js"></script>
......
......@@ -60,7 +60,7 @@
<label class="control-label">需要同步的配置</label>
</div>
<div class="col-sm-10">
<table class="table table-bordered table-striped table-hover" >
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<td><input type="checkbox" ng-click="toggleItemsCheckedStatus()"></td>
......@@ -74,16 +74,16 @@
<td width="10%"><input type="checkbox" ng-checked="item.checked"
ng-click="switchSelect(item)"></td>
<td width="20%">
<span ng-bind="item.key | limitTo: 30"></span>
<span ng-bind="item.key.length > 30 ? '...' : ''"></span>
<span ng-bind="item.key | limitTo: 250"></span>
<span ng-bind="item.key.length > 250 ? '...' : ''"></span>
</td>
<td width="50%">
<span ng-bind="item.value | limitTo: 45"></span>
<span ng-bind="item.value.length > 45 ? '...' : ''"></span>
<span ng-bind="item.value | limitTo: 250"></span>
<span ng-bind="item.value.length > 250 ? '...' : ''"></span>
</td>
<td width="20%">
<span ng-bind="item.comment | limitTo: 15"></span>
<span ng-bind="item.comment.length > 15 ? '...' :''"></span>
<span ng-bind="item.comment | limitTo: 250"></span>
<span ng-bind="item.comment.length > 250 ? '...' :''"></span>
</td>
</tr>
</tbody>
......
......@@ -11,7 +11,7 @@ var directive_module = angular.module('apollo.directive', ['app.service', 'app.u
// 首页
var index_module = angular.module('index', ['toastr', 'app.service', 'app.util', 'angular-loading-bar']);
//项目主页
var application_module = angular.module('application', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar']);
var application_module = angular.module('application', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar', 'mwl.confirm']);
//创建项目页面
var create_app_module = angular.module('create_app', ['ngResource', 'apollo.directive', 'toastr', 'app.service', 'app.util', 'angular-loading-bar']);
//配置同步页面
......
......@@ -36,13 +36,16 @@ application_module.controller("ConfigNamespaceController",
}
setInterval(function () {
$('[data-tooltip="tooltip"]').tooltip();
$('.namespace-view-table').bind( 'mousewheel DOMMouseScroll', function ( e ) {
var e0 = e.originalEvent,
delta = e0.wheelDelta || -e0.detail;
this.scrollTop += ( delta < 0 ? 1 : -1 ) * 30;
e.preventDefault();
});
$('.namespace-view-table').bind('mousewheel DOMMouseScroll',
function (e) {
var e0 = e.originalEvent,
delta = e0.wheelDelta
|| -e0.detail;
this.scrollTop +=
( delta < 0 ? 1 : -1 ) * 30;
e.preventDefault();
});
}, 2500);
}, function (result) {
......@@ -62,6 +65,7 @@ application_module.controller("ConfigNamespaceController",
};
var MAX_ROW_SIZE = 30;
var APPEND_ROW_SIZE = 8;
//把表格内容解析成文本
function parseModel2Text(namespace) {
......@@ -80,16 +84,17 @@ application_module.controller("ConfigNamespaceController",
itemCnt++;
});
namespace.itemCnt = itemCnt > MAX_ROW_SIZE ? MAX_ROW_SIZE : itemCnt + 3;
namespace.itemCnt =
itemCnt > MAX_ROW_SIZE ? MAX_ROW_SIZE : itemCnt + APPEND_ROW_SIZE;
return result;
}
////// text view oper //////
$scope.draft = {};
//保存草稿
$scope.saveDraft = function (namespace) {
$scope.draft = namespace;
$scope.toCommitNamespace = {};
$scope.setCommitNamespace = function (namespace) {
$scope.toCommitNamespace = namespace;
};
$scope.commitComment = '';
......@@ -97,16 +102,17 @@ application_module.controller("ConfigNamespaceController",
$scope.commitChange = function () {
ConfigService.modify_items($scope.pageContext.appId, $scope.pageContext.env,
$scope.pageContext.clusterName,
$scope.draft.namespace.namespaceName,
$scope.draft.text,
$scope.draft.namespace.id, $scope.commitComment).then(
$scope.toCommitNamespace.namespace.namespaceName,
$scope.toCommitNamespace.text,
$scope.toCommitNamespace.namespace.id,
$scope.commitComment).then(
function (result) {
toastr.success("更新成功");
//refresh all namespace items
$rootScope.refreshNamespaces();
$scope.draft.backupText = '';//清空备份文本
$scope.toggleTextEditStatus($scope.draft);
$scope.toCommitNamespace.commited = true;
$scope.toggleTextEditStatus($scope.toCommitNamespace);
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "更新失败");
......@@ -117,11 +123,12 @@ application_module.controller("ConfigNamespaceController",
//文本编辑框状态切换
$scope.toggleTextEditStatus = function (namespace) {
namespace.isTextEditing = !namespace.isTextEditing;
if (namespace.isTextEditing) {//切换为编辑状态,保存一下原来值
$scope.draft.backupText = namespace.text;
if (namespace.isTextEditing) {//切换为编辑状态
$scope.toCommitNamespace.commited = false;
namespace.backupText = namespace.text;
} else {
if ($scope.draft.backupText) {//取消编辑,则复原
namespace.text = $scope.draft.backupText;
if (!$scope.toCommitNamespace.commited) {//取消编辑,则复原
namespace.text = namespace.backupText;
}
}
};
......@@ -169,6 +176,20 @@ application_module.controller("ConfigNamespaceController",
$scope.item.oldValue = oldValue;
};
var toDeleteItemId = 0;
$scope.preDeleteItem = function (itemId) {
toDeleteItemId = itemId;
};
$scope.deleteItem = function () {
ConfigService.delete_item($rootScope.pageContext.env, toDeleteItemId).then(function (result) {
toastr.success("删除成功!");
$rootScope.refreshNamespaces();
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "删除失败");
});
};
var toOperationNamespaceName = '';
//修改配置
$scope.editItem = function (namespace, item) {
......
/** navbar */
directive_module.directive('apollonav', function ($compile, $window, AppService, EnvService) {
directive_module.directive('apollonav', function ($compile, $window, toastr, AppUtil, AppService, EnvService) {
return {
restrict: 'E',
templateUrl: '../views/common/nav.html',
......
......@@ -35,6 +35,10 @@ appService.service("ConfigService", ['$resource', '$q', function ($resource, $q)
update_item: {
method: 'PUT',
url: '/apps/:appId/env/:env/clusters/:clusterName/namespaces/:namespaceName/item'
},
delete_item: {
method: 'DELETE',
url: '/envs/:env/items/:itemId'
}
});
......@@ -158,6 +162,19 @@ appService.service("ConfigService", ['$resource', '$q', function ($resource, $q)
d.reject(result);
});
return d.promise;
},
delete_item: function (env, itemId) {
var d = $q.defer();
config_source.delete_item({
env: env,
itemId: itemId
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
}
......
......@@ -15,6 +15,11 @@ a {
cursor: pointer;
}
td {
word-wrap: break-word;
word-break: break-all;
}
.logo {
width: 150px;
height: 50px;
......@@ -152,8 +157,6 @@ table th {
}
.project-info td {
word-wrap: break-word;
word-break: break-all;
border-bottom: 1px dotted gray;
padding: 4px 6px;
}
......@@ -223,7 +226,7 @@ table th {
word-wrap: break-word;
}
.namespace-view-table .glyphicon{
.namespace-view-table .glyphicon {
cursor: pointer;
}
......
/**
* angular-bootstrap-confirm - Displays a bootstrap confirmation popover when clicking the given element.
* @version v2.1.0
* @link https://github.com/mattlewis92/angular-bootstrap-confirm
* @license MIT
*/
!function(n,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("angular"),require("angular-sanitize")):"function"==typeof define&&define.amd?define(["angular","angular-sanitize"],t):"object"==typeof exports?exports.angularBootstrapConfirmModuleName=t(require("angular"),require("angular-sanitize")):n.angularBootstrapConfirmModuleName=t(n.angular,n["angular-sanitize"])}(this,function(n,t){return function(n){function t(o){if(e[o])return e[o].exports;var i=e[o]={exports:{},id:o,loaded:!1};return n[o].call(i.exports,i,i.exports,t),i.loaded=!0,i.exports}var e={};return t.m=n,t.c=e,t.p="",t(0)}([function(n,t,e){"use strict";var o=e(1),i=e(2);e(3),e(1);var s="angular-bootstrap-confirm.html";n.exports=o.module("mwl.confirm",["ngSanitize","ui.bootstrap.position"]).run(["$templateCache",function(n){n.put(s,i)}]).controller("PopoverConfirmCtrl",["$scope","$rootScope","$element","$attrs","$compile","$document","$window","$timeout","$injector","$templateRequest","$parse","confirmationPopoverDefaults",function(n,t,e,i,s,r,a,c,l,u,p,f){function m(t,e){o.isDefined(t)&&p(t).assign(n,e)}function d(t,e){return o.isDefined(t)?p(t)(n):e}function v(){var n=y.positionElements(e,x.popover,i.placement||x.defaults.placement,!0);n.top+="px",n.left+="px",x.popover.css(n)}function b(){var n=i.focusButton||x.defaults.focusButton;if(n){var t=n+"-button";x.popover[0].getElementsByClassName(t)[0].focus()}}function $(){x.isVisible||d(i.isDisabled,!1)||(x.popover.css({display:"block"}),v(),b(),x.isVisible=!0,m(i.isOpen,!0))}function g(){x.isVisible&&(x.popover.css({display:"none"}),x.isVisible=!1,m(i.isOpen,!1))}function h(){x.isVisible?g():$(),n.$apply()}function C(t){!x.isVisible||x.popover[0].contains(t.target)||e[0].contains(t.target)||(g(),n.$apply())}var x=this;x.defaults=f,x.$attrs=i;var B=l.has("$uibPosition")?"$uibPosition":"$position",y=l.get(B),T=i.templateUrl||f.templateUrl,P=t.$new(!0);P.vm=x,u(T).then(function(n){x.popover=o.element(n),x.popover.css("display","none"),s(x.popover)(P),r.find("body").append(x.popover)}),x.isVisible=!1,x.showPopover=$,x.hidePopover=g,x.togglePopover=h,x.onConfirm=function(){d(i.onConfirm)},x.onCancel=function(){d(i.onCancel)},n.$watch(i.isOpen,function(n){c(function(){n?$():g()})}),e.bind("click",h),a.addEventListener("resize",v),r.bind("click",C),r.bind("touchend",C),n.$on("$destroy",function(){x.popover.remove(),e.unbind("click",h),a.removeEventListener("resize",v),r.unbind("click",C),r.unbind("touchend",C),P.$destroy()})}]).directive("mwlConfirm",function(){return{restrict:"A",controller:"PopoverConfirmCtrl"}}).value("confirmationPopoverDefaults",{confirmText:"Confirm",cancelText:"Cancel",confirmButtonType:"success",cancelButtonType:"default",placement:"top",focusButton:null,templateUrl:s,hideConfirmButton:!1,hideCancelButton:!1}).name},function(t,e){t.exports=n},function(n,t){n.exports='<div class="popover" ng-class="vm.$attrs.placement || vm.defaults.placement">\n <div class="arrow"></div>\n <h3 class="popover-title" ng-bind-html="vm.$attrs.title"></h3>\n <div class="popover-content">\n <p ng-bind-html="vm.$attrs.message"></p>\n <div class="row">\n <div\n class="col-xs-6"\n ng-if="!vm.$attrs.hideConfirmButton && !vm.defaults.hideConfirmButton"\n ng-class="{\'col-xs-offset-3\': vm.$attrs.hideCancelButton || vm.defaults.hideCancelButton}">\n <button\n class="btn btn-block confirm-button"\n ng-class="\'btn-\' + (vm.$attrs.confirmButtonType || vm.defaults.confirmButtonType)"\n ng-click="vm.onConfirm(); vm.hidePopover()"\n ng-bind-html="vm.$attrs.confirmText || vm.defaults.confirmText">\n </button>\n </div>\n <div\n class="col-xs-6"\n ng-if="!vm.$attrs.hideCancelButton && !vm.defaults.hideCancelButton"\n ng-class="{\'col-xs-offset-3\': vm.$attrs.hideConfirmButton || vm.defaults.hideConfirmButton}">\n <button\n class="btn btn-block cancel-button"\n ng-class="\'btn-\' + (vm.$attrs.cancelButtonType || vm.defaults.cancelButtonType)"\n ng-click="vm.onCancel(); vm.hidePopover()"\n ng-bind-html="vm.$attrs.cancelText || vm.defaults.cancelText">\n </button>\n </div>\n </div>\n </div>\n</div>\n'},function(n,e){n.exports=t}])});
//# sourceMappingURL=angular-bootstrap-confirm.min.js.map
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment