;
+
+ setConfig(onSend: (config: HttpRequestConfig) => HttpRequestConfig): void;
+}
+
+declare class HttpRequest extends HttpRequestAbstract { }
+export default HttpRequest;
diff --git a/uni_modules/uview-ui/libs/luch-request/index.js b/uni_modules/uview-ui/libs/luch-request/index.js
new file mode 100644
index 0000000..8fb2b44
--- /dev/null
+++ b/uni_modules/uview-ui/libs/luch-request/index.js
@@ -0,0 +1,3 @@
+import Request from './core/Request'
+
+export default Request
diff --git a/uni_modules/uview-ui/libs/luch-request/utils.js b/uni_modules/uview-ui/libs/luch-request/utils.js
new file mode 100644
index 0000000..847283d
--- /dev/null
+++ b/uni_modules/uview-ui/libs/luch-request/utils.js
@@ -0,0 +1,131 @@
+'use strict'
+
+// utils is a library of generic helper functions non-specific to axios
+
+const { toString } = Object.prototype
+
+/**
+ * Determine if a value is an Array
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is an Array, otherwise false
+ */
+export function isArray(val) {
+ return toString.call(val) === '[object Array]'
+}
+
+/**
+ * Determine if a value is an Object
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is an Object, otherwise false
+ */
+export function isObject(val) {
+ return val !== null && typeof val === 'object'
+}
+
+/**
+ * Determine if a value is a Date
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is a Date, otherwise false
+ */
+export function isDate(val) {
+ return toString.call(val) === '[object Date]'
+}
+
+/**
+ * Determine if a value is a URLSearchParams object
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is a URLSearchParams object, otherwise false
+ */
+export function isURLSearchParams(val) {
+ return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
+}
+
+/**
+ * Iterate over an Array or an Object invoking a function for each item.
+ *
+ * If `obj` is an Array callback will be called passing
+ * the value, index, and complete array for each item.
+ *
+ * If 'obj' is an Object callback will be called passing
+ * the value, key, and complete object for each property.
+ *
+ * @param {Object|Array} obj The object to iterate
+ * @param {Function} fn The callback to invoke for each item
+ */
+export function forEach(obj, fn) {
+ // Don't bother if no value provided
+ if (obj === null || typeof obj === 'undefined') {
+ return
+ }
+
+ // Force an array if not already something iterable
+ if (typeof obj !== 'object') {
+ /* eslint no-param-reassign:0 */
+ obj = [obj]
+ }
+
+ if (isArray(obj)) {
+ // Iterate over array values
+ for (let i = 0, l = obj.length; i < l; i++) {
+ fn.call(null, obj[i], i, obj)
+ }
+ } else {
+ // Iterate over object keys
+ for (const key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ fn.call(null, obj[key], key, obj)
+ }
+ }
+ }
+}
+
+/**
+ * 是否为boolean 值
+ * @param val
+ * @returns {boolean}
+ */
+export function isBoolean(val) {
+ return typeof val === 'boolean'
+}
+
+/**
+ * 是否为真正的对象{} new Object
+ * @param {any} obj - 检测的对象
+ * @returns {boolean}
+ */
+export function isPlainObject(obj) {
+ return Object.prototype.toString.call(obj) === '[object Object]'
+}
+
+/**
+ * Function equal to merge with the difference being that no reference
+ * to original objects is kept.
+ *
+ * @see merge
+ * @param {Object} obj1 Object to merge
+ * @returns {Object} Result of all merge properties
+ */
+export function deepMerge(/* obj1, obj2, obj3, ... */) {
+ const result = {}
+ function assignValue(val, key) {
+ if (typeof result[key] === 'object' && typeof val === 'object') {
+ result[key] = deepMerge(result[key], val)
+ } else if (typeof val === 'object') {
+ result[key] = deepMerge({}, val)
+ } else {
+ result[key] = val
+ }
+ }
+ for (let i = 0, l = arguments.length; i < l; i++) {
+ forEach(arguments[i], assignValue)
+ }
+ return result
+}
+
+export function isUndefined(val) {
+ return typeof val === 'undefined'
+}
diff --git a/uni_modules/uview-ui/libs/luch-request/utils/clone.js b/uni_modules/uview-ui/libs/luch-request/utils/clone.js
new file mode 100644
index 0000000..2fee704
--- /dev/null
+++ b/uni_modules/uview-ui/libs/luch-request/utils/clone.js
@@ -0,0 +1,264 @@
+/* eslint-disable */
+var clone = (function() {
+ 'use strict';
+
+ function _instanceof(obj, type) {
+ return type != null && obj instanceof type;
+ }
+
+ var nativeMap;
+ try {
+ nativeMap = Map;
+ } catch(_) {
+ // maybe a reference error because no `Map`. Give it a dummy value that no
+ // value will ever be an instanceof.
+ nativeMap = function() {};
+ }
+
+ var nativeSet;
+ try {
+ nativeSet = Set;
+ } catch(_) {
+ nativeSet = function() {};
+ }
+
+ var nativePromise;
+ try {
+ nativePromise = Promise;
+ } catch(_) {
+ nativePromise = function() {};
+ }
+
+ /**
+ * Clones (copies) an Object using deep copying.
+ *
+ * This function supports circular references by default, but if you are certain
+ * there are no circular references in your object, you can save some CPU time
+ * by calling clone(obj, false).
+ *
+ * Caution: if `circular` is false and `parent` contains circular references,
+ * your program may enter an infinite loop and crash.
+ *
+ * @param `parent` - the object to be cloned
+ * @param `circular` - set to true if the object to be cloned may contain
+ * circular references. (optional - true by default)
+ * @param `depth` - set to a number if the object is only to be cloned to
+ * a particular depth. (optional - defaults to Infinity)
+ * @param `prototype` - sets the prototype to be used when cloning an object.
+ * (optional - defaults to parent prototype).
+ * @param `includeNonEnumerable` - set to true if the non-enumerable properties
+ * should be cloned as well. Non-enumerable properties on the prototype
+ * chain will be ignored. (optional - false by default)
+ */
+ function clone(parent, circular, depth, prototype, includeNonEnumerable) {
+ if (typeof circular === 'object') {
+ depth = circular.depth;
+ prototype = circular.prototype;
+ includeNonEnumerable = circular.includeNonEnumerable;
+ circular = circular.circular;
+ }
+ // maintain two arrays for circular references, where corresponding parents
+ // and children have the same index
+ var allParents = [];
+ var allChildren = [];
+
+ var useBuffer = typeof Buffer != 'undefined';
+
+ if (typeof circular == 'undefined')
+ circular = true;
+
+ if (typeof depth == 'undefined')
+ depth = Infinity;
+
+ // recurse this function so we don't reset allParents and allChildren
+ function _clone(parent, depth) {
+ // cloning null always returns null
+ if (parent === null)
+ return null;
+
+ if (depth === 0)
+ return parent;
+
+ var child;
+ var proto;
+ if (typeof parent != 'object') {
+ return parent;
+ }
+
+ if (_instanceof(parent, nativeMap)) {
+ child = new nativeMap();
+ } else if (_instanceof(parent, nativeSet)) {
+ child = new nativeSet();
+ } else if (_instanceof(parent, nativePromise)) {
+ child = new nativePromise(function (resolve, reject) {
+ parent.then(function(value) {
+ resolve(_clone(value, depth - 1));
+ }, function(err) {
+ reject(_clone(err, depth - 1));
+ });
+ });
+ } else if (clone.__isArray(parent)) {
+ child = [];
+ } else if (clone.__isRegExp(parent)) {
+ child = new RegExp(parent.source, __getRegExpFlags(parent));
+ if (parent.lastIndex) child.lastIndex = parent.lastIndex;
+ } else if (clone.__isDate(parent)) {
+ child = new Date(parent.getTime());
+ } else if (useBuffer && Buffer.isBuffer(parent)) {
+ if (Buffer.from) {
+ // Node.js >= 5.10.0
+ child = Buffer.from(parent);
+ } else {
+ // Older Node.js versions
+ child = new Buffer(parent.length);
+ parent.copy(child);
+ }
+ return child;
+ } else if (_instanceof(parent, Error)) {
+ child = Object.create(parent);
+ } else {
+ if (typeof prototype == 'undefined') {
+ proto = Object.getPrototypeOf(parent);
+ child = Object.create(proto);
+ }
+ else {
+ child = Object.create(prototype);
+ proto = prototype;
+ }
+ }
+
+ if (circular) {
+ var index = allParents.indexOf(parent);
+
+ if (index != -1) {
+ return allChildren[index];
+ }
+ allParents.push(parent);
+ allChildren.push(child);
+ }
+
+ if (_instanceof(parent, nativeMap)) {
+ parent.forEach(function(value, key) {
+ var keyChild = _clone(key, depth - 1);
+ var valueChild = _clone(value, depth - 1);
+ child.set(keyChild, valueChild);
+ });
+ }
+ if (_instanceof(parent, nativeSet)) {
+ parent.forEach(function(value) {
+ var entryChild = _clone(value, depth - 1);
+ child.add(entryChild);
+ });
+ }
+
+ for (var i in parent) {
+ var attrs = Object.getOwnPropertyDescriptor(parent, i);
+ if (attrs) {
+ child[i] = _clone(parent[i], depth - 1);
+ }
+
+ try {
+ var objProperty = Object.getOwnPropertyDescriptor(parent, i);
+ if (objProperty.set === 'undefined') {
+ // no setter defined. Skip cloning this property
+ continue;
+ }
+ child[i] = _clone(parent[i], depth - 1);
+ } catch(e){
+ if (e instanceof TypeError) {
+ // when in strict mode, TypeError will be thrown if child[i] property only has a getter
+ // we can't do anything about this, other than inform the user that this property cannot be set.
+ continue
+ } else if (e instanceof ReferenceError) {
+ //this may happen in non strict mode
+ continue
+ }
+ }
+
+ }
+
+ if (Object.getOwnPropertySymbols) {
+ var symbols = Object.getOwnPropertySymbols(parent);
+ for (var i = 0; i < symbols.length; i++) {
+ // Don't need to worry about cloning a symbol because it is a primitive,
+ // like a number or string.
+ var symbol = symbols[i];
+ var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
+ if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
+ continue;
+ }
+ child[symbol] = _clone(parent[symbol], depth - 1);
+ Object.defineProperty(child, symbol, descriptor);
+ }
+ }
+
+ if (includeNonEnumerable) {
+ var allPropertyNames = Object.getOwnPropertyNames(parent);
+ for (var i = 0; i < allPropertyNames.length; i++) {
+ var propertyName = allPropertyNames[i];
+ var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
+ if (descriptor && descriptor.enumerable) {
+ continue;
+ }
+ child[propertyName] = _clone(parent[propertyName], depth - 1);
+ Object.defineProperty(child, propertyName, descriptor);
+ }
+ }
+
+ return child;
+ }
+
+ return _clone(parent, depth);
+ }
+
+ /**
+ * Simple flat clone using prototype, accepts only objects, usefull for property
+ * override on FLAT configuration object (no nested props).
+ *
+ * USE WITH CAUTION! This may not behave as you wish if you do not know how this
+ * works.
+ */
+ clone.clonePrototype = function clonePrototype(parent) {
+ if (parent === null)
+ return null;
+
+ var c = function () {};
+ c.prototype = parent;
+ return new c();
+ };
+
+// private utility functions
+
+ function __objToStr(o) {
+ return Object.prototype.toString.call(o);
+ }
+ clone.__objToStr = __objToStr;
+
+ function __isDate(o) {
+ return typeof o === 'object' && __objToStr(o) === '[object Date]';
+ }
+ clone.__isDate = __isDate;
+
+ function __isArray(o) {
+ return typeof o === 'object' && __objToStr(o) === '[object Array]';
+ }
+ clone.__isArray = __isArray;
+
+ function __isRegExp(o) {
+ return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
+ }
+ clone.__isRegExp = __isRegExp;
+
+ function __getRegExpFlags(re) {
+ var flags = '';
+ if (re.global) flags += 'g';
+ if (re.ignoreCase) flags += 'i';
+ if (re.multiline) flags += 'm';
+ return flags;
+ }
+ clone.__getRegExpFlags = __getRegExpFlags;
+
+ return clone;
+})();
+
+export default clone
diff --git a/uni_modules/uview-ui/libs/mixin/button.js b/uni_modules/uview-ui/libs/mixin/button.js
new file mode 100644
index 0000000..0c019c2
--- /dev/null
+++ b/uni_modules/uview-ui/libs/mixin/button.js
@@ -0,0 +1,13 @@
+export default {
+ props: {
+ lang: String,
+ sessionFrom: String,
+ sendMessageTitle: String,
+ sendMessagePath: String,
+ sendMessageImg: String,
+ showMessageCard: Boolean,
+ appParameter: String,
+ formType: String,
+ openType: String
+ }
+}
diff --git a/uni_modules/uview-ui/libs/mixin/mixin.js b/uni_modules/uview-ui/libs/mixin/mixin.js
new file mode 100644
index 0000000..f41a178
--- /dev/null
+++ b/uni_modules/uview-ui/libs/mixin/mixin.js
@@ -0,0 +1,160 @@
+module.exports = {
+ // 定义每个组件都可能需要用到的外部样式以及类名
+ props: {
+ // 每个组件都有的父组件传递的样式,可以为字符串或者对象形式
+ customStyle: {
+ type: [Object, String],
+ default: () => ({})
+ },
+ customClass: {
+ type: String,
+ default: ''
+ },
+ // 跳转的页面路径
+ url: {
+ type: String,
+ default: ''
+ },
+ // 页面跳转的类型
+ linkType: {
+ type: String,
+ default: 'navigateTo'
+ }
+ },
+ data() {
+ return {}
+ },
+ onLoad() {
+ // getRect挂载到$u上,因为这方法需要使用in(this),所以无法把它独立成一个单独的文件导出
+ this.$u.getRect = this.$uGetRect
+ },
+ created() {
+ // 组件当中,只有created声明周期,为了能在组件使用,故也在created中将方法挂载到$u
+ this.$u.getRect = this.$uGetRect
+ },
+ computed: {
+ // 在2.x版本中,将会把$u挂载到uni对象下,导致在模板中无法使用uni.$u.xxx形式
+ // 所以这里通过computed计算属性将其附加到this.$u上,就可以在模板或者js中使用uni.$u.xxx
+ // 只在nvue环境通过此方式引入完整的$u,其他平台会出现性能问题,非nvue则按需引入(主要原因是props过大)
+ $u() {
+ // #ifndef APP-NVUE
+ // 在非nvue端,移除props,http,mixin等对象,避免在小程序setData时数据过大影响性能
+ return uni.$u.deepMerge(uni.$u, {
+ props: undefined,
+ http: undefined,
+ mixin: undefined
+ })
+ // #endif
+ // #ifdef APP-NVUE
+ return uni.$u
+ // #endif
+ },
+ /**
+ * 生成bem规则类名
+ * 由于微信小程序,H5,nvue之间绑定class的差异,无法通过:class="[bem()]"的形式进行同用
+ * 故采用如下折中做法,最后返回的是数组(一般平台)或字符串(支付宝和字节跳动平台),类似['a', 'b', 'c']或'a b c'的形式
+ * @param {String} name 组件名称
+ * @param {Array} fixed 一直会存在的类名
+ * @param {Array} change 会根据变量值为true或者false而出现或者隐藏的类名
+ * @returns {Array|string}
+ */
+ bem() {
+ return function (name, fixed, change) {
+ // 类名前缀
+ const prefix = `u-${name}--`
+ const classes = {}
+ if (fixed) {
+ fixed.map((item) => {
+ // 这里的类名,会一直存在
+ classes[prefix + this[item]] = true
+ })
+ }
+ if (change) {
+ change.map((item) => {
+ // 这里的类名,会根据this[item]的值为true或者false,而进行添加或者移除某一个类
+ this[item] ? (classes[prefix + item] = this[item]) : (delete classes[prefix + item])
+ })
+ }
+ return Object.keys(classes)
+ // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
+ // #ifdef MP-ALIPAY || MP-TOUTIAO || MP-LARK
+ .join(' ')
+ // #endif
+ }
+ }
+ },
+ methods: {
+ // 跳转某一个页面
+ openPage(urlKey = 'url') {
+ const url = this[urlKey]
+ if (url) {
+ // 执行类似uni.navigateTo的方法
+ uni[this.linkType]({
+ url
+ })
+ }
+ },
+ // 查询节点信息
+ // 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸,为支付宝的bug(2020-07-21)
+ // 解决办法为在组件根部再套一个没有任何作用的view元素
+ $uGetRect(selector, all) {
+ return new Promise((resolve) => {
+ uni.createSelectorQuery()
+ .in(this)[all ? 'selectAll' : 'select'](selector)
+ .boundingClientRect((rect) => {
+ if (all && Array.isArray(rect) && rect.length) {
+ resolve(rect)
+ }
+ if (!all && rect) {
+ resolve(rect)
+ }
+ })
+ .exec()
+ })
+ },
+ getParentData(parentName = '') {
+ // 避免在created中去定义parent变量
+ if (!this.parent) this.parent = {}
+ // 这里的本质原理是,通过获取父组件实例(也即类似u-radio的父组件u-radio-group的this)
+ // 将父组件this中对应的参数,赋值给本组件(u-radio的this)的parentData对象中对应的属性
+ // 之所以需要这么做,是因为所有端中,头条小程序不支持通过this.parent.xxx去监听父组件参数的变化
+ // 此处并不会自动更新子组件的数据,而是依赖父组件u-radio-group去监听data的变化,手动调用更新子组件的方法去重新获取
+ this.parent = uni.$u.$parent.call(this, parentName)
+ if (this.parent.children) {
+ // 如果父组件的children不存在本组件的实例,才将本实例添加到父组件的children中
+ this.parent.children.indexOf(this) === -1 && this.parent.children.push(this)
+ }
+ if (this.parent && this.parentData) {
+ // 历遍parentData中的属性,将parent中的同名属性赋值给parentData
+ Object.keys(this.parentData).map((key) => {
+ this.parentData[key] = this.parent[key]
+ })
+ }
+ },
+ // 阻止事件冒泡
+ preventEvent(e) {
+ e && typeof (e.stopPropagation) === 'function' && e.stopPropagation()
+ },
+ // 空操作
+ noop(e) {
+ this.preventEvent(e)
+ }
+ },
+ onReachBottom() {
+ uni.$emit('uOnReachBottom')
+ },
+ beforeDestroy() {
+ // 判断当前页面是否存在parent和chldren,一般在checkbox和checkbox-group父子联动的场景会有此情况
+ // 组件销毁时,移除子组件在父组件children数组中的实例,释放资源,避免数据混乱
+ if (this.parent && uni.$u.test.array(this.parent.children)) {
+ // 组件销毁时,移除父组件中的children数组中对应的实例
+ const childrenList = this.parent.children
+ childrenList.map((child, index) => {
+ // 如果相等,则移除
+ if (child === this) {
+ childrenList.splice(index, 1)
+ }
+ })
+ }
+ }
+}
diff --git a/uni_modules/uview-ui/libs/mixin/mpMixin.js b/uni_modules/uview-ui/libs/mixin/mpMixin.js
new file mode 100644
index 0000000..29e7e65
--- /dev/null
+++ b/uni_modules/uview-ui/libs/mixin/mpMixin.js
@@ -0,0 +1,8 @@
+export default {
+ // #ifdef MP-WEIXIN
+ // 将自定义节点设置成虚拟的,更加接近Vue组件的表现,能更好的使用flex属性
+ options: {
+ virtualHost: true
+ }
+ // #endif
+}
diff --git a/uni_modules/uview-ui/libs/mixin/mpShare.js b/uni_modules/uview-ui/libs/mixin/mpShare.js
new file mode 100644
index 0000000..b07bbd3
--- /dev/null
+++ b/uni_modules/uview-ui/libs/mixin/mpShare.js
@@ -0,0 +1,13 @@
+module.exports = {
+ onLoad() {
+ // 设置默认的转发参数
+ uni.$u.mpShare = {
+ title: '', // 默认为小程序名称
+ path: '', // 默认为当前页面路径
+ imageUrl: '' // 默认为当前页面的截图
+ }
+ },
+ onShareAppMessage() {
+ return uni.$u.mpShare
+ }
+}
diff --git a/uni_modules/uview-ui/libs/mixin/openType.js b/uni_modules/uview-ui/libs/mixin/openType.js
new file mode 100644
index 0000000..1216181
--- /dev/null
+++ b/uni_modules/uview-ui/libs/mixin/openType.js
@@ -0,0 +1,25 @@
+export default {
+ props: {
+ openType: String
+ },
+ methods: {
+ onGetUserInfo(event) {
+ this.$emit('getuserinfo', event.detail)
+ },
+ onContact(event) {
+ this.$emit('contact', event.detail)
+ },
+ onGetPhoneNumber(event) {
+ this.$emit('getphonenumber', event.detail)
+ },
+ onError(event) {
+ this.$emit('error', event.detail)
+ },
+ onLaunchApp(event) {
+ this.$emit('launchapp', event.detail)
+ },
+ onOpenSetting(event) {
+ this.$emit('opensetting', event.detail)
+ }
+ }
+}
diff --git a/uni_modules/uview-ui/libs/mixin/style.js b/uni_modules/uview-ui/libs/mixin/style.js
new file mode 100644
index 0000000..2660180
--- /dev/null
+++ b/uni_modules/uview-ui/libs/mixin/style.js
@@ -0,0 +1,228 @@
+export default {
+ props: {
+ // flex排列方式
+ flexDirection: {
+ type: String,
+ default: ''
+ },
+ // flex-direction的简写
+ fd: {
+ type: String,
+ default: ''
+ },
+ // 展示类型
+ display: {
+ type: String,
+ default: ''
+ },
+ // display简写
+ d: {
+ type: String,
+ default: ''
+ },
+ // 主轴排列方式
+ justifyContent: {
+ type: String,
+ default: ''
+ },
+ // justifyContent的简写
+ jc: {
+ type: String,
+ default: ''
+ },
+ // 纵轴排列方式
+ alignItems: {
+ type: String,
+ default: ''
+ },
+ // align-items的简写
+ ai: {
+ type: String,
+ default: ''
+ },
+ color: {
+ type: String,
+ default: ''
+ },
+ // color简写
+ c: {
+ type: String,
+ default: ''
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: 0
+ },
+ // font-size简写
+ fs: {
+ type: [String, Number],
+ default: ''
+ },
+ margin: {
+ type: [String, Number],
+ default: 0
+ },
+ // margin简写
+ m: {
+ type: [String, Number],
+ default: 0
+ },
+ // margin-top
+ marginTop: {
+ type: [String, Number],
+ default: 0
+ },
+ // margin-top简写
+ mt: {
+ type: [String, Number],
+ default: 0
+ },
+ // margin-right
+ marginRight: {
+ type: [String, Number],
+ default: 0
+ },
+ // margin-right简写
+ mr: {
+ type: [String, Number],
+ default: 0
+ },
+ // margin-bottom
+ marginBottom: {
+ type: [String, Number],
+ default: 0
+ },
+ // margin-bottom简写
+ mb: {
+ type: [String, Number],
+ default: 0
+ },
+ // margin-left
+ marginLeft: {
+ type: [String, Number],
+ default: 0
+ },
+ // margin-left简写
+ ml: {
+ type: [String, Number],
+ default: 0
+ },
+ // padding-left
+ paddingLeft: {
+ type: [String, Number],
+ default: 0
+ },
+ // padding-left简写
+ pl: {
+ type: [String, Number],
+ default: 0
+ },
+ // padding-top
+ paddingTop: {
+ type: [String, Number],
+ default: 0
+ },
+ // padding-top简写
+ pt: {
+ type: [String, Number],
+ default: 0
+ },
+ // padding-right
+ paddingRight: {
+ type: [String, Number],
+ default: 0
+ },
+ // padding-right简写
+ pr: {
+ type: [String, Number],
+ default: 0
+ },
+ // padding-bottom
+ paddingBottom: {
+ type: [String, Number],
+ default: 0
+ },
+ // padding-bottom简写
+ pb: {
+ type: [String, Number],
+ default: 0
+ },
+ // border-radius
+ borderRadius: {
+ type: [String, Number],
+ default: 0
+ },
+ // border-radius简写
+ radius: {
+ type: [String, Number],
+ default: 0
+ },
+ // transform
+ transform: {
+ type: String,
+ default: ''
+ },
+ // 定位
+ position: {
+ type: String,
+ default: ''
+ },
+ // position简写
+ pos: {
+ type: String,
+ default: ''
+ },
+ // 宽度
+ width: {
+ type: [String, Number],
+ default: null
+ },
+ // width简写
+ w: {
+ type: [String, Number],
+ default: null
+ },
+ // 高度
+ height: {
+ type: [String, Number],
+ default: null
+ },
+ // height简写
+ h: {
+ type: [String, Number],
+ default: null
+ },
+ top: {
+ type: [String, Number],
+ default: 0
+ },
+ right: {
+ type: [String, Number],
+ default: 0
+ },
+ bottom: {
+ type: [String, Number],
+ default: 0
+ },
+ left: {
+ type: [String, Number],
+ default: 0
+ }
+ },
+ computed: {
+ viewStyle() {
+ const style = {}
+ const addStyle = uni.$u.addStyle(this.width || this.w) && (style.width = addStyle(this.width || this.w))(this.height || this.h) && (style.height = addStyle(this.height || this.h))(this.margin || this.m) && (style.margin = addStyle(this.margin || this.m))(this.marginTop || this.mt) && (style.marginTop = addStyle(this.marginTop || this.mt))(this.marginRight || this.mr) && (style.marginRight = addStyle(this.marginRight || this.mr))(this.marginBottom || this.mb) && (style.marginBottom = addStyle(this.marginBottom || this.mb))(this.marginLeft || this.ml) && (style.marginLeft = addStyle(this.marginLeft || this.ml))(this.padding || this.p) && (style.padding = addStyle(this.padding || this.p))(this.paddingTop || this.pt) && (style.paddingTop = addStyle(this.paddingTop || this.pt))(this.paddingRight || this.pr) && (style.paddingRight = addStyle(this.paddingRight || this.pr))(this.paddingBottom || this.pb) && (style.paddingBottom = addStyle(this.paddingBottom || this.pb))(this.paddingLeft || this.pl) && (style.paddingLeft = addStyle(this.paddingLeft || this.pl))(this.color || this.c) && (style.color = this.color || this.c)(this.fontSize || this.fs) && (style.fontSize = this.fontSize || this.fs)(this.borderRadius || this.radius) && (style.borderRadius = this.borderRadius || this.radius)(this.position || this.pos) && (this.position = this.position || this.pos)(this.flexDirection || this.fd) && (this.flexDirection = this.flexDirection || this.fd)(this.justifyContent || jc) && (this.justifyContent = this.justifyContent || jc)(this.alignItems || ai) && (this.alignItems = this.alignItems || ai)
+
+ return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle))
+ }
+ },
+ methods: {
+ // 获取margin或者padding的单位,比如padding: 0 20转为padding: 0 20px
+ getUnit(unit = '') {
+ // 取出两端空格,分隔成数组,再对数组的每个元素添加单位,最后再合并成字符串
+ return uni.$u.trim(unit).split(' ').map((item) => uni.$u.addUnit(item)).join(' ')
+ }
+ }
+}
diff --git a/uni_modules/uview-ui/libs/mixin/touch.js b/uni_modules/uview-ui/libs/mixin/touch.js
new file mode 100644
index 0000000..0ecbd88
--- /dev/null
+++ b/uni_modules/uview-ui/libs/mixin/touch.js
@@ -0,0 +1,59 @@
+const MIN_DISTANCE = 10
+
+function getDirection(x, y) {
+ if (x > y && x > MIN_DISTANCE) {
+ return 'horizontal'
+ }
+ if (y > x && y > MIN_DISTANCE) {
+ return 'vertical'
+ }
+ return ''
+}
+
+export default {
+ methods: {
+ getTouchPoint(e) {
+ if (!e) {
+ return {
+ x: 0,
+ y: 0
+ }
+ } if (e.touches && e.touches[0]) {
+ return {
+ x: e.touches[0].pageX,
+ y: e.touches[0].pageY
+ }
+ } if (e.changedTouches && e.changedTouches[0]) {
+ return {
+ x: e.changedTouches[0].pageX,
+ y: e.changedTouches[0].pageY
+ }
+ }
+ return {
+ x: e.clientX || 0,
+ y: e.clientY || 0
+ }
+ },
+ resetTouchStatus() {
+ this.direction = ''
+ this.deltaX = 0
+ this.deltaY = 0
+ this.offsetX = 0
+ this.offsetY = 0
+ },
+ touchStart(event) {
+ this.resetTouchStatus()
+ const touch = this.getTouchPoint(event)
+ this.startX = touch.x
+ this.startY = touch.y
+ },
+ touchMove(event) {
+ const touch = this.getTouchPoint(event)
+ this.deltaX = touch.x - this.startX
+ this.deltaY = touch.y - this.startY
+ this.offsetX = Math.abs(this.deltaX)
+ this.offsetY = Math.abs(this.deltaY)
+ this.direction = this.direction || getDirection(this.offsetX, this.offsetY)
+ }
+ }
+}
diff --git a/uni_modules/uview-ui/libs/util/async-validator.js b/uni_modules/uview-ui/libs/util/async-validator.js
new file mode 100644
index 0000000..9e114df
--- /dev/null
+++ b/uni_modules/uview-ui/libs/util/async-validator.js
@@ -0,0 +1,1343 @@
+function _extends() {
+ _extends = Object.assign || function (target) {
+ for (let i = 1; i < arguments.length; i++) {
+ const source = arguments[i]
+
+ for (const key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key]
+ }
+ }
+ }
+
+ return target
+ }
+
+ return _extends.apply(this, arguments)
+}
+
+/* eslint no-console:0 */
+const formatRegExp = /%[sdj%]/g
+let warning = function warning() {} // don't print warning message when in production env or node runtime
+
+if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production' && typeof window
+ !== 'undefined' && typeof document !== 'undefined') {
+ warning = function warning(type, errors) {
+ if (typeof console !== 'undefined' && console.warn) {
+ if (errors.every((e) => typeof e === 'string')) {
+ console.warn(type, errors)
+ }
+ }
+ }
+}
+
+function convertFieldsError(errors) {
+ if (!errors || !errors.length) return null
+ const fields = {}
+ errors.forEach((error) => {
+ const { field } = error
+ fields[field] = fields[field] || []
+ fields[field].push(error)
+ })
+ return fields
+}
+
+function format() {
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key]
+ }
+
+ let i = 1
+ const f = args[0]
+ const len = args.length
+
+ if (typeof f === 'function') {
+ return f.apply(null, args.slice(1))
+ }
+
+ if (typeof f === 'string') {
+ let str = String(f).replace(formatRegExp, (x) => {
+ if (x === '%%') {
+ return '%'
+ }
+
+ if (i >= len) {
+ return x
+ }
+
+ switch (x) {
+ case '%s':
+ return String(args[i++])
+
+ case '%d':
+ return Number(args[i++])
+
+ case '%j':
+ try {
+ return JSON.stringify(args[i++])
+ } catch (_) {
+ return '[Circular]'
+ }
+
+ break
+
+ default:
+ return x
+ }
+ })
+
+ for (let arg = args[i]; i < len; arg = args[++i]) {
+ str += ` ${arg}`
+ }
+
+ return str
+ }
+
+ return f
+}
+
+function isNativeStringType(type) {
+ return type === 'string' || type === 'url' || type === 'hex' || type === 'email' || type === 'pattern'
+}
+
+function isEmptyValue(value, type) {
+ if (value === undefined || value === null) {
+ return true
+ }
+
+ if (type === 'array' && Array.isArray(value) && !value.length) {
+ return true
+ }
+
+ if (isNativeStringType(type) && typeof value === 'string' && !value) {
+ return true
+ }
+
+ return false
+}
+
+function asyncParallelArray(arr, func, callback) {
+ const results = []
+ let total = 0
+ const arrLength = arr.length
+
+ function count(errors) {
+ results.push.apply(results, errors)
+ total++
+
+ if (total === arrLength) {
+ callback(results)
+ }
+ }
+
+ arr.forEach((a) => {
+ func(a, count)
+ })
+}
+
+function asyncSerialArray(arr, func, callback) {
+ let index = 0
+ const arrLength = arr.length
+
+ function next(errors) {
+ if (errors && errors.length) {
+ callback(errors)
+ return
+ }
+
+ const original = index
+ index += 1
+
+ if (original < arrLength) {
+ func(arr[original], next)
+ } else {
+ callback([])
+ }
+ }
+
+ next([])
+}
+
+function flattenObjArr(objArr) {
+ const ret = []
+ Object.keys(objArr).forEach((k) => {
+ ret.push.apply(ret, objArr[k])
+ })
+ return ret
+}
+
+function asyncMap(objArr, option, func, callback) {
+ if (option.first) {
+ const _pending = new Promise((resolve, reject) => {
+ const next = function next(errors) {
+ callback(errors)
+ return errors.length ? reject({
+ errors,
+ fields: convertFieldsError(errors)
+ }) : resolve()
+ }
+
+ const flattenArr = flattenObjArr(objArr)
+ asyncSerialArray(flattenArr, func, next)
+ })
+
+ _pending.catch((e) => e)
+
+ return _pending
+ }
+
+ let firstFields = option.firstFields || []
+
+ if (firstFields === true) {
+ firstFields = Object.keys(objArr)
+ }
+
+ const objArrKeys = Object.keys(objArr)
+ const objArrLength = objArrKeys.length
+ let total = 0
+ const results = []
+ const pending = new Promise((resolve, reject) => {
+ const next = function next(errors) {
+ results.push.apply(results, errors)
+ total++
+
+ if (total === objArrLength) {
+ callback(results)
+ return results.length ? reject({
+ errors: results,
+ fields: convertFieldsError(results)
+ }) : resolve()
+ }
+ }
+
+ if (!objArrKeys.length) {
+ callback(results)
+ resolve()
+ }
+
+ objArrKeys.forEach((key) => {
+ const arr = objArr[key]
+
+ if (firstFields.indexOf(key) !== -1) {
+ asyncSerialArray(arr, func, next)
+ } else {
+ asyncParallelArray(arr, func, next)
+ }
+ })
+ })
+ pending.catch((e) => e)
+ return pending
+}
+
+function complementError(rule) {
+ return function (oe) {
+ if (oe && oe.message) {
+ oe.field = oe.field || rule.fullField
+ return oe
+ }
+
+ return {
+ message: typeof oe === 'function' ? oe() : oe,
+ field: oe.field || rule.fullField
+ }
+ }
+}
+
+function deepMerge(target, source) {
+ if (source) {
+ for (const s in source) {
+ if (source.hasOwnProperty(s)) {
+ const value = source[s]
+
+ if (typeof value === 'object' && typeof target[s] === 'object') {
+ target[s] = { ...target[s], ...value }
+ } else {
+ target[s] = value
+ }
+ }
+ }
+ }
+
+ return target
+}
+
+/**
+ * Rule for validating required fields.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param source The source object being validated.
+ * @param errors An array of errors that this rule may add
+ * validation errors to.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function required(rule, value, source, errors, options, type) {
+ if (rule.required && (!source.hasOwnProperty(rule.field) || isEmptyValue(value, type || rule.type))) {
+ errors.push(format(options.messages.required, rule.fullField))
+ }
+}
+
+/**
+ * Rule for validating whitespace.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param source The source object being validated.
+ * @param errors An array of errors that this rule may add
+ * validation errors to.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function whitespace(rule, value, source, errors, options) {
+ if (/^\s+$/.test(value) || value === '') {
+ errors.push(format(options.messages.whitespace, rule.fullField))
+ }
+}
+
+/* eslint max-len:0 */
+
+const pattern = {
+ // http://emailregex.com/
+ email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
+ 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'
+ ),
+ hex: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i
+}
+var types = {
+ integer: function integer(value) {
+ return /^(-)?\d+$/.test(value);
+ },
+ float: function float(value) {
+ return /^(-)?\d+(\.\d+)?$/.test(value);
+ },
+ array: function array(value) {
+ return Array.isArray(value)
+ },
+ regexp: function regexp(value) {
+ if (value instanceof RegExp) {
+ return true
+ }
+
+ try {
+ return !!new RegExp(value)
+ } catch (e) {
+ return false
+ }
+ },
+ date: function date(value) {
+ return typeof value.getTime === 'function' && typeof value.getMonth === 'function' && typeof value.getYear
+ === 'function'
+ },
+ number: function number(value) {
+ if (isNaN(value)) {
+ return false
+ }
+
+ // 修改源码,将字符串数值先转为数值
+ return typeof +value === 'number'
+ },
+ object: function object(value) {
+ return typeof value === 'object' && !types.array(value)
+ },
+ method: function method(value) {
+ return typeof value === 'function'
+ },
+ email: function email(value) {
+ return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255
+ },
+ url: function url(value) {
+ return typeof value === 'string' && !!value.match(pattern.url)
+ },
+ hex: function hex(value) {
+ return typeof value === 'string' && !!value.match(pattern.hex)
+ }
+}
+/**
+ * Rule for validating the type of a value.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param source The source object being validated.
+ * @param errors An array of errors that this rule may add
+ * validation errors to.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function type(rule, value, source, errors, options) {
+ if (rule.required && value === undefined) {
+ required(rule, value, source, errors, options)
+ return
+ }
+
+ const custom = ['integer', 'float', 'array', 'regexp', 'object', 'method', 'email', 'number', 'date', 'url', 'hex']
+ const ruleType = rule.type
+
+ if (custom.indexOf(ruleType) > -1) {
+ if (!types[ruleType](value)) {
+ errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type))
+ } // straight typeof check
+ } else if (ruleType && typeof value !== rule.type) {
+ errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type))
+ }
+}
+
+/**
+ * Rule for validating minimum and maximum allowed values.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param source The source object being validated.
+ * @param errors An array of errors that this rule may add
+ * validation errors to.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function range(rule, value, source, errors, options) {
+ const len = typeof rule.len === 'number'
+ const min = typeof rule.min === 'number'
+ const max = typeof rule.max === 'number' // 正则匹配码点范围从U+010000一直到U+10FFFF的文字(补充平面Supplementary Plane)
+
+ const spRegexp = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g
+ let val = value
+ let key = null
+ const num = typeof value === 'number'
+ const str = typeof value === 'string'
+ const arr = Array.isArray(value)
+
+ if (num) {
+ key = 'number'
+ } else if (str) {
+ key = 'string'
+ } else if (arr) {
+ key = 'array'
+ } // if the value is not of a supported type for range validation
+ // the validation rule rule should use the
+ // type property to also test for a particular type
+
+ if (!key) {
+ return false
+ }
+
+ if (arr) {
+ val = value.length
+ }
+
+ if (str) {
+ // 处理码点大于U+010000的文字length属性不准确的bug,如"𠮷𠮷𠮷".lenght !== 3
+ val = value.replace(spRegexp, '_').length
+ }
+
+ if (len) {
+ if (val !== rule.len) {
+ errors.push(format(options.messages[key].len, rule.fullField, rule.len))
+ }
+ } else if (min && !max && val < rule.min) {
+ errors.push(format(options.messages[key].min, rule.fullField, rule.min))
+ } else if (max && !min && val > rule.max) {
+ errors.push(format(options.messages[key].max, rule.fullField, rule.max))
+ } else if (min && max && (val < rule.min || val > rule.max)) {
+ errors.push(format(options.messages[key].range, rule.fullField, rule.min, rule.max))
+ }
+}
+
+const ENUM = 'enum'
+/**
+ * Rule for validating a value exists in an enumerable list.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param source The source object being validated.
+ * @param errors An array of errors that this rule may add
+ * validation errors to.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function enumerable(rule, value, source, errors, options) {
+ rule[ENUM] = Array.isArray(rule[ENUM]) ? rule[ENUM] : []
+
+ if (rule[ENUM].indexOf(value) === -1) {
+ errors.push(format(options.messages[ENUM], rule.fullField, rule[ENUM].join(', ')))
+ }
+}
+
+/**
+ * Rule for validating a regular expression pattern.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param source The source object being validated.
+ * @param errors An array of errors that this rule may add
+ * validation errors to.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function pattern$1(rule, value, source, errors, options) {
+ if (rule.pattern) {
+ if (rule.pattern instanceof RegExp) {
+ // if a RegExp instance is passed, reset `lastIndex` in case its `global`
+ // flag is accidentally set to `true`, which in a validation scenario
+ // is not necessary and the result might be misleading
+ rule.pattern.lastIndex = 0
+
+ if (!rule.pattern.test(value)) {
+ errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern))
+ }
+ } else if (typeof rule.pattern === 'string') {
+ const _pattern = new RegExp(rule.pattern)
+
+ if (!_pattern.test(value)) {
+ errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern))
+ }
+ }
+ }
+}
+
+const rules = {
+ required,
+ whitespace,
+ type,
+ range,
+ enum: enumerable,
+ pattern: pattern$1
+}
+
+/**
+ * Performs validation for string types.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function string(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value, 'string') && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options, 'string')
+
+ if (!isEmptyValue(value, 'string')) {
+ rules.type(rule, value, source, errors, options)
+ rules.range(rule, value, source, errors, options)
+ rules.pattern(rule, value, source, errors, options)
+
+ if (rule.whitespace === true) {
+ rules.whitespace(rule, value, source, errors, options)
+ }
+ }
+ }
+
+ callback(errors)
+}
+
+/**
+ * Validates a function.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function method(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+
+ if (value !== undefined) {
+ rules.type(rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+/**
+ * Validates a number.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function number(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (value === '') {
+ value = undefined
+ }
+
+ if (isEmptyValue(value) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+
+ if (value !== undefined) {
+ rules.type(rule, value, source, errors, options)
+ rules.range(rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+/**
+ * Validates a boolean.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function _boolean(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+
+ if (value !== undefined) {
+ rules.type(rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+/**
+ * Validates the regular expression type.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function regexp(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+
+ if (!isEmptyValue(value)) {
+ rules.type(rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+/**
+ * Validates a number is an integer.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function integer(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+
+ if (value !== undefined) {
+ rules.type(rule, value, source, errors, options)
+ rules.range(rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+/**
+ * Validates a number is a floating point number.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function floatFn(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+
+ if (value !== undefined) {
+ rules.type(rule, value, source, errors, options)
+ rules.range(rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+/**
+ * Validates an array.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function array(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value, 'array') && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options, 'array')
+
+ if (!isEmptyValue(value, 'array')) {
+ rules.type(rule, value, source, errors, options)
+ rules.range(rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+/**
+ * Validates an object.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function object(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+
+ if (value !== undefined) {
+ rules.type(rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+const ENUM$1 = 'enum'
+/**
+ * Validates an enumerable list.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function enumerable$1(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+
+ if (value !== undefined) {
+ rules[ENUM$1](rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+/**
+ * Validates a regular expression pattern.
+ *
+ * Performs validation when a rule only contains
+ * a pattern property but is not declared as a string type.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function pattern$2(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value, 'string') && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+
+ if (!isEmptyValue(value, 'string')) {
+ rules.pattern(rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+function date(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+
+ if (!isEmptyValue(value)) {
+ let dateObject
+
+ if (typeof value === 'number') {
+ dateObject = new Date(value)
+ } else {
+ dateObject = value
+ }
+
+ rules.type(rule, dateObject, source, errors, options)
+
+ if (dateObject) {
+ rules.range(rule, dateObject.getTime(), source, errors, options)
+ }
+ }
+ }
+
+ callback(errors)
+}
+
+function required$1(rule, value, callback, source, options) {
+ const errors = []
+ const type = Array.isArray(value) ? 'array' : typeof value
+ rules.required(rule, value, source, errors, options, type)
+ callback(errors)
+}
+
+function type$1(rule, value, callback, source, options) {
+ const ruleType = rule.type
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value, ruleType) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options, ruleType)
+
+ if (!isEmptyValue(value, ruleType)) {
+ rules.type(rule, value, source, errors, options)
+ }
+ }
+
+ callback(errors)
+}
+
+/**
+ * Performs validation for any type.
+ *
+ * @param rule The validation rule.
+ * @param value The value of the field on the source object.
+ * @param callback The callback function.
+ * @param source The source object being validated.
+ * @param options The validation options.
+ * @param options.messages The validation messages.
+ */
+
+function any(rule, value, callback, source, options) {
+ const errors = []
+ const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
+
+ if (validate) {
+ if (isEmptyValue(value) && !rule.required) {
+ return callback()
+ }
+
+ rules.required(rule, value, source, errors, options)
+ }
+
+ callback(errors)
+}
+
+const validators = {
+ string,
+ method,
+ number,
+ boolean: _boolean,
+ regexp,
+ integer,
+ float: floatFn,
+ array,
+ object,
+ enum: enumerable$1,
+ pattern: pattern$2,
+ date,
+ url: type$1,
+ hex: type$1,
+ email: type$1,
+ required: required$1,
+ any
+}
+
+function newMessages() {
+ return {
+ default: 'Validation error on field %s',
+ required: '%s is required',
+ enum: '%s must be one of %s',
+ whitespace: '%s cannot be empty',
+ date: {
+ format: '%s date %s is invalid for format %s',
+ parse: '%s date could not be parsed, %s is invalid ',
+ invalid: '%s date %s is invalid'
+ },
+ types: {
+ string: '%s is not a %s',
+ method: '%s is not a %s (function)',
+ array: '%s is not an %s',
+ object: '%s is not an %s',
+ number: '%s is not a %s',
+ date: '%s is not a %s',
+ boolean: '%s is not a %s',
+ integer: '%s is not an %s',
+ float: '%s is not a %s',
+ regexp: '%s is not a valid %s',
+ email: '%s is not a valid %s',
+ url: '%s is not a valid %s',
+ hex: '%s is not a valid %s'
+ },
+ string: {
+ len: '%s must be exactly %s characters',
+ min: '%s must be at least %s characters',
+ max: '%s cannot be longer than %s characters',
+ range: '%s must be between %s and %s characters'
+ },
+ number: {
+ len: '%s must equal %s',
+ min: '%s cannot be less than %s',
+ max: '%s cannot be greater than %s',
+ range: '%s must be between %s and %s'
+ },
+ array: {
+ len: '%s must be exactly %s in length',
+ min: '%s cannot be less than %s in length',
+ max: '%s cannot be greater than %s in length',
+ range: '%s must be between %s and %s in length'
+ },
+ pattern: {
+ mismatch: '%s value %s does not match pattern %s'
+ },
+ clone: function clone() {
+ const cloned = JSON.parse(JSON.stringify(this))
+ cloned.clone = this.clone
+ return cloned
+ }
+ }
+}
+const messages = newMessages()
+
+/**
+ * Encapsulates a validation schema.
+ *
+ * @param descriptor An object declaring validation rules
+ * for this schema.
+ */
+
+function Schema(descriptor) {
+ this.rules = null
+ this._messages = messages
+ this.define(descriptor)
+}
+
+Schema.prototype = {
+ messages: function messages(_messages) {
+ if (_messages) {
+ this._messages = deepMerge(newMessages(), _messages)
+ }
+
+ return this._messages
+ },
+ define: function define(rules) {
+ if (!rules) {
+ throw new Error('Cannot configure a schema with no rules')
+ }
+
+ if (typeof rules !== 'object' || Array.isArray(rules)) {
+ throw new Error('Rules must be an object')
+ }
+
+ this.rules = {}
+ let z
+ let item
+
+ for (z in rules) {
+ if (rules.hasOwnProperty(z)) {
+ item = rules[z]
+ this.rules[z] = Array.isArray(item) ? item : [item]
+ }
+ }
+ },
+ validate: function validate(source_, o, oc) {
+ const _this = this
+
+ if (o === void 0) {
+ o = {}
+ }
+
+ if (oc === void 0) {
+ oc = function oc() {}
+ }
+
+ let source = source_
+ let options = o
+ let callback = oc
+
+ if (typeof options === 'function') {
+ callback = options
+ options = {}
+ }
+
+ if (!this.rules || Object.keys(this.rules).length === 0) {
+ if (callback) {
+ callback()
+ }
+
+ return Promise.resolve()
+ }
+
+ function complete(results) {
+ let i
+ let errors = []
+ let fields = {}
+
+ function add(e) {
+ if (Array.isArray(e)) {
+ let _errors
+
+ errors = (_errors = errors).concat.apply(_errors, e)
+ } else {
+ errors.push(e)
+ }
+ }
+
+ for (i = 0; i < results.length; i++) {
+ add(results[i])
+ }
+
+ if (!errors.length) {
+ errors = null
+ fields = null
+ } else {
+ fields = convertFieldsError(errors)
+ }
+
+ callback(errors, fields)
+ }
+
+ if (options.messages) {
+ let messages$1 = this.messages()
+
+ if (messages$1 === messages) {
+ messages$1 = newMessages()
+ }
+
+ deepMerge(messages$1, options.messages)
+ options.messages = messages$1
+ } else {
+ options.messages = this.messages()
+ }
+
+ let arr
+ let value
+ const series = {}
+ const keys = options.keys || Object.keys(this.rules)
+ keys.forEach((z) => {
+ arr = _this.rules[z]
+ value = source[z]
+ arr.forEach((r) => {
+ let rule = r
+
+ if (typeof rule.transform === 'function') {
+ if (source === source_) {
+ source = { ...source }
+ }
+
+ value = source[z] = rule.transform(value)
+ }
+
+ if (typeof rule === 'function') {
+ rule = {
+ validator: rule
+ }
+ } else {
+ rule = { ...rule }
+ }
+
+ rule.validator = _this.getValidationMethod(rule)
+ rule.field = z
+ rule.fullField = rule.fullField || z
+ rule.type = _this.getType(rule)
+
+ if (!rule.validator) {
+ return
+ }
+
+ series[z] = series[z] || []
+ series[z].push({
+ rule,
+ value,
+ source,
+ field: z
+ })
+ })
+ })
+ const errorFields = {}
+ return asyncMap(series, options, (data, doIt) => {
+ const { rule } = data
+ let deep = (rule.type === 'object' || rule.type === 'array') && (typeof rule.fields === 'object' || typeof rule.defaultField
+ === 'object')
+ deep = deep && (rule.required || !rule.required && data.value)
+ rule.field = data.field
+
+ function addFullfield(key, schema) {
+ return { ...schema, fullField: `${rule.fullField}.${key}` }
+ }
+
+ function cb(e) {
+ if (e === void 0) {
+ e = []
+ }
+
+ let errors = e
+
+ if (!Array.isArray(errors)) {
+ errors = [errors]
+ }
+
+ if (!options.suppressWarning && errors.length) {
+ Schema.warning('async-validator:', errors)
+ }
+
+ if (errors.length && rule.message) {
+ errors = [].concat(rule.message)
+ }
+
+ errors = errors.map(complementError(rule))
+
+ if (options.first && errors.length) {
+ errorFields[rule.field] = 1
+ return doIt(errors)
+ }
+
+ if (!deep) {
+ doIt(errors)
+ } else {
+ // if rule is required but the target object
+ // does not exist fail at the rule level and don't
+ // go deeper
+ if (rule.required && !data.value) {
+ if (rule.message) {
+ errors = [].concat(rule.message).map(complementError(rule))
+ } else if (options.error) {
+ errors = [options.error(rule, format(options.messages.required, rule.field))]
+ } else {
+ errors = []
+ }
+
+ return doIt(errors)
+ }
+
+ let fieldsSchema = {}
+
+ if (rule.defaultField) {
+ for (const k in data.value) {
+ if (data.value.hasOwnProperty(k)) {
+ fieldsSchema[k] = rule.defaultField
+ }
+ }
+ }
+
+ fieldsSchema = { ...fieldsSchema, ...data.rule.fields }
+
+ for (const f in fieldsSchema) {
+ if (fieldsSchema.hasOwnProperty(f)) {
+ const fieldSchema = Array.isArray(fieldsSchema[f]) ? fieldsSchema[f] : [fieldsSchema[f]]
+ fieldsSchema[f] = fieldSchema.map(addFullfield.bind(null, f))
+ }
+ }
+
+ const schema = new Schema(fieldsSchema)
+ schema.messages(options.messages)
+
+ if (data.rule.options) {
+ data.rule.options.messages = options.messages
+ data.rule.options.error = options.error
+ }
+
+ schema.validate(data.value, data.rule.options || options, (errs) => {
+ const finalErrors = []
+
+ if (errors && errors.length) {
+ finalErrors.push.apply(finalErrors, errors)
+ }
+
+ if (errs && errs.length) {
+ finalErrors.push.apply(finalErrors, errs)
+ }
+
+ doIt(finalErrors.length ? finalErrors : null)
+ })
+ }
+ }
+
+ let res
+
+ if (rule.asyncValidator) {
+ res = rule.asyncValidator(rule, data.value, cb, data.source, options)
+ } else if (rule.validator) {
+ res = rule.validator(rule, data.value, cb, data.source, options)
+
+ if (res === true) {
+ cb()
+ } else if (res === false) {
+ cb(rule.message || `${rule.field} fails`)
+ } else if (res instanceof Array) {
+ cb(res)
+ } else if (res instanceof Error) {
+ cb(res.message)
+ }
+ }
+
+ if (res && res.then) {
+ res.then(() => cb(), (e) => cb(e))
+ }
+ }, (results) => {
+ complete(results)
+ })
+ },
+ getType: function getType(rule) {
+ if (rule.type === undefined && rule.pattern instanceof RegExp) {
+ rule.type = 'pattern'
+ }
+
+ if (typeof rule.validator !== 'function' && rule.type && !validators.hasOwnProperty(rule.type)) {
+ throw new Error(format('Unknown rule type %s', rule.type))
+ }
+
+ return rule.type || 'string'
+ },
+ getValidationMethod: function getValidationMethod(rule) {
+ if (typeof rule.validator === 'function') {
+ return rule.validator
+ }
+
+ const keys = Object.keys(rule)
+ const messageIndex = keys.indexOf('message')
+
+ if (messageIndex !== -1) {
+ keys.splice(messageIndex, 1)
+ }
+
+ if (keys.length === 1 && keys[0] === 'required') {
+ return validators.required
+ }
+
+ return validators[this.getType(rule)] || false
+ }
+}
+
+Schema.register = function register(type, validator) {
+ if (typeof validator !== 'function') {
+ throw new Error('Cannot register a validator by type, validator is not a function')
+ }
+
+ validators[type] = validator
+}
+
+Schema.warning = warning
+Schema.messages = messages
+
+export default Schema
+// # sourceMappingURL=index.js.map
diff --git a/uni_modules/uview-ui/libs/util/calendar.js b/uni_modules/uview-ui/libs/util/calendar.js
new file mode 100644
index 0000000..e006dea
--- /dev/null
+++ b/uni_modules/uview-ui/libs/util/calendar.js
@@ -0,0 +1,546 @@
+/**
+* @1900-2100区间内的公历、农历互转
+* @charset UTF-8
+* @github https://github.com/jjonline/calendar.js
+* @Author Jea杨(JJonline@JJonline.Cn)
+* @Time 2014-7-21
+* @Time 2016-8-13 Fixed 2033hex、Attribution Annals
+* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
+* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
+* @Version 1.0.3
+* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+*/
+/* eslint-disable */
+var calendar = {
+
+ /**
+ * 农历1900-2100的润大小信息表
+ * @Array Of Property
+ * @return Hex
+ */
+ lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
+ 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
+ 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
+ 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
+ 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
+ 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
+ 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
+ 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
+ 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
+ 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
+ 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
+ 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
+ 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
+ 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
+ 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
+ /** Add By JJonline@JJonline.Cn**/
+ 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
+ 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
+ 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
+ 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
+ 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
+ 0x0d520], // 2100
+
+ /**
+ * 公历每个月份的天数普通表
+ * @Array Of Property
+ * @return Number
+ */
+ solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+ /**
+ * 天干地支之天干速查表
+ * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+ * @return Cn string
+ */
+ Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
+
+ /**
+ * 天干地支之地支速查表
+ * @Array Of Property
+ * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+ * @return Cn string
+ */
+ Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
+
+ /**
+ * 天干地支之地支速查表<=>生肖
+ * @Array Of Property
+ * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+ * @return Cn string
+ */
+ Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
+
+ /**
+ * 24节气速查表
+ * @Array Of Property
+ * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+ * @return Cn string
+ */
+ solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
+
+ /**
+ * 1900-2100各年的24节气日期速查表
+ * @Array Of Property
+ * @return 0x string For splice
+ */
+ sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
+ '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
+ 'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
+ '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
+ '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
+ '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
+ '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
+ '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
+ '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+ '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+ '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
+ '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+ '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+ '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+ '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+ '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
+ '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
+ '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+ '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+ '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
+ '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
+ '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
+ '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
+ '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+ '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
+ '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
+ '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+ '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
+ '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
+ '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
+ '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
+ '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
+
+ /**
+ * 数字转中文速查表
+ * @Array Of Property
+ * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+ * @return Cn string
+ */
+ nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
+
+ /**
+ * 日期转农历称呼速查表
+ * @Array Of Property
+ * @trans ['初','十','廿','卅']
+ * @return Cn string
+ */
+ nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+ /**
+ * 月份转农历称呼速查表
+ * @Array Of Property
+ * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+ * @return Cn string
+ */
+ nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
+
+ /**
+ * 返回农历y年一整年的总天数
+ * @param lunar Year
+ * @return Number
+ * @eg:var count = calendar.lYearDays(1987) ;//count=387
+ */
+ lYearDays: function (y) {
+ var i; var sum = 348
+ for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
+ return (sum + this.leapDays(y))
+ },
+
+ /**
+ * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+ * @param lunar Year
+ * @return Number (0-12)
+ * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+ */
+ leapMonth: function (y) { // 闰字编码 \u95f0
+ return (this.lunarInfo[y - 1900] & 0xf)
+ },
+
+ /**
+ * 返回农历y年闰月的天数 若该年没有闰月则返回0
+ * @param lunar Year
+ * @return Number (0、29、30)
+ * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+ */
+ leapDays: function (y) {
+ if (this.leapMonth(y)) {
+ return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
+ }
+ return (0)
+ },
+
+ /**
+ * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+ * @param lunar Year
+ * @return Number (-1、29、30)
+ * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+ */
+ monthDays: function (y, m) {
+ if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
+ return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
+ },
+
+ /**
+ * 返回公历(!)y年m月的天数
+ * @param solar Year
+ * @return Number (-1、28、29、30、31)
+ * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+ */
+ solarDays: function (y, m) {
+ if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+ var ms = m - 1
+ if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
+ return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
+ } else {
+ return (this.solarMonth[ms])
+ }
+ },
+
+ /**
+ * 农历年份转换为干支纪年
+ * @param lYear 农历年的年份数
+ * @return Cn string
+ */
+ toGanZhiYear: function (lYear) {
+ var ganKey = (lYear - 3) % 10
+ var zhiKey = (lYear - 3) % 12
+ if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
+ if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
+ return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
+ },
+
+ /**
+ * 公历月、日判断所属星座
+ * @param cMonth [description]
+ * @param cDay [description]
+ * @return Cn string
+ */
+ toAstro: function (cMonth, cDay) {
+ var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+ var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+ return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
+ },
+
+ /**
+ * 传入offset偏移量返回干支
+ * @param offset 相对甲子的偏移量
+ * @return Cn string
+ */
+ toGanZhi: function (offset) {
+ return this.Gan[offset % 10] + this.Zhi[offset % 12]
+ },
+
+ /**
+ * 传入公历(!)y年获得该年第n个节气的公历日期
+ * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+ * @return day Number
+ * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+ */
+ getTerm: function (y, n) {
+ if (y < 1900 || y > 2100) { return -1 }
+ if (n < 1 || n > 24) { return -1 }
+ var _table = this.sTermInfo[y - 1900]
+ var _info = [
+ parseInt('0x' + _table.substr(0, 5)).toString(),
+ parseInt('0x' + _table.substr(5, 5)).toString(),
+ parseInt('0x' + _table.substr(10, 5)).toString(),
+ parseInt('0x' + _table.substr(15, 5)).toString(),
+ parseInt('0x' + _table.substr(20, 5)).toString(),
+ parseInt('0x' + _table.substr(25, 5)).toString()
+ ]
+ var _calday = [
+ _info[0].substr(0, 1),
+ _info[0].substr(1, 2),
+ _info[0].substr(3, 1),
+ _info[0].substr(4, 2),
+
+ _info[1].substr(0, 1),
+ _info[1].substr(1, 2),
+ _info[1].substr(3, 1),
+ _info[1].substr(4, 2),
+
+ _info[2].substr(0, 1),
+ _info[2].substr(1, 2),
+ _info[2].substr(3, 1),
+ _info[2].substr(4, 2),
+
+ _info[3].substr(0, 1),
+ _info[3].substr(1, 2),
+ _info[3].substr(3, 1),
+ _info[3].substr(4, 2),
+
+ _info[4].substr(0, 1),
+ _info[4].substr(1, 2),
+ _info[4].substr(3, 1),
+ _info[4].substr(4, 2),
+
+ _info[5].substr(0, 1),
+ _info[5].substr(1, 2),
+ _info[5].substr(3, 1),
+ _info[5].substr(4, 2)
+ ]
+ return parseInt(_calday[n - 1])
+ },
+
+ /**
+ * 传入农历数字月份返回汉语通俗表示法
+ * @param lunar month
+ * @return Cn string
+ * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+ */
+ toChinaMonth: function (m) { // 月 => \u6708
+ if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+ var s = this.nStr3[m - 1]
+ s += '\u6708'// 加上月字
+ return s
+ },
+
+ /**
+ * 传入农历日期数字返回汉字表示法
+ * @param lunar day
+ * @return Cn string
+ * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+ */
+ toChinaDay: function (d) { // 日 => \u65e5
+ var s
+ switch (d) {
+ case 10:
+ s = '\u521d\u5341'; break
+ case 20:
+ s = '\u4e8c\u5341'; break
+ break
+ case 30:
+ s = '\u4e09\u5341'; break
+ break
+ default:
+ s = this.nStr2[Math.floor(d / 10)]
+ s += this.nStr1[d % 10]
+ }
+ return (s)
+ },
+
+ /**
+ * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+ * @param y year
+ * @return Cn string
+ * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+ */
+ getAnimal: function (y) {
+ return this.Animals[(y - 4) % 12]
+ },
+
+ /**
+ * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+ * @param y solar year
+ * @param m solar month
+ * @param d solar day
+ * @return JSON object
+ * @eg:console.log(calendar.solar2lunar(1987,11,01));
+ */
+ solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
+ // 年份限定、上限
+ if (y < 1900 || y > 2100) {
+ return -1// undefined转换为数字变为NaN
+ }
+ // 公历传参最下限
+ if (y == 1900 && m == 1 && d < 31) {
+ return -1
+ }
+ // 未传参 获得当天
+ if (!y) {
+ var objDate = new Date()
+ } else {
+ var objDate = new Date(y, parseInt(m) - 1, d)
+ }
+ var i; var leap = 0; var temp = 0
+ // 修正ymd参数
+ var y = objDate.getFullYear()
+ var m = objDate.getMonth() + 1
+ var d = objDate.getDate()
+ var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
+ for (i = 1900; i < 2101 && offset > 0; i++) {
+ temp = this.lYearDays(i)
+ offset -= temp
+ }
+ if (offset < 0) {
+ offset += temp; i--
+ }
+
+ // 是否今天
+ var isTodayObj = new Date()
+ var isToday = false
+ if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
+ isToday = true
+ }
+ // 星期几
+ var nWeek = objDate.getDay()
+ var cWeek = this.nStr1[nWeek]
+ // 数字表示周几顺应天朝周一开始的惯例
+ if (nWeek == 0) {
+ nWeek = 7
+ }
+ // 农历年
+ var year = i
+ var leap = this.leapMonth(i) // 闰哪个月
+ var isLeap = false
+
+ // 效验闰月
+ for (i = 1; i < 13 && offset > 0; i++) {
+ // 闰月
+ if (leap > 0 && i == (leap + 1) && isLeap == false) {
+ --i
+ isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
+ } else {
+ temp = this.monthDays(year, i)// 计算农历普通月天数
+ }
+ // 解除闰月
+ if (isLeap == true && i == (leap + 1)) { isLeap = false }
+ offset -= temp
+ }
+ // 闰月导致数组下标重叠取反
+ if (offset == 0 && leap > 0 && i == leap + 1) {
+ if (isLeap) {
+ isLeap = false
+ } else {
+ isLeap = true; --i
+ }
+ }
+ if (offset < 0) {
+ offset += temp; --i
+ }
+ // 农历月
+ var month = i
+ // 农历日
+ var day = offset + 1
+ // 天干地支处理
+ var sm = m - 1
+ var gzY = this.toGanZhiYear(year)
+
+ // 当月的两个节气
+ // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+ var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
+ var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
+
+ // 依据12节气修正干支月
+ var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
+ if (d >= firstNode) {
+ gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
+ }
+
+ // 传入的日期的节气与否
+ var isTerm = false
+ var Term = null
+ if (firstNode == d) {
+ isTerm = true
+ Term = this.solarTerm[m * 2 - 2]
+ }
+ if (secondNode == d) {
+ isTerm = true
+ Term = this.solarTerm[m * 2 - 1]
+ }
+ // 日柱 当月一日与 1900/1/1 相差天数
+ var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+ var gzD = this.toGanZhi(dayCyclical + d - 1)
+ // 该日期所属的星座
+ var astro = this.toAstro(m, d)
+
+ return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
+ },
+
+ /**
+ * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+ * @param y lunar year
+ * @param m lunar month
+ * @param d lunar day
+ * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+ * @return JSON object
+ * @eg:console.log(calendar.lunar2solar(1987,9,10));
+ */
+ lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
+ var isLeapMonth = !!isLeapMonth
+ var leapOffset = 0
+ var leapMonth = this.leapMonth(y)
+ var leapDay = this.leapDays(y)
+ if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+ if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
+ var day = this.monthDays(y, m)
+ var _day = day
+ // bugFix 2016-9-25
+ // if month is leap, _day use leapDays method
+ if (isLeapMonth) {
+ _day = this.leapDays(y, m)
+ }
+ if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
+
+ // 计算农历的时间差
+ var offset = 0
+ for (var i = 1900; i < y; i++) {
+ offset += this.lYearDays(i)
+ }
+ var leap = 0; var isAdd = false
+ for (var i = 1; i < m; i++) {
+ leap = this.leapMonth(y)
+ if (!isAdd) { // 处理闰月
+ if (leap <= i && leap > 0) {
+ offset += this.leapDays(y); isAdd = true
+ }
+ }
+ offset += this.monthDays(y, i)
+ }
+ // 转换闰月农历 需补充该年闰月的前一个月的时差
+ if (isLeapMonth) { offset += day }
+ // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+ var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+ var calObj = new Date((offset + d - 31) * 86400000 + stmap)
+ var cY = calObj.getUTCFullYear()
+ var cM = calObj.getUTCMonth() + 1
+ var cD = calObj.getUTCDate()
+
+ return this.solar2lunar(cY, cM, cD)
+ }
+}
+
+export default calendar
diff --git a/uni_modules/uview-ui/libs/util/dayjs.js b/uni_modules/uview-ui/libs/util/dayjs.js
new file mode 100644
index 0000000..c4efea0
--- /dev/null
+++ b/uni_modules/uview-ui/libs/util/dayjs.js
@@ -0,0 +1,308 @@
+!(function (t, e) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = e() : typeof define === 'function'
+ && define.amd ? define(e) : t.dayjs = e()
+}(this, () => {
+ 'use strict'
+
+ const t = 'millisecond'
+ const e = 'second'
+ const n = 'minute'
+ const r = 'hour'
+ const i = 'day'
+ const s = 'week'
+ const u = 'month'
+ const a = 'quarter'
+ const o = 'year'
+ const f = 'date'
+ const h = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d+)?$/
+ const c = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g
+ const d = {
+ name: 'en',
+ weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
+ months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_')
+ }
+ const $ = function (t, e, n) {
+ const r = String(t)
+ return !r || r.length >= e ? t : `${Array(e + 1 - r.length).join(n)}${t}`
+ }
+ const l = {
+ s: $,
+ z(t) {
+ const e = -t.utcOffset()
+ const n = Math.abs(e)
+ const r = Math.floor(n / 60)
+ const i = n % 60
+ return `${(e <= 0 ? '+' : '-') + $(r, 2, '0')}:${$(i, 2, '0')}`
+ },
+ m: function t(e, n) {
+ if (e.date() < n.date()) return -t(n, e)
+ const r = 12 * (n.year() - e.year()) + (n.month() - e.month())
+ const i = e.clone().add(r, u)
+ const s = n - i < 0
+ const a = e.clone().add(r + (s ? -1 : 1), u)
+ return +(-(r + (n - i) / (s ? i - a : a - i)) || 0)
+ },
+ a(t) {
+ return t < 0 ? Math.ceil(t) || 0 : Math.floor(t)
+ },
+ p(h) {
+ return {
+ M: u,
+ y: o,
+ w: s,
+ d: i,
+ D: f,
+ h: r,
+ m: n,
+ s: e,
+ ms: t,
+ Q: a
+ }[h] || String(h || '').toLowerCase().replace(/s$/, '')
+ },
+ u(t) {
+ return void 0 === t
+ }
+ }
+ let y = 'en'
+ const M = {}
+ M[y] = d
+ const m = function (t) {
+ return t instanceof S
+ }
+ const D = function (t, e, n) {
+ let r
+ if (!t) return y
+ if (typeof t === 'string') M[t] && (r = t), e && (M[t] = e, r = t)
+ else {
+ const i = t.name
+ M[i] = t, r = i
+ }
+ return !n && r && (y = r), r || !n && y
+ }
+ const v = function (t, e) {
+ if (m(t)) return t.clone()
+ const n = typeof e === 'object' ? e : {}
+ return n.date = t, n.args = arguments, new S(n)
+ }
+ const g = l
+ g.l = D, g.i = m, g.w = function (t, e) {
+ return v(t, {
+ locale: e.$L,
+ utc: e.$u,
+ x: e.$x,
+ $offset: e.$offset
+ })
+ }
+ var S = (function () {
+ function d(t) {
+ this.$L = D(t.locale, null, !0), this.parse(t)
+ }
+ const $ = d.prototype
+ return $.parse = function (t) {
+ this.$d = (function (t) {
+ const e = t.date
+ const n = t.utc
+ if (e === null) return new Date(NaN)
+ if (g.u(e)) return new Date()
+ if (e instanceof Date) return new Date(e)
+ if (typeof e === 'string' && !/Z$/i.test(e)) {
+ const r = e.match(h)
+ if (r) {
+ const i = r[2] - 1 || 0
+ const s = (r[7] || '0').substring(0, 3)
+ return n ? new Date(Date.UTC(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s)) : new Date(r[1], i, r[3]
+ || 1, r[4] || 0, r[5] || 0, r[6] || 0, s)
+ }
+ }
+ return new Date(e)
+ }(t)), this.$x = t.x || {}, this.init()
+ }, $.init = function () {
+ const t = this.$d
+ this.$y = t.getFullYear(), this.$M = t.getMonth(), this.$D = t.getDate(), this.$W = t.getDay(), this.$H = t.getHours(),
+ this.$m = t.getMinutes(), this.$s = t.getSeconds(), this.$ms = t.getMilliseconds()
+ }, $.$utils = function () {
+ return g
+ }, $.isValid = function () {
+ return !(this.$d.toString() === 'Invalid Date')
+ }, $.isSame = function (t, e) {
+ const n = v(t)
+ return this.startOf(e) <= n && n <= this.endOf(e)
+ }, $.isAfter = function (t, e) {
+ return v(t) < this.startOf(e)
+ }, $.isBefore = function (t, e) {
+ return this.endOf(e) < v(t)
+ }, $.$g = function (t, e, n) {
+ return g.u(t) ? this[e] : this.set(n, t)
+ }, $.unix = function () {
+ return Math.floor(this.valueOf() / 1e3)
+ }, $.valueOf = function () {
+ return this.$d.getTime()
+ }, $.startOf = function (t, a) {
+ const h = this
+ const c = !!g.u(a) || a
+ const d = g.p(t)
+ const $ = function (t, e) {
+ const n = g.w(h.$u ? Date.UTC(h.$y, e, t) : new Date(h.$y, e, t), h)
+ return c ? n : n.endOf(i)
+ }
+ const l = function (t, e) {
+ return g.w(h.toDate()[t].apply(h.toDate('s'), (c ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e)), h)
+ }
+ const y = this.$W
+ const M = this.$M
+ const m = this.$D
+ const D = `set${this.$u ? 'UTC' : ''}`
+ switch (d) {
+ case o:
+ return c ? $(1, 0) : $(31, 11)
+ case u:
+ return c ? $(1, M) : $(0, M + 1)
+ case s:
+ var v = this.$locale().weekStart || 0
+ var S = (y < v ? y + 7 : y) - v
+ return $(c ? m - S : m + (6 - S), M)
+ case i:
+ case f:
+ return l(`${D}Hours`, 0)
+ case r:
+ return l(`${D}Minutes`, 1)
+ case n:
+ return l(`${D}Seconds`, 2)
+ case e:
+ return l(`${D}Milliseconds`, 3)
+ default:
+ return this.clone()
+ }
+ }, $.endOf = function (t) {
+ return this.startOf(t, !1)
+ }, $.$set = function (s, a) {
+ let h; const c = g.p(s)
+ const d = `set${this.$u ? 'UTC' : ''}`
+ const $ = (h = {}, h[i] = `${d}Date`, h[f] = `${d}Date`, h[u] = `${d}Month`, h[o] = `${d}FullYear`, h[r] = `${d}Hours`,
+ h[n] = `${d}Minutes`, h[e] = `${d}Seconds`, h[t] = `${d}Milliseconds`, h)[c]
+ const l = c === i ? this.$D + (a - this.$W) : a
+ if (c === u || c === o) {
+ const y = this.clone().set(f, 1)
+ y.$d[$](l), y.init(), this.$d = y.set(f, Math.min(this.$D, y.daysInMonth())).$d
+ } else $ && this.$d[$](l)
+ return this.init(), this
+ }, $.set = function (t, e) {
+ return this.clone().$set(t, e)
+ }, $.get = function (t) {
+ return this[g.p(t)]()
+ }, $.add = function (t, a) {
+ let f; const
+ h = this
+ t = Number(t)
+ const c = g.p(a)
+ const d = function (e) {
+ const n = v(h)
+ return g.w(n.date(n.date() + Math.round(e * t)), h)
+ }
+ if (c === u) return this.set(u, this.$M + t)
+ if (c === o) return this.set(o, this.$y + t)
+ if (c === i) return d(1)
+ if (c === s) return d(7)
+ const $ = (f = {}, f[n] = 6e4, f[r] = 36e5, f[e] = 1e3, f)[c] || 1
+ const l = this.$d.getTime() + t * $
+ return g.w(l, this)
+ }, $.subtract = function (t, e) {
+ return this.add(-1 * t, e)
+ }, $.format = function (t) {
+ const e = this
+ if (!this.isValid()) return 'Invalid Date'
+ const n = t || 'YYYY-MM-DDTHH:mm:ssZ'
+ const r = g.z(this)
+ const i = this.$locale()
+ const s = this.$H
+ const u = this.$m
+ const a = this.$M
+ const o = i.weekdays
+ const f = i.months
+ const h = function (t, r, i, s) {
+ return t && (t[r] || t(e, n)) || i[r].substr(0, s)
+ }
+ const d = function (t) {
+ return g.s(s % 12 || 12, t, '0')
+ }
+ const $ = i.meridiem || function (t, e, n) {
+ const r = t < 12 ? 'AM' : 'PM'
+ return n ? r.toLowerCase() : r
+ }
+ const l = {
+ YY: String(this.$y).slice(-2),
+ YYYY: this.$y,
+ M: a + 1,
+ MM: g.s(a + 1, 2, '0'),
+ MMM: h(i.monthsShort, a, f, 3),
+ MMMM: h(f, a),
+ D: this.$D,
+ DD: g.s(this.$D, 2, '0'),
+ d: String(this.$W),
+ dd: h(i.weekdaysMin, this.$W, o, 2),
+ ddd: h(i.weekdaysShort, this.$W, o, 3),
+ dddd: o[this.$W],
+ H: String(s),
+ HH: g.s(s, 2, '0'),
+ h: d(1),
+ hh: d(2),
+ a: $(s, u, !0),
+ A: $(s, u, !1),
+ m: String(u),
+ mm: g.s(u, 2, '0'),
+ s: String(this.$s),
+ ss: g.s(this.$s, 2, '0'),
+ SSS: g.s(this.$ms, 3, '0'),
+ Z: r
+ }
+ return n.replace(c, (t, e) => e || l[t] || r.replace(':', ''))
+ }, $.utcOffset = function () {
+ return 15 * -Math.round(this.$d.getTimezoneOffset() / 15)
+ }, $.diff = function (t, f, h) {
+ let c; const d = g.p(f)
+ const $ = v(t)
+ const l = 6e4 * ($.utcOffset() - this.utcOffset())
+ const y = this - $
+ let M = g.m(this, $)
+ return M = (c = {}, c[o] = M / 12, c[u] = M, c[a] = M / 3, c[s] = (y - l) / 6048e5, c[i] = (y - l) / 864e5, c[r] = y / 36e5, c[n] = y / 6e4, c[e] = y / 1e3, c)[d] || y, h ? M : g.a(M)
+ }, $.daysInMonth = function () {
+ return this.endOf(u).$D
+ }, $.$locale = function () {
+ return M[this.$L]
+ }, $.locale = function (t, e) {
+ if (!t) return this.$L
+ const n = this.clone()
+ const r = D(t, e, !0)
+ return r && (n.$L = r), n
+ }, $.clone = function () {
+ return g.w(this.$d, this)
+ }, $.toDate = function () {
+ return new Date(this.valueOf())
+ }, $.toJSON = function () {
+ return this.isValid() ? this.toISOString() : null
+ }, $.toISOString = function () {
+ return this.$d.toISOString()
+ }, $.toString = function () {
+ return this.$d.toUTCString()
+ }, d
+ }())
+ const p = S.prototype
+ return v.prototype = p, [
+ ['$ms', t],
+ ['$s', e],
+ ['$m', n],
+ ['$H', r],
+ ['$W', i],
+ ['$M', u],
+ ['$y', o],
+ ['$D', f]
+ ].forEach((t) => {
+ p[t[1]] = function (e) {
+ return this.$g(e, t[0], t[1])
+ }
+ }), v.extend = function (t, e) {
+ return t.$i || (t(e, S, v), t.$i = !0), v
+ }, v.locale = D, v.isDayjs = m, v.unix = function (t) {
+ return v(1e3 * t)
+ }, v.en = M[y], v.Ls = M, v.p = {}, v
+}))
diff --git a/uni_modules/uview-ui/libs/util/emitter.js b/uni_modules/uview-ui/libs/util/emitter.js
new file mode 100644
index 0000000..1e64044
--- /dev/null
+++ b/uni_modules/uview-ui/libs/util/emitter.js
@@ -0,0 +1,51 @@
+/**
+ * 递归使用 call 方式this指向
+ * @param componentName // 需要找的组件的名称
+ * @param eventName // 事件名称
+ * @param params // 需要传递的参数
+ */
+function broadcast(componentName, eventName, params) {
+ // 循环子节点找到名称一样的子节点 否则 递归 当前子节点
+ this.$children.map((child) => {
+ if (componentName === child.$options.name) {
+ child.$emit.apply(child, [eventName].concat(params))
+ } else {
+ broadcast.apply(child, [componentName, eventName].concat(params))
+ }
+ })
+}
+export default {
+ methods: {
+ /**
+ * 派发 (向上查找) (一个)
+ * @param componentName // 需要找的组件的名称
+ * @param eventName // 事件名称
+ * @param params // 需要传递的参数
+ */
+ dispatch(componentName, eventName, params) {
+ let parent = this.$parent || this.$root// $parent 找到最近的父节点 $root 根节点
+ let { name } = parent.$options // 获取当前组件实例的name
+ // 如果当前有节点 && 当前没名称 且 当前名称等于需要传进来的名称的时候就去查找当前的节点
+ // 循环出当前名称的一样的组件实例
+ while (parent && (!name || name !== componentName)) {
+ parent = parent.$parent
+ if (parent) {
+ name = parent.$options.name
+ }
+ }
+ // 有节点表示当前找到了name一样的实例
+ if (parent) {
+ parent.$emit.apply(parent, [eventName].concat(params))
+ }
+ },
+ /**
+ * 广播 (向下查找) (广播多个)
+ * @param componentName // 需要找的组件的名称
+ * @param eventName // 事件名称
+ * @param params // 需要传递的参数
+ */
+ broadcast(componentName, eventName, params) {
+ broadcast.call(this, componentName, eventName, params)
+ }
+ }
+}
diff --git a/uni_modules/uview-ui/libs/util/route.js b/uni_modules/uview-ui/libs/util/route.js
new file mode 100644
index 0000000..2afeea5
--- /dev/null
+++ b/uni_modules/uview-ui/libs/util/route.js
@@ -0,0 +1,124 @@
+/**
+ * 路由跳转方法,该方法相对于直接使用uni.xxx的好处是使用更加简单快捷
+ * 并且带有路由拦截功能
+ */
+
+class Router {
+ constructor() {
+ // 原始属性定义
+ this.config = {
+ type: 'navigateTo',
+ url: '',
+ delta: 1, // navigateBack页面后退时,回退的层数
+ params: {}, // 传递的参数
+ animationType: 'pop-in', // 窗口动画,只在APP有效
+ animationDuration: 300, // 窗口动画持续时间,单位毫秒,只在APP有效
+ intercept: false // 是否需要拦截
+ }
+ // 因为route方法是需要对外赋值给另外的对象使用,同时route内部有使用this,会导致route失去上下文
+ // 这里在构造函数中进行this绑定
+ this.route = this.route.bind(this)
+ }
+
+ // 判断url前面是否有"/",如果没有则加上,否则无法跳转
+ addRootPath(url) {
+ return url[0] === '/' ? url : `/${url}`
+ }
+
+ // 整合路由参数
+ mixinParam(url, params) {
+ url = url && this.addRootPath(url)
+
+ // 使用正则匹配,主要依据是判断是否有"/","?","="等,如“/page/index/index?name=mary"
+ // 如果有url中有get参数,转换后无需带上"?"
+ let query = ''
+ if (/.*\/.*\?.*=.*/.test(url)) {
+ // object对象转为get类型的参数
+ query = uni.$u.queryParams(params, false)
+ // 因为已有get参数,所以后面拼接的参数需要带上"&"隔开
+ return url += `&${query}`
+ }
+ // 直接拼接参数,因为此处url中没有后面的query参数,也就没有"?/&"之类的符号
+ query = uni.$u.queryParams(params)
+ return url += query
+ }
+
+ // 对外的方法名称
+ async route(options = {}, params = {}) {
+ // 合并用户的配置和内部的默认配置
+ let mergeConfig = {}
+
+ if (typeof options === 'string') {
+ // 如果options为字符串,则为route(url, params)的形式
+ mergeConfig.url = this.mixinParam(options, params)
+ mergeConfig.type = 'navigateTo'
+ } else {
+ mergeConfig = uni.$u.deepMerge(this.config, options)
+ // 否则正常使用mergeConfig中的url和params进行拼接
+ mergeConfig.url = this.mixinParam(options.url, options.params)
+ }
+
+ // 如果本次跳转的路径和本页面路径一致,不执行跳转,防止用户快速点击跳转按钮,造成多次跳转同一个页面的问题
+ if (mergeConfig.url === uni.$u.page()) return
+
+ if (params.intercept) {
+ this.config.intercept = params.intercept
+ }
+ // params参数也带给拦截器
+ mergeConfig.params = params
+ // 合并内外部参数
+ mergeConfig = uni.$u.deepMerge(this.config, mergeConfig)
+ // 判断用户是否定义了拦截器
+ if (typeof uni.$u.routeIntercept === 'function') {
+ // 定一个promise,根据用户执行resolve(true)或者resolve(false)来决定是否进行路由跳转
+ const isNext = await new Promise((resolve, reject) => {
+ uni.$u.routeIntercept(mergeConfig, resolve)
+ })
+ // 如果isNext为true,则执行路由跳转
+ isNext && this.openPage(mergeConfig)
+ } else {
+ this.openPage(mergeConfig)
+ }
+ }
+
+ // 执行路由跳转
+ openPage(config) {
+ // 解构参数
+ const {
+ url,
+ type,
+ delta,
+ animationType,
+ animationDuration
+ } = config
+ if (config.type == 'navigateTo' || config.type == 'to') {
+ uni.navigateTo({
+ url,
+ animationType,
+ animationDuration
+ })
+ }
+ if (config.type == 'redirectTo' || config.type == 'redirect') {
+ uni.redirectTo({
+ url
+ })
+ }
+ if (config.type == 'switchTab' || config.type == 'tab') {
+ uni.switchTab({
+ url
+ })
+ }
+ if (config.type == 'reLaunch' || config.type == 'launch') {
+ uni.reLaunch({
+ url
+ })
+ }
+ if (config.type == 'navigateBack' || config.type == 'back') {
+ uni.navigateBack({
+ delta
+ })
+ }
+ }
+}
+
+export default (new Router()).route
diff --git a/uni_modules/uview-ui/package.json b/uni_modules/uview-ui/package.json
new file mode 100644
index 0000000..2be2fc8
--- /dev/null
+++ b/uni_modules/uview-ui/package.json
@@ -0,0 +1,85 @@
+{
+ "id": "uview-ui",
+ "name": "uview-ui",
+ "displayName": "uView2.0重磅发布,利剑出鞘,一统江湖",
+ "version": "2.0.38",
+ "description": "uView UI已完美兼容nvue,全面的组件和便捷的工具会让您信手拈来,如鱼得水",
+ "keywords": [
+ "uview",
+ "uview",
+ "ui",
+ "ui",
+ "uni-app",
+ "uni-app",
+ "ui"
+ ],
+ "repository": "https://github.com/umicro/uView2.0",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": "1416956117"
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/uview-ui",
+ "type": "component-vue"
+ },
+ "uni_modules": {
+ "dependencies": [],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y",
+ "alipay": "n"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "n"
+ },
+ "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"
+ },
+ "快应用": {
+ "华为": "y",
+ "联盟": "y"
+ }
+ }
+ }
+ }
+}
diff --git a/uni_modules/uview-ui/theme.scss b/uni_modules/uview-ui/theme.scss
new file mode 100644
index 0000000..331b30f
--- /dev/null
+++ b/uni_modules/uview-ui/theme.scss
@@ -0,0 +1,44 @@
+// 此文件为uView的主题变量,这些变量目前只能通过uni.scss引入才有效,另外由于
+// uni.scss中引入的样式会同时混入到全局样式文件和单独每一个页面的样式中,造成微信程序包太大,
+// 故uni.scss只建议放scss变量名相关样式,其他的样式可以通过main.js或者App.vue引入
+
+$u-main-color: #303133;
+$u-content-color: #606266;
+$u-tips-color: #909193;
+$u-light-color: #c0c4cc;
+$u-border-color: #dadbde;
+$u-bg-color: #f3f4f6;
+$u-disabled-color: #c8c9cc;
+
+$u-primary: #3c9cff;
+$u-primary-dark: #398ade;
+$u-primary-disabled: #9acafc;
+$u-primary-light: #ecf5ff;
+
+$u-warning: #f9ae3d;
+$u-warning-dark: #f1a532;
+$u-warning-disabled: #f9d39b;
+$u-warning-light: #fdf6ec;
+
+$u-success: #5ac725;
+$u-success-dark: #53c21d;
+$u-success-disabled: #a9e08f;
+$u-success-light: #f5fff0;
+
+$u-error: #f56c6c;
+$u-error-dark: #e45656;
+$u-error-disabled: #f7b2b2;
+$u-error-light: #fef0f0;
+
+$u-info: #909399;
+$u-info-dark: #767a82;
+$u-info-disabled: #c4c6c9;
+$u-info-light: #f4f4f5;
+
+// scss混入,为了少写几行#ifndef
+@mixin flex($direction: row) {
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ flex-direction: $direction;
+}
diff --git a/uni_modules/zero-markdown-view/changelog.md b/uni_modules/zero-markdown-view/changelog.md
new file mode 100644
index 0000000..d13259c
--- /dev/null
+++ b/uni_modules/zero-markdown-view/changelog.md
@@ -0,0 +1,15 @@
+## 2.0.5(2024-04-24)
+## 流式输出代码块解决方案
+## 2.0.4(2023-12-06)
+### 长按复制代码改为点击代码块复制
+## 2.0.3(2023-10-30)
+doc: 文档说明
+## 2.0.2(2023-10-30)
+- 新增长按复制代码-仅小程序可用
+- 新增代码块语言显示
+## 2.0.1(2023-10-27)
+##支持vue2,vue3
+## 2.0.0(2022-11-01)
+使用mp-html自带的插件,重新生成uniapp包,大幅减少插件体积
+## 1.0.0(2022-09-13)
+首次发布
diff --git a/uni_modules/zero-markdown-view/components/mp-html/highlight/config.js b/uni_modules/zero-markdown-view/components/mp-html/highlight/config.js
new file mode 100644
index 0000000..2f81762
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/mp-html/highlight/config.js
@@ -0,0 +1,5 @@
+export default {
+ copyByClickCode: true, // 点击代码块复制
+ showLanguageName: true, // 是否在代码块右上角显示语言的名称
+ showLineNumber: false // 是否显示行号
+}
diff --git a/uni_modules/zero-markdown-view/components/mp-html/highlight/index.js b/uni_modules/zero-markdown-view/components/mp-html/highlight/index.js
new file mode 100644
index 0000000..7faa633
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/mp-html/highlight/index.js
@@ -0,0 +1,109 @@
+/**
+ * @fileoverview highlight 插件
+ * Include prismjs (https://prismjs.com)
+ */
+import prism from './prism.min'
+import config from './config'
+import Parser from '../parser'
+
+function Highlight (vm) {
+ this.vm = vm
+}
+
+Highlight.prototype.onParse = function (node, vm) {
+ if (node.name === 'pre') {
+ if (vm.options.editable) {
+ node.attrs.class = (node.attrs.class || '') + ' hl-pre'
+ return
+ }
+ let i
+ for (i = node.children.length; i--;) {
+ if (node.children[i].name === 'code') break
+ }
+ if (i === -1) return
+ const code = node.children[i]
+ let className = code.attrs.class + ' ' + node.attrs.class
+ i = className.indexOf('language-')
+ if (i === -1) {
+ i = className.indexOf('lang-')
+ if (i === -1) {
+ className = 'language-text'
+ i = 9
+ } else {
+ i += 5
+ }
+ } else {
+ i += 9
+ }
+ let j
+ for (j = i; j < className.length; j++) {
+ if (className[j] === ' ') break
+ }
+ const lang = className.substring(i, j)
+ if (code.children.length) {
+ const text = this.vm.getText(code.children).replace(/&/g, '&')
+ if (!text) return
+ if (node.c) {
+ node.c = undefined
+ }
+ if (prism.languages[lang]) {
+ code.children = (new Parser(this.vm).parse(
+ // 加一层 pre 保留空白符
+ '' + prism.highlight(text, prism.languages[lang], lang).replace(/token /g, 'hl-') + '
'))[0].children
+ }
+ node.attrs.class = 'hl-pre'
+ code.attrs.class = 'hl-code'
+ code.attrs.style ='display:block;overflow: auto;'
+ if (config.showLanguageName) {
+ node.children.push({
+ name: 'div',
+ attrs: {
+ class: 'hl-language',
+ style: 'user-select:none;position:absolute;top:0;right:2px;font-size:10px;'
+ },
+ children: [{
+ type: 'text',
+ text: lang
+ }]
+ })
+ }
+ if (config.copyByClickCode) {
+ node.attrs.style += (node.attrs.style || '') + ';user-select:none;'
+ node.attrs['data-content'] = text
+ node.children.push({
+ name: 'div',
+ attrs: {
+ class: 'hl-copy',
+ style: 'user-select:none;position:absolute;top:0;right:3px;font-size:10px;'
+ },
+ // children: [{
+ // type: 'text',
+ // text: '复制'
+ // }]
+ })
+ vm.expose()
+ // console.log('vm',node,vm)
+ }
+ if (config.showLineNumber) {
+ const line = text.split('\n').length; const children = []
+ for (let k = line; k--;) {
+ children.push({
+ name: 'span',
+ attrs: {
+ class: 'span'
+ }
+ })
+ }
+ node.children.push({
+ name: 'span',
+ attrs: {
+ class: 'line-numbers-rows'
+ },
+ children
+ })
+ }
+ }
+ }
+}
+
+export default Highlight
diff --git a/uni_modules/zero-markdown-view/components/mp-html/highlight/prism.min.js b/uni_modules/zero-markdown-view/components/mp-html/highlight/prism.min.js
new file mode 100644
index 0000000..0b67d39
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/mp-html/highlight/prism.min.js
@@ -0,0 +1,7 @@
+/*! PrismJS 1.22.0
+https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */
+var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var c=/\blang(?:uage)?-([\w-]+)\b/i,n=0,M={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof W?new W(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=l.reach);k+=y.value.length,y=y.next){var b=y.value;if(t.length>n.length)return;if(!(b instanceof W)){var x=1;if(h&&y!=t.tail.prev){m.lastIndex=k;var w=m.exec(n);if(!w)break;var A=w.index+(f&&w[1]?w[1].length:0),P=w.index+w[0].length,S=k;for(S+=y.value.length;S<=A;)y=y.next,S+=y.value.length;if(S-=y.value.length,k=S,y.value instanceof W)continue;for(var E=y;E!==t.tail&&(Sl.reach&&(l.reach=j);var C=y.prev;L&&(C=I(t,C,L),k+=L.length),z(t,C,x);var _=new W(o,g?M.tokenize(O,g):O,v,O);y=I(t,C,_),N&&I(t,y,N),1"+a.content+""+a.tag+">"},!u.document)return u.addEventListener&&(M.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),t=n.language,r=n.code,a=n.immediateClose;u.postMessage(M.highlight(r,M.languages[t],t)),a&&u.close()},!1)),M;var e=M.util.currentScript();function t(){M.manual||M.highlightAll()}if(e&&(M.filename=e.src,e.hasAttribute("data-manual")&&(M.manual=!0)),!M.manual){var r=document.readyState;"loading"===r||"interactive"===r&&e&&e.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)}return M}(_self);export default Prism;"undefined"!=typeof global&&(global.Prism=Prism);
+Prism.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/,name:/[^\s<>'"]+/}},cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^$/i;var n={"included-cdata":{pattern://i,inside:s}};n["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var t={};t[a]={pattern:RegExp("(<__[^]*?>)(?:))*\\]\\]>|(?!)".replace(/__/g,function(){return a}),"i"),lookbehind:!0,greedy:!0,inside:n},Prism.languages.insertBefore("markup","cdata",t)}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;
+!function(e){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\((?!\s*\))\s*)(?:[^()]|\((?:[^()]|\([^()]*\))*\))+?(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+t.source+")*?(?=\\s*\\{)"),string:{pattern:t,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var s=e.languages.markup;s&&(s.tag.addInlined("style","css"),e.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/(^|["'\s])style\s*=\s*(?:"[^"]*"|'[^']*')/i,lookbehind:!0,inside:{"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{style:{pattern:/(["'])[\s\S]+(?=["']$)/,lookbehind:!0,alias:"language-css",inside:e.languages.css},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},"attr-name":/^style/i}}},s.tag))}(Prism);
+Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
+Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|(?:get|set)(?=\s*[\[$\w\xA0-\uFFFF])|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-flags":/[a-z]+$/,"regex-delimiter":/^\/|\/$/}},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.js=Prism.languages.javascript;
diff --git a/uni_modules/zero-markdown-view/components/mp-html/markdown/index.js b/uni_modules/zero-markdown-view/components/mp-html/markdown/index.js
new file mode 100644
index 0000000..8900403
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/mp-html/markdown/index.js
@@ -0,0 +1,34 @@
+/**
+ * @fileoverview markdown 插件
+ * Include marked (https://github.com/markedjs/marked)
+ * Include github-markdown-css (https://github.com/sindresorhus/github-markdown-css)
+ */
+import marked from './marked.min'
+let index = 0
+
+function Markdown (vm) {
+ this.vm = vm
+ vm._ids = {}
+}
+
+Markdown.prototype.onUpdate = function (content) {
+ if (this.vm.markdown) {
+ return marked(content)
+ }
+}
+
+Markdown.prototype.onParse = function (node, vm) {
+ if (vm.options.markdown) {
+ // 中文 id 需要转换,否则无法跳转
+ if (vm.options.useAnchor && node.attrs && /[\u4e00-\u9fa5]/.test(node.attrs.id)) {
+ const id = 't' + index++
+ this.vm._ids[node.attrs.id] = id
+ node.attrs.id = id
+ }
+ if (node.name === 'p' || node.name === 'table' || node.name === 'tr' || node.name === 'th' || node.name === 'td' || node.name === 'blockquote' || node.name === 'pre' || node.name === 'code') {
+ node.attrs.class = `md-${node.name} ${node.attrs.class || ''}`
+ }
+ }
+}
+
+export default Markdown
diff --git a/uni_modules/zero-markdown-view/components/mp-html/markdown/marked.min.js b/uni_modules/zero-markdown-view/components/mp-html/markdown/marked.min.js
new file mode 100644
index 0000000..2efcf53
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/mp-html/markdown/marked.min.js
@@ -0,0 +1,6 @@
+/*!
+ * marked - a markdown parser
+ * Copyright (c) 2011-2020, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/markedjs/marked
+ */
+function t(){"use strict";function i(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function n(e){return c[e]}var e,t=(function(t){function e(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:null,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,tokenizer:null,walkTokens:null,xhtml:!1}}t.exports={defaults:e(),getDefaults:e,changeDefaults:function(e){t.exports.defaults=e}}}(e={exports:{}}),e.exports),r=(t.defaults,t.getDefaults,t.changeDefaults,/[&<>"']/),l=/[&<>"']/g,a=/[<>"']|&(?!#?\w+;)/,o=/[<>"']|&(?!#?\w+;)/g,c={"&":"&","<":"<",">":">",'"':""","'":"'"};var u=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function h(e){return e.replace(u,function(e,t){return"colon"===(t=t.toLowerCase())?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""})}var g=/(^|[^\[])\^/g;var f=/[^\w:]/g,d=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;var k={},b=/^[^:]+:\/*[^/]*$/,m=/^([^:]+:)[\s\S]*$/,x=/^([^:]+:\/*[^/]*)[\s\S]*$/;function w(e,t){k[" "+e]||(b.test(e)?k[" "+e]=e+"/":k[" "+e]=v(e,"/",!0));var n=-1===(e=k[" "+e]).indexOf(":");return"//"===t.substring(0,2)?n?t:e.replace(m,"$1")+t:"/"===t.charAt(0)?n?t:e.replace(x,"$1")+t:e+t}function v(e,t,n){var r=e.length;if(0===r)return"";for(var i=0;it)n.splice(t);else for(;n.length>=1,e+=e;return n+e},q=t.defaults,O=v,C=R,U=_,j=T;function E(e,t,n){var r=t.href,i=t.title?U(t.title):null,t=e[1].replace(/\\([\[\]])/g,"$1");return"!"!==e[0].charAt(0)?{type:"link",raw:n,href:r,title:i,text:t}:{type:"image",raw:n,href:r,title:i,text:U(t)}}var D=function(){function e(e){this.options=e||q}var t=e.prototype;return t.space=function(e){e=this.rules.block.newline.exec(e);if(e)return 1=n.length?e.slice(n.length):e}).join("\n")}(n,t[3]||"");return{type:"code",raw:n,lang:t[2]&&t[2].trim(),text:e}}},t.heading=function(e){e=this.rules.block.heading.exec(e);if(e)return{type:"heading",raw:e[0],depth:e[1].length,text:e[2]}},t.nptable=function(e){e=this.rules.block.nptable.exec(e);if(e){var t={type:"table",header:C(e[1].replace(/^ *| *\| *$/g,"")),align:e[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:e[3]?e[3].replace(/\n$/,"").split("\n"):[],raw:e[0]};if(t.header.length===t.align.length){for(var n=t.align.length,r=0;r ?/gm,"");return{type:"blockquote",raw:t[0],text:e}}},t.list=function(e){e=this.rules.block.list.exec(e);if(e){for(var t,n,r,i,s,l=e[0],a=e[2],o=1g[0].length||3/i.test(e[0])&&(t=!1),!n&&/^<(pre|code|kbd|script)(\s|>)/i.test(e[0])?n=!0:n&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(e[0])&&(n=!1),{type:this.options.sanitize?"text":"html",raw:e[0],inLink:t,inRawBlock:n,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):U(e[0]):e[0]}},t.link=function(e){var t=this.rules.inline.link.exec(e);if(t){e=j(t[2],"()");-1$/,"$1"))&&e.replace(this.rules.inline._escapes,"$1"),title:r&&r.replace(this.rules.inline._escapes,"$1")},t[0])}},t.reflink=function(e,t){if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){e=(n[2]||n[1]).replace(/\s+/g," ");if((e=t[e.toLowerCase()])&&e.href)return E(n,e,n[0]);var n=n[0].charAt(0);return{type:"text",raw:n,text:n}}},t.strong=function(e,t,n){void 0===n&&(n="");var r=this.rules.inline.strong.start.exec(e);if(r&&(!r[1]||r[1]&&(""===n||this.rules.inline.punctuation.exec(n)))){t=t.slice(-1*e.length);var i,s="**"===r[0]?this.rules.inline.strong.endAst:this.rules.inline.strong.endUnd;for(s.lastIndex=0;null!=(r=s.exec(t));)if(i=this.rules.inline.strong.middle.exec(t.slice(0,r.index+3)))return{type:"strong",raw:e.slice(0,i[0].length),text:e.slice(2,i[0].length-2)}}},t.em=function(e,t,n){void 0===n&&(n="");var r=this.rules.inline.em.start.exec(e);if(r&&(!r[1]||r[1]&&(""===n||this.rules.inline.punctuation.exec(n)))){t=t.slice(-1*e.length);var i,s="*"===r[0]?this.rules.inline.em.endAst:this.rules.inline.em.endUnd;for(s.lastIndex=0;null!=(r=s.exec(t));)if(i=this.rules.inline.em.middle.exec(t.slice(0,r.index+2)))return{type:"em",raw:e.slice(0,i[0].length),text:e.slice(1,i[0].length-1)}}},t.codespan=function(e){var t=this.rules.inline.code.exec(e);if(t){var n=t[2].replace(/\n/g," "),r=/[^ ]/.test(n),e=n.startsWith(" ")&&n.endsWith(" ");return r&&e&&(n=n.substring(1,n.length-1)),n=U(n,!0),{type:"codespan",raw:t[0],text:n}}},t.br=function(e){e=this.rules.inline.br.exec(e);if(e)return{type:"br",raw:e[0]}},t.del=function(e){e=this.rules.inline.del.exec(e);if(e)return{type:"del",raw:e[0],text:e[2]}},t.autolink=function(e,t){e=this.rules.inline.autolink.exec(e);if(e){var n,t="@"===e[2]?"mailto:"+(n=U(this.options.mangle?t(e[1]):e[1])):n=U(e[1]);return{type:"link",raw:e[0],text:n,href:t,tokens:[{type:"text",raw:n,text:n}]}}},t.url=function(e,t){var n,r,i,s;if(n=this.rules.inline.url.exec(e)){if("@"===n[2])i="mailto:"+(r=U(this.options.mangle?t(n[0]):n[0]));else{for(;s=n[0],n[0]=this.rules.inline._backpedal.exec(n[0])[0],s!==n[0];);r=U(n[0]),i="www."===n[1]?"http://"+r:r}return{type:"link",raw:n[0],text:r,href:i,tokens:[{type:"text",raw:r,text:r}]}}},t.inlineText=function(e,t,n){e=this.rules.inline.text.exec(e);if(e){n=t?this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):U(e[0]):e[0]:U(this.options.smartypants?n(e[0]):e[0]);return{type:"text",raw:e[0],text:n}}},e}(),R=$,T=z,$=A,z={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:R,table:R,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,text:/^[^\n]+/,_label:/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,_title:/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/};z.def=T(z.def).replace("label",z._label).replace("title",z._title).getRegex(),z.bullet=/(?:[*+-]|\d{1,9}[.)])/,z.item=/^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/,z.item=T(z.item,"gm").replace(/bull/g,z.bullet).getRegex(),z.listItemStart=T(/^( *)(bull)/).replace("bull",z.bullet).getRegex(),z.list=T(z.list).replace(/bull/g,z.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+z.def.source+")").getRegex(),z._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",z._comment=/|$)/,z.html=T(z.html,"i").replace("comment",z._comment).replace("tag",z._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),z.paragraph=T(z._paragraph).replace("hr",z.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)").replace("tag",z._tag).getRegex(),z.blockquote=T(z.blockquote).replace("paragraph",z.paragraph).getRegex(),z.normal=$({},z),z.gfm=$({},z.normal,{nptable:"^ *([^|\\n ].*\\|.*)\\n {0,3}([-:]+ *\\|[-| :]*)(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)",table:"^ *\\|(.+)\\n {0,3}\\|?( *[-:]+[-| :]*)(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"}),z.gfm.nptable=T(z.gfm.nptable).replace("hr",z.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)").replace("tag",z._tag).getRegex(),z.gfm.table=T(z.gfm.table).replace("hr",z.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)").replace("tag",z._tag).getRegex(),z.pedantic=$({},z.normal,{html:T("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",z._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,fences:R,paragraph:T(z.normal._paragraph).replace("hr",z.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",z.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()});R={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:R,tag:"^comment|^[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",strong:{start:/^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,middle:/^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,endAst:/[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/},em:{start:/^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,middle:/^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,endAst:/[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:R,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~"};R.punctuation=T(R.punctuation).replace(/punctuation/g,R._punctuation).getRegex(),R._blockSkip="\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>",R._overlapSkip="__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*",R._comment=T(z._comment).replace("(?:--\x3e|$)","--\x3e").getRegex(),R.em.start=T(R.em.start).replace(/punctuation/g,R._punctuation).getRegex(),R.em.middle=T(R.em.middle).replace(/punctuation/g,R._punctuation).replace(/overlapSkip/g,R._overlapSkip).getRegex(),R.em.endAst=T(R.em.endAst,"g").replace(/punctuation/g,R._punctuation).getRegex(),R.em.endUnd=T(R.em.endUnd,"g").replace(/punctuation/g,R._punctuation).getRegex(),R.strong.start=T(R.strong.start).replace(/punctuation/g,R._punctuation).getRegex(),R.strong.middle=T(R.strong.middle).replace(/punctuation/g,R._punctuation).replace(/overlapSkip/g,R._overlapSkip).getRegex(),R.strong.endAst=T(R.strong.endAst,"g").replace(/punctuation/g,R._punctuation).getRegex(),R.strong.endUnd=T(R.strong.endUnd,"g").replace(/punctuation/g,R._punctuation).getRegex(),R.blockSkip=T(R._blockSkip,"g").getRegex(),R.overlapSkip=T(R._overlapSkip,"g").getRegex(),R._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,R._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,R._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,R.autolink=T(R.autolink).replace("scheme",R._scheme).replace("email",R._email).getRegex(),R._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,R.tag=T(R.tag).replace("comment",R._comment).replace("attribute",R._attribute).getRegex(),R._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,R._href=/<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/,R._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,R.link=T(R.link).replace("label",R._label).replace("href",R._href).replace("title",R._title).getRegex(),R.reflink=T(R.reflink).replace("label",R._label).getRegex(),R.reflinkSearch=T(R.reflinkSearch,"g").replace("reflink",R.reflink).replace("nolink",R.nolink).getRegex(),R.normal=$({},R),R.pedantic=$({},R.normal,{strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:T(/^!?\[(label)\]\((.*?)\)/).replace("label",R._label).getRegex(),reflink:T(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",R._label).getRegex()}),R.gfm=$({},R.normal,{escape:T(R.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\'+(n?e:V(e,!0))+"\n":""+(n?e:V(e,!0))+"
\n"},t.blockquote=function(e){return"\n"+e+"
\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"\n":""+e+"\n"},t.hr=function(){return this.options.xhtml?"
\n":"
\n"},t.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+r+">\n"},t.listitem=function(e){return""+e+"\n"},t.checkbox=function(e){return" "},t.paragraph=function(e){return""+e+"
\n"},t.table=function(e,t){return"\n\n"+e+"\n"+(t=t&&""+t+"")+"
\n"},t.tablerow=function(e){return"\n"+e+"
\n"},t.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+""+n+">\n"},t.strong=function(e){return""+e+""},t.em=function(e){return""+e+""},t.codespan=function(e){return""+e+""},t.br=function(){return this.options.xhtml?"
":"
"},t.del=function(e){return""+e+""},t.link=function(e,t,n){if(null===(e=G(this.options.sanitize,this.options.baseUrl,e)))return n;e='"+n+""},t.image=function(e,t,n){if(null===(e=G(this.options.sanitize,this.options.baseUrl,e)))return n;n='
":">"},t.text=function(e){return e},e}(),J=function(){function e(){}var t=e.prototype;return t.strong=function(e){return e},t.em=function(e){return e},t.codespan=function(e){return e},t.del=function(e){return e},t.html=function(e){return e},t.text=function(e){return e},t.link=function(e,t,n){return""+n},t.image=function(e,t,n){return""+n},t.br=function(){return""},e}(),K=function(){function e(){this.seen={}}var t=e.prototype;return t.serialize=function(e){return e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")},t.getNextSafeSlug=function(e,t){var n=e,r=0;if(this.seen.hasOwnProperty(n))for(r=this.seen[e];n=e+"-"+ ++r,this.seen.hasOwnProperty(n););return t||(this.seen[e]=r,this.seen[n]=0),n},t.slug=function(e,t){void 0===t&&(t={});var n=this.serialize(e);return this.getNextSafeSlug(n,t.dryrun)},e}(),Q=t.defaults,Y=y,ee=function(){function n(e){this.options=e||Q,this.options.renderer=this.options.renderer||new H,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new J,this.slugger=new K}n.parse=function(e,t){return new n(t).parse(e)},n.parseInline=function(e,t){return new n(t).parseInline(e)};var e=n.prototype;return e.parse=function(e,t){void 0===t&&(t=!0);for(var n,r,i,s,l,a,o,c,u,p,h,g,f,d,k,b="",m=e.length,x=0;xAn error occurred:
"+re(e.message+"",!0)+"
";throw e}}return se.options=se.setOptions=function(e){return te(se.defaults,e),ie(se.defaults),se},se.getDefaults=_,se.defaults=t,se.use=function(a){var t,n=te({},a);a.renderer&&function(){var e,l=se.defaults.renderer||new H;for(e in a.renderer)!function(i){var s=l[i];l[i]=function(){for(var e=arguments.length,t=new Array(e),n=0;nAn error occurred:"+re(e.message+"",!0)+"
";throw e}},se.Parser=ee,se.parser=ee.parse,se.Renderer=H,se.TextRenderer=J,se.Lexer=W,se.lexer=W.lex,se.Tokenizer=D,se.Slugger=K,se.parse=se};export default t();
\ No newline at end of file
diff --git a/uni_modules/zero-markdown-view/components/mp-html/mp-html.vue b/uni_modules/zero-markdown-view/components/mp-html/mp-html.vue
new file mode 100644
index 0000000..332c3e0
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/mp-html/mp-html.vue
@@ -0,0 +1,503 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/zero-markdown-view/components/mp-html/node/node.vue b/uni_modules/zero-markdown-view/components/mp-html/node/node.vue
new file mode 100644
index 0000000..3253509
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/mp-html/node/node.vue
@@ -0,0 +1,678 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{n.text}}
+
+
+ {{n.text}}
+
+ \n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/zero-markdown-view/components/mp-html/parser.js b/uni_modules/zero-markdown-view/components/mp-html/parser.js
new file mode 100644
index 0000000..e2e7a87
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/mp-html/parser.js
@@ -0,0 +1,1335 @@
+/**
+ * @fileoverview html 解析器
+ */
+
+// 配置
+const config = {
+ // 信任的标签(保持标签名不变)
+ trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,ruby,rt,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'),
+
+ // 块级标签(转为 div,其他的非信任标签转为 span)
+ blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,pre,section'),
+
+ // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+ // 行内标签
+ inlineTags: makeMap('abbr,b,big,code,del,em,i,ins,label,q,small,span,strong,sub,sup'),
+ // #endif
+
+ // 要移除的标签
+ ignoreTags: makeMap('area,base,canvas,embed,frame,head,iframe,input,link,map,meta,param,rp,script,source,style,textarea,title,track,wbr'),
+
+ // 自闭合的标签
+ voidTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
+
+ // html 实体
+ entities: {
+ lt: '<',
+ gt: '>',
+ quot: '"',
+ apos: "'",
+ ensp: '\u2002',
+ emsp: '\u2003',
+ nbsp: '\xA0',
+ semi: ';',
+ ndash: '–',
+ mdash: '—',
+ middot: '·',
+ lsquo: '‘',
+ rsquo: '’',
+ ldquo: '“',
+ rdquo: '”',
+ bull: '•',
+ hellip: '…',
+ larr: '←',
+ uarr: '↑',
+ rarr: '→',
+ darr: '↓'
+ },
+
+ // 默认的标签样式
+ tagStyle: {
+ // #ifndef APP-PLUS-NVUE
+ address: 'font-style:italic',
+ big: 'display:inline;font-size:1.2em',
+ caption: 'display:table-caption;text-align:center',
+ center: 'text-align:center',
+ cite: 'font-style:italic',
+ dd: 'margin-left:40px',
+ mark: 'background-color:yellow',
+ pre: 'font-family:monospace;white-space:pre',
+ s: 'text-decoration:line-through',
+ small: 'display:inline;font-size:0.8em',
+ strike: 'text-decoration:line-through',
+ u: 'text-decoration:underline'
+ // #endif
+ },
+
+ // svg 大小写对照表
+ svgDict: {
+ animatetransform: 'animateTransform',
+ lineargradient: 'linearGradient',
+ viewbox: 'viewBox',
+ attributename: 'attributeName',
+ repeatcount: 'repeatCount',
+ repeatdur: 'repeatDur'
+ }
+}
+const tagSelector={}
+const {
+ windowWidth,
+ // #ifdef MP-WEIXIN
+ system
+ // #endif
+} = uni.getSystemInfoSync()
+const blankChar = makeMap(' ,\r,\n,\t,\f')
+let idIndex = 0
+
+// #ifdef H5 || APP-PLUS
+config.ignoreTags.iframe = undefined
+config.trustTags.iframe = true
+config.ignoreTags.embed = undefined
+config.trustTags.embed = true
+// #endif
+// #ifdef APP-PLUS-NVUE
+config.ignoreTags.source = undefined
+config.ignoreTags.style = undefined
+// #endif
+
+/**
+ * @description 创建 map
+ * @param {String} str 逗号分隔
+ */
+function makeMap (str) {
+ const map = Object.create(null)
+ const list = str.split(',')
+ for (let i = list.length; i--;) {
+ map[list[i]] = true
+ }
+ return map
+}
+
+/**
+ * @description 解码 html 实体
+ * @param {String} str 要解码的字符串
+ * @param {Boolean} amp 要不要解码 &
+ * @returns {String} 解码后的字符串
+ */
+function decodeEntity (str, amp) {
+ let i = str.indexOf('&')
+ while (i !== -1) {
+ const j = str.indexOf(';', i + 3)
+ let code
+ if (j === -1) break
+ if (str[i + 1] === '#') {
+ // { 形式的实体
+ code = parseInt((str[i + 2] === 'x' ? '0' : '') + str.substring(i + 2, j))
+ if (!isNaN(code)) {
+ str = str.substr(0, i) + String.fromCharCode(code) + str.substr(j + 1)
+ }
+ } else {
+ // 形式的实体
+ code = str.substring(i + 1, j)
+ if (config.entities[code] || (code === 'amp' && amp)) {
+ str = str.substr(0, i) + (config.entities[code] || '&') + str.substr(j + 1)
+ }
+ }
+ i = str.indexOf('&', i + 1)
+ }
+ return str
+}
+
+/**
+ * @description 合并多个块级标签,加快长内容渲染
+ * @param {Array} nodes 要合并的标签数组
+ */
+function mergeNodes (nodes) {
+ let i = nodes.length - 1
+ for (let j = i; j >= -1; j--) {
+ if (j === -1 || nodes[j].c || !nodes[j].name || (nodes[j].name !== 'div' && nodes[j].name !== 'p' && nodes[j].name[0] !== 'h') || (nodes[j].attrs.style || '').includes('inline')) {
+ if (i - j >= 5) {
+ nodes.splice(j + 1, i - j, {
+ name: 'div',
+ attrs: {},
+ children: nodes.slice(j + 1, i + 1)
+ })
+ }
+ i = j - 1
+ }
+ }
+}
+
+/**
+ * @description html 解析器
+ * @param {Object} vm 组件实例
+ */
+function Parser (vm) {
+ this.options = vm || {}
+ this.tagStyle = Object.assign({}, config.tagStyle, this.options.tagStyle)
+ this.imgList = vm.imgList || []
+ this.imgList._unloadimgs = 0
+ this.plugins = vm.plugins || []
+ this.attrs = Object.create(null)
+ this.stack = []
+ this.nodes = []
+ this.pre = (this.options.containerStyle || '').includes('white-space') && this.options.containerStyle.includes('pre') ? 2 : 0
+}
+
+/**
+ * @description 执行解析
+ * @param {String} content 要解析的文本
+ */
+Parser.prototype.parse = function (content) {
+ // 插件处理
+ for (let i = this.plugins.length; i--;) {
+ if (this.plugins[i].onUpdate) {
+ content = this.plugins[i].onUpdate(content, config) || content
+ }
+ }
+
+ new Lexer(this).parse(content)
+ // 出栈未闭合的标签
+ while (this.stack.length) {
+ this.popNode()
+ }
+ if (this.nodes.length > 50) {
+ mergeNodes(this.nodes)
+ }
+ return this.nodes
+}
+
+/**
+ * @description 将标签暴露出来(不被 rich-text 包含)
+ */
+Parser.prototype.expose = function () {
+ // #ifndef APP-PLUS-NVUE
+ for (let i = this.stack.length; i--;) {
+ const item = this.stack[i]
+ if (item.c || item.name === 'a' || item.name === 'video' || item.name === 'audio') return
+ item.c = 1
+ }
+ // #endif
+}
+
+/**
+ * @description 处理插件
+ * @param {Object} node 要处理的标签
+ * @returns {Boolean} 是否要移除此标签
+ */
+Parser.prototype.hook = function (node) {
+ for (let i = this.plugins.length; i--;) {
+ if (this.plugins[i].onParse && this.plugins[i].onParse(node, this) === false) {
+ return false
+ }
+ }
+ return true
+}
+
+/**
+ * @description 将链接拼接上主域名
+ * @param {String} url 需要拼接的链接
+ * @returns {String} 拼接后的链接
+ */
+Parser.prototype.getUrl = function (url) {
+ const domain = this.options.domain
+ if (url[0] === '/') {
+ if (url[1] === '/') {
+ // // 开头的补充协议名
+ url = (domain ? domain.split('://')[0] : 'http') + ':' + url
+ } else if (domain) {
+ // 否则补充整个域名
+ url = domain + url
+ } /* #ifdef APP-PLUS */ else {
+ url = plus.io.convertLocalFileSystemURL(url)
+ } /* #endif */
+ } else if (!url.includes('data:') && !url.includes('://')) {
+ if (domain) {
+ url = domain + '/' + url
+ } /* #ifdef APP-PLUS */ else {
+ url = plus.io.convertLocalFileSystemURL(url)
+ } /* #endif */
+ }
+ return url
+}
+
+/**
+ * @description 解析样式表
+ * @param {Object} node 标签
+ * @returns {Object}
+ */
+Parser.prototype.parseStyle = function (node) {
+ const attrs = node.attrs
+ const list = (this.tagStyle[node.name] || '').split(';').concat((attrs.style || '').split(';'))
+ const styleObj = {}
+ let tmp = ''
+
+ if (attrs.id && !this.xml) {
+ // 暴露锚点
+ if (this.options.useAnchor) {
+ this.expose()
+ } else if (node.name !== 'img' && node.name !== 'a' && node.name !== 'video' && node.name !== 'audio') {
+ attrs.id = undefined
+ }
+ }
+
+ // 转换 width 和 height 属性
+ if (attrs.width) {
+ styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px')
+ attrs.width = undefined
+ }
+ if (attrs.height) {
+ styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px')
+ attrs.height = undefined
+ }
+
+ for (let i = 0, len = list.length; i < len; i++) {
+ const info = list[i].split(':')
+ if (info.length < 2) continue
+ const key = info.shift().trim().toLowerCase()
+ let value = info.join(':').trim()
+ if ((value[0] === '-' && value.lastIndexOf('-') > 0) || value.includes('safe')) {
+ // 兼容性的 css 不压缩
+ tmp += `;${key}:${value}`
+ } else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import')) {
+ // 重复的样式进行覆盖
+ if (value.includes('url')) {
+ // 填充链接
+ let j = value.indexOf('(') + 1
+ if (j) {
+ while (value[j] === '"' || value[j] === "'" || blankChar[value[j]]) {
+ j++
+ }
+ value = value.substr(0, j) + this.getUrl(value.substr(j))
+ }
+ } else if (value.includes('rpx')) {
+ // 转换 rpx(rich-text 内部不支持 rpx)
+ value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px')
+ }
+ styleObj[key] = value
+ }
+ }
+
+ node.attrs.style = tmp
+ return styleObj
+}
+
+/**
+ * @description 解析到标签名
+ * @param {String} name 标签名
+ * @private
+ */
+Parser.prototype.onTagName = function (name) {
+ this.tagName = this.xml ? name : name.toLowerCase()
+ if (this.tagName === 'svg') {
+ this.xml = (this.xml || 0) + 1 // svg 标签内大小写敏感
+ config.ignoreTags.style = undefined // svg 标签内 style 可用
+ }
+}
+
+/**
+ * @description 解析到属性名
+ * @param {String} name 属性名
+ * @private
+ */
+Parser.prototype.onAttrName = function (name) {
+ name = this.xml ? name : name.toLowerCase()
+ if (name.substr(0, 5) === 'data-') {
+ if (name === 'data-src' && !this.attrs.src) {
+ // data-src 自动转为 src
+ this.attrName = 'src'
+ } else if (this.tagName === 'img' || this.tagName === 'a') {
+ // a 和 img 标签保留 data- 的属性,可以在 imgtap 和 linktap 事件中使用
+ this.attrName = name
+ } else {
+ // 剩余的移除以减小大小
+ this.attrName = undefined
+ }
+ } else {
+ this.attrName = name
+ this.attrs[name] = 'T' // boolean 型属性缺省设置
+ }
+}
+
+/**
+ * @description 解析到属性值
+ * @param {String} val 属性值
+ * @private
+ */
+Parser.prototype.onAttrVal = function (val) {
+ const name = this.attrName || ''
+ if (name === 'style' || name === 'href') {
+ // 部分属性进行实体解码
+ this.attrs[name] = decodeEntity(val, true)
+ } else if (name.includes('src')) {
+ // 拼接主域名
+ this.attrs[name] = this.getUrl(decodeEntity(val, true))
+ } else if (name) {
+ this.attrs[name] = val
+ }
+}
+
+/**
+ * @description 解析到标签开始
+ * @param {Boolean} selfClose 是否有自闭合标识 />
+ * @private
+ */
+Parser.prototype.onOpenTag = function (selfClose) {
+ // 拼装 node
+ const node = Object.create(null)
+ node.name = this.tagName
+ node.attrs = this.attrs
+ // 避免因为自动 diff 使得 type 被设置为 null 导致部分内容不显示
+ if (this.options.nodes.length) {
+ node.type = 'node'
+ }
+ this.attrs = Object.create(null)
+
+ const attrs = node.attrs
+ const parent = this.stack[this.stack.length - 1]
+ const siblings = parent ? parent.children : this.nodes
+ const close = this.xml ? selfClose : config.voidTags[node.name]
+
+ // 替换标签名选择器
+ if (tagSelector[node.name]) {
+ attrs.class = tagSelector[node.name] + (attrs.class ? ' ' + attrs.class : '')
+ }
+
+ // 转换 embed 标签
+ if (node.name === 'embed') {
+ // #ifndef H5 || APP-PLUS
+ const src = attrs.src || ''
+ // 按照后缀名和 type 将 embed 转为 video 或 audio
+ if (src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8') || (attrs.type || '').includes('video')) {
+ node.name = 'video'
+ } else if (src.includes('.mp3') || src.includes('.wav') || src.includes('.aac') || src.includes('.m4a') || (attrs.type || '').includes('audio')) {
+ node.name = 'audio'
+ }
+ if (attrs.autostart) {
+ attrs.autoplay = 'T'
+ }
+ attrs.controls = 'T'
+ // #endif
+ // #ifdef H5 || APP-PLUS
+ this.expose()
+ // #endif
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ // 处理音视频
+ if (node.name === 'video' || node.name === 'audio') {
+ // 设置 id 以便获取 context
+ if (node.name === 'video' && !attrs.id) {
+ attrs.id = 'v' + idIndex++
+ }
+ // 没有设置 controls 也没有设置 autoplay 的自动设置 controls
+ if (!attrs.controls && !attrs.autoplay) {
+ attrs.controls = 'T'
+ }
+ // 用数组存储所有可用的 source
+ node.src = []
+ if (attrs.src) {
+ node.src.push(attrs.src)
+ attrs.src = undefined
+ }
+ this.expose()
+ }
+ // #endif
+
+ // 处理自闭合标签
+ if (close) {
+ if (!this.hook(node) || config.ignoreTags[node.name]) {
+ // 通过 base 标签设置主域名
+ if (node.name === 'base' && !this.options.domain) {
+ this.options.domain = attrs.href
+ } /* #ifndef APP-PLUS-NVUE */ else if (node.name === 'source' && parent && (parent.name === 'video' || parent.name === 'audio') && attrs.src) {
+ // 设置 source 标签(仅父节点为 video 或 audio 时有效)
+ parent.src.push(attrs.src)
+ } /* #endif */
+ return
+ }
+
+ // 解析 style
+ const styleObj = this.parseStyle(node)
+
+ // 处理图片
+ if (node.name === 'img') {
+ if (attrs.src) {
+ // 标记 webp
+ if (attrs.src.includes('webp')) {
+ node.webp = 'T'
+ }
+ // data url 图片如果没有设置 original-src 默认为不可预览的小图片
+ if (attrs.src.includes('data:') && !attrs['original-src']) {
+ attrs.ignore = 'T'
+ }
+ if (!attrs.ignore || node.webp || attrs.src.includes('cloud://')) {
+ for (let i = this.stack.length; i--;) {
+ const item = this.stack[i]
+ if (item.name === 'a') {
+ node.a = item.attrs
+ }
+ if (item.name === 'table' && !node.webp && !attrs.src.includes('cloud://')) {
+ if (!styleObj.display || styleObj.display.includes('inline')) {
+ node.t = 'inline-block'
+ } else {
+ node.t = styleObj.display
+ }
+ styleObj.display = undefined
+ }
+ // #ifndef H5 || APP-PLUS
+ const style = item.attrs.style || ''
+ if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || parseInt(styleObj.width) > 100)) {
+ styleObj.width = '100% !important'
+ styleObj.height = ''
+ for (let j = i + 1; j < this.stack.length; j++) {
+ this.stack[j].attrs.style = (this.stack[j].attrs.style || '').replace('inline-', '')
+ }
+ } else if (style.includes('flex') && styleObj.width === '100%') {
+ for (let j = i + 1; j < this.stack.length; j++) {
+ const style = this.stack[j].attrs.style || ''
+ if (!style.includes(';width') && !style.includes(' width') && style.indexOf('width') !== 0) {
+ styleObj.width = ''
+ break
+ }
+ }
+ } else if (style.includes('inline-block')) {
+ if (styleObj.width && styleObj.width[styleObj.width.length - 1] === '%') {
+ item.attrs.style += ';max-width:' + styleObj.width
+ styleObj.width = ''
+ } else {
+ item.attrs.style += ';max-width:100%'
+ }
+ }
+ // #endif
+ item.c = 1
+ }
+ attrs.i = this.imgList.length.toString()
+ let src = attrs['original-src'] || attrs.src
+ // #ifndef H5 || MP-ALIPAY || APP-PLUS || MP-360
+ if (this.imgList.includes(src)) {
+ // 如果有重复的链接则对域名进行随机大小写变换避免预览时错位
+ let i = src.indexOf('://')
+ if (i !== -1) {
+ i += 3
+ let newSrc = src.substr(0, i)
+ for (; i < src.length; i++) {
+ if (src[i] === '/') break
+ newSrc += Math.random() > 0.5 ? src[i].toUpperCase() : src[i]
+ }
+ newSrc += src.substr(i)
+ src = newSrc
+ }
+ }
+ // #endif
+ this.imgList.push(src)
+ if (!node.t) {
+ this.imgList._unloadimgs += 1
+ }
+ // #ifdef H5 || APP-PLUS
+ if (this.options.lazyLoad) {
+ attrs['data-src'] = attrs.src
+ attrs.src = undefined
+ }
+ // #endif
+ }
+ }
+ if (styleObj.display === 'inline') {
+ styleObj.display = ''
+ }
+ // #ifndef APP-PLUS-NVUE
+ if (attrs.ignore) {
+ styleObj['max-width'] = styleObj['max-width'] || '100%'
+ attrs.style += ';-webkit-touch-callout:none'
+ }
+ // #endif
+ // 设置的宽度超出屏幕,为避免变形,高度转为自动
+ if (parseInt(styleObj.width) > windowWidth) {
+ styleObj.height = undefined
+ }
+ // 记录是否设置了宽高
+ if (!isNaN(parseInt(styleObj.width))) {
+ node.w = 'T'
+ }
+ if (!isNaN(parseInt(styleObj.height)) && (!styleObj.height.includes('%') || (parent && (parent.attrs.style || '').includes('height')))) {
+ node.h = 'T'
+ }
+ } else if (node.name === 'svg') {
+ siblings.push(node)
+ this.stack.push(node)
+ this.popNode()
+ return
+ }
+ for (const key in styleObj) {
+ if (styleObj[key]) {
+ attrs.style += `;${key}:${styleObj[key].replace(' !important', '')}`
+ }
+ }
+ attrs.style = attrs.style.substr(1) || undefined
+ // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+ if (!attrs.style) {
+ delete attrs.style
+ }
+ // #endif
+ } else {
+ if ((node.name === 'pre' || ((attrs.style || '').includes('white-space') && attrs.style.includes('pre'))) && this.pre !== 2) {
+ this.pre = node.pre = 1
+ }
+ node.children = []
+ this.stack.push(node)
+ }
+
+ // 加入节点树
+ siblings.push(node)
+}
+
+/**
+ * @description 解析到标签结束
+ * @param {String} name 标签名
+ * @private
+ */
+Parser.prototype.onCloseTag = function (name) {
+ // 依次出栈到匹配为止
+ name = this.xml ? name : name.toLowerCase()
+ let i
+ for (i = this.stack.length; i--;) {
+ if (this.stack[i].name === name) break
+ }
+ if (i !== -1) {
+ while (this.stack.length > i) {
+ this.popNode()
+ }
+ } else if (name === 'p' || name === 'br') {
+ const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+ siblings.push({
+ name,
+ attrs: {
+ class: tagSelector[name] || '',
+ style: this.tagStyle[name] || ''
+ }
+ })
+ }
+}
+
+/**
+ * @description 处理标签出栈
+ * @private
+ */
+Parser.prototype.popNode = function () {
+ const node = this.stack.pop()
+ let attrs = node.attrs
+ const children = node.children
+ const parent = this.stack[this.stack.length - 1]
+ const siblings = parent ? parent.children : this.nodes
+
+ if (!this.hook(node) || config.ignoreTags[node.name]) {
+ // 获取标题
+ if (node.name === 'title' && children.length && children[0].type === 'text' && this.options.setTitle) {
+ uni.setNavigationBarTitle({
+ title: children[0].text
+ })
+ }
+ siblings.pop()
+ return
+ }
+
+ if (node.pre && this.pre !== 2) {
+ // 是否合并空白符标识
+ this.pre = node.pre = undefined
+ for (let i = this.stack.length; i--;) {
+ if (this.stack[i].pre) {
+ this.pre = 1
+ }
+ }
+ }
+
+ const styleObj = {}
+
+ // 转换 svg
+ if (node.name === 'svg') {
+ if (this.xml > 1) {
+ // 多层 svg 嵌套
+ this.xml--
+ return
+ }
+ // #ifdef APP-PLUS-NVUE
+ (function traversal (node) {
+ if (node.name) {
+ // 调整 svg 的大小写
+ node.name = config.svgDict[node.name] || node.name
+ for (const item in node.attrs) {
+ if (config.svgDict[item]) {
+ node.attrs[config.svgDict[item]] = node.attrs[item]
+ node.attrs[item] = undefined
+ }
+ }
+ for (let i = 0; i < (node.children || []).length; i++) {
+ traversal(node.children[i])
+ }
+ }
+ })(node)
+ // #endif
+ // #ifndef APP-PLUS-NVUE
+ let src = ''
+ const style = attrs.style
+ attrs.style = ''
+ attrs.xmlns = 'http://www.w3.org/2000/svg';
+ (function traversal (node) {
+ if (node.type === 'text') {
+ src += node.text
+ return
+ }
+ const name = config.svgDict[node.name] || node.name
+ src += '<' + name
+ for (const item in node.attrs) {
+ const val = node.attrs[item]
+ if (val) {
+ src += ` ${config.svgDict[item] || item}="${val}"`
+ }
+ }
+ if (!node.children) {
+ src += '/>'
+ } else {
+ src += '>'
+ for (let i = 0; i < node.children.length; i++) {
+ traversal(node.children[i])
+ }
+ src += '' + name + '>'
+ }
+ })(node)
+ node.name = 'img'
+ node.attrs = {
+ src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
+ style,
+ ignore: 'T'
+ }
+ node.children = undefined
+ // #endif
+ this.xml = false
+ config.ignoreTags.style = true
+ return
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ // 转换 align 属性
+ if (attrs.align) {
+ if (node.name === 'table') {
+ if (attrs.align === 'center') {
+ styleObj['margin-inline-start'] = styleObj['margin-inline-end'] = 'auto'
+ } else {
+ styleObj.float = attrs.align
+ }
+ } else {
+ styleObj['text-align'] = attrs.align
+ }
+ attrs.align = undefined
+ }
+
+ // 转换 dir 属性
+ if (attrs.dir) {
+ styleObj.direction = attrs.dir
+ attrs.dir = undefined
+ }
+
+ // 转换 font 标签的属性
+ if (node.name === 'font') {
+ if (attrs.color) {
+ styleObj.color = attrs.color
+ attrs.color = undefined
+ }
+ if (attrs.face) {
+ styleObj['font-family'] = attrs.face
+ attrs.face = undefined
+ }
+ if (attrs.size) {
+ let size = parseInt(attrs.size)
+ if (!isNaN(size)) {
+ if (size < 1) {
+ size = 1
+ } else if (size > 7) {
+ size = 7
+ }
+ styleObj['font-size'] = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'xxx-large'][size - 1]
+ }
+ attrs.size = undefined
+ }
+ }
+ // #endif
+
+ // 一些编辑器的自带 class
+ if ((attrs.class || '').includes('align-center')) {
+ styleObj['text-align'] = 'center'
+ }
+
+ Object.assign(styleObj, this.parseStyle(node))
+
+ if (node.name !== 'table' && parseInt(styleObj.width) > windowWidth) {
+ styleObj['max-width'] = '100%'
+ styleObj['box-sizing'] = 'border-box'
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ if (config.blockTags[node.name]) {
+ node.name = 'div'
+ } else if (!config.trustTags[node.name] && !this.xml) {
+ // 未知标签转为 span,避免无法显示
+ node.name = 'span'
+ }
+
+ if (node.name === 'a' || node.name === 'ad'
+ // #ifdef H5 || APP-PLUS
+ || node.name === 'iframe' // eslint-disable-line
+ // #endif
+ ) {
+ this.expose()
+ } else if (node.name === 'video') {
+ if ((styleObj.height || '').includes('auto')) {
+ styleObj.height = undefined
+ }
+ /* #ifdef APP-PLUS */
+ let str = ''
+ node.html = str
+ /* #endif */
+ } else if ((node.name === 'ul' || node.name === 'ol') && node.c) {
+ // 列表处理
+ const types = {
+ a: 'lower-alpha',
+ A: 'upper-alpha',
+ i: 'lower-roman',
+ I: 'upper-roman'
+ }
+ if (types[attrs.type]) {
+ attrs.style += ';list-style-type:' + types[attrs.type]
+ attrs.type = undefined
+ }
+ for (let i = children.length; i--;) {
+ if (children[i].name === 'li') {
+ children[i].c = 1
+ }
+ }
+ } else if (node.name === 'table') {
+ // 表格处理
+ // cellpadding、cellspacing、border 这几个常用表格属性需要通过转换实现
+ let padding = parseFloat(attrs.cellpadding)
+ let spacing = parseFloat(attrs.cellspacing)
+ const border = parseFloat(attrs.border)
+ const bordercolor = styleObj['border-color']
+ const borderstyle = styleObj['border-style']
+ if (node.c) {
+ // padding 和 spacing 默认 2
+ if (isNaN(padding)) {
+ padding = 2
+ }
+ if (isNaN(spacing)) {
+ spacing = 2
+ }
+ }
+ if (border) {
+ attrs.style += `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}`
+ }
+ if (node.flag && node.c) {
+ // 有 colspan 或 rowspan 且含有链接的表格通过 grid 布局实现
+ styleObj.display = 'grid'
+ if (spacing) {
+ styleObj['grid-gap'] = spacing + 'px'
+ styleObj.padding = spacing + 'px'
+ } else if (border) {
+ // 无间隔的情况下避免边框重叠
+ attrs.style += ';border-left:0;border-top:0'
+ }
+
+ const width = [] // 表格的列宽
+ const trList = [] // tr 列表
+ const cells = [] // 保存新的单元格
+ const map = {}; // 被合并单元格占用的格子
+
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ if (nodes[i].name === 'tr') {
+ trList.push(nodes[i])
+ } else {
+ traversal(nodes[i].children || [])
+ }
+ }
+ })(children)
+
+ for (let row = 1; row <= trList.length; row++) {
+ let col = 1
+ for (let j = 0; j < trList[row - 1].children.length; j++) {
+ const td = trList[row - 1].children[j]
+ if (td.name === 'td' || td.name === 'th') {
+ // 这个格子被上面的单元格占用,则列号++
+ while (map[row + '.' + col]) {
+ col++
+ }
+ let style = td.attrs.style || ''
+ let start = style.indexOf('width') ? style.indexOf(';width') : 0
+ // 提取出 td 的宽度
+ if (start !== -1) {
+ let end = style.indexOf(';', start + 6)
+ if (end === -1) {
+ end = style.length
+ }
+ if (!td.attrs.colspan) {
+ width[col] = style.substring(start ? start + 7 : 6, end)
+ }
+ style = style.substr(0, start) + style.substr(end)
+ }
+ // 设置竖直对齐
+ style += ';display:flex'
+ start = style.indexOf('vertical-align')
+ if (start !== -1) {
+ const val = style.substr(start + 15, 10)
+ if (val.includes('middle')) {
+ style += ';align-items:center'
+ } else if (val.includes('bottom')) {
+ style += ';align-items:flex-end'
+ }
+ } else {
+ style += ';align-items:center'
+ }
+ // 设置水平对齐
+ start = style.indexOf('text-align')
+ if (start !== -1) {
+ const val = style.substr(start + 11, 10)
+ if (val.includes('center')) {
+ style += ';justify-content: center'
+ } else if (val.includes('right')) {
+ style += ';justify-content: right'
+ }
+ }
+ style = (border ? `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}` + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? `;padding:${padding}px` : '') + ';' + style
+ // 处理列合并
+ if (td.attrs.colspan) {
+ style += `;grid-column-start:${col};grid-column-end:${col + parseInt(td.attrs.colspan)}`
+ if (!td.attrs.rowspan) {
+ style += `;grid-row-start:${row};grid-row-end:${row + 1}`
+ }
+ col += parseInt(td.attrs.colspan) - 1
+ }
+ // 处理行合并
+ if (td.attrs.rowspan) {
+ style += `;grid-row-start:${row};grid-row-end:${row + parseInt(td.attrs.rowspan)}`
+ if (!td.attrs.colspan) {
+ style += `;grid-column-start:${col};grid-column-end:${col + 1}`
+ }
+ // 记录下方单元格被占用
+ for (let rowspan = 1; rowspan < td.attrs.rowspan; rowspan++) {
+ for (let colspan = 0; colspan < (td.attrs.colspan || 1); colspan++) {
+ map[(row + rowspan) + '.' + (col - colspan)] = 1
+ }
+ }
+ }
+ if (style) {
+ td.attrs.style = style
+ }
+ cells.push(td)
+ col++
+ }
+ }
+ if (row === 1) {
+ let temp = ''
+ for (let i = 1; i < col; i++) {
+ temp += (width[i] ? width[i] : 'auto') + ' '
+ }
+ styleObj['grid-template-columns'] = temp
+ }
+ }
+ node.children = cells
+ } else {
+ // 没有使用合并单元格的表格通过 table 布局实现
+ if (node.c) {
+ styleObj.display = 'table'
+ }
+ if (!isNaN(spacing)) {
+ styleObj['border-spacing'] = spacing + 'px'
+ }
+ if (border || padding) {
+ // 遍历
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ const td = nodes[i]
+ if (td.name === 'th' || td.name === 'td') {
+ if (border) {
+ td.attrs.style = `border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'};${td.attrs.style || ''}`
+ }
+ if (padding) {
+ td.attrs.style = `padding:${padding}px;${td.attrs.style || ''}`
+ }
+ } else if (td.children) {
+ traversal(td.children)
+ }
+ }
+ })(children)
+ }
+ }
+ // 给表格添加一个单独的横向滚动层
+ if (this.options.scrollTable && !(attrs.style || '').includes('inline')) {
+ const table = Object.assign({}, node)
+ node.name = 'div'
+ node.attrs = {
+ style: 'overflow:auto'
+ }
+ node.children = [table]
+ attrs = table.attrs
+ }
+ } else if ((node.name === 'td' || node.name === 'th') && (attrs.colspan || attrs.rowspan)) {
+ for (let i = this.stack.length; i--;) {
+ if (this.stack[i].name === 'table') {
+ this.stack[i].flag = 1 // 指示含有合并单元格
+ break
+ }
+ }
+ } else if (node.name === 'ruby') {
+ // 转换 ruby
+ node.name = 'span'
+ for (let i = 0; i < children.length - 1; i++) {
+ if (children[i].type === 'text' && children[i + 1].name === 'rt') {
+ children[i] = {
+ name: 'div',
+ attrs: {
+ style: 'display:inline-block;text-align:center'
+ },
+ children: [{
+ name: 'div',
+ attrs: {
+ style: 'font-size:50%;' + (children[i + 1].attrs.style || '')
+ },
+ children: children[i + 1].children
+ }, children[i]]
+ }
+ children.splice(i + 1, 1)
+ }
+ }
+ } else if (node.c) {
+ (function traversal (node) {
+ node.c = 2
+ for (let i = node.children.length; i--;) {
+ const child = node.children[i]
+ // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+ if (child.name && (config.inlineTags[child.name] || ((child.attrs.style || '').includes('inline') && child.children)) && !child.c) {
+ traversal(child)
+ }
+ // #endif
+ if (!child.c || child.name === 'table') {
+ node.c = 1
+ }
+ }
+ })(node)
+ }
+
+ if ((styleObj.display || '').includes('flex') && !node.c) {
+ for (let i = children.length; i--;) {
+ const item = children[i]
+ if (item.f) {
+ item.attrs.style = (item.attrs.style || '') + item.f
+ item.f = undefined
+ }
+ }
+ }
+ // flex 布局时部分样式需要提取到 rich-text 外层
+ const flex = parent && ((parent.attrs.style || '').includes('flex') || (parent.attrs.style || '').includes('grid'))
+ // #ifdef MP-WEIXIN
+ // 检查基础库版本 virtualHost 是否可用
+ && !(node.c && wx.getNFCAdapter) // eslint-disable-line
+ // #endif
+ // #ifndef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
+ && !node.c // eslint-disable-line
+ // #endif
+ if (flex) {
+ node.f = ';max-width:100%'
+ }
+
+ if (children.length >= 50 && node.c && !(styleObj.display || '').includes('flex')) {
+ mergeNodes(children)
+ }
+ // #endif
+
+ for (const key in styleObj) {
+ if (styleObj[key]) {
+ const val = `;${key}:${styleObj[key].replace(' !important', '')}`
+ /* #ifndef APP-PLUS-NVUE */
+ if (flex && ((key.includes('flex') && key !== 'flex-direction') || key === 'align-self' || key.includes('grid') || styleObj[key][0] === '-' || (key.includes('width') && val.includes('%')))) {
+ node.f += val
+ if (key === 'width') {
+ attrs.style += ';width:100%'
+ }
+ } else /* #endif */ {
+ attrs.style += val
+ }
+ }
+ }
+ attrs.style = attrs.style.substr(1) || undefined
+ // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+ for (const key in attrs) {
+ if (!attrs[key]) {
+ delete attrs[key]
+ }
+ }
+ // #endif
+}
+
+/**
+ * @description 解析到文本
+ * @param {String} text 文本内容
+ */
+Parser.prototype.onText = function (text) {
+ if (!this.pre) {
+ // 合并空白符
+ let trim = ''
+ let flag
+ for (let i = 0, len = text.length; i < len; i++) {
+ if (!blankChar[text[i]]) {
+ trim += text[i]
+ } else {
+ if (trim[trim.length - 1] !== ' ') {
+ trim += ' '
+ }
+ if (text[i] === '\n' && !flag) {
+ flag = true
+ }
+ }
+ }
+ // 去除含有换行符的空串
+ if (trim === ' ') {
+ if (flag) return
+ // #ifdef VUE3
+ else {
+ const parent = this.stack[this.stack.length - 1]
+ if (parent && parent.name[0] === 't') return
+ }
+ // #endif
+ }
+ text = trim
+ }
+ const node = Object.create(null)
+ node.type = 'text'
+ // #ifdef (MP-BAIDU || MP-ALIPAY || MP-TOUTIAO) && VUE3
+ node.attrs = {}
+ // #endif
+ node.text = decodeEntity(text)
+ if (this.hook(node)) {
+ // #ifdef MP-WEIXIN
+ if (this.options.selectable === 'force' && system.includes('iOS') && !uni.canIUse('rich-text.user-select')) {
+ this.expose()
+ }
+ // #endif
+ const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+ siblings.push(node)
+ }
+}
+
+/**
+ * @description html 词法分析器
+ * @param {Object} handler 高层处理器
+ */
+function Lexer (handler) {
+ this.handler = handler
+}
+
+/**
+ * @description 执行解析
+ * @param {String} content 要解析的文本
+ */
+Lexer.prototype.parse = function (content) {
+ this.content = content || ''
+ this.i = 0 // 标记解析位置
+ this.start = 0 // 标记一个单词的开始位置
+ this.state = this.text // 当前状态
+ for (let len = this.content.length; this.i !== -1 && this.i < len;) {
+ this.state()
+ }
+}
+
+/**
+ * @description 检查标签是否闭合
+ * @param {String} method 如果闭合要进行的操作
+ * @returns {Boolean} 是否闭合
+ * @private
+ */
+Lexer.prototype.checkClose = function (method) {
+ const selfClose = this.content[this.i] === '/'
+ if (this.content[this.i] === '>' || (selfClose && this.content[this.i + 1] === '>')) {
+ if (method) {
+ this.handler[method](this.content.substring(this.start, this.i))
+ }
+ this.i += selfClose ? 2 : 1
+ this.start = this.i
+ this.handler.onOpenTag(selfClose)
+ if (this.handler.tagName === 'script') {
+ this.i = this.content.indexOf('', this.i)
+ if (this.i !== -1) {
+ this.i += 2
+ this.start = this.i
+ }
+ this.state = this.endTag
+ } else {
+ this.state = this.text
+ }
+ return true
+ }
+ return false
+}
+
+/**
+ * @description 文本状态
+ * @private
+ */
+Lexer.prototype.text = function () {
+ this.i = this.content.indexOf('<', this.i) // 查找最近的标签
+ if (this.i === -1) {
+ // 没有标签了
+ if (this.start < this.content.length) {
+ this.handler.onText(this.content.substring(this.start, this.content.length))
+ }
+ return
+ }
+ const c = this.content[this.i + 1]
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ // 标签开头
+ if (this.start !== this.i) {
+ this.handler.onText(this.content.substring(this.start, this.i))
+ }
+ this.start = ++this.i
+ this.state = this.tagName
+ } else if (c === '/' || c === '!' || c === '?') {
+ if (this.start !== this.i) {
+ this.handler.onText(this.content.substring(this.start, this.i))
+ }
+ const next = this.content[this.i + 2]
+ if (c === '/' && ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) {
+ // 标签结尾
+ this.i += 2
+ this.start = this.i
+ this.state = this.endTag
+ return
+ }
+ // 处理注释
+ let end = '-->'
+ if (c !== '!' || this.content[this.i + 2] !== '-' || this.content[this.i + 3] !== '-') {
+ end = '>'
+ }
+ this.i = this.content.indexOf(end, this.i)
+ if (this.i !== -1) {
+ this.i += end.length
+ this.start = this.i
+ }
+ } else {
+ this.i++
+ }
+}
+
+/**
+ * @description 标签名状态
+ * @private
+ */
+Lexer.prototype.tagName = function () {
+ if (blankChar[this.content[this.i]]) {
+ // 解析到标签名
+ this.handler.onTagName(this.content.substring(this.start, this.i))
+ while (blankChar[this.content[++this.i]]);
+ if (this.i < this.content.length && !this.checkClose()) {
+ this.start = this.i
+ this.state = this.attrName
+ }
+ } else if (!this.checkClose('onTagName')) {
+ this.i++
+ }
+}
+
+/**
+ * @description 属性名状态
+ * @private
+ */
+Lexer.prototype.attrName = function () {
+ let c = this.content[this.i]
+ if (blankChar[c] || c === '=') {
+ // 解析到属性名
+ this.handler.onAttrName(this.content.substring(this.start, this.i))
+ let needVal = c === '='
+ const len = this.content.length
+ while (++this.i < len) {
+ c = this.content[this.i]
+ if (!blankChar[c]) {
+ if (this.checkClose()) return
+ if (needVal) {
+ // 等号后遇到第一个非空字符
+ this.start = this.i
+ this.state = this.attrVal
+ return
+ }
+ if (this.content[this.i] === '=') {
+ needVal = true
+ } else {
+ this.start = this.i
+ this.state = this.attrName
+ return
+ }
+ }
+ }
+ } else if (!this.checkClose('onAttrName')) {
+ this.i++
+ }
+}
+
+/**
+ * @description 属性值状态
+ * @private
+ */
+Lexer.prototype.attrVal = function () {
+ const c = this.content[this.i]
+ const len = this.content.length
+ if (c === '"' || c === "'") {
+ // 有冒号的属性
+ this.start = ++this.i
+ this.i = this.content.indexOf(c, this.i)
+ if (this.i === -1) return
+ this.handler.onAttrVal(this.content.substring(this.start, this.i))
+ } else {
+ // 没有冒号的属性
+ for (; this.i < len; this.i++) {
+ if (blankChar[this.content[this.i]]) {
+ this.handler.onAttrVal(this.content.substring(this.start, this.i))
+ break
+ } else if (this.checkClose('onAttrVal')) return
+ }
+ }
+ while (blankChar[this.content[++this.i]]);
+ if (this.i < len && !this.checkClose()) {
+ this.start = this.i
+ this.state = this.attrName
+ }
+}
+
+/**
+ * @description 结束标签状态
+ * @returns {String} 结束的标签名
+ * @private
+ */
+Lexer.prototype.endTag = function () {
+ const c = this.content[this.i]
+ if (blankChar[c] || c === '>' || c === '/') {
+ this.handler.onCloseTag(this.content.substring(this.start, this.i))
+ if (c !== '>') {
+ this.i = this.content.indexOf('>', this.i)
+ if (this.i === -1) return
+ }
+ this.start = ++this.i
+ this.state = this.text
+ } else {
+ this.i++
+ }
+}
+
+export default Parser
diff --git a/uni_modules/zero-markdown-view/components/mp-html/style/index.js b/uni_modules/zero-markdown-view/components/mp-html/style/index.js
new file mode 100644
index 0000000..abfb371
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/mp-html/style/index.js
@@ -0,0 +1,129 @@
+/**
+ * @fileoverview style 插件
+ */
+// #ifndef APP-PLUS-NVUE
+import Parser from './parser'
+// #endif
+
+function Style () {
+ this.styles = []
+}
+
+// #ifndef APP-PLUS-NVUE
+Style.prototype.onParse = function (node, vm) {
+ // 获取样式
+ if (node.name === 'style' && node.children.length && node.children[0].type === 'text') {
+ this.styles = this.styles.concat(new Parser().parse(node.children[0].text))
+ } else if (node.name) {
+ // 匹配样式(对非文本标签)
+ // 存储不同优先级的样式 name < class < id < 后代
+ let matched = ['', '', '', '']
+ for (let i = 0, len = this.styles.length; i < len; i++) {
+ const item = this.styles[i]
+ let res = match(node, item.key || item.list[item.list.length - 1])
+ let j
+ if (res) {
+ // 后代选择器
+ if (!item.key) {
+ j = item.list.length - 2
+ for (let k = vm.stack.length; j >= 0 && k--;) {
+ // 子选择器
+ if (item.list[j] === '>') {
+ // 错误情况
+ if (j < 1 || j > item.list.length - 2) break
+ if (match(vm.stack[k], item.list[j - 1])) {
+ j -= 2
+ } else {
+ j++
+ }
+ } else if (match(vm.stack[k], item.list[j])) {
+ j--
+ }
+ }
+ res = 4
+ }
+ if (item.key || j < 0) {
+ // 添加伪类
+ if (item.pseudo && node.children) {
+ let text
+ item.style = item.style.replace(/content:([^;]+)/, (_, $1) => {
+ text = $1.replace(/['"]/g, '')
+ // 处理 attr 函数
+ .replace(/attr\((.+?)\)/, (_, $1) => node.attrs[$1.trim()] || '')
+ // 编码 \xxx
+ .replace(/\\(\w{4})/, (_, $1) => String.fromCharCode(parseInt($1, 16)))
+ return ''
+ })
+ const pseudo = {
+ name: 'span',
+ attrs: {
+ style: item.style
+ },
+ children: [{
+ type: 'text',
+ text
+ }]
+ }
+ if (item.pseudo === 'before') {
+ node.children.unshift(pseudo)
+ } else {
+ node.children.push(pseudo)
+ }
+ } else {
+ matched[res - 1] += item.style + (item.style[item.style.length - 1] === ';' ? '' : ';')
+ }
+ }
+ }
+ }
+ matched = matched.join('')
+ if (matched.length > 2) {
+ node.attrs.style = matched + (node.attrs.style || '')
+ }
+ }
+}
+
+/**
+ * @description 匹配样式
+ * @param {object} node 要匹配的标签
+ * @param {string|string[]} keys 选择器
+ * @returns {number} 0:不匹配;1:name 匹配;2:class 匹配;3:id 匹配
+ */
+function match (node, keys) {
+ function matchItem (key) {
+ if (key[0] === '#') {
+ // 匹配 id
+ if (node.attrs.id && node.attrs.id.trim() === key.substr(1)) return 3
+ } else if (key[0] === '.') {
+ // 匹配 class
+ key = key.substr(1)
+ const selectors = (node.attrs.class || '').split(' ')
+ for (let i = 0; i < selectors.length; i++) {
+ if (selectors[i].trim() === key) return 2
+ }
+ } else if (node.name === key) {
+ // 匹配 name
+ return 1
+ }
+ return 0
+ }
+
+ // 多选择器交集
+ if (keys instanceof Array) {
+ let res = 0
+ for (let j = 0; j < keys.length; j++) {
+ const tmp = matchItem(keys[j])
+ // 任意一个不匹配就失败
+ if (!tmp) return 0
+ // 优先级最大的一个作为最终优先级
+ if (tmp > res) {
+ res = tmp
+ }
+ }
+ return res
+ }
+
+ return matchItem(keys)
+}
+// #endif
+
+export default Style
diff --git a/uni_modules/zero-markdown-view/components/mp-html/style/parser.js b/uni_modules/zero-markdown-view/components/mp-html/style/parser.js
new file mode 100644
index 0000000..b639334
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/mp-html/style/parser.js
@@ -0,0 +1,175 @@
+const blank = {
+ ' ': true,
+ '\n': true,
+ '\t': true,
+ '\r': true,
+ '\f': true
+}
+
+function Parser () {
+ this.styles = []
+ this.selectors = []
+}
+
+/**
+ * @description 解析 css 字符串
+ * @param {string} content css 内容
+ */
+Parser.prototype.parse = function (content) {
+ new Lexer(this).parse(content)
+ return this.styles
+}
+
+/**
+ * @description 解析到一个选择器
+ * @param {string} name 名称
+ */
+Parser.prototype.onSelector = function (name) {
+ // 不支持的选择器
+ if (name.includes('[') || name.includes('*') || name.includes('@')) return
+ const selector = {}
+ // 伪类
+ if (name.includes(':')) {
+ const info = name.split(':')
+ const pseudo = info.pop()
+ if (pseudo === 'before' || pseudo === 'after') {
+ selector.pseudo = pseudo
+ name = info[0]
+ } else return
+ }
+
+ // 分割交集选择器
+ function splitItem (str) {
+ const arr = []
+ let i, start
+ for (i = 1, start = 0; i < str.length; i++) {
+ if (str[i] === '.' || str[i] === '#') {
+ arr.push(str.substring(start, i))
+ start = i
+ }
+ }
+ if (!arr.length) {
+ return str
+ } else {
+ arr.push(str.substring(start, i))
+ return arr
+ }
+ }
+
+ // 后代选择器
+ if (name.includes(' ')) {
+ selector.list = []
+ const list = name.split(' ')
+ for (let i = 0; i < list.length; i++) {
+ if (list[i].length) {
+ // 拆分子选择器
+ const arr = list[i].split('>')
+ for (let j = 0; j < arr.length; j++) {
+ selector.list.push(splitItem(arr[j]))
+ if (j < arr.length - 1) {
+ selector.list.push('>')
+ }
+ }
+ }
+ }
+ } else {
+ selector.key = splitItem(name)
+ }
+
+ this.selectors.push(selector)
+}
+
+/**
+ * @description 解析到选择器内容
+ * @param {string} content 内容
+ */
+Parser.prototype.onContent = function (content) {
+ // 并集选择器
+ for (let i = 0; i < this.selectors.length; i++) {
+ this.selectors[i].style = content
+ }
+ this.styles = this.styles.concat(this.selectors)
+ this.selectors = []
+}
+
+/**
+ * @description css 词法分析器
+ * @param {object} handler 高层处理器
+ */
+function Lexer (handler) {
+ this.selector = ''
+ this.style = ''
+ this.handler = handler
+}
+
+Lexer.prototype.parse = function (content) {
+ this.i = 0
+ this.content = content
+ this.state = this.blank
+ for (let len = content.length; this.i < len; this.i++) {
+ this.state(content[this.i])
+ }
+}
+
+Lexer.prototype.comment = function () {
+ this.i = this.content.indexOf('*/', this.i) + 1
+ if (!this.i) {
+ this.i = this.content.length
+ }
+}
+
+Lexer.prototype.blank = function (c) {
+ if (!blank[c]) {
+ if (c === '/' && this.content[this.i + 1] === '*') {
+ this.comment()
+ return
+ }
+ this.selector += c
+ this.state = this.name
+ }
+}
+
+Lexer.prototype.name = function (c) {
+ if (c === '/' && this.content[this.i + 1] === '*') {
+ this.comment()
+ return
+ }
+ if (c === '{' || c === ',' || c === ';') {
+ this.handler.onSelector(this.selector.trimEnd())
+ this.selector = ''
+ if (c !== '{') {
+ while (blank[this.content[++this.i]]);
+ }
+ if (this.content[this.i] === '{') {
+ this.floor = 1
+ this.state = this.val
+ } else {
+ this.selector += this.content[this.i]
+ }
+ } else if (blank[c]) {
+ this.selector += ' '
+ } else {
+ this.selector += c
+ }
+}
+
+Lexer.prototype.val = function (c) {
+ if (c === '/' && this.content[this.i + 1] === '*') {
+ this.comment()
+ return
+ }
+ if (c === '{') {
+ this.floor++
+ } else if (c === '}') {
+ this.floor--
+ if (!this.floor) {
+ this.handler.onContent(this.style)
+ this.style = ''
+ this.state = this.blank
+ return
+ }
+ }
+ this.style += c
+}
+
+export default Parser
diff --git a/uni_modules/zero-markdown-view/components/zero-markdown-view/zero-markdown-view.vue b/uni_modules/zero-markdown-view/components/zero-markdown-view/zero-markdown-view.vue
new file mode 100644
index 0000000..40f4fe0
--- /dev/null
+++ b/uni_modules/zero-markdown-view/components/zero-markdown-view/zero-markdown-view.vue
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/zero-markdown-view/package.json b/uni_modules/zero-markdown-view/package.json
new file mode 100644
index 0000000..4c62dd3
--- /dev/null
+++ b/uni_modules/zero-markdown-view/package.json
@@ -0,0 +1,86 @@
+{
+ "id": "zero-markdown-view",
+ "displayName": "zero-markdown-view(markdown解析)",
+ "version": "2.0.5",
+ "description": "一行代码即可实现markdown解析,支持自定义主题色,支持vue2,vue3.",
+ "keywords": [
+ "markdown",
+ "markdown解析",
+ "代码块",
+ "代码高亮",
+ "mp-html"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y",
+ "alipay": "n"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "u",
+ "app-nvue": "u"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "u",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "u",
+ "百度": "u",
+ "字节跳动": "u",
+ "QQ": "u",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/zero-markdown-view/readme.md b/uni_modules/zero-markdown-view/readme.md
new file mode 100644
index 0000000..1c93ad3
--- /dev/null
+++ b/uni_modules/zero-markdown-view/readme.md
@@ -0,0 +1,134 @@
+# zero-markdown-view
+
+
+## 一. 重要更新说明
+
+### v2.0.4
+- 新增点击代码块复制代码-仅小程序可用
+
+### v2.0.1
+- 兼容vue2,vue3
+
+### v2.0.0
+- 省去了 npm install marked
+- 省去了 npm install highlight.js
+- 使用mp-html自带的插件,重新生成uniapp包,大幅减少插件体积
+传送门: [优化思路及详细过程](https://juejin.cn/post/7160995270476431373/) https://juejin.cn/post/7160995270476431373/
+
+## 二. 使用方法
+
+**符合`easycom`组件模式, 导入 `uni_modules` 后直接使用即可 **
+
+```html
+
+
+
+
+
+
+
+
+```
+
+## 三. 参数说明
+
+|参数 |类型 |默认值 |描述 |
+|-- |-- |-- |-- |
+|markdown |String | |markdown文本 |
+|themeColor |String |'#007AFF' |主题色 |
+|codeBgColor|String |'#2d2d2d' |代码块背景色 (不建议修改) |
+
+
+
+## 四. 注意事项
+
+### 关于代码块流式输出闪烁,可以尝试 给代码块后增加 `\n`
+
+
+```javascript
+ computed: {
+ // 流式输出时代码块处理 , 这时候请使用 msgContent 传入组件中
+ msgContent() {
+ if (!this.content) {
+ return
+ }
+ let htmlString = ''
+ // 判断markdown中代码块标识符的数量是否为偶数
+ if (this.content.split("```").length % 2) {
+ let content = this.content
+ if (content[content.length - 1] != '\n') {
+ content += '\n'
+ }
+ htmlString = content
+ } else {
+ htmlString = this.content
+ }
+ return htmlString
+ }
+ },
+```
+
+
+
+### 如何关闭点击代码块复制功能?
+
+找到组件文件夹 `zero-markdown-view`-`mp-html`-`highlight`-`config.js`
+
+**把 `copyByClickCode` 改成 false 即可**
+```
+export default {
+ copyByClickCode: true, // 点击代码块复制
+ showLanguageName: true, // 是否在代码块右上角显示语言的名称
+ showLineNumber: false // 是否显示行号
+}
+```
+
+### 感谢 mp-html 插件
+
+插件地址: [https://ext.dcloud.net.cn/plugin?id=805](https://ext.dcloud.net.cn/plugin?id=805)
+
+文档地址: [https://jin-yufeng.gitee.io/mp-html/#/overview/quickstart](https://jin-yufeng.gitee.io/mp-html/#/overview/quickstart)
+
+
+插件预览:
+
+
+
+> 小程序搜索: zerojs零技术
+
+> 预览的小程序不一定能及时更新当前插件
diff --git a/util/index.js b/util/index.js
new file mode 100644
index 0000000..5c99122
--- /dev/null
+++ b/util/index.js
@@ -0,0 +1,238 @@
+import {getLocation, getUserInfo, setLocation} from "@/util/user";
+import {API, MAIN_API} from "@/config";
+import {get} from "@/api/request";
+import {areaInfoByNameReq, areaInfoReq} from "@/api/common";
+
+export const ORDER_STATUS_LIST = ['已取消', '进行中', '已完成', '申请退款中', '退款完成', '退款失败']
+
+export const CHECK_STATUS_LIST = [
+ {
+ label: '审核中',
+ color: 'info'
+ },
+ {
+ label: '审核通过',
+ color: 'success'
+ },
+ {
+ label: '审核拒绝',
+ color: 'error'
+ },
+ {
+ label: '已到账',
+ color: 'success'
+ },
+
+]
+
+export const chooseImg = (limit = 1, sourceType = ['album', 'camera']) => {
+ return new Promise(resolveMain => {
+ const {token} = getUserInfo()
+ uni.chooseImage({
+ count: limit,
+ sourceType,
+ extension: ['png', 'jpg', 'jpeg'],
+ sizeType: ['compressed'],
+ success: ({tempFiles}) => {
+ const resList = []
+ tempFiles.forEach(file => {
+ console.log('file', file)
+ const res = new Promise(resolve => {
+ uni.uploadFile({
+ url: `${MAIN_API}/api/oss/upload`,
+ name: 'file',
+ filePath: file.path,
+ header: {
+ Authorization: token,
+ TenantId: 10543
+ },
+ formData: {
+ file: file
+ },
+ success: uploadRes => {
+ const {data} = JSON.parse(uploadRes.data)
+ resolve({
+ path: data.url,
+ })
+ }
+ })
+ })
+ resList.push(res)
+ })
+ Promise.all(resList).then(result => {
+ resolveMain(result)
+ })
+ }
+ })
+ })
+}
+
+export const uploadImg = filePath => {
+ const {token} = getUserInfo()
+ uni.showLoading({title: '上传中'})
+ return new Promise(resolve => {
+ uni.uploadFile({
+ url: `${MAIN_API}/api/file/upload`,
+ name: 'file',
+ filePath,
+ header: {
+ Authorization: token,
+ TenantId: 10543
+ },
+ formData: {
+ type: 'header'
+ },
+ success: uploadRes => {
+ uni.hideLoading()
+ const {data: {url}} = JSON.parse(uploadRes.data)
+ resolve({
+ path: url,
+ })
+ }
+ })
+ })
+}
+
+export const getLocationAction = (save = true) => {
+ return new Promise((resolve, reject) => {
+ const res = getLocation()
+ if (res.area_id) resolve(res)
+ else {
+ uni.authorize({
+ scope: 'scope.userLocation',
+ success: () => {
+ uni.getLocation({
+ isHighAccuracy: true,
+ success: async ({longitude, latitude}) => {
+ const {result} = await get(`https://api.tianditu.gov.cn/geocoder?postStr={'lon':${longitude},'lat':${latitude},'ver':1}&type=geocode&tk=1099ec280e86f4e9b632173326cde90b`)
+ const areaRes = await areaInfoByNameReq({name: result.addressComponent.county}).catch(() => {
+ if (!this.areaId) reject(false)
+ })
+ if (save) setLocation({
+ longitude,
+ latitude,
+ province_str: result.addressComponent.province,
+ city_str: result.addressComponent.city,
+ area_str: result.addressComponent.county,
+ area_id: areaRes.data.id,
+ city_id: areaRes.data.fid,
+ province_id: areaRes.data.city.fid
+ })
+ resolve({
+ longitude,
+ latitude,
+ area_id: areaRes.data.id,
+ city_id: areaRes.data.fid,
+ province_id: areaRes.data.city.fid,
+ province_str: result.addressComponent.province,
+ city_str: result.addressComponent.city,
+ area_str: result.addressComponent.county,
+ })
+ },
+ fail: () => {
+ reject(false)
+ }
+ })
+ },
+ fail: () => {
+ uni.showModal({
+ title: '提示',
+ content: '为了更好的提供服务,请开启定位服务',
+ success: ({confirm}) => {
+ if (confirm) uni.openSetting()
+ }
+ })
+ }
+ })
+ }
+ })
+}
+
+export const chooseVideo = (sourceType = ['album', 'camera']) => {
+ return new Promise((resolveMain, reject) => {
+ uni.chooseVideo({
+ sourceType,
+ compressed: true,
+ maxDuration: 60,
+ success: async ({tempFilePath, duration, size}) => {
+ if (duration > 180) {
+ uni.showToast({
+ title: '暂时不支持3分钟以上的视频',
+ icon: 'none'
+ })
+ return reject(false)
+ }
+ // 50M限制
+ if (size > 50 * 1024 * 1024) {
+ uni.showToast({
+ title: '暂时不支持50M以上的视频',
+ icon: 'none'
+ })
+ return reject(false)
+ }
+ const {token} = getUserInfo()
+ uni.showLoading({title: '上传中...'})
+ uni.uploadFile({
+ url: `${MAIN_API}/api/file/upload`,
+ name: 'file',
+ filePath: tempFilePath,
+ header: {
+ Authorization: token
+ },
+ success: uploadRes => {
+ const {data} = JSON.parse(uploadRes.data)
+ uni.hideLoading()
+ resolveMain({
+ path: data.url,
+ })
+ }
+ })
+ }
+ })
+ })
+}
+
+export const getLocationOnly = () => {
+ return new Promise((resolve, reject) => {
+ uni.authorize({
+ scope: 'scope.userLocation',
+ success: () => {
+ uni.getLocation({
+ isHighAccuracy: true,
+ success: async ({longitude, latitude}) => {
+ const {result} = await get(`https://api.tianditu.gov.cn/geocoder?postStr={'lon':${longitude},'lat':${latitude},'ver':1}&type=geocode&tk=1099ec280e86f4e9b632173326cde90b`)
+ const {data} = await areaInfoByNameReq({name: result.addressComponent.county}).catch(() => {
+ if (!this.areaId) reject(false)
+ })
+ resolve({
+ longitude,
+ latitude,
+ area_id: data.id,
+ city_id: data.fid,
+ province_id: data.city.fid,
+ province_str: result.addressComponent.province,
+ city_str: result.addressComponent.city,
+ area_str: result.addressComponent.county,
+ })
+ },
+ fail: () => {
+ uni.setStorageSync("no_more_realtime_location", 1)
+ reject(false)
+ }
+ })
+ },
+ fail: err => {
+ uni.showModal({
+ title: '提示',
+ content: '为了更好的提供服务,请开启定位服务',
+ success: ({confirm}) => {
+ if (confirm) uni.openSetting()
+ }
+ })
+ }
+ })
+ })
+}
+
+
+export const getExpressMode = () => parseInt(uni.getStorageSync("express_mode"))
diff --git a/util/order.js b/util/order.js
new file mode 100644
index 0000000..343321c
--- /dev/null
+++ b/util/order.js
@@ -0,0 +1,100 @@
+import {BLUE, BLUE_LIGHT, GREEN, ORANGE, RED} from "@/config/color";
+
+// -1已取消、0待付款、1待发货/配送中、2待收货、3已完成、4退款中、5退款成功、6退款失败
+
+export const ORDER_STATUS_LIST = [
+ {
+ label: '已取消',
+ value: -1,
+ color: '#BBB',
+ },
+ {
+ label: '待付款',
+ value: 0,
+ color: RED,
+ },
+ {
+ label: '配送中',
+ value: 1,
+ color: BLUE_LIGHT,
+ },
+ {
+ label: '待收货',
+ value: 2,
+ color: BLUE,
+ },
+ {
+ label: '已完成',
+ value: 3,
+ color: GREEN,
+ },
+ {
+ label: '退款中',
+ value: 4,
+ color: '#F9A8D4',
+ },
+ {
+ label: '退款成功',
+ value: 5,
+ color: '#DB2777',
+ },
+ {
+ label: '退款失败',
+ value: 6,
+ color: '#831843',
+ },
+]
+
+export const EXPRESS_ORDER_STATUS_LIST = [
+ {
+ label: '已取消',
+ value: -1,
+ color: '#BBB',
+ },
+ {
+ label: '待支付',
+ value: 0,
+ color: RED,
+ },
+ {
+ label: '待接单',
+ value: 1,
+ color: BLUE_LIGHT,
+ },
+ {
+ label: '配送中',
+ value: 2,
+ color: BLUE,
+ },
+ {
+ label: '待确认',
+ value: 20,
+ color: BLUE,
+ },
+ {
+ label: '已完成',
+ value: 3,
+ color: GREEN,
+ },
+ {
+ label: '退款中',
+ value: 4,
+ color: '#F9A8D4',
+ },
+ {
+ label: '退款成功',
+ value: 5,
+ color: '#DB2777',
+ },
+ {
+ label: '退款失败',
+ value: 6,
+ color: '#831843',
+ },
+]
+
+export const getOrderStatusItem = (type, orderStatus) => {
+ if ([1, 2, 3, 4, 5].includes(type))
+ return EXPRESS_ORDER_STATUS_LIST.filter(item => item.value === orderStatus)[0]
+ else return ORDER_STATUS_LIST.filter(item => item.value === orderStatus)[0]
+}
\ No newline at end of file
diff --git a/util/user.js b/util/user.js
new file mode 100644
index 0000000..50ef413
--- /dev/null
+++ b/util/user.js
@@ -0,0 +1,51 @@
+import {MAIN_API} from "@/config";
+
+export const setLocation = ({latitude, longitude, province_str, city_str, area_str, area_id, city_id, province_id}) => {
+ if (latitude) uni.setStorageSync("latitude", latitude)
+ if (longitude) uni.setStorageSync("longitude", longitude)
+ uni.setStorageSync("province_str", province_str)
+ uni.setStorageSync("city_str", city_str)
+ uni.setStorageSync("area_str", area_str)
+ uni.setStorageSync("area_id", area_id)
+ uni.setStorageSync("city_id", city_id)
+ uni.setStorageSync("province_id", province_id)
+}
+
+export const getLocation = () => {
+ return {
+ latitude: uni.getStorageSync("latitude"),
+ longitude: uni.getStorageSync("longitude"),
+ province_str: uni.getStorageSync("province_str"),
+ city_str: uni.getStorageSync("city_str"),
+ area_str: uni.getStorageSync("area_str"),
+ area_id: uni.getStorageSync("area_id"),
+ city_id: uni.getStorageSync("city_id"),
+ province_id: uni.getStorageSync("province_id")
+ }
+}
+
+export const setUserInfo = (user) => {
+ if (user.uid) uni.setStorageSync("uid", user.uid)
+ if (user.username) uni.setStorageSync("username", user.username)
+ if (user.token) uni.setStorageSync("token", user.token)
+ if (user.header) uni.setStorageSync("header", `${MAIN_API}${user.header}`)
+ if (user.phone) uni.setStorageSync("phone", user.phone)
+}
+
+export const getUserInfo = () => {
+ return {
+ uid: uni.getStorageSync("uid"),
+ username: uni.getStorageSync("username"),
+ token: uni.getStorageSync("token"),
+ header: uni.getStorageSync("header"),
+ phone: uni.getStorageSync("phone"),
+ }
+}
+
+export const clearUserInfo = () => {
+ uni.removeStorageSync("uid");
+ uni.removeStorageSync("username");
+ uni.removeStorageSync("token");
+ uni.removeStorageSync("header");
+ uni.removeStorageSync("phone");
+}
\ No newline at end of file