整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          終于搞懂如何用Java去除HTML標簽了

          我平時的工作中,偶爾會用 Java 做一些解析HTML的工作。有的時候我需要刪除所有的HTML標簽,只保留純文字內容。這個問題在做過一些爬蟲工作的朋友來說很簡單。下面來說說,我們平時使用到的集中解析的方法。

          使用正則表達式

          通過爬蟲爬到的HTML內容,從程序角度來講,就是一個字符串。我們可以對其按照純文本處理的方式來處理。

          我們在做文本處理的時候,第一個想到的就是正則表達式。從一個字符串中刪除HTML,對于正則來說,還是比較簡單的。畢竟還是有固定的格式,比如“<...>”。

          我們常用的的正則就是 <[^>]> 或者 <.*?>

          我們在使用正則的時候,需要注意的是正則默認是貪婪匹配。也就是說,正則表達式<.*> 能夠匹配到更多的HTML內容,而不是單個標簽。

          現在,讓我們測試一下它是否能從HTML源中刪除標簽。

          正則測試刪除標簽1

          在我們測試刪除HTML標簽之前,首先讓我們創建一個HTML例子,例如example1.html

          <!DOCTYPE html>
          <html>
          <head>
              <title>這是標題</title>
          </head>
          <body>
              <p>
                  如果應用程序X沒有啟動,可能的原因是<br/>
                  1. <a href="https://maven.apache.org">Maven</a>沒有安裝<br/>
                  2. 磁盤空間不足<br/>
                  3. 內存不足
              </p>
          </body>
          </html>
          
          

          現在,讓我們寫一個測試,用String.replaceAll()來刪除HTML標簽。

          String html = ... // load example1.html
          String result = html.replaceAll("<[^>]`>", "");
          System.out.println(result);
          

          如果我們運行這個測試方法,我們會看到結果。

              這是標題
          
          
          
                  如果應用程序X沒有啟動,可能的原因是
                  1.Maven沒有安裝
                  2.磁盤空間不足
                  3.沒有足夠的內存
          

          輸出結果保留了剝離后的HTML的空白處。我們在處理提取的文本時,可以很容易地刪除或跳過這些空行或空白處。

          正則測試刪除標簽2

          我們剛才已經看到了,通過使用Regex來刪除HTML標簽是非常簡單。但是粗暴的使用這種方法會有很多問題,我們不能預測最終的結果會是怎么樣的。

          例如,一個HTML文檔可能有<script><style>標簽,而我們可能不希望在結果中出現它們的內容。

          此外,<script><style>、甚至是<body>標簽中的文本可能包含 <>字符。如果是這種情況,我們的正則方法可能會出錯。

          現在,讓我們看看另一個例子,比如example2.html

          <!DOCTYPE HTML>
          <html>
          <head>
              <title>這是標題</title>
          </head>
          <script>
              // some js function
          </script>
          <body>
              <p>
                  如果應用程序X沒有啟動,可能的原因是<br/>
                  1. <a
                      id="link"
                      href="http://maven.apache.org/">
                      Maven
                      </a> 沒有安裝<br/>
                  2. 磁盤空間不足 (<1G) <br/>
                  3. 內存不足(<64MB)<br/>
              </p>
          </body>
          </html>
          

          現在我們有一個<script>標簽和 <字符在<body>標簽內。

          如果我們對example2.html使用同樣的方法,我們會得到如下內容。

             這是標題
              // some js function
                  如果應用程序X沒有啟動,可能的原因是
                  1. 
                      Maven
                       沒有安裝
                  2. 磁盤空間不足 (
                  3. 內存不足(
          

          顯然,由于"<"字符的存在,我們丟失了一些文本。所以正則在處理文本的時候并不是萬能的。我們可以使用一些 HTML 解析器來做這些比較復雜的場景。

          使用Jsoup

          Jsoup 是一個流行的HTML解析庫,如果想要從一個HTML文檔中提取文本,我們可以簡單地調用Jsoup.parse(htmlString).text()

          在項目中使用的時候,我們首先需要添加 jsoup 的依賴庫,我們這里就通過maven的方式引入。

          <dependency>
              <groupId>org.jsoup</groupId>
              <artifactId>jsoup</artifactId>
              <version>1.14.3</version>
          </dependency>
          

          我們用 example2.html來測試一下。

          String html = ... // load example2.html
          System.out.println(Jsoup.parse(html).text());
          

          如果我們讓這個方法運行,它就會打印出來。

          這是標題 如果應用程序X沒有啟動,可能的原因是 1.Maven沒有安裝 2.沒有足夠的(<1G)磁盤空間 3.沒有足夠的(<64MB)內存
          

          從輸出結果可知,Jsoup已經成功地從HTML文檔中提取了文本。另外,<script>元素中的文本已經被忽略了。

          此外,默認情況下,Jsoup會刪除所有的文本格式和空白處,比如換行符。

          使用HTMLCleaner

          HTMLCleaner 也是一個HTML解析庫。

          首先,我們需要在pom.xml中添加HTMLCleaner 依賴。

          <dependency>
              <groupId>net.sourceforge.htmlcleaner</groupId>
              <artifactId>htmlcleaner</artifactId>
              <version>2.25</version>
          </dependency>
          

          我們可以設置[各種參數](http://htmlcleaner.sourceforge.net/parameters.php)來控制HTMLCleaner的解析行為。我們在這里使用HTMLCleaner在解析example2.html時跳過<script>元素。

          String html = ... // load example2.html
          CleanerProperties props = new CleanerProperties();
          props.setPruneTags("script");
          String result = new HtmlCleaner(props).clean(html).getText().toString();
          System.out.println(result);
          

          運行一下,HTMLCleaner將產生這樣的輸出。

          這是標題
          
          
          
                  如果應用程序X沒有啟動,可能的原因是:
                  1.Maven沒有安裝
                  2.沒有足夠的(<1G)磁盤空間
                  3.內存不足(<64MB)
          

          我們可以看到,<script>元素中的內容被忽略了, <br/>標簽轉換為提取的文本中的換行符。另外, HTMLCleaner 保留了HTML的空白內容。

          總結

          在這篇文章中,我們學習了幾種去除HTML的方法,我們需要注意的是,正則在文本處理的過程中并不是萬能的。

          程目標

          目標1:完成商家后臺商品列表的功能

          目標2:完成商家后臺商品修改的功能

          目標3:完成運營商后臺商品審核的功能

          目標4:完成運營商后臺商品刪除的功能

          目標5:掌握注解式事務的配置

          1.商家后臺-商品管理【商品列表】

          1.1需求分析

          在商家后臺,顯示該商家的商品列表信息,如下圖:

          1.2查詢商家商品列表

          1.2.1后端代碼

          修改pinyougou-shop-web工程的GoodsController.java的search方法

          	@RequestMapping("/search")
          	public PageResult search(@RequestBody TbGoods goods, int page, int rows ){
          		//獲取商家ID
          		String sellerId = SecurityContextHolder.getContext().getAuthentication().getName();
          		//添加查詢條件 
          		goods.setSellerId(sellerId);		
          		return goodsService.findPage(goods, page, rows);		
          	}
          

          修改pinyougou-sellergoods-service 工程com.pinyougou.sellergoods.service.impl 的findPage方法,修改條件構建部分代碼,將原來的模糊匹配修改為精確匹配

          if(goods.getSellerId()!=null && goods.getSellerId().length()>0){
          	//criteria.andSellerIdLike("%"+goods.getSellerId()+"%");
          	criteria.andSellerIdEqualTo(goods.getSellerId());
          }
          

          1.2.2前端代碼

          修改goods.html. 引入js

          <script type="text/javascript" src="../plugins/angularjs/angular.min.js"></script>
          <!-- 分頁組件開始 -->
          <script src="../plugins/angularjs/pagination.js"></script>
          <link rel="stylesheet" href="../plugins/angularjs/pagination.css">
          <!-- 分頁組件結束 -->
          <script type="text/javascript" src="../js/base_pagination.js"></script>	
          <script type="text/javascript" src="../js/service/goodsService.js"></script>
          <script type="text/javascript" src="../js/service/itemCatService.js"></script>
          <script type="text/javascript" src="../js/service/uploadService.js"></script>
          <script type="text/javascript" src="../js/service/typeTemplateService.js"></script>
          <script type="text/javascript" src="../js/controller/baseController.js"></script>
          <script type="text/javascript" src="../js/controller/goodsController.js"></script>
          

          添加指令

          <body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="goodsController">
          

          在頁面上放置分頁控件

          <tm-pagination conf="paginationConf"></tm-pagination>
          

          循環列表

          <tr ng-repeat="entity in list">
           <td><input type="checkbox"></td>			 
           	<td>{{entity.id}}</td>
           <td>{{entity.goodsName}}</td>
           <td>{{entity.price}}</td>
           <td>{{entity.category1Id}}</td>
           <td>{{entity.category2Id}}</td>
           <td>{{entity.category3Id}}</td>
           <td>
           	{{entity.auditStatus}}
           </td>		 
           <td class="text-center"> 
           	 <button type="button" class="btn bg-olive btn-xs">修改</button> 
           </td>
          </tr>
          

          顯示效果如下:

          1.3顯示狀態

          修改goodsController.js,添加state數組

          $scope.status=['未審核','已審核','審核未通過','關閉'];//商品狀態
          

          修改列表顯示

          {{status[entity.auditStatus]}}
          

          顯示效果如下:

          1.4顯示分類

          我們現在的列表中的分類仍然顯示ID

          如何才能顯示分類的名稱呢?

          方案一:在后端代碼寫關聯查詢語句,返回的數據中直接有分類名稱。

          方案二:在前端代碼用ID去查詢后端,異步返回商品分類名稱。

          我們目前采用方案二:

          (1)修改goodsController

          $scope.itemCatList=[];//商品分類列表
          //加載商品分類列表
          $scope.findItemCatList=function(){		
          	itemCatService.findAll().success(
          			function(response){							
          				for(var i=0;i<response.length;i++){
          					$scope.itemCatList[response[i].id]=response[i].name;
          				}
          			}
          	);
          }
          

          代碼解釋:因為我們需要根據分類ID得到分類名稱,所以我們將返回的分頁結果以數組形式再次封裝。

          (2)修改goods.html ,增加初始化調用

          <body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="goodsController" ng-init="findItemCatList()">
          

          (3)修改goods.html , 修改列表

          <td>{{itemCatList[entity.category1Id]}}</td>
          <td>{{itemCatList[entity.category2Id]}}</td>
          <td>{{itemCatList[entity.category3Id]}}</td>
          

          1.5條件查詢

          根據狀態和商品名稱進行查詢

          修改goods.html

          <div class="has-feedback">
           	狀態:<select ng-model="searchEntity.auditStatus">
           	 <option value="">全部</option> 
           <option value="0">未審核</option> 
           <option value="1">已審核</option> 
           <option value="2">審核未通過</option> 
           <option value="3">關閉</option> 
           </select>
          		商品名稱:<input ng-model="searchEntity.goodsName">						
          		<button class="btn btn-default" ng-click="reloadList()">查詢</button> 
          </div>
          

          2.商家后臺-商品管理【商品修改】

          2.1需求分析

          在商品列表頁面點擊修改,進入商品編輯頁面,并傳遞參數商品ID,商品編輯頁面接受該參數后從數據庫中讀取商品信息,用戶修改后保存信息。

          2.2基本信息讀取

          我們首選讀取商品分類、商品名稱、品牌,副標題,價格等信息

          2.2.1后端代碼

          (1)修改pinyougou-sellergoods-interface的GoodsService.java

          	/**
          	 * 根據ID獲取實體
          	 * @param id
          	 * @return
          	 */
          	public Goods findOne(Long id);
          

          (2)修改pinyougou-sellergoods-service的GoodsServiceImpl.java

          	@Override
          	public Goods findOne(Long id) {
          		Goods goods=new Goods();
          		TbGoods tbGoods = goodsMapper.selectByPrimaryKey(id);
          		goods.setGoods(tbGoods);
          		TbGoodsDesc tbGoodsDesc = goodsDescMapper.selectByPrimaryKey(id);
          		goods.setGoodsDesc(tbGoodsDesc);
          		return goods;
          	}
          

          (3)修改pinyougou-shop-web(和pinyougou-manager-web)的GoodsController.java

          	/**
          	 * 獲取實體
          	 * @param id
          	 * @return
          	 */
          	@RequestMapping("/findOne")
          	public Goods findOne(Long id){
          		return goodsService.findOne(id);		
          	}
          

          2.2.2前端代碼

          (1)在goodsController中引入$location服務

          //商品控制層(商家后臺)
          app.controller('goodsController',function($scope,$controller,$location,goodsService,uploadService,item_catService,type_templateService){
          ......
          

          (2)修改goodsController 添加代碼:

          	//查詢實體 
          	$scope.findOne=function(){			
          		var id= $location.search()['id'];//獲取參數值
          		if(id==null){
          			return ;
          		}
          		goodsService.findOne(id).success(
          			function(response){
          				$scope.entity= response;					
          			}
          		);				
          	}
          

          在goods_edit.html頁面上添加指令

          <body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="goodsController" ng-init="selectItemCat1List();findOne()">
          

          測試:

          地址欄輸入

          http://localhost:9102/admin/goods_edit.html#?id=149187842867969

          注意: ?前要加# ,則是angularJS的地址路由的書寫形式

          2.3讀取商品介紹(富文本編輯器)

          修改前端代碼 goodsController

          //查詢實體 
          $scope.findOne=function(){			
          		.................
          		goodsService.findOne(id).success(
          			function(response){
          				$scope.entity= response;	
          				//向富文本編輯器添加商品介紹
          				editor.html($scope.entity.goodsDesc.introduction);
          			}
          		);				
          }
          

          2.4顯示商品圖片列表

          修改goodsController.js ,在dataLogic方法添加代碼,將圖片列表由字符串轉換為json集合對象

          //查詢實體 
          $scope.findOne=function(){		
           ..............	
          		//如果有ID,則查詢實體
          		goodsService.findOne(id).success(
          			function(response){
          				 $scope.entity= response;	
          				 //向富文本編輯器添加商品介紹
          				 editor.html($scope.entity.goodsDesc.introduction);
          				 //顯示圖片列表
          $scope.entity.goodsDesc.itemImages= 
          JSON.parse($scope.entity.goodsDesc.itemImages);
          			}
          		);				
          }
          

          2.5讀取商品擴展屬性

          修改goodsController.js

          	//查詢實體 
          	$scope.findOne=function(){			
          		.........
          		goodsService.findOne(id).success(
          			function(response){
          				.......................
          				//顯示擴展屬性
          				$scope.entity.goodsDesc.customAttributeItems= JSON.parse($scope.entity.goodsDesc.customAttributeItems);	
          			}
          		);				
          	}		
          

          經過測試,我們發現擴展屬性值并沒有讀取出來,這是因為與下列代碼發生沖突

          $scope.$watch('entity.goods.typeTemplateId',function(newValue,oldValue){
           ......
          $scope.entity.goodsDesc.customAttributeItems = JSON.parse($scope.typeTemplate.customAttributeItems);//擴展屬性
          }
          

          我們讀取出來的值被覆蓋了,我們需要改寫代碼, 添加判斷,當用戶沒有傳遞id參數時再執行此邏輯

          //監控模板ID ,讀取品牌列表
          $scope.$watch('entity.goods.typeTemplateId',function(newValue,oldValue){
          		//讀取品牌列表和擴展屬性
          		typeTemplateService.findOne(newValue).success(
          			function(response){
          				.......
          				//如果沒有ID,則加載模板中的擴展數據
          				if($location.search()['id']==null){
          					$scope.entity.goodsDesc.customAttributeItems = JSON.parse($scope.typeTemplate.customAttributeItems);//擴展屬性	
          				}				
          			}
          		);
          		.......
          });
          

          2.6讀取商品規格屬性

          修改goodsController

          	//查詢實體 
          	$scope.findOne=function(){		
          		......
          		goodsService.findOne(id).success(
          			function(response){
          				$scope.entity= response;		
          				editor.html($scope.entity.goodsDesc.introduction);//商品介紹
          				$scope.entity.goodsDesc.itemImages= JSON.parse($scope.entity.goodsDesc.itemImages);//圖片列表
          				//擴展屬性列表
          				$scope.entity.goodsDesc.customAttributeItems =JSON.parse($scope.entity.goodsDesc.customAttributeItems);
          				//規格				$scope.entity.goodsDesc.specificationItems=JSON.parse($scope.entity.goodsDesc.specificationItems);				
          			}
          		);				
          	}
          //根據規格名稱和選項名稱返回是否被勾選
          $scope.checkAttributeValue=function(specName,optionName){
          	var items= $scope.entity.goodsDesc.specificationItems;
          	var object= $scope.searchObjectByKey(items,'attributeName',specName);
          	if(object==null){
          		return false;
          	}else{
          		if(object.attributeValue.indexOf(optionName)>=0){
          			return true;
          		}else{
          			return false;
          		}
          	}			
          }
          

          修改頁面上規格面板的復選框,運用 ng-checked指令控制復選框的勾選狀態

          <input type="checkbox" 		ng-click="updateSpecAttribute($event,pojo.text,p.optionName);createSKUTable()" 		ng-checked="checkAttributeValue(pojo.text,p.optionName)">{{p.optionName}}
          

          2.7讀取SKU數據

          顯示SKU商品列表,并自動讀取價格、庫存等數據加載到列表中

          2.7.1后端代碼

          在GoodsServiceImpl的findOne方法中加載SKU商品數據

          		//查詢SKU商品列表
          		TbItemExample example=new TbItemExample();
          		com.pinyougou.pojo.TbItemExample.Criteria criteria = example.createCriteria();
          		criteria.andGoodsIdEqualTo(id);//查詢條件:商品ID
          		List<TbItem> itemList = itemMapper.selectByExample(example);		
          		goods.setItemList(itemList);
          

          2.7.2前端代碼

          在goodsController.js修改findOne方法的代碼

          //查詢實體 
          $scope.findOne=function(){			
          	........
          	goodsService.findOne(id).success(
          		function(response){
          			$scope.entity= response;	
          			.........			
          			//SKU列表規格列轉換				
          			for( var i=0;i<$scope.entity.itemList.length;i++ ){
          $scope.entity.itemList[i].spec = 
          JSON.parse( $scope.entity.itemList[i].spec);		
          			}			
          		}
          	);				
          }
          

          2.8保存數據

          2.8.1后端代碼

          修改 pinyougou-sellergoods-interface 的 GoodsService.java

          public void update(Goods goods);
          

          修改pinyougou-sellergoods-service的GoodsServiceImpl ,將SKU列表插入的代碼提取出來,封裝到私有方法中

          /**
           * 插入SKU列表數據
           * @param goods
           */
          private void saveItemList(Goods goods){		
          	if("1".equals(goods.getGoods().getIsEnableSpec())){
          		for(TbItem item :goods.getItemList()){
          			.........中間代碼略
          		}		
          	}else{			
          		TbItem item=new TbItem();
          		.........中間代碼略
          		itemMapper.insert(item);
          	}		
          }
          

          在add方法中調用 此方法,修改如下:

          public void add(Goods goods) {
          	goods.getGoods().setAuditStatus("0");		
          	goodsMapper.insert(goods.getGoods());	//插入商品表
          	goods.getGoodsDesc().setGoodsId(goods.getGoods().getId());
          	goodsDescMapper.insert(goods.getGoodsDesc());//插入商品擴展數據
          	saveItemList(goods);//插入商品SKU列表數據
          }
          

          怎么樣,是不是比原來更加清爽了呢?

          接下來,我們修改update方法,實現修改

          public void update(Goods goods){
          	goods.getGoods().setAuditStatus("0");//設置未申請狀態:如果是經過修改的商品,需要重新設置狀態
          	goodsMapper.updateByPrimaryKey(goods.getGoods());//保存商品表
          	goodsDescMapper.updateByPrimaryKey(goods.getGoodsDesc());//保存商品擴展表
          	//刪除原有的sku列表數據		
          	TbItemExample example=new TbItemExample();
          	com.pinyougou.pojo.TbItemExample.Criteria criteria = example.createCriteria();
          	criteria.andGoodsIdEqualTo(goods.getGoods().getId());	
          	itemMapper.deleteByExample(example);
          	//添加新的sku列表數據
          	saveItemList(goods);//插入商品SKU列表數據	
          }	
          

          修改pinyougou-manager-web工程的GoodsController.java

          	@RequestMapping("/update")
          	public Result update(@RequestBody Goods goods){
          		......
          	}	
          

          修改pinyougou-shop-web工程的GoodsController.java

          	/**
          	 * 修改
          	 * @param goods
          	 * @return
          	 */
          	@RequestMapping("/update")
          	public Result update(@RequestBody Goods goods){
          		//校驗是否是當前商家的id		
          		Goods goods2 = goodsService.findOne(goods.getGoods().getId());
          		//獲取當前登錄的商家ID
          		String sellerId = SecurityContextHolder.getContext().getAuthentication().getName();
          		//如果傳遞過來的商家ID并不是當前登錄的用戶的ID,則屬于非法操作
          		if(!goods2.getGoods().getSellerId().equals(sellerId) || !goods.getGoods().getSellerId().equals(sellerId) ){
          			return new Result(false, "操作非法");		
          		}		
          		try {
          			goodsService.update(goods);
          			return new Result(true, "修改成功");
          		} catch (Exception e) {
          			e.printStackTrace();
          			return new Result(false, "修改失敗");
          		}
          	}
          

          代碼解釋:出于安全考慮,在商戶后臺執行的商品修改,必須要校驗提交的商品屬于該商戶

          2.8.2前端代碼

          (1)修改goodsController.js ,新增保存的方法

          //保存 
          $scope.save=function(){			
          	//提取文本編輯器的值
          	$scope.entity.goodsDesc.introduction=editor.html();	
          	var serviceObject;//服務層對象 				
          	if($scope.entity.goods.id!=null){//如果有ID
          		serviceObject=goodsService.update( $scope.entity ); //修改 
          	}else{
          		serviceObject=goodsService.add( $scope.entity );//增加 
          	}				
          	serviceObject.success(
          		function(response){
          			if(response.success){
          				alert('保存成功');					
          				$scope.entity={};
          				editor.html("");
          			}else{
          				alert(response.message);
          			}
          		}		
          	);				
          }
          

          (2)修改goods_edit.html 調用

          <button class="btn btn-primary" ng-click="save()"><i class="fa fa-save"></i>保存</button>
          

          2.9頁面跳轉

          (1)由商品列表頁跳轉到商品編輯頁

          修改goods.html表格行的修改按鈕

           <a href="goods_edit.html#?id={{entity.id}}" class="btn bg-olive btn-xs">修改</a>
          

          (2)由商品編輯頁跳轉到商品列表

          修改goods_edit.html 的返回列表按鈕

          <a href="goods.html" class="btn btn-default">返回列表</a>
          

          (3)保存成功后返回列表頁面

          	//保存 
          	$scope.save=function(){		
          		.....		
          		serviceObject.success(
          			function(response){
          				if(response.success){					
          					location.href="goods.html";//跳轉到商品列表頁
          				}else{
          					alert(response.message);
          				}
          			}		
          		);				
          	}	
          

          3.運營商后臺-商品管理【商品審核】

          3.1待審核商品列表

          需求:參照商家后臺商品列表。代碼:

          (1)修改pinyougou-manager-web的goodsController.js,注入itemCatService,添加代碼

          	$scope.status=['未審核','已審核','審核未通過','關閉'];//商品狀態
          	$scope.itemCatList=[];//商品分類列表
          	//查詢商品分類
          	$scope.findItemCatList=function(){
          		itemCatService.findAll().success(
          			function(response){
          				for(var i=0;i<response.length;i++){
          					$scope.itemCatList[response[i].id ]=response[i].name;		
          				}					
          			}		
          		);		
          	}
          

          (2)修改goods.html ,引入js

          <script type="text/javascript" src="../plugins/angularjs/angular.min.js"></script>
          <!-- 分頁組件開始 -->
          <script src="../plugins/angularjs/pagination.js"></script>
          <link rel="stylesheet" href="../plugins/angularjs/pagination.css">
          <!-- 分頁組件結束 -->
          <script type="text/javascript" src="../js/base_pagination.js"></script>	
          <script type="text/javascript" src="../js/service/goodsService.js"></script>
          <script type="text/javascript" src="../js/service/itemCatService.js"></script>
          <script type="text/javascript" src="../js/controller/baseController.js"></script>
          <script type="text/javascript" src="../js/controller/goodsController.js"></script>
          

          (3)指令,完成初始調用

          <body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="goodsController" ng-init="searchEntity={auditStatus:'0'};findItemCatList()">
          

          (4)循環列表

          <tr ng-repeat="entity in list">
           <td><input type="checkbox"></td>			 
           	<td>{{entity.id}}</td>
           <td>{{entity.goodsName}}</td>
           <td>{{entity.price}}</td>
           <td>{{itemCatList[entity.category1Id]}}</td>
          	<td>{{itemCatList[entity.category2Id]}}</td>
          	<td>{{itemCatList[entity.category3Id]}}</td>
           <td>{{status[entity.auditStatus]}}</td>		 
           <td class="text-center"> </td>
          </tr>
          

          (5)分頁控件

           <tm-pagination conf="paginationConf"></tm-pagination>
          

          3.2商品詳情展示(學員實現)

          需求:點擊列表右側的“詳情”按鈕,彈出窗口顯示商品信息。代碼略。

          3.3商品審核與駁回

          需求:商品審核的狀態值為1,駁回的狀態值為2 。用戶在列表中選中ID后,點擊審核或駁回,修改商品狀態,并刷新列表。

          3.3.1后端代碼

          (1)在pinyougou-sellergoods-interface的GoodsService.java新增方法定義

          	/**
          	 * 批量修改狀態
          	 * @param ids
          	 * @param status
          	 */
          	public void updateStatus(Long []ids,String status);
          

          (2)在pinyougou-sellergoods-service的GoodsServiceImpl.java實現該方法

          	public void updateStatus(Long[] ids, String status) {
          		for(Long id:ids){
          			TbGoods goods = goodsMapper.selectByPrimaryKey(id);
          			goods.setAuditStatus(status);
          			goodsMapper.updateByPrimaryKey(goods);
          		}
          	}
          

          (3)在pinyougou-shop-web的GoodsController.java新增方法

          	/**
          	 * 更新狀態
          	 * @param ids
          	 * @param status
          	 */
          	@RequestMapping("/updateStatus")
          	public Result updateStatus(Long[] ids, String status){		
          		try {
          			goodsService.updateStatus(ids, status);
          			return new Result(true, "成功");
          		} catch (Exception e) {
          			e.printStackTrace();
          			return new Result(false, "失敗");
          		}
          	}
          

          3.3.2前端代碼

          (1)修改pinyougou-manager-web的goodsService.js ,增加方法

          	//更改狀態
          	this.updateStatus=function(ids,status){
          		return $http.get('../goods/updateStatus.do?ids='+ids+"&status="+status);
          	} 
          

          (2)修改pinyougou-manager-web的goodsController.js ,增加方法

          	//更改狀態
          	$scope.updateStatus=function(status){		
          		goodsService.updateStatus($scope.selectIds,status).success(
          			function(response){
          				if(response.success){//成功
          					$scope.reloadList();//刷新列表
          					$scope.selectIds=[];//清空ID集合
          				}else{
          					alert(response.message);
          				}
          			}
          		);		
          	}
          

          (3)修改pinyougou-manager-web的goods.html 頁面,為復選框綁定事件指令

          <input type="checkbox" ng-click="updateSelection($event,entity.id)" >
          

          (4)修改頁面上的審核通過和駁回按鈕

           <button type="button" class="btn btn-default" title="審核通過" ng-click="updateStatus('1')"><i class="fa fa-check"></i> 審核通過</button>
          <button type="button" class="btn btn-default" title="駁回" ng-click="updateStatus('2')" ><i class="fa fa-ban"></i> 駁回</button>
          

          4.運營商后臺-商品管理【商品刪除】

          4.1需求分析

          我們為商品管理提供商品刪除功能,用戶選中部分商品,點擊刪除按鈕即可實現商品刪除。注意,這里的刪除并非是物理刪除,而是修改tb_goods表的is_delete字段為1 ,我們可以稱之為“邏輯刪除”

          4.2邏輯刪除的實現

          4.2.1后端代碼

          修改pinyougou-sellergoods-service工程的GoodsServiceImpl.java的delete方法

          	/**
          	 * 批量刪除
          	 */
          	@Override
          	public void delete(Long[] ids) {
          		for(Long id:ids){
          			TbGoods goods = goodsMapper.selectByPrimaryKey(id);
          			goods.setIsDelete("1");
          			goodsMapper.updateByPrimaryKey(goods);
          		}		
          	}
          

          4.2.2前端代碼

          修改pinyougou-manager-web的goods.html上的刪除按鈕

          <button type="button" class="btn btn-default" title="刪除" ng-click="dele()"><i class="fa fa-trash-o"></i> 刪除</button>
          

          4.3排除已刪除記錄

          修改pinyougou-sellergoods-service工程GoodsServiceImpl.java的findPage方法,添加以下代碼:

          criteria.andIsDeleteIsNull();//非刪除狀態
          

          5.商家后臺-【商品上下架】(學員實現)

          5.1需求分析

          什么是商品上下架?其實上下架也是商品的一個狀態,但是不同于審核狀態。審核狀態的控制權在運營商手中,而上下架的控制權在商戶手中。商戶可以隨時將一個已審核狀態的商品上架或下架。上架表示正常銷售,而下架則表示暫停銷售。

          5.2實現思路提示

          其實商品的上下架就是對上下架狀態的修改。字段為tb_goods表的is_marketable字段。1表示上架、0表示下架。

          6.注解式事務配置

          6.1事務異常測試

          我們修改pinyougou-sellergoods-service工程GoodsServiceImpl.java的add方法

          	/**
          	 * 增加
          	 */
          	@Override
          	public void add(Goods goods) {
          		goods.getGoods().setAuditStatus("0");		
          		goodsMapper.insert(goods.getGoods());	//插入商品表
          		int x=1/0;
          		goods.getGoodsDesc().setGoodsId(goods.getGoods().getId());
          		goodsDescMapper.insert(goods.getGoodsDesc());//插入商品擴展數據
          		saveItemList(goods);//插入商品SKU列表數據
          	}
          

          在插入商品表后,人為制造一個異常。我們運行程序,新增商品數據,觀察運行結果。

          通過觀察,我們發現,程序發生異常 ,商品表仍然會存儲記錄,這是不符合我們要求的。這是因為我們目前的系統還沒有配置事務。

          6.2注解式事務解決方案

          6.2.1配置文件

          在pinyougou-sellergoods-service工程的spring目錄下創建applicationContext-tx.xml

          <?xml version="1.0" encoding="UTF-8"?>
          <beans xmlns="http://www.springframework.org/schema/beans"
          	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
          	xmlns:context="http://www.springframework.org/schema/context"
          	xmlns:tx="http://www.springframework.org/schema/tx"
          	xmlns:mvc="http://www.springframework.org/schema/mvc"
          	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
          		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
           <!-- 事務管理器 --> 
           <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
           <property name="dataSource" ref="dataSource" /> 
           </bean> 
           <!-- 開啟事務控制的注解支持 --> 
           <tx:annotation-driven transaction-manager="transactionManager"/>
          </beans>
          

          6.2.2在方法上添加注解

          /**
           * 服務實現層
           * @author Administrator
           *
           */
          @Service
          @Transactional
          public class GoodsServiceImpl implements GoodsService{
          ........
          }
          

          經過測試,我們發現,系統發生異常,商品表不會新增記錄,事務配置成功。

          刪除掉測試代碼int x=1/0

          我們需要將所有涉及多表操作的服務類添加事務注解,例如SpecificationServiceImpl類

          文總結了幾點刪除按鈕的設計技巧,以防止誤操作導致防止數據丟失。enjoy~

          數據丟失是用戶使用計算機時可能遇到的最大意外之一。他們不僅丟失了數據,還損失了投入的時間和金錢。對于企業而言,這可能意味著數百個工時和數千美元的損失。不要讓這種情況發生在您的用戶身上。

          一項研究發現,人為錯誤導致30%的數據丟失。這意味著良好的用戶體驗設計可以防止這些意外發生。以下是一些預防技巧。

          在確認操作中使用紅色警告信號

          當用戶按下刪除按鈕時,請勿立即執行,有可能是用戶誤點擊了按鈕,您需提示用戶通過確認頁面確認操作。

          刪除按鈕請勿使用如藍色等常規顏色。應使用紅色按鈕警示用戶即將觸發危險性操作。紅色具有很強的視覺警告提示,因其更容易吸引用戶注意力。

          避免正常操作使用紅色按鈕,否則即為告警用戶。保留紅色按鈕僅用作刪除操作。冷色更適合使用于正常的行為呼叫按鈕,因為其警示作用較弱。

          雖然紅色按鈕能警示大部分用戶,但是有些用戶也許會忽視。額外的視覺提示,能增加警示作用。尤其是有助于無法區分色差的色盲和弱視用戶群。

          若要加強警告信號,請在確認屏幕上添加代表刪除操作的圖標。例如,用戶熟悉的刪除圖標是垃圾桶。當用戶看到圖標時,他們會將當前操作與刪除相關聯。

          您可以通過在屏幕頂部添加紅色條塊來加強警告信號。現在此確認頁面,用戶可以看到三個紅色警告信號,表明他們即將進行危險性操作。這使用戶更加注意他們的行為和處境,以防按錯按鈕。

          UX效益-打破慣性點擊

          用戶使用移動應用程序次數越多,他們越有可能養成慣性點擊的習慣。無意識慣性點擊可以更快更容易完成任務,但也更容易點擊刪除按鈕。

          紅色通常與警告和危險有聯系,并且具有負面含義。我們常看到許多用紅色來傳達警告和危險的標志。在設計中,紅色按鈕會引起用戶對傷害或損失的恐懼,以防止錯誤。這是人類為了生存而躲避危險的本能。

          研究表明,紅色物體會引起注意并促進一致的運動反應。這意味著,當用戶看到紅色的刪除按鈕時,他們可能會更快、更準確地做出響應。用戶對任務的關注越多,他們就會越好的執行該任務。

          簡潔的會話窗文本

          紅色警告信號可以防止刪除意外,但這不是您須考慮的唯一事項。您還需編寫簡潔的會話文本來確保其易于瀏覽。

          在會話窗標題的末尾添加問號來替代詢問用戶“您確定要刪除嗎?”。例如,標題為“刪除帳戶?”,用較少的字詞表示“您確定要刪除帳戶嗎?”。

          不僅如此,不要使用冗長的句子來解釋點擊確認后會發生什么。以列表格式列出他們將丟失的內容,來替代通知用戶,“如果您刪除了自己的帳戶,則會永久丟失您的個人資料,消息和照片”,以便用戶快速閱覽。

          在該示例中,會話窗文本通過從25個單詞減少到僅9個單詞來簡化。使得會話窗更容易瀏覽和理解。

          UX效益更好地理解后果

          確認對話框的目的是描述刪除操作的后果。用戶需要閱讀并理解這些,否則他們可能會得到意想不到的結果。但在冗長的對話中很難做到這一點。

          大多數用戶會跳過冗長的文字,因為閱讀需要花費時間和精力。簡潔的文本可以防止用戶跳過,幫助用戶更快地執行任務,減少錯誤,并記住更多信息。使用簡潔的對話文本,用戶可以更好地理解其行為的后果并做出正確的決定。

          居中對齊布局

          簡潔的文本使其容易瀏覽。但你還可以通過中心對齊布局使整個會話窗更進一步加強其易讀性。中心對齊布局將圖標與會話窗文本對齊,以便用戶可以同時瀏覽。還使會話窗對稱,圖標更加突出,以防遮擋。

          UX效益-減少視覺工作

          當使用視覺追蹤圖觀察一個左對齊布局和冗長的文本會話窗時,會發現有更多的注視點和更長的瀏覽路徑。

          簡潔的會話窗和中心對齊的布局只需較少的視覺工作。通過更少的注視點和更短的瀏覽路徑,用戶可以更快地瀏覽屏幕以做出明智的決定。

          • 中心對齊布局可讓用戶以單一視覺方向(從上到下)瀏覽屏幕。他們不需要移動眼睛來瀏覽屏幕,只需要專注于屏幕的中心。
          • 使用左對齊布局,用戶需要以兩個可視方向(從左到右和從右到左)瀏覽屏幕。用戶的眼睛需要做更多的工作,從而降低任務的完成效率。

          確認保留紅色警告信號

          • 當刪除按鈕出現在確認頁面上時,您希望用戶全神貫注。
          • 相反,當刪除按鈕未出現時,您不希望引起用戶對它的注意。這樣做會使用戶無意點擊時誤點刪除按鈕。

          如果刪除按鈕不在確認頁面上時,請勿使用紅色警告信號。例如,設置頁面可以有一個“刪除帳戶”按鈕,但它不需像呼叫行為按鈕來吸引不必要的注意。

          最好將刪除按鈕設為僅帶有紅色文本標簽的獨立按鈕。使用過多的紅色會導致用戶將其誤認為是屏幕上的主呼叫行為按鈕。

          UX效益-增加意外發生難度

          用戶在確認頁面上的停留時間越多,他們按錯按鈕的可能性就越大。通過思考其他屏幕上的刪除按鈕,用戶不太可能意外進入確認頁面。這使得他們遠離危險。

          在其他頁面上將刪除按鈕與正常按鈕分開也可以使用戶遠離危險。用戶不會將其誤認為是正常的號召性按鈕并想按下它。

          提供撤消按鈕

          即使有確認屏幕,意外仍然可能發生。有些用戶仍然可能誤讀了會話窗或按錯了按鈕。在確認屏幕之后,向用戶提供撤消按鈕,其中包含告知用戶已執行操作的消息。

          將撤消按鈕和完成消息放在屏幕底部的通知橫幅中。您可以根據刪錯操作的上下文使撤消按鈕成為臨時或持久的。

          臨時撤消將使橫幅在幾秒鐘后自動消失。持久撤消顯示橫幅,直到用戶通過按“關閉”按鈕關閉橫幅。請注意,持久性撤消的技術實現比臨時撤消更復雜。

          UX效益-允許用戶從事故中恢復操作

          撤消刪除操作的選項允許用戶從事故中恢復操作以防止數據丟失。數據丟失對企業和人們的生活造成嚴重后果。發布確認撤消按鈕不僅可以保存用戶的數據,還可以保存用戶的工作。

          提示用戶點擊確認

          如果撤消按鈕不是可選項,則可以提示用戶在文本框中輸入刪除以確認。提示用戶輸入確認使他們意識到刪除行為。雖然容易意外按錯按鈕,但是不可能輸入意外出錯,因為此操作需要很多步驟。

          此方法對于用戶經常使用刪除操作的效率不高。例如,刪除帖子是社交媒體應用上的常見操作。若要求用戶每次通過輸入來確認將大大降低用戶操作效率。僅用于類型罕見的刪除操作。

          UX效益-確保用戶確認意識

          無意識地按下按鈕比輸入單詞要容易得多。當用戶輸入時,他們必須考慮他們正在輸入的內容然后點擊右邊的確認按鈕。與按下按鈕相比,出錯空間更大。這使用戶意識到他們的確認行為,以防止意外按下按鈕。

          數據丟失意外

          當用戶進入確認屏幕時,他們處于意外發生的邊緣。如果您沒有采取措施防止這種情況發生,按錯了按鈕可能會損壞數據。將這些方法應用到您的應用中,將避免用戶遇到數據丟失意外。

          PS:翻譯過程中為適合我們的閱讀習慣以及個人的理解,有對原文進行一定的內容簡化和語義修飾,如有不妥歡迎大家根據官網鏈接進行比對并留言互動。

          (本文翻譯已獲得該網站的正式授權)

          原文鏈接:https://uxmovement.com/buttons/how-to-design-destructive-actions-that-prevent-data-loss/

          原作者:anthony

          譯文地址:https://www.zcool.com.cn/article/ZMTAxNzcwMA==.html

          編譯作者:黎沫limo

          本文由 @黎沫limo 翻譯發布于人人都是產品經理,未經作者許可,禁止轉載。

          題圖來自Unsplash,基于CC0協議。


          主站蜘蛛池模板: 亚洲国产成人一区二区三区| 乱人伦一区二区三区| 无码人妻少妇色欲AV一区二区| 人妻夜夜爽天天爽爽一区| 成人区人妻精品一区二区不卡视频| 福利一区二区视频| 精品一区二区三区四区在线播放| 天堂一区人妻无码| 无码人妻精品一区二区三区东京热| 亚洲一区二区三区成人网站| 国产成人精品一区在线| 卡通动漫中文字幕第一区| 日韩爆乳一区二区无码| 爆乳熟妇一区二区三区| 亚洲男人的天堂一区二区| 久久er99热精品一区二区 | 一区精品麻豆入口| 国产在线乱子伦一区二区| 红杏亚洲影院一区二区三区| 狠狠综合久久av一区二区| 亚洲一区二区三区在线观看蜜桃 | 亚洲男女一区二区三区| 亚洲一区二区三区国产精品| 丰满岳乱妇一区二区三区| 亚洲av乱码中文一区二区三区 | 久久精品视频一区| 亚洲AV午夜福利精品一区二区| 无码中文字幕一区二区三区| 乱人伦一区二区三区| 国产福利91精品一区二区| 亚洲av无码一区二区三区不卡| 一区二区三区视频免费观看| 无码乱人伦一区二区亚洲| 无码夜色一区二区三区| 国产精品福利一区二区| 精品视频一区二区三区四区五区| 一区二区三区福利视频| 无码人妻一区二区三区av| 日韩美一区二区三区| 人妻AV一区二区三区精品| 无码人妻精品一区二区三区不卡 |