\n"+r+""+s+">\n"}listitem(e){let t="";if(e.task){let n=this.checkbox({checked:!!e.checked});e.loose?"paragraph"===e.tokens[0]?.type?(e.tokens[0].text=n+" "+e.tokens[0].text,e.tokens[0].tokens&&e.tokens[0].tokens.length>0&&"text"===e.tokens[0].tokens[0].type&&(e.tokens[0].tokens[0].text=n+" "+V(e.tokens[0].tokens[0].text),e.tokens[0].tokens[0].escaped=!0)):e.tokens.unshift({type:"text",raw:n+" ",text:n+" ",escaped:!0}):t+=n+" "}return t+=this.parser.parse(e.tokens,!!e.loose),`${t}\n`}checkbox({checked:e}){return"'}paragraph({tokens:e}){return`${this.parser.parseInline(e)}
\n`}table(e){let t="",n="";for(let t=0;t${r}`),"\n"}tablerow({text:e}){return`\n${e}
\n`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`${n}>\n`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${V(e,!0)}`}br(e){return"
"}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let r=this.parser.parseInline(n),s=Y(e);if(null===s)return r;let l='"+r+"",l}image({href:e,title:t,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let s=Y(e);if(null===s)return V(n);let l=`
",l}text(e){return"tokens"in e&&e.tokens?this.parser.parseInline(e.tokens):"escaped"in e&&e.escaped?e.text:V(e.text)}},ie=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return""+e}image({text:e}){return""+e}br(){return""}},ae=class e{options;renderer;textRenderer;constructor(e){this.options=e||t,this.options.renderer=this.options.renderer||new le,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new ie}static parse(t,n){return new e(n).parse(t)}static parseInline(t,n){return new e(n).parseInline(t)}parse(e,t=!0){let n="";for(let r=0;r{let s=e[r].flat(1/0);n=n.concat(this.walkTokens(s,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{let n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){let n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let r=e.renderer.apply(this,t);return!1===r&&(r=n.apply(this,t)),r}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");let n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){let t=this.defaults.renderer||new le(this.defaults);for(let n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if(["options","parser"].includes(n))continue;let r=n,s=e.renderer[r],l=t[r];t[r]=(...e)=>{let n=s.apply(t,e);return!1===n&&(n=l.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){let t=this.defaults.tokenizer||new re(this.defaults);for(let n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;let r=n,s=e.tokenizer[r],l=t[r];t[r]=(...e)=>{let n=s.apply(t,e);return!1===n&&(n=l.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){let t=this.defaults.hooks||new oe;for(let n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if(["options","block"].includes(n))continue;let r=n,s=e.hooks[r],l=t[r];oe.passThroughHooks.has(n)?t[r]=e=>{if(this.defaults.async)return Promise.resolve(s.call(t,e)).then((e=>l.call(t,e)));let n=s.call(t,e);return l.call(t,n)}:t[r]=(...e)=>{let n=s.apply(t,e);return!1===n&&(n=l.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){let t=this.defaults.walkTokens,r=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(r.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return se.lex(e,t??this.defaults)}parser(e,t){return ae.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{let r={...n},s={...this.defaults,...r},l=this.onError(!!s.silent,!!s.async);if(!0===this.defaults.async&&!1===r.async)return l(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof t>"u"||null===t)return l(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof t)return l(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));s.hooks&&(s.hooks.options=s,s.hooks.block=e);let i=s.hooks?s.hooks.provideLexer():e?se.lex:se.lexInline,a=s.hooks?s.hooks.provideParser():e?ae.parse:ae.parseInline;if(s.async)return Promise.resolve(s.hooks?s.hooks.preprocess(t):t).then((e=>i(e,s))).then((e=>s.hooks?s.hooks.processAllTokens(e):e)).then((e=>s.walkTokens?Promise.all(this.walkTokens(e,s.walkTokens)).then((()=>e)):e)).then((e=>a(e,s))).then((e=>s.hooks?s.hooks.postprocess(e):e)).catch(l);try{s.hooks&&(t=s.hooks.preprocess(t));let e=i(t,s);s.hooks&&(e=s.hooks.processAllTokens(e)),s.walkTokens&&this.walkTokens(e,s.walkTokens);let n=a(e,s);return s.hooks&&(n=s.hooks.postprocess(n)),n}catch(e){return l(e)}}}onError(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){let e="An error occurred:
"+V(n.message+"",!0)+"
";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}},he=new ce;function pe(e,t){return he.parse(e,t)}pe.options=pe.setOptions=function(e){return he.setOptions(e),pe.defaults=he.defaults,n(pe.defaults),pe},pe.getDefaults=e,pe.defaults=t,pe.use=function(...e){return he.use(...e),pe.defaults=he.defaults,n(pe.defaults),pe},pe.walkTokens=function(e,t){return he.walkTokens(e,t)},pe.parseInline=he.parseInline,pe.Parser=ae,pe.parser=ae.parse,pe.Renderer=le,pe.TextRenderer=ie,pe.Lexer=se,pe.lexer=se.lex,pe.Tokenizer=re,pe.Hooks=oe,pe.parse=pe;var ue=pe.options,ge=pe.setOptions,ke=pe.use,de=pe.walkTokens,fe=pe.parseInline,xe=pe,be=ae.parse,we=se.lex;export{oe as Hooks,se as Lexer,ce as Marked,ae as Parser,le as Renderer,ie as TextRenderer,re as Tokenizer,t as defaults,e as getDefaults,we as lexer,pe as marked,ue as options,xe as parse,fe as parseInline,be as parser,ge as setOptions,ke as use,de as walkTokens};export default null;
diff --git a/uni_modules/uview-plus/components/u-markdown/u-markdown.vue b/uni_modules/uview-plus/components/u-markdown/u-markdown.vue
new file mode 100644
index 0000000..8312b88
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-markdown/u-markdown.vue
@@ -0,0 +1,300 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-message-input/u-message-input.vue b/uni_modules/uview-plus/components/u-message-input/u-message-input.vue
new file mode 100644
index 0000000..0da5f1c
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-message-input/u-message-input.vue
@@ -0,0 +1,318 @@
+
+
+
+
+
+
+
+
+
+ {{ charArr[index] ? charArr[index] : ''}}
+
+ {{ charArr[index] ? '●' : ''}}
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-modal/modal.js b/uni_modules/uview-plus/components/u-modal/modal.js
new file mode 100644
index 0000000..59d8abc
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-modal/modal.js
@@ -0,0 +1,36 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/modal.js
+ */
+import { t } from '../../libs/i18n'
+export default {
+ // modal 组件
+ modal: {
+ show: false,
+ title: '',
+ content: '',
+ confirmText: t("up.common.confirm"),
+ cancelText: t("up.common.cancel"),
+ showConfirmButton: true,
+ showCancelButton: false,
+ confirmColor: '#2979ff',
+ cancelColor: '#606266',
+ buttonReverse: false,
+ zoom: true,
+ asyncClose: false,
+ closeOnClickOverlay: false,
+ negativeTop: 0,
+ width: '650rpx',
+ confirmButtonShape: '',
+ duration: 400,
+ contentTextAlign: 'left',
+ asyncCloseTip: t("up.common.inOperation") + '...',
+ asyncCancelClose: false,
+ contentStyle: {}
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-modal/props.js b/uni_modules/uview-plus/components/u-modal/props.js
new file mode 100644
index 0000000..0f0177b
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-modal/props.js
@@ -0,0 +1,111 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 是否展示modal
+ show: {
+ type: Boolean,
+ default: () => defProps.modal.show
+ },
+ // 标题
+ title: {
+ type: [String],
+ default: () => defProps.modal.title
+ },
+ // 弹窗内容
+ content: {
+ type: String,
+ default: () => defProps.modal.content
+ },
+ // 确认文案
+ confirmText: {
+ type: String,
+ default: () => defProps.modal.confirmText
+ },
+ // 取消文案
+ cancelText: {
+ type: String,
+ default: () => defProps.modal.cancelText
+ },
+ // 是否显示确认按钮
+ showConfirmButton: {
+ type: Boolean,
+ default: () => defProps.modal.showConfirmButton
+ },
+ // 是否显示取消按钮
+ showCancelButton: {
+ type: Boolean,
+ default: () => defProps.modal.showCancelButton
+ },
+ // 确认按钮颜色
+ confirmColor: {
+ type: String,
+ default: () => defProps.modal.confirmColor
+ },
+ // 取消文字颜色
+ cancelColor: {
+ type: String,
+ default: () => defProps.modal.cancelColor
+ },
+ // 对调确认和取消的位置
+ buttonReverse: {
+ type: Boolean,
+ default: () => defProps.modal.buttonReverse
+ },
+ // 是否开启缩放效果
+ zoom: {
+ type: Boolean,
+ default: () => defProps.modal.zoom
+ },
+ // 是否异步关闭,只对确定按钮有效
+ asyncClose: {
+ type: Boolean,
+ default: () => defProps.modal.asyncClose
+ },
+ // 是否允许点击遮罩关闭modal
+ closeOnClickOverlay: {
+ type: Boolean,
+ default: () => defProps.modal.closeOnClickOverlay
+ },
+ // 给一个负的margin-top,往上偏移,避免和键盘重合的情况
+ negativeTop: {
+ type: [String, Number],
+ default: () => defProps.modal.negativeTop
+ },
+ // modal宽度,不支持百分比,可以数值,px,rpx单位
+ width: {
+ type: [String, Number],
+ default: () => defProps.modal.width
+ },
+ // 确认按钮的样式,circle-圆形,square-方形,如设置,将不会显示取消按钮
+ confirmButtonShape: {
+ type: String,
+ default: () => defProps.modal.confirmButtonShape
+ },
+ // 弹窗动画过度时间
+ duration: {
+ type: [Number],
+ default: defProps.modal.duration
+ },
+ // 文案对齐方式
+ contentTextAlign: {
+ type: String,
+ default: () => defProps.modal.contentTextAlign
+ },
+ // 异步确定时如果点击了取消时候的提示文案
+ asyncCloseTip: {
+ type: String,
+ default: () => defProps.modal.asyncCloseTip
+ },
+ // 是否异步关闭,只对取消按钮有效
+ asyncCancelClose: {
+ type: Boolean,
+ default: () => defProps.modal.asyncCancelClose
+ },
+ // 内容样式
+ contentStyle: {
+ type: Object,
+ default: () => defProps.modal.contentStyle
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-modal/u-modal.vue b/uni_modules/uview-plus/components/u-modal/u-modal.vue
new file mode 100644
index 0000000..eab8f72
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-modal/u-modal.vue
@@ -0,0 +1,265 @@
+
+
+
+ {{ title }}
+
+
+
+ {{ content }}
+
+
+
+
+
+
+
+
+
+
+ {{ cancelText }}
+
+
+
+
+ {{ confirmText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-navbar-mini/props.js b/uni_modules/uview-plus/components/u-navbar-mini/props.js
new file mode 100644
index 0000000..57d5b4e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-navbar-mini/props.js
@@ -0,0 +1,51 @@
+import { defineMixin } from '../../libs/vue'
+
+export const props = defineMixin({
+ props: {
+ // 是否开启顶部安全区适配
+ safeAreaInsetTop: {
+ type: Boolean,
+ default: () => true
+ },
+ // 是否固定在顶部
+ fixed: {
+ type: Boolean,
+ default: () => true
+ },
+ // 左边的图标
+ leftIcon: {
+ type: String,
+ default: 'arrow-leftward'
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: () => 'rgba(0,0,0,.15)'
+ },
+ // 导航栏高度
+ height: {
+ type: [String, Number],
+ default: () => '32px'
+ },
+ // 图标的大小
+ iconSize: {
+ type: [String, Number],
+ default: '20px'
+ },
+ // 图标的颜色
+ iconColor: {
+ type: String,
+ default: '#fff'
+ },
+ // 点击左侧区域(返回图标),是否自动返回上一页
+ autoBack: {
+ type: Boolean,
+ default: () => true
+ },
+ // 首页路径
+ homeUrl: {
+ type: [String],
+ default: ''
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-navbar-mini/u-navbar-mini.vue b/uni_modules/uview-plus/components/u-navbar-mini/u-navbar-mini.vue
new file mode 100644
index 0000000..be546d7
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-navbar-mini/u-navbar-mini.vue
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-navbar/navbar.js b/uni_modules/uview-plus/components/u-navbar/navbar.js
new file mode 100644
index 0000000..e268b35
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-navbar/navbar.js
@@ -0,0 +1,33 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/navbar.js
+ */
+import color from '../../libs/config/color'
+export default {
+ // navbar 组件
+ navbar: {
+ safeAreaInsetTop: true,
+ placeholder: false,
+ fixed: true,
+ border: false,
+ leftIcon: 'arrow-left',
+ leftText: '',
+ rightText: '',
+ rightIcon: '',
+ title: '',
+ titleColor: '',
+ bgColor: '#ffffff',
+ titleWidth: '400rpx',
+ height: '44px',
+ leftIconSize: 20,
+ leftIconColor: color.mainColor,
+ autoBack: false,
+ titleStyle: ''
+ }
+
+}
diff --git a/uni_modules/uview-plus/components/u-navbar/props.js b/uni_modules/uview-plus/components/u-navbar/props.js
new file mode 100644
index 0000000..b66f87e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-navbar/props.js
@@ -0,0 +1,97 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+ props: {
+ // 是否开启顶部安全区适配
+ safeAreaInsetTop: {
+ type: Boolean,
+ default: () => defProps.navbar.safeAreaInsetTop
+ },
+ // 固定在顶部时,是否生成一个等高元素,以防止塌陷
+ placeholder: {
+ type: Boolean,
+ default: () => defProps.navbar.placeholder
+ },
+ // 是否固定在顶部
+ fixed: {
+ type: Boolean,
+ default: () => defProps.navbar.fixed
+ },
+ // 是否显示下边框
+ border: {
+ type: Boolean,
+ default: () => defProps.navbar.border
+ },
+ // 左边的图标
+ leftIcon: {
+ type: String,
+ default: () => defProps.navbar.leftIcon
+ },
+ // 左边的提示文字
+ leftText: {
+ type: String,
+ default: () => defProps.navbar.leftText
+ },
+ // 左右的提示文字
+ rightText: {
+ type: String,
+ default: () => defProps.navbar.rightText
+ },
+ // 右边的图标
+ rightIcon: {
+ type: String,
+ default: () => defProps.navbar.rightIcon
+ },
+ // 标题
+ title: {
+ type: [String, Number],
+ default: () => defProps.navbar.title
+ },
+ // 标题颜色
+ titleColor: {
+ type: String,
+ default: () => defProps.navbar.titleColor
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: () => defProps.navbar.bgColor
+ },
+ // 状态栏背景颜色 不写会使用背景颜色bgColor
+ statusBarBgColor: {
+ type: String,
+ default: () => ''
+ },
+ // 标题的宽度
+ titleWidth: {
+ type: [String, Number],
+ default: () => defProps.navbar.titleWidth
+ },
+ // 导航栏高度
+ height: {
+ type: [String, Number],
+ default: () => defProps.navbar.height
+ },
+ // 左侧返回图标的大小
+ leftIconSize: {
+ type: [String, Number],
+ default: () => defProps.navbar.leftIconSize
+ },
+ // 左侧返回图标的颜色
+ leftIconColor: {
+ type: String,
+ default: () => defProps.navbar.leftIconColor
+ },
+ // 点击左侧区域(返回图标),是否自动返回上一页
+ autoBack: {
+ type: Boolean,
+ default: () => defProps.navbar.autoBack
+ },
+ // 标题的样式,对象或字符串
+ titleStyle: {
+ type: [String, Object],
+ default: () => defProps.navbar.titleStyle
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-navbar/u-navbar.vue b/uni_modules/uview-plus/components/u-navbar/u-navbar.vue
new file mode 100644
index 0000000..08945f2
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-navbar/u-navbar.vue
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+ {{ leftText }}
+
+
+
+ {{ title }}
+
+
+
+
+ {{ rightText }}
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-no-network/noNetwork.js b/uni_modules/uview-plus/components/u-no-network/noNetwork.js
new file mode 100644
index 0000000..96bb796
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-no-network/noNetwork.js
@@ -0,0 +1,18 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/noNetwork.js
+ */
+import { t } from '../../libs/i18n'
+export default {
+ // noNetwork
+ noNetwork: {
+ tips: t("up.noNetwork.text"),
+ zIndex: '',
+ image: 'data:image/png;base64,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'
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-no-network/props.js b/uni_modules/uview-plus/components/u-no-network/props.js
new file mode 100644
index 0000000..1091cf5
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-no-network/props.js
@@ -0,0 +1,21 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 页面文字提示
+ tips: {
+ type: String,
+ default: () => defProps.noNetwork.tips
+ },
+ // 一个z-index值,用于设置没有网络这个组件的层次,因为页面可能会有其他定位的元素层级过高,导致此组件被覆盖
+ zIndex: {
+ type: [String, Number],
+ default: () => defProps.noNetwork.zIndex
+ },
+ // image 没有网络的图片提示
+ image: {
+ type: String,
+ default: () => defProps.noNetwork.image
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-no-network/u-no-network.vue b/uni_modules/uview-plus/components/u-no-network/u-no-network.vue
new file mode 100644
index 0000000..e9dd03b
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-no-network/u-no-network.vue
@@ -0,0 +1,228 @@
+
+
+
+
+ {{tips}}
+
+
+
+ {{ t("up.noNetwork.pleaseCheck") }}
+ {{ t("up.common.settings") }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-notice-bar/noticeBar.js b/uni_modules/uview-plus/components/u-notice-bar/noticeBar.js
new file mode 100644
index 0000000..59e930b
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-notice-bar/noticeBar.js
@@ -0,0 +1,28 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/noticeBar.js
+ */
+export default {
+ // noticeBar
+ noticeBar: {
+ text: [],
+ direction: 'row',
+ step: false,
+ icon: 'volume',
+ mode: '',
+ color: '#f9ae3d',
+ bgColor: '#fdf6ec',
+ speed: 80,
+ fontSize: 14,
+ duration: 2000,
+ disableTouch: true,
+ url: '',
+ linkType: 'navigateTo',
+ justifyContent: 'flex-start'
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-notice-bar/props.js b/uni_modules/uview-plus/components/u-notice-bar/props.js
new file mode 100644
index 0000000..2dd91af
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-notice-bar/props.js
@@ -0,0 +1,76 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 显示的内容,数组
+ text: {
+ type: [Array, String],
+ default: () => defProps.noticeBar.text
+ },
+ // 通告滚动模式,row-横向滚动,column-竖向滚动
+ direction: {
+ type: String,
+ default: () => defProps.noticeBar.direction
+ },
+ // direction = row时,是否使用步进形式滚动
+ step: {
+ type: Boolean,
+ default: () => defProps.noticeBar.step
+ },
+ // 是否显示左侧的音量图标
+ icon: {
+ type: String,
+ default: () => defProps.noticeBar.icon
+ },
+ // 通告模式,link-显示右箭头,closable-显示右侧关闭图标
+ mode: {
+ type: String,
+ default: () => defProps.noticeBar.mode
+ },
+ // 文字颜色,各图标也会使用文字颜色
+ color: {
+ type: String,
+ default: () => defProps.noticeBar.color
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: () => defProps.noticeBar.bgColor
+ },
+ // 水平滚动时的滚动速度,即每秒滚动多少px(px),这有利于控制文字无论多少时,都能有一个恒定的速度
+ speed: {
+ type: [String, Number],
+ default: () => defProps.noticeBar.speed
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: () => defProps.noticeBar.fontSize
+ },
+ // 滚动一个周期的时间长,单位ms
+ duration: {
+ type: [String, Number],
+ default: () => defProps.noticeBar.duration
+ },
+ // 是否禁止用手滑动切换
+ // 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序
+ disableTouch: {
+ type: Boolean,
+ default: () => defProps.noticeBar.disableTouch
+ },
+ // 跳转的页面路径
+ url: {
+ type: String,
+ default: () => defProps.noticeBar.url
+ },
+ // 页面跳转的类型
+ linkType: {
+ type: String,
+ default: () => defProps.noticeBar.linkType
+ },
+ justifyContent: {
+ type: String,
+ default: () => defProps.noticeBar.justifyContent
+ },
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-notice-bar/u-notice-bar.vue b/uni_modules/uview-plus/components/u-notice-bar/u-notice-bar.vue
new file mode 100644
index 0000000..32de362
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-notice-bar/u-notice-bar.vue
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-notify/notify.js b/uni_modules/uview-plus/components/u-notify/notify.js
new file mode 100644
index 0000000..142760e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-notify/notify.js
@@ -0,0 +1,22 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/notify.js
+ */
+export default {
+ // notify组件
+ notify: {
+ top: 0,
+ type: 'primary',
+ color: '#ffffff',
+ bgColor: '',
+ message: '',
+ duration: 3000,
+ fontSize: 15,
+ safeAreaInsetTop: false
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-notify/props.js b/uni_modules/uview-plus/components/u-notify/props.js
new file mode 100644
index 0000000..fdb01b4
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-notify/props.js
@@ -0,0 +1,51 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 到顶部的距离
+ top: {
+ type: [String, Number],
+ default: () => defProps.notify.top
+ },
+ // 是否展示组件
+ // show: {
+ // type: Boolean,
+ // default: () => defProps.notify.show
+ // },
+ // type主题,primary,success,warning,error
+ type: {
+ type: String,
+ default: () => defProps.notify.type
+ },
+ // 字体颜色
+ color: {
+ type: String,
+ default: () => defProps.notify.color
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: () => defProps.notify.bgColor
+ },
+ // 展示的文字内容
+ message: {
+ type: String,
+ default: () => defProps.notify.message
+ },
+ // 展示时长,为0时不消失,单位ms
+ duration: {
+ type: [String, Number],
+ default: () => defProps.notify.duration
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: () => defProps.notify.fontSize
+ },
+ // 是否留出顶部安全距离(状态栏高度)
+ safeAreaInsetTop: {
+ type: Boolean,
+ default: () => defProps.notify.safeAreaInsetTop
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-notify/u-notify.vue b/uni_modules/uview-plus/components/u-notify/u-notify.vue
new file mode 100644
index 0000000..2970f4d
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-notify/u-notify.vue
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+ {{ tmpConfig.message }}
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-number-box/numberBox.js b/uni_modules/uview-plus/components/u-number-box/numberBox.js
new file mode 100644
index 0000000..8d79ab7
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-number-box/numberBox.js
@@ -0,0 +1,40 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/numberBox.js
+ */
+export default {
+ // 步进器组件
+ numberBox: {
+ name: '',
+ value: 0,
+ min: 1,
+ max: Number.MAX_SAFE_INTEGER,
+ step: 1,
+ integer: false,
+ disabled: false,
+ disabledInput: false,
+ asyncChange: false,
+ inputWidth: 35,
+ showMinus: true,
+ showPlus: true,
+ decimalLength: null,
+ longPress: true,
+ color: '#323233',
+ buttonWidth: 30,
+ buttonSize: 30,
+ buttonRadius: '0px',
+ bgColor: '#EBECEE',
+ disabledBgColor: '#f7f8fa',
+ inputBgColor: '#EBECEE',
+ cursorSpacing: 100,
+ disableMinus: false,
+ disablePlus: false,
+ iconStyle: '',
+ miniMode: false
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-number-box/props.js b/uni_modules/uview-plus/components/u-number-box/props.js
new file mode 100644
index 0000000..ada9952
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-number-box/props.js
@@ -0,0 +1,145 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 步进器标识符,在change回调返回
+ name: {
+ type: [String, Number],
+ default: () => defProps.numberBox.name
+ },
+ // #ifdef VUE2
+ // 用于双向绑定的值,初始化时设置设为默认min值(最小值)
+ value: {
+ type: [String, Number],
+ default: () => defProps.numberBox.value
+ },
+ // #endif
+ // #ifdef VUE3
+ // 用于双向绑定的值,初始化时设置设为默认min值(最小值)
+ modelValue: {
+ type: [String, Number],
+ default: () => defProps.numberBox.value
+ },
+ // #endif
+ // 最小值
+ min: {
+ type: [String, Number],
+ default: () => defProps.numberBox.min
+ },
+ // 最大值
+ max: {
+ type: [String, Number],
+ default: () => defProps.numberBox.max
+ },
+ // 加减的步长,可为小数
+ step: {
+ type: [String, Number],
+ default: () => defProps.numberBox.step
+ },
+ // 是否只允许输入整数
+ integer: {
+ type: Boolean,
+ default: () => defProps.numberBox.integer
+ },
+ // 是否禁用,包括输入框,加减按钮
+ disabled: {
+ type: Boolean,
+ default: () => defProps.numberBox.disabled
+ },
+ // 是否禁用输入框
+ disabledInput: {
+ type: Boolean,
+ default: () => defProps.numberBox.disabledInput
+ },
+ // 是否开启异步变更,开启后需要手动控制输入值
+ asyncChange: {
+ type: Boolean,
+ default: () => defProps.numberBox.asyncChange
+ },
+ // 输入框宽度,单位为px
+ inputWidth: {
+ type: [String, Number],
+ default: () => defProps.numberBox.inputWidth
+ },
+ // 是否显示减少按钮
+ showMinus: {
+ type: Boolean,
+ default: () => defProps.numberBox.showMinus
+ },
+ // 是否显示增加按钮
+ showPlus: {
+ type: Boolean,
+ default: () => defProps.numberBox.showPlus
+ },
+ // 显示的小数位数
+ decimalLength: {
+ type: [String, Number, null],
+ default: () => defProps.numberBox.decimalLength
+ },
+ // 是否开启长按加减手势
+ longPress: {
+ type: Boolean,
+ default: () => defProps.numberBox.longPress
+ },
+ // 输入框文字和加减按钮图标的颜色
+ color: {
+ type: String,
+ default: () => defProps.numberBox.color
+ },
+ // 按钮宽度
+ buttonWidth: {
+ type: [String, Number],
+ default: () => defProps.numberBox.buttonWidth
+ },
+ // 按钮大小,宽高等于此值,单位px,输入框高度和此值保持一致
+ buttonSize: {
+ type: [String, Number],
+ default: () => defProps.numberBox.buttonSize
+ },
+ // 按钮圆角
+ buttonRadius: {
+ type: [String],
+ default: () => defProps.numberBox.buttonRadius
+ },
+ // 输入框和按钮的背景颜色
+ bgColor: {
+ type: String,
+ default: () => defProps.numberBox.bgColor
+ },
+ // 按钮禁用背景色
+ disabledBgColor: {
+ type: String,
+ default: () => defProps.numberBox.disabledBgColor
+ },
+ // 输入框背景颜色
+ inputBgColor: {
+ type: String,
+ default: () => defProps.numberBox.inputBgColor
+ },
+ // 指定光标于键盘的距离,避免键盘遮挡输入框,单位px
+ cursorSpacing: {
+ type: [String, Number],
+ default: () => defProps.numberBox.cursorSpacing
+ },
+ // 是否禁用增加按钮
+ disablePlus: {
+ type: Boolean,
+ default: () => defProps.numberBox.disablePlus
+ },
+ // 是否禁用减少按钮
+ disableMinus: {
+ type: Boolean,
+ default: () => defProps.numberBox.disableMinus
+ },
+ // 加减按钮图标的样式
+ iconStyle: {
+ type: [Object, String],
+ default: () => defProps.numberBox.iconStyle
+ },
+ // 迷你模式
+ miniMode: {
+ type: Boolean,
+ default: () => defProps.numberBox.miniMode
+ },
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-number-box/u-number-box.vue b/uni_modules/uview-plus/components/u-number-box/u-number-box.vue
new file mode 100644
index 0000000..09ae4f5
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-number-box/u-number-box.vue
@@ -0,0 +1,478 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-number-keyboard/numberKeyboard.js b/uni_modules/uview-plus/components/u-number-keyboard/numberKeyboard.js
new file mode 100644
index 0000000..f94be24
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-number-keyboard/numberKeyboard.js
@@ -0,0 +1,17 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/numberKeyboard.js
+ */
+export default {
+ // 数字键盘
+ numberKeyboard: {
+ mode: 'number',
+ dotDisabled: false,
+ random: false
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-number-keyboard/props.js b/uni_modules/uview-plus/components/u-number-keyboard/props.js
new file mode 100644
index 0000000..d853858
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-number-keyboard/props.js
@@ -0,0 +1,21 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 键盘的类型,number-数字键盘,card-身份证键盘
+ mode: {
+ type: String,
+ default: () => defProps.numberKeyboard.value
+ },
+ // 是否显示键盘的"."符号
+ dotDisabled: {
+ type: Boolean,
+ default: () => defProps.numberKeyboard.dotDisabled
+ },
+ // 是否打乱键盘按键的顺序
+ random: {
+ type: Boolean,
+ default: () => defProps.numberKeyboard.random
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-number-keyboard/u-number-keyboard.vue b/uni_modules/uview-plus/components/u-number-keyboard/u-number-keyboard.vue
new file mode 100644
index 0000000..2290793
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-number-keyboard/u-number-keyboard.vue
@@ -0,0 +1,198 @@
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-overlay/overlay.js b/uni_modules/uview-plus/components/u-overlay/overlay.js
new file mode 100644
index 0000000..89f1da3
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-overlay/overlay.js
@@ -0,0 +1,18 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/overlay.js
+ */
+export default {
+ // overlay组件
+ overlay: {
+ show: false,
+ zIndex: 10070,
+ duration: 300,
+ opacity: 0.5
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-overlay/props.js b/uni_modules/uview-plus/components/u-overlay/props.js
new file mode 100644
index 0000000..39f16f3
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-overlay/props.js
@@ -0,0 +1,26 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 是否显示遮罩
+ show: {
+ type: Boolean,
+ default: () => defProps.overlay.show
+ },
+ // 层级z-index
+ zIndex: {
+ type: [String, Number],
+ default: () => defProps.overlay.zIndex
+ },
+ // 遮罩的过渡时间,单位为ms
+ duration: {
+ type: [String, Number],
+ default: () => defProps.overlay.duration
+ },
+ // 不透明度值,当做rgba的第四个参数
+ opacity: {
+ type: [String, Number],
+ default: () => defProps.overlay.opacity
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-overlay/u-overlay.vue b/uni_modules/uview-plus/components/u-overlay/u-overlay.vue
new file mode 100644
index 0000000..c2c007b
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-overlay/u-overlay.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-pagination/u-pagination.vue b/uni_modules/uview-plus/components/u-pagination/u-pagination.vue
new file mode 100644
index 0000000..b256aa6
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-pagination/u-pagination.vue
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-parse/node/node.vue b/uni_modules/uview-plus/components/u-parse/node/node.vue
new file mode 100644
index 0000000..bc5b262
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-parse/node/node.vue
@@ -0,0 +1,600 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{n.text}}
+
+
+ {{n.text}}
+
+ {{'\n'}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-parse/parse.js b/uni_modules/uview-plus/components/u-parse/parse.js
new file mode 100644
index 0000000..a70dabe
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-parse/parse.js
@@ -0,0 +1,22 @@
+/*
+ * @Author : jry
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-05-17 17:17:33
+ * @FilePath : /uview-plus/libs/config/props/parse.js
+ */
+export default {
+ // parse
+ parse: {
+ copyLink: true,
+ errorImg: '',
+ lazyLoad: false,
+ loadingImg: '',
+ pauseVideo: true,
+ previewImg: true,
+ setTitle: true,
+ showImgMenu: true
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-parse/parser.js b/uni_modules/uview-plus/components/u-parse/parser.js
new file mode 100644
index 0000000..e8fc979
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-parse/parser.js
@@ -0,0 +1,1400 @@
+/**
+ * @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',
+ foreignobject: 'foreignObject'
+ }
+}
+const tagSelector = {}
+let windowWidth, system
+// #ifdef MP-WEIXIN
+if (uni.canIUse('getWindowInfo')) {
+ windowWidth = uni.getWindowInfo().windowWidth
+ system = uni.getDeviceInfo().system
+} else {
+// #endif
+ const systemInfo = uni.getSystemInfoSync()
+ windowWidth = systemInfo.windowWidth
+ // #ifdef MP-WEIXIN
+ system = systemInfo.system
+}
+// #endif
+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()
+ // #ifdef (VUE3 && (H5 || APP-PLUS)) || APP-PLUS-NVUE
+ if (name.includes('?') || name.includes(';')) {
+ this.attrName = undefined
+ return
+ }
+ // #endif
+ 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:') && this.options.previewImg !== 'all' && !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'
+ }
+ if (node.w && node.h && styleObj['object-fit']) {
+ if (styleObj['object-fit'] === 'contain') {
+ node.m = 'aspectFit'
+ } else if (styleObj['object-fit'] === 'cover') {
+ node.m = 'aspectFill'
+ }
+ }
+ } 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
+ if (name === 'foreignObject') {
+ for (const child of (node.children || [])) {
+ if (child.attrs && !child.attrs.xmlns) {
+ child.attrs.xmlns = 'http://www.w3.org/1999/xhtml'
+ break
+ }
+ }
+ }
+ src += '<' + name
+ for (const item in node.attrs) {
+ const val = node.attrs[item]
+ if (val) {
+ src += ` ${config.svgDict[item] || item}="${val.replace(/"/g, '')}"`
+ }
+ }
+ 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 (styleObj['border-collapse'] === 'collapse') {
+ styleObj['border-collapse'] = undefined
+ spacing = 0
+ }
+ 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 if (nodes[i].name === 'colgroup') {
+ let colI = 1
+ for (const col of (nodes[i].children || [])) {
+ if (col.name === 'col') {
+ const style = col.attrs.style || ''
+ const start = style.indexOf('width') ? style.indexOf(';width') : 0
+ // 提取出宽度
+ if (start !== -1) {
+ let end = style.indexOf(';', start + 6)
+ if (end === -1) {
+ end = style.length
+ }
+ width[colI] = style.substring(start ? start + 7 : 6, end)
+ }
+ colI += 1
+ }
+ }
+ } 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 === 'tbody' || node.name === 'tr') && node.flag && node.c) {
+ node.flag = undefined;
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ if (nodes[i].name === 'td') {
+ // 颜色样式设置给单元格避免丢失
+ for (const style of ['color', 'background', 'background-color']) {
+ if (styleObj[style]) {
+ nodes[i].attrs.style = style + ':' + styleObj[style] + ';' + (nodes[i].attrs.style || '')
+ }
+ }
+ } else {
+ traversal(nodes[i].children || [])
+ }
+ }
+ })(children)
+ } 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].name === 'tbody' || this.stack[i].name === 'tr') {
+ this.stack[i].flag = 1 // 指示含有合并单元格
+ }
+ }
+ } 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/uview-plus/components/u-parse/props.js b/uni_modules/uview-plus/components/u-parse/props.js
new file mode 100644
index 0000000..5a7b4eb
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-parse/props.js
@@ -0,0 +1,54 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ containerStyle: {
+ type: String,
+ default: null
+ },
+ content: String,
+ copyLink: {
+ type: Boolean,
+ default: () => defProps.parse.copyLink
+ },
+ domain: String,
+ errorImg: {
+ type: String,
+ default: () => defProps.parse.errorImg
+ },
+ lazyLoad: {
+ type: Boolean,
+ default: () => defProps.parse.lazyLoad
+ },
+ loadingImg: {
+ type: String,
+ default: () => defProps.parse.loadingImg
+ },
+ pauseVideo: {
+ type: Boolean,
+ default: () => defProps.parse.pauseVideo
+ },
+ previewImg: {
+ type: Boolean,
+ default: () => defProps.parse.previewImg
+ },
+ scrollTable: Boolean,
+ selectable: Boolean,
+ setTitle: {
+ type: Boolean,
+ default: () => defProps.parse.setTitle
+ },
+ showImgMenu: {
+ type: Boolean,
+ default: () => defProps.parse.showImgMenu
+ },
+ tagStyle: {
+ type: Object,
+ default: () => {}
+ },
+ useAnchor: {
+ type: Boolean,
+ default: null
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-parse/u-parse.vue b/uni_modules/uview-plus/components/u-parse/u-parse.vue
new file mode 100644
index 0000000..e0871fe
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-parse/u-parse.vue
@@ -0,0 +1,504 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-pdf-reader/props.js b/uni_modules/uview-plus/components/u-pdf-reader/props.js
new file mode 100644
index 0000000..dbaa178
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-pdf-reader/props.js
@@ -0,0 +1,19 @@
+export default {
+ props: {
+ // PDF文件地址
+ src: {
+ type: String,
+ default: ''
+ },
+ // 组件高度
+ height: {
+ type: String,
+ default: '500px'
+ },
+ // pdfjs资源域名
+ baseUrl: {
+ type: String,
+ default: 'https://uview-plus.jiangruyi.com/h5'
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-pdf-reader/u-pdf-reader.vue b/uni_modules/uview-plus/components/u-pdf-reader/u-pdf-reader.vue
new file mode 100644
index 0000000..2ff0747
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-pdf-reader/u-pdf-reader.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-picker-column/props.js b/uni_modules/uview-plus/components/u-picker-column/props.js
new file mode 100644
index 0000000..a37e438
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-picker-column/props.js
@@ -0,0 +1,7 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-picker-column/u-picker-column.vue b/uni_modules/uview-plus/components/u-picker-column/u-picker-column.vue
new file mode 100644
index 0000000..f788634
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-picker-column/u-picker-column.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-picker-data/u-picker-data.vue b/uni_modules/uview-plus/components/u-picker-data/u-picker-data.vue
new file mode 100644
index 0000000..959c811
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-picker-data/u-picker-data.vue
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-picker/picker.js b/uni_modules/uview-plus/components/u-picker/picker.js
new file mode 100644
index 0000000..61fca3e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-picker/picker.js
@@ -0,0 +1,42 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/picker.js
+ */
+import { t } from '../../libs/i18n'
+export default {
+ // picker
+ picker: {
+ show: false,
+ popupMode: 'bottom',
+ showToolbar: true,
+ title: '',
+ columns: [],
+ loading: false,
+ itemHeight: 44,
+ cancelText: t("up.common.cancel"),
+ confirmText: t("up.common.confirm"),
+ cancelColor: '#909193',
+ confirmColor: '',
+ visibleItemCount: 5,
+ keyName: 'text',
+ valueName: 'value',
+ closeOnClickOverlay: false,
+ defaultIndex: [],
+ immediateChange: true,
+ zIndex: 10076,
+ disabled: false,
+ disabledColor: '',
+ placeholder: t("up.common.pleaseChoose"),
+ inputProps: {},
+ bgColor: '',
+ round: 0,
+ duration: 300,
+ overlayOpacity: 0.5,
+ pageInline: false
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-picker/props.js b/uni_modules/uview-plus/components/u-picker/props.js
new file mode 100644
index 0000000..8135a6a
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-picker/props.js
@@ -0,0 +1,162 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ modelValue: {
+ type: Array,
+ default: () => []
+ },
+ hasInput: {
+ type: Boolean,
+ default: false
+ },
+ inputProps: {
+ type: Object,
+ default: () => {
+ return {}
+ }
+ },
+ disabled: {
+ type: Boolean,
+ default: () => defProps.picker.disabled
+ },
+ disabledColor:{
+ type: String,
+ default: () => defProps.picker.disabledColor
+ },
+ placeholder: {
+ type: String,
+ default: () => defProps.picker.placeholder
+ },
+ // 是否展示picker弹窗
+ show: {
+ type: Boolean,
+ default: () => defProps.picker.show
+ },
+ // 弹出的方向,可选值为 top bottom right left center
+ popupMode: {
+ type: String,
+ default: () => defProps.picker.popupMode
+ },
+ // 是否展示顶部的操作栏
+ showToolbar: {
+ type: Boolean,
+ default: () => defProps.picker.showToolbar
+ },
+ // 顶部标题
+ title: {
+ type: String,
+ default: () => defProps.picker.title
+ },
+ // 对象数组,设置每一列的数据
+ columns: {
+ type: Array,
+ default: () => defProps.picker.columns
+ },
+ // 是否显示加载中状态
+ loading: {
+ type: Boolean,
+ default: () => defProps.picker.loading
+ },
+ // 各列中,单个选项的高度
+ itemHeight: {
+ type: [String, Number],
+ default: () => defProps.picker.itemHeight
+ },
+ // 取消按钮的文字
+ cancelText: {
+ type: String,
+ default: () => defProps.picker.cancelText
+ },
+ // 确认按钮的文字
+ confirmText: {
+ type: String,
+ default: () => defProps.picker.confirmText
+ },
+ // 取消按钮的颜色
+ cancelColor: {
+ type: String,
+ default: () => defProps.picker.cancelColor
+ },
+ // 确认按钮的颜色
+ confirmColor: {
+ type: String,
+ default: () => defProps.picker.confirmColor
+ },
+ // 每列中可见选项的数量
+ visibleItemCount: {
+ type: [String, Number],
+ default: () => defProps.picker.visibleItemCount
+ },
+ // 选项对象中,需要展示的属性键名
+ keyName: {
+ type: String,
+ default: () => defProps.picker.keyName
+ },
+ // 选项对象中,需要获取的属性值键名
+ valueName: {
+ type: String,
+ default: () => defProps.picker.valueName
+ },
+ // 是否允许点击遮罩关闭选择器
+ closeOnClickOverlay: {
+ type: Boolean,
+ default: () => defProps.picker.closeOnClickOverlay
+ },
+ // 各列的默认索引
+ defaultIndex: {
+ type: Array,
+ default: () => defProps.picker.defaultIndex
+ },
+ // 是否在手指松开时立即触发 change 事件。若不开启则会在滚动动画结束后触发 change 事件,只在微信2.21.1及以上有效
+ immediateChange: {
+ type: Boolean,
+ default: () => defProps.picker.immediateChange
+ },
+ // 工具栏右侧插槽是否开启
+ toolbarRightSlot: {
+ type: Boolean,
+ default: false
+ },
+ // 层级
+ zIndex: {
+ type: [String, Number],
+ default: () => defProps.picker.zIndex
+ },
+ // 弹窗背景色,设置为transparent可去除白色背景
+ bgColor: {
+ type: String,
+ default: () => defProps.picker.bgColor
+ },
+ // 是否显示圆角
+ round: {
+ type: [Boolean, String, Number],
+ default: () => defProps.picker.round
+ },
+ // 动画时长,单位ms
+ duration: {
+ type: [String, Number],
+ default: () => defProps.picker.duration
+ },
+ // 遮罩的透明度,0-1之间
+ overlayOpacity: {
+ type: [Number, String],
+ default: () => defProps.picker.overlayOpacity
+ },
+ // 是否页面内展示
+ pageInline:{
+ type: Boolean,
+ default: () => defProps.picker.pageInline
+ },
+ // 蒙层样式样式
+ maskClass: {
+ type: String,
+ defualt: ''
+ },
+ // 蒙层样式样式
+ maskStyle: {
+ type: String,
+ defualt: ''
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-picker/u-picker.vue b/uni_modules/uview-plus/components/u-picker/u-picker.vue
new file mode 100644
index 0000000..cbd5701
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-picker/u-picker.vue
@@ -0,0 +1,478 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getItemText(item1) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-popover/props.js b/uni_modules/uview-plus/components/u-popover/props.js
new file mode 100644
index 0000000..64f1855
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-popover/props.js
@@ -0,0 +1,59 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+ props: {
+ // 显示的文字内容
+ text: {
+ type: [String, Number],
+ default: ''
+ },
+ // 文字颜色
+ color: {
+ type: String,
+ default: '#333'
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: '#f7f7f7'
+ },
+ // 弹出框背景颜色
+ popupBgColor: {
+ type: String,
+ default: '#f7f7f7'
+ },
+ // 弹出框位置
+ placement: {
+ type: String,
+ default: 'top'
+ },
+ // 触发方式
+ triggerMode: {
+ type: String,
+ default: 'click'
+ },
+ // 是否显示 (manual模式下使用)
+ show: {
+ type: Boolean,
+ default: false
+ },
+ // z-index值
+ zIndex: {
+ type: [Number, String],
+ default: 10070
+ },
+ // 强制定位
+ forcePosition: {
+ type: Object,
+ default() {
+ return {}
+ }
+ },
+ // 弹出方向
+ direction: {
+ type: String,
+ default: 'top'
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-popover/u-popover.vue b/uni_modules/uview-plus/components/u-popover/u-popover.vue
new file mode 100644
index 0000000..fb9a2dd
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-popover/u-popover.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+ {{text}}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-popup/popup.js b/uni_modules/uview-plus/components/u-popup/popup.js
new file mode 100644
index 0000000..9560586
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-popup/popup.js
@@ -0,0 +1,33 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/popup.js
+ */
+export default {
+ // popup组件
+ popup: {
+ show: false,
+ overlay: true,
+ mode: 'bottom',
+ duration: 300,
+ closeable: false,
+ overlayStyle: {},
+ closeOnClickOverlay: true,
+ zIndex: 10075,
+ safeAreaInsetBottom: true,
+ safeAreaInsetTop: false,
+ closeIconPos: 'top-right',
+ round: '20px',
+ zoom: true,
+ bgColor: '',
+ overlayOpacity: 0.5,
+ pageInline: false,
+ touchable: false,
+ minHeight: '200px',
+ maxHeight: '600px'
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-popup/props.js b/uni_modules/uview-plus/components/u-popup/props.js
new file mode 100644
index 0000000..68e7c86
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-popup/props.js
@@ -0,0 +1,101 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 是否展示弹窗
+ show: {
+ type: Boolean,
+ default: () => defProps.popup.show
+ },
+ // 是否显示遮罩
+ overlay: {
+ type: Boolean,
+ default: () => defProps.popup.overlay
+ },
+ // 弹出的方向,可选值为 top bottom right left center
+ mode: {
+ type: String,
+ default: () => defProps.popup.mode
+ },
+ // 动画时长,单位ms
+ duration: {
+ type: [String, Number],
+ default: () => defProps.popup.duration
+ },
+ // 是否显示关闭图标
+ closeable: {
+ type: Boolean,
+ default: () => defProps.popup.closeable
+ },
+ // 自定义遮罩的样式
+ overlayStyle: {
+ type: [Object, String],
+ default: () => defProps.popup.overlayStyle
+ },
+ // 点击遮罩是否关闭弹窗
+ closeOnClickOverlay: {
+ type: Boolean,
+ default: () => defProps.popup.closeOnClickOverlay
+ },
+ // 层级
+ zIndex: {
+ type: [String, Number],
+ default: () => defProps.popup.zIndex
+ },
+ // 是否为iPhoneX留出底部安全距离
+ safeAreaInsetBottom: {
+ type: Boolean,
+ default: () => defProps.popup.safeAreaInsetBottom
+ },
+ // 是否留出顶部安全距离(状态栏高度)
+ safeAreaInsetTop: {
+ type: Boolean,
+ default: () => defProps.popup.safeAreaInsetTop
+ },
+ // 自定义关闭图标位置,top-left为左上角,top-right为右上角,bottom-left为左下角,bottom-right为右下角
+ closeIconPos: {
+ type: String,
+ default: () => defProps.popup.closeIconPos
+ },
+ // 是否显示圆角
+ round: {
+ type: [Boolean, String, Number],
+ default: () => defProps.popup.round
+ },
+ // mode=center,也即中部弹出时,是否使用缩放模式
+ zoom: {
+ type: Boolean,
+ default: () => defProps.popup.zoom
+ },
+ // 弹窗背景色,设置为transparent可去除白色背景
+ bgColor: {
+ type: String,
+ default: () => defProps.popup.bgColor
+ },
+ // 遮罩的透明度,0-1之间
+ overlayOpacity: {
+ type: [Number, String],
+ default: () => defProps.popup.overlayOpacity
+ },
+ // 是否页面内展示
+ pageInline:{
+ type: Boolean,
+ default: () => defProps.popup.pageInline
+ },
+ // 是否页开启手势滑动
+ touchable:{
+ type: Boolean,
+ default: () => defProps.popup.touchable
+ },
+ // 手势滑动最小高度
+ minHeight:{
+ type: [String],
+ default: () => defProps.popup.minHeight
+ },
+ // 手势滑动最大高度
+ maxHeight:{
+ type: [String],
+ default: () => defProps.popup.maxHeight
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-popup/u-popup.vue b/uni_modules/uview-plus/components/u-popup/u-popup.vue
new file mode 100644
index 0000000..e66015e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-popup/u-popup.vue
@@ -0,0 +1,452 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-poster/u-poster.vue b/uni_modules/uview-plus/components/u-poster/u-poster.vue
new file mode 100644
index 0000000..fe2b600
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-poster/u-poster.vue
@@ -0,0 +1,629 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-pull-refresh/u-pull-refresh.vue b/uni_modules/uview-plus/components/u-pull-refresh/u-pull-refresh.vue
new file mode 100644
index 0000000..a71a27e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-pull-refresh/u-pull-refresh.vue
@@ -0,0 +1,334 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ t("up.pullRefresh.pull") }}
+
+
+
+
+
+
+
+
+
+ {{ t("up.pullRefresh.release") }}
+
+
+
+
+
+
+
+
+
+ {{ t("up.pullRefresh.refreshing") }}...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-qrcode/qrcode.js b/uni_modules/uview-plus/components/u-qrcode/qrcode.js
new file mode 100644
index 0000000..a99aa8c
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-qrcode/qrcode.js
@@ -0,0 +1,1281 @@
+let QRCode = {};
+(function () {
+ /**
+ * 获取单个字符的utf8编码
+ * unicode BMP平面约65535个字符
+ * @param {num} code
+ * return {array}
+ */
+ function unicodeFormat8(code) {
+ // 1 byte
+ var c0, c1, c2;
+ if (code < 128) {
+ return [code];
+ // 2 bytes
+ } else if (code < 2048) {
+ c0 = 192 + (code >> 6);
+ c1 = 128 + (code & 63);
+ return [c0, c1];
+ // 3 bytes
+ } else {
+ c0 = 224 + (code >> 12);
+ c1 = 128 + (code >> 6 & 63);
+ c2 = 128 + (code & 63);
+ return [c0, c1, c2];
+ }
+ }
+ /**
+ * 获取字符串的utf8编码字节串
+ * @param {string} string
+ * @return {array}
+ */
+ function getUTF8Bytes(string) {
+ var utf8codes = [];
+ for (var i = 0; i < string.length; i++) {
+ var code = string.charCodeAt(i);
+ var utf8 = unicodeFormat8(code);
+ for (var j = 0; j < utf8.length; j++) {
+ utf8codes.push(utf8[j]);
+ }
+ }
+ return utf8codes;
+ }
+ /**
+ * 二维码算法实现
+ * @param {string} data 要编码的信息字符串
+ * @param {num} errorCorrectLevel 纠错等级
+ */
+ function QRCodeAlg(data, errorCorrectLevel) {
+ this.typeNumber = -1; //版本
+ this.errorCorrectLevel = errorCorrectLevel;
+ this.modules = null; //二维矩阵,存放最终结果
+ this.moduleCount = 0; //矩阵大小
+ this.dataCache = null; //数据缓存
+ this.rsBlocks = null; //版本数据信息
+ this.totalDataCount = -1; //可使用的数据量
+ this.data = data;
+ this.utf8bytes = getUTF8Bytes(data);
+ this.make();
+ }
+ QRCodeAlg.prototype = {
+ constructor: QRCodeAlg,
+ /**
+ * 获取二维码矩阵大小
+ * @return {num} 矩阵大小
+ */
+ getModuleCount: function () {
+ return this.moduleCount;
+ },
+ /**
+ * 编码
+ */
+ make: function () {
+ this.getRightType();
+ this.dataCache = this.createData();
+ this.createQrcode();
+ },
+ /**
+ * 设置二位矩阵功能图形
+ * @param {bool} test 表示是否在寻找最好掩膜阶段
+ * @param {num} maskPattern 掩膜的版本
+ */
+ makeImpl: function (maskPattern) {
+ this.moduleCount = this.typeNumber * 4 + 17;
+ this.modules = new Array(this.moduleCount);
+ for (var row = 0; row < this.moduleCount; row++) {
+ this.modules[row] = new Array(this.moduleCount);
+ }
+ this.setupPositionProbePattern(0, 0);
+ this.setupPositionProbePattern(this.moduleCount - 7, 0);
+ this.setupPositionProbePattern(0, this.moduleCount - 7);
+ this.setupPositionAdjustPattern();
+ this.setupTimingPattern();
+ this.setupTypeInfo(true, maskPattern);
+ if (this.typeNumber >= 7) {
+ this.setupTypeNumber(true);
+ }
+ this.mapData(this.dataCache, maskPattern);
+ },
+ /**
+ * 设置二维码的位置探测图形
+ * @param {num} row 探测图形的中心横坐标
+ * @param {num} col 探测图形的中心纵坐标
+ */
+ setupPositionProbePattern: function (row, col) {
+ for (var r = -1; r <= 7; r++) {
+ if (row + r <= -1 || this.moduleCount <= row + r) continue;
+ for (var c = -1; c <= 7; c++) {
+ if (col + c <= -1 || this.moduleCount <= col + c) continue;
+ if ((0 <= r && r <= 6 && (c == 0 || c == 6)) || (0 <= c && c <= 6 && (r == 0 || r == 6)) || (2 <= r && r <= 4 && 2 <= c && c <= 4)) {
+ this.modules[row + r][col + c] = true;
+ } else {
+ this.modules[row + r][col + c] = false;
+ }
+ }
+ }
+ },
+ /**
+ * 创建二维码
+ * @return {[type]} [description]
+ */
+ createQrcode: function () {
+ var minLostPoint = 0;
+ var pattern = 0;
+ var bestModules = null;
+ for (var i = 0; i < 8; i++) {
+ this.makeImpl(i);
+ var lostPoint = QRUtil.getLostPoint(this);
+ if (i == 0 || minLostPoint > lostPoint) {
+ minLostPoint = lostPoint;
+ pattern = i;
+ bestModules = this.modules;
+ }
+ }
+ this.modules = bestModules;
+ this.setupTypeInfo(false, pattern);
+ if (this.typeNumber >= 7) {
+ this.setupTypeNumber(false);
+ }
+ },
+ /**
+ * 设置定位图形
+ * @return {[type]} [description]
+ */
+ setupTimingPattern: function () {
+ for (var r = 8; r < this.moduleCount - 8; r++) {
+ if (this.modules[r][6] != null) {
+ continue;
+ }
+ this.modules[r][6] = (r % 2 == 0);
+ if (this.modules[6][r] != null) {
+ continue;
+ }
+ this.modules[6][r] = (r % 2 == 0);
+ }
+ },
+ /**
+ * 设置矫正图形
+ * @return {[type]} [description]
+ */
+ setupPositionAdjustPattern: function () {
+ var pos = QRUtil.getPatternPosition(this.typeNumber);
+ for (var i = 0; i < pos.length; i++) {
+ for (var j = 0; j < pos.length; j++) {
+ var row = pos[i];
+ var col = pos[j];
+ if (this.modules[row][col] != null) {
+ continue;
+ }
+ for (var r = -2; r <= 2; r++) {
+ for (var c = -2; c <= 2; c++) {
+ if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) {
+ this.modules[row + r][col + c] = true;
+ } else {
+ this.modules[row + r][col + c] = false;
+ }
+ }
+ }
+ }
+ }
+ },
+ /**
+ * 设置版本信息(7以上版本才有)
+ * @param {bool} test 是否处于判断最佳掩膜阶段
+ * @return {[type]} [description]
+ */
+ setupTypeNumber: function (test) {
+ var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
+ for (var i = 0; i < 18; i++) {
+ var mod = (!test && ((bits >> i) & 1) == 1);
+ this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
+ this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
+ }
+ },
+ /**
+ * 设置格式信息(纠错等级和掩膜版本)
+ * @param {bool} test
+ * @param {num} maskPattern 掩膜版本
+ * @return {}
+ */
+ setupTypeInfo: function (test, maskPattern) {
+ var data = (QRErrorCorrectLevel[this.errorCorrectLevel] << 3) | maskPattern;
+ var bits = QRUtil.getBCHTypeInfo(data);
+ // vertical
+ for (var i = 0; i < 15; i++) {
+ var mod = (!test && ((bits >> i) & 1) == 1);
+ if (i < 6) {
+ this.modules[i][8] = mod;
+ } else if (i < 8) {
+ this.modules[i + 1][8] = mod;
+ } else {
+ this.modules[this.moduleCount - 15 + i][8] = mod;
+ }
+ // horizontal
+ var mod = (!test && ((bits >> i) & 1) == 1);
+ if (i < 8) {
+ this.modules[8][this.moduleCount - i - 1] = mod;
+ } else if (i < 9) {
+ this.modules[8][15 - i - 1 + 1] = mod;
+ } else {
+ this.modules[8][15 - i - 1] = mod;
+ }
+ }
+ // fixed module
+ this.modules[this.moduleCount - 8][8] = (!test);
+ },
+ /**
+ * 数据编码
+ * @return {[type]} [description]
+ */
+ createData: function () {
+ var buffer = new QRBitBuffer();
+ var lengthBits = this.typeNumber > 9 ? 16 : 8;
+ buffer.put(4, 4); //添加模式
+ buffer.put(this.utf8bytes.length, lengthBits);
+ for (var i = 0, l = this.utf8bytes.length; i < l; i++) {
+ buffer.put(this.utf8bytes[i], 8);
+ }
+ if (buffer.length + 4 <= this.totalDataCount * 8) {
+ buffer.put(0, 4);
+ }
+ // padding
+ while (buffer.length % 8 != 0) {
+ buffer.putBit(false);
+ }
+ // padding
+ while (true) {
+ if (buffer.length >= this.totalDataCount * 8) {
+ break;
+ }
+ buffer.put(QRCodeAlg.PAD0, 8);
+ if (buffer.length >= this.totalDataCount * 8) {
+ break;
+ }
+ buffer.put(QRCodeAlg.PAD1, 8);
+ }
+ return this.createBytes(buffer);
+ },
+ /**
+ * 纠错码编码
+ * @param {buffer} buffer 数据编码
+ * @return {[type]}
+ */
+ createBytes: function (buffer) {
+ var offset = 0;
+ var maxDcCount = 0;
+ var maxEcCount = 0;
+ var length = this.rsBlock.length / 3;
+ var rsBlocks = new Array();
+ for (var i = 0; i < length; i++) {
+ var count = this.rsBlock[i * 3 + 0];
+ var totalCount = this.rsBlock[i * 3 + 1];
+ var dataCount = this.rsBlock[i * 3 + 2];
+ for (var j = 0; j < count; j++) {
+ rsBlocks.push([dataCount, totalCount]);
+ }
+ }
+ var dcdata = new Array(rsBlocks.length);
+ var ecdata = new Array(rsBlocks.length);
+ for (var r = 0; r < rsBlocks.length; r++) {
+ var dcCount = rsBlocks[r][0];
+ var ecCount = rsBlocks[r][1] - dcCount;
+ maxDcCount = Math.max(maxDcCount, dcCount);
+ maxEcCount = Math.max(maxEcCount, ecCount);
+ dcdata[r] = new Array(dcCount);
+ for (var i = 0; i < dcdata[r].length; i++) {
+ dcdata[r][i] = 0xff & buffer.buffer[i + offset];
+ }
+ offset += dcCount;
+ var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
+ var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
+ var modPoly = rawPoly.mod(rsPoly);
+ ecdata[r] = new Array(rsPoly.getLength() - 1);
+ for (var i = 0; i < ecdata[r].length; i++) {
+ var modIndex = i + modPoly.getLength() - ecdata[r].length;
+ ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0;
+ }
+ }
+ var data = new Array(this.totalDataCount);
+ var index = 0;
+ for (var i = 0; i < maxDcCount; i++) {
+ for (var r = 0; r < rsBlocks.length; r++) {
+ if (i < dcdata[r].length) {
+ data[index++] = dcdata[r][i];
+ }
+ }
+ }
+ for (var i = 0; i < maxEcCount; i++) {
+ for (var r = 0; r < rsBlocks.length; r++) {
+ if (i < ecdata[r].length) {
+ data[index++] = ecdata[r][i];
+ }
+ }
+ }
+ return data;
+
+ },
+ /**
+ * 布置模块,构建最终信息
+ * @param {} data
+ * @param {} maskPattern
+ * @return {}
+ */
+ mapData: function (data, maskPattern) {
+ var inc = -1;
+ var row = this.moduleCount - 1;
+ var bitIndex = 7;
+ var byteIndex = 0;
+ for (var col = this.moduleCount - 1; col > 0; col -= 2) {
+ if (col == 6) col--;
+ while (true) {
+ for (var c = 0; c < 2; c++) {
+ if (this.modules[row][col - c] == null) {
+ var dark = false;
+ if (byteIndex < data.length) {
+ dark = (((data[byteIndex] >>> bitIndex) & 1) == 1);
+ }
+ var mask = QRUtil.getMask(maskPattern, row, col - c);
+ if (mask) {
+ dark = !dark;
+ }
+ this.modules[row][col - c] = dark;
+ bitIndex--;
+ if (bitIndex == -1) {
+ byteIndex++;
+ bitIndex = 7;
+ }
+ }
+ }
+ row += inc;
+ if (row < 0 || this.moduleCount <= row) {
+ row -= inc;
+ inc = -inc;
+ break;
+ }
+ }
+ }
+ }
+ };
+ /**
+ * 填充字段
+ */
+ QRCodeAlg.PAD0 = 0xEC;
+ QRCodeAlg.PAD1 = 0x11;
+ //---------------------------------------------------------------------
+ // 纠错等级对应的编码
+ //---------------------------------------------------------------------
+ var QRErrorCorrectLevel = [1, 0, 3, 2];
+ //---------------------------------------------------------------------
+ // 掩膜版本
+ //---------------------------------------------------------------------
+ var QRMaskPattern = {
+ PATTERN000: 0,
+ PATTERN001: 1,
+ PATTERN010: 2,
+ PATTERN011: 3,
+ PATTERN100: 4,
+ PATTERN101: 5,
+ PATTERN110: 6,
+ PATTERN111: 7
+ };
+ //---------------------------------------------------------------------
+ // 工具类
+ //---------------------------------------------------------------------
+ var QRUtil = {
+ /*
+ 每个版本矫正图形的位置
+ */
+ PATTERN_POSITION_TABLE: [
+ [],
+ [6, 18],
+ [6, 22],
+ [6, 26],
+ [6, 30],
+ [6, 34],
+ [6, 22, 38],
+ [6, 24, 42],
+ [6, 26, 46],
+ [6, 28, 50],
+ [6, 30, 54],
+ [6, 32, 58],
+ [6, 34, 62],
+ [6, 26, 46, 66],
+ [6, 26, 48, 70],
+ [6, 26, 50, 74],
+ [6, 30, 54, 78],
+ [6, 30, 56, 82],
+ [6, 30, 58, 86],
+ [6, 34, 62, 90],
+ [6, 28, 50, 72, 94],
+ [6, 26, 50, 74, 98],
+ [6, 30, 54, 78, 102],
+ [6, 28, 54, 80, 106],
+ [6, 32, 58, 84, 110],
+ [6, 30, 58, 86, 114],
+ [6, 34, 62, 90, 118],
+ [6, 26, 50, 74, 98, 122],
+ [6, 30, 54, 78, 102, 126],
+ [6, 26, 52, 78, 104, 130],
+ [6, 30, 56, 82, 108, 134],
+ [6, 34, 60, 86, 112, 138],
+ [6, 30, 58, 86, 114, 142],
+ [6, 34, 62, 90, 118, 146],
+ [6, 30, 54, 78, 102, 126, 150],
+ [6, 24, 50, 76, 102, 128, 154],
+ [6, 28, 54, 80, 106, 132, 158],
+ [6, 32, 58, 84, 110, 136, 162],
+ [6, 26, 54, 82, 110, 138, 166],
+ [6, 30, 58, 86, 114, 142, 170]
+ ],
+ G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
+ G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
+ G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
+ /*
+ BCH编码格式信息
+ */
+ getBCHTypeInfo: function (data) {
+ var d = data << 10;
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
+ d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15)));
+ }
+ return ((data << 10) | d) ^ QRUtil.G15_MASK;
+ },
+ /*
+ BCH编码版本信息
+ */
+ getBCHTypeNumber: function (data) {
+ var d = data << 12;
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
+ d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18)));
+ }
+ return (data << 12) | d;
+ },
+ /*
+ 获取BCH位信息
+ */
+ getBCHDigit: function (data) {
+ var digit = 0;
+ while (data != 0) {
+ digit++;
+ data >>>= 1;
+ }
+ return digit;
+ },
+ /*
+ 获取版本对应的矫正图形位置
+ */
+ getPatternPosition: function (typeNumber) {
+ return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
+ },
+ /*
+ 掩膜算法
+ */
+ getMask: function (maskPattern, i, j) {
+ switch (maskPattern) {
+ case QRMaskPattern.PATTERN000:
+ return (i + j) % 2 == 0;
+ case QRMaskPattern.PATTERN001:
+ return i % 2 == 0;
+ case QRMaskPattern.PATTERN010:
+ return j % 3 == 0;
+ case QRMaskPattern.PATTERN011:
+ return (i + j) % 3 == 0;
+ case QRMaskPattern.PATTERN100:
+ return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
+ case QRMaskPattern.PATTERN101:
+ return (i * j) % 2 + (i * j) % 3 == 0;
+ case QRMaskPattern.PATTERN110:
+ return ((i * j) % 2 + (i * j) % 3) % 2 == 0;
+ case QRMaskPattern.PATTERN111:
+ return ((i * j) % 3 + (i + j) % 2) % 2 == 0;
+ default:
+ throw new Error("bad maskPattern:" + maskPattern);
+ }
+ },
+ /*
+ 获取RS的纠错多项式
+ */
+ getErrorCorrectPolynomial: function (errorCorrectLength) {
+ var a = new QRPolynomial([1], 0);
+ for (var i = 0; i < errorCorrectLength; i++) {
+ a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
+ }
+ return a;
+ },
+ /*
+ 获取评价
+ */
+ getLostPoint: function (qrCode) {
+ var moduleCount = qrCode.getModuleCount(),
+ lostPoint = 0,
+ darkCount = 0;
+ for (var row = 0; row < moduleCount; row++) {
+ var sameCount = 0;
+ var head = qrCode.modules[row][0];
+ for (var col = 0; col < moduleCount; col++) {
+ var current = qrCode.modules[row][col];
+ //level 3 评价
+ if (col < moduleCount - 6) {
+ if (current && !qrCode.modules[row][col + 1] && qrCode.modules[row][col + 2] && qrCode.modules[row][col + 3] && qrCode.modules[row][col + 4] && !qrCode.modules[row][col + 5] && qrCode.modules[row][col + 6]) {
+ if (col < moduleCount - 10) {
+ if (qrCode.modules[row][col + 7] && qrCode.modules[row][col + 8] && qrCode.modules[row][col + 9] && qrCode.modules[row][col + 10]) {
+ lostPoint += 40;
+ }
+ } else if (col > 3) {
+ if (qrCode.modules[row][col - 1] && qrCode.modules[row][col - 2] && qrCode.modules[row][col - 3] && qrCode.modules[row][col - 4]) {
+ lostPoint += 40;
+ }
+ }
+ }
+ }
+ //level 2 评价
+ if ((row < moduleCount - 1) && (col < moduleCount - 1)) {
+ var count = 0;
+ if (current) count++;
+ if (qrCode.modules[row + 1][col]) count++;
+ if (qrCode.modules[row][col + 1]) count++;
+ if (qrCode.modules[row + 1][col + 1]) count++;
+ if (count == 0 || count == 4) {
+ lostPoint += 3;
+ }
+ }
+ //level 1 评价
+ if (head ^ current) {
+ sameCount++;
+ } else {
+ head = current;
+ if (sameCount >= 5) {
+ lostPoint += (3 + sameCount - 5);
+ }
+ sameCount = 1;
+ }
+ //level 4 评价
+ if (current) {
+ darkCount++;
+ }
+ }
+ }
+ for (var col = 0; col < moduleCount; col++) {
+ var sameCount = 0;
+ var head = qrCode.modules[0][col];
+ for (var row = 0; row < moduleCount; row++) {
+ var current = qrCode.modules[row][col];
+ //level 3 评价
+ if (row < moduleCount - 6) {
+ if (current && !qrCode.modules[row + 1][col] && qrCode.modules[row + 2][col] && qrCode.modules[row + 3][col] && qrCode.modules[row + 4][col] && !qrCode.modules[row + 5][col] && qrCode.modules[row + 6][col]) {
+ if (row < moduleCount - 10) {
+ if (qrCode.modules[row + 7][col] && qrCode.modules[row + 8][col] && qrCode.modules[row + 9][col] && qrCode.modules[row + 10][col]) {
+ lostPoint += 40;
+ }
+ } else if (row > 3) {
+ if (qrCode.modules[row - 1][col] && qrCode.modules[row - 2][col] && qrCode.modules[row - 3][col] && qrCode.modules[row - 4][col]) {
+ lostPoint += 40;
+ }
+ }
+ }
+ }
+ //level 1 评价
+ if (head ^ current) {
+ sameCount++;
+ } else {
+ head = current;
+ if (sameCount >= 5) {
+ lostPoint += (3 + sameCount - 5);
+ }
+ sameCount = 1;
+ }
+ }
+ }
+ // LEVEL4
+ var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
+ lostPoint += ratio * 10;
+ return lostPoint;
+ }
+
+ };
+ //---------------------------------------------------------------------
+ // QRMath使用的数学工具
+ //---------------------------------------------------------------------
+ var QRMath = {
+ /*
+ 将n转化为a^m
+ */
+ glog: function (n) {
+ if (n < 1) {
+ throw new Error("glog(" + n + ")");
+ }
+ return QRMath.LOG_TABLE[n];
+ },
+ /*
+ 将a^m转化为n
+ */
+ gexp: function (n) {
+ while (n < 0) {
+ n += 255;
+ }
+ while (n >= 256) {
+ n -= 255;
+ }
+ return QRMath.EXP_TABLE[n];
+ },
+ EXP_TABLE: new Array(256),
+ LOG_TABLE: new Array(256)
+
+ };
+ for (var i = 0; i < 8; i++) {
+ QRMath.EXP_TABLE[i] = 1 << i;
+ }
+ for (var i = 8; i < 256; i++) {
+ QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
+ }
+ for (var i = 0; i < 255; i++) {
+ QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
+ }
+ //---------------------------------------------------------------------
+ // QRPolynomial 多项式
+ //---------------------------------------------------------------------
+ /**
+ * 多项式类
+ * @param {Array} num 系数
+ * @param {num} shift a^shift
+ */
+ function QRPolynomial(num, shift) {
+ if (num.length == undefined) {
+ throw new Error(num.length + "/" + shift);
+ }
+ var offset = 0;
+ while (offset < num.length && num[offset] == 0) {
+ offset++;
+ }
+ this.num = new Array(num.length - offset + shift);
+ for (var i = 0; i < num.length - offset; i++) {
+ this.num[i] = num[i + offset];
+ }
+ }
+ QRPolynomial.prototype = {
+ get: function (index) {
+ return this.num[index];
+ },
+ getLength: function () {
+ return this.num.length;
+ },
+ /**
+ * 多项式乘法
+ * @param {QRPolynomial} e 被乘多项式
+ * @return {[type]} [description]
+ */
+ multiply: function (e) {
+ var num = new Array(this.getLength() + e.getLength() - 1);
+ for (var i = 0; i < this.getLength(); i++) {
+ for (var j = 0; j < e.getLength(); j++) {
+ num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
+ }
+ }
+ return new QRPolynomial(num, 0);
+ },
+ /**
+ * 多项式模运算
+ * @param {QRPolynomial} e 模多项式
+ * @return {}
+ */
+ mod: function (e) {
+ var tl = this.getLength(),
+ el = e.getLength();
+ if (tl - el < 0) {
+ return this;
+ }
+ var num = new Array(tl);
+ for (var i = 0; i < tl; i++) {
+ num[i] = this.get(i);
+ }
+ while (num.length >= el) {
+ var ratio = QRMath.glog(num[0]) - QRMath.glog(e.get(0));
+
+ for (var i = 0; i < e.getLength(); i++) {
+ num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
+ }
+ while (num[0] == 0) {
+ num.shift();
+ }
+ }
+ return new QRPolynomial(num, 0);
+ }
+ };
+
+ //---------------------------------------------------------------------
+ // RS_BLOCK_TABLE
+ //---------------------------------------------------------------------
+ /*
+ 二维码各个版本信息[块数, 每块中的数据块数, 每块中的信息块数]
+ */
+ var RS_BLOCK_TABLE = [
+ // L
+ // M
+ // Q
+ // H
+ // 1
+ [1, 26, 19],
+ [1, 26, 16],
+ [1, 26, 13],
+ [1, 26, 9],
+
+ // 2
+ [1, 44, 34],
+ [1, 44, 28],
+ [1, 44, 22],
+ [1, 44, 16],
+
+ // 3
+ [1, 70, 55],
+ [1, 70, 44],
+ [2, 35, 17],
+ [2, 35, 13],
+
+ // 4
+ [1, 100, 80],
+ [2, 50, 32],
+ [2, 50, 24],
+ [4, 25, 9],
+
+ // 5
+ [1, 134, 108],
+ [2, 67, 43],
+ [2, 33, 15, 2, 34, 16],
+ [2, 33, 11, 2, 34, 12],
+
+ // 6
+ [2, 86, 68],
+ [4, 43, 27],
+ [4, 43, 19],
+ [4, 43, 15],
+
+ // 7
+ [2, 98, 78],
+ [4, 49, 31],
+ [2, 32, 14, 4, 33, 15],
+ [4, 39, 13, 1, 40, 14],
+
+ // 8
+ [2, 121, 97],
+ [2, 60, 38, 2, 61, 39],
+ [4, 40, 18, 2, 41, 19],
+ [4, 40, 14, 2, 41, 15],
+
+ // 9
+ [2, 146, 116],
+ [3, 58, 36, 2, 59, 37],
+ [4, 36, 16, 4, 37, 17],
+ [4, 36, 12, 4, 37, 13],
+
+ // 10
+ [2, 86, 68, 2, 87, 69],
+ [4, 69, 43, 1, 70, 44],
+ [6, 43, 19, 2, 44, 20],
+ [6, 43, 15, 2, 44, 16],
+
+ // 11
+ [4, 101, 81],
+ [1, 80, 50, 4, 81, 51],
+ [4, 50, 22, 4, 51, 23],
+ [3, 36, 12, 8, 37, 13],
+
+ // 12
+ [2, 116, 92, 2, 117, 93],
+ [6, 58, 36, 2, 59, 37],
+ [4, 46, 20, 6, 47, 21],
+ [7, 42, 14, 4, 43, 15],
+
+ // 13
+ [4, 133, 107],
+ [8, 59, 37, 1, 60, 38],
+ [8, 44, 20, 4, 45, 21],
+ [12, 33, 11, 4, 34, 12],
+
+ // 14
+ [3, 145, 115, 1, 146, 116],
+ [4, 64, 40, 5, 65, 41],
+ [11, 36, 16, 5, 37, 17],
+ [11, 36, 12, 5, 37, 13],
+
+ // 15
+ [5, 109, 87, 1, 110, 88],
+ [5, 65, 41, 5, 66, 42],
+ [5, 54, 24, 7, 55, 25],
+ [11, 36, 12],
+
+ // 16
+ [5, 122, 98, 1, 123, 99],
+ [7, 73, 45, 3, 74, 46],
+ [15, 43, 19, 2, 44, 20],
+ [3, 45, 15, 13, 46, 16],
+
+ // 17
+ [1, 135, 107, 5, 136, 108],
+ [10, 74, 46, 1, 75, 47],
+ [1, 50, 22, 15, 51, 23],
+ [2, 42, 14, 17, 43, 15],
+
+ // 18
+ [5, 150, 120, 1, 151, 121],
+ [9, 69, 43, 4, 70, 44],
+ [17, 50, 22, 1, 51, 23],
+ [2, 42, 14, 19, 43, 15],
+
+ // 19
+ [3, 141, 113, 4, 142, 114],
+ [3, 70, 44, 11, 71, 45],
+ [17, 47, 21, 4, 48, 22],
+ [9, 39, 13, 16, 40, 14],
+
+ // 20
+ [3, 135, 107, 5, 136, 108],
+ [3, 67, 41, 13, 68, 42],
+ [15, 54, 24, 5, 55, 25],
+ [15, 43, 15, 10, 44, 16],
+
+ // 21
+ [4, 144, 116, 4, 145, 117],
+ [17, 68, 42],
+ [17, 50, 22, 6, 51, 23],
+ [19, 46, 16, 6, 47, 17],
+
+ // 22
+ [2, 139, 111, 7, 140, 112],
+ [17, 74, 46],
+ [7, 54, 24, 16, 55, 25],
+ [34, 37, 13],
+
+ // 23
+ [4, 151, 121, 5, 152, 122],
+ [4, 75, 47, 14, 76, 48],
+ [11, 54, 24, 14, 55, 25],
+ [16, 45, 15, 14, 46, 16],
+
+ // 24
+ [6, 147, 117, 4, 148, 118],
+ [6, 73, 45, 14, 74, 46],
+ [11, 54, 24, 16, 55, 25],
+ [30, 46, 16, 2, 47, 17],
+
+ // 25
+ [8, 132, 106, 4, 133, 107],
+ [8, 75, 47, 13, 76, 48],
+ [7, 54, 24, 22, 55, 25],
+ [22, 45, 15, 13, 46, 16],
+
+ // 26
+ [10, 142, 114, 2, 143, 115],
+ [19, 74, 46, 4, 75, 47],
+ [28, 50, 22, 6, 51, 23],
+ [33, 46, 16, 4, 47, 17],
+
+ // 27
+ [8, 152, 122, 4, 153, 123],
+ [22, 73, 45, 3, 74, 46],
+ [8, 53, 23, 26, 54, 24],
+ [12, 45, 15, 28, 46, 16],
+
+ // 28
+ [3, 147, 117, 10, 148, 118],
+ [3, 73, 45, 23, 74, 46],
+ [4, 54, 24, 31, 55, 25],
+ [11, 45, 15, 31, 46, 16],
+
+ // 29
+ [7, 146, 116, 7, 147, 117],
+ [21, 73, 45, 7, 74, 46],
+ [1, 53, 23, 37, 54, 24],
+ [19, 45, 15, 26, 46, 16],
+
+ // 30
+ [5, 145, 115, 10, 146, 116],
+ [19, 75, 47, 10, 76, 48],
+ [15, 54, 24, 25, 55, 25],
+ [23, 45, 15, 25, 46, 16],
+
+ // 31
+ [13, 145, 115, 3, 146, 116],
+ [2, 74, 46, 29, 75, 47],
+ [42, 54, 24, 1, 55, 25],
+ [23, 45, 15, 28, 46, 16],
+
+ // 32
+ [17, 145, 115],
+ [10, 74, 46, 23, 75, 47],
+ [10, 54, 24, 35, 55, 25],
+ [19, 45, 15, 35, 46, 16],
+
+ // 33
+ [17, 145, 115, 1, 146, 116],
+ [14, 74, 46, 21, 75, 47],
+ [29, 54, 24, 19, 55, 25],
+ [11, 45, 15, 46, 46, 16],
+
+ // 34
+ [13, 145, 115, 6, 146, 116],
+ [14, 74, 46, 23, 75, 47],
+ [44, 54, 24, 7, 55, 25],
+ [59, 46, 16, 1, 47, 17],
+
+ // 35
+ [12, 151, 121, 7, 152, 122],
+ [12, 75, 47, 26, 76, 48],
+ [39, 54, 24, 14, 55, 25],
+ [22, 45, 15, 41, 46, 16],
+
+ // 36
+ [6, 151, 121, 14, 152, 122],
+ [6, 75, 47, 34, 76, 48],
+ [46, 54, 24, 10, 55, 25],
+ [2, 45, 15, 64, 46, 16],
+
+ // 37
+ [17, 152, 122, 4, 153, 123],
+ [29, 74, 46, 14, 75, 47],
+ [49, 54, 24, 10, 55, 25],
+ [24, 45, 15, 46, 46, 16],
+
+ // 38
+ [4, 152, 122, 18, 153, 123],
+ [13, 74, 46, 32, 75, 47],
+ [48, 54, 24, 14, 55, 25],
+ [42, 45, 15, 32, 46, 16],
+
+ // 39
+ [20, 147, 117, 4, 148, 118],
+ [40, 75, 47, 7, 76, 48],
+ [43, 54, 24, 22, 55, 25],
+ [10, 45, 15, 67, 46, 16],
+
+ // 40
+ [19, 148, 118, 6, 149, 119],
+ [18, 75, 47, 31, 76, 48],
+ [34, 54, 24, 34, 55, 25],
+ [20, 45, 15, 61, 46, 16]
+ ];
+
+ /**
+ * 根据数据获取对应版本
+ * @return {[type]} [description]
+ */
+ QRCodeAlg.prototype.getRightType = function () {
+ for (var typeNumber = 1; typeNumber < 41; typeNumber++) {
+ var rsBlock = RS_BLOCK_TABLE[(typeNumber - 1) * 4 + this.errorCorrectLevel];
+ if (rsBlock == undefined) {
+ throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + this.errorCorrectLevel);
+ }
+ var length = rsBlock.length / 3;
+ var totalDataCount = 0;
+ for (var i = 0; i < length; i++) {
+ var count = rsBlock[i * 3 + 0];
+ var dataCount = rsBlock[i * 3 + 2];
+ totalDataCount += dataCount * count;
+ }
+ var lengthBytes = typeNumber > 9 ? 2 : 1;
+ if (this.utf8bytes.length + lengthBytes < totalDataCount || typeNumber == 40) {
+ this.typeNumber = typeNumber;
+ this.rsBlock = rsBlock;
+ this.totalDataCount = totalDataCount;
+ break;
+ }
+ }
+ };
+
+ //---------------------------------------------------------------------
+ // QRBitBuffer
+ //---------------------------------------------------------------------
+ function QRBitBuffer() {
+ this.buffer = new Array();
+ this.length = 0;
+ }
+ QRBitBuffer.prototype = {
+ get: function (index) {
+ var bufIndex = Math.floor(index / 8);
+ return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1);
+ },
+ put: function (num, length) {
+ for (var i = 0; i < length; i++) {
+ this.putBit(((num >>> (length - i - 1)) & 1));
+ }
+ },
+ putBit: function (bit) {
+ var bufIndex = Math.floor(this.length / 8);
+ if (this.buffer.length <= bufIndex) {
+ this.buffer.push(0);
+ }
+ if (bit) {
+ this.buffer[bufIndex] |= (0x80 >>> (this.length % 8));
+ }
+ this.length++;
+ }
+ };
+
+
+
+ // xzedit
+ let qrcodeAlgObjCache = [];
+ /**
+ * 二维码构造函数,主要用于绘制
+ * @param {参数列表} opt 传递参数
+ * @return {}
+ */
+ QRCode = function (opt) {
+ //设置默认参数
+ this.options = {
+ text: '',
+ size: 256,
+ correctLevel: 3,
+ background: '#ffffff',
+ foreground: '#000000',
+ pdground: '#000000',
+ image: '',
+ imageSize: 30,
+ canvasId: opt.canvasId,
+ canvas: opt.canvas,
+ ctx: opt.ctx,
+ isNvue: opt.isNvue,
+ vuectx: opt.vuectx,
+ usingComponents: opt.usingComponents,
+ showLoading: opt.showLoading,
+ loadingText: opt.loadingText,
+ };
+
+ let canvas = null;
+
+ if (typeof opt === 'string') { // 只编码ASCII字符串
+ opt = {
+ text: opt
+ };
+ }
+ if (opt) {
+ for (var i in opt) {
+ this.options[i] = opt[i];
+ }
+ }
+ //使用QRCodeAlg创建二维码结构
+ var qrCodeAlg = null;
+ for (var i = 0, l = qrcodeAlgObjCache.length; i < l; i++) {
+ if (qrcodeAlgObjCache[i].text == this.options.text && qrcodeAlgObjCache[i].text.correctLevel == this.options.correctLevel) {
+ qrCodeAlg = qrcodeAlgObjCache[i].obj;
+ break;
+ }
+ }
+ if (i == l) {
+ qrCodeAlg = new QRCodeAlg(this.options.text, this.options.correctLevel);
+ qrcodeAlgObjCache.push({
+ text: this.options.text,
+ correctLevel: this.options.correctLevel,
+ obj: qrCodeAlg
+ });
+ }
+ /**
+ * 计算矩阵点的前景色
+ * @param {Obj} config
+ * @param {Number} config.row 点x坐标
+ * @param {Number} config.col 点y坐标
+ * @param {Number} config.count 矩阵大小
+ * @param {Number} config.options 组件的options
+ * @return {String}
+ */
+ let getForeGround = function (config) {
+ var options = config.options;
+ if (options.pdground && (
+ (config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5) ||
+ (config.row > (config.count - 6) && config.row < (config.count - 2) && config.col > 1 && config.col < 5) ||
+ (config.row > 1 && config.row < 5 && config.col > (config.count - 6) && config.col < (config.count - 2))
+ )) {
+ return options.pdground;
+ }
+ return options.foreground;
+ }
+
+ // 创建canvas
+ let createCanvas = async function (options) {
+ let isApp = false;
+ // #ifdef APP
+ isApp = true;
+ // #endif
+
+ if(options.showLoading){
+ uni.showLoading({
+ title: options.loadingText,
+ mask: true
+ });
+ }
+ var ctx = '';
+ canvas = options.canvas;
+ ctx = options.ctx;
+ // 获取canvas node节点
+ // #ifdef MP
+ // 不清楚是小程序的bug还是什么原因,canvas的node节点宽高和设置的宽高不一致 重新设置下
+ canvas.width = options.size;
+ canvas.height = options.size;
+ // #endif
+ // 设置组件中data里面的ctx
+ // options.vuectx.ctx = ctx;
+ // options.vuectx.canvas = canvas;
+
+ var count = qrCodeAlg.getModuleCount();
+ var ratioSize = options.size;
+ var ratioImgSize = options.imageSize;
+ //计算每个点的长宽
+ var tileW = (ratioSize / count).toPrecision(4);
+ var tileH = (ratioSize / count).toPrecision(4);
+ //绘制
+ for (var row = 0; row < count; row++) {
+ for (var col = 0; col < count; col++) {
+ var w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW));
+ var h = (Math.ceil((row + 1) * tileH) - Math.floor(row * tileH));
+ var foreground = getForeGround({
+ row: row,
+ col: col,
+ count: count,
+ options: options
+ });
+ if (options.isNvue) {
+ ctx.setFillStyle(qrCodeAlg.modules[row][col] ? foreground : options.background);
+ } else {
+ ctx.fillStyle = qrCodeAlg.modules[row][col] ? foreground : options.background;
+ }
+ ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h);
+ }
+ }
+ if (options.image) {
+ var x = Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
+ var y = Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
+ drawRoundedRect(ctx, x, y, ratioImgSize, ratioImgSize, 2, 6, true, true)
+ options.vuectx.drawImage(options.image, x, y, ratioImgSize, ratioImgSize);
+ // 画圆角矩形
+ function drawRoundedRect(ctxi, x, y, width, height, r, lineWidth, fill, stroke) {
+ if (options.isNvue || isApp) {
+ ctxi.setLineWidth(lineWidth);
+ ctxi.setFillStyle(options.background);
+ ctxi.setStrokeStyle(options.background);
+ }
+ else {
+ ctxi.lineWidth = lineWidth;
+ ctxi.fillStyle = options.background;
+ ctxi.strokeStyle = options.background;
+ }
+ ctxi.beginPath(); // draw top and top right corner
+ ctxi.moveTo(x + r, y);
+ ctxi.lineTo(x + width, y); // move to top-right corner
+ ctxi.arc(x + width - r, y + r, r, -Math.PI / 2, 0); // draw right side and bottom right corner
+
+ ctxi.lineTo(x + width, y + height - r);
+ ctxi.arc(x + width - r, y + height - r, r, 0, Math.PI / 2); // draw bottom and bottom left corner
+
+ ctxi.lineTo(x + r, y + height);
+ ctxi.arc(x + r, y + height - r, r, Math.PI / 2, Math.PI);// draw left and top left corner
+
+ ctxi.lineTo(x, y + r);
+ ctxi.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2);
+
+ ctxi.closePath();
+ if (fill) {
+ ctxi.fill();
+ }
+ if (stroke) {
+ ctxi.stroke();
+ }
+ }
+ }
+ setTimeout(() => {
+ // canvas2 绘制是自动的不需要手动绘制
+ if(options.isNvue || isApp){
+ ctx.draw(true, () => {
+ // 保存到临时区域
+ setTimeout(() => {
+ if (options.isNvue) {
+ ctx.toTempFilePath(
+ 0,
+ 0,
+ options.width,
+ options.height,
+ options.width,
+ options.height,
+ "",
+ 1,
+ function(res) {
+ if (options.cbResult) {
+ options.cbResult(res.tempFilePath)
+ }
+ }
+ );
+ } else {
+ uni.canvasToTempFilePath({
+ width: options.width,
+ height: options.height,
+ destWidth: options.width,
+ destHeight: options.height,
+ canvasId: options.canvasId,
+ quality: Number(1),
+ success: function (res) {
+ // console.log('绘制成功', res)
+ if (options.cbResult) {
+ // 由于官方还没有统一此接口的输出字段,所以先判定下 支付宝为 res.apFilePath
+ if (!empty(res.tempFilePath)) {
+ options.cbResult(res.tempFilePath)
+ } else if (!empty(res.apFilePath)) {
+ options.cbResult(res.apFilePath)
+ } else {
+ options.cbResult(res.tempFilePath)
+ }
+ }
+ },
+ fail: function (res) {
+ console.log('绘制失败', res)
+ if (options.cbResult) {
+ options.cbResult(res)
+ }
+ },
+ complete: function () {
+ uni.hideLoading();
+ },
+ }, options.vuectx);
+ }
+ }, options.text.length + 100);
+ });
+ }
+ else{
+ options.cbResult("")
+ }
+
+ }, options.usingComponents ? 100 : 200);
+ }
+ createCanvas(this.options);
+ // 空判定
+ let empty = function (v) {
+ let tp = typeof v,
+ rt = false;
+ if (tp == "number" && String(v) == "") {
+ rt = true
+ } else if (tp == "undefined") {
+ rt = true
+ } else if (tp == "object") {
+ if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
+ } else if (tp == "string") {
+ if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
+ } else if (tp == "function") {
+ rt = false
+ }
+ return rt
+ }
+ };
+ QRCode.prototype.clear = function (fn) {
+ var ctx = '';
+ if (options.isNvue) {
+ ctx = options.ctx;
+ } else {
+ uni.createCanvasContext(this.options.canvasId, this.options.vuectx)
+ }
+ ctx.clearRect(0, 0, this.options.size, this.options.size)
+ ctx.draw(false, () => {
+ if (fn) {
+ fn()
+ }
+ })
+ };
+})()
+
+export default QRCode
diff --git a/uni_modules/uview-plus/components/u-qrcode/u-qrcode.vue b/uni_modules/uview-plus/components/u-qrcode/u-qrcode.vue
new file mode 100644
index 0000000..03cd593
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-qrcode/u-qrcode.vue
@@ -0,0 +1,536 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-radio-group/props.js b/uni_modules/uview-plus/components/u-radio-group/props.js
new file mode 100644
index 0000000..9ff6996
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-radio-group/props.js
@@ -0,0 +1,100 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // #ifdef VUE3
+ // 绑定的值
+ modelValue: {
+ type: [String, Number, Boolean],
+ default: () => defProps.radioGroup.value
+ },
+ // #endif
+ // #ifdef VUE2
+ // 绑定的值
+ value: {
+ type: [String, Number, Boolean],
+ default: () => defProps.radioGroup.value
+ },
+ // #endif
+ // 是否禁用全部radio
+ disabled: {
+ type: Boolean,
+ default: () => defProps.radioGroup.disabled
+ },
+ // 形状,circle-圆形,square-方形
+ shape: {
+ type: String,
+ default: () => defProps.radioGroup.shape
+ },
+ // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
+ activeColor: {
+ type: String,
+ default: () => defProps.radioGroup.activeColor
+ },
+ // 未选中的颜色
+ inactiveColor: {
+ type: String,
+ default: () => defProps.radioGroup.inactiveColor
+ },
+ // 标识符
+ name: {
+ type: String,
+ default: () => defProps.radioGroup.name
+ },
+ // 整个组件的尺寸,默认px
+ size: {
+ type: [String, Number],
+ default: () => defProps.radioGroup.size
+ },
+ // 布局方式,row-横向,column-纵向
+ placement: {
+ type: String,
+ default: () => defProps.radioGroup.placement
+ },
+ // label的文本
+ label: {
+ type: [String],
+ default: () => defProps.radioGroup.label
+ },
+ // label的颜色 (默认 '#303133' )
+ labelColor: {
+ type: [String],
+ default: () => defProps.radioGroup.labelColor
+ },
+ // label的字体大小,px单位
+ labelSize: {
+ type: [String, Number],
+ default: () => defProps.radioGroup.labelSize
+ },
+ // 是否禁止点击文本操作checkbox(默认 false )
+ labelDisabled: {
+ type: Boolean,
+ default: () => defProps.radioGroup.labelDisabled
+ },
+ // 图标颜色
+ iconColor: {
+ type: String,
+ default: () => defProps.radioGroup.iconColor
+ },
+ // 图标的大小,单位px
+ iconSize: {
+ type: [String, Number],
+ default: () => defProps.radioGroup.iconSize
+ },
+ // 竖向配列时,是否显示下划线
+ borderBottom: {
+ type: Boolean,
+ default: () => defProps.radioGroup.borderBottom
+ },
+ // 图标与文字的对齐方式
+ iconPlacement: {
+ type: String,
+ default: () => defProps.radio.iconPlacement
+ },
+ // item 之间的间距
+ gap: {
+ type: [String, Number],
+ default: () => defProps.radioGroup.gap
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-radio-group/radioGroup.js b/uni_modules/uview-plus/components/u-radio-group/radioGroup.js
new file mode 100644
index 0000000..7bba89b
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-radio-group/radioGroup.js
@@ -0,0 +1,31 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : CPS
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/radioGroup.js
+ */
+export default {
+ // radio-group组件
+ radioGroup: {
+ value: '',
+ disabled: false,
+ shape: 'circle',
+ activeColor: '#2979ff',
+ inactiveColor: '#c8c9cc',
+ name: '',
+ size: 18,
+ placement: 'row',
+ label: '',
+ labelColor: '#303133',
+ labelSize: 14,
+ labelDisabled: false,
+ iconColor: '#ffffff',
+ iconSize: 12,
+ borderBottom: false,
+ iconPlacement: 'left',
+ gap: "10px"
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-radio-group/u-radio-group.vue b/uni_modules/uview-plus/components/u-radio-group/u-radio-group.vue
new file mode 100644
index 0000000..6ec9435
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-radio-group/u-radio-group.vue
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-radio/props.js b/uni_modules/uview-plus/components/u-radio/props.js
new file mode 100644
index 0000000..8862f7f
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-radio/props.js
@@ -0,0 +1,71 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // radio的名称
+ name: {
+ type: [String, Number, Boolean],
+ default: () => defProps.radio.name
+ },
+ // 形状,square为方形,circle为圆型
+ shape: {
+ type: String,
+ default: () => defProps.radio.shape
+ },
+ // 是否禁用
+ disabled: {
+ type: [String, Boolean],
+ default: () => defProps.radio.disabled
+ },
+ // 是否禁止点击提示语选中单选框
+ labelDisabled: {
+ type: [String, Boolean],
+ default: () => defProps.radio.labelDisabled
+ },
+ // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
+ activeColor: {
+ type: String,
+ default: () => defProps.radio.activeColor
+ },
+ // 未选中的颜色
+ inactiveColor: {
+ type: String,
+ default: () => defProps.radio.inactiveColor
+ },
+ // 图标的大小,单位px
+ iconSize: {
+ type: [String, Number],
+ default: () => defProps.radio.iconSize
+ },
+ // label的字体大小,px单位
+ labelSize: {
+ type: [String, Number],
+ default: () => defProps.radio.labelSize
+ },
+ // label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式
+ label: {
+ type: [String, Number],
+ default: () => defProps.radio.label
+ },
+ // 整体的大小
+ size: {
+ type: [String, Number],
+ default: () => defProps.radio.size
+ },
+ // 图标颜色
+ color: {
+ type: String,
+ default: () => defProps.radio.color
+ },
+ // label的颜色
+ labelColor: {
+ type: String,
+ default: () => defProps.radio.labelColor
+ },
+ // 图标颜色
+ iconColor: {
+ type: String,
+ default: () => defProps.radio.iconColor
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-radio/radio.js b/uni_modules/uview-plus/components/u-radio/radio.js
new file mode 100644
index 0000000..da4762d
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-radio/radio.js
@@ -0,0 +1,27 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/radio.js
+ */
+export default {
+ // radio组件
+ radio: {
+ name: '',
+ shape: '',
+ disabled: '',
+ labelDisabled: '',
+ activeColor: '',
+ inactiveColor: '',
+ iconSize: '',
+ labelSize: '',
+ label: '',
+ labelColor: '',
+ size: '',
+ iconColor: '',
+ placement: ''
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-radio/u-radio.vue b/uni_modules/uview-plus/components/u-radio/u-radio.vue
new file mode 100644
index 0000000..372f943
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-radio/u-radio.vue
@@ -0,0 +1,350 @@
+
+
+
+
+
+
+
+
+
+ {{label}}
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-rate/props.js b/uni_modules/uview-plus/components/u-rate/props.js
new file mode 100644
index 0000000..0e099ef
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-rate/props.js
@@ -0,0 +1,80 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // #ifdef VUE3
+ // 用于v-model双向绑定选中的星星数量
+ modelValue: {
+ type: [String, Number],
+ default: () => defProps.rate.value
+ },
+ // #endif
+ // #ifdef VUE2
+ // 用于v-model双向绑定选中的星星数量
+ value: {
+ type: [String, Number],
+ default: () => defProps.rate.value
+ },
+ // #endif
+ // 要显示的星星数量
+ count: {
+ type: [String, Number],
+ default: () => defProps.rate.count
+ },
+ // 是否不可选中
+ disabled: {
+ type: Boolean,
+ default: () => defProps.rate.disabled
+ },
+ // 是否只读
+ readonly: {
+ type: Boolean,
+ default: () => defProps.rate.readonly
+ },
+ // 星星的大小,单位px
+ size: {
+ type: [String, Number],
+ default: () => defProps.rate.size
+ },
+ // 未选中时的颜色
+ inactiveColor: {
+ type: String,
+ default: () => defProps.rate.inactiveColor
+ },
+ // 选中的颜色
+ activeColor: {
+ type: String,
+ default: () => defProps.rate.activeColor
+ },
+ // 星星之间的间距,单位px
+ gutter: {
+ type: [String, Number],
+ default: () => defProps.rate.gutter
+ },
+ // 最少能选择的星星个数
+ minCount: {
+ type: [String, Number],
+ default: () => defProps.rate.minCount
+ },
+ // 是否允许半星
+ allowHalf: {
+ type: Boolean,
+ default: () => defProps.rate.allowHalf
+ },
+ // 选中时的图标(星星)
+ activeIcon: {
+ type: String,
+ default: () => defProps.rate.activeIcon
+ },
+ // 未选中时的图标(星星)
+ inactiveIcon: {
+ type: String,
+ default: () => defProps.rate.inactiveIcon
+ },
+ // 是否可以通过滑动手势选择评分
+ touchable: {
+ type: Boolean,
+ default: () => defProps.rate.touchable
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-rate/rate.js b/uni_modules/uview-plus/components/u-rate/rate.js
new file mode 100644
index 0000000..c04ebf8
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-rate/rate.js
@@ -0,0 +1,26 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-18 22:43:09
+ * @FilePath : /uview-plus/libs/config/props/rate.js
+ */
+export default {
+ // rate组件
+ rate: {
+ value: 1,
+ count: 5,
+ disabled: false,
+ size: 18,
+ inactiveColor: '#b2b2b2',
+ activeColor: '#FA3534',
+ gutter: 4,
+ minCount: 1,
+ allowHalf: false,
+ activeIcon: 'star-fill',
+ inactiveIcon: 'star',
+ touchable: true
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-rate/u-rate.vue b/uni_modules/uview-plus/components/u-rate/u-rate.vue
new file mode 100644
index 0000000..3a8577d
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-rate/u-rate.vue
@@ -0,0 +1,327 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-read-more/props.js b/uni_modules/uview-plus/components/u-read-more/props.js
new file mode 100644
index 0000000..57c7a03
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-read-more/props.js
@@ -0,0 +1,63 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 默认的显示占位高度
+ showHeight: {
+ type: [String, Number],
+ default: () => defProps.readMore.showHeight
+ },
+ // 展开后是否显示"收起"按钮
+ toggle: {
+ type: Boolean,
+ default: () => defProps.readMore.toggle
+ },
+ // 关闭时的提示文字
+ closeText: {
+ type: String,
+ default: () => defProps.readMore.closeText
+ },
+ // 展开时的提示文字
+ openText: {
+ type: String,
+ default: () => defProps.readMore.openText
+ },
+ // 提示的文字颜色
+ color: {
+ type: String,
+ default: () => defProps.readMore.color
+ },
+ // 提示文字的大小
+ fontSize: {
+ type: [String, Number],
+ default: () => defProps.readMore.fontSize
+ },
+ // 是否显示阴影
+ // 此参数不能写在props/readMore.js中进行默认配置,因为使用了条件编译,在外部js中
+ // uni无法准确识别当前是否处于nvue还是非nvue下
+ shadowStyle: {
+ type: Object,
+ default: () => ({
+ // #ifndef APP-NVUE
+ backgroundImage: 'linear-gradient(-180deg, rgba(255, 255, 255, 0) 0%, #fff 80%)',
+ // #endif
+ // #ifdef APP-NVUE
+ // nvue上不支持设置复杂的backgroundImage属性
+ backgroundImage: 'linear-gradient(to top, #fff, rgba(255, 255, 255, 0.5))',
+ // #endif
+ paddingTop: '100px',
+ marginTop: '-100px'
+ })
+ },
+ // 段落首行缩进的字符个数
+ textIndent: {
+ type: String,
+ default: () => defProps.readMore.textIndent
+ },
+ // open和close事件时,将此参数返回在回调参数中
+ name: {
+ type: [String, Number],
+ default: () => defProps.readMore.name
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-read-more/readMore.js b/uni_modules/uview-plus/components/u-read-more/readMore.js
new file mode 100644
index 0000000..f644f07
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-read-more/readMore.js
@@ -0,0 +1,23 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/readMore.js
+ */
+import { t } from '../../libs/i18n'
+export default {
+ // readMore
+ readMore: {
+ showHeight: 400,
+ toggle: false,
+ closeText: t("up.readMore.expand"),
+ openText: t("up.readMore.fold"),
+ color: '#2979ff',
+ fontSize: 14,
+ textIndent: '2em',
+ name: ''
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-read-more/u-read-more.vue b/uni_modules/uview-plus/components/u-read-more/u-read-more.vue
new file mode 100644
index 0000000..fec02e1
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-read-more/u-read-more.vue
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-refresh-virtual-list/u-refresh-virtual-list.vue b/uni_modules/uview-plus/components/u-refresh-virtual-list/u-refresh-virtual-list.vue
new file mode 100644
index 0000000..9313ac3
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-refresh-virtual-list/u-refresh-virtual-list.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-row-notice/props.js b/uni_modules/uview-plus/components/u-row-notice/props.js
new file mode 100644
index 0000000..0223608
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-row-notice/props.js
@@ -0,0 +1,41 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 显示的内容,字符串
+ text: {
+ type: String,
+ default: () => defProps.rowNotice.text
+ },
+ // 是否显示左侧的音量图标
+ icon: {
+ type: String,
+ default: () => defProps.rowNotice.icon
+ },
+ // 通告模式,link-显示右箭头,closable-显示右侧关闭图标
+ mode: {
+ type: String,
+ default: () => defProps.rowNotice.mode
+ },
+ // 文字颜色,各图标也会使用文字颜色
+ color: {
+ type: String,
+ default: () => defProps.rowNotice.color
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: () => defProps.rowNotice.bgColor
+ },
+ // 字体大小,单位px
+ fontSize: {
+ type: [String, Number],
+ default: () => defProps.rowNotice.fontSize
+ },
+ // 水平滚动时的滚动速度,即每秒滚动多少px(rpx),这有利于控制文字无论多少时,都能有一个恒定的速度
+ speed: {
+ type: [String, Number],
+ default: () => defProps.rowNotice.speed
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-row-notice/rowNotice.js b/uni_modules/uview-plus/components/u-row-notice/rowNotice.js
new file mode 100644
index 0000000..53fc3fb
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-row-notice/rowNotice.js
@@ -0,0 +1,21 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/rowNotice.js
+ */
+export default {
+ // rowNotice
+ rowNotice: {
+ text: '',
+ icon: 'volume',
+ mode: '',
+ color: '#f9ae3d',
+ bgColor: '#fdf6ec',
+ fontSize: 14,
+ speed: 80
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-row-notice/u-row-notice.vue b/uni_modules/uview-plus/components/u-row-notice/u-row-notice.vue
new file mode 100644
index 0000000..0165ba8
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-row-notice/u-row-notice.vue
@@ -0,0 +1,336 @@
+
+
+
+
+
+
+
+
+
+ {{item}}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-row/props.js b/uni_modules/uview-plus/components/u-row/props.js
new file mode 100644
index 0000000..cc6a5a7
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-row/props.js
@@ -0,0 +1,21 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 给col添加间距,左右边距各占一半
+ gutter: {
+ type: [String, Number],
+ default: () => defProps.row.gutter
+ },
+ // 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`)
+ justify: {
+ type: String,
+ default: () => defProps.row.justify
+ },
+ // 垂直对齐方式,可选值为top、center、bottom
+ align: {
+ type: String,
+ default: () => defProps.row.align
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-row/row.js b/uni_modules/uview-plus/components/u-row/row.js
new file mode 100644
index 0000000..3363f1e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-row/row.js
@@ -0,0 +1,17 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/row.js
+ */
+export default {
+ // row
+ row: {
+ gutter: 0,
+ justify: 'start',
+ align: 'center'
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-row/u-row.vue b/uni_modules/uview-plus/components/u-row/u-row.vue
new file mode 100644
index 0000000..e89a941
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-row/u-row.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-safe-bottom/props.js b/uni_modules/uview-plus/components/u-safe-bottom/props.js
new file mode 100644
index 0000000..d4f490d
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-safe-bottom/props.js
@@ -0,0 +1,7 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+ props: {
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-safe-bottom/u-safe-bottom.vue b/uni_modules/uview-plus/components/u-safe-bottom/u-safe-bottom.vue
new file mode 100644
index 0000000..b9ea841
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-safe-bottom/u-safe-bottom.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-scroll-list/nvue.js b/uni_modules/uview-plus/components/u-scroll-list/nvue.js
new file mode 100644
index 0000000..fd0b825
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-scroll-list/nvue.js
@@ -0,0 +1,31 @@
+// 引入bindingx,此库类似于微信小程序wxs,目的是让js运行在视图层,减少视图层和逻辑层的通信折损
+const BindingX = uni.requireNativePlugin('bindingx')
+import { os } from '../../libs/function/index';
+export default {
+ methods: {
+ // 此处不写注释,请自行体会
+ nvueScrollHandler(e) {
+ const anchor = this.$refs['u-scroll-list__scroll-view'].ref
+ let element = {}
+ if (this.$refs['u-scroll-list__indicator__line__bar']) {
+ element = this.$refs['u-scroll-list__indicator__line__bar'].ref
+ }
+ const scrollLeft = e.contentOffset.x
+ const contentSize = e.contentSize.width
+ const { scrollWidth } = this
+ const barAllMoveWidth = this.indicatorWidth - this.indicatorBarWidth
+ // 在安卓和iOS上,需要除的倍数不一样,iOS需要除以2
+ const actionNum = os() === 'ios' ? 2 : 1
+ const expression = `(x / ${actionNum}) / ${contentSize - scrollWidth} * ${barAllMoveWidth}`
+ BindingX.bind({
+ anchor,
+ eventType: 'scroll',
+ props: [{
+ element,
+ property: 'transform.translateX',
+ expression
+ }]
+ })
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-scroll-list/props.js b/uni_modules/uview-plus/components/u-scroll-list/props.js
new file mode 100644
index 0000000..d77f536
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-scroll-list/props.js
@@ -0,0 +1,36 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 指示器的整体宽度
+ indicatorWidth: {
+ type: [String, Number],
+ default: () => defProps.scrollList.indicatorWidth
+ },
+ // 滑块的宽度
+ indicatorBarWidth: {
+ type: [String, Number],
+ default: () => defProps.scrollList.indicatorBarWidth
+ },
+ // 是否显示面板指示器
+ indicator: {
+ type: Boolean,
+ default: () => defProps.scrollList.indicator
+ },
+ // 指示器非激活颜色
+ indicatorColor: {
+ type: String,
+ default: () => defProps.scrollList.indicatorColor
+ },
+ // 指示器的激活颜色
+ indicatorActiveColor: {
+ type: String,
+ default: () => defProps.scrollList.indicatorActiveColor
+ },
+ // 指示器样式,可通过bottom,left,right进行定位
+ indicatorStyle: {
+ type: [String, Object],
+ default: () => defProps.scrollList.indicatorStyle
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-scroll-list/scrollList.js b/uni_modules/uview-plus/components/u-scroll-list/scrollList.js
new file mode 100644
index 0000000..15e3ae0
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-scroll-list/scrollList.js
@@ -0,0 +1,20 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/scrollList.js
+ */
+export default {
+ // scrollList
+ scrollList: {
+ indicatorWidth: 50,
+ indicatorBarWidth: 20,
+ indicator: true,
+ indicatorColor: '#f2f2f2',
+ indicatorActiveColor: '#3c9cff',
+ indicatorStyle: ''
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-scroll-list/scrollWxs.wxs b/uni_modules/uview-plus/components/u-scroll-list/scrollWxs.wxs
new file mode 100644
index 0000000..ce94f1d
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-scroll-list/scrollWxs.wxs
@@ -0,0 +1,50 @@
+function scroll(event, ownerInstance) {
+ // detail中含有scroll-view的信息,比如scroll-view的实际宽度,当前时间点scroll-view的移动距离等
+ var detail = event.detail
+ var scrollWidth = detail.scrollWidth
+ var scrollLeft = detail.scrollLeft
+ // 获取当前组件的dataset,说白了就是祸国殃民的腾xun搞出来的垃ji
+ var dataset = event.currentTarget.dataset
+ // 此为scroll-view外部包裹元素的宽度
+ // 某些HX版本(3.1.18),发现view元素中大写的data-scrollWidth,在wxs中,变成了全部小写,所以这里需要特别处理
+ var scrollComponentWidth = dataset.scrollWidth || dataset.scrollwidth || 0
+ // 指示器和滑块的宽度
+ var indicatorWidth = dataset.indicatorWidth || dataset.indicatorwidth || 0
+ var barWidth = dataset.barWidth || dataset.barwidth || 0
+ // 此处的计算理由为:scroll-view的滚动距离与目标滚动距离(scroll-view的实际宽度减去包裹元素的宽度)之比,等于滑块当前移动距离与总需
+ // 滑动距离(指示器的总宽度减去滑块宽度)的比值
+ var x = scrollLeft / (scrollWidth - scrollComponentWidth) * (indicatorWidth - barWidth)
+ setBarStyle(ownerInstance, x)
+}
+
+// 由于webview的无能,无法保证scroll-view在滑动过程中,一直触发scroll事件,会导致
+// 无法监听到某些滚动值,当在首尾临界值无法监听到时,这是致命的,因为错失这些值会导致滑块无法回到起点和终点
+// 所以这里需要对临界值做监听并处理
+function scrolltolower(event, ownerInstance) {
+ ownerInstance.callMethod('scrollEvent', 'right')
+ // 获取当前组件的dataset
+ var dataset = event.currentTarget.dataset
+ // 指示器和滑块的宽度
+ var indicatorWidth = dataset.indicatorWidth || dataset.indicatorwidth || 0
+ var barWidth = dataset.barWidth || dataset.barwidth || 0
+ // scroll-view滚动到右边终点时,将滑块也设置为到右边的终点,它所需移动的距离为:指示器宽度 - 滑块宽度
+ setBarStyle(ownerInstance, indicatorWidth - barWidth)
+}
+
+function scrolltoupper(event, ownerInstance) {
+ ownerInstance.callMethod('scrollEvent', 'left')
+ // 滚动到左边时,将滑块设置为0的偏移距离,回到起点
+ setBarStyle(ownerInstance, 0)
+}
+
+function setBarStyle(ownerInstance, x) {
+ ownerInstance.selectComponent('.u-scroll-list__indicator__line__bar') && ownerInstance.selectComponent('.u-scroll-list__indicator__line__bar').setStyle({
+ transform: 'translateX(' + x + 'px)'
+ })
+}
+
+module.exports = {
+ scroll: scroll,
+ scrolltolower: scrolltolower,
+ scrolltoupper: scrolltoupper
+}
diff --git a/uni_modules/uview-plus/components/u-scroll-list/u-scroll-list.vue b/uni_modules/uview-plus/components/u-scroll-list/u-scroll-list.vue
new file mode 100644
index 0000000..6e80fea
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-scroll-list/u-scroll-list.vue
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-search/props.js b/uni_modules/uview-plus/components/u-search/props.js
new file mode 100644
index 0000000..8925ec9
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-search/props.js
@@ -0,0 +1,156 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+ props: {
+ // #ifdef VUE3
+ // 绑定的值
+ modelValue: {
+ type: [String, Number],
+ default: () => defProps.search.value
+ },
+ // #endif
+ // #ifdef VUE2
+ // 绑定的值
+ value: {
+ type: [String, Number],
+ default: () => defProps.search.value
+ },
+ // #endif
+ // 搜索框形状,round-圆形,square-方形
+ shape: {
+ type: String,
+ default: () => defProps.search.shape
+ },
+ // 搜索框背景色
+ bgColor: {
+ type: String,
+ default: () => defProps.search.bgColor
+ },
+ // 占位提示文字
+ placeholder: {
+ type: String,
+ default: () => defProps.search.placeholder
+ },
+ // 是否启用清除控件
+ clearabled: {
+ type: Boolean,
+ default: () => defProps.search.clearabled
+ },
+ // 是否仅聚焦时显示清除控件
+ onlyClearableOnFocused: {
+ type: Boolean,
+ default: true
+ },
+ // 是否自动聚焦
+ focus: {
+ type: Boolean,
+ default: () => defProps.search.focus
+ },
+ // 是否在搜索框右侧显示取消按钮
+ showAction: {
+ type: Boolean,
+ default: () => defProps.search.showAction
+ },
+ // 右侧取消按钮文字
+ actionText: {
+ type: String,
+ default: () => defProps.search.actionText
+ },
+ // 搜索框左侧文本
+ label: {
+ type: [String, Number, null],
+ default: () => defProps.search.label
+ },
+ // 输入框内容对齐方式,可选值为:left|center|right
+ inputAlign: {
+ type: String,
+ default: () => defProps.search.inputAlign
+ },
+ // 是否启用输入框
+ disabled: {
+ type: Boolean,
+ default: () => defProps.search.disabled
+ },
+ // 开启showAction时,是否在input获取焦点时才显示
+ animation: {
+ type: Boolean,
+ default: () => defProps.search.animation
+ },
+ // 边框颜色,只要配置了颜色,才会有边框
+ borderColor: {
+ type: String,
+ default: () => defProps.search.borderColor
+ },
+ // 搜索图标的颜色,默认同输入框字体颜色
+ searchIconColor: {
+ type: String,
+ default: () => defProps.search.searchIconColor
+ },
+ // 搜索图标的大小
+ searchIconSize: {
+ type: [Number, String],
+ default: () => defProps.search.searchIconSize
+ },
+ // 输入框字体颜色
+ color: {
+ type: String,
+ default: () => defProps.search.color
+ },
+ // placeholder的颜色
+ placeholderColor: {
+ type: String,
+ default: () => defProps.search.placeholderColor
+ },
+ // 左边输入框的图标,可以为uView图标名称或图片路径
+ searchIcon: {
+ type: String,
+ default: () => defProps.search.searchIcon
+ },
+ // 组件与其他上下左右元素之间的距离,带单位的字符串形式,如"30px"
+ margin: {
+ type: String,
+ default: () => defProps.search.margin
+ },
+ // 应该是uView-plus版本新增的,用于控制搜索图标的插槽位置
+ iconPosition: {
+ type: String,
+ default: () => defProps.search.iconPosition
+ },
+ // 输入框最大能输入的长度,-1为不限制长度
+ maxlength: {
+ type: [String, Number],
+ default: () => defProps.search.maxlength
+ },
+ // 输入框高度,单位px
+ height: {
+ type: [String, Number],
+ default: () => defProps.search.height
+ },
+ // 键盘弹起时,是否自动上推页面
+ adjustPosition: {
+ type: Boolean,
+ default: () => defProps.search.adjustPosition
+ },
+ // 键盘收起时,是否自动失去焦点
+ autoBlur: {
+ type: Boolean,
+ default: () => defProps.search.autoBlur
+ },
+ // 输入框的样式,对象形式
+ inputStyle: {
+ type: Object,
+ default: () => defProps.search.inputStyle
+ },
+ // 右侧控件的样式,对象形式
+ actionStyle: {
+ type: Object,
+ default: () => defProps.search.actionStyle
+ },
+ // 自定义样式,对象形式
+ customStyle: {
+ type: Object,
+ default: () => defProps.search.customStyle
+ }
+ }
+})
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-search/search.js b/uni_modules/uview-plus/components/u-search/search.js
new file mode 100644
index 0000000..5daf277
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-search/search.js
@@ -0,0 +1,41 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/search.js
+ */
+import { t } from '../../libs/i18n'
+export default {
+ // search
+ search: {
+ shape: 'round',
+ bgColor: '#f2f2f2',
+ placeholder: t("up.search.placeholder"),
+ clearabled: true,
+ focus: false,
+ showAction: true,
+ actionStyle: {},
+ actionText: t("up.common.search"),
+ inputAlign: 'left',
+ inputStyle: {},
+ disabled: false,
+ borderColor: 'transparent',
+ searchIconColor: '#909399',
+ searchIconSize: 22,
+ color: '#606266',
+ placeholderColor: '#909399',
+ searchIcon: 'search',
+ iconPosition: 'left',
+ margin: '0',
+ animation: false,
+ value: '',
+ maxlength: '-1',
+ height: 32,
+ label: null,
+ adjustPosition: true,
+ autoBlur: true
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-search/u-search.vue b/uni_modules/uview-plus/components/u-search/u-search.vue
new file mode 100644
index 0000000..8a618b1
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-search/u-search.vue
@@ -0,0 +1,354 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ actionText }}
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-section/section.js b/uni_modules/uview-plus/components/u-section/section.js
new file mode 100644
index 0000000..d33780e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-section/section.js
@@ -0,0 +1,25 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/section.js
+ */
+import { t } from '../../libs/i18n'
+export default {
+ // u-section组件
+ section: {
+ title: '',
+ subTitle: t("up.common.more"),
+ right: true,
+ fontSize: 15,
+ bold: true,
+ color: '#303133',
+ subColor: '#909399',
+ showLine: true,
+ lineColor: '',
+ arrow: true
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-select/u-select.vue b/uni_modules/uview-plus/components/u-select/u-select.vue
new file mode 100644
index 0000000..6441ac5
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-select/u-select.vue
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+ {{ currentLabel }}
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item[labelName]}}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-short-video/u-short-video.vue b/uni_modules/uview-plus/components/u-short-video/u-short-video.vue
new file mode 100644
index 0000000..b755043
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-short-video/u-short-video.vue
@@ -0,0 +1,463 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.author.name }}
+ {{ item.author.desc }}
+
+
+ 关注
+
+
+
+
+
+
+
+
+ {{ item.likeCount }}
+
+
+
+ {{ item.commentCount }}
+
+
+
+ {{ item.shareCount }}
+
+
+
+ {{ item.collectCount }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-signature/u-signature.vue b/uni_modules/uview-plus/components/u-signature/u-signature.vue
new file mode 100644
index 0000000..7f87713
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-signature/u-signature.vue
@@ -0,0 +1,486 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t("up.signature.penSize") }}:
+
+
+
+
+
+
+
+ {{ t("up.signature.penColor") }}:
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-skeleton/props.js b/uni_modules/uview-plus/components/u-skeleton/props.js
new file mode 100644
index 0000000..ea6c07a
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-skeleton/props.js
@@ -0,0 +1,61 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 是否展示骨架组件
+ loading: {
+ type: Boolean,
+ default: () => defProps.skeleton.loading
+ },
+ // 是否开启动画效果
+ animate: {
+ type: Boolean,
+ default: () => defProps.skeleton.animate
+ },
+ // 段落占位图行数
+ rows: {
+ type: [String, Number],
+ default: () => defProps.skeleton.rows
+ },
+ // 段落占位图的宽度
+ rowsWidth: {
+ type: [String, Number, Array],
+ default: () => defProps.skeleton.rowsWidth
+ },
+ // 段落占位图的高度
+ rowsHeight: {
+ type: [String, Number, Array],
+ default: () => defProps.skeleton.rowsHeight
+ },
+ // 是否展示标题占位图
+ title: {
+ type: Boolean,
+ default: () => defProps.skeleton.title
+ },
+ // 段落标题的宽度
+ titleWidth: {
+ type: [String, Number],
+ default: () => defProps.skeleton.titleWidth
+ },
+ // 段落标题的高度
+ titleHeight: {
+ type: [String, Number],
+ default: () => defProps.skeleton.titleHeight
+ },
+ // 是否展示头像占位图
+ avatar: {
+ type: Boolean,
+ default: () => defProps.skeleton.avatar
+ },
+ // 头像占位图大小
+ avatarSize: {
+ type: [String, Number],
+ default: () => defProps.skeleton.avatarSize
+ },
+ // 头像占位图的形状,circle-圆形,square-方形
+ avatarShape: {
+ type: String,
+ default: () => defProps.skeleton.avatarShape
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-skeleton/skeleton.js b/uni_modules/uview-plus/components/u-skeleton/skeleton.js
new file mode 100644
index 0000000..6b421a9
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-skeleton/skeleton.js
@@ -0,0 +1,25 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/skeleton.js
+ */
+export default {
+ // skeleton
+ skeleton: {
+ loading: true,
+ animate: true,
+ rows: 0,
+ rowsWidth: '100%',
+ rowsHeight: 18,
+ title: true,
+ titleWidth: '50%',
+ titleHeight: 18,
+ avatar: false,
+ avatarSize: 32,
+ avatarShape: 'circle'
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-skeleton/u-skeleton.vue b/uni_modules/uview-plus/components/u-skeleton/u-skeleton.vue
new file mode 100644
index 0000000..3716edd
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-skeleton/u-skeleton.vue
@@ -0,0 +1,248 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-slider/mpother.js b/uni_modules/uview-plus/components/u-slider/mpother.js
new file mode 100644
index 0000000..040c848
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-slider/mpother.js
@@ -0,0 +1,113 @@
+/**
+ * 使用普通的js方案实现slider
+ */
+export default {
+ watch: {
+ value(n) {
+ // 只有在非滑动状态时,才可以通过value更新滑块值,这里监听,是为了让用户触发
+ if (this.status === 'end') {
+ this.updateSliderPlacement(n, true)
+ }
+ }
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ this.getSliderRect()
+ },
+ // 获取slider尺寸
+ getSliderRect() {
+ // 获取滑块条的尺寸信息
+ setTimeout(() => {
+ this.$uGetRect('.u-slider').then((rect) => {
+ this.sliderRect = rect
+ this.updateSliderPlacement(this.value, true)
+ })
+ }, 10)
+ },
+ // 是否可以操作
+ canNotDo() {
+ return this.disabled
+ },
+ // 获取当前手势点的X轴位移值
+ getTouchX(e) {
+ return e.touches[0].clientX
+ },
+ formatStep(value) {
+ // 移动点占总长度的百分比
+ return Math.round(Math.max(this.min, Math.min(value, this.max)) / this.step) * this.step
+ },
+ // 发出事件
+ emitEvent(event, value) {
+ this.$emit(event, value || this.value)
+ },
+ // 标记当前手势的状态
+ setTouchStatus(status) {
+ this.status = status
+ },
+ onTouchStart(e) {
+ if (this.canNotDo()) {
+ return
+ }
+ // 标示当前的状态为开始触摸滑动
+ this.emitEvent('start')
+ this.setTouchStatus('start')
+ },
+ onTouchMove(e) {
+ if (this.canNotDo()) {
+ return
+ }
+ // 滑块的左边不一定跟屏幕左边接壤,所以需要减去最外层父元素的左边值
+ const x = this.getTouchX(e)
+ const { left, width } = this.sliderRect
+ const distanceX = x - left
+ // 获得移动距离对整个滑块的百分比值,此为带有多位小数的值,不能用此更新视图
+ // 否则造成通信阻塞,需要每改变一个step值时修改一次视图
+ const percent = (distanceX / width) * 100
+ this.setTouchStatus('moving')
+ this.updateSliderPlacement(percent, true, 'moving')
+ },
+ onTouchEnd() {
+ if (this.canNotDo()) {
+ return
+ }
+ this.emitEvent('end')
+ this.setTouchStatus('end')
+ },
+ // 设置滑点的位置
+ updateSliderPlacement(value, drag, event) {
+ // 去掉小数部分,同时也是对step步进的处理
+ const { width } = this.sliderRect
+ const percent = this.formatStep(value)
+ // 设置移动的值
+ const barStyle = {
+ width: `${percent / 100 * width}px`
+ }
+ // 移动期间无需过渡动画
+ if (drag === true) {
+ barStyle.transition = 'none'
+ } else {
+ // 非移动期间,删掉对过渡为空的声明,让css中的声明起效
+ delete barStyle.transition
+ }
+ // 修改value值
+ this.$emit('input', percent)
+ // 事件的名称
+ if (event) {
+ this.emitEvent(event, percent)
+ }
+ this.barStyle = barStyle
+ },
+ onClick(e) {
+ if (this.canNotDo()) {
+ return
+ }
+ // 直接点击滑块的情况,计算方式与onTouchMove方法相同
+ const { left, width } = this.sliderRect
+ const value = ((e.detail.x - left) / width) * 100
+ this.updateSliderPlacement(value, false, 'click')
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-slider/mpwxs.js b/uni_modules/uview-plus/components/u-slider/mpwxs.js
new file mode 100644
index 0000000..8d051fd
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-slider/mpwxs.js
@@ -0,0 +1,43 @@
+import { sleep } from '../../libs/function/index';
+export default {
+ data() {
+ return {
+ sliderRect: {},
+ info: {
+ width: null,
+ left: null,
+ step: this.step,
+ disabled: this.disabled,
+ min: this.min,
+ max: this.max,
+ value: this.value
+ }
+ }
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ this.getSliderRect()
+ },
+ // 获取slider尺寸
+ getSliderRect() {
+ // 获取滑块条的尺寸信息
+ sleep().then(() => {
+ this.$uGetRect('.u-slider').then((rect) => {
+ this.info.width = rect.width
+ this.info.left = rect.left
+ })
+ })
+ },
+ // 此方法由wxs调用,用于修改v-model绑定的值
+ updateValue(value) {
+ this.$emit('input', value)
+ },
+ // 此方法由wxs调用,发出事件
+ emitEvent(e) {
+ this.$emit(e.event, e.value ? e.value : this.value)
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-slider/mpwxs.wxs b/uni_modules/uview-plus/components/u-slider/mpwxs.wxs
new file mode 100644
index 0000000..847df4a
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-slider/mpwxs.wxs
@@ -0,0 +1,121 @@
+/**
+ * 使用wxs方案实现slider
+ * 兼容微信,QQ,H5,Vue版的安卓和iOS
+ */
+/**
+ * 开始滑动操作
+ * @param {Object} e
+ * @param {Object} ownerInstance
+ */
+function onTouchMove(e, ownerInstance) {
+ // wxs事件对象下有一个instance属性,表示当前触发此事件的组件的实例,通过该实例,可以获取相关的dataset,设置样式等信息
+ // https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html
+ var instance = e.instance;
+ // getState()为一个对象,挂载在instance上,类似组件的data一样,可以存放一些变量,供以后的触发事件中使用
+ var state = instance.getState()
+
+ // 滑块组件的整体尺寸信息
+ var mp = state.mp
+ if(mp.disabled) {
+ return
+ }
+
+ var distanceX = getTouchX(e) - mp.left
+ // 获得移动距离对整个滑块的百分比值,此为带有多位小数的值,step大于1时,不能用此更新视图
+ var percent = (distanceX / mp.width) * 100
+
+ updateSliderPlacement(instance, ownerInstance, percent, 'moving')
+
+ // 阻止页面滚动,可以保证在滑动过程中,不让页面可以上下滚动,造成不好的体验
+ e.stopPropagation && e.stopPropagation()
+ e.preventDefault && e.preventDefault()
+}
+
+function onClick(e, ownerInstance) {
+ var instance = e.instance
+ var state = instance.getState()
+ var mp = state.mp
+ if(mp.disabled) {
+ return
+ }
+
+ // 直接点击滑块的情况,计算方式与onTouchMove方法相同
+ var value = ((e.detail.x - mp.left) / mp.width) * 100
+ updateSliderPlacement(instance, ownerInstance, value, 'click')
+}
+
+function sizeReady(newValue, oldValue, ownerInstance, instance) {
+ // 页面初始化时候,也会触发此方法,传递的值为空,这里不执行往后的逻辑
+ if(!newValue || newValue.disabled) {
+ return
+ }
+ var state = instance.getState()
+ state.mp = newValue
+ updateSliderPlacement(instance, ownerInstance, newValue.value)
+}
+
+// 设置滑点的位置
+function updateSliderPlacement(instance, ownerInstance, value, event) {
+ var state = instance.getState()
+ var mp = state.mp
+ if(mp.disabled) {
+ return
+ }
+
+ var percent = 0
+ if (mp.step > 1) {
+ // 如果step步进大于1,需要跳步,所以需要使用Math.round进行取整
+ percent = Math.round(Math.max(mp.min, Math.min(value, mp.max)) / mp.step) * mp.step
+ } else {
+ // 当step=1时,无需跳步,充分利用wxs性能,滑块实时跟随手势,达到丝滑的效果
+ percent = Math.max(mp.min, Math.min(value, mp.max))
+ }
+ // 返回组件的实例
+ var gapInstance = ownerInstance.selectComponent('.u-slider__gap')
+ // 在移动期间,不允许transition动画,否则会造成卡顿
+ gapInstance[event === 'click' ? 'addClass' : 'removeClass']('u-slider__gap--ani')
+ // 调用逻辑层的方法,修改v-model绑定的值
+ ownerInstance.callMethod('updateValue', Math.round(percent))
+ if(event) {
+ ownerInstance.callMethod('emitEvent', {
+ event: event,
+ value: Math.round(percent)
+ })
+ }
+
+ // 设置移动的值
+ gapInstance.requestAnimationFrame(function() {
+ gapInstance.setStyle({
+ width: percent / 100 * mp.width + 'px',
+ })
+ })
+}
+
+// 开始滑动
+function onTouchStart(e, ownerInstance) {
+ ownerInstance.callMethod('emitEvent', {
+ event: 'start',
+ value: null
+ })
+}
+
+// 停止滑动
+function onTouchEnd(e, ownerInstance) {
+ ownerInstance.callMethod('emitEvent', {
+ event: 'end',
+ value: null
+ })
+}
+
+// 获取当前手势点的X轴位移值
+function getTouchX(e) {
+ return e.touches[0].clientX
+}
+
+module.exports = {
+ onTouchStart: onTouchStart,
+ onTouchMove: onTouchMove,
+ onTouchEnd: onTouchEnd,
+ sizeReady: sizeReady,
+ onClick: onClick
+}
diff --git a/uni_modules/uview-plus/components/u-slider/nvue.js b/uni_modules/uview-plus/components/u-slider/nvue.js
new file mode 100644
index 0000000..53380ff
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-slider/nvue.js
@@ -0,0 +1,193 @@
+/**
+ * 使用bindingx方案实现slider
+ * 只能使用于nvue下
+ */
+// 引入bindingx,此库类似于微信小程序wxs,目的是让js运行在视图层,减少视图层和逻辑层的通信折损
+const BindingX = uni.requireNativePlugin('bindingx')
+// nvue操作dom的库,用于获取dom的尺寸信息
+const dom = uni.requireNativePlugin('dom')
+// nvue中用于操作元素动画的库,类似于uni.animation,只不过uni.animation不能用于nvue
+const animation = uni.requireNativePlugin('animation')
+import { range } from '../../libs/function/index';
+export default {
+ data() {
+ return {
+ // 位移的偏移量
+ x: 0,
+ // 是否正在触摸过程中,用于标记动画类是否添加或移除
+ touching: false,
+ changeFromInside: false
+ }
+ },
+ watch: {
+ // 监听vlaue的变化,此变化可能是由于内部修改v-model的值,或者外部
+ // 从服务端获取一个值后,赋值给slider的v-model而导致的
+ value(n) {
+ if (!this.changeFromInside) {
+ this.initX()
+ } else {
+ this.changeFromInside = false
+ }
+ }
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ // 更新滑块尺寸信息
+ this.getSliderRect().then((size) => {
+ this.sliderRect = size
+ this.initX()
+ })
+ },
+ // 获取节点信息
+ // 获取slider尺寸
+ getSliderRect() {
+ // 获取滑块条的尺寸信息
+ // 通过nvue的dom模块,查询节点信息
+ return new Promise((resolve) => {
+ this.$nextTick(() => {
+ dom.getComponentRect(this.$refs.slider, (res) => {
+ resolve(res.size)
+ })
+ })
+ })
+ },
+ // 初始化按钮位置
+ initButtonStyle({
+ barStyle,
+ buttonWrapperStyle
+ }) {
+ this.barStyle = barStyle
+ this.buttonWrapperStyle = buttonWrapperStyle
+ },
+ emitEvent(event, value) {
+ this.$emit(event, value || this.value)
+ },
+ // 滑动开始
+ async onTouchStart(e) {
+ // if (this.disabled) return
+ // // 阻止页面滚动,可以保证在滑动过程中,不让页面可以上下滚动,造成不好的体验
+ // e.stopPropagation && e.stopPropagation()
+ // e.preventDefault && e.preventDefault()
+ // // 更新滑块的尺寸信息
+ // this.sliderRect = await this.getSliderRect()
+ // // 标记滑动过程中触摸点的信息
+ // this.touchStart(e)
+ // this.startValue = this.format(this.value)
+ // this.dragStatus = 'start'
+
+ // 标记滑动过程中触摸点的信息
+ // this.touchStart(e)
+ },
+ // 开始滑动
+ onTouchMove(e) {
+ // if (this.disabled) return;
+ // if (this.dragStatus === 'start') {
+ // this.$emit('drag-start')
+ // }
+ // // 标记当前滑动过程中的触点信息,此方法在touch mixin中
+ // this.touchMove(e)
+ // this.dragStatus = 'draging'
+ // const {
+ // width: sliderWidth
+ // } = this.sliderRect
+ // const diff = (this.deltaX / sliderWidth) * this.getRange()
+ // this.newValue = this.startValue + diff
+ // this.updateValue(this.newValue, false, true)
+ // 获取元素ref
+ // const button = this.$refs['nvue-button'].ref
+ // const gap = this.$refs['nvue-gap'].ref
+
+ // animation.transition(gap, {
+ // styles: {
+ // width: `${this.startX + this.deltaX}px`
+ // }
+ // })
+ // // console.log(this.startX + this.deltaX);
+ // animation.transition(button, {
+ // styles: {
+ // transform: `translateX(${this.startX + this.deltaX}px)`
+ // }
+ // })
+ // this.barStyle = {
+ // width: `${this.startX + this.deltaX}px`
+ // }
+ const {
+ x
+ } = this.getTouchPoint(e)
+ this.buttonWrapperStyle = {
+ transform: `translateX(${x}px)`
+ }
+ // this.buttonWrapperStyle = {
+ // transform: `translateX(${this.format(this.startX + this.deltaX)}px)`
+ // }
+ },
+ // onTouchEnd() {
+ // if (this.disabled) return;
+ // if (this.dragStatus === 'draging') {
+ // this.updateValue(this.newValue, true)
+ // this.$emit('drag-end');
+ // }
+ // },
+ updateValue(value, end, drag) {
+ value = this.format(value)
+ const {
+ width: sliderWidth
+ } = this.sliderRect
+ const width = `${((value - this.min) * sliderWidth) / this.getRange()}`
+ this.value = value
+ this.barStyle = {
+ width: `${width}px`
+ }
+ // console.log('width', width);
+ if (drag) {
+ this.$emit('drag', {
+ value
+ })
+ }
+ if (end) {
+ this.$emit('change', value)
+ }
+ if ((drag || end)) {
+ this.changeFromInside = true
+ this.$emit('update', value)
+ }
+ },
+ // 从value的变化,倒推得出x的值该为多少
+ initX() {
+ const {
+ left,
+ width
+ } = this.sliderRect
+ // 得出x的初始偏移值,之所以需要这么做,是因为在bindingX中,触摸滑动时,只能的值本次移动的偏移值
+ // 而无法的值准确的前后移动的两个点的坐标值,weex纯粹为阿里巴巴的KPI(部门业绩考核)产物,也就这样了
+ this.x = this.value / 100 * width
+ // 设置移动的值
+ const barStyle = {
+ width: `${this.x}px`
+ }
+ // 按钮的初始值
+ const buttonWrapperStyle = {
+ transform: `translateX(${this.x - this.blockHeight / 2}px)`
+ }
+ this.initButtonStyle({
+ barStyle,
+ buttonWrapperStyle
+ })
+ },
+ // 移动点占总长度的百分比,此处需要先除以step,是为了保证step大于1时,比如10,那么在滑动11,12px这样的
+ // 距离时,实际上滑块是不会滑动的,到了16,17px,经过四舍五入后,就变成了20px,进行了下一个跳变
+ format(value) {
+ return Math.round(range(this.min, this.max, value) / this.step) * this.step
+ },
+ getRange() {
+ const {
+ max,
+ min
+ } = this
+ return max - min
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-slider/props.js b/uni_modules/uview-plus/components/u-slider/props.js
new file mode 100644
index 0000000..4cbe120
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-slider/props.js
@@ -0,0 +1,95 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+ props: {
+ // 最小可选值
+ min: {
+ type: [Number, String],
+ default: () => defProps.slider.min
+ },
+ // 最大可选值
+ max: {
+ type: [Number, String],
+ default: () => defProps.slider.max
+ },
+ // 步长,取值必须大于 0,并且可被(max - min)整除
+ step: {
+ type: [Number, String],
+ default: () => defProps.slider.step
+ },
+ // #ifdef VUE3
+ // 当前取值
+ modelValue: {
+ type: [String, Number],
+ default: () => defProps.slider.value
+ },
+ // #endif
+ // #ifdef VUE2
+ // 当前取值
+ value: {
+ type: [String, Number],
+ default: () => defProps.slider.value
+ },
+ // #endif
+ // 是否区间模式
+ isRange: {
+ type: Boolean,
+ default: false
+ },
+ // 双滑块时值
+ rangeValue: {
+ type: [Array],
+ default: [0, 0]
+ },
+ // 滑块右侧已选择部分的背景色
+ activeColor: {
+ type: String,
+ default: () => defProps.slider.activeColor
+ },
+ // 滑块左侧未选择部分的背景色
+ inactiveColor: {
+ type: String,
+ default: () => defProps.slider.inactiveColor
+ },
+ // 滑块的大小,取值范围为 12 - 28
+ blockSize: {
+ type: [Number, String],
+ default: () => defProps.slider.blockSize
+ },
+ // 滑块的颜色
+ blockColor: {
+ type: String,
+ default: () => defProps.slider.blockColor
+ },
+ // 用户对滑块的自定义颜色
+ blockStyle: {
+ type: Object,
+ default: () => defProps.slider.blockStyle
+ },
+ // 禁用状态
+ disabled: {
+ type: Boolean,
+ default: () => defProps.slider.disabled
+ },
+ // 是否显示当前的选择值
+ showValue: {
+ type: Boolean,
+ default: () => defProps.slider.showValue
+ },
+ // 是否渲染uni-app框架内置组件
+ useNative: {
+ type: Boolean,
+ default: () => defProps.slider.useNative
+ },
+ // 滑块高度
+ height: {
+ type: String,
+ default: () => defProps.slider.height
+ },
+ innerStyle: {
+ type: Object,
+ default: () => defProps.slider.innerStyle
+ },
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-slider/slider.js b/uni_modules/uview-plus/components/u-slider/slider.js
new file mode 100644
index 0000000..38cac28
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-slider/slider.js
@@ -0,0 +1,28 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/slider.js
+ */
+export default {
+ // slider组件
+ slider: {
+ value: 0,
+ blockSize: 18,
+ min: 0,
+ max: 100,
+ step: 1,
+ activeColor: '#2979ff',
+ inactiveColor: '#c0c4cc',
+ blockColor: '#ffffff',
+ showValue: false,
+ disabled:false,
+ blockStyle: {},
+ useNative: false,
+ height: '2px',
+ innerStyle: {}
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-slider/u-slider.vue b/uni_modules/uview-plus/components/u-slider/u-slider.vue
new file mode 100644
index 0000000..96e4df4
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-slider/u-slider.vue
@@ -0,0 +1,511 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ this.rangeValue[0] }}
+
+
+ {{ this.rangeValue[1] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ modelValue }}
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-status-bar/props.js b/uni_modules/uview-plus/components/u-status-bar/props.js
new file mode 100644
index 0000000..d3bf7ee
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-status-bar/props.js
@@ -0,0 +1,15 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ bgColor: {
+ type: String,
+ default: () => defProps.statusBar.bgColor
+ },
+ // 状态栏获取得高度
+ height: {
+ type: Number,
+ default: () => defProps.statusBar.height
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-status-bar/statusBar.js b/uni_modules/uview-plus/components/u-status-bar/statusBar.js
new file mode 100644
index 0000000..d5ad536
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-status-bar/statusBar.js
@@ -0,0 +1,16 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/statusBar.js
+ */
+export default {
+ // statusBar
+ statusBar: {
+ bgColor: 'transparent',
+ height: 0
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-status-bar/u-status-bar.vue b/uni_modules/uview-plus/components/u-status-bar/u-status-bar.vue
new file mode 100644
index 0000000..582ba6a
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-status-bar/u-status-bar.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-steps-item/props.js b/uni_modules/uview-plus/components/u-steps-item/props.js
new file mode 100644
index 0000000..2eabc67
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-steps-item/props.js
@@ -0,0 +1,31 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 标题
+ title: {
+ type: [String, Number],
+ default: () => defProps.stepsItem.title
+ },
+ // 描述文本
+ desc: {
+ type: [String, Number],
+ default: () => defProps.stepsItem.desc
+ },
+ // 图标大小
+ iconSize: {
+ type: [String, Number],
+ default: () => defProps.stepsItem.iconSize
+ },
+ // 当前步骤是否处于失败状态
+ error: {
+ type: Boolean,
+ default: () => defProps.stepsItem.error
+ },
+ // 自定义样式
+ itemStyle: {
+ type: [Object],
+ default: {}
+ },
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-steps-item/stepsItem.js b/uni_modules/uview-plus/components/u-steps-item/stepsItem.js
new file mode 100644
index 0000000..0f82a1c
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-steps-item/stepsItem.js
@@ -0,0 +1,18 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/stepsItem.js
+ */
+export default {
+ // steps-item组件
+ stepsItem: {
+ title: '',
+ desc: '',
+ iconSize: 17,
+ error: false
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-steps-item/u-steps-item.vue b/uni_modules/uview-plus/components/u-steps-item/u-steps-item.vue
new file mode 100644
index 0000000..b4faad8
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-steps-item/u-steps-item.vue
@@ -0,0 +1,349 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ index + 1}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-steps/props.js b/uni_modules/uview-plus/components/u-steps/props.js
new file mode 100644
index 0000000..3252b66
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-steps/props.js
@@ -0,0 +1,41 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 排列方向
+ direction: {
+ type: String,
+ default: () => defProps.steps.direction
+ },
+ // 设置第几个步骤
+ current: {
+ type: [String, Number],
+ default: () => defProps.steps.current
+ },
+ // 激活状态颜色
+ activeColor: {
+ type: String,
+ default: () => defProps.steps.activeColor
+ },
+ // 未激活状态颜色
+ inactiveColor: {
+ type: String,
+ default: () => defProps.steps.inactiveColor
+ },
+ // 激活状态的图标
+ activeIcon: {
+ type: String,
+ default: () => defProps.steps.activeIcon
+ },
+ // 未激活状态图标
+ inactiveIcon: {
+ type: String,
+ default: () => defProps.steps.inactiveIcon
+ },
+ // 是否显示点类型
+ dot: {
+ type: Boolean,
+ default: () => defProps.steps.dot
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-steps/steps.js b/uni_modules/uview-plus/components/u-steps/steps.js
new file mode 100644
index 0000000..61c63cd
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-steps/steps.js
@@ -0,0 +1,21 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/steps.js
+ */
+export default {
+ // steps组件
+ steps: {
+ direction: 'row',
+ current: 0,
+ activeColor: '#3c9cff',
+ inactiveColor: '#969799',
+ activeIcon: '',
+ inactiveIcon: '',
+ dot: false
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-steps/u-steps.vue b/uni_modules/uview-plus/components/u-steps/u-steps.vue
new file mode 100644
index 0000000..b879749
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-steps/u-steps.vue
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-sticky/props.js b/uni_modules/uview-plus/components/u-sticky/props.js
new file mode 100644
index 0000000..c889102
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-sticky/props.js
@@ -0,0 +1,42 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 吸顶容器到顶部某个距离的时候,进行吸顶,在H5平台,NavigationBar为44px
+ offsetTop: {
+ type: [String, Number],
+ default: () => defProps.sticky.offsetTop
+ },
+ // 自定义导航栏的高度
+ customNavHeight: {
+ type: [String, Number],
+ // #ifdef H5
+ // H5端的导航栏属于“自定义”导航栏的范畴,因为它是非原生的,与普通元素一致
+ default: 44,
+ // #endif
+ // #ifndef H5
+ default: () => defProps.sticky.customNavHeight
+ // #endif
+ },
+ // 是否开启吸顶功能
+ disabled: {
+ type: Boolean,
+ default: () => defProps.sticky.disabled
+ },
+ // 吸顶区域的背景颜色
+ bgColor: {
+ type: String,
+ default: () => defProps.sticky.bgColor
+ },
+ // z-index值
+ zIndex: {
+ type: [String, Number],
+ default: () => defProps.sticky.zIndex
+ },
+ // 列表中的索引值
+ index: {
+ type: [String, Number],
+ default: () => defProps.sticky.index
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-sticky/sticky.js b/uni_modules/uview-plus/components/u-sticky/sticky.js
new file mode 100644
index 0000000..683c36b
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-sticky/sticky.js
@@ -0,0 +1,20 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/sticky.js
+ */
+export default {
+ // sticky组件
+ sticky: {
+ offsetTop: 0,
+ customNavHeight: 0,
+ disabled: false,
+ bgColor: 'transparent',
+ zIndex: '',
+ index: ''
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-sticky/u-sticky.vue b/uni_modules/uview-plus/components/u-sticky/u-sticky.vue
new file mode 100644
index 0000000..d074026
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-sticky/u-sticky.vue
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-subsection/props.js b/uni_modules/uview-plus/components/u-subsection/props.js
new file mode 100644
index 0000000..799f518
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-subsection/props.js
@@ -0,0 +1,67 @@
+import {defineMixin} from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+ props: {
+ // tab的数据
+ list: {
+ type: Array,
+ default: () => defProps.subsection.list
+ },
+ // 当前活动的tab的index
+ current: {
+ type: [String, Number],
+ default: () => defProps.subsection.current
+ },
+ // 激活的颜色
+ activeColor: {
+ type: String,
+ default: () => defProps.subsection.activeColor
+ },
+ // 未激活的颜色
+ inactiveColor: {
+ type: String,
+ default: () => defProps.subsection.inactiveColor
+ },
+ // 模式选择,mode=button为按钮形式,mode=subsection时为分段模式
+ mode: {
+ type: String,
+ default: () => defProps.subsection.mode
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: () => defProps.subsection.fontSize
+ },
+ // 激活tab的字体是否加粗
+ bold: {
+ type: Boolean,
+ default: () => defProps.subsection.bold
+ },
+ // mode = button时,组件背景颜色
+ bgColor: {
+ type: String,
+ default: () => defProps.subsection.bgColor
+ },
+ // 从list元素对象中读取的键名
+ keyName: {
+ type: String,
+ default: () => defProps.subsection.keyName
+ },
+ // 从`list`元素对象中读取激活时的颜色 如果存在字段 优先级大于 activeColor
+ activeColorKeyName: {
+ type: String,
+ default: () => defProps.subsection.activeColorKeyName
+ },
+ // 从`list`元素对象中读取未激活时的颜色 如果存在字段 优先级大于 inactiveColor
+ inactiveColorKeyName: {
+ type: String,
+ default: () => defProps.subsection.inactiveColorKeyName
+ },
+ // 是否禁用
+ disabled: {
+ type: Boolean,
+ default: () => defProps.subsection.disabled
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-subsection/subsection.js b/uni_modules/uview-plus/components/u-subsection/subsection.js
new file mode 100644
index 0000000..1e3ef53
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-subsection/subsection.js
@@ -0,0 +1,26 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/subsection.js
+ */
+export default {
+ // subsection组件
+ subsection: {
+ list: [],
+ current: 0,
+ activeColor: '#3c9cff',
+ inactiveColor: '#303133',
+ mode: 'button',
+ fontSize: 12,
+ bold: true,
+ bgColor: '#eeeeef',
+ keyName: 'name',
+ activeColorKeyName: 'activeColorKey',
+ inactiveColorKeyName: 'inactiveColorKey',
+ disabled: false,
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-subsection/u-subsection.vue b/uni_modules/uview-plus/components/u-subsection/u-subsection.vue
new file mode 100644
index 0000000..4aacc24
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-subsection/u-subsection.vue
@@ -0,0 +1,405 @@
+
+
+
+
+ {{ getText(item) }}
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-swipe-action-item/alipay.sjs b/uni_modules/uview-plus/components/u-swipe-action-item/alipay.sjs
new file mode 100644
index 0000000..51a7636
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action-item/alipay.sjs
@@ -0,0 +1,229 @@
+/**
+ * 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台
+ * wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性
+ */
+
+// 开始触摸
+function touchstart(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果
+ var state = instance.getState()
+ if (state.disabled) return
+ var touches = event.touches
+ // 如果进行的是多指触控,不允许进行操作
+ if (touches && touches.length > 1) return
+ // 标识当前为滑动中状态
+ state.moving = true
+ // 记录触摸开始点的坐标值
+ state.startX = touches[0].pageX
+ state.startY = touches[0].pageY
+
+ ownerInstance.callMethod('closeOther')
+}
+
+// 触摸滑动
+function touchmove(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ if (state.disabled || !state.moving) return
+ var touches = event.touches
+ var pageX = touches[0].pageX
+ var pageY = touches[0].pageY
+ var moveX = pageX - state.startX
+ var moveY = pageY - state.startY
+ var buttonsWidth = state.buttonsWidth
+
+ // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+ if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) {
+ event.preventDefault && event.preventDefault()
+ event.stopPropagation && event.stopPropagation()
+ }
+ // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+ if (Math.abs(moveX) < Math.abs(moveY)) return
+
+ // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+ // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+ // 在超出后,设置为0
+ if (state.status === 'open') {
+ // 在开启状态下,向左滑动,需忽略
+ if (moveX < 0) moveX = 0
+ // 想要收起菜单,最大能移动的距离为按钮的总宽度
+ if (moveX > buttonsWidth) moveX = buttonsWidth
+ // 如果是已经打开了的状态,向左滑动时,移动收起菜单
+ moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance)
+ } else {
+ // 关闭状态下,右滑动需忽略
+ if (moveX > 0) moveX = 0
+ // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+ if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
+ // 只要是在滑过程中,就不断移动单元格内容部分,从而使隐藏的菜单显示出来
+ moveSwipeAction(moveX, instance, ownerInstance)
+ }
+}
+
+// 触摸结束
+function touchend(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ if (!state.moving || state.disabled) return
+ var touches = event.changedTouches ? event.changedTouches[0] : {}
+ var pageX = touches.pageX
+ var pageY = touches.pageY
+ var moveX = pageX - state.startX
+ if (state.status === 'open') {
+ // 在展开的状态下,继续左滑,无需操作
+ if (moveX < 0) return
+ // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+ if (moveX === 0) {
+ return closeSwipeAction(instance, ownerInstance)
+ }
+ // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+ if (Math.abs(moveX) < state.threshold) {
+ openSwipeAction(instance, ownerInstance)
+ } else {
+ // 如果滑动距离大于阈值,则执行收起逻辑
+ closeSwipeAction(instance, ownerInstance)
+ }
+ } else {
+ // 在关闭的状态下,右滑,无需操作
+ if (moveX > 0) return
+ // 理由同上
+ if (Math.abs(moveX) < state.threshold) {
+ closeSwipeAction(instance, ownerInstance)
+ } else {
+ openSwipeAction(instance, ownerInstance)
+ }
+ }
+}
+
+// 获取过渡时间
+function getDuration(value) {
+ if (value.toString().indexOf('s') >= 0) return value
+ return value > 30 ? value + 'ms' : value + 's'
+}
+
+// 滑动结束时判断滑动的方向
+function getMoveDirection(instance, ownerInstance) {
+ var state = instance.getState()
+}
+
+// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+function moveSwipeAction(moveX, instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+
+ // 设置菜单内容部分的偏移
+ instance.requestAnimationFrame(function() {
+ instance.setStyle({
+ // 设置translateX的值
+ 'transition': 'none',
+ transform: 'translateX(' + moveX + 'px)',
+ '-webkit-transform': 'translateX(' + moveX + 'px)'
+ })
+ })
+}
+
+// 一次性展开滑动菜单
+function openSwipeAction(instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+ // 处理duration单位问题
+ var duration = getDuration(state.duration)
+ // 展开过程中,是向左移动,所以X的偏移应该为负值
+ var buttonsWidth = -state.buttonsWidth
+ instance.requestAnimationFrame(function() {
+ // 设置菜单主体内容
+ instance.setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(' + buttonsWidth + 'px)',
+ '-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
+ })
+ })
+ setStatus('open', instance, ownerInstance)
+}
+
+// 标记菜单的当前状态,open-已经打开,close-已经关闭
+function setStatus(status, instance, ownerInstance) {
+ var state = instance.getState()
+ state.status = status
+ ownerInstance.callMethod('setState', status)
+}
+
+// 一次性收起滑动菜单
+function closeSwipeAction(instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+ var len = buttons.length
+ // 处理duration单位问题
+ var duration = getDuration(state.duration)
+ instance.requestAnimationFrame(function() {
+ // 设置菜单主体内容
+ instance.setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ })
+ // 设置各个隐藏的按钮为收起的状态
+ for (var i = len - 1; i >= 0; i--) {
+ buttons[i].setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ })
+ }
+ })
+ setStatus('close', instance, ownerInstance)
+}
+
+// status的状态发生变化
+function statusChange(newValue, oldValue, ownerInstance, instance) {
+ var state = instance.getState()
+ if (state.disabled) return
+ // 打开或关闭单元格
+ if (newValue === 'close' && state.status === 'open') {
+ closeSwipeAction(instance, ownerInstance)
+ } else if(newValue === 'open' && state.status === 'close') {
+ openSwipeAction(instance, ownerInstance)
+ }
+}
+
+// 菜单尺寸发生变化
+function sizeChange(newValue, oldValue, ownerInstance, instance) {
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ // 临时防止nv_disabled报错
+ if (!state || !newValue) {
+ return;
+ }
+ state.disabled = newValue.disabled
+ state.duration = newValue.duration
+ state.show = newValue.show
+ state.threshold = newValue.threshold
+ state.buttons = newValue.buttons
+
+ if (state.buttons) {
+ var len = state.buttons.length
+ var buttonsWidth = 0
+ var buttons = newValue.buttons
+ for (var i = 0; i < len; i++) {
+ buttonsWidth += buttons[i].width
+ }
+ }
+ state.buttonsWidth = buttonsWidth
+}
+
+export default {
+ touchstart: touchstart,
+ touchmove: touchmove,
+ touchend: touchend,
+ sizeChange: sizeChange,
+ statusChange: statusChange
+}
diff --git a/uni_modules/uview-plus/components/u-swipe-action-item/index - backup.wxs b/uni_modules/uview-plus/components/u-swipe-action-item/index - backup.wxs
new file mode 100644
index 0000000..04cab92
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action-item/index - backup.wxs
@@ -0,0 +1,256 @@
+/**
+ * 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台
+ * wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性
+ */
+
+// 开始触摸
+function touchstart(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果
+ var state = instance.getState()
+ if (state.disable) return
+ var touches = event.touches
+ // 如果进行的是多指触控,不允许进行操作
+ if (touches && touches.length > 1) return
+ // 标识当前为滑动中状态
+ state.moving = true
+ // 记录触摸开始点的坐标值
+ state.startX = touches[0].pageX
+ state.startY = touches[0].pageY
+}
+
+// 触摸滑动
+function touchmove(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ if (state.disabled || !state.moving) return
+
+ var touches = event.touches
+ var pageX = touches[0].pageX
+ var pageY = touches[0].pageY
+ var moveX = pageX - state.startX
+ var moveY = pageY - state.startY
+ var buttonsWidth = state.buttonsWidth
+
+ // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+ if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) {
+ event.preventDefault()
+ event.stopPropagation()
+ }
+ // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+ if (Math.abs(moveX) < Math.abs(moveY)) return
+
+ // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+ // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+ // 在超出后,设置为0
+ if (state.status === 'open') {
+ // 在开启状态下,向左滑动,需忽略
+ if (moveX < 0) moveX = 0
+ // 想要收起菜单,最大能移动的距离为按钮的总宽度
+ if (moveX > buttonsWidth) moveX = buttonsWidth
+ // 如果是已经打开了的状态,向左滑动时,移动收起菜单
+ moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance)
+ } else {
+ // 关闭状态下,右滑动需忽略
+ if (moveX > 0) moveX = 0
+ // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+ if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
+ // 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来
+ moveSwipeAction(moveX, instance, ownerInstance)
+ }
+}
+
+// 触摸结束
+function touchend(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ if (!state.moving || state.disabled) return
+ var touches = event.changedTouches ? event.changedTouches[0] : {}
+ var pageX = touches.pageX
+ var pageY = touches.pageY
+ var moveX = pageX - state.startX
+ if (state.status === 'open') {
+ // 在展开的状态下,继续左滑,无需操作
+ if (moveX < 0) return
+ // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+ if (moveX === 0) {
+ return closeSwipeAction(instance, ownerInstance)
+ }
+ // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+ if (Math.abs(moveX) < state.threshold) {
+ openSwipeAction(instance, ownerInstance)
+ } else {
+ // 如果滑动距离大于阈值,则执行收起逻辑
+ closeSwipeAction(instance, ownerInstance)
+ }
+ } else {
+ // 在关闭的状态下,右滑,无需操作
+ if (moveX > 0) return
+ // 理由同上
+ if (Math.abs(moveX) < state.threshold) {
+ closeSwipeAction(instance, ownerInstance)
+ } else {
+ openSwipeAction(instance, ownerInstance)
+ }
+ }
+}
+
+// 获取过渡时间
+function getDuration(value) {
+ if (value.toString().indexOf('s') >= 0) return value
+ return value > 30 ? value + 'ms' : value + 's'
+}
+
+// 滑动结束时判断滑动的方向
+function getMoveDirection(instance, ownerInstance) {
+ var state = instance.getState()
+}
+
+// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+function moveSwipeAction(moveX, instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+ var len = buttons.length
+ var previewButtonsMoveX = 0
+
+ // 设置菜单内容部分的偏移
+ instance.requestAnimationFrame(function() {
+ instance.setStyle({
+ // 设置translateX的值
+ 'transition': 'none',
+ transform: 'translateX(' + moveX + 'px)',
+ '-webkit-transform': 'translateX(' + moveX + 'px)'
+ })
+ // 折叠按钮动画
+ for (var i = len - 1; i >= 0; i--) {
+ // 通过比例,得出元素自身该移动的距离
+ var translateX = state.buttons[i].width / state.buttonsWidth * moveX
+ // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+ var realTranslateX = translateX + previewButtonsMoveX
+ buttons[i].setStyle({
+ // 在移动期间,不能使用过渡效果,否则会造成卡顿,本质原因是每次移动一点,就要花一定时间去过渡这个过程
+ 'transition': 'none',
+ 'transform': 'translateX(' + realTranslateX + 'px)',
+ '-webkit-transform': 'translateX(' + realTranslateX + 'px)'
+ })
+ // 记录本按钮之前的所有按钮的移动距离之和
+ previewButtonsMoveX += translateX
+ }
+ })
+}
+
+// 一次性展开滑动菜单
+function openSwipeAction(instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+ var len = buttons.length
+ // 处理duration单位问题
+ const duration = getDuration(state.duration)
+ // 展开过程中,是向左移动,所以X的偏移应该为负值
+ var buttonsWidth = -state.buttonsWidth
+ var previewButtonsMoveX = 0
+ instance.requestAnimationFrame(function() {
+ // 设置菜单主体内容
+ instance.setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(' + buttonsWidth + 'px)',
+ '-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
+ })
+ // 设置各个隐藏的按钮为展开的状态
+ for (var i = len - 1; i >= 0; i--) {
+ // 通过比例,得出元素自身该移动的距离
+ var translateX = state.buttons[i].width / state.buttonsWidth * buttonsWidth
+ // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+ var realTranslateX = translateX + previewButtonsMoveX
+ buttons[i].setStyle({
+ // 在移动期间,需要加上动画效果
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(' + realTranslateX + 'px)',
+ '-webkit-transform': 'translateX(' + realTranslateX + 'px)'
+ })
+ // 记录本按钮之前的所有按钮的移动距离之和
+ previewButtonsMoveX += translateX
+ }
+ })
+ setStatus('open', instance)
+}
+
+// 标记菜单的当前状态,open-已经打开,close-已经关闭
+function setStatus(status, instance) {
+ var state = instance.getState()
+ state.status = status
+}
+
+// 一次性收起滑动菜单
+function closeSwipeAction(instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+ var len = buttons.length
+ // 处理duration单位问题
+ const duration = getDuration(state.duration)
+ instance.requestAnimationFrame(function() {
+ // 设置菜单主体内容
+ instance.setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ })
+ // 设置各个隐藏的按钮为收起的状态
+ for (var i = len - 1; i >= 0; i--) {
+ buttons[i].setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ })
+ }
+ })
+ setStatus('close', instance)
+}
+
+// show的状态发生变化
+function showChange(newValue, oldValue, ownerInstance, instance) {
+ var state = instance.getState()
+ if (state.disabled) return
+ // 打开或关闭单元格
+ if (newValue) {
+ openSwipeAction(instance, ownerInstance)
+ } else {
+ closeSwipeAction(instance, ownerInstance)
+ }
+}
+
+// 菜单尺寸发生变化
+function sizeChange(newValue, oldValue, ownerInstance, instance) {
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ state.disabled = newValue.disabled
+ state.duration = newValue.duration
+ state.show = newValue.show
+ state.threshold = newValue.threshold
+ state.buttons = newValue.buttons
+
+ var len = state.buttons.length
+ if (len) {
+ var buttonsWidth = 0
+ var buttons = newValue.buttons
+ for (var i = 0; i < len; i++) {
+ buttonsWidth += buttons[i].width
+ }
+ }
+ state.buttonsWidth = buttonsWidth
+}
+
+module.exports = {
+ touchstart: touchstart,
+ touchmove: touchmove,
+ touchend: touchend,
+ sizeChange: sizeChange
+}
diff --git a/uni_modules/uview-plus/components/u-swipe-action-item/index.wxs b/uni_modules/uview-plus/components/u-swipe-action-item/index.wxs
new file mode 100644
index 0000000..d7ba3de
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action-item/index.wxs
@@ -0,0 +1,234 @@
+/**
+ * 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台
+ * wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性
+ */
+
+// 开始触摸
+function touchstart(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果
+ var state = instance.getState()
+ if (state.disabled) return
+ var touches = event.touches
+ // 如果进行的是多指触控,不允许进行操作
+ if (touches && touches.length > 1) return
+ // 标识当前为滑动中状态
+ state.moving = true
+ // 记录触摸开始点的坐标值
+ state.startX = touches[0].pageX
+ state.startY = touches[0].pageY
+
+ ownerInstance.callMethod('closeOther')
+}
+
+// 触摸滑动
+function touchmove(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ if (state.disabled || !state.moving) return
+ var touches = event.touches
+ var pageX = touches[0].pageX
+ var pageY = touches[0].pageY
+ var moveX = pageX - state.startX
+ var moveY = pageY - state.startY
+ var buttonsWidth = state.buttonsWidth
+
+ // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+ if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) {
+ event.preventDefault && event.preventDefault()
+ event.stopPropagation && event.stopPropagation()
+ }
+ // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+ if (Math.abs(moveX) < Math.abs(moveY)) return
+
+ // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+ // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+ // 在超出后,设置为0
+ if (state.status === 'open') {
+ // 在开启状态下,向左滑动,需忽略
+ if (moveX < 0) moveX = 0
+ // 想要收起菜单,最大能移动的距离为按钮的总宽度
+ if (moveX > buttonsWidth) moveX = buttonsWidth
+ // 如果是已经打开了的状态,向左滑动时,移动收起菜单
+ moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance)
+ } else {
+ // 关闭状态下,右滑动需忽略
+ if (moveX > 0) moveX = 0
+ // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+ if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
+ // 只要是在滑过程中,就不断移动单元格内容部分,从而使隐藏的菜单显示出来
+ moveSwipeAction(moveX, instance, ownerInstance)
+ }
+}
+
+// 触摸结束
+function touchend(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ if (!state.moving || state.disabled) return
+ var touches = event.changedTouches ? event.changedTouches[0] : {}
+ var pageX = touches.pageX
+ var pageY = touches.pageY
+ var moveX = pageX - state.startX
+ if (state.status === 'open') {
+ // 在展开的状态下,继续左滑,无需操作
+ if (moveX < 0) return
+ // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+ if (moveX === 0) {
+ return closeSwipeAction(instance, ownerInstance)
+ }
+ // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+ if (Math.abs(moveX) < state.threshold) {
+ openSwipeAction(instance, ownerInstance)
+ } else {
+ // 如果滑动距离大于阈值,则执行收起逻辑
+ closeSwipeAction(instance, ownerInstance)
+ }
+ } else {
+ // 在关闭的状态下,右滑,无需操作
+ if (moveX > 0) return
+ // 理由同上
+ if (Math.abs(moveX) < state.threshold) {
+ closeSwipeAction(instance, ownerInstance)
+ } else {
+ openSwipeAction(instance, ownerInstance)
+ }
+ }
+}
+
+// 获取过渡时间
+function getDuration(value) {
+ if (value.toString().indexOf('s') >= 0) return value
+ return value > 30 ? value + 'ms' : value + 's'
+}
+
+// 滑动结束时判断滑动的方向
+function getMoveDirection(instance, ownerInstance) {
+ var state = instance.getState()
+}
+
+// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+function moveSwipeAction(moveX, instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+
+ // 设置菜单内容部分的偏移
+ instance.requestAnimationFrame(function() {
+ instance.setStyle({
+ // 设置translateX的值
+ 'transition': 'none',
+ transform: 'translateX(' + moveX + 'px)',
+ '-webkit-transform': 'translateX(' + moveX + 'px)'
+ })
+ })
+}
+
+// 一次性展开滑动菜单
+function openSwipeAction(instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+ // 处理duration单位问题
+ var duration = getDuration(state.duration)
+ // 展开过程中,是向左移动,所以X的偏移应该为负值
+ var buttonsWidth = -state.buttonsWidth
+ instance.requestAnimationFrame(function() {
+ // 设置菜单主体内容
+ instance.setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(' + buttonsWidth + 'px)',
+ '-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
+ })
+ })
+ setStatus('open', instance, ownerInstance)
+}
+
+// 标记菜单的当前状态,open-已经打开,close-已经关闭
+function setStatus(status, instance, ownerInstance) {
+ var state = instance.getState()
+ state.status = status
+ ownerInstance.callMethod('setState', status)
+}
+
+// 一次性收起滑动菜单
+function closeSwipeAction(instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+ var len = buttons.length
+ // 处理duration单位问题
+ var duration = getDuration(state.duration)
+ instance.requestAnimationFrame(function() {
+ // 设置菜单主体内容
+ instance.setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ })
+ // 设置各个隐藏的按钮为收起的状态
+ for (var i = len - 1; i >= 0; i--) {
+ buttons[i].setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ })
+ }
+ })
+ setStatus('close', instance, ownerInstance)
+}
+
+// status的状态发生变化
+function statusChange(newValue, oldValue, ownerInstance, instance) {
+ var state = instance.getState()
+ if (state.disabled) return
+ // 打开或关闭单元格
+ if (newValue === 'close' && state.status === 'open') {
+ closeSwipeAction(instance, ownerInstance)
+ } else if(newValue === 'open' && state.status === 'close') {
+ openSwipeAction(instance, ownerInstance)
+ }
+}
+
+// 菜单尺寸发生变化
+function sizeChange(newValue, oldValue, ownerInstance, instance) {
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ // 临时防止nv_disabled报错
+ if (!state || !newValue) {
+ return;
+ }
+ state.disabled = newValue.disabled
+ state.duration = newValue.duration
+ state.show = newValue.show
+ state.threshold = newValue.threshold
+ state.buttons = newValue.buttons
+
+ if (state.buttons) {
+ var len = state.buttons.length
+ var buttonsWidth = 0
+ var buttons = newValue.buttons
+ for (var i = 0; i < len; i++) {
+ buttonsWidth += buttons[i].width
+ }
+ }
+ state.buttonsWidth = buttonsWidth
+
+ // 支持默认打开
+ if (state.show) {
+ openSwipeAction(instance, ownerInstance)
+ }
+}
+
+module.exports = {
+ touchstart: touchstart,
+ touchmove: touchmove,
+ touchend: touchend,
+ sizeChange: sizeChange,
+ statusChange: statusChange
+}
diff --git a/uni_modules/uview-plus/components/u-swipe-action-item/nvue - backup.js b/uni_modules/uview-plus/components/u-swipe-action-item/nvue - backup.js
new file mode 100644
index 0000000..f114ca6
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action-item/nvue - backup.js
@@ -0,0 +1,270 @@
+// nvue操作dom的库,用于获取dom的尺寸信息
+const dom = uni.requireNativePlugin('dom')
+// nvue中用于操作元素动画的库,类似于uni.animation,只不过uni.animation不能用于nvue
+const animation = uni.requireNativePlugin('animation')
+import { sleep } from '../../libs/function/index';
+export default {
+ data() {
+ return {
+ // 是否滑动中
+ moving: false,
+ // 状态,open-打开状态,close-关闭状态
+ status: 'close',
+ // 开始触摸点的X和Y轴坐标
+ startX: 0,
+ startY: 0,
+ // 所有隐藏按钮的尺寸信息数组
+ buttons: [],
+ // 所有按钮的总宽度
+ buttonsWidth: 0,
+ // 记录上一次移动的位置值
+ moveX: 0,
+ // 记录上一次滑动的位置,用于前后两次做对比,如果移动的距离小于某一阈值,则认为前后之间没有移动,为了解决可能存在的通信阻塞问题
+ lastX: 0
+ }
+ },
+ computed: {
+ // 获取过渡时间
+ getDuratin() {
+ let duration = String(this.duration)
+ // 如果ms为单位,返回ms的数值部分
+ if (duration.indexOf('ms') >= 0) return parseInt(duration)
+ // 如果s为单位,为了得到ms的数值,需要乘以1000
+ if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
+ // 如果值传了数值,且小于30,认为是s单位
+ duration = Number(duration)
+ return duration < 30 ? duration * 1000 : duration
+ }
+ },
+ watch: {
+ show: {
+ immediate: true,
+ handler(n) {
+ // if(n === true) {
+ // sleep(50).then(() => {
+ // this.openSwipeAction()
+ // })
+ // } else {
+ // this.closeSwipeAction()
+ // }
+ }
+ }
+ },
+ mounted() {
+ sleep(20).then(() => {
+ this.queryRect()
+ })
+ },
+ methods: {
+ close() {
+ this.closeSwipeAction()
+ },
+ // 触摸单元格
+ touchstart(event) {
+ if (this.disabled) return
+ this.closeOther()
+ const { touches } = event
+ // 记录触摸开始点的坐标值
+ this.startX = touches[0].pageX
+ this.startY = touches[0].pageY
+ },
+ // // 触摸滑动
+ touchmove(event) {
+ if (this.disabled) return
+ const { touches } = event
+ const { pageX } = touches[0]
+ const { pageY } = touches[0]
+ let moveX = pageX - this.startX
+ const moveY = pageY - this.startY
+ const { buttonsWidth } = this
+ const len = this.buttons.length
+
+ // 判断前后两次的移动距离,如果小于一定值,则不进行移动处理
+ if (Math.abs(pageX - this.lastX) < 0.3) return
+ this.lastX = pageX
+
+ // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+ if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > this.threshold) {
+ event.stopPropagation()
+ }
+ // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+ if (Math.abs(moveX) < Math.abs(moveY)) return
+
+ // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+ // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+ // 在超出后,设置为0
+ if (this.status === 'open') {
+ // 在开启状态下,向左滑动,需忽略
+ if (moveX < 0) moveX = 0
+ // 想要收起菜单,最大能移动的距离为按钮的总宽度
+ if (moveX > buttonsWidth) moveX = buttonsWidth
+ // 如果是已经打开了的状态,向左滑动时,移动收起菜单
+ this.moveSwipeAction(-buttonsWidth + moveX)
+ } else {
+ // 关闭状态下,右滑动需忽略
+ if (moveX > 0) moveX = 0
+ // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+ if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
+ // 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来
+ this.moveSwipeAction(moveX)
+ }
+ },
+ // 单元格结束触摸
+ touchend(event) {
+ if (this.disabled) return
+ const touches = event.changedTouches ? event.changedTouches[0] : {}
+ const { pageX } = touches
+ const { pageY } = touches
+ const { buttonsWidth } = this
+ this.moveX = pageX - this.startX
+ if (this.status === 'open') {
+ // 在展开的状态下,继续左滑,无需操作
+ if (this.moveX < 0) this.moveX = 0
+ if (this.moveX > buttonsWidth) this.moveX = buttonsWidth
+ // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+ if (this.moveX === 0) {
+ return this.closeSwipeAction()
+ }
+ // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+ if (Math.abs(this.moveX) < this.threshold) {
+ this.openSwipeAction()
+ } else {
+ // 如果滑动距离大于阈值,则执行收起逻辑
+ this.closeSwipeAction()
+ }
+ } else {
+ // 在关闭的状态下,右滑,无需操作
+ if (this.moveX >= 0) this.moveX = 0
+ if (this.moveX <= -buttonsWidth) this.moveX = -buttonsWidth
+ // 理由同上
+ if (Math.abs(this.moveX) < this.threshold) {
+ this.closeSwipeAction()
+ } else {
+ this.openSwipeAction()
+ }
+ }
+ },
+ // 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+ moveSwipeAction(moveX) {
+ if (this.moving) return
+ this.moving = true
+
+ let previewButtonsMoveX = 0
+ const len = this.buttons.length
+ animation.transition(this.$refs['u-swipe-action-item__content'].ref, {
+ styles: {
+ transform: `translateX(${moveX}px)`
+ },
+ timingFunction: 'linear'
+ }, () => {
+ this.moving = false
+ })
+ // 按钮的组的长度
+ for (let i = len - 1; i >= 0; i--) {
+ const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref
+ // 通过比例,得出元素自身该移动的距离
+ const translateX = this.buttons[i].width / this.buttonsWidth * moveX
+ // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+ const realTranslateX = translateX + previewButtonsMoveX
+ animation.transition(buttonRef, {
+ styles: {
+ transform: `translateX(${realTranslateX}px)`
+ },
+ duration: 0,
+ delay: 0,
+ timingFunction: 'linear'
+ }, () => {})
+ // 记录本按钮之前的所有按钮的移动距离之和
+ previewButtonsMoveX += translateX
+ }
+ },
+ // 关闭菜单
+ closeSwipeAction() {
+ if (this.status === 'close') return
+ this.moving = true
+ const { buttonsWidth } = this
+ animation.transition(this.$refs['u-swipe-action-item__content'].ref, {
+ styles: {
+ transform: 'translateX(0px)'
+ },
+ duration: this.getDuratin,
+ timingFunction: 'ease-in-out'
+ }, () => {
+ this.status = 'close'
+ this.moving = false
+ this.closeHandler()
+ })
+ // 按钮的组的长度
+ const len = this.buttons.length
+ for (let i = len - 1; i >= 0; i--) {
+ const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref
+ // 如果不满足边界条件,返回
+ if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return
+
+ animation.transition(buttonRef, {
+ styles: {
+ transform: 'translateX(0px)'
+ },
+ duration: this.getDuratin,
+ timingFunction: 'ease-in-out'
+ }, () => {})
+ }
+ },
+ // 打开菜单
+ openSwipeAction() {
+ if (this.status === 'open') return
+ this.moving = true
+ const buttonsWidth = -this.buttonsWidth
+ let previewButtonsMoveX = 0
+ animation.transition(this.$refs['u-swipe-action-item__content'].ref, {
+ styles: {
+ transform: `translateX(${buttonsWidth}px)`
+ },
+ duration: this.getDuratin,
+ timingFunction: 'ease-in-out'
+ }, () => {
+ this.status = 'open'
+ this.moving = false
+ this.openHandler()
+ })
+ // 按钮的组的长度
+ const len = this.buttons.length
+ for (let i = len - 1; i >= 0; i--) {
+ const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref
+ // 如果不满足边界条件,返回
+ if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return
+ // 通过比例,得出元素自身该移动的距离
+ const translateX = this.buttons[i].width / this.buttonsWidth * buttonsWidth
+ // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+ const realTranslateX = translateX + previewButtonsMoveX
+ animation.transition(buttonRef, {
+ styles: {
+ transform: `translateX(${realTranslateX}px)`
+ },
+ duration: this.getDuratin,
+ timingFunction: 'ease-in-out'
+ }, () => {})
+ previewButtonsMoveX += translateX
+ }
+ },
+ // 查询按钮节点信息
+ queryRect() {
+ // 历遍所有按钮数组,通过getRectByDom返回一个promise
+ const promiseAll = this.rightOptions.map((item, index) => this.getRectByDom(this.$refs[`u-swipe-action-item__right__button-${index}`][0]))
+ // 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式
+ Promise.all(promiseAll).then((sizes) => {
+ this.buttons = sizes
+ // 计算所有按钮总宽度
+ this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
+ })
+ },
+ // 通过nvue的dom模块,查询节点信息
+ getRectByDom(ref) {
+ return new Promise((resolve) => {
+ dom.getComponentRect(ref, (res) => {
+ resolve(res.size)
+ })
+ })
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-swipe-action-item/nvue.js b/uni_modules/uview-plus/components/u-swipe-action-item/nvue.js
new file mode 100644
index 0000000..4586708
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action-item/nvue.js
@@ -0,0 +1,174 @@
+// nvue操作dom的库,用于获取dom的尺寸信息
+const dom = uni.requireNativePlugin('dom');
+const bindingX = uni.requireNativePlugin('bindingx');
+const animation = uni.requireNativePlugin('animation');
+import { getPx, getDuration } from '../../libs/function/index';
+export default {
+ data() {
+ return {
+ // 所有按钮的总宽度
+ buttonsWidth: 0,
+ // 是否正在移动中
+ moving: false
+ }
+ },
+ computed: {
+ // 获取过渡时间
+ getDuratin() {
+ let duration = String(this.duration)
+ // 如果ms为单位,返回ms的数值部分
+ if (duration.indexOf('ms') >= 0) return parseInt(duration)
+ // 如果s为单位,为了得到ms的数值,需要乘以1000
+ if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
+ // 如果值传了数值,且小于30,认为是s单位
+ duration = Number(duration)
+ return duration < 30 ? duration * 1000 : duration
+ }
+ },
+ watch: {
+ show(n) {
+ if(n) {
+ this.moveCellByAnimation('open')
+ } else {
+ this.moveCellByAnimation('close')
+ }
+ }
+ },
+ mounted() {
+ this.initialize()
+ },
+ methods: {
+ initialize() {
+ this.queryRect()
+ },
+ // 关闭单元格,用于打开一个,自动关闭其他单元格的场景
+ closeHandler() {
+ if(this.status === 'open') {
+ // 如果在打开状态下,进行点击的话,直接关闭单元格
+ return this.moveCellByAnimation('close') && this.unbindBindingX()
+ }
+ },
+ // 点击单元格
+ clickHandler() {
+ // 如果在移动中被点击,进行忽略
+ if(this.moving) return
+ // 尝试关闭其他打开的单元格
+ this.parent && this.parent.closeOther(this)
+ if(this.status === 'open') {
+ // 如果在打开状态下,进行点击的话,直接关闭单元格
+ return this.moveCellByAnimation('close') && this.unbindBindingX()
+ }
+ },
+ // 滑动单元格
+ onTouchstart(e) {
+ // 如果当前正在移动中,或者disabled状态,则返回
+ if(this.moving || this.disabled) {
+ return this.unbindBindingX()
+ }
+ if(this.status === 'open') {
+ // 如果在打开状态下,进行点击的话,直接关闭单元格
+ return this.moveCellByAnimation('close') && this.unbindBindingX()
+ }
+ // 特殊情况下,e可能不为一个对象
+ e?.stopPropagation && e.stopPropagation()
+ e?.preventDefault && e.preventDefault()
+ this.moving = true
+ // 获取元素ref
+ const content = this.getContentRef()
+ let expression = `min(max(${-this.buttonsWidth}, x), 0)`
+ // 尝试关闭其他打开的单元格
+ this.parent && this.parent.closeOther(this)
+
+ // 阿里为了KPI而开源的BindingX
+ this.panEvent = bindingX.bind({
+ anchor: content,
+ eventType: 'pan',
+ props: [{
+ element: content,
+ // 绑定width属性,设置其宽度值
+ property: 'transform.translateX',
+ expression
+ }]
+ }, (res) => {
+ this.moving = false
+ if (res.state === 'end' || res.state === 'exit') {
+ const deltaX = res.deltaX
+ if(deltaX <= -this.buttonsWidth || deltaX >= 0) {
+ // 如果触摸滑动的过程中,大于单元格的总宽度,或者大于0,意味着已经动过滑动达到了打开或者关闭的状态
+ // 这里直接进行状态的标记
+ this.$nextTick(() => {
+ this.status = deltaX <= -this.buttonsWidth ? 'open' : 'close'
+ })
+ } else if(Math.abs(deltaX) > getPx(this.threshold)) {
+ // 在移动大于阈值、并且小于总按钮宽度时,进行自动打开或者关闭
+ // 移动距离大于0时,意味着需要关闭状态
+ if(Math.abs(deltaX) < this.buttonsWidth) {
+ this.moveCellByAnimation(deltaX > 0 ? 'close' : 'open')
+ }
+ } else {
+ // 在小于阈值时,进行关闭操作(如果在打开状态下,将不会执行bindingX)
+ this.moveCellByAnimation('close')
+ }
+ }
+ })
+ },
+ // 释放bindingX
+ unbindBindingX() {
+ // 释放上一次的资源
+ if (this?.panEvent?.token != 0) {
+ bindingX.unbind({
+ token: this.panEvent?.token,
+ // pan为手势事件
+ eventType: 'pan'
+ })
+ }
+ },
+ // 查询按钮节点信息
+ queryRect() {
+ // 历遍所有按钮数组,通过getRectByDom返回一个promise
+ const promiseAll = this.options.map((item, index) => {
+ return this.getRectByDom(this.$refs[`u-swipe-action-item__right__button-${index}`][0])
+ })
+ // 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式
+ Promise.all(promiseAll).then(sizes => {
+ this.buttons = sizes
+ // 计算所有按钮总宽度
+ this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
+ })
+ },
+ // 通过nvue的dom模块,查询节点信息
+ getRectByDom(ref) {
+ return new Promise(resolve => {
+ dom.getComponentRect(ref, res => {
+ resolve(res.size)
+ })
+ })
+ },
+ // 移动单元格到左边或者右边尽头
+ moveCellByAnimation(status = 'open') {
+ if(this.moving) return
+ // 标识当前状态
+ this.moveing = true
+ const content = this.getContentRef()
+ const x = status === 'open' ? -this.buttonsWidth : 0
+ animation.transition(content, {
+ styles: {
+ transform: `translateX(${x}px)`,
+ },
+ duration: getDuration(this.duration, false),
+ timingFunction: 'ease-in-out'
+ }, () => {
+ this.moving = false
+ this.status = status
+ this.unbindBindingX()
+ })
+ },
+ // 获取元素ref
+ getContentRef() {
+ return this.$refs['u-swipe-action-item__content'].ref
+ },
+ beforeUnmount() {
+ this.unbindBindingX()
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-swipe-action-item/other.js b/uni_modules/uview-plus/components/u-swipe-action-item/other.js
new file mode 100644
index 0000000..5ec51f0
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action-item/other.js
@@ -0,0 +1,178 @@
+export default {
+ data() {
+ return {
+ state: {
+ moving: false,
+ startX: 0,
+ startY: 0,
+ buttonsWidth: 0
+ }
+ }
+ },
+ watch: {
+ status(newValue) {
+ if (this.disabled) return
+ // 打开或关闭单元格
+ if (newValue === 'close' && this.status === 'open') {
+ this.closeSwipeAction()
+ } else if(newValue === 'open' && this.status === 'close') {
+ this.openSwipeAction()
+ }
+ },
+ options(newVal) {
+ this.getBtnWidth()
+ }
+ },
+ mounted() {
+ this.getBtnWidth()
+ },
+ methods: {
+ clickHandler() {
+ },
+ closeHandler() {
+ this.closeSwipeAction()
+ },
+ setStatus(status) {
+ this.status = status
+ },
+ getBtnWidth() {
+ let view = uni.createSelectorQuery().in(this).select(".u-swipe-action-item__right");
+ view.fields({
+ size: true,
+ scrollOffset: true
+ }, data => {
+ this.state.buttonsWidth = data.width
+ // console.log("得到节点信息" + JSON.stringify(data));
+ }).exec();
+ },
+ // 开始触摸
+ touchstart(event) {
+ // console.log(event)
+ // 标识当前为滑动中状态
+ this.state.moving = true
+ // 记录触摸开始点的坐标值
+ var touches = event.touches
+ this.state.startX = touches[0].pageX
+ this.state.startY = touches[0].pageY
+
+ // 关闭其它
+ // console.log(this.parent)
+ this.parent && this.parent.closeOther(this)
+ },
+ touchmove(event) {
+ // console.log(event)
+ if (this.disabled || !this.state.moving) return
+ var touches = event.touches
+ var pageX = touches[0].pageX
+ var pageY = touches[0].pageY
+ var moveX = pageX - this.state.startX
+ var moveY = pageY - this.state.startY
+
+ // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+ if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > this.threshold) {
+ event.preventDefault && event.preventDefault()
+ event.stopPropagation && event.stopPropagation()
+ }
+ // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+ if (Math.abs(moveX) < Math.abs(moveY)) return
+
+ // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+ // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+ // 在超出后,设置为0
+ if (this.status === 'open') {
+ // 在开启状态下,向左滑动,需忽略
+ if (moveX < 0) moveX = 0
+ // 想要收起菜单,最大能移动的距离为按钮的总宽度
+ if (moveX > this.state.buttonsWidth) moveX = this.state.buttonsWidth
+ // 如果是已经打开了的状态,向左滑动时,移动收起菜单
+ this.moveSwipeAction(-this.state.buttonsWidth + moveX)
+ } else {
+ // 关闭状态下,右滑动需忽略
+ if (moveX > 0) moveX = 0
+ // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+ if (Math.abs(moveX) > this.state.buttonsWidth) moveX = -this.state.buttonsWidth
+ // 只要是在滑过程中,就不断移动单元格内容部分,从而使隐藏的菜单显示出来
+ this.moveSwipeAction(moveX)
+ }
+ },
+ touchend(event) {
+ // console.log(event)
+ if (!this.state.moving || this.disabled) return
+ this.state.moving = false
+ var touches = event.changedTouches ? event.changedTouches[0] : {}
+ var pageX = touches.pageX
+ var pageY = touches.pageY
+ var moveX = pageX - this.state.startX
+ if (this.status === 'open') {
+ // 在展开的状态下,继续左滑,无需操作
+ if (moveX < 0) return
+ // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+ if (moveX === 0) {
+ return this.closeSwipeAction()
+ }
+ // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+ if (Math.abs(moveX) < this.threshold) {
+ this.openSwipeAction()
+ } else {
+ // 如果滑动距离大于阈值,则执行收起逻辑
+ this.closeSwipeAction()
+ }
+ } else {
+ // 在关闭的状态下,右滑,无需操作
+ if (moveX > 0) return
+ // 理由同上
+ if (Math.abs(moveX) < this.threshold) {
+ this.closeSwipeAction()
+ } else {
+ this.openSwipeAction()
+ }
+ }
+ },
+ // 一次性展开滑动菜单
+ openSwipeAction() {
+ // 处理duration单位问题
+ var duration = this.getDuration(this.duration)
+ // 展开过程中,是向左移动,所以X的偏移应该为负值
+ var buttonsWidth = -this.state.buttonsWidth
+ this.sliderStyle = {
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(' + buttonsWidth + 'px)',
+ '-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
+ }
+ this.setStatus('open')
+ },
+ // 一次性收起滑动菜单
+ closeSwipeAction() {
+ // 处理duration单位问题
+ var duration = this.getDuration(this.duration)
+ this.sliderStyle = {
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ }
+ // 设置各个隐藏的按钮为收起的状态
+ // for (var i = this.state.buttonsWidth - 1; i >= 0; i--) {
+ // buttons[i].setStyle({
+ // 'transition': 'transform ' + duration,
+ // 'transform': 'translateX(0px)',
+ // '-webkit-transform': 'translateX(0px)'
+ // })
+ // }
+ this.setStatus('close')
+ },
+ // 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+ moveSwipeAction(moveX) {
+ // 设置菜单内容部分的偏移
+ this.sliderStyle = {
+ 'transition': 'none',
+ transform: 'translateX(' + moveX + 'px)',
+ '-webkit-transform': 'translateX(' + moveX + 'px)'
+ }
+ },
+ // 获取过渡时间
+ getDuration(value) {
+ if (value.toString().indexOf('s') >= 0) return value
+ return value > 30 ? value + 'ms' : value + 's'
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-swipe-action-item/props.js b/uni_modules/uview-plus/components/u-swipe-action-item/props.js
new file mode 100644
index 0000000..9f080c8
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action-item/props.js
@@ -0,0 +1,47 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 控制打开或者关闭
+ show: {
+ type: Boolean,
+ default: () => defProps.swipeActionItem.show
+ },
+ closeOnClick: {
+ type: Boolean,
+ default: () => defProps.swipeActionItem.closeOnClick
+ },
+ // 标识符,如果是v-for,可用index索引值
+ name: {
+ type: [String, Number],
+ default: () => defProps.swipeActionItem.name
+ },
+ // 是否禁用
+ disabled: {
+ type: Boolean,
+ default: () => defProps.swipeActionItem.disabled
+ },
+ // 是否自动关闭其他swipe按钮组
+ autoClose: {
+ type: Boolean,
+ default: () => defProps.swipeActionItem.autoClose
+ },
+ // 滑动距离阈值,只有大于此值,才被认为是要打开菜单
+ threshold: {
+ type: Number,
+ default: () => defProps.swipeActionItem.threshold
+ },
+ // 右侧按钮内容
+ options: {
+ type: Array,
+ default() {
+ return defProps.swipeActionItem.rightOptions
+ }
+ },
+ // 动画过渡时间,单位ms
+ duration: {
+ type: [String, Number],
+ default: () => defProps.swipeActionItem.duration
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-swipe-action-item/swipeActionItem.js b/uni_modules/uview-plus/components/u-swipe-action-item/swipeActionItem.js
new file mode 100644
index 0000000..f6afb44
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action-item/swipeActionItem.js
@@ -0,0 +1,22 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/swipeActionItem.js
+ */
+export default {
+ // swipeActionItem 组件
+ swipeActionItem: {
+ show: false,
+ closeOnClick: true,
+ name: '',
+ disabled: false,
+ threshold: 20,
+ autoClose: true,
+ options: [],
+ duration: 300
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-swipe-action-item/u-swipe-action-item.vue b/uni_modules/uview-plus/components/u-swipe-action-item/u-swipe-action-item.vue
new file mode 100644
index 0000000..f8a718d
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action-item/u-swipe-action-item.vue
@@ -0,0 +1,249 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-swipe-action-item/wxs.js b/uni_modules/uview-plus/components/u-swipe-action-item/wxs.js
new file mode 100644
index 0000000..ee49c10
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action-item/wxs.js
@@ -0,0 +1,15 @@
+export default {
+ methods: {
+ // 关闭时执行
+ closeHandler() {
+ this.status = 'close'
+ },
+ setState(status) {
+ this.status = status
+ },
+ closeOther() {
+ // 尝试关闭其他打开的单元格
+ this.parent && this.parent.closeOther(this)
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-swipe-action/props.js b/uni_modules/uview-plus/components/u-swipe-action/props.js
new file mode 100644
index 0000000..29b14ef
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action/props.js
@@ -0,0 +1,16 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 是否自动关闭其他swipe按钮组
+ autoClose: {
+ type: Boolean,
+ default: () => defProps.swipeAction.autoClose
+ },
+ // 是否存在打开的按钮组
+ opendItem: {
+ type: Boolean,
+ default: false
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-swipe-action/swipeAction.js b/uni_modules/uview-plus/components/u-swipe-action/swipeAction.js
new file mode 100644
index 0000000..253fbfc
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action/swipeAction.js
@@ -0,0 +1,15 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/swipeAction.js
+ */
+export default {
+ // swipe-action组件
+ swipeAction: {
+ autoClose: true
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-swipe-action/u-swipe-action.vue b/uni_modules/uview-plus/components/u-swipe-action/u-swipe-action.vue
new file mode 100644
index 0000000..6f0f5ae
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swipe-action/u-swipe-action.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-swiper-indicator/props.js b/uni_modules/uview-plus/components/u-swiper-indicator/props.js
new file mode 100644
index 0000000..fd3c83e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swiper-indicator/props.js
@@ -0,0 +1,31 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 轮播的长度
+ length: {
+ type: [String, Number],
+ default: () => defProps.swiperIndicator.length
+ },
+ // 当前处于活动状态的轮播的索引
+ current: {
+ type: [String, Number],
+ default: () => defProps.swiperIndicator.current
+ },
+ // 指示器非激活颜色
+ indicatorActiveColor: {
+ type: String,
+ default: () => defProps.swiperIndicator.indicatorActiveColor
+ },
+ // 指示器的激活颜色
+ indicatorInactiveColor: {
+ type: String,
+ default: () => defProps.swiperIndicator.indicatorInactiveColor
+ },
+ // 指示器模式,line-线型,dot-点型
+ indicatorMode: {
+ type: String,
+ default: () => defProps.swiperIndicator.indicatorMode
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-swiper-indicator/swipterIndicator.js b/uni_modules/uview-plus/components/u-swiper-indicator/swipterIndicator.js
new file mode 100644
index 0000000..c474f1c
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swiper-indicator/swipterIndicator.js
@@ -0,0 +1,19 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/swiperIndicator.js
+ */
+export default {
+ // swiperIndicator 组件
+ swiperIndicator: {
+ length: 0,
+ current: 0,
+ indicatorActiveColor: '',
+ indicatorInactiveColor: '',
+ indicatorMode: 'line'
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-swiper-indicator/u-swiper-indicator.vue b/uni_modules/uview-plus/components/u-swiper-indicator/u-swiper-indicator.vue
new file mode 100644
index 0000000..f485153
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swiper-indicator/u-swiper-indicator.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-swiper/props.js b/uni_modules/uview-plus/components/u-swiper/props.js
new file mode 100644
index 0000000..87ee5ea
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swiper/props.js
@@ -0,0 +1,127 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 列表数组,元素可为字符串,如为对象可通过keyName指定目标属性名
+ list: {
+ type: Array,
+ default: () => defProps.swiper.list
+ },
+ // 是否显示面板指示器
+ indicator: {
+ type: Boolean,
+ default: () => defProps.swiper.indicator
+ },
+ // 指示器非激活颜色
+ indicatorActiveColor: {
+ type: String,
+ default: () => defProps.swiper.indicatorActiveColor
+ },
+ // 指示器的激活颜色
+ indicatorInactiveColor: {
+ type: String,
+ default: () => defProps.swiper.indicatorInactiveColor
+ },
+ // 指示器样式,可通过bottom,left,right进行定位
+ indicatorStyle: {
+ type: [String, Object],
+ default: () => defProps.swiper.indicatorStyle
+ },
+ // 指示器模式,line-线型,dot-点型
+ indicatorMode: {
+ type: String,
+ default: () => defProps.swiper.indicatorMode
+ },
+ // 是否自动切换
+ autoplay: {
+ type: Boolean,
+ default: () => defProps.swiper.autoplay
+ },
+ // 当前所在滑块的 index
+ current: {
+ type: [String, Number],
+ default: () => defProps.swiper.current
+ },
+ // 当前所在滑块的 item-id ,不能与 current 被同时指定
+ currentItemId: {
+ type: String,
+ default: () => defProps.swiper.currentItemId
+ },
+ // 滑块自动切换时间间隔
+ interval: {
+ type: [String, Number],
+ default: () => defProps.swiper.interval
+ },
+ // 滑块切换过程所需时间
+ duration: {
+ type: [String, Number],
+ default: () => defProps.swiper.duration
+ },
+ // 播放到末尾后是否重新回到开头
+ circular: {
+ type: Boolean,
+ default: () => defProps.swiper.circular
+ },
+ // 前边距,可用于露出前一项的一小部分,nvue和支付宝不支持
+ previousMargin: {
+ type: [String, Number],
+ default: () => defProps.swiper.previousMargin
+ },
+ // 后边距,可用于露出后一项的一小部分,nvue和支付宝不支持
+ nextMargin: {
+ type: [String, Number],
+ default: () => defProps.swiper.nextMargin
+ },
+ // 当开启时,会根据滑动速度,连续滑动多屏,支付宝不支持
+ acceleration: {
+ type: Boolean,
+ default: () => defProps.swiper.acceleration
+ },
+ // 同时显示的滑块数量,nvue、支付宝小程序不支持
+ displayMultipleItems: {
+ type: Number,
+ default: () => defProps.swiper.displayMultipleItems
+ },
+ // 指定swiper切换缓动动画类型,有效值:default、linear、easeInCubic、easeOutCubic、easeInOutCubic
+ // 只对微信小程序有效
+ easingFunction: {
+ type: String,
+ default: () => defProps.swiper.easingFunction
+ },
+ // list数组中指定对象的目标属性名
+ keyName: {
+ type: String,
+ default: () => defProps.swiper.keyName
+ },
+ // 图片的裁剪模式
+ imgMode: {
+ type: String,
+ default: () => defProps.swiper.imgMode
+ },
+ // 组件高度
+ height: {
+ type: [String, Number],
+ default: () => defProps.swiper.height
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: () => defProps.swiper.bgColor
+ },
+ // 组件圆角,数值或带单位的字符串
+ radius: {
+ type: [String, Number],
+ default: () => defProps.swiper.radius
+ },
+ // 是否加载中
+ loading: {
+ type: Boolean,
+ default: () => defProps.swiper.loading
+ },
+ // 是否显示标题,要求数组对象中有title属性
+ showTitle: {
+ type: Boolean,
+ default: () => defProps.swiper.showTitle
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-swiper/swiper.js b/uni_modules/uview-plus/components/u-swiper/swiper.js
new file mode 100644
index 0000000..2574c75
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swiper/swiper.js
@@ -0,0 +1,39 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/swiper.js
+ */
+export default {
+ // swiper 组件
+ swiper: {
+ list: [],
+ indicator: false,
+ indicatorActiveColor: '#FFFFFF',
+ indicatorInactiveColor: 'rgba(255, 255, 255, 0.35)',
+ indicatorStyle: '',
+ indicatorMode: 'line',
+ autoplay: true,
+ current: 0,
+ currentItemId: '',
+ interval: 3000,
+ duration: 300,
+ circular: false,
+ previousMargin: 0,
+ nextMargin: 0,
+ acceleration: false,
+ displayMultipleItems: 1,
+ easingFunction: 'default',
+ keyName: 'url',
+ imgMode: 'aspectFill',
+ height: 130,
+ bgColor: '#f3f4f6',
+ radius: 4,
+ loading: false,
+ showTitle: false
+ }
+
+}
diff --git a/uni_modules/uview-plus/components/u-swiper/u-swiper.vue b/uni_modules/uview-plus/components/u-swiper/u-swiper.vue
new file mode 100644
index 0000000..a872ba7
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-swiper/u-swiper.vue
@@ -0,0 +1,269 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-switch/props.js b/uni_modules/uview-plus/components/u-switch/props.js
new file mode 100644
index 0000000..394298d
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-switch/props.js
@@ -0,0 +1,64 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 是否为加载中状态
+ loading: {
+ type: Boolean,
+ default: () => defProps.switch.loading
+ },
+ // 是否为禁用装填
+ disabled: {
+ type: Boolean,
+ default: () => defProps.switch.disabled
+ },
+ // 开关尺寸,单位px
+ size: {
+ type: [String, Number],
+ default: () => defProps.switch.size
+ },
+ // 打开时的背景颜色
+ activeColor: {
+ type: String,
+ default: () => defProps.switch.activeColor
+ },
+ // 关闭时的背景颜色
+ inactiveColor: {
+ type: String,
+ default: () => defProps.switch.inactiveColor
+ },
+ // 通过v-model双向绑定的值
+ // #ifdef VUE3
+ modelValue: {
+ type: [Boolean, String, Number],
+ default: () => defProps.switch.value
+ },
+ // #endif
+ // #ifdef VUE2
+ value: {
+ type: [Boolean, String, Number],
+ default: () => defProps.switch.value
+ },
+ // #endif
+ // switch打开时的值
+ activeValue: {
+ type: [String, Number, Boolean],
+ default: () => defProps.switch.activeValue
+ },
+ // switch关闭时的值
+ inactiveValue: {
+ type: [String, Number, Boolean],
+ default: () => defProps.switch.inactiveValue
+ },
+ // 是否开启异步变更,开启后需要手动控制输入值
+ asyncChange: {
+ type: Boolean,
+ default: () => defProps.switch.asyncChange
+ },
+ // 圆点与外边框的距离
+ space: {
+ type: [String, Number],
+ default: () => defProps.switch.space
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-switch/switch.js b/uni_modules/uview-plus/components/u-switch/switch.js
new file mode 100644
index 0000000..2aeb351
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-switch/switch.js
@@ -0,0 +1,24 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/switch.js
+ */
+export default {
+ // switch
+ switch: {
+ loading: false,
+ disabled: false,
+ size: 25,
+ activeColor: '#2979ff',
+ inactiveColor: '#ffffff',
+ value: false,
+ activeValue: true,
+ inactiveValue: false,
+ asyncChange: false,
+ space: 0
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-switch/u-switch.vue b/uni_modules/uview-plus/components/u-switch/u-switch.vue
new file mode 100644
index 0000000..7ad5821
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-switch/u-switch.vue
@@ -0,0 +1,215 @@
+
+
+
+
+
+ :class="[modelValue && 'u-switch__node--on']"
+
+
+ :class="[value && 'u-switch__node--on']"
+
+ :style="[nodeStyle]"
+ ref="u-switch__node"
+ >
+
+ :color="modelValue ? activeColor : '#AAABAD'"
+
+
+ :color="value ? activeColor : '#AAABAD'"
+
+ :size="size * 0.6"
+ />
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-tabbar-item/props.js b/uni_modules/uview-plus/components/u-tabbar-item/props.js
new file mode 100644
index 0000000..9d208aa
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabbar-item/props.js
@@ -0,0 +1,41 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // item标签的名称,作为与u-tabbar的value参数匹配的标识符
+ name: {
+ type: [String, Number, null],
+ default: () => defProps.tabbarItem.name
+ },
+ // uview-plus内置图标或者绝对路径的图片
+ icon: {
+ icon: String,
+ default: () => defProps.tabbarItem.icon
+ },
+ // 右上角的角标提示信息
+ badge: {
+ type: [String, Number, null],
+ default: () => defProps.tabbarItem.badge
+ },
+ // 是否显示圆点,将会覆盖badge参数
+ dot: {
+ type: Boolean,
+ default: () => defProps.tabbarItem.dot
+ },
+ // 描述文本
+ text: {
+ type: String,
+ default: () => defProps.tabbarItem.text
+ },
+ // 控制徽标的位置,对象或者字符串形式,可以设置top和right属性
+ badgeStyle: {
+ type: [Object, String],
+ default: () => defProps.tabbarItem.badgeStyle
+ },
+ // 模式,默认普通模式,midButton中间按钮模式
+ mode: {
+ type: String,
+ default: () => defProps.tabbarItem.mode
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-tabbar-item/tabbarItem.js b/uni_modules/uview-plus/components/u-tabbar-item/tabbarItem.js
new file mode 100644
index 0000000..4f19759
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabbar-item/tabbarItem.js
@@ -0,0 +1,21 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/tabbarItem.js
+ */
+export default {
+ //
+ tabbarItem: {
+ name: null,
+ icon: '',
+ badge: null,
+ dot: false,
+ text: '',
+ badgeStyle: 'top: 6px;right:2px;',
+ mode: ''
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-tabbar-item/u-tabbar-item.vue b/uni_modules/uview-plus/components/u-tabbar-item/u-tabbar-item.vue
new file mode 100644
index 0000000..8efe459
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabbar-item/u-tabbar-item.vue
@@ -0,0 +1,197 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-tabbar/props.js b/uni_modules/uview-plus/components/u-tabbar/props.js
new file mode 100644
index 0000000..2645a2e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabbar/props.js
@@ -0,0 +1,57 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+ props: {
+ // 当前匹配项的name
+ value: {
+ type: [String, Number, null],
+ default: () => defProps.tabbar.value
+ },
+ // 是否为iPhoneX留出底部安全距离
+ safeAreaInsetBottom: {
+ type: Boolean,
+ default: () => defProps.tabbar.safeAreaInsetBottom
+ },
+ // 是否显示上方边框
+ border: {
+ type: Boolean,
+ default: () => defProps.tabbar.border
+ },
+ // 上方边框颜色
+ borderColor: {
+ type: String,
+ default: () => defProps.tabbar.borderColor
+ },
+ // 元素层级z-index
+ zIndex: {
+ type: [String, Number],
+ default: () => defProps.tabbar.zIndex
+ },
+ // 选中标签的颜色
+ activeColor: {
+ type: String,
+ default: () => defProps.tabbar.activeColor
+ },
+ // 未选中标签的颜色
+ inactiveColor: {
+ type: String,
+ default: () => defProps.tabbar.inactiveColor
+ },
+ // 是否固定在底部
+ fixed: {
+ type: Boolean,
+ default: () => defProps.tabbar.fixed
+ },
+ // fixed定位固定在底部时,是否生成一个等高元素防止塌陷
+ placeholder: {
+ type: Boolean,
+ default: () => defProps.tabbar.placeholder
+ },
+ // 背景色
+ backgroundColor: {
+ type: String,
+ default: () => defProps.tabbar.backgroundColor
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-tabbar/tabbar.js b/uni_modules/uview-plus/components/u-tabbar/tabbar.js
new file mode 100644
index 0000000..4818e9f
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabbar/tabbar.js
@@ -0,0 +1,24 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/tabbar.js
+ */
+export default {
+ // tabbar
+ tabbar: {
+ value: null,
+ safeAreaInsetBottom: true,
+ border: true,
+ zIndex: 1,
+ activeColor: '#1989fa',
+ inactiveColor: '#7d7e80',
+ fixed: true,
+ placeholder: true,
+ borderColor: '',
+ backgroundColor: ''
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-tabbar/u-tabbar.vue b/uni_modules/uview-plus/components/u-tabbar/u-tabbar.vue
new file mode 100644
index 0000000..c05d8a2
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabbar/u-tabbar.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-table/props.js b/uni_modules/uview-plus/components/u-table/props.js
new file mode 100644
index 0000000..a37e438
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-table/props.js
@@ -0,0 +1,7 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-table/u-table.vue b/uni_modules/uview-plus/components/u-table/u-table.vue
new file mode 100644
index 0000000..18773a1
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-table/u-table.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-table2/tableRow.vue b/uni_modules/uview-plus/components/u-table2/tableRow.vue
new file mode 100644
index 0000000..6f7b8cd
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-table2/tableRow.vue
@@ -0,0 +1,328 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ isExpanded(row) ? '▼' : '▶' }}
+
+
+
+
+ {{ row[col.key] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-table2/u-table2.vue b/uni_modules/uview-plus/components/u-table2/u-table2.vue
new file mode 100644
index 0000000..db075b5
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-table2/u-table2.vue
@@ -0,0 +1,756 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ emptyText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-tabs-item/props.js b/uni_modules/uview-plus/components/u-tabs-item/props.js
new file mode 100644
index 0000000..a37e438
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabs-item/props.js
@@ -0,0 +1,7 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-tabs-item/u-tabs-item.vue b/uni_modules/uview-plus/components/u-tabs-item/u-tabs-item.vue
new file mode 100644
index 0000000..58d9cc5
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabs-item/u-tabs-item.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-tabs/props.js b/uni_modules/uview-plus/components/u-tabs/props.js
new file mode 100644
index 0000000..5684818
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabs/props.js
@@ -0,0 +1,71 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 滑块的移动过渡时间,单位ms
+ duration: {
+ type: Number,
+ default: () => defProps.tabs.duration
+ },
+ // tabs标签数组
+ list: {
+ type: Array,
+ default: () => defProps.tabs.list
+ },
+ // 滑块颜色
+ lineColor: {
+ type: String,
+ default: () => defProps.tabs.lineColor
+ },
+ // 菜单选择中时的样式
+ activeStyle: {
+ type: [String, Object],
+ default: () => defProps.tabs.activeStyle
+ },
+ // 菜单非选中时的样式
+ inactiveStyle: {
+ type: [String, Object],
+ default: () => defProps.tabs.inactiveStyle
+ },
+ // 滑块长度
+ lineWidth: {
+ type: [String, Number],
+ default: () => defProps.tabs.lineWidth
+ },
+ // 滑块高度
+ lineHeight: {
+ type: [String, Number],
+ default: () => defProps.tabs.lineHeight
+ },
+ // 滑块背景显示大小,当滑块背景设置为图片时使用
+ lineBgSize: {
+ type: String,
+ default: () => defProps.tabs.lineBgSize
+ },
+ // 菜单item的样式
+ itemStyle: {
+ type: [String, Object],
+ default: () => defProps.tabs.itemStyle
+ },
+ // 菜单是否可滚动
+ scrollable: {
+ type: Boolean,
+ default: () => defProps.tabs.scrollable
+ },
+ // 当前选中标签的索引
+ current: {
+ type: [Number, String],
+ default: () => defProps.tabs.current
+ },
+ // 默认读取的键名
+ keyName: {
+ type: String,
+ default: () => defProps.tabs.keyName
+ },
+ // 左侧图标样式
+ iconStyle: {
+ type: [String, Object],
+ default: () => defProps.tabs.iconStyle
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-tabs/tabs.js b/uni_modules/uview-plus/components/u-tabs/tabs.js
new file mode 100644
index 0000000..089512f
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabs/tabs.js
@@ -0,0 +1,33 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/tabs.js
+ */
+export default {
+ //
+ tabs: {
+ duration: 300,
+ list: [],
+ lineColor: '',
+ activeStyle: {
+ color: '#303133'
+ },
+ inactiveStyle: {
+ color: '#606266'
+ },
+ lineWidth: 20,
+ lineHeight: 3,
+ lineBgSize: 'cover',
+ itemStyle: {
+ height: '44px'
+ },
+ scrollable: true,
+ current: 0,
+ keyName: 'name',
+ iconStyle: {}
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-tabs/u-tabs.vue b/uni_modules/uview-plus/components/u-tabs/u-tabs.vue
new file mode 100644
index 0000000..e55e0ec
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tabs/u-tabs.vue
@@ -0,0 +1,413 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item[keyName] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-tag/props.js b/uni_modules/uview-plus/components/u-tag/props.js
new file mode 100644
index 0000000..a752b14
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tag/props.js
@@ -0,0 +1,116 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 标签类型info、primary、success、warning、error
+ type: {
+ type: String,
+ default: () => defProps.tag.type
+ },
+ // 不可用
+ disabled: {
+ type: [Boolean, String],
+ default: () => defProps.tag.disabled
+ },
+ // 标签的大小,large,medium,mini
+ size: {
+ type: String,
+ default: () => defProps.tag.size
+ },
+ // tag的形状,circle(两边半圆形), square(方形,带圆角)
+ shape: {
+ type: String,
+ default: () => defProps.tag.shape
+ },
+ // 标签文字
+ text: {
+ type: [String, Number],
+ default: () => defProps.tag.text
+ },
+ // 背景颜色,默认为空字符串,即不处理
+ bgColor: {
+ type: String,
+ default: () => defProps.tag.bgColor
+ },
+ // 标签字体颜色,默认为空字符串,即不处理
+ color: {
+ type: String,
+ default: () => defProps.tag.color
+ },
+ // 标签的边框颜色
+ borderColor: {
+ type: String,
+ default: () => defProps.tag.borderColor
+ },
+ // 关闭按钮图标的颜色
+ closeColor: {
+ type: String,
+ default: () => defProps.tag.closeColor
+ },
+ // 点击时返回的索引值,用于区分例遍的数组哪个元素被点击了
+ name: {
+ type: [String, Number],
+ default: () => defProps.tag.name
+ },
+ // // 模式选择,dark|light|plain
+ // mode: {
+ // type: String,
+ // default: 'light'
+ // },
+ // 镂空时是否填充背景色
+ plainFill: {
+ type: Boolean,
+ default: () => defProps.tag.plainFill
+ },
+ // 是否镂空
+ plain: {
+ type: Boolean,
+ default: () => defProps.tag.plain
+ },
+ // 是否可关闭
+ closable: {
+ type: Boolean,
+ default: () => defProps.tag.closable
+ },
+ // 是否显示
+ show: {
+ type: Boolean,
+ default: () => defProps.tag.show
+ },
+ // 内置图标,或绝对路径的图片
+ icon: {
+ type: String,
+ default: () => defProps.tag.icon,
+ },
+ // 图标颜色
+ iconColor: {
+ type: String,
+ default: () => defProps.tag.iconColor,
+ },
+ // 自定义尺寸字体大小
+ textSize: {
+ type: String,
+ default: () => defProps.tag.textSize
+ },
+ // 自定义尺寸高度
+ height: {
+ type: String,
+ default: () => defProps.tag.height
+ },
+ // 自定义尺寸padding
+ padding: {
+ type: String,
+ default: () => defProps.tag.padding
+ },
+ // 自定义尺寸
+ borderRadius: {
+ type: String,
+ default: () => defProps.tag.borderRadius
+ },
+ // 自动计算背景色
+ autoBgColor: {
+ type: Number,
+ default: () => defProps.tag.autoBgColor
+ },
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-tag/tag.js b/uni_modules/uview-plus/components/u-tag/tag.js
new file mode 100644
index 0000000..7faca9f
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tag/tag.js
@@ -0,0 +1,35 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/tag.js
+ */
+export default {
+ // tag 组件
+ tag: {
+ type: 'primary',
+ disabled: false,
+ size: 'medium',
+ shape: 'square',
+ text: '',
+ bgColor: '',
+ color: '',
+ borderColor: '',
+ closeColor: '#C6C7CB',
+ name: '',
+ plainFill: false,
+ plain: false,
+ closable: false,
+ show: true,
+ icon: '',
+ iconColor: '',
+ textSize: '',
+ height: '',
+ padding: '',
+ borderRadius: '',
+ autoBgColor: 0
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-tag/u-tag.vue b/uni_modules/uview-plus/components/u-tag/u-tag.vue
new file mode 100644
index 0000000..96de2bb
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tag/u-tag.vue
@@ -0,0 +1,402 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-td/props.js b/uni_modules/uview-plus/components/u-td/props.js
new file mode 100644
index 0000000..a37e438
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-td/props.js
@@ -0,0 +1,7 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-td/u-td.vue b/uni_modules/uview-plus/components/u-td/u-td.vue
new file mode 100644
index 0000000..599a674
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-td/u-td.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-text/props.js b/uni_modules/uview-plus/components/u-text/props.js
new file mode 100644
index 0000000..b0fd29e
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-text/props.js
@@ -0,0 +1,117 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 主题颜色
+ type: {
+ type: String,
+ default: () => defProps.text.type
+ },
+ // 是否显示
+ show: {
+ type: Boolean,
+ default: () => defProps.text.show
+ },
+ // 显示的值
+ text: {
+ type: [String, Number],
+ default: () => defProps.text.text
+ },
+ // 前置图标
+ prefixIcon: {
+ type: String,
+ default: () => defProps.text.prefixIcon
+ },
+ // 后置图标
+ suffixIcon: {
+ type: String,
+ default: () => defProps.text.suffixIcon
+ },
+ // 文本处理的匹配模式
+ // text-普通文本,price-价格,phone-手机号,name-姓名,date-日期,link-超链接
+ mode: {
+ type: String,
+ default: () => defProps.text.mode
+ },
+ // mode=link下,配置的链接
+ href: {
+ type: String,
+ default: () => defProps.text.href
+ },
+ // 格式化规则
+ format: {
+ type: [String, Function],
+ default: () => defProps.text.format
+ },
+ // mode=phone时,点击文本是否拨打电话
+ call: {
+ type: Boolean,
+ default: () => defProps.text.call
+ },
+ // 小程序的打开方式
+ openType: {
+ type: String,
+ default: () => defProps.text.openType
+ },
+ // 是否粗体,默认normal
+ bold: {
+ type: Boolean,
+ default: () => defProps.text.bold
+ },
+ // 是否块状
+ block: {
+ type: Boolean,
+ default: () => defProps.text.block
+ },
+ // 文本显示的行数,如果设置,超出此行数,将会显示省略号
+ lines: {
+ type: [String, Number],
+ default: () => defProps.text.lines
+ },
+ // 文本颜色
+ color: {
+ type: String,
+ default: () => defProps.text.color
+ },
+ // 字体大小
+ size: {
+ type: [String, Number],
+ default: () => defProps.text.size
+ },
+ // 图标的样式
+ iconStyle: {
+ type: [Object, String],
+ default: () => defProps.text.iconStyle
+ },
+ // 文字装饰,下划线,中划线等,可选值 none|underline|line-through
+ decoration: {
+ tepe: String,
+ default: () => defProps.text.decoration
+ },
+ // 外边距,对象、字符串,数值形式均可
+ margin: {
+ type: [Object, String, Number],
+ default: () => defProps.text.margin
+ },
+ // 文本行高
+ lineHeight: {
+ type: [String, Number],
+ default: () => defProps.text.lineHeight
+ },
+ // 文本对齐方式,可选值left|center|right
+ align: {
+ type: String,
+ default: () => defProps.text.align
+ },
+ // 文字换行,可选值break-word|normal|anywhere
+ wordWrap: {
+ type: String,
+ default: () => defProps.text.wordWrap
+ },
+ // 占满剩余空间
+ flex1: {
+ type: Boolean,
+ default: () => defProps.text.flex1
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-text/text.js b/uni_modules/uview-plus/components/u-text/text.js
new file mode 100644
index 0000000..b6329f3
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-text/text.js
@@ -0,0 +1,39 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/text.js
+ */
+export default {
+ // text 组件
+ text: {
+ type: '',
+ show: true,
+ text: '',
+ prefixIcon: '',
+ suffixIcon: '',
+ mode: '',
+ href: '',
+ format: '',
+ call: false,
+ openType: '',
+ bold: false,
+ block: false,
+ lines: '',
+ color: '#303133',
+ size: 15,
+ iconStyle: {
+ fontSize: '15px'
+ },
+ decoration: 'none',
+ margin: 0,
+ lineHeight: '',
+ align: 'left',
+ wordWrap: 'normal',
+ flex1: true
+ }
+
+}
diff --git a/uni_modules/uview-plus/components/u-text/u-text.vue b/uni_modules/uview-plus/components/u-text/u-text.vue
new file mode 100644
index 0000000..6d76e68
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-text/u-text.vue
@@ -0,0 +1,234 @@
+
+
+ ¥
+
+
+
+
+
+
+
+ {{ value }}
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-text/value.js b/uni_modules/uview-plus/components/u-text/value.js
new file mode 100644
index 0000000..749ad68
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-text/value.js
@@ -0,0 +1,87 @@
+import { error, priceFormat, timeFormat } from '../../libs/function/index';
+import test from '../../libs/function/test';
+export default {
+ computed: {
+ // 经处理后需要显示的值
+ value() {
+ const {
+ text,
+ mode,
+ format,
+ href
+ } = this
+ // 价格类型
+ if (mode === 'price') {
+ // 如果text不为金额进行提示
+ if (!/^\d+(\.\d+)?$/.test(text)) {
+ error('金额模式下,text参数需要为金额格式');
+ }
+ // 进行格式化,判断用户传入的format参数为正则,或者函数,如果没有传入format,则使用默认的金额格式化处理
+ if (test.func(format)) {
+ // 如果用户传入的是函数,使用函数格式化
+ return format(text)
+ }
+ // 如果format非正则,非函数,则使用默认的金额格式化方法进行操作
+ return priceFormat(text, 2)
+ } if (mode === 'date') {
+ // 判断是否合法的日期或者时间戳
+ !test.date(text) && error('日期模式下,text参数需要为日期或时间戳格式')
+ // 进行格式化,判断用户传入的format参数为正则,或者函数,如果没有传入format,则使用默认的格式化处理
+ if (test.func(format)) {
+ // 如果用户传入的是函数,使用函数格式化
+ return format(text)
+ } if (format) {
+ // 如果format非正则,非函数,则使用默认的时间格式化方法进行操作
+ return timeFormat(text, format)
+ }
+ // 如果没有设置format,则设置为默认的时间格式化形式
+ return timeFormat(text, 'yyyy-mm-dd')
+ } if (mode === 'phone') {
+ // 判断是否合法的手机号
+ // !test.mobile(text) && error('手机号模式下,text参数需要为手机号码格式')
+ if (test.func(format)) {
+ // 如果用户传入的是函数,使用函数格式化
+ return format(text)
+ } if (format === 'encrypt') {
+ // 如果format为encrypt,则将手机号进行星号加密处理
+ return `${text.substr(0, 3)}****${text.substr(7)}`
+ }
+ return text
+ } if (mode === 'name') {
+ // 判断是否合法的字符粗
+ !(typeof (text) === 'string') && error('姓名模式下,text参数需要为字符串格式')
+ if (test.func(format)) {
+ // 如果用户传入的是函数,使用函数格式化
+ return format(text)
+ } if (format === 'encrypt') {
+ // 如果format为encrypt,则将姓名进行星号加密处理
+ return this.formatName(text)
+ }
+ return text
+ } if (mode === 'link') {
+ // 判断是否合法的字符粗
+ !test.url(href) && error('超链接模式下,href参数需要为URL格式')
+ return text
+ }
+ return text
+ }
+ },
+ methods: {
+ // 默认的姓名脱敏规则
+ formatName(name) {
+ let value = ''
+ if (name.length === 2) {
+ value = name.substr(0, 1) + '*'
+ } else if (name.length > 2) {
+ let char = ''
+ for (let i = 0, len = name.length - 2; i < len; i++) {
+ char += '*'
+ }
+ value = name.substr(0, 1) + char + name.substr(-1, 1)
+ } else {
+ value = name
+ }
+ return value
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-textarea/props.js b/uni_modules/uview-plus/components/u-textarea/props.js
new file mode 100644
index 0000000..d0fcb08
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-textarea/props.js
@@ -0,0 +1,127 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+ props: {
+ // 输入框的内容
+ value: {
+ type: [String, Number],
+ default: () => defProps.textarea.value
+ },
+ // 输入框的内容
+ modelValue: {
+ type: [String, Number],
+ default: () => defProps.textarea.value
+ },
+ // 输入框为空时占位符
+ placeholder: {
+ type: [String, Number],
+ default: () => defProps.textarea.placeholder
+ },
+ // 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/
+ placeholderClass: {
+ type: String,
+ default: () => defProps.input.placeholderClass
+ },
+ // 指定placeholder的样式
+ placeholderStyle: {
+ type: [String, Object],
+ default: () => defProps.input.placeholderStyle
+ },
+ // 输入框高度
+ height: {
+ type: [String, Number],
+ default: () => defProps.textarea.height
+ },
+ // 设置键盘右下角按钮的文字,仅微信小程序,App-vue和H5有效
+ confirmType: {
+ type: String,
+ default: () => defProps.textarea.confirmType
+ },
+ // 是否禁用
+ disabled: {
+ type: Boolean,
+ default: () => defProps.textarea.disabled
+ },
+ // 是否显示统计字数
+ count: {
+ type: Boolean,
+ default: () => defProps.textarea.count
+ },
+ // 是否自动获取焦点,nvue不支持,H5取决于浏览器的实现
+ focus: {
+ type: Boolean,
+ default: () => defProps.textarea.focus
+ },
+ // 是否自动增加高度
+ autoHeight: {
+ type: Boolean,
+ default: () => defProps.textarea.autoHeight
+ },
+ // 如果textarea是在一个position:fixed的区域,需要显示指定属性fixed为true
+ fixed: {
+ type: Boolean,
+ default: () => defProps.textarea.fixed
+ },
+ // 指定光标与键盘的距离
+ cursorSpacing: {
+ type: Number,
+ default: () => defProps.textarea.cursorSpacing
+ },
+ // 指定focus时的光标位置
+ cursor: {
+ type: [String, Number],
+ default: () => defProps.textarea.cursor
+ },
+ // 是否显示键盘上方带有”完成“按钮那一栏,
+ showConfirmBar: {
+ type: Boolean,
+ default: () => defProps.textarea.showConfirmBar
+ },
+ // 光标起始位置,自动聚焦时有效,需与selection-end搭配使用
+ selectionStart: {
+ type: Number,
+ default: () => defProps.textarea.selectionStart
+ },
+ // 光标结束位置,自动聚焦时有效,需与selection-start搭配使用
+ selectionEnd: {
+ type: Number,
+ default: () => defProps.textarea.selectionEnd
+ },
+ // 键盘弹起时,是否自动上推页面
+ adjustPosition: {
+ type: Boolean,
+ default: () => defProps.textarea.adjustPosition
+ },
+ // 是否去掉 iOS 下的默认内边距,只微信小程序有效
+ disableDefaultPadding: {
+ type: Boolean,
+ default: () => defProps.textarea.disableDefaultPadding
+ },
+ // focus时,点击页面的时候不收起键盘,只微信小程序有效
+ holdKeyboard: {
+ type: Boolean,
+ default: () => defProps.textarea.holdKeyboard
+ },
+ // 最大输入长度,设置为 -1 的时候不限制最大长度
+ maxlength: {
+ type: [String, Number],
+ default: () => defProps.textarea.maxlength
+ },
+ // 边框类型,surround-四周边框,bottom-底部边框
+ border: {
+ type: String,
+ default: () => defProps.textarea.border
+ },
+ // 用于处理或者过滤输入框内容的方法
+ formatter: {
+ type: [Function, null],
+ default: () => defProps.textarea.formatter
+ },
+ // 是否忽略组件内对文本合成系统事件的处理
+ ignoreCompositionEvent: {
+ type: Boolean,
+ default: true
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-textarea/textarea.js b/uni_modules/uview-plus/components/u-textarea/textarea.js
new file mode 100644
index 0000000..08c009b
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-textarea/textarea.js
@@ -0,0 +1,36 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/textarea.js
+ */
+export default {
+ // textarea 组件
+ textarea: {
+ value: '',
+ placeholder: '',
+ placeholderClass: 'textarea-placeholder',
+ placeholderStyle: 'color: #c0c4cc',
+ height: 70,
+ confirmType: 'done',
+ disabled: false,
+ count: false,
+ focus: false,
+ autoHeight: false,
+ fixed: false,
+ cursorSpacing: 0,
+ cursor: '',
+ showConfirmBar: true,
+ selectionStart: -1,
+ selectionEnd: -1,
+ adjustPosition: true,
+ disableDefaultPadding: false,
+ holdKeyboard: false,
+ maxlength: 140,
+ border: 'surround',
+ formatter: null
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-textarea/u-textarea.vue b/uni_modules/uview-plus/components/u-textarea/u-textarea.vue
new file mode 100644
index 0000000..b6a3449
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-textarea/u-textarea.vue
@@ -0,0 +1,285 @@
+
+
+
+
+ {{ innerValue.length }}/{{ maxlength }}
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-th/props.js b/uni_modules/uview-plus/components/u-th/props.js
new file mode 100644
index 0000000..a37e438
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-th/props.js
@@ -0,0 +1,7 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-th/u-th.vue b/uni_modules/uview-plus/components/u-th/u-th.vue
new file mode 100644
index 0000000..fe3a9f8
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-th/u-th.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-title/u-title.vue b/uni_modules/uview-plus/components/u-title/u-title.vue
new file mode 100644
index 0000000..7e19030
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-title/u-title.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-toast/toast.js b/uni_modules/uview-plus/components/u-toast/toast.js
new file mode 100644
index 0000000..19134b7
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-toast/toast.js
@@ -0,0 +1,30 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/toast.js
+ */
+export default {
+ // toast组件
+ toast: {
+ zIndex: 10090,
+ loading: false,
+ message: '',
+ icon: '',
+ type: '',
+ loadingMode: '',
+ show: '',
+ overlay: false,
+ position: 'center',
+ params: {},
+ duration: 2000,
+ isTab: false,
+ url: '',
+ callback: null,
+ back: false
+ }
+
+}
diff --git a/uni_modules/uview-plus/components/u-toast/u-toast.vue b/uni_modules/uview-plus/components/u-toast/u-toast.vue
new file mode 100644
index 0000000..ec0597c
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-toast/u-toast.vue
@@ -0,0 +1,304 @@
+
+
+
+
+
+
+
+ {{ tmpConfig.message }}
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-toolbar/props.js b/uni_modules/uview-plus/components/u-toolbar/props.js
new file mode 100644
index 0000000..fbf561f
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-toolbar/props.js
@@ -0,0 +1,41 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 是否展示工具条
+ show: {
+ type: Boolean,
+ default: () => defProps.toolbar.show
+ },
+ // 取消按钮的文字
+ cancelText: {
+ type: String,
+ default: () => defProps.toolbar.cancelText
+ },
+ // 确认按钮的文字
+ confirmText: {
+ type: String,
+ default: () => defProps.toolbar.confirmText
+ },
+ // 取消按钮的颜色
+ cancelColor: {
+ type: String,
+ default: () => defProps.toolbar.cancelColor
+ },
+ // 确认按钮的颜色
+ confirmColor: {
+ type: String,
+ default: () => defProps.toolbar.confirmColor
+ },
+ // 标题文字
+ title: {
+ type: String,
+ default: () => defProps.toolbar.title
+ },
+ // 开启右侧插槽
+ rightSlot: {
+ type: Boolean,
+ default: false
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-toolbar/toolbar.js b/uni_modules/uview-plus/components/u-toolbar/toolbar.js
new file mode 100644
index 0000000..3b5a188
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-toolbar/toolbar.js
@@ -0,0 +1,22 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/toolbar.js
+ */
+import { t } from '../../libs/i18n'
+export default {
+ // toolbar 组件
+ toolbar: {
+ show: true,
+ cancelText: t('up.common.cancel'),
+ confirmText: t('up.common.confirm'),
+ cancelColor: '#909193',
+ confirmColor: '',
+ title: ''
+ }
+
+}
diff --git a/uni_modules/uview-plus/components/u-toolbar/u-toolbar.vue b/uni_modules/uview-plus/components/u-toolbar/u-toolbar.vue
new file mode 100644
index 0000000..aabf43d
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-toolbar/u-toolbar.vue
@@ -0,0 +1,125 @@
+
+
+
+
+ {{ cancelText }}
+
+
+ {{ title }}
+
+
+ {{ confirmText }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-tooltip/props.js b/uni_modules/uview-plus/components/u-tooltip/props.js
new file mode 100644
index 0000000..121a6f8
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tooltip/props.js
@@ -0,0 +1,76 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 需要显示的提示文字
+ text: {
+ type: [String, Number],
+ default: () => defProps.tooltip.text
+ },
+ // 点击复制按钮时,复制的文本,为空则使用text值
+ copyText: {
+ type: [String, Number],
+ default: () => defProps.tooltip.copyText
+ },
+ // 文本大小
+ size: {
+ type: [String, Number],
+ default: () => defProps.tooltip.size
+ },
+ // 字体颜色
+ color: {
+ type: String,
+ default: () => defProps.tooltip.color
+ },
+ // 弹出提示框时,文本的背景色
+ bgColor: {
+ type: String,
+ default: () => defProps.tooltip.bgColor
+ },
+ // 弹出提示框的背景色
+ popupBgColor: {
+ type: String,
+ default: () => defProps.tooltip.popupBgColor
+ },
+ // 弹出提示的方向,top-上方,bottom-下方,left-左方,right-右方
+ direction: {
+ type: String,
+ default: () => defProps.tooltip.direction
+ },
+ // 弹出提示的z-index,nvue无效
+ zIndex: {
+ type: [String, Number],
+ default: () => defProps.tooltip.zIndex
+ },
+ // 是否显示复制按钮
+ showCopy: {
+ type: Boolean,
+ default: () => defProps.tooltip.showCopy
+ },
+ // 扩展的按钮组
+ buttons: {
+ type: Array,
+ default: () => defProps.tooltip.buttons
+ },
+ // 是否显示透明遮罩以防止触摸穿透
+ overlay: {
+ type: Boolean,
+ default: () => defProps.tooltip.overlay
+ },
+ // 是否显示复制成功或者失败的toast
+ showToast: {
+ type: Boolean,
+ default: () => defProps.tooltip.showToast
+ },
+ // 触发方式,可选值:longpress/click
+ triggerMode: {
+ type: String,
+ default: () => defProps.tooltip.triggerMode
+ },
+ // 强制定位
+ forcePosition: {
+ type: Object,
+ default: () => defProps.tooltip.forcePosition
+ }
+ }
+})
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-tooltip/tooltip.js b/uni_modules/uview-plus/components/u-tooltip/tooltip.js
new file mode 100644
index 0000000..54d7681
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tooltip/tooltip.js
@@ -0,0 +1,28 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/tooltip.js
+ */
+export default {
+ // tooltip 组件
+ tooltip: {
+ text: '',
+ copyText: '',
+ size: 14,
+ color: '#606266',
+ bgColor: 'transparent',
+ direction: 'top',
+ zIndex: 10071,
+ showCopy: true,
+ buttons: [],
+ overlay: true,
+ showToast: true,
+ popupBgColor: '',
+ triggerMode: 'longpress',
+ forcePosition: {}
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-tooltip/u-tooltip.vue b/uni_modules/uview-plus/components/u-tooltip/u-tooltip.vue
new file mode 100644
index 0000000..c7f452f
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tooltip/u-tooltip.vue
@@ -0,0 +1,394 @@
+
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-tr/props.js b/uni_modules/uview-plus/components/u-tr/props.js
new file mode 100644
index 0000000..a37e438
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tr/props.js
@@ -0,0 +1,7 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-tr/u-tr.vue b/uni_modules/uview-plus/components/u-tr/u-tr.vue
new file mode 100644
index 0000000..23f13b4
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tr/u-tr.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-transition/nvue-ani-map.js b/uni_modules/uview-plus/components/u-transition/nvue-ani-map.js
new file mode 100644
index 0000000..b86b962
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-transition/nvue-ani-map.js
@@ -0,0 +1,68 @@
+export default {
+ fade: {
+ enter: { opacity: 0 },
+ 'enter-to': { opacity: 1 },
+ leave: { opacity: 1 },
+ 'leave-to': { opacity: 0 }
+ },
+ 'fade-up': {
+ enter: { opacity: 0, transform: 'translateY(100%)' },
+ 'enter-to': { opacity: 1, transform: 'translateY(0)' },
+ leave: { opacity: 1, transform: 'translateY(0)' },
+ 'leave-to': { opacity: 0, transform: 'translateY(100%)' }
+ },
+ 'fade-down': {
+ enter: { opacity: 0, transform: 'translateY(-100%)' },
+ 'enter-to': { opacity: 1, transform: 'translateY(0)' },
+ leave: { opacity: 1, transform: 'translateY(0)' },
+ 'leave-to': { opacity: 0, transform: 'translateY(-100%)' }
+ },
+ 'fade-left': {
+ enter: { opacity: 0, transform: 'translateX(-100%)' },
+ 'enter-to': { opacity: 1, transform: 'translateY(0)' },
+ leave: { opacity: 1, transform: 'translateY(0)' },
+ 'leave-to': { opacity: 0, transform: 'translateX(-100%)' }
+ },
+ 'fade-right': {
+ enter: { opacity: 0, transform: 'translateX(100%)' },
+ 'enter-to': { opacity: 1, transform: 'translateY(0)' },
+ leave: { opacity: 1, transform: 'translateY(0)' },
+ 'leave-to': { opacity: 0, transform: 'translateX(100%)' }
+ },
+ 'slide-up': {
+ enter: { transform: 'translateY(100%)' },
+ 'enter-to': { transform: 'translateY(0)' },
+ leave: { transform: 'translateY(0)' },
+ 'leave-to': { transform: 'translateY(100%)' }
+ },
+ 'slide-down': {
+ enter: { transform: 'translateY(-100%)' },
+ 'enter-to': { transform: 'translateY(0)' },
+ leave: { transform: 'translateY(0)' },
+ 'leave-to': { transform: 'translateY(-100%)' }
+ },
+ 'slide-left': {
+ enter: { transform: 'translateX(-100%)' },
+ 'enter-to': { transform: 'translateY(0)' },
+ leave: { transform: 'translateY(0)' },
+ 'leave-to': { transform: 'translateX(-100%)' }
+ },
+ 'slide-right': {
+ enter: { transform: 'translateX(100%)' },
+ 'enter-to': { transform: 'translateY(0)' },
+ leave: { transform: 'translateY(0)' },
+ 'leave-to': { transform: 'translateX(100%)' }
+ },
+ zoom: {
+ enter: { transform: 'scale(0.95)' },
+ 'enter-to': { transform: 'scale(1)' },
+ leave: { transform: 'scale(1)' },
+ 'leave-to': { transform: 'scale(0.95)' }
+ },
+ 'fade-zoom': {
+ enter: { opacity: 0, transform: 'scale(0.95)' },
+ 'enter-to': { opacity: 1, transform: 'scale(1)' },
+ leave: { opacity: 1, transform: 'scale(1)' },
+ 'leave-to': { opacity: 0, transform: 'scale(0.95)' }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-transition/props.js b/uni_modules/uview-plus/components/u-transition/props.js
new file mode 100644
index 0000000..e5327ad
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-transition/props.js
@@ -0,0 +1,26 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 是否展示组件
+ show: {
+ type: Boolean,
+ default: () => defProps.transition.show
+ },
+ // 使用的动画模式
+ mode: {
+ type: String,
+ default: () => defProps.transition.mode
+ },
+ // 动画的执行时间,单位ms
+ duration: {
+ type: [String, Number],
+ default: () => defProps.transition.duration
+ },
+ // 使用的动画过渡函数
+ timingFunction: {
+ type: String,
+ default: () => defProps.transition.timingFunction
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-transition/transition.js b/uni_modules/uview-plus/components/u-transition/transition.js
new file mode 100644
index 0000000..7d2bd5d
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-transition/transition.js
@@ -0,0 +1,18 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/transition.js
+ */
+export default {
+ // transition动画组件的props
+ transition: {
+ show: false,
+ mode: 'fade',
+ duration: '300',
+ timingFunction: 'ease-out'
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-transition/transitionMixin.js b/uni_modules/uview-plus/components/u-transition/transitionMixin.js
new file mode 100644
index 0000000..b1ae487
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-transition/transitionMixin.js
@@ -0,0 +1,163 @@
+// 定义一个一定时间后自动成功的promise,让调用nextTick方法处,进入下一个then方法
+const waitTick = () => new Promise(resolve => setTimeout(resolve, 1000 / 50))
+// nvue动画模块实现细节抽离在外部文件
+// #ifdef APP-NVUE
+import animationMap from './nvue-ani-map.js'
+// #endif
+
+// #ifndef APP-NVUE
+// 定义类名,通过给元素动态切换类名,赋予元素一定的css动画样式
+const getClassNames = (name) => ({
+ enter: `u-${name}-enter u-${name}-enter-active`,
+ 'enter-to': `u-${name}-enter-to u-${name}-enter-active`,
+ leave: `u-${name}-leave u-${name}-leave-active`,
+ 'leave-to': `u-${name}-leave-to u-${name}-leave-active`
+})
+// #endif
+
+// #ifdef APP-NVUE
+// 引入nvue(weex)的animation动画模块,文档见:
+// https://weex.apache.org/zh/docs/modules/animation.html#transition
+const animation = uni.requireNativePlugin('animation')
+const getStyle = (name) => animationMap[name]
+// #endif
+
+import { nextTick } from 'vue'
+import { sleep } from '../../libs/function/index';
+export default {
+ methods: {
+ // 组件被点击发出事件
+ clickHandler() {
+ this.$emit('click')
+ },
+ // #ifndef APP-NVUE
+ // vue版本的组件进场处理
+ async vueEnter() {
+ // 动画进入时的类名
+ const classNames = getClassNames(this.mode)
+ // 定义状态和发出动画进入前事件
+ this.status = 'enter'
+ this.$emit('beforeEnter')
+ this.inited = true
+ this.display = true
+ this.classes = classNames.enter
+ await nextTick();
+ {
+ // https://github.com/umicro/uView2.0/issues/545
+ await sleep(20)
+ // 标识动画尚未结束
+ this.$emit('enter')
+ this.transitionEnded = false
+ // 组件动画进入后触发的事件
+ this.$emit('afterEnter')
+ // 赋予组件enter-to类名
+ this.classes = classNames['enter-to']
+ }
+ },
+ // 动画离场处理
+ async vueLeave() {
+ // 如果不是展示状态,无需执行逻辑
+ if (!this.display) return
+ const classNames = getClassNames(this.mode)
+ // 标记离开状态和发出事件
+ this.status = 'leave'
+ this.$emit('beforeLeave')
+ // 获得类名
+ this.classes = classNames.leave
+
+ await nextTick();
+ {
+ // 动画正在离场的状态
+ this.transitionEnded = false
+ this.$emit('leave')
+ // 组件执行动画,到了执行的执行时间后,执行一些额外处理
+ setTimeout(this.onTransitionEnd, this.duration)
+ this.classes = classNames['leave-to']
+ }
+ },
+ // #endif
+ // #ifdef APP-NVUE
+ // nvue版本动画进场
+ async nvueEnter() {
+ // 获得样式的名称
+ const currentStyle = getStyle(this.mode)
+ // 组件动画状态和发出事件
+ this.status = 'enter'
+ this.$emit('beforeEnter')
+ // 展示生成组件元素
+ this.inited = true
+ this.display = true
+ // 在nvue安卓上,由于渲染速度慢,在弹窗,键盘,日历等组件中,渲染其中的内容需要时间
+ // 导致出现弹窗卡顿,这里让其一开始为透明状态,等一定时间渲染完成后,再让其隐藏起来,再让其按正常逻辑出现
+ this.viewStyle = {
+ opacity: 0
+ }
+ // 等待弹窗内容渲染完成
+ await nextTick();
+ {
+ // 合并样式
+ this.viewStyle = currentStyle.enter
+ Promise.resolve()
+ .then(waitTick)
+ .then(() => {
+ // 组件开始进入前的事件
+ this.$emit('enter')
+ // nvue的transition动画模块需要通过ref调用组件,注意此处的ref不同于vue的this.$refs['u-transition']用法
+ animation.transition(this.$refs['u-transition'].ref, {
+ styles: currentStyle['enter-to'],
+ duration: this.duration,
+ timingFunction: this.timingFunction,
+ needLayout: false,
+ delay: 0
+ }, () => {
+ // 动画执行完毕,发出事件
+ this.$emit('afterEnter')
+ })
+ })
+ .catch(() => {})
+ }
+ },
+ nvueLeave() {
+ if (!this.display) {
+ return
+ }
+ const currentStyle = getStyle(this.mode)
+ // 定义状态和事件
+ this.status = 'leave'
+ this.$emit('beforeLeave')
+ // 合并样式
+ this.viewStyle = currentStyle.leave
+ // 放到promise中处理执行过程
+ Promise.resolve()
+ .then(waitTick) // 等待几十ms
+ .then(() => {
+ this.transitionEnded = false
+ // 动画正在离场的状态
+ this.$emit('leave')
+ animation.transition(this.$refs['u-transition'].ref, {
+ styles: currentStyle['leave-to'],
+ duration: this.duration,
+ timingFunction: this.timingFunction,
+ needLayout: false,
+ delay: 0
+ }, () => {
+ this.onTransitionEnd()
+ })
+ })
+ .catch(() => {})
+ },
+ // #endif
+ // 完成过渡后触发
+ onTransitionEnd() {
+ // 如果已经是结束的状态,无需再处理
+ if (this.transitionEnded) return
+ this.transitionEnded = true
+ // 发出组件动画执行后的事件
+ this.$emit(this.status === 'leave' ? 'afterLeave' : 'afterEnter')
+ if (!this.show && this.display) {
+ this.display = false
+ this.inited = false
+ }
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-transition/u-transition.vue b/uni_modules/uview-plus/components/u-transition/u-transition.vue
new file mode 100644
index 0000000..8e0551b
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-transition/u-transition.vue
@@ -0,0 +1,207 @@
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-transition/vue.ani-style.scss b/uni_modules/uview-plus/components/u-transition/vue.ani-style.scss
new file mode 100644
index 0000000..a31d88b
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-transition/vue.ani-style.scss
@@ -0,0 +1,113 @@
+/**
+ * vue版本动画内置的动画模式有如下:
+ * fade:淡入
+ * zoom:缩放
+ * fade-zoom:缩放淡入
+ * fade-up:上滑淡入
+ * fade-down:下滑淡入
+ * fade-left:左滑淡入
+ * fade-right:右滑淡入
+ * slide-up:上滑进入
+ * slide-down:下滑进入
+ * slide-left:左滑进入
+ * slide-right:右滑进入
+ */
+
+$u-zoom-scale: scale(0.95);
+
+.u-fade-enter-active,
+.u-fade-leave-active {
+ transition-property: opacity;
+}
+
+.u-fade-enter,
+.u-fade-leave-to {
+ opacity: 0
+}
+
+.u-fade-zoom-enter,
+.u-fade-zoom-leave-to {
+ transform: $u-zoom-scale;
+ opacity: 0;
+}
+
+.u-fade-zoom-enter-active,
+.u-fade-zoom-leave-active {
+ transition-property: transform, opacity;
+}
+
+.u-fade-down-enter-active,
+.u-fade-down-leave-active,
+.u-fade-left-enter-active,
+.u-fade-left-leave-active,
+.u-fade-right-enter-active,
+.u-fade-right-leave-active,
+.u-fade-up-enter-active,
+.u-fade-up-leave-active {
+ transition-property: opacity, transform;
+}
+
+.u-fade-up-enter,
+.u-fade-up-leave-to {
+ transform: translate3d(0, 100%, 0);
+ opacity: 0
+}
+
+.u-fade-down-enter,
+.u-fade-down-leave-to {
+ transform: translate3d(0, -100%, 0);
+ opacity: 0
+}
+
+.u-fade-left-enter,
+.u-fade-left-leave-to {
+ transform: translate3d(-100%, 0, 0);
+ opacity: 0
+}
+
+.u-fade-right-enter,
+.u-fade-right-leave-to {
+ transform: translate3d(100%, 0, 0);
+ opacity: 0
+}
+
+.u-slide-down-enter-active,
+.u-slide-down-leave-active,
+.u-slide-left-enter-active,
+.u-slide-left-leave-active,
+.u-slide-right-enter-active,
+.u-slide-right-leave-active,
+.u-slide-up-enter-active,
+.u-slide-up-leave-active {
+ transition-property: transform;
+}
+
+.u-slide-up-enter,
+.u-slide-up-leave-to {
+ transform: translate3d(0, 100%, 0)
+}
+
+.u-slide-down-enter,
+.u-slide-down-leave-to {
+ transform: translate3d(0, -100%, 0)
+}
+
+.u-slide-left-enter,
+.u-slide-left-leave-to {
+ transform: translate3d(-100%, 0, 0)
+}
+
+.u-slide-right-enter,
+.u-slide-right-leave-to {
+ transform: translate3d(100%, 0, 0)
+}
+
+.u-zoom-enter-active,
+.u-zoom-leave-active {
+ transition-property: transform
+}
+
+.u-zoom-enter,
+.u-zoom-leave-to {
+ transform: $u-zoom-scale
+}
diff --git a/uni_modules/uview-plus/components/u-tree/tree-node.vue b/uni_modules/uview-plus/components/u-tree/tree-node.vue
new file mode 100644
index 0000000..f82d9e3
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tree/tree-node.vue
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+ {{ node[props.label] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-tree/u-tree.vue b/uni_modules/uview-plus/components/u-tree/u-tree.vue
new file mode 100644
index 0000000..a0e9114
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-tree/u-tree.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-upload/mixin.js b/uni_modules/uview-plus/components/u-upload/mixin.js
new file mode 100644
index 0000000..ed046a9
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-upload/mixin.js
@@ -0,0 +1,23 @@
+import { error } from '../../libs/function/index'
+
+export const mixinUpload = {
+ watch: {
+ // 监听accept的变化,判断是否符合个平台要求
+ // 只有微信小程序才支持选择媒体,文件类型,所以这里做一个判断提示
+ accept: {
+ immediate: true,
+ handler(val) {
+ // #ifndef MP-WEIXIN
+ if (val === 'all' || val === 'media') {
+ error('只有微信小程序才支持把accept配置为all、media之一')
+ }
+ // #endif
+ // #ifndef H5 || MP-WEIXIN
+ if (val === 'file') {
+ error('只有微信小程序和H5(HX2.9.9)才支持把accept配置为file')
+ }
+ // #endif
+ }
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-upload/props.js b/uni_modules/uview-plus/components/u-upload/props.js
new file mode 100644
index 0000000..529a4b2
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-upload/props.js
@@ -0,0 +1,176 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 接受的文件类型, 可选值为all media image file video
+ accept: {
+ type: String,
+ default: () => defProps.upload.accept
+ },
+ extension: {
+ type: Array,
+ default: () => defProps.upload.extension
+ },
+ // 图片或视频拾取模式,当accept为image类型时设置capture可选额外camera可以直接调起摄像头
+ capture: {
+ type: [String, Array],
+ default: () => defProps.upload.capture
+ },
+ // 当accept为video时生效,是否压缩视频,默认为true
+ compressed: {
+ type: Boolean,
+ default: () => defProps.upload.compressed
+ },
+ // 当accept为video时生效,可选值为back或front
+ camera: {
+ type: String,
+ default: () => defProps.upload.camera
+ },
+ // 当accept为video时生效,拍摄视频最长拍摄时间,单位秒
+ maxDuration: {
+ type: Number,
+ default: () => defProps.upload.maxDuration
+ },
+ // 上传区域的图标,只能内置图标
+ uploadIcon: {
+ type: String,
+ default: () => defProps.upload.uploadIcon
+ },
+ // 上传区域的图标的颜色,默认
+ uploadIconColor: {
+ type: String,
+ default: () => defProps.upload.uploadIconColor
+ },
+ // 是否开启文件读取前事件
+ useBeforeRead: {
+ type: Boolean,
+ default: () => defProps.upload.useBeforeRead
+ },
+ // 读取后的处理函数
+ afterRead: {
+ type: Function,
+ default: null
+ },
+ // 读取前的处理函数
+ beforeRead: {
+ type: Function,
+ default: null
+ },
+ // 是否显示组件自带的图片&视频预览功能
+ previewFullImage: {
+ type: Boolean,
+ default: () => defProps.upload.previewFullImage
+ },
+ // 最大上传数量
+ maxCount: {
+ type: [String, Number],
+ default: () => defProps.upload.maxCount
+ },
+ // 是否启用
+ disabled: {
+ type: Boolean,
+ default: () => defProps.upload.disabled
+ },
+ // 预览上传的图片时的裁剪模式,和image组件mode属性一致
+ imageMode: {
+ type: String,
+ default: () => defProps.upload.imageMode
+ },
+ // 标识符,可以在回调函数的第二项参数中获取
+ name: {
+ type: String,
+ default: () => defProps.upload.name
+ },
+ // 所选的图片的尺寸, 可选值为original compressed
+ sizeType: {
+ type: Array,
+ default: () => defProps.upload.sizeType
+ },
+ // 是否开启图片多选,部分安卓机型不支持
+ multiple: {
+ type: Boolean,
+ default: () => defProps.upload.multiple
+ },
+ // 是否展示删除按钮
+ deletable: {
+ type: Boolean,
+ default: () => defProps.upload.deletable
+ },
+ // 文件大小限制,单位为byte
+ maxSize: {
+ type: [String, Number],
+ default: () => defProps.upload.maxSize
+ },
+ // 显示已上传的文件列表
+ fileList: {
+ type: Array,
+ default: () => defProps.upload.fileList
+ },
+ // 上传区域的提示文字
+ uploadText: {
+ type: String,
+ default: () => defProps.upload.uploadText
+ },
+ // 内部预览图片区域和选择图片按钮的区域宽度
+ width: {
+ type: [String, Number],
+ default: () => defProps.upload.width
+ },
+ // 内部预览图片区域和选择图片按钮的区域高度
+ height: {
+ type: [String, Number],
+ default: () => defProps.upload.height
+ },
+ // 是否在上传完成后展示预览图
+ previewImage: {
+ type: Boolean,
+ default: () => defProps.upload.previewImage
+ },
+ // 是否自动删除
+ autoDelete: {
+ type: Boolean,
+ default: () => defProps.upload.autoDelete
+ },
+ // 是否自动上传需要传递action指定地址
+ autoUpload: {
+ type: Boolean,
+ default: () => defProps.upload.autoUpload
+ },
+ // 自动上传接口地址
+ autoUploadApi: {
+ type: String,
+ default: () => defProps.upload.autoUploadApi
+ },
+ // 自动上传驱动,local/oss/cos/kodo
+ autoUploadDriver: {
+ type: String,
+ default: () => defProps.upload.autoUploadDriver
+ },
+ // 自动上传授权接口,比如oss的签名接口。
+ autoUploadAuthUrl: {
+ type: String,
+ default: () => defProps.upload.autoUploadAuthUrl
+ },
+ // 自动上传携带的header
+ autoUploadHeader: {
+ type: Object,
+ default: () => {
+ return defProps.upload.autoUploadHeader
+ }
+ },
+ // 本地计算视频封面
+ getVideoThumb: {
+ type: Boolean,
+ default: () => defProps.upload.getVideoThumb
+ },
+ // 自定义自动上传后处理
+ customAfterAutoUpload: {
+ type: Boolean,
+ default: () => defProps.upload.customAfterAutoUpload
+ },
+ videoPreviewObjectFit: {
+ type: String,
+ default: () => defProps.upload.videoPreviewObjectFit
+ },
+ }
+})
diff --git a/uni_modules/uview-plus/components/u-upload/u-upload.vue b/uni_modules/uview-plus/components/u-upload/u-upload.vue
new file mode 100644
index 0000000..91fff93
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-upload/u-upload.vue
@@ -0,0 +1,927 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item.isVideo || (item.type && item.type === 'video') ? item.name || t("up.common.video") : item.name || t("up.common.file")}}
+
+
+
+
+
+
+
+ {{ item.message }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ uploadText }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-upload/upload.js b/uni_modules/uview-plus/components/u-upload/upload.js
new file mode 100644
index 0000000..74a0835
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-upload/upload.js
@@ -0,0 +1,46 @@
+/*
+ * @Author : LQ
+ * @Description :
+ * @version : 3.0
+ * @Date : 2021-08-20 16:44:21
+ * @LastAuthor : jry
+ * @lastTime : 2025-12-19 08:55:21
+ * @FilePath : /uview-plus/libs/config/props/upload.js
+ */
+export default {
+ // upload组件
+ upload: {
+ accept: 'image',
+ extension: [],
+ capture: ['album', 'camera'],
+ compressed: true,
+ camera: 'back',
+ maxDuration: 60,
+ uploadIcon: 'camera-fill',
+ uploadIconColor: '#D3D4D6',
+ useBeforeRead: false,
+ previewFullImage: true,
+ maxCount: 52,
+ disabled: false,
+ imageMode: 'aspectFill',
+ name: '',
+ sizeType: ['original', 'compressed'],
+ multiple: false,
+ deletable: true,
+ maxSize: Number.MAX_VALUE,
+ fileList: [],
+ uploadText: '',
+ width: 80,
+ height: 80,
+ previewImage: true,
+ autoDelete: false,
+ autoUpload: false,
+ autoUploadApi: '',
+ autoUploadAuthUrl: '',
+ autoUploadDriver: '',
+ autoUploadHeader: {},
+ getVideoThumb: false,
+ customAfterAutoUpload: false,
+ videoPreviewObjectFit: 'cover'
+ }
+}
diff --git a/uni_modules/uview-plus/components/u-upload/utils.js b/uni_modules/uview-plus/components/u-upload/utils.js
new file mode 100644
index 0000000..8a31cca
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-upload/utils.js
@@ -0,0 +1,180 @@
+import test from '../../libs/function/test'
+function pickExclude(obj, keys) {
+ // 某些情况下,type可能会为
+ if (!['[object Object]', '[object File]'].includes(Object.prototype.toString.call(obj))) {
+ return {}
+ }
+ return Object.keys(obj).reduce((prev, key) => {
+ if (!keys.includes(key)) {
+ prev[key] = obj[key]
+ }
+ return prev
+ }, {})
+}
+
+function formatImage(res) {
+ return res.tempFiles.map((item) => ({
+ ...pickExclude(item, ['path']),
+ type: 'image',
+ url: item.path,
+ thumb: item.path,
+ size: item.size,
+ // #ifdef H5
+ name: item.name,
+ file: item
+ // #endif
+ // #ifndef H5
+ name: item.path.split('/').pop() + '.png',
+ // #endif
+ }))
+}
+
+function formatVideo(res) {
+ // console.log(res)
+ return [
+ {
+ ...pickExclude(res, ['tempFilePath', 'thumbTempFilePath', 'errMsg']),
+ type: 'video',
+ url: res.tempFilePath,
+ thumb: res.thumbTempFilePath,
+ size: res.size,
+ width: res.width || 0, // APP 2.1.0+、H5、微信小程序、京东小程序
+ height: res.height || 0, // APP 2.1.0+、H5、微信小程序、京东小程序
+ // #ifdef H5
+ name: res.name,
+ file: res
+ // #endif
+ // #ifndef H5
+ name: res.tempFilePath.split('/').pop() + '.mp4',
+ // #endif
+ }
+ ]
+}
+
+function formatMedia(res) {
+ return res.tempFiles.map((item) => ({
+ ...pickExclude(item, ['fileType', 'thumbTempFilePath', 'tempFilePath']),
+ type: res.type,
+ url: item.tempFilePath,
+ thumb: res.type === 'video' ? item.thumbTempFilePath : item.tempFilePath,
+ size: item.size,
+ // #ifdef H5
+ file: item
+ // #endif
+ // #ifndef H5
+ name: item.tempFilePath.split('/').pop() + (res.type === 'video' ? '.mp4': '.png'),
+ // #endif
+ }))
+}
+
+function formatFile(res) {
+ return res.tempFiles.map((item) => ({
+ ...pickExclude(item, ['path']),
+ url: item.path,
+ size:item.size,
+ // #ifdef H5
+ name: item.name,
+ type: item.type,
+ file: item
+ // #endif
+ }))
+}
+export function chooseFile({
+ accept,
+ multiple,
+ capture,
+ compressed,
+ maxDuration,
+ sizeType,
+ camera,
+ maxCount,
+ extension
+}) {
+ try {
+ capture = test.array(capture) ? capture : capture.split(',');
+ } catch(e) {
+ capture = [];
+ }
+ return new Promise((resolve, reject) => {
+ switch (accept) {
+ case 'image':
+ uni.chooseImage({
+ count: multiple ? Math.min(maxCount, 9) : 1,
+ sourceType: capture,
+ sizeType,
+ success: (res) => resolve(formatImage(res)),
+ fail: reject
+ })
+ break
+ // #ifdef MP-WEIXIN
+ // 只有微信小程序才支持chooseMedia接口
+ case 'media':
+ wx.chooseMedia({
+ count: multiple ? Math.min(maxCount, 9) : 1,
+ sourceType: capture,
+ maxDuration,
+ sizeType,
+ camera,
+ success: (res) => resolve(formatMedia(res)),
+ fail: reject
+ })
+ break
+ // #endif
+ case 'video':
+ uni.chooseVideo({
+ sourceType: capture,
+ compressed,
+ maxDuration,
+ camera,
+ success: (res) => resolve(formatVideo(res)),
+ fail: reject
+ })
+ break
+ // #ifdef MP-WEIXIN || H5
+ // 只有微信小程序才支持chooseMessageFile接口
+ case 'file':
+ let params = {
+ count: multiple ? maxCount : 1,
+ type: accept,
+ success: (res) => resolve(formatFile(res)),
+ fail: reject
+ }
+ // Array根据文件拓展名过滤,仅 type==file 时有效。每一项都不能是空字符串。默认不过滤。
+ if (extension.length && extension.length > 0) {
+ params.extension = extension
+ }
+ // #ifdef MP-WEIXIN
+ wx.chooseMessageFile(params)
+ // #endif
+ // #ifdef H5
+ // 需要hx2.9.9以上才支持uni.chooseFile
+ uni.chooseFile(params)
+ // #endif
+ break
+ // #endif
+ default:
+ // 此为保底选项,在accept不为上面任意一项的时候选取全部文件
+ // #ifdef MP-WEIXIN
+ wx.chooseMessageFile({
+ count: multiple ? maxCount : 1,
+ type: 'all',
+ success: (res) => resolve(formatFile(res)),
+ fail: reject
+ })
+ // #endif
+ // #ifdef H5
+ // 需要hx2.9.9以上才支持uni.chooseFile
+ let paramsFile = {
+ count: multiple ? maxCount : 1,
+ type: 'all',
+ success: (res) => resolve(formatFile(res)),
+ fail: reject
+ }
+ if (extension.length && extension.length > 0) {
+ paramsFile.extension = extension
+ }
+ uni.chooseFile(paramsFile)
+ // #endif
+ }
+ })
+}
diff --git a/uni_modules/uview-plus/components/u-view/u-view.vue b/uni_modules/uview-plus/components/u-view/u-view.vue
new file mode 100644
index 0000000..c7846d2
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-view/u-view.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/components/u-virtual-list/u-virtual-list.vue b/uni_modules/uview-plus/components/u-virtual-list/u-virtual-list.vue
new file mode 100644
index 0000000..c4dd8fa
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-virtual-list/u-virtual-list.vue
@@ -0,0 +1,253 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/u-waterfall/u-waterfall.vue b/uni_modules/uview-plus/components/u-waterfall/u-waterfall.vue
new file mode 100644
index 0000000..b0c793d
--- /dev/null
+++ b/uni_modules/uview-plus/components/u-waterfall/u-waterfall.vue
@@ -0,0 +1,416 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/uview-plus/components/uview-plus/uview-plus.vue b/uni_modules/uview-plus/components/uview-plus/uview-plus.vue
new file mode 100644
index 0000000..bcd3662
--- /dev/null
+++ b/uni_modules/uview-plus/components/uview-plus/uview-plus.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/uview-plus/index.js b/uni_modules/uview-plus/index.js
new file mode 100644
index 0000000..254e91f
--- /dev/null
+++ b/uni_modules/uview-plus/index.js
@@ -0,0 +1,157 @@
+// 看到此报错,是因为没有配置vite.config.js的【transpileDependencies】
+// const pleaseSetTranspileDependencies = {}, babelTest = pleaseSetTranspileDependencies?.test
+
+// 引入全局mixin
+import { mixin } from './libs/mixin/mixin.js'
+// 小程序特有的mixin
+import { mpMixin } from './libs/mixin/mpMixin.js'
+
+// 路由封装
+import route from './libs/util/route.js'
+// 颜色渐变相关,colorGradient-颜色渐变,hexToRgb-十六进制颜色转rgb颜色,rgbToHex-rgb转十六进制
+import colorGradient from './libs/function/colorGradient.js'
+
+// 规则检验
+import test from './libs/function/test.js'
+// 防抖方法
+import debounce from './libs/function/debounce.js'
+// 节流方法
+import throttle from './libs/function/throttle.js'
+// 浮点计算
+import calc from './libs/function/calc.js'
+// 浮点计算
+import digit from './libs/function/digit.js'
+// 公共文件写入的方法
+import index, { rpx2px } from './libs/function/index.js'
+
+// 配置信息
+import config from './libs/config/config.js'
+// props配置信息
+import props from './libs/config/props.js'
+// 各个需要fixed的地方的z-index配置文件
+import zIndex from './libs/config/zIndex.js'
+// 关于颜色的配置,特殊场景使用
+import color from './libs/config/color.js'
+// 平台
+import platform from './libs/function/platform'
+
+// http
+import http from './libs/function/http.js'
+
+// fontUtil
+import fontUtil from './components/u-icon/util.js';
+
+// i18n
+import i18n, { t } from './libs/i18n/index.js'
+
+// 导出
+let themeType = ['primary', 'success', 'error', 'warning', 'info'];
+export { route, http, debounce, throttle, calc, digit, platform, themeType, mixin, mpMixin, props, color, test, zIndex, fontUtil, i18n , rpx2px, t}
+export * from './libs/function/index.js'
+export * from './libs/function/colorGradient.js'
+
+/**
+ * @description 修改uView内置属性值
+ * @param {object} props 修改内置props属性
+ * @param {object} config 修改内置config属性
+ * @param {object} color 修改内置color属性
+ * @param {object} zIndex 修改内置zIndex属性
+ */
+export function setConfig(configs) {
+ index.shallowMerge(config, configs.config || {})
+ index.shallowMerge(props, configs.props || {})
+ index.shallowMerge(color, configs.color || {})
+ index.shallowMerge(zIndex, configs.zIndex || {})
+}
+index.setConfig = setConfig
+
+const $u = {
+ route,
+ date: index.timeFormat, // 另名date
+ colorGradient: colorGradient.colorGradient,
+ hexToRgb: colorGradient.hexToRgb,
+ rgbToHex: colorGradient.rgbToHex,
+ colorToRgba: colorGradient.colorToRgba,
+ test,
+ type: themeType,
+ http,
+ config, // uview-plus配置信息相关,比如版本号
+ zIndex,
+ debounce,
+ throttle,
+ calc,
+ mixin,
+ mpMixin,
+ // props,
+ ...index,
+ color,
+ platform
+}
+
+export const mount$u = function() {
+ uni.$u = $u
+}
+
+function toCamelCase(str) {
+ return str.replace(/-([a-z])/g, function(match, group1) {
+ return group1.toUpperCase();
+ }).replace(/^[a-z]/, function(match) {
+ return match.toUpperCase();
+ });
+}
+
+// #ifdef APP || H5
+const importFn = import.meta.glob('./components/u-*/u-*.vue', { eager: true })
+let components = [];
+
+// 批量注册全局组件
+for (const key in importFn) {
+ let component = importFn[key].default;
+ if (component.name && component.name.indexOf('u--') !== 0) {
+ component.install = function (Vue) {
+ Vue.component(name, component);
+ };
+
+ // 导入组件
+ components.push(component);
+ }
+}
+// #endif
+
+const install = (Vue, upuiParams = '') => {
+ // #ifdef APP || H5
+ components.forEach(function(component) {
+ const name = component.name.replace(/u-([a-zA-Z0-9-_]+)/g, 'up-$1');
+ if (name != component.name) {
+ Vue.component(component.name, component);
+ }
+ Vue.component(name, component);
+ });
+ // #endif
+
+ // 初始化
+ if (upuiParams) {
+ uni.upuiParams = upuiParams
+ let temp = upuiParams()
+ if (temp.httpIns) {
+ temp.httpIns(http)
+ }
+ if (temp.options) {
+ setConfig(temp.options)
+ }
+ }
+
+ // 同时挂载到uni和Vue.prototype中
+ // $u挂载到uni对象上
+ uni.$u = $u
+
+ // #ifndef APP-NVUE
+ // 只有vue,挂载到Vue.prototype才有意义,因为nvue中全局Vue.prototype和Vue.mixin是无效的
+ Vue.config.globalProperties.$u = $u
+ Vue.mixin(mixin)
+ // #endif
+}
+
+export default {
+ install
+}
diff --git a/uni_modules/uview-plus/index.scss b/uni_modules/uview-plus/index.scss
new file mode 100644
index 0000000..964827e
--- /dev/null
+++ b/uni_modules/uview-plus/index.scss
@@ -0,0 +1,25 @@
+// 引入公共基础类
+@import "./libs/css/common.scss";
+@import "./libs/css/components.scss";
+@import "./libs/css/flex.scss";
+@import "./libs/css/color.scss";
+
+// 非nvue的样式
+/* #ifndef APP-NVUE */
+@import "./libs/css/vue.scss";
+/* #endif */
+
+// nvue的特有样式
+/* #ifdef APP-NVUE */
+@import "./libs/css/nvue.scss";
+/* #endif */
+
+// 小程序特有的样式
+/* #ifdef MP */
+@import "./libs/css/mp.scss";
+/* #endif */
+
+// H5特有的样式
+/* #ifdef H5 */
+@import "./libs/css/h5.scss";
+/* #endif */
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/config/color.js b/uni_modules/uview-plus/libs/config/color.js
new file mode 100644
index 0000000..56b4187
--- /dev/null
+++ b/uni_modules/uview-plus/libs/config/color.js
@@ -0,0 +1,17 @@
+// 为了让用户能够自定义主题,会逐步弃用此文件,各颜色通过css提供
+// 为了给某些特殊场景使用和向后兼容,无需删除此文件(2020-06-20)
+const color = {
+ primary: '#3c9cff',
+ info: '#909399',
+ default: '#909399',
+ warning: '#f9ae3d',
+ error: '#f56c6c',
+ success: '#5ac725',
+ mainColor: '#303133',
+ contentColor: '#606266',
+ tipsColor: '#909399',
+ lightColor: '#c0c4cc',
+ borderColor: '#e4e7ed'
+}
+
+export default color
diff --git a/uni_modules/uview-plus/libs/config/config.js b/uni_modules/uview-plus/libs/config/config.js
new file mode 100644
index 0000000..e8fae46
--- /dev/null
+++ b/uni_modules/uview-plus/libs/config/config.js
@@ -0,0 +1,56 @@
+const version = '3'
+
+// 开发环境才提示,生产环境不会提示
+if (process.env.NODE_ENV === 'development') {
+ console.log(`\n %c uview-plus V${version} %c https://uview-plus.jiangruyi.com/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0;', 'color: #3c9cff;background: #ffffff; padding:5px 0;');
+}
+
+export default {
+ v: version,
+ version,
+ // 主题名称
+ type: [
+ 'primary',
+ 'success',
+ 'info',
+ 'error',
+ 'warning'
+ ],
+ // 颜色部分,本来可以通过scss的:export导出供js使用,但是奈何nvue不支持
+ color: {
+ 'u-primary': '#2979ff',
+ 'u-warning': '#ff9900',
+ 'u-success': '#19be6b',
+ 'u-error': '#fa3534',
+ 'u-info': '#909399',
+ 'u-main-color': '#303133',
+ 'u-content-color': '#606266',
+ 'u-tips-color': '#909399',
+ 'u-light-color': '#c0c4cc',
+ 'up-primary': '#2979ff',
+ 'up-warning': '#ff9900',
+ 'up-success': '#19be6b',
+ 'up-error': '#fa3534',
+ 'up-info': '#909399',
+ 'up-main-color': '#303133',
+ 'up-content-color': '#606266',
+ 'up-tips-color': '#909399',
+ 'up-light-color': '#c0c4cc'
+ },
+ // 字体图标地址
+ iconUrl: 'https://at.alicdn.com/t/font_2225171_8kdcwk4po24.ttf',
+ // 自定义图标
+ customIcon: {
+ family: '',
+ url: ''
+ },
+ customIcons: {}, // 自定义图标与unicode对应关系
+ // 默认单位,可以通过配置为rpx,那么在用于传入组件大小参数为数值时,就默认为rpx
+ unit: 'px',
+ // 拦截器
+ interceptor: {
+ navbarLeftClick: null
+ },
+ // 只加载一次字体
+ loadFontOnce: false
+}
diff --git a/uni_modules/uview-plus/libs/config/props.js b/uni_modules/uview-plus/libs/config/props.js
new file mode 100644
index 0000000..84dcf7d
--- /dev/null
+++ b/uni_modules/uview-plus/libs/config/props.js
@@ -0,0 +1,216 @@
+/**
+ * 此文件的作用为统一配置所有组件的props参数
+ * 借此用户可以全局覆盖组件的props默认值
+ * 无需在每个引入组件的页面中都配置一次
+ */
+import config from './config'
+// 各个需要fixed的地方的z-index配置文件
+import zIndex from './zIndex.js'
+// 关于颜色的配置,特殊场景使用
+import color from './color.js'
+// http
+import http from '../function/http.js'
+import { shallowMerge } from '../function/index.js'
+// 组件props
+import ActionSheet from '../../components/u-action-sheet/actionSheet'
+import Album from '../../components/u-album/album'
+import Alert from '../../components/u-alert/alert'
+import Avatar from '../../components/u-avatar/avatar'
+import AvatarGroup from '../../components/u-avatar-group/avatarGroup'
+import Backtop from '../../components/u-back-top/backtop'
+import Badge from '../../components/u-badge/badge'
+import Button from '../../components/u-button/button'
+import Calendar from '../../components/u-calendar/calendar'
+import CarKeyboard from '../../components/u-car-keyboard/carKeyboard'
+import Card from '../../components/u-card/card'
+import Cell from '../../components/u-cell/cell'
+import CellGroup from '../../components/u-cell-group/cellGroup'
+import Checkbox from '../../components/u-checkbox/checkbox'
+import CheckboxGroup from '../../components/u-checkbox-group/checkboxGroup'
+import CircleProgress from '../../components/u-circle-progress/circleProgress'
+import Code from '../../components/u-code/code'
+import CodeInput from '../../components/u-code-input/codeInput'
+import Col from '../../components/u-col/col'
+import Collapse from '../../components/u-collapse/collapse'
+import CollapseItem from '../../components/u-collapse-item/collapseItem'
+import ColumnNotice from '../../components/u-column-notice/columnNotice'
+import CountDown from '../../components/u-count-down/countDown'
+import CountTo from '../../components/u-count-to/countTo'
+import DatetimePicker from '../../components/u-datetime-picker/datetimePicker'
+import Divider from '../../components/u-divider/divider'
+import Empty from '../../components/u-empty/empty'
+import Form from '../../components/u-form/form'
+import FormItem from '../../components/u-form-item/formItem'
+import Gap from '../../components/u-gap/gap'
+import Grid from '../../components/u-grid/grid'
+import GridItem from '../../components/u-grid-item/gridItem'
+import Icon from '../../components/u-icon/icon'
+import Image from '../../components/u-image/image'
+import IndexAnchor from '../../components/u-index-anchor/indexAnchor'
+import IndexList from '../../components/u-index-list/indexList'
+import Input from '../../components/u-input/input'
+import Keyboard from '../../components/u-keyboard/keyboard'
+import Line from '../../components/u-line/line'
+import LineProgress from '../../components/u-line-progress/lineProgress'
+import Link from '../../components/u-link/link'
+import List from '../../components/u-list/list'
+import ListItem from '../../components/u-list-item/listItem'
+import LoadingIcon from '../../components/u-loading-icon/loadingIcon'
+import LoadingPage from '../../components/u-loading-page/loadingPage'
+import Loadmore from '../../components/u-loadmore/loadmore'
+import Modal from '../../components/u-modal/modal'
+import Navbar from '../../components/u-navbar/navbar'
+import NoNetwork from '../../components/u-no-network/noNetwork'
+import NoticeBar from '../../components/u-notice-bar/noticeBar'
+import Notify from '../../components/u-notify/notify'
+import NumberBox from '../../components/u-number-box/numberBox'
+import NumberKeyboard from '../../components/u-number-keyboard/numberKeyboard'
+import Overlay from '../../components/u-overlay/overlay'
+import Parse from '../../components/u-parse/parse'
+import Picker from '../../components/u-picker/picker'
+import Popup from '../../components/u-popup/popup'
+import Radio from '../../components/u-radio/radio'
+import RadioGroup from '../../components/u-radio-group/radioGroup'
+import Rate from '../../components/u-rate/rate'
+import ReadMore from '../../components/u-read-more/readMore'
+import Row from '../../components/u-row/row'
+import RowNotice from '../../components/u-row-notice/rowNotice'
+import ScrollList from '../../components/u-scroll-list/scrollList'
+import Search from '../../components/u-search/search'
+import Section from '../../components/u-section/section'
+import Skeleton from '../../components/u-skeleton/skeleton'
+import Slider from '../../components/u-slider/slider'
+import StatusBar from '../../components/u-status-bar/statusBar'
+import Steps from '../../components/u-steps/steps'
+import StepsItem from '../../components/u-steps-item/stepsItem'
+import Sticky from '../../components/u-sticky/sticky'
+import Subsection from '../../components/u-subsection/subsection'
+import SwipeAction from '../../components/u-swipe-action/swipeAction'
+import SwipeActionItem from '../../components/u-swipe-action-item/swipeActionItem'
+import Swiper from '../../components/u-swiper/swiper'
+import SwipterIndicator from '../../components/u-swiper-indicator/swipterIndicator'
+import Switch from '../../components/u-switch/switch'
+import Tabbar from '../../components/u-tabbar/tabbar'
+import TabbarItem from '../../components/u-tabbar-item/tabbarItem'
+import Tabs from '../../components/u-tabs/tabs'
+import Tag from '../../components/u-tag/tag'
+import Text from '../../components/u-text/text'
+import Textarea from '../../components/u-textarea/textarea'
+import Toast from '../../components/u-toast/toast'
+import Toolbar from '../../components/u-toolbar/toolbar'
+import Tooltip from '../../components/u-tooltip/tooltip'
+import Transition from '../../components/u-transition/transition'
+import Upload from '../../components/u-upload/upload'
+
+const props = {
+ ...ActionSheet,
+ ...Album,
+ ...Alert,
+ ...Avatar,
+ ...AvatarGroup,
+ ...Backtop,
+ ...Badge,
+ ...Button,
+ ...Calendar,
+ ...CarKeyboard,
+ ...Card,
+ ...Cell,
+ ...CellGroup,
+ ...Checkbox,
+ ...CheckboxGroup,
+ ...CircleProgress,
+ ...Code,
+ ...CodeInput,
+ ...Col,
+ ...Collapse,
+ ...CollapseItem,
+ ...ColumnNotice,
+ ...CountDown,
+ ...CountTo,
+ ...DatetimePicker,
+ ...Divider,
+ ...Empty,
+ ...Form,
+ ...FormItem,
+ ...Gap,
+ ...Grid,
+ ...GridItem,
+ ...Icon,
+ ...Image,
+ ...IndexAnchor,
+ ...IndexList,
+ ...Input,
+ ...Keyboard,
+ ...Line,
+ ...LineProgress,
+ ...Link,
+ ...List,
+ ...ListItem,
+ ...LoadingIcon,
+ ...LoadingPage,
+ ...Loadmore,
+ ...Modal,
+ ...Navbar,
+ ...NoNetwork,
+ ...NoticeBar,
+ ...Notify,
+ ...NumberBox,
+ ...NumberKeyboard,
+ ...Overlay,
+ ...Parse,
+ ...Picker,
+ ...Popup,
+ ...Radio,
+ ...RadioGroup,
+ ...Rate,
+ ...ReadMore,
+ ...Row,
+ ...RowNotice,
+ ...ScrollList,
+ ...Search,
+ ...Section,
+ ...Skeleton,
+ ...Slider,
+ ...StatusBar,
+ ...Steps,
+ ...StepsItem,
+ ...Sticky,
+ ...Subsection,
+ ...SwipeAction,
+ ...SwipeActionItem,
+ ...Swiper,
+ ...SwipterIndicator,
+ ...Switch,
+ ...Tabbar,
+ ...TabbarItem,
+ ...Tabs,
+ ...Tag,
+ ...Text,
+ ...Textarea,
+ ...Toast,
+ ...Toolbar,
+ ...Tooltip,
+ ...Transition,
+ ...Upload
+}
+
+function setConfig(configs) {
+ shallowMerge(config, configs.config || {})
+ shallowMerge(props, configs.props || {})
+ shallowMerge(color, configs.color || {})
+ shallowMerge(zIndex, configs.zIndex || {})
+}
+
+// 初始化自定义配置
+if (uni && uni.upuiParams) {
+ console.log('setting uview-plus')
+ let temp = uni.upuiParams()
+ if (temp.httpIns) {
+ temp.httpIns(http)
+ }
+ if (temp.options) {
+ setConfig(temp.options)
+ }
+}
+
+export default props
diff --git a/uni_modules/uview-plus/libs/config/zIndex.js b/uni_modules/uview-plus/libs/config/zIndex.js
new file mode 100644
index 0000000..5fc3682
--- /dev/null
+++ b/uni_modules/uview-plus/libs/config/zIndex.js
@@ -0,0 +1,20 @@
+// uniapp在H5中各API的z-index值如下:
+/**
+ * actionsheet: 999
+ * modal: 999
+ * navigate: 998
+ * tabbar: 998
+ * toast: 999
+ */
+
+export default {
+ toast: 10090,
+ noNetwork: 10080,
+ // popup包含popup,actionsheet,keyboard,picker的值
+ popup: 10075,
+ mask: 10070,
+ navbar: 980,
+ topTips: 975,
+ sticky: 970,
+ indexListSticky: 965
+}
diff --git a/uni_modules/uview-plus/libs/css/color.scss b/uni_modules/uview-plus/libs/css/color.scss
new file mode 100644
index 0000000..e930261
--- /dev/null
+++ b/uni_modules/uview-plus/libs/css/color.scss
@@ -0,0 +1,311 @@
+.u-primary-light {
+ color: $u-primary-light;
+}
+
+.u-warning-light {
+ color: $u-warning-light;
+}
+
+.u-success-light {
+ color: $u-success-light;
+}
+
+.u-error-light {
+ color: $u-error-light;
+}
+
+.u-info-light {
+ color: $u-info-light;
+}
+
+.u-primary-light-bg {
+ background-color: $u-primary-light;
+}
+
+.u-warning-light-bg {
+ background-color: $u-warning-light;
+}
+
+.u-success-light-bg {
+ background-color: $u-success-light;
+}
+
+.u-error-light-bg {
+ background-color: $u-error-light;
+}
+
+.u-info-light-bg {
+ background-color: $u-info-light;
+}
+
+.u-primary-dark {
+ color: $u-primary-dark;
+}
+
+.u-warning-dark {
+ color: $u-warning-dark;
+}
+
+.u-success-dark {
+ color: $u-success-dark;
+}
+
+.u-error-dark {
+ color: $u-error-dark;
+}
+
+.u-info-dark {
+ color: $u-info-dark;
+}
+
+.u-primary-dark-bg {
+ background-color: $u-primary-dark;
+}
+
+.u-warning-dark-bg {
+ background-color: $u-warning-dark;
+}
+
+.u-success-dark-bg {
+ background-color: $u-success-dark;
+}
+
+.u-error-dark-bg {
+ background-color: $u-error-dark;
+}
+
+.u-info-dark-bg {
+ background-color: $u-info-dark;
+}
+
+.u-primary-disabled {
+ color: $u-primary-disabled;
+}
+
+.u-warning-disabled {
+ color: $u-warning-disabled;
+}
+
+.u-success-disabled {
+ color: $u-success-disabled;
+}
+
+.u-error-disabled {
+ color: $u-error-disabled;
+}
+
+.u-info-disabled {
+ color: $u-info-disabled;
+}
+
+.u-primary {
+ color: $u-primary;
+}
+
+.u-warning {
+ color: $u-warning;
+}
+
+.u-success {
+ color: $u-success;
+}
+
+.u-error {
+ color: $u-error;
+}
+
+.u-info {
+ color: $u-info;
+}
+
+.u-primary-bg {
+ background-color: $u-primary;
+}
+
+.u-warning-bg {
+ background-color: $u-warning;
+}
+
+.u-success-bg {
+ background-color: $u-success;
+}
+
+.u-error-bg {
+ background-color: $u-error;
+}
+
+.u-info-bg {
+ background-color: $u-info;
+}
+
+.u-main-color {
+ color: $u-main-color;
+}
+
+.u-content-color {
+ color: $u-content-color;
+}
+
+.u-tips-color {
+ color: $u-tips-color;
+}
+
+.u-light-color {
+ color: $u-light-color;
+}
+
+.up-primary-light {
+ color: $u-primary-light;
+}
+
+.up-warning-light {
+ color: $u-warning-light;
+}
+
+.up-success-light {
+ color: $u-success-light;
+}
+
+.up-error-light {
+ color: $u-error-light;
+}
+
+.up-info-light {
+ color: $u-info-light;
+}
+
+.up-primary-light-bg {
+ background-color: $u-primary-light;
+}
+
+.up-warning-light-bg {
+ background-color: $u-warning-light;
+}
+
+.up-success-light-bg {
+ background-color: $u-success-light;
+}
+
+.up-error-light-bg {
+ background-color: $u-error-light;
+}
+
+.up-info-light-bg {
+ background-color: $u-info-light;
+}
+
+.up-primary-dark {
+ color: $u-primary-dark;
+}
+
+.up-warning-dark {
+ color: $u-warning-dark;
+}
+
+.up-success-dark {
+ color: $u-success-dark;
+}
+
+.up-error-dark {
+ color: $u-error-dark;
+}
+
+.up-info-dark {
+ color: $u-info-dark;
+}
+
+.up-primary-dark-bg {
+ background-color: $u-primary-dark;
+}
+
+.up-warning-dark-bg {
+ background-color: $u-warning-dark;
+}
+
+.up-success-dark-bg {
+ background-color: $u-success-dark;
+}
+
+.up-error-dark-bg {
+ background-color: $u-error-dark;
+}
+
+.up-info-dark-bg {
+ background-color: $u-info-dark;
+}
+
+.up-primary-disabled {
+ color: $u-primary-disabled;
+}
+
+.up-warning-disabled {
+ color: $u-warning-disabled;
+}
+
+.up-success-disabled {
+ color: $u-success-disabled;
+}
+
+.up-error-disabled {
+ color: $u-error-disabled;
+}
+
+.up-info-disabled {
+ color: $u-info-disabled;
+}
+
+.up-primary {
+ color: $u-primary;
+}
+
+.up-warning {
+ color: $u-warning;
+}
+
+.up-success {
+ color: $u-success;
+}
+
+.up-error {
+ color: $u-error;
+}
+
+.up-info {
+ color: $u-info;
+}
+
+.up-primary-bg {
+ background-color: $u-primary;
+}
+
+.up-warning-bg {
+ background-color: $u-warning;
+}
+
+.up-success-bg {
+ background-color: $u-success;
+}
+
+.up-error-bg {
+ background-color: $u-error;
+}
+
+.up-info-bg {
+ background-color: $u-info;
+}
+
+.up-main-color {
+ color: $u-main-color;
+}
+
+.up-content-color {
+ color: $u-content-color;
+}
+
+.up-tips-color {
+ color: $u-tips-color;
+}
+
+.up-light-color {
+ color: $u-light-color;
+}
diff --git a/uni_modules/uview-plus/libs/css/common.scss b/uni_modules/uview-plus/libs/css/common.scss
new file mode 100644
index 0000000..f6a3ae2
--- /dev/null
+++ b/uni_modules/uview-plus/libs/css/common.scss
@@ -0,0 +1,112 @@
+// 超出行数,自动显示行尾省略号,最多5行
+// 来自uview-plus的温馨提示:当您在控制台看到此报错,说明需要在App.vue的style标签加上【lang="scss"】
+@for $i from 1 through 10 {
+ .u-line-#{$i},
+ .up-line-#{$i} {
+ /* #ifdef APP-NVUE */
+ // nvue下,可以直接使用lines属性,这是weex特有样式
+ lines: $i;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ flex: 1;
+ /* #endif */
+
+ /* #ifndef APP-NVUE */
+ // vue下,单行和多行显示省略号需要单独处理
+ @if $i == 1 {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ } @else {
+ display: -webkit-box!important;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ word-break: break-all;
+ -webkit-line-clamp: $i;
+ -webkit-box-orient: vertical!important;
+ }
+ /* #endif */
+ }
+}
+
+
+// 此处加上!important并非随意乱用,而是因为目前*.nvue页面编译到H5时,
+// App.vue的样式会被uni-app的view元素的自带border属性覆盖,导致无效
+// 综上,这是uni-app的缺陷导致我们为了多端兼容,而必须要加上!important
+// 移动端兼容性较好,直接使用0.5px去实现细边框,不使用伪元素形式实现
+.u-border,
+.up-border {
+ border-width: 0.5px!important;
+ border-color: $u-border-color!important;
+ border-style: solid;
+}
+
+.u-border-top,
+.up-border-top {
+ border-top-width: 0.5px!important;
+ border-color: $u-border-color!important;
+ border-top-style: solid;
+}
+
+.u-border-left,
+.up-border-left {
+ border-left-width: 0.5px!important;
+ border-color: $u-border-color!important;
+ border-left-style: solid;
+}
+
+.u-border-right,
+.up-border-right {
+ border-right-width: 0.5px!important;
+ border-color: $u-border-color!important;
+ border-right-style: solid;
+}
+
+.u-border-bottom,
+.up-border-bottom {
+ border-bottom-width: 0.5px!important;
+ border-color: $u-border-color!important;
+ border-bottom-style: solid;
+}
+
+.u-border-top-bottom,
+.up-border-top-bottom {
+ border-top-width: 0.5px!important;
+ border-bottom-width: 0.5px!important;
+ border-color: $u-border-color!important;
+ border-top-style: solid;
+ border-bottom-style: solid;
+}
+
+// 去除button的所有默认样式,让其表现跟普通的view、text元素一样
+.u-reset-button,
+.up-reset-button {
+ padding: 0;
+ background-color: transparent;
+ /* #ifndef APP-PLUS */
+ font-size: inherit;
+ line-height: inherit;
+ color: inherit;
+ /* #endif */
+ /* #ifdef APP-NVUE */
+ border-width: 0;
+ /* #endif */
+}
+
+/* #ifndef APP-NVUE */
+.u-reset-button::after,
+.up-reset-button::after {
+ border: none;
+}
+/* #endif */
+
+.u-hover-class,
+.up-hover-class {
+ opacity: 0.7;
+}
+
+.cursor-pointer {
+ /* #ifdef H5 */
+ cursor: pointer;
+ /* #endif */
+}
diff --git a/uni_modules/uview-plus/libs/css/components.scss b/uni_modules/uview-plus/libs/css/components.scss
new file mode 100644
index 0000000..6610774
--- /dev/null
+++ b/uni_modules/uview-plus/libs/css/components.scss
@@ -0,0 +1,36 @@
+@import "./mixin.scss";
+
+/* #ifndef APP-NVUE */
+// 由于uview-plus是基于nvue环境进行开发的,此环境中普通元素默认为flex-direction: column;
+// 所以在非nvue中,需要对元素进行重置为flex-direction: column; 否则可能会表现异常
+// 2024-04-09由于微信小程序会提示 Some selectors are not allowed in component wxss所以注释以下几行
+// view,
+// scroll-view,
+// swiper-item,
+.u-empty,
+.u-empty__wrap,
+.u-transition,
+.u-tabs,
+.u-tabs__wrapper,
+.u-tabs__wrapper__scroll-view-wrapper,
+.u-tabs__wrapper__scroll-view,
+.u-tabs__wrapper__nav,
+.u-tabs__wrapper__nav__line,
+.up-empty,
+.up-empty__wrap,
+.up-transition,
+.up-tabs,
+.up-tabs__wrapper,
+.up-tabs__wrapper__scroll-view-wrapper,
+.up-tabs__wrapper__scroll-view,
+.up-tabs__wrapper__nav,
+.up-tabs__wrapper__nav__line {
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+ flex-grow: 0;
+ flex-basis: auto;
+ align-items: stretch;
+ align-content: flex-start;
+}
+/* #endif */
diff --git a/uni_modules/uview-plus/libs/css/flex.scss b/uni_modules/uview-plus/libs/css/flex.scss
new file mode 100644
index 0000000..06bdf96
--- /dev/null
+++ b/uni_modules/uview-plus/libs/css/flex.scss
@@ -0,0 +1,368 @@
+// .u-flex {
+// @include vue-flex(row);
+// }
+
+// .u-flex-x {
+// @include vue-flex(row);
+// }
+
+// .u-flex-y {
+// @include vue-flex(column);
+// }
+
+// .u-flex-xy-center {
+// @include vue-flex(row);
+// justify-content: center;
+// align-items: center;
+// }
+
+// .u-flex-x-center {
+// @include vue-flex(row);
+// justify-content: center;
+// }
+
+// .u-flex-y-center {
+// @include vue-flex(column);
+// justify-content: center;
+// }
+
+
+// flex布局
+.u-flex,
+.u-flex-row,
+.u-flex-x,
+.up-flex,
+.up-flex-row,
+.up-flex-x {
+ @include flex;
+ display: flex !important;
+ flex-direction: row !important;
+}
+
+.u-flex-y,
+.u-flex-column,
+.up-flex-y,
+.up-flex-column {
+ @include flex(column);
+ display: flex !important;
+ flex-direction: column !important;
+}
+
+.u-flex-x-center,
+.up-flex-x-center {
+ @include flex;
+ justify-content: center !important;
+}
+
+.u-flex-xy-center,
+.up-flex-xy-center {
+ @include flex;
+ justify-content: center !important;
+ align-items: center !important;
+}
+
+.u-flex-y-center,
+.up-flex-y-center {
+ @include flex;
+ align-items: center !important;
+}
+
+.u-flex-x-left,
+.up-flex-x-left {
+ @include flex;
+}
+
+.u-flex-x-reverse,
+.u-flex-row-reverse,
+.up-flex-x-reverse,
+.up-flex-row-reverse {
+ flex-direction: row-reverse !important;
+}
+
+.u-flex-y-reverse,
+.u-flex-column-reverse,
+.up-flex-y-reverse,
+.up-flex-column-reverse {
+ flex-direction: column-reverse !important;
+}
+
+/* #ifndef APP-NVUE */
+// 此处为vue版本的简写,因为nvue不支持同时作用于两个类名的样式写法
+// nvue下只能写成class="u-flex-x u-flex-x-reverse的形式"
+.u-flex.u-flex-reverse,
+.u-flex-row.u-flex-reverse,
+.u-flex-x.u-flex-reverse,
+.up-flex.up-flex-reverse,
+.up-flex-row.up-flex-reverse,
+.up-flex-x.up-flex-reverse {
+ flex-direction: row-reverse !important;
+}
+
+.u-flex-column.u-flex-reverse,
+.u-flex-y.u-flex-reverse,
+.up-flex-column.up-flex-reverse,
+.up-flex-y.up-flex-reverse {
+ flex-direction: column-reverse !important;
+}
+
+// 自动伸缩
+.u-flex-fill,
+.up-flex-fill {
+ flex: 1 1 auto !important;
+}
+
+// 边界自动伸缩
+.u-margin-top-auto,
+.u-m-t-auto,
+.up-margin-top-auto,
+.up-m-t-auto {
+ margin-top: auto !important;
+}
+
+.u-margin-right-auto,
+.u-m-r-auto,
+.up-margin-right-auto,
+.up-m-r-auto {
+ margin-right: auto !important;
+}
+
+.u-margin-bottom-auto,
+.u-m-b-auto,
+.up-margin-bottom-auto,
+.up-m-b-auto {
+ margin-bottom: auto !important;
+}
+
+.u-margin-left-auto,
+.u-m-l-auto,
+.up-margin-left-auto,
+.up-m-l-auto {
+ margin-left: auto !important;
+}
+
+.u-margin-center-auto,
+.u-m-c-auto,
+.up-margin-center-auto,
+.up-m-c-auto {
+ margin-left: auto !important;
+ margin-right: auto !important;
+}
+
+.u-margin-middle-auto,
+.u-m-m-auto,
+.up-margin-middle-auto,
+.up-m-m-auto {
+ margin-top: auto !important;
+ margin-bottom: auto !important;
+}
+/* #endif */
+
+// 换行
+.u-flex-wrap,
+.up-flex-wrap {
+ flex-wrap: wrap !important;
+}
+
+// 反向换行
+.u-flex-wrap-reverse,
+.up-flex-wrap-reverse {
+ flex-wrap: wrap-reverse !important;
+}
+
+// 主轴起点对齐
+.u-flex-start,
+.up-flex-start {
+ justify-content: flex-start !important;
+}
+
+// 主轴中间对齐
+.u-flex-center,
+.up-flex-center {
+ justify-content: center !important;
+}
+
+// 主轴终点对齐
+.u-flex-end,
+.up-flex-end {
+ justify-content: flex-end !important;
+}
+
+// 主轴等比间距
+.u-flex-between,
+.up-flex-between {
+ justify-content: space-between !important;
+}
+
+// 主轴均分间距
+.u-flex-around,
+.up-flex-around {
+ justify-content: space-around !important;
+}
+
+// 交叉轴起点对齐
+.u-flex-items-start,
+.up-flex-items-start {
+ align-items: flex-start !important;
+}
+
+// 交叉轴中间对齐
+.u-flex-items-center,
+.up-flex-items-center {
+ align-items: center !important;
+}
+
+// 交叉轴终点对齐
+.u-flex-items-end,
+.up-flex-items-end {
+ align-items: flex-end !important;
+}
+
+// 交叉轴第一行文字基线对齐
+.u-flex-items-baseline,
+.up-flex-items-baseline {
+ /* #ifndef APP-NVUE */
+ align-items: baseline !important;
+ /* #endif */
+}
+
+// 交叉轴方向拉伸对齐
+.u-flex-items-stretch,
+.up-flex-items-stretch {
+ align-items: stretch !important;
+}
+
+
+// 以下属于项目(子元素)的类
+
+// 子元素交叉轴起点对齐
+/* #ifndef APP-NVUE */
+.u-flex-self-start,
+.up-flex-self-start {
+ align-self: flex-start !important;
+}
+
+// 子元素交叉轴居中对齐
+.u-flex-self-center,
+.up-flex-self-center {
+ align-self: center !important;
+}
+
+// 子元素交叉轴终点对齐
+.u-flex-self-end,
+.up-flex-self-end {
+ align-self: flex-en !important;
+}
+
+// 子元素交叉轴第一行文字基线对齐
+.u-flex-self-baseline,
+.up-flex-self-baseline {
+ align-self: baseline !important;
+}
+
+// 子元素交叉轴方向拉伸对齐
+.u-flex-self-stretch,
+.up-flex-self-stretch {
+ align-self: stretch !important;
+}
+/* #endif */
+// 多轴交叉时的对齐方式
+
+// 起点对齐
+/* #ifndef APP-NVUE */
+.u-flex-content-start,
+.up-flex-content-start {
+ align-content: flex-start !important;
+}
+
+// 居中对齐
+.u-flex-content-center,
+.up-flex-content-center {
+ align-content: center !important;
+}
+
+// 终点对齐
+.u-flex-content-end,
+.up-flex-content-end {
+ align-content: flex-end !important;
+}
+
+// 两端对齐
+.u-flex-content-between,
+.up-flex-content-between {
+ align-content: space-between !important;
+}
+
+// 均分间距
+.u-flex-content-around,
+.up-flex-content-around {
+ align-content: space-around !important;
+}
+
+// 全部居中对齐
+.u-flex-middle,
+.up-flex-middle {
+ justify-content: center !important;
+ align-items: center !important;
+ align-self: center !important;
+ align-content: center !important;
+}
+
+// 是否可以放大
+.u-flex-grow,
+.up-flex-grow {
+ flex-grow: 1 !important;
+}
+
+// 是否可以缩小
+.u-flex-shrink,
+.up-flex-shrink {
+ flex-shrink: 1 !important;
+}
+
+/* #endif */
+
+// 定义内外边距,历遍1-80
+@for $i from 0 through 80 {
+ // 只要双数和能被5除尽的数
+ @if $i % 2 == 0 or $i % 5 == 0 {
+ // 得出:up-margin-30或者u-m-30
+ .u-margin-#{$i}, .u-m-#{$i},
+ .up-margin-#{$i}, .up-m-#{$i} {
+ margin: $i + rpx!important;
+ }
+
+ // 得出:up-padding-30或者u-p-30
+ .u-padding-#{$i}, .u-p-#{$i},
+ .up-padding-#{$i}, .up-p-#{$i} {
+ padding: $i + rpx!important;
+ }
+
+ @each $short, $long in l left, t top, r right, b bottom {
+ // 缩写版,结果如: up-m-l-30
+ // 定义外边距
+ .u-m-#{$short}-#{$i},
+ .up-m-#{$short}-#{$i} {
+ margin-#{$long}: $i + rpx!important;
+ }
+
+ // 定义内边距
+ .u-p-#{$short}-#{$i},
+ .up-p-#{$short}-#{$i} {
+ padding-#{$long}: $i + rpx!important;
+ }
+
+ // 完整版,结果如:up-margin-left-30
+ // 定义外边距
+ .u-margin-#{$long}-#{$i},
+ .up-margin-#{$long}-#{$i} {
+ margin-#{$long}: $i + rpx!important;
+ }
+
+ // 定义内边距
+ .u-padding-#{$long}-#{$i},
+ .up-padding-#{$long}-#{$i} {
+ padding-#{$long}: $i + rpx!important;
+ }
+ }
+ }
+}
diff --git a/uni_modules/uview-plus/libs/css/h5.scss b/uni_modules/uview-plus/libs/css/h5.scss
new file mode 100644
index 0000000..e69de29
diff --git a/uni_modules/uview-plus/libs/css/mixin.scss b/uni_modules/uview-plus/libs/css/mixin.scss
new file mode 100644
index 0000000..7e35b3b
--- /dev/null
+++ b/uni_modules/uview-plus/libs/css/mixin.scss
@@ -0,0 +1,8 @@
+// 通过scss的mixin功能,把原来需要写4行的css,变成一行
+// 目的是保持代码干净整洁,不至于在nvue下,到处都要写display:flex的条件编译
+@mixin flex($direction: row) {
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ flex-direction: $direction;
+}
diff --git a/uni_modules/uview-plus/libs/css/mp.scss b/uni_modules/uview-plus/libs/css/mp.scss
new file mode 100644
index 0000000..e69de29
diff --git a/uni_modules/uview-plus/libs/css/nvue.scss b/uni_modules/uview-plus/libs/css/nvue.scss
new file mode 100644
index 0000000..e69de29
diff --git a/uni_modules/uview-plus/libs/css/vue.scss b/uni_modules/uview-plus/libs/css/vue.scss
new file mode 100644
index 0000000..32949e0
--- /dev/null
+++ b/uni_modules/uview-plus/libs/css/vue.scss
@@ -0,0 +1,28 @@
+// 历遍生成4个方向的底部安全区
+@each $d in top, right, bottom, left {
+ .u-safe-area-inset-#{$d},
+ .up-safe-area-inset-#{$d} {
+ padding-#{$d}: 0;
+ padding-#{$d}: constant(safe-area-inset-#{$d});
+ padding-#{$d}: env(safe-area-inset-#{$d});
+ }
+}
+
+//提升H5端uni.toast()的层级,避免被uview-plus的modal等遮盖
+/* #ifdef H5 */
+uni-toast {
+ z-index: 10090;
+}
+uni-toast .uni-toast {
+ z-index: 10090;
+}
+/* #endif */
+
+// 隐藏scroll-view的滚动条
+::-webkit-scrollbar {
+ display: none;
+ width: 0 !important;
+ height: 0 !important;
+ -webkit-appearance: none;
+ background: transparent;
+}
diff --git a/uni_modules/uview-plus/libs/function/calc.js b/uni_modules/uview-plus/libs/function/calc.js
new file mode 100644
index 0000000..3cd9288
--- /dev/null
+++ b/uni_modules/uview-plus/libs/function/calc.js
@@ -0,0 +1,64 @@
+// 浮点数加法
+export function add (arg1, arg2) {
+ var r1, r2, m
+ try {
+ r1 = arg1.toString().split('.')[1].length
+ } catch (e) {
+ r1 = 0
+ }
+ try {
+ r2 = arg2.toString().split('.')[1].length
+ } catch (e) {
+ r2 = 0
+ }
+ m = Math.pow(10, Math.max(r1, r2))
+ return (arg1 * m + arg2 * m) / m
+}
+// 浮点数减法
+export function sub (arg1, arg2) {
+ var r1, r2, m, n
+ try {
+ r1 = arg1.toString().split('.')[1].length
+ } catch (e) {
+ r1 = 0
+ }
+ try {
+ r2 = arg2.toString().split('.')[1].length
+ } catch (e) {
+ r2 = 0
+ }
+ m = Math.pow(10, Math.max(r1, r2))
+ n = (r1 >= r2) ? r1 : r2
+ return Math.abs(((arg1 * m - arg2 * m) / m).toFixed(n))
+}
+//浮点乘法
+export function mul (a, b) {
+ var c = 0,
+ d = a.toString(),
+ e = b.toString();
+ try {
+ c += d.split(".")[1].length;
+ } catch (f) {}
+ try {
+ c += e.split(".")[1].length;
+ } catch (f) {}
+ return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c);
+}
+//浮点除法
+export function div (a, b) {
+ var c, d, e = 0,
+ f = 0;
+ try {
+ e = a.toString().split(".")[1].length;
+ } catch (g) {}
+ try {
+ f = b.toString().split(".")[1].length;
+ } catch (g) {}
+ return c = Number(a.toString().replace(".", "")), d = Number(b.toString().replace(".", "")), xyutil.mul(c / d, Math.pow(10, f - e));
+}
+export default {
+ add,
+ sub,
+ mul,
+ div
+}
diff --git a/uni_modules/uview-plus/libs/function/colorGradient.js b/uni_modules/uview-plus/libs/function/colorGradient.js
new file mode 100644
index 0000000..174f894
--- /dev/null
+++ b/uni_modules/uview-plus/libs/function/colorGradient.js
@@ -0,0 +1,134 @@
+/**
+ * 求两个颜色之间的渐变值
+ * @param {string} startColor 开始的颜色
+ * @param {string} endColor 结束的颜色
+ * @param {number} step 颜色等分的份额
+ * */
+export function colorGradient(startColor = 'rgb(0, 0, 0)', endColor = 'rgb(255, 255, 255)', step = 10) {
+ const startRGB = hexToRgb(startColor, false) // 转换为rgb数组模式
+ const startR = startRGB[0]
+ const startG = startRGB[1]
+ const startB = startRGB[2]
+
+ const endRGB = hexToRgb(endColor, false)
+ const endR = endRGB[0]
+ const endG = endRGB[1]
+ const endB = endRGB[2]
+
+ const sR = (endR - startR) / step // 总差值
+ const sG = (endG - startG) / step
+ const sB = (endB - startB) / step
+ const colorArr = []
+ for (let i = 0; i < step; i++) {
+ // 计算每一步的hex值
+ let hex = rgbToHex(`rgb(${Math.round((sR * i + startR))},${Math.round((sG * i + startG))},${Math.round((sB
+ * i + startB))})`)
+ // 确保第一个颜色值为startColor的值
+ if (i === 0) hex = rgbToHex(startColor)
+ // 确保最后一个颜色值为endColor的值
+ if (i === step - 1) hex = rgbToHex(endColor)
+ colorArr.push(hex)
+ }
+ return colorArr
+}
+
+// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
+export function hexToRgb(sColor, str = true) {
+ const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
+ sColor = String(sColor).toLowerCase()
+ if (sColor && reg.test(sColor)) {
+ if (sColor.length === 4) {
+ let sColorNew = '#'
+ for (let i = 1; i < 4; i += 1) {
+ sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
+ }
+ sColor = sColorNew
+ }
+ // 处理六位的颜色值
+ const sColorChange = []
+ for (let i = 1; i < 7; i += 2) {
+ sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`))
+ }
+ if (!str) {
+ return sColorChange
+ }
+ return `rgb(${sColorChange[0]},${sColorChange[1]},${sColorChange[2]})`
+ } if (/^(rgb|RGB)/.test(sColor)) {
+ const arr = sColor.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
+ return arr.map((val) => Number(val))
+ }
+ return sColor
+}
+
+// 将rgb表示方式转换为hex表示方式
+export function rgbToHex(rgb) {
+ const _this = rgb
+ const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
+ if (/^(rgb|RGB)/.test(_this)) {
+ const aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
+ let strHex = '#'
+ for (let i = 0; i < aColor.length; i++) {
+ let hex = Number(aColor[i]).toString(16)
+ hex = String(hex).length == 1 ? `${0}${hex}` : hex // 保证每个rgb的值为2位
+ if (hex === '0') {
+ hex += hex
+ }
+ strHex += hex
+ }
+ if (strHex.length !== 7) {
+ strHex = _this
+ }
+ return strHex
+ } if (reg.test(_this)) {
+ const aNum = _this.replace(/#/, '').split('')
+ if (aNum.length === 6) {
+ return _this
+ } if (aNum.length === 3) {
+ let numHex = '#'
+ for (let i = 0; i < aNum.length; i += 1) {
+ numHex += (aNum[i] + aNum[i])
+ }
+ return numHex
+ }
+ } else {
+ return _this
+ }
+}
+
+/**
+* JS颜色十六进制转换为rgb或rgba,返回的格式为 rgba(255,255,255,0.5)字符串
+* sHex为传入的十六进制的色值
+* alpha为rgba的透明度
+*/
+export function colorToRgba(color, alpha) {
+ color = rgbToHex(color)
+ // 十六进制颜色值的正则表达式
+ const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
+ /* 16进制颜色转为RGB格式 */
+ let sColor = String(color).toLowerCase()
+ if (sColor && reg.test(sColor)) {
+ if (sColor.length === 4) {
+ let sColorNew = '#'
+ for (let i = 1; i < 4; i += 1) {
+ sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
+ }
+ sColor = sColorNew
+ }
+ // 处理六位的颜色值
+ const sColorChange = []
+ for (let i = 1; i < 7; i += 2) {
+ sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`))
+ }
+ // return sColorChange.join(',')
+ return `rgba(${sColorChange.join(',')},${alpha})`
+ }
+
+ return sColor
+}
+
+export default {
+ colorGradient,
+ hexToRgb,
+ rgbToHex,
+ colorToRgba
+}
diff --git a/uni_modules/uview-plus/libs/function/debounce.js b/uni_modules/uview-plus/libs/function/debounce.js
new file mode 100644
index 0000000..d424aa4
--- /dev/null
+++ b/uni_modules/uview-plus/libs/function/debounce.js
@@ -0,0 +1,29 @@
+let timeout = null
+
+/**
+ * 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数
+ *
+ * @param {Function} func 要执行的回调函数
+ * @param {Number} wait 延时的时间
+ * @param {Boolean} immediate 是否立即执行
+ * @return null
+ */
+export function debounce(func, wait = 500, immediate = false) {
+ // 清除定时器
+ if (timeout !== null) clearTimeout(timeout)
+ // 立即执行,此类情况一般用不到
+ if (immediate) {
+ const callNow = !timeout
+ timeout = setTimeout(() => {
+ timeout = null
+ }, wait)
+ if (callNow) typeof func === 'function' && func()
+ } else {
+ // 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
+ timeout = setTimeout(() => {
+ typeof func === 'function' && func()
+ }, wait)
+ }
+}
+
+export default debounce
diff --git a/uni_modules/uview-plus/libs/function/digit.js b/uni_modules/uview-plus/libs/function/digit.js
new file mode 100644
index 0000000..470bea6
--- /dev/null
+++ b/uni_modules/uview-plus/libs/function/digit.js
@@ -0,0 +1,167 @@
+let _boundaryCheckingState = true; // 是否进行越界检查的全局开关
+
+/**
+ * 把错误的数据转正
+ * @private
+ * @example strip(0.09999999999999998)=0.1
+ */
+export function strip(num, precision = 15) {
+ return +parseFloat(Number(num).toPrecision(precision));
+}
+
+/**
+ * Return digits length of a number
+ * @private
+ * @param {*number} num Input number
+ */
+export function digitLength(num) {
+ // Get digit length of e
+ const eSplit = num.toString().split(/[eE]/);
+ const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);
+ return len > 0 ? len : 0;
+}
+
+/**
+ * 把小数转成整数,如果是小数则放大成整数
+ * @private
+ * @param {*number} num 输入数
+ */
+export function float2Fixed(num) {
+ if (num.toString().indexOf('e') === -1) {
+ return Number(num.toString().replace('.', ''));
+ }
+ const dLen = digitLength(num);
+ return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num);
+}
+
+/**
+ * 检测数字是否越界,如果越界给出提示
+ * @private
+ * @param {*number} num 输入数
+ */
+export function checkBoundary(num) {
+ if (_boundaryCheckingState) {
+ if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
+ console.warn(`${num} 超出了精度限制,结果可能不正确`);
+ }
+ }
+}
+
+/**
+ * 把递归操作扁平迭代化
+ * @param {number[]} arr 要操作的数字数组
+ * @param {function} operation 迭代操作
+ * @private
+ */
+export function iteratorOperation(arr, operation) {
+ const [num1, num2, ...others] = arr;
+ let res = operation(num1, num2);
+
+ others.forEach((num) => {
+ res = operation(res, num);
+ });
+
+ return res;
+}
+
+/**
+ * 高精度乘法
+ * @export
+ */
+export function times(...nums) {
+ if (nums.length > 2) {
+ return iteratorOperation(nums, times);
+ }
+
+ const [num1, num2] = nums;
+ const num1Changed = float2Fixed(num1);
+ const num2Changed = float2Fixed(num2);
+ const baseNum = digitLength(num1) + digitLength(num2);
+ const leftValue = num1Changed * num2Changed;
+
+ checkBoundary(leftValue);
+
+ return leftValue / Math.pow(10, baseNum);
+}
+
+/**
+ * 高精度加法
+ * @export
+ */
+export function plus(...nums) {
+ if (nums.length > 2) {
+ return iteratorOperation(nums, plus);
+ }
+
+ const [num1, num2] = nums;
+ // 取最大的小数位
+ const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
+ // 把小数都转为整数然后再计算
+ return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
+}
+
+/**
+ * 高精度减法
+ * @export
+ */
+export function minus(...nums) {
+ if (nums.length > 2) {
+ return iteratorOperation(nums, minus);
+ }
+
+ const [num1, num2] = nums;
+ const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
+ return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
+}
+
+/**
+ * 高精度除法
+ * @export
+ */
+export function divide(...nums) {
+ if (nums.length > 2) {
+ return iteratorOperation(nums, divide);
+ }
+
+ const [num1, num2] = nums;
+ const num1Changed = float2Fixed(num1);
+ const num2Changed = float2Fixed(num2);
+ checkBoundary(num1Changed);
+ checkBoundary(num2Changed);
+ // 重要,这里必须用strip进行修正
+ return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));
+}
+
+/**
+ * 四舍五入
+ * @export
+ */
+export function round(num, ratio) {
+ const base = Math.pow(10, ratio);
+ let result = divide(Math.round(Math.abs(times(num, base))), base);
+ if (num < 0 && result !== 0) {
+ result = times(result, -1);
+ }
+ // 位数不足则补0
+ return result;
+}
+
+/**
+ * 是否进行边界检查,默认开启
+ * @param flag 标记开关,true 为开启,false 为关闭,默认为 true
+ * @export
+ */
+export function enableBoundaryChecking(flag = true) {
+ _boundaryCheckingState = flag;
+}
+
+
+export default {
+ times,
+ plus,
+ minus,
+ divide,
+ round,
+ enableBoundaryChecking,
+};
+
diff --git a/uni_modules/uview-plus/libs/function/http.js b/uni_modules/uview-plus/libs/function/http.js
new file mode 100644
index 0000000..f4e8f66
--- /dev/null
+++ b/uni_modules/uview-plus/libs/function/http.js
@@ -0,0 +1,4 @@
+// 全局挂载引入http相关请求拦截插件
+import Request from '../luch-request'
+const http = new Request()
+export default http
diff --git a/uni_modules/uview-plus/libs/function/index.js b/uni_modules/uview-plus/libs/function/index.js
new file mode 100644
index 0000000..7d71082
--- /dev/null
+++ b/uni_modules/uview-plus/libs/function/index.js
@@ -0,0 +1,878 @@
+import {
+ number as testNumber,
+ array as testArray,
+ empty as testEmpty
+} from './test'
+import { round } from './digit.js'
+import config from '../config/config';
+/**
+ * @description 如果value小于min,取min;如果value大于max,取max
+ * @param {number} min
+ * @param {number} max
+ * @param {number} value
+ */
+export function range(min = 0, max = 0, value = 0) {
+ return Math.max(min, Math.min(max, Number(value)))
+}
+
+/**
+ * @description 用于获取用户传递值的px值 如果用户传递了"xxpx"或者"xxrpx",取出其数值部分,如果是"xxxrpx"还需要用过uni.rpx2px进行转换
+ * @param {number|string} value 用户传递值的px值
+ * @param {boolean} unit
+ * @returns {number|string}
+ */
+export function getPx(value, unit = false) {
+ if (testNumber(value)) {
+ return unit ? `${value}px` : Number(value)
+ }
+ // 如果带有rpx,先取出其数值部分,再转为px值
+ if (/(rpx|upx)$/.test(value)) {
+ return unit ? `${uni.upx2px(parseInt(value))}px` : Number(uni.upx2px(parseInt(value)))
+ }
+ return unit ? `${parseInt(value)}px` : parseInt(value)
+}
+
+/**
+ * @description 用于统一rpx2px方法,因uni-app现有API未统一。
+ * @param {number} value 用户传递值的rpx值
+ * @returns {number}
+ */
+export function rpx2px(value) {
+ // #ifdef APP
+ return uni.upx2px(value)
+ // #endif
+ // #ifndef APP
+ return uni.rpx2px(value)
+ // #endif
+}
+
+/**
+ * @description 进行延时,以达到可以简写代码的目的 比如: await uni.$u.sleep(20)将会阻塞20ms
+ * @param {number} value 堵塞时间 单位ms 毫秒
+ * @returns {Promise} 返回promise
+ */
+export function sleep(value = 30) {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve()
+ }, value)
+ })
+}
+/**
+ * @description 运行期判断平台
+ * @returns {string} 返回所在平台(小写)
+ * @link 运行期判断平台 https://uniapp.dcloud.io/frame?id=判断平台
+ */
+export function os() {
+ // #ifdef APP || H5 || MP-WEIXIN
+ return uni.getDeviceInfo().platform.toLowerCase()
+ // #endif
+ // #ifndef APP || H5 || MP-WEIXIN
+ return uni.getSystemInfoSync().platform.toLowerCase()
+ // #endif
+}
+/**
+ * @description 获取系统信息同步接口
+ * @link 获取系统信息同步接口 https://uniapp.dcloud.io/api/system/info?id=getsysteminfosync
+ */
+export function sys() {
+ return uni.getSystemInfoSync()
+}
+export function getWindowInfo() {
+ let ret = {}
+ // #ifdef APP || H5 || MP-WEIXIN
+ ret = uni.getWindowInfo()
+ // #endif
+ // #ifndef APP || H5 || MP-WEIXIN
+ ret = sys()
+ // #endif
+ return ret
+}
+export function getDeviceInfo() {
+ let ret = {}
+ // #ifdef APP || H5 || MP-WEIXIN
+ ret = uni.getDeviceInfo()
+ // #endif
+ // #ifndef APP || H5 || MP-WEIXIN
+ ret = sys()
+ // #endif
+ return ret
+}
+
+/**
+ * @description 取一个区间数
+ * @param {Number} min 最小值
+ * @param {Number} max 最大值
+ */
+export function random(min, max) {
+ if (min >= 0 && max > 0 && max >= min) {
+ const gab = max - min + 1
+ return Math.floor(Math.random() * gab + min)
+ }
+ return 0
+}
+
+/**
+ * @param {Number} len uuid的长度
+ * @param {Boolean} firstU 将返回的首字母置为"u"
+ * @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制
+ */
+export function guid(len = 32, firstU = true, radix = null) {
+ const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
+ const uuid = []
+ radix = radix || chars.length
+
+ if (len) {
+ // 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
+ for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]
+ } else {
+ let r
+ // rfc4122标准要求返回的uuid中,某些位为固定的字符
+ uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
+ uuid[14] = '4'
+
+ for (let i = 0; i < 36; i++) {
+ if (!uuid[i]) {
+ r = 0 | Math.random() * 16
+ uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]
+ }
+ }
+ }
+ // 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
+ if (firstU) {
+ uuid.shift()
+ return `u${uuid.join('')}`
+ }
+ return uuid.join('')
+}
+
+/**
+* @description 获取父组件的参数,因为支付宝小程序不支持provide/inject的写法
+ this.$parent在非H5中,可以准确获取到父组件,但是在H5中,需要多次this.$parent.$parent.xxx
+ 这里默认值等于undefined有它的含义,因为最顶层元素(组件)的$parent就是undefined,意味着不传name
+ 值(默认为undefined),就是查找最顶层的$parent
+* @param {string|undefined} name 父组件的参数名
+*/
+export function $parent(name = undefined) {
+ let parent = this.$parent
+ // 通过while历遍,这里主要是为了H5需要多层解析的问题
+ while (parent) {
+ // 父组件
+ let name2 = ''
+ if (name.startsWith('up-')) {
+ name2 = name.replace(/up-([a-zA-Z0-9-_]+)/g, 'u-$1')
+ } else if (name.startsWith('u-')) {
+ name2 = name.replace(/u-([a-zA-Z0-9-_]+)/g, 'up-$1')
+ }
+ if (parent.$options && parent.$options.name !== name && parent.$options.name !== name2) {
+ // 如果组件的name不相等,继续上一级寻找
+ parent = parent.$parent
+ } else {
+ return parent
+ }
+ }
+ return false
+}
+
+/**
+ * @description 样式转换
+ * 对象转字符串,或者字符串转对象
+ * @param {object | string} customStyle 需要转换的目标
+ * @param {String} target 转换的目的,object-转为对象,string-转为字符串
+ * @returns {object|string}
+ */
+export function addStyle(customStyle, target = 'object') {
+ // 字符串转字符串,对象转对象情形,直接返回
+ if (testEmpty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target === 'string' &&
+ typeof(customStyle) === 'string') {
+ return customStyle
+ }
+ // 字符串转对象
+ if (target === 'object') {
+ // 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的
+ customStyle = trim(customStyle)
+ // 根据";"将字符串转为数组形式
+ const styleArray = customStyle.split(';')
+ const style = {}
+ // 历遍数组,拼接成对象
+ for (let i = 0; i < styleArray.length; i++) {
+ // 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤
+ if (styleArray[i]) {
+ const item = styleArray[i].split(':')
+ style[trim(item[0])] = trim(item[1])
+ }
+ }
+ return style
+ }
+ // 这里为对象转字符串形式
+ let string = ''
+ if (typeof customStyle === 'object') {
+ customStyle.forEach((val, i) => {
+ // 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名
+ const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
+ string += `${key}:${val};`
+ })
+ }
+ // 去除两端空格
+ return trim(string)
+}
+
+/**
+ * @description 添加单位,如果有rpx,upx,%,px等单位结尾或者值为auto,直接返回,否则加上px单位结尾
+ * @param {string|number} value 需要添加单位的值
+ * @param {string} unit 添加的单位名 比如px
+ */
+export function addUnit(value = 'auto', unit = '') {
+ if (!unit) {
+ unit = config.unit || 'px'
+ }
+ if (unit == 'rpx' && testNumber(String(value))) {
+ value = value * 2
+ }
+ value = String(value)
+ // 用内置验证规则中的number判断是否为数值
+ return testNumber(value) ? `${value}${unit}` : value
+}
+
+/**
+ * @description 深度克隆
+ * @param {object} obj 需要深度克隆的对象
+ * @returns {*} 克隆后的对象或者原值(不是对象)
+ */
+export function deepClone(obj) {
+ // 对常见的“非”值,直接返回原来值
+ if ([null, undefined, NaN, false].includes(obj)) return obj
+ if (typeof obj !== 'object' && typeof obj !== 'function') {
+ // 原始类型直接返回
+ return obj
+ }
+ const o = testArray(obj) ? [] : {}
+ for (const i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ o[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
+ }
+ }
+ return o
+}
+
+/**
+ * @description JS对象深度合并
+ * @param {object} target 需要拷贝的对象
+ * @param {object} source 拷贝的来源对象
+ * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象)
+ */
+export function deepMerge(targetOrigin = {}, source = {}) {
+ let target = deepClone(targetOrigin)
+ if (typeof target !== 'object' || typeof source !== 'object') return false
+ for (const prop in source) {
+ if (!source.hasOwnProperty(prop)) continue
+ if (prop in target) {
+ if (source[prop] == null) {
+ target[prop] = source[prop]
+ }else if (typeof target[prop] !== 'object') {
+ target[prop] = source[prop]
+ } else if (typeof source[prop] !== 'object') {
+ target[prop] = source[prop]
+ } else if (target[prop].concat && source[prop].concat) {
+ target[prop] = target[prop].concat(source[prop])
+ } else {
+ target[prop] = deepMerge(target[prop], source[prop])
+ }
+ } else {
+ target[prop] = source[prop]
+ }
+ }
+ return target
+}
+/**
+ * @description JS对象深度合并
+ * @param {object} target 需要拷贝的对象
+ * @param {object} source 拷贝的来源对象
+ * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象)
+ */
+export function shallowMerge(target, source = {}) {
+ if (typeof target !== 'object' || typeof source !== 'object') return false
+ for (const prop in source) {
+ if (!source.hasOwnProperty(prop)) continue
+ if (prop in target) {
+ if (source[prop] == null) {
+ target[prop] = source[prop]
+ }else if (typeof target[prop] !== 'object') {
+ target[prop] = source[prop]
+ } else if (typeof source[prop] !== 'object') {
+ target[prop] = source[prop]
+ } else if (target[prop].concat && source[prop].concat) {
+ target[prop] = target[prop].concat(source[prop])
+ } else {
+ target[prop] = shallowMerge(target[prop], source[prop])
+ }
+ } else {
+ target[prop] = source[prop]
+ }
+ }
+ return target
+}
+
+/**
+ * @description error提示
+ * @param {*} err 错误内容
+ */
+export function error(err) {
+ // 开发环境才提示,生产环境不会提示
+ if (process.env.NODE_ENV === 'development') {
+ console.error(`uView提示:${err}`)
+ }
+}
+
+/**
+ * @description 打乱数组
+ * @param {array} array 需要打乱的数组
+ * @returns {array} 打乱后的数组
+ */
+export function randomArray(array = []) {
+ // 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0
+ return array.sort(() => Math.random() - 0.5)
+}
+
+// padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序
+// 所以这里做一个兼容polyfill的兼容处理
+if (!String.prototype.padStart) {
+ // 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解
+ String.prototype.padStart = function(maxLength, fillString = ' ') {
+ if (Object.prototype.toString.call(fillString) !== '[object String]') {
+ throw new TypeError(
+ 'fillString must be String'
+ )
+ }
+ const str = this
+ // 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉
+ if (str.length >= maxLength) return String(str)
+
+ const fillLength = maxLength - str.length
+ let times = Math.ceil(fillLength / fillString.length)
+ while (times >>= 1) {
+ fillString += fillString
+ if (times === 1) {
+ fillString += fillString
+ }
+ }
+ return fillString.slice(0, fillLength) + str
+ }
+}
+
+/**
+ * @description 格式化时间
+ * @param {String|Number} dateTime 需要格式化的时间戳
+ * @param {String} fmt 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd
+ * @returns {string} 返回格式化后的字符串
+ */
+export function timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') {
+ let date
+ // 若传入时间为假值,则取当前时间
+ if (!dateTime) {
+ date = new Date()
+ }
+ // 若为unix秒时间戳,则转为毫秒时间戳(逻辑有点奇怪,但不敢改,以保证历史兼容)
+ else if (/^\d{10}$/.test(dateTime.toString().trim())) {
+ date = new Date(dateTime * 1000)
+ }
+ // 若用户传入字符串格式时间戳,new Date无法解析,需做兼容
+ else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) {
+ date = new Date(Number(dateTime))
+ }
+ // 检查是否为UTC格式的时间字符串 (2024-12-18T02:25:31.432Z)
+ else if (typeof dateTime === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(dateTime)) {
+ date = new Date(dateTime)
+ }
+ // 其他都认为符合 RFC 2822 规范
+ else {
+ // 处理平台性差异,在Safari/Webkit中,new Date仅支持/作为分割符的字符串时间
+ date = new Date(
+ typeof dateTime === 'string'
+ ? dateTime.replace(/-/g, '/')
+ : dateTime
+ )
+ }
+
+ const timeSource = {
+ 'y': date.getFullYear().toString(), // 年
+ 'm': (date.getMonth() + 1).toString().padStart(2, '0'), // 月
+ 'd': date.getDate().toString().padStart(2, '0'), // 日
+ 'h': date.getHours().toString().padStart(2, '0'), // 时
+ 'M': date.getMinutes().toString().padStart(2, '0'), // 分
+ 's': date.getSeconds().toString().padStart(2, '0') // 秒
+ // 有其他格式化字符需求可以继续添加,必须转化成字符串
+ }
+
+ for (const key in timeSource) {
+ const [ret] = new RegExp(`${key}+`).exec(formatStr) || []
+ if (ret) {
+ // 年可能只需展示两位
+ const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0
+ formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex))
+ }
+ }
+
+ return formatStr
+}
+
+/**
+ * @description 时间戳转为多久之前
+ * @param {String|Number} timestamp 时间戳
+ * @param {String|Boolean} format
+ * 格式化规则如果为时间格式字符串,超出一定时间范围,返回固定的时间格式;
+ * 如果为布尔值false,无论什么时间,都返回多久以前的格式
+ * @returns {string} 转化后的内容
+ */
+export function timeFrom(timestamp = null, format = 'yyyy-mm-dd') {
+ if (timestamp == null) timestamp = Number(new Date())
+ timestamp = parseInt(timestamp)
+ // 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位)
+ if (timestamp.toString().length == 10) timestamp *= 1000
+ let timer = (new Date()).getTime() - timestamp
+ timer = parseInt(timer / 1000)
+ // 如果小于5分钟,则返回"刚刚",其他以此类推
+ let tips = ''
+ switch (true) {
+ case timer < 300:
+ tips = '刚刚'
+ break
+ case timer >= 300 && timer < 3600:
+ tips = `${parseInt(timer / 60)}分钟前`
+ break
+ case timer >= 3600 && timer < 86400:
+ tips = `${parseInt(timer / 3600)}小时前`
+ break
+ case timer >= 86400 && timer < 2592000:
+ tips = `${parseInt(timer / 86400)}天前`
+ break
+ default:
+ // 如果format为false,则无论什么时间戳,都显示xx之前
+ if (format === false) {
+ if (timer >= 2592000 && timer < 365 * 86400) {
+ tips = `${parseInt(timer / (86400 * 30))}个月前`
+ } else {
+ tips = `${parseInt(timer / (86400 * 365))}年前`
+ }
+ } else {
+ tips = timeFormat(timestamp, format)
+ }
+ }
+ return tips
+}
+
+/**
+ * @description 去除空格
+ * @param String str 需要去除空格的字符串
+ * @param String pos both(左右)|left|right|all 默认both
+ */
+export function trim(str, pos = 'both') {
+ str = String(str)
+ if (pos == 'both') {
+ return str.replace(/^\s+|\s+$/g, '')
+ }
+ if (pos == 'left') {
+ return str.replace(/^\s*/, '')
+ }
+ if (pos == 'right') {
+ return str.replace(/(\s*$)/g, '')
+ }
+ if (pos == 'all') {
+ return str.replace(/\s+/g, '')
+ }
+ return str
+}
+
+/**
+ * @description 对象转url参数
+ * @param {object} data,对象
+ * @param {Boolean} isPrefix,是否自动加上"?"
+ * @param {string} arrayFormat 规则 indices|brackets|repeat|comma
+ */
+export function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') {
+ const prefix = isPrefix ? '?' : ''
+ const _result = []
+ if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets'
+ for (const key in data) {
+ const value = data[key]
+ // 去掉为空的参数
+ if (['', undefined, null].indexOf(value) >= 0) {
+ continue
+ }
+ // 如果值为数组,另行处理
+ if (value.constructor === Array) {
+ // e.g. {ids: [1, 2, 3]}
+ switch (arrayFormat) {
+ case 'indices':
+ // 结果: ids[0]=1&ids[1]=2&ids[2]=3
+ for (let i = 0; i < value.length; i++) {
+ _result.push(`${key}[${i}]=${value[i]}`)
+ }
+ break
+ case 'brackets':
+ // 结果: ids[]=1&ids[]=2&ids[]=3
+ value.forEach((_value) => {
+ _result.push(`${key}[]=${_value}`)
+ })
+ break
+ case 'repeat':
+ // 结果: ids=1&ids=2&ids=3
+ value.forEach((_value) => {
+ _result.push(`${key}=${_value}`)
+ })
+ break
+ case 'comma':
+ // 结果: ids=1,2,3
+ let commaStr = ''
+ value.forEach((_value) => {
+ commaStr += (commaStr ? ',' : '') + _value
+ })
+ _result.push(`${key}=${commaStr}`)
+ break
+ default:
+ value.forEach((_value) => {
+ _result.push(`${key}[]=${_value}`)
+ })
+ }
+ } else {
+ _result.push(`${key}=${value}`)
+ }
+ }
+ return _result.length ? prefix + _result.join('&') : ''
+}
+
+/**
+ * 显示消息提示框
+ * @param {String} title 提示的内容,长度与 icon 取值有关。
+ * @param {Number} duration 提示的延迟时间,单位毫秒,默认:2000
+ */
+export function toast(title, duration = 2000) {
+ uni.showToast({
+ title: String(title),
+ icon: 'none',
+ duration
+ })
+}
+
+/**
+ * @description 根据主题type值,获取对应的图标
+ * @param {String} type 主题名称,primary|info|error|warning|success
+ * @param {boolean} fill 是否使用fill填充实体的图标
+ */
+export function type2icon(type = 'success', fill = false) {
+ // 如果非预置值,默认为success
+ if (['primary', 'info', 'error', 'warning', 'success'].indexOf(type) == -1) type = 'success'
+ let iconName = ''
+ // 目前(2019-12-12),info和primary使用同一个图标
+ switch (type) {
+ case 'primary':
+ iconName = 'info-circle'
+ break
+ case 'info':
+ iconName = 'info-circle'
+ break
+ case 'error':
+ iconName = 'close-circle'
+ break
+ case 'warning':
+ iconName = 'error-circle'
+ break
+ case 'success':
+ iconName = 'checkmark-circle'
+ break
+ default:
+ iconName = 'checkmark-circle'
+ }
+ // 是否是实体类型,加上-fill,在icon组件库中,实体的类名是后面加-fill的
+ if (fill) iconName += '-fill'
+ return iconName
+}
+
+/**
+ * @description 数字格式化
+ * @param {number|string} number 要格式化的数字
+ * @param {number} decimals 保留几位小数
+ * @param {string} decimalPoint 小数点符号
+ * @param {string} thousandsSeparator 千分位符号
+ * @returns {string} 格式化后的数字
+ */
+export function priceFormat(number, decimals = 0, decimalPoint = '.', thousandsSeparator = ',') {
+ number = (`${number}`).replace(/[^0-9+-Ee.]/g, '')
+ const n = !isFinite(+number) ? 0 : +number
+ const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals)
+ const sep = (typeof thousandsSeparator === 'undefined') ? ',' : thousandsSeparator
+ const dec = (typeof decimalPoint === 'undefined') ? '.' : decimalPoint
+ let s = ''
+
+ s = (prec ? round(n, prec) + '' : `${Math.round(n)}`).split('.')
+ const re = /(-?\d+)(\d{3})/
+ while (re.test(s[0])) {
+ s[0] = s[0].replace(re, `$1${sep}$2`)
+ }
+
+ if ((s[1] || '').length < prec) {
+ s[1] = s[1] || ''
+ s[1] += new Array(prec - s[1].length + 1).join('0')
+ }
+ return s.join(dec)
+}
+
+/**
+ * @description 获取duration值
+ * 如果带有ms或者s直接返回,如果大于一定值,认为是ms单位,小于一定值,认为是s单位
+ * 比如以30位阈值,那么300大于30,可以理解为用户想要的是300ms,而不是想花300s去执行一个动画
+ * @param {String|number} value 比如: "1s"|"100ms"|1|100
+ * @param {boolean} unit 提示: 如果是false 默认返回number
+ * @return {string|number}
+ */
+export function getDuration(value, unit = true) {
+ const valueNum = parseInt(value)
+ if (unit) {
+ if (/s$/.test(value)) return value
+ return value > 30 ? `${value}ms` : `${value}s`
+ }
+ if (/ms$/.test(value)) return valueNum
+ if (/s$/.test(value)) return valueNum > 30 ? valueNum : valueNum * 1000
+ return valueNum
+}
+
+/**
+ * @description 日期的月或日补零操作
+ * @param {String} value 需要补零的值
+ */
+export function padZero(value) {
+ return `00${value}`.slice(-2)
+}
+
+/**
+ * @description 在u-form的子组件内容发生变化,或者失去焦点时,尝试通知u-form执行校验方法
+ * @param {*} instance
+ * @param {*} event
+ */
+export function formValidate(instance, event) {
+ const formItem = $parent.call(instance, 'up-form-item')
+ const form = $parent.call(instance, 'up-form')
+ // 如果发生变化的input或者textarea等,其父组件中有u-form-item或者u-form等,就执行form的validate方法
+ // 同时将form-item的pros传递给form,让其进行精确对象验证
+ if (formItem && form) {
+ form.validateField(formItem.prop, () => {}, event)
+ }
+}
+
+/**
+ * @description 获取某个对象下的属性,用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式
+ * @param {object} obj 对象
+ * @param {string} key 需要获取的属性字段
+ * @returns {*}
+ */
+export function getProperty(obj, key) {
+ if (typeof obj !== 'object' || null == obj) {
+ return ''
+ }
+ if (typeof key !== 'string' || key === '') {
+ return ''
+ }
+ if (key.indexOf('.') !== -1) {
+ const keys = key.split('.')
+ let firstObj = obj[keys[0]] || {}
+
+ for (let i = 1; i < keys.length; i++) {
+ if (firstObj) {
+ firstObj = firstObj[keys[i]]
+ }
+ }
+ return firstObj
+ }
+ return obj[key]
+}
+
+/**
+ * @description 设置对象的属性值,如果'a.b.c'的形式进行设置
+ * @param {object} obj 对象
+ * @param {string} key 需要设置的属性
+ * @param {string} value 设置的值
+ */
+export function setProperty(obj, key, value) {
+ if (typeof obj !== 'object' || null == obj) {
+ return
+ }
+ // 递归赋值
+ const inFn = function(_obj, keys, v) {
+ // 最后一个属性key
+ if (keys.length === 1) {
+ _obj[keys[0]] = v
+ return
+ }
+ // 0~length-1个key
+ while (keys.length > 1) {
+ const k = keys[0]
+ if (!_obj[k] || (typeof _obj[k] !== 'object')) {
+ _obj[k] = {}
+ }
+ const key = keys.shift()
+ // 自调用判断是否存在属性,不存在则自动创建对象
+ inFn(_obj[k], keys, v)
+ }
+ }
+
+ if (typeof key !== 'string' || key === '') {
+
+ } else if (key.indexOf('.') !== -1) { // 支持多层级赋值操作
+ const keys = key.split('.')
+ inFn(obj, keys, value)
+ } else {
+ obj[key] = value
+ }
+}
+
+/**
+ * @description 获取当前页面路径
+ */
+export function page() {
+ const pages = getCurrentPages()
+ // 某些特殊情况下(比如页面进行redirectTo时的一些时机),pages可能为空数组
+ return `/${pages[pages.length - 1].route || ''}`
+}
+
+/**
+ * @description 获取当前路由栈实例数组
+ */
+export function pages() {
+ const pages = getCurrentPages()
+ return pages
+}
+
+export function getValueByPath(obj, path) {
+ // 将路径字符串按 '.' 分割成数组
+ const pathArr = path.split('.');
+ // 使用 reduce 方法从 obj 开始,逐级访问嵌套属性
+ return pathArr.reduce((acc, curr) => {
+ // 如果当前累加器(acc)是对象且包含当前键(curr),则返回该键对应的值
+ // 否则返回 undefined(表示路径不存在)
+ return acc && acc[curr] !== undefined ? acc[curr] : undefined;
+ }, obj);
+}
+
+/**
+ * 生成同色系浅色背景色
+ * @param {string} textColor - 支持 #RGB、#RRGGBB、rgb()、rgba() 格式
+ * @param {number} [lightness=85] - 目标亮度百分比(默认85%)
+ * @returns {string} 十六进制颜色值
+ */
+export function genLightColor(textColor, lightness = 95) {
+ // 手动解析颜色值(避免使用document)
+ const rgb = parseColorWithoutDOM(textColor);
+
+ // RGB转HSL色域
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
+
+ // 生成浅色背景
+ const bgHsl = {
+ h: hsl.h,
+ s: hsl.s,
+ l: Math.min(lightness, 95)
+ };
+
+ return hslToHex(bgHsl.h, bgHsl.s, bgHsl.l);
+ }
+
+ /* 手动解析颜色字符串(兼容uni-app环境) */
+ function parseColorWithoutDOM(colorStr) {
+ // 统一转小写处理
+ const str = colorStr.toLowerCase().trim();
+
+ // 处理十六进制格式
+ if (str.startsWith('#')) {
+ const hex = str.replace('#', '');
+ const fullHex = hex.length === 3 ?
+ hex.split('').map(c => c + c).join('') : hex;
+
+ return {
+ r: parseInt(fullHex.substring(0,2), 16),
+ g: parseInt(fullHex.substring(2,4), 16),
+ b: parseInt(fullHex.substring(4,6), 16)
+ };
+ }
+
+ // 处理rgb/rgba格式
+ const rgbMatch = str.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
+ if (rgbMatch) {
+ return {
+ r: +rgbMatch[1],
+ g: +rgbMatch[2],
+ b: +rgbMatch[3]
+ };
+ }
+
+ throw new Error('Invalid color format');
+ }
+
+// 辅助函数:RGB 转 HSL(色相、饱和度、亮度)
+function rgbToHsl(r, g, b) {
+ r /= 255, g /= 255, b /= 255;
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
+ let h, s, l = (max + min) / 2;
+
+ if (max === min) {
+ h = s = 0; // achromatic
+ } else {
+ const d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ switch (max) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h = (h * 60).toFixed(1);
+ }
+ return { h: +h, s: +(s * 100).toFixed(1), l: +(l * 100).toFixed(1) };
+}
+
+// 辅助函数:HSL 转十六进制
+function hslToHex(h, s, l) {
+ l /= 100;
+ const a = s * Math.min(l, 1 - l) / 100;
+ const f = n => {
+ const k = (n + h / 30) % 12;
+ const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
+ return Math.round(255 * color).toString(16).padStart(2, '0');
+ };
+ return `#${f(0)}${f(8)}${f(4)}`;
+}
+
+export default {
+ range,
+ getPx,
+ sleep,
+ os,
+ sys,
+ getWindowInfo,
+ random,
+ guid,
+ $parent,
+ addStyle,
+ addUnit,
+ deepClone,
+ deepMerge,
+ shallowMerge,
+ error,
+ randomArray,
+ timeFormat,
+ timeFrom,
+ trim,
+ queryParams,
+ toast,
+ type2icon,
+ priceFormat,
+ getDuration,
+ padZero,
+ formValidate,
+ getProperty,
+ setProperty,
+ page,
+ pages,
+ getValueByPath,
+ genLightColor,
+ rpx2px
+}
+
diff --git a/uni_modules/uview-plus/libs/function/platform.js b/uni_modules/uview-plus/libs/function/platform.js
new file mode 100644
index 0000000..904fccc
--- /dev/null
+++ b/uni_modules/uview-plus/libs/function/platform.js
@@ -0,0 +1,75 @@
+/**
+ * 注意:
+ * 此部分内容,在vue-cli模式下,需要在vue.config.js加入如下内容才有效:
+ * module.exports = {
+ * transpileDependencies: ['uview-v2']
+ * }
+ */
+
+let platform = 'none'
+
+// #ifdef VUE3
+platform = 'vue3'
+// #endif
+
+// #ifdef VUE2
+platform = 'vue2'
+// #endif
+
+// #ifdef APP-PLUS
+platform = 'plus'
+// #endif
+
+// #ifdef APP-NVUE
+platform = 'nvue'
+// #endif
+
+// #ifdef H5
+platform = 'h5'
+// #endif
+
+// #ifdef MP
+platform = 'mp'
+// #endif
+
+// #ifdef MP-WEIXIN
+platform = 'weixin'
+// #endif
+
+// #ifdef MP-ALIPAY
+platform = 'alipay'
+// #endif
+
+// #ifdef MP-BAIDU
+platform = 'baidu'
+// #endif
+
+// #ifdef MP-TOUTIAO
+platform = 'toutiao'
+// #endif
+
+// #ifdef MP-QQ
+platform = 'qq'
+// #endif
+
+// #ifdef MP-KUAISHOU
+platform = 'kuaishou'
+// #endif
+
+// #ifdef MP-360
+platform = '360'
+// #endif
+
+// #ifdef QUICKAPP-WEBVIEW
+platform = 'quickapp-webview'
+// #endif
+
+// #ifdef QUICKAPP-WEBVIEW-HUAWEI
+platform = 'quickapp-webview-huawei'
+// #endif
+
+// #ifdef QUICKAPP-WEBVIEW-UNION
+platform = 'quckapp-webview-union'
+// #endif
+
+export default platform
diff --git a/uni_modules/uview-plus/libs/function/test.js b/uni_modules/uview-plus/libs/function/test.js
new file mode 100644
index 0000000..579b976
--- /dev/null
+++ b/uni_modules/uview-plus/libs/function/test.js
@@ -0,0 +1,327 @@
+/**
+ * 验证电子邮箱格式
+ */
+export function email(value) {
+ return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value)
+}
+
+/**
+ * 验证手机格式
+ */
+export function mobile(value) {
+ return /^1[23456789]\d{9}$/.test(value)
+}
+
+/**
+ * 验证URL格式
+ */
+export function url(value) {
+ return /^((https|http|ftp|rtsp|mms):\/\/)(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z].[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/
+ .test(value)
+}
+
+/**
+ * 验证日期格式
+ * @param {number | string} value yyyy-mm-dd hh:mm:ss 或 时间戳
+ */
+export function date(value) {
+ if (!value) return false;
+ // number类型,判断是否是时间戳
+ if (typeof value === "number") {
+ // len === 10 秒级时间戳 len === 13 毫秒级时间戳
+ if (value.toString().length !== 10 && value.toString().length !== 13) {
+ return false;
+ }
+ return !isNaN(new Date(value).getTime());
+ }
+ if (typeof value === "string") {
+ // 是否为string类型时间戳
+ const numV = Number(value);
+ if (!isNaN(numV)) {
+ if (
+ numV.toString().length === 10 ||
+ numV.toString().length === 13
+ ) {
+ return !isNaN(new Date(numV).getTime());
+ }
+ }
+ // 非时间戳,且长度在yyyy-mm-dd 至 yyyy-mm-dd hh:mm:ss 之间
+ if (value.length < 10 || value.length > 19) {
+ return false;
+ }
+ const dateRegex =
+ /^\d{4}[-\/]\d{2}[-\/]\d{2}( \d{1,2}:\d{2}(:\d{2})?)?$/;
+ if (!dateRegex.test(value)) {
+ return false;
+ }
+ // 检查是否为有效日期
+ const dateValue = new Date(value);
+ return !isNaN(dateValue.getTime());
+ }
+ // 非number和string类型,不做校验
+ return false;
+}
+
+/**
+ * 验证ISO类型的日期格式
+ */
+export function dateISO(value) {
+ return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value)
+}
+
+/**
+ * 验证十进制数字
+ */
+export function number(value) {
+ return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value)
+}
+
+/**
+ * 验证字符串
+ */
+export function string(value) {
+ return typeof value === 'string'
+}
+
+/**
+ * 验证整数
+ */
+export function digits(value) {
+ return /^\d+$/.test(value)
+}
+
+/**
+ * 验证身份证号码
+ */
+export function idCard(value) {
+ return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
+ value
+ )
+}
+
+/**
+ * 是否车牌号
+ */
+export function carNo(value) {
+ // 新能源车牌
+ const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/
+ // 旧车牌
+ const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/
+ if (value.length === 7) {
+ return creg.test(value)
+ } if (value.length === 8) {
+ return xreg.test(value)
+ }
+ return false
+}
+
+/**
+ * 金额,只允许2位小数
+ */
+export function amount(value) {
+ // 金额,只允许保留两位小数
+ return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value)
+}
+
+/**
+ * 中文
+ */
+export function chinese(value) {
+ const reg = /^[\u4e00-\u9fa5]+$/gi
+ return reg.test(value)
+}
+
+/**
+ * 只能输入字母
+ */
+export function letter(value) {
+ return /^[a-zA-Z]*$/.test(value)
+}
+
+/**
+ * 只能是字母或者数字
+ */
+export function enOrNum(value) {
+ // 英文或者数字
+ const reg = /^[0-9a-zA-Z]*$/g
+ return reg.test(value)
+}
+
+/**
+ * 验证是否包含某个值
+ */
+export function contains(value, param) {
+ return value.indexOf(param) >= 0
+}
+
+/**
+ * 验证一个值范围[min, max]
+ */
+export function range(value, param) {
+ return value >= param[0] && value <= param[1]
+}
+
+/**
+ * 验证一个长度范围[min, max]
+ */
+export function rangeLength(value, param) {
+ return value.length >= param[0] && value.length <= param[1]
+}
+
+/**
+ * 是否固定电话
+ */
+export function landline(value) {
+ const reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/
+ return reg.test(value)
+}
+
+/**
+ * 判断是否为空
+ */
+export function empty(value) {
+ switch (typeof value) {
+ case 'undefined':
+ return true
+ case 'string':
+ if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true
+ break
+ case 'boolean':
+ if (!value) return true
+ break
+ case 'number':
+ if (value === 0 || isNaN(value)) return true
+ break
+ case 'object':
+ if (value === null || value.length === 0) return true
+ for (const i in value) {
+ return false
+ }
+ return true
+ }
+ return false
+}
+
+/**
+ * 是否json字符串
+ */
+export function jsonString(value) {
+ if (typeof value === 'string') {
+ try {
+ const obj = JSON.parse(value)
+ if (typeof obj === 'object' && obj) {
+ return true
+ }
+ return false
+ } catch (e) {
+ return false
+ }
+ }
+ return false
+}
+
+/**
+ * 是否数组
+ */
+export function array(value) {
+ if (typeof Array.isArray === 'function') {
+ return Array.isArray(value)
+ }
+ return Object.prototype.toString.call(value) === '[object Array]'
+}
+
+/**
+ * 是否对象
+ */
+export function object(value) {
+ return Object.prototype.toString.call(value) === '[object Object]'
+}
+
+/**
+ * 是否是Promise对象
+ */
+export function objectPromise(value) {
+ return Object.prototype.toString.call(value) === '[object Promise]';
+}
+
+/**
+ * 是否短信验证码
+ */
+export function code(value, len = 6) {
+ return new RegExp(`^\\d{${len}}$`).test(value)
+}
+
+/**
+ * 是否函数方法
+ * @param {Object} value
+ */
+export function func(value) {
+ return typeof value === 'function'
+}
+
+/**
+ * 是否promise对象
+ * @param {Object} value
+ */
+export function promise(value) {
+ return objectPromise(value) && func(value.then) && func(value.catch)
+}
+
+/** 是否图片格式
+ * @param {Object} value
+ */
+export function image(value) {
+ const newValue = value.split('?')[0]
+ const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i
+ return IMAGE_REGEXP.test(newValue)
+}
+
+/**
+ * 是否视频格式
+ * @param {Object} value
+ */
+export function video(value) {
+ const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8)/i
+ return VIDEO_REGEXP.test(value)
+}
+
+/**
+ * 是否为正则对象
+ * @param {Object}
+ * @return {Boolean}
+ */
+export function regExp(o) {
+ return o && Object.prototype.toString.call(o) === '[object RegExp]'
+}
+
+export default {
+ email,
+ mobile,
+ url,
+ date,
+ dateISO,
+ number,
+ digits,
+ idCard,
+ carNo,
+ amount,
+ chinese,
+ letter,
+ enOrNum,
+ contains,
+ range,
+ rangeLength,
+ empty,
+ isEmpty: empty,
+ jsonString,
+ landline,
+ object,
+ array,
+ code,
+ func,
+ promise,
+ video,
+ image,
+ regExp,
+ string
+}
diff --git a/uni_modules/uview-plus/libs/function/throttle.js b/uni_modules/uview-plus/libs/function/throttle.js
new file mode 100644
index 0000000..55cb1c7
--- /dev/null
+++ b/uni_modules/uview-plus/libs/function/throttle.js
@@ -0,0 +1,30 @@
+let timer;
+let flag;
+/**
+ * 节流原理:在一定时间内,只能触发一次
+ *
+ * @param {Function} func 要执行的回调函数
+ * @param {Number} wait 延时的时间
+ * @param {Boolean} immediate 是否立即执行
+ * @return null
+ */
+export function throttle(func, wait = 500, immediate = true) {
+ if (immediate) {
+ if (!flag) {
+ flag = true
+ // 如果是立即执行,则在wait毫秒内开始时执行
+ typeof func === 'function' && func()
+ timer = setTimeout(() => {
+ flag = false
+ }, wait)
+ }
+ } else if (!flag) {
+ flag = true
+ // 如果是非立即执行,则在wait毫秒内的结束处执行
+ timer = setTimeout(() => {
+ flag = false
+ typeof func === 'function' && func()
+ }, wait)
+ }
+}
+export default throttle
diff --git a/uni_modules/uview-plus/libs/i18n/index.js b/uni_modules/uview-plus/libs/i18n/index.js
new file mode 100644
index 0000000..09234cf
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/index.js
@@ -0,0 +1,54 @@
+import zhHans from './locales/zh-Hans.json'
+import zhHant from './locales/zh-Hant.json'
+import en from './locales/en.json'
+import es from './locales/es.json'
+import fr from './locales/fr.json'
+import de from './locales/de.json'
+import ko from './locales/ko.json'
+import ja from './locales/ja.json'
+import ru from './locales/ru.json'
+
+let settings = {
+ lang: uni.getLocale(),
+ locales: {
+ en,
+ es,
+ fr,
+ de,
+ ko,
+ ja,
+ ru,
+ 'zh-Hant': zhHant,
+ 'zh-Hans': zhHans
+ }
+};
+
+uni.onLocaleChange((locale) => {
+ settings.lang = locale;
+})
+
+/**
+ * 多语言方法
+ */
+export function t(value, params = {}) {
+ // console.log(settings.locales[settings.lang])
+ if (value) {
+ let lang = settings.lang
+ if (!settings.locales[settings.lang]) {
+ lang = 'zh-Hans'
+ }
+ let result = settings.locales[lang][value] || value;
+ // 替换{xxx}格式的变量
+ Object.keys(params).forEach(key => {
+ const reg = new RegExp(`{${key}}`, 'g');
+ result = result.replace(reg, params[key]);
+ });
+ return result;
+ } else {
+ return value;
+ }
+}
+
+export default {
+ settings: settings
+}
diff --git a/uni_modules/uview-plus/libs/i18n/locales/de.json b/uni_modules/uview-plus/libs/i18n/locales/de.json
new file mode 100644
index 0000000..0ffc53c
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/locales/de.json
@@ -0,0 +1,85 @@
+{
+ "up.common.cancel": "Abbrechen",
+ "up.common.confirm": "Bestätigen",
+ "up.common.start": "Start",
+ "up.common.end": "Ende",
+ "up.common.stop": "Stopp",
+ "up.common.copy": "Kopieren",
+ "up.common.none": "Keine",
+ "up.common.tip": "Hinweis",
+ "up.common.success": "Erfolg",
+ "up.common.fail": "Fehlgeschlagen",
+ "up.common.close": "Schließen",
+ "up.common.preview": "Vorschau",
+ "up.common.re-select": "Erneut auswählen",
+ "up.common.rotate": "Drehen",
+ "up.common.pleaseChoose": "Bitte wählen",
+ "up.common.loading": "Laden",
+ "up.common.loading2": "Wird geladen",
+ "up.common.inOperation": "In Bearbeitung",
+ "up.common.settings": "Einstellungen",
+ "up.common.retry": "Wiederholen",
+ "up.common.search": "Suchen",
+ "up.common.more": "Mehr",
+ "up.common.video": "Video",
+ "up.common.file": "Datei",
+ "up.week.one": "Mo",
+ "up.week.two": "Di",
+ "up.week.three": "Mi",
+ "up.week.four": "Do",
+ "up.week.five": "Fr",
+ "up.week.six": "Sa",
+ "up.week.seven": "So",
+ "up.barcode.error": "Barcode-Generierung fehlgeschlagen",
+ "up.calendar.chooseDates": "Datumsauswahl",
+ "up.calendar.disabled": "Dieses Datum ist deaktiviert",
+ "up.calendar.daysExceed": "Die Anzahl der ausgewählten Tage darf {days} Tage nicht überschreiten",
+ "up.cityLocate.locateCity": "Stadt lokalisieren",
+ "up.cityLocate.fail": "Lokalisierung fehlgeschlagen, bitte klicken Sie zum Wiederholen.",
+ "up.cityLocate.locating": "Lokalisierung läuft",
+ "up.code.send": "Bestätigungscode erhalten",
+ "up.code.resendAfter": "Erneut senden in X Sekunden",
+ "up.code.resend": "Erneut senden",
+ "up.cropper.emptyWidhtOrHeight": "Breite oder Höhe des Zuschneidebereichs nicht festgelegt",
+ "up.empty.car": "Warenkorb ist leer",
+ "up.empty.page": "Seite existiert nicht",
+ "up.empty.search": "Keine Suchergebnisse",
+ "up.empty.address": "Keine Lieferadresse",
+ "up.empty.wifi": "Kein WLAN",
+ "up.empty.order": "Bestellungen sind leer",
+ "up.empty.coupon": "Keine Gutscheine",
+ "up.empty.favor": "Keine Favoriten",
+ "up.empty.permission": "Keine Berechtigung",
+ "up.empty.history": "Kein Verlauf",
+ "up.empty.news": "Keine Nachrichtenliste",
+ "up.empty.message": "Nachrichtenliste ist leer",
+ "up.empty.list": "Liste ist leer",
+ "up.empty.data": "Daten sind leer",
+ "up.empty.comment": "Keine Kommentare",
+ "up.link.copyed": "Link kopiert, bitte im Browser öffnen",
+ "up.loadmoe.loadmore": "Mehr laden",
+ "up.loadmoe.nomore": "Keine weiteren Daten",
+ "up.noNetwork.text": "Ups, Netzwerksignal verloren",
+ "up.noNetwork.pleaseCheck": "Bitte überprüfen Sie das Netzwerk oder gehen Sie zu",
+ "up.noNetwork.connect": "Netzwerk verbunden",
+ "up.noNetwork.disconnect": "Keine Netzwerkverbindung",
+ "up.pagination.previous": "Vorherige Seite",
+ "up.pagination.next": "Nächste Seite",
+ "up.pullRefresh.pull": "Zum Aktualisieren nach unten ziehen",
+ "up.pullRefresh.release": "Loslassen zum Aktualisieren",
+ "up.pullRefresh.refreshing": "Aktualisierung läuft",
+ "up.readMore.expand": "Erweitern zum vollständigen Lesen",
+ "up.readMore.fold": "Einklappen",
+ "up.search.placeholder": "Bitte Schlüsselwort eingeben",
+ "up.signature.penSize": "Strichstärke",
+ "up.signature.penColor": "Strichfarbe",
+ "up.upload.sizeExceed": "Größenbegrenzung überschritten",
+ "up.upload.uploading": "Upload läuft",
+ "up.upload.previewImageFail": "Bildvorschau fehlgeschlagen",
+ "up.upload.previewVideoFail": "Videovorschau fehlgeschlagen",
+ "up.goodsSku.stock": "Lagerbestand",
+ "up.goodsSku.price": "Preis",
+ "up.goodsSku.amount": "Stück",
+ "up.goodsSku.choosed": "Ausgewählt",
+ "up.goodsSku.buyAmount": "Anzahl"
+}
diff --git a/uni_modules/uview-plus/libs/i18n/locales/en.json b/uni_modules/uview-plus/libs/i18n/locales/en.json
new file mode 100644
index 0000000..1a7cc93
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/locales/en.json
@@ -0,0 +1,85 @@
+{
+ "up.common.cancel": "Cancel",
+ "up.common.confirm": "Confirm",
+ "up.common.start": "Start",
+ "up.common.end": "End",
+ "up.common.stop": "Stop",
+ "up.common.copy": "Copy",
+ "up.common.none": "None",
+ "up.common.tip": "Tip",
+ "up.common.success": "Success",
+ "up.common.fail": "Fail",
+ "up.common.close": "Close",
+ "up.common.preview": "Preview",
+ "up.common.re-select": "Re-select",
+ "up.common.rotate": "Rotate",
+ "up.common.pleaseChoose": "Please choose",
+ "up.common.loading": "Loading",
+ "up.common.loading2": "Loading",
+ "up.common.inOperation": "In operation",
+ "up.common.settings": "Settings",
+ "up.common.retry": "Retry",
+ "up.common.search": "Search",
+ "up.common.more": "More",
+ "up.common.video": "Video",
+ "up.common.file": "File",
+ "up.week.one": "Mon",
+ "up.week.two": "Tue",
+ "up.week.three": "Wed",
+ "up.week.four": "Thu",
+ "up.week.five": "Fri",
+ "up.week.six": "Sat",
+ "up.week.seven": "Sun",
+ "up.barcode.error": "Failed to generate barcode",
+ "up.calendar.chooseDates": "Date selection",
+ "up.calendar.disabled": "This date is disabled",
+ "up.calendar.daysExceed": "The number of selected days cannot exceed {days} days",
+ "up.cityLocate.locateCity": "Locate city",
+ "up.cityLocate.fail": "Location failed, please click to retry.",
+ "up.cityLocate.locating": "Locating",
+ "up.code.send": "Get verification code",
+ "up.code.resendAfter": "Resend after X seconds",
+ "up.code.resend": "Resend",
+ "up.cropper.emptyWidhtOrHeight": "The width or height of the cropping box is not set",
+ "up.empty.car": "Shopping cart is empty",
+ "up.empty.page": "Page not found",
+ "up.empty.search": "No search results",
+ "up.empty.address": "No shipping address",
+ "up.empty.wifi": "No WiFi",
+ "up.empty.order": "Order is empty",
+ "up.empty.coupon": "No coupons",
+ "up.empty.favor": "No favorites",
+ "up.empty.permission": "No permission",
+ "up.empty.history": "No history",
+ "up.empty.news": "No news list",
+ "up.empty.message": "Message list is empty",
+ "up.empty.list": "List is empty",
+ "up.empty.data": "Data is empty",
+ "up.empty.comment": "No comments",
+ "up.link.copyed": "Link copied, please open in browser",
+ "up.loadmoe.loadmore": "Load more",
+ "up.loadmoe.nomore": "No more",
+ "up.noNetwork.text": "Oops, network signal lost",
+ "up.noNetwork.pleaseCheck": "Please check the network, or go to",
+ "up.noNetwork.connect": "Network connected",
+ "up.noNetwork.disconnect": "No network connection",
+ "up.pagination.previous": "Previous",
+ "up.pagination.next": "Next",
+ "up.pullRefresh.pull": "Pull to refresh",
+ "up.pullRefresh.release": "Release to refresh",
+ "up.pullRefresh.refreshing": "Refreshing",
+ "up.readMore.expand": "Expand to read more",
+ "up.readMore.fold": "Collapse",
+ "up.search.placeholder": "Please enter keywords",
+ "up.signature.penSize": "Stroke size",
+ "up.signature.penColor": "Stroke color",
+ "up.upload.sizeExceed": "Size limit exceeded",
+ "up.upload.uploading": "Uploading",
+ "up.upload.previewImageFail": "Failed to preview image",
+ "up.upload.previewVideoFail": "Failed to preview video",
+ "up.goodsSku.stock": "Stock",
+ "up.goodsSku.price": "Price",
+ "up.goodsSku.amount": "Items",
+ "up.goodsSku.choosed": "Selected",
+ "up.goodsSku.buyAmount": "Quantity"
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/i18n/locales/es.json b/uni_modules/uview-plus/libs/i18n/locales/es.json
new file mode 100644
index 0000000..de08574
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/locales/es.json
@@ -0,0 +1,85 @@
+{
+ "up.common.cancel": "Cancelar",
+ "up.common.confirm": "Confirmar",
+ "up.common.start": "Iniciar",
+ "up.common.end": "Finalizar",
+ "up.common.stop": "Detener",
+ "up.common.copy": "Copiar",
+ "up.common.none": "Ninguno",
+ "up.common.tip": "Consejo",
+ "up.common.success": "Éxito",
+ "up.common.fail": "Fallido",
+ "up.common.close": "Cerrar",
+ "up.common.preview": "Vista previa",
+ "up.common.re-select": "Re seleccionar",
+ "up.common.rotate": "Rotar",
+ "up.common.pleaseChoose": "Por favor seleccione",
+ "up.common.loading": "Cargando",
+ "up.common.loading2": "Cargando",
+ "up.common.inOperation": "En operación",
+ "up.common.settings": "Configuración",
+ "up.common.retry": "Reintentar",
+ "up.common.search": "Buscar",
+ "up.common.more": "Más",
+ "up.common.video": "Vídeo",
+ "up.common.file": "Archivo",
+ "up.week.one": "Lun",
+ "up.week.two": "Mar",
+ "up.week.three": "Mié",
+ "up.week.four": "Jue",
+ "up.week.five": "Vie",
+ "up.week.six": "Sáb",
+ "up.week.seven": "Dom",
+ "up.barcode.error": "Error al generar código de barras",
+ "up.calendar.chooseDates": "Selección de fecha",
+ "up.calendar.disabled": "Esta fecha está deshabilitada",
+ "up.calendar.daysExceed": "Los días seleccionados no pueden exceder {days} días",
+ "up.cityLocate.locateCity": "Localizar ciudad",
+ "up.cityLocate.fail": "Error de localización, haga clic para reintentar.",
+ "up.cityLocate.locating": "Localizando",
+ "up.code.send": "Obtener código de verificación",
+ "up.code.resendAfter": "Reenviar en X segundos",
+ "up.code.resend": "Reenviar",
+ "up.cropper.emptyWidhtOrHeight": "El ancho o alto del recorte no está configurado",
+ "up.empty.car": "Carrito de compras vacío",
+ "up.empty.page": "Página no encontrada",
+ "up.empty.search": "Sin resultados de búsqueda",
+ "up.empty.address": "Sin dirección de envío",
+ "up.empty.wifi": "Sin WiFi",
+ "up.empty.order": "Pedido vacío",
+ "up.empty.coupon": "Sin cupones",
+ "up.empty.favor": "Sin favoritos",
+ "up.empty.permission": "Sin permisos",
+ "up.empty.history": "Sin historial",
+ "up.empty.news": "Sin noticias",
+ "up.empty.message": "Lista de mensajes vacía",
+ "up.empty.list": "Lista vacía",
+ "up.empty.data": "Datos vacíos",
+ "up.empty.comment": "Sin comentarios",
+ "up.link.copyed": "Enlace copiado, por favor abra en el navegador",
+ "up.loadmoe.loadmore": "Cargar más",
+ "up.loadmoe.nomore": "No hay más",
+ "up.noNetwork.text": "¡Ups! Se perdió la señal de red",
+ "up.noNetwork.pleaseCheck": "Por favor verifique la red, o vaya a",
+ "up.noNetwork.connect": "Red conectada",
+ "up.noNetwork.disconnect": "Sin conexión a internet",
+ "up.pagination.previous": "Página anterior",
+ "up.pagination.next": "Página siguiente",
+ "up.pullRefresh.pull": "Deslizar hacia abajo para actualizar",
+ "up.pullRefresh.release": "Soltar para actualizar",
+ "up.pullRefresh.refreshing": "Actualizando",
+ "up.readMore.expand": "Expandir para leer más",
+ "up.readMore.fold": "Contraer",
+ "up.search.placeholder": "Ingrese palabra clave",
+ "up.signature.penSize": "Tamaño del trazo",
+ "up.signature.penColor": "Color del trazo",
+ "up.upload.sizeExceed": "Excede el límite de tamaño",
+ "up.upload.uploading": "Subiendo",
+ "up.upload.previewImageFail": "Error al previsualizar imagen",
+ "up.upload.previewVideoFail": "Error al previsualizar vídeo",
+ "up.goodsSku.stock": "Inventario",
+ "up.goodsSku.price": "Precio",
+ "up.goodsSku.amount": "Piezas",
+ "up.goodsSku.choosed": "Seleccionado",
+ "up.goodsSku.buyAmount": "Cantidad"
+}
diff --git a/uni_modules/uview-plus/libs/i18n/locales/fr.json b/uni_modules/uview-plus/libs/i18n/locales/fr.json
new file mode 100644
index 0000000..65e5ec6
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/locales/fr.json
@@ -0,0 +1,85 @@
+{
+ "up.common.cancel": "Annuler",
+ "up.common.confirm": "Confirmer",
+ "up.common.start": "Démarrer",
+ "up.common.end": "Terminer",
+ "up.common.stop": "Arrêter",
+ "up.common.copy": "Copier",
+ "up.common.none": "Aucun",
+ "up.common.tip": "Conseil",
+ "up.common.success": "Succès",
+ "up.common.fail": "Échec",
+ "up.common.close": "Fermer",
+ "up.common.preview": "Aperçu",
+ "up.common.re-select": "Resélectionner",
+ "up.common.rotate": "Rotation",
+ "up.common.pleaseChoose": "Veuillez choisir",
+ "up.common.loading": "Chargement",
+ "up.common.loading2": "Chargement en cours",
+ "up.common.inOperation": "En cours d'opération",
+ "up.common.settings": "Paramètres",
+ "up.common.retry": "Réessayer",
+ "up.common.search": "Rechercher",
+ "up.common.more": "Plus",
+ "up.common.video": "Vidéo",
+ "up.common.file": "Fichier",
+ "up.week.one": "Lun",
+ "up.week.two": "Mar",
+ "up.week.three": "Mer",
+ "up.week.four": "Jeu",
+ "up.week.five": "Ven",
+ "up.week.six": "Sam",
+ "up.week.seven": "Dim",
+ "up.barcode.error": "Échec de génération du code-barres",
+ "up.calendar.chooseDates": "Sélection de dates",
+ "up.calendar.disabled": "Cette date est désactivée",
+ "up.calendar.daysExceed": "Le nombre de jours sélectionnés ne peut pas dépasser {days} jours",
+ "up.cityLocate.locateCity": "Localiser la ville",
+ "up.cityLocate.fail": "Échec de localisation, veuillez cliquer pour réessayer.",
+ "up.cityLocate.locating": "Localisation en cours",
+ "up.code.send": "Obtenir le code de vérification",
+ "up.code.resendAfter": "Renvoyer dans X secondes",
+ "up.code.resend": "Renvoyer",
+ "up.cropper.emptyWidhtOrHeight": "La largeur ou la hauteur de recadrage n'est pas définie",
+ "up.empty.car": "Panier vide",
+ "up.empty.page": "Page introuvable",
+ "up.empty.search": "Aucun résultat de recherche",
+ "up.empty.address": "Aucune adresse de livraison",
+ "up.empty.wifi": "Aucun Wi-Fi",
+ "up.empty.order": "Commande vide",
+ "up.empty.coupon": "Aucun coupon",
+ "up.empty.favor": "Aucun favori",
+ "up.empty.permission": "Aucune autorisation",
+ "up.empty.history": "Aucun historique",
+ "up.empty.news": "Aucune actualité",
+ "up.empty.message": "Liste de messages vide",
+ "up.empty.list": "Liste vide",
+ "up.empty.data": "Données vides",
+ "up.empty.comment": "Aucun commentaire",
+ "up.link.copyed": "Lien copié, veuillez ouvrir dans le navigateur",
+ "up.loadmoe.loadmore": "Charger plus",
+ "up.loadmoe.nomore": "Plus de contenu",
+ "up.noNetwork.text": "Oups, le signal réseau est perdu",
+ "up.noNetwork.pleaseCheck": "Veuillez vérifier le réseau, ou aller à",
+ "up.noNetwork.connect": "Réseau connecté",
+ "up.noNetwork.disconnect": "Aucune connexion réseau",
+ "up.pagination.previous": "Page précédente",
+ "up.pagination.next": "Page suivante",
+ "up.pullRefresh.pull": "Tirer pour actualiser",
+ "up.pullRefresh.release": "Relâcher pour actualiser",
+ "up.pullRefresh.refreshing": "Actualisation en cours",
+ "up.readMore.expand": "Développer pour lire la suite",
+ "up.readMore.fold": "Réduire",
+ "up.search.placeholder": "Veuillez saisir un mot-clé",
+ "up.signature.penSize": "Taille du trait",
+ "up.signature.penColor": "Couleur du trait",
+ "up.upload.sizeExceed": "Dépassement de la limite de taille",
+ "up.upload.uploading": "Téléchargement en cours",
+ "up.upload.previewImageFail": "Échec de l'aperçu de l'image",
+ "up.upload.previewVideoFail": "Échec de l'aperçu de la vidéo",
+ "up.goodsSku.stock": "Stock",
+ "up.goodsSku.price": "Prix",
+ "up.goodsSku.amount": "Pièces",
+ "up.goodsSku.choosed": "Sélectionné",
+ "up.goodsSku.buyAmount": "Quantité"
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/i18n/locales/ja.json b/uni_modules/uview-plus/libs/i18n/locales/ja.json
new file mode 100644
index 0000000..965561c
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/locales/ja.json
@@ -0,0 +1,85 @@
+{
+ "up.common.cancel": "キャンセル",
+ "up.common.confirm": "確認",
+ "up.common.start": "開始",
+ "up.common.end": "終了",
+ "up.common.stop": "停止",
+ "up.common.copy": "コピー",
+ "up.common.none": "なし",
+ "up.common.tip": "ヒント",
+ "up.common.success": "成功",
+ "up.common.fail": "失敗",
+ "up.common.close": "閉じる",
+ "up.common.preview": "プレビュー",
+ "up.common.re-select": "再選択",
+ "up.common.rotate": "回転",
+ "up.common.pleaseChoose": "選択してください",
+ "up.common.loading": "読み込み中",
+ "up.common.loading2": "読み込み中",
+ "up.common.inOperation": "操作中",
+ "up.common.settings": "設定",
+ "up.common.retry": "再試行",
+ "up.common.search": "検索",
+ "up.common.more": "もっと見る",
+ "up.common.video": "ビデオ",
+ "up.common.file": "ファイル",
+ "up.week.one": "月",
+ "up.week.two": "火",
+ "up.week.three": "水",
+ "up.week.four": "木",
+ "up.week.five": "金",
+ "up.week.six": "土",
+ "up.week.seven": "日",
+ "up.barcode.error": "バーコードの生成に失敗しました",
+ "up.calendar.chooseDates": "日付選択",
+ "up.calendar.disabled": "この日付は無効です",
+ "up.calendar.daysExceed": "選択日数は{days}日を超えることはできません",
+ "up.cityLocate.locateCity": "都市の位置を特定",
+ "up.cityLocate.fail": "位置特定に失敗しました。再試行するにはクリックしてください。",
+ "up.cityLocate.locating": "位置特定中",
+ "up.code.send": "認証コードを取得",
+ "up.code.resendAfter": "X秒後に再送信",
+ "up.code.resend": "再送信",
+ "up.cropper.emptyWidhtOrHeight": "切り抜き枠の幅または高さが設定されていません",
+ "up.empty.car": "ショッピングカートは空です",
+ "up.empty.page": "ページが存在しません",
+ "up.empty.search": "検索結果がありません",
+ "up.empty.address": "配送先住所がありません",
+ "up.empty.wifi": "Wi-Fiがありません",
+ "up.empty.order": "注文がありません",
+ "up.empty.coupon": "クーポンがありません",
+ "up.empty.favor": "お気に入りがありません",
+ "up.empty.permission": "権限がありません",
+ "up.empty.history": "履歴がありません",
+ "up.empty.news": "ニュースがありません",
+ "up.empty.message": "メッセージがありません",
+ "up.empty.list": "リストが空です",
+ "up.empty.data": "データがありません",
+ "up.empty.comment": "コメントがありません",
+ "up.link.copyed": "リンクがコピーされました。ブラウザで開いてください",
+ "up.loadmoe.loadmore": "さらに読み込む",
+ "up.loadmoe.nomore": "これ以上データがありません",
+ "up.noNetwork.text": "ネットワーク信号が失われました",
+ "up.noNetwork.pleaseCheck": "ネットワークを確認するか、移動してください",
+ "up.noNetwork.connect": "ネットワーク接続済み",
+ "up.noNetwork.disconnect": "ネットワーク未接続",
+ "up.pagination.previous": "前へ",
+ "up.pagination.next": "次へ",
+ "up.pullRefresh.pull": "引き下げて更新",
+ "up.pullRefresh.release": "指を離して更新",
+ "up.pullRefresh.refreshing": "更新中",
+ "up.readMore.expand": "全文表示",
+ "up.readMore.fold": "折りたたむ",
+ "up.search.placeholder": "キーワードを入力してください",
+ "up.signature.penSize": "線の太さ",
+ "up.signature.penColor": "線の色",
+ "up.upload.sizeExceed": "サイズ制限を超えています",
+ "up.upload.uploading": "アップロード中",
+ "up.upload.previewImageFail": "画像プレビュー失敗",
+ "up.upload.previewVideoFail": "ビデオプレビュー失敗",
+ "up.goodsSku.stock": "在庫",
+ "up.goodsSku.price": "価格",
+ "up.goodsSku.amount": "個",
+ "up.goodsSku.choosed": "選択済み",
+ "up.goodsSku.buyAmount": "購入数量"
+}
diff --git a/uni_modules/uview-plus/libs/i18n/locales/ko.json b/uni_modules/uview-plus/libs/i18n/locales/ko.json
new file mode 100644
index 0000000..7e3c838
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/locales/ko.json
@@ -0,0 +1,85 @@
+{
+ "up.common.cancel": "취소",
+ "up.common.confirm": "확인",
+ "up.common.start": "시작",
+ "up.common.end": "종료",
+ "up.common.stop": "정지",
+ "up.common.copy": "복사",
+ "up.common.none": "없음",
+ "up.common.tip": "팁",
+ "up.common.success": "성공",
+ "up.common.fail": "실패",
+ "up.common.close": "닫기",
+ "up.common.preview": "미리보기",
+ "up.common.re-select": "재선택",
+ "up.common.rotate": "회전",
+ "up.common.pleaseChoose": "선택해주세요",
+ "up.common.loading": "로딩중",
+ "up.common.loading2": "로딩중",
+ "up.common.inOperation": "작업중",
+ "up.common.settings": "설정",
+ "up.common.retry": "재시도",
+ "up.common.search": "검색",
+ "up.common.more": "더보기",
+ "up.common.video": "비디오",
+ "up.common.file": "파일",
+ "up.week.one": "월",
+ "up.week.two": "화",
+ "up.week.three": "수",
+ "up.week.four": "목",
+ "up.week.five": "금",
+ "up.week.six": "토",
+ "up.week.seven": "일",
+ "up.barcode.error": "바코드 생성 실패",
+ "up.calendar.chooseDates": "날짜 선택",
+ "up.calendar.disabled": "해당 날짜는 사용할 수 없습니다",
+ "up.calendar.daysExceed": "선택한 날짜 수가 {days}일을 초과할 수 없습니다",
+ "up.cityLocate.locateCity": "도시 위치 찾기",
+ "up.cityLocate.fail": "위치 찾기 실패, 다시 시도하려면 클릭하세요.",
+ "up.cityLocate.locating": "위치 찾는 중",
+ "up.code.send": "인증코드 받기",
+ "up.code.resendAfter": "X초 후 재전송",
+ "up.code.resend": "재전송",
+ "up.cropper.emptyWidhtOrHeight": "자르기 영역의 너비 또는 높이가 설정되지 않았습니다",
+ "up.empty.car": "장바구니가 비어 있습니다",
+ "up.empty.page": "페이지가 존재하지 않습니다",
+ "up.empty.search": "검색 결과가 없습니다",
+ "up.empty.address": "배송 주소가 없습니다",
+ "up.empty.wifi": "Wi-Fi가 없습니다",
+ "up.empty.order": "주문이 없습니다",
+ "up.empty.coupon": "쿠폰이 없습니다",
+ "up.empty.favor": "즐겨찾기가 없습니다",
+ "up.empty.permission": "권한이 없습니다",
+ "up.empty.history": "기록이 없습니다",
+ "up.empty.news": "뉴스가 없습니다",
+ "up.empty.message": "메시지가 없습니다",
+ "up.empty.list": "목록이 비어 있습니다",
+ "up.empty.data": "데이터가 없습니다",
+ "up.empty.comment": "댓글이 없습니다",
+ "up.link.copyed": "링크가 복사되었습니다. 브라우저에서 열어주세요",
+ "up.loadmoe.loadmore": "더 불러오기",
+ "up.loadmoe.nomore": "더 이상 데이터가 없습니다",
+ "up.noNetwork.text": "네트워크 신호가 없습니다",
+ "up.noNetwork.pleaseCheck": "네트워크를 확인하거나 이동하세요",
+ "up.noNetwork.connect": "네트워크 연결됨",
+ "up.noNetwork.disconnect": "네트워크 연결 끊김",
+ "up.pagination.previous": "이전 페이지",
+ "up.pagination.next": "다음 페이지",
+ "up.pullRefresh.pull": "당겨서 새로고침",
+ "up.pullRefresh.release": "놓아서 새로고침",
+ "up.pullRefresh.refreshing": "새로고침 중",
+ "up.readMore.expand": "펼쳐서 전체 보기",
+ "up.readMore.fold": "접기",
+ "up.search.placeholder": "키워드를 입력하세요",
+ "up.signature.penSize": "선 굵기",
+ "up.signature.penColor": "선 색상",
+ "up.upload.sizeExceed": "용량 제한 초과",
+ "up.upload.uploading": "업로드 중",
+ "up.upload.previewImageFail": "이미지 미리보기 실패",
+ "up.upload.previewVideoFail": "비디오 미리보기 실패",
+ "up.goodsSku.stock": "재고",
+ "up.goodsSku.price": "가격",
+ "up.goodsSku.amount": "개",
+ "up.goodsSku.choosed": "선택됨",
+ "up.goodsSku.buyAmount": "구매 수량"
+}
diff --git a/uni_modules/uview-plus/libs/i18n/locales/ru.json b/uni_modules/uview-plus/libs/i18n/locales/ru.json
new file mode 100644
index 0000000..4ee2528
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/locales/ru.json
@@ -0,0 +1,85 @@
+{
+ "up.common.cancel": "Отмена",
+ "up.common.confirm": "Подтвердить",
+ "up.common.start": "Начало",
+ "up.common.end": "Конец",
+ "up.common.stop": "Стоп",
+ "up.common.copy": "Копировать",
+ "up.common.none": "Нет",
+ "up.common.tip": "Подсказка",
+ "up.common.success": "Успех",
+ "up.common.fail": "Ошибка",
+ "up.common.close": "Закрыть",
+ "up.common.preview": "Предпросмотр",
+ "up.common.re-select": "Выбрать снова",
+ "up.common.rotate": "Повернуть",
+ "up.common.pleaseChoose": "Пожалуйста, выберите",
+ "up.common.loading": "Загрузка",
+ "up.common.loading2": "Загружается",
+ "up.common.inOperation": "В процессе",
+ "up.common.settings": "Настройки",
+ "up.common.retry": "Повторить",
+ "up.common.search": "Поиск",
+ "up.common.more": "Больше",
+ "up.common.video": "Видео",
+ "up.common.file": "Файл",
+ "up.week.one": "Пн",
+ "up.week.two": "Вт",
+ "up.week.three": "Ср",
+ "up.week.four": "Чт",
+ "up.week.five": "Пт",
+ "up.week.six": "Сб",
+ "up.week.seven": "Вс",
+ "up.barcode.error": "Ошибка генерации штрихкода",
+ "up.calendar.chooseDates": "Выбор даты",
+ "up.calendar.disabled": "Эта дата отключена",
+ "up.calendar.daysExceed": "Количество выбранных дней не может превышать {days} дней",
+ "up.cityLocate.locateCity": "Определение города",
+ "up.cityLocate.fail": "Ошибка определения местоположения, нажмите для повтора.",
+ "up.cityLocate.locating": "Определение местоположения",
+ "up.code.send": "Получить код подтверждения",
+ "up.code.resendAfter": "Повторная отправка через X секунд",
+ "up.code.resend": "Отправить снова",
+ "up.cropper.emptyWidhtOrHeight": "Ширина или высота области обрезки не задана",
+ "up.empty.car": "Корзина пуста",
+ "up.empty.page": "Страница не существует",
+ "up.empty.search": "Нет результатов поиска",
+ "up.empty.address": "Нет адреса доставки",
+ "up.empty.wifi": "Нет Wi-Fi",
+ "up.empty.order": "Заказы отсутствуют",
+ "up.empty.coupon": "Нет купонов",
+ "up.empty.favor": "Нет избранного",
+ "up.empty.permission": "Нет разрешения",
+ "up.empty.history": "Нет истории",
+ "up.empty.news": "Нет новостей",
+ "up.empty.message": "Список сообщений пуст",
+ "up.empty.list": "Список пуст",
+ "up.empty.data": "Нет данных",
+ "up.empty.comment": "Нет комментариев",
+ "up.link.copyed": "Ссылка скопирована, откройте в браузере",
+ "up.loadmoe.loadmore": "Загрузить еще",
+ "up.loadmoe.nomore": "Больше нет данных",
+ "up.noNetwork.text": "Ой, потеряно сетевое соединение",
+ "up.noNetwork.pleaseCheck": "Проверьте сеть или перейдите к",
+ "up.noNetwork.connect": "Сеть подключена",
+ "up.noNetwork.disconnect": "Нет сетевого подключения",
+ "up.pagination.previous": "Предыдущая страница",
+ "up.pagination.next": "Следующая страница",
+ "up.pullRefresh.pull": "Потяните вниз для обновления",
+ "up.pullRefresh.release": "Отпустите для обновления",
+ "up.pullRefresh.refreshing": "Обновление",
+ "up.readMore.expand": "Развернуть для полного чтения",
+ "up.readMore.fold": "Свернуть",
+ "up.search.placeholder": "Введите ключевое слово",
+ "up.signature.penSize": "Размер штриха",
+ "up.signature.penColor": "Цвет штриха",
+ "up.upload.sizeExceed": "Превышен лимит размера",
+ "up.upload.uploading": "Загрузка",
+ "up.upload.previewImageFail": "Ошибка предпросмотра изображения",
+ "up.upload.previewVideoFail": "Ошибка предпросмотра видео",
+ "up.goodsSku.stock": "Запас",
+ "up.goodsSku.price": "Цена",
+ "up.goodsSku.amount": "Штук",
+ "up.goodsSku.choosed": "Выбрано",
+ "up.goodsSku.buyAmount": "Количество"
+}
diff --git a/uni_modules/uview-plus/libs/i18n/locales/th.json b/uni_modules/uview-plus/libs/i18n/locales/th.json
new file mode 100644
index 0000000..8d23888
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/locales/th.json
@@ -0,0 +1,85 @@
+{
+ "up.common.cancel": "ยกเลิก",
+ "up.common.confirm": "ยืนยัน",
+ "up.common.start": "เริ่มต้น",
+ "up.common.end": "สิ้นสุด",
+ "up.common.stop": "หยุด",
+ "up.common.copy": "คัดลอก",
+ "up.common.none": "ไม่มี",
+ "up.common.tip": "คำแนะนำ",
+ "up.common.success": "สำเร็จ",
+ "up.common.fail": "ล้มเหลว",
+ "up.common.close": "ปิด",
+ "up.common.preview": "ดูตัวอย่าง",
+ "up.common.re-select": "เลือกใหม่",
+ "up.common.rotate": "หมุน",
+ "up.common.pleaseChoose": "กรุณาเลือก",
+ "up.common.loading": "กำลังโหลด",
+ "up.common.loading2": "กำลังโหลด",
+ "up.common.inOperation": "กำลังดำเนินการ",
+ "up.common.settings": "การตั้งค่า",
+ "up.common.retry": "ลองใหม่",
+ "up.common.search": "ค้นหา",
+ "up.common.more": "เพิ่มเติม",
+ "up.common.video": "วิดีโอ",
+ "up.common.file": "ไฟล์",
+ "up.week.one": "จันทร์",
+ "up.week.two": "อังคาร",
+ "up.week.three": "พุธ",
+ "up.week.four": "พฤหัสบดี",
+ "up.week.five": "ศุกร์",
+ "up.week.six": "เสาร์",
+ "up.week.seven": "อาทิตย์",
+ "up.barcode.error": "สร้างบาร์โค้ดไม่สำเร็จ",
+ "up.calendar.chooseDates": "เลือกวันที่",
+ "up.calendar.disabled": "วันที่นี้ถูกปิดการใช้งาน",
+ "up.calendar.daysExceed": "จำนวนวันที่เลือกต้องไม่เกิน {days} วัน",
+ "up.cityLocate.locateCity": "ระบุตำแหน่งเมือง",
+ "up.cityLocate.fail": "การระบุตำแหน่งล้มเหลว กรุณาคลิกเพื่อลองใหม่",
+ "up.cityLocate.locating": "กำลังระบุตำแหน่ง",
+ "up.code.send": "รับรหัสยืนยัน",
+ "up.code.resendAfter": "ส่งใหม่ใน X วินาที",
+ "up.code.resend": "ส่งใหม่",
+ "up.cropper.emptyWidhtOrHeight": "ไม่ได้ตั้งค่าความกว้างหรือความสูงของกรอบตัดภาพ",
+ "up.empty.car": "รถเข็นว่างเปล่า",
+ "up.empty.page": "ไม่มีหน้านี้",
+ "up.empty.search": "ไม่มีผลลัพธ์การค้นหา",
+ "up.empty.address": "ไม่มีที่อยู่จัดส่ง",
+ "up.empty.wifi": "ไม่มี WiFi",
+ "up.empty.order": "ไม่มีคำสั่งซื้อ",
+ "up.empty.coupon": "ไม่มีคูปอง",
+ "up.empty.favor": "ไม่มีรายการโปรด",
+ "up.empty.permission": "ไม่มีสิทธิ์",
+ "up.empty.history": "ไม่มีประวัติ",
+ "up.empty.news": "ไม่มีรายการข่าว",
+ "up.empty.message": "รายการข้อความว่างเปล่า",
+ "up.empty.list": "รายการว่างเปล่า",
+ "up.empty.data": "ข้อมูลว่างเปล่า",
+ "up.empty.comment": "ไม่มีความคิดเห็น",
+ "up.link.copyed": "คัดลอกลิงก์แล้ว กรุณาเปิดในเบราว์เซอร์",
+ "up.loadmoe.loadmore": "โหลดเพิ่มเติม",
+ "up.loadmoe.nomore": "ไม่มีข้อมูลเพิ่มเติม",
+ "up.noNetwork.text": "อ๊ะ ขาดการเชื่อมต่อเครือข่าย",
+ "up.noNetwork.pleaseCheck": "กรุณาตรวจสอบเครือข่าย หรือไปที่",
+ "up.noNetwork.connect": "เชื่อมต่อเครือข่ายแล้ว",
+ "up.noNetwork.disconnect": "ไม่มีการเชื่อมต่อเครือข่าย",
+ "up.pagination.previous": "หน้าก่อนหน้า",
+ "up.pagination.next": "หน้าถัดไป",
+ "up.pullRefresh.pull": "ดึงเพื่อรีเฟรช",
+ "up.pullRefresh.release": "ปล่อยเพื่อรีเฟรช",
+ "up.pullRefresh.refreshing": "กำลังรีเฟรช",
+ "up.readMore.expand": "ขยายเพื่ออ่านทั้งหมด",
+ "up.readMore.fold": "ย่อ",
+ "up.search.placeholder": "กรุณาใส่คำสำคัญ",
+ "up.signature.penSize": "ขนาดเส้น",
+ "up.signature.penColor": "สีเส้น",
+ "up.upload.sizeExceed": "เกินขนาดที่กำหนด",
+ "up.upload.uploading": "กำลังอัปโหลด",
+ "up.upload.previewImageFail": "ดูตัวอย่างภาพไม่สำเร็จ",
+ "up.upload.previewVideoFail": "ดูตัวอย่างวิดีโอไม่สำเร็จ",
+ "up.goodsSku.stock": "สินค้าคงคลัง",
+ "up.goodsSku.price": "ราคา",
+ "up.goodsSku.amount": "ชิ้น",
+ "up.goodsSku.choosed": "เลือกแล้ว",
+ "up.goodsSku.buyAmount": "จำนวน"
+}
diff --git a/uni_modules/uview-plus/libs/i18n/locales/zh-Hans.json b/uni_modules/uview-plus/libs/i18n/locales/zh-Hans.json
new file mode 100644
index 0000000..b474ff4
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/locales/zh-Hans.json
@@ -0,0 +1,85 @@
+{
+ "up.common.cancel": "取消",
+ "up.common.confirm": "确定",
+ "up.common.start": "开始",
+ "up.common.end": "结束",
+ "up.common.stop": "停止",
+ "up.common.copy": "复制",
+ "up.common.none": "暂无",
+ "up.common.tip": "提示",
+ "up.common.success": "成功",
+ "up.common.fail": "失败",
+ "up.common.close": "关闭",
+ "up.common.preview": "预览",
+ "up.common.re-select": "重选",
+ "up.common.rotate": "旋转",
+ "up.common.pleaseChoose": "请选择",
+ "up.common.loading": "加载中",
+ "up.common.loading2": "正在加载",
+ "up.common.inOperation": "操作中",
+ "up.common.settings": "设置",
+ "up.common.retry": "重试",
+ "up.common.search": "搜索",
+ "up.common.more": "更多",
+ "up.common.video": "视频",
+ "up.common.file": "文件",
+ "up.week.one": "一",
+ "up.week.two": "二",
+ "up.week.three": "三",
+ "up.week.four": "四",
+ "up.week.five": "五",
+ "up.week.six": "六",
+ "up.week.seven": "日",
+ "up.barcode.error": "生成条码失败",
+ "up.calendar.chooseDates": "日期选择",
+ "up.calendar.disabled": "该日期已禁用",
+ "up.calendar.daysExceed": "选择天数不能超过{days}天",
+ "up.cityLocate.locateCity": "定位城市",
+ "up.cityLocate.fail": "定位失败,请点击重试。",
+ "up.cityLocate.locating": "定位中",
+ "up.code.send": "获取验证码",
+ "up.code.resendAfter": "X秒重新获取",
+ "up.code.resend": "重新获取",
+ "up.cropper.emptyWidhtOrHeight": "裁剪框的宽或高没有设置",
+ "up.empty.car": "购物车为空",
+ "up.empty.page": "页面不存在",
+ "up.empty.search": "没有搜索结果",
+ "up.empty.address": "没有收货地址",
+ "up.empty.wifi": "没有WiFi",
+ "up.empty.order": "订单为空",
+ "up.empty.coupon": "没有优惠券",
+ "up.empty.favor": "暂无收藏",
+ "up.empty.permission": "无权限",
+ "up.empty.history": "无历史记录",
+ "up.empty.news": "无新闻列表",
+ "up.empty.message": "消息列表为空",
+ "up.empty.list": "列表为空",
+ "up.empty.data": "数据为空",
+ "up.empty.comment": "暂无评论",
+ "up.link.copyed": "链接已复制,请在浏览器打开",
+ "up.loadmoe.loadmore": "加载更多",
+ "up.loadmoe.nomore": "没有更多了",
+ "up.noNetwork.text": "哎呀,网络信号丢失",
+ "up.noNetwork.pleaseCheck": "请检查网络,或前往",
+ "up.noNetwork.connect": "网络已连接",
+ "up.noNetwork.disconnect": "无网络连接",
+ "up.pagination.previous": "上一页",
+ "up.pagination.next": "下一页",
+ "up.pullRefresh.pull": "下拉刷新",
+ "up.pullRefresh.release": "释放刷新",
+ "up.pullRefresh.refreshing": "正在刷新",
+ "up.readMore.expand": "展开阅读全文",
+ "up.readMore.fold": "收起",
+ "up.search.placeholder": "请输入关键字",
+ "up.signature.penSize": "笔画大小",
+ "up.signature.penColor": "笔画颜色",
+ "up.upload.sizeExceed": "超过大小限制",
+ "up.upload.uploading": "上传中",
+ "up.upload.previewImageFail": "预览图片失败",
+ "up.upload.previewVideoFail": "预览视频失败",
+ "up.goodsSku.stock": "库存",
+ "up.goodsSku.price": "价格",
+ "up.goodsSku.amount": "件",
+ "up.goodsSku.choosed": "已选",
+ "up.goodsSku.buyAmount": "购买数量"
+}
diff --git a/uni_modules/uview-plus/libs/i18n/locales/zh-Hant.json b/uni_modules/uview-plus/libs/i18n/locales/zh-Hant.json
new file mode 100644
index 0000000..48e780b
--- /dev/null
+++ b/uni_modules/uview-plus/libs/i18n/locales/zh-Hant.json
@@ -0,0 +1,85 @@
+{
+ "up.common.cancel": "取消",
+ "up.common.confirm": "確定",
+ "up.common.start": "開始",
+ "up.common.end": "結束",
+ "up.common.stop": "停止",
+ "up.common.copy": "複製",
+ "up.common.none": "暫無",
+ "up.common.tip": "提示",
+ "up.common.success": "成功",
+ "up.common.fail": "失敗",
+ "up.common.close": "關閉",
+ "up.common.preview": "預覽",
+ "up.common.re-select": "重選",
+ "up.common.rotate": "旋轉",
+ "up.common.pleaseChoose": "請選擇",
+ "up.common.loading": "加載中",
+ "up.common.loading2": "正在加載",
+ "up.common.inOperation": "操作中",
+ "up.common.settings": "設置",
+ "up.common.retry": "重試",
+ "up.common.search": "搜索",
+ "up.common.more": "更多",
+ "up.common.video": "視頻",
+ "up.common.file": "文件",
+ "up.week.one": "一",
+ "up.week.two": "二",
+ "up.week.three": "三",
+ "up.week.four": "四",
+ "up.week.five": "五",
+ "up.week.six": "六",
+ "up.week.seven": "日",
+ "up.barcode.error": "生成條碼失敗",
+ "up.calendar.chooseDates": "日期選擇",
+ "up.calendar.disabled": "該日期已禁用",
+ "up.calendar.daysExceed": "選擇天數不能超過{days}天",
+ "up.cityLocate.locateCity": "定位城市",
+ "up.cityLocate.fail": "定位失敗,請點擊重試。",
+ "up.cityLocate.locating": "定位中",
+ "up.code.send": "獲取驗證碼",
+ "up.code.resendAfter": "X秒重新獲取",
+ "up.code.resend": "重新獲取",
+ "up.cropper.emptyWidhtOrHeight": "裁剪框的寬或高沒有設置",
+ "up.empty.car": "購物車為空",
+ "up.empty.page": "頁面不存在",
+ "up.empty.search": "沒有搜索結果",
+ "up.empty.address": "沒有收貨地址",
+ "up.empty.wifi": "沒有WiFi",
+ "up.empty.order": "訂單為空",
+ "up.empty.coupon": "沒有優惠券",
+ "up.empty.favor": "暫無收藏",
+ "up.empty.permission": "無權限",
+ "up.empty.history": "無歷史記錄",
+ "up.empty.news": "無新聞列表",
+ "up.empty.message": "消息列表為空",
+ "up.empty.list": "列表為空",
+ "up.empty.data": "數據為空",
+ "up.empty.comment": "暫無評論",
+ "up.link.copyed": "鏈接已複製,請在瀏覽器打開",
+ "up.loadmoe.loadmore": "加載更多",
+ "up.loadmoe.nomore": "沒有更多了",
+ "up.noNetwork.text": "哎呀,網絡信號丟失",
+ "up.noNetwork.pleaseCheck": "請檢查網絡,或前往",
+ "up.noNetwork.connect": "網絡已連接",
+ "up.noNetwork.disconnect": "無網絡連接",
+ "up.pagination.previous": "上一頁",
+ "up.pagination.next": "下一頁",
+ "up.pullRefresh.pull": "下拉刷新",
+ "up.pullRefresh.release": "釋放刷新",
+ "up.pullRefresh.refreshing": "正在刷新",
+ "up.readMore.expand": "展開閱讀全文",
+ "up.readMore.fold": "收起",
+ "up.search.placeholder": "請輸入關鍵字",
+ "up.signature.penSize": "筆畫大小",
+ "up.signature.penColor": "筆畫顏色",
+ "up.upload.sizeExceed": "超過大小限制",
+ "up.upload.uploading": "上傳中",
+ "up.upload.previewImageFail": "預覽圖片失敗",
+ "up.upload.previewVideoFail": "預覽視頻失敗",
+ "up.goodsSku.stock": "庫存",
+ "up.goodsSku.price": "價格",
+ "up.goodsSku.amount": "件",
+ "up.goodsSku.choosed": "已選",
+ "up.goodsSku.buyAmount": "購買數量"
+}
diff --git a/uni_modules/uview-plus/libs/luch-request/adapters/index.js b/uni_modules/uview-plus/libs/luch-request/adapters/index.js
new file mode 100644
index 0000000..e03cf5f
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/adapters/index.js
@@ -0,0 +1,97 @@
+import buildURL from '../helpers/buildURL'
+import buildFullPath from '../core/buildFullPath'
+import settle from '../core/settle'
+import { isUndefined } from '../utils'
+
+/**
+ * 返回可选值存在的配置
+ * @param {Array} keys - 可选值数组
+ * @param {Object} config2 - 配置
+ * @return {{}} - 存在的配置项
+ */
+const mergeKeys = (keys, config2) => {
+ const config = {}
+ keys.forEach((prop) => {
+ if (!isUndefined(config2[prop])) {
+ config[prop] = config2[prop]
+ }
+ })
+ return config
+}
+export default (config) => new Promise((resolve, reject) => {
+ const fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params)
+ const _config = {
+ url: fullPath,
+ header: config.header,
+ complete: (response) => {
+ config.fullPath = fullPath
+ response.config = config
+ try {
+ // 对可能字符串不是json 的情况容错
+ if (typeof response.data === 'string') {
+ response.data = JSON.parse(response.data)
+ }
+ // eslint-disable-next-line no-empty
+ } catch (e) {
+ }
+ settle(resolve, reject, response)
+ }
+ }
+ let requestTask
+ if (config.method === 'UPLOAD') {
+ delete _config.header['content-type']
+ delete _config.header['Content-Type']
+ const otherConfig = {
+ // #ifdef MP-ALIPAY
+ fileType: config.fileType,
+ // #endif
+ filePath: config.filePath,
+ name: config.name
+ }
+ const optionalKeys = [
+ // #ifdef APP-PLUS || H5
+ 'files',
+ // #endif
+ // #ifdef H5
+ 'file',
+ // #endif
+ // #ifdef H5 || APP-PLUS
+ 'timeout',
+ // #endif
+ 'formData'
+ ]
+ requestTask = uni.uploadFile({ ..._config, ...otherConfig, ...mergeKeys(optionalKeys, config) })
+ } else if (config.method === 'DOWNLOAD') {
+ // #ifdef H5 || APP-PLUS
+ if (!isUndefined(config.timeout)) {
+ _config.timeout = config.timeout
+ }
+ // #endif
+ requestTask = uni.downloadFile(_config)
+ } else {
+ const optionalKeys = [
+ 'data',
+ 'method',
+ // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
+ 'timeout',
+ // #endif
+ 'dataType',
+ // #ifndef MP-ALIPAY
+ 'responseType',
+ // #endif
+ // #ifdef APP-PLUS
+ 'sslVerify',
+ // #endif
+ // #ifdef H5
+ 'withCredentials',
+ // #endif
+ // #ifdef APP-PLUS
+ 'firstIpv4'
+ // #endif
+ ]
+ requestTask = uni.request({ ..._config, ...mergeKeys(optionalKeys, config) })
+ }
+ if (config.getTask) {
+ config.getTask(requestTask, config)
+ }
+})
diff --git a/uni_modules/uview-plus/libs/luch-request/core/InterceptorManager.js b/uni_modules/uview-plus/libs/luch-request/core/InterceptorManager.js
new file mode 100644
index 0000000..3e8728d
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/core/InterceptorManager.js
@@ -0,0 +1,50 @@
+'use strict'
+
+function InterceptorManager() {
+ this.handlers = []
+}
+
+/**
+ * Add a new interceptor to the stack
+ *
+ * @param {Function} fulfilled The function to handle `then` for a `Promise`
+ * @param {Function} rejected The function to handle `reject` for a `Promise`
+ *
+ * @return {Number} An ID used to remove interceptor later
+ */
+InterceptorManager.prototype.use = function use(fulfilled, rejected) {
+ this.handlers.push({
+ fulfilled,
+ rejected
+ })
+ return this.handlers.length - 1
+}
+
+/**
+ * Remove an interceptor from the stack
+ *
+ * @param {Number} id The ID that was returned by `use`
+ */
+InterceptorManager.prototype.eject = function eject(id) {
+ if (this.handlers[id]) {
+ this.handlers[id] = null
+ }
+}
+
+/**
+ * Iterate over all the registered interceptors
+ *
+ * This method is particularly useful for skipping over any
+ * interceptors that may have become `null` calling `eject`.
+ *
+ * @param {Function} fn The function to call for each interceptor
+ */
+InterceptorManager.prototype.forEach = function forEach(fn) {
+ this.handlers.forEach((h) => {
+ if (h !== null) {
+ fn(h)
+ }
+ })
+}
+
+export default InterceptorManager
diff --git a/uni_modules/uview-plus/libs/luch-request/core/Request.js b/uni_modules/uview-plus/libs/luch-request/core/Request.js
new file mode 100644
index 0000000..11e6dd5
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/core/Request.js
@@ -0,0 +1,199 @@
+/**
+ * @Class Request
+ * @description luch-request http请求插件
+ * @version 3.0.7
+ * @Author lu-ch
+ * @Date 2021-09-04
+ * @Email webwork.s@qq.com
+ * 文档: https://www.quanzhan.co/luch-request/
+ * github: https://github.com/lei-mu/luch-request
+ * DCloud: http://ext.dcloud.net.cn/plugin?id=392
+ * HBuilderX: beat-3.0.4 alpha-3.0.4
+ */
+
+import dispatchRequest from './dispatchRequest'
+import InterceptorManager from './InterceptorManager'
+import mergeConfig from './mergeConfig'
+import defaults from './defaults'
+import { isPlainObject } from '../utils'
+import clone from '../utils/clone'
+
+export default class Request {
+ /**
+ * @param {Object} arg - 全局配置
+ * @param {String} arg.baseURL - 全局根路径
+ * @param {Object} arg.header - 全局header
+ * @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
+ * @param {String} arg.dataType = [json] - 全局默认的dataType
+ * @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType。支付宝小程序不支持
+ * @param {Object} arg.custom - 全局默认的自定义参数
+ * @param {Number} arg.timeout - 全局默认的超时时间,单位 ms。默认60000。H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序
+ * @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书。默认true.仅App安卓端支持(HBuilderX 2.3.3+)
+ * @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证(cookies)。默认false。仅H5支持(HBuilderX 2.6.15+)
+ * @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4。默认false。仅 App-Android 支持 (HBuilderX 2.8.0+)
+ * @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器。默认statusCode >= 200 && statusCode < 300
+ */
+ constructor(arg = {}) {
+ // console.info('初始化luch-request')
+ if (!isPlainObject(arg)) {
+ arg = {}
+ console.warn('设置全局参数必须接收一个Object')
+ }
+ this.config = clone({ ...defaults, ...arg })
+ this.interceptors = {
+ request: new InterceptorManager(),
+ response: new InterceptorManager()
+ }
+ }
+
+ /**
+ * @Function
+ * @param {Request~setConfigCallback} f - 设置全局默认配置
+ */
+ setConfig(f) {
+ this.config = f(this.config)
+ }
+
+ middleware(config) {
+ config = mergeConfig(this.config, config)
+ const chain = [dispatchRequest, undefined]
+ let promise = Promise.resolve(config)
+
+ this.interceptors.request.forEach((interceptor) => {
+ chain.unshift(interceptor.fulfilled, interceptor.rejected)
+ })
+
+ this.interceptors.response.forEach((interceptor) => {
+ chain.push(interceptor.fulfilled, interceptor.rejected)
+ })
+
+ while (chain.length) {
+ promise = promise.then(chain.shift(), chain.shift())
+ }
+
+ return promise
+ }
+
+ /**
+ * @Function
+ * @param {Object} config - 请求配置项
+ * @prop {String} options.url - 请求路径
+ * @prop {Object} options.data - 请求参数
+ * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
+ * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
+ * @prop {Object} [options.header = config.header] - 请求header
+ * @prop {Object} [options.method = config.method] - 请求方法
+ * @returns {Promise}
+ */
+ request(config = {}) {
+ return this.middleware(config)
+ }
+
+ get(url, options = {}) {
+ return this.middleware({
+ url,
+ method: 'GET',
+ ...options
+ })
+ }
+
+ post(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'POST',
+ ...options
+ })
+ }
+
+ // #ifndef MP-ALIPAY
+ put(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'PUT',
+ ...options
+ })
+ }
+
+ // #endif
+
+ // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+ delete(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'DELETE',
+ ...options
+ })
+ }
+
+ // #endif
+
+ // #ifdef H5 || MP-WEIXIN
+ connect(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'CONNECT',
+ ...options
+ })
+ }
+
+ // #endif
+
+ // #ifdef H5 || MP-WEIXIN || MP-BAIDU
+ head(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'HEAD',
+ ...options
+ })
+ }
+
+ // #endif
+
+ // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+ options(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'OPTIONS',
+ ...options
+ })
+ }
+
+ // #endif
+
+ // #ifdef H5 || MP-WEIXIN
+ trace(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'TRACE',
+ ...options
+ })
+ }
+
+ // #endif
+
+ upload(url, config = {}) {
+ config.url = url
+ config.method = 'UPLOAD'
+ return this.middleware(config)
+ }
+
+ download(url, config = {}) {
+ config.url = url
+ config.method = 'DOWNLOAD'
+ return this.middleware(config)
+ }
+}
+
+/**
+ * setConfig回调
+ * @return {Object} - 返回操作后的config
+ * @callback Request~setConfigCallback
+ * @param {Object} config - 全局默认config
+ */
diff --git a/uni_modules/uview-plus/libs/luch-request/core/buildFullPath.js b/uni_modules/uview-plus/libs/luch-request/core/buildFullPath.js
new file mode 100644
index 0000000..5eb8a17
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/core/buildFullPath.js
@@ -0,0 +1,20 @@
+'use strict'
+
+import isAbsoluteURL from '../helpers/isAbsoluteURL'
+import combineURLs from '../helpers/combineURLs'
+
+/**
+ * Creates a new URL by combining the baseURL with the requestedURL,
+ * only when the requestedURL is not already an absolute URL.
+ * If the requestURL is absolute, this function returns the requestedURL untouched.
+ *
+ * @param {string} baseURL The base URL
+ * @param {string} requestedURL Absolute or relative URL to combine
+ * @returns {string} The combined full path
+ */
+export default function buildFullPath(baseURL, requestedURL) {
+ if (baseURL && !isAbsoluteURL(requestedURL)) {
+ return combineURLs(baseURL, requestedURL)
+ }
+ return requestedURL
+}
diff --git a/uni_modules/uview-plus/libs/luch-request/core/defaults.js b/uni_modules/uview-plus/libs/luch-request/core/defaults.js
new file mode 100644
index 0000000..be375a9
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/core/defaults.js
@@ -0,0 +1,29 @@
+/**
+ * 默认的全局配置
+ */
+
+export default {
+ baseURL: '',
+ header: {},
+ method: 'GET',
+ dataType: 'json',
+ // #ifndef MP-ALIPAY
+ responseType: 'text',
+ // #endif
+ custom: {},
+ // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
+ timeout: 60000,
+ // #endif
+ // #ifdef APP-PLUS
+ sslVerify: true,
+ // #endif
+ // #ifdef H5
+ withCredentials: false,
+ // #endif
+ // #ifdef APP-PLUS
+ firstIpv4: false,
+ // #endif
+ validateStatus: function validateStatus(status) {
+ return status >= 200 && status < 300
+ }
+}
diff --git a/uni_modules/uview-plus/libs/luch-request/core/dispatchRequest.js b/uni_modules/uview-plus/libs/luch-request/core/dispatchRequest.js
new file mode 100644
index 0000000..724545c
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/core/dispatchRequest.js
@@ -0,0 +1,3 @@
+import adapter from '../adapters/index'
+
+export default (config) => adapter(config)
diff --git a/uni_modules/uview-plus/libs/luch-request/core/mergeConfig.js b/uni_modules/uview-plus/libs/luch-request/core/mergeConfig.js
new file mode 100644
index 0000000..08f8b9b
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/core/mergeConfig.js
@@ -0,0 +1,103 @@
+import { deepMerge, isUndefined } from '../utils'
+
+/**
+ * 合并局部配置优先的配置,如果局部有该配置项则用局部,如果全局有该配置项则用全局
+ * @param {Array} keys - 配置项
+ * @param {Object} globalsConfig - 当前的全局配置
+ * @param {Object} config2 - 局部配置
+ * @return {{}}
+ */
+const mergeKeys = (keys, globalsConfig, config2) => {
+ const config = {}
+ keys.forEach((prop) => {
+ if (!isUndefined(config2[prop])) {
+ config[prop] = config2[prop]
+ } else if (!isUndefined(globalsConfig[prop])) {
+ config[prop] = globalsConfig[prop]
+ }
+ })
+ return config
+}
+/**
+ *
+ * @param globalsConfig - 当前实例的全局配置
+ * @param config2 - 当前的局部配置
+ * @return - 合并后的配置
+ */
+export default (globalsConfig, config2 = {}) => {
+ const method = config2.method || globalsConfig.method || 'GET'
+ let config = {
+ baseURL: globalsConfig.baseURL || '',
+ method,
+ url: config2.url || '',
+ params: config2.params || {},
+ custom: { ...(globalsConfig.custom || {}), ...(config2.custom || {}) },
+ header: deepMerge(globalsConfig.header || {}, config2.header || {})
+ }
+ const defaultToConfig2Keys = ['getTask', 'validateStatus']
+ config = { ...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2) }
+
+ // eslint-disable-next-line no-empty
+ if (method === 'DOWNLOAD') {
+ // #ifdef H5 || APP-PLUS
+ if (!isUndefined(config2.timeout)) {
+ config.timeout = config2.timeout
+ } else if (!isUndefined(globalsConfig.timeout)) {
+ config.timeout = globalsConfig.timeout
+ }
+ // #endif
+ } else if (method === 'UPLOAD') {
+ delete config.header['content-type']
+ delete config.header['Content-Type']
+ const uploadKeys = [
+ // #ifdef APP-PLUS || H5
+ 'files',
+ // #endif
+ // #ifdef MP-ALIPAY
+ 'fileType',
+ // #endif
+ // #ifdef H5
+ 'file',
+ // #endif
+ 'filePath',
+ 'name',
+ // #ifdef H5 || APP-PLUS
+ 'timeout',
+ // #endif
+ 'formData'
+ ]
+ uploadKeys.forEach((prop) => {
+ if (!isUndefined(config2[prop])) {
+ config[prop] = config2[prop]
+ }
+ })
+ // #ifdef H5 || APP-PLUS
+ if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
+ config.timeout = globalsConfig.timeout
+ }
+ // #endif
+ } else {
+ const defaultsKeys = [
+ 'data',
+ // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
+ 'timeout',
+ // #endif
+ 'dataType',
+ // #ifndef MP-ALIPAY
+ 'responseType',
+ // #endif
+ // #ifdef APP-PLUS
+ 'sslVerify',
+ // #endif
+ // #ifdef H5
+ 'withCredentials',
+ // #endif
+ // #ifdef APP-PLUS
+ 'firstIpv4'
+ // #endif
+ ]
+ config = { ...config, ...mergeKeys(defaultsKeys, globalsConfig, config2) }
+ }
+
+ return config
+}
diff --git a/uni_modules/uview-plus/libs/luch-request/core/settle.js b/uni_modules/uview-plus/libs/luch-request/core/settle.js
new file mode 100644
index 0000000..8d3638f
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/core/settle.js
@@ -0,0 +1,16 @@
+/**
+ * Resolve or reject a Promise based on response status.
+ *
+ * @param {Function} resolve A function that resolves the promise.
+ * @param {Function} reject A function that rejects the promise.
+ * @param {object} response The response.
+ */
+export default function settle(resolve, reject, response) {
+ const { validateStatus } = response.config
+ const status = response.statusCode
+ if (status && (!validateStatus || validateStatus(status))) {
+ resolve(response)
+ } else {
+ reject(response)
+ }
+}
diff --git a/uni_modules/uview-plus/libs/luch-request/helpers/buildURL.js b/uni_modules/uview-plus/libs/luch-request/helpers/buildURL.js
new file mode 100644
index 0000000..472ad6a
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/helpers/buildURL.js
@@ -0,0 +1,69 @@
+'use strict'
+
+import * as utils from '../utils'
+
+function encode(val) {
+ return encodeURIComponent(val)
+ .replace(/%40/gi, '@')
+ .replace(/%3A/gi, ':')
+ .replace(/%24/g, '$')
+ .replace(/%2C/gi, ',')
+ .replace(/%20/g, '+')
+ .replace(/%5B/gi, '[')
+ .replace(/%5D/gi, ']')
+}
+
+/**
+ * Build a URL by appending params to the end
+ *
+ * @param {string} url The base of the url (e.g., http://www.google.com)
+ * @param {object} [params] The params to be appended
+ * @returns {string} The formatted url
+ */
+export default function buildURL(url, params) {
+ /* eslint no-param-reassign:0 */
+ if (!params) {
+ return url
+ }
+
+ let serializedParams
+ if (utils.isURLSearchParams(params)) {
+ serializedParams = params.toString()
+ } else {
+ const parts = []
+
+ utils.forEach(params, (val, key) => {
+ if (val === null || typeof val === 'undefined') {
+ return
+ }
+
+ if (utils.isArray(val)) {
+ key = `${key}[]`
+ } else {
+ val = [val]
+ }
+
+ utils.forEach(val, (v) => {
+ if (utils.isDate(v)) {
+ v = v.toISOString()
+ } else if (utils.isObject(v)) {
+ v = JSON.stringify(v)
+ }
+ parts.push(`${encode(key)}=${encode(v)}`)
+ })
+ })
+
+ serializedParams = parts.join('&')
+ }
+
+ if (serializedParams) {
+ const hashmarkIndex = url.indexOf('#')
+ if (hashmarkIndex !== -1) {
+ url = url.slice(0, hashmarkIndex)
+ }
+
+ url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
+ }
+
+ return url
+}
diff --git a/uni_modules/uview-plus/libs/luch-request/helpers/combineURLs.js b/uni_modules/uview-plus/libs/luch-request/helpers/combineURLs.js
new file mode 100644
index 0000000..ac7c124
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/helpers/combineURLs.js
@@ -0,0 +1,14 @@
+'use strict'
+
+/**
+ * Creates a new URL by combining the specified URLs
+ *
+ * @param {string} baseURL The base URL
+ * @param {string} relativeURL The relative URL
+ * @returns {string} The combined URL
+ */
+export default function combineURLs(baseURL, relativeURL) {
+ return relativeURL
+ ? `${baseURL.replace(/\/+$/, '')}/${relativeURL.replace(/^\/+/, '')}`
+ : baseURL
+}
diff --git a/uni_modules/uview-plus/libs/luch-request/helpers/isAbsoluteURL.js b/uni_modules/uview-plus/libs/luch-request/helpers/isAbsoluteURL.js
new file mode 100644
index 0000000..63c6647
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/helpers/isAbsoluteURL.js
@@ -0,0 +1,14 @@
+'use strict'
+
+/**
+ * Determines whether the specified URL is absolute
+ *
+ * @param {string} url The URL to test
+ * @returns {boolean} True if the specified URL is absolute, otherwise false
+ */
+export default function isAbsoluteURL(url) {
+ // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL).
+ // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
+ // by any combination of letters, digits, plus, period, or hyphen.
+ return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
+}
diff --git a/uni_modules/uview-plus/libs/luch-request/index.d.ts b/uni_modules/uview-plus/libs/luch-request/index.d.ts
new file mode 100644
index 0000000..e939ce1
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/index.d.ts
@@ -0,0 +1,116 @@
+type AnyObject = Record
+type HttpPromise = Promise>;
+type Tasks = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask
+export interface RequestTask {
+ abort: () => void;
+ offHeadersReceived: () => void;
+ onHeadersReceived: () => void;
+}
+export interface HttpRequestConfig {
+ /** 请求基地址 */
+ baseURL?: string;
+ /** 请求服务器接口地址 */
+ url?: string;
+
+ /** 请求查询参数,自动拼接为查询字符串 */
+ params?: AnyObject;
+ /** 请求体参数 */
+ data?: AnyObject;
+
+ /** 文件对应的 key */
+ name?: string;
+ /** HTTP 请求中其他额外的 form data */
+ formData?: AnyObject;
+ /** 要上传文件资源的路径。 */
+ filePath?: string;
+ /** 需要上传的文件列表。使用 files 时,filePath 和 name 不生效,App、H5( 2.6.15+) */
+ files?: Array<{
+ name?: string;
+ file?: File;
+ uri: string;
+ }>;
+ /** 要上传的文件对象,仅H5(2.6.15+)支持 */
+ file?: File;
+
+ /** 请求头信息 */
+ header?: AnyObject;
+ /** 请求方式 */
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "CONNECT" | "HEAD" | "OPTIONS" | "TRACE" | "UPLOAD" | "DOWNLOAD";
+ /** 如果设为 json,会尝试对返回的数据做一次 JSON.parse */
+ dataType?: string;
+ /** 设置响应的数据类型,支付宝小程序不支持 */
+ responseType?: "text" | "arraybuffer";
+ /** 自定义参数 */
+ custom?: AnyObject;
+ /** 超时时间,仅微信小程序(2.10.0)、支付宝小程序支持 */
+ timeout?: number;
+ /** DNS解析时优先使用ipv4,仅 App-Android 支持 (HBuilderX 2.8.0+) */
+ firstIpv4?: boolean;
+ /** 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+) */
+ sslVerify?: boolean;
+ /** 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+) */
+ withCredentials?: boolean;
+
+ /** 返回当前请求的task, options。请勿在此处修改options。 */
+ getTask?: (task: T, options: HttpRequestConfig) => void;
+ /** 全局自定义验证器 */
+ validateStatus?: (statusCode: number) => boolean | void;
+}
+export interface HttpResponse {
+ config: HttpRequestConfig;
+ statusCode: number;
+ cookies: Array;
+ data: T;
+ errMsg: string;
+ header: AnyObject;
+}
+export interface HttpUploadResponse {
+ config: HttpRequestConfig;
+ statusCode: number;
+ data: T;
+ errMsg: string;
+}
+export interface HttpDownloadResponse extends HttpResponse {
+ tempFilePath: string;
+}
+export interface HttpError {
+ config: HttpRequestConfig;
+ statusCode?: number;
+ cookies?: Array;
+ data?: any;
+ errMsg: string;
+ header?: AnyObject;
+}
+export interface HttpInterceptorManager {
+ use(
+ onFulfilled?: (config: V) => Promise | V,
+ onRejected?: (config: E) => Promise | E
+ ): void;
+ eject(id: number): void;
+}
+export abstract class HttpRequestAbstract {
+ constructor(config?: HttpRequestConfig);
+ config: HttpRequestConfig;
+ interceptors: {
+ request: HttpInterceptorManager;
+ response: HttpInterceptorManager;
+ }
+ middleware(config: HttpRequestConfig): HttpPromise;
+ request(config: HttpRequestConfig): HttpPromise;
+ get(url: string, config?: HttpRequestConfig): HttpPromise;
+ upload(url: string, config?: HttpRequestConfig): HttpPromise;
+ delete(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ head(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ post(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ put(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ connect(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ options(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ trace(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+
+ download(url: string, config?: HttpRequestConfig): Promise;
+
+ setConfig(onSend: (config: HttpRequestConfig) => HttpRequestConfig): void;
+}
+
+declare class HttpRequest extends HttpRequestAbstract { }
+export default HttpRequest;
diff --git a/uni_modules/uview-plus/libs/luch-request/index.js b/uni_modules/uview-plus/libs/luch-request/index.js
new file mode 100644
index 0000000..8fb2b44
--- /dev/null
+++ b/uni_modules/uview-plus/libs/luch-request/index.js
@@ -0,0 +1,3 @@
+import Request from './core/Request'
+
+export default Request
diff --git a/uni_modules/uview-plus/libs/luch-request/utils.js b/uni_modules/uview-plus/libs/luch-request/utils.js
new file mode 100644
index 0000000..847283d
--- /dev/null
+++ b/uni_modules/uview-plus/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-plus/libs/luch-request/utils/clone.js b/uni_modules/uview-plus/libs/luch-request/utils/clone.js
new file mode 100644
index 0000000..2fee704
--- /dev/null
+++ b/uni_modules/uview-plus/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-plus/libs/mixin/button.js b/uni_modules/uview-plus/libs/mixin/button.js
new file mode 100644
index 0000000..73b047e
--- /dev/null
+++ b/uni_modules/uview-plus/libs/mixin/button.js
@@ -0,0 +1,18 @@
+import { defineMixin } from '../vue'
+
+export const buttonMixin = defineMixin({
+ props: {
+ lang: String,
+ sessionFrom: String,
+ sendMessageTitle: String,
+ sendMessagePath: String,
+ sendMessageImg: String,
+ showMessageCard: Boolean,
+ appParameter: String,
+ formType: String,
+ openType: String
+ }
+})
+
+export default buttonMixin
+
diff --git a/uni_modules/uview-plus/libs/mixin/mixin.js b/uni_modules/uview-plus/libs/mixin/mixin.js
new file mode 100644
index 0000000..2040aee
--- /dev/null
+++ b/uni_modules/uview-plus/libs/mixin/mixin.js
@@ -0,0 +1,201 @@
+import { defineMixin } from '../vue'
+import { deepMerge, $parent, sleep } from '../function/index'
+import test from '../function/test'
+import route from '../util/route'
+// #ifdef APP-NVUE
+// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+const dom = uni.requireNativePlugin('dom')
+// #endif
+
+export const mixin = defineMixin({
+ // 定义每个组件都可能需要用到的外部样式以及类名
+ 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 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) {
+ // h5官方回应:发行h5会自动摇树优化,所有使用uni的地方,都会被直接转换成具体的API调用 https://ask.dcloud.net.cn/question/161523?notification_id-1201922__rf-false__item_id-226372
+ // 使用封装的 route 进行跳转(直接调用方法),不使用 uni 对象
+ route({ type: this.linkType, url })
+ // 执行类似uni.navigateTo的方法
+ // uni[this.linkType]({
+ // url
+ // })
+ }
+ },
+ navTo(url = '', linkType = 'navigateTo') {
+ route({ type: this.linkType, url })
+ },
+ // 查询节点信息
+ // 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸,为支付宝的bug(2020-07-21)
+ // 解决办法为在组件根部再套一个没有任何作用的view元素
+ $uGetRect(selector, all) {
+ return new Promise((resolve) => {
+ // #ifndef APP-NVUE
+ 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()
+ // #endif
+
+ // #ifdef APP-NVUE
+ sleep(30).then(() => {
+ let selectorNvue = selector.substring(1) // 去掉开头的#或者.
+ let selectorRef = this.$refs[selectorNvue]
+ if (!selectorRef) {
+ // console.log('不存在元素,请检查是否设置了ref属性' + selectorNvue + '。')
+ resolve({
+ with: 0,
+ height: 0,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ })
+ }
+ dom.getComponentRect(selectorRef, res => {
+ // console.log(res)
+ resolve(res.size)
+ })
+ })
+ // #endif
+ })
+ },
+ 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 = $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')
+ },
+ beforeUnmount() {
+ // 判断当前页面是否存在parent和chldren,一般在checkbox和checkbox-group父子联动的场景会有此情况
+ // 组件销毁时,移除子组件在父组件children数组中的实例,释放资源,避免数据混乱
+ if (this.parent && test.array(this.parent.children)) {
+ // 组件销毁时,移除父组件中的children数组中对应的实例
+ const childrenList = this.parent.children
+ childrenList.map((child, index) => {
+ // 如果相等,则移除
+ if (child === this) {
+ childrenList.splice(index, 1)
+ }
+ })
+ }
+ }
+})
+
+export default mixin
diff --git a/uni_modules/uview-plus/libs/mixin/mpMixin.js b/uni_modules/uview-plus/libs/mixin/mpMixin.js
new file mode 100644
index 0000000..ff224c4
--- /dev/null
+++ b/uni_modules/uview-plus/libs/mixin/mpMixin.js
@@ -0,0 +1,13 @@
+import { defineMixin } from '../vue'
+
+export const mpMixin = defineMixin({
+ // #ifdef MP-WEIXIN
+ // 将自定义节点设置成虚拟的,更加接近Vue组件的表现,能更好的使用flex属性
+ options: {
+ virtualHost: true
+ }
+ // #endif
+})
+
+export default mpMixin
+
diff --git a/uni_modules/uview-plus/libs/mixin/mpShare.js b/uni_modules/uview-plus/libs/mixin/mpShare.js
new file mode 100644
index 0000000..caf53c3
--- /dev/null
+++ b/uni_modules/uview-plus/libs/mixin/mpShare.js
@@ -0,0 +1,26 @@
+import { defineMixin } from '../vue'
+import { queryParams } from '../function/index'
+export const mpShare = defineMixin({
+ data() {
+ return {
+ mpShare: {
+ title: '', // 默认为小程序名称
+ path: '', // 默认为当前页面路径
+ imageUrl: '' // 默认为当前页面的截图
+ }
+ }
+ },
+ async onLoad(options) {
+ var pages = getCurrentPages();
+ var page = pages[pages.length - 1];
+ this.mpShare.path = page.route + queryParams(options);
+ },
+ onShareAppMessage(res) {
+ if (res.from === 'button') {// 来自页面内分享按钮
+ console.log(res.target)
+ }
+ return this.mpShare;
+ }
+})
+
+export default mpShare
diff --git a/uni_modules/uview-plus/libs/mixin/openType.js b/uni_modules/uview-plus/libs/mixin/openType.js
new file mode 100644
index 0000000..a3619d5
--- /dev/null
+++ b/uni_modules/uview-plus/libs/mixin/openType.js
@@ -0,0 +1,27 @@
+import { defineMixin } from '../vue'
+
+export const openType = defineMixin({
+ 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-plus/libs/mixin/style.js b/uni_modules/uview-plus/libs/mixin/style.js
new file mode 100644
index 0000000..2b34c76
--- /dev/null
+++ b/uni_modules/uview-plus/libs/mixin/style.js
@@ -0,0 +1,249 @@
+import { defineMixin } from '../vue'
+import { addStyle, deepMerge, addUnit, trim } from '../function/index'
+
+export const style = defineMixin({
+ 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 addStyleTmp = 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 deepMerge(style, addStyleTmp(this.customStyle))
+ }
+ },
+ methods: {
+ // 获取margin或者padding的单位,比如padding: 0 20转为padding: 0 20px
+ getUnit(unit = '') {
+ // 取出两端空格,分隔成数组,再对数组的每个元素添加单位,最后再合并成字符串
+ return trim(unit).split(' ').map((item) => addUnit(item)).join(' ')
+ }
+ }
+})
diff --git a/uni_modules/uview-plus/libs/mixin/touch.js b/uni_modules/uview-plus/libs/mixin/touch.js
new file mode 100644
index 0000000..9dfc2c6
--- /dev/null
+++ b/uni_modules/uview-plus/libs/mixin/touch.js
@@ -0,0 +1,61 @@
+import { defineMixin } from '../vue'
+
+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 const touchMixin = defineMixin({
+ 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-plus/libs/util/async-validator.js b/uni_modules/uview-plus/libs/util/async-validator.js
new file mode 100644
index 0000000..9e114df
--- /dev/null
+++ b/uni_modules/uview-plus/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-plus/libs/util/calendar.js b/uni_modules/uview-plus/libs/util/calendar.js
new file mode 100644
index 0000000..5afcd30
--- /dev/null
+++ b/uni_modules/uview-plus/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-plus/libs/util/emitter.js b/uni_modules/uview-plus/libs/util/emitter.js
new file mode 100644
index 0000000..1e64044
--- /dev/null
+++ b/uni_modules/uview-plus/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-plus/libs/util/gcanvas/bridge/bridge-weex.js b/uni_modules/uview-plus/libs/util/gcanvas/bridge/bridge-weex.js
new file mode 100644
index 0000000..d556b95
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/bridge/bridge-weex.js
@@ -0,0 +1,242 @@
+const isWeex = typeof WXEnvironment !== 'undefined';
+const isWeexIOS = isWeex && /ios/i.test(WXEnvironment.platform);
+const isWeexAndroid = isWeex && !isWeexIOS;
+
+import GLmethod from '../context-webgl/GLmethod';
+
+const GCanvasModule =
+ (typeof weex !== 'undefined' && weex.requireModule) ? (weex.requireModule('gcanvas')) :
+ (typeof __weex_require__ !== 'undefined') ? (__weex_require__('@weex-module/gcanvas')) : {};
+
+let isDebugging = false;
+
+let isComboDisabled = false;
+
+const logCommand = (function () {
+ const methodQuery = [];
+ Object.keys(GLmethod).forEach(key => {
+ methodQuery[GLmethod[key]] = key;
+ })
+ const queryMethod = (id) => {
+ return methodQuery[parseInt(id)] || 'NotFoundMethod';
+ }
+ const logCommand = (id, cmds) => {
+ const mId = cmds.split(',')[0];
+ console.log(`=== callNative - componentId:${id}; method: ${mName}; cmds: ${cmds}`);
+ }
+ return logCommand;
+})();
+
+function joinArray(arr, sep) {
+ let res = '';
+ for (let i = 0; i < arr.length; i++) {
+ if (i !== 0) {
+ res += sep;
+ }
+ res += arr[i];
+ }
+ return res;
+}
+
+const commandsCache = {}
+
+const GBridge = {
+
+ callEnable: (ref, configArray) => {
+
+ commandsCache[ref] = [];
+
+ return GCanvasModule.enable({
+ componentId: ref,
+ config: configArray
+ });
+ },
+
+ callEnableDebug: () => {
+ isDebugging = true;
+ },
+
+ callEnableDisableCombo: () => {
+ isComboDisabled = true;
+ },
+
+ callSetContextType: function (componentId, context_type) {
+ GCanvasModule.setContextType(context_type, componentId);
+ },
+
+ callReset: function(id){
+ GCanvasModule.resetComponent && canvasModule.resetComponent(componentId);
+ },
+
+ render: isWeexIOS ? function (componentId) {
+ return GCanvasModule.extendCallNative({
+ contextId: componentId,
+ type: 0x60000001
+ });
+ } : function (componentId) {
+ return callGCanvasLinkNative(componentId, 0x60000001, 'render');
+ },
+
+ render2d: isWeexIOS ? function (componentId, commands, callback) {
+
+ if (isDebugging) {
+ console.log('>>> >>> render2d ===');
+ console.log('>>> commands: ' + commands);
+ }
+
+ GCanvasModule.render([commands, callback?true:false], componentId, callback);
+
+ } : function (componentId, commands,callback) {
+
+ if (isDebugging) {
+ console.log('>>> >>> render2d ===');
+ console.log('>>> commands: ' + commands);
+ }
+
+ callGCanvasLinkNative(componentId, 0x20000001, commands);
+ if(callback){
+ callback();
+ }
+ },
+
+ callExtendCallNative: isWeexIOS ? function (componentId, cmdArgs) {
+
+ throw 'should not be here anymore ' + cmdArgs;
+
+ } : function (componentId, cmdArgs) {
+
+ throw 'should not be here anymore ' + cmdArgs;
+
+ },
+
+
+ flushNative: isWeexIOS ? function (componentId) {
+
+ const cmdArgs = joinArray(commandsCache[componentId], ';');
+ commandsCache[componentId] = [];
+
+ if (isDebugging) {
+ console.log('>>> >>> flush native ===');
+ console.log('>>> commands: ' + cmdArgs);
+ }
+
+ const result = GCanvasModule.extendCallNative({
+ "contextId": componentId,
+ "type": 0x60000000,
+ "args": cmdArgs
+ });
+
+ const res = result && result.result;
+
+ if (isDebugging) {
+ console.log('>>> result: ' + res);
+ }
+
+ return res;
+
+ } : function (componentId) {
+
+ const cmdArgs = joinArray(commandsCache[componentId], ';');
+ commandsCache[componentId] = [];
+
+ if (isDebugging) {
+ console.log('>>> >>> flush native ===');
+ console.log('>>> commands: ' + cmdArgs);
+ }
+
+ const result = callGCanvasLinkNative(componentId, 0x60000000, cmdArgs);
+
+ if (isDebugging) {
+ console.log('>>> result: ' + result);
+ }
+
+ return result;
+ },
+
+ callNative: function (componentId, cmdArgs, cache) {
+
+ if (isDebugging) {
+ logCommand(componentId, cmdArgs);
+ }
+
+ commandsCache[componentId].push(cmdArgs);
+
+ if (!cache || isComboDisabled) {
+ return GBridge.flushNative(componentId);
+ } else {
+ return undefined;
+ }
+ },
+
+ texImage2D(componentId, ...args) {
+ if (isWeexIOS) {
+ if (args.length === 6) {
+ const [target, level, internalformat, format, type, image] = args;
+ GBridge.callNative(
+ componentId,
+ GLmethod.texImage2D + ',' + 6 + ',' + target + ',' + level + ',' + internalformat + ',' + format + ',' + type + ',' + image.src
+ )
+ } else if (args.length === 9) {
+ const [target, level, internalformat, width, height, border, format, type, image] = args;
+ GBridge.callNative(
+ componentId,
+ GLmethod.texImage2D + ',' + 9 + ',' + target + ',' + level + ',' + internalformat + ',' + width + ',' + height + ',' + border + ',' +
+ + format + ',' + type + ',' + (image ? image.src : 0)
+ )
+ }
+ } else if (isWeexAndroid) {
+ if (args.length === 6) {
+ const [target, level, internalformat, format, type, image] = args;
+ GCanvasModule.texImage2D(componentId, target, level, internalformat, format, type, image.src);
+ } else if (args.length === 9) {
+ const [target, level, internalformat, width, height, border, format, type, image] = args;
+ GCanvasModule.texImage2D(componentId, target, level, internalformat, width, height, border, format, type, (image ? image.src : 0));
+ }
+ }
+ },
+
+ texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image) {
+ if (isWeexIOS) {
+ if (arguments.length === 8) {
+ GBridge.callNative(
+ componentId,
+ GLmethod.texSubImage2D + ',' + 6 + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset, + ',' + format + ',' + type + ',' + image.src
+ )
+ }
+ } else if (isWeexAndroid) {
+ GCanvasModule.texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image.src);
+ }
+ },
+
+ bindImageTexture(componentId, src, imageId) {
+ GCanvasModule.bindImageTexture([src, imageId], componentId);
+ },
+
+ perloadImage([url, id], callback) {
+ console.log('********************asda**********')
+ GCanvasModule.preLoadImage([url, id], function (image) {
+ console.log('********************asda2**********')
+ image.url = url;
+ image.id = id;
+ callback(image);
+ });
+ },
+
+ measureText(text, fontStyle, componentId) {
+ return GCanvasModule.measureText([text, fontStyle], componentId);
+ },
+
+ getImageData (componentId, x, y, w, h, callback) {
+ GCanvasModule.getImageData([x, y,w,h],componentId,callback);
+ },
+
+ putImageData (componentId, data, x, y, w, h, callback) {
+ GCanvasModule.putImageData([x, y,w,h,data],componentId,callback);
+ },
+
+ toTempFilePath(componentId, x, y, width, height, destWidth, destHeight, fileType, quality, callback){
+ GCanvasModule.toTempFilePath([x, y, width,height, destWidth, destHeight, fileType, quality], componentId, callback);
+ }
+}
+
+export default GBridge;
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-2d/FillStyleLinearGradient.js b/uni_modules/uview-plus/libs/util/gcanvas/context-2d/FillStyleLinearGradient.js
new file mode 100644
index 0000000..3e7f03a
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-2d/FillStyleLinearGradient.js
@@ -0,0 +1,18 @@
+class FillStyleLinearGradient {
+
+ constructor(x0, y0, x1, y1) {
+ this._start_pos = { _x: x0, _y: y0 };
+ this._end_pos = { _x: x1, _y: y1 };
+ this._stop_count = 0;
+ this._stops = [0, 0, 0, 0, 0];
+ }
+
+ addColorStop = function (pos, color) {
+ if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) {
+ this._stops[this._stop_count] = { _pos: pos, _color: color };
+ this._stop_count++;
+ }
+ }
+}
+
+export default FillStyleLinearGradient;
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-2d/FillStylePattern.js b/uni_modules/uview-plus/libs/util/gcanvas/context-2d/FillStylePattern.js
new file mode 100644
index 0000000..6e4f646
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-2d/FillStylePattern.js
@@ -0,0 +1,8 @@
+class FillStylePattern {
+ constructor(img, pattern) {
+ this._style = pattern;
+ this._img = img;
+ }
+}
+
+export default FillStylePattern;
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-2d/FillStyleRadialGradient.js b/uni_modules/uview-plus/libs/util/gcanvas/context-2d/FillStyleRadialGradient.js
new file mode 100644
index 0000000..7790596
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-2d/FillStyleRadialGradient.js
@@ -0,0 +1,17 @@
+class FillStyleRadialGradient {
+ constructor(x0, y0, r0, x1, y1, r1) {
+ this._start_pos = { _x: x0, _y: y0, _r: r0 };
+ this._end_pos = { _x: x1, _y: y1, _r: r1 };
+ this._stop_count = 0;
+ this._stops = [0, 0, 0, 0, 0];
+ }
+
+ addColorStop(pos, color) {
+ if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) {
+ this._stops[this._stop_count] = { _pos: pos, _color: color };
+ this._stop_count++;
+ }
+ }
+}
+
+export default FillStyleRadialGradient;
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-2d/RenderingContext.js b/uni_modules/uview-plus/libs/util/gcanvas/context-2d/RenderingContext.js
new file mode 100644
index 0000000..e6b8f48
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-2d/RenderingContext.js
@@ -0,0 +1,666 @@
+import FillStylePattern from './FillStylePattern';
+import FillStyleLinearGradient from './FillStyleLinearGradient';
+import FillStyleRadialGradient from './FillStyleRadialGradient';
+import GImage from '../env/image.js';
+import {
+ ArrayBufferToBase64,
+ Base64ToUint8ClampedArray
+} from '../env/tool.js';
+
+export default class CanvasRenderingContext2D {
+
+ _drawCommands = '';
+
+ _globalAlpha = 1.0;
+
+ _fillStyle = 'rgb(0,0,0)';
+ _strokeStyle = 'rgb(0,0,0)';
+
+ _lineWidth = 1;
+ _lineCap = 'butt';
+ _lineJoin = 'miter';
+
+ _miterLimit = 10;
+
+ _globalCompositeOperation = 'source-over';
+
+ _textAlign = 'start';
+ _textBaseline = 'alphabetic';
+
+ _font = '10px sans-serif';
+
+ _savedGlobalAlpha = [];
+
+ timer = null;
+ componentId = null;
+
+ _notCommitDrawImageCache = [];
+ _needRedrawImageCache = [];
+ _redrawCommands = '';
+ _autoSaveContext = true;
+ // _imageMap = new GHashMap();
+ // _textureMap = new GHashMap();
+
+ constructor() {
+ this.className = 'CanvasRenderingContext2D';
+ //this.save()
+ }
+
+ setFillStyle(value) {
+ this.fillStyle = value;
+ }
+
+ set fillStyle(value) {
+ this._fillStyle = value;
+
+ if (typeof(value) == 'string') {
+ this._drawCommands = this._drawCommands.concat("F" + value + ";");
+ } else if (value instanceof FillStylePattern) {
+ const image = value._img;
+ if (!image.complete) {
+ image.onload = () => {
+ var index = this._needRedrawImageCache.indexOf(image);
+ if (index > -1) {
+ this._needRedrawImageCache.splice(index, 1);
+ CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ this._redrawflush(true);
+ }
+ }
+ this._notCommitDrawImageCache.push(image);
+ } else {
+ CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ }
+
+ //CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
+ } else if (value instanceof FillStyleLinearGradient) {
+ var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
+ value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
+ value._stop_count;
+ for (var i = 0; i < value._stop_count; ++i) {
+ command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
+ }
+ this._drawCommands = this._drawCommands.concat(command + ";");
+ } else if (value instanceof FillStyleRadialGradient) {
+ var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
+ .toFixed(2) + "," +
+ value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + value._end_pos._r.toFixed(2) + "," +
+ value._stop_count;
+ for (var i = 0; i < value._stop_count; ++i) {
+ command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
+ }
+ this._drawCommands = this._drawCommands.concat(command + ";");
+ }
+ }
+
+ get fillStyle() {
+ return this._fillStyle;
+ }
+
+ get globalAlpha() {
+ return this._globalAlpha;
+ }
+
+ setGlobalAlpha(value) {
+ this.globalAlpha = value;
+ }
+
+ set globalAlpha(value) {
+ this._globalAlpha = value;
+ this._drawCommands = this._drawCommands.concat("a" + value.toFixed(2) + ";");
+ }
+
+
+ get strokeStyle() {
+ return this._strokeStyle;
+ }
+
+ setStrokeStyle(value) {
+ this.strokeStyle = value;
+ }
+
+ set strokeStyle(value) {
+
+ this._strokeStyle = value;
+
+ if (typeof(value) == 'string') {
+ this._drawCommands = this._drawCommands.concat("S" + value + ";");
+ } else if (value instanceof FillStylePattern) {
+ CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
+ } else if (value instanceof FillStyleLinearGradient) {
+ var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
+ value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
+ value._stop_count;
+
+ for (var i = 0; i < value._stop_count; ++i) {
+ command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
+ }
+ this._drawCommands = this._drawCommands.concat(command + ";");
+ } else if (value instanceof FillStyleRadialGradient) {
+ var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
+ .toFixed(2) + "," +
+ value._end_pos._x.toFixed(2) + "," + value._end_pos._y + ",".toFixed(2) + value._end_pos._r.toFixed(2) + "," +
+ value._stop_count;
+
+ for (var i = 0; i < value._stop_count; ++i) {
+ command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
+ }
+ this._drawCommands = this._drawCommands.concat(command + ";");
+ }
+ }
+
+ get lineWidth() {
+ return this._lineWidth;
+ }
+
+ setLineWidth(value) {
+ this.lineWidth = value;
+ }
+
+ set lineWidth(value) {
+ this._lineWidth = value;
+ this._drawCommands = this._drawCommands.concat("W" + value + ";");
+ }
+
+ get lineCap() {
+ return this._lineCap;
+ }
+
+ setLineCap(value) {
+ this.lineCap = value;
+ }
+
+ set lineCap(value) {
+ this._lineCap = value;
+ this._drawCommands = this._drawCommands.concat("C" + value + ";");
+ }
+
+ get lineJoin() {
+ return this._lineJoin;
+ }
+
+ setLineJoin(value) {
+ this.lineJoin = value
+ }
+
+ set lineJoin(value) {
+ this._lineJoin = value;
+ this._drawCommands = this._drawCommands.concat("J" + value + ";");
+ }
+
+ get miterLimit() {
+ return this._miterLimit;
+ }
+
+ setMiterLimit(value) {
+ this.miterLimit = value
+ }
+
+ set miterLimit(value) {
+ this._miterLimit = value;
+ this._drawCommands = this._drawCommands.concat("M" + value + ";");
+ }
+
+ get globalCompositeOperation() {
+ return this._globalCompositeOperation;
+ }
+
+ set globalCompositeOperation(value) {
+
+ this._globalCompositeOperation = value;
+ let mode = 0;
+ switch (value) {
+ case "source-over":
+ mode = 0;
+ break;
+ case "source-atop":
+ mode = 5;
+ break;
+ case "source-in":
+ mode = 0;
+ break;
+ case "source-out":
+ mode = 2;
+ break;
+ case "destination-over":
+ mode = 4;
+ break;
+ case "destination-atop":
+ mode = 4;
+ break;
+ case "destination-in":
+ mode = 4;
+ break;
+ case "destination-out":
+ mode = 3;
+ break;
+ case "lighter":
+ mode = 1;
+ break;
+ case "copy":
+ mode = 2;
+ break;
+ case "xor":
+ mode = 6;
+ break;
+ default:
+ mode = 0;
+ }
+
+ this._drawCommands = this._drawCommands.concat("B" + mode + ";");
+ }
+
+ get textAlign() {
+ return this._textAlign;
+ }
+
+ setTextAlign(value) {
+ this.textAlign = value
+ }
+
+ set textAlign(value) {
+
+ this._textAlign = value;
+ let Align = 0;
+ switch (value) {
+ case "start":
+ Align = 0;
+ break;
+ case "end":
+ Align = 1;
+ break;
+ case "left":
+ Align = 2;
+ break;
+ case "center":
+ Align = 3;
+ break;
+ case "right":
+ Align = 4;
+ break;
+ default:
+ Align = 0;
+ }
+
+ this._drawCommands = this._drawCommands.concat("A" + Align + ";");
+ }
+
+ get textBaseline() {
+ return this._textBaseline;
+ }
+
+ setTextBaseline(value) {
+ this.textBaseline = value
+ }
+
+ set textBaseline(value) {
+ this._textBaseline = value;
+ let baseline = 0;
+ switch (value) {
+ case "alphabetic":
+ baseline = 0;
+ break;
+ case "middle":
+ baseline = 1;
+ break;
+ case "top":
+ baseline = 2;
+ break;
+ case "hanging":
+ baseline = 3;
+ break;
+ case "bottom":
+ baseline = 4;
+ break;
+ case "ideographic":
+ baseline = 5;
+ break;
+ default:
+ baseline = 0;
+ break;
+ }
+
+ this._drawCommands = this._drawCommands.concat("E" + baseline + ";");
+ }
+
+ get font() {
+ return this._font;
+ }
+
+ setFontSize(size) {
+ var str = this._font;
+ var strs = str.trim().split(/\s+/);
+ for (var i = 0; i < strs.length; i++) {
+ var values = ["normal", "italic", "oblique", "normal", "small-caps", "normal", "bold",
+ "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900",
+ "normal", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
+ "semi-expanded", "expanded", "extra-expanded", "ultra-expanded"
+ ];
+
+ if (-1 == values.indexOf(strs[i].trim())) {
+ if (typeof size === 'string') {
+ strs[i] = size;
+ } else if (typeof size === 'number') {
+ strs[i] = String(size) + 'px';
+ }
+ break;
+ }
+ }
+ this.font = strs.join(" ");
+ }
+
+ set font(value) {
+ this._font = value;
+ this._drawCommands = this._drawCommands.concat("j" + value + ";");
+ }
+
+ setTransform(a, b, c, d, tx, ty) {
+ this._drawCommands = this._drawCommands.concat("t" +
+ (a === 1 ? "1" : a.toFixed(2)) + "," +
+ (b === 0 ? "0" : b.toFixed(2)) + "," +
+ (c === 0 ? "0" : c.toFixed(2)) + "," +
+ (d === 1 ? "1" : d.toFixed(2)) + "," + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
+ }
+
+ transform(a, b, c, d, tx, ty) {
+ this._drawCommands = this._drawCommands.concat("f" +
+ (a === 1 ? "1" : a.toFixed(2)) + "," +
+ (b === 0 ? "0" : b.toFixed(2)) + "," +
+ (c === 0 ? "0" : c.toFixed(2)) + "," +
+ (d === 1 ? "1" : d.toFixed(2)) + "," + tx + "," + ty + ";");
+ }
+
+ resetTransform() {
+ this._drawCommands = this._drawCommands.concat("m;");
+ }
+
+ scale(a, d) {
+ this._drawCommands = this._drawCommands.concat("k" + a.toFixed(2) + "," +
+ d.toFixed(2) + ";");
+ }
+
+ rotate(angle) {
+ this._drawCommands = this._drawCommands
+ .concat("r" + angle.toFixed(6) + ";");
+ }
+
+ translate(tx, ty) {
+ this._drawCommands = this._drawCommands.concat("l" + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
+ }
+
+ save() {
+ this._savedGlobalAlpha.push(this._globalAlpha);
+ this._drawCommands = this._drawCommands.concat("v;");
+ }
+
+ restore() {
+ this._drawCommands = this._drawCommands.concat("e;");
+ this._globalAlpha = this._savedGlobalAlpha.pop();
+ }
+
+ createPattern(img, pattern) {
+ if (typeof img === 'string') {
+ var imgObj = new GImage();
+ imgObj.src = img;
+ img = imgObj;
+ }
+ return new FillStylePattern(img, pattern);
+ }
+
+ createLinearGradient(x0, y0, x1, y1) {
+ return new FillStyleLinearGradient(x0, y0, x1, y1);
+ }
+
+ createRadialGradient = function(x0, y0, r0, x1, y1, r1) {
+ return new FillStyleRadialGradient(x0, y0, r0, x1, y1, r1);
+ };
+
+ createCircularGradient = function(x0, y0, r0) {
+ return new FillStyleRadialGradient(x0, y0, 0, x0, y0, r0);
+ };
+
+ strokeRect(x, y, w, h) {
+ this._drawCommands = this._drawCommands.concat("s" + x + "," + y + "," + w + "," + h + ";");
+ }
+
+
+ clearRect(x, y, w, h) {
+ this._drawCommands = this._drawCommands.concat("c" + x + "," + y + "," + w +
+ "," + h + ";");
+ }
+
+ clip() {
+ this._drawCommands = this._drawCommands.concat("p;");
+ }
+
+ resetClip() {
+ this._drawCommands = this._drawCommands.concat("q;");
+ }
+
+ closePath() {
+ this._drawCommands = this._drawCommands.concat("o;");
+ }
+
+ moveTo(x, y) {
+ this._drawCommands = this._drawCommands.concat("g" + x.toFixed(2) + "," + y.toFixed(2) + ";");
+ }
+
+ lineTo(x, y) {
+ this._drawCommands = this._drawCommands.concat("i" + x.toFixed(2) + "," + y.toFixed(2) + ";");
+ }
+
+ quadraticCurveTo = function(cpx, cpy, x, y) {
+ this._drawCommands = this._drawCommands.concat("u" + cpx + "," + cpy + "," + x + "," + y + ";");
+ }
+
+ bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, ) {
+ this._drawCommands = this._drawCommands.concat(
+ "z" + cp1x.toFixed(2) + "," + cp1y.toFixed(2) + "," + cp2x.toFixed(2) + "," + cp2y.toFixed(2) + "," +
+ x.toFixed(2) + "," + y.toFixed(2) + ";");
+ }
+
+ arcTo(x1, y1, x2, y2, radius) {
+ this._drawCommands = this._drawCommands.concat("h" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + radius + ";");
+ }
+
+ beginPath() {
+ this._drawCommands = this._drawCommands.concat("b;");
+ }
+
+
+ fillRect(x, y, w, h) {
+ this._drawCommands = this._drawCommands.concat("n" + x + "," + y + "," + w +
+ "," + h + ";");
+ }
+
+ rect(x, y, w, h) {
+ this._drawCommands = this._drawCommands.concat("w" + x + "," + y + "," + w + "," + h + ";");
+ }
+
+ fill() {
+ this._drawCommands = this._drawCommands.concat("L;");
+ }
+
+ stroke(path) {
+ this._drawCommands = this._drawCommands.concat("x;");
+ }
+
+ arc(x, y, radius, startAngle, endAngle, anticlockwise) {
+
+ let ianticlockwise = 0;
+ if (anticlockwise) {
+ ianticlockwise = 1;
+ }
+
+ this._drawCommands = this._drawCommands.concat(
+ "y" + x.toFixed(2) + "," + y.toFixed(2) + "," +
+ radius.toFixed(2) + "," + startAngle + "," + endAngle + "," + ianticlockwise +
+ ";"
+ );
+ }
+
+ fillText(text, x, y) {
+ let tmptext = text.replace(/!/g, "!!");
+ tmptext = tmptext.replace(/,/g, "!,");
+ tmptext = tmptext.replace(/;/g, "!;");
+ this._drawCommands = this._drawCommands.concat("T" + tmptext + "," + x + "," + y + ",0.0;");
+ }
+
+ strokeText = function(text, x, y) {
+ let tmptext = text.replace(/!/g, "!!");
+ tmptext = tmptext.replace(/,/g, "!,");
+ tmptext = tmptext.replace(/;/g, "!;");
+ this._drawCommands = this._drawCommands.concat("U" + tmptext + "," + x + "," + y + ",0.0;");
+ }
+
+ measureText(text) {
+ return CanvasRenderingContext2D.GBridge.measureText(text, this.font, this.componentId);
+ }
+
+ isPointInPath = function(x, y) {
+ throw new Error('GCanvas not supported yet');
+ }
+
+ drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
+ if (typeof image === 'string') {
+ var imgObj = new GImage();
+ imgObj.src = image;
+ image = imgObj;
+ }
+ if (image instanceof GImage) {
+ if (!image.complete) {
+ imgObj.onload = () => {
+ var index = this._needRedrawImageCache.indexOf(image);
+ if (index > -1) {
+ this._needRedrawImageCache.splice(index, 1);
+ CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ this._redrawflush(true);
+ }
+ }
+ this._notCommitDrawImageCache.push(image);
+ } else {
+ CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ }
+ var srcArgs = [image, sx, sy, sw, sh, dx, dy, dw, dh];
+ var args = [];
+ for (var arg in srcArgs) {
+ if (typeof(srcArgs[arg]) != 'undefined') {
+ args.push(srcArgs[arg]);
+ }
+ }
+ this.__drawImage.apply(this, args);
+ //this.__drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh);
+ }
+ }
+
+ __drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
+ const numArgs = arguments.length;
+
+ function drawImageCommands() {
+
+ if (numArgs === 3) {
+ const x = parseFloat(sx) || 0.0;
+ const y = parseFloat(sy) || 0.0;
+
+ return ("d" + image._id + ",0,0," +
+ image.width + "," + image.height + "," +
+ x + "," + y + "," + image.width + "," + image.height + ";");
+ } else if (numArgs === 5) {
+ const x = parseFloat(sx) || 0.0;
+ const y = parseFloat(sy) || 0.0;
+ const width = parseInt(sw) || image.width;
+ const height = parseInt(sh) || image.height;
+
+ return ("d" + image._id + ",0,0," +
+ image.width + "," + image.height + "," +
+ x + "," + y + "," + width + "," + height + ";");
+ } else if (numArgs === 9) {
+ sx = parseFloat(sx) || 0.0;
+ sy = parseFloat(sy) || 0.0;
+ sw = parseInt(sw) || image.width;
+ sh = parseInt(sh) || image.height;
+ dx = parseFloat(dx) || 0.0;
+ dy = parseFloat(dy) || 0.0;
+ dw = parseInt(dw) || image.width;
+ dh = parseInt(dh) || image.height;
+
+ return ("d" + image._id + "," +
+ sx + "," + sy + "," + sw + "," + sh + "," +
+ dx + "," + dy + "," + dw + "," + dh + ";");
+ }
+ }
+ this._drawCommands += drawImageCommands();
+ }
+
+ _flush(reserve, callback) {
+ const commands = this._drawCommands;
+ this._drawCommands = '';
+ CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
+ this._needRender = false;
+ }
+
+ _redrawflush(reserve, callback) {
+ const commands = this._redrawCommands;
+ CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
+ if (this._needRedrawImageCache.length == 0) {
+ this._redrawCommands = '';
+ }
+ }
+
+ draw(reserve, callback) {
+ if (!reserve) {
+ this._globalAlpha = this._savedGlobalAlpha.pop();
+ this._savedGlobalAlpha.push(this._globalAlpha);
+ this._redrawCommands = this._drawCommands;
+ this._needRedrawImageCache = this._notCommitDrawImageCache;
+ if (this._autoSaveContext) {
+ this._drawCommands = ("v;" + this._drawCommands);
+ this._autoSaveContext = false;
+ } else {
+ this._drawCommands = ("e;X;v;" + this._drawCommands);
+ }
+ } else {
+ this._needRedrawImageCache = this._needRedrawImageCache.concat(this._notCommitDrawImageCache);
+ this._redrawCommands += this._drawCommands;
+ if (this._autoSaveContext) {
+ this._drawCommands = ("v;" + this._drawCommands);
+ this._autoSaveContext = false;
+ }
+ }
+ this._notCommitDrawImageCache = [];
+ if (this._flush) {
+ this._flush(reserve, callback);
+ }
+ }
+
+ getImageData(x, y, w, h, callback) {
+ CanvasRenderingContext2D.GBridge.getImageData(this.componentId, x, y, w, h, function(res) {
+ res.data = Base64ToUint8ClampedArray(res.data);
+ if (typeof(callback) == 'function') {
+ callback(res);
+ }
+ });
+ }
+
+ putImageData(data, x, y, w, h, callback) {
+ if (data instanceof Uint8ClampedArray) {
+ data = ArrayBufferToBase64(data);
+ CanvasRenderingContext2D.GBridge.putImageData(this.componentId, data, x, y, w, h, function(res) {
+ if (typeof(callback) == 'function') {
+ callback(res);
+ }
+ });
+ }
+ }
+
+ toTempFilePath(x, y, width, height, destWidth, destHeight, fileType, quality, callback) {
+ CanvasRenderingContext2D.GBridge.toTempFilePath(this.componentId, x, y, width, height, destWidth, destHeight,
+ fileType, quality,
+ function(res) {
+ if (typeof(callback) == 'function') {
+ callback(res);
+ }
+ });
+ }
+}
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/ActiveInfo.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/ActiveInfo.js
new file mode 100644
index 0000000..b495129
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/ActiveInfo.js
@@ -0,0 +1,11 @@
+export default class WebGLActiveInfo {
+ className = 'WebGLActiveInfo';
+
+ constructor({
+ type, name, size
+ }) {
+ this.type = type;
+ this.name = name;
+ this.size = size;
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Buffer.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Buffer.js
new file mode 100644
index 0000000..4800f67
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Buffer.js
@@ -0,0 +1,21 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLBuffer';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLBuffer {
+ className = name;
+
+ constructor(id) {
+ this.id = id;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Framebuffer.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Framebuffer.js
new file mode 100644
index 0000000..28b46d3
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Framebuffer.js
@@ -0,0 +1,21 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLFrameBuffer';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLFramebuffer {
+ className = name;
+
+ constructor(id) {
+ this.id = id;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/GLenum.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/GLenum.js
new file mode 100644
index 0000000..ac5544d
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/GLenum.js
@@ -0,0 +1,298 @@
+export default {
+ "DEPTH_BUFFER_BIT": 256,
+ "STENCIL_BUFFER_BIT": 1024,
+ "COLOR_BUFFER_BIT": 16384,
+ "POINTS": 0,
+ "LINES": 1,
+ "LINE_LOOP": 2,
+ "LINE_STRIP": 3,
+ "TRIANGLES": 4,
+ "TRIANGLE_STRIP": 5,
+ "TRIANGLE_FAN": 6,
+ "ZERO": 0,
+ "ONE": 1,
+ "SRC_COLOR": 768,
+ "ONE_MINUS_SRC_COLOR": 769,
+ "SRC_ALPHA": 770,
+ "ONE_MINUS_SRC_ALPHA": 771,
+ "DST_ALPHA": 772,
+ "ONE_MINUS_DST_ALPHA": 773,
+ "DST_COLOR": 774,
+ "ONE_MINUS_DST_COLOR": 775,
+ "SRC_ALPHA_SATURATE": 776,
+ "FUNC_ADD": 32774,
+ "BLEND_EQUATION": 32777,
+ "BLEND_EQUATION_RGB": 32777,
+ "BLEND_EQUATION_ALPHA": 34877,
+ "FUNC_SUBTRACT": 32778,
+ "FUNC_REVERSE_SUBTRACT": 32779,
+ "BLEND_DST_RGB": 32968,
+ "BLEND_SRC_RGB": 32969,
+ "BLEND_DST_ALPHA": 32970,
+ "BLEND_SRC_ALPHA": 32971,
+ "CONSTANT_COLOR": 32769,
+ "ONE_MINUS_CONSTANT_COLOR": 32770,
+ "CONSTANT_ALPHA": 32771,
+ "ONE_MINUS_CONSTANT_ALPHA": 32772,
+ "BLEND_COLOR": 32773,
+ "ARRAY_BUFFER": 34962,
+ "ELEMENT_ARRAY_BUFFER": 34963,
+ "ARRAY_BUFFER_BINDING": 34964,
+ "ELEMENT_ARRAY_BUFFER_BINDING": 34965,
+ "STREAM_DRAW": 35040,
+ "STATIC_DRAW": 35044,
+ "DYNAMIC_DRAW": 35048,
+ "BUFFER_SIZE": 34660,
+ "BUFFER_USAGE": 34661,
+ "CURRENT_VERTEX_ATTRIB": 34342,
+ "FRONT": 1028,
+ "BACK": 1029,
+ "FRONT_AND_BACK": 1032,
+ "TEXTURE_2D": 3553,
+ "CULL_FACE": 2884,
+ "BLEND": 3042,
+ "DITHER": 3024,
+ "STENCIL_TEST": 2960,
+ "DEPTH_TEST": 2929,
+ "SCISSOR_TEST": 3089,
+ "POLYGON_OFFSET_FILL": 32823,
+ "SAMPLE_ALPHA_TO_COVERAGE": 32926,
+ "SAMPLE_COVERAGE": 32928,
+ "NO_ERROR": 0,
+ "INVALID_ENUM": 1280,
+ "INVALID_VALUE": 1281,
+ "INVALID_OPERATION": 1282,
+ "OUT_OF_MEMORY": 1285,
+ "CW": 2304,
+ "CCW": 2305,
+ "LINE_WIDTH": 2849,
+ "ALIASED_POINT_SIZE_RANGE": 33901,
+ "ALIASED_LINE_WIDTH_RANGE": 33902,
+ "CULL_FACE_MODE": 2885,
+ "FRONT_FACE": 2886,
+ "DEPTH_RANGE": 2928,
+ "DEPTH_WRITEMASK": 2930,
+ "DEPTH_CLEAR_VALUE": 2931,
+ "DEPTH_FUNC": 2932,
+ "STENCIL_CLEAR_VALUE": 2961,
+ "STENCIL_FUNC": 2962,
+ "STENCIL_FAIL": 2964,
+ "STENCIL_PASS_DEPTH_FAIL": 2965,
+ "STENCIL_PASS_DEPTH_PASS": 2966,
+ "STENCIL_REF": 2967,
+ "STENCIL_VALUE_MASK": 2963,
+ "STENCIL_WRITEMASK": 2968,
+ "STENCIL_BACK_FUNC": 34816,
+ "STENCIL_BACK_FAIL": 34817,
+ "STENCIL_BACK_PASS_DEPTH_FAIL": 34818,
+ "STENCIL_BACK_PASS_DEPTH_PASS": 34819,
+ "STENCIL_BACK_REF": 36003,
+ "STENCIL_BACK_VALUE_MASK": 36004,
+ "STENCIL_BACK_WRITEMASK": 36005,
+ "VIEWPORT": 2978,
+ "SCISSOR_BOX": 3088,
+ "COLOR_CLEAR_VALUE": 3106,
+ "COLOR_WRITEMASK": 3107,
+ "UNPACK_ALIGNMENT": 3317,
+ "PACK_ALIGNMENT": 3333,
+ "MAX_TEXTURE_SIZE": 3379,
+ "MAX_VIEWPORT_DIMS": 3386,
+ "SUBPIXEL_BITS": 3408,
+ "RED_BITS": 3410,
+ "GREEN_BITS": 3411,
+ "BLUE_BITS": 3412,
+ "ALPHA_BITS": 3413,
+ "DEPTH_BITS": 3414,
+ "STENCIL_BITS": 3415,
+ "POLYGON_OFFSET_UNITS": 10752,
+ "POLYGON_OFFSET_FACTOR": 32824,
+ "TEXTURE_BINDING_2D": 32873,
+ "SAMPLE_BUFFERS": 32936,
+ "SAMPLES": 32937,
+ "SAMPLE_COVERAGE_VALUE": 32938,
+ "SAMPLE_COVERAGE_INVERT": 32939,
+ "COMPRESSED_TEXTURE_FORMATS": 34467,
+ "DONT_CARE": 4352,
+ "FASTEST": 4353,
+ "NICEST": 4354,
+ "GENERATE_MIPMAP_HINT": 33170,
+ "BYTE": 5120,
+ "UNSIGNED_BYTE": 5121,
+ "SHORT": 5122,
+ "UNSIGNED_SHORT": 5123,
+ "INT": 5124,
+ "UNSIGNED_INT": 5125,
+ "FLOAT": 5126,
+ "DEPTH_COMPONENT": 6402,
+ "ALPHA": 6406,
+ "RGB": 6407,
+ "RGBA": 6408,
+ "LUMINANCE": 6409,
+ "LUMINANCE_ALPHA": 6410,
+ "UNSIGNED_SHORT_4_4_4_4": 32819,
+ "UNSIGNED_SHORT_5_5_5_1": 32820,
+ "UNSIGNED_SHORT_5_6_5": 33635,
+ "FRAGMENT_SHADER": 35632,
+ "VERTEX_SHADER": 35633,
+ "MAX_VERTEX_ATTRIBS": 34921,
+ "MAX_VERTEX_UNIFORM_VECTORS": 36347,
+ "MAX_VARYING_VECTORS": 36348,
+ "MAX_COMBINED_TEXTURE_IMAGE_UNITS": 35661,
+ "MAX_VERTEX_TEXTURE_IMAGE_UNITS": 35660,
+ "MAX_TEXTURE_IMAGE_UNITS": 34930,
+ "MAX_FRAGMENT_UNIFORM_VECTORS": 36349,
+ "SHADER_TYPE": 35663,
+ "DELETE_STATUS": 35712,
+ "LINK_STATUS": 35714,
+ "VALIDATE_STATUS": 35715,
+ "ATTACHED_SHADERS": 35717,
+ "ACTIVE_UNIFORMS": 35718,
+ "ACTIVE_ATTRIBUTES": 35721,
+ "SHADING_LANGUAGE_VERSION": 35724,
+ "CURRENT_PROGRAM": 35725,
+ "NEVER": 512,
+ "LESS": 513,
+ "EQUAL": 514,
+ "LEQUAL": 515,
+ "GREATER": 516,
+ "NOTEQUAL": 517,
+ "GEQUAL": 518,
+ "ALWAYS": 519,
+ "KEEP": 7680,
+ "REPLACE": 7681,
+ "INCR": 7682,
+ "DECR": 7683,
+ "INVERT": 5386,
+ "INCR_WRAP": 34055,
+ "DECR_WRAP": 34056,
+ "VENDOR": 7936,
+ "RENDERER": 7937,
+ "VERSION": 7938,
+ "NEAREST": 9728,
+ "LINEAR": 9729,
+ "NEAREST_MIPMAP_NEAREST": 9984,
+ "LINEAR_MIPMAP_NEAREST": 9985,
+ "NEAREST_MIPMAP_LINEAR": 9986,
+ "LINEAR_MIPMAP_LINEAR": 9987,
+ "TEXTURE_MAG_FILTER": 10240,
+ "TEXTURE_MIN_FILTER": 10241,
+ "TEXTURE_WRAP_S": 10242,
+ "TEXTURE_WRAP_T": 10243,
+ "TEXTURE": 5890,
+ "TEXTURE_CUBE_MAP": 34067,
+ "TEXTURE_BINDING_CUBE_MAP": 34068,
+ "TEXTURE_CUBE_MAP_POSITIVE_X": 34069,
+ "TEXTURE_CUBE_MAP_NEGATIVE_X": 34070,
+ "TEXTURE_CUBE_MAP_POSITIVE_Y": 34071,
+ "TEXTURE_CUBE_MAP_NEGATIVE_Y": 34072,
+ "TEXTURE_CUBE_MAP_POSITIVE_Z": 34073,
+ "TEXTURE_CUBE_MAP_NEGATIVE_Z": 34074,
+ "MAX_CUBE_MAP_TEXTURE_SIZE": 34076,
+ "TEXTURE0": 33984,
+ "TEXTURE1": 33985,
+ "TEXTURE2": 33986,
+ "TEXTURE3": 33987,
+ "TEXTURE4": 33988,
+ "TEXTURE5": 33989,
+ "TEXTURE6": 33990,
+ "TEXTURE7": 33991,
+ "TEXTURE8": 33992,
+ "TEXTURE9": 33993,
+ "TEXTURE10": 33994,
+ "TEXTURE11": 33995,
+ "TEXTURE12": 33996,
+ "TEXTURE13": 33997,
+ "TEXTURE14": 33998,
+ "TEXTURE15": 33999,
+ "TEXTURE16": 34000,
+ "TEXTURE17": 34001,
+ "TEXTURE18": 34002,
+ "TEXTURE19": 34003,
+ "TEXTURE20": 34004,
+ "TEXTURE21": 34005,
+ "TEXTURE22": 34006,
+ "TEXTURE23": 34007,
+ "TEXTURE24": 34008,
+ "TEXTURE25": 34009,
+ "TEXTURE26": 34010,
+ "TEXTURE27": 34011,
+ "TEXTURE28": 34012,
+ "TEXTURE29": 34013,
+ "TEXTURE30": 34014,
+ "TEXTURE31": 34015,
+ "ACTIVE_TEXTURE": 34016,
+ "REPEAT": 10497,
+ "CLAMP_TO_EDGE": 33071,
+ "MIRRORED_REPEAT": 33648,
+ "FLOAT_VEC2": 35664,
+ "FLOAT_VEC3": 35665,
+ "FLOAT_VEC4": 35666,
+ "INT_VEC2": 35667,
+ "INT_VEC3": 35668,
+ "INT_VEC4": 35669,
+ "BOOL": 35670,
+ "BOOL_VEC2": 35671,
+ "BOOL_VEC3": 35672,
+ "BOOL_VEC4": 35673,
+ "FLOAT_MAT2": 35674,
+ "FLOAT_MAT3": 35675,
+ "FLOAT_MAT4": 35676,
+ "SAMPLER_2D": 35678,
+ "SAMPLER_CUBE": 35680,
+ "VERTEX_ATTRIB_ARRAY_ENABLED": 34338,
+ "VERTEX_ATTRIB_ARRAY_SIZE": 34339,
+ "VERTEX_ATTRIB_ARRAY_STRIDE": 34340,
+ "VERTEX_ATTRIB_ARRAY_TYPE": 34341,
+ "VERTEX_ATTRIB_ARRAY_NORMALIZED": 34922,
+ "VERTEX_ATTRIB_ARRAY_POINTER": 34373,
+ "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING": 34975,
+ "IMPLEMENTATION_COLOR_READ_TYPE": 35738,
+ "IMPLEMENTATION_COLOR_READ_FORMAT": 35739,
+ "COMPILE_STATUS": 35713,
+ "LOW_FLOAT": 36336,
+ "MEDIUM_FLOAT": 36337,
+ "HIGH_FLOAT": 36338,
+ "LOW_INT": 36339,
+ "MEDIUM_INT": 36340,
+ "HIGH_INT": 36341,
+ "FRAMEBUFFER": 36160,
+ "RENDERBUFFER": 36161,
+ "RGBA4": 32854,
+ "RGB5_A1": 32855,
+ "RGB565": 36194,
+ "DEPTH_COMPONENT16": 33189,
+ "STENCIL_INDEX8": 36168,
+ "DEPTH_STENCIL": 34041,
+ "RENDERBUFFER_WIDTH": 36162,
+ "RENDERBUFFER_HEIGHT": 36163,
+ "RENDERBUFFER_INTERNAL_FORMAT": 36164,
+ "RENDERBUFFER_RED_SIZE": 36176,
+ "RENDERBUFFER_GREEN_SIZE": 36177,
+ "RENDERBUFFER_BLUE_SIZE": 36178,
+ "RENDERBUFFER_ALPHA_SIZE": 36179,
+ "RENDERBUFFER_DEPTH_SIZE": 36180,
+ "RENDERBUFFER_STENCIL_SIZE": 36181,
+ "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE": 36048,
+ "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME": 36049,
+ "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL": 36050,
+ "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE": 36051,
+ "COLOR_ATTACHMENT0": 36064,
+ "DEPTH_ATTACHMENT": 36096,
+ "STENCIL_ATTACHMENT": 36128,
+ "DEPTH_STENCIL_ATTACHMENT": 33306,
+ "NONE": 0,
+ "FRAMEBUFFER_COMPLETE": 36053,
+ "FRAMEBUFFER_INCOMPLETE_ATTACHMENT": 36054,
+ "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT": 36055,
+ "FRAMEBUFFER_INCOMPLETE_DIMENSIONS": 36057,
+ "FRAMEBUFFER_UNSUPPORTED": 36061,
+ "FRAMEBUFFER_BINDING": 36006,
+ "RENDERBUFFER_BINDING": 36007,
+ "MAX_RENDERBUFFER_SIZE": 34024,
+ "INVALID_FRAMEBUFFER_OPERATION": 1286,
+ "UNPACK_FLIP_Y_WEBGL": 37440,
+ "UNPACK_PREMULTIPLY_ALPHA_WEBGL": 37441,
+ "CONTEXT_LOST_WEBGL": 37442,
+ "UNPACK_COLORSPACE_CONVERSION_WEBGL": 37443,
+ "BROWSER_DEFAULT_WEBGL": 37444
+};
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/GLmethod.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/GLmethod.js
new file mode 100644
index 0000000..f2659be
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/GLmethod.js
@@ -0,0 +1,142 @@
+let i = 1;
+
+const GLmethod = {};
+
+GLmethod.activeTexture = i++; //1
+GLmethod.attachShader = i++;
+GLmethod.bindAttribLocation = i++;
+GLmethod.bindBuffer = i++;
+GLmethod.bindFramebuffer = i++;
+GLmethod.bindRenderbuffer = i++;
+GLmethod.bindTexture = i++;
+GLmethod.blendColor = i++;
+GLmethod.blendEquation = i++;
+GLmethod.blendEquationSeparate = i++; //10
+GLmethod.blendFunc = i++;
+GLmethod.blendFuncSeparate = i++;
+GLmethod.bufferData = i++;
+GLmethod.bufferSubData = i++;
+GLmethod.checkFramebufferStatus = i++;
+GLmethod.clear = i++;
+GLmethod.clearColor = i++;
+GLmethod.clearDepth = i++;
+GLmethod.clearStencil = i++;
+GLmethod.colorMask = i++; //20
+GLmethod.compileShader = i++;
+GLmethod.compressedTexImage2D = i++;
+GLmethod.compressedTexSubImage2D = i++;
+GLmethod.copyTexImage2D = i++;
+GLmethod.copyTexSubImage2D = i++;
+GLmethod.createBuffer = i++;
+GLmethod.createFramebuffer = i++;
+GLmethod.createProgram = i++;
+GLmethod.createRenderbuffer = i++;
+GLmethod.createShader = i++; //30
+GLmethod.createTexture = i++;
+GLmethod.cullFace = i++;
+GLmethod.deleteBuffer = i++;
+GLmethod.deleteFramebuffer = i++;
+GLmethod.deleteProgram = i++;
+GLmethod.deleteRenderbuffer = i++;
+GLmethod.deleteShader = i++;
+GLmethod.deleteTexture = i++;
+GLmethod.depthFunc = i++;
+GLmethod.depthMask = i++; //40
+GLmethod.depthRange = i++;
+GLmethod.detachShader = i++;
+GLmethod.disable = i++;
+GLmethod.disableVertexAttribArray = i++;
+GLmethod.drawArrays = i++;
+GLmethod.drawArraysInstancedANGLE = i++;
+GLmethod.drawElements = i++;
+GLmethod.drawElementsInstancedANGLE = i++;
+GLmethod.enable = i++;
+GLmethod.enableVertexAttribArray = i++; //50
+GLmethod.flush = i++;
+GLmethod.framebufferRenderbuffer = i++;
+GLmethod.framebufferTexture2D = i++;
+GLmethod.frontFace = i++;
+GLmethod.generateMipmap = i++;
+GLmethod.getActiveAttrib = i++;
+GLmethod.getActiveUniform = i++;
+GLmethod.getAttachedShaders = i++;
+GLmethod.getAttribLocation = i++;
+GLmethod.getBufferParameter = i++; //60
+GLmethod.getContextAttributes = i++;
+GLmethod.getError = i++;
+GLmethod.getExtension = i++;
+GLmethod.getFramebufferAttachmentParameter = i++;
+GLmethod.getParameter = i++;
+GLmethod.getProgramInfoLog = i++;
+GLmethod.getProgramParameter = i++;
+GLmethod.getRenderbufferParameter = i++;
+GLmethod.getShaderInfoLog = i++;
+GLmethod.getShaderParameter = i++; //70
+GLmethod.getShaderPrecisionFormat = i++;
+GLmethod.getShaderSource = i++;
+GLmethod.getSupportedExtensions = i++;
+GLmethod.getTexParameter = i++;
+GLmethod.getUniform = i++;
+GLmethod.getUniformLocation = i++;
+GLmethod.getVertexAttrib = i++;
+GLmethod.getVertexAttribOffset = i++;
+GLmethod.isBuffer = i++;
+GLmethod.isContextLost = i++; //80
+GLmethod.isEnabled = i++;
+GLmethod.isFramebuffer = i++;
+GLmethod.isProgram = i++;
+GLmethod.isRenderbuffer = i++;
+GLmethod.isShader = i++;
+GLmethod.isTexture = i++;
+GLmethod.lineWidth = i++;
+GLmethod.linkProgram = i++;
+GLmethod.pixelStorei = i++;
+GLmethod.polygonOffset = i++; //90
+GLmethod.readPixels = i++;
+GLmethod.renderbufferStorage = i++;
+GLmethod.sampleCoverage = i++;
+GLmethod.scissor = i++;
+GLmethod.shaderSource = i++;
+GLmethod.stencilFunc = i++;
+GLmethod.stencilFuncSeparate = i++;
+GLmethod.stencilMask = i++;
+GLmethod.stencilMaskSeparate = i++;
+GLmethod.stencilOp = i++; //100
+GLmethod.stencilOpSeparate = i++;
+GLmethod.texImage2D = i++;
+GLmethod.texParameterf = i++;
+GLmethod.texParameteri = i++;
+GLmethod.texSubImage2D = i++;
+GLmethod.uniform1f = i++;
+GLmethod.uniform1fv = i++;
+GLmethod.uniform1i = i++;
+GLmethod.uniform1iv = i++;
+GLmethod.uniform2f = i++; //110
+GLmethod.uniform2fv = i++;
+GLmethod.uniform2i = i++;
+GLmethod.uniform2iv = i++;
+GLmethod.uniform3f = i++;
+GLmethod.uniform3fv = i++;
+GLmethod.uniform3i = i++;
+GLmethod.uniform3iv = i++;
+GLmethod.uniform4f = i++;
+GLmethod.uniform4fv = i++;
+GLmethod.uniform4i = i++; //120
+GLmethod.uniform4iv = i++;
+GLmethod.uniformMatrix2fv = i++;
+GLmethod.uniformMatrix3fv = i++;
+GLmethod.uniformMatrix4fv = i++;
+GLmethod.useProgram = i++;
+GLmethod.validateProgram = i++;
+GLmethod.vertexAttrib1f = i++; //new
+GLmethod.vertexAttrib2f = i++; //new
+GLmethod.vertexAttrib3f = i++; //new
+GLmethod.vertexAttrib4f = i++; //new //130
+GLmethod.vertexAttrib1fv = i++; //new
+GLmethod.vertexAttrib2fv = i++; //new
+GLmethod.vertexAttrib3fv = i++; //new
+GLmethod.vertexAttrib4fv = i++; //new
+GLmethod.vertexAttribPointer = i++;
+GLmethod.viewport = i++;
+
+export default GLmethod;
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/GLtype.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/GLtype.js
new file mode 100644
index 0000000..695abcb
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/GLtype.js
@@ -0,0 +1,23 @@
+const GLtype = {};
+
+[
+ "GLbitfield",
+ "GLboolean",
+ "GLbyte",
+ "GLclampf",
+ "GLenum",
+ "GLfloat",
+ "GLint",
+ "GLintptr",
+ "GLsizei",
+ "GLsizeiptr",
+ "GLshort",
+ "GLubyte",
+ "GLuint",
+ "GLushort"
+].sort().map((typeName, i) => GLtype[typeName] = 1 >> (i + 1));
+
+export default GLtype;
+
+
+
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Program.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Program.js
new file mode 100644
index 0000000..6f5691c
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Program.js
@@ -0,0 +1,21 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLProgram';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLProgram {
+ className = name;
+
+ constructor(id) {
+ this.id = id;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Renderbuffer.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Renderbuffer.js
new file mode 100644
index 0000000..d3182ae
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Renderbuffer.js
@@ -0,0 +1,21 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLRenderBuffer';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLRenderbuffer {
+ className = name;
+
+ constructor(id) {
+ this.id = id;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/RenderingContext.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/RenderingContext.js
new file mode 100644
index 0000000..5f9608f
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/RenderingContext.js
@@ -0,0 +1,1191 @@
+import GLenum from './GLenum';
+import ActiveInfo from './ActiveInfo';
+import Buffer from './Buffer';
+import Framebuffer from './Framebuffer';
+import Renderbuffer from './Renderbuffer';
+import Texture from './Texture';
+import Program from './Program';
+import Shader from './Shader';
+import ShaderPrecisionFormat from './ShaderPrecisionFormat';
+import UniformLocation from './UniformLocation';
+import GLmethod from './GLmethod';
+
+const processArray = (array, checkArrayType = false) => {
+
+ function joinArray(arr, sep) {
+ let res = '';
+ for (let i = 0; i < arr.length; i++) {
+ if (i !== 0) {
+ res += sep;
+ }
+ res += arr[i];
+ }
+ return res;
+ }
+
+ let type = 'Float32Array';
+ if (checkArrayType) {
+ if (array instanceof Uint8Array) {
+ type = 'Uint8Array'
+ } else if (array instanceof Uint16Array) {
+ type = 'Uint16Array';
+ } else if (array instanceof Uint32Array) {
+ type = 'Uint32Array';
+ } else if (array instanceof Float32Array) {
+ type = 'Float32Array';
+ } else {
+ throw new Error('Check array type failed. Array type is ' + typeof array);
+ }
+ }
+
+ const ArrayTypes = {
+ Uint8Array: 1,
+ Uint16Array: 2,
+ Uint32Array: 4,
+ Float32Array: 14
+ };
+ return ArrayTypes[type] + ',' + btoa(joinArray(array, ','))
+}
+
+export default class WebGLRenderingContext {
+
+ // static GBridge = null;
+
+ className = 'WebGLRenderingContext';
+
+ constructor(canvas, type, attrs) {
+ this._canvas = canvas;
+ this._type = type;
+ this._version = 'WebGL 1.0';
+ this._attrs = attrs;
+ this._map = new Map();
+
+ Object.keys(GLenum)
+ .forEach(name => Object.defineProperty(this, name, {
+ value: GLenum[name]
+ }));
+ }
+
+ get canvas() {
+ return this._canvas;
+ }
+
+ activeTexture = function (textureUnit) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.activeTexture + ',' + textureUnit,
+ true
+ );
+ }
+
+ attachShader = function (progarm, shader) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.attachShader + ',' + progarm.id + ',' + shader.id,
+ true
+ );
+ }
+
+ bindAttribLocation = function (program, index, name) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bindAttribLocation + ',' + program.id + ',' + index + ',' + name,
+ true
+ )
+ }
+
+ bindBuffer = function (target, buffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bindBuffer + ',' + target + ',' + (buffer ? buffer.id : 0),
+ true
+ );
+ }
+
+ bindFramebuffer = function (target, framebuffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bindFramebuffer + ',' + target + ',' + (framebuffer ? framebuffer.id : 0),
+ true
+ )
+ }
+
+ bindRenderbuffer = function (target, renderBuffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bindRenderbuffer + ',' + target + ',' + (renderBuffer ? renderBuffer.id : 0),
+ true
+ )
+ }
+
+ bindTexture = function (target, texture) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bindTexture + ',' + target + ',' + (texture ? texture.id : 0),
+ true
+ )
+ }
+
+ blendColor = function (r, g, b, a) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.blendColor + ',' + target + ',' + r + ',' + g + ',' + b + ',' + a,
+ true
+ )
+ }
+
+ blendEquation = function (mode) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.blendEquation + ',' + mode,
+ true
+ )
+ }
+
+ blendEquationSeparate = function (modeRGB, modeAlpha) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.blendEquationSeparate + ',' + modeRGB + ',' + modeAlpha,
+ true
+ )
+ }
+
+
+ blendFunc = function (sfactor, dfactor) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.blendFunc + ',' + sfactor + ',' + dfactor,
+ true
+ );
+ }
+
+ blendFuncSeparate = function (srcRGB, dstRGB, srcAlpha, dstAlpha) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.blendFuncSeparate + ',' + srcRGB + ',' + dstRGB + ',' + srcAlpha + ',' + dstAlpha,
+ true
+ );
+ }
+
+ bufferData = function (target, data, usage) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bufferData + ',' + target + ',' + processArray(data, true) + ',' + usage,
+ true
+ )
+ }
+
+ bufferSubData = function (target, offset, data) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bufferSubData + ',' + target + ',' + offset + ',' + processArray(data, true),
+ true
+ )
+ }
+
+ checkFramebufferStatus = function (target) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.checkFramebufferStatus + ',' + target
+ );
+ return Number(result);
+ }
+
+ clear = function (mask) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.clear + ',' + mask
+ );
+ this._canvas._needRender = true;
+ }
+
+ clearColor = function (r, g, b, a) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.clearColor + ',' + r + ',' + g + ',' + b,
+ true
+ )
+ }
+
+ clearDepth = function (depth) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.clearDepth + ',' + depth,
+ true
+ )
+ }
+
+ clearStencil = function (s) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.clearStencil + ',' + s
+ );
+ }
+
+ colorMask = function (r, g, b, a) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.colorMask + ',' + r + ',' + g + ',' + b + ',' + a
+ )
+ }
+
+ compileShader = function (shader) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.compileShader + ',' + shader.id,
+ true
+ )
+ }
+
+ compressedTexImage2D = function (target, level, internalformat, width, height, border, pixels) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.compressedTexImage2D + ',' + target + ',' + level + ',' + internalformat + ',' +
+ width + ',' + height + ',' + border + ',' + processArray(pixels),
+ true
+ )
+ }
+
+ compressedTexSubImage2D = function (target, level, xoffset, yoffset, width, height, format, pixels) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.compressedTexSubImage2D + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset + ',' +
+ width + ',' + height + ',' + format + ',' + processArray(pixels),
+ true
+ )
+ }
+
+
+ copyTexImage2D = function (target, level, internalformat, x, y, width, height, border) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.copyTexImage2D + ',' + target + ',' + level + ',' + internalformat + ',' + x + ',' + y + ',' +
+ width + ',' + height + ',' + border,
+ true
+ );
+ }
+
+ copyTexSubImage2D = function (target, level, xoffset, yoffset, x, y, width, height) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.copyTexSubImage2D + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset + ',' + x + ',' + y + ',' +
+ width + ',' + height
+ );
+ }
+
+ createBuffer = function () {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createBuffer + ''
+ );
+ const buffer = new Buffer(result);
+ this._map.set(buffer.uuid(), buffer);
+ return buffer;
+ }
+
+ createFramebuffer = function () {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createFramebuffer + ''
+ );
+ const framebuffer = new Framebuffer(result);
+ this._map.set(framebuffer.uuid(), framebuffer);
+ return framebuffer;
+ }
+
+
+ createProgram = function () {
+ const id = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createProgram + ''
+ );
+ const program = new Program(id);
+ this._map.set(program.uuid(), program);
+ return program;
+ }
+
+ createRenderbuffer = function () {
+ const id = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createRenderbuffer + ''
+ )
+ const renderBuffer = new Renderbuffer(id);
+ this._map.set(renderBuffer.uuid(), renderBuffer);
+ return renderBuffer;
+ }
+
+ createShader = function (type) {
+ const id = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createShader + ',' + type
+ )
+ const shader = new Shader(id, type);
+ this._map.set(shader.uuid(), shader);
+ return shader;
+ }
+
+ createTexture = function () {
+ const id = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createTexture + ''
+ );
+ const texture = new Texture(id);
+ this._map.set(texture.uuid(), texture);
+ return texture;
+ }
+
+ cullFace = function (mode) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.cullFace + ',' + mode,
+ true
+ )
+ }
+
+
+ deleteBuffer = function (buffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteBuffer + ',' + buffer.id,
+ true
+ )
+ }
+
+ deleteFramebuffer = function (framebuffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteFramebuffer + ',' + framebuffer.id,
+ true
+ )
+ }
+
+ deleteProgram = function (program) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteProgram + ',' + program.id,
+ true
+ )
+ }
+
+ deleteRenderbuffer = function (renderbuffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteRenderbuffer + ',' + renderbuffer.id,
+ true
+ )
+ }
+
+ deleteShader = function (shader) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteShader + ',' + shader.id,
+ true
+ )
+ }
+
+ deleteTexture = function (texture) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteTexture + ',' + texture.id,
+ true
+ )
+ }
+
+ depthFunc = function (func) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.depthFunc + ',' + func
+ )
+ }
+
+ depthMask = function (flag) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.depthMask + ',' + Number(flag),
+ true
+ )
+ }
+
+ depthRange = function (zNear, zFar) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.depthRange + ',' + zNear + ',' + zFar,
+ true
+ )
+ }
+
+ detachShader = function (program, shader) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.detachShader + ',' + program.id + ',' + shader.id,
+ true
+ )
+ }
+
+ disable = function (cap) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.disable + ',' + cap,
+ true
+ )
+ }
+
+ disableVertexAttribArray = function (index) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.disableVertexAttribArray + ',' + index,
+ true
+ );
+ }
+
+ drawArrays = function (mode, first, count) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.drawArrays + ',' + mode + ',' + first + ',' + count
+ )
+ this._canvas._needRender = true;
+ }
+
+ drawElements = function (mode, count, type, offset) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.drawElements + ',' + mode + ',' + count + ',' + type + ',' + offset + ';'
+ );
+ this._canvas._needRender = true;
+ }
+
+ enable = function (cap) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.enable + ',' + cap,
+ true
+ );
+ }
+
+ enableVertexAttribArray = function (index) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.enableVertexAttribArray + ',' + index,
+ true
+ )
+ }
+
+
+ flush = function () {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.flush + ''
+ )
+ }
+
+ framebufferRenderbuffer = function (target, attachment, textarget, texture, level) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.framebufferRenderbuffer + ',' + target + ',' + attachment + ',' + textarget + ',' + (texture ? texture.id : 0) + ',' + level,
+ true
+ )
+ }
+
+ framebufferTexture2D = function (target, attachment, textarget, texture, level) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.framebufferTexture2D + ',' + target + ',' + attachment + ',' + textarget + ',' + (texture ? texture.id : 0) + ',' + level,
+ true
+ )
+ }
+
+ frontFace = function (mode) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.frontFace + ',' + mode,
+ true
+ )
+ }
+
+ generateMipmap = function (target) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.generateMipmap + ',' + target,
+ true
+ )
+ }
+
+ getActiveAttrib = function (progarm, index) {
+ const resultString = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getActiveAttrib + ',' + progarm.id + ',' + index
+ )
+ const [type, size, name] = resultString.split(',');
+ return new ActiveInfo({
+ type: Number(type),
+ size: Number(size),
+ name
+ });
+ }
+
+ getActiveUniform = function (progarm, index) {
+ const resultString = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getActiveUniform + ',' + progarm.id + ',' + index
+ );
+ const [type, size, name] = resultString.split(',');
+ return new ActiveInfo({
+ type: Number(type),
+ size: Number(size),
+ name
+ })
+ }
+
+ getAttachedShaders = function (progarm) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getAttachedShaders + ',' + progarm.id
+ );
+ const [type, ...ids] = result;
+ return ids.map(id => this._map.get(Shader.uuid(id)));
+ }
+
+ getAttribLocation = function (progarm, name) {
+ return WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getAttribLocation + ',' + progarm.id + ',' + name
+ )
+ }
+
+ getBufferParameter = function (target, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getBufferParameter + ',' + target + ',' + pname
+ );
+ const [type, res] = getBufferParameter;
+ return res;
+ }
+
+ getError = function () {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getError + ''
+ )
+ return result;
+ }
+
+ getExtension = function (name) {
+ return null;
+ }
+
+ getFramebufferAttachmentParameter = function (target, attachment, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getFramebufferAttachmentParameter + ',' + target + ',' + attachment + ',' + pname
+ )
+ switch (pname) {
+ case GLenum.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
+ return this._map.get(Renderbuffer.uuid(result)) || this._map.get(Texture.uuid(result)) || null;
+ default:
+ return result;
+ }
+ }
+
+ getParameter = function (pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getParameter + ',' + pname
+ )
+ switch (pname) {
+ case GLenum.VERSION:
+ return this._version;
+ case GLenum.ARRAY_BUFFER_BINDING: // buffer
+ case GLenum.ELEMENT_ARRAY_BUFFER_BINDING: // buffer
+ return this._map.get(Buffer.uuid(result)) || null;
+ case GLenum.CURRENT_PROGRAM: // program
+ return this._map.get(Program.uuid(result)) || null;
+ case GLenum.FRAMEBUFFER_BINDING: // framebuffer
+ return this._map.get(Framebuffer.uuid(result)) || null;
+ case GLenum.RENDERBUFFER_BINDING: // renderbuffer
+ return this._map.get(Renderbuffer.uuid(result)) || null;
+ case GLenum.TEXTURE_BINDING_2D: // texture
+ case GLenum.TEXTURE_BINDING_CUBE_MAP: // texture
+ return this._map.get(Texture.uuid(result)) || null;
+ case GLenum.ALIASED_LINE_WIDTH_RANGE: // Float32Array
+ case GLenum.ALIASED_POINT_SIZE_RANGE: // Float32Array
+ case GLenum.BLEND_COLOR: // Float32Array
+ case GLenum.COLOR_CLEAR_VALUE: // Float32Array
+ case GLenum.DEPTH_RANGE: // Float32Array
+ case GLenum.MAX_VIEWPORT_DIMS: // Int32Array
+ case GLenum.SCISSOR_BOX: // Int32Array
+ case GLenum.VIEWPORT: // Int32Array
+ case GLenum.COMPRESSED_TEXTURE_FORMATS: // Uint32Array
+ default:
+ const [type, ...res] = result.split(',');
+ if (res.length === 1) {
+ return Number(res[0]);
+ } else {
+ return res.map(Number);
+ }
+ }
+ }
+
+ getProgramInfoLog = function (progarm) {
+ return WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getProgramInfoLog + ',' + progarm.id
+ )
+ }
+
+ getProgramParameter = function (program, pname) {
+ const res = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getProgramParameter + ',' + program.id + ',' + pname
+ );
+
+ const [type, result] = res.split(',').map(i => parseInt(i));
+
+ if (type === 1) {
+ return Boolean(result);
+ } else if (type === 2) {
+ return result;
+ } else {
+ throw new Error('Unrecongized program paramater ' + res + ', type: ' + typeof res);
+ }
+ }
+
+
+ getRenderbufferParameter = function (target, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getRenderbufferParameter + ',' + target + ',' + pname
+ )
+ return result;
+ }
+
+
+ getShaderInfoLog = function (shader) {
+ return WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getShaderInfoLog + ',' + shader.id
+ );
+ }
+
+ getShaderParameter = function (shader, pname) {
+ return WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getShaderParameter + ',' + shader.id + ',' + pname
+ )
+ }
+
+ getShaderPrecisionFormat = function (shaderType, precisionType) {
+ const [rangeMin, rangeMax, precision] = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getShaderPrecisionFormat + ',' + shaderType + ',' + precisionType
+ );
+ const shaderPrecisionFormat = new ShaderPrecisionFormat({
+ rangeMin: Number(rangeMin),
+ rangeMax: Number(rangeMax),
+ precision: Number(precision)
+ });
+ return shaderPrecisionFormat;
+ }
+
+ getShaderSource = function (shader) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getShaderSource + ',' + shader.id
+ );
+ return result;
+ }
+
+ getSupportedExtensions = function () {
+ return Object.keys({});
+ }
+
+ getTexParameter = function (target, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getTexParameter + ',' + target + ',' + pname
+ )
+ return result;
+ }
+
+ getUniformLocation = function (program, name) {
+ const id = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getUniformLocation + ',' + program.id + ',' + name
+ );
+ if (id === -1) {
+ return null;
+ } else {
+ return new UniformLocation(Number(id));
+ }
+ }
+
+ getVertexAttrib = function (index, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getVertexAttrib + ',' + index + ',' + pname
+ );
+ switch (pname) {
+ case GLenum.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
+ return this._map.get(Buffer.uuid(result)) || null;
+ case GLenum.CURRENT_VERTEX_ATTRIB: // Float32Array
+ default:
+ return result;
+ }
+ }
+
+ getVertexAttribOffset = function (index, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getVertexAttribOffset + ',' + index + ',' + pname
+ )
+ return Number(result);
+ }
+
+ isBuffer = function (buffer) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isBuffer + ',' + buffer.id
+ )
+ return Boolean(result);
+ }
+
+ isContextLost = function () {
+ return false;
+ }
+
+ isEnabled = function (cap) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isEnabled + ',' + cap
+ )
+ return Boolean(result);
+ }
+
+ isFramebuffer = function (framebuffer) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isFramebuffer + ',' + framebuffer.id
+ )
+ return Boolean(result);
+ }
+
+ isProgram = function (program) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isProgram + ',' + program.id
+ )
+ return Boolean(result);
+ }
+
+ isRenderbuffer = function (renderBuffer) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isRenderbuffer + ',' + renderbuffer.id
+ )
+ return Boolean(result);
+ }
+
+ isShader = function (shader) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isShader + ',' + shader.id
+ )
+ return Boolean(result);
+ }
+
+ isTexture = function (texture) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isTexture + ',' + texture.id
+ );
+ return Boolean(result);
+ }
+
+ lineWidth = function (width) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.lineWidth + ',' + width,
+ true
+ )
+ }
+
+ linkProgram = function (program) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.linkProgram + ',' + program.id,
+ true
+ );
+ }
+
+
+ pixelStorei = function (pname, param) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.pixelStorei + ',' + pname + ',' + Number(param)
+ )
+ }
+
+ polygonOffset = function (factor, units) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.polygonOffset + ',' + factor + ',' + units
+ )
+ }
+
+ readPixels = function (x, y, width, height, format, type, pixels) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.readPixels + ',' + x + ',' + y + ',' + width + ',' + height + ',' + format + ',' + type
+ )
+ return result;
+ }
+
+ renderbufferStorage = function (target, internalFormat, width, height) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.renderbufferStorage + ',' + target + ',' + internalFormat + ',' + width + ',' + height,
+ true
+ )
+ }
+
+ sampleCoverage = function (value, invert) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.sampleCoverage + ',' + value + ',' + Number(invert),
+ true
+ )
+ }
+
+ scissor = function (x, y, width, height) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.scissor + ',' + x + ',' + y + ',' + width + ',' + height,
+ true
+ )
+ }
+
+ shaderSource = function (shader, source) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.shaderSource + ',' + shader.id + ',' + source
+ )
+ }
+
+ stencilFunc = function (func, ref, mask) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilFunc + ',' + func + ',' + ref + ',' + mask,
+ true
+ )
+ }
+
+ stencilFuncSeparate = function (face, func, ref, mask) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilFuncSeparate + ',' + face + ',' + func + ',' + ref + ',' + mask,
+ true
+ )
+ }
+
+ stencilMask = function (mask) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilMask + ',' + mask,
+ true
+ )
+ }
+
+ stencilMaskSeparate = function (face, mask) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilMaskSeparate + ',' + face + ',' + mask,
+ true
+ )
+ }
+
+ stencilOp = function (fail, zfail, zpass) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilOp + ',' + fail + ',' + zfail + ',' + zpass
+ )
+ }
+
+ stencilOpSeparate = function (face, fail, zfail, zpass) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilOp + ',' + face + ',' + fail + ',' + zfail + ',' + zpass,
+ true
+ )
+ }
+
+ texImage2D = function (...args) {
+ WebGLRenderingContext.GBridge.texImage2D(this._canvas.id, ...args);
+ }
+
+
+ texParameterf = function (target, pname, param) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.texParameterf + ',' + target + ',' + pname + ',' + param,
+ true
+ )
+ }
+
+ texParameteri = function (target, pname, param) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.texParameteri + ',' + target + ',' + pname + ',' + param
+ )
+ }
+
+ texSubImage2D = function (...args) {
+ WebGLRenderingContext.GBridge.texSubImage2D(this._canvas.id, ...args);
+ }
+
+ uniform1f = function (location, v0) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform1f + ',' + location.id + ',' + v0
+ )
+ }
+
+ uniform1fv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform1fv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform1i = function (location, v0) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform1i + ',' + location.id + ',' + v0,
+ // true
+ )
+ }
+
+ uniform1iv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform1iv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform2f = function (location, v0, v1) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform2f + ',' + location.id + ',' + v0 + ',' + v1,
+ true
+ )
+ }
+
+ uniform2fv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform2fv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform2i = function (location, v0, v1) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform2i + ',' + location.id + ',' + v0 + ',' + v1,
+ true
+ )
+ }
+
+ uniform2iv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform2iv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform3f = function (location, v0, v1, v2) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform3f + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2,
+ true
+ )
+ }
+
+ uniform3fv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform3fv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform3i = function (location, v0, v1, v2) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform3i + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2,
+ true
+ )
+ }
+
+ uniform3iv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform3iv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform4f = function (location, v0, v1, v2, v3) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform4f + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3,
+ true
+ )
+ }
+
+ uniform4fv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform4fv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform4i = function (location, v0, v1, v2, v3) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform4i + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3,
+ true
+ )
+ }
+
+ uniform4iv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform4iv + ',' + location.id + ',' + processArray(value, true),
+ true
+ )
+ }
+
+ uniformMatrix2fv = function (location, transpose, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniformMatrix2fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniformMatrix3fv = function (location, transpose, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniformMatrix3fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniformMatrix4fv = function (location, transpose, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniformMatrix4fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value),
+ true
+ );
+ }
+
+ useProgram = function (progarm) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.useProgram + ',' + progarm.id + '',
+ true
+ )
+ }
+
+
+ validateProgram = function (program) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.validateProgram + ',' + program.id,
+ true
+ )
+ }
+
+ vertexAttrib1f = function (index, v0) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib1f + ',' + index + ',' + v0,
+ true
+ )
+ }
+
+ vertexAttrib2f = function (index, v0, v1) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib2f + ',' + index + ',' + v0 + ',' + v1,
+ true
+ )
+ }
+
+ vertexAttrib3f = function (index, v0, v1, v2) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib3f + ',' + index + ',' + v0 + ',' + v1 + ',' + v2,
+ true
+ )
+ }
+
+ vertexAttrib4f = function (index, v0, v1, v2, v3) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib4f + ',' + index + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3,
+ true
+ )
+ }
+
+ vertexAttrib1fv = function (index, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib1fv + ',' + index + ',' + processArray(value),
+ true
+ )
+ }
+
+ vertexAttrib2fv = function (index, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib2fv + ',' + index + ',' + processArray(value),
+ true
+ )
+ }
+
+ vertexAttrib3fv = function (index, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib3fv + ',' + index + ',' + processArray(value),
+ true
+ )
+ }
+
+ vertexAttrib4fv = function (index, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib4fv + ',' + index + ',' + processArray(value),
+ true
+ )
+ }
+
+ vertexAttribPointer = function (index, size, type, normalized, stride, offset) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttribPointer + ',' + index + ',' + size + ',' + type + ',' + Number(normalized) + ',' + stride + ',' + offset,
+ true
+ )
+ }
+
+ viewport = function (x, y, width, height) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.viewport + ',' + x + ',' + y + ',' + width + ',' + height,
+ true
+ )
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Shader.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Shader.js
new file mode 100644
index 0000000..a763886
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Shader.js
@@ -0,0 +1,22 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLShader';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLShader {
+ className = name;
+
+ constructor(id, type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/ShaderPrecisionFormat.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/ShaderPrecisionFormat.js
new file mode 100644
index 0000000..208d6c1
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/ShaderPrecisionFormat.js
@@ -0,0 +1,11 @@
+export default class WebGLShaderPrecisionFormat {
+ className = 'WebGLShaderPrecisionFormat';
+
+ constructor({
+ rangeMin, rangeMax, precision
+ }) {
+ this.rangeMin = rangeMin;
+ this.rangeMax = rangeMax;
+ this.precision = precision;
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Texture.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Texture.js
new file mode 100644
index 0000000..de4d806
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/Texture.js
@@ -0,0 +1,22 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLTexture';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLTexture {
+ className = name;
+
+ constructor(id, type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/UniformLocation.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/UniformLocation.js
new file mode 100644
index 0000000..f5e99dc
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/UniformLocation.js
@@ -0,0 +1,22 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLUniformLocation';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLUniformLocation {
+ className = name;
+
+ constructor(id, type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/classUtils.js b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/classUtils.js
new file mode 100644
index 0000000..88716be
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/context-webgl/classUtils.js
@@ -0,0 +1,3 @@
+export function getTransferedObjectUUID(name, id) {
+ return `${name.toLowerCase()}-${id}`;
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/env/canvas.js b/uni_modules/uview-plus/libs/util/gcanvas/env/canvas.js
new file mode 100644
index 0000000..a8d9bb9
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/env/canvas.js
@@ -0,0 +1,74 @@
+import GContext2D from '../context-2d/RenderingContext';
+import GContextWebGL from '../context-webgl/RenderingContext';
+
+export default class GCanvas {
+
+ // static GBridge = null;
+
+ id = null;
+
+ _needRender = true;
+
+ constructor(id, { disableAutoSwap }) {
+ this.id = id;
+
+ this._disableAutoSwap = disableAutoSwap;
+ if (disableAutoSwap) {
+ this._swapBuffers = () => {
+ GCanvas.GBridge.render(this.id);
+ }
+ }
+ }
+
+ getContext(type) {
+
+ let context = null;
+
+ if (type.match(/webgl/i)) {
+ context = new GContextWebGL(this);
+
+ context.componentId = this.id;
+
+ if (!this._disableAutoSwap) {
+ const render = () => {
+ if (this._needRender) {
+ GCanvas.GBridge.render(this.id);
+ this._needRender = false;
+ }
+ }
+ setInterval(render, 16);
+ }
+
+ GCanvas.GBridge.callSetContextType(this.id, 1); // 0 for 2d; 1 for webgl
+ } else if (type.match(/2d/i)) {
+ context = new GContext2D(this);
+
+ context.componentId = this.id;
+
+// const render = ( callback ) => {
+//
+// const commands = context._drawCommands;
+// context._drawCommands = '';
+//
+// GCanvas.GBridge.render2d(this.id, commands, callback);
+// this._needRender = false;
+// }
+// //draw方法触发
+// context._flush = render;
+// //setInterval(render, 16);
+
+ GCanvas.GBridge.callSetContextType(this.id, 0);
+ } else {
+ throw new Error('not supported context ' + type);
+ }
+
+ return context;
+
+ }
+
+ reset() {
+ GCanvas.GBridge.callReset(this.id);
+ }
+
+
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/env/image.js b/uni_modules/uview-plus/libs/util/gcanvas/env/image.js
new file mode 100644
index 0000000..810e5e0
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/env/image.js
@@ -0,0 +1,104 @@
+let incId = 1;
+
+import GBridge from '../bridge/bridge-weex.js'
+const noop = function () { };
+
+class GImage {
+
+ static GBridge = null;
+
+ constructor() {
+ this._id = incId++;
+ this._width = 0;
+ this._height = 0;
+ this._src = undefined;
+ this._onload = noop;
+ this._onerror = noop;
+ this.complete = false;
+ }
+
+ get width() {
+ return this._width;
+ }
+ set width(v) {
+ this._width = v;
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(v) {
+ this._height = v;
+ }
+
+ get src() {
+ return this._src;
+ }
+
+ set src(v) {
+
+ if (v.startsWith('//')) {
+ v = 'http:' + v;
+ }
+
+ this._src = v;
+ try {
+ GBridge.perloadImage([this._src, this._id], (data) => {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('****GBridge.perloadImage****')
+ }
+ if (typeof data === 'string') {
+ data = JSON.parse(data);
+ }
+
+ if (data.error) {
+ var evt = { type: 'error', target: this };
+ this.onerror(evt);
+ } else {
+ this.complete = true;
+ this.width = typeof data.width === 'number' ? data.width : 0;
+ this.height = typeof data.height === 'number' ? data.height : 0;
+ var evt = { type: 'load', target: this };
+ this.onload(evt);
+ }
+ });
+ } catch (error) {
+ console.log('perloadImage fail', error)
+ }
+ }
+
+ addEventListener(name, listener) {
+ if (name === 'load') {
+ this.onload = listener;
+ } else if (name === 'error') {
+ this.onerror = listener;
+ }
+ }
+
+ removeEventListener(name, listener) {
+ if (name === 'load') {
+ this.onload = noop;
+ } else if (name === 'error') {
+ this.onerror = noop;
+ }
+ }
+
+ get onload() {
+ return this._onload;
+ }
+
+ set onload(v) {
+ this._onload = v;
+ }
+
+ get onerror() {
+ return this._onerror;
+ }
+
+ set onerror(v) {
+ this._onerror = v;
+ }
+}
+
+export default GImage;
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/env/tool.js b/uni_modules/uview-plus/libs/util/gcanvas/env/tool.js
new file mode 100644
index 0000000..d3fb398
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/env/tool.js
@@ -0,0 +1,24 @@
+
+export function ArrayBufferToBase64 (buffer) {
+ var binary = '';
+ var bytes = new Uint8ClampedArray(buffer);
+ for (var len = bytes.byteLength, i = 0; i < len; i++) {
+ binary += String.fromCharCode(bytes[i]);
+ }
+ return btoa(binary);
+}
+
+export function Base64ToUint8ClampedArray(base64String) {
+ const padding = '='.repeat((4 - base64String.length % 4) % 4);
+ const base64 = (base64String + padding)
+ .replace(/\-/g, '+')
+ .replace(/_/g, '/');
+
+ const rawData = atob(base64);
+ const outputArray = new Uint8ClampedArray(rawData.length);
+
+ for (let i = 0; i < rawData.length; ++i) {
+ outputArray[i] = rawData.charCodeAt(i);
+ }
+ return outputArray;
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/gcanvas/index.js b/uni_modules/uview-plus/libs/util/gcanvas/index.js
new file mode 100644
index 0000000..a34ad58
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/gcanvas/index.js
@@ -0,0 +1,39 @@
+import GCanvas from './env/canvas';
+import GImage from './env/image';
+
+import GWebGLRenderingContext from './context-webgl/RenderingContext';
+import GContext2D from './context-2d/RenderingContext';
+
+import GBridgeWeex from './bridge/bridge-weex';
+
+export let Image = GImage;
+
+export let WeexBridge = GBridgeWeex;
+
+export function enable(el, { bridge, debug, disableAutoSwap, disableComboCommands } = {}) {
+
+ const GBridge = GImage.GBridge = GCanvas.GBridge = GWebGLRenderingContext.GBridge = GContext2D.GBridge = bridge;
+
+ GBridge.callEnable(el.ref, [
+ 0, // renderMode: 0--RENDERMODE_WHEN_DIRTY, 1--RENDERMODE_CONTINUOUSLY
+ -1, // hybridLayerType: 0--LAYER_TYPE_NONE 1--LAYER_TYPE_SOFTWARE 2--LAYER_TYPE_HARDWARE
+ false, // supportScroll
+ false, // newCanvasMode
+ 1, // compatible
+ 'white',// clearColor
+ false // sameLevel: newCanvasMode = true && true => GCanvasView and Webview is same level
+ ]);
+
+ if (debug === true) {
+ GBridge.callEnableDebug();
+ }
+ if (disableComboCommands) {
+ GBridge.callEnableDisableCombo();
+ }
+
+ var canvas = new GCanvas(el.ref, { disableAutoSwap });
+ canvas.width = el.style.width;
+ canvas.height = el.style.height;
+
+ return canvas;
+};
\ No newline at end of file
diff --git a/uni_modules/uview-plus/libs/util/route.js b/uni_modules/uview-plus/libs/util/route.js
new file mode 100644
index 0000000..4e474f5
--- /dev/null
+++ b/uni_modules/uview-plus/libs/util/route.js
@@ -0,0 +1,124 @@
+/**
+ * 路由跳转方法,该方法相对于直接使用uni.xxx的好处是使用更加简单快捷
+ * 并且带有路由拦截功能
+ */
+import { queryParams, deepMerge, page } from '../function/index';
+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 = queryParams(params, false)
+ // 因为已有get参数,所以后面拼接的参数需要带上"&"隔开
+ return url += `&${query}`
+ }
+ // 直接拼接参数,因为此处url中没有后面的query参数,也就没有"?/&"之类的符号
+ query = 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 = deepMerge(this.config, options)
+ // 否则正常使用mergeConfig中的url和params进行拼接
+ mergeConfig.url = this.mixinParam(options.url, options.params)
+ }
+
+ // 如果本次跳转的路径和本页面路径一致,不执行跳转,防止用户快速点击跳转按钮,造成多次跳转同一个页面的问题
+ if (mergeConfig.url === page()) return
+
+ if (params.intercept) {
+ this.config.intercept = params.intercept
+ }
+ // params参数也带给拦截器
+ mergeConfig.params = params
+ // 合并内外部参数
+ mergeConfig = 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-plus/libs/vue.js b/uni_modules/uview-plus/libs/vue.js
new file mode 100644
index 0000000..0a34916
--- /dev/null
+++ b/uni_modules/uview-plus/libs/vue.js
@@ -0,0 +1,3 @@
+export const defineMixin = (options) => {
+ return options
+}
diff --git a/uni_modules/uview-plus/package.json b/uni_modules/uview-plus/package.json
new file mode 100644
index 0000000..5bacb15
--- /dev/null
+++ b/uni_modules/uview-plus/package.json
@@ -0,0 +1,111 @@
+{
+ "id": "uview-plus",
+ "name": "uview-plus",
+ "displayName": "零云®uview-plus3.0重磅发布,全面的Vue3鸿蒙跨端移动组件库,组件丰富维护更新稳定。",
+ "version": "3.6.57",
+ "description": "零云®uview-plus已兼容vue3支持多语言,120+全面的组件和便捷的工具会让您信手拈来。近期新增拖动排序、条码、图片裁剪、下拉刷新、虚拟列表、签名、Markdown等。",
+ "keywords": [
+ "uview",
+ "uview-plus",
+ "ui",
+ "echarts",
+ "ui",
+ "可视化设计"
+],
+ "main": "index.js",
+ "repository": "https://github.com/ijry/uview-plus",
+ "engines": {
+ "HBuilderX": "^3.1.0",
+ "uni-app": "^4.66",
+ "uni-app-x": ""
+ },
+ "dcloudext": {
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": "598821125"
+ },
+ "declaration": {
+ "ads": "请注意本插件开发文档包含弹窗广告每日展示一次,源码纯净无广告。",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/uview-plus",
+ "type": "component-vue",
+ "darkmode": "x",
+ "i18n": "√",
+ "widescreen": "x"
+ },
+ "uni_modules": {
+ "dependencies": [],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "√",
+ "aliyun": "√",
+ "alipay": "√"
+ },
+ "client": {
+ "uni-app": {
+ "vue": {
+ "vue2": "-",
+ "vue3": "√"
+ },
+ "web": {
+ "safari": "√",
+ "chrome": "√"
+ },
+ "app": {
+ "vue": "√",
+ "nvue": "√",
+ "android": "√",
+ "ios": "√",
+ "harmony": "√"
+ },
+ "mp": {
+ "weixin": "√",
+ "alipay": "√",
+ "toutiao": "√",
+ "baidu": "-",
+ "kuaishou": "-",
+ "jd": "-",
+ "harmony": "√",
+ "qq": "√",
+ "lark": "-"
+ },
+ "quickapp": {
+ "huawei": "-",
+ "union": "-"
+ }
+ },
+ "uni-app-x": {
+ "web": {
+ "safari": "-",
+ "chrome": "-"
+ },
+ "app": {
+ "android": "-",
+ "ios": "-",
+ "harmony": "-"
+ },
+ "mp": {
+ "weixin": "-"
+ }
+ }
+ }
+ }
+ },
+ "dependencies": {
+ "clipboard": "^2.0.11",
+ "dayjs": "^1.11.3"
+ },
+ "publishConfig": {
+ "registry": "https://registry.npmjs.org/"
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uview-plus/theme.scss b/uni_modules/uview-plus/theme.scss
new file mode 100644
index 0000000..331b30f
--- /dev/null
+++ b/uni_modules/uview-plus/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/uview-plus/types/comps.d.ts b/uni_modules/uview-plus/types/comps.d.ts
new file mode 100644
index 0000000..22fd434
--- /dev/null
+++ b/uni_modules/uview-plus/types/comps.d.ts
@@ -0,0 +1,102 @@
+declare module 'vue' {
+ export interface GlobalComponents {
+ // 基础组件
+ ['up-icon']: typeof import('./comps/icon')['Icon']
+ ['up-image']: typeof import('./comps/image')['Image']
+ ['up-button']: typeof import('./comps/button')['Button']
+ ['up-text']: typeof import('./comps/text')['Text']
+ ['up-row']: typeof import('./comps/row')['Row']
+ ['up-col']: typeof import('./comps/col')['Col']
+ ['up-cell']: typeof import('./comps/cell')['Cell']
+ ['up-cell-group']: typeof import('./comps/cellGroup')['CellGroup']
+ ['up-badge']: typeof import('./comps/badge')['Badge']
+ ['up-tag']: typeof import('./comps/tag')['Tag']
+ ['up-loading-icon']: typeof import('./comps/loadingIcon')['LoadingIcon']
+ ['up-loading-page']: typeof import('./comps/loadingPage')['LoadingPage']
+
+ // 表单组件
+ ['up-form']: typeof import('./comps/form')['Form']
+ ['up-form-item']: typeof import('./comps/formItem')['FormItem']
+ ['up-calendar']: typeof import('./comps/calendar')['Calendar']
+ ['up-keyboard']: typeof import('./comps/keyboard')['Keyboard']
+ ['up-picker']: typeof import('./comps/picker')['Picker']
+ ['up-datetime-picker']: typeof import('./comps/datetimePicker')['DatetimePicker']
+ ['up-rate']: typeof import('./comps/rate')['Rate']
+ ['up-search']: typeof import('./comps/search')['Search']
+ ['up-number-box']: typeof import('./comps/numberBox')['NumberBox']
+ ['up-upload']: typeof import('./comps/upload')['Upload']
+ ['up-code']: typeof import('./comps/code')['Code']
+ ['up-input']: typeof import('./comps/input')['Input']
+ ['up-textarea']: typeof import('./comps/textarea')['Textarea']
+ ['up-checkbox']: typeof import('./comps/checkbox')['Checkbox']
+ ['up-checkbox-group']: typeof import('./comps/checkboxGroup')['CheckboxGroup']
+ ['up-radio']: typeof import('./comps/radio')['Radio']
+ ['up-radio-group']: typeof import('./comps/radioGroup')['RadioGroup']
+ ['up-switch']: typeof import('./comps/switch')['Switch']
+ ['up-slider']: typeof import('./comps/slider')['Slider']
+ ['up-album']: typeof import('./comps/album')['Album']
+
+ // 数据组件
+ ['up-list']: typeof import('./comps/list')['List']
+ ['up-list-item']: typeof import('./comps/listItem')['ListItem']
+ ['up-line-progress']: typeof import('./comps/lineProgress')['LineProgress']
+ ['up-count-down']: typeof import('./comps/countDown')['CountDown']
+ ['up-count-to']: typeof import('./comps/countTo')['CountTo']
+
+ // 反馈组件
+ ['up-tooltip']: typeof import('./comps/tooltip')['Tooltip']
+ ['up-action-sheet']: typeof import('./comps/actionSheet')['ActionSheet']
+ ['up-alert']: typeof import('./comps/alert')['Alert']
+ ['up-toast']: typeof import('./comps/toast')['Toast']
+ ['up-notice-bar']: typeof import('./comps/noticeBar')['NoticeBar']
+ ['up-notify']: typeof import('./comps/notify')['Notify']
+ ['up-swipe-action']: typeof import('./comps/swipeAction')['SwipeAction']
+ ['up-swipe-action-item']: typeof import('./comps/swipeActionItem')['SwipeActionItem']
+ ['up-collapse']: typeof import('./comps/collapse')['Collapse']
+ ['up-collapse-item']: typeof import('./comps/collapseItem')['CollapseItem']
+ ['up-popup']: typeof import('./comps/popup')['Popup']
+ ['up-modal']: typeof import('./comps/modal')['Modal']
+
+ // 布局组件
+ ['up-scroll-list']: typeof import('./comps/scrollList')['ScrollList']
+ ['up-line']: typeof import('./comps/line')['Line']
+ ['up-overlay']: typeof import('./comps/overlay')['Overlay']
+ ['up-no-network']: typeof import('./comps/noNetwork')['NoNetwork']
+ ['up-grid']: typeof import('./comps/grid')['Grid']
+ ['up-grid-item']: typeof import('./comps/gridItem')['GridItem']
+ ['up-swiper']: typeof import('./comps/swiper')['Swiper']
+ ['up-skeleton']: typeof import('./comps/skeleton')['Skeleton']
+ ['up-sticky']: typeof import('./comps/sticky')['Sticky']
+ ['up-divider']: typeof import('./comps/divider')['Divider']
+
+ // 导航组件
+ ['up-tabbar']: typeof import('./comps/tabbar')['Tabbar']
+ ['up-tabbar-item']: typeof import('./comps/tabbarItem')['TabbarItem']
+ ['up-back-top']: typeof import('./comps/backTop')['BackTop']
+ ['up-navbar']: typeof import('./comps/navbar')['Navbar']
+ ['up-tabs']: typeof import('./comps/tabs')['Tabs']
+ ['up-subsection']: typeof import('./comps/subsection')['Subsection']
+ ['up-index-list']: typeof import('./comps/indexList')['IndexList']
+ ['up-index-item']: typeof import('./comps/indexItem')['IndexItem']
+ ['up-index-anchor']: typeof import('./comps/indexAnchor')['IndexAnchor']
+ ['up-steps']: typeof import('./comps/steps')['Steps']
+ ['up-steps-item']: typeof import('./comps/stepsItem')['StepsItem']
+ ['up-empty']: typeof import('./comps/empty')['Empty']
+
+ // 其他组件
+ ['up-parse']: typeof import('./comps/parse')['Parse']
+ ['up-code-input']: typeof import('./comps/codeInput')['CodeInput']
+ ['up-loadmore']: typeof import('./comps/loadMore')['LoadMore']
+ ['up-read-more']: typeof import('./comps/readMore')['ReadMore']
+ ['up-gap']: typeof import('./comps/gap')['Gap']
+ ['up-avatar']: typeof import('./comps/avatar')['Avatar']
+ ['up-avatar-group']: typeof import('./comps/avatarGroup')['AvatarGroup']
+ ['up-link']: typeof import('./comps/link')['Link']
+ ['up-transition']: typeof import('./comps/transition')['Transition']
+ ['up-status-bar']: typeof import('./comps/statusBar')['StatusBar']
+ ['up-safe-bottom']: typeof import('./comps/safeBottom')['SafeBottom']
+ ['up-qrcode']: typeof import('./comps/qrcode')['Qrcode']
+ }
+}
+
+export {}
diff --git a/uni_modules/uview-plus/types/comps/_common.d.ts b/uni_modules/uview-plus/types/comps/_common.d.ts
new file mode 100644
index 0000000..03cb105
--- /dev/null
+++ b/uni_modules/uview-plus/types/comps/_common.d.ts
@@ -0,0 +1,9 @@
+export interface AllowedComponentProps {
+ class?: unknown;
+ style?: unknown;
+}
+
+export interface VNodeProps {
+ key?: string | number | symbol;
+ ref?: unknown;
+}
diff --git a/uni_modules/uview-plus/types/comps/actionSheet.d.ts b/uni_modules/uview-plus/types/comps/actionSheet.d.ts
new file mode 100644
index 0000000..81b3527
--- /dev/null
+++ b/uni_modules/uview-plus/types/comps/actionSheet.d.ts
@@ -0,0 +1,121 @@
+import { AllowedComponentProps, VNodeProps } from './_common'
+
+declare interface ActionSheetProps {
+ /**
+ * 是否展示
+ * @default false
+ */
+ show?: boolean
+ /**
+ * 设置标题
+ */
+ title?: string
+ /**
+ * 选项上方的描述信息
+ */
+ description?: string
+ /**
+ * 按钮的文字数组
+ * @default []
+ */
+ actions?: any[]
+ /**
+ * 取消按钮的文字,不为空时显示按钮
+ */
+ cancelText?: string
+ /**
+ * 点击某个菜单项时是否关闭弹窗
+ */
+ closeOnClickAction?: boolean
+ /**
+ * 是否开启底部安全区适配
+ * @default false
+ */
+ safeAreaInsetBottom?: boolean
+ /**
+ * 小程序的打开方式
+ */
+ openType?: string
+ /**
+ * 点击遮罩是否允许关闭
+ */
+ closeOnClickOverlay?: boolean
+ /**
+ * 圆角值,默认无圆角
+ * @default 0
+ */
+ round?: string | number
+ /**
+ * 指定返回用户信息的语言
+ * @default "en"
+ */
+ lang?: 'zh_CN' | 'zh_TW' | 'en'
+ /**
+ * 会话来源,open-type="contact"时有效。只微信小程序有效
+ */
+ sessionFrom?: string
+ /**
+ * 会话内消息卡片标题,openType="contact"时有效
+ */
+ sendMessageTitle?: string
+ /**
+ * 会话内消息卡片点击跳转小程序路径,openType="contact"时有效
+ */
+ sendMessagePath?: string
+ /**
+ * 会话内消息卡片图片,openType="contact"时有效
+ */
+ sendMessageImg?: string
+ /**
+ * 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效
+ * @default false
+ */
+ showMessageCard?: boolean
+ /**
+ * 打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效
+ */
+ appParameter?: string
+ /**
+ * 点击ActionSheet列表项时触发
+ */
+ onSelect?: (e: any) => any
+ /**
+ * 点击取消按钮时触发
+ */
+ onClose?: () => any
+ /**
+ * 获取用户信息回调,openType="getUserInfo"时有效
+ * @param detail 用户信息
+ */
+ onGetuserinfo?: (detail: any) => any
+ /**
+ * 客服消息回调,openType="contact"时有效
+ */
+ onContact?: () => any
+ /**
+ * 获取用户手机号回调,openType="getPhoneNumber"时有效
+ */
+ onGetphonenumber?: () => any
+ /**
+ * 当使用开放能力时,发生错误的回调
+ */
+ onError?: (...args: any) => any
+ /**
+ * 在打开授权设置页并关闭后回调
+ */
+ onOpensetting?: (...args: any) => any
+ /**
+ * 打开 APP 成功的回调
+ */
+ onLaunchapp?: (...args: any) => any
+}
+
+declare interface _ActionSheet {
+ new (): {
+ $props: AllowedComponentProps &
+ VNodeProps &
+ ActionSheetProps
+ }
+}
+
+export declare const ActionSheet: _ActionSheet
diff --git a/uni_modules/uview-plus/types/comps/album.d.ts b/uni_modules/uview-plus/types/comps/album.d.ts
new file mode 100644
index 0000000..886d530
--- /dev/null
+++ b/uni_modules/uview-plus/types/comps/album.d.ts
@@ -0,0 +1,82 @@
+import { AllowedComponentProps, VNodeProps } from './_common'
+
+declare interface AlbumProps {
+ /**
+ * 图片地址列表
+ */
+ urls?: string[] | Record[]
+ /**
+ * 指定从数组的对象元素中读取哪个属性作为图片地址
+ */
+ keyName?: string
+ /**
+ * 单图时,图片长边的长度
+ * @default 180
+ */
+ singleSize?: string | number
+ /**
+ * 多图时,图片边长
+ * @default 70
+ */
+ multipleSize?: string | number
+ /**
+ * 多图时,图片水平和垂直之间的间隔
+ * @default 6
+ */
+ space?: string | number
+ /**
+ * 单图时,图片缩放裁剪的模式
+ * @default "scaleToFill"
+ */
+ singleMode?: ImageMode
+ /**
+ * 多图时,图片缩放裁剪的模式
+ * @default "aspectFill"
+ */
+ multipleMode?: ImageMode
+ /**
+ * 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
+ * @default 9
+ */
+ maxCount?: string | number
+ /**
+ * 是否可以预览图片
+ * @default true
+ */
+ previewFullImage?: boolean
+ /**
+ * 每行展示图片数量,如设置,singleSize和multipleSize将会无效
+ * @default 3
+ */
+ rowCount?: string | number
+ /**
+ * 超出maxCount时是否显示查看更多的提示
+ * @default true
+ */
+ showMore?: boolean
+ /**
+ * 某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送
+ * @param width 宽度
+ */
+ onAlbumWidth?: (width: any) => any
+ /**
+ * 图片形状
+ * @default "square"
+ */
+ shape?: 'circle' | 'square'
+ /**
+ * 圆角,默认单位px
+ * @default 0
+ */
+ radius?: string | number
+}
+
+declare interface _Album {
+ new (): {
+ $props: AllowedComponentProps &
+ VNodeProps &
+ AlbumProps
+ }
+}
+
+export declare const Album: _Album
diff --git a/uni_modules/uview-plus/types/comps/alert.d.ts b/uni_modules/uview-plus/types/comps/alert.d.ts
new file mode 100644
index 0000000..b8b5541
--- /dev/null
+++ b/uni_modules/uview-plus/types/comps/alert.d.ts
@@ -0,0 +1,55 @@
+import { AllowedComponentProps, VNodeProps } from './_common'
+
+declare interface AlertProps {
+ /**
+ * 显示的文字
+ */
+ title?: string
+ /**
+ * 使用预设的颜色
+ * @default "warning"
+ */
+ type?: 'warning' | 'success' | 'primary' | 'error' | 'info'
+ /**
+ * 辅助性文字,颜色比`title`浅一点,字号也小一点,可选
+ */
+ description?: string
+ /**
+ * 关闭按钮(默认为叉号icon图标)
+ * @default false
+ */
+ closable?: boolean
+ /**
+ * 是否显示左边的辅助图标
+ * @default false
+ */
+ showIcon?: boolean
+ /**
+ * 多图时,图片缩放裁剪的模式
+ */
+ effect?: 'light' | 'dark'
+ /**
+ * 文字是否居中
+ * @default false
+ */
+ center?: boolean
+ /**
+ * 字体大小
+ * @default 14
+ */
+ fontSize?: string | number
+ /**
+ * 点击组件时触发
+ */
+ onClick?: () => any
+}
+
+declare interface _Alert {
+ new (): {
+ $props: AllowedComponentProps &
+ VNodeProps &
+ AlertProps
+ }
+}
+
+export declare const Alert: _Alert
diff --git a/uni_modules/uview-plus/types/comps/avatar.d.ts b/uni_modules/uview-plus/types/comps/avatar.d.ts
new file mode 100644
index 0000000..bc3ff36
--- /dev/null
+++ b/uni_modules/uview-plus/types/comps/avatar.d.ts
@@ -0,0 +1,85 @@
+import { AllowedComponentProps, VNodeProps } from './_common'
+import { ImageMode } from './image'
+
+declare interface AvatarProps {
+ /**
+ * 头像路径,如加载失败,将会显示默认头像(不能为相对路径)
+ */
+ src?: string
+ /**
+ * 头像形状
+ * @default "circle"
+ */
+ shape?: 'circle' | 'square'
+ /**
+ * 头像尺寸
+ * @default 40
+ */
+ size?: 'large' | 'default' | 'mini' | number
+ /**
+ * 裁剪类型
+ * @default "scaleToFill"
+ */
+ mode?: ImageMode
+ /**
+ * 用文字替代图片,级别优先于`src`
+ */
+ text?: string
+ /**
+ * 背景颜色
+ * @default "#c0c4cc"
+ */
+ bgColor?: string
+ /**
+ * 文字颜色
+ * @default "#fff"
+ */
+ color?: string
+ /**
+ * 文字大小
+ * @default 18
+ */
+ fontSize?: string | number
+ /**
+ * 显示的图标
+ */
+ icon?: string
+ /**
+ * 显示小程序头像,只对百度,微信,QQ小程序有效
+ * @default false
+ */
+ mpAvatar?: boolean
+ /**
+ * 是否使用随机背景色
+ * @default false
+ */
+ randomBgColor?: boolean
+ /**
+ * 加载失败的默认头像(组件有内置默认图片)
+ */
+ defaultUrl?: string
+ /**
+ * 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
+ */
+ colorIndex?: string | number
+ /**
+ * 组件标识符
+ * @default "level"
+ */
+ name?: string
+ /**
+ * 头像被点击
+ * @param index 用户传递的标识符
+ */
+ onClick?: (index: any) => any
+}
+
+declare interface _Avatar {
+ new (): {
+ $props: AllowedComponentProps &
+ VNodeProps &
+ AvatarProps
+ }
+}
+
+export declare const Avatar: _Avatar
diff --git a/uni_modules/uview-plus/types/comps/avatarGroup.d.ts b/uni_modules/uview-plus/types/comps/avatarGroup.d.ts
new file mode 100644
index 0000000..e9ff4c4
--- /dev/null
+++ b/uni_modules/uview-plus/types/comps/avatarGroup.d.ts
@@ -0,0 +1,62 @@
+import { AllowedComponentProps, VNodeProps } from './_common'
+import { ImageMode } from './image'
+
+declare interface AvatarGroupProps {
+ /**
+ * 头像图片组
+ * @default []
+ */
+ urls?: string[]
+ /**
+ * 最多展示的头像数量
+ * @default 5
+ */
+ maxCount?: string | number
+ /**
+ * 头像形状
+ * @default "circle"
+ */
+ shape?: 'circle' | 'square'
+ /**
+ * 裁剪模式
+ * @default "aspectFill"
+ */
+ mode?: ImageMode
+ /**
+ * 超出maxCount时是否显示查看更多的提示
+ * @default true
+ */
+ showMore?: boolean
+ /**
+ * 头像大小
+ * @default 40
+ */
+ size?: string | number
+ /**
+ * 指定从数组的对象元素中读取哪个属性作为图片地址
+ */
+ keyName?: string
+ /**
+ * 头像之间的遮挡比例(0.4代表遮挡40%)
+ * @default 0.5
+ */
+ gap?: string | number
+ /**
+ * 需额外显示的值,如设置则优先于内部的`urls.length - maxCount`值
+ */
+ extraValue?: string | number
+ /**
+ * 头像组更多点击
+ */
+ onShowMore?: () => any
+}
+
+declare interface _AvatarGroup {
+ new (): {
+ $props: AllowedComponentProps &
+ VNodeProps &
+ AvatarGroupProps
+ }
+}
+
+export declare const AvatarGroup: _AvatarGroup
diff --git a/uni_modules/uview-plus/types/comps/backTop.d.ts b/uni_modules/uview-plus/types/comps/backTop.d.ts
new file mode 100644
index 0000000..d41cbe6
--- /dev/null
+++ b/uni_modules/uview-plus/types/comps/backTop.d.ts
@@ -0,0 +1,74 @@
+import { AllowedComponentProps, VNodeProps } from './_common'
+
+declare interface BackTopProps {
+ /**
+ * 按钮形状
+ * @default "circle"
+ */
+ mode?: 'circle' | 'square'
+ /**
+ * uView内置图标名称,或图片路径
+ * @default "arrow-upward"
+ */
+ icon?: string
+ /**
+ * 返回顶部按钮的提示文字
+ */
+ text?: string
+ /**
+ * 返回顶部过程中的过渡时间,单位ms
+ * @default 100
+ */
+ duration?: string | number
+ /**
+ * 页面的滚动距离,通过`onPageScroll`生命周期获取
+ * @default 0
+ */
+ scrollTop?: string | number
+ /**
+ * 滚动条滑动多少距离时显示,单位rpx
+ * @default 400
+ */
+ top?: string | number
+ /**
+ * 返回按钮位置到屏幕底部的距离,单位rpx
+ * @default 100
+ */
+ bottom?: string | number
+ /**
+ * 返回按钮位置到屏幕右边的距离,单位rpx
+ * @default 20
+ */
+ right?: string | number
+ /**
+ * 返回顶部按钮的层级
+ * @default 9
+ */
+ zIndex?: string | number
+ /**
+ * 图标的样式
+ */
+ iconStyle?: unknown
+ /**
+ * 按钮外层的自定义样式
+ */
+ customStyle?: unknown
+}
+
+declare interface BackTopSlots {
+ /**
+ * 自定义返回按钮的所有内容
+ */
+ ['default']?: () => any
+}
+
+declare interface _BackTop {
+ new (): {
+ $props: AllowedComponentProps &
+ VNodeProps &
+ BackTopProps
+ $slots: BackTopSlots
+ }
+}
+
+export declare const BackTop: _BackTop
diff --git a/uni_modules/uview-plus/types/comps/badge.d.ts b/uni_modules/uview-plus/types/comps/badge.d.ts
new file mode 100644
index 0000000..35e434f
--- /dev/null
+++ b/uni_modules/uview-plus/types/comps/badge.d.ts
@@ -0,0 +1,76 @@
+import { AllowedComponentProps, VNodeProps } from './_common'
+
+declare interface BadgeProps {
+ /**
+ * 不展示数字,只有一个小点
+ * @default false
+ */
+ isDot?: boolean
+ /**
+ * 展示的数字,大于`overflowCount`时显示为`${overflowCount}+`,为`0`且`show-zero`为`false`时隐藏
+ */
+ value?: string | number
+ /**
+ * 组件是否显示
+ * @default true
+ */
+ show?: boolean
+ /**
+ * 最大值,超过最大值会显示 '{max}+'
+ * @default 99
+ */
+ max?: string | number
+ /**
+ * 主题类型
+ * @default "error"
+ */
+ type?: 'error' | 'warning' | 'success' | 'primary' | 'info'
+ /**
+ * 当数值为 0 时,是否展示 Badge
+ * @default false
+ */
+ showZero?: boolean
+ /**
+ * 背景颜色,优先级比`type`高,如设置,`type`参数会失效
+ */
+ bgColor?: string
+ /**
+ * 字体颜色
+ * @default "#fff"
+ */
+ color?: string
+ /**
+ * 徽标形状,circle-四角均为圆角,horn-左下角为直角
+ * @default "circle"
+ */
+ shape?: 'circle' | 'horn'
+ /**
+ * 置数字的显示方式,详细见[文档](https://www.uviewui.com/components/badge.html#%E8%AE%BE%E7%BD%AE%E6%95%B0%E5%AD%97%E7%9A%84%E6%98%BE%E7%A4%BA%E6%96%B9%E5%BC%8F-overflow-ellipsis-limit)
+ * @default "overflow"
+ */
+ numberType?: 'overflow' | 'ellipsis' | 'limit'
+ /**
+ * 设置badge的位置偏移,格式为 [x, y],也即设置的为`top`和`right`的值,`absolute`为`true`时有效
+ */
+ offset?: string[]
+ /**
+ * 是否反转背景和字体颜色
+ * @default false
+ */
+ inverted?: boolean
+ /**
+ * 组件是否绝对定位,为`true`时,`offset`参数才有效
+ * @default false
+ */
+ absolute?: boolean
+}
+
+declare interface _Badge {
+ new (): {
+ $props: AllowedComponentProps &
+ VNodeProps &
+ BadgeProps
+ }
+}
+
+export declare const Badge: _Badge
diff --git a/uni_modules/uview-plus/types/comps/button.d.ts b/uni_modules/uview-plus/types/comps/button.d.ts
new file mode 100644
index 0000000..9868916
--- /dev/null
+++ b/uni_modules/uview-plus/types/comps/button.d.ts
@@ -0,0 +1,169 @@
+import { AllowedComponentProps, VNodeProps } from './_common'
+
+declare interface ButtonProps {
+ /**
+ * 是否显示按钮的细边框
+ * @default true
+ */
+ hairline?: boolean
+ /**
+ * 按钮的样式类型
+ * @default "info"
+ */
+ type?: 'info' | 'primary' | 'error' | 'warning' | 'success' | 'text'
+ /**
+ * 按钮的大小
+ * @default "normal"
+ */
+ size?: 'normal' | 'large' | 'small' | 'mini'
+ /**
+ * 按钮外观形状
+ * @default "square"
+ */
+ shape?: 'square' | 'circle'
+ /**
+ * 按钮是否镂空
+ * @default false
+ */
+ plain?: boolean
+ /**
+ * 是否禁用
+ * @default false
+ */
+ disabled?: boolean
+ /**
+ * 按钮名称前是否带 loading 图标
+ * @default false
+ */
+ loading?: boolean
+ /**
+ * 加载中提示文字
+ */
+ loadingText?: string
+ /**
+ * 加载状态图标类型
+ * @default "spinner"
+ */
+ loadingMode?: string
+ /**
+ * 加载图标大小
+ * @default 15
+ */
+ loadingSize?: string | number
+ /**
+ * 开放能力,具体请看 [button](https://uniapp.dcloud.net.cn/component/button.html#open-type-%E6%9C%89%E6%95%88%E5%80%BC)
+ */
+ openType?: string
+ /**
+ * 用于