瀏覽代碼

修改收货地址

zs 1 年之前
父節點
當前提交
a451c0366a
共有 39 個文件被更改,包括 10900 次插入7 次删除
  1. 891 0
      common/qqmap-wx-jssdk.min.js
  2. 21 0
      common/tool.js
  3. 4233 0
      config.js
  4. 12 0
      pages.json
  5. 319 0
      pagesHome/address/add.vue
  6. 364 0
      pagesHome/address/index.vue
  7. 7 1
      pagesHome/notarization/detail.vue
  8. 0 6
      pagesHome/notarization/sign.vue
  9. 94 0
      uni_modules/uni-forms/changelog.md
  10. 627 0
      uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue
  11. 397 0
      uni_modules/uni-forms/components/uni-forms/uni-forms.vue
  12. 293 0
      uni_modules/uni-forms/components/uni-forms/utils.js
  13. 486 0
      uni_modules/uni-forms/components/uni-forms/validate.js
  14. 88 0
      uni_modules/uni-forms/package.json
  15. 23 0
      uni_modules/uni-forms/readme.md
  16. 40 0
      uni_modules/uni-icons/changelog.md
  17. 91 0
      uni_modules/uni-icons/components/uni-icons/uni-icons.uvue
  18. 110 0
      uni_modules/uni-icons/components/uni-icons/uni-icons.vue
  19. 664 0
      uni_modules/uni-icons/components/uni-icons/uniicons.css
  20. 二進制
      uni_modules/uni-icons/components/uni-icons/uniicons.ttf
  21. 664 0
      uni_modules/uni-icons/components/uni-icons/uniicons_file.ts
  22. 649 0
      uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js
  23. 88 0
      uni_modules/uni-icons/package.json
  24. 8 0
      uni_modules/uni-icons/readme.md
  25. 8 0
      uni_modules/uni-scss/changelog.md
  26. 1 0
      uni_modules/uni-scss/index.scss
  27. 82 0
      uni_modules/uni-scss/package.json
  28. 4 0
      uni_modules/uni-scss/readme.md
  29. 7 0
      uni_modules/uni-scss/styles/index.scss
  30. 3 0
      uni_modules/uni-scss/styles/setting/_border.scss
  31. 66 0
      uni_modules/uni-scss/styles/setting/_color.scss
  32. 55 0
      uni_modules/uni-scss/styles/setting/_radius.scss
  33. 56 0
      uni_modules/uni-scss/styles/setting/_space.scss
  34. 167 0
      uni_modules/uni-scss/styles/setting/_styles.scss
  35. 24 0
      uni_modules/uni-scss/styles/setting/_text.scss
  36. 146 0
      uni_modules/uni-scss/styles/setting/_variables.scss
  37. 19 0
      uni_modules/uni-scss/styles/tools/functions.scss
  38. 31 0
      uni_modules/uni-scss/theme.scss
  39. 62 0
      uni_modules/uni-scss/variables.scss

+ 891 - 0
common/qqmap-wx-jssdk.min.js

@@ -0,0 +1,891 @@
+var ERROR_CONF = {
+	KEY_ERR: 311,
+	KEY_ERR_MSG: 'key格式错误',
+	PARAM_ERR: 310,
+	PARAM_ERR_MSG: '请求参数信息有误',
+	SYSTEM_ERR: 600,
+	SYSTEM_ERR_MSG: '系统错误',
+	WX_ERR_CODE: 1000,
+	WX_OK_CODE: 200
+};
+var BASE_URL = 'https://apis.map.qq.com/ws/';
+var URL_SEARCH = BASE_URL + 'place/v1/search';
+var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion';
+var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/';
+var URL_CITY_LIST = BASE_URL + 'district/v1/list';
+var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren';
+var URL_DISTANCE = BASE_URL + 'distance/v1/';
+var URL_DIRECTION = BASE_URL + 'direction/v1/';
+var MODE = {
+	driving: 'driving',
+	transit: 'transit'
+};
+var EARTH_RADIUS = 6378136.49;
+var Utils = {
+	safeAdd(x, y) {
+		var lsw = (x & 0xffff) + (y & 0xffff);
+		var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+		return (msw << 16) | (lsw & 0xffff)
+	},
+	bitRotateLeft(num, cnt) {
+		return (num << cnt) | (num >>> (32 - cnt))
+	},
+	md5cmn(q, a, b, x, s, t) {
+		return this.safeAdd(this.bitRotateLeft(this.safeAdd(this.safeAdd(a, q), this.safeAdd(x, t)), s), b)
+	},
+	md5ff(a, b, c, d, x, s, t) {
+		return this.md5cmn((b & c) | (~b & d), a, b, x, s, t)
+	},
+	md5gg(a, b, c, d, x, s, t) {
+		return this.md5cmn((b & d) | (c & ~d), a, b, x, s, t)
+	},
+	md5hh(a, b, c, d, x, s, t) {
+		return this.md5cmn(b ^ c ^ d, a, b, x, s, t)
+	},
+	md5ii(a, b, c, d, x, s, t) {
+		return this.md5cmn(c ^ (b | ~d), a, b, x, s, t)
+	},
+	binlMD5(x, len) {
+		x[len >> 5] |= 0x80 << (len % 32);
+		x[((len + 64) >>> 9 << 4) + 14] = len;
+		var i;
+		var olda;
+		var oldb;
+		var oldc;
+		var oldd;
+		var a = 1732584193;
+		var b = -271733879;
+		var c = -1732584194;
+		var d = 271733878;
+		for (i = 0; i < x.length; i += 16) {
+			olda = a;
+			oldb = b;
+			oldc = c;
+			oldd = d;
+			a = this.md5ff(a, b, c, d, x[i], 7, -680876936);
+			d = this.md5ff(d, a, b, c, x[i + 1], 12, -389564586);
+			c = this.md5ff(c, d, a, b, x[i + 2], 17, 606105819);
+			b = this.md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
+			a = this.md5ff(a, b, c, d, x[i + 4], 7, -176418897);
+			d = this.md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
+			c = this.md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
+			b = this.md5ff(b, c, d, a, x[i + 7], 22, -45705983);
+			a = this.md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
+			d = this.md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
+			c = this.md5ff(c, d, a, b, x[i + 10], 17, -42063);
+			b = this.md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
+			a = this.md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
+			d = this.md5ff(d, a, b, c, x[i + 13], 12, -40341101);
+			c = this.md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
+			b = this.md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
+			a = this.md5gg(a, b, c, d, x[i + 1], 5, -165796510);
+			d = this.md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
+			c = this.md5gg(c, d, a, b, x[i + 11], 14, 643717713);
+			b = this.md5gg(b, c, d, a, x[i], 20, -373897302);
+			a = this.md5gg(a, b, c, d, x[i + 5], 5, -701558691);
+			d = this.md5gg(d, a, b, c, x[i + 10], 9, 38016083);
+			c = this.md5gg(c, d, a, b, x[i + 15], 14, -660478335);
+			b = this.md5gg(b, c, d, a, x[i + 4], 20, -405537848);
+			a = this.md5gg(a, b, c, d, x[i + 9], 5, 568446438);
+			d = this.md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
+			c = this.md5gg(c, d, a, b, x[i + 3], 14, -187363961);
+			b = this.md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
+			a = this.md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
+			d = this.md5gg(d, a, b, c, x[i + 2], 9, -51403784);
+			c = this.md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
+			b = this.md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
+			a = this.md5hh(a, b, c, d, x[i + 5], 4, -378558);
+			d = this.md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
+			c = this.md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
+			b = this.md5hh(b, c, d, a, x[i + 14], 23, -35309556);
+			a = this.md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
+			d = this.md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
+			c = this.md5hh(c, d, a, b, x[i + 7], 16, -155497632);
+			b = this.md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
+			a = this.md5hh(a, b, c, d, x[i + 13], 4, 681279174);
+			d = this.md5hh(d, a, b, c, x[i], 11, -358537222);
+			c = this.md5hh(c, d, a, b, x[i + 3], 16, -722521979);
+			b = this.md5hh(b, c, d, a, x[i + 6], 23, 76029189);
+			a = this.md5hh(a, b, c, d, x[i + 9], 4, -640364487);
+			d = this.md5hh(d, a, b, c, x[i + 12], 11, -421815835);
+			c = this.md5hh(c, d, a, b, x[i + 15], 16, 530742520);
+			b = this.md5hh(b, c, d, a, x[i + 2], 23, -995338651);
+			a = this.md5ii(a, b, c, d, x[i], 6, -198630844);
+			d = this.md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
+			c = this.md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
+			b = this.md5ii(b, c, d, a, x[i + 5], 21, -57434055);
+			a = this.md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
+			d = this.md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
+			c = this.md5ii(c, d, a, b, x[i + 10], 15, -1051523);
+			b = this.md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
+			a = this.md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
+			d = this.md5ii(d, a, b, c, x[i + 15], 10, -30611744);
+			c = this.md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
+			b = this.md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
+			a = this.md5ii(a, b, c, d, x[i + 4], 6, -145523070);
+			d = this.md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
+			c = this.md5ii(c, d, a, b, x[i + 2], 15, 718787259);
+			b = this.md5ii(b, c, d, a, x[i + 9], 21, -343485551);
+			a = this.safeAdd(a, olda);
+			b = this.safeAdd(b, oldb);
+			c = this.safeAdd(c, oldc);
+			d = this.safeAdd(d, oldd)
+		}
+		return [a, b, c, d]
+	},
+	binl2rstr(input) {
+		var i;
+		var output = '';
+		var length32 = input.length * 32;
+		for (i = 0; i < length32; i += 8) {
+			output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff)
+		}
+		return output
+	},
+	rstr2binl(input) {
+		var i;
+		var output = [];
+		output[(input.length >> 2) - 1] = undefined;
+		for (i = 0; i < output.length; i += 1) {
+			output[i] = 0
+		}
+		var length8 = input.length * 8;
+		for (i = 0; i < length8; i += 8) {
+			output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32)
+		}
+		return output
+	},
+	rstrMD5(s) {
+		return this.binl2rstr(this.binlMD5(this.rstr2binl(s), s.length * 8))
+	},
+	rstrHMACMD5(key, data) {
+		var i;
+		var bkey = this.rstr2binl(key);
+		var ipad = [];
+		var opad = [];
+		var hash;
+		ipad[15] = opad[15] = undefined;
+		if (bkey.length > 16) {
+			bkey = this.binlMD5(bkey, key.length * 8)
+		}
+		for (i = 0; i < 16; i += 1) {
+			ipad[i] = bkey[i] ^ 0x36363636;
+			opad[i] = bkey[i] ^ 0x5c5c5c5c
+		}
+		hash = this.binlMD5(ipad.concat(this.rstr2binl(data)), 512 + data.length * 8);
+		return this.binl2rstr(this.binlMD5(opad.concat(hash), 512 + 128))
+	},
+	rstr2hex(input) {
+		var hexTab = '0123456789abcdef';
+		var output = '';
+		var x;
+		var i;
+		for (i = 0; i < input.length; i += 1) {
+			x = input.charCodeAt(i);
+			output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f)
+		}
+		return output
+	},
+	str2rstrUTF8(input) {
+		return unescape(encodeURIComponent(input))
+	},
+	rawMD5(s) {
+		return this.rstrMD5(this.str2rstrUTF8(s))
+	},
+	hexMD5(s) {
+		return this.rstr2hex(this.rawMD5(s))
+	},
+	rawHMACMD5(k, d) {
+		return this.rstrHMACMD5(this.str2rstrUTF8(k), str2rstrUTF8(d))
+	},
+	hexHMACMD5(k, d) {
+		return this.rstr2hex(this.rawHMACMD5(k, d))
+	},
+	md5(string, key, raw) {
+		if (!key) {
+			if (!raw) {
+				return this.hexMD5(string)
+			}
+			return this.rawMD5(string)
+		}
+		if (!raw) {
+			return this.hexHMACMD5(key, string)
+		}
+		return this.rawHMACMD5(key, string)
+	},
+	getSig(requestParam, sk, feature, mode) {
+		var sig = null;
+		var requestArr = [];
+		Object.keys(requestParam).sort().forEach(function(key) {
+			requestArr.push(key + '=' + requestParam[key])
+		});
+		if (feature == 'search') {
+			sig = '/ws/place/v1/search?' + requestArr.join('&') + sk
+		}
+		if (feature == 'suggest') {
+			sig = '/ws/place/v1/suggestion?' + requestArr.join('&') + sk
+		}
+		if (feature == 'reverseGeocoder') {
+			sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk
+		}
+		if (feature == 'geocoder') {
+			sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk
+		}
+		if (feature == 'getCityList') {
+			sig = '/ws/district/v1/list?' + requestArr.join('&') + sk
+		}
+		if (feature == 'getDistrictByCityId') {
+			sig = '/ws/district/v1/getchildren?' + requestArr.join('&') + sk
+		}
+		if (feature == 'calculateDistance') {
+			sig = '/ws/distance/v1/?' + requestArr.join('&') + sk
+		}
+		if (feature == 'direction') {
+			sig = '/ws/direction/v1/' + mode + '?' + requestArr.join('&') + sk
+		}
+		sig = this.md5(sig);
+		return sig
+	},
+	location2query(data) {
+		if (typeof data == 'string') {
+			return data
+		}
+		var query = '';
+		for (var i = 0; i < data.length; i++) {
+			var d = data[i];
+			if (!!query) {
+				query += ';'
+			}
+			if (d.location) {
+				query = query + d.location.lat + ',' + d.location.lng
+			}
+			if (d.latitude && d.longitude) {
+				query = query + d.latitude + ',' + d.longitude
+			}
+		}
+		return query
+	},
+	rad(d) {
+		return d * Math.PI / 180.0
+	},
+	getEndLocation(location) {
+		var to = location.split(';');
+		var endLocation = [];
+		for (var i = 0; i < to.length; i++) {
+			endLocation.push({
+				lat: parseFloat(to[i].split(',')[0]),
+				lng: parseFloat(to[i].split(',')[1])
+			})
+		}
+		return endLocation
+	},
+	getDistance(latFrom, lngFrom, latTo, lngTo) {
+		var radLatFrom = this.rad(latFrom);
+		var radLatTo = this.rad(latTo);
+		var a = radLatFrom - radLatTo;
+		var b = this.rad(lngFrom) - this.rad(lngTo);
+		var distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(
+			radLatTo) * Math.pow(Math.sin(b / 2), 2)));
+		distance = distance * EARTH_RADIUS;
+		distance = Math.round(distance * 10000) / 10000;
+		return parseFloat(distance.toFixed(0))
+	},
+	getWXLocation(success, fail, complete) {
+		wx.getLocation({
+			type: 'gcj02',
+			success: success,
+			fail: fail,
+			complete: complete
+		})
+	},
+	getLocationParam(location) {
+		if (typeof location == 'string') {
+			var locationArr = location.split(',');
+			if (locationArr.length === 2) {
+				location = {
+					latitude: location.split(',')[0],
+					longitude: location.split(',')[1]
+				}
+			} else {
+				location = {}
+			}
+		}
+		return location
+	},
+	polyfillParam(param) {
+		param.success = param.success || function() {};
+		param.fail = param.fail || function() {};
+		param.complete = param.complete || function() {}
+	},
+	checkParamKeyEmpty(param, key) {
+		if (!param[key]) {
+			var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key + '参数格式有误');
+			param.fail(errconf);
+			param.complete(errconf);
+			return true
+		}
+		return false
+	},
+	checkKeyword(param) {
+		return !this.checkParamKeyEmpty(param, 'keyword')
+	},
+	checkLocation(param) {
+		var location = this.getLocationParam(param.location);
+		if (!location || !location.latitude || !location.longitude) {
+			var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误');
+			param.fail(errconf);
+			param.complete(errconf);
+			return false
+		}
+		return true
+	},
+	buildErrorConfig(errCode, errMsg) {
+		return {
+			status: errCode,
+			message: errMsg
+		}
+	},
+	handleData(param, data, feature) {
+		if (feature == 'search') {
+			var searchResult = data.data;
+			var searchSimplify = [];
+			for (var i = 0; i < searchResult.length; i++) {
+				searchSimplify.push({
+					id: searchResult[i].id || null,
+					title: searchResult[i].title || null,
+					latitude: searchResult[i].location && searchResult[i].location.lat || null,
+					longitude: searchResult[i].location && searchResult[i].location.lng || null,
+					address: searchResult[i].address || null,
+					category: searchResult[i].category || null,
+					tel: searchResult[i].tel || null,
+					adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null,
+					city: searchResult[i].ad_info && searchResult[i].ad_info.city || null,
+					district: searchResult[i].ad_info && searchResult[i].ad_info.district || null,
+					province: searchResult[i].ad_info && searchResult[i].ad_info.province || null
+				})
+			}
+			param.success(data, {
+				searchResult: searchResult,
+				searchSimplify: searchSimplify
+			})
+		} else if (feature == 'suggest') {
+			var suggestResult = data.data;
+			var suggestSimplify = [];
+			for (var i = 0; i < suggestResult.length; i++) {
+				suggestSimplify.push({
+					adcode: suggestResult[i].adcode || null,
+					address: suggestResult[i].address || null,
+					category: suggestResult[i].category || null,
+					city: suggestResult[i].city || null,
+					district: suggestResult[i].district || null,
+					id: suggestResult[i].id || null,
+					latitude: suggestResult[i].location && suggestResult[i].location.lat || null,
+					longitude: suggestResult[i].location && suggestResult[i].location.lng || null,
+					province: suggestResult[i].province || null,
+					title: suggestResult[i].title || null,
+					type: suggestResult[i].type || null
+				})
+			}
+			param.success(data, {
+				suggestResult: suggestResult,
+				suggestSimplify: suggestSimplify
+			})
+		} else if (feature == 'reverseGeocoder') {
+			var reverseGeocoderResult = data.result;
+			var reverseGeocoderSimplify = {
+				address: reverseGeocoderResult.address || null,
+				latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null,
+				longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null,
+				adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null,
+				city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city ||
+					null,
+				district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component
+					.district || null,
+				nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component
+					.nation || null,
+				province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component
+					.province || null,
+				street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component
+					.street || null,
+				street_number: reverseGeocoderResult.address_component && reverseGeocoderResult
+					.address_component.street_number || null,
+				recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult
+					.formatted_addresses.recommend || null,
+				rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses
+					.rough || null
+			};
+			if (reverseGeocoderResult.pois) {
+				var pois = reverseGeocoderResult.pois;
+				var poisSimplify = [];
+				for (var i = 0; i < pois.length; i++) {
+					poisSimplify.push({
+						id: pois[i].id || null,
+						title: pois[i].title || null,
+						latitude: pois[i].location && pois[i].location.lat || null,
+						longitude: pois[i].location && pois[i].location.lng || null,
+						address: pois[i].address || null,
+						category: pois[i].category || null,
+						adcode: pois[i].ad_info && pois[i].ad_info.adcode || null,
+						city: pois[i].ad_info && pois[i].ad_info.city || null,
+						district: pois[i].ad_info && pois[i].ad_info.district || null,
+						province: pois[i].ad_info && pois[i].ad_info.province || null
+					})
+				}
+				param.success(data, {
+					reverseGeocoderResult: reverseGeocoderResult,
+					reverseGeocoderSimplify: reverseGeocoderSimplify,
+					pois: pois,
+					poisSimplify: poisSimplify
+				})
+			} else {
+				param.success(data, {
+					reverseGeocoderResult: reverseGeocoderResult,
+					reverseGeocoderSimplify: reverseGeocoderSimplify
+				})
+			}
+		} else if (feature == 'geocoder') {
+			var geocoderResult = data.result;
+			var geocoderSimplify = {
+				title: geocoderResult.title || null,
+				latitude: geocoderResult.location && geocoderResult.location.lat || null,
+				longitude: geocoderResult.location && geocoderResult.location.lng || null,
+				adcode: geocoderResult.ad_info && geocoderResult.ad_info.adcode || null,
+				province: geocoderResult.address_components && geocoderResult.address_components.province ||
+					null,
+				city: geocoderResult.address_components && geocoderResult.address_components.city || null,
+				district: geocoderResult.address_components && geocoderResult.address_components.district ||
+					null,
+				street: geocoderResult.address_components && geocoderResult.address_components.street || null,
+				street_number: geocoderResult.address_components && geocoderResult.address_components
+					.street_number || null,
+				level: geocoderResult.level || null
+			};
+			param.success(data, {
+				geocoderResult: geocoderResult,
+				geocoderSimplify: geocoderSimplify
+			})
+		} else if (feature == 'getCityList') {
+			var provinceResult = data.result[0];
+			var cityResult = data.result[1];
+			var districtResult = data.result[2];
+			param.success(data, {
+				provinceResult: provinceResult,
+				cityResult: cityResult,
+				districtResult: districtResult
+			})
+		} else if (feature == 'getDistrictByCityId') {
+			var districtByCity = data.result[0];
+			param.success(data, districtByCity)
+		} else if (feature == 'calculateDistance') {
+			var calculateDistanceResult = data.result.elements;
+			var distance = [];
+			for (var i = 0; i < calculateDistanceResult.length; i++) {
+				distance.push(calculateDistanceResult[i].distance)
+			}
+			param.success(data, {
+				calculateDistanceResult: calculateDistanceResult,
+				distance: distance
+			})
+		} else if (feature == 'direction') {
+			var direction = data.result.routes;
+			param.success(data, direction)
+		} else {
+			param.success(data)
+		}
+	},
+	buildWxRequestConfig(param, options, feature) {
+		var that = this;
+		options.header = {
+			"content-type": "application/json"
+		};
+		options.method = 'GET';
+		options.success = function(res) {
+			var data = res.data;
+			if (data.status === 0) {
+				that.handleData(param, data, feature)
+			} else {
+				param.fail(data)
+			}
+		};
+		options.fail = function(res) {
+			res.statusCode = ERROR_CONF.WX_ERR_CODE;
+			param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg))
+		};
+		options.complete = function(res) {
+			var statusCode = +res.statusCode;
+			switch (statusCode) {
+				case ERROR_CONF.WX_ERR_CODE: {
+					param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+					break
+				}
+				case ERROR_CONF.WX_OK_CODE: {
+					var data = res.data;
+					if (data.status === 0) {
+						param.complete(data)
+					} else {
+						param.complete(that.buildErrorConfig(data.status, data.message))
+					}
+					break
+				}
+				default: {
+					param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG))
+				}
+			}
+		};
+		return options
+	},
+	locationProcess(param, locationsuccess, locationfail, locationcomplete) {
+		var that = this;
+		locationfail = locationfail || function(res) {
+			res.statusCode = ERROR_CONF.WX_ERR_CODE;
+			param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg))
+		};
+		locationcomplete = locationcomplete || function(res) {
+			if (res.statusCode == ERROR_CONF.WX_ERR_CODE) {
+				param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg))
+			}
+		};
+		if (!param.location) {
+			that.getWXLocation(locationsuccess, locationfail, locationcomplete)
+		} else if (that.checkLocation(param)) {
+			var location = Utils.getLocationParam(param.location);
+			locationsuccess(location)
+		}
+	}
+};
+class QQMapWX {
+	constructor(options) {
+		if (!options.key) {
+			throw Error('key值不能为空')
+		}
+		this.key = options.key
+	};
+	search(options) {
+		var that = this;
+		options = options || {};
+		Utils.polyfillParam(options);
+		if (!Utils.checkKeyword(options)) {
+			return
+		}
+		var requestParam = {
+			keyword: options.keyword,
+			orderby: options.orderby || '_distance',
+			page_size: options.page_size || 10,
+			page_index: options.page_index || 1,
+			output: 'json',
+			key: that.key
+		};
+		if (options.address_format) {
+			requestParam.address_format = options.address_format
+		}
+		if (options.filter) {
+			requestParam.filter = options.filter
+		}
+		var distance = options.distance || "1000";
+		var auto_extend = options.auto_extend || 1;
+		var region = null;
+		var rectangle = null;
+		if (options.region) {
+			region = options.region
+		}
+		if (options.rectangle) {
+			rectangle = options.rectangle
+		}
+		var locationsuccess = function(result) {
+			if (region && !rectangle) {
+				requestParam.boundary = "region(" + region + "," + auto_extend + "," + result.latitude + "," +
+					result.longitude + ")";
+				if (options.sig) {
+					requestParam.sig = Utils.getSig(requestParam, options.sig, 'search')
+				}
+			} else if (rectangle && !region) {
+				requestParam.boundary = "rectangle(" + rectangle + ")";
+				if (options.sig) {
+					requestParam.sig = Utils.getSig(requestParam, options.sig, 'search')
+				}
+			} else {
+				requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance +
+					"," + auto_extend + ")";
+				if (options.sig) {
+					requestParam.sig = Utils.getSig(requestParam, options.sig, 'search')
+				}
+			}
+			wx.request(Utils.buildWxRequestConfig(options, {
+				url: URL_SEARCH,
+				data: requestParam
+			}, 'search'))
+		};
+		Utils.locationProcess(options, locationsuccess)
+	};
+	getSuggestion(options) {
+		var that = this;
+		options = options || {};
+		Utils.polyfillParam(options);
+		if (!Utils.checkKeyword(options)) {
+			return
+		}
+		var requestParam = {
+			keyword: options.keyword,
+			region: options.region || '全国',
+			region_fix: options.region_fix || 0,
+			policy: options.policy || 0,
+			page_size: options.page_size || 10,
+			page_index: options.page_index || 1,
+			get_subpois: options.get_subpois || 0,
+			output: 'json',
+			key: that.key
+		};
+		if (options.address_format) {
+			requestParam.address_format = options.address_format
+		}
+		if (options.filter) {
+			requestParam.filter = options.filter
+		}
+		if (options.location) {
+			var locationsuccess = function(result) {
+				requestParam.location = result.latitude + ',' + result.longitude;
+				if (options.sig) {
+					requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest')
+				}
+				wx.request(Utils.buildWxRequestConfig(options, {
+					url: URL_SUGGESTION,
+					data: requestParam
+				}, "suggest"))
+			};
+			Utils.locationProcess(options, locationsuccess)
+		} else {
+			if (options.sig) {
+				requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest')
+			}
+			wx.request(Utils.buildWxRequestConfig(options, {
+				url: URL_SUGGESTION,
+				data: requestParam
+			}, "suggest"))
+		}
+	};
+	reverseGeocoder(options) {
+		var that = this;
+		options = options || {};
+		Utils.polyfillParam(options);
+		var requestParam = {
+			coord_type: options.coord_type || 5,
+			get_poi: options.get_poi || 0,
+			output: 'json',
+			key: that.key
+		};
+		if (options.poi_options) {
+			requestParam.poi_options = options.poi_options
+		}
+		var locationsuccess = function(result) {
+			requestParam.location = result.latitude + ',' + result.longitude;
+			if (options.sig) {
+				requestParam.sig = Utils.getSig(requestParam, options.sig, 'reverseGeocoder')
+			}
+			wx.request(Utils.buildWxRequestConfig(options, {
+				url: URL_GET_GEOCODER,
+				data: requestParam
+			}, 'reverseGeocoder'))
+		};
+		Utils.locationProcess(options, locationsuccess)
+	};
+	geocoder(options) {
+		var that = this;
+		options = options || {};
+		Utils.polyfillParam(options);
+		if (Utils.checkParamKeyEmpty(options, 'address')) {
+			return
+		}
+		var requestParam = {
+			address: options.address,
+			output: 'json',
+			key: that.key
+		};
+		if (options.region) {
+			requestParam.region = options.region
+		}
+		if (options.sig) {
+			requestParam.sig = Utils.getSig(requestParam, options.sig, 'geocoder')
+		}
+		wx.request(Utils.buildWxRequestConfig(options, {
+			url: URL_GET_GEOCODER,
+			data: requestParam
+		}, 'geocoder'))
+	};
+	getCityList(options) {
+		var that = this;
+		options = options || {};
+		Utils.polyfillParam(options);
+		var requestParam = {
+			output: 'json',
+			key: that.key
+		};
+		if (options.sig) {
+			requestParam.sig = Utils.getSig(requestParam, options.sig, 'getCityList')
+		}
+		wx.request(Utils.buildWxRequestConfig(options, {
+			url: URL_CITY_LIST,
+			data: requestParam
+		}, 'getCityList'))
+	};
+	getDistrictByCityId(options) {
+		var that = this;
+		options = options || {};
+		Utils.polyfillParam(options);
+		if (Utils.checkParamKeyEmpty(options, 'id')) {
+			return
+		}
+		var requestParam = {
+			id: options.id || '',
+			output: 'json',
+			key: that.key
+		};
+		if (options.sig) {
+			requestParam.sig = Utils.getSig(requestParam, options.sig, 'getDistrictByCityId')
+		}
+		wx.request(Utils.buildWxRequestConfig(options, {
+			url: URL_AREA_LIST,
+			data: requestParam
+		}, 'getDistrictByCityId'))
+	};
+	calculateDistance(options) {
+		var that = this;
+		options = options || {};
+		Utils.polyfillParam(options);
+		if (Utils.checkParamKeyEmpty(options, 'to')) {
+			return
+		}
+		var requestParam = {
+			mode: options.mode || 'walking',
+			to: Utils.location2query(options.to),
+			output: 'json',
+			key: that.key
+		};
+		if (options.from) {
+			options.location = options.from
+		}
+		if (requestParam.mode == 'straight') {
+			var locationsuccess = function(result) {
+				var locationTo = Utils.getEndLocation(requestParam.to);
+				var data = {
+					message: "query ok",
+					result: {
+						elements: []
+					},
+					status: 0
+				};
+				for (var i = 0; i < locationTo.length; i++) {
+					data.result.elements.push({
+						distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i]
+							.lat, locationTo[i].lng),
+						duration: 0,
+						from: {
+							lat: result.latitude,
+							lng: result.longitude
+						},
+						to: {
+							lat: locationTo[i].lat,
+							lng: locationTo[i].lng
+						}
+					})
+				}
+				var calculateResult = data.result.elements;
+				var distanceResult = [];
+				for (var i = 0; i < calculateResult.length; i++) {
+					distanceResult.push(calculateResult[i].distance)
+				}
+				return options.success(data, {
+					calculateResult: calculateResult,
+					distanceResult: distanceResult
+				})
+			};
+			Utils.locationProcess(options, locationsuccess)
+		} else {
+			var locationsuccess = function(result) {
+				requestParam.from = result.latitude + ',' + result.longitude;
+				if (options.sig) {
+					requestParam.sig = Utils.getSig(requestParam, options.sig, 'calculateDistance')
+				}
+				wx.request(Utils.buildWxRequestConfig(options, {
+					url: URL_DISTANCE,
+					data: requestParam
+				}, 'calculateDistance'))
+			};
+			Utils.locationProcess(options, locationsuccess)
+		}
+	};
+	direction(options) {
+		var that = this;
+		options = options || {};
+		Utils.polyfillParam(options);
+		if (Utils.checkParamKeyEmpty(options, 'to')) {
+			return
+		}
+		var requestParam = {
+			output: 'json',
+			key: that.key
+		};
+		if (typeof options.to == 'string') {
+			requestParam.to = options.to
+		} else {
+			requestParam.to = options.to.latitude + ',' + options.to.longitude
+		}
+		var SET_URL_DIRECTION = null;
+		options.mode = options.mode || MODE.driving;
+		SET_URL_DIRECTION = URL_DIRECTION + options.mode;
+		if (options.from) {
+			options.location = options.from
+		}
+		if (options.mode == MODE.driving) {
+			if (options.from_poi) {
+				requestParam.from_poi = options.from_poi
+			}
+			if (options.heading) {
+				requestParam.heading = options.heading
+			}
+			if (options.speed) {
+				requestParam.speed = options.speed
+			}
+			if (options.accuracy) {
+				requestParam.accuracy = options.accuracy
+			}
+			if (options.road_type) {
+				requestParam.road_type = options.road_type
+			}
+			if (options.to_poi) {
+				requestParam.to_poi = options.to_poi
+			}
+			if (options.from_track) {
+				requestParam.from_track = options.from_track
+			}
+			if (options.waypoints) {
+				requestParam.waypoints = options.waypoints
+			}
+			if (options.policy) {
+				requestParam.policy = options.policy
+			}
+			if (options.plate_number) {
+				requestParam.plate_number = options.plate_number
+			}
+		}
+		if (options.mode == MODE.transit) {
+			if (options.departure_time) {
+				requestParam.departure_time = options.departure_time
+			}
+			if (options.policy) {
+				requestParam.policy = options.policy
+			}
+		}
+		var locationsuccess = function(result) {
+			requestParam.from = result.latitude + ',' + result.longitude;
+			if (options.sig) {
+				requestParam.sig = Utils.getSig(requestParam, options.sig, 'direction', options.mode)
+			}
+			wx.request(Utils.buildWxRequestConfig(options, {
+				url: SET_URL_DIRECTION,
+				data: requestParam
+			}, 'direction'))
+		};
+		Utils.locationProcess(options, locationsuccess)
+	}
+};
+export default QQMapWX

