imengyu 19 hours ago
commit
deeb912a04
100 changed files with 28485 additions and 0 deletions
  1. 6 0
      .gitignore
  2. 175 0
      App.vue
  3. 69 0
      GraceUI5/components/gui-action-sheet.vue
  4. 163 0
      GraceUI5/components/gui-area-picker.vue
  5. 185 0
      GraceUI5/components/gui-article-info.vue
  6. 68 0
      GraceUI5/components/gui-article-list.vue
  7. 206 0
      GraceUI5/components/gui-audio-player.vue
  8. 108 0
      GraceUI5/components/gui-box-banner.vue
  9. 563 0
      GraceUI5/components/gui-calendar.js
  10. 203 0
      GraceUI5/components/gui-calendar.vue
  11. 197 0
      GraceUI5/components/gui-car-number.vue
  12. 102 0
      GraceUI5/components/gui-choose-images.vue
  13. 49 0
      GraceUI5/components/gui-column.vue
  14. 151 0
      GraceUI5/components/gui-count-down.vue
  15. 159 0
      GraceUI5/components/gui-count-up.vue
  16. 59 0
      GraceUI5/components/gui-coupons.vue
  17. 164 0
      GraceUI5/components/gui-date-between.vue
  18. 113 0
      GraceUI5/components/gui-datetime-between.vue
  19. 216 0
      GraceUI5/components/gui-datetime-bt-base.vue
  20. 262 0
      GraceUI5/components/gui-datetime.vue
  21. 26 0
      GraceUI5/components/gui-demo-banner.vue
  22. 22 0
      GraceUI5/components/gui-demo-desc.vue
  23. 12 0
      GraceUI5/components/gui-demo-header.vue
  24. 31 0
      GraceUI5/components/gui-demo-list.vue
  25. 236 0
      GraceUI5/components/gui-editor.vue
  26. 11 0
      GraceUI5/components/gui-empty.vue
  27. 61 0
      GraceUI5/components/gui-face-group.vue
  28. 76 0
      GraceUI5/components/gui-header-leading.vue
  29. 172 0
      GraceUI5/components/gui-im-input.vue
  30. 159 0
      GraceUI5/components/gui-im-message.vue
  31. 56 0
      GraceUI5/components/gui-image.vue
  32. 166 0
      GraceUI5/components/gui-interval-slider.vue
  33. 42 0
      GraceUI5/components/gui-iphone-bottom.vue
  34. 51 0
      GraceUI5/components/gui-link.vue
  35. 92 0
      GraceUI5/components/gui-loadmore.vue
  36. 60 0
      GraceUI5/components/gui-modal.vue
  37. 26 0
      GraceUI5/components/gui-mp-menu-spacing.vue
  38. 66 0
      GraceUI5/components/gui-number-animate.vue
  39. 120 0
      GraceUI5/components/gui-number-keyboard.vue
  40. 74 0
      GraceUI5/components/gui-order.vue
  41. 165 0
      GraceUI5/components/gui-page-loading.vue
  42. 430 0
      GraceUI5/components/gui-page.vue
  43. 92 0
      GraceUI5/components/gui-pk.vue
  44. 111 0
      GraceUI5/components/gui-popup-menu.vue
  45. 275 0
      GraceUI5/components/gui-popup.vue
  46. 45 0
      GraceUI5/components/gui-product-list.vue
  47. 117 0
      GraceUI5/components/gui-progress-scrollview.vue
  48. 83 0
      GraceUI5/components/gui-radio.vue
  49. 125 0
      GraceUI5/components/gui-refresh.vue
  50. 74 0
      GraceUI5/components/gui-right-menus.vue
  51. 56 0
      GraceUI5/components/gui-row.vue
  52. 213 0
      GraceUI5/components/gui-schedule.vue
  53. 70 0
      GraceUI5/components/gui-scroll-message.vue
  54. 113 0
      GraceUI5/components/gui-scrollitems-y.vue
  55. 102 0
      GraceUI5/components/gui-scrollitems.vue
  56. 101 0
      GraceUI5/components/gui-search.vue
  57. 47 0
      GraceUI5/components/gui-segmented-control.vue
  58. 103 0
      GraceUI5/components/gui-select-list.vue
  59. 225 0
      GraceUI5/components/gui-select-menu.vue
  60. 132 0
      GraceUI5/components/gui-single-slider.vue
  61. 36 0
      GraceUI5/components/gui-skeleton.vue
  62. 156 0
      GraceUI5/components/gui-slide-list.vue
  63. 95 0
      GraceUI5/components/gui-slide-to-unlock.vue
  64. 44 0
      GraceUI5/components/gui-speaker.vue
  65. 62 0
      GraceUI5/components/gui-spread.vue
  66. 115 0
      GraceUI5/components/gui-stags.vue
  67. 50 0
      GraceUI5/components/gui-star.vue
  68. 120 0
      GraceUI5/components/gui-step-box.vue
  69. 202 0
      GraceUI5/components/gui-submit-button.vue
  70. 111 0
      GraceUI5/components/gui-submit-comment.vue
  71. 175 0
      GraceUI5/components/gui-swiper.vue
  72. 157 0
      GraceUI5/components/gui-switch-navigation.vue
  73. 101 0
      GraceUI5/components/gui-switch-navigation2.vue
  74. 52 0
      GraceUI5/components/gui-tags.vue
  75. 96 0
      GraceUI5/components/gui-top-message.vue
  76. 61 0
      GraceUI5/components/gui-totop.vue
  77. 130 0
      GraceUI5/components/gui-touch.vue
  78. 138 0
      GraceUI5/components/gui-tree.vue
  79. 173 0
      GraceUI5/components/gui-turntable.vue
  80. 248 0
      GraceUI5/components/gui-upload-images.vue
  81. 1 0
      GraceUI5/css/animate.css
  82. 168 0
      GraceUI5/css/colors.css
  83. 1145 0
      GraceUI5/css/graceUI.css
  84. 6 0
      GraceUI5/css/iconsforbaidu.css
  85. 12553 0
      GraceUI5/data/city-data/area.js
  86. 434 0
      GraceUI5/data/city-data/city.js
  87. 38 0
      GraceUI5/data/city-data/province.js
  88. 742 0
      GraceUI5/data/cityData.js
  89. 106 0
      GraceUI5/demoData/article.js
  90. 100 0
      GraceUI5/demoData/cateChange.js
  91. 65 0
      GraceUI5/demoData/data.js
  92. 70 0
      GraceUI5/demoData/immessages.js
  93. 1265 0
      GraceUI5/js/WeCropper.js
  94. 410 0
      GraceUI5/js/barcode.js
  95. 86 0
      GraceUI5/js/checkIdCard.js
  96. 308 0
      GraceUI5/js/checker.js
  97. 573 0
      GraceUI5/js/grace.js
  98. 385 0
      GraceUI5/js/md5.js
  99. 252 0
      GraceUI5/js/parserHTML.js
  100. 0 0
      GraceUI5/js/qrcode.js

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+.DS_Store
+/dist
+/unpackage
+/unpackage/
+/node_modules/
+/.hbuilderx/

+ 175 - 0
App.vue

@@ -0,0 +1,175 @@
+<script>
+import md5Libs from '@/uni_modules/uview-ui/libs/function/md5';
+import Vue from 'vue';
+
+export default {
+	created() {
+		// #ifdef APP-PLUS
+		ime: '', plus.navigator.closeSplashscreen();
+		// #endif
+	},
+	onLaunch: async function () {
+		console.log('App Launch');
+		uni.hideTabBar();
+
+		// #ifdef APP-PLUS
+		// plus.android.requestPermissions(['Manifest.permission.READ_PHONE_STATE'], function(e){
+		// 		if(e.deniedAlways.length>0){	//权限被永久拒绝
+		// 			// 弹出提示框解释为何需要定位权限,引导用户打开设置页面开启
+		// 			console.log('Always Denied!!! '+e.deniedAlways.toString());
+		// 		}
+		// 		if(e.deniedPresent.length>0){	//权限被临时拒绝
+		// 			// 弹出提示框解释为何需要定位权限,可再次调用plus.android.requestPermissions申请权限
+		// 			console.log('Present Denied!!! '+e.deniedPresent.toString());
+		// 		}
+		// 		if(e.granted.length>0){	//权限被允许
+		// 		    //调用依赖获取定位权限的代码
+		// 			console.log('Granted!!! '+e.granted.toString());
+		// 		}
+		// 	}, function(e){
+		// 	    console.log('Request Permissions error:'+JSON.stringify(e));
+		// 	});
+		this.ime = plus.device.imei;
+		console.log(this.ime);
+
+		plus.device.getInfo({
+			success: function (e) {
+				this.ime = JSON.stringify(e);
+				console.log('getDeviceInfo success: ' + JSON.stringify(e));
+			},
+			fail: function (e) {
+				uni.showToast({
+					title: '获取设备信息失败',
+					mask: false,
+					duration: 1500
+				});
+			}
+		});
+
+		let that = this;
+		plus.runtime.getProperty(plus.runtime.appid, function (widgetInfo) {
+			console.log(widgetInfo);
+			uni.request({
+				url: 'https://mnhcdn.wenlvti.net/api/minnansoul/version/version',
+				sslVerify: false,
+				data: {
+					version: widgetInfo.version
+				},
+				success: (result) => {
+					console.log(result);
+					var data = result.data.data;
+					if (data.update && data.wgtUrl) {
+						uni.showModal({
+							title: '更新提示',
+							content: '有新的版本更新,请确认',
+							showCancel: false,
+							success(resmodal) {
+								if (resmodal.confirm) {
+									plus.runtime.openURL(data.wgtUrl);
+								}
+							}
+						});
+					}
+
+					// 只有apk包的时间
+					if (data.update && data.pkgUrl && !data.wgtUrl) {
+						uni.showModal({
+							title: '更新提示',
+							content: '有新的版本更新,请确认',
+							showCancel: false
+						});
+						var dtask = plus.downloader.createDownload(data.pkgUrl, {}, function (d, status) {
+							// 下载完成
+							if (status == 200) {
+								plus.runtime.install(plus.io.convertLocalFileSystemURL(d.filename), {}, {}, function (error) {
+									uni.showToast({
+										title: '安装失败',
+										mask: false,
+										duration: 1500
+									});
+								});
+							} else {
+								uni.showToast({
+									title: '下载失败',
+									mask: false,
+									duration: 1500
+								});
+							}
+						});
+						dtask.start();
+					}
+				},
+				fail(res) {
+					console.log(res, '错误');
+				}
+			});
+		});
+		// plus.screen.lockOrientation('portrait-primary'); //锁定屏幕
+		// const dom = weex.requireModule('dom');
+		// dom.addRule('fontFace', {
+		// 	'fontFamily': "graceIconfont",
+		// 	'src': "url('/static/grace.ttf')"
+		// });
+		// #endif
+
+		//加载配置
+		uni.getSystemInfo({
+			success: function (e) {
+				// #ifndef MP
+				Vue.prototype.StatusBar = e.statusBarHeight;
+				if (e.platform == 'android') {
+					Vue.prototype.CustomBar = e.statusBarHeight + 50;
+				} else {
+					Vue.prototype.CustomBar = e.statusBarHeight + 45;
+				}
+				// #endif
+
+				// #ifdef MP-WEIXIN
+				Vue.prototype.StatusBar = e.statusBarHeight;
+				let custom = wx.getMenuButtonBoundingClientRect();
+				Vue.prototype.Custom = custom;
+				Vue.prototype.CustomBar = custom.bottom + custom.top - e.statusBarHeight;
+				// #endif
+
+				// #ifdef MP-ALIPAY
+				Vue.prototype.StatusBar = e.statusBarHeight;
+				Vue.prototype.CustomBar = e.statusBarHeight + e.titleBarHeight;
+				// #endif
+			}
+		});
+	},
+	onReady() {
+		// plus.screen.lockOrientation( 'landscape-secondary');
+		this.plus.screen.lockOrientation('landscape-primary');
+	},
+
+	onShow: function () {
+		// console.log('App 开启');
+		uni.hideTabBar();
+	},
+	onHide: function () {
+		// console.log('App 关闭');
+	},
+	onLoad: function () {
+		uni.hideTabBar();
+	}
+};
+</script>
+
+<style lang="scss">
+@import '@/uni_modules/uview-ui/index.scss';
+// @import "common/iconfont.css";
+@import 'common/app.css';
+@import './GraceUI5/css/graceUI.css';
+@import './GraceUI5/skin/black.css';
+/* 加载图标字体 - 条件编译模式 */
+/* #ifdef APP-PLUS-NVUE */
+.gui-icons {
+	font-family: graceIconfont;
+}
+/* #endif */
+// body {
+// 	max-width: 3840rpx; //最大宽度自己可以调整
+// 	margin: auto !important;
+// }
+</style>

+ 69 - 0
GraceUI5/components/gui-action-sheet.vue

@@ -0,0 +1,69 @@
+<template>
+	<gui-popup ref="guipopupforacsheet" 
+	position="bottom" :canCloseByShade="canCloseByShade">
+		<view  
+		:style="{'margin-bottom':'25rpx','margin-left':((750-width)/2)+'rpx'}"
+		@tap.stop.prevent="stopfun">
+			<view class="gui-bg-white"
+			:style="{width:width+'rpx', height:(height+100)+'rpx', borderRadius:borderRadius}">
+				<text class="gui-action-sheet-title gui-block-text gui-border-b" 
+				:style="{color:titleColor}">{{title}}</text>
+				<scroll-view scroll-y="true" class="gui-border-box" 
+				:style="{width:width+'rpx', height:height+'rpx', paddingTop:'20rpx', paddingBottom:'20rpx'}">
+					<text class="gui-text-center gui-block-text gui-border-b" 
+					v-for="(item, index) in items" :key="index" 
+					@tap.stop="selected" :data-index="index" 
+					:style="{ color:listColor, lineHeight:listLineHeight, fontSize:listFontSize}">{{item}}</text>
+					<text class="gui-text-center gui-block-text" 
+					:style="{color:cancelBtnColor, lineHeight:listLineHeight, fontSize:listFontSize, marginTop:'10rpx'}" 
+					v-if="isCancelBtn" @tap.stop="cancel">{{cancelBtnName}}</text>
+				</scroll-view>
+			</view>
+			<!-- iphone 底部操作按钮躲避 -->
+			<gui-iphone-bottom></gui-iphone-bottom>
+		</view>
+	</gui-popup>
+</template>
+<script>
+export default{
+	name  : "gui-action-sheet",
+	props : {
+		width           : { type : Number,  default : 680},
+		height          : { type : Number,  default : 500},
+		borderRadius    : { type : String,  default : '10rpx'},
+		title           : { type : String,  default : ''},
+		titleColor      : { type : String,  default : '#2B2E3D'},
+		items           : { type : Array,   default : function(){return [];}},
+		listColor       : { type : String,  default : '#008AFF'},
+		listLineHeight  : { type : String,  default : '95rpx'},
+		listFontSize    : { type : String,  default : '28rpx'},
+		isCancelBtn     : { type : Boolean, default : true},
+		cancelBtnName   : { type : String,  default : '取消'},
+		cancelBtnColor  : { type : String,  default : 'rgba(69, 90, 100, 0.6)'},
+		canCloseByShade : { type : Boolean, default : false}
+	},
+	methods:{
+		open  : function(){
+			this.$refs.guipopupforacsheet.open();
+		},
+		close : function(){
+			this.$refs.guipopupforacsheet.close();
+		},
+		stopfun : function(e){
+			e.stopPropagation();
+			return null;
+		},
+		cancel : function () {
+			this.$emit('cancel');
+			this.close();
+		},
+		selected:function (e) {
+			this.$emit('selected', e.currentTarget.dataset.index);
+			this.close();
+		},
+	}
+}
+</script>
+<style scoped>
+.gui-action-sheet-title{text-align:center; font-size:26rpx; line-height:100rpx; height:100rpx; font-weight:bold;}
+</style>

+ 163 - 0
GraceUI5/components/gui-area-picker.vue

@@ -0,0 +1,163 @@
+<template>
+	<gui-popup 
+	ref="guipopupforareapicker" 
+	position="bottom">
+		<view 
+		class="gap-body gui-bg-white" 
+		@tap.stop.prevent="stopfun">
+			<view 
+			class="gap-header gui-flex gui-rows gui-space-between">
+				<text 
+				class="gap-header-btn gui-block-text" 
+				:class="[cancelClass]" 
+				@tap="close">{{cancelText}}</text>
+				<text 
+				class="gap-header-btn gui-block-text" 
+				:class="[confirmClass]" 
+				style="text-align:right;" 
+				@tap="confirm">{{confirmText}}</text>
+			</view>
+			<picker-view 
+			class="gap-main" 
+			:indicator-style="indicatorStyle" 
+			:value="defaultVal" 
+			@change="change">
+				<picker-view-column 
+				v-if="level >= 1">
+					<view 
+					class="gui-picker-item"
+					:style="indicatorStyle" 
+					v-for="(item, index) in province" 
+					:key="index">
+						<text 
+						:style="indicatorStyle" 
+						class="gui-block-text gui-picker-item">{{item.label}}</text>
+					</view>
+				</picker-view-column>
+				<picker-view-column 
+				v-if="level >= 2">
+					<view 
+					class="gui-picker-item"
+					:style="indicatorStyle" 
+					v-for="(item, index) in city[defaultVal[0]]" 
+					:key="index">
+						<text 
+						:style="indicatorStyle" 
+						class="gui-block-text gui-picker-item">{{item.label}}</text>
+					</view>
+				</picker-view-column>
+				<picker-view-column 
+				v-if="level >= 3">
+					<view 
+					class="gui-picker-item" 
+					:style="indicatorStyle" 
+					v-for="(item, index) in area[defaultVal[0]][defaultVal[1]]" 
+					:key="index">
+						<text 
+						:style="indicatorStyle" 
+						class="gui-block-text gui-picker-item">{{item.label}}</text>
+					</view>
+				</picker-view-column>
+			</picker-view>
+		</view>
+	</gui-popup>
+</template>
+<script>
+import provinceData from '../data/city-data/province.js';
+import cityData     from '../data/city-data/city.js';
+import areaData     from '../data/city-data/area.js';
+export default{
+	name  : "gui-area-picker",
+	props : {
+		cancelText     : { type : String, default : '取消' },
+		cancelClass    : { type : String, default : 'gui-color-gray' },
+		confirmText    : { type : String, default : '确定' },
+		confirmClass   : { type : String, default : 'gui-primary-color' },
+		value          : { type : Array , default () { return ['', '', ''] }},
+		level          : { type : Number, default : 3},
+		indicatorStyle : { type : String, default : 'height:36px; line-height:36px;'},
+	},
+	data() {
+		return {
+			province       : provinceData,
+			city           : cityData,
+			area           : areaData,
+			defaultVal     : [0,0,0],
+			realshow       : false
+		}
+	},
+	watch:{
+		value      : function(nv, ov){this.setDefault();},
+		defaultVal : function(nv, ov){
+			if(ov[0] != nv[0]){
+				this.defaultVal.splice(1,1,0);
+				this.defaultVal.splice(2,1,0);
+			}else if(ov[1] != nv[1]){
+				this.defaultVal.splice(2,1,0);
+			}
+		}
+	},
+	created() {
+		this.setDefault();
+	},
+	methods:{
+		setDefault:function(){
+			if(this.value[0] == ''){return ;}
+			this.$nextTick(() => {
+				this.defaultVal.splice(0, 1, this.arrayIndexOf(this.province, this.value[0]));
+				if(this.value[1] == ''){return ;}
+				this.$nextTick(() => {
+					this.defaultVal.splice(1,1, this.arrayIndexOf(this.city[this.defaultVal[0]], this.value[1]));
+					if(this.value[2] == ''){return ;}
+					this.$nextTick(() => {
+						this.defaultVal.splice(2,1, this.arrayIndexOf(this.area[this.defaultVal[0]][this.defaultVal[1]], this.value[2]));
+					})
+				})
+			});
+		},
+		arrayIndexOf : function(arr, needFind){
+			var index = 0;
+			for(let i = 0; i < arr.length; i++){if(arr[i].label == needFind){index = i; return i;}}
+			return index;
+		},
+		change : function (e) {
+			var res = e.detail.value;
+			if(!res[0]){res[0] = 0;}
+			if(!res[1]){res[1] = 0;}
+			if(!res[2]){res[2] = 0;}
+			this.defaultVal    = res;
+		},
+		confirm:function () {
+			var codes = [
+				provinceData[this.defaultVal[0]].value,
+				cityData[this.defaultVal[0]][this.defaultVal[1]].value,
+				areaData[this.defaultVal[0]][this.defaultVal[1]][this.defaultVal[2]].value ? areaData[this.defaultVal[0]][this.defaultVal[1]][this.defaultVal[2]].value : 0
+			];
+			var names = [
+				provinceData[this.defaultVal[0]].label,
+				cityData[this.defaultVal[0]][this.defaultVal[1]].label,
+				areaData[this.defaultVal[0]][this.defaultVal[1]][this.defaultVal[2]].label ? areaData[this.defaultVal[0]][this.defaultVal[1]][this.defaultVal[2]].label : ''
+			];
+			codes = codes.splice(0, this.level);
+			names = names.splice(0, this.level);
+			var res = {codes : codes, names : names, indexs : this.defaultVal};
+			this.$emit('confirm', res);
+			this.$refs.guipopupforareapicker.close();
+		},
+		open : function () {
+			this.$refs.guipopupforareapicker.open();
+		},
+		close : function () {
+			this.$refs.guipopupforareapicker.close();
+		},
+		stopfun : function(e){e.stopPropagation(); return null;}
+	}
+}
+</script>
+<style scoped>
+.gap-body{height:500rpx;}
+.gap-header{padding:25rpx;}
+.gap-header-btn{width:200rpx; line-height:38rpx; height:38rpx; font-size:28rpx;}
+.gap-main{width:750rpx; height:500rpx;}
+.gui-picker-item{overflow:hidden; font-size:26rpx; height:36px; line-height:36px; overflow:hidden; text-align:center;}
+</style>

+ 185 - 0
GraceUI5/components/gui-article-info.vue

@@ -0,0 +1,185 @@
+<template>
+	<view 
+	class="gui-editor-show gui-border-box" 
+	:style="{
+		paddingLeft:padding+'rpx', 
+		paddingRight:padding+'rpx', 
+		width:'750rpx'
+	}">
+		<view 
+		v-for="(item, index) in article" :key="index">
+		
+			<!-- 文本 -->
+			<text class="gui-block-text" 
+			:decode="true" 
+			:selectable="true" 
+			:user-select="true" 
+			:style="textStyle" 
+			v-if="item.type == 'txt'">{{item.content}}</text>
+			
+			<!-- 居中 -->
+			<text class="gui-block-text gui-text-center" 
+			:selectable="true" 
+			:user-select="true" 
+			:decode="true" 
+			:style="centerStyle" 
+			v-if="item.type == 'center'">{{item.content}}</text>
+			
+			<!-- 图片 -->
+			<view class="gui-img-in" 
+			v-else-if="item.type == 'img'" 
+			:data-url="item.content" @tap="showImgs">
+				<gui-image :src="item.content" :height="0" 
+				:borderRadius="imgRadius"
+				:width="(750-padding*2)"></gui-image>
+			</view>
+			
+			<!-- 引用 源码 -->
+			<text class="gui-block-text" 
+			:selectable="true" 
+			:user-select="true" 
+			:style="quoteStyle" 
+			:decode="true" 
+			v-else-if="
+			item.type == 'quote' || item.type == 'pre'
+			">{{item.content}}</text>
+			
+			<!-- 加粗 -->
+			<text class="gui-block-text gui-bold" 
+			:selectable="true" 
+			:user-select="true" 
+			:style="strongStyle" 
+			:decode="true" 
+			v-else-if="item.type == 'strong'">{{item.content}}</text>
+			
+			<!-- 链接 -->
+			<view v-else-if="item.type == 'link'">
+				<gui-link :url="item.content" :title="item.content"></gui-link>
+			</view>
+			
+			<!-- 分割符 -->
+			<text class="gui-block-text gui-text-center" 
+			:selectable="true" 
+			:user-select="true" 
+			:style="splineStyle" 
+			v-else-if="item.type == 'spline'">● ● ●</text>
+			
+			<!-- h1 -->
+			<text class="gui-block-text gui-h1" 
+			:decode="true" 
+			:selectable="true" 
+			:user-select="true" 
+			v-else-if="item.type == 'h1'">{{item.content}}</text>
+			
+			<!-- h2 -->
+			<text class="gui-block-text gui-h2" 
+			:selectable="true" 
+			:user-select="true" 
+			:decode="true" 
+			v-else-if="item.type == 'h2'">{{item.content}}</text>
+			
+			<!-- h3 -->
+			<text class="gui-block-text gui-h3" 
+			:selectable="true" 
+			:user-select="true" 
+			:decode="true" 
+			v-else-if="item.type == 'h3'">{{item.content}}</text>
+			
+			<!-- h4 -->
+			<text class="gui-block-text gui-h4" 
+			:selectable="true" 
+			:user-select="true" 
+			:decode="true" 
+			v-else-if="item.type == 'h4'">{{item.content}}</text>
+			
+			<!-- h5 -->
+			<text class="gui-block-text gui-h5" 
+			:selectable="true" 
+			:user-select="true" 
+			:decode="true" 
+			v-else-if="item.type == 'h5'">{{item.content}}</text>
+			
+			<!-- h6 -->
+			<text class="gui-block-text gui-h6" 
+			:selectable="true" 
+			:user-select="true" 
+			:decode="true" 
+			v-else-if="item.type == 'h6'">{{item.content}}</text>
+			
+			<!-- 视频 -->
+			<view v-if="item.type == 'video'">
+				<video 
+				:style="{width:(750-padding*2)+'rpx'}"
+				:src="item.content" 
+				controls></video>
+			</view>
+			
+			<!-- 间距 -->
+			<view :style="{height:itemMargin}"></view>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-article-info",
+	props : {
+		article      : {
+			type     : Array,  
+			default  : function(){return new Array();}
+		},
+		itemMargin   : {
+			type     : String, 
+			default  : '20rpx',
+		},
+		padding      : {
+			type     : Number, 
+			default  : 30,
+		},
+		textStyle    : {
+			type     : String, 
+			default  : 'line-height:58rpx; font-size:30rpx; color:#2B2E3D; text-align:justify;'
+		},
+		centerStyle  : {
+			type     : String, 
+			default  : 'line-height:58rpx; font-size:30rpx; color:#2B2E3D;'
+		},
+		imgRadius    : {
+			type     : String, 
+			default  : '6rpx',
+		},
+		quoteStyle   : {
+			type     : String, 
+			default  : 'line-height:58rpx; font-size:30rpx; color:#2B2E3D; padding:20rpx; background-color:#F8F8F8;'
+		},
+		strongStyle  : {
+			type     : String, 
+			default  : 'line-height:58rpx; font-size:30rpx; color:#2B2E3D;'
+		},
+		splineStyle  : {
+			type     : String, 
+			default  : 'line-height:58rpx; font-size:30rpx; color:rgba(69, 90, 100, 0.6);'
+		}
+	},
+	methods: {
+		showImgs:function(e){
+			var currentUrl = e.currentTarget.dataset.url;
+			var imgs       = [];
+			var items      = this.article;
+			for(let i = 0; i < items.length; i++){
+				if(items[i].type ==  'img'){
+					imgs.push(items[i].content);
+				}
+			}
+			uni.previewImage({
+				urls:imgs,
+				current:currentUrl
+			})
+		}
+	}
+}
+</script>
+<style scoped>
+/* #ifndef APP-NVUE */
+.gui-block-text{word-break: break-all;}
+/* #endif */
+</style>

+ 68 - 0
GraceUI5/components/gui-article-list.vue

@@ -0,0 +1,68 @@
+<template>
+	<view class="gui-article-list">
+		<view class="gui-article-list-item" hover-class="gui-tap" 
+		:style="itemStyle" 
+		v-for="(item, index) in articles" :key="index" 
+		@tap="newstap(item.id)">
+			<view>
+				<text class="gui-article-list-title gui-block-text" 
+				:style="titleStyle">{{item.title}}</text>
+			</view>
+			<view class="gui-article-list-img1" 
+			v-if="item.imgs.length == 1">
+				<gui-image :src="item.imgs[0]" :width="440" :height="280"></gui-image>
+			</view>
+			<view class="gui-flex gui-rows gui-nowrap gui-space-between" 
+			v-if="item.imgs.length == 2">
+				<view class="gui-article-list-img2-in">
+					<gui-image :src="item.imgs[0]" :width="335" :height="200"></gui-image>
+				</view>
+				<view class="gui-article-list-img2-in">
+					<gui-image :src="item.imgs[1]" :width="335" :height="200"></gui-image>
+				</view>
+			</view>
+			<view class="gui-flex gui-rows gui-nowrap gui-space-between" 
+			v-if="item.imgs.length >= 3">
+				<view class="gui-article-list-img3-in">
+					<gui-image :src="item.imgs[0]" :width="220" :height="150"></gui-image>
+				</view>
+				<view class="gui-article-list-img3-in">
+					<gui-image :src="item.imgs[1]" :width="220" :height="150"></gui-image>
+				</view>
+				<view class="gui-article-list-img3-in">
+					<gui-image :src="item.imgs[2]" :width="220" :height="150"></gui-image>
+				</view>
+			</view>
+			<view class="gui-article-list-footer gui-flex gui-rows gui-space-between gui-align-items-center">
+				<text class="gui-article-list-footer-items gui-ellipsis gui-color-gray gui-block-text gui-icons">&#xe69e; {{item.author}}</text>
+				<text class="gui-article-list-footer-items gui-ellipsis gui-color-gray gui-block-text gui-icons gui-text-center">&#xe609; {{item.views}}</text>
+				<text class="gui-article-list-footer-items gui-ellipsis gui-color-gray gui-block-text gui-icons gui-text-right">&#xe64c; {{item.createTime}}</text>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-article-list",
+	props : {
+		articles   : { type : Array , default : function(){return [];}},
+		titleStyle : { type : String, default : 'lineHeight:44rpx;font-size:32rpx;color:#2B2E3D;'},
+		itemStyle  : { type : String, default : 'background-color:#FFFFFF;'}
+	},
+	methods : {
+		newstap : function(id){
+			this.$emit('newstap', id);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-article-list{}
+.gui-article-list-item{margin-bottom:30rpx; padding:20rpx 30rpx;}
+.gui-article-list-title{overflow:hidden;}
+.gui-article-list-img1{margin-top:22rpx;}
+.gui-article-list-img2-in{width:335rpx; height:200rpx; margin-top:22rpx;}
+.gui-article-list-img3-in{width:220rpx; height:150rpx; margin-top:22rpx;}
+.gui-article-list-footer{margin-top:20rpx;}
+.gui-article-list-footer-items{width:220rpx; height:50rpx; line-height:50rpx; font-size:26rpx; overflow:hidden;}
+</style>

+ 206 - 0
GraceUI5/components/gui-audio-player.vue

@@ -0,0 +1,206 @@
+<template>
+	<view class="gui-player">
+		<view class="gui-player-title" 
+		:style="{color:color}">{{list[index].title}}</view>
+		<view class="gui-player-poster">
+			<image class="gui-player-poster-img" 
+			:class="[playStatus == 1 ? 'gui-playing' : '']" 
+			:src="list[index].coverImgUrl" mode="aspectFill"></image>
+		</view>
+		<!-- 播放进度 -->
+		<view style="padding:25rpx;">
+			<gui-single-slider ref="graceSingleSlider" 
+			@change="progressChange" :barWidth="100" 
+			:barText="playTime" barColor="#36395A" 
+			barBgColor="linear-gradient(to right, #FFFFFF,#FFFFFF)" 
+			bglineColor="rgba(255,255,255,0.5)" 
+			bglineAColor="#FFFFFF"></gui-single-slider>
+		</view>
+		<!-- 播放控制 -->
+		<view class="gui-player-console">
+			<!-- 循环模式 -->
+			<view class="gui-player-tool" 
+			@tap="changeType">
+				<text class="gui-player-tool gui-icons" 
+				v-if="dotype == 1">&#xe677;</text>
+				<text class="gui-player-tool gui-icons" 
+				v-if="dotype == 2" style="font-size:40rpx;">&#xe64a;</text>
+			</view>
+			<view class="gui-player-console-c">
+				<text class="gui-player-tool gui-icons" @tap="prev">&#xe659;</text>
+				<text class="gui-player-tool gui-icons" style="font-size:66rpx;" 
+				@tap="pause" v-if="playStatus == 1">&#xe64b;</text>
+				<text class="gui-player-tool gui-icons" style="font-size:66rpx;" 
+				@tap="playi" v-if="playStatus == 2">&#xe649;</text>
+				<text class="gui-player-tool gui-icons" @tap="next">&#xe65a;</text>
+			</view>
+			<text class="gui-player-tool gui-icons" @tap="openList">&#xe648;</text>
+		</view>
+		<!-- 歌曲列表 -->
+		<view class="gui-shade" :style="{left:listShow?'0':'-2000px'}" 
+		@tap.stop.parent="hideList" @touchmove.stop.parent="">
+			<scroll-view scroll-y="true" class="graceplayer-list" 
+			:style="{background:listBg, height:listHeight}">
+				<view class="graceplayer-list-item">歌曲列表</view>
+				<view class="graceplayer-list-item" 
+				:class="[index == idx ? 'graceplayer-list-item-this' : '']" 
+				v-for="(item, idx) in list" :key="idx" 
+				@tap="playList" :data-index="idx">{{item.title}}<text class="graceplayer-list-item-singer"> - {{item.singer}}</text></view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-audio-player",
+	props : {
+		color      : {type:String, default:'#FFFFFF'},
+		list       : {type:Array , default:function(){return [];}},
+		listBg     : {type:String, default:'#292E35'},
+		listHeight : {type:String, default:'880rpx'},
+		autoplay   : {type:Boolean, default:true}
+	},
+	data(){
+		return {
+			playStatus  : 1,
+			player      : null,
+			playTime    : '00:00',
+			timer       : null,
+			dotype      : 1,
+			index       : 0,
+			listShow    : false,
+			audioLength : 1
+		}
+	},
+	mounted:function(){
+		if(this.autoplay){
+			this.play();
+		}
+	},
+	created:function(){
+		// #ifdef H5
+		return ;
+		// #endif
+		this.player = uni.getBackgroundAudioManager();
+		this.player.onTimeUpdate(()=>{
+			try{
+				if(this.playStatus != 1){return ;}
+				this.audioLength = this.player.duration;
+				// 调整进度
+				var progress = this.player.currentTime / this.audioLength;
+				progress     = Math.round(progress * 100);
+				var ref      = this.$refs.graceSingleSlider;
+				if(ref){
+					ref.setProgress(progress);
+				}
+				this.playTime = this.timeFormat(this.player.currentTime);
+			}catch(e){};
+		});
+		this.player.onPlay(()=>{
+			this.playStatus  = 1;
+			this.audioLength = this.player.duration;
+		});
+		this.player.onPause(()=>{
+			this.playStatus = 2;
+		});
+		this.player.onEnded(()=>{
+			switch(this.dotype){
+				case 1 :
+				this.index++;
+				if(this.index + 1 > this.list.length){this.index = 0;}
+				this.play();
+				break;
+				case 2 :
+				this.player.seek(0);
+				this.play();
+				break;
+			}
+		});
+		if(this.player.currentTime < 1){
+			this.play();
+		}
+	},
+	methods:{
+		play:function () {
+			var audio               = this.list[this.index];
+			this.player.title       = audio.title;
+			this.player.singer      = audio.singer;
+			this.player.coverImgUrl = audio.coverImgUrl;
+			this.player.src         = audio.src;
+			this.player.play();
+		},
+		progressChange : function(e){
+			if(this.timer != null){ clearTimeout(this.timer); }
+			this.player.pause();
+			var needTime  = this.audioLength * e / 100;
+			needTime      = Math.round(needTime);
+			this.playTime = this.timeFormat(needTime);
+			this.timer = setTimeout(()=>{
+				this.player.seek(needTime);
+				this.player.play();
+			}, 800);
+		},
+		timeFormat : function (s){
+			s = Math.round(s);
+			if(s < 60){
+				if(s < 10){return '00:0'+s;}
+				return '00:'+s;
+			}else{
+				var second = s % 60;
+				s = s - second;
+				var minute = s / 60;
+				if(minute < 10){minute = '0'+minute;}
+				if(second < 10){second = '0'+second;}
+				return minute+':'+second;
+			}
+			
+		},
+		changeType : function () {
+			switch(this.dotype){
+				case 1 :
+				this.dotype = 2;
+				break;
+				case 2 :
+				this.dotype = 1;
+				break;
+			}
+		},
+		pause :function () {this.player.pause();},
+		playi :function () {this.player.play();},
+		next : function () {
+			if(this.index + 1 >= this.list.length){uni.showToast({title:"已经到底了 (:", icon:"none"}); return ;}
+			this.index++;
+			this.play();
+		},
+		prev : function () {
+			if(this.index -1 < 0){uni.showToast({title:"已经到头了 (:", icon:"none"}); return ;}
+			this.index--;
+			this.play();
+		},
+		openList : function () { this.listShow = true; },
+		hideList : function () { this.listShow = false; },
+		playList : function (e) {
+			var idx = e.currentTarget.dataset.index;
+			this.index = idx;
+			this.play();
+		},
+		setIndex : function (idx) { this.index = idx; }
+	}
+}
+</script>
+<style scoped>
+.gui-player{padding:30rpx;}
+.gui-player-title{text-align:center; line-height:50rpx; font-size:30rpx; font-weight:bold;}
+.gui-player-poster{padding:30px; line-height:0; text-align:center;}
+.gui-player-poster-img{width:380rpx; height:380rpx; border-radius:300rpx; box-shadow:0rpx 2rpx 2rpx #323232;}
+@keyframes gui-rotate360{0%{transform:rotate(0deg);} 50%{transform:rotate(180deg);} 100%{transform:rotate(360deg);}}
+.gui-playing{animation:gui-rotate360 6000ms infinite linear;}
+.gui-player-console{padding:20rpx 10rpx; display:flex; flex-direction:row; align-items:center; justify-content:space-between;}
+.gui-player-tool{width:100rpx; line-height:100rpx; text-align:center; font-size:50rpx; display:block; flex-shrink:0; color:#FFFFFF;}
+.gui-player-console-c{width:400rpx; display:flex; flex-direction:row; justify-content:space-between;}
+.graceplayer-list{width:100%; height:1000rpx; background-color:#FFFFFF; position:absolute; left:0; bottom:0; z-index:9999;}
+.gui-shade{position:fixed; width:100%; height:100%; left:0; top:0; background-color:rgba(0,0,0,0.5); bottom:0; overflow:hidden; z-index:9998; display:flex; justify-content:center; align-items:center;}
+.graceplayer-list-item{padding:25rpx 0rpx; margin:5rpx 30rpx; border-bottom:1px solid #373A3F; line-height:50rpx; font-size:30rpx; color:#FFFFFF;}
+.graceplayer-list-item-singer{color:#888888; font-size:26rpx; margin-left:50rpx;}
+.graceplayer-list-item-this{color:#64CDA5 !important; font-weight:bold;}
+</style>

+ 108 - 0
GraceUI5/components/gui-box-banner.vue

@@ -0,0 +1,108 @@
+<template>
+	<view 
+	class="grace-box-banner gui-flex gui-rows gui-nowrap" 
+	:style="{
+	backgroundColor:background, 
+	paddingTop:padding, 
+	paddingBottom:padding, 
+	borderRadius:borderRadius
+	}">
+	    <view 
+		class="grace-box-items gui-flex gui-rows gui-nowrap gui-align-items-center" 
+		hover-class="gui-tap" 
+		v-for="(item, index) in items" 
+		:key="index" 
+		@tap.stop="taped(index)">
+			<view 
+			class="gui-flex1">
+				<view
+				class="gui-flex gui-rows gui-nowrap gui-justify-content-center gui-align-items-center">
+					<text 
+					class="gui-block-text" 
+					:style="{
+						lineHeight:lineHeight, 
+						color:color[0],
+						fontSize:fontSize[0]
+					}">{{item[0]}}</text>
+					<text 
+					class="gui-block-text" 
+					:style="{
+						color:color[1], 
+						fontSize:fontSize[1], 
+						marginLeft:'5rpx'
+					}">{{item[1]}}</text>
+				</view>
+				<text 
+				class="gui-block-text gui-text-center" 
+				:style="{
+					color:color[2],
+					fontSize:fontSize[2]
+				}">{{item[2]}}</text>
+			</view>
+			<view 
+			class="grace-box-line" 
+			:style="{
+				'height'            : borderHeight,
+				'border-right-width': borderWidth,
+				'border-right-style': borderStyle,
+				'border-right-color': borderColor 
+			}"
+			v-if="index < items.length - 1"></view>
+	    </view>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-box-banner",
+	props : {
+		items:{
+			type : Array,
+			default : function () {
+				return []
+			}
+		},
+		color:{
+			type : Array,
+			default : function () {
+				return ['#333333', 'rgba(69, 90, 100, 0.5)', 'rgba(69, 90, 100, 0.5)']
+			}
+		},
+		fontSize:{
+			type : Array,
+			default : function () {
+				return ['36rpx', '24rpx', '24rpx']
+			}
+		},
+		background : {
+			type : String,
+			default : ''
+		},
+		padding:{
+			type : String,
+			default : '20rpx'
+		},
+		borderRadius:{
+			type : String,
+			default : '10rpx'
+		},
+		lineHeight:{
+			type : String,
+			default : '60rpx'
+		},
+		borderColor : {type:String, default:'#F1F1F1'},
+		borderWidth : {type:String, default:'1px'},
+		borderStyle : {type:String, default:'solid'},
+		borderHeight: {type:String, default:'60rpx'}
+	},
+	methods:{
+		taped:function (index) {
+			this.$emit('taped', index);
+		}
+	}
+}
+</script>
+<style scoped>
+.grace-box-banner{overflow:hidden;}
+.grace-box-items{width:100rpx; flex:1;}
+.grace-box-line{width:1px; height:50rpx;}
+</style>

+ 563 - 0
GraceUI5/components/gui-calendar.js

@@ -0,0 +1,563 @@
+/* eslint-disable */
+/**
+ * @公历转农历:this.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+ * @农历转公历:this.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+ */
+module.exports = {
+
+  /**
+   * 农历1900-2100的润大小信息表
+   * @Array Of Property
+   * @return Hex
+   */
+  lunarInfo:[0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,//1900-1909
+    0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,//1910-1919
+    0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,//1920-1929
+    0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,//1930-1939
+    0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,//1940-1949
+    0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,//1950-1959
+    0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,//1960-1969
+    0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,//1970-1979
+    0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,//1980-1989
+    0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,//1990-1999
+    0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,//2000-2009
+    0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,//2010-2019
+    0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,//2020-2029
+    0x05aa0,0x076a3,0x096d0,0x04afb,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,//2030-2039
+    0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,//2040-2049
+    /**Add By JJonline@JJonline.Cn**/
+    0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50, 0x06b20,0x1a6c4,0x0aae0,//2050-2059
+    0x0a2e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,//2060-2069
+    0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,//2070-2079
+    0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,//2080-2089
+    0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a2d0,0x0d150,0x0f252,//2090-2099
+    0x0d520],//2100
+
+  /**
+   * 公历每个月份的天数普通表
+   * @Array Of Property
+   * @return Number
+   */
+  solarMonth:[31,28,31,30,31,30,31,31,30,31,30,31],
+
+  /**
+   * 天干地支之天干速查表
+   * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+   * @return Cn string
+   */
+  Gan:["\u7532","\u4e59","\u4e19","\u4e01","\u620a","\u5df1","\u5e9a","\u8f9b","\u58ec","\u7678"],
+
+  /**
+   * 天干地支之地支速查表
+   * @Array Of Property
+   * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+   * @return Cn string
+   */
+  Zhi:["\u5b50","\u4e11","\u5bc5","\u536f","\u8fb0","\u5df3","\u5348","\u672a","\u7533","\u9149","\u620c","\u4ea5"],
+
+  /**
+   * 天干地支之地支速查表<=>生肖
+   * @Array Of Property
+   * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+   * @return Cn string
+   */
+  Animals:["\u9f20","\u725b","\u864e","\u5154","\u9f99","\u86c7","\u9a6c","\u7f8a","\u7334","\u9e21","\u72d7","\u732a"],
+
+  /**
+   * 24节气速查表
+   * @Array Of Property
+   * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+   * @return Cn string
+   */
+  solarTerm:["\u5c0f\u5bd2","\u5927\u5bd2","\u7acb\u6625","\u96e8\u6c34","\u60ca\u86f0","\u6625\u5206","\u6e05\u660e","\u8c37\u96e8","\u7acb\u590f","\u5c0f\u6ee1","\u8292\u79cd","\u590f\u81f3","\u5c0f\u6691","\u5927\u6691","\u7acb\u79cb","\u5904\u6691","\u767d\u9732","\u79cb\u5206","\u5bd2\u9732","\u971c\u964d","\u7acb\u51ac","\u5c0f\u96ea","\u5927\u96ea","\u51ac\u81f3"],
+
+  /**
+   * 1900-2100各年的24节气日期速查表
+   * @Array Of Property
+   * @return 0x string For splice
+   */
+  sTermInfo:['9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf97c3598082c95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f','b027097bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f','97bd0b06bdb0722c965ce1cfcc920f','b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f','97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa','9778397bd19801ec9210c965cc920e','97b6b97bd19801ec95f8c965cc920f',
+    '97bd09801d98082c95f8e1cfcc920f','97bd097bd097c36b0b6fc9210c8dc2','9778397bd197c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e','97bd09801d98082c95f8e1cfcc920f','97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec95f8c965cc920e','97bcf97c3598082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e','97bcf97c3598082c95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f','97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2','9778397bd19801ec9210c9274c920e','97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f','97bd07f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec9210c965cc920e','97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e','97bcf7f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e','97bcf7f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa','97b6b97bd197c36c9210c9274c920e','97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722','7f0e37f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa','97b6b7f0e47f531b0723b0b6fb0721','7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2','9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721','7f0e27f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa','97b6b7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9210c8dc2','977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722','7f0e37f5307f595b0b0bc920fb0722','7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0721','7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2','977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd','7f07e7f0e37f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722','7f0e37f1487f595b0b0bb0b6fb0722','7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0722','7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722','7f0e37f0e37f14898082b072297c35','7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722','7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722','7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd','7f07e7f0e37f14998083b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14898082b0723b02d5','7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722','7f0e36665b66aa89801e9808297c35','665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0722','7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35','665f67f0e37f14898082b072297c35','7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721','7f0e26665b66a449801e9808297c35','665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722'],
+
+  /**
+   * 数字转中文速查表
+   * @Array Of Property
+   * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+   * @return Cn string
+   */
+  nStr1:["\u65e5","\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341"],
+
+  /**
+   * 日期转农历称呼速查表
+   * @Array Of Property
+   * @trans ['初','十','廿','卅']
+   * @return Cn string
+   */
+  nStr2:["\u521d","\u5341","\u5eff","\u5345"],
+
+  /**
+   * 月份转农历称呼速查表
+   * @Array Of Property
+   * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+   * @return Cn string
+   */
+  nStr3:["\u6b63","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341","\u51ac","\u814a"],
+
+  /**
+   * 返回农历y年一整年的总天数
+   * @param lunar Year
+   * @return Number
+   * @eg:var count = this.lYearDays(1987) ;//count=387
+   */
+  lYearDays:function(y) {
+    var i, sum = 348;
+    for(i=0x8000; i>0x8; i>>=1) { sum += (this.lunarInfo[y-1900] & i)? 1: 0; }
+    return(sum+this.leapDays(y));
+  },
+
+  /**
+   * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+   * @param lunar Year
+   * @return Number (0-12)
+   * @eg:var leapMonth = this.leapMonth(1987) ;//leapMonth=6
+   */
+  leapMonth:function(y) { //闰字编码 \u95f0
+    return(this.lunarInfo[y-1900] & 0xf);
+  },
+
+  /**
+   * 返回农历y年闰月的天数 若该年没有闰月则返回0
+   * @param lunar Year
+   * @return Number (0、29、30)
+   * @eg:var leapMonthDay = this.leapDays(1987) ;//leapMonthDay=29
+   */
+  leapDays:function(y) {
+    if(this.leapMonth(y))  {
+      return((this.lunarInfo[y-1900] & 0x10000)? 30: 29);
+    }
+    return(0);
+  },
+
+  /**
+   * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+   * @param lunar Year
+   * @return Number (-1、29、30)
+   * @eg:var MonthDay = this.monthDays(1987,9) ;//MonthDay=29
+   */
+  monthDays:function(y,m) {
+    if(m>12 || m<1) {return -1}//月份参数从1至12,参数错误返回-1
+    return( (this.lunarInfo[y-1900] & (0x10000>>m))? 30: 29 );
+  },
+
+  /**
+   * 返回公历(!)y年m月的天数
+   * @param solar Year
+   * @return Number (-1、28、29、30、31)
+   * @eg:var solarMonthDay = this.leapDays(1987) ;//solarMonthDay=30
+   */
+  solarDays:function(y,m) {
+    if(m>12 || m<1) {return -1} //若参数错误 返回-1
+    var ms = m-1;
+    if(ms==1) { //2月份的闰平规律测算后确认返回28或29
+      return(((y%4 == 0) && (y%100 != 0) || (y%400 == 0))? 29: 28);
+    }else {
+      return(this.solarMonth[ms]);
+    }
+  },
+
+  /**
+   * 农历年份转换为干支纪年
+   * @param  lYear 农历年的年份数
+   * @return Cn string
+   */
+  toGanZhiYear:function(lYear) {
+    var ganKey = (lYear - 3) % 10;
+    var zhiKey = (lYear - 3) % 12;
+    if(ganKey == 0) ganKey = 10;//如果余数为0则为最后一个天干
+    if(zhiKey == 0) zhiKey = 12;//如果余数为0则为最后一个地支
+    return this.Gan[ganKey-1] + this.Zhi[zhiKey-1];
+
+  },
+
+  /**
+   * 公历月、日判断所属星座
+   * @param  cMonth [description]
+   * @param  cDay [description]
+   * @return Cn string
+   */
+  toAstro:function(cMonth,cDay) {
+    var s   = "\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf";
+    var arr = [20,19,21,21,21,22,23,23,23,23,22,22];
+    return s.substr(cMonth*2 - (cDay < arr[cMonth-1] ? 2 : 0),2) + "\u5ea7";//座
+  },
+
+  /**
+   * 传入offset偏移量返回干支
+   * @param offset 相对甲子的偏移量
+   * @return Cn string
+   */
+  toGanZhi:function(offset) {
+    return this.Gan[offset%10] + this.Zhi[offset%12];
+  },
+
+  /**
+   * 传入公历(!)y年获得该年第n个节气的公历日期
+   * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+   * @return day Number
+   * @eg:var _24 = this.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+   */
+  getTerm:function(y,n) {
+    if(y<1900 || y>2100) {return -1;}
+    if(n<1 || n>24) {return -1;}
+    var _table = this.sTermInfo[y-1900];
+    var _info = [
+      parseInt('0x'+_table.substr(0,5)).toString() ,
+      parseInt('0x'+_table.substr(5,5)).toString(),
+      parseInt('0x'+_table.substr(10,5)).toString(),
+      parseInt('0x'+_table.substr(15,5)).toString(),
+      parseInt('0x'+_table.substr(20,5)).toString(),
+      parseInt('0x'+_table.substr(25,5)).toString()
+    ];
+    var _calday = [
+      _info[0].substr(0,1),
+      _info[0].substr(1,2),
+      _info[0].substr(3,1),
+      _info[0].substr(4,2),
+
+      _info[1].substr(0,1),
+      _info[1].substr(1,2),
+      _info[1].substr(3,1),
+      _info[1].substr(4,2),
+
+      _info[2].substr(0,1),
+      _info[2].substr(1,2),
+      _info[2].substr(3,1),
+      _info[2].substr(4,2),
+
+      _info[3].substr(0,1),
+      _info[3].substr(1,2),
+      _info[3].substr(3,1),
+      _info[3].substr(4,2),
+
+      _info[4].substr(0,1),
+      _info[4].substr(1,2),
+      _info[4].substr(3,1),
+      _info[4].substr(4,2),
+
+      _info[5].substr(0,1),
+      _info[5].substr(1,2),
+      _info[5].substr(3,1),
+      _info[5].substr(4,2),
+    ];
+    return parseInt(_calday[n-1]);
+  },
+
+  /**
+   * 传入农历数字月份返回汉语通俗表示法
+   * @param lunar month
+   * @return Cn string
+   * @eg:var cnMonth = this.toChinaMonth(12) ;//cnMonth='腊月'
+   */
+  toChinaMonth:function(m) { // 月 => \u6708
+    if(m>12 || m<1) {return -1} //若参数错误 返回-1
+    var s = this.nStr3[m-1];
+    s+= "\u6708";//加上月字
+    return s;
+  },
+
+  /**
+   * 传入农历日期数字返回汉字表示法
+   * @param lunar day
+   * @return Cn string
+   * @eg:var cnDay = this.toChinaDay(21) ;//cnMonth='廿一'
+   */
+  toChinaDay:function(d){ //日 => \u65e5
+    var s;
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'; break;
+      case 20:
+        s = '\u4e8c\u5341'; break;
+        break;
+      case 30:
+        s = '\u4e09\u5341'; break;
+        break;
+      default :
+        s = this.nStr2[Math.floor(d/10)];
+        s += this.nStr1[d%10];
+    }
+    return(s);
+  },
+
+  /**
+   * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+   * @param y year
+   * @return Cn string
+   * @eg:var animal = this.getAnimal(1987) ;//animal='兔'
+   */
+  getAnimal: function(y) {
+    return this.Animals[(y - 4) % 12]
+  },
+
+  /**
+   * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+   * @param y  solar year
+   * @param m  solar month
+   * @param d  solar day
+   * @return JSON object
+   * @eg:console.log(this.solar2lunar(1987,11,01));
+   */
+  solar2lunar:function (y,m,d) { //参数区间1900.1.31~2100.12.31
+    if(y<1900 || y>2100) {return -1;}//年份限定、上限
+    if(y==1900&&m==1&&d<31) {return -1;}//下限
+    if(!y) { //未传参  获得当天
+      var objDate = new Date();
+    }else {
+      var objDate = new Date(y,parseInt(m)-1,d)
+    }
+    var i, leap=0, temp=0;
+    // 修正ymd参数
+    // var y = objDate.getFullYear(),m = objDate.getMonth()+1,d = objDate.getDate();
+    var offset   = (Date.UTC(objDate.getFullYear(),objDate.getMonth(),objDate.getDate()) - Date.UTC(1900,0,31))/86400000;
+    for(i=1900; i<2101 && offset>0; i++) { temp=this.lYearDays(i); offset-=temp; }
+    if(offset<0) { offset+=temp; i--; }
+
+    //是否今天
+    var isTodayObj = new Date(),isToday=false;
+    if(isTodayObj.getFullYear()==y && isTodayObj.getMonth()+1==m && isTodayObj.getDate()==d) {
+      isToday = true;
+    }
+    //星期几
+    var nWeek = objDate.getDay(),cWeek = this.nStr1[nWeek];
+    if(nWeek==0) {nWeek =7;}//数字表示周几顺应天朝周一开始的惯例
+    //农历年
+    var year = i;
+
+    var leap = this.leapMonth(i); //闰哪个月
+    var isLeap = false;
+
+    //效验闰月
+    for(i=1; i<13 && offset>0; i++) {
+      //闰月
+      if(leap>0 && i==(leap+1) && isLeap==false){
+        --i;
+        isLeap = true; temp = this.leapDays(year); //计算农历闰月天数
+      }
+      else{
+        temp = this.monthDays(year, i);//计算农历普通月天数
+      }
+      //解除闰月
+      if(isLeap==true && i==(leap+1)) { isLeap = false; }
+      offset -= temp;
+    }
+
+    if(offset==0 && leap>0 && i==leap+1)
+      if(isLeap){
+        isLeap = false;
+      }else{
+        isLeap = true; --i;
+      }
+    if(offset<0){ offset += temp; --i; }
+    //农历月
+    var month   = i;
+    //农历日
+    var day     = offset + 1;
+
+    //天干地支处理
+    var sm      =   m-1;
+    var gzY     =   this.toGanZhiYear(year);
+
+    //月柱 1900年1月小寒以前为 丙子月(60进制12)
+    var firstNode   = this.getTerm(year,(m*2-1));//返回当月「节」为几日开始
+    var secondNode  = this.getTerm(year,(m*2));//返回当月「节」为几日开始
+
+    //依据12节气修正干支月
+    var gzM     =   this.toGanZhi((y-1900)*12+m+11);
+    if(d>=firstNode) {
+      gzM     =   this.toGanZhi((y-1900)*12+m+12);
+    }
+
+    //传入的日期的节气与否
+    var isTerm = false;
+    var Term   = null;
+    if(firstNode==d) {
+      isTerm  = true;
+      Term    = this.solarTerm[m*2-2];
+    }
+    if(secondNode==d) {
+      isTerm  = true;
+      Term    = this.solarTerm[m*2-1];
+    }
+    //日柱 当月一日与 1900/1/1 相差天数
+    var dayCyclical = Date.UTC(y,sm,1,0,0,0,0)/86400000+25567+10;
+    var gzD = this.toGanZhi(dayCyclical+d-1);
+    //该日期所属的星座
+    var astro = this.toAstro(m,d);
+
+    return {'lYear':year,'lMonth':month,'lDay':day,'Animal':this.getAnimal(year),'IMonthCn':(isLeap?"\u95f0":'')+this.toChinaMonth(month),'IDayCn':this.toChinaDay(day),'cYear':y,'cMonth':m,'cDay':d,'gzYear':gzY,'gzMonth':gzM,'gzDay':gzD,'isToday':isToday,'isLeap':isLeap,'nWeek':nWeek,'ncWeek':"\u661f\u671f"+cWeek,'isTerm':isTerm,'Term':Term,'astro':astro};
+  },
+
+  /**
+   * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+   * @param y  lunar year
+   * @param m  lunar month
+   * @param d  lunar day
+   * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+   * @return JSON object
+   * @eg:console.log(this.lunar2solar(1987,9,10));
+   */
+	lunar2solar:function(y,m,d,isLeapMonth) {
+		//参数区间1900.1.31~2100.12.1
+		var isLeapMonth = !!isLeapMonth;
+		var leapOffset  = 0;
+		var leapMonth   = this.leapMonth(y);
+		var leapDay     = this.leapDays(y);
+		if(isLeapMonth&&(leapMonth!=m)) {return -1;}//传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+		if(y==2100&&m==12&&d>1 || y==1900&&m==1&&d<31) {return -1;}//超出了最大极限值
+		var day  = this.monthDays(y,m);
+		var _day = day;
+		//bugFix 2016-9-25
+		//if month is leap, _day use leapDays method
+		if(isLeapMonth) {
+		  _day = this.leapDays(y,m);
+		}
+		if(y < 1900 || y > 2100 || d > _day) {return -1;}//参数合法性效验
+
+		//计算农历的时间差
+		var offset = 0;
+		for(var i=1900;i<y;i++) {
+		  offset+=this.lYearDays(i);
+		}
+		var leap = 0,isAdd= false;
+		for(var i=1;i<m;i++) {
+		  leap = this.leapMonth(y);
+		  if(!isAdd) {//处理闰月
+			if(leap<=i && leap>0) {
+			  offset+=this.leapDays(y);isAdd = true;
+			}
+		  }
+		  offset+=this.monthDays(y,i);
+		}
+		//转换闰月农历 需补充该年闰月的前一个月的时差
+		if(isLeapMonth) {offset+=day;}
+		//1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+		var stmap   =   Date.UTC(1900,1,30,0,0,0);
+		var calObj  =   new Date((offset+d-31)*86400000+stmap);
+		var cY      =   calObj.getUTCFullYear();
+		var cM      =   calObj.getUTCMonth()+1;
+		var cD      =   calObj.getUTCDate();
+
+		return this.solar2lunar(cY,cM,cD);
+	},
+	
+	defaultLunar : {
+	  '1y1': '春节',
+	  '1y15': '元宵节',
+	  '2y2': '龙头节',
+	  '5y5': '端午节',
+	  '7y7': '七夕节',
+	  '7y15': '中元节',
+	  '8y15': '中秋节',
+	  '9y9': '重阳节',
+	  '10y1': '寒衣节',
+	  '10y15': '下元节',
+	  '12y8': '腊八节',
+	  '12y23': '小年',
+	},
+	
+	defaultGregorian : {
+	  '1y1': '元旦',
+	  '2y14': '情人节',
+	  '3y8': '妇女节',
+	  '3y12': '植树节',
+	  '5y1': '劳动节',
+	  '5y4': '青年节',
+	  '6y1': '儿童节',
+	  '7y1': '建党节',
+	  '8y1': '建军节',
+	  '9y10': '教师节',
+	  '10y1': '国庆节',
+	  '12y24': '平安夜',
+	  '12y25': '圣诞节',
+	},
+	
+	getLunarInfo : function(y, m, d) {
+		// 匹配阳历节日
+		var glDay  = m+'y'+d;
+		if(this.defaultGregorian[glDay]){
+			return this.defaultGregorian[glDay];
+		}
+		var nlObj = this.solar2lunar(y, m, d);
+		// 阴历节日
+		var nlDay = nlObj.lMonth+'y'+nlObj.lDay;
+		if(this.defaultLunar[nlDay]){
+			return this.defaultLunar[nlDay]
+		}
+		return nlObj.IDayCn;
+	}
+}

+ 203 - 0
GraceUI5/components/gui-calendar.vue

@@ -0,0 +1,203 @@
+<template>
+<gui-popup 
+	ref="guipopupforcalendar" 
+	position="bottom" 
+	:canCloseByShade="false" 
+	:zIndex="zIndex">
+	<view class="gui-calendar gui-bg-white gui-box-shadow" 
+	@tap.stop.prevent="stopfun">
+		<!-- 顶部日期及切换 -->
+		<view class="gui-calendar-header gui-flex gui-rows gui-nowrap gui-justify-content-center gui-align-items-center">
+			<text class="gui-calendar-header-btn gui-icons" 
+			@click="prevYear">&#xe600;&#xe600;</text>
+			<text class="gui-calendar-header-btn gui-icons" 
+			@click="prevMonth">&#xe600;</text>
+			<text class="gui-calendar-header-btn gui-icons" 
+			style="margin:20rpx;">{{cYear}} 年 {{cMonth}} 月</text>
+			<text class="gui-calendar-header-btn gui-icons" 
+			@click="nextMonth">&#xe601;</text>
+			<text class="gui-calendar-header-btn gui-icons" 
+			@click="nextYear">&#xe601;&#xe601;</text>
+		</view>
+		<!-- 星期 -->
+		<view class="gui-flex gui-rows gui-nowrap gui-justify-content-center gui-align-items-center">
+			<text class="gui-calendar-weeks gui-block-text" v-for="(item, index) in weeks" :key="index">{{item}}</text>
+		</view>
+		<!-- 日历列表 -->
+		<view class="gui-bg-gray gui-flex gui-rows gui-wrap gui-calendar-days">
+			<view class="gui-calendar-ditems gui-flex gui-columns gui-justify-content-center gui-align-items-center"
+			:style="{
+				backgroundColor: currentDayIn == cYear+'-'+cMonthStr+'-'+ item.date ? activeBgColor : bgColor, 
+			borderRadius:borderRadius}" 
+			@click="chooseDate(cYear+'-'+cMonthStr+'-'+item.date, item.date)" 
+			v-for="(item, index) in days" :key="index">
+				<text class="gui-date-day" 
+				:style="{color : currentDayIn == (cYear+'-'+cMonthStr+'-'+item.date) ? '#FFFFFF' : 'rgba(69, 90, 100, 0.6)'}">{{item.date}}</text>
+				<text class="gui-date-nl" v-if="isLunar" 
+				:style="{color : currentDayIn == (cYear+'-'+cMonthStr+'-'+item.date) ? '#FFFFFF' : 'rgba(69, 90, 100, 0.6)'}">{{item.nl}}</text>
+			</view>
+		</view>
+		<!-- 时间选择 -->
+		<view 
+		class="gui-flex gui-rows gui-justify-content-center gui-align-items-center gui-bg-gray " 
+		v-if="isTime">
+			<picker mode="time" @change="timechange" :value="currentTimeIn">
+				<text 
+				class="gui-date-time gui-border-b gui-border-t gui-block-text" 
+				style="border-color:#D1D1D1;" >时间 : {{currentTimeIn}}</text>
+			</picker>
+		</view>
+		<!-- 按钮 -->
+		<view class="gui-flex gui-rows gui-space-between gui-align-items-center">
+			<view class="gui-date-btns-text" hover-class="gui-tap" @tap="close">
+				<text class="gui-date-btns-text gui-block-text gui-color-gray">取消</text>
+			</view>
+			<view class="gui-date-btns-text" hover-class="gui-tap" @tap="submit">
+				<text class="gui-date-btns-text gui-block-text gui-primary-color">确认</text>
+			</view>
+		</view>
+	</view>
+</gui-popup>
+</template>
+<script>
+import guiCalendar from './gui-calendar.js';
+export default {
+	name  : "gui-calendar", 
+	props : {
+		currentDate   : { type : String,  default : "" },
+		isTime        : { type : Boolean, default : true },
+		bgColor       : {type  : String,  default : "#F7F8FA"},
+		activeBgColor : {type  : String,  default : "#008AFF"},
+		borderRadius  : {type  : String,  default : "6rpx"},
+		isLunar       : {type  : Boolean, default : true },
+		zIndex        : {type  : Number,  default : 2}
+	},
+	data(){
+		return {
+			weeks         : ['一', '二', '三', '四', '五', '六', '日'],
+			cYear         : 2016,
+			cMonth        : 6,
+			cMonthStr     : "06",
+			cDay          : "01",
+			days          : '',
+			currentDayIn  : '',
+			currentTimeIn : ''
+		}
+	},
+	created:function(){
+		this.initTime();
+	},
+	watch:{
+		currentDate  : function(){this.initTime();},
+	},
+	methods: {
+		stopfun:function(e){e.stopPropagation(); return ;},
+		timechange : function(e){
+			this.currentTimeIn = e.detail.value;
+		},
+		getDaysInOneMonth : function (){
+			var d = new Date(this.cYear, this.cMonth, 0);
+			return d.getDate();
+		},
+		getDay : function (){
+			var d = new Date(this.cYear, this.cMonth - 1, 0);
+			return d.getDay();
+		},
+		prevYear : function(){
+			this.cYear = this.cYear - 1;
+			this.changeMonth();
+		},
+		prevMonth : function(){
+			this.cMonth =  this.cMonth - 1;
+			if (this.cMonth < 1) { this.cMonth = 12; this.cYear = this.cYear - 1; }
+			this.cMonthStr = this.cMonth < 10 ? '0' + this.cMonth : this.cMonth;
+			this.changeMonth();
+		},
+		nextMonth : function(){
+			this.cMonth = this.cMonth + 1;
+			if (this.cMonth > 12){this.cMonth = 1; this.cYear = this.cYear + 1;}
+			this.cMonthStr = this.cMonth < 10 ? '0' + this.cMonth : this.cMonth;
+			this.changeMonth();
+		},
+		nextYear : function(){
+			this.cYear = this.cYear + 1;
+			this.changeMonth();
+		},
+		changeMonth:function(){
+			var daysList  = [];
+			var days      = this.getDaysInOneMonth();
+			var startWeek = this.getDay();
+			var forSteps  = 0;
+			for (var i = (0 - startWeek); i < days; i++){
+				if(i >= 0){
+					daysList[forSteps] = {date : i >= 9 ? i + 1 : '0' + (i+1), nl : ''};
+					daysList[forSteps].nl = guiCalendar.getLunarInfo(this.cYear, this.cMonth, i + 1);
+				}else{
+					daysList[forSteps] = '';
+				}
+				forSteps++;
+			}
+			this.days    = daysList;
+		},
+		chooseDate: function (sedDate, isday) {
+			if(!isday){return ;}
+			this.currentDayIn = sedDate;
+			if(this.isTime){return ;}
+			this.$emit('changeDate', sedDate);
+		},
+		submit : function(){
+			if(this.isTime){
+				this.$emit('changeDate', this.currentDayIn + ' ' + this.currentTimeIn);
+				this.$emit('confirm'   , this.currentDayIn + ' ' + this.currentTimeIn);
+			}else{
+				this.$emit('changeDate', this.currentDayIn);
+				this.$emit('confirm'   , this.currentDayIn);
+			}
+			this.close();
+		},
+		//初始化时间
+		initTime : function(){
+			if(this.currentDate == ''){
+				var dateObj        = new Date();
+				this.cYear         = dateObj.getFullYear();
+				this.cMonth        = dateObj.getMonth() + 1;
+				this.cMonthStr     = this.cMonth < 10 ? '0' + this.cMonth : this.cMonth;
+				this.cDay          = dateObj.getDate();
+				this.cDay          = this.cDay < 10 ? '0' + this.cDay : this.cDay;
+				this.currentDayIn  = this.cYear + '-' + this.cMonthStr + '-' + this.cDay;
+				this.currentTimeIn = '00:00';
+				this.changeMonth();
+			}else{
+				var dates          = this.currentDate.split(' ');
+				if (!dates[1]) { dates[1] = '';}
+				var dayArr         = dates[0].split('-');
+				this.cYear         = Number(dayArr[0]);
+				this.cMonth        = dayArr[1];
+				this.cDay          = dayArr[2];
+				var reg            = new RegExp('^0[0-9]+$');
+				if(reg.test(this.cMonth)){this.cMonth = this.cMonth.substr(1,1);}
+				this.cMonth        = Number(this.cMonth);
+				this.cMonthStr     = this.cMonth < 10 ? '0'+this.cMonth : this.cMonth;
+				this.currentDayIn  = dates[0];
+				this.currentTimeIn = dates[1];
+				this.changeMonth();
+			}
+		},
+		open:function(){this.$refs.guipopupforcalendar.open();},
+		close:function(){this.$refs.guipopupforcalendar.close();}
+	}
+}
+</script>
+<style scoped>
+.gui-calendar{border-top-left-radius:10rpx; border-top-right-radius:10rpx; padding:10rpx 0;}
+.gui-calendar-header{height:80rpx;}
+.gui-calendar-header-btn{font-size:32rpx; padding:0 10rpx; color:rgba(69, 90, 100, 0.6);}
+.gui-calendar-weeks{width:100rpx; color:#2B2E3D; height:80rpx; text-align:center; font-size:30rpx; line-height:80rpx;}
+.gui-calendar-days{padding:25rpx;}
+.gui-calendar-ditems{width:96rpx; height:96rpx; margin:2rpx;}
+
+.gui-date-day{height:38rpx; line-height:38rpx; text-align:center; font-size:32rpx;}
+.gui-date-nl{height:26rpx; line-height:26rpx; font-size:20rpx; text-align:center;}
+.gui-date-btns-text{line-height:100rpx; font-size:28rpx; text-align:center; width:300rpx;}
+.gui-date-time{font-size:28rpx; line-height:80rpx; height:80rpx; margin-bottom:30rpx;}
+</style>

+ 197 - 0
GraceUI5/components/gui-car-number.vue

@@ -0,0 +1,197 @@
+<template>
+	<gui-popup 
+	ref="guipopupforcarnumber" 
+	width="750rpx" 
+	position="bottom" 
+	@close="close" 
+	:canCloseByShade="true">
+		<view 
+		@tap.stop.prevent="stopfun" 
+		class="gui-bg-white">
+			<view 
+			class="gui-flex gui-rows gui-space-between gui-align-items-center"
+			style="padding:20rpx;">
+				<!-- 类型 -->
+				<view style="width:320rpx;">
+				<gui-segmented-control 
+				:items="['普通车牌','新能源牌']" 
+				:current="carType" 
+				@change="changeType"></gui-segmented-control>
+				</view>
+				<text class="gui-car-number-submit gui-block-text gui-color-blue" 
+				@tap="confirm">确定</text>
+			</view>
+			<!-- 车牌展示 -->
+			<view 
+			class="gui-flex gui-rows gui-nowrap gui-justify-content-center" 
+			style="padding-top:20rpx; padding-bottom:20rpx;">
+				<text class="gui-car-number-item gui-block-text" 
+				:class="[
+					idx == inputIndex ? 'gui-bg-blue' : '',
+					idx == inputIndex ? 'gui-a-shade' : ''
+				]" 
+				v-for="(item, idx) in carNumberArray" :key="idx" 
+				@tap="numberTap(idx)">{{item}}</text>
+			</view>
+			<!-- 错误信息 -->
+			<view 
+			v-if="errorshow" 
+			style="padding:20rpx; margin-bottom:10rpx;">
+				<text class="gui-text gui-block-text gui-color-red gui-text-center">请输入完整车牌号码</text>
+			</view>
+			<view class="gui-bg-gray" 
+			style="padding:15rpx;">
+				<!-- 省份前缀键盘 -->
+				<view class="gui-flex gui-rows gui-wrap" 
+				v-if="inputIndex == 0">
+					<view class="gui-car-number-key" 
+					hover-class="gui-bg-blue" 
+					:hover-stay-time="50" 
+					v-for="(item, idx) in provinces" 
+					:key="idx" 
+					@tap="provinceTap(item)">
+						<text class="gui-car-number-key-txt gui-block-text">{{item}}</text>
+					</view>
+				</view>
+				<!-- 号码键盘 -->
+				<view class="gui-flex gui-rows gui-wrap gui-space-between" 
+				v-else>
+					<view class="gui-car-number-key" 
+					hover-class="gui-kd-tap" 
+					:hover-stay-time="50" 
+					v-for="(item, idx) in keyWords" 
+					:key="idx" 
+					@tap="keyTap(item)">
+						<text class="gui-car-number-key-txt gui-block-text">{{item}}</text>
+					</view>
+					<view class="gui-car-number-key"
+					hover-class="gui-kd-tap" 
+					@tap="deleteNumber">
+						<text class="gui-car-number-key-txt gui-block-text gui-icons">&#xe623;</text>
+					</view>
+				</view>
+			</view>
+			<gui-iphone-bottom></gui-iphone-bottom>
+		</view>
+	</gui-popup>
+</template>
+<script>
+export default{
+	name  : "gui-car-number",
+	props : {},
+	data() {
+		return {
+			carNumber      : '',
+			carNumberArray : [],
+			carType        : 0,
+			inputIndex     : 0,
+			provinces      : ['京','津','沪','渝','冀','豫','云',
+							  '辽','黑','湘','皖','鲁','新','苏',
+							  '浙','赣','鄂','桂','甘','晋','蒙',
+							  '陕','吉','闽','贵','粤','青','藏',
+							  '川','宁','琼','使','领','新'
+							],
+			keyWords       : ['1','2','3','4','5','6','7','8','9','0',
+							  'A','B','C','D','E','F','G','H',
+							  'J','K','L','M','N','P','Q','R',
+							  'S','T','U','V','W','X','Y','Z',
+							  '港','澳','学','领','警'],
+			errorshow      : false
+		}
+	},
+	mounted:function(){
+		this.showNumber();
+	},
+	methods:{
+		showError  : function () {
+			this.errorshow = true;
+			setTimeout(()=>{this.errorshow = false},2000)
+		},
+		confirm    : function () {
+			let carNumberLength = this.carType == 0 ? 7 : 8;
+			if(this.carNumber.length < carNumberLength){
+				this.showError();
+				return ;
+			}
+			this.$emit('confirm', this.carNumber);
+			this.$refs.guipopupforcarnumber.close();
+		},
+		numberTap  : function (idx) {
+			this.inputIndex = idx
+		},
+		provinceTap : function(key){
+			this.carNumberArray[0] = key;
+			this.carNumber = this.carNumberArray.join('');
+			this.showNumber();
+		},
+		keyTap     : function(key){
+			let carNumberLength = this.carType == 0 ? 7 : 8;
+			if(this.inputIndex >= carNumberLength){return ;}
+			this.carNumberArray[this.inputIndex] = key;
+			this.carNumber = this.carNumberArray.join('');
+			this.showNumber();
+		},
+		showNumber : function(){
+			let carNumberArray     = this.carNumber.split('');
+			let carNumberLength    = this.carType == 0 ? 7 : 8;
+			let carNumberArrLength = carNumberArray.length;
+			this.carNumberArray    = [];
+			var inputIndex        = 0;
+			for(let i = 0; i < carNumberLength; i++){
+				if(carNumberArrLength > i){
+					this.carNumberArray[i] = carNumberArray[i];
+					inputIndex++;
+				}else{
+					this.carNumberArray[i] = '';
+				}
+			}
+			this.carNumber  = this.carNumberArray.join('');
+			this.inputIndex = inputIndex;
+		},
+		changeType : function(e){
+			this.carType = e;
+			this.showNumber();
+		},
+		setType : function(carType){
+			this.carType   = carType;
+			this.showNumber();
+		},
+		setVal  : function(carNumber){
+			this.carNumber = carNumber;
+			this.showNumber();
+		},
+		deleteNumber : function(){
+			if(this.inputIndex < 0){return ;}
+			this.carNumberArray.splice((this.inputIndex -1), 1, '');
+			this.carNumber = this.carNumberArray.join('');
+			this.showNumber();
+		},
+		open : function(){
+			this.$refs.guipopupforcarnumber.open();
+			this.$emit('open');
+		},
+		stopfun : function(e){
+			e.stopPropagation();
+			return ;
+		},
+		close : function(){
+			this.$emit('close');
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-car-number-item{width:70rpx; height:70rpx; font-size:30rpx; line-height:70rpx; text-align:center; border:1px solid #CECECE; border-radius:6rpx; margin-right:15rpx; font-weight:700;}
+.gui-car-number-key{width:76rpx; height:76rpx; border:1px solid #CECECE; border-radius:6rpx; margin:5rpx;}
+.gui-car-number-key-txt{font-size:30rpx; line-height:76rpx; text-align:center;}
+.gui-car-number-submit{width:100rpx; line-height:60rpx; text-align:center; font-size:30rpx;}
+.gui-bg-blue{color:#FFFFFF; border-color: #008AFF;}
+/* #ifndef APP-NVUE */
+@keyframes gui-a-shade{0%{opacity:1;} 25%{opacity:0.6;} 50%{opacity:0.6;} 75%{opacity:1;} 100%{opacity:1;}}
+.gui-a-shade{animation:gui-a-shade 1.5s ease infinite;}
+.gui-kd-tap{color:#FFFFFF; background-color:#008AFF; border-color:#008AFF;}
+/* #endif */
+/* #ifdef APP-NVUE */
+.gui-kd-tap{color:#FFFFFF; background-color:#CECECE; border-color:#CECECE;}
+/* #endif */
+</style>

+ 102 - 0
GraceUI5/components/gui-choose-images.vue

@@ -0,0 +1,102 @@
+<template>
+	<view class="gui-flex gui-rows gui-wrap">
+		<view class="gui-add-list-items" :style="{borderRadius:borderRadius}" 
+		v-for="(item, index) in imgLists" :key="index">
+			<image :src="item.url" :data-imgurl="item.url" 
+			:style="{borderRadius:borderRadius}" 
+			@tap="showImgs" class="gui-add-list-img" :mode="imgMode"></image>
+			<text class="gui-add-list-remove gui-icons" 
+			:style="{color:removeBtnColor}" @tap="removeImg" 
+			:id="'gui-items-img-'+index">&#xe632;</text>
+		</view>
+		<view class="gui-add-list-items gui-flex gui-columns gui-justify-content-center gui-align-items-center" 
+		@tap="addImg" v-if="imgLists.length < maxFileNumber" 
+		:style="{borderRadius:borderRadius}">
+			<text class="gui-add-list-btn-icon gui-block-text gui-color-gray">+</text>
+			<text class="gui-add-list-btn-text gui-block-text gui-color-gray">{{btnName}}</text>
+		</view>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-choose-images",
+	props : {
+		maxFileNumber  : { type : Number, default : 9 },
+		btnName        : { type : String, default : "添加照片" },
+		items          : { type : Array,  default : function () {return []; }},
+		removeBtnColor : { type : String, default : "rgba(0, 0, 0, 0.8)" },
+		imgMode        : { type : String, default : 'widthFix' },
+		sourceType     : { type : Array,  default : function () {return ['album', 'camera'];}},
+		borderRadius   : { type : String, default : "10rpx" }
+	},
+	data() {
+		return {
+			imgLists : []
+		}
+	},
+	created:function () {
+		this.initImgs();
+	},
+	watch:{
+		items:function(){ this.initImgs(); }
+	},
+    methods:{
+		initImgs : function(){
+			var imgs = [];
+			for(let i = 0; i < this.items.length; i++){
+				imgs.push({url:this.items[i],  progress : 100});
+			}
+			this.imgLists = imgs;
+		},
+        addImg : function(){
+            var num = this.maxFileNumber - this.imgLists.length;
+            if(num < 1){return false;}
+            uni.chooseImage({
+                count      : num,
+                sizeType   : ['compressed'],
+				sourceType : this.sourceType,
+                success    : (res) => {
+					if(this.imgLists.length >= this.maxFileNumber){return ;}
+					for(let i = 0; i < res.tempFilePaths.length; i++){
+						if(this.imgLists.length >= this.maxFileNumber){break;}
+						this.imgLists.push({url:res.tempFilePaths[i], progress:0});
+					}
+                    this.$emit('change', this.imgLists);
+                },
+				complete   : function(){}
+            });
+        },
+        removeImg : function(e){
+            var index = e.currentTarget.id.replace('gui-items-img-', '');
+			var removeImg =  this.imgLists.splice(index, 1);
+			this.$emit('removeImg', removeImg[0]);
+			this.$emit('change'   , this.imgLists);
+        },
+        showImgs : function(e){
+            var currentImg = e.currentTarget.dataset.imgurl;
+			var imgs = [];
+			for(let i = 0; i < this.imgLists.length; i++){
+				imgs.push(this.imgLists[i].url);
+			}
+            uni.previewImage({
+              urls: imgs,
+              current : currentImg
+            })
+        },
+		setItems : function(items){
+			this.imgLists = [];
+			for(let i = 0; i < items.length; i++){
+				this.imgLists.push({url : items[i], progress : 100});
+			}
+			this.$emit('change', this.imgLists);
+		}
+    }
+}
+</script>
+<style scoped>
+.gui-add-list-btn-text{font-size:26rpx; line-height:36rpx; text-align:center;}
+.gui-add-list-btn-icon{font-size:80rpx; height:80rpx; line-height:80rpx; margin-bottom:20rpx;}
+.gui-add-list-items{width:212rpx; height:212rpx; overflow:hidden; margin:9rpx; background-color:#F8F8F8; font-size:0; position:relative;}
+.gui-add-list-remove{width:60rpx; height:60rpx; line-height:60rpx; text-align:center; font-size:44rpx; position:absolute; z-index:1; right:0; bottom:0;}
+.gui-add-list-img{width:212rpx;}
+</style>

+ 49 - 0
GraceUI5/components/gui-column.vue

@@ -0,0 +1,49 @@
+<template>
+	<view 
+	:class="classesRuntime" 
+	:style="styleRuntime">
+		<slot></slot>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-column",
+	props : {
+		mainAxisAlignment  : {type:String, default:'flex-start'},
+		crossAxisAlignment : {type:String, default:'flex-start'},
+		customStyle        : {type:String, default:''},
+		customClasses      : {type:Array,  default:function(){return [];}}
+	},
+	data() {
+		return {
+			styleRuntime : '', 
+			classesRuntime : []
+		}
+	},
+	methods:{
+		makeStyle : function(){
+			var styleRuntime = 'justify-content:'+this.mainAxisAlignment+'; ';
+			styleRuntime += 'align-items:'+this.crossAxisAlignment+'; ';
+			styleRuntime += this.customStyle;
+			this.styleRuntime = styleRuntime;
+		},
+		makeClasses : function(){
+			var classes = ['gui-flex', 'gui-column'];
+			classes = classes.concat(this.customClasses);
+			this.classesRuntime = classes;
+		}
+	},
+	mounted:function(){
+		this.makeStyle();
+		this.makeClasses();
+	},
+	watch:{
+		mainAxisAlignment  : function(){this.makeStyle();},
+		crossAxisAlignment : function(){this.makeStyle();},
+		customStyle        : function(){this.makeStyle();},
+		customClasses        : function(){this.makeClasses();}
+	}
+}
+</script>
+<style scoped>
+</style>

+ 151 - 0
GraceUI5/components/gui-count-down.vue

@@ -0,0 +1,151 @@
+<template name="gui-count-down">
+	<view class="gui-flex gui-rows gui-nowrap gui-align-items-center" 
+	v-if="show && timer != ''">
+		<text class="gui-countdown-numbers gui-border" 
+		:style="{
+		borderColor:borderColor, 
+		width:size*lineHeight+'rpx', marginRight:spacing, 
+		height:size*lineHeight+'rpx', lineHeight:size*lineHeight+'rpx', fontSize:size+'rpx', 
+		color:fontColor, backgroundColor:bgColor}" 
+		v-if="d > 0">{{d}}</text>
+		
+		<text class="gui-countdown-splitor" 
+		:style="{color:splitorColor, fontSize:size+'rpx', marginRight:spacing}" 
+		v-if="d > 0">{{splitorText[0]}}</text>
+		
+		<text class="gui-countdown-numbers gui-border" 
+		:style="{
+		borderColor:borderColor, 
+		width:size*lineHeight+'rpx', marginRight:spacing, 
+		height:size*lineHeight+'rpx', lineHeight:size*lineHeight+'rpx', fontSize:size+'rpx', 
+		color:fontColor, backgroundColor:bgColor}" 
+		v-if="(h != '00' || zeroShow)">{{h}}</text>
+		
+		<text class="gui-countdown-splitor" 
+		:style="{color:splitorColor, fontSize:size+'rpx', marginRight:spacing}" 
+		v-if="(h != '00' || zeroShow)">{{splitorText[1]}}</text>
+		
+		<text class="gui-countdown-numbers gui-border" 
+		:style="{
+		borderColor:borderColor, 
+		width:size*lineHeight+'rpx', marginRight:spacing, 
+		height:size*lineHeight+'rpx', lineHeight:size*lineHeight+'rpx', fontSize:size+'rpx', 
+		color:fontColor, backgroundColor:bgColor}">{{i}}</text>
+		
+		<text class="gui-countdown-splitor" 
+		:style="{color:splitorColor, fontSize:size+'rpx', marginRight:spacing}">{{splitorText[2]}}</text>
+		
+		<text class="gui-countdown-numbers gui-border" 
+		:style="{
+		borderColor:borderColor, 
+		width:size*lineHeight+'rpx', marginRight:spacing, 
+		height:size*lineHeight+'rpx', lineHeight:size*lineHeight+'rpx', fontSize:size+'rpx', 
+		color:fontColor, backgroundColor:bgColor}">{{s}}</text>
+		
+		<text class="gui-countdown-splitor" 
+		:style="{color:splitorColor, fontSize:size+'rpx'}">{{splitorText[3]}}</text>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-count-down",
+	props : {
+		bgColor       : { type: String, default : "#FFFFFF" },
+		borderColor   : { type:String,  default : "#FFFFFF"},
+		fontColor     : { type: String, default : "#2B2E3D" },
+		size          : { type: Number, default : 26},
+		lineHeight    : { type: Number, default : 1.8},
+		splitorColor  : { type: String, default : "rgba(69, 90, 100, 0.6)" },
+		timer         : { type:String,  default : "" },
+		splitorText   : { type : Array,
+			default : function () {
+				return ['天', '时', '分', '秒']
+			}
+		},
+		show          : {type:Boolean, default:true},
+		zeroShow      : {type:Boolean, default:true},
+		spacing       : {type:String, default:'0rpx'}
+	},
+	data() {
+		return {
+			d           : 0,
+			h           : "",
+			i           : "",
+			s           : "",
+			leftTime    : 0,
+			outTimer    : null,
+			timerIn     : '',
+			leftTimeNum : 0
+		}
+	},
+	created : function(){
+		this.timerIn = this.timer;
+		this.runbase();
+	},
+	watch   : {
+		timer : function(){
+			this.timerIn = this.timer;
+			this.runbase();
+		}
+	},
+	methods : {
+		runbase : function(){
+			var reg = /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/;
+			var res = this.timerIn.match(reg);
+			if (res == null){ return false;}
+			var year = parseInt(res[1]);
+			if (year < 1000) { return false; }
+			var month = parseInt(res[2]);
+			var day = parseInt(res[3]);
+			var h = parseInt(res[4]);
+			if (h < 0 || h > 24) { return false; }
+			var i = parseInt(res[5]);
+			if (i < 0 || i > 60) { return false; }
+			var s = parseInt(res[6]);
+			if (s < 0 || s > 60) { return false; }
+			var leftTime = new Date(year, month - 1, day, h, i, s);
+			this.leftTime = leftTime;
+			clearTimeout(this.outTimer);
+			this.countDown();
+		},
+		countDown: function (){
+			var leftTime     = this.leftTime - new Date();
+			this.leftTimeNum = leftTime;
+			if (leftTime > 0) {
+				var day     = parseInt(leftTime  / (1000 * 60 * 60 * 24));
+				var hours   = parseInt((leftTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
+				var minutes = parseInt((leftTime % (1000 * 60 * 60)) / (1000 * 60));
+				var seconds = parseInt((leftTime % (1000 * 60)) / 1000);
+				if (hours   < 10) { hours   = '0' + hours;}
+				if (minutes < 10) { minutes = '0' + minutes; }
+				if (seconds < 10) { seconds = '0' + seconds; }
+				this.h = hours; 
+				this.i = minutes; 
+				this.s = seconds; 
+				this.d = day;
+				this.outTimer = setTimeout(()=>{this.countDown();}, 1000);
+			}else{
+				clearTimeout(this.outTimer);
+				this.h = '00'; 
+				this.i = '00'; 
+				this.s = '00'; 
+				this.d = 0;
+				this.$emit('endDo');
+			}
+		},
+		reSetTimer : function(timer){
+			clearTimeout(this.outTimer);
+			this.timerIn = timer;
+			this.runbase();
+		},
+		getTimeRemaining : function(){
+			if(this.leftTimeNum < 0){return 0;}
+			return parseInt(this.leftTimeNum / 1000);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-countdown-splitor{padding:0 5rpx;}
+.gui-countdown-numbers{border-radius:8rpx; text-align:center;}
+</style>

+ 159 - 0
GraceUI5/components/gui-count-up.vue

@@ -0,0 +1,159 @@
+<template name="gui-count-down">
+	<view class="gui-flex gui-rows gui-nowrap gui-align-items-center" 
+	v-if="show && timer != ''">
+		<text class="gui-countdown-numbers gui-border" 
+		:style="{
+		borderColor:borderColor, 
+		width:size*lineHeight+'rpx', marginRight:spacing, 
+		height:size*lineHeight+'rpx', lineHeight:size*lineHeight+'rpx', fontSize:size+'rpx', 
+		color:fontColor, backgroundColor:bgColor}" 
+		v-if="d > 0">{{d}}</text>
+		
+		<text class="gui-countdown-splitor" 
+		:style="{color:splitorColor, fontSize:size+'rpx', marginRight:spacing}" 
+		v-if="d > 0">{{splitorText[0]}}</text>
+		
+		<text class="gui-countdown-numbers gui-border" 
+		:style="{
+		borderColor:borderColor, 
+		width:size*lineHeight+'rpx', marginRight:spacing, 
+		height:size*lineHeight+'rpx', lineHeight:size*lineHeight+'rpx', fontSize:size+'rpx', 
+		color:fontColor, backgroundColor:bgColor}" 
+		v-if="(h != '00' || zeroShow)">{{h}}</text>
+		
+		<text class="gui-countdown-splitor" 
+		:style="{color:splitorColor, fontSize:size+'rpx', marginRight:spacing}" 
+		v-if="(h != '00' || zeroShow)">{{splitorText[1]}}</text>
+		
+		<text class="gui-countdown-numbers gui-border" 
+		:style="{
+		borderColor:borderColor, 
+		width:size*lineHeight+'rpx', marginRight:spacing, 
+		height:size*lineHeight+'rpx', lineHeight:size*lineHeight+'rpx', fontSize:size+'rpx', 
+		color:fontColor, backgroundColor:bgColor}">{{i}}</text>
+		
+		<text class="gui-countdown-splitor" 
+		:style="{color:splitorColor, fontSize:size+'rpx', marginRight:spacing}">{{splitorText[2]}}</text>
+		
+		<text class="gui-countdown-numbers gui-border" 
+		:style="{
+		borderColor:borderColor, 
+		width:size*lineHeight+'rpx', marginRight:spacing, 
+		height:size*lineHeight+'rpx', lineHeight:size*lineHeight+'rpx', fontSize:size+'rpx', 
+		color:fontColor, backgroundColor:bgColor}">{{s}}</text>
+		
+		<text class="gui-countdown-splitor" 
+		:style="{color:splitorColor, fontSize:size+'rpx'}">{{splitorText[3]}}</text>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-count-up",
+	props : {
+		bgColor       : { type: String, default : "#FFFFFF" },
+		borderColor   : { type:String,  default : "#FFFFFF"},
+		fontColor     : { type: String, default : "#2B2E3D" },
+		size          : { type: Number, default : 26},
+		lineHeight    : { type: Number, default : 1.8},
+		splitorColor  : { type: String, default : "rgba(69, 90, 100, 0.6)" },
+		timer         : { type:String,  default : "" },
+		splitorText   : { type : Array,
+			default : function () {
+				return ['天', '时', '分', '秒']
+			}
+		},
+		show          : {type:Boolean, default:true},
+		zeroShow      : {type:Boolean, default:true},
+		spacing       : {type:String, default:'0rpx'},
+		start         : {type:Boolean, default:true},
+	},
+	data() {
+		return {
+			d           : 0,
+			h           : "",
+			i           : "",
+			s           : "",
+			leftTime    : 0,
+			outTimer    : null,
+			timerIn     : '',
+			leftTimeNum : 0,
+			isstart     :false
+		}
+	},
+	created : function(){
+		this.timerIn = this.timer;
+		this.runbase();
+	},
+	watch   : {
+		timer : function(){
+			this.timerIn = this.timer;
+			this.runbase();
+		},
+		start : function(){
+			this.isstart = this.start;	
+			this.runbase();
+		},
+	},
+	methods : {
+		runbase : function(){
+			var reg = /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/;
+			var res = this.timerIn.match(reg);
+			if (res == null){ return false;}
+			var year = parseInt(res[1]);
+			if (year < 1000) { return false; }
+			var month = parseInt(res[2]);
+			var day = parseInt(res[3]);
+			var h = parseInt(res[4]);
+			if (h < 0 || h > 24) { return false; }
+			var i = parseInt(res[5]);
+			if (i < 0 || i > 60) { return false; }
+			var s = parseInt(res[6]);
+			if (s < 0 || s > 60) { return false; }
+			var leftTime = new Date(year, month - 1, day, h, i, s);
+			this.leftTime = leftTime;
+			clearTimeout(this.outTimer);
+			this.countUp();
+		},
+		countUp: function (){
+			var leftTime     =  new Date() - this.leftTime;
+			if (!this.isstart)
+			leftTime=0;
+			//this.leftTimeNum = leftTime;
+			if (leftTime > 0) {
+				var day     = parseInt(leftTime  / (1000 * 60 * 60 * 24));
+				var hours   = parseInt((leftTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
+				var minutes = parseInt((leftTime % (1000 * 60 * 60)) / (1000 * 60));
+				var seconds = parseInt((leftTime % (1000 * 60)) / 1000);
+				if (hours   < 10) { hours   = '0' + hours;}
+				if (minutes < 10) { minutes = '0' + minutes; }
+				if (seconds < 10) { seconds = '0' + seconds; }
+				this.h = hours; 
+				this.i = minutes; 
+				this.s = seconds; 
+				this.d = day;
+				this.outTimer = setTimeout(()=>{this.countUp();}, 1000);
+			}else{
+				clearTimeout(this.outTimer);
+				this.h = '00'; 
+				this.i = '00'; 
+				this.s = '00'; 
+				this.d = 0;
+				this.$emit('endDo');
+			}
+		},
+		reSetTimer : function(timer){
+			clearTimeout(this.outTimer);
+			this.timerIn = timer;
+			this.runbase();
+		},
+		getTimeRemaining : function(){
+			if(this.leftTimeNum < 0){return 0;}
+			return parseInt(this.leftTimeNum / 1000);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-countdown-splitor{padding:0 5rpx;}
+.gui-countdown-numbers{border-radius:8rpx; text-align:center;}
+</style>

+ 59 - 0
GraceUI5/components/gui-coupons.vue

@@ -0,0 +1,59 @@
+<template>
+	<view class="gui-coupons gui-flex gui-rows gui-nowrap">
+		<view class="gui-coupons-left gui-flex1 gui-flex gui-rows gui-nowrap gui-space-between gui-align-items-center" 
+		:style="{backgroundColor:coupon.ltBg, height:coupon.height}">
+			<view class="gui-coupons-left-number">
+				<view class="gui-flex gui-rows gui-nowrap gui-justify-content-center gui-align-items-center">
+					<text class="gui-color-gray gui-h4">{{coupon.unit}}</text>
+					<text class="gui-h3 gui-bold" 
+					:style="{color:coupon.color}">{{coupon.number}}</text>
+				</view>
+				<text class="gui-text-small gui-block-text gui-text-center" 
+				:style="{color:coupon.color}">{{coupon.txt}}</text>
+			</view>
+			<view class="gui-coupons-left-body">
+				<text class="gui-block-text gui-h5 gui-primary-color">{{coupon.title}}</text>
+				<text class="gui-block-text gui-text-small gui-color-gray">{{coupon.desc}}</text>
+			</view>
+			<text class="gui-coupons-status"
+			v-if="coupon.drawed" 
+			:style="{backgroundColor:coupon.color}">{{coupon.drawed}}</text>
+		</view>
+		<text class="gui-coupons-right gui-color-white gui-text-center gui-block-text" 
+		:style="{
+			height:coupon.height, 
+			lineHeight:coupon.height, 
+			backgroundColor:coupon.color
+		}">{{coupon.btn}}</text>
+		<view class="gui-coupons-sawtooth" 
+		:style="{
+		height:coupon.height,
+		backgroundColor:coupon.color}">
+			<view class="gui-coupons-sawtooth-circular" 
+			v-for="(item, index) in 10" :key="index"></view>
+		</view>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-coupons",
+	props : {
+		coupon : {
+			type : Object,
+			default :  function  () {
+				return {}
+			}
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-coupons{border-top-left-radius:10rpx; border-bottom-left-radius:10rpx;}
+.gui-coupons-left{width:500rpx; height:150rpx; padding-right:10rpx; border-radius:8rpx; position:relative;}
+.gui-coupons-left-number{width:168rpx; border-right:1px dashed #D2D2D2;}
+.gui-coupons-left-body{width:320rpx;}
+.gui-coupons-status{color:#FFFFFF; padding:0 10px; height:36rpx; line-height:36rpx; font-size:20rpx; position:absolute; z-index:1; right:6px; top:8px; border-radius:36rpx;}
+.gui-coupons-right{width:150rpx; height:150rpx; font-size:32rpx;}
+.gui-coupons-sawtooth{width:8px; height:150rpx; overflow:hidden; position:relative;}
+.gui-coupons-sawtooth-circular{width:10px; margin:3px 5px; height:10px; border-radius:10px; background:#F8F8F8;}
+</style>

+ 164 - 0
GraceUI5/components/gui-date-between.vue

@@ -0,0 +1,164 @@
+<template>
+	<view>
+		<view class="gui-flex gui-date-bt-block" 
+		:style="{backgroundColor:weekBg}">
+			<text class="gui-date-bt-item gui-date-bt-week gui-block-text">一</text>
+			<text class="gui-date-bt-item gui-date-bt-week gui-block-text">二</text>
+			<text class="gui-date-bt-item gui-date-bt-week gui-block-text">三</text>
+			<text class="gui-date-bt-item gui-date-bt-week gui-block-text">四</text>
+			<text class="gui-date-bt-item gui-date-bt-week gui-block-text">五</text>
+			<text class="gui-date-bt-item gui-date-bt-week gui-block-text">六</text>
+			<text class="gui-date-bt-item gui-date-bt-week gui-block-text">日</text>
+		</view>
+		<view v-for="(days, daysIndex) in daysData" :key="daysIndex">
+			<view class="gui-flex gui-date-bt-month-w">
+				<text class="gui-date-bt-month gui-block-text" 
+				:style="{backgroundColor:startBg}">{{days[0][0]}}{{unit[0]}}{{days[0][1]}}{{unit[1]}}</text>
+			</view>
+			<view class="gui-flex gui-date-bt-block">
+				<text class="gui-date-bt-item gui-date-bt-days" 
+				v-for="(item, index) in days[1]" :key="index" 
+				:style="{
+					backgroundColor:item[1] ? sedBg : itemBg, 
+					color:item[1] ? sedColor : color
+				}" 
+				:data-dt="item[0] == '' ? '' : days[0][0]+''+days[0][1]+''+item[0]" 
+				@tap.stop="selectDay">{{item[0]}}<text class="gui-date-bt-start" 
+				v-if="sedDays[0] == days[0][0]+''+days[0][1]+''+item[0] || sedDays[1] == days[0][0]+''+days[0][1]+''+item[0]" 
+				:style="{
+					backgroundColor : sedDays[1] == days[0][0]+''+days[0][1]+''+item[0] ? endBg : startBg, 
+					color:sedColor}">{{item[0]}}</text></text>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+/* 组件百分百原创 拒绝任何代码抄袭及思路抄袭 */
+export default{
+	name  : "gui-date-between",
+	props : {
+		weekBg      : { type : String, default:'#F8F8F8'},
+		unit        : { type : Array,  default:function(){return [' 年 ',' 月'];}},
+		sedBg       : { type : String, default:'rgba(54,136,255,0.8)'},
+		itemBg      : { type : String, default:'#FFFFFF'},
+		color       : { type : String, default:'#323232'},
+		sedColor    : { type : String, default:'#FFFFFF'},
+		startBg     : { type : String, default:'rgba(54,136,255,1)'},
+		endBg       : { type : String, default:'rgba(54,136,255,1)'},
+		monthNumber : { type : Number, default:2}
+	},
+	data() {
+		return {
+			sedDays:[0,0],
+			btDays :[],
+			daysData : []
+		}
+	},
+	created:function(){
+		this.setMonth(this.currentMonth());
+	},
+	methods:{
+		setBetween:function(days){
+			if(days[1] == 0){days[1] = days[0];}
+			days[0]          = Number(days[0]);
+			days[1]          = Number(days[1]);
+			this.sedDays     = days;
+			var countNumber  = 0;
+			var daysNew      = [];
+			this.daysData.forEach((itm)=>{
+				var year     = itm[0][0];
+				var month    = itm[0][1];
+				var daysIn   = [];
+				itm[1].forEach((item)=>{
+					var cDay = year+''+month+''+item[0];
+					cDay = Number(cDay);
+					if(cDay >= days[0] && cDay <= days[1]){
+						item[1] = true; 
+						countNumber++;
+					}else{
+						item[1] = false;
+					}
+					daysIn.push(item);
+				});
+				daysNew.push([[year, month], daysIn]);
+			});
+			this.daysData = daysNew;
+			this.$emit('selectDate', [days, countNumber]);
+		},
+		setMonth:function(month){
+			var reg = /^([0-9]{4}).*([0-9]{2}).*$/;
+			var res = month.match(reg);
+			if(res == null){month = this.currentMonth(); res = month.match(reg);}
+			this.setMonthBase(res);
+		},
+		setMonthBase : function(res){
+			var daysData = [];
+			if(res[2].substr(0,1) == '0'){res[2] = res[2].substr(1);}
+			res[1]    = Number(res[1]);
+			res[2]    = Number(res[2]);
+			var year  = res[1];
+			var month = res[2];
+			for(let i = 0; i < this.monthNumber; i++){
+				var monthIn = month + i;
+				var yearIn  = year;
+				if(monthIn > 12){monthIn = monthIn - 12; yearIn += 1;}
+				if(monthIn < 10){monthIn = '0'+monthIn;}
+				daysData[i] = [];
+				daysData[i].push([yearIn, monthIn]);
+				var days = this.getDays(yearIn,monthIn);
+				var daysList  = [];
+				for (let ii = (0 - days[1]); ii < days[0]; ii++){
+					if(ii >= 0){
+						daysList.push([ii >= 9 ? ii + 1 : '0' + (ii+1), false]);
+					}else{
+						daysList.push(['',false]);
+					}
+				}
+				daysData[i].push(daysList);
+			}
+			this.daysData = daysData;
+		},
+		currentMonth : function () {
+			var date = new Date();
+			var y = date.getFullYear();
+			var m = date.getMonth() + 1;
+			m = m < 10 ? ('0' + m) : m;
+			return y + '年' + m + '月';
+		},
+		getDays : function(year,month){
+			var d         = new Date(year, month, 0);
+			var days      = d.getDate();
+			var d2        = new Date(year, month - 1, 0);
+			var startWeek = d2.getDay();
+			return [days, startWeek];
+		},
+		selectDay:function(e){
+			var day = e.currentTarget.dataset.dt;
+			if(day == ''){return ;}
+			day = Number(day);
+			if(this.sedDays[0] == 0){ this.sedDays[0] = day; }
+			else if(this.sedDays[1] == 0 || this.sedDays[0] == this.sedDays[1]){
+				if(day > this.sedDays[0]){
+					this.sedDays[1] = day;
+				}else if(day < this.sedDays[0]){
+					this.sedDays[1] = this.sedDays[0];
+					this.sedDays[0] = day;
+				}
+			}else{
+				this.sedDays[0] = day;
+				this.sedDays[1] = 0;
+			}
+			this.setBetween(this.sedDays);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-date-bt-block{flex-direction:row; flex-wrap:wrap;}
+.gui-date-bt-item{width:98rpx; text-align:center; position:relative;}
+.gui-date-bt-week{line-height:80rpx; height:80rpx; font-size:28rpx;}
+.gui-date-bt-month-w{flex-direction:row; flex-wrap:nowrap; margin:25px 0;}
+.gui-date-bt-month{line-height:50rpx; text-align:center; color:#FFFFFF; width:258rpx; border-radius:30rpx; font-size:26rpx;}
+.gui-date-bt-days{line-height:98rpx; height:98rpx; width:98rpx; font-size:28rpx;}
+.gui-date-bt-start{position:absolute; font-size:28rpx; font-weight:bold; width:98rpx; height:98rpx; text-align:center; line-height:98rpx; z-index:1; left:0; top:0;}
+</style>

+ 113 - 0
GraceUI5/components/gui-datetime-between.vue

@@ -0,0 +1,113 @@
+<template>
+	<view>
+		<view @tap.stop="open"><slot></slot></view>
+		<view 
+		class="gui-dateBT-shade gui-flex gui-columns gui-justify-content-end" 
+		:style="{zIndex:zIndex, left:show?'0rpx':'-1000rpx'}">
+			<view class="gui-bg-white">
+				<view 
+				class="graceDateTime-header gui-flex gui-rows gui-space-between gui-bg-gray">
+					<text class="graceDateTime-header-btn" 
+					:style="{color:cancelTColor}" 
+					@tap="close">{{cancelText}}</text>
+					<text class="graceDateTime-header-btn" 
+					:style="{textAlign:'right', color:confirmColor}" 
+					@tap="confirm">{{confirmText}}</text>
+				</view>
+				<view>
+					<text class="graceDateTimeBT-text gui-block-text">{{titles[0]}}</text>
+				</view>
+				<!-- 起始时间 -->
+				<view 
+				style="overflow:hidden;">
+					<gui-datetime-bt-base 
+					:height="height" 
+					:value="startValue" 
+					@change="chang1" 
+					:indicatorStyle="indicatorStyle" 
+					:isTime="isTime" 
+					:isSecond="isSecond" 
+					:isMinute="isMinute" 
+					:startYear="startYear" 
+					:endYear="endYear" 
+					:isDay="isDay" 
+					:units="units"></gui-datetime-bt-base>
+				</view>
+				<!-- 结束时间 -->
+				<view class="gui-margin-top">
+					<text class="graceDateTimeBT-text gui-block-text">{{titles[1]}}</text>
+				</view>
+				<view style="overflow:hidden;">
+					<gui-datetime-bt-base 
+					:value="endValue" 
+					:indicatorStyle="indicatorStyle" 
+					:isTime="isTime" 
+					:isMinute="isMinute" 
+					@change="chang2" 
+					:isSecond="isSecond" 
+					:isDay="isDay" 
+					:startYear="startYear" 
+					:endYear="endYear" 
+					:height="height" 
+					:units="units"></gui-datetime-bt-base>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-datetime-between",
+	props : {
+		cancelText    : { type : String,  default : '取消' },
+		cancelTColor  : { type : String,  default : '#888888' },
+		confirmText   : { type : String,  default : '确定' },
+		confirmColor  : { type : String,  default : '#008AFF' },
+		startValue    : { type : String , default : ''},
+		endValue      : { type : String , default : ''},
+		isTime        : { type : Boolean, default : true},
+		isMinute      : { type : Boolean, default : true},
+		isSecond      : { type : Boolean, default : true},
+		startYear     : { type : Number,  default : 1980},
+		endYear       : { type : Number,  default : 2050},
+		units         : { type : Array ,  default : function(){return new Array('年','月','日','时','分','秒')}},
+		titles        : { type : Array ,  default : function(){return new Array('开始时间','结束时间')}},
+		zIndex        : { type : Number,  default : 90 },
+		isDay         : { type : Boolean, default : true },
+		indicatorStyle: { type : String,  default : 'height:36px; line-heiht:36px;'},
+		height        : { type : String,  default : '300rpx'}
+	},
+	data() {
+		return {
+			defaultVal     : [0,0,0,0,0,0],
+			sDate:[[],[],[],[],[],[]],
+			recDate:[[],[]],
+			show : false
+		}
+	},
+	methods:{
+		open : function () {
+			this.show = true;
+		},
+		close : function () {
+			this.show = false;
+		},
+		confirm : function(){
+			this.$emit('confirm', this.recDate);
+			this.close();
+		},
+		chang1 : function(res){
+			this.recDate[0] = res;
+		},
+		chang2 : function(res){
+			this.recDate[1] = res;
+		},
+	}
+}
+</script>
+<style scoped>
+.gui-dateBT-shade{width:750rpx; position:fixed; z-index:99; left:0; top:0; bottom:0; flex:1; overflow:hidden; background-color:rgba(0,0,0,0.5);}
+.graceDateTime-header{padding:25rpx;}
+.graceDateTime-header-btn{width:200rpx; line-height:38rpx; height:38rpx; font-size:28rpx;}
+.graceDateTimeBT-text{padding:15rpx; background-color:#FFFFFF; line-height:80rpx; height:80rpx; color:rgba(69, 90, 100, 0.6); font-size:26rpx;}
+</style>

+ 216 - 0
GraceUI5/components/gui-datetime-bt-base.vue

@@ -0,0 +1,216 @@
+<template>
+	<picker-view 
+	:style="{height:height}"
+	:indicator-style="indicatorStyle" 
+	class="graceDateTime-main" 
+	:value="defaultVal" 
+	@change="change">
+		<picker-view-column>
+			<view 
+			class="graceDateTime-item" 
+			:style="indicatorStyle" 
+			v-for="(item, index) in sDate[0]"
+			:key="index">
+				<text
+				class="graceDateTime-item" 
+				:style="indicatorStyle">{{item}}{{units[0]}}</text>
+			</view>
+		</picker-view-column>
+		<picker-view-column>
+			<view
+			class="graceDateTime-item" 
+			:style="indicatorStyle" 
+			v-for="(item, index) in sDate[1]"
+			:key="index">
+				<text 
+				class="graceDateTime-item" 
+				:style="indicatorStyle">{{item}}{{units[1]}}</text>
+			</view>
+		</picker-view-column>
+		<picker-view-column 
+		v-if="isDay">
+			<view
+			class="graceDateTime-item" 
+			:style="indicatorStyle" 
+			v-for="(item, index) in sDate[2]" 
+			:key="index">
+				<text 
+				class="graceDateTime-item" 
+				:style="indicatorStyle">{{item}}{{units[2]}}</text>
+			</view>
+		</picker-view-column>
+		<picker-view-column 
+		v-if="isTime">
+			<view
+			class="graceDateTime-item" 
+			:style="indicatorStyle" 
+			v-for="(item, index) in sDate[3]" 
+			:key="index">
+				<text 
+				class="graceDateTime-item" 
+				:style="indicatorStyle">{{item}}{{units[3]}}</text>
+			</view>
+		</picker-view-column>
+		<picker-view-column 
+		v-if="isTime && isMinute">
+			<view
+			class="graceDateTime-item" 
+			:style="indicatorStyle" 
+			v-for="(item, index) in sDate[4]" 
+			:key="index">
+				<text 
+				class="graceDateTime-item" 
+				:style="indicatorStyle">{{item}}{{units[4]}}</text>
+			</view>
+		</picker-view-column>
+		<picker-view-column  
+		v-if="isTime && isMinute && isSecond">
+			<view
+			class="graceDateTime-item" 
+			:style="indicatorStyle" 
+			v-for="(item, index) in sDate[5]" 
+			:key="index">
+				<text 
+				class="graceDateTime-item" 
+				:style="indicatorStyle">{{item}}{{units[5]}}</text>
+			</view>
+		</picker-view-column>
+	</picker-view>
+</template>
+<script>
+export default {
+	name  : "gui-datetime-bt-base",
+	props : {
+		value     : { type : String , default:''},
+		isMinute  : { type : Boolean, default : true},
+		isTime    : { type : Boolean, default : true},
+		isSecond  : { type : Boolean, default : true},
+		isDay     : { type : Boolean, default : true },
+		startYear : { type : Number, default : 1980},
+		endYear   : { type : Number, default : 2050},
+		units     : { type : Array , default:function(){return new Array('年','月','日','时','分','秒')}},
+		height    : { type : String, default : '300rpx' },
+		indicatorStyle : { type : String,  default : 'height:36px; line-heiht:36px;'}
+	},
+	data() {
+		return {
+			defaultVal     : [0,0,0,0,0,0],
+			sDate:[[],[],[],[],[],[]]
+		}
+	},
+	created() {this.init();},
+	methods: {
+		now : function () {
+			var date = new Date();
+			var y = date.getFullYear();
+			var m = date.getMonth() + 1;
+			m = m < 10 ? ('0' + m) : m;
+			var d = date.getDate();
+			d = d < 10 ? ('0' + d) : d;
+			var h = date.getHours();
+			h = h < 10 ? ('0' + h) : h;
+			var minute = date.getMinutes();
+			var second = date.getSeconds();
+			minute = minute < 10 ? ('0' + minute) : minute;
+			second = second < 10 ? ('0' + second) : second;
+			return y + '-' + m + '-' + d + ' '+ h +':' + minute + ':' + second;
+		},
+		arrayIndexOf : function(arr, needFind){
+			var index = -1;
+			for(let i = 0; i < arr.length; i++){if(arr[i] == needFind){index = i; return i;}}
+			return index;
+		},
+		setValue : function (val) {
+			if(val == ''){val = this.now();}
+			var reg = /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/;
+			var res = val.match(reg);
+			if(res == null){
+				reg = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/;
+				res = val.match(reg);
+				if(res == null){
+					this.setValue(this.now());
+					return ;
+				}
+				res[4] = '00';
+				res[5] = '00';
+				res[6] = '00';
+			}
+			this.setDefaults([res[1],res[2],res[3],res[4],res[5],res[6]]);
+		},
+		setDefaults : function (res) {
+			for(let i = 0; i < res.length; i++){
+				var index = this.arrayIndexOf(this.sDate[i], res[i]);
+				if(index == -1){index = 0;}
+				this.defaultVal.splice(i, 1, index);
+			}
+			this.changeBase(this.defaultVal);
+		},
+		// 初始化组件
+		init:function(){
+			if(this.endYear < this.startYear){this.endYear = this.startYear + 10;}
+			var years     = new Array();
+			for(let i = this.startYear; i <= this.endYear; i++){years.push(i);}
+			var months     = new Array();
+			for(let i = 1; i <= 12; i++){if(i < 10){months.push('0'+i);}else{months.push(i);}}
+			var days     = new Array();
+			for(let i = 1; i <= 31; i++){if(i < 10){days.push('0'+i);}else{days.push(i);}}
+			var hours     =  new Array();
+			for(let i = 0; i < 24; i++){if(i < 10){hours.push('0'+i);}else{hours.push(i);}}
+			var minutes  =  new Array();
+			var seconds  =  new Array();
+			for(let i = 0; i < 60; i++){
+				if(i < 10){minutes.push('0'+i); seconds.push('0'+i);}else{minutes.push(i); seconds.push(i);}
+			}
+			this.sDate = [years, months, days, hours, minutes, seconds];
+			this.$nextTick(()=>{setTimeout(()=>{ this.setValue(this.value);},800);});
+		},
+		change : function (res) {
+			this.changeBase(res.detail.value);
+		},
+		changeBase:function(res){
+			var date = new Date(this.sDate[0][res[0]], this.sDate[1][res[1]], 0);
+			var days = date.getDate();
+			var daysOut = new Array();
+			for(let i = 1; i <= days; i++){if(i < 10){daysOut.push('0'+i);}else{daysOut.push(i);}}
+			this.sDate.splice(2, 1, daysOut);
+			if(res[2] + 1 > days){res[2] = days - 1;}
+			this.defaultVal = res;
+			if(this.isTime){
+				var resdata = new Array(this.sDate[0][this.defaultVal[0]],
+				this.sDate[1][this.defaultVal[1]],
+				this.sDate[2][this.defaultVal[2]],
+				this.sDate[3][this.defaultVal[3]],
+				this.sDate[4][this.defaultVal[4]],
+				this.sDate[5][this.defaultVal[5]]);
+			}else{
+				var resdata = new Array(this.sDate[0][this.defaultVal[0]],
+				this.sDate[1][this.defaultVal[1]],
+				this.sDate[2][this.defaultVal[2]])
+			}
+			this.$emit('change', resdata);
+		},
+		confirm:function () {
+			if(this.isTime){
+				var res = new Array(this.sDate[0][this.defaultVal[0]],
+				this.sDate[1][this.defaultVal[1]],
+				this.sDate[2][this.defaultVal[2]],
+				this.sDate[3][this.defaultVal[3]],
+				this.sDate[4][this.defaultVal[4]],
+				this.sDate[5][this.defaultVal[5]]);
+			}else{
+				var res = new Array(this.sDate[0][this.defaultVal[0]],
+				this.sDate[1][this.defaultVal[1]],
+				this.sDate[2][this.defaultVal[2]])
+			}
+			this.$emit('confirm', res);
+		}
+	}
+}
+</script>
+<style scoped>
+.graceDateTime-main{width:750rpx; height:300rpx; color:#323232;}
+.graceDateTime-item{font-size:26rpx; height:36px; line-height:36px; text-align:center; overflow:hidden;}
+/* #ifndef APP-NVUE */
+.graceDateTime-item{display:block;}
+/* #endif */
+</style>

+ 262 - 0
GraceUI5/components/gui-datetime.vue

@@ -0,0 +1,262 @@
+<template>
+	<view>
+		<view 
+		@tap.stop="open"><slot></slot></view>
+		<view 
+		class="gui-dateBT-shade gui-flex gui-columns gui-justify-content-end"
+		v-if="show" 
+		:style="{zIndex:zIndex, width:width}">
+			<view 
+			class="graceDateTime-header gui-flex gui-rows gui-space-between gui-bg-gray">
+				<text 
+				class="graceDateTime-header-btn" 
+				:style="{color:cancelTColor}" 
+				@tap="close">{{cancelText}}</text>
+				<text 
+				class="graceDateTime-header-btn" 
+				:style="{textAlign:'right', color:confirmColor}" 
+				@tap="confirm">{{confirmText}}</text>
+			</view>
+			<view 
+			class="gui-bg-white">
+				<picker-view 
+				:indicator-style="indicatorStyle" 
+				class="graceDateTime-main" 
+				:value="defaultVal" 
+				@change="change" 
+				:style="{height:height, width:width}">
+					<picker-view-column>
+						<view 
+						class="graceDateTime-item"
+						:style="indicatorStyle" 
+						v-for="(item, index) in sDate[0]" 
+						:key="index">
+							<text
+							class="graceDateTime-item gui-block-text" 
+							:style="indicatorStyle">{{item}}{{units[0]}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column>
+						<view 
+						class="graceDateTime-item"
+						:style="indicatorStyle" 
+						v-for="(item, index) in sDate[1]" 
+						:key="index">
+							<text
+							class="graceDateTime-item gui-block-text" 
+							:style="indicatorStyle">{{item}}{{units[1]}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column>
+						<view 
+						class="graceDateTime-item"
+						:style="indicatorStyle" 
+						v-for="(item, index) in sDate[2]" 
+						:key="index">
+							<text
+							class="graceDateTime-item gui-block-text" 
+							:style="indicatorStyle">{{item}}{{units[2]}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column 
+					v-if="isTime">
+						<view 
+						class="graceDateTime-item"
+						:style="indicatorStyle" 
+						v-for="(item, index) in sDate[3]" 
+						:key="index">
+							<text
+							class="graceDateTime-item gui-block-text" 
+							:style="indicatorStyle">{{item}}{{units[3]}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column 
+					v-if="isTime && isMinute">
+						<view 
+						class="graceDateTime-item"
+						:style="indicatorStyle" 
+						v-for="(item, index) in sDate[4]" 
+						:key="index">
+							<text
+							class="graceDateTime-item gui-block-text" 
+							:style="indicatorStyle">{{item}}{{units[4]}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column 
+					v-if="isTime && isMinute && isSecond">
+						<view 
+						class="graceDateTime-item" 
+						:style="indicatorStyle" 
+						v-for="(item, index) in sDate[5]" 
+						:key="index">
+							<text
+							class="graceDateTime-item gui-block-text" 
+							:style="indicatorStyle">{{item}}{{units[5]}}</text>
+						</view>
+					</picker-view-column>
+				</picker-view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-datetime",
+	props : {
+		cancelText    : { type : String,  default : '取消' },
+		cancelTColor  : { type : String,  default : '#888888' },
+		confirmText   : { type : String,  default : '确定' },
+		confirmColor  : { type : String,  default : '#008AFF' },
+		value         : { type : String , default : ''},
+		isTime        : { type : Boolean, default : true},
+		isMinute      : { type : Boolean, default : true},
+		isSecond      : { type : Boolean, default : true},
+		startYear     : { type : Number,  default : 1980},
+		endYear       : { type : Number,  default : 2050},
+		units         : { type : Array ,  default : function(){return new Array('年','月','日','时','分','秒')}},
+		height        : { type : String,  default : '300rpx' },
+		zIndex        : { type : Number,  default : 90},
+		width         : { type : String,  default : '750rpx' },
+		indicatorStyle : { type : String, default : 'height:36px; line-height:36px;'}
+	},
+	data() {
+		return {
+			show:false,
+			defaultVal     : [0,0,0,0,0,0],
+			sDate:[[],[],[],[],[],[]],
+			timer:null,
+			show : false
+		}
+	},
+	created() {
+		this.init();
+	},
+	methods: {
+		stopfun:function(e){e.stopPropagation(); return ;},
+		now : function () {
+			var date = new Date();
+			var y = date.getFullYear();
+			var m = date.getMonth() + 1;
+			m = m < 10 ? ('0' + m) : m;
+			var d = date.getDate();
+			d = d < 10 ? ('0' + d) : d;
+			var h = date.getHours();
+			h = h < 10 ? ('0' + h) : h;
+			var minute = date.getMinutes();
+			var second = date.getSeconds();
+			minute = minute < 10 ? ('0' + minute) : minute;
+			second = second < 10 ? ('0' + second) : second;
+			return y + '-' + m + '-' + d + ' '+ h +':' + minute + ':' + second;
+		},
+		arrayIndexOf : function(arr, needFind){
+			var index = -1;
+			for(let i = 0; i < arr.length; i++){if(arr[i] == needFind){index = i; return i;}}
+			return index;
+		},
+		setValue : function (val) {
+			if(val == ''){val = this.now();}
+			var reg = /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/;
+			var res = val.match(reg);
+			if(res == null){
+				reg = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/;
+				res = val.match(reg);
+				if(res == null){
+					this.setValue(this.now());
+					return ;
+				}
+				res[4] = '00';
+				res[5] = '00';
+				res[6] = '00';
+			}
+			this.setDefaults([res[1],res[2],res[3],res[4],res[5],res[6]]);
+		},
+		setDefaults : function (res) {
+			for(let i = 0; i < res.length; i++){
+				var index = this.arrayIndexOf(this.sDate[i], res[i]);
+				if(index == -1){index = 0;}
+				this.defaultVal.splice(i, 1, index);
+			}
+			this.changeBase(this.defaultVal);
+		},
+		// 初始化组件
+		init:function(){
+			if(this.endYear < this.startYear){this.endYear = this.startYear + 10;}
+			var years     = new Array();
+			for(let i = this.startYear; i <= this.endYear; i++){years.push(i);}
+			var months     = new Array();
+			for(let i = 1; i <= 12; i++){if(i < 10){months.push('0'+i);}else{months.push(i);}}
+			var days     = new Array();
+			for(let i = 1; i <= 31; i++){if(i < 10){days.push('0'+i);}else{days.push(i);}}
+			var hours     =  new Array();
+			for(let i = 0; i < 24; i++){if(i < 10){hours.push('0'+i);}else{hours.push(i);}}
+			var minutes  =  new Array();
+			var seconds  =  new Array();
+			for(let i = 0; i < 60; i++){
+				if(i < 10){minutes.push('0'+i); seconds.push('0'+i);}else{minutes.push(i); seconds.push(i);}
+			}
+			this.sDate = [years, months, days, hours, minutes, seconds];
+			this.$nextTick(()=>{setTimeout(()=>{ this.setValue(this.value);}, 500);});
+		},
+		change : function (res) {
+			if(this.timer != null){clearTimeout(this.timer);}
+			this.timer = setTimeout(()=>{this.changeBase(res.detail.value);},500);
+		},
+		changeBase:function(res){
+			var date = new Date(this.sDate[0][res[0]], this.sDate[1][res[1]], 0);
+			var days = date.getDate();
+			var daysOut = new Array();
+			for(let i = 1; i <= days; i++){if(i < 10){daysOut.push('0'+i);}else{daysOut.push(i);}}
+			this.sDate.splice(2, 1, daysOut);
+			if(res[2] + 1 > days){res[2] = days - 1;}
+			this.defaultVal = res;
+			if(this.isTime){
+				var resdata = new Array(this.sDate[0][this.defaultVal[0]],
+				this.sDate[1][this.defaultVal[1]],
+				this.sDate[2][this.defaultVal[2]],
+				this.sDate[3][this.defaultVal[3]],
+				this.sDate[4][this.defaultVal[4]],
+				this.sDate[5][this.defaultVal[5]]);
+			}else{
+				var resdata = new Array(
+					this.sDate[0][this.defaultVal[0]],
+					this.sDate[1][this.defaultVal[1]],
+					this.sDate[2][this.defaultVal[2]]
+				)
+			}
+			this.$emit('change', resdata);
+		},
+		confirm:function () {
+			if(this.isTime){
+				var res = new Array(this.sDate[0][this.defaultVal[0]],
+				this.sDate[1][this.defaultVal[1]],
+				this.sDate[2][this.defaultVal[2]],
+				this.sDate[3][this.defaultVal[3]],
+				this.sDate[4][this.defaultVal[4]],
+				this.sDate[5][this.defaultVal[5]]);
+			}else{
+				var res = new Array(this.sDate[0][this.defaultVal[0]],
+				this.sDate[1][this.defaultVal[1]],
+				this.sDate[2][this.defaultVal[2]])
+			}
+			this.$emit('confirm', res);
+			this.close();
+		},
+		open : function () {
+			this.show = true;
+		},
+		close : function () {
+			this.show = false;
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-dateBT-shade{width:750rpx; position:fixed; background-color:rgba(0,0,0,0.5); z-index:99; left:0; top:0; bottom:0; flex:1; overflow:hidden;}
+.graceDateTime-header{padding:25rpx;}
+.graceDateTime-header-btn{width:200rpx; line-height:38rpx; height:38rpx; font-size:28rpx;}
+.graceDateTime-main{width:750rpx; color:#232323;}
+.graceDateTime-item{height:36px; font-size:26rpx; line-height:36px; overflow:hidden; text-align:center;}
+/* #ifndef APP-NVUE */
+.graceDateTime-item{display:block; width:100%;}
+/* #endif */
+</style>

+ 26 - 0
GraceUI5/components/gui-demo-banner.vue

@@ -0,0 +1,26 @@
+<template>
+	<view class="gui-banner">
+		<image :src="img" class="gui-banner-img"></image>
+		<view class="gui-banner-in gui-flex gui-columns gui-justify-content-center">
+			<text class="gui-block-text gui-h3 gui-color-white gui-bold gui-text-center">{{title}}</text>
+			<text class="gui-block-text gui-text-small gui-color-white gui-text-center" style="margin-top:8rpx;">{{desc}}</text>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-demo-banner",
+	props : {
+		title:{type:String, default:''},
+		img:{type:String, default:''},
+		desc:{type:String, default:'漂亮 丰富 高效的跨平台 UI 框架'}
+	}
+}
+</script>
+<style scoped>
+.gui-banner{height:228rpx; border-radius:10rpx; font-size:0; position:relative; margin-top:15rpx;}
+.gui-banner-img{width:690rpx; height:228rpx; border-radius:10rpx; opacity:0.92;}
+.gui-banner-in{height:228rpx; border-radius:10rpx; position:absolute; z-index:1; left:0; top:0;}
+.gui-h3{font-size:38rpx;}
+.gui-text-center{width:690rpx;}
+</style>

+ 22 - 0
GraceUI5/components/gui-demo-desc.vue

@@ -0,0 +1,22 @@
+<template>
+	<view style="margin-top:50rpx; ">
+		<view>
+			<text class="gui-h6 demo-desc-title">{{title}}</text>
+		</view>
+		<view class="gui-margin-top gui-bg-gray demo-desc-content gui-border-radius">
+			<text class="gui-text">{{desc}}</text>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	props:{
+		title:{type:String, default:'演示说明'},
+		desc:{type:String, default:''}
+	}
+}
+</script>
+<style>
+.demo-desc-title{color:rgba(69, 90, 100, 0.6); line-height:50rpx;}
+.demo-desc-content{padding:20rpx;}
+</style>

+ 12 - 0
GraceUI5/components/gui-demo-header.vue

@@ -0,0 +1,12 @@
+<template>
+	<view 
+	class="gui-margin gui-rows gui-align-items-center">
+		<text class="gui-h5 gui-icons gui-primary-color">&#xe642; GraceUI 5</text>
+		<text class="gui-text-small gui-color-gray" 
+		style="margin-left:20rpx; margin-top:6rpx;">更努力为更快更好的跨平台开发</text>
+	</view>
+</template>
+<script>
+</script>
+<style>
+</style>

+ 31 - 0
GraceUI5/components/gui-demo-list.vue

@@ -0,0 +1,31 @@
+<template>
+	<view>
+		<view class="demo-list"
+		v-for="(item, idx) in pageList" :key="idx">
+			<view class="gui-flex gui-rows">
+				<text class="demo-title gui-color-gray gui-h5">{{item.title}}</text>
+				<text class="gui-text-small gui-text-small-ml gui-color-gray">{{item.desc}}</text>
+			</view>
+			<navigator class="demo-list-cell gui-flex gui-rows gui-space-between gui-bg-gray gui-border-radius" 
+			v-for="(nav, index) in item.pages" :key="index" :url="nav[1]">
+				<text class="demo-list-cell-title">{{(index+1)}}. {{nav[0]}}</text>
+				<text class="demo-list-cell-arrow gui-icons gui-color-gray-light">&#xe601;</text>
+			</navigator>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	props:{
+		pageList:{type:Array, default:function(){return [];}}
+	}
+}
+</script>
+<style scoped>
+.demo-list{margin-top:60rpx;}
+.demo-title{line-height:50rpx;}
+.demo-list-cell{padding-left:25rpx; padding-right:20rpx; padding-top:20rpx; padding-bottom:20rpx; margin-top:25rpx;}
+.demo-list-cell-title{line-height:50rpx; font-size:26rpx;}
+.demo-list-cell-arrow{line-height:50rpx; font-size:36rpx;}
+.gui-text-small-ml{margin-left:20rpx; margin-top:8rpx; line-height:38rpx;}
+</style>

+ 236 - 0
GraceUI5/components/gui-editor.vue

@@ -0,0 +1,236 @@
+<template>
+<view class="gui-editor">
+	<view class="gui-border-b">
+		<textarea class="gui-editor-title" maxlength="-1" 
+		v-model="article.title" placeholder="# 请输入标题" auto-height />
+	</view>
+	<!-- 空内容提示 -->
+	<view v-if="article.contents.length < 1">
+		<text class="gui-color-gray gui-editor-empty">请点击下面的按钮,添加内容。</text>
+	</view>
+	<!-- 内容区域 -->
+	<view v-for="(item, index) in article.contents" :key="index" 
+	class="gui-editor-items">
+		<!-- 加粗 -->
+		<view v-if="item.type == 'h3'">
+			<textarea class="gui-editor-strong gui-border-box" 
+			:data-index="index" maxlength="-1" :focus="item.focusin" 
+			@input="graceEditorInput" :value="item.content" @blur="blur" 
+			placeholder="请输入标题" />
+		</view>
+		<!-- 普通文本 -->
+		<view v-else-if="item.type == 'txt'">
+			<textarea class="gui-editor-txt gui-border-box" 
+			maxlength="-1" @blur="blur" 
+			:data-index="index" :focus="item.focusin" 
+			@input="graceEditorInput" :value="item.content" 
+			placeholder="请填写文本内容" />
+		</view>
+		<!-- 居中文本 -->
+		<view v-else-if="item.type == 'center'" 
+		class="gui-flex gui-rows gui-justify-content-center" 
+		style="background-color:#F8F8F8; padding:20rpx;">
+			<input type="text" class="gui-editor-center" 
+			@blur="blur" maxlength="-1" :data-index="index" 
+			:focus="item.focusin" @input="graceEditorInput" 
+			:value="item.content" placeholder="请填写居中文本" />
+		</view>
+		<!-- 图片 -->
+		<view v-else-if="item.type == 'img'" 
+		class="gui-editor-img-wrap">
+			<image :src="item.content" class="gui-editor-img" 
+			:data-index="index" mode="aspectFit"></image>
+			<view v-if="item.error" 
+			class="gui-editor-img-error gui-flex gui-columns gui-justify-content-center">
+				<text class="gui-editor-img-error-text gui-block-text gui-text-center gui-icons">&#xe6a1; 上传失败,请重试</text>
+			</view>
+		</view>
+		<!-- 引用 -->
+		<view v-else-if="item.type == 'quote'">
+			<textarea class="gui-editor-quote gui-border-box" 
+			maxlength="-1" @blur="blur" 
+			:data-index="index" @input="graceEditorInput" :focus="item.focusin" 
+			:value="item.content" placeholder="请输入引用内容" />
+		</view>
+		<!-- 代码 -->
+		<view v-else-if="item.type == 'code'">
+			<textarea class="gui-editor-quote gui-border-box" 
+			maxlength="-1" @blur="blur" 
+			style="height:300rpx;"
+			:data-index="index" @input="graceEditorInput" :focus="item.focusin" 
+			:value="item.content" placeholder="请输入代码" />
+		</view>
+		<!-- 加粗 -->
+		<view v-else-if="item.type == 'strong'">
+			<textarea class="gui-editor-strong gui-border-box" 
+			:data-index="index" maxlength="-1" :focus="item.focusin" 
+			@input="graceEditorInput" :value="item.content" @blur="blur" 
+			placeholder="请输入加粗内容" />
+		</view>
+		<!-- 链接 -->
+		<view v-else-if="item.type == 'link'">
+			<input type="text" class="gui-editor-link gui-border-box" 
+			:focus="item.focusin" 
+			:data-index="index" @input="graceEditorInput" @blur="blur" 
+			:value="item.content" placeholder="请输入连接地址" />
+		</view>
+		<!-- 分割 -->
+		<view v-else-if="item.type == 'spline'">
+			<text class="gui-editor-spline gui-block-text" 
+			:data-index="index">● ● ●</text>
+		</view>
+		<!-- 功能 -->
+		<view class="gui-flex gui-rows gui-justify-content-end gui-editor-item-btns-wrap">
+			<view class="gui-editor-item-btns" hover-class="gui-tap" 
+			:data-index="index" @tap="moveup">
+				<text class="gui-editor-item-btns-text gui-block-text gui-icons">&#xe654; 上移</text>
+			</view>
+			<view class="gui-editor-item-btns" hover-class="gui-tap" 
+			:data-index="index" @tap="movedown">
+				<text class="gui-editor-item-btns-text gui-block-text gui-icons">&#xe603; 下移</text>
+			</view>
+			<view class="gui-editor-item-btns" @tap="deleteItem" 
+			hover-class="gui-tap" :data-index="index">
+				<text class="gui-editor-item-btns-text gui-block-text gui-icons">&#xe636; 删除</text>
+			</view>
+		</view>
+	</view>
+	<view class="gui-margin-top-large" 
+	v-if="article.contents.length >= 1"></view>
+	<!-- 选项类型选择 -->
+	<view class="gui-flex gui-rows gui-align-items-center gui-space-between gui-editor-menus gui-border-box gui-border-t" 
+	:style="{paddingBottom:ipxHeight}">
+		<text class="gui-editor-icons gui-icons" data-type="h3"
+		@tap="graceEditorAddItem" style="font-size:46rpx;">&#xe64d;</text>
+		<text class="gui-editor-icons gui-icons" data-type="txt" 
+		@tap="graceEditorAddItem" style="font-size:32rpx;">&#xe9e4;</text>
+		<text class="gui-editor-icons gui-icons" data-type="center" 
+		@tap="graceEditorAddItem">&#xe621;</text>
+		<text class="gui-editor-icons gui-icons" data-type="img" 
+		@tap="graceEditorAddItem">&#xe63d;</text>
+		<text class="gui-editor-icons gui-icons" data-type="quote" 
+		@tap="graceEditorAddItem">&#xe620;</text>
+		<text class="gui-editor-icons gui-icons" data-type="code"
+		@tap="graceEditorAddItem" style="font-size:40rpx;">&#xe64e;</text>
+		<text class="gui-editor-icons gui-icons" data-type="strong" 
+		style="font-size:32rpx;" 
+		@tap="graceEditorAddItem">&#xe640;</text>
+		<text class="gui-editor-icons gui-icons gui-bold" data-type="link" 
+		@tap="graceEditorAddItem" style="font-size:38rpx;">&#xe61e;</text>
+		<text class="gui-editor-icons gui-icons gui-bold" data-type="spline" 
+		@tap="graceEditorAddItem">&#xe61b;</text>
+	</view>
+</view>
+</template>
+<script>
+export default{
+	name : "gui-editor",
+	data() {
+		return {
+			article   : { title : '', contents:[] },
+			ipxHeight : 0
+		}
+	},
+	created:function(){
+		// #ifdef MP
+		try {
+			var system   = uni.getSystemInfoSync();
+			system.model = system.model.replace(' ', '');
+			system.model = system.model.toLowerCase();
+			var res1  = res.model.indexOf('iphonex');
+			if(res1 > 5){res1 = -1;}
+			var res2   = res.model.indexOf('iphone1');
+			if(res2 > 5){res2 = -1;}
+			if(res1 != -1 || res2 != -1){
+				this.ipxHeight = '60rpx';
+			}
+		} catch (e){return null;}
+		// #endif
+	},
+	methods:{
+		graceEditorAddItem : function(e){
+			var type = e.currentTarget.dataset.type;
+			if(type == 'img'){
+				uni.chooseImage({
+					success:(e)=>{
+						var imgs = e.tempFilePaths;
+						for(let i = 0; i < imgs.length; i++){
+							this.article.contents.push({type:type,content:imgs[i], focusin:false});
+						}
+					}
+				});
+			}else if(type == 'spline'){
+				this.article.contents.push({type:type,content:'',focusin:false});
+			}else{
+				this.article.contents.push({type:type,content:'',focusin:true});
+			}
+		},
+		graceEditorInput : function(e){
+			var index = e.currentTarget.dataset.index;
+			var val   = e.detail.value;
+			this.article.contents[index].content = val;
+		},
+		deleteItem : function(e){
+			var index = e.currentTarget.dataset.index;
+			uni.showModal({
+				title:"提示",
+				content:"确定要删除项目吗?",
+				success:(e)=>{
+					if(e.confirm){this.article.contents.splice(index, 1);}
+				}
+			})
+		},
+		blur    : function (e) {
+			var index = Number(e.currentTarget.dataset.index);
+			this.article.contents[index].focusin = false;
+			this.article.contents.splice(index, 1, this.article.contents[index]);
+		},
+		moveup  : function (e) {
+			var index = Number(e.currentTarget.dataset.index);
+			if(index > 0){
+				this.article.contents[index] = this.article.contents.splice(index - 1, 1, this.article.contents[index])[0];
+			}
+		},
+		movedown : function (e) {
+			var index = Number(e.currentTarget.dataset.index);
+			if(index < this.article.contents.length - 1){
+				this.article.contents[index] = this.article.contents.splice(index + 1, 1, this.article.contents[index])[0];
+			}
+		},
+		getData  : function () {
+			return this.article;
+		},
+		setError : function (index) {
+			this.article.contents[index].error = true;
+			this.article.contents.splice(index, 1, this.article.contents[index]);
+		},
+		setDefault : function (article) {
+			this.article = article;
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-editor{padding:10rpx 25rpx; background-color:#FFFFFF; border-radius:6rpx;}
+.gui-editor-title{padding:25rpx 0; width:690rpx; font-size:32rpx; line-height:50rpx; color:#2B2E3D;}
+.gui-editor-empty{line-height:120rpx; font-size:26rpx;}
+.gui-editor-icons{width:80rpx; height:80rpx; color:#898989; line-height:88rpx; text-align:center; font-size:34rpx; margin:5rpx 0;}
+.gui-editor-items{margin-top:20rpx;}
+.gui-editor-item-btns-wrap{padding:20rpx 5rpx;}
+.gui-editor-item-btns{width:100rpx; border-radius:30rpx; margin-left:20rpx; background-color:#898989;}
+.gui-editor-item-btns-text{text-align:center; font-size:20rpx; line-height:38rpx; border-radius:30rpx; color:#FFFFFF;}
+/* #ifndef APP-NVUE */
+.gui-editor-icons{display:block;}
+/* #endif */
+.gui-editor-txt{width:650rpx; font-size:26rpx; line-height:35rpx; padding:20rpx; height:150rpx;}
+.gui-editor-center{width:500rpx; text-align:center; font-size:28rpx; color:#333333; height:60rpx; line-height:60rpx;}
+.gui-editor-img-wrap{width:650rpx; height:320rpx; overflow:hidden; position:relative; font-size:0;}
+.gui-editor-img{width:650rpx; height:320rpx;}
+.gui-editor-quote{width:650rpx; font-size:26rpx; line-height:35rpx; padding:20rpx; background-color:#F9F9F9; height:100rpx;}
+.gui-editor-strong{width:650rpx; font-size:26rpx; line-height:35rpx; padding:20rpx; height:100rpx; font-weight:bold;}
+.gui-editor-link{width:650rpx; font-size:26rpx; line-height:35rpx; padding:20rpx; height:100rpx; color:#008AFF;}
+.gui-editor-spline{width:650rpx; line-height:60rpx; text-align:center; color:#8788A3; font-size:28rpx; opacity:0.6;}
+
+.gui-editor-img-error{position:absolute; width:650rpx; height:320rpx; left:0; top:0; background-color:rgba(0,0,0,0.8);}
+.gui-editor-img-error-text{font-size:28rpx; color:#FFFFFF;}
+</style>

+ 11 - 0
GraceUI5/components/gui-empty.vue

@@ -0,0 +1,11 @@
+<template>
+	<view class="gui-flex gui-columns gui-justify-content-center gui-align-items-center">
+		<slot name="img"></slot>
+		<slot name="text"></slot>
+		<slot name="other"></slot>
+	</view>
+</template>
+<script>
+</script>
+<style scoped>
+</style>

+ 61 - 0
GraceUI5/components/gui-face-group.vue

@@ -0,0 +1,61 @@
+<template>
+	<view class="gui-face-group" 
+	:style="{height:(size + borderWidth * 2 ) +'rpx'}">
+		<view class="gui-face-items" 
+		v-for="(item, index) in items" :key="index" 
+		:style="{'z-index':startIndex + index, 
+			left:isAddBtn ? (space * (index)) + 'rpx' : (space * index) + 'rpx', 
+			borderWidth:borderWidth + 'rpx', borderStyle :'solid ', borderColor:borderColor, 
+			width:size+'rpx', height:size+'rpx'}">
+			<image :src="item" mode="widthFix" class="gui-face-image"
+			:style="{width:size+'rpx', height:size+'rpx'}"></image>
+		</view>
+		<view class="gui-face-items" v-if="isAddBtn"
+		:style="{
+			'z-index':zindex, 
+			width:size+'rpx', 
+			height:size+'rpx', 
+			borderWidth:borderWidth + 'rpx', borderStyle :'solid ', borderColor:borderColor, 
+			left : isAddBtn ? (space * Number(this.items.length)) + 'rpx' : 0,
+			}" @tap.stop="addBtnClick">
+			<slot></slot>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-face-group",
+	props : {
+		items       : { type : Array,   default : function () { return new Array() } },
+		size        : { type : Number,  default : 80 },
+		space       : { type : Number,  default : 50 },
+		borderWidth : { type : Number,  default : 4 },
+		borderColor : { type : String,  default : '#F5F5F5' },
+		isAddBtn    : { type : Boolean, default : false },
+		startIndex  : { type : Number,  default : 100}
+	},
+	data() {
+		return {
+			zindex : 100,
+		}
+	},
+	created:function(){
+		this.zindex     = this.startIndex + this.items.length + 1;
+	},
+	watch:{
+		items : function (v) {
+			this.zindex = this.startIndex + this.items.length + 1;
+		}
+	},
+	methods:{
+		addBtnClick : function (){
+			this.$emit('addBtnClicked');
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-face-group{position:relative;}
+.gui-face-items{position:absolute; overflow:hidden; border-radius:200rpx; font-size:0;}
+.gui-face-image{border-radius:200rpx;}
+</style>

+ 76 - 0
GraceUI5/components/gui-header-leading.vue

@@ -0,0 +1,76 @@
+<template>
+	<view
+	v-if="onlyBack" 
+	class="gui-header-leader" 
+	style="padding:0;">
+		<view class="gui-header-leader-btns" 
+		hover-class="gui-tap">
+			<text 
+			class="gui-header-leader-btns gui-block-text gui-icons gui-primary-color" 
+			hover-class="gui-tap" 
+			@tap="goback" 
+			:style="'text-align:left; '+buttonStyle">&#xe643;</text>
+		</view>
+	</view>
+	<view 
+	v-else-if="onlyHome" 
+	style="padding:0;" 
+	class="gui-header-leader">
+		<view 
+		class="gui-header-leader-btns" 
+		hover-class="gui-tap">
+			<text 
+			class="gui-header-leader-btns gui-block-text gui-icons gui-primary-color" 
+			@tap="gohome" 
+			:style="'text-align:left; font-size:35rpx; '+ buttonStyle">&#xe63b;</text>
+		</view>
+	</view>
+	<view 
+	v-else 
+	class="gui-header-leader gui-flex gui-rows gui-nowrap gui-align-items-center gui-header-buttons-bg gui-border-box" 
+	:style="bgStyle">
+		<view class="gui-header-leader-btns" 
+		hover-class="gui-tap">
+			<text 
+			class="gui-header-leader-btns gui-block-text gui-icons gui-header-buttons-color" 
+			@tap="gohome" 
+			:style="'font-size:35rpx; '+ buttonStyle">&#xe63b;</text>
+		</view>
+		<view 
+		style="margin-left:12rpx;"
+		class="gui-header-leader-btns" 
+		hover-class="gui-tap">
+			<text class="gui-header-leader-btns gui-block-text gui-icons gui-header-buttons-color" 
+			@tap="goback" 
+			:style="buttonStyle">&#xe643;</text>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-header-leading",
+	props : {
+		homePage    : {type:String , default:"/pages/index/index"},
+		bgStyle     : {type:String , default:""},
+		buttonStyle : {type:String , default:""},
+		onlyBack    : {type:Boolean, default:false},
+		onlyHome    : {type:Boolean, default:false}
+	},
+	methods:{
+		goback : function () {
+			uni.navigateBack({delta:1}); 
+			this.$emit('goback');
+		},
+		gohome : function () {
+			if(this.homePage != ''){
+				uni.switchTab({url:this.homePage});
+			}
+			this.$emit('gohome');
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-header-leader{height:55rpx; border-radius:55rpx; overflow:hidden; padding:0 25rpx;}
+.gui-header-leader-btns{width:40rpx; line-height:55rpx; font-size:30rpx; text-align:center; margin:0rpx; overflow:hidden;}
+</style>

File diff suppressed because it is too large
+ 172 - 0
GraceUI5/components/gui-im-input.vue


+ 159 - 0
GraceUI5/components/gui-im-message.vue

@@ -0,0 +1,159 @@
+<template name="gui-im-messages">
+	<view class="gui-im" 
+	:style="{'background-color':background}">
+		<block v-for="(item, index) in msgs" :key="index">
+			<block v-if="item.group == group">
+				<!-- 系统消息 -->
+				<view class="gui-im-msg gui-flex gui-rows gui-justify-content-center" 
+				v-if="item.contentType == 'system'">
+					<view class="gui-im-system-msg gui-bg-black">{{item.msg}}</view>
+				</view>
+				<!-- 用户消息 -->
+				<view v-else>
+					<view class="gui-im-msg" 
+					:class="[userid == item.uindex ? 'gui-im-msg-right' : 'gui-im-msg-left']">
+						<view class="gui-im-face">
+							<image class="gui-im-face-image" :src="item.uface" mode="widthFix"></image>
+						</view>
+						<view class="gui-im-content-spacing"></view>
+						<view class="gui-im-content-in">
+							<view class="gui-im-name" v-if="userid != item.uindex">
+								<text :style="unameStyle">{{item.uname}}</text>
+							</view>
+							<!-- 文本消息 -->
+							<view v-if="item.contentType == 'txt'" 
+							:class="[userid == item.uindex ? 'gui-im-flex-end' : '']">
+								<view class="gui-im-content-txt" 
+								:class="[userid == item.uindex ? 'gui-bg-green' : '']">
+									<text :style="txtContentStyle" 
+									:class="[userid == item.uindex ? 'gui-color-white' : '']">{{item.content}}</text>
+								</view>
+							</view>
+							<!-- 图片消息 -->
+							<view v-if="item.contentType == 'img'" 
+							:class="[userid == item.uindex ? 'gui-im-flex-end' : '']">
+								<view class="gui-img-in gui-im-img">
									<image class="gui-im-img" 
									:src="item.content" :data-img="item.content" 
									@tap="showImgs" mode="widthFix"></image>
								</view>
+							</view>
+							<!-- 语言消息 -->
+							<view v-if="item.contentType == 'voice'" 
+							:class="[userid == item.uindex ? 'gui-im-flex-end' : '']">
+								<view 
+								class="gui-flex gui-rows gui-nowrap gui-align-items-center" 
+								:class="[
+								'gui-im-voice-msg', 
+								index == playIndex ? 'gui-bg-green' : '', 
+								userid != item.uindex ? 'gui-im-flex-end' : '' 
+								]" 
+									:data-voice="item.content" 
+									:data-index='index' @tap='playVoice' 
+									:style="{'width':(item.length*3)+'px'}">
+									<text class="gui-icons" 
+									:class="[index == playIndex ? 'gui-color-white' : '']">&#xe800;</text>
+									<text class="gui-im-voice-msg-text" 
+									:class="[index == playIndex ? 'gui-color-white' : '']">{{item.length}} ”</text> 
+								</view>
+							</view>
+							<view :class="[userid == item.uindex ? 'gui-text-right' : '']">
+								<text class="gui-im-timer gui-block-text">{{item.date}}</text>
+							</view>
+						</view>
+					</view>
+				</view>
+			</block>
+		</block>
+		<view style="height:100px;"></view>
+	</view>
+</template>
+<script>
+export default {
+	name   : "gui-im-messages",
+	props  : {
+		msgs             : { type : Array,  default : function(){ return []; }},
+		userid           : { type : String, default:''},
+		group            : { type : String, default : "" },
+		background       : { type : String, default : "#F7FBFE" },
+		unameStyle       : { type : String, default : 'line-height:28rpx; height:28rpx; font-size:26rpx; color:#666666;'},
+		txtContentStyle  : { type : String, default : 'line-height:38rpx; font-size:30rpx; color:#2B2E3D;'}
+	},
+	data() {
+		return {
+			player    : null,
+			playIndex : -1
+		}
+	},
+	created: function(){
+		this.player = uni.createInnerAudioContext();
+		this.player.onPlay(() => {console.log('play');});
+		this.player.onEnded(() => {
+			var cIndex     = Number(this.playIndex);
+			this.playIndex = -1;
+			for(let i = cIndex + 1; i < this.msgs.length; i++) {
+				if (this.msgs[i].contentType == 'voice') {
+					this.playNow(this.msgs[i].content, i);
+					break;
+					return;
+				}
+			}
+		});
+		this.player.onError((E)=>{console.log(E);});
+  },
+  methods: {
+		// 播放语音
+		playVoice: function (e) {
+			var voicelUrl = e.currentTarget.dataset.voice;
+			var index     = e.currentTarget.dataset.index;
+			if (this.playIndex == -1){
+				return this.playNow(voicelUrl, index);
+			}
+			if (this.playIndex == index) {
+				this.player.stop();
+				this.playIndex = -1;
+			} else {
+				this.player.stop();
+				this.playIndex = -1;
+				this.playNow(voicelUrl, index);
+			}
+		},
+		// 语音播放基础函数
+		playNow: function (voicelUrl, index){
+			this.playIndex  = index;
+			this.player.src = voicelUrl;
+			this.player.play();
+			return true;
+		},
+		// 图片预览
+		showImgs : function(e){
+			var imgs        = [];
+			var imgsCurrent = e.currentTarget.dataset.img;
+			for (var i = 0; i < this.msgs.length; i++) {
+				if (this.msgs[i].contentType == 'img') {
+					imgs.push(this.msgs[i].content);
+				}
+			}
+			uni.previewImage({urls : imgs, current : imgsCurrent});
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-im{padding:30rpx;}
+.gui-im-system-msg{color:#FFFFFF; font-size:26rpx; line-height:38rpx; padding:5px 10px; display:block; border-radius:6rpx;}
+.gui-im-msg{margin-bottom:28px; display:flex; flex-direction:row; flex-wrap:nowrap;}
+.gui-im-face{width:88rpx; height:88rpx; overflow:hidden; flex-shrink:0; border-radius:6rpx; overflow:hidden; font-size:0;}
+.gui-im-face-image{width:88rpx;}
+.gui-im-name{padding-bottom:10rpx; margin-top:-5rpx; }
+.gui-im-content-spacing{width:25rpx; height:50rpx; flex-shrink:0;}
+.gui-im-content-in{width:600rpx; overflow:hidden;}
+.gui-im-content-txt{background-color:#E7F0F3; padding:15rpx 20rpx; border-radius:6rpx; min-width:100rpx; word-break:break-all;}
+.gui-im-msg-right{flex-direction:row-reverse;}
+.gui-im-timer{margin-top:5px; line-height:30rpx; font-size:22rpx; color:#8788A3;}
+.gui-im-img{width:358rpx; border-radius:6rpx;}
+.gui-im-flex-end{display:flex; flex-direction:row; flex-wrap:nowrap; justify-content:flex-end;}
+.gui-im-voice-msg{height:80rpx; padding:0 20rpx; background-color:#E7F0F3; border-radius:6rpx; color:#2B2E3D; min-width:100rpx; max-width:400rpx;}
+.gui-im-voice-msg-text{font-size:22rpx; margin:0 5rpx;}
+@font-face {
+font-family: "guiimfont"; 
+src:url('data:font/ttf;charset=utf-8;base64,d09GRgABAAAAAARIAAsAAAAABpAOfljMqxyszBwQAGoSFheJxjYGRgYADih0telMfz23xl4GZhAIEblhYmCPr/ZxYGZl4gl4OBCSQKAC7VCZ8AeJxjYGRgYG7438AQw8IAAkCSkQEVMAEARGInC9nJQTHaVXQA623EC285A07B4O4UPCXntWPc3ndFsU1P9zhQEC9M9cU7qy0nk6T4E9XxtSdXQrbsuelDSRXs1JErJCXta2VELqATZlV44RelzRiT8oZ0j/AAlabsgAAAB4nGNgYoAALgbsgImRiZGZgTWxNCUzn4EBAAzjAi0AAA==');
+}
+.graceIMFont{font-family:"graceIMFont"; font-size:30rpx; color:#2B2E3D;}
+</style>

+ 56 - 0
GraceUI5/components/gui-image.vue

@@ -0,0 +1,56 @@
+<template>
+	<view class="gui-img" 
+	:style="{width:width+'rpx', height:height == 0 ? imgHeight+'rpx' : height+'rpx'}">
+		<image :src="src" @load="imgLoad" @error="error" 
+		:style="{width:width+'rpx', height:imgHeight+'rpx', borderRadius:borderRadius, opacity:opacity}"></image>
+		<text class="gui-img-loading gui-icons" :class="[animate?'gui-fade-out':'']" v-if="isLoading" 
+		:style="{
+			width:width+'rpx', 
+			height:height == 0 ? imgHeight+'rpx' : height+'rpx', 
+			lineHeight:height == 0 ? imgHeight+'rpx' : height+'rpx', 
+			borderRadius:borderRadius}">&#xe63a;</text>
+		<text class="gui-img-loading gui-icons" :class="[animate?'gui-fade-out':'']" v-if="failed"
+		:style="{
+			width:width+'rpx', 
+			height:height == 0 ? imgHeight+'rpx' : height+'rpx', 
+			lineHeight:height == 0 ? imgHeight+'rpx' : height+'rpx', 
+			borderRadius:borderRadius}">&#xe65b;</text>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-image",
+	props : {
+		src   : {type:String, default:''},
+		width : {type:Number, default:300},
+		height: {type:Number, default:0},
+		timer : {type:Number,default:200},
+		borderRadius:{type:String, default:'0rpx'}
+	},
+	data() {
+		return {
+			isLoading : true,
+			imgHeight : 180,
+			opacity   : 0,
+			animate   : false,
+			failed    : false
+		}
+	},
+	methods:{
+		imgLoad : function (e) {
+			var scale      = e.detail.width / e.detail.height;
+			this.imgHeight = this.width / scale;
+			this.animate = true;
+			setTimeout(() => {this.isLoading = false; this.opacity = 1;}, this.timer);
+		},
+		error : function(){
+			this.isLoading = false;
+			this.failed    = true;
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-img{width:500rpx; height:500rpx; overflow:hidden; font-size:0; position:relative;}
+.gui-img-loading{position:absolute; left:0; top:0; background-color:#F8F8F8; font-size:58rpx; color:#D9D9D9; text-align:center;}
+</style>

+ 166 - 0
GraceUI5/components/gui-interval-slider.vue

@@ -0,0 +1,166 @@
+<template>
+	<view 
+	class="grace-slider" 
+	:style="{
+		height:blockSize+'px', width:realWidth+'px'
+	}" 
+	@touchmove.stop.prevent="touchmove">
+		<view 
+		class="grace-slider-bar" 
+		:style="{
+			backgroundColor:bgColor, 
+			width:realWidth+'px', 
+			height:height+'px', 
+			top:lineTop+'px'
+		}"></view>
+		<view 
+		class="grace-slider-active-bar" 
+		:style="{
+			backgroundColor:activeColor, 
+			width:activeWidth+'px', 
+			height:height+'px', 
+			marginLeft:activeLeft+'px', 
+			top:lineTop+'px'
+		}"></view>
+		<view 
+		class="grace-slider-block" 
+		:style="{
+			backgroundColor:blockBgColor, 
+			height:blockSize+'px', 
+			width:blockSize+'px', 
+			'margin-left':xMin+'px'
+		}" 
+		@touchstart.stop="touchstart" 
+		data-tag="min"></view>
+		<view 
+		class="grace-slider-block" 
+		:style="{
+			backgroundColor:blockBgColor, 
+			height:blockSize+'px', 
+			width:blockSize+'px', 
+			'margin-left':xMax+'px'
+		}" 
+		@touchstart.stop="touchstart" 
+		data-tag="max"></view>
+	</view>
+</template>
+<script>
+const _windowWidth = uni.getSystemInfoSync().windowWidth;
+export default {
+	name : "gui-interval-slider",
+	data() {
+		return {
+			realWidth    : 200,
+			realMax      : 200,
+			xMin         : 0,
+			left1        : 0,
+			xMax         : 100,
+			currentBlock : '',
+			minValue     : 0,
+			maxValue     : 100,
+			activeWidth  : 0,
+			activeLeft   : 0,
+			lineTop      : 0
+		};
+	},
+	props: {
+		bgColor      : {type : String, default : "#F6F7F8"},
+		activeColor  : {type : String, default : "#3688FF"},
+		width        : {type : Number, default : 750},
+		height       : {type : Number, default : 2},
+		blockBgColor : {type : String, default : "#FFFFFF"},
+		blockSize    : {type : Number, default : 32},
+		min          : {type : Number, default : 0},
+		minDefault   : {type : Number, default : 0},
+		max          : {type : Number, default : 100},
+		maxDefault   : {type : Number, default : 100}
+	},
+	created:function(){
+		this.realWidth = this.upx2px(this.width);
+		this.left1     = (_windowWidth - this.realWidth) / 2;
+		this.realMax   = this.realWidth - this.blockSize;
+		this.lineTop   = (this.blockSize - this.height) / 2;
+		this.setSlider();
+	},
+	watch:{
+		minDefault : function(){
+			this.setSlider();
+		},
+		maxDefault : function(){
+			this.setSlider();
+		}
+	},
+	methods: {
+		setSlider : function(){
+			this.xMin = (this.minDefault - this.min) / (this.max - this.min) * this.realMax;
+			this.xMax = (this.maxDefault - this.min) / (this.max - this.min) * this.realMax;
+			this.minValue = this.minDefault;
+			this.maxValue = this.maxDefault;
+			this.activeLeft = this.xMin + 5;
+			this.activeWidth = this.xMax - this.xMin;
+		},
+		touchstart: function(e) {
+			this.currentBlock = e.target.dataset.tag;
+			if(this.currentBlock == 'min'){
+				var pageX = e.pageX || e.changedTouches[0].pageX;
+				pageX = pageX - this.left1;
+				this.xMin = pageX;
+			}else{
+				var pageX = e.pageX || e.changedTouches[0].pageX;
+				pageX = pageX - this.left1;
+				this.xMax = pageX;
+			}
+		},
+		touchmove: function(e) {
+			this.calculate(e);
+		},
+		px2upx: function(px) {
+			return (750 / _windowWidth ) * px;
+		},
+		upx2px : function(upx){
+			return (_windowWidth / 750) * upx;
+		},
+		calculate: function(e) {
+			var pageX = e.pageX || e.changedTouches[0].pageX;
+			pageX = pageX - this.left1;
+			if(this.currentBlock == 'min'){
+				var xMin = this.xMin + (pageX - this.xMin);
+				// 最左侧限制
+				if(xMin < 0){xMin = 0;}
+				// 最右侧限制
+				if(xMin >= this.realMax){xMin = this.realMax;}
+				this.xMin = xMin;
+				// 计算值
+				var value = this.xMin / this.realMax * (this.max - this.min);
+				value = parseInt(value);
+				value = value + this.min;
+				this.minValue = value;
+			}else{
+				var xMax = this.xMax + (pageX - this.xMax);
+				// 最左侧限制
+				if(xMax < 0){xMax = 0;}
+				// 最右侧限制
+				if(xMax >= this.realMax){xMax = this.realMax;}
+				this.xMax = xMax;
+				// 计算值
+				var value = this.xMax / this.realMax * (this.max - this.min);
+				value = parseInt(value);
+				value = value + this.min;
+				this.maxValue = value;
+			}
+			// 获得2个值并传递出去
+			if(this.maxValue >= this.minValue){
+				this.$emit('change', [this.minValue, this.maxValue]);
+			}else{
+				this.$emit('change', [this.maxValue, this.minValue]);
+			}
+		}
+	}
+};
+</script>
+<style scoped>
+.grace-slider{position:relative;}
+.grace-slider-bar{position:absolute; top:0rpx;}
+.grace-slider-block{position:absolute; top:0rpx; left:0; border-radius:500px; border-width:1px; border-style:solid; border-color:#F5F6F8;}
+.grace-slider-active-bar{position:absolute; top:25rpx;}
+</style>

+ 42 - 0
GraceUI5/components/gui-iphone-bottom.vue

@@ -0,0 +1,42 @@
+<template>
+	<view :style="{height:height}" v-if="need"></view>
+</template>
+<script>
+export default{
+	name  : "gui-iphone-bottom",
+	props : {
+		height       : {type:String,  default:'50rpx'},
+		isSwitchPage : {type:Boolean, default:false}
+	},
+	data() {
+		return {
+			need:false 
+		}
+	},
+	created:function(){
+		// #ifdef MP
+		this.setBottom();
+		// #endif
+		// #ifdef H5
+		this.setBottom();
+		// #endif
+	},
+	methods:{
+		setBottom : function () {
+			if(this.isSwitchPage){return ;}
+			var system   = uni.getSystemInfoSync();
+			system.model = system.model.replace(' ', '');
+			system.model = system.model.toLowerCase();
+			var res1     = system.model.indexOf('iphonex');
+			if(res1 > 5){res1 = -1;}
+			var res2     = system.model.indexOf('iphone1');
+			if(res2 > 5){res2 = -1;}
+			if(res1 != -1 || res2 != -1){
+				this.need = true;
+			}
+		}
+	}
+}
+</script>
+<style scoped>
+</style>

+ 51 - 0
GraceUI5/components/gui-link.vue

@@ -0,0 +1,51 @@
+<template>
+	<!-- #ifdef APP-PLUS -->
+	<text class="link" :data-url="url" @tap="openUrlForApp" 
+	:style="{color:color, lineHeight:lineHeight, fontSize:fontSize}">{{title}}</text>
+	<!-- #endif -->
+	<!-- #ifdef H5 -->
+	<a :href="url" class="link" target="_blank" 
+	:style="{color:color, lineHeight:lineHeight, fontSize:fontSize}">{{title}}</a>
+	<!-- #endif -->
+	<!-- #ifdef MP -->
+	<text class="link" :style="{color:color, lineHeight:lineHeight, fontSize:fontSize}">{{url}}</text>
+	<!-- #endif -->
+</template>
+<script>
+export default {
+	name  : "gui-link",
+	props : {
+		url:{
+			type : String,
+			default : ""
+		},
+		title : {
+			type : String,
+			default : ""
+		},
+		color:{
+			type : String,
+			default : "#008AFF"
+		},
+		fontSize : {
+			type : String,
+			default : "28rpx"
+		},
+		lineHeight : {
+			type : String,
+			default : "50rpx"
+		}
+	},
+	methods:{
+		openUrlForApp : function(e){
+			var link = e.currentTarget.dataset.url;
+			plus.runtime.openURL(link);
+		}
+	}
+}
+</script>
+<style scoped>
+/* #ifdef H5 */
+.link{text-decoration:none;}
+/* #endif */
+</style>

+ 92 - 0
GraceUI5/components/gui-loadmore.vue

@@ -0,0 +1,92 @@
+<template>
+	<view 
+	class="gui-load-more gui-flex gui-rows gui-align-items-center gui-justify-content-center" 
+	v-if="!hidden" 
+	@tap.stop.prevent="tapme">
+		<view 
+		class="gui-load-more-icon" 
+		ref="loadingiconforloadmore" 
+		v-if="loadMoreStatus == 1">
+			<text 
+			class="gui-icons gui-rotate360 gui-block-text"
+			:style="{
+				fontSize:loadMoreFontSize,
+				color:loadMoreColor[loadMoreStatus]}">&#xe9db;</text>
+		</view>
+		<text 
+		class="gui-block-text" 
+		:style="{
+			fontSize:loadMoreFontSize, 
+			color:loadMoreColor[loadMoreStatus]
+		}">{{loadMoreText[loadMoreStatus]}}</text>
+	</view>
+</template>
+<script>
+// #ifdef APP-NVUE
+var animation = weex.requireModule('animation');
+// #endif
+export default{
+	name  : "gui-loadmore",
+	props : {
+		loadMoreText     : {type:Array, default:function () {
+			return ['','更多数据加载中', '已加载全部数据', '暂无数据'];
+		}},
+		loadMoreColor    : {type:Array, default:function () {
+			return ['rgba(69, 90, 100, 0.6)', 'rgba(69, 90, 100, 0.6)', 'rgba(69, 90, 100, 0.8)', 'rgba(69, 90, 100, 0.6)'];
+		}},
+		loadMoreFontSize : {type:String, default:'26rpx'},
+		status           : {type:Number, default:0},
+	},
+	data() {
+		return {
+			loadMoreStatus : 0,
+			hidden         : false
+		}
+	},
+	created:function(){
+		this.loadMoreStatus = this.status;
+	},
+	methods:{
+		loading    : function(){
+			this.loadMoreStatus = 1;
+			// #ifdef APP-NVUE
+			setTimeout(()=>{
+				this.rotate360();
+			}, 200);
+			// #endif
+		},
+		stoploadmore : function(){
+			this.loadMoreStatus = 0;
+		},
+		nomore : function(){
+			this.loadMoreStatus = 2;
+		},
+		empty  : function(){
+			this.loadMoreStatus = 3;
+		},
+		hide   : function(){
+			this.hidden = true;
+		},
+		rotate360 : function(){
+			var el = this.$refs.loadingiconforloadmore;
+			animation.transition(el, {
+				styles     : {transform: 'rotate(7200deg)'},
+				duration   : 20000,
+				timingFunction: 'linear',
+				needLayout :false,
+				delay: 0
+			});
+		},
+		tapme : function(){
+			if(this.loadMoreStatus == 0){
+				this.$emit('tapme');
+			}
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-load-more{overflow:hidden; padding:25rpx;}
+.gui-load-more-text{line-height:35rpx;} 
+.gui-load-more-icon{padding:0 12rpx; line-height:35rpx;}
+</style>

+ 60 - 0
GraceUI5/components/gui-modal.vue

@@ -0,0 +1,60 @@
+<template>
+	<gui-popup ref="guipopupformodal" 
+	:width="width" 
+	:canCloseByShade="canCloseByShade" 
+	:zIndex="zIndex" 
+	@close="eClose">
+		<view 
+		:style="bodyStyle" 
+		@tap.stop.prevent="stopfun" 
+		class="gui-relative">
+			<view>
+				<text class="gui-block-text gui-text-center" v-if="isTitle"
+				:style="titleStyle">{{title}}</text>
+			</view>
+			<view><slot name="content"></slot></view>
+			<view><slot name="btns"></slot></view>
+			<text 
+			class="gui-popup-close gui-block-text gui-absolute-rt gui-icons"
+			:style="closeBtnStyle" 
+			@tap.stop.prevent="close" 
+			v-if="isCloseBtn">&#xe7a5;</text>
+		</view>
+	</gui-popup>
+</template>
+<script>
+export default{
+	name  : "gui-modal",
+	props : {
+		width           : { type  : String,  default : '580rpx'},
+		bodyStyle       : { type  : String,  default : 'background-color:#FFFFFF; border-radius:6rpx;' },
+		isCloseBtn      : { type  : Boolean, default : true },
+		closeBtnStyle   : { type  : String,  default : 'color:#2B2E3D; font-size:28rpx;' },
+		isTitle         : { type  : Boolean, default : true },
+		title           : { type  : String,  default : 'title' },
+		titleStyle      : { type  : String,  default : 'line-height:100rpx; font-size:28rpx; font-weight:700; color:#2B2E3D;' },
+		canCloseByShade : { type  : Boolean, default : true },
+		zIndex          : { type  : Number,  default : 99 }
+	},
+	methods:{
+		open  : function(){
+			this.$refs.guipopupformodal.open();
+			this.$emit('open');
+		},
+		close : function(){
+			this.$refs.guipopupformodal.close();
+			this.$emit('close');
+		},
+		stopfun : function(e){
+			e.stopPropagation();
+			return null;
+		},
+		eClose : function(){
+			this.$emit('close');
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-popup-close{width:80rpx; height:80rpx; line-height:80rpx; text-align:center;}
+</style>

+ 26 - 0
GraceUI5/components/gui-mp-menu-spacing.vue

@@ -0,0 +1,26 @@
+<template>
+	<view class="gui-wx-menu-spacing" :style="{width:width+'px'}"></view>
+</template>
+<script>
+export default{
+	name  : "gui-mp-menu-spacing",
+	props : {},
+	data() {
+		return {
+			width: 0
+		}
+	},
+	created:function(){
+		// #ifdef MP
+			// #ifndef MP-ALIPAY
+			let system         = uni.getSystemInfoSync();
+			let menuButtonInfo = uni.getMenuButtonBoundingClientRect();
+			this.width         = menuButtonInfo.width + (system.screenWidth - menuButtonInfo.right);
+			// #endif
+		// #endif
+	}
+}
+</script>
+<style scoped>
+.gui-wx-menu-spacing{width:80px; height:20px;}
+</style>

+ 66 - 0
GraceUI5/components/gui-number-animate.vue

@@ -0,0 +1,66 @@
+<template>
+	<text :style="{
+		fontSize:fontSize, 
+		fontWeight:fontWeight, 
+		color:color, 
+		lineHeight:lineHeight
+	}">{{numAnimate}}</text>
+</template>
+<script>
+export default{
+	name  : "gui-number-animate",
+	props : {
+		num        : { type : Number,  default : 0},
+		stepNumber : { type : Number,  default : 50 },
+		timer      : { type : Number,  default : 1000 },
+		keepInt    : { type : Boolean, default : false },
+		fontSize   : { type : String,  default : '28rpx' },
+		color      : { type : String,  default : '#333333' },
+		lineHeight : { type : String,  default : '50rpx' },
+		fontWeight : { type : String,  default : '400' }
+	},
+	data() {
+		return {
+			numAnimate : 0,
+			intervalId : null
+		}
+	},
+	created:function(){
+		if(this.num != 0){this.run();}
+	},
+	watch:{
+		num : function(val){
+			if(this.intervalId != null){clearInterval(this.intervalId);}
+			this.run();
+		}
+	},
+	methods:{
+		run : function(){
+			let timer = this.timer / this.stepNumber;
+			let step  = Math.floor((this.num / this.stepNumber) * 100) / 100;
+			this.intervalId = setInterval(() => {
+				// 正值 
+				if(this.num >= 0){
+					if(this.numAnimate >= this.num){
+						this.numAnimate = this.num;
+						clearInterval(this.intervalId);
+						this.$emit('done');
+						return;
+					}
+				}else{
+					if(this.numAnimate <= this.num){
+						this.numAnimate = this.num;
+						clearInterval(this.intervalId);
+						this.$emit('done');
+						return;
+					}
+				}
+				let  res = this.numAnimate + step;
+				this.numAnimate = this.keepInt ? parseInt(res) : Math.floor(res * 100) / 100;
+			}, timer);
+		}
+	}
+}
+</script>
+<style scoped>
+</style>

+ 120 - 0
GraceUI5/components/gui-number-keyboard.vue

@@ -0,0 +1,120 @@
+<template>
+	<gui-popup ref="guipopupfornk"
+	position="bottom" :canCloseByShade="canCloseByShade">
+		<view style="background-color:#F6F7F8;" 
+		@tap.stop.prevent="stopfun">
+			<text class="gui-keyboard-res gui-block-text" v-if="showInputRes" 
+			:style="{color:resultColor, fontSize:resultSize}">{{resVal}}</text>
+			<view class="gui-keyboard gui-flex gui-rows gui-space-between">
+				<view class="gui-keyboard-left gui-flex gui-rows gui-wrap gui-space-between">
+					<text v-for="(item, index) in [1,2,3,4,5,6,7,8,9]" :key="index" 
+					class="gui-keyboard-keys gui-block-text" 
+					:data-keynumber="item" 
+					:style="{
+						backgroundColor:tapIndex == item ? tapBgColor : '#FFFFFF', 
+						color : tapIndex == item ? '#FFFFFF' : '#2B2E3D'
+					}" 
+					@tap.stop="inputNow">{{item}}</text>
+					<text class="gui-keyboard-keys gui-block-text" 
+					:style="{
+						width: isPoint ? '259rpx':'538rpx',
+						backgroundColor:tapIndex == 0 ? tapBgColor : '#FFFFFF',
+						color : tapIndex == 0 ? '#FFFFFF' : '#2B2E3D'
+					}" 
+					data-keynumber="0" @tap.stop="inputNow">0</text>
+					<text v-if="isPoint" class="gui-keyboard-keys" 
+					:style="{
+						width:'259rpx',
+						backgroundColor:tapIndex == '.' ? tapBgColor : '#FFFFFF',
+						color : tapIndex == '.' ? '#FFFFFF' : '#2B2E3D'
+					}" 
+					data-keynumber="." @tap.stop="inputNow">.</text>
+				</view>
+				<view class="gui-keyboard-right gui-flex gui-columns gui-align-items-center">
+					<text class="gui-keyboard-keys gui-icons gui-block-text" 
+					:style="{
+						backgroundColor : tapIndex == -3 ? tapBgColor : '#FFFFFF',
+						color : tapIndex == -3 ? '#FFFFFF' : '#2B2E3D'
+					}" 
+					:data-keynumber="-3" @tap.stop="deleteNow">&#xe623;</text>
+					<text class="gui-keyboard-keys gui-keyboard-done gui-block-text" 
+					:style="{backgroundColor:tapBgColor}" @tap.stop="done">{{doneBtnName}}</text>
+				</view>
+			</view>
+		</view>
+	</gui-popup>
+</template>
+<script>
+export default{
+	name  : "gui-number-keyboard",
+	props : {
+		canCloseByShade : { type : Boolean, default : false},
+		doneBtnName     : { type : String,  default : "完成" },
+		isPoint         : { type : Boolean, default : true },
+		value           : { type : String,  default : ''},
+		showInputRes    : { type : Boolean, default : true},
+		resultColor     : { type : String,  default : '#2B2E3D'},
+		resultSize      : { type : String,  default : '32rpx'},
+		tapBgColor      : { type : String,  default : '#008AFF'},
+	},
+	methods:{
+		open  : function(){
+			this.$refs.guipopupfornk.open();
+		},
+		close : function(){
+			this.$refs.guipopupfornk.close();
+		},
+		stopfun : function(e){
+			e.stopPropagation();
+			return ;
+		},
+		inputNow : function (e){
+			var k = e.currentTarget.dataset.keynumber;
+			this.resVal += k+'';
+			this.tapIndex = k;
+			this.$emit('keyboardInput', k);
+			this.removeClass();
+		},
+		deleteNow : function (e){
+			var k = e.currentTarget.dataset.keynumber;
+			this.tapIndex = k;
+			this.resVal = this.resVal.substring(0, this.resVal.length - 1);
+			this.$emit('keyboardDelete');
+			this.removeClass();
+		},
+		done : function(){
+			this.$emit('keyboardDone');
+		},
+		setVal : function (val) {
+			this.resVal = val;
+		},
+		removeClass : function () {
+			setTimeout(()=>{this.tapIndex = -1;}, 100);
+		},
+	},
+	data() {
+		return {
+			resVal   : '',
+			tapIndex : -1
+		}
+	},
+	created:function(){
+		this.resVal = this.value+'';
+	},
+	watch:{
+		value : function (val) {
+			this.resVal = val+'';
+		}
+	}
+}
+</script>
+<style>
+.gui-keyboard{background-color:#F6F7F8; width:750rpx; padding:20rpx 0;}
+.gui-keyboard-body{flex-direction:row; justify-content:space-between; padding:10px 0;}
+.gui-keyboard-left{width:560rpx;}
+.gui-keyboard-right{width:188rpx;}
+.gui-keyboard-keys{width:166rpx; height:100rpx; margin:10rpx; background-color:#FFFFFF; text-align:center; line-height:100rpx; border-radius:8rpx; font-weight:700; font-size:50rpx;}
+.gui-keyboard-done{height:340rpx; line-height:340rpx; font-size:36rpx; font-weight:400; color:#FFFFFF;}
+.gui-keyboard-res{line-height:60rpx; text-align:center; font-size:32rpx; font-weight:bold; padding-top:20rpx;}
+.keydown{background-color:#3688FF; color:#FFFFFF;}
+</style>

+ 74 - 0
GraceUI5/components/gui-order.vue

@@ -0,0 +1,74 @@
+<template>
+	<view  @tap="changeOrderBy" 
+	class="gui-flex gui-rows gui-nowrap gui-align-items-center gui-justify-content-center">
+		<view><slot></slot></view>
+		<view v-if="orderByIn == 0" class="gui-order gui-flex gui-columns">
+			<text class="gui-block-text gui-icons gui-order-icon" 
+			:style="{width:(size+10)+'rpx', height:(size)+'rpx', 
+			lineHeight:(size)+'rpx', fontSize:fontSize+'rpx', color:color}">&#xe647;</text>
+			<text class="gui-block-text gui-icons gui-order-icon" 
+			:style="{width:(size+10)+'rpx', height:(size)+'rpx', 
+			lineHeight:(size)+'rpx', fontSize:fontSize+'rpx', color:color}">&#xe661;</text>
+		</view>
+		<view v-if="orderByIn == 1" class="gui-order gui-flex gui-columns">
+			<text class="gui-block-text gui-icons gui-order-icon" 
+			:style="{width:(size+10)+'rpx', height:(size)+'rpx', 
+			lineHeight:(size)+'rpx', fontSize:fontSize+'rpx', color:activeColor}">&#xe647;</text>
+			<text class="gui-block-text gui-icons gui-order-icon" 
+			:style="{width:(size+10)+'rpx', height:(size)+'rpx', 
+			lineHeight:(size)+'rpx', fontSize:fontSize+'rpx', color:color}">&#xe661;</text>
+		</view>
+		<view v-if="orderByIn == 2" class="gui-order gui-flex gui-columns">
+			<text class="gui-block-text gui-icons gui-order-icon" 
+			:style="{width:(size+10)+'rpx', height:(size)+'rpx', 
+			lineHeight:(size)+'rpx', fontSize:fontSize+'rpx', color:color}">&#xe647;</text>
+			<text class="gui-block-text gui-icons gui-order-icon" 
+			:style="{width:(size+10)+'rpx', height:(size)+'rpx', 
+			lineHeight:(size)+'rpx', fontSize:fontSize+'rpx', color:activeColor}">&#xe661;</text>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-order",
+	props : {
+		size        : {type:Number, default:18},
+		fontSize    : {type:Number, default:36},
+		color       : {type:String, default:'rgba(69, 90, 100, 0.3)'},
+		activeColor : {type:String, default:'#FF0036'},
+		orderBy     : {type:Number, default:0},
+		limit       : {type:Array,  default:function(){return [0,2];}}
+	},
+	data() {
+		return {
+			orderByIn : 0
+		}
+	},
+	created:function(){
+		this.orderByIn = this.orderBy;
+		this.init();
+	},
+	watch:{
+		orderBy : function(v){
+			console.log(v);
+			this.orderByIn = v;
+			this.init();
+		}
+	},
+	methods:{
+		changeOrderBy:function(){
+			this.orderByIn++;
+			if(this.orderByIn > this.limit[1]){this.orderByIn = this.limit[0];}
+			this.$emit('change', this.orderByIn);
+		},
+		init:function(){
+			if(this.orderByIn < 0){this.orderByIn = 0;}
+			else if(this.orderByIn > 2){this.orderByIn = 2;}
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-order{padding:0 15rpx;}
+.gui-order-icon{text-align:center; overflow:hidden;}
+</style>

+ 165 - 0
GraceUI5/components/gui-page-loading.vue

@@ -0,0 +1,165 @@
+<template>
+	<view 
+	class="gui-page-loading gui-flex gui-nowrap gui-align-items-center gui-justify-content-center gui-page-loading-bg"
+	@tap.stop="stopfun" 
+	@touchmove.stop.prevent="stopfun" 
+	v-if="isLoading">
+		<!-- #ifndef APP-NVUE -->
+		<view class="gui-page-loading-point gui-flex gui-rows gui-justify-content-center">
+			<view class="gui-page-loading-points animate1 gui-page-loading-color"></view>
+			<view class="gui-page-loading-points animate2 gui-page-loading-color"></view>
+			<view class="gui-page-loading-points animate3 gui-page-loading-color"></view>
+		</view>
+		<!-- #endif -->
+		<!-- #ifdef APP-NVUE -->
+		<view class="gui-page-loading-point gui-flex gui-rows gui-justify-content-center">
+			<view class="gui-page-loading-points gui-page-loading-color" ref="loadingPoints1"></view>
+			<view class="gui-page-loading-points gui-page-loading-color" ref="loadingPoints2"></view>
+			<view class="gui-page-loading-points gui-page-loading-color" ref="loadingPoints3"></view>
+		</view>
+		<!-- #endif -->
+	</view>
+</template>
+<script>
+// #ifdef APP-NVUE
+const BindingX = uni.requireNativePlugin('bindingx');
+// #endif
+export default{
+	name  : "gui-page-loading",
+	props : {},
+	data() {
+		return {
+			isLoading      : false,
+			BindingXObjs   : [null,null,null],
+			AnimateObjs    : [null,null,null],
+			animateTimer   : 800,
+			intervalID     : null
+		}
+	},
+	watch:{
+		// #ifdef APP-NVUE
+		isLoading : function (val) {
+			if(val){
+				setTimeout(()=>{
+					this.getRefs('loadingPoints1', 0, (refs)=>{
+						this.BindingXObjs = [
+							refs.ref,
+							this.$refs.loadingPoints2.ref,
+							this.$refs.loadingPoints3.ref
+						];
+						this.startAnimate();
+					});
+				}, 100);
+				this.intervalID = setInterval(()=>{
+					if(this.isLoading){
+						this.startAnimate();
+					}else{
+						clearInterval(this.intervalID);
+					}
+				}, 2000);
+			}
+		}
+		// #endif
+	},
+	methods:{
+		// #ifdef APP-NVUE
+		startAnimate   : function(){
+			this.loadingAnimate(0);
+			setTimeout(()=>{this.loadingAnimate(1);},300);
+			setTimeout(()=>{this.loadingAnimate(2);},600);
+		},
+		loadingAnimate : function (id) {
+			this.AnimateObjs[id] = BindingX.bind({
+				eventType      : 'timing',
+				exitExpression : 't>'+this.animateTimer,
+				props          : [
+					{
+						element    : this.BindingXObjs[id], 
+						property   : 'transform.scale',
+						expression : "1+t/"+this.animateTimer+"/3"
+					},
+					{
+						element    : this.BindingXObjs[id], 
+						property   : 'opacity',
+						expression : "0.6+t/"+this.animateTimer
+					}
+				]
+			}, (e)=>{
+				if(e.state === 'exit') {
+					BindingX.unbind({
+						token : this.AnimateObjs[id].token,
+						eventType: 'timing'
+					});
+					this.AnimateObjs[id] = BindingX.bind({
+						eventType      : 'timing',
+						exitExpression : 't>'+this.animateTimer,
+						props          : [
+							{
+								element    : this.BindingXObjs[id], 
+								property   : 'transform.scale',
+								expression : "1.35-t/"+this.animateTimer+"/3"
+							},
+							{
+								element    : this.BindingXObjs[id], 
+								property   : 'opacity',
+								expression : "1.6-t/"+this.animateTimer
+							}
+						]
+					}, (e)=>{
+						if(e.state === 'exit') {
+							BindingX.unbind({
+								token : this.AnimateObjs[id].token,
+								eventType: 'timing'
+							});
+						}
+					});
+				}
+			});
+		},
+		// #endif
+		stopfun        : function(e){e.stopPropagation(); return null;},
+		open           : function(){ this.isLoading = true; },
+		close          : function(){
+			setTimeout(()=>{
+				this.isLoading = false; 
+			},100);
+		},
+		getRefs : function(ref, count, fun){
+			if(count >= 50){
+				fun(this.$refs[ref]);
+				return false;
+			}
+			var refReturn = this.$refs[ref];
+			if(refReturn){
+				// #ifdef APP-NVUE
+				fun(refReturn);
+				return;
+				// #endif
+				// #ifndef APP-NVUE
+				if(refReturn._data){
+					fun(refReturn);
+					return;
+				}
+				// #endif
+			}else{
+				count++;
+				setTimeout(()=>{
+					this.getRefs(ref, count, fun);
+				}, 100);
+			}
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-page-loading{width:750rpx; position:fixed; left:0; top:0; bottom:0; flex:1; z-index:99999;}
+.gui-page-loading-points{width:20rpx; height:20rpx; border-radius:50rpx; margin:10rpx; opacity:0.5;}
+/* #ifndef APP-NVUE */
+@keyframes pageLoading1{0% {opacity:0.5; transform:scale(1);} 40% {opacity:1; transform:scale(1.5);}  60%{opacity:0.5; transform:scale(1);}}
+@keyframes pageLoading2{20% {opacity:0.5; transform:scale(1);} 60% {opacity:1; transform:scale(1.5);}  80% {opacity:0.5; transform:scale(1);}}
+@keyframes pageLoading3{40% {opacity:0.5; transform:scale(1);} 80% {opacity:1; transform:scale(1.5);}  100% {opacity:0.5; transform:scale(1);}}
+.animate1{animation:pageLoading1 1.2s infinite linear;}
+.animate2{animation:pageLoading2 1.2s infinite linear;}
+.animate3{animation:pageLoading3 1.2s infinite linear;}
+/* #endif */
+</style>

+ 430 - 0
GraceUI5/components/gui-page.vue

@@ -0,0 +1,430 @@
+<template>
+	<view :class="[
+		'gui-flex', 'gui-columns', 'gui-sbody', 
+		fullPage ? 'gui-flex1':'' , 
+		refresh || loadmore ? 'gui-flex1' : ''
+	]">
+		<!-- 自定义头部 -->
+		<view 
+		class="gui-header gui-transition-all" 
+		v-if="customHeader" 
+		id="guiPageHeader" 
+		ref="guiPageHeader" 
+		:style="'height:' 
+		+(headerSets.height+statusBarHeight)+'px; z-index:'
+		+headerSets.zIndex+';'+headerStyle">
+			<!-- 状态栏 -->
+			<view 
+			class="gui-page-status-bar" 
+			:style="'height:'+statusBarHeight+'px;'+statusBarStyle"></view>
+			<!-- 头部插槽 -->
+			<view 
+			class="gui-flex gui-columns gui-justify-content-center" 
+			@tap.stop.prevnet="headerTap" 
+			:style="{height:headerSets.height+'px'}">
+				<slot name="gHeader"></slot>
+			</view>
+		</view>
+		<!-- 自定义头部占位 -->
+		<view 
+		v-if="customHeader && isHeaderSized"
+		:style="'height:'+(headerSets.height+statusBarHeight)+'px; '+ headerSizedStyle + ';'"></view>
+		<!-- 页面主体 -->
+		<view 
+		class="gui-flex gui-columns" 
+		v-if="!refresh && !loadmore" 
+		id="guiPageBody" 
+		ref="guiPageBody" 
+		:class="[fullPage?'gui-flex1':'']">
+			<slot name="gBody"></slot>
+		</view>
+		<!-- 刷新加载主体 -->
+		<view class="gui-flex gui-columns gui-flex1" 
+		v-if="refresh || loadmore" 
+		id="guiPageBody" 
+		ref="guiPageBody" 
+		:style="{
+			marginTop:fixedTopMargin+'px', 
+			height:refreshBodyHeight+'px'
+		}">
+			<scroll-view 
+			class="gui-relative" 
+			:scroll-y="true" 
+			:show-scrollbar="false" 
+			:style="{
+				height:refreshBodyHeight+'px',
+				opacity:refreshBodyHeight < 1 ? 0 : 1
+			}" 
+			@touchstart="touchstart" 
+			@touchmove="touchmove" 
+			@touchend="touchend" 
+			@scroll="scroll" 
+			:scroll-top="scrollTop" 
+			@scrolltolower="loadmorefun">
+				<view>
+					<gui-refresh 
+					ref="guiPageRefresh" 
+					@reload="reload"
+					:refreshText="refreshText" 
+					:refreshBgColor="refreshBgColor" 
+					:refreshColor="refreshColor" 
+					:refreshFontSize="refreshFontSize"></gui-refresh>
+				</view>
+				<slot name="gBody"></slot>
+				<view 
+				v-if="loadmore" 
+				class="gui-page-loadmore">
+					<gui-loadmore 
+					ref="guipageloadmore" 
+					:status="loadMoreStatus" 
+					:loadMoreText="loadMoreText" 
+					:loadMoreColor="loadMoreColor" 
+					:loadMoreFontSize="loadMoreFontSize"></gui-loadmore>
+				</view>
+			</scroll-view>
+		</view>
+		<!-- 页面底部 -->
+		<!-- 底部占位 -->
+		<view v-if="customFooter" 
+		:style="{height:footerHeight}"></view>
+		<view class="gui-page-footer gui-border-box" 
+		:class="[isSwitchPage?'gui-switch-page-footer':'']" 
+		v-if="customFooter" 
+		id="guiPageFooter" 
+		ref="guiPageFooter" 
+		:style="{
+			height:footerHeight, 
+			'background-image':footerSets.bg, 
+			'z-index':footerSets.zIndex
+		}">
+			<view>
+				<slot name="gFooter"></slot>
+			</view>
+			<view 
+			:style="'height:'+iphoneXButtomHeight+'; '+ iphoneXButtomStyle"></view>
+		</view>
+		<!-- 右下角悬浮挂件 -->
+		<view 
+		class="gui-page-pendant" 
+		:style="{
+			right:pendantSets.right, bottom:pendantSets.bottom, 
+			width:pendantSets.width, zIndex:pendantSets.zIndex}">
+			<slot name="gPendant"></slot>
+		</view>
+		<!-- 吸顶元素 -->
+		<view 
+		class="gui-page-fixed-top" 
+		ref="guiPageFixedTop" 
+		id="guiPageFixedTop" 
+		:style="{
+			top:fixedTop+'px', 
+			zIndex:fixedTopZIndex
+		}">
+			<slot name="gFixedTop"></slot>
+		</view>
+		<!-- 全屏 loading -->
+		<gui-page-loading ref="guipageloading"></gui-page-loading>
+	</view>
+</template>
+<script>  
+// #ifdef APP-NVUE
+const dom = weex.requireModule('dom');
+// #endif
+export default{
+	name  : 'gui-page',
+	props : {
+		fullPage           : {type:Boolean, default:false},
+		customHeader       : {type:Boolean, default:false},
+		headerSets         : {type:Object , default:function(){return {height:44, zIndex:100}}},
+		headerStyle        : {type:String , default:'background-color:#FFFFFF;'},
+		isHeaderSized      : {type:Boolean, default:true},
+		statusBarStyle     : {type:String , default:'background-color:#FFFFFF;'},
+		customFooter       : {type:Boolean, default:false},
+		footerSets         : {type:Object , default:function(){return {height:100, zIndex:100, bg:'linear-gradient(to bottom, #FFFFFF,#FFFFFF)'}}},
+		pendantSets        : {type:Object , default:function(){return {width:'100rpx', right:'25rpx', bottom:'100rpx', zIndex:100};}},
+		isLoading          : {type:Boolean, default:false},
+		isSwitchPage       : {type:Boolean, default:false},
+		iphoneXButtomStyle : {type:String,  default:''},
+		headerSizedStyle   : {type:String,  default:''},
+		fixedTopZIndex     : {type:Number,  default:2},
+		/* 刷新 */
+		refresh            : {type:Boolean, default:false},
+		refreshText        : {type:Array,   default:function () {
+			return ['继续下拉刷新','松开手指开始刷新','数据刷新中','数据已刷新'];
+		}},
+		refreshBgColor     : {type:Array,   default:function () {
+			return ['#FFFFFF','#FFFFFF','#FFFFFF','#63D2BC'];
+		}},
+		refreshColor       : {type:Array,   default:function () {
+			return ['rgba(69, 90, 100, 0.6)','rgba(69, 90, 100, 0.6)','#63D2BC','#FFFFFF'];
+		}},
+		refreshFontSize    : {type:String, default:'26rpx'},
+		
+		/* 加载更多 */
+		loadmore           : {type:Boolean, default:false},
+		loadMoreText       : {type:Array, default:function () {
+			return ['','数据加载中', '已加载全部数据', '暂无数据']; 
+		}},
+		loadMoreColor      : {type:Array, default:function () {
+			return ['rgba(69, 90, 100, 0.6)', 'rgba(69, 90, 100, 0.6)', 'rgba(69, 90, 100, 0.8)', 'rgba(69, 90, 100, 0.8)'];
+		}},
+		loadMoreStatus     : {type:Number, defalut:0},
+		loadMoreFontSize   : {type:String, default:'26rpx'},
+		apiLoadingStatus   : {type:Boolean, default:false}
+	},
+	data() {
+		return {
+			footerHeight        : '100rpx',
+			iphoneXButtomHeight : '0rpx',
+			statusBarHeight     : 0,
+			// #ifdef APP-NVUE
+			animateCount        : 0,
+			// #endif
+			headerTapNumber     : 0,
+			fixedTop            : 0,
+			refreshBodyHeight   : 0,
+			loadMoreTimer       : null,
+			fixedTopMargin      : 0,
+			scrollTop           : 0,
+			srcollTimer         : null
+		}
+	},
+	
+	mounted:function(){
+		if(this.isLoading){
+			this.pageLoadingOpen();
+		}
+		// 刷新相关
+		setTimeout(()=>{
+			if(this.refresh || this.loadmore){
+				this.getDomSize('guiPageBody', (res)=>{
+					this.refreshBodyHeight = res.height;
+					this.getDomSize('guiPageFixedTop', (res)=>{
+						if(res.height){
+							this.refreshBodyHeight -= res.height;
+							this.fixedTopMargin     = res.height;
+						}				
+					})
+				});
+			}
+		},200);
+	},
+	watch:{
+		isLoading : function (val) {
+			if(val){
+				this.pageLoadingOpen();
+			}else{
+				this.pageLoadingClose();
+			}
+		}
+	},
+	created:function(){
+		this.footerHeight = this.footerSets.height + 'rpx';
+		// #ifdef H5
+		if(this.customHeader){
+			this.fixedTop = this.headerSets.height;
+		}else{
+			this.fixedTop = 44;
+		}
+		// #endif
+		try {
+			var system   = uni.getSystemInfoSync();
+			if(system.model){
+				system.model = system.model.replace(' ', '');
+				system.model = system.model.toLowerCase();
+				this.statusBarHeight = system.statusBarHeight;
+				var res1 = system.model.indexOf('iphonex');
+				if(res1 > 5){res1 = -1;}
+				var res2 = system.model.indexOf('iphone1');
+				if(res2 > 5){res2 = -1;}
+				if(res1 != -1 || res2 != -1){
+					this.iphoneXButtomHeight = '50rpx';
+					this.footerHeight        =  (this.footerSets.height + 50 ) + 'rpx';
+				}
+			}
+			// #ifdef MP-ALIPAY
+			this.statusBarHeight = 0;
+			// #endif
+			// #ifdef APP-PLUS
+			this.iphoneXButtomHeight = '0rpx';
+			this.footerHeight        =  this.footerSets.height + 'rpx';
+			if(plus.navigator.isFullscreen()){
+				this.statusBarHeight = 0;
+			}
+			// #endif
+			if(this.isSwitchPage){
+				this.iphoneXButtomHeight = '0rpx';
+				this.footerHeight        =  this.footerSets.height + 'rpx';
+			}
+			// #ifndef H5
+			if(this.customHeader){
+				this.fixedTop = this.headerSets.height + this.statusBarHeight;
+			}else{
+				this.fixedTop = 0;
+			}
+			// #endif
+		} catch (e){return null;}
+	},
+	methods:{
+		pageLoadingOpen : function(){
+			this.getRefs('guipageloading',0,(ref)=>{
+				this.$refs.guipageloading.open();
+			});
+		},
+		pageLoadingClose : function(){
+			this.getRefs('guipageloading',0,(ref)=>{
+				ref.close();
+			});
+		},
+		// 下拉刷新相关
+		touchstart : function (e){
+			if(!this.refresh){return false;}
+			if(this.apiLoadingStatus){return false;}
+			this.$refs.guiPageRefresh.touchstart(e);
+		},
+		touchmove : function(e){
+			if(!this.refresh){return false;}
+			if(this.apiLoadingStatus){return false;}
+			this.$refs.guiPageRefresh.touchmove(e);
+		},
+		touchend : function (e) {
+			if(!this.refresh){return false;}
+			if(this.apiLoadingStatus){return false;}
+			this.$refs.guiPageRefresh.touchend(e);
+		},
+		scroll:function(e){
+			if(this.srcollTimer != null){
+				clearTimeout(this.srcollTimer);
+			}
+			this.srcollTimer = setTimeout(()=>{
+				this.$refs.guiPageRefresh.scroll(e);
+				this.$emit('scroll', e);
+				this.scrollTop = e.detail.scrollTop;
+			}, 100);
+		},
+		setScrollTop : function (scrollTop){
+			this.scrollTop = scrollTop;
+		},
+		endReload : function(){
+			this.$refs.guiPageRefresh.endReload();
+		},
+		reload : function(){
+			if(this.apiLoadingStatus){return false;}
+			this.$emit('reload');
+			if(this.loadmore){this.$refs.guipageloadmore.stoploadmore();}
+		},
+		// 获取元素尺寸
+		getDomSize : function(domIDOrRef, fun, count){
+			if(!count){count = 1;}
+			if(count >= 50){
+				fun({width:0, height:0});
+				return false;
+			}
+			// #ifndef APP-NVUE
+			uni.createSelectorQuery().in(this).select('#'+domIDOrRef).boundingClientRect().exec((res)=>{
+				if(res[0] == null){
+					count += 1;
+					setTimeout(()=>{this.getDomSize(domIDOrRef, fun, count);}, 50);
+				}else{
+					fun(res[0]);
+					return ;
+				}
+			});
+			// #endif
+			// #ifdef APP-NVUE
+			var el = this.$refs[domIDOrRef];
+			dom.getComponentRect(el, (res) => {
+				if(res.result == false){
+					count += 1;
+					setTimeout(()=>{this.getDomSize(domIDOrRef, fun, count);}, 50);
+				}else{
+					fun(res.size);
+					return ;
+				}
+			});
+			// #endif
+			
+		},
+		stopfun   : function(e){e.stopPropagation(); return null;},
+		headerTap : function(){
+			this.headerTapNumber ++;
+			if(this.headerTapNumber >= 2){
+				this.$emit('gotoTop');
+				this.headerTapNumber = 0;
+			}else{
+				setTimeout(()=>{this.headerTapNumber = 0;}, 1000);
+			}
+		},
+		getRefs : function(ref, count, fun){
+			if(count >= 50){
+				fun(this.$refs[ref]);
+				return false;
+			}
+			var refReturn = this.$refs[ref];
+			if(refReturn){
+				// #ifdef APP-NVUE
+				fun(refReturn);
+				return;
+				// #endif
+				// #ifndef APP-NVUE
+				if(refReturn._data){
+					fun(refReturn);
+					return;
+				}
+				// #endif
+			}else{
+				count++;
+				setTimeout(()=>{
+					this.getRefs(ref, count, fun);
+				}, 100);
+			}
+		},
+		loadmorefun : function () {
+			if(!this.loadmore){return false;}
+			if(this.apiLoadingStatus){return false;}
+			// 获取加载组件状态看一下是否还能继续加载
+			// 保证触底只执行一次加载
+			if(this.loadMoreTimer != null){clearTimeout(this.loadMoreTimer);}
+			this.loadMoreTimer =  setTimeout(() => {
+				var status = this.$refs.guipageloadmore.loadMoreStatus;
+				if(status != 0){return null;}
+				this.$refs.guipageloadmore.loading();
+				this.$emit('loadmorefun');
+			}, 80);
+		},
+		stoploadmore : function(){
+			this.$refs.guipageloadmore.stoploadmore();
+		},
+		nomore : function () {
+			this.$refs.guipageloadmore.nomore();
+		},
+		loadEmpty : function(){
+			this.$refs.guipageloadmore.empty();
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-sbody{width:750rpx;}
+.gui-page-loading{width:750rpx; position:fixed; left:0; top:0; bottom:0; flex:1; z-index:99999;}
+.gui-page-loading-points{width:20rpx; height:20rpx; border-radius:50rpx; margin:10rpx;}
+/* #ifndef APP-NVUE */
+.gui-sbody{min-height:calc(100vh - var(--window-top) - var(--window-bottom));}
+@keyframes pageLoading1{0% {opacity:0.5; transform:scale(1);} 40% {opacity:1; transform:scale(1.5);}  60%{opacity:0.5; transform:scale(1);}}
+@keyframes pageLoading2{20% {opacity:0.5; transform:scale(1);} 60% {opacity:1; transform:scale(1.5);}  80% {opacity:0.5; transform:scale(1);}}
+@keyframes pageLoading3{40% {opacity:0.5; transform:scale(1);} 80% {opacity:1; transform:scale(1.5);}  100% {opacity:0.5; transform:scale(1);}}
+.animate1{animation:pageLoading1 1.2s infinite linear;}
+.animate2{animation:pageLoading2 1.2s infinite linear;}
+.animate3{animation:pageLoading3 1.2s infinite linear;}
+/* #endif */
+.gui-header{width:750rpx; position:fixed; left:0; top:0;}
+.gui-page-footer{width:750rpx; position:fixed; left:0; bottom:0;}
+/* #ifdef H5 */
+.gui-switch-page-footer{bottom:50px;}
+/* #endif */
+.gui-page-status-bar{width:750rpx;}
+.gui-page-pendant{position:fixed;}
+
+.gui-page-fixed-top{position:fixed; top:44px; left:0px;  width:750rpx; z-index:99998; overflow:hidden;}
+.gui-page-loadmore{padding-bottom:30rpx;}
+</style>

+ 92 - 0
GraceUI5/components/gui-pk.vue

@@ -0,0 +1,92 @@
+<template>
+	<view class="gui-pk-wrap" 
+	:style="{width:width,height:height,borderRadius:borderRadius}">
+		<!-- 背景 -->
+		<image 
+		src="https://upload-images.jianshu.io/upload_images/15372054-ef5c77eb84052ec1.png?imageMogr2/auto-orient/strip|imageView2/2/w/750/format/webp" 
+		class="gui-pk-bg" style="width:690rpx; height:460rpx;"></image>
+		<!-- pk 图标 -->
+		<view class="gui-pk-icon-wrap gui-flex gui-rows gui-justify-content-center gui-align-items-center" 
+		:style="{width:width, height:height,borderRadius:borderRadius}">
+			<view class="gui-pk-icon gui-border-box">
+				<image 
+				src="https://upload-images.jianshu.io/upload_images/15372054-24bbe2c57e61d1a2.png?imageMogr2/auto-orient/strip|imageView2/2/w/140/format/webp" 
+				mode="widthFix" style="width:80rpx; height:80rpx;"></image>
+			</view>
+		</view>
+		<view class="gui-pk gui-flex gui-rows gui-space-between gui-align-items-center" 
+		:style="{width:width, height:height,borderRadius:borderRadius}">
+			<view class="gui-pk-item gui-flex gui-columns">
+				<text class="gui-pk-title gui-block-text">{{title[0]}}</text>
+				<view class="gui-pk-btn-wrap" hover-class="gui-tap" v-if="status == 'button'">
+					<text class="gui-pk-btn gui-block-text" 
+					data-index="left" @tap="choose">{{btnName}}</text>
+				</view>
+				<view class="gui-pk-btn-wrap" v-if="status == 'progress'">
+					<progress :percent="progress[0]" activeColor="#FFFFFF" :active="active" 
+					stroke-width="3" class="gui-pk-progress" backgroundColor="#3699ff" />
+				</view>
+				<text class="gui-pk-text gui-block-text" v-if="status == 'progress'">{{progress[2]}}</text>
+			</view>
+			<view class="gui-pk-item">
+				<text class="gui-pk-title gui-block-text" style="text-align:right;">{{title[1]}}</text>
+				<view class="gui-pk-btn-wrap gui-flex gui-rows" hover-class="gui-tap" 
+				style="justify-content:flex-end;" v-if="status == 'button'">
+					<text class="gui-pk-btn gui-block-text" 
+					style="color:#FF0036;" data-index="right" @tap="choose">{{btnName}}</text>
+				</view>
+				<view class="gui-pk-btn-wrap" style="justify-content:flex-end;" v-if="status == 'progress'">
+					<progress :percent="progress[1]" stroke-width="3" class="gui-pk-progress" 
+					:active="active" activeColor="#FFFFFF" backgroundColor="#FF6666"/>
+				</view>
+				<text class="gui-pk-text gui-block-text" style="text-align:right;" v-if="status == 'progress'">{{progress[3]}}</text>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-pk",
+	props : {
+		width        : {type:String, default:'690rpx'},
+		height       : {type:String, default:'260rpx'},
+		borderRadius : {type:String, default:'8rpx'},
+		title        : {type:Array, default:function(){return ['',''];}},
+		btnName      : {type:String, default:'站队'},
+		status       : {type:String, default:'button'},
+		progress     : {type:Array, default:function(){return [80,20,'8000 票', '2000 票'];}}
+	},
+	methods:{
+		choose:function (e) {
+			this.$emit('choose', e.currentTarget.dataset.index);
+		}
+	},
+	data() {
+		return {
+			// #ifndef APP-NVUE
+			active: true,
+			// #endif
+			// #ifdef APP-NVUE
+			active: false
+			// #endif
+		}
+	},
+}
+</script>
+<style scoped>
+.gui-pk-wrap{font-size:0; overflow:hidden; position:relative;}
+.gui-pk-bg{position:absolute; left:0; top:0; z-index:1;}
+.gui-pk{position:absolute; left:0; top:0; z-index:3;}
+.gui-pk-item{width:220rpx; overflow:hidden; padding-left:35rpx; padding-right:35rpx;}
+/* #ifndef APP-PLUS */
+.gui-pk-item{box-sizing:border-box;}
+/* #endif */
+.gui-pk-title{color:#FFFFFF; font-size:50rpx; line-height:60rpx;}
+.gui-pk-btn-wrap{margin-top:32rpx;}
+.gui-pk-btn{width:120rpx; height:50rpx; line-height:50rpx; text-align:center; background-color:#FFFFFF; font-size:24rpx; border-radius:100rpx; color:#3687FF;}
+.gui-pk-btn-hv{font-weight:bold;}
+.gui-pk-progress{width:150rpx;}
+.gui-pk-text{font-size:22rpx; color:#FFFFFF; line-height:50rpx; margin-top:2px;}
+.gui-pk-icon-wrap{position:absolute; top:0px; left:0px; z-index:2;}
+.gui-pk-icon{width:120rpx; height:120rpx; padding-top:20rpx; padding-bottom:20rpx; padding-left:20rpx; padding-right:20rpx; background-color:#FFFFFF; border-radius:100rpx; font-size:0;}
+</style>

+ 111 - 0
GraceUI5/components/gui-popup-menu.vue

@@ -0,0 +1,111 @@
+<template>
+	<view class="gui-relative" v-if="showIn">
+		<view class="grace-popup-mask"
+		:class="[showIn?'grace-shade-in':'', outting ? 'grace-shade-out' : '']" 
+		@tap.stop="close" @touchmove.stop.prevent="stopfun"
+		:style="{backgroundColor:background, zIndex:zIndex}" 
+		v-if="showIn"></view>
+		<view class="grace-popup-menu" 
+		v-if="showIn" ref="guipopupmenu" 
+		:class="[showIn?'grace-shade-in':'', outting ? 'grace-shade-out' : '']" 
+		:style="{
+		top:top+'px', right:right+'px', width:menuWidth, 
+		zIndex:zIndex+1, height:showIn?'':'0rpx'
+		}">
+			<!-- #ifndef APP-NVUE -->
+			<view :class="['gui-flex', 'gui-rows', 'arrow-d-'+arrowDirection]" 
+			v-if="isArrow">
+				<view class="arrow-up" :style="{margin:arrowMargin}"></view>
+			</view>
+			<!-- #endif -->
+			<view :style="{width:menuWidth}">
+				<slot></slot>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+// #ifdef APP-NVUE
+const animation = weex.requireModule('animation');
+// #endif
+var graceJS = require('@/GraceUI5/js/grace.js');
+export default {
+	name  : "gui-popup-menu",
+	props : {
+		menuWidth    :  { type : String,  default : '258rpx' },
+		background   :  { type : String,  default : 'rgba(0,0,0, 0.3)' },
+		zIndex       :  { type : Number,  default : 99 },
+		isArrow      :  { type : Boolean, default : true},
+		arrowDirection : {type : String,  default : "right"},
+		arrowMargin  :  { type : String, default : "0 15rpx"}
+	},
+	data() {
+		return {
+			showIn  : false,
+			top     : 0,
+			right   : 0,
+			outting : false
+		}
+	},
+	methods: {
+		stopfun  : function(e){e.stopPropagation(); return null;},
+		open     : function(){
+			this.outting = false;
+			this.showIn  = true;
+			// #ifdef APP-NVUE
+			graceJS.getRefs('guipopupmenu', this, 0, (guipopupref)=>{
+				animation.transition(guipopupref, {
+					styles: {opacity:1, transform:'scale(1)'},
+					duration: 200, //ms
+					timingFunction: 'ease',
+					delay: 0 //ms
+				});
+			});
+			// #endif
+		},
+		close     : function(){
+			this.outting    = true;
+			setTimeout(()=>{
+				this.showIn = false;
+			},200);
+			// #ifdef APP-NVUE
+			graceJS.getRefs('guipopupmenu', this, 0, (guipopupref)=>{
+				animation.transition(guipopupref, {
+					styles: {opacity:0, transform:'scale(0.5)'},
+					duration: 200, //ms
+					timingFunction: 'ease',
+					delay: 0 //ms
+				});
+			});
+			// #endif
+		},
+		setTop   : function (top) {
+			this.top = top;
+		},
+		setRight : function (right) {
+			this.right = right;
+		}
+	}
+}
+</script>
+<style scoped>
+.grace-popup-menu{width:258rpx; padding:10rpx; right:0; top:0; position:absolute; opacity:0; transform:scale(0.1);}
+/* #ifdef APP-NVUE */
+.grace-popup-menu{padding:0rpx 10rpx;}
+/* #endif */
+.grace-popup-mask{width:750rpx; position:fixed; left:0; top:0px; bottom:0; flex:1;}
+/* #ifndef APP-NVUE */
+.grace-popup-mask{height:100%;}
+.arrow-up{width:0; height:0; border-left:18rpx solid transparent; border-right:18rpx solid transparent; border-bottom:18rpx solid #FFFFFF;}
+.arrow-d-right{justify-content:flex-end;}
+.arrow-d-center{justify-content:center;}
+.arrow-d-left{justify-content:flex-start;}
+.grace-shade-in{animation:grace-shade-in-a 150ms ease-in forwards;}
+@keyframes grace-shade-in-a{0%{transform:scale(0.1); opacity:0;} 100%{transform: scale(1); opacity:1;}}
+.grace-shade-out{animation:grace-shade-out-a 150ms ease-out forwards;}
+@keyframes grace-shade-out-a{0%{transform:scale(1); opacity:1;} 100%{transform: scale(0.5); opacity:0;}}
+/* #endif */
+/* #ifdef APP-NVUE */
+.grace-popup-menu{position:fixed;}
+/* #endif */
+</style>

+ 275 - 0
GraceUI5/components/gui-popup.vue

@@ -0,0 +1,275 @@
+<template>
+	<view v-if="show">
+		<!-- 居中 -->
+		<view 
+		class="gui-popup gui-flex gui-columns gui-justify-content-center gui-align-items-center" 
+		:class="[out ? 'gui-fade-out' : 'gui-fade-in']" 
+		ref="guipopup" 
+		@tap.stop="closebysd" @touchmove.stop.prevent="stopfun" 
+		:style="{
+			backgroundColor:bgColor, 
+			zIndex:zIndex, 
+			top:top+'px',
+			animationDuration:duration+'ms'
+		}" 
+		v-if="position == 'center'">
+			<view class="gui-popup-content gui-popup-center"
+			 @tap.stop="stopfun" ref="guiPopupCenter" 
+			:class="[out ? 'gui-scale-out' : 'gui-scale-in']" 
+			:style="{width:width, animationDuration:duration+'ms'}"><slot></slot></view>
+		</view>
+		<!-- 顶部 -->
+		<view class="gui-popup gui-flex gui-columns" 
+		:style="{
+			backgroundColor:bgColor, 
+			zIndex:zIndex, top:top+'px',
+			animationDuration:duration+'ms'
+		}" 
+		v-if="position == 'top'" 
+		:class="[out ? 'gui-fade-out' : 'gui-fade-in']" ref="guipopup" 
+		@tap.stop="closebysd" @touchmove.stop.prevent="stopfun">
+			<view 
+			class="gui-popup-content gui-popup-top" 
+			@tap.stop="stopfun" ref="guiPopupTop" 
+			:class="[out ? 'gui-top-out' : 'gui-top-in']" 
+			:style="{animationDuration:duration+'ms'}">
+				<slot></slot>
+			</view>
+		</view>
+		<!-- 底部 -->
+		<view class="gui-popup gui-flex gui-columns gui-justify-content-end" 
+		:style="{
+			backgroundColor:bgColor, 
+			zIndex:zIndex, 
+			top:top+'px',
+			animationDuration:duration+'ms'
+		}" 
+		v-if="position == 'bottom'" 
+		:class="[out ? 'gui-fade-out' : 'gui-fade-in']" ref="guipopup" 
+		@tap.stop="closebysd" @touchmove.stop.prevent="stopfun">
+			<view 
+			class="gui-popup-content gui-popup-bottom" 
+			@tap.stop="stopfun" ref="guiPopupBottom" 
+			:class="[out ? 'gui-bottom-out' : 'gui-bottom-in']" 
+			:style="{animationDuration:duration+'ms'}">
+				<slot></slot>
+			</view>
+		</view>
+		<!-- 左侧 -->
+		<view class="gui-popup gui-flex gui-columns" 
+		v-if="position == 'left'"
+		:class="[out ? 'gui-fade-out' : 'gui-fade-in']" 
+		ref="guipopup" 
+		@tap.stop="closebysd" @touchmove.stop.prevent="stopfun" 
+		:style="{
+			backgroundColor:bgColor, 
+			zIndex:zIndex, 
+			top:top+'px',
+			animationDuration:duration+'ms'
+		}">
+			<view class="gui-popup-content gui-flex1 gui-flex gui-columns gui-popup-left" 
+			@tap.stop="stopfun" ref="guiPopupLeft" 
+			:class="[out ? 'gui-left-out' : 'gui-left-in']" 
+			:style="{width:width, animationDuration:duration+'ms'}">
+				<slot></slot>
+			</view>
+		</view>
+		<!-- 右侧 -->
+		<view class="gui-popup gui-flex gui-columns gui-align-items-end" 
+		v-if="position == 'right'"
+		:class="[out ? 'gui-fade-out' : 'gui-fade-in']" 
+		ref="guipopup" 
+		@tap.stop="closebysd" @touchmove.stop.prevent="stopfun" 
+		:style="{
+			backgroundColor:bgColor, 
+			zIndex:zIndex, 
+			top:top+'px',
+			animationDuration:duration+'ms'
+		}">
+			<view class="gui-popup-content gui-flex1 gui-flex gui-columns gui-popup-right" 
+			@tap.stop="stopfun" ref="guiPopupRight" 
+			:class="[out ? 'gui-right-out' : 'gui-right-in']" 
+			:style="{width:width, animationDuration:duration+'ms'}">
+				<slot></slot>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+// #ifdef APP-NVUE
+const animation = weex.requireModule('animation');
+var graceJS = require('@/GraceUI5/js/grace.js');
+// #endif
+export default{
+	name  : "gui-popup",
+	props : {
+		bgColor         : { type : String,  default : 'rgba(0, 0, 0, 0.7)'},
+		position        : { type : String,  default : 'center'},
+		width           : { type : String,  default : '580rpx'},
+		canCloseByShade : { type : Boolean, default : true },
+		zIndex          : { type : Number,  default : 99999},
+		top             : { type : Number,  default : 0},
+		duration        : { type : Number,  default : 280}
+	},
+	data(){
+		return {
+			show : false,
+			out  : false
+		}
+	},
+	methods:{
+		open    : function(){
+			this.out  = false;
+			this.show = true;
+			// #ifdef APP-NVUE
+			this.weexAnimateIn();
+			// #endif
+		},
+		closebysd : function () {
+			if(this.canCloseByShade){this.close();}
+		},
+		close   : function(){
+			this.out = true;
+			// #ifdef APP-NVUE
+			this.weexAnimateOut();
+			// #endif
+			setTimeout(()=>{
+				this.show = false;
+				this.$emit('close'); 
+			},350);
+		},
+		stopfun : function(e){e.stopPropagation(); return null;},
+		
+		// #ifdef APP-NVUE
+		weexAnimateIn : function(){
+			graceJS.getRefs('guipopup', this, 0, (guipopupref)=>{
+				animation.transition(guipopupref, {
+					styles: { opacity : 1},
+					duration: this.duration, //ms
+					timingFunction: 'ease',
+					delay: 0 //ms
+				});
+			});
+			
+			if(this.position == 'center'){
+				graceJS.getRefs('guiPopupCenter', this, 0, (guipopupref)=>{
+					animation.transition(guipopupref, {
+						styles: { transform:'scale(1)'},
+						duration: this.duration, //ms
+						timingFunction: 'ease',
+						delay: 0 //ms
+					});
+				});
+			}else if(this.position == 'top'){
+				graceJS.getRefs('guiPopupTop', this, 0, (guipopupref)=>{
+					animation.transition(guipopupref, {
+						styles: {transform:'translateY(0px)'},
+						duration: this.duration, //ms
+						timingFunction: 'ease',
+						delay: 0 //ms
+					});
+				});
+			}else if(this.position == 'bottom'){
+				graceJS.getRefs('guiPopupBottom', this, 0, (guipopupref)=>{
+					animation.transition(guipopupref, {
+						styles: {transform:'translateY(0px)'},
+						duration: this.duration, //ms
+						timingFunction: 'ease',
+						delay: 0 //ms
+					});
+				});
+			}else if(this.position == 'left'){
+				graceJS.getRefs('guiPopupLeft', this, 0, (guipopupref)=>{
+					animation.transition(guipopupref, {
+						styles: {transform:'translateX(0px)'},
+						duration: this.duration, //ms
+						timingFunction: 'ease',
+						delay: 0 //ms
+					});
+				});
+			}else{
+				graceJS.getRefs('guiPopupRight', this, 0, (guipopupref)=>{
+					animation.transition(guipopupref, {
+						styles: {transform:'translateX(0px)'},
+						duration: this.duration, //ms
+						timingFunction: 'ease',
+						delay: 0 //ms
+					});
+				});
+			}
+			
+		},
+		weexAnimateOut : function(){
+			graceJS.getRefs('guipopup', this, 0, (guipopupref)=>{
+				animation.transition(guipopupref, {
+					styles: { opacity : 0},
+					duration: this.duration, //ms
+					timingFunction: 'ease',
+					delay: 0 //ms
+				});
+			});
+			
+			if(this.position == 'center'){
+				graceJS.getRefs('guiPopupCenter', this, 0, (guipopupref)=>{
+					animation.transition(guipopupref, {
+						styles: { transform:'scale(0.3)' },
+						duration: this.duration, //ms
+						timingFunction: 'ease',
+						delay: 0 //ms
+					});
+				});
+			}else if(this.position == 'top'){
+				graceJS.getRefs('guiPopupTop', this, 0, (guipopupref)=>{
+					animation.transition(guipopupref, {
+						styles: {transform:'translateY(-600px)'},
+						duration: this.duration, //ms
+						timingFunction: 'ease',
+						delay: 0 //ms
+					});
+				});
+			}else if(this.position == 'bottom'){
+				graceJS.getRefs('guiPopupBottom', this, 0, (guipopupref)=>{
+					animation.transition(guipopupref, {
+						styles: {transform:'translateY(600px)'},
+						duration: this.duration, //ms
+						timingFunction: 'ease',
+						delay: 0 //ms
+					});
+				});
+			}else if(this.position == 'left'){
+				graceJS.getRefs('guiPopupLeft', this, 0, (guipopupref)=>{
+					animation.transition(guipopupref, {
+						styles: {transform:'translateX(-500px)'},
+						duration: this.duration, //ms
+						timingFunction: 'ease',
+						delay: 0 //ms
+					});
+				});
+			}else{
+				graceJS.getRefs('guiPopupRight', this, 0, (guipopupref)=>{
+					animation.transition(guipopupref, {
+						styles: {transform:'translateX(600px)'},
+						duration: this.duration, //ms
+						timingFunction: 'ease',
+						delay: 0 //ms
+					});
+				});
+			}
+		},
+		// #endif
+	}
+}
+</script>
+<style>
+.gui-popup{width:750rpx; position:fixed; z-index:99999; left:0; top:0; bottom:0; flex:1;}
+.gui-popup-content{overflow:hidden;}
+.gui-fade-out{opacity:0;}
+.gui-popup-center{transform:scale(0.3,0.3);}
+.gui-popup-top{transform:translateY(-1000px);}
+.gui-popup-bottom{transform:translateY(600px);}
+.gui-popup-left{transform:translateX(-600px);}
+.gui-popup-right{transform:translateX(600px);}
+/* #ifndef APP-NVUE */
+.gui-popup{height:100%;}
+/* #endif */
+</style>

+ 45 - 0
GraceUI5/components/gui-product-list.vue

@@ -0,0 +1,45 @@
+<template>
+	<view class="gui-flex gui-rows gui-wrap gui-space-between">
+		<view class="gui-product" hover-class="gui-tap" 
+		v-for="(item, index) in products" :key="index" 
+		@tap="gotoInfo(index, item)"
+		:style="{
+			width  : (375 - margin*2)+'rpx',
+			marginLeft  : margin+'rpx',
+			marginRight : margin+'rpx'}">
+			<view>
+				<gui-image :src="item.img" :width="(375 - margin*2)" :height="imgHeight"></gui-image>
+			</view>
+			<view class="gui-product-lines">
+				<text class="gui-product-name">{{item.name}}</text>
+			</view>
+			<view class="gui-product-lines gui-flex gui-rows gui-nowrap gui-align-items-center">
+				<text class="gui-color-red gui-text-small">¥</text>
+				<text class="gui-h5 gui-color-red">{{item.price}}</text>
+				<text class="gui-text-small gui-color-gray gui-line-through gui-price-margin">{{item.priceMarket}}</text>
+				<text class="gui-text-small gui-color-gray gui-price-margin">{{item.selledNum}}人购买</text>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-product-list",
+	props : {
+		products    : {type : Array,  default : function(){return [];}},
+		margin      : {type : Number, default : 15},
+		imgHeight   : {type : Number, default : 345},
+	},
+	methods:{
+		gotoInfo : function (idx, item) {
+			this.$emit('gotoInfo', idx, item);
+		}
+	}
+}
+</script>
+<style>
+.gui-product{margin-bottom:38rpx;}
+.gui-product-lines{margin-top:10rpx;}
+.gui-product-name{font-size:26rpx; line-height:32rpx;}
+.gui-price-margin{margin:0 10rpx;}
+</style>

+ 117 - 0
GraceUI5/components/gui-progress-scrollview.vue

@@ -0,0 +1,117 @@
+<template>
+	<view :style="{width:width+'rpx'}">
+		<scroll-view 
+		:style="{width:width+'rpx'}"
+		:scroll-x="true" 
+		class="gui-scroll-x" 
+		@scroll="scrolling" 
+		@scrolltolower="scrolltolower" 
+		@scrolltoupper="scrolltoupper" 
+		:show-scrollbar="false">
+			<slot></slot>
+		</scroll-view>
+		<view 
+		class="gui-flex gui-rows" 
+		:class="['gui-justify-content-'+progressPosition]">
+			<view class="gui-psv-progress"
+			v-if="progressWidth > 0" 
+			:style="{
+				width  : progressWidth+'rpx',
+				height : progressHeight+'rpx',
+				borderRadius : progressRadius+'rpx',
+				backgroundColor : progressBG
+			}">
+				<view class="gui-psv-progress-bar" 
+				v-if="progressWidth > 0" 
+				:style="{
+					width  : progressBarWidth+'rpx',
+					height : progressHeight+'rpx',
+					borderRadius : progressRadius+'rpx',
+					backgroundColor : progressBarBG,
+					marginLeft      : marginLeft +'rpx'
+				}"></view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-progress-scrollview",
+	data() {
+		return {
+			warpWitdh : 350,
+			marginLeft : 0
+		}
+	},
+	props : {
+		width         : {
+			type    : Number,
+			default : 700
+		},
+		progressWidth : {
+			type    : Number,
+			default : 200
+		},
+		progressHeight : {
+			type    : Number,
+			default : 10
+		},
+		progressRadius : {
+			type    : Number,
+			default : 10
+		},
+		progressBG : {
+			type    : String,
+			default : '#D1D1D1'
+		},
+		progressBarWidth : {
+			type    : Number,
+			default : 60
+		},
+		progressBarBG : {
+			type    : String,
+			default : '#008AFF'
+		},
+		progressPosition : {
+			type:String,
+			default:'start'
+		}
+	},
+	created:function(){
+		this.warpWitdh = uni.upx2px(this.width);
+	},
+	methods:{
+		scrolling : function (event) {
+			let scrollLeft  = event.detail.scrollLeft ;
+			let scrllWidth  = event.detail.scrollWidth - this.warpWitdh;
+			let percentage  = scrollLeft / scrllWidth;
+			percentage      *= 100;
+			percentage      = parseInt(percentage);
+			if(percentage > 90){percentage = 100;}
+			if(percentage < 0){percentage  = 0;}
+			this.percentage = percentage;
+			let marginLeft  = (this.progressWidth - this.progressBarWidth) * this.percentage;
+			this.marginLeft = parseInt(marginLeft / 100);
+			this.$emit('scrolling', scrllWidth, scrollLeft, percentage);
+		},
+		scrolltolower : function () {
+			setTimeout(()=>{
+				this.percentage = 100;
+				this.marginLeft = this.progressWidth - this.progressBarWidth;
+				this.$emit('scrolltolower');
+			},300);
+		},
+		scrolltoupper : function () {
+			setTimeout(()=>{
+				this.percentage = 0;
+				this.marginLeft = 0;
+				this.$emit('scrolltoupper');
+			},300);
+		}
+	} 
+}
+</script>
+<style scoped>
+.gui-psv-progress{background-color:#F8F8F8; border-radius:30rpx;}
+.gui-psv-progress-bar{}
+</style>

+ 83 - 0
GraceUI5/components/gui-radio.vue

@@ -0,0 +1,83 @@
+<template>
+	<view class="gui-flex gui-rows gui-nowrap gui-align-items-center" 
+	:style="{justifyContent:justifyContent}" 
+	@tap.stop="changeStatus">
+		<view 
+		:style="{
+			width:(size+5)+'rpx',
+			height:(size+5)+'rpx', 
+			overflow:'hidden',
+			backgroundColor:status?checkedColor:'',
+			borderRadius:(size)+'rpx' 
+		}">
+			<text v-if="status" 
+			class="gui-radio-btn gui-icons gui-block-text gui-text-center"
+			:style="{
+				fontSize:(size-12)+'rpx', 
+				lineHeight:(size+8)+'rpx', 
+				color : '#FFFFFF',
+			}">&#xe60f;</text>
+			<text v-else
+			class="gui-radio-btn gui-icons gui-block-text gui-text-center" 
+			:style="{
+				fontSize:(size)+'rpx',
+				lineHeight:(size+5)+'rpx', 
+				color : color
+			}">&#xe762;</text>
+		</view>
+		<view class="gui-radio-lable"><slot></slot></view>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-radio",
+	props : {
+		size : {
+			type : Number,
+			default : 38
+		},
+		color : {
+			type : String,
+			default : '#EEEEEE'
+		},
+		checked : {
+			type : Boolean,
+			default : false
+		},
+		checkedColor : {
+			type : String,
+			default : '#008AFF'
+		},
+		parameter : {
+			type : Array,
+			default : function () {
+				return []
+			}
+		},
+		justifyContent:{type:String, default:'flex-start'}
+	},
+	data() {
+		return {
+			status : false
+		}
+	},
+	watch: {
+		checked : function (val, old) {
+			this.status = val;
+		}
+	},
+	created : function(){
+		this.status = this.checked;
+	},
+	methods:{
+		changeStatus : function(){
+			this.status = !this.status;
+			this.$emit('change', [this.status, this.parameter]);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-radio-btn{color:#999999;}
+.gui-radio-lable{margin-left:15rpx;}
+</style>

+ 125 - 0
GraceUI5/components/gui-refresh.vue

@@ -0,0 +1,125 @@
+<template>
+	<view 
+	class="gui-page-refresh gui-flex gui-rows gui-justify-content-center gui-align-items-center" 
+	:style="{
+	height:refreshHeight+'px', 
+	backgroundColor:refreshBgColor[refreshStatus]}" 
+	:class="[refreshStatus == 3 ? 'gload-hide' : '']">
+		<text class="gui-page-refresh-icon gui-icons gui-block-text" 
+		v-if="refreshStatus == 0 || refreshStatus == 1" 
+		:style="{
+			fontSize:refreshFontSize,
+			color:refreshColor[refreshStatus]
+		}">&#xe66c;</text>
+		<view class="gui-page-refresh-icon" ref="loadingIcon" v-if="refreshStatus == 2" >
+			<text class="gui-icons gui-rotate360 gui-block-text"
+			:style="{
+				fontSize:refreshFontSize,
+				color:refreshColor[refreshStatus]
+			}">&#xe9db;</text>
+		</view>
+		<text class="gui-page-refresh-icon gui-icons"
+		v-if="refreshStatus == 3" 
+		:style="{
+			fontSize:refreshFontSize,
+			color:refreshColor[refreshStatus]
+		}">&#xe7f8;</text>
+		<text class="gui-page-refresh-text gui-block-text" 
+		:style="{
+			fontSize:refreshFontSize,
+			color:refreshColor[refreshStatus]
+		}">{{refreshText[refreshStatus]}}</text>
+	</view>
+</template>
+<script>
+// #ifdef APP-NVUE
+var animation = weex.requireModule('animation');
+const dom = weex.requireModule('dom');
+// #endif
+export default{
+	name  : "gui-refresh",
+	props : {
+		refreshText    : {type:Array,   default:function () {
+			return ['继续下拉刷新','松开手指开始刷新','数据刷新中','数据已刷新'];
+		}},
+		refreshBgColor : {type:Array,   default:function () {
+			return ['#FFFFFF','#FFFFFF','#FFFFFF','#63D2BC'];
+		}},
+		refreshColor : {type:Array,   default:function () {
+			return ['rgba(69, 90, 100, 0.6)','rgba(69, 90, 100, 0.6)','#63D2BC','#FFFFFF'];
+		}},
+		refreshFontSize : {type:String, default:'26rpx'}
+	},
+	data() {
+		return {
+			reScrollTop         : 0,
+			refreshHeight       : 0,
+			refreshY            : 0,
+			refreshStatus       : 0,
+			refreshTimer        : 0
+		}
+	},
+	methods:{
+		touchstart : function (e){
+			if(this.reScrollTop > 10){return ;}
+			this.refreshY = e.changedTouches[0].pageY;
+		},
+		touchmove : function(e){
+			if(this.refreshStatus >= 1){ return null;}
+			if(this.reScrollTop > 10){return ;}
+			var moveY = e.changedTouches[0].pageY - this.refreshY;
+			moveY     = moveY / 2;
+			if(moveY >= 50){
+				moveY = 50;
+				this.refreshStatus = 1;
+			}
+			if(moveY > 15){this.refreshHeight = moveY;}
+		},
+		touchend : function (e) {
+			if(this.reScrollTop > 10){return ;}
+			if(this.refreshStatus < 1){
+				return this.resetFresh();
+			}else if(this.refreshStatus == 1){
+				this.refreshStatus = 2;
+				// #ifdef APP-NVUE
+				setTimeout(()=>{
+					this.rotate360();
+				}, 200);
+				// #endif
+				this.$emit('reload');
+			}
+		},
+		scroll:function(e){
+			this.reScrollTop = e.detail.scrollTop;
+		},
+		endReload : function(){
+			this.refreshStatus = 3;
+			setTimeout(()=>{this.resetFresh()}, 1000);
+		},
+		resetFresh : function () {
+			this.refreshHeight = 0;
+			this.refreshStatus = 0;
+			return null;
+		},
+		rotate360 : function(){
+			var el = this.$refs.loadingIcon;
+			animation.transition(el, {
+				styles     : {transform: 'rotate(7200deg)'},
+				duration   : 20000,
+				timingFunction: 'linear',
+				needLayout :false,
+				delay: 0
+			});
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-page-refresh{overflow:hidden}
+.gui-page-refresh-text{line-height:32rpx;}
+.gui-page-refresh-icon{padding:0 12rpx; line-height:40rpx;}
+/* #ifndef APP-NVUE */
+@keyframes gload-hide{0%{opacity:1; height:50px;} 70%{opacity:1; height:50px;} 100%{opacity:0; height:0px;}}
+.gload-hide{animation:gload-hide 1s linear;}
+/* #endif */
+</style>

+ 74 - 0
GraceUI5/components/gui-right-menus.vue

@@ -0,0 +1,74 @@
+<template>
+	<view class="gui-right-menus" 
+	:style="styles">
+		<view v-if="show" ref="guirightmenu" 
+		class="gui-rt-menus-animate" 
+		:class="[outting ? 'slideOutRight' : 'slideInRight']">
+			<slot name="menus-more"></slot>
+		</view>
+		<view hover-class="gui-tap" @tap.stop="toggle">
+			<slot name="menus-primary"></slot>
+		</view>
+	</view>
+</template>
+<script>
+// #ifdef APP-NVUE
+const animation = weex.requireModule('animation');
+var graceJS = require('@/GraceUI5/js/grace.js');
+// #endif
+export default{
+	name  : "gui-right-menus",
+	props : {
+		styles:{type:String, default:'right:20rpx; bottom:200rpx; z-index:999; width:100rpx;'},
+	},
+	data() {
+		return {
+			show    : false,
+			outting : false
+		}
+	},
+	methods:{
+		toggle : function () {
+			if(this.show){this.close();}else{this.open();}
+		},
+		open:function () {
+			this.show    = true;
+			this.outting = false;
+			// #ifdef APP-NVUE
+			graceJS.getRefs('guirightmenu', this, 0, (ref)=>{
+				animation.transition(ref, {
+					styles         : { transform:'translateX(0px)' },
+					duration       : 200, //ms
+					timingFunction : 'ease',
+					delay          : 0 //ms
+				});
+			});
+			// #endif
+		},
+		close : function () {
+			this.outting  = true;
+			setTimeout(()=>{ this.show = false; }, 200);
+			// #ifdef APP-NVUE
+			graceJS.getRefs('guirightmenu', this, 0, (ref)=>{
+				animation.transition(ref, {
+					styles         : { transform:'translateX(100px)' },
+					duration       : 200, //ms
+					timingFunction : 'ease',
+					delay          : 0 //ms
+				});
+			});
+			// #endif
+		}
+	}
+}
+</script>
+<style scoped>
+/* #ifndef APP-NVUE */
+@import "@/GraceUI5/css/animate.css";
+.gui-rt-menus-animate{animation-duration:200ms; animation-timing-function:linear;}
+/* #endif */
+/* #ifdef APP-NVUE */
+.gui-rt-menus-animate{transform:translateX(100px);}
+/* #endif */
+.gui-right-menus{position:fixed;}
+</style>

+ 56 - 0
GraceUI5/components/gui-row.vue

@@ -0,0 +1,56 @@
+<template>
+	<view 
+	:class="classesRuntime" 
+	:style="styleRuntime">
+		<slot></slot>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-column",
+	props:{
+		wrap               : {type:Boolean, default:false},
+		mainAxisAlignment  : {type:String, default:'flex-start'},
+		crossAxisAlignment : {type:String, default:'flex-start'},
+		customStyle        : {type:String, default:''},
+		customClasses      : {type:Array,  default:function(){return [];}}
+	},
+	data() {
+		return {
+			styleRuntime : '',
+			classesRuntime : []
+		}
+	},
+	methods:{
+		makeStyle : function(){
+			var styleRuntime = 'justify-content:'+this.mainAxisAlignment+'; ';
+			styleRuntime += 'align-items:'+this.crossAxisAlignment+'; ';
+			styleRuntime += this.customStyle;
+			this.styleRuntime = styleRuntime;
+		},
+		makeClasses : function(){
+			var classes = ['gui-flex', 'gui-rows'];
+			if(this.wrap){
+				classes.push('gui-wrap');
+			}else{
+				classes.push('gui-nowrap');
+			}
+			classes = classes.concat(this.customClasses);
+			this.classesRuntime = classes;
+		}
+	},
+	mounted:function(){
+		this.makeStyle();
+		this.makeClasses();
+	},
+	watch:{
+		mainAxisAlignment  : function(){this.makeStyle();},
+		crossAxisAlignment : function(){this.makeStyle();},
+		customStyle        : function(){this.makeStyle();},
+		customClasses        : function(){this.makeClasses();},
+		wrap               : function(){this.makeClasses();}
+	}
+}
+</script>
+<style scoped>
+</style>

+ 213 - 0
GraceUI5/components/gui-schedule.vue

@@ -0,0 +1,213 @@
+<template>
+	<view class="gui-schedule-wrap">
+		<view class="gui-flex gui-rows gui-space-between gui-align-items-center">
+			<picker  mode="date" 
+			:value="currentDayIn" 
+			:start="startDate" 
+			:end="endDate" 
+			@change="selectDate">
+				<text 
+				class="gui-schedule-header-date gui-icons">{{cYear}} 年 {{cMonthStr}} 月 &#xe603;</text>
+			</picker>
+			<text class="gui-border gui-schedule-today" @tap="gotoToday">返回今天</text>
+		</view>
+		<view class="gui-flex gui-rows gui-nowrap">
+			<text class="gui-schedule-weeks gui-color-gray gui-block-text" 
+			v-for="(item, index) in weeks" :key="index">{{item}}</text>
+		</view>
+		<view class="gui-flex gui-rows gui-wrap">
+			<view class="gui-schedule-7item gui-flex gui-rows gui-justify-content-center"
+			v-for="(item, index) in days" :key="index">
+				<view 
+				class="gui-date-ditems gui-flex gui-columns gui-justify-content-center" 
+				v-if="item != ''" 
+				:style="{
+					backgroundColor:currentDayIn == cYear+'-'+cMonthStr+'-'+ item.date 
+					? activeBgColor : bgColor
+				}" 
+				@click="chooseDate(cYear+'-'+cMonthStr+'-'+item.date, item.date)">
+					<text class="gui-date-day gui-block-text" 
+					:class="[currentDayIn == (cYear+'-'+cMonthStr+'-'+item.date) ? 
+					'gui-d-current-txt' : '']">{{item.date}}</text>
+					<text class="gui-date-nl gui-block-text" v-if="isLunar" 
+					:class="[currentDayIn == (cYear+'-'+cMonthStr+'-'+item.date) ? 
+					'gui-d-current-txt' : '']">{{item.nl}}</text>
+					<view class="gui-schedule-point" 
+					v-if="item.haveSe" 
+					:style="{backgroundColor:pointColor}"></view>
+				</view>
+				<view class="gui-date-ditems" v-else style="background-color:none;"></view>
+			</view>
+		</view>
+		<view class="gui-border-b gui-schedule-line"></view>
+		<view class="gui-schedule-time-list-wrap">
+			<view class="gui-schedule-time-list gui-flex gui-rows gui-nowrap"
+			v-for="(item, index) in hours" :key="index">
+				<text class="gui-schedule-timer gui-block-text gui-color-gray">{{item}}:00</text>
+				<view class="gui-border-b gui-schedule-body gui-flex gui-columns">
+					<text class="gui-schedules gui-block-text" 
+					v-for="(schedule, idx) in schedulesIn[index]" :key="idx" 
+					@tap="scheduleTap" :data-id="schedule.id" 
+					:style="{
+						backgroundColor:schedule.bgColor, 
+						color:schedule.color}">{{schedule.content}}</text>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+import guiCalendar from './gui-calendar.js';
+export default{
+	name : "gui-schedule",
+	data() {
+		return {
+			cYear      : 2020,
+			cMonth     : 1,
+			cDay       : 10,
+			cMonthStr  : '01',
+			weeks      : ['一', '二', '三', '四', '五', '六', '日'],
+			days       : [],
+			currentDayIn : '',
+			hours      : ['00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23'],
+			schedulesIn: [] 
+		}
+	},
+	props:{
+		// 当前默认日期
+		currentDate   : {type:String, default:""},
+		bgColor       : {type:String, default:"#F8F8F8"},
+		activeBgColor : {type:String, default:"#008AFF"},
+		isLunar       : {type:Boolean, default:true },
+		startDate     : {type:String, default:'1950-01-01'},
+		endDate       : {type:String, default:'2050-01-01'},
+		schedules     : {type:Array, default:function () {return []}},
+		pointColor    : {type:String, default:"#FF0036"}
+	},
+	created:function(){
+		this.currentDayIn = this.currentDate;
+		this.initTime();
+		this.getSchedulesIn();
+	},
+	methods:{
+		initTime : function(){
+			if(this.currentDayIn == ''){
+				var dateObj        = new Date();
+				this.cYear         = Number(dateObj.getFullYear());
+				this.cMonth        = Number(dateObj.getMonth() + 1);
+				this.cMonthStr     = this.cMonth < 10 ? '0' + this.cMonth : this.cMonth;
+				this.cDay          = dateObj.getDate();
+				this.cDay          = this.cDay < 10 ? '0' + this.cDay : this.cDay;
+				this.currentDayIn  = this.cYear + '-' + this.cMonthStr + '-' + this.cDay;
+				this.changeMonth();
+			}else{
+				var dates          = this.currentDayIn.split(' ');
+				if (!dates[1]) { dates[1] = '';}
+				var dayArr         = dates[0].split('-');
+				this.cYear         = Number(dayArr[0]);
+				this.cMonth        = dayArr[1];
+				this.cDay          = dayArr[2];
+				var reg            = new RegExp('^0[0-9]+$');
+				if(reg.test(this.cMonth)){this.cMonth = this.cMonth.substr(1,1);}
+				this.cMonth        = Number(this.cMonth);
+				this.cMonthStr     = this.cMonth < 10 ? '0'+this.cMonth : this.cMonth;
+				this.currentDayIn  = dates[0];
+				this.currentTimeIn = dates[1];
+				this.changeMonth();
+			}
+		},
+		changeMonth:function(){
+			var daysList  = [];
+			var days      = this.getDaysInOneMonth();
+			var startWeek = this.getDay();
+			var forSteps  = 0;
+			for (var i = (0 - startWeek); i < days; i++){
+				if(i >= 0){
+					daysList[forSteps] = {date : i >= 9 ? i + 1 : '0' + (i+1), nl : ''};
+					daysList[forSteps].nl = guiCalendar.getLunarInfo(this.cYear, this.cMonth, i + 1);
+					daysList[forSteps].haveSe = this.haveSchedule(daysList[forSteps].date);
+				}else{
+					daysList[forSteps] = '';
+				}
+				forSteps++;
+			}
+			this.days    = daysList;
+		},
+		haveSchedule : function (day) {
+			var cDay = this.cYear+'-'+this.cMonthStr+'-'+day;
+			for(let i = 0; i < this.schedules.length; i++){
+				if(this.schedules[i].datetime.indexOf(cDay) != -1){
+					return true;
+				}
+			}
+			return false;
+		},
+		getDaysInOneMonth : function (){
+			var d = new Date(this.cYear, this.cMonth, 0);
+			return d.getDate();
+		},
+		getDay : function (){
+			var d = new Date(this.cYear, this.cMonth - 1, 0);
+			return d.getDay();
+		},
+		selectDate : function(e){
+			this.currentDayIn = e.detail.value;
+			this.initTime();
+			this.getSchedulesIn();
+			this.$emit('selectDate', e.detail.value);
+		},
+		chooseDate: function (sedDate) {
+			this.currentDayIn = sedDate;
+			this.getSchedulesIn();
+			this.$emit('chooseDate', sedDate);
+		},
+		getSchedulesIn : function (){
+			var res = [];
+			for(let i = 0; i < this.hours.length; i++){
+				var ctime = this.currentDayIn + ' ' + this.hours[i] + ':00';
+				res.push([]);
+				for(let ii = 0; ii < this.schedules.length; ii++){
+					if(this.schedules[ii].datetime == ctime){
+						res[i].push(this.schedules[ii]);
+					}
+				}
+			}
+			this.schedulesIn = res;
+		},
+		scheduleTap : function (e) {
+			var id = e.currentTarget.dataset.id;
+			this.$emit('scheduleTap', id);
+		},
+		gotoToday : function(){
+			var dateObj        = new Date();
+			this.cYear         = Number(dateObj.getFullYear());
+			this.cMonth        = Number(dateObj.getMonth() + 1);
+			this.cMonthStr     = this.cMonth < 10 ? '0' + this.cMonth : this.cMonth;
+			this.cDay          = dateObj.getDate();
+			this.cDay          = this.cDay < 10 ? '0' + this.cDay : this.cDay;
+			this.currentDayIn  = this.cYear + '-' + this.cMonthStr + '-' + this.cDay;
+			this.changeMonth();
+			this.getSchedulesIn();
+			this.$emit('gotoToday', this.currentDayIn);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-schedule-wrap{width:690rpx;}
+.gui-schedule-header-date{height:88rpx; line-height:88rpx; color:#2B2E3D; font-size:32rpx;}
+.gui-schedule-7item{width:98rpx; margin-bottom:22rpx; position:relative;}
+.gui-schedule-weeks{width:98rpx; height:88rpx; font-size:26rpx; line-height:88rpx; text-align:center;}
+.gui-date-ditems{width:82rpx; height:82rpx; border-radius:80rpx;}
+.gui-d-current-txt{color:#FFFFFF !important;}
+.gui-date-day{height:32rpx; line-height:32rpx; text-align:center; font-size:28rpx;}
+.gui-date-nl{height:24rpx; line-height:26rpx; color:#888888; font-size:20rpx; text-align:center;}
+.gui-schedule-line{height:20rpx; border-color:#F8F8F8;}
+.gui-schedule-time-list{margin-top:20rpx;}
+.gui-schedule-timer{width:88rpx; font-size:22rpx; line-height:36rpx;}
+.gui-schedule-body{width:200rpx; flex:1; border-color:#F8F8F8; margin-top:15rpx;}
+.gui-schedules{padding:10rpx; line-height:30rpx; font-size:22rpx; margin-top:15rpx; border-radius:8rpx;}
+.gui-schedule-time-list-wrap{padding:20rpx;}
+.gui-schedule-today{line-height:50rpx; height:50rpx; font-size:22rpx; color:#828282; padding-left:20rpx; padding-right:20rpx; border-color:#F1F2F3;}
+.gui-schedule-point{width:18rpx; height:18rpx; border-radius:10rpx; background-color:#FF0036; position:absolute; right:6rpx; top:6rpx;}
+</style>

+ 70 - 0
GraceUI5/components/gui-scroll-message.vue

@@ -0,0 +1,70 @@
+<template>
+	<view class="gui-scroll-body gui-flex gui-rows gui-nowrap">
+		<!-- #ifdef APP-NVUE -->
+		<view class="gui-scroll-msg gui-scrolling" 
+		ref="guiscrollbody">
+			<slot></slot>
+		</view>
+		<!-- #endif -->
+		<!-- #ifndef APP-NVUE -->
+		<view class="gui-scroll-msg gui-scrolling" 
+		:style="{'animation-duration':speed+'s'}">
+			<slot></slot>
+		</view>
+		<!-- #endif -->
+	</view>
+</template>
+<script>
+// #ifdef APP-NVUE
+const BindingX = uni.requireNativePlugin('bindingx');
+// #endif
+export default {
+	name  : "gui-scroll-message",
+	props : {
+		speed    : {type:Number, default:12},
+		distance : {type:Number, default:1200}
+	},
+	data() {
+		return {
+			BindingXObj : {},
+			AnimateObj  : 0
+		}
+	},
+	// #ifdef APP-NVUE
+	mounted:function(){
+		setTimeout(()=>{
+			this.BindingXObj = this.$refs.guiscrollbody.ref;
+			this.animationL();
+		}, 100);
+	},
+	methods:{
+		animationL : function(){
+			this.AnimateObj = BindingX.bind({
+				eventType      : 'timing',
+				exitExpression : 't>'+(this.speed*1000),
+				props          : [
+				  {
+					element    : this.BindingXObj, 
+					property   : 'transform.translateX',
+					expression : "500+(-"+this.distance+")*t/"+(this.speed*1000)
+				  }
+				]
+			}, (e)=>{
+				if(e.state === 'exit') {
+					setTimeout(()=>{
+						this.animationL();
+					},100);
+				}
+			});
+		}
+	}
+	// #endif
+}
+</script>
+<style scoped>
+.gui-scroll-body{overflow:hidden;}
+/* #ifndef APP-NVUE */
+.gui-scrolling{animation:graceScrollingx 12s linear infinite;}
+@keyframes graceScrollingx{ 0% { transform:translateX(60%); } 100% { transform: translateX(-100%); }}
+/* #endif */
+</style>

+ 113 - 0
GraceUI5/components/gui-scrollitems-y.vue

@@ -0,0 +1,113 @@
+<template>
+	<scroll-view :scroll-y="!animating" 
+	:scroll-top="sctop" @scroll="scrolling" 
+	:scroll-with-animation="true" 
+	:show-scrollbar="false" 
+	:style="{height:height}">
+		<view class="gui-scrollitemsy gui-flex gui-rows gui-nowrap gui-space-between" 
+		hover-class="gui-tap" 
+		v-for="(item, idx) in itemsIn" :key="idx" 
+		:class="[ animating && idx == 0 ? 'gui-scrollitemsy-animate' : '']" 
+		:style="{opacity:animating && idx == 0 ? 0 : 1}" 
+		@touchstart="touchstart" @touchend="touchend" 
+		@tap="itemTap(idx)" ref="scitems">
+			<image 
+			:src="item.img" 
+			:style="imgStyle"></image>
+			<view class="gui-scrollitemsy-body" 
+			:style="{width:msgWidth}">
+				<text class="gui-block-text" 
+				:style="textStyle">{{item.desc}}</text>
+			</view>
+		</view>
+	</scroll-view>
+</template>
+<script>
+// #ifdef APP-NVUE
+const animation = weex.requireModule('animation');
+// #endif
+export default{
+	name  : "gui-scrollitems-y",
+	props : {
+		height    : {type : String, default : '480rpx'},
+		imgStyle  : {type : String, default : 'width:88rpx; height:88rpx; border-radius:6rpx; margin-top:5rpx; margin-right:28rpx;'},
+		textStyle : {type : String, default : 'font-size:28rpx; color:#2B2E3D; line-height:38rpx;'},
+		items     : {type : Array,  default : function(){return [];}},
+		msgWidth : {type : String,  default :'570rpx'},
+		duration  : {type : Number, default : 6000}
+	},
+	data() {
+		return {
+			timer      : null,
+			timer2     : null,
+			timer3     : null,
+			animating  : false,
+			itemsIn    : [],
+			sctop      : 0
+		}
+	},
+	created:function(){
+		this.itemsIn = this.items;
+		this.timer   = setTimeout(() => {this.animate();}, this.duration);
+	},
+	methods:{
+		animate : function(){
+			this.sctop     = 0;
+			var tmp        = this.itemsIn.pop();
+			this.items.unshift(tmp);
+			this.animating = true;
+			// #ifdef APP-NVUE
+			setTimeout(() => {this.animateforweex();}, 200);
+			// #endif
+			setTimeout(()=>{ this.animating = false; }, 600);
+			this.timer = setTimeout(() => {
+				this.animate();
+			}, this.duration);
+		},
+		touchstart : function(){
+			clearTimeout(this.timer);
+		},
+		touchend : function(){
+			clearTimeout(this.timer2);
+			this.timer2 = setTimeout(()=>{
+				this.sctop = 0;
+				setTimeout(()=>{this.animate();}, 1000)
+			}, 3000);
+		},
+		scrolling : function (e) {
+			clearTimeout(this.timer3);
+			this.timer3 = setTimeout(()=>{
+				this.sctop = e.detail.scrollTop;
+			},200);
+		},
+		itemTap : function(idx){
+			this.$emit('itemTap', this.itemsIn[idx]);
+		},
+		addItem : function(obj){
+			clearTimeout(this.timer);
+			clearTimeout(this.timer2);
+			this.itemsIn.push(obj);
+			setTimeout(()=>{ this.sctop = 0; }, 500);
+			setTimeout(()=>{ this.animate(); }, 1000);
+		},
+		animateforweex : function () {
+			var ref = this.$refs.scitems[0];
+			animation.transition(ref, {
+				styles: {opacity:1},
+				duration: 300, //ms
+				timingFunction: 'ease',
+				delay: 0 //ms
+			});
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-scrollitemsy{margin-bottom:30rpx;}
+.gui-scrollitemsy-body{overflow:hidden;}
+
+/* #ifndef APP-NVUE */
+@keyframes gui-scrollitemsy-animate{0%{height:0;} 100%{height:100rpx;}}
+.gui-scrollitemsy-animate{animation:gui-scrollitemsy-animate 500ms ease-in; }
+/* #endif */
+</style>

+ 102 - 0
GraceUI5/components/gui-scrollitems.vue

@@ -0,0 +1,102 @@
+<template>
+	<view :style="{width:width+'rpx', overflow:'hidden'}">
+		<gui-touch @thStart="thStart" @thMove="thMove" @thEnd="thEnd">
+			<view class="gui-flex gui-rows gui-nowrap" 
+			:style="{
+			width:wrapWidth+'px',
+			transform:'translateX('+scLeft+'px)'
+			}">
+				<view class="gui-scrollitems gui-img-in" 
+				hover-class="gui-tap" 
+				:style="{
+				width:itemWidth+'rpx', 
+				height:itemHeight+'rpx', 
+				marginRight:itemMargin+'rpx'}" 
+				v-for="(item, idx) in itemsIn" :key="idx" 
+				@tap="tapme(idx)">
+					<image class="gui-scroll-image" 
+					:style="{
+					width:itemWidth+'rpx', 
+					height:itemHeight+'rpx'}" 
+					:src="item.img"></image>
+					<text 
+					class="gui-scrollitems-title gui-block-text gui-bg-black-opacity3 gui-border-box" 
+					:style="{width:width+'rpx'}">{{item.title}}</text>
+				</view>
+			</view>
+		</gui-touch>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-scrollitems",
+	props : {
+		width         : {type : Number, default:690},
+		itemWidth     : {type : Number, default:345},
+		itemHeight    : {type : Number, default:200},
+		itemMargin    : {type : Number, default:10},
+		items         : {type : Array,  default:function(){return [];}},
+		duration      : {type : Number, default:25}
+	},
+	data() {
+		return {
+			scLeft        : 0,
+			resetWidth    : 0,
+			itemsIn       : [],
+			timer         : null,
+			timer2        : null,
+			speed         : 1,
+			timer3        : null,
+			wrapWidth     : 5000,
+			oX            : 0
+		}
+	},
+	created:function(){
+		var len          = this.items.length;
+		this.itemsIn     = this.items;
+		this.resetWidth  = len * (this.itemWidth + this.itemMargin);
+		this.resetWidth  = uni.upx2px(this.resetWidth);
+		this.resetWidth *= -1;
+		this.itemsIn     = this.itemsIn.concat(this.items);
+		this.wrapWidth   = len * (this.itemWidth + this.itemMargin) * 2 + 80;
+		this.scrollAnimate();
+	},
+	methods:{
+		scrollAnimate : function () {
+			if(this.scLeft <= this.resetWidth){
+				this.scLeft = 0;
+				this.timer = setTimeout(()=>{this.scrollAnimate()}, this.duration+200);
+			}else{
+				this.scLeft -= this.speed
+				this.timer   = setTimeout(()=>{this.scrollAnimate()}, this.duration);
+			}
+		},
+		thStart : function(e){
+			clearTimeout(this.timer);
+			this.timer = null;
+			this.oX    = this.scLeft;
+		},
+		thMove : function (e){
+			clearTimeout(this.timer);
+			var tmpleft   = this.oX + e[0][0];
+			if(tmpleft < this.resetWidth){ tmpleft = this.resetWidth;}
+			if(tmpleft > 0){tmpleft = 0}
+			this.scLeft   = tmpleft;
+		},
+		thEnd : function(e){
+			this.timer = null
+			if(this.timer3 != null){clearTimeout(this.timer3);}
+			this.timer3 = setTimeout(()=>{
+				this.scrollAnimate();
+			}, 500);
+		},
+		tapme : function(idx){
+			this.$emit('itemTap', this.itemsIn[idx]);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-scrollitems{overflow:hidden; position:relative;}
+.gui-scrollitems-title{position:absolute; z-index:1; left:0; bottom:0; height:44rpx; line-height:44rpx; padding:0 15rpx; overflow:hidden; font-size:22rpx; color:#FFFFFF;}
+</style>

+ 101 - 0
GraceUI5/components/gui-search.vue

@@ -0,0 +1,101 @@
+<template>
+	<view class="gui-flex gui-rows gui-nowrap gui-align-items-center" 
+	:style="{
+		height:height, 
+		backgroundColor:background,
+		borderRadius:borderRadius
+		}">
+		<text class="gui-search-icon gui-icons gui-block-text gui-text-center" @tap.stop="tapme" 
+		:style="{
+			color:iconColor, 
+			fontSize:iconFontSize, 
+			lineHeight:height, width:iconWidth
+		}">&#xe604;</text>
+		
+		<input 
+		type="text" 
+		:placeholder-class="placeholderClass" 
+		class="gui-search-input gui-flex1" 
+		:placeholder="placeholder" 
+		v-model="inputVal" 
+		v-if="!disabled" :focus="focus" 
+		:style="{
+			height:inputHeight, 
+			lineHeight:inputHeight, 
+			fontSize:inputFontSize, 
+			color:inputColor
+		}" 
+		@input="inputting" @confirm="confirm" />
+		
+		<text class="gui-search-input gui-flex1 gui-block-text" 
+		v-if="disabled" @tap.stop="tapme" 
+		:style="{
+			height:inputHeight, 
+			lineHeight:inputHeight, 
+			fontSize:inputFontSize, 
+			color:disableColor}">{{placeholder}}</text>
+		<text class="gui-search-icon gui-icons gui-block-text gui-text-center" 
+		v-if="inputVal.length > 0 && clearBtn" @tap.stop="clearKwd" 
+		:style="{
+			color:iconColor, 
+			fontSize:iconFontSize, 
+			lineHeight:height, 
+			width:iconWidth}">&#xe78a;</text>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-search",
+	props : {
+		height:{type:String, default:'66rpx'},
+		background:{type:String, default:'#FFFFFF'},
+		fontSize:{type:String, default:'28rpx'},
+		iconWidth:{type:String, default:'70rpx'},
+		iconColor:{type:String, default:'#A5A7B2'},
+		iconFontSize:{type:String, default:'30rpx'},
+		inputHeight:{type:String, default:'30rpx'},
+		inputFontSize:{type:String, default:'26rpx'},
+		inputColor:{type:String, default:'#323232'},
+		placeholder:{type:String, default:'关键字'},
+		placeholderClass:{type:String, default:''},
+		disableColor:{type:String, default:'#666666'},
+		kwd:{type:String, default:''},
+		borderRadius:{type:String, default:'66rpx'},
+		disabled:{type:Boolean, default:false},
+		focus:{type:Boolean, default:false},
+		clearBtn:{type:Boolean, default:true}
+	},
+	data() {
+		return {
+			inputVal : ''
+		}
+	},
+	created: function (){
+		this.inputVal = this.kwd;
+	},
+	watch:{
+		kwd : function(val, vo){
+			this.inputVal = val;
+		}
+	},
+	methods:{
+		clearKwd : function () {
+			this.inputVal = '';
+			this.$emit('clear', '');
+		},
+		inputting : function(e){
+			this.$emit('inputting', e.detail.value);
+		},
+		confirm : function (e) {
+			this.$emit('confirm', e.detail.value);
+			uni.hideKeyboard();
+		},
+		tapme : function () {
+			this.$emit('tapme')
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-search-input{width:100rpx; margin:0 10rpx; border-width:0rpx; padding:0; background-color:rgba(255,255,255,0);}
+</style>

+ 47 - 0
GraceUI5/components/gui-segmented-control.vue

@@ -0,0 +1,47 @@
+<template>
+	<view class="gui-flex gui-rows gui-nowrap gui-justify-content-center gui-segmented-control" 
+	:style="{borderRadius:borderRadius}">
+		<text v-for="(item, index) in items" :key="index" 
+		:class="['gui-segmented-control-item','gui-block-text',
+		index == currentIn ? 'gui-segmented-current':'',
+		index == currentIn ? 'gui-fade-in':'']" 
+		:style="{borderRadius:borderRadius}" 
+		@tap.stop="changeSC" :data-index="index">{{item}}</text>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-segmented-control",
+	props : {
+		items : {
+			type : Array,
+			default : function () { return new Array();}
+		},
+		current : { type : Number, default : 0},
+		borderRadius:{type:String, default:'6rpx'}
+	},
+	data() {
+		return {
+			currentIn: 0
+		}
+	},
+	created: function(){
+		this.currentIn = this.current;
+	},
+	watch:{
+		current : function (val) {
+			this.currentIn = val;
+		}
+	},
+	methods:{
+		changeSC:function (e) {
+			var index = Number(e.currentTarget.dataset.index);
+			this.currentIn = index;
+			this.$emit('change', index);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-segmented-control-item{width:50rpx; flex:1; text-align:center;}
+</style>

+ 103 - 0
GraceUI5/components/gui-select-list.vue

@@ -0,0 +1,103 @@
+<template>
+	<view class="gui-select-list">
+		<view v-for="(item, index) in dataIn" 
+		:key="index" class="gui-select-list-item gui-flex gui-rows gui-nowrap gui-align-items-center" 
+		:data-index="index" @tap.stop="choose">
+			<text class="gui-select-list-icon gui-icons gui-block-text gui-select-list-ring gui-select-list-icon-l"
+			v-if="checkedType == 'ring' && !item.checked">&#xe762;</text>
+			<text class="gui-select-list-icon gui-icons gui-block-text gui-select-list-ring gui-select-list-icon-l gui-fade-in gui-select-list-current"
+			v-if="checkedType == 'ring' && item.checked">&#xe685;</text>
+			<image  :src="item.img" class="gui-select-list-img" v-if="item.img" mode="widthFix"></image>
+			<view class="gui-select-list-body gui-flex gui-rows gui-nowrap gui-align-items-center" 
+			:class="[isBorder?'gui-border-b' : '']" :style="{borderColor:borderColor}">
+				<view class="gui-select-list-content">
+					<text class="gui-block-text gui-select-list-title">{{item.title}}</text>
+					<text class="gui-select-list-desc gui-block-text" v-if="item.desc">{{item.desc}}</text>
+				</view>
+				<text class="gui-icons gui-block-text gui-select-list-icon gui-select-list-current" 
+				:class="[item.checked ? 'gui-fade-in gui-select-list-current' : '']" 
+				v-if="item.checked && checkedType == 'right'">&#xe60f;</text>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-select-list",
+	props : {
+		items           : {type : Array,   default : function(){return [];}},
+		type            : {type : String,  default : "radio"},
+		checkedType     : {type : String,  default : 'right'},
+		isBorder        : {type : Boolean, default : true},
+		borderColor     : {type : String,  default : "#F6F6F6"},
+		maxSize         : {type : Number,  default : 0}
+	},
+	data() {
+		return {
+			dataIn :  []
+		}
+	},
+	created : function(){
+		this.dataIn = this.items;
+	},
+	watch : {
+		items : function(val){ this.dataIn = val;}
+	},
+	methods : {
+		// 获取选中数据的索引
+		getSelectedIndex : function(){
+			var tmpArr = [];
+			this.dataIn.forEach((item, idx)=>{
+				if(item.checked){
+					tmpArr.push(idx);
+				}
+			});
+			return tmpArr;
+		},
+		// 选择数据
+		choose : function(e){
+			var index = e.currentTarget.dataset.index;
+			if(this.type == 'radio'){
+				if(this.dataIn[index].checked){
+					this.dataIn[index].checked = false;
+					this.$emit('change', -1);
+				}else{
+					for(let i = 0; i < this.dataIn.length; i++){
+						this.dataIn[i].checked = false;
+					}
+					this.dataIn[index].checked = true;
+					this.$emit('change', index);
+				}
+			}else{
+				if(this.dataIn[index].checked){
+					this.dataIn[index].checked = false;
+				}else{
+					if(this.maxSize > 0){
+						var size = 0;
+						this.dataIn.forEach((item)=>{
+							if(item.checked){size++;}
+						});
+						size++;
+						if(size > this.maxSize){this.$emit('maxSed'); return ;}
+					}
+					this.dataIn[index].checked = true;
+				}
+				var sedArr = [];
+				for(let i = 0; i < this.dataIn.length; i++){
+					if(this.dataIn[i].checked){
+						sedArr.push(i);
+					}
+				}
+				this.$emit('change', sedArr);
+			}
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-select-list-item{font-size:0;}
+.gui-select-list-icon{margin-left:10rpx;}
+.gui-select-list-icon-l{margin-left:0; margin-right:10rpx;}
+.gui-select-list-body{width:100rpx; flex:1;}
+.gui-select-list-content{width:200rpx; flex:1; overflow:hidden; padding:25rpx 0;}
+</style>

+ 225 - 0
GraceUI5/components/gui-select-menu.vue

@@ -0,0 +1,225 @@
+<template>
+	<view 
+	class="gui-select-menu-wrap">
+		<!-- #ifndef APP-NVUE -->
+		<view 
+		class="gui-masker" 
+		v-if="show"
+		@tap.stop.prevent="close" 
+		@touchmove.stop.prevent="stopfun" 
+		:style="{zIndex:(zIndex-1)}" ></view>
+		<!-- #endif -->
+		<view 
+		class="gui-select-menu-title gui-flex gui-rows gui-nowrap gui-justify-content-center gui-align-items-center" 
+		@click.stop="showMenu" 
+		id="menuMain">
+			<text 
+			class="gui-block-text gui-ellipsis" 
+			:style="{
+				fontSize:fontSize, 
+				color:titleColor
+			}">{{itemsIn[currentIndex]}}</text>
+			<text 
+			:style="{fontSize:fontSize, color:iconColor}" 
+			class="gui-icons gui-select-menu-title-icon gui-block-text" 
+			v-if="!show">&#xe603;</text>
+			<text 
+			:style="{fontSize:fontSize, color:iconColor}" 
+			class="gui-icons gui-select-menu-title-icon gui-block-text" 
+			v-if="show">&#xe654;</text>
+		</view>
+		<view 
+		class="gui-select-menu" 
+		v-if="show" 
+		:style="{
+			top : top +'px', 
+			height:height, 
+			zIndex:zIndex 
+		}" 
+		@tap.stop="close" 
+		@touchmove.stop.prevent="stopfun">
+			<view style="marginTop:90rpx; height:0px;"></view>
+			<view
+			style="padding-bottom:10rpx;"
+			@tap.stop="stopfun" 
+			class="gui-select-item gui-flex gui-rows gui-nowrap gui-align-items-center gui-bg-gray" 
+			v-if="isInput">
+				<input 
+				type="text" 
+				v-model="inputVal" 
+				class="gui-select-input gui-flex1 gui-border-box"
+				@confirm="addTag" 
+				@input="inputting" 
+				:placeholder="placeholder" />
+				<!-- 添加 -->
+				<text 
+				class="gui-select-input-btn gui-block-text" 
+				v-if="inputType == 'add'" 
+				:style="{color:activeColor}" 
+				@tap.stop="addTag">{{addBtnName}}</text>
+				<!--  -->
+				<text
+				class="gui-select-input-btn gui-block-text" 
+				v-else-if="inputType == 'search'" 
+				:style="{color:activeColor}" 
+				@tap.stop="addTag">搜索</text>
+			</view>
+			<scroll-view 
+			:scroll-y="true" 
+			:show-scrollbar="false" 
+			:style="{height:height}" 
+			class="gui-select-menus gui-border-box gui-bg-white" 
+			:scroll-into-view="itemTo">
+				<view 
+				class="gui-select-item gui-flex gui-rows gui-nowrap gui-align-items-center gui-bg-white" 
+				v-for="(item, index) in itemsIn" :key="index" 
+				:class="[index < getSize() ? 'gui-border-b' : '']" 
+				:data-index="index" 
+				@tap.stop="select" 
+				:style="{height:itemHeight}" 
+				:id="'items'+index">
+					<text 
+					class="gui-selected-icon gui-icons" 
+					:style="{
+						color : index == currentIndex ? activeColor : color, 
+						fontSize:fontSize
+					}" 
+					v-if="index == currentIndex">&#xe7f8;</text>
+					<text 
+					:style="{
+						fontSize:fontSize, 
+						color : index == currentIndex ? activeColor : color
+					}">{{item}}</text>
+				</view>
+				<view style="height:25rpx;" class="gui-bg-white"><text> </text></view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+<script scoped>
+export default {
+	name  : "gui-select-menu",
+	props : {
+		items          : { type : Array,   default : function () { return [] } },
+		titleColor     : { type : String,  default : "#2B2E3D" },
+		color          : { type : String,  default : "#2B2E3D" },
+		iconColor      : { type : String,  default : "rgba(69, 90, 100, 0.3)"},
+		activeColor    : { type : String,  default : "#008AFF" },
+		selectIndex    : { type : Number,  default : 0},
+		isH5header     : { type : Boolean, default : true },
+		fontSize       : { type : String,  default : '28rpx' },
+		zIndex         : { type : Number,  default : 9999 },
+		isInput        : { type : Boolean, default : false},
+		placeholder    : { type : String,  default : "请输入自定义条件"},
+		addBtnName     : { type : String,  default :"+ 添加"},
+		height         : { type : String,  default : '600rpx'},
+		itemHeight     : { type : String,  default : '88rpx'},
+		inputType      : { type : String,  default : 'add' }
+	},
+	data() {
+		return {
+			currentIndex : 0,
+			top          : 0,
+			showRes      : [],
+			inputVal     : '',
+			show         : false,
+			itemsIn      : [],
+			itemTo       : ''
+		}
+	},
+	watch:{
+		selectIndex : function () {
+			this.currentIndex = this.selectIndex;
+		},
+		items : function (val) {
+			this.itemsIn = val;
+		}
+	},
+	created : function () {
+		this.currentIndex = this.selectIndex;
+		this.itemsIn      = this.items;
+	},
+	methods:{
+		stopfun  : function (e) {e.stopPropagation(); return ;},
+		showMenu : function () {
+			uni.createSelectorQuery().in(this).select('#menuMain').fields(
+				{rect: true}, (res) => {
+					this.top       = res.top;
+					// #ifdef H5
+					if(this.isH5header){
+						this.top      += 44;
+					}
+					// #endif
+					this.show = true;
+					this.$emit('showMenu');
+				}
+			).exec();
+		},
+		close : function(){
+			setTimeout(()=>{this.show = false;}, 100);
+			this.$emit('close');
+		},
+		select : function(e){
+			var index = Number(e.currentTarget.dataset.index);
+			this.currentIndex = index;
+			this.$emit('select', index, this.items[index]);
+			this.close();
+		},
+		addTag : function () {
+			if(this.inputVal == ''){return ;}
+			if(this.inputType == 'add'){
+				var newArr   = JSON.stringify(this.itemsIn);
+				newArr       = JSON.parse(newArr);
+				newArr.unshift(this.inputVal);
+				this.itemsIn = [];
+				this.itemsIn = newArr;
+				 //newArr;
+				this.$emit('submit', this.inputVal);
+				this.inputVal = '';
+				this.currentIndex = 0;
+				this.close();
+			}else{
+				this.search();
+			}
+		},
+		getSize : function(){
+			return (this.itemsIn.length - 1);
+		},
+		setCurrentIndex : function (index) {
+			this.currentIndex = index;
+		},
+		search : function(){
+			var searchIndex = -1;
+			for(var i = 0; i < this.itemsIn.length; i++){
+				if(this.itemsIn[i].indexOf(this.inputVal) != -1){
+					searchIndex = i;
+					break;
+				}
+			}
+			if(searchIndex != -1){
+				this.itemTo = 'items' + searchIndex;
+			}
+		},
+		inputting : function(){
+			if(this.inputType == 'search'){
+				this.search();
+			}
+		}
+	}
+}
+</script>
+<style scoped>
+/* #ifndef APP-NVUE */
+@import "@/GraceUI5/css/animate.css";
+.animate{animation-duration:200ms; animation-timing-function:linear;}
+/* #endif */
+.gui-masker{width:750rpx; position:fixed; left:0; top:250rpx; bottom:0; flex:1; background-color:rgba(0,0,0,0.3);}
+.gui-select-menu-title{height:100rpx;}
+.gui-select-menu-title-icon{margin-left:5px; margin-top:3px;}
+.gui-select-menu{position:fixed; width:750rpx; left:0; top:0;}
+.gui-select-menus{ height:300px;}
+.gui-select-item{padding:0 25rpx; height:88rpx;}
+.gui-selected-icon{margin-right:15rpx;}
+.gui-select-input{line-height:68rpx; height:68rpx; width:200rpx; padding:20rpx; font-size:26rpx; background-color:rgba(255,255,255,1); border-radius:60rpx;}
+.gui-select-input-btn{width:100rpx; line-height:68rpx; height:68rpx; text-align:center; font-size:28rpx; border-radius:6rpx; margin-left:15rpx;}
+</style>

+ 132 - 0
GraceUI5/components/gui-single-slider.vue

@@ -0,0 +1,132 @@
+<template>
+	<view class="gui-sg-slider" 
+	@touchstart="touchstart" 
+	@touchmove.stop.prevent="touchmove" 
+	@touchend="touchend" 
+	ref="gracesgslider" 
+	id="gracesgslider" 
+	:style="{height:barHeight+'rpx'}">
+		<view class="gui-sg-slider-line" 
+		:style="{
+			height:bglineSize+'rpx', backgroundColor:bglineColor, 
+			marginTop:((barHeight - bglineSize) / 2)+'rpx', 
+			borderRadius:borderRadius}"></view>
+		<view class="gui-sg-slider-a-line" 
+		:style="{
+			width:(left+25)+'px', 
+			top:((barHeight - bglineSize) / 2)+'rpx', 
+			backgroundColor:bglineAColor, 
+			height:bglineSize+'rpx', 
+			borderRadius:borderRadius}"></view>
+		<text class="gui-sg-slider-bar gui-block-text"
+		:style="{
+			width:barWidth+'rpx', height:barHeight+'rpx', 
+			'line-height':barHeight+'rpx', 
+			backgroundImage:barBgColor, 
+			color:barColor, left:left+'px', 
+			fontSize:barTextSize, 
+			borderRadius:borderRadius}">{{barText}}</text>
+	</view>
+</template>
+<script>
+// #ifdef APP-NVUE
+const dom = weex.requireModule('dom');
+// #endif
+export default{
+	name  : "gui-single-slider",
+	props : {
+		barHeight    : {type:Number,  default:32},
+		barWidth     : {type:Number,  default:168},
+		barColor     : {type:String,  default:'#FFFFFF'},
+		barBgColor   : {type:String,  default:'linear-gradient(to right, #3688FF,#3688FF)'},
+		bglineSize   : {type:Number,  default:2},
+		bglineColor  : {type:String,  default:'rgba(54,136,255,0.5)'},
+		bglineAColor : {type:String,  default:'#3688FF'},
+		barText      : {type:String,  default:''},
+		barTextSize  : {type:String,  default:'20rpx'},
+		borderRadius : {type:String,  default:'32rpx'},
+		canSlide     : {type:Boolean, default:true}
+	},
+	data() {
+		return {
+			left       : 0,
+			startLeft  : 0,
+			width      : 0,
+			barWidthPX : 30
+		}
+	},
+	mounted:function(){
+		this.init();
+	},
+	methods:{
+		init : function(){
+			// #ifdef APP-NVUE
+			var el = this.$refs.gracesgslider;
+			dom.getComponentRect(el, (res) => {
+				if(!res.result || res.size.width < 10){
+					setTimeout(()=>{this.init();}, 100);
+					return;
+				}
+				this.startLeft  = res.size.left;
+				this.width      = res.size.width;
+				this.barWidthPX = uni.upx2px(this.barWidth);
+			});
+			// #endif
+			// #ifndef APP-NVUE
+			uni.createSelectorQuery().in(this).select('#gracesgslider').fields(
+				{size: true, rect:true}, (res) => {
+					if(res == null){
+						setTimeout(()=>{this.init();}, 100);
+						return;
+					}
+					this.startLeft  = res.left;
+					this.width      = res.width;
+					this.barWidthPX = uni.upx2px(this.barWidth);
+				}
+			).exec();
+			// #endif
+		},
+		touchstart : function (e) {
+			if(!this.canSlide){return ;}
+			var touch = e.touches[0] || e.changedTouches[0];
+			this.changeBar(touch.pageX);
+		},
+		touchmove : function (e) {
+			if(!this.canSlide){return ;}
+			var touch = e.touches[0] || e.changedTouches[0];
+			this.changeBar(touch.pageX);
+		},
+		touchend : function (e) {
+			if(!this.canSlide){return ;}
+			var touch = e.touches[0] || e.changedTouches[0];
+			this.changeBar(touch.pageX, true);
+		},
+		changeBar : function(x){
+			var left = x - this.startLeft;
+			if(left <= 0){
+				this.left = 0;
+				this.$emit('change', 0);
+			}else if(left + this.barWidthPX > this.width){
+				left = this.width - this.barWidthPX;
+				this.left = left;
+				this.$emit('change', 100);
+			}else{
+				this.left = left;
+				var scale = this.left / (this.width - this.barWidthPX);
+				this.$emit('change', Math.round(scale * 100));
+			}
+		},
+		setProgress : function (value){
+			if(this.width < 1){ setTimeout(()=>{this.setProgress(value), 300}); return ;}
+			if(value < 0){value = 0;}
+			if(value > 100){value = 100;}
+			this.left = ( value / 100 ) * (this.width - this.barWidthPX);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-sg-slider{overflow:hidden; position:relative;}
+.gui-sg-slider-a-line{position:absolute; left:0; top:0;}
+.gui-sg-slider-bar{position:absolute; left:0; top:0; border-radius:50rpx; font-size:20rpx; text-align:center; color:#323232; overflow:hidden;}
+</style>

+ 36 - 0
GraceUI5/components/gui-skeleton.vue

@@ -0,0 +1,36 @@
+<template>
+	<view>
+		<view class="gui-skeleton-animate" 
+		:style="styles" 
+		v-if="!show"></view>
+		<view v-if="show">
+			<slot></slot>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name : "gui-skeleton",
+	data() {
+		return {
+			show:false
+		}
+	},
+	props:{
+		styles    : {type : String, default  : ''},
+		delayTime : {type : Number, default  : 800},
+		canShow   : {type : Boolean, default : true}
+	},
+	created:function(){
+		setTimeout(()=>{
+			if(this.canShow){this.show = true;}
+		}, this.delayTime);
+	}
+}
+</script>
+<style scoped>
+/* #ifndef APP-NVUE */
+@keyframes gui-skeleton-animate{0%{opacity:1;} 25%{opacity:0.5;} 50%{opacity:0.6;} 75%{opacity:1;} 100%{opacity:1;}}
+.gui-skeleton-animate{animation:gui-skeleton-animate 1.6s ease-in infinite;}
+/* #endif */
+</style>

+ 156 - 0
GraceUI5/components/gui-slide-list.vue

@@ -0,0 +1,156 @@
+<template>
+	<scroll-view class="gui-slide-list" :scroll-y="scY" 
+	:show-scrollbar="false" 
+	:style="{width:width+'rpx', height:height+'px'}" 
+	@scrolltolower="scrolltolower">
+		<view class="gui-slide-list-item gui-border-b" 
+		v-for="(item, index) in msgsIn" :key="index" 
+		:style="{width:width+'rpx'}">
+			<view class="gui-flex gui-rows gui-nowrap gui-align-items-center" 
+			:style="{
+			width:(width + btnWidth)+'rpx', overflow:'hidden',
+			transform:'translateX('+(moveIndex != index ? 0 : x)+'px)' 
+			}">
+				<view class="gui-slide-list-img-wrap" 
+				hover-class="gui-tap" 
+				@tap.stop.prevnet="itemTap(index)">
+					<image :src="item.img" class="gui-slide-list-img" 
+					mode="widthFix"></image>
+					<text class="gui-slide-list-point gui-block-text gui-bg-red gui-color-white" 
+					v-if="item.msgnumber > 0">{{item.msgnumber}}</text>
+				</view>
+				<view class="gui-slide-list-content" hover-class="gui-tap">
+					<gui-touch @thStart="thStart" @thMove="thMove" 
+					@thEnd="thEnd" @tapme="itemTap(index)" :datas="[index]">
+						<view class="gui-flex gui-rows gui-nowrap gui-space-between">
+						<text 
+						class="gui-slide-list-title-text gui-block-text">{{item.title}}</text>
+						<text 
+						class="gui-slide-list-desc gui-block-text">{{item.time}}</text>
+						</view>
+						<text 
+						class="gui-slide-list-desc gui-block-text gui-ellipsis">{{item.content}}</text>
+					</gui-touch>
+				</view>
+				<view class="gui-slide-btns gui-flex gui-rows gui-nowrap"
+				:style="{width:(btnWidth-2) +'rpx'}">
+					<text class="gui-slide-btn gui-block-text gui-text-center" 
+					v-for="(btn, btnIndex) in item.btns" :key="btnIndex" 
+					:style="{backgroundColor:btn.bgColor}" 
+					@tap.stop.prevnet="btnTap(index, btnIndex)">{{btn.name}}</text>
+				</view>
+			</view>
+		</view>
+		<!-- 加载组件 -->
+		<view style="padding:30rpx;">
+			<gui-loadmore ref="loadmoreinslidelist"></gui-loadmore>
+		</view>
+	</scroll-view>
+</template>
+<script>
+export default{
+	name  : "gui-slide-list",
+	props : {
+		width       : { type : Number, default : 750},
+		msgs        : { type : Array,  default : function(){return [];}},
+		btnWidth    : { type : Number, default : 320},
+		height      : { type : Number, default : 200},
+	},
+	data() {
+		return {
+			msgsIn    : [],
+			damping   : 0.29,
+			moveIndex : -1,
+			x         : 0,
+			oX        : 0,
+			scY       : true,
+			btnWidthpx:160,
+			touchStart:false
+		}
+	},
+	created:function(){
+		this.init(this.msgs);
+		this.btnWidthpx = (uni.upx2px(this.btnWidth) * -1) + 2;
+	},
+	watch:{
+		msgs : function(nv){this.init(nv);}
+	},
+	methods:{
+		init     : function(msgs){
+			this.moveIndex = -1;
+			this.msgsIn    = msgs;
+		},
+		thStart  : function(e, index){
+			this.x         = 0;
+			this.moveIndex = index[0];
+			this.damping   = 0.25;
+		},
+		thMove   : function (e, index){
+			var x          = e[0][0];
+			var y          = e[0][1];
+			if(Math.abs(x) < Math.abs(y)){
+				this.scY   = true;
+				return ;
+			}else{
+				this.scY   = false;
+			}
+			if(x < 0){
+				this.x += x * this.damping;
+				if(this.x < this.btnWidthpx){this.x = this.btnWidthpx;}
+				this.damping *= 1.02;
+			}else{
+				this.scY   = true;
+			}
+		},
+		thEnd    : function(e, index){
+			if(this.x > this.btnWidthpx / 8){
+				this.x = 0;
+			}else{
+				this.x = this.btnWidthpx;
+			}
+			this.scY   = true;
+			this.oX    = this.x;
+		},
+		btnTap  : function (index, indexBtn) {
+			this.$emit('btnTap',index, indexBtn);
+		},
+		itemTap : function (index) {
+			if(this.oX < 0){
+				this.oX = 0;
+				this.moveIndex = -1;
+				return ;
+			}
+			this.$emit('itemTap',index);
+			this.moveIndex = -1;
+			this.oX = 0;
+		},
+		scrolltolower : function () {
+			var laodStatus = this.$refs.loadmoreinslidelist.loadMoreStatus;
+			if(laodStatus == 0){
+				this.$emit('scrolltolower');
+			}
+		},
+		startLoadig : function(){
+			this.$refs.loadmoreinslidelist.loading();
+		},
+		nomore : function () {
+			this.$refs.loadmoreinslidelist.nomore();
+		},
+		endLoading : function(){
+			this.$refs.loadmoreinslidelist.stoploadmore();
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-slide-list{overflow:hidden;}
+.gui-slide-list-item{overflow:hidden; position:relative;}
+.gui-slide-list-img-wrap{font-size:0; position:relative; width:80rpx; height:80rpx; margin-left:25rpx; overflow:hidden;}
+.gui-slide-list-point{position:absolute; z-index:1; right:0; top:0;}
+.gui-slide-list-content{width:622rpx; overflow:hidden; padding-top:28rpx; padding-bottom:25rpx; margin-left:25rpx;}
+.gui-slide-list-title{overflow:hidden;}
+.gui-slide-btns{width:300rpx; height:116rpx;}
+.gui-slide-btn{width:100rpx; flex:1; height:125rpx; line-height:125rpx; height:125rpx; font-size:28rpx; color:#FFFFFF; overflow:hidden;}
+.gui-slide-list-img{width:80rpx; height:80rpx; border-radius:6rpx;} /* 列表图片外层样式 */
+.gui-slide-list-point{border-radius:32rpx; height:32rpx; line-height:32rpx; padding:0 10rpx; font-size:20rpx;} /* 消息数标签样式 */
+</style>

+ 95 - 0
GraceUI5/components/gui-slide-to-unlock.vue

@@ -0,0 +1,95 @@
+<template>
+	<view class="graceSlideToUnlock gui-border-box" 
+	:style="{
+	backgroundColor:bgColor, width:width+'rpx', 
+	padding:padding+'rpx', height:(size+padding*2)+'rpx'
+	}">
+		<text class="graceSlideToUnlock-text gui-block-text gui-icons" 
+		:class="[moving?'gui-fade':'']" 
+		:style="{
+			width:(width - padding* 2) +'rpx', 
+			lineHeight:size+'rpx', 
+			color:disabled?blockActiveColor:'#898989',
+			top : padding+'rpx'}">{{disabled?msgUnlock:msg}} &#xe601;&#xe601;</text>
+		<movable-area class="movable-area" 
+		:style="{
+			width:(width - padding* 2) +'rpx', 
+			height:size+'rpx', borderRadius:borderRadius,
+			top : padding+'rpx'}">
+			<movable-view direction="horizontal" 
+			@change="change" :x="moveX" :disabled="disabled" 
+			:style="{width:size+'rpx', height:size+'rpx'}">
+				<text class="graceSlideToUnlock-block gui-icons gui-block-text gui-color-white" 
+				v-if="!disabled" 
+				:style="{
+					backgroundColor:disabled ? blockActiveColor : blockColor, 
+					borderRadius:borderRadius, width:size+'rpx', height:size+'rpx', 
+					lineHeight:size+'rpx', color:iconColor, fontSize:iconSize}">&#xe641;</text>
+				<text class="graceSlideToUnlock-block gui-icons gui-block-text gui-color-white" 
+				v-if="disabled" :style="{
+					backgroundColor:disabled ? blockActiveColor : blockColor, 
+					borderRadius:borderRadius, width:size+'rpx', height:size+'rpx', 
+					lineHeight:size+'rpx', color:iconColor, fontSize:iconSize}">&#xe86a;</text>
+			</movable-view>
+		</movable-area>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-slide-to-unlock",
+	props : {
+		width            : {type : Number, default:690 },
+		padding          : {type : Number, default:6},
+		size             : {type : Number, default:68},
+		bgColor          : {type : String, default:'#F6F7F8'},
+		blockColor       : {type : String, default:'#008AFF'},
+		blockActiveColor : {type : String, default:'#39B55A'},
+		iconSize         : {type : String, default:'30rpx'},
+		iconColor        : {type : String, default:'#FFFFFF'},
+		borderRadius     : {type : String, default:'6rpx'},
+		msg              : {type : String, default:'请向右滑动滑块解锁'},
+		msgUnlock        : {type : String, default:'解锁成功'}
+	},
+	data() {
+		return {
+			maxWidth  : 300,
+			moveX     : 0,
+			disabled  : false,
+			locktimer : null,
+			moving    : false
+		}
+	},
+	created:function(){
+		this.maxWidth = uni.upx2px(this.width - this.padding * 2 - this.size - 2);
+		this.moveX    = uni.upx2px(this.padding); 
+	},
+	methods:{
+		change:function(e){
+			if(this.disabled){return ;}
+			this.moving    = true;
+			if(this.locktimer != null){clearTimeout(this.locktimer);}
+			this.locktimer = setTimeout(() => {
+				if(e.detail.x >= this.maxWidth){
+					this.moveX    = this.width;
+					this.disabled = true;
+					this.moving   = false;
+					this.$emit('unlock');
+				}else{
+					this.moveX = uni.upx2px(this.padding) + Math.random();
+					this.moving   = false;
+				}
+			}, 300);
+		}
+	}
+}
+</script>
+<style scoped>
+.graceSlideToUnlock{position:relative;}
+.movable-area{position:absolute; left:0; top:0; z-index:2;}
+.graceSlideToUnlock-block{text-align:center; opacity:0.88;}
+.graceSlideToUnlock-text{text-align:center; color:#FFFFFF; font-size:24rpx; position:absolute; left:0; top:0; z-index:1;}
+/* #ifndef APP-NVUE */
+@keyframes gui-fade{0%{opacity:1;} 50%{opacity:0;} 100%{opacity:1;}}
+.gui-fade{animation:gui-fade 2s ease-in infinite;}
+/* #endif */
+</style>

+ 44 - 0
GraceUI5/components/gui-speaker.vue

@@ -0,0 +1,44 @@
+<template>
+	<view class="gui-flex gui-rows gui-nowrap gui-align-items-center">
+		<view>
+			<slot name="icon"></slot>
+		</view>
+		<view class="gui-flex1">
+			<swiper :style="styles"
+			:vertical="vertical" @change="change" autoplay="true" :circular="true" 
+			:interval="interval" :current="current" class="gui-speaker-in">
+				<swiper-item  
+				v-for="(item, index) in items" :key="index">
+					<navigator :style="styles" 
+					:url="item.url" :open-type="item.opentype">
+						<text :style="styles" 
+						class="gui-block-text gui-ellipsis">{{item.title}}</text>
+					</navigator>
+				</swiper-item>
+			</swiper>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-speaker",
+	props : {
+		items     : {type:Array, default:function () {return [];}},
+		current   : {type:Number, default:0},
+		vertical  : {type:Boolean, default:false},
+		interval  : {type:Number, default:5000},
+		styles    : {type:String, default:'overflow:hidden; height:60rpx; line-height:60rpx; font-size:28rpx;'}
+	},
+	data() {
+		return {
+		}
+	},
+	methods:{
+		change:function (index) {
+			this.$emit('change', index.detail.current);
+		}
+	}
+}
+</script>
+<style scoped>
+</style>

+ 62 - 0
GraceUI5/components/gui-spread.vue

@@ -0,0 +1,62 @@
+<template name="graceSpread">
+	<view class="gui-spread" 
+	:class="[isShrink ? 'gui-transition-all' : '']" 
+	:style="{height:reHeight}">
+		<view :style="{paddingBottom: !isBtn && isShrink ? '80rpx' : '0rpx'}">
+			<slot></slot>
+		</view>
+		<text v-if="isBtn" @tap="spreadContent" 
+		:style="{color:btnColor, fontSize:btnTxtSize, zIndex:zIndex, width:width}"
+		class="gui-icons gui-block-text gui-spread-btn">&#xe69d; {{btnTxt}}</text>
+		
+		<text v-if="!isBtn && isShrink" @tap="shrinkContent" 
+		:style="{color:btnColor, fontSize:btnTxtSize, zIndex:zIndex, width:width}"
+		class="gui-icons gui-block-text gui-spread-btn">&#xe638; {{shrinkBtnTxt}}</text>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-spread",
+	props : {
+		width       : { type : String, default : "690rpx" },
+		height      : { type : String, default : "600rpx" },
+		btnTxt      : { type : String, default : "展开阅读全文" },
+		btnColor    : { type : String, default : "#999999" },
+		btnTxtSize  : { type : String, default : "28rpx" },
+		zIndex      : { type : Number, default : 1 },
+		isShrink    : { type : Boolean,default : false},
+		shrinkBtnTxt: { type : String, default : "收缩文章"}
+	},
+	data() {
+		return {
+			reHeight: "600px",
+			isBtn : true
+		}
+	},
+	created:function(){
+		this.reHeight = this.height;
+	},
+	methods: {
+		spreadContent : function () {
+			// #ifdef MP-BAIDU
+			this.reHeight = '';
+			// #endif
+			// #ifndef MP-BAIDU
+			this.reHeight = 'auto';
+			// #endif
+			// #ifdef APP-NVUE
+			this.reHeight = '';
+			// #endif
+			this.isBtn    = false;
+		},
+		shrinkContent : function () {
+			this.reHeight = this.height;
+			this.isBtn    = true;
+		}
+	},
+}
+</script>
+<style scoped>
+.gui-spread{overflow:hidden; position:relative;}
+.gui-spread-btn{height:80rpx; line-height:80rpx; background-color:rgba(255,255,255,0.9); position:absolute; z-index:999999; left:0; bottom:0; text-align:center;}
+</style>

+ 115 - 0
GraceUI5/components/gui-stags.vue

@@ -0,0 +1,115 @@
+<template>
+	<view class="gui-flex gui-wrap gui-rows">
+		<text v-if="type != 'remove'" 
+		v-for="(tag, idx) in tagsIn" :key="idx" 
+		:class="['gui-block-text', 'gui-ellipsis',
+			tag.checked ? checkedBg : defaultBg, 
+			tag.checked ? checkedColor : defaultColor]" 
+			:style="{
+				width:width == 0 ? '' : width+'rpx',
+				paddingLeft:padding+'rpx',
+				paddingRight:padding+'rpx',
+				lineHeight:(size*lineHeight)+'rpx',
+				height:(size*lineHeight)+'rpx', 
+				fontSize:size+'rpx',
+				borderRadius:borderRadius+'rpx',
+				marginRight:margin+'rpx',
+				marginBottom:margin+'rpx'
+			}" @tap="tapme(idx)">{{tag.text}}</text>
+		<view class="gui-flex gui-rows gui-space-between gui-align-items-center" 
+		:class="[defaultBg, defaultColor]"
+		v-for="(tag, idx) in tagsIn" :key="idx" 
+		:style="{
+			width:width == 0 ? '' : width+'rpx',
+			paddingLeft:padding+'rpx',
+			paddingRight:padding+'rpx',
+			borderRadius:borderRadius+'rpx',
+			marginRight:margin+'rpx',
+			marginBottom:margin+'rpx'
+		}" v-if="type == 'remove'" @tap="tapme(idx)">
+			<text class="gui-block-text gui-ellipsis" 
+			:style="{
+				lineHeight:(size*lineHeight)+'rpx',
+				height:(size*lineHeight)+'rpx', 
+				fontSize:size+'rpx'
+			}">{{tag.text}}</text>
+			<text class="gui-block-text gui-icons gui-tags-remove-btn"
+			:style="{
+				lineHeight:(size*lineHeight)+'rpx',
+				height:(size*lineHeight)+'rpx', 
+				fontSize:size+'rpx'
+			}">&#xe7a5;</text>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-stags", 
+	props : {
+		width        : {type:Number, default:0},
+		text         : {type:String, default:''},
+		size         : {type:Number, default:26},
+		lineHeight   : {type:Number, default:1.8},
+		padding      : {type:Number, default:15},
+		margin       : {type:Number, default:15},
+		defaultBg    : {type:String, default:'gui-bg-gray'},
+		defaultColor : {type:String, default:'gui-primary-color'},
+		borderRadius : {type:Number, default:6},
+		data         : {type:Array,  default:function(){return [];}},
+		tags         : {type:Array,  default: function(){return [];}},
+		type         : {type:String, default:'radio'},
+		checkedBg    : {type:String, default:'gui-bg-blue'},
+		checkedColor : {type:String, default:'gui-color-white'}
+	},
+	data() {
+		return {
+			tagsIn: []
+		}
+	},
+	created:function () {
+		this.tagsIn = this.tags;
+	},
+	watch:{
+		tags : function (val) {
+			this.tagsIn = val;
+		}
+	},
+	methods:{
+		tapme : function(idx){
+			if(this.type == "radio"){
+				if(this.tagsIn[idx].checked){
+					this.tagsIn[idx].checked  = false;
+					this.tagsIn.splice(idx,1, this.tagsIn[idx]);
+					this.$emit('change', -1, this.tagsIn);
+				}else{
+					for(let i = 0; i < this.tagsIn.length; i++){
+						this.tagsIn[i].checked  = false;
+						this.tagsIn.splice(i,1, this.tagsIn[i]);
+					}
+					this.tagsIn[idx].checked  = true;
+					this.tagsIn.splice(idx,1, this.tagsIn[idx]);
+					this.$emit('change', this.tagsIn[idx], this.tagsIn);
+				}
+				
+			}else if(this.type == 'checkbox'){
+				this.tagsIn[idx].checked  = !this.tagsIn[idx].checked;
+				this.tagsIn.splice(idx, 1, this.tagsIn[idx]);
+				// 记录选中的索引
+				var sedArr = [];
+				for(let i = 0; i < this.tagsIn.length; i++){
+					if(this.tagsIn[i].checked){
+						sedArr.push(i);
+					}
+				}
+				this.$emit('change', sedArr, this.tagsIn);
+			}else{
+				this.tagsIn.splice(idx, 1);
+				this.$emit('change', this.tagsIn);
+			}
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-tags-remove-btn{margin-left:10rpx;}
+</style>

+ 50 - 0
GraceUI5/components/gui-star.vue

@@ -0,0 +1,50 @@
+<template name="graceStart">
+	<view class="gui-flex gui-rows gui-nowrap">
+	  <view v-for="(item, index) in totalstars" 
+	  :style="{padding:padding}" :key="index" 
+	  @tap="changnum" :data-val="index">
+			<text class="gui-icons" 
+			:style="{'color': activecolor, 'font-size' : fontSize}" 
+			v-if="valueIn > index">&#xe634;</text>
+			<text class="gui-icons" 
+			:style="{'color': color, 'font-size' : fontSize}" 
+			v-else>&#xe634;</text>
+		</view>
+	</view>
+</template>
+<script>
+export default {
+	name: "gui-star",
+	props:{
+		fontSize     : { type : String,  default : '50rpx' },
+		totalstars   : { type : Number,  default : 5 },
+		starnum      : { type : Number,  default : 1 },
+		color        : { type : String,  default : '#E1E1E1' },
+		activecolor  : { type : String,  default : '#F5C359' },
+		cantap       : { type : Boolean, default : true },
+		padding      : { type : String,  default : '5rpx'}
+	},
+	data() {
+		return {
+			valueIn : 0
+		}
+	},
+	created:function(){
+		this.valueIn = this.starnum;	
+	},
+	watch:{
+		starnum : function (val) {
+			this.valueIn = this.starnum;
+		}
+	},
+	methods: {
+		changnum : function(e){
+			if (!this.cantap){return null;}
+			this.valueIn = Number(e.currentTarget.dataset.val) + 1;
+			this.$emit("change", Number(this.valueIn));
+		}
+	}
+}
+</script>
+<style scoped>
+</style>

+ 120 - 0
GraceUI5/components/gui-step-box.vue

@@ -0,0 +1,120 @@
+<template>
+	<view class="gui-flex gui-rows gui-nowrap gui-align-items-center" 
+	:style="{width:width}">
+		<view hover-class="gui-tap">
+			<text class="gui-block-text gui-text-center gui-number-box-button" @tap.stop="reduce"
+			:style="buttonStyle">-</text>
+		</view>
+		<input class="gui-number-box-input" :disabled="disabled"
+		v-model="inputNumber" type="digit" @blur="inputval"  
+		:style="inputStyle" />
+		<view hover-class="gui-tap">
+			<text class="gui-block-text gui-text-center gui-number-box-button" @tap.stop="add"
+			:style="buttonStyle">+</text>
+		</view>
+	</view>
+</template>
+<script>
+export default {
+	name  : "gui-step-box",
+	props : {
+		width          : { type : String,   default : '200rpx' },
+		value          : { type : Number,  default : 0 },
+		step           : { type : Number,  default : 1 },
+		maxNum         : { type : Number,  default : 9999 },
+		minNum         : { type : Number,  default : 0 },
+		buttonStyle    : { type : String,  default : 'width:66rpx; font-size:38rpx; color:rgba(69, 90, 100, 0.6);' },
+		inputStyle     : { type : String,  default : 'line-height:58rpx; height:58rpx; font-size:26rpx; color:#2B2E3D; background-color:#F6F7F8; border-radius:8rpx;'},
+		disabled       : { type : Boolean, default : false },
+		index          : { type : Number,  default : 0 },
+		datas          : { type : Array,   default : function () {
+			return [];
+		}},
+		decimal        : { type : Number, default:2}
+	},
+	data() {
+		return {
+			inputNumber    : 0,
+			callbackNumber : 0
+		}
+	},
+	created:function(){
+		this.inputNumber = Number(this.value);
+	},
+	watch:{
+		value       : function(val, vo){
+			this.inputNumber = Number(val);
+		},
+		inputNumber :function(val, vo){
+			if(val == ''){
+				return ;
+			}
+			val = Number(val);
+			if(isNaN(val)){
+				setTimeout(()=>{this.inputNumber = Number(vo);}, 200);  return; 
+			}
+			var newVal = this.decimalVal(val);
+			if(newVal != val){
+				setTimeout(()=>{this.inputNumber = Number(newVal);}, 200);  
+				return;
+			}
+			if(val > this.maxNum){
+				setTimeout(()=>{this.inputNumber = this.maxNum;}, 200);
+				return ;
+			}
+			if(val < this.minNum){
+				setTimeout(()=>{this.inputNumber = this.minNum;}, 200);
+				return ;
+			}
+		}
+	},
+	methods : {
+		add      : function(){
+			var newVal = Number(this.inputNumber) + Number(this.step);
+			newVal = this.decimalVal(newVal);
+			if(newVal > this.maxNum){return ;}
+			this.inputNumber = Number(newVal);
+			setTimeout(()=>{
+				this.$emit('change', [this.inputNumber, this.index, this.datas]);
+			}, 300);
+		},
+		reduce   : function () {
+			var newVal = Number(this.inputNumber) - Number(this.step);
+			newVal = this.decimalVal(newVal);
+			if(newVal < this.minNum){return ;}
+			this.inputNumber = newVal;
+			setTimeout(()=>{
+				this.$emit('change', [this.inputNumber, this.index, this.datas]);
+			}, 300);
+		},
+		inputval  : function (e) {
+			this.inputNumber = e.detail.value;
+			setTimeout(()=>{
+				this.$emit('change', [this.inputNumber, this.index, this.datas]);
+			}, 300);
+		},
+		decimalVal : function (val) {
+			var isDecimal = String(val).indexOf(".");
+			if(isDecimal != -1){
+				val = val.toFixed(this.decimal);
+				var valArr = String(val).split('.');
+				if(valArr[1].length > this.decimal){
+					valArr[1] = valArr[1].substr(0, this.decimal);
+					val = Number(valArr.join('.'));
+				} 
+			}
+			return val ;
+		}
+	}
+}
+</script>
+<style scoped>
+/* #ifndef APP-NVUE */
+.gui-number-box-input{text-align:center; width:500rpx;}
+.gui-number-box-button{overflow:hidden; flex-shrink:0;}
+/* #endif */
+/* #ifdef APP-NVUE */
+.gui-number-box-input{text-align:center; width:50rpx; flex:1;}
+.gui-number-box-button{overflow:hidden;}
+/* #endif */
+</style>

+ 202 - 0
GraceUI5/components/gui-submit-button.vue

@@ -0,0 +1,202 @@
+<template>
+	<view class="gui-sbutton-in" 
+	:class="[
+	baseClass, 
+	status == 1 ? defaultClass : '' ,
+	status == 2 ? loadingClass : '' ,
+	status == 3 ? successClass : '', 
+	status >= 3 ? 'gui-btn-fade-in' : '', 
+	status == 4 ? failClass : '',]">
+		<view class="gui-sbutton" v-if="status == 1">
+			<slot name="default"></slot>
+		</view>
+		<view 
+		class="gui-sbutton gui-flex gui-rows gui-nowrap gui-justify-content-center gui-align-items-center" 
+		:class="[baseClass]" 
+		v-if="status == 2">
+			<view ref="loadingPoints1" :class="[loaingPointClass, 'gui-sbutton-loading1']"></view>
+			<view ref="loadingPoints2" :class="[loaingPointClass, 'gui-sbutton-loading2']"></view>
+			<view ref="loadingPoints3" :class="[loaingPointClass, 'gui-sbutton-loading3']"></view>
+		</view>
+		<view class="gui-sbutton" v-if="status == 3">
+			<slot name="success"></slot>
+		</view>
+		<view class="gui-sbutton" v-if="status == 4">
+			<slot name="error"></slot>
+		</view>
+		<view class="gui-sbutton gui-sbutton-slot"><slot name="realBtn"></slot></view>
+	</view>
+</template>
+<script>
+// #ifdef APP-NVUE
+const BindingX = uni.requireNativePlugin('bindingx');
+// #endif
+export default{
+	name    : "gui-submit-button",
+	props   : {
+		titleClass        : {type:String, default : 'gui-sbutton-text'},
+		loaingPointClass  : {type:String, default : 'gui-sbutton-loading-point'},
+		baseClass         : {type:String, default : 'gui-sbutton'},
+		defaultClass      : {type:String, default : 'gui-sbutton-default'},
+		loadingClass      : {type:String, default : 'gui-sbutton-loading'},
+		successClass      : {type:String, default : 'gui-sbutton-success'},
+		failClass         : {type:String, default : 'gui-sbutton-fail'}
+	},
+	data() {
+		return {
+			status         : 1,
+			animateTimer   : 800,
+			BindingXObjs   : [null,null,null],
+			AnimateObjs    : [null,null,null],
+			intervalID     : null
+		}
+	},
+	// #ifdef APP-NVUE
+	watch:{
+		status:function(val){
+			switch(val){
+				case 1 :
+					clearInterval(this.intervalID);
+				break;
+				case 2 :
+					setTimeout(()=>{
+						this.getRefs('loadingPoints1', 0, (refs)=>{
+							this.BindingXObjs = [
+								refs.ref,
+								this.$refs.loadingPoints2.ref,
+								this.$refs.loadingPoints3.ref
+							];
+							this.startAnimate();
+						});
+					}, 100);
+					this.intervalID = setInterval(()=>{
+						this.startAnimate();
+					}, 1000);
+				break;
+				case 3 :
+					clearInterval(this.intervalID);
+				break;
+				case 4 :
+					clearInterval(this.intervalID);
+				break;
+				default : 
+					clearInterval(this.intervalID);
+			}
+		}
+	},
+	// #endif
+	methods : {
+		reset : function () {
+			this.status = 1;
+		},
+		loading : function () {
+			this.status = 2;
+		},
+		success : function () {
+			this.status = 3;
+		},
+		fail : function () {
+			this.status = 4;
+		},
+		// #ifdef APP-NVUE
+		startAnimate   : function(){
+			this.loadingAnimate(0);
+			setTimeout(()=>{this.loadingAnimate(1);},300);
+			setTimeout(()=>{this.loadingAnimate(2);},600);
+		},
+		loadingAnimate : function (id) {
+			this.AnimateObjs[id] = BindingX.bind({
+				eventType      : 'timing',
+				exitExpression : 't>'+this.animateTimer,
+				props          : [
+					{
+						element    : this.BindingXObjs[id], 
+						property   : 'transform.scale',
+						expression : "1+t/"+this.animateTimer+"/3"
+					},
+					{
+						element    : this.BindingXObjs[id], 
+						property   : 'opacity',
+						expression : "0.6+t/"+this.animateTimer
+					}
+				]
+			}, (e)=>{
+				if(e.state === 'exit') {
+					BindingX.unbind({
+						token : this.AnimateObjs[id].token,
+						eventType: 'timing'
+					});
+					this.AnimateObjs[id] = BindingX.bind({
+						eventType      : 'timing',
+						exitExpression : 't>'+this.animateTimer,
+						props          : [
+							{
+								element    : this.BindingXObjs[id], 
+								property   : 'transform.scale',
+								expression : "1.35-t/"+this.animateTimer+"/3"
+							},
+							{
+								element    : this.BindingXObjs[id], 
+								property   : 'opacity',
+								expression : "1.6-t/"+this.animateTimer
+							}
+						]
+					}, (e)=>{
+						if(e.state === 'exit') {
+							BindingX.unbind({
+								token : this.AnimateObjs[id].token,
+								eventType: 'timing'
+							});
+						}
+					});
+				}
+			});
+		},
+		// #endif
+		getRefs        : function(ref, count, fun){
+			if(count >= 30){return null;}
+			var refReturn = this.$refs[ref];
+			if(refReturn){
+				fun(refReturn);
+				return;
+			}else{
+				count++;
+				setTimeout(()=>{
+					this.getRefs(ref, count, fun);
+				}, 50);
+			}
+		}
+	}
+}
+</script>
+<style>
+.gui-sbutton-in{position:relative; overflow:hidden;}
+.gui-sbutton-slot{opacity:0; position:absolute; left:0; top:0; z-index:1;}
+
+/* #ifndef APP-NVUE */
+.gui-sbutton-loading1{animation:gui-sbutton-loading1 1200ms ease-in infinite;}
+@keyframes gui-sbutton-loading1{
+	0%{transform: translateY(0px);}
+	25%{transform: translateY(2px);}
+	100%{transform: translateY(0px);}
+}
+.gui-sbutton-loading2{animation:gui-sbutton-loading2 1200ms ease-in infinite;}
+@keyframes gui-sbutton-loading2{
+	0%{transform: translateY(0px);}
+	25%{transform: translateY(0px);}
+	50%{transform: translateY(2px);}
+	75%{transform: translateY(2px);}
+	100%{transform: translateY(0px);}
+}
+.gui-sbutton-loading3{animation:gui-sbutton-loading3 1200ms ease-in infinite;}
+@keyframes gui-sbutton-loading3{
+	0%{transform: translateY(0px);}
+	25%{transform: translateY(0px);}
+	50%{transform: translateY(0px);}
+	75%{transform: translateY(2px);}
+	100%{transform: translateY(0px);}
+}
+@keyframes gui-btn-fade-in{0%{opacity:0.5;} 100%{opacity:1;}}
+.gui-btn-fade-in{animation:gui-btn-fade-in 350ms ease-in forwards;}
+/* #endif */
+</style>

+ 111 - 0
GraceUI5/components/gui-submit-comment.vue

@@ -0,0 +1,111 @@
+<template>
+	<gui-popup 
+	ref="guipopupforsubcomment" 
+	position="bottom" 
+	:canCloseByShade="true" 
+	@close="closePop" 
+	:zIndex="zIndex">
+		<view class="gui-comments gui-bg-white" 
+		@tap.stop.prevent="stopfun">
+			<text class="gui-comments-at gui-block-text" 
+			:style="{color:atColor}" 
+			v-if="comment.at != ''">@ {{comment.at}}</text>
+			<view style="height:20rpx;" 
+			v-if="comment.at == ''"></view>
+			<view 
+			class="gui-flex gui-rows gui-nowrap gui-space-between">
+				<view class="gui-comments-img gui-relative" 
+				v-if="comment.img != ''">
+					<image :src="comment.img" 
+					class="gui-comments-img-in" 
+					mode="widthFix"></image>
+					<text class="gui-comments-img-remove gui-icons" 
+					@tap="removeImg" 
+					:style="{color:removeBtnColor}">&#xe78a;</text>
+				</view>
+				<textarea :show-confirm-bar="false" 
+				cursor-spacing="200" v-model="comment.content" 
+				class="gui-comments-textarea gui-border-box gui-bg-gray" 
+				:placeholder="placeholder" />
+			</view>
+			<view class="gui-flex gui-rows gui-space-between gui-align-items-center">
+				<text class="gui-comments-btns gui-icons gui-color-gray" 
+				@tap="selectImg" v-if="isImg">&#xe63d;</text>
+				<text class="gui-comments-btns" v-if="!isImg"></text>
+				<view class="gui-comments-submit" 
+				hover-class="gui-tap">
+					<text class="gui-comments-btns gui-comments-submit gui-icons" 
+					:style="{color:submitColor}" 
+					@tap="submit">提交</text>
+				</view>
+			</view>
+			<view class="">
+				<gui-iphone-bottom></gui-iphone-bottom>
+			</view>
+		</view>
+	</gui-popup>
+</template>
+<script>
+export default{
+	name  : "gui-submit-comment",
+	props : {
+		placeholder    : { type : String,  default : "说点什么吧"},
+		isImg          : { type : Boolean, default : true},
+		atColor        : { type : String,  default : '#008AFF'},
+		submitColor    : { type : String,  default : '#008AFF'},
+		removeBtnColor : { type : String,  default : '#FF0036'},
+		zIndex         : { type : Number,  default : 999}
+	},
+	data() {
+		return {
+			comment : {img:'', content:'',at:''}
+		}
+	},
+	methods:{
+		open       : function(){
+			this.$refs.guipopupforsubcomment.open();
+		},
+		close      : function(){
+			this.$refs.guipopupforsubcomment.close();
+			this.$emit('close'); 
+		},
+		closePop   : function(){
+			this.$emit('close'); 
+		},
+		stopfun    : function(e){
+			e.stopPropagation();
+			return null;
+		},
+		submit     : function () {
+			this.$emit('submit', this.comment); 
+			this.close();
+			this.comment = {img:'', content:'',at:''}
+		},
+		selectImg  : function(){
+			uni.chooseImage({
+				count:1,
+				success:(res)=>{this.comment.img = res.tempFilePaths[0];}
+			});
+		},
+		removeImg  : function () {
+			this.comment.img = '';
+		},
+		setAt      : function(name){
+			this.comment.at = name;
+		},
+		setContent : function (content) {
+			this.comment.content = content;
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-comments{padding:10rpx 30rpx;}
+.gui-comments-at{line-height:80rpx; height:80rpx; font-size:28rpx; font-weight:bold;}
+.gui-comments-textarea{width:200rpx; border-radius:8rpx; padding:15rpx; font-size:26rpx; line-height:36rpx; height:160rpx; flex:1;}
+.gui-comments-img{width:160rpx; height:160rpx; margin-right:25rpx; font-size:0; overflow:hidden; border-radius:8rpx;}
+.gui-comments-img-in{width:160rpx;}
+.gui-comments-img-remove{width:60rpx; height:60rpx; line-height:60rpx; position:absolute; right:0; top:0; text-align:center; font-size:50rpx; color:#FF0036;}
+.gui-comments-btns{width:96rpx; height:80rpx; line-height:80rpx; font-size:44rpx;}
+.gui-comments-submit{width:180rpx; text-align:right; font-size:28rpx;}
+</style>

+ 175 - 0
GraceUI5/components/gui-swiper.vue

@@ -0,0 +1,175 @@
+<template>
+	<view class="gui-swiper-card-wrap">
+		<swiper :style="{width:width+'rpx', height:heightIn+'rpx'}" 
+		class="gui-swiper-card" 
+		:indicator-dots="false" :interval="interval" :circular="true" 
+		:autoplay="autoplay" :current="currentIndex" 
+		:previous-margin="spacing+'rpx'" :next-margin="spacing+'rpx'" 
+		@change="swiperchange">
+			<swiper-item 
+			v-for="(item, index) in swiperItems" 
+			:key="index" 
+			class="gui-swiper-card-item gui-border-box">
+				<navigator 
+				class="gui-swiper-card-nav gui-transition-all" 
+				:url="item.url" :open-type="item.opentype" hover-class="none" 
+				v-if="item.opentype != 'click'" 
+				:style="{paddingLeft:current != index ? padding +'rpx':'0rpx',
+				paddingRight:current != index ? padding +'rpx':'0rpx',
+				paddingTop:current != index ? paddingY +'rpx':'0rpx',
+				paddingBottom:current != index ? paddingY +'rpx':'0rpx'}">
+					<image 
+					:style="{
+						borderRadius : borderRadius, 
+						width:current != index ? widthInSamll+'rpx':widthIn+'rpx',
+						height:current != index ? heightInSmall+'rpx':heightIn+'rpx',
+						opacity:current != index ? opacity : 1
+					}" 
+					:src="item.img" 
+					:mode="imgMode" 
+					class="gui-swiper-card-image gui-transition-all" />
+				</navigator>
+				<view 
+				class="gui-swiper-card-nav gui-transition-all" 
+				hover-class="none" 
+				v-if="item.opentype == 'click'" 
+				@tap.stop="taped" 
+				:data-index="index" 
+				:style="{paddingLeft:current != index ? padding +'rpx':'0rpx',
+				paddingRight:current != index ? padding +'rpx':'0rpx',
+				paddingTop:current != index ? paddingY +'rpx':'0rpx',
+				paddingBottom:current != index ? paddingY +'rpx':'0rpx'}">
+					<image 
+					:style="{
+						borderRadius : borderRadius, 
+						width:current != index ? widthInSamll+'rpx':widthIn+'rpx',
+						height:current != index ? heightInSmall+'rpx':heightIn+'rpx',
+						opacity:current != index ? opacity : 1
+					}" 
+					:src="item.img" 
+					:mode="imgMode" 
+					class="gui-swiper-card-image gui-transition-all" />
+				</view>
+				<view 
+				v-if="indicatorType == 'number'" 
+				class="gui-indicator-dot-numbers gui-flex gui-rows gui-nowrap" 
+				:style="{
+					height:indicatorBarHeight+'rpx', backgroundColor:indicatorBarBgColor, 
+					'border-bottom-left-radius':borderRadius, 'border-bottom-right-radius':borderRadius,
+					width:current != index ? widthInSamll+'rpx':widthIn+'rpx', 
+					left:current != index ? padding+'rpx':'0rpx', bottom:current != index ? paddingY+'rpx':'0rpx'}">
+					<text class="gui-indicator-dot-text" 
+					:style="{paddingLeft:'20rpx', 'fontStyle':'italic', color:titleColor}">{{index+1}}</text>
+					<text class="gui-indicator-dot-text" 
+					:style="{'fontSize':'36rpx', color:titleColor}">/</text>
+					<text class="gui-indicator-dot-text" 
+					:style="{fontSize:'28rpx', paddingRight:'20rpx', fontStyle:'italic', color:titleColor}">{{swiperItems.length}}</text>
+					<text class="gui-swiper-text gui-block-text gui-flex1 gui-ellipsis" 
+					:style="{color:titleColor, fontSize:titleSize, height:indicatorBarHeight+'rpx', 
+					lineHeight:indicatorBarHeight+'rpx'}">{{item.title}}</text>
+				</view>
+			</swiper-item>
+		</swiper>
+		<view 
+		class="gui-indicator-dots gui-flex gui-rows gui-nowrap gui-justify-content-center gui-align-items-center gui-border-box" 
+		v-if="indicatorType == 'dot'" 
+		:style="{width:width+'rpx', height:indicatorBarHeight+'rpx', position:indicatorPosition, 
+		paddingLeft:spacing+'rpx', paddingRight:spacing+'rpx', 'justify-content':indicatorDirection}">
+			<view class="gui-indicator-dots-wrap gui-flex gui-rows gui-nowrap gui-justify-content-center">
+				<view 
+				v-for="(item, index) in swiperItems" :key="index" 
+				:class="['gui-indicator-dot',current == index ? 'dot-show' : '']" 
+				:style="{
+					width           : current != index ? indicatorWidth+'rpx' : indicatorActiveWidth +'rpx',
+					height          : indicatorHeight+'rpx',
+					borderRadius    : indicatorRadius+'rpx',
+					backgroundColor : current != index ? indicatorColor : indicatorActiveColor}"></view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-swiper",
+	props : {
+		width :{ type : Number, default : 750 },
+		height:{ type : Number, default : 300 },
+		swiperItems : { type : Array, default : function(){return new Array();} },
+		borderRadius : { type : String, default : '10rpx'},
+		indicatorBarHeight:{type : Number, default : 68},
+		indicatorBarBgColor:{type : String, default : 'rgba(0,0,0,0)'},
+		indicatorWidth : { type:Number, default:18 },
+		indicatorActiveWidth :{ type:Number, default:18 },
+		indicatorHeight : { type:Number, default:18 },
+		indicatorRadius:{ type:Number, default:18 },
+		indicatorColor : { type : String, default : "rgba(255, 255, 255, 0.6)" },
+		indicatorActiveColor : { type : String, default : "#2B2E3D" },
+		indicatorType:{ type : String, default : "dot" },
+		indicatorPosition:{ type : String, default : "absolute" },
+		indicatorDirection:{type:String, default:'center'},
+		spacing : { type : Number, default : 50 },
+		padding : { type : Number, default : 26 },
+		interval : { type : Number, default : 5000 },
+		autoplay : { type : Boolean, default : true },
+		currentIndex : { type : Number, default : 0 },
+		opacity:{ type : Number, default:0.66},
+		titleColor:{type:String, default:"#FFFFFF"},
+		titleSize:{type:String, default:"28rpx"},
+		imgMode:{type:String, default:'widthFix'}
+	},
+	data() {
+		return {
+			current : 0,
+			isReady : false,
+			widthIn : 750,
+			heightIn  : 300,
+			widthInSamll:700,
+			heightInSmall:280,
+			paddingY:0
+		}
+	},
+	watch:{
+		currentIndex : function (val) {
+			this.current = val;
+		}
+	},
+	created:function(){
+		this.current = this.currentIndex;
+		this.init();
+	},
+	methods:{
+		init : function(){
+			// 图片宽高计算
+			this.widthIn   = this.width - this.spacing*2;
+			this.heightIn  = this.height / this.width * this.widthIn;
+			this.paddingY  = this.padding * this.height / this.width;
+			this.widthInSamll  = this.widthIn -  this.padding * 2;
+			this.heightInSmall = this.heightIn - this.paddingY * 2;
+		},
+		swiperchange : function (e) {
+			var current = e.detail.current;
+			this.current = current;
+			this.$emit('swiperchange', current);
+		},
+		taped : function(e){
+			this.$emit('taped', e.currentTarget.dataset.index);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-swiper-card-wrap{position:relative;}
+.gui-swiper-card{overflow:hidden;}
+.gui-swiper-card-item{font-size:0; overflow:hidden; line-height:0;}
+.gui-swiper-card-nav{font-size:0; position:relative;}
+.gui-indicator-dots{width:750rpx; overflow:hidden; z-index:1; left:0; bottom:0;}
+.gui-indicator-dot{margin:6rpx;}
+.gui-indicator-dots-wrap{padding:0 20rpx;}
+.gui-indicator-dot-text{text-align:center; line-height:68rpx; padding:0 4rpx; color:#FFFFFF; font-size:32rpx;}
+.gui-indicator-dot-numbers{overflow:hidden; align-items:center; position:absolute; z-index:1; left:0; bottom:0;}
+.gui-swiper-text{width:200rpx; line-height:68rpx; padding-right:25rpx; overflow:hidden;}
+/* #ifndef APP-NVUE */
+@keyframes dot-show{from{opacity:0.1;}to{opacity:1;}}
+.dot-show{animation:dot-show 300ms linear forwards;}
+/* #endif */
+</style>

+ 157 - 0
GraceUI5/components/gui-switch-navigation.vue

@@ -0,0 +1,157 @@
+<template>
+	<scroll-view 
+	:scroll-with-animation="scorllAnimation" 
+	:scroll-x="true" 
+	:show-scrollbar="false" 
+	:class="['gui-scroll-x', isCenter ? 'gui-nav-center' : '']" 
+	:style="{width:width+'rpx'}" 
+	:scroll-into-view="autoLeft" 
+	:scroll-left="scrollLeft">
+		<view 
+		class="gui-scroll-x-items gui-columns" 
+		:id="'tab-'+index+(random+'')" 
+		:style="{
+			width:size == 0 ? 'auto' : size+'rpx', 
+			marginRight:margin+'rpx', 
+			paddingLeft:padding, 
+			paddingRight:padding
+		}" 
+		v-for="(item, index) in itemsIn" 
+		:key="index" 
+		@tap="change" 
+		:data-index="index">
+			<view class="gui-flex gui-nowrap gui-align-items-start" 
+			:class="[textAlign == 'center' ? 'gui-justify-content-center' : '']">
+				<text 
+				:class="[
+					'gui-block-text', 
+					'gui-border-box', 
+					currentIndexIn == index ? 'nav-active' : ''
+				]" 
+				:style="{
+					color:currentIndexIn == index ? activeColor : color, 
+					textAlign : textAlign, lineHeight:lineHeight, 
+					fontSize:currentIndexIn == index ? activeFontSize : fontSize, 
+					fontWeight:currentIndexIn == index ? activeFontWeight : ''}">{{item.name}}</text>
+				<view v-if="item.tips">
+					<text
+					v-if="item.tips != 'point'" 
+					class="gui-nav-tips gui-block-text" 
+					:style="tipsStyle">{{item.tips}}</text>
+					<text
+					v-else 
+					class="gui-nav-tips gui-block-text" 
+					:style="tipsStyle+'; width:12rpx; height:12rpx; over-flow:hidden; padding:0rpx; margin-top:10rpx;'"></text>
+				</view>
+			</view>
+			<view 
+			class="gui-flex gui-rows" 
+			:style="{justifyContent:activeDirection}">
+				<view class="nav-active-line" 
+				:class="[currentIndexIn == index && animatie ?'gui-nav-scale':'']" 
+				:style="{
+					backgroundImage:activeLineBg, width:activeLineWidth, 
+					height:activeLineHeight, borderRadius:activeLineRadius
+				}" 
+				v-if="currentIndexIn == index"></view>
+			</view>
+		</view>
+	</scroll-view>
+</template>
+<script>
+export default {
+	name  : "gui-switch-navigation",
+	props : {
+		width              : {type : Number,  default : 690},
+		isCenter           : {type : Boolean, default : false},
+		currentIndex       : {type : Number,  default : 0},
+		size               : {type : Number,  default : 120},
+		fontSize           : {type : String,  default : '28rpx'},
+		activeFontSize     : {type : String,  default : '28rpx'},
+		items              : {type : Array,   default : function () {return []}},
+		activeLineBg       : {type : String,  default : "linear-gradient(to right, #66BFFF,#3388FF)"},
+		color              : {type : String,  default : "#333333"},
+		activeColor        : {type : String,  default : "#333333"},
+		activeLineHeight   : {type : String,  default : '6rpx'},
+		activeLineWidth    : {type : String,  default : "36rpx"},
+		activeLineRadius   : {type : String,  default : "0rpx"},
+		activeDirection    : {type : String,  default : ""},
+		activeFontWeight   : {type : Number,  default : 700},
+		margin             : {type : Number,  default : 0},
+		textAlign          : {type : String,  default : ''},
+		lineHeight         : {type : String,  default : '50rpx'},
+		padding            : {type : String,  default : '0rpx'},
+		animatie           : {type : Boolean, default : true},
+		scorllAnimation    : {type : Boolean, default : true},
+		// #ifdef APP-NVUE
+		tipsStyle          : {
+			type : String, 
+			default : 'background-color:#FF0000; padding-left:10rpx; padding-right:10rpx; color:#FFFFFF; font-size:22rpx',
+		}
+		// #endif
+		// #ifndef APP-NVUE
+		tipsStyle          : {
+			type : String,  
+			default : 'background-color:#FF0000; padding:0 10rpx; color:#FFFFFF; font-size:22rpx',
+		}
+		// #endif
+	},
+	data(){
+		return {
+			currentIndexIn : 0,
+			itemsIn        : [],
+			random         : 1,
+			scrollLeft     : 0,
+			scrllTimer     : null,
+			autoLeft       : ''
+		}
+	},
+	created:function(){
+		this.currentIndexIn = this.currentIndex;
+		this.itemsIn        = this.items;
+		this.random         = this.randomNum();
+	},
+	watch:{
+		currentIndex : function(value){
+			this.currentIndexIn = value;
+		},
+		currentIndexIn : function(val){
+			if(this.scrllTimer != null){clearTimeout(this.scrllTimer);}
+			this.scrllTimer = setTimeout(()=>{this.setLeft();}, 200);
+		},
+		items        : function(value){ this.itemsIn = value; }
+	},
+	methods:{
+		change    : function (e){
+			this.currentIndexIn = e.currentTarget.dataset.index;
+			this.$emit('change', Number(e.currentTarget.dataset.index))
+		},
+		randomNum : function () {
+			return parseInt(Math.random() * 1000);
+		},
+		setLeft   : function () {
+			if(this.size < 1){
+				this.autoLeft = 'tab-'+this.currentIndexIn+''+this.random;
+				return ;
+			}
+			var itemWidth = Number(this.margin) + Number(this.size);
+			var left      = (Number(this.currentIndexIn) + 1) * itemWidth - Number(this.width) / 2 - itemWidth / 2;
+			var maxLeft   = Number(this.itemsIn.length) * itemWidth - this.width;
+			maxLeft       = uni.upx2px(maxLeft - 30);
+			left          = uni.upx2px(left);
+			if(left > maxLeft){left = maxLeft;}
+			if(left < 0){left = 0;}
+			this.scrollLeft = left;
+		}
+	}
+}
+</script>
+<style scoped>
+.nav-active-line{margin-top:6rpx;}
+.gui-nav-center{justify-content:center; text-align:center;}
+/* #ifndef APP-NVUE */
+@keyframes gui-nav-scale{0%{transform: scale(0.1);} 100%{transform: scale(1);}}
+.gui-nav-scale{animation:gui-nav-scale 350ms forwards;}
+/* #endif */
+.gui-nav-tips{text-align:center; line-height:30rpx; height:30rpx; border-radius:50rpx; margin-top:5rpx; margin-left:8rpx;}
+</style>

+ 101 - 0
GraceUI5/components/gui-switch-navigation2.vue

@@ -0,0 +1,101 @@
+<template>
+	<scroll-view :scroll-with-animation="scorllAnimation" :scroll-x="true" :show-scrollbar="false" 
+	:class="['gui-scroll-x', isCenter ? 'gui-nav-center' : '']" 
+	:style="{width:width+'rpx'}" 
+	:scroll-left="scrollLeft">
+		<view class="gui-scroll-x-item gui-columns gui-relative" :id="'tab-'+index+(random+'')" 
+		:style="{width:size+'rpx', height:(lineHeight+lineHeightSamll+25)+'rpx'}" 
+		v-for="(item, index) in itemsIn" :key="index" @tap="navchange" :data-index="index">
+			<text 
+			:class="['gui-block-text','gui-text-center']"
+			:style="{
+			color:currentIndexIn == index ? activeColor : color, 
+			lineHeight:lineHeight+'rpx', height:lineHeight+'rpx',
+			fontSize:currentIndexIn == index ? activeFontSize : fontSize, 
+			fontWeight:currentIndexIn == index ? 'bold' : ''}">{{item.title}}</text>
+			<text :class="['gui-block-text','gui-text-center']"
+			:style="{
+			color:currentIndexIn == index ? activeColor : descColor, 
+			lineHeight:lineHeightSamll+'rpx', height:lineHeightSamll+'rpx', 
+			fontSize:fontSizeSmall}">{{item.desc}}</text>
+			<view class="nav2-active-line-in gui-flex gui-rows gui-justify-content-center gui-fade-in" 
+			v-if="currentIndexIn == index">
+				<view class="nav2-active-line" :style="{borderColor:activeColor}"></view>
+			</view>
+		</view>
+	</scroll-view>
+</template>
+<script>
+export default {
+	name  : "gui-switch-navigation2",
+	props : {
+		width           : {type : Number,  default : 690},
+		isCenter        : {type : Boolean, default : false},
+		currentIndex    : {type : Number,  default : 0},
+		size            : {type : Number,  default : 138},
+		fontSize        : {type : String,  default : '28rpx'},
+		activeFontSize  : {type : String,  default : '28rpx'},
+		lineHeight      : {type : Number,  default : 52},
+		fontSizeSmall   : {type : String,  default : '22rpx'},
+		lineHeightSamll : {type : Number,  default : 28},
+		items           : {type : Array,   default : function () {return []}},
+		color           : {type : String,  default : "#2B2E3D"},
+		descColor       : {type : String,  default : "#999999"},
+		activeColor     : {type : String,  default : "#3688FF"},
+		autoLeft        : {type : String,  default : ''},
+		scorllAnimation : {type : Boolean, default : true},
+	},
+	data() {
+		return {
+			currentIndexIn : 0,
+			itemsIn        : [],
+			random         : 1,
+			scrollLeft     : 0,
+			scrllTimer     : null
+		}
+	},
+	created:function(){
+		this.currentIndexIn = this.currentIndex;
+		this.itemsIn        = this.items;
+		this.random         = this.randomNum();
+	},
+	watch:{
+		currentIndex : function(value){ this.currentIndexIn = value; },
+		items        : function(value){ this.itemsIn = value; },
+		currentIndexIn : function(val){
+			if(this.isCenter){return ;}
+			if(this.scrllTimer != null){clearTimeout(this.scrllTimer);}
+			this.scrllTimer = setTimeout(()=>{this.setLeft();}, 200);
+		}
+	},
+	methods:{
+		navchange : function (e){
+			this.currentIndexIn = e.currentTarget.dataset.index;
+			this.$emit('change', Number(e.currentTarget.dataset.index))
+		},
+		randomNum : function () {
+			return parseInt(Math.random() * 1000);
+		},
+		setLeft   : function () {
+			if(this.isCenter){return ;}
+			var itemWidth =  Number(this.size);
+			var left      = (Number(this.currentIndexIn) + 1) * itemWidth - Number(this.width) / 2 - itemWidth / 2;
+			var maxLeft   = Number(this.itemsIn.length) * itemWidth - this.width;
+			maxLeft       = uni.upx2px(maxLeft);
+			left          = uni.upx2px(left);
+			if(left > maxLeft){left = maxLeft;}
+			if(left < 0){left = 0;}
+			this.scrollLeft = left;
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-nav-center{justify-content:center; text-align:center;}
+.nav2-active-line-in{height:18rpx; overflow:hidden; margin-top:6rpx;}
+.nav2-active-line{width:18rpx; height:18rpx; border-radius:20rpx; 
+border-width:6rpx; border-style:solid; margin-top:-15rpx;}
+/* #ifdef APP-NVUE */
+.nav2-active-line{width:30rpx; height:30rpx;}
+/* #endif */
+</style>

+ 52 - 0
GraceUI5/components/gui-tags.vue

@@ -0,0 +1,52 @@
+<template>
+	<text :class="[
+		bgClass, 'gui-block-text', 'gui-tags', 'gui-ellipsis', 'gui-border', 
+		tapping ? 'gui-tag-opacity':'']" 
+		:style="{
+		width:width == 0 ? '' : width+'rpx',
+		color:color,
+		paddingLeft:padding+'rpx',
+		paddingRight:padding+'rpx',
+		lineHeight:(size*lineHeight)+'rpx',
+		height:(size*lineHeight)+'rpx', 
+		fontSize:size+'rpx',
+		borderRadius:borderRadius+'rpx',
+		marginRight:margin+'rpx',
+		marginBottom:margin+'rpx',
+		borderColor:borderColor
+		}" @tap="tapme">{{text}}</text>
+</template>
+<script>
+export default{
+	name  : "gui-tags",
+	props : {
+		width        : {type:Number, default:0},
+		text         : {type:String, default:''},
+		size         : {type:Number, default:26},
+		lineHeight   : {type:Number, default:1.8},
+		padding      : {type:Number, default:15},
+		margin       : {type:Number, default:15},
+		bgClass      : {type:String, default:'gui-bg-blue'},
+		color        : {type:String, default:'#FFFFFF'},
+		borderRadius : {type:Number, default:6},
+		data         : {type:Array, default:function(){return [];}},
+		borderColor  : {type:String, default:'#FFFFFF'}
+	},
+	data() {
+		return {
+			tapping : false
+		}
+	},
+	methods:{
+		tapme : function(){
+			this.tapping = true;
+			setTimeout(()=>{this.tapping = false}, 200);
+			this.$emit('tapme', this.data);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-tags{text-align:center;}
+.gui-tag-opacity{opacity:0.88;}
+</style>

+ 96 - 0
GraceUI5/components/gui-top-message.vue

@@ -0,0 +1,96 @@
+<template>
+	<view class="gui-top-message" 
+	ref="guipopupfortopmsg" 
+	v-if="show" 
+	:style="{top : navHeight+'px'}" 
+	:class="[out ? 'gui-top-message-out' : 'gui-top-message-in']">
+		<slot></slot>
+	</view>
+</template>
+<script>
+// #ifdef APP-NVUE
+const animation = weex.requireModule('animation');
+// #endif
+var graceJS = require('@/GraceUI5/js/grace.js');
+export default{
+	name  : "gui-top-message",
+	props : {
+		duration  : {type:Number,  default:2000},
+		customNav : {type:Boolean, default:false }
+	},
+	data() {
+		return {
+			show    : false,
+			out     : false,
+			navHeight : 0
+		}
+	},
+	created : function (){
+		this.customNavSet();
+	},
+	methods:{
+		customNavSet : function () {
+			if(!this.customNav){
+				// #ifdef H5
+				this.navHeight = 44;
+				// #endif
+				// #ifndef H5
+				this.navHeight = 0;
+				// #endif
+			}else{
+				var system = graceJS.system();
+				this.navHeight = system.statusBarHeight;
+			}
+		},
+		open : function(){
+			this.out  = false;
+			this.show = true;
+			// #ifdef APP-NVUE
+			this.weexAnimateIn();
+			// #endif
+			setTimeout(()=>{this.close();}, this.duration);
+		},
+		close : function(){
+			this.out = true;
+			// #ifdef APP-NVUE
+			this.weexAnimateOut();
+			// #endif
+			setTimeout(()=>{this.show = false;},350);
+		},
+		// #ifdef APP-NVUE
+		weexAnimateIn : function(){
+			graceJS.getRefs('guipopupfortopmsg', this, 0, (guipopupref)=>{
+				animation.transition(guipopupref, {
+					styles: {transform:'translateY(0px)', opacity:1},
+					duration: 350, //ms
+					timingFunction: 'ease',
+					delay: 0 //ms
+				});
+			});
+		},
+		weexAnimateOut : function(){
+			graceJS.getRefs('guipopupfortopmsg', this, 0, (guipopupref)=>{
+				animation.transition(guipopupref, {
+					styles: {transform:'translateY(-200px)', opacity:0},
+					duration: 350, //ms
+					timingFunction: 'ease',
+					delay: 0 //ms
+				});
+			});
+		},
+		// #endif
+	}
+}
+</script>
+<style scoped>
+.gui-top-message{position:fixed; left:0; top:0; width:750rpx; z-index:900; transform:translateY(-200px); opacity:0.1;}
+/* #ifdef H5 */
+.gui-top-message{top:44px;}
+/* #endif */
+/* #ifndef APP-NVUE */
+@keyframes gui-top-message-in{0%{transform:translateY(-200px); opacity:0.1;} 100%{transform:translateY(0px); opacity:1;}}
+.gui-top-message-in{animation:gui-top-message-in 350ms linear forwards;}
+@keyframes gui-top-message-out{0%{transform:translateY(0px);  opacity:1;} 100%{transform:translateY(-200px); opacity:1;}}
+.gui-top-message-out{animation:gui-top-message-out 350ms linear forwards;}
+/* #endif */
+</style>

+ 61 - 0
GraceUI5/components/gui-totop.vue

@@ -0,0 +1,61 @@
+<template>
+	<view class="gui-totop gui-fade-in" 
+	hover-class="gui-tap" 
+	v-if="show" 
+	@tap="totop" 
+	:style="{
+		bottom:bottom,right:right,
+		backgroundColor:background,zIndex:zIndex,
+		borderRadius:borderRadius
+	}">
+		<text class="gui-icons gui-block-text gui-totop-text"
+		:style="{
+			color:color,
+			fontSize:fontSize
+		}">&#xe637;</text>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-totop",
+	props : {
+		top        : { type : Number, default : 0 },
+		color      : { type : String, default : "#008AFF" },
+		bottom     : { type : String, default : "80rpx" },
+		right      : { type : String, default : "30rpx" },
+		background : { type : String, default : "#FFFFFF"},
+		borderRadius  : { type : String, default : "6rpx" },
+		zIndex     : { type : Number, default : 9},
+		fontSize   : { type : String, default : "44rpx" }
+	},
+	data() {
+		return {
+			show  : false,
+			timer : null
+		}
+	},
+	watch:{
+		top : function(topVal){
+			if(this.timer != null){clearTimeout(this.timer);}
+			this.timer = setTimeout(()=>{
+				this.show = topVal > 100 ? true : false;
+			}, 80);
+		}
+	},
+	methods:{
+		totop : function(){
+			// #ifndef APP-NVUE
+			uni.pageScrollTo({
+				scrollTop:0,
+				duration:100
+			})
+			// #endif
+			this.$emit('totop');
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-totop{width:80rpx; height:80rpx; position:fixed;}
+.gui-totop-text{width:80rpx; height:80rpx; line-height:80rpx; font-size:38rpx; text-align:center;}
+</style>

+ 130 - 0
GraceUI5/components/gui-touch.vue

@@ -0,0 +1,130 @@
+<template>
+	<view 
+	@touchstart="touchstart" 
+	@touchmove="touchmove" 
+	@touchend="touchend">
+		<slot></slot>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-touch",
+	props : {
+		datas:{type:Array, default:function(){return [];}}
+	},
+	data() {
+		return {
+			toucheTimer  : 0,
+			fingerRes    : [],
+			distance     : 0,
+			taptimer     : 100
+		}
+	},
+	methods:{
+		toInt : function(arr){
+			var res = [];
+			arr.forEach((item)=>{
+				item.pageX = parseInt(item.pageX);
+				item.pageY = parseInt(item.pageY);
+				res.push(item);
+			});
+			return res;
+		},
+		touchstart : function(e){
+			this.toucheTimer  = new Date().getTime();
+			this.fingerRes    = this.toInt(e.changedTouches);
+			if(this.fingerRes.length > 2){return ;}
+			var moves = [], i = 0;
+			this.fingerRes.forEach((finger)=>{
+				var xTouch = finger.pageX;
+				var yTouch = finger.pageY;
+				moves.push([xTouch, yTouch]);
+				i++;
+			});
+			this.$emit('thStart', moves, this.datas);
+		},
+		touchmove : function(e){
+			if(this.toucheTimer < 50){return ;}
+			var timer = new Date().getTime() - this.toucheTimer;
+			if(timer < this.taptimer){return ;}
+			var touches = this.toInt(e.changedTouches);
+			if(touches.length > 2){return ;}
+			if(touches.length == 1){
+				var i = 0, moves = [];
+				touches.forEach((finger)=>{
+					var xTouch = finger.pageX - this.fingerRes[i].pageX;
+					var yTouch = finger.pageY - this.fingerRes[i].pageY;
+					moves.push([xTouch, yTouch]);
+					i++;
+				});
+				this.$emit('thMove', moves, this.datas);
+			}
+			else if(touches.length == 2){
+				if(this.distance == 0){
+					this.distance = parseInt(this.getDistance(touches[0].pageX,touches[0].pageY, touches[1].pageX, touches[1].pageY));
+				}else{
+					var distance1 = parseInt(this.getDistance(touches[0].pageX,touches[0].pageY, touches[1].pageX, touches[1].pageY));
+					var scale = distance1 / this.distance;
+					scale = Math.floor(scale * 100) / 100;
+					this.$emit('scale', scale, this.datas);
+				}
+			}
+		},
+		touchend : function (e){
+			var timer = new Date().getTime() - this.toucheTimer;
+			if(timer < this.taptimer){
+				this.$emit('tapme'); 
+				return ;
+			}
+			var touches   = this.toInt(e.changedTouches);
+			this.distance = 0;
+			if(touches.length == 1){
+				var i = 0, moves = [];
+				touches.forEach((finger)=>{
+					var xTouch = finger.pageX - this.fingerRes[i].pageX;
+					var yTouch = finger.pageY - this.fingerRes[i].pageY;
+					moves.push([xTouch, yTouch]);
+					i++;
+				});
+				moves.push(timer);
+				this.$emit('thEnd', moves, this.datas);
+				// 根据时间及距离决定滑动时间
+				if(timer < 300){
+					var mx = Math.abs(moves[0][0]);
+					var my = Math.abs(moves[0][1]);
+					if(mx > my){
+						if(mx >= 50){
+							if(moves[0][0] > 0){
+								this.$emit('swipe', 'right', this.datas);
+							}else{
+								this.$emit('swipe', 'left', this.datas);
+							}
+						}
+					}else{
+						if(my >= 50){
+							if(moves[0][1] > 0){
+								this.$emit('swipe', 'down', this.datas);
+							}else{
+								this.$emit('swipe', 'up', this.datas);
+							}
+						}
+					}
+				}
+			}
+		},
+		getDistance : function (lat1,  lng1,  lat2,  lng2){
+			var radLat1  = lat1*Math.PI / 180.0;
+			var radLat2  = lat2*Math.PI / 180.0;
+			var a        = radLat1 - radLat2;
+			var b        = lng1*Math.PI / 180.0 - lng2*Math.PI / 180.0;
+			var s        = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) + Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
+			s            = s * 6378.137;
+			return Math.round(s * 10000) / 10000;
+		},
+		tapme : function(){
+			this.isTap = true;
+		}
+	}
+}
+</script>
+<style scoped></style>

+ 138 - 0
GraceUI5/components/gui-tree.vue

@@ -0,0 +1,138 @@
+<template>
+	<view>
+		<view 
+		v-for="(tree,index) in treesIN" 
+		:key="index">
+			<view class="gui-flex gui-rows gui-nowrap gui-align-items-center gui-tree" 
+			:data-havsons="tree.children" 
+			:data-treeindexs="indexesIn" 
+			:data-index="index" 
+			:data-id="tree.id" 
+			:data-treelevel="level" 
+			:data-cancheck="(allCanCheck || !tree.children)" 
+			:style="{paddingLeft:(indent*level)+'rpx'}" 
+			@tap.stop="taped">
+				<view class="gui-tree-icons" 
+				:data-id="tree.id" 
+				@tap.stop="fold" 
+				v-if="type == 'text' && isIcon">
+					<text class="gui-tree-icons-text gui-icons" 
+					v-if="tree.children">&#xe62d;</text>
+				</view>
+				<view class="gui-tree-icons" 
+				v-if="type == 'radio' && (allCanCheck || !tree.children)">
+					<text class="gui-tree-icons-text gui-icons gui-tree-current gui-fade-in" 
+					v-if="tree.id == checkedId">&#xe7f8;</text>
+					<text class="gui-tree-icons-text gui-icons" 
+					v-else>&#xe762;</text>
+				</view>
+				<view class="gui-tree-icons" 
+				v-if="type == 'checkbox' && (allCanCheck || !tree.children)">
+					<text class="gui-tree-icons-text gui-icons gui-tree-current gui-fade-in" 
+					v-if="isChecked(tree.id)">&#xe685;</text>
+					<text class="gui-tree-icons-text gui-icons icon-checkbox" v-else>&#xe762;</text>
+				</view>
+				<text class="gui-block-text gui-tree-title gui-flex1">{{tree.label}}</text>
+			</view>
+			<view>
+				<gui-tree v-if="arrayIndexOf(notids, tree.id) == -1" 
+				:trees="tree.children" :indent="indent" 
+				:level="level+1" :allCanCheck="allCanCheck" 
+				:isIcon="isIcon" :checkedId="checkedId" 
+				:checkedIds="checkedIds" 
+				:type="type" :indexes="[indexesIn,index]" @taped="tapbase"></gui-tree>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-tree",
+	props : {
+		trees        : {type:Array,   default:function () {return [];}},
+		indent       : {type:Number,  default:28},
+		level        : {type:Number,  default:0},
+		type         : {type:String,  default:'text'},
+		isIcon       : {type:Boolean, default:true},
+		indexes      : {type:Array,   default:function () {return ['',0];}},
+		checkedId    : {type:[String, Number], default:'-1'},
+		checkedIds   : {type:Array,   default:function () {return [];}},
+		allCanCheck  : {type:Boolean, default:true},
+		isFold       : {type:Boolean, default:true}
+	},
+	data() {
+		return {
+			treesIN   : [],
+			indexesIn : [],
+			notids    : []
+		}
+	},
+	created:function(){
+		this.treesIN = this.trees;
+		if(this.indexes[0] != ''){
+			var indexes  =  this.indexes[0].split(',');
+		}else{
+			var indexes = [];
+		}
+		indexes.push(this.indexes[1]);
+		this.indexesIn = indexes.join(',');
+	},
+	watch:{
+		type : function(){
+			this.notids = [];
+		}
+	},
+	methods:{
+		fold  : function (e) {
+			var id            = e.currentTarget.dataset.id;
+			if(this.isFold){
+				var res = this.arrayIndexOf(this.notids, id);
+				if(res == -1){
+					this.notids.push(id);
+				}else{
+					this.notids.splice(res,1);
+				}
+			}
+			e.stopPropagation();
+			return ;
+		},
+		taped : function(e){
+			var treeindexs    = e.currentTarget.dataset.treeindexs;
+			treeindexs        = treeindexs.split(',');
+			var index         = e.currentTarget.dataset.index;
+			treeindexs.push(index);
+			treeindexs.shift();
+			if(this.type == 'text'){
+				this.tapbase(treeindexs);
+			}else{
+				var cancheck = e.currentTarget.dataset.cancheck;
+				if(cancheck){this.tapbase(treeindexs);}
+			}
+			e.stopPropagation();
+		},
+		tapbase : function(e){
+			this.$emit('taped', e);
+		},
+		setTrees : function (trees) {
+			this.treesIN = trees;
+		},
+		isChecked : function(checkedId){
+			for(let i = 0; i < this.checkedIds.length; i++){
+				if(this.checkedIds[i] == checkedId){
+					return true;
+				}
+			}
+			return false;
+		},
+		arrayIndexOf : function(arr, needFind){
+			var index = -1;
+			for(let i = 0; i < arr.length; i++){
+				if(arr[i] == needFind){index = i; return i;}
+			}
+			return index;
+		}
+	}
+}
+</script>
+<style scoped>
+</style>

+ 173 - 0
GraceUI5/components/gui-turntable.vue

@@ -0,0 +1,173 @@
+<template>
+	<view class="gui-turntable" 
+	:animation="animationData">
+		<view class="gui-turntable-item" 
+		v-for="(item, index) in rewardNames" :key="index" 
+		:style="{
+			transform  : 'rotate('+(inital - averageRotate * index)+'deg)'
+		}">
+			<view class="gui-turntable-inner" 
+			:style="{
+			transform: 'translateX(-300rpx) rotate(' + averageRotate + 'deg)',
+			background : rewardBGColors[index]
+			}">
+				<text class="gui-turntable-text" 
+				:style="{
+					transform : 'translateY(120rpx) translateX('+textTrX+') rotate('+textRotate+')',
+					fontSize  : fontSize,
+					color     : rewardColors[index]
+				}">{{item}}</text>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+export default{
+	name  : "gui-turntable",
+	props : {
+		// 奖品名称
+		rewardNames : {
+			type : Array,
+			default : function(){
+				return ["", "", "", "", "", ""];
+			}
+		},
+		// 奖品展示区背景颜色
+		rewardBGColors : {
+			type : Array,
+			default : function(){
+				return [];
+			}
+		},
+		// 奖品展示区文本颜色
+		rewardColors : {
+			type : Array,
+			default : function(){
+				return [];
+			}
+		},
+		// 文本尺寸
+		fontSize : {
+			type : String,
+			default : '32rpx'
+		}
+	},
+	data() {
+		return {
+			// 是否正在抽奖
+			luckyStatus     : 0,
+			// 动画对象
+			animationData   : {},
+			tpmimgtimmer    : null,
+			// 中奖奖品 index
+			RewardIndex     : -1,
+			// 角度
+			averageRotate   : 0,
+			inital          : 0,
+			textTrX         : '43rpx',
+			textRotate      : '-30deg'
+		}
+	},
+	created:function(){
+		this.init();
+	},
+	watch:{
+		rewardNames:function(){
+			this.init();
+		}
+	},
+	methods:{
+		init : function(){
+			var length          = this.rewardNames.length;
+			this.averageRotate  = 360 / length ;
+			this.inital         = (360 / length / 2) * -1;
+			switch(length){
+				case 4:
+					this.textTrX    = '0rpx';
+					this.textRotate = '-45deg';
+					break
+				case 6:
+					this.textTrX    = '43rpx';
+					this.textRotate = '55deg';
+					break
+				case 8:
+					this.textTrX    = '72rpx';
+					this.textRotate = '65deg';
+					break
+			}
+			var animation      = null;
+			animation = uni.createAnimation({
+				duration: 0,
+				timingFunction: 'ease',
+			});
+			animation.rotateZ(this.averageRotate/2).step();
+			this.animationData = animation.export();
+		},
+		goto : function(index){
+			if(this.luckyStatus != 0){
+				return ;
+			}
+			this.RewardIndex = index;
+			this.animationData = {};
+			this.luckyStatus   = 1;
+			
+			// 轮盘归零
+			var animation      = null;
+			animation = uni.createAnimation({
+				duration: 0,
+				timingFunction: 'ease',
+			});
+			animation.rotateZ(this.averageRotate/2).step();
+			this.animationData = animation.export();
+			
+			// 计算奖品角度
+			var rewardRadiu     = (360 / this.rewardNames.length);
+			var rewardRadiuNeed = 360*6 + rewardRadiu * this.RewardIndex;
+			setTimeout(()=>{
+				animation = uni.createAnimation({
+					duration:5000,
+					timingFunction: 'ease',
+				});
+				animation.rotateZ(rewardRadiuNeed).step();
+				this.animationData = animation.export();
+			},100);
+			setTimeout(()=>{
+				// 停止动画
+				this.luckyStatus = 0;
+				this.$emit('end', index);
+				this.RewardIndex = -1;
+			},5000);
+		}
+	}
+}
+</script>
+<style scoped>
+.gui-turntable{
+  position : relative;
+  transform-origin: center;
+  width    : 600rpx;
+  height   : 600rpx;
+}
+.gui-turntable-item {
+  position : absolute;
+  left     : 50%;
+  width    : 300rpx;
+  height   : 600rpx;
+  border-radius:0px 300rpx 300rpx 0;
+  overflow : hidden;
+  transform-origin : left center;
+}
+
+.gui-turntable-inner {
+  text-align: center;
+  width  : 300rpx;
+  height : 600rpx;
+  transform-origin : right center;
+  border-radius : 300rpx 0 0 300rpx;
+}
+
+.gui-turntable-text {
+  display: block;
+  transform-origin:center;
+}
+</style>

+ 248 - 0
GraceUI5/components/gui-upload-images.vue

@@ -0,0 +1,248 @@
+<template>
+	<view class="gui-flex gui-rows gui-wrap">
+		<view class="gui-add-list-items" :style="{borderRadius:borderRadius}" 
+		v-for="(item, index) in imgLists" :key="index">
+			<image :src="item.url" :data-imgurl="item.url" 
+			:style="{borderRadius:borderRadius}" 
+			@tap="showImgs" class="gui-add-list-img" :mode="imgMode"></image>
+			<!-- 进度条 -->
+			<view class="gui-upload-progress">
+			   <progress :percent="item.progress" 
+			   :stroke-width="progressSize" :activeColor="progressColor" 
+			   :backgroundColor="progressBGColor" />
+			</view>
+			<!-- 上传失败 -->
+			<view class="gui-add-list-reup gui-flex gui-columns gui-justify-content-center gui-align-items-center" 
+			@tap.stop="retry" :data-index="index" v-if="item.error">
+				<text class="gui-add-list-reup-icon gui-icons icon-retry gui-color-white">&#xe635;</text>
+				<text class="gui-add-list-reup-text gui-color-white">失败重试</text>
+			</view>
+			<!-- 删除 -->
+			<text class="gui-add-list-remove gui-icons" 
+			:style="{color:removeBtnColor}" @tap="removeImg" 
+			:id="'gui-items-img-'+index">&#xe632;</text>
+		</view>
+		<view class="gui-add-list-items gui-flex gui-columns gui-justify-content-center gui-align-items-center" 
+		@tap="addImg" v-if="imgLists.length < maxFileNumber" 
+		:style="{borderRadius:borderRadius}">
+			<text class="gui-add-list-btn-icon gui-block-text gui-color-gray">+</text>
+			<text class="gui-add-list-btn-text gui-block-text gui-color-gray">{{btnName}}</text>
+		</view>
+	</view>
+</template>
+<script>
+var graceJS = require('@/GraceUI5/js/grace.js');
+export default {
+	name  : "gui-upload-images",
+	props : {
+		maxFileNumber   : { type : Number, default : 9 },
+		btnName         : { type : String, default : "添加照片" },
+		items           : { type : Array,  default : function () {return []; }},
+		removeBtnColor  : { type : String, default : "rgba(0, 0, 0, 0.8)" },
+		sourceType      : { type : Array,  default : function () {return ['album', 'camera'];}},
+		borderRadius    : { type : String, default : "10rpx" },
+		uploadServerUrl : { type : String, default : '' },
+		progressSize    : { type : Number, default : 2},
+		progressColor   : { type : String, default : '#27BD81' },
+		progressBGColor : { type : String, default : '#F8F8F8' },
+		fileName        : { type : String, default : 'img'},
+		formData        : { type : Object, default : function(){return {};}},
+		imgMode         : { type : String, default : 'aspectFill'},
+		header          : { type : Object, default : function(){return {};}},
+		save2uniCloud   : { type : Boolean, default :false }
+	},
+	data() {
+		return {
+			imgLists  : [],
+			updatting : false
+		}
+	},
+	created : function () {
+		this.initImgs();
+	},
+	watch   : {
+		items : function(){ this.initImgs(); }
+	},
+    methods : {
+		initImgs     : function(){
+			var imgs = [];
+			for(let i = 0; i < this.items.length; i++){
+				imgs.push({url:this.items[i],  progress : 100});
+			}
+			this.imgLists = imgs;
+		},
+        addImg       : function(){
+            var num = this.maxFileNumber - this.imgLists.length;
+            if(num < 1){return false;}
+            uni.chooseImage({
+                count      : num,
+                sizeType   : ['compressed'],
+				sourceType : this.sourceType,
+                success    : (res) => {
+					if(this.imgLists.length >= this.maxFileNumber){return ;}
+					for(let i = 0; i < res.tempFilePaths.length; i++){
+						if(this.imgLists.length >= this.maxFileNumber){break;}
+						this.imgLists.push({url:res.tempFilePaths[i], progress:0});
+					}
+                    this.$emit('change', this.imgLists);
+                },
+				complete   : function(){}
+            });
+        },
+        removeImg    : function(e){
+            var index = e.currentTarget.id.replace('gui-items-img-', '');
+			var removeImg =  this.imgLists.splice(index, 1);
+			this.$emit('removeImg', removeImg[0]);
+			this.$emit('change'   , this.imgLists);
+        },
+        showImgs     : function(e){
+            var currentImg = e.currentTarget.dataset.imgurl;
+			var imgs       = [];
+			for(let i = 0; i < this.imgLists.length; i++){
+				imgs.push(this.imgLists[i].url);
+			}
+            uni.previewImage({urls:imgs, current:currentImg});
+        },
+		setItems     : function(items){
+			this.imgLists = [];
+			for(let i = 0; i < items.length; i++){
+				this.imgLists.push({url:items[i], progress:100});
+			}
+			this.$emit('change'   , this.imgLists);
+		},
+		clearAllImgs : function () { this.imgLists = [];},
+		retry        : function (e) {
+			var index = e.currentTarget.dataset.index;
+			this.upload(index);
+		},
+		upload       : function(index){
+			if(this.updatting){return ;}
+			this.updatting   = true;
+			if(!index){index = 0;}
+			uni.showLoading({title:"图片上传中", mask:true});
+			if(this.save2uniCloud){
+				this.upload2cloud(index);
+			}else{
+				this.uploadBase(index);
+			}
+		},
+		uploadBase : function (index) {
+			// 全部上传完成
+			if(index > (this.imgLists.length - 1)){
+				uni.hideLoading();
+				this.updatting = false;
+				this.$emit('uploaded', this.imgLists);
+				return ;
+			}
+			// 验证后端
+			if(this.uploadServerUrl == ''){
+				uni.showToast({title:"请设置上传服务器地址", icon:"none"});
+				return ;
+			}
+			// 检查是否是默认值
+			if(this.imgLists[index].progress >= 1){
+				this.uploadBase(index+1);
+				return ;
+			}
+			this.imgLists[index].error = false;
+			// 创建上传对象
+			const task = uni.uploadFile({
+				url      : this.uploadServerUrl,
+				filePath : this.imgLists[index].url,
+				name     : this.fileName,
+				formData : this.formData,
+				header   : this.header,
+				success  : (uploadRes) => {
+					try{
+						uploadRes = JSON.parse(uploadRes.data);
+						if(uploadRes.status != 'ok'){
+							uni.showToast({title:"上传失败 : "+uploadRes.data, icon:"none"});
+							this.error(index);
+						}else{
+							//上传图片成功
+							this.imgLists[index].progress = 100;
+							this.imgLists[index].url      = uploadRes.data;
+							this.imgLists[index].result   = uploadRes;
+							this.uploadBase(index+1);
+						}
+					}catch(e){
+						this.error(index);
+					}
+				},
+				fail    : (e) => {
+					uni.showToast({title:"上传失败,请点击图片重试", icon:"none"});
+					this.error(index);
+					
+				}
+			});
+			task.onProgressUpdate((res) => {
+				if(res.progress > 0){
+					this.imgLists[index].progress = res.progress;
+					this.imgLists.splice(index, 1, this.imgLists[index]);
+				}
+			});
+		},
+		// 上传错误
+		error : function(index){
+			uni.hideLoading();
+			this.updatting = false;
+			setTimeout(()=>{
+				this.imgLists[index].progress = 0;
+				this.imgLists[index].error    = true;
+				this.imgLists.splice(index, 1, this.imgLists[index]);
+				this.$emit('uploaderror');
+			}, 500);
+		},
+		upload2cloud:function (index) {
+			// 全部上传完成
+			if(index > (this.imgLists.length - 1)){
+				uni.hideLoading();
+				this.updatting = false;
+				this.$emit('uploaded', this.imgLists);
+				return ;
+			}
+			// 检查是否是默认值
+			if(this.imgLists[index].progress >= 1){
+				this.upload2cloud(index+1);
+				return ;
+			}
+			this.imgLists[index].error = false;
+			// 创建上传对象
+			uniCloud.uploadFile({
+				filePath  : this.imgLists[index].url,
+				cloudPath : graceJS.uuid() + '.png',
+				onUploadProgress:(progressEvent) => {
+					var percentCompleted = Math.round(
+						(progressEvent.loaded * 100) / progressEvent.total
+					);
+					this.imgLists[index].progress = percentCompleted;
+					this.imgLists.splice(index, 1, this.imgLists[index]);
+				},
+				success : (uploadRes)=>{
+					//上传图片成功
+					this.imgLists[index].progress = 100;
+					this.imgLists[index].url      = uploadRes.fileID;
+					this.imgLists[index].result   = uploadRes;
+					this.imgLists.splice(index, 1, this.imgLists[index]);
+					this.upload2cloud(index+1);
+				},
+				fail : ()=>{
+					uni.showToast({title:"上传失败", icon:"none"});
+					this.error(index);
+				}
+			});
+		}
+    }
+}
+</script>
+<style scoped>
+.gui-add-list-btn-text{font-size:26rpx; line-height:36rpx; text-align:center;}
+.gui-add-list-btn-icon{font-size:80rpx; height:80rpx; line-height:80rpx; margin-bottom:20rpx;}
+.gui-add-list-items{width:212rpx; height:212rpx; overflow:hidden; margin:9rpx; background-color:#F8F8F8; font-size:0; position:relative;}
+.gui-add-list-remove{width:60rpx; height:60rpx; line-height:60rpx; text-align:center; font-size:44rpx; position:absolute; z-index:5; right:0; top:0;}
+.gui-add-list-img{width:212rpx;}
+.gui-upload-progress{position:absolute; z-index:2; left:0; bottom:10rpx; width:180rpx; padding:0 16rpx;}
+.gui-add-list-reup{position:absolute; z-index:3; left:0; top:0rpx; width:212rpx; height:212rpx; background-color:rgba(0,0,0,0.3);}
+.gui-add-list-reup-icon{text-align:center; font-size:68rpx; line-height:100rpx;}
+.gui-add-list-reup-text{text-align:center; font-size:20rpx; line-height:30rpx;}
+</style>

File diff suppressed because it is too large
+ 1 - 0
GraceUI5/css/animate.css


+ 168 - 0
GraceUI5/css/colors.css

@@ -0,0 +1,168 @@
+/* 黑色 */
+.gui-color-black{color:#000000;}
+.gui-bg-black{background-color:#000000;}
+.gui-color-black1{color:#212121;}
+.gui-bg-black1{background-color:#212121;}
+.gui-color-black2{color:#303030;}
+.gui-bg-black2{background-color:#303030;}
+.gui-color-black3{color:#424242;}
+.gui-bg-black3{background-color:#424242;}
+
+
+/* 灰色 */
+.gui-color-grey{color:#616161;}
+.gui-bg-grey{background-color:#616161;}
+.gui-color-grey1{color:#757575;}
+.gui-bg-grey1{background-color:#757575;}
+.gui-color-grey2{color:#9E9E9E;}
+.gui-bg-grey2{background-color:#9E9E9E;}
+.gui-color-grey3{color:#BDBDBD;}
+.gui-bg-grey3{background-color:#BDBDBD;}
+.gui-color-grey4{color:#D6D6D6;}
+.gui-bg-grey4{background-color:#D6D6D6;}
+.gui-color-grey5{color:#E0E0E0;}
+.gui-bg-grey5{background-color:#E0E0E0;}
+.gui-color-grey5{color:#EEEEEE;}
+.gui-bg-grey5{background-color:#EEEEEE;}
+.gui-color-grey6{color:#F5F5F5;}
+.gui-bg-grey6{background-color:#F5F5F5;}
+.gui-color-grey7{color:#FAFAFA;}
+.gui-bg-grey7{background-color:#FAFAFA;}
+
+/* 红色 */
+.gui-color-red{color:#FF0000;}
+.gui-bg-red{background-color:#FF0000;}
+.gui-color-red1{color:#FF0036;}
+.gui-bg-red1{background-color:#FF0036;}
+.gui-color-red2{color:#F44336;}
+.gui-bg-red2{background-color:#F44336;}
+.gui-color-red3{color:#EF5350;}
+.gui-bg-red3{background-color:#EF5350;}
+
+/* 粉色 */
+.gui-color-pink{color:#E91E63;}
+.gui-bg-pink{background-color:#E91E63;}
+.gui-color-pink1{color:#FF5081;}
+.gui-bg-pink1{background-color:#FF5081;}
+.gui-color-pink2{color:#F58FB1;}
+.gui-bg-pink2{background-color:#F58FB1;}
+.gui-color-pink3{color:#FCE4EC;}
+.gui-bg-pink3{background-color:#FCE4EC;}
+
+/* 橙色 */
+.gui-color-orange{color:#FF3D00;}
+.gui-bg-orange{background-color:#FF3D00;}
+.gui-color-orange1{color:#FF6E40;}
+.gui-bg-orange1{background-color:#FF6E40;}
+.gui-color-orange2{color:#FFAB91;}
+.gui-bg-orange2{background-color:#FFAB91;}
+.gui-color-orange3{color:#FFCCBC;}
+.gui-bg-orange3{background-color:#FFCCBC;}
+
+/* 黄色 */
+.gui-color-yellow{color:#FFFF00;}
+.gui-bg-yellow{background-color:#FFFF00;}
+.gui-color-yellow1{color:#FFEB3B;}
+.gui-bg-yellow1{background-color:#FFEB3B;}
+.gui-color-yellow2{color:#FFEE58;}
+.gui-bg-yellow2{background-color:#FFEE58;}
+.gui-color-yellow3{color:#FFF9C5;}
+.gui-bg-yellow3{background-color:#FFF9C5;}
+
+/* 绿色 */
+.gui-color-green{color:#00FF00;}
+.gui-bg-green{background-color:#00FF00;}
+.gui-color-green1{color:#4CAF50;}
+.gui-bg-green1{background-color:#4CAF50;}
+.gui-color-green2{color:#66BB6A;}
+.gui-bg-green2{background-color:#66BB6A;}
+.gui-color-green3{color:#81C785;}
+.gui-bg-green3{background-color:#81C785;}
+.gui-color-green4{color:#A5D6A7;}
+.gui-bg-green4{background-color:#A5D6A7;}
+.gui-color-green4{color:#C8E6C9;}
+.gui-bg-green4{background-color:#C8E6C9;}
+.gui-color-green5{color:#E8F5E9;}
+.gui-bg-green5{background-color:#E8F5E9;}
+.gui-color-green5{color:#76FF03;}
+.gui-bg-green5{background-color:#76FF03;}
+.gui-color-green6{color:#9CCC65;}
+.gui-bg-green6{background-color:#9CCC65;}
+.gui-color-green7{color:#F1F8E9;}
+.gui-bg-green7{background-color:#F1F8E9;}
+
+/* 水鸭色 */
+.gui-color-teal{color:#00796B;}
+.gui-bg-teal{background-color:#00796B;}
+.gui-color-teal1{color:#00897B;}
+.gui-bg-teal1{background-color:#00897B;}
+.gui-color-teal2{color:#26A69A;}
+.gui-bg-teal2{background-color:#26A69A;}
+.gui-color-teal3{color:#B2DFDB;}
+.gui-bg-teal3{background-color:#B2DFDB;}
+
+/* 蓝色 */
+.gui-color-blue{color:#1565C0;}
+.gui-bg-blue{background-color:#1565C0;}
+.gui-color-blue1{color:#3688FF;}
+.gui-bg-blue1{background-color:#3688FF;}
+.gui-color-blue2{color:#1E88E5;}
+.gui-bg-blue2{background-color:#1E88E5;}
+.gui-color-blue3{color:#2196F3;}
+.gui-bg-blue3{background-color:#2196F3;}
+.gui-color-blue4{color:#42A5F5;}
+.gui-bg-blue4{background-color:#42A5F5;}
+.gui-color-blue5{color:#90CAF9;}
+.gui-bg-blue5{background-color:#90CAF9;}
+.gui-color-blue6{color:#BBDEFB;}
+.gui-bg-blue6{background-color:#BBDEFB;}
+.gui-color-blue7{color:#E3F2FD;}
+.gui-bg-blue7{background-color:#E3F2FD;}
+
+/* 蓝灰 */
+.gui-color-bluegrey{color:#455A64;}
+.gui-bg-bluegrey{background-color:#455A64;}
+.gui-color-bluegrey1{color:#546E7A;}
+.gui-bg-bluegrey1{background-color:#546E7A;}
+.gui-color-bluegrey2{color:#607D8B;}
+.gui-bg-bluegrey2{background-color:#607D8B;}
+.gui-color-bluegrey3{color:#78909C;}
+.gui-bg-bluegrey3{background-color:#78909C;}
+.gui-color-bluegrey4{color:#90A4Ae;}
+.gui-bg-bluegrey4{background-color:#90A4Ae;}
+.gui-color-bluegrey5{color:#B0BEC5;}
+.gui-bg-bluegrey5{background-color:#B0BEC5;}
+.gui-color-bluegrey6{color:#CFD8DC;}
+.gui-bg-bluegrey6{background-color:#CFD8DC;}
+.gui-color-bluegrey6{color:#ECEFF1;}
+.gui-bg-bluegrey6{background-color:#ECEFF1;}
+
+/* 青色 */
+.gui-color-cyan{color:#006065;}
+.gui-bg-cyan{background-color:#006065;}
+.gui-color-cyan1{color:#0097A7;}
+.gui-bg-cyan1{background-color:#0097A7;}
+.gui-color-cyan2{color:#80DEEA;}
+.gui-bg-cyan2{background-color:#80DEEA;}
+.gui-color-cyan3{color:#E0F7FA;}
+.gui-bg-cyan3{background-color:#E0F7FA;}
+
+/* 紫色 */
+.gui-color-purple{color:#9C27B0;}
+.gui-bg-purple{background-color:#9C27B0;}
+.gui-color-purple1{color:#E040FB;}
+.gui-bg-purple1{background-color:#E040FB;}
+.gui-color-purple2{color:#E1BEE7;}
+.gui-bg-purple2{background-color:#E1BEE7;}
+.gui-color-purple3{color:#F3E5F5;}
+.gui-bg-purple3{background-color:#F3E5F5;}
+
+/* 棕色 */
+.gui-color-brown{color:#5D4037;}
+.gui-bg-brown{background-color:#5D4037;}
+.gui-color-brown1{color:#795558;}
+.gui-bg-brown1{background-color:#795558;}
+.gui-color-brown2{color:#8D6E63;}
+.gui-bg-brown2{background-color:#8D6E63;}
+.gui-color-brown3{color:#D7CCC8;}
+.gui-bg-brown3{background-color:#D7CCC8;}

File diff suppressed because it is too large
+ 1145 - 0
GraceUI5/css/graceUI.css


File diff suppressed because it is too large
+ 6 - 0
GraceUI5/css/iconsforbaidu.css


File diff suppressed because it is too large
+ 12553 - 0
GraceUI5/data/city-data/area.js


+ 434 - 0
GraceUI5/data/city-data/city.js

@@ -0,0 +1,434 @@
+/* 本数据由 GraceUI 团队精心整理,版权归 GraceUI 所有,未经允许不得发布到公开渠道及抄袭! */
+var cityData = [
+	[{"label": "北京市","value": "110000"}],
+	[{"label": "天津市","value": "120000"}],
+	[
+		{"label": "石家庄市","value": "130100"},
+		{"label": "唐山市","value": "130200"},
+		{"label": "秦皇岛市","value": "130300"},
+		{"label": "邯郸市","value": "130400"},
+		{"label": "邢台市","value": "130500"},
+		{"label": "保定市","value": "130600"},
+		{"label": "张家口市","value": "130700"},
+		{"label": "承德市","value": "130800"},
+		{"label": "沧州市","value": "130900"},
+		{"label": "廊坊市","value": "131000"},
+		{"label": "衡水市","value": "131100"
+		}
+	],
+	[
+		{"label": "太原市","value": "140100"},
+		{"label": "大同市","value": "140200"},
+		{"label": "阳泉市","value": "140300"},
+		{"label": "长治市","value": "140400"},
+		{"label": "晋城市","value": "140500"},
+		{"label": "朔州市","value": "140600"},
+		{"label": "晋中市","value": "140700"},
+		{"label": "运城市","value": "140800"},
+		{"label": "忻州市","value": "140900"},
+		{"label": "临汾市","value": "141000"},
+		{"label": "吕梁市","value": "141100"}
+	],
+	[
+		{"label": "呼和浩特市","value": "150100"},
+		{"label": "包头市","value": "150200"},
+		{"label": "乌海市","value": "150300"},
+		{"label": "赤峰市","value": "150400"},
+		{"label": "通辽市","value": "150500"},
+		{"label": "鄂尔多斯市","value": "150600"},
+		{"label": "呼伦贝尔市","value": "150700"},
+		{"label": "巴彦淖尔市","value": "150800"},
+		{"label": "乌兰察布市","value": "150900"},
+		{"label": "兴安盟","value": "152200"},
+		{"label": "锡林郭勒盟","value": "152500"},
+		{"label": "阿拉善盟","value": "152900"}
+	],
+	[
+		{"label": "沈阳市","value": "210100"},
+		{"label": "大连市","value": "210200"},
+		{"label": "鞍山市","value": "210300"},
+		{"label": "抚顺市","value": "210400"},
+		{"label": "本溪市","value": "210500"},
+		{"label": "丹东市","value": "210600"},
+		{"label": "锦州市","value": "210700"},
+		{"label": "营口市","value": "210800"},
+		{"label": "阜新市","value": "210900"},
+		{"label": "辽阳市","value": "211000"},
+		{"label": "盘锦市","value": "211100"},
+		{"label": "铁岭市","value": "211200"},
+		{"label": "朝阳市","value": "211300"},
+		{"label": "葫芦岛市","value": "211400"}
+	],
+	[
+		{"label": "长春市","value": "220100"},
+		{"label": "吉林市","value": "220200"},
+		{"label": "四平市","value": "220300"},
+		{"label": "辽源市","value": "220400"},
+		{"label": "通化市","value": "220500"},
+		{"label": "白山市","value": "220600"},
+		{"label": "松原市","value": "220700"},
+		{"label": "白城市","value": "220800"},
+		{"label": "延边朝鲜族自治州","value": "222400"}
+	],
+	[
+		{"label": "哈尔滨市","value": "230100"},
+		{"label": "齐齐哈尔市","value": "230200"},
+		{"label": "鸡西市","value": "230300"},
+		{"label": "鹤岗市","value": "230400"},
+		{"label": "双鸭山市","value": "230500"},
+		{"label": "大庆市","value": "230600"},
+		{"label": "伊春市","value": "230700"},
+		{"label": "佳木斯市","value": "230800"},
+		{"label": "七台河市","value": "230900"},
+		{"label": "牡丹江市","value": "231000"},
+		{"label": "黑河市","value": "231100"},
+		{"label": "绥化市","value": "231200"},
+		{"label": "大兴安岭地区","value": "232700"}
+	],
+	[{"label": "上海市","value": "310100"}],
+	[
+		{"label": "南京市","value": "320100"},
+		{"label": "无锡市","value": "320200"},
+		{"label": "徐州市","value": "320300"},
+		{"label": "常州市","value": "320400"},
+		{"label": "苏州市","value": "320500"},
+		{"label": "南通市","value": "320600"},
+		{"label": "连云港市","value": "320700"},
+		{"label": "淮安市","value": "320800"},
+		{"label": "盐城市","value": "320900"},
+		{"label": "扬州市","value": "321000"},
+		{"label": "镇江市","value": "321100"},
+		{"label": "泰州市","value": "321200"},
+		{"label": "宿迁市","value": "321300"}
+	],
+	[
+		{"label": "杭州市","value": "330100"},
+		{"label": "宁波市","value": "330200"},
+		{"label": "温州市","value": "330300"},
+		{"label": "嘉兴市","value": "330400"},
+		{"label": "湖州市","value": "330500"},
+		{"label": "绍兴市","value": "330600"},
+		{"label": "金华市","value": "330700"},
+		{"label": "衢州市","value": "330800"},
+		{"label": "舟山市","value": "330900"},
+		{"label": "台州市","value": "331000"},
+		{"label": "丽水市","value": "331100"}
+	],
+	[
+		{"label": "合肥市","value": "340100"},
+		{"label": "芜湖市","value": "340200"},
+		{"label": "蚌埠市","value": "340300"},
+		{"label": "淮南市","value": "340400"},
+		{"label": "马鞍山市","value": "340500"},
+		{"label": "淮北市","value": "340600"},
+		{"label": "铜陵市","value": "340700"},
+		{"label": "安庆市","value": "340800"},
+		{"label": "黄山市","value": "341000"},
+		{"label": "滁州市","value": "341100"},
+		{"label": "阜阳市","value": "341200"},
+		{"label": "宿州市","value": "341300"},
+		{"label": "六安市","value": "341500"},
+		{"label": "亳州市","value": "341600"},
+		{"label": "池州市","value": "341700"},
+		{"label": "宣城市","value": "341800"}
+	],
+	[
+		{"label": "福州市","value": "350100"},
+		{"label": "厦门市","value": "350200"},
+		{"label": "莆田市","value": "350300"},
+		{"label": "三明市","value": "350400"},
+		{"label": "泉州市","value": "350500"},
+		{"label": "漳州市","value": "350600"},
+		{"label": "南平市","value": "350700"},
+		{"label": "龙岩市","value": "350800"},
+		{"label": "宁德市","value": "350900"}
+	],
+	[
+		{"label": "南昌市","value": "360100"},
+		{"label": "景德镇市","value": "360200"},
+		{"label": "萍乡市","value": "360300"},
+		{"label": "九江市","value": "360400"},
+		{"label": "新余市","value": "360500"},
+		{"label": "鹰潭市","value": "360600"},
+		{"label": "赣州市","value": "360700"},
+		{"label": "吉安市","value": "360800"},
+		{"label": "宜春市","value": "360900"},
+		{"label": "抚州市","value": "361000"},
+		{"label": "上饶市","value": "361100"}
+	],
+	[
+		{"label": "济南市","value": "370100"},
+		{"label": "青岛市","value": "370200"},
+		{"label": "淄博市","value": "370300"},
+		{"label": "枣庄市","value": "370400"},
+		{"label": "东营市","value": "370500"},
+		{"label": "烟台市","value": "370600"},
+		{"label": "潍坊市","value": "370700"},
+		{"label": "济宁市","value": "370800"},
+		{"label": "泰安市","value": "370900"},
+		{"label": "威海市","value": "371000"},
+		{"label": "日照市","value": "371100"},
+		//{"label": "莱芜市","value": "371200"},
+		{"label": "临沂市","value": "371300"},
+		{"label": "德州市","value": "371400"},
+		{"label": "聊城市","value": "371500"},
+		{"label": "滨州市","value": "371600"},
+		{"label": "菏泽市","value": "371700"}
+	],
+	[
+		{"label": "郑州市","value": "410100"},
+		{"label": "开封市","value": "410200"},
+		{"label": "洛阳市","value": "410300"},
+		{"label": "平顶山市","value": "410400"},
+		{"label": "安阳市","value": "410500"},
+		{"label": "鹤壁市","value": "410600"},
+		{"label": "新乡市","value": "410700"},
+		{"label": "焦作市","value": "410800"},
+		{"label": "濮阳市","value": "410900"},
+		{"label": "许昌市","value": "411000"},
+		{"label": "漯河市","value": "411100"},
+		{"label": "三门峡市","value": "411200"},
+		{"label": "南阳市","value": "411300"},
+		{"label": "商丘市","value": "411400"},
+		{"label": "信阳市","value": "411500"},
+		{"label": "周口市","value": "411600"},
+		{"label": "驻马店市","value": "411700"},
+		{"label": "省直辖县级行政区划","value": "419000"}
+	],
+	[
+		{"label": "武汉市","value": "420100"},
+		{"label": "黄石市","value": "420200"},
+		{"label": "十堰市","value": "420300"},
+		{"label": "宜昌市","value": "420500"},
+		{"label": "襄阳市","value": "420600"},
+		{"label": "鄂州市","value": "420700"},
+		{"label": "荆门市","value": "420800"},
+		{"label": "孝感市","value": "420900"},
+		{"label": "荆州市","value": "421000"},
+		{"label": "黄冈市","value": "421100"},
+		{"label": "咸宁市","value": "421200"},
+		{"label": "随州市","value": "421300"},
+		{"label": "恩施土家族苗族自治州","value": "422800"},
+		{"label": "省直辖县级行政区划","value": "429000"}
+	],
+	[
+		{"label": "长沙市","value": "430100"},
+		{"label": "株洲市","value": "430200"},
+		{"label": "湘潭市","value": "430300"},
+		{"label": "衡阳市","value": "430400"},
+		{"label": "邵阳市","value": "430500"},
+		{"label": "岳阳市","value": "430600"},
+		{"label": "常德市","value": "430700"},
+		{"label": "张家界市","value": "430800"},
+		{"label": "益阳市","value": "430900"},
+		{"label": "郴州市","value": "431000"},
+		{"label": "永州市","value": "431100"},
+		{"label": "怀化市","value": "431200"},
+		{"label": "娄底市","value": "431300"},
+		{"label": "湘西土家族苗族自治州","value": "433100"}
+	],
+	[
+		{"label": "广州市","value": "440100"},
+		{"label": "韶关市","value": "440200"},
+		{"label": "深圳市","value": "440300"},
+		{"label": "珠海市","value": "440400"},
+		{"label": "汕头市","value": "440500"},
+		{"label": "佛山市","value": "440600"},
+		{"label": "江门市","value": "440700"},
+		{"label": "湛江市","value": "440800"},
+		{"label": "茂名市","value": "440900"},
+		{"label": "肇庆市","value": "441200"},
+		{"label": "惠州市","value": "441300"},
+		{"label": "梅州市","value": "441400"},
+		{"label": "汕尾市","value": "441500"},
+		{"label": "河源市","value": "441600"},
+		{"label": "阳江市","value": "441700"},
+		{"label": "清远市","value": "441800"},
+		{"label": "东莞市","value": "441900"},
+		{"label": "中山市","value": "442000"},
+		{"label": "潮州市","value": "445100"},
+		{"label": "揭阳市","value": "445200"},
+		{"label": "云浮市","value": "445300"}
+	],
+	[
+		{"label": "南宁市","value": "450100"},
+		{"label": "柳州市","value": "450200"},
+		{"label": "桂林市","value": "450300"},
+		{"label": "梧州市","value": "450400"},
+		{"label": "北海市","value": "450500"},
+		{"label": "防城港市","value": "450600"},
+		{"label": "钦州市","value": "450700"},
+		{"label": "贵港市","value": "450800"},
+		{"label": "玉林市","value": "450900"},
+		{"label": "百色市","value": "451000"},
+		{"label": "贺州市","value": "451100"},
+		{"label": "河池市","value": "451200"},
+		{"label": "来宾市","value": "451300"},
+		{"label": "崇左市","value": "451400"}
+	],
+	[
+		{"label": "海口市","value": "460100"},
+		{"label": "三亚市","value": "460200"},
+		{"label": "三沙市","value": "460300"},
+		{"label": "儋州市","value": "460400"},
+		{"label": "省直辖县级行政区划","value": "469000"}
+	],
+	[
+		{"label": "市辖区","value": "500100"},
+		{"label": "县","value": "500200"}
+	],
+	[
+		{"label": "成都市","value": "510100"},
+		{"label": "自贡市","value": "510300"},
+		{"label": "攀枝花市","value": "510400"},
+		{"label": "泸州市","value": "510500"},
+		{"label": "德阳市","value": "510600"},
+		{"label": "绵阳市","value": "510700"},
+		{"label": "广元市","value": "510800"},
+		{"label": "遂宁市","value": "510900"},
+		{"label": "内江市","value": "511000"},
+		{"label": "乐山市","value": "511100"},
+		{"label": "南充市","value": "511300"},
+		{"label": "眉山市","value": "511400"},
+		{"label": "宜宾市","value": "511500"},
+		{"label": "广安市","value": "511600"},
+		{"label": "达州市","value": "511700"},
+		{"label": "雅安市","value": "511800"},
+		{"label": "巴中市","value": "511900"},
+		{"label": "资阳市","value": "512000"},
+		{"label": "阿坝藏族羌族自治州","value": "513200"},
+		{"label": "甘孜藏族自治州","value": "513300"},
+		{"label": "凉山彝族自治州","value": "513400"}
+	],
+	[
+		{"label": "贵阳市","value": "520100"},
+		{"label": "六盘水市","value": "520200"},
+		{"label": "遵义市","value": "520300"},
+		{"label": "安顺市","value": "520400"},
+		{"label": "毕节市","value": "520500"},
+		{"label": "铜仁市","value": "520600"},
+		{"label": "黔西南布依族苗族自治州","value": "522300"},
+		{"label": "黔东南苗族侗族自治州","value": "522600"},
+		{"label": "黔南布依族苗族自治州","value": "522700"}
+	],
+	[
+		{"label": "昆明市","value": "530100"},
+		{"label": "曲靖市","value": "530300"},
+		{"label": "玉溪市","value": "530400"},
+		{"label": "保山市","value": "530500"},
+		{"label": "昭通市","value": "530600"},
+		{"label": "丽江市","value": "530700"},
+		{"label": "普洱市","value": "530800"},
+		{"label": "临沧市","value": "530900"},
+		{"label": "楚雄彝族自治州","value": "532300"},
+		{"label": "红河哈尼族彝族自治州","value": "532500"},
+		{"label": "文山壮族苗族自治州","value": "532600"},
+		{"label": "西双版纳傣族自治州","value": "532800"},
+		{"label": "大理白族自治州","value": "532900"},
+		{"label": "德宏傣族景颇族自治州","value": "533100"},
+		{"label": "怒江傈僳族自治州","value": "533300"},
+		{"label": "迪庆藏族自治州","value": "533400"}
+	],
+	[
+		{"label": "拉萨市","value": "540100"},
+		{"label": "日喀则市","value": "540200"},
+		{"label": "昌都市","value": "540300"},
+		{"label": "林芝市","value": "540400"},
+		{"label": "山南市","value": "540500"},
+		{"label": "那曲地区","value": "542400"},
+		{"label": "阿里地区","value": "542500"}
+	],
+	[
+		{"label": "西安市","value": "610100"},
+		{"label": "铜川市","value": "610200"},
+		{"label": "宝鸡市","value": "610300"},
+		{"label": "咸阳市","value": "610400"},
+		{"label": "渭南市","value": "610500"},
+		{"label": "延安市","value": "610600"},
+		{"label": "汉中市","value": "610700"},
+		{"label": "榆林市","value": "610800"},
+		{"label": "安康市","value": "610900"},
+		{"label": "商洛市","value": "611000"}
+	],
+	[
+		{"label": "兰州市","value": "620100"},
+		{"label": "嘉峪关市","value": "620200"},
+		{"label": "金昌市","value": "620300"},
+		{"label": "白银市","value": "620400"},
+		{"label": "天水市","value": "620500"},
+		{"label": "武威市","value": "620600"},
+		{"label": "张掖市","value": "620700"},
+		{"label": "平凉市","value": "620800"},
+		{"label": "酒泉市","value": "620900"},
+		{"label": "庆阳市","value": "621000"},
+		{"label": "定西市","value": "621100"},
+		{"label": "陇南市","value": "621200"},
+		{"label": "临夏回族自治州","value": "622900"},
+		{"label": "甘南藏族自治州","value": "623000"}
+	],
+	[
+		{"label": "西宁市","value": "630100"},
+		{"label": "海东市","value": "630200"},
+		{"label": "海北藏族自治州","value": "632200"},
+		{"label": "黄南藏族自治州","value": "632300"},
+		{"label": "海南藏族自治州","value": "632500"},
+		{"label": "果洛藏族自治州","value": "632600"},
+		{"label": "玉树藏族自治州","value": "632700"},
+		{"label": "海西蒙古族藏族自治州","value": "632800"}
+	],
+	[
+		{"label": "银川市","value": "640100"},
+		{"label": "石嘴山市","value": "640200"},
+		{"label": "吴忠市","value": "640300"},
+		{"label": "固原市","value": "640400"},
+		{"label": "中卫市","value": "640500"}
+	],
+	[
+		{"label": "乌鲁木齐市","value": "650100"},
+		{"label": "克拉玛依市","value": "650200"},
+		{"label": "吐鲁番市","value": "650400"},
+		{"label": "哈密市","value": "650500"},
+		{"label": "昌吉回族自治州","value": "652300"},
+		{"label": "博尔塔拉蒙古自治州","value": "652700"},
+		{"label": "巴音郭楞蒙古自治州","value": "652800"},
+		{"label": "阿克苏地区","value": "652900"},
+		{"label": "克孜勒苏柯尔克孜自治州","value": "653000"},
+		{"label": "喀什地区","value": "653100"},
+		{"label": "和田地区","value": "653200"},
+		{"label": "伊犁哈萨克自治州","value": "654000"},
+		{"label": "塔城地区","value": "654200"},
+		{"label": "阿勒泰地区","value": "654300"},
+		{"label": "自治区直辖县级行政区划","value": "659000"}
+	],
+	[
+		{"label": "台北","value": "660100"},
+		{"label": "高雄","value": "660200"},
+		{"label": "基隆","value": "660300"},
+		{"label": "台中","value": "660400"},
+		{"label": "台南","value": "660500"},
+		{"label": "新竹","value": "660600"},
+		{"label": "嘉义","value": "660700"},
+		{"label": "宜兰","value": "660800"},
+		{"label": "桃园","value": "660900"},
+		{"label": "苗栗","value": "661000"},
+		{"label": "彰化","value": "661100"},
+		{"label": "南投","value": "661200"},
+		{"label": "云林","value": "661300"},
+		{"label": "屏东","value": "661400"},
+		{"label": "台东","value": "661500"},
+		{"label": "花莲","value": "661600"},
+		{"label": "澎湖","value": "661700"}
+	],
+	[
+		{"label": "香港岛","value": "670100"},
+		{"label": "九龙","value": "670200"},
+		{"label": "新界","value": "670300"}
+	],
+	[
+		{"label": "澳门半岛","value": "680100"},
+		{"label": "氹仔岛","value": "680200"},
+		{"label": "路环岛","value": "680300"},
+		{"label": "路氹城","value": "680400"}
+	]
+]
+export default cityData;

+ 38 - 0
GraceUI5/data/city-data/province.js

@@ -0,0 +1,38 @@
+/* 本数据由 GraceUI 团队精心整理,版权归 GraceUI 所有,未经允许不得发布到公开渠道及抄袭! */
+var provinceData = [
+	{"label": "北京市", "value": "110000"},
+	{ "label": "天津市", "value": "120000"},
+	{"label": "河北省","value": "130000"},
+	{"label": "山西省","value": "140000"},
+	{"label": "内蒙古自治区","value": "150000"},
+	{"label": "辽宁省","value": "210000"},
+	{"label": "吉林省","value": "220000"},
+	{"label": "黑龙江省","value": "230000"},
+	{"label": "上海市","value": "310000"},
+	{"label": "江苏省","value": "320000"},
+	{"label": "浙江省","value": "330000"},
+	{"label": "安徽省","value": "340000"},
+	{"label": "福建省","value": "350000"},
+	{"label": "江西省","value": "360000"},
+	{"label": "山东省","value": "370000"},
+	{"label": "河南省","value": "410000"},
+	{"label": "湖北省","value": "420000"},
+	{"label": "湖南省","value": "430000"},
+	{"label": "广东省","value": "440000"},
+	{"label": "广西壮族自治区", "value": "450000"},
+	{"label": "海南省","value": "460000"},
+	{"label": "重庆市","value": "500000"},
+	{"label": "四川省","value": "510000"},
+	{"label": "贵州省","value": "520000"},
+	{"label": "云南省","value": "530000"},
+	{"label": "西藏自治区","value": "540000"},
+	{"label": "陕西省","value": "610000"},
+	{"label": "甘肃省","value": "620000"},
+	{"label": "青海省","value": "630000"},
+	{"label": "宁夏回族自治区","value": "640000"},
+	{"label": "新疆维吾尔自治区","value": "650000"},
+	{"label": "台湾","value": "710000"},
+	{"label": "香港","value": "810000"},
+	{"label": "澳门","value": "820000"}
+]
+export default provinceData;

+ 742 - 0
GraceUI5/data/cityData.js

@@ -0,0 +1,742 @@
+/*
+本数据由 GraceUI 团队精心整理
+版权归 GraceUI 所有,未经允许不得发布到公开渠道及抄袭
+*/
+var cityData = {
+	A: [
+		
+		{ name: "安达市", code: "231281", pinyin: "andashi231281"},
+		{ name: "安国市", code: "130683", pinyin: "anguoshi130683"},
+		{ name: "安康市", code: "610900", pinyin: "ankangshi610900"},
+		{ name: "安陆市", code: "420982", pinyin: "anlushi420982"},
+		{ name: "安宁市", code: "530181", pinyin: "anningshi530181"},
+		{ name: "安庆市", code: "340800", pinyin: "anqingshi340800"},
+		{ name: "安丘市", code: "370784", pinyin: "anqiushi370784"},
+		{ name: "鞍山市", code: "210300", pinyin: "anshanshi210300"},
+		{ name: "安顺市", code: "520400", pinyin: "anshunshi520400"},
+		{ name: "安阳市", code: "410500", pinyin: "anyangshi410500"},
+		{ name: "澳门", code:"820000", pinyin:"aomen820000"}
+	],
+	B: [
+		{ name: "白城市", code: "220800", pinyin: "baichengshi220800"},
+		{ name: "百色市", code: "451000", pinyin: "baiseshi451000"},
+		{ name: "白山市", code: "220600", pinyin: "baishanshi220600"},
+		{ name: "白银市", code: "620400", pinyin: "baiyinshi620400"},
+		{ name: "保定市", code: "130600", pinyin: "baodingshi130600"},
+		{ name: "宝鸡市", code: "610300", pinyin: "baojishi610300"},
+		{ name: "保山市", code: "530500", pinyin: "baoshanshi530500"},
+		{ name: "包头市", code: "150200", pinyin: "baotoushi150200"},
+		{ name: "巴彦淖尔市", code: "150800", pinyin: "bayannaoershi150800"},
+		{ name: "巴中市", code: "511900", pinyin: "bazhongshi511900"},
+		{ name: "霸州市", code: "131081", pinyin: "bazhoushi131081"},
+		{ name: "北安市", code: "231181", pinyin: "beianshi231181"},
+		{ name: "北海市", code: "450500", pinyin: "beihaishi450500"},
+		{ name: "北京市", code: "110000", pinyin: "beijingshi110000"},
+		{ name: "北流市", code: "450981", pinyin: "beiliushi450981"},
+		{ name: "北票市", code: "211381", pinyin: "beipiaoshi211381"},
+		{ name: "北屯市", code: "659005", pinyin: "beitunshi659005"},
+		{ name: "北镇市", code: "210782", pinyin: "beizhenshi210782"},
+		{ name: "蚌埠市", code: "340300", pinyin: "bengbushi340300"},
+		{ name: "本溪市", code: "210500", pinyin: "benxishi210500"},
+		{ name: "毕节市", code: "520500", pinyin: "bijieshi520500"},
+		{ name: "滨州市", code: "371600", pinyin: "binzhoushi371600"},
+		{ name: "彬州市", code: "610482", pinyin: "binzhoushi610482"},
+		{ name: "博乐市", code: "652701", pinyin: "boleshi652701"},
+		{ name: "泊头市", code: "130981", pinyin: "botoushi130981"},
+		{ name: "亳州市", code: "341600", pinyin: "bozhoushi341600"}
+	],
+	C: [
+		{ name: "沧州市", code: "130900", pinyin: "cangzhoushi130900"},
+		{ name: "岑溪市", code: "450481", pinyin: "cenxishi450481"},
+		{ name: "长春市", code: "220100", pinyin: "changchunshi220100"},
+		{ name: "常德市", code: "430700", pinyin: "changdeshi430700"},
+		{ name: "昌都市", code: "540300", pinyin: "changdoushi540300"},
+		{ name: "长葛市", code: "411082", pinyin: "changgeshi411082"},
+		{ name: "昌吉市", code: "652301", pinyin: "changjishi652301"},
+		{ name: "常宁市", code: "430482", pinyin: "changningshi430482"},
+		{ name: "长沙市", code: "430100", pinyin: "changshashi430100"},
+		{ name: "常熟市", code: "320581", pinyin: "changshushi320581"},
+		{ name: "昌邑市", code: "370786", pinyin: "changyishi370786"},
+		{ name: "长垣市", code: "410783", pinyin: "changyuanshi410783"},
+		{ name: "长治市", code: "140400", pinyin: "changzhishi140400"},
+		{ name: "常州市", code: "320400", pinyin: "changzhoushi320400"},
+		{ name: "巢湖市", code: "340181", pinyin: "chaohushi340181"},
+		{ name: "朝阳市", code: "211300", pinyin: "chaoyangshi211300"},
+		{ name: "潮州市", code: "445100", pinyin: "chaozhoushi445100"},
+		{ name: "承德市", code: "130800", pinyin: "chengdeshi130800"},
+		{ name: "成都市", code: "510100", pinyin: "chengdoushi510100"},
+		{ name: "澄江市", code: "530481", pinyin: "chengjiangshi530481"},
+		{ name: "郴州市", code: "431000", pinyin: "chenzhoushi431000"},
+		{ name: "赤壁市", code: "421281", pinyin: "chibishi421281"},
+		{ name: "赤峰市", code: "150400", pinyin: "chifengshi150400"},
+		{ name: "赤水市", code: "520381", pinyin: "chishuishi520381"},
+		{ name: "池州市", code: "341700", pinyin: "chizhoushi341700"},
+		{ name: "崇州市", code: "510184", pinyin: "chongzhoushi510184"},
+		{ name: "崇左市", code: "451400", pinyin: "chongzuoshi451400"},
+		{ name: "楚雄市", code: "532301", pinyin: "chuxiongshi532301"},
+		{ name: "滁州市", code: "341100", pinyin: "chuzhoushi341100"},
+		{ name: "慈溪市", code: "330282", pinyin: "cixishi330282"}
+	],
+	D: [
+		{ name: "大安市", code: "220882", pinyin: "daanshi220882"},
+		{ name: "大连市", code: "210200", pinyin: "dalianshi210200"},
+		{ name: "大理市", code: "532901", pinyin: "dalishi532901"},
+		{ name: "丹东市", code: "210600", pinyin: "dandongshi210600"},
+		{ name: "当阳市", code: "420582", pinyin: "dangyangshi420582"},
+		{ name: "丹江口市", code: "420381", pinyin: "danjiangkoushi420381"},
+		{ name: "丹阳市", code: "321181", pinyin: "danyangshi321181"},
+		{ name: "儋州市", code: "460400", pinyin: "danzhoushi460400"},
+		{ name: "邛崃市", code: "510183", pinyin: "daolaishi510183"},
+		{ name: "大庆市", code: "230600", pinyin: "daqingshi230600"},
+		{ name: "大石桥市", code: "210882", pinyin: "dashiqiaoshi210882"},
+		{ name: "大同市", code: "140200", pinyin: "datongshi140200"},
+		{ name: "大冶市", code: "420281", pinyin: "dayeshi420281"},
+		{ name: "达州市", code: "511700", pinyin: "dazhoushi511700"},
+		{ name: "德惠市", code: "220183", pinyin: "dehuishi220183"},
+		{ name: "德令哈市", code: "632802", pinyin: "delinghashi632802"},
+		{ name: "登封市", code: "410185", pinyin: "dengfengshi410185"},
+		{ name: "灯塔市", code: "211081", pinyin: "dengtashi211081"},
+		{ name: "邓州市", code: "411381", pinyin: "dengzhoushi411381"},
+		{ name: "德兴市", code: "361181", pinyin: "dexingshi361181"},
+		{ name: "德阳市", code: "510600", pinyin: "deyangshi510600"},
+		{ name: "德州市", code: "371400", pinyin: "dezhoushi371400"},
+		{ name: "定西市", code: "621100", pinyin: "dingxishi621100"},
+		{ name: "定州市", code: "130682", pinyin: "dingzhoushi130682"},
+		{ name: "东方市", code: "469007", pinyin: "dongfangshi469007"},
+		{ name: "东港市", code: "210681", pinyin: "donggangshi210681"},
+		{ name: "东莞市", code: "441900", pinyin: "dongguanshi441900"},
+		{ name: "东宁市", code: "231086", pinyin: "dongningshi231086"},
+		{ name: "东台市", code: "320981", pinyin: "dongtaishi320981"},
+		{ name: "东兴市", code: "450681", pinyin: "dongxingshi450681"},
+		{ name: "东阳市", code: "330783", pinyin: "dongyangshi330783"},
+		{ name: "东营市", code: "370500", pinyin: "dongyingshi370500"},
+		{ name: "都江堰市", code: "510181", pinyin: "doujiangyanshi510181"},
+		{ name: "都匀市", code: "522701", pinyin: "douyunshi522701"},
+		{ name: "敦煌市", code: "620982", pinyin: "dunhuangshi620982"},
+		{ name: "敦化市", code: "222403", pinyin: "dunhuashi222403"}
+	],
+	E: [
+		{ name: "鄂尔多斯市", code: "150600", pinyin: "eerduosishi150600"},
+		{ name: "额尔古纳市", code: "150784", pinyin: "eergunashi150784"},
+		{ name: "阿尔山市", code: "152202", pinyin: "eershanshi152202"},
+		{ name: "阿克苏市", code: "652901", pinyin: "ekesushi652901"},
+		{ name: "阿拉尔市", code: "659002", pinyin: "elaershi659002"},
+		{ name: "阿拉山口市", code: "652702", pinyin: "elashankoushi652702"},
+		{ name: "阿勒泰市", code: "654301", pinyin: "eletaishi654301"},
+		{ name: "峨眉山市", code: "511181", pinyin: "emeishanshi511181"},
+		{ name: "恩平市", code: "440785", pinyin: "enpingshi440785"},
+		{ name: "恩施市", code: "422801", pinyin: "enshishi422801"},
+		{ name: "二连浩特市", code: "152501", pinyin: "erlianhaoteshi152501"},
+		{ name: "阿图什市", code: "653001", pinyin: "etushenshi653001"},
+		{ name: "鄂州市", code: "420700", pinyin: "ezhoushi420700"}
+	],
+	F: [
+		{ name: "防城港市", code: "450600", pinyin: "fangchenggangshi450600"},
+		{ name: "肥城市", code: "370983", pinyin: "feichengshi370983"},
+		{ name: "凤城市", code: "210682", pinyin: "fengchengshi210682"},
+		{ name: "丰城市", code: "360981", pinyin: "fengchengshi360981"},
+		{ name: "丰镇市", code: "150981", pinyin: "fengzhenshi150981"},
+		{ name: "汾阳市", code: "141182", pinyin: "fenyangshi141182"},
+		{ name: "佛山市", code: "440600", pinyin: "foshanshi440600"},
+		{ name: "福安市", code: "350981", pinyin: "fuanshi350981"},
+		{ name: "福鼎市", code: "350982", pinyin: "fudingshi350982"},
+		{ name: "富锦市", code: "230882", pinyin: "fujinshi230882"},
+		{ name: "阜康市", code: "652302", pinyin: "fukangshi652302"},
+		{ name: "福清市", code: "350181", pinyin: "fuqingshi350181"},
+		{ name: "福泉市", code: "522702", pinyin: "fuquanshi522702"},
+		{ name: "抚顺市", code: "210400", pinyin: "fushunshi210400"},
+		{ name: "阜新市", code: "210900", pinyin: "fuxinshi210900"},
+		{ name: "阜阳市", code: "341200", pinyin: "fuyangshi341200"},
+		{ name: "抚远市", code: "230883", pinyin: "fuyuanshi230883"},
+		{ name: "扶余市", code: "220781", pinyin: "fuyushi220781"},
+		{ name: "福州市", code: "350100", pinyin: "fuzhoushi350100"},
+		{ name: "抚州市", code: "361000", pinyin: "fuzhoushi361000"}
+	],
+	G: [
+		{ name: "贵溪市", code: "360681", pinyin: "guixishi360681"},
+		{ name: "贵阳市", code: "520100", pinyin: "guiyangshi520100"},
+		{ name: "盖州市", code: "210881", pinyin: "gaizhoushi210881"},
+		{ name: "赣州市", code: "360700", pinyin: "ganzhoushi360700"},
+		{ name: "高安市", code: "360983", pinyin: "gaoanshi360983"},
+		{ name: "高碑店市", code: "130684", pinyin: "gaobeidianshi130684"},
+		{ name: "高密市", code: "370785", pinyin: "gaomishi370785"},
+		{ name: "高平市", code: "140581", pinyin: "gaopingshi140581"},
+		{ name: "高邮市", code: "321084", pinyin: "gaoyoushi321084"},
+		{ name: "高州市", code: "440981", pinyin: "gaozhoushi440981"},
+		{ name: "格尔木市", code: "632801", pinyin: "geermushi632801"},
+		{ name: "个旧市", code: "532501", pinyin: "gejiushi532501"},
+		{ name: "根河市", code: "150785", pinyin: "genheshi150785"},
+		{ name: "共青城市", code: "360482", pinyin: "gongqingchengshi360482"},
+		{ name: "巩义市", code: "410181", pinyin: "gongyishi410181"},
+		{ name: "公主岭市", code: "220381", pinyin: "gongzhulingshi220381"},
+		{ name: "广安市", code: "511600", pinyin: "guanganshi511600"},
+		{ name: "广德市", code: "341882", pinyin: "guangdeshi341882"},
+		{ name: "广汉市", code: "510681", pinyin: "guanghanshi510681"},
+		{ name: "广水市", code: "421381", pinyin: "guangshuishi421381"},
+		{ name: "广元市", code: "510800", pinyin: "guangyuanshi510800"},
+		{ name: "广州市", code: "440100", pinyin: "guangzhoushi440100"},
+		{ name: "贵港市", code: "450800", pinyin: "guigangshi450800"},
+		{ name: "桂林市", code: "450300", pinyin: "guilinshi450300"},
+		{ name: "桂平市", code: "450881", pinyin: "guipingshi450881"},
+		{ name: "古交市", code: "140181", pinyin: "gujiaoshi140181"},
+		{ name: "固原市", code: "640400", pinyin: "guyuanshi640400"}
+	],
+	H: [
+		{ name: "哈尔滨市", code: "230100", pinyin: "haerbinshi230100"},
+		{ name: "海安市", code: "320685", pinyin: "haianshi320685"},
+		{ name: "海城市", code: "210381", pinyin: "haichengshi210381"},
+		{ name: "海东市", code: "630200", pinyin: "haidongshi630200"},
+		{ name: "海口市", code: "460100", pinyin: "haikoushi460100"},
+		{ name: "海林市", code: "231083", pinyin: "hailinshi231083"},
+		{ name: "海伦市", code: "231283", pinyin: "hailunshi231283"},
+		{ name: "海门市", code: "320684", pinyin: "haimenshi320684"},
+		{ name: "海宁市", code: "330481", pinyin: "hainingshi330481"},
+		{ name: "海阳市", code: "370687", pinyin: "haiyangshi370687"},
+		{ name: "哈密市", code: "650500", pinyin: "hamishi650500"},
+		{ name: "韩城市", code: "610581", pinyin: "hanchengshi610581"},
+		{ name: "汉川市", code: "420984", pinyin: "hanchuanshi420984"},
+		{ name: "邯郸市", code: "130400", pinyin: "handanshi130400"},
+		{ name: "杭州市", code: "330100", pinyin: "hangzhoushi330100"},
+		{ name: "汉中市", code: "610700", pinyin: "hanzhongshi610700"},
+		{ name: "鹤壁市", code: "410600", pinyin: "hebishi410600"},
+		{ name: "河池市", code: "451200", pinyin: "hechishi451200"},
+		{ name: "合肥市", code: "340100", pinyin: "hefeishi340100"},
+		{ name: "鹤岗市", code: "230400", pinyin: "hegangshi230400"},
+		{ name: "黑河市", code: "231100", pinyin: "heiheshi231100"},
+		{ name: "河间市", code: "130984", pinyin: "hejianshi130984"},
+		{ name: "河津市", code: "140882", pinyin: "hejinshi140882"},
+		{ name: "和龙市", code: "222406", pinyin: "helongshi222406"},
+		{ name: "衡水市", code: "131100", pinyin: "hengshuishi131100"},
+		{ name: "衡阳市", code: "430400", pinyin: "hengyangshi430400"},
+		{ name: "鹤山市", code: "440784", pinyin: "heshanshi440784"},
+		{ name: "合山市", code: "451381", pinyin: "heshanshi451381"},
+		{ name: "和田市", code: "653201", pinyin: "hetianshi653201"},
+		{ name: "河源市", code: "441600", pinyin: "heyuanshi441600"},
+		{ name: "菏泽市", code: "371700", pinyin: "hezeshi371700"},
+		{ name: "贺州市", code: "451100", pinyin: "hezhoushi451100"},
+		{ name: "合作市", code: "623001", pinyin: "hezuoshi623001"},
+		{ name: "洪湖市", code: "421083", pinyin: "honghushi421083"},
+		{ name: "洪江市", code: "431281", pinyin: "hongjiangshi431281"},
+		{ name: "侯马市", code: "141081", pinyin: "houmashi141081"},
+		{ name: "桦甸市", code: "220282", pinyin: "huadianshi220282"},
+		{ name: "淮安市", code: "320800", pinyin: "huaianshi320800"},
+		{ name: "淮北市", code: "340600", pinyin: "huaibeishi340600"},
+		{ name: "怀化市", code: "431200", pinyin: "huaihuashi431200"},
+		{ name: "淮南市", code: "340400", pinyin: "huainanshi340400"},
+		{ name: "怀仁市", code: "140681", pinyin: "huairenshi140681"},
+		{ name: "黄冈市", code: "421100", pinyin: "huanggangshi421100"},
+		{ name: "黄骅市", code: "130983", pinyin: "huanghuashi130983"},
+		{ name: "黄山市", code: "341000", pinyin: "huangshanshi341000"},
+		{ name: "黄石市", code: "420200", pinyin: "huangshishi420200"},
+		{ name: "华亭市", code: "620881", pinyin: "huatingshi620881"},
+		{ name: "华蓥市", code: "511681", pinyin: "huayingshi511681"},
+		{ name: "华阴市", code: "610582", pinyin: "huayinshi610582"},
+		{ name: "化州市", code: "440982", pinyin: "huazhoushi440982"},
+		{ name: "呼和浩特市", code: "150100", pinyin: "huhehaoteshi150100"},
+		{ name: "辉县市", code: "410782", pinyin: "huixianshi410782"},
+		{ name: "惠州市", code: "441300", pinyin: "huizhoushi441300"},
+		{ name: "虎林市", code: "230381", pinyin: "hulinshi230381"},
+		{ name: "葫芦岛市", code: "211400", pinyin: "huludaoshi211400"},
+		{ name: "呼伦贝尔市", code: "150700", pinyin: "hulunbeiershi150700"},
+		{ name: "珲春市", code: "222404", pinyin: "hunchunshi222404"},
+		{ name: "霍尔果斯市", code: "654004", pinyin: "huoerguosishi654004"},
+		{ name: "霍林郭勒市", code: "150581", pinyin: "huolinguoleshi150581"},
+		{ name: "霍州市", code: "141082", pinyin: "huozhoushi141082"},
+		{ name: "胡杨河市", code: "659010", pinyin: "huyangheshi659010"},
+		{ name: "湖州市", code: "330500", pinyin: "huzhoushi330500"}
+	],
+	J: [
+		{ name: "佳木斯市", code: "230800", pinyin: "jiamusishi230800"},
+		{ name: "建德市", code: "330182", pinyin: "jiandeshi330182"},
+		{ name: "江门市", code: "440700", pinyin: "jiangmenshi440700"},
+		{ name: "江山市", code: "330881", pinyin: "jiangshanshi330881"},
+		{ name: "江阴市", code: "320281", pinyin: "jiangyinshi320281"},
+		{ name: "江油市", code: "510781", pinyin: "jiangyoushi510781"},
+		{ name: "建瓯市", code: "350783", pinyin: "jianoushi350783"},
+		{ name: "集安市", code: "220582", pinyin: "jianshi220582"},
+		{ name: "吉安市", code: "360800", pinyin: "jianshi360800"},
+		{ name: "简阳市", code: "510185", pinyin: "jianyangshi510185"},
+		{ name: "蛟河市", code: "220281", pinyin: "jiaoheshi220281"},
+		{ name: "胶州市", code: "370281", pinyin: "jiaozhoushi370281"},
+		{ name: "焦作市", code: "410800", pinyin: "jiaozuoshi410800"},
+		{ name: "嘉兴市", code: "330400", pinyin: "jiaxingshi330400"},
+		{ name: "嘉峪关市", code: "620200", pinyin: "jiayuguanshi620200"},
+		{ name: "界首市", code: "341282", pinyin: "jieshoushi341282"},
+		{ name: "介休市", code: "140781", pinyin: "jiexiushi140781"},
+		{ name: "揭阳市", code: "445200", pinyin: "jieyangshi445200"},
+		{ name: "吉林市", code: "220200", pinyin: "jilinshi220200"},
+		{ name: "济南市", code: "370100", pinyin: "jinanshi370100"},
+		{ name: "金昌市", code: "620300", pinyin: "jinchangshi620300"},
+		{ name: "晋城市", code: "140500", pinyin: "jinchengshi140500"},
+		{ name: "景德镇市", code: "360200", pinyin: "jingdezhenshi360200"},
+		{ name: "井冈山市", code: "360881", pinyin: "jinggangshanshi360881"},
+		{ name: "景洪市", code: "532801", pinyin: "jinghongshi532801"},
+		{ name: "靖江市", code: "321282", pinyin: "jingjiangshi321282"},
+		{ name: "荆门市", code: "420800", pinyin: "jingmenshi420800"},
+		{ name: "京山市", code: "420882", pinyin: "jingshanshi420882"},
+		{ name: "靖西市", code: "451081", pinyin: "jingxishi451081"},
+		{ name: "荆州市", code: "421000", pinyin: "jingzhoushi421000"},
+		{ name: "金华市", code: "330700", pinyin: "jinhuashi330700"},
+		{ name: "济宁市", code: "370800", pinyin: "jiningshi370800"},
+		{ name: "晋江市", code: "350582", pinyin: "jinjiangshi350582"},
+		{ name: "津市市", code: "430781", pinyin: "jinshishi430781"},
+		{ name: "晋中市", code: "140700", pinyin: "jinzhongshi140700"},
+		{ name: "晋州市", code: "130183", pinyin: "jinzhoushi130183"},
+		{ name: "锦州市", code: "210700", pinyin: "jinzhoushi210700"},
+		{ name: "吉首市", code: "433101", pinyin: "jishoushi433101"},
+		{ name: "九江市", code: "360400", pinyin: "jiujiangshi360400"},
+		{ name: "酒泉市", code: "620900", pinyin: "jiuquanshi620900"},
+		{ name: "鸡西市", code: "230300", pinyin: "jixishi230300"},
+		{ name: "济源市", code: "419001", pinyin: "jiyuanshi419001"},
+		{ name: "句容市", code: "321183", pinyin: "jurongshi321183"}
+	],
+	K: [
+		{ name: "开封市", code: "410200", pinyin: "kaifengshi410200"},
+		{ name: "凯里市", code: "522601", pinyin: "kailishi522601"},
+		{ name: "开平市", code: "440783", pinyin: "kaipingshi440783"},
+		{ name: "开原市", code: "211282", pinyin: "kaiyuanshi211282"},
+		{ name: "开远市", code: "532502", pinyin: "kaiyuanshi532502"},
+		{ name: "康定市", code: "513301", pinyin: "kangdingshi513301"},
+		{ name: "喀什市", code: "653101", pinyin: "kashenshi653101"},
+		{ name: "可克达拉市", code: "659008", pinyin: "kekedalashi659008"},
+		{ name: "克拉玛依市", code: "650200", pinyin: "kelamayishi650200"},
+		{ name: "库车市", code: "652902", pinyin: "kucheshi652902"},
+		{ name: "库尔勒市", code: "652801", pinyin: "kuerleshi652801"},
+		{ name: "奎屯市", code: "654003", pinyin: "kuitunshi654003"},
+		{ name: "昆明市", code: "530100", pinyin: "kunmingshi530100"},
+		{ name: "昆山市", code: "320583", pinyin: "kunshanshi320583"},
+		{ name: "昆玉市", code: "659009", pinyin: "kunyushi659009"}
+	],
+	L: [
+		{ name: "来宾市", code: "451300", pinyin: "laibinshi451300"},
+		{ name: "莱西市", code: "370285", pinyin: "laixishi370285"},
+		{ name: "莱阳市", code: "370682", pinyin: "laiyangshi370682"},
+		{ name: "莱州市", code: "370683", pinyin: "laizhoushi370683"},
+		{ name: "廊坊市", code: "131000", pinyin: "langfangshi131000"},
+		{ name: "阆中市", code: "511381", pinyin: "langzhongshi511381"},
+		{ name: "兰溪市", code: "330781", pinyin: "lanxishi330781"},
+		{ name: "兰州市", code: "620100", pinyin: "lanzhoushi620100"},
+		{ name: "老河口市", code: "420682", pinyin: "laohekoushi420682"},
+		{ name: "拉萨市", code: "540100", pinyin: "lasashi540100"},
+		{ name: "乐昌市", code: "440281", pinyin: "lechangshi440281"},
+		{ name: "耒阳市", code: "430481", pinyin: "leiyangshi430481"},
+		{ name: "雷州市", code: "440882", pinyin: "leizhoushi440882"},
+		{ name: "乐陵市", code: "371481", pinyin: "lelingshi371481"},
+		{ name: "冷水江市", code: "431381", pinyin: "lengshuijiangshi431381"},
+		{ name: "乐平市", code: "360281", pinyin: "lepingshi360281"},
+		{ name: "乐清市", code: "330382", pinyin: "leqingshi330382"},
+		{ name: "乐山市", code: "511100", pinyin: "leshanshi511100"},
+		{ name: "廉江市", code: "440881", pinyin: "lianjiangshi440881"},
+		{ name: "涟源市", code: "431382", pinyin: "lianyuanshi431382"},
+		{ name: "连云港市", code: "320700", pinyin: "lianyungangshi320700"},
+		{ name: "连州市", code: "441882", pinyin: "lianzhoushi441882"},
+		{ name: "聊城市", code: "371500", pinyin: "liaochengshi371500"},
+		{ name: "辽阳市", code: "211000", pinyin: "liaoyangshi211000"},
+		{ name: "辽源市", code: "220400", pinyin: "liaoyuanshi220400"},
+		{ name: "利川市", code: "422802", pinyin: "lichuanshi422802"},
+		{ name: "丽江市", code: "530700", pinyin: "lijiangshi530700"},
+		{ name: "醴陵市", code: "430281", pinyin: "lilingshi430281"},
+		{ name: "临沧市", code: "530900", pinyin: "lincangshi530900"},
+		{ name: "临汾市", code: "141000", pinyin: "linfenshi141000"},
+		{ name: "灵宝市", code: "411282", pinyin: "lingbaoshi411282"},
+		{ name: "凌海市", code: "210781", pinyin: "linghaishi210781"},
+		{ name: "灵武市", code: "640181", pinyin: "lingwushi640181"},
+		{ name: "凌源市", code: "211382", pinyin: "lingyuanshi211382"},
+		{ name: "临海市", code: "331082", pinyin: "linhaishi331082"},
+		{ name: "临江市", code: "220681", pinyin: "linjiangshi220681"},
+		{ name: "临清市", code: "371581", pinyin: "linqingshi371581"},
+		{ name: "临湘市", code: "430682", pinyin: "linxiangshi430682"},
+		{ name: "临夏市", code: "622901", pinyin: "linxiashi622901"},
+		{ name: "临沂市", code: "371300", pinyin: "linyishi371300"},
+		{ name: "林芝市", code: "540400", pinyin: "linzhishi540400"},
+		{ name: "林州市", code: "410581", pinyin: "linzhoushi410581"},
+		{ name: "荔浦市", code: "450381", pinyin: "lipushi450381"},
+		{ name: "丽水市", code: "331100", pinyin: "lishuishi331100"},
+		{ name: "六安市", code: "341500", pinyin: "liuanshi341500"},
+		{ name: "六盘水市", code: "520200", pinyin: "liupanshuishi520200"},
+		{ name: "浏阳市", code: "430181", pinyin: "liuyangshi430181"},
+		{ name: "柳州市", code: "450200", pinyin: "liuzhoushi450200"},
+		{ name: "溧阳市", code: "320481", pinyin: "liyangshi320481"},
+		{ name: "隆昌市", code: "511083", pinyin: "longchangshi511083"},
+		{ name: "龙港市", code: "330383", pinyin: "longgangshi330383"},
+		{ name: "龙海市", code: "350681", pinyin: "longhaishi350681"},
+		{ name: "龙井市", code: "222405", pinyin: "longjingshi222405"},
+		{ name: "龙口市", code: "370681", pinyin: "longkoushi370681"},
+		{ name: "陇南市", code: "621200", pinyin: "longnanshi621200"},
+		{ name: "龙泉市", code: "331181", pinyin: "longquanshi331181"},
+		{ name: "龙岩市", code: "350800", pinyin: "longyanshi350800"},
+		{ name: "娄底市", code: "431300", pinyin: "loudishi431300"},
+		{ name: "滦州市", code: "130284", pinyin: "luanzhoushi130284"},
+		{ name: "陆丰市", code: "441581", pinyin: "lufengshi441581"},
+		{ name: "罗定市", code: "445381", pinyin: "luodingshi445381"},
+		{ name: "漯河市", code: "411100", pinyin: "luoheshi411100"},
+		{ name: "洛阳市", code: "410300", pinyin: "luoyangshi410300"},
+		{ name: "庐山市", code: "360483", pinyin: "lushanshi360483"},
+		{ name: "泸水市", code: "533301", pinyin: "lushuishi533301"},
+		{ name: "泸州市", code: "510500", pinyin: "luzhoushi510500"},
+		{ name: "吕梁市", code: "141100", pinyin: "lvliangshi141100"}
+	],
+	M: [
+		{ name: "马鞍山市", code: "340500", pinyin: "maanshanshi340500"},
+		{ name: "麻城市", code: "421181", pinyin: "machengshi421181"},
+		{ name: "马尔康市", code: "513201", pinyin: "maerkangshi513201"},
+		{ name: "芒市", code: "533103", pinyin: "mangshi533103"},
+		{ name: "茫崖市", code: "632803", pinyin: "mangyashi632803"},
+		{ name: "满洲里市", code: "150781", pinyin: "manzhoulishi150781"},
+		{ name: "茂名市", code: "440900", pinyin: "maomingshi440900"},
+		{ name: "梅河口市", code: "220581", pinyin: "meihekoushi220581"},
+		{ name: "眉山市", code: "511400", pinyin: "meishanshi511400"},
+		{ name: "梅州市", code: "441400", pinyin: "meizhoushi441400"},
+		{ name: "孟州市", code: "410883", pinyin: "mengzhoushi410883"},
+		{ name: "蒙自市", code: "532503", pinyin: "mengzishi532503"},
+		{ name: "绵阳市", code: "510700", pinyin: "mianyangshi510700"},
+		{ name: "绵竹市", code: "510683", pinyin: "mianzhushi510683"},
+		{ name: "弥勒市", code: "532504", pinyin: "mileshi532504"},
+		{ name: "汨罗市", code: "430681", pinyin: "miluoshi430681"},
+		{ name: "明光市", code: "341182", pinyin: "mingguangshi341182"},
+		{ name: "密山市", code: "230382", pinyin: "mishanshi230382"},
+		{ name: "漠河市", code: "232701", pinyin: "moheshi232701"},
+		{ name: "牡丹江市", code: "231000", pinyin: "mudanjiangshi231000"},
+		{ name: "穆棱市", code: "231085", pinyin: "mulengshi231085"}
+	],
+	N: [
+		{ name: "南安市", code: "350583", pinyin: "nananshi350583"},
+		{ name: "南昌市", code: "360100", pinyin: "nanchangshi360100"},
+		{ name: "南充市", code: "511300", pinyin: "nanchongshi511300"},
+		{ name: "南宫市", code: "130581", pinyin: "nangongshi130581"},
+		{ name: "南京市", code: "320100", pinyin: "nanjingshi320100"},
+		{ name: "南宁市", code: "450100", pinyin: "nanningshi450100"},
+		{ name: "南平市", code: "350700", pinyin: "nanpingshi350700"},
+		{ name: "南通市", code: "320600", pinyin: "nantongshi320600"},
+		{ name: "南雄市", code: "440282", pinyin: "nanxiongshi440282"},
+		{ name: "南阳市", code: "411300", pinyin: "nanyangshi411300"},
+		{ name: "那曲市", code: "540600", pinyin: "naqushi540600"},
+		{ name: "讷河市", code: "230281", pinyin: "neheshi230281"},
+		{ name: "内江市", code: "511000", pinyin: "neijiangshi511000"},
+		{ name: "嫩江市", code: "231183", pinyin: "nenjiangshi231183"},
+		{ name: "宁安市", code: "231084", pinyin: "ninganshi231084"},
+		{ name: "宁波市", code: "330200", pinyin: "ningboshi330200"},
+		{ name: "宁德市", code: "350900", pinyin: "ningdeshi350900"},
+		{ name: "宁国市", code: "341881", pinyin: "ningguoshi341881"},
+		{ name: "宁乡市", code: "430182", pinyin: "ningxiangshi430182"}
+	],
+	P: [
+		{ name: "盘锦市", code: "211100", pinyin: "panjinshi211100"},
+		{ name: "磐石市", code: "220284", pinyin: "panshishi220284"},
+		{ name: "攀枝花市", code: "510400", pinyin: "panzhihuashi510400"},
+		{ name: "盘州市", code: "520281", pinyin: "panzhoushi520281"},
+		{ name: "蓬莱市", code: "370684", pinyin: "penglaishi370684"},
+		{ name: "彭州市", code: "510182", pinyin: "pengzhoushi510182"},
+		{ name: "平顶山市", code: "410400", pinyin: "pingdingshanshi410400"},
+		{ name: "平度市", code: "370283", pinyin: "pingdushi370283"},
+		{ name: "平果市", code: "451082", pinyin: "pingguoshi451082"},
+		{ name: "平湖市", code: "330482", pinyin: "pinghushi330482"},
+		{ name: "平凉市", code: "620800", pinyin: "pingliangshi620800"},
+		{ name: "平泉市", code: "130881", pinyin: "pingquanshi130881"},
+		{ name: "萍乡市", code: "360300", pinyin: "pingxiangshi360300"},
+		{ name: "凭祥市", code: "451481", pinyin: "pingxiangshi451481"},
+		{ name: "邳州市", code: "320382", pinyin: "pizhoushi320382"},
+		{ name: "普洱市", code: "530800", pinyin: "puershi530800"},
+		{ name: "普宁市", code: "445281", pinyin: "puningshi445281"},
+		{ name: "莆田市", code: "350300", pinyin: "putianshi350300"},
+		{ name: "濮阳市", code: "410900", pinyin: "puyangshi410900"}
+	],
+	Q: [
+		{ name: "迁安市", code: "130283", pinyin: "qiananshi130283"},
+		{ name: "潜江市", code: "429005", pinyin: "qianjiangshi429005"},
+		{ name: "潜山市", code: "340882", pinyin: "qianshanshi340882"},
+		{ name: "启东市", code: "320681", pinyin: "qidongshi320681"},
+		{ name: "青岛市", code: "370200", pinyin: "qingdaoshi370200"},
+		{ name: "青铜峡市", code: "640381", pinyin: "qingtongxiashi640381"},
+		{ name: "庆阳市", code: "621000", pinyin: "qingyangshi621000"},
+		{ name: "清远市", code: "441800", pinyin: "qingyuanshi441800"},
+		{ name: "清镇市", code: "520181", pinyin: "qingzhenshi520181"},
+		{ name: "青州市", code: "370781", pinyin: "qingzhoushi370781"},
+		{ name: "秦皇岛市", code: "130300", pinyin: "qinhuangdaoshi130300"},
+		{ name: "沁阳市", code: "410882", pinyin: "qinyangshi410882"},
+		{ name: "钦州市", code: "450700", pinyin: "qinzhoushi450700"},
+		{ name: "琼海市", code: "469002", pinyin: "qionghaishi469002"},
+		{ name: "齐齐哈尔市", code: "230200", pinyin: "qiqihaershi230200"},
+		{ name: "七台河市", code: "230900", pinyin: "qitaiheshi230900"},
+		{ name: "栖霞市", code: "370686", pinyin: "qixiashi370686"},
+		{ name: "泉州市", code: "350500", pinyin: "quanzhoushi350500"},
+		{ name: "曲阜市", code: "370881", pinyin: "qufushi370881"},
+		{ name: "曲靖市", code: "530300", pinyin: "qujingshi530300"},
+		{ name: "衢州市", code: "330800", pinyin: "quzhoushi330800"}
+	],
+	R: [
+		{ name: "仁怀市", code: "520382", pinyin: "renhuaishi520382"},
+		{ name: "任丘市", code: "130982", pinyin: "renqiushi130982"},
+		{ name: "日喀则市", code: "540200", pinyin: "rikazeshi540200"},
+		{ name: "日照市", code: "371100", pinyin: "rizhaoshi371100"},
+		{ name: "荣成市", code: "371082", pinyin: "rongchengshi371082"},
+		{ name: "如皋市", code: "320682", pinyin: "rugaoshi320682"},
+		{ name: "瑞安市", code: "330381", pinyin: "ruianshi330381"},
+		{ name: "瑞昌市", code: "360481", pinyin: "ruichangshi360481"},
+		{ name: "瑞金市", code: "360781", pinyin: "ruijinshi360781"},
+		{ name: "瑞丽市", code: "533102", pinyin: "ruilishi533102"},
+		{ name: "乳山市", code: "371083", pinyin: "rushanshi371083"},
+		{ name: "汝州市", code: "410482", pinyin: "ruzhoushi410482"}
+	],
+	S: [
+		{ name: "三河市", code: "131082", pinyin: "sanheshi131082"},
+		{ name: "三门峡市", code: "411200", pinyin: "sanmenxiashi411200"},
+		{ name: "三明市", code: "350400", pinyin: "sanmingshi350400"},
+		{ name: "三沙市", code: "460300", pinyin: "sanshashi460300"},
+		{ name: "三亚市", code: "460200", pinyin: "sanyashi460200"},
+		{ name: "沙河市", code: "130582", pinyin: "shaheshi130582"},
+		{ name: "上海市", code: "310000", pinyin: "shanghaishi310000"},
+		{ name: "商洛市", code: "611000", pinyin: "shangluoshi611000"},
+		{ name: "商丘市", code: "411400", pinyin: "shangqiushi411400"},
+		{ name: "上饶市", code: "361100", pinyin: "shangraoshi361100"},
+		{ name: "尚志市", code: "230183", pinyin: "shangzhishi230183"},
+		{ name: "山南市", code: "540500", pinyin: "shannanshi540500"},
+		{ name: "汕头市", code: "440500", pinyin: "shantoushi440500"},
+		{ name: "汕尾市", code: "441500", pinyin: "shanweishi441500"},
+		{ name: "邵东市", code: "430582", pinyin: "shaodongshi430582"},
+		{ name: "韶关市", code: "440200", pinyin: "shaoguanshi440200"},
+		{ name: "韶山市", code: "430382", pinyin: "shaoshanshi430382"},
+		{ name: "邵武市", code: "350781", pinyin: "shaowushi350781"},
+		{ name: "绍兴市", code: "330600", pinyin: "shaoxingshi330600"},
+		{ name: "邵阳市", code: "430500", pinyin: "shaoyangshi430500"},
+		{ name: "射洪市", code: "510981", pinyin: "shehongshi510981"},
+		{ name: "什邡市", code: "510682", pinyin: "shenfangshi510682"},
+		{ name: "嵊州市", code: "330683", pinyin: "shengzhoushi330683"},
+		{ name: "神木市", code: "610881", pinyin: "shenmushi610881"},
+		{ name: "沈阳市", code: "210100", pinyin: "shenyangshi210100"},
+		{ name: "深圳市", code: "440300", pinyin: "shenzhenshi440300"},
+		{ name: "深州市", code: "131182", pinyin: "shenzhoushi131182"},
+		{ name: "石河子市", code: "659001", pinyin: "shihezishi659001"},
+		{ name: "石家庄市", code: "130100", pinyin: "shijiazhuangshi130100"},
+		{ name: "石狮市", code: "350581", pinyin: "shishishi350581"},
+		{ name: "石首市", code: "421081", pinyin: "shishoushi421081"},
+		{ name: "十堰市", code: "420300", pinyin: "shiyanshi420300"},
+		{ name: "石嘴山市", code: "640200", pinyin: "shizuishanshi640200"},
+		{ name: "寿光市", code: "370783", pinyin: "shouguangshi370783"},
+		{ name: "双河市", code: "659007", pinyin: "shuangheshi659007"},
+		{ name: "双辽市", code: "220382", pinyin: "shuangliaoshi220382"},
+		{ name: "双鸭山市", code: "230500", pinyin: "shuangyashanshi230500"},
+		{ name: "水富市", code: "530681", pinyin: "shuifushi530681"},
+		{ name: "舒兰市", code: "220283", pinyin: "shulanshi220283"},
+		{ name: "朔州市", code: "140600", pinyin: "shuozhoushi140600"},
+		{ name: "四会市", code: "441284", pinyin: "sihuishi441284"},
+		{ name: "四平市", code: "220300", pinyin: "sipingshi220300"},
+		{ name: "松原市", code: "220700", pinyin: "songyuanshi220700"},
+		{ name: "松滋市", code: "421087", pinyin: "songzishi421087"},
+		{ name: "绥芬河市", code: "231081", pinyin: "suifenheshi231081"},
+		{ name: "绥化市", code: "231200", pinyin: "suihuashi231200"},
+		{ name: "遂宁市", code: "510900", pinyin: "suiningshi510900"},
+		{ name: "随州市", code: "421300", pinyin: "suizhoushi421300"},
+		{ name: "苏州市", code: "320500", pinyin: "suzhoushi320500"}
+	],
+	T: [
+		{ name: "塔城市", code: "654201", pinyin: "tachengshi654201"},
+		{ name: "泰安市", code: "370900", pinyin: "taianshi370900"},
+		{ name: "太仓市", code: "320585", pinyin: "taicangshi320585"},
+		{ name: "台山市", code: "440781", pinyin: "taishanshi440781"},
+		{ name: "泰兴市", code: "321283", pinyin: "taixingshi321283"},
+		{ name: "太原市", code: "140100", pinyin: "taiyuanshi140100"},
+		{ name: "泰州市", code: "321200", pinyin: "taizhoushi321200"},
+		{ name: "台州市", code: "331000", pinyin: "taizhoushi331000"},
+		{ name: "唐山市", code: "130200", pinyin: "tangshanshi130200"},
+		{ name: "洮南市", code: "220881", pinyin: "taonanshi220881"},
+		{ name: "腾冲市", code: "530581", pinyin: "tengchongshi530581"},
+		{ name: "滕州市", code: "370481", pinyin: "tengzhoushi370481"},
+		{ name: "天长市", code: "341181", pinyin: "tianchangshi341181"},
+		{ name: "天津市", code: "120000", pinyin: "tianjinshi120000"},
+		{ name: "天门市", code: "429006", pinyin: "tianmenshi429006"},
+		{ name: "天水市", code: "620500", pinyin: "tianshuishi620500"},
+		{ name: "调兵山市", code: "211281", pinyin: "tiaobingshanshi211281"},
+		{ name: "铁岭市", code: "211200", pinyin: "tielingshi211200"},
+		{ name: "铁力市", code: "230781", pinyin: "tielishi230781"},
+		{ name: "铁门关市", code: "659006", pinyin: "tiemenguanshi659006"},
+		{ name: "桐城市", code: "340881", pinyin: "tongchengshi340881"},
+		{ name: "铜川市", code: "610200", pinyin: "tongchuanshi610200"},
+		{ name: "通化市", code: "220500", pinyin: "tonghuashi220500"},
+		{ name: "同江市", code: "230881", pinyin: "tongjiangshi230881"},
+		{ name: "通辽市", code: "150500", pinyin: "tongliaoshi150500"},
+		{ name: "铜陵市", code: "340700", pinyin: "tonglingshi340700"},
+		{ name: "铜仁市", code: "520600", pinyin: "tongrenshi520600"},
+		{ name: "桐乡市", code: "330483", pinyin: "tongxiangshi330483"},
+		{ name: "吐鲁番市", code: "650400", pinyin: "tulufanshi650400"},
+		{ name: "图们市", code: "222402", pinyin: "tumenshi222402"},
+		{ name: "图木舒克市", code: "659003", pinyin: "tumushukeshi659003"}
+	],
+	W: [
+		{ name: "瓦房店市", code: "210281", pinyin: "wafangdianshi210281"},
+		{ name: "万宁市", code: "469006", pinyin: "wanningshi469006"},
+		{ name: "万源市", code: "511781", pinyin: "wanyuanshi511781"},
+		{ name: "潍坊市", code: "370700", pinyin: "weifangshi370700"},
+		{ name: "威海市", code: "371000", pinyin: "weihaishi371000"},
+		{ name: "卫辉市", code: "410781", pinyin: "weihuishi410781"},
+		{ name: "渭南市", code: "610500", pinyin: "weinanshi610500"},
+		{ name: "文昌市", code: "469005", pinyin: "wenchangshi469005"},
+		{ name: "温岭市", code: "331081", pinyin: "wenlingshi331081"},
+		{ name: "文山市", code: "532601", pinyin: "wenshanshi532601"},
+		{ name: "温州市", code: "330300", pinyin: "wenzhoushi330300"},
+		{ name: "武安市", code: "130481", pinyin: "wuanshi130481"},
+		{ name: "五常市", code: "230184", pinyin: "wuchangshi230184"},
+		{ name: "吴川市", code: "440883", pinyin: "wuchuanshi440883"},
+		{ name: "五大连池市", code: "231182", pinyin: "wudalianchishi231182"},
+		{ name: "舞钢市", code: "410481", pinyin: "wugangshi410481"},
+		{ name: "武冈市", code: "430581", pinyin: "wugangshi430581"},
+		{ name: "乌海市", code: "150300", pinyin: "wuhaishi150300"},
+		{ name: "武汉市", code: "420100", pinyin: "wuhanshi420100"},
+		{ name: "芜湖市", code: "340200", pinyin: "wuhushi340200"},
+		{ name: "五家渠市", code: "659004", pinyin: "wujiaqushi659004"},
+		{ name: "乌兰察布市", code: "150900", pinyin: "wulanchabushi150900"},
+		{ name: "乌兰浩特市", code: "152201", pinyin: "wulanhaoteshi152201"},
+		{ name: "乌鲁木齐市", code: "650100", pinyin: "wulumuqishi650100"},
+		{ name: "乌苏市", code: "654202", pinyin: "wusushi654202"},
+		{ name: "无为市", code: "340281", pinyin: "wuweishi340281"},
+		{ name: "武威市", code: "620600", pinyin: "wuweishi620600"},
+		{ name: "无锡市", code: "320200", pinyin: "wuxishi320200"},
+		{ name: "武穴市", code: "421182", pinyin: "wuxueshi421182"},
+		{ name: "武夷山市", code: "350782", pinyin: "wuyishanshi350782"},
+		{ name: "五指山市", code: "469001", pinyin: "wuzhishanshi469001"},
+		{ name: "吴忠市", code: "640300", pinyin: "wuzhongshi640300"},
+		{ name: "梧州市", code: "450400", pinyin: "wuzhoushi450400"}
+	],
+	X: [
+		{ name: "厦门市", code: "350200", pinyin: "xiamenshi350200"},
+		{ name: "项城市", code: "411681", pinyin: "xiangchengshi411681"},
+		{ name: "香格里拉市", code: "533401", pinyin: "xianggelilashi533401"},
+		{ name: "湘潭市", code: "430300", pinyin: "xiangtanshi430300"},
+		{ name: "湘乡市", code: "430381", pinyin: "xiangxiangshi430381"},
+		{ name: "襄阳市", code: "420600", pinyin: "xiangyangshi420600"},
+		{ name: "咸宁市", code: "421200", pinyin: "xianningshi421200"},
+		{ name: "西安市", code: "610100", pinyin: "xianshi610100"},
+		{ name: "仙桃市", code: "429004", pinyin: "xiantaoshi429004"},
+		{ name: "咸阳市", code: "610400", pinyin: "xianyangshi610400"},
+		{ name: "孝感市", code: "420900", pinyin: "xiaoganshi420900"},
+		{ name: "孝义市", code: "141181", pinyin: "xiaoyishi141181"},
+		{ name: "西昌市", code: "513401", pinyin: "xichangshi513401"},
+		{ name: "锡林浩特市", code: "152502", pinyin: "xilinhaoteshi152502"},
+		{ name: "兴城市", code: "211481", pinyin: "xingchengshi211481"},
+		{ name: "兴化市", code: "321281", pinyin: "xinghuashi321281"},
+		{ name: "兴宁市", code: "441481", pinyin: "xingningshi441481"},
+		{ name: "兴平市", code: "610481", pinyin: "xingpingshi610481"},
+		{ name: "兴仁市", code: "522302", pinyin: "xingrenshi522302"},
+		{ name: "邢台市", code: "130500", pinyin: "xingtaishi130500"},
+		{ name: "兴义市", code: "522301", pinyin: "xingyishi522301"},
+		{ name: "西宁市", code: "630100", pinyin: "xiningshi630100"},
+		{ name: "辛集市", code: "130181", pinyin: "xinjishi130181"},
+		{ name: "新乐市", code: "130184", pinyin: "xinleshi130184"},
+		{ name: "新民市", code: "210181", pinyin: "xinminshi210181"},
+		{ name: "新密市", code: "410183", pinyin: "xinmishi410183"},
+		{ name: "新泰市", code: "370982", pinyin: "xintaishi370982"},
+		{ name: "新乡市", code: "410700", pinyin: "xinxiangshi410700"},
+		{ name: "信阳市", code: "411500", pinyin: "xinyangshi411500"},
+		{ name: "新沂市", code: "320381", pinyin: "xinyishi320381"},
+		{ name: "信宜市", code: "440983", pinyin: "xinyishi440983"},
+		{ name: "新余市", code: "360500", pinyin: "xinyushi360500"},
+		{ name: "新郑市", code: "410184", pinyin: "xinzhengshi410184"},
+		{ name: "忻州市", code: "140900", pinyin: "xinzhoushi140900"},
+		{ name: "宿迁市", code: "321300", pinyin: "xiuqianshi321300"},
+		{ name: "宿州市", code: "341300", pinyin: "xiuzhoushi341300"},
+		{ name: "宣城市", code: "341800", pinyin: "xuanchengshi341800"},
+		{ name: "宣威市", code: "530381", pinyin: "xuanweishi530381"},
+		{ name: "许昌市", code: "411000", pinyin: "xuchangshi411000"},
+		{ name: "徐州市", code: "320300", pinyin: "xuzhoushi320300"},
+		{ name: "香港特别行政区", coder:"810000", pinyin:"xianggang81000"}
+	],
+	Y: [
+		{ name: "雅安市", code: "511800", pinyin: "yaanshi511800"},
+		{ name: "牙克石市", code: "150782", pinyin: "yakeshishi150782"},
+		{ name: "延安市", code: "610600", pinyin: "yananshi610600"},
+		{ name: "盐城市", code: "320900", pinyin: "yanchengshi320900"},
+		{ name: "阳春市", code: "441781", pinyin: "yangchunshi441781"},
+		{ name: "阳江市", code: "441700", pinyin: "yangjiangshi441700"},
+		{ name: "阳泉市", code: "140300", pinyin: "yangquanshi140300"},
+		{ name: "扬中市", code: "321182", pinyin: "yangzhongshi321182"},
+		{ name: "扬州市", code: "321000", pinyin: "yangzhoushi321000"},
+		{ name: "延吉市", code: "222401", pinyin: "yanjishi222401"},
+		{ name: "偃师市", code: "410381", pinyin: "yanshishi410381"},
+		{ name: "烟台市", code: "370600", pinyin: "yantaishi370600"},
+		{ name: "宜宾市", code: "511500", pinyin: "yibinshi511500"},
+		{ name: "宜昌市", code: "420500", pinyin: "yichangshi420500"},
+		{ name: "宜城市", code: "420684", pinyin: "yichengshi420684"},
+		{ name: "伊春市", code: "230700", pinyin: "yichunshi230700"},
+		{ name: "宜春市", code: "360900", pinyin: "yichunshi360900"},
+		{ name: "宜都市", code: "420581", pinyin: "yidoushi420581"},
+		{ name: "义马市", code: "411281", pinyin: "yimashi411281"},
+		{ name: "银川市", code: "640100", pinyin: "yinchuanshi640100"},
+		{ name: "应城市", code: "420981", pinyin: "yingchengshi420981"},
+		{ name: "英德市", code: "441881", pinyin: "yingdeshi441881"},
+		{ name: "营口市", code: "210800", pinyin: "yingkoushi210800"},
+		{ name: "鹰潭市", code: "360600", pinyin: "yingtanshi360600"},
+		{ name: "荥阳市", code: "410182", pinyin: "yingyangshi410182"},
+		{ name: "伊宁市", code: "654002", pinyin: "yiningshi654002"},
+		{ name: "义乌市", code: "330782", pinyin: "yiwushi330782"},
+		{ name: "宜兴市", code: "320282", pinyin: "yixingshi320282"},
+		{ name: "益阳市", code: "430900", pinyin: "yiyangshi430900"},
+		{ name: "仪征市", code: "321081", pinyin: "yizhengshi321081"},
+		{ name: "永安市", code: "350481", pinyin: "yonganshi350481"},
+		{ name: "永城市", code: "411481", pinyin: "yongchengshi411481"},
+		{ name: "永济市", code: "140881", pinyin: "yongjishi140881"},
+		{ name: "永康市", code: "330784", pinyin: "yongkangshi330784"},
+		{ name: "永州市", code: "431100", pinyin: "yongzhoushi431100"},
+		{ name: "沅江市", code: "430981", pinyin: "yuanjiangshi430981"},
+		{ name: "原平市", code: "140981", pinyin: "yuanpingshi140981"},
+		{ name: "禹城市", code: "371482", pinyin: "yuchengshi371482"},
+		{ name: "岳阳市", code: "430600", pinyin: "yueyangshi430600"},
+		{ name: "玉环市", code: "331083", pinyin: "yuhuanshi331083"},
+		{ name: "玉林市", code: "450900", pinyin: "yulinshi450900"},
+		{ name: "榆林市", code: "610800", pinyin: "yulinshi610800"},
+		{ name: "玉门市", code: "620981", pinyin: "yumenshi620981"},
+		{ name: "运城市", code: "140800", pinyin: "yunchengshi140800"},
+		{ name: "云浮市", code: "445300", pinyin: "yunfushi445300"},
+		{ name: "榆树市", code: "220182", pinyin: "yushushi220182"},
+		{ name: "玉树市", code: "632701", pinyin: "yushushi632701"},
+		{ name: "玉溪市", code: "530400", pinyin: "yuxishi530400"},
+		{ name: "余姚市", code: "330281", pinyin: "yuyaoshi330281"},
+		{ name: "禹州市", code: "411081", pinyin: "yuzhoushi411081"}
+	],
+	Z: [
+		{ name: "枣阳市", code: "420683", pinyin: "zaoyangshi420683"},
+		{ name: "枣庄市", code: "370400", pinyin: "zaozhuangshi370400"},
+		{ name: "扎兰屯市", code: "150783", pinyin: "zhalantunshi150783"},
+		{ name: "张家港市", code: "320582", pinyin: "zhangjiagangshi320582"},
+		{ name: "张家界市", code: "430800", pinyin: "zhangjiajieshi430800"},
+		{ name: "张家口市", code: "130700", pinyin: "zhangjiakoushi130700"},
+		{ name: "漳平市", code: "350881", pinyin: "zhangpingshi350881"},
+		{ name: "樟树市", code: "360982", pinyin: "zhangshushi360982"},
+		{ name: "张掖市", code: "620700", pinyin: "zhangyeshi620700"},
+		{ name: "漳州市", code: "350600", pinyin: "zhangzhoushi350600"},
+		{ name: "湛江市", code: "440800", pinyin: "zhanjiangshi440800"},
+		{ name: "肇东市", code: "231282", pinyin: "zhaodongshi231282"},
+		{ name: "肇庆市", code: "441200", pinyin: "zhaoqingshi441200"},
+		{ name: "昭通市", code: "530600", pinyin: "zhaotongshi530600"},
+		{ name: "招远市", code: "370685", pinyin: "zhaoyuanshi370685"},
+		{ name: "郑州市", code: "410100", pinyin: "zhengzhoushi410100"},
+		{ name: "镇江市", code: "321100", pinyin: "zhenjiangshi321100"},
+		{ name: "枝江市", code: "420583", pinyin: "zhijiangshi420583"},
+		{ name: "重庆市", code: "500000", pinyin: "chongqingshi500000"},
+		{ name: "中山市", code: "442000", pinyin: "zhongshanshi442000"},
+		{ name: "中卫市", code: "640500", pinyin: "zhongweishi640500"},
+		{ name: "钟祥市", code: "420881", pinyin: "zhongxiangshi420881"},
+		{ name: "周口市", code: "411600", pinyin: "zhoukoushi411600"},
+		{ name: "舟山市", code: "330900", pinyin: "zhoushanshi330900"},
+		{ name: "庄河市", code: "210283", pinyin: "zhuangheshi210283"},
+		{ name: "诸城市", code: "370782", pinyin: "zhuchengshi370782"},
+		{ name: "珠海市", code: "440400", pinyin: "zhuhaishi440400"},
+		{ name: "诸暨市", code: "330681", pinyin: "zhujishi330681"},
+		{ name: "驻马店市", code: "411700", pinyin: "zhumadianshi411700"},
+		{ name: "涿州市", code: "130681", pinyin: "zhuozhoushi130681"},
+		{ name: "株洲市", code: "430200", pinyin: "zhuzhoushi430200"},
+		{ name: "淄博市", code: "370300", pinyin: "ziboshi370300"},
+		{ name: "子长市", code: "610681", pinyin: "zichangshi610681"},
+		{ name: "自贡市", code: "510300", pinyin: "zigongshi510300"},
+		{ name: "资兴市", code: "431081", pinyin: "zixingshi431081"},
+		{ name: "资阳市", code: "512000", pinyin: "ziyangshi512000"},
+		{ name: "邹城市", code: "370883", pinyin: "zouchengshi370883"},
+		{ name: "邹平市", code: "371681", pinyin: "zoupingshi371681"},
+		{ name: "遵化市", code: "130281", pinyin: "zunhuashi130281"},
+		{ name: "遵义市", code: "520300", pinyin: "zunyishi520300"}
+	]
+};
+var AZ = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'W', 'X', 'Y', 'Z'];
+module.exports = {
+	cityData: cityData,
+	AZ : AZ
+} 

+ 106 - 0
GraceUI5/demoData/article.js

@@ -0,0 +1,106 @@
+module.exports  = {
+	articleList : [
+		{
+			id         : 1, 
+			title      : '新闻标题文本',
+			author     : '某某作者',
+			createTime : '2020.09.05',
+			imgs       : ['https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/18.png'],
+			views      : 98052
+		},
+		{
+			id         : 2, 
+			title      : '新闻标题文本长文本新闻标题文本长文本新闻标题文本长文本',
+			author     : '某某作者',
+			createTime : '2020.09.05',
+			imgs       : [
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/1.png',
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/2.png'
+			],
+			views      : 8052
+		},
+		{
+			id         : 3, 
+			title      : '新闻标题文本,新闻标题文本',
+			author     : '某某作者',
+			createTime : '2020.09.05',
+			imgs       : [
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/17.png',
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/17.png',
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/19.png'
+			],
+			views      : 62012
+		},
+		{
+			id         : 4, 
+			title      : '新闻标题文本新闻标题文本新闻标题文本',
+			author     : '某某作者',
+			createTime : '2020.09.05',
+			imgs       : [],
+			views      : 680
+		},
+		{
+			id         : 5, 
+			title      : '新闻标题文本新闻标题文本',
+			author     : '某某作者',
+			createTime : '2020.09.05',
+			imgs       : [
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/17.png'
+			],
+			views      : 5201
+		},
+		{
+			id         : 6, 
+			title      : '新闻标题文本新闻标题文本新闻标题文本新闻标题文本',
+			author     : '某某作者',
+			createTime : '2020.09.05',
+			imgs       : [
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/21.png',
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/26.png',
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/27.png'
+			],
+			views      : 5200
+		},
+		{
+			id         : 7, 
+			title      : '新闻标题文本新闻标题文本',
+			author     : '某某作者',
+			createTime : '2020.09.05',
+			imgs       : [
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/7.png',
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/8.png'
+			],
+			views      : 36801
+		},
+		{
+			id         : 8, 
+			title      : '新闻标题文本新闻标题文本新闻标题文本',
+			author     : '某某作者',
+			createTime : '2020.09.05',
+			imgs       : [],
+			views      : 58660
+		},
+		{
+			id         : 9, 
+			title      : '新闻标题文本,新闻标题文本',
+			author     : '某某作者',
+			createTime : '2020.09.05',
+			imgs       : [
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/19.png'
+			],
+			views      : 98665
+		},
+		{
+			id         : 10, 
+			title      : '新闻标题文本新闻标题文本',
+			author     : '某某作者',
+			createTime : '2020.09.05',
+			imgs       : [
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',
+				'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png'
+			],
+			views      : 13205
+		}
+	]
+} 

+ 100 - 0
GraceUI5/demoData/cateChange.js

@@ -0,0 +1,100 @@
+module.exports  = {
+	products : [
+		{ cateid: 1, name: '分类 · 01' , products:[
+			{id:11,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:12,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:13,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:14,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:15,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:16,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 2, name: '分类 · 02', products:[
+			{id:21,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:22,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:23,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:24,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:25,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:26,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 3, name: '分类 · 03' ,products:[
+			{id:31,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:32,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:33,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:34,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:35,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:36,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 4, name: '分类 · 04' ,products:[
+			{id:41,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:42,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:43,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:44,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:45,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:46,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 5, name: '分类 · 05' ,products:[
+			{id:51,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:52,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:53,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:54,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:55,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:56,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 6, name: '分类 · 06' ,products:[
+			{id:61,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:62,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:63,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:64,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:65,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:66,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 7, name: '分类 · 07' ,products:[
+			{id:71,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:72,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:73,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:74,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:75,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:76,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 8, name: '分类 · 08' ,products:[
+			{id:81,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:82,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:83,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:84,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:85,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:86,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 9, name: '分类 · 09' ,products:[
+			{id:91,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:92,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:93,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:94,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:95,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:96,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 10, name: '分类 · 10' ,products:[
+			{id:101,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:102,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:103,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:104,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:105,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:106,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 11, name: '分类 · 11' ,products:[
+			{id:111,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:112,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:113,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:114,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:115,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:116,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]},
+		{ cateid: 12, name: '分类 · 12' ,products:[
+			{id:121,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',price:12.88},
+			{id:122,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/29.png',price:12.88},
+			{id:123,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/30.png',price:12.88},
+			{id:124,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/31.png',price:12.88},
+			{id:125,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/34.png',price:12.88},
+			{id:126,name:"标题",img:'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/37.png',price:12.88}
+		]}
+	]
+}

+ 65 - 0
GraceUI5/demoData/data.js

@@ -0,0 +1,65 @@
+var products = [
+	{
+		"img": "https://img.alicdn.com/tfs/TB1Q2Org4rI8KJjy0FpXXb5hVXa-468-1236.jpg",
+		"title": "精品女装 限时出售",
+		"price": "¥99.88",
+		"tip": "HOT"
+	},
+	{
+		"img": "https://img.alicdn.com/bao/uploaded/i1/2146742267/O1CN011ScKLXPJaX9UQyq_!!0-item_pic.jpg",
+		"title": "女装特价",
+		"price": "¥16.99",
+		"tip": "名牌"
+	},
+	{
+		"img": "https://img.alicdn.com/tps/i4/TB1q42TjMTqK1RjSZPhSutfOFXa.jpg",
+		"title": "千元手机爆款",
+		"price": "¥999.99",
+		"tip": "推荐"
+	},
+	{
+		"img": "https://img.alicdn.com/tps/i4/TB1bhT6kr2pK1RjSZFswu1NlXXa.png",
+		"title": "进口儿童座椅",
+		"price": "¥698.99",
+		"tip": "进口"
+	},
+	{
+		"img": "https://img.alicdn.com/bao/uploaded/i1/2787417447/O1CN0124sly8iXb80L4OS_!!0-item_pic.jpg",
+		"title": "品牌T恤",
+		"price": "¥22.99",
+		"tip": "nike"
+	},
+	{
+		"img": "https://img.alicdn.com/tfs/TB13FixCeuSBuNjy1XcXXcYjFXa-468-1236.jpg",
+		"title": "好吃坚果",
+		"price": "¥55.99",
+		"tip": "吃货"
+	},
+	{
+		"img": "https://img.alicdn.com/bao/uploaded/i2/452325706/O1CN011s1OQAszoPWK8Rt_!!0-item_pic.jpg",
+		"title": "进口化妆品特价",
+		"price": "¥22.99",
+		"tip": "热卖"
+	},
+	{
+		"img": "https://img.alicdn.com/tps/i4/TB1bhT6kr2pK1RjSZFswu1NlXXa.png",
+		"title": "进口儿童座椅",
+		"price": "¥698.99",
+		"tip": "进口"
+	}
+];
+
+var getArrRandomly = function (arr) {
+	var len = arr.length;
+	for (var i = 0; i < len; i++) {
+		var randomIndex = Math.floor(Math.random() * (len - i));
+		var itemAtIndex = arr[randomIndex];
+		arr[randomIndex] = arr[i];
+		arr[i] = itemAtIndex;
+	}
+	return arr;
+}
+module.exports  = {
+	products : products,
+	getArrRandomly : getArrRandomly
+} 

+ 70 - 0
GraceUI5/demoData/immessages.js

@@ -0,0 +1,70 @@
+var msgs = [
+	{
+		group         : 'group1',
+		uindex        : '10001',
+		uname         : '李晓燕',
+		contentType   : 'txt',
+		uface         : 'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/15.png',
+		content       : '昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否,知否?应是绿肥红瘦。',
+		date          : '2021.01.11 18:00'
+	},
+	{
+		group         : 'group1',
+		uindex        : '10000',
+		uname         : '老兵张嘎',
+		contentType   : 'txt',
+		uface         : 'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/13.png',
+		content       : '鼓掌 ...',
+		date          : '2021.01.11 19:20'
+	},
+	{
+		group         : 'group1',
+		uindex        : '10001',
+		uname         : '李晓燕',
+		contentType   : 'img',
+		uface         : 'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/15.png',
+		content       : 'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/25.png',
+		date          : '2021.01.11 19:22'
+	},
+	{
+		group         : 'group1',
+		uindex        : '10001',
+		uname         : 'system',
+		contentType   : 'system',
+		uface         : '',
+		msg       : '系统消息,GraceUI 欢迎您!',
+		date          : '2021.01.11 19:22'
+	},
+	{
+		group         : 'group1',
+		uindex        : '10000',
+		uname         : '老兵张嘎',
+		contentType   : 'img',
+		uface         : 'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/13.png',
+		content       : 'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/28.png',
+		date          : '2021.01.11 19:25'
+	},
+	{
+		group         : 'group1',
+		uindex        : '10001',
+		uname         : '李晓燕',
+		contentType   : 'voice',
+		uface         : 'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/15.png',
+		content       : 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-hello-uniapp/2cc220e0-c27a-11ea-9dfb-6da8e309e0d8.mp3',
+		length        : 23,
+		date          : '一小时前'
+	},
+	{
+		group         : 'group1',
+		uindex        : '10000',
+		uname         : '老兵张嘎',
+		contentType   : 'voice',
+		uface         : 'https://cmsuse.oss-cn-beijing.aliyuncs.com/g5/13.png',
+		content       : 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-hello-uniapp/2cc220e0-c27a-11ea-9dfb-6da8e309e0d8.mp3',
+		length        : 150,
+		date          : '15 分钟前'
+	}
+];
+module.exports  = {
+	msgs : msgs
+} 

File diff suppressed because it is too large
+ 1265 - 0
GraceUI5/js/WeCropper.js


+ 410 - 0
GraceUI5/js/barcode.js

@@ -0,0 +1,410 @@
+/** 
+// https://github.com/alsey/wxbarcode
+// 最后一位显示 _ 问题
+// https://github.com/alsey/wxbarcode/issues/2
+// //ok some type of shift is nessecary if (shifter != -1) { result.push(shifter); result.push(codeValue(chr1));//把这里的chr2改成chr1即可。 }
+**/
+ 
+!(function(){
+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(chr1));
+        }
+        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
+]
+})();

+ 86 - 0
GraceUI5/js/checkIdCard.js

@@ -0,0 +1,86 @@
+function checkIdcard(idcard) {
+	var Errors = new Array(
+		"ok",
+		"身份证号码位数错误",
+		"身份证号码出生日期错误",
+		"身份证号码校验错误",
+		"身份证地区错误"
+	);
+	var area = {
+		11: "北京", 12: "天津", 13: "河北", 14: "山西", 
+		15: "内蒙古", 21: "辽宁", 22: "吉林", 23: "黑龙江", 
+		31: "上海", 32: "江苏", 33: "浙江", 34: "安徽", 
+		35: "福建", 36: "江西", 37: "山东", 41: "河南", 
+		42: "湖北", 43: "湖南", 44: "广东", 45: "广西", 
+		46: "海南", 50: "重庆", 51: "四川", 52: "贵州", 
+		53: "云南", 54: "西藏", 61: "陕西", 62: "甘肃", 
+		63: "青海", 64: "宁夏", 65: "新疆", 71: "台湾", 
+		81: "香港", 82: "澳门", 91: "国外",
+	}
+
+	var idcard, Y, JYM, ereg;
+	var S, M;
+	var idcard_array = new Array();
+	idcard_array     = idcard.split("");
+	//地区检验
+	if (area[parseInt(idcard.substr(0, 2))] == null) return Errors[4];
+	//身份号码位数及格式检验
+	switch (idcard.length) {
+		case 15:
+			if ((parseInt(idcard.substr(6, 2)) + 1900) % 4 == 0 || ((parseInt(idcard.substr(6, 2)) + 1900) % 100 == 0 && (parseInt(idcard.substr(6, 2)) + 1900) % 4 == 0)) {
+				ereg = /^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$/; //测试出生日期的合法性
+			} else {
+				ereg = /^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$/; //测试出生日期的合法性
+			}
+			if (ereg.test(idcard)) return Errors[0];
+			else return Errors[2];
+		break;
+		case 18:
+			//18位身份号码检测
+			//出生日期的合法性检查
+			//闰年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))
+			//平年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))
+			if (
+			parseInt(idcard.substr(6, 4)) % 4 == 0 
+			|| 
+			(
+				parseInt(idcard.substr(6, 4)) % 100 == 0 
+				&& 
+				parseInt(idcard.substr(6, 4)) % 4 == 0
+				)
+			){
+				ereg = /^[1-9][0-9]{5}[1-2]+[0-9]{3}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$/; //闰年出生日期的合法性正则表达式
+			} else {
+				ereg = /^[1-9][0-9]{5}[1-2]+[0-9]{3}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$/; //平年出生日期的合法性正则表达式
+			}
+			// 测试出生日期的合法性
+            if (ereg.test(idcard)) {
+				//计算校验位
+				S = (parseInt(idcard_array[0]) + parseInt(idcard_array[10])) * 7
+				+ (parseInt(idcard_array[1]) + parseInt(idcard_array[11])) * 9
+				+ (parseInt(idcard_array[2]) + parseInt(idcard_array[12])) * 10
+				+ (parseInt(idcard_array[3]) + parseInt(idcard_array[13])) * 5
+				+ (parseInt(idcard_array[4]) + parseInt(idcard_array[14])) * 8
+				+ (parseInt(idcard_array[5]) + parseInt(idcard_array[15])) * 4
+				+ (parseInt(idcard_array[6]) + parseInt(idcard_array[16])) * 2
+				+ parseInt(idcard_array[7]) * 1
+				+ parseInt(idcard_array[8]) * 6
+				+ parseInt(idcard_array[9]) * 3;
+				Y = S % 11;
+				M = "F";
+				JYM = "10X98765432";
+				M = JYM.substr(Y, 1); //判断校验位
+				if (M == idcard_array[17]) return Errors[0]; //检测ID的校验位
+				else return Errors[3];
+			}else {
+				return Errors[2];
+			}
+        break;
+        default:
+			return Errors[1];
+			break;
+    }
+}
+export default{
+	checkIdcard : checkIdcard
+}

+ 308 - 0
GraceUI5/js/checker.js

@@ -0,0 +1,308 @@
+
+import idCardChecker from './checkIdCard.js';
+module.exports = {
+	error : '',
+	check : function (dataBeCheck, rule){
+		dataBeCheck = JSON.stringify(dataBeCheck);
+		var data = JSON.parse(dataBeCheck);
+		for(var i = 0; i < rule.length; i++){
+			if (!rule[i].checkType){ return true;}
+			if (!rule[i].name){return true;}
+			if (!rule[i].errorMsg) {return true;}
+			if (
+				typeof(data[rule[i].name]) == 'undefined'
+				 || 
+				 data[rule[i].name] === ''
+			){
+				this.error = rule[i].errorMsg; 
+				return false;
+			}
+			// 检查前去除内容的空格及换行
+			if(typeof(data[rule[i].name]) == 'string'){
+				data[rule[i].name] = data[rule[i].name].replace(/\s/g,"");
+			}
+			switch (rule[i].checkType){
+				case 'string':
+					var reg = new RegExp('^.{' + rule[i].checkRule + '}$');
+					if(!reg.test(data[rule[i].name])) {
+						this.error = rule[i].errorMsg; return false;
+					}
+				break;
+				case 'contain':
+					var cData = data[rule[i].name]+'';
+					if(cData.indexOf(rule[i].checkRule) == -1){
+						this.error = rule[i].errorMsg;
+						return false;
+					} 
+				break;
+				case 'notContain':
+					var cData = data[rule[i].name]+'';
+					if(cData.indexOf(rule[i].checkRule) != -1){
+						this.error = rule[i].errorMsg;
+						return false;
+					} 
+				break;
+				case 'inArray':
+					if(typeof(rule[i].checkRule) != 'object'){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+					var resInArray = rule[i].checkRule.find(
+						(val)=>{
+							if(val == data[rule[i].name]){
+								return true;
+							}
+						}
+					);
+					if(!resInArray){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'notInArray':
+					if(typeof(rule[i].checkRule) != 'object'){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+					var resInArray = rule[i].checkRule.find(
+						(val)=>{
+							if(val == data[rule[i].name]){
+								return true;
+							}
+						}
+					);
+					if(resInArray){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				
+				case 'int':
+					var ruleArr = rule[i].checkRule.split(',');
+					if(rule.length < 2){
+						ruleArr[0] = Number(ruleArr[0]) - 1;
+						ruleArr[1] = '';
+					}else{
+						ruleArr[0] = Number(ruleArr[0]) - 1;
+						ruleArr[1] = Number(ruleArr[1]) - 1;
+					}
+					var reg = new RegExp('^-?\\d{' + ruleArr[0] + ',' + ruleArr[1] + '}$');
+					if(!reg.test(data[rule[i].name])) {
+						this.error = rule[i].errorMsg; 
+						return false;
+					}
+					break;
+				break;
+				case 'between':
+					if (!this.isNumber(data[rule[i].name])){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (
+					data[rule[i].name] > minMax[1]
+					 || 
+					data[rule[i].name] < minMax[0])
+					{
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'intBetween':
+				var reg = /^-?\d+$/;
+					if (!reg.test(data[rule[i].name])) {
+						this.error = rule[i].errorMsg; 
+						return false; 
+					}
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (
+					data[rule[i].name] > minMax[1]
+					 || 
+					data[rule[i].name] < minMax[0]) {
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'betweenD':
+					var reg = /^-?\d+$/;
+					if (!reg.test(data[rule[i].name])){
+						this.error = rule[i].errorMsg; 
+						return false; 
+					}
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (
+					data[rule[i].name] > minMax[1]
+					 || 
+					data[rule[i].name] < minMax[0]){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'doubleBetween':
+					var reg = /^-?\d?.+\d+$/;
+					if (!reg.test(data[rule[i].name])){
+						this.error = rule[i].errorMsg; 
+						return false;
+					}
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (
+					data[rule[i].name] > minMax[1]
+					 || 
+					data[rule[i].name] < minMax[0]){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'betweenF': 
+					var reg = /^-?\d?.+\d+$/;
+					if (!reg.test(data[rule[i].name])){
+						this.error = rule[i].errorMsg; 
+						return false;
+					}
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (
+					data[rule[i].name] > minMax[1]
+					 || 
+					data[rule[i].name] < minMax[0]) {
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'doubleLength' : 
+					var reg = new RegExp('^-?\\d+.\\d{' + rule[i].checkRule + '}$');
+					if (!reg.test(data[rule[i].name])){
+						this.error = rule[i].errorMsg; 
+						return false;
+					}
+				break;
+				case 'gt':
+					if(data[rule[i].name] <= rule[i].checkRule){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'gtAndSame':
+					if(data[rule[i].name] < rule[i].checkRule){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'lt':
+					if(data[rule[i].name] >= rule[i].checkRule){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'ltAndSame':
+					if(data[rule[i].name] > rule[i].checkRule){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'same':
+					if(data[rule[i].name] != rule[i].checkRule){
+						this.error = rule[i].errorMsg; 
+						return false;
+					}
+				break;
+				case 'notSame':
+					if(data[rule[i].name] == rule[i].checkRule){
+						this.error = rule[i].errorMsg; return false;
+					}
+				break;
+				case 'notsame':
+					if(data[rule[i].name] == rule[i].checkRule){
+						this.error = rule[i].errorMsg; return false;
+					}
+				break;
+				case 'email':
+					var reg = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
+					if (!reg.test(data[rule[i].name])){
+						this.error = rule[i].errorMsg; 
+						return false; 
+					}
+				break;
+				case 'phoneno':
+					var reg = /^1[0-9]{10,10}$/;
+					if (!reg.test(data[rule[i].name])){
+						this.error = rule[i].errorMsg; 
+						return false; 
+					}
+				break;
+				case 'phone':
+					var reg = /^1[0-9]{10,10}$/;
+					if (!reg.test(data[rule[i].name])){
+						this.error = rule[i].errorMsg; 
+						return false; 
+					}
+				break;
+				case 'zipcode':
+					var reg = /^[0-9]{6}$/;
+					if (!reg.test(data[rule[i].name])){
+						this.error = rule[i].errorMsg; 
+						return false;
+					}
+				break;
+				case 'reg':
+					var reg = new RegExp(rule[i].checkRule);
+					if (!reg.test(data[rule[i].name])) {
+						this.error = rule[i].errorMsg; 
+						return false; 
+					}
+				break;
+				case 'in': 
+					if(rule[i].checkRule.indexOf(data[rule[i].name]) == -1){
+						this.error = rule[i].errorMsg; return false;
+					}
+				break;
+				case 'notnull':
+					if(
+					data[rule[i].name] == null || data[rule[i].name].length < 1
+					){this.error = rule[i].errorMsg; return false;}
+				break;
+				case 'samewith': 
+					if(data[rule[i].name] != data[rule[i].checkRule]){
+						this.error = rule[i].errorMsg; 
+						return false;
+					}
+				break;
+				case 'numbers':
+					var reg = new RegExp('^[0-9]{' + rule[i].checkRule + '}$');
+					if (!reg.test(data[rule[i].name])){
+						this.error = rule[i].errorMsg; return false; 
+					}
+				break;
+				case 'url':
+					var reg = /^(\w+:\/\/)?\w+(\.\w+)+.*$/;
+					if (!reg.test(data[rule[i].name])){
+						this.error = rule[i].errorMsg; 
+						return false; 
+					}
+				break;
+				case 'idCard' :
+					var idCardRes = idCardChecker.checkIdcard(data[rule[i].name]);
+					if(idCardRes != 'ok'){
+						this.error = idCardRes;
+						return false; 
+					} 
+				break;
+			}
+		}
+		return true;
+	},
+	isNumber : function (checkVal){
+		checkVal = Number(checkVal);
+		if(isNaN(checkVal)){return false;}
+		return true;
+	}
+}

+ 573 - 0
GraceUI5/js/grace.js

@@ -0,0 +1,573 @@
+var md5 = require('@/GraceUI5/js/md5.js');
+module.exports = {
+	// 版本检查
+	verson: function() {
+		var currentVersion = '5.0';
+		console.log(currentVersion);
+	},
+	// --- 页面跳转相关 ---
+	// 页面跳转
+	navigate: function(url, type, success, fail, complete) {
+		if (!type) {
+			type = 'navigateTo';
+		}
+		if (!success) {
+			success = function() {};
+		}
+		if (!fail) {
+			fail = function() {};
+		}
+		if (!complete) {
+			complete = function() {};
+		}
+		switch (type) {
+			case 'navigateTo':
+				uni.navigateTo({
+					url: url,
+					success: success,
+					fail: fail,
+					complete: complete
+				});
+				break;
+			case 'redirectTo':
+				uni.redirectTo({
+					url: url,
+					success: success,
+					fail: fail,
+					complete: complete
+				});
+				break;
+			case 'switchTab':
+				uni.switchTab({
+					url: url,
+					success: success,
+					fail: fail,
+					complete: complete
+				});
+				break;
+			case 'reLaunch':
+				uni.reLaunch({
+					url: url,
+					success: success,
+					fail: fail,
+					complete: complete
+				});
+				break;
+		}
+	},
+	// 返回
+	back: function(delta) {
+		if (!delta) {
+			delta = 1;
+		}
+		uni.navigateBack({
+			delta: delta
+		});
+	},
+
+	// --- 网络请求 ---
+	// get
+	get: function(url, data, headers, success, fail) {
+		if (!fail) {
+			fail = () => {
+				this.msg("网络请求失败");
+			}
+		}
+		if (!headers) {
+			headers = {};
+		}
+		if (this.__before != null) {
+			this.__before();
+			this.__before = null;
+		}
+		uni.request({
+			url: url,
+			data: data,
+			method: "GET",
+			dataType: "json",
+			header: headers,
+			success: (res) => {
+				success(res.data);
+			},
+			fail: fail,
+			complete: () => {
+				if (this.__after != null) {
+					this.__after();
+					this.__after = null;
+				}
+			}
+		});
+	},
+	// post
+	post: function(url, data, contentType, headers, success, fail) {
+		if (!fail) {
+			fail = () => {
+				this.msg("网络请求失败");
+			}
+		}
+		if (!headers) {
+			headers = {};
+		}
+		if (!contentType) {
+			contentType = 'form';
+		}
+		if (this.__before != null) {
+			this.__before();
+			this.__before = null;
+		}
+		switch (contentType) {
+			case "form":
+				headers['content-type'] = 'application/x-www-form-urlencoded';
+				break;
+			case "json":
+				headers['content-type'] = 'application/json';
+				break;
+			default:
+				headers['content-type'] = 'application/x-www-form-urlencoded';
+		}
+		uni.request({
+			url: url,
+			data: data,
+			method: "POST",
+			dataType: "json",
+			header: headers,
+			success: (res) => {
+				success(res.data);
+			},
+			fail: fail,
+			complete: () => {
+				if (this.__after != null) {
+					this.__after();
+					this.__after = null;
+				}
+			}
+		});
+	},
+	// 请求前置函数
+	__before: null,
+	setBefore: function(func) {
+		this.__before = func;
+	},
+	// 请求后置函数
+	__after: null,
+	setAfter: function(func) {
+		this.__after = func;
+	},
+
+	// --- 数据缓存 ---
+	setStorage: function(data) {
+		try {
+			for (let k in data) {
+				uni.setStorageSync(k, data[k] + '');
+			}
+			return true;
+		} catch (e) {
+			return false;
+		}
+	},
+	getStorage: function(keyName) {
+		try {
+			var tmpVal = uni.getStorageSync(keyName);
+			if (tmpVal == '') {
+				return false;
+			}
+			return tmpVal;
+		} catch (e) {
+			return false;
+		}
+	},
+	removeStorage: function(keyName) {
+		try {
+			uni.removeStorageSync(keyName);
+			return true;
+		} catch (e) {
+			return false;
+		}
+	},
+	clearStorage: function() {
+		try {
+			uni.clearStorageSync();
+		} catch (e) {}
+	},
+
+	// --- 图片相关 ---
+	chooseImgs: function(sets, success, fail, complete) {
+		if (!sets.count) {
+			sets.count = 1;
+		}
+		if (!sets.sizeType) {
+			sets.sizeType = ['original', 'compressed'];
+		}
+		if (!sets.sourceType) {
+			sets.sourceType = ['album', 'camera'];
+		}
+		uni.chooseImage({
+			count: sets.count, //默认9
+			sizeType: sets.sizeType, //可以指定是原图还是压缩图,默认二者都有
+			sourceType: sets.sourceType, //从相册选择
+			success: (res) => {
+				success(res.tempFilePaths);
+			},
+			fail: (e) => {
+				if (fail) {
+					fail(e);
+				}
+			},
+			complete: (e) => {
+				if (complete) {
+					complete(e);
+				}
+			}
+		});
+	},
+	getImageInfo: function(imgUrl, success, fail, complete) {
+		uni.getImageInfo({
+			src: imgUrl,
+			success: function(info) {
+				success(info);
+			},
+			fail: (e) => {
+				if (fail) {
+					fail(e);
+				}
+			},
+			complete: (e) => {
+				if (complete) {
+					complete(e);
+				}
+			}
+		});
+	},
+	previewImage: function(items, currentImg) {
+		uni.previewImage({
+			urls: items,
+			current: currentImg
+		});
+	},
+
+	// --- 系统信息 ---
+	system: function() {
+		try {
+			var res = uni.getSystemInfoSync();
+			var iPhoneXBottom = 0;
+			if (!res.model) {
+				res.model = 'no';
+			}
+			res.model = res.model.replace(' ', '');
+			res.model = res.model.toLowerCase();
+			var res1 = res.model.indexOf('iphonex');
+			if (res1 > 5) {
+				res1 = -1;
+			}
+			var res2 = res.model.indexOf('iphone1');
+			if (res2 > 5) {
+				res2 = -1;
+			}
+			if (res1 != -1 || res2 != -1) {
+				res.iPhoneXBottomHeightRpx = 50;
+				res.iPhoneXBottomHeightPx = uni.upx2px(50);
+			} else {
+				res.iPhoneXBottomHeightRpx = 0;
+				res.iPhoneXBottomHeightPx = 0;
+			}
+			return res;
+		} catch (e) {
+			return null;
+		}
+	},
+
+	// --- 消息弹框 ---
+	msg: function(msg) {
+		uni.showToast({
+			title: msg,
+			icon: "none"
+		});
+	},
+	showLoading: function(title) {
+		uni.showLoading({
+			title: title,
+			mask: true
+		});
+	},
+
+	// --- 导航条设置 ---
+	setNavBar: function(sets) {
+		if (sets.title) {
+			uni.setNavigationBarTitle({
+				title: sets.title
+			});
+		}
+		if (sets.color) {
+			uni.setNavigationBarColor({
+				frontColor: sets.color.frontColor,
+				backgroundColor: sets.color.backgroundColor,
+			 animation: {
+					duration: 400,
+			  timingFunc: 'easeIn'
+				}
+			});
+		}
+		if (sets.loading) {
+			uni.showNavigationBarLoading();
+		} else {
+			uni.hideNavigationBarLoading();
+		}
+	},
+
+	// --- 元素选择 ---
+	// 单个元素选择
+	select: function(selector, callBack) {
+		uni.createSelectorQuery().select(selector).boundingClientRect().exec((res) => {
+			callBack(res[0]);
+		});
+	},
+	// 多个元素获取
+	selectAll: function(selector, callBack) {
+		uni.createSelectorQuery().selectAll(selector).boundingClientRect().exec((res) => {
+			callBack(res[0]);
+		});
+	},
+
+	// --- 数组操作 ---
+	// 数组合并
+	arrayConcat: function() {
+		var tmpArr = [];
+		for (let i = 0; i < arguments.length; i++) {
+			tmpArr = tmpArr.concat(arguments[i]);
+		}
+		return tmpArr;
+	},
+	arrayDrop: function(array, index, howmany) {
+		if (!index) {
+			index = 0;
+		}
+		if (!howmany) {
+			howmany = 1;
+		}
+		array.splice(index, howmany);
+		return array;
+	},
+	arrayIndexOf: function(arr, needFind) {
+		var index = -1;
+		for (let i = 0; i < arr.length; i++) {
+			if (arr[i] == needFind) {
+				index = i;
+				return i;
+			}
+		}
+		return index;
+	},
+	arrayDifference: function(a, b) {
+		const set = new Set(b);
+		return a.filter(x => !set.has(x));
+	},
+	arrayShuffle: function(arr) {
+		let l = arr.length;
+		while (l) {
+			const i = Math.floor(Math.random() * l--);
+			[arr[l], arr[i]] = [arr[i], arr[l]];
+		}
+		return arr;
+	},
+	arraySum: function(arr) {
+		return arr.reduce((acc, val) => acc + val, 0);
+	},
+	arrayAvg: function(arr) {
+		return arr.reduce((acc, val) => acc + val, 0) / arr.length;
+	},
+	arrayEach: function(arr, fun) {
+		for (let i = 0; i < arr.length; i++) {
+			fun(arr[i], i);
+		}
+	},
+
+	// 2数之间的随机数
+	random: function(min, max) {
+		switch (arguments.length) {
+			case 1:
+				return parseInt(Math.random() * min + 1, 10);
+				break;
+			case 2:
+				return parseInt(Math.random() * (max - min + 1) + min, 10);
+				break;
+			default:
+				return 0;
+		}
+	},
+
+	// UUID
+	uuid: function(len) {
+		var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
+		var uuid = [],
+			i;
+		if (len) {
+			for (i = 0; i < len; i++) {
+				uuid[i] = chars[0 | Math.random() * chars.length];
+			}
+		} else {
+			var r;
+			uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
+			uuid[14] = '4';
+			for (i = 0; i < 36; i++) {
+				if (!uuid[i]) {
+					r = 0 | Math.random() * 16;
+					uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
+				}
+			}
+		}
+		return uuid.join('');
+	},
+
+	// --- 日期时间 ---
+	now: function(type, addTime) {
+		var dateObj = new Date();
+		var cTime = dateObj.getTime();
+		if (addTime) {
+			cTime += addTime;
+		}
+		if (!type) {
+			type = 'number';
+		}
+		if (type == 'number') {
+			return cTime;
+		} else if (type == 'str') {
+			return this.toDate(cTime / 1000, 'str');
+		} else if (type == 'array') {
+			return this.toDate(cTime / 1000, 'array');
+		}
+	},
+	// 时间戳转 YY-mm-dd HH:ii:ss
+	toDate: function(timeStamp, returnType) {
+		timeStamp = parseInt(timeStamp);
+		var date = new Date();
+		if (timeStamp < 90000000000) {
+			date.setTime(timeStamp * 1000);
+		} else {
+			date.setTime(timeStamp);
+		}
+		var y = date.getFullYear();
+		var m = date.getMonth() + 1;
+		m = m < 10 ? ('0' + m) : m;
+		var d = date.getDate();
+		d = d < 10 ? ('0' + d) : d;
+		var h = date.getHours();
+		h = h < 10 ? ('0' + h) : h;
+		var minute = date.getMinutes();
+		var second = date.getSeconds();
+		minute = minute < 10 ? ('0' + minute) : minute;
+		second = second < 10 ? ('0' + second) : second;
+		if (returnType == 'str') {
+			return y + '-' + m + '-' + d + ' ' + h + ':' + minute + ':' + second;
+		}
+		return [y, m, d, h, minute, second];
+	},
+	// 字符串转时间戳
+	toTimeStamp: function(timeStamp) {
+		var reg = /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/;
+		var res = timeStamp.match(reg);
+		if (res == null) {
+			var reg2 = /^([0-9]{2})\/([0-9]{2})\/([0-9]{4}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/;
+			var res2 = timeStamp.match(reg2);
+			if (res2 == null) {
+				console.log('时间格式错误 E001');
+				return false;
+			}
+			var year = parseInt(res2[3]);
+			var month = parseInt(res2[1]);
+			var day = parseInt(res2[2]);
+			var h = parseInt(res2[4]);
+			var i = parseInt(res2[5]);
+			var s = parseInt(res2[6]);
+		} else {
+			var year = parseInt(res[1]);
+			var month = parseInt(res[2]);
+			var day = parseInt(res[3]);
+			var h = parseInt(res[4]);
+			var i = parseInt(res[5]);
+			var s = parseInt(res[6]);
+		}
+		if (year < 1000) {
+			console.log('时间格式错误');
+			return false;
+		}
+		if (h < 0 || h > 24) {
+			console.log('时间格式错误');
+			return false;
+		}
+		if (i < 0 || i > 60) {
+			console.log('时间格式错误');
+			return false;
+		}
+		if (s < 0 || s > 60) {
+			console.log('时间格式错误');
+			return false;
+		}
+		return Date.parse(new Date(year, month - 1, day, h, i, s));
+	},
+	// 根据时间戳计算多少分钟/小时/天之前
+	fromTime: function(time) {
+		if (time < 90000000000) {
+			time *= 1000;
+		}
+		var timer = new Date().getTime() - time;
+		timer = parseInt(timer / 1000);
+		if (timer < 180) {
+			return '刚刚';
+		} else if (timer >= 180 && timer < 3600) {
+			return parseInt(timer / 60) + '分钟前';
+		} else if (timer >= 3600 && timer < 86400) {
+			return parseInt(timer / 3600) + '小时前';
+		} else if (timer >= 86400 && timer < 2592000) {
+			return parseInt(timer / 86400) + '天前';
+		} else {
+			return this.toDate(time, 'str');
+		}
+	},
+
+	// 延迟操作
+	delay: function(timer, func) {
+		return setTimeout(func, timer);
+	},
+	// 间隔指定时间循环某个函数
+	interval: function(timer, func) {
+		return setInterval(func, timer);
+	},
+
+	// 对象操作
+	assign: function(obj, key, val) {
+		obj[key] = val;
+	},
+	removeByKey: function(obj, key) {
+		delete obj[key];
+	},
+	each: function(obj, func) {
+		for (let k in obj) {
+			func(k, obj[k]);
+		}
+	},
+	isEmptyObj: function(obj) {
+		return JSON.stringify(obj) === '{}';
+	},
+
+	// 获取ref ( 循环获取,直到 组件创建完成并获取成功 )
+	getRefs: function(ref, _this, count, fun) {
+		if (count >= 50) {
+			fun(_this.$refs[ref]);
+			return false;
+		}
+		var refReturn = _this.$refs[ref];
+		if (refReturn) {
+			fun(refReturn);
+		} else {
+			count++;
+			setTimeout(() => {
+				this.getRefs(ref, _this, count, fun);
+			}, 100);
+		}
+	},
+	
+	// md5
+	md5 : function(str){
+		return md5.md5(str);
+	}
+}

+ 385 - 0
GraceUI5/js/md5.js

@@ -0,0 +1,385 @@
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0;   /* hex output format. 0 - lowercase; 1 - uppercase        */
+var b64pad  = "";  /* base-64 pad character. "=" for strict RFC compliance   */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_md5(s)    { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
+function b64_md5(s)    { return rstr2b64(rstr_md5(str2rstr_utf8(s))); }
+function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); }
+function hex_hmac_md5(k, d)
+  { return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
+function b64_hmac_md5(k, d)
+  { return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
+function any_hmac_md5(k, d, e)
+  { return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function md5_vm_test()
+{
+  return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+/*
+ * Calculate the MD5 of a raw string
+ */
+function rstr_md5(s)
+{
+  return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data (raw strings)
+ */
+function rstr_hmac_md5(key, data)
+{
+  var bkey = rstr2binl(key);
+  if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);
+
+  var ipad = Array(16), opad = Array(16);
+  for(var i = 0; i < 16; i++)
+  {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;
+  }
+
+  var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
+  return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
+}
+
+/*
+ * Convert a raw string to a hex string
+ */
+function rstr2hex(input)
+{
+  try { hexcase } catch(e) { hexcase=0; }
+  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+  var output = "";
+  var x;
+  for(var i = 0; i < input.length; i++)
+  {
+    x = input.charCodeAt(i);
+    output += hex_tab.charAt((x >>> 4) & 0x0F)
+           +  hex_tab.charAt( x        & 0x0F);
+  }
+  return output;
+}
+
+/*
+ * Convert a raw string to a base-64 string
+ */
+function rstr2b64(input)
+{
+  try { b64pad } catch(e) { b64pad=''; }
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  var rcode = 'NUMOOEXAXNLVGN';
+  var output = "";
+  var len = input.length;
+  for(var i = 0; i < len; i += 3)
+  {
+    var triplet = (input.charCodeAt(i) << 16)
+                | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
+                | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
+    for(var j = 0; j < 4; j++)
+    {
+      if(i * 8 + j * 6 > input.length * 8) output += b64pad;
+      else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
+    }
+  }
+  return output;
+}
+
+/*
+ * Convert a raw string to an arbitrary string encoding
+ */
+function rstr2any(input, encoding)
+{
+  var divisor = encoding.length;
+  var i, j, q, x, quotient;
+
+  /* Convert to an array of 16-bit big-endian values, forming the dividend */
+  var dividend = Array(Math.ceil(input.length / 2));
+  for(i = 0; i < dividend.length; i++)
+  {
+    dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
+  }
+
+  /*
+   * Repeatedly perform a long division. The binary array forms the dividend,
+   * the length of the encoding is the divisor. Once computed, the quotient
+   * forms the dividend for the next step. All remainders are stored for later
+   * use.
+   */
+  var full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
+  var remainders = Array(full_length);
+  for(j = 0; j < full_length; j++)
+  {
+    quotient = Array();
+    x = 0;
+    for(i = 0; i < dividend.length; i++)
+    {
+      x = (x << 16) + dividend[i];
+      q = Math.floor(x / divisor);
+      x -= q * divisor;
+      if(quotient.length > 0 || q > 0)
+        quotient[quotient.length] = q;
+    }
+    remainders[j] = x;
+    dividend = quotient;
+  }
+
+  /* Convert the remainders to the output string */
+  var output = "";
+  for(i = remainders.length - 1; i >= 0; i--)
+    output += encoding.charAt(remainders[i]);
+
+  return output;
+}
+
+/*
+ * Encode a string as utf-8.
+ * For efficiency, this assumes the input is valid utf-16.
+ */
+function str2rstr_utf8(input)
+{
+  var output = "";
+  var i = -1;
+  var x, y;
+
+  while(++i < input.length)
+  {
+    /* Decode utf-16 surrogate pairs */
+    x = input.charCodeAt(i);
+    y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
+    if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
+    {
+      x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
+      i++;
+    }
+
+    /* Encode output as utf-8 */
+    if(x <= 0x7F)
+      output += String.fromCharCode(x);
+    else if(x <= 0x7FF)
+      output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
+                                    0x80 | ( x         & 0x3F));
+    else if(x <= 0xFFFF)
+      output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
+                                    0x80 | ((x >>> 6 ) & 0x3F),
+                                    0x80 | ( x         & 0x3F));
+    else if(x <= 0x1FFFFF)
+      output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
+                                    0x80 | ((x >>> 12) & 0x3F),
+                                    0x80 | ((x >>> 6 ) & 0x3F),
+                                    0x80 | ( x         & 0x3F));
+  }
+  return output;
+}
+
+/*
+ * Encode a string as utf-16
+ */
+function str2rstr_utf16le(input)
+{
+  var output = "";
+  for(var i = 0; i < input.length; i++)
+    output += String.fromCharCode( input.charCodeAt(i)        & 0xFF,
+                                  (input.charCodeAt(i) >>> 8) & 0xFF);
+  return output;
+}
+
+function str2rstr_utf16be(input)
+{
+  var output = "";
+  for(var i = 0; i < input.length; i++)
+    output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
+                                   input.charCodeAt(i)        & 0xFF);
+  return output;
+}
+
+/*
+ * Convert a raw string to an array of little-endian words
+ * Characters >255 have their high-byte silently ignored.
+ */
+function rstr2binl(input)
+{
+  var output = Array(input.length >> 2);
+  for(var i = 0; i < output.length; i++)
+    output[i] = 0;
+  for(var i = 0; i < input.length * 8; i += 8)
+    output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
+  return output;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2rstr(input)
+{
+  var output = "";
+  for(var i = 0; i < input.length * 32; i += 8)
+    output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
+  return output;
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length.
+ */
+function binl_md5(x, len)
+{
+  /* append padding */
+  x[len >> 5] |= 0x80 << ((len) % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+
+  for(var i = 0; i < x.length; i += 16)
+  {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+
+    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
+    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
+    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
+    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
+    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
+
+    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
+    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
+    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
+    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
+    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
+    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
+    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
+    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
+    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
+    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
+    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
+    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
+    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
+    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
+    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
+    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+  return Array(a, b, c, d);
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t)
+{
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+module.exports = {
+	md5 : function(str){
+		return hex_md5(str);
+	}
+}

+ 252 - 0
GraceUI5/js/parserHTML.js

@@ -0,0 +1,252 @@
+// Regular Expressions for parsing tags and attributes
+var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
+var endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/;
+var attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; // Empty Elements - HTML 5
+
+var empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr,\n,\t,\t'); // Block Elements - HTML 5
+// fixed by xxx 将 ins 标签从块级名单中移除
+
+var block = makeMap('a,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); // Inline Elements - HTML 5
+
+var inline = makeMap('abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); // Elements that you can, intentionally, leave open
+// (and which close themselves)
+
+var closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); // Attributes that have their values filled in disabled="disabled"
+
+var fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'); 
+// Special Elements (can contain anything)
+
+var special = makeMap('script,style');
+function HTMLParser(html, handler) {
+  var index;
+  var chars;
+  var match;
+  var stack = [];
+  var last = html;
+
+  stack.last = function () {
+    return this[this.length - 1];
+  };
+
+  while (html) {
+    chars = true; // Make sure we're not in a script or style element
+    if (!stack.last() || !special[stack.last()]) {
+      // Comment
+      if (html.indexOf('<!--') == 0) {
+        index = html.indexOf('-->');
+
+        if (index >= 0) {
+          if (handler.comment) {
+            handler.comment(html.substring(4, index));
+          }
+
+          html = html.substring(index + 3);
+          chars = false;
+        } // end tag
+
+      } else if (html.indexOf('</') == 0) {
+        match = html.match(endTag);
+
+        if (match) {
+          html = html.substring(match[0].length);
+          match[0].replace(endTag, parseEndTag);
+          chars = false;
+        } // start tag
+
+      } else if (html.indexOf('<') == 0) {
+        match = html.match(startTag);
+
+        if (match) {
+          html = html.substring(match[0].length);
+          match[0].replace(startTag, parseStartTag);
+          chars = false;
+        }
+      }
+
+      if (chars) {
+        index = html.indexOf('<');
+        var text = index < 0 ? html : html.substring(0, index);
+        html = index < 0 ? '' : html.substring(index);
+
+        if (handler.chars) {
+          handler.chars(text);
+        }
+      }
+    } else {
+      html = html.replace(new RegExp('([\\s\\S]*?)<\/' + stack.last() + '[^>]*>'), function (all, text) {
+        text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, '$1$2');
+
+        if (handler.chars) {
+          handler.chars(text);
+        }
+
+        return '';
+      });
+      parseEndTag('', stack.last());
+    }
+
+    if (html == last) {
+      throw 'Parse Error: ' + html;
+    }
+
+    last = html;
+  } // Clean up any remaining tags
+
+
+  parseEndTag();
+
+  function parseStartTag(tag, tagName, rest, unary) {
+    tagName = tagName.toLowerCase();
+    if (block[tagName]) {
+      while (stack.last() && inline[stack.last()]) {
+        parseEndTag('', stack.last());
+      }
+    }
+
+    if (closeSelf[tagName] && stack.last() == tagName) {
+      parseEndTag('', tagName);
+    }
+
+    unary = empty[tagName] || !!unary;
+
+    if (!unary) {
+      stack.push(tagName);
+    }
+
+    if (handler.start) {
+      var attrs = [];
+      rest.replace(attr, function (match, name) {
+        var value = arguments[2] ? arguments[2] : arguments[3] ? arguments[3] : arguments[4] ? arguments[4] : fillAttrs[name] ? name : '';
+        attrs.push({
+          name: name,
+          value: value,
+          escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') // "
+
+        });
+      });
+      if (handler.start) {
+        handler.start(tagName, attrs, unary);
+      }
+    }
+  }
+
+  function parseEndTag(tag, tagName) {
+    // If no tag name is provided, clean shop
+    if (!tagName) {
+      var pos = 0;
+    } // Find the closest opened tag of the same type
+    else {
+        for (var pos = stack.length - 1; pos >= 0; pos--) {
+          if (stack[pos] == tagName) {
+            break;
+          }
+        }
+      }
+    if (pos >= 0) {
+      // Close all the open elements, up the stack
+      for (var i = stack.length - 1; i >= pos; i--) {
+        if (handler.end) {
+          handler.end(stack[i]);
+        }
+      } // Remove the open elements from the stack
+      stack.length = pos;
+    }
+  }
+}
+
+function makeMap(str) {
+  var obj = {};
+  var items = str.split(',');
+  for (var i = 0; i < items.length; i++) {
+    obj[items[i]] = true;
+  }
+  return obj;
+}
+function removeDOCTYPE(html) {
+  return html.replace(/<\?xml.*\?>\n/, '').replace(/<!doctype.*>\n/, '').replace(/<!DOCTYPE.*>\n/, '');
+}
+function parseAttrs(attrs) {
+  return attrs.reduce(function (pre, attr) {
+    var value = attr.value;
+    var name = attr.name;
+	if(value.indexOf('width') != -1){
+		value += '; width:100%; height:auto;'
+	}
+    if (pre[name]) {
+			pre[name] = pre[name] + " " + value;
+    } else {
+			pre[name] = value;
+    }
+    return pre;
+  }, {});
+}
+function parseHtml(html) {
+  html = removeDOCTYPE(html);
+  html = html.replace(/\n/g,'');
+  html = html.replace(/\t/g,'');
+  var stacks = [];
+  var results = {
+    node : 'root',
+    children: []
+  };
+  HTMLParser( html, {
+    start : function start(tag, attrs, unary) {
+      var node = {name: tag };
+      if (attrs.length !== 0) {
+        node.attrs = parseAttrs(attrs);
+      }
+      if (unary) {
+        var parent = stacks[0] || results;
+        if (!parent.children) {
+          parent.children = [];
+        }
+        parent.children.push(node);
+      } else {
+        stacks.unshift(node);
+      }
+    },
+    end: function end(tag) {
+      var node = stacks.shift();
+      if (node.name !== tag){console.error('invalid state: mismatch end tag');}
+      if (stacks.length === 0) {
+        results.children.push(node);
+      } else {
+        var parent = stacks[0];
+        if (!parent.children) {
+          parent.children = [];
+        }
+        parent.children.push(node);
+      }
+    },
+    chars: function chars(text) {
+      var node = {
+        type: 'text',
+        text: text
+      };
+      if (stacks.length === 0) {
+        results.children.push(node);
+      } else {
+        var parent = stacks[0];
+        if (!parent.children) {
+          parent.children = [];
+        }
+        parent.children.push(node);
+      }
+    },
+    comment: function comment(text) {
+      var node = {
+        node : 'comment',
+        text : text
+      };
+      var parent = stacks[0];
+      if (!parent.children) {
+        parent.children = [];
+      }
+      parent.children.push(node);
+    }
+  });
+  return results.children;
+}
+module.exports = {
+	parserHTML : parseHtml
+}

+ 0 - 0
GraceUI5/js/qrcode.js


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