Browse Source

git-svn-id: svn+ssh://10.10.101.21/source/platform/platform-b2b@650 f3bf4e98-0cf0-11e4-a00c-a99a8b9d557d

administrator 11 years ago
parent
commit
74d2efa99d

+ 11 - 0
src/main/java/com/uas/platform/b2b/dao/SigninLogDao.java

@@ -0,0 +1,11 @@
+package com.uas.platform.b2b.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import com.uas.platform.b2b.model.SigninLog;
+
+@Repository
+public interface SigninLogDao extends JpaRepository<SigninLog, Long> {
+
+}

+ 34 - 0
src/main/java/com/uas/platform/b2b/filter/SecurityInterceptor.java

@@ -16,6 +16,10 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.mobile.device.Device;
+import org.springframework.mobile.device.DeviceResolver;
+import org.springframework.mobile.device.LiteDeviceResolver;
+import org.springframework.mobile.device.site.SitePreference;
 import org.springframework.security.access.SecurityMetadataSource;
 import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
 import org.springframework.security.access.intercept.InterceptorStatusToken;
@@ -35,11 +39,14 @@ import org.springframework.util.StringUtils;
 import com.uas.platform.b2b.model.Authority;
 import com.uas.platform.b2b.model.Resource;
 import com.uas.platform.b2b.model.Role;
+import com.uas.platform.b2b.model.SigninLog;
 import com.uas.platform.b2b.model.User;
+import com.uas.platform.b2b.service.SigninLogService;
 import com.uas.platform.b2b.service.UserService;
 import com.uas.platform.b2b.support.SecurityConstant;
 import com.uas.platform.b2b.support.SystemSession;
 import com.uas.platform.core.model.Constant;
+import com.uas.platform.core.util.AgentUtils;
 import com.uas.platform.core.util.encry.Md5Utils;
 
 /**
@@ -59,6 +66,15 @@ public class SecurityInterceptor extends AbstractSecurityInterceptor implements
 	@Autowired
 	private UserService userService;
 
+	@Autowired
+	private SigninLogService signinLogService;
+
+	private final DeviceResolver deviceResolver;
+
+	public SecurityInterceptor() {
+		this.deviceResolver = new LiteDeviceResolver();
+	}
+
 	/**
 	 * @param request
 	 * @param response
@@ -73,11 +89,13 @@ public class SecurityInterceptor extends AbstractSecurityInterceptor implements
 		logSession(httpRequest);
 		User user = SystemSession.getUser();
 		if (user == null) {// 未登录则要求登录
+			logoutSession();
 			httpResponse.sendRedirect(httpRequest.getContextPath() + SecurityConstant.LOGIN_URL);
 			return;
 		}
 		if (user.isSys()) {// 超级用户无需验证权限
 			chain.doFilter(request, response);
+			logoutSession();
 			return;
 		}
 		invoke(new FilterInvocation(request, response, chain));
@@ -148,10 +166,26 @@ public class SecurityInterceptor extends AbstractSecurityInterceptor implements
 				}
 				request.getSession().setAttribute("user", authedUser);
 				SystemSession.setUser(authedUser);
+				// 记录登录日志
+				SitePreference preference = getDefaultSitePreferenceForDevice(this.deviceResolver.resolveDevice(request));
+				signinLogService.save(new SigninLog(authedUser, preference, AgentUtils.getIp(request), true));
 			}
 		}
 	}
 
+	private SitePreference getDefaultSitePreferenceForDevice(Device device) {
+		if (device == null) {
+			return null;
+		}
+		if (device.isMobile()) {
+			return SitePreference.MOBILE;
+		}
+		if (device.isTablet()) {
+			return SitePreference.TABLET;
+		}
+		return SitePreference.NORMAL;
+	}
+
 	/**
 	 * 线程池策略下,不会频繁删除线程,置于线程内的对象须手动删除
 	 */

+ 18 - 1
src/main/java/com/uas/platform/b2b/model/SigninLog.java

@@ -17,6 +17,8 @@ import javax.validation.constraints.NotNull;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.springframework.mobile.device.site.SitePreference;
 
+import com.uas.platform.core.model.Constant;
+
 /**
  * 登录日志
  * 
@@ -64,6 +66,12 @@ public class SigninLog implements Serializable {
 	@Column(name = "log_agent")
 	private String agent;
 
+	/**
+	 * 记住用户登录方式:通过浏览器cookie和数据库persistent_logins
+	 */
+	@Column(name = "log_remember")
+	private Short isRemember;
+
 	public SigninLog() {
 		this.time = new Date();
 	}
@@ -76,10 +84,11 @@ public class SigninLog implements Serializable {
 		this.userUU = user.getUserUU();
 	}
 
