Commit 78df9d10 authored by superDragon's avatar superDragon

feat:更新

parents
.DS_Store
node_modules
unpackage
/dist
vue.config
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
{ // 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": [{
"type": "uniCloud",
"default": {
"launchtype": "local"
}
}
]
}
<script>
import store from "@/store";
import socket from "@/config/socket";
// #ifdef H5
import {
h5Login
} from "@/config/html5Utils";
// #endif
// #ifdef APP-PLUS
import APPUpdate from "@/plugins/APPUpdate";
// #endif
export default {
onLaunch: function(e) {
//取出缓存数据
store.commit("setCacheData");
// #ifdef MP-WEIXIN
//获取二维码携带的参数
if (store.state.userInfo.token) {
socket.init();
}
// #endif
// #ifdef H5
if (store.state.userInfo.token) {
socket.init();
} else {
h5Login("force", () => {
socket.init();
});
}
// #endif
// #ifdef APP-PLUS
// if (store.state.userInfo.token) {
// socket.init();
// }
APPUpdate();
// #endif
},
onShow: function(e) {
// #ifdef MP-WEIXIN
//获取二维码携带的参数
let scene = decodeURIComponent(e.query.scene);
scene = scene.split("&");
let data = {
//场景值
scene: e.scene
};
scene.forEach(item => {
let arr = item.split("=");
if (arr.length == 2) {
data[arr[0]] = arr[1];
}
});
store.commit("setChatScenesInfo", Object.assign(e.query, data));
//小程序更新
if (uni.getUpdateManager) {
const updateManager = uni.getUpdateManager();
updateManager.onCheckForUpdate(function(res) {
// 请求完新版本信息的回调
// console.log(res.hasUpdate);
});
updateManager.onUpdateReady(function(res) {
uni.showModal({
title: "更新提示",
content: "新版本已经准备好,是否重启应用?",
success(res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate();
}
}
});
});
updateManager.onUpdateFailed(function(res) {
// 新的版本下载失败
uni.showModal({
title: "已经有新版本了哟~",
content: "新版本已经上线啦~,请您删除当前小程序,重新搜索打开哟~",
showCancel: false
});
});
}
// #endif
},
onHide: function() {}
};
</script>
<style lang="scss">
/* #ifndef APP-NVUE */
@import "./style/common.scss";
@import "./style/input.scss";
page {
height: 100%;
background-color: #f5f5f5;
}
/* #endif */
/* #ifdef H5 */
//修复H5底部导航挡住内容bug
uni-app {
height: auto;
}
//修复H5输入框上下不居中bug
.uni-input-form {
height: 100%;
}
//去除地图上高德地图标识符
.amap-copyright {
display: none !important;
}
.amap-logo {
display: none !important;
}
.amap-ui-control-zoom {
width: 60upx !important;
}
.amap-ui-control-zoom>* {
width: 60upx !important;
height: 60upx !important;
line-height: 60upx !important;
}
.amap-ui-control-theme-dark {
display: none !important;
}
/* #endif */
</style>
# uni-app项目模板(不喜勿喷)
> 一个5年的web前端开源的uni-app快速开发模板,参考学习一同进步
> 建议uni-app使用时间达到1年以上的程序员来学习
### 使用步骤
1. 下载下来,解压成文件夹
2. 把项目包丢到HBuilder X里面
3. HBuilder X要安装scss/sass编译插件
4. 运行项目
### `觉得不错,给个5星好评吧`
| `QQ交流群(607391225)` | `微信交流群(加我好友备注"进群")` |
| ----------------------------|--------------------------- |
|![QQ交流群](http://qn.kemean.cn//upload/202004/14/15868301778472k7oubi6.png)|![微信交流群](https://qn.kemean.cn/upload/202010/13/weiXin_group_code.jpg)|
| QQ群号:607391225 |微信号:zhou0612wei|
### `开源不易,需要花费很多精力,打个赏吧`
| `微信打赏码` | `支付宝打赏码` |
| ----------------------------|--------------------------- |
|![微信打赏二维码](http://qn.kemean.cn/upload/202006/17/15923814750253qjayobp.png)|![支付宝打赏二维码](http://qn.kemean.cn/upload/202006/17/1592381515304aezjp7h3.jpg)|
### [H5预览地址](http://8.129.186.35/index.html)
### [安卓APP安装包下载地址1.0.1(可体验版本更新)](http://qn.kemean.cn/upload/202006/10/1591785853646tulgw1o4.apk)
### [安卓APP安装包下载地址1.1.0(最新版)](http://qn.kemean.cn/upload/202008/19/1597832954922s442039o.apk)
### [Demo版GitHub地址](https://github.com/zhouwei1994/uni-app-demo)
### [空项目模板GitHub地址(开发用这个)](https://github.com/zhouwei1994/uni-app-template)
## 聊天案例项目
### [聊天案例项目H5地址](http://8.129.186.35/chat/index.html)
### [聊天案例项目安卓APP安装包下载地址](http://qn.kemean.cn/upload/202008/20/1597887768449e63qg66n.apk)
### 文件说明
1. components/chat-emojis.nvue 表情组件(表情、收藏表情图、表情包)
2. components/chat-message.nvue 消息显示及类型组件
3. components/chat-preview.nvue 图片及视频预览组件
4. components/chat-tabbar.nvue 底部操作组件(语音、输入框、发送)
5. utils/emojis.js 表情数据
6. utils/utils.js 工具
7. chat.nvue 聊天主页面
8. config.js 聊天配置页面及对接页面(所有的聊天对接都提取到这里了)
### 功能
1. 发 文本和表情
2. 发 表情图片
3. 发 图片
4. 发 视频
5. 发 语音
6. 发 位置
7. 发 文件
8. 发 商品
9. 禁言
### 支持聊天类型
1. 单聊
2. 群聊
### 消息渲染
1. 发送图片、视频、文件先预渲染到页面(有上传进度),模仿微信上传图片
2. 发送消息有发送中、发送失败(可点击重发)、发送成功
3. 已读、未读(有样式、后端没实现)
## 架构功能清单
### 组件示例
1. 瀑布流列表
2. 防抖音滑动视频(带进度加载)
3. 项目主题色介绍
4. 头部导航示例
5. 上拉加载,下拉刷新列表
6. 地区选择
7. 弹窗输入框
8. 滑动操作
9. 富文本编译
### SDK示例
1. 接口请求
2. APP版本更新
3. 支付
4. 分享
5. 小程序登录
6. 公众号登录
7. 登录拦截
8. 获取当前位置
9. 图片上传/文件上传
10. 七牛云图片上传/文件上传
11. 保存图片到相册
12. webSocket封装介绍
13. 公众号SDK
14. APP权限判断和跳转到系统设置
15. 常用工具
### 模板页面
1. 登录
2. 注册
3. 忘记密码
4. 绑定手机号
5. 协议
### 项目结构
``` bash
├── components // 组件
│   ├── common // 公共组件
│   └── module // 项目组件
├── config // 配置 // vuex主文件
│   ├── baseUrl.js // 项目配置
│   ├── html5Utils.js // H5相关的功能(公众号支付、公众号登录)
│   ├── login.js // 小程序登录js代码部分和登录拦截器代码
│   ├── requestConfig.js // 接口请求配置
│   ├── socket.js // webSocket相关代码
│   └── utils.js // 项目相关工具(公众号分享、小程序分享数据处理、支付、获取经纬度、支付分配)
├── pages // 项目页面
├── plugins // 公共SDK(基本上不需要改)
│   ├── APPUpdate // APP版本更新
│   ├── request // 接口请求封装
│   ├── share // APP分享
│   ├── md5.js // md5加密
│   ├── permission.js // APP权限判断和打开手机系统设置
│   ├── utils.js // 工具(时间转换、APP和小程序获取经纬度代码)
│   └── wxJsSDK.js // 微信公众号SDK去权限获取页面
├── static // 公共文件
│   ├── demo // 本项目相关的图片(可删除)
│   ├── icon // 项目图标
│   ├── mp-h5 // H5第三方包(公众号JS-SDK)
│   ├── mp-weixin // 微信小程序第三方包
│   ├── share // 分享SDK的图标
│   └── zhouWei-navBar // 导航组件的图标
├── store // vuex商店
│ ├── modules // vuex分类
│ │ ├── common.js // vuex通用数据管理
│ │ ├── user.js // vuex用户数据管理
│   │ └── order.js // vuex订单数据管理
│   └── index.js // vuex方法集合
├── style
│ ├── common.scss // 公共样式文件
│ ├── input.scss // 公共表单样式
│ ├── mixin.scss // 样式配置文件
│ └── table.scss // 本项目相关的css(可删除)
├── unpackage // 项目编译后的文件
├── App.vue // 项目主界面
├── main.js // 程序入口文件,加载各种公共组件
├── manifest.json // uni-app项目类型及环境配置
├── pages.json // 项目路由及项目界面配置
├── README.md // 项目介绍文件
└── template.h5.html // 项目发布的时候使用的文件
```
<template>
<popup v-model="currentValue">
<view class="addresTitle">
<text @click="currentValue = false">取消</text>
<view>所在地区</view>
<text @click="onConfirm">确定</text>
</view>
<z-address :dataList="addressVal" @change="addressChange" :length="length" :force="force"></z-address>
</popup>
</template>
<script>
import Popup from './popup.vue';
import zAddress from './address.vue';
export default {
components: {
Popup,
zAddress
},
props: {
dataList: {
type: Array,
default() {
return [];
}
},
value: {
type: Boolean,
default: false
},
length: {
type: Number,
default: 2
},
force:{
type: Boolean,
default: true
}
},
created() {
if (typeof this.value !== 'undefined') {
this.currentValue = this.value;
}
if (this.dataList instanceof Array) {
this.addressVal = this.dataList;
}
},
watch: {
value(val) {
this.currentValue = val;
},
currentValue(val) {
this.$emit(val ? 'on-show' : 'on-hide');
this.$emit('input', val);
},
dataList(val) {
this.addressVal = val;
}
},
data() {
return {
currentValue: false,
//选出的值
addressVal: []
};
},
methods: {
addressChange(val) {
console.log(val);
this.addressVal = val;
},
onConfirm() {
if (parseInt(this.length) <= this.addressVal.length || !this.force && this.addressVal.length > 0) {
this.currentValue = false;
this.$emit('change', this.addressVal);
} else {
uni.showToast({
title: '请选择',
icon: 'none'
});
}
}
},
mounted() {}
};
</script>
<style lang="scss" scoped>
@import '@/style/mixin.scss';
.addresTitle {
display: flex;
justify-content: space-between;
height: 88upx;
line-height: 88upx;
border-bottom: 2upx solid #ebebeb;
padding: 0 20upx;
background-color: #FFF;
}
.addresTitle view {
font-size: 32upx;
}
.addresTitle text {
width: 80upx;
flex-shrink: 0;
text-align: center;
}
.addresTitle text {
font-size: 28upx;
color: #999;
}
.addresTitle text:last-child {
color: $themeColor;
}
</style>
<template>
<view>
<view class="addres_select_val">
<view>
<view v-for="(item, index) of addressVal" :key="index" :class="{ select: addressIndex == index }" @click="selectType(index)" k>{{ item.name }}</view>
<view
:class="{ select: selectState == addressIndex }"
v-show="selectState < length || (selectState >= length && selectState < 3 && !force)"
@click="selectType(selectState)"
>
请选择
</view>
</view>
</view>
<scroll-view class="addres_box" scroll-y :scroll-top="scrollTop">
<view>
<view
v-for="(item, index) of addressList"
:key="index"
:class="{ select: addressVal.length > addressIndex && item.objId == addressVal[addressIndex].objId }"
@click="selectClick(item)"
>
{{ item.name }}
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
props: {
//选中数据
dataList: {
type: Array,
default() {
return [];
}
},
//联动长度[省,市,区]
length: {
type: Number,
default() {
return 3;
}
},
//是否强制选择,如果length=2,force = false 选择到市的时候就可以确定了,但是还可以选择到区
force: {
type: Boolean,
default() {
return true;
}
}
},
created() {
if (this.dataList instanceof Array) {
this.addressVal = this.dataList;
this.selectState = this.dataList.length;
}
},
watch: {
dataList(val) {
this.addressVal = val;
this.selectState = val.length;
}
},
data() {
return {
//选出的值
addressVal: [],
//当前选择
addressIndex: 0,
//选择的值
addressList: [],
//请选择的显示
selectState: 0,
scrollTop:0
};
},
methods: {
getRegion(pid) {
//请求数据
this.$http.get('api/kemean/aid/region', { pid: pid }, { load: false }).then(data => {
if (data.length > 0) {
this.addressList = data;
// this.$refs.scroll.scrollTop = '0px';
this.scrollTop = Math.random();
} else {
this.$emit('change', this.addressVal);
}
});
},
//切换对应的类型
selectType(index) {
this.addressIndex = index;
var len = this.addressVal.length;
if (index == 0) {
this.getRegion(0);
} else {
this.getRegion(this.addressVal[index - 1].objId);
}
if (len == this.length) {
this.selectState = this.length;
} else if (len == this.length && index == this.length && this.force) {
this.selectState = index;
} else {
this.selectState = index + 1;
}
},
//选择
selectClick(item) {
if (this.addressIndex == 0) {
this.addressVal = [];
} else {
this.addressVal.splice(this.addressIndex, this.addressVal.length - 1);
}
this.addressVal.push(item);
if (this.addressVal.length < this.length || (this.addressVal.length < 3 && !this.force)) {
this.getRegion(item.objId);
this.addressIndex++;
}
if (this.addressVal.length >= this.length || !this.force) {
this.$emit('change', this.addressVal);
}
this.selectState = this.addressVal.length;
}
},
mounted() {
this.getRegion(0);
}
};
</script>
<style lang="scss" scoped>
@import "@/style/mixin.scss";
.addres_select_val {
padding: 0upx 10upx;
border-bottom: 1upx solid #ebebeb;
box-sizing: border-box;
background-color: #fff;
}
.addres_select_val > view {
display: flex;
flex-wrap: wrap;
}
.addres_select_val > view > view {
margin-left: 20upx;
padding: 0upx 10upx;
height: 72upx;
line-height: 72upx;
border-bottom: 2upx solid #fff;
box-sizing: border-box;
font-size: 28upx;
}
.addres_select_val > view > view:first-child {
margin-left: 0upx;
}
.addres_select_val > view > view.select {
border-bottom: 2upx solid $themeColor;
color: $themeColor;
}
.addres_box {
padding: 0upx 20upx;
height: 420upx;
overflow-y: auto;
background-color: #fff;
}
.addres_box view > view {
height: 72upx;
line-height: 72upx;
font-size: 28upx;
}
.addres_box view > view.select {
color: $themeColor;
}
</style>
<template>
<view>
<!-- 登录弹窗 -->
<view class="loginMask" v-if="loginPopupShow" @click="closePopup"></view>
<view class="loginPopup" v-if="loginPopupShow">
<view class="loginBox">
<image class="logo" :src="base.logoUrl"></image>
<view class="platformName">{{ base.platformName }}</view>
<view class="description" v-if="base.description">{{ base.description }}</view>
</view>
<button type="primary" hover-class="active" open-type="getUserInfo" @getuserinfo="onAuthorization">授权登录</button>
</view>
</view>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
import base from '@/config/baseUrl';
// #ifdef MP-WEIXIN
import { getUserInfo } from '@/config/login';
// #endif
let clear;
export default {
data() {
return {
base: base
};
},
computed: {
...mapState(['userInfo', 'loginPopupShow'])
},
methods: {
...mapMutations(['setUserInfo', 'setLoginPopupShow']),
//授权登录
onAuthorization: function(e) {
if (e.detail.errMsg == 'getUserInfo:ok') {
var userInfo = e.detail;
this.setLoginPopupShow(false);
getUserInfo(userInfo, 'authorized');
}
},
closeLogin() {
if (this.loginPopupShow && this.userInfo.token) {
this.setLoginPopupShow(false);
}
},
//关闭弹窗
closePopup() {
this.setLoginPopupShow(false);
}
}
};
</script>
<style lang="scss" scoped>
@import '@/style/mixin.scss';
.loginMask {
position: fixed;
top: 0upx;
left: 0upx;
right: 0upx;
bottom: 0upx;
background-color: rgba(0, 0, 0, 0.4);
z-index: 10;
}
.loginPopup {
position: fixed;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
width: 500upx;
background-color: #fff;
border-radius: 20upx;
overflow: hidden;
z-index: 11;
.loginBox {
padding: 30upx 15upx 40upx 15upx;
display: flex;
flex-direction: column;
align-items: center;
.logo {
width: 160upx;
height: 160upx;
border-radius: 20%;
}
.platformName {
font-size: 24upx;
color: #999;
margin-top: 10upx;
}
.description {
margin-top: 15upx;
font-size: 30upx;
color: #333;
}
}
button {
border-radius: 0upx;
background-color: $themeColor;
}
.active {
background-color: $themeColor;
opacity: 0.8;
}
}
</style>
This diff is collapsed.
var cfg = require('./config.js'),
isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
class CssHandler {
constructor(tagStyle) {
var styles = Object.assign({}, cfg.userAgentStyles);
for (var item in tagStyle)
styles[item] = (styles[item] ? styles[item] + ';' : '') + tagStyle[item];
this.styles = styles;
}
getStyle(data) {
this.styles = new CssParser(data, this.styles).parse();
}
match(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;
class CssParser {
constructor(data, init) {
this.data = data;
this.floor = 0;
this.i = 0;
this.list = [];
this.res = init;
this.state = this.Space;
}
parse() {
for (var c; c = this.data[this.i]; this.i++)
this.state(c);
return this.res;
}
section() {
return this.data.substring(this.start, this.i);
}
// 状态机
Space(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;
}
Comment() {
this.i = this.data.indexOf('*/', this.i) + 1;
if (!this.i) this.i = this.data.length;
this.state = this.Space;
}
Ignore(c) {
if (c == '{') this.floor++;
else if (c == '}' && !--this.floor) this.state = this.Space;
}
Name(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;
}
NameSpace(c) {
if (c == '{') this.Content();
else if (c == ',') this.Comma();
else if (!cfg.blankChar[c]) this.state = this.Ignore;
}
Comma() {
while (cfg.blankChar[this.data[++this.i]]);
if (this.data[this.i] == '{') this.Content();
else {
this.start = this.i--;
this.state = this.Name;
}
}
Content() {
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;
}
}
This diff is collapsed.
/* 配置文件 */
// #ifdef MP-WEIXIN
const canIUse = wx.canIUse('editor'); // 高基础库标识,用于兼容
// #endif
module.exports = {
// 过滤器函数
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'),
// 块级标签,将被转为 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,basefont,canvas,command,frame,input,isindex,keygen,link,map,meta,param,script,source,style,svg,textarea,title,track,use,wbr'
// #ifdef MP-WEIXIN
+ (canIUse ? ',rp' : '')
// #endif
// #ifndef APP-PLUS
+ ',embed,iframe'
// #endif
),
// 只能被 rich-text 显示的标签
richOnlyTags: makeMap('a,colgroup,fieldset,legend,picture,table'
// #ifdef MP-WEIXIN
+ (canIUse ? ',bdi,bdo,caption,rt,ruby' : '')
// #endif
),
// 自闭合的标签
selfClosingTags: makeMap(
'area,base,basefont,br,col,circle,ellipse,embed,frame,hr,img,input,isindex,keygen,line,link,meta,param,path,polygon,rect,source,track,use,wbr'
),
// 信任的属性
trustAttrs: makeMap(
'align,alt,app-id,author,autoplay,border,cellpadding,cellspacing,class,color,colspan,controls,data-src,dir,face,height,href,id,ignore,loop,media,muted,name,path,poster,rowspan,size,span,src,start,style,type,unit-id,width,xmlns'
),
// bool 型的属性
boolAttrs: makeMap('autoplay,controls,ignore,loop,muted'),
// 信任的标签
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 = {},
list = str.split(',');
for (var i = list.length; i--;)
map[list[i]] = true;
return map;
}
var inlineTags = {
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
}
export default {
useRichText: function(item) {
return !item.c && !inlineTags[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1;
}
}
var inlineTags = {
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
}
module.exports = {
useRichText: function(item) {
return !item.c && !inlineTags[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1;
}
}
This diff is collapsed.
<template>
<view class="mask mask-show" v-if="loadingShow" @touchmove.stop.prevent="preventTouchMove">
<!-- 加载动画开始 -->
<view class="preloader">
<view class="loader"></view>
</view>
<!-- 加载动画结束 -->
<view class="title">数据读取中...</view>
</view>
</template>
<script scoped="true">
import { mapState, mapMutations } from 'vuex';
export default {
computed: {
...mapState(['loadingShow'])
},
methods:{
preventTouchMove(){
console.log('stop user scroll it!');
return;
}
}
};
</script>
<style>
.mask {
/* pointer-events: none; */
position: fixed;
z-index: 99999;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex-wrap: wrap;
}
.mask.mask-show {
background: rgba(255,255,255, 0.3);
}
.title {
color: #333;
font-size: 28rpx;
margin-top: 20rpx;
}
.loader {
display: block;
width: 120rpx;
height: 120rpx;
border-radius: 50%;
border: 3rpx solid transparent;
border-top-color: #9370db;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
.loader::before {
content: "";
position: absolute;
top: 5rpx;
left: 5rpx;
right: 5rpx;
bottom: 5rpx;
border-radius: 50%;
border: 3rpx solid transparent;
border-top-color: #ba55d3;
-webkit-animation: spin 3s linear infinite;
animation: spin 3s linear infinite;
}
.loader::after {
content: "";
position: absolute;
top: 15rpx;
left: 15rpx;
right: 15rpx;
bottom: 15rpx;
border-radius: 50%;
border: 3rpx solid transparent;
border-top-color: #ff00ff;
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>
/* 下拉刷新区域 */
.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
<!-- 下拉刷新区域 -->
<template>
<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background-color':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
<view class="downwarp-content">
<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform':downRotate}"></view>
<view class="downwarp-tip">{{downText}}</view>
</view>
</view>
</template>
<script>
export default {
props: {
option: Object , // down的配置项
type: Number, // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
rate: Number // 下拉比率 (inOffset: rate<1; outOffset: rate>=1)
},
computed: {
// 支付宝小程序需写成计算属性,prop定义default仍报错
mOption(){
return this.option || {}
},
// 是否在加载中
isDownLoading(){
return this.type === 3
},
// 旋转的角度
downRotate(){
return 'rotate(' + 360 * this.rate + 'deg)'
},
// 文本提示
downText(){
switch (this.type){
case 1: return this.mOption.textInOffset;
case 2: return this.mOption.textOutOffset;
case 3: return this.mOption.textLoading;
case 4: return this.mOption.textLoading;
default: return this.mOption.textInOffset;
}
}
}
};
</script>
<style>
@import "./mescroll-down.css";
</style>
<!--空布局
可作为独立的组件, 不使用mescroll的页面也能单独引入, 以便APP全局统一管理:
import MescrollEmpty from '@/components/mescroll-uni/components/mescroll-empty.vue';
<mescroll-empty v-if="isShowEmpty" :option="optEmpty" @emptyclick="emptyClick"></mescroll-empty>
-->
<template>
<view class="mescroll-empty" :class="{ 'empty-fixed': option.fixed }" :style="{ 'z-index': option.zIndex, top: option.top }">
<view> <image v-if="icon" class="empty-icon" :src="icon" mode="widthFix" /> </view>
<view v-if="tip" class="empty-tip">{{ tip }}</view>
<view v-if="option.btnText" class="empty-btn" @click="emptyClick">{{ option.btnText }}</view>
</view>
</template>
<script>
// 引入全局配置
import GlobalOption from './../mescroll-uni-option.js';
export default {
props: {
// empty的配置项: 默认为GlobalOption.up.empty
option: {
type: Object,
default() {
return {};
}
}
},
// 使用computed获取配置,用于支持option的动态配置
computed: {
// 图标
icon() {
return this.option.icon == null ? GlobalOption.up.empty.icon : this.option.icon; // 此处不使用短路求值, 用于支持传空串不显示图标
},
// 文本提示
tip() {
return this.option.tip == null ? GlobalOption.up.empty.tip : this.option.tip; // 此处不使用短路求值, 用于支持传空串不显示文本提示
}
},
methods: {
// 点击按钮
emptyClick() {
this.$emit('emptyclick');
}
}
};
</script>
<style lang="scss">
@import '@/style/mixin.scss';
/* 无任何数据的空布局 */
.mescroll-empty {
box-sizing: border-box;
width: 100%;
padding: 100rpx 50rpx;
text-align: center;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
}
.mescroll-empty.empty-fixed {
z-index: 99;
position: absolute; /*transform会使fixed失效,最终会降级为absolute */
top: 100rpx;
left: 0;
}
.mescroll-empty .empty-icon {
width: 280rpx;
height: 280rpx;
}
.mescroll-empty .empty-tip {
margin-top: 20rpx;
font-size: 24rpx;
color: gray;
}
.mescroll-empty .empty-btn {
display: inline-block;
margin-top: 40rpx;
min-width: 200rpx;
padding: 18rpx;
font-size: 28rpx;
border: 1rpx solid #e04b28;
border-radius: 60rpx;
color: #e04b28;
}
.mescroll-empty .empty-btn:active {
opacity: 0.75;
}
</style>
<!-- 回到顶部的按钮 -->
<template>
<image
v-if="mOption.src"
class="mescroll-totop"
:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-totop-safearea': mOption.safearea}]"
:style="{'z-index':mOption.zIndex, 'left': left, 'right': right, 'bottom':addUnit(mOption.bottom), 'width':addUnit(mOption.width), 'border-radius':addUnit(mOption.radius)}"
:src="mOption.src"
mode="widthFix"
@click="toTopClick"
/>
</template>
<script>
export default {
props: {
// up.toTop的配置项
option: Object,
// 是否显示
value: false
},
computed: {
// 支付宝小程序需写成计算属性,prop定义default仍报错
mOption(){
return this.option || {}
},
// 优先显示左边
left(){
return this.mOption.left ? this.addUnit(this.mOption.left) : 'auto';
},
// 右边距离 (优先显示左边)
right() {
return this.mOption.left ? 'auto' : this.addUnit(this.mOption.right);
}
},
methods: {
addUnit(num){
if(!num) return 0;
if(typeof num === 'number') return num + 'rpx';
return num
},
toTopClick() {
this.$emit('input', false); // 使v-model生效
this.$emit('click'); // 派发点击事件
}
}
};
</script>
<style>
/* 回到顶部的按钮 */
.mescroll-totop {
z-index: 9990;
position: fixed !important; /* 加上important避免编译到H5,在多mescroll中定位失效 */
right: 20rpx;
bottom: 120rpx;
width: 72rpx;
height: auto;
border-radius: 50%;
opacity: 0;
transition: opacity 0.5s; /* 过渡 */
margin-bottom: var(--window-bottom); /* css变量 */
}
/* 适配 iPhoneX */
@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
.mescroll-totop-safearea {
margin-bottom: calc(var(--window-bottom) + constant(safe-area-inset-bottom)); /* window-bottom + 适配 iPhoneX */
margin-bottom: calc(var(--window-bottom) + env(safe-area-inset-bottom));
}
}
/* 显示 -- 淡入 */
.mescroll-totop-in {
opacity: 1;
}
/* 隐藏 -- 淡出且不接收事件*/
.mescroll-totop-out {
opacity: 0;
pointer-events: none;
}
</style>
/* 上拉加载区域 */
.mescroll-upwarp {
min-height: 60rpx;
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
<!-- 上拉加载区域 -->
<template>
<view class="mescroll-upwarp" :style="{'background-color':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
<view v-show="isUpLoading">
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
<view class="upwarp-tip">{{ mOption.textLoading }}</view>
</view>
<!-- 无数据 -->
<view v-if="isUpNoMore" class="upwarp-nodata">{{ mOption.textNoMore }}</view>
</view>
</template>
<script>
export default {
props: {
option: Object, // up的配置项
type: Number // 上拉加载的状态:0(loading前),1(loading中),2(没有更多了)
},
computed: {
// 支付宝小程序需写成计算属性,prop定义default仍报错
mOption() {
return this.option || {};
},
// 加载中
isUpLoading() {
return this.type === 1;
},
// 没有更多了
isUpNoMore() {
return this.type === 2;
}
}
};
</script>
<style>
@import './mescroll-up.css';
</style>
.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
This diff is collapsed.
// 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;
// 全局配置
// 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
isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25
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: "http://www.mescroll.com/img/mescroll-empty.png?v=1", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
tip: '~ 空空如也 ~' // 提示
}
}
}
export default GlobalOption
.mescroll-uni-warp{
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);
}
}
This diff is collapsed.
This diff is collapsed.
/**
* 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;
/**
* 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对象
mescrollInit(mescroll) {
this.mescroll = mescroll;
this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序 (mescroll-mixins.js)
// 自动加载当前tab的数据
if(this.i === this.index){
this.isInit = true; // 标记为true
this.mescroll.triggerDownScroll();
}
},
}
}
export default MescrollMoreItemMixin;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<template>
<view>
<!-- 加载动画组件 -->
<z-loading></z-loading>
<!-- #ifdef MP-WEIXIN -->
<!-- 小程序登录弹窗组件 -->
<applets-login></applets-login>
<!-- #endif -->
</view>
</template>
<script>
import zLoading from "@/components/common/loading.vue"
// #ifdef MP-WEIXIN
import appletsLogin from "@/components/common/applets-login.vue"
// #endif
export default {
components:{
zLoading,
// #ifdef MP-WEIXIN
appletsLogin
// #endif
}
};
</script>
<style lang="scss" scoped>
</style>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/***************纯粹的数据请求(如果使用这种可以删除掉fileUpload.js)******************/
// import request from "./core/request.js";
// export default request;
/********数据请求同时继承了文件上传(包括七牛云上传)************/
import upload from "./upload/upload.js";
export default upload;
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment