Browse Source

冷链送药app1.0

qianduan 9 months ago
commit
245aac9331
100 changed files with 19867 additions and 0 deletions
  1. 13 0
      .env.js
  2. 5 0
      .gitignore
  3. 16 0
      .hbuilderx/launch.json
  4. 39 0
      App.vue
  5. 31 0
      common/amap-wx.130.js
  6. 392 0
      common/barcode.js
  7. 92 0
      common/downApp.js
  8. 112 0
      common/index.scss
  9. 22 0
      components/x-barCode.vue
  10. 67 0
      components/x-bluetooth.vue
  11. 66 0
      components/x-checkbox.vue
  12. 201 0
      components/x-form.vue
  13. 151 0
      components/x-humiture.vue
  14. 97 0
      components/x-navbottom.vue
  15. 45 0
      components/x-orderCard.vue
  16. 328 0
      components/x-orderManagement.vue
  17. 34 0
      components/x-radar.vue
  18. 156 0
      components/x-statistics.vue
  19. 160 0
      components/x-steps.vue
  20. 21 0
      index.html
  21. 33 0
      main.js
  22. 133 0
      manifest.json
  23. 135 0
      package-lock.json
  24. 6 0
      package.json
  25. 82 0
      pages.json
  26. 290 0
      pages/codeLogin.vue
  27. 128 0
      pages/home/index.vue
  28. 192 0
      pages/home/particulars.vue
  29. 92 0
      pages/indexRouter.vue
  30. 261 0
      pages/login.vue
  31. 841 0
      pages/mine/Printreading.vue
  32. 423 0
      pages/mine/index.vue
  33. 131 0
      pages/mine/password.vue
  34. 198 0
      pages/order/addWaybill.vue
  35. 492 0
      pages/order/delivery.vue
  36. 41 0
      pages/order/gprint/code128.js
  37. 243 0
      pages/order/gprint/commands.js
  38. 9 0
      pages/order/gprint/encoding-indexes.js
  39. 3313 0
      pages/order/gprint/encoding.js
  40. 196 0
      pages/order/gprint/gbk.js
  41. 205 0
      pages/order/gprint/printerjobs.js
  42. 92 0
      pages/order/gprint/printerutil.js
  43. 66 0
      pages/order/gprint/util.js
  44. 4 0
      pages/order/gprint/weapp.qrcode.esm.js
  45. 172 0
      pages/order/humiture.vue
  46. 294 0
      pages/order/index.vue
  47. 211 0
      pages/order/logisticsDetails.vue
  48. 243 0
      pages/order/orderDetails.vue
  49. 721 0
      pages/order/quantum.vue
  50. 133 0
      pages/order/waybill.js
  51. 267 0
      pages/register.vue
  52. 6 0
      smartAppversion.json
  53. BIN
      static/back.png
  54. 65 0
      static/css/styles.scss
  55. 539 0
      static/fonts/demo.css
  56. 441 0
      static/fonts/demo_index.html
  57. 59 0
      static/fonts/iconfont.css
  58. 0 0
      static/fonts/iconfont.js
  59. 86 0
      static/fonts/iconfont.json
  60. BIN
      static/fonts/iconfont.ttf
  61. BIN
      static/fonts/iconfont.woff
  62. BIN
      static/fonts/iconfont.woff2
  63. 452 0
      static/js/BluetoothTool.js
  64. 9 0
      static/js/encoding-indexes.js
  65. 3312 0
      static/js/encoding.js
  66. 312 0
      static/js/esc.js
  67. 222 0
      static/js/tsc.js
  68. BIN
      static/logo.png
  69. BIN
      static/portrait.png
  70. BIN
      static/task/arrows.png
  71. BIN
      static/task/endpoint.png
  72. BIN
      static/task/startpoint.png
  73. BIN
      static/unfold.png
  74. 47 0
      store/storage.js
  75. 10 0
      uni.promisify.adaptor.js
  76. 76 0
      uni.scss
  77. 97 0
      utils/request.js
  78. 21 0
      uview-ui/LICENSE
  79. 66 0
      uview-ui/README.md
  80. 362 0
      uview-ui/changelog.md
  81. 78 0
      uview-ui/components/u--form/u--form.vue
  82. 47 0
      uview-ui/components/u--image/u--image.vue
  83. 73 0
      uview-ui/components/u--input/u--input.vue
  84. 44 0
      uview-ui/components/u--text/u--text.vue
  85. 48 0
      uview-ui/components/u--textarea/u--textarea.vue
  86. 54 0
      uview-ui/components/u-action-sheet/props.js
  87. 278 0
      uview-ui/components/u-action-sheet/u-action-sheet.vue
  88. 59 0
      uview-ui/components/u-album/props.js
  89. 259 0
      uview-ui/components/u-album/u-album.vue
  90. 44 0
      uview-ui/components/u-alert/props.js
  91. 243 0
      uview-ui/components/u-alert/u-alert.vue
  92. 52 0
      uview-ui/components/u-avatar-group/props.js
  93. 103 0
      uview-ui/components/u-avatar-group/u-avatar-group.vue
  94. 78 0
      uview-ui/components/u-avatar/props.js
  95. 58 0
      uview-ui/components/u-avatar/u-avatar.vue
  96. 54 0
      uview-ui/components/u-back-top/props.js
  97. 129 0
      uview-ui/components/u-back-top/u-back-top.vue
  98. 72 0
      uview-ui/components/u-badge/props.js
  99. 171 0
      uview-ui/components/u-badge/u-badge.vue
  100. 46 0
      uview-ui/components/u-button/nvue.scss

+ 13 - 0
.env.js

@@ -0,0 +1,13 @@
+const UNI_APP = {
+	APP_DEV_URL: 'https://colddelivery.coldbaozhida.com/cold_delivery',
+	APP_PROD_URL: 'https://colddelivery.coldbaozhida.com/cold_delivery',
+	APP_LINK_URL: 'https://colddelivery.coldbaozhida.com',
+	// APP_DEV_URL: 'http://192.168.11.77:6280', //测试
+	// APP_PROD_URL: 'http://192.168.11.77:6280', //测试
+	// APP_LINK_URL: 'http://192.168.11.77:6280', //测试
+
+	AES_KEY: "675I3123J3Toq10L",
+	AES_IV: "AdC01PoIU3LnSh10",
+}
+
+module.exports = UNI_APP;

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+node_modules/
+uni_modules/
+.project
+unpackage/
+.DS_Store

+ 16 - 0
.hbuilderx/launch.json

@@ -0,0 +1,16 @@
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+    "version": "0.0",
+    "configurations": [{
+     	"app-plus" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"default" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"type" : "uniCloud"
+     }
+    ]
+}

+ 39 - 0
App.vue

@@ -0,0 +1,39 @@
+<script>
+	import {
+		checkUpdateApp
+	} from './common/downApp.js';
+	// 冷链送药移动端
+	export default {
+		onLaunch: function() {
+			// console.log('App Launch')
+		},
+		onShow: function() {
+			// #ifdef APP-PLUS 
+			uni.getSystemInfo({
+				success: (res) => {
+					var that = this;
+					//检测当前平台,如果是安卓则启动安卓更新  
+					if (res.platform == "android") {
+						checkUpdateApp();
+					}
+				}
+			})
+			// #endif
+			// console.log('App Show')
+		},
+		onHide: function() {
+			// console.log('App Hide')
+		},
+	}
+</script>
+
+<style lang="scss">
+	/*每个页面公共css */
+	@import "uview-ui/index.scss";
+	@import "common/index.scss";
+	@import url(@/static/fonts/iconfont.css);
+
+	page {
+		background-color: #f3f4f6;
+	}
+</style>

+ 31 - 0
common/amap-wx.130.js

@@ -0,0 +1,31 @@
+function AMapWX(a){this.key=a.key;this.requestConfig={key:a.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};this.MeRequestConfig={key:a.key,serviceName:"https://restapi.amap.com/rest/me"}}
+AMapWX.prototype.getWxLocation=function(a,b){wx.getLocation({type:"gcj02",success:function(c){c=c.longitude+","+c.latitude;wx.setStorage({key:"userLocation",data:c});b(c)},fail:function(c){wx.getStorage({key:"userLocation",success:function(d){d.data&&b(d.data)}});a.fail({errCode:"0",errMsg:c.errMsg||""})}})};
+AMapWX.prototype.getMEKeywordsSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.keywords&&(d.keywords=b.keywords);b.city&&(d.city=b.city);b.filter&&(d.filter=b.filter);b.sortrule&&(d.sortrule=b.sortrule);b.pageNum&&(d.pageNum=b.pageNum);b.pageSize&&(d.pageSize=b.pageSize);b.sig&&(d.sig=
+b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/local",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})};
+AMapWX.prototype.getMEIdSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.id&&(d.id=b.id);b.sig&&(d.sig=b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/id",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&
+0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})};
+AMapWX.prototype.getMEPolygonSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.keywords&&(d.keywords=b.keywords);b.polygon&&(d.polygon=b.polygon);b.filter&&(d.filter=b.filter);b.sortrule&&(d.sortrule=b.sortrule);b.pageNum&&(d.pageNum=b.pageNum);b.pageSize&&(d.pageSize=b.pageSize);
+b.sig&&(d.sig=b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/polygon",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})};
+AMapWX.prototype.getMEaroundSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.keywords&&(d.keywords=b.keywords);b.center&&(d.center=b.center);b.radius&&(d.radius=b.radius);b.filter&&(d.filter=b.filter);b.sortrule&&(d.sortrule=b.sortrule);b.pageNum&&(d.pageNum=b.pageNum);b.pageSize&&
+(d.pageSize=b.pageSize);b.sig&&(d.sig=b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/around",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})};
+AMapWX.prototype.getGeo=function(a){var b=this.requestConfig,c=a.options;b={key:this.key,extensions:"all",s:b.s,platform:b.platform,appname:this.key,sdkversion:b.sdkversion,logversion:b.logversion};c.address&&(b.address=c.address);c.city&&(b.city=c.city);c.batch&&(b.batch=c.batch);c.sig&&(b.sig=c.sig);wx.request({url:"https://restapi.amap.com/v3/geocode/geo",data:b,method:"GET",header:{"content-type":"application/json"},success:function(d){(d=d.data)&&d.status&&"1"===d.status?a.success(d):a.fail({errCode:"0",
+errMsg:d})},fail:function(d){a.fail({errCode:"0",errMsg:d.errMsg||""})}})};
+AMapWX.prototype.getRegeo=function(a){function b(d){var e=c.requestConfig;wx.request({url:"https://restapi.amap.com/v3/geocode/regeo",data:{key:c.key,location:d,extensions:"all",s:e.s,platform:e.platform,appname:c.key,sdkversion:e.sdkversion,logversion:e.logversion},method:"GET",header:{"content-type":"application/json"},success:function(g){if(g.data.status&&"1"==g.data.status){g=g.data.regeocode;var h=g.addressComponent,f=[],k=g.roads[0].name+"\u9644\u8fd1",m=d.split(",")[0],n=d.split(",")[1];if(g.pois&&
+g.pois[0]){k=g.pois[0].name+"\u9644\u8fd1";var l=g.pois[0].location;l&&(m=parseFloat(l.split(",")[0]),n=parseFloat(l.split(",")[1]))}h.provice&&f.push(h.provice);h.city&&f.push(h.city);h.district&&f.push(h.district);h.streetNumber&&h.streetNumber.street&&h.streetNumber.number?(f.push(h.streetNumber.street),f.push(h.streetNumber.number)):f.push(g.roads[0].name);f=f.join("");a.success([{iconPath:a.iconPath,width:a.iconWidth,height:a.iconHeight,name:f,desc:k,longitude:m,latitude:n,id:0,regeocodeData:g}])}else a.fail({errCode:g.data.infocode,
+errMsg:g.data.info})},fail:function(g){a.fail({errCode:"0",errMsg:g.errMsg||""})}})}var c=this;a.location?b(a.location):c.getWxLocation(a,function(d){b(d)})};
+AMapWX.prototype.getWeather=function(a){function b(g){var h="base";a.type&&"forecast"==a.type&&(h="all");wx.request({url:"https://restapi.amap.com/v3/weather/weatherInfo",data:{key:d.key,city:g,extensions:h,s:e.s,platform:e.platform,appname:d.key,sdkversion:e.sdkversion,logversion:e.logversion},method:"GET",header:{"content-type":"application/json"},success:function(f){if(f.data.status&&"1"==f.data.status)if(f.data.lives){if((f=f.data.lives)&&0<f.length){f=f[0];var k={city:{text:"\u57ce\u5e02",data:f.city},
+weather:{text:"\u5929\u6c14",data:f.weather},temperature:{text:"\u6e29\u5ea6",data:f.temperature},winddirection:{text:"\u98ce\u5411",data:f.winddirection+"\u98ce"},windpower:{text:"\u98ce\u529b",data:f.windpower+"\u7ea7"},humidity:{text:"\u6e7f\u5ea6",data:f.humidity+"%"}};k.liveData=f;a.success(k)}}else f.data.forecasts&&f.data.forecasts[0]&&a.success({forecast:f.data.forecasts[0]});else a.fail({errCode:f.data.infocode,errMsg:f.data.info})},fail:function(f){a.fail({errCode:"0",errMsg:f.errMsg||""})}})}
+function c(g){wx.request({url:"https://restapi.amap.com/v3/geocode/regeo",data:{key:d.key,location:g,extensions:"all",s:e.s,platform:e.platform,appname:d.key,sdkversion:e.sdkversion,logversion:e.logversion},method:"GET",header:{"content-type":"application/json"},success:function(h){if(h.data.status&&"1"==h.data.status){h=h.data.regeocode;if(h.addressComponent)var f=h.addressComponent.adcode;else h.aois&&0<h.aois.length&&(f=h.aois[0].adcode);b(f)}else a.fail({errCode:h.data.infocode,errMsg:h.data.info})},
+fail:function(h){a.fail({errCode:"0",errMsg:h.errMsg||""})}})}var d=this,e=d.requestConfig;a.city?b(a.city):d.getWxLocation(a,function(g){c(g)})};
+AMapWX.prototype.getPoiAround=function(a){function b(e){e={key:c.key,location:e,s:d.s,platform:d.platform,appname:c.key,sdkversion:d.sdkversion,logversion:d.logversion};a.querytypes&&(e.types=a.querytypes);a.querykeywords&&(e.keywords=a.querykeywords);wx.request({url:"https://restapi.amap.com/v3/place/around",data:e,method:"GET",header:{"content-type":"application/json"},success:function(g){if(g.data.status&&"1"==g.data.status){if((g=g.data)&&g.pois){for(var h=[],f=0;f<g.pois.length;f++){var k=0==
+f?a.iconPathSelected:a.iconPath;h.push({latitude:parseFloat(g.pois[f].location.split(",")[1]),longitude:parseFloat(g.pois[f].location.split(",")[0]),iconPath:k,width:22,height:32,id:f,name:g.pois[f].name,address:g.pois[f].address})}a.success({markers:h,poisData:g.pois})}}else a.fail({errCode:g.data.infocode,errMsg:g.data.info})},fail:function(g){a.fail({errCode:"0",errMsg:g.errMsg||""})}})}var c=this,d=c.requestConfig;a.location?b(a.location):c.getWxLocation(a,function(e){b(e)})};
+AMapWX.prototype.getStaticmap=function(a){function b(e){c.push("location="+e);a.zoom&&c.push("zoom="+a.zoom);a.size&&c.push("size="+a.size);a.scale&&c.push("scale="+a.scale);a.markers&&c.push("markers="+a.markers);a.labels&&c.push("labels="+a.labels);a.paths&&c.push("paths="+a.paths);a.traffic&&c.push("traffic="+a.traffic);e="https://restapi.amap.com/v3/staticmap?"+c.join("&");a.success({url:e})}var c=[];c.push("key="+this.key);var d=this.requestConfig;c.push("s="+d.s);c.push("platform="+d.platform);
+c.push("appname="+d.appname);c.push("sdkversion="+d.sdkversion);c.push("logversion="+d.logversion);a.location?b(a.location):this.getWxLocation(a,function(e){b(e)})};
+AMapWX.prototype.getInputtips=function(a){var b=Object.assign({},this.requestConfig);a.location&&(b.location=a.location);a.keywords&&(b.keywords=a.keywords);a.type&&(b.type=a.type);a.city&&(b.city=a.city);a.citylimit&&(b.citylimit=a.citylimit);wx.request({url:"https://restapi.amap.com/v3/assistant/inputtips",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.tips&&a.success({tips:c.data.tips})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||
+""})}})};
+AMapWX.prototype.getDrivingRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);a.strategy&&(b.strategy=a.strategy);a.waypoints&&(b.waypoints=a.waypoints);a.avoidpolygons&&(b.avoidpolygons=a.avoidpolygons);a.avoidroad&&(b.avoidroad=a.avoidroad);wx.request({url:"https://restapi.amap.com/v3/direction/driving",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&a.success({paths:c.data.route.paths,
+taxi_cost:c.data.route.taxi_cost||""})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})};
+AMapWX.prototype.getWalkingRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);wx.request({url:"https://restapi.amap.com/v3/direction/walking",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&a.success({paths:c.data.route.paths})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})};
+AMapWX.prototype.getTransitRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);a.strategy&&(b.strategy=a.strategy);a.city&&(b.city=a.city);a.cityd&&(b.cityd=a.cityd);wx.request({url:"https://restapi.amap.com/v3/direction/transit/integrated",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&(c=c.data.route,a.success({distance:c.distance||"",taxi_cost:c.taxi_cost||
+"",transits:c.transits}))},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})};
+AMapWX.prototype.getRidingRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);wx.request({url:"https://restapi.amap.com/v3/direction/riding",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&a.success({paths:c.data.route.paths})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})};module.exports.AMapWX=AMapWX;

+ 392 - 0
common/barcode.js

@@ -0,0 +1,392 @@
+var CHAR_TILDE = 126;
+var CODE_FNC1 = 102;
+
+var SET_STARTA = 103;
+var SET_STARTB = 104;
+var SET_STARTC = 105;
+var SET_SHIFT = 98;
+var SET_CODEA = 101;
+var SET_CODEB = 100;
+var SET_STOP = 106;
+
+
+var REPLACE_CODES = {
+	CHAR_TILDE: CODE_FNC1 //~ corresponds to FNC1 in GS1-128 standard
+}
+
+var CODESET = {
+	ANY: 1,
+	AB: 2,
+	A: 3,
+	B: 4,
+	C: 5
+};
+
+function getBytes(str) {
+	var bytes = [];
+	for (var i = 0; i < str.length; i++) {
+		bytes.push(str.charCodeAt(i));
+	}
+	return bytes;
+}
+
+exports.code128 = function(ctx, text, width, height) {
+
+	width = parseInt(width);
+
+	height = parseInt(height);
+
+	var codes = stringToCode128(text);
+
+	var g = new Graphics(ctx, width, height);
+
+	var barWeight = g.area.width / ((codes.length - 3) * 11 + 35);
+
+	var x = g.area.left;
+	var y = g.area.top;
+	for (var i = 0; i < codes.length; i++) {
+		var c = codes[i];
+		//two bars at a time: 1 black and 1 white
+		for (var bar = 0; bar < 8; bar += 2) {
+			var barW = PATTERNS[c][bar] * barWeight;
+			// var barH = height - y - this.border;
+			var barH = height - y;
+			var spcW = PATTERNS[c][bar + 1] * barWeight;
+
+			//no need to draw if 0 width
+			if (barW > 0) {
+				g.fillFgRect(x, y, barW, barH);
+			}
+
+			x += barW + spcW;
+		}
+	}
+
+	ctx.draw();
+}
+
+
+function stringToCode128(text) {
+
+	var barc = {
+		currcs: CODESET.C
+	};
+
+	var bytes = getBytes(text);
+	//decide starting codeset
+	var index = bytes[0] == CHAR_TILDE ? 1 : 0;
+
+	var csa1 = bytes.length > 0 ? codeSetAllowedFor(bytes[index++]) : CODESET.AB;
+	var csa2 = bytes.length > 0 ? codeSetAllowedFor(bytes[index++]) : CODESET.AB;
+	barc.currcs = getBestStartSet(csa1, csa2);
+	barc.currcs = perhapsCodeC(bytes, barc.currcs);
+
+	//if no codeset changes this will end up with bytes.length+3
+	//start, checksum and stop
+	var codes = new Array();
+
+	switch (barc.currcs) {
+		case CODESET.A:
+			codes.push(SET_STARTA);
+			break;
+		case CODESET.B:
+			codes.push(SET_STARTB);
+			break;
+		default:
+			codes.push(SET_STARTC);
+			break;
+	}
+
+
+	for (var i = 0; i < bytes.length; i++) {
+		var b1 = bytes[i]; //get the first of a pair
+		//should we translate/replace
+		if (b1 in REPLACE_CODES) {
+			codes.push(REPLACE_CODES[b1]);
+			i++ //jump to next
+			b1 = bytes[i];
+		}
+
+		//get the next in the pair if possible
+		var b2 = bytes.length > (i + 1) ? bytes[i + 1] : -1;
+
+		codes = codes.concat(codesForChar(b1, b2, barc.currcs));
+		//code C takes 2 chars each time
+		if (barc.currcs == CODESET.C) i++;
+	}
+
+	//calculate checksum according to Code 128 standards
+	var checksum = codes[0];
+	for (var weight = 1; weight < codes.length; weight++) {
+		checksum += (weight * codes[weight]);
+	}
+	codes.push(checksum % 103);
+
+	codes.push(SET_STOP);
+
+	//encoding should now be complete
+	return codes;
+
+	function getBestStartSet(csa1, csa2) {
+		//tries to figure out the best codeset
+		//to start with to get the most compact code
+		var vote = 0;
+		vote += csa1 == CODESET.A ? 1 : 0;
+		vote += csa1 == CODESET.B ? -1 : 0;
+		vote += csa2 == CODESET.A ? 1 : 0;
+		vote += csa2 == CODESET.B ? -1 : 0;
+		//tie goes to B due to my own predudices
+		return vote > 0 ? CODESET.A : CODESET.B;
+	}
+
+	function perhapsCodeC(bytes, codeset) {
+		for (var i = 0; i < bytes.length; i++) {
+			var b = bytes[i]
+			if ((b < 48 || b > 57) && b != CHAR_TILDE)
+				return codeset;
+		}
+		return CODESET.C;
+	}
+
+	//chr1 is current byte
+	//chr2 is the next byte to process. looks ahead.
+	function codesForChar(chr1, chr2, currcs) {
+		var result = [];
+		var shifter = -1;
+
+		if (charCompatible(chr1, currcs)) {
+			if (currcs == CODESET.C) {
+				if (chr2 == -1) {
+					shifter = SET_CODEB;
+					currcs = CODESET.B;
+				} else if ((chr2 != -1) && !charCompatible(chr2, currcs)) {
+					//need to check ahead as well
+					if (charCompatible(chr2, CODESET.A)) {
+						shifter = SET_CODEA;
+						currcs = CODESET.A;
+					} else {
+						shifter = SET_CODEB;
+						currcs = CODESET.B;
+					}
+				}
+			}
+		} else {
+			//if there is a next char AND that next char is also not compatible
+			if ((chr2 != -1) && !charCompatible(chr2, currcs)) {
+				//need to switch code sets
+				switch (currcs) {
+					case CODESET.A:
+						shifter = SET_CODEB;
+						currcs = CODESET.B;
+						break;
+					case CODESET.B:
+						shifter = SET_CODEA;
+						currcs = CODESET.A;
+						break;
+				}
+			} else {
+				//no need to shift code sets, a temporary SHIFT will suffice
+				shifter = SET_SHIFT;
+			}
+		}
+
+		//ok some type of shift is nessecary
+		if (shifter != -1) {
+			result.push(shifter);
+			result.push(codeValue(chr2));
+		} else {
+			if (currcs == CODESET.C) {
+				//include next as well
+				result.push(codeValue(chr1, chr2));
+			} else {
+				result.push(codeValue(chr1));
+			}
+		}
+		barc.currcs = currcs;
+
+		return result;
+	}
+}
+
+//reduce the ascii code to fit into the Code128 char table
+function codeValue(chr1, chr2) {
+	if (typeof chr2 == "undefined") {
+		return chr1 >= 32 ? chr1 - 32 : chr1 + 64;
+	} else {
+		return parseInt(String.fromCharCode(chr1) + String.fromCharCode(chr2));
+	}
+}
+
+function charCompatible(chr, codeset) {
+	var csa = codeSetAllowedFor(chr);
+	if (csa == CODESET.ANY) return true;
+	//if we need to change from current
+	if (csa == CODESET.AB) return true;
+	if (csa == CODESET.A && codeset == CODESET.A) return true;
+	if (csa == CODESET.B && codeset == CODESET.B) return true;
+	return false;
+}
+
+function codeSetAllowedFor(chr) {
+	if (chr >= 48 && chr <= 57) {
+		//0-9
+		return CODESET.ANY;
+	} else if (chr >= 32 && chr <= 95) {
+		//0-9 A-Z
+		return CODESET.AB;
+	} else {
+		//if non printable
+		return chr < 32 ? CODESET.A : CODESET.B;
+	}
+}
+
+var Graphics = function(ctx, width, height) {
+
+	this.width = width;
+	this.height = height;
+	this.quiet = Math.round(this.width / 40);
+
+	this.border_size = 0;
+	this.padding_width = 0;
+
+	this.area = {
+		width: width - this.padding_width * 2 - this.quiet * 2,
+		height: height - this.border_size * 2,
+		top: this.border_size - 4,
+		left: this.padding_width + this.quiet
+	};
+
+	this.ctx = ctx;
+	this.fg = "#000000";
+	this.bg = "#ffffff";
+
+	// fill background
+	this.fillBgRect(0, 0, width, height);
+
+	// fill center to create border
+	this.fillBgRect(0, this.border_size, width, height - this.border_size * 2);
+}
+
+//use native color
+Graphics.prototype._fillRect = function(x, y, width, height, color) {
+	this.ctx.setFillStyle(color)
+	this.ctx.fillRect(x, y, width, height)
+}
+
+Graphics.prototype.fillFgRect = function(x, y, width, height) {
+	this._fillRect(x, y, width, height, this.fg);
+}
+
+Graphics.prototype.fillBgRect = function(x, y, width, height) {
+	this._fillRect(x, y, width, height, this.bg);
+}
+
+var PATTERNS = [
+	[2, 1, 2, 2, 2, 2, 0, 0], // 0
+	[2, 2, 2, 1, 2, 2, 0, 0], // 1
+	[2, 2, 2, 2, 2, 1, 0, 0], // 2
+	[1, 2, 1, 2, 2, 3, 0, 0], // 3
+	[1, 2, 1, 3, 2, 2, 0, 0], // 4
+	[1, 3, 1, 2, 2, 2, 0, 0], // 5
+	[1, 2, 2, 2, 1, 3, 0, 0], // 6
+	[1, 2, 2, 3, 1, 2, 0, 0], // 7
+	[1, 3, 2, 2, 1, 2, 0, 0], // 8
+	[2, 2, 1, 2, 1, 3, 0, 0], // 9
+	[2, 2, 1, 3, 1, 2, 0, 0], // 10
+	[2, 3, 1, 2, 1, 2, 0, 0], // 11
+	[1, 1, 2, 2, 3, 2, 0, 0], // 12
+	[1, 2, 2, 1, 3, 2, 0, 0], // 13
+	[1, 2, 2, 2, 3, 1, 0, 0], // 14
+	[1, 1, 3, 2, 2, 2, 0, 0], // 15
+	[1, 2, 3, 1, 2, 2, 0, 0], // 16
+	[1, 2, 3, 2, 2, 1, 0, 0], // 17
+	[2, 2, 3, 2, 1, 1, 0, 0], // 18
+	[2, 2, 1, 1, 3, 2, 0, 0], // 19
+	[2, 2, 1, 2, 3, 1, 0, 0], // 20
+	[2, 1, 3, 2, 1, 2, 0, 0], // 21
+	[2, 2, 3, 1, 1, 2, 0, 0], // 22
+	[3, 1, 2, 1, 3, 1, 0, 0], // 23
+	[3, 1, 1, 2, 2, 2, 0, 0], // 24
+	[3, 2, 1, 1, 2, 2, 0, 0], // 25
+	[3, 2, 1, 2, 2, 1, 0, 0], // 26
+	[3, 1, 2, 2, 1, 2, 0, 0], // 27
+	[3, 2, 2, 1, 1, 2, 0, 0], // 28
+	[3, 2, 2, 2, 1, 1, 0, 0], // 29
+	[2, 1, 2, 1, 2, 3, 0, 0], // 30
+	[2, 1, 2, 3, 2, 1, 0, 0], // 31
+	[2, 3, 2, 1, 2, 1, 0, 0], // 32
+	[1, 1, 1, 3, 2, 3, 0, 0], // 33
+	[1, 3, 1, 1, 2, 3, 0, 0], // 34
+	[1, 3, 1, 3, 2, 1, 0, 0], // 35
+	[1, 1, 2, 3, 1, 3, 0, 0], // 36
+	[1, 3, 2, 1, 1, 3, 0, 0], // 37
+	[1, 3, 2, 3, 1, 1, 0, 0], // 38
+	[2, 1, 1, 3, 1, 3, 0, 0], // 39
+	[2, 3, 1, 1, 1, 3, 0, 0], // 40
+	[2, 3, 1, 3, 1, 1, 0, 0], // 41
+	[1, 1, 2, 1, 3, 3, 0, 0], // 42
+	[1, 1, 2, 3, 3, 1, 0, 0], // 43
+	[1, 3, 2, 1, 3, 1, 0, 0], // 44
+	[1, 1, 3, 1, 2, 3, 0, 0], // 45
+	[1, 1, 3, 3, 2, 1, 0, 0], // 46
+	[1, 3, 3, 1, 2, 1, 0, 0], // 47
+	[3, 1, 3, 1, 2, 1, 0, 0], // 48
+	[2, 1, 1, 3, 3, 1, 0, 0], // 49
+	[2, 3, 1, 1, 3, 1, 0, 0], // 50
+	[2, 1, 3, 1, 1, 3, 0, 0], // 51
+	[2, 1, 3, 3, 1, 1, 0, 0], // 52
+	[2, 1, 3, 1, 3, 1, 0, 0], // 53
+	[3, 1, 1, 1, 2, 3, 0, 0], // 54
+	[3, 1, 1, 3, 2, 1, 0, 0], // 55
+	[3, 3, 1, 1, 2, 1, 0, 0], // 56
+	[3, 1, 2, 1, 1, 3, 0, 0], // 57
+	[3, 1, 2, 3, 1, 1, 0, 0], // 58
+	[3, 3, 2, 1, 1, 1, 0, 0], // 59
+	[3, 1, 4, 1, 1, 1, 0, 0], // 60
+	[2, 2, 1, 4, 1, 1, 0, 0], // 61
+	[4, 3, 1, 1, 1, 1, 0, 0], // 62
+	[1, 1, 1, 2, 2, 4, 0, 0], // 63
+	[1, 1, 1, 4, 2, 2, 0, 0], // 64
+	[1, 2, 1, 1, 2, 4, 0, 0], // 65
+	[1, 2, 1, 4, 2, 1, 0, 0], // 66
+	[1, 4, 1, 1, 2, 2, 0, 0], // 67
+	[1, 4, 1, 2, 2, 1, 0, 0], // 68
+	[1, 1, 2, 2, 1, 4, 0, 0], // 69
+	[1, 1, 2, 4, 1, 2, 0, 0], // 70
+	[1, 2, 2, 1, 1, 4, 0, 0], // 71
+	[1, 2, 2, 4, 1, 1, 0, 0], // 72
+	[1, 4, 2, 1, 1, 2, 0, 0], // 73
+	[1, 4, 2, 2, 1, 1, 0, 0], // 74
+	[2, 4, 1, 2, 1, 1, 0, 0], // 75
+	[2, 2, 1, 1, 1, 4, 0, 0], // 76
+	[4, 1, 3, 1, 1, 1, 0, 0], // 77
+	[2, 4, 1, 1, 1, 2, 0, 0], // 78
+	[1, 3, 4, 1, 1, 1, 0, 0], // 79
+	[1, 1, 1, 2, 4, 2, 0, 0], // 80
+	[1, 2, 1, 1, 4, 2, 0, 0], // 81
+	[1, 2, 1, 2, 4, 1, 0, 0], // 82
+	[1, 1, 4, 2, 1, 2, 0, 0], // 83
+	[1, 2, 4, 1, 1, 2, 0, 0], // 84
+	[1, 2, 4, 2, 1, 1, 0, 0], // 85
+	[4, 1, 1, 2, 1, 2, 0, 0], // 86
+	[4, 2, 1, 1, 1, 2, 0, 0], // 87
+	[4, 2, 1, 2, 1, 1, 0, 0], // 88
+	[2, 1, 2, 1, 4, 1, 0, 0], // 89
+	[2, 1, 4, 1, 2, 1, 0, 0], // 90
+	[4, 1, 2, 1, 2, 1, 0, 0], // 91
+	[1, 1, 1, 1, 4, 3, 0, 0], // 92
+	[1, 1, 1, 3, 4, 1, 0, 0], // 93
+	[1, 3, 1, 1, 4, 1, 0, 0], // 94
+	[1, 1, 4, 1, 1, 3, 0, 0], // 95
+	[1, 1, 4, 3, 1, 1, 0, 0], // 96
+	[4, 1, 1, 1, 1, 3, 0, 0], // 97
+	[4, 1, 1, 3, 1, 1, 0, 0], // 98
+	[1, 1, 3, 1, 4, 1, 0, 0], // 99
+	[1, 1, 4, 1, 3, 1, 0, 0], // 100
+	[3, 1, 1, 1, 4, 1, 0, 0], // 101
+	[4, 1, 1, 1, 3, 1, 0, 0], // 102
+	[2, 1, 1, 4, 1, 2, 0, 0], // 103
+	[2, 1, 1, 2, 1, 4, 0, 0], // 104
+	[2, 1, 1, 2, 3, 2, 0, 0], // 105
+	[2, 3, 3, 1, 1, 1, 2, 0] // 106
+]

+ 92 - 0
common/downApp.js

@@ -0,0 +1,92 @@
+export function checkUpdateApp() {
+	plus.runtime.getProperty(plus.runtime.appid, function(widgetinfo) {
+		if (widgetinfo.name == '冷链物流') {
+			uni.request({
+				url: 'https://www.pgyer.com/apiv2/app/check',
+				data: {
+					_api_key: '202eadee73f2051186b26106cc8bfed1',
+					appKey: '970036fad7e2118d83dcdc5ac36eabc5'
+				},
+				success: check => {
+					if (check.statusCode == 200 && check.data.code == 0 && check.data.data) {
+						let data = check.data.data;
+						let version = widgetinfo.version,
+							appVersion = data.buildVersion,
+							appName = widgetinfo.name,
+							appurl = data.downloadURL,
+							intro = '发现新的《冷链物流》APP,需要更新APP';
+
+						if (data && version < appVersion) {
+							uni.showModal({
+								title: '更新提示',
+								confirmText: '立即升级',
+								content: intro,
+								success: res => {
+									if (res.confirm) {
+										uni.showLoading({
+											title: '正在更新',
+											mask: true
+										});
+										uni.downloadFile({
+											url: appurl,
+											success: download => {
+												if (download.statusCode ==
+													200) {
+													plus.runtime.install(
+														download
+														.tempFilePath, {
+															force: false
+														},
+														() => {
+															uni
+														.hideLoading();
+															plus.runtime
+																.restart(); //更新成功启动
+														},
+														err => {
+															uni
+														.hideLoading();
+															uni.showToast({
+																title: '更新失败,将跳转下载页面',
+																icon: 'none',
+																duration: 2000
+															});
+														}
+													);
+													setTimeout(function() {
+														plus.runtime
+															.openURL(
+																appurl
+																);
+													}, 2000);
+												}
+											}
+										});
+									} else if (res.cancel) {
+										console.log('用户点击取消');
+										uni.showToast({
+											title: '低版本无法继续使用,请先升级',
+											icon: 'none',
+											duration: 2000
+										});
+										//退出app
+										setTimeout(function() {
+											plus.runtime.quit();
+										}, 2000);
+									}
+								},
+								fail: () => {
+									uni.hideLoading();
+								}
+							});
+						} else {
+							console.log('暂无新版本=====')
+						}
+					}
+				}
+			});
+		} else {
+			//非安卓
+		}
+	});
+}

+ 112 - 0
common/index.scss

@@ -0,0 +1,112 @@
+// 颜色
+$color-blue:#3387FF;
+$color-white:#FFFFFF;
+// 字体大小
+$font_size_34:34rpx;
+$font_size_36:36rpx;
+
+page{
+	background-color: #fff;
+}
+.but_button690{
+	margin: 10rpx;
+	width: 690rpx;
+	height: 88rpx;
+	font-size: $font_size_34;
+	font-weight: normal;
+}
+.but_button630{
+	margin: 10rpx;
+	width: 630rpx;
+	height: 88rpx;
+	font-size: $font_size_34;
+	font-weight: normal;
+}
+
+.center_in_sequence{
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+}
+.center_in{
+	width: 100%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+.space_between{
+	width: 100%;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+}
+.safeDistance{
+	padding-top: constant(safe-area-inset-top);
+	padding-top: env(safe-area-inset-top);
+}
+.up_and_down{
+	display: flex;
+	flex-direction: column;
+	justify-content: space-between;
+	align-items: center;
+}
+.font36{
+	font-size: $font_size_36;
+	font-weight: bold;
+	text-align: left;
+	color: #333;
+}
+
+	.btn_print {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		background-color: #fff;
+		padding: 20rpx;
+		box-shadow: 0rpx 0rpx 20rpx 0rpx rgba(0, 0, 0, 0.1);
+	}
+.wx_card {
+		position: fixed;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		bottom: 50rpx;
+		left: 0rpx;
+		right: 0rpx;
+		margin-bottom: 50rpx;
+	}
+
+	.card_btn_wx {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		width: 70rpx;
+		height: 70rpx;
+		border-radius: 50%;
+	}
+
+	// 伪元素1rpx边框
+	.frame {
+		position: relative; //重要
+	}
+
+	.frame::after {
+		position: absolute;
+		content: '';
+		border: 2rpx solid #b4b3b1;
+		border-radius: 50%;
+		width: 200%;
+		height: 200%;
+		top: 0;
+		left: 0;
+		transform: scale(0.5);
+		transform-origin: 0 0;
+		pointer-events: none;
+		/* 使伪元素不会阻止鼠标事件 */
+	}
+
+	.title_login_wx {
+		font-size: 26rpx;
+		margin-top: 12rpx;
+	}

+ 22 - 0
components/x-barCode.vue

@@ -0,0 +1,22 @@
+<template>
+	<view>
+		<canvas class="bar_code" canvas-id="Brcode"></canvas>
+	</view>
+</template>
+
+<script>
+	import brCode from "../common/barcode.js"
+	export default {
+		data() {
+			return {}
+		},
+		mounted() {
+			brCode.code128(uni.createCanvasContext('Brcode'), '23627747723', 300, 90);
+		},
+		methods: {}
+	}
+</script>
+
+<style lang="scss">
+	.bar_code {}
+</style>

+ 67 - 0
components/x-bluetooth.vue

@@ -0,0 +1,67 @@
+<template>
+	<view>
+		<view class="card_item_tooth" v-for="(item,index) in list" :key="index">
+			<view class="card_wifi">
+				<u-icon name="wifi" color="#2979ff" size="26"></u-icon>
+				<view class="wifi_item_title">{{item.name}}</view>
+			</view>
+			<view>
+				<u-button size="small" :type="btnType" :text="text" @click="connect(item)"></u-button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'x-bluetooth',
+		props: {
+			list: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			text: {
+				type: String,
+				default () {
+					return '连接'
+				}
+			},
+			btnType: {
+				type: String,
+				default () {
+					return 'primary'
+				}
+			},
+		},
+		data() {
+			return {}
+		},
+		mounted() {},
+		methods: {
+			connect(value) {
+				this.$emit('connect', value)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.card_item_tooth {
+		padding: 20rpx 0rpx;
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+	}
+
+	.card_wifi {
+		display: flex;
+		align-items: center;
+	}
+
+	.wifi_item_title {
+		font-size: 28rpx;
+		margin-left: 16rpx;
+	}
+</style>

+ 66 - 0
components/x-checkbox.vue

@@ -0,0 +1,66 @@
+<template>
+	<view class="checkbox-container">
+		<view class="checkbox" :class="{'checkbox-checked': isChecked}">
+			<!-- 复选框内部可以放一个小图标或者其他装饰 -->
+			<text v-if="isChecked" class="checkbox-icon">
+				<u-icon name="checkbox-mark" color="#fff"></u-icon>
+			</text>
+		</view>
+		<text class="check_title">{{ label }}</text>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			label: String, // 复选框旁边的文本
+			isChecked: Boolean, // 复选框旁边的文本
+		},
+		data() {
+			return {};
+		},
+		methods: {
+			toggleCheck() {
+				// this.isChecked = !this.isChecked;
+				// 触发自定义事件,可以在父组件中监听该事件
+				// this.$emit('change', this.isChecked);
+			},
+		},
+	};
+</script>
+
+<style scoped>
+	.checkbox-container {
+		display: flex;
+		align-items: center;
+	}
+
+	.checkbox {
+		width: 35rpx;
+		height: 35rpx;
+		border: 1rpx solid #ccc;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin-right: 10rpx;
+		border-radius: 8rpx;
+		user-select: none;
+		cursor: pointer;
+	}
+
+	.check_title {
+		margin-left: 5rpx;
+		font-size: 32rpx;
+		font-weight: 600;
+	}
+
+	.checkbox-checked {
+		border-color: #2979ff;
+		background-color: #2979ff;
+	}
+
+	.checkbox-icon {
+		color: #fff;
+		font-size: 24rpx;
+	}
+</style>

+ 201 - 0
components/x-form.vue

@@ -0,0 +1,201 @@
+<template>
+	<!-- 表单 -->
+	<view style="margin: 0rpx 20rpx 0rpx 30rpx;">
+		<u-form :model="model" :rules="rules" labelPosition="left" ref="uForm" labelWidth="70">
+			<view v-for="(item,index) in list" :key="index">
+				<u-form-item :required="item.required ? true : false" :label="item.label" :prop="item.field"
+					v-if="item.type == 'input' && customerName(item)">
+					<view class="card_form_item">
+						<u-input v-model="model[`${item.field}`]" :disabled="item.disabled ? true : false"
+							:placeholder="item.placeholder"></u-input>
+					</view>
+				</u-form-item>
+				<u-form-item :required="item.required ? true : false" :label="item.label" :prop="item.field"
+					v-else-if="item.type == 'select'">
+					<view class="card_form_item">
+						<u-input :placeholder="item.placeholder" suffixIcon="arrow-down"
+							v-model="model[`${item.field}`]" @focus="change(item)"></u-input>
+					</view>
+				</u-form-item>
+				<u-form-item :required="item.required ? true : false" :label="item.label" :prop="item.field"
+					v-else-if="item.type == 'textarea'">
+					<view class="card_form_item">
+						<u-textarea v-model="model[`${item.field}`]" autoHeight
+							:placeholder="item.placeholder"></u-textarea>
+					</view>
+				</u-form-item>
+				<u-form-item :required="item.required ? true : false" :label="item.label" :prop="item.field"
+					v-else-if="item.type == 'upload' && !item.visible">
+					<view class="card_form_item">
+						<u-upload :fileList="fileList1" name="1" multiple :maxCount="10" @afterRead="afterRead"
+							@delete="deletePic"></u-upload>
+					</view>
+				</u-form-item>
+			</view>
+		</u-form>
+		<u-picker :defaultIndex="findIndex(model[fieldType], columns[0])" :show="show" :columns="columns"
+			keyName="label" @confirm="confirm($event, fieldType)" @cancel="cancel"></u-picker>
+	</view>
+</template>
+
+<script>
+	const ENV = require('@/.env.js')
+	export default {
+		name: 'x-form',
+		props: {
+			list: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			model: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			rules: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+		},
+		data() {
+			return {
+				show: false,
+				columns: [],
+				fieldType: '',
+				fileList1: [],
+			}
+		},
+		// 必须要在onReady生命周期,因为onLoad生命周期组件可能尚未创建完毕
+		onReady() {
+			this.$refs.uForm.setRules(this.rules);
+		},
+		methods: {
+			customerName(value) {
+				var userInfo = this.$cache.getCache('userInfo')
+				if (userInfo.userType == 'customer') {
+					if (value.field != 'customerName') {
+						if (value.type == 'input') {
+							return true
+						}
+					}
+				} else {
+					if (value.type == 'input') {
+						return true
+					}
+				}
+			},
+			change(e) {
+				this.columns = []
+				this.fieldType = e.field
+				this.show = true
+				this.$nextTick(() => {
+					this.columns.push(e.options)
+				})
+			},
+			// 选择确定
+			confirm(value, type) {
+				this.show = false
+				this.model[type] = value.value[0].label
+			},
+			// 点击确定
+			cancel() {
+				this.show = false
+			},
+			findIndex(code, list) {
+				if (!code || !list) {
+					return [0]
+				}
+				return [list.findIndex((item) => (item.label === code || item.label === code))]
+			},
+
+			// 新增图片
+			async afterRead(event) {
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				uni.showLoading({
+					title: '上传中',
+					mask: true,
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+				var arr = []
+				this.fileList1.forEach(item1 => {
+					arr.push(item1.url)
+				})
+				uni.hideLoading();
+				this.model.tamperProofLabelImg = arr.join()
+				this.$refs.uForm.validateField('tamperProofLabelImg')
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+				var arr = []
+				this.fileList1.forEach(item1 => {
+					arr.push(item1.url)
+				})
+				uni.hideLoading();
+				this.model.tamperProofLabelImg = arr.join()
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: ENV.APP_DEV_URL + '/api/upload', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						// formData: {
+						// 	user: 'test'
+						// },
+						header: {
+							'Authorization': 'Bearer ' + uni.getStorageSync('access_token'),
+						},
+						success: (res) => {
+							let state = JSON.parse(res.data)
+							setTimeout(() => {
+								if (state.code == 200) {
+									resolve(state.data)
+								}
+							}, 100)
+						}
+					});
+				})
+			},
+			//子组件校验,传递到父组件
+			async validateForm() {
+				let flag = null
+				await this.$refs.uForm.validate(valid => {}).then(res => {
+					flag = true
+				}).catch(err => {
+					flag = false
+				})
+				return flag
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.card_form_item {
+		width: 100%;
+	}
+</style>

+ 151 - 0
components/x-humiture.vue

@@ -0,0 +1,151 @@
+<template>
+	<view style="width: 100%;">
+		<view class="card_humiture border_humiture" v-if="humitureData.length > 0">
+			<view class="headline_item w_wsd">温度(°C)</view>
+			<view class="headline_item w_wsd">湿度(Rh)</view>
+			<view class="headline_item w_time">时间</view>
+		</view>
+		<view class="card_record" v-if="humitureData.length > 0">
+			<scroll-view class="scroll-view" scroll-y="true" @scrolltolower="loadMore">
+				<view class="card_humiture" v-for="(item,index) in humitureData" :key="index">
+					<view class="title_item w_wsd">{{item.T_t || ''}}</view>
+					<view class="title_item w_wsd">{{item.T_rh || ''}}</view>
+					<view class="title_item w_time">{{item.T_time || ''}}</view>
+				</view>
+			</scroll-view>
+		</view>
+		<view v-else style="padding: 30rpx 0rpx;">
+			<u-empty mode="data" text="暂无温湿度记录"></u-empty>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'Xhumiture',
+		props: {
+			probeList: {
+				type: Array,
+				default: () => [],
+			},
+			waybillNo: {
+				type: String,
+				default: () => '',
+			},
+			taskId: {
+				type: Number,
+				default: () => null,
+			},
+		},
+		data() {
+			return {
+				current: 0,
+				loadingMore: true,
+				pageSize: 10,
+				currentPage: 1,
+				tIds: [],
+				humitureData: [],
+				loading: ''
+			}
+		},
+		mounted() {
+			// this.getProbe()
+		},
+		methods: {
+			getlist(num) {
+				this.humitureData = []
+				this.currentPage = 1
+				this.getList(num)
+			},
+			getList(tIds) {
+				this.loadingMore = true;
+				this.tIds = []
+				this.tIds.push(tIds)
+
+				function methods1(arr) {
+					return Array.from(new Set(arr));
+				}
+				this.tIds = methods1(this.tIds)
+				let params = {
+					t_ids: this.tIds,
+					taskId: this.taskId,
+					waybillNo: this.waybillNo,
+					page: this.currentPage,
+					pageSize: this.pageSize,
+				}
+				this.$api.post('/api/waybill-task/data', params).then(res => {
+					if (res.code == 200) {
+						const data = res.data.list
+						if (this.loadingMore == true && data) {
+							this.humitureData = this.humitureData.concat(data);
+						}
+						if (data.length < this.pageSize) {
+							this.loadingMore = false
+							this.loading = '没有更多了'
+						} else {
+							this.loadingMore = true
+							this.currentPage++
+						}
+					}
+				})
+			},
+			sectionChange1(value) {
+				this.current = value
+				this.currentPage = 1
+				this.humitureData = []
+				const num = this.probeList[value].T_id
+				this.getList(num)
+			},
+			// 滚动加载更多
+			loadMore() {
+				this.getList(this.tIds[0]);
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.sub_card {
+		margin: 0rpx 20rpx 20rpx 20rpx;
+	}
+
+	.card_humiture {
+		width: 100%;
+		display: flex;
+		align-items: center;
+		padding: 20rpx 0rpx;
+		margin-bottom: 10rpx;
+	}
+
+	.border_humiture {
+		border-bottom: 1rpx solid #EBEEF5;
+	}
+
+	.headline_item {
+		font-size: 30rpx;
+		font-weight: 600;
+	}
+
+	.title_item {
+		font-size: 28rpx
+	}
+
+	.w_wsd {
+		text-align: center;
+		width: 27%;
+	}
+
+	.w_time {
+		text-align: center;
+		width: 46%;
+	}
+
+	.card_record {
+		margin-bottom: 20rpx;
+	}
+
+	.scroll-view {
+		max-height: 500rpx;
+		overflow: hidden;
+	}
+</style>

+ 97 - 0
components/x-navbottom.vue

@@ -0,0 +1,97 @@
+<template>
+	<!-- 底部tabbar -->
+	<view class="navmainbox">
+		<view class="navigation">
+			<view class="navcont" v-for="(item,index) in list" :key="index" @click="gonavs(item,index)">
+				<span class="iconfont icon_image" :class="[item.icon,nowchos == index ? 'activeName' :'']"></span>
+				<view class="navchosbox" :class="nowchos == index ? 'activeName' :''">
+					{{item.title}}
+				</view>
+			</view>
+		</view>
+		<!-- //防止 固定的底部菜单遮挡页面上的内容 -->
+		<view class="navmainbox" style="position: relative;opacity: 0;z-index: 0;">
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'x-navbottom',
+		props: {
+			nowchos: {
+				type: Number,
+				default () {
+					return 0
+				}
+			},
+		},
+		data() {
+			return {
+				dataList: [],
+				list: [{
+						icon: 'icon-shouye',
+						title: '首页',
+					},
+					{
+						icon: 'icon-wode',
+						title: '我的',
+					}
+				],
+			}
+		},
+		mounted() {},
+		methods: {
+			gonavs(e, cindex) {
+				this.$emit('botomchos', cindex)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.navmainbox {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		z-index: 5;
+		background-color: #ffffff;
+		box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
+		width: 100%;
+		height: 100rpx;
+		padding-bottom: constant(safe-area-inset-bottom); //兼容 IOS<11.2
+		padding-bottom: env(safe-area-inset-bottom); //兼容 IOS>11.2
+	}
+
+	.navigation {
+		z-index: 2024;
+		position: relative;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		height: 100rpx;
+		padding-bottom: var(--window-bottom);
+	}
+
+	.navigation .navcont {
+		position: relative;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		flex: 1;
+	}
+
+	.navchosbox {
+		color: #727289 !important;
+		margin-top: 2rpx;
+		font-size: 26rpx;
+	}
+
+	.activeName {
+		color: #3E80FF !important;
+	}
+
+	.icon_image {
+		font-size: 44rpx;
+	}
+</style>

+ 45 - 0
components/x-orderCard.vue

@@ -0,0 +1,45 @@
+<template>
+	<!-- 订单详情表单 -->
+	<view class="card_ordercard">
+		<view class="title_detail"><span>姓名:</span>{{list.name}}</view>
+		<view class="title_detail"><span>联系方式:</span>{{list.phone}}</view>
+		<view class="title_detail"><span>地址:</span>{{list.address}}</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'x-orderCard',
+		props: {
+			list: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+		},
+		data() {
+			return {
+
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.card_ordercard {
+		border-radius: 20rpx;
+		padding: 20rpx;
+		background-color: #fff;
+	}
+
+	.title_detail {
+		margin: 10rpx 0rpx;
+
+		span {
+			font-size: 28rpx;
+			color: #909399;
+			margin-right: 10rpx;
+		}
+	}
+</style>

+ 328 - 0
components/x-orderManagement.vue

@@ -0,0 +1,328 @@
+<template>
+	<view style="width: 100%;">
+		<view class="card_ordermang" v-for="(item,index) in orderList" :key="index" @click="goOrderDetails(item)">
+			<view class="head_ordermang space_between">
+				<view class="waybill_title">运单号:<span>{{item.waybillNo}}</span></view>
+				<span class="card_state" :style="{color:getState(item.status)}">{{orderStatus(item.status)}}</span>
+			</view>
+			<view class="title_ordermang"><span>收货地址:</span>{{item.consigneeAddressDetails}}</view>
+			<view class="title_ordermang"><span>收货电话:</span>{{item.consigneeAddressPhone}}</view>
+			<view class="title_ordermang"><span>下单时间:</span>{{item.orderTime}}</view>
+			<view style="display: flex;" v-if="item.status == 1">
+				<view class="btn_printil markd10" v-if="item.status == 1 || item.status == 2 || item.status == 3"
+					@click.stop="modifyOrder(item)">
+					<u-button size="small" type="warning" text="修改订单"></u-button>
+				</view>
+				<!-- <view class="btn_printil markd10" @click.stop="printBarCode(item)">
+					<u-button size="small" type="primary" text="打印条码"></u-button>
+				</view> -->
+				<view class="btn_printil markd10" @click.stop="sendOrders(item)">
+					<u-button size="small" type="success" text="派单"></u-button>
+				</view>
+			</view>
+			<view style="display: flex;" v-if="item.status == 2">
+				<view class="btn_printil markd10" @click.stop="rejection(item)">
+					<u-button size="small" type="error" text="拒收"></u-button>
+				</view>
+				<view class="btn_printil markd10" @click.stop="signFor(item)">
+					<u-button size="small" type="primary" text="签收"></u-button>
+				</view>
+			</view>
+		</view>
+		<u-popup :show="signForShow" mode="center" round="6" closeable :closeOnClickOverlay="false" @close="close">
+			<view class="center_in title_sign">{{signRejection}}</view>
+			<view class="card_sign_for">
+				<view class="img_sign_rejection">订单号: {{orderNumber}}</view>
+				<view class="card_image_sign">
+					<view class="img_sign_rejection">{{signRejection}}图片</view>
+					<u-upload :fileList="fileList1" name="1" multiple :maxCount="10" @afterRead="afterRead"
+						@delete="deletePic"></u-upload>
+				</view>
+				<view class="rejection_card" v-if="rejectionFlag">
+					<view class="img_sign_rejection">拒收原因</view>
+					<u-textarea v-model="rejectionValue" placeholder="请输入拒收原因" autoHeight></u-textarea>
+				</view>
+				<u-button size="small" type="primary" text="确定" @click="confirm()"></u-button>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	const ENV = require('@/.env.js')
+	export default {
+		name: 'xOrderManagement',
+		props: {
+			// 订单列表
+			orderList: {
+				type: Array,
+				default: () => [],
+			},
+			userInfo: {
+				type: Object,
+				default: () => {},
+			}
+		},
+		data() {
+			return {
+				signForShow: false,
+				fileList1: [],
+				rejectionValue: '',
+				rejectionFlag: false,
+				signRejection: '',
+				orderNumber: '',
+				imageUrl: '',
+			}
+		},
+		mounted() {
+			// console.log(this.userInfo, 26)
+		},
+		methods: {
+			// 打印条码
+			printBarCode(value) {
+				this.$cache.setCache('commodity', JSON.stringify(value))
+				uni.navigateTo({
+					url: '/pages/order/quantum?waybillNo=' + value.waybillNo + '&printType=barCode'
+				});
+			},
+			// 派单
+			sendOrders(value) {
+				this.$emit('sendOrders', value)
+			},
+			// 拒收
+			rejection(value) {
+				this.orderNumber = value.waybillNo
+				this.signForShow = true
+				this.rejectionFlag = true
+				this.signRejection = '拒收'
+			},
+			// 签收
+			signFor(value) {
+				this.orderNumber = value.waybillNo
+				this.signForShow = true
+				this.rejectionFlag = false
+				this.signRejection = '签收'
+			},
+			// 确定签收/拒收
+			confirm() {
+				let params = {
+					waybillNo: this.orderNumber,
+					receiptImg: this.imageUrl,
+					status: 3,
+					rejectionReason: this.rejectionValue,
+				}
+				if (this.rejectionFlag) {
+					params.status = 4
+				} else {
+					params.status = 3
+				}
+				this.$api.post('/api/waybill/receipt', params).then(res => {
+					console.log(res, 266)
+					if (res.code == 200) {
+						uni.$u.toast(res.msg)
+						this.signForShow = false
+						this.$emit('operateSuccessfully')
+					} else {
+						uni.$u.toast(res.msg)
+					}
+				})
+			},
+			// 打印温湿度记录
+			humidityRecording(value) {
+				this.$cache.setCache('commodity', JSON.stringify(value))
+				uni.navigateTo({
+					url: '/pages/order/quantum?waybillNo=' + value.waybillNo + '&printType=record'
+				});
+			},
+			// 修改订单
+			modifyOrder(value) {
+				this.$cache.setCache('orderDetails', value)
+				uni.redirectTo({
+					url: '/pages/order/addWaybill?title=修改订单&type=2'
+				});
+			},
+			// 订单详情
+			goOrderDetails(value) {
+				this.$cache.setCache('orderDetails', value)
+				uni.navigateTo({
+					url: '/pages/order/orderDetails?type=details'
+				});
+			},
+			// 订单状态
+			orderStatus(value) {
+				if (value == 1) {
+					return '已下单'
+				} else if (value == 2) {
+					return '配送中'
+				} else if (value == 3) {
+					return '已送达'
+				} else if (value == 4) {
+					return '已取消'
+				}
+			},
+			// 订单文字颜色
+			getState(value) {
+				if (value == 1) {
+					return '#9ddd54'
+				} else if (value == 2) {
+					return '#4bc7fc'
+				} else if (value == 3) {
+					return '#1cc723'
+				} else if (value == 4) {
+					return '#fe880e'
+				}
+			},
+			// 新增图片
+			async afterRead(event) {
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				uni.showLoading({
+					title: '上传中',
+					mask: true,
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+				var arr = []
+				this.fileList1.forEach(item1 => {
+					arr.push(item1.url)
+				})
+				uni.hideLoading();
+				this.imageUrl = arr.join()
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+				var arr = []
+				this.fileList1.forEach(item1 => {
+					arr.push(item1.url)
+				})
+				this.imageUrl = arr.join()
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: ENV.APP_DEV_URL + '/api/upload', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						header: {
+							'Authorization': 'Bearer ' + uni.getStorageSync('access_token'),
+						},
+						success: (res) => {
+							let state = JSON.parse(res.data)
+							setTimeout(() => {
+								if (state.code == 200) {
+									resolve(state.data)
+								}
+							}, 100)
+						}
+					});
+				})
+			},
+			close() {
+				console.log(22)
+				this.signForShow = false
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.card_ordermang {
+		background-color: #fff;
+		padding: 20rpx;
+		border-radius: 20rpx;
+		margin: 20rpx;
+	}
+
+	.head_ordermang {
+		margin-bottom: 10rpx;
+	}
+
+	.title_ordermang {
+		margin-top: 10rpx;
+		font-size: 26rpx;
+		color: #909399;
+
+		span {
+			font-size: 26rpx;
+			color: #909399;
+			margin-right: 10rpx;
+		}
+	}
+
+	.waybill_title {
+		font-size: 32rpx;
+
+		span {
+			margin-left: 10rpx;
+		}
+	}
+
+	.card_state {
+		color: #606266;
+		font-size: 32rpx;
+	}
+
+	.card_specification {
+		display: flex;
+		align-items: center;
+	}
+
+	.specification_title {
+		margin-right: 20rpx;
+	}
+
+	.btn_printil {
+		flex: 1;
+		margin-top: 10px;
+	}
+
+	.markd10:nth-child(1) {
+		margin: 10rpx 10rpx 0rpx 0rpx;
+	}
+
+	.markd10:nth-child(2) {
+		margin: 10rpx 0rpx 0rpx 10rpx;
+	}
+
+	.title_sign {
+		padding: 10rpx 0rpx;
+		height: 60rpx;
+	}
+
+	.card_sign_for {
+		width: 600rpx;
+		padding: 10rpx 20rpx 20rpx 20rpx;
+	}
+
+	.rejection_card {
+		margin-bottom: 20rpx;
+	}
+
+	.card_image_sign {
+		display: flex;
+		flex-direction: column;
+		margin-bottom: 10rpx;
+	}
+
+	.img_sign_rejection {
+		font-size: 30rpx;
+		margin-bottom: 10rpx;
+	}
+</style>

+ 34 - 0
components/x-radar.vue

@@ -0,0 +1,34 @@
+<template>
+	<!-- 雷达扫描 -->
+	<view>
+		<view class="radar1">
+			<view class="search" :class="BlueState?'startSearch':''"></view>
+			<view class="radar2">
+				<view class="radar3">
+					<view class="radar4">
+					</view>	
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props:['BlueState'],
+		data() {
+			return {
+
+			};
+		},
+		watch:{
+			BlueState(val){
+				// console.log('状态发生变化',val)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import '../static/css/styles.scss';
+</style>

+ 156 - 0
components/x-statistics.vue

@@ -0,0 +1,156 @@
+<template>
+	<!-- 首页统计 -->
+	<view class="home_card">
+		<view class="card_item_home" v-for="(item,index) in list" :key="index" @click="goOrder(index)">
+			<view class="home_head">
+				<view class="home_line" :style="{backgroundColor:item.color,}"></view>
+				<view class="home_item_title">{{item.value || 0}}</view>
+			</view>
+			<view class="item_subheading">{{item.title}}</view>
+			<view class="home_bottom_item">
+				<view class="home_right_icon center_in">
+					<u-icon name="arrow-right" size="15" color="#fff"></u-icon>
+				</view>
+				<span :style="{color: item.color}" class="iconfont home_icon_image" :class="item.icon"></span>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'XStatistics',
+		props: {
+			userType: {
+				type: Number,
+				default: 1,
+			},
+			orderStatistics: {
+				type: Object,
+				default: {},
+			},
+		},
+		data() {
+			return {
+				list: [],
+				list1: [{
+					sign: 'waitDeliveryNum',
+					id: 'detailsil',
+					title: '已下单',
+					icon: 'icon-cancel',
+					color: '#9ddd54',
+					value: null,
+				}, {
+					sign: 'inDeliveryNum',
+					id: 'truck',
+					title: '配送中',
+					icon: 'icon-delivery',
+					color: '#4bc7fc',
+					value: null,
+				}, {
+					sign: 'receiptNum',
+					id: 'signfor',
+					title: '已送达',
+					icon: 'icon-yisongda',
+					color: '#1cc723',
+					value: null,
+				}, {
+					sign: 'rejectionNum',
+					id: 'add',
+					title: '已取消',
+					icon: 'icon-yiquxiao',
+					color: '#fe880e',
+					value: null,
+				}],
+				token: '',
+			}
+		},
+		created() {
+			this.list = this.list1
+			for (let key in this.orderStatistics) {
+				this.list.forEach((item, index) => {
+					if (key == item.sign) {
+						item.value = this.orderStatistics[key]
+					}
+				})
+			}
+		},
+		mounted() {
+			var token = this.$cache.getToken()
+			this.token = token
+		},
+		methods: {
+			// 订单页面
+			goOrder(index) {
+				let num = index
+				num = num + 1
+				if (this.token) {
+					uni.navigateTo({
+						url: '/pages/order/index?current=' + num
+					});
+				} else {
+					uni.$u.toast('请先登录')
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.home_card {
+		display: flex;
+		flex-direction: row;
+		flex-wrap: wrap;
+		justify-content: space-evenly;
+	}
+
+	.home_head {
+		margin-top: 30rpx;
+		display: flex;
+		align-items: center;
+	}
+
+	.home_line {
+		width: 8rpx;
+		height: 40rpx;
+		border-top-right-radius: 4rpx;
+		border-bottom-right-radius: 4rpx;
+	}
+
+	.home_item_title {
+		margin-left: 20rpx;
+		font-size: 50rpx;
+		font-weight: 600;
+	}
+
+	.item_subheading {
+		margin-left: 20rpx;
+		font-size: 30rpx;
+		color: #909399;
+	}
+
+	.card_item_home {
+		margin-top: 20rpx;
+		width: calc(50% - 40rpx);
+		background-color: #fff;
+		border-radius: 20rpx;
+	}
+
+	.home_bottom_item {
+		margin: 30rpx;
+		display: flex;
+		justify-content: space-between;
+		align-items: baseline;
+	}
+
+	.home_right_icon {
+		width: 44rpx;
+		height: 44rpx;
+		background-color: #e7e6e4;
+		border-radius: 50%;
+	}
+
+	.home_icon_image {
+		font-size: 70rpx;
+	}
+</style>

+ 160 - 0
components/x-steps.vue

@@ -0,0 +1,160 @@
+<template>
+	<view class="bg">
+		<view class="steps">
+			<view class="steps_item" v-for="(i, index) in infoList" :key="index">
+				<view class="s_r">
+					<view class="line" :style="{ backgroundColor: index != 0 ? '#EAEAEA' : 'rgba(0,0,0,0)' }"></view>
+					<view class="index" :style="{ backgroundColor: i.backgroundColor, color: color }"></view>
+					<view class="line"
+						:style="{ backgroundColor: index != infoList.length - 1 ? '#EAEAEA' : 'rgba(0,0,0,0)' }"></view>
+				</view>
+				<view class="s_l">
+					<view class="info_item">
+						<!-- 真是节点名称、时间 -->
+						<view class="top_info">
+							<text class="title_scacm">{{ i.text }}
+								<span v-if="i.phone">
+									联系电话:<span style="color: #f29100;margin-right: 15rpx;">{{i.phone}}</span>
+								</span>
+								{{ i.text1 }}
+							</text>
+							<text class="date">{{ i.createdAt || ''}}</text>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'XSteps',
+		props: {
+			infoList: {
+				type: Array,
+				default: [],
+			},
+			color: {
+				type: String,
+				default: '#fff',
+			},
+			backgroundColor: {
+				type: String,
+				default: '#2979ff',
+			},
+			lineNum: {
+				type: Number,
+				default: 0,
+			},
+		},
+		methods: {
+			// 审核状态
+			auditStatus(i) {
+				if (i == 2) {
+					return {
+						text: '通过',
+						color: '#00D288',
+						bgColor: '#EAFFF8',
+					}
+				} else if (i == 3) {
+					return {
+						text: '驳回',
+						color: '#FF4141',
+						bgColor: '#FFDCD5',
+					}
+				}
+			},
+			handlePreview(url) {
+				uni.previewImage({
+					urls: [url]
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	.steps {
+		display: flex;
+		flex-direction: column;
+
+		.steps_item {
+			display: flex;
+			flex-direction: row;
+
+			.s_r {
+				padding: 0 20rpx;
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+
+				.line {
+					flex: 1;
+					width: 5rpx;
+				}
+
+				.index {
+					width: 24rpx;
+					height: 24rpx;
+					border-radius: 50rpx;
+					border: 4rpx solid #e3eeff;
+					box-sizing: border-box;
+				}
+			}
+
+			.s_l {
+				display: flex;
+				flex-direction: column;
+				padding: 20rpx 0;
+				flex: 1;
+
+				.info_item {
+					background-color: #ffffff;
+					margin-right: 10rpx;
+					border-radius: 10rpx;
+					display: flex;
+					flex-direction: column;
+					justify-content: center;
+
+					.top_info {
+						display: flex;
+						flex-direction: column;
+					}
+
+					text {
+						font-size: 28rpx;
+						font-weight: 500;
+						color: rgba(51, 51, 51, 1);
+					}
+
+					.date {
+						font-size: 23rpx;
+						color: #afb4be;
+					}
+				}
+
+				.info_item:active {
+					background-color: #f4f4f4;
+				}
+			}
+		}
+	}
+
+	.title_scacm {
+		display: -webkit-box !important;
+		width: 100%;
+		font-size: 32rpx;
+		font-weight: 500;
+		color: rgba(102, 102, 102, 1);
+		word-break: break-all;
+	}
+
+	.ml5 {
+		margin-left: 10rpx;
+	}
+
+	.mt10 {
+		margin-top: 20rpx;
+	}
+</style>

+ 21 - 0
index.html

@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+	<script src="https://webapi.amap.com/maps?v=1.4.15&key=042f12d3f6f07494669e8eef6ebd301a"></script> <!-- 引入高德地图SDK -->
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>

+ 33 - 0
main.js

@@ -0,0 +1,33 @@
+import App from './App'
+
+// 引入全局uView
+import uView from 'uview-ui';
+Vue.use(uView);
+
+import request from './utils/request.js'
+import Storage from './store/storage.js'
+Vue.prototype.$api = request
+Vue.prototype.$cache = Storage
+
+// #ifndef VUE3
+import Vue from 'vue'
+import './uni.promisify.adaptor'
+Vue.config.productionTip = false
+App.mpType = 'app'
+const app = new Vue({
+	...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import {
+	createSSRApp
+} from 'vue'
+export function createApp() {
+	const app = createSSRApp(App)
+	return {
+		app
+	}
+}
+// #endif

+ 133 - 0
manifest.json

@@ -0,0 +1,133 @@
+{
+    "name" : "冷链送药",
+    "appid" : "__UNI__49186F9",
+    "description" : "",
+    "versionName" : "1.0.2",
+    "versionCode" : 101,
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {
+            "Maps" : {},
+            "Bluetooth" : {},
+            "Camera" : {},
+            "Barcode" : {}
+        },
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {
+                "dSYMs" : false
+            },
+            /* SDK配置 */
+            "sdkConfigs" : {
+                "geolocation" : {},
+                "maps" : {
+                    "amap" : {
+                        "name" : "amapzQIiQLSo",
+                        "appkey_ios" : "1",
+                        "appkey_android" : "042f12d3f6f07494669e8eef6ebd301a"
+                    }
+                },
+                "ad" : {}
+            },
+            "icons" : {
+                "android" : {
+                    "hdpi" : "unpackage/res/icons/72x72.png",
+                    "xhdpi" : "unpackage/res/icons/96x96.png",
+                    "xxhdpi" : "unpackage/res/icons/144x144.png",
+                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
+                },
+                "ios" : {
+                    "appstore" : "unpackage/res/icons/1024x1024.png",
+                    "ipad" : {
+                        "app" : "unpackage/res/icons/76x76.png",
+                        "app@2x" : "unpackage/res/icons/152x152.png",
+                        "notification" : "unpackage/res/icons/20x20.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "proapp@2x" : "unpackage/res/icons/167x167.png",
+                        "settings" : "unpackage/res/icons/29x29.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "spotlight" : "unpackage/res/icons/40x40.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
+                    },
+                    "iphone" : {
+                        "app@2x" : "unpackage/res/icons/120x120.png",
+                        "app@3x" : "unpackage/res/icons/180x180.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "notification@3x" : "unpackage/res/icons/60x60.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "settings@3x" : "unpackage/res/icons/87x87.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
+                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
+                    }
+                }
+            }
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true,
+        "permission" : {}
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "2",
+    "h5" : {
+        "sdkConfigs" : {
+            "maps" : {
+                "amap" : {
+                    "key" : "7c0390cf6ae1cd6afeef85becf6fa515",
+                    "securityJsCode" : "fb3133f64cddac06df9888ec1a4b3ad5",
+                    "serviceHost" : ""
+                }
+            }
+        }
+    }
+}

+ 135 - 0
package-lock.json

@@ -0,0 +1,135 @@
+{
+  "name": "baozhida-logistics-app",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "@types/amap-js-api": "^1.4.16",
+        "barcode": "^0.1.0",
+        "jsbarcode": "^3.11.6"
+      }
+    },
+    "node_modules/@types/amap-js-api": {
+      "version": "1.4.16",
+      "resolved": "https://registry.npmmirror.com/@types/amap-js-api/-/amap-js-api-1.4.16.tgz",
+      "integrity": "sha512-gfcEswuU4GNRdGAh74FhxfLlU6qxmVIJYPf3HV0uKucCWQ9iSr8TonDhqBvHVmmQEY60K+UAeIh0YLwKo/jpFQ=="
+    },
+    "node_modules/array-parallel": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmmirror.com/array-parallel/-/array-parallel-0.1.3.tgz",
+      "integrity": "sha512-TDPTwSWW5E4oiFiKmz6RGJ/a80Y91GuLgUYuLd49+XBS75tYo8PNgaT2K/OxuQYqkoI852MDGBorg9OcUSTQ8w=="
+    },
+    "node_modules/array-series": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmmirror.com/array-series/-/array-series-0.1.5.tgz",
+      "integrity": "sha512-L0XlBwfx9QetHOsbLDrE/vh2t018w9462HM3iaFfxRiK83aJjAt/Ja3NMkOW7FICwWTlQBa3ZbL5FKhuQWkDrg=="
+    },
+    "node_modules/barcode": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/barcode/-/barcode-0.1.0.tgz",
+      "integrity": "sha512-GslbXakjG61fwHnIN/vrUkPsa61WVAJDnb7jAwmbjRW5bZdwINymo1+FgjYJrGf2MDHxAt3bUWgmEMF8ETZxHQ==",
+      "dependencies": {
+        "gm": "1.16.0"
+      }
+    },
+    "node_modules/debug": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-0.7.0.tgz",
+      "integrity": "sha512-UWZnvGiX9tQgtrsA+mhGLKnUFvr1moempl9IvqQKyFnEgN0T4kpCE+KJcqTLcVxQjRVRnLF3VSE1Hchki5N98g==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/gm": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmmirror.com/gm/-/gm-1.16.0.tgz",
+      "integrity": "sha512-b5oVGr8wCI7VNfjzeXiFocCZrcpkRUxSoVYfksBMEp/jo2nYwRKhcfOURarxFwjXyW1GvEY2EmmupVLnh0vXjg==",
+      "dependencies": {
+        "array-parallel": "~0.1.0",
+        "array-series": "~0.1.0",
+        "debug": "0.7.0",
+        "stream-to-buffer": "~0.0.1",
+        "through": "~2.3.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/jsbarcode": {
+      "version": "3.11.6",
+      "resolved": "https://registry.npmmirror.com/jsbarcode/-/jsbarcode-3.11.6.tgz",
+      "integrity": "sha512-G5TKGyKY1zJo0ZQKFM1IIMfy0nF2rs92BLlCz+cU4/TazIc4ZH+X1GYeDRt7TKjrYqmPfTjwTBkU/QnQlsYiuA=="
+    },
+    "node_modules/stream-to-buffer": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/stream-to-buffer/-/stream-to-buffer-0.0.1.tgz",
+      "integrity": "sha512-LsvisgE3iThboRqA+XLmtnY9ktPLVPOj3zZxXMhlezeCcAh0RhomquXJgB8H+lb/RR/pPcbNVGHVKFUwjpoRtw==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/through/-/through-2.3.8.tgz",
+      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
+    }
+  },
+  "dependencies": {
+    "@types/amap-js-api": {
+      "version": "1.4.16",
+      "resolved": "https://registry.npmmirror.com/@types/amap-js-api/-/amap-js-api-1.4.16.tgz",
+      "integrity": "sha512-gfcEswuU4GNRdGAh74FhxfLlU6qxmVIJYPf3HV0uKucCWQ9iSr8TonDhqBvHVmmQEY60K+UAeIh0YLwKo/jpFQ=="
+    },
+    "array-parallel": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmmirror.com/array-parallel/-/array-parallel-0.1.3.tgz",
+      "integrity": "sha512-TDPTwSWW5E4oiFiKmz6RGJ/a80Y91GuLgUYuLd49+XBS75tYo8PNgaT2K/OxuQYqkoI852MDGBorg9OcUSTQ8w=="
+    },
+    "array-series": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmmirror.com/array-series/-/array-series-0.1.5.tgz",
+      "integrity": "sha512-L0XlBwfx9QetHOsbLDrE/vh2t018w9462HM3iaFfxRiK83aJjAt/Ja3NMkOW7FICwWTlQBa3ZbL5FKhuQWkDrg=="
+    },
+    "barcode": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/barcode/-/barcode-0.1.0.tgz",
+      "integrity": "sha512-GslbXakjG61fwHnIN/vrUkPsa61WVAJDnb7jAwmbjRW5bZdwINymo1+FgjYJrGf2MDHxAt3bUWgmEMF8ETZxHQ==",
+      "requires": {
+        "gm": "1.16.0"
+      }
+    },
+    "debug": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-0.7.0.tgz",
+      "integrity": "sha512-UWZnvGiX9tQgtrsA+mhGLKnUFvr1moempl9IvqQKyFnEgN0T4kpCE+KJcqTLcVxQjRVRnLF3VSE1Hchki5N98g=="
+    },
+    "gm": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmmirror.com/gm/-/gm-1.16.0.tgz",
+      "integrity": "sha512-b5oVGr8wCI7VNfjzeXiFocCZrcpkRUxSoVYfksBMEp/jo2nYwRKhcfOURarxFwjXyW1GvEY2EmmupVLnh0vXjg==",
+      "requires": {
+        "array-parallel": "~0.1.0",
+        "array-series": "~0.1.0",
+        "debug": "0.7.0",
+        "stream-to-buffer": "~0.0.1",
+        "through": "~2.3.1"
+      }
+    },
+    "jsbarcode": {
+      "version": "3.11.6",
+      "resolved": "https://registry.npmmirror.com/jsbarcode/-/jsbarcode-3.11.6.tgz",
+      "integrity": "sha512-G5TKGyKY1zJo0ZQKFM1IIMfy0nF2rs92BLlCz+cU4/TazIc4ZH+X1GYeDRt7TKjrYqmPfTjwTBkU/QnQlsYiuA=="
+    },
+    "stream-to-buffer": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/stream-to-buffer/-/stream-to-buffer-0.0.1.tgz",
+      "integrity": "sha512-LsvisgE3iThboRqA+XLmtnY9ktPLVPOj3zZxXMhlezeCcAh0RhomquXJgB8H+lb/RR/pPcbNVGHVKFUwjpoRtw=="
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/through/-/through-2.3.8.tgz",
+      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
+    }
+  }
+}

+ 6 - 0
package.json

@@ -0,0 +1,6 @@
+{
+  "dependencies": {
+    "barcode": "^0.1.0",
+    "jsbarcode": "^3.11.6"
+  }
+}

+ 82 - 0
pages.json

@@ -0,0 +1,82 @@
+{
+	"easycom": {
+		"autoscan": true, //是否自动扫描组件
+		"custom": {
+			"^x-(.*)": "@/components/x-$1.vue", // 匹配components目录内的vue文件
+			"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
+		}
+	},
+	"pages": [{
+		"path": "pages/indexRouter",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/login",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/codeLogin",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/register",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/home/particulars",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/order/index",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/order/delivery",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/order/orderDetails",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/order/quantum",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/order/addWaybill",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/mine/password",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/order/logisticsDetails",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}, {
+		"path": "pages/order/humiture",
+		"style": {
+			"navigationStyle": "custom"
+		}
+	}],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F8F8F8"
+	},
+	"uniIdRouter": {}
+}

+ 290 - 0
pages/codeLogin.vue

@@ -0,0 +1,290 @@
+<template>
+	<!-- 验证码登录 -->
+	<view>
+		<u-navbar title="" autoBack placeholder></u-navbar>
+		<view class="card_login">
+			<view class="card_head_logo">
+				<image class="mine_image" src="../static/logo.png" mode=""></image>
+				<view class="welcome_title">欢迎登录宝智达冷链运输</view>
+			</view>
+			<u-form labelPosition="left" :model="formList" :rules="rules" ref="uForm">
+				<u-form-item label="手机号" prop="phone" labelWidth="80" borderBottom ref="item1">
+					<u-input v-model="formList.phone" border="none" placeholder="请填写手机号"></u-input>
+				</u-form-item>
+				<u-form-item label="验证码" prop="verifyCode" labelWidth="80" borderBottom>
+					<u-input v-model="formList.verifyCode" border="none" placeholder="请填写验证码"></u-input>
+					<u-button slot="right" @tap="getCode" :text="tips" type="success" size="mini"
+						:disabled="disabled1"></u-button>
+				</u-form-item>
+			</u-form>
+			<u-code ref="uCode" @change="codeChange" seconds="90" @start="disabled1 = true"
+				@end="disabled1 = false"></u-code>
+			<view class="btn_login">
+				<view class="title_register" @click="posswordLogin">密码登录</view>
+				<view class="title_register" @click="goRegister">去注册</view>
+			</view>
+			<view class="card_hint">
+				<view class="card_checkbox">
+					<u-checkbox-group @change="checkboxChange">
+						<u-checkbox name="asgas" shape="circle"></u-checkbox>
+					</u-checkbox-group>
+					<span class="message" :style="{display:hintShow ? 'block' : 'none'}">
+						请先阅读并同意协议
+					</span>
+				</view>
+				<view class="agreement_title">我已阅读并同意<span class="protocol_title">《用户协议》</span>、<span
+						class="protocol_title">《隐私政策》</span>,
+					并授权宝智达气瓶使用该宝智达气瓶账号信息(如昵称、头像、收货地址)进行统一管理
+				</view>
+			</view>
+			<view class="card_btn">
+				<u-button type="primary" :disabled="disabled" text="登录" @click="getLogin"></u-button>
+			</view>
+		</view>
+		<!-- <view class="wx_card">
+			<view class="card_btn_wx frame">
+				<u-icon name="weixin-fill" size="26" color="#07c160"></u-icon>
+			</view>
+			<span class="title_login_wx">微信登录</span>
+		</view> -->
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				formList: {
+					phone: '',
+					verifyCode: '',
+				},
+				checked: false,
+				hintShow: false,
+				userInfo: {},
+				tips: '',
+				disabled1: false,
+				rules: {
+					phone: {
+						type: 'string',
+						required: true,
+						message: '请填写手机号',
+						trigger: ['blur']
+					},
+					verifyCode: {
+						type: 'string',
+						required: true,
+						message: '请填写验证码',
+						trigger: ['blur']
+					},
+				},
+			}
+		},
+		computed: {
+			disabled() {
+				if (this.formList.phone == '' || this.formList.verifyCode == '') {
+					return true;
+				} else {
+					return false;
+				}
+			}
+		},
+		// beforeCreate() {
+		// 	var token = this.$cache.getToken()
+		// 	if (token) {
+		// 		uni.redirectTo({
+		// 			url: '/pages/indexRouter'
+		// 		})
+		// 	}
+		// },
+		methods: {
+			checkboxChange() {
+				if (this.checked) {
+					this.checked = false
+				} else {
+					this.checked = true
+					this.hintShow = false
+				}
+			},
+			getLogin() {
+				if (!this.checked) {
+					this.hintShow = true
+				} else {
+					uni.showLoading({
+						title: '登录中...'
+					});
+					this.$api.post('/api/applet/login', {
+						phone: this.formList.phone,
+						verifyCode: this.formList.verifyCode
+					}).then((res) => {
+						console.log(res,4443)
+						if (res.code == 200) {
+							this.$cache.setToken(res.data.token)
+							// this.getUserInfo()
+							uni.redirectTo({
+								url: '/pages/indexRouter'
+							})
+						} else {
+							uni.showToast({
+								title: res.msg,
+								icon: 'none'
+							});
+						}
+						uni.hideLoading();
+					})
+				}
+			},
+			getUserInfo() {
+				this.$api.get('/api/user/profile').then(res => {
+					if (res.code == 200) {
+						this.userInfo = res.data
+						this.$cache.setCache('userInfo', this.userInfo)
+						uni.redirectTo({
+							url: '/pages/indexRouter'
+						})
+					} else {
+						uni.showToast({
+							title: '当前用户不存在',
+							icon: 'none'
+						});
+					}
+				})
+			},
+			getCode() {
+				if (this.$refs.uCode.canGetCode) {
+					// 模拟向后端请求验证码
+					if (this.formList.phone) {
+						uni.showLoading({
+							title: '正在获取验证码'
+						})
+						// 通知验证码组件内部开始倒计时
+						this.$refs.uCode.start();
+						this.$api.get('/api/verify-code', {
+							phone: this.formList.phone,
+						}).then(res => {
+							if (res.code == 200) {
+								// 这里此提示会被this.start()方法中的提示覆盖
+								uni.$u.toast('验证码已发送');
+							}
+							uni.hideLoading();
+						})
+					} else {
+						uni.$u.toast('请先输入手机号');
+					}
+				} else {
+					uni.$u.toast('倒计时结束后再发送');
+				}
+			},
+			codeChange(text) {
+				this.tips = text;
+			},
+			// 密码登录
+			posswordLogin() {
+				uni.redirectTo({
+					url: '/pages/login'
+				})
+			},
+			// 注册
+			goRegister() {
+				uni.navigateTo({
+					url: '/pages/register'
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	page {
+		background-color: #fff !important;
+	}
+
+	.welcome_title {
+		font-size: 46rpx;
+		font-weight: 600;
+		margin-bottom: 60rpx;
+		margin-top: 10rpx;
+	}
+
+	.card_head_logo {
+		margin-top: 50rpx;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.card_login {
+		padding: 20rpx 40rpx;
+	}
+
+	.mine_image {
+		width: 100rpx;
+		height: 100rpx;
+	}
+
+	.login_input_box {
+		margin-bottom: 30rpx;
+	}
+
+	.title_register {
+		display: flex;
+		justify-content: flex-end;
+		font-size: 28rpx;
+		color: #494949;
+	}
+
+	.card_hint {
+		margin-top: 60rpx;
+		display: flex;
+		align-items: flex-start;
+	}
+
+	.agreement_title {
+		font-size: 26rpx;
+		color: #333;
+	}
+
+	.protocol_title {
+		color: #1177ff;
+	}
+
+	.card_btn {
+		margin-top: 20rpx;
+	}
+
+	.card_checkbox {
+		margin-top: 5rpx;
+		position: relative;
+		cursor: pointer;
+	}
+
+	.message {
+		display: none;
+		position: absolute;
+		padding: 5px 8px;
+		border: 1px solid #494949;
+		border-radius: 9px;
+		top: -36px;
+		font-size: 12px;
+		width: 110px;
+		background: #494949;
+		color: #fff;
+		left: -6px;
+	}
+
+	.message::after {
+		content: '';
+		position: absolute;
+		left: 8px;
+		top: 100%;
+		border: 8px solid transparent;
+		border-top: 6px solid #494949;
+		margin-left: -2px;
+	}
+
+	.btn_login {
+		margin-top: 20rpx;
+		display: flex;
+		justify-content: space-between;
+	}
+</style>

+ 128 - 0
pages/home/index.vue

@@ -0,0 +1,128 @@
+<template>
+	<!-- 首页 -->
+	<view class="card_index_bgc">
+		<view class="company_name" v-if="userInfo.dept">{{userInfo.dept.name || ''}}</view>
+		<x-statistics :userType="homeUserType" :orderStatistics="orderStatistics"></x-statistics>
+		<view class="card_operate">
+			<view class="btn_item_tab" v-for="(item,index) in tableList" :key="index" @click="getOperate(item)">
+				<view class="card_image center_in" :style="{backgroundColor:item.color}">
+					<span class="iconfont icon_image" :class="item.icon"></span>
+				</view>
+				<view class="item_title_tab">{{item.title}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			token: {
+				type: String,
+				default: () => '',
+			},
+			userInfo: {
+				type: Object,
+				default: () => {},
+			},
+			orderStatistics: {
+				type: Object,
+				default: () => {},
+			}
+		},
+		data() {
+			return {
+				homeUserType: 1,
+				tableList: [],
+				userList: [{
+					id: 'add',
+					title: '添加运单',
+					icon: 'icon-tianjiadingdan',
+					color: '#1cc723',
+				}],
+			}
+		},
+		created() {
+			this.homeUserType = 3
+			this.tableList = this.userList
+		},
+		methods: {
+			getOperate(value) {
+				if (this.token) {
+					if (['truck', 'unload', 'signfor', 'put', 'out'].includes(value.id)) {
+						uni.navigateTo({
+							url: '/pages/order/delivery?id=' + value.id + '&title=' + value.title
+						});
+					} else if (value.id == 'add') {
+						uni.navigateTo({
+							url: '/pages/order/addWaybill?title=添加订单&type=1'
+						});
+					} else if (value.id == 'details') {
+						uni.navigateTo({
+							url: '/pages/home/particulars?title=true'
+						});
+					} else if (value.id == 'detailsil') {
+						uni.navigateTo({
+							url: '/pages/home/particulars?title=false'
+						});
+					}
+				} else {
+					uni.$u.toast('请先登录')
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.card_index_bgc {
+		padding-top: constant(safe-area-inset-top);
+		padding-top: env(safe-area-inset-top);
+		background-image: linear-gradient(#a9e3f1, #f3f4f6);
+	}
+
+	.company_name {
+		font-size: 36rpx;
+		padding: 50rpx 30rpx 20rpx 30rpx;
+		font-weight: 600;
+	}
+
+	.card_swiper {
+		padding: 20rpx 20rpx 0rpx 20rpx;
+	}
+
+	.card_operate {
+		display: flex;
+		flex-wrap: wrap;
+		justify-content: flex-start;
+		background-color: #fff;
+		padding: 20rpx 0rpx;
+		margin: 20rpx;
+		border-radius: 20rpx;
+	}
+
+	.btn_item_tab {
+		width: 25%;
+		display: flex;
+		align-items: center;
+		flex-direction: column;
+		margin: 10rpx 0rpx;
+	}
+
+	.card_image {
+		width: 90rpx;
+		height: 90rpx;
+		border-radius: 30rpx;
+		margin-bottom: 10rpx;
+	}
+
+	.icon_image {
+		font-size: 50rpx;
+		color: #fff;
+	}
+
+	.item_title_tab {
+		font-size: 26rpx;
+		color: #606266;
+	}
+</style>

+ 192 - 0
pages/home/particulars.vue

@@ -0,0 +1,192 @@
+<template>
+	<!-- 仓库、车辆详情页面 -->
+	<view>
+		<u-navbar :title="detailTitle" autoBack placeholder></u-navbar>
+		<view class="card_time">
+			<uni-datetime-picker v-model="datetimeRange" type="datetimerange" rangeSeparator="至" @change="changeTime" />
+		</view>
+		<view class="sub_card" v-if="probeList.length > 0">
+			<u-subsection :list="probeList" :current="current" keyName="T_name" @change="sectionChange"></u-subsection>
+		</view>
+		<view class="card_particulars">
+			<view class="humiture_line"></view>
+			<view class="card_humiture border_humiture">
+				<view class="headline_item w_wsd">温度(°C)</view>
+				<view class="headline_item w_wsd">湿度(Rh)</view>
+				<view class="headline_item w_time">时间</view>
+			</view>
+			<view style="width: 100%;" v-if="humitureList.length > 0">
+				<view class="card_humiture" v-for="(item,index) in humitureList" :key="index">
+					<view class="title_item w_wsd">{{item.T_t || ''}}</view>
+					<view class="title_item w_wsd">{{item.T_rh || ''}}</view>
+					<view class="title_item w_time">{{item.T_time || ''}}</view>
+				</view>
+				<view class="center_in">
+					<view v-if="loadingMore" style="width: 60%;"><u-divider :text="loading"></u-divider></view>
+				</view>
+			</view>
+			<view v-else style="padding: 30rpx 0rpx;">
+				<u-empty mode="data" text="暂无温湿度记录"></u-empty>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				current: 0,
+				detailTitle: '',
+				probeList: [],
+				humitureList: [],
+				datetimeRange: [],
+				defaultItem: {},
+				pageSize: 15,
+				currentPage: 1,
+				loadingMore: false,
+				loading: '加载中'
+			}
+		},
+		onReachBottom() {
+			if (!this.loadingMore) {
+				this.getDeviceData(this.defaultItem)
+			}
+		},
+		onLoad(value) {
+			if (value.title == 'true' || value.title === true) {
+				this.detailTitle = '仓库信息'
+			} else if (value.title == 'false' || value.title === false) {
+				this.detailTitle = '车辆信息'
+			}
+			this.getSensor()
+		},
+		methods: {
+			getSensor() {
+				this.$api.get('/api/device/sensor-list').then(res => {
+					if (res.code == 200) {
+						if (res.data.list) {
+							this.probeList = res.data.list
+						}
+						if (this.probeList.length > 0) {
+							this.defaultItem = this.probeList[0]
+						}
+						this.getDeviceData(this.defaultItem)
+					}
+				})
+			},
+			getDeviceData(value) {
+				this.loadingMore = true;
+				let params = {
+					t_sn: value.T_sn,
+					t_id: value.T_id,
+					startTime: '',
+					endTime: '',
+					page: this.currentPage,
+					pageSize: this.pageSize,
+				}
+				if (this.datetimeRange.length > 0) {
+					params.startTime = this.datetimeRange[0]
+					params.endTime = this.datetimeRange[1]
+				}
+				this.$api.get('/api/device/data', params).then(res => {
+					if (res.code == 200) {
+						const data = res.data.list
+						if (this.loadingMore == true && data) {
+							this.humitureList = this.humitureList.concat(data);
+						}
+						if (data.length < this.pageSize) {
+							this.loadingMore = true
+							this.loading = '没有更多了'
+						} else {
+							this.loading = '加载中'
+							this.loadingMore = false
+							this.currentPage++
+						}
+					}
+				})
+			},
+			// 日期选择
+			changeTime(value) {
+				this.datetimeRange = value
+				this.currentPage = 1
+				this.humitureList = []
+				this.getDeviceData(this.defaultItem)
+			},
+			// 选择探头
+			sectionChange(numIndex) {
+				this.currentPage = 1
+				this.humitureList = []
+				this.current = numIndex
+				const arrList = this.probeList[numIndex]
+				this.defaultItem = arrList
+				this.getDeviceData(arrList)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	page {
+		background-color: #fff !important;
+	}
+
+	.card_time {
+		display: flex;
+		margin: 20rpx;
+	}
+
+	.sub_card {
+		margin: 20rpx;
+	}
+
+	.card_particulars {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		padding: 20rpx 0rpx;
+		margin: 30rpx 20rpx 20rpx 20rpx;
+		border-radius: 40rpx;
+		background-color: #fff;
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+	}
+
+	.humiture_line {
+		width: 80rpx;
+		height: 10rpx;
+		border-radius: 20rpx;
+		background-color: #C0C4CC;
+		margin: 20rpx;
+	}
+
+	.card_humiture {
+		width: 100%;
+		display: flex;
+		align-items: center;
+		padding: 20rpx 0rpx;
+		margin-bottom: 10rpx;
+	}
+
+	.border_humiture {
+		border-bottom: 1rpx solid #EBEEF5;
+	}
+
+	.headline_item {
+		font-size: 30rpx;
+		font-weight: 600;
+	}
+
+	.title_item {
+		font-size: 28rpx
+	}
+
+	.w_wsd {
+		text-align: center;
+		width: 27%;
+	}
+
+	.w_time {
+		text-align: center;
+		width: 46%;
+	}
+</style>

+ 92 - 0
pages/indexRouter.vue

@@ -0,0 +1,92 @@
+<template>
+	<view>
+		<Home :token="token" :userInfo="userInfo" :orderStatistics="orderStatistics" v-if="nowchos === 0"
+			:key="Math.random()"></Home>
+		<Mine ref="mine" :token="token" :userInfo="userInfo" v-else-if="nowchos === 1"></Mine>
+		<view class="bottomboxs">
+			<x-navbottom :nowchos='nowchos' @botomchos='botomchos'></x-navbottom>
+		</view>
+	</view>
+</template>
+
+<script>
+	import Home from './home/index.vue' // 引入首页
+	import Mine from './mine/index.vue' // 我的
+	export default {
+		components: { //在这里注册相应的组件
+			Home,
+			Mine,
+		},
+		data() {
+			return {
+				nowchos: 0, //当前选择了那个底部菜单
+				isorders: 0,
+				userType: 3,
+				token: '',
+				userInfo: {},
+				orderStatistics: {},
+			}
+		},
+		onShow() {
+			var token = this.$cache.getToken()
+			this.token = token
+			var userInfo = this.$cache.getCache('userInfo')
+			if (userInfo) {
+				this.userInfo = userInfo
+			} else {
+				this.userInfo = {}
+			}
+			this.getUserInfo()
+		},
+		methods: {
+			getUserInfo() {
+				this.$api.get('/api/user/profile').then(res => {
+					if (res.code == 200) {
+						this.userInfo = res.data.user
+						this.$cache.setCache('userInfo', this.userInfo)
+						if (this.nowchos == 0) {
+							this.getHomeList()
+						}
+					} else {
+						this.$cache.removeToken()
+						this.$cache.removeCache('userInfo')
+						uni.showToast({
+							title: '当前用户不存在',
+							icon: 'none'
+						});
+					}
+				})
+			},
+			// 获取订单统计
+			getHomeList() {
+				this.$api.get('/api/waybill/home').then(res => {
+					if (res.code == 200) {
+						this.orderStatistics = res.data
+					}
+				})
+			},
+			// tab
+			botomchos(e) {
+				const that = this
+				if (e == 2) {
+					that.$nextTick(() => {
+						setTimeout(function() {
+							that.$refs.mine.refreshTokenil()
+						}, 100);
+					})
+				}
+				uni.setStorageSync('nowchos', e);
+				that.nowchos = e
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.bottomboxs {
+		position: relative;
+		bottom: 0;
+		z-index: 2023;
+		height: 100rpx;
+	}
+</style>

+ 261 - 0
pages/login.vue

@@ -0,0 +1,261 @@
+<template>
+	<!-- 登录注册 -->
+	<view>
+		<u-navbar title="" leftIconSize="0" placeholder></u-navbar>
+		<view class="card_login">
+			<view class="card_head_logo">
+				<image class="mine_image" src="../static/logo.png" mode=""></image>
+				<view class="welcome_title">欢迎登录宝智达冷链运输</view>
+			</view>
+			<u--form labelPosition="left" :model="formList" :rules="rules" ref="uForm">
+				<u-form-item label="账号" prop="username" labelWidth="50" borderBottom ref="item1">
+					<u-input v-model="formList.username" border="none" placeholder="请填写账号"></u-input>
+				</u-form-item>
+				<u-form-item label="密码" prop="password" labelWidth="50" borderBottom ref="item1">
+					<u-input v-show="isText === true" placeholder="请填写密码" border="none" v-model="formList.password"
+						class="input_class_pwd" :password="true">
+						<template slot="suffix" v-if="formList.password">
+							<u-icon name="eye-off" @click="isText = false" size="20"></u-icon>
+						</template>
+					</u-input>
+					<u-input v-show="isText === false" placeholder="请填写密码" border="none" v-model="formList.password"
+						class="input_class_pwd" :password="false">
+						<template slot="suffix" v-if="formList.password">
+							<u-icon name="eye-fill" @click="isText = true" size="20"></u-icon>
+						</template>
+					</u-input>
+				</u-form-item>
+			</u--form>
+			<!-- <view class="btn_login">
+				<view class="title_register" @click="codeLogin">验证码登录</view>
+				<view class="title_register" @click="goRegister">去注册</view>
+			</view> -->
+			<view class="card_hint">
+				<view class="card_checkbox">
+					<u-checkbox-group @change="checkboxChange">
+						<u-checkbox name="asgas" shape="circle"></u-checkbox>
+					</u-checkbox-group>
+					<span class="message" :style="{display:hintShow ? 'block' : 'none'}">
+						请先阅读并同意协议
+					</span>
+				</view>
+				<view class="agreement_title">我已阅读并同意<span class="protocol_title">《用户协议》</span>、<span
+						class="protocol_title">《隐私政策》</span>,
+					并授权宝智达气瓶使用该宝智达气瓶账号信息(如昵称、头像、收货地址)进行统一管理
+				</view>
+			</view>
+			<view class="card_btn">
+				<u-button type="primary" :disabled="disabled" text="登录" @click="getLogin"></u-button>
+			</view>
+		</view>
+		<!-- <view class="wx_card">
+			<view class="card_btn_wx frame">
+				<u-icon name="weixin-fill" size="26" color="#07c160"></u-icon>
+			</view>
+			<span class="title_login_wx">微信登录</span>
+		</view> -->
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				formList: {
+					username: '',
+					password: '',
+				},
+				rules: {
+					username: {
+						type: 'string',
+						required: true,
+						message: '请填写手机号',
+						trigger: ['blur']
+					},
+					password: {
+						type: 'string',
+						required: true,
+						message: '请填写密码',
+						trigger: ['blur']
+					},
+				},
+				checked: false,
+				hintShow: false,
+				userInfo: {},
+				isText: true,
+			}
+		},
+		computed: {
+			disabled() {
+				if (this.formList.username == '' || this.formList.password == '') {
+					return true;
+				} else {
+					return false;
+				}
+			},
+		},
+		methods: {
+			checkboxChange() {
+				if (this.checked) {
+					this.checked = false
+				} else {
+					this.checked = true
+					this.hintShow = false
+				}
+			},
+			getLogin() {
+				if (!this.checked) {
+					this.hintShow = true
+				} else {
+					uni.showLoading({
+						title: '登录中...'
+					});
+					this.$api.post('/api/login', {
+						mobile: true,
+						type: 1,
+						username: this.formList.username,
+						password: this.formList.password
+					}).then((res) => {
+						if (res.code == 200) {
+							this.$cache.setToken(res.token)
+							// this.getUserInfo()
+
+							uni.redirectTo({
+								url: '/pages/indexRouter'
+							})
+						} else {
+							uni.showToast({
+								title: res.data.msg,
+								icon: 'none'
+							});
+						}
+						uni.hideLoading();
+					}).catch(() => {
+						uni.hideLoading();
+					})
+				}
+			},
+			getUserInfo() {
+				this.$api.get('/api/user/profile').then(res => {
+					if (res.code == 200) {
+						this.userInfo = res.data.user
+						this.$cache.setCache('userInfo', this.userInfo)
+						uni.redirectTo({
+							url: '/pages/indexRouter'
+						})
+					} else {
+						uni.showToast({
+							title: '当前用户不存在',
+							icon: 'none'
+						});
+					}
+				})
+			},
+			// 验证码登录
+			codeLogin() {
+				uni.redirectTo({
+					url: '/pages/codeLogin'
+				})
+			},
+			// 注册
+			goRegister() {
+				this.$refs.uForm.resetFields()
+				uni.navigateTo({
+					url: '/pages/register'
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	page {
+		background-color: #fff !important;
+	}
+
+	.welcome_title {
+		font-size: 46rpx;
+		font-weight: 600;
+		margin-bottom: 60rpx;
+		margin-top: 10rpx;
+	}
+
+	.card_head_logo {
+		margin-top: 50rpx;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.card_login {
+		padding: 20rpx 40rpx;
+	}
+
+	.mine_image {
+		width: 100rpx;
+		height: 100rpx;
+	}
+
+	.title_register {
+		display: flex;
+		justify-content: flex-end;
+		font-size: 28rpx;
+		color: #494949;
+	}
+
+	.card_hint {
+		margin-top: 60rpx;
+		display: flex;
+		align-items: flex-start;
+	}
+
+	.agreement_title {
+		font-size: 26rpx;
+		color: #333;
+	}
+
+	.protocol_title {
+		color: #1177ff;
+	}
+
+	.card_btn {
+		margin-top: 20rpx;
+	}
+
+	.card_checkbox {
+		margin-top: 5rpx;
+		position: relative;
+		cursor: pointer;
+	}
+
+	.message {
+		display: none;
+		position: absolute;
+		padding: 5px 8px;
+		border: 1px solid #494949;
+		border-radius: 9px;
+		top: -36px;
+		font-size: 12px;
+		width: 110px;
+		background: #494949;
+		color: #fff;
+		left: -6px;
+	}
+
+	.message::after {
+		content: '';
+		position: absolute;
+		left: 8px;
+		top: 100%;
+		border: 8px solid transparent;
+		border-top: 6px solid #494949;
+		margin-left: -2px;
+	}
+
+	.btn_login {
+		margin-top: 20rpx;
+		display: flex;
+		justify-content: space-between;
+	}
+</style>

+ 841 - 0
pages/mine/Printreading.vue

@@ -0,0 +1,841 @@
+<template>
+	<view class="bluetooths">
+		<view class="bluetooths_btn">
+			<text class="bluetooths_txt1">搜索打印机/停止搜索</text>
+			<u-switch v-model="checked" size='40' @change='change'></u-switch>
+		</view>
+		<view class="text-red">已连接设备</view>
+		<view>
+			<view v-if="connected===''">
+				<view style="padding:30rpx 0;color: #cccc;">暂未连接任何设备</view>
+			</view>
+			<view v-else style="display: flex;align-items: center;justify-content: space-between;padding:30rpx 0;">
+				<view style="display: flex;align-items: center;">
+					<image src="../../static/img/dyj.svg" mode="widthFix" style="width: 60rpx;height: 60rpx;"></image>
+					<view style="margin-left: 30rpx;color: #040203;font-size: 35rpx;">
+						{{connected==''?'打印机未命名':connected}}
+					</view>
+				</view>
+				<view>
+					<view style="text-decoration: underline;">{{close}}</view>
+				</view>
+			</view>
+		</view>
+		<view class="text-red">可连接设备</view>
+		<view v-if="devices.length==0">
+			<view style="padding:30rpx 0;color: #cccc;">暂无可连接设备</view>
+		</view>
+		<view v-else>
+			<view v-for="(item, index) in devices" :key="index">
+				<view v-if="item.name!=''" style="display: flex;align-items: center;justify-content: space-between;padding:30rpx 0;" @click="onConn(item)">
+					<view style="display: flex;align-items: center;">
+						<image src="../../static/img/dyj.svg" mode="widthFix" style="width: 60rpx;height: 60rpx;">
+						</image>
+						<view style="margin-left: 30rpx;color: #040203;font-size: 35rpx;">
+							{{item.name==''?'打印机未命名':item.name}}
+						</view>
+					</view>
+					<view>
+						<u-icon name="info-circle" color="#7c7a7b" size="30"></u-icon>
+					</view>
+				</view>
+			</view>
+		</view>
+		<u-button type="primary" :disabled="disabled" style="margin-top: 100upx;" @click="senBleLabel2()">票据打印</u-button>
+	</view>
+</template>
+<script>
+	import tsc from '../../../pages_warehouse/static/libs/tsc.js'
+	import esc from '../../../pages_warehouse/static/libs/esc.js'
+
+	export default {
+		data() {
+			return {
+				
+				Admin_rh:this.$store.state.Admin_rh,//控制登录用户显示湿度 0隐藏 1显示
+				bool:true,//链接成功可打印 true未连接
+				onConns:true,//打印机是否链接 true未链接
+				devices: [],
+				disabled:false,//防止多次触发
+				disBoll:null,
+				currDev: null,
+				connId: '',
+				checked: false,
+				connected: '', //已连接蓝牙
+				tableDomId: '',
+				tableImgPath: '',
+				dangTime:null,//打印时间
+				songhuo: this.$store.state.FirmList.songhuo, //送货公司
+				shouhuo: this.$store.state.FirmList.shouhuo, //收货公司
+				PrinList: this.$store.state.PrinList, //打印T_sn与主机名
+				wdList:[] ,//数据包
+				Admin_name:this.$store.state.Admin_name ,//公司名称
+				maxData:[
+					{T_name:'T1传感器',Maxwen: '',Minwen: '',pingjue: 0,},
+					{T_name:'T2传感器',Maxwen: '',Minwen: '',pingjue: 0,},
+					{T_name:'T3传感器',Maxwen: '',Minwen: '',pingjue: 0,},
+					{T_name:'T4传感器',Maxwen: '',Minwen: '',pingjue: 0,}
+				]
+			}
+		},
+		watch:{
+			bool(newName, oldName) {
+				console.log('是否变化',newName, oldName)
+				if(newName){
+					this.senBleLabel2()
+				}
+			},
+		},
+		created() {
+			this.wdList = this.$store.state.PrinData.wdList
+			console.log('created================1',this.$store.state.PrinData.wdList)
+			// console.log('created================2',this.$store.state.PrinData.wdList.reverse())
+			console.log('created================3',this.wdList)
+		},
+		mounted() {
+			console.log('1================1',this.wdList)
+			var staTime = this.wdList[this.wdList.length-1].T_time
+			var endTime = this.wdList[0].T_time
+			
+			this.ewApi()
+			console.log('2================1',this.wdList)
+		},
+		onUnload() {
+		    this.closeBluetoothAdapter()
+		},
+		methods: {
+			ewApi() {
+				console.log('wdList数据',this.wdList)
+				var arr = this.wdList
+				var _this = this;
+				var Arr1 = []//T1数据
+				var Arr2 = []//T2数据
+				var Arr3 = []//T3数据
+				var Arr4 = []//T4数据
+				arr.forEach(function(item,index){
+					
+					Arr1.push(item.T1)
+					Arr2.push(item.T2)
+					Arr3.push(item.T3)
+					Arr4.push(item.T4)
+				})
+				console.log('检查t1-t4',Arr1,Arr2,Arr3,Arr4)
+				if(Arr1[0]!=null){
+					var it1 = ''
+					var iArr1 = []
+					var sum = 0 //求和
+					Arr1.forEach(function(item,index){
+						var T_it = item.T_t
+						it1 = item.T_t
+						iArr1.push(it1)
+						sum = Number(sum) + Number(T_it)//求和
+					})
+					this.maxData[0].Maxwen = Math.max(...iArr1)
+					this.maxData[0].Minwen = Math.min(...iArr1)
+					console.log('====================================888')
+					this.maxData[0].pingjue = parseFloat(sum/arr.length).toFixed(1)//求平均值
+					console.log('====================================999')
+					console.log('iArr1',iArr1,this.maxData)
+				}
+				if(Arr2[0]!=null){
+					console.log('进入iArr2')
+					var it1 = ''
+					var iArr1 = []
+					var sum = 0 //求和
+					Arr2.forEach(function(item,index){
+						console.log('循环====',item,index)
+						var T_it = item.T_t
+						it1 = item.T_t
+						iArr1.push(it1)
+						sum = Number(sum) + Number(T_it)//求和
+					})
+					console.log('=================进入iArr288888888888')
+					this.maxData[1].Maxwen = Math.max(...iArr1)
+					this.maxData[1].Minwen = Math.min(...iArr1)
+					console.log('=================进入iArr2')
+					this.maxData[1].pingjue = parseFloat(sum/arr.length).toFixed(1)//求平均值
+					console.log('iArr2',iArr1,this.maxData)
+				}
+				if(Arr3[0]!=null){
+					var it1 = ''
+					var iArr1 = []
+					var sum = 0 //求和
+					Arr3.forEach(function(item,index){
+						var T_it = item.T_t
+						it1 = item.T_t
+						iArr1.push(it1)
+						sum = Number(sum) + Number(T_it)//求和
+					})
+					this.maxData[2].Maxwen = Math.max(...iArr1)
+					this.maxData[2].Minwen = Math.min(...iArr1)
+					this.maxData[2].pingjue = parseFloat(sum/arr.length).toFixed(1)//求平均值
+					console.log('iArr1',iArr1,this.maxData)
+				}
+				if(Arr4[0]!=null){
+					var it1 = ''
+					var iArr1 = []
+					var sum = 0 //求和
+					Arr4.forEach(function(item,index){
+						var T_it = item.T_t
+						it1 = item.T_t
+						iArr1.push(it1)
+						sum = Number(sum) + Number(T_it)//求和
+					})
+					this.maxData[3].Maxwen = Math.max(...iArr1)
+					this.maxData[3].Minwen = Math.min(...iArr1)
+					this.maxData[3].pingjue = parseFloat(sum/arr.length).toFixed(1)//求平均值
+					console.log('iArr1',iArr1,this.maxData)
+				}
+				console.log('检查最大值',this.maxData)
+			},
+			getCurrentTime() { //获取当前时间
+				var _this = this;
+				let yy = new Date().getFullYear();
+				let mm = new Date().getMonth() + 1;
+				let dd = new Date().getDate();
+				let hh = new Date().getHours();
+				let mf = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes();
+				let ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() : new Date().getSeconds();
+				if (hh < 10) {
+					hh = '0' + hh
+				}
+				if (mm < 10) {
+					mm = '0' + mm
+				}
+				if (dd < 10) {
+					dd = '0' + dd
+				}
+				_this.dangTime = yy + '-' + mm + '-' + dd + ' ' + hh + ':' + mf + ':' + ss;
+			},
+			change(status) {
+				console.log('sss', status)
+				if (status) { //搜索打印机
+					this.searchBle()
+				} else { //停止搜索
+					this.closeBluetoothAdapter()
+					this.stopFindBule()
+				}
+			},
+			//关闭蓝牙模块
+			closeBluetoothAdapter() {
+				uni.closeBluetoothAdapter({
+					success(res) {
+						uni.showToast({
+							title:'关闭蓝牙模块',
+							icon:'none'
+						})
+					}
+				})
+			},
+			destroyed: function() {
+				if (this.connId != '') {
+					uni.closeBLEConnection({
+						deviceId: this.connId,
+						success(res) {
+							console.log(res)
+						}
+					})
+				}
+			},
+			searchBle() {
+				var that = this
+				console.log("initBule")
+				uni.openBluetoothAdapter({success(res) {
+						console.log("打开 蓝牙模块",res)
+						that.onDevice()
+						uni.getBluetoothAdapterState({
+							success: function(res) {
+								console.log(res)
+								if (res.available) {
+									if (res.discovering) {
+										that.stopFindBule()
+									}
+									//搜索蓝牙
+									//开始搜寻附近的蓝牙外围设备
+									console.log("开始搜寻附近的蓝牙外围设备")
+									uni.startBluetoothDevicesDiscovery({
+										success(res) {
+											console.log(res)
+										}
+									})
+
+								} else {
+									console.log('本机蓝牙不可用')
+								}
+							},
+						})
+					},
+					fail:err=>{ //未打开 
+						uni.showToast({icon:'none',title: '查看手机蓝牙是否打开'});
+						that.checked = false
+					}
+				})
+
+
+			},
+			onDevice() {//寻找到蓝牙设备
+				var that = this
+				//监听寻找到新设备的事件
+				uni.onBluetoothDeviceFound(function(devices) {
+					console.log(JSON.stringify(devices))
+					var re = JSON.parse(JSON.stringify(devices))
+					console.log(re.devices[0].name + "  " + re.devices[0].deviceId)
+					let name = re.devices[0].name
+					if (name != "未知设备") {
+						let deviceId = re.devices[0].deviceId
+						that.devices.push({
+							name: name,
+							deviceId: deviceId,
+							services: []
+						})
+					}
+				})
+			},
+
+			stopFindBule() {//停止搜寻附近的蓝牙外围设备
+				var that = this
+				uni.stopBluetoothDevicesDiscovery({
+					success(res) {
+						console.log(res)
+						that.checked = false
+					}
+				})
+			},
+			onConn(item) {//点击连接相应蓝牙设备
+				var that = this
+				let deviceId = item.deviceId
+				uni.showLoading({
+					title:'连接中...'
+				})
+				uni.createBLEConnection({
+					deviceId: deviceId,
+					complete(res) {
+						if (res.errMsg == "createBLEConnection:ok") {
+							that.connected = item.name
+							uni.hideLoading()
+							uni.showToast({
+								title: "蓝牙连接成功",
+								icon: 'none',
+							})
+							that.onConns = false
+							that.connId = deviceId;
+							that.currDev = item
+							setTimeout(function() {
+								that.getBLEServices(deviceId)
+							}, 2000)
+						} else {
+							console.log(res)
+						}
+						//连接成功 关闭搜索
+						that.stopFindBule()
+
+					},
+				})
+
+			},
+			getBLEServices(_deviceId) {//获取蓝牙设备所有服务(service)
+				var that = this;
+				let deviceId = _deviceId
+				uni.getBLEDeviceServices({
+					// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+					deviceId: deviceId,
+					complete(res) {
+						console.log(res)
+						let serviceId = ""
+
+						for (var s = 0; s < res.services.length; s++) {
+							console.log(res.services[s].uuid)
+							let serviceId = res.services[s].uuid
+							uni.getBLEDeviceCharacteristics({
+								// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+								deviceId: deviceId,
+								// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
+								serviceId: serviceId,
+								success(res) {
+									var re = JSON.parse(JSON.stringify(res))
+									console.log('re',re)
+									for (var c = 0; c < re.characteristics.length; c++) {
+										if (re.characteristics[c].properties.write == true) {
+											let uuid = re.characteristics[c].uuid
+											console.log(' deviceId = [' + deviceId + ']  serviceId = [' + serviceId + '] characteristics=[' + uuid)
+											for (var index in that.devices) {
+												if (that.devices[index].deviceId == deviceId) {
+													that.devices[index].services.push({
+														serviceId: serviceId,
+														characteristicId: uuid
+													})
+													break
+												}
+
+											}
+											that.bool  = false
+											console.log(JSON.stringify(that.devices))
+											
+										}
+									}
+								}
+							})
+
+						}
+					},
+					fail(res) {
+						console.log(res)
+					},
+				})
+
+			},
+			senBlData(deviceId, serviceId, characteristicId, uint8Array) {
+				var that = this
+				var uint8Buf = Array.from(uint8Array);
+				function split_array(datas, size) {
+					uni.showLoading({
+						title:'打印中..',
+					})
+					var result = {};
+					var j = 0
+					if (datas.length < size) {
+						size = datas.length
+					}
+					for (var i = 0; i < datas.length; i += size) {
+						result[j] = datas.slice(i, i + size)
+						j++
+					}
+					// result[j] = datas
+					console.log('打印result',result,j)
+					that.disBoll = j
+					return result
+				}
+				var sendloop = split_array(uint8Buf, 20);
+				function realWriteData(sendloop, i) {
+					console.log('发送数据2',sendloop, i)
+					if(that.disBoll===i){
+						that.disabled = false
+						uni.hideLoading()
+						console.log('此处设置为false',that.disabled)
+					}
+					var data = sendloop[i]
+					if (typeof(data) == "undefined") {
+						return
+					}
+					var buffer = new ArrayBuffer(data.length)
+					var dataView = new DataView(buffer)
+					for (var j = 0; j < data.length; j++) {
+						dataView.setUint8(j, data[j]);
+					}
+					uni.writeBLECharacteristicValue({
+						deviceId,
+						serviceId,
+						characteristicId,
+						value: buffer,
+						success(res) {
+							realWriteData(sendloop, i + 1);
+							// if(uni.getSystemInfoSync().platform == 'ios'){
+							// 	console.log('ios系统')
+							// 	setTimeout(() =>{
+									
+							// 	},50)
+							// }else if(uni.getSystemInfoSync().platform == 'android'){
+							// 	console.log('android系统')
+							// 	realWriteData(sendloop, i + 1);
+							// }
+						},
+						fail(e) {
+							uni.showToast({
+								title:"发送数据失败",
+								icon:'error',
+								duration:'5000'
+							})
+							console.log(e)
+						}
+					})
+				}
+				var i = 0;
+				realWriteData(sendloop, i);
+				// if(uni.getSystemInfoSync().platform == 'ios'){
+				// 	console.log('ios系统')
+				// 	setTimeout(() =>{
+						
+				// 	},50)
+				// }else if(uni.getSystemInfoSync().platform == 'android'){
+				// 	console.log('android系统')
+				// 	var i = 0;
+				// 	realWriteData(sendloop, i);
+				// }
+			},
+			senBleLabel2() {
+				console.log('=======================================')
+				console.log(this.wdList)
+				//票据模式
+				if(this.onConns){
+					uni.showToast({
+						title:'打印机未连接哦',
+						icon:'none'
+					})
+				}else if(this.bool){
+					uni.showToast({
+						title:'正在读取数据...',
+						icon:'none'
+					})
+				}else{
+						uni.hideLoading()
+						this.disabled = true
+						console.log('此处设置为true',this.disabled)
+						var Arr1 = []//T1数据
+						var Arr2 = []//T2数据
+						var Arr3 = []//T3数据
+						var Arr4 = []//T4数据
+						this.wdList.forEach(function(item,index){
+							Arr1.push(item.T1)
+							Arr2.push(item.T2)
+							Arr3.push(item.T3)
+							Arr4.push(item.T4)
+						})
+						if(Arr2[0]==null && Arr3[0]==null && Arr4[0]==null){
+							console.log('等于一个探头')
+							var wenyu = this.PrinList.T_Tlower+'~'+this.PrinList.T_Tupper//温度阈值
+							var shiyu = this.PrinList.T_RHlower+'~'+this.PrinList.T_RHupper//湿度阈值
+							this.getCurrentTime()
+							let deviceId = this.currDev.deviceId;
+							let serviceId = this.currDev.services[0].serviceId;
+							let characteristicId = this.currDev.services[0].characteristicId;
+							var tm1 = this.wdList[0].T_time
+							var tm2 = this.wdList[this.wdList.length-1].T_time
+							
+							var staTime = tm1.slice(0,tm1.length-3)
+							var endTime = tm2.slice(0,tm2.length-3)
+							
+							var command = esc.jpPrinter.createNew()
+							command.init()
+							command.setText("#冷链记录单");
+							command.setPrint();
+							command.setText(`${this.Admin_name}`);
+							command.setPrint();
+							command.setText(`送货单位:${this.songhuo===null?'':this.songhuo}`);
+							command.setPrint();
+							command.setText(`收货单位:${this.shouhuo===null?'':this.shouhuo}`);
+							command.setPrint();
+							// command.setText(`打印时间:${this.dangTime}\n`);
+							command.setText(`-------------------------------`);
+							command.setPrint();
+							command.setText(`设备号:${this.PrinList.T_sn}`);
+							command.setPrint();
+							command.setText(`标识名:${this.PrinList.T_devName}`);
+							command.setPrint();
+							command.setText(`温度阈值:${wenyu}°C`);
+							command.setPrint();
+							if(this.Admin_rh===1){
+								command.setText(`湿度阈值:${shiyu}Rh`);
+								command.setPrint();
+							}
+							command.setText(`-------------------------------`);
+							command.setText("探头");
+							command.setAbsolutePrintPosition(50);
+							command.setText("|");//20
+							command.setAbsolutePrintPosition(70);
+							command.setText("最大值");//80
+							command.setAbsolutePrintPosition(150);
+							command.setText("|");//----80
+							command.setAbsolutePrintPosition(170);
+							command.setText("最小值");//80
+							command.setAbsolutePrintPosition(250);
+							command.setText("|");//20
+							command.setAbsolutePrintPosition(270);
+							command.setText("平均值");
+							command.setPrint()
+							
+							
+							command.setText("T1");
+							command.setAbsolutePrintPosition(50);
+							command.setText("|");//20
+							command.setAbsolutePrintPosition(70);
+							command.setText(`${this.maxData[0].Maxwen==''?'----':parseFloat(this.maxData[0].Maxwen).toFixed(1)}`);//80
+							command.setAbsolutePrintPosition(150);
+							command.setText("|");//----80
+							command.setAbsolutePrintPosition(170);
+							command.setText(`${this.maxData[0].Minwen==''?'----':parseFloat(this.maxData[0].Minwen).toFixed(1)}`);//80
+							command.setAbsolutePrintPosition(250);
+							command.setText("|");//20
+							command.setAbsolutePrintPosition(270);
+							command.setText(`${this.maxData[0].pingjue==''?'----':this.maxData[0].pingjue}`);
+							command.setPrint()
+							command.setText(`-------------------------------`);
+							command.setPrint()
+							let times = ''
+							let dataTM = [] //筛选出日期
+							let _this = this
+							let array = this.wdList
+							console.log('检查数据包array',array)
+							for(var i=0;i<array.length;i++){
+								if (times.substr(0, 10) != array[i].T_time.substr(0, 10)) {
+									times = array[i].T_time.substr(0, 10);
+									if (array[i].T_time.substr(0, 10) != _this.times) {
+										dataTM.push(times)
+									}
+								}
+							}
+							const set = new Set(dataTM); //[1,1,1,2,2,3,4]
+							dataTM = [...set] //去除重复的数据
+							console.log('检查数据包dataTM',dataTM)
+							for(var i=0;i<dataTM.length;i++){ 
+								command.setPrint()
+								command.setText(`日期:${dataTM[i]} (单位:°C)`)
+								command.setPrint()
+								if(Arr2[0]==null && Arr3[0]==null && Arr4[0]==null){
+									command.setText("时间");
+									command.setAbsolutePrintPosition(75);
+									command.setText("|");//20
+									command.setAbsolutePrintPosition(100);
+									command.setText("T1");//80
+									command.setAbsolutePrintPosition(160);
+									command.setText("||");//----80
+									command.setAbsolutePrintPosition(200);
+									command.setText("时间");//80
+									command.setAbsolutePrintPosition(275);
+									command.setText("|");//20
+									command.setAbsolutePrintPosition(300);
+									command.setText("T1");
+									command.setPrint()
+								}else{
+									command.setText("时间");
+									command.setAbsolutePrintPosition(75);
+									command.setText("|");//20
+									command.setAbsolutePrintPosition(100);
+									command.setText("T1");//80
+									command.setAbsolutePrintPosition(160);
+									command.setText("|");//----80
+									command.setAbsolutePrintPosition(200);
+									command.setText("T2");//80
+									command.setAbsolutePrintPosition(275);
+									command.setText("|");//20
+									command.setAbsolutePrintPosition(300);
+									command.setText("T3");
+									command.setAbsolutePrintPosition(360);
+									command.setText("T4");
+									command.setPrint()
+								}
+								// var xc_l =  Math.ceil(array.length / 2);
+								var xc_l_j = 0
+								// console.log("xc_l:",xc_l)
+								for(var j=0;j<array.length;j++){
+									console.log('dayin',array[j].T1.times1)
+									if (dataTM[i] != array[j].T_time.substr(0,10)) {
+										continue;
+									}
+									if(xc_l_j == 0 ){
+										command.setText(`${array[j].T1.times1}`);
+										command.setAbsolutePrintPosition(75);
+										command.setText("|");
+										command.setAbsolutePrintPosition(100);
+										command.setText(`${array[j].T1==null?'----':parseFloat(array[j].T1.T_t).toFixed(1)}`);
+										command.setAbsolutePrintPosition(160);
+										command.setText("||");
+										xc_l_j =1
+									}else{
+										command.setAbsolutePrintPosition(200);
+										command.setText(`${array[j].T1.times1}`);
+										command.setAbsolutePrintPosition(275);
+										command.setText("|");
+										command.setAbsolutePrintPosition(300);
+										command.setText(`${array[j].T1==null?'----':parseFloat(array[j].T1.T_t).toFixed(1)}`);
+										command.setPrint()
+										xc_l_j =0
+									}
+								}
+								// 强行换行
+								if(xc_l_j == 1){
+									command.setPrint()
+									xc_l_j =0
+								}
+							}
+							command.setText(`-------------------------------`);
+							command.setPrint()
+							command.setText(`开始时间:${staTime}`);
+							command.setPrint()
+							command.setText(`结束时间:${endTime}`);
+							command.setPrint()
+							command.setText(`-------------------------------`);
+							command.setPrint()
+							command.setText(`配送人:`);
+							command.setPrint()
+							command.setPrint()
+							command.setText(`接收人:`);
+							command.setPrint()
+							command.setPrint()
+							command.setPrint()
+							command.setPrintAndFeedRow(1)
+							this.senBlData(deviceId, serviceId, characteristicId, command.getData())
+						}else{
+							console.log('大于一个探头')
+							var wenyu = this.PrinList.T_Tlower+'~'+this.PrinList.T_Tupper//温度阈值
+							var shiyu = this.PrinList.T_RHlower+'~'+this.PrinList.T_RHupper//湿度阈值
+							var staTime = this.wdList[0].T_time
+							var endTime = this.wdList[this.wdList.length-1].T_time
+							this.getCurrentTime()
+							let deviceId = this.currDev.deviceId;
+							let serviceId = this.currDev.services[0].serviceId;
+							let characteristicId = this.currDev.services[0].characteristicId;
+							var command = esc.jpPrinter.createNew()
+							command.init()
+							command.setText(`#冷链记录单`);
+							command.setPrint();
+							command.setText(`${this.Admin_name}`);
+							command.setPrint();
+							command.setText(`送货单位:${this.songhuo===null?'':this.songhuo}`);
+							command.setPrint();
+							command.setText(`收货单位:${this.shouhuo===null?'':this.shouhuo}`);
+							command.setPrint();
+							// command.setText(`打印时间:${this.dangTime}\n`);
+							command.setText(`-------------------------------`);
+							command.setPrint();
+							command.setText(`设备号:${this.PrinList.T_sn}`);
+							command.setPrint();
+							command.setText(`标识名:${this.PrinList.T_devName}`);
+							command.setPrint();
+							command.setText(`温度阈值:${wenyu}°C`);
+							command.setPrint();
+							if(this.Admin_rh==1){
+								command.setText(`湿度阈值:${shiyu}Rh`);
+								command.setPrint();
+							}
+							command.setText(`-------------------------------`);
+							command.setPrint();
+							
+							command.setText("探头");
+							command.setAbsolutePrintPosition(50);
+							command.setText("|");//20
+							command.setAbsolutePrintPosition(70);
+							command.setText("最大值");//80
+							command.setAbsolutePrintPosition(150);
+							command.setText("|");//----80
+							command.setAbsolutePrintPosition(170);
+							command.setText("最小值");//80
+							command.setAbsolutePrintPosition(250);
+							command.setText("|");//20
+							command.setAbsolutePrintPosition(270);
+							command.setText("平均值");
+							command.setPrint()
+							
+							for(var i= 0 ;i<this.maxData.length;i++){
+								console.log('开始打印',this.maxData[i])
+								command.setText("T"+(i+1));
+								command.setAbsolutePrintPosition(50);
+								command.setText("|");//20
+								command.setAbsolutePrintPosition(70);
+								command.setText(`${this.maxData[i].Maxwen==''?'---':parseFloat(this.maxData[i].Maxwen).toFixed(1)}`);//80
+								command.setAbsolutePrintPosition(150);
+								command.setText("|");//----80
+								command.setAbsolutePrintPosition(170);
+								command.setText(`${this.maxData[i].Minwen==''?'---':parseFloat(this.maxData[i].Minwen).toFixed(1)}`);//80
+								command.setAbsolutePrintPosition(250);
+								command.setText("|");//20
+								command.setAbsolutePrintPosition(270);
+								command.setText(`${this.maxData[i].pingjue==''?'---':this.maxData[i].pingjue}`);
+								command.setPrint()
+ 							}
+							command.setText(`-------------------------------`);
+							let times = ''
+							let dataTM = [] //筛选出日期
+							let _this = this
+							let array = this.wdList//数据包this.wdList 已经在data函数反转.reverse()   最早一条在最前面  最新一条在最后面
+							for(var i=0;i<array.length;i++){
+								if (times.substr(0, 10) != array[i].T_time.substr(0, 10)) {
+									times = array[i].T_time.substr(0, 10);
+									if (array[i].T_time.substr(0, 10) != _this.times) {
+										dataTM.push(times)
+									}
+								}
+							}
+							const set = new Set(dataTM); //[1,1,1,2,2,3,4]
+							dataTM = [...set] //去除重复的数据
+							for(var i=0;i<dataTM.length;i++){ 
+								command.setPrint()
+								command.setText(`日期:${dataTM[i]} (单位:°C)\n`)
+								command.setText("时间");
+								command.setAbsolutePrintPosition(70);
+								command.setText("|");//20
+								command.setAbsolutePrintPosition(90);
+								command.setText("T1");//80
+								command.setAbsolutePrintPosition(140);
+								command.setText("|");//----80
+								command.setAbsolutePrintPosition(160);
+								command.setText("T2");//80
+								command.setAbsolutePrintPosition(210);
+								command.setText("|");//20
+								command.setAbsolutePrintPosition(230);
+								command.setText("T3");
+								command.setAbsolutePrintPosition(280);
+								command.setText("|");//20
+								command.setAbsolutePrintPosition(300);
+								command.setText("T4");
+								command.setPrint()
+								for(var j=0;j<array.length;j++){
+									if (dataTM[i] == array[j].T_time.substr(0,10)) { //根据你遍历的年月日和数据里面的日期进行对比,满足就是那个日期的数据,打印对应的数据应该就可以了
+										command.setText(`${array[j].T1.times1}`);
+										command.setAbsolutePrintPosition(70);
+										command.setText("|");//20
+										command.setAbsolutePrintPosition(90);
+										command.setText(`${array[j].T1==null?'---':parseFloat(array[j].T1.T_t).toFixed(1)}`);//80
+										command.setAbsolutePrintPosition(140);
+										command.setText("|");//----80
+										command.setAbsolutePrintPosition(160);
+										command.setText(`${array[j].T2==null?'---':parseFloat(array[j].T2.T_t).toFixed(1)}`);//80
+										command.setAbsolutePrintPosition(210);
+										command.setText("|");//20
+										command.setAbsolutePrintPosition(230);
+										command.setText(`${array[j].T3==null?'---':parseFloat(array[j].T3.T_t).toFixed(1)}`);
+										command.setAbsolutePrintPosition(280);
+										command.setText("|");//20
+										command.setAbsolutePrintPosition(300);
+										command.setText(`${array[j].T4==null?'---':parseFloat(array[j].T4.T_t).toFixed(1)}`);
+										command.setPrint()
+									}
+								}
+							}
+							command.setText(`-------------------------------\n`);
+							command.setText(`开始时间:${staTime}\n`);
+							command.setText(`结束时间:${endTime}\n`);
+							command.setText(`-------------------------------\n`);
+							command.setText(`配送人:\n`);
+							command.setText(`\n`);
+							command.setText(`接收人:\n`);
+							command.setText(`\n`);
+							command.setText(`\n`);
+							command.setPrintAndFeedRow(1)
+							this.senBlData(deviceId, serviceId, characteristicId, command.getData())
+						}
+				}
+			},
+		}
+	}
+</script>
+<style lang="scss">
+	.bluetooths {
+		padding: 10px 20px;
+
+		.bluetooths_btn {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding: 15px 0;
+			background: #fff;
+			border-bottom: 1px solid #F1F1F1;
+
+			.bluetooths_txt1 {
+				font-size: 30rpx;
+				font-weight: bold;
+				color: #007AFF;
+			}
+		}
+
+		.blueBTn {
+			font-size: 25rpx;
+			flex: 1;
+		}
+	}
+
+	.text-red {
+		margin-top: 20px;
+		color: #7c7a7b;
+		font-size: 30rpx;
+	}
+</style>

+ 423 - 0
pages/mine/index.vue

@@ -0,0 +1,423 @@
+<template>
+	<!-- 我的 -->
+	<view class="safeDistance">
+		<view style="padding: 20rpx;">
+			<view class="card_mine">
+				<view class="card_avatar" v-if="token != ''">
+					<image class="mine_image" src="../../static/portrait.png" mode=""></image>
+					<view class="card_user_title">
+						<view class="mine_phone">{{userInfo.username}}</view>
+						<view class="mine_phone">{{userInfo.nickName}}</view>
+					</view>
+				</view>
+				<view class="card_avatar" v-else @click="login">
+					<image class="mine_image" src="../../static/portrait.png" mode=""></image>
+					<view class="mine_title">登录/注册</view>
+				</view>
+			</view>
+			<view class="order_card">
+				<view class="order_head">
+					<view class="head_left">我的运单</view>
+					<!-- <view class="head_right">
+					<view class="head_left">全部</view>
+					<u-icon name="arrow-right"></u-icon>
+				</view> -->
+				</view>
+				<view class="card_order">
+					<view class="item_order" v-for="(item,index) in tableList" :key="index" @click="goOrder(index)">
+						<view class="card_tab_image center_in"
+							:style="{background: item.bgColor,backgroundColor:item.colorBg}">
+							<span class="iconfont icon_image" :class="item.icon"></span>
+						</view>
+						<view class="order_title">{{item.title}}</view>
+					</view>
+				</view>
+			</view>
+			<view class="card_system">
+				<view class="space_between" @click="changePassword">
+					<view style="display: flex;align-items: center;">
+						<view class="card_edit_icon center_in">
+							<u-icon name="setting" size="22" color="#ffffff"></u-icon>
+						</view>
+						<view class="option_title">修改密码</view>
+					</view>
+					<u-icon name="arrow-right" size="20"></u-icon>
+				</view>
+			</view>
+			<view class="card_system">
+				<view class="space_between" @click="logOut">
+					<view style="display: flex;align-items: center;">
+						<view class="card_icon_set center_in">
+							<span class="iconfont icon-tuichudenglu"></span>
+						</view>
+						<view class="option_title">退出登录</view>
+					</view>
+					<u-icon name="arrow-right" size="20"></u-icon>
+				</view>
+			</view>
+			<!-- <view class="card_system">
+				<view class="space_between" @click="handleCheckVersion">
+					<view style="display: flex;align-items: center;">
+						<view class="card_icon_set center_in violet">
+							<u-icon name="info" size="20" color="#ffffff"></u-icon>
+						</view>
+						<view class="option_title">版本更新</view>
+					</view>
+					<u-icon name="arrow-right" size="20"></u-icon>
+				</view>
+			</view> -->
+			<!-- <view class="card_system">
+			<view class="space_between" @click="bluetoothPrinting">
+				<view style="display: flex;align-items: center;">
+					<view class="card_edit_icon center_in">
+						<u-icon name="setting" size="22" color="#ffffff"></u-icon>
+					</view>
+					<view class="option_title">蓝牙打印</view>
+				</view>
+				<u-icon name="arrow-right" size="20"></u-icon>
+			</view>
+		</view> -->
+			<u-modal :show="logoutShow" showCancelButton :title="title" :content='content' @cancel="cancel"
+				@confirm="confirm"></u-modal>
+		</view>
+	</view>
+</template>
+
+<script>
+	const ENV = require('../../.env.js')
+	export default {
+		props: {
+			token: {
+				type: String,
+				default: () => '',
+			},
+			userInfo: {
+				type: Object,
+				default: () => {},
+			},
+		},
+		data() {
+			return {
+				tableList: [],
+				list: [{
+					title: '全部',
+					icon: 'icon-dingdan',
+					colorBg: '#4bc7fc',
+					bgColor: 'linear-gradient(to right, #84d4f6, #4bc7fc)',
+				}, {
+					title: '已下单',
+					icon: 'icon-cancel',
+					colorBg: '#9ddd54',
+					bgColor: 'linear-gradient(to right, #b9f377, #9ddd54)',
+				}, {
+					title: '配送中',
+					icon: 'icon-delivery',
+					colorBg: '#4bc7fc',
+					bgColor: 'linear-gradient(to right, #84d4f6, #4bc7fc)',
+				}, {
+					title: '已送达',
+					icon: 'icon-yisongda',
+					colorBg: '#1cc723',
+					bgColor: 'linear-gradient(to right, #54ef5a, #1cc723)',
+				}, {
+					title: '已取消',
+					icon: 'icon-yiquxiao',
+					colorBg: '#fe880e',
+					bgColor: 'linear-gradient(to right, #f69f45, #fe880e)',
+				}],
+				logoutShow: false,
+				title: '确定退出?',
+				content: '退出登录后将无法查看运单,重新登录后即可查看',
+				innerVer: null,
+				version: null,
+				isCheckVersion: false
+			}
+		},
+		created() {
+			this.tableList = this.list
+		},
+		methods: {
+			// 修改密码
+			changePassword() {
+				if (this.token) {
+					uni.navigateTo({
+						url: '/pages/mine/password'
+					});
+				} else {
+					uni.$u.toast('请先登录')
+				}
+			},
+			// 登录注册
+			login() {
+				uni.navigateTo({
+					url: '/pages/login'
+				});
+			},
+			// 订单页面
+			goOrder(index) {
+				if (this.token) {
+					uni.navigateTo({
+						url: '/pages/order/index?current=' + index
+					});
+				} else {
+					uni.$u.toast('请先登录')
+				}
+			},
+			// 退出登录
+			logOut() {
+				if (this.token) {
+					this.logoutShow = true
+				} else {
+					uni.$u.toast('请先登录')
+				}
+			},
+			// 版本更新
+			versionUpdating() {
+				// https: //coldlogistics.coldbaozhida.com/smartAppversion.json
+			},
+			handleCheckVersion() {
+				let _this = this;
+				// #ifdef APP-PLUS 
+				_this.isCheckVersion = true;
+				plus.screen.lockOrientation('portrait-primary') // 竖屏锁定
+				plus.runtime.getProperty(plus.runtime.appid, (widgetInfo) => {
+					_this.innerVer = widgetInfo.version;
+					_this.version = widgetInfo.versionCode;
+					uni.request({
+						url: ENV.APP_LINK_URL + '/smartAppversion.json?_t=' + new Date()
+							.getTime(), //版本检测
+						method: 'GET',
+						header: {},
+						success: (result) => {
+							console.log(result.data.code, 14)
+							if (result.data.code === 0) {
+								setTimeout(() => {
+									_this.isCheckVersion = false;
+								}, 100);
+								if (result.data.version - _this.version > 0) { // 如果最新版本大于现在已经安装的App的版本
+									uni.showModal({
+										title: "更新提示",
+										content: "发现新版本,请确认下载更新?",
+										success: (res) => {
+											if (res.confirm) {
+												uni.showLoading({
+													title: '下载更新包中...'
+												});
+												uni.downloadFile({
+													url: result.data.url,
+													success: (downloadResult) => {
+														uni.hideLoading();
+														if (downloadResult
+															.statusCode === 200
+														) {
+															plus.runtime
+																.install(
+																	downloadResult
+																	.tempFilePath, {
+																		force: true
+																	},
+																	function() {
+																		console
+																			.log(
+																				'App安装成功!'
+																			);
+																		uni.showModal({
+																			title: 'App安装成功!',
+																			showCancel: false
+																		})
+																		plus.runtime
+																			.restart();
+																	},
+																	function(
+																		e) {
+																		console
+																			.log(
+																				'App安装失败!'
+																			);
+																	})
+														}
+													}
+												});
+											}
+										}
+									})
+								} else {
+									uni.showModal({
+										title: '当前已是最新版本',
+										showCancel: false
+									})
+								}
+							}
+						}
+					})
+				})
+				// #endif
+			},
+			// 蓝牙打印
+			bluetoothPrinting() {
+				uni.navigateTo({
+					url: '/pages/mine/bluetooth'
+				});
+			},
+			// 确定退出登录
+			confirm() {
+				this.$cache.removeToken()
+				this.$cache.removeCache('userInfo')
+				uni.redirectTo({
+					url: '/pages/login'
+				})
+			},
+			// 取消
+			cancel() {
+				this.logoutShow = false
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.card_user_title {
+		display: flex;
+		flex-direction: column;
+		align-items: flex-start;
+	}
+
+	.card_mine {
+		margin-top: 30rpx;
+		padding: 0rpx 30rpx;
+		background-color: #fff;
+		border-radius: 20rpx;
+	}
+
+	.mine_image {
+		width: 100rpx;
+		height: 100rpx;
+		border-radius: 50%;
+	}
+
+	.mine_phone {
+		margin-left: 20rpx;
+		font-size: 30rpx;
+	}
+
+	.mine_title {
+		margin-left: 20rpx;
+		font-size: 40rpx;
+		font-weight: bold;
+	}
+
+	.card_avatar {
+		display: flex;
+		align-items: center;
+		padding-top: 30rpx;
+		padding-bottom: 30rpx;
+	}
+
+	.order_card {
+		margin-top: 20rpx;
+		background-color: #fff;
+		padding: 20rpx;
+		border-radius: 20rpx;
+	}
+
+	.order_head {
+		position: relative;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding-bottom: 20rpx;
+	}
+
+	.order_head:before {
+		content: " ";
+		position: absolute;
+		left: 0;
+		bottom: 0;
+		width: 100%;
+		height: 1px;
+		border-top: 1px solid #e7e6e4;
+		-webkit-transform-origin: 0 0;
+		transform-origin: 0 0;
+		-webkit-transform: scaleY(0.5);
+		transform: scaleY(0.5);
+	}
+
+	.head_left {
+		margin-right: 10rpx;
+	}
+
+	.head_right {
+		display: flex;
+		align-items: center;
+	}
+
+	.card_order {
+		margin-top: 30rpx;
+		display: flex;
+		justify-content: space-around;
+	}
+
+	.item_order {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+	}
+
+	.card_tab_image {
+		width: 80rpx;
+		height: 80rpx;
+		border-radius: 50%;
+	}
+
+	.order_title {
+		margin-top: 6rpx;
+		font-size: 25rpx;
+	}
+
+	.icon_image {
+		color: #fff;
+		font-size: 37rpx;
+	}
+
+	.card_system {
+		margin-top: 20rpx;
+		background-color: #fff;
+		padding: 30rpx 20rpx;
+		border-radius: 20rpx;
+	}
+
+	.card_mine_option {
+		position: relative;
+		margin: 20rpx 0rpx;
+		padding-bottom: 30rpx;
+	}
+
+	.option_title {
+		margin-left: 10rpx;
+		font-size: 28rpx;
+		font-weight: 500;
+	}
+
+	.card_edit_icon {
+		width: 55rpx;
+		height: 55rpx;
+		border-radius: 50%;
+		background-color: #ff9900;
+	}
+
+	.card_icon_set {
+		width: 55rpx;
+		height: 55rpx;
+		border-radius: 50%;
+		background-color: #fa3534;
+
+		span {
+			color: #fff;
+			font-size: 40rpx;
+		}
+	}
+
+	.violet {
+		background-color: #fe654a !important;
+	}
+</style>

+ 131 - 0
pages/mine/password.vue

@@ -0,0 +1,131 @@
+<template>
+	<view>
+		<u-navbar title="修改密码" autoBack placeholder></u-navbar>
+		<view class="card_password">
+			<view class="item_pass">
+				<view class="pass_title">旧密码<span style="color: red;">*</span></view>
+				<u-input v-show="isText1 === true" placeholder="请填写密码" border="surround" v-model="userInfo.oldCode"
+					class="input_class_pwd" :password="true">
+					<template slot="suffix" v-if="userInfo.oldCode">
+						<u-icon name="eye-off" @click="isText1 = false" size="20"></u-icon>
+					</template>
+				</u-input>
+				<u-input v-show="isText1 === false" placeholder="请填写密码" border="surround" v-model="userInfo.oldCode"
+					class="input_class_pwd" :password="false">
+					<template slot="suffix" v-if="userInfo.oldCode">
+						<u-icon name="eye-fill" @click="isText1 = true" size="20"></u-icon>
+					</template>
+				</u-input>
+			</view>
+			<view class="item_pass">
+				<view class="pass_title">新密码<span style="color: red;">*</span></view>
+				<u-input v-show="isText2 === true" placeholder="请填写新密码" border="surround" v-model="userInfo.sedNewCode"
+					class="input_class_pwd" :password="true">
+					<template slot="suffix" v-if="userInfo.sedNewCode">
+						<u-icon name="eye-off" @click="isText2 = false" size="20"></u-icon>
+					</template>
+				</u-input>
+				<u-input v-show="isText2 === false" placeholder="请填写新密码" border="surround" v-model="userInfo.sedNewCode"
+					class="input_class_pwd" :password="false">
+					<template slot="suffix" v-if="userInfo.sedNewCode">
+						<u-icon name="eye-fill" @click="isText2 = true" size="20"></u-icon>
+					</template>
+				</u-input>
+			</view>
+			<view class="item_pass">
+				<view class="pass_title">再次输入新密码<span style="color: red;">*</span></view>
+				<u-input v-show="isText3 === true" placeholder="请再次填写新密码" border="surround" v-model="userInfo.newCode"
+					class="input_class_pwd" :password="true">
+					<template slot="suffix" v-if="userInfo.newCode">
+						<u-icon name="eye-off" @click="isText3 = false" size="20"></u-icon>
+					</template>
+				</u-input>
+				<u-input v-show="isText3 === false" placeholder="请再次填写新密码" border="surround" v-model="userInfo.newCode"
+					class="input_class_pwd" :password="false">
+					<template slot="suffix" v-if="userInfo.newCode">
+						<u-icon name="eye-fill" @click="isText3 = true" size="20"></u-icon>
+					</template>
+				</u-input>
+			</view>
+		</view>
+		<view class="card_btn">
+			<u-button type="primary" :disabled="disabled" text="提交" @click="amend"></u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				userInfo: {
+					oldCode: '',
+					sedNewCode: '',
+					newCode: ''
+				},
+				isText1: true,
+				isText2: true,
+				isText3: true,
+			}
+		},
+		computed: {
+			disabled() {
+				if (this.userInfo.oldCode == '' || this.userInfo.sedNewCode == '' || this.userInfo.newCode == '') {
+					return true;
+				} else {
+					return false;
+				}
+			},
+		},
+		methods: {
+			amend() {
+				if (this.userInfo.sedNewCode === this.userInfo.newCode) {
+					this.$api.put('/api/user/pwd/set', {
+						oldPassword: this.userInfo.oldCode,
+						newPassword: this.userInfo.newCode,
+					}).then(res => {
+						if (res.code == 200) {
+							this.$cache.removeToken()
+							this.$cache.removeCache('userInfo')
+							uni.redirectTo({
+								url: '/pages/login'
+							})
+						} else {
+							uni.showToast({
+								title: res.data.msg,
+								icon: 'none'
+							});
+						}
+					})
+				} else {
+					uni.showToast({
+						title: '输入两次密码不一样',
+						icon: 'none'
+					});
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page {
+		background-color: #fff !important;
+	}
+
+	.card_password {
+		padding: 20rpx 40rpx 40rpx 40rpx;
+	}
+
+	.item_pass {
+		margin-top: 30rpx;
+	}
+
+	.pass_title {
+		margin-bottom: 20rpx;
+	}
+
+	.card_btn {
+		margin: 40rpx;
+	}
+</style>

+ 198 - 0
pages/order/addWaybill.vue

@@ -0,0 +1,198 @@
+<template>
+	<!-- 添加运单 -->
+	<view>
+		<u-navbar :title="headline" autoBack placeholder></u-navbar>
+		<view class="title_waybill">寄件人:</view>
+		<x-form ref="sender" :list="list" :model="senderModel" :rules="rules"></x-form>
+		<view class="title_waybill">收件人:</view>
+		<x-form ref="recipients" :list="list" :model="recipientsModel" :rules="rules"></x-form>
+		<view class="title_waybill1"></view>
+		<x-form ref="goods" :list="goodsList" :model="goodsModel" :rules="goodsRules"></x-form>
+		<view style="width: 100%;height: 150rpx;"></view>
+		<view class="btn_print" @click="printWaybill">
+			<u-button type="primary" :text="headline"></u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		formRules,
+		pickupRulesil
+	} from "./waybill.js";
+	export default {
+		name: 'addWaybill',
+		data() {
+			return {
+				senderModel: {
+					name: '',
+					phone: '',
+					address: '',
+				},
+				recipientsModel: {
+					name: '',
+					phone: '',
+					address: '',
+				},
+				goodsModel: {
+					tamperProofLabel: '',
+					tamperProofLabelImg: '',
+					remark: '',
+				},
+				rules: {
+					name: {
+						required: true,
+						message: '请输入姓名',
+						trigger: ['blur', 'change']
+					},
+					phone: {
+						required: true,
+						message: '请输入电话',
+						trigger: ['blur', 'change']
+					},
+					address: {
+						required: true,
+						message: '请输入地址',
+						trigger: ['blur', 'change']
+					},
+				},
+				goodsRules: {
+					tamperProofLabel: {
+						required: true,
+						message: '请输入防拆标签码',
+						trigger: ['blur', 'change']
+					},
+					tamperProofLabelImg: {
+						required: true,
+						message: '请上传防拆标签图片',
+						trigger: ['blur', 'change']
+					},
+				},
+				list: pickupRulesil(),
+				goodsList: formRules(),
+				userInfo: {},
+				headline: '添加运单',
+				type: 1,
+			}
+		},
+		onLoad(value) {
+			this.headline = value.title
+			this.type = Number(value.type)
+			if (this.type == 2) {
+				var orderList = this.$cache.getCache('orderDetails')
+				this.senderModel.name = orderList.senderAddressName
+				this.senderModel.phone = orderList.senderAddressPhone
+				this.senderModel.address = orderList.senderAddressDetails
+				this.recipientsModel.name = orderList.consigneeAddressName
+				this.recipientsModel.phone = orderList.consigneeAddressPhone
+				this.recipientsModel.address = orderList.consigneeAddressDetails
+				this.goodsModel.tamperProofLabel = orderList.tamperProofLabel
+				this.goodsModel.tamperProofLabelImg = orderList.tamperProofLabelImg
+				this.goodsModel.remark = orderList.remark
+				this.goodsModel.id = orderList.id
+				let arrImg = orderList.tamperProofLabelImg.split(',')
+				let arrImgList = []
+				arrImg.forEach((item) => {
+					const arr = {
+						url: item,
+					}
+					arrImgList.push(arr)
+				})
+				this.$nextTick(() => {
+					this.$refs.goods.fileList1 = arrImgList
+				})
+			}
+		},
+		mounted() {
+			var userInfo = this.$cache.getCache('userInfo')
+			this.userInfo = userInfo
+		},
+		methods: {
+			// tianjia运单
+			async printWaybill() {
+				let flag = await this.$refs['sender'].validateForm();
+				let flag1 = await this.$refs['recipients'].validateForm();
+				let flag2 = await this.$refs['goods'].validateForm();
+				if (flag && flag1 && flag2) {
+					let params = {
+						senderAddressName: this.senderModel.name,
+						senderAddressPhone: this.senderModel.phone,
+						senderAddressDetails: this.senderModel.address,
+						consigneeAddressName: this.recipientsModel.name,
+						consigneeAddressPhone: this.recipientsModel.phone,
+						consigneeAddressDetails: this.recipientsModel.address,
+						...this.goodsModel,
+					}
+					console.log(params, 222)
+					uni.showLoading();
+					if (this.type == 1) {
+						// 添加订单
+						this.$api.post('/api/waybill', params).then(res => {
+							if (res.code == 200) {
+								uni.redirectTo({
+									url: '/pages/order/index'
+								});
+							} else {
+								uni.$u.toast('添加失败')
+							}
+							uni.hideLoading();
+						}).catch(() => {
+							uni.hideLoading();
+						})
+					} else {
+						// 修改订单
+						this.$api.put('/api/waybill', params).then(res => {
+							if (res.code == 200) {
+								// uni.$u.toast(res.msg)
+								uni.redirectTo({
+									url: '/pages/order/index'
+								});
+							} else {
+								uni.$u.toast('修改失败')
+							}
+							uni.hideLoading();
+						}).catch(() => {
+							uni.hideLoading();
+						})
+					}
+				} else {
+					uni.$u.toast('请先完善表单')
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	page {
+		background-color: #fff !important;
+	}
+
+	.title_waybill {
+		position: relative;
+		margin: 20rpx;
+		border-bottom: 1rpx;
+		padding-bottom: 20rpx;
+	}
+
+	.title_waybill1 {
+		position: relative;
+		margin: 20rpx;
+		border-bottom: 1rpx;
+		padding-bottom: 20rpx;
+	}
+
+	.title_waybill:before {
+		content: " ";
+		position: absolute;
+		left: 0;
+		bottom: 0;
+		width: 100%;
+		height: 1px;
+		border-top: 1px solid #e7e6e4;
+		-webkit-transform-origin: 0 0;
+		transform-origin: 0 0;
+		-webkit-transform: scaleY(0.5);
+		transform: scaleY(0.5);
+	}
+</style>

+ 492 - 0
pages/order/delivery.vue

@@ -0,0 +1,492 @@
+<template>
+	<!-- 确认送达 -->
+	<view>
+		<u-navbar :title="navTitle" autoBack placeholder @leftClick="leftClick"></u-navbar>
+		<view class="card_order_details">
+			<view style="display: flex;align-items: center;margin-bottom: 10rpx;">
+				<view class="details_title">{{getTitle(navTitle)}} <span class="line_title">*</span></view>
+				<uni-datetime-picker type="datetime" :start="start" :end="end" v-model="valueTime" />
+			</view>
+			<view class="card_search">
+				<view class="details_title">运单号 <span class="line_title">*</span></view>
+				<view class="card_input">
+					<u-input border="surround" v-model="frequencyCoding">
+						<template slot="suffix">
+							<u-icon name="scan" size="26" @click="sweep"></u-icon>
+						</template>
+					</u-input>
+				</view>
+				<view class="deleteCurrent" @click="removeWaybill(frequencyCoding)">
+					<u-icon class="icon_current" name="backspace" height="10" size="20"></u-icon>
+					<view class="title_nape">删除</view>
+				</view>
+			</view>
+			<view v-if="selectiveType != 'signfor'">
+				<view class="card_frequency">
+					<view class="card_high space_between">
+						<view class="card_frequency_title">已录入运单号</view>
+						<view class="card_bottle">已扫<span>{{list.length}}</span></view>
+					</view>
+					<view style="width: 100%;" v-if="list.length > 0">
+						<view class="item_coding" v-for="(item,index) in list" :key="index">
+							<view class="title_coding">{{item}}</view>
+							<u-icon name="close-circle-fill" color="#c0c4cc" size="20"
+								@click="removeWaybill(item)"></u-icon>
+						</view>
+					</view>
+					<view class="card_empty" v-else>
+						<u-empty mode="list"></u-empty>
+					</view>
+				</view>
+			</view>
+			<view v-else>
+				<view class="card_sign_in">
+					<view class="details_title1">运单签收图片 <span class="line_title">*</span></view>
+					<view>
+						<u-upload :fileList="fileList1" name="1" :maxCount="1" @afterRead="afterRead"
+							@delete="deletePic"></u-upload>
+					</view>
+				</view>
+				<view class="card_sign_in">
+					<view class="details_title1">随货通行单图片 <span class="line_title">*</span></view>
+					<view>
+						<u-upload :fileList="fileList2" name="2" :maxCount="1" @afterRead="afterRead"
+							@delete="deletePic"></u-upload>
+					</view>
+				</view>
+				<view class="card_sign_in">
+					<view class="details_title1">冷链交接单图片 <span class="line_title">*</span></view>
+					<view>
+						<u-upload :fileList="fileList3" name="3" :maxCount="1" @afterRead="afterRead"
+							@delete="deletePic"></u-upload>
+					</view>
+				</view>
+			</view>
+			<view style="width: 100%;height: 120rpx;"></view>
+			<view class="card_btn">
+				<u-button style="margin-bottom: 20rpx;" type="primary" :disabled="jurisdiction"
+					@click="submit">提交</u-button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	const ENV = require('@/.env.js')
+	export default {
+		data() {
+			return {
+				frequencyCoding: '',
+				navTitle: '',
+				selectiveType: '',
+				cmpCode: '',
+				list: [],
+				orderId: '',
+				valueTime: this.getDateTime(new Date()),
+				start: Date.now() - 10 * 24 * 3600000,
+				end: Date.now() + 10 * 24 * 3600000,
+				fileList1: [],
+				fileList2: [],
+				fileList3: [],
+			}
+		},
+		computed: {
+			jurisdiction() {
+				if (this.selectiveType == 'signfor') {
+					if (this.fileList1.length > 0 && this.fileList2.length > 0 && this.fileList3.length > 0) {
+						if (this.fileList1[0].message == '成功' && this.fileList2[0].message == '成功' && this.fileList3[0]
+							.message == '成功') {
+							return false
+						} else {
+							return true
+						}
+					} else {
+						return true
+					}
+				}
+			}
+		},
+		onLoad(receive) {
+			this.navTitle = receive.title
+			this.selectiveType = receive.id
+		},
+		methods: {
+			leftClick() {
+				uni.$emit('refresh');
+			},
+			submit() {
+				this.getEntering()
+				if (this.list.length > 0) {
+					if (this.valueTime) {
+						if (this.selectiveType == 'put') {
+							// 扫码入库
+							this.scanCodes('/api/waybill/warehouse-in')
+						} else if (this.selectiveType == 'out') {
+							// 扫码出库
+							this.scanCodes('/api/waybill/warehouse-out')
+						} else if (this.selectiveType == 'truck') {
+							// 扫码装车
+							this.scanCodes('/api/waybill/car-in')
+						} else if (this.selectiveType == 'unload') {
+							// 扫码下车
+							this.scanCodes('/api/waybill/car-out')
+						} else if (this.selectiveType == 'signfor') {
+							if (this.fileList1.length > 0 && this.fileList2.length > 0 && this.fileList3.length > 0) {
+								// 扫码签收
+								this.scanCodesSignFor('/api/waybill/receipt')
+							} else {
+								uni.$u.toast('请完善表单')
+							}
+						}
+					} else {
+						const title = this.getTitle(this.navTitle)
+						uni.$u.toast('请先选择' + title + '时间')
+					}
+				} else {
+					uni.$u.toast('请先录入运单号')
+				}
+			},
+			getTitle(value) {
+				const title = value.split('扫码')
+				const headline = title[1] + '时间'
+				return headline
+			},
+			// 提交扫码列表
+			scanCodes(url) {
+				uni.showLoading();
+				this.$api.post(url, {
+					startTime: this.valueTime,
+					waybillNoList: this.list
+				}).then(res => {
+					if (res.code == 200) {
+						this.frequencyCoding = ''
+						this.list = []
+						uni.$u.toast(res.msg)
+					} else {
+						uni.$u.toast(res.data.msg)
+					}
+					uni.hideLoading();
+				}).catch(() => {
+					uni.hideLoading();
+				})
+			},
+			// 提交扫码签收列表
+			scanCodesSignFor(url) {
+				const signUrl = this.fileList1[0].url + ',' + this.fileList2[0].url + ',' + this.fileList3[0].url
+				this.$api.post(url, {
+					startTime: this.valueTime,
+					waybillNo: this.frequencyCoding,
+					receiptImg: signUrl,
+				}).then(res => {
+					if (res.code == 200) {
+						this.frequencyCoding = ''
+						this.fileList1 = []
+						this.fileList2 = []
+						this.fileList3 = []
+						this.list = []
+						uni.$u.toast(res.msg)
+					} else {
+						uni.$u.toast(res.data.msg)
+					}
+				})
+			},
+			// 扫码录入
+			getEntering() {
+				if (this.frequencyCoding) {
+					this.list.push(this.frequencyCoding)
+
+					function methods1(arr) {
+						return Array.from(new Set(arr));
+					}
+					this.list = methods1(this.list)
+				}
+			},
+			// 扫一扫
+			sweep() {
+				// 允许从相机和相册扫码
+				uni.scanCode({
+					scanType: ['barCode'],
+					// scanType: ['qrCode'],
+					autoZoom: false,
+					success: (res) => {
+						console.log(res);
+						if (res.result) {
+							let url = res.result;
+							this.frequencyCoding = url
+							this.list.push(url)
+
+							function methods1(arr) {
+								return Array.from(new Set(arr));
+							}
+							this.list = methods1(this.list)
+						} else {
+							console.log('请重新扫描');
+							return false;
+						}
+					},
+					fail: (res) => {
+						console.log('未识别到二维码');
+					}
+				})
+			},
+			// 移除错误运单号
+			removeWaybill(value) {
+				if (this.frequencyCoding == value) {
+					this.frequencyCoding = ''
+				}
+				const arr = deleteElementById(this.list, value)
+				this.list = arr
+
+				function deleteElementById(arr, key) {
+					return arr.filter((item) => item !== key);
+				}
+			},
+			// 新增图片
+			async afterRead(event) {
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '成功',
+						url: result
+					}))
+					fileListLen++
+				}
+				if (event.name == '1') {
+					var arr = []
+					this.fileList1.forEach(item1 => {
+						arr.push(item1.url)
+					})
+				} else if (event.name == '2') {
+					var arr1 = []
+					this.fileList2.forEach(item2 => {
+						arr1.push(item2.url)
+					})
+				} else if (event.name == '3') {
+					var arr2 = []
+					this.fileList3.forEach(item3 => {
+						arr2.push(item3.url)
+					})
+				}
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: ENV.APP_DEV_URL + '/api/upload', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						// formData: {
+						// 	user: 'test'
+						// },
+						header: {
+							'Authorization': 'Bearer ' + uni.getStorageSync('access_token'),
+						},
+						success: (res) => {
+							let state = JSON.parse(res.data)
+							setTimeout(() => {
+								if (state.code == 200) {
+									resolve(state.data)
+								}
+							})
+						}
+					});
+				})
+			},
+			getDateTime(date, addZero = true) {
+				return `${this.getDate(date, addZero)} ${this.getTime(date, addZero)}`
+			},
+			getDate(date, addZero = true) {
+				date = new Date(date)
+				const year = date.getFullYear()
+				const month = date.getMonth() + 1
+				const day = date.getDate()
+				return `${year}-${addZero ? this.addZero(month) : month}-${addZero ? this.addZero(day) : day}`
+			},
+			getTime(date, addZero = true) {
+				date = new Date(date)
+				const hour = date.getHours()
+				const minute = date.getMinutes()
+				const second = date.getSeconds()
+				return this.hideSecond ?
+					`${addZero ? this.addZero(hour) : hour}:${addZero ? this.addZero(minute) : minute}` :
+					`${addZero ? this.addZero(hour) : hour}:${addZero ? this.addZero(minute) : minute}:${addZero ? this.addZero(second) : second}`
+			},
+			addZero(num) {
+				if (num < 10) {
+					num = `0${num}`
+				}
+				return num
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	page {
+		background-color: #fff !important;
+	}
+
+	.card_order_details {
+		margin: 10rpx 30rpx 30rpx 30rpx;
+	}
+
+	.details_title {
+		flex: none;
+		color: #333;
+		font-size: 32rpx;
+		font-weight: 500;
+		margin-bottom: 10rpx;
+		margin-right: 15rpx;
+	}
+
+	.details_title1 {
+		width: 140rpx;
+		text-align: center;
+		flex: none;
+		color: #333;
+		font-size: 32rpx;
+		font-weight: 500;
+		margin-bottom: 10rpx;
+		margin-right: 15rpx;
+	}
+
+	.card_time {
+		width: 100%;
+		border: 1rpx solid #dadbde;
+		border-radius: 10rpx;
+		padding: 6px 9px;
+		margin-left: 10rpx;
+		line-height: 48rpx;
+	}
+
+	.card_search {
+		display: flex;
+		align-items: center;
+		margin: 20rpx 0rpx;
+	}
+
+	.card_sign_in {
+		margin-top: 30rpx;
+		display: flex;
+	}
+
+	.scan_title {
+		font-size: 20rpx;
+	}
+
+	.card_sweep {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		flex: none;
+	}
+
+	.card_input {
+		width: 100%;
+		margin-left: 30rpx;
+	}
+
+	.line_title {
+		color: red;
+	}
+
+	.card_high {
+		margin-top: 30rpx;
+	}
+
+	.card_frequency {
+		display: flex;
+		flex-wrap: wrap;
+	}
+
+	.card_frequency_title {
+		font-size: 32rpx;
+	}
+
+	.card_bottle {
+		font-size: 30rpx;
+
+		span {
+			color: red;
+		}
+	}
+
+	.item_coding {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		width: 100%;
+		margin-top: 10px;
+		padding: 25rpx 0rpx;
+		border-bottom: 2rpx solid #dfdfdf;
+	}
+
+	.title_coding {
+		font-size: 30rpx;
+	}
+
+	.card_empty {
+		width: 100%;
+		margin-top: 50rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.card_btn {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		padding-left: 30rpx;
+		padding-right: 30rpx;
+		padding-top: 20rpx;
+		background-color: #fff;
+		padding-bottom: constant(safe-area-inset-bottom); //兼容 IOS<11.2
+		padding-bottom: env(safe-area-inset-bottom); //兼容 IOS>11.2
+	}
+
+	.deleteCurrent {
+		display: flex;
+		align-items: center;
+		flex-direction: column;
+		justify-content: center;
+		border: 1px solid #e5e5e5;
+		padding: 5rpx 0rpx 5rpx 5rpx;
+		margin-left: 10rpx;
+		border-radius: 8rpx;
+		height: 66rpx;
+
+		.icon_current {
+			height: 15px;
+		}
+
+		.title_nape {
+			margin: 0rpx 10rpx;
+			display: flex;
+			font-size: 20rpx;
+			width: 40rpx;
+		}
+	}
+
+	::v-deep .u-border {
+		border-width: 1px !important;
+		border-color: #e5e5e5 !important;
+		border-style: solid;
+	}
+</style>

+ 41 - 0
pages/order/gprint/code128.js

@@ -0,0 +1,41 @@
+function getCode128(val) {	
+	// 开始位为固定格式 str.charCodeAt()
+	let code128A = [0x7B, 0x41]
+	let code128B = [0x7B, 0x42]
+	let code128C = [0x7B, 0x43]
+	let ret = [];
+	let n67 = 0;
+	for (let i = 0; i < val.length; i += 2) {
+		let tmp = val.substr(i, 2);
+		if (tmp == '00') {
+			if(i==0){
+				ret.push(123);
+				ret.push(66);
+				n67 = 66;
+			}else if(n67==67){
+				ret.push(123);
+				ret.push(66);
+				n67 = 66;
+			}
+			
+			ret.push(48);
+			ret.push(48);
+		} else {
+			if(i==0){
+				ret.push(123);
+				ret.push(67);
+				n67 = 67;
+			}else if(n67==66){
+				ret.push(123);
+				ret.push(67);
+				n67 = 67;
+			}
+			ret.push(parseInt(tmp));
+		}
+	}
+	ret.unshift(ret.length) 
+	
+	return ret;
+}
+
+module.exports = getCode128;

+ 243 - 0
pages/order/gprint/commands.js

@@ -0,0 +1,243 @@
+/**
+ * 修改自https://github.com/song940/node-escpos/blob/master/commands.js
+ * ESC/POS _ (Constants)
+ */
+const encode = require('./encoding');
+var _ = {
+	LF: [0x0a],
+	FS: [0x1c],
+	FF: [0x0c],
+	GS: [0x1d],
+	DLE: [0x10],
+	EOT: [0x04],
+	NUL: [0x00],
+	ESC: [0x1b],
+	EOL: '\n',
+};
+
+/**
+ * [FEED_CONTROL_SEQUENCES Feed control sequences]
+ * @type {Object}
+ */
+_.FEED_CONTROL_SEQUENCES = {
+	CTL_LF: [0x0a], // Print and line feed
+	CTL_GLF: [0x4a, 0x00], // Print and feed paper (without spaces between lines)
+	CTL_FF: [0x0c], // Form feed
+	CTL_CR: [0x0d], // Carriage return
+	CTL_HT: [0x09], // Horizontal tab
+	CTL_VT: [0x0b], // Vertical tab
+};
+
+_.CHARACTER_SPACING = {
+	CS_DEFAULT: [0x1b, 0x20, 0x00],
+	CS_SET: [0x1b, 0x20]
+};
+
+_.LINE_SPACING = {
+	LS_DEFAULT: [0x1b, 0x32],
+	LS_SET: [0x1b, 0x33],
+	LS_ZERO: [0x1b, 0x00]
+};
+
+/**
+ * [HARDWARE Printer hardware]
+ * @type {Object}
+ */
+_.HARDWARE = {
+	HW_INIT: [0x1b, 0x40], // Clear data in buffer and reset modes
+	HW_SELECT: [0x1b, 0x3d, 0x01], // Printer select
+	HW_RESET: [0x1b, 0x3f, 0x0a, 0x00], // Reset printer hardware
+};
+
+/**
+ * [CASH_DRAWER Cash Drawer]
+ * @type {Object}
+ */
+_.CASH_DRAWER = {
+	CD_KICK_2: [0x1b, 0x70, 0x00], // Sends a pulse to pin 2 []
+	CD_KICK_5: [0x1b, 0x70, 0x01], // Sends a pulse to pin 5 []
+};
+
+/**
+ * [MARGINS Margins sizes]
+ * @type {Object}
+ */
+_.MARGINS = {
+	BOTTOM: [0x1b, 0x4f], // Fix bottom size
+	LEFT: [0x1b, 0x6c], // Fix left size
+	RIGHT: [0x1b, 0x51], // Fix right size
+};
+
+/**
+ * [PAPER Paper]
+ * @type {Object}
+ */
+_.PAPER = {
+	PAPER_FULL_CUT: [0x1d, 0x56, 0x00], // Full cut paper
+	PAPER_PART_CUT: [0x1d, 0x56, 0x01], // Partial cut paper
+	PAPER_CUT_A: [0x1d, 0x56, 0x41], // Partial cut paper
+	PAPER_CUT_B: [0x1d, 0x56, 0x42], // Partial cut paper
+};
+
+/**
+ * [TEXT_FORMAT Text format]
+ * @type {Object}
+ */
+_.TEXT_FORMAT = {
+	TXT_NORMAL: [0x1b, 0x21, 0x00], // Normal text
+	TXT_2HEIGHT: [0x1b, 0x21, 0x10], // Double height text
+	TXT_2WIDTH: [0x1b, 0x21, 0x20], // Double width text
+	TXT_4SQUARE: [0x1b, 0x21, 0x30], // Double width & height text
+
+	TXT_UNDERL_OFF: [0x1b, 0x2d, 0x00], // Underline font OFF
+	TXT_UNDERL_ON: [0x1b, 0x2d, 0x01], // Underline font 1-dot ON
+	TXT_UNDERL2_ON: [0x1b, 0x2d, 0x02], // Underline font 2-dot ON
+	TXT_BOLD_OFF: [0x1b, 0x45, 0x00], // Bold font OFF
+	TXT_BOLD_ON: [0x1b, 0x45, 0x01], // Bold font ON
+	TXT_ITALIC_OFF: [0x1b, 0x35], // Italic font ON
+	TXT_ITALIC_ON: [0x1b, 0x34], // Italic font ON
+
+	TXT_FONT_A: [0x1b, 0x4d, 0x00], // Font type A
+	TXT_FONT_B: [0x1b, 0x4d, 0x01], // Font type B
+	TXT_FONT_C: [0x1b, 0x4d, 0x02], // Font type C
+
+	TXT_ALIGN_LT: [0x1b, 0x61, 0x00], // Left justification
+	TXT_ALIGN_CT: [0x1b, 0x61, 0x01], // Centering
+	TXT_ALIGN_RT: [0x1b, 0x61, 0x02], // Right justification
+};
+
+/**
+ * [BARCODE_FORMAT Barcode format]
+ * @type {Object}
+ */
+_.BARCODE_FORMAT = {
+	BARCODE_TXT_OFF: [0x1d, 0x48, 0x00], // HRI barcode chars OFF
+	BARCODE_TXT_ABV: [0x1d, 0x48, 0x01], // HRI barcode chars above
+	BARCODE_TXT_BLW: [0x1d, 0x48, 0x02], // HRI barcode chars below
+	BARCODE_TXT_BTH: [0x1d, 0x48, 0x03], // HRI barcode chars both above and below
+
+	BARCODE_FONT_A: [0x1d, 0x66, 0x00], // Font type A for HRI barcode chars
+	BARCODE_FONT_B: [0x1d, 0x66, 0x01], // Font type B for HRI barcode chars
+
+	BARCODE_HEIGHT: function(height) { // Barcode Height [1-255]
+		return [0x1d, 0x68, height];
+	},
+	BARCODE_WIDTH: function(width) { // Barcode Width  [2-6]
+		return [0x1d, 0x77, width];
+	},
+	BARCODE_HEIGHT_DEFAULT: [0x1d, 0x68, 0x64], // Barcode height default:100
+	BARCODE_WIDTH_DEFAULT: [0x1d, 0x77, 0x01], // Barcode width default:1
+
+	BARCODE_UPC_A: [0x1d, 0x6b, 0x00], // Barcode type UPC-A
+	BARCODE_UPC_E: [0x1d, 0x6b, 0x01], // Barcode type UPC-E
+	BARCODE_EAN13: [0x1d, 0x6b, 0x02], // Barcode type EAN13
+	BARCODE_EAN8: [0x1d, 0x6b, 0x03], // Barcode type EAN8
+	BARCODE_CODE39: [0x1d, 0x6b, 0x04], // Barcode type CODE39
+	BARCODE_ITF: [0x1d, 0x6b, 0x05], // Barcode type ITF
+	BARCODE_NW7: [0x1d, 0x6b, 0x06], // Barcode type NW7
+	BARCODE_CODE93: [0x1d, 0x6b, 0x48], // Barcode type CODE93
+	BARCODE_CODE128: [0x1d, 0x6b, 0x49], // Barcode type CODE128
+};
+
+/**
+ * [QRCODE_FORMAT qrcode format二维码]
+ * @type {Object}
+ */
+_.QRCODE_FORMAT = {
+	QRCODE_SIZE: function(n) { // 设置二维码大小
+		let data = [29, 40, 107, 3, 0, 49, 67]
+		if (n > 15) {
+			n = 15
+		}
+		if (n < 1) {
+			n = 1
+		}
+		data.push(n)
+		return data;
+	},
+	QRCODE_ERROR: function(n) { // 设置纠错等级
+		/*
+		n      功能      纠错能力
+		48 选择纠错等级 L 7
+		49 选择纠错等级 M 15
+		50 选择纠错等级 Q 25
+		51 选择纠错等级 H 30
+		*/
+		let data = [29, 40, 107, 3, 0, 49, 69]
+		data.push(n)
+		return data;
+	},
+	QRCODE_DATA: function(content) { // 设置二维码内容
+		let data = [29, 40, 107]
+		var code = new encode.TextEncoder(
+			'gb18030', {
+				NONSTANDARD_allowLegacyEncoding: true
+			}).encode(content)
+		data.push(parseInt((code.length + 3) % 256))
+		data.push(parseInt((code.length + 3) / 256))
+		data.push(49)
+		data.push(80)
+		data.push(48)
+
+		for (var i = 0; i < code.length; ++i) {
+			data.push(code[i])
+		}
+		return data;
+	},
+	QRCODE_PRINT: [29, 40, 107, 3, 0, 49, 81, 48], // 打印二维码
+
+};
+/**
+ * [IMAGE_FORMAT Image format]
+ * @type {Object}
+ */
+_.IMAGE_FORMAT = {
+	S_RASTER_N: [0x1d, 0x76, 0x30, 0x00], // Set raster image normal size
+	S_RASTER_2W: [0x1d, 0x76, 0x30, 0x01], // Set raster image double width
+	S_RASTER_2H: [0x1d, 0x76, 0x30, 0x02], // Set raster image double height
+	S_RASTER_Q: [0x1d, 0x76, 0x30, 0x03], // Set raster image quadruple
+};
+
+/**
+ * [BITMAP_FORMAT description]
+ * @type {Object}
+ */
+_.BITMAP_FORMAT = {
+	BITMAP_S8: [0x1b, 0x2a, 0x00],
+	BITMAP_D8: [0x1b, 0x2a, 0x01],
+	BITMAP_S24: [0x1b, 0x2a, 0x20],
+	BITMAP_D24: [0x1b, 0x2a, 0x21]
+};
+
+/**
+ * [GSV0_FORMAT description]
+ * @type {Object}
+ */
+_.GSV0_FORMAT = {
+	GSV0_NORMAL: [0x1d, 0x76, 0x30, 0x00],
+	GSV0_DW: [0x1d, 0x76, 0x30, 0x01],
+	GSV0_DH: [0x1d, 0x76, 0x30, 0x02],
+	GSV0_DWDH: [0x1d, 0x76, 0x30, 0x03]
+};
+
+/**
+ * [BEEP description]
+ * @type {string}
+ */
+_.BEEP = [0x1b, 0x42]; // Printer Buzzer pre hex
+
+/**
+ * [COLOR description]
+ * @type {Object}
+ */
+
+_.COLOR = {
+	0: [0x1b, 0x72, 0x00], // black
+	1: [0x1b, 0x72, 0x01] // red
+};
+
+/**
+ * [exports description]
+ * @type {[type]}
+ */
+module.exports = _;

File diff suppressed because it is too large
+ 9 - 0
pages/order/gprint/encoding-indexes.js


+ 3313 - 0
pages/order/gprint/encoding.js

@@ -0,0 +1,3313 @@
+
+// This is free and unencumbered software released into the public domain.
+// See LICENSE.md for more information.
+
+/**
+ * @fileoverview Global |this| required for resolving indexes in node.
+ * @suppress {globalThis}
+ */
+(function(global) {
+  'use strict';
+
+  // If we're in node require encoding-indexes and attach it to the global.
+  if (typeof module !== "undefined" && module.exports &&
+    !global["encoding-indexes"]) {
+    global["encoding-indexes"] =
+      require("./encoding-indexes.js")["encoding-indexes"];
+  }
+
+  //
+  // Utilities
+  //
+
+  /**
+   * @param {number} a The number to test.
+   * @param {number} min The minimum value in the range, inclusive.
+   * @param {number} max The maximum value in the range, inclusive.
+   * @return {boolean} True if a >= min and a <= max.
+   */
+  function inRange(a, min, max) {
+    return min <= a && a <= max;
+  }
+
+  /**
+   * @param {!Array.<*>} array The array to check.
+   * @param {*} item The item to look for in the array.
+   * @return {boolean} True if the item appears in the array.
+   */
+  function includes(array, item) {
+    return array.indexOf(item) !== -1;
+  }
+
+  var floor = Math.floor;
+
+  /**
+   * @param {*} o
+   * @return {Object}
+   */
+  function ToDictionary(o) {
+    if (o === undefined) return {};
+    if (o === Object(o)) return o;
+    throw TypeError('Could not convert argument to dictionary');
+  }
+
+  /**
+   * @param {string} string Input string of UTF-16 code units.
+   * @return {!Array.<number>} Code points.
+   */
+  function stringToCodePoints(string) {
+    // https://heycam.github.io/webidl/#dfn-obtain-unicode
+
+    // 1. Let S be the DOMString value.
+    var s = String(string);
+
+    // 2. Let n be the length of S.
+    var n = s.length;
+
+    // 3. Initialize i to 0.
+    var i = 0;
+
+    // 4. Initialize U to be an empty sequence of Unicode characters.
+    var u = [];
+
+    // 5. While i < n:
+    while (i < n) {
+
+      // 1. Let c be the code unit in S at index i.
+      var c = s.charCodeAt(i);
+
+      // 2. Depending on the value of c:
+
+      // c < 0xD800 or c > 0xDFFF
+      if (c < 0xD800 || c > 0xDFFF) {
+        // Append to U the Unicode character with code point c.
+        u.push(c);
+      }
+
+      // 0xDC00 ≤ c ≤ 0xDFFF
+      else if (0xDC00 <= c && c <= 0xDFFF) {
+        // Append to U a U+FFFD REPLACEMENT CHARACTER.
+        u.push(0xFFFD);
+      }
+
+      // 0xD800 ≤ c ≤ 0xDBFF
+      else if (0xD800 <= c && c <= 0xDBFF) {
+        // 1. If i = n−1, then append to U a U+FFFD REPLACEMENT
+        // CHARACTER.
+        if (i === n - 1) {
+          u.push(0xFFFD);
+        }
+        // 2. Otherwise, i < n−1:
+        else {
+          // 1. Let d be the code unit in S at index i+1.
+          var d = s.charCodeAt(i + 1);
+
+          // 2. If 0xDC00 ≤ d ≤ 0xDFFF, then:
+          if (0xDC00 <= d && d <= 0xDFFF) {
+            // 1. Let a be c & 0x3FF.
+            var a = c & 0x3FF;
+
+            // 2. Let b be d & 0x3FF.
+            var b = d & 0x3FF;
+
+            // 3. Append to U the Unicode character with code point
+            // 2^16+2^10*a+b.
+            u.push(0x10000 + (a << 10) + b);
+
+            // 4. Set i to i+1.
+            i += 1;
+          }
+
+          // 3. Otherwise, d < 0xDC00 or d > 0xDFFF. Append to U a
+          // U+FFFD REPLACEMENT CHARACTER.
+          else  {
+            u.push(0xFFFD);
+          }
+        }
+      }
+
+      // 3. Set i to i+1.
+      i += 1;
+    }
+
+    // 6. Return U.
+    return u;
+  }
+
+  /**
+   * @param {!Array.<number>} code_points Array of code points.
+   * @return {string} string String of UTF-16 code units.
+   */
+  function codePointsToString(code_points) {
+    var s = '';
+    for (var i = 0; i < code_points.length; ++i) {
+      var cp = code_points[i];
+      if (cp <= 0xFFFF) {
+        s += String.fromCharCode(cp);
+      } else {
+        cp -= 0x10000;
+        s += String.fromCharCode((cp >> 10) + 0xD800,
+                                 (cp & 0x3FF) + 0xDC00);
+      }
+    }
+    return s;
+  }
+
+
+  //
+  // Implementation of Encoding specification
+  // https://encoding.spec.whatwg.org/
+  //
+
+  //
+  // 4. Terminology
+  //
+
+  /**
+   * An ASCII byte is a byte in the range 0x00 to 0x7F, inclusive.
+   * @param {number} a The number to test.
+   * @return {boolean} True if a is in the range 0x00 to 0x7F, inclusive.
+   */
+  function isASCIIByte(a) {
+    return 0x00 <= a && a <= 0x7F;
+  }
+
+  /**
+   * An ASCII code point is a code point in the range U+0000 to
+   * U+007F, inclusive.
+   */
+  var isASCIICodePoint = isASCIIByte;
+
+
+  /**
+   * End-of-stream is a special token that signifies no more tokens
+   * are in the stream.
+   * @const
+   */ var end_of_stream = -1;
+
+  /**
+   * A stream represents an ordered sequence of tokens.
+   *
+   * @constructor
+   * @param {!(Array.<number>|Uint8Array)} tokens Array of tokens that provide
+   * the stream.
+   */
+  function Stream(tokens) {
+    /** @type {!Array.<number>} */
+    this.tokens = [].slice.call(tokens);
+    // Reversed as push/pop is more efficient than shift/unshift.
+    this.tokens.reverse();
+  }
+
+  Stream.prototype = {
+    /**
+     * @return {boolean} True if end-of-stream has been hit.
+     */
+    endOfStream: function() {
+      return !this.tokens.length;
+    },
+
+    /**
+     * When a token is read from a stream, the first token in the
+     * stream must be returned and subsequently removed, and
+     * end-of-stream must be returned otherwise.
+     *
+     * @return {number} Get the next token from the stream, or
+     * end_of_stream.
+     */
+     read: function() {
+      if (!this.tokens.length)
+        return end_of_stream;
+       return this.tokens.pop();
+     },
+
+    /**
+     * When one or more tokens are prepended to a stream, those tokens
+     * must be inserted, in given order, before the first token in the
+     * stream.
+     *
+     * @param {(number|!Array.<number>)} token The token(s) to prepend to the
+     * stream.
+     */
+    prepend: function(token) {
+      if (Array.isArray(token)) {
+        var tokens = /**@type {!Array.<number>}*/(token);
+        while (tokens.length)
+          this.tokens.push(tokens.pop());
+      } else {
+        this.tokens.push(token);
+      }
+    },
+
+    /**
+     * When one or more tokens are pushed to a stream, those tokens
+     * must be inserted, in given order, after the last token in the
+     * stream.
+     *
+     * @param {(number|!Array.<number>)} token The tokens(s) to push to the
+     * stream.
+     */
+    push: function(token) {
+      if (Array.isArray(token)) {
+        var tokens = /**@type {!Array.<number>}*/(token);
+        while (tokens.length)
+          this.tokens.unshift(tokens.shift());
+      } else {
+        this.tokens.unshift(token);
+      }
+    }
+  };
+
+  //
+  // 5. Encodings
+  //
+
+  // 5.1 Encoders and decoders
+
+  /** @const */
+  var finished = -1;
+
+  /**
+   * @param {boolean} fatal If true, decoding errors raise an exception.
+   * @param {number=} opt_code_point Override the standard fallback code point.
+   * @return {number} The code point to insert on a decoding error.
+   */
+  function decoderError(fatal, opt_code_point) {
+    if (fatal)
+      throw TypeError('Decoder error');
+    return opt_code_point || 0xFFFD;
+  }
+
+  /**
+   * @param {number} code_point The code point that could not be encoded.
+   * @return {number} Always throws, no value is actually returned.
+   */
+  function encoderError(code_point) {
+    throw TypeError('The code point ' + code_point + ' could not be encoded.');
+  }
+
+  /** @interface */
+  function Decoder() {}
+  Decoder.prototype = {
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point, or |finished|.
+     */
+    handler: function(stream, bite) {}
+  };
+
+  /** @interface */
+  function Encoder() {}
+  Encoder.prototype = {
+    /**
+     * @param {Stream} stream The stream of code points being encoded.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit, or |finished|.
+     */
+    handler: function(stream, code_point) {}
+  };
+
+  // 5.2 Names and labels
+
+  // TODO: Define @typedef for Encoding: {name:string,labels:Array.<string>}
+  // https://github.com/google/closure-compiler/issues/247
+
+  /**
+   * @param {string} label The encoding label.
+   * @return {?{name:string,labels:Array.<string>}}
+   */
+  function getEncoding(label) {
+    // 1. Remove any leading and trailing ASCII whitespace from label.
+    label = String(label).trim().toLowerCase();
+
+    // 2. If label is an ASCII case-insensitive match for any of the
+    // labels listed in the table below, return the corresponding
+    // encoding, and failure otherwise.
+    if (Object.prototype.hasOwnProperty.call(label_to_encoding, label)) {
+      return label_to_encoding[label];
+    }
+    return null;
+  }
+
+  /**
+   * Encodings table: https://encoding.spec.whatwg.org/encodings.json
+   * @const
+   * @type {!Array.<{
+   *          heading: string,
+   *          encodings: Array.<{name:string,labels:Array.<string>}>
+   *        }>}
+   */
+  var encodings = [
+    {
+      "encodings": [
+        {
+          "labels": [
+            "unicode-1-1-utf-8",
+            "utf-8",
+            "utf8"
+          ],
+          "name": "UTF-8"
+        }
+      ],
+      "heading": "The Encoding"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "866",
+            "cp866",
+            "csibm866",
+            "ibm866"
+          ],
+          "name": "IBM866"
+        },
+        {
+          "labels": [
+            "csisolatin2",
+            "iso-8859-2",
+            "iso-ir-101",
+            "iso8859-2",
+            "iso88592",
+            "iso_8859-2",
+            "iso_8859-2:1987",
+            "l2",
+            "latin2"
+          ],
+          "name": "ISO-8859-2"
+        },
+        {
+          "labels": [
+            "csisolatin3",
+            "iso-8859-3",
+            "iso-ir-109",
+            "iso8859-3",
+            "iso88593",
+            "iso_8859-3",
+            "iso_8859-3:1988",
+            "l3",
+            "latin3"
+          ],
+          "name": "ISO-8859-3"
+        },
+        {
+          "labels": [
+            "csisolatin4",
+            "iso-8859-4",
+            "iso-ir-110",
+            "iso8859-4",
+            "iso88594",
+            "iso_8859-4",
+            "iso_8859-4:1988",
+            "l4",
+            "latin4"
+          ],
+          "name": "ISO-8859-4"
+        },
+        {
+          "labels": [
+            "csisolatincyrillic",
+            "cyrillic",
+            "iso-8859-5",
+            "iso-ir-144",
+            "iso8859-5",
+            "iso88595",
+            "iso_8859-5",
+            "iso_8859-5:1988"
+          ],
+          "name": "ISO-8859-5"
+        },
+        {
+          "labels": [
+            "arabic",
+            "asmo-708",
+            "csiso88596e",
+            "csiso88596i",
+            "csisolatinarabic",
+            "ecma-114",
+            "iso-8859-6",
+            "iso-8859-6-e",
+            "iso-8859-6-i",
+            "iso-ir-127",
+            "iso8859-6",
+            "iso88596",
+            "iso_8859-6",
+            "iso_8859-6:1987"
+          ],
+          "name": "ISO-8859-6"
+        },
+        {
+          "labels": [
+            "csisolatingreek",
+            "ecma-118",
+            "elot_928",
+            "greek",
+            "greek8",
+            "iso-8859-7",
+            "iso-ir-126",
+            "iso8859-7",
+            "iso88597",
+            "iso_8859-7",
+            "iso_8859-7:1987",
+            "sun_eu_greek"
+          ],
+          "name": "ISO-8859-7"
+        },
+        {
+          "labels": [
+            "csiso88598e",
+            "csisolatinhebrew",
+            "hebrew",
+            "iso-8859-8",
+            "iso-8859-8-e",
+            "iso-ir-138",
+            "iso8859-8",
+            "iso88598",
+            "iso_8859-8",
+            "iso_8859-8:1988",
+            "visual"
+          ],
+          "name": "ISO-8859-8"
+        },
+        {
+          "labels": [
+            "csiso88598i",
+            "iso-8859-8-i",
+            "logical"
+          ],
+          "name": "ISO-8859-8-I"
+        },
+        {
+          "labels": [
+            "csisolatin6",
+            "iso-8859-10",
+            "iso-ir-157",
+            "iso8859-10",
+            "iso885910",
+            "l6",
+            "latin6"
+          ],
+          "name": "ISO-8859-10"
+        },
+        {
+          "labels": [
+            "iso-8859-13",
+            "iso8859-13",
+            "iso885913"
+          ],
+          "name": "ISO-8859-13"
+        },
+        {
+          "labels": [
+            "iso-8859-14",
+            "iso8859-14",
+            "iso885914"
+          ],
+          "name": "ISO-8859-14"
+        },
+        {
+          "labels": [
+            "csisolatin9",
+            "iso-8859-15",
+            "iso8859-15",
+            "iso885915",
+            "iso_8859-15",
+            "l9"
+          ],
+          "name": "ISO-8859-15"
+        },
+        {
+          "labels": [
+            "iso-8859-16"
+          ],
+          "name": "ISO-8859-16"
+        },
+        {
+          "labels": [
+            "cskoi8r",
+            "koi",
+            "koi8",
+            "koi8-r",
+            "koi8_r"
+          ],
+          "name": "KOI8-R"
+        },
+        {
+          "labels": [
+            "koi8-ru",
+            "koi8-u"
+          ],
+          "name": "KOI8-U"
+        },
+        {
+          "labels": [
+            "csmacintosh",
+            "mac",
+            "macintosh",
+            "x-mac-roman"
+          ],
+          "name": "macintosh"
+        },
+        {
+          "labels": [
+            "dos-874",
+            "iso-8859-11",
+            "iso8859-11",
+            "iso885911",
+            "tis-620",
+            "windows-874"
+          ],
+          "name": "windows-874"
+        },
+        {
+          "labels": [
+            "cp1250",
+            "windows-1250",
+            "x-cp1250"
+          ],
+          "name": "windows-1250"
+        },
+        {
+          "labels": [
+            "cp1251",
+            "windows-1251",
+            "x-cp1251"
+          ],
+          "name": "windows-1251"
+        },
+        {
+          "labels": [
+            "ansi_x3.4-1968",
+            "ascii",
+            "cp1252",
+            "cp819",
+            "csisolatin1",
+            "ibm819",
+            "iso-8859-1",
+            "iso-ir-100",
+            "iso8859-1",
+            "iso88591",
+            "iso_8859-1",
+            "iso_8859-1:1987",
+            "l1",
+            "latin1",
+            "us-ascii",
+            "windows-1252",
+            "x-cp1252"
+          ],
+          "name": "windows-1252"
+        },
+        {
+          "labels": [
+            "cp1253",
+            "windows-1253",
+            "x-cp1253"
+          ],
+          "name": "windows-1253"
+        },
+        {
+          "labels": [
+            "cp1254",
+            "csisolatin5",
+            "iso-8859-9",
+            "iso-ir-148",
+            "iso8859-9",
+            "iso88599",
+            "iso_8859-9",
+            "iso_8859-9:1989",
+            "l5",
+            "latin5",
+            "windows-1254",
+            "x-cp1254"
+          ],
+          "name": "windows-1254"
+        },
+        {
+          "labels": [
+            "cp1255",
+            "windows-1255",
+            "x-cp1255"
+          ],
+          "name": "windows-1255"
+        },
+        {
+          "labels": [
+            "cp1256",
+            "windows-1256",
+            "x-cp1256"
+          ],
+          "name": "windows-1256"
+        },
+        {
+          "labels": [
+            "cp1257",
+            "windows-1257",
+            "x-cp1257"
+          ],
+          "name": "windows-1257"
+        },
+        {
+          "labels": [
+            "cp1258",
+            "windows-1258",
+            "x-cp1258"
+          ],
+          "name": "windows-1258"
+        },
+        {
+          "labels": [
+            "x-mac-cyrillic",
+            "x-mac-ukrainian"
+          ],
+          "name": "x-mac-cyrillic"
+        }
+      ],
+      "heading": "Legacy single-byte encodings"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "chinese",
+            "csgb2312",
+            "csiso58gb231280",
+            "gb2312",
+            "gb_2312",
+            "gb_2312-80",
+            "gbk",
+            "iso-ir-58",
+            "x-gbk"
+          ],
+          "name": "GBK"
+        },
+        {
+          "labels": [
+            "gb18030"
+          ],
+          "name": "gb18030"
+        }
+      ],
+      "heading": "Legacy multi-byte Chinese (simplified) encodings"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "big5",
+            "big5-hkscs",
+            "cn-big5",
+            "csbig5",
+            "x-x-big5"
+          ],
+          "name": "Big5"
+        }
+      ],
+      "heading": "Legacy multi-byte Chinese (traditional) encodings"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "cseucpkdfmtjapanese",
+            "euc-jp",
+            "x-euc-jp"
+          ],
+          "name": "EUC-JP"
+        },
+        {
+          "labels": [
+            "csiso2022jp",
+            "iso-2022-jp"
+          ],
+          "name": "ISO-2022-JP"
+        },
+        {
+          "labels": [
+            "csshiftjis",
+            "ms932",
+            "ms_kanji",
+            "shift-jis",
+            "shift_jis",
+            "sjis",
+            "windows-31j",
+            "x-sjis"
+          ],
+          "name": "Shift_JIS"
+        }
+      ],
+      "heading": "Legacy multi-byte Japanese encodings"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "cseuckr",
+            "csksc56011987",
+            "euc-kr",
+            "iso-ir-149",
+            "korean",
+            "ks_c_5601-1987",
+            "ks_c_5601-1989",
+            "ksc5601",
+            "ksc_5601",
+            "windows-949"
+          ],
+          "name": "EUC-KR"
+        }
+      ],
+      "heading": "Legacy multi-byte Korean encodings"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "csiso2022kr",
+            "hz-gb-2312",
+            "iso-2022-cn",
+            "iso-2022-cn-ext",
+            "iso-2022-kr"
+          ],
+          "name": "replacement"
+        },
+        {
+          "labels": [
+            "utf-16be"
+          ],
+          "name": "UTF-16BE"
+        },
+        {
+          "labels": [
+            "utf-16",
+            "utf-16le"
+          ],
+          "name": "UTF-16LE"
+        },
+        {
+          "labels": [
+            "x-user-defined"
+          ],
+          "name": "x-user-defined"
+        }
+      ],
+      "heading": "Legacy miscellaneous encodings"
+    }
+  ];
+
+  // Label to encoding registry.
+  /** @type {Object.<string,{name:string,labels:Array.<string>}>} */
+  var label_to_encoding = {};
+  encodings.forEach(function(category) {
+    category.encodings.forEach(function(encoding) {
+      encoding.labels.forEach(function(label) {
+        label_to_encoding[label] = encoding;
+      });
+    });
+  });
+
+  // Registry of of encoder/decoder factories, by encoding name.
+  /** @type {Object.<string, function({fatal:boolean}): Encoder>} */
+  var encoders = {};
+  /** @type {Object.<string, function({fatal:boolean}): Decoder>} */
+  var decoders = {};
+
+  //
+  // 6. Indexes
+  //
+
+  /**
+   * @param {number} pointer The |pointer| to search for.
+   * @param {(!Array.<?number>|undefined)} index The |index| to search within.
+   * @return {?number} The code point corresponding to |pointer| in |index|,
+   *     or null if |code point| is not in |index|.
+   */
+  function indexCodePointFor(pointer, index) {
+    if (!index) return null;
+    return index[pointer] || null;
+  }
+
+  /**
+   * @param {number} code_point The |code point| to search for.
+   * @param {!Array.<?number>} index The |index| to search within.
+   * @return {?number} The first pointer corresponding to |code point| in
+   *     |index|, or null if |code point| is not in |index|.
+   */
+  function indexPointerFor(code_point, index) {
+    var pointer = index.indexOf(code_point);
+    return pointer === -1 ? null : pointer;
+  }
+
+  /**
+   * @param {string} name Name of the index.
+   * @return {(!Array.<number>|!Array.<Array.<number>>)}
+   *  */
+  function index(name) {
+    if (!('encoding-indexes' in global)) {
+      throw Error("Indexes missing." +
+                  " Did you forget to include encoding-indexes.js first?");
+    }
+    return global['encoding-indexes'][name];
+  }
+
+  /**
+   * @param {number} pointer The |pointer| to search for in the gb18030 index.
+   * @return {?number} The code point corresponding to |pointer| in |index|,
+   *     or null if |code point| is not in the gb18030 index.
+   */
+  function indexGB18030RangesCodePointFor(pointer) {
+    // 1. If pointer is greater than 39419 and less than 189000, or
+    // pointer is greater than 1237575, return null.
+    if ((pointer > 39419 && pointer < 189000) || (pointer > 1237575))
+      return null;
+
+    // 2. If pointer is 7457, return code point U+E7C7.
+    if (pointer === 7457) return 0xE7C7;
+
+    // 3. Let offset be the last pointer in index gb18030 ranges that
+    // is equal to or less than pointer and let code point offset be
+    // its corresponding code point.
+    var offset = 0;
+    var code_point_offset = 0;
+    var idx = index('gb18030-ranges');
+    var i;
+    for (i = 0; i < idx.length; ++i) {
+      /** @type {!Array.<number>} */
+      var entry = idx[i];
+      if (entry[0] <= pointer) {
+        offset = entry[0];
+        code_point_offset = entry[1];
+      } else {
+        break;
+      }
+    }
+
+    // 4. Return a code point whose value is code point offset +
+    // pointer − offset.
+    return code_point_offset + pointer - offset;
+  }
+
+  /**
+   * @param {number} code_point The |code point| to locate in the gb18030 index.
+   * @return {number} The first pointer corresponding to |code point| in the
+   *     gb18030 index.
+   */
+  function indexGB18030RangesPointerFor(code_point) {
+    // 1. If code point is U+E7C7, return pointer 7457.
+    if (code_point === 0xE7C7) return 7457;
+
+    // 2. Let offset be the last code point in index gb18030 ranges
+    // that is equal to or less than code point and let pointer offset
+    // be its corresponding pointer.
+    var offset = 0;
+    var pointer_offset = 0;
+    var idx = index('gb18030-ranges');
+    var i;
+    for (i = 0; i < idx.length; ++i) {
+      /** @type {!Array.<number>} */
+      var entry = idx[i];
+      if (entry[1] <= code_point) {
+        offset = entry[1];
+        pointer_offset = entry[0];
+      } else {
+        break;
+      }
+    }
+
+    // 3. Return a pointer whose value is pointer offset + code point
+    // − offset.
+    return pointer_offset + code_point - offset;
+  }
+
+  /**
+   * @param {number} code_point The |code_point| to search for in the Shift_JIS
+   *     index.
+   * @return {?number} The code point corresponding to |pointer| in |index|,
+   *     or null if |code point| is not in the Shift_JIS index.
+   */
+  function indexShiftJISPointerFor(code_point) {
+    // 1. Let index be index jis0208 excluding all entries whose
+    // pointer is in the range 8272 to 8835, inclusive.
+    shift_jis_index = shift_jis_index ||
+      index('jis0208').map(function(code_point, pointer) {
+        return inRange(pointer, 8272, 8835) ? null : code_point;
+      });
+    var index_ = shift_jis_index;
+
+    // 2. Return the index pointer for code point in index.
+    return index_.indexOf(code_point);
+  }
+  var shift_jis_index;
+
+  /**
+   * @param {number} code_point The |code_point| to search for in the big5
+   *     index.
+   * @return {?number} The code point corresponding to |pointer| in |index|,
+   *     or null if |code point| is not in the big5 index.
+   */
+  function indexBig5PointerFor(code_point) {
+    // 1. Let index be index Big5 excluding all entries whose pointer
+    big5_index_no_hkscs = big5_index_no_hkscs ||
+      index('big5').map(function(code_point, pointer) {
+        return (pointer < (0xA1 - 0x81) * 157) ? null : code_point;
+      });
+    var index_ = big5_index_no_hkscs;
+
+    // 2. If code point is U+2550, U+255E, U+2561, U+256A, U+5341, or
+    // U+5345, return the last pointer corresponding to code point in
+    // index.
+    if (code_point === 0x2550 || code_point === 0x255E ||
+        code_point === 0x2561 || code_point === 0x256A ||
+        code_point === 0x5341 || code_point === 0x5345) {
+      return index_.lastIndexOf(code_point);
+    }
+
+    // 3. Return the index pointer for code point in index.
+    return indexPointerFor(code_point, index_);
+  }
+  var big5_index_no_hkscs;
+
+  //
+  // 8. API
+  //
+
+  /** @const */ var DEFAULT_ENCODING = 'utf-8';
+
+  // 8.1 Interface TextDecoder
+
+  /**
+   * @constructor
+   * @param {string=} label The label of the encoding;
+   *     defaults to 'utf-8'.
+   * @param {Object=} options
+   */
+  function TextDecoder(label, options) {
+    // Web IDL conventions
+    if (!(this instanceof TextDecoder))
+      throw TypeError('Called as a function. Did you forget \'new\'?');
+    label = label !== undefined ? String(label) : DEFAULT_ENCODING;
+    options = ToDictionary(options);
+
+    // A TextDecoder object has an associated encoding, decoder,
+    // stream, ignore BOM flag (initially unset), BOM seen flag
+    // (initially unset), error mode (initially replacement), and do
+    // not flush flag (initially unset).
+
+    /** @private */
+    this._encoding = null;
+    /** @private @type {?Decoder} */
+    this._decoder = null;
+    /** @private @type {boolean} */
+    this._ignoreBOM = false;
+    /** @private @type {boolean} */
+    this._BOMseen = false;
+    /** @private @type {string} */
+    this._error_mode = 'replacement';
+    /** @private @type {boolean} */
+    this._do_not_flush = false;
+
+
+    // 1. Let encoding be the result of getting an encoding from
+    // label.
+    var encoding = getEncoding(label);
+
+    // 2. If encoding is failure or replacement, throw a RangeError.
+    if (encoding === null || encoding.name === 'replacement')
+      throw RangeError('Unknown encoding: ' + label);
+    if (!decoders[encoding.name]) {
+      throw Error('Decoder not present.' +
+                  ' Did you forget to include encoding-indexes.js first?');
+    }
+
+    // 3. Let dec be a new TextDecoder object.
+    var dec = this;
+
+    // 4. Set dec's encoding to encoding.
+    dec._encoding = encoding;
+
+    // 5. If options's fatal member is true, set dec's error mode to
+    // fatal.
+    if (Boolean(options['fatal']))
+      dec._error_mode = 'fatal';
+
+    // 6. If options's ignoreBOM member is true, set dec's ignore BOM
+    // flag.
+    if (Boolean(options['ignoreBOM']))
+      dec._ignoreBOM = true;
+
+    // For pre-ES5 runtimes:
+    if (!Object.defineProperty) {
+      this.encoding = dec._encoding.name.toLowerCase();
+      this.fatal = dec._error_mode === 'fatal';
+      this.ignoreBOM = dec._ignoreBOM;
+    }
+
+    // 7. Return dec.
+    return dec;
+  }
+
+  if (Object.defineProperty) {
+    // The encoding attribute's getter must return encoding's name.
+    Object.defineProperty(TextDecoder.prototype, 'encoding', {
+      /** @this {TextDecoder} */
+      get: function() { return this._encoding.name.toLowerCase(); }
+    });
+
+    // The fatal attribute's getter must return true if error mode
+    // is fatal, and false otherwise.
+    Object.defineProperty(TextDecoder.prototype, 'fatal', {
+      /** @this {TextDecoder} */
+      get: function() { return this._error_mode === 'fatal'; }
+    });
+
+    // The ignoreBOM attribute's getter must return true if ignore
+    // BOM flag is set, and false otherwise.
+    Object.defineProperty(TextDecoder.prototype, 'ignoreBOM', {
+      /** @this {TextDecoder} */
+      get: function() { return this._ignoreBOM; }
+    });
+  }
+
+  /**
+   * @param {BufferSource=} input The buffer of bytes to decode.
+   * @param {Object=} options
+   * @return {string} The decoded string.
+   */
+  TextDecoder.prototype.decode = function decode(input, options) {
+    var bytes;
+    if (typeof input === 'object' && input instanceof ArrayBuffer) {
+      bytes = new Uint8Array(input);
+    } else if (typeof input === 'object' && 'buffer' in input &&
+               input.buffer instanceof ArrayBuffer) {
+      bytes = new Uint8Array(input.buffer,
+                             input.byteOffset,
+                             input.byteLength);
+    } else {
+      bytes = new Uint8Array(0);
+    }
+
+    options = ToDictionary(options);
+
+    // 1. If the do not flush flag is unset, set decoder to a new
+    // encoding's decoder, set stream to a new stream, and unset the
+    // BOM seen flag.
+    if (!this._do_not_flush) {
+      this._decoder = decoders[this._encoding.name]({
+        fatal: this._error_mode === 'fatal'});
+      this._BOMseen = false;
+    }
+
+    // 2. If options's stream is true, set the do not flush flag, and
+    // unset the do not flush flag otherwise.
+    this._do_not_flush = Boolean(options['stream']);
+
+    // 3. If input is given, push a copy of input to stream.
+    // TODO: Align with spec algorithm - maintain stream on instance.
+    var input_stream = new Stream(bytes);
+
+    // 4. Let output be a new stream.
+    var output = [];
+
+    /** @type {?(number|!Array.<number>)} */
+    var result;
+
+    // 5. While true:
+    while (true) {
+      // 1. Let token be the result of reading from stream.
+      var token = input_stream.read();
+
+      // 2. If token is end-of-stream and the do not flush flag is
+      // set, return output, serialized.
+      // TODO: Align with spec algorithm.
+      if (token === end_of_stream)
+        break;
+
+      // 3. Otherwise, run these subsubsteps:
+
+      // 1. Let result be the result of processing token for decoder,
+      // stream, output, and error mode.
+      result = this._decoder.handler(input_stream, token);
+
+      // 2. If result is finished, return output, serialized.
+      if (result === finished)
+        break;
+
+      if (result !== null) {
+        if (Array.isArray(result))
+          output.push.apply(output, /**@type {!Array.<number>}*/(result));
+        else
+          output.push(result);
+      }
+
+      // 3. Otherwise, if result is error, throw a TypeError.
+      // (Thrown in handler)
+
+      // 4. Otherwise, do nothing.
+    }
+    // TODO: Align with spec algorithm.
+    if (!this._do_not_flush) {
+      do {
+        result = this._decoder.handler(input_stream, input_stream.read());
+        if (result === finished)
+          break;
+        if (result === null)
+          continue;
+        if (Array.isArray(result))
+          output.push.apply(output, /**@type {!Array.<number>}*/(result));
+        else
+          output.push(result);
+      } while (!input_stream.endOfStream());
+      this._decoder = null;
+    }
+
+    // A TextDecoder object also has an associated serialize stream
+    // algorithm...
+    /**
+     * @param {!Array.<number>} stream
+     * @return {string}
+     * @this {TextDecoder}
+     */
+    function serializeStream(stream) {
+      // 1. Let token be the result of reading from stream.
+      // (Done in-place on array, rather than as a stream)
+
+      // 2. If encoding is UTF-8, UTF-16BE, or UTF-16LE, and ignore
+      // BOM flag and BOM seen flag are unset, run these subsubsteps:
+      if (includes(['UTF-8', 'UTF-16LE', 'UTF-16BE'], this._encoding.name) &&
+          !this._ignoreBOM && !this._BOMseen) {
+        if (stream.length > 0 && stream[0] === 0xFEFF) {
+          // 1. If token is U+FEFF, set BOM seen flag.
+          this._BOMseen = true;
+          stream.shift();
+        } else if (stream.length > 0) {
+          // 2. Otherwise, if token is not end-of-stream, set BOM seen
+          // flag and append token to stream.
+          this._BOMseen = true;
+        } else {
+          // 3. Otherwise, if token is not end-of-stream, append token
+          // to output.
+          // (no-op)
+        }
+      }
+      // 4. Otherwise, return output.
+      return codePointsToString(stream);
+    }
+
+    return serializeStream.call(this, output);
+  };
+
+  // 8.2 Interface TextEncoder
+
+  /**
+   * @constructor
+   * @param {string=} label The label of the encoding. NONSTANDARD.
+   * @param {Object=} options NONSTANDARD.
+   */
+  function TextEncoder(label, options) {
+    // Web IDL conventions
+    if (!(this instanceof TextEncoder))
+      throw TypeError('Called as a function. Did you forget \'new\'?');
+    options = ToDictionary(options);
+
+    // A TextEncoder object has an associated encoding and encoder.
+
+    /** @private */
+    this._encoding = null;
+    /** @private @type {?Encoder} */
+    this._encoder = null;
+
+    // Non-standard
+    /** @private @type {boolean} */
+    this._do_not_flush = false;
+    /** @private @type {string} */
+    this._fatal = Boolean(options['fatal']) ? 'fatal' : 'replacement';
+
+    // 1. Let enc be a new TextEncoder object.
+    var enc = this;
+
+    // 2. Set enc's encoding to UTF-8's encoder.
+    if (Boolean(options['NONSTANDARD_allowLegacyEncoding'])) {
+      // NONSTANDARD behavior.
+      label = label !== undefined ? String(label) : DEFAULT_ENCODING;
+      var encoding = getEncoding(label);
+      if (encoding === null || encoding.name === 'replacement')
+        throw RangeError('Unknown encoding: ' + label);
+      if (!encoders[encoding.name]) {
+        throw Error('Encoder not present.' +
+                    ' Did you forget to include encoding-indexes.js first?');
+      }
+      enc._encoding = encoding;
+    } else {
+      // Standard behavior.
+      enc._encoding = getEncoding('utf-8');
+
+      if (label !== undefined && 'console' in global) {
+        console.warn('TextEncoder constructor called with encoding label, '
+                     + 'which is ignored.');
+      }
+    }
+
+    // For pre-ES5 runtimes:
+    if (!Object.defineProperty)
+      this.encoding = enc._encoding.name.toLowerCase();
+
+    // 3. Return enc.
+    return enc;
+  }
+
+  if (Object.defineProperty) {
+    // The encoding attribute's getter must return encoding's name.
+    Object.defineProperty(TextEncoder.prototype, 'encoding', {
+      /** @this {TextEncoder} */
+      get: function() { return this._encoding.name.toLowerCase(); }
+    });
+  }
+
+  /**
+   * @param {string=} opt_string The string to encode.
+   * @param {Object=} options
+   * @return {!Uint8Array} Encoded bytes, as a Uint8Array.
+   */
+  TextEncoder.prototype.encode = function encode(opt_string, options) {
+    opt_string = opt_string === undefined ? '' : String(opt_string);
+    options = ToDictionary(options);
+
+    // NOTE: This option is nonstandard. None of the encodings
+    // permitted for encoding (i.e. UTF-8, UTF-16) are stateful when
+    // the input is a USVString so streaming is not necessary.
+    if (!this._do_not_flush)
+      this._encoder = encoders[this._encoding.name]({
+        fatal: this._fatal === 'fatal'});
+    this._do_not_flush = Boolean(options['stream']);
+
+    // 1. Convert input to a stream.
+    var input = new Stream(stringToCodePoints(opt_string));
+
+    // 2. Let output be a new stream
+    var output = [];
+
+    /** @type {?(number|!Array.<number>)} */
+    var result;
+    // 3. While true, run these substeps:
+    while (true) {
+      // 1. Let token be the result of reading from input.
+      var token = input.read();
+      if (token === end_of_stream)
+        break;
+      // 2. Let result be the result of processing token for encoder,
+      // input, output.
+      result = this._encoder.handler(input, token);
+      if (result === finished)
+        break;
+      if (Array.isArray(result))
+        output.push.apply(output, /**@type {!Array.<number>}*/(result));
+      else
+        output.push(result);
+    }
+    // TODO: Align with spec algorithm.
+    if (!this._do_not_flush) {
+      while (true) {
+        result = this._encoder.handler(input, input.read());
+        if (result === finished)
+          break;
+        if (Array.isArray(result))
+          output.push.apply(output, /**@type {!Array.<number>}*/(result));
+        else
+          output.push(result);
+      }
+      this._encoder = null;
+    }
+    // 3. If result is finished, convert output into a byte sequence,
+    // and then return a Uint8Array object wrapping an ArrayBuffer
+    // containing output.
+    return new Uint8Array(output);
+  };
+
+
+  //
+  // 9. The encoding
+  //
+
+  // 9.1 utf-8
+
+  // 9.1.1 utf-8 decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function UTF8Decoder(options) {
+    var fatal = options.fatal;
+
+    // utf-8's decoder's has an associated utf-8 code point, utf-8
+    // bytes seen, and utf-8 bytes needed (all initially 0), a utf-8
+    // lower boundary (initially 0x80), and a utf-8 upper boundary
+    // (initially 0xBF).
+    var /** @type {number} */ utf8_code_point = 0,
+        /** @type {number} */ utf8_bytes_seen = 0,
+        /** @type {number} */ utf8_bytes_needed = 0,
+        /** @type {number} */ utf8_lower_boundary = 0x80,
+        /** @type {number} */ utf8_upper_boundary = 0xBF;
+
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and utf-8 bytes needed is not 0,
+      // set utf-8 bytes needed to 0 and return error.
+      if (bite === end_of_stream && utf8_bytes_needed !== 0) {
+        utf8_bytes_needed = 0;
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream, return finished.
+      if (bite === end_of_stream)
+        return finished;
+
+      // 3. If utf-8 bytes needed is 0, based on byte:
+      if (utf8_bytes_needed === 0) {
+
+        // 0x00 to 0x7F
+        if (inRange(bite, 0x00, 0x7F)) {
+          // Return a code point whose value is byte.
+          return bite;
+        }
+
+        // 0xC2 to 0xDF
+        else if (inRange(bite, 0xC2, 0xDF)) {
+          // 1. Set utf-8 bytes needed to 1.
+          utf8_bytes_needed = 1;
+
+          // 2. Set UTF-8 code point to byte & 0x1F.
+          utf8_code_point = bite & 0x1F;
+        }
+
+        // 0xE0 to 0xEF
+        else if (inRange(bite, 0xE0, 0xEF)) {
+          // 1. If byte is 0xE0, set utf-8 lower boundary to 0xA0.
+          if (bite === 0xE0)
+            utf8_lower_boundary = 0xA0;
+          // 2. If byte is 0xED, set utf-8 upper boundary to 0x9F.
+          if (bite === 0xED)
+            utf8_upper_boundary = 0x9F;
+          // 3. Set utf-8 bytes needed to 2.
+          utf8_bytes_needed = 2;
+          // 4. Set UTF-8 code point to byte & 0xF.
+          utf8_code_point = bite & 0xF;
+        }
+
+        // 0xF0 to 0xF4
+        else if (inRange(bite, 0xF0, 0xF4)) {
+          // 1. If byte is 0xF0, set utf-8 lower boundary to 0x90.
+          if (bite === 0xF0)
+            utf8_lower_boundary = 0x90;
+          // 2. If byte is 0xF4, set utf-8 upper boundary to 0x8F.
+          if (bite === 0xF4)
+            utf8_upper_boundary = 0x8F;
+          // 3. Set utf-8 bytes needed to 3.
+          utf8_bytes_needed = 3;
+          // 4. Set UTF-8 code point to byte & 0x7.
+          utf8_code_point = bite & 0x7;
+        }
+
+        // Otherwise
+        else {
+          // Return error.
+          return decoderError(fatal);
+        }
+
+        // Return continue.
+        return null;
+      }
+
+      // 4. If byte is not in the range utf-8 lower boundary to utf-8
+      // upper boundary, inclusive, run these substeps:
+      if (!inRange(bite, utf8_lower_boundary, utf8_upper_boundary)) {
+
+        // 1. Set utf-8 code point, utf-8 bytes needed, and utf-8
+        // bytes seen to 0, set utf-8 lower boundary to 0x80, and set
+        // utf-8 upper boundary to 0xBF.
+        utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0;
+        utf8_lower_boundary = 0x80;
+        utf8_upper_boundary = 0xBF;
+
+        // 2. Prepend byte to stream.
+        stream.prepend(bite);
+
+        // 3. Return error.
+        return decoderError(fatal);
+      }
+
+      // 5. Set utf-8 lower boundary to 0x80 and utf-8 upper boundary
+      // to 0xBF.
+      utf8_lower_boundary = 0x80;
+      utf8_upper_boundary = 0xBF;
+
+      // 6. Set UTF-8 code point to (UTF-8 code point << 6) | (byte &
+      // 0x3F)
+      utf8_code_point = (utf8_code_point << 6) | (bite & 0x3F);
+
+      // 7. Increase utf-8 bytes seen by one.
+      utf8_bytes_seen += 1;
+
+      // 8. If utf-8 bytes seen is not equal to utf-8 bytes needed,
+      // continue.
+      if (utf8_bytes_seen !== utf8_bytes_needed)
+        return null;
+
+      // 9. Let code point be utf-8 code point.
+      var code_point = utf8_code_point;
+
+      // 10. Set utf-8 code point, utf-8 bytes needed, and utf-8 bytes
+      // seen to 0.
+      utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0;
+
+      // 11. Return a code point whose value is code point.
+      return code_point;
+    };
+  }
+
+  // 9.1.2 utf-8 encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function UTF8Encoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. Set count and offset based on the range code point is in:
+      var count, offset;
+      // U+0080 to U+07FF, inclusive:
+      if (inRange(code_point, 0x0080, 0x07FF)) {
+        // 1 and 0xC0
+        count = 1;
+        offset = 0xC0;
+      }
+      // U+0800 to U+FFFF, inclusive:
+      else if (inRange(code_point, 0x0800, 0xFFFF)) {
+        // 2 and 0xE0
+        count = 2;
+        offset = 0xE0;
+      }
+      // U+10000 to U+10FFFF, inclusive:
+      else if (inRange(code_point, 0x10000, 0x10FFFF)) {
+        // 3 and 0xF0
+        count = 3;
+        offset = 0xF0;
+      }
+
+      // 4. Let bytes be a byte sequence whose first byte is (code
+      // point >> (6 × count)) + offset.
+      var bytes = [(code_point >> (6 * count)) + offset];
+
+      // 5. Run these substeps while count is greater than 0:
+      while (count > 0) {
+
+        // 1. Set temp to code point >> (6 × (count − 1)).
+        var temp = code_point >> (6 * (count - 1));
+
+        // 2. Append to bytes 0x80 | (temp & 0x3F).
+        bytes.push(0x80 | (temp & 0x3F));
+
+        // 3. Decrease count by one.
+        count -= 1;
+      }
+
+      // 6. Return bytes bytes, in order.
+      return bytes;
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['UTF-8'] = function(options) {
+    return new UTF8Encoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['UTF-8'] = function(options) {
+    return new UTF8Decoder(options);
+  };
+
+  //
+  // 10. Legacy single-byte encodings
+  //
+
+  // 10.1 single-byte decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {!Array.<number>} index The encoding index.
+   * @param {{fatal: boolean}} options
+   */
+  function SingleByteDecoder(index, options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream, return finished.
+      if (bite === end_of_stream)
+        return finished;
+
+      // 2. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 3. Let code point be the index code point for byte − 0x80 in
+      // index single-byte.
+      var code_point = index[bite - 0x80];
+
+      // 4. If code point is null, return error.
+      if (code_point === null)
+        return decoderError(fatal);
+
+      // 5. Return a code point whose value is code point.
+      return code_point;
+    };
+  }
+
+  // 10.2 single-byte encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {!Array.<?number>} index The encoding index.
+   * @param {{fatal: boolean}} options
+   */
+  function SingleByteEncoder(index, options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. Let pointer be the index pointer for code point in index
+      // single-byte.
+      var pointer = indexPointerFor(code_point, index);
+
+      // 4. If pointer is null, return error with code point.
+      if (pointer === null)
+        encoderError(code_point);
+
+      // 5. Return a byte whose value is pointer + 0x80.
+      return pointer + 0x80;
+    };
+  }
+
+  (function() {
+    if (!('encoding-indexes' in global))
+      return;
+    encodings.forEach(function(category) {
+      if (category.heading !== 'Legacy single-byte encodings')
+        return;
+      category.encodings.forEach(function(encoding) {
+        var name = encoding.name;
+        var idx = index(name.toLowerCase());
+        /** @param {{fatal: boolean}} options */
+        decoders[name] = function(options) {
+          return new SingleByteDecoder(idx, options);
+        };
+        /** @param {{fatal: boolean}} options */
+        encoders[name] = function(options) {
+          return new SingleByteEncoder(idx, options);
+        };
+      });
+    });
+  }());
+
+  //
+  // 11. Legacy multi-byte Chinese (simplified) encodings
+  //
+
+  // 11.1 gbk
+
+  // 11.1.1 gbk decoder
+  // gbk's decoder is gb18030's decoder.
+  /** @param {{fatal: boolean}} options */
+  decoders['GBK'] = function(options) {
+    return new GB18030Decoder(options);
+  };
+
+  // 11.1.2 gbk encoder
+  // gbk's encoder is gb18030's encoder with its gbk flag set.
+  /** @param {{fatal: boolean}} options */
+  encoders['GBK'] = function(options) {
+    return new GB18030Encoder(options, true);
+  };
+
+  // 11.2 gb18030
+  // 11.2.1 gb18030 decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function GB18030Decoder(options) {
+    var fatal = options.fatal;
+    // gb18030's decoder has an associated gb18030 first, gb18030
+    // second, and gb18030 third (all initially 0x00).
+    var /** @type {number} */ gb18030_first = 0x00,
+        /** @type {number} */ gb18030_second = 0x00,
+        /** @type {number} */ gb18030_third = 0x00;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and gb18030 first, gb18030
+      // second, and gb18030 third are 0x00, return finished.
+      if (bite === end_of_stream && gb18030_first === 0x00 &&
+          gb18030_second === 0x00 && gb18030_third === 0x00) {
+        return finished;
+      }
+      // 2. If byte is end-of-stream, and gb18030 first, gb18030
+      // second, or gb18030 third is not 0x00, set gb18030 first,
+      // gb18030 second, and gb18030 third to 0x00, and return error.
+      if (bite === end_of_stream &&
+          (gb18030_first !== 0x00 || gb18030_second !== 0x00 ||
+           gb18030_third !== 0x00)) {
+        gb18030_first = 0x00;
+        gb18030_second = 0x00;
+        gb18030_third = 0x00;
+        decoderError(fatal);
+      }
+      var code_point;
+      // 3. If gb18030 third is not 0x00, run these substeps:
+      if (gb18030_third !== 0x00) {
+        // 1. Let code point be null.
+        code_point = null;
+        // 2. If byte is in the range 0x30 to 0x39, inclusive, set
+        // code point to the index gb18030 ranges code point for
+        // (((gb18030 first − 0x81) × 10 + gb18030 second − 0x30) ×
+        // 126 + gb18030 third − 0x81) × 10 + byte − 0x30.
+        if (inRange(bite, 0x30, 0x39)) {
+          code_point = indexGB18030RangesCodePointFor(
+              (((gb18030_first - 0x81) * 10 + gb18030_second - 0x30) * 126 +
+               gb18030_third - 0x81) * 10 + bite - 0x30);
+        }
+
+        // 3. Let buffer be a byte sequence consisting of gb18030
+        // second, gb18030 third, and byte, in order.
+        var buffer = [gb18030_second, gb18030_third, bite];
+
+        // 4. Set gb18030 first, gb18030 second, and gb18030 third to
+        // 0x00.
+        gb18030_first = 0x00;
+        gb18030_second = 0x00;
+        gb18030_third = 0x00;
+
+        // 5. If code point is null, prepend buffer to stream and
+        // return error.
+        if (code_point === null) {
+          stream.prepend(buffer);
+          return decoderError(fatal);
+        }
+
+        // 6. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 4. If gb18030 second is not 0x00, run these substeps:
+      if (gb18030_second !== 0x00) {
+
+        // 1. If byte is in the range 0x81 to 0xFE, inclusive, set
+        // gb18030 third to byte and return continue.
+        if (inRange(bite, 0x81, 0xFE)) {
+          gb18030_third = bite;
+          return null;
+        }
+
+        // 2. Prepend gb18030 second followed by byte to stream, set
+        // gb18030 first and gb18030 second to 0x00, and return error.
+        stream.prepend([gb18030_second, bite]);
+        gb18030_first = 0x00;
+        gb18030_second = 0x00;
+        return decoderError(fatal);
+      }
+
+      // 5. If gb18030 first is not 0x00, run these substeps:
+      if (gb18030_first !== 0x00) {
+
+        // 1. If byte is in the range 0x30 to 0x39, inclusive, set
+        // gb18030 second to byte and return continue.
+        if (inRange(bite, 0x30, 0x39)) {
+          gb18030_second = bite;
+          return null;
+        }
+
+        // 2. Let lead be gb18030 first, let pointer be null, and set
+        // gb18030 first to 0x00.
+        var lead = gb18030_first;
+        var pointer = null;
+        gb18030_first = 0x00;
+
+        // 3. Let offset be 0x40 if byte is less than 0x7F and 0x41
+        // otherwise.
+        var offset = bite < 0x7F ? 0x40 : 0x41;
+
+        // 4. If byte is in the range 0x40 to 0x7E, inclusive, or 0x80
+        // to 0xFE, inclusive, set pointer to (lead − 0x81) × 190 +
+        // (byte − offset).
+        if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFE))
+          pointer = (lead - 0x81) * 190 + (bite - offset);
+
+        // 5. Let code point be null if pointer is null and the index
+        // code point for pointer in index gb18030 otherwise.
+        code_point = pointer === null ? null :
+            indexCodePointFor(pointer, index('gb18030'));
+
+        // 6. If code point is null and byte is an ASCII byte, prepend
+        // byte to stream.
+        if (code_point === null && isASCIIByte(bite))
+          stream.prepend(bite);
+
+        // 7. If code point is null, return error.
+        if (code_point === null)
+          return decoderError(fatal);
+
+        // 8. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 6. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 7. If byte is 0x80, return code point U+20AC.
+      if (bite === 0x80)
+        return 0x20AC;
+
+      // 8. If byte is in the range 0x81 to 0xFE, inclusive, set
+      // gb18030 first to byte and return continue.
+      if (inRange(bite, 0x81, 0xFE)) {
+        gb18030_first = bite;
+        return null;
+      }
+
+      // 9. Return error.
+      return decoderError(fatal);
+    };
+  }
+
+  // 11.2.2 gb18030 encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   * @param {boolean=} gbk_flag
+   */
+  function GB18030Encoder(options, gbk_flag) {
+    var fatal = options.fatal;
+    // gb18030's decoder has an associated gbk flag (initially unset).
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. If code point is U+E5E5, return error with code point.
+      if (code_point === 0xE5E5)
+        return encoderError(code_point);
+
+      // 4. If the gbk flag is set and code point is U+20AC, return
+      // byte 0x80.
+      if (gbk_flag && code_point === 0x20AC)
+        return 0x80;
+
+      // 5. Let pointer be the index pointer for code point in index
+      // gb18030.
+      var pointer = indexPointerFor(code_point, index('gb18030'));
+
+      // 6. If pointer is not null, run these substeps:
+      if (pointer !== null) {
+
+        // 1. Let lead be floor(pointer / 190) + 0x81.
+        var lead = floor(pointer / 190) + 0x81;
+
+        // 2. Let trail be pointer % 190.
+        var trail = pointer % 190;
+
+        // 3. Let offset be 0x40 if trail is less than 0x3F and 0x41 otherwise.
+        var offset = trail < 0x3F ? 0x40 : 0x41;
+
+        // 4. Return two bytes whose values are lead and trail + offset.
+        return [lead, trail + offset];
+      }
+
+      // 7. If gbk flag is set, return error with code point.
+      if (gbk_flag)
+        return encoderError(code_point);
+
+      // 8. Set pointer to the index gb18030 ranges pointer for code
+      // point.
+      pointer = indexGB18030RangesPointerFor(code_point);
+
+      // 9. Let byte1 be floor(pointer / 10 / 126 / 10).
+      var byte1 = floor(pointer / 10 / 126 / 10);
+
+      // 10. Set pointer to pointer − byte1 × 10 × 126 × 10.
+      pointer = pointer - byte1 * 10 * 126 * 10;
+
+      // 11. Let byte2 be floor(pointer / 10 / 126).
+      var byte2 = floor(pointer / 10 / 126);
+
+      // 12. Set pointer to pointer − byte2 × 10 × 126.
+      pointer = pointer - byte2 * 10 * 126;
+
+      // 13. Let byte3 be floor(pointer / 10).
+      var byte3 = floor(pointer / 10);
+
+      // 14. Let byte4 be pointer − byte3 × 10.
+      var byte4 = pointer - byte3 * 10;
+
+      // 15. Return four bytes whose values are byte1 + 0x81, byte2 +
+      // 0x30, byte3 + 0x81, byte4 + 0x30.
+      return [byte1 + 0x81,
+              byte2 + 0x30,
+              byte3 + 0x81,
+              byte4 + 0x30];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['gb18030'] = function(options) {
+    return new GB18030Encoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['gb18030'] = function(options) {
+    return new GB18030Decoder(options);
+  };
+
+
+  //
+  // 12. Legacy multi-byte Chinese (traditional) encodings
+  //
+
+  // 12.1 Big5
+
+  // 12.1.1 Big5 decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function Big5Decoder(options) {
+    var fatal = options.fatal;
+    // Big5's decoder has an associated Big5 lead (initially 0x00).
+    var /** @type {number} */ Big5_lead = 0x00;
+
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and Big5 lead is not 0x00, set
+      // Big5 lead to 0x00 and return error.
+      if (bite === end_of_stream && Big5_lead !== 0x00) {
+        Big5_lead = 0x00;
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream and Big5 lead is 0x00, return
+      // finished.
+      if (bite === end_of_stream && Big5_lead === 0x00)
+        return finished;
+
+      // 3. If Big5 lead is not 0x00, let lead be Big5 lead, let
+      // pointer be null, set Big5 lead to 0x00, and then run these
+      // substeps:
+      if (Big5_lead !== 0x00) {
+        var lead = Big5_lead;
+        var pointer = null;
+        Big5_lead = 0x00;
+
+        // 1. Let offset be 0x40 if byte is less than 0x7F and 0x62
+        // otherwise.
+        var offset = bite < 0x7F ? 0x40 : 0x62;
+
+        // 2. If byte is in the range 0x40 to 0x7E, inclusive, or 0xA1
+        // to 0xFE, inclusive, set pointer to (lead − 0x81) × 157 +
+        // (byte − offset).
+        if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0xA1, 0xFE))
+          pointer = (lead - 0x81) * 157 + (bite - offset);
+
+        // 3. If there is a row in the table below whose first column
+        // is pointer, return the two code points listed in its second
+        // column
+        // Pointer | Code points
+        // --------+--------------
+        // 1133    | U+00CA U+0304
+        // 1135    | U+00CA U+030C
+        // 1164    | U+00EA U+0304
+        // 1166    | U+00EA U+030C
+        switch (pointer) {
+          case 1133: return [0x00CA, 0x0304];
+          case 1135: return [0x00CA, 0x030C];
+          case 1164: return [0x00EA, 0x0304];
+          case 1166: return [0x00EA, 0x030C];
+        }
+
+        // 4. Let code point be null if pointer is null and the index
+        // code point for pointer in index Big5 otherwise.
+        var code_point = (pointer === null) ? null :
+            indexCodePointFor(pointer, index('big5'));
+
+        // 5. If code point is null and byte is an ASCII byte, prepend
+        // byte to stream.
+        if (code_point === null && isASCIIByte(bite))
+          stream.prepend(bite);
+
+        // 6. If code point is null, return error.
+        if (code_point === null)
+          return decoderError(fatal);
+
+        // 7. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 4. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 5. If byte is in the range 0x81 to 0xFE, inclusive, set Big5
+      // lead to byte and return continue.
+      if (inRange(bite, 0x81, 0xFE)) {
+        Big5_lead = bite;
+        return null;
+      }
+
+      // 6. Return error.
+      return decoderError(fatal);
+    };
+  }
+
+  // 12.1.2 Big5 encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function Big5Encoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. Let pointer be the index Big5 pointer for code point.
+      var pointer = indexBig5PointerFor(code_point);
+
+      // 4. If pointer is null, return error with code point.
+      if (pointer === null)
+        return encoderError(code_point);
+
+      // 5. Let lead be floor(pointer / 157) + 0x81.
+      var lead = floor(pointer / 157) + 0x81;
+
+      // 6. If lead is less than 0xA1, return error with code point.
+      if (lead < 0xA1)
+        return encoderError(code_point);
+
+      // 7. Let trail be pointer % 157.
+      var trail = pointer % 157;
+
+      // 8. Let offset be 0x40 if trail is less than 0x3F and 0x62
+      // otherwise.
+      var offset = trail < 0x3F ? 0x40 : 0x62;
+
+      // Return two bytes whose values are lead and trail + offset.
+      return [lead, trail + offset];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['Big5'] = function(options) {
+    return new Big5Encoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['Big5'] = function(options) {
+    return new Big5Decoder(options);
+  };
+
+
+  //
+  // 13. Legacy multi-byte Japanese encodings
+  //
+
+  // 13.1 euc-jp
+
+  // 13.1.1 euc-jp decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function EUCJPDecoder(options) {
+    var fatal = options.fatal;
+
+    // euc-jp's decoder has an associated euc-jp jis0212 flag
+    // (initially unset) and euc-jp lead (initially 0x00).
+    var /** @type {boolean} */ eucjp_jis0212_flag = false,
+        /** @type {number} */ eucjp_lead = 0x00;
+
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and euc-jp lead is not 0x00, set
+      // euc-jp lead to 0x00, and return error.
+      if (bite === end_of_stream && eucjp_lead !== 0x00) {
+        eucjp_lead = 0x00;
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream and euc-jp lead is 0x00, return
+      // finished.
+      if (bite === end_of_stream && eucjp_lead === 0x00)
+        return finished;
+
+      // 3. If euc-jp lead is 0x8E and byte is in the range 0xA1 to
+      // 0xDF, inclusive, set euc-jp lead to 0x00 and return a code
+      // point whose value is 0xFF61 − 0xA1 + byte.
+      if (eucjp_lead === 0x8E && inRange(bite, 0xA1, 0xDF)) {
+        eucjp_lead = 0x00;
+        return 0xFF61 - 0xA1 + bite;
+      }
+
+      // 4. If euc-jp lead is 0x8F and byte is in the range 0xA1 to
+      // 0xFE, inclusive, set the euc-jp jis0212 flag, set euc-jp lead
+      // to byte, and return continue.
+      if (eucjp_lead === 0x8F && inRange(bite, 0xA1, 0xFE)) {
+        eucjp_jis0212_flag = true;
+        eucjp_lead = bite;
+        return null;
+      }
+
+      // 5. If euc-jp lead is not 0x00, let lead be euc-jp lead, set
+      // euc-jp lead to 0x00, and run these substeps:
+      if (eucjp_lead !== 0x00) {
+        var lead = eucjp_lead;
+        eucjp_lead = 0x00;
+
+        // 1. Let code point be null.
+        var code_point = null;
+
+        // 2. If lead and byte are both in the range 0xA1 to 0xFE,
+        // inclusive, set code point to the index code point for (lead
+        // − 0xA1) × 94 + byte − 0xA1 in index jis0208 if the euc-jp
+        // jis0212 flag is unset and in index jis0212 otherwise.
+        if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) {
+          code_point = indexCodePointFor(
+            (lead - 0xA1) * 94 + (bite - 0xA1),
+            index(!eucjp_jis0212_flag ? 'jis0208' : 'jis0212'));
+        }
+
+        // 3. Unset the euc-jp jis0212 flag.
+        eucjp_jis0212_flag = false;
+
+        // 4. If byte is not in the range 0xA1 to 0xFE, inclusive,
+        // prepend byte to stream.
+        if (!inRange(bite, 0xA1, 0xFE))
+          stream.prepend(bite);
+
+        // 5. If code point is null, return error.
+        if (code_point === null)
+          return decoderError(fatal);
+
+        // 6. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 6. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 7. If byte is 0x8E, 0x8F, or in the range 0xA1 to 0xFE,
+      // inclusive, set euc-jp lead to byte and return continue.
+      if (bite === 0x8E || bite === 0x8F || inRange(bite, 0xA1, 0xFE)) {
+        eucjp_lead = bite;
+        return null;
+      }
+
+      // 8. Return error.
+      return decoderError(fatal);
+    };
+  }
+
+  // 13.1.2 euc-jp encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function EUCJPEncoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. If code point is U+00A5, return byte 0x5C.
+      if (code_point === 0x00A5)
+        return 0x5C;
+
+      // 4. If code point is U+203E, return byte 0x7E.
+      if (code_point === 0x203E)
+        return 0x7E;
+
+      // 5. If code point is in the range U+FF61 to U+FF9F, inclusive,
+      // return two bytes whose values are 0x8E and code point −
+      // 0xFF61 + 0xA1.
+      if (inRange(code_point, 0xFF61, 0xFF9F))
+        return [0x8E, code_point - 0xFF61 + 0xA1];
+
+      // 6. If code point is U+2212, set it to U+FF0D.
+      if (code_point === 0x2212)
+        code_point = 0xFF0D;
+
+      // 7. Let pointer be the index pointer for code point in index
+      // jis0208.
+      var pointer = indexPointerFor(code_point, index('jis0208'));
+
+      // 8. If pointer is null, return error with code point.
+      if (pointer === null)
+        return encoderError(code_point);
+
+      // 9. Let lead be floor(pointer / 94) + 0xA1.
+      var lead = floor(pointer / 94) + 0xA1;
+
+      // 10. Let trail be pointer % 94 + 0xA1.
+      var trail = pointer % 94 + 0xA1;
+
+      // 11. Return two bytes whose values are lead and trail.
+      return [lead, trail];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['EUC-JP'] = function(options) {
+    return new EUCJPEncoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['EUC-JP'] = function(options) {
+    return new EUCJPDecoder(options);
+  };
+
+  // 13.2 iso-2022-jp
+
+  // 13.2.1 iso-2022-jp decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function ISO2022JPDecoder(options) {
+    var fatal = options.fatal;
+    /** @enum */
+    var states = {
+      ASCII: 0,
+      Roman: 1,
+      Katakana: 2,
+      LeadByte: 3,
+      TrailByte: 4,
+      EscapeStart: 5,
+      Escape: 6
+    };
+    // iso-2022-jp's decoder has an associated iso-2022-jp decoder
+    // state (initially ASCII), iso-2022-jp decoder output state
+    // (initially ASCII), iso-2022-jp lead (initially 0x00), and
+    // iso-2022-jp output flag (initially unset).
+    var /** @type {number} */ iso2022jp_decoder_state = states.ASCII,
+        /** @type {number} */ iso2022jp_decoder_output_state = states.ASCII,
+        /** @type {number} */ iso2022jp_lead = 0x00,
+        /** @type {boolean} */ iso2022jp_output_flag = false;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // switching on iso-2022-jp decoder state:
+      switch (iso2022jp_decoder_state) {
+      default:
+      case states.ASCII:
+        // ASCII
+        // Based on byte:
+
+        // 0x1B
+        if (bite === 0x1B) {
+          // Set iso-2022-jp decoder state to escape start and return
+          // continue.
+          iso2022jp_decoder_state = states.EscapeStart;
+          return null;
+        }
+
+        // 0x00 to 0x7F, excluding 0x0E, 0x0F, and 0x1B
+        if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E
+            && bite !== 0x0F && bite !== 0x1B) {
+          // Unset the iso-2022-jp output flag and return a code point
+          // whose value is byte.
+          iso2022jp_output_flag = false;
+          return bite;
+        }
+
+        // end-of-stream
+        if (bite === end_of_stream) {
+          // Return finished.
+          return finished;
+        }
+
+        // Otherwise
+        // Unset the iso-2022-jp output flag and return error.
+        iso2022jp_output_flag = false;
+        return decoderError(fatal);
+
+      case states.Roman:
+        // Roman
+        // Based on byte:
+
+        // 0x1B
+        if (bite === 0x1B) {
+          // Set iso-2022-jp decoder state to escape start and return
+          // continue.
+          iso2022jp_decoder_state = states.EscapeStart;
+          return null;
+        }
+
+        // 0x5C
+        if (bite === 0x5C) {
+          // Unset the iso-2022-jp output flag and return code point
+          // U+00A5.
+          iso2022jp_output_flag = false;
+          return 0x00A5;
+        }
+
+        // 0x7E
+        if (bite === 0x7E) {
+          // Unset the iso-2022-jp output flag and return code point
+          // U+203E.
+          iso2022jp_output_flag = false;
+          return 0x203E;
+        }
+
+        // 0x00 to 0x7F, excluding 0x0E, 0x0F, 0x1B, 0x5C, and 0x7E
+        if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E && bite !== 0x0F
+            && bite !== 0x1B && bite !== 0x5C && bite !== 0x7E) {
+          // Unset the iso-2022-jp output flag and return a code point
+          // whose value is byte.
+          iso2022jp_output_flag = false;
+          return bite;
+        }
+
+        // end-of-stream
+        if (bite === end_of_stream) {
+          // Return finished.
+          return finished;
+        }
+
+        // Otherwise
+        // Unset the iso-2022-jp output flag and return error.
+        iso2022jp_output_flag = false;
+        return decoderError(fatal);
+
+      case states.Katakana:
+        // Katakana
+        // Based on byte:
+
+        // 0x1B
+        if (bite === 0x1B) {
+          // Set iso-2022-jp decoder state to escape start and return
+          // continue.
+          iso2022jp_decoder_state = states.EscapeStart;
+          return null;
+        }
+
+        // 0x21 to 0x5F
+        if (inRange(bite, 0x21, 0x5F)) {
+          // Unset the iso-2022-jp output flag and return a code point
+          // whose value is 0xFF61 − 0x21 + byte.
+          iso2022jp_output_flag = false;
+          return 0xFF61 - 0x21 + bite;
+        }
+
+        // end-of-stream
+        if (bite === end_of_stream) {
+          // Return finished.
+          return finished;
+        }
+
+        // Otherwise
+        // Unset the iso-2022-jp output flag and return error.
+        iso2022jp_output_flag = false;
+        return decoderError(fatal);
+
+      case states.LeadByte:
+        // Lead byte
+        // Based on byte:
+
+        // 0x1B
+        if (bite === 0x1B) {
+          // Set iso-2022-jp decoder state to escape start and return
+          // continue.
+          iso2022jp_decoder_state = states.EscapeStart;
+          return null;
+        }
+
+        // 0x21 to 0x7E
+        if (inRange(bite, 0x21, 0x7E)) {
+          // Unset the iso-2022-jp output flag, set iso-2022-jp lead
+          // to byte, iso-2022-jp decoder state to trail byte, and
+          // return continue.
+          iso2022jp_output_flag = false;
+          iso2022jp_lead = bite;
+          iso2022jp_decoder_state = states.TrailByte;
+          return null;
+        }
+
+        // end-of-stream
+        if (bite === end_of_stream) {
+          // Return finished.
+          return finished;
+        }
+
+        // Otherwise
+        // Unset the iso-2022-jp output flag and return error.
+        iso2022jp_output_flag = false;
+        return decoderError(fatal);
+
+      case states.TrailByte:
+        // Trail byte
+        // Based on byte:
+
+        // 0x1B
+        if (bite === 0x1B) {
+          // Set iso-2022-jp decoder state to escape start and return
+          // continue.
+          iso2022jp_decoder_state = states.EscapeStart;
+          return decoderError(fatal);
+        }
+
+        // 0x21 to 0x7E
+        if (inRange(bite, 0x21, 0x7E)) {
+          // 1. Set the iso-2022-jp decoder state to lead byte.
+          iso2022jp_decoder_state = states.LeadByte;
+
+          // 2. Let pointer be (iso-2022-jp lead − 0x21) × 94 + byte − 0x21.
+          var pointer = (iso2022jp_lead - 0x21) * 94 + bite - 0x21;
+
+          // 3. Let code point be the index code point for pointer in
+          // index jis0208.
+          var code_point = indexCodePointFor(pointer, index('jis0208'));
+
+          // 4. If code point is null, return error.
+          if (code_point === null)
+            return decoderError(fatal);
+
+          // 5. Return a code point whose value is code point.
+          return code_point;
+        }
+
+        // end-of-stream
+        if (bite === end_of_stream) {
+          // Set the iso-2022-jp decoder state to lead byte, prepend
+          // byte to stream, and return error.
+          iso2022jp_decoder_state = states.LeadByte;
+          stream.prepend(bite);
+          return decoderError(fatal);
+        }
+
+        // Otherwise
+        // Set iso-2022-jp decoder state to lead byte and return
+        // error.
+        iso2022jp_decoder_state = states.LeadByte;
+        return decoderError(fatal);
+
+      case states.EscapeStart:
+        // Escape start
+
+        // 1. If byte is either 0x24 or 0x28, set iso-2022-jp lead to
+        // byte, iso-2022-jp decoder state to escape, and return
+        // continue.
+        if (bite === 0x24 || bite === 0x28) {
+          iso2022jp_lead = bite;
+          iso2022jp_decoder_state = states.Escape;
+          return null;
+        }
+
+        // 2. Prepend byte to stream.
+        stream.prepend(bite);
+
+        // 3. Unset the iso-2022-jp output flag, set iso-2022-jp
+        // decoder state to iso-2022-jp decoder output state, and
+        // return error.
+        iso2022jp_output_flag = false;
+        iso2022jp_decoder_state = iso2022jp_decoder_output_state;
+        return decoderError(fatal);
+
+      case states.Escape:
+        // Escape
+
+        // 1. Let lead be iso-2022-jp lead and set iso-2022-jp lead to
+        // 0x00.
+        var lead = iso2022jp_lead;
+        iso2022jp_lead = 0x00;
+
+        // 2. Let state be null.
+        var state = null;
+
+        // 3. If lead is 0x28 and byte is 0x42, set state to ASCII.
+        if (lead === 0x28 && bite === 0x42)
+          state = states.ASCII;
+
+        // 4. If lead is 0x28 and byte is 0x4A, set state to Roman.
+        if (lead === 0x28 && bite === 0x4A)
+          state = states.Roman;
+
+        // 5. If lead is 0x28 and byte is 0x49, set state to Katakana.
+        if (lead === 0x28 && bite === 0x49)
+          state = states.Katakana;
+
+        // 6. If lead is 0x24 and byte is either 0x40 or 0x42, set
+        // state to lead byte.
+        if (lead === 0x24 && (bite === 0x40 || bite === 0x42))
+          state = states.LeadByte;
+
+        // 7. If state is non-null, run these substeps:
+        if (state !== null) {
+          // 1. Set iso-2022-jp decoder state and iso-2022-jp decoder
+          // output state to states.
+          iso2022jp_decoder_state = iso2022jp_decoder_state = state;
+
+          // 2. Let output flag be the iso-2022-jp output flag.
+          var output_flag = iso2022jp_output_flag;
+
+          // 3. Set the iso-2022-jp output flag.
+          iso2022jp_output_flag = true;
+
+          // 4. Return continue, if output flag is unset, and error
+          // otherwise.
+          return !output_flag ? null : decoderError(fatal);
+        }
+
+        // 8. Prepend lead and byte to stream.
+        stream.prepend([lead, bite]);
+
+        // 9. Unset the iso-2022-jp output flag, set iso-2022-jp
+        // decoder state to iso-2022-jp decoder output state and
+        // return error.
+        iso2022jp_output_flag = false;
+        iso2022jp_decoder_state = iso2022jp_decoder_output_state;
+        return decoderError(fatal);
+      }
+    };
+  }
+
+  // 13.2.2 iso-2022-jp encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function ISO2022JPEncoder(options) {
+    var fatal = options.fatal;
+    // iso-2022-jp's encoder has an associated iso-2022-jp encoder
+    // state which is one of ASCII, Roman, and jis0208 (initially
+    // ASCII).
+    /** @enum */
+    var states = {
+      ASCII: 0,
+      Roman: 1,
+      jis0208: 2
+    };
+    var /** @type {number} */ iso2022jp_state = states.ASCII;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream and iso-2022-jp encoder
+      // state is not ASCII, prepend code point to stream, set
+      // iso-2022-jp encoder state to ASCII, and return three bytes
+      // 0x1B 0x28 0x42.
+      if (code_point === end_of_stream &&
+          iso2022jp_state !== states.ASCII) {
+        stream.prepend(code_point);
+        iso2022jp_state = states.ASCII;
+        return [0x1B, 0x28, 0x42];
+      }
+
+      // 2. If code point is end-of-stream and iso-2022-jp encoder
+      // state is ASCII, return finished.
+      if (code_point === end_of_stream && iso2022jp_state === states.ASCII)
+        return finished;
+
+      // 3. If ISO-2022-JP encoder state is ASCII or Roman, and code
+      // point is U+000E, U+000F, or U+001B, return error with U+FFFD.
+      if ((iso2022jp_state === states.ASCII ||
+           iso2022jp_state === states.Roman) &&
+          (code_point === 0x000E || code_point === 0x000F ||
+           code_point === 0x001B)) {
+        return encoderError(0xFFFD);
+      }
+
+      // 4. If iso-2022-jp encoder state is ASCII and code point is an
+      // ASCII code point, return a byte whose value is code point.
+      if (iso2022jp_state === states.ASCII &&
+          isASCIICodePoint(code_point))
+        return code_point;
+
+      // 5. If iso-2022-jp encoder state is Roman and code point is an
+      // ASCII code point, excluding U+005C and U+007E, or is U+00A5
+      // or U+203E, run these substeps:
+      if (iso2022jp_state === states.Roman &&
+          ((isASCIICodePoint(code_point) &&
+           code_point !== 0x005C && code_point !== 0x007E) ||
+          (code_point == 0x00A5 || code_point == 0x203E))) {
+
+        // 1. If code point is an ASCII code point, return a byte
+        // whose value is code point.
+        if (isASCIICodePoint(code_point))
+          return code_point;
+
+        // 2. If code point is U+00A5, return byte 0x5C.
+        if (code_point === 0x00A5)
+          return 0x5C;
+
+        // 3. If code point is U+203E, return byte 0x7E.
+        if (code_point === 0x203E)
+          return 0x7E;
+      }
+
+      // 6. If code point is an ASCII code point, and iso-2022-jp
+      // encoder state is not ASCII, prepend code point to stream, set
+      // iso-2022-jp encoder state to ASCII, and return three bytes
+      // 0x1B 0x28 0x42.
+      if (isASCIICodePoint(code_point) &&
+          iso2022jp_state !== states.ASCII) {
+        stream.prepend(code_point);
+        iso2022jp_state = states.ASCII;
+        return [0x1B, 0x28, 0x42];
+      }
+
+      // 7. If code point is either U+00A5 or U+203E, and iso-2022-jp
+      // encoder state is not Roman, prepend code point to stream, set
+      // iso-2022-jp encoder state to Roman, and return three bytes
+      // 0x1B 0x28 0x4A.
+      if ((code_point === 0x00A5 || code_point === 0x203E) &&
+          iso2022jp_state !== states.Roman) {
+        stream.prepend(code_point);
+        iso2022jp_state = states.Roman;
+        return [0x1B, 0x28, 0x4A];
+      }
+
+      // 8. If code point is U+2212, set it to U+FF0D.
+      if (code_point === 0x2212)
+        code_point = 0xFF0D;
+
+      // 9. Let pointer be the index pointer for code point in index
+      // jis0208.
+      var pointer = indexPointerFor(code_point, index('jis0208'));
+
+      // 10. If pointer is null, return error with code point.
+      if (pointer === null)
+        return encoderError(code_point);
+
+      // 11. If iso-2022-jp encoder state is not jis0208, prepend code
+      // point to stream, set iso-2022-jp encoder state to jis0208,
+      // and return three bytes 0x1B 0x24 0x42.
+      if (iso2022jp_state !== states.jis0208) {
+        stream.prepend(code_point);
+        iso2022jp_state = states.jis0208;
+        return [0x1B, 0x24, 0x42];
+      }
+
+      // 12. Let lead be floor(pointer / 94) + 0x21.
+      var lead = floor(pointer / 94) + 0x21;
+
+      // 13. Let trail be pointer % 94 + 0x21.
+      var trail = pointer % 94 + 0x21;
+
+      // 14. Return two bytes whose values are lead and trail.
+      return [lead, trail];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['ISO-2022-JP'] = function(options) {
+    return new ISO2022JPEncoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['ISO-2022-JP'] = function(options) {
+    return new ISO2022JPDecoder(options);
+  };
+
+  // 13.3 Shift_JIS
+
+  // 13.3.1 Shift_JIS decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function ShiftJISDecoder(options) {
+    var fatal = options.fatal;
+    // Shift_JIS's decoder has an associated Shift_JIS lead (initially
+    // 0x00).
+    var /** @type {number} */ Shift_JIS_lead = 0x00;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and Shift_JIS lead is not 0x00,
+      // set Shift_JIS lead to 0x00 and return error.
+      if (bite === end_of_stream && Shift_JIS_lead !== 0x00) {
+        Shift_JIS_lead = 0x00;
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream and Shift_JIS lead is 0x00,
+      // return finished.
+      if (bite === end_of_stream && Shift_JIS_lead === 0x00)
+        return finished;
+
+      // 3. If Shift_JIS lead is not 0x00, let lead be Shift_JIS lead,
+      // let pointer be null, set Shift_JIS lead to 0x00, and then run
+      // these substeps:
+      if (Shift_JIS_lead !== 0x00) {
+        var lead = Shift_JIS_lead;
+        var pointer = null;
+        Shift_JIS_lead = 0x00;
+
+        // 1. Let offset be 0x40, if byte is less than 0x7F, and 0x41
+        // otherwise.
+        var offset = (bite < 0x7F) ? 0x40 : 0x41;
+
+        // 2. Let lead offset be 0x81, if lead is less than 0xA0, and
+        // 0xC1 otherwise.
+        var lead_offset = (lead < 0xA0) ? 0x81 : 0xC1;
+
+        // 3. If byte is in the range 0x40 to 0x7E, inclusive, or 0x80
+        // to 0xFC, inclusive, set pointer to (lead − lead offset) ×
+        // 188 + byte − offset.
+        if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFC))
+          pointer = (lead - lead_offset) * 188 + bite - offset;
+
+        // 4. If pointer is in the range 8836 to 10715, inclusive,
+        // return a code point whose value is 0xE000 − 8836 + pointer.
+        if (inRange(pointer, 8836, 10715))
+          return 0xE000 - 8836 + pointer;
+
+        // 5. Let code point be null, if pointer is null, and the
+        // index code point for pointer in index jis0208 otherwise.
+        var code_point = (pointer === null) ? null :
+              indexCodePointFor(pointer, index('jis0208'));
+
+        // 6. If code point is null and byte is an ASCII byte, prepend
+        // byte to stream.
+        if (code_point === null && isASCIIByte(bite))
+          stream.prepend(bite);
+
+        // 7. If code point is null, return error.
+        if (code_point === null)
+          return decoderError(fatal);
+
+        // 8. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 4. If byte is an ASCII byte or 0x80, return a code point
+      // whose value is byte.
+      if (isASCIIByte(bite) || bite === 0x80)
+        return bite;
+
+      // 5. If byte is in the range 0xA1 to 0xDF, inclusive, return a
+      // code point whose value is 0xFF61 − 0xA1 + byte.
+      if (inRange(bite, 0xA1, 0xDF))
+        return 0xFF61 - 0xA1 + bite;
+
+      // 6. If byte is in the range 0x81 to 0x9F, inclusive, or 0xE0
+      // to 0xFC, inclusive, set Shift_JIS lead to byte and return
+      // continue.
+      if (inRange(bite, 0x81, 0x9F) || inRange(bite, 0xE0, 0xFC)) {
+        Shift_JIS_lead = bite;
+        return null;
+      }
+
+      // 7. Return error.
+      return decoderError(fatal);
+    };
+  }
+
+  // 13.3.2 Shift_JIS encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function ShiftJISEncoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point or U+0080, return a
+      // byte whose value is code point.
+      if (isASCIICodePoint(code_point) || code_point === 0x0080)
+        return code_point;
+
+      // 3. If code point is U+00A5, return byte 0x5C.
+      if (code_point === 0x00A5)
+        return 0x5C;
+
+      // 4. If code point is U+203E, return byte 0x7E.
+      if (code_point === 0x203E)
+        return 0x7E;
+
+      // 5. If code point is in the range U+FF61 to U+FF9F, inclusive,
+      // return a byte whose value is code point − 0xFF61 + 0xA1.
+      if (inRange(code_point, 0xFF61, 0xFF9F))
+        return code_point - 0xFF61 + 0xA1;
+
+      // 6. If code point is U+2212, set it to U+FF0D.
+      if (code_point === 0x2212)
+        code_point = 0xFF0D;
+
+      // 7. Let pointer be the index Shift_JIS pointer for code point.
+      var pointer = indexShiftJISPointerFor(code_point);
+
+      // 8. If pointer is null, return error with code point.
+      if (pointer === null)
+        return encoderError(code_point);
+
+      // 9. Let lead be floor(pointer / 188).
+      var lead = floor(pointer / 188);
+
+      // 10. Let lead offset be 0x81, if lead is less than 0x1F, and
+      // 0xC1 otherwise.
+      var lead_offset = (lead < 0x1F) ? 0x81 : 0xC1;
+
+      // 11. Let trail be pointer % 188.
+      var trail = pointer % 188;
+
+      // 12. Let offset be 0x40, if trail is less than 0x3F, and 0x41
+      // otherwise.
+      var offset = (trail < 0x3F) ? 0x40 : 0x41;
+
+      // 13. Return two bytes whose values are lead + lead offset and
+      // trail + offset.
+      return [lead + lead_offset, trail + offset];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['Shift_JIS'] = function(options) {
+    return new ShiftJISEncoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['Shift_JIS'] = function(options) {
+    return new ShiftJISDecoder(options);
+  };
+
+  //
+  // 14. Legacy multi-byte Korean encodings
+  //
+
+  // 14.1 euc-kr
+
+  // 14.1.1 euc-kr decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function EUCKRDecoder(options) {
+    var fatal = options.fatal;
+
+    // euc-kr's decoder has an associated euc-kr lead (initially 0x00).
+    var /** @type {number} */ euckr_lead = 0x00;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and euc-kr lead is not 0x00, set
+      // euc-kr lead to 0x00 and return error.
+      if (bite === end_of_stream && euckr_lead !== 0) {
+        euckr_lead = 0x00;
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream and euc-kr lead is 0x00, return
+      // finished.
+      if (bite === end_of_stream && euckr_lead === 0)
+        return finished;
+
+      // 3. If euc-kr lead is not 0x00, let lead be euc-kr lead, let
+      // pointer be null, set euc-kr lead to 0x00, and then run these
+      // substeps:
+      if (euckr_lead !== 0x00) {
+        var lead = euckr_lead;
+        var pointer = null;
+        euckr_lead = 0x00;
+
+        // 1. If byte is in the range 0x41 to 0xFE, inclusive, set
+        // pointer to (lead − 0x81) × 190 + (byte − 0x41).
+        if (inRange(bite, 0x41, 0xFE))
+          pointer = (lead - 0x81) * 190 + (bite - 0x41);
+
+        // 2. Let code point be null, if pointer is null, and the
+        // index code point for pointer in index euc-kr otherwise.
+        var code_point = (pointer === null)
+              ? null : indexCodePointFor(pointer, index('euc-kr'));
+
+        // 3. If code point is null and byte is an ASCII byte, prepend
+        // byte to stream.
+        if (pointer === null && isASCIIByte(bite))
+          stream.prepend(bite);
+
+        // 4. If code point is null, return error.
+        if (code_point === null)
+          return decoderError(fatal);
+
+        // 5. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 4. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 5. If byte is in the range 0x81 to 0xFE, inclusive, set
+      // euc-kr lead to byte and return continue.
+      if (inRange(bite, 0x81, 0xFE)) {
+        euckr_lead = bite;
+        return null;
+      }
+
+      // 6. Return error.
+      return decoderError(fatal);
+    };
+  }
+
+  // 14.1.2 euc-kr encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function EUCKREncoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. Let pointer be the index pointer for code point in index
+      // euc-kr.
+      var pointer = indexPointerFor(code_point, index('euc-kr'));
+
+      // 4. If pointer is null, return error with code point.
+      if (pointer === null)
+        return encoderError(code_point);
+
+      // 5. Let lead be floor(pointer / 190) + 0x81.
+      var lead = floor(pointer / 190) + 0x81;
+
+      // 6. Let trail be pointer % 190 + 0x41.
+      var trail = (pointer % 190) + 0x41;
+
+      // 7. Return two bytes whose values are lead and trail.
+      return [lead, trail];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['EUC-KR'] = function(options) {
+    return new EUCKREncoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['EUC-KR'] = function(options) {
+    return new EUCKRDecoder(options);
+  };
+
+
+  //
+  // 15. Legacy miscellaneous encodings
+  //
+
+  // 15.1 replacement
+
+  // Not needed - API throws RangeError
+
+  // 15.2 Common infrastructure for utf-16be and utf-16le
+
+  /**
+   * @param {number} code_unit
+   * @param {boolean} utf16be
+   * @return {!Array.<number>} bytes
+   */
+  function convertCodeUnitToBytes(code_unit, utf16be) {
+    // 1. Let byte1 be code unit >> 8.
+    var byte1 = code_unit >> 8;
+
+    // 2. Let byte2 be code unit & 0x00FF.
+    var byte2 = code_unit & 0x00FF;
+
+    // 3. Then return the bytes in order:
+        // utf-16be flag is set: byte1, then byte2.
+    if (utf16be)
+      return [byte1, byte2];
+    // utf-16be flag is unset: byte2, then byte1.
+    return [byte2, byte1];
+  }
+
+  // 15.2.1 shared utf-16 decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {boolean} utf16_be True if big-endian, false if little-endian.
+   * @param {{fatal: boolean}} options
+   */
+  function UTF16Decoder(utf16_be, options) {
+    var fatal = options.fatal;
+    var /** @type {?number} */ utf16_lead_byte = null,
+        /** @type {?number} */ utf16_lead_surrogate = null;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and either utf-16 lead byte or
+      // utf-16 lead surrogate is not null, set utf-16 lead byte and
+      // utf-16 lead surrogate to null, and return error.
+      if (bite === end_of_stream && (utf16_lead_byte !== null ||
+                                utf16_lead_surrogate !== null)) {
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream and utf-16 lead byte and utf-16
+      // lead surrogate are null, return finished.
+      if (bite === end_of_stream && utf16_lead_byte === null &&
+          utf16_lead_surrogate === null) {
+        return finished;
+      }
+
+      // 3. If utf-16 lead byte is null, set utf-16 lead byte to byte
+      // and return continue.
+      if (utf16_lead_byte === null) {
+        utf16_lead_byte = bite;
+        return null;
+      }
+
+      // 4. Let code unit be the result of:
+      var code_unit;
+      if (utf16_be) {
+        // utf-16be decoder flag is set
+        //   (utf-16 lead byte << 8) + byte.
+        code_unit = (utf16_lead_byte << 8) + bite;
+      } else {
+        // utf-16be decoder flag is unset
+        //   (byte << 8) + utf-16 lead byte.
+        code_unit = (bite << 8) + utf16_lead_byte;
+      }
+      // Then set utf-16 lead byte to null.
+      utf16_lead_byte = null;
+
+      // 5. If utf-16 lead surrogate is not null, let lead surrogate
+      // be utf-16 lead surrogate, set utf-16 lead surrogate to null,
+      // and then run these substeps:
+      if (utf16_lead_surrogate !== null) {
+        var lead_surrogate = utf16_lead_surrogate;
+        utf16_lead_surrogate = null;
+
+        // 1. If code unit is in the range U+DC00 to U+DFFF,
+        // inclusive, return a code point whose value is 0x10000 +
+        // ((lead surrogate − 0xD800) << 10) + (code unit − 0xDC00).
+        if (inRange(code_unit, 0xDC00, 0xDFFF)) {
+          return 0x10000 + (lead_surrogate - 0xD800) * 0x400 +
+              (code_unit - 0xDC00);
+        }
+
+        // 2. Prepend the sequence resulting of converting code unit
+        // to bytes using utf-16be decoder flag to stream and return
+        // error.
+        stream.prepend(convertCodeUnitToBytes(code_unit, utf16_be));
+        return decoderError(fatal);
+      }
+
+      // 6. If code unit is in the range U+D800 to U+DBFF, inclusive,
+      // set utf-16 lead surrogate to code unit and return continue.
+      if (inRange(code_unit, 0xD800, 0xDBFF)) {
+        utf16_lead_surrogate = code_unit;
+        return null;
+      }
+
+      // 7. If code unit is in the range U+DC00 to U+DFFF, inclusive,
+      // return error.
+      if (inRange(code_unit, 0xDC00, 0xDFFF))
+        return decoderError(fatal);
+
+      // 8. Return code point code unit.
+      return code_unit;
+    };
+  }
+
+  // 15.2.2 shared utf-16 encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {boolean} utf16_be True if big-endian, false if little-endian.
+   * @param {{fatal: boolean}} options
+   */
+  function UTF16Encoder(utf16_be, options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is in the range U+0000 to U+FFFF, inclusive,
+      // return the sequence resulting of converting code point to
+      // bytes using utf-16be encoder flag.
+      if (inRange(code_point, 0x0000, 0xFFFF))
+        return convertCodeUnitToBytes(code_point, utf16_be);
+
+      // 3. Let lead be ((code point − 0x10000) >> 10) + 0xD800,
+      // converted to bytes using utf-16be encoder flag.
+      var lead = convertCodeUnitToBytes(
+        ((code_point - 0x10000) >> 10) + 0xD800, utf16_be);
+
+      // 4. Let trail be ((code point − 0x10000) & 0x3FF) + 0xDC00,
+      // converted to bytes using utf-16be encoder flag.
+      var trail = convertCodeUnitToBytes(
+        ((code_point - 0x10000) & 0x3FF) + 0xDC00, utf16_be);
+
+      // 5. Return a byte sequence of lead followed by trail.
+      return lead.concat(trail);
+    };
+  }
+
+  // 15.3 utf-16be
+  // 15.3.1 utf-16be decoder
+  /** @param {{fatal: boolean}} options */
+  encoders['UTF-16BE'] = function(options) {
+    return new UTF16Encoder(true, options);
+  };
+  // 15.3.2 utf-16be encoder
+  /** @param {{fatal: boolean}} options */
+  decoders['UTF-16BE'] = function(options) {
+    return new UTF16Decoder(true, options);
+  };
+
+  // 15.4 utf-16le
+  // 15.4.1 utf-16le decoder
+  /** @param {{fatal: boolean}} options */
+  encoders['UTF-16LE'] = function(options) {
+    return new UTF16Encoder(false, options);
+  };
+  // 15.4.2 utf-16le encoder
+  /** @param {{fatal: boolean}} options */
+  decoders['UTF-16LE'] = function(options) {
+    return new UTF16Decoder(false, options);
+  };
+
+  // 15.5 x-user-defined
+
+  // 15.5.1 x-user-defined decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function XUserDefinedDecoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream, return finished.
+      if (bite === end_of_stream)
+        return finished;
+
+      // 2. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 3. Return a code point whose value is 0xF780 + byte − 0x80.
+      return 0xF780 + bite - 0x80;
+    };
+  }
+
+  // 15.5.2 x-user-defined encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function XUserDefinedEncoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1.If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. If code point is in the range U+F780 to U+F7FF, inclusive,
+      // return a byte whose value is code point − 0xF780 + 0x80.
+      if (inRange(code_point, 0xF780, 0xF7FF))
+        return code_point - 0xF780 + 0x80;
+
+      // 4. Return error with code point.
+      return encoderError(code_point);
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['x-user-defined'] = function(options) {
+    return new XUserDefinedEncoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['x-user-defined'] = function(options) {
+    return new XUserDefinedDecoder(options);
+  };
+
+  if (!global['TextEncoder'])
+    global['TextEncoder'] = TextEncoder;
+  if (!global['TextDecoder'])
+    global['TextDecoder'] = TextDecoder;
+
+  if (typeof module !== "undefined" && module.exports) {
+    module.exports = {
+      TextEncoder: global['TextEncoder'],
+      TextDecoder: global['TextDecoder'],
+      EncodingIndexes: global["encoding-indexes"]
+    };
+  }
+
+// For strict environments where `this` inside the global scope
+// is `undefined`, take a pure object instead
+}(this || {}));

File diff suppressed because it is too large
+ 196 - 0
pages/order/gprint/gbk.js


+ 205 - 0
pages/order/gprint/printerjobs.js

@@ -0,0 +1,205 @@
+const commands = require('./commands');
+const gbk = require('./gbk');
+
+const printerJobs = function() {
+	this._queue = Array.from(commands.HARDWARE.HW_INIT);
+	this._enqueue = function(cmd) {
+		this._queue.push.apply(this._queue, cmd);
+	}
+};
+
+/**
+ * 增加打印内容
+ * @param  {string} content  文字内容
+ */
+printerJobs.prototype.text = function(content) {
+	if (content) {
+		let uint8Array = gbk.encode(content);
+		let encoded = Array.from(uint8Array);
+		this._enqueue(encoded);
+	}
+	return this;
+};
+
+/**
+ * 打印文字
+ * @param  {string} content  文字内容
+ */
+printerJobs.prototype.print = function(content) {
+	this.text(content);
+	this._enqueue(commands.LF);
+	return this;
+};
+
+/**
+ * 分页
+ */
+printerJobs.prototype.ctlff = function() {
+	this._enqueue(commands.FEED_CONTROL_SEQUENCES.CTL_FF);
+	return this;
+};
+
+/**
+ * 打印文字并换行
+ * @param  {string}  content  文字内容
+ */
+printerJobs.prototype.println = function(content = '') {
+	return this.print(content + commands.EOL);
+};
+
+/**
+ * 设置对齐方式
+ * @param {string} align 对齐方式 LT/CT/RT
+ */
+printerJobs.prototype.setAlign = function(align) {
+	this._enqueue(commands.TEXT_FORMAT['TXT_ALIGN_' + align.toUpperCase()]);
+	return this;
+};
+
+/**
+ * 设置字体
+ * @param  {string} family A/B/C
+ */
+printerJobs.prototype.setFont = function(family) {
+	this._enqueue(commands.TEXT_FORMAT['TXT_FONT_' + family.toUpperCase()]);
+	return this;
+};
+
+/**
+ * 设定字体尺寸
+ * @param  {number} width 字体宽度 1~2
+ * @param  {number} height 字体高度 1~2
+ */
+printerJobs.prototype.setSize = function(width, height) {
+	if (2 >= width && 2 >= height) {
+		this._enqueue(commands.TEXT_FORMAT.TXT_NORMAL);
+		if (2 === width && 2 === height) {
+			this._enqueue(commands.TEXT_FORMAT.TXT_4SQUARE);
+		} else if (1 === width && 2 === height) {
+			this._enqueue(commands.TEXT_FORMAT.TXT_2HEIGHT);
+		} else if (2 === width && 1 === height) {
+			this._enqueue(commands.TEXT_FORMAT.TXT_2WIDTH);
+		}
+	}
+	return this;
+};
+
+/**
+ * 设定字体是否加粗
+ * @param  {boolean} bold
+ */
+printerJobs.prototype.setBold = function(bold) {
+	if (typeof bold !== 'boolean') {
+		bold = true;
+	}
+	this._enqueue(bold ? commands.TEXT_FORMAT.TXT_BOLD_ON : commands.TEXT_FORMAT.TXT_BOLD_OFF);
+	return this;
+};
+
+/**
+ * 设定是否开启下划线
+ * @param  {boolean} underline
+ */
+printerJobs.prototype.setUnderline = function(underline) {
+	if (typeof underline !== 'boolean') {
+		underline = true;
+	}
+	this._enqueue(underline ? commands.TEXT_FORMAT.TXT_UNDERL_ON : commands.TEXT_FORMAT.TXT_UNDERL_OFF);
+	return this;
+};
+
+/**
+ * 设置行间距为 n 点行,默认值行间距是 30 点
+ * @param {number} n 0≤n≤255
+ */
+printerJobs.prototype.setLineSpacing = function(n) {
+	if (n === undefined || n === null) {
+		this._enqueue(commands.LINE_SPACING.LS_DEFAULT);
+	} else if (n === 0) {
+		this._enqueue(commands.LINE_SPACING.LS_ZERO);
+	} else {
+		this._enqueue(commands.LINE_SPACING.LS_SET);
+		this._enqueue([n]);
+	}
+	return this;
+};
+
+/**
+ * 打印空行
+ * @param {number} n
+ */
+printerJobs.prototype.lineFeed = function(n = 1) {
+	return this.print(new Array(n).fill(commands.EOL).join(''));
+};
+
+/**
+ *  设置字体颜色,需要打印机支持
+ *  @param  {number} color - 0 默认颜色黑色 1 红色
+ */
+printerJobs.prototype.setColor = function(color) {
+	this._enqueue(commands.COLOR[color === 1 ? 1 : 0]);
+	return this;
+};
+
+/**
+ * https://support.loyverse.com/hardware/printers/use-the-beeper-in-a-escpos-printers
+ * 蜂鸣警报,需要打印机支持
+ * @param  {number} n    蜂鸣次数,1-9
+ * @param  {number} t 蜂鸣长短,1-9
+ */
+printerJobs.prototype.beep = function(n, t) {
+	this._enqueue(commands.BEEP);
+	this._enqueue([n, t]);
+	return this;
+};
+
+/**
+ * 清空任务
+ */
+printerJobs.prototype.clear = function() {
+	this._queue = Array.from(commands.HARDWARE.HW_INIT);
+	return this;
+};
+
+/**
+ * 打印条码
+ * @param  {string} content  条码内容 code128A,B,C可以混合打印在一起
+ */
+printerJobs.prototype.printBarcode = function(content) {
+	if (content) {
+		let bar = commands.BARCODE_FORMAT;
+		// const cmds = [].concat(bar.BARCODE_TXT_OFF, bar.BARCODE_HEIGHT_DEFAULT, bar.BARCODE_WIDTH(2), bar
+		// 	.BARCODE_CODE128, content);
+		const cmds = [].concat(bar.BARCODE_TXT_BLW, bar.BARCODE_HEIGHT_DEFAULT, bar.BARCODE_WIDTH(2), bar
+			.BARCODE_CODE128, content);
+		this._enqueue(cmds);
+		// this._enqueue(commands.LF);
+	}
+
+	return this;
+};
+
+/**
+ * 打印二维码
+ * @param  {string} content  二维码内容
+ */
+printerJobs.prototype.printQrcode = function(content) {
+	if (content) {
+		let qr = commands.QRCODE_FORMAT;
+		// const cmds = [].concat([27, 97, 1], [29, 118, 48, 0, 30, 0, 240, 0], content, [27, 74, 3], [27, 64]);
+		const cmds = [].concat(qr.QRCODE_SIZE(4), qr.QRCODE_ERROR(49), qr.QRCODE_DATA(content),
+			qr.QRCODE_PRINT);
+		this._enqueue(cmds);
+		// this._enqueue(commands.LF);
+	}
+
+	return this;
+};
+/**
+ * 返回ArrayBuffer
+ */
+printerJobs.prototype.buffer = function() {
+	return new Uint8Array(this._queue).buffer;
+};
+
+module.exports = printerJobs;

+ 92 - 0
pages/order/gprint/printerutil.js

@@ -0,0 +1,92 @@
+// 打印机纸宽58mm,页的宽度384,字符宽度为1,每行最多盛放32个字符
+const PAGE_WIDTH = 384;
+const MAX_CHAR_COUNT_EACH_LINE = 32;
+
+/**
+ * @param str
+ * @returns {boolean} str是否全是中文
+ */
+function isChinese(str) {
+	return /^[\u4e00-\u9fa5]$/.test(str);
+}
+
+/**
+ * 返回字符串宽度(1个中文=2个英文字符)
+ * @param str
+ * @returns {number}
+ */
+function getStringWidth(str) {
+	let width = 0;
+	for (let i = 0, len = str.length; i < len; i++) {
+		width += isChinese(str.charAt(i)) ? 2 : 1;
+	}
+	return width;
+}
+
+/**
+ * 同一行输出str1, str2,str1居左, str2居右
+ * @param {string} str1 内容1
+ * @param {string} str2 内容2
+ * @param {number} fontWidth 字符宽度 1/2
+ * @param {string} fillWith str1 str2之间的填充字符
+ *
+ */
+function inline(str1, str2, fillWith = ' ', fontWidth = 1) {
+	const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth;
+	// 需要填充的字符数量
+	let fillCount = lineWidth - (getStringWidth(str1) + getStringWidth(str2)) % lineWidth;
+	let fillStr = new Array(fillCount).fill(fillWith.charAt(0)).join('');
+	return str1 + fillStr + str2;
+}
+
+/**
+ * 用字符填充一整行
+ * @param {string} fillWith 填充字符
+ * @param {number} fontWidth 字符宽度 1/2
+ */
+function fillLine(fillWith = '-', fontWidth = 1) {
+	const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth;
+	return new Array(lineWidth).fill(fillWith.charAt(0)).join('');
+}
+
+/**
+ * 文字内容居中,左右用字符填充
+ * @param {string} str 文字内容
+ * @param {number} fontWidth 字符宽度 1/2
+ * @param {string} fillWith str1 str2之间的填充字符
+ */
+function fillAround(str, fillWith = '-', fontWidth = 1) {
+	const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth;
+	let strWidth = getStringWidth(str);
+	// 内容已经超过一行了,没必要填充
+	if (strWidth >= lineWidth) {
+		return str;
+	}
+	// 需要填充的字符数量
+	let fillCount = lineWidth - strWidth;
+	// 左侧填充的字符数量
+	let leftCount = Math.round(fillCount / 2);
+	// 两侧的填充字符,需要考虑左边需要填充,右边不需要填充的情况
+	let fillStr = new Array(leftCount).fill(fillWith.charAt(0)).join('');
+	return fillStr + str + fillStr.substr(0, fillCount - leftCount);
+}
+
+// ArrayBuffer转16进度字符串示例
+function ab2hex(buffer) {
+	const hexArr = Array.prototype.map.call(
+		new Uint8Array(buffer),
+		function(bit) {
+			return ('00' + bit.toString(16)).slice(-2)
+		}
+	)
+	return hexArr.join(',')
+}
+
+
+module.exports = {
+	inline: inline,
+	fillLine: fillLine,
+	fillAround: fillAround,
+	ab2hex: ab2hex,
+	getStringWidth: getStringWidth,
+};

+ 66 - 0
pages/order/gprint/util.js

@@ -0,0 +1,66 @@
+const formatTime = date => {
+	const year = date.getFullYear()
+	const month = date.getMonth() + 1
+	const day = date.getDate()
+	const hour = date.getHours()
+	const minute = date.getMinutes()
+	const second = date.getSeconds()
+
+	return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
+}
+
+const formatNumber = n => {
+	n = n.toString()
+	return n[1] ? n : '0' + n
+}
+
+
+//4合1 
+function convert4to1(res) {
+	let arr = [];
+	for (let i = 0; i < res.length; i++) {
+		if (i % 4 == 0) {
+			let rule = 0.29900 * res[i] + 0.58700 * res[i + 1] + 0.11400 * res[i + 2];
+			if (rule > 200) {
+				res[i] = 0;
+			} else {
+				res[i] = 1;
+			}
+			arr.push(res[i]);
+		}
+	}
+	return arr;
+}
+
+//8合1
+function convert8to1(arr) {
+	let data = [];
+	for (let k = 0; k < arr.length; k += 8) {
+		let temp = arr[k] * 128 + arr[k + 1] * 64 + arr[k + 2] * 32 + arr[k + 3] * 16 + arr[k + 4] * 8 + arr[k + 5] * 4 +
+			arr[k + 6] * 2 + arr[k + 7] * 1
+		data.push(temp);
+	}
+	return data;
+}
+
+//我的图片宽度是240,那么拼接的指令就是[29, 118, 48, 0, 30, 0, 240, 0]
+//我的图片宽度是160,那么拼接的指令就是[29, 118, 48, 0, 20, 0, 160, 0]
+//补充一点,打印非二维码的图片,宽度一定要是24的倍数,不然打印也会出现乱码
+function toArrayBuffer(res) {
+	let arr = convert4to1(res);
+	let data = convert8to1(arr);
+	let cmds = [].concat([27, 97, 1], [29, 118, 48, 0, 30, 0, 240, 0], data, [27, 74, 3], [27, 64]);
+	return new Uint8Array(cmds).buffer;
+}
+
+function zip_image(res) {
+	let arr = convert4to1(res.data);
+	let data = convert8to1(arr);
+	return data;
+}
+
+module.exports = {
+	formatTime: formatTime,
+	toArrayBuffer: toArrayBuffer,
+	zip_image: zip_image,
+}

File diff suppressed because it is too large
+ 4 - 0
pages/order/gprint/weapp.qrcode.esm.js


+ 172 - 0
pages/order/humiture.vue

@@ -0,0 +1,172 @@
+<template>
+	<!-- 温湿度 -->
+	<view>
+		<u-navbar title="温湿度记录" autoBack placeholder></u-navbar>
+		<view v-if="humitureList.length > 0">
+			<view class="card_particulars" v-for="(item,index) in humitureList" :key="index">
+				<view class="card_describe">
+					<view class="describe_title">{{item.title}}</view>
+					<view class="describe_time">{{item.startTime}}</view>
+					<view class="describe_time">{{item.endTime}}</view>
+				</view>
+				<view class="humiture_details" @click="foldingPanel(item)">
+					<view class="title_details">温湿度</view>
+					<view style="display: flex;">
+						<view class="title_blue title_details">查看详情</view>
+						<u-icon :class="item.collapseil ? '' : 'overturn'" name="arrow-down"></u-icon>
+					</view>
+				</view>
+				<view v-show="item.collapseil">
+					<x-humiture ref="humiture" :taskId="item.id" :waybillNo="orderList.waybillNo"
+						:current="item.presentNum" :probeList="item.deviceSensorList"
+						@sectionChange="sectionChange"></x-humiture>
+				</view>
+			</view>
+		</view>
+		<view style="margin-top: 30%;" v-else>
+			<u-empty mode="data" text="当前没有温湿度记录"></u-empty>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				orderList: {},
+				humitureList: [],
+				userType: '',
+				myChart: null,
+				chartData: {},
+				chartData1: {},
+			}
+		},
+		mounted() {
+			var userInfo = this.$cache.getCache('userInfo')
+			this.userType = userInfo.userType
+			var orderList = this.$cache.getCache('orderDetails')
+			this.orderList = orderList
+			this.getList()
+		},
+		methods: {
+			getList() {
+				this.$api.get('/api/waybill-task', {
+					waybillNo: this.orderList.waybillNo,
+				}).then(res => {
+					if (res.code == 200) {
+						this.humitureList = res.data.list
+						this.humitureList.forEach((item, index) => {
+							item.title = ''
+							item.collapseil = true
+							if (item.coolerBox.id) {
+								item.title = item.coolerBox.name
+							}
+							if(item.deviceSensorList){
+								this.$nextTick(() => {
+									this.$refs.humiture[index].getlist(item.deviceSensorList[0].T_id)
+								})
+							}
+						})
+					}
+				})
+			},
+			// 折叠面板
+			foldingPanel(value) {
+				this.humitureList.forEach((item, index) => {
+					if (item.id == value.id) {
+						if (item.collapseil) {
+							item.collapseil = false
+						} else {
+							this.$refs.humiture[index].getlist(item.deviceSensorList[0].T_id)
+							item.collapseil = true
+						}
+					}
+				})
+				this.$forceUpdate()
+			},
+			open(e) {
+				// console.log('open', e)
+			},
+			close(e) {
+				// console.log('close', e)
+			},
+			change(e) {
+				if (e[0].status == 'open') {
+					this.humitureList.forEach((item, index) => {
+						this.$refs.humiture[index].getlist(item.deviceSensorList[0].T_id)
+					})
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.card_particulars {
+		display: flex;
+		flex-direction: column;
+		padding-top: 20rpx;
+		margin: 30rpx 20rpx 20rpx 20rpx;
+		border-radius: 20rpx;
+		background-color: #fff;
+	}
+
+	.card_describe {
+		padding-left: 20rpx;
+		padding-bottom: 20rpx;
+		border-bottom: 1rpx solid #EBEEF5;
+	}
+
+	.describe_title {
+		font-size: 30rpx;
+		font-weight: 600;
+	}
+
+	.describe_time {
+		font-size: 28rpx;
+	}
+
+	.humiture_details {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 20rpx;
+	}
+
+	.title_blue {
+		color: #2979ff;
+		margin-right: 10rpx;
+	}
+
+	.title_details {
+		font-size: 28rpx;
+	}
+
+	::v-deep .u-collapse-item__content__text {
+		padding: 0rpx 0rpx 30rpx 0rpx;
+	}
+
+	::v-deep .u-line {
+		display: none;
+	}
+
+	.card_qiucharts {
+		display: flex;
+		align-items: center;
+		flex-direction: column;
+	}
+
+	.card_echart {
+		width: 80%;
+		height: 500rpx;
+	}
+
+	.card_echart {
+		width: 80%;
+		height: 500rpx;
+	}
+
+	.overturn {
+		transform: rotate(180deg);
+	}
+</style>

+ 294 - 0
pages/order/index.vue

@@ -0,0 +1,294 @@
+<template>
+	<!-- 订单页面 -->
+	<view>
+		<u-navbar title="我的运单" autoBack placeholder></u-navbar>
+		<view class="tab_order">
+			<u-tabs :list="tableList" lineColor="#333333" lineWidth="60" :scrollable="false" :current="current"
+				@change="tabClick"></u-tabs>
+		</view>
+		<view class="card_order_mangement" v-if="orderList.length > 0">
+			<x-orderManagement :orderList="orderList" :userInfo="userInfo" @sendOrders="sendOrders"
+				@operateSuccessfully="operateSuccessfully"></x-orderManagement>
+			<view v-if="loadingMore" style="width: 50%;"><u-divider :text="loading"></u-divider></view>
+		</view>
+		<u-empty mode="order" marginTop="50" v-else></u-empty>
+		<u-popup :show="sendShow" closeable round="8" @close="close">
+			<view class="card_send">
+				<view class="send_title">派单</view>
+				<view class="card_input_send">
+					<view class="incubator_title"><span>*</span>保温箱</view>
+					<u-input placeholder="请输入保温箱名称" border="surround" v-model="incubatorValue" @input="incubatorInput"
+						@focus="focus"></u-input>
+				</view>
+				<view class="card_thermostat">
+					<scroll-view class="scroll-view" scroll-y="true" @scrolltolower="loadMore">
+						<view v-if="thermostatList.length > 0">
+							<view class="card_item_option" v-for="(item,index) in thermostatList" :key="index"
+								@click="selectIncubator(item)">
+								<view :style="{color:item.flag ? '#409EFF' : ''}">{{item.name}}</view>
+								<view v-if="item.flag"><u-icon :color="item.flag ? '#409EFF' : ''" name="checkmark"
+										size="20"></u-icon></view>
+							</view>
+						</view>
+						<view class="nomatchingData" v-else>暂无保温箱</view>
+					</scroll-view>
+				</view>
+				<view class="card_btn">
+					<u-button type="primary" text="确定" @click="confirm"></u-button>
+				</view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				tableList: [],
+				list: [{
+					name: '全部',
+				}, {
+					id: 1,
+					name: '已下单',
+				}, {
+					id: 2,
+					name: '配送中'
+				}, {
+					id: 3,
+					name: '已送达'
+				}, {
+					id: 4,
+					name: '已取消'
+				}],
+				current: 0,
+				userInfo: {},
+				orderList: [],
+				pageSize: 10,
+				currentPage: 1,
+				loadingMore: true,
+				loading: '加载中',
+				status: null,
+				sendShow: false,
+				incubatorValue: '',
+				thermostatList: [],
+				incubatorMore: true,
+				Pagination: 1,
+				searchShow: true,
+			}
+		},
+		onReachBottom() {
+			if (!this.loadingMore) {
+				this.getList()
+			}
+		},
+		onLoad(value) {
+			if (value.current) {
+				this.current = Number(value.current)
+			}
+			var userInfo = this.$cache.getCache('userInfo')
+			// console.log(userInfo, 25)
+			this.userInfo = userInfo
+			this.tableList = this.list
+			let statusType = ''
+			if (this.current != 0 || this.current) {
+				statusType = this.tableList[this.current].id
+			}
+			this.status = statusType
+			this.getList()
+		},
+		methods: {
+			// 获取司机和仓管订单列表
+			getList() {
+				this.loadingMore = true;
+				this.$api.get('/api/waybill', {
+					page: this.currentPage,
+					pageSize: this.pageSize,
+					status: this.status,
+				}).then(res => {
+					if (res.code == 200) {
+						const data = res.data.list
+						if (this.loadingMore == true && data) {
+							this.orderList = this.orderList.concat(data);
+						}
+						if (this.orderList.length < this.pageSize) {
+							this.loadingMore = true
+							this.loading = '没有更多了'
+						} else {
+							this.loading = '加载中'
+							this.loadingMore = false
+							this.currentPage++
+						}
+					}
+				})
+			},
+			// 派单
+			sendOrders(value) {
+				// console.log(value, 3333)
+				this.waybillId = value.id
+				this.sendShow = true
+				this.getIncubator()
+			},
+			// 重新获取列表
+			operateSuccessfully() {
+				this.orderList = []
+				this.getList()
+			},
+			getIncubator() {
+				this.$api.get('/api/cooler-box', {
+					page: this.Pagination,
+					pageSize: this.pageSize,
+					name: this.incubatorValue,
+				}).then(res => {
+					if (res.code == 200) {
+						const data = res.data.list
+						data.forEach(item => {
+							item.flag = false
+						})
+						if (this.incubatorMore == true) {
+							this.thermostatList = this.thermostatList.concat(data);
+						}
+						if (data.length < this.pageSize) {
+							this.incubatorMore = false
+						} else {
+							this.incubatorMore = true
+						}
+					}
+				})
+			},
+			// 确定派单
+			confirm() {
+				let boxId = null
+				this.thermostatList.forEach((item, index) => {
+					if (item.flag) {
+						boxId = item.id
+					}
+				})
+				if (boxId != null) {
+					const arr = []
+					arr.push(this.waybillId)
+					this.$api.post('/api/waybill/delivery', {
+						waybillIds: arr,
+						coolerBoxId: boxId,
+					}).then(res => {
+						if (res.code == 200) {
+							this.currentPage = 1
+							uni.$u.toast('派单成功')
+							this.sendShow = false
+							this.tabClick()
+						}
+					})
+				} else {
+					uni.$u.toast('请先选择保温箱')
+				}
+			},
+			// 滚动加载更多
+			loadMore() {
+				if (this.incubatorMore) {
+					this.Pagination++
+					this.getIncubator()
+				}
+			},
+			// tab订单
+			tabClick(row) {
+				this.currentPage = 1
+				this.orderList = []
+				if (row) {
+					this.status = row.id
+				}
+				this.getList()
+			},
+			close() {
+				this.sendShow = false
+				this.incubatorMore = true
+				this.thermostatList = []
+			},
+			// 搜索保温箱
+			incubatorInput(value) {
+				if (this.searchShow) {
+					this.incubatorMore = true
+					this.Pagination = 1
+					this.thermostatList = []
+					this.getIncubator()
+				}
+			},
+			// 鼠标聚焦
+			focus() {
+				this.searchShow = true
+			},
+			// 选择保温箱
+			selectIncubator(value) {
+				this.thermostatList.forEach(item => {
+					if (item.id == value.id) {
+						item.flag = true
+						this.incubatorValue = item.name
+					} else {
+						item.flag = false
+					}
+				})
+				this.searchShow = false
+				this.$forceUpdate()
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.tab_order {
+		background-color: #fff;
+	}
+
+	.card_order_mangement {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+	}
+
+	.card_send {
+		padding: 20rpx;
+	}
+
+	.send_title {
+		text-align: center;
+		margin: 10px;
+	}
+
+	.card_input_send {
+		display: flex;
+		align-items: center;
+		margin-bottom: 20rpx;
+	}
+
+	.incubator_title {
+		margin-right: 10rpx;
+
+		span {
+			color: red;
+		}
+	}
+
+	.card_btn {
+		padding: 20rpx 0rpx;
+	}
+
+	.scroll-view {
+		max-height: 600rpx;
+		overflow: hidden;
+	}
+
+	.card_item_option {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 20rpx;
+		border-bottom: 1rpx solid #e4e7ed;
+	}
+
+	.nomatchingData {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		padding: 20rpx;
+		color: #c8c9cc;
+	}
+</style>

+ 211 - 0
pages/order/logisticsDetails.vue

@@ -0,0 +1,211 @@
+<template>
+	<!-- 物流详情 -->
+	<view class="card_logist">
+		<u-navbar title="物流详情" autoBack placeholder></u-navbar>
+		<view style="position: absolute;top: 0;left: 0;right: 0;bottom: 0;">
+			<map style="width: 100%;height: 100%;" id="container" :latitude="latitude" :longitude="longitude"
+				:scale="scale" :polyline="polylineList" :markers="markersList">
+				<view slot="loading">
+					<view class="loading-icon">加载中...</view>
+				</view>
+			</map>
+		</view>
+		<view class="card_logistics">
+			<view class="center_in img_head" @click="clickImg">
+				<u-image :class="logisticsFlag ? '' : 'overturn'" src="../../static/unfold.png" width="80rpx"
+					height="50rpx"></u-image>
+			</view>
+			<view v-if="logisticsFlag">
+				<view class="title_logistics">物流详情</view>
+				<view class="card_steps" :key="Math.random()" v-if="activitiesList.length > 0">
+					<x-steps :infoList="activitiesList"></x-steps>
+				</view>
+				<view v-else style="padding-bottom: 20rpx;">
+					<u-empty mode="data" text="暂无物流信息"></u-empty>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				orderList: {},
+				trackList: [],
+				map: null,
+				longitude: 106.6282014,
+				latitude: 26.64669,
+				scale: 14,
+				activitiesList: [],
+				active: 0,
+				statusList: [{
+					id: 1,
+					title: '已下单',
+				}, {
+					id: 2,
+					title: '配送中',
+				}, {
+					id: 3,
+					title: '已送达',
+				}, {
+					id: 4,
+					title: '已取消',
+				}, {
+					id: 5,
+					title: '已拒收',
+				}],
+				polylineList: [],
+				markersList: [],
+				logisticsFlag: true,
+			}
+		},
+		mounted() {
+			var orderList = this.$cache.getCache('orderDetails')
+			this.orderList = orderList
+			this.$nextTick(() => {
+				setTimeout(() => {
+					this.getList()
+				})
+			})
+		},
+		methods: {
+			getList() {
+				this.$api.get('/api/waybill-logistics', {
+					waybillNo: this.orderList.waybillNo,
+				}).then(res => {
+					if (res.code == 200) {
+						var arrList = res.data.list
+						arrList.forEach(async (item) => {
+							item.backgroundColor = '#ffffff'
+							item.text = ''
+							let arr = ''
+							this.statusList.forEach((item1) => {
+								if (item.status == item1.id) {
+									arr = item1.title
+								}
+							});
+							let yonTitle = ''
+							if (item.coolerBox.id) {
+								yonTitle = item.coolerBox.name
+							}
+							item.text = item.address + '【' + yonTitle + '】' + '  ' + '您的货物' + arr
+						})
+						arrList[0].backgroundColor = '#2979ff'
+						this.activitiesList = arrList.reverse()
+						this.getTrack()
+					}
+				})
+			},
+			// 获取轨迹
+			getTrack() {
+				this.$api.get('/api/waybill-task/locus', {
+					waybillNo: this.orderList.waybillNo,
+				}).then(res => {
+					if (res.code == 200) {
+						let arr = res.data
+						let arrList = []
+						if (arr.length > 0) {
+							arr.forEach(item => {
+								const arr1 = item.T_site.split(',')
+								const list = {
+									latitude: arr1[1],
+									longitude: arr1[0],
+								}
+								arrList.push(list)
+							})
+
+							let polyline = [{
+								width: 30,
+								points: arrList,
+								color: '#3591FC',
+								arrowLine: true,
+								arrowIconPath: '/static/task/arrows.png',
+							}]
+							this.polylineList = polyline
+							const markers0 = {
+								latitude: arrList[0].latitude,
+								longitude: arrList[0].longitude,
+								id: 0,
+								iconPath: '/static/task/startpoint.png',
+								width: 20,
+								height: 23
+							}
+							const markers1 = {
+								latitude: arrList[arrList.length - 1].latitude,
+								longitude: arrList[arrList.length - 1].longitude,
+								id: 1,
+								iconPath: '/static/task/endpoint.png',
+								width: 20,
+								height: 23
+							}
+							this.markersList[0] = markers0
+							this.markersList[1] = markers1
+							this.latitude = arrList[arrList.length - 1].latitude
+							this.longitude = arrList[arrList.length - 1].longitude
+							// console.log(this.polylineList, this.markersList, 266)
+							this.$forceUpdate()
+						}
+					}
+				})
+			},
+			clickImg() {
+				if (this.logisticsFlag) {
+					this.logisticsFlag = false
+				} else {
+					this.logisticsFlag = true
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	#map {
+		width: 100%;
+		height: 500rpx;
+	}
+
+	.loading-icon {
+		color: #333;
+		font-size: 28rpx;
+		padding: 10px;
+		background-color: rgba(255, 255, 255, 0.8);
+		border-radius: 5px;
+		text-align: center;
+	}
+
+	.card_logist ::v-deep .u-navbar__content {
+		z-index: 2024;
+	}
+
+	.card_logistics {
+		position: absolute;
+		bottom: 0;
+		z-index: 2004;
+		width: calc(100% - 40rpx);
+		margin: 20rpx 20rpx 0rpx 20rpx;
+		height: auto;
+		background-color: #fff;
+		border-top-right-radius: 10rpx;
+		border-top-left-radius: 10rpx;
+	}
+
+	.img_head {
+		padding: 10rpx 0rpx;
+	}
+
+	.title_logistics {
+		font-weight: 600;
+		padding: 0rpx 20rpx 0rpx 20rpx;
+	}
+
+	.card_steps {
+		padding: 20rpx;
+	}
+
+	.overturn {
+		transform: rotate(180deg);
+	}
+</style>

+ 243 - 0
pages/order/orderDetails.vue

@@ -0,0 +1,243 @@
+<template>
+	<!-- 订单详情 -->
+	<view>
+		<u-navbar title="" autoBack placeholder></u-navbar>
+		<view class="card_order_detail">
+			<view class="orderNumber">
+				<view class="card_order_title">
+					<span>运单号:</span>{{orderList.waybillNo}}
+				</view>
+				<span class="card_state"
+					:style="{color:getState(orderList.status)}">{{orderStatus(orderList.status)}}</span>
+			</view>
+			<view class="title_delivery">寄件人:</view>
+			<x-orderCard :list="senderList"></x-orderCard>
+			<view class="title_delivery">收件人:</view>
+			<x-orderCard :list="consigneeList"></x-orderCard>
+			<view class="card_humiture">
+				<view class="title_claim" v-if="orderList.coolerBox.id">
+					<span>保温箱:</span>
+					<view style="width: auto;">
+						<u-tag :text="orderList.coolerBox.name" plain size="mini" type="success"></u-tag>
+					</view>
+				</view>
+				<view class="card_anti_dismantle">
+					<span>防拆标签图片:</span>
+					<view style="display: flex;">
+						<view style="margin-right: 20rpx;" v-for="(item,index) in getimagetamper(orderList)" :key="index">
+							<u-image radius="4" :showLoading="true" :src="item" width="80px" height="80px"></u-image>
+						</view>
+					</view>
+				</view>
+				<view class="title_claim">
+					<span>防拆标签码:</span>
+					<view style="width: auto;font-size: 28rpx;">
+						{{orderList.tamperProofLabel}}
+					</view>
+				</view>
+				<view class="title_claim"><span>备注:</span>{{orderList.remark}}</view>
+			</view>
+			<view class="card_humiture1" v-if="orderList.status == 8">
+				<view class="order_iamge_item" v-for="(item,index) in srcList" :key="index">
+					<u-image :showLoading="true" :src="item.url" width="80px" height="80px" radius="2"></u-image>
+					<view class="order_item_title">{{item.title}}</view>
+				</view>
+			</view>
+			<view v-if="orderList.status != 1">
+				<view class="line_back" style="margin-top: 40rpx;" @click="logistics">查看物流详情</view>
+				<view class="line_back" @click="goHumiture">查看温湿度</view>
+			</view>
+			<view style="width: 100%;height: 80rpx;"></view>
+		</view>
+		<!-- <view class="btn_print" v-if="type == 'details' && orderList.status == 2 || orderList.status == 3">
+			<u-button type="primary" text="打印条码"></u-button>
+		</view> -->
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				type: 'details',
+				senderList: {
+					name: '',
+					phone: '',
+					address: '',
+				},
+				consigneeList: {
+					name: '',
+					phone: '',
+					address: '',
+				},
+				orderList: {},
+				srcList: [{
+					title: '运单签收图片',
+					url: '',
+				}, {
+					title: '随货通行单图片',
+					url: '',
+				}, {
+					title: '冷链交接单图片',
+					url: '',
+				}],
+				userInfo: {},
+			}
+		},
+		onLoad(value) {
+			var userInfo = this.$cache.getCache('userInfo')
+			this.userInfo = userInfo
+			this.type = value.type
+		},
+		onShow() {
+			var orderList = this.$cache.getCache('orderDetails')
+			this.orderList = orderList
+			this.senderList.name = orderList.senderAddressName
+			this.senderList.phone = orderList.senderAddressPhone
+			this.senderList.address = orderList.senderAddressDetails
+			this.consigneeList.name = orderList.consigneeAddressName
+			this.consigneeList.phone = orderList.consigneeAddressPhone
+			this.consigneeList.address = orderList.consigneeAddressDetails
+			if (orderList.status == 8) {
+				const arr = orderList.ReceiptImg.split(',')
+				this.srcList[0].url = arr[0]
+				this.srcList[1].url = arr[1]
+				this.srcList[2].url = arr[2]
+			}
+		},
+		methods: {
+			// 物流详情
+			logistics() {
+				uni.navigateTo({
+					url: '/pages/order/logisticsDetails'
+				});
+			},
+			// 温湿度记录
+			goHumiture() {
+				uni.navigateTo({
+					url: '/pages/order/humiture'
+				});
+			},
+			// 订单状态
+			orderStatus(value) {
+				if (value == 1) {
+					return '已下单'
+				} else if (value == 2) {
+					return '配送中'
+				} else if (value == 3) {
+					return '已送达'
+				} else if (value == 4) {
+					return '已取消'
+				}
+			},
+			// 订单文字颜色
+			getState(value) {
+				if (value == 1) {
+					return '#9ddd54'
+				} else if (value == 2) {
+					return '#4bc7fc'
+				} else if (value == 3) {
+					return '#1cc723'
+				} else if (value == 4) {
+					return '#fe880e'
+				}
+			},
+			getimagetamper(value) {
+				let arr = value.tamperProofLabelImg.split(',')
+				return arr
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.card_order_detail {
+		margin: 20rpx;
+	}
+
+	.orderNumber {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		background-color: #fff;
+		border-radius: 20rpx;
+		padding: 15rpx;
+	}
+
+	.card_order_title {
+		span {
+			font-size: 28rpx;
+			color: #909399;
+			margin-right: 10rpx;
+		}
+	}
+
+	.card_state {
+		color: #606266;
+		font-size: 30rpx;
+	}
+
+	.title_delivery {
+		font-size: 30rpx;
+		font-weight: 600;
+		margin: 20rpx 0rpx 10rpx 10rpx;
+	}
+
+	.card_humiture {
+		margin-top: 30rpx;
+		border-radius: 20rpx;
+		padding: 1rpx 20rpx;
+		background-color: #fff;
+	}
+
+	.card_humiture1 {
+		display: flex;
+		justify-content: space-around;
+		align-items: center;
+		flex-wrap: wrap;
+		margin-top: 10rpx;
+		border-radius: 20rpx;
+		padding: 20rpx;
+		background-color: #fff;
+	}
+
+	.order_iamge_item {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+	}
+
+	.order_item_title {
+		font-weight: 600;
+		margin-top: 10rpx;
+		font-size: 26rpx;
+	}
+
+	.title_claim {
+		display: flex;
+		margin: 20rpx 0rpx;
+
+		span {
+			font-size: 28rpx;
+			color: #909399;
+			margin-right: 10rpx;
+		}
+	}
+
+	.line_back {
+		font-size: 30rpx;
+		margin: 20rpx 0rpx;
+		color: #3c9cff;
+	}
+
+	.card_anti_dismantle {
+		display: flex;
+		flex-direction: column;
+
+		span {
+			font-size: 28rpx;
+			color: #909399;
+			margin-bottom: 10rpx;
+		}
+	}
+</style>

+ 721 - 0
pages/order/quantum.vue

@@ -0,0 +1,721 @@
+<template>
+	<view>
+		<u-navbar :title="headline" autoBack placeholder></u-navbar>
+		<view class="card_stamp">
+			<view class="title_bluetooth">匹配蓝牙设备</view>
+			<view class="center_in">
+				<x-radar :BlueState="BlueState"></x-radar>
+			</view>
+			<view class="nearby_title">{{nearbyTitle}}</view>
+			<view class="search_card">
+				<u-button style="width: 200rpx;" size="small" type="warning" :text="searchTitle"
+					@click="openBluetoothAdapter(searchType)"></u-button>
+			</view>
+			<view class="space_between">
+				<view class="waybill_num">运单号: {{waybillNo}}</view>
+			</view>
+			<view class="waybill_numil" v-if="printType == 'record'">请选择运单温湿度时间段</view>
+			<view class="card_waybill" v-for="(item,index) in timeQuantumList" :key="index" @click="selectChange(item)">
+				<x-checkbox :label="item.title" :isChecked="item.isChecked"></x-checkbox>
+				<view class="time_title">{{item.startTime}}</view>
+				<view class="time_title">{{item.endTime}}</view>
+			</view>
+			<view class="card_bluetooth" v-if="pairedDeviceList.length > 0">
+				<view class="equipment_title">已配对设备</view>
+				<view class="card_equipment">
+					<x-bluetooth :list="pairedDeviceList" text="打印" btnType="success" @connect="getPrint"></x-bluetooth>
+				</view>
+			</view>
+			<view class="card_bluetooth">
+				<view class="equipment_title">可连接设备</view>
+				<view class="card_equipment">
+					<x-bluetooth :list="devices" @connect="connect"></x-bluetooth>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		printList
+	} from './waybill.js'
+	import PrinterJobs from './gprint/printerjobs.js'
+	import printerUtil from './gprint/printerutil.js'
+	import util from './gprint/util.js'
+	import drawQrcode from './gprint/weapp.qrcode.esm.js'
+	import getCode128 from './gprint/code128'
+	export default {
+		data() {
+			return {
+				headline: '',
+				barCodeShow: false,
+				pairedDeviceList: [],
+				matchingUnit: {},
+				BlueState: true,
+				nearbyTitle: '使用蓝牙设备打印...',
+				searchTitle: '开始搜索',
+				searchType: false,
+				devices: [],
+				deviceId: null,
+				serviceId: null,
+				characteristicId: null,
+				waybillNo: '',
+				printType: '',
+				waybillList: {},
+				humitureData: [],
+				maxData: [],
+				timeQuantumList: [],
+				checkboxValue: [],
+				userInfo: {},
+			}
+		},
+		onLoad(value) {
+			const arr = this.$cache.getCache('commodity')
+			this.waybillList = JSON.parse(arr)
+			var userInfo = this.$cache.getCache('userInfo')
+			this.userInfo = userInfo
+			if (value.printType == 'barCode') {
+				this.headline = '条码打印'
+			} else if (value.printType == 'record') {
+				this.headline = '温湿度记录打印'
+				this.getHumiture(value.waybillNo)
+			}
+			this.waybillNo = value.waybillNo
+			this.printType = value.printType
+		},
+		methods: {
+			openBluetoothAdapter(value) {
+				var that = this
+				if (value) {
+					that.BlueState = true
+					that.searchTitle = '开始搜索'
+					that.nearbyTitle = '已停止蓝牙查找附近设备...'
+					that.searchType = false
+				} else {
+					that.BlueState = false
+					console.log('开始搜索')
+					that.searchTitle = '停止搜索'
+					that.nearbyTitle = '正通过蓝牙查找附近设备...'
+					that.searchType = true
+					uni.openBluetoothAdapter({ //打开蓝牙适配器接口
+						success: (res) => { //已打开
+							that.onDevice() //监听寻找到新设备的事件
+							uni.getBluetoothAdapterState({ //获取本机蓝牙适配器状态
+								success: function(res) {
+									// console.log(res)
+									if (res.available) {
+										//搜索蓝牙
+										//开始搜寻附近的蓝牙外围设备
+										console.log("开始搜寻附近的蓝牙外围设备")
+										uni.startBluetoothDevicesDiscovery({
+											success(res) {
+												console.log(res)
+											}
+										})
+
+									} else {
+										console.log('本机蓝牙不可用')
+									}
+								},
+							})
+						},
+						fail: err => { //未打开 
+							uni.showToast({
+								icon: 'none',
+								title: '查看手机蓝牙是否打开'
+							});
+						}
+					})
+				}
+			},
+			onDevice() {
+				var that = this
+				//监听寻找到新设备的事件
+				uni.onBluetoothDeviceFound(function(devices) {
+					var re = JSON.parse(JSON.stringify(devices))
+					if (re.devices[0].name != '') {
+						let deviceId = re.devices[0].deviceId
+						let name = re.devices[0].name
+						that.devices.push({
+							name: name,
+							deviceId: deviceId,
+							services: []
+						})
+
+						for (let i = 0; i < that.devices.length; i++) {
+							for (let j = i + 1; j < that.devices.length; j++) {
+								if (that.devices[i].deviceId == that.devices[j].deviceId) {
+									that.devices.splice(j, 1);
+									j--;
+								}
+							}
+						}
+						that.devices = that.devices
+					}
+				})
+			},
+			stopFindBule() {
+				const that = this
+				uni.stopBluetoothDevicesDiscovery({
+					success: e => {
+						that.searchTitle = '开始搜索'
+						that.nearbyTitle = '已停止蓝牙查找附近设备...'
+						that.searchType = false
+						that.BlueState = true
+						console.log('停止搜索蓝牙设备:' + e.errMsg);
+					},
+					fail: e => {
+						console.log('停止搜索蓝牙设备失败,错误码:' + e.errCode);
+					}
+				});
+			},
+			connect(value) {
+				// console.log(value, 66)
+				this.createBLEConnection(value)
+			},
+			// item 是要连接的设备数据
+			createBLEConnection(item) {
+				let that = this;
+				that.matchingUnit.deviceId = item.deviceId
+				uni.showLoading({
+					title: "连接中,请稍等",
+					mask: true,
+				});
+				uni.createBLEConnection({
+					deviceId: item.deviceId,
+					success(res) {
+						that.stopFindBule(); // 停止搜索蓝牙
+						setTimeout(function() {
+							that.getBLEDeviceServices(item); // 获取蓝牙的服务
+						}, 2000)
+					},
+					fail(res) {
+						uni.showToast({
+							title: item.name + "蓝牙连接失败",
+							icon: "none",
+						});
+						uni.hideLoading()
+					}
+				});
+			},
+			getBLEDeviceServices(item) {
+				const that = this
+				setTimeout(() => {
+					uni.getBLEDeviceServices({
+						// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+						deviceId: item.deviceId,
+						complete(res) {
+							let serviceId = ""
+							if (res.services.length > 0) {
+								for (var s = 0; s < res.services.length; s++) {
+									let serviceId = res.services[s].uuid
+									that.matchingUnit.serviceId = serviceId
+									uni.getBLEDeviceCharacteristics({ //获取蓝牙设备某个服务中所有特征值(characteristic)
+										// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+										deviceId: item.deviceId,
+										// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
+										serviceId: serviceId,
+										success(res) {
+											// console.log('获取服务成功')
+											that.pairedDeviceList = []
+											that.pairedDeviceList.push(item)
+											var re = JSON.parse(JSON.stringify(res))
+											for (var c = 0; c < re.characteristics.length; c++) {
+												if (re.characteristics[c].properties.write ==
+													true) {
+													let uuid = re.characteristics[c].uuid
+													that.matchingUnit.characteristicId = uuid
+													break
+												}
+											}
+											uni.hideLoading()
+										}
+									})
+								}
+							} else {
+								uni.hideLoading()
+							}
+						},
+						fail(res) {
+							console.log(res)
+						},
+					})
+				});
+			},
+			// 断开连接
+			closeBLEConnection(item) {
+				uni.closeBLEConnection({
+					deviceId: item.deviceId,
+					success: (res) => {
+						// res.characteristics 特征值列表
+						// 读写都需要用到特征值
+					},
+					fail: (res) => {
+						console.log(res);
+					},
+				})
+			},
+			// 配对设备打印
+			async getPrint(value) {
+				this.deviceId = this.matchingUnit.deviceId
+				this.serviceId = this.matchingUnit.serviceId
+				this.characteristicId = this.matchingUnit.characteristicId
+				if (this.printType == 'barCode') {
+					const showFirstName = (name) => {
+						let newStr;
+						if (name.length === 2) {
+							newStr = name.substr(0, 1) + '*';
+						} else if (name.length > 2) {
+							let char = '';
+							for (let i = 0, len = name.length - 1; i < len; i++) {
+								char += '*';
+							}
+							newStr = name.substr(0, 1) + char;
+						} else {
+							newStr = name;
+						}
+						return newStr;
+					}
+					const shippingAddress = (addres) => {
+						let title;
+						if (addres.length < 16) {
+							title = addres + ' \n'
+						} else {
+							title = addres
+						}
+						return title
+					}
+					let printerJobs = new PrinterJobs();
+					let codeValue = this.waybillNo
+					const ENV = require('../../.env.js')
+					let logistics = ENV.APP_LINK_URL + '/newInquiry?waybillNo=' + codeValue
+					let code128 = getCode128(codeValue);
+					let numArr = []
+					if (this.waybillList.quantity != 0) {
+						for (var i = 0; i < this.waybillList.quantity; i++) {
+							let numa = i + 1
+							numArr.push(numa)
+						}
+					} else {
+						let numa = 1
+						numArr.push(numa)
+					}
+					numArr.forEach((numitem, indexnum) => {
+						printerJobs
+							.setSize(1, 1)
+							.setAlign('LT')
+							.print('#' + this.userInfo.dept.name)
+							.setAlign('CT')
+							.printBarcode(code128)
+							.setSize(1, 1)
+							.setAlign('LT')
+							.text('  ')
+							.text(codeValue + ' \n')
+							.print(printerUtil.fillLine())
+							.setAlign('LT')
+							.setSize(1, 2)
+							.setSize(2, 1)
+							.setBold()
+							.text('收:')
+							.setSize(1, 1)
+							.setBold(false)
+							.text(' ' + showFirstName(this.waybillList.senderAddressName))
+							.text(' ' + this.waybillList.senderAddressPhone.substr(0, 3) + "****" + this
+								.waybillList
+								.senderAddressPhone.substr(7) + ' \n')
+							.print(shippingAddress(this.waybillList.senderAddressDetails))
+							.print(printerUtil.fillLine())
+							.setSize(1, 2)
+							.setSize(2, 1)
+							.setBold()
+							.text('寄:')
+							.setSize(1, 1)
+							.setBold(false)
+							.text(' ' + showFirstName(this.waybillList.consigneeAddressName))
+							.text(' ' + this.waybillList.consigneeAddressPhone.substr(0, 3) + "****" + this
+								.waybillList
+								.consigneeAddressPhone.substr(7) + ' \n')
+							.print(shippingAddress(this.waybillList.consigneeAddressDetails))
+							.print(printerUtil.fillLine())
+							.text('备注:')
+							.text('防拆标签码:' + this.waybillList.tamperProofLabel + ' \n')
+							.setAlign('CT')
+							.printQrcode(logistics)
+							.print('扫码查询物流温湿度信息 \n')
+							.println()
+					})
+					let buffer = printerJobs.buffer();
+					this.printbuffs(buffer);
+				} else {
+					let printerJobs = new PrinterJobs();
+					printerJobs
+						.setSize(1, 1)
+						.setAlign('lt')
+						.print('#' + this.userInfo.dept.name)
+						.print('运单号:' + this.waybillNo)
+						.print('货物类型:' + this.waybillList.cargoType)
+						.print('温度需求:' + this.waybillList.temperatureInterval)
+						.print('配送要求:' + this.waybillList.deliveryCondition)
+						.print('寄件人:' + this.waybillList.senderAddressName)
+						.print('收件人:' + this.waybillList.consigneeAddressName)
+						.print(printerUtil.fillLine())
+					this.humitureData.forEach((item, index) => {
+						// console.log(item,123)
+						const exists = this.checkboxValue.some(Tid => Tid === item.id);
+						if (item.isChecked) {
+							// printerJobs
+							// .print('设备号:' + item.sn)
+							// .print('标识名:' + item.title)
+							// .print('开始时间:' + item.startTime)
+							// .print('结束时间:' + item.endTime)
+							// .print(printerUtil.fillLine())
+							item.arr.forEach((item1, index1) => {
+								printerJobs
+									.print('日期:' + item1.time + '单位℃ ')
+									.text('时间 ')
+									.text('|')
+									.text(' ')
+									.text('T1  ')
+									.text(' ')
+									.text('|')
+									.text(' ')
+									.text('T2  ')
+									.text(' ')
+									.text('|')
+									.text(' ')
+									.text('T3  ')
+									.text(' ')
+									.text('|')
+									.text(' ')
+									.text('T4  \n')
+								item1.arr.forEach((item2, index2) => {
+									printerJobs
+										.text(`${item2[0].time}`)
+										.text('|')
+										.text(' ')
+										.text(
+											`${item2[0] == '---' ? '----' : character(item2[0].T_t) }`
+										)
+										.text(' ')
+										.text('|')
+										.text(' ')
+										.text(
+											`${item2[1] == '---' ? '----' : character(item2[1].T_t) }`
+										)
+										.text(' ')
+										.text('|')
+										.text(' ')
+										.text(
+											`${item2[2] == '---' ? '----' : character(item2[2].T_t) }`
+										)
+										.text(' ')
+										.text('|')
+										.text(' ')
+										.text(
+											`${item2[3] == '---' ? '----' : character(item2[3].T_t) }`
+										)
+
+									function character(value) {
+										var arr = value.toFixed(1)
+										if (arr.length == 3) {
+											var arr1 = arr + ' '
+											return arr1
+										} else {
+											return arr
+										}
+									}
+								})
+							})
+							// printerJobs.println()
+						}
+					})
+					printerJobs.println()
+					let buffer = printerJobs.buffer();
+					this.printbuffs(buffer);
+				}
+			},
+			printbuffs(buffer) {
+				// 1.并行调用多次会存在写失败的可能性
+				// 2.建议每次写入不超过20字节
+				// 分包处理,延时调用
+				const maxChunk = 20;
+				const delay = 20;
+				for (let i = 0, j = 0, length = buffer.byteLength; i < length; i += maxChunk, j++) {
+					let subPackage = buffer.slice(i, i + maxChunk <= length ? (i + maxChunk) : length);
+					setTimeout(this.printbuff, j * delay, subPackage);
+				}
+			},
+			printbuff(buffer) {
+				let deviceId = this.deviceId;
+				let serviceId = this.serviceId;
+				let characteristicId = this.characteristicId;
+				// console.log(deviceId, serviceId, characteristicId, 333)
+				return new Promise((resolve, reject) => {
+					uni.writeBLECharacteristicValue({
+						deviceId,
+						serviceId,
+						characteristicId,
+						value: buffer,
+						success(res) {
+							//console.log('message发送成功', JSON.stringify(res));
+							resolve(res);
+						},
+						fail(err) {
+							console.log('message发送失败', JSON.stringify(err));
+							reject(err);
+						}
+					});
+				});
+			},
+			// 获取设备信息探头
+			getHumiture(waybillNo) {
+				this.checkboxValue = []
+				this.$api.get('/api/waybill-task', {
+					waybillNo: waybillNo,
+				}).then(res => {
+					if (res.code == 200) {
+						const arrlsist = res.data.list
+						let arrList = []
+						arrlsist.forEach((item, index) => {
+							let dataList = {}
+							dataList.id = item.id
+							dataList.sn = item.sn
+							dataList.deviceSensorList = item.deviceSensorList
+							dataList.startTime = item.startTime
+							dataList.endTime = item.endTime
+							if (item.car.id) {
+								dataList.title = item.car.carNo
+							} else if (item.warehouse.id) {
+								dataList.title = item.warehouse.name
+							}
+							arrList.push(dataList)
+						})
+						this.timeQuantumList = arrList
+						this.timeQuantumList.forEach((item1, index1) => {
+							item1.isChecked = true
+							this.checkboxValue.push(item1.id)
+						})
+						this.humitureInfo(arrList)
+					}
+				})
+			},
+			// 选择打印
+			selectChange(value) {
+				this.timeQuantumList.forEach((item, index) => {
+					if (value.id == item.id) {
+						if (value.isChecked) {
+							item.isChecked = false
+						} else {
+							item.isChecked = true
+						}
+					}
+					if (item.isChecked) {
+						this.checkboxValue = []
+						this.checkboxValue.push(item.id)
+					}
+				})
+				// console.log(this.humitureData,'------')
+				this.$forceUpdate()
+			},
+			// 获取温湿度信息
+			humitureInfo(value) {
+				// console.log(printList(), 2666)
+				// console.log(value, 25)
+				let arrData = value
+				let arrData1 = printList()
+				arrData.forEach((item, index) => {
+					let arr = []
+					item.deviceSensorList.forEach((ote, te) => {
+						arr.push(ote.T_id)
+					})
+					let params = {
+						t_ids: arr,
+						taskId: item.id,
+						waybillNo: this.waybillNo,
+						page: 1,
+						pageSize: 9999,
+					}
+					// console.log(item,999)
+					item.arr = []
+					this.getWaybillTask(params).then(res => {
+						const arrL = JSON.parse(JSON.stringify(res))
+						const timeList = JSON.parse(JSON.stringify(res))
+						const arrLil = res
+						for (let i = 0; i < arrLil.length; i++) {
+							for (let j = i + 1; j < arrLil.length; j++) {
+								if (arrLil[i].T_time == arrLil[j].T_time) {
+									arrLil.splice(j, 1);
+									j--;
+								}
+							}
+						}
+						var resultArr = arrLil
+						let list1 = []
+						resultArr.forEach(k => {
+							let arr1 = {
+								T_sn: k.T_sn,
+								time: k.T_time,
+								arr: [],
+							}
+							list1.push(arr1)
+						})
+						this.maxData = list1
+						for (let i = 0; i < timeList.length; i++) {
+							for (let j = i + 1; j < timeList.length; j++) {
+								if (timeList[i].time == timeList[j].time) {
+									timeList.splice(j, 1);
+									j--;
+								}
+							}
+						}
+						this.maxData.forEach((item3, index3) => {
+							timeList.forEach((item2, index2) => {
+								const result1 = arrL.find(fruit => fruit.T_id === 1 &&
+									fruit.time === item2.time && fruit.T_time === item3
+									.time)
+								const result2 = arrL.find(fruit => fruit.T_id === 2 &&
+									fruit.time === item2.time && fruit.T_time === item3
+									.time)
+								const result3 = arrL.find(fruit => fruit.T_id === 3 &&
+									fruit.time === item2.time && fruit.T_time === item3
+									.time)
+								const result4 = arrL.find(fruit => fruit.T_id === 4 &&
+									fruit.time === item2.time && fruit.T_time === item3
+									.time)
+
+								const flag = areAllUndefined(result1, result2, result3,
+									result4)
+								if (!flag) {
+									let list2 = [
+										result1 == undefined ? '---' : result1,
+										result2 == undefined ? '---' : result2,
+										result3 == undefined ? '---' : result3,
+										result4 == undefined ? '---' : result4,
+									]
+									item3.arr.push(list2)
+								}
+
+								function areAllUndefined(...args) {
+									return args.every(arg => arg === undefined);
+								}
+							})
+						})
+						item.arr = this.maxData
+					})
+				})
+				this.humitureData = arrData
+				// console.log(this.humitureData, 26)
+			},
+			getWaybillTask(params) {
+				return new Promise((resolve, reject) => {
+					this.$api.post('/api/waybill-task/data', params).then(res => {
+						if (res.code == 200) {
+							let arr1 = res.data.list
+							if (arr1) {
+								let arr2 = []
+								arr1.forEach(item1 => {
+									const dateString = item1.T_time;
+									const parts = dateString.split(" ")[0].split("-");
+									const extractedDate = parts[0] + "-" + parts[1] + "-" + parts[
+										2];
+									const parts1 = dateString.split(" ")[1].split(":");
+									const extractedTime = parts1[0] + ":" + parts1[1];
+									let arr3 = {
+										T_sn: item1.T_sn,
+										T_id: item1.T_id,
+										T_time: extractedDate,
+										T_t: item1.T_t,
+										T_rh: item1.T_rh,
+										time: extractedTime
+									}
+									arr2.push(arr3)
+								})
+								resolve(arr2)
+							}
+						}
+					})
+				});
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	page {
+		background-color: #fff;
+	}
+
+	.title_bluetooth {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		font-size: 40rpx;
+		margin: 20rpx;
+		font-weight: 600;
+	}
+
+	.nearby_title {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		font-size: 30rpx;
+		margin: 20rpx;
+		font-weight: 600;
+	}
+
+	.card_stamp {
+		display: flex;
+		flex-direction: column;
+		padding: 30rpx 20rpx;
+	}
+
+	.search_card {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.card_bluetooth {
+		margin-top: 20rpx;
+	}
+
+	.equipment_title {
+		font-size: 28rpx;
+		font-weight: 600;
+	}
+
+	.card_equipment {
+		margin-top: 20rpx;
+		min-height: 90rpx;
+		box-shadow: 0 4rpx 24rpx 0 rgba(0, 0, 0, 0.1);
+		padding: 10rpx 20rpx;
+		border-radius: 10rpx;
+	}
+
+	.waybill_num {
+		margin-top: 20rpx;
+		font-size: 30rpx;
+		font-weight: 600;
+	}
+
+	.waybill_numil {
+		margin-top: 10rpx;
+		font-size: 28rpx;
+		color: #767a82;
+	}
+
+	.time_title {
+		margin-top: 5rpx;
+		font-size: 28rpx;
+	}
+
+	.card_waybill {
+		display: flex;
+		flex-direction: column;
+		box-shadow: 1rpx 2rpx 14rpx rgba(0, 0, 0, .12);
+		padding: 20rpx;
+		border-radius: 10rpx;
+		margin-top: 20rpx;
+	}
+</style>

+ 133 - 0
pages/order/waybill.js

@@ -0,0 +1,133 @@
+export const formRules = () => {
+	return [{
+		field: 'tamperProofLabel',
+		label: '防拆标签码',
+		placeholder: '请输入防拆标签码',
+		type: 'input',
+		required: true,
+	}, {
+		field: 'tamperProofLabelImg',
+		label: '防拆标签图片',
+		placeholder: '请上传防拆标签图片',
+		type: 'upload',
+		required: true,
+	}, {
+		field: 'remark',
+		label: '备注',
+		placeholder: '备注',
+		type: 'textarea',
+	}]
+}
+
+export const pickupRulesil = () => {
+	return [{
+		field: 'name',
+		label: '姓名',
+		placeholder: '请输入姓名',
+		type: 'input',
+		required: true,
+	}, {
+		field: 'phone',
+		label: '电话',
+		placeholder: '请输入电话',
+		type: 'input',
+		required: true,
+	}, {
+		field: 'address',
+		label: '地址',
+		placeholder: '请输入地址',
+		type: 'input',
+		required: true,
+	}]
+}
+export const printList = () => {
+	return [{
+		arr: [{
+			arr: [
+				[{
+						T_sn: '2024117235321460',
+						T_id: 1,
+						T_rh: 59.8,
+						T_t: 3.1,
+						T_time: '2024-04-24',
+						time: '15:30',
+					},
+					{
+						T_sn: '2024117235321460',
+						T_id: 2,
+						T_rh: 59.8,
+						T_t: 2.1,
+						T_time: '2024-04-24',
+						time: '15:30',
+					},
+					{
+						T_sn: '2024117235321460',
+						T_id: 3,
+						T_rh: 59.8,
+						T_t: 23.1,
+						T_time: '2024-04-24',
+						time: '15:30',
+					},
+					{
+						T_sn: '2024117235321460',
+						T_id: 4,
+						T_rh: 59.8,
+						T_t: 23.1,
+						T_time: '2024-04-24',
+						time: '15:30',
+					}
+				]
+			],
+			time: "2024-04-24"
+		}],
+		id: 38,
+		title: '测试',
+		isChecked: true,
+		endTime: '2024-04-24 15:31:18',
+		startTime: '2024-04-24 15:31:18'
+	}, {
+		arr: [{
+			arr: [
+				[{
+						T_sn: '2024117235321460',
+						T_id: 1,
+						T_rh: 59.8,
+						T_t: 3.1,
+						T_time: '2024-04-24',
+						time: '15:30',
+					},
+					{
+						T_sn: '2024117235321460',
+						T_id: 2,
+						T_rh: 59.8,
+						T_t: 2.1,
+						T_time: '2024-04-24',
+						time: '15:30',
+					},
+					{
+						T_sn: '2024117235321460',
+						T_id: 3,
+						T_rh: 59.8,
+						T_t: 23.1,
+						T_time: '2024-04-24',
+						time: '15:30',
+					},
+					{
+						T_sn: '2024117235321460',
+						T_id: 4,
+						T_rh: 59.8,
+						T_t: 23.1,
+						T_time: '2024-04-24',
+						time: '15:30',
+					}
+				]
+			],
+			time: "2024-04-24"
+		}],
+		id: 37,
+		title: '测试1',
+		isChecked: true,
+		endTime: '2024-04-24 15:31:18',
+		startTime: '2024-04-24 15:31:18'
+	}, ]
+}

+ 267 - 0
pages/register.vue

@@ -0,0 +1,267 @@
+<template>
+	<view>
+		<u-navbar title="" autoBack placeholder></u-navbar>
+		<view class="card_login">
+			<view class="card_head_logo">
+				<image class="mine_image" src="../static/logo.png" mode=""></image>
+				<view class="welcome_title">欢迎注册宝智达冷链运输</view>
+			</view>
+			<u-form labelPosition="left" :model="formList" :rules="rules" ref="uForm">
+				<u-form-item label="类型" prop="type" labelWidth="80" borderBottom ref="item1">
+					<u-radio-group v-model="formList.type" placement="row">
+						<u-radio shape="circle" :customStyle="{marginRight: '20px'}" v-for="(item, index) in radiolist"
+							:key="index" :label="item.name" :name="item.value"></u-radio>
+					</u-radio-group>
+				</u-form-item>
+				<u-form-item label="手机号" prop="phone" labelWidth="80" borderBottom ref="item1">
+					<u-input v-model="formList.phone" border="none" placeholder="请填写手机号"></u-input>
+				</u-form-item>
+				<u-form-item label="验证码" prop="verifyCode" labelWidth="80" borderBottom>
+					<u-input v-model="formList.verifyCode" border="none" placeholder="请填写验证码"></u-input>
+					<u-button slot="right" @tap="getCode" :text="tips" type="success" size="mini"
+						:disabled="disabled1"></u-button>
+				</u-form-item>
+				<u-form-item label="密码" prop="password" labelWidth="80" borderBottom ref="item1">
+					<u-input v-show="isText === true" placeholder="请填写密码" border="none" v-model="formList.password"
+						class="input_class_pwd" :password="true">
+						<template slot="suffix" v-if="formList.password">
+							<u-icon name="eye-off" @click="isText = false" size="20"></u-icon>
+						</template>
+					</u-input>
+					<u-input v-show="isText === false" placeholder="请填写密码" border="none" v-model="formList.password"
+						class="input_class_pwd" :password="false">
+						<template slot="suffix" v-if="formList.password">
+							<u-icon name="eye-fill" @click="isText = true" size="20"></u-icon>
+						</template>
+					</u-input>
+				</u-form-item>
+			</u-form>
+			<u-code ref="uCode" @change="codeChange" seconds="90" @start="disabled1 = true"
+				@end="disabled1 = false"></u-code>
+			<view class="title_register" @click="goLogin">去登录</view>
+			<view class="card_btn">
+				<u-button type="primary" :disabled="disabled" text="注册" @click="getRegister"></u-button>
+			</view>
+		</view>
+		<!-- <view class="wx_card">
+			<view class="card_btn_wx frame">
+				<u-icon name="weixin-fill" size="26" color="#07c160"></u-icon>
+			</view>
+			<span class="title_login_wx">微信登录</span>
+		</view> -->
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				radiolist: [{
+					name: '私人',
+					value: 1,
+				}, {
+					name: '商户',
+					value: 0,
+				}],
+				formList: {
+					type: 1,
+					phone: '',
+					verifyCode: '',
+					password: '',
+				},
+				tips: '',
+				disabled1: false,
+				rules: {
+					phone: {
+						type: 'string',
+						required: true,
+						message: '请填写手机号',
+						trigger: ['blur']
+					},
+					verifyCode: {
+						type: 'string',
+						required: true,
+						message: '请填写验证码',
+						trigger: ['blur']
+					},
+					password: {
+						type: 'string',
+						required: true,
+						message: '请填写密码',
+						trigger: ['blur']
+					},
+				},
+				isText: true,
+			}
+		},
+		computed: {
+			disabled() {
+				if (this.formList.phone == '' || this.formList.verifyCode == '' || this.formList.password == '') {
+					return true;
+				} else {
+					return false;
+				}
+			},
+		},
+		methods: {
+			// 注册
+			getRegister() {
+				this.$refs.uForm.validate().then(res => {
+					uni.showLoading();
+					this.$api.post('/api/applet/register', {
+						phone: this.formList.phone,
+						password: this.formList.password,
+						verifyCode: this.formList.verifyCode
+					}).then((res) => {
+						if (res.code == 200) {
+							uni.showToast({
+								title: res.msg,
+								icon: 'none',
+								duration: 2000
+							});
+							this.formList.phone = ''
+							this.formList.password = ''
+							this.formList.verifyCode = ''
+						} else {
+							this.formList.phone = ''
+							this.formList.password = ''
+							this.formList.verifyCode = ''
+							uni.showToast({
+								title: res.data.msg,
+								icon: 'none',
+								duration: 2000
+							});
+						}
+						uni.hideLoading();
+					})
+				}).catch(errors => {
+					// uni.$u.toast('校验失败')
+				})
+			},
+			getCode() {
+				if (this.$refs.uCode.canGetCode) {
+					// 模拟向后端请求验证码
+					if (this.formList.phone) {
+						uni.showLoading({
+							title: '正在获取验证码'
+						})
+						// 通知验证码组件内部开始倒计时
+						this.$refs.uCode.start();
+						this.$api.get('/api/verify-code', {
+							phone: this.formList.phone,
+						}).then(res => {
+							if (res.code == 200) {
+								// 这里此提示会被this.start()方法中的提示覆盖
+								uni.$u.toast('验证码已发送');
+							}
+							uni.hideLoading();
+						})
+					} else {
+						uni.$u.toast('请先输入手机号');
+					}
+				} else {
+					uni.$u.toast('倒计时结束后再发送');
+				}
+			},
+			codeChange(text) {
+				this.tips = text;
+			},
+			// 登录
+			goLogin() {
+				// uni.redirectTo({
+				// 	url: '/pages/login'
+				// })
+				uni.navigateBack({
+					delta: 1
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	page {
+		background-color: #fff !important;
+	}
+
+	.welcome_title {
+		font-size: 46rpx;
+		font-weight: 600;
+		margin-bottom: 60rpx;
+		margin-top: 10rpx;
+	}
+
+	.card_head_logo {
+		margin-top: 50rpx;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.card_login {
+		padding: 20rpx 40rpx;
+	}
+
+	.mine_image {
+		width: 100rpx;
+		height: 100rpx;
+	}
+
+	.title_register {
+		margin-top: 20rpx;
+		display: flex;
+		justify-content: flex-end;
+		font-size: 28rpx;
+		color: #494949;
+	}
+
+	.card_hint {
+		margin-top: 60rpx;
+		display: flex;
+		align-items: flex-start;
+	}
+
+	.agreement_title {
+		font-size: 30rpx;
+		color: #333;
+	}
+
+	.protocol_title {
+		color: #1177ff;
+	}
+
+	.card_btn {
+		margin-top: 50rpx;
+	}
+
+	.card_checkbox {
+		margin-top: 5rpx;
+		position: relative;
+		cursor: pointer;
+	}
+
+	.message {
+		display: none;
+		position: absolute;
+		padding: 5px 8px;
+		border: 1px solid #494949;
+		border-radius: 9px;
+		top: -36px;
+		font-size: 12px;
+		width: 110px;
+		background: #494949;
+		color: #fff;
+		left: -6px;
+	}
+
+	.message::after {
+		content: '';
+		position: absolute;
+		left: 8px;
+		top: 100%;
+		border: 8px solid transparent;
+		border-top: 6px solid #494949;
+		margin-left: -2px;
+	}
+</style>

+ 6 - 0
smartAppversion.json

@@ -0,0 +1,6 @@
+{
+	"code": 0,
+	"msg": "success",
+	"version": "505", // 线上版本号
+	"url": "https://coldlogistics.coldbaozhida.com/冷链物流.apk" // 更新下载地址
+}

BIN
static/back.png


+ 65 - 0
static/css/styles.scss

@@ -0,0 +1,65 @@
+.radar1 {
+	width: 200rpx;
+	height: 200rpx;
+	position: relative;
+	border-radius: 50%;
+	border: 1px solid #cedbeb;
+	background: #f2f4ff;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	overflow: hidden;
+	.search {
+		content: '';
+		position: absolute;
+		width: calc(200rpx / 2);
+		height: calc(200rpx /2);
+		background: linear-gradient(
+			45deg,
+			rgba(0, 0, 0, 0) 50%,
+			rgba(169, 200, 246, 1) 100%
+			);
+		border-radius: 100% 0 0 0;
+		top: -0.5px;
+		left: -0.5px;
+		animation: scanning 2s linear infinite;
+		transform-origin: 100% 100%;
+	}
+	.startSearch{
+		animation-play-state:paused;
+		
+	}
+	@keyframes scanning {
+		to {
+			transform: rotate(1turn);
+		}
+	}
+	.radar2{
+		background: #e8f2fe;
+		width: 100rpx;
+		height: 100rpx;
+		border-radius: 50%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		.radar3{
+			background: #e0ecfc;
+			width: 50rpx;
+			height: 50rpx;
+			border-radius: 50%;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			.radar4{
+				background: #277afc;
+				width:10rpx;
+				height: 10rpx;
+				border-radius: 50%;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+			}
+		}
+	}
+}
+

+ 539 - 0
static/fonts/demo.css

@@ -0,0 +1,539 @@
+/* Logo 字体 */
+@font-face {
+  font-family: "iconfont logo";
+  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
+  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
+}
+
+.logo {
+  font-family: "iconfont logo";
+  font-size: 160px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+/* tabs */
+.nav-tabs {
+  position: relative;
+}
+
+.nav-tabs .nav-more {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  height: 42px;
+  line-height: 42px;
+  color: #666;
+}
+
+#tabs {
+  border-bottom: 1px solid #eee;
+}
+
+#tabs li {
+  cursor: pointer;
+  width: 100px;
+  height: 40px;
+  line-height: 40px;
+  text-align: center;
+  font-size: 16px;
+  border-bottom: 2px solid transparent;
+  position: relative;
+  z-index: 1;
+  margin-bottom: -1px;
+  color: #666;
+}
+
+
+#tabs .active {
+  border-bottom-color: #f00;
+  color: #222;
+}
+
+.tab-container .content {
+  display: none;
+}
+
+/* 页面布局 */
+.main {
+  padding: 30px 100px;
+  width: 960px;
+  margin: 0 auto;
+}
+
+.main .logo {
+  color: #333;
+  text-align: left;
+  margin-bottom: 30px;
+  line-height: 1;
+  height: 110px;
+  margin-top: -50px;
+  overflow: hidden;
+  *zoom: 1;
+}
+
+.main .logo a {
+  font-size: 160px;
+  color: #333;
+}
+
+.helps {
+  margin-top: 40px;
+}
+
+.helps pre {
+  padding: 20px;
+  margin: 10px 0;
+  border: solid 1px #e7e1cd;
+  background-color: #fffdef;
+  overflow: auto;
+}
+
+.icon_lists {
+  width: 100% !important;
+  overflow: hidden;
+  *zoom: 1;
+}
+
+.icon_lists li {
+  width: 100px;
+  margin-bottom: 10px;
+  margin-right: 20px;
+  text-align: center;
+  list-style: none !important;
+  cursor: default;
+}
+
+.icon_lists li .code-name {
+  line-height: 1.2;
+}
+
+.icon_lists .icon {
+  display: block;
+  height: 100px;
+  line-height: 100px;
+  font-size: 42px;
+  margin: 10px auto;
+  color: #333;
+  -webkit-transition: font-size 0.25s linear, width 0.25s linear;
+  -moz-transition: font-size 0.25s linear, width 0.25s linear;
+  transition: font-size 0.25s linear, width 0.25s linear;
+}
+
+.icon_lists .icon:hover {
+  font-size: 100px;
+}
+
+.icon_lists .svg-icon {
+  /* 通过设置 font-size 来改变图标大小 */
+  width: 1em;
+  /* 图标和文字相邻时,垂直对齐 */
+  vertical-align: -0.15em;
+  /* 通过设置 color 来改变 SVG 的颜色/fill */
+  fill: currentColor;
+  /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
+      normalize.css 中也包含这行 */
+  overflow: hidden;
+}
+
+.icon_lists li .name,
+.icon_lists li .code-name {
+  color: #666;
+}
+
+/* markdown 样式 */
+.markdown {
+  color: #666;
+  font-size: 14px;
+  line-height: 1.8;
+}
+
+.highlight {
+  line-height: 1.5;
+}
+
+.markdown img {
+  vertical-align: middle;
+  max-width: 100%;
+}
+
+.markdown h1 {
+  color: #404040;
+  font-weight: 500;
+  line-height: 40px;
+  margin-bottom: 24px;
+}
+
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+  color: #404040;
+  margin: 1.6em 0 0.6em 0;
+  font-weight: 500;
+  clear: both;
+}
+
+.markdown h1 {
+  font-size: 28px;
+}
+
+.markdown h2 {
+  font-size: 22px;
+}
+
+.markdown h3 {
+  font-size: 16px;
+}
+
+.markdown h4 {
+  font-size: 14px;
+}
+
+.markdown h5 {
+  font-size: 12px;
+}
+
+.markdown h6 {
+  font-size: 12px;
+}
+
+.markdown hr {
+  height: 1px;
+  border: 0;
+  background: #e9e9e9;
+  margin: 16px 0;
+  clear: both;
+}
+
+.markdown p {
+  margin: 1em 0;
+}
+
+.markdown>p,
+.markdown>blockquote,
+.markdown>.highlight,
+.markdown>ol,
+.markdown>ul {
+  width: 80%;
+}
+
+.markdown ul>li {
+  list-style: circle;
+}
+
+.markdown>ul li,
+.markdown blockquote ul>li {
+  margin-left: 20px;
+  padding-left: 4px;
+}
+
+.markdown>ul li p,
+.markdown>ol li p {
+  margin: 0.6em 0;
+}
+
+.markdown ol>li {
+  list-style: decimal;
+}
+
+.markdown>ol li,
+.markdown blockquote ol>li {
+  margin-left: 20px;
+  padding-left: 4px;
+}
+
+.markdown code {
+  margin: 0 3px;
+  padding: 0 5px;
+  background: #eee;
+  border-radius: 3px;
+}
+
+.markdown strong,
+.markdown b {
+  font-weight: 600;
+}
+
+.markdown>table {
+  border-collapse: collapse;
+  border-spacing: 0px;
+  empty-cells: show;
+  border: 1px solid #e9e9e9;
+  width: 95%;
+  margin-bottom: 24px;
+}
+
+.markdown>table th {
+  white-space: nowrap;
+  color: #333;
+  font-weight: 600;
+}
+
+.markdown>table th,
+.markdown>table td {
+  border: 1px solid #e9e9e9;
+  padding: 8px 16px;
+  text-align: left;
+}
+
+.markdown>table th {
+  background: #F7F7F7;
+}
+
+.markdown blockquote {
+  font-size: 90%;
+  color: #999;
+  border-left: 4px solid #e9e9e9;
+  padding-left: 0.8em;
+  margin: 1em 0;
+}
+
+.markdown blockquote p {
+  margin: 0;
+}
+
+.markdown .anchor {
+  opacity: 0;
+  transition: opacity 0.3s ease;
+  margin-left: 8px;
+}
+
+.markdown .waiting {
+  color: #ccc;
+}
+
+.markdown h1:hover .anchor,
+.markdown h2:hover .anchor,
+.markdown h3:hover .anchor,
+.markdown h4:hover .anchor,
+.markdown h5:hover .anchor,
+.markdown h6:hover .anchor {
+  opacity: 1;
+  display: inline-block;
+}
+
+.markdown>br,
+.markdown>p>br {
+  clear: both;
+}
+
+
+.hljs {
+  display: block;
+  background: white;
+  padding: 0.5em;
+  color: #333333;
+  overflow-x: auto;
+}
+
+.hljs-comment,
+.hljs-meta {
+  color: #969896;
+}
+
+.hljs-string,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-strong,
+.hljs-emphasis,
+.hljs-quote {
+  color: #df5000;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-type {
+  color: #a71d5d;
+}
+
+.hljs-literal,
+.hljs-symbol,
+.hljs-bullet,
+.hljs-attribute {
+  color: #0086b3;
+}
+
+.hljs-section,
+.hljs-name {
+  color: #63a35c;
+}
+
+.hljs-tag {
+  color: #333333;
+}
+
+.hljs-title,
+.hljs-attr,
+.hljs-selector-id,
+.hljs-selector-class,
+.hljs-selector-attr,
+.hljs-selector-pseudo {
+  color: #795da3;
+}
+
+.hljs-addition {
+  color: #55a532;
+  background-color: #eaffea;
+}
+
+.hljs-deletion {
+  color: #bd2c00;
+  background-color: #ffecec;
+}
+
+.hljs-link {
+  text-decoration: underline;
+}
+
+/* 代码高亮 */
+/* PrismJS 1.15.0
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+code[class*="language-"],
+pre[class*="language-"] {
+  color: black;
+  background: none;
+  text-shadow: 0 1px white;
+  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+  text-align: left;
+  white-space: pre;
+  word-spacing: normal;
+  word-break: normal;
+  word-wrap: normal;
+  line-height: 1.5;
+
+  -moz-tab-size: 4;
+  -o-tab-size: 4;
+  tab-size: 4;
+
+  -webkit-hyphens: none;
+  -moz-hyphens: none;
+  -ms-hyphens: none;
+  hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection,
+pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection,
+code[class*="language-"] ::-moz-selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+@media print {
+
+  code[class*="language-"],
+  pre[class*="language-"] {
+    text-shadow: none;
+  }
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+  padding: 1em;
+  margin: .5em 0;
+  overflow: auto;
+}
+
+:not(pre)>code[class*="language-"],
+pre[class*="language-"] {
+  background: #f5f2f0;
+}
+
+/* Inline code */
+:not(pre)>code[class*="language-"] {
+  padding: .1em;
+  border-radius: .3em;
+  white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+  color: slategray;
+}
+
+.token.punctuation {
+  color: #999;
+}
+
+.namespace {
+  opacity: .7;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+  color: #905;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+  color: #690;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+  color: #9a6e3a;
+  background: hsla(0, 0%, 100%, .5);
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+  color: #07a;
+}
+
+.token.function,
+.token.class-name {
+  color: #DD4A68;
+}
+
+.token.regex,
+.token.important,
+.token.variable {
+  color: #e90;
+}
+
+.token.important,
+.token.bold {
+  font-weight: bold;
+}
+
+.token.italic {
+  font-style: italic;
+}
+
+.token.entity {
+  cursor: help;
+}

+ 441 - 0
static/fonts/demo_index.html

@@ -0,0 +1,441 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8"/>
+  <title>iconfont Demo</title>
+  <link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
+  <link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
+  <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
+  <link rel="stylesheet" href="demo.css">
+  <link rel="stylesheet" href="iconfont.css">
+  <script src="iconfont.js"></script>
+  <!-- jQuery -->
+  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
+  <!-- 代码高亮 -->
+  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
+  <style>
+    .main .logo {
+      margin-top: 0;
+      height: auto;
+    }
+
+    .main .logo a {
+      display: flex;
+      align-items: center;
+    }
+
+    .main .logo .sub-title {
+      margin-left: 0.5em;
+      font-size: 22px;
+      color: #fff;
+      background: linear-gradient(-45deg, #3967FF, #B500FE);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    }
+  </style>
+</head>
+<body>
+  <div class="main">
+    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
+      <img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
+      
+    </a></h1>
+    <div class="nav-tabs">
+      <ul id="tabs" class="dib-box">
+        <li class="dib active"><span>Unicode</span></li>
+        <li class="dib"><span>Font class</span></li>
+        <li class="dib"><span>Symbol</span></li>
+      </ul>
+      
+      <a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4559910" target="_blank" class="nav-more">查看项目</a>
+      
+    </div>
+    <div class="tab-container">
+      <div class="content unicode" style="display: block;">
+          <ul class="icon_lists dib-box">
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe60a;</span>
+                <div class="name">添加订单</div>
+                <div class="code-name">&amp;#xe60a;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe749;</span>
+                <div class="name">全部订单</div>
+                <div class="code-name">&amp;#xe749;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe606;</span>
+                <div class="name">logout</div>
+                <div class="code-name">&amp;#xe606;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe609;</span>
+                <div class="name">首页</div>
+                <div class="code-name">&amp;#xe609;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe66f;</span>
+                <div class="name">扫一扫</div>
+                <div class="code-name">&amp;#xe66f;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6dc;</span>
+                <div class="name">核销订单</div>
+                <div class="code-name">&amp;#xe6dc;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe60e;</span>
+                <div class="name">我的</div>
+                <div class="code-name">&amp;#xe60e;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe616;</span>
+                <div class="name">已取消</div>
+                <div class="code-name">&amp;#xe616;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe607;</span>
+                <div class="name">已送达</div>
+                <div class="code-name">&amp;#xe607;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe83e;</span>
+                <div class="name">配送中</div>
+                <div class="code-name">&amp;#xe83e;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe62e;</span>
+                <div class="name">物流信息-已下单</div>
+                <div class="code-name">&amp;#xe62e;</div>
+              </li>
+          
+          </ul>
+          <div class="article markdown">
+          <h2 id="unicode-">Unicode 引用</h2>
+          <hr>
+
+          <p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
+          <ul>
+            <li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
+            <li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
+          </ul>
+          <blockquote>
+            <p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
+          </blockquote>
+          <p>Unicode 使用步骤如下:</p>
+          <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
+<pre><code class="language-css"
+>@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.woff2?t=1716520790453') format('woff2'),
+       url('iconfont.woff?t=1716520790453') format('woff'),
+       url('iconfont.ttf?t=1716520790453') format('truetype');
+}
+</code></pre>
+          <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
+<pre><code class="language-css"
+>.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+</code></pre>
+          <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
+<pre>
+<code class="language-html"
+>&lt;span class="iconfont"&gt;&amp;#x33;&lt;/span&gt;
+</code></pre>
+          <blockquote>
+            <p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
+          </blockquote>
+          </div>
+      </div>
+      <div class="content font-class">
+        <ul class="icon_lists dib-box">
+          
+          <li class="dib">
+            <span class="icon iconfont icon-tianjiadingdan"></span>
+            <div class="name">
+              添加订单
+            </div>
+            <div class="code-name">.icon-tianjiadingdan
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-dingdan"></span>
+            <div class="name">
+              全部订单
+            </div>
+            <div class="code-name">.icon-dingdan
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-tuichudenglu"></span>
+            <div class="name">
+              logout
+            </div>
+            <div class="code-name">.icon-tuichudenglu
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-shouye"></span>
+            <div class="name">
+              首页
+            </div>
+            <div class="code-name">.icon-shouye
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-saoyisao"></span>
+            <div class="name">
+              扫一扫
+            </div>
+            <div class="code-name">.icon-saoyisao
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-qianshou"></span>
+            <div class="name">
+              核销订单
+            </div>
+            <div class="code-name">.icon-qianshou
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-wode"></span>
+            <div class="name">
+              我的
+            </div>
+            <div class="code-name">.icon-wode
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-yiquxiao"></span>
+            <div class="name">
+              已取消
+            </div>
+            <div class="code-name">.icon-yiquxiao
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-yisongda"></span>
+            <div class="name">
+              已送达
+            </div>
+            <div class="code-name">.icon-yisongda
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-delivery"></span>
+            <div class="name">
+              配送中
+            </div>
+            <div class="code-name">.icon-delivery
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-cancel"></span>
+            <div class="name">
+              物流信息-已下单
+            </div>
+            <div class="code-name">.icon-cancel
+            </div>
+          </li>
+          
+        </ul>
+        <div class="article markdown">
+        <h2 id="font-class-">font-class 引用</h2>
+        <hr>
+
+        <p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
+        <p>与 Unicode 使用方式相比,具有如下特点:</p>
+        <ul>
+          <li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
+          <li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
+        </ul>
+        <p>使用步骤如下:</p>
+        <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
+<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
+</code></pre>
+        <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
+<pre><code class="language-html">&lt;span class="iconfont icon-xxx"&gt;&lt;/span&gt;
+</code></pre>
+        <blockquote>
+          <p>"
+            iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
+        </blockquote>
+      </div>
+      </div>
+      <div class="content symbol">
+          <ul class="icon_lists dib-box">
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-tianjiadingdan"></use>
+                </svg>
+                <div class="name">添加订单</div>
+                <div class="code-name">#icon-tianjiadingdan</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-dingdan"></use>
+                </svg>
+                <div class="name">全部订单</div>
+                <div class="code-name">#icon-dingdan</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-tuichudenglu"></use>
+                </svg>
+                <div class="name">logout</div>
+                <div class="code-name">#icon-tuichudenglu</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-shouye"></use>
+                </svg>
+                <div class="name">首页</div>
+                <div class="code-name">#icon-shouye</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-saoyisao"></use>
+                </svg>
+                <div class="name">扫一扫</div>
+                <div class="code-name">#icon-saoyisao</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-qianshou"></use>
+                </svg>
+                <div class="name">核销订单</div>
+                <div class="code-name">#icon-qianshou</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-wode"></use>
+                </svg>
+                <div class="name">我的</div>
+                <div class="code-name">#icon-wode</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-yiquxiao"></use>
+                </svg>
+                <div class="name">已取消</div>
+                <div class="code-name">#icon-yiquxiao</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-yisongda"></use>
+                </svg>
+                <div class="name">已送达</div>
+                <div class="code-name">#icon-yisongda</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-delivery"></use>
+                </svg>
+                <div class="name">配送中</div>
+                <div class="code-name">#icon-delivery</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-cancel"></use>
+                </svg>
+                <div class="name">物流信息-已下单</div>
+                <div class="code-name">#icon-cancel</div>
+            </li>
+          
+          </ul>
+          <div class="article markdown">
+          <h2 id="symbol-">Symbol 引用</h2>
+          <hr>
+
+          <p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
+            这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
+          <ul>
+            <li>支持多色图标了,不再受单色限制。</li>
+            <li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
+            <li>兼容性较差,支持 IE9+,及现代浏览器。</li>
+            <li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
+          </ul>
+          <p>使用步骤如下:</p>
+          <h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
+<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
+</code></pre>
+          <h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
+<pre><code class="language-html">&lt;style&gt;
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+&lt;/style&gt;
+</code></pre>
+          <h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
+<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
+  &lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
+&lt;/svg&gt;
+</code></pre>
+          </div>
+      </div>
+
+    </div>
+  </div>
+  <script>
+  $(document).ready(function () {
+      $('.tab-container .content:first').show()
+
+      $('#tabs li').click(function (e) {
+        var tabContent = $('.tab-container .content')
+        var index = $(this).index()
+
+        if ($(this).hasClass('active')) {
+          return
+        } else {
+          $('#tabs li').removeClass('active')
+          $(this).addClass('active')
+
+          tabContent.hide().eq(index).fadeIn()
+        }
+      })
+    })
+  </script>
+</body>
+</html>

+ 59 - 0
static/fonts/iconfont.css

@@ -0,0 +1,59 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 4559910 */
+  src: url('@/static/fonts/iconfont.woff2?t=1716520790453') format('woff2'),
+       url('@/static/fonts/iconfont.woff?t=1716520790453') format('woff'),
+       url('@/static/fonts/iconfont.ttf?t=1716520790453') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-tianjiadingdan:before {
+  content: "\e60a";
+}
+
+.icon-dingdan:before {
+  content: "\e749";
+}
+
+.icon-tuichudenglu:before {
+  content: "\e606";
+}
+
+.icon-shouye:before {
+  content: "\e609";
+}
+
+.icon-saoyisao:before {
+  content: "\e66f";
+}
+
+.icon-qianshou:before {
+  content: "\e6dc";
+}
+
+.icon-wode:before {
+  content: "\e60e";
+}
+
+.icon-yiquxiao:before {
+  content: "\e616";
+}
+
+.icon-yisongda:before {
+  content: "\e607";
+}
+
+.icon-delivery:before {
+  content: "\e83e";
+}
+
+.icon-cancel:before {
+  content: "\e62e";
+}
+

File diff suppressed because it is too large
+ 0 - 0
static/fonts/iconfont.js


+ 86 - 0
static/fonts/iconfont.json

@@ -0,0 +1,86 @@
+{
+  "id": "4559910",
+  "name": "冷链送药app",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "9002997",
+      "name": "添加订单",
+      "font_class": "tianjiadingdan",
+      "unicode": "e60a",
+      "unicode_decimal": 58890
+    },
+    {
+      "icon_id": "375773",
+      "name": "全部订单",
+      "font_class": "dingdan",
+      "unicode": "e749",
+      "unicode_decimal": 59209
+    },
+    {
+      "icon_id": "5744740",
+      "name": "logout",
+      "font_class": "tuichudenglu",
+      "unicode": "e606",
+      "unicode_decimal": 58886
+    },
+    {
+      "icon_id": "7352948",
+      "name": "首页",
+      "font_class": "shouye",
+      "unicode": "e609",
+      "unicode_decimal": 58889
+    },
+    {
+      "icon_id": "12097604",
+      "name": "扫一扫",
+      "font_class": "saoyisao",
+      "unicode": "e66f",
+      "unicode_decimal": 58991
+    },
+    {
+      "icon_id": "16695860",
+      "name": "核销订单",
+      "font_class": "qianshou",
+      "unicode": "e6dc",
+      "unicode_decimal": 59100
+    },
+    {
+      "icon_id": "25214248",
+      "name": "我的",
+      "font_class": "wode",
+      "unicode": "e60e",
+      "unicode_decimal": 58894
+    },
+    {
+      "icon_id": "1418242",
+      "name": "已取消",
+      "font_class": "yiquxiao",
+      "unicode": "e616",
+      "unicode_decimal": 58902
+    },
+    {
+      "icon_id": "23413983",
+      "name": "已送达",
+      "font_class": "yisongda",
+      "unicode": "e607",
+      "unicode_decimal": 58887
+    },
+    {
+      "icon_id": "23919714",
+      "name": "配送中",
+      "font_class": "delivery",
+      "unicode": "e83e",
+      "unicode_decimal": 59454
+    },
+    {
+      "icon_id": "32447645",
+      "name": "物流信息-已下单",
+      "font_class": "cancel",
+      "unicode": "e62e",
+      "unicode_decimal": 58926
+    }
+  ]
+}

BIN
static/fonts/iconfont.ttf


BIN
static/fonts/iconfont.woff


BIN
static/fonts/iconfont.woff2


+ 452 - 0
static/js/BluetoothTool.js

@@ -0,0 +1,452 @@
+/**
+ * html 5+ 串口蓝牙操作
+ * 2021.04.23 uni-app版本
+ * @auth boolTrue
+ */
+
+/**
+ * 初始化参数
+ */ 
+//#ifdef APP-PLUS
+let BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
+let Intent = plus.android.importClass("android.content.Intent");
+let IntentFilter = plus.android.importClass("android.content.IntentFilter");
+let BluetoothDevice = plus.android.importClass("android.bluetooth.BluetoothDevice");
+let UUID = plus.android.importClass("java.util.UUID");
+let Toast = plus.android.importClass("android.widget.Toast");
+//连接串口设备的 UUID
+let MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
+
+let invoke = plus.android.invoke;
+let btAdapter = BluetoothAdapter.getDefaultAdapter();
+let activity = plus.android.runtimeMainActivity();
+
+let btSocket = null;
+let btInStream = null;
+let btOutStream = null;
+let setIntervalId = 0;
+
+let btFindReceiver = null; //蓝牙搜索广播接收器
+let btStatusReceiver = null; //蓝牙状态监听广播
+//#endif
+/**
+ * 构造对象
+ */ 
+var blueToothTool = {
+	state : {
+		bluetoothEnable: false, //蓝牙是否开启
+		bluetoothState: "", //当前蓝牙状态
+		discoveryDeviceState: false, //是否正在搜索蓝牙设备
+		readThreadState: false, //数据读取线程状态
+	},
+	options : {
+		/**
+		 * 监听蓝牙状态回调
+		 * @param {String} state
+		 */
+		listenBTStatusCallback: function(state) {},
+		/**
+		 * 搜索到新的蓝牙设备回调
+		 * @param {Device} newDevice
+		 */
+		discoveryDeviceCallback: function(newDevice) {},
+		/**
+		 * 蓝牙搜索完成回调
+		 */
+		discoveryFinishedCallback: function() {},
+		/**
+		 * 接收到数据回调
+		 * @param {Array} dataByteArr
+		 */
+		readDataCallback: function(dataByteArr) {},
+		/**
+		 * 蓝牙连接中断回调
+		 * @param {Exception} e
+		 */
+		connExceptionCallback: function(e) {}
+	},
+	init(setOptions) {
+		Object.assign(this.options, setOptions);
+		this.state.bluetoothEnable = this.getBluetoothStatus();
+		this.listenBluetoothStatus();
+	},
+	shortToast(msg) {
+		Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show();
+	},
+	/**
+	 * 是否支持蓝牙
+	 * @return {boolean}
+	 */
+	isSupportBluetooth() {
+		if(btAdapter != null) {
+			return true;
+		}
+		return false;
+	},
+	/**
+	 * 获取蓝牙的状态
+	 * @return {boolean} 是否已开启
+	 */
+	getBluetoothStatus() {
+		if(btAdapter != null) {
+			return btAdapter.isEnabled();
+		}
+		return false;
+	},
+	/**
+	 * 打开蓝牙
+	 * @param activity
+	 * @param requestCode
+	 */
+	turnOnBluetooth() {
+		if(btAdapter == null) {
+			shortToast("没有蓝牙");
+			return;
+		}
+		if(!btAdapter.isEnabled()) {
+			if(activity == null) {
+				shortToast("未获取到activity");
+				return;
+			} else {
+				let intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+				let requestCode = 1;
+				activity.startActivityForResult(intent, requestCode);
+				return;
+			}
+		} else {
+			shortToast("蓝牙已经打开");
+		}
+	},
+	/**
+	 * 关闭蓝牙
+	 */
+	turnOffBluetooth() {
+		if(btAdapter != null && btAdapter.isEnabled()) {
+			btAdapter.disable();
+		}
+		if(btFindReceiver != null) {
+			try {
+				activity.unregisterReceiver(btFindReceiver);
+			} catch(e) {
+
+			}
+			btFindReceiver = null;
+		}
+		this.state.bluetoothEnable = false;
+		this.cancelDiscovery();
+		closeBtSocket();
+
+		if(btAdapter != null && btAdapter.isEnabled()) {
+			btAdapter.disable();
+			shortToast("蓝牙关闭成功");
+		} else {
+			shortToast("蓝牙已经关闭");
+		}
+	},
+	/**
+	 * 获取已经配对的设备
+	 * @return {Array} connetedDevices
+	 */
+	getPairedDevices() {
+		let pairedDevices = [];
+
+		//蓝牙连接android原生对象,是一个set集合
+		let pairedDevicesAndroid = null;
+		if(btAdapter != null && btAdapter.isEnabled()) {
+			pairedDevicesAndroid = btAdapter.getBondedDevices();
+		} else {
+			shortToast("蓝牙未开启");
+		}
+
+		if(!pairedDevicesAndroid) {
+			return pairedDevices;
+		}
+
+		//遍历连接设备的set集合,转换为js数组
+		let it = invoke(pairedDevicesAndroid, "iterator");
+		while(invoke(it, "hasNext")) {
+			let device = invoke(it, "next");
+			pairedDevices.push({
+				"name": invoke(device, "getName"),
+				"address": invoke(device, "getAddress")
+			});
+		}
+		return pairedDevices;
+	},
+	/**
+	 * 发现设备
+	 */
+	discoveryNewDevice() {
+		if(btFindReceiver != null) {
+			try {
+				activity.unregisterReceiver(btFindReceiver);
+			} catch(e) {
+				console.error(e);
+			}
+			btFindReceiver = null;
+			this.cancelDiscovery();
+		}
+		let Build = plus.android.importClass("android.os.Build");
+		
+		 //6.0以后的如果需要利用本机查找周围的wifi和蓝牙设备, 申请权限
+        if(Build.VERSION.SDK_INT >= 6.0){
+			
+        }
+		let options = this.options
+		btFindReceiver = plus.android.implements("io.dcloud.android.content.BroadcastReceiver", {
+			"onReceive": function(context, intent) {
+				plus.android.importClass(context);
+				plus.android.importClass(intent);
+				let action = intent.getAction();
+
+				if(BluetoothDevice.ACTION_FOUND == action) { // 找到设备
+					let device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+					let newDevice = {
+						"name": plus.android.invoke(device, "getName"),
+						"address": plus.android.invoke(device, "getAddress")
+					}
+					options.discoveryDeviceCallback && options.discoveryDeviceCallback(newDevice);
+				}
+				if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action) { // 搜索完成
+					cancelDiscovery();
+					options.discoveryFinishedCallback && options.discoveryFinishedCallback();
+				}
+			}
+		});
+		let filter = new IntentFilter();
+		filter.addAction(BluetoothDevice.ACTION_FOUND);
+		filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+		activity.registerReceiver(btFindReceiver, filter);
+		btAdapter.startDiscovery(); //开启搜索
+		this.state.discoveryDeviceState = true;
+	},
+	/**
+	 * 蓝牙状态监听
+	 * @param {Activity} activity
+	 */
+	listenBluetoothStatus() {
+		if(btStatusReceiver != null) {
+			try {
+				activity.unregisterReceiver(btStatusReceiver);
+			} catch(e) {
+				console.error(e);
+			}
+			btStatusReceiver = null;
+		}
+		btStatusReceiver = plus.android.implements("io.dcloud.android.content.BroadcastReceiver", {
+			"onReceive": (context, intent)=> {
+				plus.android.importClass(context);
+				plus.android.importClass(intent);
+
+				let action = intent.getAction();
+				switch(action) {
+					case BluetoothAdapter.ACTION_STATE_CHANGED:
+						let blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
+						let stateStr = "";
+						switch(blueState) {
+							case BluetoothAdapter.STATE_TURNING_ON:
+								stateStr = "STATE_TURNING_ON";
+								break;
+							case BluetoothAdapter.STATE_ON:
+								this.state.bluetoothEnable = true;
+								stateStr = "STATE_ON";
+								break;
+							case BluetoothAdapter.STATE_TURNING_OFF:
+								stateStr = "STATE_TURNING_OFF";
+								break;
+							case BluetoothAdapter.STATE_OFF:
+								stateStr = "STATE_OFF";
+								this.state.bluetoothEnable = false;
+								break;
+						}
+						this.state.bluetoothState = stateStr;
+						this.options.listenBTStatusCallback && this.options.listenBTStatusCallback(stateStr);
+						break;
+				}
+			}
+		});
+		let filter = new IntentFilter();
+		filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+		activity.registerReceiver(btStatusReceiver, filter);
+		// 首次连接 状态回调
+		if(this.state.bluetoothEnable) {
+			this.options.listenBTStatusCallback && this.options.listenBTStatusCallback('STATE_ON');
+		}
+	},
+	/**
+	 * 根据蓝牙地址,连接设备
+	 * @param {Stirng} address
+	 * @return {Boolean}
+	 */
+	connDevice(address, callback) {
+		let InputStream = plus.android.importClass("java.io.InputStream");
+		let OutputStream = plus.android.importClass("java.io.OutputStream");
+		let BluetoothSocket = plus.android.importClass("android.bluetooth.BluetoothSocket");
+
+		this.cancelDiscovery();
+		if(btSocket != null) {
+			this.closeBtSocket();
+		}
+		this.state.readThreadState = false;
+
+		try {
+			let device = invoke(btAdapter, "getRemoteDevice", address);
+			btSocket = invoke(device, "createRfcommSocketToServiceRecord", MY_UUID);
+		} catch(e) {
+			console.error(e);
+			shortToast("连接失败,获取Socket失败!");
+			callback(false)
+			return false;
+		}
+		try {
+			invoke(btSocket, "connect");
+			this.readData(); //读数据
+			this.shortToast("连接成功");
+			callback(true)
+		} catch(e) {
+			console.error(e);
+			this.shortToast("连接失败");
+			callback(false)
+			try {
+				btSocket.close();
+				btSocket = null;
+			} catch(e1) {
+				console.error(e1);
+			}
+			return false;
+		}
+		return true;
+	},
+	/**
+	 * 断开连接设备
+	 * @param {Object} address
+	 * @return {Boolean}
+	 */
+	disConnDevice() {
+		if(btSocket != null) {
+			this.closeBtSocket();
+		}
+		this.state.readThreadState = false;
+		this.shortToast("断开连接成功");
+	},
+	/**
+	 * 断开连接设备
+	 * @param {Object} address
+	 * @return {Boolean}
+	 */
+	closeBtSocket() {
+		this.state.readThreadState = false;
+		if(!btSocket) {
+			return;
+		}
+		try {
+			btSocket.close();
+		} catch(e) {
+			console.error(e);
+			btSocket = null;
+		}
+	},
+	/**
+	 * 取消发现
+	 */
+	cancelDiscovery() {
+		if(btAdapter.isDiscovering()) {
+			btAdapter.cancelDiscovery();
+		}
+		if(btFindReceiver != null) {
+			activity.unregisterReceiver(btFindReceiver);
+			btFindReceiver = null;
+		}
+		this.state.discoveryDeviceState = false;
+	},
+	/**
+	 * 读取数据
+	 * @param {Object} activity
+	 * @param {Function} callback
+	 * @return {Boolean}
+	 */
+	readData() {
+		if(!btSocket) {
+			this.shortToast("请先连接蓝牙设备!");
+			return false;
+		}
+		try {
+			btInStream = invoke(btSocket, "getInputStream");
+			btOutStream = invoke(btSocket, "getOutputStream");
+		} catch(e) {
+			console.error(e);
+			this.shortToast("创建输入输出流失败!");
+			this.closeBtSocket();
+			return false;
+		}
+		this.read();
+		this.state.readThreadState = true;
+		return true;
+	},
+	/**
+	 * 模拟java多线程读取数据
+	 */
+	read() {
+		let setTimeCount = 0;
+		clearInterval(setIntervalId);
+		setIntervalId = setInterval(()=> {
+			setTimeCount++;
+			if(this.state.readThreadState) {
+				let t = new Date().getTime();
+				//心跳检测
+				if(setTimeCount % 20 == 0) {
+					try {
+						btOutStream.write([0b00]);
+					} catch(e) {
+						this.state.readThreadState = false;
+						this.options.connExceptionCallback && this.options.connExceptionCallback(e);
+					}
+				}
+				let dataArr = [];
+				while(invoke(btInStream, "available") !== 0) {
+					let data = invoke(btInStream, "read");
+					dataArr.push(data);
+					let ct = new Date().getTime();
+					if(ct - t > 20) {
+						break;
+					}
+				}
+				if(dataArr.length > 0) {
+					this.options.readDataCallback && this.options.readDataCallback(dataArr);
+				}
+			}
+		}, 40);
+	},
+	/**
+	 * 发送数据
+	 * @param {String} dataStr
+	 * @return {Boolean}
+	 */
+	sendData(dataStr) {
+		if(!btOutStream) {
+			this.shortToast("创建输出流失败!");
+			return;
+		}
+		let bytes = invoke(dataStr, 'getBytes', 'gbk');
+		try {
+			btOutStream.write(bytes);
+		} catch(e) {
+			return false;
+		}
+		return true;
+	},
+	sendByteData(byteData) {
+		if(!btOutStream) {
+			this.shortToast("创建输出流失败!");
+			return;
+		}
+		try {
+			btOutStream.write(byteData);
+		} catch(e) {
+			return false;
+		}
+		return true;
+	}
+}
+
+module.exports = blueToothTool

File diff suppressed because it is too large
+ 9 - 0
static/js/encoding-indexes.js


+ 3312 - 0
static/js/encoding.js

@@ -0,0 +1,3312 @@
+// This is free and unencumbered software released into the public domain.
+// See LICENSE.md for more information.
+
+/**
+ * @fileoverview Global |this| required for resolving indexes in node.
+ * @suppress {globalThis}
+ */
+(function(global) {
+  'use strict';
+
+  // If we're in node require encoding-indexes and attach it to the global.
+  if (typeof module !== "undefined" && module.exports &&
+    !global["encoding-indexes"]) {
+    global["encoding-indexes"] =
+      require("./encoding-indexes.js")["encoding-indexes"];
+  }
+
+  //
+  // Utilities
+  //
+
+  /**
+   * @param {number} a The number to test.
+   * @param {number} min The minimum value in the range, inclusive.
+   * @param {number} max The maximum value in the range, inclusive.
+   * @return {boolean} True if a >= min and a <= max.
+   */
+  function inRange(a, min, max) {
+    return min <= a && a <= max;
+  }
+
+  /**
+   * @param {!Array.<*>} array The array to check.
+   * @param {*} item The item to look for in the array.
+   * @return {boolean} True if the item appears in the array.
+   */
+  function includes(array, item) {
+    return array.indexOf(item) !== -1;
+  }
+
+  var floor = Math.floor;
+
+  /**
+   * @param {*} o
+   * @return {Object}
+   */
+  function ToDictionary(o) {
+    if (o === undefined) return {};
+    if (o === Object(o)) return o;
+    throw TypeError('Could not convert argument to dictionary');
+  }
+
+  /**
+   * @param {string} string Input string of UTF-16 code units.
+   * @return {!Array.<number>} Code points.
+   */
+  function stringToCodePoints(string) {
+    // https://heycam.github.io/webidl/#dfn-obtain-unicode
+
+    // 1. Let S be the DOMString value.
+    var s = String(string);
+
+    // 2. Let n be the length of S.
+    var n = s.length;
+
+    // 3. Initialize i to 0.
+    var i = 0;
+
+    // 4. Initialize U to be an empty sequence of Unicode characters.
+    var u = [];
+
+    // 5. While i < n:
+    while (i < n) {
+
+      // 1. Let c be the code unit in S at index i.
+      var c = s.charCodeAt(i);
+
+      // 2. Depending on the value of c:
+
+      // c < 0xD800 or c > 0xDFFF
+      if (c < 0xD800 || c > 0xDFFF) {
+        // Append to U the Unicode character with code point c.
+        u.push(c);
+      }
+
+      // 0xDC00 ≤ c ≤ 0xDFFF
+      else if (0xDC00 <= c && c <= 0xDFFF) {
+        // Append to U a U+FFFD REPLACEMENT CHARACTER.
+        u.push(0xFFFD);
+      }
+
+      // 0xD800 ≤ c ≤ 0xDBFF
+      else if (0xD800 <= c && c <= 0xDBFF) {
+        // 1. If i = n−1, then append to U a U+FFFD REPLACEMENT
+        // CHARACTER.
+        if (i === n - 1) {
+          u.push(0xFFFD);
+        }
+        // 2. Otherwise, i < n−1:
+        else {
+          // 1. Let d be the code unit in S at index i+1.
+          var d = s.charCodeAt(i + 1);
+
+          // 2. If 0xDC00 ≤ d ≤ 0xDFFF, then:
+          if (0xDC00 <= d && d <= 0xDFFF) {
+            // 1. Let a be c & 0x3FF.
+            var a = c & 0x3FF;
+
+            // 2. Let b be d & 0x3FF.
+            var b = d & 0x3FF;
+
+            // 3. Append to U the Unicode character with code point
+            // 2^16+2^10*a+b.
+            u.push(0x10000 + (a << 10) + b);
+
+            // 4. Set i to i+1.
+            i += 1;
+          }
+
+          // 3. Otherwise, d < 0xDC00 or d > 0xDFFF. Append to U a
+          // U+FFFD REPLACEMENT CHARACTER.
+          else  {
+            u.push(0xFFFD);
+          }
+        }
+      }
+
+      // 3. Set i to i+1.
+      i += 1;
+    }
+
+    // 6. Return U.
+    return u;
+  }
+
+  /**
+   * @param {!Array.<number>} code_points Array of code points.
+   * @return {string} string String of UTF-16 code units.
+   */
+  function codePointsToString(code_points) {
+    var s = '';
+    for (var i = 0; i < code_points.length; ++i) {
+      var cp = code_points[i];
+      if (cp <= 0xFFFF) {
+        s += String.fromCharCode(cp);
+      } else {
+        cp -= 0x10000;
+        s += String.fromCharCode((cp >> 10) + 0xD800,
+                                 (cp & 0x3FF) + 0xDC00);
+      }
+    }
+    return s;
+  }
+
+
+  //
+  // Implementation of Encoding specification
+  // https://encoding.spec.whatwg.org/
+  //
+
+  //
+  // 4. Terminology
+  //
+
+  /**
+   * An ASCII byte is a byte in the range 0x00 to 0x7F, inclusive.
+   * @param {number} a The number to test.
+   * @return {boolean} True if a is in the range 0x00 to 0x7F, inclusive.
+   */
+  function isASCIIByte(a) {
+    return 0x00 <= a && a <= 0x7F;
+  }
+
+  /**
+   * An ASCII code point is a code point in the range U+0000 to
+   * U+007F, inclusive.
+   */
+  var isASCIICodePoint = isASCIIByte;
+
+
+  /**
+   * End-of-stream is a special token that signifies no more tokens
+   * are in the stream.
+   * @const
+   */ var end_of_stream = -1;
+
+  /**
+   * A stream represents an ordered sequence of tokens.
+   *
+   * @constructor
+   * @param {!(Array.<number>|Uint8Array)} tokens Array of tokens that provide
+   * the stream.
+   */
+  function Stream(tokens) {
+    /** @type {!Array.<number>} */
+    this.tokens = [].slice.call(tokens);
+    // Reversed as push/pop is more efficient than shift/unshift.
+    this.tokens.reverse();
+  }
+
+  Stream.prototype = {
+    /**
+     * @return {boolean} True if end-of-stream has been hit.
+     */
+    endOfStream: function() {
+      return !this.tokens.length;
+    },
+
+    /**
+     * When a token is read from a stream, the first token in the
+     * stream must be returned and subsequently removed, and
+     * end-of-stream must be returned otherwise.
+     *
+     * @return {number} Get the next token from the stream, or
+     * end_of_stream.
+     */
+     read: function() {
+      if (!this.tokens.length)
+        return end_of_stream;
+       return this.tokens.pop();
+     },
+
+    /**
+     * When one or more tokens are prepended to a stream, those tokens
+     * must be inserted, in given order, before the first token in the
+     * stream.
+     *
+     * @param {(number|!Array.<number>)} token The token(s) to prepend to the
+     * stream.
+     */
+    prepend: function(token) {
+      if (Array.isArray(token)) {
+        var tokens = /**@type {!Array.<number>}*/(token);
+        while (tokens.length)
+          this.tokens.push(tokens.pop());
+      } else {
+        this.tokens.push(token);
+      }
+    },
+
+    /**
+     * When one or more tokens are pushed to a stream, those tokens
+     * must be inserted, in given order, after the last token in the
+     * stream.
+     *
+     * @param {(number|!Array.<number>)} token The tokens(s) to push to the
+     * stream.
+     */
+    push: function(token) {
+      if (Array.isArray(token)) {
+        var tokens = /**@type {!Array.<number>}*/(token);
+        while (tokens.length)
+          this.tokens.unshift(tokens.shift());
+      } else {
+        this.tokens.unshift(token);
+      }
+    }
+  };
+
+  //
+  // 5. Encodings
+  //
+
+  // 5.1 Encoders and decoders
+
+  /** @const */
+  var finished = -1;
+
+  /**
+   * @param {boolean} fatal If true, decoding errors raise an exception.
+   * @param {number=} opt_code_point Override the standard fallback code point.
+   * @return {number} The code point to insert on a decoding error.
+   */
+  function decoderError(fatal, opt_code_point) {
+    if (fatal)
+      throw TypeError('Decoder error');
+    return opt_code_point || 0xFFFD;
+  }
+
+  /**
+   * @param {number} code_point The code point that could not be encoded.
+   * @return {number} Always throws, no value is actually returned.
+   */
+  function encoderError(code_point) {
+    throw TypeError('The code point ' + code_point + ' could not be encoded.');
+  }
+
+  /** @interface */
+  function Decoder() {}
+  Decoder.prototype = {
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point, or |finished|.
+     */
+    handler: function(stream, bite) {}
+  };
+
+  /** @interface */
+  function Encoder() {}
+  Encoder.prototype = {
+    /**
+     * @param {Stream} stream The stream of code points being encoded.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit, or |finished|.
+     */
+    handler: function(stream, code_point) {}
+  };
+
+  // 5.2 Names and labels
+
+  // TODO: Define @typedef for Encoding: {name:string,labels:Array.<string>}
+  // https://github.com/google/closure-compiler/issues/247
+
+  /**
+   * @param {string} label The encoding label.
+   * @return {?{name:string,labels:Array.<string>}}
+   */
+  function getEncoding(label) {
+    // 1. Remove any leading and trailing ASCII whitespace from label.
+    label = String(label).trim().toLowerCase();
+
+    // 2. If label is an ASCII case-insensitive match for any of the
+    // labels listed in the table below, return the corresponding
+    // encoding, and failure otherwise.
+    if (Object.prototype.hasOwnProperty.call(label_to_encoding, label)) {
+      return label_to_encoding[label];
+    }
+    return null;
+  }
+
+  /**
+   * Encodings table: https://encoding.spec.whatwg.org/encodings.json
+   * @const
+   * @type {!Array.<{
+   *          heading: string,
+   *          encodings: Array.<{name:string,labels:Array.<string>}>
+   *        }>}
+   */
+  var encodings = [
+    {
+      "encodings": [
+        {
+          "labels": [
+            "unicode-1-1-utf-8",
+            "utf-8",
+            "utf8"
+          ],
+          "name": "UTF-8"
+        }
+      ],
+      "heading": "The Encoding"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "866",
+            "cp866",
+            "csibm866",
+            "ibm866"
+          ],
+          "name": "IBM866"
+        },
+        {
+          "labels": [
+            "csisolatin2",
+            "iso-8859-2",
+            "iso-ir-101",
+            "iso8859-2",
+            "iso88592",
+            "iso_8859-2",
+            "iso_8859-2:1987",
+            "l2",
+            "latin2"
+          ],
+          "name": "ISO-8859-2"
+        },
+        {
+          "labels": [
+            "csisolatin3",
+            "iso-8859-3",
+            "iso-ir-109",
+            "iso8859-3",
+            "iso88593",
+            "iso_8859-3",
+            "iso_8859-3:1988",
+            "l3",
+            "latin3"
+          ],
+          "name": "ISO-8859-3"
+        },
+        {
+          "labels": [
+            "csisolatin4",
+            "iso-8859-4",
+            "iso-ir-110",
+            "iso8859-4",
+            "iso88594",
+            "iso_8859-4",
+            "iso_8859-4:1988",
+            "l4",
+            "latin4"
+          ],
+          "name": "ISO-8859-4"
+        },
+        {
+          "labels": [
+            "csisolatincyrillic",
+            "cyrillic",
+            "iso-8859-5",
+            "iso-ir-144",
+            "iso8859-5",
+            "iso88595",
+            "iso_8859-5",
+            "iso_8859-5:1988"
+          ],
+          "name": "ISO-8859-5"
+        },
+        {
+          "labels": [
+            "arabic",
+            "asmo-708",
+            "csiso88596e",
+            "csiso88596i",
+            "csisolatinarabic",
+            "ecma-114",
+            "iso-8859-6",
+            "iso-8859-6-e",
+            "iso-8859-6-i",
+            "iso-ir-127",
+            "iso8859-6",
+            "iso88596",
+            "iso_8859-6",
+            "iso_8859-6:1987"
+          ],
+          "name": "ISO-8859-6"
+        },
+        {
+          "labels": [
+            "csisolatingreek",
+            "ecma-118",
+            "elot_928",
+            "greek",
+            "greek8",
+            "iso-8859-7",
+            "iso-ir-126",
+            "iso8859-7",
+            "iso88597",
+            "iso_8859-7",
+            "iso_8859-7:1987",
+            "sun_eu_greek"
+          ],
+          "name": "ISO-8859-7"
+        },
+        {
+          "labels": [
+            "csiso88598e",
+            "csisolatinhebrew",
+            "hebrew",
+            "iso-8859-8",
+            "iso-8859-8-e",
+            "iso-ir-138",
+            "iso8859-8",
+            "iso88598",
+            "iso_8859-8",
+            "iso_8859-8:1988",
+            "visual"
+          ],
+          "name": "ISO-8859-8"
+        },
+        {
+          "labels": [
+            "csiso88598i",
+            "iso-8859-8-i",
+            "logical"
+          ],
+          "name": "ISO-8859-8-I"
+        },
+        {
+          "labels": [
+            "csisolatin6",
+            "iso-8859-10",
+            "iso-ir-157",
+            "iso8859-10",
+            "iso885910",
+            "l6",
+            "latin6"
+          ],
+          "name": "ISO-8859-10"
+        },
+        {
+          "labels": [
+            "iso-8859-13",
+            "iso8859-13",
+            "iso885913"
+          ],
+          "name": "ISO-8859-13"
+        },
+        {
+          "labels": [
+            "iso-8859-14",
+            "iso8859-14",
+            "iso885914"
+          ],
+          "name": "ISO-8859-14"
+        },
+        {
+          "labels": [
+            "csisolatin9",
+            "iso-8859-15",
+            "iso8859-15",
+            "iso885915",
+            "iso_8859-15",
+            "l9"
+          ],
+          "name": "ISO-8859-15"
+        },
+        {
+          "labels": [
+            "iso-8859-16"
+          ],
+          "name": "ISO-8859-16"
+        },
+        {
+          "labels": [
+            "cskoi8r",
+            "koi",
+            "koi8",
+            "koi8-r",
+            "koi8_r"
+          ],
+          "name": "KOI8-R"
+        },
+        {
+          "labels": [
+            "koi8-ru",
+            "koi8-u"
+          ],
+          "name": "KOI8-U"
+        },
+        {
+          "labels": [
+            "csmacintosh",
+            "mac",
+            "macintosh",
+            "x-mac-roman"
+          ],
+          "name": "macintosh"
+        },
+        {
+          "labels": [
+            "dos-874",
+            "iso-8859-11",
+            "iso8859-11",
+            "iso885911",
+            "tis-620",
+            "windows-874"
+          ],
+          "name": "windows-874"
+        },
+        {
+          "labels": [
+            "cp1250",
+            "windows-1250",
+            "x-cp1250"
+          ],
+          "name": "windows-1250"
+        },
+        {
+          "labels": [
+            "cp1251",
+            "windows-1251",
+            "x-cp1251"
+          ],
+          "name": "windows-1251"
+        },
+        {
+          "labels": [
+            "ansi_x3.4-1968",
+            "ascii",
+            "cp1252",
+            "cp819",
+            "csisolatin1",
+            "ibm819",
+            "iso-8859-1",
+            "iso-ir-100",
+            "iso8859-1",
+            "iso88591",
+            "iso_8859-1",
+            "iso_8859-1:1987",
+            "l1",
+            "latin1",
+            "us-ascii",
+            "windows-1252",
+            "x-cp1252"
+          ],
+          "name": "windows-1252"
+        },
+        {
+          "labels": [
+            "cp1253",
+            "windows-1253",
+            "x-cp1253"
+          ],
+          "name": "windows-1253"
+        },
+        {
+          "labels": [
+            "cp1254",
+            "csisolatin5",
+            "iso-8859-9",
+            "iso-ir-148",
+            "iso8859-9",
+            "iso88599",
+            "iso_8859-9",
+            "iso_8859-9:1989",
+            "l5",
+            "latin5",
+            "windows-1254",
+            "x-cp1254"
+          ],
+          "name": "windows-1254"
+        },
+        {
+          "labels": [
+            "cp1255",
+            "windows-1255",
+            "x-cp1255"
+          ],
+          "name": "windows-1255"
+        },
+        {
+          "labels": [
+            "cp1256",
+            "windows-1256",
+            "x-cp1256"
+          ],
+          "name": "windows-1256"
+        },
+        {
+          "labels": [
+            "cp1257",
+            "windows-1257",
+            "x-cp1257"
+          ],
+          "name": "windows-1257"
+        },
+        {
+          "labels": [
+            "cp1258",
+            "windows-1258",
+            "x-cp1258"
+          ],
+          "name": "windows-1258"
+        },
+        {
+          "labels": [
+            "x-mac-cyrillic",
+            "x-mac-ukrainian"
+          ],
+          "name": "x-mac-cyrillic"
+        }
+      ],
+      "heading": "Legacy single-byte encodings"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "chinese",
+            "csgb2312",
+            "csiso58gb231280",
+            "gb2312",
+            "gb_2312",
+            "gb_2312-80",
+            "gbk",
+            "iso-ir-58",
+            "x-gbk"
+          ],
+          "name": "GBK"
+        },
+        {
+          "labels": [
+            "gb18030"
+          ],
+          "name": "gb18030"
+        }
+      ],
+      "heading": "Legacy multi-byte Chinese (simplified) encodings"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "big5",
+            "big5-hkscs",
+            "cn-big5",
+            "csbig5",
+            "x-x-big5"
+          ],
+          "name": "Big5"
+        }
+      ],
+      "heading": "Legacy multi-byte Chinese (traditional) encodings"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "cseucpkdfmtjapanese",
+            "euc-jp",
+            "x-euc-jp"
+          ],
+          "name": "EUC-JP"
+        },
+        {
+          "labels": [
+            "csiso2022jp",
+            "iso-2022-jp"
+          ],
+          "name": "ISO-2022-JP"
+        },
+        {
+          "labels": [
+            "csshiftjis",
+            "ms932",
+            "ms_kanji",
+            "shift-jis",
+            "shift_jis",
+            "sjis",
+            "windows-31j",
+            "x-sjis"
+          ],
+          "name": "Shift_JIS"
+        }
+      ],
+      "heading": "Legacy multi-byte Japanese encodings"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "cseuckr",
+            "csksc56011987",
+            "euc-kr",
+            "iso-ir-149",
+            "korean",
+            "ks_c_5601-1987",
+            "ks_c_5601-1989",
+            "ksc5601",
+            "ksc_5601",
+            "windows-949"
+          ],
+          "name": "EUC-KR"
+        }
+      ],
+      "heading": "Legacy multi-byte Korean encodings"
+    },
+    {
+      "encodings": [
+        {
+          "labels": [
+            "csiso2022kr",
+            "hz-gb-2312",
+            "iso-2022-cn",
+            "iso-2022-cn-ext",
+            "iso-2022-kr"
+          ],
+          "name": "replacement"
+        },
+        {
+          "labels": [
+            "utf-16be"
+          ],
+          "name": "UTF-16BE"
+        },
+        {
+          "labels": [
+            "utf-16",
+            "utf-16le"
+          ],
+          "name": "UTF-16LE"
+        },
+        {
+          "labels": [
+            "x-user-defined"
+          ],
+          "name": "x-user-defined"
+        }
+      ],
+      "heading": "Legacy miscellaneous encodings"
+    }
+  ];
+
+  // Label to encoding registry.
+  /** @type {Object.<string,{name:string,labels:Array.<string>}>} */
+  var label_to_encoding = {};
+  encodings.forEach(function(category) {
+    category.encodings.forEach(function(encoding) {
+      encoding.labels.forEach(function(label) {
+        label_to_encoding[label] = encoding;
+      });
+    });
+  });
+
+  // Registry of of encoder/decoder factories, by encoding name.
+  /** @type {Object.<string, function({fatal:boolean}): Encoder>} */
+  var encoders = {};
+  /** @type {Object.<string, function({fatal:boolean}): Decoder>} */
+  var decoders = {};
+
+  //
+  // 6. Indexes
+  //
+
+  /**
+   * @param {number} pointer The |pointer| to search for.
+   * @param {(!Array.<?number>|undefined)} index The |index| to search within.
+   * @return {?number} The code point corresponding to |pointer| in |index|,
+   *     or null if |code point| is not in |index|.
+   */
+  function indexCodePointFor(pointer, index) {
+    if (!index) return null;
+    return index[pointer] || null;
+  }
+
+  /**
+   * @param {number} code_point The |code point| to search for.
+   * @param {!Array.<?number>} index The |index| to search within.
+   * @return {?number} The first pointer corresponding to |code point| in
+   *     |index|, or null if |code point| is not in |index|.
+   */
+  function indexPointerFor(code_point, index) {
+    var pointer = index.indexOf(code_point);
+    return pointer === -1 ? null : pointer;
+  }
+
+  /**
+   * @param {string} name Name of the index.
+   * @return {(!Array.<number>|!Array.<Array.<number>>)}
+   *  */
+  function index(name) {
+    if (!('encoding-indexes' in global)) {
+      throw Error("Indexes missing." +
+                  " Did you forget to include encoding-indexes.js first?");
+    }
+    return global['encoding-indexes'][name];
+  }
+
+  /**
+   * @param {number} pointer The |pointer| to search for in the gb18030 index.
+   * @return {?number} The code point corresponding to |pointer| in |index|,
+   *     or null if |code point| is not in the gb18030 index.
+   */
+  function indexGB18030RangesCodePointFor(pointer) {
+    // 1. If pointer is greater than 39419 and less than 189000, or
+    // pointer is greater than 1237575, return null.
+    if ((pointer > 39419 && pointer < 189000) || (pointer > 1237575))
+      return null;
+
+    // 2. If pointer is 7457, return code point U+E7C7.
+    if (pointer === 7457) return 0xE7C7;
+
+    // 3. Let offset be the last pointer in index gb18030 ranges that
+    // is equal to or less than pointer and let code point offset be
+    // its corresponding code point.
+    var offset = 0;
+    var code_point_offset = 0;
+    var idx = index('gb18030-ranges');
+    var i;
+    for (i = 0; i < idx.length; ++i) {
+      /** @type {!Array.<number>} */
+      var entry = idx[i];
+      if (entry[0] <= pointer) {
+        offset = entry[0];
+        code_point_offset = entry[1];
+      } else {
+        break;
+      }
+    }
+
+    // 4. Return a code point whose value is code point offset +
+    // pointer − offset.
+    return code_point_offset + pointer - offset;
+  }
+
+  /**
+   * @param {number} code_point The |code point| to locate in the gb18030 index.
+   * @return {number} The first pointer corresponding to |code point| in the
+   *     gb18030 index.
+   */
+  function indexGB18030RangesPointerFor(code_point) {
+    // 1. If code point is U+E7C7, return pointer 7457.
+    if (code_point === 0xE7C7) return 7457;
+
+    // 2. Let offset be the last code point in index gb18030 ranges
+    // that is equal to or less than code point and let pointer offset
+    // be its corresponding pointer.
+    var offset = 0;
+    var pointer_offset = 0;
+    var idx = index('gb18030-ranges');
+    var i;
+    for (i = 0; i < idx.length; ++i) {
+      /** @type {!Array.<number>} */
+      var entry = idx[i];
+      if (entry[1] <= code_point) {
+        offset = entry[1];
+        pointer_offset = entry[0];
+      } else {
+        break;
+      }
+    }
+
+    // 3. Return a pointer whose value is pointer offset + code point
+    // − offset.
+    return pointer_offset + code_point - offset;
+  }
+
+  /**
+   * @param {number} code_point The |code_point| to search for in the Shift_JIS
+   *     index.
+   * @return {?number} The code point corresponding to |pointer| in |index|,
+   *     or null if |code point| is not in the Shift_JIS index.
+   */
+  function indexShiftJISPointerFor(code_point) {
+    // 1. Let index be index jis0208 excluding all entries whose
+    // pointer is in the range 8272 to 8835, inclusive.
+    shift_jis_index = shift_jis_index ||
+      index('jis0208').map(function(code_point, pointer) {
+        return inRange(pointer, 8272, 8835) ? null : code_point;
+      });
+    var index_ = shift_jis_index;
+
+    // 2. Return the index pointer for code point in index.
+    return index_.indexOf(code_point);
+  }
+  var shift_jis_index;
+
+  /**
+   * @param {number} code_point The |code_point| to search for in the big5
+   *     index.
+   * @return {?number} The code point corresponding to |pointer| in |index|,
+   *     or null if |code point| is not in the big5 index.
+   */
+  function indexBig5PointerFor(code_point) {
+    // 1. Let index be index Big5 excluding all entries whose pointer
+    big5_index_no_hkscs = big5_index_no_hkscs ||
+      index('big5').map(function(code_point, pointer) {
+        return (pointer < (0xA1 - 0x81) * 157) ? null : code_point;
+      });
+    var index_ = big5_index_no_hkscs;
+
+    // 2. If code point is U+2550, U+255E, U+2561, U+256A, U+5341, or
+    // U+5345, return the last pointer corresponding to code point in
+    // index.
+    if (code_point === 0x2550 || code_point === 0x255E ||
+        code_point === 0x2561 || code_point === 0x256A ||
+        code_point === 0x5341 || code_point === 0x5345) {
+      return index_.lastIndexOf(code_point);
+    }
+
+    // 3. Return the index pointer for code point in index.
+    return indexPointerFor(code_point, index_);
+  }
+  var big5_index_no_hkscs;
+
+  //
+  // 8. API
+  //
+
+  /** @const */ var DEFAULT_ENCODING = 'utf-8';
+
+  // 8.1 Interface TextDecoder
+
+  /**
+   * @constructor
+   * @param {string=} label The label of the encoding;
+   *     defaults to 'utf-8'.
+   * @param {Object=} options
+   */
+  function TextDecoder(label, options) {
+    // Web IDL conventions
+    if (!(this instanceof TextDecoder))
+      throw TypeError('Called as a function. Did you forget \'new\'?');
+    label = label !== undefined ? String(label) : DEFAULT_ENCODING;
+    options = ToDictionary(options);
+
+    // A TextDecoder object has an associated encoding, decoder,
+    // stream, ignore BOM flag (initially unset), BOM seen flag
+    // (initially unset), error mode (initially replacement), and do
+    // not flush flag (initially unset).
+
+    /** @private */
+    this._encoding = null;
+    /** @private @type {?Decoder} */
+    this._decoder = null;
+    /** @private @type {boolean} */
+    this._ignoreBOM = false;
+    /** @private @type {boolean} */
+    this._BOMseen = false;
+    /** @private @type {string} */
+    this._error_mode = 'replacement';
+    /** @private @type {boolean} */
+    this._do_not_flush = false;
+
+
+    // 1. Let encoding be the result of getting an encoding from
+    // label.
+    var encoding = getEncoding(label);
+
+    // 2. If encoding is failure or replacement, throw a RangeError.
+    if (encoding === null || encoding.name === 'replacement')
+      throw RangeError('Unknown encoding: ' + label);
+    if (!decoders[encoding.name]) {
+      throw Error('Decoder not present.' +
+                  ' Did you forget to include encoding-indexes.js first?');
+    }
+
+    // 3. Let dec be a new TextDecoder object.
+    var dec = this;
+
+    // 4. Set dec's encoding to encoding.
+    dec._encoding = encoding;
+
+    // 5. If options's fatal member is true, set dec's error mode to
+    // fatal.
+    if (Boolean(options['fatal']))
+      dec._error_mode = 'fatal';
+
+    // 6. If options's ignoreBOM member is true, set dec's ignore BOM
+    // flag.
+    if (Boolean(options['ignoreBOM']))
+      dec._ignoreBOM = true;
+
+    // For pre-ES5 runtimes:
+    if (!Object.defineProperty) {
+      this.encoding = dec._encoding.name.toLowerCase();
+      this.fatal = dec._error_mode === 'fatal';
+      this.ignoreBOM = dec._ignoreBOM;
+    }
+
+    // 7. Return dec.
+    return dec;
+  }
+
+  if (Object.defineProperty) {
+    // The encoding attribute's getter must return encoding's name.
+    Object.defineProperty(TextDecoder.prototype, 'encoding', {
+      /** @this {TextDecoder} */
+      get: function() { return this._encoding.name.toLowerCase(); }
+    });
+
+    // The fatal attribute's getter must return true if error mode
+    // is fatal, and false otherwise.
+    Object.defineProperty(TextDecoder.prototype, 'fatal', {
+      /** @this {TextDecoder} */
+      get: function() { return this._error_mode === 'fatal'; }
+    });
+
+    // The ignoreBOM attribute's getter must return true if ignore
+    // BOM flag is set, and false otherwise.
+    Object.defineProperty(TextDecoder.prototype, 'ignoreBOM', {
+      /** @this {TextDecoder} */
+      get: function() { return this._ignoreBOM; }
+    });
+  }
+
+  /**
+   * @param {BufferSource=} input The buffer of bytes to decode.
+   * @param {Object=} options
+   * @return {string} The decoded string.
+   */
+  TextDecoder.prototype.decode = function decode(input, options) {
+    var bytes;
+    if (typeof input === 'object' && input instanceof ArrayBuffer) {
+      bytes = new Uint8Array(input);
+    } else if (typeof input === 'object' && 'buffer' in input &&
+               input.buffer instanceof ArrayBuffer) {
+      bytes = new Uint8Array(input.buffer,
+                             input.byteOffset,
+                             input.byteLength);
+    } else {
+      bytes = new Uint8Array(0);
+    }
+
+    options = ToDictionary(options);
+
+    // 1. If the do not flush flag is unset, set decoder to a new
+    // encoding's decoder, set stream to a new stream, and unset the
+    // BOM seen flag.
+    if (!this._do_not_flush) {
+      this._decoder = decoders[this._encoding.name]({
+        fatal: this._error_mode === 'fatal'});
+      this._BOMseen = false;
+    }
+
+    // 2. If options's stream is true, set the do not flush flag, and
+    // unset the do not flush flag otherwise.
+    this._do_not_flush = Boolean(options['stream']);
+
+    // 3. If input is given, push a copy of input to stream.
+    // TODO: Align with spec algorithm - maintain stream on instance.
+    var input_stream = new Stream(bytes);
+
+    // 4. Let output be a new stream.
+    var output = [];
+
+    /** @type {?(number|!Array.<number>)} */
+    var result;
+
+    // 5. While true:
+    while (true) {
+      // 1. Let token be the result of reading from stream.
+      var token = input_stream.read();
+
+      // 2. If token is end-of-stream and the do not flush flag is
+      // set, return output, serialized.
+      // TODO: Align with spec algorithm.
+      if (token === end_of_stream)
+        break;
+
+      // 3. Otherwise, run these subsubsteps:
+
+      // 1. Let result be the result of processing token for decoder,
+      // stream, output, and error mode.
+      result = this._decoder.handler(input_stream, token);
+
+      // 2. If result is finished, return output, serialized.
+      if (result === finished)
+        break;
+
+      if (result !== null) {
+        if (Array.isArray(result))
+          output.push.apply(output, /**@type {!Array.<number>}*/(result));
+        else
+          output.push(result);
+      }
+
+      // 3. Otherwise, if result is error, throw a TypeError.
+      // (Thrown in handler)
+
+      // 4. Otherwise, do nothing.
+    }
+    // TODO: Align with spec algorithm.
+    if (!this._do_not_flush) {
+      do {
+        result = this._decoder.handler(input_stream, input_stream.read());
+        if (result === finished)
+          break;
+        if (result === null)
+          continue;
+        if (Array.isArray(result))
+          output.push.apply(output, /**@type {!Array.<number>}*/(result));
+        else
+          output.push(result);
+      } while (!input_stream.endOfStream());
+      this._decoder = null;
+    }
+
+    // A TextDecoder object also has an associated serialize stream
+    // algorithm...
+    /**
+     * @param {!Array.<number>} stream
+     * @return {string}
+     * @this {TextDecoder}
+     */
+    function serializeStream(stream) {
+      // 1. Let token be the result of reading from stream.
+      // (Done in-place on array, rather than as a stream)
+
+      // 2. If encoding is UTF-8, UTF-16BE, or UTF-16LE, and ignore
+      // BOM flag and BOM seen flag are unset, run these subsubsteps:
+      if (includes(['UTF-8', 'UTF-16LE', 'UTF-16BE'], this._encoding.name) &&
+          !this._ignoreBOM && !this._BOMseen) {
+        if (stream.length > 0 && stream[0] === 0xFEFF) {
+          // 1. If token is U+FEFF, set BOM seen flag.
+          this._BOMseen = true;
+          stream.shift();
+        } else if (stream.length > 0) {
+          // 2. Otherwise, if token is not end-of-stream, set BOM seen
+          // flag and append token to stream.
+          this._BOMseen = true;
+        } else {
+          // 3. Otherwise, if token is not end-of-stream, append token
+          // to output.
+          // (no-op)
+        }
+      }
+      // 4. Otherwise, return output.
+      return codePointsToString(stream);
+    }
+
+    return serializeStream.call(this, output);
+  };
+
+  // 8.2 Interface TextEncoder
+
+  /**
+   * @constructor
+   * @param {string=} label The label of the encoding. NONSTANDARD.
+   * @param {Object=} options NONSTANDARD.
+   */
+  function TextEncoder(label, options) {
+    // Web IDL conventions
+    if (!(this instanceof TextEncoder))
+      throw TypeError('Called as a function. Did you forget \'new\'?');
+    options = ToDictionary(options);
+
+    // A TextEncoder object has an associated encoding and encoder.
+
+    /** @private */
+    this._encoding = null;
+    /** @private @type {?Encoder} */
+    this._encoder = null;
+
+    // Non-standard
+    /** @private @type {boolean} */
+    this._do_not_flush = false;
+    /** @private @type {string} */
+    this._fatal = Boolean(options['fatal']) ? 'fatal' : 'replacement';
+
+    // 1. Let enc be a new TextEncoder object.
+    var enc = this;
+
+    // 2. Set enc's encoding to UTF-8's encoder.
+    if (Boolean(options['NONSTANDARD_allowLegacyEncoding'])) {
+      // NONSTANDARD behavior.
+      label = label !== undefined ? String(label) : DEFAULT_ENCODING;
+      var encoding = getEncoding(label);
+      if (encoding === null || encoding.name === 'replacement')
+        throw RangeError('Unknown encoding: ' + label);
+      if (!encoders[encoding.name]) {
+        throw Error('Encoder not present.' +
+                    ' Did you forget to include encoding-indexes.js first?');
+      }
+      enc._encoding = encoding;
+    } else {
+      // Standard behavior.
+      enc._encoding = getEncoding('utf-8');
+
+      if (label !== undefined && 'console' in global) {
+        console.warn('TextEncoder constructor called with encoding label, '
+                     + 'which is ignored.');
+      }
+    }
+
+    // For pre-ES5 runtimes:
+    if (!Object.defineProperty)
+      this.encoding = enc._encoding.name.toLowerCase();
+
+    // 3. Return enc.
+    return enc;
+  }
+
+  if (Object.defineProperty) {
+    // The encoding attribute's getter must return encoding's name.
+    Object.defineProperty(TextEncoder.prototype, 'encoding', {
+      /** @this {TextEncoder} */
+      get: function() { return this._encoding.name.toLowerCase(); }
+    });
+  }
+
+  /**
+   * @param {string=} opt_string The string to encode.
+   * @param {Object=} options
+   * @return {!Uint8Array} Encoded bytes, as a Uint8Array.
+   */
+  TextEncoder.prototype.encode = function encode(opt_string, options) {
+    opt_string = opt_string === undefined ? '' : String(opt_string);
+    options = ToDictionary(options);
+
+    // NOTE: This option is nonstandard. None of the encodings
+    // permitted for encoding (i.e. UTF-8, UTF-16) are stateful when
+    // the input is a USVString so streaming is not necessary.
+    if (!this._do_not_flush)
+      this._encoder = encoders[this._encoding.name]({
+        fatal: this._fatal === 'fatal'});
+    this._do_not_flush = Boolean(options['stream']);
+
+    // 1. Convert input to a stream.
+    var input = new Stream(stringToCodePoints(opt_string));
+
+    // 2. Let output be a new stream
+    var output = [];
+
+    /** @type {?(number|!Array.<number>)} */
+    var result;
+    // 3. While true, run these substeps:
+    while (true) {
+      // 1. Let token be the result of reading from input.
+      var token = input.read();
+      if (token === end_of_stream)
+        break;
+      // 2. Let result be the result of processing token for encoder,
+      // input, output.
+      result = this._encoder.handler(input, token);
+      if (result === finished)
+        break;
+      if (Array.isArray(result))
+        output.push.apply(output, /**@type {!Array.<number>}*/(result));
+      else
+        output.push(result);
+    }
+    // TODO: Align with spec algorithm.
+    if (!this._do_not_flush) {
+      while (true) {
+        result = this._encoder.handler(input, input.read());
+        if (result === finished)
+          break;
+        if (Array.isArray(result))
+          output.push.apply(output, /**@type {!Array.<number>}*/(result));
+        else
+          output.push(result);
+      }
+      this._encoder = null;
+    }
+    // 3. If result is finished, convert output into a byte sequence,
+    // and then return a Uint8Array object wrapping an ArrayBuffer
+    // containing output.
+    return new Uint8Array(output);
+  };
+
+
+  //
+  // 9. The encoding
+  //
+
+  // 9.1 utf-8
+
+  // 9.1.1 utf-8 decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function UTF8Decoder(options) {
+    var fatal = options.fatal;
+
+    // utf-8's decoder's has an associated utf-8 code point, utf-8
+    // bytes seen, and utf-8 bytes needed (all initially 0), a utf-8
+    // lower boundary (initially 0x80), and a utf-8 upper boundary
+    // (initially 0xBF).
+    var /** @type {number} */ utf8_code_point = 0,
+        /** @type {number} */ utf8_bytes_seen = 0,
+        /** @type {number} */ utf8_bytes_needed = 0,
+        /** @type {number} */ utf8_lower_boundary = 0x80,
+        /** @type {number} */ utf8_upper_boundary = 0xBF;
+
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and utf-8 bytes needed is not 0,
+      // set utf-8 bytes needed to 0 and return error.
+      if (bite === end_of_stream && utf8_bytes_needed !== 0) {
+        utf8_bytes_needed = 0;
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream, return finished.
+      if (bite === end_of_stream)
+        return finished;
+
+      // 3. If utf-8 bytes needed is 0, based on byte:
+      if (utf8_bytes_needed === 0) {
+
+        // 0x00 to 0x7F
+        if (inRange(bite, 0x00, 0x7F)) {
+          // Return a code point whose value is byte.
+          return bite;
+        }
+
+        // 0xC2 to 0xDF
+        else if (inRange(bite, 0xC2, 0xDF)) {
+          // 1. Set utf-8 bytes needed to 1.
+          utf8_bytes_needed = 1;
+
+          // 2. Set UTF-8 code point to byte & 0x1F.
+          utf8_code_point = bite & 0x1F;
+        }
+
+        // 0xE0 to 0xEF
+        else if (inRange(bite, 0xE0, 0xEF)) {
+          // 1. If byte is 0xE0, set utf-8 lower boundary to 0xA0.
+          if (bite === 0xE0)
+            utf8_lower_boundary = 0xA0;
+          // 2. If byte is 0xED, set utf-8 upper boundary to 0x9F.
+          if (bite === 0xED)
+            utf8_upper_boundary = 0x9F;
+          // 3. Set utf-8 bytes needed to 2.
+          utf8_bytes_needed = 2;
+          // 4. Set UTF-8 code point to byte & 0xF.
+          utf8_code_point = bite & 0xF;
+        }
+
+        // 0xF0 to 0xF4
+        else if (inRange(bite, 0xF0, 0xF4)) {
+          // 1. If byte is 0xF0, set utf-8 lower boundary to 0x90.
+          if (bite === 0xF0)
+            utf8_lower_boundary = 0x90;
+          // 2. If byte is 0xF4, set utf-8 upper boundary to 0x8F.
+          if (bite === 0xF4)
+            utf8_upper_boundary = 0x8F;
+          // 3. Set utf-8 bytes needed to 3.
+          utf8_bytes_needed = 3;
+          // 4. Set UTF-8 code point to byte & 0x7.
+          utf8_code_point = bite & 0x7;
+        }
+
+        // Otherwise
+        else {
+          // Return error.
+          return decoderError(fatal);
+        }
+
+        // Return continue.
+        return null;
+      }
+
+      // 4. If byte is not in the range utf-8 lower boundary to utf-8
+      // upper boundary, inclusive, run these substeps:
+      if (!inRange(bite, utf8_lower_boundary, utf8_upper_boundary)) {
+
+        // 1. Set utf-8 code point, utf-8 bytes needed, and utf-8
+        // bytes seen to 0, set utf-8 lower boundary to 0x80, and set
+        // utf-8 upper boundary to 0xBF.
+        utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0;
+        utf8_lower_boundary = 0x80;
+        utf8_upper_boundary = 0xBF;
+
+        // 2. Prepend byte to stream.
+        stream.prepend(bite);
+
+        // 3. Return error.
+        return decoderError(fatal);
+      }
+
+      // 5. Set utf-8 lower boundary to 0x80 and utf-8 upper boundary
+      // to 0xBF.
+      utf8_lower_boundary = 0x80;
+      utf8_upper_boundary = 0xBF;
+
+      // 6. Set UTF-8 code point to (UTF-8 code point << 6) | (byte &
+      // 0x3F)
+      utf8_code_point = (utf8_code_point << 6) | (bite & 0x3F);
+
+      // 7. Increase utf-8 bytes seen by one.
+      utf8_bytes_seen += 1;
+
+      // 8. If utf-8 bytes seen is not equal to utf-8 bytes needed,
+      // continue.
+      if (utf8_bytes_seen !== utf8_bytes_needed)
+        return null;
+
+      // 9. Let code point be utf-8 code point.
+      var code_point = utf8_code_point;
+
+      // 10. Set utf-8 code point, utf-8 bytes needed, and utf-8 bytes
+      // seen to 0.
+      utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0;
+
+      // 11. Return a code point whose value is code point.
+      return code_point;
+    };
+  }
+
+  // 9.1.2 utf-8 encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function UTF8Encoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. Set count and offset based on the range code point is in:
+      var count, offset;
+      // U+0080 to U+07FF, inclusive:
+      if (inRange(code_point, 0x0080, 0x07FF)) {
+        // 1 and 0xC0
+        count = 1;
+        offset = 0xC0;
+      }
+      // U+0800 to U+FFFF, inclusive:
+      else if (inRange(code_point, 0x0800, 0xFFFF)) {
+        // 2 and 0xE0
+        count = 2;
+        offset = 0xE0;
+      }
+      // U+10000 to U+10FFFF, inclusive:
+      else if (inRange(code_point, 0x10000, 0x10FFFF)) {
+        // 3 and 0xF0
+        count = 3;
+        offset = 0xF0;
+      }
+
+      // 4. Let bytes be a byte sequence whose first byte is (code
+      // point >> (6 × count)) + offset.
+      var bytes = [(code_point >> (6 * count)) + offset];
+
+      // 5. Run these substeps while count is greater than 0:
+      while (count > 0) {
+
+        // 1. Set temp to code point >> (6 × (count − 1)).
+        var temp = code_point >> (6 * (count - 1));
+
+        // 2. Append to bytes 0x80 | (temp & 0x3F).
+        bytes.push(0x80 | (temp & 0x3F));
+
+        // 3. Decrease count by one.
+        count -= 1;
+      }
+
+      // 6. Return bytes bytes, in order.
+      return bytes;
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['UTF-8'] = function(options) {
+    return new UTF8Encoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['UTF-8'] = function(options) {
+    return new UTF8Decoder(options);
+  };
+
+  //
+  // 10. Legacy single-byte encodings
+  //
+
+  // 10.1 single-byte decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {!Array.<number>} index The encoding index.
+   * @param {{fatal: boolean}} options
+   */
+  function SingleByteDecoder(index, options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream, return finished.
+      if (bite === end_of_stream)
+        return finished;
+
+      // 2. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 3. Let code point be the index code point for byte − 0x80 in
+      // index single-byte.
+      var code_point = index[bite - 0x80];
+
+      // 4. If code point is null, return error.
+      if (code_point === null)
+        return decoderError(fatal);
+
+      // 5. Return a code point whose value is code point.
+      return code_point;
+    };
+  }
+
+  // 10.2 single-byte encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {!Array.<?number>} index The encoding index.
+   * @param {{fatal: boolean}} options
+   */
+  function SingleByteEncoder(index, options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. Let pointer be the index pointer for code point in index
+      // single-byte.
+      var pointer = indexPointerFor(code_point, index);
+
+      // 4. If pointer is null, return error with code point.
+      if (pointer === null)
+        encoderError(code_point);
+
+      // 5. Return a byte whose value is pointer + 0x80.
+      return pointer + 0x80;
+    };
+  }
+
+  (function() {
+    if (!('encoding-indexes' in global))
+      return;
+    encodings.forEach(function(category) {
+      if (category.heading !== 'Legacy single-byte encodings')
+        return;
+      category.encodings.forEach(function(encoding) {
+        var name = encoding.name;
+        var idx = index(name.toLowerCase());
+        /** @param {{fatal: boolean}} options */
+        decoders[name] = function(options) {
+          return new SingleByteDecoder(idx, options);
+        };
+        /** @param {{fatal: boolean}} options */
+        encoders[name] = function(options) {
+          return new SingleByteEncoder(idx, options);
+        };
+      });
+    });
+  }());
+
+  //
+  // 11. Legacy multi-byte Chinese (simplified) encodings
+  //
+
+  // 11.1 gbk
+
+  // 11.1.1 gbk decoder
+  // gbk's decoder is gb18030's decoder.
+  /** @param {{fatal: boolean}} options */
+  decoders['GBK'] = function(options) {
+    return new GB18030Decoder(options);
+  };
+
+  // 11.1.2 gbk encoder
+  // gbk's encoder is gb18030's encoder with its gbk flag set.
+  /** @param {{fatal: boolean}} options */
+  encoders['GBK'] = function(options) {
+    return new GB18030Encoder(options, true);
+  };
+
+  // 11.2 gb18030
+  // 11.2.1 gb18030 decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function GB18030Decoder(options) {
+    var fatal = options.fatal;
+    // gb18030's decoder has an associated gb18030 first, gb18030
+    // second, and gb18030 third (all initially 0x00).
+    var /** @type {number} */ gb18030_first = 0x00,
+        /** @type {number} */ gb18030_second = 0x00,
+        /** @type {number} */ gb18030_third = 0x00;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and gb18030 first, gb18030
+      // second, and gb18030 third are 0x00, return finished.
+      if (bite === end_of_stream && gb18030_first === 0x00 &&
+          gb18030_second === 0x00 && gb18030_third === 0x00) {
+        return finished;
+      }
+      // 2. If byte is end-of-stream, and gb18030 first, gb18030
+      // second, or gb18030 third is not 0x00, set gb18030 first,
+      // gb18030 second, and gb18030 third to 0x00, and return error.
+      if (bite === end_of_stream &&
+          (gb18030_first !== 0x00 || gb18030_second !== 0x00 ||
+           gb18030_third !== 0x00)) {
+        gb18030_first = 0x00;
+        gb18030_second = 0x00;
+        gb18030_third = 0x00;
+        decoderError(fatal);
+      }
+      var code_point;
+      // 3. If gb18030 third is not 0x00, run these substeps:
+      if (gb18030_third !== 0x00) {
+        // 1. Let code point be null.
+        code_point = null;
+        // 2. If byte is in the range 0x30 to 0x39, inclusive, set
+        // code point to the index gb18030 ranges code point for
+        // (((gb18030 first − 0x81) × 10 + gb18030 second − 0x30) ×
+        // 126 + gb18030 third − 0x81) × 10 + byte − 0x30.
+        if (inRange(bite, 0x30, 0x39)) {
+          code_point = indexGB18030RangesCodePointFor(
+              (((gb18030_first - 0x81) * 10 + gb18030_second - 0x30) * 126 +
+               gb18030_third - 0x81) * 10 + bite - 0x30);
+        }
+
+        // 3. Let buffer be a byte sequence consisting of gb18030
+        // second, gb18030 third, and byte, in order.
+        var buffer = [gb18030_second, gb18030_third, bite];
+
+        // 4. Set gb18030 first, gb18030 second, and gb18030 third to
+        // 0x00.
+        gb18030_first = 0x00;
+        gb18030_second = 0x00;
+        gb18030_third = 0x00;
+
+        // 5. If code point is null, prepend buffer to stream and
+        // return error.
+        if (code_point === null) {
+          stream.prepend(buffer);
+          return decoderError(fatal);
+        }
+
+        // 6. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 4. If gb18030 second is not 0x00, run these substeps:
+      if (gb18030_second !== 0x00) {
+
+        // 1. If byte is in the range 0x81 to 0xFE, inclusive, set
+        // gb18030 third to byte and return continue.
+        if (inRange(bite, 0x81, 0xFE)) {
+          gb18030_third = bite;
+          return null;
+        }
+
+        // 2. Prepend gb18030 second followed by byte to stream, set
+        // gb18030 first and gb18030 second to 0x00, and return error.
+        stream.prepend([gb18030_second, bite]);
+        gb18030_first = 0x00;
+        gb18030_second = 0x00;
+        return decoderError(fatal);
+      }
+
+      // 5. If gb18030 first is not 0x00, run these substeps:
+      if (gb18030_first !== 0x00) {
+
+        // 1. If byte is in the range 0x30 to 0x39, inclusive, set
+        // gb18030 second to byte and return continue.
+        if (inRange(bite, 0x30, 0x39)) {
+          gb18030_second = bite;
+          return null;
+        }
+
+        // 2. Let lead be gb18030 first, let pointer be null, and set
+        // gb18030 first to 0x00.
+        var lead = gb18030_first;
+        var pointer = null;
+        gb18030_first = 0x00;
+
+        // 3. Let offset be 0x40 if byte is less than 0x7F and 0x41
+        // otherwise.
+        var offset = bite < 0x7F ? 0x40 : 0x41;
+
+        // 4. If byte is in the range 0x40 to 0x7E, inclusive, or 0x80
+        // to 0xFE, inclusive, set pointer to (lead − 0x81) × 190 +
+        // (byte − offset).
+        if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFE))
+          pointer = (lead - 0x81) * 190 + (bite - offset);
+
+        // 5. Let code point be null if pointer is null and the index
+        // code point for pointer in index gb18030 otherwise.
+        code_point = pointer === null ? null :
+            indexCodePointFor(pointer, index('gb18030'));
+
+        // 6. If code point is null and byte is an ASCII byte, prepend
+        // byte to stream.
+        if (code_point === null && isASCIIByte(bite))
+          stream.prepend(bite);
+
+        // 7. If code point is null, return error.
+        if (code_point === null)
+          return decoderError(fatal);
+
+        // 8. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 6. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 7. If byte is 0x80, return code point U+20AC.
+      if (bite === 0x80)
+        return 0x20AC;
+
+      // 8. If byte is in the range 0x81 to 0xFE, inclusive, set
+      // gb18030 first to byte and return continue.
+      if (inRange(bite, 0x81, 0xFE)) {
+        gb18030_first = bite;
+        return null;
+      }
+
+      // 9. Return error.
+      return decoderError(fatal);
+    };
+  }
+
+  // 11.2.2 gb18030 encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   * @param {boolean=} gbk_flag
+   */
+  function GB18030Encoder(options, gbk_flag) {
+    var fatal = options.fatal;
+    // gb18030's decoder has an associated gbk flag (initially unset).
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. If code point is U+E5E5, return error with code point.
+      if (code_point === 0xE5E5)
+        return encoderError(code_point);
+
+      // 4. If the gbk flag is set and code point is U+20AC, return
+      // byte 0x80.
+      if (gbk_flag && code_point === 0x20AC)
+        return 0x80;
+
+      // 5. Let pointer be the index pointer for code point in index
+      // gb18030.
+      var pointer = indexPointerFor(code_point, index('gb18030'));
+
+      // 6. If pointer is not null, run these substeps:
+      if (pointer !== null) {
+
+        // 1. Let lead be floor(pointer / 190) + 0x81.
+        var lead = floor(pointer / 190) + 0x81;
+
+        // 2. Let trail be pointer % 190.
+        var trail = pointer % 190;
+
+        // 3. Let offset be 0x40 if trail is less than 0x3F and 0x41 otherwise.
+        var offset = trail < 0x3F ? 0x40 : 0x41;
+
+        // 4. Return two bytes whose values are lead and trail + offset.
+        return [lead, trail + offset];
+      }
+
+      // 7. If gbk flag is set, return error with code point.
+      if (gbk_flag)
+        return encoderError(code_point);
+
+      // 8. Set pointer to the index gb18030 ranges pointer for code
+      // point.
+      pointer = indexGB18030RangesPointerFor(code_point);
+
+      // 9. Let byte1 be floor(pointer / 10 / 126 / 10).
+      var byte1 = floor(pointer / 10 / 126 / 10);
+
+      // 10. Set pointer to pointer − byte1 × 10 × 126 × 10.
+      pointer = pointer - byte1 * 10 * 126 * 10;
+
+      // 11. Let byte2 be floor(pointer / 10 / 126).
+      var byte2 = floor(pointer / 10 / 126);
+
+      // 12. Set pointer to pointer − byte2 × 10 × 126.
+      pointer = pointer - byte2 * 10 * 126;
+
+      // 13. Let byte3 be floor(pointer / 10).
+      var byte3 = floor(pointer / 10);
+
+      // 14. Let byte4 be pointer − byte3 × 10.
+      var byte4 = pointer - byte3 * 10;
+
+      // 15. Return four bytes whose values are byte1 + 0x81, byte2 +
+      // 0x30, byte3 + 0x81, byte4 + 0x30.
+      return [byte1 + 0x81,
+              byte2 + 0x30,
+              byte3 + 0x81,
+              byte4 + 0x30];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['gb18030'] = function(options) {
+    return new GB18030Encoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['gb18030'] = function(options) {
+    return new GB18030Decoder(options);
+  };
+
+
+  //
+  // 12. Legacy multi-byte Chinese (traditional) encodings
+  //
+
+  // 12.1 Big5
+
+  // 12.1.1 Big5 decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function Big5Decoder(options) {
+    var fatal = options.fatal;
+    // Big5's decoder has an associated Big5 lead (initially 0x00).
+    var /** @type {number} */ Big5_lead = 0x00;
+
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and Big5 lead is not 0x00, set
+      // Big5 lead to 0x00 and return error.
+      if (bite === end_of_stream && Big5_lead !== 0x00) {
+        Big5_lead = 0x00;
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream and Big5 lead is 0x00, return
+      // finished.
+      if (bite === end_of_stream && Big5_lead === 0x00)
+        return finished;
+
+      // 3. If Big5 lead is not 0x00, let lead be Big5 lead, let
+      // pointer be null, set Big5 lead to 0x00, and then run these
+      // substeps:
+      if (Big5_lead !== 0x00) {
+        var lead = Big5_lead;
+        var pointer = null;
+        Big5_lead = 0x00;
+
+        // 1. Let offset be 0x40 if byte is less than 0x7F and 0x62
+        // otherwise.
+        var offset = bite < 0x7F ? 0x40 : 0x62;
+
+        // 2. If byte is in the range 0x40 to 0x7E, inclusive, or 0xA1
+        // to 0xFE, inclusive, set pointer to (lead − 0x81) × 157 +
+        // (byte − offset).
+        if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0xA1, 0xFE))
+          pointer = (lead - 0x81) * 157 + (bite - offset);
+
+        // 3. If there is a row in the table below whose first column
+        // is pointer, return the two code points listed in its second
+        // column
+        // Pointer | Code points
+        // --------+--------------
+        // 1133    | U+00CA U+0304
+        // 1135    | U+00CA U+030C
+        // 1164    | U+00EA U+0304
+        // 1166    | U+00EA U+030C
+        switch (pointer) {
+          case 1133: return [0x00CA, 0x0304];
+          case 1135: return [0x00CA, 0x030C];
+          case 1164: return [0x00EA, 0x0304];
+          case 1166: return [0x00EA, 0x030C];
+        }
+
+        // 4. Let code point be null if pointer is null and the index
+        // code point for pointer in index Big5 otherwise.
+        var code_point = (pointer === null) ? null :
+            indexCodePointFor(pointer, index('big5'));
+
+        // 5. If code point is null and byte is an ASCII byte, prepend
+        // byte to stream.
+        if (code_point === null && isASCIIByte(bite))
+          stream.prepend(bite);
+
+        // 6. If code point is null, return error.
+        if (code_point === null)
+          return decoderError(fatal);
+
+        // 7. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 4. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 5. If byte is in the range 0x81 to 0xFE, inclusive, set Big5
+      // lead to byte and return continue.
+      if (inRange(bite, 0x81, 0xFE)) {
+        Big5_lead = bite;
+        return null;
+      }
+
+      // 6. Return error.
+      return decoderError(fatal);
+    };
+  }
+
+  // 12.1.2 Big5 encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function Big5Encoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. Let pointer be the index Big5 pointer for code point.
+      var pointer = indexBig5PointerFor(code_point);
+
+      // 4. If pointer is null, return error with code point.
+      if (pointer === null)
+        return encoderError(code_point);
+
+      // 5. Let lead be floor(pointer / 157) + 0x81.
+      var lead = floor(pointer / 157) + 0x81;
+
+      // 6. If lead is less than 0xA1, return error with code point.
+      if (lead < 0xA1)
+        return encoderError(code_point);
+
+      // 7. Let trail be pointer % 157.
+      var trail = pointer % 157;
+
+      // 8. Let offset be 0x40 if trail is less than 0x3F and 0x62
+      // otherwise.
+      var offset = trail < 0x3F ? 0x40 : 0x62;
+
+      // Return two bytes whose values are lead and trail + offset.
+      return [lead, trail + offset];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['Big5'] = function(options) {
+    return new Big5Encoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['Big5'] = function(options) {
+    return new Big5Decoder(options);
+  };
+
+
+  //
+  // 13. Legacy multi-byte Japanese encodings
+  //
+
+  // 13.1 euc-jp
+
+  // 13.1.1 euc-jp decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function EUCJPDecoder(options) {
+    var fatal = options.fatal;
+
+    // euc-jp's decoder has an associated euc-jp jis0212 flag
+    // (initially unset) and euc-jp lead (initially 0x00).
+    var /** @type {boolean} */ eucjp_jis0212_flag = false,
+        /** @type {number} */ eucjp_lead = 0x00;
+
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and euc-jp lead is not 0x00, set
+      // euc-jp lead to 0x00, and return error.
+      if (bite === end_of_stream && eucjp_lead !== 0x00) {
+        eucjp_lead = 0x00;
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream and euc-jp lead is 0x00, return
+      // finished.
+      if (bite === end_of_stream && eucjp_lead === 0x00)
+        return finished;
+
+      // 3. If euc-jp lead is 0x8E and byte is in the range 0xA1 to
+      // 0xDF, inclusive, set euc-jp lead to 0x00 and return a code
+      // point whose value is 0xFF61 − 0xA1 + byte.
+      if (eucjp_lead === 0x8E && inRange(bite, 0xA1, 0xDF)) {
+        eucjp_lead = 0x00;
+        return 0xFF61 - 0xA1 + bite;
+      }
+
+      // 4. If euc-jp lead is 0x8F and byte is in the range 0xA1 to
+      // 0xFE, inclusive, set the euc-jp jis0212 flag, set euc-jp lead
+      // to byte, and return continue.
+      if (eucjp_lead === 0x8F && inRange(bite, 0xA1, 0xFE)) {
+        eucjp_jis0212_flag = true;
+        eucjp_lead = bite;
+        return null;
+      }
+
+      // 5. If euc-jp lead is not 0x00, let lead be euc-jp lead, set
+      // euc-jp lead to 0x00, and run these substeps:
+      if (eucjp_lead !== 0x00) {
+        var lead = eucjp_lead;
+        eucjp_lead = 0x00;
+
+        // 1. Let code point be null.
+        var code_point = null;
+
+        // 2. If lead and byte are both in the range 0xA1 to 0xFE,
+        // inclusive, set code point to the index code point for (lead
+        // − 0xA1) × 94 + byte − 0xA1 in index jis0208 if the euc-jp
+        // jis0212 flag is unset and in index jis0212 otherwise.
+        if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) {
+          code_point = indexCodePointFor(
+            (lead - 0xA1) * 94 + (bite - 0xA1),
+            index(!eucjp_jis0212_flag ? 'jis0208' : 'jis0212'));
+        }
+
+        // 3. Unset the euc-jp jis0212 flag.
+        eucjp_jis0212_flag = false;
+
+        // 4. If byte is not in the range 0xA1 to 0xFE, inclusive,
+        // prepend byte to stream.
+        if (!inRange(bite, 0xA1, 0xFE))
+          stream.prepend(bite);
+
+        // 5. If code point is null, return error.
+        if (code_point === null)
+          return decoderError(fatal);
+
+        // 6. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 6. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 7. If byte is 0x8E, 0x8F, or in the range 0xA1 to 0xFE,
+      // inclusive, set euc-jp lead to byte and return continue.
+      if (bite === 0x8E || bite === 0x8F || inRange(bite, 0xA1, 0xFE)) {
+        eucjp_lead = bite;
+        return null;
+      }
+
+      // 8. Return error.
+      return decoderError(fatal);
+    };
+  }
+
+  // 13.1.2 euc-jp encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function EUCJPEncoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. If code point is U+00A5, return byte 0x5C.
+      if (code_point === 0x00A5)
+        return 0x5C;
+
+      // 4. If code point is U+203E, return byte 0x7E.
+      if (code_point === 0x203E)
+        return 0x7E;
+
+      // 5. If code point is in the range U+FF61 to U+FF9F, inclusive,
+      // return two bytes whose values are 0x8E and code point −
+      // 0xFF61 + 0xA1.
+      if (inRange(code_point, 0xFF61, 0xFF9F))
+        return [0x8E, code_point - 0xFF61 + 0xA1];
+
+      // 6. If code point is U+2212, set it to U+FF0D.
+      if (code_point === 0x2212)
+        code_point = 0xFF0D;
+
+      // 7. Let pointer be the index pointer for code point in index
+      // jis0208.
+      var pointer = indexPointerFor(code_point, index('jis0208'));
+
+      // 8. If pointer is null, return error with code point.
+      if (pointer === null)
+        return encoderError(code_point);
+
+      // 9. Let lead be floor(pointer / 94) + 0xA1.
+      var lead = floor(pointer / 94) + 0xA1;
+
+      // 10. Let trail be pointer % 94 + 0xA1.
+      var trail = pointer % 94 + 0xA1;
+
+      // 11. Return two bytes whose values are lead and trail.
+      return [lead, trail];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['EUC-JP'] = function(options) {
+    return new EUCJPEncoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['EUC-JP'] = function(options) {
+    return new EUCJPDecoder(options);
+  };
+
+  // 13.2 iso-2022-jp
+
+  // 13.2.1 iso-2022-jp decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function ISO2022JPDecoder(options) {
+    var fatal = options.fatal;
+    /** @enum */
+    var states = {
+      ASCII: 0,
+      Roman: 1,
+      Katakana: 2,
+      LeadByte: 3,
+      TrailByte: 4,
+      EscapeStart: 5,
+      Escape: 6
+    };
+    // iso-2022-jp's decoder has an associated iso-2022-jp decoder
+    // state (initially ASCII), iso-2022-jp decoder output state
+    // (initially ASCII), iso-2022-jp lead (initially 0x00), and
+    // iso-2022-jp output flag (initially unset).
+    var /** @type {number} */ iso2022jp_decoder_state = states.ASCII,
+        /** @type {number} */ iso2022jp_decoder_output_state = states.ASCII,
+        /** @type {number} */ iso2022jp_lead = 0x00,
+        /** @type {boolean} */ iso2022jp_output_flag = false;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // switching on iso-2022-jp decoder state:
+      switch (iso2022jp_decoder_state) {
+      default:
+      case states.ASCII:
+        // ASCII
+        // Based on byte:
+
+        // 0x1B
+        if (bite === 0x1B) {
+          // Set iso-2022-jp decoder state to escape start and return
+          // continue.
+          iso2022jp_decoder_state = states.EscapeStart;
+          return null;
+        }
+
+        // 0x00 to 0x7F, excluding 0x0E, 0x0F, and 0x1B
+        if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E
+            && bite !== 0x0F && bite !== 0x1B) {
+          // Unset the iso-2022-jp output flag and return a code point
+          // whose value is byte.
+          iso2022jp_output_flag = false;
+          return bite;
+        }
+
+        // end-of-stream
+        if (bite === end_of_stream) {
+          // Return finished.
+          return finished;
+        }
+
+        // Otherwise
+        // Unset the iso-2022-jp output flag and return error.
+        iso2022jp_output_flag = false;
+        return decoderError(fatal);
+
+      case states.Roman:
+        // Roman
+        // Based on byte:
+
+        // 0x1B
+        if (bite === 0x1B) {
+          // Set iso-2022-jp decoder state to escape start and return
+          // continue.
+          iso2022jp_decoder_state = states.EscapeStart;
+          return null;
+        }
+
+        // 0x5C
+        if (bite === 0x5C) {
+          // Unset the iso-2022-jp output flag and return code point
+          // U+00A5.
+          iso2022jp_output_flag = false;
+          return 0x00A5;
+        }
+
+        // 0x7E
+        if (bite === 0x7E) {
+          // Unset the iso-2022-jp output flag and return code point
+          // U+203E.
+          iso2022jp_output_flag = false;
+          return 0x203E;
+        }
+
+        // 0x00 to 0x7F, excluding 0x0E, 0x0F, 0x1B, 0x5C, and 0x7E
+        if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E && bite !== 0x0F
+            && bite !== 0x1B && bite !== 0x5C && bite !== 0x7E) {
+          // Unset the iso-2022-jp output flag and return a code point
+          // whose value is byte.
+          iso2022jp_output_flag = false;
+          return bite;
+        }
+
+        // end-of-stream
+        if (bite === end_of_stream) {
+          // Return finished.
+          return finished;
+        }
+
+        // Otherwise
+        // Unset the iso-2022-jp output flag and return error.
+        iso2022jp_output_flag = false;
+        return decoderError(fatal);
+
+      case states.Katakana:
+        // Katakana
+        // Based on byte:
+
+        // 0x1B
+        if (bite === 0x1B) {
+          // Set iso-2022-jp decoder state to escape start and return
+          // continue.
+          iso2022jp_decoder_state = states.EscapeStart;
+          return null;
+        }
+
+        // 0x21 to 0x5F
+        if (inRange(bite, 0x21, 0x5F)) {
+          // Unset the iso-2022-jp output flag and return a code point
+          // whose value is 0xFF61 − 0x21 + byte.
+          iso2022jp_output_flag = false;
+          return 0xFF61 - 0x21 + bite;
+        }
+
+        // end-of-stream
+        if (bite === end_of_stream) {
+          // Return finished.
+          return finished;
+        }
+
+        // Otherwise
+        // Unset the iso-2022-jp output flag and return error.
+        iso2022jp_output_flag = false;
+        return decoderError(fatal);
+
+      case states.LeadByte:
+        // Lead byte
+        // Based on byte:
+
+        // 0x1B
+        if (bite === 0x1B) {
+          // Set iso-2022-jp decoder state to escape start and return
+          // continue.
+          iso2022jp_decoder_state = states.EscapeStart;
+          return null;
+        }
+
+        // 0x21 to 0x7E
+        if (inRange(bite, 0x21, 0x7E)) {
+          // Unset the iso-2022-jp output flag, set iso-2022-jp lead
+          // to byte, iso-2022-jp decoder state to trail byte, and
+          // return continue.
+          iso2022jp_output_flag = false;
+          iso2022jp_lead = bite;
+          iso2022jp_decoder_state = states.TrailByte;
+          return null;
+        }
+
+        // end-of-stream
+        if (bite === end_of_stream) {
+          // Return finished.
+          return finished;
+        }
+
+        // Otherwise
+        // Unset the iso-2022-jp output flag and return error.
+        iso2022jp_output_flag = false;
+        return decoderError(fatal);
+
+      case states.TrailByte:
+        // Trail byte
+        // Based on byte:
+
+        // 0x1B
+        if (bite === 0x1B) {
+          // Set iso-2022-jp decoder state to escape start and return
+          // continue.
+          iso2022jp_decoder_state = states.EscapeStart;
+          return decoderError(fatal);
+        }
+
+        // 0x21 to 0x7E
+        if (inRange(bite, 0x21, 0x7E)) {
+          // 1. Set the iso-2022-jp decoder state to lead byte.
+          iso2022jp_decoder_state = states.LeadByte;
+
+          // 2. Let pointer be (iso-2022-jp lead − 0x21) × 94 + byte − 0x21.
+          var pointer = (iso2022jp_lead - 0x21) * 94 + bite - 0x21;
+
+          // 3. Let code point be the index code point for pointer in
+          // index jis0208.
+          var code_point = indexCodePointFor(pointer, index('jis0208'));
+
+          // 4. If code point is null, return error.
+          if (code_point === null)
+            return decoderError(fatal);
+
+          // 5. Return a code point whose value is code point.
+          return code_point;
+        }
+
+        // end-of-stream
+        if (bite === end_of_stream) {
+          // Set the iso-2022-jp decoder state to lead byte, prepend
+          // byte to stream, and return error.
+          iso2022jp_decoder_state = states.LeadByte;
+          stream.prepend(bite);
+          return decoderError(fatal);
+        }
+
+        // Otherwise
+        // Set iso-2022-jp decoder state to lead byte and return
+        // error.
+        iso2022jp_decoder_state = states.LeadByte;
+        return decoderError(fatal);
+
+      case states.EscapeStart:
+        // Escape start
+
+        // 1. If byte is either 0x24 or 0x28, set iso-2022-jp lead to
+        // byte, iso-2022-jp decoder state to escape, and return
+        // continue.
+        if (bite === 0x24 || bite === 0x28) {
+          iso2022jp_lead = bite;
+          iso2022jp_decoder_state = states.Escape;
+          return null;
+        }
+
+        // 2. Prepend byte to stream.
+        stream.prepend(bite);
+
+        // 3. Unset the iso-2022-jp output flag, set iso-2022-jp
+        // decoder state to iso-2022-jp decoder output state, and
+        // return error.
+        iso2022jp_output_flag = false;
+        iso2022jp_decoder_state = iso2022jp_decoder_output_state;
+        return decoderError(fatal);
+
+      case states.Escape:
+        // Escape
+
+        // 1. Let lead be iso-2022-jp lead and set iso-2022-jp lead to
+        // 0x00.
+        var lead = iso2022jp_lead;
+        iso2022jp_lead = 0x00;
+
+        // 2. Let state be null.
+        var state = null;
+
+        // 3. If lead is 0x28 and byte is 0x42, set state to ASCII.
+        if (lead === 0x28 && bite === 0x42)
+          state = states.ASCII;
+
+        // 4. If lead is 0x28 and byte is 0x4A, set state to Roman.
+        if (lead === 0x28 && bite === 0x4A)
+          state = states.Roman;
+
+        // 5. If lead is 0x28 and byte is 0x49, set state to Katakana.
+        if (lead === 0x28 && bite === 0x49)
+          state = states.Katakana;
+
+        // 6. If lead is 0x24 and byte is either 0x40 or 0x42, set
+        // state to lead byte.
+        if (lead === 0x24 && (bite === 0x40 || bite === 0x42))
+          state = states.LeadByte;
+
+        // 7. If state is non-null, run these substeps:
+        if (state !== null) {
+          // 1. Set iso-2022-jp decoder state and iso-2022-jp decoder
+          // output state to states.
+          iso2022jp_decoder_state = iso2022jp_decoder_state = state;
+
+          // 2. Let output flag be the iso-2022-jp output flag.
+          var output_flag = iso2022jp_output_flag;
+
+          // 3. Set the iso-2022-jp output flag.
+          iso2022jp_output_flag = true;
+
+          // 4. Return continue, if output flag is unset, and error
+          // otherwise.
+          return !output_flag ? null : decoderError(fatal);
+        }
+
+        // 8. Prepend lead and byte to stream.
+        stream.prepend([lead, bite]);
+
+        // 9. Unset the iso-2022-jp output flag, set iso-2022-jp
+        // decoder state to iso-2022-jp decoder output state and
+        // return error.
+        iso2022jp_output_flag = false;
+        iso2022jp_decoder_state = iso2022jp_decoder_output_state;
+        return decoderError(fatal);
+      }
+    };
+  }
+
+  // 13.2.2 iso-2022-jp encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function ISO2022JPEncoder(options) {
+    var fatal = options.fatal;
+    // iso-2022-jp's encoder has an associated iso-2022-jp encoder
+    // state which is one of ASCII, Roman, and jis0208 (initially
+    // ASCII).
+    /** @enum */
+    var states = {
+      ASCII: 0,
+      Roman: 1,
+      jis0208: 2
+    };
+    var /** @type {number} */ iso2022jp_state = states.ASCII;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream and iso-2022-jp encoder
+      // state is not ASCII, prepend code point to stream, set
+      // iso-2022-jp encoder state to ASCII, and return three bytes
+      // 0x1B 0x28 0x42.
+      if (code_point === end_of_stream &&
+          iso2022jp_state !== states.ASCII) {
+        stream.prepend(code_point);
+        iso2022jp_state = states.ASCII;
+        return [0x1B, 0x28, 0x42];
+      }
+
+      // 2. If code point is end-of-stream and iso-2022-jp encoder
+      // state is ASCII, return finished.
+      if (code_point === end_of_stream && iso2022jp_state === states.ASCII)
+        return finished;
+
+      // 3. If ISO-2022-JP encoder state is ASCII or Roman, and code
+      // point is U+000E, U+000F, or U+001B, return error with U+FFFD.
+      if ((iso2022jp_state === states.ASCII ||
+           iso2022jp_state === states.Roman) &&
+          (code_point === 0x000E || code_point === 0x000F ||
+           code_point === 0x001B)) {
+        return encoderError(0xFFFD);
+      }
+
+      // 4. If iso-2022-jp encoder state is ASCII and code point is an
+      // ASCII code point, return a byte whose value is code point.
+      if (iso2022jp_state === states.ASCII &&
+          isASCIICodePoint(code_point))
+        return code_point;
+
+      // 5. If iso-2022-jp encoder state is Roman and code point is an
+      // ASCII code point, excluding U+005C and U+007E, or is U+00A5
+      // or U+203E, run these substeps:
+      if (iso2022jp_state === states.Roman &&
+          ((isASCIICodePoint(code_point) &&
+           code_point !== 0x005C && code_point !== 0x007E) ||
+          (code_point == 0x00A5 || code_point == 0x203E))) {
+
+        // 1. If code point is an ASCII code point, return a byte
+        // whose value is code point.
+        if (isASCIICodePoint(code_point))
+          return code_point;
+
+        // 2. If code point is U+00A5, return byte 0x5C.
+        if (code_point === 0x00A5)
+          return 0x5C;
+
+        // 3. If code point is U+203E, return byte 0x7E.
+        if (code_point === 0x203E)
+          return 0x7E;
+      }
+
+      // 6. If code point is an ASCII code point, and iso-2022-jp
+      // encoder state is not ASCII, prepend code point to stream, set
+      // iso-2022-jp encoder state to ASCII, and return three bytes
+      // 0x1B 0x28 0x42.
+      if (isASCIICodePoint(code_point) &&
+          iso2022jp_state !== states.ASCII) {
+        stream.prepend(code_point);
+        iso2022jp_state = states.ASCII;
+        return [0x1B, 0x28, 0x42];
+      }
+
+      // 7. If code point is either U+00A5 or U+203E, and iso-2022-jp
+      // encoder state is not Roman, prepend code point to stream, set
+      // iso-2022-jp encoder state to Roman, and return three bytes
+      // 0x1B 0x28 0x4A.
+      if ((code_point === 0x00A5 || code_point === 0x203E) &&
+          iso2022jp_state !== states.Roman) {
+        stream.prepend(code_point);
+        iso2022jp_state = states.Roman;
+        return [0x1B, 0x28, 0x4A];
+      }
+
+      // 8. If code point is U+2212, set it to U+FF0D.
+      if (code_point === 0x2212)
+        code_point = 0xFF0D;
+
+      // 9. Let pointer be the index pointer for code point in index
+      // jis0208.
+      var pointer = indexPointerFor(code_point, index('jis0208'));
+
+      // 10. If pointer is null, return error with code point.
+      if (pointer === null)
+        return encoderError(code_point);
+
+      // 11. If iso-2022-jp encoder state is not jis0208, prepend code
+      // point to stream, set iso-2022-jp encoder state to jis0208,
+      // and return three bytes 0x1B 0x24 0x42.
+      if (iso2022jp_state !== states.jis0208) {
+        stream.prepend(code_point);
+        iso2022jp_state = states.jis0208;
+        return [0x1B, 0x24, 0x42];
+      }
+
+      // 12. Let lead be floor(pointer / 94) + 0x21.
+      var lead = floor(pointer / 94) + 0x21;
+
+      // 13. Let trail be pointer % 94 + 0x21.
+      var trail = pointer % 94 + 0x21;
+
+      // 14. Return two bytes whose values are lead and trail.
+      return [lead, trail];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['ISO-2022-JP'] = function(options) {
+    return new ISO2022JPEncoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['ISO-2022-JP'] = function(options) {
+    return new ISO2022JPDecoder(options);
+  };
+
+  // 13.3 Shift_JIS
+
+  // 13.3.1 Shift_JIS decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function ShiftJISDecoder(options) {
+    var fatal = options.fatal;
+    // Shift_JIS's decoder has an associated Shift_JIS lead (initially
+    // 0x00).
+    var /** @type {number} */ Shift_JIS_lead = 0x00;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and Shift_JIS lead is not 0x00,
+      // set Shift_JIS lead to 0x00 and return error.
+      if (bite === end_of_stream && Shift_JIS_lead !== 0x00) {
+        Shift_JIS_lead = 0x00;
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream and Shift_JIS lead is 0x00,
+      // return finished.
+      if (bite === end_of_stream && Shift_JIS_lead === 0x00)
+        return finished;
+
+      // 3. If Shift_JIS lead is not 0x00, let lead be Shift_JIS lead,
+      // let pointer be null, set Shift_JIS lead to 0x00, and then run
+      // these substeps:
+      if (Shift_JIS_lead !== 0x00) {
+        var lead = Shift_JIS_lead;
+        var pointer = null;
+        Shift_JIS_lead = 0x00;
+
+        // 1. Let offset be 0x40, if byte is less than 0x7F, and 0x41
+        // otherwise.
+        var offset = (bite < 0x7F) ? 0x40 : 0x41;
+
+        // 2. Let lead offset be 0x81, if lead is less than 0xA0, and
+        // 0xC1 otherwise.
+        var lead_offset = (lead < 0xA0) ? 0x81 : 0xC1;
+
+        // 3. If byte is in the range 0x40 to 0x7E, inclusive, or 0x80
+        // to 0xFC, inclusive, set pointer to (lead − lead offset) ×
+        // 188 + byte − offset.
+        if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFC))
+          pointer = (lead - lead_offset) * 188 + bite - offset;
+
+        // 4. If pointer is in the range 8836 to 10715, inclusive,
+        // return a code point whose value is 0xE000 − 8836 + pointer.
+        if (inRange(pointer, 8836, 10715))
+          return 0xE000 - 8836 + pointer;
+
+        // 5. Let code point be null, if pointer is null, and the
+        // index code point for pointer in index jis0208 otherwise.
+        var code_point = (pointer === null) ? null :
+              indexCodePointFor(pointer, index('jis0208'));
+
+        // 6. If code point is null and byte is an ASCII byte, prepend
+        // byte to stream.
+        if (code_point === null && isASCIIByte(bite))
+          stream.prepend(bite);
+
+        // 7. If code point is null, return error.
+        if (code_point === null)
+          return decoderError(fatal);
+
+        // 8. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 4. If byte is an ASCII byte or 0x80, return a code point
+      // whose value is byte.
+      if (isASCIIByte(bite) || bite === 0x80)
+        return bite;
+
+      // 5. If byte is in the range 0xA1 to 0xDF, inclusive, return a
+      // code point whose value is 0xFF61 − 0xA1 + byte.
+      if (inRange(bite, 0xA1, 0xDF))
+        return 0xFF61 - 0xA1 + bite;
+
+      // 6. If byte is in the range 0x81 to 0x9F, inclusive, or 0xE0
+      // to 0xFC, inclusive, set Shift_JIS lead to byte and return
+      // continue.
+      if (inRange(bite, 0x81, 0x9F) || inRange(bite, 0xE0, 0xFC)) {
+        Shift_JIS_lead = bite;
+        return null;
+      }
+
+      // 7. Return error.
+      return decoderError(fatal);
+    };
+  }
+
+  // 13.3.2 Shift_JIS encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function ShiftJISEncoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point or U+0080, return a
+      // byte whose value is code point.
+      if (isASCIICodePoint(code_point) || code_point === 0x0080)
+        return code_point;
+
+      // 3. If code point is U+00A5, return byte 0x5C.
+      if (code_point === 0x00A5)
+        return 0x5C;
+
+      // 4. If code point is U+203E, return byte 0x7E.
+      if (code_point === 0x203E)
+        return 0x7E;
+
+      // 5. If code point is in the range U+FF61 to U+FF9F, inclusive,
+      // return a byte whose value is code point − 0xFF61 + 0xA1.
+      if (inRange(code_point, 0xFF61, 0xFF9F))
+        return code_point - 0xFF61 + 0xA1;
+
+      // 6. If code point is U+2212, set it to U+FF0D.
+      if (code_point === 0x2212)
+        code_point = 0xFF0D;
+
+      // 7. Let pointer be the index Shift_JIS pointer for code point.
+      var pointer = indexShiftJISPointerFor(code_point);
+
+      // 8. If pointer is null, return error with code point.
+      if (pointer === null)
+        return encoderError(code_point);
+
+      // 9. Let lead be floor(pointer / 188).
+      var lead = floor(pointer / 188);
+
+      // 10. Let lead offset be 0x81, if lead is less than 0x1F, and
+      // 0xC1 otherwise.
+      var lead_offset = (lead < 0x1F) ? 0x81 : 0xC1;
+
+      // 11. Let trail be pointer % 188.
+      var trail = pointer % 188;
+
+      // 12. Let offset be 0x40, if trail is less than 0x3F, and 0x41
+      // otherwise.
+      var offset = (trail < 0x3F) ? 0x40 : 0x41;
+
+      // 13. Return two bytes whose values are lead + lead offset and
+      // trail + offset.
+      return [lead + lead_offset, trail + offset];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['Shift_JIS'] = function(options) {
+    return new ShiftJISEncoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['Shift_JIS'] = function(options) {
+    return new ShiftJISDecoder(options);
+  };
+
+  //
+  // 14. Legacy multi-byte Korean encodings
+  //
+
+  // 14.1 euc-kr
+
+  // 14.1.1 euc-kr decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function EUCKRDecoder(options) {
+    var fatal = options.fatal;
+
+    // euc-kr's decoder has an associated euc-kr lead (initially 0x00).
+    var /** @type {number} */ euckr_lead = 0x00;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and euc-kr lead is not 0x00, set
+      // euc-kr lead to 0x00 and return error.
+      if (bite === end_of_stream && euckr_lead !== 0) {
+        euckr_lead = 0x00;
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream and euc-kr lead is 0x00, return
+      // finished.
+      if (bite === end_of_stream && euckr_lead === 0)
+        return finished;
+
+      // 3. If euc-kr lead is not 0x00, let lead be euc-kr lead, let
+      // pointer be null, set euc-kr lead to 0x00, and then run these
+      // substeps:
+      if (euckr_lead !== 0x00) {
+        var lead = euckr_lead;
+        var pointer = null;
+        euckr_lead = 0x00;
+
+        // 1. If byte is in the range 0x41 to 0xFE, inclusive, set
+        // pointer to (lead − 0x81) × 190 + (byte − 0x41).
+        if (inRange(bite, 0x41, 0xFE))
+          pointer = (lead - 0x81) * 190 + (bite - 0x41);
+
+        // 2. Let code point be null, if pointer is null, and the
+        // index code point for pointer in index euc-kr otherwise.
+        var code_point = (pointer === null)
+              ? null : indexCodePointFor(pointer, index('euc-kr'));
+
+        // 3. If code point is null and byte is an ASCII byte, prepend
+        // byte to stream.
+        if (pointer === null && isASCIIByte(bite))
+          stream.prepend(bite);
+
+        // 4. If code point is null, return error.
+        if (code_point === null)
+          return decoderError(fatal);
+
+        // 5. Return a code point whose value is code point.
+        return code_point;
+      }
+
+      // 4. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 5. If byte is in the range 0x81 to 0xFE, inclusive, set
+      // euc-kr lead to byte and return continue.
+      if (inRange(bite, 0x81, 0xFE)) {
+        euckr_lead = bite;
+        return null;
+      }
+
+      // 6. Return error.
+      return decoderError(fatal);
+    };
+  }
+
+  // 14.1.2 euc-kr encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function EUCKREncoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. Let pointer be the index pointer for code point in index
+      // euc-kr.
+      var pointer = indexPointerFor(code_point, index('euc-kr'));
+
+      // 4. If pointer is null, return error with code point.
+      if (pointer === null)
+        return encoderError(code_point);
+
+      // 5. Let lead be floor(pointer / 190) + 0x81.
+      var lead = floor(pointer / 190) + 0x81;
+
+      // 6. Let trail be pointer % 190 + 0x41.
+      var trail = (pointer % 190) + 0x41;
+
+      // 7. Return two bytes whose values are lead and trail.
+      return [lead, trail];
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['EUC-KR'] = function(options) {
+    return new EUCKREncoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['EUC-KR'] = function(options) {
+    return new EUCKRDecoder(options);
+  };
+
+
+  //
+  // 15. Legacy miscellaneous encodings
+  //
+
+  // 15.1 replacement
+
+  // Not needed - API throws RangeError
+
+  // 15.2 Common infrastructure for utf-16be and utf-16le
+
+  /**
+   * @param {number} code_unit
+   * @param {boolean} utf16be
+   * @return {!Array.<number>} bytes
+   */
+  function convertCodeUnitToBytes(code_unit, utf16be) {
+    // 1. Let byte1 be code unit >> 8.
+    var byte1 = code_unit >> 8;
+
+    // 2. Let byte2 be code unit & 0x00FF.
+    var byte2 = code_unit & 0x00FF;
+
+    // 3. Then return the bytes in order:
+        // utf-16be flag is set: byte1, then byte2.
+    if (utf16be)
+      return [byte1, byte2];
+    // utf-16be flag is unset: byte2, then byte1.
+    return [byte2, byte1];
+  }
+
+  // 15.2.1 shared utf-16 decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {boolean} utf16_be True if big-endian, false if little-endian.
+   * @param {{fatal: boolean}} options
+   */
+  function UTF16Decoder(utf16_be, options) {
+    var fatal = options.fatal;
+    var /** @type {?number} */ utf16_lead_byte = null,
+        /** @type {?number} */ utf16_lead_surrogate = null;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream and either utf-16 lead byte or
+      // utf-16 lead surrogate is not null, set utf-16 lead byte and
+      // utf-16 lead surrogate to null, and return error.
+      if (bite === end_of_stream && (utf16_lead_byte !== null ||
+                                utf16_lead_surrogate !== null)) {
+        return decoderError(fatal);
+      }
+
+      // 2. If byte is end-of-stream and utf-16 lead byte and utf-16
+      // lead surrogate are null, return finished.
+      if (bite === end_of_stream && utf16_lead_byte === null &&
+          utf16_lead_surrogate === null) {
+        return finished;
+      }
+
+      // 3. If utf-16 lead byte is null, set utf-16 lead byte to byte
+      // and return continue.
+      if (utf16_lead_byte === null) {
+        utf16_lead_byte = bite;
+        return null;
+      }
+
+      // 4. Let code unit be the result of:
+      var code_unit;
+      if (utf16_be) {
+        // utf-16be decoder flag is set
+        //   (utf-16 lead byte << 8) + byte.
+        code_unit = (utf16_lead_byte << 8) + bite;
+      } else {
+        // utf-16be decoder flag is unset
+        //   (byte << 8) + utf-16 lead byte.
+        code_unit = (bite << 8) + utf16_lead_byte;
+      }
+      // Then set utf-16 lead byte to null.
+      utf16_lead_byte = null;
+
+      // 5. If utf-16 lead surrogate is not null, let lead surrogate
+      // be utf-16 lead surrogate, set utf-16 lead surrogate to null,
+      // and then run these substeps:
+      if (utf16_lead_surrogate !== null) {
+        var lead_surrogate = utf16_lead_surrogate;
+        utf16_lead_surrogate = null;
+
+        // 1. If code unit is in the range U+DC00 to U+DFFF,
+        // inclusive, return a code point whose value is 0x10000 +
+        // ((lead surrogate − 0xD800) << 10) + (code unit − 0xDC00).
+        if (inRange(code_unit, 0xDC00, 0xDFFF)) {
+          return 0x10000 + (lead_surrogate - 0xD800) * 0x400 +
+              (code_unit - 0xDC00);
+        }
+
+        // 2. Prepend the sequence resulting of converting code unit
+        // to bytes using utf-16be decoder flag to stream and return
+        // error.
+        stream.prepend(convertCodeUnitToBytes(code_unit, utf16_be));
+        return decoderError(fatal);
+      }
+
+      // 6. If code unit is in the range U+D800 to U+DBFF, inclusive,
+      // set utf-16 lead surrogate to code unit and return continue.
+      if (inRange(code_unit, 0xD800, 0xDBFF)) {
+        utf16_lead_surrogate = code_unit;
+        return null;
+      }
+
+      // 7. If code unit is in the range U+DC00 to U+DFFF, inclusive,
+      // return error.
+      if (inRange(code_unit, 0xDC00, 0xDFFF))
+        return decoderError(fatal);
+
+      // 8. Return code point code unit.
+      return code_unit;
+    };
+  }
+
+  // 15.2.2 shared utf-16 encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {boolean} utf16_be True if big-endian, false if little-endian.
+   * @param {{fatal: boolean}} options
+   */
+  function UTF16Encoder(utf16_be, options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1. If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is in the range U+0000 to U+FFFF, inclusive,
+      // return the sequence resulting of converting code point to
+      // bytes using utf-16be encoder flag.
+      if (inRange(code_point, 0x0000, 0xFFFF))
+        return convertCodeUnitToBytes(code_point, utf16_be);
+
+      // 3. Let lead be ((code point − 0x10000) >> 10) + 0xD800,
+      // converted to bytes using utf-16be encoder flag.
+      var lead = convertCodeUnitToBytes(
+        ((code_point - 0x10000) >> 10) + 0xD800, utf16_be);
+
+      // 4. Let trail be ((code point − 0x10000) & 0x3FF) + 0xDC00,
+      // converted to bytes using utf-16be encoder flag.
+      var trail = convertCodeUnitToBytes(
+        ((code_point - 0x10000) & 0x3FF) + 0xDC00, utf16_be);
+
+      // 5. Return a byte sequence of lead followed by trail.
+      return lead.concat(trail);
+    };
+  }
+
+  // 15.3 utf-16be
+  // 15.3.1 utf-16be decoder
+  /** @param {{fatal: boolean}} options */
+  encoders['UTF-16BE'] = function(options) {
+    return new UTF16Encoder(true, options);
+  };
+  // 15.3.2 utf-16be encoder
+  /** @param {{fatal: boolean}} options */
+  decoders['UTF-16BE'] = function(options) {
+    return new UTF16Decoder(true, options);
+  };
+
+  // 15.4 utf-16le
+  // 15.4.1 utf-16le decoder
+  /** @param {{fatal: boolean}} options */
+  encoders['UTF-16LE'] = function(options) {
+    return new UTF16Encoder(false, options);
+  };
+  // 15.4.2 utf-16le encoder
+  /** @param {{fatal: boolean}} options */
+  decoders['UTF-16LE'] = function(options) {
+    return new UTF16Decoder(false, options);
+  };
+
+  // 15.5 x-user-defined
+
+  // 15.5.1 x-user-defined decoder
+  /**
+   * @constructor
+   * @implements {Decoder}
+   * @param {{fatal: boolean}} options
+   */
+  function XUserDefinedDecoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream The stream of bytes being decoded.
+     * @param {number} bite The next byte read from the stream.
+     * @return {?(number|!Array.<number>)} The next code point(s)
+     *     decoded, or null if not enough data exists in the input
+     *     stream to decode a complete code point.
+     */
+    this.handler = function(stream, bite) {
+      // 1. If byte is end-of-stream, return finished.
+      if (bite === end_of_stream)
+        return finished;
+
+      // 2. If byte is an ASCII byte, return a code point whose value
+      // is byte.
+      if (isASCIIByte(bite))
+        return bite;
+
+      // 3. Return a code point whose value is 0xF780 + byte − 0x80.
+      return 0xF780 + bite - 0x80;
+    };
+  }
+
+  // 15.5.2 x-user-defined encoder
+  /**
+   * @constructor
+   * @implements {Encoder}
+   * @param {{fatal: boolean}} options
+   */
+  function XUserDefinedEncoder(options) {
+    var fatal = options.fatal;
+    /**
+     * @param {Stream} stream Input stream.
+     * @param {number} code_point Next code point read from the stream.
+     * @return {(number|!Array.<number>)} Byte(s) to emit.
+     */
+    this.handler = function(stream, code_point) {
+      // 1.If code point is end-of-stream, return finished.
+      if (code_point === end_of_stream)
+        return finished;
+
+      // 2. If code point is an ASCII code point, return a byte whose
+      // value is code point.
+      if (isASCIICodePoint(code_point))
+        return code_point;
+
+      // 3. If code point is in the range U+F780 to U+F7FF, inclusive,
+      // return a byte whose value is code point − 0xF780 + 0x80.
+      if (inRange(code_point, 0xF780, 0xF7FF))
+        return code_point - 0xF780 + 0x80;
+
+      // 4. Return error with code point.
+      return encoderError(code_point);
+    };
+  }
+
+  /** @param {{fatal: boolean}} options */
+  encoders['x-user-defined'] = function(options) {
+    return new XUserDefinedEncoder(options);
+  };
+  /** @param {{fatal: boolean}} options */
+  decoders['x-user-defined'] = function(options) {
+    return new XUserDefinedDecoder(options);
+  };
+
+  if (!global['TextEncoder'])
+    global['TextEncoder'] = TextEncoder;
+  if (!global['TextDecoder'])
+    global['TextDecoder'] = TextDecoder;
+
+  if (typeof module !== "undefined" && module.exports) {
+    module.exports = {
+      TextEncoder: global['TextEncoder'],
+      TextDecoder: global['TextDecoder'],
+      EncodingIndexes: global["encoding-indexes"]
+    };
+  }
+
+// For strict environments where `this` inside the global scope
+// is `undefined`, take a pure object instead
+}(this || {}));

+ 312 - 0
static/js/esc.js

@@ -0,0 +1,312 @@
+var encode = require("./encoding.js")
+// var app = getApp();
+var jpPrinter = {
+  createNew: function () {
+    var jpPrinter = {};
+    var data = [];
+
+    var bar = ["UPC-A", "UPC-E", "EAN13", "EAN8", "CODE39", "ITF", "CODABAR", "CODE93", "CODE128"];
+
+    jpPrinter.name = "账单模式";
+
+    jpPrinter.init = function () { //初始化打印机
+      data.push(27)
+      data.push(64)
+    };
+
+    jpPrinter.setText = function (content) { //设置文本内容
+      var code = new encode.TextEncoder(
+        'gb18030', {
+          NONSTANDARD_allowLegacyEncoding: true
+        }).encode(content)
+      for (var i = 0; i < code.length; ++i) {
+        data.push(code[i])
+      }
+    }
+
+    jpPrinter.setBarcodeWidth = function (width) { //设置条码宽度
+      data.push(29)
+      data.push(119)
+      if (width > 6) {
+        width = 6;
+      }
+      if (width < 2) {
+        width = 1;
+      }
+      data.push(width)
+    }
+
+    jpPrinter.setBarcodeHeight = function (height) { //设置条码高度
+      data.push(29)
+      data.push(104)
+      data.push(height)
+    }
+
+    jpPrinter.setBarcodeContent = function (t, content) {
+		console.log('设置',t, content)
+      var ty = 73;
+      data.push(29)
+      data.push(107)
+      switch (t) {
+        case bar[0]:
+          ty = 65;
+          break;
+        case bar[1]:
+          ty = 66;
+          break;
+        case bar[2]:
+          ty = 67;
+          break;
+        case bar[3]:
+          ty = 68;
+          break;
+        case bar[4]:
+          ty = 69;
+          break;
+        case bar[5]:
+          ty = 70;
+          break;
+        case bar[6]:
+          ty = 71;
+          break;
+        case bar[7]:
+          ty = 72;
+          break;
+        case bar[8]:
+          ty = 73;
+          break;
+      }
+      data.push(ty)
+    }
+
+    jpPrinter.setSelectSizeOfModuleForQRCode = function (n) { //设置二维码大小
+      data.push(29)
+      data.push(40)
+      data.push(107)
+      data.push(3)
+      data.push(0)
+      data.push(49)
+      data.push(67)
+      if (n > 15) {
+        n = 15
+      }
+      if (n < 1) {
+        n = 1
+      }
+      data.push(n)
+    }
+
+    jpPrinter.setSelectErrorCorrectionLevelForQRCode = function (n) { //设置纠错等级
+      /*
+      n      功能      纠错能力
+      48 选择纠错等级 L 7
+      49 选择纠错等级 M 15
+      50 选择纠错等级 Q 25
+      51 选择纠错等级 H 30
+      */
+      data.push(29)
+      data.push(40)
+      data.push(107)
+      data.push(3)
+      data.push(0)
+      data.push(49)
+      data.push(69)
+      data.push(n)
+    }
+
+    jpPrinter.setStoreQRCodeData = function (content) { //设置二维码内容
+      var code = new encode.TextEncoder(
+        'gb18030', {
+          NONSTANDARD_allowLegacyEncoding: true
+        }).encode(content)
+      data.push(29)
+      data.push(40)
+      data.push(107)
+      data.push(parseInt((code.length + 3) % 256))
+      data.push(parseInt((code.length + 3) / 256))
+      data.push(49)
+      data.push(80)
+      data.push(48)
+
+      for (var i = 0; i < code.length; ++i) {
+        data.push(code[i])
+      }
+    }
+
+    jpPrinter.setPrintQRCode = function () { //打印二维码
+      data.push(29)
+      data.push(40)
+      data.push(107)
+      data.push(3)
+      data.push(0)
+      data.push(49)
+      data.push(81)
+      data.push(48)
+    }
+
+    jpPrinter.setHorTab = function () { //移动打印位置到下一个水平定位点的位置
+      data.push(9)
+    }
+
+    jpPrinter.setAbsolutePrintPosition = function (where) { //设置绝对打印位置
+      data.push(27)
+      data.push(36)
+      data.push(parseInt(where % 256))
+      data.push(parseInt(where / 256))
+    }
+
+    jpPrinter.setRelativePrintPositon = function (where) { //设置相对横向打印位置
+      data.push(27)
+      data.push(92)
+      data.push(parseInt(where % 256))
+      data.push(parseInt(where / 256))
+    }
+
+    jpPrinter.setSelectJustification = function (which) { //对齐方式
+      /*
+      0, 48 左对齐
+      1, 49 中间对齐
+      2, 50 右对齐
+      */
+      data.push(27)
+      data.push(97)
+      data.push(which)
+    }
+
+    jpPrinter.setLeftMargin = function (n) { //设置左边距
+      data.push(29)
+      data.push(76)
+      data.push(parseInt(n % 256))
+      data.push(parseInt(n / 256))
+    }
+
+    jpPrinter.setPrintingAreaWidth = function (width) { //设置打印区域宽度
+      data.push(29)
+      data.push(87)
+      data.push(parseInt(width % 256))
+      data.push(parseInt(width / 256))
+    }
+
+    jpPrinter.setSound = function (n, t) { //设置蜂鸣器
+      data.push(27)
+      data.push(66)
+      if (n < 0) {
+        n = 1;
+      } else if (n > 9) {
+        n = 9;
+      }
+
+      if (t < 0) {
+        t = 1;
+      } else if (t > 9) {
+        t = 9;
+      }
+      data.push(n)
+      data.push(t)
+    }
+
+    jpPrinter.setBitmap = function (res) { //参数,画布的参数
+      console.log('画布的参数',res)
+      var width = parseInt((res.width + 7) / 8 * 8 / 8)
+      var height = res.height;
+      var time = 1;
+      var temp = res.data.length - width * 32;
+      var point_list = []
+      console.log(width + "--" + height)
+      data.push(29)
+      data.push(118)
+      data.push(48)
+      data.push(0)
+      data.push((parseInt((res.width + 7) / 8) * 8) / 8)
+      data.push(0)
+      data.push(parseInt(res.height % 256))
+      data.push(parseInt(res.height / 256))
+      console.log(res.data.length)
+      console.log("temp=" + temp)
+      for (var i = 0; i < height; ++i) {
+        for (var j = 0; j < width; ++j) {
+          for (var k = 0; k < 32; k += 4) {
+            var po = {}
+            if (res.data[temp] == 0 && res.data[temp + 1] == 0 && res.data[temp + 2] == 0 && res.data[temp + 3] == 0) {
+              po.point = 0;
+            } else {
+              po.point = 1;
+            }
+            point_list.push(po)
+            temp += 4
+          }
+        }
+        time++
+        temp = res.data.length - width * 32 * time
+      }
+      for (var i = 0; i < point_list.length; i += 8) {
+        var p = point_list[i].point * 128 + point_list[i + 1].point * 64 + point_list[i + 2].point * 32 + point_list[i + 3].point * 16 + point_list[i + 4].point * 8 + point_list[i + 5].point * 4 + point_list[i + 6].point * 2 + point_list[i + 7].point
+        data.push(p)
+      }
+    }
+
+    jpPrinter.setPrint = function () { //打印并换行
+      data.push(10)
+    }
+
+    jpPrinter.setPrintAndFeed = function (feed) { //打印并走纸feed个单位
+      data.push(27)
+      data.push(74)
+      data.push(feed)
+    }
+
+    jpPrinter.setPrintAndFeedRow = function (row) { //打印并走纸row行
+      data.push(27)
+      data.push(100)
+      data.push(row)
+    }
+
+    jpPrinter.getData = function () { //获取打印数据
+      return data;
+    };
+
+
+    return jpPrinter;
+  },
+
+  Query: function () {
+    var queryStatus = {};
+    var buf;
+    var dateView;
+    queryStatus.getRealtimeStatusTransmission = function (n) { //查询打印机实时状态
+      /*
+      n = 1:传送打印机状态
+      n = 2:传送脱机状态
+      n = 3:传送错误状态
+      n = 4:传送纸传感器状态
+      */
+      buf = new ArrayBuffer(3)
+      dateView = new DataView(buf)
+      dateView.setUint8(0, 16)
+      dateView.setUint8(1, 4)
+      dateView.setUint8(2, n)
+      queryStatus.query(buf)
+    }
+
+    queryStatus.query = function (buf) {
+      wx.writeBLECharacteristicValue({
+        deviceId: app.BLEInformation.deviceId,
+        serviceId: app.BLEInformation.writeServiceId,
+        characteristicId: app.BLEInformation.writeCharaterId,
+        value: buf,
+        success: function (res) {
+
+        },
+        complete: function (res) {
+          console.log(res)
+          buf = null
+          dateView = null;
+        }
+      })
+    }
+    return queryStatus;
+  }
+
+};
+
+module.exports.jpPrinter = jpPrinter;

+ 222 - 0
static/js/tsc.js

@@ -0,0 +1,222 @@
+// var app = getApp();
+var encode = require("./encoding.js");
+var jpPrinter = {
+  createNew: function () {
+    var jpPrinter = {};
+    var data = "";
+    var command = []
+
+    jpPrinter.name = "标签模式";
+
+    jpPrinter.init = function () { };
+
+    jpPrinter.addCommand = function (content) {  //将指令转成数组装起
+      var code = new encode.TextEncoder(
+        'gb18030', {
+          NONSTANDARD_allowLegacyEncoding: true
+        }).encode(content)
+      for (var i = 0; i < code.length; ++i) {
+        command.push(code[i])
+      }
+    }
+
+    jpPrinter.setSize = function (pageWidght, pageHeight) { //设置页面大小
+      data = "SIZE " + pageWidght.toString() + " mm" + "," + pageHeight.toString() + " mm" + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setSpeed = function (printSpeed) { //设置打印机速度
+      data = "SPEED " + printSpeed.toString() + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setDensity = function (printDensity) { //设置打印机浓度
+      data = "DENSITY " + printDensity.toString() + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setGap = function (printGap) { //传感器
+      data = "GAP " + printGap.toString() + " mm\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setCountry = function (country) { //选择国际字符集
+      /*
+      001:USA
+      002:French
+      003:Latin America
+      034:Spanish
+      039:Italian
+      044:United Kingdom
+      046:Swedish
+      047:Norwegian
+      049:German
+       */
+      data = "COUNTRY " + country + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setCodepage = function (codepage) { //选择国际代码页
+      /*
+      8-bit codepage 字符集代表
+      437:United States
+      850:Multilingual
+      852:Slavic
+      860:Portuguese
+      863:Canadian/French
+      865:Nordic
+      Windows code page
+      1250:Central Europe
+      1252:Latin I
+      1253:Greek
+      1254:Turkish
+      以下代码页仅限于 12×24 dot 英数字体
+      WestEurope:WestEurope
+      Greek:Greek
+      Hebrew:Hebrew
+      EastEurope:EastEurope
+      Iran:Iran
+      IranII:IranII
+      Latvian:Latvian
+      Arabic:Arabic
+      Vietnam:Vietnam
+      Uygur:Uygur
+      Thai:Thai
+      1252:Latin I
+      1257:WPC1257
+      1251:WPC1251
+      866:Cyrillic
+      858:PC858
+      747:PC747
+      864:PC864
+      1001:PC100
+      */
+      data = "CODEPAGE " + codepage + "\r\n";
+      jpPrinter.addCommand(data)
+    }
+
+    jpPrinter.setCls = function () { //清除打印机缓存
+      data = "CLS" + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setFeed = function (feed) { //将纸向前推出n
+      data = "FEED " + feed + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setBackFeed = function (backup) { //将纸向后回拉n
+      data = "BACKFEED " + backup + "\r\n";
+      jpPrinter.addCommand(data)
+    }
+
+    jpPrinter.setDirection = function (direction) { //设置打印方向,参考编程手册  
+      data = "DIRECTION " + direction + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setReference = function (x, y) { //设置坐标原点,与打印方向有关
+      data = "REFERENCE " + x + "," + y + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setFromfeed = function () { //根据Size进一张标签纸
+      data = "FORMFEED \r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setHome = function () { //根据Size找到下一张标签纸的位置
+      data = "HOME \r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setSound = function (level, interval) { //控制蜂鸣器
+      data = "SOUND " + level + "," + interval + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setLimitfeed = function (limit) { // 检测垂直间距
+      data = "LIMITFEED " + limit + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setBar = function (x, y, width, height) { //绘制线条
+      data = "BAR " + x + "," + y + "," + width + "," + height + "\r\n"
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setBox = function (x_start, y_start, x_end, y_end, thickness) { //绘制方框
+      data = "BOX " + x_start + "," + y_start + "," + x_end + "," + y_end + "," + thickness + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setErase = function (x_start, y_start, x_width, y_height) { //清除指定区域的数据
+      data = "ERASE " + x_start + "," + y_start + "," + x_width + "," + y_height + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setReverse = function (x_start, y_start, x_width, y_height) { //将指定的区域反相打印
+      data = "REVERSE " + x_start + "," + y_start + "," + x_width + "," + y_height + "\r\n";
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setText = function (x, y, font, x_, y_, str) { //打印文字
+      data = "TEXT " + x + "," + y + ",\"" + font + "\"," + 0 + "," + x_ + "," + y_ + "," + "\"" + str + "\"\r\n"
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setQR = function (x, y, level, width, mode, content) { //打印二维码
+      data = "QRCODE " + x + "," + y + "," + level + "," + width + "," + mode + "," + 0 + ",\"" + content + "\"\r\n"
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setBar = function (x, y, codetype, height, readable, narrow, wide, content) { //打印条形码
+      data = "BARCODE " + x + "," + y + ",\"" + codetype + "\"," + height + "," + readable + "," + 0 + "," + narrow + "," + wide + ",\"" + content + "\"\r\n"
+      jpPrinter.addCommand(data)
+    };
+
+    jpPrinter.setBitmap = function (x, y, mode, res) {  //添加图片,res为画布参数
+      console.log(res)
+      var width = parseInt((res.width + 7) / 8 * 8 / 8)
+      var height = res.height;
+      var time = 1;
+      var temp = res.data.length - width * 32;
+      var pointList = []
+      console.log(width + "--" + height)
+      data = "BITMAP " + x + "," + y + "," + width + "," + height + "," + mode + ","
+      jpPrinter.addCommand(data)
+      for (var i = 0; i < height; ++i) {
+        console.log(temp)
+        for (var j = 0; j < width; ++j) {
+          for (var k = 0; k < 32; k += 4) {
+            if (res.data[temp] == 0 && res.data[temp + 1] == 0 && res.data[temp + 2] == 0 && res.data[temp + 3] == 0) {
+              pointList.push(1)
+            } else {
+              pointList.push(0)
+            }
+            temp += 4
+          }
+        }
+        time++
+        temp = res.data.length - width * 32 * time
+      }
+      for (var i = 0; i < pointList.length; i += 8) {
+        var p = pointList[i] * 128 + pointList[i + 1] * 64 + pointList[i + 2] * 32 + pointList[i + 3] * 16 + pointList[i + 4] * 8 + pointList[i + 5] * 4 + pointList[i + 6] * 2 + pointList[i + 7]
+        command.push(p)
+      }
+    }
+
+    jpPrinter.setPagePrint = function () { //打印页面
+      data = "PRINT 1,1\r\n"
+      jpPrinter.addCommand(data)
+    };
+    //获取打印数据
+    jpPrinter.getData = function () {
+      return command;
+    };
+
+    return jpPrinter;
+  }
+};
+
+module.exports.jpPrinter = jpPrinter;

BIN
static/logo.png


BIN
static/portrait.png


BIN
static/task/arrows.png


BIN
static/task/endpoint.png


BIN
static/task/startpoint.png


BIN
static/unfold.png


+ 47 - 0
store/storage.js

@@ -0,0 +1,47 @@
+class Storage {
+	// token
+	static getToken() {
+		let token = uni.getStorageSync('access_token')
+		return token || '';
+	}
+	static setCache(key, value) {
+		return uni.setStorageSync(key, value)
+	}
+	static getCache(key) {
+		return uni.getStorageSync(key)
+	}
+
+	static setToken(token = null) {
+		uni.setStorageSync('access_token', token)
+	}
+	static removeCache(key) {
+		uni.removeStorageSync(key)
+	}
+	static removeToken() {
+		uni.removeStorageSync('access_token')
+	}
+	// 清除
+	static clear() {
+		uni.clearStorageSync()
+		uni.clearStorage()
+	}
+	static isLogin() {
+		const token = uni.getStorageSync('access_token')
+		const user = uni.getStorageSync('userInfo')
+		if (token && user) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+	static resetToken() {
+		// const token = this.getToken()
+		// const str = cryptojs.Decrypt(token);
+		// const tokens = str.split(',');
+		// const tokenStr = tokens[0] + ',' + ((new Date()).valueOf());
+		// // const res = cryptojs.Encrypt(tokenStr)
+		return this.getToken();
+	}
+}
+
+export default Storage

+ 10 - 0
uni.promisify.adaptor.js

@@ -0,0 +1,10 @@
+uni.addInterceptor({
+  returnValue (res) {
+    if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
+      return res;
+    }
+    return new Promise((resolve, reject) => {
+      res.then((res) => res[0] ? reject(res[0]) : resolve(res[1]));
+    });
+  },
+});

+ 76 - 0
uni.scss

@@ -0,0 +1,76 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+@import 'uview-ui/theme.scss';
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:12px;
+$uni-font-size-base:14px;
+$uni-font-size-lg:16px;
+
+/* 图片尺寸 */
+$uni-img-size-sm:20px;
+$uni-img-size-base:26px;
+$uni-img-size-lg:40px;
+
+/* Border Radius */
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:20px;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:26px;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:15px;

+ 97 - 0
utils/request.js

@@ -0,0 +1,97 @@
+import Storage from './../store/storage.js';
+
+const ENV = require('./../.env.js')
+
+const HttpCodes = {
+	UNAUTHORIZED: 401, //登录失效
+}
+
+class request {
+	static request(method, url, data = null, that = null) {
+		let promise = new Promise(function(resolve, reject) {
+			let _url
+			if (process.env.NODE_ENV !== 'production') {
+				_url = ENV.APP_DEV_URL + url
+			} else {
+				_url = ENV.APP_PROD_URL + url
+			}
+			const param = {
+				url: _url,
+				method: method,
+				data: data,
+				header: {
+					'Authorization': 'Bearer ' + Storage.getToken(),
+					'Content-Type': 'application/json',
+				},
+				success(res) {
+					if (res.statusCode === 200) {
+						if (res.data.code === 200) {
+							resolve(res.data)
+						} else if (res.data.code == 401) {
+							uni.reLaunch({
+								url: '/pages/login'
+							})
+						} else if (res.data.code == 6401) {
+							uni.request({
+								url: ENV.APP_PROD_URL + '/api/refresh_token',
+								method: 'GET',
+								header: {
+									'Authorization': 'Bearer ' + Storage.getToken(),
+									'Content-Type': 'application/json',
+								},
+							}).then(res => {
+								if (res.data.code == 200) {
+									Storage.setToken(res.data.token)
+									param.header.Authorization = 'Bearer ' + res.data.token
+									uni.request(param)
+								} else if (res.data.code == 401) {
+									Storage.removeToken()
+									Storage.removeCache('userInfo')
+									uni.redirectTo({
+										url: '/pages/login'
+									})
+								}
+							})
+						} else {
+							resolve(res)
+						}
+					} else {
+						resolve(res)
+					}
+				},
+				fail(res) {
+					resolve(res)
+				}
+			}
+			uni.request(param)
+		}).catch((res) => {
+			if (res.statusCode === 200) {
+				if (res.data.code !== 200) {
+					return res.data
+				}
+			} else {
+				console.log('服务器错误:', res)
+				return res;
+			}
+		})
+		return promise
+	}
+
+	static get(url, data, that) {
+		return this.request('GET', url, data, that)
+	}
+
+	static post(url, data, that) {
+		return this.request('POST', url, data, that)
+	}
+
+	static put(url, data, that) {
+		return this.request('PUT', url, data, that)
+	}
+
+	static delete(url, data, that) {
+		return this.request('DELETE', url, data, that)
+	}
+}
+
+export default request

+ 21 - 0
uview-ui/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 www.uviewui.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 66 - 0
uview-ui/README.md

@@ -0,0 +1,66 @@
+<p align="center">
+    <img alt="logo" src="https://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;">
+</p>
+<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uView 2.0</h3>
+<h3 align="center">多平台快速开发的UI框架</h3>
+
+[![stars](https://img.shields.io/github/stars/umicro/uView2.0?style=flat-square&logo=GitHub)](https://github.com/umicro/uView2.0)
+[![forks](https://img.shields.io/github/forks/umicro/uView2.0?style=flat-square&logo=GitHub)](https://github.com/umicro/uView2.0)
+[![issues](https://img.shields.io/github/issues/umicro/uView2.0?style=flat-square&logo=GitHub)](https://github.com/umicro/uView2.0/issues)
+[![Website](https://img.shields.io/badge/uView-up-blue?style=flat-square)](https://uviewui.com)
+[![release](https://img.shields.io/github/v/release/umicro/uView2.0?style=flat-square)](https://gitee.com/umicro/uView2.0/releases)
+[![license](https://img.shields.io/github/license/umicro/uView2.0?style=flat-square)](https://en.wikipedia.org/wiki/MIT_License)
+
+## 说明
+
+uView UI,是[uni-app](https://uniapp.dcloud.io/)全面兼容nvue的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水
+
+## [官方文档:https://uviewui.com](https://uviewui.com)
+
+
+## 预览
+
+您可以通过**微信**扫码,查看最佳的演示效果。
+<br>
+<br>
+<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" >
+
+
+## 链接
+
+- [官方文档](https://www.uviewui.com/)
+- [更新日志](https://www.uviewui.com/components/changelog.html)
+- [升级指南](https://www.uviewui.com/components/changeGuide.html)
+- [关于我们](https://www.uviewui.com/cooperation/about.html)
+
+## 交流反馈
+
+欢迎加入我们的QQ群交流反馈:[点此跳转](https://www.uviewui.com/components/addQQGroup.html)
+
+## 关于PR
+
+> 我们非常乐意接受各位的优质PR,但在此之前我希望您了解uView2.0是一个需要兼容多个平台的(小程序、h5、ios app、android app)包括nvue页面、vue页面。
+> 所以希望在您修复bug并提交之前尽可能的去这些平台测试一下兼容性。最好能携带测试截图以方便审核。非常感谢!
+
+## 安装
+
+#### **uni-app插件市场链接** —— [https://ext.dcloud.net.cn/plugin?id=1593](https://ext.dcloud.net.cn/plugin?id=1593)
+
+请通过[官网安装文档](https://www.uviewui.com/components/install.html)了解更详细的内容
+
+## 快速上手
+
+请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容
+
+## 使用方法
+配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。
+
+```html
+<template>
+	<u-button text="按钮"></u-button>
+</template>
+```
+
+## 版权信息
+uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uView应用到您的产品中。
+

+ 362 - 0
uview-ui/changelog.md

@@ -0,0 +1,362 @@
+## 2.0.36(2023-03-27)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 重构`deepClone` & `deepMerge`方法
+2. 其他优化
+## 2.0.34(2022-09-24)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. `u-input`、`u-textarea`增加`ignoreCompositionEvent`属性
+2. 修复`route`方法调用可能报错的问题
+3. 修复`u-no-network`组件`z-index`无效的问题
+4. 修复`textarea`组件在h5上confirmType=""报错的问题
+5. `u-rate`适配`nvue`
+6. 优化验证手机号码的正则表达式(根据工信部发布的《电信网编号计划(2017年版)》进行修改。)
+7. `form-item`添加`labelPosition`属性
+8. `u-calendar`修复`maxDate`设置为当前日期,并且当前时间大于08:00时无法显示日期列表的问题 (#724)
+9. `u-radio`增加一个默认插槽用于自定义修改label内容 (#680)
+10. 修复`timeFormat`函数在safari重的兼容性问题 (#664)
+## 2.0.33(2022-06-17)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复`loadmore`组件`lineColor`类型错误问题
+2. 修复`u-parse`组件`imgtap`、`linktap`不生效问题
+## 2.0.32(2022-06-16)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+1. `u-loadmore`新增自定义颜色、虚/实线
+2. 修复`u-swiper-action`组件部分平台不能上下滑动的问题
+3. 修复`u-list`回弹问题
+4. 修复`notice-bar`组件动画在低端安卓机可能会抖动的问题
+5. `u-loading-page`添加控制图标大小的属性`iconSize`
+6. 修复`u-tooltip`组件`color`参数不生效的问题
+7. 修复`u--input`组件使用`blur`事件输出为`undefined`的bug
+8. `u-code-input`组件新增键盘弹起时,是否自动上推页面参数`adjustPosition`
+9. 修复`image`组件`load`事件无回调对象问题
+10. 修复`button`组件`loadingSize`设置无效问题
+10. 其他修复
+## 2.0.31(2022-04-19)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复`upload`在`vue`页面上传成功后没有成功标志的问题
+2. 解决演示项目中微信小程序模拟上传图片一直出于上传中问题
+3. 修复`u-code-input`组件在`nvue`页面编译到`app`平台上光标异常问题(`app`去除此功能)
+4. 修复`actionSheet`组件标题关闭按钮点击事件名称错误的问题
+5. 其他修复
+## 2.0.30(2022-04-04)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. `u-rate`增加`readonly`属性
+2. `tabs`滑块支持设置背景图片
+3. 修复`u-subsection` `mode`为`subsection`时,滑块样式不正确的问题
+4. `u-code-input`添加光标效果动画
+5. 修复`popup`的`open`事件不触发
+6. 修复`u-flex-column`无效的问题
+7. 修复`u-datetime-picker`索引在特定场合异常问题
+8. 修复`u-datetime-picker`最小时间字符串模板错误问题
+9. `u-swiper`添加`m3u8`验证
+10. `u-swiper`修改判断image和video逻辑
+11. 修复`swiper`无法使用本地图片问题,增加`type`参数
+12. 修复`u-row-notice`格式错误问题
+13. 修复`u-switch`组件当`unit`为`rpx`时,`nodeStyle`消失的问题
+14. 修复`datetime-picker`组件`showToolbar`与`visibleItemCount`属性无效的问题
+15. 修复`upload`组件条件编译位置判断错误,导致`previewImage`属性设置为`false`时,整个组件都会被隐藏的问题
+16. 修复`u-checkbox-group`设置`shape`属性无效的问题
+17. 修复`u-upload`的`capture`传入字符串的时候不生效的问题
+18. 修复`u-action-sheet`组件,关闭事件逻辑错误的问题
+19. 修复`u-list`触顶事件的触发错误的问题
+20. 修复`u-text`只有手机号可拨打的问题
+21. 修复`u-textarea`不能换行的问题
+22. 其他修复
+## 2.0.29(2022-03-13)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复`u--text`组件设置`decoration`属性未生效的问题
+2. 修复`u-datetime-picker`使用`formatter`后返回值不正确
+3. 修复`u-datetime-picker` `intercept` 可能为undefined
+4. 修复已设置单位 uni..config.unit = 'rpx'时,线型指示器 `transform` 的位置翻倍,导致指示器超出宽度
+5. 修复mixin中bem方法生成的类名在支付宝和字节小程序中失效
+6. 修复默认值传值为空的时候,打开`u-datetime-picker`报错,不能选中第一列时间的bug
+7. 修复`u-datetime-picker`使用`formatter`后返回值不正确
+8. 修复`u-image`组件`loading`无效果的问题
+9. 修复`config.unit`属性设为`rpx`时,导航栏占用高度不足导致塌陷的问题
+10. 修复`u-datetime-picker`组件`itemHeight`无效问题
+11. 其他修复
+## 2.0.28(2022-02-22)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. search组件新增searchIconSize属性
+2. 兼容Safari/Webkit中传入时间格式如2022-02-17 12:00:56
+3. 修复text value.js 判断日期出format错误问题
+4. priceFormat格式化金额出现精度错误
+5. priceFormat在部分情况下出现精度损失问题
+6. 优化表单rules提示
+7. 修复avatar组件src为空时,展示状态不对
+8. 其他修复
+## 2.0.27(2022-01-28)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1.样式修复
+## 2.0.26(2022-01-28)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1.样式修复
+## 2.0.25(2022-01-27)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复text组件mode=price时,可能会导致精度错误的问题
+2. 添加$u.setConfig()方法,可设置uView内置的config, props, zIndex, color属性,详见:[修改uView内置配置方案](https://uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
+3. 优化form组件在errorType=toast时,如果输入错误页面会有抖动的问题
+4. 修复$u.addUnit()对配置默认单位可能无效的问题
+## 2.0.24(2022-01-25)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复swiper在current指定非0时缩放有误
+2. 修复u-icon添加stop属性的时候报错
+3. 优化遗留的通过正则判断rpx单位的问题
+4. 优化Layout布局 vue使用gutter时,会超出固定区域
+5. 优化search组件高度单位问题(rpx -> px)
+6. 修复u-image slot 加载和错误的图片失去了高度
+7. 修复u-index-list中footer插槽与header插槽存在性判断错误
+8. 修复部分机型下u-popup关闭时会闪烁
+9. 修复u-image在nvue-app下失去宽高
+10. 修复u-popup运行报错
+11. 修复u-tooltip报错
+12. 修复box-sizing在app下的警告
+13. 修复u-navbar在小程序中报运行时错误
+14. 其他修复
+## 2.0.23(2022-01-24)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复image组件在hx3.3.9的nvue下可能会显示异常的问题
+2. 修复col组件gutter参数带rpx单位处理不正确的问题
+3. 修复text组件单行时无法显示省略号的问题
+4. navbar添加titleStyle参数
+5. 升级到hx3.3.9可消除nvue下控制台样式警告的问题
+## 2.0.22(2022-01-19)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. $u.page()方法优化,避免在特殊场景可能报错的问题
+2. picker组件添加immediateChange参数
+3. 新增$u.pages()方法
+## 2.0.21(2022-01-19)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 优化:form组件在用户设置rules的时候提示用户model必传
+2. 优化遗留的通过正则判断rpx单位的问题
+3. 修复微信小程序环境中tabbar组件开启safeAreaInsetBottom属性后,placeholder高度填充不正确
+4. 修复swiper在current指定非0时缩放有误
+5. 修复u-icon添加stop属性的时候报错
+6. 修复upload组件在accept=all的时候没有作用
+7. 修复在text组件mode为phone时call属性无效的问题
+8. 处理u-form clearValidate方法
+9. 其他修复
+## 2.0.20(2022-01-14)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复calendar默认会选择一个日期,如果直接点确定的话,无法取到值的问题
+2. 修复Slider缺少disabled props 还有注释
+3. 修复u-notice-bar点击事件无法拿到index索引值的问题
+4. 修复u-collapse-item在vue文件下,app端自定义插槽不生效的问题
+5. 优化头像为空时显示默认头像 
+6. 修复图片地址赋值后判断加载状态为完成问题
+7. 修复日历滚动到默认日期月份区域
+8. search组件暴露点击左边icon事件
+9. 修复u-form clearValidate方法不生效
+10. upload h5端增加返回文件参数(文件的name参数)
+11. 处理upload选择文件后url为blob类型无法预览的问题
+12. u-code-input 修复输入框没有往左移出一半屏幕
+13. 修复Upload上传 disabled为true时,控制台报hoverClass类型错误
+14. 临时处理ios app下grid点击坍塌问题
+15. 其他修复
+## 2.0.19(2021-12-29)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 优化微信小程序包体积可在微信中预览,请升级HbuilderX3.3.4,同时在“运行->运行到小程序模拟器”中勾选“运行时是否压缩代码”
+2. 优化微信小程序setData性能,处理某些方法如$u.route()无法在模板中使用的问题
+3. navbar添加autoBack参数
+4. 允许avatar组件的事件冒泡
+5. 修复cell组件报错问题
+6. 其他修复
+## 2.0.18(2021-12-28)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复app端编译报错问题
+2. 重新处理微信小程序端setData过大的性能问题
+3. 修复边框问题
+4. 修复最大最小月份不大于0则没有数据出现的问题
+5. 修复SwipeAction微信小程序端无法上下滑动问题
+6. 修复input的placeholder在小程序端默认显示为true问题
+7. 修复divider组件click事件无效问题
+8. 修复u-code-input maxlength 属性值为 String 类型时显示异常
+9. 修复当 grid只有 1到2时 在小程序端algin设置无效的问题
+10. 处理form-item的label为top时,取消错误提示的左边距
+11. 其他修复
+## 2.0.17(2021-12-26)
+## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 解决HBuilderX3.3.3.20211225版本导致的样式问题
+2. calendar日历添加monthNum参数
+3. navbar添加center slot
+## 2.0.16(2021-12-25)
+## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 解决微信小程序setData性能问题
+2. 修复count-down组件change事件不触发问题
+## 2.0.15(2021-12-21)
+## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复Cell单元格titleWidth无效
+2. 修复cheakbox组件ischecked不更新
+3. 修复keyboard是否显示"."按键默认值问题
+4. 修复number-keyboard是否显示键盘的"."符号问题
+5. 修复Input输入框 readonly无效
+6. 修复u-avatar 导致打包app、H5时候报错问题
+7. 修复Upload上传deletable无效
+8. 修复upload当设置maxSize时无效的问题
+9. 修复tabs lineWidth传入带单位的字符串的时候偏移量计算错误问题
+10. 修复rate组件在有padding的view内,显示的星星位置和可触摸区域不匹配,无法正常选中星星
+## 2.0.13(2021-12-14)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复配置默认单位为rpx可能会导致自定义导航栏高度异常的问题
+## 2.0.12(2021-12-14)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复tabs组件在vue环境下划线消失的问题
+2. 修复upload组件在安卓小程序无法选择视频的问题
+3. 添加uni.$u.config.unit配置,用于配置参数默认单位,详见:[默认单位配置](https://www.uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
+4. 修复textarea组件在没绑定v-model时,字符统计不生效问题
+5. 修复nvue下控制是否出现滚动条失效问题
+## 2.0.11(2021-12-13)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. text组件align参数无效的问题
+2. subsection组件添加keyName参数
+3. upload组件无法判断[Object file]类型的问题
+4. 处理notify层级过低问题
+5. codeInput组件添加disabledDot参数
+6. 处理actionSheet组件round参数无效的问题
+7. calendar组件添加round参数用于控制圆角值
+8. 处理swipeAction组件在vue环境下默认被打开的问题
+9. button组件的throttleTime节流参数无效的问题
+10. 解决u-notify手动关闭方法close()无效的问题
+11. input组件readonly不生效问题
+12. tag组件type参数为info不生效问题
+## 2.0.10(2021-12-08)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复button sendMessagePath属性不生效
+2. 修复DatetimePicker选择器title无效
+3. 修复u-toast设置loading=true不生效
+4. 修复u-text金额模式传0报错
+5. 修复u-toast组件的icon属性配置不生效
+6. button的icon在特殊场景下的颜色优化
+7. IndexList优化,增加#
+## 2.0.9(2021-12-01)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 优化swiper的height支持100%值(仅vue有效),修复嵌入视频时click事件无法触发的问题
+2. 优化tabs组件对list值为空的判断,或者动态变化list时重新计算相关尺寸的问题
+3. 优化datetime-picker组件逻辑,让其后续打开的默认值为上一次的选中值,需要通过v-model绑定值才有效
+4. 修复upload内嵌在其他组件中,选择图片可能不会换行的问题
+## 2.0.8(2021-12-01)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复toast的position参数无效问题
+2. 处理input在ios nvue上无法获得焦点的问题
+3. avatar-group组件添加extraValue参数,让剩余展示数量可手动控制
+4. tabs组件添加keyName参数用于配置从对象中读取的键名
+5. 处理text组件名字脱敏默认配置无效的问题
+6. 处理picker组件item文本太长换行问题
+## 2.0.7(2021-11-30)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复radio和checkbox动态改变v-model无效的问题。
+2. 优化form规则validator在微信小程序用法
+3. 修复backtop组件mode参数在微信小程序无效的问题
+4. 处理Album的previewFullImage属性无效的问题
+5. 处理u-datetime-picker组件mode='time'在选择改变时间时,控制台报错的问题
+## 2.0.6(2021-11-27)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 处理tag组件在vue下边框无效的问题。
+2. 处理popup组件圆角参数可能无效的问题。
+3. 处理tabs组件lineColor参数可能无效的问题。
+4. propgress组件在值很小时,显示异常的问题。
+## 2.0.5(2021-11-25)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. calendar在vue下显示异常问题。 
+2. form组件labelPosition和errorType参数无效的问题
+3. input组件inputAlign无效的问题
+4. 其他一些修复
+## 2.0.4(2021-11-23)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+0. input组件缺失@confirm事件,以及subfix和prefix无效问题
+1. component.scss文件样式在vue下干扰全局布局问题
+2. 修复subsection在vue环境下表现异常的问题
+3. tag组件的bgColor等参数无效的问题
+4. upload组件不换行的问题
+5. 其他的一些修复处理
+## 2.0.3(2021-11-16)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 处理modal的confirm回调事件拼写错误问题
+6. 处理input组件@input事件参数错误问题
+7. 其他一些修复
+## 2.0.2(2021-11-16)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 修复input组件formatter参数缺失问题
+6. 优化loading-icon组件的scss写法问题,防止不兼容新版本scss
+## 2.0.0(2020-11-15)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 修复input组件formatter参数缺失问题
+
+

+ 78 - 0
uview-ui/components/u--form/u--form.vue

@@ -0,0 +1,78 @@
+<template>
+	<uvForm
+		ref="uForm"
+		:model="model"
+		:rules="rules"
+		:errorType="errorType"
+		:borderBottom="borderBottom"
+		:labelPosition="labelPosition"
+		:labelWidth="labelWidth"
+		:labelAlign="labelAlign"
+		:labelStyle="labelStyle"
+		:customStyle="customStyle"
+	>
+		<slot />
+	</uvForm>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-form被uni-app官方占用了,u-form在nvue中相当于form组件
+	 * 所以在nvue下,取名为u--form,内部其实还是u-form.vue,只不过做一层中转
+	 */
+	import uvForm from '../u-form/u-form.vue';
+	import props from '../u-form/props.js'
+	export default {
+		// #ifdef MP-WEIXIN
+		name: 'u-form',
+		// #endif
+		// #ifndef MP-WEIXIN
+		name: 'u--form',
+		// #endif
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvForm
+		},
+		created() {
+			this.children = []
+		},
+		methods: {
+			// 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则
+			setRules(rules) {
+				this.$refs.uForm.setRules(rules)
+			},
+			validate() {
+				/**
+				 * 在微信小程序中,通过this.$parent拿到的父组件是u--form,而不是其内嵌的u-form
+				 * 导致在u-form组件中,拿不到对应的children数组,从而校验无效,所以这里每次调用u-form组件中的
+				 * 对应方法的时候,在小程序中都先将u--form的children赋值给u-form中的children
+				 */
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.validate()
+			},
+			validateField(value, callback, event) {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.validateField(value, callback, event)
+			},
+			resetFields() {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.resetFields()
+			},
+			clearValidate(props) {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.clearValidate(props)
+			},
+			setMpData() {
+				this.$refs.uForm.children = this.children
+			}
+		},
+	}
+</script>

+ 47 - 0
uview-ui/components/u--image/u--image.vue

@@ -0,0 +1,47 @@
+<template>
+	<uvImage 
+		:src="src"
+		:mode="mode"
+		:width="width"
+		:height="height"
+		:shape="shape"
+		:radius="radius"
+		:lazyLoad="lazyLoad"
+		:showMenuByLongpress="showMenuByLongpress"
+		:loadingIcon="loadingIcon"
+		:errorIcon="errorIcon"
+		:showLoading="showLoading"
+		:showError="showError"
+		:fade="fade"
+		:webp="webp"
+		:duration="duration"
+		:bgColor="bgColor"
+		:customStyle="customStyle"
+		@click="$emit('click')"
+		@error="$emit('error')"
+		@load="$emit('load')"
+	>
+		<template v-slot:loading>
+			<slot name="loading"></slot>
+		</template>
+		<template v-slot:error>
+			<slot name="error"></slot>
+		</template>
+	</uvImage>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-image被uni-app官方占用了,u-image在nvue中相当于image组件
+	 * 所以在nvue下,取名为u--image,内部其实还是u-iamge.vue,只不过做一层中转
+	 */
+	import uvImage from '../u-image/u-image.vue';
+	import props from '../u-image/props.js';
+	export default {
+		name: 'u--image',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvImage
+		},
+	}
+</script>

+ 73 - 0
uview-ui/components/u--input/u--input.vue

@@ -0,0 +1,73 @@
+<template>
+	<uvInput 
+		:value="value"
+		:type="type"
+		:fixed="fixed"
+		:disabled="disabled"
+		:disabledColor="disabledColor"
+		:clearable="clearable"
+		:password="password"
+		:maxlength="maxlength"
+		:placeholder="placeholder"
+		:placeholderClass="placeholderClass"
+		:placeholderStyle="placeholderStyle"
+		:showWordLimit="showWordLimit"
+		:confirmType="confirmType"
+		:confirmHold="confirmHold"
+		:holdKeyboard="holdKeyboard"
+		:focus="focus"
+		:autoBlur="autoBlur"
+		:disableDefaultPadding="disableDefaultPadding"
+		:cursor="cursor"
+		:cursorSpacing="cursorSpacing"
+		:selectionStart="selectionStart"
+		:selectionEnd="selectionEnd"
+		:adjustPosition="adjustPosition"
+		:inputAlign="inputAlign"
+		:fontSize="fontSize"
+		:color="color"
+		:prefixIcon="prefixIcon"
+		:suffixIcon="suffixIcon"
+		:suffixIconStyle="suffixIconStyle"
+		:prefixIconStyle="prefixIconStyle"
+		:border="border"
+		:readonly="readonly"
+		:shape="shape"
+		:customStyle="customStyle"
+		:formatter="formatter"
+		:ignoreCompositionEvent="ignoreCompositionEvent"
+		@focus="$emit('focus')"
+		@blur="e => $emit('blur', e)"
+		@keyboardheightchange="$emit('keyboardheightchange')"
+		@change="e => $emit('change', e)"
+		@input="e => $emit('input', e)"
+		@confirm="e => $emit('confirm', e)"
+		@clear="$emit('clear')"
+		@click="$emit('click')"
+	>
+		<!-- #ifdef MP -->
+		<slot name="prefix"></slot>
+		<slot name="suffix"></slot>
+		<!-- #endif -->
+		<!-- #ifndef MP -->
+		<slot name="prefix" slot="prefix"></slot>
+		<slot name="suffix" slot="suffix"></slot>
+		<!-- #endif -->
+	</uvInput>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-input被uni-app官方占用了,u-input在nvue中相当于input组件
+	 * 所以在nvue下,取名为u--input,内部其实还是u-input.vue,只不过做一层中转
+	 */
+	import uvInput from '../u-input/u-input.vue';
+	import props from '../u-input/props.js'
+	export default {
+		name: 'u--input',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvInput
+		},
+	}
+</script>

+ 44 - 0
uview-ui/components/u--text/u--text.vue

@@ -0,0 +1,44 @@
+<template>
+    <uvText
+        :type="type"
+        :show="show"
+        :text="text"
+        :prefixIcon="prefixIcon"
+        :suffixIcon="suffixIcon"
+        :mode="mode"
+        :href="href"
+        :format="format"
+        :call="call"
+        :openType="openType"
+        :bold="bold"
+        :block="block"
+        :lines="lines"
+        :color="color"
+		:decoration="decoration"
+        :size="size"
+        :iconStyle="iconStyle"
+        :margin="margin"
+        :lineHeight="lineHeight"
+        :align="align"
+        :wordWrap="wordWrap"
+        :customStyle="customStyle"
+        @click="$emit('click')"
+    ></uvText>
+</template>
+
+<script>
+/**
+ * 此组件存在的理由是,在nvue下,u-text被uni-app官方占用了,u-text在nvue中相当于input组件
+ * 所以在nvue下,取名为u--input,内部其实还是u-text.vue,只不过做一层中转
+ * 不使用v-bind="$attrs",而是分开独立写传参,是因为微信小程序不支持此写法
+ */
+import uvText from "../u-text/u-text.vue";
+import props from "../u-text/props.js";
+export default {
+    name: "u--text",
+    mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+    components: {
+        uvText,
+    },
+};
+</script>

+ 48 - 0
uview-ui/components/u--textarea/u--textarea.vue

@@ -0,0 +1,48 @@
+<template>
+	<uvTextarea
+		:value="value"
+		:placeholder="placeholder"
+		:height="height"
+		:confirmType="confirmType"
+		:disabled="disabled"
+		:count="count"
+		:focus="focus"
+		:autoHeight="autoHeight"
+		:fixed="fixed"
+		:cursorSpacing="cursorSpacing"
+		:cursor="cursor"
+		:showConfirmBar="showConfirmBar"
+		:selectionStart="selectionStart"
+		:selectionEnd="selectionEnd"
+		:adjustPosition="adjustPosition"
+		:disableDefaultPadding="disableDefaultPadding"
+		:holdKeyboard="holdKeyboard"
+		:maxlength="maxlength"
+		:border="border"
+		:customStyle="customStyle"
+		:formatter="formatter"
+		:ignoreCompositionEvent="ignoreCompositionEvent"
+		@focus="e => $emit('focus')"
+		@blur="e => $emit('blur')"
+		@linechange="e => $emit('linechange', e)"
+		@confirm="e => $emit('confirm')"
+		@input="e => $emit('input', e)"
+		@keyboardheightchange="e => $emit('keyboardheightchange')"
+	></uvTextarea>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u--textarea被uni-app官方占用了,u-textarea在nvue中相当于textarea组件
+	 * 所以在nvue下,取名为u--textarea,内部其实还是u-textarea.vue,只不过做一层中转
+	 */
+	import uvTextarea from '../u-textarea/u-textarea.vue';
+	import props from '../u-textarea/props.js'
+	export default {
+		name: 'u--textarea',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvTextarea
+		},
+	}
+</script>

+ 54 - 0
uview-ui/components/u-action-sheet/props.js

@@ -0,0 +1,54 @@
+export default {
+    props: {
+        // 操作菜单是否展示 (默认false)
+        show: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.show
+        },
+        // 标题
+        title: {
+            type: String,
+            default: uni.$u.props.actionSheet.title
+        },
+        // 选项上方的描述信息
+        description: {
+            type: String,
+            default: uni.$u.props.actionSheet.description
+        },
+        // 数据
+        actions: {
+            type: Array,
+            default: uni.$u.props.actionSheet.actions
+        },
+        // 取消按钮的文字,不为空时显示按钮
+        cancelText: {
+            type: String,
+            default: uni.$u.props.actionSheet.cancelText
+        },
+        // 点击某个菜单项时是否关闭弹窗
+        closeOnClickAction: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.closeOnClickAction
+        },
+        // 处理底部安全区(默认true)
+        safeAreaInsetBottom: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.safeAreaInsetBottom
+        },
+        // 小程序的打开方式
+        openType: {
+            type: String,
+            default: uni.$u.props.actionSheet.openType
+        },
+        // 点击遮罩是否允许关闭 (默认true)
+        closeOnClickOverlay: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.closeOnClickOverlay
+        },
+        // 圆角值
+        round: {
+            type: [Boolean, String, Number],
+            default: uni.$u.props.actionSheet.round
+        }
+    }
+}

+ 278 - 0
uview-ui/components/u-action-sheet/u-action-sheet.vue

@@ -0,0 +1,278 @@
+
+<template>
+	<u-popup
+	    :show="show"
+	    mode="bottom"
+	    @close="closeHandler"
+	    :safeAreaInsetBottom="safeAreaInsetBottom"
+	    :round="round"
+	>
+		<view class="u-action-sheet">
+			<view
+			    class="u-action-sheet__header"
+			    v-if="title"
+			>
+				<text class="u-action-sheet__header__title u-line-1">{{title}}</text>
+				<view
+				    class="u-action-sheet__header__icon-wrap"
+				    @tap.stop="cancel"
+				>
+					<u-icon
+					    name="close"
+					    size="17"
+					    color="#c8c9cc"
+					    bold
+					></u-icon>
+				</view>
+			</view>
+			<text
+			    class="u-action-sheet__description"
+				:style="[{
+					marginTop: `${title && description ? 0 : '18px'}`
+				}]"
+			    v-if="description"
+			>{{description}}</text>
+			<slot>
+				<u-line v-if="description"></u-line>
+				<view class="u-action-sheet__item-wrap">
+					<template v-for="(item, index) in actions">
+						<!-- #ifdef MP -->
+						<button
+						    :key="index"
+						    class="u-reset-button"
+						    :openType="item.openType"
+						    @getuserinfo="onGetUserInfo"
+						    @contact="onContact"
+						    @getphonenumber="onGetPhoneNumber"
+						    @error="onError"
+						    @launchapp="onLaunchApp"
+						    @opensetting="onOpenSetting"
+						    :lang="lang"
+						    :session-from="sessionFrom"
+						    :send-message-title="sendMessageTitle"
+						    :send-message-path="sendMessagePath"
+						    :send-message-img="sendMessageImg"
+						    :show-message-card="showMessageCard"
+						    :app-parameter="appParameter"
+						    @tap="selectHandler(index)"
+						    :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
+						>
+							<!-- #endif -->
+							<view
+							    class="u-action-sheet__item-wrap__item"
+							    @tap.stop="selectHandler(index)"
+							    :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
+							    :hover-stay-time="150"
+							>
+								<template v-if="!item.loading">
+									<text
+									    class="u-action-sheet__item-wrap__item__name"
+									    :style="[itemStyle(index)]"
+									>{{ item.name }}</text>
+									<text
+									    v-if="item.subname"
+									    class="u-action-sheet__item-wrap__item__subname"
+									>{{ item.subname }}</text>
+								</template>
+								<u-loading-icon
+								    v-else
+								    custom-class="van-action-sheet__loading"
+								    size="18"
+								    mode="circle"
+								/>
+							</view>
+							<!-- #ifdef MP -->
+						</button>
+						<!-- #endif -->
+						<u-line v-if="index !== actions.length - 1"></u-line>
+					</template>
+				</view>
+			</slot>
+			<u-gap
+			    bgColor="#eaeaec"
+			    height="6"
+			    v-if="cancelText"
+			></u-gap>
+			<view hover-class="u-action-sheet--hover">
+				<text
+				    @touchmove.stop.prevent
+				    :hover-stay-time="150"
+				    v-if="cancelText"
+				    class="u-action-sheet__cancel-text"
+				    @tap="cancel"
+				>{{cancelText}}</text>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+	import openType from '../../libs/mixin/openType'
+	import button from '../../libs/mixin/button'
+	import props from './props.js';
+	/**
+	 * ActionSheet 操作菜单
+	 * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
+	 * @tutorial https://www.uviewui.com/components/actionSheet.html
+	 * 
+	 * @property {Boolean}			show				操作菜单是否展示 (默认 false )
+	 * @property {String}			title				操作菜单标题
+	 * @property {String}			description			选项上方的描述信息
+	 * @property {Array<Object>}	actions				按钮的文字数组,见官方文档示例
+	 * @property {String}			cancelText			取消按钮的提示文字,不为空时显示按钮
+	 * @property {Boolean}			closeOnClickAction	点击某个菜单项时是否关闭弹窗 (默认 true )
+	 * @property {Boolean}			safeAreaInsetBottom	处理底部安全区 (默认 true )
+	 * @property {String}			openType			小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error )
+	 * @property {Boolean}			closeOnClickOverlay	点击遮罩是否允许关闭  (默认 true )
+	 * @property {Number|String}	round				圆角值,默认无圆角  (默认 0 )
+	 * @property {String}			lang				指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文
+	 * @property {String}			sessionFrom			会话来源,openType="contact"时有效
+	 * @property {String}			sendMessageTitle	会话内消息卡片标题,openType="contact"时有效
+	 * @property {String}			sendMessagePath		会话内消息卡片点击跳转小程序路径,openType="contact"时有效
+	 * @property {String}			sendMessageImg		会话内消息卡片图片,openType="contact"时有效
+	 * @property {Boolean}			showMessageCard		是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false )
+	 * @property {String}			appParameter		打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效
+	 * 
+	 * @event {Function} select			点击ActionSheet列表项时触发 
+	 * @event {Function} close			点击取消按钮时触发
+	 * @event {Function} getuserinfo	用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效
+	 * @event {Function} contact		客服消息回调,openType="contact"时有效
+	 * @event {Function} getphonenumber	获取用户手机号回调,openType="getPhoneNumber"时有效
+	 * @event {Function} error			当使用开放能力时,发生错误的回调,openType="error"时有效
+	 * @event {Function} launchapp		打开 APP 成功的回调,openType="launchApp"时有效
+	 * @event {Function} opensetting	在打开授权设置页后回调,openType="openSetting"时有效
+	 * @example <u-action-sheet :actions="list" :title="title" :show="show"></u-action-sheet>
+	 */
+	export default {
+		name: "u-action-sheet",
+		// 一些props参数和methods方法,通过mixin混入,因为其他文件也会用到
+		mixins: [openType, button, uni.$u.mixin, props],
+		data() {
+			return {
+
+			}
+		},
+		computed: {
+			// 操作项目的样式
+			itemStyle() {
+				return (index) => {
+					let style = {};
+					if (this.actions[index].color) style.color = this.actions[index].color
+					if (this.actions[index].fontSize) style.fontSize = uni.$u.addUnit(this.actions[index].fontSize)
+					// 选项被禁用的样式
+					if (this.actions[index].disabled) style.color = '#c0c4cc'
+					return style;
+				}
+			},
+		},
+		methods: {
+			closeHandler() {
+				// 允许点击遮罩关闭时,才发出close事件
+				if(this.closeOnClickOverlay) {
+					this.$emit('close')
+				}
+			},
+			// 点击取消按钮
+			cancel() {
+				this.$emit('close')
+			},
+			selectHandler(index) {
+				const item = this.actions[index]
+				if (item && !item.disabled && !item.loading) {
+					this.$emit('select', item)
+					if (this.closeOnClickAction) {
+						this.$emit('close')
+					}
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+	$u-action-sheet-reset-button-width:100% !default;
+	$u-action-sheet-title-font-size: 16px !default;
+	$u-action-sheet-title-padding: 12px 30px !default;
+	$u-action-sheet-title-color: $u-main-color !default;
+	$u-action-sheet-header-icon-wrap-right:15px !default;
+	$u-action-sheet-header-icon-wrap-top:15px !default;
+	$u-action-sheet-description-font-size:13px !default;
+	$u-action-sheet-description-color:14px !default;
+	$u-action-sheet-description-margin: 18px 15px !default;
+	$u-action-sheet-item-wrap-item-padding:15px !default;
+	$u-action-sheet-item-wrap-name-font-size:16px !default;
+	$u-action-sheet-item-wrap-subname-font-size:13px !default;
+	$u-action-sheet-item-wrap-subname-color: #c0c4cc !default;
+	$u-action-sheet-item-wrap-subname-margin-top:10px !default;
+	$u-action-sheet-cancel-text-font-size:16px !default;
+	$u-action-sheet-cancel-text-color:$u-content-color !default;
+	$u-action-sheet-cancel-text-font-size:15px !default;
+	$u-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
+
+	.u-reset-button {
+		width: $u-action-sheet-reset-button-width;
+	}
+
+	.u-action-sheet {
+		text-align: center;
+		&__header {
+			position: relative;
+			padding: $u-action-sheet-title-padding;
+			&__title {
+				font-size: $u-action-sheet-title-font-size;
+				color: $u-action-sheet-title-color;
+				font-weight: bold;
+				text-align: center;
+			}
+
+			&__icon-wrap {
+				position: absolute;
+				right: $u-action-sheet-header-icon-wrap-right;
+				top: $u-action-sheet-header-icon-wrap-top;
+			}
+		}
+
+		&__description {
+			font-size: $u-action-sheet-description-font-size;
+			color: $u-tips-color;
+			margin: $u-action-sheet-description-margin;
+			text-align: center;
+		}
+
+		&__item-wrap {
+
+			&__item {
+				padding: $u-action-sheet-item-wrap-item-padding;
+				@include flex;
+				align-items: center;
+				justify-content: center;
+				flex-direction: column;
+
+				&__name {
+					font-size: $u-action-sheet-item-wrap-name-font-size;
+					color: $u-main-color;
+					text-align: center;
+				}
+
+				&__subname {
+					font-size: $u-action-sheet-item-wrap-subname-font-size;
+					color: $u-action-sheet-item-wrap-subname-color;
+					margin-top: $u-action-sheet-item-wrap-subname-margin-top;
+					text-align: center;
+				}
+			}
+		}
+
+		&__cancel-text {
+			font-size: $u-action-sheet-cancel-text-font-size;
+			color: $u-action-sheet-cancel-text-color;
+			text-align: center;
+			padding: $u-action-sheet-cancel-text-font-size;
+		}
+
+		&--hover {
+			background-color: $u-action-sheet-cancel-text-hover-background-color;
+		}
+	}
+</style>

+ 59 - 0
uview-ui/components/u-album/props.js

@@ -0,0 +1,59 @@
+export default {
+    props: {
+        // 图片地址,Array<String>|Array<Object>形式
+        urls: {
+            type: Array,
+            default: uni.$u.props.album.urls
+        },
+        // 指定从数组的对象元素中读取哪个属性作为图片地址
+        keyName: {
+            type: String,
+            default: uni.$u.props.album.keyName
+        },
+        // 单图时,图片长边的长度
+        singleSize: {
+            type: [String, Number],
+            default: uni.$u.props.album.singleSize
+        },
+        // 多图时,图片边长
+        multipleSize: {
+            type: [String, Number],
+            default: uni.$u.props.album.multipleSize
+        },
+        // 多图时,图片水平和垂直之间的间隔
+        space: {
+            type: [String, Number],
+            default: uni.$u.props.album.space
+        },
+        // 单图时,图片缩放裁剪的模式
+        singleMode: {
+            type: String,
+            default: uni.$u.props.album.singleMode
+        },
+        // 多图时,图片缩放裁剪的模式
+        multipleMode: {
+            type: String,
+            default: uni.$u.props.album.multipleMode
+        },
+        // 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
+        maxCount: {
+            type: [String, Number],
+            default: uni.$u.props.album.maxCount
+        },
+        // 是否可以预览图片
+        previewFullImage: {
+            type: Boolean,
+            default: uni.$u.props.album.previewFullImage
+        },
+        // 每行展示图片数量,如设置,singleSize和multipleSize将会无效
+        rowCount: {
+            type: [String, Number],
+            default: uni.$u.props.album.rowCount
+        },
+        // 超出maxCount时是否显示查看更多的提示
+        showMore: {
+            type: Boolean,
+            default: uni.$u.props.album.showMore
+        }
+    }
+}

+ 259 - 0
uview-ui/components/u-album/u-album.vue

@@ -0,0 +1,259 @@
+<template>
+    <view class="u-album">
+        <view
+            class="u-album__row"
+            ref="u-album__row"
+            v-for="(arr, index) in showUrls"
+            :forComputedUse="albumWidth"
+            :key="index"
+        >
+            <view
+                class="u-album__row__wrapper"
+                v-for="(item, index1) in arr"
+                :key="index1"
+                :style="[imageStyle(index + 1, index1 + 1)]"
+                @tap="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
+            >
+                <image
+                    :src="getSrc(item)"
+                    :mode="
+                        urls.length === 1
+                            ? imageHeight > 0
+                                ? singleMode
+                                : 'widthFix'
+                            : multipleMode
+                    "
+                    :style="[
+                        {
+                            width: imageWidth,
+                            height: imageHeight
+                        }
+                    ]"
+                ></image>
+                <view
+                    v-if="
+                        showMore &&
+                        urls.length > rowCount * showUrls.length &&
+                        index === showUrls.length - 1 &&
+                        index1 === showUrls[showUrls.length - 1].length - 1
+                    "
+                    class="u-album__row__wrapper__text"
+                >
+                    <u--text
+                        :text="`+${urls.length - maxCount}`"
+                        color="#fff"
+                        :size="multipleSize * 0.3"
+                        align="center"
+                        customStyle="justify-content: center"
+                    ></u--text>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+import props from './props.js'
+// #ifdef APP-NVUE
+// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+const dom = uni.requireNativePlugin('dom')
+// #endif
+
+/**
+ * Album 相册
+ * @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
+ * @tutorial https://www.uviewui.com/components/album.html
+ *
+ * @property {Array}           urls             图片地址列表 Array<String>|Array<Object>形式
+ * @property {String}          keyName          指定从数组的对象元素中读取哪个属性作为图片地址
+ * @property {String | Number} singleSize       单图时,图片长边的长度  (默认 180 )
+ * @property {String | Number} multipleSize     多图时,图片边长 (默认 70 )
+ * @property {String | Number} space            多图时,图片水平和垂直之间的间隔 (默认 6 )
+ * @property {String}          singleMode       单图时,图片缩放裁剪的模式 (默认 'scaleToFill' )
+ * @property {String}          multipleMode     多图时,图片缩放裁剪的模式 (默认 'aspectFill' )
+ * @property {String | Number} maxCount         取消按钮的提示文字 (默认 9 )
+ * @property {Boolean}         previewFullImage 是否可以预览图片 (默认 true )
+ * @property {String | Number} rowCount         每行展示图片数量,如设置,singleSize和multipleSize将会无效	(默认 3 )
+ * @property {Boolean}         showMore         超出maxCount时是否显示查看更多的提示 (默认 true )
+ *
+ * @event    {Function}        albumWidth       某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送  (回调参数 width )
+ * @example <u-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></u-album>
+ */
+export default {
+    name: 'u-album',
+    mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+    data() {
+        return {
+            // 单图的宽度
+            singleWidth: 0,
+            // 单图的高度
+            singleHeight: 0,
+            // 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比
+            singlePercent: 0.6
+        }
+    },
+    watch: {
+        urls: {
+            immediate: true,
+            handler(newVal) {
+                if (newVal.length === 1) {
+                    this.getImageRect()
+                }
+            }
+        }
+    },
+    computed: {
+        imageStyle() {
+            return (index1, index2) => {
+                const { space, rowCount, multipleSize, urls } = this,
+                    { addUnit, addStyle } = uni.$u,
+                    rowLen = this.showUrls.length,
+                    allLen = this.urls.length
+                const style = {
+                    marginRight: addUnit(space),
+                    marginBottom: addUnit(space)
+                }
+                // 如果为最后一行,则每个图片都无需下边框
+                if (index1 === rowLen) style.marginBottom = 0
+                // 每行的最右边一张和总长度的最后一张无需右边框
+                if (
+                    index2 === rowCount ||
+                    (index1 === rowLen &&
+                        index2 === this.showUrls[index1 - 1].length)
+                )
+                    style.marginRight = 0
+                return style
+            }
+        },
+        // 将数组划分为二维数组
+        showUrls() {
+            const arr = []
+            this.urls.map((item, index) => {
+                // 限制最大展示数量
+                if (index + 1 <= this.maxCount) {
+                    // 计算该元素为第几个素组内
+                    const itemIndex = Math.floor(index / this.rowCount)
+                    // 判断对应的索引是否存在
+                    if (!arr[itemIndex]) {
+                        arr[itemIndex] = []
+                    }
+                    arr[itemIndex].push(item)
+                }
+            })
+            return arr
+        },
+        imageWidth() {
+            return uni.$u.addUnit(
+                this.urls.length === 1 ? this.singleWidth : this.multipleSize
+            )
+        },
+        imageHeight() {
+            return uni.$u.addUnit(
+                this.urls.length === 1 ? this.singleHeight : this.multipleSize
+            )
+        },
+        // 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
+        // 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
+        albumWidth() {
+            let width = 0
+            if (this.urls.length === 1) {
+                width = this.singleWidth
+            } else {
+                width =
+                    this.showUrls[0].length * this.multipleSize +
+                    this.space * (this.showUrls[0].length - 1)
+            }
+            this.$emit('albumWidth', width)
+            return width
+        }
+    },
+    methods: {
+        // 预览图片
+        onPreviewTap(url) {
+            const urls = this.urls.map((item) => {
+                return this.getSrc(item)
+            })
+            uni.previewImage({
+                current: url,
+                urls
+            })
+        },
+        // 获取图片的路径
+        getSrc(item) {
+            return uni.$u.test.object(item)
+                ? (this.keyName && item[this.keyName]) || item.src
+                : item
+        },
+        // 单图时,获取图片的尺寸
+        // 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
+        // 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
+        getImageRect() {
+            const src = this.getSrc(this.urls[0])
+            uni.getImageInfo({
+                src,
+                success: (res) => {
+                    // 判断图片横向还是竖向展示方式
+                    const isHorizotal = res.width >= res.height
+                    this.singleWidth = isHorizotal
+                        ? this.singleSize
+                        : (res.width / res.height) * this.singleSize
+                    this.singleHeight = !isHorizotal
+                        ? this.singleSize
+                        : (res.height / res.width) * this.singleWidth
+                },
+                fail: () => {
+                    this.getComponentWidth()
+                }
+            })
+        },
+        // 获取组件的宽度
+        async getComponentWidth() {
+            // 延时一定时间,以获取dom尺寸
+            await uni.$u.sleep(30)
+            // #ifndef APP-NVUE
+            this.$uGetRect('.u-album__row').then((size) => {
+                this.singleWidth = size.width * this.singlePercent
+            })
+            // #endif
+
+            // #ifdef APP-NVUE
+            // 这里ref="u-album__row"所在的标签为通过for循环出来,导致this.$refs['u-album__row']是一个数组
+            const ref = this.$refs['u-album__row'][0]
+            ref &&
+                dom.getComponentRect(ref, (res) => {
+                    this.singleWidth = res.size.width * this.singlePercent
+                })
+            // #endif
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../../libs/css/components.scss';
+
+.u-album {
+    @include flex(column);
+
+    &__row {
+        @include flex(row);
+        flex-wrap: wrap;
+
+        &__wrapper {
+            position: relative;
+
+            &__text {
+                position: absolute;
+                top: 0;
+                left: 0;
+                right: 0;
+                bottom: 0;
+                background-color: rgba(0, 0, 0, 0.3);
+                @include flex(row);
+                justify-content: center;
+                align-items: center;
+            }
+        }
+    }
+}
+</style>

+ 44 - 0
uview-ui/components/u-alert/props.js

@@ -0,0 +1,44 @@
+export default {
+    props: {
+        // 显示文字
+        title: {
+            type: String,
+            default: uni.$u.props.alert.title
+        },
+        // 主题,success/warning/info/error
+        type: {
+            type: String,
+            default: uni.$u.props.alert.type
+        },
+        // 辅助性文字
+        description: {
+            type: String,
+            default: uni.$u.props.alert.description
+        },
+        // 是否可关闭
+        closable: {
+            type: Boolean,
+            default: uni.$u.props.alert.closable
+        },
+        // 是否显示图标
+        showIcon: {
+            type: Boolean,
+            default: uni.$u.props.alert.showIcon
+        },
+        // 浅或深色调,light-浅色,dark-深色
+        effect: {
+            type: String,
+            default: uni.$u.props.alert.effect
+        },
+        // 文字是否居中
+        center: {
+            type: Boolean,
+            default: uni.$u.props.alert.center
+        },
+        // 字体大小
+        fontSize: {
+            type: [String, Number],
+            default: uni.$u.props.alert.fontSize
+        }
+    }
+}

+ 243 - 0
uview-ui/components/u-alert/u-alert.vue

@@ -0,0 +1,243 @@
+<template>
+	<u-transition
+	    mode="fade"
+	    :show="show"
+	>
+		<view
+		    class="u-alert"
+		    :class="[`u-alert--${type}--${effect}`]"
+		    @tap.stop="clickHandler"
+		    :style="[$u.addStyle(customStyle)]"
+		>
+			<view
+			    class="u-alert__icon"
+			    v-if="showIcon"
+			>
+				<u-icon
+				    :name="iconName"
+				    size="18"
+				    :color="iconColor"
+				></u-icon>
+			</view>
+			<view
+			    class="u-alert__content"
+			    :style="[{
+					paddingRight: closable ? '20px' : 0
+				}]"
+			>
+				<text
+				    class="u-alert__content__title"
+				    v-if="title"
+					:style="[{
+						fontSize: $u.addUnit(fontSize),
+						textAlign: center ? 'center' : 'left'
+					}]"
+				    :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
+				>{{ title }}</text>
+				<text
+				    class="u-alert__content__desc"
+					v-if="description"
+					:style="[{
+						fontSize: $u.addUnit(fontSize),
+						textAlign: center ? 'center' : 'left'
+					}]"
+				    :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
+				>{{ description }}</text>
+			</view>
+			<view
+			    class="u-alert__close"
+			    v-if="closable"
+			    @tap.stop="closeHandler"
+			>
+				<u-icon
+				    name="close"
+				    :color="iconColor"
+				    size="15"
+				></u-icon>
+			</view>
+		</view>
+	</u-transition>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * Alert  警告提示
+	 * @description 警告提示,展现需要关注的信息。
+	 * @tutorial https://www.uviewui.com/components/alertTips.html
+	 * 
+	 * @property {String}			title       显示的文字 
+	 * @property {String}			type        使用预设的颜色  (默认 'warning' )
+	 * @property {String}			description 辅助性文字,颜色比title浅一点,字号也小一点,可选  
+	 * @property {Boolean}			closable    关闭按钮(默认为叉号icon图标)  (默认 false )
+	 * @property {Boolean}			showIcon    是否显示左边的辅助图标   ( 默认 false )
+	 * @property {String}			effect      多图时,图片缩放裁剪的模式  (默认 'light' )
+	 * @property {Boolean}			center		文字是否居中  (默认 false )
+	 * @property {String | Number}	fontSize    字体大小  (默认 14 )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @event    {Function}        click       点击组件时触发
+	 * @example  <u-alert :title="title"  type = "warning" :closable="closable" :description = "description"></u-alert>
+	 */
+	export default {
+		name: 'u-alert',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+				show: true
+			}
+		},
+		computed: {
+			iconColor() {
+				return this.effect === 'light' ? this.type : '#fff'
+			},
+			// 不同主题对应不同的图标
+			iconName() {
+				switch (this.type) {
+					case 'success':
+						return 'checkmark-circle-fill';
+						break;
+					case 'error':
+						return 'close-circle-fill';
+						break;
+					case 'warning':
+						return 'error-circle-fill';
+						break;
+					case 'info':
+						return 'info-circle-fill';
+						break;
+					case 'primary':
+						return 'more-circle-fill';
+						break;
+					default: 
+						return 'error-circle-fill';
+				}
+			}
+		},
+		methods: {
+			// 点击内容
+			clickHandler() {
+				this.$emit('click')
+			},
+			// 点击关闭按钮
+			closeHandler() {
+				this.show = false
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-alert {
+		position: relative;
+		background-color: $u-primary;
+		padding: 8px 10px;
+		@include flex(row);
+		align-items: center;
+		border-top-left-radius: 4px;
+		border-top-right-radius: 4px;
+		border-bottom-left-radius: 4px;
+		border-bottom-right-radius: 4px;
+
+		&--primary--dark {
+			background-color: $u-primary;
+		}
+
+		&--primary--light {
+			background-color: #ecf5ff;
+		}
+
+		&--error--dark {
+			background-color: $u-error;
+		}
+
+		&--error--light {
+			background-color: #FEF0F0;
+		}
+
+		&--success--dark {
+			background-color: $u-success;
+		}
+
+		&--success--light {
+			background-color: #f5fff0;
+		}
+
+		&--warning--dark {
+			background-color: $u-warning;
+		}
+
+		&--warning--light {
+			background-color: #FDF6EC;
+		}
+
+		&--info--dark {
+			background-color: $u-info;
+		}
+
+		&--info--light {
+			background-color: #f4f4f5;
+		}
+
+		&__icon {
+			margin-right: 5px;
+		}
+
+		&__content {
+			@include flex(column);
+			flex: 1;
+
+			&__title {
+				color: $u-main-color;
+				font-size: 14px;
+				font-weight: bold;
+				color: #fff;
+				margin-bottom: 2px;
+			}
+
+			&__desc {
+				color: $u-main-color;
+				font-size: 14px;
+				flex-wrap: wrap;
+				color: #fff;
+			}
+		}
+
+		&__title--dark,
+		&__desc--dark {
+			color: #FFFFFF;
+		}
+
+		&__text--primary--light,
+		&__text--primary--light {
+			color: $u-primary;
+		}
+
+		&__text--success--light,
+		&__text--success--light {
+			color: $u-success;
+		}
+
+		&__text--warning--light,
+		&__text--warning--light {
+			color: $u-warning;
+		}
+
+		&__text--error--light,
+		&__text--error--light {
+			color: $u-error;
+		}
+
+		&__text--info--light,
+		&__text--info--light {
+			color: $u-info;
+		}
+
+		&__close {
+			position: absolute;
+			top: 11px;
+			right: 10px;
+		}
+	}
+</style>

+ 52 - 0
uview-ui/components/u-avatar-group/props.js

@@ -0,0 +1,52 @@
+export default {
+    props: {
+        // 头像图片组
+        urls: {
+            type: Array,
+            default: uni.$u.props.avatarGroup.urls
+        },
+        // 最多展示的头像数量
+        maxCount: {
+            type: [String, Number],
+            default: uni.$u.props.avatarGroup.maxCount
+        },
+        // 头像形状
+        shape: {
+            type: String,
+            default: uni.$u.props.avatarGroup.shape
+        },
+        // 图片裁剪模式
+        mode: {
+            type: String,
+            default: uni.$u.props.avatarGroup.mode
+        },
+        // 超出maxCount时是否显示查看更多的提示
+        showMore: {
+            type: Boolean,
+            default: uni.$u.props.avatarGroup.showMore
+        },
+        // 头像大小
+        size: {
+            type: [String, Number],
+            default: uni.$u.props.avatarGroup.size
+        },
+        // 指定从数组的对象元素中读取哪个属性作为图片地址
+        keyName: {
+            type: String,
+            default: uni.$u.props.avatarGroup.keyName
+        },
+		// 头像之间的遮挡比例
+        gap: {
+            type: [String, Number],
+            validator(value) {
+                return value >= 0 && value <= 1
+            },
+            default: uni.$u.props.avatarGroup.gap
+        },
+		// 需额外显示的值
+		extraValue: {
+			type: [Number, String],
+			default: uni.$u.props.avatarGroup.extraValue
+		}
+    }
+}

+ 103 - 0
uview-ui/components/u-avatar-group/u-avatar-group.vue

@@ -0,0 +1,103 @@
+<template>
+	<view class="u-avatar-group">
+		<view
+		    class="u-avatar-group__item"
+		    v-for="(item, index) in showUrl"
+		    :key="index"
+		    :style="{
+				marginLeft: index === 0 ? 0 : $u.addUnit(-size * gap)
+			}"
+		>
+			<u-avatar
+			    :size="size"
+			    :shape="shape"
+			    :mode="mode"
+			    :src="$u.test.object(item) ? keyName && item[keyName] || item.url : item"
+			></u-avatar>
+			<view
+			    class="u-avatar-group__item__show-more"
+			    v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
+				@tap="clickHandler"
+			>
+				<u--text
+				    color="#ffffff"
+				    :size="size * 0.4"
+				    :text="`+${extraValue || urls.length - showUrl.length}`"
+					align="center"
+					customStyle="justify-content: center"
+				></u--text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * AvatarGroup  头像组
+	 * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
+	 * @tutorial https://www.uviewui.com/components/avatar.html
+	 * 
+	 * @property {Array}           urls     头像图片组 (默认 [] )
+	 * @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 )
+	 * @property {String}          shape    头像形状( 'circle' (默认) | 'square' )
+	 * @property {String}          mode     图片裁剪模式(默认 'scaleToFill' )
+	 * @property {Boolean}         showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
+	 * @property {String | Number} size      头像大小 (默认 40 )
+	 * @property {String}          keyName  指定从数组的对象元素中读取哪个属性作为图片地址 
+	 * @property {String | Number} gap      头像之间的遮挡比例(0.4代表遮挡40%)  (默认 0.5 )
+	 * @property {String | Number} extraValue  需额外显示的值
+	 * @event    {Function}        showMore 头像组更多点击
+	 * @example  <u-avatar-group:urls="urls" size="35" gap="0.4" ></u-avatar-group:urls=>
+	 */
+	export default {
+		name: 'u-avatar-group',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+
+			}
+		},
+		computed: {
+			showUrl() {
+				return this.urls.slice(0, this.maxCount)
+			}
+		},
+		methods: {
+			clickHandler() {
+				this.$emit('showMore')
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-avatar-group {
+		@include flex;
+
+		&__item {
+			margin-left: -10px;
+			position: relative;
+
+			&--no-indent {
+				// 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持
+				margin-left: 0;
+			}
+
+			&__show-more {
+				position: absolute;
+				top: 0;
+				bottom: 0;
+				left: 0;
+				right: 0;
+				background-color: rgba(0, 0, 0, 0.3);
+				@include flex;
+				align-items: center;
+				justify-content: center;
+				border-radius: 100px;
+			}
+		}
+	}
+</style>

+ 78 - 0
uview-ui/components/u-avatar/props.js

@@ -0,0 +1,78 @@
+export default {
+    props: {
+        // 头像图片路径(不能为相对路径)
+        src: {
+            type: String,
+            default: uni.$u.props.avatar.src
+        },
+        // 头像形状,circle-圆形,square-方形
+        shape: {
+            type: String,
+            default: uni.$u.props.avatar.shape
+        },
+        // 头像尺寸
+        size: {
+            type: [String, Number],
+            default: uni.$u.props.avatar.size
+        },
+        // 裁剪模式
+        mode: {
+            type: String,
+            default: uni.$u.props.avatar.mode
+        },
+        // 显示的文字
+        text: {
+            type: String,
+            default: uni.$u.props.avatar.text
+        },
+        // 背景色
+        bgColor: {
+            type: String,
+            default: uni.$u.props.avatar.bgColor
+        },
+        // 文字颜色
+        color: {
+            type: String,
+            default: uni.$u.props.avatar.color
+        },
+        // 文字大小
+        fontSize: {
+            type: [String, Number],
+            default: uni.$u.props.avatar.fontSize
+        },
+        // 显示的图标
+        icon: {
+            type: String,
+            default: uni.$u.props.avatar.icon
+        },
+        // 显示小程序头像,只对百度,微信,QQ小程序有效
+        mpAvatar: {
+            type: Boolean,
+            default: uni.$u.props.avatar.mpAvatar
+        },
+        // 是否使用随机背景色
+        randomBgColor: {
+            type: Boolean,
+            default: uni.$u.props.avatar.randomBgColor
+        },
+        // 加载失败的默认头像(组件有内置默认图片)
+        defaultUrl: {
+            type: String,
+            default: uni.$u.props.avatar.defaultUrl
+        },
+        // 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
+        colorIndex: {
+            type: [String, Number],
+            // 校验参数规则,索引在0-19之间
+            validator(n) {
+                return uni.$u.test.range(n, [0, 19]) || n === ''
+            },
+            default: uni.$u.props.avatar.colorIndex
+        },
+        // 组件标识符
+        name: {
+            type: String,
+            default: uni.$u.props.avatar.name
+        }
+    }
+}

File diff suppressed because it is too large
+ 58 - 0
uview-ui/components/u-avatar/u-avatar.vue


+ 54 - 0
uview-ui/components/u-back-top/props.js

@@ -0,0 +1,54 @@
+export default {
+    props: {
+        // 返回顶部的形状,circle-圆形,square-方形
+        mode: {
+            type: String,
+            default: uni.$u.props.backtop.mode
+        },
+        // 自定义图标
+        icon: {
+            type: String,
+            default: uni.$u.props.backtop.icon
+        },
+        // 提示文字
+        text: {
+            type: String,
+            default: uni.$u.props.backtop.text
+        },
+        // 返回顶部滚动时间
+        duration: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.duration
+        },
+        // 滚动距离
+        scrollTop: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.scrollTop
+        },
+        // 距离顶部多少距离显示,单位px
+        top: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.top
+        },
+        // 返回顶部按钮到底部的距离,单位px
+        bottom: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.bottom
+        },
+        // 返回顶部按钮到右边的距离,单位px
+        right: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.right
+        },
+        // 层级
+        zIndex: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.zIndex
+        },
+        // 图标的样式,对象形式
+        iconStyle: {
+            type: Object,
+            default: uni.$u.props.backtop.iconStyle
+        }
+    }
+}

+ 129 - 0
uview-ui/components/u-back-top/u-back-top.vue

@@ -0,0 +1,129 @@
+<template>
+	<u-transition
+	    mode="fade"
+	    :customStyle="backTopStyle"
+	    :show="show"
+	>
+		<view
+		    class="u-back-top"
+			:style="[contentStyle]"
+		    v-if="!$slots.default && !$slots.$default"
+			@click="backToTop"
+		>
+			<u-icon
+			    :name="icon"
+			    :custom-style="iconStyle"
+			></u-icon>
+			<text
+			    v-if="text"
+			    class="u-back-top__text"
+			>{{text}}</text>
+		</view>
+		<slot v-else />
+	</u-transition>
+</template>
+
+<script>
+	import props from './props.js';
+	// #ifdef APP-NVUE
+	const dom = weex.requireModule('dom')
+	// #endif
+	/**
+	 * backTop 返回顶部
+	 * @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
+	 * @tutorial https://uviewui.com/components/backTop.html
+	 * 
+	 * @property {String}			mode  		返回顶部的形状,circle-圆形,square-方形 (默认 'circle' )
+	 * @property {String} 			icon 		自定义图标 (默认 'arrow-upward' ) 见官方文档示例
+	 * @property {String} 			text 		提示文字 
+	 * @property {String | Number}  duration	返回顶部滚动时间 (默认 100)
+	 * @property {String | Number}  scrollTop	滚动距离 (默认 0 )
+	 * @property {String | Number}  top  		距离顶部多少距离显示,单位px (默认 400 )
+	 * @property {String | Number}  bottom  	返回顶部按钮到底部的距离,单位px (默认 100 )
+	 * @property {String | Number}  right  		返回顶部按钮到右边的距离,单位px (默认 20 )
+	 * @property {String | Number}  zIndex 		层级   (默认 9 )
+	 * @property {Object<Object>}  	iconStyle 	图标的样式,对象形式   (默认 {color: '#909399',fontSize: '19px'})
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * 
+	 * @example <u-back-top :scrollTop="scrollTop"></u-back-top>
+	 */
+	export default {
+		name: 'u-back-top',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
+		computed: {
+			backTopStyle() {
+				// 动画组件样式
+				const style = {
+					bottom: uni.$u.addUnit(this.bottom),
+					right: uni.$u.addUnit(this.right),
+					width: '40px',
+					height: '40px',
+					position: 'fixed',
+					zIndex: 10,
+				}
+				return style
+			},
+			show() {
+				return uni.$u.getPx(this.scrollTop) > uni.$u.getPx(this.top)
+			},
+			contentStyle() {
+				const style = {}
+				let radius = 0
+				// 是否圆形
+				if(this.mode === 'circle') {
+					radius = '100px'
+				} else {
+					radius = '4px'
+				}
+				// 为了兼容安卓nvue,只能这么分开写
+				style.borderTopLeftRadius = radius
+				style.borderTopRightRadius = radius
+				style.borderBottomLeftRadius = radius
+				style.borderBottomRightRadius = radius
+				return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle))
+			}
+		},
+		methods: {
+			backToTop() {
+				// #ifdef APP-NVUE
+				if (!this.$parent.$refs['u-back-top']) {
+					uni.$u.error(`nvue页面需要给页面最外层元素设置"ref='u-back-top'`)
+				}
+				dom.scrollToElement(this.$parent.$refs['u-back-top'], {
+					offset: 0
+				})
+				// #endif
+				
+				// #ifndef APP-NVUE
+				uni.pageScrollTo({
+					scrollTop: 0,
+					duration: this.duration
+				});
+				// #endif
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '../../libs/css/components.scss';
+     $u-back-top-flex:1 !default;
+     $u-back-top-height:100% !default;
+     $u-back-top-background-color:#E1E1E1 !default;
+     $u-back-top-tips-font-size:12px !default;
+	.u-back-top {
+		@include flex;
+		flex-direction: column;
+		align-items: center;
+		flex:$u-back-top-flex;
+		height: $u-back-top-height;
+		justify-content: center;
+		background-color: $u-back-top-background-color;
+
+		&__tips {
+			font-size:$u-back-top-tips-font-size;
+			transform: scale(0.8);
+		}
+	}
+</style>

+ 72 - 0
uview-ui/components/u-badge/props.js

@@ -0,0 +1,72 @@
+export default {
+    props: {
+        // 是否显示圆点
+        isDot: {
+            type: Boolean,
+            default: uni.$u.props.badge.isDot
+        },
+        // 显示的内容
+        value: {
+            type: [Number, String],
+            default: uni.$u.props.badge.value
+        },
+        // 是否显示
+        show: {
+            type: Boolean,
+            default: uni.$u.props.badge.show
+        },
+        // 最大值,超过最大值会显示 '{max}+'
+        max: {
+            type: [Number, String],
+            default: uni.$u.props.badge.max
+        },
+        // 主题类型,error|warning|success|primary
+        type: {
+            type: String,
+            default: uni.$u.props.badge.type
+        },
+        // 当数值为 0 时,是否展示 Badge
+        showZero: {
+            type: Boolean,
+            default: uni.$u.props.badge.showZero
+        },
+        // 背景颜色,优先级比type高,如设置,type参数会失效
+        bgColor: {
+            type: [String, null],
+            default: uni.$u.props.badge.bgColor
+        },
+        // 字体颜色
+        color: {
+            type: [String, null],
+            default: uni.$u.props.badge.color
+        },
+        // 徽标形状,circle-四角均为圆角,horn-左下角为直角
+        shape: {
+            type: String,
+            default: uni.$u.props.badge.shape
+        },
+        // 设置数字的显示方式,overflow|ellipsis|limit
+        // overflow会根据max字段判断,超出显示`${max}+`
+        // ellipsis会根据max判断,超出显示`${max}...`
+        // limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数
+        numberType: {
+            type: String,
+            default: uni.$u.props.badge.numberType
+        },
+        // 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+        offset: {
+            type: Array,
+            default: uni.$u.props.badge.offset
+        },
+        // 是否反转背景和字体颜色
+        inverted: {
+            type: Boolean,
+            default: uni.$u.props.badge.inverted
+        },
+        // 是否绝对定位
+        absolute: {
+            type: Boolean,
+            default: uni.$u.props.badge.absolute
+        }
+    }
+}

+ 171 - 0
uview-ui/components/u-badge/u-badge.vue

@@ -0,0 +1,171 @@
+<template>
+	<text
+		v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)"
+		:class="[isDot ? 'u-badge--dot' : 'u-badge--not-dot', inverted && 'u-badge--inverted', shape === 'horn' && 'u-badge--horn', `u-badge--${type}${inverted ? '--inverted' : ''}`]"
+		:style="[$u.addStyle(customStyle), badgeStyle]"
+		class="u-badge"
+	>{{ isDot ? '' :showValue }}</text>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * badge 徽标数
+	 * @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
+	 * @tutorial https://uviewui.com/components/badge.html
+	 * 
+	 * @property {Boolean} 			isDot 		是否显示圆点 (默认 false )
+	 * @property {String | Number} 	value 		显示的内容
+	 * @property {Boolean} 			show 		是否显示 (默认 true )
+	 * @property {String | Number} 	max 		最大值,超过最大值会显示 '{max}+'  (默认999)
+	 * @property {String} 			type 		主题类型,error|warning|success|primary (默认 'error' )
+	 * @property {Boolean} 			showZero	当数值为 0 时,是否展示 Badge (默认 false )
+	 * @property {String} 			bgColor 	背景颜色,优先级比type高,如设置,type参数会失效
+	 * @property {String} 			color 		字体颜色 (默认 '#ffffff' )
+	 * @property {String} 			shape 		徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' )
+	 * @property {String} 			numberType	设置数字的显示方式,overflow|ellipsis|limit  (默认 'overflow' )
+	 * @property {Array}} 			offset		设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+	 * @property {Boolean} 			inverted	是否反转背景和字体颜色(默认 false )
+	 * @property {Boolean} 			absolute	是否绝对定位(默认 false )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @example <u-badge :type="type" :count="count"></u-badge>
+	 */
+	export default {
+		name: 'u-badge',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		computed: {
+			// 是否将badge中心与父组件右上角重合
+			boxStyle() {
+				let style = {};
+				return style;
+			},
+			// 整个组件的样式
+			badgeStyle() {
+				const style = {}
+				if(this.color) {
+					style.color = this.color
+				}
+				if (this.bgColor && !this.inverted) {
+					style.backgroundColor = this.bgColor
+				}
+				if (this.absolute) {
+					style.position = 'absolute'
+					// 如果有设置offset参数
+					if(this.offset.length) {
+						// top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top
+						const top = this.offset[0]
+						const right = this.offset[1] || top
+						style.top = uni.$u.addUnit(top)
+						style.right = uni.$u.addUnit(right)
+					}
+				}
+				return style
+			},
+			showValue() {
+				switch (this.numberType) {
+					case "overflow":
+						return Number(this.value) > Number(this.max) ? this.max + "+" : this.value
+						break;
+					case "ellipsis":
+						return Number(this.value) > Number(this.max) ? "..." : this.value
+						break;
+					case "limit":
+						return Number(this.value) > 999 ? Number(this.value) >= 9999 ?
+							Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value /
+								1e3 * 100) / 100 + "k" : this.value
+						break;
+					default:
+						return Number(this.value)
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	$u-badge-primary: $u-primary !default;
+	$u-badge-error: $u-error !default;
+	$u-badge-success: $u-success !default;
+	$u-badge-info: $u-info !default;
+	$u-badge-warning: $u-warning !default;
+	$u-badge-dot-radius: 100px !default;
+	$u-badge-dot-size: 8px !default;
+	$u-badge-dot-right: 4px !default;
+	$u-badge-dot-top: 0 !default;
+	$u-badge-text-font-size: 11px !default;
+	$u-badge-text-right: 10px !default;
+	$u-badge-text-padding: 2px 5px !default;
+	$u-badge-text-align: center !default;
+	$u-badge-text-color: #FFFFFF !default;
+
+	.u-badge {
+		border-top-right-radius: $u-badge-dot-radius;
+		border-top-left-radius: $u-badge-dot-radius;
+		border-bottom-left-radius: $u-badge-dot-radius;
+		border-bottom-right-radius: $u-badge-dot-radius;
+		@include flex;
+		line-height: $u-badge-text-font-size;
+		text-align: $u-badge-text-align;
+		font-size: $u-badge-text-font-size;
+		color: $u-badge-text-color;
+
+		&--dot {
+			height: $u-badge-dot-size;
+			width: $u-badge-dot-size;
+		}
+		
+		&--inverted {
+			font-size: 13px;
+		}
+		
+		&--not-dot {
+			padding: $u-badge-text-padding;
+		}
+
+		&--horn {
+			border-bottom-left-radius: 0;
+		}
+
+		&--primary {
+			background-color: $u-badge-primary;
+		}
+		
+		&--primary--inverted {
+			color: $u-badge-primary;
+		}
+
+		&--error {
+			background-color: $u-badge-error;
+		}
+		
+		&--error--inverted {
+			color: $u-badge-error;
+		}
+
+		&--success {
+			background-color: $u-badge-success;
+		}
+		
+		&--success--inverted {
+			color: $u-badge-success;
+		}
+
+		&--info {
+			background-color: $u-badge-info;
+		}
+		
+		&--info--inverted {
+			color: $u-badge-info;
+		}
+
+		&--warning {
+			background-color: $u-badge-warning;
+		}
+		
+		&--warning--inverted {
+			color: $u-badge-warning;
+		}
+	}
+</style>

+ 46 - 0
uview-ui/components/u-button/nvue.scss

@@ -0,0 +1,46 @@
+$u-button-active-opacity:0.75 !default;
+$u-button-loading-text-margin-left:4px !default;
+$u-button-text-color: #FFFFFF !default;
+$u-button-text-plain-error-color:$u-error !default;
+$u-button-text-plain-warning-color:$u-warning !default;
+$u-button-text-plain-success-color:$u-success !default;
+$u-button-text-plain-info-color:$u-info !default;
+$u-button-text-plain-primary-color:$u-primary !default;
+.u-button {
+	&--active {
+		opacity: $u-button-active-opacity;
+	}
+	
+	&--active--plain {
+		background-color: rgb(217, 217, 217);
+	}
+	
+	&__loading-text {
+		margin-left:$u-button-loading-text-margin-left;
+	}
+	
+	&__text,
+	&__loading-text {
+		color:$u-button-text-color;
+	}
+	
+	&__text--plain--error {
+		color:$u-button-text-plain-error-color;
+	}
+	
+	&__text--plain--warning {
+		color:$u-button-text-plain-warning-color;
+	}
+	
+	&__text--plain--success{
+		color:$u-button-text-plain-success-color;
+	}
+	
+	&__text--plain--info {
+		color:$u-button-text-plain-info-color;
+	}
+	
+	&__text--plain--primary {
+		color:$u-button-text-plain-primary-color;
+	}
+}

Some files were not shown because too many files changed in this diff