-	public SigninLog(User user, SitePreference preference, String ip) {
+	public SigninLog(User user, SitePreference preference, String ip, boolean remember) {
 		this(user);
 		setAgent(preference);
 		setIp(ip);
+		setIsRemember(remember ? Constant.YES : Constant.NO);
 	}
 
 	@JsonIgnore
@@ -135,4 +144,12 @@ public class SigninLog implements Serializable {
 		this.agent = preference.name();
 	}
 
+	public Short getIsRemember() {
+		return isRemember;
+	}
+
+	public void setIsRemember(Short isRemember) {
+		this.isRemember = isRemember;
+	}
+
 }

+ 14 - 0
src/main/java/com/uas/platform/b2b/service/SigninLogService.java

@@ -0,0 +1,14 @@
+package com.uas.platform.b2b.service;
+
+import com.uas.platform.b2b.model.SigninLog;
+
+public interface SigninLogService {
+
+	/**
+	 * 保存登录日志
+	 * 
+	 * @param log
+	 */
+	public void save(SigninLog log);
+
+}

+ 21 - 0
src/main/java/com/uas/platform/b2b/service/impl/SigninLogServiceImpl.java

@@ -0,0 +1,21 @@
+package com.uas.platform.b2b.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.uas.platform.b2b.dao.SigninLogDao;
+import com.uas.platform.b2b.model.SigninLog;
+import com.uas.platform.b2b.service.SigninLogService;
+
+@Service
+public class SigninLogServiceImpl implements SigninLogService {
+
+	@Autowired
+	private SigninLogDao signinLogDao;
+
+	@Override
+	public void save(SigninLog log) {
+		signinLogDao.save(log);
+	}
+
+}

+ 37 - 0
src/main/java/com/uas/platform/b2b/support/CustomAuthenticationSuccessHandler.java

@@ -9,6 +9,10 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mobile.device.Device;
+import org.springframework.mobile.device.DeviceResolver;
+import org.springframework.mobile.device.LiteDeviceResolver;
+import org.springframework.mobile.device.site.SitePreference;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
 import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
@@ -18,16 +22,32 @@ import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
 import com.uas.platform.b2b.model.Role;
+import com.uas.platform.b2b.model.SigninLog;
 import com.uas.platform.b2b.model.User;
+import com.uas.platform.b2b.service.SigninLogService;
 import com.uas.platform.b2b.service.UserService;
 import com.uas.platform.core.model.Constant;
+import com.uas.platform.core.util.AgentUtils;
 
 public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
 
 	@Autowired
 	private UserService userService;
+	
+	@Autowired
+	private SigninLogService signinLogService;
 
 	private RequestCache requestCache = new HttpSessionRequestCache();
+	
+	private final DeviceResolver deviceResolver;
+	
+	public CustomAuthenticationSuccessHandler() {
+		this(new LiteDeviceResolver());
+	}
+
+	public CustomAuthenticationSuccessHandler(DeviceResolver deviceResolver) {
+		this.deviceResolver = deviceResolver;
+	}
 
 	@Override
 	public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response,
@@ -77,6 +97,23 @@ public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationS
 			}
 		}
 		request.getSession().setAttribute("user", user);
+		// 记录登录日志
+		SitePreference preference = getDefaultSitePreferenceForDevice(this.deviceResolver.resolveDevice(request));
+		signinLogService.save(new SigninLog(user, preference, AgentUtils.getIp(request), false));
+
+	}
+	
+	private SitePreference getDefaultSitePreferenceForDevice(Device device) {
+		if (device == null) {
+			return null;
+		}
+		if (device.isMobile()) {
+			return SitePreference.MOBILE;
+		}
+		if (device.isTablet()) {
+			return SitePreference.TABLET;
+		}
+		return SitePreference.NORMAL;
 	}
 
 }

+ 24 - 0
src/main/webapp/resources/css/index.css

@@ -1129,4 +1129,28 @@ a {
 	margin-top: 10px;
 	height: 26px;
 	opacity: 1;
+}
+/*enterprise*/
+.base-info {
+	-webkit-box-shadow: 0 0 7px 0 rgba(119, 119, 119, 0.2);
+	box-shadow: 0 0 7px 0 rgba(119, 119, 119, 0.2);
+	border-bottom: 1px solid #d9d9d9;
+	margin-bottom: 10px;
+}
+
+.base-info .item {
+	line-height: 26px;
+	border-bottom: 1px dotted #e8e8e8;
+	padding: 5px 0;
+}
+
+.base-info .title {
+	float: left;
+	width: 78px;
+	color: #999;
+	font-weight: bold;
+}
+
+.base-info .content {
+	float: left;
 }

