diff --git a/yudao-vue-ui/.hbuilderx/launch.json b/yudao-vue-ui/.hbuilderx/launch.json
new file mode 100644
index 000000000..07c1d5fa5
--- /dev/null
+++ b/yudao-vue-ui/.hbuilderx/launch.json
@@ -0,0 +1,16 @@
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+ // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+ "version": "0.0",
+ "configurations": [{
+ "default" :
+ {
+ "launchtype" : "local"
+ },
+ "h5" :
+ {
+ "launchtype" : "local"
+ },
+ "type" : "uniCloud"
+ }
+ ]
+}
diff --git a/yudao-vue-ui/App.vue b/yudao-vue-ui/App.vue
new file mode 100644
index 000000000..6b658ecf3
--- /dev/null
+++ b/yudao-vue-ui/App.vue
@@ -0,0 +1,19 @@
+
+
+
\ No newline at end of file
diff --git a/yudao-vue-ui/common/css/common.css b/yudao-vue-ui/common/css/common.css
new file mode 100644
index 000000000..2f22c237e
--- /dev/null
+++ b/yudao-vue-ui/common/css/common.css
@@ -0,0 +1,182 @@
+/* #ifndef APP-PLUS-NVUE */
+view,
+scroll-view,
+swiper,
+swiper-item,
+cover-view,
+cover-image,
+icon,
+text,
+rich-text,
+progress,
+button,
+checkbox,
+form,
+input,
+label,
+radio,
+slider,
+switch,
+textarea,
+navigator,
+audio,
+camera,
+image,
+video {
+ box-sizing: border-box;
+}
+image{
+ display: block;
+}
+text{
+ line-height: 1;
+ /* font-family: Helvetica Neue, Helvetica, sans-serif; */
+}
+button{
+ padding: 0;
+ margin: 0;
+ background-color: rgba(0,0,0,0) !important;
+}
+button:after{
+ border: 0;
+}
+.bottom-fill{
+ height: constant(safe-area-inset-bottom);
+ height: env(safe-area-inset-bottom);
+}
+.fix-bot{
+ box-sizing: content-box;
+ padding-bottom: constant(safe-area-inset-bottom);
+ padding-bottom: env(safe-area-inset-bottom);
+}
+
+/* 边框 */
+.round{
+ position: relative;
+ border-radius: 100rpx;
+}
+.round:after{
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 200%;
+ height: 200%;
+ transform: scale(.5) translate(-50%,-50%);
+ border: 1px solid #878787;
+ border-radius: 100rpx;
+ box-sizing: border-box;
+}
+.b-b:after{
+ position: absolute;
+ z-index: 3;
+ left: 0;
+ top: auto;
+ bottom: 0;
+ right: 0;
+ height: 0;
+ content: '';
+ transform: scaleY(.5);
+ border-bottom: 1px solid #e0e0e0;
+}
+.b-t:before{
+ position: absolute;
+ z-index: 3;
+ left: 0;
+ top: 0;
+ right: 0;
+ height: 0;
+ content: '';
+ transform: scaleY(.5);
+ border-bottom: 1px solid #e5e5e5;
+}
+.b-r:after{
+ position: absolute;
+ z-index: 3;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ width: 0;
+ content: '';
+ transform: scaleX(.5);
+ border-right: 1px solid #e5e5e5;
+}
+.b-l:before{
+ position: absolute;
+ z-index: 3;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: 0;
+ content: '';
+ transform: scaleX(.5);
+ border-left: 1px solid #e5e5e5;
+}
+.b-b, .b-t, .b-l, .b-r{
+ position: relative;
+}
+/* 点击态 */
+.hover-gray {
+ background: #fafafa !important;
+}
+.hover-dark {
+ background: #f0f0f0 !important;
+}
+
+.hover-opacity {
+ opacity: 0.7;
+}
+
+/* #endif */
+
+.clamp {
+ /* #ifdef APP-PLUS-NVUE */
+ lines: 1;
+ /* #endif */
+ /* #ifndef APP-PLUS-NVUE */
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ display: block;
+ /* #endif */
+}
+.clamp2 {
+ /* #ifdef APP-PLUS-NVUE */
+ lines: 2;
+ /* #endif */
+ /* #ifndef APP-PLUS-NVUE */
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ /* #endif */
+}
+
+/* 布局 */
+.row{
+ /* #ifndef APP-PLUS-NVUE */
+ display:flex;
+ /* #endif */
+ flex-direction:row;
+ align-items: center;
+}
+.column{
+ /* #ifndef APP-PLUS-NVUE */
+ display:flex;
+ /* #endif */
+ flex-direction: column;
+}
+.center{
+ /* #ifndef APP-PLUS-NVUE */
+ display:flex;
+ /* #endif */
+ align-items: center;
+ justify-content: center;
+}
+.fill{
+ flex: 1;
+}
+/* input */
+.placeholder{
+ color: #999 !important;
+}
\ No newline at end of file
diff --git a/yudao-vue-ui/common/css/icon.css b/yudao-vue-ui/common/css/icon.css
new file mode 100644
index 000000000..15a177608
--- /dev/null
+++ b/yudao-vue-ui/common/css/icon.css
@@ -0,0 +1,271 @@
+@font-face {
+ font-family: "mix-icon";
+ font-weight: normal;
+ font-style: normal;
+ src: url('https://at.alicdn.com/t/font_1913318_2ui3nitf38x.ttf') format('truetype');
+}
+
+.mix-icon {
+ font-family: "mix-icon" !important;
+ font-size: 16px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-fanhui:before {
+ content: "\e7d5";
+}
+
+.icon-shoujihaoma:before {
+ content: "\e7ec";
+}
+
+.icon-close:before {
+ content: "\e60f";
+}
+
+.icon-xingbie-nv:before {
+ content: "\e60e";
+}
+
+.icon-wuliuyunshu:before {
+ content: "\e7ed";
+}
+
+.icon-jingpin:before {
+ content: "\e608";
+}
+
+.icon-zhangdanmingxi01:before {
+ content: "\e637";
+}
+
+.icon-tixian1:before {
+ content: "\e625";
+}
+
+.icon-chongzhi:before {
+ content: "\e605";
+}
+
+.icon-wodezhanghu_zijinjilu:before {
+ content: "\e615";
+}
+
+.icon-tixian:before {
+ content: "\e6ab";
+}
+
+.icon-qianbao:before {
+ content: "\e6c4";
+}
+
+.icon-guanbi1:before {
+ content: "\e61a";
+}
+
+.icon-daipingjia:before {
+ content: "\e604";
+}
+
+.icon-daifahuo:before {
+ content: "\e6bd";
+}
+
+.icon-yue:before {
+ content: "\e600";
+}
+
+.icon-wxpay:before {
+ content: "\e602";
+}
+
+.icon-alipay:before {
+ content: "\e603";
+}
+
+.icon-tishi:before {
+ content: "\e662";
+}
+
+.icon-shoucang-1:before {
+ content: "\e607";
+}
+
+.icon-gouwuche:before {
+ content: "\e657";
+}
+
+.icon-shoucang:before {
+ content: "\e645";
+}
+
+.icon-home:before {
+ content: "\e60c";
+}
+
+.icon-bangzhu1:before {
+ content: "\e63d";
+}
+
+.icon-xingxing:before {
+ content: "\e70b";
+}
+
+.icon-shuxiangliebiao:before {
+ content: "\e635";
+}
+
+.icon-hengxiangliebiao:before {
+ content: "\e636";
+}
+
+.icon-guanbi2:before {
+ content: "\e7be";
+}
+
+.icon-down:before {
+ content: "\e65c";
+}
+
+.icon-arrow-top:before {
+ content: "\e63e";
+}
+
+.icon-xiaoxi:before {
+ content: "\e634";
+}
+
+.icon-saoma:before {
+ content: "\e655";
+}
+
+.icon-dizhi1:before {
+ content: "\e618";
+}
+
+.icon-ditu-copy:before {
+ content: "\e609";
+}
+
+.icon-lajitong:before {
+ content: "\e682";
+}
+
+.icon-bianji:before {
+ content: "\e60d";
+}
+
+.icon-yanzhengma1:before {
+ content: "\e613";
+}
+
+.icon-yanjing:before {
+ content: "\e65b";
+}
+
+.icon-mima:before {
+ content: "\e628";
+}
+
+.icon-biyan:before {
+ content: "\e633";
+}
+
+.icon-iconfontweixin:before {
+ content: "\e611";
+}
+
+.icon-shouye:before {
+ content: "\e626";
+}
+
+.icon-daifukuan:before {
+ content: "\e68f";
+}
+
+.icon-pinglun-copy:before {
+ content: "\e612";
+}
+
+.icon-lishijilu:before {
+ content: "\e6b9";
+}
+
+.icon-shoucang_xuanzhongzhuangtai:before {
+ content: "\e6a9";
+}
+
+.icon-share:before {
+ content: "\e656";
+}
+
+.icon-shezhi1:before {
+ content: "\e61d";
+}
+
+.icon-shouhoutuikuan:before {
+ content: "\e631";
+}
+
+.icon-dizhi:before {
+ content: "\e614";
+}
+
+.icon-yishouhuo:before {
+ content: "\e71a";
+}
+
+.icon-xuanzhong:before {
+ content: "\e632";
+}
+
+.icon-xiangzuo:before {
+ content: "\e653";
+}
+
+.icon-iconfontxingxing:before {
+ content: "\e6b0";
+}
+
+.icon-jia2:before {
+ content: "\e60a";
+}
+
+.icon-sousuo:before {
+ content: "\e7ce";
+}
+
+.icon-xiala:before {
+ content: "\e644";
+}
+
+.icon-xia:before {
+ content: "\e62d";
+}
+
+.icon--jianhao:before {
+ content: "\e60b";
+}
+
+.icon-you:before {
+ content: "\e606";
+}
+
+.icon-yk_yuanquan:before {
+ content: "\e601";
+}
+
+.icon-xing:before {
+ content: "\e627";
+}
+
+.icon-guanbi:before {
+ content: "\e71d";
+}
+
+.icon-loading:before {
+ content: "\e646";
+}
+
diff --git a/yudao-vue-ui/components/jyf-parser/jyf-parser.vue b/yudao-vue-ui/components/jyf-parser/jyf-parser.vue
new file mode 100644
index 000000000..01484f9d2
--- /dev/null
+++ b/yudao-vue-ui/components/jyf-parser/jyf-parser.vue
@@ -0,0 +1,630 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/jyf-parser/libs/CssHandler.js b/yudao-vue-ui/components/jyf-parser/libs/CssHandler.js
new file mode 100644
index 000000000..8000377d1
--- /dev/null
+++ b/yudao-vue-ui/components/jyf-parser/libs/CssHandler.js
@@ -0,0 +1,97 @@
+const cfg = require('./config.js'),
+ isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+
+function CssHandler(tagStyle) {
+ var styles = Object.assign(Object.create(null), cfg.userAgentStyles);
+ for (var item in tagStyle)
+ styles[item] = (styles[item] ? styles[item] + ';' : '') + tagStyle[item];
+ this.styles = styles;
+}
+CssHandler.prototype.getStyle = function(data) {
+ this.styles = new parser(data, this.styles).parse();
+}
+CssHandler.prototype.match = function(name, attrs) {
+ var tmp, matched = (tmp = this.styles[name]) ? tmp + ';' : '';
+ if (attrs.class) {
+ var items = attrs.class.split(' ');
+ for (var i = 0, item; item = items[i]; i++)
+ if (tmp = this.styles['.' + item])
+ matched += tmp + ';';
+ }
+ if (tmp = this.styles['#' + attrs.id])
+ matched += tmp + ';';
+ return matched;
+}
+module.exports = CssHandler;
+
+function parser(data, init) {
+ this.data = data;
+ this.floor = 0;
+ this.i = 0;
+ this.list = [];
+ this.res = init;
+ this.state = this.Space;
+}
+parser.prototype.parse = function() {
+ for (var c; c = this.data[this.i]; this.i++)
+ this.state(c);
+ return this.res;
+}
+parser.prototype.section = function() {
+ return this.data.substring(this.start, this.i);
+}
+// 状态机
+parser.prototype.Space = function(c) {
+ if (c == '.' || c == '#' || isLetter(c)) {
+ this.start = this.i;
+ this.state = this.Name;
+ } else if (c == '/' && this.data[this.i + 1] == '*')
+ this.Comment();
+ else if (!cfg.blankChar[c] && c != ';')
+ this.state = this.Ignore;
+}
+parser.prototype.Comment = function() {
+ this.i = this.data.indexOf('*/', this.i) + 1;
+ if (!this.i) this.i = this.data.length;
+ this.state = this.Space;
+}
+parser.prototype.Ignore = function(c) {
+ if (c == '{') this.floor++;
+ else if (c == '}' && !--this.floor) this.state = this.Space;
+}
+parser.prototype.Name = function(c) {
+ if (cfg.blankChar[c]) {
+ this.list.push(this.section());
+ this.state = this.NameSpace;
+ } else if (c == '{') {
+ this.list.push(this.section());
+ this.Content();
+ } else if (c == ',') {
+ this.list.push(this.section());
+ this.Comma();
+ } else if (!isLetter(c) && (c < '0' || c > '9') && c != '-' && c != '_')
+ this.state = this.Ignore;
+}
+parser.prototype.NameSpace = function(c) {
+ if (c == '{') this.Content();
+ else if (c == ',') this.Comma();
+ else if (!cfg.blankChar[c]) this.state = this.Ignore;
+}
+parser.prototype.Comma = function() {
+ while (cfg.blankChar[this.data[++this.i]]);
+ if (this.data[this.i] == '{') this.Content();
+ else {
+ this.start = this.i--;
+ this.state = this.Name;
+ }
+}
+parser.prototype.Content = function() {
+ this.start = ++this.i;
+ if ((this.i = this.data.indexOf('}', this.i)) == -1) this.i = this.data.length;
+ var content = this.section();
+ for (var i = 0, item; item = this.list[i++];)
+ if (this.res[item]) this.res[item] += ';' + content;
+ else this.res[item] = content;
+ this.list = [];
+ this.state = this.Space;
+}
diff --git a/yudao-vue-ui/components/jyf-parser/libs/MpHtmlParser.js b/yudao-vue-ui/components/jyf-parser/libs/MpHtmlParser.js
new file mode 100644
index 000000000..8911e36d3
--- /dev/null
+++ b/yudao-vue-ui/components/jyf-parser/libs/MpHtmlParser.js
@@ -0,0 +1,534 @@
+/**
+ * html 解析器
+ * @tutorial https://github.com/jin-yufeng/Parser
+ * @version 20200719
+ * @author JinYufeng
+ * @listens MIT
+ */
+const cfg = require('./config.js'),
+ blankChar = cfg.blankChar,
+ CssHandler = require('./CssHandler.js'),
+ windowWidth = uni.getSystemInfoSync().windowWidth;
+var emoji;
+
+function MpHtmlParser(data, options = {}) {
+ this.attrs = {};
+ this.CssHandler = new CssHandler(options.tagStyle, windowWidth);
+ this.data = data;
+ this.domain = options.domain;
+ this.DOM = [];
+ this.i = this.start = this.audioNum = this.imgNum = this.videoNum = 0;
+ options.prot = (this.domain || '').includes('://') ? this.domain.split('://')[0] : 'http';
+ this.options = options;
+ this.state = this.Text;
+ this.STACK = [];
+ // 工具函数
+ this.bubble = () => {
+ for (var i = this.STACK.length, item; item = this.STACK[--i];) {
+ if (cfg.richOnlyTags[item.name]) {
+ if (item.name == 'table' && !Object.hasOwnProperty.call(item, 'c')) item.c = 1;
+ return false;
+ }
+ item.c = 1;
+ }
+ return true;
+ }
+ this.decode = (val, amp) => {
+ var i = -1,
+ j, en;
+ while (1) {
+ if ((i = val.indexOf('&', i + 1)) == -1) break;
+ if ((j = val.indexOf(';', i + 2)) == -1) break;
+ if (val[i + 1] == '#') {
+ en = parseInt((val[i + 2] == 'x' ? '0' : '') + val.substring(i + 2, j));
+ if (!isNaN(en)) val = val.substr(0, i) + String.fromCharCode(en) + val.substr(j + 1);
+ } else {
+ en = val.substring(i + 1, j);
+ if (cfg.entities[en] || en == amp)
+ val = val.substr(0, i) + (cfg.entities[en] || '&') + val.substr(j + 1);
+ }
+ }
+ return val;
+ }
+ this.getUrl = url => {
+ if (url[0] == '/') {
+ if (url[1] == '/') url = this.options.prot + ':' + url;
+ else if (this.domain) url = this.domain + url;
+ } else if (this.domain && url.indexOf('data:') != 0 && !url.includes('://'))
+ url = this.domain + '/' + url;
+ return url;
+ }
+ this.isClose = () => this.data[this.i] == '>' || (this.data[this.i] == '/' && this.data[this.i + 1] == '>');
+ this.section = () => this.data.substring(this.start, this.i);
+ this.parent = () => this.STACK[this.STACK.length - 1];
+ this.siblings = () => this.STACK.length ? this.parent().children : this.DOM;
+}
+MpHtmlParser.prototype.parse = function() {
+ if (emoji) this.data = emoji.parseEmoji(this.data);
+ for (var c; c = this.data[this.i]; this.i++)
+ this.state(c);
+ if (this.state == this.Text) this.setText();
+ while (this.STACK.length) this.popNode(this.STACK.pop());
+ return this.DOM;
+}
+// 设置属性
+MpHtmlParser.prototype.setAttr = function() {
+ var name = this.attrName.toLowerCase(),
+ val = this.attrVal;
+ if (cfg.boolAttrs[name]) this.attrs[name] = 'T';
+ else if (val) {
+ if (name == 'src' || (name == 'data-src' && !this.attrs.src)) this.attrs.src = this.getUrl(this.decode(val, 'amp'));
+ else if (name == 'href' || name == 'style') this.attrs[name] = this.decode(val, 'amp');
+ else if (name.substr(0, 5) != 'data-') this.attrs[name] = val;
+ }
+ this.attrVal = '';
+ while (blankChar[this.data[this.i]]) this.i++;
+ if (this.isClose()) this.setNode();
+ else {
+ this.start = this.i;
+ this.state = this.AttrName;
+ }
+}
+// 设置文本节点
+MpHtmlParser.prototype.setText = function() {
+ var back, text = this.section();
+ if (!text) return;
+ text = (cfg.onText && cfg.onText(text, () => back = true)) || text;
+ if (back) {
+ this.data = this.data.substr(0, this.start) + text + this.data.substr(this.i);
+ let j = this.start + text.length;
+ for (this.i = this.start; this.i < j; this.i++) this.state(this.data[this.i]);
+ return;
+ }
+ if (!this.pre) {
+ // 合并空白符
+ var tmp = [];
+ for (let i = text.length, c; c = text[--i];)
+ if (!blankChar[c] || (!blankChar[tmp[0]] && (c = ' '))) tmp.unshift(c);
+ text = tmp.join('');
+ }
+ this.siblings().push({
+ type: 'text',
+ text: this.decode(text)
+ });
+}
+// 设置元素节点
+MpHtmlParser.prototype.setNode = function() {
+ var node = {
+ name: this.tagName.toLowerCase(),
+ attrs: this.attrs
+ },
+ close = cfg.selfClosingTags[node.name];
+ this.attrs = {};
+ if (!cfg.ignoreTags[node.name]) {
+ // 处理属性
+ var attrs = node.attrs,
+ style = this.CssHandler.match(node.name, attrs, node) + (attrs.style || ''),
+ styleObj = {};
+ if (attrs.id) {
+ if (this.options.compress & 1) attrs.id = void 0;
+ else if (this.options.useAnchor) this.bubble();
+ }
+ if ((this.options.compress & 2) && attrs.class) attrs.class = void 0;
+ switch (node.name) {
+ case 'a':
+ case 'ad': // #ifdef APP-PLUS
+ case 'iframe':
+ // #endif
+ this.bubble();
+ break;
+ case 'font':
+ if (attrs.color) {
+ styleObj['color'] = attrs.color;
+ attrs.color = void 0;
+ }
+ if (attrs.face) {
+ styleObj['font-family'] = attrs.face;
+ attrs.face = void 0;
+ }
+ if (attrs.size) {
+ var size = parseInt(attrs.size);
+ if (size < 1) size = 1;
+ else if (size > 7) size = 7;
+ var map = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
+ styleObj['font-size'] = map[size - 1];
+ attrs.size = void 0;
+ }
+ break;
+ case 'embed':
+ // #ifndef APP-PLUS
+ var src = node.attrs.src || '',
+ type = node.attrs.type || '';
+ if (type.includes('video') || src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8'))
+ node.name = 'video';
+ else if (type.includes('audio') || src.includes('.m4a') || src.includes('.wav') || src.includes('.mp3') || src.includes(
+ '.aac'))
+ node.name = 'audio';
+ else break;
+ if (node.attrs.autostart)
+ node.attrs.autoplay = 'T';
+ node.attrs.controls = 'T';
+ // #endif
+ // #ifdef APP-PLUS
+ this.bubble();
+ break;
+ // #endif
+ case 'video':
+ case 'audio':
+ if (!attrs.id) attrs.id = node.name + (++this[`${node.name}Num`]);
+ else this[`${node.name}Num`]++;
+ if (node.name == 'video') {
+ if (this.videoNum > 3)
+ node.lazyLoad = 1;
+ if (attrs.width) {
+ styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px');
+ attrs.width = void 0;
+ }
+ if (attrs.height) {
+ styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px');
+ attrs.height = void 0;
+ }
+ }
+ attrs.source = [];
+ if (attrs.src) {
+ attrs.source.push(attrs.src);
+ attrs.src = void 0;
+ }
+ this.bubble();
+ break;
+ case 'td':
+ case 'th':
+ if (attrs.colspan || attrs.rowspan)
+ for (var k = this.STACK.length, item; item = this.STACK[--k];)
+ if (item.name == 'table') {
+ item.c = void 0;
+ break;
+ }
+ }
+ if (attrs.align) {
+ styleObj['text-align'] = attrs.align;
+ attrs.align = void 0;
+ }
+ // 压缩 style
+ var styles = style.split(';');
+ style = '';
+ for (var i = 0, len = styles.length; i < len; i++) {
+ var info = styles[i].split(':');
+ if (info.length < 2) continue;
+ let key = info[0].trim().toLowerCase(),
+ value = info.slice(1).join(':').trim();
+ if (value.includes('-webkit') || value.includes('-moz') || value.includes('-ms') || value.includes('-o') || value.includes(
+ 'safe'))
+ style += `;${key}:${value}`;
+ else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import'))
+ styleObj[key] = value;
+ }
+ if (node.name == 'img') {
+ if (attrs.src && !attrs.ignore) {
+ if (this.bubble())
+ attrs.i = (this.imgNum++).toString();
+ else attrs.ignore = 'T';
+ }
+ if (attrs.ignore) {
+ style += ';-webkit-touch-callout:none';
+ styleObj['max-width'] = '100%';
+ }
+ var width;
+ if (styleObj.width) width = styleObj.width;
+ else if (attrs.width) width = attrs.width.includes('%') ? attrs.width : attrs.width + 'px';
+ if (width) {
+ styleObj.width = width;
+ attrs.width = '100%';
+ if (parseInt(width) > windowWidth) {
+ styleObj.height = '';
+ if (attrs.height) attrs.height = void 0;
+ }
+ }
+ if (styleObj.height) {
+ attrs.height = styleObj.height;
+ styleObj.height = '';
+ } else if (attrs.height && !attrs.height.includes('%'))
+ attrs.height += 'px';
+ }
+ for (var key in styleObj) {
+ var value = styleObj[key];
+ if (!value) continue;
+ if (key.includes('flex') || key == 'order' || key == 'self-align') node.c = 1;
+ // 填充链接
+ if (value.includes('url')) {
+ var j = value.indexOf('(');
+ if (j++ != -1) {
+ while (value[j] == '"' || value[j] == "'" || blankChar[value[j]]) j++;
+ value = value.substr(0, j) + this.getUrl(value.substr(j));
+ }
+ }
+ // 转换 rpx
+ else if (value.includes('rpx'))
+ value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px');
+ else if (key == 'white-space' && value.includes('pre') && !close)
+ this.pre = node.pre = true;
+ style += `;${key}:${value}`;
+ }
+ style = style.substr(1);
+ if (style) attrs.style = style;
+ if (!close) {
+ node.children = [];
+ if (node.name == 'pre' && cfg.highlight) {
+ this.remove(node);
+ this.pre = node.pre = true;
+ }
+ this.siblings().push(node);
+ this.STACK.push(node);
+ } else if (!cfg.filter || cfg.filter(node, this) != false)
+ this.siblings().push(node);
+ } else {
+ if (!close) this.remove(node);
+ else if (node.name == 'source') {
+ var parent = this.parent();
+ if (parent && (parent.name == 'video' || parent.name == 'audio') && node.attrs.src)
+ parent.attrs.source.push(node.attrs.src);
+ } else if (node.name == 'base' && !this.domain) this.domain = node.attrs.href;
+ }
+ if (this.data[this.i] == '/') this.i++;
+ this.start = this.i + 1;
+ this.state = this.Text;
+}
+// 移除标签
+MpHtmlParser.prototype.remove = function(node) {
+ var name = node.name,
+ j = this.i;
+ // 处理 svg
+ var handleSvg = () => {
+ var src = this.data.substring(j, this.i + 1);
+ if (!node.attrs.xmlns) src = ' xmlns="http://www.w3.org/2000/svg"' + src;
+ var i = j;
+ while (this.data[j] != '<') j--;
+ src = this.data.substring(j, i).replace("viewbox", "viewBox") + src;
+ var parent = this.parent();
+ if (node.attrs.width == '100%' && parent && (parent.attrs.style || '').includes('inline'))
+ parent.attrs.style = 'width:300px;max-width:100%;' + parent.attrs.style;
+ this.siblings().push({
+ name: 'img',
+ attrs: {
+ src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
+ style: (/vertical[^;]+/.exec(node.attrs.style) || []).shift(),
+ ignore: 'T'
+ }
+ })
+ }
+ if (node.name == 'svg' && this.data[j] == '/') return handleSvg(this.i++);
+ while (1) {
+ if ((this.i = this.data.indexOf('', this.i + 1)) == -1) {
+ if (name == 'pre' || name == 'svg') this.i = j;
+ else this.i = this.data.length;
+ return;
+ }
+ this.start = (this.i += 2);
+ while (!blankChar[this.data[this.i]] && !this.isClose()) this.i++;
+ if (this.section().toLowerCase() == name) {
+ // 代码块高亮
+ if (name == 'pre') {
+ this.data = this.data.substr(0, j + 1) + cfg.highlight(this.data.substring(j + 1, this.i - 5), node.attrs) + this.data
+ .substr(this.i - 5);
+ return this.i = j;
+ } else if (name == 'style')
+ this.CssHandler.getStyle(this.data.substring(j + 1, this.i - 7));
+ else if (name == 'title')
+ this.DOM.title = this.data.substring(j + 1, this.i - 7);
+ if ((this.i = this.data.indexOf('>', this.i)) == -1) this.i = this.data.length;
+ if (name == 'svg') handleSvg();
+ return;
+ }
+ }
+}
+// 节点出栈处理
+MpHtmlParser.prototype.popNode = function(node) {
+ // 空白符处理
+ if (node.pre) {
+ node.pre = this.pre = void 0;
+ for (let i = this.STACK.length; i--;)
+ if (this.STACK[i].pre)
+ this.pre = true;
+ }
+ var siblings = this.siblings(),
+ len = siblings.length,
+ childs = node.children;
+ if (node.name == 'head' || (cfg.filter && cfg.filter(node, this) == false))
+ return siblings.pop();
+ var attrs = node.attrs;
+ // 替换一些标签名
+ if (cfg.blockTags[node.name]) node.name = 'div';
+ else if (!cfg.trustTags[node.name]) node.name = 'span';
+ // 去除块标签前后空串
+ if (node.name == 'div' || node.name == 'p' || node.name[0] == 't') {
+ if (len > 1 && siblings[len - 2].text == ' ')
+ siblings.splice(--len - 1, 1);
+ if (childs.length && childs[childs.length - 1].text == ' ')
+ childs.pop();
+ }
+ // 处理列表
+ if (node.c && (node.name == 'ul' || node.name == 'ol')) {
+ if ((node.attrs.style || '').includes('list-style:none')) {
+ for (let i = 0, child; child = childs[i++];)
+ if (child.name == 'li')
+ child.name = 'div';
+ } else if (node.name == 'ul') {
+ var floor = 1;
+ for (let i = this.STACK.length; i--;)
+ if (this.STACK[i].name == 'ul') floor++;
+ if (floor != 1)
+ for (let i = childs.length; i--;)
+ childs[i].floor = floor;
+ } else {
+ for (let i = 0, num = 1, child; child = childs[i++];)
+ if (child.name == 'li') {
+ child.type = 'ol';
+ child.num = ((num, type) => {
+ if (type == 'a') return String.fromCharCode(97 + (num - 1) % 26);
+ if (type == 'A') return String.fromCharCode(65 + (num - 1) % 26);
+ if (type == 'i' || type == 'I') {
+ num = (num - 1) % 99 + 1;
+ var one = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'],
+ ten = ['X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
+ res = (ten[Math.floor(num / 10) - 1] || '') + (one[num % 10 - 1] || '');
+ if (type == 'i') return res.toLowerCase();
+ return res;
+ }
+ return num;
+ })(num++, attrs.type) + '.';
+ }
+ }
+ }
+ // 处理表格的边框
+ if (node.name == 'table') {
+ var padding = attrs.cellpadding,
+ spacing = attrs.cellspacing,
+ border = attrs.border;
+ if (node.c) {
+ this.bubble();
+ attrs.style = (attrs.style || '') + ';display:table';
+ if (!padding) padding = 2;
+ if (!spacing) spacing = 2;
+ }
+ if (border) attrs.style = `border:${border}px solid gray;${attrs.style || ''}`;
+ if (spacing) attrs.style = `border-spacing:${spacing}px;${attrs.style || ''}`;
+ if (border || padding || node.c)
+ (function f(ns) {
+ for (var i = 0, n; n = ns[i]; i++) {
+ if (n.type == 'text') continue;
+ var style = n.attrs.style || '';
+ if (node.c && n.name[0] == 't') {
+ n.c = 1;
+ style += ';display:table-' + (n.name == 'th' || n.name == 'td' ? 'cell' : (n.name == 'tr' ? 'row' : 'row-group'));
+ }
+ if (n.name == 'th' || n.name == 'td') {
+ if (border) style = `border:${border}px solid gray;${style}`;
+ if (padding) style = `padding:${padding}px;${style}`;
+ } else f(n.children || []);
+ if (style) n.attrs.style = style;
+ }
+ })(childs)
+ if (this.options.autoscroll) {
+ var table = Object.assign({}, node);
+ node.name = 'div';
+ node.attrs = {
+ style: 'overflow:scroll'
+ }
+ node.children = [table];
+ }
+ }
+ this.CssHandler.pop && this.CssHandler.pop(node);
+ // 自动压缩
+ if (node.name == 'div' && !Object.keys(attrs).length && childs.length == 1 && childs[0].name == 'div')
+ siblings[len - 1] = childs[0];
+}
+// 状态机
+MpHtmlParser.prototype.Text = function(c) {
+ if (c == '<') {
+ var next = this.data[this.i + 1],
+ isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+ if (isLetter(next)) {
+ this.setText();
+ this.start = this.i + 1;
+ this.state = this.TagName;
+ } else if (next == '/') {
+ this.setText();
+ if (isLetter(this.data[++this.i + 1])) {
+ this.start = this.i + 1;
+ this.state = this.EndTag;
+ } else this.Comment();
+ } else if (next == '!' || next == '?') {
+ this.setText();
+ this.Comment();
+ }
+ }
+}
+MpHtmlParser.prototype.Comment = function() {
+ var key;
+ if (this.data.substring(this.i + 2, this.i + 4) == '--') key = '-->';
+ else if (this.data.substring(this.i + 2, this.i + 9) == '[CDATA[') key = ']]>';
+ else key = '>';
+ if ((this.i = this.data.indexOf(key, this.i + 2)) == -1) this.i = this.data.length;
+ else this.i += key.length - 1;
+ this.start = this.i + 1;
+ this.state = this.Text;
+}
+MpHtmlParser.prototype.TagName = function(c) {
+ if (blankChar[c]) {
+ this.tagName = this.section();
+ while (blankChar[this.data[this.i]]) this.i++;
+ if (this.isClose()) this.setNode();
+ else {
+ this.start = this.i;
+ this.state = this.AttrName;
+ }
+ } else if (this.isClose()) {
+ this.tagName = this.section();
+ this.setNode();
+ }
+}
+MpHtmlParser.prototype.AttrName = function(c) {
+ if (c == '=' || blankChar[c] || this.isClose()) {
+ this.attrName = this.section();
+ if (blankChar[c])
+ while (blankChar[this.data[++this.i]]);
+ if (this.data[this.i] == '=') {
+ while (blankChar[this.data[++this.i]]);
+ this.start = this.i--;
+ this.state = this.AttrValue;
+ } else this.setAttr();
+ }
+}
+MpHtmlParser.prototype.AttrValue = function(c) {
+ if (c == '"' || c == "'") {
+ this.start++;
+ if ((this.i = this.data.indexOf(c, this.i + 1)) == -1) return this.i = this.data.length;
+ this.attrVal = this.section();
+ this.i++;
+ } else {
+ for (; !blankChar[this.data[this.i]] && !this.isClose(); this.i++);
+ this.attrVal = this.section();
+ }
+ this.setAttr();
+}
+MpHtmlParser.prototype.EndTag = function(c) {
+ if (blankChar[c] || c == '>' || c == '/') {
+ var name = this.section().toLowerCase();
+ for (var i = this.STACK.length; i--;)
+ if (this.STACK[i].name == name) break;
+ if (i != -1) {
+ var node;
+ while ((node = this.STACK.pop()).name != name) this.popNode(node);
+ this.popNode(node);
+ } else if (name == 'p' || name == 'br')
+ this.siblings().push({
+ name,
+ attrs: {}
+ });
+ this.i = this.data.indexOf('>', this.i);
+ this.start = this.i + 1;
+ if (this.i == -1) this.i = this.data.length;
+ else this.state = this.Text;
+ }
+}
+module.exports = MpHtmlParser;
diff --git a/yudao-vue-ui/components/jyf-parser/libs/config.js b/yudao-vue-ui/components/jyf-parser/libs/config.js
new file mode 100644
index 000000000..1cfc111b5
--- /dev/null
+++ b/yudao-vue-ui/components/jyf-parser/libs/config.js
@@ -0,0 +1,93 @@
+/* 配置文件 */
+// #ifdef MP-WEIXIN
+const canIUse = wx.canIUse('editor'); // 高基础库标识,用于兼容
+// #endif
+module.exports = {
+ // 出错占位图
+ errorImg: null,
+ // 过滤器函数
+ filter: null,
+ // 代码高亮函数
+ highlight: null,
+ // 文本处理函数
+ onText: null,
+ // 实体编码列表
+ entities: {
+ quot: '"',
+ apos: "'",
+ semi: ';',
+ nbsp: '\xA0',
+ ensp: '\u2002',
+ emsp: '\u2003',
+ ndash: '–',
+ mdash: '—',
+ middot: '·',
+ lsquo: '‘',
+ rsquo: '’',
+ ldquo: '“',
+ rdquo: '”',
+ bull: '•',
+ hellip: '…'
+ },
+ blankChar: makeMap(' ,\xA0,\t,\r,\n,\f'),
+ boolAttrs: makeMap('allowfullscreen,autoplay,autostart,controls,ignore,loop,muted'),
+ // 块级标签,将被转为 div
+ blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,section' + (
+ // #ifdef MP-WEIXIN
+ canIUse ? '' :
+ // #endif
+ ',pre')),
+ // 将被移除的标签
+ ignoreTags: makeMap(
+ 'area,base,canvas,frame,input,link,map,meta,param,script,source,style,svg,textarea,title,track,wbr'
+ // #ifdef MP-WEIXIN
+ + (canIUse ? ',rp' : '')
+ // #endif
+ // #ifndef APP-PLUS
+ + ',iframe'
+ // #endif
+ ),
+ // 只能被 rich-text 显示的标签
+ richOnlyTags: makeMap('a,colgroup,fieldset,legend,table'
+ // #ifdef MP-WEIXIN
+ + (canIUse ? ',bdi,bdo,caption,rt,ruby' : '')
+ // #endif
+ ),
+ // 自闭合的标签
+ selfClosingTags: makeMap(
+ 'area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'
+ ),
+ // 信任的标签
+ 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,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'
+ // #ifdef MP-WEIXIN
+ + (canIUse ? ',bdi,bdo,caption,pre,rt,ruby' : '')
+ // #endif
+ // #ifdef APP-PLUS
+ + ',embed,iframe'
+ // #endif
+ ),
+ // 默认的标签样式
+ userAgentStyles: {
+ address: 'font-style:italic',
+ big: 'display:inline;font-size:1.2em',
+ blockquote: 'background-color:#f6f6f6;border-left:3px solid #dbdbdb;color:#6c6c6c;padding:5px 0 5px 10px',
+ 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;overflow:scroll',
+ s: 'text-decoration:line-through',
+ small: 'display:inline;font-size:0.8em',
+ u: 'text-decoration:underline'
+ }
+}
+
+function makeMap(str) {
+ var map = Object.create(null),
+ list = str.split(',');
+ for (var i = list.length; i--;)
+ map[list[i]] = true;
+ return map;
+}
diff --git a/yudao-vue-ui/components/jyf-parser/libs/handler.wxs b/yudao-vue-ui/components/jyf-parser/libs/handler.wxs
new file mode 100644
index 000000000..d3b1aaabe
--- /dev/null
+++ b/yudao-vue-ui/components/jyf-parser/libs/handler.wxs
@@ -0,0 +1,22 @@
+var inline = {
+ abbr: 1,
+ b: 1,
+ big: 1,
+ code: 1,
+ del: 1,
+ em: 1,
+ i: 1,
+ ins: 1,
+ label: 1,
+ q: 1,
+ small: 1,
+ span: 1,
+ strong: 1,
+ sub: 1,
+ sup: 1
+}
+module.exports = {
+ use: function(item) {
+ return !item.c && !inline[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1
+ }
+}
diff --git a/yudao-vue-ui/components/jyf-parser/libs/trees.vue b/yudao-vue-ui/components/jyf-parser/libs/trees.vue
new file mode 100644
index 000000000..8232aac14
--- /dev/null
+++ b/yudao-vue-ui/components/jyf-parser/libs/trees.vue
@@ -0,0 +1,500 @@
+
+
+
+
+
+
+
+
+
+ {{n.text}}
+
+ \n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{n.num}}
+
+ █
+
+ █
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mescroll-uni/components/mescroll-down.css b/yudao-vue-ui/components/mescroll-uni/components/mescroll-down.css
new file mode 100644
index 000000000..72bf106c7
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/components/mescroll-down.css
@@ -0,0 +1,55 @@
+/* 下拉刷新区域 */
+.mescroll-downwarp {
+ position: absolute;
+ top: -100%;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ text-align: center;
+}
+
+/* 下拉刷新--内容区,定位于区域底部 */
+.mescroll-downwarp .downwarp-content {
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ min-height: 60rpx;
+ padding: 20rpx 0;
+ text-align: center;
+}
+
+/* 下拉刷新--提示文本 */
+.mescroll-downwarp .downwarp-tip {
+ display: inline-block;
+ font-size: 28rpx;
+ vertical-align: middle;
+ margin-left: 16rpx;
+ /* color: gray; 已在style设置color,此处删去*/
+}
+
+/* 下拉刷新--旋转进度条 */
+.mescroll-downwarp .downwarp-progress {
+ display: inline-block;
+ width: 32rpx;
+ height: 32rpx;
+ border-radius: 50%;
+ border: 2rpx solid gray;
+ border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
+ vertical-align: middle;
+}
+
+/* 旋转动画 */
+.mescroll-downwarp .mescroll-rotate {
+ animation: mescrollDownRotate 0.6s linear infinite;
+}
+
+@keyframes mescrollDownRotate {
+ 0% {
+ transform: rotate(0deg);
+ }
+
+ 100% {
+ transform: rotate(360deg);
+ }
+}
\ No newline at end of file
diff --git a/yudao-vue-ui/components/mescroll-uni/components/mescroll-down.vue b/yudao-vue-ui/components/mescroll-uni/components/mescroll-down.vue
new file mode 100644
index 000000000..9fd1567fa
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/components/mescroll-down.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+ {{downText}}
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mescroll-uni/components/mescroll-empty.vue b/yudao-vue-ui/components/mescroll-uni/components/mescroll-empty.vue
new file mode 100644
index 000000000..fad535685
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/components/mescroll-empty.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-vue-ui/components/mescroll-uni/components/mescroll-empty1.vue b/yudao-vue-ui/components/mescroll-uni/components/mescroll-empty1.vue
new file mode 100644
index 000000000..08a3e58cb
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/components/mescroll-empty1.vue
@@ -0,0 +1,95 @@
+
+
+
+
+ {{ tip }}
+ {{ option.btnText }}
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mescroll-uni/components/mescroll-top.vue b/yudao-vue-ui/components/mescroll-uni/components/mescroll-top.vue
new file mode 100644
index 000000000..5115fd8d8
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/components/mescroll-top.vue
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mescroll-uni/components/mescroll-up.css b/yudao-vue-ui/components/mescroll-uni/components/mescroll-up.css
new file mode 100644
index 000000000..cbf48cd23
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/components/mescroll-up.css
@@ -0,0 +1,47 @@
+/* 上拉加载区域 */
+.mescroll-upwarp {
+ box-sizing: border-box;
+ min-height: 110rpx;
+ padding: 30rpx 0;
+ text-align: center;
+ clear: both;
+}
+
+/*提示文本 */
+.mescroll-upwarp .upwarp-tip,
+.mescroll-upwarp .upwarp-nodata {
+ display: inline-block;
+ font-size: 28rpx;
+ vertical-align: middle;
+ /* color: gray; 已在style设置color,此处删去*/
+}
+
+.mescroll-upwarp .upwarp-tip {
+ margin-left: 16rpx;
+}
+
+/*旋转进度条 */
+.mescroll-upwarp .upwarp-progress {
+ display: inline-block;
+ width: 32rpx;
+ height: 32rpx;
+ border-radius: 50%;
+ border: 2rpx solid gray;
+ border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
+ vertical-align: middle;
+}
+
+/* 旋转动画 */
+.mescroll-upwarp .mescroll-rotate {
+ animation: mescrollUpRotate 0.6s linear infinite;
+}
+
+@keyframes mescrollUpRotate {
+ 0% {
+ transform: rotate(0deg);
+ }
+
+ 100% {
+ transform: rotate(360deg);
+ }
+}
\ No newline at end of file
diff --git a/yudao-vue-ui/components/mescroll-uni/components/mescroll-up.vue b/yudao-vue-ui/components/mescroll-uni/components/mescroll-up.vue
new file mode 100644
index 000000000..11c2e1fb1
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/components/mescroll-up.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+ {{ mOption.textLoading }}
+
+
+ {{ mOption.textNoMore }}
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mescroll-uni/mescroll-body.css b/yudao-vue-ui/components/mescroll-uni/mescroll-body.css
new file mode 100644
index 000000000..084ba8f84
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/mescroll-body.css
@@ -0,0 +1,14 @@
+.mescroll-body {
+ position: relative; /* 下拉刷新区域相对自身定位 */
+ height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
+ overflow: hidden; /* 遮住顶部下拉刷新区域 */
+ box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+ .mescroll-safearea {
+ padding-bottom: constant(safe-area-inset-bottom);
+ padding-bottom: env(safe-area-inset-bottom);
+ }
+}
\ No newline at end of file
diff --git a/yudao-vue-ui/components/mescroll-uni/mescroll-body.vue b/yudao-vue-ui/components/mescroll-uni/mescroll-body.vue
new file mode 100644
index 000000000..695800b30
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/mescroll-body.vue
@@ -0,0 +1,344 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ mescroll.optUp.textLoading }}
+
+
+
+
+
+ 国云网络提供技术支持
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mescroll-uni/mescroll-mixins.js b/yudao-vue-ui/components/mescroll-uni/mescroll-mixins.js
new file mode 100644
index 000000000..a37973926
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/mescroll-mixins.js
@@ -0,0 +1,65 @@
+// mescroll-body 和 mescroll-uni 通用
+
+// import MescrollUni from "./mescroll-uni.vue";
+// import MescrollBody from "./mescroll-body.vue";
+
+const MescrollMixin = {
+ // components: { // 非H5端无法通过mixin注册组件, 只能在main.js中注册全局组件或具体界面中注册
+ // MescrollUni,
+ // MescrollBody
+ // },
+ data() {
+ return {
+ mescroll: null //mescroll实例对象
+ }
+ },
+ // 注册系统自带的下拉刷新 (配置down.native为true时生效, 还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+ onPullDownRefresh(){
+ this.mescroll && this.mescroll.onPullDownRefresh();
+ },
+ // 注册列表滚动事件,用于判定在顶部可下拉刷新,在指定位置可显示隐藏回到顶部按钮 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
+ onPageScroll(e) {
+ this.mescroll && this.mescroll.onPageScroll(e);
+ },
+ // 注册滚动到底部的事件,用于上拉加载 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
+ onReachBottom() {
+ this.mescroll && this.mescroll.onReachBottom();
+ },
+ methods: {
+ // mescroll组件初始化的回调,可获取到mescroll对象
+ mescrollInit(mescroll) {
+ this.mescroll = mescroll;
+ this.mescrollInitByRef(); // 兼容字节跳动小程序
+ },
+ // 以ref的方式初始化mescroll对象 (兼容字节跳动小程序: http://www.mescroll.com/qa.html?v=20200107#q26)
+ mescrollInitByRef() {
+ if(!this.mescroll || !this.mescroll.resetUpScroll){
+ let mescrollRef = this.$refs.mescrollRef;
+ if(mescrollRef) this.mescroll = mescrollRef.mescroll
+ }
+ },
+ // 下拉刷新的回调 (mixin默认resetUpScroll)
+ downCallback() {
+ if(this.mescroll.optUp.use){
+ this.mescroll.resetUpScroll()
+ }else{
+ setTimeout(()=>{
+ this.mescroll.endSuccess();
+ }, 500)
+ }
+ },
+ // 上拉加载的回调
+ upCallback() {
+ // mixin默认延时500自动结束加载
+ setTimeout(()=>{
+ this.mescroll.endErr();
+ }, 500)
+ }
+ },
+ mounted() {
+ this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
+ }
+
+}
+
+export default MescrollMixin;
diff --git a/yudao-vue-ui/components/mescroll-uni/mescroll-uni-option.js b/yudao-vue-ui/components/mescroll-uni/mescroll-uni-option.js
new file mode 100644
index 000000000..bd22e46e8
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/mescroll-uni-option.js
@@ -0,0 +1,33 @@
+// 全局配置
+// mescroll-body 和 mescroll-uni 通用
+const GlobalOption = {
+ down: {
+ // 其他down的配置参数也可以写,这里只展示了常用的配置:
+ textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+ textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+ textLoading: '加载中 ...', // 加载中的提示文本
+ offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+ native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+ },
+ up: {
+ // 其他up的配置参数也可以写,这里只展示了常用的配置:
+ textLoading: '加载中 ...', // 加载中的提示文本
+ textNoMore: '- 我也是有底线的 -', // 没有更多数据的提示文本
+ offset: 80, // 距底部多远时,触发upCallback
+ toTop: {
+ // 回到顶部按钮,需配置src才显示
+ src: "http://www.mescroll.com/img/mescroll-totop.png?v=1", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
+ offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
+ right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+ bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+ width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+ },
+ empty: {
+ use: true, // 是否显示空布局
+ icon: "/static/empty/hamster.png", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
+ tip: '~ 空空如也 ~' // 提示
+ }
+ }
+}
+
+export default GlobalOption
diff --git a/yudao-vue-ui/components/mescroll-uni/mescroll-uni.css b/yudao-vue-ui/components/mescroll-uni/mescroll-uni.css
new file mode 100644
index 000000000..39438cdf4
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/mescroll-uni.css
@@ -0,0 +1,36 @@
+.mescroll-uni-warp{
+ height: 100%;
+}
+
+.mescroll-uni-content{
+ height: 100%;
+}
+
+.mescroll-uni {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ min-height: 200rpx;
+ overflow-y: auto;
+ box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 定位的方式固定高度 */
+.mescroll-uni-fixed{
+ z-index: 1;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ width: auto; /* 使right生效 */
+ height: auto; /* 使bottom生效 */
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+ .mescroll-safearea {
+ padding-bottom: constant(safe-area-inset-bottom);
+ padding-bottom: env(safe-area-inset-bottom);
+ }
+}
diff --git a/yudao-vue-ui/components/mescroll-uni/mescroll-uni.js b/yudao-vue-ui/components/mescroll-uni/mescroll-uni.js
new file mode 100644
index 000000000..241a7d61e
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/mescroll-uni.js
@@ -0,0 +1,788 @@
+/* mescroll
+ * version 1.3.0
+ * 2020-07-10 wenju
+ * http://www.mescroll.com
+ */
+
+export default function MeScroll(options, isScrollBody) {
+ let me = this;
+ me.version = '1.3.0'; // mescroll版本号
+ me.options = options || {}; // 配置
+ me.isScrollBody = isScrollBody || false; // 滚动区域是否为原生页面滚动; 默认为scroll-view
+
+ me.isDownScrolling = false; // 是否在执行下拉刷新的回调
+ me.isUpScrolling = false; // 是否在执行上拉加载的回调
+ let hasDownCallback = me.options.down && me.options.down.callback; // 是否配置了down的callback
+
+ // 初始化下拉刷新
+ me.initDownScroll();
+ // 初始化上拉加载,则初始化
+ me.initUpScroll();
+
+ // 自动加载
+ setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+ // 自动触发下拉刷新 (只有配置了down的callback才自动触发下拉刷新)
+ if ((me.optDown.use || me.optDown.native) && me.optDown.auto && hasDownCallback) {
+ if (me.optDown.autoShowLoading) {
+ me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
+ } else {
+ me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
+ }
+ }
+ // 自动触发上拉加载
+ if(!me.isUpAutoLoad){ // 部分小程序(头条小程序)emit是异步, 会导致isUpAutoLoad判断有误, 先延时确保先执行down的callback,再执行up的callback
+ setTimeout(function(){
+ me.optUp.use && me.optUp.auto && !me.isUpAutoLoad && me.triggerUpScroll();
+ },100)
+ }
+ }, 30); // 需让me.optDown.inited和me.optUp.inited先执行
+}
+
+/* 配置参数:下拉刷新 */
+MeScroll.prototype.extendDownScroll = function(optDown) {
+ // 下拉刷新的配置
+ MeScroll.extend(optDown, {
+ use: true, // 是否启用下拉刷新; 默认true
+ auto: true, // 是否在初始化完毕之后自动执行下拉刷新的回调; 默认true
+ native: false, // 是否使用系统自带的下拉刷新; 默认false; 仅mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+ autoShowLoading: false, // 如果设置auto=true(在初始化完毕之后自动执行下拉刷新的回调),那么是否显示下拉刷新的进度; 默认false
+ isLock: false, // 是否锁定下拉刷新,默认false;
+ offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+ startTop: 100, // scroll-view快速滚动到顶部时,此时的scroll-top可能大于0, 此值用于控制最大的误差
+ inOffsetRate: 1, // 在列表顶部,下拉的距离小于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
+ outOffsetRate: 0.2, // 在列表顶部,下拉的距离大于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
+ bottomOffset: 20, // 当手指touchmove位置在距离body底部20px范围内的时候结束上拉刷新,避免Webview嵌套导致touchend事件不执行
+ minAngle: 45, // 向下滑动最少偏移的角度,取值区间 [0,90];默认45度,即向下滑动的角度大于45度则触发下拉;而小于45度,将不触发下拉,避免与左右滑动的轮播等组件冲突;
+ textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+ textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+ textLoading: '加载中 ...', // 加载中的提示文本
+ bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorTop)
+ textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
+ inited: null, // 下拉刷新初始化完毕的回调
+ inOffset: null, // 下拉的距离进入offset范围内那一刻的回调
+ outOffset: null, // 下拉的距离大于offset那一刻的回调
+ onMoving: null, // 下拉过程中的回调,滑动过程一直在执行; rate下拉区域当前高度与指定距离的比值(inOffset: rate<1; outOffset: rate>=1); downHight当前下拉区域的高度
+ beforeLoading: null, // 准备触发下拉刷新的回调: 如果return true,将不触发showLoading和callback回调; 常用来完全自定义下拉刷新, 参考案例【淘宝 v6.8.0】
+ showLoading: null, // 显示下拉刷新进度的回调
+ afterLoading: null, // 显示下拉刷新进度的回调之后,马上要执行的代码 (如: 在wxs中使用)
+ beforeEndDownScroll: null, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
+ endDownScroll: null, // 结束下拉刷新的回调
+ afterEndDownScroll: null, // 结束下拉刷新的回调,马上要执行的代码 (如: 在wxs中使用)
+ callback: function(mescroll) {
+ // 下拉刷新的回调;默认重置上拉加载列表为第一页
+ mescroll.resetUpScroll();
+ }
+ })
+}
+
+/* 配置参数:上拉加载 */
+MeScroll.prototype.extendUpScroll = function(optUp) {
+ // 上拉加载的配置
+ MeScroll.extend(optUp, {
+ use: true, // 是否启用上拉加载; 默认true
+ auto: true, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
+ isLock: false, // 是否锁定上拉加载,默认false;
+ isBoth: true, // 上拉加载时,如果滑动到列表顶部是否可以同时触发下拉刷新;默认true,两者可同时触发;
+ callback: null, // 上拉加载的回调;function(page,mescroll){ }
+ page: {
+ num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
+ size: 10, // 每页数据的数量
+ time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复;
+ },
+ noMoreSize: 5, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
+ offset: 80, // 距底部多远时,触发upCallback
+ textLoading: '加载中 ...', // 加载中的提示文本
+ textNoMore: '-- 我也是有底线的 --', // 没有更多数据的提示文本
+ bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorBottom)
+ textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
+ inited: null, // 初始化完毕的回调
+ showLoading: null, // 显示加载中的回调
+ showNoMore: null, // 显示无更多数据的回调
+ hideUpScroll: null, // 隐藏上拉加载的回调
+ errDistance: 60, // endErr的时候需往上滑动一段距离,使其往下滑动时再次触发onReachBottom,仅mescroll-body生效
+ toTop: {
+ // 回到顶部按钮,需配置src才显示
+ src: null, // 图片路径,默认null (绝对路径或网络图)
+ offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000
+ duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
+ btnClick: null, // 点击按钮的回调
+ onShow: null, // 是否显示的回调
+ zIndex: 9990, // fixed定位z-index值
+ left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+ right: 20, // 到右边的距离, 默认20 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+ bottom: 120, // 到底部的距离, 默认120 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+ safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false, 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取本vue的safearea值)
+ width: 72, // 回到顶部图标的宽度, 默认72 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+ radius: "50%" // 圆角, 默认"50%" (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+ },
+ empty: {
+ use: true, // 是否显示空布局
+ icon: null, // 图标路径
+ tip: '~ 暂无相关数据 ~', // 提示
+ btnText: '', // 按钮
+ btnClick: null, // 点击按钮的回调
+ onShow: null, // 是否显示的回调
+ fixed: false, // 是否使用fixed定位,默认false; 配置fixed为true,以下的top和zIndex才生效 (transform会使fixed失效,最终会降级为absolute)
+ top: "100rpx", // fixed定位的top值 (完整的单位值,如 "10%"; "100rpx")
+ zIndex: 99 // fixed定位z-index值
+ },
+ onScroll: false // 是否监听滚动事件
+ })
+}
+
+/* 配置参数 */
+MeScroll.extend = function(userOption, defaultOption) {
+ if (!userOption) return defaultOption;
+ for (let key in defaultOption) {
+ if (userOption[key] == null) {
+ let def = defaultOption[key];
+ if (def != null && typeof def === 'object') {
+ userOption[key] = MeScroll.extend({}, def); // 深度匹配
+ } else {
+ userOption[key] = def;
+ }
+ } else if (typeof userOption[key] === 'object') {
+ MeScroll.extend(userOption[key], defaultOption[key]); // 深度匹配
+ }
+ }
+ return userOption;
+}
+
+/* 简单判断是否配置了颜色 (非透明,非白色) */
+MeScroll.prototype.hasColor = function(color) {
+ if(!color) return false;
+ let c = color.toLowerCase();
+ return c != "#fff" && c != "#ffffff" && c != "transparent" && c != "white"
+}
+
+/* -------初始化下拉刷新------- */
+MeScroll.prototype.initDownScroll = function() {
+ let me = this;
+ // 配置参数
+ me.optDown = me.options.down || {};
+ if(!me.optDown.textColor && me.hasColor(me.optDown.bgColor)) me.optDown.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
+ me.extendDownScroll(me.optDown);
+
+ // 如果是mescroll-body且配置了native,则禁止自定义的下拉刷新
+ if(me.isScrollBody && me.optDown.native){
+ me.optDown.use = false
+ }else{
+ me.optDown.native = false // 仅mescroll-body支持,mescroll-uni不支持
+ }
+
+ me.downHight = 0; // 下拉区域的高度
+
+ // 在页面中加入下拉布局
+ if (me.optDown.use && me.optDown.inited) {
+ // 初始化完毕的回调
+ setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+ me.optDown.inited(me);
+ }, 0)
+ }
+}
+
+/* 列表touchstart事件 */
+MeScroll.prototype.touchstartEvent = function(e) {
+ if (!this.optDown.use) return;
+
+ this.startPoint = this.getPoint(e); // 记录起点
+ this.startTop = this.getScrollTop(); // 记录此时的滚动条位置
+ this.startAngle = 0; // 初始角度
+ this.lastPoint = this.startPoint; // 重置上次move的点
+ this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
+ this.inTouchend = false; // 标记不是touchend
+}
+
+/* 列表touchmove事件 */
+MeScroll.prototype.touchmoveEvent = function(e) {
+ if (!this.optDown.use) return;
+ let me = this;
+
+ let scrollTop = me.getScrollTop(); // 当前滚动条的距离
+ let curPoint = me.getPoint(e); // 当前点
+
+ let moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+
+ // 向下拉 && 在顶部
+ // mescroll-body,直接判定在顶部即可
+ // scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
+ // scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
+ if (moveY > 0 && (
+ (me.isScrollBody && scrollTop <= 0)
+ ||
+ (!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
+ )) {
+ // 可下拉的条件
+ if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
+ me.optUp.isBoth))) {
+
+ // 下拉的初始角度是否在配置的范围内
+ if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+ if (me.startAngle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
+
+ // 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+ if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+ me.inTouchend = true; // 标记执行touchend
+ me.touchendEvent(); // 提前触发touchend
+ return;
+ }
+
+ me.preventDefault(e); // 阻止默认事件
+
+ let diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
+
+ // 下拉距离 < 指定距离
+ if (me.downHight < me.optDown.offset) {
+ if (me.movetype !== 1) {
+ me.movetype = 1; // 加入标记,保证只执行一次
+ me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
+ me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+ }
+ me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
+
+ // 指定距离 <= 下拉距离
+ } else {
+ if (me.movetype !== 2) {
+ me.movetype = 2; // 加入标记,保证只执行一次
+ me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
+ me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+ }
+ if (diff > 0) { // 向下拉
+ me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
+ } else { // 向上收
+ me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+ }
+ }
+
+ me.downHight = Math.round(me.downHight) // 取整
+ let rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
+ me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
+ }
+ }
+
+ me.lastPoint = curPoint; // 记录本次移动的点
+}
+
+/* 列表touchend事件 */
+MeScroll.prototype.touchendEvent = function(e) {
+ if (!this.optDown.use) return;
+ // 如果下拉区域高度已改变,则需重置回来
+ if (this.isMoveDown) {
+ if (this.downHight >= this.optDown.offset) {
+ // 符合触发刷新的条件
+ this.triggerDownScroll();
+ } else {
+ // 不符合的话 则重置
+ this.downHight = 0;
+ this.endDownScrollCall(this);
+ }
+ this.movetype = 0;
+ this.isMoveDown = false;
+ } else if (!this.isScrollBody && this.getScrollTop() === this.startTop) { // scroll-view到顶/左/右/底的滑动事件
+ let isScrollUp = this.getPoint(e).y - this.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+ // 上滑
+ if (isScrollUp) {
+ // 需检查滑动的角度
+ let angle = this.getAngle(this.getPoint(e), this.startPoint); // 两点之间的角度,区间 [0,90]
+ if (angle > 80) {
+ // 检查并触发上拉
+ this.triggerUpScroll(true);
+ }
+ }
+ }
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+MeScroll.prototype.getPoint = function(e) {
+ if (!e) {
+ return {
+ x: 0,
+ y: 0
+ }
+ }
+ if (e.touches && e.touches[0]) {
+ return {
+ x: e.touches[0].pageX,
+ y: e.touches[0].pageY
+ }
+ } else if (e.changedTouches && e.changedTouches[0]) {
+ return {
+ x: e.changedTouches[0].pageX,
+ y: e.changedTouches[0].pageY
+ }
+ } else {
+ return {
+ x: e.clientX,
+ y: e.clientY
+ }
+ }
+}
+
+/* 计算两点之间的角度: 区间 [0,90]*/
+MeScroll.prototype.getAngle = function(p1, p2) {
+ let x = Math.abs(p1.x - p2.x);
+ let y = Math.abs(p1.y - p2.y);
+ let z = Math.sqrt(x * x + y * y);
+ let angle = 0;
+ if (z !== 0) {
+ angle = Math.asin(y / z) / Math.PI * 180;
+ }
+ return angle
+}
+
+/* 触发下拉刷新 */
+MeScroll.prototype.triggerDownScroll = function() {
+ if (this.optDown.beforeLoading && this.optDown.beforeLoading(this)) {
+ //return true则处于完全自定义状态
+ } else {
+ this.showDownScroll(); // 下拉刷新中...
+ !this.optDown.native && this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+ }
+}
+
+/* 显示下拉进度布局 */
+MeScroll.prototype.showDownScroll = function() {
+ this.isDownScrolling = true; // 标记下拉中
+ if (this.optDown.native) {
+ uni.startPullDownRefresh(); // 系统自带的下拉刷新
+ this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
+ } else{
+ this.downHight = this.optDown.offset; // 更新下拉区域高度
+ this.showDownLoadingCall(this.downHight); // 下拉刷新中...
+ }
+}
+
+MeScroll.prototype.showDownLoadingCall = function(downHight) {
+ this.optDown.showLoading && this.optDown.showLoading(this, downHight); // 下拉刷新中...
+ this.optDown.afterLoading && this.optDown.afterLoading(this, downHight); // 下拉刷新中...触发之后马上要执行的代码
+}
+
+/* 显示系统自带的下拉刷新时需要处理的业务 */
+MeScroll.prototype.onPullDownRefresh = function() {
+ this.isDownScrolling = true; // 标记下拉中
+ this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
+ this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+}
+
+/* 结束下拉刷新 */
+MeScroll.prototype.endDownScroll = function() {
+ if (this.optDown.native) { // 结束原生下拉刷新
+ this.isDownScrolling = false;
+ this.endDownScrollCall(this);
+ uni.stopPullDownRefresh();
+ return
+ }
+ let me = this;
+ // 结束下拉刷新的方法
+ let endScroll = function() {
+ me.downHight = 0;
+ me.isDownScrolling = false;
+ me.endDownScrollCall(me);
+ if(!me.isScrollBody){
+ me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
+ me.scrollTo(0,0) // scroll-view需重置滚动条到顶部,避免startTop大于0时,对下拉刷新的影响
+ }
+ }
+ // 结束下拉刷新时的回调
+ let delay = 0;
+ if (me.optDown.beforeEndDownScroll) delay = me.optDown.beforeEndDownScroll(me); // 结束下拉刷新的延时,单位ms
+ if (typeof delay === 'number' && delay > 0) {
+ setTimeout(endScroll, delay);
+ } else {
+ endScroll();
+ }
+}
+
+MeScroll.prototype.endDownScrollCall = function() {
+ this.optDown.endDownScroll && this.optDown.endDownScroll(this);
+ this.optDown.afterEndDownScroll && this.optDown.afterEndDownScroll(this);
+}
+
+/* 锁定下拉刷新:isLock=ture,null锁定;isLock=false解锁 */
+MeScroll.prototype.lockDownScroll = function(isLock) {
+ if (isLock == null) isLock = true;
+ this.optDown.isLock = isLock;
+}
+
+/* 锁定上拉加载:isLock=ture,null锁定;isLock=false解锁 */
+MeScroll.prototype.lockUpScroll = function(isLock) {
+ if (isLock == null) isLock = true;
+ this.optUp.isLock = isLock;
+}
+
+/* -------初始化上拉加载------- */
+MeScroll.prototype.initUpScroll = function() {
+ let me = this;
+ // 配置参数
+ me.optUp = me.options.up || {use: false}
+ if(!me.optUp.textColor && me.hasColor(me.optUp.bgColor)) me.optUp.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
+ me.extendUpScroll(me.optUp);
+
+ if (me.optUp.use === false) return; // 配置不使用上拉加载时,则不初始化上拉布局
+ me.optUp.hasNext = true; // 如果使用上拉,则默认有下一页
+ me.startNum = me.optUp.page.num + 1; // 记录page开始的页码
+
+ // 初始化完毕的回调
+ if (me.optUp.inited) {
+ setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+ me.optUp.inited(me);
+ }, 0)
+ }
+}
+
+/*滚动到底部的事件 (仅mescroll-body生效)*/
+MeScroll.prototype.onReachBottom = function() {
+ if (this.isScrollBody && !this.isUpScrolling) { // 只能支持下拉刷新的时候同时可以触发上拉加载,否则滚动到底部就需要上滑一点才能触发onReachBottom
+ if (!this.optUp.isLock && this.optUp.hasNext) {
+ this.triggerUpScroll();
+ }
+ }
+}
+
+/*列表滚动事件 (仅mescroll-body生效)*/
+MeScroll.prototype.onPageScroll = function(e) {
+ if (!this.isScrollBody) return;
+
+ // 更新滚动条的位置 (主要用于判断下拉刷新时,滚动条是否在顶部)
+ this.setScrollTop(e.scrollTop);
+
+ // 顶部按钮的显示隐藏
+ if (e.scrollTop >= this.optUp.toTop.offset) {
+ this.showTopBtn();
+ } else {
+ this.hideTopBtn();
+ }
+}
+
+/*列表滚动事件*/
+MeScroll.prototype.scroll = function(e, onScroll) {
+ // 更新滚动条的位置
+ this.setScrollTop(e.scrollTop);
+ // 更新滚动内容高度
+ this.setScrollHeight(e.scrollHeight);
+
+ // 向上滑还是向下滑动
+ if (this.preScrollY == null) this.preScrollY = 0;
+ this.isScrollUp = e.scrollTop - this.preScrollY > 0;
+ this.preScrollY = e.scrollTop;
+
+ // 上滑 && 检查并触发上拉
+ this.isScrollUp && this.triggerUpScroll(true);
+
+ // 顶部按钮的显示隐藏
+ if (e.scrollTop >= this.optUp.toTop.offset) {
+ this.showTopBtn();
+ } else {
+ this.hideTopBtn();
+ }
+
+ // 滑动监听
+ this.optUp.onScroll && onScroll && onScroll()
+}
+
+/* 触发上拉加载 */
+MeScroll.prototype.triggerUpScroll = function(isCheck) {
+ if (!this.isUpScrolling && this.optUp.use && this.optUp.callback) {
+ // 是否校验在底部; 默认不校验
+ if (isCheck === true) {
+ let canUp = false;
+ // 还有下一页 && 没有锁定 && 不在下拉中
+ if (this.optUp.hasNext && !this.optUp.isLock && !this.isDownScrolling) {
+ if (this.getScrollBottom() <= this.optUp.offset) { // 到底部
+ canUp = true; // 标记可上拉
+ }
+ }
+ if (canUp === false) return;
+ }
+ this.showUpScroll(); // 上拉加载中...
+ this.optUp.page.num++; // 预先加一页,如果失败则减回
+ this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
+ this.num = this.optUp.page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
+ this.size = this.optUp.page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
+ this.time = this.optUp.page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
+ this.optUp.callback(this); // 执行回调,联网加载数据
+ }
+}
+
+/* 显示上拉加载中 */
+MeScroll.prototype.showUpScroll = function() {
+ this.isUpScrolling = true; // 标记上拉加载中
+ this.optUp.showLoading && this.optUp.showLoading(this); // 回调
+}
+
+/* 显示上拉无更多数据 */
+MeScroll.prototype.showNoMore = function() {
+ this.optUp.hasNext = false; // 标记无更多数据
+ this.optUp.showNoMore && this.optUp.showNoMore(this); // 回调
+}
+
+/* 隐藏上拉区域**/
+MeScroll.prototype.hideUpScroll = function() {
+ this.optUp.hideUpScroll && this.optUp.hideUpScroll(this); // 回调
+}
+
+/* 结束上拉加载 */
+MeScroll.prototype.endUpScroll = function(isShowNoMore) {
+ if (isShowNoMore != null) { // isShowNoMore=null,不处理下拉状态,下拉刷新的时候调用
+ if (isShowNoMore) {
+ this.showNoMore(); // isShowNoMore=true,显示无更多数据
+ } else {
+ this.hideUpScroll(); // isShowNoMore=false,隐藏上拉加载
+ }
+ }
+ this.isUpScrolling = false; // 标记结束上拉加载
+}
+
+/* 重置上拉加载列表为第一页
+ *isShowLoading 是否显示进度布局;
+ * 1.默认null,不传参,则显示上拉加载的进度布局
+ * 2.传参true, 则显示下拉刷新的进度布局
+ * 3.传参false,则不显示上拉和下拉的进度 (常用于静默更新列表数据)
+ */
+MeScroll.prototype.resetUpScroll = function(isShowLoading) {
+ if (this.optUp && this.optUp.use) {
+ let page = this.optUp.page;
+ this.prePageNum = page.num; // 缓存重置前的页码,加载失败可退回
+ this.prePageTime = page.time; // 缓存重置前的时间,加载失败可退回
+ page.num = this.startNum; // 重置为第一页
+ page.time = null; // 重置时间为空
+ if (!this.isDownScrolling && isShowLoading !== false) { // 如果不是下拉刷新触发的resetUpScroll并且不配置列表静默更新,则显示进度;
+ if (isShowLoading == null) {
+ this.removeEmpty(); // 移除空布局
+ this.showUpScroll(); // 不传参,默认显示上拉加载的进度布局
+ } else {
+ this.showDownScroll(); // 传true,显示下拉刷新的进度布局,不清空列表
+ }
+ }
+ this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
+ this.num = page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
+ this.size = page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
+ this.time = page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
+ this.optUp.callback && this.optUp.callback(this); // 执行上拉回调
+ }
+}
+
+/* 设置page.num的值 */
+MeScroll.prototype.setPageNum = function(num) {
+ this.optUp.page.num = num - 1;
+}
+
+/* 设置page.size的值 */
+MeScroll.prototype.setPageSize = function(size) {
+ this.optUp.page.size = size;
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据量(必传)
+ * totalPage: 总页数(必传)
+ * systime: 服务器时间 (可空)
+ */
+MeScroll.prototype.endByPage = function(dataSize, totalPage, systime) {
+ let hasNext;
+ if (this.optUp.use && totalPage != null) hasNext = this.optUp.page.num < totalPage; // 是否还有下一页
+ this.endSuccess(dataSize, hasNext, systime);
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据量(必传)
+ * totalSize: 列表所有数据总数量(必传)
+ * systime: 服务器时间 (可空)
+ */
+MeScroll.prototype.endBySize = function(dataSize, totalSize, systime) {
+ let hasNext;
+ if (this.optUp.use && totalSize != null) {
+ let loadSize = (this.optUp.page.num - 1) * this.optUp.page.size + dataSize; // 已加载的数据总数
+ hasNext = loadSize < totalSize; // 是否还有下一页
+ }
+ this.endSuccess(dataSize, hasNext, systime);
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据个数(不是所有页的数据总和),用于上拉加载判断是否还有下一页.如果不传,则会判断还有下一页
+ * hasNext: 是否还有下一页,布尔类型;用来解决这个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据dataSize判断,则需翻到第三页才会知道无更多数据,如果传了hasNext,则翻到第二页即可显示无更多数据.
+ * systime: 服务器时间(可空);用来解决这个小问题:当准备翻下一页时,数据库新增了几条记录,此时翻下一页,前面的几条数据会和上一页的重复;这里传入了systime,那么upCallback的page.time就会有值,把page.time传给服务器,让后台过滤新加入的那几条记录
+ */
+MeScroll.prototype.endSuccess = function(dataSize, hasNext, systime) {
+ let me = this;
+ // 结束下拉刷新
+ if (me.isDownScrolling) me.endDownScroll();
+
+ // 结束上拉加载
+ if (me.optUp.use) {
+ let isShowNoMore; // 是否已无更多数据
+ if (dataSize != null) {
+ let pageNum = me.optUp.page.num; // 当前页码
+ let pageSize = me.optUp.page.size; // 每页长度
+ // 如果是第一页
+ if (pageNum === 1) {
+ if (systime) me.optUp.page.time = systime; // 设置加载列表数据第一页的时间
+ }
+ if (dataSize < pageSize || hasNext === false) {
+ // 返回的数据不满一页时,则说明已无更多数据
+ me.optUp.hasNext = false;
+ if (dataSize === 0 && pageNum === 1) {
+ // 如果第一页无任何数据且配置了空布局
+ isShowNoMore = false;
+ me.showEmpty();
+ } else {
+ // 总列表数少于配置的数量,则不显示无更多数据
+ let allDataSize = (pageNum - 1) * pageSize + dataSize;
+ if (allDataSize < me.optUp.noMoreSize) {
+ isShowNoMore = false;
+ } else {
+ isShowNoMore = true;
+ }
+ me.removeEmpty(); // 移除空布局
+ }
+ } else {
+ // 还有下一页
+ isShowNoMore = false;
+ me.optUp.hasNext = true;
+ me.removeEmpty(); // 移除空布局
+ }
+ }
+
+ // 隐藏上拉
+ me.endUpScroll(isShowNoMore);
+ }
+}
+
+/* 回调失败,结束下拉刷新和上拉加载 */
+MeScroll.prototype.endErr = function(errDistance) {
+ // 结束下拉,回调失败重置回原来的页码和时间
+ if (this.isDownScrolling) {
+ let page = this.optUp.page;
+ if (page && this.prePageNum) {
+ page.num = this.prePageNum;
+ page.time = this.prePageTime;
+ }
+ this.endDownScroll();
+ }
+ // 结束上拉,回调失败重置回原来的页码
+ if (this.isUpScrolling) {
+ this.optUp.page.num--;
+ this.endUpScroll(false);
+ // 如果是mescroll-body,则需往回滚一定距离
+ if(this.isScrollBody && errDistance !== 0){ // 不处理0
+ if(!errDistance) errDistance = this.optUp.errDistance; // 不传,则取默认
+ this.scrollTo(this.getScrollTop() - errDistance, 0) // 往上回滚的距离
+ }
+ }
+}
+
+/* 显示空布局 */
+MeScroll.prototype.showEmpty = function() {
+ this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(true)
+}
+
+/* 移除空布局 */
+MeScroll.prototype.removeEmpty = function() {
+ this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(false)
+}
+
+/* 显示回到顶部的按钮 */
+MeScroll.prototype.showTopBtn = function() {
+ if (!this.topBtnShow) {
+ this.topBtnShow = true;
+ this.optUp.toTop.onShow && this.optUp.toTop.onShow(true);
+ }
+}
+
+/* 隐藏回到顶部的按钮 */
+MeScroll.prototype.hideTopBtn = function() {
+ if (this.topBtnShow) {
+ this.topBtnShow = false;
+ this.optUp.toTop.onShow && this.optUp.toTop.onShow(false);
+ }
+}
+
+/* 获取滚动条的位置 */
+MeScroll.prototype.getScrollTop = function() {
+ return this.scrollTop || 0
+}
+
+/* 记录滚动条的位置 */
+MeScroll.prototype.setScrollTop = function(y) {
+ this.scrollTop = y;
+}
+
+/* 滚动到指定位置 */
+MeScroll.prototype.scrollTo = function(y, t) {
+ this.myScrollTo && this.myScrollTo(y, t) // scrollview需自定义回到顶部方法
+}
+
+/* 自定义scrollTo */
+MeScroll.prototype.resetScrollTo = function(myScrollTo) {
+ this.myScrollTo = myScrollTo
+}
+
+/* 滚动条到底部的距离 */
+MeScroll.prototype.getScrollBottom = function() {
+ return this.getScrollHeight() - this.getClientHeight() - this.getScrollTop()
+}
+
+/* 计步器
+ star: 开始值
+ end: 结束值
+ callback(step,timer): 回调step值,计步器timer,可自行通过window.clearInterval(timer)结束计步器;
+ t: 计步时长,传0则直接回调end值;不传则默认300ms
+ rate: 周期;不传则默认30ms计步一次
+ * */
+MeScroll.prototype.getStep = function(star, end, callback, t, rate) {
+ let diff = end - star; // 差值
+ if (t === 0 || diff === 0) {
+ callback && callback(end);
+ return;
+ }
+ t = t || 300; // 时长 300ms
+ rate = rate || 30; // 周期 30ms
+ let count = t / rate; // 次数
+ let step = diff / count; // 步长
+ let i = 0; // 计数
+ let timer = setInterval(function() {
+ if (i < count - 1) {
+ star += step;
+ callback && callback(star, timer);
+ i++;
+ } else {
+ callback && callback(end, timer); // 最后一次直接设置end,避免计算误差
+ clearInterval(timer);
+ }
+ }, rate);
+}
+
+/* 滚动容器的高度 */
+MeScroll.prototype.getClientHeight = function(isReal) {
+ let h = this.clientHeight || 0
+ if (h === 0 && isReal !== true) { // 未获取到容器的高度,可临时取body的高度 (可能会有误差)
+ h = this.getBodyHeight()
+ }
+ return h
+}
+MeScroll.prototype.setClientHeight = function(h) {
+ this.clientHeight = h;
+}
+
+/* 滚动内容的高度 */
+MeScroll.prototype.getScrollHeight = function() {
+ return this.scrollHeight || 0;
+}
+MeScroll.prototype.setScrollHeight = function(h) {
+ this.scrollHeight = h;
+}
+
+/* body的高度 */
+MeScroll.prototype.getBodyHeight = function() {
+ return this.bodyHeight || 0;
+}
+MeScroll.prototype.setBodyHeight = function(h) {
+ this.bodyHeight = h;
+}
+
+/* 阻止浏览器默认滚动事件 */
+MeScroll.prototype.preventDefault = function(e) {
+ // 小程序不支持e.preventDefault, 已在wxs中禁止
+ // app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止, 或使用renderjs禁止
+ // cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
+ if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
+}
\ No newline at end of file
diff --git a/yudao-vue-ui/components/mescroll-uni/mescroll-uni.vue b/yudao-vue-ui/components/mescroll-uni/mescroll-uni.vue
new file mode 100644
index 000000000..88925a61a
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/mescroll-uni.vue
@@ -0,0 +1,408 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{downText}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ mescroll.optUp.textLoading }}
+
+
+ {{ mescroll.optUp.textNoMore }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mescroll-uni/mixins/mescroll-comp.js b/yudao-vue-ui/components/mescroll-uni/mixins/mescroll-comp.js
new file mode 100644
index 000000000..f3763794e
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/mixins/mescroll-comp.js
@@ -0,0 +1,23 @@
+/**
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
+ * 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
+ * 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
+ */
+const MescrollCompMixin = {
+ // 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
+ onPageScroll(e) {
+ let item = this.$refs["mescrollItem"];
+ if(item && item.mescroll) item.mescroll.onPageScroll(e);
+ },
+ onReachBottom() {
+ let item = this.$refs["mescrollItem"];
+ if(item && item.mescroll) item.mescroll.onReachBottom();
+ },
+ // 当down的native: true时, 还需传递此方法进到子组件
+ onPullDownRefresh(){
+ let item = this.$refs["mescrollItem"];
+ if(item && item.mescroll) item.mescroll.onPullDownRefresh();
+ }
+}
+
+export default MescrollCompMixin;
diff --git a/yudao-vue-ui/components/mescroll-uni/mixins/mescroll-more-item.js b/yudao-vue-ui/components/mescroll-uni/mixins/mescroll-more-item.js
new file mode 100644
index 000000000..2549158ce
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/mixins/mescroll-more-item.js
@@ -0,0 +1,51 @@
+/**
+ * mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
+ */
+const MescrollMoreItemMixin = {
+ // 支付宝小程序不支持props的mixin,需写在具体的页面中
+ // #ifndef MP-ALIPAY
+ props:{
+ i: Number, // 每个tab页的专属下标
+ index: { // 当前tab的下标
+ type: Number,
+ default(){
+ return 0
+ }
+ }
+ },
+ // #endif
+ data() {
+ return {
+ downOption:{
+ auto:false // 不自动加载
+ },
+ upOption:{
+ auto:false // 不自动加载
+ },
+ isInit: false // 当前tab是否已初始化
+ }
+ },
+ watch:{
+ // 监听下标的变化
+ index(val){
+ if (this.i === val && !this.isInit) {
+ this.isInit = true; // 标记为true
+ this.mescroll && this.mescroll.triggerDownScroll();
+ }
+ }
+ },
+ methods: {
+ // mescroll组件初始化的回调,可获取到mescroll对象 (覆盖mescroll-mixins.js的mescrollInit, 为了标记isInit)
+ mescrollInit(mescroll) {
+ this.mescroll = mescroll;
+ this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序
+ // 自动加载当前tab的数据
+ if(this.i === this.index){
+ this.isInit = true; // 标记为true
+ this.mescroll.triggerDownScroll();
+ }
+ },
+ }
+}
+
+export default MescrollMoreItemMixin;
diff --git a/yudao-vue-ui/components/mescroll-uni/mixins/mescroll-more.js b/yudao-vue-ui/components/mescroll-uni/mixins/mescroll-more.js
new file mode 100644
index 000000000..142aa75d8
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/mixins/mescroll-more.js
@@ -0,0 +1,56 @@
+/**
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
+ * 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
+ * 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
+ */
+const MescrollMoreMixin = {
+ data() {
+ return {
+ tabIndex: 0 // 当前tab下标
+ }
+ },
+ // 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
+ onPageScroll(e) {
+ let mescroll = this.getMescroll(this.tabIndex);
+ mescroll && mescroll.onPageScroll(e);
+ },
+ onReachBottom() {
+ let mescroll = this.getMescroll(this.tabIndex);
+ mescroll && mescroll.onReachBottom();
+ },
+ // 当down的native: true时, 还需传递此方法进到子组件
+ onPullDownRefresh(){
+ let mescroll = this.getMescroll(this.tabIndex);
+ mescroll && mescroll.onPullDownRefresh();
+ },
+ methods:{
+ // 根据下标获取对应子组件的mescroll
+ getMescroll(i){
+ if(!this.mescrollItems) this.mescrollItems = [];
+ if(!this.mescrollItems[i]) {
+ // v-for中的refs
+ let vForItem = this.$refs["mescrollItem"];
+ if(vForItem){
+ this.mescrollItems[i] = vForItem[i]
+ }else{
+ // 普通的refs,不可重复
+ this.mescrollItems[i] = this.$refs["mescrollItem"+i];
+ }
+ }
+ let item = this.mescrollItems[i]
+ return item ? item.mescroll : null
+ },
+ // 切换tab,恢复滚动条位置
+ tabChange(i){
+ let mescroll = this.getMescroll(i);
+ if(mescroll){
+ // 延时(比$nextTick靠谱一些),确保元素已渲染
+ setTimeout(()=>{
+ mescroll.scrollTo(mescroll.getScrollTop(),0)
+ },30)
+ }
+ }
+ }
+}
+
+export default MescrollMoreMixin;
diff --git a/yudao-vue-ui/components/mescroll-uni/wxs/bounce.js b/yudao-vue-ui/components/mescroll-uni/wxs/bounce.js
new file mode 100644
index 000000000..4fe179f9a
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/wxs/bounce.js
@@ -0,0 +1,23 @@
+// bounce: iOS橡皮筋,Android半月弧,h5浏览器下拉背景等效果, 适用于h5和renderjs (下拉刷新时禁止)
+const bounce = {
+ // false: 禁止bounce; true:允许bounce
+ setBounce: function(isBounce){
+ window.$isMescrollBounce = isBounce
+ }
+}
+
+// 引入即自动初始化 (仅初始化一次)
+if(window && window.$isMescrollBounce == null){
+ // 是否允许bounce, 默认允许
+ window.$isMescrollBounce = true
+ // 每次点击时重置bounce
+ window.addEventListener('touchstart', function(){
+ window.$isMescrollBounce = true
+ }, {passive: true})
+ // 滑动中标记是否禁止bounce (如:下拉刷新时禁止)
+ window.addEventListener('touchmove', function(e){
+ !window.$isMescrollBounce && e.preventDefault() // 禁止bounce
+ }, {passive: false})
+}
+
+export default bounce;
\ No newline at end of file
diff --git a/yudao-vue-ui/components/mescroll-uni/wxs/mixins.js b/yudao-vue-ui/components/mescroll-uni/wxs/mixins.js
new file mode 100644
index 000000000..7f49ff72d
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/wxs/mixins.js
@@ -0,0 +1,102 @@
+// 定义在wxs (含renderjs) 逻辑层的数据和方法, 与视图层相互通信
+const WxsMixin = {
+ data() {
+ return {
+ // 传入wxs视图层的数据 (响应式)
+ wxsProp: {
+ optDown:{}, // 下拉刷新的配置
+ scrollTop:0, // 滚动条的距离
+ bodyHeight:0, // body的高度
+ isDownScrolling:false, // 是否正在下拉刷新中
+ isUpScrolling:false, // 是否正在上拉加载中
+ isScrollBody:true, // 是否为mescroll-body滚动
+ isUpBoth:true, // 上拉加载时,是否同时可以下拉刷新
+ t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
+ },
+
+ // 标记调用wxs视图层的方法
+ callProp: {
+ callType: '', // 方法名
+ t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
+ },
+
+ // 不用wxs的平台使用此处的wxsBiz对象,抹平wxs的写法 (微信小程序和APP使用的wxsBiz对象是./wxs/wxs.wxs)
+ // #ifndef MP-WEIXIN || APP-PLUS || H5
+ wxsBiz: {
+ //注册列表touchstart事件,用于下拉刷新
+ touchstartEvent: e=> {
+ this.mescroll.touchstartEvent(e);
+ },
+ //注册列表touchmove事件,用于下拉刷新
+ touchmoveEvent: e=> {
+ this.mescroll.touchmoveEvent(e);
+ },
+ //注册列表touchend事件,用于下拉刷新
+ touchendEvent: e=> {
+ this.mescroll.touchendEvent(e);
+ },
+ propObserver(){}, // 抹平wxs的写法
+ callObserver(){} // 抹平wxs的写法
+ },
+ // #endif
+
+ // 不用renderjs的平台使用此处的renderBiz对象,抹平renderjs的写法 (app 和 h5 使用的renderBiz对象是./wxs/renderjs.js)
+ // #ifndef APP-PLUS || H5
+ renderBiz: {
+ propObserver(){} // 抹平renderjs的写法
+ }
+ // #endif
+ }
+ },
+ methods: {
+ // wxs视图层调用逻辑层的回调
+ wxsCall(msg){
+ if(msg.type === 'setWxsProp'){
+ // 更新wxsProp数据 (值改变才触发更新)
+ this.wxsProp = {
+ optDown: this.mescroll.optDown,
+ scrollTop: this.mescroll.getScrollTop(),
+ bodyHeight: this.mescroll.getBodyHeight(),
+ isDownScrolling: this.mescroll.isDownScrolling,
+ isUpScrolling: this.mescroll.isUpScrolling,
+ isUpBoth: this.mescroll.optUp.isBoth,
+ isScrollBody:this.mescroll.isScrollBody,
+ t: Date.now()
+ }
+ }else if(msg.type === 'setLoadType'){
+ // 设置inOffset,outOffset的状态
+ this.downLoadType = msg.downLoadType
+ }else if(msg.type === 'triggerDownScroll'){
+ // 主动触发下拉刷新
+ this.mescroll.triggerDownScroll();
+ }else if(msg.type === 'endDownScroll'){
+ // 结束下拉刷新
+ this.mescroll.endDownScroll();
+ }else if(msg.type === 'triggerUpScroll'){
+ // 主动触发上拉加载
+ this.mescroll.triggerUpScroll(true);
+ }
+ }
+ },
+ mounted() {
+ // #ifdef MP-WEIXIN || APP-PLUS || H5
+ // 配置主动触发wxs显示加载进度的回调
+ this.mescroll.optDown.afterLoading = ()=>{
+ this.callProp = {callType: "showLoading", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+ }
+ // 配置主动触发wxs隐藏加载进度的回调
+ this.mescroll.optDown.afterEndDownScroll = ()=>{
+ this.callProp = {callType: "endDownScroll", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+ setTimeout(()=>{
+ if(this.downLoadType === 4 || this.downLoadType === 0){
+ this.callProp = {callType: "clearTransform", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+ }
+ },320)
+ }
+ // 初始化wxs的数据
+ this.wxsCall({type: 'setWxsProp'})
+ // #endif
+ }
+}
+
+export default WxsMixin;
diff --git a/yudao-vue-ui/components/mescroll-uni/wxs/renderjs.js b/yudao-vue-ui/components/mescroll-uni/wxs/renderjs.js
new file mode 100644
index 000000000..207f38857
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/wxs/renderjs.js
@@ -0,0 +1,92 @@
+// 使用renderjs直接操作window对象,实现动态控制app和h5的bounce
+// bounce: iOS橡皮筋,Android半月弧,h5浏览器下拉背景等效果 (下拉刷新时禁止)
+// https://uniapp.dcloud.io/frame?id=renderjs
+
+// 与wxs的me实例一致
+var me = {}
+
+// 初始化window对象的touch事件 (仅初始化一次)
+if(window && !window.$mescrollRenderInit){
+ window.$mescrollRenderInit = true
+
+
+ window.addEventListener('touchstart', function(e){
+ if (me.disabled()) return;
+ me.startPoint = me.getPoint(e); // 记录起点
+ }, {passive: true})
+
+
+ window.addEventListener('touchmove', function(e){
+ if (me.disabled()) return;
+ if (me.getScrollTop() > 0) return; // 需在顶部下拉,才禁止bounce
+
+ var curPoint = me.getPoint(e); // 当前点
+ var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+ // 向下拉
+ if (moveY > 0) {
+ // 可下拉的条件
+ if (!me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling && me.isUpBoth))) {
+
+ // 只有touch在mescroll的view上面,才禁止bounce
+ var el = e.target;
+ var isMescrollTouch = false;
+ while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
+ var cls = el.classList;
+ if (cls && cls.contains('mescroll-render-touch')) {
+ isMescrollTouch = true
+ break;
+ }
+ el = el.parentNode; // 继续检查其父元素
+ }
+ // 禁止bounce (不会对swiper和iOS侧滑返回造成影响)
+ if (isMescrollTouch && e.cancelable && !e.defaultPrevented) e.preventDefault();
+ }
+ }
+ }, {passive: false})
+}
+
+/* 获取滚动条的位置 */
+me.getScrollTop = function() {
+ return me.scrollTop || 0
+}
+
+/* 是否禁用下拉刷新 */
+me.disabled = function(){
+ return !me.optDown || !me.optDown.use || me.optDown.native
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+me.getPoint = function(e) {
+ if (!e) {
+ return {x: 0,y: 0}
+ }
+ if (e.touches && e.touches[0]) {
+ return {x: e.touches[0].pageX,y: e.touches[0].pageY}
+ } else if (e.changedTouches && e.changedTouches[0]) {
+ return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
+ } else {
+ return {x: e.clientX,y: e.clientY}
+ }
+}
+
+/**
+ * 监听逻辑层数据的变化 (实时更新数据)
+ */
+function propObserver(wxsProp) {
+ me.optDown = wxsProp.optDown
+ me.scrollTop = wxsProp.scrollTop
+ me.isDownScrolling = wxsProp.isDownScrolling
+ me.isUpScrolling = wxsProp.isUpScrolling
+ me.isUpBoth = wxsProp.isUpBoth
+}
+
+/* 导出模块 */
+const renderBiz = {
+ data() {
+ return {
+ propObserver: propObserver,
+ }
+ }
+}
+
+export default renderBiz;
\ No newline at end of file
diff --git a/yudao-vue-ui/components/mescroll-uni/wxs/wxs.wxs b/yudao-vue-ui/components/mescroll-uni/wxs/wxs.wxs
new file mode 100644
index 000000000..52c3f7fb1
--- /dev/null
+++ b/yudao-vue-ui/components/mescroll-uni/wxs/wxs.wxs
@@ -0,0 +1,267 @@
+// 使用wxs处理交互动画, 提高性能, 同时避免小程序bounce对下拉刷新的影响
+// https://uniapp.dcloud.io/frame?id=wxs
+// https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html
+
+// 模拟mescroll实例, 与mescroll.js的写法尽量保持一致
+var me = {}
+
+// ------ 自定义下拉刷新动画 start ------
+
+/* 下拉过程中的回调,滑动过程一直在执行 (rate<1为inOffset; rate>1为outOffset) */
+me.onMoving = function (ins, rate, downHight){
+ ins.requestAnimationFrame(function () {
+ ins.selectComponent('.mescroll-wxs-content').setStyle({
+ transform: 'translateY(' + downHight + 'px)',
+ transition: ''
+ })
+ // 环形进度条
+ var progress = ins.selectComponent('.mescroll-wxs-progress')
+ progress && progress.setStyle({transform: 'rotate(' + 360 * rate + 'deg)'})
+ })
+}
+
+/* 显示下拉刷新进度 */
+me.showLoading = function (ins){
+ me.downHight = me.optDown.offset
+ ins.requestAnimationFrame(function () {
+ ins.selectComponent('.mescroll-wxs-content').setStyle({
+ transform: 'translateY(' + me.downHight + 'px)',
+ transition: 'transform 300ms'
+ })
+ })
+}
+
+/* 结束下拉 */
+me.endDownScroll = function (ins){
+ me.downHight = 0;
+ me.isDownScrolling = false;
+ ins.requestAnimationFrame(function () {
+ ins.selectComponent('.mescroll-wxs-content').setStyle({
+ transform: 'translateY(0)', // 不可以写空串,否则scroll-view渲染不完整 (延时350ms会调clearTransform置空)
+ transition: 'transform 300ms'
+ })
+ })
+}
+
+/* 结束下拉动画执行完毕后, 清除transform和transition, 避免对列表内容样式造成影响, 如: h5的list-msg示例下拉进度条漏出来等 */
+me.clearTransform = function (ins){
+ ins.requestAnimationFrame(function () {
+ ins.selectComponent('.mescroll-wxs-content').setStyle({
+ transform: '',
+ transition: ''
+ })
+ })
+}
+
+// ------ 自定义下拉刷新动画 end ------
+
+/**
+ * 监听逻辑层数据的变化 (实时更新数据)
+ */
+function propObserver(wxsProp) {
+ me.optDown = wxsProp.optDown
+ me.scrollTop = wxsProp.scrollTop
+ me.bodyHeight = wxsProp.bodyHeight
+ me.isDownScrolling = wxsProp.isDownScrolling
+ me.isUpScrolling = wxsProp.isUpScrolling
+ me.isUpBoth = wxsProp.isUpBoth
+ me.isScrollBody = wxsProp.isScrollBody
+ me.startTop = wxsProp.scrollTop // 及时更新touchstart触发的startTop, 避免scroll-view快速惯性滚动到顶部取值不准确
+}
+
+/**
+ * 监听逻辑层数据的变化 (调用wxs的方法)
+ */
+function callObserver(callProp, oldValue, ins) {
+ if (me.disabled()) return;
+ if(callProp.callType){
+ // 逻辑层(App Service)的style已失效,需在视图层(Webview)设置style
+ if(callProp.callType === 'showLoading'){
+ me.showLoading(ins)
+ }else if(callProp.callType === 'endDownScroll'){
+ me.endDownScroll(ins)
+ }else if(callProp.callType === 'clearTransform'){
+ me.clearTransform(ins)
+ }
+ }
+}
+
+/**
+ * touch事件
+ */
+function touchstartEvent(e, ins) {
+ if (me.disabled()) return true;
+
+ me.downHight = 0; // 下拉的距离
+ me.startPoint = me.getPoint(e); // 记录起点
+ me.startTop = me.getScrollTop(); // 记录此时的滚动条位置
+ me.startAngle = 0; // 初始角度
+ me.lastPoint = me.startPoint; // 重置上次move的点
+ me.maxTouchmoveY = me.getBodyHeight() - me.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
+ me.inTouchend = false; // 标记不是touchend
+
+ me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
+}
+
+function touchmoveEvent(e, ins) {
+ var isPrevent = true // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
+
+ if (me.disabled()) return isPrevent;
+
+ var scrollTop = me.getScrollTop(); // 当前滚动条的距离
+ var curPoint = me.getPoint(e); // 当前点
+
+ var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+
+ // 向下拉 && 在顶部
+ // mescroll-body,直接判定在顶部即可
+ // scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
+ // scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
+ if (moveY > 0 && (
+ (me.isScrollBody && scrollTop <= 0)
+ ||
+ (!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
+ )) {
+ // 可下拉的条件
+ if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
+ me.isUpBoth))) {
+
+ // 下拉的角度是否在配置的范围内
+ if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+ if (me.startAngle < me.optDown.minAngle) return isPrevent; // 如果小于配置的角度,则不往下执行下拉刷新
+
+ // 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+ if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+ me.inTouchend = true; // 标记执行touchend
+ touchendEvent(e, ins); // 提前触发touchend
+ return isPrevent;
+ }
+
+ isPrevent = false // 小程序是return false
+
+ var diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
+
+ // 下拉距离 < 指定距离
+ if (me.downHight < me.optDown.offset) {
+ if (me.movetype !== 1) {
+ me.movetype = 1; // 加入标记,保证只执行一次
+ // me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
+ me.callMethod(ins, {type: 'setLoadType', downLoadType: 1})
+ me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+ }
+ me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
+
+ // 指定距离 <= 下拉距离
+ } else {
+ if (me.movetype !== 2) {
+ me.movetype = 2; // 加入标记,保证只执行一次
+ // me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
+ me.callMethod(ins, {type: 'setLoadType', downLoadType: 2})
+ me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+ }
+ if (diff > 0) { // 向下拉
+ me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
+ } else { // 向上收
+ me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+ }
+ }
+
+ me.downHight = Math.round(me.downHight) // 取整
+ var rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
+ // me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
+ me.onMoving(ins, rate, me.downHight)
+ }
+ }
+
+ me.lastPoint = curPoint; // 记录本次移动的点
+
+ return isPrevent // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
+}
+
+function touchendEvent(e, ins) {
+ if (me.disabled()) return true;
+ // 如果下拉区域高度已改变,则需重置回来
+ if (me.isMoveDown) {
+ if (me.downHight >= me.optDown.offset) {
+ // 符合触发刷新的条件
+ me.downHight = me.optDown.offset; // 更新下拉区域高度
+ // me.triggerDownScroll();
+ me.callMethod(ins, {type: 'triggerDownScroll'})
+ } else {
+ // 不符合的话 则重置
+ me.downHight = 0;
+ // me.optDown.endDownScroll && me.optDown.endDownScroll(me);
+ me.callMethod(ins, {type: 'endDownScroll'})
+ }
+ me.movetype = 0;
+ me.isMoveDown = false;
+ } else if (!me.isScrollBody && me.getScrollTop() === me.startTop) { // scroll-view到顶/左/右/底的滑动事件
+ var isScrollUp = me.getPoint(e).y - me.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+ // 上滑
+ if (isScrollUp) {
+ // 需检查滑动的角度
+ var angle = me.getAngle(me.getPoint(e), me.startPoint); // 两点之间的角度,区间 [0,90]
+ if (angle > 80) {
+ // 检查并触发上拉
+ // me.triggerUpScroll(true);
+ me.callMethod(ins, {type: 'triggerUpScroll'})
+ }
+ }
+ }
+ me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
+}
+
+/* 是否禁用下拉刷新 */
+me.disabled = function(){
+ return !me.optDown || !me.optDown.use || me.optDown.native
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+me.getPoint = function(e) {
+ if (!e) {
+ return {x: 0,y: 0}
+ }
+ if (e.touches && e.touches[0]) {
+ return {x: e.touches[0].pageX,y: e.touches[0].pageY}
+ } else if (e.changedTouches && e.changedTouches[0]) {
+ return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
+ } else {
+ return {x: e.clientX,y: e.clientY}
+ }
+}
+
+/* 计算两点之间的角度: 区间 [0,90]*/
+me.getAngle = function (p1, p2) {
+ var x = Math.abs(p1.x - p2.x);
+ var y = Math.abs(p1.y - p2.y);
+ var z = Math.sqrt(x * x + y * y);
+ var angle = 0;
+ if (z !== 0) {
+ angle = Math.asin(y / z) / Math.PI * 180;
+ }
+ return angle
+}
+
+/* 获取滚动条的位置 */
+me.getScrollTop = function() {
+ return me.scrollTop || 0
+}
+
+/* 获取body的高度 */
+me.getBodyHeight = function() {
+ return me.bodyHeight || 0;
+}
+
+/* 调用逻辑层的方法 */
+me.callMethod = function(ins, param) {
+ if(ins) ins.callMethod('wxsCall', param)
+}
+
+/* 导出模块 */
+module.exports = {
+ propObserver: propObserver,
+ callObserver: callObserver,
+ touchstartEvent: touchstartEvent,
+ touchmoveEvent: touchmoveEvent,
+ touchendEvent: touchendEvent
+}
\ No newline at end of file
diff --git a/yudao-vue-ui/components/mix-action-sheet/mix-action-sheet.vue b/yudao-vue-ui/components/mix-action-sheet/mix-action-sheet.vue
new file mode 100644
index 000000000..5bd13983b
--- /dev/null
+++ b/yudao-vue-ui/components/mix-action-sheet/mix-action-sheet.vue
@@ -0,0 +1,82 @@
+
+
+
+
+ {{ data.title }}
+
+
+ {{ item.text }}
+
+
+ 取消
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-button/mix-button.vue b/yudao-vue-ui/components/mix-button/mix-button.vue
new file mode 100644
index 000000000..9acfc14a2
--- /dev/null
+++ b/yudao-vue-ui/components/mix-button/mix-button.vue
@@ -0,0 +1,154 @@
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-code/mix-code.vue b/yudao-vue-ui/components/mix-code/mix-code.vue
new file mode 100644
index 000000000..d34da9d01
--- /dev/null
+++ b/yudao-vue-ui/components/mix-code/mix-code.vue
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+ {{ timeDown > 0 ? '重新获取 ' + timeDown + 's' : '获取验证码' }}
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-empty/mix-empty.vue b/yudao-vue-ui/components/mix-empty/mix-empty.vue
new file mode 100644
index 000000000..248c86295
--- /dev/null
+++ b/yudao-vue-ui/components/mix-empty/mix-empty.vue
@@ -0,0 +1,209 @@
+
+
+
+
+ {{ hasLogin ? '空空如也' : '先去登录嘛' }}
+ 别忘了买点什么犒赏一下自己哦
+
+ {{ hasLogin ? '随便逛逛' : '去登录' }}
+
+
+
+
+ 找不到您的收货地址哦,先去添加一个吧~
+
+
+
+
+
+
+ 收藏夹空空的,先去逛逛吧~
+
+ 随便逛逛
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-icon-loading/mix-icon-loading.vue b/yudao-vue-ui/components/mix-icon-loading/mix-icon-loading.vue
new file mode 100644
index 000000000..7daa641ed
--- /dev/null
+++ b/yudao-vue-ui/components/mix-icon-loading/mix-icon-loading.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-list-cell/mix-list-cell.vue b/yudao-vue-ui/components/mix-list-cell/mix-list-cell.vue
new file mode 100644
index 000000000..643a86608
--- /dev/null
+++ b/yudao-vue-ui/components/mix-list-cell/mix-list-cell.vue
@@ -0,0 +1,117 @@
+
+
+
+
+ {{ title }}
+ {{ tips }}
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-load-more/mix-load-more.vue b/yudao-vue-ui/components/mix-load-more/mix-load-more.vue
new file mode 100644
index 000000000..1cfdaabd9
--- /dev/null
+++ b/yudao-vue-ui/components/mix-load-more/mix-load-more.vue
@@ -0,0 +1,60 @@
+
+
+
+
+ {{ textList[status] }}
+
+
+
+ 国云网络提供技术支持
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-vue-ui/components/mix-loading/mix-loading.vue b/yudao-vue-ui/components/mix-loading/mix-loading.vue
new file mode 100644
index 000000000..04f26ed82
--- /dev/null
+++ b/yudao-vue-ui/components/mix-loading/mix-loading.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-modal/mix-modal.vue b/yudao-vue-ui/components/mix-modal/mix-modal.vue
new file mode 100644
index 000000000..df57bc987
--- /dev/null
+++ b/yudao-vue-ui/components/mix-modal/mix-modal.vue
@@ -0,0 +1,105 @@
+
+
+
+ {{ title }}
+
+ {{ text }}
+
+
+
+ {{ cancelText }}
+
+
+ {{ confirmText }}
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-nav-bar/mix-nav-bar.vue b/yudao-vue-ui/components/mix-nav-bar/mix-nav-bar.vue
new file mode 100644
index 000000000..8a869bc92
--- /dev/null
+++ b/yudao-vue-ui/components/mix-nav-bar/mix-nav-bar.vue
@@ -0,0 +1,139 @@
+
+
+
+
+ {{ item.name }}
+ {{ counts[index] }}
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-number-box/mix-number-box.vue b/yudao-vue-ui/components/mix-number-box/mix-number-box.vue
new file mode 100644
index 000000000..63a3aa7fd
--- /dev/null
+++ b/yudao-vue-ui/components/mix-number-box/mix-number-box.vue
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-price-view/mix-price-view.vue b/yudao-vue-ui/components/mix-price-view/mix-price-view.vue
new file mode 100644
index 000000000..182ee26ef
--- /dev/null
+++ b/yudao-vue-ui/components/mix-price-view/mix-price-view.vue
@@ -0,0 +1,53 @@
+
+
+ ¥
+ {{ priceArr[0] }}
+ .{{ priceArr[1] }}
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-timeline/mix-timeline.vue b/yudao-vue-ui/components/mix-timeline/mix-timeline.vue
new file mode 100644
index 000000000..b4c41a60f
--- /dev/null
+++ b/yudao-vue-ui/components/mix-timeline/mix-timeline.vue
@@ -0,0 +1,137 @@
+
+
+
+
+ {{ item.time | date('H:i') }}
+ {{ item.time | date('m/d') }}
+
+
+
+
+
+ {{ item.title }}
+ {{ item.tip }}
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/mix-upload-image/mix-upload-image.vue b/yudao-vue-ui/components/mix-upload-image/mix-upload-image.vue
new file mode 100644
index 000000000..b1881656b
--- /dev/null
+++ b/yudao-vue-ui/components/mix-upload-image/mix-upload-image.vue
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+ {{item.progress}}%
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/number-keyboard/number-keyboard.vue b/yudao-vue-ui/components/number-keyboard/number-keyboard.vue
new file mode 100644
index 000000000..b4d9a66fd
--- /dev/null
+++ b/yudao-vue-ui/components/number-keyboard/number-keyboard.vue
@@ -0,0 +1,186 @@
+
+
+
+ {{num.number}}
+
+ 0
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/pay-password-keyboard/pay-password-keyboard.vue b/yudao-vue-ui/components/pay-password-keyboard/pay-password-keyboard.vue
new file mode 100644
index 000000000..8719a8581
--- /dev/null
+++ b/yudao-vue-ui/components/pay-password-keyboard/pay-password-keyboard.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
+ 输入支付密码
+
+
+
+
+
+ 重置密码
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/uni-popup/popup.js b/yudao-vue-ui/components/uni-popup/popup.js
new file mode 100644
index 000000000..2a7f22f08
--- /dev/null
+++ b/yudao-vue-ui/components/uni-popup/popup.js
@@ -0,0 +1,25 @@
+import message from './message.js';
+// 定义 type 类型:弹出类型:top/bottom/center
+const config = {
+ // 顶部弹出
+ top:'top',
+ // 底部弹出
+ bottom:'bottom',
+ // 居中弹出
+ center:'center',
+ // 消息提示
+ message:'top',
+ // 对话框
+ dialog:'center',
+ // 分享
+ share:'bottom',
+}
+
+export default {
+ data(){
+ return {
+ config:config
+ }
+ },
+ mixins: [message],
+}
diff --git a/yudao-vue-ui/components/uni-popup/uni-popup.vue b/yudao-vue-ui/components/uni-popup/uni-popup.vue
new file mode 100644
index 000000000..8acb1698b
--- /dev/null
+++ b/yudao-vue-ui/components/uni-popup/uni-popup.vue
@@ -0,0 +1,302 @@
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/uni-swipe-action-item/bindingx.js b/yudao-vue-ui/components/uni-swipe-action-item/bindingx.js
new file mode 100644
index 000000000..50b92416f
--- /dev/null
+++ b/yudao-vue-ui/components/uni-swipe-action-item/bindingx.js
@@ -0,0 +1,245 @@
+const BindingX = uni.requireNativePlugin('bindingx');
+const dom = uni.requireNativePlugin('dom');
+const animation = uni.requireNativePlugin('animation');
+
+export default {
+ data() {
+ return {
+ right: 0,
+ button: [],
+ preventGesture: false
+ }
+ },
+
+ watch: {
+ show(newVal) {
+ if (!this.position || JSON.stringify(this.position) === '{}') return;
+ if (this.autoClose) return
+ if (this.isInAnimation) return
+ if (newVal) {
+ this.open()
+ } else {
+ this.close()
+ }
+ },
+ },
+ created() {
+ if (this.swipeaction.children !== undefined) {
+ this.swipeaction.children.push(this)
+ }
+ },
+ mounted() {
+ this.boxSelector = this.getEl(this.$refs['selector-box-hock']);
+ this.selector = this.getEl(this.$refs['selector-content-hock']);
+ this.buttonSelector = this.getEl(this.$refs['selector-button-hock']);
+ this.position = {}
+ this.x = 0
+ setTimeout(() => {
+ this.getSelectorQuery()
+ }, 200)
+ },
+ beforeDestroy() {
+ if (this.timing) {
+ BindingX.unbind({
+ token: this.timing.token,
+ eventType: 'timing'
+ })
+ }
+ if (this.eventpan) {
+ BindingX.unbind({
+ token: this.eventpan.token,
+ eventType: 'pan'
+ })
+ }
+ this.swipeaction.children.forEach((item, index) => {
+ if (item === this) {
+ this.swipeaction.children.splice(index, 1)
+ }
+ })
+ },
+ methods: {
+ onClick(index, item) {
+ this.$emit('click', {
+ content: item,
+ index
+ })
+ },
+ touchstart(e) {
+ if (this.isInAnimation) return
+ if (this.stop) return
+ this.stop = true
+ if (this.autoClose) {
+ this.swipeaction.closeOther(this)
+ }
+ let endWidth = this.right
+ let boxStep = `(x+${this.x})`
+ let pageX = `${boxStep}> ${-endWidth} && ${boxStep} < 0?${boxStep}:(x+${this.x} < 0? ${-endWidth}:0)`
+
+ let props = [{
+ element: this.selector,
+ property: 'transform.translateX',
+ expression: pageX
+ }]
+
+ let left = 0
+ for (let i = 0; i < this.options.length; i++) {
+ let buttonSelectors = this.getEl(this.$refs['button-hock'][i]);
+ if (this.button.length === 0 || !this.button[i] || !this.button[i].width) return
+ let moveMix = endWidth - left
+ left += this.button[i].width
+ let step = `(${this.x}+x)/${endWidth}`
+ let moveX = `(${step}) * ${moveMix}`
+ let pageButtonX = `${moveX}&& (x+${this.x} > ${-endWidth})?${moveX}:${-moveMix}`
+ props.push({
+ element: buttonSelectors,
+ property: 'transform.translateX',
+ expression: pageButtonX
+ })
+ }
+
+ this.eventpan = this._bind(this.boxSelector, props, 'pan', (e) => {
+ if (e.state === 'end') {
+ this.x = e.deltaX + this.x;
+ if (this.x < -endWidth) {
+ this.x = -endWidth
+ }
+ if (this.x > 0) {
+ this.x = 0
+ }
+ this.stop = false
+ this.bindTiming();
+ }
+ })
+ },
+ touchend(e) {
+ this.$nextTick(() => {
+ if (this.isopen && !this.isDrag && !this.isInAnimation) {
+ this.close()
+ }
+ })
+ },
+ bindTiming() {
+ if (this.isopen) {
+ this.move(this.x, -this.right)
+ } else {
+ this.move(this.x, -40)
+ }
+ },
+ move(left, value) {
+ if (left >= value) {
+ this.close()
+ } else {
+ this.open()
+ }
+ },
+ /**
+ * 开启swipe
+ */
+ open() {
+ this.animation(true)
+ },
+ /**
+ * 关闭swipe
+ */
+ close() {
+ this.animation(false)
+ },
+ /**
+ * 开启关闭动画
+ * @param {Object} type
+ */
+ animation(type) {
+ this.isDrag = true
+ let endWidth = this.right
+ let time = 200
+ this.isInAnimation = true;
+
+ let exit = `t>${time}`;
+ let translate_x_expression = `easeOutExpo(t,${this.x},${type?(-endWidth-this.x):(-this.x)},${time})`
+ let props = [{
+ element: this.selector,
+ property: 'transform.translateX',
+ expression: translate_x_expression
+ }]
+
+ let left = 0
+ for (let i = 0; i < this.options.length; i++) {
+ let buttonSelectors = this.getEl(this.$refs['button-hock'][i]);
+ if (this.button.length === 0 || !this.button[i] || !this.button[i].width) return
+ let moveMix = endWidth - left
+ left += this.button[i].width
+ let step = `${this.x}/${endWidth}`
+ let moveX = `(${step}) * ${moveMix}`
+ let pageButtonX = `easeOutExpo(t,${moveX},${type ? -moveMix + '-' + moveX: 0 + '-' + moveX},${time})`
+ props.push({
+ element: buttonSelectors,
+ property: 'transform.translateX',
+ expression: pageButtonX
+ })
+ }
+
+ this.timing = BindingX.bind({
+ eventType: 'timing',
+ exitExpression: exit,
+ props: props
+ }, (e) => {
+ if (e.state === 'end' || e.state === 'exit') {
+ this.x = type ? -endWidth : 0
+ this.isInAnimation = false;
+
+ this.isopen = this.isopen || false
+ if (this.isopen !== type) {
+ this.$emit('change', type)
+ }
+ this.isopen = type
+ this.isDrag = false
+ }
+ });
+ },
+ /**
+ * 绑定 BindingX
+ * @param {Object} anchor
+ * @param {Object} props
+ * @param {Object} fn
+ */
+ _bind(anchor, props, eventType, fn) {
+ return BindingX.bind({
+ anchor,
+ eventType,
+ props
+ }, (e) => {
+ typeof(fn) === 'function' && fn(e)
+ });
+ },
+ /**
+ * 获取ref
+ * @param {Object} el
+ */
+ getEl(el) {
+ return el.ref
+ },
+ /**
+ * 获取节点信息
+ */
+ getSelectorQuery() {
+ dom.getComponentRect(this.$refs['selector-content-hock'], (data) => {
+ if (this.position.content) return
+ this.position.content = data.size
+ })
+ for (let i = 0; i < this.options.length; i++) {
+ dom.getComponentRect(this.$refs['button-hock'][i], (data) => {
+ if (!this.button) {
+ this.button = []
+ }
+ if (this.options.length === this.button.length) return
+ this.button.push(data.size)
+ this.right += data.size.width
+ if (this.autoClose) return
+ if (this.show) {
+ this.open()
+ }
+ })
+ }
+ }
+ }
+}
diff --git a/yudao-vue-ui/components/uni-swipe-action-item/index.wxs b/yudao-vue-ui/components/uni-swipe-action-item/index.wxs
new file mode 100644
index 000000000..24c94bb08
--- /dev/null
+++ b/yudao-vue-ui/components/uni-swipe-action-item/index.wxs
@@ -0,0 +1,204 @@
+/**
+ * 监听页面内值的变化,主要用于动态开关swipe-action
+ * @param {Object} newValue
+ * @param {Object} oldValue
+ * @param {Object} ownerInstance
+ * @param {Object} instance
+ */
+function sizeReady(newValue, oldValue, ownerInstance, instance) {
+ var state = instance.getState()
+ state.position = JSON.parse(newValue)
+ if (!state.position || state.position.length === 0) return
+ var show = state.position[0].show
+ state.left = state.left || state.position[0].left;
+ // 通过用户变量,开启或关闭
+ if (show) {
+ openState(true, instance, ownerInstance)
+ } else {
+ openState(false, instance, ownerInstance)
+ }
+}
+
+/**
+ * 开始触摸操作
+ * @param {Object} e
+ * @param {Object} ins
+ */
+function touchstart(e, ins) {
+ var instance = e.instance;
+ var state = instance.getState();
+ var pageX = e.touches[0].pageX;
+ // 开始触摸时移除动画类
+ instance.removeClass('ani');
+ var owner = ins.selectAllComponents('.button-hock')
+ for (var i = 0; i < owner.length; i++) {
+ owner[i].removeClass('ani');
+ }
+ // state.position = JSON.parse(instance.getDataset().position);
+ state.left = state.left || state.position[0].left;
+ // 获取最终按钮组的宽度
+ state.width = pageX - state.left;
+ ins.callMethod('closeSwipe')
+}
+
+/**
+ * 开始滑动操作
+ * @param {Object} e
+ * @param {Object} ownerInstance
+ */
+function touchmove(e, ownerInstance) {
+ var instance = e.instance;
+ var disabled = instance.getDataset().disabled
+ var state = instance.getState()
+ // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+ disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
+
+ if (disabled) return
+ var pageX = e.touches[0].pageX;
+ move(pageX - state.width, instance, ownerInstance)
+}
+
+/**
+ * 结束触摸操作
+ * @param {Object} e
+ * @param {Object} ownerInstance
+ */
+function touchend(e, ownerInstance) {
+ var instance = e.instance;
+ var disabled = instance.getDataset().disabled
+ var state = instance.getState()
+
+ // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+ disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
+
+ if (disabled) return
+ // 滑动过程中触摸结束,通过阙值判断是开启还是关闭
+ // fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13
+ moveDirection(state.left, -40, instance, ownerInstance)
+}
+
+/**
+ * 设置移动距离
+ * @param {Object} value
+ * @param {Object} instance
+ * @param {Object} ownerInstance
+ */
+function move(value, instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取可滑动范围
+ var x = Math.max(-state.position[1].width, Math.min((value), 0));
+ state.left = x;
+ instance.setStyle({
+ transform: 'translateX(' + x + 'px)',
+ '-webkit-transform': 'translateX(' + x + 'px)'
+ })
+ // 折叠按钮动画
+ buttonFold(x, instance, ownerInstance)
+}
+
+/**
+ * 移动方向判断
+ * @param {Object} left
+ * @param {Object} value
+ * @param {Object} ownerInstance
+ * @param {Object} ins
+ */
+function moveDirection(left, value, ins, ownerInstance) {
+ var state = ins.getState()
+ var position = state.position
+ var isopen = state.isopen
+ if (!position[1].width) {
+ openState(false, ins, ownerInstance)
+ return
+ }
+ // 如果已经是打开状态,进行判断是否关闭,还是保留打开状态
+ if (isopen) {
+ if (-left <= position[1].width) {
+ openState(false, ins, ownerInstance)
+ } else {
+ openState(true, ins, ownerInstance)
+ }
+ return
+ }
+ // 如果是关闭状态,进行判断是否打开,还是保留关闭状态
+ if (left <= value) {
+ openState(true, ins, ownerInstance)
+ } else {
+ openState(false, ins, ownerInstance)
+ }
+}
+
+/**
+ * 设置按钮移动距离
+ * @param {Object} value
+ * @param {Object} instance
+ * @param {Object} ownerInstance
+ */
+function buttonFold(value, instance, ownerInstance) {
+ var ins = ownerInstance.selectAllComponents('.button-hock');
+ var state = instance.getState();
+ var position = state.position;
+ var arr = [];
+ var w = 0;
+ for (var i = 0; i < ins.length; i++) {
+ if (!ins[i].getDataset().button) return
+ var btnData = JSON.parse(ins[i].getDataset().button)
+
+ // fix by mehaotian TODO 在 app-vue 中,字符串转对象,需要转两次,这里先这么兼容
+ if (typeof(btnData) === 'string') {
+ btnData = JSON.parse(btnData)
+ }
+
+ var button = btnData[i] && btnData[i].width || 0
+ w += button
+ arr.push(-w)
+ // 动态计算按钮组每个按钮的折叠动画移动距离
+ var distance = arr[i - 1] + value * (arr[i - 1] / position[1].width)
+ if (i != 0) {
+ ins[i].setStyle({
+ transform: 'translateX(' + distance + 'px)',
+ })
+ }
+ }
+}
+
+/**
+ * 开启状态
+ * @param {Boolean} type
+ * @param {Object} ins
+ * @param {Object} ownerInstance
+ */
+function openState(type, ins, ownerInstance) {
+ var state = ins.getState()
+ var position = state.position
+ if (state.isopen === undefined) {
+ state.isopen = false
+ }
+ // 只有状态有改变才会通知页面改变状态
+ if (state.isopen !== type) {
+ // 通知页面,已经打开
+ ownerInstance.callMethod('change', {
+ open: type
+ })
+ }
+ // 设置打开和移动状态
+ state.isopen = type
+
+
+ // 添加动画类
+ ins.addClass('ani');
+ var owner = ownerInstance.selectAllComponents('.button-hock')
+ for (var i = 0; i < owner.length; i++) {
+ owner[i].addClass('ani');
+ }
+ // 设置最终移动位置
+ move(type ? -position[1].width : 0, ins, ownerInstance)
+
+}
+
+module.exports = {
+ sizeReady: sizeReady,
+ touchstart: touchstart,
+ touchmove: touchmove,
+ touchend: touchend
+}
diff --git a/yudao-vue-ui/components/uni-swipe-action-item/mpalipay.js b/yudao-vue-ui/components/uni-swipe-action-item/mpalipay.js
new file mode 100644
index 000000000..2b494a4b8
--- /dev/null
+++ b/yudao-vue-ui/components/uni-swipe-action-item/mpalipay.js
@@ -0,0 +1,160 @@
+export default {
+ data() {
+ return {
+ isshow: false,
+ viewWidth: 0,
+ buttonWidth: 0,
+ disabledView: false,
+ x: 0,
+ transition: false
+ }
+ },
+ watch: {
+ show(newVal) {
+ if (this.autoClose) return
+ if (newVal) {
+ this.open()
+ } else {
+ this.close()
+ }
+ },
+ },
+ created() {
+ if (this.swipeaction.children !== undefined) {
+ this.swipeaction.children.push(this)
+ }
+ },
+ beforeDestroy() {
+ this.swipeaction.children.forEach((item, index) => {
+ if (item === this) {
+ this.swipeaction.children.splice(index, 1)
+ }
+ })
+ },
+ mounted() {
+ this.isopen = false
+ this.transition = true
+ setTimeout(() => {
+ this.getQuerySelect()
+ }, 50)
+
+ },
+ methods: {
+ onClick(index, item) {
+ this.$emit('click', {
+ content: item,
+ index
+ })
+ },
+ touchstart(e) {
+ let {
+ pageX,
+ pageY
+ } = e.changedTouches[0]
+ this.transition = false
+ this.startX = pageX
+ if (this.autoClose) {
+ this.swipeaction.closeOther(this)
+ }
+ },
+ touchmove(e) {
+ let {
+ pageX,
+ } = e.changedTouches[0]
+ this.slide = this.getSlide(pageX)
+ if (this.slide === 0) {
+ this.disabledView = false
+ }
+
+ },
+ touchend(e) {
+ this.stop = false
+ this.transition = true
+ if (this.isopen) {
+ if (this.moveX === -this.buttonWidth) {
+ this.close()
+ return
+ }
+ this.move()
+ } else {
+ if (this.moveX === 0) {
+ this.close()
+ return
+ }
+ this.move()
+ }
+ },
+ open() {
+ this.x = this.moveX
+ this.$nextTick(() => {
+ this.x = -this.buttonWidth
+ this.moveX = this.x
+
+ if(!this.isopen){
+ this.isopen = true
+ this.$emit('change', true)
+ }
+ })
+ },
+ close() {
+ this.x = this.moveX
+ this.$nextTick(() => {
+ this.x = 0
+ this.moveX = this.x
+ if(this.isopen){
+ this.isopen = false
+ this.$emit('change', false)
+ }
+ })
+ },
+ move() {
+ if (this.slide === 0) {
+ this.open()
+ } else {
+ this.close()
+ }
+ },
+ onChange(e) {
+ let x = e.detail.x
+ this.moveX = x
+ if (x >= this.buttonWidth) {
+ this.disabledView = true
+ this.$nextTick(() => {
+ this.x = this.buttonWidth
+ })
+ }
+ },
+ getSlide(x) {
+ if (x >= this.startX) {
+ this.startX = x
+ return 1
+ } else {
+ this.startX = x
+ return 0
+ }
+
+ },
+ getQuerySelect() {
+ const query = uni.createSelectorQuery().in(this);
+ query.selectAll('.viewWidth-hook').boundingClientRect(data => {
+
+ this.viewWidth = data[0].width
+ this.buttonWidth = data[1].width
+ this.transition = false
+ this.$nextTick(() => {
+ this.transition = true
+ })
+
+ if (!this.buttonWidth) {
+ this.disabledView = true
+ }
+
+ if (this.autoClose) return
+ if (this.show) {
+ this.open()
+ }
+ }).exec();
+
+ }
+ }
+}
diff --git a/yudao-vue-ui/components/uni-swipe-action-item/mpother.js b/yudao-vue-ui/components/uni-swipe-action-item/mpother.js
new file mode 100644
index 000000000..3534b813a
--- /dev/null
+++ b/yudao-vue-ui/components/uni-swipe-action-item/mpother.js
@@ -0,0 +1,158 @@
+// #ifdef APP-NVUE
+const dom = weex.requireModule('dom');
+// #endif
+export default {
+ data() {
+ return {
+ uniShow: false,
+ left: 0
+ }
+ },
+ computed: {
+ moveLeft() {
+ return `translateX(${this.left}px)`
+ }
+ },
+ watch: {
+ show(newVal) {
+ if (!this.position || JSON.stringify(this.position) === '{}') return;
+ if (this.autoClose) return
+ if (newVal) {
+ this.$emit('change', true)
+ this.open()
+ } else {
+ this.$emit('change', false)
+ this.close()
+ }
+ }
+ },
+ mounted() {
+ this.position = {}
+ if (this.swipeaction.children !== undefined) {
+ this.swipeaction.children.push(this)
+ }
+ setTimeout(() => {
+ this.getSelectorQuery()
+ }, 100)
+ },
+ beforeDestoy() {
+ this.swipeaction.children.forEach((item, index) => {
+ if (item === this) {
+ this.swipeaction.children.splice(index, 1)
+ }
+ })
+ },
+ methods: {
+ onClick(index, item) {
+ this.$emit('click', {
+ content: item,
+ index
+ })
+ this.close()
+ },
+ touchstart(e) {
+ const {
+ pageX
+ } = e.touches[0]
+ if (this.disabled) return
+ const left = this.position.content.left
+ if (this.autoClose) {
+ this.swipeaction.closeOther(this)
+ }
+ this.width = pageX - left
+ if (this.isopen) return
+ if (this.uniShow) {
+ this.uniShow = false
+ this.isopen = true
+ this.openleft = this.left + this.position.button.width
+ }
+ },
+ touchmove(e, index) {
+ if (this.disabled) return
+ const {
+ pageX
+ } = e.touches[0]
+ this.setPosition(pageX)
+ },
+ touchend() {
+ if (this.disabled) return
+ if (this.isopen) {
+ this.move(this.openleft, 0)
+ return
+ }
+ this.move(this.left, -40)
+ },
+ setPosition(x, y) {
+ if (!this.position.button.width) {
+ return
+ }
+ // this.left = x - this.width
+ this.setValue(x - this.width)
+ },
+ setValue(value) {
+ // 设置最大最小值
+ this.left = Math.max(-this.position.button.width, Math.min(parseInt(value), 0))
+ this.position.content.left = this.left
+ if (this.isopen) {
+ this.openleft = this.left + this.position.button.width
+ }
+ },
+ move(left, value) {
+ if (left >= value) {
+ this.$emit('change', false)
+ this.close()
+ } else {
+ this.$emit('change', true)
+ this.open()
+ }
+ },
+ open() {
+ this.uniShow = true
+ this.left = -this.position.button.width
+ this.setValue(-this.position.button.width)
+ },
+ close() {
+ this.uniShow = true
+ this.setValue(0)
+ setTimeout(() => {
+ this.uniShow = false
+ this.isopen = false
+ }, 300)
+ },
+ getSelectorQuery() {
+ // #ifndef APP-NVUE
+ const views = uni.createSelectorQuery()
+ .in(this)
+ views
+ .selectAll('.selector-query-hock')
+ .boundingClientRect(data => {
+ this.position.content = data[1]
+ this.position.button = data[0]
+ if (this.autoClose) return
+ if (this.show) {
+ this.open()
+ } else {
+ this.close()
+ }
+ })
+ .exec()
+ // #endif
+ // #ifdef APP-NVUE
+ dom.getComponentRect(this.$refs['selector-content-hock'], (data) => {
+ if (this.position.content) return
+ this.position.content = data.size
+ })
+ dom.getComponentRect(this.$refs['selector-button-hock'], (data) => {
+ if (this.position.button) return
+ this.position.button = data.size
+ if (this.autoClose) return
+ if (this.show) {
+ this.open()
+ } else {
+ this.close()
+ }
+ })
+ // #endif
+ }
+ }
+}
diff --git a/yudao-vue-ui/components/uni-swipe-action-item/mpwxs.js b/yudao-vue-ui/components/uni-swipe-action-item/mpwxs.js
new file mode 100644
index 000000000..f9d281344
--- /dev/null
+++ b/yudao-vue-ui/components/uni-swipe-action-item/mpwxs.js
@@ -0,0 +1,97 @@
+export default {
+ data() {
+ return {
+ position: [],
+ button: []
+ }
+ },
+ computed: {
+ pos() {
+ return JSON.stringify(this.position)
+ },
+ btn() {
+ return JSON.stringify(this.button)
+ }
+ },
+ watch: {
+ show(newVal) {
+ if (this.autoClose) return
+ let valueObj = this.position[0]
+ if (!valueObj) {
+ this.init()
+ return
+ }
+ valueObj.show = newVal
+ this.$set(this.position, 0, valueObj)
+ }
+ },
+ created() {
+ if (this.swipeaction.children !== undefined) {
+ this.swipeaction.children.push(this)
+ }
+ },
+ mounted() {
+ this.init()
+
+ },
+ beforeDestroy() {
+ this.swipeaction.children.forEach((item, index) => {
+ if (item === this) {
+ this.swipeaction.children.splice(index, 1)
+ }
+ })
+ },
+ methods: {
+ init() {
+
+ setTimeout(() => {
+ this.getSize()
+ this.getButtonSize()
+ }, 50)
+ },
+ closeSwipe(e) {
+ if (!this.autoClose) return
+ this.swipeaction.closeOther(this)
+ },
+
+ change(e) {
+ this.$emit('change', e.open)
+ let valueObj = this.position[0]
+ if (valueObj.show !== e.open) {
+ valueObj.show = e.open
+ this.$set(this.position, 0, valueObj)
+ }
+ },
+ onClick(index, item) {
+ this.$emit('click', {
+ content: item,
+ index
+ })
+ },
+ appTouchStart(){},
+ appTouchEnd(){},
+ getSize() {
+ const views = uni.createSelectorQuery().in(this)
+ views
+ .selectAll('.selector-query-hock')
+ .boundingClientRect(data => {
+ if (this.autoClose) {
+ data[0].show = false
+ } else {
+ data[0].show = this.show
+ }
+ this.position = data
+ })
+ .exec()
+ },
+ getButtonSize() {
+ const views = uni.createSelectorQuery().in(this)
+ views
+ .selectAll('.button-hock')
+ .boundingClientRect(data => {
+ this.button = data
+ })
+ .exec()
+ }
+ }
+}
diff --git a/yudao-vue-ui/components/uni-swipe-action-item/uni-swipe-action-item.vue b/yudao-vue-ui/components/uni-swipe-action-item/uni-swipe-action-item.vue
new file mode 100644
index 000000000..af962d6ab
--- /dev/null
+++ b/yudao-vue-ui/components/uni-swipe-action-item/uni-swipe-action-item.vue
@@ -0,0 +1,270 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/uni-swipe-action/uni-swipe-action.vue b/yudao-vue-ui/components/uni-swipe-action/uni-swipe-action.vue
new file mode 100644
index 000000000..c8b656c11
--- /dev/null
+++ b/yudao-vue-ui/components/uni-swipe-action/uni-swipe-action.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/uni-transition/uni-transition.vue b/yudao-vue-ui/components/uni-transition/uni-transition.vue
new file mode 100644
index 000000000..212ea1e14
--- /dev/null
+++ b/yudao-vue-ui/components/uni-transition/uni-transition.vue
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/components/version-update/base-cloud-mobile.scss b/yudao-vue-ui/components/version-update/base-cloud-mobile.scss
new file mode 100644
index 000000000..b64871653
--- /dev/null
+++ b/yudao-vue-ui/components/version-update/base-cloud-mobile.scss
@@ -0,0 +1,1250 @@
+div,a,img,span,page,view,navigator,image,text,input,textarea,button,form{
+ box-sizing: border-box;
+}
+
+a{
+ text-decoration: none;
+ color: $main;
+}
+
+form{
+ display: block;
+ width: 100%;
+}
+
+image{will-change: transform}
+
+input,textarea,form{
+ width: 100%;
+ height: auto;
+ min-height: 10px;
+ display: block;
+ font-size: inherit;
+}
+
+button{
+ color: inherit;
+ line-height: inherit;
+ margin: 0;
+ background-color: transparent;
+ padding: 0;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ &:after{
+ display: none;
+ }
+}
+
+switch .uni-switch-input{
+ margin-right: 0;
+}
+.wx-switch-input,.uni-switch-input{width:42px !important;height:22px !important;}
+ .wx-switch-input::before,.uni-switch-input::before{width:40px !important;height: 20px !important;}
+ .wx-switch-input::after,.uni-switch-input::after{width: 20px !important;height: 20px !important;}
+
+
+/**背景颜色**/
+.bg{
+ background-color: $main;
+ color: $mainInverse;
+}
+.gradualBg{
+ background-image: $mainGradual;
+ color: $mainGradualInverse ;
+}
+.grayBg{
+ background-color: #f7f7f7;
+ color: #30302f;
+}
+.whiteBg{
+ background-color: #fff;
+ color: #000;
+}
+.blackBg{
+ background-color: #000;
+ color: #fff;
+}
+.orangeBg{
+ background-color: $orange;
+ color: #fff;
+}
+.redBg{
+ background-color: $red;
+ color: #fff;
+}
+.yellowBg{
+ background-color: $yellow;
+ color: #000;
+}
+.greenBg{
+ background-color: $green;
+ color: #fff;
+}
+.brownBg{
+ background-color: $brown;
+ color: #fff;
+}
+.blueBg{
+ background-color: $blue;
+ color: #fff;
+}
+.purpleBg{
+ background-color: $purple;
+ color: #fff;
+}
+
+/* 文字颜色 */
+.main{
+ color: $main;
+}
+.green{
+ color: $green;
+}
+.red{
+ color: $red;
+}
+.yellow{
+ color: $yellow;
+}
+.black{
+ color: $black;
+}
+.white{
+ color: $white;
+}
+.gray{
+ color: $gray;
+}
+.grey{
+ color: $grey;
+}
+.orange{
+ color: $orange;
+}
+.brown{
+ color: $brown;
+}
+.blue{
+ color: $blue;
+}
+.purple{
+ color: $purple;
+}
+
+.hoverMain{
+ &:hover{
+ color: $main;
+ }
+}
+
+.hoverGreen{
+ &:hover{
+ color: $green;
+ }
+}
+
+.hoverRed{
+ &:hover{
+ color: $red;
+ }
+}
+
+.hoverBlue{
+ &:hover{
+ color: $blue;
+ }
+}
+
+.hoverGray{
+ &:hover{
+ color: $gray;
+ }
+}
+
+.hoverWhite{
+ &:hover{
+ color: $white;
+ }
+}
+
+.hoverBlack{
+ &:hover{
+ color: $black;
+ }
+}
+
+.hoverOrange{
+ &:hover{
+ color: $orange;
+ }
+}
+
+.hoverYellow{
+ &:hover{
+ color: $yellow;
+ }
+}
+
+.hoverBrown{
+ &:hover{
+ color: $brown;
+ }
+}
+
+.hoverPurple{
+ &:hover{
+ color: $purple;
+ }
+}
+
+/* 宽度 高度 */
+$w:0;
+@while $w <= 500 {
+ @if $w <= 100 {
+ .w#{$w}p{
+ width: $w*1%;
+ }
+ .h#{$w}p{
+ height: $w*1%;
+ }
+ @if $w == 100 {
+ .100p{
+ width: 100%;
+ height: 100%;
+ }
+ .ww{
+ width: 100vw;
+ }
+ .hh{
+ height: 100vh;
+ }
+ }
+ }
+ .w#{$w}{
+ width: $w*2upx;
+ }
+ .h#{$w}{
+ height: $w*2upx;
+ }
+ @if $w < 10 {
+ $w : $w + 1 ;
+ } @else{
+ $w : $w + 5 ;
+ }
+}
+
+
+/* 字号 */
+@for $fz from 12 through 100 {
+ .fz#{$fz}{
+ font-size: $fz*2upx !important;
+ }
+}
+
+/* 边距 - 覆盖顺序是小的尺寸覆盖大的尺寸 少的方向覆盖多的方向 */
+$s : 0 ;
+@while $s <= 500 {
+ .pd#{$s}{
+ padding: $s*2upx!important;
+ }
+ .m#{$s}{
+ margin: $s*2upx!important;
+ }
+ @if $s == 15 {
+ .pd{
+ padding: 30upx!important;
+ }
+ .m{
+ margin: 30upx!important;
+ }
+ }
+ @if $s < 10 {
+ $s : $s + 1 ;
+ } @else if($s < 100){
+ $s : $s + 5 ;
+ } @else if($s < 300){
+ $s : $s + 10 ;
+ } @else{
+ $s : $s + 50 ;
+ }
+}
+
+$s : 0 ;
+@while $s <= 500 {
+ .ptb#{$s}{
+ padding-top: $s*2upx!important;
+ padding-bottom: $s*2upx!important;
+ }
+ .plr#{$s}{
+ padding-left: $s*2upx!important;
+ padding-right: $s*2upx!important;
+ }
+ .mtb#{$s}{
+ margin-top: $s*2upx!important;
+ margin-bottom: $s*2upx!important;
+ }
+ .mlr#{$s}{
+ margin-left: $s*2upx!important;
+ margin-right: $s*2upx!important;
+ }
+ @if $s == 15 {
+ .ptb{
+ padding-top: 30upx!important;
+ padding-bottom: 30upx!important;
+ }
+ .plr{
+ padding-left: 30upx!important;
+ padding-right: 30upx!important;
+ }
+
+ .mlr{
+ margin-left: 30upx!important;
+ margin-right: 30upx!important;
+ }
+ .mtb{
+ margin-top: 30upx!important;
+ margin-bottom: 30upx!important;
+ }
+ }
+ @if $s < 10 {
+ $s : $s + 1 ;
+ } @else if($s < 100){
+ $s : $s + 5 ;
+ } @else if($s < 300){
+ $s : $s + 10 ;
+ } @else{
+ $s : $s + 50 ;
+ }
+}
+
+$s : 0 ;
+@while $s <= 500 {
+ .pl#{$s}{
+ padding-left: $s*2upx!important;
+ }
+ .pr#{$s}{
+ padding-right: $s*2upx!important;
+ }
+ .pt#{$s}{
+ padding-top: $s*2upx!important;
+ }
+ .pb#{$s}{
+ padding-bottom: $s*2upx!important;
+ }
+ .ml#{$s}{
+ margin-left: $s*2upx!important;
+ }
+ .mr#{$s}{
+ margin-right: $s*2upx!important;
+ }
+ .mt#{$s}{
+ margin-top: $s*2upx!important;
+ }
+ .mb#{$s}{
+ margin-bottom: $s*2upx!important;
+ }
+ @if $s == 15 {
+ .pt{
+ padding-top: 30upx!important;
+ }
+ .pb{
+ padding-bottom: 30upx!important;
+ }
+ .pl{
+ padding-left: 30upx!important;
+ }
+ .pr{
+ padding-right: 30upx!important;
+ }
+ .mt{
+ margin-top: 30upx!important;
+ }
+ .mr{
+ margin-right: 30upx!important;
+ }
+ .ml{
+ margin-left: 30upx!important;
+ }
+ .mb{
+ margin-bottom: 30upx!important;
+ }
+ }
+ @if $s < 10 {
+ $s : $s + 1 ;
+ } @else if($s < 100){
+ $s : $s + 5 ;
+ } @else if($s < 300){
+ $s : $s + 10 ;
+ } @else{
+ $s : $s + 50 ;
+ }
+}
+
+
+
+/* 文字溢出隐藏 */
+.clip{
+ width: 100%;
+ display: -webkit-box;
+ overflow: hidden;
+ -webkit-line-clamp: 1;
+ -webkit-box-orient: vertical;
+ @for $i from 2 through 10{
+ &.c#{$i}{
+ -webkit-line-clamp: $i;
+ }
+ }
+}
+
+.cut{
+ display: block;
+ width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* 价格 */
+.price{
+ font-size: inherit ;
+ &:before{
+ content: "¥";
+ font-size: 70%;
+ color: inherit;
+ font-weight: normal;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif ;
+ }
+ .point{
+ display: inline-block;
+ font-size: 70%;
+ font-weight: inherit;
+ letter-spacing: 1px;
+ color: inherit;
+ }
+ &.noPrefix{
+ &:before{
+ content: '';
+ }
+ }
+}
+
+/* 布局 */
+.grid,.gridNoPd,.gridSmPd,.gridNoMb{
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-flex-wrap: wrap;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ width: 100%;
+ padding: 20upx 20upx 0 20upx;
+ >.item,>image,>view,>navigator,>text,>button{
+ width: 50%;
+ padding: 0 10upx;
+ margin-bottom: 20upx;
+ }
+ @for $i from 1 through 50{
+ &.g#{$i}{
+ >.item,>image,>view,>navigator,>text,>button{
+ width: 100%/$i;
+ }
+ }
+ }
+}
+
+.gridNoMb{
+ >.item,>image,>view,>navigator,>text,>button{
+ margin-bottom: 0;
+ }
+}
+
+.gridNoPd{
+ padding: 0;
+ >.item,>image,>view,>navigator,>text,>button{
+ padding: 0;
+ margin-bottom: 0;
+ }
+}
+.gridSmPd{
+ padding: 0;
+ >.item,>image,>view,>navigator,>text,>button{
+ padding-right: 0;
+ &:first-child{
+ padding-left: 0;
+ padding-right: 10upx;
+ }
+ }
+}
+
+/* flex布局 */
+.flex{
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ &.t{
+ align-items: flex-start;
+ }
+ &.b{
+ align-items: flex-end;
+ }
+ &.cv{ //垂直方向铺满
+ align-items: stretch;
+ }
+ &.bk{ //水平方向铺满
+ flex-direction: column;
+ }
+ &.lt{
+ justify-content: flex-start;
+ }
+ &.ct{
+ justify-content: center;
+ }
+ &.rt{
+ justify-content: flex-end;
+ }
+ &.ar{
+ justify-content: space-around;
+ }
+ &.av{
+ >.item,view,button,navigator,image,text{
+ flex:1;
+ }
+ }
+}
+
+/* 定位布局 */
+.father{
+ position: relative;
+}
+.abs,.fixed{
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 1;
+ &image{
+ width: 100%;
+ height: 100%;
+ }
+ &.top{
+ bottom: auto;
+ }
+ &.bottom{
+ top: auto;
+ }
+ &.left{
+ right: auto;
+ }
+ &.right{
+ left: auto;
+ }
+}
+@for $i from 0 through 20 {
+ .z#{$i}{
+ z-index: $i !important;
+ }
+}
+
+@for $i from 1 through 200 {
+ .top-#{$i}{
+ bottom: auto;
+ top: $i * -2upx;
+ }
+ .left-#{$i}{
+ right: auto;
+ left: $i * -2upx;
+ }
+ .bottom-#{$i}{
+ top: auto;
+ bottom: $i * -2upx;
+ }
+ .right-#{$i}{
+ left: auto;
+ right: $i * -2upx;
+ }
+ .top#{$i}{
+ bottom: auto;
+ top: $i * 2upx;
+ }
+ .left#{$i}{
+ right: auto;
+ left: $i * 2upx;
+ }
+ .bottom#{$i}{
+ top: auto;
+ bottom: $i * 2upx;
+ }
+ .right#{$i}{
+ left: auto;
+ right: $i * 2upx;
+ }
+ .top-#{$i}p{
+ bottom: auto;
+ top: $i * -1%;
+ }
+ .left-#{$i}p{
+ right: auto;
+ left: $i * -1%;
+ }
+ .bottom-#{$i}p{
+ top: auto;
+ bottom: $i * -1%;
+ }
+ .right-#{$i}p{
+ left: auto;
+ right: $i * -1%;
+ }
+ .top#{$i}p{
+ bottom: auto;
+ top: $i * 1%;
+ }
+ .left#{$i}p{
+ right: auto;
+ left: $i * 1%;
+ }
+ .bottom#{$i}p{
+ top: auto;
+ bottom: $i * 1%;
+ }
+ .right#{$i}p{
+ left: auto;
+ right: $i * 1%;
+ }
+}
+
+.fixed{
+ position: fixed;
+}
+
+/* fix-auto布局 */
+.fixAuto,.fixAutoNoPd,.fixAutoSmPd{
+ display: table;
+ width: 100%;
+ padding: 20upx 10upx;
+ >.item,>view,>image,>navigator,>text,>button{
+ vertical-align: top;
+ padding: 0 10upx;
+ display: table-cell ;
+ }
+ &.middle{
+ >.item,>view,>image,>navigator,>text{
+ vertical-align: middle;
+ }
+ }
+ &.bottom{
+ >.item,>view,>image,>navigator,>text{
+ vertical-align: bottom;
+ }
+ }
+}
+.fixAutoSmPd{
+ padding: 0;
+ >.item,>view,>image,>navigator,>text{
+ padding-right: 0;
+ &:first-child{
+ padding-left: 0;
+ padding-right: 10upx;
+ }
+ }
+}
+.fixAutoNoPd{
+ padding: 0;
+ >.item,>view,>image,>navigator,>text{
+ padding: 0;
+ }
+}
+
+/* 浮动组件 */
+.clear{
+ &:after{
+ content: "";
+ clear: both;
+ height: 0;
+ display: block;
+ visibility: hidden;
+ }
+}
+.fl{
+ float: left;
+}
+.fr{
+ float: right;
+}
+
+/* 按钮样式类 */
+.btn,.roundBtn{
+ cursor: pointer;
+ display: inline-block;
+ text-align: center;
+ padding: 8upx 24upx;
+ background-color: $main;
+ color: $mainInverse ;
+ font-size: 28upx;
+ border: 1px solid $main;
+ -webkit-border-radius: 8upx;
+ -moz-border-radius: 8upx;
+ border-radius: 8upx;
+ transition: 0.4s;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ &.gradualBg{
+ border-color: transparent;
+ background-image: $mainGradual;
+ color: $mainGradualInverse;
+
+ }
+ &.blackBg{
+ background-color: $black;
+ border-color: $black;
+ color: #fff;
+ &.shadow{
+ box-shadow: 0px 2px 9px -1px rgba($black , 0.4);
+ }
+ }
+ &.greenBg{
+ background-color: $green;
+ border-color: $green;
+ color: #fff;
+ &.shadow{
+ box-shadow: 0px 2px 9px -1px rgba($green , 0.4);
+ }
+ }
+ &.grayBg{
+ border-color: rgba(#30302f,0.2);
+ background-color: #f7f7f7;
+ color: #30302f;
+ &.shadow{
+ box-shadow: 0px 2px 9px -1px rgba( #30302f , 0.2);
+ }
+ }
+ &.whiteBg{
+ border-color: rgba(#fff,0.2);
+ background-color: #fff;
+ color: #000;
+ }
+
+ &.orangeBg{
+ border-color: $orange;
+ background-color: $orange;
+ color: #fff;
+ &.shadow{
+ box-shadow: 0px 2px 9px -1px rgba( $orange , 0.4);
+ }
+ }
+ &.redBg{
+ border-color: $red;
+ background-color: $red;
+ color: #fff;
+ &.shadow{
+ box-shadow: 0px 2px 9px -1px rgba( $red , 0.4);
+ }
+ }
+ &.yellowBg{
+ border-color: $yellow;
+ background-color: $yellow;
+ color: #000;
+ &.shadow{
+ box-shadow: 0px 2px 9px -1px rgba( $yellow , 0.4);
+ }
+ }
+
+ &.line{
+ background-color: transparent;
+ background-image: none;
+ color: $main;
+ &.blackBg{
+ color: $black;
+ }
+ &.greenBg{
+ color: $green;
+ }
+ &.yellowBg{
+ color: $yellow;
+ }
+ &.grayBg{
+ color: #30302f;
+ }
+ &.whiteBg{
+ border-color: rgba(#fff,0.7);
+ color: #fff;
+ }
+ &.orangeBg{
+ color: $orange;
+ }
+ &.redBg{
+ color: $red;
+ }
+ }
+ &+.btn,&+.roundBtn{
+ margin-left: 20upx;
+ }
+ &.block{
+ margin: 0;
+ padding: 20upx 24upx;
+ display: block;
+ width: 100%;
+ &+.btn{
+ margin-left: 0;
+ }
+ }
+ &:hover{
+ -webkit-transform: scale(0.99);
+ -moz-transform: scale(0.99);
+ -ms-transform: scale(0.99);
+ -o-transform: scale(0.99);
+ transform: scale(0.99);
+ opacity: 0.8;
+ }
+ &.disabled{
+ -webkit-transform: scale(1);
+ -moz-transform: scale(1);
+ -ms-transform: scale(1);
+ -o-transform: scale(1);
+ transform: scale(1);
+ opacity: 0.4;
+ cursor: not-allowed;
+ }
+}
+
+[class^="tag"] , [class*=" tag"]{
+ display: inline-block;
+ font-size: 24upx;
+ padding: 4upx 14upx;
+ border-radius: 4upx;
+ margin-right: 6upx;
+ margin-left: 6upx;
+}
+.tag{
+ background-color: rgba($main,0.2);
+ color: $main;
+}
+.tagBlue{
+ background-color: rgba($blue,0.2);
+ color: $blue;
+}
+.tagGray{
+ background-color: rgba($gray,0.2);
+ color: $gray;
+}
+
+.tagGradual{
+ background-image: linear-gradient(to top right,rgba($main,0.2),rgba($main,0.1));
+ color: $main;
+}
+
+.tagBlack{
+ background-color: rgba($black,0.2);
+ color: $black;
+}
+.tagGreen{
+ background-color: rgba($green,0.2);
+ color: $green;
+}
+
+.tagWhite{
+ background-color: rgba($white,0.2);
+ color: $white;
+}
+
+.tagOrange{
+ background-color: rgba($orange,0.2);
+ color: $orange;
+}
+.tagRed{
+ background-color: rgba($red,0.2);
+ color: $red;
+}
+.tagYellow{
+ background-color: rgba($yellow,0.2);
+ color: $yellow;
+}
+
+/* 边线(实线、虚线) */
+.bdn{
+ border: none;
+}
+.bd{
+ border: 1px solid $borderColor;
+ &.dashed{
+ border-style: dashed;
+ }
+}
+.bt{
+ border-top:1px solid $borderColor;
+ &.dashed{
+ border-top-style: dashed;
+ }
+}
+.bb{
+ border-bottom:1px solid $borderColor;
+ &.dashed{
+ border-bottom-style: dashed;
+ }
+}
+.bl{
+ border-left:1px solid $borderColor;
+ &.dashed{
+ border-left-style: dashed;
+ }
+}
+.br{
+ border-right: 1px solid $borderColor;
+ &.dashed{
+ border-right-style: dashed;
+ }
+}
+
+$b:2;
+@while $b <= 10 {
+ .bd#{$b}{
+ border: #{$b}px solid $borderColor;
+ &.dashed{
+ border-style: dashed;
+ }
+ }
+ .bt#{$b}{
+ border-top:#{$b}px solid $borderColor;
+ &.dashed{
+ border-top-style: dashed;
+ }
+ }
+ .bb#{$b}{
+ border-bottom:#{$b}px solid $borderColor;
+ &.dashed{
+ border-bottom-style: dashed;
+ }
+ }
+ .bl#{$b}{
+ border-left:#{$b}px solid $borderColor;
+ &.dashed{
+ border-left-style: dashed;
+ }
+ }
+ .br#{$b}{
+ border-right: #{$b}px solid $borderColor;
+ &.dashed{
+ border-right-style: dashed;
+ }
+ }
+ $b : $b + 1 ;
+}
+
+/* 边线颜色 */
+.mainBd{
+ border-color: $main;
+}
+.greenBd{
+ border-color: $green;
+}
+.redBd{
+ border-color: $red;
+}
+.yellowBd{
+ border-color:$yellow ;
+}
+.blackBd{
+ border-color: $black;
+}
+.whiteBd{
+ border-color:$white ;
+}
+.grayBd{
+ border-color:$gray;
+}
+.greyBd{
+ border-color:$grey;
+}
+.orangeBd{
+ border-color:$orange;
+}
+
+/* 圆角 */
+.radius,.rds{
+ -webkit-border-radius: 100%!important;
+ -moz-border-radius: 100%!important;
+ border-radius: 100%!important;
+}
+
+$r:0;
+@while $r <= 50{
+ .rds#{$r},&.radius#{$r}{
+ -webkit-border-radius:$r*2upx!important;
+ -moz-border-radius:$r*2upx!important;
+ border-radius:$r*2upx!important;
+ }
+ $r : $r + 1;
+}
+
+.rdsTl,.radiusTopLeft{
+ border-top-left-radius:100%!important;
+}
+.rdsTr,.radiusTopRight{
+ border-top-right-radius: 100%!important;
+}
+.rdsBl,.radiusBottomLeft{
+ border-bottom-left-radius: 100%!important;
+}
+.rdsBr,.radiusBottomRight{
+ border-bottom-right-radius: 100%!important;
+}
+
+$r:0;
+@while $r <= 50{
+ .rdsTl#{$r},.radiusTopLeft#{$r}{
+ border-top-left-radius: $r*2upx!important;
+ }
+ .rdsTr#{$r},.radiusTopRight#{$r}{
+ border-top-right-radius: $r*2upx!important;
+ }
+ .rdsBl#{$r},.radiusBottomLeft#{$r}{
+ border-bottom-left-radius: $r*2upx!important;
+ }
+ .rdsBr#{$r},.radiusBottomRight#{$r}{
+ border-bottom-right-radius: $r*2upx!important;
+ }
+ $r : $r + 1;
+}
+
+/* 正方形&长方形 */
+[class^="square"] , [class*=" square"]{
+ width: 100%;
+ position: relative;
+ height: auto;
+ >.item,>image,>view,>navigator,>text,>button{
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ }
+}
+
+$p : 200 ;
+@while $p > 0 {
+ .square#{$p}{
+ padding-top: $p*1%;
+ }
+ @if $p == 100 {
+ .square{
+ padding-top: 100%;
+ }
+ }
+ $p : $p - 5 ;
+}
+
+
+
+/* 阴影 */
+.shadow{
+ box-shadow: 0px 2px 9px -1px rgba(0, 0, 0, 0.1);
+}
+
+/* 遮罩层 */
+.wrapper-top,.wt{
+ background-image: linear-gradient(rgba(0,0,0,0.3) , rgba(0,0,0,0.02));
+}
+.wrapper-bottom,.wb{
+ background-image: linear-gradient( rgba(0,0,0,0.02) , rgba(0,0,0,0.3) );
+}
+
+[class^="wp"],[class*=" wp"] {
+ z-index: 10;
+}
+
+/* 透明度 */
+@for $op from 0 through 10 {
+ .op#{$op}{
+ opacity: $op * 0.1!important;
+ }
+ .wp#{$op}{
+ background-color: rgba(#000,$op*0.1);
+ }
+ @if $op == 5 {
+ .wp{
+ background-color: rgba(#000,0.5);
+ }
+ }
+}
+
+/* 分割线 */
+[class*=" split"],[class^="split"] {
+ position: relative;
+ &:before{
+ content:"";
+ display: block;
+ position: absolute;
+ left: 0;
+ top: 50%;
+ border-left: 1px solid $borderColor;
+ }
+}
+
+$s:10;
+@while $s <= 100 {
+ .split#{$s}{
+ &:before{
+ height: #{$s*2}upx;
+ margin-top: -#{$s}upx;
+ }
+ }
+ @if $s == 10 {
+ .split{
+ &:before{
+ height: 20upx;
+ margin-top: -10upx;
+ }
+ }
+ }
+ $s:$s+2;
+}
+
+.hover,[class^="hover"],[class*=" hover"]{
+ transition: all 0.4s;
+ cursor: pointer;
+ &:hover{
+ opacity: 0.8 !important;
+ }
+}
+
+
+
+.statusBar{
+ height: var(--status-bar-height);
+}
+
+.winBottom{
+ height: var(--windown-bottom);
+}
+
+.safeBottom{
+ padding-bottom: constant(safe-area-inset-bottom);
+ padding-bottom: env(safe-area-inset-bottom);
+}
+
+.disabled{
+ opacity:0.8;
+ cursor: not-allowed;
+}
+
+
+
+.grid,.gridNoMb,.gridNoPd{
+ >.btn,>.roundBtn{
+ &+.btn,&+.roundBtn{
+ margin-left: 0 ;
+ }
+ }
+}
+
+.roundBtn{
+ -webkit-border-radius: 100upx;
+ -moz-border-radius: 100upx;
+ border-radius: 100upx;
+}
+
+
+
+ /* 位置 */
+ .text-center,.tc{
+ text-align: center!important;
+ }
+ .text-left,.tl{
+ text-align: left!important;
+ }
+ .text-right,.tr{
+ text-align: right!important;
+ }
+ .text-justify,.tj{
+ text-align: justify!important;
+ }
+ .text-bold,.bold{
+ font-weight: bold!important;
+ }
+ .text-normal,.normal{
+ font-weight: normal!important;
+ }
+ .break{
+ white-space: normal;
+ word-break: break-all;
+ }
+ .noBreak{
+ white-space: nowrap;
+ word-break: keep-all;
+ }
+ .inline{
+ display: inline-block;
+ }
+ .block{
+ display: block;
+ width: 100%;
+ }
+ .none{
+ display: none;
+ }
+ .center-block{
+ margin: 0 auto;
+ display: block;
+ }
+ .hidden{
+ overflow: hidden;
+ }
+ .hiddenX{
+ overflow-x: hidden;
+ }
+ .hiddenY{
+ overflow-y: hidden;
+ }
+ .auto{
+ overflow: auto;
+ }
+ .autoX{
+ overflow-x: auto;
+ }
+ .autoY{
+ overflow-y: auto;
+ }
+ .showInMb{
+ display: block;
+ }
+ .showInPc{
+ display: none;
+ }
+ table{
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ border: 1px solid #e6e6e6;
+ thead{
+ tr{
+ background-color: #f2f2f2;
+ th{
+ color: #8799a3;
+ width: 1%;
+ }
+ }
+ }
+ tr{
+ background-color: #fff;
+ transition: all 0.4s;
+ td,th{
+ border: 1px solid #e6e6e6;
+ overflow: hidden;
+ -o-text-overflow: ellipsis;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ word-wrap: break-word;
+ padding: 5px 10px;
+ height: 28px;
+ line-height: 28px;
+ &.autoWidth{
+ width: auto;
+ }
+ }
+ &:hover{
+ background-color: #f2f2f2;
+ }
+ }
+ }
\ No newline at end of file
diff --git a/yudao-vue-ui/components/version-update/static/airship.png b/yudao-vue-ui/components/version-update/static/airship.png
new file mode 100644
index 000000000..f46ad4961
Binary files /dev/null and b/yudao-vue-ui/components/version-update/static/airship.png differ
diff --git a/yudao-vue-ui/components/version-update/static/cloudLeft.png b/yudao-vue-ui/components/version-update/static/cloudLeft.png
new file mode 100644
index 000000000..0d86534a6
Binary files /dev/null and b/yudao-vue-ui/components/version-update/static/cloudLeft.png differ
diff --git a/yudao-vue-ui/components/version-update/static/cloudRight.png b/yudao-vue-ui/components/version-update/static/cloudRight.png
new file mode 100644
index 000000000..46e5491bc
Binary files /dev/null and b/yudao-vue-ui/components/version-update/static/cloudRight.png differ
diff --git a/yudao-vue-ui/components/version-update/static/login-wave.png b/yudao-vue-ui/components/version-update/static/login-wave.png
new file mode 100644
index 000000000..ae9ae90aa
Binary files /dev/null and b/yudao-vue-ui/components/version-update/static/login-wave.png differ
diff --git a/yudao-vue-ui/components/version-update/static/shipAir.png b/yudao-vue-ui/components/version-update/static/shipAir.png
new file mode 100644
index 000000000..96e827fe4
Binary files /dev/null and b/yudao-vue-ui/components/version-update/static/shipAir.png differ
diff --git a/yudao-vue-ui/components/version-update/static/shipGas.png b/yudao-vue-ui/components/version-update/static/shipGas.png
new file mode 100644
index 000000000..df396f696
Binary files /dev/null and b/yudao-vue-ui/components/version-update/static/shipGas.png differ
diff --git a/yudao-vue-ui/components/version-update/static/smallCloud.png b/yudao-vue-ui/components/version-update/static/smallCloud.png
new file mode 100644
index 000000000..885bcdcf3
Binary files /dev/null and b/yudao-vue-ui/components/version-update/static/smallCloud.png differ
diff --git a/yudao-vue-ui/components/version-update/static/star.png b/yudao-vue-ui/components/version-update/static/star.png
new file mode 100644
index 000000000..63c80454a
Binary files /dev/null and b/yudao-vue-ui/components/version-update/static/star.png differ
diff --git a/yudao-vue-ui/components/version-update/version-update.vue b/yudao-vue-ui/components/version-update/version-update.vue
new file mode 100644
index 000000000..5f36aeb7d
--- /dev/null
+++ b/yudao-vue-ui/components/version-update/version-update.vue
@@ -0,0 +1,1811 @@
+/**
+* BaseCloud APP更新检测组件
+v1.0.4
+*/
+
+
+
+ 版本{{version}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{title}} {{ updateData.version ? 'v' + updateData.version : ''}}
+
+
+
+
+ {{ progress }}
+
+ %
+
+
+
+
+ 版本更新中,不要离开...
+
+
+
+
+ 版本升级成功
+
+ 更新已完成,请重启应用体验新版
+
+
+
+
+
+
+ {{ defaultContent }}
+
+
+ {{ index + 1 }}.{{ item }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/index.html b/yudao-vue-ui/index.html
new file mode 100644
index 000000000..b61f63ec8
--- /dev/null
+++ b/yudao-vue-ui/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/main.js b/yudao-vue-ui/main.js
new file mode 100644
index 000000000..afc6b0895
--- /dev/null
+++ b/yudao-vue-ui/main.js
@@ -0,0 +1,21 @@
+import App from './App'
+
+// #ifndef VUE3
+import Vue from 'vue'
+Vue.config.productionTip = false
+App.mpType = 'app'
+const app = new Vue({
+ ...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import { createSSRApp } from 'vue'
+export function createApp() {
+ const app = createSSRApp(App)
+ return {
+ app
+ }
+}
+// #endif
\ No newline at end of file
diff --git a/yudao-vue-ui/manifest.json b/yudao-vue-ui/manifest.json
new file mode 100644
index 000000000..dd17511b8
--- /dev/null
+++ b/yudao-vue-ui/manifest.json
@@ -0,0 +1,72 @@
+{
+ "name" : "ruoyi-vue-ui",
+ "appid" : "__UNI__764D04C",
+ "description" : "",
+ "versionName" : "1.0.0",
+ "versionCode" : "100",
+ "transformPx" : false,
+ /* 5+App特有相关 */
+ "app-plus" : {
+ "usingComponents" : true,
+ "nvueStyleCompiler" : "uni-app",
+ "compilerVersion" : 3,
+ "splashscreen" : {
+ "alwaysShowBeforeRender" : true,
+ "waiting" : true,
+ "autoclose" : true,
+ "delay" : 0
+ },
+ /* 模块配置 */
+ "modules" : {},
+ /* 应用发布信息 */
+ "distribute" : {
+ /* android打包配置 */
+ "android" : {
+ "permissions" : [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ ]
+ },
+ /* ios打包配置 */
+ "ios" : {},
+ /* SDK配置 */
+ "sdkConfigs" : {}
+ }
+ },
+ /* 快应用特有相关 */
+ "quickapp" : {},
+ /* 小程序特有相关 */
+ "mp-weixin" : {
+ "appid" : "",
+ "setting" : {
+ "urlCheck" : false
+ },
+ "usingComponents" : true
+ },
+ "mp-alipay" : {
+ "usingComponents" : true
+ },
+ "mp-baidu" : {
+ "usingComponents" : true
+ },
+ "mp-toutiao" : {
+ "usingComponents" : true
+ },
+ "uniStatistics" : {
+ "enable" : false
+ },
+ "vueVersion" : "2"
+}
diff --git a/yudao-vue-ui/pages.json b/yudao-vue-ui/pages.json
new file mode 100644
index 000000000..1f8ddb3a3
--- /dev/null
+++ b/yudao-vue-ui/pages.json
@@ -0,0 +1,43 @@
+{
+ "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+ {
+ "path": "pages/index/index",
+ "style": {
+ "navigationBarTitleText": "uni-app"
+ }
+ }, {
+ "path": "pages/tabbar/user",
+ "style": {
+ "navigationBarTitleText": "",
+ "enablePullDownRefresh": false
+ }
+ }
+ ],
+ "globalStyle": {
+ "navigationBarTextStyle": "black",
+ "navigationBarTitleText": "uni-app",
+ "navigationBarBackgroundColor": "#F8F8F8",
+ "backgroundColor": "#F8F8F8"
+ },
+ "tabBar": {
+ "color": "#666",
+ "selectedColor": "#FF5A5F",
+ "borderStyle": "black",
+ "list": [{
+ "text": "首页",
+ "pagePath": "pages/index/index",
+ "iconPath": "static/tarbar/index.png",
+ "selectedIconPath": "static/tarbar/index-active.png"
+ }, {
+ "text": "商品",
+ "pagePath": "pages/product/list",
+ "iconPath": "static/tarbar/product.png",
+ "selectedIconPath": "static/tarbar/product-active.png"
+ }, {
+ "text": "我的",
+ "pagePath": "pages/tabbar/user",
+ "iconPath": "static/tarbar/ucenter.png",
+ "selectedIconPath": "static/tarbar/ucenter-active.png"
+ }]
+ }
+}
diff --git a/yudao-vue-ui/pages/index/index.vue b/yudao-vue-ui/pages/index/index.vue
new file mode 100644
index 000000000..ec0ec2608
--- /dev/null
+++ b/yudao-vue-ui/pages/index/index.vue
@@ -0,0 +1,52 @@
+
+
+
+
+ {{title}}
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/pages/tabbar/user.vue b/yudao-vue-ui/pages/tabbar/user.vue
new file mode 100644
index 000000000..57fd71a1b
--- /dev/null
+++ b/yudao-vue-ui/pages/tabbar/user.vue
@@ -0,0 +1,257 @@
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo.nickname }}
+ 普通会员
+
+
+
+ 点击注册/登录
+
+
+
+
+
+
+
+
+
+
+
+
+ 待付款
+ {{ orderCount.c0 }}
+
+
+
+ 待发货
+ {{ orderCount.c1 }}
+
+
+
+ 待收货
+ {{ orderCount.c2 }}
+
+
+
+ 待评价
+ {{ orderCount.c3 }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-vue-ui/static/backgroud/user.jpg b/yudao-vue-ui/static/backgroud/user.jpg
new file mode 100644
index 000000000..a57073374
Binary files /dev/null and b/yudao-vue-ui/static/backgroud/user.jpg differ
diff --git a/yudao-vue-ui/static/icon/arc.png b/yudao-vue-ui/static/icon/arc.png
new file mode 100644
index 000000000..c7ef6df0d
Binary files /dev/null and b/yudao-vue-ui/static/icon/arc.png differ
diff --git a/yudao-vue-ui/static/icon/default-avatar.png b/yudao-vue-ui/static/icon/default-avatar.png
new file mode 100644
index 000000000..09fea4fc8
Binary files /dev/null and b/yudao-vue-ui/static/icon/default-avatar.png differ
diff --git a/yudao-vue-ui/static/logo.png b/yudao-vue-ui/static/logo.png
new file mode 100644
index 000000000..b5771e209
Binary files /dev/null and b/yudao-vue-ui/static/logo.png differ
diff --git a/yudao-vue-ui/static/tarbar/index-active.png b/yudao-vue-ui/static/tarbar/index-active.png
new file mode 100644
index 000000000..b1d65c731
Binary files /dev/null and b/yudao-vue-ui/static/tarbar/index-active.png differ
diff --git a/yudao-vue-ui/static/tarbar/index.png b/yudao-vue-ui/static/tarbar/index.png
new file mode 100644
index 000000000..7f9a4a09e
Binary files /dev/null and b/yudao-vue-ui/static/tarbar/index.png differ
diff --git a/yudao-vue-ui/static/tarbar/logo.png b/yudao-vue-ui/static/tarbar/logo.png
new file mode 100644
index 000000000..b5771e209
Binary files /dev/null and b/yudao-vue-ui/static/tarbar/logo.png differ
diff --git a/yudao-vue-ui/static/tarbar/product-active.png b/yudao-vue-ui/static/tarbar/product-active.png
new file mode 100644
index 000000000..a107caa80
Binary files /dev/null and b/yudao-vue-ui/static/tarbar/product-active.png differ
diff --git a/yudao-vue-ui/static/tarbar/product.png b/yudao-vue-ui/static/tarbar/product.png
new file mode 100644
index 000000000..8c212a02f
Binary files /dev/null and b/yudao-vue-ui/static/tarbar/product.png differ
diff --git a/yudao-vue-ui/static/tarbar/ucenter-active.png b/yudao-vue-ui/static/tarbar/ucenter-active.png
new file mode 100644
index 000000000..f5eb065a0
Binary files /dev/null and b/yudao-vue-ui/static/tarbar/ucenter-active.png differ
diff --git a/yudao-vue-ui/static/tarbar/ucenter.png b/yudao-vue-ui/static/tarbar/ucenter.png
new file mode 100644
index 000000000..17ab66add
Binary files /dev/null and b/yudao-vue-ui/static/tarbar/ucenter.png differ
diff --git a/yudao-vue-ui/uni.scss b/yudao-vue-ui/uni.scss
new file mode 100644
index 000000000..05a48c7e9
--- /dev/null
+++ b/yudao-vue-ui/uni.scss
@@ -0,0 +1,78 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:12px;
+$uni-font-size-base:14px;
+$uni-font-size-lg:16;
+
+/* 图片尺寸 */
+$uni-img-size-sm:20px;
+$uni-img-size-base:26px;
+$uni-img-size-lg:40px;
+
+/* Border Radius */
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
+$base-color: #ff536f;
+
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:20px;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:26px;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:15px;