+ 21 - 0
common/tool.js

@@ -0,0 +1,21 @@
+
+import QQMapWX from './qqmap-wx-jssdk.min.js'
+export const getAddress = (lng, lat) => {
+	return new Promise((resove, reject) => {
+		const qqmapsdk = new QQMapWX({
+			key: '5WLBZ-H6RWU-NQVVO-BQG7I-XEHFK-PAFSF',
+		})
+		qqmapsdk.reverseGeocoder({
+			location: {
+				latitude: lat,
+				longitude: lng
+			},
+			success: (res) => {
+				resove(res)
+			},
+			fail: (e) => {
+				reject(e)
+			},
+		})
+	})
+}

文件差異過大導致無法顯示
+ 4233 - 0
config.js


+ 12 - 0
pages.json

@@ -80,6 +80,18 @@
 						"navigationBarTitleText": "签字"
 					}
 				},
+				{
+					"path": "address/index",
+					"style": {
+						"navigationBarTitleText": "收货地址"
+					}
+				},
+				{
+					"path": "address/add",
+					"style": {
+						"navigationBarTitleText": "新增地址"
+					}
+				},
 				{
 					"path": "notice/index",
 					"style": {

+ 319 - 0
pagesHome/address/add.vue

@@ -0,0 +1,319 @@
+<template>
+	<view class="main">
+		<view class="one">
+			<uni-forms ref="form" v-model="form" :rules="rules" label-width="auto">
+				<uni-forms-item label="收货人" name="name">
+					<input class="input" type="text" v-model="form.name" placeholder="请输入收货人" />
+				</uni-forms-item>
+				<uni-forms-item label="联系电话" name="phone">
+					<input class="input" type="number" v-model="form.phone" placeholder="请输入联系电话" />
+				</uni-forms-item>
+				<uni-forms-item label="地图选点">
+					<button type="default" size="mini" class="toLoacl" @click="toLocaltion()">地图选点</button>
+				</uni-forms-item>
+				<uni-forms-item label="所在省份" name="province">
+					<picker class="picker" mode="selector" :range="provinceList" @change="proChange" range-key="name">
+						<view>{{form.province||'请选择所在省份'}}</view>
+					</picker>
+				</uni-forms-item>
+				<uni-forms-item label="所在市" name="city">
+					<picker class="picker" mode="selector" :range="cityList" @change="cityChange" range-key="name">
+						<view>{{form.city||'请选择所在市'}}</view>
+					</picker>
+				</uni-forms-item>
+				<uni-forms-item label="所在区" name="area">
+					<picker class="picker" mode="selector" :range="areaList" @change="areaChange">
+						<view>{{form.area||'请选择所在区'}}</view>
+					</picker>
+				</uni-forms-item>
+				<uni-forms-item label="详细地址" name="address">
+					<textarea class="input" v-model="form.address" placeholder="请输入详细地址" />
+				</uni-forms-item>
+				<uni-forms-item label="是否默认" name="is_default">
+					<switch color="#FB1438" :checked="form.is_default=='1'?true:false" @change="defaChange" />
+				</uni-forms-item>
+			</uni-forms>
+			<view class="btn">
+				<button type="primary" size="mini" @click="onSubmit('form')">提交保存</button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		getAddress
+	} from '@/common/tool.js';
+	export default {
+		data() {
+			return {
+				user: {},
+				id: '',
+				form: {},
+				rules: {
+					name: {
+						rules: [ //
+							{
+								required: true,
+								errorMessage: '请输入收货人',
+							},
+							{
+								minLength: 2,
+								errorMessage: '收货人名称长度至少在 {minLength} 个字符以上',
+							}
+						]
+					},
+					phone: {
+						rules: [{
+							required: true,
+							errorMessage: '请输入联系电话',
+							tel: true
+						}]
+					},
+					province: {
+						rules: [{
+							required: true,
+							errorMessage: '请选择所在省'
+						}]
+					},
+					city: {
+						rules: [{
+							required: true,
+							errorMessage: '请选择所在市'
+						}]
+					},
+					area: {
+						rules: [{
+							required: false,
+							errorMessage: '请选择所在区'
+						}]
+					},
+					address: {
+						rules: [{
+							required: true,
+							errorMessage: '请输入详细地址'
+						}]
+					},
+				},
+				provinceList: [ //所在省份
+				],
+				cityList: [ //所在市
+				],
+				areaList: [ //所在区
+				],
+				paltForm: ''
+			};
+		},
+		onLoad: function(e) {
+			const that = this;
+			that.$set(that, `id`, e.id || '');
+			if (e.id) {
+				uni.setNavigationBarTitle({
+					title: '修改地址'
+				});
+			}
+			that.searchPor();
+			that.watchLogin();
+		},
+		onShow: function() {
+			const that = this;
+			let config = that.$config;
+			if (config) {
+				// 账号平台
+				that.$set(that, `paltForm`, config.system.uniPlatform)
+			}
+		},
+		methods: {
+			// 监听用户是否登录
+			watchLogin() {
+				const that = this;
+				uni.getStorage({
+					key: 'token',
+					success: async function(res) {
+						let user = that.$jwt(res.data);
+						if (user) {
+							that.$set(that, `user`, user);
+							if (that.id) {
+								let arr = await that.$api(`address/${that.id}`, 'GET')
+								if (arr.errcode == '0') {
+									let index = that.provinceList.findIndex((i) => i.name == arr.data
+										.province);
+									if (index) {
+										that.proChange(parseFloat(index));
+									}
+									that.$set(that, `form`, arr.data)
+								}
+							} else {
+								that.$set(that, `form`, {
+									name: user.name || '',
+									phone: user.phone || ''
+								})
+							}
+						}
+
+					},
+					fail: function(err) {
+						uni.navigateTo({
+							url: '/pagesHome/login/index'
+						})
+					}
+				})
+			},
+			// 查找省
+			searchPor() {
+				const that = this;
+				let list = that.$config.china;
+				that.$set(that, `provinceList`, list)
+			},
+			// 定位
+			toLocaltion() {
+				const that = this;
+				uni.chooseLocation({
+					success: function(res) {
+						that.searchAddress(res);
+					}
+				})
+			},
+			// 解析位置详细信息
+			searchAddress(e) {
+				const that = this;
+				that.$set(that.form, `address`, e.address);
+				if (that.paltForm == 'app') {
+					var point = new plus.maps.Point(e.longitude, e.latitude);
+					plus.maps.Map.reverseGeocode(point, {}, function(event) {
+						var address = event.address;
+						var reg = /.+?(省|市|自治区|自治州|县|区)/g;
+						let addressList = address.match(reg).toString().split(",");
+						let province = addressList[0] || '';
+						let city = addressList[1] || '';
+						let area = addressList[2] || '';
+						that.$set(that.form, `province`, province);
+						that.$set(that.form, `city`, city);
+						that.$set(that.form, `area`, area);
+					})
+				} else if (that.paltForm == 'mp-weixin') {
+					getAddress(e.longitude, e.latitude).then((res) => {
+						if (res.status == 0) {
+							let province = res.result.address_component.province;
+							let city = res.result.address_component.city;
+							let area = res.result.address_component.district;
+							that.$set(that.form, `province`, province);
+							that.$set(that.form, `city`, city);
+							that.$set(that.form, `area`, area);
+						} else {
+							uni.showToast({
+								title: '解析位置失败,请重新选择!'
+							});
+						}
+					})
+				}
+			},
+			// 选择省份
+			proChange(e) {
+				const that = this;
+				let index = 0;
+				if (e && e.detail && e.detail.value) index = e.detail.value
+				else index = e;
+				let data = that.provinceList[index];
+				if (data) {
+					if (that.id) {
+						that.$set(that.form, `city`, '');
+						that.$set(that.form, `area`, '');
+					}
+					that.$set(that.form, `province`, data.name);
+					that.$set(that, `cityList`, data.city)
+				}
+			},
+			// 选择市
+			cityChange(e) {
+				const that = this;
+				let data = that.cityList[e.detail.value];
+				if (data) {
+					if (that.id) {
+						that.$set(that.form, `area`, '');
+					}
+					that.$set(that.form, `city`, data.name);
+					that.$set(that, `areaList`, data.area);
+				}
+			},
+			// 选择区
+			areaChange(e) {
+				const that = this;
+				let data = that.areaList[e.detail.value];
+				if (data) {
+					that.$set(that.form, `area`, data)
+				}
+			},
+			// 是否默认
+			defaChange(e) {
+				const that = this;
+				let value = e.detail.value == true ? '1' : '0';
+				that.$set(that.form, `is_default`, value)
+			},
+			// 提交保存
+			onSubmit(ref) {
+				const that = this;
+				let id = that.id;
+				let user = that.user;
+				that.$refs[ref].validate().then(async params => {
+					params.customer = user.id;
+					let res;
+					if (id) res = await that.$api(`address/${id}`, 'POST', params)
+					else res = await that.$api(`address`, 'POST', params);
+					if (res.errcode == '0') {
+						uni.showToast({
+							title: '维护信息成功',
+							icon: 'none'
+						})
+						uni.navigateBack({
+							delta: 1
+						})
+					} else {
+						uni.showToast({
+							title: res.errmsg,
+							icon: 'none'
+						})
+					}
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.main {
+		display: flex;
+		flex-direction: column;
+
+		.one {
+			padding: 2vw;
+
+			.toLoacl {
+				margin: 1vw 0 0 0;
+			}
+
+			.input {
+				width: 94%;
+				padding: 2vw;
+				border: 1px solid #3333;
+				border-radius: 5px;
+			}
+
+			.picker {
+				border: 1px solid #3333;
+				border-radius: 5px;
+				padding: 2vw;
+			}
+
+			.btn {
+				text-align: center;
+
+				button {
+					width: 30%;
+					font-size: 14px;
+					background-color: var(--fFFColor);
+				}
+			}
+		}
+	}
+</style>

+ 364 - 0
pagesHome/address/index.vue

@@ -0,0 +1,364 @@
+<template>
+	<view class="main">
+		<view class="one">
+			<scroll-view scroll-y="true" class="scroll-view" @scrolltolower="toPage" @scroll="toScroll">
+				<radio-group @change="addressChange">
+					<view class="list" v-for="(item,index) in list" :key="index">
+						<label class="radio">
+							<radio :value="item._id" :checked="item._id == address_id" />
+						</label>
+						<view class="list_1">
+							<view class="name">
+								<text>{{item.name}}</text>
+								<text>{{item.phone}}</text>
+							</view>
+							<view class="address">
+								<text>{{item.province}}</text>
+								<text>{{item.city}}</text>
+								<text>{{item.area}}</text>
+								<text>{{item.address}}</text>
+								<text>{{item.number}}</text>
+							</view>
+							<view class="btn">
+								<button class="btn_1" size="mini" @click="toDefa(item)"
+									v-if="item.is_default==false">设为默认</button>
+								<button class="btn_2" size="mini" @click="toCommon(item)">编辑</button>
+								<button class="btn_3" size="mini" @click="toDel(item)">删除</button>
+							</view>
+							<view class="default" v-if="item.is_default==true">
+								<text>默认</text>
+							</view>
+						</view>
+					</view>
+					<view class="is_bottom" v-if="is_bottom">
+						<text>{{config.bottom_title}}</text>
+					</view>
+				</radio-group>
+			</scroll-view>
+		</view>
+		<view class="two">
+			<view class="two_1">
+				<button size="mini" type="default" @click="toWxaddress()">获取微信地址</button>
+			</view>
+			<view class="two_1">
+				<button size="mini" type="default" @click="toCommon()">新增收货地址</button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script setup lang="ts">
+	import { getCurrentInstance, ref } from 'vue';
+	//该依赖已内置不需要单独安装
+	import { onLoad, onHide, onShow, onPullDownRefresh } from "@dcloudio/uni-app";
+	// 请求接口
+	const $api = getCurrentInstance()?.appContext.config.globalProperties.$api;
+	const $config = getCurrentInstance()?.appContext.config.globalProperties.$config;
+	// 基本信息
+	const config = ref({ logo: [] });
+	const user = ref({});
+	// id
+	const address_id = ref('');
+	// 列表
+	const list = ref([]);
+	const total = ref(0);
+	const skip = ref(0);
+	const limit = ref(6);
+	const page = ref(0);
+	// 数据是否触底
+	const is_bottom = ref(false);
+	const scrollTop = ref(0);
+	onLoad(async (options) => {
+		address_id.value = options && options.id
+	})
+	onShow(async () => {
+		await searchConfig();
+		await searchUser();
+		await clearPage();
+		await search();
+	})
+	onPullDownRefresh(async () => {
+		await clearPage();
+		await search();
+		uni.stopPullDownRefresh();
+	})
+	onHide(async () => {
+		await clearPage();
+	})
+	// config信息
+	const searchConfig = async () => {
+		config.value = uni.getStorageSync('config');
+	};
+	// 用户信息
+	const searchUser = async () => {
+		user.value = uni.getStorageSync('user');
+	};
+	// 查询
+	const search = async () => {
+		if (user.value._id) {
+			const info = {
+				skip: skip.value,
+				limit: limit.value,
+				user: user.value._id,
+				is_use: '0'
+			}
+			const res = await $api('address', 'GET', info);
+			if (res.errcode === 0) {
+				list.value = list.value.concat(res.data)
+				total.value = res.total
+			} else {
+				uni.showToast({
+					title: res.errmsg || '',
+					icon: 'error',
+				});
+			}
+		} else {
+			uni.navigateTo({
+				url: `/pagesHome/login/index`
+			})
+		}
+	};
+	// 选择收货地址
+	const addressChange = async (e) => {
+		let data = list.value.find((i) => i._id == e.detail.value);
+		if (data) {
+			address_id.value == data._id;
+			uni.$emit("id", e.detail.value);
+			uni.navigateBack({
+				delta: 1
+			});
+		}
+	};
+	// 设置默认
+	const toDefa = async (e) => {
+		uni.showModal({
+			title: '提示',
+			content: '确定设置该地址为默认地址吗?',
+			success: async function (res) {
+				if (res.confirm) {
+					const arr = await $api(`address/toDefault/${e._id}`, `POST`);
+					if (arr.errcode == '0') {
+						uni.showToast({
+							title: '添加默认成功',
+							icon: 'none'
+						})
+						await clearPage()
+						await search()
+					} else {
+						uni.showToast({
+							title: arr.errmsg,
+							icon: 'none'
+						})
+					}
+				}
+			}
+		});
+	};
+	// 获取微信地址
+	const toWxaddress = async () => {
+		uni.chooseAddress({
+			success: async function (add) {
+				let params = {
+					user: user.value._id,
+					name: add.userName,
+					phone: add.telNumber,
+					province: add.provinceName,
+					city: add.cityName,
+					area: add.countyName,
+					address: add.detailInfo
+				}
+				const arr = await $api(`address`, 'POST', params);
+				if (arr.errcode == '0') {
+					uni.showToast({
+						title: '添加收货地址成功',
+						icon: 'none'
+					})
+					clearPage();
+				} else {
+					uni.showToast({
+						title: arr.errmsg,
+						icon: 'none'
+					})
+				}
+			},
+			fail: function () {
+				clearPage();
+			}
+		})
+	};
+	// 编辑
+	const toCommon = async (e) => {
+		uni.navigateTo({
+			url: `/pagesHome/address/add?id=${e && e._id || ''}`
+		})
+	};
+	// 删除
+	const toDel = async (e) => {
+		uni.showModal({
+			title: '提示',
+			content: '确定删除该地址吗?',
+			success: async function (res) {
+				if (res.confirm) {
+					const arr = await $api(`address/${e._id}`, 'DELETE');
+					if (arr.errcode == '0') {
+						uni.showToast({
+							title: '删除信息成功',
+							icon: 'none'
+						})
+						if (address_id.value == e._id) uni.$emit("id", '')
+						await clearPage()
+						await search()
+					} else {
+						uni.showToast({
+							title: arr.errmsg,
+							icon: 'none'
+						})
+					}
+				}
+			}
+		});
+	};
+	// 分页
+	const toPage = () => {
+		if (total.value > list.value.length) {
+			uni.showLoading({
+				title: '加载中',
+				mask: true
+			})
+			page.value = page.value + 1;
+			skip.value = page.value * limit.value;
+			search();
+			uni.hideLoading();
+		} else is_bottom.value = true
+	};
+	// 清空列表
+	const clearPage = () => {
+		list.value = []
+		skip.value = 0
+		limit.value = 6
+		page.value = 0
+	};
+</script>
+
+<style lang="scss">
+	.main {
+		display: flex;
+		flex-direction: column;
+		width: 100vw;
+		height: 100vh;
+		background-color: var(--f1Color);
+
+		.one {
+			position: relative;
+			flex-grow: 1;
+
+			.list {
+				display: flex;
+				background: var(--fffColor);
+				margin: 2vw 2vw 0 2vw;
+				padding: 2vw;
+				border-radius: 5px;
+				position: relative;
+
+				.list_1 {
+					flex-grow: 1;
+
+					.name {
+						font-size: var(--font16Size);
+						margin: 0 0 2vw 0;
+
+						text {
+							padding: 0 2vw 0 0;
+						}
+					}
+
+					.address {
+						font-size: var(--font14Size);
+						margin: 0 0 1vw 0;
+
+						text {
+							padding: 0 2vw 0 0;
+						}
+					}
+
+					.btn {
+						text-align: center;
+
+						button {
+							margin: 0 2vw 2vw 2vw;
+							background-color: var(--f35BColor);
+							color: var(--fffColor);
+						}
+
+						.btn_2 {
+							background-color: var(--fF0Color);
+						}
+
+						.btn_3 {
+							background-color: var(--fFB1Color);
+						}
+					}
+
+					.default {
+						position: absolute;
+						top: 0;
+						right: 0;
+
+						text {
+							background: var(--fFB1Color);
+							font-size: var(--font12Size);
+							padding: 1vw;
+							border-radius: 5px;
+						}
+					}
+				}
+			}
+		}
+
+		.two {
+			display: flex;
+			justify-content: space-around;
+
+			.two_1 {
+				margin: 0 0 2vw 0;
+
+				button {
+					color: var(--mainColor);
+					background-color: var(--fFFColor);
+				}
+
+
+			}
+
+			.two_1:nth-child(2) {
+				button {
+					background-color: var(--fFFColor);
+				}
+			}
+		}
+	}
+
+	.scroll-view {
+		position: absolute;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+
+		.list-scroll-view {
+			display: flex;
+			flex-direction: column;
+		}
+	}
+
+	.is_bottom {
+		text-align: center;
+
+		text {
+			padding: 2vw 0;
+			display: inline-block;
+			color: #858585;
+			font-size: 14px;
+		}
+	}
+</style>

+ 7 - 1
pagesHome/notarization/detail.vue

@@ -39,7 +39,7 @@
 						</view>
 						<view class="two_3" v-if="is_open">
 							<view class="left">请选择收货地址</view>
-							<view class="right">
+							<view class="right" @tap="toAddress">
 								<text>{{data.address||''}}</text>
 								<u-icon name="arrow-right" size="18"></u-icon>
 							</view>
@@ -172,6 +172,12 @@
 			url: `/pagesHome/notarization/sign?id=${info.value._id}`
 		})
 	};
+	// 选择地址
+	const toAddress = () => {
+		uni.navigateTo({
+			url: `/pagesHome/address/index?id=${info.value._id}`
+		})
+	};
 </script>
 <style lang="scss" scoped>
 	.content {

+ 0 - 6
pagesHome/notarization/sign.vue

@@ -172,9 +172,6 @@
 	}
 
 	.sign-box {
-		width: 80%;
-		height: 90%;
-		margin: auto;
 		display: flex;
 		flex-direction: column;
 		text-align: center;
@@ -186,15 +183,12 @@
 
 	.sigh-btns {
 		height: 100%;
-		margin: auto;
 		display: flex;
 		flex-direction: column;
 		justify-content: space-around;
 	}
 
 	.btn {
-		margin: auto;
-		padding: 8rpx;
 		transform: rotate(90deg);
 		border: grey 1rpx solid;
 	}

+ 94 - 0
uni_modules/uni-forms/changelog.md

@@ -0,0 +1,94 @@
+## 1.4.10(2023-11-03)
+- 优化 labelWidth 描述错误
+## 1.4.9(2023-02-10)
+- 修复 required 参数无法动态绑定
+## 1.4.8(2022-08-23)
+- 优化 根据 rules 自动添加 required 的问题
+## 1.4.7(2022-08-22)
+- 修复 item 未设置 require 属性,rules 设置 require 后,星号也显示的 bug,详见:[https://ask.dcloud.net.cn/question/151540](https://ask.dcloud.net.cn/question/151540)
+## 1.4.6(2022-07-13)
+- 修复 model 需要校验的值没有声明对应字段时,导致第一次不触发校验的bug
+## 1.4.5(2022-07-05)
+- 新增 更多表单示例
+- 优化 子表单组件过期提示的问题
+- 优化 子表单组件uni-datetime-picker、uni-data-select、uni-data-picker的显示样式
+## 1.4.4(2022-07-04)
+- 更新 删除组件日志
+## 1.4.3(2022-07-04)
+- 修复 由 1.4.0 引发的 label 插槽不生效的bug
+## 1.4.2(2022-07-04)
+- 修复 子组件找不到 setValue 报错的bug
+## 1.4.1(2022-07-04)
+- 修复 uni-data-picker 在 uni-forms-item 中报错的bug
+- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug
+## 1.4.0(2022-06-30)
+- 【重要】组件逻辑重构,部分用法用旧版本不兼容,请注意兼容问题
+- 【重要】组件使用 Provide/Inject 方式注入依赖,提供了自定义表单组件调用 uni-forms 校验表单的能力
+- 新增 model 属性,等同于原 value/modelValue 属性,旧属性即将废弃
+- 新增 validateTrigger 属性的 blur 值,仅 uni-easyinput 生效
+- 新增 onFieldChange 方法,可以对子表单进行校验,可替代binddata方法
+- 新增 子表单的 setRules 方法,配合自定义校验函数使用
+- 新增 uni-forms-item 的 setRules 方法,配置动态表单使用可动态更新校验规则
+- 优化 动态表单校验方式,废弃拼接name的方式
+## 1.3.3(2022-06-22)
+- 修复 表单校验顺序无序问题
+## 1.3.2(2021-12-09)
+-
+## 1.3.1(2021-11-19)
+- 修复 label 插槽不生效的bug
+## 1.3.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-forms](https://uniapp.dcloud.io/component/uniui/uni-forms)
+## 1.2.7(2021-08-13)
+- 修复 没有添加校验规则的字段依然报错的Bug
+## 1.2.6(2021-08-11)
+- 修复 重置表单错误信息无法清除的问题
+## 1.2.5(2021-08-11)
+- 优化 组件文档
+## 1.2.4(2021-08-11)
+- 修复 表单验证只生效一次的问题
+## 1.2.3(2021-07-30)
+- 优化 vue3下事件警告的问题
+## 1.2.2(2021-07-26)
+- 修复 vue2 下条件编译导致destroyed生命周期失效的Bug
+- 修复 1.2.1 引起的示例在小程序平台报错的Bug
+## 1.2.1(2021-07-22)
+- 修复 动态校验表单,默认值为空的情况下校验失效的Bug
+- 修复 不指定name属性时,运行报错的Bug
+- 优化 label默认宽度从65调整至70,使required为true且四字时不换行
+- 优化 组件示例,新增动态校验示例代码
+- 优化 组件文档,使用方式更清晰
+## 1.2.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.2(2021-06-25)
+- 修复 pattern 属性在微信小程序平台无效的问题
+## 1.1.1(2021-06-22)
+- 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug
+## 1.1.0(2021-06-22)
+- 修复 只写setRules方法而导致校验不生效的Bug
+- 修复 由上个办法引发的错误提示文字错位的Bug
+## 1.0.48(2021-06-21)
+- 修复 不设置 label 属性 ,无法设置label插槽的问题
+## 1.0.47(2021-06-21)
+- 修复 不设置label属性,label-width属性不生效的bug
+- 修复 setRules 方法与rules属性冲突的问题
+## 1.0.46(2021-06-04)
+- 修复 动态删减数据导致报错的问题
+## 1.0.45(2021-06-04)
+- 新增 modelValue 属性 ,value 即将废弃
+## 1.0.44(2021-06-02)
+- 新增 uni-forms-item 可以设置单独的 rules
+- 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤
+- 优化 submit 事件重命名为 validate
+## 1.0.43(2021-05-12)
+- 新增 组件示例地址
+## 1.0.42(2021-04-30)
+- 修复 自定义检验器失效的问题
+## 1.0.41(2021-03-05)
+- 更新 校验器
+- 修复 表单规则设置类型为 number 的情况下,值为0校验失败的Bug
+## 1.0.40(2021-03-04)
+- 修复 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug
+## 1.0.39(2021-02-05)
+- 调整为uni_modules目录规范
+- 修复 校验器传入 int 等类型 ,返回String类型的Bug

+ 627 - 0
uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue

@@ -0,0 +1,627 @@
+<template>
+	<view class="uni-forms-item"
+		:class="['is-direction-' + localLabelPos ,border?'uni-forms-item--border':'' ,border && isFirstBorder?'is-first-border':'']">
+		<slot name="label">
+			<view class="uni-forms-item__label" :class="{'no-label':!label && !required}"
+				:style="{width:localLabelWidth,justifyContent: localLabelAlign}">
+				<text v-if="required" class="is-required">*</text>
+				<text>{{label}}</text>
+			</view>
+		</slot>
+		<!-- #ifndef APP-NVUE -->
+		<view class="uni-forms-item__content">
+			<slot></slot>
+			<view class="uni-forms-item__error" :class="{'msg--active':msg}">
+				<text>{{msg}}</text>
+			</view>
+		</view>
+		<!-- #endif -->
+		<!-- #ifdef APP-NVUE -->
+		<view class="uni-forms-item__nuve-content">
+			<view class="uni-forms-item__content">
+				<slot></slot>
+			</view>
+			<view class="uni-forms-item__error" :class="{'msg--active':msg}">
+				<text class="error-text">{{msg}}</text>
+			</view>
+		</view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+	/**
+	 * uni-fomrs-item 表单子组件
+	 * @description uni-fomrs-item 表单子组件,提供了基础布局已经校验能力
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=2773
+	 * @property {Boolean} required 是否必填,左边显示红色"*"号
+	 * @property {String } 	label 				输入框左边的文字提示
+	 * @property {Number } 	labelWidth 			label的宽度,单位px(默认70)
+	 * @property {String } 	labelAlign = [left|center|right] label的文字对齐方式(默认left)
+	 * 	@value left		label 左侧显示
+	 * 	@value center	label 居中
+	 * 	@value right	label 右侧对齐
+	 * @property {String } 	errorMessage 		显示的错误提示内容,如果为空字符串或者false,则不显示错误信息
+	 * @property {String } 	name 				表单域的属性名,在使用校验规则时必填
+	 * @property {String } 	leftIcon 			【1.4.0废弃】label左边的图标,限 uni-ui 的图标名称
+	 * @property {String } 	iconColor 		【1.4.0废弃】左边通过icon配置的图标的颜色(默认#606266)
+	 * @property {String} validateTrigger = [bind|submit|blur]	【1.4.0废弃】校验触发器方式 默认 submit
+	 * 	@value bind 	发生变化时触发
+	 * 	@value submit 提交时触发
+	 * 	@value blur 	失去焦点触发
+	 * @property {String } 	labelPosition = [top|left] 【1.4.0废弃】label的文字的位置(默认left)
+	 * 	@value top	顶部显示 label
+	 * 	@value left	左侧显示 label
+	 */
+
+	export default {
+		name: 'uniFormsItem',
+		options: {
+			virtualHost: true
+		},
+		provide() {
+			return {
+				uniFormItem: this
+			}
+		},
+		inject: {
+			form: {
+				from: 'uniForm',
+				default: null
+			},
+		},
+		props: {
+			// 表单校验规则
+			rules: {
+				type: Array,
+				default () {
+					return null;
+				}
+			},
+			// 表单域的属性名,在使用校验规则时必填
+			name: {
+				type: [String, Array],
+				default: ''
+			},
+			required: {
+				type: Boolean,
+				default: false
+			},
+			label: {
+				type: String,
+				default: ''
+			},
+			// label的宽度
+			labelWidth: {
+				type: [String, Number],
+				default: ''
+			},
+			// label 居中方式,默认 left 取值 left/center/right
+			labelAlign: {
+				type: String,
+				default: ''
+			},
+			// 强制显示错误信息
+			errorMessage: {
+				type: [String, Boolean],
+				default: ''
+			},
+			// 1.4.0 弃用,统一使用 form 的校验时机
+			// validateTrigger: {
+			// 	type: String,
+			// 	default: ''
+			// },
+			// 1.4.0 弃用,统一使用 form 的label 位置
+			// labelPosition: {
+			// 	type: String,
+			// 	default: ''
+			// },
+			// 1.4.0 以下属性已经废弃,请使用  #label 插槽代替
+			leftIcon: String,
+			iconColor: {
+				type: String,
+				default: '#606266'
+			},
+		},
+		data() {
+			return {
+				errMsg: '',
+				userRules: null,
+				localLabelAlign: 'left',
+				localLabelWidth: '70px',
+				localLabelPos: 'left',
+				border: false,
+				isFirstBorder: false,
+			};
+		},
+		computed: {
+			// 处理错误信息
+			msg() {
+				return this.errorMessage || this.errMsg;
+			}
+		},
+		watch: {
+			// 规则发生变化通知子组件更新
+			'form.formRules'(val) {
+				// TODO 处理头条vue3 watch不生效的问题
+				// #ifndef MP-TOUTIAO
+				this.init()
+				// #endif
+			},
+			'form.labelWidth'(val) {
+				// 宽度
+				this.localLabelWidth = this._labelWidthUnit(val)
+
+			},
+			'form.labelPosition'(val) {
+				// 标签位置
+				this.localLabelPos = this._labelPosition()
+			},
+			'form.labelAlign'(val) {
+
+			}
+		},
+		created() {
+			this.init(true)
+			if (this.name && this.form) {
+				// TODO 处理头条vue3 watch不生效的问题
+				// #ifdef MP-TOUTIAO
+				this.$watch('form.formRules', () => {
+					this.init()
+				})
+				// #endif
+
+				// 监听变化
+				this.$watch(
+					() => {
+						const val = this.form._getDataValue(this.name, this.form.localData)
+						return val
+					},
+					(value, oldVal) => {
+						const isEqual = this.form._isEqual(value, oldVal)
+						// 简单判断前后值的变化,只有发生变化才会发生校验
+						// TODO  如果 oldVal = undefined ,那么大概率是源数据里没有值导致 ,这个情况不哦校验 ,可能不严谨 ,需要在做观察
+						// fix by mehaotian 暂时取消 && oldVal !== undefined ,如果formData 中不存在,可能会不校验
+						if (!isEqual) {
+							const val = this.itemSetValue(value)
+							this.onFieldChange(val, false)
+						}
+					}, {
+						immediate: false
+					}
+				);
+			}
+
+		},
+		// #ifndef VUE3
+		destroyed() {
+			if (this.__isUnmounted) return
+			this.unInit()
+		},
+		// #endif
+		// #ifdef VUE3
+		unmounted() {
+			this.__isUnmounted = true
+			this.unInit()
+		},
+		// #endif
+		methods: {
+			/**
+			 * 外部调用方法
+			 * 设置规则 ,主要用于小程序自定义检验规则
+			 * @param {Array} rules 规则源数据
+			 */
+			setRules(rules = null) {
+				this.userRules = rules
+				this.init(false)
+			},
+			// 兼容老版本表单组件
+			setValue() {
+				// console.log('setValue 方法已经弃用,请使用最新版本的 uni-forms 表单组件以及其他关联组件。');
+			},
+			/**
+			 * 外部调用方法
+			 * 校验数据
+			 * @param {any} value 需要校验的数据
+			 * @param {boolean} 是否立即校验
+			 * @return {Array|null} 校验内容
+			 */
+			async onFieldChange(value, formtrigger = true) {
+				const {
+					formData,
+					localData,
+					errShowType,
+					validateCheck,
+					validateTrigger,
+					_isRequiredField,
+					_realName
+				} = this.form
+				const name = _realName(this.name)
+				if (!value) {
+					value = this.form.formData[name]
+				}
+				// fixd by mehaotian 不在校验前清空信息,解决闪屏的问题
+				// this.errMsg = '';
+
+				// fix by mehaotian 解决没有检验规则的情况下,抛出错误的问题
+				const ruleLen = this.itemRules.rules && this.itemRules.rules.length
+				if (!this.validator || !ruleLen || ruleLen === 0) return;
+
+				// 检验时机
+				// let trigger = this.isTrigger(this.itemRules.validateTrigger, this.validateTrigger, validateTrigger);
+				const isRequiredField = _isRequiredField(this.itemRules.rules || []);
+				let result = null;
+				// 只有等于 bind 时 ,才能开启时实校验
+				if (validateTrigger === 'bind' || formtrigger) {
+					// 校验当前表单项
+					result = await this.validator.validateUpdate({
+							[name]: value
+						},
+						formData
+					);
+
+					// 判断是否必填,非必填,不填不校验,填写才校验 ,暂时只处理 undefined  和空的情况
+					if (!isRequiredField && (value === undefined || value === '')) {
+						result = null;
+					}
+
+					// 判断错误信息显示类型
+					if (result && result.errorMessage) {
+						if (errShowType === 'undertext') {
+							// 获取错误信息
+							this.errMsg = !result ? '' : result.errorMessage;
+						}
+						if (errShowType === 'toast') {
+							uni.showToast({
+								title: result.errorMessage || '校验错误',
+								icon: 'none'
+							});
+						}
+						if (errShowType === 'modal') {
+							uni.showModal({
+								title: '提示',
+								content: result.errorMessage || '校验错误'
+							});
+						}
+					} else {
+						this.errMsg = ''
+					}
+					// 通知 form 组件更新事件
+					validateCheck(result ? result : null)
+				} else {
+					this.errMsg = ''
+				}
+				return result ? result : null;
+			},
+			/**
+			 * 初始组件数据
+			 */
+			init(type = false) {
+				const {
+					validator,
+					formRules,
+					childrens,
+					formData,
+					localData,
+					_realName,
+					labelWidth,
+					_getDataValue,
+					_setDataValue
+				} = this.form || {}
+				// 对齐方式
+				this.localLabelAlign = this._justifyContent()
+				// 宽度
+				this.localLabelWidth = this._labelWidthUnit(labelWidth)
+				// 标签位置
+				this.localLabelPos = this._labelPosition()
+				// 将需要校验的子组件加入form 队列
+				this.form && type && childrens.push(this)
+
+				if (!validator || !formRules) return
+				// 判断第一个 item
+				if (!this.form.isFirstBorder) {
+					this.form.isFirstBorder = true;
+					this.isFirstBorder = true;
+				}
+
+				// 判断 group 里的第一个 item
+				if (this.group) {
+					if (!this.group.isFirstBorder) {
+						this.group.isFirstBorder = true;
+						this.isFirstBorder = true;
+					}
+				}
+				this.border = this.form.border;
+				// 获取子域的真实名称
+				const name = _realName(this.name)
+				const itemRule = this.userRules || this.rules
+				if (typeof formRules === 'object' && itemRule) {
+					// 子规则替换父规则
+					formRules[name] = {
+						rules: itemRule
+					}
+					validator.updateSchema(formRules);
+				}
+				// 注册校验规则
+				const itemRules = formRules[name] || {}
+				this.itemRules = itemRules
+				// 注册校验函数
+				this.validator = validator
+				// 默认值赋予
+				this.itemSetValue(_getDataValue(this.name, localData))
+			},
+			unInit() {
+				if (this.form) {
+					const {
+						childrens,
+						formData,
+						_realName
+					} = this.form
+					childrens.forEach((item, index) => {
+						if (item === this) {
+							this.form.childrens.splice(index, 1)
+							delete formData[_realName(item.name)]
+						}
+					})
+				}
+			},
+			// 设置item 的值
+			itemSetValue(value) {
+				const name = this.form._realName(this.name)
+				const rules = this.itemRules.rules || []
+				const val = this.form._getValue(name, value, rules)
+				this.form._setDataValue(name, this.form.formData, val)
+				return val
+			},
+
+			/**
+			 * 移除该表单项的校验结果
+			 */
+			clearValidate() {
+				this.errMsg = '';
+			},
+
+			// 是否显示星号
+			_isRequired() {
+				// TODO 不根据规则显示 星号,考虑后续兼容
+				// if (this.form) {
+				// 	if (this.form._isRequiredField(this.itemRules.rules || []) && this.required) {
+				// 		return true
+				// 	}
+				// 	return false
+				// }
+				return this.required
+			},
+
+			// 处理对齐方式
+			_justifyContent() {
+				if (this.form) {
+					const {
+						labelAlign
+					} = this.form
+					let labelAli = this.labelAlign ? this.labelAlign : labelAlign;
+					if (labelAli === 'left') return 'flex-start';
+					if (labelAli === 'center') return 'center';
+					if (labelAli === 'right') return 'flex-end';
+				}
+				return 'flex-start';
+			},
+			// 处理 label宽度单位 ,继承父元素的值
+			_labelWidthUnit(labelWidth) {
+
+				// if (this.form) {
+				// 	const {
+				// 		labelWidth
+				// 	} = this.form
+				return this.num2px(this.labelWidth ? this.labelWidth : (labelWidth || (this.label ? 70 : 'auto')))
+				// }
+				// return '70px'
+			},
+			// 处理 label 位置
+			_labelPosition() {
+				if (this.form) return this.form.labelPosition || 'left'
+				return 'left'
+
+			},
+
+			/**
+			 * 触发时机
+			 * @param {Object} rule 当前规则内时机
+			 * @param {Object} itemRlue 当前组件时机
+			 * @param {Object} parentRule 父组件时机
+			 */
+			isTrigger(rule, itemRlue, parentRule) {
+				//  bind  submit
+				if (rule === 'submit' || !rule) {
+					if (rule === undefined) {
+						if (itemRlue !== 'bind') {
+							if (!itemRlue) {
+								return parentRule === '' ? 'bind' : 'submit';
+							}
+							return 'submit';
+						}
+						return 'bind';
+					}
+					return 'submit';
+				}
+				return 'bind';
+			},
+			num2px(num) {
+				if (typeof num === 'number') {
+					return `${num}px`
+				}
+				return num
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	.uni-forms-item {
+		position: relative;
+		display: flex;
+		/* #ifdef APP-NVUE */
+		// 在 nvue 中,使用 margin-bottom error 信息会被隐藏
+		padding-bottom: 22px;
+		/* #endif */
+		/* #ifndef APP-NVUE */
+		margin-bottom: 22px;
+		/* #endif */
+		flex-direction: row;
+
+		&__label {
+			display: flex;
+			flex-direction: row;
+			align-items: center;
+			text-align: left;
+			font-size: 14px;
+			color: #606266;
+			height: 36px;
+			padding: 0 12px 0 0;
+			/* #ifndef APP-NVUE */
+			vertical-align: middle;
+			flex-shrink: 0;
+			/* #endif */
+
+			/* #ifndef APP-NVUE */
+			box-sizing: border-box;
+
+			/* #endif */
+			&.no-label {
+				padding: 0;
+			}
+		}
+
+		&__content {
+			/* #ifndef MP-TOUTIAO */
+			// display: flex;
+			// align-items: center;
+			/* #endif */
+			position: relative;
+			font-size: 14px;
+			flex: 1;
+			/* #ifndef APP-NVUE */
+			box-sizing: border-box;
+			/* #endif */
+			flex-direction: row;
+
+			/* #ifndef APP || H5 || MP-WEIXIN || APP-NVUE */
+			// TODO 因为小程序平台会多一层标签节点 ,所以需要在多余节点继承当前样式
+			&>uni-easyinput,
+			&>uni-data-picker {
+				width: 100%;
+			}
+
+			/* #endif */
+
+		}
+
+		& .uni-forms-item__nuve-content {
+			display: flex;
+			flex-direction: column;
+			flex: 1;
+		}
+
+		&__error {
+			color: #f56c6c;
+			font-size: 12px;
+			line-height: 1;
+			padding-top: 4px;
+			position: absolute;
+			/* #ifndef APP-NVUE */
+			top: 100%;
+			left: 0;
+			transition: transform 0.3s;
+			transform: translateY(-100%);
+			/* #endif */
+			/* #ifdef APP-NVUE */
+			bottom: 5px;
+			/* #endif */
+
+			opacity: 0;
+
+			.error-text {
+				// 只有 nvue 下这个样式才生效
+				color: #f56c6c;
+				font-size: 12px;
+			}
+
+			&.msg--active {
+				opacity: 1;
+				transform: translateY(0%);
+			}
+		}
+
+		// 位置修饰样式
+		&.is-direction-left {
+			flex-direction: row;
+		}
+
+		&.is-direction-top {
+			flex-direction: column;
+
+			.uni-forms-item__label {
+				padding: 0 0 8px;
+				line-height: 1.5715;
+				text-align: left;
+				/* #ifndef APP-NVUE */
+				white-space: initial;
+				/* #endif */
+			}
+		}
+
+		.is-required {
+			// color: $uni-color-error;
+			color: #dd524d;
+			font-weight: bold;
+		}
+	}
+
+
+	.uni-forms-item--border {
+		margin-bottom: 0;
+		padding: 10px 0;
+		// padding-bottom: 0;
+		border-top: 1px #eee solid;
+
+		/* #ifndef APP-NVUE */
+		.uni-forms-item__content {
+			flex-direction: column;
+			justify-content: flex-start;
+			align-items: flex-start;
+
+			.uni-forms-item__error {
+				position: relative;
+				top: 5px;
+				left: 0;
+				padding-top: 0;
+			}
+		}
+
+		/* #endif */
+
+		/* #ifdef APP-NVUE */
+		display: flex;
+		flex-direction: column;
+
+		.uni-forms-item__error {
+			position: relative;
+			top: 0px;
+			left: 0;
+			padding-top: 0;
+			margin-top: 5px;
+		}
+
+		/* #endif */
+
+	}
+
+	.is-first-border {
+		/* #ifndef APP-NVUE */
+		border: none;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		border-width: 0;
+		/* #endif */
+	}
+</style>

+ 397 - 0
uni_modules/uni-forms/components/uni-forms/uni-forms.vue

@@ -0,0 +1,397 @@
+<template>
+	<view class="uni-forms">
+		<form>
+			<slot></slot>
+		</form>
+	</view>
+</template>
+
+<script>
+	import Validator from './validate.js';
+	import {
+		deepCopy,
+		getValue,
+		isRequiredField,
+		setDataValue,
+		getDataValue,
+		realName,
+		isRealName,
+		rawData,
+		isEqual
+	} from './utils.js'
+
+	// #ifndef VUE3
+	// 后续会慢慢废弃这个方法
+	import Vue from 'vue';
+	Vue.prototype.binddata = function(name, value, formName) {
+		if (formName) {
+			this.$refs[formName].setValue(name, value);
+		} else {
+			let formVm;
+			for (let i in this.$refs) {
+				const vm = this.$refs[i];
+				if (vm && vm.$options && vm.$options.name === 'uniForms') {
+					formVm = vm;
+					break;
+				}
+			}
+			if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性');
+			formVm.setValue(name, value);
+		}
+	};
+	// #endif
+	/**
+	 * Forms 表单
+	 * @description 由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=2773
+	 * @property {Object} rules	表单校验规则
+	 * @property {String} validateTrigger = [bind|submit|blur]	校验触发器方式 默认 submit
+	 * @value bind		发生变化时触发
+	 * @value submit	提交时触发
+	 * @value blur	  失去焦点时触发
+	 * @property {String} labelPosition = [top|left]	label 位置 默认 left
+	 * @value top		顶部显示 label
+	 * @value left	左侧显示 label
+	 * @property {String} labelWidth	label 宽度,默认 70px
+	 * @property {String} labelAlign = [left|center|right]	label 居中方式  默认 left
+	 * @value left		label 左侧显示
+	 * @value center	label 居中
+	 * @value right		label 右侧对齐
+	 * @property {String} errShowType = [undertext|toast|modal]	校验错误信息提示方式
+	 * @value undertext	错误信息在底部显示
+	 * @value toast			错误信息toast显示
+	 * @value modal			错误信息modal显示
+	 * @event {Function} submit	提交时触发
+	 * @event {Function} validate	校验结果发生变化触发
+	 */
+	export default {
+		name: 'uniForms',
+		emits: ['validate', 'submit'],
+		options: {
+			virtualHost: true
+		},
+		props: {
+			// 即将弃用
+			value: {
+				type: Object,
+				default () {
+					return null;
+				}
+			},
+			// vue3 替换 value 属性
+			modelValue: {
+				type: Object,
+				default () {
+					return null;
+				}
+			},
+			// 1.4.0 开始将不支持 v-model ,且废弃 value 和 modelValue
+			model: {
+				type: Object,
+				default () {
+					return null;
+				}
+			},
+			// 表单校验规则
+			rules: {
+				type: Object,
+				default () {
+					return {};
+				}
+			},
+			//校验错误信息提示方式 默认 undertext 取值 [undertext|toast|modal]
+			errShowType: {
+				type: String,
+				default: 'undertext'
+			},
+			// 校验触发器方式 默认 bind 取值 [bind|submit]
+			validateTrigger: {
+				type: String,
+				default: 'submit'
+			},
+			// label 位置,默认 left 取值  top/left
+			labelPosition: {
+				type: String,
+				default: 'left'
+			},
+			// label 宽度
+			labelWidth: {
+				type: [String, Number],
+				default: ''
+			},
+			// label 居中方式,默认 left 取值 left/center/right
+			labelAlign: {
+				type: String,
+				default: 'left'
+			},
+			border: {
+				type: Boolean,
+				default: false
+			}
+		},
+		provide() {
+			return {
+				uniForm: this
+			}
+		},
+		data() {
+			return {
+				// 表单本地值的记录,不应该与传如的值进行关联
+				formData: {},
+				formRules: {}
+			};
+		},
+		computed: {
+			// 计算数据源变化的
+			localData() {
+				const localVal = this.model || this.modelValue || this.value
+				if (localVal) {
+					return deepCopy(localVal)
+				}
+				return {}
+			}
+		},
+		watch: {
+			// 监听数据变化 ,暂时不使用,需要单独赋值
+			// localData: {},
+			// 监听规则变化
+			rules: {
+				handler: function(val, oldVal) {
+					this.setRules(val)
+				},
+				deep: true,
+				immediate: true
+			}
+		},
+		created() {
+			// #ifdef VUE3
+			let getbinddata = getApp().$vm.$.appContext.config.globalProperties.binddata
+			if (!getbinddata) {
+				getApp().$vm.$.appContext.config.globalProperties.binddata = function(name, value, formName) {
+					if (formName) {
+						this.$refs[formName].setValue(name, value);
+					} else {
+						let formVm;
+						for (let i in this.$refs) {
+							const vm = this.$refs[i];
+							if (vm && vm.$options && vm.$options.name === 'uniForms') {
+								formVm = vm;
+								break;
+							}
+						}
+						if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性');
+						formVm.setValue(name, value);
+					}
+				}
+			}
+			// #endif
+
+			// 子组件实例数组
+			this.childrens = []
+			// TODO 兼容旧版 uni-data-picker ,新版本中无效,只是避免报错
+			this.inputChildrens = []
+			this.setRules(this.rules)
+		},
+		methods: {
+			/**
+			 * 外部调用方法
+			 * 设置规则 ,主要用于小程序自定义检验规则
+			 * @param {Array} rules 规则源数据
+			 */
+			setRules(rules) {
+				// TODO 有可能子组件合并规则的时机比这个要早,所以需要合并对象 ,而不是直接赋值,可能会被覆盖
+				this.formRules = Object.assign({}, this.formRules, rules)
+				// 初始化校验函数
+				this.validator = new Validator(rules);
+			},
+
+			/**
+			 * 外部调用方法
+			 * 设置数据,用于设置表单数据,公开给用户使用 , 不支持在动态表单中使用
+			 * @param {Object} key
+			 * @param {Object} value
+			 */
+			setValue(key, value) {
+				let example = this.childrens.find(child => child.name === key);
+				if (!example) return null;
+				this.formData[key] = getValue(key, value, (this.formRules[key] && this.formRules[key].rules) || [])
+				return example.onFieldChange(this.formData[key]);
+			},
+
+			/**
+			 * 外部调用方法
+			 * 手动提交校验表单
+			 * 对整个表单进行校验的方法,参数为一个回调函数。
+			 * @param {Array} keepitem 保留不参与校验的字段
+			 * @param {type} callback 方法回调
+			 */
+			validate(keepitem, callback) {
+				return this.checkAll(this.formData, keepitem, callback);
+			},
+
+			/**
+			 * 外部调用方法
+			 * 部分表单校验
+			 * @param {Array|String} props 需要校验的字段
+			 * @param {Function} 回调函数
+			 */
+			validateField(props = [], callback) {
+				props = [].concat(props);
+				let invalidFields = {};
+				this.childrens.forEach(item => {
+					const name = realName(item.name)
+					if (props.indexOf(name) !== -1) {
+						invalidFields = Object.assign({}, invalidFields, {
+							[name]: this.formData[name]
+						});
+					}
+				});
+				return this.checkAll(invalidFields, [], callback);
+			},
+
+			/**
+			 * 外部调用方法
+			 * 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果
+			 * @param {Array|String} props 需要移除校验的字段 ,不填为所有
+			 */
+			clearValidate(props = []) {
+				props = [].concat(props);
+				this.childrens.forEach(item => {
+					if (props.length === 0) {
+						item.errMsg = '';
+					} else {
+						const name = realName(item.name)
+						if (props.indexOf(name) !== -1) {
+							item.errMsg = '';
+						}
+					}
+				});
+			},
+
+			/**
+			 * 外部调用方法 ,即将废弃
+			 * 手动提交校验表单
+			 * 对整个表单进行校验的方法,参数为一个回调函数。
+			 * @param {Array} keepitem 保留不参与校验的字段
+			 * @param {type} callback 方法回调
+			 */
+			submit(keepitem, callback, type) {
+				for (let i in this.dataValue) {
+					const itemData = this.childrens.find(v => v.name === i);
+					if (itemData) {
+						if (this.formData[i] === undefined) {
+							this.formData[i] = this._getValue(i, this.dataValue[i]);
+						}
+					}
+				}
+
+				if (!type) {
+					console.warn('submit 方法即将废弃,请使用validate方法代替!');
+				}
+
+				return this.checkAll(this.formData, keepitem, callback, 'submit');
+			},
+
+			// 校验所有
+			async checkAll(invalidFields, keepitem, callback, type) {
+				// 不存在校验规则 ,则停止校验流程
+				if (!this.validator) return
+				let childrens = []
+				// 处理参与校验的item实例
+				for (let i in invalidFields) {
+					const item = this.childrens.find(v => realName(v.name) === i)
+					if (item) {
+						childrens.push(item)
+					}
+				}
+
+				// 如果validate第一个参数是funciont ,那就走回调
+				if (!callback && typeof keepitem === 'function') {
+					callback = keepitem;
+				}
+
+				let promise;
+				// 如果不存在回调,那么使用 Promise 方式返回
+				if (!callback && typeof callback !== 'function' && Promise) {
+					promise = new Promise((resolve, reject) => {
+						callback = function(valid, invalidFields) {
+							!valid ? resolve(invalidFields) : reject(valid);
+						};
+					});
+				}
+
+				let results = [];
+				// 避免引用错乱 ,建议拷贝对象处理
+				let tempFormData = JSON.parse(JSON.stringify(invalidFields))
+				// 所有子组件参与校验,使用 for 可以使用  awiat
+				for (let i in childrens) {
+					const child = childrens[i]
+					let name = realName(child.name);
+					const result = await child.onFieldChange(tempFormData[name]);
+					if (result) {
+						results.push(result);
+						// toast ,modal 只需要执行第一次就可以
+						if (this.errShowType === 'toast' || this.errShowType === 'modal') break;
+					}
+				}
+
+
+				if (Array.isArray(results)) {
+					if (results.length === 0) results = null;
+				}
+				if (Array.isArray(keepitem)) {
+					keepitem.forEach(v => {
+						let vName = realName(v);
+						let value = getDataValue(v, this.localData)
+						if (value !== undefined) {
+							tempFormData[vName] = value
+						}
+					});
+				}
+
+				// TODO submit 即将废弃
+				if (type === 'submit') {
+					this.$emit('submit', {
+						detail: {
+							value: tempFormData,
+							errors: results
+						}
+					});
+				} else {
+					this.$emit('validate', results);
+				}
+
+				// const resetFormData = rawData(tempFormData, this.localData, this.name)
+				let resetFormData = {}
+				resetFormData = rawData(tempFormData, this.name)
+				callback && typeof callback === 'function' && callback(results, resetFormData);
+
+				if (promise && callback) {
+					return promise;
+				} else {
+					return null;
+				}
+
+			},
+
+			/**
+			 * 返回validate事件
+			 * @param {Object} result
+			 */
+			validateCheck(result) {
+				this.$emit('validate', result);
+			},
+			_getValue: getValue,
+			_isRequiredField: isRequiredField,
+			_setDataValue: setDataValue,
+			_getDataValue: getDataValue,
+			_realName: realName,
+			_isRealName: isRealName,
+			_isEqual: isEqual
+		}
+	};
+</script>
+
+<style lang="scss">
+	.uni-forms {}
+</style>

+ 293 - 0
uni_modules/uni-forms/components/uni-forms/utils.js

@@ -0,0 +1,293 @@
+/**
+ * 简单处理对象拷贝
+ * @param {Obejct} 被拷贝对象
+ * @@return {Object} 拷贝对象
+ */
+export const deepCopy = (val) => {
+	return JSON.parse(JSON.stringify(val))
+}
+/**
+ * 过滤数字类型
+ * @param {String} format 数字类型
+ * @@return {Boolean} 返回是否为数字类型
+ */
+export const typeFilter = (format) => {
+	return format === 'int' || format === 'double' || format === 'number' || format === 'timestamp';
+}
+
+/**
+ * 把 value 转换成指定的类型,用于处理初始值,原因是初始值需要入库不能为 undefined
+ * @param {String} key 字段名
+ * @param {any} value 字段值
+ * @param {Object} rules 表单校验规则
+ */
+export const getValue = (key, value, rules) => {
+	const isRuleNumType = rules.find(val => val.format && typeFilter(val.format));
+	const isRuleBoolType = rules.find(val => (val.format && val.format === 'boolean') || val.format === 'bool');
+	// 输入类型为 number
+	if (!!isRuleNumType) {
+		if (!value && value !== 0) {
+			value = null
+		} else {
+			value = isNumber(Number(value)) ? Number(value) : value
+		}
+	}
+
+	// 输入类型为 boolean
+	if (!!isRuleBoolType) {
+		value = isBoolean(value) ? value : false
+	}
+
+	return value;
+}
+
+/**
+ * 获取表单数据
+ * @param {String|Array} name 真实名称,需要使用 realName 获取
+ * @param {Object} data 原始数据
+ * @param {any} value  需要设置的值
+ */
+export const setDataValue = (field, formdata, value) => {
+	formdata[field] = value
+	return value || ''
+}
+
+/**
+ * 获取表单数据
+ * @param {String|Array} field 真实名称,需要使用 realName 获取
+ * @param {Object} data 原始数据
+ */
+export const getDataValue = (field, data) => {
+	return objGet(data, field)
+}
+
+/**
+ * 获取表单类型
+ * @param {String|Array} field 真实名称,需要使用 realName 获取
+ */
+export const getDataValueType = (field, data) => {
+	const value = getDataValue(field, data)
+	return {
+		type: type(value),
+		value
+	}
+}
+
+/**
+ * 获取表单可用的真实name
+ * @param {String|Array} name 表单name
+ * @@return {String} 表单可用的真实name
+ */
+export const realName = (name, data = {}) => {
+	const base_name = _basePath(name)
+	if (typeof base_name === 'object' && Array.isArray(base_name) && base_name.length > 1) {
+		const realname = base_name.reduce((a, b) => a += `#${b}`, '_formdata_')
+		return realname
+	}
+	return base_name[0] || name
+}
+
+/**
+ * 判断是否表单可用的真实name
+ * @param {String|Array} name 表单name
+ * @@return {String} 表单可用的真实name
+ */
+export const isRealName = (name) => {
+	const reg = /^_formdata_#*/
+	return reg.test(name)
+}
+
+/**
+ * 获取表单数据的原始格式
+ * @@return {Object|Array} object 需要解析的数据
+ */
+export const rawData = (object = {}, name) => {
+	let newData = JSON.parse(JSON.stringify(object))
+	let formData = {}
+	for(let i in newData){
+		let path = name2arr(i)
+		objSet(formData,path,newData[i])
+	}
+	return formData
+}
+
+/**
+ * 真实name还原为 array
+ * @param {*} name 
+ */
+export const name2arr = (name) => {
+	let field = name.replace('_formdata_#', '')
+	field = field.split('#').map(v => (isNumber(v) ? Number(v) : v))
+	return field
+}
+
+/**
+ * 对象中设置值
+ * @param {Object|Array} object 源数据
+ * @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c']
+ * @param {String} value 需要设置的值
+ */
+export const objSet = (object, path, value) => {
+	if (typeof object !== 'object') return object;
+	_basePath(path).reduce((o, k, i, _) => {
+		if (i === _.length - 1) { 
+			// 若遍历结束直接赋值
+			o[k] = value
+			return null
+		} else if (k in o) { 
+			// 若存在对应路径,则返回找到的对象,进行下一次遍历
+			return o[k]
+		} else { 
+			// 若不存在对应路径,则创建对应对象,若下一路径是数字,新对象赋值为空数组,否则赋值为空对象
+			o[k] = /^[0-9]{1,}$/.test(_[i + 1]) ? [] : {}
+			return o[k]
+		}
+	}, object)
+	// 返回object
+	return object;
+}
+
+// 处理 path, path有三种形式:'a[0].b.c'、'a.0.b.c' 和 ['a','0','b','c'],需要统一处理成数组,便于后续使用
+function _basePath(path) {
+	// 若是数组,则直接返回
+	if (Array.isArray(path)) return path
+	// 若有 '[',']',则替换成将 '[' 替换成 '.',去掉 ']'
+	return path.replace(/\[/g, '.').replace(/\]/g, '').split('.')
+}
+
+/**
+ * 从对象中获取值
+ * @param {Object|Array} object 源数据
+ * @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c']
+ * @param {String} defaultVal 如果无法从调用链中获取值的默认值
+ */
+export const objGet = (object, path, defaultVal = 'undefined') => {
+	// 先将path处理成统一格式
+	let newPath = _basePath(path)
+	// 递归处理,返回最后结果
+	let val = newPath.reduce((o, k) => {
+		return (o || {})[k]
+	}, object);
+	return !val || val !== undefined ? val : defaultVal
+}
+
+
+/**
+ * 是否为 number 类型 
+ * @param {any} num 需要判断的值
+ * @return {Boolean} 是否为 number
+ */
+export const isNumber = (num) => {
+	return !isNaN(Number(num))
+}
+
+/**
+ * 是否为 boolean 类型 
+ * @param {any} bool 需要判断的值
+ * @return {Boolean} 是否为 boolean
+ */
+export const isBoolean = (bool) => {
+	return (typeof bool === 'boolean')
+}
+/**
+ * 是否有必填字段
+ * @param {Object} rules 规则
+ * @return {Boolean} 是否有必填字段
+ */
+export const isRequiredField = (rules) => {
+	let isNoField = false;
+	for (let i = 0; i < rules.length; i++) {
+		const ruleData = rules[i];
+		if (ruleData.required) {
+			isNoField = true;
+			break;
+		}
+	}
+	return isNoField;
+}
+
+
+/**
+ * 获取数据类型
+ * @param {Any} obj 需要获取数据类型的值
+ */
+export const type = (obj) => {
+	var class2type = {};
+
+	// 生成class2type映射
+	"Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) {
+		class2type["[object " + item + "]"] = item.toLowerCase();
+	})
+	if (obj == null) {
+		return obj + "";
+	}
+	return typeof obj === "object" || typeof obj === "function" ?
+		class2type[Object.prototype.toString.call(obj)] || "object" :
+		typeof obj;
+}
+
+/**
+ * 判断两个值是否相等
+ * @param {any} a 值  
+ * @param {any} b 值  
+ * @return {Boolean} 是否相等
+ */
+export const isEqual = (a, b) => {
+	//如果a和b本来就全等
+	if (a === b) {
+		//判断是否为0和-0
+		return a !== 0 || 1 / a === 1 / b;
+	}
+	//判断是否为null和undefined
+	if (a == null || b == null) {
+		return a === b;
+	}
+	//接下来判断a和b的数据类型
+	var classNameA = toString.call(a),
+		classNameB = toString.call(b);
+	//如果数据类型不相等,则返回false
+	if (classNameA !== classNameB) {
+		return false;
+	}
+	//如果数据类型相等,再根据不同数据类型分别判断
+	switch (classNameA) {
+		case '[object RegExp]':
+		case '[object String]':
+			//进行字符串转换比较
+			return '' + a === '' + b;
+		case '[object Number]':
+			//进行数字转换比较,判断是否为NaN
+			if (+a !== +a) {
+				return +b !== +b;
+			}
+			//判断是否为0或-0
+			return +a === 0 ? 1 / +a === 1 / b : +a === +b;
+		case '[object Date]':
+		case '[object Boolean]':
+			return +a === +b;
+	}
+	//如果是对象类型
+	if (classNameA == '[object Object]') {
+		//获取a和b的属性长度
+		var propsA = Object.getOwnPropertyNames(a),
+			propsB = Object.getOwnPropertyNames(b);
+		if (propsA.length != propsB.length) {
+			return false;
+		}
+		for (var i = 0; i < propsA.length; i++) {
+			var propName = propsA[i];
+			//如果对应属性对应值不相等,则返回false
+			if (a[propName] !== b[propName]) {
+				return false;
+			}
+		}
+		return true;
+	}
+	//如果是数组类型
+	if (classNameA == '[object Array]') {
+		if (a.toString() == b.toString()) {
+			return true;
+		}
+		return false;
+	}
+}

+ 486 - 0
uni_modules/uni-forms/components/uni-forms/validate.js

@@ -0,0 +1,486 @@
+var pattern = {
+	email: /^\S+?@\S+?\.\S+?$/,
+	idcard: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
+	url: new RegExp(
+		"^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$",
+		'i')
+};
+
+const FORMAT_MAPPING = {
+	"int": 'integer',
+	"bool": 'boolean',
+	"double": 'number',
+	"long": 'number',
+	"password": 'string'
+	// "fileurls": 'array'
+}
+
+function formatMessage(args, resources = '') {
+	var defaultMessage = ['label']
+	defaultMessage.forEach((item) => {
+		if (args[item] === undefined) {
+			args[item] = ''
+		}
+	})
+
+	let str = resources
+	for (let key in args) {
+		let reg = new RegExp('{' + key + '}')
+		str = str.replace(reg, args[key])
+	}
+	return str
+}
+
+function isEmptyValue(value, type) {
+	if (value === undefined || value === null) {
+		return true;
+	}
+
+	if (typeof value === 'string' && !value) {
+		return true;
+	}
+
+	if (Array.isArray(value) && !value.length) {
+		return true;
+	}
+
+	if (type === 'object' && !Object.keys(value).length) {
+		return true;
+	}
+
+	return false;
+}
+
+const types = {
+	integer(value) {
+		return types.number(value) && parseInt(value, 10) === value;
+	},
+	string(value) {
+		return typeof value === 'string';
+	},
+	number(value) {
+		if (isNaN(value)) {
+			return false;
+		}
+		return typeof value === 'number';
+	},
+	"boolean": function(value) {
+		return typeof value === 'boolean';
+	},
+	"float": function(value) {
+		return types.number(value) && !types.integer(value);
+	},
+	array(value) {
+		return Array.isArray(value);
+	},
+	object(value) {
+		return typeof value === 'object' && !types.array(value);
+	},
+	date(value) {
+		return value instanceof Date;
+	},
+	timestamp(value) {
+		if (!this.integer(value) || Math.abs(value).toString().length > 16) {
+			return false
+		}
+		return true;
+	},
+	file(value) {
+		return typeof value.url === 'string';
+	},
+	email(value) {
+		return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255;
+	},
+	url(value) {
+		return typeof value === 'string' && !!value.match(pattern.url);
+	},
+	pattern(reg, value) {
+		try {
+			return new RegExp(reg).test(value);
+		} catch (e) {
+			return false;
+		}
+	},
+	method(value) {
+		return typeof value === 'function';
+	},
+	idcard(value) {
+		return typeof value === 'string' && !!value.match(pattern.idcard);
+	},
+	'url-https'(value) {
+		return this.url(value) && value.startsWith('https://');
+	},
+	'url-scheme'(value) {
+		return value.startsWith('://');
+	},
+	'url-web'(value) {
+		return false;
+	}
+}
+
+class RuleValidator {
+
+	constructor(message) {
+		this._message = message
+	}
+
+	async validateRule(fieldKey, fieldValue, value, data, allData) {
+		var result = null
+
+		let rules = fieldValue.rules
+
+		let hasRequired = rules.findIndex((item) => {
+			return item.required
+		})
+		if (hasRequired < 0) {
+			if (value === null || value === undefined) {
+				return result
+			}
+			if (typeof value === 'string' && !value.length) {
+				return result
+			}
+		}
+
+		var message = this._message
+
+		if (rules === undefined) {
+			return message['default']
+		}
+
+		for (var i = 0; i < rules.length; i++) {
+			let rule = rules[i]
+			let vt = this._getValidateType(rule)
+
+			Object.assign(rule, {
+				label: fieldValue.label || `["${fieldKey}"]`
+			})
+
+			if (RuleValidatorHelper[vt]) {
+				result = RuleValidatorHelper[vt](rule, value, message)
+				if (result != null) {
+					break
+				}
+			}
+
+			if (rule.validateExpr) {
+				let now = Date.now()
+				let resultExpr = rule.validateExpr(value, allData, now)
+				if (resultExpr === false) {
+					result = this._getMessage(rule, rule.errorMessage || this._message['default'])
+					break
+				}
+			}
+
+			if (rule.validateFunction) {
+				result = await this.validateFunction(rule, value, data, allData, vt)
+				if (result !== null) {
+					break
+				}
+			}
+		}
+
+		if (result !== null) {
+			result = message.TAG + result
+		}
+
+		return result
+	}
+
+	async validateFunction(rule, value, data, allData, vt) {
+		let result = null
+		try {
+			let callbackMessage = null
+			const res = await rule.validateFunction(rule, value, allData || data, (message) => {
+				callbackMessage = message
+			})
+			if (callbackMessage || (typeof res === 'string' && res) || res === false) {
+				result = this._getMessage(rule, callbackMessage || res, vt)
+			}
+		} catch (e) {
+			result = this._getMessage(rule, e.message, vt)
+		}
+		return result
+	}
+
+	_getMessage(rule, message, vt) {
+		return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default'])
+	}
+
+	_getValidateType(rule) {
+		var result = ''
+		if (rule.required) {
+			result = 'required'
+		} else if (rule.format) {
+			result = 'format'
+		} else if (rule.arrayType) {
+			result = 'arrayTypeFormat'
+		} else if (rule.range) {
+			result = 'range'
+		} else if (rule.maximum !== undefined || rule.minimum !== undefined) {
+			result = 'rangeNumber'
+		} else if (rule.maxLength !== undefined || rule.minLength !== undefined) {
+			result = 'rangeLength'
+		} else if (rule.pattern) {
+			result = 'pattern'
+		} else if (rule.validateFunction) {
+			result = 'validateFunction'
+		}
+		return result
+	}
+}
+
+const RuleValidatorHelper = {
+	required(rule, value, message) {
+		if (rule.required && isEmptyValue(value, rule.format || typeof value)) {
+			return formatMessage(rule, rule.errorMessage || message.required);
+		}
+
+		return null
+	},
+
+	range(rule, value, message) {
+		const {
+			range,
+			errorMessage
+		} = rule;
+
+		let list = new Array(range.length);
+		for (let i = 0; i < range.length; i++) {
+			const item = range[i];
+			if (types.object(item) && item.value !== undefined) {
+				list[i] = item.value;
+			} else {
+				list[i] = item;
+			}
+		}
+
+		let result = false
+		if (Array.isArray(value)) {
+			result = (new Set(value.concat(list)).size === list.length);
+		} else {
+			if (list.indexOf(value) > -1) {
+				result = true;
+			}
+		}
+
+		if (!result) {
+			return formatMessage(rule, errorMessage || message['enum']);
+		}
+
+		return null
+	},
+
+	rangeNumber(rule, value, message) {
+		if (!types.number(value)) {
+			return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
+		}
+
+		let {
+			minimum,
+			maximum,
+			exclusiveMinimum,
+			exclusiveMaximum
+		} = rule;
+		let min = exclusiveMinimum ? value <= minimum : value < minimum;
+		let max = exclusiveMaximum ? value >= maximum : value > maximum;
+
+		if (minimum !== undefined && min) {
+			return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMinimum ?
+				'exclusiveMinimum' : 'minimum'
+			])
+		} else if (maximum !== undefined && max) {
+			return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMaximum ?
+				'exclusiveMaximum' : 'maximum'
+			])
+		} else if (minimum !== undefined && maximum !== undefined && (min || max)) {
+			return formatMessage(rule, rule.errorMessage || message['number'].range)
+		}
+
+		return null
+	},
+
+	rangeLength(rule, value, message) {
+		if (!types.string(value) && !types.array(value)) {
+			return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
+		}
+
+		let min = rule.minLength;
+		let max = rule.maxLength;
+		let val = value.length;
+
+		if (min !== undefined && val < min) {
+			return formatMessage(rule, rule.errorMessage || message['length'].minLength)
+		} else if (max !== undefined && val > max) {
+			return formatMessage(rule, rule.errorMessage || message['length'].maxLength)
+		} else if (min !== undefined && max !== undefined && (val < min || val > max)) {
+			return formatMessage(rule, rule.errorMessage || message['length'].range)
+		}
+
+		return null
+	},
+
+	pattern(rule, value, message) {
+		if (!types['pattern'](rule.pattern, value)) {
+			return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
+		}
+
+		return null
+	},
+
+	format(rule, value, message) {
+		var customTypes = Object.keys(types);
+		var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : (rule.format || rule.arrayType);
+
+		if (customTypes.indexOf(format) > -1) {
+			if (!types[format](value)) {
+				return formatMessage(rule, rule.errorMessage || message.typeError);
+			}
+		}
+
+		return null
+	},
+
+	arrayTypeFormat(rule, value, message) {
+		if (!Array.isArray(value)) {
+			return formatMessage(rule, rule.errorMessage || message.typeError);
+		}
+
+		for (let i = 0; i < value.length; i++) {
+			const element = value[i];
+			let formatResult = this.format(rule, element, message)
+			if (formatResult !== null) {
+				return formatResult
+			}
+		}
+
+		return null
+	}
+}
+
+class SchemaValidator extends RuleValidator {
+
+	constructor(schema, options) {
+		super(SchemaValidator.message);
+
+		this._schema = schema
+		this._options = options || null
+	}
+
+	updateSchema(schema) {
+		this._schema = schema
+	}
+
+	async validate(data, allData) {
+		let result = this._checkFieldInSchema(data)
+		if (!result) {
+			result = await this.invokeValidate(data, false, allData)
+		}
+		return result.length ? result[0] : null
+	}
+
+	async validateAll(data, allData) {
+		let result = this._checkFieldInSchema(data)
+		if (!result) {
+			result = await this.invokeValidate(data, true, allData)
+		}
+		return result
+	}
+
+	async validateUpdate(data, allData) {
+		let result = this._checkFieldInSchema(data)
+		if (!result) {
+			result = await this.invokeValidateUpdate(data, false, allData)
+		}
+		return result.length ? result[0] : null
+	}
+
+	async invokeValidate(data, all, allData) {
+		let result = []
+		let schema = this._schema
+		for (let key in schema) {
+			let value = schema[key]
+			let errorMessage = await this.validateRule(key, value, data[key], data, allData)
+			if (errorMessage != null) {
+				result.push({
+					key,
+					errorMessage
+				})
+				if (!all) break
+			}
+		}
+		return result
+	}
+
+	async invokeValidateUpdate(data, all, allData) {
+		let result = []
+		for (let key in data) {
+			let errorMessage = await this.validateRule(key, this._schema[key], data[key], data, allData)
+			if (errorMessage != null) {
+				result.push({
+					key,
+					errorMessage
+				})
+				if (!all) break
+			}
+		}
+		return result
+	}
+
+	_checkFieldInSchema(data) {
+		var keys = Object.keys(data)
+		var keys2 = Object.keys(this._schema)
+		if (new Set(keys.concat(keys2)).size === keys2.length) {
+			return ''
+		}
+
+		var noExistFields = keys.filter((key) => {
+			return keys2.indexOf(key) < 0;
+		})
+		var errorMessage = formatMessage({
+			field: JSON.stringify(noExistFields)
+		}, SchemaValidator.message.TAG + SchemaValidator.message['defaultInvalid'])
+		return [{
+			key: 'invalid',
+			errorMessage
+		}]
+	}
+}
+
+function Message() {
+	return {
+		TAG: "",
+		default: '验证错误',
+		defaultInvalid: '提交的字段{field}在数据库中并不存在',
+		validateFunction: '验证无效',
+		required: '{label}必填',
+		'enum': '{label}超出范围',
+		timestamp: '{label}格式无效',
+		whitespace: '{label}不能为空',
+		typeError: '{label}类型无效',
+		date: {
+			format: '{label}日期{value}格式无效',
+			parse: '{label}日期无法解析,{value}无效',
+			invalid: '{label}日期{value}无效'
+		},
+		length: {
+			minLength: '{label}长度不能少于{minLength}',
+			maxLength: '{label}长度不能超过{maxLength}',
+			range: '{label}必须介于{minLength}和{maxLength}之间'
+		},
+		number: {
+			minimum: '{label}不能小于{minimum}',
+			maximum: '{label}不能大于{maximum}',
+			exclusiveMinimum: '{label}不能小于等于{minimum}',
+			exclusiveMaximum: '{label}不能大于等于{maximum}',
+			range: '{label}必须介于{minimum}and{maximum}之间'
+		},
+		pattern: {
+			mismatch: '{label}格式不匹配'
+		}
+	};
+}
+
+
+SchemaValidator.message = new Message();
+
+export default SchemaValidator

+ 88 - 0
uni_modules/uni-forms/package.json

@@ -0,0 +1,88 @@
+{
+  "id": "uni-forms",
+  "displayName": "uni-forms 表单",
+  "version": "1.4.10",
+  "description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据",
+  "keywords": [
+    "uni-ui",
+    "表单",
+    "校验",
+    "表单校验",
+    "表单验证"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uni-scss",
+      "uni-icons"
+    ],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+        "QQ": "y",
+        "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 23 - 0
uni_modules/uni-forms/readme.md

@@ -0,0 +1,23 @@
+
+
+## Forms 表单
+
+> **组件名:uni-forms**
+> 代码块: `uForms`、`uni-forms-item`
+> 关联组件:`uni-forms-item`、`uni-easyinput`、`uni-data-checkbox`、`uni-group`。
+
+
+uni-app的内置组件已经有了 `<form>`组件,用于提交表单内容。
+
+然而几乎每个表单都需要做表单验证,为了方便做表单验证,减少重复开发,`uni ui` 又基于 `<form>`组件封装了 `<uni-forms>`组件,内置了表单验证功能。
+
+`<uni-forms>` 提供了 `rules`属性来描述校验规则、`<uni-forms-item>`子组件来包裹具体的表单项,以及给原生或三方组件提供了 `binddata()` 来设置表单值。
+
+每个要校验的表单项,不管input还是checkbox,都必须放在`<uni-forms-item>`组件中,且一个`<uni-forms-item>`组件只能放置一个表单项。
+
+`<uni-forms-item>`组件内部预留了显示error message的区域,默认是在表单项的底部。
+
+另外,`<uni-forms>`组件下面的各个表单项,可以通过`<uni-group>`包裹为不同的分组。同一`<uni-group>`下的不同表单项目将聚拢在一起,同其他group保持垂直间距。`<uni-group>`仅影响视觉效果。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-forms)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 40 - 0
uni_modules/uni-icons/changelog.md

@@ -0,0 +1,40 @@
+## 2.0.9(2024-01-12)
+fix: 修复图标大小默认值错误的问题
+## 2.0.8(2023-12-14)
+- 修复 项目未使用 ts 情况下,打包报错的bug
+## 2.0.7(2023-12-14)
+- 修复 size 属性为 string 时,不加单位导致尺寸异常的bug
+## 2.0.6(2023-12-11)
+- 优化 兼容老版本icon类型,如 top ,bottom 等
+## 2.0.5(2023-12-11)
+- 优化 兼容老版本icon类型,如 top ,bottom 等
+## 2.0.4(2023-12-06)
+- 优化 uni-app x 下示例项目图标排序
+## 2.0.3(2023-12-06)
+- 修复 nvue下引入组件报错的bug
+## 2.0.2(2023-12-05)
+-优化 size 属性支持单位
+## 2.0.1(2023-12-05)
+- 新增 uni-app x 支持定义图标
+## 1.3.5(2022-01-24)
+- 优化 size 属性可以传入不带单位的字符串数值
+## 1.3.4(2022-01-24)
+- 优化 size 支持其他单位
+## 1.3.3(2022-01-17)
+- 修复 nvue 有些图标不显示的bug,兼容老版本图标
+## 1.3.2(2021-12-01)
+- 优化 示例可复制图标名称
+## 1.3.1(2021-11-23)
+- 优化 兼容旧组件 type 值
+## 1.3.0(2021-11-19)
+- 新增 更多图标
+- 优化 自定义图标使用方式
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-icons](https://uniapp.dcloud.io/component/uniui/uni-icons)
+## 1.1.7(2021-11-08)
+## 1.2.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.5(2021-05-12)
+- 新增 组件示例地址
+## 1.1.4(2021-02-05)
+- 调整为uni_modules目录规范

+ 91 - 0
uni_modules/uni-icons/components/uni-icons/uni-icons.uvue

@@ -0,0 +1,91 @@
+<template>
+	<text class="uni-icons" :style="styleObj">
+		<slot>{{unicode}}</slot>
+	</text>
+</template>
+
+<script>
+	import { fontData, IconsDataItem } from './uniicons_file'
+
+	/**
+	 * Icons 图标
+	 * @description 用于展示 icon 图标
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=28
+	 * @property {Number} size 图标大小
+	 * @property {String} type 图标图案,参考示例
+	 * @property {String} color 图标颜色
+	 * @property {String} customPrefix 自定义图标
+	 * @event {Function} click 点击 Icon 触发事件
+	 */
+	export default {
+		name: "uni-icons",
+		props: {
+			type: {
+				type: String,
+				default: ''
+			},
+			color: {
+				type: String,
+				default: '#333333'
+			},
+			size: {
+				type: Object,
+				default: 16
+			},
+			fontFamily: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {};
+		},
+		computed: {
+			unicode() : string {
+				let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
+				if (codes !== null) {
+					return codes.unicode
+				}
+				return ''
+			},
+			iconSize() : string {
+				const size = this.size
+				if (typeof size == 'string') {
+					const reg = /^[0-9]*$/g
+					return reg.test(size as string) ? '' + size + 'px' : '' + size;
+					// return '' + this.size
+				}
+				return this.getFontSize(size as number)
+			},
+			styleObj() : UTSJSONObject {
+				if (this.fontFamily !== '') {
+					return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
+				}
+				return { color: this.color, fontSize: this.iconSize }
+			}
+		},
+		created() { },
+		methods: {
+			/**
+			 * 字体大小
+			 */
+			getFontSize(size : number) : string {
+				return size + 'px';
+			},
+		},
+	}
+</script>
+
+<style scoped>
+	@font-face {
+		font-family: UniIconsFontFamily;
+		src: url('./uniicons.ttf');
+	}
+
+	.uni-icons {
+		font-family: UniIconsFontFamily;
+		font-size: 18px;
+		font-style: normal;
+		color: #333;
+	}
+</style>

+ 110 - 0
uni_modules/uni-icons/components/uni-icons/uni-icons.vue

@@ -0,0 +1,110 @@
+<template>
+	<!-- #ifdef APP-NVUE -->
+	<text :style="styleObj" class="uni-icons" @click="_onClick">{{unicode}}</text>
+	<!-- #endif -->
+	<!-- #ifndef APP-NVUE -->
+	<text :style="styleObj" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick">
+		<slot></slot>
+	</text>
+	<!-- #endif -->
+</template>
+
+<script>
+	import { fontData } from './uniicons_file_vue.js';
+
+	const getVal = (val) => {
+		const reg = /^[0-9]*$/g
+		return (typeof val === 'number' || reg.test(val)) ? val + 'px' : val;
+	}
+
+	// #ifdef APP-NVUE
+	var domModule = weex.requireModule('dom');
+	import iconUrl from './uniicons.ttf'
+	domModule.addRule('fontFace', {
+		'fontFamily': "uniicons",
+		'src': "url('" + iconUrl + "')"
+	});
+	// #endif
+
+	/**
+	 * Icons 图标
+	 * @description 用于展示 icons 图标
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=28
+	 * @property {Number} size 图标大小
+	 * @property {String} type 图标图案,参考示例
+	 * @property {String} color 图标颜色
+	 * @property {String} customPrefix 自定义图标
+	 * @event {Function} click 点击 Icon 触发事件
+	 */
+	export default {
+		name: 'UniIcons',
+		emits: ['click'],
+		props: {
+			type: {
+				type: String,
+				default: ''
+			},
+			color: {
+				type: String,
+				default: '#333333'
+			},
+			size: {
+				type: [Number, String],
+				default: 16
+			},
+			customPrefix: {
+				type: String,
+				default: ''
+			},
+			fontFamily: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {
+				icons: fontData
+			}
+		},
+		computed: {
+			unicode() {
+				let code = this.icons.find(v => v.font_class === this.type)
+				if (code) {
+					return code.unicode
+				}
+				return ''
+			},
+			iconSize() {
+				return getVal(this.size)
+			},
+			styleObj() {
+				if (this.fontFamily !== '') {
+					return `color: ${this.color}; font-size: ${this.iconSize}; font-family: ${this.fontFamily};`
+				}
+				return `color: ${this.color}; font-size: ${this.iconSize};`
+			}
+		},
+		methods: {
+			_onClick() {
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	/* #ifndef APP-NVUE */
+	@import './uniicons.css';
+
+	@font-face {
+		font-family: uniicons;
+		src: url('./uniicons.ttf');
+	}
+
+	/* #endif */
+	.uni-icons {
+		font-family: uniicons;
+		text-decoration: none;
+		text-align: center;
+	}
+</style>

+ 664 - 0
uni_modules/uni-icons/components/uni-icons/uniicons.css

@@ -0,0 +1,664 @@
+
+.uniui-cart-filled:before {
+  content: "\e6d0";
+}
+
+.uniui-gift-filled:before {
+  content: "\e6c4";
+}
+
+.uniui-color:before {
+  content: "\e6cf";
+}
+
+.uniui-wallet:before {
+  content: "\e6b1";
+}
+
+.uniui-settings-filled:before {
+  content: "\e6ce";
+}
+
+.uniui-auth-filled:before {
+  content: "\e6cc";
+}
+
+.uniui-shop-filled:before {
+  content: "\e6cd";
+}
+
+.uniui-staff-filled:before {
+  content: "\e6cb";
+}
+
+.uniui-vip-filled:before {
+  content: "\e6c6";
+}
+
+.uniui-plus-filled:before {
+  content: "\e6c7";
+}
+
+.uniui-folder-add-filled:before {
+  content: "\e6c8";
+}
+
+.uniui-color-filled:before {
+  content: "\e6c9";
+}
+
+.uniui-tune-filled:before {
+  content: "\e6ca";
+}
+
+.uniui-calendar-filled:before {
+  content: "\e6c0";
+}
+
+.uniui-notification-filled:before {
+  content: "\e6c1";
+}
+
+.uniui-wallet-filled:before {
+  content: "\e6c2";
+}
+
+.uniui-medal-filled:before {
+  content: "\e6c3";
+}
+
+.uniui-fire-filled:before {
+  content: "\e6c5";
+}
+
+.uniui-refreshempty:before {
+  content: "\e6bf";
+}
+
+.uniui-location-filled:before {
+  content: "\e6af";
+}
+
+.uniui-person-filled:before {
+  content: "\e69d";
+}
+
+.uniui-personadd-filled:before {
+  content: "\e698";
+}
+
+.uniui-arrowthinleft:before {
+  content: "\e6d2";
+}
+
+.uniui-arrowthinup:before {
+  content: "\e6d3";
+}
+
+.uniui-arrowthindown:before {
+  content: "\e6d4";
+}
+
+.uniui-back:before {
+  content: "\e6b9";
+}
+
+.uniui-forward:before {
+  content: "\e6ba";
+}
+
+.uniui-arrow-right:before {
+  content: "\e6bb";
+}
+
+.uniui-arrow-left:before {
+  content: "\e6bc";
+}
+
+.uniui-arrow-up:before {
+  content: "\e6bd";
+}
+
+.uniui-arrow-down:before {
+  content: "\e6be";
+}
+
+.uniui-arrowthinright:before {
+  content: "\e6d1";
+}
+
+.uniui-down:before {
+  content: "\e6b8";
+}
+
+.uniui-bottom:before {
+  content: "\e6b8";
+}
+
+.uniui-arrowright:before {
+  content: "\e6d5";
+}
+
+.uniui-right:before {
+  content: "\e6b5";
+}
+
+.uniui-up:before {
+  content: "\e6b6";
+}
+
+.uniui-top:before {
+  content: "\e6b6";
+}
+
+.uniui-left:before {
+  content: "\e6b7";
+}
+
+.uniui-arrowup:before {
+  content: "\e6d6";
+}
+
+.uniui-eye:before {
+  content: "\e651";
+}
+
+.uniui-eye-filled:before {
+  content: "\e66a";
+}
+
+.uniui-eye-slash:before {
+  content: "\e6b3";
+}
+
+.uniui-eye-slash-filled:before {
+  content: "\e6b4";
+}
+
+.uniui-info-filled:before {
+  content: "\e649";
+}
+
+.uniui-reload:before {
+  content: "\e6b2";
+}
+
+.uniui-micoff-filled:before {
+  content: "\e6b0";
+}
+
+.uniui-map-pin-ellipse:before {
+  content: "\e6ac";
+}
+
+.uniui-map-pin:before {
+  content: "\e6ad";
+}
+
+.uniui-location:before {
+  content: "\e6ae";
+}
+
+.uniui-starhalf:before {
+  content: "\e683";
+}
+
+.uniui-star:before {
+  content: "\e688";
+}
+
+.uniui-star-filled:before {
+  content: "\e68f";
+}
+
+.uniui-calendar:before {
+  content: "\e6a0";
+}
+
+.uniui-fire:before {
+  content: "\e6a1";
+}
+
+.uniui-medal:before {
+  content: "\e6a2";
+}
+
+.uniui-font:before {
+  content: "\e6a3";
+}
+
+.uniui-gift:before {
+  content: "\e6a4";
+}
+
+.uniui-link:before {
+  content: "\e6a5";
+}
+
+.uniui-notification:before {
+  content: "\e6a6";
+}
+
+.uniui-staff:before {
+  content: "\e6a7";
+}
+
+.uniui-vip:before {
+  content: "\e6a8";
+}
+
+.uniui-folder-add:before {
+  content: "\e6a9";
+}
+
+.uniui-tune:before {
+  content: "\e6aa";
+}
+
+.uniui-auth:before {
+  content: "\e6ab";
+}
+
+.uniui-person:before {
+  content: "\e699";
+}
+
+.uniui-email-filled:before {
+  content: "\e69a";
+}
+
+.uniui-phone-filled:before {
+  content: "\e69b";
+}
+
+.uniui-phone:before {
+  content: "\e69c";
+}
+
+.uniui-email:before {
+  content: "\e69e";
+}
+
+.uniui-personadd:before {
+  content: "\e69f";
+}
+
+.uniui-chatboxes-filled:before {
+  content: "\e692";
+}
+
+.uniui-contact:before {
+  content: "\e693";
+}
+
+.uniui-chatbubble-filled:before {
+  content: "\e694";
+}
+
+.uniui-contact-filled:before {
+  content: "\e695";
+}
+
+.uniui-chatboxes:before {
+  content: "\e696";
+}
+
+.uniui-chatbubble:before {
+  content: "\e697";
+}
+
+.uniui-upload-filled:before {
+  content: "\e68e";
+}
+
+.uniui-upload:before {
+  content: "\e690";
+}
+
+.uniui-weixin:before {
+  content: "\e691";
+}
+
+.uniui-compose:before {
+  content: "\e67f";
+}
+
+.uniui-qq:before {
+  content: "\e680";
+}
+
+.uniui-download-filled:before {
+  content: "\e681";
+}
+
+.uniui-pyq:before {
+  content: "\e682";
+}
+
+.uniui-sound:before {
+  content: "\e684";
+}
+
+.uniui-trash-filled:before {
+  content: "\e685";
+}
+
+.uniui-sound-filled:before {
+  content: "\e686";
+}
+
+.uniui-trash:before {
+  content: "\e687";
+}
+
+.uniui-videocam-filled:before {
+  content: "\e689";
+}
+
+.uniui-spinner-cycle:before {
+  content: "\e68a";
+}
+
+.uniui-weibo:before {
+  content: "\e68b";
+}
+
+.uniui-videocam:before {
+  content: "\e68c";
+}
+
+.uniui-download:before {
+  content: "\e68d";
+}
+
+.uniui-help:before {
+  content: "\e679";
+}
+
+.uniui-navigate-filled:before {
+  content: "\e67a";
+}
+
+.uniui-plusempty:before {
+  content: "\e67b";
+}
+
+.uniui-smallcircle:before {
+  content: "\e67c";
+}
+
+.uniui-minus-filled:before {
+  content: "\e67d";
+}
+
+.uniui-micoff:before {
+  content: "\e67e";
+}
+
+.uniui-closeempty:before {
+  content: "\e66c";
+}
+
+.uniui-clear:before {
+  content: "\e66d";
+}
+
+.uniui-navigate:before {
+  content: "\e66e";
+}
+
+.uniui-minus:before {
+  content: "\e66f";
+}
+
+.uniui-image:before {
+  content: "\e670";
+}
+
+.uniui-mic:before {
+  content: "\e671";
+}
+
+.uniui-paperplane:before {
+  content: "\e672";
+}
+
+.uniui-close:before {
+  content: "\e673";
+}
+
+.uniui-help-filled:before {
+  content: "\e674";
+}
+
+.uniui-paperplane-filled:before {
+  content: "\e675";
+}
+
+.uniui-plus:before {
+  content: "\e676";
+}
+
+.uniui-mic-filled:before {
+  content: "\e677";
+}
+
+.uniui-image-filled:before {
+  content: "\e678";
+}
+
+.uniui-locked-filled:before {
+  content: "\e668";
+}
+
+.uniui-info:before {
+  content: "\e669";
+}
+
+.uniui-locked:before {
+  content: "\e66b";
+}
+
+.uniui-camera-filled:before {
+  content: "\e658";
+}
+
+.uniui-chat-filled:before {
+  content: "\e659";
+}
+
+.uniui-camera:before {
+  content: "\e65a";
+}
+
+.uniui-circle:before {
+  content: "\e65b";
+}
+
+.uniui-checkmarkempty:before {
+  content: "\e65c";
+}
+
+.uniui-chat:before {
+  content: "\e65d";
+}
+
+.uniui-circle-filled:before {
+  content: "\e65e";
+}
+
+.uniui-flag:before {
+  content: "\e65f";
+}
+
+.uniui-flag-filled:before {
+  content: "\e660";
+}
+
+.uniui-gear-filled:before {
+  content: "\e661";
+}
+
+.uniui-home:before {
+  content: "\e662";
+}
+
+.uniui-home-filled:before {
+  content: "\e663";
+}
+
+.uniui-gear:before {
+  content: "\e664";
+}
+
+.uniui-smallcircle-filled:before {
+  content: "\e665";
+}
+
+.uniui-map-filled:before {
+  content: "\e666";
+}
+
+.uniui-map:before {
+  content: "\e667";
+}
+
+.uniui-refresh-filled:before {
+  content: "\e656";
+}
+
+.uniui-refresh:before {
+  content: "\e657";
+}
+
+.uniui-cloud-upload:before {
+  content: "\e645";
+}
+
+.uniui-cloud-download-filled:before {
+  content: "\e646";
+}
+
+.uniui-cloud-download:before {
+  content: "\e647";
+}
+
+.uniui-cloud-upload-filled:before {
+  content: "\e648";
+}
+
+.uniui-redo:before {
+  content: "\e64a";
+}
+
+.uniui-images-filled:before {
+  content: "\e64b";
+}
+
+.uniui-undo-filled:before {
+  content: "\e64c";
+}
+
+.uniui-more:before {
+  content: "\e64d";
+}
+
+.uniui-more-filled:before {
+  content: "\e64e";
+}
+
+.uniui-undo:before {
+  content: "\e64f";
+}
+
+.uniui-images:before {
+  content: "\e650";
+}
+
+.uniui-paperclip:before {
+  content: "\e652";
+}
+
+.uniui-settings:before {
+  content: "\e653";
+}
+
+.uniui-search:before {
+  content: "\e654";
+}
+
+.uniui-redo-filled:before {
+  content: "\e655";
+}
+
+.uniui-list:before {
+  content: "\e644";
+}
+
+.uniui-mail-open-filled:before {
+  content: "\e63a";
+}
+
+.uniui-hand-down-filled:before {
+  content: "\e63c";
+}
+
+.uniui-hand-down:before {
+  content: "\e63d";
+}
+
+.uniui-hand-up-filled:before {
+  content: "\e63e";
+}
+
+.uniui-hand-up:before {
+  content: "\e63f";
+}
+
+.uniui-heart-filled:before {
+  content: "\e641";
+}
+
+.uniui-mail-open:before {
+  content: "\e643";
+}
+
+.uniui-heart:before {
+  content: "\e639";
+}
+
+.uniui-loop:before {
+  content: "\e633";
+}
+
+.uniui-pulldown:before {
+  content: "\e632";
+}
+
+.uniui-scan:before {
+  content: "\e62a";
+}
+
+.uniui-bars:before {
+  content: "\e627";
+}
+
+.uniui-checkbox:before {
+  content: "\e62b";
+}
+
+.uniui-checkbox-filled:before {
+  content: "\e62c";
+}
+
+.uniui-shop:before {
+  content: "\e62f";
+}
+
+.uniui-headphones:before {
+  content: "\e630";
+}
+
+.uniui-cart:before {
+  content: "\e631";
+}

二進制
uni_modules/uni-icons/components/uni-icons/uniicons.ttf


+ 664 - 0
uni_modules/uni-icons/components/uni-icons/uniicons_file.ts

@@ -0,0 +1,664 @@
+
+export type IconsData = {
+	id : string
+	name : string
+	font_family : string
+	css_prefix_text : string
+	description : string
+	glyphs : Array<IconsDataItem>
+}
+
+export type IconsDataItem = {
+	font_class : string
+	unicode : string
+}
+
+
+export const fontData = [
+  {
+    "font_class": "arrow-down",
+    "unicode": "\ue6be"
+  },
+  {
+    "font_class": "arrow-left",
+    "unicode": "\ue6bc"
+  },
+  {
+    "font_class": "arrow-right",
+    "unicode": "\ue6bb"
+  },
+  {
+    "font_class": "arrow-up",
+    "unicode": "\ue6bd"
+  },
+  {
+    "font_class": "auth",
+    "unicode": "\ue6ab"
+  },
+  {
+    "font_class": "auth-filled",
+    "unicode": "\ue6cc"
+  },
+  {
+    "font_class": "back",
+    "unicode": "\ue6b9"
+  },
+  {
+    "font_class": "bars",
+    "unicode": "\ue627"
+  },
+  {
+    "font_class": "calendar",
+    "unicode": "\ue6a0"
+  },
+  {
+    "font_class": "calendar-filled",
+    "unicode": "\ue6c0"
+  },
+  {
+    "font_class": "camera",
+    "unicode": "\ue65a"
+  },
+  {
+    "font_class": "camera-filled",
+    "unicode": "\ue658"
+  },
+  {
+    "font_class": "cart",
+    "unicode": "\ue631"
+  },
+  {
+    "font_class": "cart-filled",
+    "unicode": "\ue6d0"
+  },
+  {
+    "font_class": "chat",
+    "unicode": "\ue65d"
+  },
+  {
+    "font_class": "chat-filled",
+    "unicode": "\ue659"
+  },
+  {
+    "font_class": "chatboxes",
+    "unicode": "\ue696"
+  },
+  {
+    "font_class": "chatboxes-filled",
+    "unicode": "\ue692"
+  },
+  {
+    "font_class": "chatbubble",
+    "unicode": "\ue697"
+  },
+  {
+    "font_class": "chatbubble-filled",
+    "unicode": "\ue694"
+  },
+  {
+    "font_class": "checkbox",
+    "unicode": "\ue62b"
+  },
+  {
+    "font_class": "checkbox-filled",
+    "unicode": "\ue62c"
+  },
+  {
+    "font_class": "checkmarkempty",
+    "unicode": "\ue65c"
+  },
+  {
+    "font_class": "circle",
+    "unicode": "\ue65b"
+  },
+  {
+    "font_class": "circle-filled",
+    "unicode": "\ue65e"
+  },
+  {
+    "font_class": "clear",
+    "unicode": "\ue66d"
+  },
+  {
+    "font_class": "close",
+    "unicode": "\ue673"
+  },
+  {
+    "font_class": "closeempty",
+    "unicode": "\ue66c"
+  },
+  {
+    "font_class": "cloud-download",
+    "unicode": "\ue647"
+  },
+  {
+    "font_class": "cloud-download-filled",
+    "unicode": "\ue646"
+  },
+  {
+    "font_class": "cloud-upload",
+    "unicode": "\ue645"
+  },
+  {
+    "font_class": "cloud-upload-filled",
+    "unicode": "\ue648"
+  },
+  {
+    "font_class": "color",
+    "unicode": "\ue6cf"
+  },
+  {
+    "font_class": "color-filled",
+    "unicode": "\ue6c9"
+  },
+  {
+    "font_class": "compose",
+    "unicode": "\ue67f"
+  },
+  {
+    "font_class": "contact",
+    "unicode": "\ue693"
+  },
+  {
+    "font_class": "contact-filled",
+    "unicode": "\ue695"
+  },
+  {
+    "font_class": "down",
+    "unicode": "\ue6b8"
+  },
+	{
+	  "font_class": "bottom",
+	  "unicode": "\ue6b8"
+	},
+  {
+    "font_class": "download",
+    "unicode": "\ue68d"
+  },
+  {
+    "font_class": "download-filled",
+    "unicode": "\ue681"
+  },
+  {
+    "font_class": "email",
+    "unicode": "\ue69e"
+  },
+  {
+    "font_class": "email-filled",
+    "unicode": "\ue69a"
+  },
+  {
+    "font_class": "eye",
+    "unicode": "\ue651"
+  },
+  {
+    "font_class": "eye-filled",
+    "unicode": "\ue66a"
+  },
+  {
+    "font_class": "eye-slash",
+    "unicode": "\ue6b3"
+  },
+  {
+    "font_class": "eye-slash-filled",
+    "unicode": "\ue6b4"
+  },
+  {
+    "font_class": "fire",
+    "unicode": "\ue6a1"
+  },
+  {
+    "font_class": "fire-filled",
+    "unicode": "\ue6c5"
+  },
+  {
+    "font_class": "flag",
+    "unicode": "\ue65f"
+  },
+  {
+    "font_class": "flag-filled",
+    "unicode": "\ue660"
+  },
+  {
+    "font_class": "folder-add",
+    "unicode": "\ue6a9"
+  },
+  {
+    "font_class": "folder-add-filled",
+    "unicode": "\ue6c8"
+  },
+  {
+    "font_class": "font",
+    "unicode": "\ue6a3"
+  },
+  {
+    "font_class": "forward",
+    "unicode": "\ue6ba"
+  },
+  {
+    "font_class": "gear",
+    "unicode": "\ue664"
+  },
+  {
+    "font_class": "gear-filled",
+    "unicode": "\ue661"
+  },
+  {
+    "font_class": "gift",
+    "unicode": "\ue6a4"
+  },
+  {
+    "font_class": "gift-filled",
+    "unicode": "\ue6c4"
+  },
+  {
+    "font_class": "hand-down",
+    "unicode": "\ue63d"
+  },
+  {
+    "font_class": "hand-down-filled",
+    "unicode": "\ue63c"
+  },
+  {
+    "font_class": "hand-up",
+    "unicode": "\ue63f"
+  },
+  {
+    "font_class": "hand-up-filled",
+    "unicode": "\ue63e"
+  },
+  {
+    "font_class": "headphones",
+    "unicode": "\ue630"
+  },
+  {
+    "font_class": "heart",
+    "unicode": "\ue639"
+  },
+  {
+    "font_class": "heart-filled",
+    "unicode": "\ue641"
+  },
+  {
+    "font_class": "help",
+    "unicode": "\ue679"
+  },
+  {
+    "font_class": "help-filled",
+    "unicode": "\ue674"
+  },
+  {
+    "font_class": "home",
+    "unicode": "\ue662"
+  },
+  {
+    "font_class": "home-filled",
+    "unicode": "\ue663"
+  },
+  {
+    "font_class": "image",
+    "unicode": "\ue670"
+  },
+  {
+    "font_class": "image-filled",
+    "unicode": "\ue678"
+  },
+  {
+    "font_class": "images",
+    "unicode": "\ue650"
+  },
+  {
+    "font_class": "images-filled",
+    "unicode": "\ue64b"
+  },
+  {
+    "font_class": "info",
+    "unicode": "\ue669"
+  },
+  {
+    "font_class": "info-filled",
+    "unicode": "\ue649"
+  },
+  {
+    "font_class": "left",
+    "unicode": "\ue6b7"
+  },
+  {
+    "font_class": "link",
+    "unicode": "\ue6a5"
+  },
+  {
+    "font_class": "list",
+    "unicode": "\ue644"
+  },
+  {
+    "font_class": "location",
+    "unicode": "\ue6ae"
+  },
+  {
+    "font_class": "location-filled",
+    "unicode": "\ue6af"
+  },
+  {
+    "font_class": "locked",
+    "unicode": "\ue66b"
+  },
+  {
+    "font_class": "locked-filled",
+    "unicode": "\ue668"
+  },
+  {
+    "font_class": "loop",
+    "unicode": "\ue633"
+  },
+  {
+    "font_class": "mail-open",
+    "unicode": "\ue643"
+  },
+  {
+    "font_class": "mail-open-filled",
+    "unicode": "\ue63a"
+  },
+  {
+    "font_class": "map",
+    "unicode": "\ue667"
+  },
+  {
+    "font_class": "map-filled",
+    "unicode": "\ue666"
+  },
+  {
+    "font_class": "map-pin",
+    "unicode": "\ue6ad"
+  },
+  {
+    "font_class": "map-pin-ellipse",
+    "unicode": "\ue6ac"
+  },
+  {
+    "font_class": "medal",
+    "unicode": "\ue6a2"
+  },
+  {
+    "font_class": "medal-filled",
+    "unicode": "\ue6c3"
+  },
+  {
+    "font_class": "mic",
+    "unicode": "\ue671"
+  },
+  {
+    "font_class": "mic-filled",
+    "unicode": "\ue677"
+  },
+  {
+    "font_class": "micoff",
+    "unicode": "\ue67e"
+  },
+  {
+    "font_class": "micoff-filled",
+    "unicode": "\ue6b0"
+  },
+  {
+    "font_class": "minus",
+    "unicode": "\ue66f"
+  },
+  {
+    "font_class": "minus-filled",
+    "unicode": "\ue67d"
+  },
+  {
+    "font_class": "more",
+    "unicode": "\ue64d"
+  },
+  {
+    "font_class": "more-filled",
+    "unicode": "\ue64e"
+  },
+  {
+    "font_class": "navigate",
+    "unicode": "\ue66e"
+  },
+  {
+    "font_class": "navigate-filled",
+    "unicode": "\ue67a"
+  },
+  {
+    "font_class": "notification",
+    "unicode": "\ue6a6"
+  },
+  {
+    "font_class": "notification-filled",
+    "unicode": "\ue6c1"
+  },
+  {
+    "font_class": "paperclip",
+    "unicode": "\ue652"
+  },
+  {
+    "font_class": "paperplane",
+    "unicode": "\ue672"
+  },
+  {
+    "font_class": "paperplane-filled",
+    "unicode": "\ue675"
+  },
+  {
+    "font_class": "person",
+    "unicode": "\ue699"
+  },
+  {
+    "font_class": "person-filled",
+    "unicode": "\ue69d"
+  },
+  {
+    "font_class": "personadd",
+    "unicode": "\ue69f"
+  },
+  {
+    "font_class": "personadd-filled",
+    "unicode": "\ue698"
+  },
+  {
+    "font_class": "personadd-filled-copy",
+    "unicode": "\ue6d1"
+  },
+  {
+    "font_class": "phone",
+    "unicode": "\ue69c"
+  },
+  {
+    "font_class": "phone-filled",
+    "unicode": "\ue69b"
+  },
+  {
+    "font_class": "plus",
+    "unicode": "\ue676"
+  },
+  {
+    "font_class": "plus-filled",
+    "unicode": "\ue6c7"
+  },
+  {
+    "font_class": "plusempty",
+    "unicode": "\ue67b"
+  },
+  {
+    "font_class": "pulldown",
+    "unicode": "\ue632"
+  },
+  {
+    "font_class": "pyq",
+    "unicode": "\ue682"
+  },
+  {
+    "font_class": "qq",
+    "unicode": "\ue680"
+  },
+  {
+    "font_class": "redo",
+    "unicode": "\ue64a"
+  },
+  {
+    "font_class": "redo-filled",
+    "unicode": "\ue655"
+  },
+  {
+    "font_class": "refresh",
+    "unicode": "\ue657"
+  },
+  {
+    "font_class": "refresh-filled",
+    "unicode": "\ue656"
+  },
+  {
+    "font_class": "refreshempty",
+    "unicode": "\ue6bf"
+  },
+  {
+    "font_class": "reload",
+    "unicode": "\ue6b2"
+  },
+  {
+    "font_class": "right",
+    "unicode": "\ue6b5"
+  },
+  {
+    "font_class": "scan",
+    "unicode": "\ue62a"
+  },
+  {
+    "font_class": "search",
+    "unicode": "\ue654"
+  },
+  {
+    "font_class": "settings",
+    "unicode": "\ue653"
+  },
+  {
+    "font_class": "settings-filled",
+    "unicode": "\ue6ce"
+  },
+  {
+    "font_class": "shop",
+    "unicode": "\ue62f"
+  },
+  {
+    "font_class": "shop-filled",
+    "unicode": "\ue6cd"
+  },
+  {
+    "font_class": "smallcircle",
+    "unicode": "\ue67c"
+  },
+  {
+    "font_class": "smallcircle-filled",
+    "unicode": "\ue665"
+  },
+  {
+    "font_class": "sound",
+    "unicode": "\ue684"
+  },
+  {
+    "font_class": "sound-filled",
+    "unicode": "\ue686"
+  },
+  {
+    "font_class": "spinner-cycle",
+    "unicode": "\ue68a"
+  },
+  {
+    "font_class": "staff",
+    "unicode": "\ue6a7"
+  },
+  {
+    "font_class": "staff-filled",
+    "unicode": "\ue6cb"
+  },
+  {
+    "font_class": "star",
+    "unicode": "\ue688"
+  },
+  {
+    "font_class": "star-filled",
+    "unicode": "\ue68f"
+  },
+  {
+    "font_class": "starhalf",
+    "unicode": "\ue683"
+  },
+  {
+    "font_class": "trash",
+    "unicode": "\ue687"
+  },
+  {
+    "font_class": "trash-filled",
+    "unicode": "\ue685"
+  },
+  {
+    "font_class": "tune",
+    "unicode": "\ue6aa"
+  },
+  {
+    "font_class": "tune-filled",
+    "unicode": "\ue6ca"
+  },
+  {
+    "font_class": "undo",
+    "unicode": "\ue64f"
+  },
+  {
+    "font_class": "undo-filled",
+    "unicode": "\ue64c"
+  },
+  {
+    "font_class": "up",
+    "unicode": "\ue6b6"
+  },
+	{
+	  "font_class": "top",
+	  "unicode": "\ue6b6"
+	},
+  {
+    "font_class": "upload",
+    "unicode": "\ue690"
+  },
+  {
+    "font_class": "upload-filled",
+    "unicode": "\ue68e"
+  },
+  {
+    "font_class": "videocam",
+    "unicode": "\ue68c"
+  },
+  {
+    "font_class": "videocam-filled",
+    "unicode": "\ue689"
+  },
+  {
+    "font_class": "vip",
+    "unicode": "\ue6a8"
+  },
+  {
+    "font_class": "vip-filled",
+    "unicode": "\ue6c6"
+  },
+  {
+    "font_class": "wallet",
+    "unicode": "\ue6b1"
+  },
+  {
+    "font_class": "wallet-filled",
+    "unicode": "\ue6c2"
+  },
+  {
+    "font_class": "weibo",
+    "unicode": "\ue68b"
+  },
+  {
+    "font_class": "weixin",
+    "unicode": "\ue691"
+  }
+] as IconsDataItem[]
+
+// export const fontData = JSON.parse<IconsDataItem>(fontDataJson)

+ 649 - 0
uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js

@@ -0,0 +1,649 @@
+
+export const fontData = [
+  {
+    "font_class": "arrow-down",
+    "unicode": "\ue6be"
+  },
+  {
+    "font_class": "arrow-left",
+    "unicode": "\ue6bc"
+  },
+  {
+    "font_class": "arrow-right",
+    "unicode": "\ue6bb"
+  },
+  {
+    "font_class": "arrow-up",
+    "unicode": "\ue6bd"
+  },
+  {
+    "font_class": "auth",
+    "unicode": "\ue6ab"
+  },
+  {
+    "font_class": "auth-filled",
+    "unicode": "\ue6cc"
+  },
+  {
+    "font_class": "back",
+    "unicode": "\ue6b9"
+  },
+  {
+    "font_class": "bars",
+    "unicode": "\ue627"
+  },
+  {
+    "font_class": "calendar",
+    "unicode": "\ue6a0"
+  },
+  {
+    "font_class": "calendar-filled",
+    "unicode": "\ue6c0"
+  },
+  {
+    "font_class": "camera",
+    "unicode": "\ue65a"
+  },
+  {
+    "font_class": "camera-filled",
+    "unicode": "\ue658"
+  },
+  {
+    "font_class": "cart",
+    "unicode": "\ue631"
+  },
+  {
+    "font_class": "cart-filled",
+    "unicode": "\ue6d0"
+  },
+  {
+    "font_class": "chat",
+    "unicode": "\ue65d"
+  },
+  {
+    "font_class": "chat-filled",
+    "unicode": "\ue659"
+  },
+  {
+    "font_class": "chatboxes",
+    "unicode": "\ue696"
+  },
+  {
+    "font_class": "chatboxes-filled",
+    "unicode": "\ue692"
+  },
+  {
+    "font_class": "chatbubble",
+    "unicode": "\ue697"
+  },
+  {
+    "font_class": "chatbubble-filled",
+    "unicode": "\ue694"
+  },
+  {
+    "font_class": "checkbox",
+    "unicode": "\ue62b"
+  },
+  {
+    "font_class": "checkbox-filled",
+    "unicode": "\ue62c"
+  },
+  {
+    "font_class": "checkmarkempty",
+    "unicode": "\ue65c"
+  },
+  {
+    "font_class": "circle",
+    "unicode": "\ue65b"
+  },
+  {
+    "font_class": "circle-filled",
+    "unicode": "\ue65e"
+  },
+  {
+    "font_class": "clear",
+    "unicode": "\ue66d"
+  },
+  {
+    "font_class": "close",
+    "unicode": "\ue673"
+  },
+  {
+    "font_class": "closeempty",
+    "unicode": "\ue66c"
+  },
+  {
+    "font_class": "cloud-download",
+    "unicode": "\ue647"
+  },
+  {
+    "font_class": "cloud-download-filled",
+    "unicode": "\ue646"
+  },
+  {
+    "font_class": "cloud-upload",
+    "unicode": "\ue645"
+  },
+  {
+    "font_class": "cloud-upload-filled",
+    "unicode": "\ue648"
+  },
+  {
+    "font_class": "color",
+    "unicode": "\ue6cf"
+  },
+  {
+    "font_class": "color-filled",
+    "unicode": "\ue6c9"
+  },
+  {
+    "font_class": "compose",
+    "unicode": "\ue67f"
+  },
+  {
+    "font_class": "contact",
+    "unicode": "\ue693"
+  },
+  {
+    "font_class": "contact-filled",
+    "unicode": "\ue695"
+  },
+  {
+    "font_class": "down",
+    "unicode": "\ue6b8"
+  },
+	{
+	  "font_class": "bottom",
+	  "unicode": "\ue6b8"
+	},
+  {
+    "font_class": "download",
+    "unicode": "\ue68d"
+  },
+  {
+    "font_class": "download-filled",
+    "unicode": "\ue681"
+  },
+  {
+    "font_class": "email",
+    "unicode": "\ue69e"
+  },
+  {
+    "font_class": "email-filled",
+    "unicode": "\ue69a"
+  },
+  {
+    "font_class": "eye",
+    "unicode": "\ue651"
+  },
+  {
+    "font_class": "eye-filled",
+    "unicode": "\ue66a"
+  },
+  {
+    "font_class": "eye-slash",
+    "unicode": "\ue6b3"
+  },
+  {
+    "font_class": "eye-slash-filled",
+    "unicode": "\ue6b4"
+  },
+  {
+    "font_class": "fire",
+    "unicode": "\ue6a1"
+  },
+  {
+    "font_class": "fire-filled",
+    "unicode": "\ue6c5"
+  },
+  {
+    "font_class": "flag",
+    "unicode": "\ue65f"
+  },
+  {
+    "font_class": "flag-filled",
+    "unicode": "\ue660"
+  },
+  {
+    "font_class": "folder-add",
+    "unicode": "\ue6a9"
+  },
+  {
+    "font_class": "folder-add-filled",
+    "unicode": "\ue6c8"
+  },
+  {
+    "font_class": "font",
+    "unicode": "\ue6a3"
+  },
+  {
+    "font_class": "forward",
+    "unicode": "\ue6ba"
+  },
+  {
+    "font_class": "gear",
+    "unicode": "\ue664"
+  },
+  {
+    "font_class": "gear-filled",
+    "unicode": "\ue661"
+  },
+  {
+    "font_class": "gift",
+    "unicode": "\ue6a4"
+  },
+  {
+    "font_class": "gift-filled",
+    "unicode": "\ue6c4"
+  },
+  {
+    "font_class": "hand-down",
+    "unicode": "\ue63d"
+  },
+  {
+    "font_class": "hand-down-filled",
+    "unicode": "\ue63c"
+  },
+  {
+    "font_class": "hand-up",
+    "unicode": "\ue63f"
+  },
+  {
+    "font_class": "hand-up-filled",
+    "unicode": "\ue63e"
+  },
+  {
+    "font_class": "headphones",
+    "unicode": "\ue630"
+  },
+  {
+    "font_class": "heart",
+    "unicode": "\ue639"
+  },
+  {
+    "font_class": "heart-filled",
+    "unicode": "\ue641"
+  },
+  {
+    "font_class": "help",
+    "unicode": "\ue679"
+  },
+  {
+    "font_class": "help-filled",
+    "unicode": "\ue674"
+  },
+  {
+    "font_class": "home",
+    "unicode": "\ue662"
+  },
+  {
+    "font_class": "home-filled",
+    "unicode": "\ue663"
+  },
+  {
+    "font_class": "image",
+    "unicode": "\ue670"
+  },
+  {
+    "font_class": "image-filled",
+    "unicode": "\ue678"
+  },
+  {
+    "font_class": "images",
+    "unicode": "\ue650"
+  },
+  {
+    "font_class": "images-filled",
+    "unicode": "\ue64b"
+  },
+  {
+    "font_class": "info",
+    "unicode": "\ue669"
+  },
+  {
+    "font_class": "info-filled",
+    "unicode": "\ue649"
+  },
+  {
+    "font_class": "left",
+    "unicode": "\ue6b7"
+  },
+  {
+    "font_class": "link",
+    "unicode": "\ue6a5"
+  },
+  {
+    "font_class": "list",
+    "unicode": "\ue644"
+  },
+  {
+    "font_class": "location",
+    "unicode": "\ue6ae"
+  },
+  {
+    "font_class": "location-filled",
+    "unicode": "\ue6af"
+  },
+  {
+    "font_class": "locked",
+    "unicode": "\ue66b"
+  },
+  {
+    "font_class": "locked-filled",
+    "unicode": "\ue668"
+  },
+  {
+    "font_class": "loop",
+    "unicode": "\ue633"
+  },
+  {
+    "font_class": "mail-open",
+    "unicode": "\ue643"
+  },
+  {
+    "font_class": "mail-open-filled",
+    "unicode": "\ue63a"
+  },
+  {
+    "font_class": "map",
+    "unicode": "\ue667"
+  },
+  {
+    "font_class": "map-filled",
+    "unicode": "\ue666"
+  },
+  {
+    "font_class": "map-pin",
+    "unicode": "\ue6ad"
+  },
+  {
+    "font_class": "map-pin-ellipse",
+    "unicode": "\ue6ac"
+  },
+  {
+    "font_class": "medal",
+    "unicode": "\ue6a2"
+  },
+  {
+    "font_class": "medal-filled",
+    "unicode": "\ue6c3"
+  },
+  {
+    "font_class": "mic",
+    "unicode": "\ue671"
+  },
+  {
+    "font_class": "mic-filled",
+    "unicode": "\ue677"
+  },
+  {
+    "font_class": "micoff",
+    "unicode": "\ue67e"
+  },
+  {
+    "font_class": "micoff-filled",
+    "unicode": "\ue6b0"
+  },
+  {
+    "font_class": "minus",
+    "unicode": "\ue66f"
+  },
+  {
+    "font_class": "minus-filled",
+    "unicode": "\ue67d"
+  },
+  {
+    "font_class": "more",
+    "unicode": "\ue64d"
+  },
+  {
+    "font_class": "more-filled",
+    "unicode": "\ue64e"
+  },
+  {
+    "font_class": "navigate",
+    "unicode": "\ue66e"
+  },
+  {
+    "font_class": "navigate-filled",
+    "unicode": "\ue67a"
+  },
+  {
+    "font_class": "notification",
+    "unicode": "\ue6a6"
+  },
+  {
+    "font_class": "notification-filled",
+    "unicode": "\ue6c1"
+  },
+  {
+    "font_class": "paperclip",
+    "unicode": "\ue652"
+  },
+  {
+    "font_class": "paperplane",
+    "unicode": "\ue672"
+  },
+  {
+    "font_class": "paperplane-filled",
+    "unicode": "\ue675"
+  },
+  {
+    "font_class": "person",
+    "unicode": "\ue699"
+  },
+  {
+    "font_class": "person-filled",
+    "unicode": "\ue69d"
+  },
+  {
+    "font_class": "personadd",
+    "unicode": "\ue69f"
+  },
+  {
+    "font_class": "personadd-filled",
+    "unicode": "\ue698"
+  },
+  {
+    "font_class": "personadd-filled-copy",
+    "unicode": "\ue6d1"
+  },
+  {
+    "font_class": "phone",
+    "unicode": "\ue69c"
+  },
+  {
+    "font_class": "phone-filled",
+    "unicode": "\ue69b"
+  },
+  {
+    "font_class": "plus",
+    "unicode": "\ue676"
+  },
+  {
+    "font_class": "plus-filled",
+    "unicode": "\ue6c7"
+  },
+  {
+    "font_class": "plusempty",
+    "unicode": "\ue67b"
+  },
+  {
+    "font_class": "pulldown",
+    "unicode": "\ue632"
+  },
+  {
+    "font_class": "pyq",
+    "unicode": "\ue682"
+  },
+  {
+    "font_class": "qq",
+    "unicode": "\ue680"
+  },
+  {
+    "font_class": "redo",
+    "unicode": "\ue64a"
+  },
+  {
+    "font_class": "redo-filled",
+    "unicode": "\ue655"
+  },
+  {
+    "font_class": "refresh",
+    "unicode": "\ue657"
+  },
+  {
+    "font_class": "refresh-filled",
+    "unicode": "\ue656"
+  },
+  {
+    "font_class": "refreshempty",
+    "unicode": "\ue6bf"
+  },
+  {
+    "font_class": "reload",
+    "unicode": "\ue6b2"
+  },
+  {
+    "font_class": "right",
+    "unicode": "\ue6b5"
+  },
+  {
+    "font_class": "scan",
+    "unicode": "\ue62a"
+  },
+  {
+    "font_class": "search",
+    "unicode": "\ue654"
+  },
+  {
+    "font_class": "settings",
+    "unicode": "\ue653"
+  },
+  {
+    "font_class": "settings-filled",
+    "unicode": "\ue6ce"
+  },
+  {
+    "font_class": "shop",
+    "unicode": "\ue62f"
+  },
+  {
+    "font_class": "shop-filled",
+    "unicode": "\ue6cd"
+  },
+  {
+    "font_class": "smallcircle",
+    "unicode": "\ue67c"
+  },
+  {
+    "font_class": "smallcircle-filled",
+    "unicode": "\ue665"
+  },
+  {
+    "font_class": "sound",
+    "unicode": "\ue684"
+  },
+  {
+    "font_class": "sound-filled",
+    "unicode": "\ue686"
+  },
+  {
+    "font_class": "spinner-cycle",
+    "unicode": "\ue68a"
+  },
+  {
+    "font_class": "staff",
+    "unicode": "\ue6a7"
+  },
+  {
+    "font_class": "staff-filled",
+    "unicode": "\ue6cb"
+  },
+  {
+    "font_class": "star",
+    "unicode": "\ue688"
+  },
+  {
+    "font_class": "star-filled",
+    "unicode": "\ue68f"
+  },
+  {
+    "font_class": "starhalf",
+    "unicode": "\ue683"
+  },
+  {
+    "font_class": "trash",
+    "unicode": "\ue687"
+  },
+  {
+    "font_class": "trash-filled",
+    "unicode": "\ue685"
+  },
+  {
+    "font_class": "tune",
+    "unicode": "\ue6aa"
+  },
+  {
+    "font_class": "tune-filled",
+    "unicode": "\ue6ca"
+  },
+  {
+    "font_class": "undo",
+    "unicode": "\ue64f"
+  },
+  {
+    "font_class": "undo-filled",
+    "unicode": "\ue64c"
+  },
+  {
+    "font_class": "up",
+    "unicode": "\ue6b6"
+  },
+	{
+	  "font_class": "top",
+	  "unicode": "\ue6b6"
+	},
+  {
+    "font_class": "upload",
+    "unicode": "\ue690"
+  },
+  {
+    "font_class": "upload-filled",
+    "unicode": "\ue68e"
+  },
+  {
+    "font_class": "videocam",
+    "unicode": "\ue68c"
+  },
+  {
+    "font_class": "videocam-filled",
+    "unicode": "\ue689"
+  },
+  {
+    "font_class": "vip",
+    "unicode": "\ue6a8"
+  },
+  {
+    "font_class": "vip-filled",
+    "unicode": "\ue6c6"
+  },
+  {
+    "font_class": "wallet",
+    "unicode": "\ue6b1"
+  },
+  {
+    "font_class": "wallet-filled",
+    "unicode": "\ue6c2"
+  },
+  {
+    "font_class": "weibo",
+    "unicode": "\ue68b"
+  },
+  {
+    "font_class": "weixin",
+    "unicode": "\ue691"
+  }
+]
+
+// export const fontData = JSON.parse<IconsDataItem>(fontDataJson)

+ 88 - 0
uni_modules/uni-icons/package.json

@@ -0,0 +1,88 @@
+{
+  "id": "uni-icons",
+  "displayName": "uni-icons 图标",
+  "version": "2.0.9",
+  "description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "icon",
+    "图标"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": "^3.2.14"
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y",
+          "app-uvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+					"钉钉": "y",
+					"快手": "y",
+					"飞书": "y",
+					"京东": "y"
+        },
+        "快应用": {
+          "华为": "y",
+          "联盟": "y"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 8 - 0
uni_modules/uni-icons/readme.md

@@ -0,0 +1,8 @@
+## Icons 图标
+> **组件名:uni-icons**
+> 代码块: `uIcons`
+
+用于展示 icons 图标 。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-icons)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 8 - 0
uni_modules/uni-scss/changelog.md

@@ -0,0 +1,8 @@
+## 1.0.3(2022-01-21)
+- 优化 组件示例
+## 1.0.2(2021-11-22)
+- 修复 / 符号在 vue 不同版本兼容问题引起的报错问题
+## 1.0.1(2021-11-22)
+- 修复 vue3中scss语法兼容问题
+## 1.0.0(2021-11-18)
+- init

+ 1 - 0
uni_modules/uni-scss/index.scss

@@ -0,0 +1 @@
+@import './styles/index.scss';

+ 82 - 0
uni_modules/uni-scss/package.json

@@ -0,0 +1,82 @@
+{
+  "id": "uni-scss",
+  "displayName": "uni-scss 辅助样式",
+  "version": "1.0.3",
+  "description": "uni-sass是uni-ui提供的一套全局样式 ,通过一些简单的类名和sass变量,实现简单的页面布局操作,比如颜色、边距、圆角等。",
+  "keywords": [
+    "uni-scss",
+    "uni-ui",
+    "辅助样式"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "category": [
+        "JS SDK",
+        "通用 SDK"
+    ],
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "u"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "n",
+          "联盟": "n"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 4 - 0
uni_modules/uni-scss/readme.md

@@ -0,0 +1,4 @@
+`uni-sass` 是 `uni-ui`提供的一套全局样式 ,通过一些简单的类名和`sass`变量,实现简单的页面布局操作,比如颜色、边距、圆角等。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-sass)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 7 - 0
uni_modules/uni-scss/styles/index.scss

@@ -0,0 +1,7 @@
+@import './setting/_variables.scss';
+@import './setting/_border.scss';
+@import './setting/_color.scss';
+@import './setting/_space.scss';
+@import './setting/_radius.scss';
+@import './setting/_text.scss';
+@import './setting/_styles.scss';

+ 3 - 0
uni_modules/uni-scss/styles/setting/_border.scss

@@ -0,0 +1,3 @@
+.uni-border {
+	border: 1px $uni-border-1 solid;
+}

+ 66 - 0
uni_modules/uni-scss/styles/setting/_color.scss

@@ -0,0 +1,66 @@
+
+// TODO 暂时不需要 class ,需要用户使用变量实现 ,如果使用类名其实并不推荐
+// @mixin get-styles($k,$c) {
+// 	@if $k == size or $k == weight{
+// 		font-#{$k}:#{$c}
+// 	}@else{
+// 		#{$k}:#{$c}
+// 	}
+// }
+$uni-ui-color:(
+	// 主色
+	primary: $uni-primary,
+	primary-disable: $uni-primary-disable,
+	primary-light: $uni-primary-light,
+	// 辅助色
+	success: $uni-success,
+	success-disable: $uni-success-disable,
+	success-light: $uni-success-light,
+	warning: $uni-warning,
+	warning-disable: $uni-warning-disable,
+	warning-light: $uni-warning-light,
+	error: $uni-error,
+	error-disable: $uni-error-disable,
+	error-light: $uni-error-light,
+	info: $uni-info,
+	info-disable: $uni-info-disable,
+	info-light: $uni-info-light,
+	// 中性色
+	main-color: $uni-main-color,
+	base-color: $uni-base-color,
+	secondary-color: $uni-secondary-color,
+	extra-color: $uni-extra-color,
+	// 背景色
+	bg-color: $uni-bg-color,
+	// 边框颜色
+	border-1: $uni-border-1,
+	border-2: $uni-border-2,
+	border-3: $uni-border-3,
+	border-4: $uni-border-4,
+	// 黑色
+	black:$uni-black,
+	// 白色
+	white:$uni-white,
+	// 透明
+	transparent:$uni-transparent
+) !default;
+@each $key, $child in $uni-ui-color {
+	.uni-#{"" + $key} {
+		color: $child;
+	}
+	.uni-#{"" + $key}-bg {
+		background-color: $child;
+	}
+}
+.uni-shadow-sm {
+	box-shadow: $uni-shadow-sm;
+}
+.uni-shadow-base {
+	box-shadow: $uni-shadow-base;
+}
+.uni-shadow-lg {
+	box-shadow: $uni-shadow-lg;
+}
+.uni-mask {
+	background-color:$uni-mask;
+}

+ 55 - 0
uni_modules/uni-scss/styles/setting/_radius.scss

@@ -0,0 +1,55 @@
+@mixin radius($r,$d:null ,$important: false){
+  $radius-value:map-get($uni-radius, $r) if($important, !important, null);
+  // Key exists within the $uni-radius variable
+  @if (map-has-key($uni-radius, $r) and  $d){
+		@if $d == t {
+				border-top-left-radius:$radius-value;
+				border-top-right-radius:$radius-value;
+		}@else if $d == r {
+				border-top-right-radius:$radius-value;
+				border-bottom-right-radius:$radius-value;
+		}@else if $d == b {
+				border-bottom-left-radius:$radius-value;
+				border-bottom-right-radius:$radius-value;
+		}@else if $d == l {
+				border-top-left-radius:$radius-value;
+				border-bottom-left-radius:$radius-value;
+		}@else if $d == tl {
+				border-top-left-radius:$radius-value;
+		}@else if $d == tr {
+				border-top-right-radius:$radius-value;
+		}@else if $d == br {
+				border-bottom-right-radius:$radius-value;
+		}@else if $d == bl {
+				border-bottom-left-radius:$radius-value;
+		}
+  }@else{
+		border-radius:$radius-value;
+  }
+}
+
+@each $key, $child in $uni-radius {
+	@if($key){
+		.uni-radius-#{"" + $key} {
+				@include radius($key)
+		}
+	}@else{
+		.uni-radius {
+				@include radius($key)
+		}
+	}
+}
+
+@each $direction in t, r, b, l,tl, tr, br, bl {
+	@each $key, $child in $uni-radius {
+		@if($key){
+			.uni-radius-#{"" + $direction}-#{"" + $key} {
+				@include radius($key,$direction,false)
+			}
+		}@else{
+			.uni-radius-#{$direction} {
+				@include radius($key,$direction,false)
+			}
+		}
+	}
+}

+ 56 - 0
uni_modules/uni-scss/styles/setting/_space.scss

@@ -0,0 +1,56 @@
+
+@mixin fn($space,$direction,$size,$n) {
+	@if $n {
+		#{$space}-#{$direction}: #{$size*$uni-space-root}px
+	} @else {
+		 #{$space}-#{$direction}: #{-$size*$uni-space-root}px
+	}
+}
+@mixin get-styles($direction,$i,$space,$n){
+	@if $direction == t {
+		@include fn($space, top,$i,$n);
+	} 
+	@if $direction == r {
+		@include fn($space, right,$i,$n);
+	} 
+	@if $direction == b {
+		@include fn($space, bottom,$i,$n);
+	} 
+	@if $direction == l {
+	 @include fn($space, left,$i,$n);
+	} 
+	@if $direction == x {
+		@include fn($space, left,$i,$n);
+		@include fn($space, right,$i,$n);
+	} 
+	@if $direction == y {
+		@include fn($space, top,$i,$n);
+		@include fn($space, bottom,$i,$n);
+	} 
+	@if $direction == a {
+		@if $n {
+			#{$space}:#{$i*$uni-space-root}px;
+		} @else {
+			#{$space}:#{-$i*$uni-space-root}px;
+		}
+	} 
+}
+
+@each $orientation in m,p {
+	$space: margin;
+	@if $orientation == m {
+		$space: margin;
+	} @else {
+		$space: padding;
+	}
+	@for $i from 0 through 16 {
+		@each $direction in t, r, b, l, x, y, a {
+			.uni-#{$orientation}#{$direction}-#{$i} { 
+				@include  get-styles($direction,$i,$space,true);
+			} 
+			.uni-#{$orientation}#{$direction}-n#{$i} { 
+				@include  get-styles($direction,$i,$space,false);
+			}
+		}
+	}
+}

