\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/huakai-app/uni_modules/uview-plus/components/u-markdown/u-markdown.vue b/huakai-app/uni_modules/uview-plus/components/u-markdown/u-markdown.vue
new file mode 100644
index 0000000..8312b88
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-markdown/u-markdown.vue
@@ -0,0 +1,300 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/huakai-app/uni_modules/uview-plus/components/u-message-input/u-message-input.vue b/huakai-app/uni_modules/uview-plus/components/u-message-input/u-message-input.vue
new file mode 100644
index 0000000..0da5f1c
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-modal/modal.js b/huakai-app/uni_modules/uview-plus/components/u-modal/modal.js
new file mode 100644
index 0000000..59d8abc
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-modal/props.js b/huakai-app/uni_modules/uview-plus/components/u-modal/props.js
new file mode 100644
index 0000000..0f0177b
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-modal/u-modal.vue b/huakai-app/uni_modules/uview-plus/components/u-modal/u-modal.vue
new file mode 100644
index 0000000..eab8f72
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-modal/u-modal.vue
@@ -0,0 +1,265 @@
+
+
+
+ {{ title }}
+
+
+
+ {{ content }}
+
+
+
+
+
+
+
+
+
+
+ {{ cancelText }}
+
+
+
+
+ {{ confirmText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-navbar-mini/props.js b/huakai-app/uni_modules/uview-plus/components/u-navbar-mini/props.js
new file mode 100644
index 0000000..57d5b4e
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-navbar-mini/u-navbar-mini.vue b/huakai-app/uni_modules/uview-plus/components/u-navbar-mini/u-navbar-mini.vue
new file mode 100644
index 0000000..be546d7
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-navbar-mini/u-navbar-mini.vue
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-navbar/navbar.js b/huakai-app/uni_modules/uview-plus/components/u-navbar/navbar.js
new file mode 100644
index 0000000..e268b35
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-navbar/props.js b/huakai-app/uni_modules/uview-plus/components/u-navbar/props.js
new file mode 100644
index 0000000..b66f87e
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-navbar/u-navbar.vue b/huakai-app/uni_modules/uview-plus/components/u-navbar/u-navbar.vue
new file mode 100644
index 0000000..08945f2
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-navbar/u-navbar.vue
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+ {{ leftText }}
+
+
+
+ {{ title }}
+
+
+
+
+ {{ rightText }}
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-no-network/noNetwork.js b/huakai-app/uni_modules/uview-plus/components/u-no-network/noNetwork.js
new file mode 100644
index 0000000..96bb796
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-no-network/props.js b/huakai-app/uni_modules/uview-plus/components/u-no-network/props.js
new file mode 100644
index 0000000..1091cf5
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-no-network/u-no-network.vue b/huakai-app/uni_modules/uview-plus/components/u-no-network/u-no-network.vue
new file mode 100644
index 0000000..e9dd03b
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-notice-bar/noticeBar.js b/huakai-app/uni_modules/uview-plus/components/u-notice-bar/noticeBar.js
new file mode 100644
index 0000000..59e930b
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-notice-bar/props.js b/huakai-app/uni_modules/uview-plus/components/u-notice-bar/props.js
new file mode 100644
index 0000000..2dd91af
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-notice-bar/u-notice-bar.vue b/huakai-app/uni_modules/uview-plus/components/u-notice-bar/u-notice-bar.vue
new file mode 100644
index 0000000..32de362
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-notice-bar/u-notice-bar.vue
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-notify/notify.js b/huakai-app/uni_modules/uview-plus/components/u-notify/notify.js
new file mode 100644
index 0000000..142760e
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-notify/props.js b/huakai-app/uni_modules/uview-plus/components/u-notify/props.js
new file mode 100644
index 0000000..fdb01b4
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-notify/u-notify.vue b/huakai-app/uni_modules/uview-plus/components/u-notify/u-notify.vue
new file mode 100644
index 0000000..2970f4d
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-notify/u-notify.vue
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+ {{ tmpConfig.message }}
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-number-box/numberBox.js b/huakai-app/uni_modules/uview-plus/components/u-number-box/numberBox.js
new file mode 100644
index 0000000..8d79ab7
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-number-box/props.js b/huakai-app/uni_modules/uview-plus/components/u-number-box/props.js
new file mode 100644
index 0000000..ada9952
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-number-box/u-number-box.vue b/huakai-app/uni_modules/uview-plus/components/u-number-box/u-number-box.vue
new file mode 100644
index 0000000..09ae4f5
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-number-box/u-number-box.vue
@@ -0,0 +1,478 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-number-keyboard/numberKeyboard.js b/huakai-app/uni_modules/uview-plus/components/u-number-keyboard/numberKeyboard.js
new file mode 100644
index 0000000..f94be24
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-number-keyboard/props.js b/huakai-app/uni_modules/uview-plus/components/u-number-keyboard/props.js
new file mode 100644
index 0000000..d853858
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-number-keyboard/u-number-keyboard.vue b/huakai-app/uni_modules/uview-plus/components/u-number-keyboard/u-number-keyboard.vue
new file mode 100644
index 0000000..2290793
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-number-keyboard/u-number-keyboard.vue
@@ -0,0 +1,198 @@
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-overlay/overlay.js b/huakai-app/uni_modules/uview-plus/components/u-overlay/overlay.js
new file mode 100644
index 0000000..89f1da3
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-overlay/props.js b/huakai-app/uni_modules/uview-plus/components/u-overlay/props.js
new file mode 100644
index 0000000..39f16f3
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-overlay/u-overlay.vue b/huakai-app/uni_modules/uview-plus/components/u-overlay/u-overlay.vue
new file mode 100644
index 0000000..c2c007b
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-overlay/u-overlay.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-pagination/u-pagination.vue b/huakai-app/uni_modules/uview-plus/components/u-pagination/u-pagination.vue
new file mode 100644
index 0000000..3b9f269
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-pagination/u-pagination.vue
@@ -0,0 +1,287 @@
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-parse/node/node.vue b/huakai-app/uni_modules/uview-plus/components/u-parse/node/node.vue
new file mode 100644
index 0000000..bc5b262
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-parse/node/node.vue
@@ -0,0 +1,600 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{n.text}}
+
+
+ {{n.text}}
+
+ {{'\n'}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-parse/parse.js b/huakai-app/uni_modules/uview-plus/components/u-parse/parse.js
new file mode 100644
index 0000000..a70dabe
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-parse/parser.js b/huakai-app/uni_modules/uview-plus/components/u-parse/parser.js
new file mode 100644
index 0000000..e8fc979
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-parse/props.js b/huakai-app/uni_modules/uview-plus/components/u-parse/props.js
new file mode 100644
index 0000000..733c2b1
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-parse/u-parse.vue b/huakai-app/uni_modules/uview-plus/components/u-parse/u-parse.vue
new file mode 100644
index 0000000..e0871fe
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-parse/u-parse.vue
@@ -0,0 +1,504 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-pdf-reader/props.js b/huakai-app/uni_modules/uview-plus/components/u-pdf-reader/props.js
new file mode 100644
index 0000000..dbaa178
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-pdf-reader/u-pdf-reader.vue b/huakai-app/uni_modules/uview-plus/components/u-pdf-reader/u-pdf-reader.vue
new file mode 100644
index 0000000..2ff0747
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-picker-column/props.js b/huakai-app/uni_modules/uview-plus/components/u-picker-column/props.js
new file mode 100644
index 0000000..a37e438
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-picker-column/u-picker-column.vue b/huakai-app/uni_modules/uview-plus/components/u-picker-column/u-picker-column.vue
new file mode 100644
index 0000000..f788634
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-picker-column/u-picker-column.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-picker-data/u-picker-data.vue b/huakai-app/uni_modules/uview-plus/components/u-picker-data/u-picker-data.vue
new file mode 100644
index 0000000..959c811
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-picker-data/u-picker-data.vue
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-picker/picker.js b/huakai-app/uni_modules/uview-plus/components/u-picker/picker.js
new file mode 100644
index 0000000..61fca3e
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-picker/props.js b/huakai-app/uni_modules/uview-plus/components/u-picker/props.js
new file mode 100644
index 0000000..cb2f2ea
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-picker/props.js
@@ -0,0 +1,166 @@
+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 {}
+ }
+ },
+ inputBorder: {
+ type: String,
+ default: () => defProps.input.inputBorder
+ },
+ 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/huakai-app/uni_modules/uview-plus/components/u-picker/u-picker.vue b/huakai-app/uni_modules/uview-plus/components/u-picker/u-picker.vue
new file mode 100644
index 0000000..625e8db
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-picker/u-picker.vue
@@ -0,0 +1,478 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getItemText(item1) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-popover/props.js b/huakai-app/uni_modules/uview-plus/components/u-popover/props.js
new file mode 100644
index 0000000..64f1855
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-popover/u-popover.vue b/huakai-app/uni_modules/uview-plus/components/u-popover/u-popover.vue
new file mode 100644
index 0000000..fb9a2dd
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-popover/u-popover.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+ {{text}}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/huakai-app/uni_modules/uview-plus/components/u-popup/popup.js b/huakai-app/uni_modules/uview-plus/components/u-popup/popup.js
new file mode 100644
index 0000000..c745556
--- /dev/null
+++ b/huakai-app/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: '#fff',
+ overlayOpacity: 0.5,
+ pageInline: false,
+ touchable: false,
+ minHeight: '200px',
+ maxHeight: '600px'
+ }
+}
diff --git a/huakai-app/uni_modules/uview-plus/components/u-popup/props.js b/huakai-app/uni_modules/uview-plus/components/u-popup/props.js
new file mode 100644
index 0000000..68e7c86
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-popup/u-popup.vue b/huakai-app/uni_modules/uview-plus/components/u-popup/u-popup.vue
new file mode 100644
index 0000000..f1e5a92
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-popup/u-popup.vue
@@ -0,0 +1,448 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/huakai-app/uni_modules/uview-plus/components/u-poster/u-poster.vue b/huakai-app/uni_modules/uview-plus/components/u-poster/u-poster.vue
new file mode 100644
index 0000000..fe2b600
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-poster/u-poster.vue
@@ -0,0 +1,629 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/huakai-app/uni_modules/uview-plus/components/u-pull-refresh/u-pull-refresh.vue b/huakai-app/uni_modules/uview-plus/components/u-pull-refresh/u-pull-refresh.vue
new file mode 100644
index 0000000..a71a27e
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-qrcode/qrcode.js b/huakai-app/uni_modules/uview-plus/components/u-qrcode/qrcode.js
new file mode 100644
index 0000000..b420833
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-qrcode/qrcode.js
@@ -0,0 +1,1269 @@
+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,
+ ctx: opt.ctx,
+ isNvue: opt.isNvue,
+ vuectx: opt.vuectx,
+ usingComponents: opt.usingComponents,
+ showLoading: opt.showLoading,
+ loadingText: opt.loadingText,
+ };
+
+ 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 = '';
+ ctx = options.ctx;
+
+ 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/huakai-app/uni_modules/uview-plus/components/u-qrcode/u-qrcode.vue b/huakai-app/uni_modules/uview-plus/components/u-qrcode/u-qrcode.vue
new file mode 100644
index 0000000..1196d4a
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-qrcode/u-qrcode.vue
@@ -0,0 +1,548 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-radio-group/props.js b/huakai-app/uni_modules/uview-plus/components/u-radio-group/props.js
new file mode 100644
index 0000000..9ff6996
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-radio-group/radioGroup.js b/huakai-app/uni_modules/uview-plus/components/u-radio-group/radioGroup.js
new file mode 100644
index 0000000..7bba89b
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-radio-group/u-radio-group.vue b/huakai-app/uni_modules/uview-plus/components/u-radio-group/u-radio-group.vue
new file mode 100644
index 0000000..6ec9435
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-radio-group/u-radio-group.vue
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-radio/props.js b/huakai-app/uni_modules/uview-plus/components/u-radio/props.js
new file mode 100644
index 0000000..8862f7f
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-radio/radio.js b/huakai-app/uni_modules/uview-plus/components/u-radio/radio.js
new file mode 100644
index 0000000..da4762d
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-radio/u-radio.vue b/huakai-app/uni_modules/uview-plus/components/u-radio/u-radio.vue
new file mode 100644
index 0000000..372f943
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-radio/u-radio.vue
@@ -0,0 +1,350 @@
+
+
+
+
+
+
+
+
+
+ {{label}}
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-rate/props.js b/huakai-app/uni_modules/uview-plus/components/u-rate/props.js
new file mode 100644
index 0000000..0e099ef
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-rate/rate.js b/huakai-app/uni_modules/uview-plus/components/u-rate/rate.js
new file mode 100644
index 0000000..c04ebf8
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-rate/u-rate.vue b/huakai-app/uni_modules/uview-plus/components/u-rate/u-rate.vue
new file mode 100644
index 0000000..3a8577d
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-rate/u-rate.vue
@@ -0,0 +1,327 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-read-more/props.js b/huakai-app/uni_modules/uview-plus/components/u-read-more/props.js
new file mode 100644
index 0000000..57c7a03
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-read-more/readMore.js b/huakai-app/uni_modules/uview-plus/components/u-read-more/readMore.js
new file mode 100644
index 0000000..f644f07
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-read-more/u-read-more.vue b/huakai-app/uni_modules/uview-plus/components/u-read-more/u-read-more.vue
new file mode 100644
index 0000000..fec02e1
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-read-more/u-read-more.vue
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-refresh-virtual-list/u-refresh-virtual-list.vue b/huakai-app/uni_modules/uview-plus/components/u-refresh-virtual-list/u-refresh-virtual-list.vue
new file mode 100644
index 0000000..9313ac3
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-refresh-virtual-list/u-refresh-virtual-list.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-row-notice/props.js b/huakai-app/uni_modules/uview-plus/components/u-row-notice/props.js
new file mode 100644
index 0000000..0223608
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-row-notice/rowNotice.js b/huakai-app/uni_modules/uview-plus/components/u-row-notice/rowNotice.js
new file mode 100644
index 0000000..53fc3fb
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-row-notice/u-row-notice.vue b/huakai-app/uni_modules/uview-plus/components/u-row-notice/u-row-notice.vue
new file mode 100644
index 0000000..0165ba8
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-row-notice/u-row-notice.vue
@@ -0,0 +1,336 @@
+
+
+
+
+
+
+
+
+
+ {{item}}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-row/props.js b/huakai-app/uni_modules/uview-plus/components/u-row/props.js
new file mode 100644
index 0000000..cc6a5a7
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-row/row.js b/huakai-app/uni_modules/uview-plus/components/u-row/row.js
new file mode 100644
index 0000000..3363f1e
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-row/u-row.vue b/huakai-app/uni_modules/uview-plus/components/u-row/u-row.vue
new file mode 100644
index 0000000..e89a941
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-row/u-row.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-safe-bottom/props.js b/huakai-app/uni_modules/uview-plus/components/u-safe-bottom/props.js
new file mode 100644
index 0000000..d4f490d
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-safe-bottom/u-safe-bottom.vue b/huakai-app/uni_modules/uview-plus/components/u-safe-bottom/u-safe-bottom.vue
new file mode 100644
index 0000000..b9ea841
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-safe-bottom/u-safe-bottom.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-scroll-list/nvue.js b/huakai-app/uni_modules/uview-plus/components/u-scroll-list/nvue.js
new file mode 100644
index 0000000..fd0b825
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-scroll-list/props.js b/huakai-app/uni_modules/uview-plus/components/u-scroll-list/props.js
new file mode 100644
index 0000000..d77f536
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-scroll-list/scrollList.js b/huakai-app/uni_modules/uview-plus/components/u-scroll-list/scrollList.js
new file mode 100644
index 0000000..15e3ae0
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-scroll-list/scrollWxs.wxs b/huakai-app/uni_modules/uview-plus/components/u-scroll-list/scrollWxs.wxs
new file mode 100644
index 0000000..ce94f1d
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-scroll-list/u-scroll-list.vue b/huakai-app/uni_modules/uview-plus/components/u-scroll-list/u-scroll-list.vue
new file mode 100644
index 0000000..6e80fea
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-scroll-list/u-scroll-list.vue
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-search/props.js b/huakai-app/uni_modules/uview-plus/components/u-search/props.js
new file mode 100644
index 0000000..8925ec9
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-search/search.js b/huakai-app/uni_modules/uview-plus/components/u-search/search.js
new file mode 100644
index 0000000..5daf277
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-search/u-search.vue b/huakai-app/uni_modules/uview-plus/components/u-search/u-search.vue
new file mode 100644
index 0000000..8a618b1
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-search/u-search.vue
@@ -0,0 +1,354 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ actionText }}
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-section/section.js b/huakai-app/uni_modules/uview-plus/components/u-section/section.js
new file mode 100644
index 0000000..d33780e
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-select/u-select.vue b/huakai-app/uni_modules/uview-plus/components/u-select/u-select.vue
new file mode 100644
index 0000000..2c9f0cd
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-select/u-select.vue
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+ {{ currentLabel }}
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item[labelName]}}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-short-video/u-short-video.vue b/huakai-app/uni_modules/uview-plus/components/u-short-video/u-short-video.vue
new file mode 100644
index 0000000..b755043
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-signature/u-signature.vue b/huakai-app/uni_modules/uview-plus/components/u-signature/u-signature.vue
new file mode 100644
index 0000000..d76ba34
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-skeleton/props.js b/huakai-app/uni_modules/uview-plus/components/u-skeleton/props.js
new file mode 100644
index 0000000..ea6c07a
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-skeleton/skeleton.js b/huakai-app/uni_modules/uview-plus/components/u-skeleton/skeleton.js
new file mode 100644
index 0000000..6b421a9
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-skeleton/u-skeleton.vue b/huakai-app/uni_modules/uview-plus/components/u-skeleton/u-skeleton.vue
new file mode 100644
index 0000000..3716edd
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-skeleton/u-skeleton.vue
@@ -0,0 +1,248 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-slider/mpother.js b/huakai-app/uni_modules/uview-plus/components/u-slider/mpother.js
new file mode 100644
index 0000000..040c848
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-slider/mpwxs.js b/huakai-app/uni_modules/uview-plus/components/u-slider/mpwxs.js
new file mode 100644
index 0000000..8d051fd
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-slider/mpwxs.wxs b/huakai-app/uni_modules/uview-plus/components/u-slider/mpwxs.wxs
new file mode 100644
index 0000000..847df4a
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-slider/nvue.js b/huakai-app/uni_modules/uview-plus/components/u-slider/nvue.js
new file mode 100644
index 0000000..53380ff
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-slider/props.js b/huakai-app/uni_modules/uview-plus/components/u-slider/props.js
new file mode 100644
index 0000000..8e0ff14
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-slider/props.js
@@ -0,0 +1,110 @@
+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
+ },
+ // 是否垂直方向
+ vertical: {
+ type: Boolean,
+ default: defProps.slider.vertical
+ },
+ // 滑块粗细,为了支持垂直模式下统一粗细参数,用size代替height。
+ size: {
+ type: [Number, String],
+ default: () => defProps.slider.size
+ },
+ // 滑块长度,水平和垂直模式的长度。
+ length: {
+ type: [Number, String],
+ default: 'auto'
+ }
+ }
+})
\ No newline at end of file
diff --git a/huakai-app/uni_modules/uview-plus/components/u-slider/slider.js b/huakai-app/uni_modules/uview-plus/components/u-slider/slider.js
new file mode 100644
index 0000000..05a0716
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-slider/slider.js
@@ -0,0 +1,31 @@
+/*
+ * @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: '',
+ innerStyle: {},
+ vertical: false,
+ size: '2px',
+ length: 'auto'
+ }
+}
\ No newline at end of file
diff --git a/huakai-app/uni_modules/uview-plus/components/u-slider/u-slider.vue b/huakai-app/uni_modules/uview-plus/components/u-slider/u-slider.vue
new file mode 100644
index 0000000..6d87fc6
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-slider/u-slider.vue
@@ -0,0 +1,588 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ this.rangeValue[0] }}
+
+
+ {{ this.rangeValue[1] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ modelValue }}
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-status-bar/props.js b/huakai-app/uni_modules/uview-plus/components/u-status-bar/props.js
new file mode 100644
index 0000000..d3bf7ee
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-status-bar/statusBar.js b/huakai-app/uni_modules/uview-plus/components/u-status-bar/statusBar.js
new file mode 100644
index 0000000..d5ad536
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-status-bar/u-status-bar.vue b/huakai-app/uni_modules/uview-plus/components/u-status-bar/u-status-bar.vue
new file mode 100644
index 0000000..582ba6a
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-status-bar/u-status-bar.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-steps-item/props.js b/huakai-app/uni_modules/uview-plus/components/u-steps-item/props.js
new file mode 100644
index 0000000..2eabc67
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-steps-item/stepsItem.js b/huakai-app/uni_modules/uview-plus/components/u-steps-item/stepsItem.js
new file mode 100644
index 0000000..0f82a1c
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-steps-item/u-steps-item.vue b/huakai-app/uni_modules/uview-plus/components/u-steps-item/u-steps-item.vue
new file mode 100644
index 0000000..b4faad8
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-steps-item/u-steps-item.vue
@@ -0,0 +1,349 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ index + 1}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-steps/props.js b/huakai-app/uni_modules/uview-plus/components/u-steps/props.js
new file mode 100644
index 0000000..3252b66
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-steps/steps.js b/huakai-app/uni_modules/uview-plus/components/u-steps/steps.js
new file mode 100644
index 0000000..61c63cd
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-steps/u-steps.vue b/huakai-app/uni_modules/uview-plus/components/u-steps/u-steps.vue
new file mode 100644
index 0000000..255e2b7
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-steps/u-steps.vue
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-sticky/props.js b/huakai-app/uni_modules/uview-plus/components/u-sticky/props.js
new file mode 100644
index 0000000..c889102
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-sticky/sticky.js b/huakai-app/uni_modules/uview-plus/components/u-sticky/sticky.js
new file mode 100644
index 0000000..683c36b
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-sticky/u-sticky.vue b/huakai-app/uni_modules/uview-plus/components/u-sticky/u-sticky.vue
new file mode 100644
index 0000000..d074026
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-sticky/u-sticky.vue
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-subsection/props.js b/huakai-app/uni_modules/uview-plus/components/u-subsection/props.js
new file mode 100644
index 0000000..799f518
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-subsection/subsection.js b/huakai-app/uni_modules/uview-plus/components/u-subsection/subsection.js
new file mode 100644
index 0000000..1e3ef53
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-subsection/u-subsection.vue b/huakai-app/uni_modules/uview-plus/components/u-subsection/u-subsection.vue
new file mode 100644
index 0000000..4aacc24
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-subsection/u-subsection.vue
@@ -0,0 +1,405 @@
+
+
+
+
+ {{ getText(item) }}
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/alipay.sjs b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/alipay.sjs
new file mode 100644
index 0000000..51a7636
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/index - backup.wxs b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/index - backup.wxs
new file mode 100644
index 0000000..04cab92
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/index.wxs b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/index.wxs
new file mode 100644
index 0000000..d7ba3de
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/nvue - backup.js b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/nvue - backup.js
new file mode 100644
index 0000000..f114ca6
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/nvue.js b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/nvue.js
new file mode 100644
index 0000000..4586708
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/other.js b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/other.js
new file mode 100644
index 0000000..5ec51f0
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/props.js b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/props.js
new file mode 100644
index 0000000..9f080c8
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/swipeActionItem.js b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/swipeActionItem.js
new file mode 100644
index 0000000..f6afb44
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/u-swipe-action-item.vue b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/u-swipe-action-item.vue
new file mode 100644
index 0000000..f8a718d
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/u-swipe-action-item.vue
@@ -0,0 +1,249 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/wxs.js b/huakai-app/uni_modules/uview-plus/components/u-swipe-action-item/wxs.js
new file mode 100644
index 0000000..ee49c10
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action/props.js b/huakai-app/uni_modules/uview-plus/components/u-swipe-action/props.js
new file mode 100644
index 0000000..29b14ef
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action/swipeAction.js b/huakai-app/uni_modules/uview-plus/components/u-swipe-action/swipeAction.js
new file mode 100644
index 0000000..253fbfc
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swipe-action/u-swipe-action.vue b/huakai-app/uni_modules/uview-plus/components/u-swipe-action/u-swipe-action.vue
new file mode 100644
index 0000000..6f0f5ae
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-swipe-action/u-swipe-action.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-swiper-indicator/props.js b/huakai-app/uni_modules/uview-plus/components/u-swiper-indicator/props.js
new file mode 100644
index 0000000..fd3c83e
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swiper-indicator/swipterIndicator.js b/huakai-app/uni_modules/uview-plus/components/u-swiper-indicator/swipterIndicator.js
new file mode 100644
index 0000000..c474f1c
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swiper-indicator/u-swiper-indicator.vue b/huakai-app/uni_modules/uview-plus/components/u-swiper-indicator/u-swiper-indicator.vue
new file mode 100644
index 0000000..f485153
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-swiper-indicator/u-swiper-indicator.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-swiper/props.js b/huakai-app/uni_modules/uview-plus/components/u-swiper/props.js
new file mode 100644
index 0000000..87ee5ea
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swiper/swiper.js b/huakai-app/uni_modules/uview-plus/components/u-swiper/swiper.js
new file mode 100644
index 0000000..2574c75
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-swiper/u-swiper.vue b/huakai-app/uni_modules/uview-plus/components/u-swiper/u-swiper.vue
new file mode 100644
index 0000000..a872ba7
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-swiper/u-swiper.vue
@@ -0,0 +1,269 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-switch/props.js b/huakai-app/uni_modules/uview-plus/components/u-switch/props.js
new file mode 100644
index 0000000..40a3454
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-switch/props.js
@@ -0,0 +1,74 @@
+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
+ },
+ // 打开时圆点的颜色
+ dotActiveColor: {
+ type: String,
+ default: () => defProps.switch.dotActiveColor
+ },
+ // 关闭时圆点的颜色
+ dotInactiveColor: {
+ type: String,
+ default: () => defProps.switch.dotInactiveColor
+ },
+ // 通过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/huakai-app/uni_modules/uview-plus/components/u-switch/switch.js b/huakai-app/uni_modules/uview-plus/components/u-switch/switch.js
new file mode 100644
index 0000000..4326ad6
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-switch/switch.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/switch.js
+ */
+export default {
+ // switch
+ switch: {
+ loading: false,
+ disabled: false,
+ size: 25,
+ activeColor: '#2979ff',
+ inactiveColor: '#ffffff',
+ dotActiveColor: '#ffffff',
+ dotInactiveColor: '#ffffff',
+ value: false,
+ activeValue: true,
+ inactiveValue: false,
+ asyncChange: false,
+ space: 0
+ }
+}
diff --git a/huakai-app/uni_modules/uview-plus/components/u-switch/u-switch.vue b/huakai-app/uni_modules/uview-plus/components/u-switch/u-switch.vue
new file mode 100644
index 0000000..dcf0e2d
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-switch/u-switch.vue
@@ -0,0 +1,217 @@
+
+
+
+
+
+ :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/huakai-app/uni_modules/uview-plus/components/u-tabbar-item/props.js b/huakai-app/uni_modules/uview-plus/components/u-tabbar-item/props.js
new file mode 100644
index 0000000..9d208aa
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-tabbar-item/tabbarItem.js b/huakai-app/uni_modules/uview-plus/components/u-tabbar-item/tabbarItem.js
new file mode 100644
index 0000000..4f19759
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-tabbar-item/u-tabbar-item.vue b/huakai-app/uni_modules/uview-plus/components/u-tabbar-item/u-tabbar-item.vue
new file mode 100644
index 0000000..8efe459
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-tabbar/props.js b/huakai-app/uni_modules/uview-plus/components/u-tabbar/props.js
new file mode 100644
index 0000000..2645a2e
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-tabbar/tabbar.js b/huakai-app/uni_modules/uview-plus/components/u-tabbar/tabbar.js
new file mode 100644
index 0000000..4818e9f
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-tabbar/u-tabbar.vue b/huakai-app/uni_modules/uview-plus/components/u-tabbar/u-tabbar.vue
new file mode 100644
index 0000000..c05d8a2
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-tabbar/u-tabbar.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-table/props.js b/huakai-app/uni_modules/uview-plus/components/u-table/props.js
new file mode 100644
index 0000000..a37e438
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-table/u-table.vue b/huakai-app/uni_modules/uview-plus/components/u-table/u-table.vue
new file mode 100644
index 0000000..18773a1
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-table/u-table.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/huakai-app/uni_modules/uview-plus/components/u-table2/tableRow.vue b/huakai-app/uni_modules/uview-plus/components/u-table2/tableRow.vue
new file mode 100644
index 0000000..6f7b8cd
--- /dev/null
+++ b/huakai-app/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/huakai-app/uni_modules/uview-plus/components/u-table2/u-table2.vue b/huakai-app/uni_modules/uview-plus/components/u-table2/u-table2.vue
new file mode 100644
index 0000000..d9637aa
--- /dev/null
+++ b/huakai-app/uni_modules/uview-plus/components/u-table2/u-table2.vue
@@ -0,0 +1,912 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+