+ 83 - 2
src/main/webapp/resources/tpl/index/account/enterprise.html

@@ -1,9 +1,90 @@
-<div class="pane">
+<div class="pane base-info">
 	<div class="pane-header">
 		注册信息<a href="#" class="pull-right text-simple"><i
 			class="fa fa-pencil fa-fw"></i>编辑</a>
 	</div>
 	<div class="pane-body">
-		
+		<div class="row row-sm item">
+			<div class="col-xs-6">
+				<span class="title">企业名称</span>
+				<div class="content">1234</div>
+			</div>
+			<div class="col-xs-6">
+				<span class="title">企业简称</span>
+				<div class="content">1234</div>
+			</div>
+		</div>
+		<div class="row row-sm item">
+			<div class="col-xs-6">
+				<span class="title">营业执照</span>
+				<div class="content">1234</div>
+			</div>
+			<div class="col-xs-6">
+				<span class="title">注册地区</span>
+				<div class="content">1234</div>
+			</div>
+		</div>
+	</div>
+</div>
+<div class="row row-sm">
+	<div class="col-xs-6">
+		<div class="pane base-info">
+			<div class="pane-header">
+				企业管理员<a href="#" class="pull-right text-simple"><i
+					class="fa fa-pencil fa-fw"></i>编辑</a>
+			</div>
+			<div class="pane-body">
+				<div class="row row-sm item">
+					<div class="col-xs-6">
+						<span class="title">姓名</span>
+						<div class="content">1234</div>
+					</div>
+					<div class="col-xs-6">
+						<span class="title">性别</span>
+						<div class="content">1234</div>
+					</div>
+				</div>
+				<div class="row row-sm item">
+					<div class="col-xs-6">
+						<span class="title">电话</span>
+						<div class="content">1234</div>
+					</div>
+					<div class="col-xs-6">
+						<span class="title">邮箱</span>
+						<div class="content">1234</div>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+	<div class="col-xs-6">
+		<div class="pane base-info">
+			<div class="pane-header">
+				其他信息<a href="#" class="pull-right text-simple"><i
+					class="fa fa-pencil fa-fw"></i>编辑</a>
+			</div>
+			<div class="pane-body">
+				<div class="row row-sm item">
+					<div class="col-xs-6">
+						<span class="title">企业名称</span>
+						<div class="content">1234</div>
+					</div>
+					<div class="col-xs-6">
+						<span class="title">企业简称</span>
+						<div class="content">1234</div>
+					</div>
+				</div>
+				<div class="row row-sm item">
+					<div class="col-xs-6">
+						<span class="title">营业执照</span>
+						<div class="content">1234</div>
+					</div>
+					<div class="col-xs-6">
+						<span class="title">注册地区</span>
+						<div class="content">1234</div>
+					</div>
+				</div>
+			</div>
+		</div>
 	</div>
 </div>

+ 22 - 22
src/main/webapp/resources/tpl/index/sale/change.html

@@ -225,20 +225,20 @@
 			<td colspan="6"></td>
 		</tr>
 	</thead>
-	<tbody ng-repeat="change in $data">
+	<tbody ng-repeat="change in $data track by change.id">
 		<tr class="order-hd">
 			<td class="first">
 				<div class="order-main">
 					<span> <input type="checkbox" class="selector"
 						ng-model="change.$selected">
-					</span> <span class="text-num text-bold" title="{{change.date}}"
-						ng-bind="change.date | date:'yyyy-MM-dd'"></span> <span>流水号:<a
-						class="text-num" ng-bind="change.code" href="#"></a></span> <span>订单:<a
-						class="text-num text-bold" ng-bind="change.order.code" href="#"></a></span>
+					</span> <span class="text-num text-bold"
+						ng-bind="::change.date | date:'yyyy-MM-dd'"></span> <span>流水号:<a
+						class="text-num" ng-bind="::change.code" href="#"></a></span> <span>订单:<a
+						class="text-num text-bold" ng-bind="::change.order.code" href="#"></a></span>
 				</div>
 			</td>
 			<td colspan="3"><a href="#"
-				ng-bind="change.order.enterprise.enName"></a></td>
+				ng-bind="::change.order.enterprise.enName"></a></td>
 			<td colspan="1" class="order-sum">
 			</td>
 			<td colspan="1" class="text-center">
@@ -250,7 +250,7 @@
 				</div>
 			</td>
 		</tr>
-		<tr ng-show="change.$editing" class="br-b">
+		<tr ng-if="change.$editing" class="br-b">
 			<td colspan="3"></td>
 			<td>
 				<div style="margin: 0 auto" ng-init="change.$agreed = 1">
@@ -277,50 +277,50 @@
 					<a ng-click="onReplyClick(change)" class="text-inverse">确认回复</a>
 				</div> <br></td>
 		</tr>
-		<tr class="order-bd" ng-repeat="item in change.orderChangeItems">
+		<tr class="order-bd" ng-repeat="item in ::change.orderChangeItems">
 			<td class="product">
 				<div class="text-num text-bold">
-					<a href="#" ng-bind="item.newProduct.code"></a>
+					<a href="#" ng-bind="::item.newProduct.code"></a>
 				</div>
 				<div>
-					<a href="#" ng-bind="item.newProduct.title"></a>
+					<a href="#" ng-bind="::item.newProduct.title"></a>
 				</div>
-				<div class="text-muted" title="{{item.newProduct.spec}}"
-					ng-bind="item.newProduct.spec"></div>
+				<div class="text-muted"
+					ng-bind="::item.newProduct.spec"></div>
 			</td>
 			<td class="text-center">
 				<div class="text-num"
 					ng-class="{'text-inverse': item.newPrice != item.orderItem.price}"
-					title="{{item.newPrice}}" ng-bind="item.newPrice"></div>
+					title="{{item.newPrice}}" ng-bind="::item.newPrice"></div>
 				<div ng-show="item.newPrice != item.orderItem.price">
-					<s class="text-num text-muted" ng-bind="item.orderItem.price"></s>
+					<s class="text-num text-muted" ng-bind="::item.orderItem.price"></s>
 				</div>
 			</td>
 			<td class="text-center">
 				<div class="text-num"
 					ng-class="{'text-inverse': item.newQty != item.orderItem.qty}"
-					title="{{item.newQty}}" ng-bind="item.newQty"></div>
+					title="{{item.newQty}}" ng-bind="::item.newQty"></div>
 				<div ng-show="item.newQty != item.orderItem.qty">
 					<s class="text-num text-muted" title="{{item.orderItem.qty}}"
-						ng-bind="item.orderItem.qty"></s>
+						ng-bind="::item.orderItem.qty"></s>
 				</div>
-				<div class="text-muted" ng-bind="item.newProduct.unit"></div>
+				<div class="text-muted" ng-bind="::item.newProduct.unit"></div>
 			</td>
 			<td class="text-center br-l">
 				<div class="text-num"
 					ng-class="{'text-inverse': item.newDelivery != item.orderItem.delivery}"
-					ng-bind="item.newDelivery | date:'yyyy-MM-dd'"></div>
+					ng-bind="::item.newDelivery | date:'yyyy-MM-dd'"></div>
 				<div ng-show="item.newDelivery != item.orderItem.delivery">
 					<s class="text-num text-muted"
-						ng-bind="item.orderItem.delivery | date:'yyyy-MM-dd'"></s>
+						ng-bind="::item.orderItem.delivery | date:'yyyy-MM-dd'"></s>
 				</div>
 			</td>
 			<td class="text-center br-l" colspan="2">
-				<div ng-bind="item.description"></div>
-				<div ng-show="change.agreed == 1" class="block">
+				<div ng-bind="::item.description"></div>
+				<div ng-if="change.agreed == 1" class="block">
 					<span class="text-trans success">已同意</span>
 				</div>
-				<div ng-show="change.agreed == 0" class="block">
+				<div ng-if="change.agreed == 0" class="block">
 					<span class="text-trans warning">不同意</span>
 				</div>
 			</td>

+ 19 - 19
src/main/webapp/resources/tpl/index/sale/inquiry.html

@@ -232,7 +232,7 @@
 				<td colspan="7"></td>
 			</tr>
 		</thead>
-		<tbody ng-repeat="inquiryItem in $data">
+		<tbody ng-repeat="inquiryItem in $data track by inquiryItem.id">
 			<tr class="order-hd">
 				<td class="first">
 					<div class="order-main">
@@ -240,15 +240,15 @@
 							ng-model="inquiryItem.$selected">
 						</span> <span class="text-num text-bold"
 							title="{{inquiryItem.inquiry.date}}"
-							ng-bind="inquiryItem.inquiry.date | date:'yyyy-MM-dd'"></span> <span>流水号:<a
-							class="text-num" ng-bind="inquiryItem.inquiry.code" href="#"></a></span>
+							ng-bind="::inquiryItem.inquiry.date | date:'yyyy-MM-dd'"></span> <span>流水号:<a
+							class="text-num" ng-bind="::inquiryItem.inquiry.code" href="#"></a></span>
 					</div>
 				</td>
 				<td colspan="3"><a href="#"
-					ng-bind="inquiryItem.inquiry.enterprise.enName"></a></td>
+					ng-bind="::inquiryItem.inquiry.enterprise.enName"></a></td>
 				<td class="text-right" colspan="2" class="order-sum"><span
 					class="text-inverse text-bold"