+ 167 - 0
uni_modules/uni-scss/styles/setting/_styles.scss

@@ -0,0 +1,167 @@
+/* #ifndef APP-NVUE */
+
+$-color-white:#fff;
+$-color-black:#000;
+@mixin base-style($color) {
+	color: #fff;
+	background-color: $color;
+	border-color: mix($-color-black, $color, 8%);
+	&:not([hover-class]):active {
+		background: mix($-color-black, $color, 10%);
+		border-color: mix($-color-black, $color, 20%);
+		color: $-color-white;
+		outline: none;
+	}
+}
+@mixin is-color($color) {
+	@include base-style($color);
+	&[loading] {
+		@include base-style($color);
+		&::before {
+			margin-right:5px;
+		}
+	}
+	&[disabled] {
+	  &,
+		&[loading],
+	  &:not([hover-class]):active {
+	    color: $-color-white;
+			border-color: mix(darken($color,10%), $-color-white);
+	    background-color: mix($color, $-color-white);
+	  }
+	}
+
+}
+@mixin base-plain-style($color) {
+	color:$color;
+	background-color: mix($-color-white, $color, 90%);
+	border-color: mix($-color-white, $color, 70%);
+	&:not([hover-class]):active {
+	  background: mix($-color-white, $color, 80%);
+	  color: $color;
+	  outline: none;
+		border-color: mix($-color-white, $color, 50%);
+	}
+}
+@mixin is-plain($color){
+	&[plain] {
+		@include base-plain-style($color);
+		&[loading] {
+			@include base-plain-style($color);
+			&::before {
+				margin-right:5px;
+			}
+		}
+		&[disabled] {
+		  &,
+		  &:active {
+		    color: mix($-color-white, $color, 40%);
+		    background-color: mix($-color-white, $color, 90%);
+				border-color: mix($-color-white, $color, 80%);
+		  }
+		}
+	}
+}
+
+
+.uni-btn {
+	margin: 5px;
+	color: #393939;
+	border:1px solid #ccc;
+	font-size: 16px;
+	font-weight: 200;
+	background-color: #F9F9F9;
+	// TODO 暂时处理边框隐藏一边的问题
+	overflow: visible;
+	&::after{
+		border: none;
+	}
+
+	&:not([type]),&[type=default] {
+		color: #999;
+		&[loading] {
+			background: none;
+			&::before {
+				margin-right:5px;
+			}
+		}
+
+
+
+		&[disabled]{
+			color: mix($-color-white, #999, 60%);
+		  &,
+			&[loading],
+		  &:active {
+				color: mix($-color-white, #999, 60%);
+		    background-color: mix($-color-white,$-color-black , 98%);
+				border-color: mix($-color-white,  #999, 85%);
+		  }
+		}
+
+		&[plain] {
+			color: #999;
+			background: none;
+			border-color: $uni-border-1;
+			&:not([hover-class]):active {
+				background: none;
+			  color: mix($-color-white, $-color-black, 80%);
+				border-color: mix($-color-white, $-color-black, 90%);
+			  outline: none;
+			}
+			&[disabled]{
+			  &,
+				&[loading],
+			  &:active {
+			    background: none;
+					color: mix($-color-white, #999, 60%);
+					border-color: mix($-color-white,  #999, 85%);
+			  }
+			}
+		}
+	}
+
+	&:not([hover-class]):active {
+	  color: mix($-color-white, $-color-black, 50%);
+	}
+
+	&[size=mini] {
+		font-size: 16px;
+		font-weight: 200;
+		border-radius: 8px;
+	}
+
+
+
+	&.uni-btn-small {
+		font-size: 14px;
+	}
+	&.uni-btn-mini {
+		font-size: 12px;
+	}
+
+	&.uni-btn-radius {
+		border-radius: 999px;
+	}
+	&[type=primary] {
+		@include is-color($uni-primary);
+		@include is-plain($uni-primary)
+	}
+	&[type=success] {
+		@include is-color($uni-success);
+		@include is-plain($uni-success)
+	}
+	&[type=error] {
+		@include is-color($uni-error);
+		@include is-plain($uni-error)
+	}
+	&[type=warning] {
+		@include is-color($uni-warning);
+		@include is-plain($uni-warning)
+	}
+	&[type=info] {
+		@include is-color($uni-info);
+		@include is-plain($uni-info)
+	}
+}
+/* #endif */

+ 24 - 0
uni_modules/uni-scss/styles/setting/_text.scss

@@ -0,0 +1,24 @@
+@mixin get-styles($k,$c) {
+	@if $k == size or $k == weight{
+		font-#{$k}:#{$c}
+	}@else{
+		#{$k}:#{$c}
+	}
+}
+
+@each $key, $child in $uni-headings {
+	/* #ifndef APP-NVUE */
+	.uni-#{$key} {
+		@each $k, $c in $child {
+			@include get-styles($k,$c)
+		}
+	}
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	.container .uni-#{$key} {
+		@each $k, $c in $child {
+			@include get-styles($k,$c)
+		}
+	}
+	/* #endif */
+}

+ 146 - 0
uni_modules/uni-scss/styles/setting/_variables.scss

@@ -0,0 +1,146 @@
+// @use "sass:math";
+@import  '../tools/functions.scss';
+// 间距基础倍数
+$uni-space-root: 2 !default;
+// 边框半径默认值
+$uni-radius-root:5px !default;
+$uni-radius: () !default;
+// 边框半径断点
+$uni-radius: map-deep-merge(
+  (
+    0: 0,
+		// TODO 当前版本暂时不支持 sm 属性
+    // 'sm': math.div($uni-radius-root, 2),
+    null: $uni-radius-root,
+    'lg': $uni-radius-root * 2,
+    'xl': $uni-radius-root * 6,
+    'pill': 9999px,
+    'circle': 50%
+  ),
+  $uni-radius
+);
+// 字体家族
+$body-font-family: 'Roboto', sans-serif !default;
+// 文本
+$heading-font-family: $body-font-family !default;
+$uni-headings: () !default;
+$letterSpacing: -0.01562em;
+$uni-headings: map-deep-merge(
+  (
+    'h1': (
+      size: 32px,
+			weight: 300,
+			line-height: 50px,
+			// letter-spacing:-0.01562em
+    ),
+    'h2': (
+      size: 28px,
+      weight: 300,
+      line-height: 40px,
+      // letter-spacing: -0.00833em
+    ),
+    'h3': (
+      size: 24px,
+      weight: 400,
+      line-height: 32px,
+      // letter-spacing: normal
+    ),
+    'h4': (
+      size: 20px,
+      weight: 400,
+      line-height: 30px,
+      // letter-spacing: 0.00735em
+    ),
+    'h5': (
+      size: 16px,
+      weight: 400,
+      line-height: 24px,
+      // letter-spacing: normal
+    ),
+    'h6': (
+      size: 14px,
+      weight: 500,
+      line-height: 18px,
+      // letter-spacing: 0.0125em
+    ),
+    'subtitle': (
+      size: 12px,
+      weight: 400,
+      line-height: 20px,
+      // letter-spacing: 0.00937em
+    ),
+    'body': (
+      font-size: 14px,
+			font-weight: 400,
+			line-height: 22px,
+			// letter-spacing: 0.03125em
+    ),
+    'caption': (
+      'size': 12px,
+      'weight': 400,
+      'line-height': 20px,
+      // 'letter-spacing': 0.03333em,
+      // 'text-transform': false
+    )
+  ),
+  $uni-headings
+);
+
+
+
+// 主色
+$uni-primary: #2979ff !default;
+$uni-primary-disable:lighten($uni-primary,20%) !default;
+$uni-primary-light: lighten($uni-primary,25%) !default;
+
+// 辅助色
+// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。
+$uni-success: #18bc37 !default;
+$uni-success-disable:lighten($uni-success,20%) !default;
+$uni-success-light: lighten($uni-success,25%) !default;
+
+$uni-warning: #f3a73f !default;
+$uni-warning-disable:lighten($uni-warning,20%) !default;
+$uni-warning-light: lighten($uni-warning,25%) !default;
+
+$uni-error: #e43d33 !default;
+$uni-error-disable:lighten($uni-error,20%) !default;
+$uni-error-light: lighten($uni-error,25%) !default;
+
+$uni-info: #8f939c !default;
+$uni-info-disable:lighten($uni-info,20%) !default;
+$uni-info-light: lighten($uni-info,25%) !default;
+
+// 中性色
+// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。
+$uni-main-color: #3a3a3a !default; 			// 主要文字
+$uni-base-color: #6a6a6a !default;			// 常规文字
+$uni-secondary-color: #909399 !default;	// 次要文字
+$uni-extra-color: #c7c7c7 !default;			// 辅助说明
+
+// 边框颜色
+$uni-border-1: #F0F0F0 !default;
+$uni-border-2: #EDEDED !default;
+$uni-border-3: #DCDCDC !default;
+$uni-border-4: #B9B9B9 !default;
+
+// 常规色
+$uni-black: #000000 !default;
+$uni-white: #ffffff !default;
+$uni-transparent: rgba($color: #000000, $alpha: 0) !default;
+
+// 背景色
+$uni-bg-color: #f7f7f7 !default;
+
+/* 水平间距 */
+$uni-spacing-sm: 8px !default;
+$uni-spacing-base: 15px !default;
+$uni-spacing-lg: 30px !default;
+
+// 阴影
+$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5) !default;
+$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default;
+$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5) !default;
+
+// 蒙版
+$uni-mask: rgba($color: #000000, $alpha: 0.4) !default;

+ 19 - 0
uni_modules/uni-scss/styles/tools/functions.scss

@@ -0,0 +1,19 @@
+// 合并 map
+@function map-deep-merge($parent-map, $child-map){
+	$result: $parent-map;
+	@each $key, $child in $child-map {
+		$parent-has-key: map-has-key($result, $key);
+		$parent-value: map-get($result, $key);
+		$parent-type: type-of($parent-value);
+		$child-type: type-of($child);
+		$parent-is-map: $parent-type == map;
+		$child-is-map: $child-type == map;
+			
+		@if (not $parent-has-key) or ($parent-type != $child-type) or (not ($parent-is-map and $child-is-map)){
+			$result: map-merge($result, ( $key: $child ));
+		}@else {
+			$result: map-merge($result, ( $key: map-deep-merge($parent-value, $child) ));
+		}
+	}
+	@return $result;
+};

+ 31 - 0
uni_modules/uni-scss/theme.scss

@@ -0,0 +1,31 @@
+// 间距基础倍数
+$uni-space-root: 2;
+// 边框半径默认值
+$uni-radius-root:5px;
+// 主色
+$uni-primary: #2979ff;
+// 辅助色
+$uni-success: #4cd964;
+// 警告色
+$uni-warning: #f0ad4e;
+// 错误色
+$uni-error: #dd524d;
+// 描述色
+$uni-info: #909399;
+// 中性色
+$uni-main-color: #303133;
+$uni-base-color: #606266;
+$uni-secondary-color: #909399;
+$uni-extra-color: #C0C4CC;
+// 背景色
+$uni-bg-color: #f5f5f5;
+// 边框颜色
+$uni-border-1: #DCDFE6;
+$uni-border-2: #E4E7ED;
+$uni-border-3: #EBEEF5;
+$uni-border-4: #F2F6FC;
+
+// 常规色
+$uni-black: #000000;
+$uni-white: #ffffff;
+$uni-transparent: rgba($color: #000000, $alpha: 0);

+ 62 - 0
uni_modules/uni-scss/variables.scss

@@ -0,0 +1,62 @@
+@import './styles/setting/_variables.scss';
+// 间距基础倍数
+$uni-space-root: 2;
+// 边框半径默认值
+$uni-radius-root:5px;
+
+// 主色
+$uni-primary: #2979ff;
+$uni-primary-disable:mix(#fff,$uni-primary,50%);
+$uni-primary-light: mix(#fff,$uni-primary,80%);
+
+// 辅助色
+// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。
+$uni-success: #18bc37;
+$uni-success-disable:mix(#fff,$uni-success,50%);
+$uni-success-light: mix(#fff,$uni-success,80%);
+
+$uni-warning: #f3a73f;
+$uni-warning-disable:mix(#fff,$uni-warning,50%);
+$uni-warning-light: mix(#fff,$uni-warning,80%);
+
+$uni-error: #e43d33;
+$uni-error-disable:mix(#fff,$uni-error,50%);
+$uni-error-light: mix(#fff,$uni-error,80%);
+
+$uni-info: #8f939c;
+$uni-info-disable:mix(#fff,$uni-info,50%);
+$uni-info-light: mix(#fff,$uni-info,80%);
+
+// 中性色
+// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。
+$uni-main-color: #3a3a3a; 			// 主要文字
+$uni-base-color: #6a6a6a;			// 常规文字
+$uni-secondary-color: #909399;	// 次要文字
+$uni-extra-color: #c7c7c7;			// 辅助说明
+
+// 边框颜色
+$uni-border-1: #F0F0F0;
+$uni-border-2: #EDEDED;
+$uni-border-3: #DCDCDC;
+$uni-border-4: #B9B9B9;
+
+// 常规色
+$uni-black: #000000;
+$uni-white: #ffffff;
+$uni-transparent: rgba($color: #000000, $alpha: 0);
+
+// 背景色
+$uni-bg-color: #f7f7f7;
+
+/* 水平间距 */
+$uni-spacing-sm: 8px;
+$uni-spacing-base: 15px;
+$uni-spacing-lg: 30px;
+
+// 阴影
+$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5);
+$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2);
+$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5);
+
+// 蒙版
+$uni-mask: rgba($color: #000000, $alpha: 0.4);