-					ng-bind="inquiryItem.inquiry.endDate | timespan"></span> <span
+					ng-bind="::inquiryItem.inquiry.endDate | timespan"></span> <span
 					class="text-muted">后截止报价</span></td>
 				<td class="text-center">
 					<div class="operates">
@@ -260,13 +260,13 @@
 			<tr class="order-bd">
 				<td class="product">
 					<div class="text-num text-bold">
-						<a href="#" ng-bind="inquiryItem.product.code"></a>
+						<a href="#" ng-bind="::inquiryItem.product.code"></a>
 					</div>
 					<div>
-						<a href="#" ng-bind="inquiryItem.product.title"></a>
+						<a href="#" ng-bind="::inquiryItem.product.title"></a>
 					</div>
-					<div class="text-muted" title="{{inquiryItem.product.spec}}"
-						ng-bind="inquiryItem.product.spec"></div>
+					<div class="text-muted"
+						ng-bind="::inquiryItem.product.spec"></div>
 				</td>
 				<td class="text-center">
 					<div ng-show="!inquiryItem.$editing">
@@ -274,7 +274,7 @@
 							<span ng-bind="reply.lapQty"></span>
 						</div>
 					</div>
-					<div style="margin: 0 auto;" ng-show="inquiryItem.$editing">
+					<div style="margin: 0 auto;" ng-if="inquiryItem.$editing">
 						<div ng-repeat="reply in inquiryItem.replies">
 							<div class="form-group input-group input-group-xs">
 								<input type="text" class="form-control input-xs"
@@ -306,7 +306,7 @@
 							<div class="dropdown-menu pane" style="width: 270px;">
 								<div class="pane-body">
 									<ul class="list-unstyled list-menu">
-										<li ng-repeat="historyItem in inquiryItem.history">
+										<li ng-repeat="historyItem in ::inquiryItem.history">
 											<div ng-class="{'text-inverse': $index==0}">
 												<div class="row row-sm"
 													ng-repeat="historyReply in historyItem.replies">
@@ -326,7 +326,7 @@
 							</div>
 						</div>
 					</div>
-					<div style="margin: 0 auto;" ng-show="inquiryItem.$editing">
+					<div style="margin: 0 auto;" ng-if="inquiryItem.$editing">
 						<div ng-repeat="reply in inquiryItem.replies">
 							<div class="form-group">
 								<input type="text" class="form-control input-xs"
@@ -340,7 +340,7 @@
 						<span ng-bind="inquiryItem.minOrderQty"></span> <span
 							ng-show="inquiryItem.minOrderQty == null" class="text-muted">-</span>
 					</div>
-					<div ng-show="inquiryItem.$editing">
+					<div ng-if="inquiryItem.$editing">
 						<input type="text" class="form-control input-xs"
 							ng-model="inquiryItem.minOrderQty" placeholder="最小订购">
 					</div>
@@ -350,7 +350,7 @@
 						<span ng-bind="inquiryItem.minPackQty"></span> <span
 							ng-show="inquiryItem.minPackQty == null" class="text-muted">-</span>
 					</div>
-					<div ng-show="inquiryItem.$editing">
+					<div ng-if="inquiryItem.$editing">
 						<input type="text" class="form-control input-xs"
 							ng-model="inquiryItem.minPackQty" placeholder="最小包装">
 					</div>
@@ -368,7 +368,7 @@
 							<span class="text-muted">-</span>
 						</div>
 					</div>
-					<div ng-show="inquiryItem.$editing">
+					<div ng-if="inquiryItem.$editing">
 						<div class="form-group input-group input-group-xs input-trigger">
 							<input type="text" ng-model="inquiryItem.vendFromDate"
 								class="form-control" placeholder="开始日期"
@@ -402,16 +402,16 @@
 					</div>
 				</td>
 				<td class="text-center br-l">
-					<div ng-show="inquiryItem.status == 201 && inquiryItem.agreed == null" class="block">
+					<div ng-if="inquiryItem.status == 201 && inquiryItem.agreed == null" class="block">
 						<span class="text-trans warning">已报价</span>
 					</div>
-					<div ng-show="inquiryItem.status == 201 && inquiryItem.agreed == 1" class="block">
+					<div ng-if="inquiryItem.status == 201 && inquiryItem.agreed == 1" class="block">
 						<span class="text-trans success">报价已采纳</span>
 					</div>
-					<div ng-show="inquiryItem.status == 201 && inquiryItem.agreed == 0" class="block">
+					<div ng-if="inquiryItem.status == 201 && inquiryItem.agreed == 0" class="block">
 						<span class="text-trans error">报价未采纳</span>
 					</div>
-					<div ng-show="inquiryItem.status == 200">
+					<div ng-if="inquiryItem.status == 200">
 						<div ng-show="!inquiryItem.$editing">
 							<a ng-click="inquiryItem.$editing=!inquiryItem.$editing">报 价</a>
 						</div>

+ 23 - 23
src/main/webapp/resources/tpl/index/sale/notice.html

@@ -255,38 +255,38 @@
 			<td colspan="6"></td>
 		</tr>
 	</thead>
-	<tbody ng-repeat="notice in $data">
+	<tbody ng-repeat="notice in $data track by notice.id">
 		<tr class="order-hd">
 			<td class="first">
 				<div class="order-main">
 					<span> <input type="checkbox" class="selector"
 						ng-model="notice.$selected">
-					</span> <span class="text-num text-bold" title="{{notice.date}}"
-						ng-bind="notice.date | date:'yyyy-MM-dd'"></span>
+					</span> <span class="text-num text-bold"
+						ng-bind="::notice.date | date:'yyyy-MM-dd'"></span>
 				</div>
 			</td>
 			<td colspan="2"><a href="#"
 				ng-bind="notice.orderItem.order.enterprise.enName"></a></td>
 			<td class="text-right dropdown" colspan="2" class="order-sum"><a
 				class="dropdown-toggle" href="javascript:void(0);"> <span
-					class="text-num text-bold" ng-bind="notice.orderItem.order.code"></span>
+					class="text-num text-bold" ng-bind="::notice.orderItem.order.code"></span>
 					第{{notice.orderItem.number}}行
 			</a>
 				<div class="dropdown-menu order-snapshot" style="padding: 10px 15px">
 					<div class="text-center text-bold title">订单快照</div>
 					<dl class="dl-horizontal">
 						<dt>日期</dt>
-						<dd>{{notice.orderItem.order.date | date : 'yyyy-MM-dd'}}</dd>
+						<dd>{{::notice.orderItem.order.date | date : 'yyyy-MM-dd'}}</dd>
 						<dt>采购员</dt>
-						<dd>{{notice.orderItem.order.user.userName}}</dd>
+						<dd>{{::notice.orderItem.order.user.userName}}</dd>
 						<dt>付款条件</dt>
-						<dd>{{notice.orderItem.order.payments}}</dd>
+						<dd>{{::notice.orderItem.order.payments}}</dd>
 						<dt>付款币种</dt>
-						<dd>{{notice.orderItem.order.currency}}</dd>
+						<dd>{{::notice.orderItem.order.currency}}</dd>
 						<dt>交货地址</dt>
-						<dd>{{notice.orderItem.order.shipAddress}}</dd>
+						<dd>{{::notice.orderItem.order.shipAddress}}</dd>
 						<dt>备注</dt>
-						<dd>{{notice.orderItem.order.remark}}</dd>
+						<dd>{{::notice.orderItem.order.remark}}</dd>
 					</dl>
 				</div></td>
 			<td class="text-center">
@@ -299,17 +299,17 @@
 		<tr class="order-bd">
 			<td class="product">
 				<div class="text-num text-bold">
-					<a href="#" ng-bind="notice.orderItem.product.code"></a>
+					<a href="#" ng-bind="::notice.orderItem.product.code"></a>
 				</div>
 				<div>
-					<a href="#" ng-bind="notice.orderItem.product.title"></a>
+					<a href="#" ng-bind="::notice.orderItem.product.title"></a>
 				</div>
-				<div class="text-muted" title="{{notice.orderItem.product.spec}}"
-					ng-bind="notice.orderItem.product.spec"></div>
+				<div class="text-muted"
+					ng-bind="::notice.orderItem.product.spec"></div>
 			</td>
 			<td class="text-center">
 				<div ng-show="!notice.$editing">{{notice.remark}}</div>
-				<div ng-show="notice.$editing">
+				<div ng-if="notice.$editing">
 					<input type="text" class="form-control input-xs"
 						ng-model="notice.send.code" placeholder="送货单号">
 				</div>
@@ -324,10 +324,10 @@
 				<div ng-show="!notice.endQty || notice.endQty < notice.qty">
 					<div ng-show="!notice.$editing">
 						<ul>
-							<li>本次需求:{{notice.qty}}<span class="text-muted pull-right">[订单数:{{notice.orderItem.qty}}]</span></li>
+							<li>本次需求:{{::notice.qty}}<span class="text-muted pull-right">[订单数:{{::notice.orderItem.qty}}]</span></li>
 							<li class="dropdown dropdown-submenu">剩余未发:<span
 								class="text-num text-inverse"
-								ng-bind="notice.qty-(notice.endQty || 0)"></span> <a
+								ng-bind="::notice.qty-(notice.endQty || 0)"></span> <a
 								href="javascript:void(0);" class="dropdown-toggle pull-right"><span
 									ng-class="{'text-muted' : notice.endQty == null || notice.endQty == 0,'text-default': notice.endQty > 0}">[已发货:{{notice.endQty}}]</span></a>
 								<div
@@ -338,24 +338,24 @@
 							</li>
 						</ul>
 					</div>
-					<div style="margin: 0 auto" ng-show="notice.$editing"
+					<div style="margin: 0 auto" ng-if="notice.$editing"
 						ng-init="notice.send.qty=notice.qty-(notice.endQty || 0)">
 						<input type="text" class="form-control input-xs"
 							ng-model="notice.send.qty" placeholder="数量">
 					</div>
 				</div>
 			</td>
-			<td class="text-center br-l">{{notice.orderItem.price}}</td>
-			<td class="text-center br-l">{{notice.delivery | date :
+			<td class="text-center br-l">{{::notice.orderItem.price}}</td>
+			<td class="text-center br-l">{{::notice.delivery | date :
 				'yyyy-MM-dd'}}</td>
 			<td class="text-center br-l">
-				<div ng-show="notice.endQty>=notice.qty" class="block">
+				<div ng-if="notice.endQty>=notice.qty" class="block">
 					<span class="text-trans success">已发货</span>
 				</div>
-				<div ng-show="notice.end" class="block">
+				<div ng-if="notice.end" class="block">
 					<span class="text-trans warning">已结案</span>
 				</div>
-				<div ng-show="(!notice.endQty || notice.endQty<notice.qty) && !notice.end">
+				<div ng-if="(!notice.endQty || notice.endQty<notice.qty) && !notice.end">
 					<div ng-show="!notice.$editing">
 						<a ng-click="notice.$editing=!notice.$editing">发 货</a>
 					</div>

+ 34 - 34
src/main/webapp/resources/tpl/index/sale/order.html

@@ -220,20 +220,20 @@
 			<td colspan="6"></td>
 		</tr>
 	</thead>
-	<tbody ng-repeat="order in $data">
+	<tbody ng-repeat="order in $data track by order.id">
 		<tr class="order-hd" ng-dbclick="order.$collapsed=!order.$collapsed">
 			<td class="first">
 				<div class="order-main">
 					<span> <input type="checkbox" class="selector"
 						ng-model="order.$selected">
-					</span> <span class="text-num text-bold" title="{{order.date}}"
-						ng-bind="order.date | date:'yyyy-MM-dd'"></span> <span>订单号:<a
-						class="text-num" ng-bind="order.code" href="#"></a></span>
+					</span> <span class="text-num text-bold"
+						ng-bind="::order.date | date:'yyyy-MM-dd'"></span> <span>订单号:<a
+						class="text-num" ng-bind="::order.code" href="#"></a></span>
 				</div>
 			</td>
-			<td colspan="3"><a href="#" ng-bind="order.enterprise.enName"></a></td>
-			<td colspan="1" class="order-sum">{{order.currency}}: <span
-				ng-bind="getOrderTotal(order.orderItems) | number : 2"
+			<td colspan="3"><a href="#" ng-bind="::order.enterprise.enName"></a></td>
+			<td colspan="1" class="order-sum">{{::order.currency}}: <span
+				ng-bind="::getOrderTotal(order.orderItems) | number : 2"
 				class="text-num text-bold"></span>
 			</td>
 			<td colspan="1" class="text-center">
@@ -245,35 +245,35 @@
 				</div>
 			</td>
 		</tr>
-		<tr class="order-bd" ng-repeat="item in order.orderItems"
-			ng-hide="order.$collapsed">
+		<tr class="order-bd" ng-repeat="item in order.orderItems track by item.id"
+			ng-if="!order.$collapsed">
 			<td class="product">
 				<div class="text-num text-bold">
-					<a href="#" ng-bind="item.product.code"></a>
+					<a href="#" ng-bind="::item.product.code"></a>
 				</div>
 				<div>
-					<a href="#" ng-bind="item.product.title"></a>
+					<a href="#" ng-bind="::item.product.title"></a>
 				</div>
-				<div class="text-muted" title="{{item.product.spec}}"
-					ng-bind="item.product.spec"></div>
+				<div class="text-muted"
+					ng-bind="::item.product.spec"></div>
 			</td>
-			<td class="text-center text-num" title="{{item.price}}"
-				ng-bind="item.price"></td>
+			<td class="text-center text-num"
+				ng-bind="::item.price"></td>
 			<td class="text-center">
-				<div class="text-num" title="{{item.qty}}" ng-bind="item.qty"></div>
-				<div class="text-muted" ng-bind="item.product.unit"></div>
-				<div style="margin: 0 auto" ng-show="item.$editing">
+				<div class="text-num" ng-bind="::item.qty"></div>
+				<div class="text-muted" ng-bind="::item.product.unit"></div>
+				<div style="margin: 0 auto" ng-if="item.$editing">
 					<input type="text" ng-model="item.reply.qty"
 						ng-init="item.reply.qty=item.qty-item.replyQty"
 						class="form-control input-xs" placeholder="回复数量">
 				</div>
 			</td>
 			<td class="text-center br-l">
-				<div class="text-num" ng-bind="item.delivery | date:'yyyy-MM-dd'"></div>
+				<div class="text-num" ng-bind="::item.delivery | date:'yyyy-MM-dd'"></div>
 				<br>
 				<div style="margin: 0 auto"
 					class="input-group input-group-xs input-trigger"
-					ng-show="item.$editing">
+					ng-if="item.$editing">
 					<input type="text" ng-model="item.reply.delivery"
 						ng-init="item.reply.delivery=parseDate(item.delivery)"
 						class="form-control" placeholder="回复交期"
@@ -292,18 +292,18 @@
 			</td>
 			<td class="br-l">
 				<div ng-show="!item.$editing">
-					<div ng-show="!item.replyQty" class="text-muted text-center">未回复</div>
-					<div ng-show="item.replyQty > 0 && item.replyQty < item.qty">
+					<div ng-if="!item.replyQty" class="text-muted text-center">未回复</div>
+					<div ng-if="item.replyQty > 0 && item.replyQty < item.qty">
 						<div class="progress progress-sm">
 							<div class="progress-bar progress-bar-success"
 								ng-style="{'width': 100*item.replyQty/item.qty + '%'}">
 								<span class="sr-only"></span>
 							</div>
 						</div>
-						已回复 <span class="text-default">{{item.replyQty}}</span> /
-						{{item.qty}}
+						已回复 <span class="text-default">{{::item.replyQty}}</span> /
+						<span>{{::item.qty}}</span>
 					</div>
-					<div ng-show="item.replyQty > 0" class="dropdown" ng-class="{'text-center': item.replyQty>=item.qty}">
+					<div ng-if="item.replyQty > 0" class="dropdown" ng-class="{'text-center': item.replyQty>=item.qty}">
 							<a href="javascript:void(0);"
 								class="dropdown-toggle text-default"
 								ng-mouseover="getReply(item)">回复历史<i
@@ -311,14 +311,14 @@
 							<div class="dropdown-menu pane" style="width: 270px;">
 								<div class="pane-body">
 									<ul class="list-unstyled list-menu">
-										<li ng-repeat="reply in item.replies">
+										<li ng-repeat="reply in ::item.replies">
 											<div class="row row-sm"
 												ng-class="{'text-inverse': $index==0}">
-												<div class="col-xs-6">数量{{reply.qty}}</div>
-												<div class="col-xs-6">交期{{reply.delivery |
+												<div class="col-xs-6">数量{{::reply.qty}}</div>
+												<div class="col-xs-6">交期{{::reply.delivery |
 													date:'yyyy-MM-dd'}}</div>
 											</div>
-											<div class="text-muted">{{reply.recorder}}{{reply.date
+											<div class="text-muted">{{::reply.recorder}}{{::reply.date
 												| date:'yyyy-MM-dd HH:mm:ss'}}回复</div>
 										</li>
 									</ul>
@@ -326,24 +326,24 @@
 							</div>
 						</div>
 				</div>
-				<div style="margin: 0 auto" ng-show="item.$editing">
+				<div style="margin: 0 auto" ng-if="item.$editing">
 					<br>
 					<br> <input type="text" ng-model="item.reply.remark"
 						class="form-control input-xs" placeholder="回复备注" />
 				</div>
 			</td>
 			<td class="text-center br-l">
-				<div ng-show="item.replyQty>=item.qty" class="block">
+				<div ng-if="item.replyQty>=item.qty" class="block">
 					<span class="text-trans success">已回复</span>
 				</div>
-				<div ng-show="item.end" class="block">
+				<div ng-if="item.end" class="block">
 					<span class="text-trans warning">已结案</span>
 				</div>
-				<div ng-show="(!item.replyQty || item.replyQty<item.qty) && !item.end">
+				<div ng-if="(!item.replyQty || item.replyQty<item.qty) && !item.end">
 					<div ng-show="!item.$editing">
 						<a ng-click="item.$editing=!item.$editing">回复</a>
 					</div>
-					<div ng-show="item.$editing">
+					<div ng-if="item.$editing">
 						<div>
 							<a ng-click="item.$editing=!item.$editing">取消</a>
 						</div>